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