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