Как выполнить регламентное задание кодом: из формы, на сервере и через COM
Содержание
Частое заблуждение — искать у регламентного задания метод «Выполнить». Его нет. Чтобы запустить регламентное задание 1С 8.3 программно, код на сервере находит задание через менеджер РегламентныеЗадания, берёт имя процедуры из метаданных (ИмяМетода) и передаёт его в ФоновыеЗадания.Выполнить() вместе с параметрами задания.
Дальше всё зависит от того, откуда вы запускаете: из кнопки на форме, из серверного кода или из внешнего скрипта через COM-соединение. Ниже — рабочий код для каждого случая, разница в поведении файловой и клиент-серверной базы и то, как ловить ошибки.
Из чего состоит регламентное задание в метаданных и в базе
У регламентного задания две «половины». Их путают чаще всего.
- 📐 Объект метаданных —
Метаданные.РегламентныеЗадания.ИмяЗадания. Хранит неизменяемое:ИмяМетода(строка видаОбщийМодуль.Процедура), признакПредопределенное, синоним. - 🗄 Объект в базе —
РегламентноеЗадание, который возвращает менеджерРегламентныеЗадания. Хранит то, что настраивают в режиме Предприятия:Использование,Расписание,Параметры(массив),ИмяПользователя,Ключ,Наименование, число повторов при аварийном завершении.
Процедура, указанная в ИмяМетода, должна быть экспортной и лежать в общем модуле с флагом «Сервер». Это и есть то, что реально выполняется.
Как найти задание кодом
| Ситуация | Метод менеджера |
|---|---|
| Задание предопределённое (одно на базу) | РегламентныеЗадания.НайтиПредопределенное(Метаданные.РегламентныеЗадания.Имя) |
| Неопределённое, экземпляров может быть несколько | РегламентныеЗадания.ПолучитьРегламентныеЗадания(Отбор) |
| Известен уникальный идентификатор | РегламентныеЗадания.НайтиПоУникальномуИдентификатору(Ид) |
В отборе ПолучитьРегламентныеЗадания можно указать Метаданные, Ключ, Использование, Наименование, Предопределенное.
Менеджер РегламентныеЗадания на клиенте недоступен. Кроме того, для работы с ним нужно право «Администрирование». В типовых конфигурациях код оборачивают в УстановитьПривилегированныйРежим(Истина), но перед этим сами проверяют права вызывающего.
Серверная функция для запуска регламентного задания
Удобно держать запуск в одном месте. Создайте общий модуль ЗапускРегламентныхЗаданий с флагами «Сервер» и «Внешнее соединение» (второй понадобится для COM). «Вызов сервера» не включайте: функция работает в привилегированном режиме, и прямой доступ с клиента ей ни к чему.
// Возвращает уникальный идентификатор запущенного фонового задания.
Функция ЗапуститьРегламентноеЗадание(ИмяЗадания, Параметры = Неопределено) Экспорт
Если Не ПравоДоступа("Администрирование", Метаданные) Тогда
ВызватьИсключение "Недостаточно прав для запуска регламентных заданий";
КонецЕсли;
МетаданныеЗадания = Метаданные.РегламентныеЗадания.Найти(ИмяЗадания);
Если МетаданныеЗадания = Неопределено Тогда
ВызватьИсключение СтрШаблон("В конфигурации нет регламентного задания %1", ИмяЗадания);
КонецЕсли;
УстановитьПривилегированныйРежим(Истина);
Если МетаданныеЗадания.Предопределенное Тогда
Задание = РегламентныеЗадания.НайтиПредопределенное(МетаданныеЗадания);
Иначе
Отбор = Новый Структура("Метаданные", МетаданныеЗадания);
Найденные = РегламентныеЗадания.ПолучитьРегламентныеЗадания(Отбор);
Задание = ?(Найденные.Количество() = 0, Неопределено, Найденные[0]);
КонецЕсли;
Если Задание = Неопределено Тогда
ВызватьИсключение СтрШаблон("Задание %1 не создано в базе", ИмяЗадания);
КонецЕсли;
// Не запускаем второй экземпляр, если первый ещё работает
ОтборАктивных = Новый Структура("ИмяМетода, Состояние",
МетаданныеЗадания.ИмяМетода, СостояниеФоновогоЗадания.Активно);
Если ФоновыеЗадания.ПолучитьФоновыеЗадания(ОтборАктивных).Количество() > 0 Тогда
ВызватьИсключение СтрШаблон("Задание %1 уже выполняется", ИмяЗадания);
КонецЕсли;
ПараметрыЗапуска = ?(Параметры = Неопределено, Задание.Параметры, Параметры);
Ключ = Строка(Задание.УникальныйИдентификатор);
Наименование = "Ручной запуск: " + МетаданныеЗадания.Синоним;
ФоновоеЗадание = ФоновыеЗадания.Выполнить(МетаданныеЗадания.ИмяМетода,
ПараметрыЗапуска, Ключ, Наименование);
Возврат ФоновоеЗадание.УникальныйИдентификатор;
КонецФункции
Что здесь важно:
- Ключ. Платформа не даст запустить второе активное фоновое задание с тем же методом и ключом — выбросит исключение. Ключ из идентификатора задания защищает от двойного нажатия.
- Параметры. Берутся из самого задания, но их можно подменить: передайте свой массив. Значения должны быть сериализуемыми — строки, числа, даты, ссылки, структуры из таких же типов.
- Синоним в наименовании нужен, чтобы ручной запуск было легко найти в списке фоновых заданий.
Если нужен синхронный запуск в текущем сеансе, вызовите процедуру напрямую: ЗагрузкаКурсовВалют.Выполнить(). В типовых конфигурациях на БСП для вызова по строке есть ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяМетода, Параметры) — он проверяет имя метода, в отличие от голого Выполнить().
Запуск регламентного задания из формы: клиент → сервер
Сценарий: на форме обработки кнопка «Выполнить сейчас». Клиент вызывает сервер, получает идентификатор фонового задания и раз в пару секунд опрашивает его состояние. Форма при этом не зависает.
Добавьте реквизит формы ИдентификаторЗадания с типом «Уникальный идентификатор» и команду ВыполнитьСейчас.
&НаКлиенте
Процедура ВыполнитьСейчас(Команда)
ИдентификаторЗадания = ЗапуститьНаСервере("ЗагрузкаКурсовВалют");
ПодключитьОбработчикОжидания("ПроверитьЗадание", 2, Ложь);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЗапуститьНаСервере(ИмяЗадания)
Возврат ЗапускРегламентныхЗаданий.ЗапуститьРегламентноеЗадание(ИмяЗадания);
КонецФункции
&НаСервереБезКонтекста
Функция СостояниеЗадания(Идентификатор)
УстановитьПривилегированныйРежим(Истина);
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
Если Задание = Неопределено Тогда
Возврат "Задание не найдено";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
Возврат "";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
Возврат "Выполнено";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно
И Задание.ИнформацияОбОшибке <> Неопределено Тогда
Возврат "Ошибка: " + КраткоеПредставлениеОшибки(Задание.ИнформацияОбОшибке);
Иначе
Возврат "Задание отменено или завершилось аварийно";
КонецЕсли;
КонецФункции
&НаКлиенте
Процедура ПроверитьЗадание()
Результат = СостояниеЗадания(ИдентификаторЗадания);
Если ПустаяСтрока(Результат) Тогда
Возврат; // ещё работает
КонецЕсли;
ОтключитьОбработчикОжидания("ПроверитьЗадание");
ПоказатьПредупреждение(, Результат);
КонецПроцедуры
Код одинаково работает в тонком и веб-клиенте. Вызов ЗапуститьНаСервере упадёт с исключением, если у пользователя нет прав или задание уже идёт, — текст исключения пользователь увидит сам.
Не ждите завершения задания внутри серверного вызова из формы. Пока сервер ждёт, клиент висит, а при долгом задании сеанс может упереться в таймауты. Опрос через обработчик ожидания — правильный путь.
Пользователям без программирования то же самое доступно в типовых конфигурациях на БСП: Администрирование → Обслуживание → Регламентные операции → Регламентные и фоновые задания, кнопка «Выполнить сейчас». Если хотите повторить логику типовой, найдите в общих модулях поиском ВыполнитьРегламентноеЗаданиеВручную — так называется служебная функция БСП.
Фоновое выполнение на сервере: файловая база, ожидание, отмена
Где выполняется задание
- 🖥 Клиент-серверная база . Фоновое задание выполняет рабочий процесс кластера. Оно живёт независимо от сеанса, который его запустил: можно закрыть форму, задание продолжит работу.
- 📁 Файловая база. Менеджера кластера нет. Фоновое задание выполняется в процессе того клиентского приложения, которое его запустило, и обрывается вместе с ним. Закрыли 1С — задание прервалось.
По расписанию в файловой базе регламентные задания выполняет один из клиентских сеансов. Если никто не открыл базу, по расписанию ничего не запустится. Для выделенного «робота» запускают клиент с параметром /AllowExecuteScheduledJobs -Force.
Ожидание результата в серверном коде
Иногда ждать нужно — например, в обработке, которую вызывают по COM. Для этого у фонового задания есть метод ожидания с таймаутом в секундах. В актуальных версиях платформы это ОжидатьЗавершенияВыполнения(), он возвращает обновлённый объект задания; старый ОжидатьЗавершения() помечен как устаревший.
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Ид);
Задание = Задание.ОжидатьЗавершенияВыполнения(600);
Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
// таймаут вышел, задание продолжает работать
КонецЕсли;
Сообщения и отмена
- 💬 Всё, что процедура задания выводит через
Новый СообщениеПользователюиСообщить(), накапливается в задании. Забрать это можноЗадание.ПолучитьСообщенияПользователю(Истина)— параметр удаляет уже полученные сообщения, чтобы не читать их дважды. - ⛔
Задание.Отменить()останавливает задание не мгновенно: платформа прерывает его при ближайшем обращении к серверу. Транзакция, открытая на момент отмены, откатится.
Запуск через смену расписания
Есть и «мягкий» способ: не создавать фоновое задание руками, а поправить расписание, чтобы планировщик сам запустил задание. Плюс — сработают ИмяПользователя и повторы при аварийном завершении из настроек задания.
Расписание = Новый РасписаниеРегламентногоЗадания;
Расписание.ПериодПовтораДней = 1;
Расписание.ПериодПовтораВТечениеДня = 600; // каждые 10 минут
Задание.Расписание = Расписание;
Задание.Использование = Истина;
Задание.Записать();
Минус — запуск не мгновенный и не под вашим контролем: планировщик сам решит, когда стартовать.
Запуск регламентных заданий из внешнего скрипта через COM
Задача из практики: обмен должен стартовать после выгрузки файлов из другой системы, а не по часам. Внешний скрипт подключается к базе через COM-соединение и вызывает серверную функцию.
Подготовка
- Зарегистрируйте COM-компоненту от имени администратора Windows:
regsvr32 "C:\Program Files\1cv8\<версия>\bin\comcntr.dll". Разрядность платформы должна совпадать с разрядностью программы, которая создаёт объект: 64-битный PowerShell — 64-битная платформа. - Заведите в базе отдельного пользователя-«робота» с правом «Администрирование» и паролем. Учётку обычного сотрудника не используйте.
- Добавьте в модуль
ЗапускРегламентныхЗаданийфункцию, которая запускает задание и ждёт результата. Флаг «Внешнее соединение» у модуля уже стоит.
// Возвращает "OK", "TIMEOUT" или текст ошибки.
Функция ВыполнитьИДождаться(ИмяЗадания, ТаймаутСекунд = 600) Экспорт
Ид = ЗапуститьРегламентноеЗадание(ИмяЗадания);
УстановитьПривилегированныйРежим(Истина);
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Ид);
Задание = Задание.ОжидатьЗавершенияВыполнения(ТаймаутСекунд);
Если Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
Возврат "OK";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
Возврат "TIMEOUT";
ИначеЕсли Задание.ИнформацияОбОшибке <> Неопределено Тогда
Возврат ПодробноеПредставлениеОшибки(Задание.ИнформацияОбОшибке);
Иначе
Возврат "Задание отменено";
КонецЕсли;
КонецФункции
Скрипт PowerShell
$connector = New-Object -ComObject V83.COMConnector
$base = $connector.Connect('Srvr="srv-1c";Ref="buh";Usr="robot";Pwd="пароль"')
$result = $base.ЗапускРегламентныхЗаданий.ВыполнитьИДождаться("ЗагрузкаКурсовВалют", 600)
Write-Output $result
$base = $null
[System.GC]::Collect()
if ($result -ne "OK") { exit 1 }
Код возврата 1 планировщик Windows и системы мониторинга воспринимают как сбой. Для файловой базы строка подключения другая: File="D:\Bases\buh";Usr="robot";Pwd="пароль".
Сохраняйте скрипт в UTF-8 с BOM, иначе Windows PowerShell 5.1 исказит русское имя модуля. Пароль в открытом виде в скрипте — плохая идея: храните его в защищённом хранилище учётных данных Windows и читайте при запуске.
Соединение закрывается, когда освобождается COM-объект. Поэтому в конце скрипта ссылку обнуляют и вызывают сборку мусора, иначе сеанс может повисеть в списке сеансов базы.
Ошибки при запуске и журнал регистрации
Платформа сама пишет в журнал регистрации события фоновых заданий: запуск, успешное завершение, ошибку, отмену. Открыть журнал можно в Конфигураторе: Администрирование → Журнал регистрации, — и отфильтровать по событиям группы «Фоновое задание».
Собственный след надёжнее. Добавьте записи в саму процедуру задания:
Процедура ЗагрузкаКурсовВалют() Экспорт
ИмяСобытия = "РегламентныеЗадания.ЗагрузкаКурсовВалют";
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Информация, , , "Старт");
Попытка
// основная работа задания
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(Инфо));
ВызватьИсключение;
КонецПопытки;
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Информация, , , "Готово");
КонецПроцедуры
Не глотайте исключение молча. Повторный ВызватьИсключение в блоке Исключение нужен, чтобы задание получило состояние «Завершено аварийно». Иначе фоновое задание отчитается об успехе, а повторы при аварийном завершении не сработают.
В типовых конфигурациях первая строка процедуры регламентного задания — вызов ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(...). Он срабатывает и при ручном запуске. Если база — копия рабочей (её перенесли или развернули из выгрузки), БСП блокирует работу с внешними ресурсами, и задания, которые ходят в интернет или в обмены, не выполнятся, пока администратор не подтвердит, что это рабочая база.
ИмяМетода в свойствах задания: модуль должен быть общим, с флагом «Сервер», а процедура — экспортной. Опечатка в имени модуля даёт ту же ошибку.ФоновыеЗадания.ПолучитьФоновыеЗадания() с отбором по ключу и дождитесь или отмените.ИнформацияОбОшибке у фонового задания. Частая причина — несериализуемый тип в массиве параметров.Какой способ запуска выбрать
| Способ | Где выполняется | Ждёт результата | Когда подходит | Риски |
|---|---|---|---|---|
| Кнопка «Выполнить сейчас» в БСП | Фоновое задание на сервере | Нет | Разовый запуск администратором | Почти нет |
ФоновыеЗадания.Выполнить() из формы | Фоновое задание на сервере | Опрос состояния | Своя обработка, кнопка для пользователя | Двойной запуск без ключа |
| Прямой вызов процедуры | Текущий сеанс | Да, синхронно | Отладка, короткие задания | Висит клиент, права текущего пользователя |
| Смена расписания | Планировщик | Нет | Перенести или участить запуск | Время старта не контролируется |
| COM-соединение | Фоновое задание на сервере | Да, с таймаутом | Запуск по событию извне, интеграции | Пароль робота в скрипте, разрядность COM |
Для отладки удобнее прямой вызов: точка останова в процедуре задания срабатывает в обычном сеансе отладки. Фоновое задание отлаживается отдельно, через подключение к предметам отладки «Фоновое задание», и это неудобно для быстрой проверки.
Частые вопросы
Почему после ручного запуска не обновилось свойство ПоследнееЗадание?
ФоновыеЗадания.Выполнить(), не связано с регламентным: его свойство РегламентноеЗадание пустое. ПоследнееЗадание показывает только запуски планировщика. Ищите ручные запуски по ключу или наименованию.Под каким пользователем выполнится задание при запуске кодом?
ФоновыеЗадания.Выполнить(), а не под тем, кто указан в поле ИмяПользователя регламентного задания. Если права различаются, результат тоже может отличаться.Можно ли запустить задание с другими параметрами, не меняя его настройки?
ФоновыеЗадания.Выполнить() свой массив. Сохранённые Параметры регламентного задания при этом не изменятся.Как заставить регламентные задания работать по расписанию в файловой базе?
/AllowExecuteScheduledJobs -Force, а для сеансов пользователей выполнение можно отключить через -Off.