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