Как правильно проверить заполнение реквизита в 1С и избежать ошибки «Элемент не выбран»?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде «1С:Предприятие» программисты часто сталкиваются с необходимостью проверки, заполнено ли то или иное поле (реквизит), прежде чем выводить данные на печать или обрабатывать их в коде. Типичный сценарий: если адрес заполнен, мы хотим вывести полное наименование города, а если пуст — записать пробел или пустую строку в печатную форму.

Однако простая интуитивная проверка на равенство значению Неопределено часто приводит к аварийному завершению программы с ошибкой: «Ошибка при вызове метода контекста (...) по причине: Элемент не выбран!». Разберем по шагам, почему возникает эта ошибка и как правильно реализовать проверку в коде.

Выясняем причину ошибки

Рассмотрим код, который изначально пытался использовать разработчик:


Если Адрес = Неопределено Тогда
    КомОбъект.FormFields("Г").Result = " ";
Иначе
    КомОбъект.FormFields("Г").Result = Адрес.Город.ПолноеНаименование();
КонецЕсли;

Этот код вызывает сбой в строке с вызовом метода ПолноеНаименование(). Выясним причину, проанализировав особенности работы платформы «1С:Предприятие» со ссылочными типами данных:

  1. Разница между Неопределено и пустой ссылкой: В 1С незаполненные реквизиты ссылочного типа (справочники, документы) хранят не пустое значение Неопределено, а специальную типизированную пустую ссылку (ее аналог можно получить методом ПустаяСсылка() конкретного менеджера). Сравнение Адрес = Неопределено возвращает Ложь, и программа заходит в ветку Иначе.
  2. Обращение через точку к пустой ссылке: Когда мы пишем Адрес.Город, и при этом реквизит Адрес пуст, платформа не выдает ошибку. Она возвращает значение по умолчанию для вложенного реквизита — в данном случае пустую ссылку справочника городов. Но как только мы пытаемся вызвать встроенный метод (такой как ПолноеНаименование()) у этой пустой ссылки, платформа генерирует исключение: «Элемент не выбран!», так как физически объект базы данных отсутствует и метод вызвать невозможно.

Нюансы работы с составными типами данных

Ситуация усложняется, если реквизит имеет составной тип данных (например, когда в поле «Адрес» можно записать как элемент справочника «Адреса», так и строку или контрагента). Начальным незаполненным значением для составного типа действительно является Неопределено. Однако если пользователь сначала выбрал конкретный тип (справочник «Адреса»), а затем просто очистил это поле, значение реквизита изменится. Вместо Неопределено в нем окажется пустая ссылка выбранного ранее типа. Именно поэтому проверка на равенство Неопределено крайне ненадежна в реальных бизнес-сценариях.

Решение 1. Использование универсальной функции ЗначениеЗаполнено()

Самый простой, надежный и рекомендуемый платформенный метод решения этой проблемы — использование встроенной функции ЗначениеЗаполнено(). Данная функция является универсальной: она возвращает Истина, если переданное ей значение отличается от пустой ссылки любого типа, Неопределено, Null, пустой строки, пустой даты или нуля.

Перепишем исходный код с использованием этой функции:


Если ЗначениеЗаполнено(Адрес.Город) Тогда
    КомОбъект.FormFields("Г").Result = Адрес.Город.ПолноеНаименование();
Иначе
    КомОбъект.FormFields("Г").Result = " ";
КонецЕсли;

В данном случае, если реквизит Город в адресе не заполнен (содержит пустую ссылку), функция ЗначениеЗаполнено() вернет Ложь, управление перейдет в ветку Иначе, и вызов метода ПолноеНаименование() не произойдет. Это полностью исключает возникновение ошибки.

Решение 2. Сравнение с пустой ссылкой

Если мы точно знаем тип данных проверяемого реквизита, мы можем выполнить явное сравнение с пустой ссылкой этого типа. Рассмотрим два способа реализации такой проверки:

Способ А. Сравнение с пустой ссылкой менеджера справочника:


Если Адрес.Город <> Справочники.Города.ПустаяСсылка() Тогда
    КомОбъект.FormFields("Г").Result = Адрес.Город.ПолноеНаименование();
Иначе
    КомОбъект.FormFields("Г").Result = " ";
КонецЕсли;

Способ Б. Использование метода .Пустая():

У ссылочных типов данных есть удобный встроенный метод Пустая(). Мы можем применить его:


Если Не Адрес.Город.Пустая() Тогда
    КомОбъект.FormFields("Г").Result = Адрес.Город.ПолноеНаименование();
Иначе
    КомОбъект.FormFields("Г").Result = " ";
КонецЕсли;

Важное ограничение: метод Пустая() можно использовать только тогда, когда мы абсолютно уверены, что в переменной находится ссылка. Если там окажется значение Неопределено или Null (что часто бывает при работе с составными типами данных или при левом соединении в запросах), вызов метода приведет к критической ошибке «Метод объекта не обнаружен».

Решение 3. Использование запросов для оптимизации производительности

Хотя проверка через ЗначениеЗаполнено() решает проблему с ошибкой, многоуровневое обращение через точку (Адрес.Город.ПолноеНаименование()) обладает существенным недостатком — оно порождает неявные запросы к базе данных. Если такой код выполняется в цикле (например, при выводе строк отчета или заполнении табличной части), производительность системы резко снизится. Это классическая проблема «N+1 запросов».

Чтобы избежать падения производительности, правильнее всего получать готовое представление города с помощью объекта Запрос. Посмотрим на пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЕСТЬNULL(ДокументСсылка.Адрес.Город.ПолноеНаименование, """") КАК ГородНаименование
    |ИЗ
    |	Документ.Распоряжение КАК ДокументСсылка
    |ГДЕ
    |	ДокументСсылка.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    Если ЗначениеЗаполнено(Выборка.ГородНаименование) Тогда
        КомОбъект.FormFields("Г").Result = Выборка.ГородНаименование;
    Иначе
        КомОбъект.FormFields("Г").Result = " ";
    КонецЕсли;
КонецЕсли;

Использование запроса не только ускоряет работу программы, выполняя всего одно обращение к СУБД, но и автоматически обрабатывает пустые ссылки: если город или адрес не заполнены, запрос вернет пустую строку, что исключает возникновение ошибки «Элемент не выбран!» при формировании печатной формы.

Решение 4. Использование функций БСП

Если разработка ведется в типовой конфигурации на базе Библиотеки стандартных подсистем (БСП), мы можем воспользоваться готовыми оптимизированными функциями общего назначения для безопасного чтения реквизитов. Это позволяет избежать написания громоздких запросов вручную и предотвращает получение объектов в память.

Рассмотрим пример использования функции ЗначениеРеквизитаОбъекта:


ГородСсылка = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Адрес, "Город");
Если ЗначениеЗаполнено(ГородСсылка) Тогда
    ПолноеНаименование = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ГородСсылка, "ПолноеНаименование");
    КомОбъект.FormFields("Г").Result = ПолноеНаименование;
Иначе
    КомОбъект.FormFields("Г").Result = " ";
КонецЕсли;

Таким образом, мы разобрали основные способы решения проблемы «Элемент не выбран». Для простых точечных проверок на клиенте или в формах идеально подойдет функция ЗначениеЗаполнено(), а при массовой обработке данных и формировании сложных печатных форм всегда следует отдавать предпочтение запросам.

← На главную