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