Как реализовать таймер уведомлений для заметок пользователей в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки корпоративных систем часто возникает задача создания механизма внутренних напоминаний. Например, пользователю необходимо создать заметку и получить уведомление о ней в строго определенное время. Рассмотрим, как реализовать такую систему «с нуля» в самописной конфигурации, используя стандартные механизмы платформы 1С:Предприятие 8.3: периодические регистры сведений и обработчики ожидания.

Структура хранения данных

Прежде всего, нам необходимо определиться, где будут храниться наши заметки. Для этой цели идеально подходит Регистр сведений. Разберем структуру регистра ЗаметкиПользователя:

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

Чтобы система «понимала», когда нужно показать уведомление, нам требуется механизм периодической проверки базы данных. В управляемом приложении 1С для этого используется метод ПодключитьОбработчикОжидания(). Рассмотрим, как инициализировать этот процесс при запуске программы.

Откроем Модуль управляемого приложения и проанализируем следующий код:


Процедура ПриНачалеРаботыСистемы()
    // Подключаем проверку каждые 60 секунд. 
    // Интервал в 3-5 секунд избыточен и создаст лишнюю нагрузку на сервер.
    ПодключитьОбработчикОжидания("ПроверитьУведомленияПользователя", 60, Ложь);
КонецПроцедуры

Процедура ПроверитьУведомленияПользователя() Экспорт
    // Вызываем процедуру клиентского общего модуля
    ЗаметкиКлиент.ПроверитьНаличиеАктивныхЗаметок();
КонецПроцедуры

Здесь важно обратить внимание на параметр Ложь в методе подключения — он означает, что обработчик будет выполняться многократно с заданным интервалом, пока мы его принудительно не отключим.

Реализация серверной проверки

Теперь выясним, как эффективно искать заметки, время которых наступило. Мы не должны тянуть все записи на клиент. Напишем функцию в серверном общем модуле (например, ЗаметкиСервер), которая будет возвращать только необходимые данные.

Проанализируем запрос, который отбирает актуальные записи:


Функция ПолучитьАктуальныеЗаметкиТекущегоПользователя() Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЗаметкиПользователя.Период КАК ВремяНапоминания,
        |	ЗаметкиПользователя.Описание КАК Описание
        |ИЗ
        |	РегистрСведений.ЗаметкиПользователя КАК ЗаметкиПользователя
        |ГДЕ
        |	ЗаметкиПользователя.Пользователь = &ТекущийПользователь
        |	И ЗаметкиПользователя.Напомнить = ИСТИНА
        |	И ЗаметкиПользователя.Завершена = ЛОЖЬ
        |	И ЗаметкиПользователя.Период <= &ТекущийМомент";

    // Используем ТекущаяДатаСеанса для корректной работы в разных часовых поясах
    Запрос.УстановитьПараметр("ТекущийМомент", ТекущаяДатаСеанса());
    Запрос.УстановитьПараметр("ТекущийПользователь", Пользователи.ТекущийПользователь());

    Результат = Запрос.Выполнить();
    Если Результат.Пустой() Тогда
        Возврат Неопределено;
    КонецЕсли;

    Возврат Результат.Выгрузить();
КонецФункции

Важный момент: использование ТекущаяДатаСеанса() критично, если ваши пользователи работают в разных часовых поясах относительно сервера. Также обратите внимание на условие ЗаметкиПользователя.Период <= &ТекущийМомент. Если мы будем использовать строгое равенство, то при пропуске одной минуты (например, из-за зависания компьютера или тяжелого отчета) уведомление никогда не сработает.

Отображение уведомления пользователю

Когда данные получены на клиент, нам нужно их визуализировать. В обсуждении предлагалось использовать модальные окна, однако в современных интерфейсах это считается «дурным тоном», так как блокирует работу пользователя. Рассмотрим более элегантный способ — всплывающие оповещения.

В общем модуле ЗаметкиКлиент реализуем логику обработки:


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

Оптимизация производительности и решение проблем

Разберем типичные ошибки, которые могут возникнуть при реализации данной задачи:

  1. Частота опроса. Установка таймера на 1–3 секунды создаст лавинообразную нагрузку на сервер при большом количестве активных пользователей. Оптимальный интервал для заметок — 60 секунд (1 минута).
  2. Бесконечные окна. Если вы используете метод ОткрытьФорму(), то при наличии трех пропущенных заметок у пользователя откроется три окна сразу. Чтобы этого избежать, перед открытием формы проверяйте, не открыта ли она уже для данной записи.
  3. Индексация данных. Чтобы запрос к регистру сведений работал быстро даже при миллионах записей, убедитесь, что измерения индексированы. В периодических регистрах поле Период индексируется автоматически, что обеспечивает высокую скорость выборки по времени.
  4. Завершение работы. Если пользователь нажал «ОК» в уведомлении, обязательно записывайте этот факт в базу данных (устанавливайте флаг Завершена = Истина), иначе при следующем срабатывании обработчика ожидания уведомление появится снова.

Использование БСП для профессиональной разработки

Если вы работаете не в полностью «чистой» базе, а используете Библиотеку стандартных подсистем (БСП), крайне рекомендуется не изобретать велосипед, а воспользоваться подсистемой НапоминанияПользователя. Рассмотрим ее преимущества:

Проанализировав ситуацию, мы видим, что создание собственной системы таймеров — отличная задача для понимания клиент-серверного взаимодействия в 1С. Однако для крупных систем всегда стоит отдавать предпочтение оптимизированным механизмам БСП или расширять функционал через фоновые задания, чтобы минимизировать количество вызовов Запрос.Выполнить() в контексте клиентского сеанса.

← На главную