From ae2b791ff615020794d374a4210905a323da675f Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 7 Nov 2026 16:41:44 +0400 Subject: [PATCH] =?UTF-8?q?=D0=90=D1=80=D1=85=D0=B8=D0=B2=20change=20kfo-c?= =?UTF-8?q?alculation-contract=20(AURORA-1546/1547,=20#206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../.openspec.yaml | 0 .../design.md | 0 .../plans/2026-11-06-kfo-contract.md | 0 .../proposal.md | 0 .../specs/arm-core/spec.md | 0 .../tasks.md | 0 openspec/specs/arm-core/spec.md | 84 +++++++++++++++++++ 7 files changed, 84 insertions(+) rename openspec/changes/{kfo-calculation-contract => archive/2026-11-07-kfo-calculation-contract}/.openspec.yaml (100%) rename openspec/changes/{kfo-calculation-contract => archive/2026-11-07-kfo-calculation-contract}/design.md (100%) rename openspec/changes/{kfo-calculation-contract => archive/2026-11-07-kfo-calculation-contract}/plans/2026-11-06-kfo-contract.md (100%) rename openspec/changes/{kfo-calculation-contract => archive/2026-11-07-kfo-calculation-contract}/proposal.md (100%) rename openspec/changes/{kfo-calculation-contract => archive/2026-11-07-kfo-calculation-contract}/specs/arm-core/spec.md (100%) rename openspec/changes/{kfo-calculation-contract => archive/2026-11-07-kfo-calculation-contract}/tasks.md (100%) diff --git a/openspec/changes/kfo-calculation-contract/.openspec.yaml b/openspec/changes/archive/2026-11-07-kfo-calculation-contract/.openspec.yaml similarity index 100% rename from openspec/changes/kfo-calculation-contract/.openspec.yaml rename to openspec/changes/archive/2026-11-07-kfo-calculation-contract/.openspec.yaml diff --git a/openspec/changes/kfo-calculation-contract/design.md b/openspec/changes/archive/2026-11-07-kfo-calculation-contract/design.md similarity index 100% rename from openspec/changes/kfo-calculation-contract/design.md rename to openspec/changes/archive/2026-11-07-kfo-calculation-contract/design.md diff --git a/openspec/changes/kfo-calculation-contract/plans/2026-11-06-kfo-contract.md b/openspec/changes/archive/2026-11-07-kfo-calculation-contract/plans/2026-11-06-kfo-contract.md similarity index 100% rename from openspec/changes/kfo-calculation-contract/plans/2026-11-06-kfo-contract.md rename to openspec/changes/archive/2026-11-07-kfo-calculation-contract/plans/2026-11-06-kfo-contract.md diff --git a/openspec/changes/kfo-calculation-contract/proposal.md b/openspec/changes/archive/2026-11-07-kfo-calculation-contract/proposal.md similarity index 100% rename from openspec/changes/kfo-calculation-contract/proposal.md rename to openspec/changes/archive/2026-11-07-kfo-calculation-contract/proposal.md diff --git a/openspec/changes/kfo-calculation-contract/specs/arm-core/spec.md b/openspec/changes/archive/2026-11-07-kfo-calculation-contract/specs/arm-core/spec.md similarity index 100% rename from openspec/changes/kfo-calculation-contract/specs/arm-core/spec.md rename to openspec/changes/archive/2026-11-07-kfo-calculation-contract/specs/arm-core/spec.md diff --git a/openspec/changes/kfo-calculation-contract/tasks.md b/openspec/changes/archive/2026-11-07-kfo-calculation-contract/tasks.md similarity index 100% rename from openspec/changes/kfo-calculation-contract/tasks.md rename to openspec/changes/archive/2026-11-07-kfo-calculation-contract/tasks.md diff --git a/openspec/specs/arm-core/spec.md b/openspec/specs/arm-core/spec.md index 87ff399..03ef7a6 100644 --- a/openspec/specs/arm-core/spec.md +++ b/openspec/specs/arm-core/spec.md @@ -533,3 +533,87 @@ locator?}` (`fileWarning` контракта): у `2.0` — как пришли, #### Scenario: Занятый crm_id - **WHEN** создаётся ГСЗ с `crm_id`, который уже есть у другой ГСК или ГСЗ - **THEN** 409 `CRM_ID_EXISTS`, ничего не создано + +### 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` -- 2.47.2