В процессе разработки в конфигурации 1С:Управление торговлей 11.0 (особенно в старых релизах, таких как 11.0.6.9) программисты часто сталкиваются с задачей оперативного оповещения пользователей. Типичный сценарий: один пользователь создает задачу или запускает бизнес-процесс, а у исполнителя должно мгновенно (или с минимальной задержкой) всплыть окно-напоминание.
Сложность заключается в том, что старые версии УТ 11.0 базируются на ранних редакциях Библиотеки стандартных подсистем (БСП 1.2.1.11). В этих редакциях еще не существовало удобных и привычных сегодня методов клиентского программного интерфейса, таких как НапоминанияПользователяClient.НапомнитьВУказанноеВремя. Давайте детально разберем, как обойти это ограничение, почему стандартный механизм БСП может не срабатывать сразу и как реализовать надежное оповещение своими силами.
Если вы попытаетесь использовать в УТ 11.0 конструкцию:
НапоминанияПользователяКлиент.НапомнитьВУказанноеВремя(Текст, Время, Предмет)
система выдаст ошибку, так как этого метода попросту нет в глобальном поиске по конфигурации. Данный метод появился только в БСП версии 2.0 и выше (что соответствует релизам УТ 11.1 и новее). Тем не менее, сам регистр сведений НапоминанияПользователя в системе присутствует. Это означает, что базовая архитектура хранения напоминаний заложена, но программные интерфейсы для работы с ней разработчику старой конфигурации приходится прописывать вручную.
Поскольку готовых серверных или клиентских общих процедур для создания напоминания в БСП 1.2 нет, мы можем записать данные напрямую в регистр сведений НапоминанияПользователя. Делать это необходимо на стороне сервера, например, в подписке на событие ПриЗаписи задачи или непосредственно в модуле объекта задачи при ее создании.
Рассмотрим пример кода, который создает напоминание для конкретного исполнителя задачи:
Процедура СоздатьНапоминаниеИсполнителю(СсылкаНаЗадачу, Исполнитель, СрокНапоминания) Экспорт
// Инициализируем менеджер записи регистра сведений
МенеджерЗаписи = РегистрыСведений.НапоминанияПользователя.СоздатьМенеджерЗаписи();
// Заполняем измерения регистра
МенеджерЗаписи.Пользователь = Исполнитель; // Кому придет уведомление
МенеджерЗаписи.ВремяСобытия = ТекущаяДата(); // Время фиксации события
МенеджерЗаписи.Источник = СсылкаНаЗадачу; // Документ, задача или бизнес-процесс
// Заполняем ресурсы и реквизиты для корректной работы БСП
МенеджерЗаписи.СрокНапоминания = СрокНапоминания; // Время, когда окно должно всплыть
МенеджерЗаписи.Описание = "Вам назначена новая задача: " + СокрЛП(СсылкаНаЗадачу.Наименование);
// Выбираем способ установки времени из системного перечисления
МенеджерЗаписи.СпособУстановкиВремениНапоминания = Перечисления.СпособыУстановкиВремениНапоминания.ВУказанноеВремя;
Попытка
МенеджерЗаписи.Записать();
Исключение
// Обязательно логируем возможные ошибки блокировок или прав доступа
ЗаписьЖурналаРегистрации("ПрограммноеСозданиеНапоминания",
УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка записи напоминания: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Важное примечание по реквизитам: Чтобы напоминание действительно отобразилось у пользователя-получателя, поле Пользователь должно содержать именно ссылку на элемент справочника Пользователи, соответствующий исполнителю задачи, а не текстовое представление или роль.
Часто разработчики сталкиваются с тем, что запись в регистр успешно добавлена, но у пользователя на экране ничего не происходит. Давайте проанализируем технические причины такого поведения:
ПроверитьТекущиеНапоминания), который считывает данные из регистра раз в несколько минут (по умолчанию от 5 до 15 минут). Оповещение не всплывет мгновенно — нужно дождаться срабатывания обработчика.Если штатный механизм БСП 1.2 работает нестабильно из-за многочисленных доработок старой конфигурации или не обеспечивает требуемую скорость реакции, гораздо проще и надежнее внедрить собственный легковесный механизм оповещения через очередь сообщений.
Давайте разберем по шагам, как это реализовать:
Создадим новый регистр сведений ОчередьОповещенийПользователей со следующей структурой:
Получатель (Тип: СправочникСсылка.Пользователи), Идентификатор (Тип: УникальныйИдентификатор).ТекстОповещения (Тип: Строка), Источник (Тип: ЛюбаяСсылка).В модуле объекта задачи в процедуре ПриЗаписи добавим несложный код для регистрации оповещения:
Процедура ПриЗаписи(Отказ)
Если ЭтоНовый() Тогда
// Записываем оповещение в нашу кастомную очередь
НоваяЗапись = РегистрыСведений.ОчередьОповещенийПользователей.СоздатьМенеджерЗаписи();
НоваяЗапись.Получатель = ЭтотОбъект.Исполнитель;
НоваяЗапись.Идентификатор = Новый УникальныйИдентификатор();
НоваяЗапись.ТекстОповещения = "Вам назначена задача: " + ЭтотОбъект.Наименование;
НоваяЗапись.Источник = ЭтотОбъект.Ссылка;
НоваяЗапись.Записать();
КонецЕсли;
КонецПроцедуры
В модуле управляемого приложения в процедуре ПриНачалеРаботыСистемы подключим быстрый и легкий обработчик ожидания, который будет проверять наличие новых записей каждые 30–60 секунд:
Процедура ПриНачалеРаботыСистемы()
// Подключаем обработчик ожидания с интервалом 30 секунд
ПодключитьОбработчикОжидания("ПроверитьНовыеЗадачиИсполнителя", 30);
КонецПроцедуры
В глобальном или обычном неглобальном общем модуле (например, ОповещенияКлиент) создаем процедуру обработки:
Процедура ПроверитьНовыеЗадачиИсполнителя() Экспорт
// Получаем массив новых оповещений с сервера
СписокОповещений = ОповещенияСервер.ПолучитьИОчиститьОчередьОповещений(ПараметрыСеанса.ТекущийПользователь);
Для Каждого Оповещение Из СписокОповещений Цикл
// Показываем стандартное всплывающее облако 1С в правом нижнем углу
ПоказатьОповещениеПользователя(
"Назначена задача!",
ПолучитьНавигационнуюСсылку(Оповещение.Источник),
Оповещение.ТекстОповещения,
БиблиотекаКартинок.Информация
);
КонецЦикла;
КонецПроцедуры
На стороне сервера (в модуле ОповещенияСервер) мы считываем накопленные строки и сразу же удаляем их из регистра, чтобы избежать повторного показа:
Функция ПолучитьИОчиститьОчередьОповещений(ТекущийПользователь) Экспорт
РезультатТаблица = Новый Массив;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Очередь.Идентификатор КАК Идентификатор,
| Очередь.ТекстОповещения КАК ТекстОповещения,
| Очередь.Источник КАК Источник
|ИЗ
| РегистрСведений.ОчередьОповещенийПользователей КАК Очередь
|ГДЕ
| Очередь.Получатель = &ТекущийПользователь";
Запрос.УстановитьПараметр("ТекущийПользователь", ТекущийПользователь);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Формируем структуру данных для передачи на клиент
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("ТекстОповещения", Выборка.ТекстОповещения);
СтруктураДанных.Вставить("Источник", Выборка.Источник);
РезультатТаблица.Добавить(СтруктураДанных);
// Удаляем запись из очереди
МЗ = РегистрыСведений.ОчередьОповещенийПользователей.СоздатьМенеджерЗаписи();
МЗ.Получатель = ТекущийПользователь;
МЗ.Идентификатор = Выборка.Идентификатор;
МЗ.Удалить();
КонецЦикла;
Возврат РезультатТаблица;
КонецФункции
Если ваша конфигурация работает на платформе 1С:Предприятие версии 8.3.10 и выше, вы можете полностью отказаться от тяжелых регламентных заданий, регистров очереди и частых обработчиков ожидания, нагружающих сервер СУБД. Идеальным инструментом для мгновенной отправки сообщений между пользователями является Система взаимодействия.
При таком подходе в момент создания задачи серверный код отправляет push-сообщение получателю через объект СистемаВзаимодействия. Пользователь получает уведомление в течение доли секунды, прямо в интерфейсе платформы 1С.
Разберем базовый пример программной отправки сообщения:
// Находим обсуждение между текущим пользователем и исполнителем
ПользователиОбсуждения = Новый Массив;
ПользователиОбсуждения.Добавить(СистемаВзаимодействия.ПолучитьТекущегоПользователя().Идентификатор);
ПользователиОбсуждения.Добавить(СистемаВзаимодействия.ПолучитьПользователяИнформационнойБазы(Исполнитель.УникальныйИдентификатор()).Идентификатор);
// Создаем или получаем существующее обсуждение типа "Тет-а-тет"
Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
Обсуждение.Участники = ПользователиОбсуждения;
Обсуждение.Ключ = "Задача_" + Строка(ЗадачаСсылка.УникальныйИдентификатор());
Обсуждение.Записать();
// Отправляем сообщение с текстом о новой задаче
НовоеСообщение = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
НовоеСообщение.Текст = "Вам назначена новая задача! Пожалуйста, ознакомьтесь с деталями.";
НовоеСообщение.Данные = ЗадачаСсылка;
НовоеСообщение.Записать();
Система взаимодействия избавит базу данных от лишней транзакционной нагрузки и обеспечит моментальную доставку уведомлений без зависаний сессий пользователей.