300 lines
43 KiB
Markdown
300 lines
43 KiB
Markdown
# 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` и итогов 1100/1600 (решение #12); `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` по изменённым
|
||
полям; раздел — `KFO` (в `audit_section` он есть; исполнение плана, R-1 ledger). Миграция 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 — две в `kfo_result_line_company`, пять в `kfo_result` — и значение
|
||
`audit_action`) — в 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` не увидит вкладку — так и задумано матрицей |
|
||
| 20 | 06.11.2026 | У итоговых строк `deviation_amount = null` | Сумма расхождений актива и пассива в итоге бессмысленна; сводка считается по строкам бланка | Экрану нужна сумма раздела — добавить на фронте (ledger, Task 1) |
|
||
| 21 | 06.11.2026 | Примеры контракта: гудвилл + превышение, гудвилл выключен, заморожено; примера «норма» нет | Все три — одна версия сквозного примера, в ней превышение | У фронта нет зелёной сводки в фейке — подменить `exceeded` руками (ledger, Task 1) |
|
||
| 22 | 06.11.2026 | `KFO_EDIT` — раздел журнала `KFO`, не `CONSOLIDATION` | `audit_section.KFO` в модели есть и точнее | Фильтр журнала «Консолидация» не покажет правки КФО (ledger, Task 2) |
|
||
| 23 | 06.11.2026 | OpenAPI КФО генерируется `scripts/kfo_contract/gen_kfo_openapi.py` из `kfo_example.py`, руками не правится | Числа примеров совпадают с §8; проверки правил в одном месте | Правка руками разойдётся с генератором при следующей перегенерации (ledger, Task 3; ревью M-9) |
|
||
| 24 | 06.11.2026 | Порядок исключений: гудвилл-пары, затем агрегаты; остаток для каскада уменьшают все исключения | Ревью I-1: иначе распределение 1310/1350/1370 у ДО с несколькими ГО зависит от реализации | Иное распределение между 1310/1350/1370, если методологи считают иначе |
|
||
| 25 | 06.11.2026 | Округляется одна ячейка: `vgo` и `manual` — до тысяч, `auto = vgo − manual` | Ревью I-2: раздельное округление ломает `vgo = auto + manual` | `auto` может отличаться на 1 тыс. от «своей» суммы в рублях |
|
||
| 26 | 06.11.2026 | `min_or_full` при `amount_2 ≤ 0` — как пустая сторона (`amount_1`), при `amount_1 ≤ 0` — 0; расхождения — по всем агрегатам `min`, в том числе односторонним | Ревью I-3; односторонний остаток — тоже неисключённое ВГО (вариант 1 OQ-186) | Порог срабатывает чаще из-за односторонних агрегатов |
|
||
| 27 | 06.11.2026 | `share_percent` при `|доля| ≥ 1000 %` — `null` (колонка `numeric(7,4)` не расширяется) | Ревью I-4: при почти нулевом итоге доля бессмысленна и переполняет колонку | Экран не покажет экстремальные доли |
|
||
| 28 | 06.11.2026 | Итоги — слагаемые с +1 после приведения знака; формулы §7 с минусом к КФО не применяются; 2410/2411 — как в форме (OQ-12); итога нет в форме (упрощённая) — Σ слагаемых | Ревью I-5, I-6 | Σ строки 2410 по компаниям с разной конвенцией знака неверна (на итоги не влияет — итоги из формы) |
|
||
| 29 | 06.11.2026 | Корректировка агрегата правила 2, закрытого гудвилл-парой, не применяется и не входит в сводку; предупреждение `CORRECTION_SUPERSEDED_BY_GOODWILL` | Ревью M-5: аналитик видит её в таблице корректировок, а КФО игнорирует | Лишнее предупреждение, если аналитик ввёл её намеренно |
|