В процессе автоматизации торговой деятельности часто возникает необходимость оперативного информирования сотрудников о ключевых событиях в системе. Типичный пример — уведомление менеджера, кладовщика или руководителя о том, что в базе данных был создан документ определенного вида. В конфигурациях на управляемых формах эта задача решается достаточно просто с помощью встроенных механизмов платформы. Однако в конфигурации «Управление торговлей 10.3», работающей на обычных формах (толстый клиент), реализация такого функционала требует индивидуального архитектурного подхода.
В этой статье мы подробно разберем, как спроектировать и внедрить надежную систему оповещений пользователей о создании документов. Мы проанализируем различные технические подходы, разберем по шагам создание необходимых объектов метаданных и напишем программный код для реализации бизнес-логики.
Для создания надежной системы уведомлений нам необходимо решить три ключевые подзадачи:
Рассмотрим пошагово реализацию каждого этапа.
На форумах часто предлагают использовать событие ПриПрисвоенииНомера. Мы настоятельно не рекомендуем этот метод. Во-первых, при записи нового документа номер может присваиваться автоматически в самом конце транзакции, либо пользователь может отменить запись после присвоения номера. Во-вторых, этот метод не гарантирует, что документ действительно физически записан в базу данных.
Наиболее надежный способ — использовать подписку на событие ПриЗаписи (OnWrite). Разберем по шагам процесс создания подписки:
ОповеститьОСозданииДокументаПриЗаписи.ДокументОбъект.ЗаказПокупателя, ДокументОбъект.РеализацияТоваровУслуг).ПриЗаписи.ОповещенияСервер) с установленными флажками Сервер и Вызов сервера. Создайте в нем процедуру-обработчик.Проанализируем программный код обработчика подписки:
Процедура ОповеститьОСозданииДокументаПриЗаписиПриЗаписи(Источник, Отказ) Экспорт
// Исключаем обработку при обмене данными
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Проверяем, что документ действительно новый (создается впервые)
Если Источник.ЭтоНовый() Тогда
// Определяем получателя уведомления (например, ответственного или конкретную роль)
Получатель = НайтиПолучателяУведомления(Источник);
Если ЗначениеЗаполнено(Получатель) Тогда
ЗарегистрироватьСобытиеВРегистре(Источник.Ссылка, Получатель);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Использование метода ЭтоНовый() внутри транзакции записи гарантирует, что мы реагируем исключительно на факт первичного создания документа, полностью игнорируя последующие перепроведения или редактирования старых документов.
Для временного хранения неотправленных уведомлений мы создадим непериодический независимый регистр сведений ОчередьОповещенийПользователей. Использование регистра сведений решает проблему потери сообщений: если нужного пользователя нет на рабочем месте, запись будет дожидаться его входа в систему.
Разберем оптимальную структуру регистра сведений:
Пользователь (тип СправочникСсылка.Пользователи). Это измерение должно быть первым в списке для обеспечения быстрого поиска по индексу при опросе из сеанса конкретного пользователя.Документ (тип ДокументСсылка или составной тип из нужных документов).ДатаСоздания (тип Дата, состав даты — Время). Позволяет контролировать актуальность и сортировать сообщения.Теперь напишем вспомогательную процедуру записи события в созданный регистр:
Процедура ЗарегистрироватьСобытиеВРегистре(СсылкаНаДокумент, ЦелевойПользователь)
МенеджерЗаписи = РегистрыСведений.ОчередьОповещенийПользователей.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Пользователь = ЦелевойПользователь;
МенеджерЗаписи.Документ = СсылкаНаДокумент;
МенеджерЗаписи.ДатаСоздания = ТекущаяДата();
Попытка
МенеджерЗаписи.Записать(Истина); // Перезаписываем, если запись уже существовала
Исключение
ЗаписьЖурналаРегистрации("Оповещения.ОшибкаЗаписи",
УровеньЖурналаРегистрации.Ошибка, , ,
"Не удалось записать событие в регистр: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Поскольку в обычных формах отсутствует полноценный механизм push-уведомлений в реальном времени (без использования тяжеловесных внешних компонент), мы применим классический паттерн "pull" — периодический опрос базы данных клиентом.
Для этого в модуле обычного приложения (или в глобальном модуле) настроим подключение обработчика ожидания. Оптимальный интервал опроса составляет 10–30 секунд. Более частый опрос может создать избыточную нагрузку на СУБД при большом числе работающих пользователей.
Добавим в процедуру ПриНачалеРаботыСистемы следующий код:
Процедура ПриНачалеРаботыСистемы()
// Подключаем обработчик с интервалом 15 секунд
ПодключитьОбработчикОжидания("ПроверитьНовыеДокументыДляОповещения", 15);
КонецПроцедуры
Теперь реализуем глобальную процедуру опроса регистра:
Процедура ПроверитьНовыеДокументыДляОповещения() Экспорт
ТекущийПользователь = ПараметрыСеанса.ТекущийПользователь;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Очередь.Документ КАК Документ,
| Очередь.Документ.Представление КАК Представление
|ИЗ
| РегистрСведений.ОчередьОповещенийПользователей КАК Очередь
|ГДЕ
| Очередь.Пользователь = &ТекущийПользователь";
Запрос.УстановитьПараметр("ТекущийПользователь", ТекущийПользователь);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Вызываем визуальное оповещение пользователя
ПоказатьВсплывающееОкно(Выборка.Документ, Выборка.Представление);
// Удаляем запись из регистра, так как событие обработано
УдалитьСобытиеИзОчереди(ТекущийПользователь, Выборка.Документ);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Процедура удаления отработавших записей выглядит следующим образом:
Процедура УдалитьСобытиеИзОчереди(Пользователь, Документ)
МенеджерЗаписи = РегистрыСведений.ОчередьОповещенийПользователей.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Пользователь = Пользователь;
МенеджерЗаписи.Документ = Документ;
МенеджерЗаписи.Удалить();
КонецПроцедуры
В управляемом приложении доступен удобный метод ПоказатьОповещениеПользователя, выводящий аккуратное окно в правом нижнем углу экрана. В обычном приложении 1С:Предприятие 8.3/8.2 этот метод может работать некорректно или полностью отсутствовать в зависимости от режима запуска сеанса. В качестве готового варианта информирования можно использовать гаджет оповещения о задачах пользователя в УТ 10.3.
Для имитации красивого всплывающего уведомления в правом нижнем углу экрана мы можем создать специальную небольшую вспомогательную форму. Разберем алгоритм ее создания:
ВсплывающееОповещение и добавьте в нее форму (Обычную).Посмотрим на пример позиционирования формы при ее открытии:
Процедура ПриОткрытии()
// Примерный расчет координат на основе разрешения экрана.
// В реальных задачах можно использовать COM-объект "Wscript.Shell" для точного определения.
ШиринаЭкран = 1920;
ВысотаЭкран = 1080;
ЭтаФорма.Ширина = 300;
ЭтаФорма.Высота = 120;
ЭтаФорма.Лево = ШиринаЭкран - ЭтаФорма.Ширина - 50;
ЭтаФорма.Верх = ВысотаЭкран - ЭтаФорма.Высота - 100;
// Настраиваем автозакрытие формы через 7 секунд, если пользователь не кликнул по ней
ПодключитьОбработчикОжидания("ЗакрытьФормуАвтоматически", 7);
КонецПроцедуры
Описанный выше метод опроса регистра сведений отлично подходит, если пользователи постоянно находятся внутри интерфейса 1С. Однако на практике сотрудники часто сворачивают программу или работают вне офиса. В этом случае крайне эффективным решением становится интеграция 1С с мессенджерами, в частности, создание Telegram-бота.
Этот способ позволяет отправлять уведомления асинхронно, мгновенно и без создания дополнительной нагрузки на базу данных 1С постоянными запросами опроса регистра сведений. Разберем по шагам, как настроить отправку сообщений в Telegram при создании документа:
ПриЗаписи, которую мы создали на Шаге 1, вместо записи в регистр сведений вызывайте процедуру отправки HTTP-запроса на сервер Telegram.Рассмотрим пример кода отправки сообщения в Telegram:
Процедура ОтправитьОповещениеВTelegram(ТекстСообщения, ТокенБота, ИДЧата) Экспорт
// Формируем URL для отправки API-запроса
АдресРесурса = "bot" + ТокенБота + "/sendMessage";
// Подготавливаем параметры запроса
ПараметрыЗапроса = "chat_id=" + ИДЧата + "&text=" + КодироватьСтроку(ТекстСообщения, СпособКодированияСтроки.URLКодирование);
// Настраиваем безопасное SSL-соединение
ssl = Новый ЗащищенноеСоединениеOpenSSL();
Попытка
HTTPСоединение = Новый HTTPСоединение("api.telegram.org", 443, , , , , ssl);
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса + "?" + ПараметрыЗапроса);
HTTPОтвет = HTTPСоединение.Получить(HTTPЗапрос);
Если HTTPОтвет.КодСостояния <> 200 Тогда
ЗаписьЖурналаРегистрации("Telegram.Ошибка", УровеньЖурналаРегистрации.Предупреждение, , ,
"Код ответа сервера: " + HTTPОтвет.КодСостояния + ". Ответ: " + HTTPОтвет.ПолучитьТелоКакСтроку());
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("Telegram.КритическаяОшибка", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Этот код отправляет текстовое сообщение напрямую на сервера Telegram. Пользователь моментально получит уведомление на своем мобильном устройстве или персональном компьютере, даже если программа 1С в этот момент закрыта. При необходимости оповещения через корпоративный мессенджер подойдёт инструмент для отправки сообщений в Rocket.Chat через API.
Мы детально проанализировали задачу оповещения пользователей в конфигурации «Управление торговлей 10.3» и рассмотрели два наиболее жизнеспособных решения. Классическая схема «Подписка на событие -> Регистр Сведений -> Обработчик ожидания» является полностью автономной, независимой от интернета и надежно хранит историю непрочитанных сообщений непосредственно внутри базы данных. Альтернативный вариант с интеграцией Telegram современен, разгружает СУБД от частых регламентных запросов и позволяет держать сотрудников в курсе событий компании в режиме 24/7.