204 lines
18 KiB
Markdown
204 lines
18 KiB
Markdown
# КФО: §8, DBML v5.26, контракт API — план
|
||
|
||
> **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:** к утру 09.11.2026 дать Цыганкову (AURORA-1546) и Степанову (AURORA-1547) правила §8 «КФО», модель v5.26 и
|
||
контракт API КФО, по которым расчёт и экран строятся параллельно.
|
||
|
||
**Architecture:** только документы репозитория `arm-ka-docs`. §8 — алгоритм; DBML — что хранится; контракт — что
|
||
отдаёт и принимает API; OpenSpec-delta — требования в форме SHALL/сценариев. Числа сквозного примера §8 и `examples`
|
||
контракта — одни и те же, считаются одним скриптом в scratchpad, чтобы не разойтись.
|
||
|
||
**Tech Stack:** Markdown, DBML, OpenAPI 3.1 (YAML), OpenSpec 1.13.1, `make check`.
|
||
|
||
**Spec:** `openspec/changes/kfo-calculation-contract/design.md`, `proposal.md`.
|
||
|
||
Все пути — от корня worktree `/Users/andrey.epifancev/projects/awp-ca/.wt/kfo-contract/docs`; команды — из него.
|
||
|
||
## Global Constraints
|
||
|
||
- Результат КФО — тыс. руб., строки с двумя знаками; ОСВ и агрегаты — рубли; float нет.
|
||
- `cleaned_amount = client_amount − vgo_amount`; `bank_amount = sum_amount − vgo_amount + goodwill_adjust` — на всех строках.
|
||
- Строки расходов, приводимые к `−|x|`: 2120, 2210, 2220, 2330, 2350.
|
||
- Новый режим правила: `min_or_full`; правило 2 seed v1: `article_2_line: "1310/1350/1370"`.
|
||
- Новые поля: `kfo_result_line_company.client_amount`, `.vgo_amount`; `kfo_result.warnings`, `corrections_count_bb`,
|
||
`corrections_count_ofr`, `corrections_amount_bb`, `corrections_amount_ofr`; `audit_action.KFO_EDIT`.
|
||
- Коды предупреждений: `FORM_MISSING`, `FORM_AMBIGUOUS`, `FORM_UNITS_UNKNOWN`, `LINE_OUTSIDE_BLANK`, `UNALLOCATED_ROWS`,
|
||
`INCOME_RULES_MISSING`, `GOODWILL_NOT_APPLIED`, `GOODWILL_ROW_REASSIGNED`.
|
||
- Эндпоинты: `GET /api/v1/consolidations/versions/{version_id}/kfo` (`KFO_VIEW`),
|
||
`PUT …/kfo/annotations` (`CONSOLIDATION_EDIT`); ошибка `UNKNOWN_LINE`; `units = "thousand_rub"`.
|
||
- Новые открытые вопросы — с OQ-180 (177 и 179 заняты открытыми PR #195 и #203).
|
||
- Версия модели — v5.26; v5.25 — PR #203 (если слит до нас — строим поверх него, см. design §3).
|
||
- `docs/source/**` не трогать. Язык — русский; маркеры решений — `R-n` с номером design-таблицы.
|
||
|
||
## Review Focus
|
||
|
||
1. Агрегат с корректировкой, у которого и автосумма > 0 — исключение ровно одно (корректировка), не сумма двух.
|
||
2. Пара гудвилла с `G ≤ 0` или выключенным `goodwill_enabled` — обычный агрегат правила 2, строка `GOODWILL` = 0.
|
||
3. Расходная строка 2120 положительной в форме одной компании и отрицательной в другой — после приведения Σ по 2120 и
|
||
итог 2100 сходятся.
|
||
4. Каскад правила 2 при нехватке остатка 1310 и 1350 — остаток целиком на 1370, Σ исключений ДО = исключению ГО 1170.
|
||
5. Версия без форм у части компаний — КФО считается, компания с нулями, `FORM_MISSING` в `warnings`.
|
||
|
||
Каждый пункт — числом в сквозном примере §8 (Task 1) и в одном из `examples` контракта (Task 3).
|
||
|
||
---
|
||
|
||
### Task 1: §8 «КФО», §8 «Корректировка», открытые вопросы
|
||
|
||
**Files:**
|
||
- Modify: `context/05-domain-rules.md` — §8: блок «КФО» заменить; блоки «Корректировка», «Гудвилл» («КФО» в конце),
|
||
«Пороги» — привести; §5 п. 1 — строка о режиме `min_or_full`.
|
||
- Modify: `context/07-open-questions.md` — OQ-180…: открытые вопросы КФО.
|
||
- Create (scratchpad, не в репо): `/private/tmp/claude-501/-Users-andrey-epifancev-projects-awp-ca/38dca83c-0de5-4c8e-b11b-50b6f660f3f7/scratchpad/kfo_example.py`
|
||
|
||
**Interfaces:**
|
||
- Produces: числа сквозного примера (компании «Альфа», «Бета», «Гамма»; таблица исключений, строки 1170, 1230, 1310,
|
||
1350, 1370, 1520, 1100, 1300, 1600, 1700, `GOODWILL`, 2110, 2120, 2100; пороги) — Task 3 копирует их в `examples`.
|
||
|
||
- [ ] **Step 1: Скрипт сквозного примера.** `kfo_example.py` на `Decimal`: три компании, агрегаты — правило 6
|
||
(«Альфа» → «Бета» в допуске 2 %; «Бета» → «Альфа» 20 % с корректировкой, у которой автосумма была бы 0, и ещё один
|
||
агрегат 6 в допуске 3 % с корректировкой, где итоги сторон 100/90 — проверка «меньшего»); правило 2 «Альфа» → «Бета»
|
||
с гудвиллом (ФВ, УК, d, P, G > 0); правило 2 «Альфа» → «Гамма» без пары на стороне 2 (`min_or_full` → `amount_1`)
|
||
с нехваткой 1310/1350 — каскад на 1370; ОФР-агрегат 2110 «Альфа» / 2120 «Бета» (у «Беты» 2120 положительное в форме);
|
||
«Гамма» без ОФР — `FORM_MISSING`. Скрипт печатает таблицы строк по компаниям и итоги, пороги ББ/ОФР.
|
||
Assert внутри скрипта: `bank(1600) == bank(1700)`; Σ исключений ДО по правилу 2 = исключению ГО 1170; Σ `vgo`
|
||
строки 2100 = 0; у агрегата с корректировкой в `vgo` нет автосуммы.
|
||
|
||
- [ ] **Step 2: Прогнать скрипт.** Run: `python3 -I <scratchpad>/kfo_example.py` — Expected: таблицы печатаются, ни
|
||
одного `AssertionError`.
|
||
|
||
- [ ] **Step 3: Переписать блок «КФО» §8** по design §4.1 п. 1–12 (единицы, строки и редакция бланка, «по данным
|
||
клиента» и знак расходов, агрегаты, таблица режимов и каскад, корректировка вместо автосуммы, `vgo` и округление,
|
||
строка и итоги, гудвилл, расхождения, пороги, реквизиты, пересчёт и заморозка, предупреждения и их хранение), со
|
||
ссылками на источники (DOC-274230, DOC-259025, DOC-272613, DOC-274636) и пометками «на старте — вариант N, решает
|
||
заказчик (OQ-nnn)». Сквозной пример — числами из Step 2. Отдельным абзацем — прогон на РБ 01.10.2024 (66н) и
|
||
ожидаемое расхождение с эталоном по OQ-155.
|
||
|
||
- [ ] **Step 4: Привести соседние блоки §8 и §5.** «Корректировка»: агрегат `min_or_full` — `eliminated_amount`,
|
||
`exceeds_threshold = false`, статусы `AUTO` / `ONE_SIDED`; одна функция автосуммы для таблицы корректировок и КФО;
|
||
«в КФО по выбранным статьям уходит меньшая сумма» — раскрыть по design п. 5. «Гудвилл», абзац «КФО» — ссылка на
|
||
новый блок, ветка по паре независимо от агрегата, `GOODWILL` в 1100/1600. «Пороги» — заменить ссылкой на новый блок
|
||
(дубли формул убрать). §5 п. 1 — `mode = min_or_full` в псевдокоде.
|
||
|
||
- [ ] **Step 5: OQ-180… в `07-open-questions.md`.** По одному вопросу на развилку design, которую решает заказчик:
|
||
строки вне бланка; состав колонок; значение без формы; итоговая строка; округление; место строки гудвилла;
|
||
совокупный объём расхождений; ЧА — 1300 или расчёт; нулевая база порога; действие при превышении; «подход» по
|
||
Методике; согласование клиентом; пересчёт автоматически или по кнопке; правило 1 автоматически (follow-up seed). В
|
||
каждом — источник, «допущение принято: вариант …», ссылка на §8.
|
||
|
||
- [ ] **Step 6: Проверка.** Run: `grep -n "eliminated_auto (Σ сумм элиминации троек" context/05-domain-rules.md` —
|
||
Expected: пусто (старая формула удалена). Run: `grep -c "OQ-18[0-9]" context/07-open-questions.md` — Expected: ≥ 10.
|
||
|
||
- [ ] **Step 7: Commit**
|
||
|
||
```bash
|
||
git add context/05-domain-rules.md context/07-open-questions.md
|
||
git commit -m "КФО: §8 под DOC-274230/259025/272613, открытые вопросы OQ-180… (#206)"
|
||
```
|
||
|
||
### Task 2: DBML v5.26 и ссылки на версию
|
||
|
||
**Files:**
|
||
- Create: `model/data-model-v5.26.dbml` (копия v5.24 или v5.25 — см. Global Constraints), `model/entities-v5.26.md`.
|
||
- Modify: `Makefile:9`, `openspec/config.yaml:4,9`, `context/00-workspace-layout.md`, `context/03-architecture.md`,
|
||
`context/04-data-model-guide.md`, `context/08-source-docs-index.md`, `context/KICKOFF-PROMPT.md`,
|
||
`repo-templates/arm-core.CLAUDE.md`, `analyst/eliminations-vgo.md` — ссылки v5.24 → v5.26, где речь о текущей версии.
|
||
- Modify (если `npx` есть): `model/diagrams/part08_corrections_kfo.{svg,png}` через `make diagrams`.
|
||
|
||
**Interfaces:**
|
||
- Consumes: формулировки полей из Task 1 (§8).
|
||
- Produces: имена колонок и enum — ровно как в Global Constraints; Task 3 на них ссылается.
|
||
|
||
- [ ] **Step 1: Скопировать модель и записать changelog v5.26** в шапку: 1) `kfo_result_line_company` + `client_amount`,
|
||
`vgo_amount`; 2) `kfo_result` + `warnings jsonb`, четыре поля сводки корректировок; 3) `audit_action.KFO_EDIT`;
|
||
4) `elimination_mode` — значение `min_or_full`; 5) пояснения `kfo_result_line` по §8 (без изменения структуры);
|
||
6) миграция — одна в arm-core, AURORA-1546, все новые колонки nullable, откат удаляет колонки, `KFO_EDIT` при
|
||
наличии событий не откатывается (как `FILE_REPROCESS` в v5.21).
|
||
|
||
- [ ] **Step 2: Правки таблиц и enum** по design §4.2 — `note` каждого поля ссылается на §8 п. N.
|
||
|
||
- [ ] **Step 3: `entities-v5.26.md`** — копия с правками разделов `kfo_result`, `kfo_result_line`,
|
||
`kfo_result_line_company`, `audit_event`, `elimination_rule`; шапка — v5.26.
|
||
|
||
- [ ] **Step 4: Ссылки на текущую версию** в файлах из списка; в `04-data-model-guide.md` — абзац «v5.26».
|
||
|
||
- [ ] **Step 5: Проверка.** Run: `grep -rn "v5\.24" Makefile openspec/config.yaml context/00-workspace-layout.md context/04-data-model-guide.md`
|
||
— Expected: только исторические упоминания («v5.24: таблица vgo_assignment»), не «текущая». Run:
|
||
`grep -n "client_amount\|KFO_EDIT\|min_or_full\|corrections_count_bb" model/data-model-v5.26.dbml` — Expected: все
|
||
четыре найдены. Если есть `npx`: `make diagrams` — Expected: без ошибок, diff `part08_*`.
|
||
|
||
- [ ] **Step 6: Commit**
|
||
|
||
```bash
|
||
git add model/ Makefile openspec/config.yaml context/ repo-templates/ analyst/
|
||
git commit -m "DBML v5.26: разрез КФО по компании, предупреждения и аудит КФО, режим min_or_full (#206)"
|
||
```
|
||
|
||
### Task 3: Контракт API КФО и правка контракта корректировок
|
||
|
||
**Files:**
|
||
- Create: `dev/api-contract-kfo.md`, `dev/api-contract-kfo.openapi.yaml`.
|
||
- Modify: `dev/api-contract-corrections.md` — агрегат `min_or_full`; `dev/frontend-api-guide.md` — строка-ссылка на
|
||
контракт КФО в оглавлении контрактов.
|
||
|
||
**Interfaces:**
|
||
- Consumes: числа Task 1 Step 2; имена полей Task 2.
|
||
- Produces: схемы `KfoResultOut`, `KfoLineOut`, `KfoLineCompanyOut`, `KfoSummaryOut`, `KfoRequisitesOut`,
|
||
`KfoWarningOut`, `KfoAnnotationsIn`, `KfoCommentIn` — их берёт 1547 как фейк и 1546 как схему ответа.
|
||
|
||
- [ ] **Step 1: `api-contract-kfo.md`** — структура как у `api-contract-goodwill.md`: 1 «Общее» (права `KFO_VIEW` /
|
||
`CONSOLIDATION_EDIT`, тыс. руб. и `units`, порядок проверок правок), 2 «Понятия» (кратко со ссылками на §8 п. N, без
|
||
повторения формул), 3 «Эндпоинты» (таблица design §4.3, `GET` и `PUT` подробно: пересчёт при чтении, заморозка,
|
||
`null` очищает, лимиты 2000/4000, `UNKNOWN_LINE`, аудит `KFO_EDIT`), 4 «Схемы» (поля с типами и смыслом), 5 «Состояния
|
||
экрана DOC-274647 → поля ответа», 6 «Примеры» — три ответа (обычный; превышение порога ББ с гудвиллом; замороженный),
|
||
сокращённые до строк сквозного примера, 7 «Открытые вопросы» — ссылки на OQ-180….
|
||
|
||
- [ ] **Step 2: `api-contract-kfo.openapi.yaml`** — OpenAPI 3.1, два пути, схемы из Interfaces, `examples` из Step 1
|
||
(полные JSON), коды ошибок как в design §4.3; деньги — `type: string, pattern: '^-?\d{1,16}\.\d{2}$'`, `nullable`
|
||
через `type: [string, "null"]`.
|
||
|
||
- [ ] **Step 3: Валидация OpenAPI.** Run:
|
||
`uv run --with openapi-spec-validator python -I -c "from openapi_spec_validator import validate; from openapi_spec_validator.readers import read_from_filename; validate(read_from_filename('dev/api-contract-kfo.openapi.yaml')[0]); print('ok')"`
|
||
— Expected: `ok`. Плюс: каждый `example` проходит свою схему — тот же запуск с `jsonschema` по
|
||
`components.schemas.KfoResultOut` (скрипт в scratchpad) — Expected: `ok` для трёх примеров.
|
||
|
||
- [ ] **Step 4: Контракт корректировок.** В `api-contract-corrections.md` §2/§3.1: для правила с `elimination_mode =
|
||
min_or_full` — `eliminated_amount` по §8, `exceeds_threshold = false`, статус `AUTO` / `ONE_SIDED`; сноска «изменение
|
||
кода — AURORA-1546». Проверить, что в `api-contract-corrections.openapi.yaml` описание поля `status` / `eliminated_amount`
|
||
не противоречит (поправить описание, не схему).
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add dev/
|
||
git commit -m "КФО: контракт API (dev/api-contract-kfo.*), агрегат min_or_full в контракте корректировок (#206)"
|
||
```
|
||
|
||
### Task 4: OpenSpec-delta, проверка, PR, вложения в Jira
|
||
|
||
**Files:**
|
||
- Create: `openspec/changes/kfo-calculation-contract/specs/arm-core/spec.md`, `openspec/changes/kfo-calculation-contract/tasks.md`.
|
||
|
||
- [ ] **Step 1: Spec delta** — `## ADDED Requirements` в capability `arm-core`: «Расчёт КФО версии» (сценарии: агрегат
|
||
с корректировкой; гудвилл G > 0 и G ≤ 0; каскад правила 2; форма отсутствует; приведение знака расходов),
|
||
«Чтение результата КФО» (пересчёт при чтении, заморозка), «Ручные поля КФО» (сохранение, `null` очищает,
|
||
`UNKNOWN_LINE`, аудит `KFO_EDIT`), «Автосумма агрегата min_or_full» (таблица корректировок и КФО совпадают).
|
||
`tasks.md` — четыре задачи этого плана с отметками.
|
||
|
||
- [ ] **Step 2: Проверка репозитория.** Run: `make validate-openspec CHANGE=kfo-calculation-contract` — Expected: valid.
|
||
Run: `make check` — Expected: все цели зелёные.
|
||
|
||
- [ ] **Step 3: Финальное ревью ветки** (сабагент opus, только чтение): §8 ↔ DBML ↔ контракт — одни имена и числа;
|
||
Review Focus 1–5 покрыты; ни одной развилки заказчика без пометки. Находки `C-n/I-n/M-n` — исправить или
|
||
`deferred` в design.md.
|
||
|
||
- [ ] **Step 4: Push и PR** — `git push -u origin feat/206-kfo-contract`; `tea pulls create --login home --repo
|
||
RSHB/arm-ka-docs --head feat/206-kfo-contract --base main` с описанием: что и зачем, Closes #206, порядок с #203,
|
||
«Rulings» (design §6), таблица находок. Merge — только по команде владельца.
|
||
|
||
- [ ] **Step 5: Вложения в Jira** — пакет `batch_create`: AURORA-1546 — `context/05-domain-rules.md`,
|
||
`model/data-model-v5.26.dbml`; AURORA-1547 — `dev/api-contract-kfo.md`, `dev/api-contract-kfo.openapi.yaml`;
|
||
в обе — комментарий голосом владельца со ссылкой на PR (что смотреть: §8 «КФО» / контракт, открытые вопросы OQ-180…).
|
||
Expected: все команды пакета `done`.
|