# consolidated-osv-ui Specification ## Purpose Аналитик видит сводную ОСВ версии [30]: сводку с несопоставленными строками, пробелы по кварталам, таблицу с фильтрами в заголовках колонок и экспорт в xlsx; сопоставляет контрагента строки с ЮЛ справочника [32]. ## Requirements ### Requirement: Сводка версии Экран «Сводная ОСВ» [30] SHALL показывать шапку версии (ГК, период, номер, статус) и сводку `GET /consolidations/versions/{id}/osv/summary`: строк всего, документов, несопоставленных (`unmapped_total` — счётчик «Осталось N» — строки без сопоставленного ЮЛ и без ИНН из файла, arm-core #74; сопоставление — «Указать» в строке), автоматически сопоставленных. #### Scenario: Контрольные цифры РБ 2024 Q3 - **WHEN** открыт свод версии РБ 2024 Q3 после подтверждения 22 ОСВ из `fixtures/osv/{rb,schastl}/2024-Q{1,2,3}` - **THEN** «строк» = 1385, «документов» = 22, «несопоставленных» = 1260 ### Requirement: Таблица свода и фильтры Экран SHALL показывать строки `GET /consolidations/versions/{id}/osv` постранично (размер страницы ≤ 2000): ЮЛ, счёт, контрагент, ИНН контрагента, сальдо на начало Дт/Кт, обороты Дт/Кт, сальдо на конец Дт/Кт (суммы — строками с разделителями разрядов) и последней колонкой «Тип контрагента» — бейдж ГСК (синий) / ГК (зелёный) / Внешний (серый), как в макете [30]. Фильтры — в заголовках колонок: ЮЛ (`legal_entity_id`), корень счёта (`account_root`), поиск по контрагенту (`q`, ≤ 500 символов), тип контрагента (`counterparty_type`, одно значение — на колонке «Тип контрагента»); только несопоставленные (`unmapped`) — кнопкой «Показать ЮЛ без ИНН» в баннере и воронкой колонки «ИНН» (подсвечена, пока фильтр включён, макет [32-osv-no-inn]). «Применить» и «Сбросить» SHALL закрывать окно фильтра. Смена фильтра SHALL перечитывать первую страницу. #### Scenario: Строка из контрольных цифр - **WHEN** фильтр ЮЛ «Пекарня Счастливочная» и поиск «бисквит» - **THEN** строка `60.01` показывает сальдо нач. Кт 4 454 151.93, обороты Дт 128 058 143.65, Кт 125 950 450.77, сальдо кон. Кт 2 346 459.05, признак «ГСК» ### Requirement: Ручное сопоставление контрагента Ячейка ИНН (моноширинный шрифт, по центру) SHALL показывать, как в макете [32-osv-no-inn]: - ИНН из файла (`counterparty_inn`) — просто текстом, сопоставлена строка или нет (строка с ИНН сопоставления не требует, решение владельца 08.10.2026, отмена П-1); - строка сопоставлена, ИНН в файле нет — ИНН ЮЛ справочника (`counterparty_entity_inn`) кнопкой-«таблеткой» (`osv-inn-mapped-button`); нажатие открывает ту же модалку для пересопоставления, у текущего ЮЛ вместо «Связать» — неактивное «Связано»; - нет ни ЮЛ, ни ИНН — подсвеченная ячейка со ссылкой «Указать» (`osv-inn-link-button`). «Указать» и «таблетка» SHALL открывать модалку «Выбор организации для связи с <имя контрагента из файла>» [32-osv-map-counterparty]: поиск, таблица ИНН / Организация / ГК / ГСК / ГСЗ / «Связать», рекомендуемые (`recommended`) — первыми под заголовком «Рекомендуемые» с зелёной подсветкой. Кандидаты — `GET …/counterparty-candidates?scope=&entry_id=&q=` с паузой 300 мс после ввода: по умолчанию `scope=group` (периметр и ГК); галочка «Искать во всём справочнике» — `scope=all`, при поиске короче 3 символов запрос не отправляется и показывается подсказка. «Связать» SHALL отправлять `POST …/osv/entries/{entry_ids.at(-1)}/counterparty {legal_entity_id}`, после успеха — сообщение (с кем связано, сколько записей ОСВ сопоставлено автоприменением, создан или перенесён алиас), закрывать модалку и перечитывать страницу свода и сводку («Осталось N» на клиенте не вычисляется). Ответы 409: - `ALIAS_TAKEN` — подтверждение «Имя уже закреплено за <владельцы>. Перенести?» → повтор с `move_alias = true`; - `LOCK_REQUIRED` — подтверждение «Взять версию в работу?» → `POST …/lock` → повтор; отказ — ничего не меняется; - `LOCKED_BY_OTHER` (от правки или от `POST …/lock`) — «Версию держит <имя>» (имя — из ответа `POST …/lock` или из `lock` карточки версии), без перехвата; - `VERSION_NOT_EDITABLE` и прочие ошибки — текстом сервера. #### Scenario: Сопоставление «Пекарня Счастливочная ООО» в ОСВ РБ - **WHEN** в своде РБ 2024 Q3 у строки контрагента «Пекарня Счастливочная ООО» без сопоставления нажато «Указать» и у рекомендуемой «Пекарни Счастливочной» — «Связать» (версия не взята в работу → «Взять в работу») - **THEN** строка показывает ЮЛ и тип «ГСК», «Осталось N» уменьшается на число сопоставленных строк, у ЮЛ появляется алиас #### Scenario: Алиас занят - **WHEN** имя строки уже алиас другого ЮЛ и аналитик выбирает третье ЮЛ - **THEN** показан вопрос о переносе с именами владельцев; «Перенести» повторяет запрос с `move_alias = true` ### Requirement: Пробелы Экран SHALL показывать `gaps[]` сводки списком «ЮЛ — счёт — недостающие кварталы» с пояснением, что строки этих пар в своде отсутствуют. #### Scenario: Пробелы РБ 2024 Q3 - **WHEN** открыт свод версии РБ 2024 Q3 - **THEN** показано 7 пробелов, у каждого недостающие кварталы «Q1, Q2» ### Requirement: Экспорт Кнопка «Скачать xlsx» SHALL получать `GET /consolidations/versions/{id}/osv/export` с текущими фильтрами через клиент API с токеном и сохранять файл; имя — из `Content-Disposition`, а если заголовок недоступен — `Сводная_ОСВ_<ГК>_<год>_<квартал>.xlsx`. #### Scenario: Экспорт с фильтром - **WHEN** аналитик скачивает свод с фильтром по ЮЛ - **THEN** сохраняется xlsx, а ошибка сервера показывается текстом, а не скачивается ### Requirement: Единицы и знаки после запятой Над таблицей сводной ОСВ [30] SHALL быть два выпадающих списка: «Единицы» — «руб.», «тыс. руб.», «млн руб.», «млрд руб.» (по умолчанию «руб.»: суммы API — в рублях) и «Знаков после запятой» — 0, 1, 2, 3 (по умолчанию 2). Выбор SHALL менять только показ сумм шести денежных колонок (сальдо на начало, обороты, сальдо на конец, Дт/Кт); запрос, фильтры и экспорт в xlsx не меняются. Пересчёт SHALL выполняться над строкой суммы из API без арифметики с плавающей точкой: запятая сдвигается на 3/6/9 разрядов, округление до выбранного числа знаков — половина от нуля, разряды разделяются пробелом, дробная часть — через запятую. Нулевая сумма SHALL показываться «—» при любых единицах; ненулевая сумма, округлившаяся до нуля, — нулём с выбранным числом знаков, без знака «минус». #### Scenario: Тысячи рублей - **WHEN** выбраны «тыс. руб.» и 3 знака, сумма строки — `24000.00` - **THEN** в ячейке «24,000» #### Scenario: Округление без потери разрядов - **WHEN** выбраны «тыс. руб.» и 2 знака, сумма — `4454151.93` - **THEN** в ячейке «4 454,15» #### Scenario: Ноль - **WHEN** сумма ячейки — `0.00`, выбраны любые единицы и число знаков - **THEN** в ячейке «—» #### Scenario: Малая сумма в крупных единицах - **WHEN** выбраны «млн руб.» и 2 знака, сумма — `-0.40` - **THEN** в ячейке «0,00»