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