From f7cb32e3d4a9ac37c497c99f6cb90f6ecfeca519 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Wed, 28 Oct 2026 21:09:27 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F:=20=D0=BF=D1=80=D0=B8=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D0=BA=D0=B8=20=D0=BB=D0=B8=D1=81=D1=82=D0=B0=20?= =?UTF-8?q?=D0=9E=D0=A1=D0=92,=20=D1=81=D0=B8=D0=BD=D1=82=D0=B5=D1=82?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=20s48=E2=80=93s57,=20=D0=B8=D1=82=D0=BE?= =?UTF-8?q?=D0=B3=20=D1=80=D0=B5=D0=B3=D1=80=D0=B5=D1=81=D1=81=D0=B8=D0=B8?= =?UTF-8?q?=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 5 ++++- README.md | 2 +- openspec/changes/osv-other-systems/design.md | 22 ++++++++++++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d61ac68..2818b06 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,7 +75,10 @@ CORS, регистрация SMB-сессии, импорт `raisa_fastapi_prote обращения к ячейке константами. Значения enum контракта вычисляются из `src.contract` и под линтер не попадают. - Каждая запись словаря должна срабатывать хотя бы на одном реальном или синтетическом файле (`tests/fixtures/test_rules_coverage.py`); мёртвый синоним удаляется, пробел в фикстурах закрывается синтетикой. -- Golden-снимки (`../fixtures/golden/expected_osv_parse/`, 136 файлов: 72 реальных + 23 пакета ДКБ (`osv-dkb/**` до 1 МиБ, снимки в `osv-dkb/`) + 41 синтетический) — регрессия, а не +- Признак листа ОСВ шире названия ведомости — запасной признак по заголовку таблицы (`detect.has_osv_table`, #81). + Его ограничения (три группы, Дт и Кт рядом, первые 30 строк, без свёрнутого сальдо) держат негативы синтетики + s55–s57: ослабили ограничение — падает свой негатив. Новое ослабление — только с новым негативом. +- Golden-снимки (`../fixtures/golden/expected_osv_parse/`, 149 файлов: 72 реальных + 23 пакета ДКБ (`osv-dkb/**` до 1 МиБ, снимки в `osv-dkb/`) + 54 синтетических) — регрессия, а не оракул; правятся только `make golden-update` с просмотром diff и отдельным коммитом в `fixtures`. Корректность проверяют property-тест и ручная сверка «Итого». - Деньги — `Decimal`, `float` в домене нет; `needs_check` — только по контрольным суммам, структуру под сходимость не diff --git a/README.md b/README.md index 66d6a47..6e1882c 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ ParseOutcome`. Структура ОСВ ищется **по содержимо | `grid.py` | модель листа: `Cell`, `Sheet`, `MergedRange`, `column_letter` (буква колонки — только для диагностики) | | `readers/` | чтение книги в `Sheet`: `read_book(data, file_type) -> (листы, предупреждения)`; формат определяется по сигнатуре байтов (xlsx — openpyxl; части архива, отличающиеся от ссылок `[Content_Types].xml`/`*.rels` только регистром (`xl/SharedStrings.xml`), до openpyxl переименовываются по ссылкам, как их находит Excel — AURORA-1707; xls — xlrd с `formatting_info`, xlsb — pyxlsb, без outline level); несовпадение с `file_type` — предупреждение; до библиотеки чтения — лимиты чтения (`_limits.py`, см. «Лимиты») | | `rules.py` | загрузчик словарей `rules/*.yaml` в frozen-модели (pydantic), `normalize_text`, `Pattern`; ошибка словаря — `RuntimeError` с файлом и ключом при старте | -| `detect.py` | вид листа (`classify_sheet`: ОСВ / форма ББ-ОФР / прочее), блок заголовка таблицы (`find_table_header`: 1–4 строки, шесть колонок сумм, колонка текста, ИНН, дополнительные колонки, правая граница, повтор заголовка внутри данных) | +| `detect.py` | вид листа (`classify_sheet`: ОСВ / форма ББ-ОФР / прочее; ОСВ — по названию ведомости `sheet_markers` или, если лист не форма, по полному заголовку таблицы `has_osv_table`: три группы, у каждой «Дебет» и «Кредит» рядом, в первых 30 строках, #81), блок заголовка таблицы (`find_table_header`: 1–4 строки, шесть колонок сумм, колонка текста, ИНН, дополнительные колонки, правая граница, повтор заголовка внутри данных) | | `header.py` | шапка документа (организация, счёт, период, тип данных, валюта) и схема группировок `group_schema` (варианты шапки 1 и 2) | | `hierarchy.py` | каскад стратегий уровней строк: `outline` → `indent` → `account_pattern` → `header_account` → `sums`; `trivial` — меньше двух строк данных; `header_account` — плоский список под счётом шапки (код счёта в шапке, строк-счетов в теле нет; плоская форма 1С, #59); выбранная стратегия пишется в `warnings[]` документа листа как `OSV_HIERARCHY_BY_<СТРАТЕГИЯ>` (`info`) | | `numbers.py` | суммы в `Decimal` с двумя знаками (`to_money`), ИНН из текста ячейки (`inn_digits`); `float` в домене нет | diff --git a/openspec/changes/osv-other-systems/design.md b/openspec/changes/osv-other-systems/design.md index 54b688e..5129fd9 100644 --- a/openspec/changes/osv-other-systems/design.md +++ b/openspec/changes/osv-other-systems/design.md @@ -52,9 +52,10 @@ ### D1. Запасной признак листа — последним шагом (решение владельца 28.10.2026) Порядок классификации: признак ОСВ по названию (`sheet_markers`) → форма / продолжение / прочее (пакет). Только если -пакет вернул «прочее», лист проверяется запасным признаком: в первых `SHEET_MARKER_ROWS` строках есть строка групп, где +пакет вернул «прочее», лист проверяется запасным признаком — раньше слабого продолжения формы (R-12): в первых `SHEET_MARKER_ROWS` строках есть строка групп, где найдены **все три** группы (начало, обороты, конец), и у каждой группы найдены **обе** стороны — Дебет и Кредит -(в ячейках под группой или в самой ячейке группы). Свёрнутое сальдо (одна колонка в группе) запасной признак не даёт. +(в ячейках под группой или в самой ячейке группы), и между ними в строках заголовка нет других подписей (R-11). +Свёрнутое сальдо (одна колонка в группе) запасной признак не даёт. Нашёлся — лист `OSV`, дальше обычный конвейер ОСВ; нет — `SHEET_OTHER`, как сейчас. Почему так: расшифровки ББ с колонками «Остаток на начало / … / Остаток на конец» забирает классификатор форм раньше, @@ -105,6 +106,17 @@ xlsb) на main `a8c9605` и на ветке. Сравнение по каждо и сторон дополняются написаниями из D2. PR в arm-ka-docs из ветки `feat/81-osv-other-systems` worktree `.wt/osv-dict/docs`, ссылка на tabular-parser#81. +## Итог регрессии (28.10.2026) + +Банк первички, 2152 файла (`admin/osv-other-systems-regress/report.md` workspace): 60 файлов из ошибки → ОСВ «Готово», +70 → «Требует проверки», 40 узнаны как ОСВ, но стоят на 1720–1723; ни один лист формы или прочий лист книги с формами +не стал ОСВ; ухудшения — только R-14. Файлы списков 1717/1719 (175): 60 «Готово», 60 «Требует проверки», 50 ошибка +формата, 4 ложных «Готово» безымянных ведомостей 009, которые забирает конвейер форм (D1), 1 — неполный заголовок. + +Контрольный набор синтетики s48–s57: позитивы на базе падали, негативы проходили; на ветке проходят все. Мутации +запасного признака (без R-11, без лимита 30 строк, со свёрнутым сальдо, с двумя группами) и снятие якоря `^` у названий +ловит каждая свой негатив (s55, s56, s57). + ## Risks / Trade-offs - [`^осв\b` и «оборотн… ведомост» в тексте формы] → проверка ОСВ идёт первой; регрессия D3 ловит листы форм, ставшие @@ -126,4 +138,10 @@ xlsb) на main `a8c9605` и на ветке. Сравнение по каждо | 2026-10-28 | R-6. Запасной признак переиспользует поиск строки групп и колонок обычного заголовка (`_find_group_row`, `_columns_of_group`) в строгом режиме, а не отдельную функцию | запасной признак и конвейер не должны расходиться в том, что считать заголовком | — | | 2026-10-28 | R-7. Порядок PR: `fixtures` (golden синтетики, отдельный коммит `make golden-update`) → tabular-parser → docs | golden-тесты tabular-parser читают `../fixtures` | — | | 2026-10-28 | R-8. Приёмка — синтетика + `make check-full` + нет ухудшений D3; число исправленных файлов банка — отчёт, не порог | эксперимент автора задач — ориентир, а не обязательство; остаток держат 1720–1723 | — | +| 2026-10-28 | R-9. Написание «Остаток по дебету/кредиту на начало/конец» (6 файлов КТЗ/ЧАЗ) включено в словарь: группа «остаток» с опережающей проверкой, сторона — из остатка ячейки | та же семья «Остаток на начало/конец» (1719), решается записью словаря | лишняя запись, удаляется | +| 2026-10-28 | R-10. В синтетике s50 у SAP-листа обороты «Оборот Дт / Оборот Кт», а не реальное «Дт оборот» | сторона перед группой — AURORA-1722, вне объёма (R-4) | файлы Виолы остаются с ошибкой до 1722 | +| 2026-10-28 | R-11. Запасной признак требует, чтобы между колонками «Дебет» и «Кредит» каждой группы в строках заголовка не было других подписей | иначе 1С «Обороты счета» (Дт/Кт оборота разделены колонками корр. счетов) проходит как ОСВ вопреки R-1; держит негатив s55 | безымянная ОСВ с колонкой между Дт и Кт не распознаётся | +| 2026-10-28 | R-12. Запасной признак проверяется раньше слабого продолжения формы (лист из 1–2 кодов сразу после формы), но после сильных признаков формы и продолжения | на базе лист Navision после формы ББ пакет присоединял к ней как слабое продолжение (синтетика s52); полная таблица ОСВ — сильнее эвристики «1–2 кода» | лист-продолжение формы с полной таблицей ОСВ отделится от формы (у продолжения формы таких групп нет) | +| 2026-10-28 | R-13. Признак «ОСВ …» сужен до `^осв\s+по\s+(\w+\s+по\s+)?сч\b` вместо `^осв\b` | первый прогон банка: строка «ОСВ за квартал по счетам …» перечня документов на листе «Входные данные_ИП» шаблона банка делала лист ОСВ, его разбор падал, и вся книга расшифровок становилась ошибкой (86 файлов); все 20 реальных названий начинаются с «ОСВ по сч.» / «ОСВ по ДЗ по сч.»; держит негатив s55 | «ОСВ» с другим продолжением по названию не узнаётся (остаётся запасной признак) | +| 2026-10-28 | R-14. Два файла «Сводная ОСВ … (забаланс)»: ложное «Готово» расшифровки на базе → ошибка иерархии ОСВ на ветке принято, а не исправлено | это цель изменения (ведомость «Оборотный баланс / Оборотная ведомость» перестаёт быть расшифровкой, 1718); дальше файл держит иерархия «002.01: Название» — AURORA-1721 | у двух книг «Ошибка» вместо фальшивого «Готово» до 1721 | | 2026-10-28 | R-4. AURORA-1722 (SAP со стороной перед группой, НОВО-МАР) не входит | в задачах указан порядок: после 1717/1719; причина варианта НОВО-МАР не разобрана | эти файлы остаются с ошибкой до 1722 |