Andrey Epifancev 47e6372cbe chore(openspec): архив unified-statement-normalization (#116)
Спеки применены: 5 новых capability (arm-core, arm-web, scan-gateway,
statement-normalization, tabular-recognition-service) с Purpose, изменения
quality-checks и recognition-contract. Ссылки в 05-domain-rules, DBML v5.19,
entities, dev/checks.md и dev/qa-statements-v2.md ведут в архив.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 09:25:43 +04:00

72 lines
7.7 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.

# tabular-recognition-service Specification
## Purpose
Требования к tabular-parser в единой нормализации ББ/ОФР: адаптер листов Excel (шаг 1) в общую сетку ячеек и выдача результата распознавания контракта 2.0, нормализованного общим кодом `arm_statements`.
## Requirements
### Requirement: Адаптер Excel шага 1
tabular-parser SHALL оставить у себя чтение книги (лимиты `PARSE_MAX_BYTES`, `PARSE_TIMEOUT_SECONDS`, формат по
сигнатуре, лимиты zip и объединённых ячеек, `UNREADABLE`), классификацию листов (ОСВ — отдельный путь; форма;
продолжение; прочее) и адаптер «блок формы на листе → `FormCandidate`» общего кода; шаги 2–8 по формам SHALL
выполняться копией пакета `arm_statements`. Адаптер SHALL: выделять блоки форм на листе по заголовку или ОКУД (без
кодов между ними блоки сливаются, заголовок после кодов — новая форма); собирать шапку каждого блока в его границах
(организация, ИНН без ИНН аудитора, ОКПО, ОКОПФ, ОКФС, единицы, дата, адрес) как `Cell` с `confidence = 100`;
заполнять у каждой ячейки `text` по числовому формату (tp#42) и `value`, `ref = <лист>!<A1>` (имя листа в одинарных
кавычках по правилу Excel, если в нём есть что-то кроме букв, цифр и `_`); дописывать
листы-продолжения в сетку открытой формы до раскладки (маркер «Форма … с.2» или позиция листа; проверки типа,
диапазона кодов и, при трёх и более кодах, числа колонок — как `join_blocker`), записывая границы листов в
`segment_breaks`. Несовпадение формата с `file_type` — предупреждение файла `FILE_TYPE_MISMATCH` (`info`). Лист без
признаков ОСВ и формы, без `type_hint` и кодов — предупреждение файла `SHEET_OTHER` (`info`, `locator = sheet:<имя>`),
документа нет.
#### Scenario: Книга ББ + ОФР
- **WHEN** книга содержит лист «Баланс» (ОКУД 0710001), лист «Баланс (2)» с маркером «Форма 0710001 с.2» и лист «ОФР»
- **THEN** адаптер выдаёт два кандидата: `BALANCE` с `locator = sheet:Баланс,Баланс (2)` и одной сеткой из двух листов
(`segment_breaks` — первая строка второго листа) и `INCOME` с `locator = sheet:ОФР`
#### Scenario: Ссылки ячеек
- **WHEN** код строки стоит в ячейке C27 листа «Баланс»
- **THEN** `ref` строки — `Баланс!C27`; у листа «Бухгалтерский баланс. Лист 1» — `'Бухгалтерский баланс. Лист 1'!C27`
### Requirement: Строки без кода и неизвестные колонки в Excel
tabular-parser SHALL отдавать строки без кода с содержимым со статусами `detail` / `missing` / `unreadable` (Р-8; ранее
не отдавались, OQ-36 / OQ-141), числовые колонки с нераспознанной подписью — колонкой без даты и `error` (ранее
пропускались), повторы кодов — всеми строками без `duplicate_line_codes`.
#### Scenario: НПЗ «Реализация иного имущества»
- **WHEN** в ОФР ФСБУ 4/2023 строка «Реализация иного имущества…» без кода с суммами стоит под строкой 2340
- **THEN** в результате строка `detail` с `parent_ref` строки 2340; ранее она отбрасывалась со счётчиком
### Requirement: Манифест ожиданий на реальных формах
Манифест `tabular-parser/api/tests/statements_manifest.yaml` SHALL быть обновлён в PR перехода на 2.0 по правилу
«посчитанного» соотношения §7 (пустая цель при непустых членах — нарушение): `failed_relations` страниц 2 баланса без
1600 получают `["1600 = 1700"]` — `unagrande-co/2024-FY/Б.Б. Лист 2.xls`, `unagrande-group/2024-FY/ББ. Лист 2 (2).xls`,
`unagrande-group/2025-Q3/ББ. Лист 2 3кв.25.xls` (было `[]`, причина «страница 2 без шапки» остаётся); страницы 1 «только
актив» (`Б.Б Лист 1.xls`, `ББ. Лист 1 .xls`, `ББ. Лист 1 3кв.25.xls`) сохраняют `["1600 = 1700"]` (теперь как «непустая
цель при пустых членах»). Статусы `done` (Межениновская ББ и ОФР, НПЗ ББ и ОФР, ОФР «Лист 1» Унагранде ×2) MUST NOT
меняться: их строки без кода уходят в `detail`, строки шапки выше заголовка — в `title`. Остальные записи манифеста
пересчитываются прогоном и правятся только с объяснением в описании PR.
#### Scenario: Страница 2 баланса
- **WHEN** разбирается `unagrande-group/2025-Q3/ББ. Лист 2 3кв.25.xls` (есть 1700, нет 1600)
- **THEN** документ `needs_check`, в `checks.relations_failed` — `1600 = 1700` с пустой целью
### Requirement: Путь ОСВ в контракте v2.0
У `osvDocument` tabular-parser SHALL выдавать `status` (`done`, когда контрольные суммы §1.4 листа сошлись, иначе
`needs_check` — прежние условия §1.6, применённые к листу) и `warnings[]` — прежние предупреждения листа структурами с `severity = info` и кодом с
префиксом `OSV_`; строк вида `sheet=<имя> <текст>` в `metrics.warnings` больше нет. Остальной разбор ОСВ не меняется.
#### Scenario: Предупреждение ОСВ
- **WHEN** на листе ОСВ есть строка без родителя
- **THEN** у документа ОСВ предупреждение `{code: "OSV_…", severity: "info", message}`, статус документа по §1.6
### Requirement: Статус книги
Статус конверта tabular-parser SHALL вычисляться по общему правилу: `done` ⇔ все документы `done` ∧ нет предупреждений
файла уровня `error`; книга без листов ОСВ и форм — `error INVALID_DOCUMENT`. Порядок документов — сначала ОСВ в
порядке листов, затем формы.
#### Scenario: Книга только из пояснений
- **WHEN** книга содержит только листы пояснений (0710003, «Пояснения»)
- **THEN** документы `DISCLOSURE` со статусом `done`, конверт `done`