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