AURORA-1734, AURORA-1735: разрез по смене формы и редкий столбец (§11, спека шагов 1–2) #214
@ -1689,6 +1689,16 @@ file:<имя json>;table:<i>`; документа у такой таблицы
|
||||
число колонок — `SEGMENT_COLUMNS_MISMATCH` (`error`): значения по позиции, лишние столбцы сегмента в колонки не
|
||||
попадают, их номера — в тексте предупреждения (S-56). Столбец кодов не найден ни так, ни так —
|
||||
`SEGMENT_WITHOUT_CODES` (`error`); строки сегмента не теряются (роль `data`, статусы по §11.4).
|
||||
**Смена формы внутри кандидата** (AURORA-1734, решение владельца 06.11.2026): ИС может вернуть ББ и ОФР одной
|
||||
таблицей («Баланс АК-ЮЛ.pdf»: шапка ОФР «Коды | За I полугодие 2026 года | …» на строке 41 таблицы «Бухгалтерский
|
||||
баланс»). До шагов 2–8 кандидат режется на части: сегмент **со своей шапкой**, первые `min_codes` (3) кодов которого —
|
||||
одной формы и другой, чем у текущей части (тип части — по большинству кодов её первого сегмента с кодами), начинает
|
||||
новую часть. Часть — обычный кандидат со своими колонками и периодами; первая сохраняет `locator`, `type_hint`,
|
||||
`evidence` и предупреждения адаптера, следующие — `locator` `<исходный>;form:<k>`, без `type_hint` (тип только по
|
||||
кодам, без `TYPE_CONFLICT`). Сегменты без шапки, с меньшим числом кодов или той же формы (повторная шапка второй
|
||||
страницы ББ, раздел 25xx после ОФР) не режут; строки другой формы, приклеенные к сегменту без своей шапки, тоже.
|
||||
Разрез — функция пакета `split_by_form`, хост вызывает её перед разбором каждой таблицы или листа; документы частей
|
||||
различаются `file_ordinal` (Р-10), модель и контракт не меняются.
|
||||
3. **Дата колонки — один словарь периодов** `statement_periods.yaml`, объединяющий прежний словарь парсера и шаблоны
|
||||
`dates.py` шлюза (порядок приоритета текстовых форм: диапазон «с 01.01.2025 по 30.09.2025» — конец; «30 сентября 2025
|
||||
г./r./года»; «I квартал 2026» / «1-й кв.» / «первый квартал»; «1 полугодие» / «1 п/г»; «9 месяцев 2025»; «январь –
|
||||
@ -1754,6 +1764,13 @@ file:<имя json>;table:<i>`; документа у такой таблицы
|
||||
меняется: подписанных колонок нет вовсе; столбец без подписи **между** подписанными — `COLUMN_WITHOUT_DATE`; столбец
|
||||
правее с **любым текстом подписи**, не разобранным ни как период, ни как исключение (OCR-мусор «| | г», «Проверка»), — `COLUMN_WITHOUT_DATE`, как раньше. Правило общее для Excel и
|
||||
OCR и живёт в пакете `arm_statements` (шаг 2), не в адаптере хоста.
|
||||
**Редкий столбец с неразобранной подписью** (AURORA-1735, решение владельца 06.11.2026; «Баланск Калтан.pdf»: подпись
|
||||
«За I полугодие 2025 года» разорвана на «31» и «а I полугодие 2025 года», под «31» — постороннее «$301» в строке 2400):
|
||||
столбец с текстом подписи, не разобранным ни как период, ни как исключение, стоящий **между** колонками периодов
|
||||
(есть левее и правее), с числом не более чем в одной строке с кодом при трёх и более у каждой колонки периода
|
||||
сегмента, колонкой сумм не считается — его числа идут в то же `NUMBERS_OUTSIDE_TABLE` (`info`) с текстом «числа вне
|
||||
колонок формы не учитываются: …» (если среди адресов есть такой столбец; иначе прежний текст «правее таблицы»).
|
||||
Столбец с числами в двух и более строках с кодом остаётся колонкой с `COLUMN_WITHOUT_DATE`.
|
||||
7. **Столбец наименования** — подпись, начинающаяся с «наименование», в строке заголовка; иначе столбец левее кода с
|
||||
наибольшим числом текстов, кроме «Пояснения» (пустые или ≤ 3 символов); наименование строки — ячейка этого столбца,
|
||||
пуста — ближайшая текстовая ячейка левее кода, кроме «Пояснения».
|
||||
@ -2018,7 +2035,7 @@ III (aep_3). Алгоритм: четырёхзначные коды строк
|
||||
| `VALUE_OUT_OF_RANGE` | info | число Excel длиннее 16 целых цифр | — |
|
||||
| `SHEET_OTHER` | info, файл | лист Excel «прочее» без типа и кодов (`locator = sheet:<имя>`; документа нет) | — |
|
||||
| `ROW_SPLIT` | info | разлеплена склеенная строка | — |
|
||||
| `NUMBERS_OUTSIDE_TABLE` | info | числа правее последней подписанной колонки значений не учтены (шаг 2, п. 6); адреса ячеек — в тексте | — |
|
||||
| `NUMBERS_OUTSIDE_TABLE` | info | числа правее последней подписанной колонки значений или в редком столбце с неразобранной подписью между колонками периодов не учтены (шаг 2, п. 6); адреса ячеек — в тексте | — |
|
||||
| `OSV_*` | info | предупреждения листа ОСВ (§1) | — |
|
||||
| `CODE_NOT_IN_DICTIONARY`, `CODE_NOT_IN_EDITION`, `NAME_MISMATCH`, `STATEMENTS_VERSION_MISMATCH` | info, ядро | проверки ядра (§9.3, §9.5) | пересчёт |
|
||||
| `RESULT_STATUS_INCONSISTENT` | info, ядро | `document.status = done` из результата противоречит строкам, `needs_review`, нарушениям или предупреждениям документа — ядро считает его `needs_check` (§9.3) | пересчёт после правки |
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
каждый кандидат с изоляцией ошибок. Менять `CandidateResult` на список ломает API у трёх потребителей; разрез на
|
||||
кандидаты сохраняет шаги 2–8 нетронутыми — каждая часть проходит их как обычный кандидат (свои колонки, периоды,
|
||||
§7). Сегменты для разреза ищутся тем же кодом шага 2 (`_split_and_analyse`), что и для раскладки.
|
||||
- **Условие разреза:** своя шапка + ≥ `min_codes` (3) кодов + другой `document_type` по большинству кодов. Шапка нужна,
|
||||
- **Условие разреза:** своя шапка + первые `min_codes` (3) кодов сегмента — одной формы, отличной от формы текущей части. Шапка нужна,
|
||||
чтобы не резать по случайным строкам; порог — чтобы пара строк другой формы (OCR-мусор, «пояснения» в хвосте) не
|
||||
создавала документ. Тип части — по первому сегменту части с определённым типом.
|
||||
- **Часть `k ≥ 1`:** `type_hint = null`, пустые `evidence` и `warnings` — метка таблицы ИС относится к первой форме, а
|
||||
@ -33,9 +33,17 @@
|
||||
| 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`, аналитик ставит дату. Это честный результат
|
||||
|
||||
@ -3,10 +3,12 @@
|
||||
### Requirement: Шаг 2 — разрез кандидата по смене формы
|
||||
Перед шагами 2–8 кандидат SHALL разрезаться на части по смене формы (AURORA-1734, решение владельца 06.11.2026).
|
||||
Сегменты кандидата ищутся по правилу «Шаг 2 — сегменты, повторная шапка и границы листов». Тип формы сегмента — по
|
||||
большинству его кодов (отрезок → `document_type`, как на шаге 6). Новая часть SHALL начинаться с первой строки сегмента,
|
||||
если одновременно: у сегмента своя строка-шапка; кодов в нём не меньше порога `min_codes` словаря диапазонов; тип по
|
||||
его кодам определён и отличается от типа текущей части (тип части — по первому сегменту части с определённым типом).
|
||||
Сегменты без шапки, с малым числом кодов или того же типа остаются в текущей части.
|
||||
первым `min_codes` кодам (порог словаря диапазонов): все они одной формы (отрезок → `document_type`, как на шаге 6) —
|
||||
это тип, которым сегмент открывается; иначе тип не определён. Новая часть SHALL начинаться с первой строки сегмента,
|
||||
если у сегмента своя строка-шапка и он открывается формой, отличной от типа текущей части (тип части — по большинству
|
||||
кодов её первого сегмента с кодами; после разреза — тип, которым открылась часть). Сегменты без шапки, с числом кодов
|
||||
меньше порога или той же формы остаются в текущей части; строки другой формы, приклеенные к сегменту без своей шапки
|
||||
(сегмент открывается кодами текущей формы), разреза не дают.
|
||||
|
||||
Часть — обычный `FormCandidate` для шагов 2–8: строки сетки с первой строки сегмента до начала следующей части,
|
||||
`segment_breaks` — сдвинутые на начало части; `header` — тот же, что у исходного кандидата (шапка файла ИС одна на
|
||||
@ -31,6 +33,10 @@
|
||||
- **WHEN** в таблице ОФР повторная шапка открывает сегмент со строками 2510…2910
|
||||
- **THEN** разреза нет: тип сегмента тот же (`INCOME`), сегмент — продолжение ОФР
|
||||
|
||||
#### Scenario: ОФР без своей шапки после пассива
|
||||
- **WHEN** после повторной шапки ББ идут строки 1310, 1300, 1700, а следом без шапки — 2110, 2120, 2100, 2400
|
||||
- **THEN** разреза нет: сегмент открывается кодами ББ (как и до изменения)
|
||||
|
||||
#### Scenario: Пара строк другой формы
|
||||
- **WHEN** после строк ББ повторная шапка открывает сегмент с двумя кодами 2xxx (меньше порога `min_codes`)
|
||||
- **THEN** разреза нет, сегмент остаётся в части ББ (как раньше)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user