diff --git a/openspec/changes/kfo-calculation-contract/.openspec.yaml b/openspec/changes/kfo-calculation-contract/.openspec.yaml new file mode 100644 index 0000000..8a94d76 --- /dev/null +++ b/openspec/changes/kfo-calculation-contract/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-11-06 diff --git a/openspec/changes/kfo-calculation-contract/design.md b/openspec/changes/kfo-calculation-contract/design.md new file mode 100644 index 0000000..41e582e --- /dev/null +++ b/openspec/changes/kfo-calculation-contract/design.md @@ -0,0 +1,288 @@ +# Design: расчёт КФО — правила §8, модель v5.26, контракт API + +Change `kfo-calculation-contract`, issue RSHB/arm-ka-docs#206, ветка `feat/206-kfo-contract`. +Читатели результата — Цыганков (AURORA-1546, расчёт), Степанов (AURORA-1547, экран на фейке). Читают как постановку, +без созвона: документ должен отвечать на «что строить» сам. + +## 1. Зачем + +С 10.11.2026 в работу идут расчёт и экран КФО (Гант: разработка 10–16.11, тест заказчика 17–25.11). Сейчас у них нет +опоры: §8 описывает КФО по прежней редакции, в модели не хватает колонок по компании, контракта API нет. Цель — +к утру 09.11 дать три документа, по которым бэк и фронт стартуют параллельно и не расходятся. + +Успех: Цыганков пишет расчёт по §8 и DBML, не спрашивая «а как тут»; Степанов строит экран по контракту на фейковых +данных, и экран без правок подключается к настоящему бэку. + +## 2. Цели, не цели, анти-цели + +**Цели** +- §8 «КФО» — полный алгоритм от форм и агрегатов до строки КФО, расхождений и порогов, с примерами. +- DBML v5.26 — всё, что нужно хранить, чтобы отдать экран из `kfo_result` без пересчёта. +- Контракт API КФО — чтение результата и сохранение ручных полей, md + OpenAPI 3.1. + +**Не цели** — см. `proposal.md` «Non-goals» (код, ОФР-правила 1999, механизм пересчёта 1417, КФО Общий, подтверждение +клиента, экспорт, проверки переходов, новые поля версии). + +**Анти-цели** (провал, даже если документы формально готовы) +- Решения заказчика выданы за принятые. Каждая развилка из «Что решить» DOC-274636 помечается: «на старте — вариант N, + решает заказчик». +- Сумма исключается дважды (автосумма + корректировка, обычный ББ-2 + гудвилл). +- Контракт, который экран не может отрисовать без своих расчётов: фронт не считает ни долей, ни итогов, ни порогов. +- Код роли или номер правила в бизнес-логике: поведение правила задаётся данными (`elimination_mode`), не `rule_no`. + +## 3. Ограничения + +- Источник истины по полям — DBML; полей, которых нет в v5.26, в §8 и контракте нет. +- Деньги — `numeric(18,2)`, в API — строки с двумя знаками; float нет. +- Производные консолидации не хранятся (DECISION-5); `kfo_result(_line, _line_company)` — хранимый результат версии, + это разрешено моделью; агрегаты ВГО по-прежнему считаются на запрос. +- Соглашения API — как в `dev/api-contract-corrections.md` §1 и `dev/api-contract-goodwill.md` §1 (префикс, права, + 404 вне видимости, формат ошибок, порядок проверок правок, `editable` / `read_only_reason`). +- **Нумерация DBML.** В docs открыт PR #203 (v5.25, метрики OCR, AURORA-1759). Наша версия — v5.26. Если #203 слит + раньше нашего PR — строим v5.26 поверх v5.25. Если позже — v5.26 строим от v5.24 и в шапке пишем «v5.25 — PR #203»; + второй слитый PR перед merge вливает изменения первого в свой файл (номера не переписываются задним числом). +- Язык — русский, идентификаторы — английские. + +## 4. Что решено + +### 4.1 §8 «КФО» — содержание + +Блок «КФО» §8 заменяется целиком; блоки «Корректировка», «Гудвилл», «Пороги», «Сравнение с КФО клиента» правятся +только там, где противоречат новому блоку. + +**Единицы.** Результат КФО хранится и отдаётся в **тыс. руб.** с двумя знаками. Суммы ОСВ и агрегатов — в рублях, +переводятся в тысячи один раз на ячейке «в т. ч. взаимные» (компания × строка, см. «Округление»). + +**1. Строки.** Бланк ББ и ОФР — полный вариант (`full`) той редакции, которую ядро выбирает по **отчётной дате версии** +(как §11.9 и словарь имён ядра: ≤ 31.12.2024 — `66n`, позже — `fsbu4`; `contracts/statement-code-orders.yaml`), +наименования — словарь имён ядра. Для «Русского бисквита» на 01.10.2024 это 66н (1120, 1231, 2930 есть, 1105, 1215, +2420 нет). Плюс служебная строка `GOODWILL` (п. 8). Все строки бланка выводятся, даже нулевые. +Строки форм компаний вне бланка в КФО не входят (предупреждение `LINE_OUTSIDE_BLANK`), строки без кода — тоже +(решение 17.10.2026). На старте — вариант черновика (DOC-274636 вопрос 1): только бланк + гудвилл; решает заказчик. + +**2. «По данным клиента» (`client_amount`).** По каждой компании периметра (`consolidation_member`) — подтверждённый +(`CONFIRMED`) документ ББ / ОФР, привязанный к версии (`consolidation_document`). Колонка: ББ — на конец отчётного +квартала, ОФР — за период с начала года по конец квартала (по дате колонки, не по `is_current`; так же, как УК +гудвилла). Строки со статусом `ok`, повторы кода суммируются. Единицы формы — словарём единиц `arm_statements` → +тыс. руб.: «тыс. руб.» как есть, «руб.» / 1000, «млн руб.» × 1000, до двух знаков `ROUND_HALF_UP`. +**Знак строк расходов.** Формы разных компаний могут хранить расходы по-разному (§7, §11.9: скобки, минус или «без +знака»). КФО приводит строки расходов бланка — 2120, 2210, 2220, 2330, 2350 — к одной конвенции: `client_amount = +−|значение|`. Остальные строки — как в форме (1370, 2300, 2400 и прочие могут быть любого знака по существу). +- нет формы → значения компании 0, предупреждение `FORM_MISSING` (вариант 1 вопроса 3 черновика; решает заказчик); +- две и более подтверждённые формы одного типа → 0, `FORM_AMBIGUOUS`; +- единицы не распознаны → 0, `FORM_UNITS_UNKNOWN`. +КФО считается и при неполном комплекте (вопрос 17 черновика: вариант «считать, предупредить»). + +**3. Агрегаты.** Источник взаимных сумм — агрегаты таблицы корректировок (§5 п. 2a, `VgoService`): тройка (правило, +компания 1, компания 2) с суммами сторон `amount_1`, `amount_2` (рубли) после ручных решений `vgo_assignment`. +Нераспределённые строки ни в один агрегат не входят и в КФО не исключаются (предупреждение `UNALLOCATED_ROWS` с числом +строк без отметки «не учитывать в КФО»). Тип отчётности агрегата — из правила. Отдельный обход ОСВ в КФО не делается: +то, что видит аналитик в таблице корректировок, и есть вход КФО. + +**4. Исключения агрегата.** Каждый агрегат даёт список исключений `(компания, код строки, сумма в рублях ≥ 0)`. +Если у агрегата есть применённая корректировка (`correction.status = APPLIED`) — п. 5, иначе автосумма по режиму +правила (`elimination_rule.elimination_mode`): + +| Режим | Автосумма | Куда | +|---|---|---| +| `min` (ББ 3–8) | `min(amount_1, amount_2)`, если обе стороны > 0 и отклонение в допуске (§8 «Корректировка», порог как в коде, OQ-178); иначе 0 | `article_1_line` у компании 1, `article_2_line` у компании 2 | +| `min_or_full` (ББ 2, новое) | обе стороны > 0 → `min(amount_1, amount_2)`; сторона 2 = 0 → `amount_1`; сторона 1 = 0 → 0. Порога нет (DOC-274230 правило 2) | компания 1 — `article_1_line`; компания 2 — каскад по кодам `article_2_line` | +| `manual` (ББ 1) | 0 — только корректировка | — | +| режимы ОФР | по правилам AURORA-1999 (§5); до неё агрегатов ОФР нет | по правилу | + +**Каскад** (составная статья «a/b/c»): сумма снимается со строки `a` компании в пределах её доступного остатка +(«по данным клиента» строки в рублях минус уже снятое с неё другими агрегатами, но не ниже 0), остаток — со строки +`b`, затем `c`; что не уместилось — целиком на последнюю строку (DOC-274230: «если остаётся положительный остаток, он +исключается из строки 1370»). Агрегаты обходятся в порядке `rule_no`, затем ИНН компании 1, затем ИНН компании 2 — +порядок детерминирован. Каскад применяется и к составной статье в корректировке, если в ней код с «/» (в коде +корректировки код один, поэтому на практике — только автосумма правила 2). + +Изменения seed правил ББ (в AURORA-1546, `arm-core/api/seed/core/elimination_rules_v1.yaml`): правило 2 — +`elimination_mode: min_or_full`, `article_2_line: "1310/1350/1370"`; правка версии 1 на месте, без новой версии +правил (п. 6 «Decisions»). + +**Автосумма — одна функция для таблицы корректировок и КФО.** Сейчас `correction_source.py` знает только `manual` и +«остальное как `min`»; с `min_or_full` таблица корректировок показала бы правило 2 с порогом 5 % и односторонний +агрегат нулём, а КФО исключил бы иначе. Поэтому этот PR правит и §8 «Корректировка», и `dev/api-contract-corrections.md`: +у агрегата `min_or_full` `eliminated_amount` = автосумма по таблице выше, `exceeds_threshold = false` всегда, статус — +`AUTO` при обеих сторонах, `ONE_SIDED` с `eliminated_amount = amount_1` при пустой стороне 2. Код — в AURORA-1546: +автосумма агрегата выносится в одну функцию домена, её вызывают и `correction_source`, и расчёт КФО. Правило 1 остаётся `manual`: по DOC-274230 обе его суммы — у компании 2 (Кт 76.7.1 − Дт +76.7.9), а сторона агрегата устроена «компания 1 — дебет, компания 2 — кредит»; автосумма правила 1 — после +переработки seed (follow-up), сейчас только корректировкой. + +**5. Корректировка агрегата заменяет автосумму.** Применённая корректировка агрегата отменяет его автосумму целиком +и не прибавляется к ней (DOC-274636 допущение 4; DOC-272613: сохранённые корректировки не меняют суммы сторон). +Исключения: компания 1 — статьи `debit[]` (основная + дочерние), компания 2 — `credit[]`, суммы как введены. Если +заполнены обе стороны и итоги сторон разные, каждая сторона исключает **меньший** итог (ответ Лысенко п. 8, §8 +«Корректировка»): снимается со статей стороны по порядку — основная, затем дочерние в порядке ввода — до исчерпания. +Пустая сторона ничего не исключает, а другая — как введена (одностороннее правило 1, ОФР 1/2/3). + +**6. «В т. ч. взаимные» по компании (`vgo_amount`).** Σ исключений компании по строке в рублях → тыс. руб., до целых +тысяч, половина — от нуля (`ROUND_HALF_UP` по модулю), со **знаком строки**: строки расходов (2120, 2210, 2220, 2330, +2350; после приведения п. 2 они ≤ 0) получают отрицательное `vgo_amount`. Так на всех строках +одно равенство: `cleaned_amount = client_amount − vgo_amount`, исключение всегда приближает значение к нулю (при +превышении — проходит через ноль). На старте — вариант 1 вопроса 8 черновика (округление ячейки); решает заказчик. + +**7. Строка КФО.** По компании: `client_amount`, `vgo_amount`, `cleaned_amount`, `share_percent = cleaned / +bank_amount × 100` (хранится 4 знака, отдаётся 2; `bank_amount = 0` → `null`). По строке: `sum_amount = Σ client`, +`vgo_amount = Σ vgo по компаниям` (= `eliminated_auto + eliminated_manual`, где auto — от агрегатов без корректировки, +manual — от агрегатов с корректировкой; гудвилл-пары — в auto), `goodwill_adjust` (у `GOODWILL` и у итогов, куда он +входит), `bank_amount = sum_amount − vgo_amount + goodwill_adjust`. + +**Итоговые строки** (1100, 1200, 1300, 1400, 1500, 1600, 1700, 2100, 2200, 2300, 2400, 2500): «по данным клиента» — итог +из формы компании, «в т. ч. взаимные» — Σ `vgo_amount` слагаемых по формуле бланка с их знаками (1100 ← 1110…1190; +1600 ← 1100 + 1200; 1700 ← 1300 + 1400 + 1500; 2100 ← 2110 + 2120; 2200 ← 2100 + 2210 + 2220; дальше по бланку); +очищенные — разность. **Гудвилл в итогах:** `goodwill_adjust` итоговой строки = Σ `goodwill_adjust` её слагаемых, то +есть `G` у 1100 и 1600; разреза по компаниям у этой части нет, поэтому `bank_amount` итога = Σ очищенных компаний + +`goodwill_adjust`, а доли компаний у 1100/1600 считаются от `bank_amount` и в сумме дают меньше 100 % на долю гудвилла. +Проверка сходимости: `bank(1600) = bank(1700)`, если сходятся формы компаний (у ГО −ФВ, у ДО −P, гудвилл +G, ФВ − P = G). Итог КФО совпадает с подписанным клиентом минус взаимные, а не с суммой своих строк +(вариант 2 вопроса 7 черновика; решает заказчик). Пример: 10 млн исключены из 2110 «Альфы» и 2120 «Беты» — у 2100 +каждой компании `vgo` ±10 000, у строки 2100 КФО Σ `vgo` = 0, валовая прибыль группы не меняется. + +**8. Гудвилл** (DOC-259025, §8 «Гудвилл»). Ветка гудвилла идёт **по паре ГО → ДО таблицы гудвилла, независимо от +агрегатов**: для пары с `use_goodwill` (действующая, отмечена, доля и комментарий, `G > 0`) при `goodwill_enabled` у +версии — исключения ГО `1170` на ФВ (как в таблице гудвилла — все строки 58.01 и потомков пары), ДО `1310` на P (без +каскада), `G` — в строку `GOODWILL`; агрегат правила 2 той же пары (компания 1 = ГО, компания 2 = ДО), если он есть, +**не применяется** — ни автосумма, ни корректировка. Если строку 58.01 этой пары аналитик перенёс в другое правило +или отметил «не учитывать», она всё равно входит в ФВ — предупреждение `GOODWILL_ROW_REASSIGNED`: ВГО по ней могут +исключиться дважды, решает аналитик. При `G ≤ 0` или выключенном гудвилле — обычный агрегат (п. 4–5). Строка `GOODWILL` — +раздел I, сразу после 1170, без разреза по компаниям, `goodwill_adjust = bank_amount = Σ G` применимых пар в тыс. руб. +(до целых тысяч); входит в итоги 1100 и 1600; `kfo_result.goodwill_total` — то же значение. Место строки: по DOC-259025 +(R-1) — отдельная строка раздела I; макет `[31-04]` показывает «В т.ч. Гудвилл» под 1170 — расхождение в открытых +вопросах, решают методологи. + +**9. Расхождения по статьям.** По агрегатам режима `min` (ББ и ОФР): `|amount_1 − amount_2|` — на статью стороны с +большей суммой (компания 1 — `article_1_line`, компания 2 — `article_2_line`), независимо от корректировок (вариант 1 +вопроса 10 черновика; решает заказчик). Агрегаты `manual`, `min_or_full` и гудвилл-пары в расхождения не входят. По +строке — Σ в рублях → тыс. руб. до целых. `total_deviation_bb` / `total_deviation_ofr` — Σ по строкам своего отчёта. +`has_deviations` — хотя бы одна строка ≠ 0. + +**10. Пороги** (ТЗ 6.8, DOC-274636 правило 8; величины — КФО банка, тыс. руб.): +- ББ: ЧА = строка 1300 (вопрос 11, вариант FNR03), ВБ = 1600. `ЧА ≥ 10 % ВБ` → база ЧА, предел 10 % ЧА; иначе (в том + числе ЧА ≤ 0) → база ВБ, предел 1 % ВБ. +- ОФР: `2200 ≥ 0` и `2200 / 2110 ≥ 10 %` → база 2200, предел 10 % 2200; иначе → база 2110, предел 1 % 2110. `2110 = 0` + → база 2110, предел 0. +- Превышение — `total_deviation > предел` (равно — норма). Процент расхождения от базы — для сводки; база = 0 → `null`. +- Превышение хотя бы по одному отчёту → нужно обоснование (`threshold_justification`). Обязательность перед отправкой + на верификацию (вариант 2 вопроса 13) проверяет переход статуса — AURORA-2002 / 1414, не КФО. + +**11. Реквизиты формы** (ТЗ 6.4, DOC-274636 правило 9): ГК, ГСК, отчётная дата, номер и статус версии, сценарий, +подход (`approach` — пакет документов), гудвилл учтён да/нет и сумма, расхождения ББ/ОФР и превышения, обоснование, +корректировки (число агрегатов с применённой корректировкой и Σ их исключений по типу отчёта), причины отклонений от +Методики (`consolidation_version.methodology_deviations`, ввод на вкладке), решение УОБ (номер, дата — только чтение). +«Подход по Методике» и «согласовано клиентом» — не делаем до ответа заказчика (вопросы 14, 15). + +**12. Пересчёт и заморозка.** Результат хранится у версии (`kfo_result`, одна запись). Пока версия не заморожена, +чтение отдаёт результат по текущим данным версии: если входы изменились после `computed_at`, сервер пересчитывает и +сохраняет до ответа (как обнаруживать изменения — AURORA-1417). Кнопки «Рассчитать» нет (вариант 1 вопроса 16). +Комментарии строк и обоснование переносятся при пересчёте по (тип отчёта, код строки). При «Верифицирован» +`is_frozen = true` (ставит AURORA-2002) — дальше результат только читается. + +**Предупреждения** — `{code, message, legal_entity_id?, statement_type?, count?}`, не блокируют расчёт: `FORM_MISSING`, +`FORM_AMBIGUOUS`, `FORM_UNITS_UNKNOWN`, `LINE_OUTSIDE_BLANK`, `UNALLOCATED_ROWS`, `INCOME_RULES_MISSING` (до 1999 — +агрегатов ОФР нет, взаимные обороты = 0), `GOODWILL_NOT_APPLIED` (гудвилл включён, а применимых пар нет), +`GOODWILL_ROW_REASSIGNED` (п. 8). Предупреждения и сводка корректировок (п. 11) — часть результата: **хранятся в +`kfo_result`** и пишутся при расчёте, чтобы замороженная версия отдавала ровно то, что видели при верификации. + +§8 содержит сквозной пример: две компании, правила 2 (с гудвиллом и без), 6 (в допуске и с корректировкой), ОФР-пара +2110/2120, итоги 1100/1600/2100, пороги — числа сходятся до тысячи. Плюс прогон на «Русском бисквите» 01.10.2024 +(эталон `Консолидация_Группа РБ.xlsx`) — как критерий 1546, с известными расхождениями: алгоритм по ТЗ (min, порог 5 %), +а эталон исключает каждую сторону полностью (OQ-155). + +### 4.2 DBML v5.26 + +- `kfo_result_line_company`: **+ `client_amount numeric(18,2)`** («по данным клиента», тыс. руб.), + **+ `vgo_amount numeric(18,2)`** («в т. ч. взаимные», со знаком строки); `amount` — пояснение «очищенные показатели»; + `share_percent numeric(7,4)` — «4 знака хранения, в API 2». Строка `GOODWILL` разреза не имеет. +- `kfo_result_line`: пояснения по §8 — единицы тыс. руб.; `sum_amount` = Σ client; `eliminated_auto` / `eliminated_manual` + — от агрегатов без / с корректировкой, **не складываются по одному агрегату**; `vgo_amount = auto + manual`; + `goodwill_adjust` — только у `GOODWILL`; `consolidated_amount = sum − vgo + goodwill_adjust`; `deviation_amount` — + п. 9; `comment` ≤ 2000, переживает пересчёт; `source_kind` — всегда `balance` (ОСВ как источник — вопрос 3). +- `kfo_result`: пояснения порогов по п. 10, `goodwill_total` по п. 8, `is_frozen` — ставит переход «Верифицирован». +- `kfo_result`: **+ `warnings jsonb`** (массив предупреждений расчёта, п. 12), **+ `corrections_count_bb int`, + `corrections_count_ofr int`, `corrections_amount_bb numeric(18,2)`, `corrections_amount_ofr numeric(18,2)`** (число + агрегатов с применённой корректировкой и Σ их исключений в тыс. руб., п. 11). +- `audit_action`: **+ `KFO_EDIT`** — сохранение ручных полей КФО (комментарии строк, обоснование порога, причины + отклонений от Методики) одним событием на запрос, `object_type = kfo_result`, `before` / `after` по изменённым + полям; раздел — тот же, что у корректировок и гудвилла (`CONSOLIDATION`). Миграция enum — в 1546. +- `elimination_rule.elimination_mode`: в пояснение — значение `min_or_full` (varchar, миграция схемы не нужна). +- Шапка — changelog v5.26; `entities-v5.26.md` — копия с теми же правками; ссылки «v5.24» в `CLAUDE.md` workspace и + `openspec/config.yaml` — на v5.26. +- Миграция `arm-core` (две колонки, nullable) — в AURORA-1546. + +Разреза «расхождение по компании» не добавляем: на экране «Расхождения» — общая колонка строки. + +### 4.3 Контракт API (`dev/api-contract-kfo.md` + `.openapi.yaml`) + +Префикс `/api/v1/consolidations/versions/{version_id}`. + +| Метод и путь | Право | Ответ | Ошибки | +|---|---|---|---| +| `GET …/kfo` | `KFO_VIEW` | 200 `KfoResultOut` | 404 `NOT_FOUND` | +| `PUT …/kfo/annotations` | `CONSOLIDATION_EDIT` | 200 `KfoResultOut` | 404, 409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER`, 422 `UNKNOWN_LINE`, 422 pydantic | + +Чтение — по `KFO_VIEW` (право есть в seed и означает ровно это), не по `CONSOLIDATION_VIEW`, как корректировки и +гудвилл; правка — `CONSOLIDATION_EDIT` (`KFO_EDIT` в seed нет). + +`KfoResultOut`: +- `version_id`, `computed_at`, `is_frozen`, `editable`, `read_only_reason`, `units = "thousand_rub"`; +- `companies[]` — `{legal_entity_id, name, inn}` в порядке колонок (по имени); +- `requisites` — п. 11; +- `summary.balance` / `summary.income` — `{deviation_total, base_kind (NET_ASSETS | BALANCE_TOTAL | SALES_PROFIT | + REVENUE), base_amount, limit_percent, limit_amount, deviation_percent, exceeded}`; +- `justification_required`, `threshold_justification`; +- `goodwill` — `{enabled, total}`; +- `balance.lines[]`, `income.lines[]` — `KfoLineOut`: `line_code`, `line_name`, `ordinal`, `is_total`, + `is_threshold_base` (подсветка строки базы при превышении), `companies[]` `{legal_entity_id, client_amount, + vgo_amount, cleaned_amount, share_percent}` (у `GOODWILL` — пусто), `sum_amount`, `vgo_amount`, `eliminated_auto`, + `eliminated_manual`, `goodwill_adjust`, `bank_amount`, `deviation_amount`, `comment`; +- `warnings[]`. + +Деньги — строки с двумя знаками **в тыс. руб.** (в отличие от корректировок и гудвилла, где рубли: результат КФО по +определению в тысячах; поле `units` явно). Переключатели «Единицы» (тыс. / млн) и «Знаков после запятой» — только показ +на фронте. + +`PUT …/kfo/annotations` — тело `{comments?: [{statement_type, line_code, comment | null}], threshold_justification?, +methodology_deviations?}`; отсутствующее поле не меняется, `null` — очищает; комментарий ≤ 2000, обоснование и причины +≤ 4000; строки нет в бланке — 422 `UNKNOWN_LINE`. Сохраняет набором в одной транзакции, аудит — `KFO_EDIT` (4.2). +Ответ — полный `KfoResultOut`. + +Состояния экрана из DOC-274647 выражаются полями ответа: нет компаний / нет форм — `warnings`; заморожено — `is_frozen` ++ `editable = false`. Примеры ответа (обычный, с превышением и гудвиллом, замороженный) — в md и в `examples` +OpenAPI, на числах сквозного примера §8; Степанов берёт их как фейк. + +### 4.4 Вложения в Jira + +После открытия PR (не дожидаясь merge): AURORA-1546 — `05-domain-rules.md` (§8) и `data-model-v5.26.dbml`; +AURORA-1547 — `api-contract-kfo.md` и `api-contract-kfo.openapi.yaml`; в обеих — комментарий со ссылкой на PR. После +правок по ревью — новые версии вложений. + +## 5. Что остаётся исполнителю плана + +Точный перечень и порядок строк бланка (из словаря ядра и `statement-code-orders.yaml`), тексты предупреждений, +формулировки §8, разметка OpenAPI (имена схем, `examples`), числа сквозного примера, расстановка открытых вопросов в +`07-open-questions.md` (новые OQ с номерами по порядку). + +## 6. Decisions made without discussion + +| # | Дата | Решение | Обоснование | Цена ошибки | +|---|---|---|---|---| +| 1 | 06.11.2026 | Развилки «решает заказчик» из DOC-274636 закрываются вариантом черновика как стартовым, с пометкой | Раскладка спринта 4, R-2; иначе 1546 не стартует | Доработка по ответу заказчика | +| 2 | 06.11.2026 | Вход КФО — агрегаты таблицы корректировок (§5 п. 2a), не отдельный обход ОСВ | Одна истина для аналитика и КФО; двойной учёт уже снят разнесением | Если заказчик захочет КФО «мимо» разнесения — переделка источника | +| 3 | 06.11.2026 | Корректировка агрегата заменяет автосумму; при разных итогах сторон каждая исключает меньший | DOC-274636 доп. 4, ответ Лысенко п. 8 | Неверная сумма ВГО на агрегатах с корректировкой | +| 4 | 06.11.2026 | Правило 2 считается автоматически по DOC-274230 (режим `min_or_full`, каскад 1310 → 1350 → 1370) правкой seed версии 1 на месте | Описание 1546 (владелец, 06.11) требует каскада; верифицированных версий нет — объяснимость старых результатов не страдает | Версии, созданные до правки, начнут считать правило 2 по-новому; если где-то это недопустимо — нужна версия правил 2 | +| 5 | 06.11.2026 | Правило 1 остаётся ручным (только корректировка) | Обе суммы DOC-274230 — у компании 2, сторона агрегата устроена иначе; переделка seed не влезает | ВГО по 76.7.x не исключаются без корректировки аналитика | +| 6 | 06.11.2026 | Результат КФО в API — в тыс. руб. (поле `units`), не в рублях, как в других контрактах | Округление до тысяч — часть расчёта; рубли из результата не восстановить | Фронт, привыкший к рублям, неверно масштабирует — снимается полем `units` | +| 7 | 06.11.2026 | `vgo_amount` со знаком строки; `cleaned = client − vgo` на всех строках | Расходы в формах с минусом; одно равенство без исключений для экрана и итогов | Если формы хранят расходы положительными — знак у 2120/2210/… перевернётся (проверить на фикстурах в 1546) | +| 8 | 06.11.2026 | Строка `GOODWILL` — отдельная строка раздела I после 1170, без разреза по компаниям, входит в 1100/1600 | DOC-259025 важнее макета (R-1); разрез по компаниям гудвиллу не определён | Если методологи выберут «в т. ч. под 1170» — перенос строки и пересчёт итогов | +| 9 | 06.11.2026 | Чтение пересчитывает устаревший результат сам; кнопки «Рассчитать» нет | Вариант 1 вопроса 16; фронту не нужно знать про устаревание | Долгий GET на больших версиях — оптимизация в 1417 | +| 10 | 06.11.2026 | Разреза «расхождение по компании» в модели нет (в чате обещал добавить) | На экране «Расхождения» — общая колонка строки | Понадобится расшифровка расхождения по компании — новая колонка | +| 11 | 06.11.2026 | Порядок обхода агрегатов для каскада — `rule_no`, ИНН компании 1, ИНН компании 2 | Детерминированный результат при нескольких агрегатах на одну строку | Иное распределение между 1310/1350/1370 при нехватке остатка | +| 12 | 06.11.2026 | Гудвилл в итогах — `goodwill_adjust` у 1100/1600 = Σ слагаемых (вопрос ревью 1, вариант «а») | Одна формула `bank = sum − vgo + goodwill_adjust` на всех строках; 1600 сходится с 1700 | Доли компаний у 1100/1600 в сумме < 100 % — если смутит заказчика, показывать долю гудвилла отдельно | +| 13 | 06.11.2026 | Строки расходов 2120, 2210, 2220, 2330, 2350 приводятся к `−|x|` независимо от знака в форме | Формы хранят знак по-разному (§7, §11.9); без единой конвенции Σ по компаниям и итоги неверны | Законно положительное значение строки расходов (сторно) станет отрицательным | +| 14 | 06.11.2026 | Автосумма агрегата — одна функция для таблицы корректировок и КФО; PR правит §8 «Корректировка» и контракт корректировок под `min_or_full` | Иначе аналитик видит одно, а КФО исключает другое | Шире PR и 1546 (правка `correction_source.py`) | +| 15 | 06.11.2026 | Предупреждения и сводка корректировок хранятся в `kfo_result` (`warnings jsonb`, 4 колонки) | Замороженная версия должна отдавать то, что видели при верификации | Лишние колонки, если ЕХ всё равно заморозит ответ целиком в срез | +| 16 | 06.11.2026 | Аудит ручных полей КФО — новое действие `KFO_EDIT` | В `audit_action` подходящего нет; инвариант «append-only аудит» | Если заказчику нужен отдельный раздел журнала — новое значение раздела | +| 17 | 06.11.2026 | Бланк — по редакции на отчётную дату версии (66н ≤ 2024, ФСБУ 4 позже) | Иначе КФО за 2024 теряет статьи 66н; так же выбирает редакцию ядро | Версия на стыке редакций с формами разных редакций — строки вне бланка в предупреждения | +| 18 | 06.11.2026 | Гудвилл-ветка — по паре таблицы гудвилла, независимо от агрегатов; агрегат правила 2 пары подавляется; перенос строки 58.01 — предупреждение | ФВ гудвилла уже определён по всем строкам 58.01 пары (контракт гудвилла) | Двойное исключение строки, перенесённой аналитиком в другое правило, — видно по предупреждению | +| 19 | 06.11.2026 | Чтение КФО — по `KFO_VIEW`, правка — по `CONSOLIDATION_EDIT` | `KFO_VIEW` в seed есть и означает ровно это; `KFO_EDIT` нет | Роль с `CONSOLIDATION_VIEW` без `KFO_VIEW` не увидит вкладку — так и задумано матрицей | diff --git a/openspec/changes/kfo-calculation-contract/proposal.md b/openspec/changes/kfo-calculation-contract/proposal.md new file mode 100644 index 0000000..0b2f4f2 --- /dev/null +++ b/openspec/changes/kfo-calculation-contract/proposal.md @@ -0,0 +1,48 @@ +# Proposal: расчёт КФО — правила §8, модель v5.26, контракт API (docs #206) + +## Why + +Разработка КФО стартует 10.11.2026: AURORA-1546 (расчёт, Цыганков) и AURORA-1547 (экран, Степанов, на фейковых данных +по контракту). Обе задачи в «Нужно до старта» ссылаются на три документа, которых нет или которые устарели: + +- §8 «КФО» в `context/05-domain-rules.md` написан по прежней редакции: корректировки складываются с автоэлиминацией, + нет распределения по строкам правил 1 и 2, нет ветки гудвилла в расчёте, нет единиц и округления; +- в модели `kfo_result_line_company` нет «по данным клиента» и «в т. ч. взаимные» по компании — колонки экрана из + сохранённого результата не восстановить; +- контракта API КФО нет совсем. + +Источники: страницы Лысенко — DOC-274230 «Правила пар счетов» (27.10), DOC-259025 «Гудвилл» (03.11), DOC-272613 +«Корректировка данных агрегатом» (04.11); черновики команды — DOC-274636 «Расчёт КФО», DOC-274647 «Экран: КФО»; +макет `[31-04]`. При расхождении страница Лысенко важнее черновика (решение владельца 28.10.2026, распространено на КФО +в раскладке спринта 4, R-1). + +## What changes + +- `context/05-domain-rules.md` §8, блок «КФО» — переписан: источники значений, порядок расчёта, автосумма агрегата по + каждому правилу ББ, распределение по строкам, корректировка агрегата вместо автосуммы, гудвилл, ОФР, итоговые строки, + единицы и округление, расхождения и пороги, реквизиты, пересчёт и заморозка, предупреждения. +- `model/data-model-v5.26.dbml` + `model/entities-v5.26.md` — `kfo_result_line_company` получает `client_amount` + и `vgo_amount`; пояснения `kfo_result_line` (`vgo_amount`, `eliminated_manual`, `GOODWILL`, единицы) и `kfo_result` + приведены к §8; changelog в шапке. +- `dev/api-contract-kfo.md` + `dev/api-contract-kfo.openapi.yaml` — новый контракт: чтение результата КФО версии, + сохранение ручных полей (комментарии строк, обоснование порога, причины отклонений от Методики). +- §8 «Корректировка» и `dev/api-contract-corrections.md` — автосумма агрегата нового режима `min_or_full` (правило 2): + таблица корректировок и КФО считают её одной функцией. +- DBML v5.26 также: `kfo_result.warnings` и сводка корректировок, `audit_action.KFO_EDIT`, пояснение `elimination_mode`. +- `context/07-open-questions.md` — открытые вопросы КФО с вариантом, принятым на старте. + +Срез плана: Срез 6 (`09-delivery-plan.md`), Гант 2.5 / 2.8. Дочерние change'и — в `arm-core` (AURORA-1546, 1547, +1417), заводят исполнители. + +Схема среза ЕХ `ex-payload-v1` — отдельный change и PR (под AURORA-1560), строится на форме ответа этого контракта. + +## Non-goals + +- Код расчёта и экрана — AURORA-1546, 1547 в `arm-core`. +- Правила пар ОФР как данные и их расчёт — AURORA-1999 (§5); здесь только как КФО читает агрегаты ОФР. +- Механизм пересчёта и инвалидации (события, триггеры) — AURORA-1417; здесь только наблюдаемое поведение для экрана. +- «КФО Общий» (динамика), «Подтверждение КФО клиента», выгрузка формы 2.03.01.01с — AURORA-2003, 1549/1550, 1565. +- Проверки перед переходами статусов (обязательность обоснования при отправке на верификацию) — AURORA-2002 / 1414; + здесь только признак «нужно обоснование». +- Новые поля версии под «подход по Методике» и «согласовано клиентом» — ждут ответа заказчика (DOC-274636, вопросы 14, 15). +- Схема `ex-payload-v1` — следующий change.