11 Commits

Author SHA1 Message Date
Andrey Epifancev
6ccb99194d feat(model): индексы osv_entry и stored_file в v5.15 (I-1, OQ-117)
- ORM: ix_osv_entry_parent_entry_id, ix_osv_entry_recognition_job_id (models/osv.py),
  ix_stored_file_current_recognition_job_id (models/files.py) — по DBML v5.15 п. 4.
- Миграция 0003_dbml_v5_15: create_index в upgrade, drop_index в downgrade.
- tests/test_migrations.py: индексы есть после upgrade head (btree, не unique), исчезают
  после downgrade на 0002 и возвращаются при повторном upgrade.
- openspec change: design.md решение №53 (I-1 закрыт в v5.15), Migration Plan; дельта
  data-model — 0003 создаёт индексы, откат на шаг их удаляет.
- README «Ограничения Среза 2c»: убран пункт об отсутствии индексов.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 00:56:28 +04:00
Andrey Epifancev
ab606dc59a feat(model): DBML v5.15 — osv_data_type NU, email в нижнем регистре, миграция 0003
- OsvDataType.NU после BU, DBML_VERSION = 5.15, Makefile/dbml_parity/docstring'и на data-model-v5.15.dbml;
- миграция 0003_dbml_v5_15: ADD VALUE 'NU' AFTER 'BU' + lower(app_user.email); downgrade пересоздаёт osv_data_type
  (регистр email не возвращается);
- email хранится в нижнем регистре: users_admin.create_user и seed (_apply_users); с UNIQUE по строчному email гонка
  создания в другом регистре теперь ловится индексом (тест test_api_constraint_races обновлён);
- тесты: enum NU, откат на шаг, lower email на миграции, seed/админка, вставка NU, фиктивная v5.16.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 21:28:04 +04:00
Andrey Epifancev
458392e94d feat(model): DBML v5.14 — related_group_member.kind, audit_action +2, миграция 0002, последовательности кодов
- RelatedGroupMember.kind (related_group_kind, NOT NULL) между legal_entity_id и valid_from, как в DBML;
  частичный уникальный индекс uq_related_group_member_entity_kind_current (legal_entity_id, kind) WHERE is_current —
  ЮЛ одновременно в одной ГСК и одной ГСЗ.
- AuditAction: LEGAL_ENTITY_CHANGE (после GROUP_CHANGE), USER_CHANGE (после USER_ROLE_CHANGE).
- Последовательности company_group_code_seq / related_group_code_seq в metadata (design D-5).
- Миграция 0002_dbml_v5_14: related_group_member пересоздаётся с переносом строк (kind из группы), чтобы порядок
  колонок совпал с DBML и create_all; ALTER TYPE ADD VALUE ×2; downgrade пересоздаёт audit_action v5.13.
- DBML_VERSION = 5.14, пути к data-model-v5.14.dbml (Makefile, dbml_parity), тесты: откат на шаг, перенос строк,
  уникальность gsk+gsz, офлайн-SQL до head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-04 20:49:32 +04:00
Andrey Epifancev
8d7d014391 fix: nullable jsonb пишет SQL NULL вместо jsonb null (I-1)
Все nullable jsonb-колонки ORM (audit_event.before_value/after_value,
ex_snapshot_item.payload, recognition_job.group_schema/metrics,
osv_entry.original_values) объявлены через nullable_jsonb() =
JSONB(none_as_null=True): Python None сохраняется как SQL NULL, IS NULL
находит строку. DDL не меняется; сверка с DBML и миграции зелёные.

Обходы для не-объекта в metrics (CASE по jsonb_typeof в публикаторе,
merge_metrics в inbox) оставлены: строки до исправления и сырой SQL
могут содержать jsonb null. Тесты: SQL NULL после AuditService.record
без before/after, metrics фабрики None -> SQL NULL, повтор публикации и
слияние метрик для SQL NULL и jsonb null, перечень колонок с флагом.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:49:26 +04:00
Andrey Epifancev
48525731ff feat: ORM-модели частей 07–09 DBML v5.13 (консолидация, КФО, аудит/ЕХ, required_document_rule)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:32:48 +04:00
Andrey Epifancev
4e363bd00f feat: ORM-модели частей 04–06 DBML v5.13 (файлы, ОСВ, отчётность)
files.py: s3_object, stored_file, document (NULLS NOT DISTINCT), recognition_job;
FK-цикл stored_file <-> recognition_job разорван через use_alter с явным именем.
osv.py: osv_entry. statements.py: financial_statement_meta/column/line/value.
Тест частей 01–03 не привязан к полному составу metadata.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:22:29 +04:00
Andrey Epifancev
fb2b9ef9fc feat: ORM-модели частей 01–03 DBML v5.13 (справочники, доступ, учёт), naming_convention в MetaData
16 таблиц: directories, access, accounting; схема dkb, детерминированные имена ограничений.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:05:04 +04:00
Andrey Epifancev
8e6223a273 feat: models/enums.py — 27 enum'ов DBML v5.13, фабрика pg_enum, DBML_VERSION
Тест сверяет имена и упорядоченные значения с блоками Enum {} DBML через pydbml.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 18:47:23 +04:00
Andrey Epifancev
72415ab891 style: ruff format и правки линтера на каркасе шаблона
Без смены поведения: форматирование, сортировка импортов, удаление неиспользуемых
импортов, __all__ в src/db, datetime.UTC, HTTPStatus.NOT_FOUND вместо 404,
аннотация lvl в logger, noqa F401 на проверке доступности raisa_fastapi_protected_api,
mypy: miniopy_async не подтягивается (follow_imports=skip).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 18:02:19 +04:00
Andrey Epifancev
8e1f423ad6 chore: вычищены остатки доменного кода, каркас проходит tsc/biome/rsbuild 2026-10-02 23:34:55 +04:00
Andrey Epifancev
ba922225ab chore: каркас из шаблона команды (DKB_ARMKA@ba172f8, доменный код удалён) 2026-10-02 23:15:37 +04:00