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