При разработке на платформе 1С программисты часто сталкиваются с ситуацией, когда код, отлично работающий в обычном сеансе пользователя, внезапно падает при выполнении в регламентном или фоновом задании. Разберем типичную проблему: при попытке получить объект документа с помощью метода ПолучитьОбъект() система выдает неочевидную ошибку "Метод объекта не обнаружен".
Проанализируем ситуацию по шагам. У нас есть запрос, который выполняется в регламентном задании (контекст сервера), выбирает ссылки из регистра сведений, и в цикле мы пытаемся получить сам объект для дальнейшей работы. Посмотрим на исходный пример кода:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ASP_ПроведениеТребованиеСклад.СсылкаТребование.Ссылка КАК СсылкаТребование,
| ASP_ПроведениеТребованиеСклад.СсылкаСчетФактура КАК СсылкаСчет
|ИЗ
| РегистрСведений.ASP_ПроведениеТребованиеСклад КАК ASP_ПроведениеТребованиеСклад";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Док = ВыборкаДетальныеЗаписи.СсылкаТребование;
Документ = Док.ПолучитьОБъект(); // На этой строке происходит падение
Реквизит = Документ.УПД;
КонецЦикла;
Фоновые и регламентные задания в 1С всегда выполняются на стороне сервера в отдельном системном сеансе, у которого полностью отсутствует пользовательский интерфейс. Любая попытка вызова интерактивных методов (таких как Предупреждение(), Вопрос(), ОткрытьФорму()) в серверном контексте приводит к сбою.
Но почему ошибка возникает именно на строке с ПолучитьОбъект(), а текст ошибки гласит, что метод не обнаружен? Здесь мы сталкиваемся с механизмом "ленивой компиляции" модулей в 1С.
Платформа 1С не компилирует код модуля объекта (документа или справочника) при старте системы. Компиляция и инициализация происходят ровно в тот момент, когда мы впервые обращаемся к объекту в коде — то есть при вызове метода ПолучитьОбъект().
Если в модуле объекта присутствуют клиентские вызовы (например, показ предупреждения при проведении или проверке заполнения), платформа пытается скомпилировать этот код под текущий серверный контекст фонового задания. Натыкаясь на недоступную серверу функцию Предупреждение(), процесс компиляции прерывается, класс объекта не может инициализироваться, и система сообщает, что метод недоступен.
Разберем, как правильно скрыть интерактивный код от сервера. Согласно стандартам разработки 1С, модуль объекта вообще не должен содержать интерактивного кода — все вызовы диалогов должны находиться в модуле формы. Однако в старых конфигурациях на обычных формах это правило часто нарушалось.
Чтобы код успешно скомпилировался на сервере, обрамим все интерактивные методы инструкциями препроцессора. Рассмотрим пример решения:
#Если Клиент Тогда
// Этот блок будет скомпилирован только в клиентском сеансе
Предупреждение("Документ не может быть проведен!");
#КонецЕсли
Если вы работаете в управляемом приложении, используется аналогичная директива #Если НаКлиенте Тогда. После добавления таких условий модуль объекта успешно пройдет ленивую компиляцию в фоновом задании, и метод ПолучитьОбъект() отработает без ошибок.
Посмотрим на исходную задачу внимательнее. Если цель кода — просто прочитать значение реквизита документа (в нашем случае реквизит УПД), то вызов метода ПолучитьОбъект() является избыточным и сильно замедляет работу системы. Мы инициализируем тяжелый объект документа только ради чтения одного свойства.
Гораздо эффективнее и безопаснее получать все необходимые данные непосредственно в запросе. Изменим наш запрос, чтобы он сразу возвращал нужный реквизит:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ASP_ПроведениеТребованиеСклад.СсылкаТребование.Ссылка КАК СсылкаТребование,
| ASP_ПроведениеТребованиеСклад.СсылкаСчетФактура КАК СсылкаСчет,
| ASP_ПроведениеТребованиеСклад.СсылкаТребование.УПД КАК УПД // Получаем реквизит сразу
|ИЗ
| РегистрСведений.ASP_ПроведениеТребованиеСклад КАК ASP_ПроведениеТребованиеСклад";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Теперь нам не нужно получать объект, мы берем готовое значение
Реквизит = ВыборкаДетальныеЗаписи.УПД;
// ... дальнейшая логика
КонецЦикла;
Такой подход полностью исключает проблему с инициализацией модуля объекта и работает в разы быстрее.
Чтобы не дожидаться падения регламентных заданий, подобные ошибки архитектуры нужно выявлять на этапе разработки. Рассмотрим, как это сделать с помощью штатных инструментов конфигуратора:
После завершения анализа система выдаст подробный список всех объектов и процедур, в которых используются методы, несовместимые с выбранными серверными контекстами. Вам останется только пройтись по списку и обернуть спорные участки кода в директивы препроцессора или вынести их в модули форм.
Следуя этим правилам разделения контекста и оптимизируя запросы к базе данных, вы обеспечите стабильную работу фоновых и регламентных заданий вашей информационной базы 1С.