- 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
79 lines
8.6 KiB
Markdown
79 lines
8.6 KiB
Markdown
# 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 | не раскрывать существование чужих ГК | одна константа |
|