В практике программирования на платформе 1С часто встречается задача, когда необходимо из «сырых» данных о долгах получить чистую картину взаимоотношений между контрагентами. Типичный пример: организация А должна организации Б, а организация Б, в свою очередь, должна организации А. В отчете мы хотим видеть не две строки с долгами, а одну — с итоговым сальдо. Рассмотрим, как решить эту задачу исключительно средствами языка запросов 1С, не прибегая к механизмам СКД или постобработке на встроенном языке.
Первая проблема, с которой мы сталкиваемся — это наличие двух разных полей (Должник и Кредитор) в одной строке. Для эффективной группировки нам нужно привести данные к единому виду. Проанализируем ситуацию: долг для одного участника является активом (плюс), а для другого — пассивом (минус).
Разберем по шагам, как это реализовать в запросе:
ОБЪЕДИНИТЬ ВСЕ, чтобы превратить таблицу с двумя колонками в таблицу с одной колонкой Контрагент.Посмотрим на пример кода, реализующего этот подход:
ВЫБРАТЬ
Тест.Должник КАК Контрагент,
Тест.СуммаДолга КАК Сумма
ПОМЕСТИТЬ ВТ_ВсеПотоки
ИЗ
Документ.Взаимозачет.ТабличнаяЧасть КАК Тест
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Тест.Кредитор,
-Тест.СуммаДолга
ИЗ
Документ.Взаимозачет.ТабличнаяЧасть КАК Тест;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Контрагент КАК Контрагент,
СУММА(ВТ.Сумма) КАК ЧистоеСальдо
ПОМЕСТИТЬ ВТ_ИтоговыеОстатки
ИЗ
ВТ_ВсеПотоки КАК ВТ
ГРУППИРОВАТЬ ПО
ВТ.Контрагент;
Теперь у нас есть таблица, где для каждого контрагента понятно: должен он (сумма > 0) или должны ему (сумма < 0). Это база, от которой мы можем строить финальный отчет.
После того как мы получили чистые остатки, нам нужно снова «связать» должников с кредиторами для эталонного вида отчета. Выясним причину, почему это не всегда получается сделать простым соединением: суммы долгов могут не совпадать копейка в копейку, если мы рассматриваем сложную сеть из множества компаний.
Однако в простом случае (когда суммы зеркальны) мы можем применить левое соединение таблицы самой с собой:
ВЫБРАТЬ
Должники.Контрагент КАК Должник,
Кредиторы.Контрагент КАК Кредитор,
Должники.ЧистоеСальдо КАК СуммаДолга
ИЗ
ВТ_ИтоговыеОстатки КАК Должники
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИтоговыеОстатки КАК Кредиторы
ПО Должники.ЧистоеСальдо = -Кредиторы.ЧистоеСальдо
И Должники.Контрагент <> Кредиторы.Контрагент
ГДЕ
Должники.ЧистоеСальдо > 0
Важный момент: это решение идеально работает только для парных взаимозачетов. Если один должник должен трем кредиторам, такое соединение может выдать некорректный результат (дублирование строк). Для более сложных случаев следует использовать алгоритм распределения.
Если задача стоит более масштабно — распределить общую массу долгов между всеми кредиторами максимально эффективно — нам потребуется механизм нарастающих итогов. В 1С это реализуется через соединение временной таблицы с самой собой по условию «меньше или равно».
Рассмотрим алгоритм действий:
ВТ_Дебиторы (где сумма > 0) и ВТ_Кредиторы (где сумма < 0, берем по модулю).НачалоОтрезка1 < КонецОтрезка2 И КонецОтрезка1 > НачалоОтрезка2.Проанализируем пример расчета нарастающего итога в запросе:
ВЫБРАТЬ
Т1.Контрагент,
Т1.Сумма,
СУММА(Т2.Сумма) КАК НарастающийИтог
ПОМЕСТИТЬ ВТ_ДебиторыСИтогом
ИЗ
ВТ_Дебиторы КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Дебиторы КАК Т2
ПО Т1.Контрагент >= Т2.Контрагент
ГРУППИРОВАТЬ ПО
Т1.Контрагент,
Т1.Сумма
Используя такие «интервалы», мы можем математически точно распределить долг каждого должника между кредиторами в том порядке, в котором они идут в выборке. Это классическая реализация метода ФИФО в языке запросов, которая не требует рекурсии, недоступной в 1С.
При работе с большими объемами данных (например, если в документе тысячи строк ТЗ) важно помнить о производительности. Рассмотрим несколько правил:
Контрагент или поля, используемые для нарастающего итога). Для этого используйте ИНДЕКСИРОВАТЬ ПО.ПОМЕСТИТЬ вместо вложенных запросов. Платформа 1С гораздо лучше оптимизирует планы запросов, когда данные разделены на физические временные таблицы в базе данных.Должник и Кредитор имеют один и тот же тип данных, чтобы избежать неявного приведения типов при ОБЪЕДИНИТЬ ВСЕ, что может существенно замедлить выполнение.Таким образом, мы выяснили, что даже без использования объектной модели или сложных циклов на встроенном языке, можно построить мощный аналитический отчет. Главный секрет заключается в правильной предварительной подготовке данных — переходе от плоской таблицы «кто-кому» к балансовой модели «сальдо по контрагенту».