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