Andrey Epifancev bbb1b598ad docs(spec): просмотр ОСВ — плоская таблица, колонки по факту (#39, AURORA-1666)
Требование «ОСВ-документ» описывало дерево с колонками «контрагент, ИНН»,
что расходилось с плоской таблицей (#12) и с новым правилом колонок (#39).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:06:14 +04:00

92 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# document-review-ui Specification
## Purpose
Аналитик проверяет распознанный документ и подтверждает его: форма ББ/ОФР рядом со сканом PDF (правка значений и шапки, проверки и их акцепт) [41]/[41-02], ОСВ-документ — плоской таблицей строк только для чтения.
## Requirements
### Requirement: Экран документа
Маршрут `/documents/:document_id` SHALL загрузить `GET /documents/{id}` и показать вид по `document_type`: `BALANCE` и
`INCOME` — проверка формы, `OSV` — просмотр ОСВ; для прочих типов — карточку документа без данных. Шапка экрана SHALL
показывать тип, ЮЛ-владельца, период и статус документа, кнопку «Назад».
#### Scenario: Открытие формы баланса
- **WHEN** аналитик открывает документ `BALANCE`
- **THEN** показан экран проверки формы
### Requirement: Скан рядом с формой
Для ББ/ОФР экран SHALL показывать исходный файл документа: PDF — постранично через pdf.js из байт
`GET /files/{file_id}/content?disposition=inline`, полученных клиентом API с токеном (не через `<iframe>` и не прямой
ссылкой); изображение (png/jpg) — тегом `<img>` из blob; xlsx — без превью, с кнопкой «Скачать исходник».
#### Scenario: PDF под CSP sandbox
- **WHEN** сервер отдаёт PDF с `Content-Security-Policy: sandbox`
- **THEN** страницы PDF отрисованы на canvas и листаются
### Requirement: Форма ББ/ОФР — просмотр и правка
Экран SHALL показывать `GET /documents/{id}/statement`: шапку (ИНН, дата отчёта, единицы — значение и исходное при
правке; наименование, ОКУД/ОКПО) и таблицу «строка (код, наименование) × колонки» с суммами из `values[]`, включая
строки и ячейки, достроенные сервером по справочнику редакции (`id = null`). Ячейки с `confidence < 100` или
`is_recognized = false` SHALL подсвечиваться как «требует внимания», с `is_modified = true` — как «исправлено» с
исходным значением `original_amount` в подсказке; пустая достроенная ячейка не подсвечивается. Строки «БАЛАНС» формы
`BALANCE` (коды 1600 и 1700) SHALL выделяться фоном `#f9f0ff` и жирным шрифтом; подсветка ячейки перекрывает фон строки.
Единицы (`header.units`) SHALL выводиться над таблицей. Пока документ не `CONFIRMED`, ячейка SHALL быть редактируемой:
ввод проверяется по формату суммы (до 2 знаков после точки, знак минус допустим, пустое — `null`), сохранение
отправляет `PATCH /documents/{id}/statement {values: [...]}` суммой-строкой: ячейка строки из БД — `{line_id,
column_id, amount}`, ячейка достроенной строки (`id = null`) — `{line_code, column_id, amount}`; ответ заменяет
состояние экрана. Правка шапки (ИНН, дата отчёта, единицы) SHALL отправляться тем же `PATCH` в `header`;
`inn: null` не отправляется.
#### Scenario: Исправление ячейки
- **WHEN** аналитик меняет значение ячейки и сохраняет
- **THEN** ячейка помечена «исправлено», проверки пересчитаны по ответу сервера, акцепт снят
#### Scenario: Заполнение строки, которой не было в распознавании
- **WHEN** у формы нет строки 1700 из источника, аналитик вводит сумму в достроенную строку 1700 и сохраняет
- **THEN** уходит `{line_code: "1700", column_id, amount}`, по ответу строка получила `id`, ячейка помечена «исправлено»
#### Scenario: Строки «БАЛАНС»
- **WHEN** открыта форма `BALANCE`
- **THEN** строки 1600 и 1700 выделены цветом и жирным, у ОФР выделения нет
#### Scenario: Неверный формат суммы
- **WHEN** аналитик вводит «12,3,4»
- **THEN** запрос не отправляется, поле показывает ошибку формата
### Requirement: Проверки и их акцепт
Экран SHALL показывать `checks`: жёсткие (`failed`), мягкие (`soft_failed`), межформенную (`cross_form`) и акцепт
(`accepted`: кто, когда, комментарий). При непустом `failed` и `accepted = null` SHALL быть доступно «Принять с
комментарием»: комментарий 1–2000 символов → `POST /documents/{id}/statement/checks/accept {comment}`.
#### Scenario: Акцепт нарушений
- **WHEN** есть жёсткие нарушения и аналитик принимает их с комментарием
- **THEN** блок проверок показывает акцепт, нарушения остаются видимыми
### Requirement: Подтверждение документа
Кнопка «Подтвердить» SHALL отправлять `POST /documents/{id}/confirm` и быть недоступна для `CONFIRMED` и `ERROR`. При 409
`CHECKS_NOT_PASSED` экран SHALL показать `detail.failures[]` и предложить акцепт; при 409 `PERIOD_REQUIRED` — подсказать
исправить дату отчёта; прочие ошибки — `message`. После успеха экран SHALL перейти в режим только чтения.
#### Scenario: Подтверждение с неакцептованными нарушениями
- **WHEN** аналитик подтверждает форму с жёсткими нарушениями без акцепта
- **THEN** показан список нарушений из ответа и кнопка «Принять с комментарием»
### Requirement: ОСВ-документ
Для `OSV` экран SHALL показывать строки `GET /documents/{id}/entries` (все страницы, `limit` ≤ 2000) плоской
таблицей строк нижнего уровня (договоры отдельными строками не идут — их сумма в строке контрагента) с колонками:
субсчёт, подразделение (если в файле есть этот уровень), аналитика, ИНН (если есть контрагенты), сальдо на начало Дт/Кт,
обороты Дт/Кт, сальдо на конец Дт/Кт (суммы — строками с разделителями разрядов, рубли), только чтение. Кнопка «Подтвердить» — как для форм.
Заголовок колонки аналитики SHALL зависеть от вида строк-листьев (структурные строки субсчёта и подразделения не
учитываются, договоры строками не идут): только `COUNTERPARTY` — «Контрагент»; один другой вид — его `group_name`
(«Основные средства»); смесь видов или вид без `group_name` — «Аналитика», а перечень видов — во всплывающей подсказке
заголовка. Колонка «ИНН» и подсказка поиска «Контрагент или ИНН» SHALL показываться только при наличии контрагентов
(`COUNTERPARTY`); иначе колонки ИНН нет, а поиск подписан «Поиск по аналитике».
#### Scenario: ОСВ по основным средствам
- **WHEN** листья документа — аналитика с `group_name` «Основные средства», контрагентов нет
- **THEN** колонка называется «Основные средства», колонки ИНН нет, поиск подписан «Поиск по аналитике»
#### Scenario: Подтверждение ОСВ
- **WHEN** аналитик подтверждает ОСВ-документ `NEEDS_CHECK`
- **THEN** документ становится «Загружен», версия, к которой он подходит, переходит в `DATA_CORRECTION`