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