Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
65 lines
6.5 KiB
Markdown
65 lines
6.5 KiB
Markdown
# 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.
|