Merge pull request 'Архив change kfo-calculation-contract (AURORA-1546/1547, #206)' (#216) from chore/archive-kfo-calculation-contract into main

This commit is contained in:
aep 2026-11-07 15:42:08 +03:00
commit cf7c1aebdd
7 changed files with 84 additions and 0 deletions

View File

@ -533,3 +533,87 @@ locator?}` (`fileWarning` контракта): у `2.0` — как пришли,
#### Scenario: Занятый crm_id #### Scenario: Занятый crm_id
- **WHEN** создаётся ГСЗ с `crm_id`, который уже есть у другой ГСК или ГСЗ - **WHEN** создаётся ГСЗ с `crm_id`, который уже есть у другой ГСК или ГСЗ
- **THEN** 409 `CRM_ID_EXISTS`, ничего не создано - **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`