В процессе разработки на платформе 1С:Предприятие часто возникают задачи по написанию универсальных алгоритмов. Например, нам может потребоваться программно перебрать список различных документов и заполнить в них поле Организация или Ответственный. Однако не во всех типах объектов эти реквизиты могут существовать. Попытка обращения к отсутствующему реквизиту приведет к критической ошибке выполнения кода. Чтобы этого избежать, мы должны научиться определять состав метаданных объекта "на лету", понимая, как устроена структура хранения БД в виде дерева конфигурации.
В этой статье мы подробно разберем, как проанализировать структуру объекта, изучив программную работу с метаданными в 1С 8.3, выясним разницу между обычными и стандартными реквизитами, а также рассмотрим современные методы решения этой задачи с использованием Библиотеки стандартных подсистем (БСП).
Самый распространенный и надежный способ — это обращение к свойству Метаданные() конкретного объекта или ссылки. Этот метод позволяет получить доступ к описанию структуры объекта в конфигурации (по схожему принципу работает MCP сервер для получения списка и описания метаданных 1С, см. MCP-сервер для получения описания метаданных и проверки кода). Разберем ситуацию: у нас есть переменная СсылкаНаОбъект, и мы хотим узнать, есть ли у неё реквизит Склад.
Проанализируем следующий фрагмент кода:
// Получаем объект метаданных документа или справочника
МетаданныеОбъекта = СсылкаНаОбъект.Метаданные();
// Ищем реквизит в коллекции Реквизиты
Если МетаданныеОбъекта.Реквизиты.Найти("Склад") <> Неопределено Тогда
// Реквизит найден, можем безопасно к нему обращаться
ЗначениеСклада = СсылкаНаОбъект.Склад;
Иначе
// Реквизит отсутствует в данной конфигурации для этого типа объекта
Сообщить("У объекта " + МетаданныеОбъекта.Имя + " нет реквизита Склад");
КонецЕсли;
Важно помнить, что метод Найти() возвращает объект описания реквизита, если он существует, или значение Неопределено, если поиск не дал результатов. Это самый быстрый способ проверки для "собственных" реквизитов объекта, созданных разработчиком вручную.
Зачастую разработчики забывают, что такие поля, как Дата, Номер, Код, Наименование, Проведен или ПометкаУдаления, не входят в коллекцию Реквизиты. Они относятся к стандартным реквизитам, которые система обрабатывает иначе, чем обычные поля. Если вы будете искать Дата в коллекции Реквизиты, система вернет Неопределено, даже если это документ.
Рассмотрим по шагам, как проверить наличие стандартного реквизита:
МетаданныеОбъекта = СсылкаНаОбъект.Метаданные();
// Правильный поиск стандартного реквизита
Если МетаданныеОбъекта.СтандартныеРеквизиты.Найти("Дата") <> Неопределено Тогда
// Это объект, имеющий дату (например, документ)
Сообщить("Объект содержит стандартный реквизит Дата");
КонецЕсли;
Для создания по-настоящему универсальной функции проверки нам необходимо объединить поиск по обеим коллекциям. Также стоит учитывать Общие реквизиты, которые могут быть включены для данного типа документа.
Давайте создадим программный механизм, который проверит наличие поля во всех возможных коллекциях метаданных. Это наиболее безопасный подход для кросс-платформенной разработки внутри системы 1С. Для визуального контроля состава объектов и их полей удобно использовать универсальный отчет по метаданным. Для этой задачи есть универсальный отчет по метаданным для разработчика.
Проанализируем пример такой функции:
Функция ЕстьРеквизитОбъекта(ИмяРеквизита, СсылкаИлиОбъект) Экспорт
// Получаем метаданные, поддерживается и ссылка, и сам объект
МД = СсылкаИлиОбъект.Метаданные();
// 1. Проверяем обычные реквизиты
Если МД.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
Возврат Истина;
КонецЕсли;
// 2. Проверяем стандартные реквизиты
Если МД.СтандартныеРеквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
Возврат Истина;
КонецЕсли;
// 3. Проверяем табличные части (иногда требуется проверить и их наличие)
Если МД.ТабличныеЧасти.Найти(ИмяРеквизита) <> Неопределено Тогда
Возврат Истина;
КонецЕсли;
Возврат Ложь;
КонецФункции
При работе с табличными частями полезно также использовать автоматический анализ метаданных и граф зависимостей реквизитов, чтобы понимать взаимосвязи объектов в базе.
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), что справедливо для большинства современных решений (БП 3.0, УТ 11, ERP, ЗУП 3), то изобретать велосипед не нужно. В составе БСП есть общий модуль ОбщегоНазначения, который содержит высокооптимизированные функции для этих целей. Подробнее о возможностях библиотеки можно узнать, изучив справочник по методам БСП с примерами использования.
Разберем, как использовать готовый метод:
// Использование функции из состава БСП
ИмяРеквизита = "Организация";
Если ОбщегоНазначения.ЕстьРеквизитОбъекта(ИмяРеквизита, СсылкаНаДокумент.Метаданные()) Тогда
// Реквизит точно существует
Значение = СсылкаНаДокумент[ИмяРеквизита];
КонецЕсли;
Важное замечание: Функция ОбщегоНазначения.ЕстьРеквизитОбъекта в качестве второго параметра ожидает именно объект метаданных, а не саму ссылку. Это позволяет функции работать быстрее, не обращаясь к базе данных лишний раз.
В современных конфигурациях 1С активно используются общие реквизиты (например, ОбластьДанныхОсновныеДанные в облачных решениях или универсальные разделители). Они не входят ни в Реквизиты, ни в СтандартныеРеквизиты конкретного документа в дереве метаданных напрямую, так как они определены на уровне всей конфигурации.
Выясним причину, почему поиск может не сработать: общий реквизит отображается в метаданных объекта только в том случае, если он используется этим объектом (свойство "Использование" установлено в значение "Использовать"). Для проверки таких полей следует обращаться к коллекции Метаданные.ОбщиеРеквизиты и анализировать состав их свойств Состав.
Рассмотрим ситуацию, когда проверка наличия реквизита выполняется внутри цикла по тысячам документов. Обращение к Метаданные() — операция относительно легкая, но многократный поиск строки в коллекции может замедлить выполнение кода, поэтому важно проводить кеширование проверок метаданных при массовой обработке.
Совет: Если вы обрабатываете массив объектов одного типа, выполните проверку один раз перед началом цикла, сохраните результат в булеву переменную и используйте её в условии внутри цикла.
// Плохой пример: проверка на каждой итерации
Для Каждого Стр Из МассивДокументов Цикл
Если Стр.Метаданные().Реквизиты.Найти("Сумма") <> Неопределено Тогда
// ... действия
КонецЕсли;
КонецЦикла;
// Хороший пример: предварительная проверка
Если МассивДокументов.Количество() > 0 Тогда
ЕстьРеквизитСумма = МассивДокументов[0].Метаданные().Реквизиты.Найти("Сумма") <> Неопределено;
Для Каждого Стр Из МассивДокументов Цикл
Если ЕстьРеквизитСумма Тогда
// ... действия
КонецЕсли;
КонецЦикла;
КонецЕсли;
Подводя итог, можно сказать, что наиболее универсальным и современным способом является использование функций БСП, а при их отсутствии — последовательный поиск в коллекциях Реквизиты и СтандартныеРеквизиты через объект Метаданные. При написании сложного кода всегда полезно проводить анализ конфигураций и расширений на наличие ошибок, чтобы избежать проблем при обращении к несуществующим методам или полям. Всегда учитывайте контекст использования и не забывайте о различиях между типами реквизитов в платформе 1С.