К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как вывести дерево значений в макет 1С

Разбираем, как вывести дерево значений в табличный документ 1С: обход дерева рекурсией, макет с областями, отступы по уровням. Примеры кода и типичные ошибки.

Дерево значений в 1С — удобная структура для иерархических данных: группировок номенклатуры, структуры организации, вложенных заказов. Но в отличие от таблицы значений, его нельзя просто передать в табличный документ одним методом — платформа не умеет выводить иерархию автоматически. Разработчику нужно самостоятельно обойти дерево и построчно заполнить макет.

Типовая задача выглядит так: есть макет с областями «Шапка», «Строка», «Подвал» и дерево значений с колонками, например «Наименование», «Сумма», «Количество». Требуется получить печатную форму, где родительские строки идут перед вложенными, а уровень вложенности виден визуально — отступом или оформлением.

Ниже разобран рабочий подход: подготовка макета, рекурсивный обход дерева, вывод отступов по уровням и контроль результата. Примеры кода приведены на встроенном языке 1С и подходят для управляемых и обычных форм, так как работа с табличным документом выполняется на сервере.

Подготовка дерева значений и макета

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

Макет создаётся как обычный макет табличного документа (общий макет или макет объекта). Минимальный набор областей: «Шапка» с заголовками колонок, «Строка» — для обычной строки данных, при необходимости «Группа» — для родительских строк с другим оформлением, и «Подвал» с итогами. В ячейках области «Строка» задаются параметры, имена которых должны совпадать с именами колонок дерева — тогда заполнение сводится к одному вызову.

Рекурсивный обход дерева значений

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

Процедура ВывестиСтрокиДерева(Строки, ТабДок, Макет, Уровень)

ОбластьСтрока = Макет.ПолучитьОбласть("Строка");

Для Каждого СтрокаДерева Из Строки Цикл

ОбластьСтрока.Параметры.Заполнить(СтрокаДерева);

ОбластьСтрока.Параметры.Уровень = Уровень;

ТабДок.Вывести(ОбластьСтрока, Уровень);

ВывестиСтрокиДерева(СтрокаДерева.Строки, ТабДок, Макет, Уровень + 1);

КонецЦикла;

КонецПроцедуры

Метод Заполнить() у параметров области переносит значения из строки дерева по совпадающим именам — это избавляет от ручного присваивания каждого параметра. Второй параметр метода Вывести() задаёт уровень группировки табличного документа: благодаря ему в готовой форме пользователь сможет сворачивать и разворачивать группы строк кнопками уровней, как в отчётах.

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

ТабДок = Новый ТабличныйДокумент;

Макет = ПолучитьМакет("МакетДерева");

ТабДок.Вывести(Макет.ПолучитьОбласть("Шапка"));

ВывестиСтрокиДерева(Дерево.Строки, ТабДок, Макет, 0);

ТабДок.Вывести(Макет.ПолучитьОбласть("Подвал"));

Порядок действий
  1. 1
    Создайте макет табличного документа с областями Шапка, Строка, Подвал и параметрами в ячейках
  2. 2
    Проверьте, что имена параметров совпадают с именами колонок дерева значений
  3. 3
    Напишите рекурсивную процедуру обхода строк с параметром уровня
  4. 4
    Внутри цикла заполняйте параметры области методом Заполнить() и выводите её в табличный документ
  5. 5
    Вызовите процедуру для коллекции Строки корневого уровня
  6. 6
    Откройте табличный документ и проверьте порядок строк и отступы

Отступы и визуализация уровней вложенности

Чтобы иерархия читалась на печати, уровень нужно показать визуально. Есть два рабочих способа. Первый — отступ пробелами или неразрывными пробелами в самом тексте: параметр наименования формируется с префиксом, длина которого зависит от уровня. Способ прост, но при выгрузке в Excel отступы «прилипают» к тексту.

Второй способ чище: отступ задаётся оформлением ячейки. В макете создаётся параметр «Отступ», а в самой ячейке в свойстве «Отступ» указывается выражение через параметр, либо готовится несколько областей «Строка1», «Строка2», «Строка3» с разным отступом, и процедура выбирает область по уровню:

ИмяОбласти = "Строка" + Мин(Уровень + 1, 4);

ОбластьСтрока = Макет.ПолучитьОбласть(ИмяОбласти);

Функция Мин() ограничивает уровень, чтобы при глубокой иерархии не возникло обращения к несуществующей области. Дополнительно родительские строки можно выделять жирным шрифтом — заведите в макете отдельную область «Группа» и проверяйте наличие вложенных строк: Если СтрокаДерева.Строки.Количество() > 0 Тогда.

Если печатную форму будут выгружать в Excel и фильтровать, откажитесь от отступов пробелами в тексте. Лучше выведите уровень в отдельную скрытую колонку — это сохранит и иерархию, и чистые данные.
ПЗ
Практическое замечание

Вывод итогов по группам и сворачивание строк

Часто требуется, чтобы родительская строка показывала сумму по потомкам. Если дерево построено запросом с итогами, итоговые значения уже лежат в родительских строках — дополнительных расчётов не нужно. Если дерево собрано вручную, суммы считаются при обходе: после рекурсивного вызова по вложенным строкам накапливайте итог и присваивайте его родителю, либо выводите область «ИтогГруппы» после возврата из рекурсии.

Группировки табличного документа (второй параметр метода Вывести()) дают пользователю кнопки управления уровнями в левом верхнем углу формы. Чтобы они работали корректно, уровень при выводе должен строго соответствовать вложенности, а сами группировки формируются автоматически по мере вывода. При необходимости можно программно свернуть строки методом ТабДок.ПоказатьУровеньГруппировокСтрок(0) после формирования.

Альтернатива: вывод дерева через СКД

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

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

Сравнение вариантов

Ручной обход дерева в макет

Полный контроль оформления и структуры; подходит для регламентированных печатных форм. Требует написания и поддержки кода обхода.

СКД с иерархическими группировками

Быстрая разработка, отборы и варианты из коробки; подходит для отчётов. Ограниченная гибкость оформления отдельных уровней.

Запрос с ИТОГИ ПО и обходом по группировкам

Итоги считает запрос; хорошая середина, когда данные берутся из одного источника, но форма выводится вручную.

Типичные ошибки и проверка результата

Первая ошибка — перепутанный порядок вывода: разработчик выводит вложенные строки до родительской или забывает рекурсивный вызов, и иерархия «сплющивается». Вторая — несовпадение имён параметров макета и колонок дерева, из-за чего ячейки пустые. Третья — утечка уровня: параметр уровня не увеличивается при рекурсии либо не сбрасывается, и отступы растут бесконечно.

Отдельная группа проблем — производительность и объём. Получение области макета ПолучитьОбласть() внутри цикла на каждой строке замедляет формирование: получайте область один раз до цикла, если она одна для всех строк. Также проверьте, что в дереве нет зацикленных ссылок (строка, являющаяся потомком самой себя), — рекурсия уйдёт в бесконечный цикл. Такое возможно при ручном построении дерева по ссылочным данным; защититесь ограничением максимальной глубины.

Типичные ошибки
01
Имена параметров макета не совпадают с именами колонок дерева — ячейки выводятся пустыми
02
Уровень не увеличивается в рекурсивном вызове — отступы и группировки не работают
03
ПолучитьОбласть() вызывается на каждой строке — форма формируется медленно
04
Нет ограничения глубины рекурсии — при зацикленных данных платформа падает по переполнению стека
05
Забыт второй параметр у Вывести() — пользователь не может сворачивать группы

Проверка результата проста: выведите форму и сверьте порядок строк с деревом в отладчике (таблицу дерева удобно смотреть через «Вычислить выражение»). Родитель всегда идёт выше своих потомков, отступы монотонно растут с уровнем, итоги родителя равны сумме потомков. Кнопки уровней группировок должны сворачивать форму до верхнего уровня без «потерянных» строк.

Контрольные точки

Как проверить результат

Порядок строк
Норма

Каждый родитель выведен перед своими потомками

Тревожный признак

Вложенные строки идут раньше родителя или вперемешку

Отступы
Норма

Отступ растёт на один шаг с каждым уровнем

Тревожный признак

Все строки с одинаковым отступом или отступы скачут

Итоги групп
Норма

Сумма родителя равна сумме потомков

Тревожный признак

Итоги расходятся или равны нулю

Сворачивание
Норма

Кнопки уровней сворачивают форму без потери строк

Тревожный признак

При сворачивании строки исчезают или дублируются

Частые вопросы

Можно ли вывести дерево значений в макет без рекурсии?

Да, через явный стек или преобразование дерева в плоскую таблицу с колонкой «Уровень» и последующим выводом в цикле. На практике рекурсия проще и читаемее; итеративный вариант имеет смысл только при очень глубоких деревьях, где есть риск переполнения стека.

Почему параметры области не заполняются методом Заполнить()?

Метод переносит значения только по совпадающим именам. Проверьте, что параметр объявлен именно в свойствах ячейки макета (поле «Заполнение» — «Параметр»), а имя в точности совпадает с именем колонки дерева, включая регистр букв и отсутствие пробелов.

Как вывести дерево в уже существующий табличный документ с другими данными?

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

Как сделать, чтобы при печати группы были изначально свёрнуты?

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

Что делать, если дерево строится из запроса, а не вручную?

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

Итог

Что важно запомнить

Дерево значений выводится в макет рекурсивным обходом: заполнение параметров области, Вывести() с указанием уровня группировки, рекурсия по вложенным строкам. Имена параметров должны совпадать с именами колонок, уровень — ограничиваться, а для отчётов без жёсткой формы выгоднее использовать СКД.