Работа с деревьями значений в 1С:Предприятие — одна из самых востребованных задач при разработке отчетов, обработок и аналитических форм. Однако стандартные механизмы платформы не всегда предоставляют удобные инструменты для визуализации иерархических данных. Вывод такого дерева в табличный документ позволяет не только наглядно представить структуру, но и экспортировать её в Excel, PDF или распечатать. Эта статья охватывает все актуальные способы решения задачи — от простых встроенных методов до сложных алгоритмов с рекурсией и динамическим форматированием.
Особенность работы с деревьями в 1С 8.3 заключается в том, что платформа оперирует объектами типа ДеревоЗначений, которые не имеют прямого метода экспорта в таблицу. Разработчику приходится вручную обходить узлы, учитывать уровни вложенности и настраивать отступы. Мы разберём универсальный алгоритм с поддержкой произвольной глубины вложенности, который работает даже для деревьев с 10+ уровнями, а также оптимизированные варианты для типовых задач (например, вывод справочников с иерархией).
Статья будет полезна как начинающим программистам 1С, так и опытным специалистам. Первые найдут здесь готовые коды с подробными комментариями, а вторые — нюансы производительности и неочевидные приёмы форматирования (например, как автоматически раскрасить узлы разных уровней или добавить иконки для визуального разделения веток).
1. Что такое дерево значений и зачем его выводить в табличный документ
Объект ДеревоЗначений в 1С представляет собой иерархическую структуру данных, где каждый элемент (узел) может содержать дочерние элементы. Классические примеры использования:
- 📁 Справочники с иерархией (номенклатура, контрагенты, подразделения)
- 📊 Аналитические отчеты с группировкой по периодам/категориям
- 🔄 Логи операций (например, история изменений документа)
- 📈 Планы видов характеристик (для сложных аналитических разрезов)
Вывод такого дерева в табличный документ решает несколько практических задач:
- 🖨️ Печать иерархических данных с сохранением структуры (например, для инвентаризационных описей).
- 📤 Экспорт в Excel для дальнейшей аналитики в сторонних инструментах.
- 👁️ Визуализация сложных структур (например, дерево подчинённости сотрудников).
- 🔍 Отладка алгоритмов, работающих с рекурсивными данными.
Главное отличие от стандартного вывода через ПолеФормы или ТаблицаЗначений — возможность гибко настраивать отображение: задавать отступы для уровней, применять условное форматирование, добавлять служебные колонки (например, с количеством дочерних элементов). Без такого вывода анализ многомерных данных в 1С часто превращается в утомительное прокручивание списков.
⚠️ Внимание: В версиях 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 уровней.
☑️ Подготовка к выводу дерева
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 с сохранением иерархии
Табличный документ 1С можно сохранить в 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. Бесконечная рекурсия
Симптомы: Зависание 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+:
Процедура ПостроитьДиаграммуДерева(Дерево)
Диаграмма = ЭлементыФормы.Диаграмма;
Данные = Новый ТаблицаЗначений;
Данные.Колонки.Добавить("Родитель");
Данные.Колонки.Добавить("Потомок");
// Рекурсивное заполнение связей
ЗаполнитьСвязиДерева(Дерево, Данные,"");
// Настройка диаграммы
Диаграмма.Тип = ТипДиаграм