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