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