Proposal, design (D-1…D-16, решения без обсуждения), tasks и дельты спек: новые capability consolidations-api и consolidated-osv; изменения data-model (DBML v5.16, counterparty_match_kind), documents-api (привязка при подтверждении), primary-files (consolidation_version_id, отвязка при автозамене), osv-result-mapping (match_kind, NEEDS_CHECK → DATA_CORRECTION), directory-policy (редактируемость версии), audit-trail (события консолидации). Решения владельца 06.10.2026: OQ-03 пробел, OQ-19 блокировка, статусы по событиям, явная привязка, экспорт одним листом. Refs RSHB/arm-core#18 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
88 lines
9.4 KiB
Markdown
88 lines
9.4 KiB
Markdown
# Proposal: Срез 4 — консолидация: версии, периметр, блокировка, привязка документов, сводная ОСВ, экспорт xlsx
|
||
|
||
**Срез:** 4 (`../docs/context/09-delivery-plan.md`). **Репозиторий:** arm-core. **Issue:** RSHB/arm-core#18.
|
||
**Родительский change:** `../docs/openspec/changes/slice-4-consolidation` (DBML v5.16, правила §2/§3/§9 по факту, OQ;
|
||
RSHB/arm-ka-docs#19). Объём по решению владельца 06.10.2026: **бэкенд, API, экспорт xlsx**; UI (список, карточка,
|
||
состав, вкладка ОСВ) делает другой разработчик по этим спекам.
|
||
|
||
## Why
|
||
|
||
После Среза 2c строки ОСВ лежат в `osv_entry` по документам, но объекта «консолидация» в ядре нет: модели шести таблиц
|
||
есть, сервисов, репозиториев и эндпоинтов нет; права `CONSOLIDATION_*` ни на одном маршруте не используются;
|
||
`period_check = UNKNOWN`. Без Среза 4 нет периметра версии, нет сводной ОСВ, на которой стоят шахматка и ВГО (Срез 5),
|
||
нет чек-листа комплекта и некуда показывать результат ручного сопоставления контрагентов.
|
||
|
||
## What Changes
|
||
|
||
- **DBML v5.16**: enum `counterparty_match_kind` (`INN`, `NAME`, `ALIAS`, `MANUAL`, `PROPAGATED`) и колонка
|
||
`osv_entry.counterparty_match_kind` — как появился `counterparty_id` (решение владельца 06.10.2026: строки,
|
||
сопоставленные автоматически по имени или алиасу, должны выделяться в API). Миграция `0004_dbml_v5_16` с backfill
|
||
по уже смапленным строкам. Таблиц 43, enum'ов 28.
|
||
- **Маппинг (2c)** пишет `counterparty_match_kind` по шагу резолва (`CounterpartyIndex.resolve` возвращает шаг).
|
||
- **Консолидации** (`consolidations-api`): `POST /consolidations` (ГК + ГСК + год + квартал → `consolidation`,
|
||
`consolidation_version` №1 `INITIATED`, `consolidation_member` из текущего состава ГСК, минимум 2 ЮЛ);
|
||
список по ГК с деревом версий; карточка версии с периметром, ответственным и ячейками чек-листа; `PUT …/members`;
|
||
блокировка `POST/DELETE …/lock` с перехватом по причине; `POST …/status` (в Срезе 4 — только `CANCELLED` и возврат
|
||
в `INITIATED`); чек-лист `GET …/checklist` (перенесён из Среза 3: без него негде показать пробелы кварталов).
|
||
- **Привязка документов** (`documents-api`, `primary-files`): подтверждение документа привязывает его
|
||
(`consolidation_document`) ко всем версиям в работе, которым он подходит по периметру и периоду, и двигает статус
|
||
`INITIATED`/`DOCUMENTS_PROCESSING → DATA_CORRECTION`; создание версии и добавление ЮЛ подхватывают уже подтверждённые
|
||
документы; автозамена файла и удаление ЮЛ из периметра снимают привязку. Загрузка файла принимает необязательный
|
||
`consolidation_version_id` (`INITIATED → DOCUMENTS_PROCESSING`); маппинг документа `NEEDS_CHECK` для ЮЛ периметра —
|
||
`DOCUMENTS_PROCESSING → DATA_CORRECTION`.
|
||
- **Сводная ОСВ** (`consolidated-osv`): чистая функция `domain/consolidated_osv.py` от версии, периметра и привязанных
|
||
ОСВ: приведение квартальных ОСВ к нарастающим (§2: ключ (счёт, контрагент), пропуск квартала → пробел, строки не
|
||
строятся), признак контрагента ГСК/ГК/Внешний, дорезолв незаполненного `counterparty_id` на чтение (не сохраняется),
|
||
`match_kind`/`match_stored`. `GET …/osv` (страница, фильтры), `GET …/osv/summary` (счётчики, пробелы),
|
||
`GET …/counterparty-candidates`, `POST …/osv/entries/{id}/counterparty` (алиас, перенос алиаса, автоприменение,
|
||
`OSV_COUNTERPARTY_MAP`), `GET …/osv/export` (xlsx, openpyxl, два листа, аудит `EXPORT`).
|
||
- **Политика** (`directory-policy`): видимость консолидации = видимость её ГК; правки версии — только в статусе «в
|
||
работе» и при своей активной блокировке.
|
||
- **Аудит** (`audit-trail`): `CONSOLIDATION_CREATE`, `MEMBERSHIP_CHANGE`, `CONSOLIDATION_LOCK`,
|
||
`CONSOLIDATION_LOCK_TAKEOVER`, `CONSOLIDATION_STATUS_CHANGE`, `OSV_COUNTERPARTY_MAP`, `EXPORT` — все с
|
||
`consolidation_version_id`.
|
||
- **Тесты**: домен без БД (приведение периодов, пробелы, признак), API на PostgreSQL, гонки блокировок, сквозной
|
||
`fixtures`-тест: консолидация группы «Русский бисквит» за Q3 2024, одно ручное сопоставление → ГСК у всех строк
|
||
«Пекарня Счастливочная».
|
||
- **Документы**: README среза, CLAUDE.md, спеки.
|
||
|
||
## Capabilities
|
||
|
||
### New Capabilities
|
||
- `consolidations-api`: создание, список, карточка, периметр, блокировка, статусы, чек-лист.
|
||
- `consolidated-osv`: расчёт сводной ОСВ, кандидаты и ручное сопоставление контрагента, экспорт xlsx.
|
||
|
||
### Modified Capabilities
|
||
- `data-model`: схема равна DBML **v5.16**, миграции 0001 → 0004.
|
||
- `documents-api`: подтверждение документа привязывает его к версиям и меняет их статус.
|
||
- `primary-files`: загрузка с `consolidation_version_id`; автозамена снимает привязку заменённых документов.
|
||
- `osv-result-mapping`: `counterparty_match_kind` при резолве; переход версии в `DATA_CORRECTION` по `NEEDS_CHECK`.
|
||
- `directory-policy`: видимость консолидаций и правило редактируемости версии.
|
||
- `audit-trail`: события консолидации.
|
||
|
||
## Impact
|
||
|
||
- **Таблицы DBML v5.16:** `consolidation`, `consolidation_version`, `consolidation_status_history`, `consolidation_lock`,
|
||
`consolidation_member`, `consolidation_document`, `osv_entry` (новая колонка), `legal_entity_alias`,
|
||
`required_document_rule`, `audit_event`.
|
||
- **Правила:** §2 (приведение периодов), §3 (сводная ОСВ, ручное сопоставление), §9.1 (статусы Среза 4), §9.3
|
||
(`period_check` остаётся `UNKNOWN`, см. design D-12), §9.5 (отвязка при автозамене), §10 (права), `04-data-model-guide`
|
||
(«что не хранится»).
|
||
- **Код:** новые `api/endpoints/consolidations/`, `services/consolidations.py`, `services/consolidation_locks.py`,
|
||
`services/consolidation_documents.py`, `services/consolidated_osv.py`, `services/osv_export.py`,
|
||
`repositories/consolidations.py`, `domain/consolidated_osv.py`, `domain/consolidation_periods.py`,
|
||
`schemas/consolidations.py`; правки `services/documents.py`, `services/result_mapping.py`, `services/primary_files.py`,
|
||
`repositories/counterparties.py`, `domain/policy.py`, `api/endpoints/files/upload.py`, миграция `0004`.
|
||
- **Потребители:** фронт (2d/4-UI) — по этим спекам; tabular-parser и контракт v1.1 не меняются.
|
||
|
||
## Non-goals
|
||
|
||
- Маршрут согласования после `DATA_CORRECTION` (`CONSOLIDATION_RF` и далее), УОБ, `ex_snapshot`, «Добавить версию»,
|
||
сценарий `CONFIRM`, подход `EXTENDED` — Срезы 6–7. Переходы в эти статусы отвечают 409 `TRANSITION_NOT_ALLOWED`.
|
||
- Запрет загрузки/подтверждения документов в статусах после `DATA_CORRECTION` (ТЗ 03_9 A1) — Срез 7, OQ-118.
|
||
- `period_check` документа по версии (OQ-120): остаётся `UNKNOWN`; подходящесть документа версии считается на лету.
|
||
- Шахматка, пары ВГО, корректировки (Срез 5), ББ/ОФР из Excel (2b), PDF (Срез 3), UI (другой разработчик).
|
||
- CSV чек-листа (макет `37`) — фронт строит из JSON.
|
||
- Отвязка контрагента от строки (обратное действие): перевязка покрывает исправление ошибки.
|
||
- Зоны ответственности в политике (OQ-86), истечение блокировки по времени (OQ-119).
|