Compare commits
1 Commits
main
...
feat/188-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecbe5297d8 |
2
Makefile
2
Makefile
@ -6,7 +6,7 @@ PY := $(if $(wildcard $(VENV)/bin/python),$(CURDIR)/$(VENV)/bin/python,python3)
|
|||||||
# Пусто — проверяются все change'и и спеки; `make validate-openspec CHANGE=<id>` — один change
|
# Пусто — проверяются все change'и и спеки; `make validate-openspec CHANGE=<id>` — один change
|
||||||
CHANGE ?=
|
CHANGE ?=
|
||||||
# Текущая версия модели данных — по ней рисуются диаграммы (make diagrams)
|
# Текущая версия модели данных — по ней рисуются диаграммы (make diagrams)
|
||||||
DBML ?= model/data-model-v5.24.dbml
|
DBML ?= model/data-model-v5.23.dbml
|
||||||
|
|
||||||
.PHONY: venv validate-contracts validate-openspec test-snapshot test-sync-statements sync-statements statements-check check diagrams regress
|
.PHONY: venv validate-contracts validate-openspec test-snapshot test-sync-statements sync-statements statements-check check diagrams regress
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# Элиминация ВГО: правила, расчёт, корректировки
|
# Элиминация ВГО: правила, расчёт, корректировки
|
||||||
|
|
||||||
Документ для аналитика, 19.10.2026 (RSHB/arm-ka-docs#132; сопоставление — по решениям OQ-07 и OQ-173, #136). Основа — модель данных
|
Документ для аналитика, 19.10.2026 (RSHB/arm-ka-docs#132; сопоставление — по решениям OQ-07 и OQ-173, #136). Основа — модель данных
|
||||||
[`model/data-model-v5.24.dbml`](../model/data-model-v5.24.dbml) и правила предметной области
|
[`model/data-model-v5.23.dbml`](../model/data-model-v5.23.dbml) и правила предметной области
|
||||||
[`context/05-domain-rules.md`](../context/05-domain-rules.md) §3–§6, §8. Источник истины — они: при расхождении прав
|
[`context/05-domain-rules.md`](../context/05-domain-rules.md) §3–§6, §8. Источник истины — они: при расхождении прав
|
||||||
DBML и `05-domain-rules.md`, а этот документ поправь следом. Где написано наше прочтение ТЗ, а не прямая цитата, это
|
DBML и `05-domain-rules.md`, а этот документ поправь следом. Где написано наше прочтение ТЗ, а не прямая цитата, это
|
||||||
помечено; такие места собраны в раздел 6 вопросами.
|
помечено; такие места собраны в раздел 6 вопросами.
|
||||||
@ -23,7 +23,6 @@ DBML и `05-domain-rules.md`, а этот документ поправь сле
|
|||||||
| `elimination_rule`, `elimination_rule_account` | правила элиминации Прил. 3 / Прил. 4 (раздел 2) |
|
| `elimination_rule`, `elimination_rule_account` | правила элиминации Прил. 3 / Прил. 4 (раздел 2) |
|
||||||
| `consolidation_version.elimination_rules_version` | по какой версии правил считалась версия консолидации |
|
| `consolidation_version.elimination_rules_version` | по какой версии правил считалась версия консолидации |
|
||||||
| `correction` | ручные корректировки аналитика (раздел 4) |
|
| `correction` | ручные корректировки аналитика (раздел 4) |
|
||||||
| `vgo_assignment` | ручное решение по строке ОСВ сверх автоматического сопоставления — привязка к правилу, снятие, «не учитывать в КФО» у нераспределённой строки (раздел 3.2a) |
|
|
||||||
| `goodwill` | гудвилл: доля владения, решение «учитывать в КФО», комментарий |
|
| `goodwill` | гудвилл: доля владения, решение «учитывать в КФО», комментарий |
|
||||||
| `kfo_result`, `kfo_result_line` | результат КФО |
|
| `kfo_result`, `kfo_result_line` | результат КФО |
|
||||||
|
|
||||||
@ -151,24 +150,8 @@ deviation_amount = |ΣA − ΣB|; deviation_percent = deviation_amount / min(ΣA
|
|||||||
(аванс, переплата), у A по B строки 150 и −50 дают ΣA = 100.
|
(аванс, переплата), у A по B строки 150 и −50 дают ΣA = 100.
|
||||||
|
|
||||||
Счета правил пересекаются (дебиторка у № 5–8 общая; 67.03 входит и в № 3, и в маску 67 у № 7; 66.03 — в № 4 и в № 8),
|
Счета правил пересекаются (дебиторка у № 5–8 общая; 67.03 входит и в № 3, и в маску 67 у № 7; 66.03 — в № 4 и в № 8),
|
||||||
поэтому без ручного решения строка такого счёта учитывается в **каждом** подходящем правиле, и сумма по паре компаний
|
поэтому строка такого счёта пока учитывается в **каждом** подходящем правиле, и сумма по паре компаний может превысить
|
||||||
может превысить сумму стороны. Двойной учёт снимает ручное разнесение строк по правилам (раздел 3.2a, AURORA-1751).
|
сумму стороны. Двойной учёт снимается разнесением строк по правилам и ручным переносом между правилами (AURORA-1751).
|
||||||
|
|
||||||
### 3.2a. Ручное разнесение строк (`vgo_assignment`, AURORA-1751)
|
|
||||||
|
|
||||||
Автоматическое сопоставление (3.2) по-прежнему не хранится и пересчитывается на каждый запрос. Поверх него аналитик
|
|
||||||
может закрепить решение по конкретной строке ОСВ — в таблице корректировок (раздел 4), не в шахматке:
|
|
||||||
|
|
||||||
- **привязать к правилу** — строка идёт только в агрегат этого правила, даже если по счёту подходит ещё нескольким
|
|
||||||
(снимает двойной учёт из 3.2/3.3) и даже без встречной стороны;
|
|
||||||
- **снять привязку** — строка возвращается в нераспределённые; автоматика её туда больше не относит, пока решение не
|
|
||||||
снято явно (`RESET`);
|
|
||||||
- **«не учитывать в КФО»** — только у нераспределённой строки (строки агрегата так не отмечаются: лишнюю убирают из
|
|
||||||
правила — она становится нераспределённой — и уже её отмечают).
|
|
||||||
|
|
||||||
Решение одно на всю строку свода (`osv_entry`), а не по стороне: строка с одновременно дебетовым и кредитовым сальдо
|
|
||||||
(редкий случай) получает одно решение на обе позиции. Повторное решение по той же строке заменяет прежнее, историю
|
|
||||||
хранит аудит (`ASSIGNMENT_CHANGE`).
|
|
||||||
|
|
||||||
**Упаковка строк** — какие строки ОСВ с какими сопоставлены (детализация и строки без пары). Правило охватывает
|
**Упаковка строк** — какие строки ОСВ с какими сопоставлены (детализация и строки без пары). Правило охватывает
|
||||||
несколько счетов, поэтому строки сопоставляются группами **1:1, 1:N, N:1**; N:M нет, N ≤ 5. Выбирается **минимальная
|
несколько счетов, поэтому строки сопоставляются группами **1:1, 1:N, N:1**; N:M нет, N ≤ 5. Выбирается **минимальная
|
||||||
@ -200,8 +183,8 @@ deviation_amount = |ΣA − ΣB|; deviation_percent = deviation_amount / min(ΣA
|
|||||||
| нет второй стороны (ΣB = 0) | элиминируется 0, строки — «без пары» (вопрос В-4) |
|
| нет второй стороны (ΣB = 0) | элиминируется 0, строки — «без пары» (вопрос В-4) |
|
||||||
| правило с `mode = manual` (№ 1, № 2) | автоматически не элиминируется, только корректировкой / гудвиллом |
|
| правило с `mode = manual` (№ 1, № 2) | автоматически не элиминируется, только корректировкой / гудвиллом |
|
||||||
|
|
||||||
Каждое правило считается отдельно, поэтому без ручного решения строка общего счёта входит в суммы нескольких правил
|
Каждое правило считается отдельно, поэтому строка общего счёта пока входит в суммы нескольких правил (раздел 3.2) —
|
||||||
(раздел 3.2) — двойной учёт снимает ручное разнесение строк (раздел 3.2a, AURORA-1751).
|
двойной учёт снимается разнесением строк (AURORA-1751).
|
||||||
|
|
||||||
Алгоритм — по ТЗ (решение владельца 08.10.2026, OQ-155): пары, `min`, порог 5 %. Эталонный Excel «Консолидация_Группа
|
Алгоритм — по ТЗ (решение владельца 08.10.2026, OQ-155): пары, `min`, порог 5 %. Эталонный Excel «Консолидация_Группа
|
||||||
РБ» элиминирует у каждой компании полную сумму её строк на группу — это сознательное расхождение с эталоном.
|
РБ» элиминирует у каждой компании полную сумму её строк на группу — это сознательное расхождение с эталоном.
|
||||||
@ -274,8 +257,8 @@ vgo_amount = eliminated_auto + eliminated_manual
|
|||||||
| `credit_line_code`, `credit_account_id`, `credit_adjustment` | кредитовая сторона |
|
| `credit_line_code`, `credit_account_id`, `credit_adjustment` | кредитовая сторона |
|
||||||
| `deviation_amount`, `deviation_percent` | расхождение пары — справочно, пересчитывается |
|
| `deviation_amount`, `deviation_percent` | расхождение пары — справочно, пересчитывается |
|
||||||
| `reason_code` | ТЗ 6.4: нереализованная выручка, акциз, агентское вознаграждение, амортизация по внутригрупповой аренде, проценты по внутригрупповым займам, доходы от участия, прочее |
|
| `reason_code` | ТЗ 6.4: нереализованная выручка, акциз, агентское вознаграждение, амортизация по внутригрупповой аренде, проценты по внутригрупповым займам, доходы от участия, прочее |
|
||||||
| `comment` | **обязателен всегда** (ТЗ 6.8), не только при расхождении |
|
| `comment` | **обязателен** (ТЗ 6.8) |
|
||||||
| `status` | в модели `DRAFT`/`APPLIED`; реализовано — сохранение сразу даёт `APPLIED`, `DRAFT` сервер не выставляет; «отозвать» (`REVOKED`) нет — корректировку меняют повторным сохранением целиком (обе стороны и комментарий — заново) |
|
| `status` | `DRAFT` → `APPLIED` / `REVOKED`; в расчёт входят только `APPLIED` |
|
||||||
| `parent_correction_id` | распределение суммы одной стороны по нескольким статьям — дочерние записи, глубина 1 |
|
| `parent_correction_id` | распределение суммы одной стороны по нескольким статьям — дочерние записи, глубина 1 |
|
||||||
| `version_no` | растёт при каждом изменении |
|
| `version_no` | растёт при каждом изменении |
|
||||||
|
|
||||||
@ -316,8 +299,7 @@ vgo_amount = eliminated_auto + eliminated_manual
|
|||||||
|
|
||||||
**Сумма элиминации (правило № 6):** ΣA = 110 429 113,15, ΣB = 106 311 116,46, расхождение 3,87 % ≤ 5 % →
|
**Сумма элиминации (правило № 6):** ΣA = 110 429 113,15, ΣB = 106 311 116,46, расхождение 3,87 % ≤ 5 % →
|
||||||
элиминируется `min` = 106 311 116,46: статья 1230 у Счастливочной, 1520 у РБ. Это при условии, что у РБ нет 66.04 / 67.04
|
элиминируется `min` = 106 311 116,46: статья 1230 у Счастливочной, 1520 у РБ. Это при условии, что у РБ нет 66.04 / 67.04
|
||||||
на Счастливочную: иначе 76.06 и 76.АВ войдут и в правило № 5 — двойной учёт, пока аналитик не привяжет их вручную к
|
на Счастливочную: иначе 76.06 и 76.АВ войдут и в правило № 5 — двойной учёт до разнесения строк (AURORA-1751).
|
||||||
одному из правил (раздел 3.2a, AURORA-1751).
|
|
||||||
|
|
||||||
**76.АВ** (НДС по авансам) сейчас входит в дебиторку по правилу «76 кроме 76.07.5/76.07.9». Эталон его исключает —
|
**76.АВ** (НДС по авансам) сейчас входит в дебиторку по правилу «76 кроме 76.07.5/76.07.9». Эталон его исключает —
|
||||||
вопрос OQ-164 (В-9).
|
вопрос OQ-164 (В-9).
|
||||||
@ -342,4 +324,4 @@ vgo_amount = eliminated_auto + eliminated_manual
|
|||||||
| В-9 | 76.АВ / 76.ВА (НДС по авансам) — исключать из ДЗ / КЗ? Есть ли ещё такие субсчета 76? | OQ-164 открыт, вопрос в AURORA-1373 |
|
| В-9 | 76.АВ / 76.ВА (НДС по авансам) — исключать из ДЗ / КЗ? Есть ли ещё такие субсчета 76? | OQ-164 открыт, вопрос в AURORA-1373 |
|
||||||
| В-10 | Округление при элиминации: рубли → тыс. руб. на выходе, ROUND_HALF_UP? | допущение OQ-26 |
|
| В-10 | Округление при элиминации: рубли → тыс. руб. на выходе, ROUND_HALF_UP? | допущение OQ-26 |
|
||||||
| В-11 | Правило № 1: если у компании А нет ОСВ по 03 — результат в строку «ППА»? | открыто в §4 |
|
| В-11 | Правило № 1: если у компании А нет ОСВ по 03 — результат в строку «ППА»? | открыто в §4 |
|
||||||
| В-12 | ~~Счета правил пересекаются — как избежать двойной элиминации?~~ | **решено 21.10.2026, реализовано (AURORA-1751):** каскад по остатку отменён, правила считаются отдельно, двойной учёт снимает ручное разнесение строк по правилам — `vgo_assignment`, раздел 3.2a |
|
| В-12 | ~~Счета правил пересекаются — как избежать двойной элиминации?~~ | **решено 21.10.2026:** каскад по остатку отменён, правила считаются отдельно, двойной учёт снимает разнесение строк по правилам (AURORA-1751, раздел 3.2) |
|
||||||
|
|||||||
@ -76,7 +76,7 @@ git remote add origin <url-arm-core> && git push -u origin main
|
|||||||
|
|
||||||
| Слой | Где | Статус | Правило для агента |
|
| Слой | Где | Статус | Правило для агента |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Модель данных | `docs/model/data-model-v5.24.dbml` | стабильный, версионируется | единственный источник имён и структур |
|
| Модель данных | `docs/model/data-model-v5.23.dbml` | стабильный, версионируется | единственный источник имён и структур |
|
||||||
| Правила домена | `docs/context/05-domain-rules.md` | курируемый — пишет человек/агент по итогам ревью | **единственный вход алгоритмов в код**; из ТЗ напрямую не кодируем |
|
| Правила домена | `docs/context/05-domain-rules.md` | курируемый — пишет человек/агент по итогам ревью | **единственный вход алгоритмов в код**; из ТЗ напрямую не кодируем |
|
||||||
| Открытые вопросы | `docs/context/07-open-questions.md` | живой реестр | каждое допущение в коде — `ASSUMPTION(OQ-nn)` |
|
| Открытые вопросы | `docs/context/07-open-questions.md` | живой реестр | каждое допущение в коде — `ASSUMPTION(OQ-nn)` |
|
||||||
| Экспорт ТЗ | `docs/source/tz/` | снимок на дату из `docs/source/tz/_export.md` | читать для контекста; расхождение с §05 → вопрос, не код |
|
| Экспорт ТЗ | `docs/source/tz/` | снимок на дату из `docs/source/tz/_export.md` | читать для контекста; расхождение с §05 → вопрос, не код |
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# 03. Архитектура (целевая, по решениям DECISION-1…5 и ARCH-* в DBML v5.24)
|
# 03. Архитектура (целевая, по решениям DECISION-1…5 и ARCH-* в DBML v5.23)
|
||||||
|
|
||||||
## Компоненты (= репозитории, см. `00-workspace-layout.md`)
|
## Компоненты (= репозитории, см. `00-workspace-layout.md`)
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ api/src/
|
|||||||
# машины статусов, policy — pydantic-модели на входе и выходе
|
# машины статусов, policy — pydantic-модели на входе и выходе
|
||||||
services/ # use-cases: оркестрация domain + repositories + outbox + audit_event
|
services/ # use-cases: оркестрация domain + repositories + outbox + audit_event
|
||||||
repositories/ # SQLAlchemy-запросы, в т. ч. запросы-вычисления (сводная ОСВ, шахматка)
|
repositories/ # SQLAlchemy-запросы, в т. ч. запросы-вычисления (сводная ОСВ, шахматка)
|
||||||
models/ # ORM 1:1 с DBML v5.24; имена таблиц/колонок/enum — как в DBML
|
models/ # ORM 1:1 с DBML v5.23; имена таблиц/колонок/enum — как в DBML
|
||||||
schemas/ # pydantic DTO API /api/v1
|
schemas/ # pydantic DTO API /api/v1
|
||||||
core/ # config, clients (S3; NATS outbox-publisher и result-consumer), auth (JWT РАИСА / dev-эмитент),
|
core/ # config, clients (S3; NATS outbox-publisher и result-consumer), auth (JWT РАИСА / dev-эмитент),
|
||||||
# logger, exception_handlers
|
# logger, exception_handlers
|
||||||
@ -341,7 +341,7 @@ FastAPI» платформы РАИСА/aurora): дерево импортиро
|
|||||||
`version_table_schema` и `include_name` Alembic). Следуем шаблону, не `public`.
|
`version_table_schema` и `include_name` Alembic). Следуем шаблону, не `public`.
|
||||||
- Alembic берёт URL из настроек, не из `alembic.ini`; ревизии — `api/alembic/versions/`, имя файла стандартное
|
- Alembic берёт URL из настроек, не из `alembic.ini`; ревизии — `api/alembic/versions/`, имя файла стандартное
|
||||||
`<rev>_<slug>.py`. Автогенерация — только черновик: итоговую ревизию сверяет с DBML `api/scripts/check_dbml_vs_orm.py`.
|
`<rev>_<slug>.py`. Автогенерация — только черновик: итоговую ревизию сверяет с DBML `api/scripts/check_dbml_vs_orm.py`.
|
||||||
- PK везде `uuid` через `UUIDPrimaryMixin`; имена таблиц, колонок и enum'ов — ровно как в DBML v5.24.
|
- PK везде `uuid` через `UUIDPrimaryMixin`; имена таблиц, колонок и enum'ов — ровно как в DBML v5.23.
|
||||||
|
|
||||||
### Инфраструктурные клиенты
|
### Инфраструктурные клиенты
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# 04. Как читать модель данных v5.24
|
# 04. Как читать модель данных v5.23
|
||||||
|
|
||||||
Файлы: `docs/model/data-model-v5.24.dbml` (источник истины), `docs/model/entities-v5.24.md` (смысл таблиц; предыдущие
|
Файлы: `docs/model/data-model-v5.23.dbml` (источник истины), `docs/model/entities-v5.23.md` (смысл таблиц; предыдущие
|
||||||
версии `data-model-v5.13…v5.20.dbml` и `entities-v5.13…v5.20.md` остаются как история),
|
версии `data-model-v5.13…v5.20.dbml` и `entities-v5.13…v5.20.md` остаются как история),
|
||||||
`docs/model/diagrams/part01…09.{svg,png}` (диаграммы v5.21 по частям из таблицы ниже; перерисовываются из DBML командой
|
`docs/model/diagrams/part01…09.{svg,png}` (диаграммы v5.21 по частям из таблицы ниже; перерисовываются из DBML командой
|
||||||
`make diagrams` в корне репозитория docs — скрипт `scripts/gen_model_diagrams.py`, нужен npx; при новой версии DBML
|
`make diagrams` в корне репозитория docs — скрипт `scripts/gen_model_diagrams.py`, нужен npx; при новой версии DBML
|
||||||
@ -17,11 +17,9 @@
|
|||||||
| 05 Разобранные ОСВ | `osv_entry` | строка ОСВ с шестью суммами, сырым контрагентом и резолвом |
|
| 05 Разобранные ОСВ | `osv_entry` | строка ОСВ с шестью суммами, сырым контрагентом и резолвом |
|
||||||
| 06 Формы ББ/ОФР | `financial_statement_meta`, `_column`, `_line`, `_value`, `document_issue` | шапка, колонки-даты, строки со статусами, значения с confidence и `needs_review`, предупреждения и нарушения §7 |
|
| 06 Формы ББ/ОФР | `financial_statement_meta`, `_column`, `_line`, `_value`, `document_issue` | шапка, колонки-даты, строки со статусами, значения с confidence и `needs_review`, предупреждения и нарушения §7 |
|
||||||
| 07 Консолидация | `consolidation`, `consolidation_version`, `_status_history`, `_lock`, `_member`, `_document` | логическая консолидация (ГК × период) и её версии с периметром |
|
| 07 Консолидация | `consolidation`, `consolidation_version`, `_status_history`, `_lock`, `_member`, `_document` | логическая консолидация (ГК × период) и её версии с периметром |
|
||||||
| 08 Корректировки и КФО | `correction`, `vgo_assignment`, `goodwill`, `kfo_result`, `kfo_result_line`, `kfo_result_line_company`, `client_kfo_line` | ручные решения и результат |
|
| 08 Корректировки и КФО | `correction`, `goodwill`, `kfo_result`, `kfo_result_line`, `kfo_result_line_company`, `client_kfo_line` | ручные решения и результат |
|
||||||
| 09 Комментарии, аудит, ЕХ | `comment`, `audit_event`, `ex_snapshot`, `ex_snapshot_member`, `ex_snapshot_item` | полиморфные комментарии, append-only журнал, замороженные срезы |
|
| 09 Комментарии, аудит, ЕХ | `comment`, `audit_event`, `ex_snapshot`, `ex_snapshot_member`, `ex_snapshot_item` | полиморфные комментарии, append-only журнал, замороженные срезы |
|
||||||
|
|
||||||
**v5.24:** новая таблица `vgo_assignment` — ручное решение аналитика по строке сводной ОСВ сверх автоматического разнесения по правилам ВГО (§5): привязка к правилу / снятие, «не учитывать в КФО» у нераспределённой строки (AURORA-1751).
|
|
||||||
|
|
||||||
**v5.23:** `consolidation_version.elimination_rules_version` — `not null`: версия правил элиминации, действующая на дату создания версии консолидации, фиксируется при создании; существующим версиям проставляется миграцией (AURORA-1536).
|
**v5.23:** `consolidation_version.elimination_rules_version` — `not null`: версия правил элиминации, действующая на дату создания версии консолидации, фиксируется при создании; существующим версиям проставляется миграцией (AURORA-1536).
|
||||||
|
|
||||||
**v5.22:** из описания `comment.object_type` убраны `chess_cell` и `kfo_result_line`, сохранены `document | consolidation_version | …`; структура БД не меняется, миграция не требуется (AURORA-1446).
|
**v5.22:** из описания `comment.object_type` убраны `chess_cell` и `kfo_result_line`, сохранены `document | consolidation_version | …`; структура БД не меняется, миграция не требуется (AURORA-1446).
|
||||||
|
|||||||
@ -676,7 +676,7 @@ closing_* = closing_* строки Qn
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## §5. Автоматическое сопоставление ВГО и ручное распределение строк (DOC-223044, DOC-272613; OQ-07, OQ-173 — решения владельца 19.10, 21.10 и 23.10.2026; AURORA-1751/1540/1541 — реализовано, слито в main)
|
## §5. Автоматическое сопоставление ВГО (DOC-223044; OQ-07, OQ-173 — решения владельца 19.10, 21.10 и 23.10.2026)
|
||||||
|
|
||||||
Границы: только `closing_debit` / `closing_credit`; только компании периметра версии; правила и счета сторон — из §4
|
Границы: только `closing_debit` / `closing_credit`; только компании периметра версии; правила и счета сторон — из §4
|
||||||
(версия правил — `consolidation_version.elimination_rules_version`), только правила ББ (`statement_type = balance`,
|
(версия правил — `consolidation_version.elimination_rules_version`), только правила ББ (`statement_type = balance`,
|
||||||
@ -688,19 +688,11 @@ closing_* = closing_* строки Qn
|
|||||||
Два уровня: **сумма элиминации** по тройке — агрегат, идёт в КФО; **упаковка** — какие строки ОСВ с какими сопоставлены
|
Два уровня: **сумма элиминации** по тройке — агрегат, идёт в КФО; **упаковка** — какие строки ОСВ с какими сопоставлены
|
||||||
(детализация, строки без пары). Способ упаковки сумму элиминации не меняет (решение владельца 19.10.2026).
|
(детализация, строки без пары). Способ упаковки сумму элиминации не меняет (решение владельца 19.10.2026).
|
||||||
|
|
||||||
**Два экрана, два механизма.** Исторически первым был экран «Пары ВГО» (п. 1–3 ниже, эндпоинты `elimination-totals` /
|
|
||||||
`elimination-pairs`, `IntercompanyMatrixService`) — он остался в API, но фронт его больше не показывает: место заняла
|
|
||||||
«Таблица корректировок» (п. 2a, эндпоинты `vgo-assignments/*` и `corrections`, `VgoService`), которая решает и двойной
|
|
||||||
учёт, и корректировку одним экраном. Правила п. 1–2 (счета, упаковка, пороги) у обоих общие; различается то, что
|
|
||||||
происходит со строкой, подходящей под несколько правил сразу.
|
|
||||||
|
|
||||||
**1. Сумма элиминации по тройке — каждое правило отдельно.** Каскад по остатку в порядке `rule_no` **отменён** (решение
|
**1. Сумма элиминации по тройке — каждое правило отдельно.** Каскад по остатку в порядке `rule_no` **отменён** (решение
|
||||||
владельца 21.10.2026 по созвону с заказчиком 19.10.2026, OQ-173): счета правил пересекаются (дебет № 5–8 общий; кредит
|
владельца 21.10.2026 по созвону с заказчиком 19.10.2026, OQ-173): счета правил пересекаются (дебет № 5–8 общий; кредит
|
||||||
67.03 — № 3 и маска 67 у № 7, 66.03 — № 4 и маска 66 у № 8). На экране «Пары ВГО» (`elimination-totals`/`elimination-pairs`)
|
67.03 — № 3 и маска 67 у № 7, 66.03 — № 4 и маска 66 у № 8), и строка такого счёта пока входит в Σ **каждого** подходящего
|
||||||
строка такого счёта входит в Σ **каждого** подходящего правила без исключений — это посчитанный агрегат по одной тройке,
|
правила. Двойной учёт снимает разнесение строк по правилам и ручной перенос между правилами (AURORA-1751); до него
|
||||||
он не знает о других тройках и не хранит состояние; сумма элиминации по паре компаний на этом экране может превышать
|
сумма элиминации по паре компаний может превышать сумму стороны — это известное ограничение, не дефект.
|
||||||
сумму стороны. Известное ограничение этого конкретного экрана, не дефект — он больше не в UI, двойной счёт решает
|
|
||||||
«Таблица корректировок» (п. 2a).
|
|
||||||
```
|
```
|
||||||
ΣA = Σ строк A с counterparty_id = B по счетам стороны 1 правила r (с исключениями is_excluded), сумма стороны side_1
|
ΣA = Σ строк A с counterparty_id = B по счетам стороны 1 правила r (с исключениями is_excluded), сумма стороны side_1
|
||||||
ΣB = Σ строк B с counterparty_id = A по счетам стороны 2 правила r, противоположная сторона
|
ΣB = Σ строк B с counterparty_id = A по счетам стороны 2 правила r, противоположная сторона
|
||||||
@ -717,7 +709,7 @@ mode = manual: элиминация_r = 0 — только корректиро
|
|||||||
**2. Упаковка строк в тройке** — группами **1:1, 1:N, N:1** (правило охватывает несколько счетов, поэтому одной
|
**2. Упаковка строк в тройке** — группами **1:1, 1:N, N:1** (правило охватывает несколько счетов, поэтому одной
|
||||||
строке на одной стороне могут соответствовать несколько строк на другой). N:M нет. Выбирается **минимальная упаковка** —
|
строке на одной стороне могут соответствовать несколько строк на другой). N:M нет. Выбирается **минимальная упаковка** —
|
||||||
группа с меньшим числом строк: если сумму закрывают и «1 Дт ↔ 2 Кт», и «1 Дт ↔ 3 Кт», берётся 1:2. Каждое правило
|
группа с меньшим числом строк: если сумму закрывают и «1 Дт ↔ 2 Кт», и «1 Дт ↔ 3 Кт», берётся 1:2. Каждое правило
|
||||||
упаковывается независимо (каскада нет): на экране «Пары ВГО» строка может войти в пары разных правил одновременно.
|
упаковывается независимо (каскада нет): строка может войти в пары разных правил, пока её не разнесли (AURORA-1751).
|
||||||
```
|
```
|
||||||
вход правила r: строки a стороны 1 (A → B) и b стороны 2 (B → A) по счетам правила r с суммой > 0
|
вход правила r: строки a стороны 1 (A → B) и b стороны 2 (B → A) по счетам правила r с суммой > 0
|
||||||
строки с суммой < 0 в упаковку не идут → «без пары», причина NEGATIVE_AMOUNT (разбирает аналитик)
|
строки с суммой < 0 в упаковку не идут → «без пары», причина NEGATIVE_AMOUNT (разбирает аналитик)
|
||||||
@ -734,56 +726,18 @@ mode = manual: элиминация_r = 0 — только корректиро
|
|||||||
Результат **не хранится** (DECISION-5) — возвращается как структура для шахматки, корректировок и КФО. Ручное решение по
|
Результат **не хранится** (DECISION-5) — возвращается как структура для шахматки, корректировок и КФО. Ручное решение по
|
||||||
паре — `correction` с естественным ключом (`rule_id`, `legal_entity_id`, `counterparty_id`).
|
паре — `correction` с естественным ключом (`rule_id`, `legal_entity_id`, `counterparty_id`).
|
||||||
|
|
||||||
**2a. Разнесение строк по правилам — «Таблица корректировок» (DOC-272613 «Ручное добавление и перенос строк», §5;
|
**3. Выдача.** Два запроса (контракт — `dev/frontend-api-guide.md`): **список пар версии** — все тройки «правило ББ ×
|
||||||
AURORA-1751; эндпоинт `GET …/vgo-assignments/distribute`, домен `api/src/domain/vgo_distribution.py`, сервис
|
упорядоченная пара компаний периметра», где хотя бы одна сторона не ноль, с ΣA, ΣB, расхождением, процентом, суммой
|
||||||
`VgoService`).** Тот же набор строк (`closing_debit`/`closing_credit` > 0, контрагент — компания периметра, правила ББ
|
элиминации, статусом (сопоставлено автоматически / с расхождением > 5 % / без встречной стороны / ручное правило) и
|
||||||
`measure = closing`), но решение по строке — **одно на всю модель**, не по тройке:
|
`osv_entry.id` строк обеих сторон; сводная ОСВ строится один раз на запрос. **Детализация тройки** — упаковка п. 2:
|
||||||
|
точные группы, группы с расхождением ≤ 5 %, строки без пары с причиной.
|
||||||
1. точные упаковки (п. 2) закрепляются первыми по возрастанию `rule_no` без повторного использования строк — у пары
|
|
||||||
правило с меньшим номером Прил. 3 забирает строку раньше;
|
|
||||||
2. из пар с расхождением (п. 2, `AMBIGUOUS` не считается) берутся те, что не делят ни одной строки ни с точной
|
|
||||||
парой, ни между собой — неоднозначные остаются нераспределёнными;
|
|
||||||
3. из оставшихся («без пары», п. 2) строка, чей счёт входит ровно в одно применимое правило, уходит в это правило
|
|
||||||
**даже без встречной стороны** (решение одностороннее — `decision: AUTO`, без пары в агрегате);
|
|
||||||
4. строка, чей счёт входит в несколько правил, и ни на один из них нет однозначного решения — **нераспределённая**
|
|
||||||
(`reason: MULTIPLE_RULES`); в сумму ни одного правила она не входит, пока её не разнесут вручную. Именно так здесь
|
|
||||||
снят двойной учёт из п. 1 — не пересчётом, а исключением спорной строки из обеих сумм до решения аналитика;
|
|
||||||
5. **ручное решение аналитика важнее автоматики** (п. 1–4) — хранится в `vgo_assignment` (DBML v5.24; одна запись на
|
|
||||||
строку свода в версии и типе отчётности, обновляется повторным решением, не дублируется):
|
|
||||||
- привязка к правилу (`action = ASSIGN`, `rule_id`) — строка идёт только в это правило, даже если по счёту подходит
|
|
||||||
ещё нескольким, и даже без встречной стороны;
|
|
||||||
- снятие привязки (`action = UNASSIGNED`) — строка возвращается в нераспределённые, автоматика её туда больше не
|
|
||||||
относит, пока решение не снято явно;
|
|
||||||
- «не учитывать в КФО» (`is_exclude`) — **только у нераспределённой строки** (строка агрегата так не отмечается:
|
|
||||||
её сначала убирают из правила — `UNASSIGNED`, — и уже нераспределённую отмечают); CHECK в БД запрещает
|
|
||||||
`is_exclude = true` одновременно с `rule_id`;
|
|
||||||
- поле `side` (черновик `dev/api-contract-corrections.md` предполагал его для строки с одновременно дебетовым и
|
|
||||||
кредитовым сальдо) в реализации нет — решение одно на обе позиции строки.
|
|
||||||
|
|
||||||
Сумма стороны агрегата здесь — Σ всех её строк (п. 1 + п. 2 + ручные), включая односторонние (п. 3) и перенесённые
|
|
||||||
вручную; агрегат целиком уходит в КФО, лишнюю строку убирают из правила, а не из агрегата (ответ Лысенко п. 4). Аудит
|
|
||||||
решения — `audit_event(ASSIGNMENT_CHANGE)`, `before`/`after` по затронутым строкам.
|
|
||||||
|
|
||||||
**3. Выдача.** На экране «Пары ВГО» (контракт — `dev/frontend-api-guide.md` §5.7.2) — два запроса: **список пар
|
|
||||||
версии** (`GET …/elimination-totals`) — все тройки «правило ББ × упорядоченная пара компаний периметра», где хотя бы
|
|
||||||
одна сторона не ноль, с ΣA, ΣB, расхождением, процентом, суммой элиминации, статусом (сопоставлено автоматически / с
|
|
||||||
расхождением > 5 % / без встречной стороны / ручное правило) и `osv_entry.id` строк обеих сторон; сводная ОСВ строится
|
|
||||||
один раз на запрос. **Детализация тройки** (`GET …/elimination-pairs`) — упаковка п. 2: точные группы, группы с
|
|
||||||
расхождением ≤ 5 %, строки без пары с причиной.
|
|
||||||
|
|
||||||
На «Таблице корректировок» (контракт — `dev/api-contract-corrections.md`) — один запрос `GET …/vgo-assignments/distribute`
|
|
||||||
отдаёт сразу все группы (п. 2a) и нераспределённые строки; решение по строке — `PUT|DELETE …/vgo-assignments/rule`,
|
|
||||||
`PUT …/vgo-assignments/exclude` (единой ручки действия нет — см. контракт §3.3 и `[allocation-split]`); таблица
|
|
||||||
корректировок (§8) читает те же группы как источник агрегатов.
|
|
||||||
|
|
||||||
**Алгоритм — по ТЗ** (решение владельца 08.10.2026, OQ-155): пары, `min`, порог 5 %, а не полная элиминация каждой
|
**Алгоритм — по ТЗ** (решение владельца 08.10.2026, OQ-155): пары, `min`, порог 5 %, а не полная элиминация каждой
|
||||||
стороны, как в эталонном Excel. Пример на фикстурах (Q3 2024, правило № 6): у Счастливочной на «Русский бисквит» 62.01
|
стороны, как в эталонном Excel. Пример на фикстурах (Q3 2024, правило № 6): у Счастливочной на «Русский бисквит» 62.01
|
||||||
«БП Производство» 106 311 116,46, 62.01 «БП Аренда» 1 401 330, 76.06 350 000, 76.АВ 2 366 666,69; у РБ — одна строка
|
«БП Производство» 106 311 116,46, 62.01 «БП Аренда» 1 401 330, 76.06 350 000, 76.АВ 2 366 666,69; у РБ — одна строка
|
||||||
60.01 106 311 116,46. Упаковка: точная пара 1:1 (106 311 116,46), остальные три строки Счастливочной — без пары.
|
60.01 106 311 116,46. Упаковка: точная пара 1:1 (106 311 116,46), остальные три строки Счастливочной — без пары.
|
||||||
Сумма элиминации: ΣA = 110 429 113,15, ΣB = 106 311 116,46, расхождение 3,87 % ≤ 5 % → min = 106 311 116,46. На экране
|
Сумма элиминации: ΣA = 110 429 113,15, ΣB = 106 311 116,46, расхождение 3,87 % ≤ 5 % → min = 106 311 116,46. Строки 76.06
|
||||||
«Пары ВГО» строки 76.06 и 76.АВ входят и в правило № 5, если у РБ есть 66.04/67.04 на Счастливочную, — двойной учёт
|
и 76.АВ входят и в правило № 5, если у РБ есть 66.04/67.04 на Счастливочную, — двойной учёт до AURORA-1751.
|
||||||
(п. 1); на «Таблице корректировок» (п. 2a) без ручного решения они останутся нераспределёнными (`MULTIPLE_RULES`), а
|
|
||||||
не посчитаются дважды.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -927,29 +881,10 @@ OQ-145); тем же правилом сервисы разрешают ничь
|
|||||||
|
|
||||||
## §8. Корректировки, гудвилл, КФО, пороги
|
## §8. Корректировки, гудвилл, КФО, пороги
|
||||||
|
|
||||||
**Корректировка** (DOC-227278, ТЗ 6.8; DOC-272613 «Таблица корректировок»; AURORA-1540/1541, реализовано —
|
**Корректировка** (DOC-227278, ТЗ 6.8): запись на пару (правило, компания 1, компания 2) и форму (ББ/ОФР); `debit_line_code
|
||||||
`arm-core#206/#208`, контракт `dev/api-contract-corrections.md`): запись на агрегат — пару (правило, компания 1,
|
/ debit_adjustment`, `credit_line_code / credit_adjustment`; распределение суммы одной стороны по нескольким строкам —
|
||||||
компания 2) и форму (ББ/ОФР), естественный ключ в `correction` не меняется. Сохранение —
|
дочерние записи (`parent_correction_id`, глубина 1). Контроль: `Σ debit_adjustment` ≈ `Σ credit_adjustment` (±5 %),
|
||||||
`PUT …/corrections/aggregates/{rule_id}/{company_1_id}/{company_2_id}` — **заменяет корректировку агрегата целиком**:
|
иначе ВГО распределено не полностью. Комментарий обязателен. История — `audit_event(CORRECTION_*)`, `version_no++`.
|
||||||
обе стороны и комментарий приходят каждый раз, частичного PATCH нет. Статьи стороны — массив `{line_code, amount}`,
|
|
||||||
первая — основная запись (`debit_line_code/debit_adjustment` или `credit_line_code/credit_adjustment` в `correction`),
|
|
||||||
остальные — дочерние (`parent_correction_id`, глубина 1); пустая сторона допустима, обе пустые — ошибка.
|
|
||||||
|
|
||||||
- **Статус.** В модели `DRAFT`/`APPLIED`; реализовано — сохранение сразу даёт `APPLIED`, `DRAFT` сервер не выставляет.
|
|
||||||
Отзыва (`REVOKED`) нет — корректировку меняют повторным сохранением (ответ Лысенко п. 9).
|
|
||||||
- **Комментарий обязателен всегда** (не только при расхождении — ответ п. 6, п. 8), не длиннее 1000 символов.
|
|
||||||
- **Контроль распределения** (`CorrectionBalanceOut`): `debit_total`/`credit_total` — суммы сохранённых статей;
|
|
||||||
`difference`/`difference_percent` между ними, `remainder_1`/`remainder_2` — сумма стороны агрегата минус сохранённая
|
|
||||||
статья. Это только отображаемый контроль — корректировку можно сохранить, даже если стороны не сошлись; в КФО по
|
|
||||||
выбранным статьям уходит меньшая сумма (ответ п. 8).
|
|
||||||
- **Порог отклонения агрегата** (`exceeds_threshold`, не путать с контролем выше): `deviation_percent = |ΣA − ΣB| /
|
|
||||||
min(ΣA, ΣB) × 100` сторон агрегата (§5, п. 1/2a); реализовано **строго больше 5 %** (R-35, `domain/corrections.py`,
|
|
||||||
`arm-core#215`/Task 8) — ровно 5,00 % в допуске. ТЗ/DOC-272613 читались как «от 5 % включительно»; аналитик на это не
|
|
||||||
отвечал — открытый вопрос OQ-178, фронт синхронизирован с фактическим поведением сервера, а не с текстом.
|
|
||||||
- **Оптимистичная блокировка.** `expected_version_no` — номер редакции действующей корректировки, которую видел
|
|
||||||
аналитик (`null`, если её ещё нет); не совпал с текущим `correction.version_no` (или `null` при уже существующей) —
|
|
||||||
409 `CORRECTION_VERSION_CONFLICT`, кто-то сохранил раньше, фронт перечитывает таблицу.
|
|
||||||
- История — `audit_event(CORRECTION_CREATE`/`CORRECTION_EDIT)`, `before`/`after`, `version_no++` при каждом сохранении.
|
|
||||||
|
|
||||||
**Гудвилл** (DOC-259025, ТЗ 6.7): по каждой паре ГО→ДО периметра:
|
**Гудвилл** (DOC-259025, ТЗ 6.7): по каждой паре ГО→ДО периметра:
|
||||||
`FV = Σ closing_debit (58.01, контрагент ДО)`; `UK = 1310 ББ ДО на отчётную дату` (FNR03: Кт 80 + Кт 83, при
|
`FV = Σ closing_debit (58.01, контрагент ДО)`; `UK = 1310 ББ ДО на отчётную дату` (FNR03: Кт 80 + Кт 83, при
|
||||||
|
|||||||
@ -182,4 +182,3 @@
|
|||||||
| OQ-174 | Какая версия правил элиминации (§4) применяется к версии консолидации, если `consolidation_version.elimination_rules_version` не задан: открытые на сегодня, действующие на отчётную дату консолидации или на дату её составления; когда фиксировать номер версии | ревью arm-core PR #141 (21.10.2026) | §4, §5, AURORA-1536 | **закрыт решением владельца 21.10.2026 (docs #145):** версия правил, действующая на дату составления консолидации (создания версии), а не на отчётный период; фиксируется в `elimination_rules_version` при создании версии, не в GET-запросе; реализация — AURORA-1536. С DBML v5.23 (решение владельца 23.10.2026, docs #164) поле обязательно: у версий, созданных раньше, номер проставлен миграцией по дате создания |
|
| OQ-174 | Какая версия правил элиминации (§4) применяется к версии консолидации, если `consolidation_version.elimination_rules_version` не задан: открытые на сегодня, действующие на отчётную дату консолидации или на дату её составления; когда фиксировать номер версии | ревью arm-core PR #141 (21.10.2026) | §4, §5, AURORA-1536 | **закрыт решением владельца 21.10.2026 (docs #145):** версия правил, действующая на дату составления консолидации (создания версии), а не на отчётный период; фиксируется в `elimination_rules_version` при создании версии, не в GET-запросе; реализация — AURORA-1536. С DBML v5.23 (решение владельца 23.10.2026, docs #164) поле обязательно: у версий, созданных раньше, номер проставлен миграцией по дате создания |
|
||||||
| OQ-175 | Процент расхождения шахматки при отрицательной меньшей стороне: `variance_pct = variance / min(amount_a, amount_b) * 100` при отрицательном сальдо (возможно по §1) уходит в минус — пара (100, −5) даёт −2100 %, порог > 5 % не срабатывает, пара не подсвечивается. Считать от `\|min\|`, от меньшей по модулю стороны или подсвечивать пары с разными знаками всегда? | ревью arm-core #165 (AURORA-1651), 22.10.2026 | §6, шахматка, xlsx | открыт; до решения — как в коде (min со знаком); у пар ВГО §5 суммы сторон тоже со знаком (решение владельца 23.10.2026), но при `min ≤ 0` процент не считается и тройка подсвечивается |
|
| OQ-175 | Процент расхождения шахматки при отрицательной меньшей стороне: `variance_pct = variance / min(amount_a, amount_b) * 100` при отрицательном сальдо (возможно по §1) уходит в минус — пара (100, −5) даёт −2100 %, порог > 5 % не срабатывает, пара не подсвечивается. Считать от `\|min\|`, от меньшей по модулю стороны или подсвечивать пары с разными знаками всегда? | ревью arm-core #165 (AURORA-1651), 22.10.2026 | §6, шахматка, xlsx | открыт; до решения — как в коде (min со знаком); у пар ВГО §5 суммы сторон тоже со знаком (решение владельца 23.10.2026), но при `min ≤ 0` процент не считается и тройка подсвечивается |
|
||||||
| OQ-176 | Счёт `000` «Вспомогательный» (технический счёт 1С для ввода остатков, `kind = AP`, `is_off_balance = false`) входит в шахматку «все счета». Исключать его вместе с забалансовыми? | ревью arm-core #165 (AURORA-1651), 22.10.2026 | §6, режим «все счета» | открыт; до решения — входит (решение владельца 22.10.2026: не трогать) |
|
| OQ-176 | Счёт `000` «Вспомогательный» (технический счёт 1С для ввода остатков, `kind = AP`, `is_off_balance = false`) входит в шахматку «все счета». Исключать его вместе с забалансовыми? | ревью arm-core #165 (AURORA-1651), 22.10.2026 | §6, режим «все счета» | открыт; до решения — входит (решение владельца 22.10.2026: не трогать) |
|
||||||
| OQ-178 | Порог превышения отклонения агрегата в таблице корректировок — ровно 5 % в допуске (строго `>`) или на проверку («от 5 % включительно», как читается DOC-272613/ТЗ)? Аналитик (Лысенко) на прямой вопрос не отвечал | `dev/api-contract-corrections.md` §2; R-34/R-35 (`arm-core`, `openspec/changes/corrections-table-ui/design.md`) | §5 п. 2a, §8 «Корректировка», экран «Таблица корректировок» | открыт; реализовано строго `>` 5 % (R-35, `domain/corrections.py`) — фронт и сервер синхронны между собой, но не точно со спорным текстом ТЗ; правка при подтверждении — один оператор в `editorModel.ts`/`CorrectionTable.tsx` и в `domain/corrections.py` |
|
|
||||||
|
|||||||
@ -48,8 +48,8 @@
|
|||||||
|
|
||||||
| Файл | Что |
|
| Файл | Что |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `data-model-v5.24.dbml` | источник истины; changelog v5.1…v5.24 в шапке; блоки «ИСТОЧНИК» над каждой таблицей (v5.13…v5.23 остаются рядом как история) |
|
| `data-model-v5.23.dbml` | источник истины; changelog v5.1…v5.23 в шапке; блоки «ИСТОЧНИК» над каждой таблицей (v5.13…v5.22 остаются рядом как история) |
|
||||||
| `entities-v5.24.md` | описание сущностей по частям 01–09, таблицы «Что изменилось» по версиям, сценарий «как читать модель», открытые вопросы (v5.13…v5.23 остаются как история) |
|
| `entities-v5.23.md` | описание сущностей по частям 01–09, таблицы «Что изменилось» по версиям, сценарий «как читать модель», открытые вопросы (v5.13…v5.22 остаются как история) |
|
||||||
| `diagrams/part01…09.{svg,png}` | диаграммы v5.21 по частям; перерисовываются `make diagrams` (`scripts/gen_model_diagrams.py`) |
|
| `diagrams/part01…09.{svg,png}` | диаграммы v5.21 по частям; перерисовываются `make diagrams` (`scripts/gen_model_diagrams.py`) |
|
||||||
| `source/tz/data-model-part1_4.dbml` (лежит не в `model/`, а в экспорте ТЗ) | v4 часть 1 — историческое |
|
| `source/tz/data-model-part1_4.dbml` (лежит не в `model/`, а в экспорте ТЗ) | v4 часть 1 — историческое |
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
1. `CLAUDE.md` (workspace) и `docs/context/00-workspace-layout.md` — как устроена рабочая область, статус аналитики, макет.
|
1. `CLAUDE.md` (workspace) и `docs/context/00-workspace-layout.md` — как устроена рабочая область, статус аналитики, макет.
|
||||||
2. `docs/context/01-project-overview.md`, `02-glossary.md` — предметная область.
|
2. `docs/context/01-project-overview.md`, `02-glossary.md` — предметная область.
|
||||||
3. `docs/context/03-architecture.md`, `04-data-model-guide.md`, затем **весь** `docs/model/data-model-v5.24.dbml` и
|
3. `docs/context/03-architecture.md`, `04-data-model-guide.md`, затем **весь** `docs/model/data-model-v5.23.dbml` и
|
||||||
`docs/model/entities-v5.24.md`.
|
`docs/model/entities-v5.23.md`.
|
||||||
4. `docs/context/05-domain-rules.md` — алгоритмы (особенно §1.0 и §12 — парсеры ищут структуру по содержимому);
|
4. `docs/context/05-domain-rules.md` — алгоритмы (особенно §1.0 и §12 — парсеры ищут структуру по содержимому);
|
||||||
`06-test-data.md`; `07-open-questions.md`; `10-user-journey.md`; `09-delivery-plan.md`.
|
`06-test-data.md`; `07-open-questions.md`; `10-user-journey.md`; `09-delivery-plan.md`.
|
||||||
5. `arm-core/`, `tabular-parser/`, `scan-gateway/` — три репозитория, развёрнутые из нулевого коммита шаблона команды,
|
5. `arm-core/`, `tabular-parser/`, `scan-gateway/` — три репозитория, развёрнутые из нулевого коммита шаблона команды,
|
||||||
@ -37,7 +37,7 @@ NATS, MinIO, эмулятор ИС Сканирование), контракт `
|
|||||||
`07-open-questions.md`; если нужен ответ для среза — делай допущение по правилам из этого файла.
|
`07-open-questions.md`; если нужен ответ для среза — делай допущение по правилам из этого файла.
|
||||||
|
|
||||||
Ограничения на всё время работы:
|
Ограничения на всё время работы:
|
||||||
- имена таблиц, колонок и enum'ов — ровно как в DBML v5.24; поля, которых нет в DBML, не изобретать;
|
- имена таблиц, колонок и enum'ов — ровно как в DBML v5.23; поля, которых нет в DBML, не изобретать;
|
||||||
- алгоритмы — только из `05-domain-rules.md`; из страниц ТЗ напрямую не кодировать;
|
- алгоритмы — только из `05-domain-rules.md`; из страниц ТЗ напрямую не кодировать;
|
||||||
- парсеры: никаких координат ячеек, букв колонок, имён листов и номеров строк в коде — структура по содержимому,
|
- парсеры: никаких координат ячеек, букв колонок, имён листов и номеров строк в коде — структура по содержимому,
|
||||||
синонимы в `rules/*.yaml`; фикстуры — регрессия, не спецификация;
|
синонимы в `rules/*.yaml`; фикстуры — регрессия, не спецификация;
|
||||||
|
|||||||
@ -55,7 +55,7 @@ AURORA-1540 и AURORA-1751 (29.10): разнесение строк и реше
|
|||||||
- **Отклонение** (DOC-272613 «Расчёт»): `deviation_amount = |amount_1 − amount_2|`;
|
- **Отклонение** (DOC-272613 «Расчёт»): `deviation_amount = |amount_1 − amount_2|`;
|
||||||
`deviation_percent = deviation_amount / min(amount_1, amount_2) × 100`, если меньшая сторона > 0, иначе `null`.
|
`deviation_percent = deviation_amount / min(amount_1, amount_2) × 100`, если меньшая сторона > 0, иначе `null`.
|
||||||
**Порог: строго меньше 5 % — в допуске, от 5 % включительно — на проверку** (`exceeds_threshold`, DOC-272613).
|
**Порог: строго меньше 5 % — в допуске, от 5 % включительно — на проверку** (`exceeds_threshold`, DOC-272613).
|
||||||
**Открытый вопрос OQ-178:** реализовано (R-35) ровно 5 % — в допуске (`> 5`); аналитик на это не отвечал.
|
**Открытый вопрос:** в описании AURORA-1540 и в WIP API (#208) ровно 5 % — в допуске (`> 5`); аналитик на это не отвечал.
|
||||||
Отклонение у отдельных строк агрегата не считается (ответ п. 3).
|
Отклонение у отдельных строк агрегата не считается (ответ п. 3).
|
||||||
- **Корректировка** — решение аналитика по агрегату: какие суммы и из каких статей ББ (ОФР) исключить по каждой стороне,
|
- **Корректировка** — решение аналитика по агрегату: какие суммы и из каких статей ББ (ОФР) исключить по каждой стороне,
|
||||||
комментарий. Делается **только на уровне агрегата** (DOC-272613, ответ п. 2); на строку и на нераспределённую строку
|
комментарий. Делается **только на уровне агрегата** (DOC-272613, ответ п. 2); на строку и на нераспределённую строку
|
||||||
@ -133,8 +133,8 @@ AURORA-1540 и AURORA-1751 (29.10): разнесение строк и реше
|
|||||||
После решения пересчитываются затронутые агрегаты; у агрегата с применённой корректировкой ставится
|
После решения пересчитываются затронутые агрегаты; у агрегата с применённой корректировкой ставится
|
||||||
`composition_changed = true` (ответ 5). Ответ — вся таблица (решение меняет и агрегаты, и нераспределённые строки).
|
`composition_changed = true` (ответ 5). Ответ — вся таблица (решение меняет и агрегаты, и нераспределённые строки).
|
||||||
|
|
||||||
Аудит: реализовано существующим `audit_action.ASSIGNMENT_CHANGE` (не новым значением, как предполагалось здесь раньше) —
|
Аудит: в `audit_action` DBML v5.23 подходящего действия нет — нужно новое (например, `VGO_ASSIGNMENT_CHANGE`) вместе с
|
||||||
таблица `vgo_assignment` в DBML v5.24 (AURORA-1751); поля `side` в ней нет (решение см. там же).
|
таблицей `vgo_assignment` в DBML v5.24 (AURORA-1751).
|
||||||
|
|
||||||
### 3.4 `GET …/export` — xlsx
|
### 3.4 `GET …/export` — xlsx
|
||||||
|
|
||||||
@ -235,28 +235,27 @@ rule_id? (uuid, для ASSIGN)}`.
|
|||||||
|
|
||||||
## 5. Для бэкенда (AURORA-1540)
|
## 5. Для бэкенда (AURORA-1540)
|
||||||
|
|
||||||
- Модель: `correction` — как в DBML v5.24 (структура не менялась с v5.18); одна запись на сторону агрегата (основная, `parent_correction_id = NULL`) и дочерние
|
- Модель: `correction` — как в DBML v5.23; одна запись на сторону агрегата (основная, `parent_correction_id = NULL`) и дочерние
|
||||||
статьи; поля `debit_*` заполняются у записей стороны 1, `credit_*` — у стороны 2; `legal_entity_id` — компания стороны,
|
статьи; поля `debit_*` заполняются у записей стороны 1, `credit_*` — у стороны 2; `legal_entity_id` — компания стороны,
|
||||||
`counterparty_id` — другая компания агрегата. `deviation_*` записываются на момент сохранения — по ним считается
|
`counterparty_id` — другая компания агрегата. `deviation_*` записываются на момент сохранения — по ним считается
|
||||||
`composition_changed`.
|
`composition_changed`.
|
||||||
- **Кто что делает** (`[allocation-split]`, 29.10): AURORA-1751 — разнесение строк (чистая функция) и ручные решения
|
- **Кто что делает** (`[allocation-split]`, 29.10, уточнено 30.10): AURORA-1751 — разнесение строк (чистая функция), ручные
|
||||||
(`vgo_assignment`, аудит); AURORA-1540 — таблица, корректировка, экспорт поверх функции 1751 (источник агрегатов —
|
решения (`POST …/allocations`, `vgo_assignment`, аудит, DBML v5.24); AURORA-1540 — таблица, корректировка, экспорт поверх
|
||||||
`CorrectionAggregateSource`, вызывает функцию 1751).
|
функции 1751 (источник агрегатов в WIP #208 — `CorrectionAggregateSource`, его реализация вызывает функцию 1751).
|
||||||
- **Как фактически реализовано (`arm-core#208`, слито):** единой `POST …/allocations` нет — три отдельные ручки
|
- **Граница — по интерфейсу функции, не по таблице.** AURORA-1540 не читает `vgo_assignment` и не знает её схему; он вызывает
|
||||||
`PUT|DELETE …/vgo-assignments/rule` (привязать/снять правило) и `PUT …/vgo-assignments/exclude` (отметка «не учитывать
|
функцию разнесения AURORA-1751 и получает готовые `rules[]`, `aggregates[]` и нераспределённые строки, где `side` у каждой
|
||||||
в КФО»); фронт (`corrections.ts`, `allocate()`) собирает их под прежним контрактом «действие → таблица» и сам
|
позиции (`CorrectionEntryOut`, `UnallocatedEntryOut`) уже разрешён — функция сама решает, как внутри сопоставить строку
|
||||||
перечитывает таблицу после вызова.
|
свода с ручными решениями. Как 1751 хранит эти решения — её дело, в контракт с 1540 не входит.
|
||||||
- `vgo_assignment` (DBML v5.24) — **только ручные решения**, автоматическое разнесение не хранится (DECISION-5): иначе
|
- `vgo_assignment` (AURORA-1751, внутреннее хранилище) — **только ручные решения**, автоматическое разнесение не хранится
|
||||||
ручное решение не отличить от автоматического и при пересчёте после замены ОСВ оно затрётся. Поля:
|
(DECISION-5): иначе ручное решение не отличить от автоматического и при пересчёте после замены ОСВ оно затрётся. Одна
|
||||||
`consolidation_version_id, statement_type, osv_entry_id, rule_id (NULL), action (ASSIGN | UNASSIGNED), is_exclude
|
визуальная позиция обычно требует отдельной записи; строка с дебетовым и кредитовым сальдо одновременно даёт две позиции и,
|
||||||
(bool), created_by/at, updated_by/at`; `ASSIGN` — с `rule_id`, `UNASSIGNED` — без; уникальность
|
как правило, две записи — у нераспределённой строки обычно одна. Решает 1751 сама (например, полем `side` и уникальностью
|
||||||
`(consolidation_version_id, statement_type, osv_entry_id)`. **Поля `side`, которое черновик этого пункта предполагал
|
по нему); контракт этого не прописывает. Нужны DBML v5.24, ORM-модель в `src/models` и `audit_action` для решений по
|
||||||
для строки с одновременно дебетовым и кредитовым сальдо, в реализации нет** — решение одно на обе позиции строки, не
|
строкам — всё на стороне 1751.
|
||||||
по стороне отдельно.
|
|
||||||
- Разнесение — чистая функция в `api/src/domain/` (как `elimination_packing`): строки свода × правила × ручные решения →
|
- Разнесение — чистая функция в `api/src/domain/` (как `elimination_packing`): строки свода × правила × ручные решения →
|
||||||
агрегаты и нераспределённые строки (с `line_code` и `candidate_rule_ids`). Нынешняя упаковка считает каждое правило
|
агрегаты и нераспределённые строки (с `line_code`, `candidate_rule_ids` и уже разрешённым `side` на каждой позиции).
|
||||||
отдельно (TODO в `intercompany_matrix.build_pairs`) — для таблицы корректировок строка должна попадать не более чем в одно
|
Нынешняя упаковка считает каждое правило отдельно (TODO в `intercompany_matrix.build_pairs`) — для таблицы корректировок
|
||||||
правило.
|
строка должна попадать не более чем в одно правило.
|
||||||
- Правила ББ 1 и 2 (DOC-274230) имеют свой расчёт и не наследуют min; что идёт в `eliminated_amount` по ним — по DOC-274230.
|
- Правила ББ 1 и 2 (DOC-274230) имеют свой расчёт и не наследуют min; что идёт в `eliminated_amount` по ним — по DOC-274230.
|
||||||
- ОФР: тот же ответ с `statement_type = income` **(ждёт ответа в AURORA-1396)**; до него сервер может отдавать пустые
|
- ОФР: тот же ответ с `statement_type = income` **(ждёт ответа в AURORA-1396)**; до него сервер может отдавать пустые
|
||||||
`aggregates` и `unallocated` для `income`.
|
`aggregates` и `unallocated` для `income`.
|
||||||
|
|||||||
@ -1,190 +0,0 @@
|
|||||||
# Контракт API таблицы гудвилла (AURORA-1543 / AURORA-1544)
|
|
||||||
|
|
||||||
Черновик контракта для бэкенда (AURORA-1543, Цыганков) и фронта (AURORA-1544, Голов) — по аналогии с
|
|
||||||
`dev/api-contract-corrections.md`: фронт строит экран по контракту на фейковых данных, не дожидаясь бэкенда, подключение
|
|
||||||
к реальным ручкам — потом. Машиночитаемая копия — `dev/api-contract-goodwill.openapi.yaml`; при расхождении двух файлов
|
|
||||||
правится тот, что ошибся, и оба держатся одинаковыми.
|
|
||||||
|
|
||||||
Источники, в порядке приоритета:
|
|
||||||
1. **AURORA-1369** (решение методологов, срок 02.11.2026) — формула ещё не подтверждена, см. §5 «Открытые вопросы». Пока
|
|
||||||
не закрыта, бэкенд не считает по-настоящему — но контракт (форма ответа, поля, эндпоинты) этим не затронут: методологи
|
|
||||||
могут поменять *формулу* (входит ли 1350, источник УК), а не *набор полей* (поля `charter_capital_amount` /
|
|
||||||
`additional_capital_amount` под обе ветки уже есть в DBML).
|
|
||||||
2. `05-domain-rules.md` §8 «Гудвилл» — текущий рабочий алгоритм (допущения OQ-10, OQ-37).
|
|
||||||
3. DBML v5.24, `Table goodwill`, `Table kfo_result_line` (`line_code = 'GOODWILL'`), `consolidation_version.goodwill_enabled`.
|
|
||||||
4. Макет: `[31-03]` «Гудвилл» (таблица, режим «Редактировать»), `[31-04]` «КФО» (строка «В т.ч. Гудвилл» под 1170).
|
|
||||||
5. ТЗ 6.7, Прил. 3 п. 2, Прил. 8 — только там, где остальное молчит.
|
|
||||||
|
|
||||||
## 1. Общее
|
|
||||||
|
|
||||||
Всё как в `dev/api-contract-slice-4.md` §1 и `dev/api-contract-corrections.md` §1:
|
|
||||||
- префикс `/api/v1`, `Authorization: Bearer <JWT>`;
|
|
||||||
- чтение — право `CONSOLIDATION_VIEW`, правки — `CONSOLIDATION_EDIT`;
|
|
||||||
- версия вне видимости — 404 `NOT_FOUND`, как несуществующая;
|
|
||||||
- доменные ошибки — `{"detail": {"code", "message", ...}}`, 422 валидации тела — массив pydantic;
|
|
||||||
- **деньги — строки с двумя знаками** (`"1000000.00"`), всегда в **рублях**; перевод в тыс. руб. — на фронте;
|
|
||||||
- порядок проверок правок: право (403) → видимость (404) → версия редактируема (409) → операция; коды — те же, что везде:
|
|
||||||
409 `VERSION_NOT_EDITABLE`, `LOCK_REQUIRED`, `LOCKED_BY_OTHER`. Фронт читает `editable` / `read_only_reason` из ответа
|
|
||||||
таблицы, правило не повторяет.
|
|
||||||
|
|
||||||
## 2. Понятия
|
|
||||||
|
|
||||||
- **Головная организация периметра (ГО)** — член версии консолидации с `is_head_entity = true` (как в `VersionCard.members[]`,
|
|
||||||
`dev/api-contract-slice-4.md` §3). **Дочерняя организация (ДО)** — любой другой член периметра. Таблица гудвилла — по
|
|
||||||
одной строке на каждую пару (ГО, ДО); строки не создаются и не удаляются пользователем, их состав определяется
|
|
||||||
составом периметра (`PUT …/members`) на момент запроса. **Если в периметре несколько `is_head_entity`** — строки
|
|
||||||
считаются для каждой ГО отдельно (как для независимых голов); на реальных данных такого пока не встречалось —
|
|
||||||
ASSUMPTION, уточнить при первом расхождении.
|
|
||||||
- **Фондовые вложения (`financial_investment_amount`, FV)** — `Σ closing_debit` счёта `58.01` ГО по контрагенту ДО
|
|
||||||
(сводная ОСВ версии). Может быть `0.00` — строка всё равно показывается (полный список ДО периметра под каждой ГО,
|
|
||||||
как в макете `[31-03]`).
|
|
||||||
- **Уставный капитал ДО (`charter_capital_amount`, УК)** — стр. `1310` ББ ДО на отчётную дату; при наличии ОСВ ДО —
|
|
||||||
`Кт 80 + Кт 83`, иначе `1310` из формы (§8 domain-rules, уточняет первую часть OQ-10). **Добавочный капитал
|
|
||||||
(`additional_capital_amount`)** — стр. `1350` ББ ДО, считается и хранится всегда, независимо от того, участвует ли
|
|
||||||
в формуле (см. OQ-10 ниже).
|
|
||||||
- **Доля владения (`ownership_share`)** — ручной ввод аналитика, % (`numeric(7,4)`); единственное обязательное поле
|
|
||||||
правки наряду с `comment`.
|
|
||||||
- **Доля участия (`participation_amount`)** = `(charter_capital_amount [+ additional_capital_amount, если решение
|
|
||||||
методологов это подтвердит]) × ownership_share / 100`.
|
|
||||||
- **Гудвилл (`goodwill_amount`)** = `financial_investment_amount − participation_amount`; может быть отрицательным.
|
|
||||||
- **Учёт в КФО (`include_in_kfo`)** — чекбокс «Учёт в отчёте» строки (макет `[31-03]`, с «Выделить все» по ГО на
|
|
||||||
фронте — пакетное действие фронта, не отдельная ручка). Комментарий обязателен **только** когда `include_in_kfo = true`
|
|
||||||
(ТЗ 6.7). Положительная сумма отмеченных строк уходит в `kfo_result_line` с `line_code = 'GOODWILL'`
|
|
||||||
(раздел I КФО, допущение OQ-37) — расчёт и запись при построении результата КФО, не в этом контракте.
|
|
||||||
- **Признак расчёта гудвилла (`goodwill_enabled`)** — переключатель версии (`consolidation_version.goodwill_enabled`,
|
|
||||||
решено OQ-27: живёт на вкладке «Гудвилл», не в модалке создания). Сейчас в контракте нет ручки, которая бы его
|
|
||||||
выставляла (только чтение через `VersionCard`, `dev/api-contract-slice-4.md`) — добавляется здесь (§3.2).
|
|
||||||
Выключен — таблица показывается как есть (доли и комментарии не теряются), но ничего не уходит в КФО.
|
|
||||||
|
|
||||||
## 3. Эндпоинты
|
|
||||||
|
|
||||||
| Метод и путь | Право | Запрос | Ответ | Ошибки |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| `GET /consolidations/versions/{version_id}/goodwill` | VIEW | — | 200 `GoodwillTableOut` | 404 `NOT_FOUND` |
|
|
||||||
| `PUT /consolidations/versions/{version_id}/goodwill-settings` | EDIT | `GoodwillSettingsIn` | 200 `GoodwillTableOut` | 404, 409 правок версии |
|
|
||||||
| `PUT /consolidations/versions/{version_id}/goodwill/{head_legal_entity_id}/{subsidiary_legal_entity_id}` | EDIT | `GoodwillRowIn` | 200 `GoodwillRowOut` | 404 `NOT_FOUND` (версия или пара не из периметра), 409 правок версии, 422 `COMMENT_REQUIRED`, 422 `INVALID_SHARE` |
|
|
||||||
| `GET /consolidations/versions/{version_id}/goodwill/export` | VIEW | — | файл xlsx | 404 `NOT_FOUND` |
|
|
||||||
|
|
||||||
### 3.1 `GET …/goodwill` — таблица
|
|
||||||
|
|
||||||
Все строки одним ответом (пар — единицы, не десятки; без страниц). Порядок: по имени ГО, внутри — по имени ДО.
|
|
||||||
Группировку по ГО и подсчёт «Выделить все» делает фронт. Пустой периметр (только ГО, без ДО) — `rows = []`, это не ошибка.
|
|
||||||
|
|
||||||
### 3.2 `PUT …/goodwill-settings` — переключатель версии
|
|
||||||
|
|
||||||
Меняет `consolidation_version.goodwill_enabled`. Аудит — новое действие в `audit_action` (например,
|
|
||||||
`GOODWILL_ENABLED_CHANGE`), результат КФО помечается устаревшим при любом изменении значения (как в AURORA-1417).
|
|
||||||
Выключение не трогает сохранённые строки (`ownership_share`, `comment`, `include_in_kfo` остаются).
|
|
||||||
|
|
||||||
### 3.3 `PUT …/goodwill/{head_legal_entity_id}/{subsidiary_legal_entity_id}` — сохранить строку
|
|
||||||
|
|
||||||
Сохраняет **все поля строки целиком** (как в `PUT …/corrections/aggregates/…` — текущие `ownership_share`,
|
|
||||||
`include_in_kfo` и `comment` приходят каждый раз, частичного `PATCH` нет).
|
|
||||||
|
|
||||||
- `head_legal_entity_id` должен быть членом периметра с `is_head_entity = true`, `subsidiary_legal_entity_id` — любым
|
|
||||||
другим членом периметра; иначе 404 `NOT_FOUND` (пары не существует).
|
|
||||||
- `ownership_share` — строка `0`…`100`, до 4 знаков после запятой (`numeric(7,4)`); вне диапазона или больше 4 знаков —
|
|
||||||
422 `INVALID_SHARE`.
|
|
||||||
- `comment` обязателен, если `include_in_kfo = true`; пустой после обрезки в этом случае — 422 `COMMENT_REQUIRED`
|
|
||||||
(как `COMMENT_REQUIRED` в контракте корректировок); не длиннее 1000 символов. При `include_in_kfo = false` комментарий
|
|
||||||
необязателен и может быть `null`.
|
|
||||||
- `financial_investment_amount`, `charter_capital_amount`, `additional_capital_amount`, `participation_amount`,
|
|
||||||
`goodwill_amount` в тело **не входят** — это вычисляемые поля, пересчитываются сервером из текущей ОСВ версии при
|
|
||||||
каждом ответе, в запросе их нет.
|
|
||||||
- Аудит `GOODWILL_ROW_EDIT` (поле нужно добавить в `audit_action`, как `CORRECTION_*` для корректировок) с `before`/`after`;
|
|
||||||
результат КФО помечается устаревшим.
|
|
||||||
|
|
||||||
Ответ — пересчитанная строка (а не вся таблица — в отличие от корректировок, строки гудвилла друг на друга не влияют).
|
|
||||||
|
|
||||||
### 3.4 `GET …/export` — xlsx
|
|
||||||
|
|
||||||
Таблица гудвилла одним листом, группировка по ГО, итоговая строка «Всего» (макет `[31-03]`). Имя файла —
|
|
||||||
`Гудвилл_<название ГК>_<год>_<квартал>.xlsx` в `Content-Disposition` (как в экспорте корректировок). Аудит `EXPORT`.
|
|
||||||
|
|
||||||
## 4. Схемы
|
|
||||||
|
|
||||||
Поля без `?` есть всегда; `null` указан явно.
|
|
||||||
|
|
||||||
**`GoodwillTableOut`**
|
|
||||||
```
|
|
||||||
version_id uuid
|
|
||||||
goodwill_enabled bool
|
|
||||||
editable bool
|
|
||||||
read_only_reason null | "VERSION_NOT_EDITABLE" | "LOCK_REQUIRED" | "LOCKED_BY_OTHER"
|
|
||||||
rows[] GoodwillRowOut
|
|
||||||
totals GoodwillTotalsOut
|
|
||||||
```
|
|
||||||
|
|
||||||
**`GoodwillRowOut`**
|
|
||||||
```
|
|
||||||
head_legal_entity Ref {id, name}
|
|
||||||
subsidiary_legal_entity Ref
|
|
||||||
ownership_share share | null % 4 знака; null — ещё не введена
|
|
||||||
financial_investment_amount money
|
|
||||||
charter_capital_amount money
|
|
||||||
additional_capital_amount money
|
|
||||||
participation_amount money
|
|
||||||
goodwill_amount money может быть отрицательным
|
|
||||||
include_in_kfo bool
|
|
||||||
comment string | null
|
|
||||||
updated_by Ref | null
|
|
||||||
updated_at datetime | null
|
|
||||||
```
|
|
||||||
|
|
||||||
**`GoodwillTotalsOut`** — сумма по всем строкам ответа: `{financial_investment_amount, participation_amount,
|
|
||||||
goodwill_amount}`, плюс `goodwill_in_kfo_amount` — сумма `goodwill_amount` только по строкам с `include_in_kfo = true`
|
|
||||||
и положительным `goodwill_amount` (то, что реально уйдёт в `kfo_result_line.line_code = 'GOODWILL'`).
|
|
||||||
|
|
||||||
**`GoodwillRowIn`** `{ownership_share (share-строка 0…100), include_in_kfo (bool), comment (string | null, обязателен
|
|
||||||
при include_in_kfo = true, ≤ 1000 символов)}`.
|
|
||||||
|
|
||||||
**`GoodwillSettingsIn`** `{goodwill_enabled (bool)}`.
|
|
||||||
|
|
||||||
## 5. Открытые вопросы (AURORA-1369, срок 02.11.2026)
|
|
||||||
|
|
||||||
- **OQ-10** — входит ли стр. 1350 (добавочный капитал) в базу участия. Допущение сейчас: **нет**, `participation_amount`
|
|
||||||
считается только от `charter_capital_amount`; `additional_capital_amount` хранится и показывается в таблице (колонка
|
|
||||||
есть и в DBML, и на фронте — макет её не показывает явно, но поле в `goodwill` есть с пометкой ADD), но не входит в
|
|
||||||
формулу. Если методологи подтвердят, что 1350 нужен — меняется только формула на бэкенде, контракт не меняется.
|
|
||||||
- **OQ-37** — код строки «Гудвилл» в результате КФО: служебный `GOODWILL` (как в DBML, принято этим контрактом) или
|
|
||||||
`1105` как в распознанной форме ББ (ФСБУ 4/2023). Контракта это не касается напрямую (он про таблицу `goodwill`, не
|
|
||||||
про `kfo_result_line`), но влияет на отдельную задачу построения КФО — не блокирует 1543/1544.
|
|
||||||
- Несколько `is_head_entity` в одном периметре — не встречалось на фикстурах, поведение не проверено отдельно.
|
|
||||||
|
|
||||||
**Фронт (AURORA-1544) ничего из этого не ждёт** — строит экран по схемам §4 на фейковых данных; поля не меняются от
|
|
||||||
исхода открытых вопросов, меняются только числа внутри них.
|
|
||||||
|
|
||||||
## 6. Пример
|
|
||||||
|
|
||||||
`GET /api/v1/consolidations/versions/7b…/goodwill` (сокращено):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version_id": "7b1c…",
|
|
||||||
"goodwill_enabled": true,
|
|
||||||
"editable": true,
|
|
||||||
"read_only_reason": null,
|
|
||||||
"rows": [
|
|
||||||
{
|
|
||||||
"head_legal_entity": {"id": "a…", "name": "АО «Русский бисквит»"},
|
|
||||||
"subsidiary_legal_entity": {"id": "b…", "name": "ООО «Пекарня Счастливочная»"},
|
|
||||||
"ownership_share": "100.0000",
|
|
||||||
"financial_investment_amount": "5000000.00",
|
|
||||||
"charter_capital_amount": "4500000.00",
|
|
||||||
"additional_capital_amount": "0.00",
|
|
||||||
"participation_amount": "4500000.00",
|
|
||||||
"goodwill_amount": "500000.00",
|
|
||||||
"include_in_kfo": true,
|
|
||||||
"comment": "Доля подтверждена уставом от 01.01.2024",
|
|
||||||
"updated_by": {"id": "u…", "name": "Иванов И. И."},
|
|
||||||
"updated_at": "2026-10-30T12:00:00+03:00"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"totals": {
|
|
||||||
"financial_investment_amount": "5000000.00",
|
|
||||||
"participation_amount": "4500000.00",
|
|
||||||
"goodwill_amount": "500000.00",
|
|
||||||
"goodwill_in_kfo_amount": "500000.00"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@ -1,278 +0,0 @@
|
|||||||
openapi: 3.1.0
|
|
||||||
info:
|
|
||||||
title: АРМ КА — таблица гудвилла
|
|
||||||
version: 0.1.0-draft
|
|
||||||
description: >
|
|
||||||
Черновик контракта AURORA-1543 (бэкенд) / AURORA-1544 (фронт). Текстовое описание, источники и открытые вопросы —
|
|
||||||
dev/api-contract-goodwill.md; оба файла держатся одинаковыми. Деньги — строки с двумя знаками в рублях, доля
|
|
||||||
владения — строка с четырьмя знаками. Чтение — CONSOLIDATION_VIEW, правки — CONSOLIDATION_EDIT.
|
|
||||||
servers:
|
|
||||||
- url: /api/v1
|
|
||||||
security:
|
|
||||||
- bearerAuth: []
|
|
||||||
|
|
||||||
paths:
|
|
||||||
/consolidations/versions/{version_id}/goodwill:
|
|
||||||
get:
|
|
||||||
operationId: getGoodwillTable
|
|
||||||
summary: Таблица гудвилла версии
|
|
||||||
x-permission: CONSOLIDATION_VIEW
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/VersionId'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Строки пар (ГО, ДО) периметра, итоги
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: { $ref: '#/components/schemas/GoodwillTableOut' }
|
|
||||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
||||||
'403': { $ref: '#/components/responses/Forbidden' }
|
|
||||||
'404': { $ref: '#/components/responses/DomainError' }
|
|
||||||
|
|
||||||
/consolidations/versions/{version_id}/goodwill-settings:
|
|
||||||
put:
|
|
||||||
operationId: saveGoodwillSettings
|
|
||||||
summary: Переключатель расчёта гудвилла версии (consolidation_version.goodwill_enabled)
|
|
||||||
description: >
|
|
||||||
Аудит GOODWILL_ENABLED_CHANGE; результат КФО устаревает при любом изменении значения. Выключение не стирает
|
|
||||||
сохранённые строки (ownership_share, comment, include_in_kfo).
|
|
||||||
x-permission: CONSOLIDATION_EDIT
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/VersionId'
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: { $ref: '#/components/schemas/GoodwillSettingsIn' }
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Таблица с обновлённым флагом
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: { $ref: '#/components/schemas/GoodwillTableOut' }
|
|
||||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
||||||
'403': { $ref: '#/components/responses/Forbidden' }
|
|
||||||
'404': { $ref: '#/components/responses/DomainError' }
|
|
||||||
'409': { $ref: '#/components/responses/DomainError' }
|
|
||||||
|
|
||||||
/consolidations/versions/{version_id}/goodwill/{head_legal_entity_id}/{subsidiary_legal_entity_id}:
|
|
||||||
put:
|
|
||||||
operationId: saveGoodwillRow
|
|
||||||
summary: Сохранить строку гудвилла (создать или заменить целиком)
|
|
||||||
description: >
|
|
||||||
Аудит GOODWILL_ROW_EDIT; результат КФО устаревает. Вычисляемые поля (financial_investment_amount,
|
|
||||||
charter_capital_amount, additional_capital_amount, participation_amount, goodwill_amount) в тело запроса не
|
|
||||||
входят — пересчитываются сервером. 404 — версия или пара (head/subsidiary) не из периметра; 409 — правок
|
|
||||||
версии; 422 — COMMENT_REQUIRED (include_in_kfo = true без комментария), INVALID_SHARE (вне 0…100 или больше
|
|
||||||
4 знаков после запятой).
|
|
||||||
x-permission: CONSOLIDATION_EDIT
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/VersionId'
|
|
||||||
- $ref: '#/components/parameters/HeadLegalEntityId'
|
|
||||||
- $ref: '#/components/parameters/SubsidiaryLegalEntityId'
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: { $ref: '#/components/schemas/GoodwillRowIn' }
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Пересчитанная строка
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: { $ref: '#/components/schemas/GoodwillRowOut' }
|
|
||||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
||||||
'403': { $ref: '#/components/responses/Forbidden' }
|
|
||||||
'404': { $ref: '#/components/responses/DomainError' }
|
|
||||||
'409': { $ref: '#/components/responses/DomainError' }
|
|
||||||
'422': { $ref: '#/components/responses/DomainOrValidationError' }
|
|
||||||
|
|
||||||
/consolidations/versions/{version_id}/goodwill/export:
|
|
||||||
get:
|
|
||||||
operationId: exportGoodwillTable
|
|
||||||
summary: Выгрузка таблицы гудвилла в xlsx
|
|
||||||
description: Аудит EXPORT.
|
|
||||||
x-permission: CONSOLIDATION_VIEW
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/VersionId'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Файл xlsx
|
|
||||||
content:
|
|
||||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
|
|
||||||
schema: { type: string, format: binary }
|
|
||||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
||||||
'403': { $ref: '#/components/responses/Forbidden' }
|
|
||||||
'404': { $ref: '#/components/responses/DomainError' }
|
|
||||||
|
|
||||||
components:
|
|
||||||
securitySchemes:
|
|
||||||
bearerAuth: { type: http, scheme: bearer, bearerFormat: JWT }
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
VersionId:
|
|
||||||
name: version_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
schema: { type: string, format: uuid }
|
|
||||||
HeadLegalEntityId:
|
|
||||||
name: head_legal_entity_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
description: Член периметра с is_head_entity = true
|
|
||||||
schema: { type: string, format: uuid }
|
|
||||||
SubsidiaryLegalEntityId:
|
|
||||||
name: subsidiary_legal_entity_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
description: Любой другой член периметра
|
|
||||||
schema: { type: string, format: uuid }
|
|
||||||
|
|
||||||
responses:
|
|
||||||
Unauthorized:
|
|
||||||
description: Нет или плохой токен
|
|
||||||
Forbidden:
|
|
||||||
description: Нет права
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties: { detail: { type: string } }
|
|
||||||
DomainError:
|
|
||||||
description: Доменная ошибка
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: { $ref: '#/components/schemas/DomainErrorOut' }
|
|
||||||
DomainOrValidationError:
|
|
||||||
description: Доменная ошибка или массив ошибок валидации pydantic
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
oneOf:
|
|
||||||
- $ref: '#/components/schemas/DomainErrorOut'
|
|
||||||
- type: object
|
|
||||||
properties:
|
|
||||||
detail:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
loc: { type: array, items: {} }
|
|
||||||
msg: { type: string }
|
|
||||||
type: { type: string }
|
|
||||||
|
|
||||||
schemas:
|
|
||||||
Money:
|
|
||||||
type: string
|
|
||||||
pattern: '^-?\d+\.\d{2}$'
|
|
||||||
description: Сумма в рублях, два знака; может быть отрицательной (goodwill_amount)
|
|
||||||
examples: ['500000.00']
|
|
||||||
Share:
|
|
||||||
type: [string, 'null']
|
|
||||||
pattern: '^\d{1,3}\.\d{4}$'
|
|
||||||
description: Доля владения, % (numeric(7,4)); null — ещё не введена
|
|
||||||
ShareIn:
|
|
||||||
type: string
|
|
||||||
pattern: '^\d{1,3}(\.\d{1,4})?$'
|
|
||||||
description: 0…100, до 4 знаков после запятой; вне диапазона — 422 INVALID_SHARE
|
|
||||||
Ref:
|
|
||||||
type: object
|
|
||||||
required: [id, name]
|
|
||||||
properties:
|
|
||||||
id: { type: string, format: uuid }
|
|
||||||
name: { type: string }
|
|
||||||
|
|
||||||
DomainErrorOut:
|
|
||||||
type: object
|
|
||||||
required: [detail]
|
|
||||||
properties:
|
|
||||||
detail:
|
|
||||||
type: object
|
|
||||||
required: [code, message]
|
|
||||||
properties:
|
|
||||||
code:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- NOT_FOUND
|
|
||||||
- VERSION_NOT_EDITABLE
|
|
||||||
- LOCK_REQUIRED
|
|
||||||
- LOCKED_BY_OTHER
|
|
||||||
- COMMENT_REQUIRED
|
|
||||||
- INVALID_SHARE
|
|
||||||
message: { type: string, description: Текст по-русски }
|
|
||||||
additionalProperties: true
|
|
||||||
|
|
||||||
GoodwillTableOut:
|
|
||||||
type: object
|
|
||||||
required: [version_id, goodwill_enabled, editable, read_only_reason, rows, totals]
|
|
||||||
properties:
|
|
||||||
version_id: { type: string, format: uuid }
|
|
||||||
goodwill_enabled: { type: boolean }
|
|
||||||
editable: { type: boolean }
|
|
||||||
read_only_reason:
|
|
||||||
type: [string, 'null']
|
|
||||||
enum: [null, VERSION_NOT_EDITABLE, LOCK_REQUIRED, LOCKED_BY_OTHER]
|
|
||||||
rows:
|
|
||||||
type: array
|
|
||||||
items: { $ref: '#/components/schemas/GoodwillRowOut' }
|
|
||||||
totals: { $ref: '#/components/schemas/GoodwillTotalsOut' }
|
|
||||||
|
|
||||||
GoodwillRowOut:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- head_legal_entity
|
|
||||||
- subsidiary_legal_entity
|
|
||||||
- ownership_share
|
|
||||||
- financial_investment_amount
|
|
||||||
- charter_capital_amount
|
|
||||||
- additional_capital_amount
|
|
||||||
- participation_amount
|
|
||||||
- goodwill_amount
|
|
||||||
- include_in_kfo
|
|
||||||
- comment
|
|
||||||
- updated_by
|
|
||||||
- updated_at
|
|
||||||
properties:
|
|
||||||
head_legal_entity: { $ref: '#/components/schemas/Ref' }
|
|
||||||
subsidiary_legal_entity: { $ref: '#/components/schemas/Ref' }
|
|
||||||
ownership_share: { $ref: '#/components/schemas/Share' }
|
|
||||||
financial_investment_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
charter_capital_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
additional_capital_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
participation_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
goodwill_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
include_in_kfo: { type: boolean }
|
|
||||||
comment: { type: [string, 'null'] }
|
|
||||||
updated_by:
|
|
||||||
oneOf:
|
|
||||||
- $ref: '#/components/schemas/Ref'
|
|
||||||
- type: 'null'
|
|
||||||
updated_at: { type: [string, 'null'], format: date-time }
|
|
||||||
|
|
||||||
GoodwillTotalsOut:
|
|
||||||
type: object
|
|
||||||
required: [financial_investment_amount, participation_amount, goodwill_amount, goodwill_in_kfo_amount]
|
|
||||||
properties:
|
|
||||||
financial_investment_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
participation_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
goodwill_amount: { $ref: '#/components/schemas/Money' }
|
|
||||||
goodwill_in_kfo_amount:
|
|
||||||
allOf: [{ $ref: '#/components/schemas/Money' }]
|
|
||||||
description: Сумма goodwill_amount только по строкам include_in_kfo = true с положительным goodwill_amount
|
|
||||||
|
|
||||||
GoodwillRowIn:
|
|
||||||
type: object
|
|
||||||
required: [ownership_share, include_in_kfo, comment]
|
|
||||||
properties:
|
|
||||||
ownership_share: { $ref: '#/components/schemas/ShareIn' }
|
|
||||||
include_in_kfo: { type: boolean }
|
|
||||||
comment:
|
|
||||||
type: [string, 'null']
|
|
||||||
maxLength: 1000
|
|
||||||
description: Обязателен (непустой после обрезки), если include_in_kfo = true
|
|
||||||
|
|
||||||
GoodwillSettingsIn:
|
|
||||||
type: object
|
|
||||||
required: [goodwill_enabled]
|
|
||||||
properties:
|
|
||||||
goodwill_enabled: { type: boolean }
|
|
||||||
@ -626,9 +626,8 @@ variance_percent?, exceeds_threshold, one_sided, skipped_osv_entry_count, osv_en
|
|||||||
|
|
||||||
Префикс — `/consolidations/versions/{version_id}`. Права — `CONSOLIDATION_VIEW` и policy версии (чужая — 404); блокировка не
|
Префикс — `/consolidations/versions/{version_id}`. Права — `CONSOLIDATION_VIEW` и policy версии (чужая — 404); блокировка не
|
||||||
нужна. Считается на лету по сводной ОСВ версии и правилам ББ зафиксированной версии правил (`elimination_rules_version`,
|
нужна. Считается на лету по сводной ОСВ версии и правилам ББ зафиксированной версии правил (`elimination_rules_version`,
|
||||||
с DBML v5.23 есть у каждой версии), ничего не хранится. Каждое правило по-прежнему считается отдельно здесь (строка общего
|
с DBML v5.23 есть у каждой версии), ничего не хранится. Каждое правило считается отдельно: строка общего счёта может войти в
|
||||||
счёта может войти в несколько правил): двойной учёт снимается не на этом экране, а ручным разнесением строк
|
несколько правил (двойной учёт снимает AURORA-1751).
|
||||||
(`vgo_assignment`, AURORA-1751) в таблице корректировок — см. `dev/api-contract-corrections.md`.
|
|
||||||
|
|
||||||
| Метод и путь | Запрос | Ответ | Ошибки |
|
| Метод и путь | Запрос | Ответ | Ошибки |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|||||||
@ -105,14 +105,3 @@ python scripts/mockup_crawl_extra.py docs/mockup/dkb_24.09.26/dkb_24.09.26.html
|
|||||||
|
|
||||||
`mockup_crawl_extra.py` пишет `docs/mockup/report-extra.json` (заголовки, кнопки, поля и текст каждого снятого состояния) —
|
`mockup_crawl_extra.py` пишет `docs/mockup/report-extra.json` (заголовки, кнопки, поля и текст каждого снятого состояния) —
|
||||||
по нему можно сверять подписи без открытия PNG.
|
по нему можно сверять подписи без открытия PNG.
|
||||||
|
|
||||||
## Макет `dkb_23.10.26_corrections` — отдельный, только «Таблица корректировки»
|
|
||||||
|
|
||||||
Второй макет, не связанный с обходом выше. Это SPA-бандл (`dkb_23.10.26_corrections.html`, React, навигация —
|
|
||||||
`localStorage`, без роутов), который Лысенко приложил к вики-странице DOC-272613 («Корректировка данных агрегатом»,
|
|
||||||
вложение `#193`) — дизайн-референс для AURORA-1541/1540/1751 (агрегаты правил элиминации, нераспределённые записи,
|
|
||||||
ручной перенос между правилами). При открытии сразу стоит на вкладке «Таблица корректировки» одной из консолидаций.
|
|
||||||
|
|
||||||
Экраны headless-браузером не сняты (не обход всего макета, одна вкладка) — открывать сам файл в браузере или через
|
|
||||||
Playwright. Сверка с аналитикой Лысенко и расхождения — в `AURORA-1395` (комментарий `[design-23-10-check]`), а не
|
|
||||||
здесь.
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 701 KiB After Width: | Height: | Size: 604 KiB |
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 90 KiB |
@ -1,256 +0,0 @@
|
|||||||
# АРМ КА — описание сущностей модели данных v5.24
|
|
||||||
|
|
||||||
Источник истины по структуре — `data-model-v5.24.dbml` (полный). Диаграммы по частям — `diagrams/` (part01…09 svg/png; перерисовываются `make diagrams` из текущего DBML). Здесь — что каждая таблица значит для бизнеса, как связана с соседями и откуда взялась.
|
|
||||||
|
|
||||||
## Что изменилось в v5.24 (AURORA-1751/1540/1541; RSHB/arm-ka-docs#196; миграция уже слита в arm-core main)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | Новая таблица `vgo_assignment` | Ручное решение аналитика по строке сводной ОСВ сверх автоматического разнесения по правилам ВГО (`context/05-domain-rules.md` §5): привязка к правилу, снятие привязки, «не учитывать в КФО» у нераспределённой строки. Само разнесение остаётся чистой функцией и не хранится (DECISION-5) — таблица только перекрывает его решением аналитика. Одна действующая запись на строку ОСВ в версии и типе отчётности |
|
|
||||||
| 2 | Поле `side` не добавлено | Черновик контракта (`dev/api-contract-corrections.md` §5) предполагал отдельную позицию для строки с одновременно дебетовым и кредитовым сальдо; в уже слитой миграции 0011 решение одно на обе позиции строки, не по стороне |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.23.
|
|
||||||
|
|
||||||
## Что изменилось в v5.23 (решения владельца 23.10.2026; RSHB/arm-ka-docs#164, AURORA-1536)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `consolidation_version.elimination_rules_version` — `not null` | Версия правил элиминации (`elimination_rule.version_no`), действующая на **дату создания** версии консолидации (OQ-174), фиксируется при создании и дальше не меняется. По ней считаются шахматка по правилу-пресету и пары ВГО (`context/05-domain-rules.md` §4, §5). Вариант «пусто — значит последние открытые правила» отвергнут: после публикации новой версии правил старые консолидации молча пересчитались бы по ней. Версия консолидации без действующих правил не создаётся |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.22. Реализация — одна миграция в `arm-core`: существующим версиям проставляется номер
|
|
||||||
версии правил, действовавшей на дату `created_at` (по Москве; если такой нет — последней открытой), затем `NOT NULL`; откат
|
|
||||||
снимает только ограничение.
|
|
||||||
|
|
||||||
## Что изменилось в v5.22 (22.10.2026; AURORA-1446)
|
|
||||||
|
|
||||||
Из описания `comment.object_type` убраны устаревшие варианты `chess_cell` и `kfo_result_line`.
|
|
||||||
Сохранены `document | consolidation_version | …`; сейчас используется привязка к документу. Комментарий к правке пары ВГО хранится
|
|
||||||
в `correction.comment`, к строкам КФО — в `kfo_result_line.comment` и `client_kfo_line.comment`.
|
|
||||||
Структура БД не меняется, миграция не требуется. Предыдущие версии модели сохранены.
|
|
||||||
|
|
||||||
## Что изменилось в v5.21 (решение владельца 22.10.2026; RSHB/arm-ka-docs#149, AURORA-1442)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `audit_action` + `FILE_REPROCESS` | Повторная обработка файла пользователем (`context/05-domain-rules.md` §9.5 п. 13) — отдельное событие журнала, раздел `PRIMARY_DOCUMENTS`. Файл не загружается заново: создаётся новое `recognition_job` с `attempt + 1` и становится `current_recognition_job_id`. `FILE_UPLOAD` отвергнут — это приём нового файла, и журнал показал бы вторую загрузку, которой не было; `FILE_REPLACE` — автозамена одного файла другим. Событие пишется на файл (`object_type = stored_file`): `before_value` — прежнее задание (`job_id`, `attempt`, `status`, `error_code`), `after_value` — новое (`job_id`, `attempt`); повторная обработка пакета версии — по событию на каждый отправленный файл, с `consolidation_id` в `after_value` |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.20. Реализация — одна миграция в `arm-core`: только добавляет значение enum; откат при
|
|
||||||
наличии событий `FILE_REPROCESS` останавливается с ошибкой (журнал append-only).
|
|
||||||
|
|
||||||
## Что изменилось в v5.20 (решения владельца 21.10.2026; RSHB/arm-ka-docs#147, OQ-161, AURORA-1442)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `document`: `is_modified` (not null, default `false`), `original_values` (jsonb), `modified_by` (FK → `app_user`), `modified_at` | Аналитик уточняет **период и счёт документа ОСВ**, если они распознаны неверно (`context/05-domain-rules.md` §9.5 п. 12; OQ-161, вариант 2). Новые значения пишутся **на месте** — в `period_year`, `period_quarter`, `is_cumulative`, `osv_account_code` той же строки `document`; при первой правке прежние значения этих четырёх полей копируются в `original_values` и дальше не меняются; `is_modified` — текущие отличаются от исходных. Так же устроена правка сумм `osv_entry` (`original_values`) — первичка не мутируется: файл, JSON результата в S3 и исходные значения остаются как пришли. Копия `document` (второй документ с другим `file_ordinal`) отвергнута: `file_ordinal` — номер документа в `documents[]` результата, а не номер редакции; копия задвоила бы строки ОСВ, подтверждение и привязки. Строки `osv_entry` при смене счёта не меняются (у них свои коды субсчетов) |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.19. Реализация — одна миграция в `arm-core`: только добавляет колонки (`is_modified =
|
|
||||||
false`, остальные `NULL`); откат на данных с `is_modified = true` останавливается с ошибкой (потерялись бы исходные значения).
|
|
||||||
|
|
||||||
## Что изменилось в v5.19 (решения владельца 17.10.2026, Р-1…Р-19; RSHB/arm-ka-docs#116)
|
|
||||||
|
|
||||||
Change `openspec/changes/archive/2026-10-19-unified-statement-normalization`: формы ББ/ОФР нормализуются общим кодом сервисов, контракт
|
|
||||||
результата v2.0; ядро хранит результат как пришёл (`context/05-domain-rules.md` §11, §9.3, §9.5).
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `financial_statement_line`: снята уникальность `(meta_id, line_code)` (индекс остаётся неуникальным); новые `row_index`, `source_ref` (частичный уникальный `(meta_id, source_ref) WHERE source_ref IS NOT NULL`), `line_status` (enum `statement_line_status`: `ok` / `unreadable` / `missing` / `detail`), `line_code_text`, `parent_row_index`, `needs_review`, `is_confirmed` | Повтор кода — законный случай (один код, разные наименования; Р-4): хранятся все строки, чтения «по коду» идут через агрегат формы по коду (сумма строк `ok`, не хранится — DECISION-5). Строки без кода хранятся со статусом (Р-8, отменяет OQ-36 / OQ-141): `detail` — расшифровка под строкой с кодом, в §7, агрегаты, КФО и ЕХ не входит; `unreadable` / `missing` блокируют подтверждение, пока у них есть суммы (Р-13). Порядок и положение в источнике (`row_index`, `source_ref`) нужны экрану, чтобы показать строку и предупреждение у места. **Признак «строка создана правкой аналитика» — `row_index IS NULL`** (прежний признак `line_code_original_value IS NULL` не годится: у строк `unreadable` / `missing` / `detail` источника кода тоже нет); `line_code_original_value` — код из результата как пришёл, правками не меняется |
|
|
||||||
| 2 | `financial_statement_value`: `source_text`, `needs_review`, `is_confirmed`, `confirmed_by`, `confirmed_at` | `needs_review` **хранится** (пересматривает п. 4 v5.13): его считает сервис («подтверждение проверкой» §7 — Р-5, Р-7), ядро пересчитывает только после правок; подтверждение ячейки без правки аналитиком переживает пересчёт. Сырой текст источника — подсказка ячейки |
|
|
||||||
| 3 | `financial_statement_column`: `period_kind`, `label_confidence` | Относительные подписи бланка 66н («На отчётную дату», «На 31 декабря предыдущего года») — дата колонки от эффективной даты документа, ядро досчитывает после правки даты отчёта (Р-6); колонка без даты остаётся с `period_kind = none` |
|
|
||||||
| 4 | `financial_statement_meta`: `form_variant` (enum `statement_form_variant`), `needs_review_fields`, `header_source` (jsonb), `organization_name_original_value`, `organization_name_modified` | Упрощённая форма — свой набор соотношений §7 (`NULL` — документ до v5.19 или результат 1.x, набор полный); поля шапки, требующие проверки (сейчас — неуверенная дата отчёта); сырой текст и уверенность всех полей шапки результата (`<поле>_text`, `<поле>_confidence`) — подсказка у поля на экране; наименование организации правится, как ИНН |
|
|
||||||
| 5 | Новая таблица `document_issue` + enum `document_issue_kind`, `issue_severity`, `issue_source` | Предупреждения и нарушения §7 документа с уровнем и привязкой к строкам (`line_ids`), колонке (`column_id`, а вне колонок формы — `column_index`), полю шапки; у нарушения — `codes` (коды цели и членов); как пришли от сервиса или посчитаны ядром. Очищается при перезагрузке файла; при пересчёте после правки `relation` / `soft` заменяются целиком, `warning` сервиса сохраняются, кроме снимаемых правкой. **Единственный источник нарушений** для экрана и подтверждения: для результатов 1.x ядро один раз при приёме считает §7 копией `arm_statements` и пишет записи с `source = core`; формы до миграции не пересчитываются (база очищается, Р-24). Ведётся и у документов ОСВ — предупреждения листа `OSV_*` результата 2.0 |
|
|
||||||
| 6 | `recognition_job.statements_version` | Коммит копии общего кода `arm_statements`, которым посчитан результат (`service.statements_version`); расхождение с копией ядра при пересчёте — `info` |
|
|
||||||
| 7 | `document`: уникальность `(stored_file_id, document_type, osv_account_code)` — частичная, кроме `BALANCE` / `INCOME`; новая колонка `file_ordinal` + уникальный `(stored_file_id, file_ordinal)` | Вторая форма того же типа в файле хранится (Р-10, Р-15); при двух формах одного типа и периода ни одна не автоподтверждается, подтверждение второй при подтверждённой первой — 409 `DUPLICATE_FORM` (Р-18). ОСВ по счёту, расшифровка и прочий — по-прежнему один на тип на файл (§9.5 п. 2) |
|
|
||||||
| 8 | `related_group.crm_id` (`varchar`, nullable, unique) | ID ГСК/ГСЗ в ЕЦП CRM из форм `51-03`, `51-04`, `77-02` (AURORA-1619, arm-core#112, OQ-33); вводится вручную, импорт ЕЦП не заполняет (OQ-91); уникален, как `crm_id` ГК и ЮЛ |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.18. Реализация — одна миграция в `arm-core`; перед выкладкой база очищается от форм
|
|
||||||
ББ/ОФР (решение владельца 18.10.2026, Р-24), поэтому миграция только добавляет колонки, таблицу и значения по умолчанию и
|
|
||||||
§7 по существующим формам не считает. Заполнение (миграция проходит и на непустой базе): `row_index` — `NULL` у строк,
|
|
||||||
созданных правкой (`line_code_original_value IS NULL`), у остальных — по возрастанию `line_code` в форме; `line_status =
|
|
||||||
ok`, `needs_review` значений — **НЕ** `values_ok`, `source_text = string_value`, `period_kind` — `absolute` при
|
|
||||||
`period_date`, иначе `none`, `header_source` — из `*_original_value`, `file_ordinal` — по `(created_at, id)` в файле;
|
|
||||||
`document_issue` пуст — у форм, оставшихся в неочищенной базе, нарушения до первой правки не показываются. Откат
|
|
||||||
гарантирован на пустой БД и на данных, которые v5.18 представляет без потери первичных данных и ручного ввода (без
|
|
||||||
повторов кода, строк без кода, двух форм одного типа в файле, `crm_id` групп и правленого наименования организации); на
|
|
||||||
таких данных `downgrade` останавливается с ошибкой, а производные колонки v5.19 и `document_issue` удаляет с записью в
|
|
||||||
журнал миграции.
|
|
||||||
|
|
||||||
## Что изменилось в v5.18 (решения владельца 09.10.2026, RSHB/arm-ka-docs#90)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `stored_file.consolidation_id` (`uuid`, nullable, FK → `consolidation`) + индекс `(legal_entity_id, consolidation_id)` | Файл загружается **в консолидацию** (период ГК) и становится основой её «рабочего стола» (полный состав стола — `context/05-domain-rules.md` §9.5 п. 11), общего для всех версий периода; `NULL` — загрузка без контекста (страница ЮЛ, монозаёмщики, API). Панель «Первичные документы» в версии показывает рабочий стол консоли для ЮЛ плюс файлы с документами, привязанными к версиям консоли. Что использует конкретная версия — по-прежнему `consolidation_document`; заморозка после `VERIFIED` — привязки и `ex_snapshot`. Привязка файла к **версии** (`consolidation_file`) отвергнута: рабочие версии периода делят первичку, а новая версия после `VERIFIED` подхватывает подтверждённое (`openspec/changes/console-files/design.md`) |
|
|
||||||
| 2 | Note `recognition_job.reply_deadline` | Без изменения структуры: срок ответа OCR — 20 мин (OQ-159) |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.17. Реализация — миграция `0006` в `arm-core` (backfill `consolidation_id` по `consolidation_document`).
|
|
||||||
|
|
||||||
## Что изменилось в v5.17 (решение владельца 07.10.2026, пересмотр OQ-31, RSHB/arm-ka-docs#53)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `legal_entity.kpp` и `legal_entity_history.kpp` (`varchar`, nullable) | КПП юридического лица нужен (макет: колонка «КПП» на экранах `50`, `61`, поле форм `51-02`, `79`; фронтенд: «в ручке получения ЮЛ не приходит КПП»). Формат — 9 символов: 4 цифры, 2 символа (цифра или латинская A–Z), 3 цифры, иначе `INVALID_KPP`. У ИП (`entity_type = ИП`) КПП запрещён. Для ЮЛ необязателен: существующие записи и импорт без колонки остаются валидными. Уникален по-прежнему только `inn` (КПП — атрибут; сопоставление контрагентов ОСВ по ИНН не меняется). Изменение КПП — обычная правка со снимком в `legal_entity_history`. Отменяет п. 2 v5.14 («КПП не вводится») |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.16. Реализация — миграция `0005` в `arm-core`.
|
|
||||||
|
|
||||||
## Что изменилось в v5.16 (Срез 4, решения владельца 06.10.2026)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | Enum `counterparty_match_kind` (`INN`, `NAME`, `ALIAS`, `MANUAL`, `PROPAGATED`) и колонка `osv_entry.counterparty_match_kind` (nullable) | Способ сопоставления контрагента строки ОСВ хранится. `NULL` ⇔ `counterparty_id` `NULL`; `INN` / `NAME` / `ALIAS` — шаг автоматического резолва при маппинге (ИНН; точное имя; нормализованное имя ЮЛ или алиас); `MANUAL` — ручное сопоставление аналитиком; `PROPAGATED` — автоприменение по имени в версии (DOC-221278 п. 12–13). Вычислять на чтение нельзя: после ручного связывания появляется алиас, и ручное сопоставление неотличимо от `ALIAS`. Миграция `0004` заполняет колонку по уже смапленным строкам (ИНН без пробелов = `legal_entity.inn` → `INN`; `trim(name) = legal_entity.name` → `NAME`; остальные с `counterparty_id` → `ALIAS` — приближение: ручных сопоставлений до Среза 4 не было) |
|
|
||||||
| 2 | Семантика `consolidation_document` | Без изменения структуры: строка создаётся при подтверждении документа (по документам ЮЛ — членов периметра версий «в работе») и при добавлении ЮЛ в периметр (подтверждённые подходящие документы подхватываются); удаляется при замене/удалении файла и исключении ЮЛ из периметра. Для одной (ЮЛ, `document_type`, `osv_account_code`, период, признак нарастающего) привязан один документ — самый поздний `confirmed_at` (D-5 Среза 4) |
|
|
||||||
| 3 | Семантика `consolidation_lock` | Без изменения структуры: блокировка без истечения по времени — активна, пока её не снял владелец, администратор или не перехватил другой пользователь с `CONSOLIDATION_EDIT` по причине (OQ-119) |
|
|
||||||
|
|
||||||
Пробелы квартальной склейки и сама сводная ОСВ по-прежнему не хранятся (DECISION-5). Остальные таблицы и enum — как в v5.15 (43 таблицы; enum — 28). Реализация — миграция `0004` в `arm-core`.
|
|
||||||
|
|
||||||
## Что изменилось в v5.15 (Срез 2c, решения владельца 05.10.2026)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `osv_data_type` + `NU` (после `BU`) | Строки налогового учёта из ОСВ «БУ, НУ» хранятся в `osv_entry` как есть (OQ-45); контракт результата допускал `NU` с v1.0, ядро раньше такой строки принять не могло |
|
|
||||||
| 2 | `app_user.email` — в нижнем регистре | Без изменения структуры: нормализация при записи (seed, администрирование), поиск по `lower(email)` сохраняется, уникальность — существующий UNIQUE по `email` (OQ-59). Expression-индекс `lower(email)` не вводится: сверка ORM↔DBML не умеет expression-индексы. Миграция `0003` приводит существующие email к нижнему регистру (при дублях по регистру — падает, слияние решает человек) |
|
|
||||||
| 3 | Семантика `stored_file.status`, `recognition_job.group_schema` | Без изменения структуры: `PROCESSING` ставит inbox по ack сервиса, `PROCESSED`/`ERROR` — цикл маппинга результата (OQ-67); маппинг выполняется отдельно от inbox (OQ-69); `group_schema` заполняет маппер; «Итого» в `osv_entry` не хранится (OQ-01) |
|
|
||||||
| 4 | Индексы `osv_entry (parent_entry_id)`, `osv_entry (recognition_job_id)`, `stored_file (current_recognition_job_id)` | Индексы для удаления файла и обхода дерева строк: без них проверки FK (в том числе self-FK `parent_entry_id`) при `DELETE /files/{id}` и очистке файла перед повторным маппингом шли seq scan'ом. Замер: DELETE документа 1000 строк 8,6 с → 16 мс на 200 тыс. строк. Обычные (не unique) индексы; закрывает OQ-117 (I-1 финального ревью Среза 2c) |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.14 (43 таблицы, 27 enum). Реализация — миграция `0003` в `arm-core`.
|
|
||||||
|
|
||||||
## Что изменилось в v5.14 (Срез 1, решения владельца 04.10.2026)
|
|
||||||
|
|
||||||
| № | Изменение | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | `related_group_member.kind` + индекс `(legal_entity_id, kind) WHERE is_current` | ЮЛ состоит **одновременно в одной ГСК и одной ГСЗ** (но только в одной группе каждого вида). Отменяет ANL-030 / v5.8 («только в одной ГСК или ГСЗ»). `kind` — денормализация `related_group.kind` (равенство проверяет ядро при записи, тест на рассинхрон); индекс не выразить через join, триггер не выбран. Закрывает OQ-32 |
|
|
||||||
| 2 | КПП | **Не вводится** *(отменено в v5.17: КПП введён)*. Поле есть только в макете; у `legal_entity` его нет, импорт его не читает (закрывал OQ-31 до v5.17) |
|
|
||||||
| 3 | Семантика `company_group.crm_id`, `base_regional_office_id` | Без изменения структуры: `crm_id` = «iD ГК» выгрузки ЕЦП CRM, `base_regional_office_id` = «Базовый РФ ГК в CRM»; флаги «входит в ГСК/ГСЗ» выгрузки → членство в одной неявной ГСК/ГСЗ на ГК (правила — `05-domain-rules.md` §13) |
|
|
||||||
| 4 | `audit_action` + `LEGAL_ENTITY_CHANGE`, `USER_CHANGE` | Журнал изменений ЮЛ (создание/изменение/закрытие/перевод, алиасы) и пользователей (создание/деактивация администратором) — ТЗ 8.2; в v5.13 для них не было действия (были `GROUP_CHANGE`, `MEMBERSHIP_CHANGE`, `USER_ROLE_CHANGE`) |
|
|
||||||
|
|
||||||
Остальные таблицы и enum — как в v5.13 (43 таблицы). Реализация — миграция `0002` в `arm-core`.
|
|
||||||
|
|
||||||
## Что изменилось в v5.13 (ревью 30.09–01.10)
|
|
||||||
|
|
||||||
| № | Замечание | Решение |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | Вместо RBAC «в коде» — роли **и** права | `permission` + `role_permission` возвращены; права только через роли, прямых назначений пользователю нет. Исключения — заведением роли |
|
|
||||||
| 2 | `osv_entry.parent_entry_id` не нужен — храним только строки с контрагентами | **Отложено.** Открыт вопрос: хранить ли строки уровня ACCOUNT для сверки «Σ контрагентов = итог счёта»; аналитика «Парсинг ОСВ» п.11 пока говорит «все строки» |
|
|
||||||
| 3 | `osv_group_schema` не особо нужна | Убрана. Схема шапки — `recognition_job.group_schema jsonb`; тип строки — `osv_entry.group_type` |
|
|
||||||
| 4 | `financial_statement_value.needs_review` непонятен | Убран: производное от `confidence` / `is_recognized`, порог может меняться |
|
|
||||||
| 5 | В `recognition_job` не нужны и `result`, и `raw_result` | Оставлен только `result_s3_object_id`. Сырой ответ ИС Сканирование — S3 `raw/{job_id}.json` по конвенции, в БД не регистрируется |
|
|
||||||
| 6 | `stored_file.fields_modified` — что это | Счётчик «исправлено полей» с карточки документа (v5.7). Убран — `COUNT(is_modified)` по данным |
|
|
||||||
| 6-бис | `document_type` не предусматривает несколько ОСВ или ББ+ОФР в одном файле | **Разделение файл / документ**: `stored_file` — физический файл, `document` — логическая единица (1:N). Чек-лист, проверки, подтверждение, FK разобранных данных, `consolidation_document`, `ex_snapshot_item` — на `document`. Замена (`superseded_by`) — файл целиком |
|
|
||||||
| 7 | `file_type`: убрать Р7 | Убран. `xls/xlsx/xlsb → tabular-parser`, `pdf/png/jpg → scan-gateway` |
|
|
||||||
|
|
||||||
**Принципы модели (коротко):**
|
|
||||||
- Первичка не редактируется: файлы и распознанные значения хранятся как пришли; правки аналитика — рядом (`is_modified`, `original_*`) и в аудите.
|
|
||||||
- Производные консолидации в работе (сводная ОСВ, пары ВГО, шахматка) **не хранятся** — считаются запросом. Хранятся только ручные решения (корректировки, гудвилл, подтверждение КФО клиента).
|
|
||||||
- При верификации всё замораживается в Единое хранилище (ЕХ) как самодостаточный срез, который не зависит от таблиц консолидации.
|
|
||||||
- Единственный писатель в БД — ядро `arm-core`; сервисы распознавания видят только NATS и S3.
|
|
||||||
- Права — данные (`role_permission`), ограничения по ЗО/филиалу/статусу — policy-слой поверх прав.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Часть 01 — Группы компаний, организации, филиалы
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `regional_office` | Филиал банка: головной офис (ГО, `is_head_office`) и региональные филиалы (РФ). Определяет зону видимости пользователя и закрепление ГК. | ← `company_group.base_regional_office_id`, `legal_entity.regional_office_id`, `app_user.regional_office_id` | ТЗ 8.1 |
|
|
||||||
| `company_group` | Группа компаний (ГК) — верхний уровень справочника клиентов. Текущее состояние (SCD4). `crm_id` = «iD ГК» из ЕЦП CRM (NULL, если создана вручную Куратором; v5.14 — семантика по выгрузке), внутренний `code`, закрепление за филиалом (`base_regional_office_id` = «Базовый РФ ГК в CRM»), флаг архива. | → `regional_office`; ← `legal_entity`, `related_group`, `consolidation` | ТЗ 6.1, Прил. 2; макет |
|
|
||||||
| `company_group_history` | Прошлые снимки ГК: каждое изменение — новая строка с `valid_from/valid_to`. | → `company_group` | ТЗ 6.10, 8.2 |
|
|
||||||
| `legal_entity` | Юридическое лицо / ИП. Натуральный ключ — ИНН; **v5.17:** `kpp` — КПП (9 символов, nullable, только у ЮЛ, не ключ). `company_group_id` = текущая ГК; **NULL = монозаёмщик**. `close_date` — закрыто/архивировано. | → `company_group`, `regional_office`; ← `related_group_member`, `stored_file`, `document`, `osv_entry.counterparty_id`, `consolidation_member` | ТЗ 6.1, Прил. 2; ЕХ 6.1 |
|
|
||||||
| `legal_entity_history` | Прошлые снимки ЮЛ + вид события (создание, изменение, переход в другую ГК, закрытие, вывод из ГК) и дата вступления в силу. | → `legal_entity` | ТЗ 8.2; макет |
|
|
||||||
| `legal_entity_alias` | Словарь написаний контрагента: как имя встречается в ОСВ и нормализованный ключ для матчинга. Пополняется при ручном сопоставлении. | → `legal_entity` | ТЗ 6.5 |
|
|
||||||
| `related_group` | ГСК или ГСЗ внутри ГК; `kind` различает. Для ГСК формируется КФО. Иерархия не поддерживается. **v5.19:** `crm_id` — ID группы в ЕЦП CRM (вручную, уникален). | → `company_group`; ← `related_group_member`, `consolidation.related_group_id` | ТЗ 6.1, Прил. 2 |
|
|
||||||
| `related_group_member` | Состав ГСК/ГСЗ — история членства (SCD2). **v5.14:** ЮЛ может одновременно входить в **одну ГСК и одну ГСЗ** (не более одной группы каждого вида): колонка `kind` = `related_group.kind` (денормализация) и частичный уникальный индекс `(legal_entity_id, kind) WHERE is_current`. Решение владельца 04.10.2026, отменяет ANL-030. | → `related_group`, `legal_entity` | ТЗ 6.1; макет [79] |
|
|
||||||
|
|
||||||
## Часть 02 — Пользователи, роли, права
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `app_user` | Пользователь АРМ. Аутентификация внешняя (РАИСА/SSO, `external_id`). Филиал (ГО/РФ), зона ответственности (ДКБ/ДМСБ/ДМ), табельный номер. Видимость данных = права роли + филиал + ЗО. | → `regional_office`; ← `user_role`, все `*_by` поля | ТЗ 8.1, Прил. 2 |
|
|
||||||
| `role` | Роли: seed из ТЗ 8.1 (КА РФ, КА ГО, РКА, КМ, Куратор ГО, Эксперт, Администратор; `is_system`) + роли, заведённые администратором для исключений. **v5.13:** код роли в бизнес-логике не проверяется — только права. | ← `user_role`, `role_permission` | ТЗ 8.1 |
|
|
||||||
| `permission` | **v5.13.** Атомарное право «объект × действие»: `DIRECTORY/DOCUMENT/CONSOLIDATION/KFO/EX/EXPORT/USERS/AUDIT` × `VIEW/EDIT/APPROVE/VERIFY/DELETE/EXPORT/ADMIN`. Seed, привязан к коду — администратор новые коды не создаёт. Право говорит «может»; policy-слой (ЗО, филиал, статус КФО) — «эту ли и сейчас ли». | ← `role_permission` | ТЗ Прил. 2, сноска 29 |
|
|
||||||
| `role_permission` | **v5.13.** Матрица доступов как данные: `effective_permissions(user) = ∪ permissions(roles(user))`. Редактируется администратором на экране «Изменение ролей и прав доступа»; история — `audit_event(ROLE_PERMISSION_CHANGE)`. Прямых прав у пользователя нет. | → `role`, `permission` | ТЗ Прил. 2; макет |
|
|
||||||
| `user_role` | Назначение ролей пользователю (M:N). История — в аудите. | → `app_user`, `role` | ТЗ 8.1 |
|
|
||||||
|
|
||||||
## Часть 03 — Бухгалтерские справочники и правила
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `chart_of_account` | План счетов с иерархией, тип A/P/AP, признак забалансового. Seed — полный план 1С; нестандартные субсчета клиентов добавляет администратор. | ← `osv_entry.account_id`, `elimination_rule_account`, `required_document_rule` | ТЗ 6.2/6.5, Прил. 3/4 |
|
|
||||||
| `elimination_rule` | Правило элиминации из Прил. 3 (ББ) / Прил. 4 (ОФР) как данные: пара статей, сторона, мера, режим, НДС, акцизы. Версионируется. | ← `elimination_rule_account`, `correction.rule_id`; версия фиксируется в `consolidation_version` | ТЗ 6.4, Прил. 3/4 |
|
|
||||||
| `elimination_rule_account` | Счета/субсчета каждой стороны правила, включая исключения («76 кроме 76.7.1»). | → `elimination_rule`, `chart_of_account` | ТЗ Прил. 3/4 |
|
|
||||||
| `required_document_rule` | Какие документы обязательны по подходу. **v5.13:** сверяется с `document` (тип + счёт + период), а не с файлом: выполнено = есть `document CONFIRMED` у каждого ЮЛ периметра. | → `chart_of_account` | ТЗ 5.1, 6.2, сноска 12 |
|
|
||||||
|
|
||||||
## Часть 04 — Файлы, документы, S3, задания распознавания
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `s3_object` | Реестр всех объектов в S3: бакет-роль, ключ, размер, sha256, срок жизни, ссылка на архив в data lake. Единая точка ретеншна (5 лет по ТЗ 8.4) и дедупа. | ← `stored_file`, `recognition_job` | ТЗ 6.2, 8.4; ЕХ 8.3 |
|
|
||||||
| `stored_file` | **v5.13: только физический файл** одной загрузки: чей (ЮЛ), формат, hash, кто/когда загрузил, статус обработки (`UPLOADED → PROCESSING → PROCESSED / ERROR / SUPERSEDED`), версия при замене. **v5.18:** `consolidation_id` — консолидация, в контексте которой файл загружен (основа «рабочего стола консоли», общего для версий периода; полный состав — `context/05-domain-rules.md` §9.5 п. 11; `NULL` — без контекста). Что внутри файла — `document`. Замена — файл целиком со всеми документами. | → `legal_entity`, `consolidation`, `s3_object`, `recognition_job`; ← `document`, `recognition_job` | ТЗ 6.2, 8.2; UC-01…05 |
|
|
||||||
| `document` | **v5.13, новая.** Логический документ внутри файла: ББ, ОФР, ОСВ по счёту (`osv_account_code`), расшифровка, прочее. Период, отчётная дата, где в файле (`locator`: лист / страницы), статус проверки (`RECOGNIZED / NEEDS_CHECK / CONFIRMED / ERROR`), результаты проверок периода и контрагента, кто подтвердил. Один файл → N документов (PDF с ББ+ОФР; xlsx с несколькими ОСВ). Создаёт ядро при маппинге результата `recognition_job`. На документ ссылаются разобранные данные, чек-лист, консолидация и ЕХ. **v5.19:** `file_ordinal` — индекс документа в `documents[]` результата (у всех документов); формы ББ/ОФР одного типа в файле могут повторяться (ОСВ по счёту, расшифровка, прочий — один на тип); предупреждения и нарушения — `document_issue`. **v5.20:** период и счёт ОСВ уточняются аналитиком на месте, распознанные — в `original_values` (`is_modified`, `modified_by/at`). | → `stored_file`, `legal_entity`, `app_user`; ← `osv_entry`, `financial_statement_meta`, `document_issue`, `consolidation_document`, `ex_snapshot_item` | ТЗ 6.2, 5.1, 8.2; макет (чек-лист, «Подтвердить данные») |
|
|
||||||
| `recognition_job` | Одна попытка распознать/распарсить файл. **Единственная точка обмена с NATS** (outbox/inbox, дедуп по `result_msg_id`, DLQ). Хранит сервис и версию, **одну** ссылку на нормализованный JSON-результат в S3 (v5.13), метрики, и **схему группировок шапки ОСВ по листам** (`group_schema jsonb`, v5.13 — вместо `osv_group_schema`). **v5.19:** `statements_version` — версия копии общего кода нормализации, которой посчитан результат форм. | → `stored_file`, `s3_object`; ← `osv_entry`, `financial_statement_meta` | ТЗ 6.2, 6.10; DECISION-4 |
|
|
||||||
|
|
||||||
## Часть 05 — Разобранные ОСВ
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `osv_entry` | Строка ОСВ: документ (счёт + период), владелец, тип строки по схеме шапки (`group_type`, v5.13), уровень и родитель (`parent_entry_id` — **открыт вопрос, замечание 2**), код счёта и резолв в план счетов, сырое имя/ИНН контрагента, резолв в ЮЛ и способ сопоставления (`counterparty_match_kind`, v5.16), шесть сумм, тип данных (БУ/валюта/кол-во), «подакцизный» для 90-го. Ручные правки: `is_modified`, `original_values`, `modified_by`. Признак ГСК/ГК/Внешний не хранится — выводится из периметра версии; способ сопоставления контрагента хранится (`NULL` ⇔ контрагент не сопоставлен). | → `document`, `recognition_job`, `legal_entity` ×2, `chart_of_account` | ТЗ 6.2/6.5, Прил. 2; «Парсинг ОСВ» |
|
|
||||||
|
|
||||||
## Часть 06 — Распознанные формы ББ/ОФР
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `financial_statement_meta` | Шапка распознанной формы (1:1 с `document` BALANCE/INCOME): ИНН и наименование как в документе, отчётная дата, единица измерения, реквизиты. Ревью формы (`reviewed_by/at`) и акцепт КА непройденных логических проверок с комментарием. Подтверждение документа — `document.confirmed_by`. **v5.19:** `form_variant` (полная / упрощённая — набор соотношений §7), `needs_review_fields`, `header_source` (сырой текст и уверенность полей шапки), правка наименования (`organization_name_original_value`, `organization_name_modified`); результат проверок §7 и предупреждения хранятся в `document_issue` как пришли от сервиса, ядро пересчитывает их только после правок. | → `document`, `recognition_job`, `app_user`; ← `column`, `line` | ТЗ 6.2/6.3; FNR01; макет |
|
|
||||||
| `financial_statement_column` | Колонки-периоды формы с датой — нужны для межформенной проверки (1370 на начало года) и гудвилла. **v5.19:** `period_kind` (абсолютная / относительная подпись бланка / без даты), `label_confidence`. | → `meta`; ← `value` | ТЗ 6.3 п.7, 6.7 |
|
|
||||||
| `financial_statement_line` | Строка формы как в источнике: код (или его сырой текст) и наименование как распознано. **v5.19:** все строки источника — с кодом (`ok`, повторы кода тоже) и без (`unreadable`, `missing`, `detail` — расшифровка под родителем `parent_row_index`); порядок `row_index` (`NULL` ⇔ строка создана правкой аналитика), положение `source_ref`, `needs_review` неуверенного кода, подтверждение кода `is_confirmed`. Раскладка экрана — по коду «как на бумаге». | → `meta`; ← `value`, `document_issue.line_ids` | ТЗ 6.2, Прил. 2 |
|
|
||||||
| `financial_statement_value` | Число в ячейке (строка × колонка): сумма, уверенность, `is_recognized`, ручная правка с исходным значением. **v5.19:** подсветка — только `needs_review` (хранится: уверенность, `is_recognized` и §7 сведены сервисом, ядро пересчитывает после правок); `source_text` — сырой текст источника; `is_confirmed` / `confirmed_by/at` — подтверждено без правки. (v5.13 этот флаг убирала как производный.) | → `line`, `column`, `app_user` | ТЗ 6.2, Прил. 2 |
|
|
||||||
| `document_issue` | **v5.19, новая.** Предупреждение или нарушение §7 документа (у формы; у ОСВ — предупреждения листа): вид (`warning` / `relation` / `soft`), код и уровень (`error` / `info`), сообщение, привязка к строкам (`line_ids`), колонке (`column_id` или `column_index`) и полю шапки, источник (`service` / `core`); для нарушения — соотношение, `codes`, `expected`, `actual`. Экран показывает их у места. | → `document`, `financial_statement_column` | change `unified-statement-normalization` (design, шаг 8) |
|
|
||||||
|
|
||||||
## Часть 07 — Консолидация
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `consolidation` | Логическая консолидация: **ГК** + отчётный период. ГСК фиксируется/создаётся при верификации по фактическому периметру. Уникальна по (ГК, год, квартал). **v5.18:** файлы, загруженные в консолидацию, — `stored_file.consolidation_id` (основа рабочего стола, общего для версий периода; полный состав — `context/05-domain-rules.md` §9.5 п. 11). | → `company_group`, `related_group`; ← `consolidation_version`, `stored_file` | ТЗ 6.10, 7.2 |
|
|
||||||
| `consolidation_version` | Версия консолидации: номер, статус, сценарий, подход, гудвилл, инициатор и его офис (маршрут), кто утвердил / верифицировал, решение УОБ, отклонения от Методики, версия правил элиминации, ссылка на предыдущую. После VERIFIED — read-only. | → `consolidation`, `app_user`, `regional_office`, self; ← всё в частях 07–08 | ТЗ 5.1, 6.4, 6.10; макет |
|
|
||||||
| `consolidation_status_history` | Каждый переход статуса: из → в, кто, когда, комментарий (обязателен при возврате). | → `consolidation_version`, `app_user` | ТЗ 6.10, 8.2 |
|
|
||||||
| `consolidation_lock` | «Взять в работу»: кто редактирует версию, перехват с причиной (v5.16: без истечения по времени, OQ-119). Из макета — ANL-036. | → `consolidation_version`, `app_user` | макет |
|
|
||||||
| `consolidation_member` | Периметр версии — зафиксированный список ЮЛ; `is_head_entity` для гудвилла. | → `consolidation_version`, `legal_entity` | ТЗ 6.1, 6.10 |
|
|
||||||
| `consolidation_document` | **v5.13:** какие **документы** (не файлы) участвуют в версии. Чек-лист = `required_document_rule` × периметр − `consolidation_document`. Переиспользование одного документа в нескольких версиях/периодах, монозаёмщики из ЕХ. | → `consolidation_version`, `document` | ТЗ Прил. 2; ЕХ 7 |
|
|
||||||
|
|
||||||
*Не хранятся (DECISION-5), считаются запросом по версии:* сводная ОСВ, признак контрагента ГСК/ГК/Внешний, пары ВГО, шахматка, результирующая таблица ВГО.
|
|
||||||
|
|
||||||
## Часть 08 — Корректировки, гудвилл, результат КФО
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `correction` | Ручная корректировка ВГО: пара (правило + компания 1 + компания 2 — естественный ключ), ББ/ОФР, строки и суммы по Дт и Кт, дочерние строки для распределения, причина, обязательный комментарий, статус, номер версии (история — в аудите). КФО = свод − Σ корректировок. | → `consolidation_version`, `elimination_rule`, `legal_entity` ×2, `chart_of_account` ×2, self | ТЗ 6.4, 6.8, Прил. 9 |
|
|
||||||
| `vgo_assignment` | Ручное решение аналитика по строке сводной ОСВ сверх автоматического разнесения по правилам ВГО: привязка к правилу / снятие привязки, «не учитывать в КФО» у нераспределённой строки. Разнесение само по себе не хранится — только это решение поверх него. | → `consolidation_version`, `osv_entry` (ON DELETE CASCADE), `elimination_rule`, `app_user` ×2 | DOC-272613 §5, AURORA-1751 |
|
|
||||||
| `goodwill` | Строка расчёта гудвилла: головная × дочерняя, доля владения, ФВ по 58.01, УК (1310) и добавочный капитал (1350 — вопрос методологам), гудвилл, «включить в КФО», комментарий. | → `consolidation_version`, `legal_entity` ×2, `app_user` | ТЗ 6.7, Прил. 3 п.2, Прил. 8 |
|
|
||||||
| `kfo_result` | Шапка результата КФО: когда посчитан, заморожен ли, ЧА и ВБ, выручка, прибыль, расхождения, пороги, обоснование, итог гудвилла. | → `consolidation_version` (1:1); ← `kfo_result_line` | ТЗ 6.4, 6.8, Прил. 10 |
|
|
||||||
| `kfo_result_line` | Строка консолидированного ББ/ОФР по коду ФСБУ 4/2023: сумма → авто-элиминация → корректировки → гудвилл → итог; «в т.ч. ВГО», источник, расхождение, комментарий. | → `kfo_result`; ← `kfo_result_line_company` | ТЗ 6.4, Прил. 10; макет |
|
|
||||||
| `kfo_result_line_company` | Разрез строки КФО по компаниям периметра: сумма и доля. | → `kfo_result_line`, `legal_entity` | ЕХ 6.6; макет |
|
|
||||||
| `client_kfo_line` | Сценарий «подтверждение КФО клиента»: по строке — значение и ВГО от клиента (ручной ввод), значение и ВГО банка, расхождение, причина. | → `consolidation_version`, `app_user` | ТЗ 5.1, 6.10; макет |
|
|
||||||
|
|
||||||
## Часть 09 — Комментарии, аудит, Единое хранилище
|
|
||||||
|
|
||||||
| Сущность | Что это | Ключевые связи | Откуда |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `comment` | Полиморфный комментарий к объекту (`document | consolidation_version | …`); сейчас используется для документов. Правка и удаление не предусмотрены; аудит не ведётся. | → `app_user`; `object_type + object_id` без FK | ТЗ 6.3; AURORA-1446; макет |
|
|
||||||
| `audit_event` | Журнал действий (append-only): кто, когда, действие, раздел, объект, версия консолидации, before/after, результат, IP/браузер. Покрывает историю правок распознанных данных, корректировок, выгрузок, импорта справочников, входов, **изменений матрицы прав** (v5.13), подтверждения документов, **изменений ЮЛ и пользователей** (v5.14: `LEGAL_ENTITY_CHANGE`, `USER_CHANGE`). | → `app_user`, `consolidation_version` | ТЗ 8.2, Прил. 2; макет |
|
|
||||||
| `ex_snapshot` | Срез ЕХ: одна ГСК на одну отчётную дату, версия, статус (актуальный/архив), когда и кем верифицирован и записан. Самодостаточен — на консолидацию ссылается только данными. | → `related_group`, `company_group`, `app_user`, self; ← `member`, `item` | ЕХ ТЗ 6.2, 6.4, 5.3 |
|
|
||||||
| `ex_snapshot_member` | Периметр среза на дату: ЮЛ с ИНН и наименованием на момент верификации. | → `ex_snapshot`, `legal_entity` | ЕХ ТЗ 6.3 |
|
|
||||||
| `ex_snapshot_item` | Содержимое среза. Два вида: исходный документ → ссылка на **`document`** (v5.13; скачать — через его файл) и всё производное → `payload` jsonb. | → `ex_snapshot`, `document`, `legal_entity` | ЕХ ТЗ 5.2, 6.7 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Как читать модель по сценарию «сформировать КФО»
|
|
||||||
|
|
||||||
1. **Справочники** (01–03): ГК → ЮЛ → ГСК/ГСЗ; план счетов; правила элиминации; что обязательно загрузить.
|
|
||||||
2. **Создание консолидации** (07): `consolidation` от ГК + период → `consolidation_version` → `consolidation_member` (периметр).
|
|
||||||
3. **Загрузка** (04): `stored_file` + `s3_object` → `recognition_job` (queued → NATS → сервис → результат в S3 → done) → ядро создаёт **`document`** на каждый ББ / ОФР / ОСВ по счёту, найденный в файле.
|
|
||||||
4. **Разбор** (05–06): ОСВ → `osv_entry`; ББ/ОФР → `financial_statement_meta/column/line/value`. Аналитик проверяет, правит, подтверждает **документ**; логические проверки — на лету; сопоставляет контрагентов (→ `osv_entry.counterparty_id`, `legal_entity_alias`). Чек-лист закрывается по `document CONFIRMED`.
|
|
||||||
5. **Консолидация** (вычисляется): сводная ОСВ, пары ВГО, шахматка — из `osv_entry` × периметр × правила. Аналитик вносит `correction`, заполняет `goodwill`, при сценарии подтверждения — `client_kfo_line`. Результат — `kfo_result(_line)`.
|
|
||||||
6. **Согласование** (07): `consolidation_status_history` по маршруту; блокировка `consolidation_lock`. Что можно — решает `role_permission` + policy-слой по статусу/ЗО.
|
|
||||||
7. **Верификация → ЕХ** (09): `ex_snapshot` + `member` + `item` (документы по ссылке, остальное — payload). Версия → read-only. Всё это время — `audit_event`.
|
|
||||||
|
|
||||||
## Открытые вопросы после v5.14
|
|
||||||
|
|
||||||
- **Замечание 2** (`osv_entry.parent_entry_id`, какие строки ОСВ хранить) — отложено; затрагивает и аналитику «Парсинг ОСВ» п.11.
|
|
||||||
- Замена по-документно (одна ОСВ из файла с пятью) — сейчас не предусмотрена, замена файлом целиком. Если понадобится — `superseded_by` переезжает в `document`.
|
|
||||||
- Статус `document.RECOGNIZED` (ручная проверка не требуется по порогу) — подтвердить с аналитиками, нужен ли, или все документы проходят через `NEEDS_CHECK → CONFIRMED`.
|
|
||||||
@ -1,12 +1,12 @@
|
|||||||
schema: spec-driven
|
schema: spec-driven
|
||||||
|
|
||||||
context: |
|
context: |
|
||||||
Репозиторий arm-ka-docs — контекст, модель данных (DBML v5.24), правила домена, контракты между сервисами
|
Репозиторий arm-ka-docs — контекст, модель данных (DBML v5.23), правила домена, контракты между сервисами
|
||||||
(docs/contracts/*.schema.json), макет UI и экспорт ТЗ системы АРМ КА (ДКБ, Россельхозбанк).
|
(docs/contracts/*.schema.json), макет UI и экспорт ТЗ системы АРМ КА (ДКБ, Россельхозбанк).
|
||||||
Здесь оформляются СКВОЗНЫЕ изменения: контракт + окружение разработки (docs/dev) + проверки качества (make check) + изменения
|
Здесь оформляются СКВОЗНЫЕ изменения: контракт + окружение разработки (docs/dev) + проверки качества (make check) + изменения
|
||||||
документов контекста. Код сервисов живёт в соседних репозиториях (../arm-core, ../tabular-parser, ../scan-gateway),
|
документов контекста. Код сервисов живёт в соседних репозиториях (../arm-core, ../tabular-parser, ../scan-gateway),
|
||||||
у каждого свой openspec/ и свой change; сквозной change ссылается на них.
|
у каждого свой openspec/ и свой change; сквозной change ссылается на них.
|
||||||
Источники истины по приоритету: docs/model/data-model-v5.24.dbml → docs/model/entities-v5.24.md →
|
Источники истины по приоритету: docs/model/data-model-v5.23.dbml → docs/model/entities-v5.23.md →
|
||||||
docs/context/05-domain-rules.md → docs/source/tz (черновик, имена v4). Открытые вопросы — docs/context/07-open-questions.md;
|
docs/context/05-domain-rules.md → docs/source/tz (черновик, имена v4). Открытые вопросы — docs/context/07-open-questions.md;
|
||||||
план срезов — docs/context/09-delivery-plan.md; соглашения шаблона команды — docs/context/03-architecture.md.
|
план срезов — docs/context/09-delivery-plan.md; соглашения шаблона команды — docs/context/03-architecture.md.
|
||||||
Язык документов и коммитов — русский, идентификаторы — английские, как в DBML. docs/source/** не редактируется.
|
Язык документов и коммитов — русский, идентификаторы — английские, как в DBML. docs/source/** не редактируется.
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
- NATS subjects и схема ключей S3 — `../docs/context/03-architecture.md`.
|
- NATS subjects и схема ключей S3 — `../docs/context/03-architecture.md`.
|
||||||
|
|
||||||
## Правила репозитория
|
## Правила репозитория
|
||||||
- ORM 1:1 с `../docs/model/data-model-v5.24.dbml`; `scripts/check_dbml_vs_orm.py` — обязательная проверка CI.
|
- ORM 1:1 с `../docs/model/data-model-v5.23.dbml`; `scripts/check_dbml_vs_orm.py` — обязательная проверка CI.
|
||||||
- Алгоритмы — только из `../docs/context/05-domain-rules.md`; номер § — в docstring.
|
- Алгоритмы — только из `../docs/context/05-domain-rules.md`; номер § — в docstring.
|
||||||
- Вычисляемые объекты консолидации — `backend/app/domain/`, без ORM-таблиц.
|
- Вычисляемые объекты консолидации — `backend/app/domain/`, без ORM-таблиц.
|
||||||
- Фронт: пользователь слаб во фронте — для TS/React изменений делай ревью сам и объясняй решения в PR.
|
- Фронт: пользователь слаб во фронте — для TS/React изменений делай ревью сам и объясняй решения в PR.
|
||||||
|
|||||||
@ -51,7 +51,7 @@ PARTS = {
|
|||||||
),
|
),
|
||||||
"08_corrections_kfo": (
|
"08_corrections_kfo": (
|
||||||
"Корректировки, гудвилл, результат КФО, подтверждение КФО клиента",
|
"Корректировки, гудвилл, результат КФО, подтверждение КФО клиента",
|
||||||
["correction", "vgo_assignment", "goodwill", "kfo_result", "kfo_result_line", "kfo_result_line_company", "client_kfo_line"],
|
["correction", "goodwill", "kfo_result", "kfo_result_line", "kfo_result_line_company", "client_kfo_line"],
|
||||||
),
|
),
|
||||||
"09_audit_ex": ("Комментарии, аудит, ЕХФД", ["comment", "audit_event", "ex_snapshot", "ex_snapshot_member", "ex_snapshot_item"]),
|
"09_audit_ex": ("Комментарии, аудит, ЕХФД", ["comment", "audit_event", "ex_snapshot", "ex_snapshot_member", "ex_snapshot_item"]),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
Для каждого сценария expected.json: консолидация ГК/ГСК за период → блокировка → загрузка файлов фикстур в версию →
|
Для каждого сценария expected.json: консолидация ГК/ГСК за период → блокировка → загрузка файлов фикстур в версию →
|
||||||
ожидание распознавания (tabular-parser, scan-gateway + эмулятор ИС) → подтверждение ОСВ-документов → проверки свода
|
ожидание распознавания (tabular-parser, scan-gateway + эмулятор ИС) → подтверждение ОСВ-документов → проверки свода
|
||||||
(summary, пробелы, контрольные строки), экспорта xlsx, шахматки, таблицы корректировок (сохранение и 409
|
(summary, пробелы, контрольные строки), экспорта xlsx, шахматки, форм ББ/ОФР; плюс негатив (повтор создания — 409,
|
||||||
CORRECTION_VERSION_CONFLICT), форм ББ/ОФР; плюс негатив (повтор создания — 409, блокировка без права EDIT — 403).
|
блокировка без права EDIT — 403). Итог — report.json (проверки) и snapshots/<сценарий>.*.json (весь свод, шахматка,
|
||||||
Итог — report.json (проверки) и snapshots/<сценарий>.*.json (весь свод, шахматка, формы); с --baseline снимки
|
формы); с --baseline снимки сравниваются с прошлым прогоном (расхождения — в report.json, раздел diffs).
|
||||||
сравниваются с прошлым прогоном (расхождения — в report.json, раздел diffs).
|
|
||||||
|
|
||||||
python3 -I stand_scenario.py --api http://127.0.0.1:18000 --fixtures ../fixtures --expected expected.json --out DIR
|
python3 -I stand_scenario.py --api http://127.0.0.1:18000 --fixtures ../fixtures --expected expected.json --out DIR
|
||||||
[--baseline DIR_ПРОШЛОГО_stand] [--timeout 600]
|
[--baseline DIR_ПРОШЛОГО_stand] [--timeout 600]
|
||||||
@ -349,33 +348,6 @@ def run_scenario(api: Api, sc: dict, ctx: dict, fixtures: Path, rep: Report, out
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# таблица корректировок (AURORA-1540/1541) и ручное распределение строк ВГО (AURORA-1751) — без UI-покрытия (#198)
|
|
||||||
st, _, _ = api.call("GET", f"/consolidations/versions/{vid}/vgo-assignments/distribute")
|
|
||||||
rep.check(f"{sid}: распределение строк ВГО 200", st == 200, f"{st}")
|
|
||||||
st, corrections, _ = api.call("GET", f"/consolidations/versions/{vid}/corrections", params={"statement_type": "balance"})
|
|
||||||
if rep.check(f"{sid}: таблица корректировок 200", st == 200, f"{st}"):
|
|
||||||
aggregates = corrections["aggregates"]
|
|
||||||
if rep.check(f"{sid}: таблица корректировок — есть агрегаты", len(aggregates) > 0, f"{len(aggregates)}"):
|
|
||||||
agg = aggregates[0]
|
|
||||||
path = f"/consolidations/versions/{vid}/corrections/aggregates/{agg['rule_id']}/{agg['company_1']['id']}/{agg['company_2']['id']}"
|
|
||||||
body = {
|
|
||||||
"debit": [{"line_code": agg["side_1"]["article_line"].split("/")[0], "amount": "1.00"}],
|
|
||||||
"credit": [{"line_code": agg["side_2"]["article_line"].split("/")[0], "amount": "1.00"}],
|
|
||||||
"comment": "регрессия (#198): проверка сохранения и конфликта версий",
|
|
||||||
"expected_version_no": None,
|
|
||||||
}
|
|
||||||
st1, saved, _ = api.call("PUT", path, body, params={"statement_type": "balance"})
|
|
||||||
if st1 == 422 and (saved or {}).get("detail", {}).get("code") == "INVALID_LINE_CODE":
|
|
||||||
# статья не из формы ББ компании — в этом сценарии нет подтверждённых форм (только ОСВ); не регрессия
|
|
||||||
print(f"{sid}: корректировка — пропуск сохранения, нет формы ББ со статьёй агрегата: {saved}", flush=True)
|
|
||||||
elif rep.check(f"{sid}: корректировка — сохранение 200", st1 == 200, f"{st1} {str(saved)[:200]}"):
|
|
||||||
st2, conflict, _ = api.call("PUT", path, body, params={"statement_type": "balance"})
|
|
||||||
rep.check(
|
|
||||||
f"{sid}: корректировка — повтор с устаревшим expected_version_no — 409",
|
|
||||||
st2 == 409 and (conflict or {}).get("detail", {}).get("code") == "CORRECTION_VERSION_CONFLICT",
|
|
||||||
f"{st2} {conflict}",
|
|
||||||
)
|
|
||||||
|
|
||||||
# формы ББ/ОФР из PDF (эмулятор ИС): типы документов, строки, проверки
|
# формы ББ/ОФР из PDF (эмулятор ИС): типы документов, строки, проверки
|
||||||
stmts = {}
|
stmts = {}
|
||||||
for inn, want in sc["statements"].items():
|
for inn, want in sc["statements"].items():
|
||||||
|
|||||||
@ -47,11 +47,7 @@ def main() -> int:
|
|||||||
if g:
|
if g:
|
||||||
pages.append(("group", f"/groups/{g}"))
|
pages.append(("group", f"/groups/{g}"))
|
||||||
for sid, vid in (ids.get("versions") or {}).items():
|
for sid, vid in (ids.get("versions") or {}).items():
|
||||||
pages += [
|
pages += [(f"{sid}-osv", f"/groups/{g}/versions/{vid}/osv"), (f"{sid}-chess", f"/groups/{g}/versions/{vid}/chess")]
|
||||||
(f"{sid}-osv", f"/groups/{g}/versions/{vid}/osv"),
|
|
||||||
(f"{sid}-chess", f"/groups/{g}/versions/{vid}/chess"),
|
|
||||||
(f"{sid}-correction", f"/groups/{g}/versions/{vid}/correction"),
|
|
||||||
]
|
|
||||||
for name, doc in (ids.get("documents") or {}).items():
|
for name, doc in (ids.get("documents") or {}).items():
|
||||||
pages.append((f"doc-{name}", f"/documents/{doc}"))
|
pages.append((f"doc-{name}", f"/documents/{doc}"))
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user