Merge pull request 'Архив OpenSpec: goodwill-table и goodwill-ui' (#228) from chore/archive-goodwill into main

This commit is contained in:
aep 2026-11-06 18:10:01 +03:00
commit 31f327f86f
14 changed files with 177 additions and 2 deletions

View File

@ -0,0 +1,104 @@
# goodwill-table Specification
## Purpose
Таблица гудвилла позволяет аналитику сохранить долю и обоснование для фактических вложений между компаниями версии и получить проверяемые суммы для последующего расчёта КФО.
## Requirements
### Requirement: Фактические направленные пары
Система SHALL находить пары ГО→ДО по ненулевой сумме closing_debit счёта 58.01 и его потомков по дереву счетов. Обе компании входят в периметр и различаются; контрагент сопоставлен. is_head_entity MUST NOT влиять на результат. Источники подчиняются правилам актуальности, периодов и исключения двойного суммирования сводной ОСВ.
#### Scenario: Реальная ОСВ счёта 58
- **WHEN** строится таблица по привязанной ОСВ `fixtures/osv-dkb/primanych/2025-Q3/Оборотно-сальдовая ведомость по счету 58 за 9 месяцев 2025 г. ООО Приманыческий.xls`
- **THEN** учитываются только подходящие строки 58.01 и потомков с контрагентами периметра; 58.03 не включается, строки ACCOUNT не включаются; собственные приоритеты перекрывающихся файлов не применяются
#### Scenario: Направление не зависит от признака головы
- **WHEN** подходящие вложения найдены у двух участников периметра с любыми значениями is_head_entity
- **THEN** каждый владелец становится ГО своих пар, включая встречные направления; пустой контрагент, самовложение и внешний контрагент не создают пары
### Requirement: Сохранение исчезнувших пар
Таблица SHALL объединять текущие пары с ранее сохранёнными этой версии. has_investments=false обозначает «Нет вложений»: пара отсутствует, её сальдо нулевое или участник вышел из периметра. По решению пользователя денежные поля неактивной строки SHALL быть null. Сохранённый ввод MUST оставаться; неактивная строка не входит в итоги и вклад КФО. Чтение MUST NOT создавать или изменять записи goodwill.
#### Scenario: Замена источника
- **WHEN** после сохранения пары из реальной ОСВ документ заменён и подходящих вложений больше нет
- **THEN** строка остаётся с has_investments=false, прежними долей, комментарием и галочкой, но без вклада в итоги; повторное появление пары восстанавливает расчёт по текущим источникам
### Requirement: Источник капитала и расчёт
Система SHALL использовать 1310 ББ ДО на конец отчётного периода в рублях; единицы формы SHALL распознаваться словарём единиц arm_statements, нераспознанные единицы дают отсутствующий УК. P=round(УК×доля/100,2), G=ФВ−P. Дубликаты 1310 обрабатываются агрегированием формы. Ноль допустим; отсутствие доли или достоверного УК даёт null P/G. 1350 не участвует: ASSUMPTION(OQ-10).
#### Scenario: Ноль отличается от отсутствия расчёта
- **WHEN** из привязанного реального ББ получен УК U, доля ещё не введена
- **THEN** P и G равны null; при доле 0 P равен 0.00 и G равен ФВ; отсутствующая 1310 не подменяется нулём
#### Scenario: Единицы, исправленные вручную
- **WHEN** у формы ББ единицы «тыс.руб.» или «в тысячах рублей»
- **THEN** УК переводится в рубли умножением на 1000; единицы «штук» дают УК null и предупреждение CAPITAL_SOURCE_MISSING
#### Scenario: Проверка отчётной колонки
- **WHEN** в привязанном ББ есть несколько сравнительных колонок и единицы «тыс. руб.»
- **THEN** УК берётся по дате конца отчётного периода, а не только is_current; сумма переводится в рубли до расчёта
### Requirement: Получение таблицы
GET /api/v1/consolidations/versions/{version_id}/goodwill SHALL возвращать version_id, goodwill_enabled, editable, read_only_reason, rows, groups, totals. Строки отсортированы по именам ГО/ДО и ID; содержат поля MD-контракта, has_investments и warnings. Деньги — строки с двумя знаками в рублях или null, доли — строки с двумя знаками. groups — итог «Всего» по каждой ГО в порядке строк, totals — итог по листу.
#### Scenario: Пустая таблица
- **WHEN** нет подходящих вложений и сохранённых пар
- **THEN** rows пуст, денежные итоги 0.00; запрос не создаёт goodwill
### Requirement: Итоги и предупреждения
Система SHALL суммировать ФВ и УК действующих строк независимо от выбора; P/G — только действующих выбранных рассчитанных строк. goodwill_in_kfo_amount включает только положительные применимые G и равен нулю при выключенном goodwill_enabled. Сумма заполненных долей действующих ГО в одной ДО свыше 100% даёт предупреждение, не ошибку.
#### Scenario: Несколько ГО одной ДО
- **WHEN** у действующих пар одной ДО заполнены доли 70% и 40%
- **THEN** строки содержат предупреждение OWNERSHIP_TOTAL_EXCEEDED, сохранение не блокируется; проценты по разным ДО не складываются в общий процент
#### Scenario: Пример DOC-259025 §11.1
- **WHEN** у ГО «Альфа» отмечены Бета (ФВ 180 000, УК 100 000, 80 %) и Гамма (ФВ 50 000, УК 100 000, 80 %), Дельта (ФВ 60 000, УК 60 000) без доли и не отмечена
- **THEN** «Всего» и «Итого по листу»: ФВ 290 000, УК 260 000, P 160 000, G 70 000, в КФО 100 000 (при выключенном переключателе — 0)
### Requirement: Пакетное сохранение
PUT …/goodwill SHALL принимать набор строк {head_legal_entity_id, subsidiary_legal_entity_id, ownership_share, include_in_kfo, comment} и сохранять его в одной транзакции: все строки проверяются до записи, ошибка любой строки откатывает весь пакет и несёт ключ пары. Доля допускает null только при снятом выборе, иначе 0–100, не больше двух знаков после запятой, запятая или точка; превышение точности не округляется. Для выбранной действующей строки обязателен непустой комментарий до 1000 символов. Ошибки — 404 NOT_FOUND (пары нет в таблице), 422 INVALID_SHARE, COMMENT_REQUIRED, MONEY_OUT_OF_RANGE, DUPLICATE_ROW. Строка, ввод которой не изменился, MUST NOT перезаписываться и писать аудит. Ответ — пересчитанная таблица.
#### Scenario: Ошибка одной строки пакета
- **WHEN** пакет содержит корректную строку и строку отмеченной пары без комментария
- **THEN** ответ 422 COMMENT_REQUIRED с head_legal_entity_id и subsidiary_legal_entity_id этой строки; ни одна запись goodwill и ни одно событие GOODWILL_EDIT не созданы
#### Scenario: Пакет без изменений
- **WHEN** повторно отправлена сохранённая строка с той же долей («80,5» после «80.50»), галочкой и комментарием
- **THEN** updated_at строки прежний, событий GOODWILL_EDIT не добавилось
#### Scenario: Снятие выбора
- **WHEN** сохраняется действующая строка с include_in_kfo=false и прежними долей и комментарием
- **THEN** ввод сохраняется, строка исключается из итогов P/G и вклада КФО; ответ содержит пересчитанную таблицу
#### Scenario: Повторное сохранение и неактивная строка
- **WHEN** повторно сохраняется существующая пара
- **THEN** новая запись не создаётся; произвольная неизвестная пара даёт 404, сохранённая неактивная пара не получает дополнительного запрета GOODWILL_PAIR_INACTIVE; отсутствующий ввод не затирает сохранённые данные
### Requirement: Переключатель версии
PUT …/goodwill-settings SHALL принимать goodwill_enabled и возвращать таблицу. Выключение MUST NOT очищать строки и ручной ввод; оно отключает вклад в КФО. Изменение значения и сохранение строки SHALL записывать GOODWILL_EDIT с before/after, автором и версией в одной транзакции; повтор прежнего значения переключателя не создаёт изменение.
#### Scenario: Выключение и включение
- **WHEN** пользователь выключает гудвилл и затем включает обратно
- **THEN** доли, комментарии и галочки сохранены; вклад вновь вычисляется из актуальных данных, записи КФО не изменяются
### Requirement: Общие права и блокировки
Чтение и экспорт SHALL требовать CONSOLIDATION_VIEW и видимость версии. Изменения SHALL требовать CONSOLIDATION_EDIT, редактируемый статус и собственную активную блокировку. Невидимая версия возвращает 404 NOT_FOUND; изменения без доступа — 403, без редактируемости/блокировки — 409 VERSION_NOT_EDITABLE, LOCK_REQUIRED или LOCKED_BY_OTHER.
#### Scenario: Просмотр завершённой версии
- **WHEN** видимая версия VERIFIED или CANCELLED открывается либо изменяется
- **THEN** чтение доступно с editable=false, изменение отклоняется без записи goodwill или аудита изменения
### Requirement: Экспорт XLSX
GET …/goodwill/export SHALL выгружать XLSX с составом и порядком восьми колонок экранной таблицы. Параметры API остаются прежними: sign выбирает фильтр знака без изменения общих итогов. Деньги выгружаются в рублях; экспорт использует сохранённый ввод, оставляет нерассчитанные значения пустыми и записывает EXPORT. Цветовое оформление стандартное.
#### Scenario: Выгрузка таблицы
- **WHEN** выгружается таблица реальной версии с сохранёнными решениями и неактивной парой
- **THEN** XLSX соответствует серверной таблице, содержит «Нет вложений» и не учитывает эту строку в итогах; отдельное событие EXPORT содержит report=goodwill и число строк
### Requirement: Пустые значения конечного дебета
По решению пользователя система SHALL суммировать только заполненные closing_debit пары; пустые значения остальных строк MUST NOT блокировать расчёт P/G и сохранение ручного ввода.
#### Scenario: Заполненная и пустая строки одной пары
- **WHEN** у пары есть вложения 269997300.00 и строка с closing_debit=null, доля 100%, УК 100000000.00 и комментарий
- **THEN** сохранение разрешено; ФВ=269997300.00, P=100000000.00, G=169997300.00

View File

@ -0,0 +1,67 @@
# goodwill-ui Specification
## Purpose
Вкладка «Гудвилл» отчёта версии (AURORA-1544): таблица гудвилла по головным организациям, режим редактирования долей и комментариев, переключатель учёта гудвилла в КФО, фильтр знака и экспорт — по контракту `../docs/dev/api-contract-goodwill.md` и DOC-259025.
## Requirements
### Requirement: Таблица гудвилла
Вкладка `goodwill` страницы отчёта версии SHALL загружать таблицу одним запросом `GET /consolidations/versions/{id}/goodwill`
(контракт `../docs/dev/api-contract-goodwill.md`) и показывать блоки по ГО в порядке ответа: строка ГО, строки ДО, строка
«Всего» блока из `groups[].totals`, внизу «Итого по листу» из `totals`. Колонки — по макету `[31-03]`: «Учет в отчете»,
«Наименование дочерней организации», «Доля владения», «Фондовые вложения головной организации», «Доля участия головной
организации в уставном капитале», «Уставный капитал», «Гудвилл (+/-)», «Комментарий». Сумма `null` SHALL показываться
прочерком, ноль — нулём в выбранных единицах; итоги фронт MUST NOT считать сам.
#### Scenario: Нулевой и нерассчитанный гудвилл
- **WHEN** у одной строки `goodwill_amount = "0.00"`, у другой `null`
- **THEN** первая показывает «0,00», вторая — «—»
#### Scenario: Строка «Нет вложений»
- **WHEN** у строки `has_investments = false`
- **THEN** строка серая, с меткой «Нет вложений», чекбокс недоступен, суммы — прочерки, сохранённые доля и комментарий видны
#### Scenario: Предупреждения
- **WHEN** у строки есть `warnings` (например, `CAPITAL_SOURCE_MISSING`)
- **THEN** рядом с названием ДО иконка предупреждения, по наведению — тексты `message`
### Requirement: Редактирование таблицы гудвилла
Кнопка «Редактировать» SHALL быть доступна при `editable = true`; иначе — недоступна с подсказкой причины
(`read_only_reason`). В режиме редактирования (баннер «Доступны для редактирования столбцы «Доля владения» и
«Комментарий».») у действующих строк доступна галочка; у отмеченной действующей строки — поля доли и комментария, P и G
строки SHALL пересчитываться предварительно по введённой доле. Доля — 0–100, не больше двух знаков, запятая или точка;
ошибочная доля MUST NOT обрезаться или подменяться, а показывается ошибкой у поля. «Сохранить» SHALL быть недоступно,
пока у отмеченной действующей строки нет корректной доли или комментария. Групповой чекбокс блока SHALL отмечать и снимать
все действующие строки блока, включая скрытые фильтром, и показывать промежуточное состояние при частичном выборе.
«Отменить изменения» SHALL возвращать последнее сохранённое состояние.
#### Scenario: Сохранение набора
- **WHEN** аналитик отметил две строки, ввёл доли «80,5» и «100» и комментарии и нажал «Сохранить»
- **THEN** уходит один `PUT …/goodwill` только с изменёнными строками; ответ заменяет таблицу, режим редактирования закрывается
#### Scenario: Снятие галочки у строки без доли
- **WHEN** аналитик снимает галочку у строки, где доля не вводилась, и сохраняет
- **THEN** строка уходит с `ownership_share: null` и сохраняется без ошибки
#### Scenario: Ошибка строки от сервера
- **WHEN** сервер отвечает 422 `COMMENT_REQUIRED` с ключом пары
- **THEN** поле комментария этой строки подсвечено ошибкой, режим редактирования не закрыт, введённое не потеряно
#### Scenario: Лишние знаки доли
- **WHEN** аналитик вводит долю «80,123»
- **THEN** у поля ошибка «Число от 0 до 100, не больше двух знаков после запятой», значение в поле не меняется, «Сохранить» недоступно
### Requirement: Переключатель, фильтр и экспорт гудвилла
Панель вкладки SHALL содержать переключатель «Учитывать гудвилл в КФО» (`PUT …/goodwill-settings`, доступен при
`editable` вне режима редактирования) с подписью суммы `goodwill_in_kfo_amount` или «Гудвилл не учитывается в КФО»;
фильтр «Гудвилл»: «Все значения», «Положительные», «Отрицательные», «Нулевые» (нерассчитанный гудвилл — только во «Все
значения»); единицы и знаки после запятой; «Настройка таблицы»; «Экспорт» — `GET …/goodwill/export` с текущим фильтром
(`sign`); «На весь экран».
#### Scenario: Экспорт с фильтром
- **WHEN** выбран фильтр «Положительные» и нажат «Экспорт»
- **THEN** запрос уходит с `sign=positive`
#### Scenario: Выключение гудвилла
- **WHEN** аналитик выключает переключатель
- **THEN** таблица перечитывается из ответа, под переключателем подпись «Гудвилл не учитывается в КФО», ручной ввод строк не меняется

View File

@ -8,12 +8,12 @@
### Requirement: Вкладки страницы отчёта по версии ### Requirement: Вкладки страницы отчёта по версии
Страница `/groups/:group_id/versions/:version_id/:tab` SHALL показывать шапку версии (ГК, период, статус отчёта) один раз Страница `/groups/:group_id/versions/:version_id/:tab` SHALL показывать шапку версии (ГК, период, статус отчёта) один раз
и под ней ряд вкладок шагов консолидации и содержимое активной вкладки. Активная вкладка определяется сегментом `:tab`: и под ней ряд вкладок шагов консолидации и содержимое активной вкладки. Активная вкладка определяется сегментом `:tab`:
`osv` — «ОСВ» (сводная ОСВ), `chess` — «Шахматка», `correction` — «Таблица корректировки» (спека `corrections-table-ui`). `osv` — «ОСВ» (сводная ОСВ), `chess` — «Шахматка», `correction` — «Таблица корректировки» (спека `corrections-table-ui`), `goodwill` — «Гудвилл» (спека `goodwill-ui`).
Выбор вкладки SHALL менять последний сегмент адреса на ключ вкладки Выбор вкладки SHALL менять последний сегмент адреса на ключ вкладки
(навигация, не локальное состояние) заменой текущей записи истории браузера, без новой записи: кнопка «Назад» в шапке и (навигация, не локальное состояние) заменой текущей записи истории браузера, без новой записи: кнопка «Назад» в шапке и
кнопка «Назад» браузера ведут туда, откуда открыт отчёт, а не по переключениям вкладок (AURORA-1851). Активная вкладка кнопка «Назад» браузера ведут туда, откуда открыт отчёт, а не по переключениям вкладок (AURORA-1851). Активная вкладка
подсвечена зелёным и выделена жирным. Неизвестный ключ вкладки SHALL подсвечена зелёным и выделена жирным. Неизвестный ключ вкладки SHALL
заменяться на `osv` без новой записи в истории браузера. Вкладки «Гудвилл», «КФО», «КФО Общий», заменяться на `osv` без новой записи в истории браузера. Вкладки «КФО», «КФО Общий»,
«Подтверждение КФО клиента» SHALL быть недоступны с подсказкой «Будет в следующих срезах». При смене `version_id` всё «Подтверждение КФО клиента» SHALL быть недоступны с подсказкой «Будет в следующих срезах». При смене `version_id` всё
состояние страницы (карточка версии, данные, фильтры) SHALL создаваться заново. состояние страницы (карточка версии, данные, фильтры) SHALL создаваться заново.
@ -38,6 +38,10 @@
«Шахматка» и нажал «Назад» в шапке «Шахматка» и нажал «Назад» в шапке
- **THEN** одним нажатием открыт список консолидаций ГК - **THEN** одним нажатием открыт список консолидаций ГК
#### Scenario: Переход на гудвилл
- **WHEN** открыта таблица корректировки версии и нажата вкладка «Гудвилл»
- **THEN** адрес становится `…/versions/{id}/goodwill`, шапка версии остаётся, под вкладками показана таблица гудвилла
### Requirement: Таблица шахматки ЮЛ × ЮЛ ### Requirement: Таблица шахматки ЮЛ × ЮЛ
Экран SHALL загружать матрицу при открытии вкладки (`POST /consolidations/versions/{id}/intercompany-matrix`, тело — по требованию «Фильтр счетов Дт и Кт и режимы расчёта») и показывать Экран SHALL загружать матрицу при открытии вкладки (`POST /consolidations/versions/{id}/intercompany-matrix`, тело — по требованию «Фильтр счетов Дт и Кт и режимы расчёта») и показывать
таблицу: первая колонка «ДБ / КТ» (закреплена слева) — компании периметра в порядке ответа (`companies[]`), остальные колонки — таблицу: первая колонка «ДБ / КТ» (закреплена слева) — компании периметра в порядке ответа (`companies[]`), остальные колонки —