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

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

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

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

Дерево значений в 1С — удобная структура для иерархических данных: подразделений, групп номенклатуры, планов счетов с подчинёнными элементами. Но штатного метода «вывести дерево в табличный документ» в платформе нет — ТабличныйДокумент умеет работать с плоскими областями и группировками строк, а иерархию приходится формировать вручную при обходе дерева.

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

Ниже разобраны оба варианта с готовым кодом, способы оформить отступы и итоги по веткам, а также ограничения, о которых стоит знать до того, как писать собственную процедуру вывода.

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

Табличный документ — это линейная сетка ячеек. Единственный механизм иерархии в нём — группировки строк (аналог группировок в Excel), которые создаются методами НачатьГруппуСтрок() и ЗакончитьГруппуСтрок(). Поэтому любой вывод дерева сводится к рекурсивному обходу строк дерева значений и последовательному заполнению областей макета.

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

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

Отступы в тексте

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

Группировки строк

Сворачиваемые уровни на экране, компактный вид. Требует аккуратного парного вызова Начать/ЗакончитьГруппуСтрок, при печати группы обычно выводятся развёрнутыми.

Комбинированный

Отступы плюс группировки: наглядно и на экране, и на бумаге. Кода больше, но это самый универсальный вариант.

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

Перед выводом убедитесь, что дерево значений заполнено корректно: у каждой строки коллекция подчинённых строк доступна через свойство Строки, а сами данные лежат в колонках, имена которых вы будете использовать при выводе. Типичная структура — колонки «Наименование», «Сумма» и служебная колонка уровня (её можно не хранить, уровень вычисляется при рекурсии).

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

Если макет не нужен (простой отчёт для служебных целей), можно обойтись без него и писать значения напрямую в ячейки: ТабДок.Область("R" + НомерСтроки + "C1").Текст = .... Это быстрее пишется, но хуже сопровождается — для прикладных решений макет предпочтительнее.

Рекурсивный обход дерева с отступами

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

bsl
Процедура ВывестиСтрокиДерева(Строки, ТабДок, Макет, Уровень)
	Для Каждого СтрокаДерева Из Строки Цикл
		Область = Макет.ПолучитьОбласть("Строка");
		Область.Параметры.Наименование = СтрПовторить("    ", Уровень) + СтрокаДерева.Наименование;
		Область.Параметры.Сумма = СтрокаДерева.Сумма;
		ТабДок.Вывести(Область);
		ВывестиСтрокиДерева(СтрокаДерева.Строки, ТабДок, Макет, Уровень + 1);
	КонецЦикла;
КонецПроцедуры

Начальный вызов выполняется с уровня 0 для корневых строк дерева: ВывестиСтрокиДерева(Дерево.Строки, ТабДок, Макет, 0). Функция СтрПовторить формирует отступ нужной глубины — обычно хватает 2–4 пробелов на уровень.

Учтите: при глубокой иерархии (более 6–8 уровней) текст с пробелами упирается в ширину колонки. В этом случае лучше увеличить ширину первой колонки в макете, уменьшить шаг отступа или перейти на группировки строк, где визуальный отступ даёт сама платформа.

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

Для интерактивного просмотра вместо отступов (или вместе с ними) используют группировки. Перед выводом подчинённых строк вызывается ТабДок.НачатьГруппуСтрок(), после — ТабДок.ЗакончитьГруппуСтрок(). Платформа сама рисует кнопки сворачивания слева от строк.

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

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

Оформление: итоги по веткам и визуальное выделение уровней

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

Для визуального различия уровней заведите в макете несколько областей строк — например, «СтрокаУровень1» с жирным шрифтом и «СтрокаУровень2» обычным — и выбирайте область по значению переменной Уровень. Это читается заметно лучше, чем одни отступы.

Проверка результата и ограничения

После вывода проверьте документ до передачи пользователю: количество строк, порядок следования, корректность итогов и поведение группировок. Удобный приём — сравнить количество выведенных строк с количеством строк дерева, посчитанным рекурсивно.

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

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

Количество строк
Норма

Число выведенных строк равно числу строк дерева (рекурсивный подсчёт)

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

Строк меньше — потеряны ветки; больше — возможен повторный вывод

Иерархия на экране
Норма

Группы сворачиваются и разворачиваются, отступы соответствуют уровням

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

Группы перекрываются, отступы сбиты, строки вышли из своих веток

Итоги
Норма

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

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

Расхождения — значит, итоги посчитаны с задвоением или не по всем веткам

Печатная форма
Норма

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

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

Текст обрезан, глубокие уровни ушли за границу страницы

Помните об ограничениях. Табличный документ хранится в памяти целиком: деревья в десятки тысяч строк выводятся медленно и потребляют заметный объём ОЗУ — для таких объёмов рассмотрите постраничный вывод или СКД с иерархической группировкой. Группировки строк не сохраняют интерактивность при выгрузке в Excel-формат старых версий и не работают в веб-клиенте так же, как в тонком — проверяйте целевую среду.

Типичные ошибки
01
Обход только первого уровня: разработчик выводит Дерево.Строки без рекурсии — подчинённые строки теряются.
02
Незакрытые группы строк из-за Прервать/Возврат внутри цикла — документ получает битую структуру группировок.
03
Отступы неразрывными пробелами в перемешку с обычными — при выгрузке в Excel сортировка и поиск по наименованию работают некорректно.
04
Заполнение параметров области напрямую из строки дерева через Заполнить() при несовпадении имён колонок и параметров — параметры остаются пустыми без ошибки.
05
Вывод огромного дерева целиком без сворачивания — пользователь получает документ на тысячи строк и не может с ним работать.

Альтернатива: СКД и поле «Дерево» на форме

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

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

Маршрут решения

Как выбрать действие

Нужна печатная форма или файл MXL/ExcelРекурсивный обход дерева с выводом в табличный документ и отступами
Документ смотрят на экране и сворачивают веткиДобавьте группировки строк через НачатьГруппуСтрок/ЗакончитьГруппуСтрок
Нужен полноценный отчёт с отборами и настройкамиИспользуйте СКД с иерархической группировкой вместо ручного вывода
Нужно редактирование иерархии пользователемВыводите дерево значений в элемент формы «Дерево», а не в табличный документ
Итог

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

Дерево значений выводится в табличный документ рекурсивным обходом: каждая строка — одна область макета, вложенность показывается отступами или группировками строк. Контролируйте парность Начать/ЗакончитьГруппуСтрок, итоги считайте без задвоения, а для больших объёмов и отчётных задач рассмотрите СКД.

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

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

Да. Значения пишутся напрямую в ячейки через обращение к областям вида ТабДок.Область("R" + НомСтроки + "C1").Текст = Значение, а номер строки увеличивается в цикле обхода. Это подходит для служебных выгрузок, но оформление (шрифты, границы) придётся задавать программно для каждой области, что громоздко. Для прикладных печатных форм макет удобнее и проще в сопровождении.

Почему параметры области не заполняются через Область.Параметры.Заполнить(СтрокаДерева)?

Метод Заполнить сопоставляет значения по именам: имя колонки дерева должно совпадать с именем параметра в макете. Если колонка называется «НоменклатураНаименование», а параметр — «Наименование», заполнения не произойдёт, и ошибки платформа не выдаст. Либо приведите имена к соответствию, либо заполняйте параметры по одному явно.

Как вывести дерево свёрнутым до первого уровня?

Передайте в НачатьГруппуСтрок(, Истина) вторым параметром Истина — группа будет создана свёрнутой. Чтобы первый уровень остался развёрнутым, а остальные свёрнулись, проверяйте переменную уровня в рекурсии и передавайте Истина только начиная со второго уровня вложенности.

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

При сохранении в формате Excel платформа пытается перенести группировки строк как группировки листа, но результат зависит от версии платформы и формата файла: в ряде случаев группы теряются или выводятся развёрнутыми. Если критичен интерактив в Excel, проверяйте результат на целевой версии платформы или выводите отступы в тексте как запасной вариант.

Как посчитать итоги по каждой ветке дерева перед выводом?

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

Что делать, если дерево очень большое и вывод тормозит?

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