# Spec Delta: data-model ## Purpose Схема базы данных ядра совпадает с моделью данных DBML v5.13 один в один: таблицы, колонки, типы, enum'ы, ключи и индексы; миграции применяются к пустой базе и полностью откатываются; расхождение между ORM и DBML невозможно пропустить. ## ADDED Requirements ### Requirement: Схема БД равна DBML v5.13 ORM-метаданные SHALL содержать ровно 43 таблицы и 27 enum'ов из `../docs/model/data-model-v5.13.dbml` в схеме `dkb`; имена таблиц, колонок, enum'ов и их значений SHALL совпадать с DBML буквально; nullability, PK, UNIQUE, FK, значения по умолчанию и индексы SHALL соответствовать DBML, включая частичные уникальные индексы (`related_group_member` WHERE `is_current`, `consolidation_lock` WHERE `released_at IS NULL`) и `NULLS NOT DISTINCT` на `document (stored_file_id, document_type, osv_account_code)`. Поля, отсутствующие в DBML, MUST NOT существовать. #### Scenario: Сверка ORM с DBML - **WHEN** выполняется `python scripts/check_dbml_vs_orm.py ../../docs/model/data-model-v5.13.dbml` - **THEN** вывод пуст и код возврата 0 #### Scenario: Лишняя колонка в ORM - **WHEN** в ORM добавлена колонка, которой нет в DBML (или удалена существующая) - **THEN** скрипт печатает `таблица.колонка: отсутствует в DBML` (или `отсутствует в ORM`) и возвращает код 1 #### Scenario: Расхождение типа или nullability - **WHEN** колонка `osv_entry.opening_debit` объявлена в ORM как `Numeric(18, 4)` или `NOT NULL` - **THEN** скрипт сообщает `ожидалось numeric(18,2) NULL / фактически numeric(18,4) NOT NULL` и возвращает код 1 ### Requirement: Миграции применяются с нуля и откатываются Единственная ревизия `0001` SHALL создавать схему `dkb` (если нет), все enum'ы, таблицы, ограничения и индексы на пустой базе PostgreSQL 16 (требуется ≥ 15); `downgrade` SHALL удалять всё созданное, оставляя пустую схему; повторный `upgrade` после `downgrade` SHALL проходить без ошибок. #### Scenario: Полный цикл - **WHEN** на пустой БД выполняются `alembic upgrade head`, `alembic downgrade base`, `alembic upgrade head` - **THEN** все три команды завершаются успешно, после последней в `information_schema.tables` схемы `dkb` 43 таблицы и в `pg_type` 27 enum-типов #### Scenario: Автогенерация ничего не находит - **WHEN** после `upgrade head` выполняется `alembic revision --autogenerate` на той же БД - **THEN** сгенерированная ревизия не содержит операций (пустые `upgrade`/`downgrade`) ### Requirement: Версия DBML зафиксирована в коде Пакет моделей SHALL объявлять константу версии DBML (`5.13`); скрипт сверки SHALL сравнивать её с версией в шапке файла DBML и падать при расхождении — смена модели без осознанного обновления ORM невозможна. #### Scenario: Новая версия DBML без обновления ORM - **WHEN** в `docs/model/` появляется `data-model-v5.14.dbml`, а константа в ORM — `5.13` - **THEN** сверка против нового файла падает с сообщением о версии до сравнения таблиц ### Requirement: Деньги и проценты — точные типы Все денежные колонки SHALL быть `numeric(18,2)`, проценты — `numeric(7,4)`/`numeric(9,4)`, уверенность — `numeric(5,2)`; ORM SHALL отдавать их как `Decimal`, `float` в моделях MUST NOT встречаться. #### Scenario: Чтение денежной колонки - **WHEN** из `osv_entry` читается строка со `closing_debit = 106311116.46` - **THEN** значение имеет тип `Decimal` и равно `Decimal("106311116.46")`