Andrey Epifancev 9ee0796314 КФО: OpenSpec-delta arm-core и tasks.md (#206)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 20:32:11 +04:00

7.6 KiB
Raw Blame History

ADDED Requirements

Requirement: Расчёт КФО версии

Ядро SHALL считать результат КФО версии консолидации по context/05-domain-rules.md §8 «КФО» и хранить его в kfo_result, kfo_result_line, kfo_result_line_company (DBML v5.26) в тыс. руб.: строки бланка редакции на отчётную дату версии и служебную строку GOODWILL; по компании — client_amount (строки расходов 2120, 2210, 2220, 2330, 2350 приведены к −|x|), vgo_amount (Σ исключений компании по строке в рублях, до целых тысяч, половина от нуля, со знаком строки), amount = client_amount − vgo_amount, share_percent; по строке — consolidated_amount = sum_amount − vgo_amount + goodwill_adjust. Вход взаимных сумм SHALL быть агрегатами таблицы корректировок (§5 п. 2a); применённая корректировка агрегата SHALL заменять его автосумму, а не прибавляться к ней.

Scenario: Агрегат с корректировкой

  • WHEN у агрегата правила 6 автосумма 0 (отклонение 400 %) и применена корректировка «Дт Б 1230 10 000 000, Кт А 1520 10 000 000»
  • THEN у Б по 1230 и у А по 1520 исключается по 10 000 тыс., автосумма агрегата не добавляется; у агрегата в допуске с корректировкой автосумма тоже не добавляется

Scenario: Меньший итог сторон корректировки

  • WHEN у корректировки агрегата Дт = 20 000 000 (1230) + 5 000 000 (1170), Кт = 22 500 000 (1520)
  • THEN каждая сторона исключает 22 500 000: компания 1 — 20 000 000 из 1230 и 2 500 000 из 1170, компания 2 — 22 500 000 из 1520

Scenario: Каскад правила 2

  • WHEN агрегат правила 2 (min_or_full) без стороны 2 даёт 15 000 000, а у компании 2 строки 1310 = 10 000 тыс., 1350 = 3 000 тыс.
  • THEN у компании 1 исключается 15 000 тыс. из 1170, у компании 2 — 10 000 из 1310, 3 000 из 1350, 2 000 из 1370

Scenario: Гудвилл учтён

  • WHEN у пары ГО → ДО таблицы гудвилла use_goodwill (ФВ 50 000 000, P 32 000 000, G 18 000 000) и goodwill_enabled = true
  • THEN у ГО из 1170 исключается 50 000 тыс., у ДО из 1310 — 32 000 тыс., строка GOODWILL = 18 000 тыс. и входит в 1100 и 1600, агрегат правила 2 этой пары не применяется, bank_amount 1600 равен bank_amount 1700

Scenario: Гудвилл не учтён

  • WHEN у той же пары G ≤ 0 или goodwill_enabled = false
  • THEN применяется агрегат правила 2 как обычно, строка GOODWILL = 0

Scenario: Расходы с разным знаком в формах

  • WHEN у компании А строка 2120 хранится как −120 000, у компании Б как +60 000, и 10 000 тыс. исключаются из 2110 А и 2120 Б
  • THEN client_amount 2120 Б = −60 000, vgo_amount = −10 000, amount = −50 000; Σ vgo_amount строки 2100 = 0

Scenario: Нет формы у компании

  • WHEN у компании периметра нет подтверждённой формы ОФР, привязанной к версии
  • THEN КФО считается, значения компании по строкам ОФР = 0, в kfo_result.warnings — FORM_MISSING с компанией и statement_type = income

Requirement: Автосумма агрегата одной функцией

Автосумма агрегата SHALL считаться одной функцией домена для таблицы корректировок (eliminated_amount) и для КФО, по elimination_rule.elimination_mode: min — меньшая сторона в допуске, иначе 0; min_or_full — меньшая сторона при обеих > 0, amount_1 при пустой стороне 2, 0 при пустой стороне 1, без порога (exceeds_threshold = false); manual — 0.

Scenario: Правило 2 в таблице корректировок

  • WHEN у агрегата правила 2 сторона 2 пуста, amount_1 = 15 000 000
  • THEN таблица корректировок отдаёт status = ONE_SIDED, eliminated_amount = "15000000.00", exceeds_threshold = false, и КФО исключает ту же сумму

Requirement: Чтение результата КФО

GET /api/v1/consolidations/versions/{version_id}/kfo (право KFO_VIEW) SHALL отдавать KfoResultOut по dev/api-contract-kfo.openapi.yaml, деньги — строками с двумя знаками в тыс. руб., units = "thousand_rub". Пока версия не заморожена, ответ SHALL отражать текущие данные версии: устаревший результат пересчитывается и сохраняется до ответа. У замороженной версии (kfo_result.is_frozen) — сохранённый результат без пересчёта, с сохранёнными warnings.

Scenario: Изменение после расчёта

  • WHEN после computed_at аналитик сохранил корректировку агрегата
  • THEN следующий GET …/kfo возвращает результат с учётом корректировки и новым computed_at

Scenario: Замороженная версия

  • WHEN версия в статусе «Верифицирован» и is_frozen = true
  • THEN ответ — сохранённый результат, editable = false, read_only_reason = "VERSION_NOT_EDITABLE", пересчёта нет

Requirement: Ручные поля КФО

PUT /api/v1/consolidations/versions/{version_id}/kfo/annotations (право CONSOLIDATION_EDIT, блокировка версии) SHALL сохранять одной транзакцией комментарии строк (≤ 2000), threshold_justification и methodology_deviations (≤ 4000): отсутствующее поле не меняется, null очищает; комментарии и обоснование переживают пересчёт. Изменение SHALL писать одно событие audit_event(KFO_EDIT) раздела KFO с before / after.

Scenario: Неизвестная строка

  • WHEN в comments[] строка, которой нет в бланке версии
  • THEN 422 UNKNOWN_LINE с detail.statement_type, detail.line_code, ничего не сохраняется

Scenario: Сохранение обоснования

  • WHEN у версии превышение порога ББ и аналитик с блокировкой сохраняет threshold_justification
  • THEN ответ — KfoResultOut с этим обоснованием, в журнале одно событие KFO_EDIT