Как реализовать отправку сообщений пользователям в центр оповещений 1С:Предприятия 8.3 без использования Системы взаимодействия?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет
← На главную

Всем привет! Сегодня мы разберем крайне актуальную задачу для многих разработчиков 1С – как отправлять уведомления пользователям напрямую в центр оповещений, минуя при этом Систему взаимодействия. Этот вопрос часто возникает, когда нам необходимо оповестить пользователя о завершении длительного фонового процесса, появлении нового документа или любом другом событии, произошедшем на сервере.

Представим, что у нас есть конфигурация «Бухгалтерия предприятия КОРП, редакция 3.0» на платформе 1С:Предприятие 8.3. Мы хотим, чтобы сообщения, хранящиеся, например, в регистре сведений, доставлялись конкретным пользователям и отображались в виде всплывающих оповещений, а затем оставались в центре оповещений до момента ознакомления. Разберем два основных подхода к решению этой задачи, которые зависят от версии вашей платформы 1С:Предприятие.

Решение 1: Использование нового механизма уведомлений 1С:Предприятия (начиная с версии 8.3.26)

Если вы используете платформу 1С:Предприятие версии 8.3.26 или выше, то вам крупно повезло, поскольку фирма "1С" реализовала специализированный, гораздо более эффективный и простой в использовании механизм для отправки уведомлений с сервера на клиент. Этот механизм значительно упрощает работу и не требует сложных схем с периодическим опросом.

Давайте пошагово рассмотрим, как работает этот новый механизм:

  1. Объект УведомленияКлиента или МенеджерУведомленийКлиента.

    В глобальном контексте платформы появился новый объект УведомленияКлиента (или МенеджерУведомленийКлиента), который предоставляет необходимые методы для работы с уведомлениями. Он доступен как на клиенте, так и на сервере.

  2. Подключение обработчика на клиенте: метод ПодключитьОбработчик.

    На стороне клиентского приложения нам необходимо указать, какая процедура будет вызываться, когда на клиент придет уведомление с сервера. Для этого используем метод ПодключитьОбработчик.

    Метод ПодключитьОбработчик принимает два основных параметра:

    • Ключ (строка): Строковое значение, которое позволяет идентифицировать тип уведомления. Если мы хотим, чтобы этот обработчик реагировал на все уведомления, можно передать Неопределено. Это очень удобно для маршрутизации уведомлений: можно иметь разные обработчики для разных типов сообщений (например, 'НовыйДокумент', 'ЗадачаВыполнена', 'ВажноеОповещение').
    • Обработчик (объект ОписаниеОповещения): Это описание процедуры, которая будет вызвана при получении уведомления. Процедура должна быть экспортируемой, располагаться на клиенте и принимать два параметра: КлючУведомления (строка) и ДанныеУведомления (произвольный тип).

    Обычно подключение такого обработчика выполняется при старте системы, например, в обработчике события ПриНачалеРаботыСистемы модуля управляемого приложения или в глобальном клиентском общем модуле.

    Пример кода для клиентского модуля:

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

    С серверной стороны мы можем отправить уведомление одному или нескольким клиентским сеансам. Для этого используем метод ОтправитьУведомление объекта УведомленияКлиента.

    Метод ОтправитьУведомление принимает следующие параметры:

    • Ключ (строка): Тот же строковый ключ, который мы указывали при подключении обработчика на клиенте. Позволяет системе найти нужный клиентский обработчик.
    • Данные (произвольный тип): Это любые данные, которые вы хотите передать на клиент. Это может быть строка с текстом сообщения, структура с дополнительными параметрами (например, ссылка на объект, который нужно открыть), массив и т.д.
    • Адресаты (массив чисел, опционально): Массив номеров сеансов, которым нужно отправить уведомление. Если этот параметр не заполнен, уведомление будет отправлено всем активным клиентским сеансам. Номера сеансов можно получить, например, из регистра сведений, где хранятся пользователи и их активные сеансы, или из системных функций, таких как ПолучитьСписокСеансов().

    Пример кода для серверного общего модуля:

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

    Важно: Новый механизм обеспечивает гарантированную доставку уведомлений. Если метод отправки выполнен на сервере, платформа предпримет попытку доставить уведомление клиентскому приложению, пока клиентский сеанс существует, независимо от дальнейшей судьбы серверных процессов.

Решение 2: Использование ПодключитьОбработчикОжидания для старых версий платформы (до 8.3.26)

Если ваша версия платформы 1С:Предприятие ниже 8.3.26, то вам придется реализовать механизм периодического опроса сервера с клиента. Это более ресурсоемкий подход, но до появления нового механизма он был стандартным способом решения подобных задач.

Давайте разберем по шагам, как это можно реализовать:

  1. Создание регистра сведений для хранения сообщений.

    Нам потребуется регистр сведений, в котором будут храниться сообщения для пользователей. Этот регистр должен быть непериодическим, независимым, и содержать как минимум следующие измерения и ресурсы:

    • Измерение Пользователь (Тип: СправочникСсылка.Пользователи) — кому предназначено сообщение.
    • Измерение УникальныйИдентификаторСообщения (Тип: Строка или УникальныйИдентификатор) — для уникальной идентификации сообщения и его удаления после прочтения.
    • Ресурс ТекстСообщения (Тип: Строка) — само сообщение.
    • Ресурс ДатаСоздания (Тип: Дата) — для сортировки или фильтрации.
    • Ресурс Ознакомлен (Тип: Булево) — флаг, указывающий, прочитано ли сообщение.

    Когда на сервере возникает событие, требующее оповещения, мы записываем новую запись в этот регистр сведений.

  2. Подключение обработчика ожидания на клиенте.

    На стороне клиентского приложения мы будем использовать процедуру ПодключитьОбработчикОжидания. Эта процедура позволяет нам указать клиентскую процедуру, которая будет вызываться через определенный интервал времени. Важно, что вызов происходит только в "состоянии покоя", то есть когда пользователь неактивен, и программа не выполняет других активных действий.

    • Где запускать: ПодключитьОбработчикОжидания обычно подключается при начале работы системы (например, в обработчике события ПриНачалеРаботыСистемы модуля управляемого приложения или в глобальном клиентском общем модуле).
    • Процедура-обработчик: Процедура, которую мы указываем в ПодключитьОбработчикОжидания, должна быть экспортируемой, располагаться на клиенте и не иметь параметров.
    • Периодичность: Интервал проверки задается в секундах. Мы не рекомендуем ставить слишком маленький интервал (например, 1-2 секунды), чтобы не создавать избыточную нагрузку на сервер. Интервал в 10-30 секунд обычно достаточен.

    Пример кода для клиентского модуля (модуль управляемого приложения или глобальный клиентский общий модуль):

    
    // В модуле управляемого приложения или глобальном клиентском общем модуле
    
    &НаКлиенте
    Процедура ПриНачалеРаботыСистемы()
        // Подключаем обработчик ожидания, который будет запускаться каждые 15 секунд
        // "ПроверитьНовыеСообщения" - это название процедуры, которую мы создадим
        ПодключитьОбработчикОжидания("ПроверитьНовыеСообщения", 15, Истина); // Истина - для выполнения в фоновом режиме
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ПроверитьНовыеСообщения() Экспорт
        // Эта процедура будет периодически вызываться на клиенте
        // Отсюда мы должны обратиться к серверу, чтобы получить новые сообщения
    
        // Проверяем, есть ли новые сообщения для текущего пользователя на сервере
        НовыеСообщения = ОбщегоНазначенияКлиентСервер.ПолучитьНовыеСообщенияДляПользователя(); 
        // Предполагаем, что ОбщегоНазначенияКлиентСервер - это глобальный общий модуль
    
        Если НовыеСообщения <> Неопределено Тогда
            Для Каждого Сообщение Из НовыеСообщения Цикл
                // Выводим оповещение пользователю
                Оповещение = Новый ОписаниеОповещения("ОзнакомлениеССообщением", ЭтотОбъект, Сообщение.УникальныйИдентификаторСообщения);
                ПоказатьОповещениеПользователя(Сообщение.ТекстСообщения, Оповещение,,, СтатусОповещенияПользователя.Важное);
            КонецЦикла;
        КонецЕсли;
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ОзнакомлениеССообщением(УникальныйИдентификаторСообщения) Экспорт
        // Процедура, которая будет вызвана при нажатии на оповещение
        // Здесь можно пометить сообщение как ознакомленное или удалить его
        ОбщегоНазначенияКлиентСервер.ПометитьСообщениеКакОзнакомленное(УникальныйИдентификаторСообщения);
    КонецПроцедуры
    
  3. Серверная функция для получения и пометки сообщений.

    В серверном общем модуле (который может быть вызван из клиентского кода) нам потребуется функция, которая будет выполнять запрос к нашему регистру сведений, получать новые сообщения для текущего пользователя и, возможно, помечать их как "Ознакомлен = Истина" или удалять.

    Пример кода для серверного общего модуля (например, ОбщегоНазначенияКлиентСервер):

    
    // В серверном общем модуле (например, ОбщегоНазначенияКлиентСервер)
    
    &НаСервере
    Функция ПолучитьНовыеСообщенияДляПользователя() Экспорт
        // Запрос к регистру сведений, чтобы получить неознакомленные сообщения для текущего пользователя
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |    СообщенияПользователям.ТекстСообщения,
            |    СообщенияПользователям.УникальныйИдентификаторСообщения
            |ИЗ
            |    РегистрСведений.СообщенияПользователям КАК СообщенияПользователям
            |ГДЕ
            |    СообщенияПользователям.Пользователь = &ТекущийПользователь
            |    И СообщенияПользователям.Ознакомлен = ЛОЖЬ
            |УПОРЯДОЧИТЬ ПО
            |    СообщенияПользователям.ДатаСоздания";
    
        Запрос.УстановитьПараметр("ТекущийПользователь", Пользователи.ТекущийПользователь());
    
        РезультатЗапроса = Запрос.Выполнить();
        Выборка = РезультатЗапроса.Выбрать();
    
        МассивСообщений = Новый Массив;
        Пока Выборка.Следующий() Цикл
            МассивСообщений.Добавить(Новый Структура(
                "ТекстСообщения, УникальныйИдентификаторСообщения",
                Выборка.ТекстСообщения, Выборка.УникальныйИдентификаторСообщения));
        КонецЦикла;
    
        // Теперь, когда мы получили сообщения для отправки на клиент,
        // мы можем пометить их как 'Ознакомлен' на сервере, чтобы они не были отправлены повторно.
        // Или же это можно сделать после получения подтверждения с клиента.
        // Для простоты, давайте пометим их сразу после получения.
    
        МенеджерЗаписи = РегистрыСведений.СообщенияПользователям.СоздатьМенеджерЗаписи();
        Для Каждого Сообщение Из МассивСообщений Цикл
            МенеджерЗаписи.УникальныйИдентификаторСообщения = Сообщение.УникальныйИдентификаторСообщения;
            Если МенеджерЗаписи.Прочитать() Тогда
                МенеджерЗаписи.Ознакомлен = Истина;
                МенеджерЗаписи.Записать();
            КонецЕсли;
        КонецЦикла;
    
        Возврат МассивСообщений;
    КонецФункции
    
    &НаСервере
    Процедура ПометитьСообщениеКакОзнакомленное(УникальныйИдентификаторСообщения) Экспорт
        // Процедура для пометки сообщения как ознакомленного по факту клика на оповещение
        МенеджерЗаписи = РегистрыСведений.СообщенияПользователям.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.УникальныйИдентификаторСообщения = УникальныйИдентификаторСообщения;
        Если МенеджерЗаписи.Прочитать() Тогда
            МенеджерЗаписи.Ознакомлен = Истина;
            МенеджерЗаписи.Записать();
        КонецЕсли;
    КонецПроцедуры
    

    Важно: При использовании этого подхода, необходимо очень внимательно подойти к периодичности опроса и объему данных, которые передаются. Частые обращения к серверу или передача большого объема информации могут негативно сказаться на производительности системы и увеличить нагрузку на сервер приложений и СУБД.

  4. Отображение оповещения на клиенте: ПоказатьОповещениеПользователя.

    После того как обработчик ожидания получит новые сообщения с сервера, на клиенте вызывается метод ПоказатьОповещениеПользователя. Этот метод позволяет отобразить всплывающее окно в правом нижнем углу экрана. Оповещение может содержать текст, навигационную ссылку и статус важности. Важные оповещения попадают в центр оповещений и остаются там, пока пользователь с ними не ознакомится.

    Параметры ПоказатьОповещениеПользователя:

    • ТекстОповещения (строка): Основной текст сообщения.
    • ОписаниеОповещения (объект ОписаниеОповещения, опционально): Описание процедуры, которая будет вызвана при нажатии на оповещение. Позволяет создать интерактивные уведомления.
    • ИдентификаторУведомления (строка, опционально): Уникальный идентификатор оповещения для управления им (например, чтобы позже закрыть).
    • СрокОтображения (число, опционально): Время в секундах, через которое оповещение автоматически закроется.
    • Статус (перечисление СтатусОповещенияПользователя, опционально): Определяет внешний вид и поведение оповещения (например, Важное, Обычное). СтатусОповещенияПользователя.Важное гарантирует, что оповещение попадет в центр оповещений.

Выбор решения и общие рекомендации

Исходя из вышеизложенного, мы видим, что наиболее предпочтительным и эффективным является использование нового механизма уведомлений, доступного с версии 8.3.26. Он обеспечивает более надежную доставку, снижает нагрузку на систему и гораздо проще в реализации.

Если же ваша платформа ниже 8.3.26, то метод с ПодключитьОбработчикОжидания остается единственным нативным вариантом без Системы взаимодействия. При его реализации мы настоятельно рекомендуем следующее:

В любом случае, вне зависимости от выбранного подхода, наша задача — обеспечить, чтобы пользователи получали важную информацию оперативно и без лишних сложностей. Удачи в реализации!

← На главную