## ADDED Requirements ### 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: Строки сводной ОСВ без пары по пресету `GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/preset/{rule_id}/unpaired-osv` (`CONSOLIDATION_VIEW`, policy) SHALL возвращать `{items, total}`: все строки сводной ОСВ версии, чьи записи ОСВ входят в `osv_entry_ids_debit/credit` ячеек шахматки пресета с `one_sided = true`. Формат строки — как на странице сводной ОСВ, без пагинации; `total` — число элементов. Ошибки — как у расчёта пресета. #### Scenario: Есть односторонняя ячейка - **WHEN** в шахматке пресета у ячейки `(A, B)` `one_sided = true` - **THEN** `items` содержит строки сводной ОСВ с записями этой ячейки, `total` равен их числу #### Scenario: Все пары двусторонние - **WHEN** у шахматки пресета нет односторонних ячеек - **THEN** `items = []`, `total = 0` #### Scenario: Неизвестное правило - **WHEN** `rule_id` не существует - **THEN** 404 ### Requirement: XLSX шахматки пресета `GET …/intercompany-matrix/preset/{rule_id}/export` SHALL возвращать XLSX с теми же строками, что JSON-расчёт пресета. Имя файла SHALL иметь вид `intercompany-matrix_<ГК>_<год>-<квартал>_v_rule-balance-.xlsx`. Шапка SHALL содержать режим «Пресет», коды счетов дебета и кредита правила, номер правила, статьи А и Б, фактическую версию правил и строку «Показатель» со значением «Сальдо» для `closing` или «Оборот» для `turnover`. Строка «Показатель» SHALL присутствовать и в выгрузках ALL и MANUAL (значение «Сальдо»). #### Scenario: Выгрузка по оборотам - **WHEN** пользователь выгружает пресет с `measure = turnover` - **THEN** шапка содержит «Режим — Пресет», «Показатель — Оборот», номер, статьи и версию правила; имя файла заканчивается на `_rule-balance-.xlsx` #### Scenario: Правило не найдено - **WHEN** `rule_id` не существует - **THEN** 404, файл не отдаётся, событие аудита не пишется