From 5682897ed05d68fd1563dab1e7f91e8a31f95a85 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Fri, 16 Oct 2026 15:42:30 +0400 Subject: [PATCH] =?UTF-8?q?OpenSpec=20change=20intercompany-matrix:=20?= =?UTF-8?q?=D1=82=D1=80=D0=B5=D0=B1=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=88=D0=B0=D1=85=D0=BC=D0=B0=D1=82=D0=BA=D0=B8=20=D0=B8=D0=B7?= =?UTF-8?q?=20specs/=20=D0=B2=20=D0=B4=D0=B5=D0=BB=D1=8C=D1=82=D1=8B=20(#1?= =?UTF-8?q?14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Требования AURORA-1537, дописанные в openspec/specs/{audit-trail,consolidated-osv, consolidations-api,directories-api}, перенесены в дельты change'а; specs/ возвращены к main. Известные дефекты (AURORA-1651/1652/1653) зафиксированы в design.md. Co-Authored-By: Claude Opus 5.5 --- .../intercompany-matrix/.openspec.yaml | 2 + .../changes/intercompany-matrix/design.md | 50 +++++++++++++++++++ .../changes/intercompany-matrix/proposal.md | 32 ++++++++++++ .../specs/audit-trail/spec.md | 13 +++++ .../specs/consolidated-osv/spec.md | 49 ++++++++++++++++++ .../specs/consolidations-api/spec.md | 29 +++++++++++ .../specs/directories-api/spec.md | 16 ++++++ openspec/changes/intercompany-matrix/tasks.md | 4 ++ openspec/specs/audit-trail/spec.md | 12 ----- openspec/specs/consolidated-osv/spec.md | 48 ------------------ openspec/specs/consolidations-api/spec.md | 28 ----------- openspec/specs/directories-api/spec.md | 15 ------ 12 files changed, 195 insertions(+), 103 deletions(-) create mode 100644 openspec/changes/intercompany-matrix/.openspec.yaml create mode 100644 openspec/changes/intercompany-matrix/design.md create mode 100644 openspec/changes/intercompany-matrix/proposal.md create mode 100644 openspec/changes/intercompany-matrix/specs/audit-trail/spec.md create mode 100644 openspec/changes/intercompany-matrix/specs/consolidated-osv/spec.md create mode 100644 openspec/changes/intercompany-matrix/specs/consolidations-api/spec.md create mode 100644 openspec/changes/intercompany-matrix/specs/directories-api/spec.md create mode 100644 openspec/changes/intercompany-matrix/tasks.md diff --git a/openspec/changes/intercompany-matrix/.openspec.yaml b/openspec/changes/intercompany-matrix/.openspec.yaml new file mode 100644 index 0000000..0cb4765 --- /dev/null +++ b/openspec/changes/intercompany-matrix/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-16 diff --git a/openspec/changes/intercompany-matrix/design.md b/openspec/changes/intercompany-matrix/design.md new file mode 100644 index 0000000..0be5e7f --- /dev/null +++ b/openspec/changes/intercompany-matrix/design.md @@ -0,0 +1,50 @@ +## Context + +Реализация — PR RSHB/arm-core#114 (AURORA-1537). Шахматка — чистая функция от сводной ОСВ версии, периметра и выбранных +счетов; в БД не хранится (инвариант «производные консолидации не хранятся», DECISION-5). + +## Decisions + +1. Матрица считается поверх `ConsolidatedOsvService.build_with_members` (бывший приватный `_build`): строки свода и + участники периметра берутся из того же расчёта, что и сводная ОСВ; в `SourceRow`/`ConsolidatedRow` добавлено + `account_id`. +2. Пара `(A, B)`: сторона 1 — дебет ОСВ A по контрагенту B, сторона 2 — кредит ОСВ B по контрагенту A. Расчёт + поддерживает показатели `closing`/`turnover`, но в режимах ALL и MANUAL всегда используется конечное сальдо + (`closing`). +3. Режим ALL берёт все счета вида `A`/`AP` (дебет) и `P`/`AP` (кредит); MANUAL раскрывает выбранные счета по иерархии + `chart_of_account` и фильтрует по виду. +4. Порог расхождения 5 % проверяется по точному значению до округления; суммы в JSON — десятичные строки с двумя + знаками (`ROUND_HALF_UP`). +5. Строки пары без `account_id` в суммы не входят и считаются в `skipped_osv_entry_count`. +6. Результирующая таблица — пять фиксированных статей (`RESULT_ARTICLES`) со списками кодов счетов и исключений; + отсутствие любого кода в плане счетов — 422 `RESULT_ACCOUNTS_MISSING`. +7. xlsx: имя файла `intercompany-matrix_<ГК>_<год>-<квартал>_v.xlsx`; каждая выгрузка пишет + собственное событие `audit_event(EXPORT)`. +8. Сверка с эталоном «Русский бисквит» — только по точным парам счетов и компаний; общая сумма элиминации эталона не + приравнивается к минимуму по паре (решение владельца 08.10.2026). + +## Known issues + +Требования ниже описывают текущее поведение; дефекты вынесены в задачи Jira и в этом change не исправляются. + +- AURORA-1651: `one_sided` считается по наличию исходных записей, а не по суммам и строкам-основаниям — пара «100 млн + против строки с нулевым сальдо» не подсвечивается, «0 против 0» подсвечивается ложно; подсветка таких пар в xlsx; + режим ALL захватывает забалансовые счета 001–012. +- AURORA-1652: `GET …/elimination-rules` пуст до задачи 1536, хотя требование описывает непустой ответ; формат сумм + результирующей таблицы `"0"` вместо `"0.00"` (расхождение с форматом матрицы); ссылки на §§ в docstring; ссылка README + на гайд фронта. +- AURORA-1653: переход «ячейка → сводная ОСВ» — фильтр по паре в `GET …/osv`; `osv_entry_ids_*` содержат ID нулевых + строк и не отсортированы. + +## Decisions made without discussion + +- 16.10.2026. Требования автора перенесены из `openspec/specs/*` в дельты `ADDED` дословно, без правки содержания. + Обоснование: владелец поручил оформить change процессно, а содержательные замечания ревью уже вынесены в задачи. + Цена ошибки: низкая — текст можно поправить в этом же change'е до архивации. +- 16.10.2026. Спека фиксирует текущее поведение, включая семантику `one_sided` и формат `"0"` результирующей таблицы; + исправление требований идёт вместе с кодом в AURORA-1651/1652/1653 (там же MODIFIED-дельты). Обоснование: иначе спека + разойдётся с кодом PR. Цена ошибки: до исправления спека описывает известные дефекты (отмечены в `## Known issues`). +- 16.10.2026. Все требования оформлены как `ADDED` в существующих capability, новая capability не заводится. + Обоснование: шахматка — отчёт по сводной ОСВ (consolidated-osv), API версий (consolidations-api), справочников и + аудита; отдельная capability дробила бы уже согласованную структуру. Цена ошибки: низкая, перенос между capability — + правка путей дельт. diff --git a/openspec/changes/intercompany-matrix/proposal.md b/openspec/changes/intercompany-matrix/proposal.md new file mode 100644 index 0000000..351169c --- /dev/null +++ b/openspec/changes/intercompany-matrix/proposal.md @@ -0,0 +1,32 @@ +## Why + +Отчёт «Шахматка ВГО» (ТЗ §6): аналитик сверяет внутригрупповые обороты пар компаний периметра по строкам сводной ОСВ +версии, видит расхождения сверх порога, получает результирующую таблицу по пяти статьям и выгрузку в xlsx. Jira +AURORA-1537, реализация — PR RSHB/arm-core#114 (автор — Потапова). Требования автор дописала прямо в +`openspec/specs/`; при подготовке к слиянию они оформлены как дельты этого change'а (решение владельца 16.10.2026). + +## What Changes + +- Расчёт шахматки без хранения в БД (`GET …/intercompany-matrix/all`, `POST …/intercompany-matrix/manual`) по строкам + сводной ОСВ версии и её периметру; режимы ALL и MANUAL. +- Результирующая таблица ВГО из пяти фиксированных статей (`GET …/intercompany-matrix/result-table`). +- Выгрузка xlsx шахматки (`…/all/export`, `…/manual/export`) и аудит `EXPORT` каждой выгрузки. +- Правила элиминации версии (`GET …/elimination-rules`). +- Плоский список счетов для фильтров (`GET /api/v1/directories/accounts`). +- В сводную ОСВ добавлен `account_id` строки (внутреннее поле домена, в API свода не выходит). + +## Capabilities + +### New Capabilities +Нет. + +### Modified Capabilities +- `consolidated-osv`: шахматка по сводной ОСВ и её xlsx. +- `consolidations-api`: правила элиминации версии, результирующая таблица ВГО. +- `directories-api`: плоский список счетов. +- `audit-trail`: аудит экспорта шахматки. + +## Impact + +Только `arm-core` (API, сервисы, схемы), таблицы и DBML не меняются. Код реализован и протестирован в PR #114; этот +change фиксирует требования к текущему поведению ветки, включая известные дефекты (см. `design.md`, `## Known issues`). diff --git a/openspec/changes/intercompany-matrix/specs/audit-trail/spec.md b/openspec/changes/intercompany-matrix/specs/audit-trail/spec.md new file mode 100644 index 0000000..5266ea8 --- /dev/null +++ b/openspec/changes/intercompany-matrix/specs/audit-trail/spec.md @@ -0,0 +1,13 @@ +## ADDED Requirements + +### Requirement: Аудит экспорта шахматки +Каждый успешный `GET …/intercompany-matrix/all/export` и `POST …/intercompany-matrix/manual/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`. + +#### Scenario: Две выгрузки +- **WHEN** пользователь дважды выгружает ту же шахматку +- **THEN** в `audit_event` две разные записи `EXPORT`, каждая со своим payload и числом ячеек diff --git a/openspec/changes/intercompany-matrix/specs/consolidated-osv/spec.md b/openspec/changes/intercompany-matrix/specs/consolidated-osv/spec.md new file mode 100644 index 0000000..734e365 --- /dev/null +++ b/openspec/changes/intercompany-matrix/specs/consolidated-osv/spec.md @@ -0,0 +1,49 @@ +## ADDED Requirements + +### Requirement: Шахматка по сводной ОСВ версии +`GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/all` и +`POST /api/v1/consolidations/versions/{version_id}/intercompany-matrix/manual` +(`CONSOLIDATION_VIEW`, policy) SHALL рассчитывать матрицу без сохранения в БД, из строк сводной ОСВ версии +и её периметра. ALL включает все счета вида `A`/`AP` для дебета и `P`/`AP` для кредита. MANUAL принимает +обязательные непустые `debit_account_ids: UUID[]` и `credit_account_ids: UUID[]`, раскрывает выбранные счета +по иерархии и оставляет подходящий вид. Неизвестный счёт SHALL давать 422 `ACCOUNT_NOT_FOUND`, неверный вид — +422 `ACCOUNT_KIND_MISMATCH`; пустой массив — 422 валидации тела. Невидимая и несуществующая версия SHALL +отвечать одинаковым 404 `NOT_FOUND`. + +Ответ SHALL содержать `companies[] {id, name}` для всего периметра и `cells[]` для каждой упорядоченной пары +разных компаний: `company_a_id`, `company_b_id`, `amount_a`, `amount_b`, `variance_amount`, `variance_percent`, +`exceeds_threshold`, `one_sided`, `skipped_osv_entry_count`, `osv_entry_ids_debit`, `osv_entry_ids_credit`. +Для пары `(A, B)` `amount_a` — сумма `closing_debit` строк A с контрагентом B по дебетовым счетам, +`amount_b` — сумма `closing_credit` строк B с контрагентом A по кредитовым. Расхождение — модуль разности; +процент — расхождение / меньшую сумму × 100 или null при нуле. `exceeds_threshold` SHALL быть true при точном +проценте > 5, до округления; суммы и процент в JSON SHALL быть десятичными строками с двумя знаками после +запятой (`ROUND_HALF_UP`). `one_sided` SHALL быть true при наличии исходных записей ОСВ только с одной стороны, +в том числе при нулевой сумме. `skipped_osv_entry_count` SHALL считать исходные строки ОСВ пары с +`account_id = null` на обеих сторонах; они не входят в суммы. Массивы `osv_entry_ids_debit/credit` SHALL +содержать ID исходных записей соответствующей стороны. + +#### Scenario: Пустой периметр и отсутствие ОСВ +- **WHEN** у версии нет членов периметра +- **THEN** `companies = []`, `cells = []`; если компании есть, но ОСВ по выбранным счетам нет, ячейки присутствуют с нулевыми суммами + +#### Scenario: Только одна сторона пары +- **WHEN** у A есть строка ОСВ с контрагентом B, а у B встречной строки нет +- **THEN** для `(A, B)` `one_sided = true`, `variance_percent = null`, соответствующий массив `osv_entry_ids_*` пуст + +#### Scenario: Счёт не сопоставлен +- **WHEN** у пары есть строка свода с `account_id = null` и двумя `entry_ids` +- **THEN** она не входит в суммы, `skipped_osv_entry_count` обеих направленных ячеек пары увеличивается на 2 + +#### Scenario: Эталон «Русский бисквит» +- **WHEN** матрица строится по ОСВ РБ и Пекарни Счастливочной из эталонных фикстур +- **THEN** суммы сравниваются с эталоном только для точных пар счетов и компаний; общая сумма элиминации эталона не приравнивается к минимуму по паре в API (решение владельца 08.10.2026) + +### Requirement: XLSX шахматки +`GET …/intercompany-matrix/all/export` и `POST …/intercompany-matrix/manual/export` SHALL использовать +те же режимы, тело и права, что и соответствующие JSON-маршруты. Ответ — XLSX с именем +`intercompany-matrix_<ГК>_<год>-<квартал>_v.xlsx`, шапкой (ГК, период, версия, единицы, режим, +выбранные счета) и строками разницы, дебета, кредита и процента расхождения для каждой пары. + +#### Scenario: Экспорт выбранных счетов +- **WHEN** пользователь выгружает MANUAL для ГК «Русский бисквит» за 2024 Q1, версия 1 +- **THEN** имя содержит `Русский бисквит_2024-Q1_v1.xlsx`, шапка содержит выбранные счета, ячейки процента показывают два знака и символ `%` без повторного масштабирования diff --git a/openspec/changes/intercompany-matrix/specs/consolidations-api/spec.md b/openspec/changes/intercompany-matrix/specs/consolidations-api/spec.md new file mode 100644 index 0000000..7ed286b --- /dev/null +++ b/openspec/changes/intercompany-matrix/specs/consolidations-api/spec.md @@ -0,0 +1,29 @@ +## ADDED Requirements + +### 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 содержать `accounts: UUID[]` и `excluded: UUID[]` до раскрытия иерархии счетов. +Невидимая и несуществующая версия SHALL отвечать одинаковым 404 `NOT_FOUND`. + +#### Scenario: Правило с исключением +- **WHEN** у правила первая сторона включает счёт 76 и исключает 76.07.5 +- **THEN** ID 76 находится в `company_1.accounts`, ID 76.07.5 — в `company_1.excluded` + +### Requirement: Результирующая таблица ВГО +`GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/result-table` +(`CONSOLIDATION_VIEW`, policy) SHALL возвращать `{companies[], articles[]}`. Компании — все члены периметра +по имени; статья содержит `code`, `name`, `amounts[] {company_id, amount}` и `group_total`. +Пять фиксированных статей §6: дебиторская и кредиторская задолженность, финансовые вложения, +долгосрочные и краткосрочные займы. Сумма компании SHALL считаться по всем остальным компаниям периметра +из конечного сальдо нужной стороны и выбранных счетов статьи; `group_total` — сумма по компаниям. +Отсутствие любого необходимого кода счёта SHALL давать 422 `RESULT_ACCOUNTS_MISSING` с перечнем кодов. + +#### Scenario: Компания без ОСВ +- **WHEN** в периметре есть компания без подходящих строк ОСВ +- **THEN** она есть в `companies`, её сумма по каждой статье — строка `"0"` + +#### Scenario: Нет обязательного счёта +- **WHEN** в плане счетов отсутствует счёт 58.03 +- **THEN** ответ 422 `RESULT_ACCOUNTS_MISSING`, сообщение содержит `58.03` diff --git a/openspec/changes/intercompany-matrix/specs/directories-api/spec.md b/openspec/changes/intercompany-matrix/specs/directories-api/spec.md new file mode 100644 index 0000000..d6997c0 --- /dev/null +++ b/openspec/changes/intercompany-matrix/specs/directories-api/spec.md @@ -0,0 +1,16 @@ +## ADDED Requirements + +### Requirement: Плоский список счетов для фильтров шахматки +`GET /api/v1/directories/accounts` SHALL возвращать пользователю с `DIRECTORY_VIEW` все строки +`chart_of_account` одним массивом в естественном порядке кодов. Каждая строка SHALL содержать `id`, `code`, +`parent_id`, `name`, `kind` (`A`/`P`/`AP`/null), `row_type`, `is_off_balance`, `is_debit`, `is_credit`; +корневые счета имеют `parent_id = null`. `is_debit = true` для `A` и `AP`, `is_credit = true` для `P` и `AP`; +при `kind = null` оба признака ложны. + +#### Scenario: Счета разных уровней +- **WHEN** в плане счетов есть счёт, субсчёт и аналитика +- **THEN** ответ содержит все три строки без вложенности, а `parent_id` указывает на родительскую строку + +#### Scenario: Нет права на справочник +- **WHEN** пользователь без `DIRECTORY_VIEW` запрашивает список счетов +- **THEN** ответ 403 diff --git a/openspec/changes/intercompany-matrix/tasks.md b/openspec/changes/intercompany-matrix/tasks.md new file mode 100644 index 0000000..36b2415 --- /dev/null +++ b/openspec/changes/intercompany-matrix/tasks.md @@ -0,0 +1,4 @@ +## 1. Реализация + +Реализация уже выполнена автором в PR RSHB/arm-core#114 (AURORA-1537); задач плана нет. Доработки по замечаниям ревью — +AURORA-1651, AURORA-1652, AURORA-1653. diff --git a/openspec/specs/audit-trail/spec.md b/openspec/specs/audit-trail/spec.md index 085fe86..05b12ba 100644 --- a/openspec/specs/audit-trail/spec.md +++ b/openspec/specs/audit-trail/spec.md @@ -145,15 +145,3 @@ ORM-модель `audit_event` MUST NOT использоваться для `UPD #### Scenario: Автоматический переход - **WHEN** подтверждение документа перевело версию в `DATA_CORRECTION` - **THEN** событие `CONSOLIDATION_STATUS_CHANGE` с `user_id` подтвердившего, `before_value.status = INITIATED` - -### Requirement: Аудит экспорта шахматки -Каждый успешный `GET …/intercompany-matrix/all/export` и `POST …/intercompany-matrix/manual/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`. - -#### Scenario: Две выгрузки -- **WHEN** пользователь дважды выгружает ту же шахматку -- **THEN** в `audit_event` две разные записи `EXPORT`, каждая со своим payload и числом ячеек diff --git a/openspec/specs/consolidated-osv/spec.md b/openspec/specs/consolidated-osv/spec.md index c6e648f..2dbd2bf 100644 --- a/openspec/specs/consolidated-osv/spec.md +++ b/openspec/specs/consolidated-osv/spec.md @@ -162,51 +162,3 @@ filename="svod-osv.xlsx"; filename*=UTF-8''svod-osv_<ГК>_<год>-<кварт - **WHEN** экспорт версии с 3 строками и 1 пробелом открыт `openpyxl` - **THEN** лист «Сводная ОСВ» содержит заголовок и 3 строки, суммы — числа с двумя знаками, равные API; лист «Пробелы» — одну строку с `Q2`; `audit_event(EXPORT)` с `after_value.rows = 3` - -### Requirement: Шахматка по сводной ОСВ версии -`GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/all` и -`POST /api/v1/consolidations/versions/{version_id}/intercompany-matrix/manual` -(`CONSOLIDATION_VIEW`, policy) SHALL рассчитывать матрицу без сохранения в БД, из строк сводной ОСВ версии -и её периметра. ALL включает все счета вида `A`/`AP` для дебета и `P`/`AP` для кредита. MANUAL принимает -обязательные непустые `debit_account_ids: UUID[]` и `credit_account_ids: UUID[]`, раскрывает выбранные счета -по иерархии и оставляет подходящий вид. Неизвестный счёт SHALL давать 422 `ACCOUNT_NOT_FOUND`, неверный вид — -422 `ACCOUNT_KIND_MISMATCH`; пустой массив — 422 валидации тела. Невидимая и несуществующая версия SHALL -отвечать одинаковым 404 `NOT_FOUND`. - -Ответ SHALL содержать `companies[] {id, name}` для всего периметра и `cells[]` для каждой упорядоченной пары -разных компаний: `company_a_id`, `company_b_id`, `amount_a`, `amount_b`, `variance_amount`, `variance_percent`, -`exceeds_threshold`, `one_sided`, `skipped_osv_entry_count`, `osv_entry_ids_debit`, `osv_entry_ids_credit`. -Для пары `(A, B)` `amount_a` — сумма `closing_debit` строк A с контрагентом B по дебетовым счетам, -`amount_b` — сумма `closing_credit` строк B с контрагентом A по кредитовым. Расхождение — модуль разности; -процент — расхождение / меньшую сумму × 100 или null при нуле. `exceeds_threshold` SHALL быть true при точном -проценте > 5, до округления; суммы и процент в JSON SHALL быть десятичными строками с двумя знаками после -запятой (`ROUND_HALF_UP`). `one_sided` SHALL быть true при наличии исходных записей ОСВ только с одной стороны, -в том числе при нулевой сумме. `skipped_osv_entry_count` SHALL считать исходные строки ОСВ пары с -`account_id = null` на обеих сторонах; они не входят в суммы. Массивы `osv_entry_ids_debit/credit` SHALL -содержать ID исходных записей соответствующей стороны. - -#### Scenario: Пустой периметр и отсутствие ОСВ -- **WHEN** у версии нет членов периметра -- **THEN** `companies = []`, `cells = []`; если компании есть, но ОСВ по выбранным счетам нет, ячейки присутствуют с нулевыми суммами - -#### Scenario: Только одна сторона пары -- **WHEN** у A есть строка ОСВ с контрагентом B, а у B встречной строки нет -- **THEN** для `(A, B)` `one_sided = true`, `variance_percent = null`, соответствующий массив `osv_entry_ids_*` пуст - -#### Scenario: Счёт не сопоставлен -- **WHEN** у пары есть строка свода с `account_id = null` и двумя `entry_ids` -- **THEN** она не входит в суммы, `skipped_osv_entry_count` обеих направленных ячеек пары увеличивается на 2 - -#### Scenario: Эталон «Русский бисквит» -- **WHEN** матрица строится по ОСВ РБ и Пекарни Счастливочной из эталонных фикстур -- **THEN** суммы сравниваются с эталоном только для точных пар счетов и компаний; общая сумма элиминации эталона не приравнивается к минимуму по паре в API (решение владельца 08.10.2026) - -### Requirement: XLSX шахматки -`GET …/intercompany-matrix/all/export` и `POST …/intercompany-matrix/manual/export` SHALL использовать -те же режимы, тело и права, что и соответствующие JSON-маршруты. Ответ — XLSX с именем -`intercompany-matrix_<ГК>_<год>-<квартал>_v.xlsx`, шапкой (ГК, период, версия, единицы, режим, -выбранные счета) и строками разницы, дебета, кредита и процента расхождения для каждой пары. - -#### Scenario: Экспорт выбранных счетов -- **WHEN** пользователь выгружает MANUAL для ГК «Русский бисквит» за 2024 Q1, версия 1 -- **THEN** имя содержит `Русский бисквит_2024-Q1_v1.xlsx`, шапка содержит выбранные счета, ячейки процента показывают два знака и символ `%` без повторного масштабирования diff --git a/openspec/specs/consolidations-api/spec.md b/openspec/specs/consolidations-api/spec.md index 5b64bd6..19331bd 100644 --- a/openspec/specs/consolidations-api/spec.md +++ b/openspec/specs/consolidations-api/spec.md @@ -223,31 +223,3 @@ account, status, missing_quarters, document_ids}`: `document_type` и `account` #### Scenario: Невидимая версия при занятой строке - **WHEN** версию чужого филиала держит `FOR UPDATE` другая транзакция, пользователь без видимости вызывает `POST …/lock` - **THEN** 404 `VERSION_NOT_FOUND` без ожидания - -### 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 содержать `accounts: UUID[]` и `excluded: UUID[]` до раскрытия иерархии счетов. -Невидимая и несуществующая версия SHALL отвечать одинаковым 404 `NOT_FOUND`. - -#### Scenario: Правило с исключением -- **WHEN** у правила первая сторона включает счёт 76 и исключает 76.07.5 -- **THEN** ID 76 находится в `company_1.accounts`, ID 76.07.5 — в `company_1.excluded` - -### Requirement: Результирующая таблица ВГО -`GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/result-table` -(`CONSOLIDATION_VIEW`, policy) SHALL возвращать `{companies[], articles[]}`. Компании — все члены периметра -по имени; статья содержит `code`, `name`, `amounts[] {company_id, amount}` и `group_total`. -Пять фиксированных статей §6: дебиторская и кредиторская задолженность, финансовые вложения, -долгосрочные и краткосрочные займы. Сумма компании SHALL считаться по всем остальным компаниям периметра -из конечного сальдо нужной стороны и выбранных счетов статьи; `group_total` — сумма по компаниям. -Отсутствие любого необходимого кода счёта SHALL давать 422 `RESULT_ACCOUNTS_MISSING` с перечнем кодов. - -#### Scenario: Компания без ОСВ -- **WHEN** в периметре есть компания без подходящих строк ОСВ -- **THEN** она есть в `companies`, её сумма по каждой статье — строка `"0"` - -#### Scenario: Нет обязательного счёта -- **WHEN** в плане счетов отсутствует счёт 58.03 -- **THEN** ответ 422 `RESULT_ACCOUNTS_MISSING`, сообщение содержит `58.03` diff --git a/openspec/specs/directories-api/spec.md b/openspec/specs/directories-api/spec.md index 245fa18..f9491f3 100644 --- a/openspec/specs/directories-api/spec.md +++ b/openspec/specs/directories-api/spec.md @@ -189,18 +189,3 @@ SHALL выполняться в одной транзакции с запись #### Scenario: Ошибка внутри мутации - **WHEN** при переводе ЮЛ закрытие членства завершается ошибкой БД - **THEN** ЮЛ остаётся в прежней ГК, строк истории и аудита нет - -### Requirement: Плоский список счетов для фильтров шахматки -`GET /api/v1/directories/accounts` SHALL возвращать пользователю с `DIRECTORY_VIEW` все строки -`chart_of_account` одним массивом в естественном порядке кодов. Каждая строка SHALL содержать `id`, `code`, -`parent_id`, `name`, `kind` (`A`/`P`/`AP`/null), `row_type`, `is_off_balance`, `is_debit`, `is_credit`; -корневые счета имеют `parent_id = null`. `is_debit = true` для `A` и `AP`, `is_credit = true` для `P` и `AP`; -при `kind = null` оба признака ложны. - -#### Scenario: Счета разных уровней -- **WHEN** в плане счетов есть счёт, субсчёт и аналитика -- **THEN** ответ содержит все три строки без вложенности, а `parent_id` указывает на родительскую строку - -#### Scenario: Нет права на справочник -- **WHEN** пользователь без `DIRECTORY_VIEW` запрашивает список счетов -- **THEN** ответ 403