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

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

1С: как определить, есть ли реквизит у объекта

Способы определить, есть ли реквизит у объекта в 1С: через метаданные, НайтиРеквизит, обход коллекции и функцию ЕстьРеквизитОбъекта. Примеры кода и ошибки.

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

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

Зачем вообще проверять наличие реквизита

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

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

Третий сценарий — поддержка конфигураций, находящихся на частичной поддержке или доработанных в разных филиалах по-разному. Код, который проверяет наличие реквизита перед использованием, устойчивее к обновлениям и расхождениям в структуре баз.

Способ 1: проверка через метаданные объекта

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

Если ДокументОбъект.Метаданные().Реквизиты.Найти("Соглашение") <> Неопределено Тогда

Соглашение = ДокументОбъект.Соглашение;

КонецЕсли;

Метод Найти() принимает имя реквизита строкой и возвращает объект описания метаданных либо Неопределено, если реквизита нет. Именно сравнение с Неопределено и является признаком наличия или отсутствия реквизита.

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

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

Способ 2: универсальная функция ЕстьРеквизитОбъекта

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

Функция ЕстьРеквизитОбъекта(ИмяРеквизита, МетаданныеОбъекта) Экспорт

Возврат НЕ (МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) = Неопределено);

КонецФункции

Обратите внимание: функция принимает не сам объект, а его метаданные. Это позволяет вызывать её и для ссылок (через Ссылка.Метаданные()), и для объектов, и вообще без создания экземпляра — только по описанию метаданных, что быстрее.

Расширенный вариант функции дополнительно проверяет стандартные реквизиты и табличные части:

Функция ЕстьРеквизитОбъекта(ИмяРеквизита, МетаданныеОбъекта) Экспорт

Если МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда

Возврат Истина;

КонецЕсли;

Для Каждого СтандартныйРеквизит Из МетаданныеОбъекта.СтандартныеРеквизиты Цикл

Если СтандартныйРеквизит.Имя = ИмяРеквизита Тогда

Возврат Истина;

КонецЕсли;

КонецЦикла;

Возврат Ложь;

КонецФункции

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

Отдельная задача — определить, есть ли колонка в табличной части объекта. Логика та же, но путь длиннее: сначала находим табличную часть, затем ищем реквизит в её коллекции.

МетаданныеОбъекта = ДокументОбъект.Метаданные();

МетаданныеТЧ = МетаданныеОбъекта.ТабличныеЧасти.Найти("Товары");

Если МетаданныеТЧ <> Неопределено

И МетаданныеТЧ.Реквизиты.Найти("СтавкаНДС") <> Неопределено Тогда

// Колонка существует, можно обращаться

КонецЕсли;

Здесь важна двойная проверка: сначала сама табличная часть, потом реквизит внутри неё. Если пропустить первую проверку и обратиться к ТабличныеЧасти.Найти("Товары").Реквизиты при отсутствующей табличной части, получите ошибку обращения к Неопределено.

Способ 3: через структуру и ЗаполнитьЗначенияСвойств

Иногда вместо прямой проверки удобнее использовать механизм заполнения по именам свойств. Метод ЗаполнитьЗначенияСвойств(Приёмник, Источник) переносит только те свойства, которые существуют в приёмнике, и молча пропускает отсутствующие.

СтруктураДанных = Новый Структура("Договор, Соглашение, Склад");

ЗаполнитьЗначенияСвойств(СтруктураДанных, ДокументОбъект);

Если СтруктураДанных.Договор <> Неопределено Тогда

// Реквизит был у объекта и заполнен

КонецЕсли;

У этого подхода есть ограничение: он не отличает «реквизита нет» от «реквизит есть, но пустой» — в обоих случаях значение останется Неопределено. Поэтому для строгой проверки существования реквизита он не подходит, а вот для безопасного чтения набора полей — вполне рабочий вариант.

Сравнение подходов

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

Точная проверка существования реквизита. Подходит всегда, но требует отдельного цикла для стандартных реквизитов.

ЗаполнитьЗначенияСвойств

Быстрое чтение нескольких полей без ошибок, но не различает отсутствие реквизита и пустое значение.

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

Можно обернуть обращение в исключение, но это медленнее и затрудняет отладку. Использовать только как крайний вариант.

Почему не стоит использовать Попытка/Исключение

Иногда встречается код, где обращение к реквизиту оборачивают в Попытка ... Исключение КонецПопытки и по факту исключения делают вывод, что реквизита нет. Формально это работает, но у подхода есть серьёзные минусы.

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

Производительность и кеширование метаданных

Вызов Метаданные() и поиск в коллекции — недорогие операции, но в цикле по сотням тысяч объектов одного вида повторять их для каждого объекта избыточно: структура метаданных у всех документов одного вида одинакова.

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

МетаданныеДокумента = Ссылка.Метаданные(); // один раз до цикла

ЕстьДоговор = МетаданныеДокумента.Реквизиты.Найти("Договор") <> Неопределено;

Для Каждого Строка Из ТаблицаДанных Цикл

Если ЕстьДоговор Тогда

// работа с реквизитом

КонецЕсли;

КонецЦикла;

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

Проверка результата и контрольные точки

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

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

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

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

Документ с реквизитом
Норма

Функция возвращает Истина, значение читается без ошибки

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

Функция возвращает Ложь при существующем реквизите — проверьте имя и регистр букв

Документ без реквизита
Норма

Функция возвращает Ложь, ветка обращения к реквизиту не выполняется

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

Ошибка «Поле объекта не обнаружено» — проверка не срабатывает

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

Находится через перебор СтандартныеРеквизиты

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

Возвращается Неопределено — стандартные реквизиты не проверяются

Часто задаваемые вопросы

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

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

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

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

Работает ли Найти() с учётом регистра имени реквизита?

Нет, поиск в коллекциях метаданных регистронезависим, как и сам язык 1С. Имена «Договор» и «договор» будут найдены одинаково. Тем не менее рекомендуется придерживаться точного написания имени из конфигуратора для читаемости кода.

Как проверить наличие табличной части у документа?

Через ту же коллекцию метаданных: МетаданныеОбъекта.ТабличныеЧасти.Найти("Товары"). Если вернулось Неопределено — табличной части с таким именем у объекта нет. Движения и измерения регистров проверяются аналогично через свои коллекции.

Есть ли готовая функция в типовых конфигурациях?

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

Что делать, если нужно проверить реквизит по имени, пришедшему извне?

Обязательно проверяйте такое имя через метаданные перед обращением и ограничивайте список допустимых имён, если источник ненадёжен. Обращение к реквизиту по произвольной строке через Объект[ИмяРеквизита] без проверки — источник ошибок и потенциальных уязвимостей.

Итог

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

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