Разработка и Конфигурация · 12 мин
Как проверить наличие реквизита у объекта 1С
Проверка наличия реквизита у объекта 1С: метод через метаданные, СвойствоСтруктуры, попытка-исключение. Примеры кода для справочников, документов и табличных частей.
Обращение к несуществующему реквизиту объекта в 1С — одна из самых частых причин ошибки «Поле объекта не обнаружено». Такая ситуация возникает, когда код должен работать с разными видами документов или справочников, у которых набор реквизитов различается, а также когда обработка запускается в конфигурациях с разным составом объектов метаданных. Чтобы код не падал с исключением, перед обращением к реквизиту нужно убедиться, что он действительно существует.
Проверить наличие реквизита можно несколькими способами: через коллекцию метаданных объекта, через структуру, через конструкцию «Попытка — Исключение» или через универсальные функции общих модулей. Каждый способ имеет свои ограничения по производительности и применимости: одни работают только с объектами конфигурации, другие подходят и для произвольных структур, третьи замедляют выполнение в массовых обработках.
Ниже разобраны рабочие варианты проверки с примерами кода на встроенном языке 1С, критерии выбора подхода и типичные ошибки, которые приводят к ложным результатам или потере производительности.
Проверка через метаданные объекта — основной способ
У любого объекта конфигурации (справочника, документа, плана счетов и т.д.) есть метод Метаданные(), который возвращает описание объекта. Внутри него находится коллекция Реквизиты, содержащая все реквизиты, добавленные разработчиком в конфигураторе. Метод Найти() этой коллекции ищет реквизит по имени и возвращает либо объект описания, либо Неопределено.
Если Объект.Метаданные().Реквизиты.Найти("Комментарий") <> Неопределено Тогда
Сообщить("Реквизит есть, значение: " + Объект.Комментарий);
Иначе
Сообщить("Реквизита Комментарий у объекта нет");
КонецЕсли;Важно понимать, что коллекция Реквизиты содержит только реквизиты, явно созданные в конфигураторе. Стандартные реквизиты (Ссылка, Код, Наименование, Номер, Дата, Проведен и другие) в неё не входят — они находятся в отдельной коллекции СтандартныеРеквизиты. Если нужно проверить и их, поиск выполняют в обеих коллекциях.
Кроме того, реквизиты могут быть общими — вынесенными в ветку «Общие реквизиты» дерева конфигурации. Такие реквизиты присутствуют у объекта, но в его собственной коллекции Реквизиты не отображаются. Для полной проверки учитывайте и этот источник.
Универсальная функция проверки реквизита
В реальных проектах проверку оформляют отдельной функцией в общем модуле, чтобы не дублировать код. Функция принимает объект (или его метаданные) и имя реквизита, а возвращает Истина или Ложь. Ниже пример, который учитывает и обычные, и стандартные реквизиты.
Функция ЕстьРеквизит(Объект, ИмяРеквизита) Экспорт
МетаданныеОбъекта = Объект.Метаданные();
Если МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
Возврат Истина;
КонецЕсли;
Для Каждого СтандартныйРеквизит Из МетаданныеОбъекта.СтандартныеРеквизиты Цикл
Если СтандартныйРеквизит.Имя = ИмяРеквизита Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункцииЕсли ваша конфигурация построена на Библиотеке стандартных подсистем (БСП), в ней уже есть готовая функция ОбщегоНазначения.ЕстьРеквизитОбъекта(). Она работает по тому же принципу через метаданные и дополнительно проверяет общие реквизиты. Использование штатной функции предпочтительнее: она протестирована и обновляется вместе с библиотекой.
При вызове функции в цикле по тысячам объектов одного вида имеет смысл кэшировать результат: состав реквизитов у всех документов одного вида одинаков, поэтому проверку достаточно выполнить один раз для метаданных вида, а не для каждого объекта.
Проверка реквизитов табличной части
Табличные части имеют собственную коллекцию реквизитов, вложенную в описание табличной части. Путь к ней: Метаданные().ТабличныеЧасти.Найти(\"ИмяТЧ\").Реквизиты. Сначала проверяют наличие самой табличной части, затем — наличие реквизита внутри неё.
МетаданныеОбъекта = Объект.Метаданные();
ТЧ = МетаданныеОбъекта.ТабличныеЧасти.Найти("Товары");
Если ТЧ <> Неопределено
И ТЧ.Реквизиты.Найти("Характеристика") <> Неопределено Тогда
// Реквизит Характеристика есть в табличной части Товары
КонецЕсли;Двойная проверка обязательна: если табличной части с таким именем нет, обращение к её коллекции реквизитов вызовет ошибку. Вложенная конструкция с оператором И гарантирует, что вторая проверка выполнится только при успешной первой, поскольку в 1С условия внутри И вычисляются последовательно с прекращением при первом ложном результате в составных выражениях, оформленных корректно.
Проверка через структуру и заполнение значений
Когда данные представлены не объектом конфигурации, а структурой (например, результат разбора JSON или параметры обработки), метаданные недоступны. Для структур используют метод Свойство(), который возвращает Истина, если ключ существует, и может вернуть значение через второй параметр.
ЗначениеРеквизита = Неопределено;
Если СтруктураДанных.Свойство("Организация", ЗначениеРеквизита) Тогда
// Ключ есть, значение в переменной ЗначениеРеквизита
КонецЕсли;Для объектов конфигурации есть смежный приём: функция ЗаполнитьЗначенияСвойств() или получение данных через запрос с конструкцией «ВЫБРАТЬ ... ИЗ» не подходят для проверки, а вот выгрузка объекта в структуру через сериализацию — слишком тяжёлый путь. Поэтому правило простое: для объектов метаданных — проверка через Метаданные(), для структур — через Свойство().
Сравнение способов проверки
Основной способ для справочников и документов. Быстрый, но не видит стандартные и общие реквизиты без дополнительных проверок.
Только для структур и соответствий. Не применим к объектам конфигурации напрямую.
Универсально, но медленно и скрывает реальные ошибки. Допустимо только как крайняя мера вне циклов.
Поле, которого нет в объекте, вызовет ошибку компоновки запроса ещё до выполнения — проверку делают заранее через метаданные.
Проверка через «Попытка — Исключение»: когда допустимо
Обращение к реквизиту внутри блока Попытка с перехватом исключения технически работает: если реквизита нет, сработает исключение, и код продолжит выполнение. Однако этот способ считается антипаттерном для регулярной проверки по нескольким причинам.
Во-первых, генерация исключения — дорогая операция, и в циклах по тысячам объектов обработка заметно замедлится. Во-вторых, блок Попытка перехватит не только ошибку отсутствия реквизита, но и любую другую ошибку внутри блока, что замаскирует реальные проблемы в логике. В-третьих, такой код сложнее отлаживать: отладчик останавливается на каждом исключении.
Оправданное применение — разовые операции с объектами неизвестного типа, где состав возможных вариантов не ограничен метаданными конфигурации, например при работе с COM-объектами или внешними компонентами. Во всех штатных сценариях внутри конфигурации проверка через метаданные предпочтительнее.
Проверка реквизита в запросах и динамических списках
Отдельная ситуация — построение запроса, текст которого формируется программно в зависимости от состава реквизитов. Если включить в текст запроса несуществующее поле, запрос упадёт с ошибкой ещё на этапе компоновки. Поэтому текст запроса собирают динамически, добавляя поле только после проверки через метаданные.
- 1Получите объект метаданных нужного вида: Метаданные.Документы.РеализацияТоваровУслуг
- 2Проверьте наличие реквизита методом Реквизиты.Найти("ИмяПоля")
- 3Если реквизит есть — добавьте поле в текст запроса, если нет — подставьте конструкцию Неопределено или NULL
- 4Выполните запрос и проверьте результат на тестовых данных
- 5Убедитесь, что запрос корректно работает для всех видов объектов, для которых он предназначен
Тот же принцип действует для динамических списков с произвольным запросом и для СКД: состав полей проверяется до передачи текста в компоновщик. Проверка через метаданные здесь — единственный корректный вариант, так как попытка выполнить ошибочный запрос обойдётся дороже любой предварительной проверки.
Производительность и кэширование результатов
Обращение к метаданным — не бесплатная операция, особенно в клиент-серверном режиме, где вызов Метаданные() у объекта может потребовать обращения к серверу. В обработках, которые проверяют реквизит для каждого объекта из большой выборки, это становится узким местом.
Правильное решение — проверять реквизит один раз для вида объекта и сохранять результат. Состав реквизитов не меняется в течение сеанса работы: он определяется конфигурацией, а не данными. Поэтому результат проверки можно безопасно кэшировать в переменной или соответствии, где ключ — полное имя метаданных, а значение — булево.
Как убедиться, что проверка работает правильно
После внедрения проверки протестируйте её на объектах минимум трёх категорий: где реквизит точно есть, где его точно нет, и пограничный случай — стандартный или общий реквизит. Для каждой категории результат должен быть предсказуемым и совпадать с фактическим составом объекта в конфигураторе.
Как проверить результат
Найти() возвращает объект описания реквизита
Возвращает Неопределено — проверьте точность имени, оно чувствительно к написанию
Находится через коллекцию СтандартныеРеквизиты
Поиск только в Реквизиты даёт ложное отсутствие
Время выполнения сопоставимо с вариантом без проверки
Заметное замедление — проверка вызывается в цикле без кэширования
Полезный приём — временно вывести результат проверки в журнал регистрации или окно сообщений для контрольного набора объектов. Если для документов одного вида функция возвращает разные результаты, это признак ошибки: состав реквизитов не может различаться внутри одного вида объектов.
Если обработка должна работать в разных конфигурациях (например, универсальная выгрузка), проверяйте не только реквизиты, но и само существование вида объекта: Метаданные.Документы.Найти("ИмяВида"). Это избавит от ошибок ещё на этапе поиска объекта, а не на этапе обращения к его полям.
Частые вопросы
Как проверить наличие реквизита у ссылки, а не у объекта?
У ссылки тоже доступен метод Метаданные(), поэтому проверка выглядит так же: Ссылка.Метаданные().Реквизиты.Найти("Имя"). Получать объект методом ПолучитьОбъект() только ради проверки не нужно — это лишнее обращение к базе данных.
Почему Найти() возвращает Неопределено, хотя реквизит точно есть?
Чаще всего причина в том, что реквизит стандартный (Ссылка, Код, Наименование, Номер, Дата, ПометкаУдаления) или общий — они хранятся в других коллекциях. Реже — опечатка в имени: поиск регистронезависим, но лишние пробелы или перепутанные буквы дают отрицательный результат.
Можно ли проверить наличие реквизита у результата запроса (выборки)?
Напрямую через метаданные нельзя — выборка не является объектом конфигурации. Проверяйте состав полей через Колонки результата запроса: РезультатЗапроса.Колонки.Найти("ИмяПоля"). Для строки выборки состав колонок соответствует полям, перечисленным в тексте запроса.
Есть ли встроенная функция «ЕстьРеквизит» во встроенном языке?
Нет, штатной глобальной функции с таким именем в платформе нет. В конфигурациях на базе БСП есть функция ОбщегоНазначения.ЕстьРеквизитОбъекта(), в остальных случаях проверку пишут самостоятельно через Метаданные().Реквизиты.Найти().
Как проверить реквизит у объекта неизвестного типа?
Сначала определите тип: если это структура — используйте метод Свойство(), если объект конфигурации — Метаданные().Реквизиты.Найти(). Проверить принадлежность можно через ТипЗнч() и сравнение с Тип("Структура") или через попытку обращения к методу Метаданные().
Влияет ли проверка реквизита на права доступа?
Проверка через метаданные показывает наличие реквизита в конфигурации, но не учитывает права текущего пользователя. Если на реквизит настроены ограничения доступа на уровне записей или прав, обращение к нему может быть недоступно даже при успешной проверке существования.
Что важно запомнить
Основной способ проверки — Метаданные().Реквизиты.Найти(Имя) с сравнением результата с Неопределено. Учитывайте стандартные и общие реквизиты, для структур используйте Свойство(), избегайте Попытка-Исключение в циклах и кэшируйте результат проверки при массовой обработке.