From 43f4dce6c5074e99fe3744560989057c2faae02c Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Fri, 23 Oct 2026 00:05:02 +0400 Subject: [PATCH] =?UTF-8?q?=D0=90=D1=80=D1=85=D0=B8=D0=B2=20OpenSpec=20int?= =?UTF-8?q?ercompany-matrix-presets=20(PR=20#141)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../design.md | 0 .../proposal.md | 0 .../specs/audit-trail/spec.md | 0 .../specs/consolidated-osv/spec.md | 0 .../specs/consolidations-api/spec.md | 0 .../tasks.md | 2 +- openspec/specs/audit-trail/spec.md | 13 +++-- openspec/specs/consolidated-osv/spec.md | 47 +++++++++++++++++++ openspec/specs/consolidations-api/spec.md | 20 +++++++- 9 files changed, 76 insertions(+), 6 deletions(-) rename openspec/changes/{intercompany-matrix-presets => archive/2026-10-23-intercompany-matrix-presets}/design.md (100%) rename openspec/changes/{intercompany-matrix-presets => archive/2026-10-23-intercompany-matrix-presets}/proposal.md (100%) rename openspec/changes/{intercompany-matrix-presets => archive/2026-10-23-intercompany-matrix-presets}/specs/audit-trail/spec.md (100%) rename openspec/changes/{intercompany-matrix-presets => archive/2026-10-23-intercompany-matrix-presets}/specs/consolidated-osv/spec.md (100%) rename openspec/changes/{intercompany-matrix-presets => archive/2026-10-23-intercompany-matrix-presets}/specs/consolidations-api/spec.md (100%) rename openspec/changes/{intercompany-matrix-presets => archive/2026-10-23-intercompany-matrix-presets}/tasks.md (97%) diff --git a/openspec/changes/intercompany-matrix-presets/design.md b/openspec/changes/archive/2026-10-23-intercompany-matrix-presets/design.md similarity index 100% rename from openspec/changes/intercompany-matrix-presets/design.md rename to openspec/changes/archive/2026-10-23-intercompany-matrix-presets/design.md diff --git a/openspec/changes/intercompany-matrix-presets/proposal.md b/openspec/changes/archive/2026-10-23-intercompany-matrix-presets/proposal.md similarity index 100% rename from openspec/changes/intercompany-matrix-presets/proposal.md rename to openspec/changes/archive/2026-10-23-intercompany-matrix-presets/proposal.md diff --git a/openspec/changes/intercompany-matrix-presets/specs/audit-trail/spec.md b/openspec/changes/archive/2026-10-23-intercompany-matrix-presets/specs/audit-trail/spec.md similarity index 100% rename from openspec/changes/intercompany-matrix-presets/specs/audit-trail/spec.md rename to openspec/changes/archive/2026-10-23-intercompany-matrix-presets/specs/audit-trail/spec.md diff --git a/openspec/changes/intercompany-matrix-presets/specs/consolidated-osv/spec.md b/openspec/changes/archive/2026-10-23-intercompany-matrix-presets/specs/consolidated-osv/spec.md similarity index 100% rename from openspec/changes/intercompany-matrix-presets/specs/consolidated-osv/spec.md rename to openspec/changes/archive/2026-10-23-intercompany-matrix-presets/specs/consolidated-osv/spec.md diff --git a/openspec/changes/intercompany-matrix-presets/specs/consolidations-api/spec.md b/openspec/changes/archive/2026-10-23-intercompany-matrix-presets/specs/consolidations-api/spec.md similarity index 100% rename from openspec/changes/intercompany-matrix-presets/specs/consolidations-api/spec.md rename to openspec/changes/archive/2026-10-23-intercompany-matrix-presets/specs/consolidations-api/spec.md diff --git a/openspec/changes/intercompany-matrix-presets/tasks.md b/openspec/changes/archive/2026-10-23-intercompany-matrix-presets/tasks.md similarity index 97% rename from openspec/changes/intercompany-matrix-presets/tasks.md rename to openspec/changes/archive/2026-10-23-intercompany-matrix-presets/tasks.md index 2a4374d..52e27f8 100644 --- a/openspec/changes/intercompany-matrix-presets/tasks.md +++ b/openspec/changes/archive/2026-10-23-intercompany-matrix-presets/tasks.md @@ -20,4 +20,4 @@ - [x] 3.3 Мелкие замечания раунда 2 (шапка xlsx пресета — счета правила и «кроме», подписи статей при `side_1 = K`, лишняя проверка в `_export_response`, реквизиты правила в `MatrixExportInfo` и `_filename`, описания ошибок preset в OpenAPI, проверка правила до расчёта свода) вынесены в технический долг AURORA-1761 (решение владельца 22.10.2026) — см. design.md. -- [ ] 3.4 После слияния: архив change. +- [x] 3.4 После слияния: архив change. diff --git a/openspec/specs/audit-trail/spec.md b/openspec/specs/audit-trail/spec.md index ffe6640..47a617f 100644 --- a/openspec/specs/audit-trail/spec.md +++ b/openspec/specs/audit-trail/spec.md @@ -147,17 +147,24 @@ ORM-модель `audit_event` MUST NOT использоваться для `UPD - **THEN** событие `CONSOLIDATION_STATUS_CHANGE` с `user_id` подтвердившего, `before_value.status = INITIATED` ### Requirement: Аудит экспорта шахматки -Каждый успешный `GET …/intercompany-matrix/all/export` и `POST …/intercompany-matrix/manual/export` +Каждый успешный `GET …/intercompany-matrix/all/export`, `POST …/intercompany-matrix/manual/export` и +`GET …/intercompany-matrix/preset/{rule_id}/export` SHALL добавлять отдельное неизменяемое событие `audit_event` с `action = EXPORT`, `section = EXPORT`, `object_type = consolidation_version`, `object_id = consolidation_version_id`, `user_id` инициатора, `before_value = null` и `after_value = {report: "intercompany_matrix", mode, debit_account_ids, -credit_account_ids, cells}`. `mode` — `ALL` или `MANUAL`, массивы счетов — ID из запроса (в ALL пустые), -`cells` — число ячеек. Выгрузка сводной ОСВ продолжает писать собственное событие `EXPORT`. +credit_account_ids, cells}`; для `PRESET` в `after_value` SHALL быть дополнительно `rule_id`, `rule_no` и +`rule_version_no` (фактическая версия применённого правила, в том числе при незафиксированной версии правил). `mode` — `ALL`, +`MANUAL` или `PRESET`, массивы счетов — ID из запроса (в ALL и PRESET пустые), `cells` — число ячеек. Выгрузка +сводной ОСВ продолжает писать собственное событие `EXPORT`. #### Scenario: Две выгрузки - **WHEN** пользователь дважды выгружает ту же шахматку - **THEN** в `audit_event` две разные записи `EXPORT`, каждая со своим payload и числом ячеек +#### Scenario: Выгрузка пресета +- **WHEN** пользователь выгружает `GET …/intercompany-matrix/preset/{rule_id}/export` +- **THEN** событие `EXPORT` содержит `mode = PRESET`, `rule_id`, `rule_no`, `rule_version_no`, пустые массивы счетов и число ячеек + ### Requirement: Аудит экспорта результирующей таблицы ВГО Каждый успешный `GET …/intercompany-matrix/result-table/export` SHALL добавлять отдельное неизменяемое событие `audit_event` с `action = EXPORT`, `section = EXPORT`, `object_type = consolidation_version`, diff --git a/openspec/specs/consolidated-osv/spec.md b/openspec/specs/consolidated-osv/spec.md index c6e648f..5ad071f 100644 --- a/openspec/specs/consolidated-osv/spec.md +++ b/openspec/specs/consolidated-osv/spec.md @@ -210,3 +210,50 @@ filename="svod-osv.xlsx"; filename*=UTF-8''svod-osv_<ГК>_<год>-<кварт #### Scenario: Экспорт выбранных счетов - **WHEN** пользователь выгружает MANUAL для ГК «Русский бисквит» за 2024 Q1, версия 1 - **THEN** имя содержит `Русский бисквит_2024-Q1_v1.xlsx`, шапка содержит выбранные счета, ячейки процента показывают два знака и символ `%` без повторного масштабирования + +### Requirement: Шахматка по правилу-пресету +`GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/preset/{rule_id}` +(`CONSOLIDATION_VIEW`, policy) SHALL рассчитывать шахматку в режиме PRESET с тем же ответом, что ALL и MANUAL. +Счета дебета и кредита SHALL браться из правила элиминации `rule_id`: дебет — сторона `company_1`, если +`side_1 = D`, иначе `company_2`; кредит — вторая сторона; исключения (`excluded`) вычитаются, выбранные счета +раскрываются по иерархии и фильтруются по виду, как в MANUAL. Показатель SHALL браться из `measure` правила: +`closing` — конечное сальдо (`closing_debit`/`closing_credit`), `turnover` — обороты (`turnover_debit`/`turnover_credit`). +Правило SHALL быть действительным для версии консолидации: при заданном `elimination_rules_version` — с тем же +`version_no`, иначе — без `valid_to` и с максимальным открытым `version_no`; несуществующее или недействительное правило SHALL давать 404. Ошибки 422: +`RULE_REQUIRED` — правило не выбрано, `INVALID_RULE` — правило ОФР либо неверные `side_1`, `measure` или сторона счёта правила, +`ACCOUNTS_REQUIRED` — у одной из сторон нет счетов. Невидимая и несуществующая версия SHALL отвечать 404 `NOT_FOUND`. + +#### Scenario: Пресет по сальдо +- **WHEN** правило с `side_1 = D`, `measure = closing` содержит дебетовые счета на стороне 1 и кредитовые на стороне 2 +- **THEN** `amount_a` пары — сумма `closing_debit` по счетам стороны 1, `amount_b` — сумма `closing_credit` по счетам стороны 2 + +#### Scenario: Пресет по оборотам +- **WHEN** у правила `measure = turnover` +- **THEN** суммы считаются по `turnover_debit`/`turnover_credit` + +#### Scenario: Правило другой версии +- **WHEN** у версии `elimination_rules_version = 2`, а `rule_id` принадлежит правилу версии 1 +- **THEN** 404 для расчёта и выгрузки + +#### Scenario: Неизвестное правило +- **WHEN** `rule_id` не существует +- **THEN** 404 + +#### Scenario: Правило ОФР +- **WHEN** у правила `statement_type = income` +- **THEN** расчёт и выгрузка возвращают 422 `INVALID_RULE` + +### Requirement: XLSX шахматки пресета +`GET …/intercompany-matrix/preset/{rule_id}/export` SHALL возвращать XLSX с теми же строками, что JSON-расчёт +пресета. Имя файла SHALL иметь вид `intercompany-matrix_<ГК>_<год>-<квартал>_v_rule-balance-.xlsx`. Шапка SHALL +содержать режим «Пресет», коды счетов дебета и кредита (раскрытые по иерархии, с учётом исключений правила), номер +правила, статьи А и Б (`article_1_line` / `article_2_line`), фактическую версию правил и строку «Показатель» со значением «Сальдо» для `closing` или «Оборот» для `turnover`. Строка «Показатель» SHALL присутствовать и в выгрузках ALL и MANUAL (значение +«Сальдо»). + +#### Scenario: Выгрузка по оборотам +- **WHEN** пользователь выгружает пресет с `measure = turnover` +- **THEN** шапка содержит «Режим — Пресет», «Показатель — Оборот», номер, статьи и версию правила; имя файла заканчивается на `_rule-balance-.xlsx` + +#### Scenario: Правило не найдено +- **WHEN** `rule_id` не существует +- **THEN** 404, файл не отдаётся, событие аудита не пишется diff --git a/openspec/specs/consolidations-api/spec.md b/openspec/specs/consolidations-api/spec.md index 66eaf9c..abd8832 100644 --- a/openspec/specs/consolidations-api/spec.md +++ b/openspec/specs/consolidations-api/spec.md @@ -226,8 +226,12 @@ account, status, missing_quarters, document_ids}`: `document_type` и `account` ### Requirement: Правила элиминации версии `GET /api/v1/consolidations/versions/{version_id}/elimination-rules` (`CONSOLIDATION_VIEW`, policy по ГК) -SHALL возвращать все правила версии массивом. Каждое правило SHALL содержать `rule_no`, `version_no`, -`statement_type`, `side_1` (`D`/`K`), `measure` (`closing`/`turnover`), `company_1`, `company_2`; +SHALL возвращать объект `{version_no, is_version_fixed, rules}`. Если у версии задан +`consolidation_version.elimination_rules_version`, `version_no` SHALL равняться ему, `is_version_fixed = true`, а +`rules` — правилами этой версии. Если он не задан, `is_version_fixed = false`, а `rules` — +открытые правила (`valid_to IS NULL`) только с максимальным `version_no`; ответ SHALL содержать этот фактический +`version_no`. При отсутствии открытых правил `version_no = null`, `rules = []`. Каждое правило SHALL содержать `id`, `rule_no`, +`version_no`, `statement_type`, `side_1` (`D`/`K`), `measure` (`closing`/`turnover`), `company_1`, `company_2`; каждая сторона SHALL содержать `accounts: UUID[]` и `excluded: UUID[]` до раскрытия иерархии счетов. Невидимая и несуществующая версия SHALL отвечать одинаковым 404 `NOT_FOUND`. @@ -235,6 +239,18 @@ SHALL возвращать все правила версии массивом. - **WHEN** у правила первая сторона включает счёт 76 и исключает 76.07.5 - **THEN** ID 76 находится в `company_1.accounts`, ID 76.07.5 — в `company_1.excluded` +#### Scenario: Версия правил зафиксирована +- **WHEN** у версии консолидации `elimination_rules_version = 1` +- **THEN** ответ содержит `is_version_fixed = true`, `version_no = 1` и только правила с `version_no = 1` + +#### Scenario: Версия правил не зафиксирована +- **WHEN** у версии консолидации `elimination_rules_version` пуст +- **THEN** ответ содержит `is_version_fixed = false`, фактический `version_no` и открытые правила только этой версии + +#### Scenario: Открыты две версии правил +- **WHEN** открыты правила с `version_no = 1` и `version_no = 2`, в том числе с одинаковым `rule_no` +- **THEN** ответ содержит только правила версии 2 и `version_no = 2` + ### Requirement: Результирующая таблица ВГО `GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/result-table` (`CONSOLIDATION_VIEW`, policy) SHALL возвращать `{companies[], articles[]}`. Компании — все члены периметра