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

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

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

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

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

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

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

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

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

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

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

Иначе

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

КонецЕсли;

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

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

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

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

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

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

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

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

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

МетаТЧ = Мета.ТабличныеЧасти.Найти("Товары");

Если МетаТЧ <> Неопределено

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

// безопасное обращение к строке табличной части

КонецЕсли;

Готовая функция в типовых конфигурациях (БСП)

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

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

Договор = ДокументСсылка.Договор;

КонецЕсли;

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

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

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

Работает в любой конфигурации без зависимостей; требует аккуратности с табличными частями и стандартными реквизитами.

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

Удобно в типовых на БСП, читаемый код; не подходит для самописных конфигураций без библиотеки.

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

Позволяет обойтись без проверки, но замедляет код и маскирует другие ошибки; применять только как крайний вариант.

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

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

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

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

Возврат; // такого вида документа нет в конфигурации

КонецЕсли;

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

ТекстЗапроса = ТекстЗапроса + ", Док.Склад";

КонецЕсли;

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

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

Способ через Попытку — когда он допустим и чем опасен

Иногда встречается код, где обращение к реквизиту обёрнуто в конструкцию Попытка...Исключение: если поля нет, срабатывает исключение и присваивается значение по умолчанию. Формально это работает, но у подхода есть серьёзные недостатки.

Попытка

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

Исключение

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

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

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

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

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

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

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

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

Вызов Реквизиты.Найти() в отладчике
Норма

Возвращается объект описания реквизита с нужным именем

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

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

Запуск на документе без реквизита
Норма

Код отрабатывает ветку «Иначе» без исключений

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

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

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

Обе проверки (ТЧ и реквизит) проходят последовательно

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

Ошибка «Значение не является значением объектного типа»

Для массовых обработок дополнительно замерьте время выполнения: если проверка метаданных стоит внутри цикла по документам одного вида, её разумно вынести за цикл и выполнить один раз — результат для всех документов одного вида одинаков.

Итог

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

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

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

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

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

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

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

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

Да. У ссылки доступен метод Метаданные(), потому что состав реквизитов определяется видом документа, а не конкретным экземпляром. Это предпочтительный вариант при массовой обработке — он не тратит память на чтение объекта.

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

Отсутствие реквизита — свойство конфигурации: поле не описано в метаданных вида документа. Незаполненный реквизит существует, но содержит пустое значение. Первое проверяется через метаданные, второе — через ЗначениеЗаполнено() или сравнение с пустым значением соответствующего типа.

Работает ли проверка в управляемых формах на клиенте?

Метод Метаданные() у объектов данных доступен только на сервере. На клиенте в тонком или веб-клиенте проверку выполняют в серверной процедуре формы или общего модуля с признаком «Сервер», а результат возвращают в клиентский код.

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

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