Andrey Epifancev 97c8dc05a3 Архив change statement-form-split (AURORA-1734/1735, #214)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:21:12 +04:00

53 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Context
Решения владельца 06.11.2026 (выбраны рекомендованные варианты): 1734 — резать по смене формы у себя; 1735 — отсеивать
редкую колонку с неразобранной подписью. Разбор на JSON из задач (прогон нормализатора scan-gateway на пакете 0.1.3):
- АК-ЮЛ: один документ `BALANCE`, 54 кода (ББ + ОФР), `SEGMENT_COLUMNS_MISMATCH` у сегмента строки 19, десять жёстких
нарушений ББ. Строка 41 — шапка с «Коды» (столбец кодов сдвинут в столбец 2), сегмент открывается, но остаётся в ББ.
- Калтан, ОФР: колонки 4 / 5 («31») / 6, `COLUMN_WITHOUT_DATE` по столбцу 5; в столбце 5 разбирается одно число —
«$301 .» (тире «—», «——-» числами не считаются).
## Decisions
- **Разрез — отдельная функция до раскладки.** `normalize_candidate` возвращает один документ, хосты вызывают его на
каждый кандидат с изоляцией ошибок. Менять `CandidateResult` на список ломает API у трёх потребителей; разрез на
кандидаты сохраняет шаги 2–8 нетронутыми — каждая часть проходит их как обычный кандидат (свои колонки, периоды,
§7). Сегменты для разреза ищутся тем же кодом шага 2 (`_split_and_analyse`), что и для раскладки.
- **Условие разреза:** своя шапка + первые `min_codes` (3) кодов сегмента — одной формы, отличной от формы текущей части. Шапка нужна,
чтобы не резать по случайным строкам; порог — чтобы пара строк другой формы (OCR-мусор, «пояснения» в хвосте) не
создавала документ. Тип части — по первому сегменту части с определённым типом.
- **Часть `k ≥ 1`:** `type_hint = null`, пустые `evidence` и `warnings` — метка таблицы ИС относится к первой форме, а
предупреждения адаптера (единицы, шапка) уже есть у первой части; повторять их в каждой части — дубли.
- **Редкий столбец:** «между» — есть колонка периода левее и правее; «редкий» — число не более чем в одной строке с
кодом при ≥ 3 у каждой колонки периода сегмента. Правило применяется в `segment_columns` (у каждого сегмента со своей
шапкой), адреса идут в общее `NUMBERS_OUTSIDE_TABLE` формы.
## Decisions made without discussion
| Дата | Решение | Обоснование | Цена ошибки |
|---|---|---|---|
| 2026-11-06 | `locator` части — `<locator>;form:<k>` (k с 1); первая часть — прежний `locator` | Locator нигде не разбирается (ядро и UI показывают как строку); формат читаемый, у первой части ничего не меняется — golden и уже сохранённые документы не съезжают | Низкая: строка в карточке документа; поменять формат — одна строка кода |
| 2026-11-06 | Порог разреза — существующий `min_codes` словаря диапазонов (3), не новый ключ | Тот же смысл «достаточно кодов, чтобы считать это формой» уже есть в словаре | Низкая: порог — данные |
| 2026-11-06 | Разрез — общий для OCR и Excel | Правила шага 2 общие (как Р-44); у Excel формы обычно на разных листах — разрез почти не срабатывает, но лист «ББ + ОФР» с повторной шапкой тоже станет двумя документами | Средняя: golden tabular-parser может измениться — проверяется при подъёме пина, при сюрпризе — флаг источника |
| 2026-11-06 | Текст `NUMBERS_OUTSIDE_TABLE`: «числа вне колонок формы не учитываются», если есть адрес редкого столбца, иначе прежний | Прежний текст «правее таблицы» неверен для столбца посередине; прежние случаи и их golden не меняются | Нулевая: текст предупреждения |
| 2026-11-06 | «Редкий»: ≤ 1 число в столбце и ≥ 3 у каждой колонки периода | Калтан: 1 против 10+; столбец с двумя числами уже может быть данными — оставляем аналитику | Низкая: порог — константа пакета |
| 2026-11-06 | Реальные JSON АК-ЮЛ и Калтана в fixtures не кладём; тесты пакета — на синтетических кандидатах (`tests/builders.py`), прогон реальных файлов — вручную через нормализатор scan-gateway | Файлы заказчика с контура; синтетика воспроизводит обе раскладки строка в строку | Низкая |
| 2026-11-07 | Тип сегмента для разреза — по первым `min_codes` кодам (все одной формы), а не по большинству всех кодов сегмента (исполнение, Task 2) | Строки ОФР без своей шапки прилипают к сегменту пассива ББ с повторной шапкой; по большинству такой сегмент — «ОФР», и разрез по шапке ББ унёс бы пассив в ОФР | Низкая: сегмент, который открывается 1–2 кодами своей формы и дальше идёт чужой, не режется — как до изменения |
| 2026-11-07 | Для разреза — помощник `segments.split_segments` (первая строка, своя шапка, коды сегмента) вместо прямого импорта приватных функций модуля | `split.py` не зависит от приватных имён | Нулевая |
| 2026-11-07 | Редкий столбец отсеивается, только если в строке его числа у всех колонок периода есть числа (финальное ревью, I-1) | Иначе правило молча выбрасывает значение, которое OCR сдвинул из пустой ячейки периода, и документ становится `done` (пассив АК-ЮЛ: итог 1700 = 46922 в столбце «На 2»); сужение совпадает со смыслом решения владельца «постороннее число» | Низкая: обрывок с посторонним числом в строке с пустым периодом остаётся `COLUMN_WITHOUT_DATE` — как до изменения |
| 2026-11-07 | Части `k ≥ 1` получают из предупреждений адаптера `XLSB_NO_FORMATS` (финальное ревью, I-2) | Без него шаг 4 проверяет масштаб xlsb как у файла с форматами: `FRACTIONAL_IN_THOUSANDS` вместо `XLSB_SCALE_SUSPECT` | Нулевая |
## Risks / Trade-offs
- Пассив ББ АК-ЮЛ (прогон 07.11): столбец «На 2» с одним числом 46922 — сдвинутый OCR итог 1700 за 31.12.2024 (ячейка
столбца периода в этой строке пуста), поэтому после уточнения I-1 правило не срабатывает: поведение прежнее
(`SEGMENT_COLUMNS_MISMATCH`, `needs_check`).
- АК-ЮЛ после разреза: ОФР — отдельный документ, но подписи его колонок 5 («За I п / 20:») и 6 («юлугодие / 25 года»)
ИС тоже разорвала, и ни одна не разбирается как период — столбец 5 не «между периодами», правило редкого столбца не
срабатывает: обе колонки с `COLUMN_WITHOUT_DATE`, документ `needs_check`, аналитик ставит дату. Это честный результат
на испорченной шапке; ББ после разреза теряет 2xxx-строки и жёсткие нарушения, вызванные ими.
- Саракташагротехремонт — JSON в задаче нет (только PDF); ожидаем ту же раскладку, проверим после выкладки.
- Хостам нужен один вызов `split_by_form`; без него поведение прежнее — подъём пина без правки хоста ничего не ломает.