65 lines
6.5 KiB
Markdown
Raw Permalink 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.

# Proposal: Срез 2a — парсер ОСВ из XLS/XLSX/XLSB (динамическое обнаружение структуры §1)
**Срез:** 2 «Загрузка файлов + табличный парсер» (`../docs/context/09-delivery-plan.md`), первый подсрез по решению
владельца 05.10.2026: **только парсер ОСВ**. ББ/ОФР из Excel (§12), загрузка файлов и маппинг результата в ядре, UI —
следующие подсрезы. **Репозиторий:** arm-tabular-parser. **Issue:** RSHB/arm-tabular-parser#5.
**Связанные change'и:** `../docs/openspec/changes/slice-2a-osv-parser` (контракт v1.1, §1, OQ; RSHB/arm-ka-docs#9),
фикстуры RSHB/arm-ka-fixtures#4 (синтетика, золотые снимки).
## Why
После Среза 0 сервис принимает задания и отвечает `NOT_IMPLEMENTED`. Без разбора ОСВ невозможны ни сводная ОСВ, ни
ВГО, ни КФО — это самый рискованный блок системы (§1.0: ОСВ приходят из разных конфигураций 1С и других систем, и
«ни одна буква колонки, ни один номер строки, ни одно имя листа не являются контрактом»). В fixtures есть 66 реальных
ОСВ двух организаций (xls и xlsx, 2 и 3 строки заголовка, плавающие колонки, иерархия до 4 уровней, мусор правее
таблицы, пустая ОСВ), на которых парсер обязан проходить без единого правила под файл.
## What Changes
- **Парсер ОСВ** `api/src/parser/`: единая модель листа (`grid.py`), читатели xlsx/xls/xlsb, детекторы структуры по
содержимому (`detect.py`), шапка и `group_schema` (`header.py`), каскад стратегий иерархии (`hierarchy.py`),
классификация строк (`classify.py`), нормализация чисел в `Decimal` (`numbers.py`), контрольные суммы §1.4
(`checks.py`), оркестратор (`osv.py`). `get_parser()` возвращает реальную реализацию; воркер, NATS и S3 без изменений.
- **Словари** `api/rules/{osv_headers,osv_groups,periods,data_types}.yaml` с pydantic-валидацией при старте и тестом
«каждая запись срабатывает хотя бы раз».
- **Зависимости** `api/requirements.txt`: `openpyxl==3.1.5`, `xlrd==2.0.2`, `pyxlsb==1.0.10`.
- **Настройки** `PARSE_MAX_BYTES` (50 МиБ), `PARSE_TIMEOUT_SECONDS` (120) — проверка дедлайна между шагами, без
убийства потока.
- **Контракт v1.1**: парсер пишет `schema_version = "1.1"` и заполняет `rows[].extra` (нераспознанные колонки, §1.2);
копия схемы в `api/contracts/` обновляется вместе с источником в `../docs/contracts/`.
- **Тесты**: unit по модулям на синтетических сетках; property-тест инвариантов §1.4 на всех `../fixtures/osv/**`;
золотые снимки `../fixtures/golden/expected_osv_parse/`; синтетические файлы `../fixtures/osv/synthetic/` из
детерминированного генератора; тест-линтер «нет координат в `src/parser/`»; хелпер `ARM_KA_FIXTURES` (OQ-81).
- **Документы**: README (как запустить на файле, коды ошибок, словари), CLAUDE.md, спека `osv-parsing`.
## Capabilities
### New Capabilities
- `osv-parsing`: разбор ОСВ из Excel по §1 — обнаружение листов, заголовка и колонок по содержимому, шапка,
иерархия, классификация, числа, контрольные суммы, статусы и диагностика, словари, лимиты.
### Modified Capabilities
- `tabular-recognition-service`: требования «Парсер — заменяемый интерфейс» и «Результат по контракту» — реализация по
умолчанию разбирает ОСВ, листы ББ/ОФР пропускаются с предупреждением (§12, следующий подсрез), `NOT_IMPLEMENTED`
остаётся только у заглушки `NullParser` в тестах воркера; результат пишется с `schema_version 1.1`.
## Impact
- Код: `api/src/parser/**` (новое), `api/src/core/config.py` (две настройки), `api/src/contract/common.py`
(`CONTRACT_VERSION = "1.1"`), `api/contracts/recognition-result.schema.json` (копия v1.1), `api/rules/*.yaml`,
`api/requirements.txt`, тесты.
- Таблицы DBML: не затрагиваются (сервис без БД). Поля результата — только из контракта.
- Правила: §1.0–§1.5, §1.2 (`extra`), §1.4 (инварианты), §1.5 (период). Допущения: `ASSUMPTION(OQ-02)` — `is_cumulative`
только по тексту; `ASSUMPTION(OQ-45)` — `data_type = NU` выдаётся как есть; `ASSUMPTION(OQ-01)` — «Итого» выдаётся
строкой `TOTAL`, решение о хранении — за ядром.
- Соседи: `../docs` (контракт v1.1, §1), `../fixtures` (синтетика, golden), `arm-core` — копия схемы v1.1 отдельным
bounded-PR, логика ядра не меняется (`NoopMapper`).
## Non-goals
- ББ/ОФР из Excel (§12) — подсрез 2b; листы с признаками форм в этом подсрезе пропускаются с предупреждением.
- Загрузка файлов, `stored_file`/`recognition_job` в ядре, маппинг результата в `document`/`osv_entry`, UI — подсрезы 2c/2d.
- Проверка `is_cumulative` по данным других ОСВ (OQ-02), резолв контрагентов и счетов — ядро.
- Отмена разбора извне, JetStream, изменение дедупа `(job_id, attempt)` (OQ-73).
- Распознавание сканов и PDF.