From d4e155455038f00a339c6a2adb53345e71503905 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Fri, 6 Nov 2026 19:09:43 +0400 Subject: [PATCH] =?UTF-8?q?=D0=90=D1=80=D1=85=D0=B8=D0=B2=20OpenSpec:=20go?= =?UTF-8?q?odwill-table=20=D0=B8=20goodwill-ui=20(#222,=20#220)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../2026-11-06-goodwill-table}/.openspec.yaml | 0 .../2026-11-06-goodwill-table}/design.md | 0 .../2026-11-06-goodwill-table}/proposal.md | 0 .../specs/goodwill-table/spec.md | 0 .../2026-11-06-goodwill-table}/tasks.md | 0 .../2026-11-06-goodwill-ui}/.openspec.yaml | 0 .../2026-11-06-goodwill-ui}/design.md | 0 .../2026-11-06-goodwill-ui}/proposal.md | 0 .../specs/goodwill-ui/spec.md | 0 .../specs/intercompany-matrix-ui/spec.md | 0 .../2026-11-06-goodwill-ui}/tasks.md | 0 openspec/specs/goodwill-table/spec.md | 104 ++++++++++++++++++ openspec/specs/goodwill-ui/spec.md | 67 +++++++++++ openspec/specs/intercompany-matrix-ui/spec.md | 8 +- 14 files changed, 177 insertions(+), 2 deletions(-) rename openspec/changes/{goodwill-table => archive/2026-11-06-goodwill-table}/.openspec.yaml (100%) rename openspec/changes/{goodwill-table => archive/2026-11-06-goodwill-table}/design.md (100%) rename openspec/changes/{goodwill-table => archive/2026-11-06-goodwill-table}/proposal.md (100%) rename openspec/changes/{goodwill-table => archive/2026-11-06-goodwill-table}/specs/goodwill-table/spec.md (100%) rename openspec/changes/{goodwill-table => archive/2026-11-06-goodwill-table}/tasks.md (100%) rename openspec/changes/{goodwill-ui => archive/2026-11-06-goodwill-ui}/.openspec.yaml (100%) rename openspec/changes/{goodwill-ui => archive/2026-11-06-goodwill-ui}/design.md (100%) rename openspec/changes/{goodwill-ui => archive/2026-11-06-goodwill-ui}/proposal.md (100%) rename openspec/changes/{goodwill-ui => archive/2026-11-06-goodwill-ui}/specs/goodwill-ui/spec.md (100%) rename openspec/changes/{goodwill-ui => archive/2026-11-06-goodwill-ui}/specs/intercompany-matrix-ui/spec.md (100%) rename openspec/changes/{goodwill-ui => archive/2026-11-06-goodwill-ui}/tasks.md (100%) create mode 100644 openspec/specs/goodwill-table/spec.md create mode 100644 openspec/specs/goodwill-ui/spec.md diff --git a/openspec/changes/goodwill-table/.openspec.yaml b/openspec/changes/archive/2026-11-06-goodwill-table/.openspec.yaml similarity index 100% rename from openspec/changes/goodwill-table/.openspec.yaml rename to openspec/changes/archive/2026-11-06-goodwill-table/.openspec.yaml diff --git a/openspec/changes/goodwill-table/design.md b/openspec/changes/archive/2026-11-06-goodwill-table/design.md similarity index 100% rename from openspec/changes/goodwill-table/design.md rename to openspec/changes/archive/2026-11-06-goodwill-table/design.md diff --git a/openspec/changes/goodwill-table/proposal.md b/openspec/changes/archive/2026-11-06-goodwill-table/proposal.md similarity index 100% rename from openspec/changes/goodwill-table/proposal.md rename to openspec/changes/archive/2026-11-06-goodwill-table/proposal.md diff --git a/openspec/changes/goodwill-table/specs/goodwill-table/spec.md b/openspec/changes/archive/2026-11-06-goodwill-table/specs/goodwill-table/spec.md similarity index 100% rename from openspec/changes/goodwill-table/specs/goodwill-table/spec.md rename to openspec/changes/archive/2026-11-06-goodwill-table/specs/goodwill-table/spec.md diff --git a/openspec/changes/goodwill-table/tasks.md b/openspec/changes/archive/2026-11-06-goodwill-table/tasks.md similarity index 100% rename from openspec/changes/goodwill-table/tasks.md rename to openspec/changes/archive/2026-11-06-goodwill-table/tasks.md diff --git a/openspec/changes/goodwill-ui/.openspec.yaml b/openspec/changes/archive/2026-11-06-goodwill-ui/.openspec.yaml similarity index 100% rename from openspec/changes/goodwill-ui/.openspec.yaml rename to openspec/changes/archive/2026-11-06-goodwill-ui/.openspec.yaml diff --git a/openspec/changes/goodwill-ui/design.md b/openspec/changes/archive/2026-11-06-goodwill-ui/design.md similarity index 100% rename from openspec/changes/goodwill-ui/design.md rename to openspec/changes/archive/2026-11-06-goodwill-ui/design.md diff --git a/openspec/changes/goodwill-ui/proposal.md b/openspec/changes/archive/2026-11-06-goodwill-ui/proposal.md similarity index 100% rename from openspec/changes/goodwill-ui/proposal.md rename to openspec/changes/archive/2026-11-06-goodwill-ui/proposal.md diff --git a/openspec/changes/goodwill-ui/specs/goodwill-ui/spec.md b/openspec/changes/archive/2026-11-06-goodwill-ui/specs/goodwill-ui/spec.md similarity index 100% rename from openspec/changes/goodwill-ui/specs/goodwill-ui/spec.md rename to openspec/changes/archive/2026-11-06-goodwill-ui/specs/goodwill-ui/spec.md diff --git a/openspec/changes/goodwill-ui/specs/intercompany-matrix-ui/spec.md b/openspec/changes/archive/2026-11-06-goodwill-ui/specs/intercompany-matrix-ui/spec.md similarity index 100% rename from openspec/changes/goodwill-ui/specs/intercompany-matrix-ui/spec.md rename to openspec/changes/archive/2026-11-06-goodwill-ui/specs/intercompany-matrix-ui/spec.md diff --git a/openspec/changes/goodwill-ui/tasks.md b/openspec/changes/archive/2026-11-06-goodwill-ui/tasks.md similarity index 100% rename from openspec/changes/goodwill-ui/tasks.md rename to openspec/changes/archive/2026-11-06-goodwill-ui/tasks.md diff --git a/openspec/specs/goodwill-table/spec.md b/openspec/specs/goodwill-table/spec.md new file mode 100644 index 0000000..639af23 --- /dev/null +++ b/openspec/specs/goodwill-table/spec.md @@ -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 diff --git a/openspec/specs/goodwill-ui/spec.md b/openspec/specs/goodwill-ui/spec.md new file mode 100644 index 0000000..7716288 --- /dev/null +++ b/openspec/specs/goodwill-ui/spec.md @@ -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** таблица перечитывается из ответа, под переключателем подпись «Гудвилл не учитывается в КФО», ручной ввод строк не меняется diff --git a/openspec/specs/intercompany-matrix-ui/spec.md b/openspec/specs/intercompany-matrix-ui/spec.md index 11ffa8c..f5e957a 100644 --- a/openspec/specs/intercompany-matrix-ui/spec.md +++ b/openspec/specs/intercompany-matrix-ui/spec.md @@ -8,12 +8,12 @@ ### Requirement: Вкладки страницы отчёта по версии Страница `/groups/:group_id/versions/:version_id/:tab` SHALL показывать шапку версии (ГК, период, статус отчёта) один раз и под ней ряд вкладок шагов консолидации и содержимое активной вкладки. Активная вкладка определяется сегментом `:tab`: -`osv` — «ОСВ» (сводная ОСВ), `chess` — «Шахматка», `correction` — «Таблица корректировки» (спека `corrections-table-ui`). +`osv` — «ОСВ» (сводная ОСВ), `chess` — «Шахматка», `correction` — «Таблица корректировки» (спека `corrections-table-ui`), `goodwill` — «Гудвилл» (спека `goodwill-ui`). Выбор вкладки SHALL менять последний сегмент адреса на ключ вкладки (навигация, не локальное состояние) заменой текущей записи истории браузера, без новой записи: кнопка «Назад» в шапке и кнопка «Назад» браузера ведут туда, откуда открыт отчёт, а не по переключениям вкладок (AURORA-1851). Активная вкладка подсвечена зелёным и выделена жирным. Неизвестный ключ вкладки SHALL -заменяться на `osv` без новой записи в истории браузера. Вкладки «Гудвилл», «КФО», «КФО Общий», +заменяться на `osv` без новой записи в истории браузера. Вкладки «КФО», «КФО Общий», «Подтверждение КФО клиента» SHALL быть недоступны с подсказкой «Будет в следующих срезах». При смене `version_id` всё состояние страницы (карточка версии, данные, фильтры) SHALL создаваться заново. @@ -38,6 +38,10 @@ «Шахматка» и нажал «Назад» в шапке - **THEN** одним нажатием открыт список консолидаций ГК +#### Scenario: Переход на гудвилл +- **WHEN** открыта таблица корректировки версии и нажата вкладка «Гудвилл» +- **THEN** адрес становится `…/versions/{id}/goodwill`, шапка версии остаётся, под вкладками показана таблица гудвилла + ### Requirement: Таблица шахматки ЮЛ × ЮЛ Экран SHALL загружать матрицу при открытии вкладки (`POST /consolidations/versions/{id}/intercompany-matrix`, тело — по требованию «Фильтр счетов Дт и Кт и режимы расчёта») и показывать таблицу: первая колонка «ДБ / КТ» (закреплена слева) — компании периметра в порядке ответа (`companies[]`), остальные колонки — -- 2.47.2