При автоматизации типовых конфигураций на обычных формах (в частности, 1С:Управление торговлей 10.3) разработчики часто сталкиваются с ситуацией: внешняя или встроенная обработка отлично работает при ручном запуске пользователем через главное меню Файл - Открыть, но при попытке вызвать ее из регламентного задания система выдает ошибку «Метод объекта не обнаружен» или сообщает, что вызываемая процедура не определена.
Разберем детально, почему возникает такое поведение платформы, как устроено исполнение кода в регламентных заданиях и какими способами можно перевести алгоритм на корректную работу в фоновом режиме.
Проанализируем архитектуру выполнения кода, чтобы понять корень проблемы:
МодульОбычногоПриложения компилируется только на стороне клиента. Если обработка обращается к процедурам, функциям или глобальным переменным этого модуля (например, к переменной глТекущийПользователь), серверное регламентное задание падает с ошибкой, так как этих методов в его пространстве имен просто не существует.
#Если Клиент Тогда
Процедура МояПроцедура() Экспорт
// Код процедуры
КонецПроцедуры
#КонецЕсли
#Если Клиент Тогда, то при создании объекта обработки на сервере через Обработки.МояОбработка.Создать() данный метод вообще не компилируется. Экземпляр объекта успешно создается, но платформа сообщает: «Метод объекта не обнаружен».#Если Клиент Тогда, компилятор на сервере начнет проверять содержимое метода. Если в теле процедуры встречаются команды вроде Предупреждение(), Вопрос(), ОткрытьФорму() или работа с объектами торгового оборудования, регламентное задание завершится аварийно, так как интерфейсные методы на сервере запрещены.ПараметрыСеанса.ТекущийПользователь вызывают исключения.Рассмотрим основной способ исправления кода обработки. Необходимо сделать так, чтобы метод компилировался на сервере, но не вызывал интерактивных окон диалога.
Для этого экспортную процедуру оставляем доступной для сервера, а все интерактивные вызовы изолируем проверками либо переносим информационные сообщения в системный журнал регистрации:
Процедура ВыполнитьДействие() Экспорт
// Выполняем основную серверную логику обработки данных
ВыполнитьОбработкуДанных();
#Если Клиент Тогда
// Интерактивное оповещение пользователя при ручном запуске
ПоказатьОповещениеПользователя("Обработка завершена успешно!");
#Иначе
// При выполнении в регламентном задании пишем событие в журнал
ЗаписьЖурналаРегистрации(
"РегламентнаяОбработка",
УровеньЖурналаРегистрации.Информация,
Метаданные.Обработки.МояОбработка,
,
"Обработка успешно выполнена регламентным заданием."
);
#КонецЕсли
КонецПроцедуры
Если в коде используются функции вроде Вопрос() для подтверждения действий, в фоновом режиме логика должна безусловно принимать утвердительный ответ по умолчанию, минуя диалоговое окно.
Если процедура, которая нужна обработке, находится в МодулеОбычногоПриложения, вызывать ее напрямую из регламентного задания нельзя. Разберем правильный перенос логики:
Экспорт:
// В общем модуле РегламентныеЗаданияСервер:
Процедура ОбщаяЛогика() Экспорт
// Код, приспособленный для работы на стороне сервера
КонецПроцедуры
Теперь эту процедуру можно без ошибок вызывать как из модуля обработки в контексте регламентного задания, так и при интерактивной работе пользователя.
Посмотрим на более чистую архитектуру вызова обработок из фоновых заданий. Если процедуре не требуются реквизиты конкретного экземпляра обработки, перенесем ее в модуль менеджера обработки. Код модуля менеджера компилируется на сервере и доступен без предварительного создания объекта через .Создать():
// В модуле менеджера обработки МояОбработка:
Процедура ВыполнитьАлгоритм() Экспорт
// Серверная логика обработки данных
КонецПроцедуры
// В процедуре регламентного задания общего модуля:
Процедура ВыполнитьЗадание() Экспорт
// Вызываем процедуру напрямую без создания объекта в памяти
Обработки.МояОбработка.ВыполнитьАлгоритм();
КонецПроцедуры
Если серверный алгоритм обращается к типовым механизмам УТ 10.3, которые требуют установленного пользователя, инициализируем параметры сеанса вручную перед запуском алгоритма обработки:
Процедура ВыполнитьЗадание() Экспорт
// Проверяем заполненность текущего пользователя в сеансе
Если Не ЗначениеЗаполнено(ПараметрыСеанса.ТекущийПользователь) Тогда
СлужебныйПользователь = Справочники.Пользователи.НайтиПоНаименованию("РегламентныйПользователь");
ПараметрыСеанса.ТекущийПользователь = СлужебныйПользователь;
КонецЕсли;
// Вызываем целевую обработку
Обр = Обработки.МояОбработка.Создать();
Обр.ВыполнитьДействие();
КонецПроцедуры
В ситуациях, когда обработка слишком тесно интегрирована с клиентскими механизмами и переписывание кода под сервер нецелесообразно по времени, применяют запуск полноценного сеанса толстого клиента через планировщик операционной системы:
"C:\Program Files\1cv8\common\1cestart.exe" ENTERPRISE /S "server-1c\trade" /N "Робот" /P "Пароль" /Execute "C:\Scripts\RunTask.epf"
ПриОткрытии() обязательно прописывают безусловное завершение работы, чтобы процесс не оставался висеть в памяти:
Процедура ПриОткрытии()
Попытка
Обр = Обработки.МояОбработка.Создать();
Обр.ВыполнитьДействие();
Исключение
ЗаписьЖурналаРегистрации("ОшибкаЗадания", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;
// Обязательно закрываем клиентский сеанс
ЗавершитьРаботуСистемы(Ложь);
КонецПроцедуры
При таком подходе сеанс запускается как полноценный толстый клиент: МодульОбычногоПриложения отрабатывает полностью, глобальные переменные инициализируются, а директивы #Если Клиент Тогда остаются активными.