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

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

Дерево значений в 1С: как создать и заполнить

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

Дерево значений в 1С — это объект встроенного языка, предназначенный для хранения иерархических данных: строки могут иметь подчинённые строки произвольной глубины. Типичные сценарии использования — отображение структуры подразделений, группировка данных отчёта по нескольким уровням, представление состава документа с вложенными позициями.

Заполнение дерева значений всегда состоит из трёх этапов: создание объекта и описание его колонок, добавление строк с нужным уровнем вложенности, передача результата на форму или в дальнейшую обработку. Ошибки чаще всего возникают на втором этапе — разработчик добавляет строки не в тот уровень или забывает, что после выгрузки запроса с итогами строки верхнего уровня содержат пустые значения данных.

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

Создание дерева значений и описание колонок

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

Тип колонки задаётся объектом ОписаниеТипов. Если тип не указать, колонка примет значение любого типа, но это усложнит сортировку и отображение. Для служебных колонок, которые не должны видеть пользователи (например, ссылка на объект), тип описывать всё равно стоит — это упрощает отладку.

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

Дерево.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));

Дерево.Колонки.Добавить("Ссылка");

Дерево.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));

Заполнение дерева вручную: строки и подчинённые уровни

Строки первого уровня добавляются через коллекцию Строки самого дерева. Чтобы добавить подчинённую строку, нужно обратиться к коллекции Строки конкретной родительской строки. Ссылку на родителя сохраняют в переменную — это основной приём при построении многоуровневой структуры в цикле.

СтрокаГруппа = Дерево.Строки.Добавить();

СтрокаГруппа.Наименование = "Московский филиал";

СтрокаСотрудник = СтрокаГруппа.Строки.Добавить();

СтрокаСотрудник.Наименование = "Иванов И.И.";

СтрокаСотрудник.Сумма = 15000;

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

Порядок ручного заполнения
  1. 1
    Создайте объект Новый ДеревоЗначений и опишите колонки
  2. 2
    Добавьте строку верхнего уровня через Дерево.Строки.Добавить() и сохраните её в переменную
  3. 3
    Добавляйте подчинённые строки через Родитель.Строки.Добавить()
  4. 4
    Заполните поля каждой строки сразу после добавления
  5. 5
    Проверьте уровни: у строки есть свойство Уровень(), а у дерева — КоличествоУровней()

Заполнение из запроса с итогами по группировкам

Самый производительный способ — получить дерево напрямую из запроса. Для этого в тексте запроса используется секция ИТОГИ ... ПО с перечислением полей группировки, а при выгрузке указывается способ обхода ОбходРезультатаЗапроса.ПоГруппировкамСИерархией. Платформа сама построит уровни по порядку полей итогов.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Продажи.Организация КАК Организация,

| Продажи.Номенклатура КАК Номенклатура,

| СУММА(Продажи.Сумма) КАК Сумма

|ИЗ

| РегистрНакопления.Продажи.Обороты КАК Продажи

|СГРУППИРОВАТЬ ПО

| Продажи.Организация,

| Продажи.Номенклатура

|ИТОГИ

| СУММА(Сумма)

|ПО

| Организация";

Дерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

Важная особенность: строки, соответствующие итогам по группировке, содержат значение только в группировочном поле и итоговых ресурсах, остальные поля будут пустыми (NULL). Если нужна иерархия с детальными записями под каждым уровнем, используют ИТОГИ ПО ОБЩИЕ в сочетании с группировками или конструкцию ПоГруппировкам без иерархии — тогда получится плоская структура с одним уровнем.

Заполнение из таблицы значений и справочника

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

Альтернатива — обход выборки из справочника с отбором по родителю. Для каждого уровня выполняется отдельный запрос или выборка Справочники.Номенклатура.Выбрать(Родитель). Способ прост, но на больших справочниках порождает много обращений к базе — предпочтительнее один запрос с итогами по полю Ссылка ИЕРАРХИЯ или выгрузка всей таблицы и построение дерева в памяти.

Способы заполнения дерева

Запрос с ИТОГИ ПО

Самый быстрый для данных из СУБД; ограничение — жёсткая привязка уровней к полям группировки

Ручное добавление строк

Полный контроль над логикой; подходит для небольших структур и сложных условий

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

Нагляден для иерархии неограниченной глубины; на больших объёмах медленнее из-за множества запросов

Построение из таблицы с полем Родитель

Универсальный вариант для данных из файлов и обменов; требует поиска родителя по всем уровням

Вывод дерева на управляемую форму

На управляемой форме дерево значений размещают через реквизит формы типа «ДеревоЗначений» и элемент формы «Таблица» с отображением дерева. Программно созданное дерево передают в реквизит методом ЗначениеВРеквизитФормы() на сервере. Колонки реквизита должны совпадать по именам с колонками заполняемого дерева, иначе данные не отобразятся.

&НаСервере

Процедура ЗаполнитьДеревоНаСервере()

Дерево = ПолучитьДеревоИзЗапроса();

ЗначениеВРеквизитФормы(Дерево, "ДеревоДанных");

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

Если дерево создаётся динамически и состав колонок заранее неизвестен, реквизит и элементы формы добавляют программно: сначала массив добавляемых реквизитов через ИзменитьРеквизиты(), затем элементы формы с привязкой к путям данных. Это заметно сложнее, поэтому при фиксированной структуре реквизит лучше создать в конфигураторе.

Типичные ошибки
01
Добавление строк в Дерево.Строки вместо Родитель.Строки — вся иерархия схлопывается в один уровень
02
Забытая секция ИТОГИ ПО в запросе — выгрузка с ПоГруппировкамСИерархией возвращает плоский результат
03
Несовпадение имён колонок дерева и реквизита формы — данные на форме не отображаются без сообщения об ошибке
04
Обращение к полю строки, которого нет в колонках — исключение «Поле объекта не обнаружено»
05
Модификация дерева на клиенте с вызовом сервера в цикле — резкое падение производительности формы

Проверка результата и производительность

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

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

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

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

Количество уровней
Норма

Соответствует числу полей в ИТОГИ ПО или глубине логики построения

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

Все строки на одном уровне или появились лишние пустые уровни

Итоговые суммы в родительских строках
Норма

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

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

Родительские строки пустые или суммы не сходятся

Отображение на форме
Норма

Дерево разворачивается, видны все уровни

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

Пустая таблица формы при заполненном дереве — проверяйте имена колонок реквизита

Итог

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

Дерево заполняется через Строки.Добавить() у нужного родителя; быстрее всего — выгрузкой запроса с ИТОГИ ПО и обходом ПоГруппировкамСИерархией. Для формы имена колонок дерева и реквизита должны совпадать, а заполнение выполняется одним серверным вызовом.

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

Чем дерево значений отличается от таблицы значений?

Таблица значений — плоская структура: строки одного уровня без иерархии. Дерево значений дополнительно хранит у каждой строки коллекцию подчинённых строк и ссылку на родителя. Таблицу можно преобразовать в дерево программно по полю «Родитель».

Как найти строку в дереве значений?

Используйте Дерево.Строки.Найти(Значение, "ИмяКолонки", Истина) — третий параметр Истина включает поиск по всем подчинённым уровням. Для поиска нескольких строк подходит НайтиСтроки() с структурой отбора.

Как удалить все строки дерева?

Вызовите Дерево.Строки.Очистить() — удалятся строки всех уровней, колонки сохранятся. Чтобы удалить конкретную ветку, очистите коллекцию Строки у родительской строки или удалите её через Родитель.Строки.Удалить(Строка).

Можно ли заполнить дерево значений на клиенте?

На тонком клиенте тип ДеревоЗначений недоступен напрямую — работа идёт через реквизит формы типа ДанныеФормыДерево. Заполнение выполняют на сервере, а на клиенте данные только отображают и редактируют построчно.

Как развернуть все уровни дерева на форме?

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

Почему в строках итогов пустые поля после выгрузки запроса?

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