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