152 lines
19 KiB
Markdown
152 lines
19 KiB
Markdown
## Why
|
||
|
||
Формы ББ/ОФР сегодня нормализуются двумя разными конвейерами: scan-gateway (PDF → ИС Сканирование) и tabular-parser
|
||
(Excel). У них разные правила раскладки таблицы, отбора строк, разбора сумм, проверок §7 и статуса; ядро не доверяет
|
||
ни одному из них и пересчитывает проверки и статус заново по косвенным признакам, дважды дедуплицирует строки и держит
|
||
третью и четвёртую копии словарей (§7 и «код → наименование»). Пользователь видит последствия как отдельные «ошибки
|
||
распознавания» — AURORA-1406, 1462, 1511, 1517, 1519, 1522, 1523, 1524, 1634: строки молча пропадают, повтор кода
|
||
теряет сумму, «чистая» ячейка с потерянной цифрой не подсвечивается, предупреждения сервисов нигде не видны.
|
||
|
||
Дизайн `design.md` (финальная версия, решения владельца Р-1…Р-19 от 17.10.2026, три раунда ревью) приводит оба
|
||
сервиса к одной цепочке шагов 1–9 с единым выходом каждого шага и общим кодом шагов 2–8; ядро хранит результат как
|
||
пришёл и пересчитывает его тем же общим кодом только после правок аналитика.
|
||
|
||
Срез: вне плана срезов `09-delivery-plan.md` — переработка Среза 2b (Excel-формы), Среза 3a (OCR-нормализатор) и
|
||
Среза 3b (формы в ядре). Issue RSHB/arm-ka-docs#116.
|
||
|
||
## What Changes
|
||
|
||
- **Общий код нормализации** — новый репозиторий `arm-statements`, пакет `arm_statements/` (Р-1): типы шага 1
|
||
(`FormCandidate`, `Cell`), шаги 2–8 (раскладка, отбор строк, значения, код и наименование, сборка документа,
|
||
самопроверки §7, статус и `needs_review`), словари §7 (полный и упрощённый наборы), периодов, заголовков форм,
|
||
подписей колонок, упорядоченные перечни кодов форм по редакциям и вариантам, диапазоны кодов. Словаря «код →
|
||
наименование» в пакете нет (Р-3). В tabular-parser, scan-gateway и arm-core пакет лежит копией, которую раскладывает
|
||
и проверяет скрипт `sync-statements` репозитория docs (по образцу `sync-web.sh`).
|
||
- **Строки не выбрасываются молча:** статусы строк `ok` | `unreadable` | `missing` | `detail`, `row_index`, `ref`,
|
||
сырой текст кода и ячейки (`text`) доезжают до БД и экрана; повтор кода — законный случай, все строки сохраняются
|
||
(Р-4, Р-8). **BREAKING** для потребителей: `line_code` становится nullable.
|
||
- **Качество структурно и с привязкой к месту:** `needs_review` у значения, строки и полей шапки; «подтверждение
|
||
проверкой» §7 вместо порога уверенности (Р-5, Р-7, Р-11, Р-12, Р-16, Р-17); `document.status` у каждого документа;
|
||
предупреждения — структуры `{code, severity, message, line_refs, column_index, field}` вместо строк.
|
||
- **Контракт `recognition-result.schema.json` v2.0** (MAJOR, Р-9) и конверт `recognition-result-message.schema.json`
|
||
v2.0 (структурные `metrics.warnings`); запрос `recognition-request` остаётся v1.0. Путь ОСВ в tabular-parser
|
||
меняется только в части структуры предупреждений и `document.status`. На окне перехода схемы 1.x лежат рядом
|
||
(`*.v1.schema.json`, примеры `examples/v1/`); тест идентичности копии у ядра и сервисов сверяет файл копии с файлом
|
||
docs своего MAJOR, если он есть, иначе с основным (схемы и примеры; правило — спека `recognition-contract`).
|
||
- **Упорядоченные перечни кодов форм** — данные `contracts/statement-code-orders.yaml` (66н / ФСБУ 4, полная /
|
||
упрощённая, законные повторы кода — Р-20); в пакет `arm_statements` — байтовой копией.
|
||
- **arm-core:** DBML v5.19 и одна миграция (статусы и порядок строк, `needs_review`, `is_confirmed`, `source_text`,
|
||
`period_kind`, `form_variant`, таблица `document_issue`, снятие уникальности `(meta_id, line_code)` и — для ББ/ОФР —
|
||
`document (stored_file_id, document_type, osv_account_code)`, `recognition_job.statements_version`,
|
||
`related_group.crm_id` по arm-core#112); «агрегат формы по коду» вместо «одна строка на код»; приём 1.x и 2.0 на
|
||
время перехода; новое условие `RECOGNIZED`; PATCH `…/statement` с `lines`, `columns`, `values[].confirmed`; 409
|
||
`LINES_UNRESOLVED` и `DUPLICATE_FORM` при подтверждении.
|
||
- **arm-web:** экран проверки формы — строки со статусами на своих местах, расшифровки под переключателем, подсветка
|
||
только по `needs_review`, предупреждения и нарушения у места, назначение кода, «Это расшифровка», подтверждение кода
|
||
и ячейки, дата колонки.
|
||
- **Документы:** `05-domain-rules.md` — §11 становится общей главой нормализации ББ/ОФР с подразделами по
|
||
источникам, §12 — заголовок-редирект; §7 — «посчитанное» соотношение, `abs_terms` и знак, упрощённый набор; §9.3,
|
||
§9.5 — статус, маппинг и подтверждение формы по v2.0. `07-open-questions.md` — закрыты OQ-36, OQ-141 (Р-8) и
|
||
вопросы, снятые решениями дизайна; новые вопросы — склейка половинок формы (Р-15) и открытое в дизайне.
|
||
`frontend-api-guide.md` — новые поля формы и PATCH (после реализации).
|
||
|
||
Читают и пишут контракт: `arm-core` (читает результат и конверт, публикует запрос), `tabular-parser` и `scan-gateway`
|
||
(читают запрос, пишут результат и конверт). Дочерние change'и (заводятся при планировании, по одному на репозиторий):
|
||
`arm-statements` (новый), RSHB/arm-core, RSHB/arm-tabular-parser, RSHB/arm-scan-gateway, RSHB/arm-web; связанная
|
||
задача — RSHB/arm-core#112 (`related_group.crm_id` входит в ту же миграцию, Р-10).
|
||
|
||
### Порядок выкладки — «сначала ядро» (обязателен)
|
||
|
||
0. arm-core, tabular-parser и scan-gateway: тест идентичности копии контракта по новому правилу — схема копии
|
||
сверяется с `<имя>.v<M>.schema.json` своего MAJOR, если такой файл есть в docs, иначе с основным; пример — с
|
||
`examples/v<M>/<имя>.json`, если он есть, иначе с `examples/<имя>.json`; файлы другого MAJOR не сверяются. Правило
|
||
проходит и до слияния шага 2 (файлов `.v1` ещё нет), и после; без него `make check` всех трёх краснеет между шагами 2
|
||
и 3 (ядро) или 2 и 5 (сервисы).
|
||
1. `arm-statements` — пакет с тестами; `sync-statements` в docs; копия пакета подключается к хостам (линтеры,
|
||
`testpaths`, `statements-check`) — код сервисов её ещё не вызывает.
|
||
2. docs: контракт v2.0, DBML v5.19, правила (этот change).
|
||
3. arm-core: **сначала очистка форм ББ/ОФР на контуре** (Р-24: владелец или платформенная команда по его заявке,
|
||
после резервной копии БД — скрипт ядра `scripts/purge_statements.py`: `--dry-run` с отчётом, затем `--apply`; удаляются
|
||
документы `BALANCE` / `INCOME`, их `financial_statement_*`, комментарии и привязки к версиям «в работе», а файлы, где
|
||
кроме форм нет ОСВ, — целиком с заданиями; смешанные файлы «ОСВ + формы» — по решению владельца по отчёту; при
|
||
формах в верифицированных версиях скрипт отказывает; ОСВ, консолидации, справочники не трогаются — спека `arm-core`,
|
||
«Очистка форм перед выкладкой»), затем миграция v5.19 (только колонки, индексы и значения по умолчанию; §7 для
|
||
старых форм не считается), маппер 2.0 рядом с маппером 1.x, приём ack и конвертов 1.x и 2.0, PATCH, экранные поля
|
||
API. Формы пересоздаются повторной загрузкой файлов. До этого шага ни один сервис не шлёт 2.0: первый же
|
||
`line_code = null` дал бы файлу `ERROR`.
|
||
4. arm-web: экран по новым полям (поля 1.x-документов заполнены по умолчанию — экран работает и на них).
|
||
5. tabular-parser и scan-gateway переходят на 2.0 в любом порядке относительно друг друга.
|
||
6. После перехода обоих сервисов маппер 1.x удаляется из ядра отдельным PR (не в этом change).
|
||
|
||
Копии схем в сервисах (`api/contracts/`, тест идентичности в `make check`) после слияния контракта v2.0 в docs
|
||
сверяются с `*.v1.schema.json` и `examples/v1/` (правило шага 0; у запроса файла `.v1` нет — с основным); каждый сервис
|
||
обновляет копию до v2.0 в своём PR шага 5, ядро держит копии обоих MAJOR с PR шага 3. Схемы `*.v1.schema.json` и
|
||
`examples/v1/` удаляются вместе с маппером 1.x (шаг 6): сначала PR ядра убирает свои копии `.v1`, затем docs.
|
||
|
||
**Чего ждать на реальных сканах.** Значения строк `detail` в `needs_review` и в статус не входят (Р-23), законные
|
||
повторы кода и мягкие строки ОФР документ не держат (Р-20, Р-21). Поэтому ББ и ОФР РБ («Бухгалтерская отчетность 1
|
||
кв.25») и Счастливочной («БО 1 кв 2025») могут стать `done`: по прогону ревью все их неуверенные значения строк с кодом
|
||
покрыты сошедшимися соотношениями, неуверенные коды подтверждаются перечнем `fsbu4`, а держали их только ячейки
|
||
расшифровок. aep_3 и aep_6 остаются `needs_check` (строки `unreadable` / `missing` с суммами, несошедшиеся
|
||
соотношения). Фактическая доля `done` фиксируется в PR scan-gateway.
|
||
|
||
## Non-goals
|
||
|
||
- Разбор ОСВ (`classify.py`, `to_money` ОСВ, правила §1) не меняется; путь ОСВ в tabular-parser меняется только в
|
||
структуре предупреждений и `document.status`.
|
||
- Переключатель «как в файле» (порядок строк по `row_index`) на экране — не в этой работе (Р-14).
|
||
- Склейка двух половинок формы из одного файла в ядре не предоставляется — файл перезагружают; вопрос в
|
||
`07-open-questions.md` (Р-15). Склейка страниц из разных файлов (OQ-140) — тоже нет.
|
||
- Восстановление строк по наименованию (OQ-149) и сопоставление формы без колонки кодов по именам строк (OQ-139) — нет.
|
||
- Запрос `recognition-request` остаётся v1.0; subject'ы NATS, `Nats-Msg-Id`, ack и повторы не меняются по механике.
|
||
- Межформенная проверка `1370 − 1370(нач. года) = 2400` остаётся в ядре и мягкой; формулы §7 по редакциям (OQ-146) и
|
||
знаки 2410/2420 (OQ-12) не пересматриваются.
|
||
- Замена пакета-копии на зависимость из Nexus платформы — не сейчас (платформа Python-пакеты не собирает, Р-1).
|
||
- Приведение единиц измерения сверх числового формата ячейки (OQ-138) — нет.
|
||
|
||
## Capabilities
|
||
|
||
### New Capabilities
|
||
- `statement-normalization`: общая цепочка нормализации ББ/ОФР (шаги 1–8) — пакет `arm_statements`, его типы,
|
||
правила и словари, `sync-statements`.
|
||
- `tabular-recognition-service`: адаптер Excel шага 1 в tabular-parser и что остаётся в сервисе (чтение книги, листы
|
||
ОСВ, сборка конверта v2.0).
|
||
- `scan-gateway`: адаптер json ИС шага 1 в scan-gateway и что остаётся в сервисе (горячие папки, склейка набора json,
|
||
`raw/`, сборка конверта v2.0).
|
||
- `arm-core`: хранение результата v2.0 (DBML v5.19), агрегат по коду, `document_issue`, условие `RECOGNIZED`, PATCH
|
||
формы, порядок 409 подтверждения, приём 1.x и 2.0.
|
||
- `arm-web`: экран проверки формы ББ/ОФР по новым полям.
|
||
|
||
### Modified Capabilities
|
||
- `recognition-contract`: файл результата и конверт — v2.0; правила перехода (ядро принимает 1.x и 2.0, «сначала
|
||
ядро»); статус и структурные предупреждения у всех документов, включая ОСВ; тест идентичности копии — по MAJOR.
|
||
- `quality-checks`: `make check` охватывает копию `arm_statements` (тесты, `mypy`), `make check-full` — `statements-check`
|
||
(`sync-statements --check`).
|
||
|
||
## Impact
|
||
|
||
- Новый репозиторий `arm-statements`; скрипт `sync-statements` и цель Makefile в docs (вызывается из `make check-full`
|
||
потребителей, не из `make check`; коды 0 / 10 / 20 / 2).
|
||
- docs: `contracts/statement-code-orders.yaml`, схемы `*.v1.schema.json` и `examples/v1/` на окне перехода;
|
||
`scripts/validate_contracts.py` проверяет оба MAJOR и перечни кодов, `make venv` ставит PyYAML.
|
||
- tabular-parser: `parser/statements/*` (кроме разбора книги и адаптера шага 1) уходит в общий код; словари
|
||
`statement_checks.yaml`, `statement_periods.yaml`, `statement_headers.yaml` переезжают в пакет; путь ОСВ — структура
|
||
предупреждений и `document.status`; манифест `api/tests/statements_manifest.yaml` обновляется: `failed_relations`
|
||
страниц 2 баланса (`unagrande-co/2024-FY/Б.Б. Лист 2.xls`, `unagrande-group/2024-FY/ББ. Лист 2 (2).xls`,
|
||
`unagrande-group/2025-Q3/ББ. Лист 2 3кв.25.xls`) получают `1600 = 1700` («посчитанное» соотношение, пустая цель),
|
||
статусы `done` не меняются.
|
||
- scan-gateway: `normalizer/ocr/text|dates|amounts` становятся общими разборами, `layout|rows|header|document` — шагами
|
||
2–6 общего кода; `api/rules/statement_lines.yaml` удаляется, `statement_labels.yaml` и `ocr_lookalikes.yaml` уходят в
|
||
словари пакета.
|
||
- arm-core: очистка форм ББ/ОФР перед выкладкой (`scripts/purge_statements.py`, Р-24); миграция v5.19
|
||
(`financial_statement_*`, `document`, `document_issue`, `recognition_job`, `related_group`; только колонки, индексы и
|
||
значения по умолчанию, §7 по старым формам не считается — на неочищенной базе их нарушения до первой правки не
|
||
показываются); `JobBrief.warnings` — структуры, `DocumentOut.issues` (в том числе ОСВ); `result_mapping`, `statement_mapping`,
|
||
`statements`, `documents`, `auto_confirm`, схемы API формы; признак строки правки — `row_index IS NULL`;
|
||
`api/rules/statement_checks.yaml` удаляется (копия словаря §7), `statement_lines.yaml` остаётся только словарём имён
|
||
(несколько имён на код, загрузчик повтор кода не запрещает); PATCH шапки принимает `organization_name`.
|
||
- arm-web: `widgets/DocumentReview` (экран [41]), типы API.
|
||
- Решение v5.13 «`needs_review` считается, не хранится» пересматривается (design, Decisions № 17).
|
||
- Риск окна выкладки: пока сервис шлёт 1.x, документы его конвейера получают статус по прежнему правилу D-6 и поля
|
||
v2.0 по умолчанию — экран и подтверждение работают, новых сигналов качества нет.
|