# 09. План работ вертикальными срезами Принцип: каждый срез — рабочий end-to-end кусок (миграции + API + минимальный UI + тесты на реальных фикстурах), который можно показать аналитику. Порядок выбран так, чтобы самое рискованное (парсер ОСВ, матчинг ВГО) проверялось на реальных данных как можно раньше, а внешние зависимости (ИС Сканирование, РАИСА, NATS платформы) не блокировали. Каждый срез = OpenSpec change **в каждом затронутом репозитории** (`openspec/changes//` с `proposal.md`, `specs/`, `tasks.md`), сквозная часть — change в `docs/`; задачи — issues в Gitea; PR — по одному на репозиторий. Jira-эпики пользователя маппятся на срезы (пользователь ведёт соответствие сам). ## Срез 0 — Скелет (2–3 дня, три репозитория + docs) — **завершён 04.10.2026** - Три репозитория из нулевого коммита шаблона команды; соглашения шаблона — в `03-architecture.md` и в `CLAUDE.md` каждого репозитория. **Первая задача агента — прочитать шаблон и дополнить документы.** (Сделано: раздел «Соглашения шаблона команды» в `03-architecture.md`; в `tabular-parser`/`scan-gateway` фронт и БД вырезаются задачами среза.) - OpenSpec change'и среза: `docs/openspec/changes/slice-0-skeleton` (сквозной: контракт, compose, проверки качества, документы) и по одному `openspec/changes/slice-0-skeleton` в `arm-core`, `tabular-parser`, `scan-gateway`; issue #1 в каждом репозитории. - `docker-compose` workspace-уровня (`docs/dev/docker-compose.yml`): PostgreSQL 16, NATS 2.10 **без JetStream** (как на платформе; ack и повторная публикация — на уровне приложения), MinIO (S3, один бакет `arm-ka`), эмулятор ИС Сканирование (часть `scan-gateway`, отдаёт `fixtures/ocr/aep_N.json`), три сервиса (профиль `services`, Python 3.11). `Makefile` с общими целями `up/test/test-integration/lint/check/check-full` (+ `migrate/seed` в ядре) в каждом репо. Порты, запуск профиля `services` и ручная сквозная проверка обмена `recognition_job` через NATS (сценарии `tabular` → `NOT_IMPLEMENTED`, `image` → эмулятор ИС → `INVALID_DOCUMENT`, потеря ack → `dlq`) — `docs/dev/README.md`, разделы «Профиль `services`» и «Проверка окружения». - Контракт v1.0 — три схемы в `docs/contracts/`: `recognition-request`, `recognition-result-message` (конверты NATS) и `recognition-result` (файл результата) + `examples/`. Общий пакет `arm-ka-contracts` **не публикуется**: каждый сервис хранит копию схем в `api/contracts/` и pydantic-модели в `src/contract/`, идентичность с `docs/contracts` проверяется в `make check` (решение D-1 change `slice-0-skeleton` в docs). - Alembic: миграция 0001 — **все** таблицы и enum'ы DBML v5.13 (черновик из ORM, сверка с DBML скриптом `api/scripts/check_dbml_vs_orm.py` — расхождение = ошибка CI). - Seed (`arm-core/api/seed/`, идемпотентно): `regional_office` (ГО; примеры РФ — только `--dev`), `role` (7), `permission` (16 осмысленных сочетаний объект × действие по §10), `role_permission` по §10, `chart_of_account` (План счетов 94н + типовые субсчета 1С, OQ-47), `elimination_rule` v1 из §4 (только ББ), `required_document_rule` для BASIC/EXTENDED (OQ-46). - Auth: валидация JWT + dev-эмитент; `app_user` seed (по одному на роль); зависимость `require(permission)`. - `audit_event` helper; `s3_object` клиент; outbox-паблишер (ack `processing`, повторная публикация без ack, `dlq`) и подписка `files.tabular_parsed` (дедуп по `result_msg_id`, пустой маппер документов). - Проверки качества вместо CI (в Gitea CI нет, пайплайн платформы вне репозиториев): `make check` (ruff, mypy, pytest, контракт) и `make check-full` (+ интеграционные тесты против compose, цикл миграций, `check_dbml_vs_orm`) — `docs/dev/checks.md`. - DoD: `GET /api/v1/me` возвращает пользователя и его эффективные права; все миграции применяются и откатываются; `make check-full` зелёный в трёх репозиториях. ## Срез 1 — Справочники (ГК / ЮЛ / ГСК / ГСЗ / филиалы) + пользователи и роли — **завершён 05.10.2026** Объём — **вариант Б** по решению владельца 04.10.2026: без UI справочника, с минимальным экраном администратора; цель — быстрее дойти до Среза 2 (`tabular-parser`). Изначальный состав среза (CRUD + импорт + админка + policy + фикстуры + UI справочника по макету) разделён: всё, кроме UI справочника, сделано; UI вынесен в отдельный срез. **Сделано (по факту):** - **API справочников** (`arm-core`, `/api/v1/directories/*`): ГК (`code`, `crm_id` nullable, закрепление за РФ, архив, история SCD4), ЮЛ/ИП (ИНН 10/12, алиасы, перевод в другую ГК с `effective_date`, закрытие/вывод из ГК, история), ГСК/ГСЗ и состав (SCD2; ЮЛ — одна ГСК и одна ГСЗ одновременно), филиалы (чтение). Каждая мутация пишет `audit_event`. - **Импорт из выгрузки ЕЦП CRM** (`POST /directories/import`, право `DIRECTORY_ADMIN`): правила — `05-domain-rules.md` §13, `dry_run` по умолчанию, `on_existing ∈ {skip, update}`, отчёт с кодами, `audit_event(DIRECTORY_IMPORT)` и файл в S3; полная выгрузка (8400 строк) обрабатывается за ~1 с. - **Policy видимости** (§10.1): по `regional_office.is_head_office`, без проверки кода роли; ЗО не фильтрует (OQ-86); объект вне видимости — 404. - **Админ-API и минимальный экран `/admin`** («Пользователи и роли»): пользователи, роли, матрица прав (`USER_CHANGE`, `USER_ROLE_CHANGE`, `ROLE_PERMISSION_CHANGE`), защита `LAST_ADMIN`; вход — dev-форма (SSO РАИСА — позже). - **DBML v5.14** (`related_group_member.kind`, partial unique `(legal_entity_id, kind)`, `audit_action` + `LEGAL_ENTITY_CHANGE`/`USER_CHANGE`) и **миграция `0002`** в `arm-core`; `entities-v5.14.md`. - **Фикстура** `fixtures/directories/ecp-export-russkiy-biskvit.xlsx` (21 строка ГК «Русский Бисквит»; `06-test-data.md` §6.1). - **Документы:** §13 и §10.1 в `05-domain-rules.md` (сверены с кодом, задача 3.1 change `slice-1-directories`), OQ-31/32 закрыты, OQ-86…OQ-97 открыты. - **PR:** arm-core #6, docs #7, fixtures #3 (issue-ы: arm-core #5, docs #6, fixtures #2). **Перенесено в будущий срез «UI справочника»** (номер не присвоен; планируется после Среза 2): - Экраны справочника по макету: «ГК и организации» [50] (список ГК | карточка с вкладками ЮЛ / ГСК / ГСЗ / Архив), создание ГК/ЮЛ/ГСК/ГСЗ [51], «ЮЛ вне ГК» [61], вкладка ГСК/ГСЗ [77], изменение ЮЛ [78]–[79], перевод ЮЛ [79-01], вывод из состава ГК [80], модалка импорта [38]. API для них готов с Среза 1 (см. `10-user-journey.md`). - Пока UI нет, справочники заполняются импортом ЕЦП и вызовами API (`/docs`). ## Срез 2 — Загрузка файлов + табличный парсер (ОСВ, ББ, ОФР из Excel) — самый рискованный, делать рано Решение владельца 05.10.2026: срез делится на четыре подсреза со своими change'ами и PR; сначала **только парсер ОСВ**. Порядок: 2a → 2b → 2c → 2d. Change'и закрытых срезов заархивированы: пути ниже вида `/openspec/changes/` теперь `/openspec/changes/archive/<дата>-`. DoD среза в целом (без правил под конкретный файл, без координат в коде парсера) — в конце раздела. ### Срез 2a — Парсер ОСВ (`tabular-parser`) — закрыт 05.10.2026 (tabular-parser #6, архив #8; docs #10, архив #15; fixtures #5) Change'и: `tabular-parser/openspec/changes/slice-2a-osv-parser` (issue arm-tabular-parser#5, PR #6), сквозной `docs/openspec/changes/slice-2a-osv-parser` (arm-ka-docs#9), фикстуры arm-ka-fixtures#4. Копия схемы v1.1 в `arm-core` — отдельный bounded-PR без логики после merge docs и tabular-parser. **Сделано (по факту):** - **Парсер ОСВ** `tabular-parser/api/src/parser/`: динамическое обнаружение структуры по содержимому (§1.0): листы ОСВ, заголовок таблицы и колонки сумм по тексту (обязательны «обороты» и «сальдо на конец»), шапка и `group_schema` (варианты 1 и 2), иерархия каскадом `outline → indent → account_pattern → sums` (+ `trivial` для пустой ОСВ), классификация строк, `Decimal`, контрольные суммы §1.4, статусы и коды (`05-domain-rules.md` §1.6), лимиты `PARSE_MAX_BYTES` (50 МиБ) и `PARSE_TIMEOUT_SECONDS` (120 с). Чтение XLSX/XLS/XLSB по сигнатуре. - **Словари** `api/rules/{osv_headers,osv_groups,periods,data_types}.yaml` (данные, не код) с проверкой при старте и тестом «каждая запись срабатывает хотя бы раз». - **Контракт v1.1**: `rows[].extra` (нераспознанные колонки внутри таблицы); примеры `result-osv-minimal.json`, `result-osv-needs-check.json`. - **Результат на реальных данных:** все 66 файлов `fixtures/osv/**` — `done`, контрольные суммы сходятся, максимум 0,6 с на файл; 16 синтетических файлов (`fixtures/osv/synthetic/`); 82 golden-снимка (`fixtures/golden/expected_osv_parse/`); тест-линтер «нет координат в `src/parser/`»; CLI `python -m src.parser.cli <файл>`. - **Нетривиальное решение (R-21):** сверка «сумма детей = родитель» по сальдо идёт по колонкам либо по нетто, если у родителя сальдо свёрнуто (1С показывает счёт нетто, субсчета — развёрнуто); без него 32 из 66 файлов не сходились. - **Допущения парсера, решаются в 2c:** `is_cumulative` только по тексту периода (OQ-02), `data_type = NU` как есть (OQ-45), «Итого» выдаётся строкой `TOTAL` (OQ-01). - **Документы:** `05-domain-rules.md` §1 приведён к факту (§1.6 — результат парсера), `06-test-data.md` §7 (синтетика, golden, рукописный xlsb), OQ-98…OQ-105 открыты (финальное ревью добавило OQ-106…OQ-109), OQ-81 частично закрыт (в `tabular-parser` `ARM_KA_FIXTURES` поддержан; `scan-gateway` — follow-up). - **Не сделано / риски:** xlsb проверен только рукописным синтетическим файлом (OQ-102); ИНН из текста строки не извлекался (OQ-100; реализовано позже в tabular-parser PR #15); счета с буквой в первом сегменте не распознаются (OQ-103). ### Срез 2b — ББ/ОФР из Excel (`tabular-parser`) — закрыт 06.10.2026 (tabular-parser #16, docs #27, fixtures #10; follow-up arm-tabular-parser#20) - `tabular-parser`: ББ/ОФР из Excel по §12 в тот же `statementDocument`; поиск листов с формами по содержимому (в 2a такие листы пропускаются с предупреждением `skipped: statement (slice 2b)`); словари синонимов `rules/*.yaml`; контрольные формулы §7 для форм. - Тесты: property-тесты инвариантов на **всех** файлах `fixtures/statements-xlsx/**` (формулы §7) + синтетика (ББ+ОФР на одном листе, csv-подобный лист); снимки результата — рядом с golden ОСВ. - **Реализовано (ветка `feat/14-slice-2b-statement-parser`, tabular-parser PR #16; docs — PR #27, ветка `feat/26-slice-2b-statement-parser`):** `BookParser` + конвейер форм `statements/`, словари `statement_*.yaml`, контракт v1.2 (`DISCLOSURE`, `checks`, границы периода ОСВ; копии схемы в arm-core PR #27 и scan-gateway PR #5), `§12`/`§7` по решениям владельца. Тесты: манифест `api/tests/statements_manifest.yaml` (18 реальных файлов), синтетика `st01`–`st07`, golden `expected_statement_parse/` (18 + 7 снимков), интеграционный тест воркера на книге НПЗ. - **Объём:** парсер + контракт v1.2; **маппинг форм в ядре (`statementDocument → document + financial_statement_*`) — Срез 3**. По факту `done` только у форм Межениновской; остальные `needs_check` по данным файлов (рубли с копейками при «тыс. руб.» — на момент среза, до учёта числового формата (#42), страницы без шапки, шаблоны) — OQ-138, OQ-140. - **Не сделано / риски:** единицы приводит числовой формат ячейки, а не парсер (OQ-138, пересмотрено 15.10.2026: значение «как показывает формат»; xlsb — как в файле); форма без колонки кодов не разбирается (OQ-139); страницы в разных файлах не склеиваются (OQ-140); строки без кода не хранятся (OQ-141); после контракта v1.2 `docs/contracts/**` заморожены (копии схемы в arm-core и scan-gateway должны совпадать с ними; в tabular-parser версия закреплена тестом `api/tests/unit/test_contract_v12.py`, побайтное сравнение копий — вручную через `diff` при раскатке); косметика T1.1/T2.x уходит в следующий MINOR. ### Срез 2c — Загрузка файлов и маппинг результата в ядре (`arm-core`) — закрыт 06.10.2026 (arm-core #15, архив #16; docs #17, архив #18) Change'и: `arm-core/openspec/changes/slice-2c-osv-ingest` (issue arm-core#14, ветка `feat/14-slice-2c-osv-ingest`), сквозной `docs/openspec/changes/slice-2c-osv-ingest` (ветка `feat/16-slice-2c-osv-ingest`, DBML v5.15). **Сделано (по факту):** - **Модель:** DBML v5.15 — `osv_data_type + NU` (налоговый учёт, OQ-45), `app_user.email` в нижнем регистре (закрывает OQ-59); миграция `0003_dbml_v5_15`, цикл `upgrade → downgrade → upgrade` и пустой autogenerate. - **Загрузка:** `POST /legal-entities/{id}/files` (xls/xlsx/xlsb/pdf/png/jpg, проверка сигнатуры, лимит 50 МиБ, дедуп по `sha256` в пределах ЮЛ, частичный успех `accepted`/`rejected`), S3 `uploads/{file_id}/{job_id}/…`, `stored_file` + `recognition_job` (outbox) + `FILE_UPLOAD`. - **Статусы файла** ведёт код по текущему заданию (`UPLOADED → PROCESSING → PROCESSED | ERROR`, `SUPERSEDED`): ack, результат, `error`, `timeout`, `dlq`; inbox результат не читает (OQ-67, OQ-69). - **Цикл маппинга** (третий фоновый цикл рядом с публикатором и вахтёром): проверка ключа результата (OQ-70), чтение S3 вне транзакции, `document` на лист ОСВ, `osv_entry` без `TOTAL` с деревом, `account_root`/`account_id`, резолв `counterparty_id` по ИНН/имени/алиасу, `counterparty_check`, статус `RECOGNIZED`/`NEEDS_CHECK`, `is_cumulative` по данным Q1 (OQ-02), до 3 попыток; ОСВ_60 за 2024 (1034 строки) — 0,3 с. - **Автозамена версий** (`SUPERSEDED`, `version_no`, `FILE_REPLACE`; заменяется и `CONFIRMED`), **API** файлов/документов/строк, `POST /documents/{id}/confirm`, `DELETE /files/{id}` (запрет при `CONFIRMED` и при заменивших другие, `expires_at` у объектов S3). - **Проверки на реальных данных:** golden — все 82 снимка парсера (85 документов, 13 510 строк, ≈ 4 с) разобраны, инварианты спеки выполняются; сквозной прогон `make e2e` с реальным `tabular-parser` через compose (ОСВ_66 за 2024 → `PROCESSED`, 9 строк). Итог `make check-full` (контроллер, HEAD f9b8a21): EXIT 0 — unit 900, integration 437, e2e skipped (профиль `services` не поднят), docker-build и web-check OK; отдельный прогон e2e с реальным `tabular-parser`: 1 passed (4,3 с); `-m fixtures`: 13 passed + 1 skipped. - **Решения без обсуждения** — `arm-core/openspec/changes/slice-2c-osv-ingest/design.md`, разделы «Decisions made without discussion» (№1–52). Правила — `05-domain-rules.md` §1.3, §1.5, §3 п. 4, §9.2–9.5; архитектура — `03-architecture.md` («Цикл маппинга результата и порядок блокировок»); QA — `dev/qa-guide.md` §6.6. - **Закрыты OQ:** 67, 69, 70, 59, 109. **Открыты новые:** OQ-110 (удаление файла после консолидаций), OQ-111 (ретеншн S3 при удалении), OQ-112 (остаточные объекты e2e), OQ-113 (полнота субсчетов seed), OQ-114 (`is_modified` в `EntryOut`), OQ-115 (частичная замена файла), OQ-116 (нарастающая по Q1 → `NEEDS_CHECK`). - **Остаётся (Срез 2d и далее):** UI «Первичные документы»; маппинг ББ/ОФР (`statementDocument`) — общий для Excel и OCR (Срез 3 его переиспользует); ручное сопоставление контрагента и пересчёт резолва при построении свода (Срезы 4–5); OQ-100 (ИНН из текста имени контрагента) по-прежнему не решён — резолв идёт по ИНН колонки, имени и алиасу. - Копия схемы контракта v1.1 в `arm-core` (`OsvRow.extra`) — если не сделана bounded-PR раньше. ### Срез 2d — UI «Первичные документы» **Статус:** API готов (Срезы 2c, 3b, arm-core #42); UI — у отдельного исполнителя фронта по `dev/frontend-api-guide.md`. - Панель «Первичные документы», окно загрузки, таблица ОСВ с деревом строк и подсветкой нераспознанных контрагентов. - **Доработка 09.10.2026 (docs #90, DBML v5.18):** файлы в контексте консолидации — `stored_file.consolidation_id`, маршруты `POST/GET /consolidations/{cid}/legal-entities/{le}/files`, панель [39] показывает рабочий стол консоли; спецификация — `openspec/changes/console-files/`; дочерние PR arm-core и arm-web. Целевая модель версий — OQ-160. **DoD среза 2 целиком:** все реальные файлы и вся синтетика проходят **без единого правила под конкретный файл или координату**; в коде парсера нет литералов букв колонок/номеров строк/имён листов (проверяется тестом-линтером). ## Срез 3 — ББ/ОФР: scan-gateway, маппинг форм, логические проверки, подтверждение документа Срез 3 делится на два подсреза с отдельными change'ами. ### Срез 3a — scan-gateway: горячие папки и нормализатор OCR (репозиторий scan-gateway) **Статус: закрыт 07.10.2026** (scan-gateway #8, архив #10; docs #47; fixtures #13; follow-up — issue arm-scan-gateway#9). Change `slice-3a-ocr-normalizer` (arm-scan-gateway#6; docs#46, fixtures#12), спецификация — `scan-gateway/openspec/changes/slice-3a-ocr-normalizer/` (design D-1…D-9, «Decisions made without discussion» №1–№35). Реализация — по `05-domain-rules.md` §11 и §11.1. - Горячие папки в раскладке FileWorker (OQ-137): `Input/.`, `.part` в `Tmp` и запись без замены, `Output//*.json` набором (по стабилизации), очистка — под флагом `SCAN_CLEANUP` (по умолчанию выключена, OQ-144); таймаут 5 мин. - Эмулятор ИС Сканирование для dev: пара PDF + JSON по sha256 содержимого (`fixtures/ocr`), набор по умолчанию `SCAN_EMULATOR_DEFAULT`; сырой ответ → S3 `raw/`. - Нормализатор `OcrNormalizer`: перенос проверенного на стенде модуля `ofr_result_conversion` старого ядра (единственное разрешённое исключение для `reference-old-arm`, решение владельца 06.10.2026) в `statementDocument` v1.2 — по документу на таблицу; справочники `api/rules/` с редакциями форм `66n` / `fsbu4` (`fsbu4` заполняет владелец, OQ-150); `checks` не передаются. - Эталоны нормализатора — `fixtures/ocr/expected_normalize/` (отдельный PR fixtures), `make golden-update`. - Новые вопросы: OQ-144…OQ-152. ### Срез 3b — маппинг форм, проверки, подтверждение и API до сводной ОСВ (arm-core) **Статус: закрыт 07.10.2026** (arm-core #39, архив #41; docs #49; follow-up arm-core#40); UI экрана «Проверка распознавания» в срез не входит. Объём по решению владельца 07.10.2026: весь API пути пользователя до сводной ОСВ включительно. Issue arm-core#36, docs#48; спецификация — `arm-core/openspec/changes/archive/2026-10-07-slice-3b-statements-api/` (design D-1…D-14, «Decisions made without discussion» №1–№22); документы workspace — bounded-PR в docs (без сквозного change: DBML и контракт не менялись). **Сделано (по факту):** - Маппинг форм в ядре: `statementDocument` `BALANCE`/`INCOME` (Excel и OCR) → `document` + `financial_statement_meta/column/line/value` в той же транзакции `map_file`; период по дате отчёта либо по самой поздней дате колонок; `DISCLOSURE`/`OTHER` — документ без данных; файл только с формами — не ошибка (`05-domain-rules.md` §9.5 п. 2, `03-architecture.md`). - Проверки §7 в ядре (`domain/statement_checks.py`, словарь `api/rules/statement_checks.yaml` — копия словаря tabular-parser), межформенная проверка 1370/2400 — мягкая; акцепт жёстких нарушений с комментарием; статус документа формы (§9.3). - API формы: `GET`/`PATCH /documents/{id}/statement`, `POST …/statement/checks/accept`; правка значений и ИНН/даты отчёта/единиц только до подтверждения; подтверждение ББ/ОФР с условиями `PERIOD_REQUIRED` и `CHECKS_NOT_PASSED`; `RECOGNIZED_DATA_EDIT` с before/after. - Мелкие дыры пути пользователя: `GET /files/{id}/content` (исходный файл потоком), комментарии к документу и `comments_count`, `GET /users/brief`, `documents_count` у ЮЛ, CSV чек-листа (`…/checklist?format=csv`). - Контракт API — `dev/api-contract-slice-3b.md`; QA — `dev/qa-guide.md` §6.9. - **Не вошло:** UI; правка строк ОСВ, добавление/удаление строк формы и правка кода/названия строки; отмена подтверждения (исправление — новым файлом); формулы §7 и справочники по редакциям (OQ-146); аудит скачивания (OQ-153). - **Закрыты допущения:** OQ-23, 36, 77, 140, 145 («Срез 3b: допущение реализовано»). **Открыт новый:** OQ-153. Открытые вопросы 3b: OQ-146 (словари по редакциям), OQ-149, OQ-152. **Исходный план среза (для справки):** - Маппинг: `financial_statement_meta/column/line/value` из `statementDocument` (общий для Excel и OCR); числа, скобки, confidence; ИНН/имя сверка. - Логические проверки §7 (чистые функции `domain/checks.py`), акцепт КА, «Подтвердить данные» → `CONFIRMED`, `RECOGNIZED_DATA_EDIT` с before/after. - Чек-лист `required_document_rule × периметр − document CONFIRMED` (API + модалка + CSV). - UI: экран «Проверка распознавания» (исходник слева — PDF.js, результат справа, блок «Логические ошибки»). - Тесты: пары `aep_N.pdf/json`; машиночитаемые PDF АО РБ 2024 как dev-путь без OCR (опционально pdfplumber). ## Срез 4 — Консолидация: карточка, версии, периметр, периоды, сводная ОСВ **Статус: закрыт 07.10.2026** (arm-core #19, архив #20; docs #20, архив #21; follow-up arm-core#21): бэкенд + API + экспорт xlsx; UI — отдельный исполнитель. Объём по решению владельца 06.10.2026: бэкенд, API, экспорт xlsx. Issue arm-core#18, docs#19; спецификация — `arm-core/openspec/changes/archive/2026-10-06-slice-4-consolidation/` (design D-1…D-16, «Decisions made without discussion» №1–№85), документный change — `docs/openspec/changes/archive/2026-10-06-slice-4-consolidation/`. **Сделано (по факту):** - DBML v5.16: `osv_entry.counterparty_match_kind` + enum `counterparty_match_kind` (миграция `0004_dbml_v5_16`, backfill). - Версии консолидации (создание, список по ГК, карточка), периметр (`PUT …/members`), блокировка «взять в работу» (перехват с причиной, без истечения), статусы: автоматические по событиям документов (INITIATED → DOCUMENTS_PROCESSING → DATA_CORRECTION) и ручные (`CANCELLED`, возврат в `INITIATED`); остальные — Срез 7. - Явная привязка подтверждённых документов к версии (`consolidation_document`): подтверждение, подхват при создании версии и добавлении ЮЛ, отвязка при автозамене файла и удалении ЮЛ; Q1-эквивалентность ОСВ (R-14, OQ-124). - **Чек-лист** комплекта документов (перенесён из Среза 3: `required_document_rule × периметр`, статусы ячеек) — API; CSV не делали. - Сводная ОСВ как чистая функция от версии: выбор источников и склейка кварталов (§2), пробелы, дорезолв контрагента, признак `GSK/GK/EXTERNAL`, способ сопоставления; страница, сводка, фильтры. - Кандидаты и ручное сопоставление контрагента с алиасом, переносом алиаса и автоприменением (`PROPAGATED`). - Экспорт xlsx (лист «Сводная ОСВ» и лист «Пробелы»). - Сквозной тест на фикстурах РБ: ОСВ 60/62 РБ и 60/62/76 Счастливочной за Q1–Q3 2024 → версия Q3 → свод 1385 строк, 22 документа привязано; пары с одним лишь Q3 (РБ: 01, 58.03, 66, 67, 76; Счастливочная: 01, 02) — в `gaps` с пропуском Q1–Q2; одно ручное сопоставление «Пекарня Счастливочная ООО» → ГСК. - Контракт API для фронта — `dev/api-contract-slice-4.md`; QA — `dev/qa-guide.md` §7. **Закрыты OQ:** 03, 19, 40 (допущение реализовано), 110 (для Среза 4). **Открыты новые:** OQ-118…OQ-129 (запрет загрузки после DATA_CORRECTION, истечение блокировки, `period_check`, порог кэша свода, `PROCESSING` в чек-листе, общие строки `osv_entry` у версий, Q1-эквивалентность, склейка Q1 версии Q1, область ручного сопоставления, рекомендация кандидата, неоднозначность дорезолва, формулировка сценария «1 + propagated»). **Остаётся (UI и последующие срезы):** экраны `34`–`37`, `42`, `30`, `32-*`, `31-00`; CSV чек-листа (сделан в Срезе 3b); «Добавить версию», `CONFIRM`, `EXTENDED`, гудвилл, маршрут согласования (Срезы 6–7); формы ББ/ОФР в чек-листе по реальным документам — Срез 3b (реализован); шахматка и ВГО — Срез 5. **Исходный план среза (для справки):** `consolidation` / `consolidation_version` / `consolidation_member` / `consolidation_document`; приведение квартальных ОСВ (§2); сводная ОСВ (§3) как запрос; признак контрагента; ручное сопоставление с алиасами и автоприменением; UI со списком, карточкой, составом, сводной ОСВ с фильтрами и экспортом; тест: строки «Юг-Финанс / ПЕКАРНЯ СЧАСТЛИВОЧНАЯ» → ГСК после одного ручного сопоставления. ## Срез 5 — ВГО: автоматическое сопоставление, шахматка, корректировки - `elimination_rule` как данные + версия в `consolidation_version`; матчинг §5 (чистые функции на pydantic-моделях); шахматка §6; `correction` с дочерними строками, контроль 5 %, статусы DRAFT/APPLIED/REVOKED; `ADJUSTMENT`. - UI: шахматка (ячейки с комментариями), таблица пар (авто/ручные/несопоставленные), таблица корректировок с импортом/экспортом xlsx. - Тест: пара 60.01 АО РБ ↔ 62.01 Счастливочная на 30.09.2024 = 106 311 116,46 (точный матч по правилу № 6); заём 42 млн 66.03 ↔ 58.03; сверка «в т. ч. взаимные обязательства» с `Консолидация_Группа РБ.xlsx` лист `01.10.2024` — **только по точным парам**: эталон элиминирует полную сумму ВГО каждой стороны, а Срез 5 — по алгоритму ТЗ (`min` по паре); расхождение ожидаемо и описывается в тесте (OQ-155, решение владельца 08.10.2026). ## Срез 6 — КФО: результат, гудвилл, пороги, сравнение с клиентом - `kfo_result(_line, _line_company)`: сумма → авто-элиминация → корректировки → гудвилл → итог; единицы (OQ-26); пороги §8; `goodwill`; `client_kfo_line` для `CONFIRM`. - UI: КФО (колонки как в эталонном Excel), сводка расхождений, гудвилл, подтверждение КФО клиента, экспорт xlsx. - Тест: КФО группы РБ на 01.10.2024 vs эталонный лист (допуск на ручные правки аналитика — зафиксировать расхождения и показать аналитику). ## Срез 7 — Маршрут согласования, ЕХ, журнал, выгрузки - Полная машина статусов §9.1 с `consolidation_status_history`, policy по ролям/филиалам; № и дата УОБ; новая версия. - `VERIFIED` → `ex_snapshot/_member/_item` (payload по схеме `docs/contracts/ex-payload-v1.schema.json`); read-only. - ЛК ЕХ: дерево ГК → ГСК → организации, просмотр payload, скачивание первички, выгрузка xlsx на лету. - Журнал действий: экран с фильтрами по разделам/объектам/полям. - Экспорт: настраиваемые колонки, `audit_event(EXPORT)`, S3 `exports/` 7 дней. ## Срез 8 — Нефункциональные: ретеншн, архивирование, производительность, безопасность - Lifecycle S3 и удаление по ТЗ 8.4 (5 лет), архив в data lake (ссылка), объёмы `osv_entry` (индексы, partitioning по году при необходимости), нагрузочный тест парсера (1000-строчные ОСВ × 20 компаний), OWASP-проверка, аудит доступа. ## Поперечные правила - Любой срез, затрагивающий модель, начинается с diff к DBML (новая версия v5.14+ с записью в changelog) и миграции. - Вычисляемые объекты — только `domain/` + `repositories/` запросы; никаких таблиц «для ускорения» без DECISION. - Каждый алгоритм из `05-domain-rules.md` — отдельный модуль с docstring-ссылкой на § и тестами на фикстурах. - Открытые вопросы — только через `07-open-questions.md` с `ASSUMPTION(OQ-nn)` в коде.