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