From 9ee07963144010fe727b60e441d704ee579e9b94 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Fri, 6 Nov 2026 20:32:11 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=A4=D0=9E:=20OpenSpec-delta=20arm-core?= =?UTF-8?q?=20=D0=B8=20tasks.md=20(#206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../specs/arm-core/spec.md | 85 +++++++++++++++++++ .../changes/kfo-calculation-contract/tasks.md | 11 +++ 2 files changed, 96 insertions(+) create mode 100644 openspec/changes/kfo-calculation-contract/specs/arm-core/spec.md create mode 100644 openspec/changes/kfo-calculation-contract/tasks.md diff --git a/openspec/changes/kfo-calculation-contract/specs/arm-core/spec.md b/openspec/changes/kfo-calculation-contract/specs/arm-core/spec.md new file mode 100644 index 0000000..afc42f1 --- /dev/null +++ b/openspec/changes/kfo-calculation-contract/specs/arm-core/spec.md @@ -0,0 +1,85 @@ +## 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` diff --git a/openspec/changes/kfo-calculation-contract/tasks.md b/openspec/changes/kfo-calculation-contract/tasks.md new file mode 100644 index 0000000..c235412 --- /dev/null +++ b/openspec/changes/kfo-calculation-contract/tasks.md @@ -0,0 +1,11 @@ +# Tasks: kfo-calculation-contract (docs #206) + +План — `plans/2026-11-06-kfo-contract.md`. + +- [x] 1. `context/05-domain-rules.md` §8 «КФО» (сквозной пример), §8 «Корректировка», §5 п. 1; `07-open-questions.md` OQ-180…193 +- [x] 2. `model/data-model-v5.26.dbml`, `model/entities-v5.26.md`, ссылки на текущую версию, диаграммы +- [x] 3. `dev/api-contract-kfo.md` + `.openapi.yaml`; `api-contract-corrections.*` — агрегат `min_or_full`; `frontend-api-guide.md` §5.7.3 +- [ ] 4. Spec delta, `make check`, финальное ревью, PR, вложения в AURORA-1546 / AURORA-1547 +- [ ] 5. Документы контекста: `03-architecture` (без изменений — архитектура та же), `07-open-questions` (п. 1), `09-delivery-plan` (Срез 6 — после merge кода 1546/1547) + +Дочерние change'и — в `arm-core` (AURORA-1546, 1547, 1417); заводят исполнители.