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