docs(#116): внесены V-1…V-13 и G-1…G-30, решения Р-12…Р-15 (структура кода только при уверенности < 100, блокировка подтверждения, раскладка по коду, второй документ того же периода)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
ba0bdeb744
commit
e6400bf52f
@ -1,8 +1,9 @@
|
|||||||
# Единый алгоритм нормализации ББ/ОФР (scan-gateway и tabular-parser) — дизайн
|
# Единый алгоритм нормализации ББ/ОФР (scan-gateway и tabular-parser) — дизайн
|
||||||
|
|
||||||
Issue: arm-ka-docs#116. Статус: целевая картина по шагам 1–9 согласована с владельцем (решения Р-1…Р-11, 17.10.2026),
|
Issue: arm-ka-docs#116. Статус: целевая картина по шагам 1–9 согласована с владельцем (решения Р-1…Р-15, 17.10.2026);
|
||||||
находки независимого ревью F-1…F-25 внесены (`review-2026-10-17.md`); следующий шаг — спека (дельты OpenSpec,
|
внесены находки двух раундов ревью: F-1…F-25 (`review-2026-10-17.md`), V-1…V-13 (`review-2026-10-17-closure.md`),
|
||||||
контракт v2.0, §11/§12 в `05-domain-rules.md`, DBML v5.19).
|
G-1…G-30 (`review-2026-10-17-blind.md`); следующий шаг — спека (дельты OpenSpec, контракт v2.0, §11/§12 в
|
||||||
|
`05-domain-rules.md`, DBML v5.19).
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
|
|
||||||
@ -318,31 +319,37 @@ sheet:<A>,<B>`. `DISCLOSURE` и форма без колонки кодов —
|
|||||||
|
|
||||||
Каждый шаг ниже: **вход = выход предыдущего шага**, единый выход для обоих сервисов, список «различие → к чему
|
Каждый шаг ниже: **вход = выход предыдущего шага**, единый выход для обоих сервисов, список «различие → к чему
|
||||||
приводим → что меняется в scan-gateway → что меняется в tabular-parser». Решения владельца помечены датой; сводка
|
приводим → что меняется в scan-gateway → что меняется в tabular-parser». Решения владельца помечены датой; сводка
|
||||||
решений Р-1…Р-11 — в конце (все приняты 17.10.2026). Находки независимого ревью F-1…F-25
|
решений Р-1…Р-15 — в конце (все приняты 17.10.2026). Находки ревью внесены в текст с маркерами: первый раунд F-1…F-25
|
||||||
(`review-2026-10-17.md`) внесены в текст с маркерами. Имена полей — рабочие, уточняются при написании спеки.
|
(`review-2026-10-17.md`), второй раунд V-1…V-13 (`review-2026-10-17-closure.md`) и G-1…G-30
|
||||||
|
(`review-2026-10-17-blind.md`). Имена полей — рабочие, уточняются при написании спеки.
|
||||||
|
|
||||||
Принципы, из которых выведены решения по строкам:
|
Принципы, из которых выведены решения по строкам:
|
||||||
|
|
||||||
1. **Один выход на шаг.** Код, зависящий от источника, живёт только в шаге 1 (адаптеры «json ИС → кандидат», «лист
|
1. **Один выход на шаг.** Код, зависящий от источника, живёт только в шаге 1 (адаптеры «json ИС → кандидат», «лист
|
||||||
Excel → кандидат»). Шаги 2–8 — общий код с общими словарями (форма общего кода — Р-1).
|
Excel → кандидат»). Шаги 2–8 — общий код с общими словарями (форма общего кода — Р-1).
|
||||||
2. **Ничего не выбрасывать молча.** Строка, колонка или ячейка, которую не удалось истолковать, идёт дальше со
|
2. **Ничего не выбрасывать молча.** Строка, колонка или ячейка, которую не удалось истолковать, идёт дальше со
|
||||||
статусом, а не превращается в счётчик; пользователь её видит и исправляет. Положение в источнике (`ref`) и порядок
|
статусом, а не превращается в счётчик; пользователь её видит и исправляет. Положение в источнике (`ref`), порядок
|
||||||
строк (`row_index`) доезжают до базы и экрана (F-4).
|
строк (`row_index`) и сырой текст ячейки (`text`) доезжают до базы и экрана (F-4, G-3). Подтвердить документ, не
|
||||||
|
разобрав строки с суммами без кода, нельзя (Р-13).
|
||||||
3. **Качество — структурно и с привязкой к месту:** уверенность ячейки (от источника), `is_recognized` (разбор числа),
|
3. **Качество — структурно и с привязкой к месту:** уверенность ячейки (от источника), `is_recognized` (разбор числа),
|
||||||
статус строки, нарушения §7 по ячейкам, предупреждения с уровнем и привязкой. Уверенность, `is_recognized` и §7
|
статус строки, нарушения §7 по ячейкам, предупреждения с уровнем и привязкой. Уверенность, `is_recognized` и §7
|
||||||
сервис сводит в один признак ячейки `needs_review` (шаг 8) и из него, статусов строк и предупреждений выводит
|
сервис сводит в один признак ячейки `needs_review` (шаг 8) и из него, статусов строк и предупреждений выводит
|
||||||
статус документа. Ядро при загрузке ничего не пересчитывает, хранит `needs_review`, нарушения и предупреждения как
|
статус документа. Ядро при загрузке не пересчитывает ни проверки, ни статус, ни `needs_review`: хранит их как
|
||||||
пришли; после правок аналитика считает тем же общим кодом, добавляя своё знание: правка снимает `needs_review`,
|
пришли и добавляет только предупреждения `info` по своему словарю строк (G-26). После правок аналитика ядро считает
|
||||||
акцепт снимает жёсткие нарушения, квартал периода (F-8, F-18).
|
тем же общим кодом, добавляя своё знание: правка или подтверждение ячейки снимает `needs_review`, акцепт снимает
|
||||||
4. **Один словарь — одно место:** проверки §7, периоды, заголовки форм, упорядоченные перечни кодов форм по редакциям
|
жёсткие нарушения, квартал периода и статус конверта остаются условиями `RECOGNIZED` (F-8, F-18, G-7, G-12).
|
||||||
(без наименований) — в общем коде сервисов; соответствие «код → наименование» (словарь строк с редакциями) — только
|
4. **Один словарь — одно место:** проверки §7 (полный и упрощённый наборы), периоды, заголовки форм, упорядоченные
|
||||||
в ядре, сервисы его не знают и отдают распознанный текст (решение владельца 17.10.2026, Р-3).
|
перечни кодов форм по редакциям (без наименований) — в общем коде сервисов; соответствие «код → наименование» —
|
||||||
|
только в ядре, сервисы его не знают и отдают распознанный текст (Р-3). Порядок и состав редакций ядро берёт из
|
||||||
|
своей копии общего кода, словарь ядра хранит только имена; `sync-statements --check` сверяет, что все коды словаря
|
||||||
|
ядра есть в перечне (V-11).
|
||||||
5. **Очистка OCR (§11.1) — не отдельный шаг, а первая стадия типовых разборов текста** в общем коде, там, где уже
|
5. **Очистка OCR (§11.1) — не отдельный шаг, а первая стадия типовых разборов текста** в общем коде, там, где уже
|
||||||
известен смысл ячейки: ИНН и дата шапки (шаг 1), подписи колонок (шаг 2), код строки и разлепление склеенных
|
известен смысл ячейки: дата шапки (шаг 1), подписи колонок (шаг 2), код строки и разлепление склеенных строк
|
||||||
строк (шаг 3), сумма (шаг 4). Буквы-двойники («О» → 0, «З» → 3, «б» → 6, «S» → 5, «I», «l», «|» → 1) заменяются во
|
(шаг 3), сумма (шаг 4). Буквы-двойники («О» → 0, «З» → 3, «б» → 6, «S» → 5, «I», «l», «|» → 1) заменяются в датах,
|
||||||
всех четырёх разборах, включая суммы и коды — **решение владельца 17.10.2026 (Р-11)**, отменяет §11.1 п. 1 и решение
|
подписях, кодах и суммах — **решение владельца 17.10.2026 (Р-11)**, отменяет §11.1 п. 1 и решение № 22 среза 3a
|
||||||
№ 22 среза 3a («только даты»): сумма или код с заменой получает `is_recognized = false`, а дальше её судьбу решают
|
(«только даты»): сумма или код с заменой получает `is_recognized = false`, а дальше её судьбу решают контрольные
|
||||||
контрольные соотношения §7 (Р-7 с оговорками F-1, F-2). Разбор вызывается для любой ячейки без `value`; у Excel он
|
соотношения §7 (Р-7 с оговорками F-1, F-2). ИНН двойники не трогают: только цифры, как сейчас — иначе «3|1 21 2|»
|
||||||
|
из aep_3 даёт лишние единицы и `MISMATCH` с ЮЛ (G-18). Разбор вызывается для любой ячейки без `value`; у Excel он
|
||||||
срабатывает только на текстовых ячейках и почти всегда ничего не меняет. Из `normalizer/ocr/` шлюза `text`,
|
срабатывает только на текстовых ячейках и почти всегда ничего не меняет. Из `normalizer/ocr/` шлюза `text`,
|
||||||
`dates`, `amounts` становятся общими разборами, `layout`, `rows`, `header`, `document` — шагами 2–6 общего кода.
|
`dates`, `amounts` становятся общими разборами, `layout`, `rows`, `header`, `document` — шагами 2–6 общего кода.
|
||||||
|
|
||||||
@ -368,8 +375,8 @@ sheet:<A>,<B>`. `DISCLOSURE` и форма без колонки кодов —
|
|||||||
- `warnings` — предупреждения шага (формат — шаг 8).
|
- `warnings` — предупреждения шага (формат — шаг 8).
|
||||||
|
|
||||||
Ячейка `Cell` одна для шапки и таблицы: `text` (как показывает источник), `value` (число или дата, если источник их
|
Ячейка `Cell` одна для шапки и таблицы: `text` (как показывает источник), `value` (число или дата, если источник их
|
||||||
знает; у OCR — `null`), `confidence` 0–100 (у Excel — 100), `ref` (позиция в источнике для сообщений: `row_index` /
|
знает; у OCR — `null`), `confidence` 0–100 (у Excel — 100), `ref` — позиция в источнике, уникальная в документе
|
||||||
`column_index` у OCR, адрес A1 у Excel).
|
(G-9): OCR — `row:<row_index>`, при разлеплении строки (шаг 3 п. 2) — `row:<row_index>#<k>`; Excel — `<лист>!<A1>`.
|
||||||
|
|
||||||
- **1. Кто размечает** — остаётся, фиксируется полем `source`
|
- **1. Кто размечает** — остаётся, фиксируется полем `source`
|
||||||
- scan-gateway: без изменений
|
- scan-gateway: без изменений
|
||||||
@ -377,7 +384,7 @@ sheet:<A>,<B>`. `DISCLOSURE` и форма без колонки кодов —
|
|||||||
- **2. Единица разметки** — файл → список кандидатов; шаги 2–8 работают только с кандидатом (Р-1 — форма общего кода)
|
- **2. Единица разметки** — файл → список кандидатов; шаги 2–8 работают только с кандидатом (Р-1 — форма общего кода)
|
||||||
- scan-gateway: адаптер «таблица json → кандидат»
|
- scan-gateway: адаптер «таблица json → кандидат»
|
||||||
- tabular-parser: адаптер «блок формы на листе → кандидат»
|
- tabular-parser: адаптер «блок формы на листе → кандидат»
|
||||||
- **3. Шапка** — у каждого кандидата своя; поля = поля `header` контракта, каждое — `Cell`; разбор текста поля (ИНН → цифры, дата → `date`, единицы → словарь) — общий код по `text`
|
- **3. Шапка** — у каждого кандидата своя; поля = поля `header` контракта, каждое — `Cell`; разбор текста поля — общий код по `text`: ИНН → только цифры, без двойников (G-18); дата → словарь периодов (шаг 2 п. 3), который включает все шаблоны сегодняшнего `dates.py` шлюза (G-4); единицы → словарь
|
||||||
- scan-gateway: шапку json копирует в каждую свою таблицу
|
- scan-gateway: шапку json копирует в каждую свою таблицу
|
||||||
- tabular-parser: добавляет сырой текст и уверенность 100
|
- tabular-parser: добавляет сырой текст и уверенность 100
|
||||||
- **4. Тип формы** — одно правило: ОКУД → заголовок по одному словарю → `null`; окончательно — по кодам строк в шаге 6, у обоих
|
- **4. Тип формы** — одно правило: ОКУД → заголовок по одному словарю → `null`; окончательно — по кодам строк в шаге 6, у обоих
|
||||||
@ -386,9 +393,9 @@ sheet:<A>,<B>`. `DISCLOSURE` и форма без колонки кодов —
|
|||||||
- **5. Уверенность** — поле `Cell.confidence`; Excel всегда 100
|
- **5. Уверенность** — поле `Cell.confidence`; Excel всегда 100
|
||||||
- scan-gateway: без изменений
|
- scan-gateway: без изменений
|
||||||
- tabular-parser: добавляет поле
|
- tabular-parser: добавляет поле
|
||||||
- **6. Многостраничная форма** — одна форма = одна сетка на выходе шага 1; строки продолжения дописываются в конец сетки по маркеру «Форма … с.2» или по позиции листа, граница листа запоминается в `segment_breaks`; тип из маркера сверяется с `type_hint`, расхождение — отдельный кандидат + предупреждение. Раскладка у каждого сегмента своя (шаг 2 п. 2), поэтому разная ширина колонок или объединённые ячейки на листе-продолжении без шапки не сдвигают значения (F-16); число колонок сегментов сверяется там же
|
- **6. Многостраничная форма** — одна форма = одна сетка на выходе шага 1; строки продолжения дописываются в конец сетки по маркеру «Форма … с.2» или по позиции листа, граница листа запоминается в `segment_breaks`; тип из маркера сверяется с `type_hint`, расхождение — отдельный кандидат + предупреждение `error`. Лист без маркера и без шапки с одним-двумя кодами присоединяется только если его коды в диапазоне `type_hint` открытой формы (как `_weak_blocker` сейчас, G-8); иначе — отдельный кандидат. Раскладка у каждого сегмента своя (шаг 2 п. 2), поэтому разная ширина колонок или объединённые ячейки на листе-продолжении без шапки не сдвигают значения (F-16); число колонок сегментов сверяется там же
|
||||||
- scan-gateway: без изменений
|
- scan-gateway: без изменений
|
||||||
- tabular-parser: склейка переезжает до раскладки; `join_blocker` остаётся только по типу; проверка числа колонок переезжает в шаг 2 п. 2
|
- tabular-parser: склейка переезжает до раскладки; `join_blocker` остаётся по типу и по диапазону кодов слабого продолжения; проверка числа колонок переезжает в шаг 2 п. 2
|
||||||
- **7. Ячейка** — один формат `Cell`; Excel: `text` — как показывает числовой формат, `value` — число по формату (tp#42; формат не разобран — сырое значение + предупреждение); OCR: `text` сырой, `value` `null`, `confidence` от ИС
|
- **7. Ячейка** — один формат `Cell`; Excel: `text` — как показывает числовой формат, `value` — число по формату (tp#42; формат не разобран — сырое значение + предупреждение); OCR: `text` сырой, `value` `null`, `confidence` от ИС
|
||||||
- scan-gateway: обёртка над `value` + `confidence` ИС
|
- scan-gateway: обёртка над `value` + `confidence` ИС
|
||||||
- tabular-parser: отображаемый текст и значение считаются здесь, а не в шаге 4
|
- tabular-parser: отображаемый текст и значение считаются здесь, а не в шаге 4
|
||||||
@ -399,26 +406,30 @@ sheet:<A>,<B>`. `DISCLOSURE` и форма без колонки кодов —
|
|||||||
|
|
||||||
**Единый выход:** `TableLayout` — сегменты сетки (участки между повторными шапками и границами листов), у каждого:
|
**Единый выход:** `TableLayout` — сегменты сетки (участки между повторными шапками и границами листов), у каждого:
|
||||||
столбец кода, столбец наименования, колонки значений `[{col, label, period_end, period_kind}]`, сопоставленные с
|
столбец кода, столбец наименования, колонки значений `[{col, label, period_end, period_kind}]`, сопоставленные с
|
||||||
первым сегментом по порядку; роль каждой строки сетки (`header` | `numbering` | `data`).
|
первым сегментом по порядку; роль каждой строки сетки (`title` — выше строки заголовка сегмента, а без заголовка —
|
||||||
|
выше первой строки с кодом, V-8 | `header` | `numbering` | `data`).
|
||||||
|
|
||||||
- **1. Столбец кодов** — одно правило (уточнено по вопросу владельца 17.10.2026; F-5, F-21, F-24): код — текст без пробелов и переносов, 4–5 цифр (допускается `.0`), первые четыре в диапазонах словаря кодов всех форм, включая 5xxx пояснений (`type_hint` неизвестен — объединение диапазонов). (а) Столбец, над данными которого стоит подпись «Код» / «Код строки» / «Коды» **и под которой есть хотя бы один код из диапазона**; берётся последняя такая подпись над первой строкой с кодом. Защита обязательна: в выгрузках 1С первая подпись «Коды» на листе — титульный блок реквизитов, под ней ОКУД, дата, ОКПО, ИНН, ОКЕИ, а не коды строк (F-5). Порога по числу кодов нет — документ из двух строк остаётся документом. (б) Подписи нет → столбец с наибольшим числом ячеек-кодов, кодов ≥ 3, доля среди непустых ≥ порога (так парсер сейчас); нет — «колонка кодов не найдена». Статусы строк с нечитаемым или пустым кодом — новое поведение для обоих: шлюз сегодня такие строки пропускает со счётчиком, а кодом считает любое целое (F-24)
|
- **1. Столбец кодов** — одно правило (уточнено по вопросу владельца 17.10.2026; F-5, F-21, F-24, V-1, G-5, G-19): код — текст без пробелов, переносов и краевой пунктуации («1220 ,» → 1220, G-22), после замены двойников (порядок: убрать пробелы и переносы → двойники → проверка; `|` → 1 только если результат попадает в диапазон, G-18), 4–5 цифр (допускается `.0`), первые четыре в диапазонах словаря кодов: 1xxx — ББ, 2xxx — ОФР, 3xxx–6xxx — прочие формы и пояснения (`type_hint` неизвестен — объединение диапазонов). Многострочная ячейка — код, если каждая её часть код (V-9). (а) Столбец, над данными которого стоит подпись «Код» / «Коды» / «Код строки» (словарь подписей расширяется, G-28) **и под которой доля кодов среди непустых ячеек ≥ 0,6 или кодов ≥ 3**; несколько кандидатов — столбец с наибольшим числом кодов; берётся последняя такая подпись над первой строкой с кодом. Защита обязательна: в выгрузках 1С под подписью «Коды» титульного блока стоят ОКУД, дата, ОКПО, ИНН, ОКОПФ (12267 у АО, 12300 у ООО — по первым четырём цифрам это «коды баланса»), ОКЕИ: один код из девяти, доля 0,11 (V-1, G-5). Документ из двух строк остаётся документом: два из двух, доля 1,0. (б) Подписи нет → столбец с наибольшим числом ячеек-кодов, кодов ≥ 3, доля ≥ 0,6 (так парсер сейчас); нет — «колонка кодов не найдена». Статусы строк с нечитаемым или пустым кодом — новое поведение для обоих: шлюз сегодня такие строки пропускает со счётчиком, а кодом считает любое целое (F-24)
|
||||||
- scan-gateway: проход по подписи получает защиту «под подписью есть код»; в запасном подсчёте `int(text)` → правило словаря (закрывает AURORA-1517 вместе с п. 5)
|
- scan-gateway: проход по подписи получает защиту по доле; в запасном подсчёте `int(text)` → правило словаря (закрывает AURORA-1517 вместе с п. 5)
|
||||||
- tabular-parser: добавляет проход по подписи до подсчёта; диапазон формы → объединение; `relaxed` для одиноких продолжений не нужен (они склеены в шаге 1)
|
- tabular-parser: добавляет проход по подписи до подсчёта; диапазон формы → объединение
|
||||||
- **2. Повторная шапка и границы листов** — одно правило: новый сегмент открывает строка с признаком шапки («Код» / та же сигнатура) **или** граница листа из `segment_breaks` (F-16); у сегмента своя раскладка: по его шапке, а без шапки — столбец кодов по правилу п. 1 и колонки значений по числам в строках с кодами (как у продолжений парсера сейчас); колонки значений сегмента сопоставляются с первым сегментом по порядку; число не совпало — предупреждение `error`, значения по позиции
|
- **2. Повторная шапка и границы листов** — одно правило: новый сегмент открывает строка с признаком шапки («Код» / та же сигнатура) **или** граница листа из `segment_breaks` (F-16); у сегмента своя раскладка: по его шапке, а без шапки — столбец кодов по правилу п. 1, и если сегмент не первый и кодов в нём меньше трёх, столбец кодов наследуется от предыдущего сегмента при условии, что его коды в диапазоне формы (так `relaxed` + `_weak_blocker` парсера сейчас: лист «_2» НПЗ с единственной строкой 2400, V-4, G-8); колонки значений без шапки — по числам в строках с кодами; колонки значений сегмента сопоставляются с первым сегментом по порядку; число не совпало — предупреждение `error`, значения по позиции; столбец кодов не найден ни так, ни так — предупреждение `error` сегмента, строки сегмента не теряются (роль `data`, статусы по шагу 3)
|
||||||
- scan-gateway: как сейчас, через общее правило
|
- scan-gateway: как сейчас, через общее правило
|
||||||
- tabular-parser: бывший «лист-продолжение со своей раскладкой» становится сегментом; бывшая проверка числа колонок в `join_blocker` — предупреждение сегмента
|
- tabular-parser: бывший «лист-продолжение со своей раскладкой» становится сегментом; бывшая проверка числа колонок в `join_blocker` — предупреждение сегмента
|
||||||
- **3. Дата колонки** — один словарь периодов (`statement_periods.yaml`) → `period_end` и `period_kind`: абсолютные подписи («На 31 марта 2025 г.», «За январь – март 2025 г.», «31.12.2024», «За 9 месяцев 2025 г.», «2024») → `absolute`; относительные («На отчётную дату», «На 31 декабря предыдущего года», «На 31 декабря года, предшествующего предыдущему», «За отчётный период», «За аналогичный период прошлого года») → `relative:<вид>`, дата считается от **эффективной даты документа**: дата отчёта из шапки, иначе самая поздняя абсолютная дата колонок (F-6); нет ни той ни другой → `period_end = null`, `period_kind` остаётся относительным, и ядро досчитает дату после правки даты отчёта аналитиком (F-10). Перед разбором — очистка двойников OCR (общий шаг, у Excel не срабатывает). Относительные подписи — решение владельца 17.10.2026 (Р-6): это бланки по приказу 66н без впечатанных дат, у шлюза сейчас «колонка 0 без даты: На отчетную дату»
|
- **3. Дата колонки** — один словарь периодов (`statement_periods.yaml`), **объединяющий** сегодняшний yaml парсера и шаблоны `dates.py` шлюза (G-4): «На 31 марта 2025 г.», «За январь – март 2025 г.», «31.12.2024», «30/09/25», «30092025», «За 9 месяцев 2025 г.», «за I квартал 2025 г.», «1 полугодие 2026», «с 01.01.2025 по 31.03.2025», «2024», двузначный год, хвост «0:00:00», клетки «2|0|2|4|»; строки `test_dates.py::test_parses_real_forms` шлюза — обязательные тесты словаря. Результат — `period_end` и `period_kind`: абсолютные подписи → `absolute`; относительные («На отчётную дату», «На 31 декабря предыдущего года», «На 31 декабря года, предшествующего предыдущему», «За отчётный период», «За аналогичный период прошлого года») → `relative:<вид>`, дата считается от **эффективной даты документа**: дата отчёта из шапки, иначе самая поздняя абсолютная дата колонок (F-6); нет ни той ни другой → `period_end = null`, `period_kind` остаётся относительным, ядро досчитает после правки даты отчёта (F-10); подписи нет → `period_kind = none`. Перед разбором — очистка двойников OCR (общий шаг, у Excel не срабатывает). Относительные подписи — решение владельца 17.10.2026 (Р-6): это бланки по приказу 66н без впечатанных дат, у шлюза сейчас «колонка 0 без даты: На отчетную дату»
|
||||||
- scan-gateway: `parse_date` «размером с дату» → словарь
|
- scan-gateway: `parse_date` → словарь, его шаблоны переезжают в словарь
|
||||||
- tabular-parser: добавляет очистку двойников (no-op) и относительные подписи
|
- tabular-parser: добавляет очистку двойников (no-op), относительные подписи и шаблоны шлюза
|
||||||
- **4. Строка заголовка и её отсутствие** — одно правило (обе ветки парсера сохраняются, F-5): строка заголовка — последняя над первой строкой с кодом, где в столбце кодов стоит подпись «Код»; иначе последняя над первой строкой с кодом с двумя и более подписями-периодами правее кодов (или одной, не голым годом); двухстрочный заголовок склеивается. Не найдена → колонки значений = столбцы правее кода, где в строках с кодами стоят числа; `label` пустая, `period_end` `null`; одно предупреждение `error` «строка заголовка не найдена»
|
- **4. Строка заголовка и её отсутствие** — одно правило (обе ветки парсера сохраняются, F-5): строка заголовка — последняя над первой строкой с кодом, где в столбце кодов стоит подпись «Код»; иначе последняя над первой строкой с кодом с двумя и более подписями-периодами правее кодов (или одной, не голым годом); двухстрочный заголовок склеивается. Всё выше строки заголовка — роль `title`, в данные не попадает (титульный блок «Дата 30 | 9 | 2025» Межениновской, V-8). Не найдена → колонки значений = столбцы правее кода, где в строках с кодами стоят числа; `label` пустая, `period_end` `null`, `period_kind = none`; одно предупреждение `error` «строка заголовка не найдена»; роль `title` — всё выше первой строки с кодом
|
||||||
- scan-gateway: синтез шапки уходит (он эквивалентен); добавляет ветку «заголовок по периодам»
|
- scan-gateway: синтез шапки уходит (он эквивалентен); добавляет ветку «заголовок по периодам»
|
||||||
- tabular-parser: добавляет предупреждение
|
- tabular-parser: добавляет предупреждение
|
||||||
- **5. Строка нумерации «1 2 3 4 5»** — одно правило `is_numbering_row` → роль `numbering`, в данные не попадает
|
- **5. Строка нумерации «1 2 3 4 5»** — одно правило `is_numbering_row` → роль `numbering`, в данные не попадает
|
||||||
- scan-gateway: добавляет (AURORA-1517)
|
- scan-gateway: добавляет (AURORA-1517)
|
||||||
- tabular-parser: как сейчас
|
- tabular-parser: как сейчас
|
||||||
- **6. Подпись без периода** — **решение владельца (17.10.2026, Р-6):** подпись — период по словарю (абсолютный или относительный, п. 3) → колонка с датой; подпись из словаря исключений («Изменение», «Отклонение», «%», «Темп», «Доля», «Примечание») → пропуск с предупреждением `info`; иначе, если в столбце есть числа в строках с кодами (OCR-мусор вроде «| | г» с уверенностью 17 в aep_3, обрезанная или самодельная подпись) — колонка остаётся с подписью как есть, `period_end = null`, `period_kind = none`, предупреждение `error` с привязкой к колонке, документ `needs_check`, аналитик подтверждает после сверки с источником; иначе пропуск. Ядру колонка без даты не мешает: период формы берётся из эффективной даты (п. 3, `statement_period`). Пропуск числовой колонки с нераспознанной подписью отвергнут: для OCR это тихая потеря периода с возможным автоподтверждением
|
- **6. Подпись без периода** — **решение владельца (17.10.2026, Р-6):** подпись — период по словарю (абсолютный или относительный, п. 3) → колонка с датой; подпись из словаря исключений («Изменение», «Отклонение», «%», «Темп», «Доля», «Примечание») → пропуск с предупреждением `info`; иначе, если в столбце есть числа в строках с кодами (OCR-мусор вроде «| | г» с уверенностью 17 в aep_3, обрезанная или самодельная подпись) — колонка остаётся с подписью как есть, `period_end = null`, `period_kind = none`, предупреждение `error` с привязкой к колонке, документ `needs_check`; аналитик ставит дату колонке через PATCH (G-13) или подтверждает после сверки с источником; иначе пропуск. Ядру колонка без даты не мешает для периода формы (эффективная дата, п. 3), но межформенная проверка и гудвилл «на отчётную дату» по такой колонке не считаются, пока дата не проставлена (G-13). Пропуск числовой колонки с нераспознанной подписью отвергнут: для OCR это тихая потеря периода с возможным автоподтверждением
|
||||||
- scan-gateway: добавляет словарь исключений и относительные периоды
|
- scan-gateway: добавляет словарь исключений и относительные периоды
|
||||||
- tabular-parser: перестаёт молча пропускать неизвестные числовые колонки; добавляет относительные периоды
|
- tabular-parser: перестаёт молча пропускать неизвестные числовые колонки; добавляет относительные периоды
|
||||||
|
- **7. Столбец наименования** — одно правило (G-29): подпись «Наименование (показателя)» в строке заголовка → этот столбец; иначе столбец левее кодов с наибольшим числом текстов, кроме столбца «Пояснения»; наименование строки — ячейка этого столбца, пусто → ближайшая текстовая ячейка левее кода
|
||||||
|
- scan-gateway: «наименование», иначе слева от кода → общее правило
|
||||||
|
- tabular-parser: `_name_column` / `_line_name` → общее правило
|
||||||
|
|
||||||
### Шаг 3 — Отбор строк данных
|
### Шаг 3 — Отбор строк данных
|
||||||
|
|
||||||
@ -434,26 +445,27 @@ sheet:<A>,<B>`. `DISCLOSURE` и форма без колонки кодов —
|
|||||||
- `detail` — **решение владельца 17.10.2026 (Р-8):** строка без кода с числами, стоящая под строкой с кодом в том же
|
- `detail` — **решение владельца 17.10.2026 (Р-8):** строка без кода с числами, стоящая под строкой с кодом в том же
|
||||||
сегменте, — расшифровка («в том числе: …», «из них …», «Оборудование к установке», «Товар (41.01)»); родитель —
|
сегменте, — расшифровка («в том числе: …», «из них …», «Оборудование к установке», «Товар (41.01)»); родитель —
|
||||||
ближайшая строка с кодом выше. Сервисы её отдают, ядро хранит, в проверки §7, агрегаты и критерий `done` она не
|
ближайшая строка с кодом выше. Сервисы её отдают, ядро хранит, в проверки §7, агрегаты и критерий `done` она не
|
||||||
входит, экран по умолчанию не показывает (F-3). Отменяет OQ-36 / OQ-141 («строки без кода не отдаются»);
|
входит, экран по умолчанию не показывает (F-3). Отменяет OQ-36 / OQ-141 («строки без кода не отдаются»); OQ-149
|
||||||
- `missing` — строка без кода с числами, над которой в сегменте нет строки с кодом (кода не было или он потерян в
|
(«восстановление строк по наименованию») остаётся открытым;
|
||||||
самом начале таблицы).
|
- `missing` — строка без кода с числами между заголовком сегмента и первой строкой с кодом (кода не было или он
|
||||||
|
потерян в самом начале таблицы); строки роли `title` сюда не попадают (V-8).
|
||||||
|
|
||||||
Строка без кода и без чисел (заголовок раздела «АКТИВ», «в том числе:» без сумм) — не строка. Счётчиков отброшенных
|
Строка без кода и без чисел (заголовок раздела «АКТИВ», «в том числе:» без сумм) — не строка. Счётчиков отброшенных
|
||||||
нет — есть статусы. Повтор кода — не статус: по правилам отчётности один код может стоять у нескольких строк с
|
нет — есть статусы. Повтор кода — не статус: по правилам отчётности один код может стоять у нескольких строк с
|
||||||
разными наименованиями, все они `ok`. Строка формы с пустой ячейкой кода под другой строкой с кодом попадёт в `detail`
|
разными наименованиями, все они `ok`. Строка формы с пустой ячейкой кода под другой строкой с кодом попадёт в `detail`
|
||||||
и будет скрыта; её значения не войдут в сумму раздела, нарушение §7 подсветит родителя, аналитик раскроет детали и
|
и будет скрыта; её значения не войдут в сумму раздела, нарушение §7 подсветит родителя (цель соотношения отсутствует
|
||||||
назначит код (F-14) — осознанная цена Р-8.
|
при непустых членах — нарушение, G-2), аналитик раскроет расшифровки и назначит код (Р-13) — осознанная цена Р-8.
|
||||||
|
|
||||||
- **1. Код-мусор** — статус `unreadable`, строка сохраняется с сырым текстом кода (AURORA-1462: «ИЧ,250» видна, аналитик назначает 1250). Разбор кода: двойники (Р-11), пробелы и переносы убираются («1 100» в aep_3 — это 1100, F-21), затем правило 4–5 цифр
|
- **1. Код-мусор** — статус `unreadable`, строка сохраняется с сырым текстом кода (AURORA-1462: «ИЧ,250» видна, аналитик назначает 1250). Разбор кода — по правилу шага 2 п. 1: пробелы, переносы и краевая пунктуация, двойники, 4–5 цифр («1 100» в aep_3 — это 1100, F-21; «| 140» → 1140, G-18)
|
||||||
- scan-gateway: счётчик → статус
|
- scan-gateway: счётчик → статус
|
||||||
- tabular-parser: отличает «нет кода» от «не разобран»
|
- tabular-parser: отличает «нет кода» от «не разобран»
|
||||||
- **2. Склейка двух строк в одну** — одно правило: ячейка кода содержит N частей через перевод строки и ячейки значений — N частей → N строк (OQ-156), разлепление делается и тогда, когда часть кода нечитаема — она становится строкой `unreadable` (F-25); число частей не совпало — одна строка, многострочное значение → `amount = null`, `string_value` = текст, `is_recognized = false`; у Excel не срабатывает
|
- **2. Склейка двух строк в одну** — одно правило, выполняется **до** разбора кода (V-9): ячейка кода содержит N частей через перевод строки и ячейки значений — N частей → N строк (OQ-156) с `ref` `row:<i>#<k>`; разлепление делается, если хотя бы одна часть кода разобралась как код (G-21: «44 30\nI |» aep_6 не множится), нечитаемая часть становится строкой `unreadable` (F-25); число частей не совпало или ни одна часть не код — одна строка, многострочное значение → `amount = null`, `string_value` = текст, `is_recognized = false`; у Excel не срабатывает
|
||||||
- scan-gateway: как сейчас, через общее правило; разлепление при нечитаемой части — новое
|
- scan-gateway: как сейчас, через общее правило; разлепление при нечитаемой части — новое
|
||||||
- tabular-parser: правило общее, no-op
|
- tabular-parser: правило общее, no-op
|
||||||
- **3. Повтор кода** — **решение владельца (17.10.2026):** повтор кода — законный случай (один код, разные наименования), не ошибка и не предупреждение. Все строки сохраняются как есть со статусом `ok`; текстовое предупреждение и `checks.duplicate_line_codes` уходят. Для проверок §7, подтверждения и всего, что идёт после, значения одного кода идут суммой (см. «Что уходит из ядра»; договорённость ДБД в AURORA-1431; закрывает AURORA-1406). Неверно прочитанный код, случайно совпавший с существующим, ловит §7 через сумму
|
- **3. Повтор кода** — **решение владельца (17.10.2026):** повтор кода — законный случай (один код, разные наименования), не ошибка и не предупреждение. Все строки сохраняются как есть со статусом `ok`; текстовое предупреждение и `checks.duplicate_line_codes` уходят. Для проверок §7, подтверждения и всего, что идёт после, значения одного кода идут суммой (см. «Что уходит из ядра»; договорённость ДБД в AURORA-1431; закрывает AURORA-1406). Неверно прочитанный код, случайно совпавший с существующим, ловит §7 через сумму
|
||||||
- scan-gateway: «оставлена первая» → все строки
|
- scan-gateway: «оставлена первая» → все строки
|
||||||
- tabular-parser: `duplicates` уходит
|
- tabular-parser: `duplicates` уходит
|
||||||
- **4. Отброшенная строка** — строки не отбрасываются: `unreadable` / `missing` / `detail` идут дальше со статусом, `row_index` и `ref` (AURORA-1634, 1462); ядро не достраивает вместо них пустую шаблонную строку молча. Отмена OQ-36 / OQ-141 — Р-8
|
- **4. Отброшенная строка** — строки не отбрасываются: `unreadable` / `missing` / `detail` идут дальше со статусом, `row_index` и `ref` (AURORA-1634, 1462); ядро не достраивает вместо них пустую шаблонную строку молча; подтвердить документ, пока у `unreadable` / `missing` есть суммы, нельзя (Р-13). Отмена OQ-36 / OQ-141 — Р-8
|
||||||
- scan-gateway: счётчики → статусы
|
- scan-gateway: счётчики → статусы
|
||||||
- tabular-parser: счётчики → статусы; строки без кода с суммами начинают отдаваться
|
- tabular-parser: счётчики → статусы; строки без кода с суммами начинают отдаваться
|
||||||
|
|
||||||
@ -461,22 +473,23 @@ sheet:<A>,<B>`. `DISCLOSURE` и форма без колонки кодов —
|
|||||||
|
|
||||||
**Вход:** ячейки значений `RawLine` по колонкам раскладки (`Cell` или `null`).
|
**Вход:** ячейки значений `RawLine` по колонкам раскладки (`Cell` или `null`).
|
||||||
|
|
||||||
**Единый выход:** `StatementValue {column_index, amount, string_value, confidence, is_recognized}` — как в контракте,
|
**Единый выход:** `StatementValue {column_index, amount, string_value, text, confidence, is_recognized}`: `text` —
|
||||||
с одним смыслом полей у обоих; `needs_review` досчитывается в шаге 8.
|
сырой текст источника всегда, как `Cell.text` (G-3, V-3); `string_value` — как сейчас, только когда сумма не
|
||||||
|
разобрана (`amount = null`, непустой текст); `needs_review` досчитывается в шаге 8.
|
||||||
|
|
||||||
- **1. Предобработка текста** — у ячейки после шага 1 либо есть `value`, либо только `text`. `value` — число → `amount = value`, квантование до 2 знаков half-up, не больше 16 целых цифр (`numeric(18,2)`), иначе `amount = null`, `string_value` = текст, `is_recognized = false` и предупреждение (F-20); `value` — дата или логическое → `amount = null`, `string_value` = текст, `is_recognized = false` (F-20); `value` нет → один `parse_amount(text)` для обоих: двойники (Р-11), разделители тысяч, скобки → минус, причём `}` и `]` принимаются как закрывающая скобка (OQ-147, F-1), прочерк → `null`; «лучшая догадка» из цифр ограничена 16 цифрами, иначе `null` (F-20); Excel-ячейки «число текстом» идут тем же путём
|
- **1. Предобработка текста** — у ячейки после шага 1 либо есть `value`, либо только `text`. `value` — число → `amount = value`, квантование до 2 знаков half-up, не больше 16 целых цифр (`numeric(18,2)`), иначе `amount = null`, `string_value` = текст, `is_recognized = false` и предупреждение (F-20); `value` — дата или логическое → `amount = null`, `string_value` = текст, `is_recognized = false` (F-20); `value` нет → один `parse_amount(text, source)` для обоих: двойники (Р-11), пробелы и тонкие пробелы, минус «−» (U+2212) как знак, скобки → минус, причём `}` и `]` принимаются как закрывающая скобка (OQ-147, F-1), прочерк → `null`; запятая: у `excel` — десятичный разделитель («4,200» = 4,20, как `to_money` сейчас), у `ocr` — разделитель тысяч при группе из трёх цифр (AURORA-1405, sg#35) (G-16); «лучшая догадка» из цифр ограничена 16 цифрами, иначе `null` (F-20); Excel-ячейки «число текстом» идут тем же путём
|
||||||
- scan-gateway: `parse_amount` переезжает в общий код и получает квантование, предел длины, `}` / `]`
|
- scan-gateway: `parse_amount` переезжает в общий код и получает квантование, предел длины, `}` / `]`, «−», тонкие пробелы
|
||||||
- tabular-parser: `to_money` заменяется общим `parse_amount`
|
- tabular-parser: `to_money` заменяется общим `parse_amount` с `source = excel`
|
||||||
- **2. Смысл `is_recognized`** — один: текст разобран как число без выброшенных и исправленных символов (замена двойника — исправление); `confidence` — отдельно, от источника; оба признака сервис сводит в `needs_review` (шаг 8, «Подтверждение проверкой»), экран подсвечивает по нему
|
- **2. Смысл `is_recognized`** — один: текст разобран как число без выброшенных и исправленных символов (замена двойника — исправление; `}` / `]` вместо скобки — тоже); `confidence` — отдельно, от источника; оба признака сервис сводит в `needs_review` (шаг 8, «Подтверждение проверкой»), экран подсвечивает по нему
|
||||||
- scan-gateway: отвязывает от `confidence`
|
- scan-gateway: отвязывает от `confidence`
|
||||||
- tabular-parser: как сейчас
|
- tabular-parser: как сейчас
|
||||||
- **3. «42831 1»** — в `parse_amount` пробел допустим только как разделитель тысяч (группы ровно по три цифры); иначе `amount` — лучшая догадка (цифры подряд), `is_recognized = false`, `string_value` — сырой текст (AURORA-1511, 1519)
|
- **3. «42831 1»** — в `parse_amount` пробел допустим только как разделитель тысяч (группы ровно по три цифры); иначе `amount` — лучшая догадка (цифры подряд), `is_recognized = false`, сырой текст — в `text` (AURORA-1511, 1519)
|
||||||
- scan-gateway: меняется через общий код
|
- scan-gateway: меняется через общий код
|
||||||
- tabular-parser: то же правило, у Excel почти не срабатывает
|
- tabular-parser: то же правило, у Excel почти не срабатывает
|
||||||
- **4. Единицы** — одно правило: единицы «тыс. руб.» и дробных ≥ 10 % → предупреждение документа `info` (как у парсера сейчас, документ остаётся `done`)
|
- **4. Единицы** — одно правило: единицы «тыс. руб.» и дробных ≥ 10 % → предупреждение документа `info` (как у парсера сейчас, документ остаётся `done`)
|
||||||
- scan-gateway: добавляет
|
- scan-gateway: добавляет
|
||||||
- tabular-parser: как сейчас
|
- tabular-parser: как сейчас
|
||||||
- **5. Пустая ячейка** — **решение владельца (17.10.2026, Р-2):** одно правило: позиции нет, текст пуст или прочерк → `amount = null`, `string_value = null`, `is_recognized = true`, `confidence` ячейки (нет ячейки — 100), собственной подсветки нет. По `fixtures/ocr` ИС отдаёт ячейку на каждую позицию каждой строки: пустые — пустой текст с уверенностью 100, непрочитанные — с уверенностью 0, и такие идут в `needs_review` по общему правилу шага 8; позиция отсутствует только при несовпадении колонок сегмента, что покрыто предупреждением `error` шага 2 п. 2
|
- **5. Пустая ячейка** — **решение владельца (17.10.2026, Р-2):** одно правило: позиции нет, текст пуст или прочерк → `amount = null`, `string_value = null`, `is_recognized = true`, `confidence` ячейки (нет ячейки — 100), собственной подсветки нет. По `fixtures/ocr` ИС отдаёт ячейку на каждую позицию каждой строки: пустые — пустой текст с уверенностью 100, непрочитанные — с уверенностью 0, и такие идут в `needs_review` по общему правилу шага 8 (снимается подтверждением ячейки аналитиком, G-12); позиция отсутствует только при несовпадении колонок сегмента, что покрыто предупреждением `error` шага 2 п. 2
|
||||||
- scan-gateway: «ячейки нет → 0, не распознано» уходит
|
- scan-gateway: «ячейки нет → 0, не распознано» уходит
|
||||||
- tabular-parser: как сейчас
|
- tabular-parser: как сейчас
|
||||||
|
|
||||||
@ -492,13 +505,13 @@ line_name, line_name_confidence, parent_ref, values}`: `line_code` — разо
|
|||||||
- **1. Источник наименования** — **решение владельца (17.10.2026):** сервисы отдают распознанный текст наименования как есть, с уверенностью источника; соответствие «код → наименование» — только в ядре и только когда нужно (показ справочного имени, достройка шаблонных строк). Экран показывает справочное имя, файловое — рядом (Р-3; AURORA-1406, 1522)
|
- **1. Источник наименования** — **решение владельца (17.10.2026):** сервисы отдают распознанный текст наименования как есть, с уверенностью источника; соответствие «код → наименование» — только в ядре и только когда нужно (показ справочного имени, достройка шаблонных строк). Экран показывает справочное имя, файловое — рядом (Р-3; AURORA-1406, 1522)
|
||||||
- scan-gateway: имя из `statement_lines.yaml` → имя из OCR
|
- scan-gateway: имя из `statement_lines.yaml` → имя из OCR
|
||||||
- tabular-parser: как сейчас
|
- tabular-parser: как сейчас
|
||||||
- **2. Редакция (66н / ФСБУ 4)** — в сервисах редакция нужна только для упорядоченного перечня кодов (подтверждение кода структурой, шаг 8; F-9) и выбирается по эффективной дате документа (шаг 2 п. 3), без даты — проверяются обе редакции. Наименования, достройка шаблонных строк и проверка «код не из редакции формы» — ядро по его словарю строк
|
- **2. Редакция (66н / ФСБУ 4)** — в сервисах редакция нужна только для упорядоченного перечня кодов (подтверждение кода структурой, шаг 8; F-9) и выбирается по эффективной дате документа (шаг 2 п. 3), без даты — проверяются обе редакции. Наименования и достройка шаблонных строк — ядро по его словарю имён; проверка «код не из редакции формы» — ядро по своей копии перечня общего кода (V-11)
|
||||||
- scan-gateway: `_choose_edition` → выбор редакции перечня кодов в общем коде
|
- scan-gateway: `_choose_edition` → выбор редакции перечня кодов в общем коде
|
||||||
- tabular-parser: добавляет выбор редакции перечня кодов
|
- tabular-parser: добавляет выбор редакции перечня кодов
|
||||||
- **3. Справочник строк** — только в ядре; в общий код сервисов не входит; копия в шлюзе удаляется
|
- **3. Справочник строк** — только в ядре; в общий код сервисов не входит; копия в шлюзе удаляется
|
||||||
- scan-gateway: удаляет `api/rules/statement_lines.yaml`
|
- scan-gateway: удаляет `api/rules/statement_lines.yaml`
|
||||||
- tabular-parser: без изменений
|
- tabular-parser: без изменений
|
||||||
- **4. Уверенность кода** — от источника (ИС / 100), без срезания до 99 по справочнику; «код не в справочнике» — проверка ядра по его словарю, не сервисов
|
- **4. Уверенность кода** — от источника (ИС / 100), без срезания до 99 по справочнику; «код не в справочнике» — проверка ядра, предупреждение `info` к строке, не сервисов
|
||||||
- scan-gateway: убирает `min(conf, 99)`
|
- scan-gateway: убирает `min(conf, 99)`
|
||||||
- tabular-parser: как сейчас
|
- tabular-parser: как сейчас
|
||||||
|
|
||||||
@ -508,9 +521,9 @@ line_name, line_name_confidence, parent_ref, values}`: `line_code` — разо
|
|||||||
|
|
||||||
**Единый выход:** `StatementDocument {document_type, locator, header, columns, lines}` без `checks`.
|
**Единый выход:** `StatementDocument {document_type, locator, header, columns, lines}` без `checks`.
|
||||||
|
|
||||||
- **1. Тип формы** — одно правило: большинство кодов 1xxx → `BALANCE`, 2xxx → `INCOME`, 5xxx и прочие → `DISCLOSURE` (F-22); совпало с `type_hint` или `type_hint` `null` — тип по кодам; противоречит — тип по кодам + предупреждение `error`; кодов нет — `type_hint`; без колонки кодов: `type_hint` есть → документ без строк (`DISCLOSURE` — `done`, иначе `needs_check` с предупреждением `error`), `type_hint` нет → не документ, предупреждение `info`. Для `DISCLOSURE` шаги 2–7 не выполняются, `columns` / `lines` пустые (как сейчас; ядро строки пояснений не хранит)
|
- **1. Тип формы** — одно правило: большинство кодов 1xxx → `BALANCE`, 2xxx → `INCOME`, 3xxx–6xxx → `DISCLOSURE` (F-22, G-19); совпало с `type_hint` или `type_hint` `null` — тип по кодам; противоречит — тип по кодам + предупреждение `error`; кодов нет — `type_hint`; без колонки кодов: `type_hint` есть → документ без строк (`DISCLOSURE` — `done`, иначе `needs_check` с предупреждением `error`), `type_hint` нет → не документ, предупреждение `info`. `DISCLOSURE` по `type_hint` — шаги 2–7 не выполняются; `DISCLOSURE` по кодам — шаги 2–5 выполнены, строки и колонки отбрасываются (ядро строки пояснений не хранит), проверки не считаются (G-19). Упрощённая форма (ОКУД 0710096 в `evidence`, либо есть 1600 и нет ни 1100, ни 1200) помечается `form_variant = simplified` — шаг 7 и перечень кодов берут упрощённый набор (G-17)
|
||||||
- scan-gateway: как сейчас, через общее правило
|
- scan-gateway: как сейчас, через общее правило; добавляет упрощённый вариант
|
||||||
- tabular-parser: добавляет сверку с кодами
|
- tabular-parser: добавляет сверку с кодами и упрощённый вариант
|
||||||
- **2. Склейка страниц** — решена в шаге 1, различия нет
|
- **2. Склейка страниц** — решена в шаге 1, различия нет
|
||||||
- scan-gateway: без изменений
|
- scan-gateway: без изменений
|
||||||
- tabular-parser: без изменений
|
- tabular-parser: без изменений
|
||||||
@ -530,16 +543,21 @@ line_name, line_name_confidence, parent_ref, values}`: `line_code` — разо
|
|||||||
- значение кода в колонке — сумма по всем строкам `ok` с этим кодом (повтор кода — законный случай, шаг 3 п. 3); строки
|
- значение кода в колонке — сумма по всем строкам `ok` с этим кодом (повтор кода — законный случай, шаг 3 п. 3); строки
|
||||||
`detail`, `unreadable`, `missing` в соотношения не входят; `duplicate_line_codes` уходит; нарушение подсвечивает все
|
`detail`, `unreadable`, `missing` в соотношения не входят; `duplicate_line_codes` уходит; нарушение подсвечивает все
|
||||||
строки кода;
|
строки кода;
|
||||||
|
- **цель отсутствует** (нет строки `ok` с кодом цели) или `null`, а хотя бы один член непуст — **жёсткое нарушение**,
|
||||||
|
не пропуск (G-2: иначе «БАЛАНС» без кода, ушедший в `detail`, делает 1600 невидимым, и документ проходит в `done`);
|
||||||
|
сегодня оба сервиса и ядро такое соотношение пропускают — поведение меняется;
|
||||||
- «сошлось» / «не сошлось» (F-13): жёсткое соотношение не сошлось, если расхождение больше допуска ±1 **и** больше
|
- «сошлось» / «не сошлось» (F-13): жёсткое соотношение не сошлось, если расхождение больше допуска ±1 **и** больше
|
||||||
округления 0,001 % — только тогда оно в `relations_failed`; расхождение в пределах округления и мягкие соотношения —
|
округления 0,001 % — только тогда оно в `relations_failed`; расхождение в пределах округления и мягкие соотношения —
|
||||||
в `soft_relations_failed`, это `info`, на статус и подсветку не влияют (OQ-138 сохраняется);
|
в `soft_relations_failed`, это `info`, на статус и подсветку не влияют (OQ-138 сохраняется);
|
||||||
- соотношение с членами по модулю (`abs_terms`: 2120, 2210, 2220, 2330, 2350, 2410) сходится при любом знаке такого
|
- соотношение с членами по модулю (`abs_terms`: 2120, 2210, 2220, 2330, 2350, 2410) сходится при любом знаке такого
|
||||||
члена и потому **подтверждает только величину, не знак** (F-1) — см. «Подтверждение проверкой»;
|
члена и потому **подтверждает только величину, не знак** (F-1) — см. «Подтверждение проверкой»;
|
||||||
- соотношение считается **посчитанным** только если хотя бы один его член в колонке непуст; все члены `null` — не
|
- соотношение считается **посчитанным** только если хотя бы один его член в колонке непуст; все члены `null` — не
|
||||||
проверяется и ничего не подтверждает (F-2; то же правило, что для «родитель = Σ подстрок» по AURORA-1527).
|
проверяется и ничего не подтверждает (F-2; то же правило, что для «родитель = Σ подстрок» по AURORA-1527);
|
||||||
|
- набор соотношений выбирается по `form_variant`: полный или упрощённый (ОКУД 0710096: нет подытогов 1100 / 1200 /
|
||||||
|
1300, свои равенства; G-17), оба — в одном словаре `statement_checks.yaml`.
|
||||||
|
|
||||||
Проверки по словарю строк (код не в словаре, код не из редакции формы по дате отчёта) — в ядре, сервисы словаря строк
|
Проверки по словарю имён (код не в словаре) и по перечню кодов (код не из редакции формы по эффективной дате) — в
|
||||||
не знают (Р-3).
|
ядре, как предупреждения `info` к строке; сервисы словаря имён не знают (Р-3, Р-12).
|
||||||
|
|
||||||
- **1. Проверки только у парсера; словарь §7 в двух копиях; ядро результат не читает** — проверки — общий код с одним словарём `statement_checks.yaml` у обоих сервисов; для OCR нарушение §7 — главный признак неверно прочитанной цифры; ядро читает `checks` из результата, хранит их (`document_issue`, см. «Что уходит из ядра») и пересчитывает тем же кодом только после правок аналитика (PATCH)
|
- **1. Проверки только у парсера; словарь §7 в двух копиях; ядро результат не читает** — проверки — общий код с одним словарём `statement_checks.yaml` у обоих сервисов; для OCR нарушение §7 — главный признак неверно прочитанной цифры; ядро читает `checks` из результата, хранит их (`document_issue`, см. «Что уходит из ядра») и пересчитывает тем же кодом только после правок аналитика (PATCH)
|
||||||
- scan-gateway: добавляет шаг
|
- scan-gateway: добавляет шаг
|
||||||
@ -551,135 +569,176 @@ line_name, line_name_confidence, parent_ref, values}`: `line_code` — разо
|
|||||||
|
|
||||||
**Единый выход:** у каждого документа `status` (`done` | `needs_check`), `warnings[]`, `needs_review` у каждого
|
**Единый выход:** у каждого документа `status` (`done` | `needs_check`), `warnings[]`, `needs_review` у каждого
|
||||||
значения и у каждой строки; у файла `metrics.warnings[]`; статус конверта — агрегат. Предупреждение — структура
|
значения и у каждой строки; у файла `metrics.warnings[]`; статус конверта — агрегат. Предупреждение — структура
|
||||||
`{code, severity: error | info, message, line_ref?, column_index?}`, а не строка.
|
`{code, severity: error | info, message, line_refs?: [], column_index?}`, а не строка; `line_refs` — список, потому
|
||||||
|
что нарушение подсвечивает все строки кода (G-25).
|
||||||
|
|
||||||
- **1. Критерий `done`** — одна функция общего кода с входами «раскладка, шапка, статусы строк, `needs_review`, нарушения, предупреждения» (F-8): раскладка найдена ∧ (организация или ИНН) ∧ период определён (дата отчёта **или** самая поздняя абсолютная дата колонок — `statement_period` ядра; у ИС поле `date` в трёх из четырёх фикстур не дата, F-6) ∧ все строки `ok` или `detail` ∧ нет значений и строк `needs_review` (см. «Подтверждение проверкой» ниже) ∧ нет жёстких нарушений §7 вне допуска и округления (F-13) ∧ нет предупреждений уровня `error`. `DISCLOSURE` — `done` без условий о раскладке, строках и проверках (как сейчас). Excel выполняет условия уверенности автоматически. Квартал периода — знание ядра, в `done` не входит (F-18)
|
- **1. Критерий `done`** — одна функция общего кода с входами «раскладка, шапка, статусы строк, `needs_review`, нарушения, предупреждения» (F-8): раскладка найдена ∧ (организация или ИНН) ∧ период определён (дата отчёта **или** самая поздняя абсолютная дата колонок — `statement_period` ядра, F-6) ∧ все строки `ok` или `detail` ∧ нет значений и строк `needs_review` (см. «Подтверждение проверкой» ниже) ∧ нет жёстких нарушений §7 вне допуска и округления (F-13) ∧ нет предупреждений уровня `error`. `DISCLOSURE` — `done` без условий о раскладке, строках и проверках (как сейчас). Excel выполняет условия уверенности автоматически; структурная проверка кодов к Excel не применяется (Р-12). Квартал периода и статус конверта — знание ядра, в `done` не входят (F-18, G-7)
|
||||||
- scan-gateway: «ничего не выброшено и уверенность 100» → общий критерий
|
- scan-gateway: «ничего не выброшено и уверенность 100» → общий критерий
|
||||||
- tabular-parser: `_is_ok` → общий критерий; добавляются строки и значения
|
- tabular-parser: `_is_ok` → общий критерий; добавляются строки и значения
|
||||||
- **2. Влияние предупреждений** — по уровню: `error` → `needs_check`, `info` — не влияет; у обоих одинаково. Таблица уровней (F-12), полная версия — в спеке. `error`: ошибочный json набора при успешном соседе (уровень файла; решение № 28 среза 3a), `BACKEND_PARSING_ERROR` таблицы, таблица или лист с содержимым без колонки кодов (в том числе потерянный лист-продолжение), колонки сегмента не совпали, колонка без даты, строка заголовка не найдена, тип по кодам противоречит заголовку, ИНН не распознан, период не определён (нет ни даты отчёта, ни дат колонок), формат файла не совпал с `file_type`. `info`: подпись из словаря исключений пропущена, дробные при «тыс. руб.», дата отчёта не разобрана при наличии дат колонок, ИНН распознан неуверенно (ядро сверяет с ИНН ЮЛ), единицы не найдены (аналитик правит в шапке), xlsb без числовых форматов (tp#42, документ остаётся `done`), не документ без `type_hint` и кодов
|
- **2. Влияние предупреждений** — по уровню: `error` → `needs_check`, `info` — не влияет; у обоих одинаково. Таблица уровней (F-12, V-6, G-6, G-20), полная версия — в спеке. `error`: ошибочный json набора при успешном соседе (уровень файла; решение № 28 среза 3a), `BACKEND_PARSING_ERROR` таблицы, таблица или сегмент с содержимым без колонки кодов (в том числе потерянный лист-продолжение), колонки сегмента не совпали, колонка без даты, строка заголовка не найдена, тип по кодам противоречит заголовку, тип маркера продолжения противоречит форме, ни организации, ни ИНН в шапке, период не определён (нет ни даты отчёта, ни дат колонок). `info`: ИНН не найден или неуверен при найденной организации (сверку с ЮЛ делает ядро — `MISMATCH`; НПЗ ОФР без ИНН остаётся `done`), организация не найдена при найденном ИНН, дата отчёта не разобрана при наличии дат колонок, единицы не найдены (аналитик правит в шапке), формат файла не совпал с `file_type` (файл прочитан верно), подпись из словаря исключений пропущена, дробные при «тыс. руб.», xlsb без числовых форматов (tp#42, документ остаётся `done`), не документ без `type_hint` и кодов, код не в словаре имён или не из редакции (ядро). Принцип: `error` — только то, что меняет данные или их полноту (решение № 1)
|
||||||
- scan-gateway: «любое → `needs_check`» уходит
|
- scan-gateway: «любое → `needs_check`» уходит
|
||||||
- tabular-parser: предупреждения начинают влиять
|
- tabular-parser: предупреждения начинают влиять
|
||||||
- **3. Статус документа** — в контракте: `document.status`; конверт `done` ⇔ все документы `done` ∧ нет предупреждений файла уровня `error`; иначе `needs_check`; документов нет — `error` с кодом по источнику: `RECOGNITION_FAILED` у шлюза, `INVALID_DOCUMENT` у парсера — это различие остаётся, коды описывают разные причины (F-23)
|
- **3. Статус документа** — в контракте: `document.status`; конверт `done` ⇔ все документы `done` ∧ нет предупреждений файла уровня `error`; иначе `needs_check`; документов нет — `error` с кодом по источнику: `RECOGNITION_FAILED` у шлюза, `INVALID_DOCUMENT` у парсера — это различие остаётся, коды описывают разные причины (F-23). Статус конверта остаётся условием `RECOGNIZED` в ядре (G-7): потерянный json набора не даёт соседнему документу автоподтвердиться
|
||||||
- scan-gateway: добавляет
|
- scan-gateway: добавляет
|
||||||
- tabular-parser: внутренний `ok` → в контракт
|
- tabular-parser: внутренний `ok` → в контракт
|
||||||
- **4. Формат предупреждений** — структура (см. выше) вместо строк `<json>: table:<i>: …` / `sheet=<имя> …`; место — в `locator` документа, а не в тексте
|
- **4. Формат предупреждений** — структура (см. выше) вместо строк `<json>: table:<i>: …` / `sheet=<имя> …`; место — в `locator` документа, а не в тексте
|
||||||
- scan-gateway: меняет
|
- scan-gateway: меняет
|
||||||
- tabular-parser: меняет
|
- tabular-parser: меняет
|
||||||
- **5. Привязка** — к документу, строке (`line_ref`) или ячейке (`line_ref` + `column_index`); `line_ref` — это `ref` строки, он хранится в ядре вместе с `row_index` (F-4), поэтому экран показывает предупреждение у места и после сохранения
|
- **5. Привязка** — к документу, строкам (`line_refs`) или ячейкам (`line_refs` + `column_index`); `ref` строки уникален в документе и хранится в ядре вместе с `row_index` (F-4, G-9), поэтому экран показывает предупреждение у места и после сохранения
|
||||||
- scan-gateway: меняет
|
- scan-gateway: меняет
|
||||||
- tabular-parser: меняет
|
- tabular-parser: меняет
|
||||||
|
|
||||||
**Подтверждение проверкой (вопрос владельца 17.10.2026; F-1, F-2, F-13).** Проверка §7 — арбитр, уверенность —
|
**Подтверждение проверкой (вопрос владельца 17.10.2026; F-1, F-2, F-13, V-13).** Проверка §7 — арбитр, уверенность —
|
||||||
подсказка там, где проверки нет. По каждому значению сервис считает `needs_review`:
|
подсказка там, где проверки нет. По каждому значению сервис считает `needs_review`:
|
||||||
|
|
||||||
|
- значение **нуждается в покрытии**, если `confidence` ниже порога (Р-5: 100) или `is_recognized = false` (число
|
||||||
|
собрано из грязного текста или с заменой двойников, Р-7, Р-11); чистые значения (уверенность 100 и
|
||||||
|
`is_recognized = true`) в покрытии не нуждаются и ни одним правилом ниже не подсвечиваются (V-13);
|
||||||
- значение **покрыто**, если в его колонке есть жёсткое соотношение с участием его кода, которое посчитано (хотя бы
|
- значение **покрыто**, если в его колонке есть жёсткое соотношение с участием его кода, которое посчитано (хотя бы
|
||||||
один член непуст, F-2) и сошлось (в пределах допуска или округления, F-13), и `amount` самого значения не `null`
|
один член непуст, F-2) и сошлось (в пределах допуска или округления, F-13), и `amount` самого значения не `null`
|
||||||
(мусор, давший `null`, покрытым не бывает, F-2);
|
(мусор, давший `null`, покрытым не бывает, F-2);
|
||||||
- `needs_review = true`, если `confidence` ниже порога (Р-5: 100) или `is_recognized = false` (число собрано из
|
|
||||||
грязного текста или с заменой двойников, Р-7, Р-11) **и** значение не покрыто;
|
|
||||||
- **знак** (F-1): для члена соотношения с `abs_terms` покрытие подтверждает только величину. Знак подтверждён, если
|
- **знак** (F-1): для члена соотношения с `abs_terms` покрытие подтверждает только величину. Знак подтверждён, если
|
||||||
форма записи знака у ячейки (скобки, минус, без знака) совпадает с конвенцией колонки — формой записи у большинства
|
форма записи знака у ячейки (скобки, минус, без знака — по `text`) совпадает с конвенцией колонки — формой записи
|
||||||
членов `abs_terms` в этой колонке с `is_recognized = true`; не совпадает или сравнивать не с чем — `needs_review =
|
у большинства членов `abs_terms` этой колонки с непустым `amount` и `is_recognized = true`; не совпадает или
|
||||||
true` независимо от соотношения. Так «(7744536}» с `}` как закрывающей скобкой даёт −7 744 536 и подтверждается,
|
сравнивать не с чем — `needs_review = true` независимо от соотношения. Так «(7744536}» с `}` как закрывающей скобкой
|
||||||
если 2210 и 2220 в той же колонке тоже в скобках; «7744536}» без открывающей скобки при остальных в скобках —
|
даёт −7 744 536 и подтверждается, если 2210 и 2220 в той же колонке тоже в скобках; «{38378)» при остальных в
|
||||||
подсвечивается;
|
скобках — подсвечивается;
|
||||||
|
- `needs_review = true`, если значение нуждается в покрытии и не покрыто (или не подтверждён знак);
|
||||||
- `true` у всех ячеек жёсткого соотношения в колонке, где оно не сошлось вне допуска и округления, независимо от
|
- `true` у всех ячеек жёсткого соотношения в колонке, где оно не сошлось вне допуска и округления, независимо от
|
||||||
уверенности; мягкие соотношения и расхождения в пределах округления подсветки не дают;
|
уверенности; мягкие соотношения и расхождения в пределах округления подсветки не дают;
|
||||||
- иначе `false`: значение подтверждено проверкой, не подсвечивается и статус не трогает; сырой текст и уверенность
|
- иначе `false`: значение подтверждено проверкой, не подсвечивается и статус не трогает; сырой текст (`text`) и
|
||||||
остаются в подсказке ячейки.
|
уверенность остаются в подсказке ячейки.
|
||||||
|
|
||||||
Экран подсвечивает ячейки только по `needs_review`. Ядро хранит `needs_review` как пришло и пересчитывает его вместе с
|
Экран подсвечивает ячейки только по `needs_review`. Ядро хранит `needs_review` и `text` как пришли и пересчитывает
|
||||||
`checks` общим кодом после PATCH, добавляя своё: правленое значение (`is_modified`) — `needs_review = false`; акцепт
|
`needs_review` вместе с `checks` общим кодом после PATCH (конвенция знака считается от сохранённого `text`, V-3),
|
||||||
нарушений — жёсткие нарушения не считаются; предупреждения `error` документа сохраняются и после правки (F-8). Не
|
добавляя своё: правленое значение (`is_modified`) или подтверждённое аналитиком без изменения (`confirmed`, G-12) —
|
||||||
покрыто проверками и остаётся на уверенности: поля шапки, справочные строки ОФР 25xx / 29xx, детализация без проверки
|
`needs_review = false`; акцепт нарушений — жёсткие нарушения не считаются; предупреждения `error` документа
|
||||||
родителя, `DISCLOSURE`, колонки, где есть только мягкие соотношения. Риск — две взаимно компенсирующие ошибки в одном
|
сохраняются и после правки (F-8). Не покрыто проверками и остаётся на уверенности: поля шапки, справочные строки ОФР
|
||||||
соотношении; принимается (перекрёстные соотношения баланса его снижают). Закрывает AURORA-1524 без ущерба для 1519.
|
25xx / 29xx, детализация без проверки родителя, `DISCLOSURE`, колонки, где есть только мягкие соотношения. Риск — две
|
||||||
Сошедшееся посчитанное равенство подтверждает и догадку из грязного текста (Р-7, решение владельца 17.10.2026):
|
взаимно компенсирующие ошибки в одном соотношении; принимается (перекрёстные соотношения баланса его снижают).
|
||||||
случайное совпадение требует второй, компенсирующей ошибки в том же равенстве.
|
Закрывает AURORA-1524 без ущерба для 1519. Сошедшееся посчитанное равенство подтверждает и догадку из грязного текста
|
||||||
|
(Р-7, решение владельца 17.10.2026): случайное совпадение требует второй, компенсирующей ошибки в том же равенстве.
|
||||||
|
|
||||||
**Код и наименование строки (Р-5, решение владельца 17.10.2026; F-9).** Уверенность кода подтверждается не §7
|
**Код и наименование строки (Р-5, Р-12, решения владельца 17.10.2026; F-9, V-2, G-1).** Уверенность кода
|
||||||
(перепутанные соседи 1220 и 1230 дают ту же сумму раздела), а структурой: в общем коде лежит **упорядоченный перечень
|
подтверждается не §7 (перепутанные соседи 1220 и 1230 дают ту же сумму раздела), а структурой; правило применяется
|
||||||
кодов** каждой формы по редакциям (66н, ФСБУ 4) без наименований — порядок бланка, где итоги стоят после слагаемых
|
**только к кодам с уверенностью ниже 100** (Р-12): у Excel и чистых OCR-кодов расхождение с перечнем даёт
|
||||||
(1110 … 1190, 1100; 2110, 2120, 2100), а не по возрастанию. Код подтверждён, если он есть в перечне и его положение
|
предупреждение `info` ядра «код не из редакции», а не `needs_check` — иначе пятизначные подстроки 1С (11101, 23201)
|
||||||
относительно соседних подтверждённых кодов совпадает с порядком бланка хотя бы в одной из проверяемых редакций
|
и нестандартные строки клиента блокировали бы автоподтверждение. В общем коде лежит **упорядоченный перечень кодов**
|
||||||
(редакция по эффективной дате, без даты — обе); равные соседние коды допустимы (Р-4). Подтверждён — уверенность не
|
каждой формы по редакциям (66н, ФСБУ 4) и вариантам (полная, упрощённая) без наименований — порядок бланка, где
|
||||||
важна; иначе `needs_review` у строки (`StatementLine.needs_review`), документ — `needs_check`. По `fixtures/ocr` коды
|
итоги стоят после слагаемых (1110 … 1190, 1100; 2110, 2120, 2100), а не по возрастанию. Источник перечня —
|
||||||
часто приходят с уверенностью 50 и 25, поэтому требовать 100, как шлюз сейчас, нельзя. Перечень кодов — не словарь
|
объединение кодов шаблонов банка (словарь ядра) и кодов печатных форм 1С по редакции: 1105, 1530, 2420, 2460, 2930 и
|
||||||
строк: наименований в нём нет, Р-3 не нарушается. Уверенность наименования на статус и подсветку в сервисах не
|
другие, которых в словаре ядра нет (G-1). Алгоритм: четырёхзначные коды строки документа образуют
|
||||||
влияет: сервисы отдают текст как есть; сопоставление файлового наименования со справочным по коду — вопрос ядра,
|
последовательность; подтверждены коды из наибольшей подпоследовательности, согласованной с порядком бланка хотя бы в
|
||||||
открыт (см. «Что уходит из ядра»).
|
одной из проверяемых редакций (редакция по эффективной дате, без даты — обе); остальные — `needs_review` у строки
|
||||||
|
(`StatementLine.needs_review`), документ — `needs_check`. Пятизначный код подтверждён, если подтверждён его
|
||||||
|
четырёхзначный родитель (ближайшая строка выше с теми же четырьмя первыми цифрами, иначе код из первых четырёх
|
||||||
|
цифр — правило `subline_parents`) и подстрока стоит после него (V-2). Равные соседние коды допустимы (Р-4). По
|
||||||
|
`fixtures/ocr` коды часто приходят с уверенностью 50 и 25, поэтому требовать 100, как шлюз сейчас, нельзя.
|
||||||
|
Перечень кодов — не словарь строк: наименований в нём нет, Р-3 не нарушается; ядро пользуется своей копией перечня
|
||||||
|
для проверки «код не из редакции», словарь ядра хранит только имена (V-11). Уверенность наименования на статус и
|
||||||
|
подсветку в сервисах не влияет: сервисы отдают текст как есть; сопоставление файлового наименования со справочным по
|
||||||
|
коду — вопрос ядра, открыт (см. «Что уходит из ядра»).
|
||||||
|
|
||||||
### Шаг 9 — Результат
|
### Шаг 9 — Результат
|
||||||
|
|
||||||
Одинаково, как сейчас; контракт `recognition-result.schema.json` → **v2.0** (**решение владельца 17.10.2026, Р-9**: по
|
Одинаково, как сейчас; контракт `recognition-result.schema.json` → **v2.0** (**решение владельца 17.10.2026, Р-9**: по
|
||||||
правилам контракта смена типа поля — MAJOR, F-7). Что меняется: `line_code` nullable, `line_status`, `line_code_text`,
|
правилам контракта смена типа поля — MAJOR, F-7). Что меняется: `line_code` nullable, `line_status`, `line_code_text`,
|
||||||
`row_index`, `ref`, `parent_ref`, `needs_review` у значения и у строки; `document.status`; `document.warnings[]` и
|
`row_index`, `ref`, `parent_ref`, `needs_review` у значения и у строки, `values[].text`; `document.status`,
|
||||||
`metrics.warnings[]` — структуры с уровнем и привязкой; `columns[].period_kind`; `checks` обязателен для `BALANCE` /
|
`document.form_variant`; `document.warnings[]` и `metrics.warnings[]` — структуры с уровнем и привязкой `line_refs[]`;
|
||||||
`INCOME` со строками у обоих конвейеров (у документа без строк отсутствует), нарушения с кодами слагаемых, без
|
`columns[].period_kind`; `checks` обязателен для `BALANCE` / `INCOME` со строками у обоих конвейеров (у документа без
|
||||||
`duplicate_line_codes`; `service.statements_version` — коммит копии общего кода, которым посчитан результат (F-17).
|
строк отсутствует), нарушения с кодами слагаемых, без `duplicate_line_codes`; `service.statements_version` — коммит
|
||||||
Выкладка: ядро поднимает `SUPPORTED_MAJOR` и на время перехода принимает 1.x и 2.0 (два маппера), сервисы переходят
|
копии общего кода, которым посчитан результат (F-17). Выкладка: ядро поднимает `SUPPORTED_MAJOR` и на время перехода
|
||||||
на 2.0 после ядра — порядок «сначала ядро» обязателен, иначе первый же результат с `null` в `line_code` даёт файлу
|
принимает 1.x и 2.0 (два маппера; при приёме 1.x новые поля заполняются по умолчанию: `line_status = ok`, `row_index`
|
||||||
`ERROR` (F-7).
|
по порядку, `text = string_value`, `needs_review` по старому правилу `values_ok`), сервисы переходят на 2.0 после
|
||||||
|
ядра — порядок «сначала ядро» обязателен, иначе первый же результат с `null` в `line_code` даёт файлу `ERROR` (F-7).
|
||||||
|
Запрос `recognition-request` остаётся 1.x.
|
||||||
|
|
||||||
### Что уходит из ядра и что в нём меняется
|
### Что уходит из ядра и что в нём меняется
|
||||||
|
|
||||||
- `build_statement`: повторная дедупликация колонок, кодов и значений (контракт гарантирует уникальность колонок;
|
- `build_statement`: повторная дедупликация колонок, кодов и значений (контракт гарантирует уникальность колонок;
|
||||||
повторы кодов — законные строки, хранятся все как пришли).
|
повторы кодов — законные строки, хранятся все как пришли).
|
||||||
- **DBML v5.19 и одна миграция** (F-4, F-8, F-10, Р-4, Р-10): `financial_statement_line` — снять уникальность
|
- **DBML v5.19 и одна миграция** (F-4, F-8, F-10, V-3, V-5, V-10, Р-4, Р-10): `financial_statement_line` — снять
|
||||||
`(meta_id, line_code)`, добавить `row_index`, `source_ref`, `line_status`, `line_code_text`, `parent_row_index`
|
уникальность `(meta_id, line_code)`, добавить `row_index`, `source_ref` (уникален в документе), `line_status`,
|
||||||
(родитель `detail`); `financial_statement_value.needs_review` (как пришло от сервиса, пересчитывается ядром после
|
`line_code_text`, `parent_row_index` (родитель `detail`), `needs_review`; `financial_statement_value` — `source_text`
|
||||||
PATCH; пересматривает решение v5.13 «считается, не хранится»); `financial_statement_column.period_kind`;
|
(сырой текст, из `values[].text`) и `needs_review` (как пришло от сервиса, пересчитывается ядром после PATCH;
|
||||||
таблица `document_issue` (вид `warning` | `relation` | `soft`, код, уровень, сообщение, `line_id?`, `column_id?`,
|
пересматривает решение v5.13 «считается, не хранится»); `financial_statement_column.period_kind`;
|
||||||
источник `service` | `core`) — хранит предупреждения и нарушения как пришли и после пересчёта; `document` — снять
|
`financial_statement_meta.form_variant`; таблица `document_issue` (вид `warning` | `relation` | `soft`, код, уровень,
|
||||||
уникальность `(stored_file_id, document_type, osv_account_code)` и добавить порядковый номер формы в файле
|
сообщение, список `line_id`, `column_id?`, источник `service` | `core`) — хранит предупреждения и нарушения как
|
||||||
(**решение владельца 17.10.2026, Р-10**; F-11: вторая форма того же типа больше не теряется по OQ-77, привязка к
|
пришли, очищается при перезагрузке файла, записи источника `core` заменяются при каждом пересчёте (G-25);
|
||||||
версии консолидации — по периоду, как у ОСВ); в ту же миграцию — `crm_id` ГСК / ГСЗ (AURORA-1619, arm-core#112,
|
`recognition_job.statements_version`; `document` — снять уникальность `(stored_file_id, document_type,
|
||||||
отложенный «до следующих изменений БД»).
|
osv_account_code)` и добавить порядковый номер формы в файле (**решение владельца 17.10.2026, Р-10**; F-11); в ту же
|
||||||
|
миграцию — `crm_id` ГСК / ГСЗ (AURORA-1619, arm-core#112, отложенный «до следующих изменений БД»).
|
||||||
- Повторы кодов (решение владельца 17.10.2026): для проверок §7, подтверждения и всего, что идёт после (КФО, выгрузки,
|
- Повторы кодов (решение владельца 17.10.2026): для проверок §7, подтверждения и всего, что идёт после (КФО, выгрузки,
|
||||||
сверки), документ читается через **одну функцию «агрегат формы по коду»** — сумма значений по колонке, список
|
сверки), документ читается через **одну функцию «агрегат формы по коду»** — сумма значений строк `ok` по колонке,
|
||||||
`line_id`, справочное имя — чистой функцией, без хранения (DECISION-5). На неё переводятся все точки, где сегодня
|
список `line_id`, справочное имя — чистой функцией, без хранения (DECISION-5). На неё переводятся все точки, где
|
||||||
«одна строка на код» (F-15): `_line_amount` (межформенная проверка 1370 / 2400), `statement_amounts`,
|
сегодня «одна строка на код» (F-15): `_line_amount` (межформенная проверка 1370 / 2400), `statement_amounts`,
|
||||||
`subline_parents` / `parent_code`, `formLayout` (дети под родителем), `_resolve_cells`, `metrics.fields_*`.
|
`subline_parents` / `parent_code`, `formLayout` (дети под родителем), `_resolve_cells`, `metrics.fields_*`.
|
||||||
Экран показывает строки как в файле, по `row_index`.
|
- `statement_status` (D-6): `RECOGNIZED` ⇔ конверт `done` (G-7, решение № 28: потерянный json не даёт соседу
|
||||||
- `statement_status` (D-6): `RECOGNIZED` ⇔ `document.status = done` ∧ ИНН ≠ `MISMATCH` ∧ период — конец квартала
|
автоподтвердиться) ∧ `document.status = done` ∧ ИНН ≠ `MISMATCH` ∧ период — конец квартала (`period_known`, F-18:
|
||||||
(`period_known`, F-18: иначе документ `RECOGNIZED`, но не подтверждаемый, 409 `PERIOD_REQUIRED`); остальные признаки
|
иначе документ `RECOGNIZED`, но не подтверждаемый, 409 `PERIOD_REQUIRED`); остальные признаки D-6 не
|
||||||
D-6 не пересчитываются.
|
пересчитываются. Вторая форма того же типа и периода у того же ЮЛ из одного файла не автоподтверждается (Р-15).
|
||||||
- §7 при загрузке: `checks`, `needs_review` и предупреждения берутся из результата и хранятся (`document_issue`);
|
- §7 при загрузке: `checks`, `needs_review` и предупреждения берутся из результата и хранятся (`document_issue`);
|
||||||
пересчёт общим кодом — только после PATCH, с правилами `is_modified` ⇒ `needs_review = false`, акцепт ⇒ жёсткие
|
ядро добавляет только `info` по словарю имён и перечню кодов (G-26). Пересчёт общим кодом — только после PATCH, с
|
||||||
нарушения сняты, предупреждения `error` сервиса сохраняются (F-8). Версия общего кода из результата
|
правилами `is_modified` или `confirmed` ⇒ `needs_review = false` (G-12), `line_code_modified` ⇒ `needs_review` строки
|
||||||
(`statements_version`) пишется в `recognition_job`; при пересчёте ядро сравнивает её со своей копией, расхождение —
|
= false (V-5), акцепт ⇒ жёсткие нарушения сняты, предупреждения `error` сервиса сохраняются (F-8). Версия общего кода
|
||||||
предупреждение `error` к документу, не падение (F-17).
|
из результата (`statements_version`) пишется в `recognition_job`; при пересчёте ядро сравнивает её со своей копией,
|
||||||
|
расхождение — предупреждение `error` к документу, не падение (F-17).
|
||||||
|
- **PATCH `…/statement`** (F-14, G-12, G-13, Р-13): помимо `header` и `values` принимает `values[].confirmed`
|
||||||
|
(подтвердить ячейку без изменения значения: `needs_review = false`, `is_modified` не ставится, аудит), `columns:
|
||||||
|
[{column_id, period_date}]` (дата колонке без подписи; снимает предупреждение `error` «колонка без даты»,
|
||||||
|
пересчитывает `is_current`; `report_date: null` обнуляет `period_end` относительных колонок, G-23) и `lines:
|
||||||
|
[{line_id, line_code}]` («назначить код») / `[{line_id, line_status: detail}]` («отметить расшифровкой»). Переходы:
|
||||||
|
`unreadable` / `missing` / `detail` → `ok` назначением кода; `unreadable` / `missing` / `ok` → `detail` отметкой;
|
||||||
|
`ok` → `ok` с другим кодом (смена кода, прежний в `line_code_original_value`). Правила: код 4–5 цифр, может
|
||||||
|
совпадать с существующим (Р-4); незнакомый словарю — `info`; назначенный код снимает `needs_review` строки;
|
||||||
|
`line_code_modified`, `line_code_original_value`; у `detail` родитель пересчитывается как ближайшая строка `ok`
|
||||||
|
выше по `row_index`; шаблонная строка с таким кодом заменяется настоящей; пара «`line_status = ok` без кода» — 422;
|
||||||
|
удаления строк нет (первичка не мутируется, `detail` — способ вывести строку из сумм). Аудит
|
||||||
|
`RECOGNIZED_DATA_EDIT` с `before / after` по коду, статусу и родителю. Один PATCH — один пересчёт и одна запись.
|
||||||
|
- **Подтверждение документа** (Р-13, G-10): `form_confirmable` / `_check_statement` → 409 `LINES_UNRESOLVED` со
|
||||||
|
списком, пока есть строки `unreadable` / `missing` с непустым `amount`; строки без сумм не мешают. Автоподтверждения
|
||||||
|
это не касается (`done` таких строк не допускает). `detail` в агрегат, КФО и снимок ЕХ не попадают — цена Р-8.
|
||||||
|
Подтверждение второго документа того же типа и периода у ЮЛ при подтверждённом первом — 409 со ссылкой на первый
|
||||||
|
(Р-15).
|
||||||
- Правка даты отчёта (PATCH шапки): для колонок с `period_kind = relative:<вид>` ядро досчитывает `period_end` общим
|
- Правка даты отчёта (PATCH шапки): для колонок с `period_kind = relative:<вид>` ядро досчитывает `period_end` общим
|
||||||
кодом от новой даты (F-10).
|
кодом от новой даты (F-10).
|
||||||
- **Назначение кода строке** (F-14): `PATCH …/statement` принимает `lines: [{line_id, line_code | null, line_status:
|
- Словарь §7: единственный экземпляр в общем коде (Р-1), копия ядра удаляется. Словарь имён строк (код → наименование
|
||||||
ok | detail}]`; назначенный код может совпадать с существующим (Р-4); `line_status` → `ok`, `line_code_modified`,
|
по редакциям) остаётся только в ядре, копия шлюза удаляется; порядок и состав редакций ядро берёт из своей копии
|
||||||
`line_code_original_value`, аудит `RECOGNIZED_DATA_EDIT` с `before / after`, пересчёт `checks` и `needs_review`;
|
перечня общего кода (V-11).
|
||||||
строке `detail` можно назначить код (становится `ok`), строку `missing` — объявить расшифровкой (`detail`).
|
- Достройка шаблонных строк: по словарю ядра; код, у которого есть хотя бы одна строка `ok` из файла, шаблоном не
|
||||||
- Словарь §7: единственный экземпляр в общем коде (Р-1), копия ядра удаляется. Словарь строк (код → наименование,
|
достраивается.
|
||||||
редакции) остаётся только в ядре, копия шлюза удаляется; проверки по нему (код не в словаре, код не из редакции по
|
- **Экран** (решение владельца 17.10.2026, Р-14; G-15): раскладка остаётся «как на бумаге» — разделы, внутри строки
|
||||||
дате отчёта) ядро делает само при загрузке и после PATCH, как предупреждения к строке в `document_issue`.
|
`ok` по возрастанию кода, несколько строк одного кода в порядке файла, шаблонные строки на своих местах,
|
||||||
- Достройка шаблонных строк: по словарю ядра; строки `missing` / `unreadable` показываются как пришли, на своём месте по
|
пятизначные подстроки под родителем. Строка `unreadable` / `missing` ставится под ближайшей предыдущей по
|
||||||
`row_index`, с подсветкой; код, у которого есть хотя бы одна строка `ok` из файла, шаблоном не достраивается.
|
`row_index` строкой, которая разместилась по коду, подсвечена целиком, в ячейке кода серым сырой текст с подсказкой.
|
||||||
- Экран: подсветка ячеек только по `needs_review` (уверенность, `is_recognized` и нарушения §7 уже сведены сервисом);
|
Строки `detail` скрыты под переключателем «Показать расшифровки (N)», как пустые строки детализации, показываются
|
||||||
строк — по `line_status` и `needs_review` строки; строки `detail` скрыты по умолчанию под родителем,
|
под родителем с отступом. Подсветка ячеек только по `needs_review` (уверенность, `is_recognized` и нарушения §7 уже
|
||||||
переключатель как у пустых строк детализации (Р-8); предупреждения и нарушения — из `document_issue`, с привязкой к
|
сведены сервисом), в подсказке — `text` и уверенность; предупреждения и нарушения — из `document_issue`, с привязкой
|
||||||
месту; поле кода редактируемо у строк `unreadable` / `missing` / `detail`.
|
к месту. В режиме «Редактировать»: ячейка кода у `unreadable` / `missing` — поле ввода с подсказкой из справочника
|
||||||
|
(код → справочное имя), меню строки «Это расшифровка»; у `detail` — «Назначить код»; у обычной строки — «Сделать
|
||||||
|
расшифровкой» и смена кода; подтверждение ячейки без правки; дата у колонки без подписи. Кнопка «Подтвердить
|
||||||
|
данные» при неразобранных строках с суммами отключена с подсказкой «Разберите строки без кода: N» и переходом к
|
||||||
|
первой; сервер отвечает тем же 409.
|
||||||
- Открытый вопрос ядра (не сервисов): сопоставлять ли файловое наименование строки со справочным по коду и что
|
- Открытый вопрос ядра (не сервисов): сопоставлять ли файловое наименование строки со справочным по коду и что
|
||||||
делать при расхождении (владелец 17.10.2026: «наверное, будем пытаться сопоставить»). В сервисах уверенность и
|
делать при расхождении (владелец 17.10.2026: «наверное, будем пытаться сопоставить»). В сервисах уверенность и
|
||||||
текст наименования на статус не влияют.
|
текст наименования на статус не влияют. Второй открытый вопрос (Р-15, V-7): склейка двух половинок формы из одного
|
||||||
|
файла не предоставляется, файл перезагружают — в `07-open-questions.md`.
|
||||||
|
|
||||||
### Решения владельца (Р-1…Р-11, 17.10.2026)
|
### Решения владельца (Р-1…Р-15, 17.10.2026)
|
||||||
|
|
||||||
- **Р-1** (шаг 1, п. 2) — **решено 17.10.2026: копия по скрипту.** Платформа не собирает Python-пакеты и не кладёт их
|
- **Р-1** (шаг 1, п. 2) — **решено 17.10.2026: копия по скрипту.** Платформа не собирает Python-пакеты и не кладёт их
|
||||||
в свой Nexus, поэтому общий код — dataclass-ы шага 1 (сериализуемые в json, golden-файлы в fixtures), шаги 2–8,
|
в свой Nexus, поэтому общий код — dataclass-ы шага 1 (сериализуемые в json), шаги 2–8, словари §7, периодов,
|
||||||
словари §7, периодов, заголовков форм, перечни кодов (без словаря строк) со своим загрузчиком — живёт в отдельном
|
заголовков форм, перечни кодов (без словаря имён) со своим загрузчиком — живёт в отдельном репозитории
|
||||||
репозитории `arm-statements` со своими тестами и оформлен как пакет `arm_statements/` с `pyproject`. Уточнения по
|
`arm-statements` со своими тестами и оформлен как пакет `arm_statements/` с `pyproject`. Границы пакета (G-14):
|
||||||
F-17: в tabular-parser, scan-gateway и arm-core пакет лежит копией вместе со своими тестами и конфигом линтеров,
|
пакет владеет своими типами входа и выхода, хост конвертирует выход в свою модель контракта (у ядра на переходе их
|
||||||
импортируется по имени пакета `arm_statements` (путь добавляется в `pyproject`), поэтому замена копии на
|
две); golden-тесты пакета — внутри пакета на синтетических и обезличенных данных, тесты на реальных фикстурах
|
||||||
зависимость не трогает импорты; маркер коммита источника попадает в результат как `service.statements_version`;
|
остаются в scan-gateway и tabular-parser и соседний репозиторий не читают. Уточнения по F-17: в tabular-parser,
|
||||||
раскладывает и проверяет свежесть скрипт `sync-statements` в docs по образцу `sync-web.sh` (`--check`, код 20 при
|
scan-gateway и arm-core пакет лежит копией вместе со своими тестами и конфигом линтеров, импортируется по имени
|
||||||
прямых правках) — **не** `make check` потребителя: тест, читающий соседний репозиторий, уже ломал main (arm-core #33,
|
пакета `arm_statements` (путь добавляется в `pyproject`), поэтому замена копии на зависимость не трогает импорты;
|
||||||
пайплайн платформы вне репозиториев). Прямые правки копии запрещены.
|
маркер коммита источника попадает в результат как `service.statements_version`; раскладывает и проверяет свежесть
|
||||||
|
скрипт `sync-statements` в docs по образцу `sync-web.sh` (`--check`, код 20 при прямых правках), вызывается из
|
||||||
|
`make check-full` перед PR локально — **не** из `make check` и не из пайплайна: тест, читающий соседний репозиторий,
|
||||||
|
уже ломал main (arm-core #33, пайплайн платформы вне репозиториев). Прямые правки копии запрещены.
|
||||||
- **Р-2** (шаг 4, п. 5) — **решено владельцем 17.10.2026:** отсутствующая позиция под колонкой — чистый `null` без
|
- **Р-2** (шаг 4, п. 5) — **решено владельцем 17.10.2026:** отсутствующая позиция под колонкой — чистый `null` без
|
||||||
собственной подсветки. Основание: по fixtures ИС отдаёт ячейку на каждую позицию (пустая — уверенность 100,
|
собственной подсветки. Основание: по fixtures ИС отдаёт ячейку на каждую позицию (пустая — уверенность 100,
|
||||||
непрочитанная — 0, и её ловит общее правило `needs_review`); отсутствие позиции бывает только при несовпадении
|
непрочитанная — 0, и её ловит общее правило `needs_review`); отсутствие позиции бывает только при несовпадении
|
||||||
@ -695,19 +754,19 @@ line_name, line_name_confidence, parent_ref, values}`: `line_code` — разо
|
|||||||
уверенность дискретная: 100, ~80, 71, 67, 60, 50, 25, 0, промежутка 90–99 нет; в группе 70–89 есть настоящие ошибки,
|
уверенность дискретная: 100, ~80, 71, 67, 60, 50, 25, 0, промежутка 90–99 нет; в группе 70–89 есть настоящие ошибки,
|
||||||
например «11 30» с уверенностью 80); ложных подсветок становится меньше за счёт подтверждения проверкой §7, а не
|
например «11 30» с уверенностью 80); ложных подсветок становится меньше за счёт подтверждения проверкой §7, а не
|
||||||
порога. Код строки подтверждается структурой — упорядоченным перечнем кодов формы по редакции (F-9), не
|
порога. Код строки подтверждается структурой — упорядоченным перечнем кодов формы по редакции (F-9), не
|
||||||
уверенностью. Уверенность наименования в сервисах не используется; сопоставление со справочным — открытый вопрос
|
уверенностью; область применения — Р-12. Уверенность наименования в сервисах не используется; сопоставление со
|
||||||
ядра (= решение № 7 по спринту 2, AURORA-1519 / 1524).
|
справочным — открытый вопрос ядра (= решение № 7 по спринту 2, AURORA-1519 / 1524).
|
||||||
- **Р-6** (шаг 2, п. 6) — **решено владельцем 17.10.2026:** числовая колонка с нераспознанной подписью остаётся в
|
- **Р-6** (шаг 2, п. 6) — **решено владельцем 17.10.2026:** числовая колонка с нераспознанной подписью остаётся в
|
||||||
документе без даты с предупреждением `error` (`needs_check`, подтверждение руками); пропуск отвергнут как тихая
|
документе без даты с предупреждением `error` (`needs_check`, подтверждение руками); пропуск отвергнут как тихая
|
||||||
потеря периода. Относительные подписи бланка 66н («На отчётную дату», «На 31 декабря предыдущего года», …)
|
потеря периода. Относительные подписи бланка 66н («На отчётную дату», «На 31 декабря предыдущего года», …)
|
||||||
добавляются в словарь периодов и считаются от эффективной даты документа (F-6); после правки даты отчёта ядро
|
добавляются в словарь периодов и считаются от эффективной даты документа (F-6); после правки даты отчёта ядро
|
||||||
досчитывает их (F-10).
|
досчитывает их (F-10); дату колонке можно поставить руками (G-13).
|
||||||
- **Р-7** (шаг 8, «Подтверждение проверкой») — **решено владельцем 17.10.2026: да.** Сошедшееся жёсткое соотношение §7
|
- **Р-7** (шаг 8, «Подтверждение проверкой») — **решено владельцем 17.10.2026: да.** Сошедшееся жёсткое соотношение §7
|
||||||
снимает подсветку и с `is_recognized = false`; такой документ может получить `done` и автоподтвердиться, сырой
|
снимает подсветку и с `is_recognized = false`; такой документ может получить `done` и автоподтвердиться, сырой
|
||||||
текст остаётся в подсказке ячейки. Оговорки по ревью: соотношение должно быть посчитанным, а не «0 = 0» (F-2); для
|
текст остаётся в подсказке ячейки. Оговорки по ревью: соотношение должно быть посчитанным, а не «0 = 0» (F-2); для
|
||||||
членов по модулю знак подтверждается конвенцией колонки, а не соотношением (F-1). Варианты «всегда подсвечивать» и
|
членов по модулю знак подтверждается конвенцией колонки, а не соотношением (F-1); правило знака — только для
|
||||||
«снимать подсветку, но не автоподтверждать» отвергнуты: первый возвращает ложные подсветки AURORA-1524, второй даёт
|
сомнительных ячеек (V-13). Варианты «всегда подсвечивать» и «снимать подсветку, но не автоподтверждать» отвергнуты:
|
||||||
статус «требует проверки» без подсветок.
|
первый возвращает ложные подсветки AURORA-1524, второй даёт статус «требует проверки» без подсветок.
|
||||||
- **Р-8** (шаг 3, F-3) — **решено владельцем 17.10.2026:** расшифровки без кода («в том числе …») отдаются сервисами
|
- **Р-8** (шаг 3, F-3) — **решено владельцем 17.10.2026:** расшифровки без кода («в том числе …») отдаются сервисами
|
||||||
со статусом `detail` и хранятся ядром; в проверки, агрегаты и `done` не входят; экран по умолчанию их не показывает
|
со статусом `detail` и хранятся ядром; в проверки, агрегаты и `done` не входят; экран по умолчанию их не показывает
|
||||||
(переключатель, как у пустых строк детализации). Отменяет OQ-36 / OQ-141.
|
(переключатель, как у пустых строк детализации). Отменяет OQ-36 / OQ-141.
|
||||||
@ -719,24 +778,39 @@ line_name, line_name_confidence, parent_ref, values}`: `line_code` — разо
|
|||||||
- **Р-11** (принцип 5, F-19) — **решено владельцем 17.10.2026:** буквы-двойники заменяются и в суммах, и в кодах;
|
- **Р-11** (принцип 5, F-19) — **решено владельцем 17.10.2026:** буквы-двойники заменяются и в суммах, и в кодах;
|
||||||
результат — `is_recognized = false`, дальше его проверяют контрольные соотношения §7, и если всё сошлось,
|
результат — `is_recognized = false`, дальше его проверяют контрольные соотношения §7, и если всё сошлось,
|
||||||
подсвечивать не нужно (Р-7 с оговорками F-1, F-2). Отменяет §11.1 п. 1 «только даты» и решение № 22 среза 3a.
|
подсвечивать не нужно (Р-7 с оговорками F-1, F-2). Отменяет §11.1 п. 1 «только даты» и решение № 22 среза 3a.
|
||||||
Данные: в четырёх json ИС двойников в суммах и кодах нет вовсе — правило на вырост, не под текущие сканы.
|
Уточнение по G-18: ИНН двойники не трогают. Данные: в четырёх json ИС двойников в суммах и кодах нет вовсе —
|
||||||
|
правило на вырост, не под текущие сканы.
|
||||||
|
- **Р-12** (шаг 8, «Код и наименование строки»; G-1, V-2) — **решено владельцем 17.10.2026:** структурное
|
||||||
|
подтверждение кода применяется только к кодам с уверенностью ниже 100; у Excel и чистых OCR-кодов расхождение с
|
||||||
|
перечнем даёт `info` от ядра, не `needs_check`.
|
||||||
|
- **Р-13** (подтверждение документа; G-10, F-14) — **решено владельцем 17.10.2026:** подтверждение документа
|
||||||
|
блокируется (409 `LINES_UNRESOLVED`), пока есть строки `unreadable` / `missing` с суммами; аналитик назначает код
|
||||||
|
или отмечает строку расшифровкой в режиме правки, через `PATCH …/statement lines` (детали — в «Что уходит из
|
||||||
|
ядра»).
|
||||||
|
- **Р-14** (экран; G-15) — **решено владельцем 17.10.2026:** раскладка по коду «как на бумаге» остаётся (просьба
|
||||||
|
владельца 09.10.2026); строки со статусами ставятся под ближайшей предыдущей размещённой строкой по `row_index`,
|
||||||
|
расшифровки под родителем и скрыты по умолчанию; переключатель «как в файле» — не в этой работе.
|
||||||
|
- **Р-15** (ядро; V-7, G-24) — **решено владельцем 17.10.2026:** вторая форма того же типа и периода у одного ЮЛ из
|
||||||
|
того же файла не автоподтверждается; подтверждение второй при подтверждённой первой — 409 со ссылкой на первую;
|
||||||
|
склейка половинок формы из одного файла не предоставляется (файл перезагружают), вопрос — в `07-open-questions.md`.
|
||||||
|
От ИС Сканирование такой случай не ожидается (владелец), основной источник — Excel с листом-продолжением другого
|
||||||
|
типа или двойной печатью.
|
||||||
|
|
||||||
## Decisions made without discussion
|
## Decisions made without discussion
|
||||||
|
|
||||||
Решения агента 17.10.2026, принятые при оформлении целевой картины без отдельного обсуждения с владельцем; все
|
Решения агента 17.10.2026, принятые при оформлении целевой картины без отдельного обсуждения с владельцем; все
|
||||||
обратимы правкой спеки или словаря. Пункты 9–13 добавлены при внесении находок ревью F-1…F-25.
|
обратимы правкой спеки или словаря. Пункты 9–13 добавлены при внесении F-1…F-25, 14–19 — при внесении V-1…V-13 и
|
||||||
|
G-1…G-30.
|
||||||
|
|
||||||
1. **Уровни предупреждений** (шаг 8, п. 2): `error` — то, что меняет данные или их полноту (нет строки заголовка,
|
1. **Уровни предупреждений** (шаг 8, п. 2): `error` — только то, что меняет данные или их полноту; полный список —
|
||||||
колонка без даты, тип по кодам противоречит заголовку, колонки сегмента не совпали, потерянный json или лист,
|
в шаге 8 п. 2. Цена ошибки: лишний или пропущенный `needs_check`, правится таблицей уровней в спеке.
|
||||||
таблица без колонки кодов, ИНН не распознан, период не определён); `info` — остальное (пропущена подпись из
|
2. **`DISCLOSURE` всегда `done`** без условий о раскладке, строках и проверках; по `type_hint` шаги 2–7 не
|
||||||
словаря исключений, дробные при «тыс. руб.», xlsb без форматов, ИНН неуверенно, единицы не найдены). Цена ошибки:
|
выполняются, по кодам (3xxx–6xxx) строки отбрасываются (F-22, G-19) — сохранено текущее поведение парсера. Цена:
|
||||||
лишний или пропущенный `needs_check`, правится таблицей уровней в спеке.
|
пояснения не проверяются; так и сейчас.
|
||||||
2. **`DISCLOSURE` всегда `done`** без условий о раскладке, строках и проверках; шаги 2–7 для него не выполняются,
|
3. **Столбец кодов** — подпись «Код» принимается при доле кодов под ней ≥ 0,6 или кодах ≥ 3, при нескольких
|
||||||
5xxx в правиле большинства → `DISCLOSURE` (F-22) — сохранено текущее поведение парсера. Цена: пояснения не
|
кандидатах — больше кодов (V-1, G-5); запасной подсчёт «кодов ≥ 3 и доля ≥ 0,6» взят у парсера; сегмент не первый с
|
||||||
проверяются; так и сейчас.
|
кодами < 3 наследует столбец предыдущего при кодах в диапазоне формы (V-4, G-8). Цена: таблица без подписи и с
|
||||||
3. **Столбец кодов** — подпись «Код» принимается только с кодом под ней (F-5), запасной подсчёт «кодов ≥ 3 и доля
|
одним-двумя кодами в первом сегменте → документ без строк с `needs_check`.
|
||||||
среди непустых ≥ порога» взят у парсера; `relaxed` для одиноких продолжений снят, потому что продолжения
|
|
||||||
склеиваются в шаге 1. Цена: таблица без подписи и с одним-двумя кодами → документ без строк с `needs_check`.
|
|
||||||
4. **Начальный словарь исключений подписей** («Изменение», «Отклонение», «%», «Темп», «Доля», «Примечание») — по тестам
|
4. **Начальный словарь исключений подписей** («Изменение», «Отклонение», «%», «Темп», «Доля», «Примечание») — по тестам
|
||||||
парсера; пополняется словарём, не кодом.
|
парсера; пополняется словарём, не кодом.
|
||||||
5. **Грамматика `locator`** — пары `ключ:значение` через `;`, ключи по источнику, как сейчас (`file:` у OCR, F-23).
|
5. **Грамматика `locator`** — пары `ключ:значение` через `;`, ключи по источнику, как сейчас (`file:` у OCR, F-23).
|
||||||
@ -745,18 +819,32 @@ line_name, line_name_confidence, parent_ref, values}`: `line_code` — разо
|
|||||||
`is_recognized = false` (F-20). Цена: минимальная.
|
`is_recognized = false` (F-20). Цена: минимальная.
|
||||||
7. **`checks` отсутствует у документа без строк**, обязателен у `BALANCE` / `INCOME` со строками.
|
7. **`checks` отсутствует у документа без строк**, обязателен у `BALANCE` / `INCOME` со строками.
|
||||||
8. **Рабочие имена** `FormCandidate`, `TableLayout`, `RawLine`, `needs_review`, `line_status`, `line_code_text`,
|
8. **Рабочие имена** `FormCandidate`, `TableLayout`, `RawLine`, `needs_review`, `line_status`, `line_code_text`,
|
||||||
`row_index`, `source_ref`, `parent_ref`, `period_kind`, `document_issue`, `statements_version`, `arm-statements`,
|
`row_index`, `source_ref`, `parent_ref`, `period_kind`, `form_variant`, `document_issue`, `statements_version`,
|
||||||
`sync-statements` — по образцу существующих (`sync-web.sh`); меняются при написании спеки без последствий.
|
`LINES_UNRESOLVED`, `arm-statements`, `sync-statements` — по образцу существующих (`sync-web.sh`); меняются при
|
||||||
|
написании спеки без последствий.
|
||||||
9. **Правило `detail`** (шаг 3): строка без кода с числами под строкой с кодом в том же сегменте; родитель — ближайшая
|
9. **Правило `detail`** (шаг 3): строка без кода с числами под строкой с кодом в том же сегменте; родитель — ближайшая
|
||||||
строка с кодом выше. Цена: строка формы с потерянным кодом под другой строкой скрывается до нарушения §7 у
|
строка с кодом выше. Цена: строка формы с потерянным кодом под другой строкой скрывается до нарушения §7 у
|
||||||
родителя; лечится назначением кода (F-14).
|
родителя (цель отсутствует — нарушение, G-2); лечится назначением кода (Р-13).
|
||||||
10. **Конвенция знака** для членов `abs_terms` (F-1): форма записи у большинства членов `abs_terms` колонки с
|
10. **Конвенция знака** для членов `abs_terms` (F-1): форма записи у большинства членов `abs_terms` колонки с непустым
|
||||||
`is_recognized = true`; расхождение или отсутствие образца — `needs_review`. Цена: ОФР, где расходы записаны
|
`amount` и `is_recognized = true`; проверяется только у ячеек, нуждающихся в покрытии (V-13). Цена: ОФР, где
|
||||||
вперемешку скобками и минусом, подсвечивает расходы; это и нужно.
|
сомнительные расходы записаны не так, как остальные, подсвечивает их; это и нужно.
|
||||||
11. **Хранить `needs_review`, нарушения и предупреждения** в ядре (`financial_statement_value.needs_review`,
|
11. **Хранить `needs_review`, `text`, нарушения и предупреждения** в ядре (`financial_statement_value.needs_review`,
|
||||||
`document_issue`) вместо пересчёта на каждом GET (F-8) — пересматривает решение v5.13 «считается, не хранится».
|
`source_text`, `financial_statement_line.needs_review`, `document_issue`) вместо пересчёта на каждом GET (F-8, V-3,
|
||||||
Цена: ещё одна таблица и миграция; взамен ядро при чтении ничего не считает.
|
V-5) — пересматривает решение v5.13 «считается, не хранится». Цена: ещё одна таблица и миграция; взамен ядро при
|
||||||
|
чтении ничего не считает.
|
||||||
12. **Эффективная дата документа** = дата отчёта, иначе самая поздняя абсолютная дата колонок (F-6) — та же формула,
|
12. **Эффективная дата документа** = дата отчёта, иначе самая поздняя абсолютная дата колонок (F-6) — та же формула,
|
||||||
что `statement_period` ядра; от неё считаются относительные подписи и выбирается редакция перечня кодов.
|
что `statement_period` ядра; от неё считаются относительные подписи и выбирается редакция перечня кодов.
|
||||||
13. **Предел и квантование суммы** в общем `parse_amount` — 16 целых цифр, 2 знака, half-up, как `quantize_money`
|
13. **Предел и квантование суммы** в общем `parse_amount` — 16 целых цифр, 2 знака, half-up, как `quantize_money`
|
||||||
парсера (F-20); «догадка из цифр» длиннее 16 — `null`.
|
парсера (F-20); «догадка из цифр» длиннее 16 — `null`.
|
||||||
|
14. **Цель соотношения отсутствует при непустых членах — жёсткое нарушение** (G-2); сегодня пропуск у обоих сервисов и
|
||||||
|
ядра. Цена: упрощённые формы без подытогов — только с упрощённым набором соотношений (G-17), иначе всегда
|
||||||
|
`needs_check`, как и сегодня.
|
||||||
|
15. **Упрощённая форма** определяется по ОКУД 0710096 или по структуре (есть 1600, нет 1100 и 1200) и получает свой
|
||||||
|
набор соотношений и перечень кодов (G-17). Цена: ошибка определения → не те соотношения → `needs_check`.
|
||||||
|
16. **Грамматика `ref`** (G-9): OCR `row:<row_index>[#<k>]`, Excel `<лист>!<A1>`; уникален в документе.
|
||||||
|
17. **`parse_amount` знает источник** (G-16): запятая у Excel — десятичный разделитель, у OCR — тысячи при группе из
|
||||||
|
трёх цифр. Единственное место, где шаги 2–8 смотрят на `source`.
|
||||||
|
18. **`document_issue`**: очищается при перезагрузке файла, записи источника `core` заменяются при каждом пересчёте,
|
||||||
|
`line_id` — список (G-25).
|
||||||
|
19. **Границы пакета `arm_statements`** (G-14): свои типы входа и выхода, конвертация в модель контракта на стороне
|
||||||
|
хоста, golden внутри пакета на синтетике, тесты на реальных фикстурах — только в сервисах.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user