diff --git a/context/05-domain-rules.md b/context/05-domain-rules.md index a7941d0..6641ad8 100644 --- a/context/05-domain-rules.md +++ b/context/05-domain-rules.md @@ -450,7 +450,8 @@ variance = |amount_a − amount_b|; variance_pct = variance / min(amount_a, amou (+1215), `1300`, `1400`, `1500`, `1600 = 1100 + 1200`, `1700 = 1300 + 1400 + 1500`, `1600 = 1700` — и ОФР — `2100`, `2200`, `2300`; **мягкими** (предупреждение) — `2400 = 2300 − 2410 + 2420` и варианты ФСБУ 4/2023 (`2400 = 2300 + 2410 + 2460`, `2410 = 2411 + 2412`), `2500 = 2400 + 2510 + 2520 + 2530` и «сумма пятизначных подстрок = родитель» -(`ASSUMPTION(OQ-12)`). Допуск ±1 в единицах формы, расходы — двойной расчёт знаков (`abs_terms`). Межформенная проверка +(`ASSUMPTION(OQ-12)`). Допуск ±1 в единицах формы (расхождение до 0,001 % наибольшего числа соотношения — округление, +мягкое, OQ-138), расходы — двойной расчёт знаков (`abs_terms`). Межформенная проверка `1370 − 1370(начало года) = 2400` парсером не выполняется. Это сигнал о структуре и данных файла, а не замена проверок ядра: ядро (Срез 3) пересчитывает §7 и ведёт акцепт (`financial_statement_meta.checks_accepted_*`). @@ -875,11 +876,11 @@ OQ-92 (пустые «Организация»/«Группа компаний» отсутствующий код = 0, соотношение проверяется, только если присутствует его цель; правая часть считается дважды — как записано и с членами `abs_terms` по модулю (1С выгружает расходы со знаком минус, 66н — в скобках). Результат — `documents[].checks` (`relations_failed`, `soft_relations_failed`, `duplicate_line_codes`) и предупреждения. - **Фактически сходятся полные формы только у Межениновской**: книга НПЗ (`npz/2025-Q3`) — ББ `needs_check`: `1600 = 1700` - расходится на 1.94 / 9.02 / 2.48 руб., потому что значения в рублях с копейками при шапке «тыс. руб.»; допуск ±1 остаётся - (решение владельца 06.10.2026, вариант А, OQ-138). ОФР НПЗ и ОФР Унагранде (стр. 1) тоже `needs_check` по 2100/2200/2300 - по той же причине; ББ Унагранде (стр. 1) — только актив, `1600 = 1700` не сходится. Это сигнал о данных файла, а не о - неверно угаданной структуре; допуск под файл не подгоняется. + Расхождение жёсткого соотношения больше ±1, но не больше 0,001 % от наибольшего по модулю числа соотношения — **округление итогов** (OQ-138, решение владельца 06.10.2026: единицам шапки верим): уходит в `soft_relations_failed` с предупреждением «сошлось в пределах округления», статус не меняется (`rounding_tolerance_percent` в словаре). Пример: книга НПЗ (`npz/2025-Q3`) — `1600 = 1700` расходится на 1.94 / 9.02 / 2.48 при итогах ~17 млрд, + ОФР Унагранде Компани (стр. 1) — итог 2100 округлён до тысяч, ОФР Унагранде Групп 3 кв. 2025 — 2200 = −1 365 000 как + малая разность слагаемых ~393 млн; все `done`. Пятизначная подстрока относится к ближайшей четырёхзначной строке выше с + теми же тремя первыми цифрами (12310 под 1230). ББ Унагранде (стр. 1) — только актив, `1600 = 1700` не сходится, + `needs_check`. 8. **Расшифровки** (0710003/0710004, пояснения, лист «Примечания» шаблона) — `DISCLOSURE` с `locator` и шапкой, если нашлась, без `columns`/`lines`; на статус не влияет (OQ-23). Книга только из таких листов — `done`. 9. **Склейка**: листы-продолжения внутри книги присоединяются к последнему открытому документу формы, если число колонок diff --git a/context/06-test-data.md b/context/06-test-data.md index 04fce8e..e78e5a2 100644 --- a/context/06-test-data.md +++ b/context/06-test-data.md @@ -161,9 +161,9 @@ arm-tabular-parser#11, OQ-131…OQ-135. ИНН организаций в шап | Шаблоны | `templates/` | 66н и ФСБУ 4/2023: коды есть, сумм и шапки нет; заголовок на две строки; в шаблоне ФСБУ «АКТИВ» и код 1110 в одной строке (OQ-142) | **Манифест** `tabular-parser/api/tests/statements_manifest.yaml`: для каждого из 18 файлов — организация, ИНН, документы по -порядку (`type`, `status`, `complete`, причина `needs_check`, `failed_relations`). Статусы сверены прогоном: `done` только -у Межениновской (ББ и ОФР); остальные — `needs_check` по данным файла (рубли с копейками при шапке «тыс. руб.», только -актив, страница 2 без шапки, шаблон без сумм), НПЗ — `1600 = 1700` расходится на 1.94 / 9.02 / 2.48 руб. (OQ-138). +порядку (`type`, `status`, `complete`, причина `needs_check`, `failed_relations`). Статусы сверены прогоном: `done` — полные +формы (Межениновская ББ и ОФР, книга НПЗ, ОФР стр. 1 Унагранде Компани и Групп 3 кв. 2025: расхождения итогов — +округление, мягкие, OQ-138); `needs_check` — по данным файла: только актив (ББ стр. 1), страница 2 без шапки, шаблон без сумм. Таблица «файл → статус» — README `tabular-parser`, «Парсер форм». **Golden** `fixtures/golden/expected_statement_parse/<тот же путь без расширения>.json` — 24 снимка (18 реальных файлов + diff --git a/context/07-open-questions.md b/context/07-open-questions.md index 7f26256..9d65ae8 100644 --- a/context/07-open-questions.md +++ b/context/07-open-questions.md @@ -143,7 +143,7 @@ | OQ-135 | Отчёт 1С «Анализ счета» с отбором по контрагентам группы (`fixtures/analysis-reports/`, в именах файлов «ОСВ_…ПоКомпаниямГруппы»): не ОСВ, но содержит ВГО по корреспонденции счетов. Нужен ли отдельный тип документа для Среза 5, или ВГО считаются только из ОСВ | пакет ДКБ 06.10.2026 | `document_type`, Срез 5 | открыт: допущение — не разбирать; парсер отвечает `INVALID_DOCUMENT` | | OQ-136 | ИНН организаций пакета ДКБ (Цимлянский, НКЗ, Новоросметалл, Приманыческий, Агрокомплекс) в шапках ОСВ отсутствуют; для теста консолидации по этим группам нужны ИНН и состав ГК/ГСК (выгрузка ЕЦП или список от ДКБ) | пакет ДКБ 06.10.2026 | seed/фикстуры справочников, тесты Среза 4–5 на новых группах | открыт | | OQ-137 | Раскладка горячих папок ИС Сканирование: scan-gateway кладёт `Input/_` через временный `Tmp/.<имя>.part`, ничего не удаляет; PDFWorker старого АРМ (`RSHB/armka_file_worker`, проверен в контуре) кладёт `Input/.` через временный `<назначение>..tmp` рядом (ИС игнорирует `*.tmp`), читает батч `Output//*.json` и удаляет `Output//` и входящий файл после чтения. Какая раскладка актуальна? | сравнение `armka_file_worker` (README «Файлы», `clients.py`) и `scan-gateway/gateway/hotfolder.py`, `smb.py`; OQ-78, OQ-79 | scan-gateway (спека `image-recognition-gateway`, эмулятор, тесты) | **закрыт владельцем 06.10.2026: «правильно как в FileWorker»** — имя `.`, временный `*.tmp` рядом с назначением (каталог `Tmp` из OQ-79 не нужен), очистка `Output//` и входящего файла после чтения; `Output//*.json` батчем и стабилизация набора (OQ-78) остаются. Реализация — arm-scan-gateway #6 | -| OQ-138 | Единицы «тыс. руб.» в шапке против значений в рублях с копейками в выгрузках 1С (Унагранде Компани/Групп, НПЗ) и тысяч с дробями (Межениновская): кто и где приводит единицы. Примеры: книга НПЗ (`npz/2025-Q3`) — ББ `needs_check`, `1600 = 1700` расходится на 1.94 / 9.02 / 2.48 руб.; ОФР НПЗ и ОФР Унагранде (стр. 1) — `needs_check` по 2100/2200/2300 по той же причине; полные формы по факту сходятся только у Межениновской | `fixtures/statements-xlsx/README.md`, tabular-parser design 2b D-6, D-7, решения №22–24 (R-11, R-13, R-14) | Срез 3 (маппинг форм), §8 (КФО в тыс. руб.) | допущение принято (решение владельца 06.10.2026, вариант А): парсер отдаёт значения и `units` как в файле, при «тыс.» и ≥ 10 % значений с дробной частью — предупреждение; допуск ±1 не меняется, не нормализуем; приведение единиц — ядро/аналитик (Срез 3) | +| OQ-138 | Единицы «тыс. руб.» в шапке против значений в рублях с копейками в выгрузках 1С (Унагранде Компани/Групп, НПЗ) и тысяч с дробями (Межениновская): кто и где приводит единицы. Примеры: книга НПЗ (`npz/2025-Q3`) — ББ `needs_check`, `1600 = 1700` расходится на 1.94 / 9.02 / 2.48 руб.; ОФР НПЗ и ОФР Унагранде (стр. 1) — `needs_check` по 2100/2200/2300 по той же причине; полные формы по факту сходятся только у Межениновской | `fixtures/statements-xlsx/README.md`, tabular-parser design 2b D-6, D-7, решения №22–24 (R-11, R-13, R-14) | Срез 3 (маппинг форм), §8 (КФО в тыс. руб.) | **закрыт решением владельца 06.10.2026:** единица «тыс. руб.» в шапке считается верной, значения отдаются как в файле; расхождения итогов в пределах округления (до 0,001 % наибольшего числа соотношения) статус не портят — мягкие нарушения «в пределах округления» (arm-tabular-parser#20). Книга НПЗ, ОФР стр. 1 Унагранде — `done`. Принимать ли расхождение и приводить ли единицы — решает КА в ядре (акцепт проверок, Срез 3) | | OQ-139 | Форма без колонки кодов: сопоставление по именам строк из шаблонов 66н/ФСБУ (словарь `statement_lines.yaml`, `line_code_confidence < 100`)? Сейчас документ без строк и `needs_check` («колонка кодов не найдена») | решение владельца 06.10.2026 (вопрос 6), tabular-parser design 2b D-4 | §12 п. 2, Срез 3 | открыт: нужен реальный файл без колонки кодов; допущение — не сопоставлять по именам | | OQ-140 | Страница-продолжение в отдельном файле (1С печатает ББ/ОФР постранично, один файл = страница): склейка в ядре по ЮЛ + период + тип формы; признак `is_continuation` / `form_page` в контракте (MINOR, две необязательных поля) | решение владельца 06.10.2026 (вопрос 2), design 2b D-8 | §12 п. 9, Срез 3, контракт v1.x | допущение принято: страницы разных файлов не склеиваются; страница 2 без шапки — документ с пустой шапкой и `needs_check`, «загрузите форму одним файлом» | | OQ-141 | Строка без кода, но с суммами в ОФР ФСБУ 4/2023 (НПЗ «Реализация иного имущества…»): хранить ли (OQ-36 расширяется с расшифровочных строк на строки с суммами) | `fixtures/statements-xlsx/npz`, design 2b D-6 | Срез 3, DBML `financial_statement_line` (хранит только строки с кодом) | допущение принято: строки без кода не отдаются, при наличии сумм — предупреждение «строк без кода с суммами пропущено: N» | diff --git a/context/09-delivery-plan.md b/context/09-delivery-plan.md index a9688ea..672ccc3 100644 --- a/context/09-delivery-plan.md +++ b/context/09-delivery-plan.md @@ -106,7 +106,7 @@ Change'и: `tabular-parser/openspec/changes/slice-2a-osv-parser` (issue arm-tabu - **Не сделано / риски:** xlsb проверен только рукописным синтетическим файлом (OQ-102); ИНН из текста строки не извлекался (OQ-100; реализовано позже в tabular-parser PR #15); счета с буквой в первом сегменте не распознаются (OQ-103). -### Срез 2b — ББ/ОФР из Excel (`tabular-parser`) — реализован, ожидает ревью и merge +### Срез 2b — ББ/ОФР из Excel (`tabular-parser`) — закрыт 06.10.2026 (tabular-parser #16, docs #27, fixtures #10; follow-up arm-tabular-parser#20) - `tabular-parser`: ББ/ОФР из Excel по §12 в тот же `statementDocument`; поиск листов с формами по содержимому (в 2a такие листы пропускаются с предупреждением `skipped: statement (slice 2b)`); словари синонимов `rules/*.yaml`; diff --git a/contracts/examples/result-statement-excel.json b/contracts/examples/result-statement-excel.json index fcee529..260528b 100644 --- a/contracts/examples/result-statement-excel.json +++ b/contracts/examples/result-statement-excel.json @@ -3,7 +3,7 @@ "job_id": "3f1c7a2e-5b64-4d0a-9c1e-8a7b6d5e4f30", "file_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "service": { "name": "tabular-parser", "version": "0.3.0" }, - "status": "needs_check", + "status": "done", "metrics": { "duration_ms": 412, "rows": 7, "warnings": ["sheet=Бухгалтерский баланс. Лист 1 значений с дробной частью при единице тыс. руб.: 3"] }, "documents": [ { diff --git a/contracts/examples/result-statement-minimal.json b/contracts/examples/result-statement-minimal.json index 51e3b17..913cfc5 100644 --- a/contracts/examples/result-statement-minimal.json +++ b/contracts/examples/result-statement-minimal.json @@ -44,8 +44,7 @@ { "column_index": 1, "amount": "-12.30", "confidence": 90, "is_recognized": true } ] } - ], - "checks": { "relations_failed": [], "soft_relations_failed": [], "duplicate_line_codes": [] } + ] } ] } diff --git a/contracts/recognition-result.schema.json b/contracts/recognition-result.schema.json index 9a168cf..43781b3 100644 --- a/contracts/recognition-result.schema.json +++ b/contracts/recognition-result.schema.json @@ -3,7 +3,7 @@ "$id": "arm-ka/recognition-result/v1", "title": "Результат распознавания файла (tabular-parser / scan-gateway), контракт v1.2", "description": "Один файл (stored_file) → один job → N документов. Сервисы не знают БД: только структура файла. Нормализация счетов, резолв контрагентов, проверки — в arm-core.", - "$comment": "Версионирование: schema_version = MAJOR.MINOR. MINOR (1.1) — необязательные поля и новые значения enum; MAJOR (2.0) — удаление/переименование полей, смена типов или семантики. Потребитель принимает тот же MAJOR и любой MINOR. Новый MINOR раскатывается сначала на потребителей (ядро и сервисы обновляют копии схем раньше, чем производитель начнёт слать 1.1), потому что закрытые enum отвергают незнакомые значения.", + "$comment": "Версионирование: schema_version = MAJOR.MINOR. MINOR (1.1, 1.2, …) — необязательные поля и новые значения enum; MAJOR (2.0) — удаление/переименование полей, смена типов или семантики. Потребитель принимает тот же MAJOR и любой MINOR. Новый MINOR раскатывается сначала на потребителей (ядро и сервисы обновляют копии схем раньше, чем производитель начнёт слать новый MINOR), потому что закрытые enum отвергают незнакомые значения.", "type": "object", "required": ["schema_version", "job_id", "file_id", "service", "status", "documents"], "properties": { @@ -210,7 +210,7 @@ "description": "Результаты самопроверки формы по соотношениям §7 (tabular-parser). Отсутствие объекта — «не проверялось» (OCR-путь); пустые массивы — «проверено, сошлось».", "properties": { "relations_failed": { "type": "array", "items": { "$ref": "#/$defs/failedRelation" }, "description": "нарушения жёстких соотношений: документ needs_check" }, - "soft_relations_failed": { "type": "array", "items": { "$ref": "#/$defs/failedRelation" }, "description": "нарушения мягких соотношений (OQ-12, подстроки «в том числе»): только предупреждение" }, + "soft_relations_failed": { "type": "array", "items": { "$ref": "#/$defs/failedRelation" }, "description": "нарушения мягких соотношений (OQ-12, подстроки «в том числе») и жёстких в пределах округления итогов (OQ-138): только предупреждение" }, "duplicate_line_codes": { "type": "array", "items": { "type": "string" }, "description": "коды строк, встретившиеся в документе больше одного раза (осталась первая строка)" } } }