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