Как правильно добавить строку в ДеревоЗначений с сохранением иерархии

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Работа с объектом ДеревоЗначений в 1С:Предприятие 8 часто вызывает трудности у начинающих разработчиков, особенно когда требуется не просто добавить новую пустую строку, а скопировать отобранные данные из одного дерева в другое, сохранив при этом структуру «родитель — потомок». В этой статье мы подробно разберем, как программно манипулировать строками дерева, почему обычное присваивание родителя не работает и как оптимизировать поиск данных.

Постановка задачи и типичные ошибки

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

Первая ошибка, с которой сталкиваются разработчики — попытка добавить строку в дерево, которое еще не имеет колонок. В отличие от таблиц на форме, объект ДеревоЗначений в памяти должен быть явно сконфигурирован. Рассмотрим, как подготовить структуру.

Шаг 1. Подготовка структуры нового дерева

Прежде чем добавлять строки, мы должны убедиться, что НовоеДерево имеет точно такие же колонки, как и исходное. Если этого не сделать, метод ЗаполнитьЗначенияСвойств просто не найдет целевых полей и данные не будут скопированы.


// Получаем объект ДеревоЗначений из реквизита формы
ДеревоСтарое = РеквизитФормыВЗначение("Товары");

// Создаем новый объект
НовоеДерево = Новый ДеревоЗначений;

// Копируем структуру колонок
Для Каждого Колонка Из ДеревоСтарое.Колонки Цикл
    // Важно передавать не только имя, но и тип данных колонки
    НовоеДерево.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения, Колонка.Заголовок);
КонецЦикла;

Совет: Вместо ручного перебора колонок можно использовать метод Скопировать() без параметров — он создаст копию структуры дерева без самих данных. Это более быстрый и надежный способ.

Шаг 2. Поиск строк в исходном дереве

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


ПараметрыОтбора = Новый Структура("Номенклатура, Характеристика", ВыбНоменклатура, ВыбХарактеристика);
// Параметр Истина позволяет искать рекурсивно по всем уровням дерева
МассивНайденныхСтрок = ДеревоСтарое.Строки.НайтиСтроки(ПараметрыОтбора, Истина);

Проанализируем ситуацию: метод возвращает массив объектов типа СтрокаДереваЗначений. Эти объекты все еще «принадлежат» старому дереву. Мы не можем просто вставить их в новое дерево методом Добавить, нам нужно создать новые строки в целевом объекте и заполнить их значениями.

Шаг 3. Решение проблемы иерархии (родительских узлов)

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

Разберем правильный алгоритм: для каждой найденной строки нужно проверить, есть ли её «родитель» в новом дереве. Если нет — создать родителя, и только потом добавлять в него подчиненную строку. Рассмотрим пример реализации для дерева с двумя уровнями (Заказ -> Номенклатура):


Для Каждого ТекущаяСтрока Из МассивНайденныхСтрок Цикл
    
    // Проверяем, есть ли у строки родитель (1-й уровень)
    СтарыйРодитель = ТекущаяСтрока.Родитель;
    
    Если СтарыйРодитель = Неопределено Тогда
        // Строка и так находится в корне
        НоваяСтрока = НовоеДерево.Строки.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, ТекущаяСтрока);
    Иначе
        // Ищем, не добавляли ли мы уже этого родителя в новое дерево
        // Предположим, у родителя есть уникальное поле "Заказ"
        ПараметрыПоискаРодителя = Новый Структура("Заказ", СтарыйРодитель.Заказ);
        НайденныеРодители = НовоеДерево.Строки.НайтиСтроки(ПараметрыПоискаРодителя);
        
        Если НайденныеРодители.Количество() = 0 Тогда
            // Родителя еще нет, создаем его в корне
            НовыйРодитель = НовоеДерево.Строки.Добавить();
            ЗаполнитьЗначенияСвойств(НовыйРодитель, СтарыйРодитель);
        Иначе
            // Родитель уже был создан ранее
            НовыйРодитель = НайденныеРодители[0];
        КонецЕсли;
        
        // Теперь добавляем нашу строку именно в коллекцию строк найденного родителя
        НоваяСтрока = НовыйРодитель.Строки.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, ТекущаяСтрока);
    КонецЕсли;
    
КонецЦикла;

Шаг 4. Возврат данных на форму

После того как НовоеДерево заполнено, его нужно отобразить пользователю. Реквизиты формы и объекты в памяти — это разные типы данных. Для синхронизации используем метод ЗначениеВРеквизитФормы.


// Очищаем текущее дерево на форме (необязательно, если ЗначениеВРеквизитФормы перезаписывает всё)
// ЗначениеВРеквизитФормы(Источник, ПутьКРеквизиту)
ЗначениеВРеквизитФормы(НовоеДерево, "Товары");

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

Оптимизация: Избегайте запросов в цикле

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

Как сделать правильно:

  1. Выгрузите все необходимые идентификаторы (ссылки на заказы или номенклатуру) из дерева в массив.
  2. Передайте этот массив в ОДИН запрос с условием ГДЕ Ссылка В (&МассивСсылок).
  3. Загрузите результат запроса в таблицу значений или используйте выборку для сопоставления с деревом.

Альтернативный подход: Удаление лишнего

Иногда вместо того, чтобы копировать нужные строки в новое дерево, проще пойти от обратного:

  1. Скопировать все дерево целиком: НовоеДерево = ДеревоСтарое.Скопировать();
  2. Обойти дерево с конца (чтобы индексы не смещались) и удалить строки, не подходящие под условия.
Этот метод автоматически сохраняет всю структуру иерархии и типы колонок, избавляя вас от написания сложной логики поиска родителей.

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

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

← На главную