proposal, design (D-1…D-14, решения без обсуждения №1–10), дельты osv-result-mapping (маппинг форм, статус), statement-checks (новая: §7, межформенная, акцепт), documents-api (форма, правка, акцепт, подтверждение, комментарии, исходный файл), consolidations-api (CSV чек-листа), directories-api (documents_count), users-admin (справочник имён). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
10 KiB
Spec Delta: osv-result-mapping
MODIFIED Requirements
Requirement: Цикл маппинга отделён от inbox
Фоновая задача ядра SHALL с периодом MAPPING_POLL_SECONDS (5) выбирать одно задание со status ∈ {done, needs_check}, чей файл имеет status = PROCESSING и current_recognition_job_id = это задание (FOR UPDATE SKIP LOCKED), проверять, что result_s3_object относится к бакету хранилища и его ключ начинается с
results/{file_id}/{job_id}/ (OQ-70), читать объект из S3 вне транзакции, валидировать RecognitionResult v1.x и
в одной транзакции создать документы и строки (листы ОСВ и формы ББ/ОФР), записать recognition_job.group_schema,
перевести файл в PROCESSED и выполнить автозамену. Результат без документов SHALL давать неповторяемую ошибку
маппинга «в результате нет документов»; результат с формами, но без ОСВ, ошибкой MUST NOT быть. Inbox MUST NOT читать
файл результата и MUST NOT создавать документы.
Scenario: Результат смапплен
- WHEN в S3 лежит результат парсера для
rb/2024-FY/ОСВ_66 за 2024.xls, заданиеdone, файлPROCESSING - THEN не позднее чем через
MAPPING_POLL_SECONDS+ время маппинга файл —PROCESSED, создан одинdocument(OSV,osv_account_code = "66",period_year = 2024,period_quarter = Q4,is_cumulative = true,locator = sheet:Лист_1),osv_entry— все строки результата кромеTOTAL,group_schemaзадания содержит[{"locator": "sheet:Лист_1", "groups": [...]}]
Scenario: Ключ результата вне папки задания
- WHEN
result_s3_object.key = results/<другой file_id>/.../result.json - THEN файл —
ERRORс ошибкой маппинга, документы не созданы, повтор не выполняется
Scenario: Сбой маппинга с повтором
- WHEN вставка строк падает по ошибке БД
- THEN транзакция откачена,
metrics.mapping_attempts = 1, файл остаётсяPROCESSING; на третьей неудаче —ERRORсmetrics.mapping_error
Scenario: Повторный маппинг идемпотентен
- WHEN маппинг прерван после частичной вставки (имитация) и выполнен снова
- THEN у файла ровно один набор документов и строк, дублей по
(document_id, row_number)нет
Scenario: Файл только с формами
- WHEN смаплен golden-снимок
mezheninovskaya/2025-Q3/1_Форма1_бух_09(одна формаBALANCE, ОСВ нет) - THEN файл
PROCESSED, создан документBALANCE, ошибки «нет документов ОСВ» нет
Scenario: Пустой результат
- WHEN результат валиден по схеме, но
documents = [] - THEN файл
ERRORсmetrics.mapping_error = "в результате нет документов", повтор не выполняется
ADDED Requirements
Requirement: Документ формы ББ/ОФР
Каждый statementDocument с document_type ∈ {BALANCE, INCOME} SHALL давать document (osv_account_code = NULL,
is_cumulative = NULL, locator из результата, period_check = UNKNOWN) и financial_statement_meta 1:1. Дата
периода — header.report_date, при её отсутствии — самая поздняя columns[].period_date; period_year — год даты,
period_quarter — квартал, если дата — последний день марта, июня, сентября или декабря, иначе NULL с
предупреждением; reporting_date — эта дата. Шапка: inn, inn_original_value = header.inn, inn_confidence,
inn_verified = (inn_confidence = 100); organization_name; report_date = header.report_date,
report_date_original_value = header.report_date_text; units = units_original_value = header.units; okud,
okpo, okopf, okfs, address — как пришли; statement_type — balance/income. Колонки — по columns[],
is_current = true у колонки с самой поздней period_date. Строки — по lines[] с исходниками кода и названия; повтор
кода — первая строка и предупреждение. Значение SHALL создаваться на каждую пару «строка × колонка»: из результата —
amount, string_value, confidence, is_recognized; отсутствующая ячейка — все поля NULL; is_modified = false,
original_amount = NULL. checks результата MUST NOT использоваться для статуса. counterparty_check — тем же
правилом, что у листа ОСВ (ИНН шапки, иначе имя или алиас владельца).
Scenario: Форма из Excel
- WHEN смаплен golden-снимок
mezheninovskaya/2025-Q3/1_Форма1_бух_09 - THEN документ
BALANCE,period_year = 2025,period_quarter = Q3,reporting_date = 2025-09-30; уfinancial_statement_meta37 строк и по значению на каждую пару строка × колонка
Scenario: Книга с двумя формами
- WHEN смаплен golden-снимок книги НПЗ
npz/2025-Q3/…НОВОРОССИЙСКИЙ ПРОКАТНЫЙ ЗАВОД - THEN у файла документы
BALANCE(132 строки) иINCOME(43 строки), оба за 2025 Q3
Scenario: Форма из OCR без даты отчёта
- WHEN смаплен эталон нормализатора
ocr/expected_normalize/aep_6(дата отчётаnull, колонки баланса 2025-06-30, 2024-12-31, 2023-12-31) - THEN документ
BALANCEза 2025 Q2,is_current— у колонки 2025-06-30,report_dateв шапкеNULL,innNULL
Scenario: Дата не конец квартала
- WHEN дата отчёта формы — 2025-10-31
- THEN
period_year = 2025,period_quarter = NULL, предупреждение маппинга, документNEEDS_CHECK
Requirement: Повтор формы в файле и прочие документы
Второй документ BALANCE или INCOME в одном файле MUST NOT создаваться: предупреждение маппинга, первый документ —
NEEDS_CHECK. Документы DISCLOSURE и OTHER результата SHALL давать по одному document каждого типа на файл без
данных (locator — все locator этого типа через запятую, статус RECOGNIZED). Форма BALANCE/INCOME без строк
SHALL создаваться с шапкой и колонками.
Scenario: Расшифровки
- WHEN смаплен синтетический снимок
st06_header_variants(формаINCOMEи пятьDISCLOSURE) - THEN у файла два документа:
INCOMEи одинDISCLOSUREс пятьюlocatorчерез запятую
Scenario: Форма без колонки кодов
- WHEN смаплен синтетический снимок
st03_no_codes(BALANCE, строк 0) - THEN документ
BALANCENEEDS_CHECKс шапкой и колонками, строк нет
Requirement: Статус документа формы
Документ BALANCE/INCOME SHALL быть RECOGNIZED, только если одновременно: задание done; нет непройденных
неакцептованных жёстких проверок §7 (statement-checks); counterparty_check ≠ MISMATCH; period_year и
period_quarter определены; у каждого значения is_modified = true либо (is_recognized не false и confidence
равна 100 или NULL); нет предупреждений маппинга по документу; есть хотя бы одна строка. Иначе NEEDS_CHECK. Статус
SHALL пересчитываться при маппинге, после правки формы и после акцепта, кроме документа CONFIRMED. Документ формы
NEEDS_CHECK SHALL участвовать в переходе версии в DATA_CORRECTION наравне с ОСВ (требование «Документ NEEDS_CHECK
переводит версию в корректировку»).
Scenario: Чистая форма Excel
- WHEN смаплен
mezheninovskaya/2025-Q3/1_Форма2_бух_09(ОФР,done, соотношения сходятся, ИНН шапки = ИНН владельца файла) - THEN документ
INCOMERECOGNIZED
Scenario: Форма OCR
- WHEN смаплен эталон
ocr/expected_normalize/aep_3(уверенность значений < 100, ИНН шапки ≠ ИНН владельца) - THEN документ
BALANCENEEDS_CHECK,counterparty_check = MISMATCH
Scenario: Форма двигает версию
- WHEN версия ГК 2025 Q3 в
DOCUMENTS_PROCESSING, в периметре ЮЛ файла; смаплена формаBALANCEза 2025 Q3 вNEEDS_CHECK - THEN версия в
DATA_CORRECTION, привязкиconsolidation_documentнет