При разработке сложных обработок в системе 1С:Предприятие 8.3, особенно когда речь идет о пакетном изменении данных (например, справочника контрагентов), перед программистом часто встает архитектурный вопрос: где именно объявить коллекцию данных, такую как ДеревоЗначений? Стоит ли сделать его реквизитом самой формы или вынести в реквизиты объекта обработки? На первый взгляд разница кажется несущественной, но она критически влияет на доступность данных в коде, производительность и возможность работы в фоновом режиме. Разберем эту тему подробно, проанализируем нюансы реализации и выясним, как избежать типичных ошибок при работе с типами данных управляемого приложения.
Прежде всего, давайте проанализируем фундаментальное различие. Реквизит формы существует только в контексте этой конкретной формы. Он идеально подходит для чисто интерактивных задач, когда данные нужны только пользователю для визуального контроля и выбора. К такому дереву легко обращаться из модуля формы, но оно совершенно «невидимо» для модуля объекта.
Реквизит объекта (обработки или отчета) является частью структуры метаданных. Это означает, что он доступен как в модуле объекта (через ЭтотОбъект), так и в любой форме этой обработки (через путь Объект.ИмяРеквизита). Рассмотрим ситуацию: если вы планируете вызывать логику обработки дерева не только из формы, но и программно из других модулей или через фоновые задания, то выбор в пользу реквизита объекта очевиден.
Одной из главных сложностей для начинающих разработчиков является работа с типами данных на сервере. В управляемом интерфейсе данные дерева на форме хранятся в типе ДанныеФормыДерево. С этим типом нельзя работать так же гибко, как с обычным ДеревоЗначений (например, нельзя использовать специфические методы обхода или сложные поисковые алгоритмы). Для этого используются методы преобразования.
Проанализируем типичную ситуацию, когда дерево является реквизитом формы с именем ДЗКонтрагенты. Код для работы с ним на сервере будет выглядеть так:
&НаСервере
Процедура ОбработатьДеревоНаСервере()
// Преобразуем данные формы в реальное дерево значений
ДеревоОбъект = РеквизитФормыВЗначение("ДЗКонтрагенты");
// Выполняем действия с деревом (типа ДеревоЗначений)
Для Каждого Строка Из ДеревоОбъект.Строки Цикл
// Логика обработки
КонецЦикла;
// Возвращаем измененные данные обратно на форму
ЗначениеВРеквизитФормы(ДеревоОбъект, "ДЗКонтрагенты");
КонецПроцедуры
А теперь выясним причину распространенной ошибки, упомянутой в обсуждении. Если вы создали ДЗКонтрагенты как реквизит объекта, попытка вызвать РеквизитФормыВЗначение("ДЗКонтрагенты") приведет к ошибке «Недопустимое значение параметра». Это происходит потому, что дерево теперь является частью структуры Объект. Чтобы получить к нему доступ как к объекту, нужно преобразовывать весь объект целиком:
&НаСервере
Процедура ОбработатьДеревоОбъектаНаСервере()
// Преобразуем весь реквизит формы "Объект" в прикладной объект (ОбработкаОбъект)
ТекОбъект = РеквизитФормыВЗначение("Объект");
// Теперь дерево доступно как реквизит этого объекта
Для Каждого Строка Из ТекОбъект.ДЗКонтрагенты.Строки Цикл
// Работаем с данными
КонецЦикла;
// Если дерево или другие реквизиты объекта изменились, возвращаем состояние в форму
ЗначениеВРеквизитФормы(ТекОбъект, "Объект");
КонецПроцедуры
Рассмотрим важный аспект, который часто упускают при проектировании — сериализацию. Когда вы нажимаете кнопку на форме, вызывающую серверную процедуру, 1С передает контекст формы с клиента на сервер.
Если ДеревоЗначений является реквизитом объекта (Объект.ДЗКонтрагенты), оно будет передаваться на сервер и обратно всегда, когда в процедуре задействован Объект. Если в дереве тысячи строк контрагентов, это вызовет заметные задержки («тормоза») интерфейса.
В случае, если дерево — это отдельный реквизит формы, у нас появляется больше гибкости. Мы можем вызывать серверные процедуры без контекста (&НаСервереБезКонтекста) и передавать туда только необходимые данные, либо не передавать дерево вовсе, если в конкретном вызове оно не требуется.
Выясним, как обстоят дела с гибкостью структуры колонок.
ИзменитьРеквизиты(). Это позволяет создавать универсальные обработки, которые подстраиваются под текущие задачи (например, выводят разные наборы реквизитов контрагента для проверки).Если ваша обработка должна выполнять тяжелые операции (например, обновление тысяч записей) в фоновом задании, использование реквизита объекта выглядит логичнее. Вы можете вынести всю логику формирования и обработки дерева в Модуль объекта. Это позволит запускать обработку программно, без открытия формы:
// Пример вызова из внешнего кода
Обработка = Обработки.МассовоеИзменениеКонтрагентов.Создать();
Обработка.ЗаполнитьДеревоПоФильтру(ПараметрыОтбора); // Метод в модуле объекта
Обработка.ВыполнитьИзменения();
Однако помните: само ДеревоЗначений нельзя напрямую передать в параметры фонового задания (это не тип, хранящийся в БД). Вам придется либо заново формировать его внутри фонового потока, либо передавать через ВременноеХранилище.
Подведем итог нашему анализу. Выбор места размещения ДереваЗначений зависит от ваших целей:
Важный совет: При использовании ЗначениеВРеквизитФормы для больших деревьев, помните, что это приведет к полному обновлению элемента управления на форме. Это может привести к потере состояния развернутых узлов. Чтобы этого избежать, перед обновлением сохраняйте идентификаторы развернутых строк и восстанавливайте их после возврата данных на форму.