ЕХ: дизайн и план change ex-payload-schema (#208)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b6384a5ad3
commit
9086c064e4
2
openspec/changes/ex-payload-schema/.openspec.yaml
Normal file
2
openspec/changes/ex-payload-schema/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-11-06
|
||||
125
openspec/changes/ex-payload-schema/design.md
Normal file
125
openspec/changes/ex-payload-schema/design.md
Normal file
@ -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 |
|
||||
@ -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.
|
||||
36
openspec/changes/ex-payload-schema/proposal.md
Normal file
36
openspec/changes/ex-payload-schema/proposal.md
Normal file
@ -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 у аналитика; схема от них не
|
||||
зависит.
|
||||
Loading…
x
Reference in New Issue
Block a user