Работа с ресурсами в СКД: как скорректировать итоги по группировкам без изменения детальных записей

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке отчетов на Системе компоновки данных (СКД) часто возникает классическая задача: необходимо скорректировать или обнулить итоговые показатели на уровне группировок при выполнении определенных условий (например, если сумма по статье бюджета стала отрицательной), но при этом сохранить исходные детальные записи без изменений. Если просто применить условие в запросе или в общем выражении ресурса, то изменятся и детальные записи, что лишит пользователя возможности расшифровать, из чего сложился этот итог.

В этой статье мы подробно разберем, как правильно организовать логику расчета ресурсов в СКД для иерархических отчетов, чтобы детальные записи оставались исходными, а итоги по родительским группировкам пересчитывались по новой логике.

В чем сложность стандартного подхода?

Когда мы пишем простое выражение ресурса вроде Сумма(СуммаПлан), СКД суммирует исходные данные из набора данных (запроса). Если мы попытаемся написать условие непосредственно в выражении ресурса:


ВЫБОР 
    КОГДА Сумма(СуммаПлан) < 0 
        ТОГДА 0 
    ИНАЧЕ Сумма(СуммаПлан) 
КОНЕЦ

то данное выражение применится на всех уровнях компоновки, включая детальные записи. В результате все отрицательные значения в отчете просто превратятся в нули, и пользователь не увидит реальной картины на самом нижнем уровне.

Для решения этой проблемы в СКД предусмотрено несколько профессиональных механизмов. Рассмотрим их по шагам.

Способ 1. Разделение расчета ресурса по группировкам («Рассчитывать по...»)

Самый простой и правильный способ разделить логику детальных записей и итогов — использовать колонку «Рассчитывать по» на вкладке «Ресурсы» в схеме компоновки данных. Вы можете добавить одно и то же поле в таблицу ресурсов несколько раз с разными выражениями.

Выполним следующую настройку:

  1. Строка 1 (для детальных записей):
    • Путь к данным: СуммаПлан
    • Выражение: Сумма(СуммаПлан)
    • Рассчитывать по: Детальные записи
    Это гарантирует, что на самом нижнем уровне пользователь увидит реальные (в том числе отрицательные) суммы.
  2. Строка 2 (для группировки по статьям):
    • Путь к данным: СуммаПлан
    • Выражение: ВЫБОР КОГДА Сумма(СуммаПлан) < 0 ТОГДА 0 ИНАЧЕ Сумма(СуммаПлан) КОНЕЦ
    • Рассчитывать по: Статья (или ваша конкретная группировка)

Благодаря такой настройке, СКД применит условие обнуления только тогда, когда будет выводить строку группировки «Статья», оставив подчиненные строки нетронутыми.

Способ 2. Использование функции «ВычислитьВыражениеСГруппировкойМассив» для пересчета иерархии

Предыдущий способ отлично работает, если у нас всего один уровень группировки. Но что делать, если у нас сложная иерархия (например, «Статья 2» содержит в себе «Статья 2.1»), и нам нужно, чтобы вышестоящий родитель («Статья 2») рассчитывал свой итог как сумму уже скорректированных (обнуленных) значений дочерних статей, а не складывал исходные отрицательные значения из запроса?

Для этого в СКД применяется мощная агрегатная функция ВычислитьВыражениеСГруппировкойМассив. Она позволяет сначала рассчитать итоги по группировке с учетом нашего условия, собрать их в массив, а затем сложить уже очищенные данные на уровне родителя.

Проанализируем выражение для ресурса родительской группировки:


Сумма(ВычислитьВыражениеСГруппировкойМассив("ВЫБОР КОГДА Сумма(СуммаПлан) < 0 ТОГДА 0 ИНАЧЕ Сумма(СуммаПлан) КОНЕЦ", "Статья"))

Разберем, как работает эта конструкция:

  1. Для каждой подчиненной «Статьи» СКД вычисляет выражение из первого параметра. Если сумма по статье отрицательная, она временно принимает значение 0.
  2. Полученные результаты по всем статьям складываются в массив.
  3. Внешняя функция Сумма() складывает элементы этого массива и выводит корректный итог для вышестоящего уровня иерархии.

Способ 3. Использование условных выражений с агрегатными функциями

Иногда логику можно упростить с помощью математических агрегатных функций, таких как Минимум или Максимум, прямо в выражении ресурса. Рассмотрим пример решения, предложенного на форуме:


ВЫБОР 
    КОГДА Минимум(СуммаПлан) < 0 
        ТОГДА 0 
    ИНАЧЕ Сумма(СуммаПлан) 
КОНЕЦ

Этот способ хорошо подходит, когда нам нужно обнулить итог всей группировки, если хотя бы одна из подчиненных детальных записей имеет отрицательное значение. Однако использовать его стоит с осторожностью, так как функция Минимум анализирует исходные строки набора данных, а не рассчитанные итоги промежуточных уровней.

Способ 4. Программная корректировка через вывод в Дерево значений

Если зависимости между статьями слишком сложные (например, расчет значения «Статьи 2» зависит от сложного алгоритма сравнения с показателями «Статьи 1»), стандартных формул СКД может не хватить. В этом случае программисты часто совершают ошибку: полностью отказываются от СКД, пишут ручной вывод в табличный документ или полностью переписывают запросы.

Более изящное и гибкое решение — выполнить компоновку во временное ДеревоЗначений, программно пересчитать в нем итоги по любой логике и затем вывести это дерево в отчет.

Посмотрим на пример реализации в процедуре ПриКомпоновкеРезультата модуля объекта отчета:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // 1. Инициализируем схему и настройки
    СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    // 2. Выполняем компоновку
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
    
    // 3. Выводим результат не в табличный документ, а в Дерево значений
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВДеревоЗначений;
    ДеревоРезультата = Новый ДеревоЗначений;
    ПроцессорВывода.УстановитьОбъект(ДеревоРезультата);
    ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
    
    // 4. Программно обходим полученное дерево и корректируем итоги родительских веток
    КорректироватьИтогиДерева(ДеревоРезультата.Строки);
    
    // 5. Выводим уже скорректированное Дерево значений в итоговый табличный документ
    ДокументРезультат.Очистить();
    ПроцессорВыводаВТабДок = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВыводаВТабДок.УстановитьДокумент(ДокументРезультат);
    
    // Для этого шага можно использовать программный генератор макета на основе скорректированного дерева
    // или специализированные модули вывода.
    
КонецПроцедуры

Этот подход сохраняет все преимущества СКД (динамические отборы, пользовательские настройки, оформление), но предоставляет разработчику 100% контроль над финальными цифрами в отчете.

Рекомендации разработчику

← На главную