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