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