В процессе эксплуатации систем на базе 1С:Предприятие 8.3 администраторы и разработчики часто сталкиваются с ситуацией, когда настройки регламентных заданий в режиме «Конфигуратор» и в режиме «Предприятие» начинают различаться. Платформа устроена таким образом, что после первого запуска базы данных значения расписаний копируются из метаданных во внутренние системные таблицы. В дальнейшем, если расписание было изменено пользователем или программно в режиме «Предприятие», обновление метаданных в конфигураторе никак не влияет на уже существующие записи в базе. В этой статье мы подробно разберем, как решить проблему «зависших» настроек и программно сбросить расписание к заводским значениям.
Проанализируем техническую сторону вопроса. Многие начинающие разработчики пытаются получить доступ к расписанию через объект метаданных: Метаданные.РегламентныеЗадания.ИмяЗадания.Расписание. Однако на практике это свойство в режиме исполнения часто возвращает Неопределено. Это происходит потому, что в объекте метаданных хранится лишь «шаблон» расписания. Платформа 1С не предоставляет прямого программного метода «Сбросить к значениям по умолчанию» для объекта РегламентноеЗадание. Чтобы обойти это ограничение, нам придется использовать обходные пути.
Рассмотрим изящный трюк, который позволяет вытащить «дефолтное» расписание прямо из конфигурации без использования внешних файлов или эталонных баз. Суть метода заключается в том, что при программном создании нового (не предопределенного) регламентного задания на основании метаданных, оно инициализируется именно теми значениями, которые прописаны в конфигураторе.
Давайте разберем по шагам алгоритм действий:
Посмотрим на пример кода, реализующий этот подход:
// Выбираем конкретное регламентное задание
МетаданныеЗадания = Метаданные.РегламентныеЗадания.ОчисткаНенужныхДанных;
// Создаем временное задание, чтобы считать дефолтное расписание
ВременноеЗадание = РегламентныеЗадания.СоздатьРегламентноеЗадание(МетаданныеЗадания);
ДефолтноеРасписание = ВременноеЗадание.Расписание;
// Находим наше основное предопределенное задание в базе
НайденоеЗадание = РегламентныеЗадания.НайтиПредопределенное(МетаданныеЗадания);
Если НайденоеЗадание <> Неопределено Тогда
// Устанавливаем расписание из шаблона конфигуратора
НайденоеЗадание.Расписание = ДефолтноеРасписание;
НайденоеЗадание.Записать();
// Не забываем удалить временный объект, чтобы он не висел в списке
ВременноеЗадание.Удалить();
Сообщить("Расписание задания " + МетаданныеЗадания.Имя + " успешно сброшено.");
КонецЕсли;
Проанализируем ситуацию, когда у нас имеется множество однотипных информационных баз (например, филиальная сеть или облачный сервис), и нам нужно массово привести расписания к единому стандарту, настроенному в одной «эталонной» базе. В этом случае удобно использовать COM-соединение.
Этот метод позволяет прочитать настройки в одной базе и транслировать их во все остальные. Выясним причину, почему это эффективно: вы можете один раз вручную настроить идеальное расписание в интерфейсе «эталона», а затем распространить его программно, не заботясь о нюансах метаданных.
Рассмотрим пример кода для чтения данных из эталона:
// Устанавливаем соединение с базой-эталоном
COMСоединитель = Новый COMОбъект("V83.COMConnector");
СтрокаПодключения = "Srvr='ServerName';Ref='BaseName';Usr='Admin';Pwd='Pass';";
Соединение = COMСоединитель.Connect(СтрокаПодключения);
// Создаем таблицу для хранения эталонных настроек
СписокЗаданий = Новый ТаблицаЗначений;
СписокЗаданий.Колонки.Добавить("Метаданные");
СписокЗаданий.Колонки.Добавить("Расписание");
СписокЗаданий.Колонки.Добавить("Использование");
// Получаем список регламентных заданий из эталона
РегламентныеЭталон = Соединение.РегламентныеЗадания.ПолучитьРегламентныеЗадания();
Для Каждого ЗаданиеЭталон Из РегламентныеЭталон Цикл
НоваяСтрока = СписокЗаданий.Добавить();
НоваяСтрока.Метаданные = ЗаданиеЭталон.Метаданные.Представление();
НоваяСтрока.Расписание = ЗаданиеЭталон.Расписание;
НоваяСтрока.Использование = ЗаданиеЭталон.Use;
КонецЦикла;
После того как мы получили таблицу настроек, нам необходимо применить их к текущей базе. Важно помнить, что объект РасписаниеРегламентногоЗадания обладает множеством свойств, которые нужно перенести корректно.
// Применяем настройки к текущей ИБ
РегламентныеТекущейИБ = РегламентныеЗадания.ПолучитьРегламентныеЗадания();
Для Каждого ЗаданиеИБ Из РегламентныеТекущейИБ Цикл
ПараметрыЭталона = СписокЗаданий.Найти(ЗаданиеИБ.Метаданные.Представление(), "Метаданные");
Если ПараметрыЭталона <> Неопределено Тогда
// Копируем ключевые свойства расписания
ЗаданиеИБ.Расписание.BeginDate = ПараметрыЭталона.Расписание.BeginDate;
ЗаданиеИБ.Расписание.BeginTime = ПараметрыЭталона.Расписание.BeginTime;
ЗаданиеИБ.Расписание.RepeatPause = ПараметрыЭталона.Расписание.RepeatPause;
ЗаданиеИБ.Расписание.RepeatPeriodInDay = ПараметрыЭталона.Расписание.RepeatPeriodInDay;
ЗаданиеИБ.Расписание.DaysRepeatPeriod = ПараметрыЭталона.Расписание.DaysRepeatPeriod;
// ... аналогично для остальных свойств (EndTime, WeeksPeriod и др.)
ЗаданиеИБ.Использование = ПараметрыЭталона.Использование;
ЗаданиеИБ.Записать();
КонецЕсли;
КонецЦикла;
Если ваша конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), задача может быть решена штатными средствами подсистемы «Регламентные задания». В БСП реализованы механизмы хранения настроек и их обновления при изменении версии конфигурации.
Рассмотрим несколько полезных методов:
РегламентныеЗаданияСлужебный.ПриОбновленииИнформационнойБазы(). Она инициирует проверку соответствия заданий в базе и метаданных.Проанализируем несколько критических моментов, которые стоит учитывать при написании своей обработки для сброса расписаний:
Удалить(). Их можно только выключить (Использование = Ложь) или изменить их параметры.РегламентноеЗадание программный код должен выполняться в привилегированном режиме или от имени пользователя с административными правами.Записать() после изменения свойств объекта Расписание. Без этого вызова изменения останутся только в оперативной памяти вашего сеанса.В заключение отметим, что самым надежным и быстрым способом сброса является создание временного объекта (Способ 1), так как он не требует внешних подключений и всегда гарантирует получение актуальных данных из текущей конфигурации.