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