feat(osv): форматы пакета ДКБ — период-диапазон, Итого по Σ уровня 0, «Итого развернутое», шапка ОСВ 63 (#11) #13

Merged
aep merged 6 commits from feat/11-dkb-formats into main 2026-10-06 13:04:23 +03:00
Owner

Refs #11 (пункт 2 — счёт 91, OQ-132 — не делался: ждёт решения владельца; поэтому не Closes).

Что сделано

  1. Период-диапазон (periods.yaml: months, range; header.py). «Октябрь 2024 г. – Сентябрь 2025 г.» теперь period_text целиком, period_year = 2025, period_quarter = null, is_cumulative = null и предупреждение «скользящий период 12 мес. … не с начала года». Январь–конец квартала с годами — нарастающий; ровно квартал («Апрель – Июнь 2025») — квартальный. Контракт v1.1 не менялся.
  2. «Итого» ↔ Σ строк уровня 0 (checks.py, OQ-106/OQ-133): «Итого» = первому корню (как раньше) либо сумме строк уровня 0. Сальдо — по колонкам либо по нетто, если «Итого» свёрнуто (правило R-21, применённое к паре «Итого» ↔ Σ уровня 0, см. решения); обороты по колонкам.
  3. Шапка ОСВ 63 НМ: период отдельной ячейкой (periods.standalone), тип данных отдельной ячейкой (data_types.standalone_header), организация — хвост названия ведомости, код счёта — первая строка-счёт таблицы (с предупреждением), «Код/Наименование» — подписи колонок (osv_headers.column_captions), текст строки в колонке правее колонки текста (уровень = колонка), строка сумм без подписи в конце = «Итого» (с предупреждением).
  4. «Итого развернутое» (osv_headers.total_row_secondary): вторая строка TOTAL уровня 0, основная «Итого» — первая; в сверку не входит.
  5. Агрокомплекс 62: нарушения тождества — исследовано, не дефект разноса колонок (rejected, см. находки).

Как проверить

make check            # 1925 passed
make test-fixtures    # 874 passed (golden 88 файлов не изменились)

CLI по пакету: python -m src.parser.cli "fixtures/osv-dkb/nm/2025-Q3/НМ_ОСВ_63_Контрагенты_2025.09-01_24498.xlsx" (из api/, ENV_FILE=.env.test). Покрытие словарей теперь включает fixtures/osv-dkb/** (osv_dkb_files), синтетику не добавлял.

Файл → статус до/после (изменившиеся листы)

файл / лист до после
agrokompleks/2024-10_2025-09 / 76 needs_check; «Октябрь 2024 г.» 2024 Q4 нараст.; Итого≠корню needs_check; «Октябрь 2024 г. - Сентябрь 2025 г.» 2025 null/null; Итого сошёлся (остаются тождество 1217 строк, сумма родителей 11 — данные)
agrokompleks/2025-Q3 / 62 needs_check; Итого≠корню needs_check; Итого сошёлся (остаётся тождество 582 строки — данные)
agrokompleks/2025-Q3 / 67 needs_check (Итого) done
agrokompleks/2025-Q3 / 76 needs_check; Итого≠корню needs_check; Итого сошёлся (остаются тождество 1187, родители 9 — данные)
nm/2025-Q3 / 58, 66, 67 needs_check (Итого) done
nm/2025-Q3 / 60, 62, 76 needs_check (Итого) done
nm/2025-Q3 / ОСВ 63 (Лист1) needs_check; счёт, период, тип данных не найдены; org = «Бухгалтерский»; 52 строки OTHER done; счёт 63, «9 месяцев 2025 г.» 2025 Q3 нараст., BU, org «НОВОРОСМЕТАЛЛ ООО»; контрагенты — уровень 1 (тип OTHER: в файле нет метки «Контрагенты»)
npz/2024-10_2025-09 / 60, 62, 76 done; «Октябрь 2024 г.» 2024 Q4 нараст. (неверно) done; «Октябрь 2024 г. - Сентябрь 2025 г.» 2025 null/null + предупреждение
Остальные листы пакета (Цимлянский, Приманыческий, НКЗ, НПЗ 2025-Q3) без изменений; 91 — по-прежнему needs_check (OQ-132).

Находки

маркер суть статус
A11-1 Для скользящего периода в контракте v1.1 нет поля «не с начала года»; признак выражен через null/null + предупреждение. Предложение для v1.2: period_start_month/period_end_month (или period_kind: ROLLING) deferred
A11-2 Агрокомплекс 62/76: строки аналитики магазинов (62.04, 76.11, 76.41.1…) несут только обороты, сальдо на конец в файле пусто (родитель 62.04 сходится Σ детей: parent_sum=0 для 62; пустые названия контрагентов в 76 — «не задано»). Разнос колонок верен (те же колонки, что у соседних строк) — нарушения тождества — свойство данных, того же класса, что OQ-132 rejected: данные файла, не дефект парсера
A11-3 Тип группировки контрагентов ОСВ 63 НМ — OTHER: ни шапка, ни заголовок не называют «Контрагенты» (только имя файла) deferred (ядро/аналитик; структуру по имени файла не определяем)
A11-4 Синонимы НУ для standalone_header («Налоговый» отдельной ячейкой) не добавлены: нет файла-покрытия deferred
A11-5 НКЗ 58 (лист) needs_check «сумма детей ≠ родитель» (строки 12, 24) — вне пунктов #11 deferred
A11-6 docs: 05-domain-rules.md §1.4/§1.5 и OQ-131/133/134 нужно обновить (репозиторий docs не трогал) deferred

Decisions made without discussion

  • 2026-10-06: скользящий период → period_quarter/is_cumulative = null (не Q3/false): ядро иначе привяжет 12-месячный документ как квартальный. Цена ошибки: ядро видит «период не определён» и просит аналитика.
  • 2026-10-06: сверка «Итого» с Σ уровня 0 использует нетто-допуск R-21 при свёрнутом сальдо «Итого» (в НМ/Агрокомплексе «Итого» — свёрнутое, субсчета — развёрнутое). Это применение, а не расширение R-21, но трактовка моя. Цена ошибки: пропуск перестановки ст��рон при совпавшем нетто на листе с несколькими корнями. Нужно подтверждение владельца.
  • 2026-10-06: старое правило «Итого = первому корню» оставлено достаточным (Σ — дополнительный путь), чтобы не менять статусы существующих 88 golden-файлов.
  • 2026-10-06: строка сумм без подписи в конце таблицы принимается за «Итого» с предупреждением (ОСВ 63 НМ). Цена ошибки: при потере подписи реальная последняя строка станет TOTAL; ловит сверка «Итого = корень».
  • 2026-10-06: вторая итоговая строка включается в rows как TOTAL (вторая запись TOTAL в документе); ядро должно учитывать, что TOTAL может быть не один.

Что проверить человеку

  • Решение про нетто-сверку Итого (выше) и про null/null для скользящего периода.
  • Предложение поля для контракта v1.2 (A11-1).
  • Что ядро (arm-core) переживает документ с двумя строками TOTAL.

🤖 Generated with Claude Code

Refs #11 (пункт 2 — счёт 91, OQ-132 — не делался: ждёт решения владельца; поэтому не Closes). ## Что сделано 1. **Период-диапазон** (`periods.yaml`: `months`, `range`; `header.py`). «Октябрь 2024 г. – Сентябрь 2025 г.» теперь `period_text` целиком, `period_year` = 2025, `period_quarter` = null, `is_cumulative` = null и предупреждение «скользящий период 12 мес. … не с начала года». Январь–конец квартала с годами — нарастающий; ровно квартал («Апрель – Июнь 2025») — квартальный. Контракт v1.1 не менялся. 3. **«Итого» ↔ Σ строк уровня 0** (`checks.py`, OQ-106/OQ-133): «Итого» = первому корню (как раньше) либо сумме строк уровня 0. Сальдо — по колонкам либо по нетто, если «Итого» свёрнуто (правило R-21, применённое к паре «Итого» ↔ Σ уровня 0, см. решения); обороты по колонкам. 4. **Шапка ОСВ 63 НМ**: период отдельной ячейкой (`periods.standalone`), тип данных отдельной ячейкой (`data_types.standalone_header`), организация — хвост названия ведомости, код счёта — первая строка-счёт таблицы (с предупреждением), «Код/Наименование» — подписи колонок (`osv_headers.column_captions`), текст строки в колонке правее колонки текста (уровень = колонка), строка сумм без подписи в конце = «Итого» (с предупреждением). 5. **«Итого развернутое»** (`osv_headers.total_row_secondary`): вторая строка TOTAL уровня 0, основная «Итого» — первая; в сверку не входит. 6. **Агрокомплекс 62: нарушения тождества** — исследовано, **не дефект разноса колонок** (rejected, см. находки). ## Как проверить ``` make check # 1925 passed make test-fixtures # 874 passed (golden 88 файлов не изменились) ``` CLI по пакету: `python -m src.parser.cli "fixtures/osv-dkb/nm/2025-Q3/НМ_ОСВ_63_Контрагенты_2025.09-01_24498.xlsx"` (из `api/`, `ENV_FILE=.env.test`). Покрытие словарей теперь включает `fixtures/osv-dkb/**` (`osv_dkb_files`), синтетику не добавлял. ### Файл → статус до/после (изменившиеся листы) | файл / лист | до | после | |---|---|---| | agrokompleks/2024-10_2025-09 / 76 | needs_check; «Октябрь 2024 г.» 2024 Q4 нараст.; Итого≠корню | needs_check; «Октябрь 2024 г. - Сентябрь 2025 г.» 2025 null/null; Итого сошёлся (остаются тождество 1217 строк, сумма родителей 11 — данные) | | agrokompleks/2025-Q3 / 62 | needs_check; Итого≠корню | needs_check; Итого сошёлся (остаётся тождество 582 строки — данные) | | agrokompleks/2025-Q3 / 67 | needs_check (Итого) | done | | agrokompleks/2025-Q3 / 76 | needs_check; Итого≠корню | needs_check; Итого сошёлся (остаются тождество 1187, родители 9 — данные) | | nm/2025-Q3 / 58, 66, 67 | needs_check (Итого) | done | | nm/2025-Q3 / 60, 62, 76 | needs_check (Итого) | done | | nm/2025-Q3 / ОСВ 63 (Лист1) | needs_check; счёт, период, тип данных не найдены; org = «Бухгалтерский»; 52 строки OTHER | done; счёт 63, «9 месяцев 2025 г.» 2025 Q3 нараст., BU, org «НОВОРОСМЕТАЛЛ ООО»; контрагенты — уровень 1 (тип OTHER: в файле нет метки «Контрагенты») | | npz/2024-10_2025-09 / 60, 62, 76 | done; «Октябрь 2024 г.» 2024 Q4 нараст. (неверно) | done; «Октябрь 2024 г. - Сентябрь 2025 г.» 2025 null/null + предупреждение | Остальные листы пакета (Цимлянский, Приманыческий, НКЗ, НПЗ 2025-Q3) без изменений; 91 — по-прежнему needs_check (OQ-132). ## Находки | маркер | суть | статус | |---|---|---| | A11-1 | Для скользящего периода в контракте v1.1 нет поля «не с начала года»; признак выражен через null/null + предупреждение. Предложение для v1.2: `period_start_month`/`period_end_month` (или `period_kind: ROLLING`) | deferred | | A11-2 | Агрокомплекс 62/76: строки аналитики магазинов (62.04, 76.11, 76.41.1…) несут только обороты, сальдо на конец в файле пусто (родитель 62.04 сходится Σ детей: parent_sum=0 для 62; пустые названия контрагентов в 76 — «не задано»). Разнос колонок верен (те же колонки, что у соседних строк) — нарушения тождества — свойство данных, того же класса, что OQ-132 | rejected: данные файла, не дефект парсера | | A11-3 | Тип группировки контрагентов ОСВ 63 НМ — OTHER: ни шапка, ни заголовок не называют «Контрагенты» (только имя файла) | deferred (ядро/аналитик; структуру по имени файла не определяем) | | A11-4 | Синонимы НУ для `standalone_header` («Налоговый» отдельной ячейкой) не добавлены: нет файла-покрытия | deferred | | A11-5 | НКЗ 58 (лист) needs_check «сумма детей ≠ родитель» (строки 12, 24) — вне пунктов #11 | deferred | | A11-6 | docs: `05-domain-rules.md` §1.4/§1.5 и OQ-131/133/134 нужно обновить (репозиторий docs не трогал) | deferred | ## Decisions made without discussion - 2026-10-06: скользящий период → `period_quarter`/`is_cumulative` = null (не Q3/false): ядро иначе привяжет 12-месячный документ как квартальный. Цена ошибки: ядро видит «период не определён» и просит аналитика. - 2026-10-06: сверка «Итого» с Σ уровня 0 использует нетто-допуск R-21 при свёрнутом сальдо «Итого» (в НМ/Агрокомплексе «Итого» — свёрнутое, субсчета — развёрнутое). Это применение, а не расширение R-21, но трактовка моя. Цена ошибки: пропуск перестановки ст��рон при совпавшем нетто на листе с несколькими корнями. **Нужно подтверждение владельца.** - 2026-10-06: старое правило «Итого = первому корню» оставлено достаточным (Σ — дополнительный путь), чтобы не менять статусы существующих 88 golden-файлов. - 2026-10-06: строка сумм без подписи в конце таблицы принимается за «Итого» с предупреждением (ОСВ 63 НМ). Цена ошибки: при потере подписи реальная последняя строка станет TOTAL; ловит сверка «Итого = корень». - 2026-10-06: вторая итоговая строка включается в `rows` как TOTAL (вторая запись TOTAL в документе); ядро должно учитывать, что TOTAL может быть не один. ## Что проверить человеку - Решение про нетто-сверку Итого (выше) и про null/null для скользящего периода. - Предложение поля для контракта v1.2 (A11-1). - Что ядро (arm-core) переживает документ с двумя строками TOTAL. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 4 commits 2026-10-06 12:04:58 +03:00
Раньше первый месяц диапазона давал «2024 Q4, нарастающий». Теперь словарь periods.yaml (months, range)
разбирает диапазон: период режется по году конца, январь–конец квартала — нарастающий, ровно квартал — квартальный,
остальное (скользящие 12 месяцев) — год конца, квартал и is_cumulative = null + предупреждение «не с начала года».
Контракт v1.1 не менялся.

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
Refs #11

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
- 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
aep added 2 commits 2026-10-06 13:00:41 +03:00
- OQ-100: ИНН в конце названия («Полиграф, 3528315710», «(ИНН …)», пробел) уходит в counterparty_inn, имя — без ИНН
  и разделителя; колонка ИНН приоритетнее; только для COUNTERPARTY.
- OQ-130: строка без сумм с одним ИНН над строкой с суммами того же уровня склеивается со следующей и не уходит в OTHER.
- «ИНН/КПП» в ячейке колонки ИНН даёт ИНН.
- Правила — osv_headers.yaml (counterparty_inn_suffix, inn_only_row, inn_kpp_cell), не код.
- Синтетика s17–s19 (генератор, тесты); golden — в репозитории fixtures (feat/9-golden).

Stacked on #13 (feat/11-dkb-formats).
Closes #9

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
Reviewed-on: #15
aep merged commit 5cb8e9c003 into main 2026-10-06 13:04:23 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/arm-tabular-parser#13
No description provided.