05 §1.3–1.4: периоды, нетто-ребёнок, иерархия по отступам (AURORA-1823, 1863, 1858) #178

Merged
aep merged 1 commits from feat/177-osv-false-check-rules into main 2026-10-28 10:07:52 +03:00

View File

@ -299,6 +299,33 @@ AURORA-1661; прикладной сценарий валютных строк
на ней, а не на первой «ИТОГ по …» (та не начинается с «Итого» и итогом листа не является). Сверка «Итого = Σ строк на ней, а не на первой «ИТОГ по …» (та не начинается с «Итого» и итогом листа не является). Сверка «Итого = Σ строк
уровня 0» (§1.4) идёт по субсчетам. уровня 0» (§1.4) идёт по субсчетам.
**Иерархия по отступам: подуровень счёта, строка закрытия, вложенная аналитика** (AURORA-1858, tabular-parser #78).
ОСВ 1С без outline («Субконто», вид аналитики в шапке не назван) задаёт уровни только отступом: 0 у счетов, 1 у
аналитики. Отступов не хватает в трёх случаях, стратегия `indent` их уточняет:
1. **Подуровень счёта на том же отступе.** `68.01.1` стоит на одном отступе с `68.01`. Счёт, код которого продолжает код
более раннего счёта того же отступа в том же блоке (`68.01.1` → `68.01`), становится его ребёнком, вместе со своей
аналитикой. Только если все такие пары листа на одном отступе, раскладка однозначна и сумма детей перекладываемого
родителя после перекладки сходится; иначе лист остаётся по отступам (защищает плоские списки `60`, `60.01`, `60.02`).
2. **Безымянная строка закрытия.** Строка без текста и кода сразу за строкой счёта, с оборотами, равными оборотам счёта
(1С печатает её под 90.09 и 91.09), — ребёнок счёта. Пустую ячейку отступа читатель не хранит, поэтому без правила
строка попадала на уровень 0 и удваивала сумму корней.
3. **Вложенность аналитики по суммам** (решение владельца 28.10.2026 после ревью). Если аналитика родителя на одном
отступе, а вложенность видна только по суммам (91.01: «Прочие внереализационные» = две строки ниже), то **только у
родителя с нарушением «сумма детей = родитель»** строка, равная сумме следующих соседей, жадно становится их
родителем (как стратегия `sums`). Перестройка принимается, только если после неё родитель сходится, иначе — откат.
Защиты — правило применяется, только если одновременно:
- в схеме шапки после счёта нет названных видов аналитики («Субконто» без вида), то есть вложение не меняет
`group_type` и имя группы строки (иначе контрагент мог бы стать «договором» соседа);
- все дети родителя стоят на одном отступе (если отступ различает уровни, ему верим);
- ни вкладываемая строка, ни её новый родитель не несут ИНН.
Вложение меняет дерево в результате (`level`, `parent_row_number`), а не только проверку. Родитель без нарушения не
перестраивается; равные суммы соседей (`50 + 50 = 100`) сами по себе вложенности не дают. Остаточный риск:
- RR-6 — на листе «Субконто» без вида дубль строки аналитики с той же суммой, что у соседа выше, неотличим от
«группа → единственная статья» (на 91.01: «Проценты к получению» = «по векселям») и принимается за вложенность:
ошибка «лишняя строка» в такой форме не ловится.
Правила 1 и 3 опираются на сходимость, но это не «подгонка» (§1.0 п. 7): дерево меняется только в узких случаях, где
структура подтверждается и кодом/формой выгрузки, и суммами; реальное расхождение в остальных случаях даёт `needs_check`.
Нормализация кода счёта (ядро, `domain/osv_mapping.py`): `60.1 → 60.01`, `60.01.1 → 60.01.1`, `76.7.1 → 76.07.1` — Нормализация кода счёта (ядро, `domain/osv_mapping.py`): `60.1 → 60.01`, `60.01.1 → 60.01.1`, `76.7.1 → 76.07.1` —
числовой **второй** сегмент (1–3 цифры) дополняется нулём слева до двух цифр, первый и третий сегменты и буквенные числовой **второй** сегмент (1–3 цифры) дополняется нулём слева до двух цифр, первый и третий сегменты и буквенные
(`76.АВ`, `МЦ.04`) остаются как в файле. `account_code` строки хранится как в результате (`account_code_raw`), (`76.АВ`, `МЦ.04`) остаются как в файле. `account_code` строки хранится как в результате (`account_code_raw`),
@ -341,6 +368,30 @@ AURORA-1661; прикладной сценарий валютных строк
значения родителя и выше не затронуты); значения родителя и выше не затронуты);
- родитель (0, 0) при детях (X Дт, X Кт) принимается (нетто 0) — осознанное поведение, закреплено тестом; - родитель (0, 0) при детях (X Дт, X Кт) принимается (нетто 0) — осознанное поведение, закреплено тестом;
- вне R-21: текст вида «12.ООО» принимается за счёт (§1.3, R-16). - вне R-21: текст вида «12.ООО» принимается за счёт (§1.3, R-16).
**Развёрнутый родитель над свёрнутым субсчётом** (AURORA-1863, решение владельца 28.10.2026, tabular-parser #78).
1С считает развёрнутое сальдо счёта по нижнему уровню аналитики, а субсчёт может показать свёрнутым: счёт 76 —
47,1 млн Дт / 12,9 млн Кт, его субсчёт 76.07 — 8,2 млн Кт при аналитике 4,6 млн Дт / 12,8 млн Кт. По колонкам счёт
с субсчетами не сходится, по нетто сходится до копейки. Сальдо развёрнутого родителя сверяется по нетто, **только если
среди его прямых детей есть «нетто-ребёнок»** — ребёнок, который сам сошёлся со своими детьми лишь по нетто (по
колонкам — нет). Признак идёт вверх только по непрерывной цепочке таких строк: строка, сошедшаяся по колонкам, его не
передаёт, так что слепая зона не растёт до корня. Обороты — по колонкам всегда. Развёрнутый родитель без
нетто-ребёнка сверяется по колонкам, как раньше: «нетто всегда» (широкий вариант) отклонён — он прячет любую ошибку,
сохраняющую нетто. Так в банке первичных документов остаются `needs_check` 14 файлов одной организации, где счёт 68
(и 71) расходится с субсчетами на одинаковую сумму по Дт и Кт (0,05; 137,00; 5 939,76) без свёрнутого ребёнка:
отличить схлопнутую пару от потерянной по данным нельзя. Остаточный риск:
- RR-5 — у развёрнутого родителя с нетто-ребёнком колонки сальдо не информативны: ошибка, сохраняющая нетто родителя
(одинаковый сдвиг Дт и Кт, перестановка сторон), не ловится, как у свёрнутого родителя (RR-1).
**Строки разреза «Период»** (AURORA-1823, tabular-parser #78). В выгрузке 1С с группировкой «Период» в шапке под
контрагентом идут строки «Обороты за <месяц> <год>» (или «… <N> кв. <год>»), у каждой своё сальдо на начало и конец
периода; сальдо конца периода — сальдо начала следующего. Группировка распознаётся по имени в схеме шапки (словарь
`period_groups` в `osv_groups.yaml`; тип группы остаётся `OTHER`, контракт не меняется). Если **все** дети родителя —
строки-периоды, родитель сверяется цепочкой: обороты = Σ оборотов периодов (по колонкам), сальдо начала = сальдо начала
**первого** периода, сальдо конца = сальдо конца **последнего** (в порядке строк; по колонкам либо по нетто, как выше),
и цепочка непрерывна: сальдо конца каждого периода = сальдо начала следующего (пропущенные месяцы разрыва не дают —
1С не печатает месяц без движения, сальдо переходит дальше). Сумма сальдо периодов не считается. У родителя со смешанными детьми (периоды и обычные строки) суммы считаются как
прежде.
- Строка «Итого» (TOTAL) == корневая строка ACCOUNT (первая строка уровня 0); если корней уровня 0 несколько (лист без - Строка «Итого» (TOTAL) == корневая строка ACCOUNT (первая строка уровня 0); если корней уровня 0 несколько (лист без
строки корневого счёта: верхний уровень — субсчета 58.01, 58.03, OQ-133, PR #13) — «Итого» == **сумме строк уровня 0** строки корневого счёта: верхний уровень — субсчета 58.01, 58.03, OQ-133, PR #13) — «Итого» == **сумме строк уровня 0**
(показатели без суммы и сама «Итого» не входят; сальдо — по колонкам либо по нетто, если «Итого» свёрнуто, как у родителя (показатели без суммы и сама «Итого» не входят; сальдо — по колонкам либо по нетто, если «Итого» свёрнуто, как у родителя