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