Как вывести результат запроса в табличный документ с сохранением иерархии?

Программист 1С v8.3 (Управляемые формы)
← На главную

При разработке отчетов в 1С часто возникает задача вывести данные не плоским списком, а в виде дерева, где дочерние элементы подчинены родительским. Типичный пример — дерево ресурсных спецификаций или иерархия номенклатуры с текущими остатками на складах. Если по каким-то причинам использование Системы компоновки данных (СКД) невозможно или нецелесообразно, нам приходится реализовывать этот механизм программно с помощью обработки результата запроса и вывода в табличный документ.

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

Способ 1. Использование итогов в запросе и обход по группировкам

Самый эффективный способ построения иерархии в табличном документе «вручную» — это использование секции ИТОГИ в тексте запроса. Без этой секции результат запроса всегда будет линейным (плоским), и метод Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) не даст ожидаемого эффекта.

Проанализируем ситуацию: у нас есть исходные данные (например, из таблицы значений), которые мы помещаем во временную таблицу, и нам нужно соединить их с остатками. Чтобы получить дерево, мы должны добавить группировку в итогах. Рассмотрим пример кода:


// 1. Подготовка текста запроса с итогами
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Данные.Номенклатура КАК НоменклатураРодитель,
    |    Данные.ЕдиницаИзмерения,
    |    Данные.Количество
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |    &Данные КАК Данные
    |;
    |
    |ВЫБРАТЬ
    |    ВТ_Данные.НоменклатураРодитель КАК Спецификация,
    |    ВТ_Данные.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    |    ВТ_Данные.Количество КАК Количество,
    |    СвободныеОстатки.Склад КАК Склад,
    |    ЕСТЬNULL(СвободныеОстатки.ВНаличии, 0) КАК ВНаличии
    |ИЗ
    |    ВТ_Данные КАК ВТ_Данные
    |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СвободныеОстатки.Остатки КАК СвободныеОстатки
    |        ПО ВТ_Данные.НоменклатураРодитель = СвободныеОстатки.Номенклатура
    |ИТОГИ
    |    СУММА(Количество),
    |    СУММА(ВНаличии)
    |ПО
    |    Спецификация ИЕРАРХИЯ";

Запрос.УстановитьПараметр("Данные", ТЗ);
РезультатЗапроса = Запрос.Выполнить();

Обратите внимание на конструкцию ИТОГИ ... ПО Спецификация ИЕРАРХИЯ. Именно она подготавливает структуру для дерева. Теперь нам нужно правильно вывести эти данные в макет. Для этого мы будем использовать вложенные циклы, каждый из которых отвечает за свой уровень иерархии.

Разберем алгоритм вывода:


// 2. Вывод в табличный документ
ТабДок.Очистить();
ТабДок.НачатьАвтогруппировкуСтрок();

// Получаем области из макета
ОбластьГруппировка = Макет.ПолучитьОбласть("Группировка");
ОбластьДетали = Макет.ПолучитьОбласть("Детали");

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

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

ТабДок.ЗакончитьАвтогруппировкуСтрок();
// Установим отображение плюсиков сверху
ТабДок.РасположениеГруппировокСтрок = РасположениеГруппировокТабличногоДокумента.Верх;

Важные нюансы при работе с уровнями

При формировании дерева в ТабличныйДокумент крайне важно следить за тем, какое число передается вторым параметром в метод Вывести(). Этот параметр определяет «вложенность» строки.

  1. Начало группировки: Обязательно вызывайте НачатьАвтогруппировкуСтрок() перед выводом строк и ЗакончитьАвтогруппировкуСтрок() после окончания цикла.
  2. Значение уровня: Метод Выборка.Уровень() возвращает число, начиная с 0. Если вы хотите сделать сложную вложенность, убедитесь, что уровни идут последовательно (нельзя после уровня 0 сразу вывести уровень 5, это может привести к некорректному отображению дерева).
  3. Скрытие пустых итогов: При использовании ЛЕВОЕ СОЕДИНЕНИЕ в итогах могут появиться пустые значения (NULL). Всегда используйте функцию ЕСТЬNULL() в тексте запроса, чтобы отчет выглядел аккуратно.

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

Если структура вашего отчета предполагает неограниченную вложенность (например, спецификация внутри спецификации), использование вложенных циклов «Пока Следующий» становится неудобным. В этом случае мы рекомендуем выгрузить результат запроса в объект ДеревоЗначений и использовать рекурсивную процедуру.

Проанализируем пример такой реализации:


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

// Вызываем процедуру рекурсивного обхода
ВывестиСтрокиДереваРекурсивно(ДеревоРезультат.Строки, ТабДок, Макет);

// Сама процедура
Процедура ВывестиСтрокиДереваРекурсивно(СтрокиДерева, ТабДок, Макет)
    Для Каждого Строка Из СтрокиДерева Цикл
        
        // Определяем, какую область макета использовать
        Область = Макет.ПолучитьОбласть("СтрокаДерева");
        Область.Параметры.Заполнить(Строка);
        
        // Выводим строку с указанием уровня
        ТабДок.Вывести(Область, Строка.Уровень());
        
        // Рекурсивный вызов для подчиненных строк
        Если Строка.Строки.Количество() > 0 Тогда
            ВывестиСтрокиДереваРекурсивно(Строка.Строки, ТабДок, Макет);
        КонецЕсли;
        
    КонецЦикла;
КонецПроцедуры

Этот подход универсален: сколько бы уровней вложенности ни было в ваших данных, процедура обработает их все автоматически, сохраняя правильную структуру группировок в табличном документе.

Настройка внешнего вида группировок

Чтобы отчет выглядел профессионально, после завершения вывода данных стоит применить дополнительные настройки ТабличногоДокумента:

  1. Управление видимостью: Если вы хотите, чтобы при открытии отчета все группы были свернуты, используйте метод ТабДок.ПоказатьУровеньГруппировокСтрок(0).
  2. Защита данных: Установка свойств ТабДок.ТолькоПросмотр = Истина позволит пользователю комфортно нажимать на «плюсики» группировок без риска случайно изменить данные в ячейках.
  3. Фиксация заголовков: Если отчет длинный, полезно зафиксировать шапку таблицы через свойство ТабДок.ФиксацияСверху.

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

← На главную