From 480db4f3aa479368243eda2747500def6cc405cf Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 19 Oct 2026 12:26:03 +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=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=BA=D0=B8-=D0=BF=D0=BE=D0=BA=D0=B0=D0=B7=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D0=B8=20=D0=9E=D0=A1=D0=92=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 4 ++-- README.md | 17 ++++++++++++++++- openspec/changes/osv-indicator-column/tasks.md | 16 ++++++++-------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5d880ed..4aed44e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,7 +62,7 @@ CORS, регистрация SMB-сессии, импорт `raisa_fastapi_prote (`column_letter` — только диагностика). `read_book(data, file_type) -> (листы, предупреждения)`, формат — по сигнатуре. - `rules.py` — загрузчик словарей `api/rules/*.yaml`; `detect.py` — вид листа и заголовок таблицы; `header.py` — шапка и `group_schema`; `hierarchy.py` — каскад `outline → indent → account_pattern → sums` (+ `trivial`); `inn_rows.py` — - строки ИНН рядом с контрагентом (OQ-163); `numbers.py` — `Decimal` и ИНН (контрольная сумма); `classify.py` — строки и группировки; `checks.py` — контрольные суммы §1.4; `osv.py` — + строки ИНН рядом с контрагентом (OQ-163); `indicators.py` — строки-показатели (колонка «Показатели», «В валюте»); `numbers.py` — `Decimal` и ИНН (контрольная сумма); `classify.py` — строки и группировки; `checks.py` — контрольные суммы §1.4; `osv.py` — оркестратор `OsvParser`; `deadline.py` — кооперативный таймаут; `errors.py` — коды ошибок; `cli.py` — разбор файла. - `get_parser(settings)` создаёт `OsvParser` один раз при старте (кэша нет); `NullParser` — только для тестов воркера. @@ -75,7 +75,7 @@ CORS, регистрация SMB-сессии, импорт `raisa_fastapi_prote обращения к ячейке константами. Значения enum контракта вычисляются из `src.contract` и под линтер не попадают. - Каждая запись словаря должна срабатывать хотя бы на одном реальном или синтетическом файле (`tests/fixtures/test_rules_coverage.py`); мёртвый синоним удаляется, пробел в фикстурах закрывается синтетикой. -- Golden-снимки (`../fixtures/golden/expected_osv_parse/`, 127 файлов: 72 реальных + 23 пакета ДКБ (`osv-dkb/**` до 1 МиБ, снимки в `osv-dkb/`) + 32 синтетических) — регрессия, а не +- Golden-снимки (`../fixtures/golden/expected_osv_parse/`, 131 файл: 72 реальных + 23 пакета ДКБ (`osv-dkb/**` до 1 МиБ, снимки в `osv-dkb/`) + 36 синтетических) — регрессия, а не оракул; правятся только `make golden-update` с просмотром diff и отдельным коммитом в `fixtures`. Корректность проверяют property-тест и ручная сверка «Итого». - Деньги — `Decimal`, `float` в домене нет; `needs_check` — только по контрольным суммам, структуру под сходимость не diff --git a/README.md b/README.md index 1482637..d0250f8 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ ParseOutcome`. Структура ОСВ ищется **по содержимо | `hierarchy.py` | каскад стратегий уровней строк: `outline` → `indent` → `account_pattern` → `sums`; `trivial` — меньше двух строк данных; выбранная стратегия пишется в `warnings[]` документа листа как `OSV_HIERARCHY_BY_<СТРАТЕГИЯ>` (`info`) | | `numbers.py` | суммы в `Decimal` с двумя знаками (`to_money`), ИНН из текста ячейки (`inn_digits`); `float` в домене нет | | `inn_rows.py` | строки ИНН рядом с контрагентом (восемь раскладок, контрольная сумма без подсказки шапки, противоречие подсказке, OQ-163); выполняется над деревом до классификации | +| `indicators.py` | строки-показатели (§1.3): метка вида суммы из колонки «Показатели» или текста строки (`read_label`), привязка показателя к строке данных над ним (`levels_with_indicators`) | | `classify.py` | сбор строк таблицы (`collect_body_rows`: «Итого», свёрнутое сальдо) и тип группировки, счёт, контрагент, `data_type`, `extra` (`classify_rows`) | | `checks.py` | контрольные суммы §1.4: тождество сальдо строки, «сумма детей = родитель», «Итого» = корень; счета без сальдо по аналитике (91, 99) — вне тождества строки (`osv_checks.yaml`) | | `osv.py` | оркестратор `OsvParser`: лимит размера → чтение (с лимитами чтения) → лист за листом → статус и метрики | @@ -153,6 +154,20 @@ ParseOutcome`. Структура ОСВ ищется **по содержимо `warnings[]` его документа (`OSV_*`, `info`): стратегия иерархии (`OSV_HIERARCHY_BY_<СТРАТЕГИЯ>`), свёрнутое сальдо, нераспознанные числа с номером строки, непрочитанные поля шапки, «нет строк данных» (пустая ОСВ). +### Строки-показатели и колонка «Показатели» (§1.3, AURORA-1613, AURORA-1661) + +Колонка «Показатели» (`service_columns.indicator`; заголовок часто «Показа-тели») несёт вид суммы строки: «БУ» — обычная +строка данных (`data_type = BU`), «НУ», «Вал.», «Кол.» — показатели (`CURRENCY`/`QUANTITY` по словарю +`data_types.yaml`: `indicator_column`), «ПР», «ВР» (`indicator_column_untyped`) и неизвестная метка — показатель с +`data_type = null` (неизвестная — ещё и предупреждение `OSV_ROWS`). Показатель — строка с признаком `indicator`: он +не входит в суммирование «сумма детей = родитель» и не проверяется как родитель, его родитель в дереве — предыдущая строка +данных (`indicators.py`, вне каскада иерархии); `row_value` строки с меткой колонки — сама метка, колонка остаётся в +`extra`. Строка «В валюте» (пары «Валюта <код>» / «В валюте» на одном уровне) — показатель `CURRENCY`, «Валюта <код>» — +обычная строка в рублях. Итоговые строки «Итого» по БУ и НУ входят в таблицу; сверка идёт с первой итоговой +строкой, не показателем. Если шапка не называет вид данных, строки без собственной метки получают `BU`, а в +`warnings[]` листа — `OSV_HEADER` «вид данных в шапке не указан — строки без метки приняты как БУ» (`header.data_type` +остаётся `null`). + ### Контрольные суммы (§1.4) Допуск ±0.01, пустое значение = 0. Строки «Вал.», «Кол.» и код валюты (`data_type = CURRENCY | QUANTITY`) в суммирование @@ -378,7 +393,7 @@ git -C ../fixtures add golden && git -C ../fixtures commit # снимки — для изменившегося — только diff; сводка «файлов / без изменений / новых / изменено / лишних» идёт в stderr. Снимки, которым не соответствует ни один файл, считаются лишними: `make golden-update` их удаляет, `make golden-check` завершается кодом 1. -Каталог `fixtures/osv/synthetic/` — 19 синтетических файлов `s01`–`s19` (генератор — `tests/synthetic/generate.py`): +Каталог `fixtures/osv/synthetic/` — синтетические файлы `s01`–`s36` (генератор — `tests/synthetic/generate.py`): форматы, которых нет в реальных ОСВ (шапка варианта 2, «Вал.»/«Кол.», xlsb, без outline level, текстовые числа и т. д.). Дополнительно: `tests/fixtures/test_rules_coverage.py` требует, чтобы каждая запись словарей `api/rules/*.yaml` сработала хотя бы на одном файле (мёртвый синоним — удалить или добавить синтетику, см. `api/rules/README.md`), а diff --git a/openspec/changes/osv-indicator-column/tasks.md b/openspec/changes/osv-indicator-column/tasks.md index c8a1224..4273ff9 100644 --- a/openspec/changes/osv-indicator-column/tasks.md +++ b/openspec/changes/osv-indicator-column/tasks.md @@ -3,30 +3,30 @@ План исполнения — `docs/superpowers/plans/2026-10-19-osv-indicator-column.md` (задачи 1–8, модели исполнителей там). ## 1. Словари -- [ ] 1.1 `api/rules/osv_headers.yaml` — `service_columns.indicator`; `api/rules/data_types.yaml` — `indicator_column`, +- [x] 1.1 `api/rules/osv_headers.yaml` — `service_columns.indicator`; `api/rules/data_types.yaml` — `indicator_column`, `indicator_column_untyped`, `row: «В валюте»`; `src/parser/rules.py` — поля `DataTypeRules`; тесты `test_rules.py` ## 2. Синтетика -- [ ] 2.1 `tests/synthetic/generate.py` — s33–s36 (`_grid_osv`); `tests/synthetic/test_synthetic_files.py` — ожидания +- [x] 2.1 `tests/synthetic/generate.py` — s33–s36 (`_grid_osv`); `tests/synthetic/test_synthetic_files.py` — ожидания и сценарии (сначала красные); файлы в `../fixtures/osv/synthetic/` ## 3. Колонка «Показатели» в заголовке -- [ ] 3.1 `src/parser/detect.py` — `TableHeader.indicator_col`; тест `test_detect.py` +- [x] 3.1 `src/parser/detect.py` — `TableHeader.indicator_col`; тест `test_detect.py` ## 4. Строки-показатели при сборе строк -- [ ] 4.1 `src/parser/hierarchy.py` (`BodyRow`), новый `src/parser/indicators.py` (`read_label`), `src/parser/classify.py` +- [x] 4.1 `src/parser/hierarchy.py` (`BodyRow`), новый `src/parser/indicators.py` (`read_label`), `src/parser/classify.py` (`collect_body_rows`, `_label_cols`, `_trailing_totals`); тесты `test_classify.py`, `test_indicators.py` ## 5. Иерархия -- [ ] 5.1 `indicators.levels_with_indicators`, `osv.py` (вызов), `inn_rows._kind`; тест `test_indicators.py` +- [x] 5.1 `indicators.levels_with_indicators`, `osv.py` (вызов), `inn_rows._kind`; тест `test_indicators.py` ## 6. Классификация и проверки -- [ ] 6.1 `classify.py` (`_Kind`, `ClassifiedRow.indicator`, вид данных строки), `checks.py` (`_is_indicator`, `_total`), +- [x] 6.1 `classify.py` (`_Kind`, `ClassifiedRow.indicator`, вид данных строки), `checks.py` (`_is_indicator`, `_total`), `osv.py` (предупреждение «приняты как БУ»); тесты `test_classify.py`, `test_checks.py` ## 7. Регрессия и реальные файлы -- [ ] 7.1 Синтетика зелёная; `make golden-check` — снимки реальных файлов и s01–s32 без изменений; `make golden-update` +- [x] 7.1 Синтетика зелёная; `make golden-check` — снимки реальных файлов и s01–s32 без изменений; `make golden-update` только для s33–s36; локальная сверка файлов контура (не в git) ## 8. Документация и проверки -- [ ] 8.1 README (раздел «Парсер ОСВ»), `../docs/context/06-test-data.md` §7 (s33–s36), `make check-full` +- [x] 8.1 README (раздел «Парсер ОСВ»), `../docs/context/06-test-data.md` §7 (s33–s36), `make check-full`