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