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