arm-ka-docs/context/09-delivery-plan.md
Andrey Epifancev 70ba5e9db8 docs: Срез 1 — entities-v5.14, guide, план срезов, индексы, сверка §13 с кодом
- model/entities-v5.14.md: таблица «Что изменилось в v5.14», related_group_member (ГСК и ГСЗ одновременно), audit_event
- context/04: файлы и partial unique (legal_entity_id, kind) v5.14, OQ-31/32 закрыты
- ссылки на v5.14: 00, 03, 05, KICKOFF-PROMPT, dev/checks.md, openspec/config.yaml, repo-templates/arm-core.CLAUDE.md
- 09: Срез 1 по факту (вариант Б), перенос UI справочника; 10: статус экранов справочника и /admin
- 08: выгрузка ЕЦП; 06: §6.1 фикстура directories/ecp-export-russkiy-biskvit.xlsx
- 05 §13: сверка с ImportCode (добавлен GSZ_CREATED, перечень 16 кодов), порядок проверок, уточнения реализации,
  лимиты файла, ошибки файла, след в аудите и S3
- tasks.md: 2.1–2.3, 3.1 выполнены

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 11:09:03 +04:00

150 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 09. План работ вертикальными срезами
Принцип: каждый срез — рабочий end-to-end кусок (миграции + API + минимальный UI + тесты на реальных фикстурах),
который можно показать аналитику. Порядок выбран так, чтобы самое рискованное (парсер ОСВ, матчинг ВГО) проверялось
на реальных данных как можно раньше, а внешние зависимости (ИС Сканирование, РАИСА, NATS платформы) не блокировали.
Каждый срез = OpenSpec change **в каждом затронутом репозитории** (`openspec/changes/<slug>/` с `proposal.md`, `specs/`,
`tasks.md`), сквозная часть — change в `docs/`; задачи — issues в Gitea; PR — по одному на репозиторий. Jira-эпики
пользователя маппятся на срезы (пользователь ведёт соответствие сам).
## Срез 0 — Скелет (2–3 дня, три репозитория + docs)
- Три репозитория из нулевого коммита шаблона команды; соглашения шаблона — в `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`) и
подписка `recognition.result` (дедуп по `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) — самый рискованный, делать рано
- `arm-core`: `stored_file` + `s3_object` + `recognition_job` (outbox) + дедуп по `content_hash` + замена файла (`SUPERSEDED`).
- `tabular-parser`: **динамическое** обнаружение структуры (§1.0): поиск листов с ОСВ/ББ/ОФР по содержимому, заголовка
таблицы и колонок сумм по тексту, иерархии строк каскадом стратегий (outline level → отступ → шаблон кода → суммы),
шапки регулярками; классификация по `group_schema`; контрольные суммы; ББ/ОФР из Excel по §12 в тот же
`statementDocument`; словари синонимов `rules/*.yaml`; результат по контракту; NATS request/result; S3 `results/`.
- Маппинг результата в ядре: `document` (OSV) + `osv_entry` (все строки, OQ-01) + резолв `account_id`, `account_root`,
`counterparty_id` по ИНН/имени/алиасу; `document.period_check / counterparty_check`; статусы.
- Тесты: property-тесты инвариантов на **всех** файлах `fixtures/osv/**` и `fixtures/statements-xlsx/**` («Итого» =
корень; тождество сальдо/оборотов; Σ детей = родитель; формулы §7 для форм) + синтетика для форматов, которых нет в
фикстурах (шапка вариант 2, без outline level, csv-подобный лист, Вал./Кол., xlsb, ББ+ОФР на одном листе, текстовые
числа). Снимок результатов → `fixtures/golden/expected_osv_parse/`.
- Маппинг ББ/ОФР в ядре из `statementDocument` — общий для Excel и OCR (Срез 3 его переиспользует).
- UI: панель «Первичные документы», окно загрузки, таблица ОСВ с деревом строк и подсветкой нераспознанных контрагентов.
- DoD: все реальные файлы и вся синтетика проходят **без единого правила под конкретный файл или координату**; в коде
парсера нет литералов букв колонок/номеров строк/имён листов (проверяется тестом-линтером).
## Срез 3 — ББ/ОФР: scan-gateway, маппинг форм, логические проверки, подтверждение документа
- `scan-gateway`: горячие папки (по FNR02) с заглушкой-эмулятором ИС Сканирование для dev (возвращает `aep_*.json`
по имени файла); сырой ответ → S3 `raw/`; нормализация в контракт результата; таймаут 5 мин.
- Маппинг: `financial_statement_meta/column/line/value` из JSON; числа, скобки, 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 — Консолидация: карточка, версии, периметр, периоды, сводная ОСВ
- `consolidation` / `consolidation_version` / `consolidation_member` / `consolidation_document`; статусы INITIATED →
DOCUMENTS_PROCESSING → DATA_CORRECTION; `consolidation_lock`.
- Приведение квартальных ОСВ к нарастающим (§2) как вычисляемое представление.
- Сводная ОСВ (§3) как запрос; признак контрагента; ручное сопоставление с алиасами и автоприменением.
- UI: список консолидаций, карточка, состав, сводная ОСВ с фильтрами и экспортом xlsx.
- Тест: сводная ОСВ группы РБ за 9 мес. 2024: строки «Юг-Финанс / ПЕКАРНЯ СЧАСТЛИВОЧНАЯ» → ГСК после одного ручного
сопоставления.
## Срез 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`.
## Срез 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)` в коде.