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