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