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