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