Как обойти динамические колонки (свойства) строки дерева значений в управляемой форме 1С

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

При работе с управляемыми формами в 1С:Предприятии 8.3 разработчики часто сталкиваются со сложными динамическими структурами данных. Типичный пример — документ установки цен в конфигурациях «1С:Розница» или «1С:Управление торговлей». В таких интерфейсах колонки видов цен или магазинов создаются динамически в виде дерева или таблицы значений.

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

В чем заключается основная путаница: строки vs колонки

Часто начинающие разработчики путают подчиненные строки дерева значений (иерархические узлы) и динамические свойства (колонки) конкретной строки. В отладчике объект типа ДанныеФормыЭлементДерева отображает свойства динамических колонок (например, виды цен с уникальными идентификаторами вроде ВидЦены_d88dea17...) в общем списке свойств элемента.

Если мы пытаемся использовать метод ПолучитьЭлементы(), платформа возвращает пустую коллекцию. Это происходит потому, что данный метод предназначен исключительно для получения подчиненных строк (узлов дерева), а не его колонок. У самой строки дерева на клиенте нет свойства Колонки, поэтому конструкция вида ТекущиеДанные.Колонки вызовет ошибку выполнения.

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

---

Способ 1. Обход колонок через элементы формы (на клиенте)

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

Рассмотрим по шагам, как реализовать этот алгоритм:

  1. Получаем текущую строку дерева через свойства таблицы формы.
  2. Обходим коллекцию ПодчиненныеЭлементы элемента формы, представляющего таблицу дерева.
  3. Из свойства ПутьКДанным каждого элемента вычленяем непосредственное имя колонки.
  4. Проверяем имя по маске (например, ищем префикс вида цен или магазина) и заполняем данные.

Посмотрим на пример кода, реализующий этот подход:


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

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

---

Способ 2. Использование служебной таблицы настроек (на клиенте)

В типовых конфигурациях 1С (таких как «Розница» или «Управление торговлей») разработчики изначально предусматривают служебные коллекции для управления динамическими колонками. Например, метаданные о выведенных в дерево ценах хранятся в отдельной таблице значений на форме — часто она называется ВыбранныеЦены или ПараметрыКолонок.

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

Выясним, как работает этот метод на практике:


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

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

---

Способ 3. Преобразование реквизита формы в «ДеревоЗначений» (на сервере)

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

Этот способ позволяет получить полноценный объект типа ДеревоЗначений, у которого есть стандартная коллекция Колонки, доступная для полного обхода циклом Для Каждого.

Рассмотрим по шагам серверную реализацию с рекурсивным обходом строк:


&НаСервере
Процедура ЗаполнитьЦеныНаСервере()
    
    // Конвертируем ДанныеФормыДерево в классическое ДеревоЗначений
    ДеревоОбъект = РеквизитФормыВЗначение("ДеревоЦен", Тип("ДеревоЗначений"));
    
    // Создаем массив имен только тех колонок, которые относятся к ценам
    МассивКолонокЦен = Новый Массив;
    Для Каждого Колонка Из ДеревоОбъект.Колонки Цикл
        Если СтрНачинаетсяС(Колонка.Имя, "ВидЦены") Тогда
            МассивКолонокЦен.Добавить(Колонка.Имя);
        КонецЕсли;
    КонецЦикла;
    
    // Запускаем рекурсивный обход всех строк дерева для изменения значений
    ЗаполнитьЗначенияРекурсивно(ДеревоОбъект.Строки, МассивКолонокЦен);
    
    // Возвращаем измененное дерево обратно в реквизит формы
    ЗначениеВРеквизитФормы(ДеревоОбъект, "ДеревоЦен");

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

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

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

---

Способ 4. Получение структуры колонок через ПолучитьРеквизиты() (на сервере)

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

Проанализируем пример функции:


&НаСервере
Функция ПолучитьИменаДинамическихКолонок()
    
    МассивИмен = Новый Массив;
    
    // Получаем реквизиты, вложенные в "ДеревоЦен"
    СписокРеквизитов = ПолучитьРеквизиты("ДеревоЦен");
    
    Для Каждого ОписаниеРеквизита Из СписокРеквизитов Цикл
        // Отбираем динамические колонки по префиксу имени
        Если СтрНачинаетсяС(ОписаниеРеквизита.Имя, "ВидЦены") Тогда
            МассивИмен.Добавить(ОписаниеРеквизита.Имя);
        КонецЕсли;
    КонецЦикла;
    
    Возврат МассивИмен;

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

---

Резюме и рекомендации

Для эффективной работы с динамическими деревьями в 1С придерживайтесь следующих правил:

← На главную