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

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

1С: как проверить существование реквизита

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

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

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

Способ проверки зависит от того, с чем вы работаете: с прикладным объектом (документом, справочником), с записью регистра, со структурой/соответствием или с произвольным значением неизвестного типа. Ниже разобраны рабочие подходы для каждого случая, их ограничения и типичные ошибки.

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

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

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

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

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

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

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

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

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

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

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

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

КонецЕсли;

Возврат МД.СтандартныеРеквизиты.Найти(ИмяРеквизита) <> Неопределено;

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

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

Реквизиты табличных частей не входят в основную коллекцию Реквизиты объекта. Для них нужен двухуровневый доступ: сначала найти табличную часть по имени, затем в её коллекции реквизитов найти нужный.

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

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

ТЧ = МД.ТабличныеЧасти.Найти(ИмяТЧ);

Если ТЧ = Неопределено Тогда

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

КонецЕсли;

Возврат ТЧ.Реквизиты.Найти(ИмяРеквизита) <> Неопределено;

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

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

Проверка свойства в структуре и соответствии

Структуры и соответствия — не объекты метаданных, поэтому для них применяются другие механизмы. У структуры есть метод Свойство(Имя, НайденноеЗначение): он возвращает Истина, если ключ существует, и помещает значение во второй параметр. Это одновременно и проверка, и безопасное чтение.

Перем ЗначениеРеквизита;

Если СтруктураДанных.Свойство("Организация", ЗначениеРеквизита) Тогда

// Ключ есть, значение в переменной ЗначениеРеквизита

Иначе

// Ключа нет — используем значение по умолчанию

КонецЕсли;

У соответствия метода Свойство нет; там проверку выполняют через Соответствие.Получить(Ключ) — если ключ отсутствует, вернётся Неопределено. Учтите тонкость: если значение по существующему ключу само равно Неопределено, отличить «ключа нет» от «значение пустое» таким способом нельзя. Для строгой проверки перебирайте коллекцию циклом Для Каждого КлючИЗначение Из Соответствие Цикл.

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

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

Подходит для документов, справочников, ПВХ и других прикладных объектов. Не работает для структур и примитивных типов.

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

Одновременно проверяет ключ и читает значение. Только для структур и фиксированных структур.

Соответствие.Получить()

Быстрый доступ по ключу, но не отличает отсутствующий ключ от значения Неопределено.

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

Универсальный, но медленный и маскирует другие ошибки. Использовать как последний вариант.

Универсальная проверка для произвольного значения

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

Функция ЕстьРеквизитУниверсально(Значение, ИмяРеквизита) Экспорт

Если ТипЗнч(Значение) = Тип("Структура")

Или ТипЗнч(Значение) = Тип("ФиксированнаяСтруктура") Тогда

Возврат Значение.Свойство(ИмяРеквизита);

КонецЕсли;

Попытка

МД = Значение.Метаданные();

Исключение

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

КонецПопытки;

Возврат МД.Реквизиты.Найти(ИмяРеквизита) <> Неопределено;

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

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

Безопасное чтение и запись после проверки

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

Если ЕстьРеквизит(Объект, "Договор") Тогда

Объект["Договор"] = НовыйДоговор;

КонецЕсли;

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

Типичные ошибки и ограничения

Первая распространённая ошибка — проверка только основной коллекции Реквизиты при обращении к стандартному реквизиту. Код вроде Реквизиты.Найти("Номер") вернёт Неопределено, хотя реквизит «Номер» у документа есть, — он стандартный.

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

Третья ошибка — кэширование результата без учёта контекста. Состав реквизитов зависит от вида объекта: у документов «ПоступлениеТоваров» и «РеализацияТоваров» он разный. Если функция проверки вызывается в цикле по разнотипным объектам, кэшировать результат по имени реквизита нельзя — только по паре «тип объекта + имя реквизита».

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

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

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

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

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

Поиск существующего реквизита
Норма

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

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

Вернул Неопределено — проверьте регистр не виноват, значит реквизит в другой коллекции

Поиск стандартного реквизита (Номер, Дата)
Норма

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

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

Не найден ни в одной коллекции — имя указано неверно

Обращение к реквизиту после проверки
Норма

Чтение и запись проходят без исключений

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

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

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

Итог

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

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

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

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

У ссылки доступен метод Метаданные(), который возвращает описание вида объекта. Вызов Ссылка.Метаданные().Реквизиты.Найти("Имя") выполняется без обращения к данным базы и без выборки объекта — это быстрый и безопасный способ проверки.

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

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

Можно ли проверить реквизит через Попытка/Исключение?

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

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

Через метаданные объекта-владельца: Объект.Метаданные().ТабличныеЧасти.Найти("ИмяТЧ").Реквизиты.Найти("ИмяРеквизита"). У самой строки табличной части метода Метаданные() нет.

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

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

Как проверить программно добавленный реквизит управляемой формы?

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