Как настроить индивидуальное расписание регламентного задания для каждого элемента справочника в 1С?

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

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

В этой статье мы подробно разберем, как решить эту задачу. Мы проанализируем три основных архитектурных подхода, рассмотрим их преимущества и недостатки, а также приведем конкретные примеры программного кода.

Способ 1. Паттерн «Регламентное задание — Диспетчер» (Рекомендуемый вариант)

Это наиболее гибкий, производительный и масштабируемый вариант, который не перегружает СУБД и планировщик платформы «1С:Предприятие». Суть метода заключается в создании одного общего регламентного задания (диспетчера), которое запускается достаточно часто (например, раз в 1–5 минут) и инициирует выполнение задач по элементам справочника, у которых подошло время запуска.

Давайте разберем по шагам, как реализовать этот паттерн.

Шаг 1. Хранение расписания в справочнике

Объект платформы РасписаниеРегламентногоЗадания нельзя напрямую сохранить в реквизит базы данных стандартного типа. Чтобы обойти это ограничение, мы будем сериализовать расписание в XML-строку и хранить его в реквизите справочника с типом Строка (неограниченной длины) или ХранилищеЗначения. Также нам понадобятся реквизиты Использование (тип Булево) и индексированный реквизит ДатаСледующегоЗапуска (тип Дата).

Рассмотрим, как программно сохранить расписание, настроенное пользователем через стандартный диалог:


&НаКлиенте
Процедура НастроитьРасписание(Команда)
    
    // Открываем диалог настройки расписания
    Диалог = Новый ДиалогРасписанияРегламентногоЗадания(ПолучитьРасписаниеИзСтроки(Объект.РасписаниеXML));
    Оповещение = Новый ОписаниеОповещения("ПослеНастройкиРасписания", ЭтотОбъект);
    Диалог.Показать(Оповещение);
    
КонецПроцедуры

&НаКлиенте
Процедура ПослеНастройкиРасписания(Расписание, ДополнительныеПараметры) Экспорт
    
    Если Расписание <> Неопределено Тогда
        Объект.РасписаниеXML = ПолучитьСтрокуXMLИзРасписания(Расписание);
        // Рассчитываем дату следующего запуска прямо при записи расписания
        Объект.ДатаСледующегоЗапуска = ПолучитьДатуСледующегоЗапускаНаСервере(Расписание);
    КонецЕсли;
    
КонецПроцедуры

Для преобразования расписания в XML и обратно на сервере используются следующие функции:


&НаСервереБезКонтекста
Функция ПолучитьСтрокуXMLИзРасписания(Расписание)
    
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    ЗаписатьXML(ЗаписьXML, Расписание);
    Возврат ЗаписьXML.Закрыть();
    
КонецФункции

&НаСервереБезКонтекста
Функция ПолучитьРасписаниеИзСтроки(СтрокаXML)
    
    Если ПустаяСтрока(СтрокаXML) Тогда
        Возврат Новый РасписаниеРегламентногоЗадания;
    КонецЕсли;
    
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СтрокаXML);
    Возврат ПрочитатьXML(ЧтениеXML, Тип("РасписаниеРегламентногоЗадания"));
    
КонецФункции

Шаг 2. Расчет даты следующего запуска

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


&НаСервереБезКонтекста
Функция ПолучитьДатуСледующегоЗапускаНаСервере(Расписание)
    Возврат Расписание.ПолучитьДатуСледующегоЗапуска(ТекущаяДатаСеанса());
КонецФункции

Шаг 3. Логика работы Диспетчера

Создаем общее регламентное задание, которое обращается к общему серверному модулю. Процедура-диспетчер выбирает только те элементы, время запуска которых уже наступило, запускает их выполнение в фоновом режиме, а затем обновляет дату следующего запуска для каждого отработавшего элемента:


Процедура ВыполнитьДиспетчеризациюЗаданий() Экспорт
    
    ТекущееВремя = ТекущаяДатаСеанса();
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	МойСправочник.Ссылка КАК Ссылка,
        |	МойСправочник.РасписаниеXML КАК РасписаниеXML
        |ИЗ
        |	Справочник.МойСправочник КАК МойСправочник
        |ГДЕ
        |	МойСправочник.Использование = ИСТИНА
        |	И МойСправочник.ДатаСледующегоЗапуска <= &ТекущееВремя";
    
    Запрос.УстановитьПараметр("ТекущееВремя", ТекущееВремя);
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        
        // Запускаем обработку элемента асинхронно через фоновое задание
        ПараметрыЗапуска = Новый Массив;
        ПараметрыЗапуска.Добавить(Выборка.Ссылка);
        
        ФоновыеЗадания.Выполнить(
            "ОбщийМодульСервер.ВыполнитьДействиеПоЭлементу", 
            ПараметрыЗапуска, 
            Строка(Выборка.Ссылка.УникальныйИдентификатор()), 
            "Запуск обработки для: " + Строка(Выборка.Ссылка)
        );
        
        // Рассчитываем новое время следующего запуска
        Расписание = ПолучитьРасписаниеИзСтроки(Выборка.РасписаниеXML);
        СледующийЗапуск = Расписание.ПолучитьДатуСледующегоЗапуска(ТекущееВремя + 1); // Сдвиг на секунду вперед
        
        // Обновляем данные в справочнике
        ОбъектЭлемента = Выборка.Ссылка.ПолучитьОбъект();
        ОбъектЭлемента.ДатаСледующегоЗапуска = СледующийЗапуск;
        ОбъектЭлемента.Записать();
        
    КонецЦикла;
    
КонецПроцедуры

Этот подход гарантирует высокую скорость работы, так как запрос к СУБД происходит по индексированному полю даты, а сами тяжелые процессы выполняются параллельно в фоновых заданиях, не блокируя работу основного диспетчера.

Способ 2. Динамическое создание регламентных заданий платформы

Платформа «1С:Предприятие» позволяет программно создавать полноценные регламентные задания в режиме «1С:Предприятие» (в пользовательском режиме).

Рассмотрим этот вариант. При таком подходе каждому элементу справочника будет соответствовать свое собственное уникальное регламентное задание в общем списке системных регламентных заданий.

Реализация механизма:

  1. В справочник добавляется реквизит ИдентификаторРегламентногоЗадания (тип УникальныйИдентификатор или Строка).
  2. При записи элемента справочника мы проверяем, создано ли уже регламентное задание. Если нет — создаем его программно. Если создано — обновляем его расписание.
  3. В параметрах регламентного задания мы передаем ссылку на текущий элемент справочника.

Посмотрим на пример кода, выполняемый в модуле объекта справочника при записи:


Процедура ПередЗаписью(Отказ)
    
    // Проверим, заполнено ли расписание
    Если ПустаяСтрока(РасписаниеXML) Тогда
        Возврат;
    КонецЕсли;
    
    // Попытаемся найти существующее регламентное задание
    РегЗадание = Неопределено;
    Если ЗначениеЗаполнено(ИдентификаторРегламентногоЗадания) Тогда
        ИД = Новый УникальныйИдентификатор(ИдентификаторРегламентногоЗадания);
        РегЗадание = РегламентныеЗадания.НайтиПоУникальномуИдентификатору(ИД);
    КонецЕсли;
    
    // Если задание не найдено, создаем новое
    Если РегЗадание = Неопределено Тогда
        РегЗадание = РегламентныеЗадания.СоздатьРегламентноеЗадание("ВыполнитьДействиеПоЭлементуСправочника");
        ИдентификаторРегламентногоЗадания = Строка(РегЗадание.УникальныйИдентификатор);
    КонецЕсли;
    
    // Настраиваем параметры регламентного задания
    РегЗадание.Наименование = "Обработка элемента: " + Наименование;
    РегЗадание.Использование = Использование;
    
    // Передаем ссылку на текущий элемент в массив параметров задания
    ПараметрыЗадания = Новый Массив;
    ПараметрыЗадания.Добавить(Ссылка);
    РегЗадание.Параметры = ПараметрыЗадания;
    
    // Устанавливаем расписание
    РегЗадание.Расписание = ПолучитьРасписаниеИзСтроки(РасписаниеXML);
    
    // Сохраняем регламентное задание в системном списке
    РегЗадание.Записать();
    
КонецПроцедуры

Не забудьте обработать процедуру удаления элемента справочника: при удалении элемента связанное регламентное задание также необходимо программно удалить из системы с помощью метода РегламентноеЗадание.Удалить().

Важное ограничение метода: Данный вариант отлично подходит, если количество элементов справочника невелико (до 50–100 элементов). Если в вашем справочнике будут тысячи элементов, создание тысяч системных регламентных заданий создаст колоссальную нагрузку на планировщик платформы и СУБД, что может привести к зависанию сервера 1С.

Способ 3. Использование подсистемы «Очередь заданий» БСП

Если ваша конфигурация разработана на базе БСП (Библиотеки Стандартных Подсистем), например, «1С:ERP», «1С:Комплексная автоматизация», «Управление торговлей» или «Бухгалтерия предприятия», то в вашем распоряжении уже есть готовый мощный промышленный механизм — подсистема «Очередь заданий».

Этот механизм специально спроектирован для работы в нагруженных системах, а также полностью совместим с работой в облачной модели сервиса (например, в «1С:Фреш»), где создание обычных регламентных заданий напрямую запрещено из соображений безопасности.

В БСП за управление такими задачами отвечает справочник ШаблоныЗаданийОчереди и регистр сведений ОчередьЗаданий. Чтобы задействовать этот механизм, выполните следующие шаги:

  1. Изучите общий модуль ОчередьЗаданий вашей конфигурации.
  2. Для работы с расписанием используйте функции общего модуля БСП ОбщегоНазначенияКлиентСервер.РасписаниеВСтруктуру() и обратную функцию СтруктураВРасписание(). Это позволяет легко упаковывать сложные объекты расписания в простые структуры данных для безопасной передачи между сервером и клиентом.
  3. Вместо самостоятельного написания планировщиков, регистрируйте ваши задания через программный интерфейс БСП: ОчередьЗаданий.ДобавитьЗадание(), передавая туда имя метода, параметры и структуру расписания. БСП сама возьмет на себя заботу о своевременном запуске, логировании, обработке ошибок и перезапуске в случае аварийного завершения фонового процесса.

Резюме и рекомендации

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

← На главную