Как выполнить регламентное задание кодом: из формы, на сервере и через COM

· 9 мин

Содержание

Частое заблуждение — искать у регламентного задания метод «Выполнить». Его нет. Чтобы запустить регламентное задание 1С 8.3 программно, код на сервере находит задание через менеджер РегламентныеЗадания, берёт имя процедуры из метаданных (ИмяМетода) и передаёт его в ФоновыеЗадания.Выполнить() вместе с параметрами задания.

Дальше всё зависит от того, откуда вы запускаете: из кнопки на форме, из серверного кода или из внешнего скрипта через COM-соединение. Ниже — рабочий код для каждого случая, разница в поведении файловой и клиент-серверной базы и то, как ловить ошибки.

Из чего состоит регламентное задание в метаданных и в базе

У регламентного задания две «половины». Их путают чаще всего.

  • 📐 Объект метаданных — Метаданные.РегламентныеЗадания.ИмяЗадания. Хранит неизменяемое: ИмяМетода (строка вида ОбщийМодуль.Процедура), признак Предопределенное, синоним.
  • 🗄 Объект в базе — РегламентноеЗадание, который возвращает менеджер РегламентныеЗадания. Хранит то, что настраивают в режиме Предприятия: Использование, Расписание, Параметры (массив), ИмяПользователя, Ключ, Наименование, число повторов при аварийном завершении.

Процедура, указанная в ИмяМетода, должна быть экспортной и лежать в общем модуле с флагом «Сервер». Это и есть то, что реально выполняется.

Как найти задание кодом

СитуацияМетод менеджера
Задание предопределённое (одно на базу)РегламентныеЗадания.НайтиПредопределенное(Метаданные.РегламентныеЗадания.Имя)
Неопределённое, экземпляров может быть несколькоРегламентныеЗадания.ПолучитьРегламентныеЗадания(Отбор)
Известен уникальный идентификаторРегламентныеЗадания.НайтиПоУникальномуИдентификатору(Ид)

В отборе ПолучитьРегламентныеЗадания можно указать Метаданные, Ключ, Использование, Наименование, Предопределенное.

Менеджер РегламентныеЗадания на клиенте недоступен. Кроме того, для работы с ним нужно право «Администрирование». В типовых конфигурациях код оборачивают в УстановитьПривилегированныйРежим(Истина), но перед этим сами проверяют права вызывающего.

Серверная функция для запуска регламентного задания

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

// Возвращает уникальный идентификатор запущенного фонового задания.
Функция ЗапуститьРегламентноеЗадание(ИмяЗадания, Параметры = Неопределено) Экспорт

	Если Не ПравоДоступа("Администрирование", Метаданные) Тогда
		ВызватьИсключение "Недостаточно прав для запуска регламентных заданий";
	КонецЕсли;

	МетаданныеЗадания = Метаданные.РегламентныеЗадания.Найти(ИмяЗадания);
	Если МетаданныеЗадания = Неопределено Тогда
		ВызватьИсключение СтрШаблон("В конфигурации нет регламентного задания %1", ИмяЗадания);
	КонецЕсли;

	УстановитьПривилегированныйРежим(Истина);

	Если МетаданныеЗадания.Предопределенное Тогда
		Задание = РегламентныеЗадания.НайтиПредопределенное(МетаданныеЗадания);
	Иначе
		Отбор = Новый Структура("Метаданные", МетаданныеЗадания);
		Найденные = РегламентныеЗадания.ПолучитьРегламентныеЗадания(Отбор);
		Задание = ?(Найденные.Количество() = 0, Неопределено, Найденные[0]);
	КонецЕсли;

	Если Задание = Неопределено Тогда
		ВызватьИсключение СтрШаблон("Задание %1 не создано в базе", ИмяЗадания);
	КонецЕсли;

	// Не запускаем второй экземпляр, если первый ещё работает
	ОтборАктивных = Новый Структура("ИмяМетода, Состояние",
		МетаданныеЗадания.ИмяМетода, СостояниеФоновогоЗадания.Активно);
	Если ФоновыеЗадания.ПолучитьФоновыеЗадания(ОтборАктивных).Количество() > 0 Тогда
		ВызватьИсключение СтрШаблон("Задание %1 уже выполняется", ИмяЗадания);
	КонецЕсли;

	ПараметрыЗапуска = ?(Параметры = Неопределено, Задание.Параметры, Параметры);
	Ключ = Строка(Задание.УникальныйИдентификатор);
	Наименование = "Ручной запуск: " + МетаданныеЗадания.Синоним;

	ФоновоеЗадание = ФоновыеЗадания.Выполнить(МетаданныеЗадания.ИмяМетода,
		ПараметрыЗапуска, Ключ, Наименование);

	Возврат ФоновоеЗадание.УникальныйИдентификатор;

КонецФункции

Что здесь важно:

  1. Ключ. Платформа не даст запустить второе активное фоновое задание с тем же методом и ключом — выбросит исключение. Ключ из идентификатора задания защищает от двойного нажатия.
  2. Параметры. Берутся из самого задания, но их можно подменить: передайте свой массив. Значения должны быть сериализуемыми — строки, числа, даты, ссылки, структуры из таких же типов.
  3. Синоним в наименовании нужен, чтобы ручной запуск было легко найти в списке фоновых заданий.

Если нужен синхронный запуск в текущем сеансе, вызовите процедуру напрямую: ЗагрузкаКурсовВалют.Выполнить(). В типовых конфигурациях на БСП для вызова по строке есть ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяМетода, Параметры) — он проверяет имя метода, в отличие от голого Выполнить().

Запуск регламентного задания из формы: клиент → сервер

Сценарий: на форме обработки кнопка «Выполнить сейчас». Клиент вызывает сервер, получает идентификатор фонового задания и раз в пару секунд опрашивает его состояние. Форма при этом не зависает.

Добавьте реквизит формы ИдентификаторЗадания с типом «Уникальный идентификатор» и команду ВыполнитьСейчас.

&НаКлиенте
Процедура ВыполнитьСейчас(Команда)
	ИдентификаторЗадания = ЗапуститьНаСервере("ЗагрузкаКурсовВалют");
	ПодключитьОбработчикОжидания("ПроверитьЗадание", 2, Ложь);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЗапуститьНаСервере(ИмяЗадания)
	Возврат ЗапускРегламентныхЗаданий.ЗапуститьРегламентноеЗадание(ИмяЗадания);
КонецФункции

&НаСервереБезКонтекста
Функция СостояниеЗадания(Идентификатор)
	УстановитьПривилегированныйРежим(Истина);
	Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
	Если Задание = Неопределено Тогда
		Возврат "Задание не найдено";
	ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
		Возврат "";
	ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
		Возврат "Выполнено";
	ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно
		И Задание.ИнформацияОбОшибке <> Неопределено Тогда
		Возврат "Ошибка: " + КраткоеПредставлениеОшибки(Задание.ИнформацияОбОшибке);
	Иначе
		Возврат "Задание отменено или завершилось аварийно";
	КонецЕсли;
КонецФункции

&НаКлиенте
Процедура ПроверитьЗадание()
	Результат = СостояниеЗадания(ИдентификаторЗадания);
	Если ПустаяСтрока(Результат) Тогда
		Возврат; // ещё работает
	КонецЕсли;
	ОтключитьОбработчикОжидания("ПроверитьЗадание");
	ПоказатьПредупреждение(, Результат);
КонецПроцедуры

Код одинаково работает в тонком и веб-клиенте. Вызов ЗапуститьНаСервере упадёт с исключением, если у пользователя нет прав или задание уже идёт, — текст исключения пользователь увидит сам.

Не ждите завершения задания внутри серверного вызова из формы. Пока сервер ждёт, клиент висит, а при долгом задании сеанс может упереться в таймауты. Опрос через обработчик ожидания — правильный путь.

Пользователям без программирования то же самое доступно в типовых конфигурациях на БСП: Администрирование → Обслуживание → Регламентные операции → Регламентные и фоновые задания, кнопка «Выполнить сейчас». Если хотите повторить логику типовой, найдите в общих модулях поиском ВыполнитьРегламентноеЗаданиеВручную — так называется служебная функция БСП.

Фоновое выполнение на сервере: файловая база, ожидание, отмена

Где выполняется задание

  • 🖥 Клиент-серверная база . Фоновое задание выполняет рабочий процесс кластера. Оно живёт независимо от сеанса, который его запустил: можно закрыть форму, задание продолжит работу.
  • 📁 Файловая база. Менеджера кластера нет. Фоновое задание выполняется в процессе того клиентского приложения, которое его запустило, и обрывается вместе с ним. Закрыли 1С — задание прервалось.

По расписанию в файловой базе регламентные задания выполняет один из клиентских сеансов. Если никто не открыл базу, по расписанию ничего не запустится. Для выделенного «робота» запускают клиент с параметром /AllowExecuteScheduledJobs -Force.

Ожидание результата в серверном коде

Иногда ждать нужно — например, в обработке, которую вызывают по COM. Для этого у фонового задания есть метод ожидания с таймаутом в секундах. В актуальных версиях платформы это ОжидатьЗавершенияВыполнения(), он возвращает обновлённый объект задания; старый ОжидатьЗавершения() помечен как устаревший.

Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Ид);
Задание = Задание.ОжидатьЗавершенияВыполнения(600);
Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
	// таймаут вышел, задание продолжает работать
КонецЕсли;

Сообщения и отмена

  • 💬 Всё, что процедура задания выводит через Новый СообщениеПользователю и Сообщить(), накапливается в задании. Забрать это можно Задание.ПолучитьСообщенияПользователю(Истина) — параметр удаляет уже полученные сообщения, чтобы не читать их дважды.
  • ⛔ Задание.Отменить() останавливает задание не мгновенно: платформа прерывает его при ближайшем обращении к серверу. Транзакция, открытая на момент отмены, откатится.

Запуск через смену расписания

Есть и «мягкий» способ: не создавать фоновое задание руками, а поправить расписание, чтобы планировщик сам запустил задание. Плюс — сработают ИмяПользователя и повторы при аварийном завершении из настроек задания.

Расписание = Новый РасписаниеРегламентногоЗадания;
Расписание.ПериодПовтораДней = 1;
Расписание.ПериодПовтораВТечениеДня = 600; // каждые 10 минут
Задание.Расписание = Расписание;
Задание.Использование = Истина;
Задание.Записать();

Минус — запуск не мгновенный и не под вашим контролем: планировщик сам решит, когда стартовать.

Запуск регламентных заданий из внешнего скрипта через COM

Задача из практики: обмен должен стартовать после выгрузки файлов из другой системы, а не по часам. Внешний скрипт подключается к базе через COM-соединение и вызывает серверную функцию.

Подготовка

  1. Зарегистрируйте COM-компоненту от имени администратора Windows: regsvr32 "C:\Program Files\1cv8\<версия>\bin\comcntr.dll". Разрядность платформы должна совпадать с разрядностью программы, которая создаёт объект: 64-битный PowerShell — 64-битная платформа.
  2. Заведите в базе отдельного пользователя-«робота» с правом «Администрирование» и паролем. Учётку обычного сотрудника не используйте.
  3. Добавьте в модуль ЗапускРегламентныхЗаданий функцию, которая запускает задание и ждёт результата. Флаг «Внешнее соединение» у модуля уже стоит.
// Возвращает "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.
Можно ли запустить регламентное задание из веб-клиента?
Да. Код запуска выполняется на сервере, а опрос состояния через обработчик ожидания работает в веб-клиенте так же, как в тонком.