К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как проверить наличие реквизита у объекта 1С

Проверка наличия реквизита у объекта 1С: метод через метаданные, СвойствоСтруктуры, попытка-исключение. Примеры кода для справочников, документов и табличных частей.

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

Проверить наличие реквизита можно несколькими способами: через коллекцию метаданных объекта, через структуру, через конструкцию «Попытка — Исключение» или через универсальные функции общих модулей. Каждый способ имеет свои ограничения по производительности и применимости: одни работают только с объектами конфигурации, другие подходят и для произвольных структур, третьи замедляют выполнение в массовых обработках.

Ниже разобраны рабочие варианты проверки с примерами кода на встроенном языке 1С, критерии выбора подхода и типичные ошибки, которые приводят к ложным результатам или потере производительности.

Проверка через метаданные объекта — основной способ

У любого объекта конфигурации (справочника, документа, плана счетов и т.д.) есть метод Метаданные(), который возвращает описание объекта. Внутри него находится коллекция Реквизиты, содержащая все реквизиты, добавленные разработчиком в конфигураторе. Метод Найти() этой коллекции ищет реквизит по имени и возвращает либо объект описания, либо Неопределено.

bsl
Если Объект.Метаданные().Реквизиты.Найти("Комментарий") <> Неопределено Тогда
    Сообщить("Реквизит есть, значение: " + Объект.Комментарий);
Иначе
    Сообщить("Реквизита Комментарий у объекта нет");
КонецЕсли;

Важно понимать, что коллекция Реквизиты содержит только реквизиты, явно созданные в конфигураторе. Стандартные реквизиты (Ссылка, Код, Наименование, Номер, Дата, Проведен и другие) в неё не входят — они находятся в отдельной коллекции СтандартныеРеквизиты. Если нужно проверить и их, поиск выполняют в обеих коллекциях.

Кроме того, реквизиты могут быть общими — вынесенными в ветку «Общие реквизиты» дерева конфигурации. Такие реквизиты присутствуют у объекта, но в его собственной коллекции Реквизиты не отображаются. Для полной проверки учитывайте и этот источник.

Универсальная функция проверки реквизита

В реальных проектах проверку оформляют отдельной функцией в общем модуле, чтобы не дублировать код. Функция принимает объект (или его метаданные) и имя реквизита, а возвращает Истина или Ложь. Ниже пример, который учитывает и обычные, и стандартные реквизиты.

bsl
Функция ЕстьРеквизит(Объект, ИмяРеквизита) Экспорт
    МетаданныеОбъекта = Объект.Метаданные();
    Если МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
        Возврат Истина;
    КонецЕсли;
    Для Каждого СтандартныйРеквизит Из МетаданныеОбъекта.СтандартныеРеквизиты Цикл
        Если СтандартныйРеквизит.Имя = ИмяРеквизита Тогда
            Возврат Истина;
        КонецЕсли;
    КонецЦикла;
    Возврат Ложь;
КонецФункции

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

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

Проверка реквизитов табличной части

Табличные части имеют собственную коллекцию реквизитов, вложенную в описание табличной части. Путь к ней: Метаданные().ТабличныеЧасти.Найти(\"ИмяТЧ\").Реквизиты. Сначала проверяют наличие самой табличной части, затем — наличие реквизита внутри неё.

bsl
МетаданныеОбъекта = Объект.Метаданные();
ТЧ = МетаданныеОбъекта.ТабличныеЧасти.Найти("Товары");
Если ТЧ <> Неопределено
    И ТЧ.Реквизиты.Найти("Характеристика") <> Неопределено Тогда
    // Реквизит Характеристика есть в табличной части Товары
КонецЕсли;

Двойная проверка обязательна: если табличной части с таким именем нет, обращение к её коллекции реквизитов вызовет ошибку. Вложенная конструкция с оператором И гарантирует, что вторая проверка выполнится только при успешной первой, поскольку в 1С условия внутри И вычисляются последовательно с прекращением при первом ложном результате в составных выражениях, оформленных корректно.

Проверка через структуру и заполнение значений

Когда данные представлены не объектом конфигурации, а структурой (например, результат разбора JSON или параметры обработки), метаданные недоступны. Для структур используют метод Свойство(), который возвращает Истина, если ключ существует, и может вернуть значение через второй параметр.

bsl
ЗначениеРеквизита = Неопределено;
Если СтруктураДанных.Свойство("Организация", ЗначениеРеквизита) Тогда
    // Ключ есть, значение в переменной ЗначениеРеквизита
КонецЕсли;

Для объектов конфигурации есть смежный приём: функция ЗаполнитьЗначенияСвойств() или получение данных через запрос с конструкцией «ВЫБРАТЬ ... ИЗ» не подходят для проверки, а вот выгрузка объекта в структуру через сериализацию — слишком тяжёлый путь. Поэтому правило простое: для объектов метаданных — проверка через Метаданные(), для структур — через Свойство().

Сравнение способов проверки

Метаданные().Реквизиты.Найти()

Основной способ для справочников и документов. Быстрый, но не видит стандартные и общие реквизиты без дополнительных проверок.

Структура.Свойство()

Только для структур и соответствий. Не применим к объектам конфигурации напрямую.

Попытка — Исключение

Универсально, но медленно и скрывает реальные ошибки. Допустимо только как крайняя мера вне циклов.

Запрос к данным

Поле, которого нет в объекте, вызовет ошибку компоновки запроса ещё до выполнения — проверку делают заранее через метаданные.

Проверка через «Попытка — Исключение»: когда допустимо

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

Во-первых, генерация исключения — дорогая операция, и в циклах по тысячам объектов обработка заметно замедлится. Во-вторых, блок Попытка перехватит не только ошибку отсутствия реквизита, но и любую другую ошибку внутри блока, что замаскирует реальные проблемы в логике. В-третьих, такой код сложнее отлаживать: отладчик останавливается на каждом исключении.

Оправданное применение — разовые операции с объектами неизвестного типа, где состав возможных вариантов не ограничен метаданными конфигурации, например при работе с COM-объектами или внешними компонентами. Во всех штатных сценариях внутри конфигурации проверка через метаданные предпочтительнее.

Проверка реквизита в запросах и динамических списках

Отдельная ситуация — построение запроса, текст которого формируется программно в зависимости от состава реквизитов. Если включить в текст запроса несуществующее поле, запрос упадёт с ошибкой ещё на этапе компоновки. Поэтому текст запроса собирают динамически, добавляя поле только после проверки через метаданные.

Порядок построения безопасного запроса
  1. 1
    Получите объект метаданных нужного вида: Метаданные.Документы.РеализацияТоваровУслуг
  2. 2
    Проверьте наличие реквизита методом Реквизиты.Найти("ИмяПоля")
  3. 3
    Если реквизит есть — добавьте поле в текст запроса, если нет — подставьте конструкцию Неопределено или NULL
  4. 4
    Выполните запрос и проверьте результат на тестовых данных
  5. 5
    Убедитесь, что запрос корректно работает для всех видов объектов, для которых он предназначен

Тот же принцип действует для динамических списков с произвольным запросом и для СКД: состав полей проверяется до передачи текста в компоновщик. Проверка через метаданные здесь — единственный корректный вариант, так как попытка выполнить ошибочный запрос обойдётся дороже любой предварительной проверки.

Производительность и кэширование результатов

Обращение к метаданным — не бесплатная операция, особенно в клиент-серверном режиме, где вызов Метаданные() у объекта может потребовать обращения к серверу. В обработках, которые проверяют реквизит для каждого объекта из большой выборки, это становится узким местом.

Правильное решение — проверять реквизит один раз для вида объекта и сохранять результат. Состав реквизитов не меняется в течение сеанса работы: он определяется конфигурацией, а не данными. Поэтому результат проверки можно безопасно кэшировать в переменной или соответствии, где ключ — полное имя метаданных, а значение — булево.

Типичные ошибки
01
Проверка только коллекции Реквизиты без учёта стандартных реквизитов — код сообщает, что поля Номер или Дата «не существует», хотя обращение к ним работает
02
Вызов Метаданные().Реквизиты.Найти() внутри цикла по тысячам объектов — лишние серверные вызовы и замедление обработки
03
Использование Попытка-Исключение как основного способа — маскировка других ошибок и падение производительности
04
Проверка реквизита табличной части без проверки существования самой табличной части — ошибка при обращении к Неопределено
05
Сравнение результата Найти() с пустой строкой или нулём вместо Неопределено — проверка всегда даёт неверный результат

Как убедиться, что проверка работает правильно

После внедрения проверки протестируйте её на объектах минимум трёх категорий: где реквизит точно есть, где его точно нет, и пограничный случай — стандартный или общий реквизит. Для каждой категории результат должен быть предсказуемым и совпадать с фактическим составом объекта в конфигураторе.

Контрольные точки

Как проверить результат

Реквизит, добавленный в конфигураторе
Норма

Найти() возвращает объект описания реквизита

Тревожный признак

Возвращает Неопределено — проверьте точность имени, оно чувствительно к написанию

Стандартный реквизит (Номер, Дата, Ссылка)
Норма

Находится через коллекцию СтандартныеРеквизиты

Тревожный признак

Поиск только в Реквизиты даёт ложное отсутствие

Обработка на большой выборке
Норма

Время выполнения сопоставимо с вариантом без проверки

Тревожный признак

Заметное замедление — проверка вызывается в цикле без кэширования

Полезный приём — временно вывести результат проверки в журнал регистрации или окно сообщений для контрольного набора объектов. Если для документов одного вида функция возвращает разные результаты, это признак ошибки: состав реквизитов не может различаться внутри одного вида объектов.

Если обработка должна работать в разных конфигурациях (например, универсальная выгрузка), проверяйте не только реквизиты, но и само существование вида объекта: Метаданные.Документы.Найти("ИмяВида"). Это избавит от ошибок ещё на этапе поиска объекта, а не на этапе обращения к его полям.
ПЗ
Практическое замечание

Частые вопросы

Как проверить наличие реквизита у ссылки, а не у объекта?

У ссылки тоже доступен метод Метаданные(), поэтому проверка выглядит так же: Ссылка.Метаданные().Реквизиты.Найти("Имя"). Получать объект методом ПолучитьОбъект() только ради проверки не нужно — это лишнее обращение к базе данных.

Почему Найти() возвращает Неопределено, хотя реквизит точно есть?

Чаще всего причина в том, что реквизит стандартный (Ссылка, Код, Наименование, Номер, Дата, ПометкаУдаления) или общий — они хранятся в других коллекциях. Реже — опечатка в имени: поиск регистронезависим, но лишние пробелы или перепутанные буквы дают отрицательный результат.

Можно ли проверить наличие реквизита у результата запроса (выборки)?

Напрямую через метаданные нельзя — выборка не является объектом конфигурации. Проверяйте состав полей через Колонки результата запроса: РезультатЗапроса.Колонки.Найти("ИмяПоля"). Для строки выборки состав колонок соответствует полям, перечисленным в тексте запроса.

Есть ли встроенная функция «ЕстьРеквизит» во встроенном языке?

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

Как проверить реквизит у объекта неизвестного типа?

Сначала определите тип: если это структура — используйте метод Свойство(), если объект конфигурации — Метаданные().Реквизиты.Найти(). Проверить принадлежность можно через ТипЗнч() и сравнение с Тип("Структура") или через попытку обращения к методу Метаданные().

Влияет ли проверка реквизита на права доступа?

Проверка через метаданные показывает наличие реквизита в конфигурации, но не учитывает права текущего пользователя. Если на реквизит настроены ограничения доступа на уровне записей или прав, обращение к нему может быть недоступно даже при успешной проверке существования.

Итог

Что важно запомнить

Основной способ проверки — Метаданные().Реквизиты.Найти(Имя) с сравнением результата с Неопределено. Учитывайте стандартные и общие реквизиты, для структур используйте Свойство(), избегайте Попытка-Исключение в циклах и кэшируйте результат проверки при массовой обработке.