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

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

Как проверить наличие реквизита у документа в 1С 8

Способы проверить наличие реквизита у документа в 1С 8: метаданные, Найти(), ЗаполнитьЗначенияСвойств, типовые ошибки и примеры кода для разработчиков.

При доработке конфигураций 1С разработчик часто пишет универсальный код, который должен работать с разными видами документов. Например, обработка обходит все документы реализации и поступления и пытается прочитать реквизит «Склад». Если хотя бы у одного вида документа такого реквизита нет, обращение Документ.Склад вызовет ошибку «Поле объекта не обнаружено», и выполнение прервётся.

Проверка наличия реквизита перед обращением к нему — стандартный приём защитного программирования. В 1С 8 есть несколько рабочих способов: через коллекцию метаданных, через метод Найти() и через косвенные приёмы вроде ЗаполнитьЗначенияСвойств. Каждый из них подходит для своей ситуации и имеет ограничения по производительности.

Почему прямое обращение к реквизиту опасно

Встроенный язык 1С обращается к реквизитам объекта по имени во время выполнения. Если реквизита нет в конфигурации, платформа не может «молча» вернуть пустое значение — она генерирует исключение. В отладчике это выглядит как «Поле объекта не обнаружено (Склад)», а в рабочей базе пользователь увидит окно с ошибкой, а фоновое задание просто завершится аварийно.

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

Проверка через метаданные — основной способ

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

Типовой код проверки шапки документа выглядит так:

Если ДокументОбъект.Метаданные().Реквизиты.Найти("Склад") <> Неопределено Тогда

Склад = ДокументОбъект.Склад;

Иначе

Склад = Неопределено;

КонецЕсли;

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

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

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

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

Если Мета.ТабличныеЧасти.Найти("Товары") <> Неопределено Тогда

МетаТЧ = Мета.ТабличныеЧасти.Товары;

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

// Колонка "Серия" есть, можно читать ДокументОбъект.Товары[0].Серия

КонецЕсли;

КонецЕсли;

Обратите внимание: у самой строки табличной части метода Метаданные() нет. Поэтому проверять колонку через объект строки напрямую нельзя — только через метаданные родительского документа.

Альтернативные приёмы проверки

Функция ЗаполнитьЗначенияСвойств

Если нужно перенести значения реквизитов из одного объекта в другой, не зная заранее их состава, удобна функция общего назначения ЗаполнитьЗначенияСвойств(Приемник, Источник). Она копирует только совпадающие по именам свойства и не падает, если какого-то реквизита нет у приёмника. Это не проверка в чистом виде, но часто именно она решает исходную задачу без явного поиска реквизита.

Проверка через структуру

Иногда удобнее работать не с объектом документа, а со структурой, полученной из него. У структуры есть метод Свойство("Имя", Значение), который возвращает Истина, если ключ есть. Такой подход применяют, когда данные документа уже сериализованы, например при разборе ответа веб-сервиса или строки JSON.

Сравнение вариантов

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

Точный и быстрый способ для объектов конфигурации; не работает для структур и произвольных данных

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

Подходит для структур и данных из обмена; не применим к объектам документа напрямую

ЗаполнитьЗначенияСвойств

Решает задачу копирования без явной проверки; не сообщает, каких реквизитов не хватило

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

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

Проверка реквизита у ссылки и у вида документа

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

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

Порядок действий
  1. 1
    Определите, с чем работаете: объект, ссылка или вид документа
  2. 2
    Получите описание метаданных через Метаданные() или Метаданные.Документы
  3. 3
    Вызовите Реквизиты.Найти("Имя") и сравните результат с Неопределено
  4. 4
    Для табличных частей сначала проверьте ТабличныеЧасти.Найти(), затем реквизит внутри неё
  5. 5
    Только после успешной проверки обращайтесь к значению реквизита

Производительность и кэширование проверок

Обращение к метаданным — недорогая операция, но в цикле по тысячам документов одного вида повторять её бессмысленно: состав реквизитов не меняется между экземплярами. Правильный приём — проверить один раз до цикла и сохранить результат в переменную или соответствие «вид документа → есть ли реквизит».

Пример кэширования для универсальной обработки:

КэшРеквизитов = Новый Соответствие;

// внутри цикла:

ИмяВида = Док.Метаданные().Имя;

ЕстьСклад = КэшРеквизитов[ИмяВида];

Если ЕстьСклад = Неопределено Тогда

ЕстьСклад = Док.Метаданные().Реквизиты.Найти("Склад") <> Неопределено;

КэшРеквизитов.Вставить(ИмяВида, ЕстьСклад);

КонецЕсли;

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

Частые ошибки и как их диагностировать

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

Вторая ловушка — регистр имён. Метод Найти() нечувствителен к регистру, как и весь встроенный язык, поэтому «Склад» и «СКЛАД» найдут один и тот же реквизит. А вот опечатка в имени («СкладОсновной» вместо «Склад») молча вернёт Неопределено, и ветка чтения значения просто не выполнится. Если код «не видит» точно существующий реквизит, первым делом сверьте имя с конфигуратором посимвольно.

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

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

Вызов Найти() для существующего реквизита
Норма

Возвращается объект описания реквизита, отличный от Неопределено

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

Возвращается Неопределено — проверьте имя и коллекцию (обычные или стандартные)

Чтение значения после проверки
Норма

Значение читается без исключения

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

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

Работа с табличной частью
Норма

Колонка найдена через метаданные табличной части

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

Попытка проверки через строку ТЧ приводит к ошибке

FAQ

Можно ли проверить наличие реквизита у документа в запросе?

Напрямую — нет: если реквизита нет в конфигурации, запрос с таким полем не скомпилируется. Проверку делают до построения текста запроса через метаданные, а затем формируют текст запроса динамически, включая только существующие поля.

Как проверить реквизит, если имя хранится в переменной?

Метод Найти() принимает строку, поэтому переменную можно передать напрямую: Реквизиты.Найти(ИмяРеквизита). Для чтения значения после проверки используйте обращение через квадратные скобки: ДокументОбъект[ИмяРеквизита].

Работает ли проверка для дополнительных реквизитов (БСП)?

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

Чем отличается проверка у объекта и у данных формы?

У объекта документа проверяют через Метаданные(). На управляемой форме реквизиты формы — это отдельный состав; там о наличии элемента данных судят через коллекцию реквизитов формы или поиск элемента формы по имени, метаданные документа для этого не используются.

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

Технически работает, но это худший вариант: исключение дороже по времени, затрудняет отладку и скрывает другие ошибки внутри блока. Применяйте его только когда состав объекта заранее неизвестен и метаданные недоступны, например при разборе внешних COM-объектов.

Как проверить наличие реквизита сразу у нескольких видов документов?

Пройдите циклом по Метаданные.Документы и для каждого вида вызовите Реквизиты.Найти(). Результат удобно сложить в соответствие «имя вида — признак наличия» и использовать его в дальнейшей логике обработки.

Итог

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

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