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