Как программно определить, что реквизит относится к Плану видов характеристик в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке универсальных механизмов в 1С (таких как групповое изменение реквизитов, поиск ссылок или динамическое построение запросов) мы часто сталкиваемся с необходимостью анализировать метаданные конфигурации. Одна из самых нетривиальных задач в этой области — определить, является ли конкретный реквизит объекта (справочника, документа или табличной части) характеристикой, привязанной к определенному Плану видов характеристик (ПВХ).

Сложность заключается в том, что если в конфигураторе реквизиту явно задан тип Характеристика.ИмяПВХ, то при программном обращении к метаданным система возвращает обычный объект ОписаниеТипов, содержащий массив всех разрешенных в этом ПВХ типов. Напрямую узнать имя самого ПВХ-контейнера через свойство Реквизит.Тип не удается. В этой статье мы подробно разберем, почему так происходит, и рассмотрим три надежных способа решения этой проблемы, а также исправим распространенное заблуждение по работе с характеристиками в запросах.

Почему возникает сложность: особенности хранения типов характеристик

Когда мы настраиваем реквизит в конфигураторе и выбираем тип из ветки «Характеристика», платформа 1С на самом деле выполняет автоматическое развертывание этого типа. В режиме «Предприятие» свойство метаданных реквизита Тип хранит составной тип данных, включающий все типы, которые отмечены в самом Плане видов характеристик. Для платформы это выглядит как обычное ОписаниеТипов, и она «забывает», что этот тип был унаследован от конкретного ПВХ.

Попытка вызвать метод Типы() у описания типов реквизита вернет массив конкретных типов (например, Число, Строка, СправочникСсылка.Номенклатура), но не ссылку на сам объект метаданных ПВХ. Давайте разберем по шагам, какими способами мы можем обойти это ограничение и получить точную информацию о связи реквизита с Планом видов характеристик.

Способ 1. Проверка через свойство «Связь по типу» (СвязьПоТипу) — самый надежный метод

В реальных конфигурациях тип «Характеристика» практически никогда не используется изолированно. Характеристика всегда логически связана с другим реквизитом, который определяет конкретное свойство или вид субконто (то есть хранит ссылку на элемент ПВХ). Например, в табличной части дополнительных реквизитов всегда есть реквизит Свойство (ссылка на ПВХ) и реквизит Значение (тип Характеристика).

Связь между этими реквизитами жестко настраивается в конфигураторе с помощью свойства Связь по типу. Мы можем использовать эту связь для точного определения ПВХ. Давайте проанализируем, как это сделать программно:

Рассмотрим алгоритм решения:

  1. Получаем объект метаданных проверяемого реквизита.
  2. Проверяем наличие заполненного свойства СвязьПоТипу.
  3. Если связь существует, находим связанный реквизит-владелец.
  4. Анализируем тип реквизита-владельца: он должен указывать на План видов характеристик.

Реализуем этот алгоритм на практике с помощью следующего кода:


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

Этот способ является наиболее надежным, поскольку он исключает ложные срабатывания. Он опирается на строгую логическую связь, заложенную разработчиком при проектировании конфигурации.

Способ 2. Сравнение Описания Типов через операцию вычитания

Если по какой-то причине свойство «Связь по типу» не заполнено (например, реквизиту просто вручную назначили составной тип, совпадающий с типами ПВХ), мы можем пойти путем сравнения описаний типов. Мы сравним ОписаниеТипов нашего реквизита и ОписаниеТипов самого Плана видов характеристик.

Вместо медленного перебора массивов типов в циклах, мы применим эффективную возможность конструктора ОписаниеТипов — вычитание типов. Если при вычитании типов ПВХ из типов реквизита у нас остается пустой набор типов, это означает, что типы нашего реквизита полностью входят в состав ПВХ (являются его подмножеством).

Давайте посмотрим на пример реализации такой проверки:


Функция ПроверитьВхождениеТипаВПВХ(ТипРеквизита, ИмяПВХ) Экспорт
    
    // Получаем описание типов из самого Плана видов характеристик
    ТипПВХ = Метаданные.ПланыВидовХарактеристик[ИмяПВХ].Тип;
    
    // Создаем новое описание типов, исключая из проверяемого типа все типы, разрешенные в ПВХ
    ОстатокТипов = Новый ОписаниеТипов(ТипРеквизита, , ТипПВХ);
    
    // Если в остатке не осталось типов, значит наш реквизит полностью совместим со структурой ПВХ
    Если ОстатокТипов.Типы().Количество() = 0 Тогда
        Возврат Истина;
    КонецЕсли;
    
    Возврат Ложь;
КонецФункции

Важное примечание: Этот метод дает высокую степень вероятности, но не стопроцентную гарантию. Если в конфигурации есть два разных ПВХ с абсолютно одинаковым набором типов, этот алгоритм может сопоставить реквизит не с тем ПВХ. Тем не менее, для большинства практических задач этого решения более чем достаточно.

Способ 3. Использование коллекции «Характеристики» объектов метаданных

Начиная с версии платформы 8.2.11, разработчики 1С получили мощный встроенный инструмент — коллекцию Характеристики у прикладных объектов метаданных (справочников, документов и т.д.). Этот механизм изначально предназначен для интеграции с Системой компоновки данных (СКД), но мы можем использовать его в своих целях.

Если мы разрабатываем универсальный поиск или обработку, нам не нужно гадать и перебирать реквизиты вручную. Мы можем напрямую спросить у метаданных объекта, какие характеристики для него зарегистрированы. Проанализируем пример кода:


Процедура ВывестиХарактеристикиСправочника(ИмяСправочника)
    
    СправочникМетаданные = Метаданные.Справочники[ИмяСправочника];
    ОписанияХарактеристик = СправочникМетаданные.Характеристики;
    
    Для Каждого Описание Из ОписанияХарактеристик Цикл
        
        СтрокаСообщения = "Найдена характеристика для объекта " + ИмяСправочника + ":";
        СтрокаСообщения = СтрокаСообщения + " ПВХ-источник: " + Описание.Источник.ПолноеИмя();
        СтрокаСообщения = СтрокаСообщения + "; Таблица хранения: " + Описание.ТаблицаВыбора.ПолноеИмя();
        СтрокаСообщения = СтрокаСообщения + "; Поле ключа: " + Описание.ВидХарактеристики;
        СтрокаСообщения = СтрокаСообщения + "; Поле значения: " + Описание.ЗначениеХарактеристики;
        
        Сообщить(СтрокаСообщения);
        
    КонецЦикла;
    
КонецПроцедуры

Этот подход позволяет автоматически определять, в каких таблицах хранятся значения характеристик для конкретного справочника или документа (например, в табличной части или в регистре сведений), какой ПВХ за них отвечает и по каким полям делать соединение. Это самый передовой и архитектурно правильный способ написания универсального кода.

Разбор типичной ошибки: Использование суффикса «.Value» (или «.Значение») в запросах

В процессе обсуждения подобных задач на форумах программисты часто совершают серьезную техническую ошибку при динамическом формировании текста запроса. Они считают, что если реквизит имеет тип характеристики ПВХ, то в запросе к нему нужно обращаться через специальное свойство, дописывая к имени поля суффикс .Value (или .Значение), например:


// ОШИБОЧНЫЙ ВАРИАНТ ФОРМИРОВАНИЯ ЗАПРОСА ДЛЯ 1С
ТекстЗапроса = "ВЫБРАТЬ Таблица.Ссылка ИЗ Документ.РасходныйОрдер КАК Таблица ГДЕ Таблица.ДополнительныйРеквизит.Value = &Значение";

Выясним причину этого заблуждения и разберем, как обстоят дела на самом деле:


ТекстЗапроса = "ВЫБРАТЬ Таблица.Ссылка ИЗ Документ.РасходныйОрдер КАК Таблица ГДЕ Таблица.ДополнительныйРеквизит = &Значение";

Откуда же берется суффикс Value? Эта конструкция используется исключительно при работе с внешними интерфейсами платформы, например, при обращении к базе данных через протокол 1С:OData (REST API). В OData составные типы действительно расширяются свойствами вида Реквизит_Type и Реквизит_Value. Если вы пишете код внутри самой платформы 1С, забудьте про эти суффиксы — они гарантированно приведут к синтаксической ошибке выполнения запроса.

Мы подробно рассмотрели особенности работы с типами Планов видов характеристик в метаданных 1С. Для простых и надежных проверок всегда используйте анализ свойства СвязьПоТипу. Для создания гибких и универсальных отчетов или обработок используйте коллекцию Характеристики. Эти методы позволят вашему коду оставаться стабильным и не зависеть от изменений в структуре конфигурации.

← На главную