docs(openspec): intercompany-matrix-ui — целевое поведение экрана (ревью #158, раунд 3)

Итоги отдаёт бэкенд (AURORA-1756/1757), результирующая таблица и её выгрузка,
«руб.», точное округление, одна сторона Дт/Кт без запроса, правило отметок
дерева по полному плану счетов, отдельные флаги выгрузки, полноэкранный режим.
Known issues — по состоянию 601a125.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-22 16:30:01 +04:00
parent 718d83d6d1
commit b8f0672029
4 changed files with 176 additions and 80 deletions

View File

@ -1,55 +1,55 @@
## Context
Экран «Шахматка» [31-01] добавлен в `arm-core/web` PR #158 (ветка `feat/chessTable`) как вторая вкладка страницы отчёта по
версии. Страница `layouts/ConsolidatedOsvPage.tsx` разделена: общую шапку и вкладки держит `layouts/ConsolidatioReportPage.tsx`,
содержимое вкладок — `widgets/ConsolidatedOsvContent` (прежняя сводная ОСВ без изменений поведения) и
`widgets/ConsolidatedChessContent`. Бэкенд шахматки — change `2026-10-16-intercompany-matrix`; расчёт — чистая функция
версии, ничего не хранится.
Экран «Шахматка» [31-01] добавлен в `arm-core/web` PR #158 (ветка `feat/chessTable`, AURORA-1538) как вторая вкладка страницы
отчёта по версии. Страница `layouts/ConsolidatedOsvPage.tsx` разделена: общую шапку и вкладки держит
`layouts/ConsolidationReportPage.tsx`, содержимое вкладок — `widgets/ConsolidatedOsvContent` (прежняя сводная ОСВ без изменений
поведения) и `widgets/ConsolidatedChessContent` (матрица `ConsolidationChessTable` и результирующая таблица
`ConsolidationChessResTable`). Бэкенд — change `2026-10-16-intercompany-matrix` (матрица, результирующая таблица) и
`2026-10-22-intercompany-result-table-export` (выгрузка результирующей таблицы); расчёт — чистая функция версии, ничего не
хранится.
## Decisions (как устроено в ветке)
## Decisions
- **D-1. Маршрут `:tab`.** `OsvTabs` берёт активную вкладку из `useParams().tab` (по умолчанию `osv`) и по выбору заменяет
последний сегмент адреса. Страница выбирает содержимое по таблице `tab → компонент`; неизвестный `tab` даёт пустое содержимое.
последний сегмент адреса. Неизвестный `tab` заменяется на `osv` (`navigate(…, {replace: true})`).
- **D-2. Ключ версии.** Страница отчёта разделена на обёртку с `key={version_id}` и содержимое: при смене версии карточка,
фильтры и данные создаются заново (как было в `ConsolidatedOsvPage`).
- **D-3. Режим `all` / `manual` выводится из выбора счетов:** `manual` только при непустых Дт и Кт (иначе бэкенд ответит 422),
в остальных случаях `all`. Один и тот же признак выбирает маршрут экспорта.
- **D-3. Режим расчёта из выбора счетов:** `manual` — при непустых Дт и Кт, `all` — при пустых обоих; при одной выбранной
стороне запроса нет (бэкенд ответил бы 422 на `manual`, а `all` показал бы не то, что выбрано). Тот же признак выбирает
маршрут экспорта.
- **D-4. Единицы, знаки и фильтр компаний — на клиенте.** Запрос повторяется только при смене выбора счетов или версии.
Единицы — сдвиг запятой в десятичной строке (`utils/chessTableCellFormatValue.ts`), `Intl.NumberFormat` получает строку и
форматирует её как точное десятичное (без float).
- **D-5. План счетов** — `GET /directories/accounts` целиком, деревья Дт и Кт строятся на клиенте по `parent_id`.
- **D-6. `apiFetchBlob(path, init)`** принимает `RequestInit`, чтобы выгружать POST-ом с телом; без `init` поведение прежнее.
- **D-7. Полноэкранный режим** — Fullscreen API на контейнере блока шахматки, состояние следует за `fullscreenchange`.
- **D-7. Полноэкранный режим** — Fullscreen API на контейнере блока, состояние следует за `fullscreenchange`; выпадающие окна
antd рендерятся в `document.fullscreenElement` (`ConfigProvider getPopupContainer`).
- **D-8. Итоги отдаёт бэкенд.** Решение владельца 22.10.2026: все агрегаты (строки «Итого») считает API, фронт сумм не
складывает. Пока API их не отдаёт, строк «Итого» под матрицей и под результирующей таблицей нет; колонка «Итого» результирующей
таблицы — `group_total` из ответа. Итоги в API и правило суммирования — AURORA-1756 (бэк, ждёт аналитику AURORA-1373), вывод на
экран — AURORA-1757.
## Known issues
Открытые проблемы ветки, которые исправляет автор PR; спека их НЕ закрепляет как требования.
Открытые проблемы ветки на 601a125 (ревью, раунд 3), которые исправляет автор PR; спека описывает целевое поведение.
1. Глобальный CSS (`App.css`, `ConsolidatedChessTable.css`, `TreeFilterDropdown.css`) с `!important` на классах antd — влияет на
все таблицы и деревья приложения; стили должны быть локальными (CSS-модули или `className` экрана).
2. В полноэкранном режиме выпадающие списки фильтров (Компании, Дт, Кт, Единицы, Знаки) не видны: popup рендерится в `body`,
вне полноэкранного элемента; нужен `getPopupContainer` на контейнер шахматки.
3. Суммы форматируются через `number` / `parseFloat` / `toFixed` (float в денежном домене), без разделителей тысяч и без «руб.»;
`utils/money.ts` работает со строками — использовать его. Деление на единицы делать над десятичной строкой.
4. Строка «Итого» суммирует расхождения (`variance_amount`), а в макете 31-01 в итогах — верхние числа (суммы стороны A); итоги нужны
по каждой из трёх величин либо по макету. Знак `(…)` разбирается эвристикой `parseAmount`, хотя API отдаёт десятичные строки.
5. Подписи дочерних счетов в выборе Дт/Кт — UUID: `Select` получает `options` только корневых узлов, выбранные дочерние ключи
выводятся как идентификаторы; нужны подписи «код - название» для любых узлов.
6. Выбор счетов только с одной стороны молча даёт режим `all`: пользователь видит выбранный счёт, но данные по всем счетам;
нужны подсказка/блокировка либо явное поведение.
7. Гонка ответов: `loadData` не отменяет и не отбрасывает устаревшие ответы (последний пришедший побеждает, не последний
запрошенный); нужен `AbortController` или счётчик запросов. Эффект зависит от `loadData`/`notification` не по правилам хуков.
8. Нет результирующей таблицы ВГО (`GET …/intercompany-matrix/result-table`), пресетов выбора счетов и выбора года/квартала
(заглушки `disabled` в панели фильтров), экран макета 31-01 реализован не полностью.
9. Прочее по ревью: `any` в таблице и дереве, `console.log` в обработчике полноэкранного режима, ошибки не `ApiError` молча
глотаются (`loadData`, `download`), экспорт не передаёт единицы/знаки/компании, пустой `Select` для года/квартала,
опечатки в именах (`ConsolidatioReportPage`, `consodilation`, `expoerLoading`).
После исправления дефектов требования соответствующих разделов спеки уточняются отдельным change.
1. Строки «Итого» под матрицей и под результирующей таблицей считаются на клиенте (`sumAmounts`) — убрать (D-8).
2. Поиск в дереве счетов: отметки считаются по отфильтрованному дереву, отметка единственного видимого субсчёта отмечает
родителя, и на бэк уходит весь родительский счёт (`TreeFilterDropdown.tsx`).
3. Выбрана одна сторона Дт/Кт: таблица показывает прежние данные, подсказка — всплывающее уведомление и постоянный текст внутри
выпадающего окна, «Экспорт» активен и выгружает режим `all`.
4. Стили: правило тегов в `App.css` и класс на форме ЮЛ (`CatalogueJurForm.tsx`) — вне экрана; у селекта Кт класса нет;
`.resRow`, `.empty-cell` не под `.chessTable`; у `Tree` снят `className='filterTree'`.
5. Желательно: запасные имена файлов (`exportFilename` добавляет `Сводная_ОСВ_`) и общий `exportLoading` на две кнопки;
нечисловое значение в тыс./млн/млрд искажается (`0.abc`); `sumAmounts(...) ?? '0'`; `as any` в форматтере; мёртвый код
(скобки в форматтере, `formatted === '—'`); ячейки по макету (скобки у расхождения, «—», значок `skipped_osv_entry_count`);
заглушки «Год»/«Квартал»; `services/consodilation`, типы вне `types/api.ts`, `withJsonBody`, `Account.kind`.
## Decisions made without discussion
- **2026-10-21 (а). Спеку пишет ревьюер в ветке автора** — по решению владельца (разрешено явно); альтернатива — оставить
автору. Цена ошибки: автор может не согласиться с формулировками; правится одним коммитом в этом же change.
- **2026-10-21 (б). Спека описывает фактическое поведение, дефекты вынесены в «Known issues».** Обоснование: спека — единственный
- **2026-10-21 (б). Спека описывает фактическое поведение, дефекты вынесены в «Known issues».** Отменено 22.10.2026 (е). Обоснование: спека — единственный
источник истины при рулингах, а закреплять баги требованиями нельзя; иначе их закрытие превратится в «нарушение спеки». Цена
ошибки: отдельные требования (например, режим `all` при выборе одной стороны, «Итого» из расхождений) описаны как есть
и после исправления потребуют MODIFIED.
@ -65,3 +65,18 @@
- **2026-10-21 (д). Дельта к `consolidated-osv-ui` не делается:** вкладки описаны в новой спеке, текст
`consolidated-osv-ui` не правился, чтобы не смешивать UI-половины. Цена ошибки: при архивации две спеки частично описывают
один компонент (`OsvTabs`).
- **2026-10-22 (е). Спека переписана на целевое поведение** (раунд 3 ревью): владелец решил, что спеки и тесты чужих PR ведём
мы, а автор правит продуктовый код; целевая спека даёт автору однозначный ориентир, расхождения кода — в «Known issues».
Цена ошибки: до исправлений спека и код ветки расходятся; PR не сливается, пока «Known issues» 1–4 не закрыты.
- **2026-10-22 (ж). Выбрана одна сторона Дт/Кт — пустая таблица с текстом** «Для расчёта по счетам выберите счета и в Дт,
и в Кт» у таблицы и у фильтров, «Экспорт» матрицы недоступен. Альтернатива — оставить прежние данные с подсказкой; отвергнута:
на экране были бы данные, не соответствующие фильтру. Цена ошибки: правка текста и одного условия.
- **2026-10-22 (з). Правило отметок дерева по полному плану счетов** (родитель отмечен ⇔ отмечены все потомки), в том числе при
поиске. Обоснование: бэкенд разворачивает выбранный счёт в потомков, лишняя отметка родителя меняет расчёт. Цена ошибки: если
аналитик захочет «родитель + выбранные субсчета» как отдельный режим — MODIFIED требования.
- **2026-10-22 (и). Запасные имена файлов** `Шахматка_<ГК>_<год>_<квартал>.xlsx` и
`Результирующая_таблица_ВГО_<ГК>_<год>_<квартал>.xlsx`. Цена ошибки: низкая, имя из `Content-Disposition` приоритетнее.
- **2026-10-22 (к). Тесты форматтера** `utils/chessTableCellFormatValue.test.ts` написаны ревьюером: проверяют сдвиг единиц,
точное округление, большие суммы, «-0,00» и пустые значения; случай нечислового значения в тыс./млн/млрд не покрыт — он
в Known issues (5) и тест добавляется вместе с исправлением.
- **2026-10-22 (л). В ветку влит `main`** (PR #159), чтобы выгрузка результирующей таблицы работала на стенде из ветки.

View File

@ -2,27 +2,30 @@
В ветке `feat/chessTable` (PR RSHB/arm-core#158, автор — Голов) во фронте появился экран «Шахматка» — вкладка страницы
отчёта по версии консолидации рядом со «Сводной ОСВ». Бэкенд шахматки (`GET/POST …/intercompany-matrix/*`) уже в main
(change `2026-10-16-intercompany-matrix`), у экрана спеки нет. Спеку пишет ревьюер в ветке автора по решению владельца
(21.10.2026): она фиксирует фактическое поведение экрана и отделяет от него открытые дефекты (`design.md`, «Known issues»),
чтобы дефекты не закрепились требованием.
(change `2026-10-16-intercompany-matrix`), у экрана спеки нет. Спеку и тесты ведёт ревьюер в ветке автора по решению
владельца (21–22.10.2026): спека задаёт целевое поведение экрана, расхождения кода ветки с ней — `design.md`, «Known issues».
## What Changes
Репозиторий — только arm-core (`web/`). Экран «Шахматка» [31-01], эндпоинты arm-core:
`GET …/intercompany-matrix/all`, `POST …/intercompany-matrix/manual`, `GET …/intercompany-matrix/all/export`,
`POST …/intercompany-matrix/manual/export`, `GET /directories/accounts`.
`POST …/intercompany-matrix/manual/export`, `GET …/intercompany-matrix/result-table`,
`GET …/intercompany-matrix/result-table/export`, `GET /directories/accounts`.
- Маршрут `/groups/:group_id/versions/:version_id/:tab`: страница отчёта (`ConsolidatioReportPage`) с общей шапкой и
- Маршрут `/groups/:group_id/versions/:version_id/:tab`: страница отчёта (`ConsolidationReportPage`) с общей шапкой и
вкладками (`OsvTabs`); `tab = osv` — сводная ОСВ, `tab = chess` — шахматка. Прежний адрес `…/osv` продолжает работать.
- Таблица ЮЛ × ЮЛ: в ячейке три числа (сумма A, сумма Б, расхождение), строка «Итого».
- Таблица ЮЛ × ЮЛ: в ячейке три числа (сумма A, сумма Б, расхождение); итоги считает бэкенд (пока API их не отдаёт —
строки «Итого» нет).
- Результирующая таблица ВГО по статьям с колонкой «Итого» (`group_total`) и выгрузкой в XLSX.
- Фильтры: компании, счета Дт, счета Кт (деревом), единицы, знаков после запятой; режимы расчёта `all` / `manual`.
- Экспорт в XLSX (`all` / `manual`), полноэкранный режим.
- Экспорт в XLSX (`all` / `manual`, результирующая таблица), полноэкранный режим.
- Новая спека `intercompany-matrix-ui` (ADDED Requirements).
## Non-goals
- Исправление дефектов экрана (перечень — `design.md`, «Known issues»): они остаются автору PR.
- Результирующая таблица ВГО (`GET …/intercompany-matrix/result-table`) и пресеты счетов — во фронте не реализованы.
- Исправление продуктового кода по «Known issues» (`design.md`) — за автором PR; спеку и тесты ведёт ревьюер.
- Пресеты счетов — во фронте не реализованы.
- Итоги «Итого» в API и их вывод — AURORA-1756, AURORA-1757.
- Выбор года и квартала на экране: селекторы заглушены (`disabled`), версия задаётся адресом.
- Изменения бэкенда и DBML: шахматка — чистая функция от версии, ничего не хранится (DECISION-5).
- Вкладки «Таблица корректировки», «Гудвилл», «КФО», «КФО Общий», «Подтверждение КФО клиента» — недоступны.
@ -38,8 +41,9 @@
## Impact
- `web/src/App.tsx`, `web/src/layouts/{ConsolidatioReportPage,index}.ts(x)`, `web/src/widgets/ConsolidatedOsv/OsvTabs.tsx`,
- `web/src/App.tsx`, `web/src/layouts/{ConsolidationReportPage,index}.ts(x)`, `web/src/widgets/ConsolidatedOsv/OsvTabs.tsx`,
`web/src/widgets/ConsolidatedOsvContent/`, `web/src/widgets/ConsolidatedChessContent/`,
`web/src/widgets/ConsolidationChessTable/`, `web/src/components/TreeFilterDropdown/`,
`web/src/services/consodilation/chess.ts`, `web/src/types/chess/chess.ts`, `web/src/api/client.ts` (`apiFetchBlob` принимает `init`).
`web/src/widgets/ConsolidationChessTable/`, `web/src/widgets/ConsolidationChessResTable/`,
`web/src/widgets/ConsolidationChessFormatTableFilter/`, `web/src/components/TreeFilterDropdown/`,
`web/src/utils/chessTableCellFormatValue.ts`, `web/src/services/consodilation/intercompanyMatrix.ts`, `web/src/types/chess/chess.ts`, `web/src/api/client.ts` (`apiFetchBlob` принимает `init`).
- Права: экран читает данные под `CONSOLIDATION_VIEW`; меню по правам не скрывается.

View File

@ -4,9 +4,10 @@
Страница `/groups/:group_id/versions/:version_id/:tab` SHALL показывать шапку версии (ГК, период, статус отчёта) один раз
и под ней ряд вкладок шагов консолидации и содержимое активной вкладки. Активная вкладка определяется сегментом `:tab`:
`osv` — «ОСВ» (сводная ОСВ), `chess` — «Шахматка». Выбор вкладки SHALL менять последний сегмент адреса на ключ вкладки
(навигация, не локальное состояние), активная вкладка подсвечена зелёным и выделена жирным. Вкладки «Таблица корректировки»,
«Гудвилл», «КФО», «КФО Общий», «Подтверждение КФО клиента» SHALL быть недоступны с подсказкой «Будет в следующих срезах».
При смене `version_id` всё состояние страницы (карточка версии, данные, фильтры) SHALL создаваться заново.
(навигация, не локальное состояние), активная вкладка подсвечена зелёным и выделена жирным. Неизвестный ключ вкладки SHALL
заменяться на `osv` без новой записи в истории браузера. Вкладки «Таблица корректировки», «Гудвилл», «КФО», «КФО Общий»,
«Подтверждение КФО клиента» SHALL быть недоступны с подсказкой «Будет в следующих срезах». При смене `version_id` всё
состояние страницы (карточка версии, данные, фильтры) SHALL создаваться заново.
#### Scenario: Переход на шахматку
- **WHEN** открыта сводная ОСВ версии по адресу `…/versions/{id}/osv` и нажата вкладка «Шахматка»
@ -16,14 +17,18 @@
- **WHEN** открыт адрес `…/versions/{id}/osv`
- **THEN** показана сводная ОСВ, вкладка «ОСВ» активна
#### Scenario: Неизвестная вкладка
- **WHEN** открыт адрес `…/versions/{id}/abc`
- **THEN** адрес заменяется на `…/versions/{id}/osv`, показана сводная ОСВ; кнопка «Назад» браузера не возвращает на `…/abc`
### Requirement: Таблица шахматки ЮЛ × ЮЛ
Экран SHALL загружать матрицу при открытии вкладки (`GET /consolidations/versions/{id}/intercompany-matrix/all`) и показывать
таблицу: первая колонка «ДБ / КТ» (закреплена слева) — компании периметра в порядке ответа (`companies[]`), остальные колонки —
те же компании. В ячейке пары (строка A, колонка Б) SHALL быть три числа друг под другом: сумма `amount_a`, сумма `amount_b`
и расхождение `variance_amount`; расхождение выделяется красным, если `exceeds_threshold = true`. Если ячейки пары в ответе нет
(в том числе на диагонали), SHALL показываться прочерк «—» на приглушённом фоне. Ниже таблицы SHALL закрепляться строка «Итого»
с одним числом по каждой колонке. Пока идёт загрузка, таблица показывает индикатор загрузки; ошибка API показывается
уведомлением с текстом сервера.
(в том числе на диагонали), SHALL показываться прочерк «—» на приглушённом фоне. Итоговые суммы (строка «Итого») экран SHALL
NOT вычислять сам: агрегаты отдаёт бэкенд, пока API их не возвращает — строки «Итого» под матрицей нет. Пока идёт загрузка,
таблица показывает индикатор загрузки; ошибка API показывается уведомлением с текстом сервера.
#### Scenario: Ячейка с расхождением выше порога
- **WHEN** для пары `(A, Б)` сервер вернул `exceeds_threshold = true`
@ -33,36 +38,79 @@
- **WHEN** в `cells[]` нет ячейки для пары компаний
- **THEN** на их пересечении показан прочерк «—»
#### Scenario: Итоги не считаются на клиенте
- **WHEN** матрица загружена
- **THEN** под таблицей нет строки «Итого»; ни одна сумма на экране не получена сложением сумм ответа на клиенте
### Requirement: Результирующая таблица ВГО
Под матрицей экран SHALL показывать результирующую таблицу ВГО, загруженную при открытии вкладки
(`GET /consolidations/versions/{id}/intercompany-matrix/result-table`): первая колонка «Статья» — статьи ответа (`articles[]`,
`name`) в порядке ответа, далее колонки компаний (`companies[]`) с суммой статьи по компании (`amounts[]` по `company_id`) и
колонка «Итого» со значением `group_total` из ответа. Строки «Итого» под таблицей нет, пока API не отдаёт итог по статьям
(клиент суммы не складывает). У таблицы свои выбор единиц и знаков (по правилам требования «Единицы и знаки», независимо от
матрицы), кнопка «Экспорт» и кнопка «На весь экран». Пока идёт загрузка, таблица показывает индикатор; ошибка API (в том числе
422 `RESULT_ACCOUNTS_MISSING`) показывается уведомлением с текстом сервера.
#### Scenario: Колонка «Итого» из ответа
- **WHEN** сервер вернул статью «Дебиторская задолженность» с `group_total = "1296510.00"`, выбраны «тыс. руб.» и 2 знака
- **THEN** в колонке «Итого» этой статьи показано `1 296,51`
#### Scenario: Нет обязательного счёта
- **WHEN** сервер ответил 422 `RESULT_ACCOUNTS_MISSING`
- **THEN** показано уведомление с текстом сервера, матрица шахматки при этом показывается как обычно
### Requirement: Единицы и знаки
Экран SHALL позволять выбрать единицы суммы — «тыс. руб.» (по умолчанию), «млн. руб.», «млрд руб.» — и число знаков после
запятой 0–3 (по умолчанию 2). Выбор применяется на клиенте к уже загруженным значениям (повторного запроса не делает):
значение делится на 1000 / 1 000 000 / 1 000 000 000 и показывается с выбранным числом знаков, десятичный разделитель — запятая.
Значение, которое не удаётся прочитать как число, SHALL показываться как пришло.
Экран SHALL позволять выбрать единицы суммы — «руб.», «тыс. руб.» (по умолчанию), «млн. руб.», «млрд руб.» — и число знаков
после запятой 0–3 (по умолчанию 2). Выбор применяется на клиенте к уже загруженным значениям (повторного запроса не делает):
запятая сдвигается в десятичной строке на 3 / 6 / 9 разрядов, значение округляется до выбранного числа знаков (половина —
от нуля) без перевода в число с плавающей точкой и показывается с разделителем разрядов и десятичной запятой. Значение,
округлённое до нуля, показывается без минуса. Пустое значение — пустая ячейка. Значение, которое не удаётся прочитать как
число, SHALL показываться как пришло — при любых единицах.
#### Scenario: Смена единиц
- **WHEN** выбраны «млн. руб.» и 1 знак, а сервер вернул `amount_a = "2346459.05"`
- **THEN** в ячейке показано `2,3`
#### Scenario: Точное округление
- **WHEN** выбраны «тыс. руб.» и 2 знака, а сервер вернул `"1005"`
- **THEN** показано `1,01` (не `1,00`, как дало бы округление числа с плавающей точкой)
#### Scenario: Маленькая отрицательная сумма
- **WHEN** выбраны «тыс. руб.» и 2 знака, а сервер вернул `"-4.00"`
- **THEN** показано `0,00`, а не `-0,00`
### Requirement: Фильтр компаний
Выбор «Компании» (множественный, пустой выбор — «Все компании») SHALL ограничивать таблицу на клиенте: остаются строки и
Выбор «Компании» (множественный, пустой выбор — «Все компании») SHALL ограничивать матрицу на клиенте: остаются строки и
колонки выбранных компаний и ячейки, у которых обе компании выбраны. Запрос на сервер при этом не отправляется. Список
вариантов — компании последнего загруженного ответа. Выпадающее окно выбора — дерево с поиском по названию, кнопками
применения и сброса.
#### Scenario: Две компании из периметра
- **WHEN** выбраны две компании из пяти
- **THEN** таблица содержит две строки и две колонки, и «Итого» считается по оставшимся ячейкам
- **THEN** матрица содержит две строки и две колонки
### Requirement: Фильтр счетов Дт и Кт и режимы расчёта
Экран SHALL загружать план счетов `GET /directories/accounts` один раз при открытии и строить из него два дерева по иерархии
`parent_id`: дебетовые счета (`is_debit`) и кредитовые (`is_credit`); подпись узла — «код - название». Выбор счетов Дт и Кт —
множественный, пустой выбор — «Все счета».
`parent_id`: дебетовые счета (`is_debit`) и кредитовые (`is_credit`); подпись узла — «код - название», в том числе у выбранных
дочерних счетов в поле выбора. Выбор счетов Дт и Кт — множественный, пустой выбор — «Все счета».
Режим расчёта определяется выбором: если выбраны счета и в Дт, и в Кт, SHALL выполняться
`POST …/intercompany-matrix/manual` с `{debit_account_ids, credit_account_ids}` (режим `manual`); в остальных случаях
(ничего не выбрано либо выбрана только одна сторона) SHALL выполняться `GET …/intercompany-matrix/all` (режим `all`). Каждое
изменение выбора счетов перезапрашивает матрицу. Кнопка «Сбросить фильтры» появляется, когда выбрано что-либо в компаниях, Дт
или Кт, и очищает все три выбора. Выбор единиц и знаков кнопкой не сбрасывается. Ошибка загрузки плана счетов показывается уведомлением.
Отметки в дереве SHALL считаться по полному плану счетов: родительский счёт отмечен тогда и только тогда, когда отмечены все
его потомки в полном дереве. Поиск по дереву скрывает не совпавшие узлы, но не меняет этого правила: отметка единственного
видимого при поиске субсчёта не отмечает его родителя, а снятие отметки с субсчёта снимает её и с родителя. Скрытые поиском
отметки при отметке видимых узлов сохраняются.
Режим расчёта определяется выбором:
- ничего не выбрано ни в Дт, ни в Кт — SHALL выполняться `GET …/intercompany-matrix/all` (режим `all`);
- выбраны счета и в Дт, и в Кт — SHALL выполняться `POST …/intercompany-matrix/manual` с `{debit_account_ids,
credit_account_ids}` (режим `manual`);
- выбрана только одна сторона — запрос SHALL NOT отправляться, таблица матрицы SHALL быть пустой и вместо данных показывать
текст «Для расчёта по счетам выберите счета и в Дт, и в Кт»; тот же текст SHALL показываться у фильтров Дт/Кт (не
всплывающим уведомлением) и только в этом состоянии; кнопка «Экспорт» матрицы SHALL быть недоступна.
Каждое изменение выбора счетов, приводящее к режиму `all` или `manual`, перезапрашивает матрицу; на экране остаётся ответ на
последний запрос, индикатор загрузки снимается и при переходе в состояние «выбрана одна сторона». Кнопка «Сбросить фильтры»
появляется, когда выбрано что-либо в компаниях, Дт или Кт, и очищает все три выбора. Выбор единиц и знаков кнопкой не
сбрасывается. Ошибка загрузки плана счетов показывается уведомлением.
#### Scenario: Обе стороны выбраны
- **WHEN** выбран счёт 60.01 в Дт и счёт 62.01 в Кт
@ -70,28 +118,52 @@
#### Scenario: Выбрана одна сторона
- **WHEN** выбран счёт только в Дт
- **THEN** отправляется `GET …/intercompany-matrix/all` (режим `all`); выбор Дт остаётся в фильтре
- **THEN** запрос матрицы не отправляется, таблица пуста и показывает «Для расчёта по счетам выберите счета и в Дт, и в Кт»,
тот же текст виден у фильтров, «Экспорт» матрицы недоступен, выбор Дт остаётся в фильтре
#### Scenario: Выбрана одна сторона во время загрузки
- **WHEN** выбраны Дт и Кт, запрос `manual` ещё выполняется, и пользователь очищает Кт
- **THEN** индикатор загрузки снят, ответ прежнего запроса на экран не выводится, таблица в состоянии «выбрана одна сторона»
#### Scenario: Поиск и отметка субсчёта
- **WHEN** в дереве Дт введён поиск, видна ветка 60 с единственным субсчётом 60.02, и отмечен 60.02
- **THEN** выбранным остаётся только 60.02; после сброса поиска счёт 60 и его прочие субсчета не отмечены
#### Scenario: Снятие субсчёта отмеченного родителя
- **WHEN** отмечен счёт 60 (отмечены 60, 60.01, 60.02) и снята отметка с 60.01
- **THEN** выбранным остаётся 60.02, счёт 60 не отмечен
#### Scenario: Сброс
- **WHEN** выбраны компании и счета, нажато «Сбросить фильтры»
- **THEN** все три выбора пусты, матрица перезапрошена в режиме `all`, кнопка исчезла
### Requirement: Экспорт шахматки в XLSX
Кнопка «Экспорт» SHALL скачивать XLSX в том же режиме, что и таблица: при выбранных счетах и в Дт, и в Кт —
`POST …/intercompany-matrix/manual/export` с телом `{debit_account_ids, credit_account_ids}`, иначе
`GET …/intercompany-matrix/all/export`. Файл сохраняется под именем из `Content-Disposition`, при его отсутствии — «Шахматка».
Пока идёт выгрузка, кнопка в состоянии загрузки; ошибка API показывается уведомлением. Фильтр компаний, единицы и знаки
в запрос экспорта не передаются.
### Requirement: Экспорт в XLSX
Кнопка «Экспорт» матрицы SHALL скачивать XLSX в том же режиме, что и таблица: в режиме `manual` —
`POST …/intercompany-matrix/manual/export` с телом `{debit_account_ids, credit_account_ids}`, в режиме `all` —
`GET …/intercompany-matrix/all/export`; при выбранной одной стороне кнопка недоступна. Кнопка «Экспорт» результирующей таблицы
SHALL скачивать `GET …/intercompany-matrix/result-table/export`. Файл сохраняется под именем из `Content-Disposition`, при его
отсутствии — `Шахматка_<ГК>_<год>_<квартал>.xlsx` и `Результирующая_таблица_ВГО_<ГК>_<год>_<квартал>.xlsx` соответственно.
Пока идёт выгрузка, в состоянии загрузки только нажатая кнопка; ошибка API показывается уведомлением. Фильтр компаний, единицы и
знаки в запрос экспорта не передаются.
#### Scenario: Экспорт по всем счетам
- **WHEN** счета не выбраны и нажато «Экспорт»
- **WHEN** счета не выбраны и нажато «Экспорт» матрицы
- **THEN** отправлен `GET …/intercompany-matrix/all/export`, браузер сохраняет файл `intercompany-matrix_….xlsx`
#### Scenario: Экспорт результирующей таблицы
- **WHEN** нажато «Экспорт» результирующей таблицы
- **THEN** отправлен `GET …/intercompany-matrix/result-table/export`, кнопка «Экспорт» матрицы не переходит в состояние загрузки
### Requirement: Полноэкранный режим
Кнопка «На весь экран» SHALL переводить блок шахматки (панель фильтров и таблицу) в полноэкранный режим средствами Fullscreen API
с белым фоном и таблицей на высоту окна; в нём кнопка меняется на «Свернуть». Выход из полноэкранного режима клавишей Esc
SHALL возвращать обычный вид и подпись кнопки.
Кнопка «На весь экран» матрицы и результирующей таблицы SHALL переводить свой блок (панель фильтров и таблицу) в
полноэкранный режим средствами Fullscreen API с белым фоном и таблицей на высоту окна; в нём кнопка меняется на «Свернуть».
Выпадающие списки фильтров и подсказки SHALL оставаться видимыми в полноэкранном режиме. Выход клавишей Esc SHALL возвращать
обычный вид и подпись кнопки. Отказ браузера войти в полноэкранный режим показывается уведомлением.
#### Scenario: Вход и выход
- **WHEN** нажато «На весь экран», затем Esc
- **THEN** блок разворачивается на весь экран, затем возвращается в страницу, кнопка снова «На весь экран»
#### Scenario: Фильтр в полноэкранном режиме
- **WHEN** матрица в полноэкранном режиме и открыт выбор «Компании»
- **THEN** выпадающее окно видно поверх полноэкранного блока

View File

@ -1,6 +1,11 @@
## 1. Спека и ревью (RSHB/arm-core#158)
## 1. Спека и ревью (RSHB/arm-core#158, AURORA-1538)
- [x] 1.1 Спека `intercompany-matrix-ui` по фактическому поведению ветки `feat/chessTable`.
- [ ] 1.2 Автор исправляет пункты «Known issues» из `design.md` и обновляет требования спеки отдельным коммитом.
- [ ] 1.3 Перед PR: `make web-check`, `npm test`, `npm run lint` — зелёные, вывод в описание PR.
- [ ] 1.4 Сверка экрана с макетом 31-01 в браузере (`docs/mockup/INDEX.md`).
- [x] 1.1 Спека `intercompany-matrix-ui` (ревьюер; с раунда 3 — целевое поведение).
- [x] 1.2 Тесты форматтера сумм `utils/chessTableCellFormatValue.test.ts` (ревьюер).
- [ ] 1.3 Автор исправляет «Known issues» 1–4 из `design.md` (желательное — 5 — по возможности).
- [ ] 1.4 Перед слиянием: `make web-check`, `npm test`, `npm run lint` — зелёные, вывод в описание PR.
- [ ] 1.5 Сверка экрана с макетом 31-01 / 31-01-01 в браузере (`docs/mockup/INDEX.md`), в том числе полноэкранный режим.
## 2. После слияния
- [ ] 2.1 Итоги «Итого» из API — AURORA-1756 (бэк), AURORA-1757 (фронт).