В процессе разработки или поддержки механизмов обмена данными в 1С:Предприятие 8 часто встречается ситуация, когда код, успешно работающий в обычной обработке, начинает выдавать ошибку «Ошибка при вызове контекста (ПолучитьОбъект)» при выполнении на сервере или в фоновом задании. Особенность этой проблемы в том, что она часто не позволяет использовать отладчик напрямую, а текст ошибки кажется малоинформативным. Разберем детально, почему это происходит и как найти «виновника» проблемы.
Когда мы вызываем метод ПолучитьОбъект() у ссылки, платформа выполняет несколько критически важных действий, каждое из которых может стать точкой отказа:
ПередЗаписью, ПриУстановкеНовогоКода), что также требует компиляции соответствующих общих модулей.Если на любом из этих этапов возникает ошибка, метод ПолучитьОбъект() завершается аварийно.
Разберем наиболее частую ситуацию: код выполняется в фоновом задании на сервере. Если в модуле объекта справочника (например, ФизическиеЛица) прописаны вызовы, предназначенные только для клиента, серверная компиляция упадет. Рассмотрим пример опасного кода в модуле объекта:
// В модуле объекта справочника
Процедура ПередЗаписью(Отказ)
Если ЭтоНовый() Тогда
// ОШИБКА: Вопрос() и Предупреждение() нельзя вызывать на сервере!
Ответ = Вопрос("Подтвердите создание?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Нет Тогда
Отказ = Истина;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Выясним причину: при вызове ПолучитьОбъект() на сервере платформа пытается скомпилировать этот модуль. Встретив Вопрос(), она выдает ошибку компиляции, так как эта функция недоступна в серверном контексте. Решение: всегда оборачивайте клиентский код в инструкции препроцессора или выносите его в модули формы.
#Если Клиент Тогда
// Здесь код, который работает только на клиенте
#КонецЕсли
Посмотрим на область инициализации модуля объекта. Это код, который находится «в подвале» модуля. Программисты часто используют его для инициализации экспортных переменных или логгирования.
Проанализируем ситуацию: если в этой области происходит обращение к общему модулю, у которого не установлена галочка «Сервер», или вызывается функция, вызывающая исключение при определенных условиях, метод ПолучитьОбъект() не сможет вернуть объект.
// В самом конце модуля объекта
// Если ОбщийМодульВызова недоступен на сервере, ПолучитьОбъект() упадет здесь
ИмяТекущегоПользователя = ОбщийМодульВызова.ОпределитьТекущегоПользователя();
Иногда ошибка кроется не в самом справочнике, а в Подписках на события. Рассмотрим механизм: при получении объекта платформа проверяет доступность всех процедур-обработчиков подписок. Если хотя бы один общий модуль, где находится обработчик, содержит синтаксическую ошибку или недоступен в текущем режиме исполнения (например, внешнее соединение), вызов ПолучитьОбъект() завершится ошибкой.
Для диагностики рекомендуется временно отключить подписки на событие ПриКопировании или ПередЗаписью для данного объекта и проверить, пропадет ли ошибка.
Разберем программный код, представленный в обсуждении. Автор использует тернарный оператор для проверки ссылки:
ФизЛицо = ?(ТипЗнч(Выборка.Ссылка) = Тип("СправочникСсылка.ФизическиеЛица")
И ЗначениеЗаполнено(Выборка.Ссылка)
И Выборка.Ссылка <> Null,
Выборка.Ссылка.ПолучитьОбъект(),
Справочники.ФизическиеЛица.СоздатьЭлемент());
Проанализируем риски этого подхода:
ЗначениеЗаполнено() вернет Истина, а тип будет соответствовать ссылке. В этом случае ПолучитьОбъект() вернет Неопределено. Если далее по коду ожидается именно объект, произойдет ошибка.ЛЕВОЕ СОЕДИНЕНИЕ, поле Ссылка может содержать NULL. Хотя в коде есть проверка на Null, использование тернарного оператора ?(...) иногда может приводить к вычислению обеих веток выражения в зависимости от контекста платформы.Поскольку отладчик в фоновом задании часто недоступен, выполним следующие шаги для локализации проблемы:
Шаг 1: Расширенное логгирование. Перепишем проблемный участок кода, используя конструкцию Попытка...Исключение, и запишем детальную информацию об ошибке в Журнал Регистрации.
Попытка
ТекущаяСсылка = Выборка.Ссылка;
Если ЗначениеЗаполнено(ТекущаяСсылка) Тогда
ФизЛицоОбъект = ТекущаяСсылка.ПолучитьОбъект();
Если ФизЛицоОбъект = Неопределено Тогда
ЗаписьЖурналаРегистрации("Обмен.Ошибка", УровеньЖурналаРегистрации.Предупреждение, , , "Битая ссылка: " + ТекущаяСсылка);
ФизЛицоОбъект = Справочники.ФизическиеЛица.СоздатьЭлемент();
КонецЕсли;
Иначе
ФизЛицоОбъект = Справочники.ФизическиеЛица.СоздатьЭлемент();
КонецЕсли;
Исключение
Инфо = ИнформацияОбОшибке();
ТекстОшибки = "Ошибка получения объекта: " + ТекущаяСсылка + Символы.ПС
+ ОписаниеОшибки() + Символы.ПС
+ Инфо.ИсходноеИмяМетода;
ЗаписьЖурналаРегистрации("Обмен.КритическаяОшибка", УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
ВызватьИсключение; // Передаем ошибку дальше, если нужно остановить процесс
КонецПопытки;
Шаг 2: Проверка интерактивным созданием. Попробуйте зайти в программу под тем же пользователем (сервисным пользователем обмена) и вручную создать элемент справочника. Часто система сразу выдаст окно с ошибкой в конкретной строке модуля.
Шаг 3: Синтаксический контроль. Откройте модуль объекта справочника ФизическиеЛица и выполните Синтаксический контроль (Ctrl + F7), обязательно установив в параметрах проверки галочки «Сервер», «Внешнее соединение» и «Тонкий клиент». Это подсветит участки кода, которые не могут быть скомпилированы на сервере. Для автоматизированного поиска таких проблем во всех модулях и расширениях можно использовать конфигурацию для анализа метаданных и выявления ошибок в модулях.
Шаг 4: Использование Технологического Журнала. Если ничего не помогает, настройте Технологический Журнал на сбор события EXCP. В логах будет указан полный стек вызова вплоть до конкретной строки общего модуля или модуля объекта, на которой произошло прерывание.
Резюме: Ошибка при ПолучитьОбъект() — это почти всегда сигнал о том, что модуль объекта «нездоров» в текущем контексте. Уделяйте внимание чистоте серверного кода и всегда проверяйте ссылки на валидность перед получением объекта.