Спека document-review-ui обновлена дельтой change'а; change перенесён в openspec/changes/archive/2026-10-09-statement-template-lines/. Реализация — PR #20. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
82 lines
7.7 KiB
Markdown
82 lines
7.7 KiB
Markdown
# 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) деревом по
|
||
`parent_entry_id` с колонками: счёт/субсчёт, контрагент, ИНН, сальдо на начало Дт/Кт, обороты Дт/Кт, сальдо на конец
|
||
Дт/Кт (суммы — строками с разделителями разрядов, рубли), только чтение. Кнопка «Подтвердить» — как для форм.
|
||
|
||
#### Scenario: Подтверждение ОСВ
|
||
- **WHEN** аналитик подтверждает ОСВ-документ `NEEDS_CHECK`
|
||
- **THEN** документ становится «Загружен», версия, к которой он подходит, переходит в `DATA_CORRECTION`
|