Как программно вывести состав подсистемы и всех её вложенных элементов в 1С

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

При разработке сложных инструментов администрирования, генераторов документации или систем управления правами доступа часто возникает необходимость программно получить полный список объектов, входящих в ту или иную подсистему. В 1С:Предприятие подсистемы имеют иерархическую структуру, что требует особого подхода к обходу их состава. В этой статье мы подробно разберем, как реализовать рекурсивный обход метаданных, определить тип каждого объекта и корректно вывести результат в дерево значений.

Особенности структуры подсистем в метаданных

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

Важно учитывать разницу между версиями платформы. В старых версиях (8.1) и некоторых режимах совместимости работа с этими коллекциями могла отличаться, но базовый принцип остается неизменным: подсистема может содержать как объекты конфигурации, так и другие (подчиненные) подсистемы. Именно это диктует нам использование рекурсивного алгоритма для получения полной картины.

Способ 1: Рекурсивный обход через коллекцию «Состав»

Это наиболее прямой метод. Мы берем конкретную подсистему, читаем её состав, а затем переходим к её дочерним подсистемам. Рассмотрим по шагам, как это реализовать. Сначала подготовим дерево значений для вывода данных.


// Подготовка дерева значений для вывода
ТаблицаВыводаДерева = Новый ДеревоЗначений;
ТаблицаВыводаДерева.Колонки.Добавить("ИмяОбъекта");
ТаблицаВыводаДерева.Колонки.Добавить("ТипОбъекта");
ТаблицаВыводаДерева.Колонки.Добавить("СсылкаНаМетаданные");

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


Процедура ПолучитьСоставПодсистемыРекурсивно(ТекущаяПодсистема, СтрокаРодитель)
    
    // 1. Обрабатываем объекты, входящие непосредственно в текущую подсистему
    Для Каждого ОбъектМетаданных Из ТекущаяПодсистема.Состав Цикл
        НоваяСтрока = СтрокаРодитель.Строки.Добавить();
        НоваяСтрока.ИмяОбъекта = ОбъектМетаданных.Имя;
        НоваяСтрока.СсылкаНаМетаданные = ОбъектМетаданных;
        
        // Определяем тип объекта для наглядности
        НоваяСтрока.ТипОбъекта = ПолучитьТипОбъектаМетаданных(ОбъектМетаданных);
    КонецЦикла;
    
    // 2. Рекурсивно заходим во вложенные подсистемы
    Для Каждого ВложеннаяПодсистема Из ТекущаяПодсистема.Подсистемы Цикл
        СтрокаПодсистемы = СтрокаРодитель.Строки.Добавить();
        СтрокаПодсистемы.ИмяОбъекта = "Подсистема: " + ВложеннаяПодсистема.Имя;
        
        // Вызываем эту же процедуру для вложенного узла
        ПолучитьСоставПодсистемыРекурсивно(ВложеннаяПодсистема, СтрокаПодсистемы);
    КонецЦикла;
    
КонецПроцедуры

Как определить тип объекта метаданных

В процессе обхода коллекции Состав мы получаем объекты типа ОбъектМетаданных. Нам нужно понять, является ли этот объект справочником, документом или, например, обработкой. Посмотрим на пример функции, которая помогает идентифицировать тип:


Функция ПолучитьТипОбъектаМетаданных(ОбъектМетаданных)
    
    // Самый надежный способ в 8.2/8.3 - использование ПолноеИмя()
    ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
    ПерваяТочка = Найти(ПолноеИмя, ".");
    Если ПерваяТочка > 0 Тогда
        Возврат Лев(ПолноеИмя, ПерваяТочка - 1);
    КонецЕсли;
    
    Возврат "Не определено";
КонецФункции

Рассмотрим подробнее альтернативный вариант, который упоминался на форумах. Можно использовать метод Содержит() у коллекций метаданных. Однако этот метод менее производителен, если вам нужно проверять объект на соответствие всем возможным типам (справочники, документы, отчеты и т.д.), так как придется перебирать все коллекции Метаданные.Справочники.Содержит(Объект), Метаданные.Документы.Содержит(Объект) и так далее.

Способ 2: Обратный поиск по всем метаданным

Иногда задача стоит иначе: нужно вывести дерево всех метаданных конфигурации, распределенных по подсистемам. В этом случае мы можем пойти «от противного» — перебирать все объекты конфигурации и смотреть, в какие подсистемы они входят. Проанализируем ситуацию: этот метод хорош, когда один объект может входить в несколько подсистем, и нам важно это отразить.


Процедура ЗаполнитьДеревоПоВсемДокументам()
    
    Для Каждого Док Из Метаданные.Документы Цикл
        Для Каждого ПодсистемаОбъекта Из Док.Подсистемы Цикл
            // Ищем строку подсистемы в нашем дереве
            СтрокаДерева = НайтиСтрокуПодсистемыВДереве(ПодсистемаОбъекта);
            Если СтрокаДерева <> Неопределено Тогда
                НовСтрока = СтрокаДерева.Строки.Добавить();
                НовСтрока.ИмяОбъекта = Док.Имя;
                НовСтрока.ТипОбъекта = "Документ";
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
    
КонецПроцедуры

Важный нюанс: метод Найти() в дереве значений по колонке, содержащей объект метаданных, может работать нестабильно в некоторых версиях платформы, если сравнивать объекты напрямую. Рекомендуется использовать уникальный идентификатор или полное имя объекта метаданных для поиска узла в дереве.

Решение проблем с отображением вложенных структур

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

  1. Создаем корень дерева (например, «Конфигурация» или имя главной подсистемы).
  2. Запускаем рекурсию только по Метаданные.Подсистемы, создавая пустые ветки в дереве.
  3. Вторым проходом или в той же рекурсии заполняем ветки объектами из свойства Состав.
  4. Используем метод СоздатьКолонки() для элемента формы ТаблицаЦикла, чтобы визуализировать результат.

Проанализируем типичную ошибку начинающих разработчиков: попытку получить состав через Метаданные.Подсистемы.ИмяПодсистемы.Подсистемы.Состав. Помните, что Состав — это коллекция, которую нужно обходить циклом Для Каждого, а не пытаться получить её элементы по индексу без проверки их наличия.

Заключение

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

← На главную