2026-11-06 17:00:46 +04:00

10 KiB
Raw Blame History

MODIFIED Requirements

Requirement: Шахматка по сводной ОСВ версии

POST /api/v1/consolidations/versions/{version_id}/intercompany-matrix принимает {debit_account_ids?: UUID[], credit_account_ids?: UUID[], legal_entity_ids?: UUID[]}. Если список стороны отсутствует или пуст, для неё выбираются все подходящие балансовые счета (is_off_balance = false); непустой список раскрывается по иерархии и может содержать забалансовые счета. Пустые списки обеих сторон эквивалентны режиму ALL, хотя бы один непустой список — MANUAL. Маршрут (CONSOLIDATION_VIEW, policy) SHALL рассчитывать матрицу без сохранения в БД, из строк сводной ОСВ версии и её периметра. Для пустой стороны выбираются счета вида A/AP для дебета или P/AP для кредита; забалансовые счета (001–012) MUST NOT входить в такую выборку. Неизвестный счёт SHALL давать 422 ACCOUNT_NOT_FOUND, неверный вид — 422 ACCOUNT_KIND_MISMATCH. Пустой legal_entity_ids выбирает все ЮЛ периметра; непустой ограничивает обе оси матрицы и суммы выбранными ЮЛ. ID вне периметра SHALL давать 422 ENTITY_NOT_IN_PERIMETER. Невидимая и несуществующая версия SHALL отвечать одинаковым 404 NOT_FOUND.

Ответ SHALL содержать companies[] {id, name} для периметра (при непустом legal_entity_ids — только выбранные ЮЛ в порядке периметра), total_debit и total_credit с суммой по каждому ЮЛ (ключ — ID ЮЛ, значение — десятичная строка с двумя знаками), и 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 тогда и только тогда, когда ровно одна из сумм amount_a, amount_b равна нулю (знак ненулевой суммы не важен); наличие строк ОСВ с нулевым сальдо на признак не влияет. Пара подсвечивается, если exceeds_threshold или one_sided. skipped_osv_entry_count SHALL считать исходные строки ОСВ пары с account_id = null на обеих сторонах; они не входят в суммы. Массивы osv_entry_ids_debit/credit SHALL содержать ID исходных записей соответствующей стороны. total_debit («Итого Дт», колонка справа) суммирует amount_a ячеек строки по company_a_id, total_credit («Итого Кт», строка снизу) — amount_b ячеек столбца по company_b_id; ключи — ID всех ЮЛ из companies. Диагональ не участвует, отрицательные суммы входят со знаком, суммы ячеек точные (numeric(18,2)). Для ЮЛ без подходящих строк, в том числе когда в выборке одно ЮЛ, итог равен "0.00" (правило аналитика, DOC-221277 «Итоги шахматки», 03.11.2026).

Scenario: Пустой периметр и отсутствие ОСВ

  • WHEN у версии нет членов периметра
  • THEN companies = [], cells = [], total_debit = {}, total_credit = {}; если компании есть, но ОСВ по выбранным счетам нет, ячейки присутствуют с нулевыми суммами

Scenario: Только одна сторона пары

  • WHEN у A есть строка ОСВ с контрагентом B и ненулевым сальдо, а у B встречной строки нет
  • THEN для (A, B) one_sided = true, variance_percent = null, соответствующий массив osv_entry_ids_* пуст

Scenario: Встречная строка с нулевым сальдо

  • WHEN у A по B сальдо 100 000 000, а у B по A есть строка ОСВ с нулевым сальдо (одни обороты)
  • THEN one_sided = true, exceeds_threshold = false, пара подсвечивается

Scenario: Обе стороны нулевые

  • WHEN у A и у B есть строки ОСВ друг на друга, но обе суммы равны 0
  • THEN one_sided = false, exceeds_threshold = false, пара не подсвечивается

Scenario: Отрицательное сальдо

  • WHEN у A по B сальдо −50, у B по A — 0
  • THEN one_sided = true

Scenario: Забалансовые счета

  • WHEN у A по B есть сальдо на счёте 001 (is_off_balance = true)
  • THEN в режиме ALL оно в суммы не входит; в MANUAL с выбранным счётом 001 — входит

Scenario: Счёт не сопоставлен

  • WHEN у пары есть строка свода с account_id = null и двумя entry_ids
  • THEN она не входит в суммы, skipped_osv_entry_count обеих направленных ячеек пары увеличивается на 2

Scenario: Эталон «Русский бисквит»

  • WHEN матрица строится по ОСВ РБ и Пекарни Счастливочной из эталонных фикстур
  • THEN суммы сравниваются с эталоном только для точных пар счетов и компаний; общая сумма элиминации эталона не приравнивается к минимуму по паре в API (решение владельца 08.10.2026)

Scenario: Итоги Дт и Кт

  • WHEN в периметре A, B, C; amount_a ячеек (A, B) = 100 и (A, C) = 20, amount_b ячеек (A, B) = 70 и (C, B) = 5
  • THEN total_debit[A] = "120.00", total_credit[B] = "75.00"

Scenario: Фильтр по ЮЛ

  • WHEN запрос с legal_entity_ids = [A, B] при периметре A, B, C
  • THEN companies и обе оси — только A и B, ячейки и итоги не учитывают C; ID вне периметра — 422 ENTITY_NOT_IN_PERIMETER

Scenario: Пустая одна сторона

  • WHEN debit_account_ids = [62], credit_account_ids пуст
  • THEN дебет — счёт 62 с потомками, кредит — все балансовые счета вида P/AP; режим выгрузки и аудита — MANUAL

Requirement: XLSX шахматки

POST …/intercompany-matrix/export принимает то же тело, что единый JSON-маршрут, и строит XLSX по той же выборке ЮЛ и счетов. В шапке пустая сторона обозначается «Все балансовые счета»; аудит сохраняет legal_entity_ids из запроса и использует mode = ALL, когда пусты обе стороны, иначе mode = MANUAL.

Ответ — XLSX с именем intercompany-matrix_<ГК>_<год>-<квартал>_v<n>.xlsx, шапкой (ГК, период, версия, единицы, режим, выбранные счета; для ALL — «Все балансовые счета») и строками разницы, дебета, кредита и процента расхождения для каждой пары. Пара SHALL заливаться цветом порога, если exceeds_threshold или one_sided — так же, как в API. Справа от колонок компаний SHALL быть колонка «Итого Дт» с total_debit по каждому ЮЛ строки (ячейка объединена на строки пары); под матрицей SHALL быть строка «Итого Кт» с total_credit по каждому ЮЛ колонки; ячейка на пересечении пустая. Значения — те же, что в JSON.

Scenario: Экспорт выбранных счетов

  • WHEN пользователь выгружает MANUAL для ГК «Русский бисквит» за 2024 Q1, версия 1
  • THEN имя содержит Русский бисквит_2024-Q1_v1.xlsx, шапка содержит выбранные счета, ячейки процента показывают два знака и символ % без повторного масштабирования

Scenario: Подсветка пары без встречной стороны

  • WHEN выгружается ALL, у пары amount_a = 100000000, amount_b = 0
  • THEN все строки пары залиты цветом порога, в шапке «Режим» и «Счета дебета/кредита» — «Все балансовые счета»