OQ-07, OQ-173: сопоставление ВГО — упаковка 1:1 / 1:N / N:1 и каскад по остатку (#136) #137

Merged
aep merged 3 commits from feat/136-oq07-group-matching into main 2026-10-19 13:39:50 +03:00
4 changed files with 52 additions and 9 deletions
Showing only changes of commit 68e944166b - Show all commits

View File

@ -1,6 +1,6 @@
# Элиминация ВГО: правила, расчёт, корректировки
Документ для аналитика, 19.10.2026 (RSHB/arm-ka-docs#132; сопоставление — по решениям OQ-07 и OQ-169, #136). Основа — модель данных
Документ для аналитика, 19.10.2026 (RSHB/arm-ka-docs#132; сопоставление — по решениям OQ-07 и OQ-173, #136). Основа — модель данных
[`model/data-model-v5.19.dbml`](../model/data-model-v5.19.dbml) и правила предметной области
[`context/05-domain-rules.md`](../context/05-domain-rules.md) §3–§6, §8. Источник истины — они: при расхождении прав
DBML и `05-domain-rules.md`, а этот документ поправь следом. Где написано наше прочтение ТЗ, а не прямая цитата, это
@ -133,7 +133,7 @@ DBML и `05-domain-rules.md`, а этот документ поправь сле
Единица расчёта — **тройка** (правило, компания A, компания B). Правила считаются для каждой упорядоченной пары
компаний — и (A, B), и (B, A), поэтому зеркальные правила № 9–16 Прил. 3 отдельно не заводятся (№ 14 для (A, B) — это
№ 6 для (B, A)). У тройки два уровня (решения владельца 19.10.2026, OQ-07 и OQ-169):
№ 6 для (B, A)). У тройки два уровня (решения владельца 19.10.2026, OQ-07 и OQ-173):
**Сумма элиминации** — агрегат, идёт в КФО. Считается **каскадом по остатку**: правила пары (A, B) идут по порядку
`rule_no`, каждое берёт min из того, что осталось после предыдущих правил:
@ -325,4 +325,4 @@ vgo_amount = eliminated_auto + eliminated_manual
| В-9 | 76.АВ / 76.ВА (НДС по авансам) — исключать из ДЗ / КЗ? Есть ли ещё такие субсчета 76? | OQ-164 открыт, вопрос в AURORA-1373 |
| В-10 | Округление при элиминации: рубли → тыс. руб. на выходе, ROUND_HALF_UP? | допущение OQ-26 |
| В-11 | Правило № 1: если у компании А нет ОСВ по 03 — результат в строку «ППА»? | открыто в §4 |
| В-12 | ~~Счета правил пересекаются — как избежать двойной элиминации?~~ | **решено 19.10.2026 (OQ-169):** каскад по остатку в порядке `rule_no` (раздел 3.2) |
| В-12 | ~~Счета правил пересекаются — как избежать двойной элиминации?~~ | **решено 19.10.2026 (OQ-173):** каскад по остатку в порядке `rule_no` (раздел 3.2) |

View File

@ -82,6 +82,25 @@ r11 Договоры ←
такой элемент не попадает, у группировки-владельца `has_inn = true`; элемент — подсказка для строк ИНН (§1.3, OQ-163):
задаёт направление и снимает проверку контрольной суммы ИНН. Строки ИНН склеиваются с наименованием по §1.3.
**Плоская форма 1С по одному счёту (реализовано, tabular-parser #59, AURORA-1661, решения владельца 16.10 и 19.10.2026).**
Пример — выгрузка «Оборотно-сальдовая ведомость по счету: 76.Н.1»:
```
r2 Оборотно-сальдовая ведомость по счету: 76.Н.1 ← код счёта — и через двоеточие
r3 Контрагенты ← строка вида субконто (у файла-письма — «Организации»)
r4 за 2025 г.
r5 ООО "Товарное хозяйство" ← организация (или хвост «План счетов <организация>»)
r9 Субконто | Сальдо на начало периода | Обороты за период | Сальдо на конец периода
r11 Август - Агро ООО | … ← сразу контрагенты, строки-счёта нет
```
- Код счёта после «по счету» читается и через двоеточие («по счету: 76.Н.1»). **Счёт шапки важнее счёта строки
таблицы:** фолбэк «код первой строки-счёта» (выше, OQ-134) работает, только если в шапке кода нет.
- Строка шапки **между названием ведомости и периодом** — строка вида субконто, а не организация. Её элементы
(через запятую) — виды субконто: известный вид (`osv_groups.yaml`, явная запись `mapping`: «Контрагенты», «Организации» —
синоним `COUNTERPARTY`, «Договоры» …) становится группировкой схемы после `ACCOUNT`, если схема варианта 1 — одна
`ACCOUNT` («Субконто»); неизвестный вид («Материалы») в схему не попадает.
- Организация — по порядку: хвост «План счетов <организация>» (`header_markers.chart_of_accounts`) → первая не-метка
шапки вне строки вида субконто → хвост названия ведомости.
Нормализация значений шапки в типы:
| Текст в шапке | `group_type` |
@ -204,6 +223,22 @@ AURORA-1661; прикладной сценарий валютных строк
дерево строится по номеру родителя, строка с несуществующим родителем сохраняется с `parent_entry_id = NULL` и даёт
предупреждение маппинга (документ `NEEDS_CHECK`). Суммы и `data_type` (включая `NU`) пишутся как в результате.
**Плоский список под счётом шапки (реализовано, tabular-parser #59, AURORA-1661).** Если в шапке есть код счёта, а в теле
нет ни одной строки-счёта (п. 1), каскад иерархии (§1.0 п. 5) после `outline`, `indent`, `account_pattern` и до `sums`
пробует стратегию `header_account`: все строки таблицы — уровень 0, предупреждение `OSV_HIERARCHY_BY_HEADER_ACCOUNT`
(«плоский список под счётом шапки»). Условие — определение плоской формы 1С; при наличии outline или отступов побеждают
они (счёт шапки при этом всё равно наследуют строки). **Счёт шапки — счёт корня ветки:** строки до первой строки-счёта
(и все строки плоского списка) получают `account_code_raw` шапки, а не `null`.
**Контрагенты расчётных счетов без названия вида (решение владельца 16.10.2026; риск OQ-158).** Если схема группировок —
одна `ACCOUNT` (вид субконто в шапке не назван), строка на позиции 1 (первая не-счётная) под счётом из списка
`counterparty_accounts` (`osv_groups.yaml`, данные) — `COUNTERPARTY` без имени группировки, ИНН — по общим правилам (колонка,
строка ИНН, хвост названия). Список: 58, 60, 62, 66, 67, 75, 76, 80 (без 71 и 73 — подотчётные и персонал, R-9); запись
`76` покрывает `76`, `76.2`, `76.Н.1`, но не `760`. Глубже позиции 1 — `OTHER`; именованные группировки схемы («Учредители»,
«Работники организаций») правило не переопределяет. Предупреждение `OSV_ROWS` (по одному на различный код счёта):
«вид субконто в шапке не назван: строки аналитики счёта <код> приняты за контрагентов по плану счетов — проверьте».
Риск: физлица и подотчётные на расчётных счетах без названия вида попадут в «Осталось N» сопоставления (OQ-158).
Нормализация кода счёта (ядро, `domain/osv_mapping.py`): `60.1 → 60.01`, `60.01.1 → 60.01.1`, `76.7.1 → 76.07.1` —
числовой **второй** сегмент (1–3 цифры) дополняется нулём слева до двух цифр, первый и третий сегменты и буквенные
(`76.АВ`, `МЦ.04`) остаются как в файле. `account_code` строки хранится как в результате (`account_code_raw`),
@ -249,8 +284,12 @@ AURORA-1661; прикладной сценарий валютных строк
- Строка «Итого» (TOTAL) == корневая строка ACCOUNT (первая строка уровня 0); если корней уровня 0 несколько (лист без
строки корневого счёта: верхний уровень — субсчета 58.01, 58.03, OQ-133, PR #13) — «Итого» == **сумме строк уровня 0**
(показатели без суммы и сама «Итого» не входят; сальдо — по колонкам либо по нетто, если «Итого» свёрнуто, как у родителя
выше, R-21); при одном корне сверка прежняя. Вторая итоговая строка сразу за «Итого» («Итого развернутое») — тоже `TOTAL`,
в сверку не входит; строка сумм без подписи в конце таблицы принимается за «Итого» с предупреждением.
выше, R-21); при одном корне сверка прежняя. Плоский список под счётом шапки (§1.3, `header_account`) — тоже случай
«корня ACCOUNT нет»: «Итого» == сумме строк уровня 0 (прежняя сверка пустой/нулевой «Итого» — только когда строк уровня 0
нет). Вторая итоговая строка («Итого развернутое») — тоже `TOTAL`, **в любом порядке относительно «Итого»**: и сразу за ней,
и **перед** ней (плоская форма 1С, tabular-parser #59); в сверку «Итого = корень» не входит; тождество строки на ней не проверяется, только если обороты пусты и на листе есть другая
«Итого» (перед «Итого» в 1С — без оборотов, тождество не проверяется; после «Итого» — с оборотами, проверяется как обычная строка;
tabular-parser #59, R-16, R-19) — сверяется основная «Итого» (если на листе только «Итого развернутое», она единственная итоговая и сверяется как «Итого»); строка сумм без подписи в конце таблицы принимается за «Итого» с предупреждением.
**Таблица кончается на первой «Итого»** (на корневом уровне оформления; плюс «Итого развернутое» сразу за ней): всё
ниже (подпись «Ответственный:», блоки, дописанные аналитиком, в том числе строки, начинающиеся с «Итого») — вне
документа, в `rows` не попадает. Сначала ищется первая строка корневого уровня с текстом ровно «Итого»
@ -482,7 +521,7 @@ closing_* = closing_* строки Qn
---
## §5. Автоматическое сопоставление ВГО (DOC-223044; OQ-07, OQ-169 — решения владельца 19.10.2026)
## §5. Автоматическое сопоставление ВГО (DOC-223044; OQ-07, OQ-173 — решения владельца 19.10.2026)
Границы: только `closing_debit` / `closing_credit`; только компании периметра версии; правила и счета сторон — из §4
(версия правил — `consolidation_version.elimination_rules_version`); строки `group_type = COUNTERPARTY`,
@ -493,7 +532,7 @@ closing_* = closing_* строки Qn
Два уровня: **сумма элиминации** по тройке — агрегат, идёт в КФО; **упаковка** — какие строки ОСВ с какими сопоставлены
(детализация, строки без пары). Способ упаковки сумму элиминации не меняет (решение владельца 19.10.2026).
**1. Сумма элиминации по тройке — каскадом по остатку** (AURORA-1536; OQ-169). Счета правил пересекаются (дебет
**1. Сумма элиминации по тройке — каскадом по остатку** (AURORA-1536; OQ-173). Счета правил пересекаются (дебет
№ 5–8 общий; кредит 67.03 — № 3 и маска 67 у № 7, 66.03 — № 4 и маска 66 у № 8), и по буквальным формулам Прил. 3 одна
и та же сумма элиминировалась бы несколькими правилами. Поэтому правила пары (A, B) считаются **по порядку `rule_no`**, и
каждое берёт min из **остатка** сторон после предыдущих — тот же принцип, что Прил. 3 задаёт в правиле № 1 («сначала из

View File

@ -174,4 +174,8 @@
| OQ-166 | Упрощённая форма ББ/ОФР (приказ 66н, прил. 5): признак — по структуре кодов (ББ: есть 1600, нет 1100 и 1200; ОФР: есть 2400 или 2110, нет 2100, 2200, 2300), ОКУД 0710096 признаком не служит. Набор соотношений §7 и перечень кодов упрощённой формы заданы по бланку (`05-domain-rules.md` §7, решение S-5 спеки; ББ — жёсткие, ОФР `2400 = 2110 − 2120 − 2330 + 2340 − 2350 − 2410` — мягкое, как `2400` полного набора, решение владельца 18.10.2026, Р-22; перечни — `contracts/statement-code-orders.yaml`, у `fsbu4` — как у 66н) без образца в `fixtures/` | change `unified-statement-normalization`, design (Decisions № 14) | `arm_statements` `statement_checks.yaml`, перечень кодов, `form_variant` | открыт: допущение — формулы §7 «Упрощённый набор»; нужен реальный образец упрощённой отчётности (Excel и скан) в `fixtures/` для проверки признака и формул |
| OQ-167 | Несколько отчётных периодов в одном файле, отданном в ИС Сканирование (например, ББ за два квартала в одном PDF): шапка в json ИС одна на json и копируется во все его таблицы | change `unified-statement-normalization`, design шаг 1 (допущение владельца 17.10.2026) | scan-gateway (адаптер шага 1), период документа | допущение владельца: ИС отдаёт разные периоды разными json; образца нет — проверить на первом таком файле, при расхождении — шапка из json неверна для части таблиц |
| OQ-168 | Коды упорядоченного перечня форм без образца в `fixtures/` (`contracts/statement-code-orders.yaml`, §11.9): в перечень вошли только коды шаблонов банка и коды печатных форм 1С и сканов, найденные в `fixtures/` (1105, 1130, 1140, 1215, 1231, 1530, 2420, 2460, 2530, 2930, повторы 1220 / 1520 / 2410 у `fsbu4`, раздел III НКО у `66n`). Не подтверждены образцом: 1120 в `fsbu4`; 1105, 1215, 2420 в `66n`; 2421, 2430, 2450 (прежние строки ОФР 66н); 2930 и раздел III НКО в `fsbu4`; клиентский 1231 в `fsbu4`; упрощённые перечни обеих редакций (OQ-166). Добавлять ли их без образца | change `unified-statement-normalization`, ревью спеки K-5 (18.10.2026) | `arm_statements` (перечень кодов, структурное подтверждение неуверенного кода), ядро (`CODE_NOT_IN_EDITION`) | открыт до появления образцов; **временное правило принято владельцем 18.10.2026 (Р-26):** коды без образца (2421 / 2430 / 2450 в `66n`, 1120 и другие в `fsbu4`) в перечень не входят; неуверенный такой код на скане не подтверждается структурой — `needs_review` строки, `needs_check`; у Excel и уверенного кода — только `info` ядра `CODE_NOT_IN_EDITION`. Пополнение — строкой в yaml с источником (файл fixtures), когда образец появится |
| OQ-169 | Пересечение правил в сумме элиминации: счета сторон правил §4 пересекаются — дебет № 5–8 (76 у № 5; 60/62/76 у № 6–8), кредит: 67.03 (№ 3 и маска 67 у № 7), 66.03 (№ 4 и маска 66 у № 8), 66.04/67.04 (№ 5 и № 7/8). Строка такого счёта входит в Σ стороны каждого правила, и сумма min(ΣA, ΣB) по правилам может превысить Σ самой стороны — двойная элиминация. Считать ΣA правила только по строкам, которые упаковка (§5 п. 2) отнесла к этому правилу? Или распределять сторону по правилам в фиксированном порядке? | решение по OQ-07 (docs #136, 19.10.2026), описание AURORA-1536 | §5 п. 1, AURORA-1536, КФО (§8 `eliminated_auto`) | **закрыт решением владельца 19.10.2026 (docs #136):** каскад по остатку — правила пары (A, B) считаются по порядку `rule_no` (в Прил. 3 он уже от частного к общему: № 3/4 раньше № 7/8, № 5 раньше № 6, № 6 → 7 → 8), каждое берёт min из остатка сторон после предыдущих, как в правиле № 1 Прил. 3 («сначала из 1450 до обнуления…»); упаковка — в том же порядке, строка сопоставляется одним правилом; ничьей между правилами нет. Поле порядка не нужно. Алгоритм — `05-domain-rules.md` §5 |
| OQ-169 | Группировка «Учредители» на счёте 80 в плоской форме ОСВ 1С (O-29 прогона контура, файл `02c5df51`): правило расчётных счетов (§1.3) её не переопределяет — строки остаются `OTHER` с именем группировки, в сопоставление контрагентов не попадают. Нужно ли делать учредителей `COUNTERPARTY` (гудвилл §8 п. 2 / §4 п. 2 берёт участие в капитале по контрагенту-учредителю) или оставить ручное сопоставление | tabular-parser #59, change `osv-flat-form` (R-8, решение сессии при одобрении запуска владельцем 19.10.2026) | §1.3, §4 п. 2 (гудвилл), сопоставление контрагентов | открыт; допущение принято: именованная группировка «Учредители» остаётся `OTHER` (R-8) |
| OQ-170 | Состав `counterparty_accounts` (контрагенты расчётных счетов без названия вида, §1.3): исключены 71 «Расчёты с подотчётными лицами» и 73 «Расчёты с персоналом» (физлица, OQ-158), список — 58, 60, 62, 66, 67, 75, 76, 80. Верно ли исключение; нужны ли 68, 69, 70, 77 и др. | tabular-parser #59, change `osv-flat-form` (R-9, решение сессии при одобрении запуска 19.10.2026) | §1.3, сопоставление контрагентов (OQ-158) | открыт; допущение принято: 71/73 исключены (аналитика без названия вида на них остаётся `OTHER`, в сопоставление не попадает) |
| OQ-171 | Вид субконто «Организации» в строке между названием ведомости и периодом (файл-письмо «ОСВ сч. 60 за 2 кв. 2026», план счетов ООО Хреновская Нива): что это за вид в 1С — организации-контрагенты (справочник «Организации» или «Контрагенты» под другим названием) или собственные организации учёта; на каких счетах встречается | tabular-parser #59, change `osv-flat-form` (R-10, решение сессии при одобрении запуска 19.10.2026) | §1.1 (синоним в `osv_groups.yaml`), сопоставление контрагентов | открыт; допущение принято: синоним `COUNTERPARTY` — только множественное число «Организации» (`^организации\b`, R-17); единственное «Организация» (собственная организация учёта в шапке) остаётся `OTHER`; другой смысл «Организации» на иных счетах даст `COUNTERPARTY` вместо `OTHER`, виден по предупреждениям |
| OQ-172 | Безымянная аналитика первого уровня на счёте **63** (резервы по сомнительным долгам) в плоской форме без названия вида: на файлах контура таких строк нет; 51 строка — в fixtures, golden `osv-dkb/nm/2025-Q3/НМ_ОСВ_63_Контрагенты_…json` (имя файла «63_Контрагенты» — довод за включение 63); счёт 63 не входит в `counterparty_accounts` (§1.3), строки остаются `OTHER` без имени. Нужен ли 63 в списке (аналитика резерва — контрагенты-должники) | tabular-parser #59, приёмка Task 6 (19.10.2026) | §1.3, сопоставление контрагентов | открыт; допущение принято: 63 в список не входит, строки `OTHER` |
| OQ-173 | Пересечение правил в сумме элиминации: счета сторон правил §4 пересекаются — дебет № 5–8 (76 у № 5; 60/62/76 у № 6–8), кредит: 67.03 (№ 3 и маска 67 у № 7), 66.03 (№ 4 и маска 66 у № 8), 66.04/67.04 (№ 5 и № 7/8). Строка такого счёта входит в Σ стороны каждого правила, и сумма min(ΣA, ΣB) по правилам может превысить Σ самой стороны — двойная элиминация. Считать ΣA правила только по строкам, которые упаковка (§5 п. 2) отнесла к этому правилу? Или распределять сторону по правилам в фиксированном порядке? | решение по OQ-07 (docs #136, 19.10.2026), описание AURORA-1536 | §5 п. 1, AURORA-1536, КФО (§8 `eliminated_auto`) | **закрыт решением владельца 19.10.2026 (docs #136):** каскад по остатку — правила пары (A, B) считаются по порядку `rule_no` (в Прил. 3 он уже от частного к общему: № 3/4 раньше № 7/8, № 5 раньше № 6, № 6 → 7 → 8), каждое берёт min из остатка сторон после предыдущих, как в правиле № 1 Прил. 3 («сначала из 1450 до обнуления…»); упаковка — в том же порядке, строка сопоставляется одним правилом; ничьей между правилами нет. Поле порядка не нужно. Алгоритм — `05-domain-rules.md` §5 |

View File

@ -60,7 +60,7 @@
| Файл | Что | Опирается на |
|---|---|---|
| `analyst/eliminations-vgo.md` | элиминация ВГО: что хранится и что считается (DECISION-5), раскладка правил `elimination_rule` / `elimination_rule_account` (Прил. 3/4), сопоставление пар, шахматка, путь до КФО, корректировки, гудвилл; пример на фикстурах РБ Q3 2024; вопросы В-1…В-12 (19.10.2026, #132; сопоставление по OQ-07 и OQ-169 — #136) | DBML v5.19; §3–§6, §8 правил; DOC-223044, DOC-221277, DOC-227278, DOC-259025 |
| `analyst/eliminations-vgo.md` | элиминация ВГО: что хранится и что считается (DECISION-5), раскладка правил `elimination_rule` / `elimination_rule_account` (Прил. 3/4), сопоставление пар, шахматка, путь до КФО, корректировки, гудвилл; пример на фикстурах РБ Q3 2024; вопросы В-1…В-12 (19.10.2026, #132; сопоставление по OQ-07 и OQ-173 — #136) | DBML v5.19; §3–§6, §8 правил; DOC-223044, DOC-221277, DOC-227278, DOC-259025 |
## Макет UI