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