## 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` части — `;form:` (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` не зависит от приватных имён | Нулевая | ## Risks / Trade-offs - Правило редкого столбца срабатывает и на ББ АК-ЮЛ (прогон 07.11): в сегменте пассива столбец 5 с обрывком «На 2» и одним числом 46922 (итог 1700 за 31.12.2024, OCR сдвинул его из столбца 6) уходит в `NUMBERS_OUTSIDE_TABLE` (`row:40`). До изменения этот сегмент сопоставлялся по порядку (`SEGMENT_COLUMNS_MISMATCH`, значения столбца 5 уезжали в колонку 6, настоящие значения столбца 6 терялись); теперь столбец 6 сопоставлен по дате, потеряно одно число, адрес виден аналитику, жёсткая проверка `1600 = 1700` ловит пустой 1700. - АК-ЮЛ после разреза: ОФР — отдельный документ, но подписи его колонок 5 («За I п / 20:») и 6 («юлугодие / 25 года») ИС тоже разорвала, и ни одна не разбирается как период — столбец 5 не «между периодами», правило редкого столбца не срабатывает: обе колонки с `COLUMN_WITHOUT_DATE`, документ `needs_check`, аналитик ставит дату. Это честный результат на испорченной шапке; ББ после разреза теряет 2xxx-строки и жёсткие нарушения, вызванные ими. - Саракташагротехремонт — JSON в задаче нет (только PDF); ожидаем ту же раскладку, проверим после выкладки. - Хостам нужен один вызов `split_by_form`; без него поведение прежнее — подъём пина без правки хоста ничего не ломает.