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