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

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

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

Как проверить наличие реквизита в документе 1С: метод ЕстьРеквизитОбъекта, работа через метаданные, примеры кода для шапки и табличных частей, типичные ошибки.

При разработке универсальных обработок, обменов данными и расширений часто возникает задача: код должен работать с разными видами документов, у которых набор реквизитов отличается. Прямое обращение вида Документ.Комментарий вызовет ошибку «Поле объекта не обнаружено», если такого реквизита в конкретном виде документа нет. Поэтому перед обращением к реквизиту его наличие нужно проверить.

В платформе 1С:Предприятие 8.3 есть два основных подхода: штатный метод ЕстьРеквизитОбъекта() из общего модуля «ОбщегоНазначения» в типовых конфигурациях на БСП и универсальная проверка через объект метаданных. Оба способа рабочие, но у них разные условия применения и ограничения.

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

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

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

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

Третий случай — программное формирование запросов и СКД, где нужно понять, можно ли включать поле в текст запроса. Там проверка выполняется на уровне метаданных до построения запроса.

Проверка через метаданные: универсальный способ

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

МетаданныеДокумента = ДокументСсылка.Метаданные();

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

// реквизит есть, можно обращаться к нему

КонецЕсли;

Обратите внимание: проверка выполняется по метаданным, а не по самому объекту. У объекта документа нет коллекции «Реквизиты» — попытка написать ДокументОбъект.Реквизиты.Найти() — частая ошибка новичков, она приводит к исключению. Метаданные можно получить из ссылки (Ссылка.Метаданные()), из объекта (Объект.Метаданные()) или напрямую из дерева конфигурации (Метаданные.Документы.РеализацияТоваровУслуг).

Если нужно проверить не только штатные, но и стандартные реквизиты (например, «Организация» может быть как штатным, так и отсутствовать, а «Дата» есть всегда), стандартные реквизиты проверяются через коллекцию СтандартныеРеквизиты. Для общих реквизитов, назначенных документу, потребуется отдельный анализ свойства ОбщиеРеквизиты метаданного или использование метода БСП, который это учитывает.

bsl
Функция ЕстьРеквизит(ИмяРеквизита, МетаданныеОбъекта)
    Возврат МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено;
КонецФункции

Метод ЕстьРеквизитОбъекта в типовых конфигурациях

В конфигурациях на базе Библиотеки стандартных подсистем (УТ 11, ERP, БП 3.0, ЗУП и других) есть готовая функция ОбщегоНазначения.ЕстьРеквизитОбъекта(ИмяРеквизита, МетаданныеОбъекта). Она возвращает Истина или Ложь и корректно учитывает не только штатные реквизиты, но и общие реквизиты, назначенные объекту.

Если ОбщегоНазначения.ЕстьРеквизитОбъекта("Подразделение", ДокументСсылка.Метаданные()) Тогда

Подразделение = ДокументСсылка.Подразделение;

КонецЕсли;

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

Важно не путать эту функцию с попыткой проверить реквизит «на лету» через попытку обращения в конструкции Попытка...Исключение. Такой приём работает, но это антипаттерн: исключения медленнее обычной проверки и затрудняют отладку, потому что перехватывают любые ошибки, а не только отсутствие реквизита.

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

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

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

ОбщегоНазначения.ЕстьРеквизитОбъекта

Удобен в типовых на БСП, учитывает общие реквизиты. Не работает без БСП.

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

Не требует знания метаданных, но медленнее, скрывает другие ошибки и считается плохой практикой.

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

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

МетаданныеДокумента = ДокументСсылка.Метаданные();

ТабличнаяЧасть = МетаданныеДокумента.ТабличныеЧасти.Найти("Товары");

Если ТабличнаяЧасть <> Неопределено

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

// можно безопасно обращаться к Товары.СтавкаНДС

КонецЕсли;

Если пропустить проверку существования самой табличной части и сразу искать её реквизит, обращение ТабличныеЧасти.Найти("Товары").Реквизиты при отсутствующей части вернёт ошибку «Обращение к реквизиту объекта Неопределено». Вложенная проверка через два условия решает эту проблему.

Для динамических сценариев, когда имя табличной части тоже приходит параметром, удобно обращаться по индексу-имени: МетаданныеДокумента.ТабличныеЧасти[ИмяТЧ] — но только после проверки через Найти(), иначе обращение по несуществующему имени вызовет исключение.

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

Проверка в запросах и при обращении к данным

Если реквизит нужен в тексте запроса, проверку выполняют до формирования текста — по метаданным, как описано выше. В зависимости от результата в текст запроса подставляют либо поле, либо константу-заглушку:

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

ТекстПоля = "Док.Склад";

Иначе

ТекстПоля = "ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)";

КонецЕсли;

Такой приём позволяет строить один шаблон запроса для нескольких видов документов. Альтернатива — конструкция ВЫБОР КОГДА в самом запросе, но она не спасает от отсутствующего поля: компилятор запроса проверяет поля на этапе выполнения, и обращение к несуществующему реквизиту вызовет ошибку независимо от логики ВЫБОР.

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

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

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

Пример: перед циклом обработки определить вид документа, один раз проверить наличие реквизитов «Склад», «Подразделение», «Комментарий», сохранить результат и дальше в цикле использовать готовые флаги. Это упрощает код и убирает повторные обращения к метаданным.

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

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

Проверка результата и отладка

Чтобы убедиться, что проверка работает правильно, протестируйте её на двух видах документов: с искомым реквизитом и без него. Для быстрой ручной проверки удобно использовать консоль кода или отладчик: вывести МетаданныеДокумента.Реквизиты.Найти("ИмяРеквизита") и посмотреть результат.

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

При работе с внешними обработками, запускаемыми в разных базах, полезно логировать результат проверки: записать в журнал регистрации, какие реквизиты найдены, а какие нет. Это упрощает диагностику, когда обработка в одной базе работает, а в другой — нет.

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

Как проверить корректность работы кода

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

Найти() вернул описание реквизита, обращение к данным прошло без ошибок

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

Возвращено Неопределено — проверьте точность имени реквизита (регистр не важен, но опечатки критичны)

Проверка на документе без реквизита
Норма

Найти() вернул Неопределено, код прошёл по ветке Иначе без исключений

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

Возникло исключение — значит проверка стоит после обращения к реквизиту, а не до

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

Обе проверки (ТЧ и реквизит) пройдены, данные прочитаны

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

Ошибка обращения к Неопределено — не проверено существование самой табличной части

Часто задаваемые вопросы

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

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

Учитывает ли Реквизиты.Найти() стандартные реквизиты вроде «Дата» или «Номер»?

Нет, стандартные реквизиты находятся в отдельной коллекции СтандартныеРеквизиты объекта метаданных. Однако стандартные реквизиты присутствуют у всех документов, поэтому проверять их наличие обычно не требуется.

Работает ли проверка для реквизитов, добавленных расширением конфигурации?

Да, если расширение подключено в текущем сеансе, его реквизиты входят в состав метаданных и обнаруживаются методом Найти(). Если расширение отключено, проверка вернёт Неопределено, и код корректно уйдёт в ветку отсутствия реквизита.

Чем проверка через метаданные лучше обращения к реквизиту в Попытке?

Проверка через метаданные быстрее, не генерирует исключений и не перехватывает посторонние ошибки. Попытка/Исключение скрывает любые сбои внутри блока, что затрудняет отладку, и в тонком клиенте работает медленнее обычной проверки условия.

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

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

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

Да, у метаданных регистров есть коллекции Измерения, Ресурсы и Реквизиты. Проверка выполняется по каждой коллекции отдельно, например: Метаданные.РегистрыНакопления.ТоварыНаСкладах.Измерения.Найти("Склад").

Итог

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

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