Andrey Epifancev 93cd1f8aa0 feat(seed): dev-ГК под документы fixtures — «Русский Бисквит» из выгрузки ЕЦП и владельцы форм ДКБ
seed/dev/company_groups.yaml: ГК «Русский Бисквит» ровно как в fixtures/directories/ecp-export-russkiy-biskvit.xlsx
(20 ЮЛ/ИП, crm_id, филиал «Санкт-Петербургский РФ», ГСК из РБ и Счастливочной), ГК Унагранде/НПЗ/Межениновская по
ИНН из форм ББ/ОФР; алиасы под имена контрагентов и шапок ОСВ («Юг-Финанс / ПЕКАРНЯ СЧАСТЛИВОЧНАЯ», ...), чтобы
документы из fixtures ложились на «своих» ЮЛ и резолвились. Загрузчик проверяет ИНН, виды, ссылки членов и
уникальность «ЮЛ в одной ГСК/ГСЗ» до записи; apply — upsert по name/inn/(ГК, name), коды из последовательностей
API, без удалений. `--no-dev-groups` / `run_seed(dev_groups=False)` — dev-данные без ГК; фикстуры тестов API
используют его, чтобы начинать с пустых справочников.

make check: 1132 passed; интеграция seed/import: 23 passed. Два теста падают и на main из-за обновлённых golden
(test_mapping_golden 88 → 113, test_consolidation_fixtures UNMAPPED_TOTAL) — отдельная issue.

Closes #31

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 17:05:46 +04:00

101 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# reference-data-seed Specification
## Purpose
Справочники продукта (роли, права и их матрица, план счетов, правила элиминации, состав обязательных документов) и
dev-данные загружаются из файлов репозитория одной идемпотентной командой; повторный запуск ничего не дублирует.
## Requirements
### Requirement: Идемпотентный seed справочников продукта
Команда `python -m scripts.seed` (из `api/`) SHALL загрузить из `api/seed/core/`: `regional_office` (Головной офис,
`is_head_office = true`), `role` — 7 системных ролей ТЗ 8.1 (`KA_RF`, `KA_GO`, `RKA`, `CLIENT_MANAGER`, `CURATOR_GO`,
`EXPERT`, `ADMIN`, `is_system = true`), `permission` — 16 сочетаний объект × действие из §10 правил с кодами
`<OBJECT>_<ACTION>` (`EXPORT` при совпадении), `role_permission` по матрице §10 (ASSUMPTION(OQ-16)), `chart_of_account`,
`elimination_rule` + `elimination_rule_account` версии 1 для ББ (§4), `required_document_rule` для `BASIC` и `EXTENDED`.
Записи SHALL вставляться или обновляться по натуральному ключу; удаление MUST NOT выполняться.
Исключение — матрица `role_permission` (ей после первичной загрузки владеет администратор, issue #9): в ПУСТУЮ таблицу
матрица файла SHALL загружаться целиком; в заполненную SHALL вставляться только пары, у которых роль или право
вставлены этим же запуском seed (их администратор снять не мог), а остальные пары файла MUST NOT добавляться — право,
снятое администратором, не возвращается, добавленное сверх файла не отзывается. Отчёт команды SHALL сообщать, что
матрица пропущена (или сколько добавлено пар новых ролей и прав).
#### Scenario: Первый запуск на пустой схеме
- **WHEN** после `alembic upgrade head` выполняется `python -m scripts.seed`
- **THEN** в `role` 7 строк, в `permission` 16, в `role_permission` — число строк равно сумме прав по матрице §10,
в `elimination_rule` 8 строк с `statement_type = balance`, `version_no = 1`, в `required_document_rule` для `BASIC`
10 строк (BALANCE, INCOME, OSV × 01, 58, 60, 62, 66, 67, 76, 90) и для `EXTENDED` 27 строк
#### Scenario: Повторный запуск
- **WHEN** `python -m scripts.seed` выполняется второй раз без изменения файлов
- **THEN** число строк во всех затронутых таблицах не меняется, команда завершается с кодом 0, в отчёте
`role_permission` помечена как пропущенная
#### Scenario: Право, снятое администратором
- **WHEN** администратор снял с роли `EXPERT` право `EX_VIEW`, после чего seed запущен снова
- **THEN** связь `EXPERT × EX_VIEW` не восстановлена, остальная матрица не изменилась
#### Scenario: Новые роль и право в файлах seed
- **WHEN** матрица заполнена, а в `permissions.yaml` добавлено новое право, в `roles.yaml` — новая роль, и в
`role_permissions.yaml` — их пары (новое право существующей роли, существующее право новой роли)
- **THEN** seed вставляет ровно эти пары; снятые ранее администратором связи существующих ролей и прав не возвращаются
#### Scenario: Администратор снял все связи
- **WHEN** таблица `role_permission` пуста и seed запущен снова
- **THEN** матрица файла загружается целиком, как при первом запуске
#### Scenario: Изменение названия роли в файле
- **WHEN** в `roles.yaml` изменено `name` роли `EXPERT` и seed запущен снова
- **THEN** строка `role` с `code = EXPERT` обновлена, новая не создана
### Requirement: Счета правил и чек-листа существуют в плане счетов
Все коды счетов, на которые ссылаются `elimination_rules_v1.yaml` и `required_document_rules.yaml`, SHALL резолвиться в
`chart_of_account.code` после seed; `elimination_rule_account.is_excluded = true` SHALL быть у исключений вида
«76 кроме 76.07.5, 76.07.9»; `chart_of_account.kind` SHALL быть задан для всех счетов, `is_off_balance = true`
для забалансовых счетов 001–012 и буквенных корней плана 1С (МЦ, НЕ, ГТД, ИНВ, КВ, ОТ, РВ, УСН) со всеми их субсчетами.
`chart_of_accounts.csv` SHALL содержать полный план счетов 1С заказчика (`fixtures/directories/chart-of-accounts-1c.xlsx`,
488 строк, включая буквенные субсчета `01.К`, `62.ОТ`, `76.АВ`; ASSUMPTION(OQ-113): выгрузка полна); `kind` строки — «Вид»
1С (у субсчёта может отличаться от родителя); субсчёт без родителя в выгрузке (`Я68.01`) загружается с пустым `parent_code`.
#### Scenario: Правило № 6 (основная пара)
- **WHEN** seed завершён
- **THEN** у правила `rule_no = 6` сторона 1 содержит счета 60.02, 60.22, 60.32, 62.01, 62.03, 62.21, 62.31, 76 и исключения
76.07.5, 76.07.9; сторона 2 — 60.01, 60.03, 60.21, 60.31, 62.02, 62.22, 62.32, 76 и исключение 76.07.1;
`elimination_mode = min`, `measure = closing`, `side_1 = D`
#### Scenario: Субсчета реальных ОСВ в плане счетов
- **WHEN** seed завершён
- **THEN** в `chart_of_account` есть `01.01`, `66.01`, `76.01`, `76.ОТ`, `01.К` и `МЦ.04` (строки ОСВ с этими кодами получают
`account_id`); `60.АВ` в плане 1С нет, `account_id` таких строк остаётся `NULL`
#### Scenario: Ссылка на несуществующий счёт
- **WHEN** в файл правил добавлен счёт, которого нет в `chart_of_accounts.csv`
- **THEN** seed падает до записи в БД с сообщением, какой счёт и в каком правиле не найден
### Requirement: Dev-данные только по явному флагу
`python -m scripts.seed --dev` SHALL дополнительно загрузить из `api/seed/dev/`: примеры региональных филиалов и по одному
`app_user` на каждую системную роль (`<role>@armka.local`, активные, с назначенной ролью через `user_role`). Без флага
`--dev` эти данные MUST NOT загружаться. Флаг `--dev` SHALL приниматься только при `DEBUG=true`: при `DEBUG=false`
команда MUST завершиться с кодом 2 и сообщением «--dev доступен только при DEBUG=true (dev-пользователи не для прода)»
до чтения файлов seed и обращения к БД (OQ-60).
#### Scenario: Dev-пользователи
- **WHEN** выполнен `python -m scripts.seed --dev`
- **THEN** в `app_user` 7 строк с email вида `ka_rf@armka.local`, каждая связана ровно с одной ролью через `user_role`
#### Scenario: Dev-ГК под документы fixtures
- **WHEN** `python -m scripts.seed --dev` на пустой схеме
- **THEN** созданы ГК «Русский Бисквит» (crm_id и состав — выгрузка ЕЦП CRM из fixtures: 20 ЮЛ/ИП, филиал
«Санкт-Петербургский РФ», ГСК из АО «Русский бисквит» и ООО «Пекарня Счастливочная») и ГК владельцев форм пакета ДКБ
(Унагранде, НПЗ, Межениновская); у ЮЛ `3528104476` алиасы «Юг-Финанс / ПЕКАРНЯ СЧАСТЛИВОЧНАЯ» и имя из шапки ОСВ, так
что документы из fixtures ложатся на «своих» ЮЛ с `counterparty_check = MATCH`; коды `GK-`/`GSK-NNNNNN` — из тех же
последовательностей, что API; повторный запуск ничего не меняет; без `--dev` ни одной ГК и ЮЛ нет
#### Scenario: Прод-режим
- **WHEN** выполнен `python -m scripts.seed` без `--dev`
- **THEN** таблица `app_user` пуста, `regional_office` содержит только Головной офис
#### Scenario: `--dev` без DEBUG
- **WHEN** при `DEBUG=false` выполнен `python -m scripts.seed --dev`
- **THEN** код возврата 2, в stderr — сообщение об отказе, БД не затронута