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