Как получить родительскую управляемую форму на сервере в 1С:Предприятии?

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

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

Суть проблемы: Невозможность передачи объекта "УправляемаяФорма" на сервер

Основная сложность заключается в архитектуре 1С:Предприятия, где клиентская часть и серверная часть работают в разных контекстах и имеют различные наборы доступных типов объектов. Объект УправляемаяФорма является чисто клиентским объектом. Он существует исключительно на стороне клиента для отображения интерфейса, взаимодействия с пользователем и управления элементами формы.

Выясним причину этой особенности:

  1. Различные контексты выполнения: Клиент и сервер представляют собой отдельные процессы. Клиентский контекст отвечает за пользовательский интерфейс, а серверный — за бизнес-логику, работу с данными и транзакции. Объекты, предназначенные для UI (как УправляемаяФорма), не имеют серверного представления, которое могло бы быть десериализовано или использовано на сервере.
  2. Безопасность и целостность данных: Прямая передача клиентского объекта формы на сервер могла бы привести к неконтролируемым изменениям состояния интерфейса или нарушению принципов управляемого приложения. Это также потенциально открыло бы пути для манипуляций с клиентским интерфейсом из серверного кода, что не соответствует модели безопасности 1С.
  3. Объём данных: Объект формы может содержать значительный объем данных, включая элементы управления, их свойства, данные реквизитов формы и связанные объекты. Передача всей этой информации между клиентом и сервером была бы крайне неэффективной и избыточной.

Попытка передать объект типа УправляемаяФорма на сервер, например, через параметр серверной процедуры или используя функцию ПоместитьВоВременноеХранилище, неизбежно вызовет ошибку. Проанализируем типичное сообщение об ошибке:


Ошибка при вызове метода контекста (ПоместитьВоВременноеХранилище) по причине: 
Ошибка помещения значения во временное хранилище по причине: 
Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа. по причине: 
Ошибка преобразования данных XDTO: Запись значения свойства 'request': форма: Элемент имя: { по причине: 
Ошибка отображения типов: Отсутствует отображение для типа 'УправляемаяФорма'

Это сообщение ясно указывает на то, что для типа УправляемаяФорма отсутствует необходимое "отображение" (сериализация/десериализация) для передачи между клиентом и сервером.

Особый случай: Доступность ЭтаФорма на сервере

Многие разработчики, столкнувшись с этой проблемой, задаются вопросом: "Но ведь ЭтаФорма доступна в модуле формы на сервере, почему же нельзя передать родительскую форму?"

Здесь важно понять разницу. Когда мы используем ЭтаФорма в серверной процедуре модуля формы (например, модуля формы документа), мы обращаемся к серверному представлению данных, которые связаны с *текущей* формой. Это не сам клиентский объект УправляемаяФорма, а скорее его "серверный двойник" или контекст, который позволяет серверу получить доступ к реквизитам формы, данным объекта и выполнять операции, которые влияют на состояние формы, но не на ее клиентский интерфейс напрямую. Этот контекст создается и поддерживается платформой автоматически для текущей формы.

Проблема возникает, когда мы пытаемся получить *чужую* форму (родительскую) на сервере из другого контекста (например, из внешней обработки). Для такой "чужой" формы серверный контекст не создается и не передается автоматически, и нет механизма для его получения по ссылке на клиентскую форму.

Решение проблемы: Передача данных, а не формы

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

1. Работа с объектом документа на сервере

Наиболее правильный и часто используемый подход при заполнении табличных частей документа из внешней обработки или другого источника. Вместо передачи формы, мы передаем на сервер сам объект документа или его ссылку.

  1. Получаем объект на клиенте: Если ваша внешняя обработка открыта как обработка заполнения табличной части документа, у вас на клиенте доступен объект ВладелецФормы. Из него вы можете получить сам объект документа через свойство Объект.
  2. 
    // В модуле формы внешней обработки, на клиенте
    &НаКлиенте
    Процедура КомандаЗаполнить(Команда)
        // ВладелецФормы - это УправляемаяФорма документа
        ДокументОбъект = ВладелецФормы.Объект; 
            
        // Передаем ДокументОбъект и другие необходимые параметры на сервер
        ЗаполнитьДанныеНаСервере(ДокументОбъект, МассивСотрудников);
            
        // После возвращения с сервера, обновляем данные в форме документа
        ВладелецФормы.ОбновитьОтображениеДанных(); // Или более специфичные методы обновления
    КонецПроцедуры
    
  3. Выполняем операции на сервере: На сервере мы получаем объект документа и выполняем с ним все необходимые операции (например, заполнение табличной части).
  4. 
    // В модуле формы внешней обработки, на сервере
    &НаСервере
    Процедура ЗаполнитьДанныеНаСервере(ДокументОбъект, Сотрудники)
        // ДокументОбъект - это уже серверный объект типа ДокументОбъект.<ИмяДокумента>
        Если ДокументОбъект.МожноЗаполнитьЗарплату Тогда
            ДокументОбъект.ДополнитьЗарплату(Сотрудники);
        КонецЕсли;
            
        // Важно: записываем измененный объект
        ДокументОбъект.Записать(); 
    КонецПроцедуры
    

Обратите внимание, что в данном случае мы работаем с ДокументОбъект как с серверным объектом данных, а не с его формой. Все изменения применяются к объекту в базе данных.


// Пример из обсуждения, показывающий обновление данных формы из объекта на клиенте
&НаКлиенте
// ... внутри какой-то процедуры, которая вызывает серверный код
НовыйОбъект = Форма.Объект; // Получаем объект, возможно, измененный на сервере
КопироватьДанныеФормы(НовыйОбъект, Форма.Объект); // Копируем данные из объекта в реквизиты формы
// ...

Этот подход является стандартным и наиболее корректным для взаимодействия между внешними обработками и документами в управляемом приложении 1С.

2. Адаптация стандартных процедур, ожидающих "Форму"

Автор исходного сообщения столкнулся с проблемой, что типовая конфигурация содержит серверную процедуру, например, ВедомостьДополнитьНаСервере(Форма, Сотрудники), которая ожидает в качестве первого параметра объект типа УправляемаяФорма документа.


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

Проанализируем эту процедуру: она использует параметр Форма для двух основных целей:

  1. Получение серверного объекта документа: ТекущийОбъект = Форма.РеквизитФормыВЗначение("Объект");
  2. Вызов клиентских или серверных методов формы для обновления интерфейса и обработки сообщений: Форма.ОбработатьСообщенияПользователю;, Форма.ЗначениеВРеквизитФормы(ТекущийОбъект, "Объект");, Форма.ПриПолученииДанныхНаСервере(ТекущийОбъект);

Если нам нужно вызвать такую процедуру из внешней обработки, где ВладелецФормы — это клиентская форма документа, а ЭтаФорма — форма самой обработки, у нас есть несколько вариантов:

Вариант A: Использование расширения конфигурации

Если вы хотите сохранить структуру вызовов и избежать копипаста, наиболее элегантным решением будет создание расширения конфигурации. В расширении вы можете:

  1. Модифицировать существующую процедуру: Переопределить процедуру ВедомостьДополнитьНаСервере (или создать свою с похожим функционалом), изменив тип первого параметра на ДокументОбъект.<ИмяДокумента> вместо УправляемаяФорма. Внутри новой процедуры вам не потребуется Форма.РеквизитФормыВЗначение("Объект"), так как объект будет передан напрямую.
  2. Разделить логику: Вынести часть кода, которая работает только с объектом (ТекущийОбъект.ДополнитьЗарплату(Сотрудники);), в отдельную экспортную серверную функцию или процедуру общего модуля, которая принимает только объект документа. Функции, связанные с обновлением формы (Форма.ОбработатьСообщенияПользователю; и т.д.), останутся в модуле формы документа и будут вызываться уже на клиенте после выполнения серверной части.

Вариант B: Передача только необходимой информации

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

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

Таким образом, мы "перехватываем" нужную нам бизнес-логику, работаем с объектом на сервере, а затем позволяем клиентской форме документа обновиться, используя стандартные механизмы 1С. Это избавляет нас от необходимости передавать УправляемаяФорма на сервер и позволяет эффективно использовать функционал внешней обработки.

Заключение

Прямая передача объекта УправляемаяФорма с клиента на сервер в 1С:Предприятии невозможна из-за архитектурных особенностей платформы. Вместо этого, разработчики должны сосредоточиться на передаче конкретных данных или объектов информационной базы, а также на правильном управлении клиентскими формами через их обновление после серверных операций.

Мы рассмотрели, как работать с объектом документа на сервере и как адаптировать вызовы к типовым процедурам, которые изначально ожидали параметр типа УправляемаяФорма. Эти подходы позволяют решать сложные задачи взаимодействия между клиентскими интерфейсами и серверной логикой, соблюдая при этом архитектурные принципы 1С:Предприятия.

← На главную