From 9086c064e494a7eb6fe9988ed015913b89d76a26 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Fri, 6 Nov 2026 23:03:19 +0400 Subject: [PATCH] =?UTF-8?q?=D0=95=D0=A5:=20=D0=B4=D0=B8=D0=B7=D0=B0=D0=B9?= =?UTF-8?q?=D0=BD=20=D0=B8=20=D0=BF=D0=BB=D0=B0=D0=BD=20change=20ex-payloa?= =?UTF-8?q?d-schema=20(#208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../changes/ex-payload-schema/.openspec.yaml | 2 + openspec/changes/ex-payload-schema/design.md | 125 ++++++++++++++++++ .../plans/2026-11-06-ex-payload.md | 69 ++++++++++ .../changes/ex-payload-schema/proposal.md | 36 +++++ 4 files changed, 232 insertions(+) create mode 100644 openspec/changes/ex-payload-schema/.openspec.yaml create mode 100644 openspec/changes/ex-payload-schema/design.md create mode 100644 openspec/changes/ex-payload-schema/plans/2026-11-06-ex-payload.md create mode 100644 openspec/changes/ex-payload-schema/proposal.md diff --git a/openspec/changes/ex-payload-schema/.openspec.yaml b/openspec/changes/ex-payload-schema/.openspec.yaml new file mode 100644 index 0000000..8a94d76 --- /dev/null +++ b/openspec/changes/ex-payload-schema/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-11-06 diff --git a/openspec/changes/ex-payload-schema/design.md b/openspec/changes/ex-payload-schema/design.md new file mode 100644 index 0000000..cd75bd2 --- /dev/null +++ b/openspec/changes/ex-payload-schema/design.md @@ -0,0 +1,125 @@ +# Design: схема среза ЕХ ex-payload-v1 + +Change `ex-payload-schema`, issue RSHB/arm-ka-docs#208, ветка `feat/208-ex-payload` (от `feat/206-kfo-contract`). +Читатель — Потапова (AURORA-1560, запись среза), следом — исполнитель AURORA-2001 (чтение среза) и тестировщик 1562. +Читают как постановку, без созвона. + +## 1. Зачем + +Срез ЕХ — самодостаточная заморозка верифицированной версии: его читают и выгружают, когда рабочих таблиц консолидации +уже нет. Всё производное (сводная ОСВ, агрегаты ВГО, шахматка, КФО, гудвилл, корректировки, сравнение с клиентом, +карточка версии) лежит в `ex_snapshot_item.payload` (jsonb). Без схемы payload каждый, кто пишет и читает срез, решает +формат сам — и просмотр среза разойдётся с экраном рабочей версии. + +Успех: Потапова пишет срез, проверяя каждый элемент схемой; исполнитель 2001 рендерит и выгружает срез по той же схеме, +не заглядывая в таблицы консолидации. + +## 2. Цели, не цели, анти-цели + +**Цели** — схема конверта и данных по каждому `item_type`, примеры, проверка в `make check`; правила среза в §14; правки +DBML, без которых схему не записать (`version_card`, действие журнала). + +**Не цели** — `proposal.md`. + +**Анти-цели** +- Payload, который без таблиц консолидации не прочитать: ссылки допустимы только на то, что живёт не меньше среза — + `document`, `legal_entity`, `related_group`, `company_group`, `app_user` (DOC-274687 правило 4); остальные id (строк + ОСВ, корректировок, правил) — справочно, для рендера не нужны. +- Схема, которая заставляет фронт ЕХ пересчитывать: в payload лежат готовые значения так, как их видел аналитик. +- Повторный перевод единиц: суммы в payload — как в источнике (DOC-274687 правило 5), единица — рядом. + +## 3. Ограничения + +- JSON Schema 2020-12, как остальные контракты; проверка примеров — `scripts/validate_contracts.py` (`make check`). +- Деньги — строки с двумя знаками (`^-?\d{1,16}\.\d{2}$`), как в API; `null` — «не рассчитано». +- Схема версионируется: `schema_version` в конверте и `ex_snapshot_item.payload_schema_version` = `"1.0"`; + несовместимое изменение — `ex-payload-v2.schema.json` рядом, старые срезы не переписываются. +- Модель — DBML v5.26 из #207: эта ветка дописывает в неё пункты changelog (версия ещё не слита, номер не тратим). + +## 4. Что решено + +### 4.1 Конверт элемента + +Каждый `payload` (у всех `item_type`, кроме `primary_document`, у которого `payload = null` и есть `document_id`): + +``` +{schema_version: "1.0", item_type, units: "rub" | "thousand_rub" | "none", generated_at (date-time), + source_version_no (int), data: {...}} +``` + +`item_type` в конверте совпадает с колонкой; `units` — единица всех денег в `data`; `generated_at` — момент расчёта +перед записью. Конверт — `additionalProperties: false`. + +### 4.2 Данные по типам + +Общий принцип (**главнее любой таблицы ниже**): `data` — **ответ API экрана рабочей версии в момент верификации, теми же +DTO, именами и вложенностью**, без полей редактирования (`editable`, `read_only_reason`, `lock`) и плюс то, что нужно +**выгрузке xlsx рабочей версии** (срез выгружается на лету из payload и должен совпадать с выгрузкой рабочей версии — +proposal, AURORA-1562). Обязательные поля задаёт схема; лишние поля допускаются (`additionalProperties: true`), рендер +опирается только на обязательные. Ссылки на компании — как в DTO (`Ref {id, name}` или `legal_entity_id` + +`legal_entity_name`/`_inn`): имена в DTO уже есть, отдельный `CompanyRef` не вводится; список компаний периметра с ИНН — +в `version_card.members`. Id строк ОСВ, корректировок и правил остаются в данных справочно. + +| `item_type` | `units` | `data` (DTO) | +|---|---|---| +| `summary_osv` | `rub` | `{rows: ConsolidatedRowOut[], summary: OsvSummaryOut}` (`dev/api-contract-slice-4.md` §3) — строки свода, включая `match_kind` (колонка «Способ сопоставления» выгрузки), и `summary.gaps[]` (лист «Пробелы») | +| `vgo_pairs` | `rub` | `{balance: CorrectionTableOut, income: CorrectionTableOut \| null}` (`dev/api-contract-corrections.*`) — агрегаты таблицы корректировок со сторонами и строками ОСВ (`entries`, нужны выгрузке), нераспределённые строки, итоги; без `editable`/`read_only_reason`; `income` — `null`, пока агрегатов ОФР нет (AURORA-1999). Это вход КФО, не прежний экран «Пары ВГО» | +| `chess` | `rub` | `{matrix_all: IntercompanyMatrixOut, presets: [{rule_no, matrix: IntercompanyMatrixOut}], result_table}` (`dev/frontend-api-guide.md` §5.7.1) — «все счета», пресет каждого правила ББ версии правил, результирующая таблица пяти статей | +| `kfo_balance` / `kfo_income` | `thousand_rub` | `KfoResultOut` (`dev/api-contract-kfo.md`) без `editable`/`read_only_reason`, с `balance` **или** `income` своего отчёта (второй отчёт — в своём элементе); `requisites` (в т. ч. `corrections_balance`/`_income` для формы 2.03.01.01с), `summary`, `goodwill`, обоснование, `warnings` — в обоих элементах одинаковые | +| `goodwill` | `rub` | только при `goodwill_enabled`: `GoodwillTableOut` (`dev/api-contract-goodwill.*`) без `editable`/`read_only_reason` — строки, группы, итоги | +| `corrections` | `rub` | применённые корректировки (`APPLIED`; `REVOKED` нет, `DRAFT` не пишется): `{items: [{statement_type, rule_no, company_1: Ref, company_2: Ref, correction: CorrectionOut}]}` — `CorrectionOut` как в контракте корректировок (`updated_by`/`updated_at` — кто и когда сохранил) | +| `other_report` | `thousand_rub` | только при `scenario = CONFIRM`: `{report_kind: "client_kfo_comparison", lines[] {statement_type, line_code, line_name, client_amount, client_vgo_amount, bank_amount, bank_vgo_amount, deviation_amount, deviation_percent, deviation_reason, comment}}` по `client_kfo_line` (контракта API ещё нет — AURORA-1549; имена — как в DBML) | +| `version_card` | `none` | один на срез, **новый тип**: `company_group {id, name}`, `related_group {id, name}`, `reporting_date`, `year`, `quarter`, `version_no`, `scenario`, `approach`, `goodwill_enabled`, `initiator` (UserRef), `initiator_office {id, name}`, `members[]` `{legal_entity_id, name, inn, is_head_entity}`, `history[]` как `VersionCard.history` (`{from_status, to_status, changed_by (UserRef), changed_at, comment}`), `verified_by` (UserRef), `verified_at`, `uob_decision_no`, `uob_decision_date`, `methodology_deviations` | + +`UserRef {user_id, full_name}` — ФИО на дату верификации (пользователь может быть отключён, но не удаляется). + +### 4.3 Правила среза — §14 `05-domain-rules.md` + +Выжимка DOC-274687 под нашу модель: срез пишется один раз при «Верифицирован» (после фиксации ГСК, AURORA-2002), один на +версию (`source_version_id` уникален); состав — таблица 4.2 + `primary_document` по каждому документу +`consolidation_document` + `ex_snapshot_member` по каждому ЮЛ версии; строки форм без кода, непривязанные документы и +черновики корректировок не входят; `goodwill` — только при `goodwill_enabled`, `other_report` — только при `CONFIRM`; +номер среза — следующий по (ГСК, год, квартал) (уникальный индекс DBML; новая ГСК после смены периметра начинает +с 1); прежний срез запись в архив не переводит (архив ручной, решение 27.10.2026): переход в «Верифицирован» уже +проверил, что у той же **ГК** за ту же дату актуального среза нет (AURORA-2002), поэтому после записи актуальный срез по +ГК и дате ровно один — новый; архивным срезам той же ГК и даты без ссылки ставится `superseded_by_snapshot_id` нового +(ГК, а не ГСК — чтобы ссылку получил и срез прежней ГСК); разархивирование ссылку снимает (AURORA-2001). Если заказчик +выберет ключ «ГСК × дата» (вопрос AURORA-1397) — меняются проверка 2002 и это правило связи, схема payload — нет; производные считаются в +последний раз и замораживаются, `kfo_result.is_frozen = true`; сбой записи не отменяет верификацию (повтор — вопрос +заказчику, OQ); аудит записи — `EX_SNAPSHOT_WRITE`. Вопросы заказчику (кто архивирует, ключ ГК или ГСК, повтор при +сбое, PDF) — OQ со ссылкой на AURORA-1397. + +### 4.4 DBML v5.26 (дописать в changelog #207) + +- `ex_snapshot_item.item_type` — + `version_card`; `payload` — «конверт `contracts/ex-payload-v1.schema.json`, у + `primary_document` — null»; `payload_schema_version` — `"1.0"`. +- `audit_action` + `EX_SNAPSHOT_WRITE` (раздел `KFO`, `object_type = ex_snapshot`, `after` — номер среза, ГСК, дата, + число элементов). +- `ex_snapshot.status` — «ACTUAL — не архивирован; ARCHIVE — архивирован пользователем в ЕХ» (сейчас — «предыдущие → + ARCHIVE», противоречит ручному архиву). Миграция колонок не нужна; значение enum — в миграции AURORA-1560. + +### 4.5 Вложения в Jira + +После открытия PR: AURORA-1560 — схема, §14 (`05-domain-rules.md`), DBML; комментарий со ссылкой на PR. AURORA-2001 — +ссылка в комментарии (чтение — по той же схеме). + +## 5. Что остаётся исполнителю плана + +Точная разметка схемы (`$defs`, имена), тексты §14, числа примеров (из `scripts/kfo_contract/kfo_example.py`), какие +элементы показать примерами (минимум: `kfo_balance`, `version_card`, `corrections`, `summary_osv`). + +## 6. Decisions made without discussion + +| # | Дата | Решение | Обоснование | Цена ошибки | +|---|---|---|---|---| +| 1 | 06.11.2026 | `data` = замороженный ответ экрана теми же именами полей, обязательные поля — схемой, лишние допускаются | Рендер ЕХ переиспользует экраны рабочей версии; исполнителю не надо вырезать поля | Лишние поля раздувают срез; рендер, опирающийся на необязательное поле, сломается на старом срезе | +| 2 | 06.11.2026 | `vgo_pairs` — агрегаты таблицы корректировок, не прежний экран «Пары ВГО» | Это вход КФО и то, что аналитик видит в UI | Если ЕХ нужна детализация упаковки по строкам — новый тип элемента | +| 3 | 06.11.2026 | `chess` — «все счета» + пресет каждого правила ББ + результирующая таблица; ручной выбор счетов не замораживается | Ручной режим — исследование, его состояние не хранится | В ЕХ нельзя посмотреть шахматку по произвольным счетам | +| 4 | 06.11.2026 | Карточка версии — новый `item_type version_card` с ФИО, а не колонки `ex_snapshot` | DOC-274687 оставляет выбор команде; элемент не трогает схему таблицы | Фильтры ЕХ по участникам маршрута пойдут по jsonb | +| 5 | 06.11.2026 | В срез — только `APPLIED` корректировки (DOC-274687 предполагал и `REVOKED`) | `REVOKED` в системе нет: корректировку меняют пересохранением (ответ Лысенко п. 9) | История правок корректировки — только в аудите | +| 6 | 06.11.2026 | DBML-правки — в ту же неслитую v5.26, что в #207 | Одна версия на пакет КФО + ЕХ; номер не тратим | Если #207 сольют раньше и v5.26 «закроется» — перенести правки в v5.27 | +| 7 | 06.11.2026 | `EX_SNAPSHOT_WRITE` — раздел журнала `KFO` | Раздела `EX` в `audit_section` нет; запись среза — часть верификации КФО | Фильтр журнала по ЕХ не найдёт событие | +| 9 | 06.11.2026 | Payload покрывает и выгрузку xlsx рабочей версии (пробелы, способ сопоставления, строки ОСВ агрегатов, сводка корректировок), не только экран | Срез выгружается на лету из payload, выгрузка должна совпасть с рабочей (1562); ответ на вопрос проверки исполнителем 1 | Срез заметно больше (строки ОСВ агрегатов) | +| 10 | 06.11.2026 | `data` — DTO API без изменений имён и вложенности; таблица §4.2 подчинена принципу | Рендер и выгрузка ЕХ переиспользуют виджеты и выгрузки рабочей версии без маппинга; вопрос проверки 2 | Изменение DTO API после 1.0 требует новой версии схемы или совместимого расширения | +| 11 | 06.11.2026 | Нумерация среза — по ГСК; «один актуальный» и `superseded_by` — по ГК и дате | Как в DBML и DOC-274686 (решения Р-35/Р-36 по ГК); вопрос проверки 3 | При ключе «ГСК» у заказчика — правка правила связи и проверки 2002 | +| 8 | 06.11.2026 | Без отдельного согласования дизайна с владельцем: схема описана в чате 06.11 («все так»), владелец сказал «делай схему» | Срок 12.11, владелец просил не останавливаться | Расхождение с ожиданием владельца видно только в PR | diff --git a/openspec/changes/ex-payload-schema/plans/2026-11-06-ex-payload.md b/openspec/changes/ex-payload-schema/plans/2026-11-06-ex-payload.md new file mode 100644 index 0000000..905a712 --- /dev/null +++ b/openspec/changes/ex-payload-schema/plans/2026-11-06-ex-payload.md @@ -0,0 +1,69 @@ +# Схема среза ЕХ ex-payload-v1 — план + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** схема `contracts/ex-payload-v1.schema.json` с примерами и проверкой, §14 правил и правки DBML v5.26 — к старту +AURORA-1560 (12.11.2026). + +**Architecture:** JSON Schema 2020-12: конверт + `data` по `item_type` через `$defs`; DTO-определения повторяют +обязательные поля контрактов API (`additionalProperties: true` в `data`). Примеры — на числах +`scripts/kfo_contract/kfo_example.py` (КФО) и ручных данных той же группы «Альфа/Бета/Гамма». + +**Tech Stack:** JSON Schema 2020-12, `scripts/validate_contracts.py` (jsonschema), Markdown, DBML, OpenSpec. + +**Spec:** `openspec/changes/ex-payload-schema/design.md`, `proposal.md`. + +Пути — от корня worktree `/Users/andrey.epifancev/projects/awp-ca/.wt/ex-payload/docs`. + +## Global Constraints + +- `schema_version = "1.0"`; `$id` — `ex-payload-v1.schema.json`; `units ∈ {rub, thousand_rub, none}`. +- `item_type ∈ {summary_osv, vgo_pairs, chess, kfo_balance, kfo_income, goodwill, corrections, other_report, version_card}`; + у `primary_document` payload нет. +- Деньги — `^-?\d{1,16}\.\d{2}$`; конверт — `additionalProperties: false`, `data` — `true`. +- `audit_action.EX_SNAPSHOT_WRITE` (раздел `KFO`); `ex_snapshot_item.item_type` + `version_card`; пояснение `ex_snapshot.status`. +- Новые OQ — с OQ-195. + +## Review Focus + +1. Конверт с `item_type`, не совпадающим с `data` (например, `kfo_balance` с данными гудвилла) — схема отклоняет. +2. `kfo_balance` без `requisites.corrections_balance` — отклоняется (нужно форме 2.03.01.01с). +3. `goodwill` при выключенном гудвилле не пишется — правило в §14, не в схеме; пример — только включённый. +4. Деньги числом (`1000.5`) вместо строки — отклоняется. +5. Пример `kfo_balance` совпадает с примером `goodwill_exceeded` контракта КФО по строкам и сводке. + +Пункты 1, 2, 4 — отрицательные примеры в тесте Task 1; 5 — генерацией примера из того же скрипта. + +--- + +### Task 1: Схема, примеры, проверка + +**Files:** Create `contracts/ex-payload-v1.schema.json`, `contracts/examples/ex-payload-*.json` (минимум `kfo_balance`, +`version_card`, `corrections`, `summary_osv`, `vgo_pairs`, `chess`, `goodwill`), `scripts/kfo_contract/gen_ex_examples.py`; +Modify `scripts/validate_contracts.py` (+ схема `ex-payload`, шаблон `ex-payload-`), `scripts/kfo_contract/` (тест отрицательных случаев). + +- [ ] Step 1: тест `scripts/kfo_contract/test_ex_payload.py` — положительные примеры проходят; отрицательные (Review + Focus 1, 2, 4) — нет. Run: `.venv/bin/python -I scripts/kfo_contract/test_ex_payload.py` — Expected: FAIL (схемы нет). +- [ ] Step 2: схема + генератор примеров (КФО — из `kfo_example.py`/`gen_kfo_openapi.result()`) + регистрация в + `validate_contracts.py`. +- [ ] Step 3: Run тест и `make validate-contracts` — Expected: PASS, все `ex-payload-*` OK. +- [ ] Step 4: commit. + +### Task 2: §14, DBML, OQ + +**Files:** Modify `context/05-domain-rules.md` (+ §14 после §13), `model/data-model-v5.26.dbml`, `model/entities-v5.26.md`, +`context/07-open-questions.md` (OQ-195…), `context/09-delivery-plan.md:287` (ссылка на §14). + +- [ ] Step 1: Run `grep -c "EX_SNAPSHOT_WRITE\|version_card" model/data-model-v5.26.dbml` — Expected: 0 (RED). +- [ ] Step 2: §14 по design §4.3; DBML и entities по §4.4 (пункты changelog 6–8 v5.26); OQ: кто архивирует (ссылка + на 1397), ключ ГК/ГСК, повтор при сбое, PDF. +- [ ] Step 3: Run тот же grep — Expected: ≥ 2; `make check` — зелёный. +- [ ] Step 4: commit. + +### Task 3: Spec delta, ревью, PR, Jira + +- [ ] Step 1: `specs/arm-core/spec.md` (ADDED: «Срез ЕХ при верификации», «Payload элемента среза»), `tasks.md`; + `make validate-openspec CHANGE=ex-payload-schema` — valid. +- [ ] Step 2: финальное ревью (opus); Critical/Important — исправить. +- [ ] Step 3: push, PR (base `main`, «сливать после #207»), вложения в AURORA-1560 (схема, `05-domain-rules.md`, DBML), + комментарий в 1560 и 2001. diff --git a/openspec/changes/ex-payload-schema/proposal.md b/openspec/changes/ex-payload-schema/proposal.md new file mode 100644 index 0000000..23929b4 --- /dev/null +++ b/openspec/changes/ex-payload-schema/proposal.md @@ -0,0 +1,36 @@ +# Proposal: схема среза ЕХ ex-payload-v1 (docs #208) + +## Why + +AURORA-1560 (запись среза ЕХ при «Верифицирован», Потапова, разработка 12–18.11.2026, тест заказчика с 19.11) в +«Нужно до старта» называет схему замороженных данных `docs/contracts/ex-payload-v1.schema.json` — её нет, поля по типам +элементов среза не описаны. Без неё нельзя ни записать срез, ни проверить, что просмотр и выгрузка из среза совпадают с +рабочей версией (AURORA-2001 — чтение среза, AURORA-1562 — тест). + +Источники: DOC-274687 «Срез при верификации», DOC-274686 «Версии и прошлые периоды», DOC-274685 «Архив» (черновики +команды, Старовойтов); ЕХ ТЗ 5.2, 6.2–6.4; решения команды 28–29.09.2026 (самодостаточный срез, первичка ссылкой, файлы +выгрузок не хранятся, PDF не делать); DBML `ex_snapshot*`; контракт КФО (`dev/api-contract-kfo.md`, PR #207). + +## What changes + +- `contracts/ex-payload-v1.schema.json` — JSON Schema 2020-12: конверт элемента среза и данные по `item_type` + (`summary_osv`, `vgo_pairs`, `chess`, `kfo_balance`, `kfo_income`, `goodwill`, `corrections`, `other_report`, + `version_card`); примеры `contracts/examples/ex-payload-*.json` на числах сквозного примера §8 «КФО»; проверка в + `scripts/validate_contracts.py`. +- `context/05-domain-rules.md` — новый §14 «Срез ЕХ при верификации»: когда пишется, что входит, самодостаточность, + суммы и единицы, сбой записи, корректировки только `APPLIED`, карточка версии. +- DBML v5.26 (та же неслитая версия, что в #207) — `ex_snapshot_item.item_type` + `version_card`, версия схемы + `"1.0"`; `audit_action` + `EX_SNAPSHOT_WRITE`; пояснение `ex_snapshot.status` под ручной архив. +- `context/07-open-questions.md` — открытые вопросы среза. + +Срез плана: Срез 7 (`09-delivery-plan.md`), Гант 3.3. Дочерний change — `arm-core` (AURORA-1560), заводит исполнитель. +Строится на ветке #207 (форма КФО), сливается после него. + +## Non-goals + +- Код записи среза — AURORA-1560; чтение, архив и разархивирование — AURORA-2001; переход «Верифицирован» — AURORA-2002. +- Копия файлов первички в отдельный префикс S3 — AURORA-1935. +- PDF «Консолидированная отчётность» — не делаем до решения заказчика. +- Перенос в data lake и сроки хранения — страница «Архив», не схема. +- Вопросы заказчику по ЕХ (ручной или автоматический архив, ключ ГК или ГСК) — AURORA-1397 у аналитика; схема от них не + зависит.