arm-ka-docs/model/data-model-v5.19.dbml
Andrey Epifancev 47e6372cbe chore(openspec): архив unified-statement-normalization (#116)
Спеки применены: 5 новых capability (arm-core, arm-web, scan-gateway,
statement-normalization, tabular-recognition-service) с Purpose, изменения
quality-checks и recognition-contract. Ссылки в 05-domain-rules, DBML v5.19,
entities, dev/checks.md и dev/qa-statements-v2.md ведут в архив.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 09:25:43 +04:00

1507 lines
129 KiB
Plaintext
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.

// ДКБ · Модель данных v5.19: справочники, файлы/распознавание, консолидация, КФО, аудит, ЕХФД
// Формат: DBML (dbdiagram.io). Вставить на https://dbdiagram.io/d
// >>> Источник истины по модели данных. Аналитика в Confluence ссылается сюда.
// Над каждой таблицей — блок «ИСТОЧНИК»: пункт ТЗ АРМ КА BIQ-34 / ТЗ ЕХФД (помечено «ЕХ ТЗ») / Методика;
// страница аналитики (md); экран макета dkb_24.09.26; версия модели, в которой таблица появилась (v4 = исходная DBML аналитики).
//
// v5.19 (решения владельца 17.10.2026, Р-1…Р-19; RSHB/arm-ka-docs#116, openspec/changes/archive/2026-10-19-unified-statement-normalization;
// контракт результата v2.0; одна миграция arm-core):
// 1. financial_statement_line: снята уникальность (meta_id, line_code) — повтор кода законен, хранятся все строки (Р-4);
// line_code остаётся nullable и пуст у строк unreadable / missing / detail (Р-8, отменяет OQ-36 / OQ-141).
// Новые колонки: row_index (порядок строки в документе; у шаблонных и созданных правкой — NULL), source_ref
// (положение в источнике, частичный уникальный индекс (meta_id, source_ref) WHERE source_ref IS NOT NULL),
// line_status (enum statement_line_status), line_code_text (сырой текст ячейки кода), parent_row_index (родитель
// detail), needs_review (как пришло от сервиса, пересчитывается ядром после правок), is_confirmed (код подтверждён
// аналитиком).
// 2. financial_statement_value: source_text (values[].text — сырой текст источника), needs_review (как пришло,
// пересчитывается после правок; пересматривает п. 4 v5.13 «считается, не хранится»), is_confirmed + confirmed_by /
// confirmed_at (подтверждено аналитиком без изменения значения).
// 3. financial_statement_column: period_kind (absolute | relative:<вид> | none), label_confidence.
// 4. financial_statement_meta: form_variant (enum statement_form_variant), needs_review_fields (поля шапки с
// needs_review, jsonb-массив имён).
// 5. Новая таблица document_issue (+ enum'ы document_issue_kind, issue_severity, issue_source): предупреждения и нарушения
// §7 документа как пришли от сервиса или посчитаны ядром; очищается при перезагрузке файла; при пересчёте после
// правки записи relation / soft заменяются целиком, warning источника service сохраняются, кроме снимаемых правкой.
// 6. recognition_job.statements_version — коммит копии общего кода arm_statements, которым посчитан результат.
// 7. document: уникальность (stored_file_id, document_type, osv_account_code) — только для типов кроме BALANCE / INCOME
// (частичный индекс; ОСВ по счёту, DISCLOSURE, OTHER — «один на тип на файл», §9.5 п. 2); вторая форма того же типа
// в файле хранится (Р-10, Р-15); новая колонка file_ordinal — индекс документа в documents[] результата (у всех
// документов при любом MAJOR контракта; несозданный документ номер не занимает), уникален в файле.
// 8. related_group.crm_id (varchar, nullable, unique) — ID ГСК/ГСЗ в ЕЦП CRM (AURORA-1619, arm-core#112, OQ-33).
// 9. Признак «строка создана правкой аналитика» — financial_statement_line.row_index IS NULL (заменяет прежний
// признак line_code_original_value IS NULL: у строк unreadable / missing / detail источника кода тоже нет).
// line_code_original_value — код строки из результата как пришёл (NULL у статусов кроме ok и у строк, созданных
// правкой); правками не меняется.
// 10. financial_statement_meta: header_source (jsonb — сырой текст и уверенность всех полей шапки результата,
// {"<поле>": {"text", "confidence"}}), organization_name_original_value и organization_name_modified (наименование
// правится PATCH шапки, как ИНН).
// 11. document_issue: codes (jsonb — коды цели и членов нарушения §7), column_index (столбец предупреждения вне колонок
// формы — COLUMN_SKIPPED и др.).
// Перед выкладкой база очищается от форм ББ/ОФР (решение владельца 18.10.2026, Р-24; скрипт ядра
// scripts/purge_statements.py): миграция только добавляет колонки, таблицу, enum'ы, индексы и значения по умолчанию,
// §7 по существующим формам не считает. Заполнение (миграция проходит и на непустой базе): row_index строк — NULL у
// строк с line_code_original_value IS NULL (созданных правкой), у остальных — по возрастанию line_code внутри формы с 0;
// line_status = ok, needs_review строк = false, needs_review значений = НЕ values_ok (values_ok — правлено или
// is_recognized не false и confidence 100 / NULL), source_text = string_value, is_confirmed = false, period_kind =
// absolute при period_date, иначе none, form_variant = NULL (набор §7 — полный), header_source — из *_original_value
// и inn_confidence, organization_name_original_value = organization_name, file_ordinal — по (created_at, id) внутри
// файла с 0 (у всех документов), related_group.crm_id = NULL; document_issue — пуст: корректность нарушений у форм,
// оставшихся в неочищенной базе, не гарантируется до первой правки.
// Откат гарантирован на пустой БД и на данных, которые v5.18 представляет без потери первичных данных и ручного ввода;
// downgrade останавливается с ошибкой и ничего не меняет при повторе кода в форме, строке без кода, двух формах одного
// типа в файле, непустом related_group.crm_id, organization_name_modified = true; производные и сигнальные колонки
// v5.19 и document_issue удаляет с записью числа удалённых значений в журнал миграции.
//
// v5.18 (решения владельца 09.10.2026; RSHB/arm-ka-docs#90, openspec/changes/console-files):
// 1. stored_file.consolidation_id (uuid, nullable, FK → consolidation): консолидация (ГК + год + квартал), в контексте
// которой загружен файл, — основа «рабочего стола консоли» (полный состав стола — context/05-domain-rules.md §9.5 п. 11), общего для всех версий периода. NULL — загрузка без контекста
// (страница ЮЛ, монозаёмщики, API). Пишется один раз при загрузке (POST /consolidations/{id}/legal-entities/{le}/files);
// при автозамене новый файл без контекста наследует consolidation_id заменённого. Индекс (legal_entity_id, consolidation_id).
// Что использует конкретная версия, по-прежнему определяет consolidation_document; заморозка после VERIFIED — привязки и ex_snapshot.
// Миграция 0006 arm-core: backfill по consolidation_document (при нескольких периодах — самый ранний).
// 2. Note recognition_job.reply_deadline: срок ответа OCR — 20 мин (OQ-159, решение владельца 09.10.2026), структура не меняется.
//
// v5.17 (решение владельца 07.10.2026, пересмотр OQ-31; RSHB/arm-ka-docs#53):
// 1. legal_entity.kpp и legal_entity_history.kpp (varchar, nullable): КПП юридического лица. Формат — 9 символов:
// 4 цифры, 2 символа (цифра или латинская A–Z), 3 цифры; иначе INVALID_KPP. У ИП КПП запрещён (INVALID_KPP;
// импорт — строка пропускается). Для ЮЛ необязателен: существующие записи и импорт без колонки КПП остаются валидными.
// Уникальность — только legal_entity.inn (КПП — атрибут, сопоставление контрагентов ОСВ по ИНН не меняется).
// Изменение КПП — обычная правка со снимком в legal_entity_history. Отменяет п. 2 v5.14 («КПП не вводится»).
//
// v5.16 (Срез 4, решения владельца 06.10.2026):
// 1. Новый enum counterparty_match_kind и колонка osv_entry.counterparty_match_kind (nullable): способ, которым строке ОСВ
// сопоставлен контрагент. NULL ⇔ counterparty_id NULL; INN / NAME / ALIAS — шаг автоматического резолва при маппинге
// (ИНН; точное имя; нормализованное имя или алиас); MANUAL — ручное сопоставление аналитиком; PROPAGATED —
// автоприменение по имени в версии (DOC-221278 п. 12–13). Миграция 0004 заполняет колонку по уже смапленным строкам.
// 2. Без изменений структуры, уточнение семантики consolidation_document: строка создаётся при подтверждении документа
// (по документам ЮЛ — членов периметра версии «в работе»), при подходящем подтверждённом документе — при добавлении ЮЛ
// в периметр; удаляется при отвязке документа (замена файла, удаление, исключение ЮЛ из периметра). Для одной
// (ЮЛ, document_type, osv_account_code, период, признак нарастающего) привязан один документ — самый поздний confirmed_at.
// 3. Без изменений структуры, уточнение семантики consolidation_lock: блокировка без истечения по времени — активна, пока её не
// снял владелец, администратор или перехват другим пользователем с CONSOLIDATION_EDIT по причине (OQ-119).
//
// v5.15 (Срез 2c, решения владельца 05.10.2026):
// 1. osv_data_type: добавлено NU — строки налогового учёта из ОСВ «БУ, НУ» хранятся в osv_entry как есть (OQ-45);
// контракт результата допускал NU с v1.0.
// 2. app_user.email хранится в нижнем регистре: нормализация при записи (seed, администрирование), поиск по
// lower(email) остаётся; уникальность — существующий UNIQUE по email (OQ-59). Структура не меняется.
// 3. Без изменений структуры, уточнение семантики: stored_file.status — PROCESSING ставится inbox'ом по ack сервиса,
// PROCESSED/ERROR — циклом маппинга результата (OQ-67); маппинг выполняется отдельно от inbox (OQ-69);
// recognition_job.group_schema заполняет маппер; «Итого» в osv_entry не хранится (OQ-01).
// 4. Индексы для удаления файла и обхода дерева строк: osv_entry (parent_entry_id), osv_entry (recognition_job_id),
// stored_file (current_recognition_job_id) — проверки FK при DELETE документа/файла и очистке перед повторным
// маппингом шли seq scan'ом (OQ-117). Замер: DELETE документа 1000 строк 8,6 с → 16 мс на 200 тыс. строк.
//
// v5.14 (Срез 1, решения владельца 04.10.2026):
// 1. ЮЛ может состоять ОДНОВРЕМЕННО в одной ГСК и одной ГСЗ (но только в одной группе каждого вида) — отменяет
// ANL-030/v5.8. В related_group_member возвращена денормализованная колонка kind (= related_group.kind,
// равенство проверяет ядро при записи); частичный уникальный индекс стал (legal_entity_id, kind) WHERE is_current.
// 2. КПП у ЮЛ не вводится (OQ-31: поле есть только в макете; решение владельца — не нужен). [Отменено в v5.17 — КПП введён.]
// 3. Без изменений структуры, уточнение семантики: company_group.crm_id = «iD ГК» выгрузки ЕЦП CRM
// (docs/source/biq/ecp-export-lagal-entity.xlsx), base_regional_office — «Базовый РФ ГК в 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.
//
// v5.13 (ревью 30.09–01.10, замечания 1–7; п.2 parent_entry_id — отложен):
// 1. Права как данные: permission + role_permission (матрица доступов ТЗ сноска 29 — в БД, не в коде);
// права только через роли, прямых назначений пользователю нет. Отменяет решение v5.4.
// 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_s3_object_id; сырой ответ ИС Сканирование в БД не регистрируется
// (scan-gateway пишет его в S3 по конвенции raw/{job_id}.json для разбора инцидентов).
// 6. stored_file.fields_modified убран — COUNT(is_modified) по данным документа.
// 6-бис. Разделение «файл / документ»: stored_file = физический файл (один на загрузку), document = логическая
// единица (ББ, ОФР, ОСВ по счёту, расшифровка) — один файл содержит N документов (PDF с ББ+ОФР, xlsx с
// несколькими ОСВ листами). Чек-лист, проверки периода/контрагента, подтверждение, FK разобранных данных,
// consolidation_document и ex_snapshot_item — на document. Замена (superseded_by) остаётся на файле целиком.
// 7. file_type: убран P7.
//
// Изменения относительно v4 (кратко):
// FIX — исправлены дефекты v4 (PK у meta, типы, копипаст-note про «контрагента», row_type, parent_entry_id)
// ADD — audit_event, status_history, company_group_assignment, elimination_rule, statement_check_result,
// consolidated_osv_line, chess_cell, vgo_pair_match (агрегат по ТЗ 6.4), ofr_vgo_match, kfo_result_*,
// client_kfo_line, comment, export_log/preset, ex_snapshot*, correction_version
// CHG — role → 7 ролей ТЗ + сегмент (ЗО); consolidation_version — сценарий/подход/гудвилл/решение УОБ;
// stored_file — счёт ОСВ, версии/замена; osv_entry — признак контрагента + override; legal_entity — монозаёмщик
//
// Ключи: у ВСЕХ сущностей суррогатный PK (uuid). Натуральные ключи — UNIQUE-колонки.
// (Для высокообъёмных таблиц — osv_entry, audit_event — uuid можно заменить на bigint identity.)
// ЮЛ и ГК — SCD Type 4 (живая таблица + *_history). Членство в ГСК/ГСЗ — SCD Type 2 (is_current).
// Первичка не мутируется: правки распознанных значений = поле + *_original_value + is_modified + audit_event.
// Производные объекты версии (сводная ОСВ, шахматка, КФО) — snapshot на consolidation_version:
// пересчитываются пока версия «в работе», замораживаются при VERIFIED (ТЗ 6.10, 7.2).
//
// DECISION-1 (ТЗ 6.4 vs v4): первичная модель сопоставления ВГО — АГРЕГАТ по паре компаний × правилу пары счетов
// (vgo_pair_match), как в ТЗ/Прил. 3. Строковое 1:1 сопоставление (vgo_match) сохранено как детализация агрегата.
// DECISION-2 (ЕХФД): ЕХ — те же таблицы + ex_snapshot как «замороженный указатель» на состав верифицированной версии.
// Отдельная физическая БД для ЕХ не предполагается; архив в data lake — выгрузка снапшота.
// DECISION-3: ГО — обычная запись regional_office с is_head_office=true (вместо NULL).
// DECISION-5 (производные консолидации): сводная ОСВ, пары ВГО и шахматка НЕ хранятся, пока версия в работе —
// считаются запросом из osv_entry (актуальные прогоны) × consolidation_member × elimination_rule.
// Признак контрагента ГСК/ГК/Внешний — функция от osv_entry.counterparty_id и периметра версии; ручное действие
// аналитика = сопоставление контрагента с ЮЛ (counterparty_id + алиас), комментарий — в audit_event.
// v5.16: способ сопоставления контрагента (osv_entry.counterparty_match_kind) ХРАНИТСЯ; пробелы квартальной склейки и сам свод — нет.
// При VERIFIED производные замораживаются один раз: структурированный снапшот — jsonb в ex_snapshot_item.payload
// (просмотр в ЛК ЕХ), подготовленный xlsx для выгрузки — s3_object. Отдельная NoSQL-база не вводится.
// Корректировки и гудвилл ссылаются на пару ВГО по естественному ключу (правило, компания 1, компания 2).
// DECISION-4 (распознавание): два отдельных stateless-сервиса без доступа к БД —
// tabular-parser (XLS/XLSX/XLSB) и scan-gateway (адаптер ИС Сканирование для PDF/PNG/JPG).
// Запрос: NATS recognition.{tabular|image}.request + файл в S3 uploads/. Результат: JSON в S3 results/ +
// NATS recognition.result. Единственный писатель в БД — arm-core; корреляция по recognition_job.id;
// идемпотентность по msg_id; outbox при создании recognition_job; DLQ.
// v5.13: результат одного файла содержит N документов; ядро создаёт document при маппинге результата.
//
// v5.1 (по макету dkb_24.09.26): consolidation_lock (взятие в работу), participant_disclosure (расшифровки участника),
// legal_entity_history.event_type, statement_meta реквизиты (ОКПО/ОКОПФ/ОКФС/адрес), kfo_result_line: vgo/share,
// client_kfo_line: ВГО клиента/банка + причина, audit_event: section/result/user_agent, статусы REJECTED/ARCHIVED,
// document_check (проверка периода/контрагента файла), company_group.crm_id nullable (ANL-037).
// v5.2 (DECISION-4): recognition_job: subject/msg_id/deadline/external_task_id/result_schema_version/error_code, статусы timeout/dlq.
// v5.12 (ревью part09): ЕХ самодостаточен — срез не ссылается FK на консолидацию и её результаты (их удаляют по сроку,
// срез хранится «вечно»): ex_snapshot.source_version_id/no без FK; ex_snapshot_item = stored_file_id (исходники храним)
// ИЛИ payload jsonb (сводная ОСВ, ВГО, шахматка, КФО, гудвилл, корректировки); убраны object_type/object_id,
// prepared_s3_object_id (готовые файлы не храним), ex_snapshot.source_system, audit_event.s3_object_id;
// добавлен ex_snapshot.superseded_by_snapshot_id (архивный → актуальный).
// v5.11: consolidation_version.elimination_rules_version — какая версия правил Прил. 3/4 применялась (объяснимость и
// воспроизводимость расчёта замороженной версии).
// v5.10 (DECISION-5, ревью part08): убраны consolidated_osv_line, consolidated_osv_source, vgo_pair_match, chess_sheet, chess_cell,
// osv_entry.counterparty_type (выводится); correction ссылается на пару по (rule_id, company_1_id, company_2_id);
// ex_snapshot_item получил payload jsonb.
// v5.9 (ревью part06/07): statement_check_result убрана — проверки считаются на лету, акцепт КА — на документе
// (financial_statement_meta.checks_accepted_*); reviewed_by/at перенесены в meta (документ ревьюится целиком);
// financial_statement_line: убраны ordinal/row_kind/section (порядок и группирующие строки — шаблон формы на фронте);
// consolidation: company_group_id NOT NULL, related_group_id nullable — ГСК фиксируется при VERIFIED (ANL-029);
// consolidation_version: убран package_uuid (документы в ИС Сканирование по одному, ключ = recognition_job.id), добавлен updated_by.
// v5.8 (ревью part01): убраны company_group_assignment (видимость ТЗ 8.1 выводится из company_group.base_regional_office_id,
// legal_entity.regional_office_id, app_user.regional_office_id; история перезакрепления — company_group_history),
// legal_entity.is_mono_borrower (монозаёмщик = company_group_id IS NULL), related_group_member.kind и индекс related_group(id, kind)
// (после ANL-030 partial-unique по legal_entity_id без условия на kind).
// v5.7: метрики качества распознавания — recognition_job.fields_total/low_confidence/unrecognized (что пришло от сервиса).
// (stored_file.fields_modified/fields_reviewed и financial_statement_value.needs_review — убраны в v5.9/v5.13 как производные.)
// v5.6 (ревизия 28.09, решения по каждой убранной таблице):
// account_code_alias — нормализация кодов в коде; редкие субсчета клиентов добавляются в chart_of_account
// vat_rate — константы; средневзвешенная ставка хранится в vgo_pair_match
// crm_import_batch — импорт справочников только на старте (миграция); след в audit_event(DIRECTORY_IMPORT) + файл в s3_object
// osv_entry_edit — audit_event(RECOGNIZED_DATA_EDIT) + osv_entry.original_values/modified_by/modified_at
// document_check — колонки period_check/counterparty_check (с v5.13 — на document); сверка с периодом версии вычисляется
// export_preset — выбор колонок в момент экспорта, без сохранения наборов
// export_log — audit_event(EXPORT, after_value={report_code, format, columns, rows}); файлы выгрузок не хранятся (S3 lifecycle exports/ = 7 дней)
// v5.5: убрана correction_version — история корректировок (сноска 22) ведётся audit_event(before/after), version_no в correction.
// v5.4: возвращён s3_object (реестр объектов S3 — lifecycle/ретеншн ТЗ 8.4, sha256, единая точка ключей); permission/role_permission — матрица в коде (ОТМЕНЕНО в v5.13).
// v5.3 РЕВИЗИЯ по правилу «в базе только то, что есть в ТЗ или на экране макета». Удалено 14 таблиц:
// - account_code_alias: нормализация кодов (60.1=60.01) — алгоритм в коде (ARCH-004), не справочник
// - vat_rate: ставки 0/10/20/22 — константы; средневзвешенная ставка хранится в vgo_pair_match.vat_weighted_rate
// - disclosure_item: расшифровки участника: в UI скрыты, в ТЗ только тип документа «Расшифровка» → вне scope до решения ANL-040
// - participant_disclosure: см. disclosure_item
// - crm_import_batch: журнал импортов не в ТЗ/UI; факт импорта и ошибки — audit_event(GROUP_CHANGE/IMPORT)
// - permission / role_permission: ВОЗВРАЩЕНЫ в v5.13 (замечание 1)
// - integration_message: РЕШЕНИЕ — обмен с NATS завязан только на recognition_job (status queued→published = outbox; result_msg_id UNIQUE = inbox/дедуп); отдельная таблица появится, только если возникнет второй тип сообщений
// - osv_entry_edit: история правок сумм ОСВ — audit_event(RECOGNIZED_DATA_EDIT, before/after)
// - document_check: результат проверки периода/контрагента — две колонки (с v5.13 — в document)
// - vgo_match: строковое сопоставление 1:1 — только в аналитике, нет ни в ТЗ (агрегат по Прил. 3), ни в UI; детализация агрегата — consolidated_osv_source
// - export_preset: сохранённые пресеты колонок не в ТЗ/UI (выбор столбцов — при экспорте, без сохранения)
// - export_log: история выгрузок (ТЗ 8.2, ЕХ 6.6) — audit_event(EXPORT) с составом в after_value
// ============ ENUMS ============
Enum entity_type {
"ЮЛ"
"ИП"
}
Enum related_group_kind {
gsk
gsz
}
Enum account_kind {
A [note: 'актив']
P [note: 'пассив']
AP [note: 'активно-пассивный']
}
Enum account_row_type {
account [note: 'синтетический счёт, напр. 60']
subaccount [note: 'субсчёт, напр. 60.01']
analytic [note: 'подсубсчёт, напр. 60.01.1 / 76.07.1']
}
Enum business_segment {
DKB [note: 'крупный бизнес']
DMSB [note: 'малый и средний']
DM [note: 'микробизнес']
}
Enum document_type {
BALANCE [note: 'Бухгалтерский баланс (0710001)']
INCOME [note: 'ОФР (0710002)']
OSV [note: 'ОСВ по счёту — osv_account_code обязателен']
DISCLOSURE [note: 'Расшифровка']
OTHER [note: 'Прочий документ']
}
Enum file_type {
XLS
XLSX
XLSB
PDF
PNG
JPG
}
Enum recognition_pipeline {
tabular [note: 'XLS/XLSX/XLSB — tabular-parser']
image [note: 'PDF/PNG/JPG — ИС Сканирование через scan-gateway']
}
Enum period_quarter {
Q1
Q2
Q3
Q4
}
// v5.13: статус ФАЙЛА — только техническая обработка. Проверка и подтверждение аналитиком — на document.
Enum file_status {
UPLOADED [note: 'Загружен: поступил, в очереди']
PROCESSING [note: 'Обработка: распознавание/парсинг']
PROCESSED [note: 'Результат получен, документы созданы']
ERROR [note: 'Ошибка: нечитаемый файл, требуется повторная загрузка']
SUPERSEDED [note: 'Заменён новой версией файла (ТЗ 8.2) — вместе со всеми документами в нём']
}
// v5.13: статус ДОКУМЕНТА (ANL-003: «Требуется проверка» при confidence < 100% или непройденных проверках;
// «Подтвержден» — финальный после проверки аналитиком)
Enum document_status {
RECOGNIZED [note: 'данные получены, ручная проверка не требуется по порогу']
NEEDS_CHECK [note: 'Требуется ручная проверка: confidence < 100% / контрольные суммы / период или контрагент не совпали']
CONFIRMED [note: 'Подтвержден аналитиком: данные зафиксированы, доступны консолидации']
ERROR [note: 'документ не разобран (напр. не найдена шапка ОСВ)']
}
Enum s3_bucket_role {
uploads
results
exports
archive
}
Enum recognition_status {
queued [note: 'создано, запрос в outbox']
published [note: 'запрос опубликован в NATS']
processing [note: 'сервис подтвердил взятие в работу (опц. heartbeat)']
done
needs_check [note: 'результат получен, но confidence < 100% / контрольные суммы не сошлись']
error
timeout [note: 'reply_deadline истёк без результата']
dlq [note: 'сообщение ушло в DLQ после max_deliver']
}
Enum statement_type {
balance
income
}
// v5.19: статус строки формы ББ/ОФР (05-domain-rules.md §11.4)
Enum statement_line_status {
ok [note: 'код разобран']
unreadable [note: 'в ячейке кода есть цифры, но код не разобран (сырой текст — line_code_text)']
missing [note: 'строка без кода с содержимым, выше в документе нет строки с кодом']
detail [note: 'расшифровка без кода под строкой с кодом (Р-8): в §7, агрегаты, КФО и ЕХ не входит']
}
// v5.19: вариант формы — по структуре кодов (§11.7)
Enum statement_form_variant {
full
simplified [note: 'упрощённая форма (приказ 66н, прил. 5): ББ без 1100/1200, ОФР без 2100/2200/2300']
}
// v5.19: вид записи document_issue
Enum document_issue_kind {
warning [note: 'предупреждение (05-domain-rules.md §11.9, таблица уровней)']
relation [note: 'жёсткое соотношение §7, не сошедшееся вне допуска и округления']
soft [note: 'мягкое соотношение или жёсткое в пределах округления (OQ-138)']
}
Enum issue_severity {
error [note: 'документ needs_check']
info [note: 'на статус не влияет']
}
Enum issue_source {
service [note: 'пришло в результате распознавания']
core [note: 'посчитано ядром (проверки по словарю имён и перечню кодов; пересчёт §7 после правки)']
}
Enum osv_group_type {
ACCOUNT
DEPARTMENT
COUNTERPARTY
CONTRACT
OTHER
}
Enum counterparty_match_kind {
INN [note: 'автоматический резолв при маппинге: ИНН строки совпал с legal_entity.inn']
NAME [note: 'автоматический резолв: точное совпадение имени строки с legal_entity.name']
ALIAS [note: 'автоматический резолв: нормализованное имя ЮЛ или алиас (legal_entity_alias)']
MANUAL [note: 'ручное сопоставление аналитиком']
PROPAGATED [note: 'автоприменение ручного сопоставления к строкам с тем же именем в версии (DOC-221278 п. 12–13)']
}
Enum osv_data_type {
BU
NU [note: 'v5.15: налоговый учёт из ОСВ «БУ, НУ» (OQ-45)']
CURRENCY
QUANTITY
SUM
}
Enum consolidation_scenario {
CREATE [note: '«Создание КФО» при отсутствии клиентской консолидации']
CONFIRM [note: '«Подтверждение КФО клиента»']
}
Enum consolidation_approach {
BASIC [note: 'базовый: ББ, ОФР, ОСВ 01,58,60,62,66,67,76,90']
EXTENDED [note: 'опция к базовому: + ОСВ 02,03,08,44,26,91,51,52,59,80,83,86,97,001,008,009,011']
}
// Статусы КФО по ТЗ 6.10 + «Корректировка» при расхождении ≥5% (ТЗ 6.4)
Enum consolidation_status {
INITIATED
DOCUMENTS_PROCESSING
DATA_CORRECTION [note: 'корректировка распознанных данных']
CONSOLIDATION_RF
ADJUSTMENT [note: '«Корректировка» ВГО (расхождение ≥ 5%); подстатус этапа консолидации']
VERIFICATION_RF
CONSOLIDATION_GO
VERIFICATION_GO [note: 'РКА ГО, если КФО инициировано КА ГО (ТЗ 6.10 маршрут)']
VERIFIED
CANCELLED
// v5.1 по макету — ANL-035: подтвердить с заказчиком
REJECTED [note: 'Отклонено: возвращено на доработку (макет). Альтернатива — событие в status_history без отдельного статуса']
ARCHIVED [note: 'Архив: скрыто из основного списка (макет); применимо к VERIFIED/CANCELLED']
}
Enum correction_status {
DRAFT
APPLIED
REVOKED
}
Enum document_check_result {
MATCH
MISMATCH
UNKNOWN
}
Enum le_history_event {
CREATE
UPDATE
MOVE_TO_GROUP [note: '«Переход в другую ГК»']
CLOSE [note: '«Закрытие» / вывод из состава ГК']
REMOVE_FROM_GROUP
}
Enum ex_snapshot_status {
ACTUAL
ARCHIVE
}
// v5.13: объекты и действия матрицы прав (ТЗ Прил. 2 «Роль: перечень полномочий», сноска 29)
Enum permission_object {
DIRECTORY [note: 'справочники ГК/ГСК/ГСЗ/ЮЛ']
DOCUMENT [note: 'первичные документы и распознанные данные']
CONSOLIDATION [note: 'консолидация, версии, корректировки, гудвилл']
KFO [note: 'результат КФО: утверждение, верификация']
EX [note: 'Единое хранилище']
EXPORT
USERS [note: 'пользователи и роли']
AUDIT [note: 'журнал действий']
}
Enum permission_action {
VIEW
EDIT
APPROVE [note: 'утверждение в РФ']
VERIFY [note: 'верификация в ГО']
DELETE
EXPORT
ADMIN
}
Enum audit_section {
DIRECTORIES
CONSOLIDATION
PRIMARY_DOCUMENTS
RECOGNITION
KFO
EXPORT
ACCESS
SESSION
USERS
}
Enum audit_action {
LOGIN
LOGIN_FAILED
LOGOUT
CONSOLIDATION_CREATE
CONSOLIDATION_STATUS_CHANGE
CONSOLIDATION_APPROVE
CONSOLIDATION_VERIFY
GROUP_CHANGE
LEGAL_ENTITY_CHANGE [note: 'v5.14: ЮЛ создано/изменено/закрыто/переведено, алиасы']
MEMBERSHIP_CHANGE
ASSIGNMENT_CHANGE
FILE_UPLOAD
FILE_DELETE
FILE_REPLACE
DOCUMENT_CONFIRM
CONSOLIDATION_LOCK
CONSOLIDATION_LOCK_TAKEOVER
DISCLOSURE_EDIT
RECOGNIZED_DATA_EDIT
OSV_COUNTERPARTY_MAP
OSV_TYPE_OVERRIDE
CORRECTION_CREATE
CORRECTION_EDIT
CORRECTION_REVOKE
GOODWILL_EDIT
EXPORT
DIRECTORY_IMPORT
USER_ROLE_CHANGE
USER_CHANGE [note: 'v5.14: пользователь создан/изменён/деактивирован администратором']
ROLE_PERMISSION_CHANGE
}
// ============ СПРАВОЧНИКИ ============
// ИСТОЧНИК:
// ТЗ: ТЗ 8.1 (пользователь ГО / пользователь РФ; закрепление ГК за РФ); Прил. 2 «Пользователь: привязка к РФ/ГО»
// Аналитика: md «Модель данных» (regional_office)
// Макет: справочник ГК: колонка «Основной РФ»; пользователи: «Ростовский РФ» и т.п.
// В модели: v4
// Зачем: справочник филиалов; DECISION-3 — ГО явной записью
Table regional_office {
id uuid [pk]
name varchar [not null, unique]
is_head_office boolean [not null, default: false, note: 'DECISION-3: ГО — явная запись']
is_active boolean [not null, default: true]
Note: 'Филиал: ГО и региональные филиалы (РФ)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.1 (справочник ГК, импорт из ЕЦП CRM, создание вручную Куратором); Прил. 2 «ГК: id, наименование, id ЕЦП CRM»
// Аналитика: md «Модель данных» (company_group, SCD4)
// Макет: «Группы компаний»: Создать ГК, CRM ID «генерируется автоматически», Архив
// В модели: v4 + v5.1 (code, crm_id nullable — ANL-037)
Table company_group {
id uuid [pk]
code varchar [not null, unique, note: 'v5.1 ANL-037: внутренний код ГК, генерируется системой']
crm_id varchar [unique, note: 'ключ из ЕЦП CRM; NULL для ГК, созданной вручную Куратором до сверки с CRM']
name varchar [not null]
base_regional_office_id uuid [ref: > regional_office.id, note: 'закрепление ГК за филиалом (ТЗ 6.1/8.1); перезакрепление Куратором = UPDATE + снимок в history']
is_archived boolean [not null, default: false, note: 'v5.1 макет: «Архив» ГК']
valid_from timestamptz [not null]
updated_by uuid [ref: > app_user.id]
created_at timestamptz
updated_at timestamptz
Note: 'ГК — ТЕКУЩЕЕ состояние (SCD4). История — company_group_history'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.10 «фиксация изменений ГК/ГСК в истории изменений»; 8.2 «изменение состава ГСК, ГК»
// Аналитика: md «Модель данных» (company_group_history)
// Макет: «История изменений» в карточке ГК
// В модели: v4
// Зачем: прошлые снимки ГК (SCD4)
Table company_group_history {
id uuid [pk]
company_group_id uuid [not null, ref: > company_group.id]
crm_id varchar
name varchar
base_regional_office_id uuid
valid_from timestamptz
valid_to timestamptz
changed_by uuid [ref: > app_user.id]
Indexes { (company_group_id, valid_from) [unique] }
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.1 (ЮЛ/ИП, только одна ГСК/ГСЗ); Прил. 2 «ЮЛ: id, наименование, ИНН, id CRM, ГК, признак»; ЕХ ТЗ 6.1 «Монозаёмщик»
// Аналитика: md «Модель данных» (legal_entity, SCD4)
// Макет: «Юридические лица», «ЮЛ вне ГК» / «Монозаемщики»
// В модели: v4 + v5 (crm_id, is_mono_borrower, ГК nullable); v5.17 — kpp
Table legal_entity {
id uuid [pk]
inn varchar [not null, unique]
crm_id varchar [unique, note: 'ADD: id ЮЛ в ЕЦП CRM (Прил. 2 ТЗ)']
entity_type entity_type
name varchar [not null]
company_group_id uuid [ref: > company_group.id, note: 'nullable: NULL = монозаёмщик (ЕХ 6.1), признак выводится']
regional_office_id uuid [ref: > regional_office.id]
close_date date [note: 'NULL = активна']
valid_from timestamptz [not null]
updated_by uuid [ref: > app_user.id]
created_at timestamptz
updated_at timestamptz
kpp varchar [note: 'v5.17 (добавлена последней колонкой, как её добавляет миграция 0005): КПП, nullable; 9 символов (4 цифры, 2 цифры/латинские A–Z, 3 цифры); у ИП запрещён — проверяет ядро (INVALID_KPP). Источник: макет [50][51-02][61][79], решение владельца 07.10.2026 (OQ-31)']
Note: 'Организация (ЮЛ/ИП) — ТЕКУЩЕЕ состояние (SCD4). История — legal_entity_history'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 8.2 (журнал изменений справочников); 6.10 (история изменений состава)
// Аналитика: md «Модель данных» (legal_entity_history)
// Макет: Архив ГК: события «Переход в другую ГК», «Закрытие», дата перевода
// В модели: v4 + v5.1 (event_type, effective_date — ANL-046); v5.17 — kpp
Table legal_entity_history {
id uuid [pk]
legal_entity_id uuid [not null, ref: > legal_entity.id]
inn varchar
crm_id varchar
entity_type entity_type
name varchar
company_group_id uuid
regional_office_id uuid
close_date date
event_type le_history_event [note: 'v5.1 макет: «Переход в другую ГК» / «Закрытие» — для архива ГК']
effective_date date [note: 'дата перевода/закрытия (не раньше текущей — ANL-046)']
valid_from timestamptz
valid_to timestamptz
changed_by uuid [ref: > app_user.id]
kpp varchar [note: 'v5.17 (последней колонкой, миграция 0005): снимок КПП на момент события']
Indexes { (legal_entity_id, valid_from) [unique] }
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.5 «ведение справочника соответствий наименований контрагентов», «использование ранее подтверждённых сопоставлений»
// Аналитика: md «Сводная ОСВ» (алгоритм ручного сопоставления, legal_entity_alias); FNR01 п.9
// Макет: карточка ЮЛ: «Альтернативные наименования», «Связь с компанией из ОСВ (алиас)»
// В модели: v4
Table legal_entity_alias {
id uuid [pk]
legal_entity_id uuid [not null, ref: > legal_entity.id]
alias varchar [not null, note: 'как в ОСВ']
normalized_alias varchar [not null, note: 'lower/trim/без пунктуации/без ОПФ']
created_by uuid [ref: > app_user.id]
created_at timestamptz
Indexes {
(legal_entity_id, normalized_alias) [unique]
normalized_alias [note: 'ANL-028: конфликт «один алиас у разных ЮЛ» решается при ручном сопоставлении']
}
Note: 'Словарь соответствий наименований контрагентов (ТЗ 6.5)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.1 (ГСК/ГСЗ в составе ГК; иерархия не поддерживается; в одной ГК несколько ГСК — сноска 11); Прил. 2 «ГСК/ГСЗ»
// Аналитика: md «Модель данных» (related_group, kind)
// Макет: вкладки «ГСК», «ГСЗ», коды GSK-001/GSZ-001
// В модели: v4; v5.19 — crm_id
Table related_group {
id uuid [pk]
code varchar [not null, unique]
company_group_id uuid [not null, ref: > company_group.id]
kind related_group_kind [not null]
name varchar [not null]
created_date date
crm_id varchar [unique, note: 'v5.19: ID ГСК/ГСЗ в ЕЦП CRM (формы 51-03, 51-04, 77-02; arm-core#112, OQ-33); вводится вручную, импорт ЕЦП не заполняет (OQ-91); NULL — не задан']
Note: 'ГСК/ГСЗ — current-state. Иерархия групп не поддерживается (ТЗ 6.1)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.1 «КА может редактировать состав ГСК»; «одно ЮЛ только в одной ГСК или ГСЗ»
// Аналитика: md «Модель данных» (related_group_member, SCD2)
// Макет: «Состав», «Входит в ГСК / ГСЗ», «Редактировать состав»
// В модели: v4 (индекс уточнён — ANL-030); v5.14 — kind и индекс (legal_entity_id, kind)
Table related_group_member {
id uuid [pk]
related_group_id uuid [not null, ref: > related_group.id]
legal_entity_id uuid [not null, ref: > legal_entity.id]
kind related_group_kind [not null, note: 'v5.14: денормализация related_group.kind ради частичного уникального индекса; равенство с группой проверяет ядро при записи']
valid_from timestamptz [not null]
valid_to timestamptz
is_current boolean [not null]
created_by uuid [ref: > app_user.id]
Indexes {
(related_group_id, legal_entity_id) [note: 'partial UNIQUE WHERE is_current']
(legal_entity_id, kind) [note: 'partial UNIQUE WHERE is_current — v5.14: ЮЛ в одной ГСК И одной ГСЗ одновременно (решение владельца 04.10.2026, отменяет ANL-030); ГК у ЮЛ и у группы должна совпадать (проверяет ядро)']
}
Note: 'Состав ГСК/ГСЗ — SCD2. Исключение = закрытие версии (valid_to, is_current=false)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2/6.5 (счета и субсчета 01…90, нормализация 60.1=60.01); Прил. 3/4 (пары счетов); сноска 32
// Аналитика: md «Модель данных» (chart_of_account); md «Шахматка» (kind A/P/AP, row_type)
// Макет: фильтр «Дебетовые/Кредитовые счета», полный план счетов 1С в бандле
// В модели: v4 + v5 (row_type, is_off_balance)
Table chart_of_account {
id uuid [pk]
code varchar [not null, unique, note: 'нормализованный код: 60.01, 76.07.1']
parent_id uuid [ref: > chart_of_account.id]
name varchar [not null]
kind account_kind
row_type account_row_type [not null, note: 'FIX: используется в «Шахматке»']
is_off_balance boolean [not null, default: false, note: '001, 008, 009, 011']
Note: 'План счетов'
}
// ADD ТЗ Прил. 3/4 как данные (сноски 32/35: пары могут корректироваться на этапе реализации)
// ИСТОЧНИК:
// ТЗ: ТЗ 6.4 + Прил. 3 «Перечень статей ББ» и Прил. 4 «Модель расчётов ОФР»; сноски 32/35 «могут быть скорректированы на этапе реализации»
// Аналитика: md «Автоматическое сопоставление ВГО» (таблица «Правила встречных счетов» — хардкод)
// Макет: —
// В модели: v5 новая
// Зачем: правила элиминации как данные, версионируемые
Table elimination_rule {
id uuid [pk]
statement_type statement_type [not null, note: 'balance = Прил. 3, income = Прил. 4']
rule_no smallint [not null, note: '№ строки приложения']
article_1_line varchar [not null, note: 'статья Компании 1, код строки ББ/ОФР, напр. 1230']
article_2_line varchar [not null, note: 'статья Компании 2, напр. 1520']
side_1 varchar [not null, note: 'D | K — сторона Компании 1']
measure varchar [not null, note: 'closing | turnover — сальдо на конец (ББ) или оборот (ОФР)']
elimination_mode varchar [not null, note: 'min | full | manual — min(A,B) / вся сумма ВГО (ОФР выручка) / вручную']
vat_handling varchar [note: 'none | check_90_01_vs_2110 | weighted_rate — только для ОФР']
excise_adjust boolean [not null, default: false]
comment text
version_no int [not null]
valid_from date [not null]
valid_to date
Indexes { (statement_type, rule_no, version_no) [unique] }
Note: 'Правило элиминации. Состав счетов — в elimination_rule_account'
}
// ИСТОЧНИК:
// ТЗ: ТЗ Прил. 3/4 (столбцы «Пары счетов Компании 1 и Компании 2», исключения «76 кроме 76.7.1»)
// Аналитика: md «Автоматическое сопоставление ВГО»
// Макет: —
// В модели: v5 новая
// Зачем: состав счетов сторон правила
Table elimination_rule_account {
id uuid [pk]
rule_id uuid [not null, ref: > elimination_rule.id]
company_no smallint [not null, note: '1 или 2']
account_id uuid [not null, ref: > chart_of_account.id]
is_excluded boolean [not null, default: false, note: 'true = исключение внутри маски (напр. «76 кроме 76.7.1»)']
Note: 'Счета/субсчета сторон правила'
}
// ============ ПОЛЬЗОВАТЕЛИ И ДОСТУП ============
// ИСТОЧНИК:
// ТЗ: ТЗ 8.1 (роли, ГО/РФ, ЗО); Прил. 2 «Пользователь: ФИО, роль, привязка к РФ/ГО, ЗО, табельный номер»; РАИСА
// Аналитика: md «Аутентификация и авторизация» (users: email, role, JWT РАИСА); md «Модель данных» (app_user)
// Макет: «Пользователи»: Логин, Роль, Активен, Деактивация
// В модели: v4 + v5 (employee_no, business_segment)
Table app_user {
id uuid [pk]
external_id varchar [unique, note: 'субъект РАИСА/SSO; аутентификация внешняя (ANL-018)']
email varchar [unique, note: 'v5.15: хранится в нижнем регистре (нормализация при записи), сравнение по lower(email) (OQ-59)']
full_name varchar [not null]
employee_no varchar [note: 'ADD: табельный номер (Прил. 2 ТЗ)']
regional_office_id uuid [not null, ref: > regional_office.id, note: 'ГО или РФ (DECISION-3)']
business_segment business_segment [note: 'ADD: зона ответственности ДКБ/ДМСБ/ДМ (ТЗ 8.1)']
is_active boolean [not null, default: true]
created_at timestamptz
updated_at timestamptz
}
// ИСТОЧНИК:
// ТЗ: ТЗ 8.1 таблица ролей (КА РФ, КА ГО, РКА, КМ, Куратор, Эксперт, Администратор); Прил. 2 «Роль»
// Аналитика: md «Ролевая модель»; md «Модель данных» (role.code)
// Макет: «Изменение ролей и прав доступа» (Аналитик ДКБ, Наблюдатель… — расходится с ТЗ, ANL-034)
// В модели: v4 (коды → ТЗ в v5); v5.13 — матрица полномочий в role_permission
Table role {
id uuid [pk]
code varchar [not null, unique, note: 'ТЗ 8.1: KA_RF | KA_GO | RKA | CLIENT_MANAGER | CURATOR_GO | EXPERT | ADMIN. Seed; администратор может заводить дополнительные роли (замена исключениям по пользователю)']
name varchar [not null]
description text
is_system boolean [not null, default: false, note: 'роли ТЗ 8.1 — не удаляются']
is_active boolean [not null, default: true]
Note: 'Роль. Полномочия — через role_permission (v5.13); код роли в бизнес-логике больше не проверяется'
}
// ИСТОЧНИК:
// ТЗ: ТЗ Прил. 2 «Роль: перечень полномочий»; сноска 29 «матрица доступов будет разработана»
// Аналитика: md «Ролевая модель»
// Макет: карточка роли: «Права: Просмотр, редактирование КФО / справочники / консолидации»
// В модели: v5 новая, убрана в v5.3/v5.4, ВОЗВРАЩЕНА в v5.13 (замечание 1)
// Зачем: атомарное право «объект × действие»; seed, администратор новые коды не создаёт
Table permission {
id uuid [pk]
code varchar [not null, unique, note: 'напр. CONSOLIDATION_EDIT, KFO_VERIFY, DIRECTORY_EDIT, USERS_ADMIN']
object_type permission_object [not null]
action permission_action [not null]
name varchar [not null]
description text
Indexes { (object_type, action) [unique] }
Note: 'Справочник прав (seed, привязан к коду). Ограничения по ЗО/филиалу и статусу КФО — отдельный policy-слой поверх прав: право говорит «может», policy — «эту ли и сейчас ли»'
}
// ИСТОЧНИК:
// ТЗ: ТЗ Прил. 2 «Роль: перечень полномочий»
// Аналитика: нет
// Макет: «Права» в карточке роли, «Изменение ролей и прав доступа»
// В модели: v5.13 (возвращена)
// Зачем: матрица доступов как данные; редактируется администратором, история — audit_event(ROLE_PERMISSION_CHANGE)
Table role_permission {
role_id uuid [not null, ref: > role.id]
permission_id uuid [not null, ref: > permission.id]
granted_by uuid [ref: > app_user.id]
granted_at timestamptz
Indexes { (role_id, permission_id) [pk] }
Note: 'effective_permissions(user) = ∪ permissions(roles(user)). Прямых прав у пользователя нет (решение 30.09): исключения — через заведение роли'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 8.1 (роль пользователя); Прил. 2 «Пользователь — Роль: многие-к-одному»
// Аналитика: md «Модель данных» v4 (user_role M:N)
// Макет: «Роль» пользователя
// В модели: v4
// Зачем: M:N оставлен (пользователь может быть КА и Куратором); если ТЗ строго 1:1 — заменить на app_user.role_id
Table user_role {
id uuid [pk]
user_id uuid [not null, ref: > app_user.id]
role_id uuid [not null, ref: > role.id]
Indexes { (user_id, role_id) [unique] }
Note: 'M:N current-state. История назначений — audit_event(USER_ROLE_CHANGE)'
}
// ============ ФАЙЛЫ / РАСПОЗНАВАНИЕ ============
// ИСТОЧНИК:
// ТЗ: 6.2 (файлы с префиксом UUID, исходные документы хранятся); 8.4 «хранение 5 лет с даты прекращения отношений, архивирование/удаление»; ЕХ 6.2 «ссылка на исходные документы с возможностью скачать», ЕХ 8.3 «архивирование в data lake»
// Аналитика: md «Модель данных» v4 (s3_key, raw_result_s3_key — строки)
// Макет: —
// В модели: v5.2 новая (DECISION-4), подтверждена в v5.4
// Зачем: единая точка ключей всех бакетов для lifecycle/ретеншна и дедупа; без реестра ключи размазаны по 4 таблицам
Table s3_object {
id uuid [pk]
bucket_role s3_bucket_role [not null]
bucket varchar [not null]
key varchar [not null, note: 'схема ключа: {file_id}/{job_id}/{name}']
content_type varchar
size_bytes bigint
sha256 varchar
version_id varchar [note: 'S3 object version при включённом versioning']
created_by varchar [note: 'arm-core | tabular-parser | scan-gateway | user:{id}']
created_at timestamptz [not null]
expires_at timestamptz [note: 'lifecycle: NULL = весь срок (ТЗ 8.4); exports/ = 7 дней; results/ — решение ARCH-010']
archived_to varchar [note: 'ссылка в data lake после архивирования (ЕХ 8.3)']
Indexes { (bucket, key) [unique] }
Note: 'Реестр объектов S3. stored_file / recognition_job ссылаются сюда FK'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2 (типы и форматы документов, UUID); 8.2 «первичные документы не редактируются, только замена с версионностью»
// Аналитика: md «Загрузка документов» (панель, UC-01…05); FNR01 «Маппинг stored_file»
// Макет: «Первичные документы», «Загрузка документов», «Замена документа», «Удаление документа»
// В модели: v4 + v5 (version_no, superseded_by) + v5.2 (s3_object_id); v5.13 — только ФИЗИЧЕСКИЙ файл, документы → document; v5.18 — consolidation_id
Table stored_file {
id uuid [pk]
legal_entity_id uuid [not null, ref: > legal_entity.id, note: 'организация-владелец (выбрана при загрузке)']
file_type file_type [not null]
original_name varchar
s3_object_id uuid [not null, ref: > s3_object.id, note: 'исходный файл в uploads']
content_hash varchar [note: 'sha256, дедуп (UC-01 A2)']
size_bytes bigint
status file_status [not null]
version_no int [not null, default: 1, note: 'ADD ТЗ 8.2: версия при замене']
superseded_by_file_id uuid [ref: > stored_file.id, note: 'ссылка на файл-замену; статус SUPERSEDED. Замена — файл ЦЕЛИКОМ со всеми документами в нём (решение 01.10)']
current_recognition_job_id uuid [ref: > recognition_job.id, note: 'авторитетный прогон; его результат породил актуальные document']
uploaded_by uuid [ref: > app_user.id]
uploaded_at timestamptz
updated_at timestamptz
consolidation_id uuid [ref: > consolidation.id, note: 'v5.18: консолидация (период ГК), в контексте которой загружен файл — основа «рабочего стола консоли» (полный состав — context/05-domain-rules.md §9.5 п. 11), общего для версий периода; NULL — без контекста. Пишется при загрузке; при автозамене файл без контекста наследует значение заменённого']
Indexes {
(legal_entity_id, status)
(legal_entity_id, consolidation_id) [note: 'v5.18: основа рабочего стола консоли для ЮЛ (§9.5 п. 11)']
current_recognition_job_id [note: 'v5.15: проверка FK при удалении recognition_job / файла (OQ-117)']
}
Note: 'v5.13: физический файл одной загрузки (S3 — только ссылка). Что внутри — document (1:N). Первичка не редактируется. Удаление физическое запрещено после INITIATED — только SUPERSEDED (ANL-020). v5.18: consolidation_id — где загружен; что использует версия — consolidation_document'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2 (типы документов: ББ, ОФР, ОСВ по счетам; обязательный набор); 5.1 (подход); 8.2; Прил. 2 «Документ ФО»
// Аналитика: md «Загрузка документов» (чек-лист, UC-01 A1 период, «Подтвердить данные»); md «Парсинг ОСВ» (счёт/период из шапки)
// Макет: «Чек-лист загруженных документов», «Соответствует период», «Не соответствует контрагент», «Подтвердить данные»
// В модели: v5.13 новая (замечание 6-бис); v5.19 — file_ordinal, уникальность типа только для не-форм
// Зачем: один файл = N документов (PDF ББ+ОФР; xlsx с несколькими ОСВ). Чек-лист, проверки, подтверждение и
// FK разобранных данных — здесь; без этого «ОСВ 58 загружена?» по файлу не ответить
Table document {
id uuid [pk]
stored_file_id uuid [not null, ref: > stored_file.id]
legal_entity_id uuid [not null, ref: > legal_entity.id, note: 'денормализация stored_file.legal_entity_id — выборки чек-листа по ЮЛ+период']
document_type document_type [not null]
osv_account_code varchar [note: 'для OSV — счёт по шапке (01, 60, 62…), нормализованный; NULL для прочих типов']
period_year smallint [note: 'из шапки/формы']
period_quarter period_quarter
is_cumulative boolean [note: 'true = нарастающий итог с начала года']
reporting_date date [note: 'отчётная дата, если определена']
locator varchar [note: 'где в файле: лист xlsx / диапазон страниц PDF (напр. «sheet:ОСВ 58», «pages:1-2»)']
status document_status [not null]
period_check document_check_result [note: 'UC-01 A1 / макет «Соответствует период»']
counterparty_check document_check_result [note: 'макет «Не соответствует контрагент» — ИНН/наименование из шапки vs legal_entity']
confirmed_by uuid [ref: > app_user.id, note: 'кто нажал «Подтвердить данные» → audit_event(DOCUMENT_CONFIRM)']
confirmed_at timestamptz
created_at timestamptz
updated_at timestamptz
file_ordinal smallint [not null, default: 0, note: 'v5.19: индекс документа в documents[] результата распознавания (с 0), у всех документов при любом MAJOR контракта; несозданный документ номер не занимает (номера с пропусками); различает две формы одного типа в файле (Р-10)']
Indexes {
(stored_file_id, document_type, osv_account_code) [note: 'partial UNIQUE WHERE document_type NOT IN (\'BALANCE\', \'INCOME\') — NULLS NOT DISTINCT; v5.19: одна ОСВ по счёту / одна расшифровка / один прочий на файл; формы ББ/ОФР одного типа в файле могут повторяться (Р-10)']
(stored_file_id, file_ordinal) [unique, note: 'v5.19: номер документа уникален в файле']
(legal_entity_id, document_type, osv_account_code, period_year, period_quarter, status) [note: 'чек-лист required_document_rule']
}
Note: 'Логический документ внутри файла. Создаётся ядром при маппинге результата recognition_job. Разобранные данные: OSV → osv_entry; BALANCE/INCOME → financial_statement_meta. Счётчики «исправлено полей» — COUNT(is_modified) по данным, не хранятся. v5.19: предупреждения и нарушения §7 — document_issue; две формы одного типа и периода в файле не автоподтверждаются (Р-18)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2 (статусы процесса распознавания, ИС Сканирование); 6.10 «повторная обработка»; 7.1
// Аналитика: FNR01 «Маппинг recognition_job»; md «Загрузка документов» UC-02; md «Парсинг ОСВ» (схема группировок шапки)
// Макет: «Система распознавания: получен ответ», «Обработка», «Ошибка»
// В модели: v4, переписана в v5.2 под контракт NATS (DECISION-4); v5.13 — group_schema jsonb, убран raw_result
Table recognition_job {
id uuid [pk, note: 'job_id в контракте NATS']
file_id uuid [not null, ref: > stored_file.id]
pipeline recognition_pipeline [not null, note: 'tabular → tabular-parser; image → scan-gateway']
request_subject varchar [not null, note: 'recognition.tabular.request | recognition.image.request']
request_msg_id varchar [unique, note: 'Nats-Msg-Id запроса (outbox)']
result_msg_id varchar [unique, note: 'Nats-Msg-Id результата (inbox, дедуп)']
schema_version varchar [not null, note: 'версия контракта сообщений']
service_name varchar [note: 'tabular-parser | scan-gateway — из результата']
service_version varchar
status recognition_status [not null]
attempt smallint [not null, default: 1]
max_attempts smallint [not null, default: 1, note: 'OCR: 1 (0 повторов), парсер: 3']
requested_at timestamptz
published_at timestamptz
started_at timestamptz
finished_at timestamptz
reply_deadline timestamptz [note: 'таймаут ожидания (OCR — 20 мин)']
external_task_id varchar [note: 'id задания в ИС Сканирование (только image)']
error_code varchar [note: 'UNSUPPORTED_FORMAT | UNREADABLE | TIMEOUT | SCHEMA_MISMATCH | …']
error_message text
result_s3_object_id uuid [ref: > s3_object.id, note: 'нормализованный JSON в results — единственная ссылка на результат (v5.13). Сырой ответ ИС Сканирование — S3 raw/{job_id}.json по конвенции, в БД не регистрируется']
result_schema_version varchar [note: 'версия схемы JSON результата (ARCH-008/009)']
group_schema jsonb [note: 'v5.13 (вместо osv_group_schema): схема группировок шапки ОСВ по листам — [{\"locator\":\"sheet:ОСВ 58\",\"groups\":[{\"type\":\"ACCOUNT\"},{\"type\":\"COUNTERPARTY\",\"has_inn\":true},{\"type\":\"CONTRACT\"}]}]; только для tabular']
metrics jsonb [note: 'duration_ms, pages, rows, warnings (v5.19 / контракт 2.0: предупреждения уровня файла — структуры {code, severity, message, locator?}; error уровня файла блокирует RECOGNIZED документов файла)']
fields_total int [note: 'v5.7: всего извлечённых значений (ячейки формы / строки ОСВ)']
fields_low_confidence int [note: 'v5.7: значений с confidence < 100 (ТЗ 6.2) — заполняется при маппинге результата']
fields_unrecognized int [note: 'v5.7: значений с is_recognized = false']
statements_version varchar [note: 'v5.19: service.statements_version результата — коммит копии общего кода arm_statements; при пересчёте после правки сравнивается с копией ядра (расхождение — document_issue info)']
Indexes { (file_id, attempt) [unique] }
Note: 'РЕШЕНИЕ v5.4: единственная точка обмена с NATS. Запись = outbox: создаётся в queued в одной транзакции с stored_file, фоновый публикатор переводит в published (при сбое публикации — повтор по queued старше N сек); результат приходит по NATS, дедуп по result_msg_id UNIQUE; redelivery после max_deliver → status=dlq, replay = сброс в queued с attempt+1. Ядро маппит JSON из S3 в document → osv_entry / financial_statement_*'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2 (Osv_report_lines); 6.5 (строка ОСВ: компания, счёт, контрагент, признак, 6 сумм); Прил. 2 «Строка сводной ОСВ», «Распознанный показатель»
// Аналитика: FNR01 «Маппинг osv_entry»; md «Парсинг ОСВ» п.11 (row_number, level, group_type, data_type)
// Макет: «Оборотно-сальдовая ведомость»: Дебет/Кредит начало/оборот/конец, «Тип контрагента»
// В модели: v4 + v5 (owner_legal_entity_id — FIX, parent_entry_id, is_modified, is_excisable); v5.13 — document_id, group_type вместо group_schema_id; v5.16 — counterparty_match_kind
Table osv_entry {
id uuid [pk]
recognition_job_id uuid [not null, ref: > recognition_job.id]
document_id uuid [not null, ref: > document.id, note: 'v5.13: ОСВ-документ (счёт + период); файл — через document.stored_file_id']
owner_legal_entity_id uuid [not null, ref: > legal_entity.id, note: 'FIX: организация-ВЛАДЕЛЕЦ ОСВ (денормализация). В v4 называлось legal_entity_id с ошибочной note']
parent_entry_id uuid [ref: > osv_entry.id, note: 'родитель в дереве строк ОСВ (ТЗ 6.5). ОТКРЫТО (замечание 2, отложено 01.10): хранить ли только контрагентские строки и нужно ли дерево']
group_type osv_group_type [note: 'v5.13: тип строки по схеме шапки (recognition_job.group_schema) — ACCOUNT / DEPARTMENT / COUNTERPARTY / CONTRACT / OTHER']
group_name varchar [note: 'исходное название группировки из шапки («Контрагенты», «Договоры»)']
row_number int
level smallint
row_value varchar [note: 'исходный текст строки']
data_type osv_data_type
account_code varchar [note: 'сырой код счёта']
account_id uuid [ref: > chart_of_account.id]
account_root varchar
counterparty_name varchar [note: 'сырое имя из ОСВ, не изменяется']
counterparty_inn varchar [note: 'сырой ИНН, не изменяется']
counterparty_id uuid [ref: > legal_entity.id, note: 'резолв по ИНН/имени/алиасу или ручное сопоставление; NULL = не сопоставлен. Признак ГСК/ГК/Внешний выводится из периметра версии (DECISION-5)']
opening_debit numeric(18,2)
opening_credit numeric(18,2)
turnover_debit numeric(18,2)
turnover_credit numeric(18,2)
closing_debit numeric(18,2)
closing_credit numeric(18,2)
is_modified boolean [not null, default: false, note: 'значения правились аналитиком']
original_values jsonb [note: 'v5.6: суммы до первой ручной правки {opening_debit,…}; NULL = не правилось']
modified_by uuid [ref: > app_user.id]
modified_at timestamptz
is_excisable boolean [note: 'ADD ТЗ 6.4: признак «подакцизный товар» для ОСВ 90']
counterparty_match_kind counterparty_match_kind [note: 'v5.16: NULL ⇔ counterparty_id NULL; INN/NAME/ALIAS — шаг автоматического резолва при маппинге; MANUAL — ручное сопоставление; PROPAGATED — автоприменение по имени в версии (DOC-221278 п. 12–13)']
Indexes {
(document_id, row_number) [unique]
(owner_legal_entity_id, account_id, counterparty_id)
parent_entry_id [note: 'v5.15: обход дерева строк и проверка self-FK при удалении документа (OQ-117)']
recognition_job_id [note: 'v5.15: проверка FK при удалении recognition_job / файла (OQ-117)']
}
Note: 'Строка ОСВ. Правки сумм аналитиком — original_values/modified_by + audit_event(RECOGNIZED_DATA_EDIT, before/after)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2 «извлекаются значения по кодам форм Приказа 157н»; 6.3 проверки единиц измерения/даты
// Аналитика: FNR01 «fin_report_header» (report_type, org, unit, currency, reporting_date); md «Модель данных» v4
// Макет: «Реквизиты организации» (ОКПО, ОКОПФ, ОКФС, ОКУД, ОКЕИ, адрес), «Единица измерения», «Отчетная дата»
// В модели: v4 (FIX pk) + v5.1 (реквизиты — ANL-044); v5.13 — document_id; v5.19 — form_variant, needs_review_fields
Table financial_statement_meta {
id uuid [pk, note: 'FIX: был без PK']
document_id uuid [not null, unique, ref: > document.id, note: 'v5.13: документ BALANCE/INCOME (1:1); файл — через document']
recognition_job_id uuid [not null, ref: > recognition_job.id]
statement_type statement_type [not null]
inn varchar
inn_original_value varchar
inn_confidence numeric(5,2)
inn_verified boolean
inn_modified boolean
organization_name varchar [note: 'как в документе; сверяется с legal_entity/alias, при несовпадении — document.counterparty_check']
report_date date
report_date_original_value varchar [note: 'FIX: строка как распознано (был `date []`)']
report_date_verified boolean
report_date_modified boolean
units varchar [note: 'тыс. руб. / руб.']
units_original_value varchar
units_verified boolean
units_modified boolean
currency varchar [default: 'RUB']
okpo varchar [note: 'v5.1 ANL-044: реквизиты по макету экрана распознавания']
okopf varchar
okfs varchar
okud varchar [note: '0710001 / 0710002']
okei varchar
legal_form varchar [note: 'ОПФ']
ownership_form varchar
address varchar
subject_to_audit boolean
reviewed_by uuid [ref: > app_user.id, note: 'v5.9: кто проверил данные формы; подтверждение документа — document.confirmed_by']
reviewed_at timestamptz
checks_accepted_by uuid [ref: > app_user.id, note: 'v5.9 ТЗ 6.3 п.7: акцепт КА непройденных проверок (напр. 1370/2400) с комментарием']
checks_accepted_at timestamptz
checks_accepted_comment text
form_variant statement_form_variant [note: 'v5.19: full | simplified — по структуре кодов (§11.7); выбирает набор соотношений §7; NULL — документ до v5.19 или результат 1.x: набор полный']
needs_review_fields jsonb [note: 'v5.19: имена полей шапки, требующих проверки (["report_date"]), как пришло от сервиса; правка поля или подтверждение тем же значением убирает его из списка']
header_source jsonb [note: 'v5.19: шапка результата как пришла — {"<поле>": {"text": <поле>_text, "confidence": <поле>_confidence}} для organization_name, inn, report_date, units, okud, okpo, okopf, okfs, okei, address; не меняется. *_original_value — как прежде: inn_original_value = inn, report_date_original_value = report_date_text, units_original_value = units_text; inn_confidence = header_source.inn.confidence']
organization_name_original_value varchar [note: 'v5.19: наименование организации из результата; правками не меняется']
organization_name_modified boolean [note: 'v5.19: наименование правилось аналитиком (PATCH шапки)']
Note: 'Шапка распознанной формы ББ/ОФР. v5.19: результат проверок §7 и предупреждения хранятся в document_issue как пришли от сервиса и пересчитываются ядром только после правок (пересматривает «не хранятся — считаются на лету»)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.3 п.7 «стр. 1370 на конец и на начало года»; 6.7 «УК ДО на отчётную дату»
// Аналитика: FNR01 «rows[0].cells[2..4] — наименования колонок периода»; md «Логические проверки» («по каждой отчётной дате (колонке)»)
// Макет: колонки «На 30 сентября 2025 г.», «На 31 декабря 2024 г.», «Выбор периодов»
// В модели: v5 новая (FIX-14)
// Зачем: дата колонки — без неё межформенная проверка и гудвилл не считаются
Table financial_statement_column {
id uuid [pk]
meta_id uuid [not null, ref: > financial_statement_meta.id]
column_index smallint [not null]
label varchar [note: 'как в форме: «На 30 сентября 2025 г.», «За 9 месяцев 2025 г.»']
period_date date [note: 'ADD FIX-14: дата колонки — нужна для 1370(нач.года), гудвилла «на отчётную дату», сравнения версий']
is_current boolean [note: 'колонка отчётного периода']
period_kind varchar [note: 'v5.19: absolute | relative:current | relative:prev_year_end | relative:prev_prev_year_end | relative:prev_period | none; относительные подписи бланка 66н — period_date от эффективной даты документа, ядро досчитывает после правки даты отчёта (Р-6)']
label_confidence numeric(5,2) [note: 'v5.19: уверенность подписи колонки (Excel — 100; подписи нет — NULL)']
Indexes { (meta_id, column_index) [unique] }
Note: 'Колонки-периоды формы (2–3 сравнительные даты)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2 (коды статей по формам 157н); 6.3 (Таблица 1 — коды строк 1100…2400)
// Аналитика: FNR01 «fin_report_rows» (row_name, row_code, section, is_total_row); md «Модель данных» v4
// Макет: «Результат распознавания»: Наименование показателя, Код, разделы
// В модели: v4 (FIX: убраны копипаст-поля); v5.19 — строки без кода и повторы кода, статусы, порядок, needs_review
Table financial_statement_line {
id uuid [pk]
meta_id uuid [not null, ref: > financial_statement_meta.id]
line_code varchar [note: 'код строки ОКУД, напр. 1230']
line_code_original_value varchar [note: 'v5.19: line_code строки из результата как пришёл (NULL у unreadable / missing / detail и у строк, созданных правкой); правками кода не меняется. До v5.19 NULL был признаком «создана правкой» — теперь это row_index IS NULL']
line_code_confidence numeric(5,2)
line_code_modified boolean
line_name varchar
line_name_original_value varchar
line_name_confidence numeric(5,2)
line_name_modified boolean [note: 'FIX: был numeric']
row_index int [note: 'v5.19: порядок строки в документе источника (с 0). NULL ⇔ строка создана правкой аналитика (шаблонная, origin = template); у строк из результата — всегда не NULL (маппер 1.x и 2.0)']
source_ref varchar [note: 'v5.19: положение в источнике (row:8, row:14#2, Баланс!C27); NULL у строк, созданных правкой']
line_status statement_line_status [not null, default: 'ok', note: 'v5.19: ok | unreadable | missing | detail; line_code пуст ⇔ статус не ok']
line_code_text varchar [note: 'v5.19: сырой текст ячейки кода, как распознано']
parent_row_index int [note: 'v5.19: row_index строки-родителя у detail; пересчитывается при правке']
needs_review boolean [not null, default: false, note: 'v5.19: неуверенный код не подтверждён структурой (как пришло от сервиса; правка или подтверждение кода — false)']
is_confirmed boolean [not null, default: false, note: 'v5.19: код подтверждён аналитиком без изменения (PATCH lines confirmed)']
Indexes {
(meta_id, line_code) [note: 'v5.19: не уникален — повтор кода законен (Р-4); чтения «по коду» — агрегат формы по коду']
(meta_id, source_ref) [note: 'partial UNIQUE WHERE source_ref IS NOT NULL — v5.19: положение в источнике уникально в форме']
}
Note: 'Строка формы как в источнике (v5.19): строки с кодом, без кода (unreadable / missing / detail) и повторы кода; раскладка экрана по коду и шаблон формы — на фронте и в ядре по словарю имён'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.2 «уверенность распознавания ниже 100% → Требуется ручная проверка»; Прил. 2 «Распознанный показатель: значение, уверенность, признак ручной корректировки»
// Аналитика: FNR01 (value_current/prior/prior2, confidence, скобки → отрицательное); md «Логические проверки» (правка ячеек)
// Макет: подсветка ячеек, «не распознано», «Редактировать/Сохранить», «Подтвердить данные»
// В модели: v4 + v5 (column_id, confidence, original_amount, modified_by); v5.13 — убран needs_review; v5.19 — needs_review хранится, source_text, is_confirmed
Table financial_statement_value {
id uuid [pk]
line_id uuid [not null, ref: > financial_statement_line.id]
column_id uuid [not null, ref: > financial_statement_column.id]
amount numeric(18,2)
string_value varchar [note: 'как распознано, если не число']
confidence numeric(5,2) [note: 'уверенность распознавания (Прил. 2 ТЗ). v5.19: подсветка — только needs_review (уверенность, is_recognized и §7 сведены сервисом)']
is_recognized boolean
is_modified boolean [not null, default: false]
original_amount numeric(18,2) [note: 'значение до ручной правки']
modified_by uuid [ref: > app_user.id]
modified_at timestamptz
source_text varchar [note: 'v5.19: сырой текст ячейки источника (values[].text), не меняется']
needs_review boolean [not null, default: false, note: 'v5.19: как пришло от сервиса (подтверждение проверкой §7, 05-domain-rules.md §11.9), пересчитывается ядром после правок; is_modified или is_confirmed ⇒ false. Пересматривает п. 4 v5.13']
is_confirmed boolean [not null, default: false, note: 'v5.19: подтверждено аналитиком без изменения значения (is_modified не ставится)']
confirmed_by uuid [ref: > app_user.id]
confirmed_at timestamptz
Indexes { (line_id, column_id) [unique] }
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.3 (логические проверки, акцепт); 6.2 «уверенность ниже 100% → Требуется ручная проверка»
// Аналитика: openspec/changes/archive/2026-10-19-unified-statement-normalization (design: шаг 8, «Что уходит из ядра»)
// Макет: «Результат распознавания»: ошибки проверок, подсветка ячеек
// В модели: v5.19 новая
// Зачем: предупреждения сервиса и нарушения §7 с привязкой к строкам, ячейкам и полям шапки — экран показывает их у
// места и после сохранения; ядро не пересчитывает их при загрузке (Decisions № 17, 19)
Table document_issue {
id uuid [pk]
document_id uuid [not null, ref: > document.id]
kind document_issue_kind [not null]
code varchar [not null, note: 'код предупреждения (05-domain-rules.md §11.9); у relation — RELATION_FAILED (severity error), у soft — RELATION_SOFT (severity info)']
severity issue_severity [not null]
message text [not null]
line_ids jsonb [note: 'массив financial_statement_line.id, к которым относится запись (нарушение — все строки кодов соотношения); FK нет — при удалении шаблонной строки (назначение её кода строке источника) ядро убирает её id из line_ids всех записей документа']
column_id uuid [ref: > financial_statement_column.id]
column_index smallint [note: 'столбец сетки из предупреждения, если колонки нет среди financial_statement_column (COLUMN_SKIPPED и др.); у колонки формы — column_id']
codes jsonb [note: 'для relation / soft: коды цели и членов соотношения (["1600","1100","1200"]); экран подсвечивает строки этих кодов']
field varchar [note: 'поле шапки (report_date, inn, organization_name, …)']
source issue_source [not null]
relation varchar [note: 'для relation / soft: текст соотношения (1600 = 1100 + 1200)']
expected numeric(18,2) [note: 'для relation / soft: цель']
actual numeric(18,2) [note: 'для relation / soft: правая часть']
created_at timestamptz [not null]
Indexes { document_id }
Note: 'v5.19: предупреждения и нарушения документа (формы — предупреждения и нарушения §7; ОСВ — предупреждения листа OSV_* результата 2.0). Удаляются вместе с документом при перезагрузке файла; при пересчёте после правки relation / soft заменяются целиком, warning источника service сохраняются, кроме снимаемых правкой (таблица §11.9)'
}
// ============ КОНСОЛИДАЦИЯ ============
// ИСТОЧНИК:
// ТЗ: ТЗ 6.10 (процесс формирования КФО за отчётный период); 7.2 «формируется по требованию»; Прил. 2 «Консолидация: ГСК, отчётный период»
// Аналитика: md «Сводная ОСВ» / «Шахматка» (consolidation.year/quarter); md «Модель данных» v4
// Макет: «Создание консолидации: ГК → ГСК → Квартал»
// В модели: v4 + v5 (reporting_date)
// Зачем: логическая консолидация ГСК+период; версии — ниже
Table consolidation {
id uuid [pk]
company_group_id uuid [not null, ref: > company_group.id, note: 'v5.9: консолидация создаётся от ГК']
related_group_id uuid [ref: > related_group.id, note: 'v5.9: ГСК — NULL до утверждения; фиксируется/создаётся при VERIFIED по составу consolidation_member (ANL-029)']
year smallint [not null]
quarter period_quarter [not null]
reporting_date date [not null, note: 'ADD: 01.04 / 01.07 / 01.10 / 01.01 след. года — для ЕХ и сравнений']
created_at timestamptz
Indexes { (company_group_id, year, quarter) [unique] }
Note: 'Логическая консолидация ГК за период; версии различаются периметром (consolidation_member). Прошлые периоды — по company_group_id. v5.18: файлы, загруженные в консолидацию, — stored_file.consolidation_id (основа рабочего стола; полный состав — context/05-domain-rules.md §9.5 п. 11)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 5.1 (сценарий, подход, чекбокс Гудвилл); 6.10 (статусная модель, маршрут по инициатору, № и дата решения УОБ, «новая версия КФО» после Верифицирован); 6.4 (причины отклонений от Методики)
// Аналитика: md «Статусная модель»; md «Модель данных» v4 (consolidation_version: version_no, status varchar, initiator, approved_in_rf, verified_in_go, kfo_confirmation_enabled)
// Макет: «Версия», «Дата верификации», «Включить/Отключить подтверждение КФО», «Смена статуса отчёта», «Ответственный / Утвердил / Верифицировал»
// В модели: v4 + v5 (enum статусов, scenario, approach, goodwill_enabled, initiator_office_id, uob_*, methodology_deviations, previous_version_id)
Table consolidation_version {
id uuid [pk]
consolidation_id uuid [not null, ref: > consolidation.id]
version_no int [not null]
status consolidation_status [not null, note: 'CHG: enum вместо varchar']
scenario consolidation_scenario [not null, note: 'ADD ТЗ 5.1']
approach consolidation_approach [not null, default: 'BASIC', note: 'ADD ТЗ 5.1']
goodwill_enabled boolean [not null, default: false, note: 'ADD ТЗ 5.1/6.7: чек-бокс расчёта Гудвилла']
initiator_id uuid [not null, ref: > app_user.id]
initiator_office_id uuid [not null, ref: > regional_office.id, note: 'ГО/РФ инициатора — определяет маршрут (ТЗ 6.10)']
approved_in_rf_by uuid [ref: > app_user.id]
approved_in_rf_at timestamptz
verified_in_go_by uuid [ref: > app_user.id]
verified_in_go_at timestamptz
uob_decision_no varchar [note: 'ADD ТЗ 6.10: № решения УОБ']
uob_decision_date date [note: 'ADD ТЗ 6.10']
methodology_deviations text [note: 'ADD ТЗ 6.4: причины отклонений от Методики']
elimination_rules_version int [note: 'v5.11: версия elimination_rule (version_no), по которой считалась версия; фиксируется при первом расчёте, замораживается при VERIFIED']
is_in_work boolean [not null, default: true]
previous_version_id uuid [ref: > consolidation_version.id, note: '«новая версия КФО» после VERIFIED']
cancelled_reason text
created_at timestamptz
created_by uuid [ref: > app_user.id]
updated_by uuid [ref: > app_user.id]
updated_at timestamptz
Indexes { (consolidation_id, version_no) [unique] }
Note: 'Версия консолидации. После VERIFIED — read-only; изменения только через новую версию'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.10 (переходы, возврат на доработку); 8.2 «изменение статусов; согласование и утверждение КФО»
// Аналитика: md «Статусная модель» (переходы)
// Макет: «Смена статуса отчёта», «Комментарий (обязательно)», «Отклонено: отчёт возвращён на доработку»
// В модели: v5 новая
Table consolidation_status_history {
id uuid [pk]
version_id uuid [not null, ref: > consolidation_version.id]
from_status consolidation_status
to_status consolidation_status [not null]
changed_by uuid [not null, ref: > app_user.id]
changed_at timestamptz [not null]
comment text [note: 'обязателен при возврате на доработку']
Note: 'ADD ТЗ 6.10/8.2: маршрут согласования'
}
// ADD v5.1 (макет, ANL-036): «взять в работу» / перехват прав редактирования
// ИСТОЧНИК:
// ТЗ: нет в ТЗ (ANL-036)
// Аналитика: нет
// Макет: «Взятие КФО в работу (перехват прав редактирования)», «Причина: Отпуск», «В данный момент консолидация редактируется пользователем …»
// В модели: v5.1 новая (макет)
// Зачем: блокировка версии; подтвердить с заказчиком
// v5.16: без истечения по времени (OQ-119): активна, пока не снята владельцем/администратором или не перехвачена по причине
Table consolidation_lock {
id uuid [pk]
consolidation_version_id uuid [not null, ref: > consolidation_version.id]
locked_by uuid [not null, ref: > app_user.id]
locked_at timestamptz [not null]
released_at timestamptz [note: 'NULL = активная блокировка']
taken_over_from uuid [ref: > app_user.id, note: 'при перехвате — предыдущий владелец']
takeover_reason text [note: 'обязателен при перехвате (макет: «Причина: Отпуск»)']
Indexes { consolidation_version_id [note: 'partial UNIQUE WHERE released_at IS NULL'] }
Note: 'Ответственный за версию сейчас = активная блокировка'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.1 «КА в рамках консолидации редактирует состав ГСК»; 6.10 «выбор состава ГСК»; ЕХ 6.3 «Периметр на дату»
// Аналитика: md «Сводная ОСВ» п.2 («зафиксированный периметр; изменение related_group_member не меняет версию»); md «Модель данных» v4
// Макет: «Состав ЮЛ», «Минимум для консолидации: 2», «Редактировать состав»
// В модели: v4 + v5 (is_head_entity для Гудвилла)
Table consolidation_member {
id uuid [pk]
consolidation_version_id uuid [not null, ref: > consolidation_version.id]
legal_entity_id uuid [not null, ref: > legal_entity.id]
is_head_entity boolean [not null, default: false, note: 'ADD: головная организация (для Гудвилла)']
Indexes { (consolidation_version_id, legal_entity_id) [unique] }
Note: 'Зафиксированный периметр версии. Изменение related_group_member не меняет существующие версии'
}
// ИСТОЧНИК:
// ТЗ: ТЗ Прил. 2 «Документ ФО: ссылка на Консолидацию и ЮЛ»; 6.4/6.5 «использование данных предыдущих периодов»; ЕХ 7 «повторное использование первички без дублей»
// Аналитика: md «Сводная ОСВ» п.3 (файлы выбираются по ЮЛ+год+квартал — без явной связи)
// Макет: «Первичные документы консолидации»
// В модели: v5 новая; v5.13 — document_id вместо stored_file_id
// Зачем: явная привязка документа к версии (переиспользование, монозаёмщики из ЕХ)
// v5.16: точки привязки/отвязки — подтверждение документа, добавление ЮЛ в периметр; замена/удаление файла, исключение ЮЛ (Срез 4, D-5)
Table consolidation_document {
id uuid [pk]
consolidation_version_id uuid [not null, ref: > consolidation_version.id]
document_id uuid [not null, ref: > document.id, note: 'v5.13: логический документ (ОСВ по счёту / ББ / ОФР), не файл']
Indexes { (consolidation_version_id, document_id) [unique] }
Note: 'ADD Прил. 2 ТЗ «Документ ФО → Консолидация»: явная привязка документов к версии (переиспользование в нескольких версиях/периодах, монозаёмщики из ЕХ). Чек-лист = required_document_rule × периметр − consolidation_document'
}
// ---- Корректировки ----
// ИСТОЧНИК:
// ТЗ: ТЗ 6.8 (форма корректировок: колонки ДБ/КР/Комментарий, >5%, импорт/экспорт, отдельная форма ОФР); 6.4 (доп. корректировки ОФР); Прил. 2 «Расхождение/корректировка»; Прил. 9
// Аналитика: md «Корректировка данных» (correction: parent_correction_id, debit/credit_line_code, adjustment); md «Модель данных» v4
// Макет: «Таблица корректировки»: Актив/Пассив/Контроль/Корректировка, «Строка баланса Дт/Кт», «Комментарий»
// В модели: v4 + v5 (statement_type, pair_match_id, reason_code, comment NOT NULL, status, version_no)
Table correction {
id uuid [pk]
consolidation_version_id uuid [not null, ref: > consolidation_version.id]
statement_type statement_type [not null, note: 'ADD: ББ или ОФР (ТЗ 6.8 — отдельные формы)']
rule_id uuid [ref: > elimination_rule.id, note: 'v5.10: пара ВГО по естественному ключу (правило + компании); NULL = ручная без пары']
parent_correction_id uuid [ref: > correction.id, note: 'дочерняя строка распределения по нескольким статьям; глубина 1']
legal_entity_id uuid [ref: > legal_entity.id, note: 'компания 1 (чья первичка корректируется)']
counterparty_id uuid [ref: > legal_entity.id, note: 'компания 2']
debit_line_code varchar
debit_account_id uuid [ref: > chart_of_account.id]
debit_adjustment numeric(18,2)
credit_line_code varchar
credit_account_id uuid [ref: > chart_of_account.id]
credit_adjustment numeric(18,2)
deviation_amount numeric(18,2)
deviation_percent numeric(9,4)
reason_code varchar [note: 'ADD ТЗ 6.4: unrealized_revenue | excise | agency_commission | intra_lease_depreciation | intra_loan_interest | equity_income | other']
comment text [not null, note: 'ТЗ 6.8: комментарий обязателен']
status correction_status [not null, default: 'DRAFT']
version_no int [not null, default: 1, note: 'сноска 22 ТЗ: история версий корректировки — audit_event(CORRECTION_EDIT, before/after); инкремент при каждом изменении']
created_by uuid [ref: > app_user.id]
created_at timestamptz
updated_by uuid [ref: > app_user.id]
updated_at timestamptz
Indexes {
consolidation_version_id
(consolidation_version_id, rule_id, legal_entity_id, counterparty_id)
}
Note: 'Overlay поверх первички: итог = свод первички − Σ применённых корректировок. Расхождение пары (deviation_*) — справочно, пересчитывается'
}
// ---- Гудвилл (ТЗ 6.7, Прил. 3 п.2, Прил. 8) ----
// ИСТОЧНИК:
// ТЗ: ТЗ 6.7 (Гудвилл: ФВ ГО − доля в капитале ДО, комментарий обязателен, отдельная строка ББ); Прил. 3 п.2 (58.01 против 80/83, стр. 1310/1350/1370); Прил. 8
// Аналитика: md «Гудвилл» (шаги 1–10: 58.01, стр. 1310, доля владения, include_in_kfo); md «Модель данных» v4 (goodwill)
// Макет: «Таблица гудвилла», «Доля владения», «Учет в отчете», «Изменение долей владения (гудвилл)»
// В модели: v4 + v5 (additional_capital_amount, comment)
Table goodwill {
id uuid [pk]
consolidation_version_id uuid [not null, ref: > consolidation_version.id]
head_legal_entity_id uuid [not null, ref: > legal_entity.id]
subsidiary_legal_entity_id uuid [not null, ref: > legal_entity.id]
ownership_share numeric(7,4) [note: 'ручной ввод, %']
financial_investment_amount numeric(18,2) [note: 'ΣДБ 58.01 ГО по ДО']
charter_capital_amount numeric(18,2) [note: 'стр. 1310 ББ ДО на отчётную дату']
additional_capital_amount numeric(18,2) [note: 'ADD ТЗ Прил. 3: стр. 1350 ББ ДО']
participation_amount numeric(18,2) [note: '(1310 + 1350) * share / 100 — DECISION: уточнить у методологов, входит ли 1350 в базу']
goodwill_amount numeric(18,2)
include_in_kfo boolean [not null, default: false]
comment text [note: 'ТЗ 6.7: обязателен при include_in_kfo']
created_by uuid [ref: > app_user.id]
created_at timestamptz
updated_by uuid [ref: > app_user.id]
updated_at timestamptz
Indexes { (consolidation_version_id, head_legal_entity_id, subsidiary_legal_entity_id) [unique] }
}
// ---- Результат КФО (ТЗ 6.4, Прил. 10) ----
// ИСТОЧНИК:
// ТЗ: ТЗ 6.4 (итоговая форма КФО: подход, Гудвилл, расхождения, корректировки, верификация); 6.8 (контрольные пороги 10% ЧА / 1% ВБ, 10% прибыли / 1% выручки); Прил. 10
// Аналитика: нет (пробел — ANL-008/009)
// Макет: «КФО», «Сводка расхождений КФО», «Есть несохранённые изменения в КФО»
// В модели: v5 новая
// Зачем: шапка результата + пороги
Table kfo_result {
id uuid [pk]
consolidation_version_id uuid [not null, unique, ref: > consolidation_version.id]
computed_at timestamptz [not null]
is_frozen boolean [not null, default: false, note: 'true после VERIFIED']
net_assets numeric(18,2) [note: 'ЧА ГСК']
balance_total numeric(18,2) [note: 'ВБ ГСК']
revenue numeric(18,2)
sales_profit numeric(18,2)
total_deviation_bb numeric(18,2) [note: 'Σ расхождений по статьям ББ']
total_deviation_ofr numeric(18,2)
threshold_bb_exceeded boolean [note: 'ТЗ 6.8: > 10% ЧА (или > 1% ВБ, если ЧА < 10% ВБ)']
threshold_ofr_exceeded boolean [note: '> 10% прибыли от продаж (или > 1% выручки при рентабельности < 10%)']
threshold_justification text [note: 'обоснование принятия риска при превышении']
goodwill_total numeric(18,2)
has_deviations boolean
Note: 'Шапка результата КФО + контрольные пороги'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 6.4 «шаг 1 — сложение по кодам строк; шаг 2 — элиминация; неэлиминируемая часть остаётся»; Прил. 2 «Результат КФО: статья, значение»; Прил. 10; ФСБУ 4/2023
// Аналитика: нет (пробел — ANL-008)
// Макет: колонки «в т.ч. взаимные обязательства», «Очищенные показатели», «Расхождения», «источник: Баланс/ОСВ», «в т.ч. Гудвилл»
// В модели: v5 новая + v5.1 (vgo_amount, source_kind, deviation_amount, comment)
Table kfo_result_line {
id uuid [pk]
kfo_result_id uuid [not null, ref: > kfo_result.id]
statement_type statement_type [not null]
line_code varchar [not null, note: 'код строки ФСБУ 4/2023; GOODWILL — отдельная строка раздела I']
line_name varchar
sum_amount numeric(18,2) [note: 'шаг 1: простое сложение по компаниям']
eliminated_auto numeric(18,2) [note: 'шаг 2: авто-элиминация по правилам']
eliminated_manual numeric(18,2) [note: 'Σ применённых корректировок']
goodwill_adjust numeric(18,2)
consolidated_amount numeric(18,2) [not null, note: 'итог («Очищенные показатели» в макете)']
vgo_amount numeric(18,2) [note: 'v5.1 макет: «в т.ч. взаимные обязательства/обороты» = eliminated_auto + eliminated_manual']
source_kind varchar [note: 'v5.1 макет: balance | osv — источник значения статьи']
deviation_amount numeric(18,2) [note: 'v5.1 макет «Расхождения» по статье']
comment text [note: 'v5.1 макет: пояснение к строке КФО']
ordinal int
Indexes { (kfo_result_id, statement_type, line_code) [unique] }
}
// ИСТОЧНИК:
// ТЗ: ЕХ ТЗ 6.6 «выгрузка расшифровки статей отчётности в разрезе контрагентов»; ТЗ 6.9 (настраиваемые поля экспорта)
// Аналитика: нет
// Макет: «Доля в показателе по группе, %», расшифровка по компаниям
// В модели: v5 новая + v5.1 (share_percent)
Table kfo_result_line_company {
kfo_result_line_id uuid [not null, ref: > kfo_result_line.id]
legal_entity_id uuid [not null, ref: > legal_entity.id]
amount numeric(18,2)
share_percent numeric(7,4) [note: 'v5.1 макет: «Доля в показателе по группе, %»']
Indexes { (kfo_result_line_id, legal_entity_id) [pk] }
Note: 'Разрез по компаниям для расшифровки статьи (ЕХ 6.6 «расшифровка в разрезе контрагентов»)'
}
// ---- Сценарий «Подтверждение КФО клиента» (ТЗ 5.1, 6.10) ----
// ИСТОЧНИК:
// ТЗ: ТЗ 5.1 «подтверждение консолидированной отчётности, предоставленной Клиентом»; 6.10 «ручной ввод данных КФО клиента; сравнение и анализ расхождений»; Методика сноска 14 (процент к наименьшему)
// Аналитика: md «Статусная модель» (kfo_confirmation_enabled — только флаг; ANL-006)
// Макет: «Подтверждение КФО клиента»: Версия клиента / Версия банка, ВГО клиента / ВГО Банка, Расхождение, Причина
// В модели: v5 новая + v5.1 (client_vgo/bank_vgo/deviation_reason)
Table client_kfo_line {
id uuid [pk]
consolidation_version_id uuid [not null, ref: > consolidation_version.id]
statement_type statement_type [not null]
line_code varchar [not null]
client_amount numeric(18,2) [note: 'ручной ввод из КФО клиента («Консолидированная отчетность от клиента»)']
client_vgo_amount numeric(18,2) [note: 'v5.1 макет: «ВГО клиента» — ручной ввод']
bank_amount numeric(18,2) [note: 'из kfo_result_line на момент сравнения']
bank_vgo_amount numeric(18,2) [note: 'v5.1 макет: «ВГО Банка» из kfo_result_line.vgo_amount']
deviation_amount numeric(18,2)
deviation_percent numeric(9,4) [note: 'относительно наименьшего (Методика сноска 14)']
deviation_reason text [note: 'v5.1 макет: «Причина расхождения»']
comment text
entered_by uuid [ref: > app_user.id]
entered_at timestamptz
Indexes { (consolidation_version_id, statement_type, line_code) [unique] }
}
// ============ КОММЕНТАРИИ, АУДИТ, ЭКСПОРТ ============
// ИСТОЧНИК:
// ТЗ: ТЗ 6.3 «возможность добавления комментария к отчётности»; 6.4 «комментарии» в форме КФО; 6.5/6.8 «обязательный комментарий»
// Аналитика: md «Загрузка документов» («Комментарий к документу»)
// Макет: «Новый комментарий», «Комментарий к документу», комментарии в ячейках шахматки, «Редактировать комментарии» в КФО
// В модели: v5 новая
// Зачем: полиморфные комментарии к объектам
Table comment {
id uuid [pk]
object_type varchar [not null, note: 'document | consolidation_version | chess_cell(ключ пары) | kfo_result_line | …']
object_id uuid [not null]
text text [not null]
author_id uuid [not null, ref: > app_user.id]
created_at timestamptz [not null]
Indexes { (object_type, object_id) }
Note: 'ADD ТЗ 6.3/6.4: комментарии к отчётности и КФО (в т.ч. обязательные при расхождениях)'
}
// ИСТОЧНИК:
// ТЗ: ТЗ 8.2 (журнал изменений, логирование всех действий, перечень событий: вход/выход, создание консолидации, ГСК/ГК, загрузка, распознанные данные, корректировки, статусы, утверждение, выгрузка; хранение ≥ срока данных); Прил. 2 «Запись журнала изменений»
// Аналитика: нет (пробел — ANL-019); md «Ролевая модель» ссылается на «журнал аудита»
// Макет: «Журнал действий»: разделы, Объект/Событие/Что изменилось/Результат/Идентификатор, IP/браузер, «неверный пароль»
// В модели: v5 новая + v5.1 (section, result, changed_field, user_agent)
Table audit_event {
id uuid [pk]
occurred_at timestamptz [not null]
user_id uuid [ref: > app_user.id, note: 'NULL для системных событий']
action audit_action [not null]
section audit_section [not null, note: 'v5.1 макет: раздел журнала']
object_type varchar [not null]
object_id uuid
consolidation_version_id uuid [ref: > consolidation_version.id, note: 'для выборки «история изменений КФО»']
description text
before_value jsonb
after_value jsonb
comment text
result varchar [note: 'v5.1 макет: Успешно / Отклонено / Ошибка']
changed_field varchar [note: 'v5.1 макет: фильтр «Изменяемое поле»']
ip_address varchar
user_agent varchar [note: 'v5.1 макет: браузер в событиях сеанса']
Indexes {
(consolidation_version_id, occurred_at)
(object_type, object_id)
(user_id, occurred_at)
}
Note: 'ТЗ 8.2: журнал изменений/аудит. Append-only. Хранение ≥ срока хранения консолидации. Также покрывает: историю правок распознанных данных, факт/состав выгрузок (ЕХ 6.6), импорт справочников, изменения матрицы прав'
}
// ============ ЕХФД (ТЗ BIQ-34649) ============
// DECISION-2: срез = замороженный указатель на верифицированную версию + список её артефактов.
// ИСТОЧНИК:
// ТЗ: ЕХ ТЗ 6.2 «запись срезами: одна ГСК на одну отчётную дату, после Верифицирован»; 6.4 «версии среза Актуальная/Архив, неизменяемость»; 5.3 «архивирование в data lake»
// Аналитика: нет (пробел — ANL-032)
// Макет: «Единое хранилище», «Сформированная отчетность», «Верифицирована»
// В модели: v5 новая (DECISION-2)
// Зачем: замороженный указатель на верифицированную версию
Table ex_snapshot {
id uuid [pk]
related_group_id uuid [not null, ref: > related_group.id]
company_group_id uuid [not null, ref: > company_group.id, note: 'денормализация на момент верификации']
reporting_date date [not null]
year smallint [not null]
quarter period_quarter [not null]
version_no int [not null]
status ex_snapshot_status [not null, note: 'ACTUAL — последняя верифицированная; предыдущие → ARCHIVE']
source_version_id uuid [not null, unique, note: 'v5.12: id consolidation_version-источника БЕЗ FK — источник удаляется по сроку, срез нет']
source_version_no int [not null]
superseded_by_snapshot_id uuid [ref: > ex_snapshot.id, note: 'v5.12: для ARCHIVE — ссылка на актуальный срез, который его заместил']
verified_at timestamptz [not null]
written_at timestamptz [not null]
written_by uuid [ref: > app_user.id, note: 'инициатор записи']
archived_at timestamptz
data_lake_ref varchar [note: 'ссылка на архив в data lake']
Indexes {
(related_group_id, year, quarter, version_no) [unique]
(related_group_id, year, quarter, status)
}
Note: 'Срез данных ЕХ: одна ГСК на одну отчётную дату (ЕХ 6.2). Неизменяем и самодостаточен (не зависит от таблиц консолидации). Состав — ex_snapshot_item + ex_snapshot_member'
}
// ИСТОЧНИК:
// ТЗ: ЕХ ТЗ 6.3 «Периметр на дату: состав ЮЛ/ИП группы на отчётную дату»
// Аналитика: нет
// Макет: дерево «ГК → ГСК → Организации» с ИНН
// В модели: v5 новая
// Зачем: денормализация периметра на дату среза
Table ex_snapshot_member {
ex_snapshot_id uuid [not null, ref: > ex_snapshot.id]
legal_entity_id uuid [not null, ref: > legal_entity.id]
inn varchar [not null, note: 'денормализация на дату среза']
name varchar [not null]
Indexes { (ex_snapshot_id, legal_entity_id) [pk] }
Note: 'Периметр на дату (ЕХ 6.3)'
}
// ИСТОЧНИК:
// ТЗ: ЕХ ТЗ 5.2 «Состав хранимых данных» (КФО, первичные, сводная ОСВ, шахматка, гудвилл, корректировки, иные отчёты); 6.7.3/6.7.4 «Сформированная отчётность / Первичные документы», «Просмотр / Выгрузка»
// Аналитика: нет
// Макет: область документов ЕХ, «Скачать», «Связать» документ с организацией
// В модели: v5 новая; v5.13 — document_id вместо stored_file_id
// Зачем: состав среза со ссылками на объекты
Table ex_snapshot_item {
id uuid [pk]
ex_snapshot_id uuid [not null, ref: > ex_snapshot.id]
item_type varchar [not null, note: 'primary_document | summary_osv | vgo_pairs | chess | kfo_balance | kfo_income | goodwill | corrections | other_report']
legal_entity_id uuid [ref: > legal_entity.id, note: 'для первички — организация']
document_id uuid [ref: > document.id, note: 'v5.13: только для primary_document — исходники хранятся весь срок, FK допустим; «Скачать» — через document.stored_file → s3_object']
payload jsonb [note: 'v5.12 DECISION-5: замороженные данные для всех остальных типов; самодостаточно, без ссылок на таблицы консолидации']
payload_schema_version varchar
Indexes { (ex_snapshot_id, item_type) }
Note: 'Состав среза: primary_document → document; всё остальное → payload. «Просмотр» в ЛК ЕХ рендерит payload по схеме; «Выгрузка» собирает xlsx из payload на лету'
}
// ============ ПРОЧЕЕ ============
// ИСТОЧНИК:
// ТЗ: ТЗ 5.1 (базовый / расширенный пакет ОСВ); 6.2 «набор обязательных ОСВ»; сноска 12 «процесс останавливается, если отсутствует документ»
// Аналитика: md «Загрузка документов» (чек-лист: «ОСВ по обязательным счетам», «прочие документы»)
// Макет: «Чек-лист загруженных документов», «Условные обозначения статусов»
// В модели: v5 новая
// Зачем: конфигурация чек-листа по подходу; с v5.13 сверяется с document (тип + счёт + период), а не с файлом
Table required_document_rule {
id uuid [pk]
approach consolidation_approach [not null]
document_type document_type [not null]
account_id uuid [ref: > chart_of_account.id, note: 'для OSV']
is_mandatory boolean [not null, note: 'обязателен → блокирует переход к консолидации (сноска 12 ТЗ)']
Indexes { (approach, document_type, account_id) [unique] }
Note: 'ADD ТЗ 5.1/6.2: состав обязательных документов по подходу — основа чек-листа. Выполнено = есть document CONFIRMED с таким типом/счётом за период у каждого ЮЛ периметра'
}