12 KiB
Raw Blame History

Spec Delta

MODIFIED Requirements

Requirement: Результат по контракту

После подтверждения сервис SHALL прочитать объект s3.key из бакета s3.bucket, вызвать парсер, записать файл результата (по recognition-result.schema.json v2.0, schema_version = "2.0") в results/{file_id}/{job_id}/result.json и опубликовать конверт результата (по recognition-result-message.schema.json v2.0, result_schema_version = "2.0") с заголовком Nats-Msg-Id = result:<job_id>:<attempt>. Ack processing SHALL нести schema_version = "2.0"; запрос recognition-request остаётся v1.0. service.statements_version файла результата SHALL быть версией библиотеки arm_statements (VERSION или метаданные установленного дистрибутива; без версии сервис не стартует) и записываться при наличии statementDocument. metrics.warnings конверта SHALL содержать предупреждения уровня файла структурами {code, severity, message, locator?}, не строками. Статус конверта — по общему правилу: done ⇔ все документы done ∧ нет предупреждений файла уровня error. При неудаче публикации сервис SHALL повторить её до трёх раз с паузой, затем залогировать ошибку.

Scenario: Запрос с заглушкой парсера (Срез 0)

  • WHEN воркеру явно передана заглушка NullParser (только тесты воркера; реализация по умолчанию — реальный парсер), запрос подтверждён и объект существует в uploads/
  • THEN в files.tabular_parsed появляется конверт status = error, error.code = NOT_IMPLEMENTED, job_id/file_id из запроса, metrics.duration_ms ≥ 0; файл результата может отсутствовать

Scenario: Запрос с реальным парсером

  • WHEN запрос подтверждён и объект в uploads/ существует, но не является книгой Excel (сигнатура не XLS/XLSX/XLSB)
  • THEN в files.tabular_parsed появляется конверт status = error, error.code = UNSUPPORTED_FORMAT (повреждённая книга — UNREADABLE), job_id/file_id из запроса, metrics.duration_ms ≥ 0; файл результата может отсутствовать; корректная ОСВ в том же потоке даёт done (см. «Парсер — заменяемый интерфейс»)

Scenario: Результат и ack версии 2.0

  • WHEN обработан запрос schema_version = "1.0" с книгой, содержащей баланс
  • THEN ack processing и конверт результата несут schema_version = "2.0", result_schema_version = "2.0", файл результата проходит схему v2.0, service.statements_version равен версии библиотеки arm_statements

Scenario: Предупреждение файла структурой

  • WHEN в книге лист «Примечания» без признаков ОСВ и формы
  • THEN metrics.warnings конверта содержит {code: "SHEET_OTHER", severity: "info", message, locator: "sheet:Примечания"}

Scenario: Повтор публикации

  • WHEN первая публикация конверта результата завершается ошибкой соединения, вторая — успешно
  • THEN конверт опубликован один раз с тем же Nats-Msg-Id, обработка не повторяется

Requirement: Парсер — заменяемый интерфейс

Разбор файла SHALL выполняться через единый интерфейс «байты + тип файла → документы/метрики/ошибка», не знающий о NATS и S3; воркер MUST NOT зависеть от конкретной реализации. Реализация по умолчанию (get_parser()) SHALL быть оркестратором книги BookParser: он читает книгу один раз, классифицирует листы по содержимому и отдаёт листы ОСВ конвейеру спеки osv-parsing, листы форм и их продолжения — конвейеру спеки statement-parsing (адаптер Excel и установленная библиотека arm_statements); прочие листы дают предупреждение файла SHEET_OTHER. Файл результата SHALL писаться с schema_version = "2.0"; документы книги — ОСВ в порядке листов, затем формы в порядке листов. Заглушка NullParser (error NOT_IMPLEMENTED) SHALL использоваться только в тестах воркера и MUST NOT быть реализацией по умолчанию. Реализация по умолчанию SHALL создаваться один раз при старте приложения (словари ОСВ читаются и проверяются там же, словари форм — default_rules() пакета). Каталог api/rules/ SHALL содержать словари ОСВ (osv_headers, osv_groups, periods, data_types, osv_checks) и README; словарей форм ББ/ОФР в сервисе нет — они часть библиотеки arm_statements.

Scenario: Подмена реализации в тесте

  • WHEN в тесте воркеру передан парсер, возвращающий один osvDocument
  • THEN файл результата с этим документом записан в S3, конверт имеет status = done и result_s3 на этот файл

Scenario: Реальная ОСВ через воркер

  • WHEN в интеграционном тесте в S3 лежит rb/2024-FY/ОСВ_66 за 2024.xls и опубликован запрос file_type = XLS
  • THEN после ack публикуется конверт done с result_schema_version = "2.0", файл результата проходит схему v2.0 и содержит один osvDocument со status = done и account_code_raw = "66"

Scenario: Реальные формы через воркер

  • WHEN в интеграционном тесте в S3 лежит книга statements-xlsx/npz/2025-Q3/…xlsx и опубликован запрос file_type = XLSX
  • THEN после ack публикуется конверт done с result_schema_version = "2.0", файл результата проходит схему v2.0 и содержит два statementDocument (BALANCE, INCOME) со строками line_status, ref, checks и пустыми checks.relations_failed; расхождения итогов в пределах округления (1600 = 1700 на 1.94 / 9.02 / 2.48, 2100/2200/ 2300) — в soft_relations_failed (OQ-138); service.statements_version равен версии библиотеки

Scenario: Книга без ОСВ и форм

  • WHEN в S3 лежит книга Excel, ни один лист которой не имеет признаков ОСВ или формы
  • THEN публикуется конверт error, error.code = INVALID_DOCUMENT, сообщение называет число просмотренных листов

Scenario: Сервис без версии общего кода не стартует

  • WHEN у библиотеки arm_statements нет ни VERSION, ни версии установленного дистрибутива
  • THEN require_statements_version() бросает RuntimeError с текстом «библиотека arm_statements без VERSION и версии пакета: выполните make install», сервис не стартует

Scenario: Установленный дистрибутив без VERSION

  • WHEN библиотека установлена как pylib_ai_arm_statements==0.1.1, файл VERSION отсутствует, а метаданные дистрибутива доступны
  • THEN сервис стартует и указывает service.statements_version = "0.1.1" при обработке реальной книги statements-xlsx/npz/2025-Q3/…xlsx

ADDED Requirements

Requirement: Локальная поставка библиотеки форм

Окружение сервиса SHALL использовать установленную библиотеку pylib_ai_arm_statements==0.1.1 с импортом arm_statements. make venv SHALL пропускать строки pylib_ai* и raisa_* при установке из requirements через индекс и устанавливать библиотеку из STATEMENTS_DIR (по умолчанию ../pylib_ai_arm_statements). make install SHALL создавать окружение при необходимости и переустанавливать библиотеку из этого пути.

Scenario: Подготовка окружения

  • WHEN разработчик выполняет make venv при доступной локальной библиотеке
  • THEN runtime- и dev-зависимости установлены в api/.venv, библиотека импортируется из установленного пакета; запрос на скачивание pylib_ai* из PyPI не выполняется

Scenario: Переустановка после изменения библиотеки

  • WHEN разработчик выполняет make install STATEMENTS_DIR=<путь-к-библиотеке> после изменения её исходников без смены версии
  • THEN библиотека переустановлена из указанного пути в api/.venv и изменения доступны сервису

Requirement: Библиотека форм в контейнерном образе

make docker-build SHALL передавать STATEMENTS_DIR именованным контекстом BuildKit arm_statements. Сборка SHALL устанавливать библиотеку из этого контекста вместе с публичными зависимостями, без загрузки pylib_ai* из PyPI. Образ SHALL содержать установленный пакет и его словари, доступные при запуске без исходной папки хоста.

Scenario: Сборка и запуск с локальной библиотекой

  • WHEN образ собран через make docker-build при доступном BuildKit и локальной библиотеке
  • THEN внутри образа импортируется arm_statements, доступны словари и версия 0.1.1; исходная папка хоста при запуске не требуется

Scenario: Прямая сборка

  • WHEN сборка запускается напрямую командой docker build
  • THEN вызывающая сторона SHALL передать --build-context arm_statements=<путь-к-библиотеке>; отсутствие контекста не заменяется загрузкой библиотеки из PyPI