# КФО: §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 /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`.