260 Commits

Author SHA1 Message Date
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
75045af723 test: обмен в реальном lifespan и два инстанса ядра на NATS (I-7)
tests/test_exchange_lifespan_nats.py (integration, NATS и PostgreSQL из
.env.test, уникальный subject результатов, queue group arm-core):
- настоящий lifespan при NATS_ENABLED=true: /healthcheck -> nats=ok,
  подписка видна в мониторинге NATS (/connz) с qgroup arm-core, публикатор
  lifespan переводит задание в published, inbox принимает ack и результат;
  остановка гасит задачи обмена до nats.stop(), закрывает пул БД и не
  оставляет висящих задач;
- два NatsConnection с настоящим handle_message: результат обрабатывает
  ровно один инстанс, задание и s3_object меняются один раз; повторная
  доставка того же сообщения ничего не меняет.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:59:58 +04:00
Andrey Epifancev
cce92212d2 test: убран --capture=no из addopts pytest (M-2)
Вывод loguru из негативных тестов больше не засоряет прогон: pytest
перехватывает stderr и показывает его только для упавших тестов (секция
«Captured stderr call»). Ни один тест не зависел от отключённого
перехвата; полный прогон зелёный.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:56:53 +04:00
Andrey Epifancev
8bab965988 fix: dev-запуск слушает только 127.0.0.1 (M-11)
В api/.env.example задан SERVER__HOST=127.0.0.1 (и SERVER__PORT=8000):
при DEBUG и AUTH_DEV_ISSUER эндпоинт dev-токенов выдаёт токен любому, кто
знает email. Значение приложения по умолчанию (0.0.0.0) не меняется.
.env.test — те же ключи; новый тест сверяет набор ключей .env.example и
.env.test. README: рекомендация в быстром старте и строка таблицы.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:55:59 +04:00
Andrey Epifancev
feaac9cb9a test: CREATE/DROP DATABASE через служебную БД postgres (M-9)
Команды уровня кластера в тестах (создание и удаление временных БД, роль
в тесте предвыданной схемы) подключались к рабочей БД armka. Теперь —
к служебной БД postgres того же сервера (service_url): тестам не нужна
существующая armka. Проверено прогоном с несуществующей БД в
OPENBAO__DATABASE__DATABASE_URL.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:55:22 +04:00
Andrey Epifancev
87816ef6c9 fix: job_id невалидного сообщения пишется в лог через repr (M-7)
job_id из сообщения, не прошедшего контракт, — данные отправителя; без
экранирования перевод строки в нём позволял подделать строку лога.
Теперь repr(job_id[:64]), как в рулинге R-18 для kid токена.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:54:30 +04:00
Andrey Epifancev
7a6a7fb36b fix: валидация настроек обмена PositiveInt/NonNegativeInt (M-4)
OUTBOX_POLL_SECONDS и ACK_TIMEOUT_SECONDS — PositiveInt, OUTBOX_MAX_REPUBLISH —
NonNegativeInt: 0 и отрицательные значения периода и таймаута, отрицательное
число повторов отвергаются при старте. Значения по умолчанию прежние.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:53:48 +04:00
Andrey Epifancev
3d31c1eac5 fix: SMB необязателен, остановка lifespan по независимым шагам (I-3, T4.5, M-3)
- OPENBAO__SMB__* необязательны (по умолчанию пусто); при пустом SMB_HOST
  сессия SMB не регистрируется (DEBUG «SMB не настроен»), в .env.example
  и .env.test SMB выключен — в логе dev-запуска больше нет трейсбэка SMB.
- Задан SMB_HOST: register_session и reset_connection_cache вызываются
  через asyncio.to_thread и не блокируют цикл событий.
- Остановка: задачи обмена -> NATS -> S3 -> SMB -> engine.dispose();
  каждый шаг в своём try/except с логом step=..., ошибка одного шага не
  отменяет следующие. Пул соединений БД закрывается последним (M-3).
- README: «что в логе при запуске», таблица настроек SMB, порядок
  остановки.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:53:23 +04:00
Andrey Epifancev
6a6ffb54f4 fix: ошибки БД и отказ проверки JWT пишутся в лог (I-2, T16.9)
Обработчики IntegrityError (409) и SQLAlchemyError (500) молча отдавали
ответ. Теперь каждая такая ошибка — одна запись ERROR: путь и метод
запроса, классы ошибок SQLAlchemy и драйвера, SQLSTATE, SQL-текст с
плейсхолдерами (усечён до 200 символов) и стек вызовов. Текст исключения
в лог не идёт: str() ошибки asyncpg/SQLAlchemy содержит параметры запроса
и DETAIL PostgreSQL со значениями ключей (email, ИНН). Ответы прежние.

JwksVerifier.verify: TypeError/ValueError, превращаемые в AuthError,
теперь пишут warning в том же санированном виде repr(str(exc))[:200].

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:50:57 +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
ac394bb4ce docs: перенос строк в docstring app.py (E501)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:23:43 +04:00
Andrey Epifancev
2b1fdab294 docs: контракты обмена и ex-payload в CLAUDE.md, уточнён docstring app.py
Строка «Контракты» перечисляет три реальные схемы recognition-* и копии в api/contracts/; ex-payload-v1 помечен
как будущий (Срез 7). В раскладке — src/api/endpoints. В docstring app.py убрано утверждение, что middleware
РАИСА подключается при DEBUG=False (пакет лишь условно импортируется).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 01:23:18 +04:00
Andrey Epifancev
81bb79e600 feat: make check-full — check, dbml-check, интеграционные тесты против compose, docker build, web; .env.test на порты compose
Цели docker-build, web-check, check-infra (подсказка «make up»); .env.test переведён на 5432/4222/9000 (R-19),
test_config и README обновлены. Цикл миграций входит в test-integration и отдельным шагом не дублируется.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 00:58:57 +04:00
Andrey Epifancev
9b81fa8e37 feat: inbox recognition.result — ack processing, идемпотентный приём результата, s3_object результата, ResultMapper/NoopMapper
handle_message(msg, *, session_factory, mapper, now): разбор конверта по контракту v1.0, одна короткая транзакция
с SELECT … FOR UPDATE (ждёт блокировку публикатора/вахтёра); ack published → processing; результат — дедуп по
result_msg_id, финальные статусы (R-22) не трогаются, регистрация s3_object (results), слияние метрик с сохранением
republish_count. Подписка app.py получает handler с фабрикой сессий и NoopMapper; шаблонный core/nats_callback.py
удалён (R-3). audit_event не пишется (R-7).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 00:46:48 +04:00
Andrey Epifancev
2e96971475 feat: outbox-публикатор и вахтёр сроков recognition_job, фоновые задачи обмена в lifespan при NATS_ENABLED
- services/recognition_exchange.py: publish_pending — одно задание на короткую транзакцию
  (SELECT … LIMIT 1 FOR UPDATE OF recognition_job SKIP LOCKED → публикация → UPDATE → COMMIT):
  queued → published (request_msg_id = <job_id>:<attempt>, request_subject, published_at, reply_deadline —
  5 мин по умолчанию, requested_at); published без ack дольше ACK_TIMEOUT → повтор с тем же Nats-Msg-Id и
  attempt, metrics.republish_count (jsonb ||, прочие ключи сохраняются); после OUTBOX_MAX_REPUBLISH → dlq/NO_ACK;
  запрос не собирается по контракту → error/INVALID_REQUEST; первая NatsPublishError откатывает задание и
  прерывает проход; нет соединения — проход пропускается. published с прошедшим reply_deadline оставлен вахтёру.
- expire_deadlines — один UPDATE processing/published с reply_deadline < now → timeout/TIMEOUT.
- run_publisher_loop / run_watchdog_loop (30 с), ExchangeTasks: запуск в lifespan вместе с NatsConnection,
  остановка до nats.stop(). audit_event не пишется (R-7).
- Тесты: интеграционные против PostgreSQL и NATS (схема запроса, повторы и dlq на инжектируемых часах, ack после
  первого повтора, вахтёр, ошибка публикации, два конкурентных прохода, циклы), lifespan; фабрика CommittedJobs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 00:20:06 +04:00
Andrey Epifancev
4d13a1ddb2 fix: настройка OPENBAO__MINIO__MINIO_SECURE — http-MinIO из compose без SSL-ошибки при старте (R-20)
MinioSettings.MINIO_SECURE (по умолчанию true — https, как в шаблоне и на платформе) передаётся в Minio(secure=...);
.env.example и .env.test — false (MinIO из docker compose слушает http). README: трейсбэк S3 из раздела
«что в логе» убран, переменная добавлена в таблицу конфигурации.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 23:47:39 +04:00
Andrey Epifancev
4e5e1a7b5b feat: src/core/nats.py — фоновое подключение к core NATS, publish с Nats-Msg-Id, подписка recognition.result в queue group arm-core
- NatsConnection: supervisor первого подключения в фоне (старт приложения не ждёт NATS), nats-py
  переподключается сам (max_reconnect_attempts=-1, reconnect_time_wait=2) и восстанавливает подписки;
  экспоненциальный backoff 1→30 с, если connect завершился исключением; логирование ошибок без шума
  (первая ошибка серии — warning, дальше не чаще раза в минуту).
- publish(subject, payload, msg_id): заголовок Nats-Msg-Id + flush; нет соединения или нет подтверждения —
  NatsPublishError. subscribe_results(handler): queue group arm-core, исключения обработчика логируются.
- build_nats_auth перенесён из app.py; ошибка учётных данных — NatsConfigError, категория config_error,
  без повторов (T4.1). Первое подключение повторяется (T4.2): прежний await connect в lifespan при
  недоступном NATS висел бесконечно (nats-py с max_reconnect_attempts=-1 повторяет первое подключение сам).
- app.py: подключение переехало в core/nats.py (R-3), lifespan запускает/останавливает NatsConnection,
  подписка — заглушка services/recognition_exchange.handle_message (полная обработка — задача 24).
- Тесты: юнит (поддельный клиент, инжектируемый sleep, отсутствие JetStream в src/) и интеграционные
  против NATS без JetStream (заголовок, queue group без дублей, недоступный сервер).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 23:47:39 +04:00
Andrey Epifancev
dcbeea5756 fix: сканер audit_event ловит псевдонимы импорта, __table__.update()/delete() и Query API
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 23:30:27 +04:00
Andrey Epifancev
a58f5d9df1 feat: AuditService.record — единственная точка записи audit_event, тесты и статический запрет UPDATE/DELETE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 23:21:15 +04:00
Andrey Epifancev
d7f6c280bb fix: целочисленные float (1.0) принимаются как целые, bool в header_variant отвергается
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 23:08:25 +04:00
Andrey Epifancev
c4afe4bc0b feat: pydantic-модели контракта распознавания v1.0 и тесты согласованности со схемами
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 23:01:23 +04:00
Andrey Epifancev
490d2568bc chore: копии схем контракта распознавания v1.0 и jsonschema в dev-зависимостях
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 23:01:23 +04:00
Andrey Epifancev
048e09f724 fix: несуществующий маршрут под /api/v1 — 404, а не ответ SPA-заглушки
При выключенном dev-эмитенте POST /api/v1/dev/token отвечал 405 от StaticFiles.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:28:52 +04:00
Andrey Epifancev
f5a5bbf95f feat: GET /api/v1/me и POST /api/v1/dev/token, условный build_api_router (T17.4)
Dev-эмитент подключается только при AUTH_DEV_ISSUER и DEBUG; статический тест
«нет кодов ролей» расширен на src/api/** и src/services/**.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:26:18 +04:00
Andrey Epifancev
cd64087b0d feat: dev-секрет HS256 короче 32 байт отвергается (T16.7)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:26:18 +04:00
Andrey Epifancev
953d978cc7 fix: обработчик 422 — актуальная константа статуса и сериализация ctx ошибок валидации
Без jsonable_encoder ValueError из field_validator давал 500 вместо 422;
HTTP_422_UNPROCESSABLE_ENTITY помечена устаревшей в starlette.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:26:18 +04:00
Andrey Epifancev
11941caa20 feat: зависимости current_user и require(code), репозиторий пользователей, схема me
Пользователь, филиал, роли и права читаются одним запросом; роль is_active=false прав не даёт;
email без учёта регистра, неоднозначность — 403 (R-17).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:14:10 +04:00
Andrey Epifancev
5ed52a7239 fix: JWKS-проверяющий — алгоритм по типу ключа, без кэша ключей по kid; email в нижний регистр
I-1: в jwt.decode передаётся PyJWK (а не голый ключ), рассогласование alg и типа ключа даёт 401,
а не TypeError/500; страховка на (TypeError, ValueError).
I-2: cache_keys=False — кэш ключей по kid не имеет срока жизни и держал бы отозванный ключ;
остаётся кэш набора JWKS на 300 с.
R-17: Claims.email = strip().lower().
R-18: запредельный exp не даёт 500; пустой sub отвергается; kid из заголовка пишется в лог через repr и усекается.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:01:33 +04:00
Andrey Epifancev
d7469f31bc chore: зафиксирована версия cryptography в requirements.txt (R-18c)
auth.py импортирует cryptography напрямую, а requirements.txt — полный freeze.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:01:33 +04:00
Andrey Epifancev
70b47a097d feat: ядро аутентификации (Claims, TokenVerifier, DevHs256Verifier, JwksVerifier, build_verifier)
Dev HS256 включается только при AUTH_DEV_ISSUER и DEBUG одновременно; РАИСА — RS256/ES256 по
публичному ключу или JWKS с кэшем. PyJWT[crypto] добавлен в requirements.txt как прод-зависимость.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 21:51:51 +04:00
Andrey Epifancev
cb2723c3a2 feat: dev-seed (2 РФ и по пользователю на роль) по флагу --dev, проверка ссылок до записи
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 21:41:07 +04:00
Andrey Epifancev
176999cb71 feat: seed правил элиминации ББ (§4) и чек-листа обязательных документов, проверка счетов до записи
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 21:29:16 +04:00
Andrey Epifancev
caf009d343 feat: seed плана счетов 94н с субсчетами правил и чек-листа, шаг загрузчика chart_of_account
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 21:17:18 +04:00
Andrey Epifancev
186ae802e6 feat: seed ролей, прав, матрицы §10 и Головного офиса, загрузчик scripts.seed
Двухфазный загрузчик: валидация yaml целиком до записи (SeedError), затем upsert по
натуральному ключу без удалений; run_seed(session, *, dev, seed_dir) без commit, CLI коммитит.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 21:07:07 +04:00
Andrey Epifancev
c8c218e4ac test: сужен перехват ошибок PostgreSQL в conftest, самодостаточные тесты изоляции транзакции
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 20:54:30 +04:00
Andrey Epifancev
c3cf307866 test: conftest — БД сессии с миграциями, транзакция с откатом на тест, маркер fixtures, тест чтения Decimal
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 20:46:50 +04:00
Andrey Epifancev
78e4a95327 fix: схема dkb создаётся только при отсутствии — миграции работают без права CREATE на базу
PostgreSQL проверяет право CREATE на базу раньше IF NOT EXISTS: роль миграций, которой платформа выдала готовую
схему dkb без этого права, падала на CREATE SCHEMA IF NOT EXISTS. env.py (онлайн) проверяет pg_namespace и создаёт
схему только при отсутствии; офлайн-SQL и upgrade() ревизии 0001 — DO-блок с той же проверкой. Тест: схема заранее
во владении отдельной роли, CREATE на базу отозван у PUBLIC — upgrade/check/downgrade/upgrade проходят.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 20:36:13 +04:00
Andrey Epifancev
ed5ed43fa6 feat: миграция 0001_dbml_v5_13 — полный DDL DBML v5.13 с откатом; тесты цикла и make migration-cycle
Явный DDL без импорта src.models: схема dkb, 27 enum-типов (явный CREATE/DROP TYPE), 43 таблицы с индексами
(partial unique, NULLS NOT DISTINCT), FK цикла stored_file -> recognition_job отдельным шагом; downgrade —
FK цикла, таблицы в обратном порядке, типы. Тесты (integration, scratch-БД, alembic подпроцессом):
upgrade -> downgrade -> upgrade, пустой compare_metadata и alembic check, каталог = create_all ORM,
поведение partial unique/NULLS NOT DISTINCT/FK-цикла; офлайн-SQL — CREATE SCHEMA первым.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 20:27:23 +04:00
Andrey Epifancev
3b110b6417 feat: alembic env.py — CREATE SCHEMA dkb до таблицы версий, URL из -x dburl, compare_server_default
Схема dkb создаётся и фиксируется до context.configure (онлайн) и печатается первой в офлайн-SQL.
URL: -x dburl > sqlalchemy.url, заданный вызывающим кодом (не заглушка driver://) > настройки.
Async-движок из URL напрямую (без configparser-интерполяции %).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 20:27:15 +04:00
Andrey Epifancev
7ea1e67f4e fix: сверка DBML — табличные UNIQUE и CHECK, действия FK, неизвестные классы типов
I-1: табличный UniqueConstraint ORM сверяется как уникальный индекс по колонкам
(флаг колонки unique=True — по-прежнему в свойствах колонки); любое CHECK в ORM —
«отсутствует в DBML».
I-2: у совпавших целей FK сравниваются ON DELETE / ON UPDATE (нет в DBML = NO ACTION),
DEFERRABLE и INITIALLY.
I-3: неизвестный класс типа отрисовывается repr с параметрами (VARCHAR(length=50),
Uuid(as_uuid=False)) и никогда не совпадает с именем типа DBML.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 20:11:25 +04:00
Andrey Epifancev
e064830702 feat: scripts/check_dbml_vs_orm.py — CLI сверки ORM с DBML (версия, таблицы, колонки, типы, FK, индексы, enum'ы)
Ядро сверки перенесено из tests/dbml_parity.py в scripts/dbml_parity.py (единственная
реализация, R-14); tests/dbml_parity.py и dbml_helpers.py — тонкие обёртки с пропуском
теста без docs. Единый формат строк: `<объект>: ожидалось X / фактически Y`,
`<объект>: отсутствует в ORM|DBML`; тип и nullability — одной строкой в нотации DBML.
Версия из шапки DBML сверяется с DBML_VERSION до сравнения таблиц. Коды: 0/1/2.
Без рабочего .env скрипт берёт api/.env.example. Makefile: dbml-check (в check-full),
mypy проверяет scripts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:56:58 +04:00
Andrey Epifancev
218ce88a31 test: поведенческая проверка partial unique consolidation_lock — первая блокировка фиксируется, снятие проверяется rowcount, имя индекса в ошибке
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:40:11 +04:00
Andrey Epifancev
ef55485dfc test: чётность частей 07–09 с DBML, полнота 43 таблиц и 27 enum, partial unique consolidation_lock на PG16
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:32:48 +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
856ee2129f test: чётность частей 04–06 с DBML, NULLS NOT DISTINCT в компараторе, create_all 25 таблиц на PG16
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:22:29 +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
24fbe74840 test: общий модуль сверки ORM с DBML (dbml_parity): типы колонок, default, partial WHERE; самопроверка компаратора
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:12:53 +04:00
Andrey Epifancev
8aa1b80c33 test: типизация хелпера проверки partial unique
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:05:56 +04:00
Andrey Epifancev
56c98a5d05 test: чётность ORM частей 01–03 с DBML, create_all на временной БД, фикстура scratch_db_url
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 19:05:04 +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