В разработке на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда после выполнения кода на стороне сервера (например, в подписке на событие ОбработкаПроведения) требуется моментально обновить интерфейс у пользователя или вывести ему оповещение. Основная сложность заключается в том, что сервер не может напрямую «толкнуть» клиентское приложение и запустить на нем процедуру. Взаимодействие в 1С практически всегда инициируется клиентом.
Рассмотрим подробнее, как обойти это ограничение и реализовать передачу сигнала с сервера на клиент, используя современные инструменты платформы и приемы из типовых конфигураций.
Проанализируем самый современный и технологичный способ — Систему взаимодействия. Этот механизм позволяет серверу отправлять сообщения конкретным пользователям, которые клиентское приложение получает практически мгновенно через специальный обработчик.
Разберем реализацию по шагам:
СистемаВзаимодействия.
// Пример кода в серверной подписке
Процедура ПослеПроведенияНаСервере(Источник, Отказ, РежимПроведения) Экспорт
// Определяем получателя (например, текущий пользователь или автор документа)
НужныйПользователь = Источник.Автор;
ИдентификаторПользователя = СистемаВзаимодействия.ПолучитьИдентификаторПользователя(НужныйПользователь.ИдентификаторПользователяИБ);
// Создаем сообщение
НовоеСообщение = СистемаВзаимодействия.СоздатьСообщение(СистемаВзаимодействия.ПолучитьОбсуждениеПоУмолчанию());
НовоеСообщение.Текст = "Документ " + Строка(Источник) + " успешно проведен!";
// Можно передать структуру с данными для обработки на клиенте
ДанныеДляКлиента = Новый Структура;
ДанныеДляКлиента.Вставить("Событие", "ДокументПроведен");
ДанныеДляКлиента.Вставить("Ссылка", Источник.Ссылка);
НовоеСообщение.Данные = ДанныеДляКлиента;
НовоеСообщение.Получатели.Добавить(ИдентификаторПользователя);
НовоеСообщение.Записать();
КонецПроцедуры
// В модуле управляемого приложения
Процедура ПриНачалеРаботыСистемы()
ПодключитьОбработчикИзмененияДанных(Новый ОписаниеОповещения("ОбработкаСообщенияСистемыВзаимодействия", ЭтотОбъект));
КонецПроцедуры
Процедура ОбработкаСообщенияСистемыВзаимодействия(Сообщение, ДополнительныеПараметры) Экспорт
// Проверяем наши данные
Если Сообщение.Данные <> Неопределено И Сообщение.Данные.Событие = "ДокументПроведен" Тогда
ПоказатьОповещениеПользователя("Внимание!", , "Проведен документ: " + Сообщение.Данные.Ссылка);
// Здесь можно вызвать процедуру Оповестить()
Оповестить("ОбновитьРабочийСтол", Сообщение.Данные.Ссылка);
КонецЕсли;
КонецПроцедуры
Этот метод хорош тем, что он не создает лишней нагрузки на базу данных постоянными запросами. Однако он требует наличия настроенного Сервера взаимодействия.
Если Система взаимодействия не используется, проанализируем классический подход с использованием вспомогательного регистра. Суть заключается в том, что сервер оставляет «записку» в базе данных, а клиент периодически заходит и проверяет, нет ли для него новых «записок».
Выясним, как это реализовать структурно:
ОчередьОповещений с измерениями Пользователь (тип СправочникСсылка.Пользователи) и ИдентификаторЗаписи (тип УникальныйИдентификатор), и ресурсом ДанныеОповещения (тип ХранилищеЗначения).
МенеджерЗаписи = РегистрыСведений.ОчередьОповещений.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Пользователь = ПараметрыСеанса.ТекущийПользователь;
МенеджерЗаписи.ИдентификаторЗаписи = Новый УникальныйИдентификатор();
МенеджерЗаписи.ДанныеОповещения = Новый ХранилищеЗначения("ОбновитьДанные");
МенеджерЗаписи.Записать();
ПодключитьОбработчикОжидания.Рассмотрим пример настройки в модуле управляемого приложения или форме рабочего стола:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Запускаем проверку каждые 10 секунд
ПодключитьОбработчикОжидания("ПроверитьНовыеОповещения", 10);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьНовыеОповещения() Экспорт
// Вызываем серверную функцию для проверки регистра
Данные = ПроверитьНаличиеЗаписейВРегистреНаСервере();
Если Данные <> Неопределено Тогда
Оповестить("ВыполнитьОбновление", Данные);
КонецЕсли;
КонецПроцедуры
Важный момент: чтобы не нагружать систему, серверная функция должна выполнять максимально легкий запрос к индексу регистра и сразу удалять прочитанные записи, чтобы не обрабатывать их повторно.
Посмотрим на ситуацию со стороны типовых решений, таких как УНФ или ERP. В них уже внедрена Библиотека стандартных подсистем (БСП), которая содержит готовые механизмы для уведомлений.
Например, подсистема НапоминанияПользователя. Мы можем программно создать напоминание для текущего пользователя, которое должно сработать "сейчас".
// Пример использования БСП для создания немедленного напоминания
Напоминание = РегистрыСведений.НапоминанияПользователя.СоздатьМенеджерЗаписи();
Напоминание.Пользователь = ПараметрыСеанса.ТекущийПользователь;
Напоминание.ВремяСобытия = ТекущаяДатаСеанса();
Напоминание.СпособУстановкиВремени = Перечисления.СпособыУстановкиВремениНапоминания.В指定Время;
Напоминание.Текст = "Документ проведен, обновите данные.";
Напоминание.Источник = Источник.Ссылка;
Напоминание.Записать();
БСП сама запустит на клиенте обработчик ожидания, который увидит новую запись и выведет стандартное окно напоминания или оповещение. Это избавляет разработчика от необходимости писать свой транспортный механизм.
В сообщениях форума упоминался ассистент управления «Даша» в УНФ. Проанализируем, как это устроено. По сути, это комбинация методов: событие (проведение) регистрируется в специальной таблице задач (очереди), а на клиенте работает фоновый процесс или периодический опрос этой очереди. Как только «Даша» видит выполненную задачу на сервере, она инициирует визуальное изменение на рабочем столе пользователя.
Важно понимать: ФоновоеЗадание всегда выполняется на сервере. Оно не имеет доступа к Окно, Форма или методу Оповестить. Фоновое задание может только подготовить данные в базе. Чтобы эти данные "всплыли" на клиенте, нам все равно придется использовать либо Систему взаимодействия, либо Обработчик ожидания.
Выясним причины выбора того или иного метода:
НапоминанияПользователя или ТекущиеДела.Таким образом, прямой вызов Оповестить из серверной подписки невозможен, но используя один из описанных "мостов", мы можем эффективно передать управление на сторону клиента.