Строка блока заголовка «Контрагенты.ИНН» под «Контрагенты» — тот же реквизит,
что элемент списка субконто варианта 2: в group_schema не попадает, у владельца
has_inn, строки склеиваются glue_inn_attributes. Общая часть вариантов —
_attach_inn_attribute и _positions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
В шапке варианта 2 «субконто: Контрагенты, Контрагенты.ИНН» 1С выводит ИНН
отдельной строкой на уровень глубже контрагента. Парсер принимал реквизит за
второй уровень контрагентов: строка ИНН становилась контрагентом с именем
«2370012036.0» и без ИНН, у наименования ИНН тоже не было, в свод ядра
попадали обе строки.
- header: элемент «<группировка>.ИНН» — InnAttribute, в group_schema не
попадает, у владельца has_inn = true; GroupSchema.positions держит позицию
для классификатора;
- classify: строка реквизита — OTHER с ИНН ячейки (column_inn, R-24);
glue_inn_attributes отдаёт ИНН контрагенту, убирает строку, поднимает её
детей; не склеилось (несколько строк, суммы не равны, ИНН не распознан или
расходится) — OTHER, предупреждение, needs_check; пустая ячейка (физлицо) —
склейка без ИНН (R-1);
- readers: целый float из xls читается как int — без «.0».
Файл банка: 713 из 716 контрагентов с ИНН, 3 физлица без ИНН, status done.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
У 91 и 99 строки аналитики несут только обороты, сальдо закрыто на субсчёт закрытия
(91.09), поэтому каждая ОСВ 91 получала needs_check. Список счетов — данные
api/rules/osv_checks.yaml (accounts_without_analytic_balance: 91, 99; 90 не входит).
- тождество строки на таких счетах не проверяется;
- «сумма детей = родитель» сохраняется: обороты всегда, сальдо — только у родителя
с дочерними субсчетами (корень -> субсчета); у родителя с одной аналитикой сальдо
не сверяется;
- «Итого» сверяется как обычно;
- строка относится к счёту списка по коду ближайшего счёта ветки (91, 91.01; не 910).
Тесты: unit checks/rules, манифест osv_dkb_expectations (две ОСВ 91 теперь done).
README, CLAUDE.md, openspec/specs/osv-parsing, api/rules/README.md обновлены.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Тот же источник настроек, что в arm-core#44: приоритет обычный env -> OPENBAO__APP__* -> .env -> умолчание;
неизвестное имя — ошибка при старте; ключ APP не ломает OpenBAOSettings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
- OQ-138 (решение владельца 06.10.2026): единицы шапки верны; жёсткое расхождение до rounding_tolerance_percent = 0,001 %
от наибольшего по модулю числа соотношения — округление итогов: soft_relations_failed + предупреждение, статус не
меняется. НПЗ, ОФР стр. 1 Унагранде Компани и Групп теперь done.
- N-1/N-2: лист с 1–2 кодами — продолжение только сразу после листа формы, коды из диапазона формы открытого документа;
присоединение видно в предупреждениях. N-3: негативные тесты BookParser и StatementParser.
- M-9: пятизначная подстрока относится к ближайшей четырёхзначной строке выше с теми же тремя цифрами (12310 под 1230).
- N-4: на продолжении без шапки колонка «Пояснения» узнаётся по коротким значениям и не даёт line_name.
- T7.1: ответ с ошибкой листа ОСВ называет неразобранные листы форм; T7.2: metrics.rows для форм — в спеке.
make check-full: 2217 passed, интеграция 10 passed; golden 138/138 (снимки — arm-ka-fixtures fix/20-golden).
Refs #20
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
I-1: лист с 1–2 кодами после листа формы — продолжение (min_codes_continuation в словаре).
M-10: тип формы маркера продолжения запрещает присоединение к документу иного типа.
I-2: одиночный месяц не задаёт границы при дате на число/квартале (not_single_month).
I-3: название строки из ближайшей текстовой ячейки левее кодов, пустое имя без confidence.
M-5: units_thousands и okud_code в словаре, без type: ignore в detect.py.
M-8: строка нумерации и нули не считаются строкой без кода с суммами.
M-11: предупреждение о дробных значениях — одно на документ.
T13.5: _upper_header_row возвращает верхнюю строку при склейке хотя бы одной колонки.
M-6/M-7/T15.1/T15.2: остатки statement_markers, описание fractional_share_percent, README, тест воркера.
Refs #14
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
Номер OQ-137 в main docs занят вопросом о раскладке горячих папок; design: решение №26.
Refs #14
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
StatementChecks собирался пустым и дополнялся append, а to_wire сериализует только
заданные поля: нарушения не доходили до JSON. Списки теперь задаются явно, пустой
список у проверенного документа сериализуется (checks.relations_failed = []).
Refs #14
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Знаки 1С доказываются на ОФР Межениновской; ОФР НПЗ и Унагранде расходятся из-за
единиц (OQ-137). У НПЗ INCOME на двух листах, лист с одной строкой 2400 пропускается.
Refs #14
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Манифест statements_manifest.yaml по 18 реальным файлам statements-xlsx (сверен
прогоном парсера, R-13, OQ-137), property-тест по манифесту, снимки форм в
golden_helpers/golden_update/test_golden, словари форм в покрытии, синтетика
st06 для вариантов заголовков. Мёртвые записи словаря удалены (name_caption,
метки okved и okud_label) либо покрыты st06.
Refs #14
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Баланс НПЗ расходится по 1600 = 1700 на 1.94 / 9.02 / 2.48 руб. (значения в
рублях при шапке «тыс. руб.», OQ-137): статус книги needs_check, допуск ±1
не меняется. Сценарий «Полные формы сходятся» ограничен формами Межениновской.
Refs #14
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Генератор st02: значения ST02_VALUES сходятся по всем жёстким соотношениям. _upper_header_row: верхняя строка берётся,
только если подпись склеивается с датой в период. Refs #14
Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Без подписи «Код» строкой заголовка выбиралась нижняя строка с датами; верхняя («На 31 декабря», «Изменение, %»)
оставалась над заголовком: колонка без периода не попадала в skipped_labels, метки периодов не склеивались.
Тест синтетики st02 проверяет точное предупреждение, две колонки и отсутствие значений «Изменение, %».
Refs #14
Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
R-6: проверка только если цель присутствует (в колонке)
R-8: убран zero tolerance для подстрок, общий tolerance
R-7: тесты с полными данными (подкомпоненты)
R-9: валидация — явные правила 4-цифр, подстроки 5-цифр
R-7: усиление abs_terms теста (цепочка 2100→2200→2300)
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Перенесено FRACTIONAL_SHARE в api/rules/statement_headers.yaml как fractional_share_percent (1..100),
добавлена валидация в StatementHeaderRules, целочисленное сравнение: fractional * 100 >= numeric * percent.
Добавлены тесты границ: ровно 10% (2 из 20), ниже 10% (1 из 12), без падения при нулевых числовых значениях.
Проверено RED мутацией: смена >= на > → падение теста граничного условия.
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Добавлен тест, проверяющий, что ИНН аудитора НИКОГДА не берётся, даже когда
аудиторский блок расположен РАНЬШЕ (выше) строк с ИНН организации.
Лист:
- Строка 3-5: блок аудитора (auditor_from=3)
- Строка 4: аудиторский ИНН = 7716021332
- Строки 6-8: организация
- Строка 8: org ИНН = 3249004697
С охраной min(last_row, auditor_from-1) = min(9, 2) = 2:
Поиск в строках 1-2, ИНН не найден → header.inn is None ✓ GREEN
Без охраны (поиск в строках 1-9):
Аудиторский ИНН найден первым → header.inn = 7716021332 ✗ RED
Этот тест доказывает, что guard критически важен для исключения аудитора.
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Обнаружено: test_inn_from_auditor_above_organization_inn_line не тестирует guard,
т.к. org INN всегда раньше auditor block в реальных файлах. Без guard первый найденный
INN всё равно организации (появляется раньше). Guard не differentiates этот сценарий.
Удалены:
- test_inn_skips_auditor_block — ложноположительный, docstring утверждал Review Focus,
но тест проходит из-за порядка (org INN раньше auditor), не из-за guard.
Функциональность покрыта test_one_c_header.
- test_inn_from_auditor_above_organization_inn_line — guard не differentiates;
добавлен в попытке тестировать сценарий, невозможный по дизайну.
Оставлен test_inn_from_auditor_block_only_is_none — единственный тест, где guard
реально нужен: без org INN, только auditor блок → guard исключает его, возвращает None.
Guard работает корректно: тестирует edge case отсутствия org INN.
Итог: 6 тестов, все честны относительно требований к guard.
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Обнаружена важная находка (T9.I1): test_inn_skips_auditor_block в ONE_C fixture
проходил из-за структуры данных (org INN выше auditor INN), не благодаря guard.
Добавлены два явных guard-теста, которые ПАДАЮТ БЕЗ guard и ПРОХОДЯТ с guard:
(a) test_inn_from_auditor_block_only_is_none — только block аудитора в шапке:
Без guard (last_row): _inn находит auditor INN = 9999999999 ✗
С guard (min(last_row, auditor_from-1)): находит Nothing = None ✓
(b) test_inn_from_auditor_above_organization_inn_line — org INN до auditor block:
Оба случая находят org INN = 1234567890, но без guard мог бы найти auditor ✓
Дополнительно:
- Удалён импорт приватной _FULL_CONFIDENCE; использован литерал 100 с noqa
- Убрана необходимая кавычка в аннотации _report_date (datetime.date | None)
- Все 8 тестов зелены: 2085 passed в make check
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Реализация Task 9: parse_header() извлекает значения полей из шапки документа ББ/ОФР
по меткам словаря Rules.statement_headers с особыми правилами:
- ИНН ниже блока аудитора пропускается (Review Focus)
- Организация ищется после метки, при пустом значении — выше с признаком ОПФ
- Дата из подзаголовка формы или тройки число/месяц/год
- Единицы, адрес, ОКПО, ОКОПФ/ОКФС по меткам; все найденные поля получают confidence=100
- При отсутствии продолжения блока метка берётся как form_name, остальные поля None
Тесты: 6 базовых сценариев + test_inn_skips_auditor_block (обязательный).
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Листы-продолжения форм (STATEMENT_CONTINUATION) идут в конвейер форм, а не в разбор ОСВ (R-5).
Refs #14
Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b