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