Andrey Epifancev 98f00c0faa docs: change retry-recognition-and-edit-osv-header, README и CLAUDE.md — DBML v5.21 (ревью #161)
Дельта data-model переименована на v5.21, пакет — только файлы в ERROR,
tasks — ссылки на docs PR #157 и задачи ревью; config.yaml — DBML v5.21.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:13:41 +04:00

12 KiB
Raw Blame History

RENAMED Requirements

  • FROM: ### Requirement: Схема БД равна DBML v5.19
  • TO: ### Requirement: Схема БД равна DBML v5.21

MODIFIED Requirements

Requirement: Схема БД равна DBML v5.21

ORM-метаданные SHALL содержать ровно 44 таблицы и 33 enum'а из ../docs/model/data-model-v5.21.dbml в схеме PostgreSQL из настройки DB_SCHEMA (по умолчанию dkb; в контуре — OPENBAO__APP__DB_SCHEMA; простой идентификатор [a-z_][a-z0-9_]{0,62}, не начинающийся с pg_, иначе приложение не стартует); сверка с DBML идёт в эталонной схеме dkb; имена таблиц, колонок, enum'ов и их значений SHALL совпадать с DBML буквально; nullability, PK, UNIQUE, FK, значения по умолчанию и индексы SHALL соответствовать DBML, включая частичные уникальные индексы (related_group_member (related_group_id, legal_entity_id) и (legal_entity_id, kind) WHERE is_current, consolidation_lock WHERE released_at IS NULL) и NULLS NOT DISTINCT на document (stored_file_id, document_type, osv_account_code). related_group_member.kind SHALL быть NOT NULL и равняться related_group.kind группы (проверяет ядро при вставке). audit_action SHALL содержать LEGAL_ENTITY_CHANGE и USER_CHANGE; osv_data_type SHALL содержать NU (после BU). Enum counterparty_match_kind SHALL содержать INN, NAME, ALIAS, MANUAL, PROPAGATED; колонка osv_entry.counterparty_match_kind (nullable) SHALL быть NULL тогда и только тогда, когда counterparty_id NULL (проверяет ядро при записи). app_user.email SHALL храниться в нижнем регистре. Миграция 0004_dbml_v5_16 SHALL создавать enum и колонку и заполнять её по уже смапленным строкам (ИНН строки без пробелов = legal_entity.inn → INN; trim(counterparty_name) = legal_entity.name → NAME; остальные с counterparty_id → ALIAS); откат SHALL удалять колонку и enum. Колонка kpp (varchar, nullable, без индексов и уникальности; уникален только inn) SHALL быть последней в legal_entity и legal_entity_history; формат и запрет КПП у ИП проверяет ядро. Миграция 0005_dbml_v5_17 SHALL добавлять обе колонки (существующие записи — kpp IS NULL), откат — удалять. Колонка stored_file.consolidation_id (uuid, nullable, FK → consolidation.id; v5.18) SHALL быть последней в stored_file; индекс ix_stored_file_legal_entity_id_consolidation_id (legal_entity_id, consolidation_id). Миграция 0006_dbml_v5_18 SHALL добавлять колонку, FK и индекс и заполнять колонку по consolidation_document: файлу с документом, привязанным к версии, — консолидация этой версии; при привязках к версиям разных консолидаций — самый ранний период (год, затем квартал); файлы без привязок остаются NULL; откат SHALL удалять индекс и колонку. Поля, отсутствующие в DBML, MUST NOT существовать. Схема v5.19 (RSHB/arm-ka-docs#116) SHALL добавлять: таблицу document_issue и enum'ы statement_line_status, statement_form_variant, document_issue_kind и прочие enum'ы DBML v5.19; колонки document.file_ordinal (уникальна в паре (stored_file_id, file_ordinal)), recognition_job.statements_version, related_group.crm_id (varchar, nullable, уникальна), в financial_statement_meta — form_variant, needs_review_fields, header_source, organization_name_original_value, organization_name_modified; в financial_statement_column — period_kind, label_confidence; в financial_statement_line — row_index, source_ref, line_status, line_code_text, parent_row_index, needs_review, is_confirmed; в financial_statement_value — source_text, needs_review, is_confirmed (с confirmed_by/at). Уникальность (meta_id, line_code) снимается, добавляется частичная уникальность (meta_id, source_ref) WHERE source_ref IS NOT NULL; уникальность документа (stored_file_id, document_type, osv_account_code) становится частичной (только для типов, кроме BALANCE / INCOME). Состав и порядок колонок — буквально по DBML v5.19. Схема v5.20 SHALL добавлять в конец document колонки is_modified (boolean NOT NULL DEFAULT false), original_values (jsonb, nullable), modified_by (uuid, nullable, FK → app_user.id) и modified_at (timestamptz, nullable); схема v5.21 SHALL добавлять значение FILE_REPROCESS в enum audit_action. Состав и порядок колонок — буквально по DBML v5.21.

Scenario: Сверка ORM с DBML

  • WHEN выполняется python scripts/check_dbml_vs_orm.py ../../docs/model/data-model-v5.21.dbml (make dbml-check)
  • 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

Scenario: ЮЛ одновременно в ГСК и ГСЗ

  • WHEN для одного legal_entity_id вставлены две текущие строки related_group_member с kind = gsk и kind = gsz
  • THEN обе вставки проходят; третья текущая строка с kind = gsk нарушает уникальный индекс

Scenario: Строка налогового учёта

  • WHEN в osv_entry вставляется строка с data_type = 'NU'
  • THEN вставка проходит

Scenario: Backfill при миграции

  • WHEN до миграции 0004 есть строки с counterparty_id, найденным по ИНН, по имени и по алиасу, и строка без counterparty_id
  • THEN после alembic upgrade head у них counterparty_match_kind = INN, NAME, ALIAS и NULL соответственно; alembic downgrade 0003 удаляет колонку и enum

Scenario: Backfill консолидации файла при миграции 0006

  • WHEN до миграции 0006 есть файл A с документом, привязанным к версии консолидации 2025 Q2 и к версии консолидации 2025 Q1, и файл B без привязок
  • THEN после alembic upgrade head у A consolidation_id = консолидация 2025 Q1, у B — NULL; колонка последняя в stored_file; alembic downgrade 0005_dbml_v5_17 удаляет индекс и колонку, повторный upgrade их возвращает

Scenario: Сверка с DBML v5.19

  • WHEN выполняется make dbml-check по ../docs/model/data-model-v5.21.dbml
  • THEN таблицы, колонки, enum'ы, ключи и индексы ORM совпадают с DBML без расхождений

Requirement: Версия DBML зафиксирована в коде

Пакет моделей SHALL объявлять константу версии DBML (DBML_VERSION = "5.21"); скрипт сверки SHALL сравнивать её с версией в шапке файла DBML и падать при расхождении — смена модели без осознанного обновления ORM невозможна.

Scenario: Новая версия DBML без обновления ORM

  • WHEN в docs/model/ появляется data-model-v5.22.dbml, а константа в ORM — 5.21
  • THEN сверка против нового файла падает с сообщением о версии до сравнения таблиц

ADDED Requirements

Requirement: Миграция DBML v5.20

Одна добавочная миграция после 0007_dbml_v5_19 SHALL добавить четыре колонки document по DBML v5.20; существующие записи получают is_modified = false и остальные поля NULL. Миграция MUST NOT менять документы, их строки, подтверждения и привязки. Downgrade SHALL до любых изменений проверить отсутствие document.is_modified = true и при наличии остановиться с ошибкой и числом записей.

Scenario: Обновление существующих ОСВ

  • WHEN миграция применяется к базе с подтверждёнными ОСВ РБ
  • THEN добавлены четыре поля, данные ОСВ, id, file_ordinal, подтверждения и привязки сохранены; сверка с DBML v5.20 проходит

Scenario: Защита от потери исправлений

  • WHEN хотя бы один документ имеет is_modified = true и запрошен downgrade до v5.19
  • THEN откат прекращается до DDL с причиной и числом записей, схема и данные не меняются

Scenario: Пустая база и нестандартная схема

  • WHEN выполняется upgrade head, downgrade base, upgrade head на пустой базе с настроенным DB_SCHEMA
  • THEN цикл успешен, ORM соответствует DBML v5.20, autogenerate пуст

Requirement: Миграция DBML v5.21 и действие аудита повторной обработки

Отдельная миграция v5.21 после миграции v5.20 SHALL добавить только FILE_REPROCESS в enum audit_action по DBML v5.21. Миграция SHALL сохранять все существующие значения и события. Downgrade MUST NOT удалять либо переписывать audit_event; при наличии FILE_REPROCESS он SHALL завершаться ошибкой до изменения схемы.

Scenario: Схема после дополнения модели

  • WHEN миграция v5.21 применена к БД v5.20
  • THEN событие нового типа записывается, прежние события читаются без изменений, сверка DBML успешна

Scenario: Откат после ручного повтора

  • WHEN существует audit_event с FILE_REPROCESS и выполняется downgrade с v5.21 к v5.20
  • THEN ошибка с причиной и количеством несовместимых событий до DDL, события и схема сохранены

Scenario: Независимый откат enum

  • WHEN в БД v5.21 нет FILE_REPROCESS и выполняется downgrade к v5.20
  • THEN удалено только новое значение enum; четыре поля document, исходные значения и привязки сохранены