Andrey Epifancev a6fd2d0535 OpenSpec: change slice-2a-osv-parser (парсер ОСВ §1 — спека)
proposal, design (D-1…D-10), спеки osv-parsing (новая) и tabular-recognition-service (MODIFIED «Парсер — заменяемый
интерфейс»), tasks (13 задач). Решение владельца 05.10.2026: подсрез 2a — только парсер ОСВ.

Issue: RSHB/arm-tabular-parser#5

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 11:41:52 +04:00

6.4 KiB
Raw Blame History

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: требование «Парсер — заменяемый интерфейс» — реализация по умолчанию разбирает ОСВ, NOT_IMPLEMENTED остаётся только для ББ/ОФР-листов (§12, следующий подсрез); результат пишется с 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.