arm-ka-docs/context/04-data-model-guide.md
Andrey Epifancev ecc789667f model: DBML v5.19 — строки форм со статусами, needs_review, document_issue, crm_id ГСК/ГСЗ (#116)
- financial_statement_line: снята уникальность (meta_id, line_code); row_index, source_ref (частичный
  уникальный индекс), line_status (enum statement_line_status), line_code_text, parent_row_index,
  needs_review, is_confirmed
- financial_statement_value: source_text, needs_review (хранится — пересмотр п. 4 v5.13), is_confirmed,
  confirmed_by/at; financial_statement_column: period_kind, label_confidence;
  financial_statement_meta: form_variant (enum statement_form_variant), needs_review_fields
- новая таблица document_issue + enum document_issue_kind, issue_severity, issue_source
- recognition_job.statements_version; document: file_ordinal, уникальность типа на файл — только не для
  BALANCE/INCOME (Р-10); related_group.crm_id (arm-core#112)
- changelog и правила заполнения миграции в шапке; entities-v5.19.md; диаграммы части 01, 04, 06
  (document_issue — в части 06); ссылки на текущую модель и DBML в Makefile — v5.19

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 00:53:06 +04:00

104 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 04. Как читать модель данных v5.19
Файлы: `docs/model/data-model-v5.19.dbml` (источник истины), `docs/model/entities-v5.19.md` (смысл таблиц; предыдущие
версии `data-model-v5.13…v5.18.dbml` и `entities-v5.13…v5.18.md` остаются как история),
`docs/model/diagrams/part01…09.{svg,png}` (диаграммы v5.19 по частям из таблицы ниже; перерисовываются из DBML командой
`make diagrams` в корне репозитория docs — скрипт `scripts/gen_model_diagrams.py`, нужен npx; при новой версии DBML
обновить `DBML` в Makefile, а новую таблицу — добавить в раскладку частей скрипта и в эту таблицу).
## Девять частей модели
| Часть | Таблицы | Что это |
|---|---|---|
| 01 Группы и организации | `regional_office`, `company_group(+_history)`, `legal_entity(+_history)`, `legal_entity_alias`, `related_group`, `related_group_member` | справочник клиентов; SCD4 для ГК/ЮЛ, SCD2 для членства |
| 02 Пользователи и права | `app_user`, `role`, `permission`, `role_permission`, `user_role` | матрица прав как данные |
| 03 Бухгалтерские справочники | `chart_of_account`, `elimination_rule`, `elimination_rule_account`, `required_document_rule` | план счетов (seed из 1С), правила элиминации Прил. 3/4 (версионируются), чек-лист по подходу |
| 04 Файлы и распознавание | `s3_object`, `stored_file`, `document`, `recognition_job` | физический файл → задание → логические документы |
| 05 Разобранные ОСВ | `osv_entry` | строка ОСВ с шестью суммами, сырым контрагентом и резолвом |
| 06 Формы ББ/ОФР | `financial_statement_meta`, `_column`, `_line`, `_value`, `document_issue` | шапка, колонки-даты, строки со статусами, значения с confidence и `needs_review`, предупреждения и нарушения §7 |
| 07 Консолидация | `consolidation`, `consolidation_version`, `_status_history`, `_lock`, `_member`, `_document` | логическая консолидация (ГК × период) и её версии с периметром |
| 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 журнал, замороженные срезы |
**v5.19:** формы ББ/ОФР по контракту v2.0 (change `unified-statement-normalization`, `05-domain-rules.md` §11): строки
формы хранятся все — с кодом (повтор кода законен, уникальность `(meta_id, line_code)` снята) и без (`line_status`
`unreadable` / `missing` / `detail`); `needs_review` значения и строки **хранится** (пересматривает v5.13); предупреждения и
нарушения §7 — `document_issue`; две формы одного типа в файле — разные `document` (`file_ordinal`); чтения «по коду» —
агрегат формы по коду (сумма строк `ok`, не хранится). Плюс `related_group.crm_id`.
**v5.18:** `stored_file.consolidation_id` — в какую консолидацию (период ГК) загружен файл; файлы, загруженные в консолидацию, — основа рабочего стола консоли, общего для её версий (полный состав стола, с файлами документов, привязанных к версиям, — `05-domain-rules.md` §9.5 п. 11). Что использует версия — `consolidation_document`.
## Соглашения
- PK везде `uuid`; натуральные ключи — `UNIQUE` (ИНН, код счёта, crm_id, код ГСК). Для `osv_entry` и `audit_event`
допустима замена на `bigint identity` при объёмах.
- Денежные поля `numeric(18,2)`, проценты `numeric(7,4)`/`numeric(9,4)`, confidence `numeric(5,2)` (0–100).
- Временные метки `timestamptz`; отчётные даты `date`.
- Партиальные уникальные индексы (помечены в notes): `related_group_member (legal_entity_id, kind) WHERE is_current`
(v5.14: ЮЛ — не более одной текущей группы каждого вида, т.е. одна ГСК и одна ГСЗ одновременно; `kind` — денормализация
`related_group.kind`),
`consolidation_lock.consolidation_version_id WHERE released_at IS NULL`,
`document (stored_file_id, document_type, osv_account_code) NULLS NOT DISTINCT`.
- Enum'ы PostgreSQL — ровно как в DBML (`consolidation_status`, `document_status`, `audit_action` …). Добавление значения —
миграция + версия DBML.
- История: `*_history` — снимки предыдущего состояния (SCD4); `related_group_member` — версии строк (SCD2);
остальное — `audit_event(before_value, after_value)`.
- Поля `*_by` → `app_user.id`; `NULL` — системное действие.
## Что намеренно НЕ хранится (DECISION-5) и как это вычислять
Способ сопоставления контрагента хранится (`osv_entry.counterparty_match_kind`, v5.16); пробелы квартальной склейки и сам свод — нет.
| Объект | Вычисление |
|---|---|
| Сводная ОСВ | `osv_entry` актуальных `document` (OSV, CONFIRMED, период версии) всех `consolidation_member` версии; только строки `group_type = COUNTERPARTY` и `data_type IN (BU, SUM)`; строки не агрегируются |
| Признак контрагента | `counterparty_id ∈ member(version)` → ГСК; иначе `legal_entity.company_group_id = consolidation.company_group_id` → ГК; иначе Внешний |
| Пары ВГО | по `elimination_rule(_account)` версии правил `consolidation_version.elimination_rules_version` — §5 правил |
| Шахматка | агрегат пар по компаниям и выбранным наборам счетов — §6 |
| Логические проверки форм | формулы §7 по `financial_statement_value` |
| Чек-лист документов | `required_document_rule(approach) × consolidation_member − document(CONFIRMED)` |
| «Исправлено полей» | `COUNT(*) WHERE is_modified` |
| «Требует проверки» ячейка | `confidence < порог OR NOT is_recognized` |
Всё это — чистые функции от `consolidation_version_id`; результат кэшируется только в памяти запроса.
При `VERIFIED` единственный раз сериализуется в `ex_snapshot_item.payload`.
## v4 → v5.13: таблица переименований (обязательна при чтении ТЗ)
v5.14 имена таблиц и полей не менял (добавлены `related_group_member.kind` и два значения `audit_action`), v5.15 — тоже
(добавлено значение `osv_data_type.NU`), v5.16 — тоже (колонка `osv_entry.counterparty_match_kind`), v5.17 — тоже (колонка `legal_entity.kpp`), v5.18 — тоже (колонка `stored_file.consolidation_id`), v5.19 — тоже (колонки форм, `document_issue`, `related_group.crm_id`), поэтому таблица справедлива и для v5.14…v5.19.
| В страницах ТЗ (модель v4 и старше) | В v5.13 / v5.14 / v5.15 / v5.16 / v5.17 / v5.18 / v5.19 |
|---|---|
| `stored_file.period_year / period_quarter / document_type / osv_account` | `document.period_year / period_quarter / document_type / osv_account_code / is_cumulative` |
| `stored_file.status` «Загружен / Обработка / Требуется проверка / Ошибка» | техника: `stored_file.status` UPLOADED/PROCESSING/PROCESSED/ERROR/SUPERSEDED; проверка: `document.status` RECOGNIZED/NEEDS_CHECK/CONFIRMED/ERROR |
| `stored_file.current_recognition_job_id` как источник строк | `osv_entry.document_id` / `financial_statement_meta.document_id`; `current_recognition_job_id` остаётся как «авторитетный прогон» файла |
| `osv_entry.legal_entity_id` | `osv_entry.owner_legal_entity_id` |
| `osv_entry.stored_file_id` | `osv_entry.document_id` (файл — через `document.stored_file_id`) |
| `osv_entry.group_schema_id → osv_group_schema / osv_grouping_schema` | `osv_entry.group_type` (enum) + `recognition_job.group_schema jsonb` |
| `osv_entry.counterparty_type` (ГК/ГСК/Внешний) | не хранится, вычисляется |
| `fin_report_header`, `Fin_report_rows`, `report_id`, `row_code`, `value_current/prior/prior2`, `needs_review` | `financial_statement_meta` / `_column` (`period_date`, `is_current`) / `_line.line_code` / `_value.amount, confidence, is_recognized` |
| `financial_statement_line.amount` | `financial_statement_value.amount` (строка × колонка) |
| `Consolidation_package`, `Consolidation_template`, `Company_data`, `Intercompany_reconciliation`, `vgo_pair_match`, `chess_cell`, `consolidated_osv_line` | `consolidation`/`consolidation_version`/`consolidation_member`; КФО — `kfo_result(_line)`; остальное вычисляется |
| `Company`, `Organizations`, `Organization_aliases`, `ref_crm_region`, `consolidation_group`, `group_membership` | `legal_entity`, `legal_entity_alias`, `regional_office`, `company_group`, `related_group(_member)` |
| `users` (email, role) | `app_user` + `user_role` + `role_permission` |
| `raw_result_s3_key`, `queue_msg_id` | `recognition_job.result_s3_object_id` (единственная ссылка на результат), `request_msg_id / result_msg_id` |
| `chart_of_account.account_code` | `chart_of_account.code` |
| `role.code` KA_HEAD / KA_GO_DM | `KA_RF, KA_GO, RKA, CLIENT_MANAGER, CURATOR_GO, EXPERT, ADMIN` (seed) |
| статус документа `MANUAL_CHECK` | `document.status = NEEDS_CHECK` |
| статус консолидации `CREATED` (черновик) | `INITIATED` |
## Открытые места модели (не придумывать, спросить)
- **Закрыто в v5.14:** OQ-32 (членство в ГСК и ГСЗ одновременно) — решение владельца 04.10.2026: ЮЛ в одной ГК и одновременно
в одной ГСК и одной ГСЗ (ANL-030 отменён); OQ-31 (КПП) — v5.14 не вводил, **введён в v5.17** (`legal_entity.kpp`, решение владельца 07.10.2026).
- **№2 `osv_entry.parent_entry_id`**: хранить ли все строки ОСВ (п. 11 «Парсинг ОСВ» — да) или только контрагентские;
нужно ли дерево. Рекомендация для среза 2: хранить **все** строки с `level` и `parent_entry_id` — это дёшево и
позволяет сверять «Σ контрагентов = итог счёта»; вопрос закрывать с аналитиками.
- `document.RECOGNIZED` — нужен ли статус «проверка не требуется», или всё идёт через `NEEDS_CHECK → CONFIRMED`.
- Замена по-документно (одна ОСВ из файла с пятью листами) — сейчас только файлом целиком.
- `goodwill.participation_amount` — входит ли стр. 1350 в базу (вопрос методологам).
- `user_role` M:N vs 1:1 — если ТЗ строго «одна роль», заменить на `app_user.role_id`.
- `consolidation_status` REJECTED/ARCHIVED — из макета, не из ТЗ (ANL-035).