Разработка и Конфигурация · 11 мин
Как вывести дерево значений в табличный документ в 1С
Как программно вывести дерево значений в табличный документ в 1С: обход дерева, группировки, отступы по уровням, примеры кода и типичные ошибки разработчика.
Дерево значений в 1С — удобная структура для иерархических данных: подразделений, групп номенклатуры, планов счетов с подчинёнными элементами. Но штатного метода «вывести дерево в табличный документ» в платформе нет — ТабличныйДокумент умеет работать с плоскими областями и группировками строк, а иерархию приходится формировать вручную при обходе дерева.
На практике задача решается двумя способами: простым линейным выводом с отступами (каждая строка дерева становится строкой табличного документа, вложенность показывается отступом текста) и выводом с использованием группировок строк табличного документа, когда уровни можно сворачивать и разворачивать. Оба подхода строятся на рекурсивном обходе дерева.
Ниже разобраны оба варианта с готовым кодом, способы оформить отступы и итоги по веткам, а также ограничения, о которых стоит знать до того, как писать собственную процедуру вывода.
Как устроена задача и когда какой подход выбирать
Табличный документ — это линейная сетка ячеек. Единственный механизм иерархии в нём — группировки строк (аналог группировок в Excel), которые создаются методами НачатьГруппуСтрок() и ЗакончитьГруппуСтрок(). Поэтому любой вывод дерева сводится к рекурсивному обходу строк дерева значений и последовательному заполнению областей макета.
Выбор подхода зависит от назначения документа. Если табличный документ нужен для печати или выгрузки в файл — достаточно отступов в тексте: механизм группировок в сохранённом MXL или при печати не даёт интерактива. Если документ показывается пользователю на экране в элементе формы «Поле табличного документа» — группировки строк дают удобные «плюсики» для сворачивания веток.
Сравнение вариантов вывода
Простейший код, работает при печати и в файле. Нет интерактива, при глубокой иерархии текст уходит вправо и может обрезаться.
Сворачиваемые уровни на экране, компактный вид. Требует аккуратного парного вызова Начать/ЗакончитьГруппуСтрок, при печати группы обычно выводятся развёрнутыми.
Отступы плюс группировки: наглядно и на экране, и на бумаге. Кода больше, но это самый универсальный вариант.
Подготовка: дерево значений и макет
Перед выводом убедитесь, что дерево значений заполнено корректно: у каждой строки коллекция подчинённых строк доступна через свойство Строки, а сами данные лежат в колонках, имена которых вы будете использовать при выводе. Типичная структура — колонки «Наименование», «Сумма» и служебная колонка уровня (её можно не хранить, уровень вычисляется при рекурсии).
Для оформления создайте макет типа «Табличный документ» с именованными областями: например, «Шапка» с заголовками колонок и «Строка» с параметрами Наименование, Сумма. Параметры областей заполняются через Область.Параметры.Наименование = ... перед выводом.
Если макет не нужен (простой отчёт для служебных целей), можно обойтись без него и писать значения напрямую в ячейки: ТабДок.Область("R" + НомерСтроки + "C1").Текст = .... Это быстрее пишется, но хуже сопровождается — для прикладных решений макет предпочтительнее.
Рекурсивный обход дерева с отступами
Базовый вариант — процедура, которая принимает коллекцию строк и текущий уровень, выводит каждую строку и вызывает сама себя для подчинённых. Отступ формируется добавлением пробелов или строки-отступа перед наименованием.
Процедура ВывестиСтрокиДерева(Строки, ТабДок, Макет, Уровень)
Для Каждого СтрокаДерева Из Строки Цикл
Область = Макет.ПолучитьОбласть("Строка");
Область.Параметры.Наименование = СтрПовторить(" ", Уровень) + СтрокаДерева.Наименование;
Область.Параметры.Сумма = СтрокаДерева.Сумма;
ТабДок.Вывести(Область);
ВывестиСтрокиДерева(СтрокаДерева.Строки, ТабДок, Макет, Уровень + 1);
КонецЦикла;
КонецПроцедурыНачальный вызов выполняется с уровня 0 для корневых строк дерева: ВывестиСтрокиДерева(Дерево.Строки, ТабДок, Макет, 0). Функция СтрПовторить формирует отступ нужной глубины — обычно хватает 2–4 пробелов на уровень.
Учтите: при глубокой иерархии (более 6–8 уровней) текст с пробелами упирается в ширину колонки. В этом случае лучше увеличить ширину первой колонки в макете, уменьшить шаг отступа или перейти на группировки строк, где визуальный отступ даёт сама платформа.
Вывод с группировками строк (сворачиваемые уровни)
Для интерактивного просмотра вместо отступов (или вместе с ними) используют группировки. Перед выводом подчинённых строк вызывается ТабДок.НачатьГруппуСтрок(), после — ТабДок.ЗакончитьГруппуСтрок(). Платформа сама рисует кнопки сворачивания слева от строк.
Процедура ВывестиСтрокиДерева(Строки, ТабДок, Макет)
Для Каждого СтрокаДерева Из Строки Цикл
Область = Макет.ПолучитьОбласть("Строка");
Область.Параметры.Заполнить(СтрокаДерева);
ТабДок.Вывести(Область);
Если СтрокаДерева.Строки.Количество() > 0 Тогда
ТабДок.НачатьГруппуСтрок(, Ложь);
ВывестиСтрокиДерева(СтрокаДерева.Строки, ТабДок, Макет);
ТабДок.ЗакончитьГруппуСтрок();
КонецЕсли;
КонецЦикла;
КонецПроцедурыВторой параметр НачатьГруппуСтрок определяет, будет ли группа свёрнута при открытии: Ложь — развёрнута, Истина — свёрнута. Для больших деревьев удобно выводить свёрнутыми все уровни, кроме первого, чтобы пользователь видел общую картину и раскрывал нужные ветки.
Оформление: итоги по веткам и визуальное выделение уровней
Часто требуется, чтобы родительская строка показывала сумму по всем подчинённым. Если итоги не рассчитаны заранее, их можно получить в том же рекурсивном обходе: функция возвращает сумму ветки, а родительская строка складывает результаты вызовов. Альтернатива — предварительный расчёт через Дерево.Строки.Итог("Сумма", Истина), который считает итог с учётом иерархии для корневой коллекции.
Для визуального различия уровней заведите в макете несколько областей строк — например, «СтрокаУровень1» с жирным шрифтом и «СтрокаУровень2» обычным — и выбирайте область по значению переменной Уровень. Это читается заметно лучше, чем одни отступы.
Проверка результата и ограничения
После вывода проверьте документ до передачи пользователю: количество строк, порядок следования, корректность итогов и поведение группировок. Удобный приём — сравнить количество выведенных строк с количеством строк дерева, посчитанным рекурсивно.
Как проверить результат
Число выведенных строк равно числу строк дерева (рекурсивный подсчёт)
Строк меньше — потеряны ветки; больше — возможен повторный вывод
Группы сворачиваются и разворачиваются, отступы соответствуют уровням
Группы перекрываются, отступы сбиты, строки вышли из своих веток
Сумма родителя равна сумме подчинённых строк
Расхождения — значит, итоги посчитаны с задвоением или не по всем веткам
При предварительном просмотре все уровни видны и читаемы
Текст обрезан, глубокие уровни ушли за границу страницы
Помните об ограничениях. Табличный документ хранится в памяти целиком: деревья в десятки тысяч строк выводятся медленно и потребляют заметный объём ОЗУ — для таких объёмов рассмотрите постраничный вывод или СКД с иерархической группировкой. Группировки строк не сохраняют интерактивность при выгрузке в Excel-формат старых версий и не работают в веб-клиенте так же, как в тонком — проверяйте целевую среду.
Альтернатива: СКД и поле «Дерево» на форме
Если цель — отчёт, а не произвольная печатная форма, рассмотрите систему компоновки данных: она умеет выводить иерархические группировки из иерархических справочников и запросов с конструкцией ИТОГИ ПО ... ИЕРАРХИЯ без единой строки кода вывода. Дерево значений при этом придётся либо заменить запросом, либо передать в СКД как внешний набор данных.
Для интерактивной работы в форме табличный документ вообще не обязателен: элемент формы «Дерево» отображает дерево значений напрямую, с картинками строк, флажками и редактированием. Табличный документ оправдан, когда нужен именно печатный результат, файл на выгрузку или фиксированная форма с произвольным оформлением.
Как выбрать действие
Что важно запомнить
Дерево значений выводится в табличный документ рекурсивным обходом: каждая строка — одна область макета, вложенность показывается отступами или группировками строк. Контролируйте парность Начать/ЗакончитьГруппуСтрок, итоги считайте без задвоения, а для больших объёмов и отчётных задач рассмотрите СКД.
Частые вопросы
Можно ли вывести дерево значений в табличный документ без макета?
Да. Значения пишутся напрямую в ячейки через обращение к областям вида ТабДок.Область("R" + НомСтроки + "C1").Текст = Значение, а номер строки увеличивается в цикле обхода. Это подходит для служебных выгрузок, но оформление (шрифты, границы) придётся задавать программно для каждой области, что громоздко. Для прикладных печатных форм макет удобнее и проще в сопровождении.
Почему параметры области не заполняются через Область.Параметры.Заполнить(СтрокаДерева)?
Метод Заполнить сопоставляет значения по именам: имя колонки дерева должно совпадать с именем параметра в макете. Если колонка называется «НоменклатураНаименование», а параметр — «Наименование», заполнения не произойдёт, и ошибки платформа не выдаст. Либо приведите имена к соответствию, либо заполняйте параметры по одному явно.
Как вывести дерево свёрнутым до первого уровня?
Передайте в НачатьГруппуСтрок(, Истина) вторым параметром Истина — группа будет создана свёрнутой. Чтобы первый уровень остался развёрнутым, а остальные свёрнулись, проверяйте переменную уровня в рекурсии и передавайте Истина только начиная со второго уровня вложенности.
Сохранятся ли группировки при сохранении документа в Excel?
При сохранении в формате Excel платформа пытается перенести группировки строк как группировки листа, но результат зависит от версии платформы и формата файла: в ряде случаев группы теряются или выводятся развёрнутыми. Если критичен интерактив в Excel, проверяйте результат на целевой версии платформы или выводите отступы в тексте как запасной вариант.
Как посчитать итоги по каждой ветке дерева перед выводом?
Два рабочих способа. Первый — рекурсивная функция, которая возвращает сумму ветки: для листа возвращается его значение, для родителя — сумма результатов вызовов по подчинённым строкам, и это значение записывается в строку. Второй — метод Строки.Итог("ИмяКолонки", Истина) для получения общего итога с учётом иерархии, но он не раскладывает суммы по промежуточным узлам, поэтому для итогов по каждой ветке подходит только рекурсия.
Что делать, если дерево очень большое и вывод тормозит?
Табличный документ формируется в памяти целиком, поэтому десятки тысяч строк — уже риск. Меры: сворачивайте группы по умолчанию, уберите лишнее оформление, отключите авторасчёт ширины колонок. Если этого мало — переходите на СКД с иерархической группировкой или выводите данные порциями с управляемой постраничной разбивкой.