Узкий вариант: нетто сальдо допустимо, если среди прямых детей есть нетто-ребёнок
(прошёл сверку только по нетто либо имеет такого потомка); обороты — по колонкам.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
I-1 (R-17): синоним COUNTERPARTY только «Организации» (^организации\b).
I-2 (R-18): правило расчётных счетов не применяется к строкам, известным словарю как не-контрагенты (валюта, «В валюте»).
I-4 (R-19): тождество у второй итоговой строки пропускается только без оборотов и при наличии другой «Итого».
M-1, M-5, M-6, M-7 (вымышленное ФИО ИП в синтетике), M-9.
Co-Authored-By: Claude Sonnet 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
- header: период отдельной ячейкой (periods.yaml: standalone), тип данных отдельной ячейкой («Бухгалтерский»,
data_types.yaml: standalone_header), организация — хвост названия ведомости; код счёта — из первой строки-счёта
таблицы (с предупреждением), если «по счету» в шапке нет.
- classify/detect: текст строки может стоять правее колонки текста (колонка задаёт уровень), подписи «Код»/
«Наименование» (osv_headers.yaml: column_captions) — не группировки, строка сумм без подписи в конце таблицы —
«Итого» (с предупреждением).
- checks: сумма уровня 0 сверяется с «Итого» по правилу R-21 (свёрнутое сальдо «Итого» — по нетто), как у родителя.
- tests: osv_dkb_files, покрытие словарей включает osv-dkb/**; README.
Refs #11
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
- checks: «Итого» = первый корень ACCOUNT либо сумма всех строк уровня 0 (лист без строки корневого счёта,
верхний уровень 58.01/58.03; OQ-106, OQ-133). Прежнее правило «= первому корню» остаётся достаточным;
строки-показатели в сумму не входят; основная «Итого» — первая TOTAL листа.
- classify/hierarchy: вторые итоговые строки сразу за «Итого» (osv_headers.yaml: total_row_secondary) остаются
в таблице как TOTAL уровня 0, а не уходят в OTHER; без обычной «Итого» вторая служит единственной итоговой.
Refs #11
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
- I-1 (R-23): readers/_limits.py — сумма ZipInfo.file_size ≤ 10 × PARSE_MAX_BYTES (параметр read_book от
OsvParser), потоковый скан <mergeCell ref> до openpyxl и площадь merged после чтения любого формата
(MERGED_AREA_LIMIT = 200 000; реальный максимум 10 090) → UNSUPPORTED_FORMAT; дедлайн в циклах detect
(поиск строки групп, дополнительные колонки); README «Лимиты» исправлен, «Известные ограничения» (defusedxml).
- I-2: OsvParser логирует стек непредвиденной ошибки (loguru ERROR), UNREADABLE — WARNING без стека.
- M-1: ACCOUNT_RE с re.ASCII | re.DOTALL (многострочное название после запятой, только цифры 0–9).
- M-3 (R-24): правило детектора ИНН не дополняет 9/11 цифр нулём; дополнение — только при извлечении
(numbers.inn_digits) из колонки, выбранной по заголовку или по строгому правилу.
- T9.1: тесты R-21 (свёрнутый родитель на противоположной стороне; (0,0) при детях (X, X)).
- T1.1: хелперы фикстур пропускают скрытые файлы/каталоги и lock-файлы Excel (~$…).
- T12.3: golden-update не удаляет снимки части без исходного каталога (osv/synthetic или реальные файлы).
Golden-снимки не изменились (82/82).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
checks.py: тождество сальдо по каждой строке, суммы родителя = Σ детей того же data_type
(CURRENCY/QUANTITY исключены; обороты — по колонкам, сальдо — по колонкам или по нетто при
свёрнутом сальдо родителя), «Итого» = корневой счёт (R-13: нет корня — «Итого» пусто/0;
R-20: нет «Итого» — false и предупреждение).
osv.py: OsvParser — лимит размера, чтение по сигнатуре, листы по содержимому (формы ББ/ОФР и
прочие — пропуск с предупреждением), карта исходов D-6, кооперативный дедлайн, metrics
(rows без TOTAL, warnings с «sheet=<имя> hierarchy=<стратегия>» первой записью по листу).
get_parser(settings, rules_dir=…) → OsvParser (словари грузятся при создании: битый словарь —
падение старта); PARSE_MAX_BYTES / PARSE_TIMEOUT_SECONDS в Settings и .env.*; app.py передаёт
настройки. test_null_parser.py → test_parser_factory.py.
Тесты: юнит-карта статусов, «Испорченная сумма», «Пустая ОСВ», два листа ОСВ, новый синоним
без релиза кода (R-8); property-тест на 66 реальных файлах (все done, < 5 с, схема v1.1).
test_app и интеграционный round-trip ждут ошибку реального парсера вместо NOT_IMPLEMENTED.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2