При разработке функционала в 1С:Предприятии, особенно при использовании управляемых форм и внешних обработок, часто возникает вопрос о взаимодействии клиентского и серверного кода. Одной из распространенных задач является необходимость обратиться к родительской форме (например, форме документа, из которой открыта внешняя обработка) из серверного контекста. Рассмотрим подробнее, почему это напрямую невозможно и какие существуют архитектурные подходы для решения подобных задач.
Суть проблемы: Невозможность передачи объекта "УправляемаяФорма" на сервер
Основная сложность заключается в архитектуре 1С:Предприятия, где клиентская часть и серверная часть работают в разных контекстах и имеют различные наборы доступных типов объектов. Объект УправляемаяФорма является чисто клиентским объектом. Он существует исключительно на стороне клиента для отображения интерфейса, взаимодействия с пользователем и управления элементами формы.
Выясним причину этой особенности:
УправляемаяФорма), не имеют серверного представления, которое могло бы быть десериализовано или использовано на сервере.Попытка передать объект типа УправляемаяФорма на сервер, например, через параметр серверной процедуры или используя функцию ПоместитьВоВременноеХранилище, неизбежно вызовет ошибку. Проанализируем типичное сообщение об ошибке:
Ошибка при вызове метода контекста (ПоместитьВоВременноеХранилище) по причине:
Ошибка помещения значения во временное хранилище по причине:
Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа. по причине:
Ошибка преобразования данных XDTO: Запись значения свойства 'request': форма: Элемент имя: { по причине:
Ошибка отображения типов: Отсутствует отображение для типа 'УправляемаяФорма'
Это сообщение ясно указывает на то, что для типа УправляемаяФорма отсутствует необходимое "отображение" (сериализация/десериализация) для передачи между клиентом и сервером.
Многие разработчики, столкнувшись с этой проблемой, задаются вопросом: "Но ведь ЭтаФорма доступна в модуле формы на сервере, почему же нельзя передать родительскую форму?"
Здесь важно понять разницу. Когда мы используем ЭтаФорма в серверной процедуре модуля формы (например, модуля формы документа), мы обращаемся к серверному представлению данных, которые связаны с *текущей* формой. Это не сам клиентский объект УправляемаяФорма, а скорее его "серверный двойник" или контекст, который позволяет серверу получить доступ к реквизитам формы, данным объекта и выполнять операции, которые влияют на состояние формы, но не на ее клиентский интерфейс напрямую. Этот контекст создается и поддерживается платформой автоматически для текущей формы.
Проблема возникает, когда мы пытаемся получить *чужую* форму (родительскую) на сервере из другого контекста (например, из внешней обработки). Для такой "чужой" формы серверный контекст не создается и не передается автоматически, и нет механизма для его получения по ссылке на клиентскую форму.
Так как саму форму передать на сервер нельзя, мы должны пересмотреть подход и сосредоточиться на передаче данных, которые нам необходимы, или объектов информационной базы, которые форма отображает. Рассмотрим основные стратегии:
Наиболее правильный и часто используемый подход при заполнении табличных частей документа из внешней обработки или другого источника. Вместо передачи формы, мы передаем на сервер сам объект документа или его ссылку.
ВладелецФормы. Из него вы можете получить сам объект документа через свойство Объект.
// В модуле формы внешней обработки, на клиенте
&НаКлиенте
Процедура КомандаЗаполнить(Команда)
// ВладелецФормы - это УправляемаяФорма документа
ДокументОбъект = ВладелецФормы.Объект;
// Передаем ДокументОбъект и другие необходимые параметры на сервер
ЗаполнитьДанныеНаСервере(ДокументОбъект, МассивСотрудников);
// После возвращения с сервера, обновляем данные в форме документа
ВладелецФормы.ОбновитьОтображениеДанных(); // Или более специфичные методы обновления
КонецПроцедуры
// В модуле формы внешней обработки, на сервере
&НаСервере
Процедура ЗаполнитьДанныеНаСервере(ДокументОбъект, Сотрудники)
// ДокументОбъект - это уже серверный объект типа ДокументОбъект.<ИмяДокумента>
Если ДокументОбъект.МожноЗаполнитьЗарплату Тогда
ДокументОбъект.ДополнитьЗарплату(Сотрудники);
КонецЕсли;
// Важно: записываем измененный объект
ДокументОбъект.Записать();
КонецПроцедуры
Обратите внимание, что в данном случае мы работаем с ДокументОбъект как с серверным объектом данных, а не с его формой. Все изменения применяются к объекту в базе данных.
ОбновитьОтображениеДанных(), ЗначениеВРеквизитФормы() или КопироватьДанныеФормы().
// Пример из обсуждения, показывающий обновление данных формы из объекта на клиенте
&НаКлиенте
// ... внутри какой-то процедуры, которая вызывает серверный код
НовыйОбъект = Форма.Объект; // Получаем объект, возможно, измененный на сервере
КопироватьДанныеФормы(НовыйОбъект, Форма.Объект); // Копируем данные из объекта в реквизиты формы
// ...
Этот подход является стандартным и наиболее корректным для взаимодействия между внешними обработками и документами в управляемом приложении 1С.
Автор исходного сообщения столкнулся с проблемой, что типовая конфигурация содержит серверную процедуру, например, ВедомостьДополнитьНаСервере(Форма, Сотрудники), которая ожидает в качестве первого параметра объект типа УправляемаяФорма документа.
Процедура ВедомостьДополнитьНаСервере(Форма, Сотрудники) Экспорт
ТекущийОбъект = Форма.РеквизитФормыВЗначение("Объект");
Если ТекущийОбъект.МожноЗаполнитьЗарплату Тогда
ТекущийОбъект.ДополнитьЗарплату(Сотрудники);
КонецЕсли;
Форма.ОбработатьСообщенияПользователю;
Форма.ЗначениеВРеквизитФормы(ТекущийОбъект, "Объект");
Форма.ПриПолученииДанныхНаСервере(ТекущийОбъект);
КонецПроцедуры
Проанализируем эту процедуру: она использует параметр Форма для двух основных целей:
ТекущийОбъект = Форма.РеквизитФормыВЗначение("Объект");Форма.ОбработатьСообщенияПользователю;, Форма.ЗначениеВРеквизитФормы(ТекущийОбъект, "Объект");, Форма.ПриПолученииДанныхНаСервере(ТекущийОбъект);Если нам нужно вызвать такую процедуру из внешней обработки, где ВладелецФормы — это клиентская форма документа, а ЭтаФорма — форма самой обработки, у нас есть несколько вариантов:
Вариант A: Использование расширения конфигурации
Если вы хотите сохранить структуру вызовов и избежать копипаста, наиболее элегантным решением будет создание расширения конфигурации. В расширении вы можете:
ВедомостьДополнитьНаСервере (или создать свою с похожим функционалом), изменив тип первого параметра на ДокументОбъект.<ИмяДокумента> вместо УправляемаяФорма. Внутри новой процедуры вам не потребуется Форма.РеквизитФормыВЗначение("Объект"), так как объект будет передан напрямую.ТекущийОбъект.ДополнитьЗарплату(Сотрудники);), в отдельную экспортную серверную функцию или процедуру общего модуля, которая принимает только объект документа. Функции, связанные с обновлением формы (Форма.ОбработатьСообщенияПользователю; и т.д.), останутся в модуле формы документа и будут вызываться уже на клиенте после выполнения серверной части.Вариант B: Передача только необходимой информации
Если расширения не подходят, а модифицировать типовой код нежелательно, мы должны извлечь только ту часть логики, которая нам нужна, и работать с ней.
ВедомостьДополнитьНаСервере основная серверная логика — это ТекущийОбъект.ДополнитьЗарплату(Сотрудники);. Все остальное — это работа с формой и сообщениями, что должно выполняться на клиенте.
// В модуле формы внешней обработки, на сервере
&НаСервере
Процедура ДополнитьЗарплатуДокументаНаСервере(ОбъектДокумента, Сотрудники) Экспорт
// Здесь мы уже имеем объект документа, без привязки к форме
Если ОбъектДокумента.МожноЗаполнитьЗарплату Тогда
ОбъектДокумента.ДополнитьЗарплату(Сотрудники);
КонецЕсли;
// Записываем объект, если это необходимо
ОбъектДокумента.Записать();
КонецПроцедуры
ВладелецФормы.Объект, передавать его в вашу новую серверную процедуру, а затем на клиенте обновлять форму родительского документа.
// В модуле формы внешней обработки, на клиенте
&НаКлиенте
Процедура ЗаполнитьТЧДокумента(Команда)
// Получаем объект документа из родительской формы
ДокументОбъект = ВладелецФормы.Объект;
// Массив сотрудников, например, собранный из реквизитов обработки
МассивСотрудников = ПолучитьСотрудниковИзОбработки();
// Вызываем нашу серверную процедуру
ДополнитьЗарплатуДокументаНаСервере(ДокументОбъект, МассивСотрудников);
// После выполнения серверной логики, обновляем форму документа
// ВладелецФормы - это УправляемаяФорма документа
ВладелецФормы.ОбновитьОтображениеДанных();
// Или, если нужны специфические обновления, используем:
// ВладелецФормы.ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
// ВладелецФормы.ОбработатьСообщенияПользователю();
КонецПроцедуры
Таким образом, мы "перехватываем" нужную нам бизнес-логику, работаем с объектом на сервере, а затем позволяем клиентской форме документа обновиться, используя стандартные механизмы 1С. Это избавляет нас от необходимости передавать УправляемаяФорма на сервер и позволяет эффективно использовать функционал внешней обработки.
Прямая передача объекта УправляемаяФорма с клиента на сервер в 1С:Предприятии невозможна из-за архитектурных особенностей платформы. Вместо этого, разработчики должны сосредоточиться на передаче конкретных данных или объектов информационной базы, а также на правильном управлении клиентскими формами через их обновление после серверных операций.
Мы рассмотрели, как работать с объектом документа на сервере и как адаптировать вызовы к типовым процедурам, которые изначально ожидали параметр типа УправляемаяФорма. Эти подходы позволяют решать сложные задачи взаимодействия между клиентскими интерфейсами и серверной логикой, соблюдая при этом архитектурные принципы 1С:Предприятия.