8.4 KiB
Raw Blame History

Proposal: Срез 2c — загрузка файлов ОСВ, очередь распознавания, маппинг результата в document/osv_entry

Срез: 2, подсрез 2c (../docs/context/09-delivery-plan.md; порядок по решению владельца 05.10.2026: 2a → 2c → 2b → 2d). Репозиторий: arm-core. Issue: RSHB/arm-core#14. Родительский change: ../docs/openspec/changes/slice-2c-osv-ingest (DBML v5.15, правила по факту, OQ; RSHB/arm-ka-docs#16). Фикстуры не меняются (golden-снимки парсера из Среза 2a).

Why

После Среза 2a парсер разбирает ОСВ, но результат некуда положить: в ядре нет загрузки файлов (stored_file нигде не создаётся), inbox фиксирует конверт, а маппер — NoopMapper. Без 2c нет сквозного пути «файл → строки ОСВ в БД», на котором стоят чек-лист (Срез 3), сводная ОСВ и ВГО (Срезы 4–5), и тестировщику нечего проверять на реальных данных.

What Changes

  • DBML v5.15: osv_data_type + NU (OQ-45); app_user.email хранится в нижнем регистре (OQ-59, нормализация при записи, UNIQUE прежний); миграция 0003_dbml_v5_15 (enum + UPDATE app_user SET email = lower(email)); DBML_VERSION, Makefile, dbml_parity, тесты миграций — на v5.15. Таблиц 43, enum'ов 27.
  • Загрузка файлов POST /api/v1/legal-entities/{id}/files (multipart, несколько файлов, DOCUMENT_EDIT, policy видимости ЮЛ): s3_object (uploads/{file_id}/{job_id}/{имя}, sha256) + stored_file(UPLOADED) + recognition_job(queued) + current_recognition_job_id одной транзакцией; тип по расширению и сигнатуре (xls/xlsx/xlsb → tabular, pdf/png/jpg → image); дедуп по sha256 в пределах ЮЛ (409); лимит UPLOAD_MAX_BYTES 50 МиБ; частичный успех по файлам; аудит FILE_UPLOAD.
  • Статусы файла в обмене: ack → PROCESSING; error/timeout/dlq → ERROR с причиной (OQ-67, inbox).
  • Цикл маппинга (services/result_mapping.py, фоновая задача lifespan, период MAPPING_POLL_SECONDS): задание done|needs_check с файлом PROCESSING и current_recognition_job_id = job → FOR UPDATE SKIP LOCKED → проверка ключа (OQ-70) → чтение результата из S3 вне транзакции → валидация v1.1 → одна транзакция: document + osv_entry + group_schema + статус файла PROCESSED; ошибка чтения/валидации → ERROR; исключение → повтор до 3 раз (metrics.mapping_attempts), затем ERROR (OQ-69 — маппинг отделён от inbox).
  • Маппинг по §1.3/§3/§9.3: все строки кроме TOTAL (OQ-01) с parent_entry_id; нормализация кода счёта, account_root, account_id по плану счетов; резолв контрагента ИНН → имя → алиас, несколько кандидатов → NULL; counterparty_check по шапке; period_check = UNKNOWN до Среза 4; is_cumulative по данным Q1 (OQ-02); статус документа RECOGNIZED | NEEDS_CHECK (OQ-05); второй лист с тем же счётом — предупреждение и NEEDS_CHECK.
  • Автозамена (решение владельца 05.10.2026): после маппинга прежние файлы того же ЮЛ с документом того же типа, счёта и периода → SUPERSEDED (superseded_by_file_id, version_no), аудит FILE_REPLACE (с предупреждением, если заменён CONFIRMED). Удаление DELETE /files/{id} — пока нет CONFIRMED документов; аудит FILE_DELETE.
  • API чтения (DOCUMENT_VIEW): список файлов ЮЛ с заданием и документами, карточка файла, документ с проверками и схемой, строки документа деревом (до 2000 на страницу); подтверждение POST /documents/{id}/confirm (DOCUMENT_EDIT, DOCUMENT_CONFIRM).
  • S3: S3Storage.get_bytes(key); FakeStorage.get_bytes в тестах.
  • Тесты: unit (нормализация кода, резолв, проверки шапки), integration (загрузка/дедуп/статусы/цикл/автозамена/ удаление/API), fixtures — маппинг всех 82 golden-снимков парсера без NATS и сквозной прогон через compose с реальным tabular-parser на rb/2024-FY/ОСВ_66 за 2024.xls.
  • Документы: README (загрузка, статусы, маппинг, API), CLAUDE.md, спеки.

Capabilities

New Capabilities

  • primary-files: загрузка файлов отчётности, дедуп, статусы файла, автозамена, удаление.
  • osv-result-mapping: цикл маппинга результата распознавания в document/osv_entry, резолвы, проверки, статусы документа.
  • documents-api: чтение файлов, документов и строк ОСВ, подтверждение документа.

Modified Capabilities

  • recognition-job-exchange: ack и финальные статусы задания меняют stored_file.status; результат done|needs_check не маппится в inbox, а передаётся циклу маппинга (NoopMapper и протокол ResultMapper удаляются).
  • data-model: схема равна DBML v5.15, цепочка миграций 0001 → 0003.
  • audit-trail: события файлов и документов (FILE_UPLOAD, FILE_REPLACE, FILE_DELETE, DOCUMENT_CONFIRM).

Impact

  • Таблицы DBML v5.15: s3_object, stored_file, recognition_job (group_schema, metrics), document, osv_entry, chart_of_account (чтение), legal_entity, legal_entity_alias (чтение), app_user (email в нижнем регистре), audit_event. Полей вне DBML нет.
  • Правила: §1.3 (маппинг строк, нормализация, account_root/account_id), §1.5 (is_cumulative по данным, period_check), §3 п. 4 (резолв контрагента), §9.2–9.4 (статусы). Допущения: ASSUMPTION(OQ-02) (по данным — только сравнение с Q1 того же года), ASSUMPTION(OQ-55) (события обмена не аудируются), ASSUMPTION(OQ-100) (ИНН из текста строки не извлекается), ASSUMPTION(OQ-103) (буквенный первый сегмент → account_id = NULL).
  • Соседи: ../docs (DBML v5.15, §, OQ), tabular-parser — без изменений; compose — без изменений.
  • Seed: users.yaml dev-email уже в нижнем регистре; permissions/матрица не меняются (DOCUMENT_VIEW/DOCUMENT_EDIT есть).

Non-goals

  • UI (панель «Первичные документы», окно загрузки, экран проверки ОСВ) — подсрез 2d.
  • Маппинг statementDocument (ББ/ОФР), логические проверки §7, правка распознанных значений (RECOGNIZED_DATA_EDIT), чек-лист — Срез 3 / подсрез 2b.
  • Ручное сопоставление контрагентов (OSV_COUNTERPARTY_MAP), period_check по версии консолидации — Срез 4.
  • Replay заданий (dlq → queued, OQ-73), аудит смены статусов задания (OQ-55), JetStream.
  • Ограничение удаления по консолидациям (ANL-020) — Срез 4.