Andrey Epifancev 36bc966bf6 feat(consolidation): привязка документов к версиям при подтверждении, подхват и отвязка при автозамене
- consolidation_documents: чистая fits (D-5), bind_confirmed (подхват, самый поздний confirmed_at на ключ R-1),
  unbind_entity, on_document_confirmed (привязка ко всем подходящим версиям «в работе», переход в DATA_CORRECTION
  через transition), on_documents_superseded (отвязка документов заменённых файлов).
- DocumentService.confirm вызывает хук привязки; supersede_previous снимает привязки и пишет
  FILE_REPLACE.after_value.unbound_version_ids; create и PUT …/members подхватывают по-настоящему.
- R-2: advisory-блокировка привязок ГК (lock_group_bindings) в create и lock_versions_for_entity закрывает
  гонку «создание версии ∥ подтверждение/автозамена».
- R-3: lock_document и lock_file_documents — FOR NO KEY UPDATE (совместимо с FOR KEY SHARE вставки привязки).
- Тесты: таблица fits, сценарии documents-api и primary-files, две версии одного документа, гонки на двух
  сессиях (confirm ∥ members в обе стороны, create ∥ confirm в обе стороны, цикл блокировок документа и версии).
- design.md: решения №30–№36; plan и tasks.md (3.1) отмечены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 04:57:15 +04:00

7.2 KiB
Raw Blame History

Tasks: Срез 4 — arm-core

Каждая задача ≤ половины дня, начинается с тестов, заканчивается зелёным make check и интеграционными тестами своей области. Допущения в коде — # ASSUMPTION(OQ-nn). DBML v5.16 — ../docs/model/data-model-v5.16.dbml (ветка feat/19-slice-4-consolidation в .wt/slice-4/docs). Детальный план с кодом — docs/superpowers/plans/ (writing-plans).

1. Модель

  • 1.1 DBML v5.16, миграция 0004, match_kind в маппинге. CounterpartyMatchKind enum, колонка OsvEntry.counterparty_match_kind; миграция 0004_dbml_v5_16 (enum, колонка, backfill SQL, downgrade); DBML_VERSION, Makefile, dbml_parity, test_migrations (HEAD), test_enums, test_check_dbml_script (фиктивная 5.17); CounterpartyIndex.resolve → Resolution(entity_id, kind) | None; result_mapping._with_counterparty пишет kind; EntryOut.match_kind. Тесты: backfill на трёх строках, golden-маппинг с kind, test_counterparty_resolve.
  • 1.2 Фабрики и репозиторий. tests/factories.py: make_consolidation(session, *, group, year, quarter, members, status), make_lock, make_confirmed_osv(entity, account, year, quarter, cumulative, rows); repositories/consolidations.py: get_version(scope), list_periods(group), lock_version, lock_versions_for_entity(entity_id, statuses), active_lock, members, bound_documents, load_osv_rows, history; CommittedJobs.cleanup() знает таблицы консолидации.

2. Версии, периметр, блокировка, статусы

  • 2.1 Создание, список, карточка. schemas/consolidations.py (D-16), services/consolidations.py:create/list_periods/card, domain/consolidation_periods.py:reporting_date, эндпоинты POST /consolidations, GET /company-groups/{id}/consolidations, GET /consolidations/versions/{id}, роутер. Тесты: сценарии consolidations-api «Создание», «Список и карточка», policy 404.
  • 2.2 Статусы и политика. domain/policy.py:IN_WORK_STATUSES, ensure_version_editable; services/consolidation_status.py: transition(version, to, by, comment, *, manual) с таблицей Среза 4; POST …/status. Тесты: CANCELLED с/без комментария, возврат в INITIATED, TRANSITION_NOT_ALLOWED, VERSION_NOT_EDITABLE, LOCK_REQUIRED.
  • 2.3 Блокировка. services/consolidation_locks.py:take/release, POST/DELETE …/lock, IntegrityError → LOCKED_BY_OTHER. Тесты: сценарии «Взять в работу», гонка двух POST (test_api_constraint_races), CANCELLED снимает блокировку.
  • 2.4 Периметр. PUT …/members с подхватом/отвязкой (вызывает 3.1), MEMBERSHIP_CHANGE. Тесты: сценарии «Периметр версии».

3. Привязка документов и чек-лист

  • 3.1 Привязка. services/consolidation_documents.py:fits(document, version), bind_confirmed(version, entity_ids), unbind_entity, on_document_confirmed(document, user), on_file_superseded(file, documents); правки DocumentService.confirm (версии FOR UPDATE после документа), supersede_previous (unbound_version_ids в аудите). Тесты: сценарии documents-api (привязка, период, CANCELLED, подхват, две версии документа), primary-files «Заменённый документ был в версии», порядок блокировок (подтверждение ∥ правка периметра).
  • 3.2 Статусы по событиям файлов. upload.py: consolidation_version_id (проверки, переход после приёма); result_mapping: хук on_documents_mapped (NEEDS_CHECK → DATA_CORRECTION, uploaded_by, пропуск при NULL). Тесты: сценарии primary-files «Загрузка в контексте версии», osv-result-mapping «Предупреждение маппинга».
  • 3.3 Чек-лист. domain/checklist.py:build(rules, members, documents, files) (чистая), GET …/checklist, ячейки в карточке. Тесты: пробел Q2, субсчёт, PROCESSING, ERROR, ББ/ОФР за квартал версии.

4. Сводная ОСВ

  • 4.1 Домен. domain/consolidation_periods.py:select_sources (нарастающий/квартальные/пробел), domain/consolidated_osv.py: build(...) (склейка по ключу, нули, дубли, entry_ids, warnings, дорезолв, признак, фильтры, порядок). Тесты без БД: все сценарии «Выбор источников», «Строки свода», фильтры, версия за Q1, дубль ключа, расхождение counterparty_id.
  • 4.2 API свода. services/consolidated_osv.py:page/summary, GET …/osv, GET …/osv/summary. Тесты: страница на PostgreSQL с тремя кварталами, unmapped, match_kind, q, чтение CANCELLED, curator_go читает.
  • 4.3 Кандидаты и сопоставление. GET …/counterparty-candidates, POST …/osv/entries/{id}/counterparty (services/counterparty_mapping.py: алиас, ALIAS_TAKEN, перенос, MANUAL/PROPAGATED, ИНН-противоречие, аудит). Тесты: все сценарии «Кандидаты», «Ручное сопоставление», блокировка/статус.
  • 4.4 Экспорт. services/osv_export.py (openpyxl, два листа, BytesIO), GET …/osv/export, Content-Disposition, аудит EXPORT. Тест: файл читается openpyxl, суммы равны API, лист «Пробелы».

5. Сквозной тест и документы

  • 5.1 Фикстуры РБ. tests/test_consolidation_fixtures.py (маркер fixtures): маппинг golden-снимков ОСВ РБ и Счастливочной за Q1–Q3 2024 → подтверждение → консолидация 2024 Q3 → свод: счета 60/62 у обеих и 76 у Счастливочной, остальные в gaps; одно сопоставление «Пекарня Счастливочная ООО» → GSK у всех строк РБ с этим именем; экспорт открывается.
  • 5.2 Документы. README (консолидации, статусы Среза 4, свод, сопоставление, экспорт, коды ошибок), CLAUDE.md (новые модули, порядок блокировок), openspec спеки; синхронизация с docs-change (OQ, §2/§3/§9).