arm-ka-docs/dev/api-contract-kfo.md
2026-11-06 20:30:50 +04:00

15 KiB
Raw Blame History

Контракт API результата КФО (AURORA-1546 / AURORA-1547)

Контракт вкладки «КФО» отчёта версии консолидации — до реализации (docs #206, OpenSpec change kfo-calculation-contract). Машиночитаемая копия — dev/api-contract-kfo.openapi.yaml с тремя полными примерами ответа (их можно брать как фейк для экрана). Когда расчёт появится в arm-core, копия выгружается из app.openapi() и сверяется тестом, как у гудвилла; если файлы разойдутся с кодом — прав код, оба файла правятся вслед за ним.

Источники, в порядке приоритета:

  1. 05-domain-rules.md §8 «КФО» — алгоритм (п. 1–12) и сквозной пример. Здесь формулы не повторяются — только ссылки «п. N».
  2. DBML v5.26: kfo_result, kfo_result_line, kfo_result_line_company, consolidation_version.
  3. DOC-274647 «Экран: КФО» (черновик) и макет [31-04], [31-04-01] — состав колонок, состояния экрана.

1. Общее

Как в dev/api-contract-corrections.md §1 и dev/api-contract-goodwill.md §1:

  • префикс /api/v1, Authorization: Bearer <JWT>;
  • чтение — право KFO_VIEW (не CONSOLIDATION_VIEW, как у корректировок и гудвилла: право в seed есть и означает ровно это), правки — CONSOLIDATION_EDIT (KFO_EDIT в seed нет);
  • версия вне видимости — 404 NOT_FOUND, как несуществующая;
  • доменные ошибки — {"detail": {"code", "message", ...}}, 422 валидации тела — массив pydantic;
  • деньги — строки с двумя знаками в тыс. руб. ("588500.00"), поле units = "thousand_rub". Это отличие от корректировок и гудвилла, где рубли: результат КФО по определению в тысячах (округление — часть расчёта, §8 «КФО» п. 6), рубли из него не восстановить. Переключатели «Единицы» (тыс. / млн) и «Знаков после запятой» — только показ;
  • проценты — строки с двумя знаками ("70.42"), null — не определён (нулевая база);
  • порядок проверок правок: право (403) → видимость (404) → версия редактируема (409 VERSION_NOT_EDITABLE, LOCK_REQUIRED, LOCKED_BY_OTHER) → операция. Фронт берёт editable / read_only_reason из ответа и правило сам не повторяет; у пользователя без CONSOLIDATION_EDIT — read_only_reason = "VERSION_NOT_EDITABLE".

Фронт ничего не считает. Доли, итоги, «в т. ч. взаимные», расхождения, базы и пределы порогов, признак превышения и подсветка строки базы приходят с сервера.

2. Понятия

  • Строки — бланк ББ и ОФР редакции на отчётную дату версии (66н до 31.12.2024, ФСБУ 4 позже) плюс служебная GOODWILL в разделе I сразу после 1170 (§8 «КФО» п. 1, 8). Приходят все строки бланка в его порядке (ordinal), включая нулевые; is_total — итоговая строка.
  • По компании (companies[] строки, порядок — как KfoResultOut.companies): «По данным клиента» (client_amount), «В том числе взаимные обязательства / обороты» (vgo_amount), «Очищенные показатели» (cleaned_amount = client_amount − vgo_amount), «Доля в показателе по группе, %» (share_percent). Строки расходов (2120, 2210, 2220, 2330, 2350) — со знаком минус, и их vgo_amount тоже (п. 2, 6). У GOODWILL разреза нет.
  • По строке: sum_amount (Σ клиента), vgo_amount (Σ взаимных = eliminated_auto + eliminated_manual), goodwill_adjust (у GOODWILL, 1100, 1600), bank_amount — «КФО, подготовленная банком» (sum_amount − vgo_amount + goodwill_adjust), deviation_amount — «Расхождения» (у итогов null), comment.
  • Сводка расхождений (summary.balance, summary.income) — две плашки экрана: сумма расхождений, база (base_kind: ЧА / ВБ / прибыль от продаж / выручка), предел и процент от базы, exceeded — красная плашка (п. 10). При превышении у строки базы is_threshold_base = true — подсветка.
  • Обоснование (threshold_justification) — принятие риска при превышении; justification_required — превышение есть хотя бы по одному отчёту. Обязательность перед отправкой на верификацию проверяет переход статуса (AURORA-2002), не этот API.
  • Реквизиты формы (requisites, п. 11): ГК, ГСК, отчётная дата, номер и статус версии, сценарий, подход (пакет документов), причины отклонений от Методики (methodology_deviations — редактируется здесь), решение УОБ (только чтение), корректировки — число агрегатов с применённой корректировкой и Σ их исключений.
  • Предупреждения (warnings[], п. 12) — не блокируют: FORM_MISSING, FORM_AMBIGUOUS, FORM_UNITS_UNKNOWN, LINE_OUTSIDE_BLANK, UNALLOCATED_ROWS (count — строк), INCOME_RULES_MISSING, GOODWILL_NOT_APPLIED, GOODWILL_ROW_REASSIGNED. Тексты message — с сервера, фронт показывает как есть.

3. Эндпоинты

Метод и путь Право Запрос Ответ Ошибки
GET /consolidations/versions/{version_id}/kfo KFO_VIEW — 200 KfoResultOut 404 NOT_FOUND
PUT /consolidations/versions/{version_id}/kfo/annotations CONSOLIDATION_EDIT KfoAnnotationsIn 200 KfoResultOut 404 NOT_FOUND, 409 правок версии, 422 UNKNOWN_LINE, 422 валидации тела

Выгрузки («Таблица», «Форма 2.03.01.01с») — AURORA-1565, отдельным контрактом.

3.1 GET …/kfo — результат

Весь результат одним ответом, без страниц. Пока версия не заморожена, ответ — по текущим данным версии: если что-то изменилось после computed_at (документы, сопоставление, разнесение, корректировки, гудвилл), сервер пересчитывает и сохраняет результат до ответа (как он это обнаруживает — AURORA-1417). Кнопки «Рассчитать» нет. У замороженной версии (is_frozen = true, ставится при «Верифицирован») — сохранённый результат без пересчёта, editable = false.

Версия без компаний или без форм — не ошибка: строки бланка с нулями и warnings (FORM_MISSING по каждой компании и отчёту).

3.2 PUT …/kfo/annotations — ручные поля

Тело:

{
  "comments": [{"statement_type": "balance", "line_code": "1520", "comment": "Акт сверки на 01.10 не получен"}],
  "threshold_justification": "Расхождение по 1520 — сверка в работе; риск принят",
  "methodology_deviations": null
}
  • Поле, которого нет в теле, не меняется; null — очищает. Пустой объект {} — допустим, ничего не пишет.
  • comments[] — только изменённые строки; comment ≤ 2000 символов, null или пустой после обрезки — очистить. Строки нет в бланке версии (или GOODWILL в income) — 422 UNKNOWN_LINE с detail.statement_type, detail.line_code; ничего не сохраняется.
  • threshold_justification, methodology_deviations — ≤ 4000 символов; длиннее — 422 массивом pydantic.
  • Обоснование можно сохранить и без превышения — сервер его не стирает; justification_required от этого не меняется.
  • Всё одной транзакцией; комментарии и обоснование переживают пересчёт (по statement_type, line_code).
  • Аудит — одно событие KFO_EDIT на запрос (object_type = kfo_result, раздел KFO), before / after — только изменённые поля. Запрос без изменений аудит не пишет.

Ответ — KfoResultOut целиком (с пересчётом, если он нужен).

4. Схемы

Полностью — в OpenAPI. Кратко:

KfoResultOut
  version_id uuid; computed_at date-time; is_frozen bool; editable bool; read_only_reason enum|null
  units "thousand_rub"
  companies[]        {legal_entity_id, name, inn} — порядок колонок (по имени)
  requisites         KfoRequisitesOut
  summary            {balance: KfoThresholdOut, income: KfoThresholdOut}
  justification_required bool; threshold_justification string|null
  goodwill           {enabled bool, total money}
  balance, income    {lines: KfoLineOut[]}
  warnings[]         KfoWarningOut {code, message, legal_entity_id?, statement_type?, count?}

KfoLineOut
  line_code, line_name, ordinal, is_total, is_threshold_base
  companies[]        {legal_entity_id, client_amount, vgo_amount, cleaned_amount, share_percent|null} (у GOODWILL — [])
  sum_amount, vgo_amount, eliminated_auto, eliminated_manual, goodwill_adjust, bank_amount   money
  deviation_amount   money|null (у итогов null)
  comment            string|null

KfoThresholdOut
  deviation_total money; base_kind NET_ASSETS|BALANCE_TOTAL|SALES_PROFIT|REVENUE; base_amount money
  limit_percent "10.00"|"1.00"; limit_amount money; deviation_percent pct|null; exceeded bool

KfoRequisitesOut
  company_group_name, related_group_name|null, reporting_date, version_no, version_status, scenario, approach
  methodology_deviations|null, uob_decision_no|null, uob_decision_date|null
  corrections_balance, corrections_income   {count int, amount money}

KfoAnnotationsIn   {comments?: [{statement_type, line_code, comment|null}], threshold_justification?, methodology_deviations?}

5. Состояния экрана (DOC-274647) → поля ответа

Состояние Как видно из ответа
Нет форм у части компаний warnings[].code = FORM_MISSING с legal_entity_id, statement_type; у компании нули
ОФР не исключается автоматически INCOME_RULES_MISSING (до AURORA-1999); взаимные обороты — только корректировками
Есть нераспределённые строки UNALLOCATED_ROWS, count
Превышение порога summary.*.exceeded = true, is_threshold_base у строки базы, justification_required = true
Гудвилл учтён goodwill.enabled = true, goodwill.total, строка GOODWILL с bank_amount
Только чтение editable = false, read_only_reason; при «Верифицирован» ещё is_frozen = true

6. Примеры

Три полных ответа — в OpenAPI (examples у GET …/kfo), на числах сквозного примера §8 «КФО» (три компании, бланк сокращён до строк примера; настоящий ответ содержит все строки бланка):

  • goodwill_exceeded — гудвилл включён, превышение порога ББ (42 900 > 37 100), justification_required = true;
  • goodwill_disabled — та же версия без учёта гудвилла: строка GOODWILL = 0, 1600 = 580 500;
  • frozen — «Верифицирован»: is_frozen, editable = false, обоснование, комментарий 1520, реквизиты УОБ.

Фрагмент goodwill_exceeded — строка 1230:

{
  "line_code": "1230", "line_name": "Дебиторская задолженность", "ordinal": 40,
  "is_total": false, "is_threshold_base": false,
  "companies": [
    {"legal_entity_id": "1111…", "client_amount": "520000.00", "vgo_amount": "120000.00", "cleaned_amount": "400000.00", "share_percent": "70.42"},
    {"legal_entity_id": "2222…", "client_amount": "130000.00", "vgo_amount": "10000.00",  "cleaned_amount": "120000.00", "share_percent": "21.13"},
    {"legal_entity_id": "3333…", "client_amount": "48000.00",  "vgo_amount": "0.00",      "cleaned_amount": "48000.00",  "share_percent": "8.45"}
  ],
  "sum_amount": "698000.00", "vgo_amount": "130000.00", "eliminated_auto": "100000.00", "eliminated_manual": "30000.00",
  "goodwill_adjust": "0.00", "bank_amount": "568000.00", "deviation_amount": "2900.00", "comment": null
}

7. Открытые вопросы

Варианты, принятые на старте, могут измениться по ответу заказчика — контракт меняется вместе с §8: OQ-180 (строки вне бланка), OQ-181 (колонки), OQ-182 (значение без формы), OQ-183 (итоговая строка), OQ-184 (округление), OQ-185 (место гудвилла), OQ-186 (объём расхождений), OQ-187 (ЧА), OQ-188 (нулевая база), OQ-189 (действие при превышении), OQ-190/191 (подход по Методике, согласование клиентом — полей нет), OQ-192 (пересчёт).