# 04. Как читать модель данных v5.16 Файлы: `docs/model/data-model-v5.16.dbml` (источник истины), `docs/model/entities-v5.16.md` (смысл таблиц; предыдущие версии `data-model-v5.13…v5.15.dbml` и `entities-v5.13…v5.15.md` остаются как история), `docs/model/diagrams/part01…09.svg` (диаграммы v5.12 — на одну версию старше: нет `permission`/`role_permission`, нет `document`, есть `osv_group_schema`, нет `related_group_member.kind`; читать с поправкой на changelog v5.13…v5.16 в шапке DBML). ## Девять частей модели | Часть | Таблицы | Что это | |---|---|---| | 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` | шапка, колонки-даты, строки с кодами, значения с confidence | | 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 журнал, замороженные срезы | ## Соглашения - 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.14, v5.15 и v5.16. | В страницах ТЗ (модель v4 и старше) | В v5.13 / v5.14 / v5.15 / v5.16 | |---|---| | `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 (КПП) — не вводится. - **№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).