При разработке и сопровождении конфигураций 1С разработчики нередко сталкиваются с ситуацией: в коде создается объект прикладного типа (например, документ или справочник), после чего происходит обращение к его экспортной процедуре, объявленной в модуле объекта. В интерактивном режиме под толстым клиентом или внешней обработкой код выполняется без замечаний, однако при запуске той же логики через регламентное задание платформа генерирует исключение «Метод объекта не обнаружен».
Разберем детально, почему возникает подобное поведение платформы, как локализовать причину и правильно адаптировать код модулей объектов для работы в фоновых заданиях.
Рассмотрим типовой сценарий, в котором возникает ошибка. В общем модуле описана процедура, вызываемая регламентным заданием:
Процедура ВыполнитьРегламентнуюОтправку() Экспорт
ЭлПисьмо = Документы.ЭлектронноеПисьмо.СоздатьДокумент();
// Заполнение реквизитов документа
ЭлПисьмо.ОтправитьПисьмо();
КонецПроцедуры
Если запустить эту процедуру вручную из внешней обработки в клиентском сеансе, метод ОтправитьПисьмо() успешно отрабатывает. Но при запуске регламентного задания сервер 1С выдает ошибку отсутствия метода. Выясним причину такой разницы в поведении.
Регламентные и фоновые задания всегда исполняются исключительно на стороне сервера в неинтерактивном сеансе. При компиляции модуля объекта платформой анализируются инструкции препроцессора. Если в модуле объекта процедура объявлена следующим образом:
#Если Клиент Тогда
Процедура ОтправитьПисьмо(ФормаПисьма = Неопределено) Экспорт
// Исполняемый код отправки
КонецПроцедуры
#КонецЕсли
В этом случае при компиляции на сервере весь блок кода внутри #Если Клиент Тогда просто отсекается. Для серверного контекста фонового задания такой процедуры в составе объекта не существует, что и приводит к ошибке времени выполнения «Метод объекта не обнаружен».
Разберем по шагам, как выявить и исправить проблему в конфигурации:
#Если, #ИначеЕсли, #Иначе и #КонецЕсли. Особое внимание следует обратить на блоки #Если Клиент Тогда, #Если ТолстыйКлиентОбычноеПриложение Тогда и незакрытые директивы препроцессора в предшествующих процедурах модуля.Сервер, Внешнее соединение и Фоновое задание.В зависимости от архитектуры конфигурации и выполняемой задачи применим один из следующих подходов.
Если алгоритм процедуры не содержит интерактивных вызовов (таких как предупреждения, вопросы пользователю или открытие экранных форм), процедуру необходимо сделать доступной на сервере, удалив обрамляющую директиву #Если Клиент Тогда или заменив её условием:
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
Процедура ОтправитьПисьмо() Экспорт
// Бизнес-логика, не зависящая от интерфейса
СтатусПисьма = Перечисления.СтатусыПисем.Отправленное;
ЭтотОбъект.Записать();
КонецПроцедуры
#КонецЕсли
Важно: в модулях объектов и менеджеров директивы компиляции вида &НаСервере или &НаКлиенте не используются — они предназначены исключительно для модулей управляемых форм и команд.
Если исходная процедура взаимодействовала с формой (например, принимала параметр ФормаПисьма), перенесем чистую бизнес-логику в отдельную процедуру без обращения к элементам интерфейса:
// Процедура доступна на клиенте и сервере
Процедура ВыполнитьОтправкуНаСервере() Экспорт
// Формирование и отправка почтового сообщения через ИнтернетПочта
ПочтовыйПрофиль = Новый ИнтернетПочтовыйПрофиль;
// Заполнение параметров профиля...
Почта = Новый ИнтернетПочта;
Почта.Подключиться(ПочтовыйПрофиль);
// Отправка...
Почта.Отключиться();
КонецПроцедуры
#Если Клиент Тогда
Процедура ОтправитьПисьмоИнтерактивно(ФормаПисьма = Неопределено) Экспорт
ВыполнитьОтправкуНаСервере();
Если ФормаПисьма <> Неопределено Тогда
ФормаПисьма.Обновить();
КонецЕсли;
КонецПроцедуры
#КонецЕсли
Если для выполнения действия не требуется инициализация и блокировка всего экземпляра документа, логику целесообразно выносить в модуль менеджера документа или серверный общий модуль с установленными флагами Сервер и Вызов сервера:
// В общем модуле или модуле менеджера
Процедура СформироватьИОтправить(СсылкаНаДокумент) Экспорт
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
ДокументОбъект.Заполнить();
ДокументОбъект.Записать();
КонецПроцедуры
Такой подход обеспечивает универсальность кода, снижает накладные расходы сервера и предотвращает ошибки видимости методов в различных контекстах исполнения.