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