Merge pull request 'openspec: архив intercompany-matrix (#114)' (#117) from chore/114-archive-intercompany-matrix into main
This commit is contained in:
commit
18225153f1
@ -145,3 +145,15 @@ 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 и числом ячеек
|
||||
|
||||
@ -162,3 +162,51 @@ 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<n>.xlsx`, шапкой (ГК, период, версия, единицы, режим,
|
||||
выбранные счета) и строками разницы, дебета, кредита и процента расхождения для каждой пары.
|
||||
|
||||
#### Scenario: Экспорт выбранных счетов
|
||||
- **WHEN** пользователь выгружает MANUAL для ГК «Русский бисквит» за 2024 Q1, версия 1
|
||||
- **THEN** имя содержит `Русский бисквит_2024-Q1_v1.xlsx`, шапка содержит выбранные счета, ячейки процента показывают два знака и символ `%` без повторного масштабирования
|
||||
|
||||
@ -223,3 +223,31 @@ 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`
|
||||
|
||||
@ -189,3 +189,18 @@ 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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user