Как правильно сформировать и отобразить дерево значений из запроса на форме в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Отображение иерархических данных в виде дерева на форме — распространенная задача при разработке в 1С. Часто разработчики сталкиваются с ситуацией, когда результат запроса с секцией ИТОГИ при прямой выгрузке в дерево значений отображается некорректно: узлы не разворачиваются, значения группировок и детальных записей попадают в разные колонки или родительские строки остаются пустыми. Разберем причины такого поведения платформы и рассмотрим практические способы решения этой задачи.

Анализ причин проблемы

Проанализируем, почему метод Выгрузить() объекта РезультатЗапроса может сформировать неожиданную структуру данных:

  1. Неверный параметр обхода результата запроса. Использование режима ОбходРезультатаЗапроса.ПоГруппировкамСИерархией требует обязательного наличия ключевого слова ИЕРАРХИЯ в блоке ИТОГИ запроса (например, ИТОГИ ПО ГруппаРЦ ИЕРАРХИЯ), а также того, чтобы само группировочное поле ссылалось на иерархический справочник. Если иерархия в запросе не задана, вызов данного режима приводит к ошибкам или искажению структуры узлов. Для обычных группировок следует применять режим ОбходРезультатаЗапроса.ПоГруппировкам.
  2. Разделение колонок группировок и детальных полей. При автоматической выгрузке результата запроса платформа 1С создает в объекте ДеревоЗначений отдельные колонки для каждого поля выборки и каждого поля итогов. В строках-родителях (уровень группы) детальные поля содержат значение NULL, а в подчиненных строках детальных записей поле группы остается незаполненным. В результате данные «разъезжаются» по разным колонкам таблицы.

Способ 1. Ручное заполнение дерева значений через выборку запроса

Если требуется получить строгую иерархическую структуру, где родительские узлы содержат группы, а дочерние строки — подчиненные элементы, наиболее надежным подходом является программный обход выборки запроса. Разберем этот алгоритм по шагам.

Создадим дерево значений с нужными колонками и заполним его с помощью двухуровневого цикла по группировкам и детальным записям:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Полуфабрикаты.ГруппаРЦ КАК ГруппаРЦ,
    |    Полуфабрикаты.Ссылка КАК Полуфабрикат
    |ИЗ
    |    Справочник.Полуфабрикаты КАК Полуфабрикаты
    |ИТОГИ ПО
    |    ГруппаРЦ";

РезультатЗапроса = Запрос.Выполнить();

// Создаем и настраиваем структуру дерева значений
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("ГруппаРЦ");
Дерево.Колонки.Добавить("Полуфабрикат");

// Выполняем обход первого уровня — группировок
ВыборкаГруппаРЦ = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "ГруппаРЦ");
Пока ВыборкаГруппаРЦ.Следующий() Цикл
    
    СтрокаГруппа = Дерево.Строки.Добавить();
    СтрокаГруппа.ГруппаРЦ = ВыборкаГруппаРЦ.ГруппаРЦ;
    
    // Выполняем обход второго уровня — детальных записей группы
    ДетальнаяВыборка = ВыборкаГруппаРЦ.Выбрать();
    Пока ДетальнаяВыборка.Следующий() Цикл
        СтрокаЭлемент = СтрокаГруппа.Строки.Добавить();
        СтрокаЭлемент.Полуфабрикат = ДетальнаяВыборка.Полуфабрикат;
    КонецЦикла;
    
КонецЦикла;

Такой подход позволяет полностью контролировать заполнение колонок на каждом уровне дерева и исключает появление пустых или некорректных строк.

Способ 2. Вывод иерархии в одну общую колонку

Если стоит задача отобразить и группу, и подчиненный элемент в одной и той же иерархической колонке (например, в колонке «Наименование»), создадим колонку составного типа либо типа Строка и будем помещать в нее значения как верхнего, так и вложенного уровня:


Дерево = Новый ДеревоЗначений;
// Добавляем колонку составного типа
Дерево.Колонки.Добавить("ЗначениеИерархии", Новый ОписаниеТипов("СправочникСсылка.ГруппаРЦ, СправочникСсылка.Полуфабрикаты"));

ВыборкаГруппа = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "ГруппаРЦ");
Пока ВыборкаГруппа.Следующий() Цикл
    
    СтрокаРодитель = Дерево.Строки.Добавить();
    СтрокаРодитель.ЗначениеИерархии = ВыборкаГруппа.ГруппаРЦ;
    
    ДетальныеЗаписи = ВыборкаГруппа.Выбрать();
    Пока ДетальныеЗаписи.Следующий() Цикл
        СтрокаПотомок = СтрокаРодитель.Строки.Добавить();
        СтрокаПотомок.ЗначениеИерархии = ДетальныеЗаписи.Полуфабрикат;
    КонецЦикла;
    
КонецЦикла;

Способ 3. Корректная прямая выгрузка без циклов

Если устраивает штатное разделение колонок платформы (когда колонка группы заполнена в строках-группах, а колонка элемента — в подчиненных строках), исключим сложные объединения в запросе и применим метод Выгрузить() с правильным типом обхода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Полуфабрикаты.ГруппаРЦ КАК ГруппаРЦ,
    |    Полуфабрикаты.Ссылка КАК Полуфабрикат
    |ИЗ
    |    Справочник.Полуфабрикаты КАК Полуфабрикаты
    |ИТОГИ ПО
    |    ГруппаРЦ";

// Выгружаем дерево с типом обхода ПоГруппировкам
Дерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);

Особенности отображения дерева на форме

Разберем, как передать сформированное дерево в интерфейс в зависимости от режима работы форм:

  1. Обычные формы: Связываем значение элемента управления ТабличноеПоле со сформированным деревом значений и пересоздаем колонки:
    
    ЭлементыФормы.ТабличноеПоле1.Значение = Дерево;
    ЭлементыФормы.ТабличноеПоле1.СоздатьКолонки();
    
    Убедимся, что для первой видимой колонки установлено свойство отображения иерархического дерева (значка развертывания).
  2. Управляемые формы: Объект ДеревоЗначений не может существовать на клиенте. В серверной процедуре поместим данные дерева в реквизит формы с типом ДеревоЗначений с помощью системной функции: ЗначениеВРеквизитФормы(Дерево, "РеквизитФормыДерево"); В свойствах таблицы формы на клиенте установим параметр Отображение в значение Дерево.

Альтернативный вариант: Использование Системы компоновки данных (СКД)

Если требуется построить сложное дерево с итогами, подсчетом ресурсов и гибкими отборами, рекомендуется использовать схему компоновки данных. СКД берет на себя вычисление уровней иерархии, оформление групп и корректную компоновку полей в единые колонки без необходимости писать ручной код построчного обхода.

← На главную