- data-model-v5.14.dbml: related_group_member.kind + partial unique (legal_entity_id, kind) WHERE is_current (ЮЛ одновременно в одной ГСК и одной ГСЗ — решение владельца 04.10.2026), audit_action + LEGAL_ENTITY_CHANGE, USER_CHANGE; КПП не вводится. - 05-domain-rules.md: §13 правила импорта выгрузки ЕЦП (формат, построчные коды, транзакция, след, ручные операции), §10.1 видимость справочников по филиалу без кода роли. - 07-open-questions.md: OQ-31/32 закрыты, OQ-25 уточнён по выгрузке, добавлены OQ-86…OQ-91. - source/biq/ecp-export-lagal-entity.xlsx — первоисточник формата (добавлен владельцем). - openspec/changes/slice-1-directories: proposal, design, tasks (skip_specs). Issue: RSHB/arm-ka-docs#6 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
8.6 KiB
Design: Срез 1 (сквозное) — DBML v5.14, импорт ЕЦП, видимость
Context
См. proposal.md. Факты, на которых стоит дизайн:
- Выгрузка ЕЦП (
docs/source/biq/ecp-export-lagal-entity.xlsx, лист «Выписки_01.04.26»): 8400 строк, колонкиИНН,iD ГК,Базовый РФ ГК в CRM,Группа компаний,Организация,ГСК,ГСЗ,Тип; 345 ГК; 53 значения РФ (51 «… РФ», плюс «ЦКБ» 1370 строк и «Головная организация»); типы ЮЛ 7395, ИП 469,нерез.354,ФЛ180,нет инн2; ИНН: 10 цифр у ЮЛ, 12 у ИП, 279 нечисловых; 215 ИНН встречаются в нескольких ГК; флаги ГСК/ГСЗ — строки «входит в ГСК» / «входит в ГСЗ» (плюс мусор:0, ИНН в колонке флага); идентификатора ГСК/ГСЗ нет; у 213 ГК есть члены ГСК (до 83). - Решения владельца 04.10.2026: без UI справочника; КПП не нужен; ЮЛ в одной ГК, одновременно в одной ГСК и одной ГСЗ; экран администратора минимальный.
- Инварианты: права — данные, код роли в логике не проверяется; первичка не мутируется; append-only аудит; поля вне DBML не вводить.
Goals / Non-Goals
Goals: одна версия DBML, которую реализует миграция 0002; правила импорта, по которым можно написать тесты на реальной выгрузке; правило видимости без кода роли. Non-Goals: см. proposal.
Decisions
D-1. v5.14: денормализованный kind в related_group_member
Уникальность «одна группа каждого вида на ЮЛ» нельзя выразить индексом через join; варианты — триггер или денормализация.
Выбрана денормализация (kind = related_group.kind, равенство проверяет ядро при вставке, тест на рассинхрон), индекс
(legal_entity_id, kind) WHERE is_current. Альтернатива — триггер: сложнее переносить, невидим в ORM. Цена ошибки:
убрать колонку и индекс миграцией.
D-2. Версия DBML — новый файл, старый остаётся
data-model-v5.14.dbml рядом с v5.13 (история версий — файлы, changelog в шапке); entities-v5.14.md — копия с таблицей
изменений наверху; ссылки в CLAUDE.md репозиториев и Makefile/DBML_VERSION arm-core переводятся на v5.14 в дочернем change.
D-3. Импорт — правила §13 как данные для тестов, строки не блокируют файл
Построчные коды (UNSUPPORTED_TYPE, INVALID_INN, DUPLICATE_IN_FILE, RF_CREATED, GK_*, LE_*, GSK_CREATED,
MEMBER_ADDED, UNKNOWN_FLAG) фиксируются в §13.2 и становятся контрактом отчёта и тестов arm-core; файл отвергается
только при отсутствии обязательных колонок или нечитаемости. Одна транзакция, dry_run, стратегия on_existing ∈ {skip, update}.
D-4. Неоднозначности выгрузки — осторожные допущения + OQ, не угадывание
нерез./ФЛ — пропуск (OQ-87); ИНН только по длине (OQ-88); «ЦКБ»/«Головная организация» — обычные филиалы (OQ-89);
ИНН в нескольких ГК — первая выигрывает, остальные в отчёт (OQ-90); одна неявная ГСК/ГСЗ на ГК с именем ГК (OQ-91).
Каждое допущение помечается ASSUMPTION(OQ-nn) в коде импортёра, все обратимы одной командой или повторным импортом с update.
D-5. Видимость — по regional_office.is_head_office, без ролей
ГО видит всё, филиал — ГК своего РФ и своих монозаёмщиков; ЗО не фильтрует (OQ-86). Запись вне видимости — 404.
D-6. Выгрузка хранится в docs/source/biq/, срез — в fixtures
Полная выгрузка — первоисточник формата и прогон «на всём объёме» (помечена как внутренняя: реальные ИНН); для unit-тестов —
21 строка ГК «Русский Бисквит» в fixtures/directories/. fixtures/README и 06-test-data.md §6 обновляются.
Risks / Trade-offs
- [Импорт всей выгрузки создаст 53 филиала и ~345 ГК в dev-БД] →
dry_runпервым, отчёт показываетRF_CREATED. - [Денормализованный
kindразойдётся с группой] → проверка в сервисе + тест; индекс всё равно защищает уникальность. - [Допущения D-4 окажутся неверны] → данные исправимы повторным импортом с
updateили одной миграцией данных; OQ отслеживаются.
Migration Plan
Миграция 0002 (arm-core): ALTER TABLE related_group_member ADD kind (заполнение из related_group.kind для существующих строк —
в dev их нет), замена индекса. Откат — обратная замена индекса и DROP COLUMN.
Open Questions
OQ-86…OQ-91 (см. 07-open-questions.md) — ни один не блокирует: приняты допущения D-4/D-5. Вопросы владельцу: «ЦКБ» и
«Головная организация» = ГО? нужны ли нерезиденты/ФЛ?
Decisions made without discussion
| № | Дата | Решение | Обоснование | Цена ошибки |
|---|---|---|---|---|
| 1 | 2026-10-04 | D-1: kind в related_group_member вместо триггера |
индекс виден в ORM и сверке с DBML | миграция: убрать колонку |
| 2 | 2026-10-04 | D-2: новый файл v5.14, v5.13 остаётся | правило «новая версия DBML с changelog» | нет |
| 9 | 2026-10-04 | v5.14: два новых значения audit_action — LEGAL_ENTITY_CHANGE, USER_CHANGE |
без них события ЮЛ/пользователей пришлось бы писать под чужим действием (GROUP_CHANGE) |
удалить значения enum (миграция) |
| 3 | 2026-10-04 | D-3: коды отчёта импорта фиксированы в §13.2; строки не блокируют файл; on_existing skip/update; dry_run |
макет [38] «Пропустить существующие»; реальная выгрузка грязная | добавить/переименовать коды |
| 4 | 2026-10-04 | D-4: пять допущений по неоднозначностям выгрузки (OQ-87…OQ-91) | нельзя угадывать; все обратимы | повторный импорт / миграция данных |
| 5 | 2026-10-04 | D-5: видимость по is_head_office, ЗО не фильтрует |
права — данные; у ГК нет сегмента | добавить поле сегмента (DBML v5.15) и фильтр |
| 6 | 2026-10-04 | D-6: полная выгрузка в docs/source/biq, срез в fixtures |
первоисточник формата vs тестовые данные | перенос файла |
| 7 | 2026-10-04 | Срез 1 без UI справочника, экран администратора минимальный | решение владельца 04.10.2026 | UI — отдельный срез |
| 8 | 2026-10-04 | Записи вне видимости отвечают 404, не 403 | не раскрывать существование чужих ГК | одна константа |