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