При разработке функционала в 1С, основанного на регламентных заданиях, часто возникает потребность передать в выполняемый метод задания не только простые типы данных (строки, числа, булево, даты), но и более сложные объекты, такие как структуры, массивы, списки значений, или даже ссылки на объекты базы данных. Стандартный механизм регламентных заданий предоставляет свойство ДополнительныеПараметры, однако оно имеет ограничения: в него можно помещать только значения, которые могут быть сериализованы средствами платформы 1С. Это означает, что напрямую передать произвольную структуру со сложными вложенными объектами или ссылки на временные данные не получится, что приводит к ошибкам выполнения. В этой статье мы подробно рассмотрим, как обойти это ограничение и обеспечить надежную передачу сложных параметров в регламентные задания, используя проверенные подходы.
Мы вместе разберем два основных метода, которые позволяют эффективно работать со сложными параметрами: использование временного хранилища значений и применение специализированного регистра сведений. Каждый из этих подходов имеет свои преимущества и области применения, и мы покажем, как их реализовать на практике с помощью примеров кода.
Одним из наиболее часто используемых и гибких способов передачи сложных, но временных параметров в регламентное задание является применение платформенного механизма временного хранилища значений. Этот метод идеально подходит, когда параметры нужны только для однократного выполнения задания и не требуют постоянного хранения в базе данных.
Почему ПоместитьВоВременноеХранилище?
Функция ПоместитьВоВременноеХранилище позволяет сохранить любое сериализуемое значение (структуру, массив, список значений и т.д.) во временном хранилище и возвращает уникальный идентификатор (UUID) этого значения. Этот идентификатор является простой строкой и может быть успешно передан через свойство ДополнительныеПараметры регламентного задания. Затем, внутри выполняемого метода регламентного задания, мы можем использовать функцию ПолучитьИзВременногоХранилища, чтобы восстановить исходный объект по его идентификатору.
Рассмотрим по шагам, как реализовать этот подход.
На первом этапе, в том месте кода, где мы планируем запустить или обновить регламентное задание, мы формируем структуру или любой другой объект с необходимыми параметрами. Затем используем функцию ПоместитьВоВременноеХранилище для сохранения этих параметров и получения их идентификатора. Этот идентификатор мы и будем передавать в регламентное задание.
Предположим, у нас есть список документов и период, которые нужно обработать регламентным заданием. Мы поместим их в структуру.
// Где-то в коде, который создает или изменяет регламентное задание (например, в обработке, форме или модуле конфигурации)
Функция СоздатьИлиИзменитьРегламентноеЗадание()
// ... Здесь могут быть другие настройки или получение существующих данных
// 1. Формируем сложные параметры, которые нужно передать
МойСписокДокументов = Новый СписокЗначений();
МойСписокДокументов.Добавить(Документы.РеализацияТоваровУслуг.НайтиПоНомеру("000000001"));
МойСписокДокументов.Добавить(Документы.РеализацияТоваровУслуг.НайтиПоНомеру("000000002"));
МойПериод = Новый Структура("ДатаНачала, ДатаОкончания", '20230101', '20230131');
// Объединяем параметры в одну структуру для удобства передачи
НовыеПараметры = Новый Структура();
НовыеПараметры.Вставить("СписокДокументов", МойСписокДокументов);
НовыеПараметры.Вставить("Период", МойПериод);
НовыеПараметры.Вставить("Ответственный", Пользователи.ТекущийПользователь()); // Можно передавать и ссылки
// 2. Помещаем сложные параметры во временное хранилище и получаем идентификатор
// Этот идентификатор и будет передан в ДополнительныеПараметры регламентного задания
ИдентификаторХранилища = ПоместитьВоВременноеХранилище(НовыеПараметры);
// 3. Находим или создаем регламентное задание
// Укажем уникальное имя для нашего задания
ИмяРегламентногоЗадания = "МоеРегламентноеЗаданиеДляОбработкиДокументов";
РегламентноеЗаданиеОбъект = РегламентныеЗадания.НайтиПоИмени(ИмяРегламентногоЗадания);
Если РегламентноеЗаданиеОбъект = Неопределено Тогда
// Если задание не найдено, создаем новое
РегламентноеЗаданиеОбъект = РегламентныеЗадания.СоздатьРегламентноеЗадание();
РегламентноеЗаданиеОбъект.Имя = ИмяРегламентногоЗадания;
РегламентноеЗаданиеОбъект.Использование = Истина; // Задание активно
// Важно: регламентное задание всегда выполняется от имени какого-либо пользователя
РегламентноеЗаданиеОбъект.Пользователь = Пользователи.НайтиПоИмени("Администратор");
// Ключ для уникальной идентификации задания, если их несколько с одинаковым именем метода
РегламентноеЗаданиеОбъект.Ключ = ИмяРегламентногоЗадания + "Ключ";
// Настраиваем расписание выполнения, например, каждый час
Расписание = РегламентноеЗаданиеОбъект.Расписание.Добавить();
Расписание.НачалоПовтора = Новый Дата(1,1,1);
Расписание.ОкончаниеПовтора = Новый Дата(2999,1,1);
Расписание.ИнтервалПовтора = 3600; // Повторять каждые 3600 секунд (1 час)
Расписание.Использование = Истина;
КонецЕсли;
// 4. Указываем метод, который будет выполняться
// Метод должен быть экспортным в модуле менеджера или в общем модуле
РегламентноеЗаданиеОбъект.ИмяМетода = "МояКонфигурация.Модули.МодульМенеджера.ВыполнитьЗадание";
// 5. Присваиваем идентификатор хранилища свойству ДополнительныеПараметры
РегламентноеЗаданиеОбъект.ДополнительныеПараметры = ИдентификаторХранилища;
// 6. Записываем регламентное задание, чтобы изменения вступили в силу
Попытка
РегламентноеЗаданиеОбъект.Записать();
Сообщить("Регламентное задание успешно создано/обновлено с параметрами.");
Исключение
Сообщить("Ошибка при записи регламентного задания: " + ОписаниеОшибки());
КонецПопытки;
КонецФункции
В модуле, который выполняет наше регламентное задание (обычно это общий модуль или модуль менеджера), мы должны создать экспортную функцию с сигнатурой ВыполнитьЗадание(ПараметрыЗадания). Внутри этой функции мы получим переданный идентификатор из ПараметрыЗадания.ДополнительныеПараметры и используем его для извлечения наших сложных параметров из временного хранилища.
// Модуль менеджера (например, 'Модули.МодульМенеджера' в вашей конфигурации)
// Важно: этот модуль должен быть доступен для вызова фоновыми/регламентными заданиями
// Проверьте свойства модуля: 'ВызовСервера' и 'КлиентСервер' (если используются обе стороны)
Функция ВыполнитьЗадание(ПараметрыЗадания) Экспорт
Попытка
// 1. Проверяем, был ли передан идентификатор хранилища
Если ПараметрыЗадания.ДополнительныеПараметры <> Неопределено Тогда
ИдентификаторХранилища = ПараметрыЗадания.ДополнительныеПараметры;
// 2. Получаем исходные параметры из временного хранилища по идентификатору
ПараметрыДляОбработки = ПолучитьИзВременногоХранилища(ИдентификаторХранилища);
Если ПараметрыДляОбработки = Неопределено Тогда
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Ошибка, , ,
"Параметры для задания '" + ПараметрыЗадания.Имя +
"' не найдены во временном хранилище по идентификатору: " + ИдентификаторХранилища);
Возврат;
КонецЕсли;
// 3. Извлекаем отдельные параметры из структуры
СписокДокументов = ПараметрыДляОбработки.СписокДокументов;
Период = ПараметрыДляОбработки.Период;
Ответственный = ПараметрыДляОбработки.Ответственный;
// 4. ... Дальнейшая логика обработки с использованием полученных параметров
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Информация, , ,
"Запуск задания '" + ПараметрыЗадания.Имя +
"' с параметрами: СписокДокументов (" + СписокДокументов.Количество() +
"), Период с " + Период.ДатаНачала + " по " + Период.ДатаОкончания + ".");
// Пример обработки:
Для Каждого Документ Из СписокДокументов Цикл
// Обрабатываем каждый документ
// Например, перепроводим или обновляем данные
Сообщить("Обрабатываем документ: " + Документ.Ссылка);
КонецЦикла;
// 5. Очень важно: Удалить параметры из временного хранилища после использования.
// Это предотвращает накопление ненужных данных и утечки памяти.
УдалитьИзВременногоХранилища(ИдентификаторХранилища);
Иначе
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Предупреждение, , ,
"Регламентное задание '" + ПараметрыЗадания.Имя +
"' запущено без дополнительных параметров.");
КонецЕсли;
Исключение
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка выполнения регламентного задания '" + ПараметрыЗадания.Имя +
"': " + ОписаниеОшибки());
КонецПопытки;
КонецФункции
Преимущества метода временного хранилища:
Недостатки метода временного хранилища:
УдалитьИзВременногоХранилища, иначе данные будут занимать память до истечения срока жизни (или до перезапуска сервера).Когда параметры регламентного задания должны быть доступны постоянно, могут меняться между запусками, или когда одно и то же задание может выполняться с разными наборами параметров, не связанными с конкретным запуском, более надежным решением является использование специализированного регистра сведений. Этот подход обеспечивает персистентность данных и позволяет гибко управлять параметрами.
Почему регистр сведений?
Регистр сведений — это объект метаданных 1С, предназначенный для хранения произвольных данных, не зависящих от документов. Мы можем создать регистр сведений, в котором каждое измерение будет определять уникальность набора параметров (например, имя регламентного задания), а ресурс будет хранить сами параметры в виде ХранилищеЗначения. Объект ХранилищеЗначения позволяет сохранять любые данные в бинарном виде и затем восстанавливать их.
Разберем реализацию этого подхода.
Первым шагом нам необходимо создать в конфигурации новый регистр сведений. Назовем его, например, ПараметрыРегламентныхЗаданий.
ИдентификаторЗадания (Тип: Строка, Длина: 255). Это измерение будет хранить уникальное имя или ключ нашего регламентного задания, позволяя нам однозначно идентифицировать набор параметров.Параметры (Тип: ХранилищеЗначения). Этот ресурс будет хранить сериализованные сложные параметры для соответствующего задания.Теперь мы создадим функцию, которая будет сохранять или обновлять параметры для конкретного регламентного задания в нашем новом регистре сведений. Эта функция будет принимать имя задания и структуру с параметрами.
// Где-то в коде, который создает или изменяет регламентное задание (например, в общем модуле)
Функция ЗадатьПараметрыРегламентногоЗадания(ИмяЗадания, НовыеПараметры) Экспорт
// 1. Создаем набор записей для регистра сведений
НаборЗаписей = РегистрыСведений.ПараметрыРегламентныхЗаданий.СоздатьНаборЗаписей();
// 2. Устанавливаем отбор по идентификатору задания, чтобы найти существующие параметры
НаборЗаписей.Отбор.ИдентификаторЗадания.Установить(ИмяЗадания);
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() > 0 Тогда
// 3. Если записи найдены, обновляем существующую запись
НаборЗаписей[0].Параметры = Новый ХранилищеЗначения(НовыеПараметры);
Иначе
// 4. Если записей нет, создаем новую запись
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.ИдентификаторЗадания = ИмяЗадания;
НоваяЗапись.Параметры = Новый ХранилищеЗначения(НовыеПараметры);
КонецЕсли;
// 5. Записываем набор записей в регистр
Попытка
НаборЗаписей.Записать();
Сообщить("Параметры для регламентного задания '" + ИмяЗадания + "' успешно сохранены/обновлены.");
Исключение
Сообщить("Ошибка при сохранении параметров для задания '" + ИмяЗадания + "': " + ОписаниеОшибки());
КонецПопытки;
КонецФункции
// Пример вызова этой функции при создании/обновлении регламентного задания
Функция СоздатьИлиИзменитьРегламентноеЗаданиеЧерезРегистр()
// ... (предполагается, что код по созданию/поиску РегламентногоЗаданияОбъект аналогичен предыдущему примеру)
ИмяРегламентногоЗадания = "МоеРегламентноеЗаданиеЧерезРС";
// Подготавливаем параметры
МойСписокДокументов = Новый СписокЗначений();
МойСписокДокументов.Добавить(Документы.РеализацияТоваровУслуг.НайтиПоНомеру("000000003"));
МойПериод = Новый Структура("ДатаНачала, ДатаОкончания", '20230201', '20230228');
НовыеПараметры = Новый Структура("СписокДокументов, Период", МойСписокДокументов, МойПериод);
// Сохраняем параметры в регистре сведений
ЗадатьПараметрыРегламентногоЗадания(ИмяРегламентногоЗадания, НовыеПараметры);
// Находим или создаем регламентное задание
РегламентноеЗаданиеОбъект = РегламентныеЗадания.НайтиПоИмени(ИмяРегламентногоЗадания);
Если РегламентноеЗаданиеОбъект = Неопределено Тогда
РегламентноеЗаданиеОбъект = РегламентныеЗадания.СоздатьРегламентноеЗадание();
РегламентноеЗаданиеОбъект.Имя = ИмяРегламентногоЗадания;
РегламентноеЗаданиеОбъект.Использование = Истина;
РегламентноеЗаданиеОбъект.Пользователь = Пользователи.НайтиПоИмени("Администратор");
РегламентноеЗаданиеОбъект.Ключ = ИмяРегламентногоЗадания + "Ключ";
Расписание = РегламентноеЗаданиеОбъект.Расписание.Добавить();
Расписание.ИнтервалПовтора = 7200; // Каждый 2 часа
Расписание.Использование = Истина;
КонецЕсли;
// В ДополнительныеПараметры теперь достаточно передать только имя задания
// по которому мы будем искать параметры в регистре сведений
РегламентноеЗаданиеОбъект.ДополнительныеПараметры = ИмяРегламентногоЗадания;
РегламентноеЗаданиеОбъект.ИмяМетода = "МояКонфигурация.Модули.МодульМенеджера.ВыполнитьЗаданиеИзРегистра";
РегламентноеЗаданиеОбъект.Записать();
КонецФункции
В экспортной функции регламентного задания мы теперь будем получать имя задания из ПараметрыЗадания.ДополнительныеПараметры (или из ПараметрыЗадания.Имя, если мы используем это же имя как идентификатор). По этому имени мы найдем соответствующую запись в регистре сведений и извлечем из нее параметры.
// Модуль менеджера (например, 'Модули.МодульМенеджера')
Функция ВыполнитьЗаданиеИзРегистра(ПараметрыЗадания) Экспорт
Попытка
// 1. Получаем имя регламентного задания, которое будем использовать как идентификатор
// Можно использовать ПараметрыЗадания.Имя, если оно является ключом для регистра
// Или, если мы передали его через ДополнительныеПараметры, то:
ИмяИлиКлючЗадания = ПараметрыЗадания.ДополнительныеПараметры;
// Или, если не передавали, но имя задания уникально:
// ИмяИлиКлючЗадания = ПараметрыЗадания.Имя;
Если ЗначениеЗаполнено(ИмяИлиКлючЗадания) Тогда
// 2. Создаем набор записей для чтения из регистра
НаборЗаписей = РегистрыСведений.ПараметрыРегламентныхЗаданий.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ИдентификаторЗадания.Установить(ИмяИлиКлючЗадания);
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() > 0 Тогда
// 3. Получаем параметры из ХранилищаЗначения
ПараметрыДляОбработки = НаборЗаписей[0].Параметры.Получить();
Если ПараметрыДляОбработки = Неопределено Тогда
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Ошибка, , ,
"Не удалось получить параметры из хранилища для задания '" +
ИмяИлиКлючЗадания + "'.");
Возврат;
КонецЕсли;
// 4. Извлекаем отдельные параметры из структуры
СписокДокументов = ПараметрыДляОбработки.СписокДокументов;
Период = ПараметрыДляОбработки.Период;
// 5. ... Дальнейшая логика обработки с использованием полученных параметров
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Информация, , ,
"Запуск задания '" + ИмяИлиКлючЗадания +
"' с параметрами из РС: СписокДокументов (" + СписокДокументов.Количество() +
"), Период с " + Период.ДатаНачала + " по " + Период.ДатаОкончания + ".");
// Пример обработки:
Для Каждого Документ Из СписокДокументов Цикл
// Обрабатываем каждый документ
Сообщить("Обрабатываем документ (из РС): " + Документ.Ссылка);
КонецЦикла;
// 6. Опционально: Удаление параметров из регистра, если они нужны только для одного выполнения
// Если параметры могут меняться или используются многократно, этот шаг не нужен
// НаборЗаписей.Очистить();
// НаборЗаписей.Записать();
Иначе
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Предупреждение, , ,
"Параметры для регламентного задания '" + ИмяИлиКлючЗадания +
"' не найдены в регистре сведений 'ПараметрыРегламентныхЗаданий'.");
КонецЕсли;
Иначе
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Предупреждение, , ,
"Регламентное задание '" + ПараметрыЗадания.Имя +
"' запущено, но идентификатор параметров не передан.");
КонецЕсли;
Исключение
ЗаписатьЖурналРегистрации("РегламентноеЗадание", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка выполнения регламентного задания '" + ПараметрыЗадания.Имя +
"': " + ОписаниеОшибки());
КонецПопытки;
КонецФункции
Преимущества метода с регистром сведений:
Недостатки метода с регистром сведений:
Вне зависимости от выбранного метода, существуют общие рекомендации, которые помогут вам создать надежные и производительные регламентные задания:
Попытка...Исключение и логируйте все ошибки в журнал регистрации 1С. Это критически важно для диагностики проблем, поскольку регламентные задания выполняются в фоновом режиме и не имеют интерактивного интерфейса для отображения ошибок.ЗаписатьЖурналРегистрации для фиксации ключевых этапов выполнения задания, количества обработанных объектов, предупреждений и ошибок. Это поможет вам отслеживать работу задания.Ключ для их однозначной идентификации, чтобы не перепутать параметры и расписания.Пользователь в объекте РегламентноеЗадание), имеет необходимые права доступа для выполнения всех операций, которые предусматривает ваше задание (чтение/запись документов, регистров и т.д.).Выбирая между временным хранилищем и регистром сведений, мы рекомендуем отталкиваться от специфики задачи. Если параметры нужны для одного конкретного запуска и не требуют сохранения, временное хранилище будет более легким и быстрым решением. Если же требуется персистентность, возможность изменения параметров без пересоздания задания или разделение параметров для разных экземпляров одного задания, то регистр сведений станет более мощным и управляемым инструментом.