Гудвилл: UI-спека goodwill-ui, правки по проверке на стенде
OpenSpec change goodwill-ui (таблица, редактирование, переключатель/фильтр/экспорт; вкладка «Гудвилл» в intercompany-matrix-ui больше не заглушка; R-1…R-3). По стенду: стрелка раскрытия блока — у названия, «Выделить все» в одну строку, текст пустой таблицы. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2f5b455aaa
commit
5f2b0e890f
2
openspec/changes/goodwill-ui/.openspec.yaml
Normal file
2
openspec/changes/goodwill-ui/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-11-06
|
||||||
26
openspec/changes/goodwill-ui/design.md
Normal file
26
openspec/changes/goodwill-ui/design.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
Экран — по контракту `../docs/dev/api-contract-goodwill.md`, DOC-259025 §4–§6 и макету `[31-03]`, `[31-03-01]`. Суммы —
|
||||||
|
строки, арифметика только в копейках (`utils/money.ts`), чистые функции вкладки — `utils/goodwill.ts` с vitest.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- Итоги «Всего» и «Итого по листу» берутся из ответа (`groups[].totals`, `totals`), фронт их не считает.
|
||||||
|
- В режиме редактирования P и G строки — предпросмотр (`previewAmounts`, та же формула и округление, что на бэке).
|
||||||
|
- Сохранение — один `PUT …/goodwill` с изменёнными строками (`changedRows`); ошибка строки (422 с ключом пары)
|
||||||
|
подсвечивает поле этой строки, режим не закрывается.
|
||||||
|
- Переключатель `goodwill_enabled` меняется вне режима редактирования, отдельным запросом.
|
||||||
|
|
||||||
|
## Decisions made without discussion
|
||||||
|
|
||||||
|
Ревью arm-core #220 (06.11.2026, владелец: «давай всё сделаем сами»).
|
||||||
|
|
||||||
|
- **R-1 (2026-11-06). Правки вне задачи в PR откатены к `main`.** Обработка ошибок в 8 файлах справочников, `Alert` в
|
||||||
|
`CatalogueJurTable`, удалённые комментарии в `types/api.ts`, шахматке и корректировках. Обоснование: не относятся к
|
||||||
|
AURORA-1544, давали ложный тост на незаполненной форме (`validateFields` бросает не `Error`) и конфликтовали с #219/#221.
|
||||||
|
Оставлены общий фильтр единиц и `useFullscreen` (используется только вкладкой гудвилла). Цена ошибки: если правки нужны —
|
||||||
|
отдельной задачей.
|
||||||
|
- **R-2 (2026-11-06). Итоги — только с бэка** (правило владельца «агрегаты отдаёт бэк»). Цена ошибки: нет.
|
||||||
|
- **R-3 (2026-11-06). Предпросмотр — только у строк;** «Всего» и «Итого по листу» в режиме правки показывают сохранённое
|
||||||
|
и обновляются после «Сохранить». Обоснование: агрегаты не считаем на фронте (R-2). Цена ошибки: аналитик видит итог
|
||||||
|
после сохранения, а не во время ввода (DOC-259025 §5.1 п. 3 говорит «предварительные … итоги»).
|
||||||
29
openspec/changes/goodwill-ui/proposal.md
Normal file
29
openspec/changes/goodwill-ui/proposal.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
AURORA-1544: вкладка «Гудвилл» страницы отчёта версии. Первая версия экрана (PR arm-core #220, Голов) строилась на
|
||||||
|
фейковых данных по черновику контракта 30.10, а бэкенд (#222, AURORA-1543) сделан по аналитике DOC-259025 «Гудвилл»
|
||||||
|
(Лысенко, 03.11.2026): пары по фактическим вложениям, строки «Нет вложений», nullable суммы, предупреждения, итоги по ГО,
|
||||||
|
пакетное сохранение. Ревью 06.11.2026: экран переведён на фактический API и DOC-259025.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
Репозиторий — arm-core, только `web/`. Контракт — `../docs/dev/api-contract-goodwill.md` (docs #204).
|
||||||
|
|
||||||
|
- Вкладка «Гудвилл» (`…/versions/{id}/goodwill`) доступна.
|
||||||
|
- Таблица по макету `[31-03]`: блоки ГО, строки ДО, «Всего» блока и «Итого по листу» из ответа (`groups`, `totals`).
|
||||||
|
- Режим «Редактировать» (`[31-03-01]`): галочки, доля, комментарий, предпросмотр P/G, пакетное сохранение.
|
||||||
|
- Переключатель «Учитывать гудвилл в КФО» (`goodwill_enabled`, OQ-27), фильтр знака, экспорт с фильтром.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
- `goodwill-ui`: вкладка «Гудвилл».
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
- `intercompany-matrix-ui`: вкладка «Гудвилл» больше не заглушка.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
`web/src/widgets/ConsolidationGoodwill/*`, `web/src/utils/goodwill.ts` (+ тесты), `web/src/types/goodwill/goodwill.ts`,
|
||||||
|
`web/src/services/api/goodwill.ts`, `web/src/hooks/useFullScreen.tsx`, общий фильтр единиц
|
||||||
|
`web/src/components/ConsolidationFormatTableFilter` (шахматка переведена на него), вкладки отчёта.
|
||||||
62
openspec/changes/goodwill-ui/specs/goodwill-ui/spec.md
Normal file
62
openspec/changes/goodwill-ui/specs/goodwill-ui/spec.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
## ADDED 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** таблица перечитывается из ответа, под переключателем подпись «Гудвилл не учитывается в КФО», ручной ввод строк не меняется
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Вкладки страницы отчёта по версии
|
||||||
|
Страница `/groups/:group_id/versions/:version_id/:tab` SHALL показывать шапку версии (ГК, период, статус отчёта) один раз
|
||||||
|
и под ней ряд вкладок шагов консолидации и содержимое активной вкладки. Активная вкладка определяется сегментом `:tab`:
|
||||||
|
`osv` — «ОСВ» (сводная ОСВ), `chess` — «Шахматка», `correction` — «Таблица корректировки» (спека `corrections-table-ui`), `goodwill` — «Гудвилл» (спека `goodwill-ui`).
|
||||||
|
Выбор вкладки SHALL менять последний сегмент адреса на ключ вкладки
|
||||||
|
(навигация, не локальное состояние) заменой текущей записи истории браузера, без новой записи: кнопка «Назад» в шапке и
|
||||||
|
кнопка «Назад» браузера ведут туда, откуда открыт отчёт, а не по переключениям вкладок (AURORA-1851). Активная вкладка
|
||||||
|
подсвечена зелёным и выделена жирным. Неизвестный ключ вкладки SHALL
|
||||||
|
заменяться на `osv` без новой записи в истории браузера. Вкладки «КФО», «КФО Общий»,
|
||||||
|
«Подтверждение КФО клиента» SHALL быть недоступны с подсказкой «Будет в следующих срезах». При смене `version_id` всё
|
||||||
|
состояние страницы (карточка версии, данные, фильтры) SHALL создаваться заново.
|
||||||
|
|
||||||
|
#### Scenario: Переход на шахматку
|
||||||
|
- **WHEN** открыта сводная ОСВ версии по адресу `…/versions/{id}/osv` и нажата вкладка «Шахматка»
|
||||||
|
- **THEN** адрес становится `…/versions/{id}/chess`, шапка версии остаётся, под вкладками показана шахматка
|
||||||
|
|
||||||
|
#### Scenario: Переход на таблицу корректировки
|
||||||
|
- **WHEN** открыта шахматка версии по адресу `…/versions/{id}/chess` и нажата вкладка «Таблица корректировки»
|
||||||
|
- **THEN** адрес становится `…/versions/{id}/correction`, шапка версии остаётся, под вкладками показана таблица корректировок
|
||||||
|
|
||||||
|
#### Scenario: Прежний адрес сводной ОСВ
|
||||||
|
- **WHEN** открыт адрес `…/versions/{id}/osv`
|
||||||
|
- **THEN** показана сводная ОСВ, вкладка «ОСВ» активна
|
||||||
|
|
||||||
|
#### Scenario: Неизвестная вкладка
|
||||||
|
- **WHEN** открыт адрес `…/versions/{id}/abc`
|
||||||
|
- **THEN** адрес заменяется на `…/versions/{id}/osv`, показана сводная ОСВ; кнопка «Назад» браузера не возвращает на `…/abc`
|
||||||
|
|
||||||
|
#### Scenario: Назад после переключения вкладок
|
||||||
|
- **WHEN** аналитик открыл отчёт версии кнопкой «К отчётности» в списке консолидаций, переключился «Шахматка» → «ОСВ» →
|
||||||
|
«Шахматка» и нажал «Назад» в шапке
|
||||||
|
- **THEN** одним нажатием открыт список консолидаций ГК
|
||||||
|
|
||||||
|
#### Scenario: Переход на гудвилл
|
||||||
|
- **WHEN** открыта таблица корректировки версии и нажата вкладка «Гудвилл»
|
||||||
|
- **THEN** адрес становится `…/versions/{id}/goodwill`, шапка версии остаётся, под вкладками показана таблица гудвилла
|
||||||
6
openspec/changes/goodwill-ui/tasks.md
Normal file
6
openspec/changes/goodwill-ui/tasks.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
## 1. Доводка по ревью (план docs/superpowers/plans/2026-11-06-goodwill-review-front.md)
|
||||||
|
|
||||||
|
- [x] 1.1 Ветка без правок вне задачи, влит main (R-1).
|
||||||
|
- [x] 1.2 Типы и запросы по API бэка, чистые функции `utils/goodwill.ts` с тестами.
|
||||||
|
- [x] 1.3 Вкладка по API, DOC-259025 и макету.
|
||||||
|
- [x] 1.4 UI-спека `goodwill-ui`, проверка на стенде.
|
||||||
@ -66,12 +66,13 @@ export const ConsolidationGoodwillTable: React.FC<ConsolidationGoodwillTableI> =
|
|||||||
rowKey='key'
|
rowKey='key'
|
||||||
pagination={false}
|
pagination={false}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
locale={{ emptyText: 'Вложений по 58.01 между компаниями периметра нет' }}
|
||||||
scroll={{ x: 'max-content', y: isFullscreen ? 'calc(100vh - 200px)' : undefined }}
|
scroll={{ x: 'max-content', y: isFullscreen ? 'calc(100vh - 200px)' : undefined }}
|
||||||
size='small'
|
size='small'
|
||||||
bordered
|
bordered
|
||||||
title={() => toolbar}
|
title={() => toolbar}
|
||||||
key={treeData.map((g) => g.key).join('|')}
|
key={treeData.map((g) => g.key).join('|')}
|
||||||
expandable={{ defaultExpandAllRows: true }}
|
expandable={{ defaultExpandAllRows: true, expandIconColumnIndex: 1 }}
|
||||||
summary={() => <ConsolidationGoodwillSummary totals={data?.totals ?? null} columns={columns} units={units} decimals={decimals} />}
|
summary={() => <ConsolidationGoodwillSummary totals={data?.totals ?? null} columns={columns} units={units} decimals={decimals} />}
|
||||||
rowClassName={rowClassName}
|
rowClassName={rowClassName}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -88,7 +88,7 @@ export const getGoodwillColumns = ({
|
|||||||
{
|
{
|
||||||
title: 'Учет в отчете',
|
title: 'Учет в отчете',
|
||||||
key: 'include_in_kfo',
|
key: 'include_in_kfo',
|
||||||
width: 120,
|
width: 150,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (_, node) => {
|
render: (_, node) => {
|
||||||
if (node.kind === 'total') return <Text strong>—</Text>;
|
if (node.kind === 'total') return <Text strong>—</Text>;
|
||||||
@ -101,7 +101,7 @@ export const getGoodwillColumns = ({
|
|||||||
checked={active.length > 0 && checked === active.length}
|
checked={active.length > 0 && checked === active.length}
|
||||||
indeterminate={checked > 0 && checked < active.length}
|
indeterminate={checked > 0 && checked < active.length}
|
||||||
onChange={(e) => onToggleGroup(node.headId, e.target.checked)}>
|
onChange={(e) => onToggleGroup(node.headId, e.target.checked)}>
|
||||||
<Text type='secondary' style={{ fontSize: 12 }}>
|
<Text type='secondary' style={{ fontSize: 12, whiteSpace: 'nowrap' }}>
|
||||||
Выделить все
|
Выделить все
|
||||||
</Text>
|
</Text>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user