Как вызвать процедуру формы другого документа в управляемых формах 1С?

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

Переход с обычных форм «1С:Предприятия 8» на управляемые формы часто вызывает трудности у разработчиков, привыкших к определенным паттернам программирования. Типичный пример — необходимость выполнить пересчет показателей в одном документе (например, в распоряжении на отгрузку) при проведении или изменении другого документа (например, складского ордера).

В обычных формах эта задача решалась буквально в три строки кода через получение формы объекта «через точку» и вызов ее экспортного метода. В управляемых формах такая схема не работает из-за жесткого разделения контекста на клиентский и серверный. Давайте подробно разберем, почему старый подход больше неприменим, какие ограничения накладывает платформа 1С и как правильно решить эту задачу современными методами без нарушения стандартов разработки.

Почему старый подход не работает в управляемом приложении?

Вспомним, как выглядел код решения этой задачи в обычных формах:


Расходная = Распоряжение.ПолучитьОбъект();
ФормаДокумента = Расходная.ПолучитьФорму("ФормаДокумента");
ФормаДокумента.ПерезаполнитьПоОтгрузке();
Расходная.Записать(РежимЗаписиДокумента.Проведение);

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

  1. Разделение контекста. Метод ПолучитьФорму() возвращает клиентский объект управляемой формы. На сервере (в модуле объекта или менеджера при проведении) формы физически не существуют. Серверный код ничего не знает об интерфейсе.
  2. Ресурсоемкость. Вызов формы ради выполнения алгоритмов заполнения табличных частей — крайне неоптимальная операция. При создании формы платформа инициализирует все реквизиты, элементы управления, выполняет серверные обработчики компоновки, что создает огромную нагрузку на систему, особенно при групповом перепроведении документов.
  3. Интерактивность. Процедуры формы часто завязаны на контекст отображения (используют переменную ЭтаФорма, обращаются к элементам интерфейса, открывают диалоговые окна). Запуск такого кода в фоновом режиме или на сервере гарантированно вызовет исключение.

Ниже мы подробно проанализируем четыре способа решения этой задачи в управляемом приложении: от простого клиентского обхода до правильного архитектурного разделения логики.

Вариант 1. Архитектурно правильный — перенос бизнес-логики на сервер

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

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


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

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

Теперь из модуля проведения складского ордера (на сервере) мы можем легко вызвать этот общий метод, не обращаясь к формам:


// В модуле объекта складского ордера при проведении
Если ЗначениеЗаполнено(ЭтотОбъект.Распоряжение) Тогда
    РаспоряжениеОбъект = ЭтотОбъект.Распоряжение.ПолучитьОбъект();
    ПродажиСерверПересчет.ВыполнитьПерезаполнениеПоОтгрузке(РаспоряжениеОбъект);
КонецЕсли;

Преимущества: Высокая скорость работы, полное соответствие стандартам 1С, стабильная работа при групповом проведении документов без участия пользователя.

Вариант 2. Использование расширений конфигурации (для типовых решений)

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

Рассмотрим пошагово, как реализовать этот механизм:

  1. Создайте новое расширение конфигурации.
  2. Добавьте в расширение документ РеализацияТоваровУслуг и его форму.
  3. Добавьте в расширение общий модуль или создайте собственный внутри расширения (например, Мой_ПродажиСервер).
  4. Перенесите логику формирования параметров пересчета в общий модуль расширения, используя аннотацию &Вместо или &ИзменениеИКонтроля для переопределения поведения методов формы.

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

Вариант 3. Использование паттерна «Оповещение» для открытых форм

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

Разберем реализацию этого паттерна по шагам:

Шаг 1. В форме документа-источника (например, складского ордера) в обработчике завершения записи отправляем оповещение системе:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Отправляем широковещательное оповещение всем открытым формам
    ПараметрыОповещения = Новый Структура;
    ПараметрыОповещения.Вставить("ДокументСсылка", Объект.Распоряжение);
    
    Оповестить("ДокументОрдерЗаписан", ПараметрыОповещения, ЭтаФорма);
КонецПроцедуры

Шаг 2. В форме документа-получателя (реализации товаров и услуг) определяем стандартный обработчик ОбработкаОповещения:


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    
    // Проверяем, что событие предназначено для нашего документа
    Если ИмяСобытия = "ДокументОрдерЗаписан" Тогда
        Если Параметр.ДокументСсылка = Объект.Ссылка Тогда
            // Вызываем процедуру пересчета показателей текущей формы
            ПерезаполнитьПоОтгрузке("");
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Преимущества: Полная развязка кода. Если форма реализации закрыта — ничего не происходит и ресурсы не тратятся. Если форма открыта — пользователь мгновенно увидит актуальные пересчитанные показатели на своем экране.

Вариант 4. Клиентский переход с передачей параметров (Обходной путь)

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

Проанализируем реализацию этого механизма:

Шаг 1. В форме документа-приемника (например, РеализацияТоваровУслуг) добавляем новый реквизит формы с именем ПерезаполнятьПриОткрытии (тип Булево). В модуле формы в обработчике открытия прописываем вызов процедуры заполнения:


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    // Проверяем флаг, переданный при инициализации формы
    Если ПерезаполнятьПриОткрытии Тогда
        ПерезаполнитьПоОтгрузке("");
        // Записываем изменения и закрываем форму
        Записать();
        Закрыть();
    КонецЕсли;
КонецПроцедуры

Шаг 2. В форме документа-источника (складского ордера) в обработчике завершения записи программно получаем форму распоряжения, устанавливаем реквизит и открываем её:


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

Важное предупреждение: Данный метод является «костылем» и нарушает стандарты разработки «1С:Совместимо» (АПК:741). Напрямую обращаться к реквизитам формы до ее открытия не рекомендуется. Правильным развитием этого подхода является передача флага через структуру параметров метода ОткрытьФорму() и его обработка на сервере в процедуре ПриСозданииНаСервере.

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

Резюме

Мы проанализировали особенности работы с управляемыми формами в контексте выполнения процедур других объектов. При проектировании взаимодействия документов всегда отдавайте приоритет Варианту 1 (вынос логики в общие модули на сервер). Это гарантирует быстродействие вашей информационной системы и отсутствие ошибок при интеграции с другими подсистемами 1С.

← На главную