Работа с деревьями значений в 1С:Предприятие — одна из самых востребованных задач при разработке отчетов, обработок и аналитических форм. Однако стандартные механизмы платформы не всегда предоставляют удобные инструменты для визуализации иерархических данных. Вывод такого дерева в табличный документ позволяет не только наглядно представить структуру, но и экспортировать её в Excel, PDF или распечатать. Эта статья охватывает все актуальные способы решения задачи — от простых встроенных методов до сложных алгоритмов с рекурсией и динамическим форматированием.

Особенность работы с деревьями в 1С 8.3 заключается в том, что платформа оперирует объектами типа ДеревоЗначений, которые не имеют прямого метода экспорта в таблицу. Разработчику приходится вручную обходить узлы, учитывать уровни вложенности и настраивать отступы. Мы разберём универсальный алгоритм с поддержкой произвольной глубины вложенности, который работает даже для деревьев с 10+ уровнями, а также оптимизированные варианты для типовых задач (например, вывод справочников с иерархией).

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

1. Что такое дерево значений и зачем его выводить в табличный документ

Объект ДеревоЗначений в представляет собой иерархическую структуру данных, где каждый элемент (узел) может содержать дочерние элементы. Классические примеры использования:

  • 📁 Справочники с иерархией (номенклатура, контрагенты, подразделения)
  • 📊 Аналитические отчеты с группировкой по периодам/категориям
  • 🔄 Логи операций (например, история изменений документа)
  • 📈 Планы видов характеристик (для сложных аналитических разрезов)

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

  • 🖨️ Печать иерархических данных с сохранением структуры (например, для инвентаризационных описей).
  • 📤 Экспорт в Excel для дальнейшей аналитики в сторонних инструментах.
  • 👁️ Визуализация сложных структур (например, дерево подчинённости сотрудников).
  • 🔍 Отладка алгоритмов, работающих с рекурсивными данными.

Главное отличие от стандартного вывода через ПолеФормы или ТаблицаЗначений — возможность гибко настраивать отображение: задавать отступы для уровней, применять условное форматирование, добавлять служебные колонки (например, с количеством дочерних элементов). Без такого вывода анализ многомерных данных в часто превращается в утомительное прокручивание списков.

⚠️ Внимание: В версиях 1С:Предприятие 8.3.20+ появился метод ЗаписатьДеревоЗначений для табличного документа, но он работает только с простыми структурами (без рекурсивных ссылок) и не поддерживает кастомизацию отступов. Для сложных случаев всё равно потребуется ручная обработка.

2. Подготовка данных: как правильно сформировать дерево значений

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

2.1. Создание дерева вручную (для тестирования)

Для отладки алгоритмов удобно создавать тестовое дерево прямо в коде. Пример с 3 уровнями вложенности:

Дерево = Новый ДеревоЗначений;

КорневойУзел = Дерево.Добавить("Электроника"); // Уровень 0

// Уровень 1

УзелНоутбуки = Дерево.Добавить("Ноутбуки", КорневойУзел);

УзелСмартфоны = Дерево.Добавить("Смартфоны", КорневойУзел);

// Уровень 2

Дерево.Добавить("Apple", УзелСмартфоны);

Дерево.Добавить("Samsung", УзелСмартфоны);

Дерево.Добавить("HP", УзелНоутбуки);

Дерево.Добавить("Lenovo", УзелНоутбуки);

Обратите внимание на параметр Родитель в методе Добавить — он определяет вложенность. Если его не указать, элемент станет корневым.

2.2. Загрузка дерева из справочника с иерархией

Для работы с реальными данными чаще всего используется метод ВыгрузитьДерево у менеджера справочника. Пример для справочника Номенклатура:

Справочник = Справочники.Номенклатура;

Дерево = Справочник.ВыгрузитьДерево(

Новый Структура("Иерархия, ПометкаУдаления", Истина, Ложь)

);

Важные параметры структуры:

  • Иерархия — если Ложь, вернётся плоский список.
  • ПометкаУдаления — фильтр по удалённым элементам.
  • Отбор — дополнительные условия (например, "ВидыНоменклатуры = &ВидыНоменклатуры.Товар").
⚠️ Внимание: Метод ВыгрузитьДерево возвращает объект типа ДеревоЗначений, но его структура зависит от настроек справочника. Если иерархия построена по Родителю, а не по ЭтомуЭлементу, могут возникнуть проблемы с рекурсией. Проверяйте настройку"Иерархический справочник" в конфигураторе.
📊 Какой метод формирования дерева вы используете чаще?
Создаю вручную для тестов
Выгружаю из справочника
Получаю из внешнего источника
Использую рекурсивные запросы

3. Базовый метод вывода: обход дерева с рекурсией

Самый надёжный способ вывести дерево в табличный документ — рекурсивный обход узлов с учётом уровня вложенности. Этот метод работает в любых версиях 1С 8.x и поддерживает произвольную глубину.

3.1. Алгоритм с отступами по уровням

Основная идея: для каждого узла добавляем строку в таблицу, а перед названием ставим отступ, пропорциональный уровню вложенности. Пример кода:

Процедура ВывестиДеревоВТаблицу(Дерево, ТабличныйДокумент)

// Настраиваем колонки

Область = ТабличныйДокумент.Область(1, 1, 1, 2);

Область.Текст ="Наименование||Уровень";

Область.ШрифтЖирный = Истина;

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

ТекущаяСтрока = 2;

ОбойтиУзлы(Дерево, ТабличныйДокумент, ТекущаяСтрока, 0);

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

Процедура ОбойтиУзлы(Узел, ТабличныйДокумент, ТекущаяСтрока, Уровень)

Если Узел = Неопределено Тогда

Возврат;

КонецЕсли;

// Добавляем строку с отступом

Отступ = Строка(Повтор(Символ(9), Уровень)); // Символ табуляции

Область = ТабличныйДокумент.Область(ТекущаяСтрока, 1, ТекущаяСтрока, 2);

Область.Текст = Отступ + Узел.Значение +"|" + Уровень;

// Обходим дочерние узлы

Для Каждого ДочернийУзел Из Узел.Элементы Цикл

ТекущаяСтрока = ТекущаяСтрока + 1;

ОбойтиУзлы(ДочернийУзел, ТабличныйДокумент, ТекущаяСтрока, Уровень + 1);

КонецЦикла;

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

Ключевые моменты:

  • 🔹 Используется символ табуляции (Символ(9)) для отступов — его ширина настраивается в свойствах табличного документа.
  • 🔹 Рекурсия позволяет обработать любое количество уровней без жёсткого ограничения.
  • 🔹 Колонка"Уровень" добавлена для наглядности, но её можно убрать или заменить на другие данные (например, количество дочерних элементов).

3.2. Оптимизация для больших деревьев

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

Процедура ВывестиДеревоИтеративно(Дерево, ТабличныйДокумент)

Стек = Новый Массив;

Стек.Добавить(Новый Структура("Узел,Уровень", Дерево, 0));

ТекущаяСтрока = 2;

Пока Стек.Количество > 0 Цикл

Текущий = Стек.Удалить(0);

Узел = Текущий.Узел;

Уровень = Текущий.Уровень;

// Добавляем строку

Область = ТабличныйДокумент.Область(ТекущаяСтрока, 1, ТекущаяСтрока, 2);

Область.Текст = Строка(Повтор("", Уровень)) + Узел.Значение +"|" + Уровень;

// Дочерние узлы добавляем в стек в обратном порядке (чтобы обрабатывались слева направо)

Для Инд = Узел.Элементы.Количество - 1 По 0 Шаг -1 Цикл

Стек.Добавить(Новый Структура("Узел,Уровень", Узел.Элементы[Инд], Уровень + 1));

КонецЦикла;

ТекущаяСтрока = ТекущаяСтрока + 1;

КонецЦикла;

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

Итеративный подход избегает рекурсии, но требует больше памяти для хранения стека. Он незаменим для деревьев глубиной >100 уровней.

☑️ Подготовка к выводу дерева

Выполнено: 0 / 5

4. Расширенные возможности: форматирование и дополнительные колонки

Базовый вывод часто недостаточно нагляден. Рассмотрим, как улучшить визуализацию:

4.1. Цветовая маркировка уровней

Чтобы визуально отделить уровни, можно раскрасить строки. Например, чередовать серый и белый фон:

Процедура ОбойтиУзлыСЦветами(Узел, ТабличныйДокумент, ТекущаяСтрока, Уровень)

Если Узел = Неопределено Тогда

Возврат;

КонецЕсли;

// Определяем цвет фона

ЦветФона =?(Уровень % 2 = 0, RGB(240, 240, 240), RGB(255, 255, 255));

Область = ТабличныйДокумент.Область(ТекущаяСтрока, 1, ТекущаяСтрока, 2);

Область.Текст = Строка(Повтор("", Уровень)) + Узел.Значение +"|" + Уровень;

Область.ЦветФона = ЦветФона;

// Рекурсия для дочерних узлов

Для Каждого ДочернийУзел Из Узел.Элементы Цикл

ТекущаяСтрока = ТекущаяСтрока + 1;

ОбойтиУзлыСЦветами(ДочернийУзел, ТабличныйДокумент, ТекущаяСтрока, Уровень + 1);

КонецЦикла;

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

Для более сложного форматирования можно использовать:

  • 🎨 Шрифты: выделять корневые узлы жирным, листья — курсивом.
  • 🔲 Границы: рисовать линии между связанными узлами.
  • 📌 Иконки: добавлять значки папок (📁) для узлов с дочерними элементами.

4.2. Добавление служебных колонок

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

Колонка Пример данных Как получить
Количество дочерних элементов 5 Узел.Элементы.Количество
Полный путь Электроника → Ноутбуки → HP Рекурсивный сбор с родительских узлов
Тип узла Группа / Элемент ?(Узел.Элементы.Количество > 0,"Группа","Элемент")
Ссылка на объект Справочник.Номенклатура.НоутбукHP Узел.Значение.Ссылка (если дерево из справочника)

Пример кода с дополнительными колонками:

Процедура ОбойтиУзлыРасширенно(Узел, ТабличныйДокумент, ТекущаяСтрока, Уровень, Путь ="")

Если Узел = Неопределено Тогда

Возврат;

КонецЕсли;

// Формируем полный путь

ТекущийПуть =?(Путь ="", Узел.Значение, Путь +" →" + Узел.Значение);

ТипУзла =?(Узел.Элементы.Количество > 0,"Группа","Элемент");

Область = ТабличныйДокумент.Область(ТекущаяСтрока, 1, ТекущаяСтрока, 5);

Область.Текст = Строка(Повтор("", Уровень)) + Узел.Значение +"|" +

Уровень +"|" +

Узел.Элементы.Количество +"|" +

ТипУзла +"|" +

ТекущийПуть;

// Рекурсия

Для Каждого ДочернийУзел Из Узел.Элементы Цикл

ТекущаяСтрока = ТекущаяСтрока + 1;

ОбойтиУзлыРасширенно(ДочернийУзел, ТабличныйДокумент, ТекущаяСтрока, Уровень + 1, ТекущийПуть);

КонецЦикла;

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

💡

Для деревьев с глубиной >5 уровней рекомендуется использовать символ"│" (вертикальная черта) вместо табуляции для отступов. Это визуально связывает родительские и дочерние узлы. Пример: Область.Текст = Строка(Повтор("│", Уровень)) +"├─" + Узел.Значение;

5. Продвинутые техники: динамическое форматирование и экспорт

Для профессиональных отчётов базовых методов недостаточно. Рассмотрим продвинутые приёмы:

5.1. Автоматическая настройка ширины колонок

Чтобы таблица выглядела аккуратно, ширину колонок нужно подогнать под содержимое. Делается это после заполнения данных:

Процедура АвтоПодборШириныКолонок(ТабличныйДокумент)

// Проходим по всем колонкам

Для НомерКолонки = 1 По ТабличныйДокумент.КоличествоКолонок Цикл

МаксДлина = 0;

// Ищем самую длинную строку в колонке

Для НомерСтроки = 1 По ТабличныйДокумент.КоличествоСтрок Цикл

Текст = ТабличныйДокумент.Область(НомерСтроки, НомерКолонки).Текст;

Если СтрДлина(Текст) > МаксДлина Тогда

МаксДлина = СтрДлина(Текст);

КонецЕсли;

КонецЦикла;

// Устанавливаем ширину с запасом

ТабличныйДокумент.Колонка(НомерКолонки).Ширина = МаксДлина * 8; // 8 пикселей на символ

КонецЦикла;

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

5.2. Экспорт в Excel с сохранением иерархии

Табличный документ можно сохранить в Excel с сохранением форматирования:

Процедура ЭкспортироватьВExcel(ТабличныйДокумент, ИмяФайла)

// Настройки экспорта

Параметры = Новый Структура;

Параметры.Вставить("Формат","Excel2007"); // или"Excel2010","Excel2013"

Параметры.Вставить("ИмяЛиста","Дерево значений");

// Сохраняем файл

ТабличныйДокумент.Записать(ИмяФайла,, Параметры);

// Открываем файл (опционально)

Попытка

Система = Новый COMОбъект("WScript.Shell");

Система.Run(ИмяФайла, 1, Ложь);

Исключение

Сообщить("Не удалось открыть файл:" + ОписаниеОшибки);

КонецПопытки;

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

Важные нюансы экспорта:

  • 📄 Формат Excel2007 поддерживает до 1 млн строк, в отличие от старого Excel97 (65 тыс.).
  • 🔧 Для корректного отображения иерархии в Excel можно добавить колонку с формулой отступов (например, =REPT("", B2), где B2 — уровень).
  • 🎨 Цвета и шрифты сохранятся только если в параметрах экспорта указать Параметры.Вставить("СохранятьФорматирование", Истина).

5.3. Динамическое сворачивание/разворачивание веток

В 1С 8.3.18+ появилась поддержка группировки строк в табличных документах, что позволяет сворачивать ветки дерева:

Процедура НастроитьГруппировку(ТабличныйДокумент)

// Группируем строки по уровню вложенности

ТабличныйДокумент.ГруппировкаСтрок.Очистить;

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

// Настраиваем отображение

ТабличныйДокумент.ГруппировкаСтрок.СвернутыеГруппы = Истина; // Свернуть все по умолчанию

ТабличныйДокумент.ГруппировкаСтрок.ОтображатьИтоги = Ложь;

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

Ограничения:

  • 🚫 Работает только в тонком клиенте и веб-клиенте.
  • 📉 Значительно увеличивает время формирования документа для больших деревьев (>1000 строк).
  • 🔄 Требует 1С:Предприятие 8.3.18 или новее.
Как ускорить вывод больших деревьев?

Для деревьев с >10 000 узлов рекомендуется:

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

2. Отключить автоподбор ширины колонок (настраивать вручную).

3. Выводить данные порциями по 1000 строк с паузой (через Подождать(1)).

4. Для тонкого клиента использовать серверный вызов для формирования данных.

6. Типичные ошибки и их решение

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

6.1. Бесконечная рекурсия

Симптомы: Зависание или ошибка"Переполнение стека".

Причины и решения:

Причина Решение
Циклические ссылки в дереве (узел ссылается сам на себя) Перед обходом проверять Если Узел.Родитель = Узел Тогда Продолжить;
Слишком большая глубина вложенности (>200 уровней) Заменить рекурсию на итеративный обход со стеком
Ошибка в условии завершения рекурсии Всегда проверять Если Узел = Неопределено Тогда Возврат;

6.2. Неправильные отступы

Проблема: Узлы разных уровней отображаются без отступов или с одинаковыми.

Чек-лист для диагностики:

  • ✅ Проверьте, что параметр Уровень передаётся корректно в рекурсивную функцию (Уровень + 1).
  • ✅ Убедитесь, что для отступов используется невидимый символ (табуляция или пробелы), а не печатный ("-","→").
  • ✅ В свойствах табличного документа проверьте настройку ОтображатьСетку = Ложь — иногда сетка визуально"съедает" отступы.

6.3. Потеря данных при экспорте в Excel

Типичные симптомы:

  • 📉 Обрезаются длинные строки (например, полные пути узлов).
  • 🔢 Числовые значения (например, уровни) преобразуются в даты.
  • 🎨 Исчезает форматирование (цвета, шрифты).

Решения:

  • 📏 Для длинных строк в Excel предварительно установите ширину колонки: ТабличныйДокумент.Колонка(1).Ширина = 500;
  • 🔢 Для числовых значений явно укажите формат: Область.Формат ="ЧЦ=0";
  • 🎨 Для сохранения форматирования используйте параметр: Параметры.Вставить("СохранятьФорматирование", Истина);
⚠️ Внимание: При экспорте в Excel через COMОбъект("Excel.Application") может возникнуть ошибка"Не удалось создать OLE-объект". Это связано с правами или отсутствием Microsoft Office на сервере. В таких случаях используйте стандартный метод Записать табличного документа.

7. Альтернативные способы визуализации деревьев

Табличный документ — не единственный способ отобразить иерархические данные. Рассмотрим альтернативы:

7.1. Дерево в поле формы (элемент управления)

Для интерактивной работы удобнее использовать специализированный элемент управления ПолеДерева:

// В модуле формы:

Процедура ПриСозданииНаСервере

Элементы.ДеревоНоменклатуры.Дерево = Справочники.Номенклатура.ВыгрузитьДерево;

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

// В модуле объекта:

Процедура ДеревоНоменклатурыПриИзменении(Элемент)

Сообщить("Выбран:" + Элемент.Значение);

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

Преимущества:

  • 🖱️ Интерактивность: пользователь может разворачивать/сворачивать ветки.
  • 🔍 Быстрый поиск по дереву (Элемент.НайтиПоЗначению).
  • 📋 Поддержка drag-and-drop для изменения структуры.

7.2. Вывод в HTML-документ

Для веб-клиента или отправки по почте удобно формировать HTML-разметку:

Функция ДеревоВHTML(Дерево)

HTML ="

    ";

    HTML = HTML + ОбойтиУзлыHTML(Дерево);

    HTML = HTML +"

";

Возврат HTML;

КонецФункции

Функция ОбойтиУзлыHTML(Узел)

Если Узел = Неопределено Тогда

Возврат"";

КонецЕсли;

Результат ="

  • " + Узел.Значение;

    Если Узел.Элементы.Количество > 0 Тогда

    Результат = Результат +"

      ";

      Для Каждого ДочернийУзел Из Узел.Элементы Цикл

      Результат = Результат + ОбойтиУзлыHTML(ДочернийУзел);

      КонецЦикла;

      Результат = Результат +"

    ";

    КонецЕсли;

    Результат = Результат +"

  • ";

    Возврат Результат;

    КонецФункции

    Пример использования:

    HTMLТекст = ДеревоВHTML(ДеревоНоменклатуры);
    

    Письмо = Новый ИнтернетПочтаПисьмо;

    Письмо.Текст = HTMLТекст;

    Письмо.ТипТекста = ТипТекстаHTMLДокумент;

    7.3. Визуализация через диаграммы

    Для анализа структуры можно использовать Диаграмма (например, органиграмму или радиальное дерево). Пример для 1С:Предприятие 8.3.14+:

    Процедура ПостроитьДиаграммуДерева(Дерево)
    

    Диаграмма = ЭлементыФормы.Диаграмма;

    Данные = Новый ТаблицаЗначений;

    Данные.Колонки.Добавить("Родитель");

    Данные.Колонки.Добавить("Потомок");

    // Рекурсивное заполнение связей

    ЗаполнитьСвязиДерева(Дерево, Данные,"");

    // Настройка диаграммы

    Диаграмма.Тип = ТипДиаграм