# Proposal: Срез 2b — парсер ББ и ОФР из Excel (§12), контракт v1.2 **Срез:** 2 «Загрузка файлов + табличный парсер», подсрез 2b (`../docs/context/09-delivery-plan.md`). **Репозиторий:** arm-tabular-parser. **Issue:** RSHB/arm-tabular-parser#14. **Сквозной change:** `../docs/openspec/changes/slice-2b-statement-parser` (контракт v1.2, §12, OQ; RSHB/arm-ka-docs#26). Golden и синтетика — отдельный PR в arm-ka-fixtures. Копии схемы v1.2 — bounded-PR в arm-core и arm-scan-gateway (без изменения поведения) до мержа этого change. Решение владельца 06.10.2026 (brainstorming): объём среза — **только парсер и контракт**; маппинг `statementDocument` в ядро остаётся в Срезе 3. ## Why После Среза 2a сервис разбирает только ОСВ, а листы с признаками ББ/ОФР пропускает с предупреждением `skipped: statement (slice 2b)`. Без форм из Excel нет пути «отчётность компании → консолидация»: ББ и ОФР дают строки КФО (§8), гудвилл (1310) и логические проверки (§7). В fixtures есть 15 реальных файлов пакета ДКБ в трёх раскладках (постраничные xls 1С, многолистовая книга ФСБУ 4/2023, выгрузка `TDSheet`) и четыре пустых шаблона 66н / ФСБУ 4/2023 — на них парсер обязан проходить без единого правила под файл, организацию или имя листа (§1.0, §12). ## What Changes - **Оркестратор книги** `api/src/parser/book.py` (`BookParser`): лимит размера, чтение книги, дедлайн, классификация листов (`detect.classify_sheet`), раздача листов конвейерам ОСВ и форм, сбор документов, предупреждений, метрик и статуса. `OsvParser` сужается до конвейера листа ОСВ (`parse_sheet`); `get_parser()` возвращает `BookParser`. - **Конвейер форм** `api/src/parser/statements/` (`StatementParser`): заголовок формы и ОКУД, колонка кодов, шапка документа, колонки-периоды, строки и значения, соотношения §7, склейка листов-продолжений внутри книги, расшифровки как `DISCLOSURE`. - **Словари** `api/rules/statement_headers.yaml`, `statement_periods.yaml`, `statement_checks.yaml` (соотношения §7 — данные, не код) с pydantic-валидацией при старте и тестом покрытия. - **Контракт v1.2** (копия `api/contracts/`, `CONTRACT_VERSION = "1.2"`): `statementDocument.document_type` + `DISCLOSURE`; `statementDocument.checks`; `osvDocument.header.period_start/period_end/is_from_year_start`. - **Тесты**: unit по модулям на синтетических сетках; property-тест на всех `../fixtures/statements-xlsx/**` по манифесту; golden `../fixtures/golden/expected_statement_parse/`; синтетика `../fixtures/statements-xlsx/synthetic/`; линтер «нет координат» на `statements/**` и `book.py`; покрытие словарей; интеграционный тест воркера с книгой НПЗ. - **Документы**: README (раздел «Парсер форм»), CLAUDE.md сервиса, `rules/README.md`, спеки `statement-parsing` (новая) и `tabular-recognition-service` (дельта). ## Capabilities ### New Capabilities - `statement-parsing`: разбор ББ/ОФР из Excel по §12 — классификация листов и продолжений, тип формы, шапка, колонки- периоды, колонка кодов, строки и значения, соотношения §7, склейка листов, статусы и диагностика, словари. ### Modified Capabilities - `tabular-recognition-service`: «Парсер — заменяемый интерфейс» — реализация по умолчанию разбирает и ОСВ, и формы (`BookParser`), результат пишется с `schema_version = "1.2"`; каталог `api/rules/` содержит словари форм. ## Impact - Код: `api/src/parser/book.py` (новое), `api/src/parser/statements/**` (новое), `api/src/parser/osv.py` (удаление `parse`/`_parse_book`), `api/src/parser/detect.py` (`classify_sheet`: продолжения форм, поиск признаков по области до таблицы), `api/src/parser/__init__.py` (`get_parser`), `api/src/contract/{common,result}.py` (v1.2), `api/contracts/` (копия), `api/rules/statement_*.yaml`, тесты, README, CLAUDE.md. - Таблицы DBML: не затрагиваются (сервис без БД). Поля результата — только из контракта v1.2. - Правила: §12 п. 1–8 (с решениями владельца 06.10.2026, внесёнными в §12 сквозным change'ем), §7 (формулы), §1.2 (числа). Допущения: `ASSUMPTION(OQ-12)` — знаки 2410/2420/2430 проверяются мягко; `ASSUMPTION(OQ-36)` — строки без кода не отдаются; `ASSUMPTION(OQ-23)` — расшифровки только классифицируются; `ASSUMPTION(OQ-37)` — код 1105 как пришёл. - Соседи: `../docs` (контракт v1.2, §12, OQ), `../fixtures` (golden, синтетика), `arm-core` и `scan-gateway` — копия схемы и pydantic-модели v1.2 отдельными bounded-PR, логика не меняется (ядро по-прежнему маппит только ОСВ). - Пересечение с параллельной работой по ОСВ (issues #9, #10, #11): модули `header.py`, `classify.py`, `checks.py`, `hierarchy.py`, `periods.yaml`, `osv_headers.yaml`, golden ОСВ в этом change не правятся; точка пересечения — `osv.py` и `test_osv_parser.py`, разрешается при rebase на main перед PR. ## Non-goals - Маппинг `statementDocument` в `document` / `financial_statement_*` ядра, логические проверки §7 в ядре, подтверждение документа, чек-лист ББ/ОФР — Срез 3. - Склейка страниц одной формы из **разных файлов** (постраничные xls 1С): страница-продолжение без шапки отдаётся как документ с пустой шапкой и `needs_check`; признак продолжения в контракте — позже, при появлении потребителя в ядре. - Сопоставление строк по наименованиям при отсутствии колонки кодов (§12 п. 2, словарь `statement_lines.yaml`) — отложено до реального файла; форма без колонки кодов даёт `needs_check`. - Нормализация единиц измерения (деление на 1000, пересчёт рублей в тысячи) — ядро и аналитик; парсер отдаёт как есть. - Разбор содержимого расшифровок (`DISCLOSURE`), лист `К балансу`, рекомендуемые формы банка (OQ-23). - Изменения в парсере ОСВ, кроме переноса оркестрации книги в `BookParser`; заполнение новых полей шапки ОСВ `period_start/period_end/is_from_year_start` — в ветке issue #11 либо follow-up, см. design D-9.