Обход метаданных конфигурации 1С 8.3: от простого цикла до рекурсии

· 9 мин

Содержание

Перебрать объекты метаданных в 1С проще всего циклом Для Каждого ОбъектМД Из Метаданные.Справочники Цикл в серверной процедуре. Это первый способ из пяти. Второй — точечный поиск через Метаданные.НайтиПоПолномуИмени() и НайтиПоТипу(). Третий — рекурсивный обход подсистем, табличных частей и реквизитов. Четвёртый — ПолучитьСтруктуруХраненияБазыДанных(), когда нужны таблицы СУБД. Пятый — без кода: отчёт по конфигурации или выгрузка в файлы из Конфигуратора.

Цикл по коллекции берут для отчётов и проверок «по всем объектам». Поиск по имени — когда объект известен заранее. Рекурсия нужна для вложенных структур. Структура хранения — для администратора, который ищет, какой объект стоит за таблицей _Reference123.

Что лежит в свойстве Метаданные и где запускать код

Глобальное свойство Метаданные возвращает объект ОбъектМетаданныхКонфигурация. У него есть коллекции по видам объектов. Каждая коллекция перебирается циклом и содержит объекты типа ОбъектМетаданных.

Коллекции, которые нужны чаще всего:

  • 📁 Справочники, Документы, Перечисления, Константы;
  • 📊 РегистрыСведений, РегистрыНакопления, РегистрыБухгалтерии, РегистрыРасчета;
  • 🗂️ ПланыВидовХарактеристик, ПланыСчетов, ПланыВидовРасчета, ПланыОбмена;
  • 🔄 БизнесПроцессы, Задачи, ЖурналыДокументов;
  • 🧩 Обработки, Отчеты, ОбщиеМодули, Роли, Подсистемы, ОбщиеРеквизиты.

У любого объекта метаданных есть базовые свойства: Имя, Синоним, Комментарий и метод ПолноеИмя(). Полное имя выглядит так: Справочник.Номенклатура, РегистрНакопления.ТоварыНаСкладах.

Свойство Метаданные недоступно в тонком клиенте и веб-клиенте. Код в процедуре с директивой &НаКлиенте упадёт с ошибкой Переменная не определена (Метаданные). Пишите обход в процедуре &НаСервере, &НаСервереБезКонтекста или в серверном общем модуле.

Для разовых экспериментов удобна внешняя обработка: форма с кнопкой и серверный обработчик. Запускается через Файл → Открыть в режиме «Предприятие», если у пользователя есть право на интерактивное открытие внешних обработок.

Способ 1. Перебор коллекций Метаданные циклом

Базовый пример — вывести все справочники с синонимами:

&НаСервереБезКонтекста
Процедура ВывестиСправочникиНаСервере()

    Для Каждого ОбъектМД Из Метаданные.Справочники Цикл
        Сообщить(ОбъектМД.ПолноеИмя() + " — " + ОбъектМД.Синоним);
    КонецЦикла;

КонецПроцедуры

Чтобы обойти сразу несколько видов объектов, соберите коллекции в массив. Обращаться к Метаданные по строковому имени коллекции не нужно, достаточно перечислить их явно:

&НаСервереБезКонтекста
Функция СписокОбъектовКонфигурации()

    Коллекции = Новый Массив;
    Коллекции.Добавить(Метаданные.Справочники);
    Коллекции.Добавить(Метаданные.Документы);
    Коллекции.Добавить(Метаданные.РегистрыСведений);
    Коллекции.Добавить(Метаданные.РегистрыНакопления);

    Результат = Новый ТаблицаЗначений;
    Результат.Колонки.Добавить("Вид");
    Результат.Колонки.Добавить("Имя");
    Результат.Колонки.Добавить("Синоним");

    Для Каждого Коллекция Из Коллекции Цикл
        Для Каждого ОбъектМД Из Коллекция Цикл
            Строка = Результат.Добавить();
            Строка.Вид = СтрРазделить(ОбъектМД.ПолноеИмя(), ".")[0];
            Строка.Имя = ОбъектМД.Имя;
            Строка.Синоним = ОбъектМД.Синоним;
        КонецЦикла;
    КонецЦикла;

    Возврат Результат;

КонецФункции

Вид объекта берём из первой части полного имени. ТипЗнч(ОбъектМД) здесь не поможет: для любого объекта он вернёт один и тот же тип ОбъектМетаданных.

Что перебирать внутри объекта

У каждого вида свой набор подчинённых коллекций:

Вид объектаПодчинённые коллекции
СправочникРеквизиты, ТабличныеЧасти, СтандартныеРеквизиты, Владельцы
ДокументРеквизиты, ТабличныеЧасти, СтандартныеРеквизиты, Движения
Регистр сведений, накопленияИзмерения, Ресурсы, Реквизиты
ПеречислениеЗначенияПеречисления
ПодсистемаПодсистемы, Состав

Движения документа — это коллекция регистров, по которым он делает записи. По ней легко собрать карту «документ → регистры».

Способ 2. Поиск объекта по имени и типу, доступ к менеджеру

Перебирать всю коллекцию, чтобы найти один объект, не нужно. Для этого есть два метода.

  1. Метаданные.НайтиПоПолномуИмени("Справочник.Номенклатура") — вернёт объект метаданных или Неопределено, если такого нет.
  2. Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)) — по типу ссылки находит её объект метаданных. Удобно в универсальном коде, который получает «какую-то ссылку».

Проверить, к какому виду относится объект, можно методом Содержит() у коллекции:

ОбъектМД = Метаданные.НайтиПоТипу(ТипЗнч(Ссылка));
Если ОбъектМД <> Неопределено И Метаданные.Документы.Содержит(ОбъектМД) Тогда
    // это документ
КонецЕсли;

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

От метаданных обычно переходят к данным. Глобальные коллекции менеджеров индексируются по имени:

МенеджерСправочника = Справочники[ОбъектМД.Имя];
Выборка = МенеджерСправочника.Выбрать();

В конфигурациях на БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) есть готовая функция ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя). Она сама разбирает вид по полному имени и возвращает нужный менеджер. В типовой конфигурации лучше пользоваться ею, чем писать свой разбор.

💡 В БСП есть ещё справочник ИдентификаторыОбъектовМетаданных. Он хранит ссылки на объекты конфигурации, их можно записывать в реквизиты и регистры. Сам объект метаданных в базу не записать.

Способ 3. Рекурсивный обход метаданных: подсистемы и реквизиты

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

Классический пример — дерево подсистем. Подсистема содержит вложенные подсистемы, глубина не ограничена:

&НаСервереБезКонтекста
Процедура ОбойтиПодсистемы(Подсистемы, Уровень, Результат)

    Для Каждого ПодсистемаМД Из Подсистемы Цикл
        Строка = Результат.Добавить();
        Строка.Уровень = Уровень;
        Строка.Имя = ПодсистемаМД.Имя;
        Строка.Объектов = ПодсистемаМД.Состав.Количество();

        ОбойтиПодсистемы(ПодсистемаМД.Подсистемы, Уровень + 1, Результат);
    КонецЦикла;

КонецПроцедуры

Вызов: создайте таблицу значений с колонками Уровень, Имя, Объектов и передайте Метаданные.Подсистемы, 1 и эту таблицу.

Обход реквизитов шапки и табличных частей

Второй частый случай — пройти по всем полям объекта, включая табличные части. Стандартные реквизиты (Код, Наименование, Дата, Номер) в коллекцию Реквизиты не входят, их перебирают отдельно через СтандартныеРеквизиты.

&НаСервереБезКонтекста
Процедура СобратьПоля(ОбъектМД, Результат)

    Для Каждого РеквизитМД Из ОбъектМД.Реквизиты Цикл
        Результат.Добавить(ОбъектМД.Имя + "." + РеквизитМД.Имя);
    КонецЦикла;

    Для Каждого ТабличнаяЧастьМД Из ОбъектМД.ТабличныеЧасти Цикл
        Для Каждого РеквизитМД Из ТабличнаяЧастьМД.Реквизиты Цикл
            Результат.Добавить(ОбъектМД.Имя + "." + ТабличнаяЧастьМД.Имя + "." + РеквизитМД.Имя);
        КонецЦикла;
    КонецЦикла;

КонецПроцедуры

Способ 4 и 5. Перебор без бизнес-логики: структура хранения и Конфигуратор

ПолучитьСтруктуруХраненияБазыДанных

Функция возвращает таблицу значений: объект метаданных, его назначение (основная таблица, табличная часть, итоги) и имя таблицы в СУБД, с полями и индексами. Первым параметром передают массив объектов или их полных имён, без параметра — получают всю базу.

Объекты = Новый Массив;
Объекты.Добавить("Справочник.Номенклатура");
Структура = ПолучитьСтруктуруХраненияБазыДанных(Объекты);

Для Каждого Строка Из Структура Цикл
    Сообщить(Строка.Метаданные + " | " + Строка.Назначение + " | " + Строка.ИмяТаблицыХранения);
КонецЦикла;

Так администратор понимает, что за таблица «распухла» в SQL или чей индекс тормозит.

Без кода

  • 📄 В Конфигураторе: Конфигурация → Отчет по конфигурации. Получите текстовый отчёт со всеми объектами и их составом.
  • 💾 Конфигурация → Выгрузить конфигурацию в файлы — дерево XML-файлов. По нему удобно искать grep-ом или в редакторе кода, сравнивать версии в Git.

Какой способ выбрать

СпособКогдаОграничения
Цикл по коллекцииОтчёт или проверка по всем объектам видаТолько сервер
НайтиПоПолномуИмени, НайтиПоТипуОбъект известен заранееНужно проверять Неопределено
РекурсияПодсистемы, табличные части, вложенные поляГлубину и коллекции задаёте сами
ПолучитьСтруктуруХраненияБазыДанныхСвязь объектов с таблицами СУБДДля всей базы — большой результат
Отчёт, выгрузка в файлыРазовый анализ без программированияНужен доступ к Конфигуратору
Что вам нужно получить?

Как ускорить обход большой конфигурации

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

  • 🐢 Запрос на каждый объект. Посчитать записи во всех справочниках сотней отдельных запросов долго. Соберите один текст через ОБЪЕДИНИТЬ ВСЕ или пакет запросов.
  • 🐢 Повторный обход. Если список нужен часто, вынесите функцию в общий модуль с повторным использованием возвращаемых значений «На время сеанса». Возвращайте ФиксированныйМассив или ФиксированнаяСтруктура: изменяемое значение из кэша можно случайно испортить.
  • 🐢 Поиск в цикле. Не ищите объект перебором, если знаете имя: НайтиПоПолномуИмени отработает сразу.
  • 🐢 Лишние уровни. Не спускайтесь в табличные части и стандартные реквизиты, если задаче хватает шапки.

Пример подсчёта записей одним запросом:

Части = Новый Массив;
Для Каждого СправочникМД Из Метаданные.Справочники Цикл
    Части.Добавить(СтрШаблон(
        "ВЫБРАТЬ ""%1"" КАК Объект, КОЛИЧЕСТВО(*) КАК Записей ИЗ %1 КАК Т",
        СправочникМД.ПолноеИмя()));
КонецЦикла;

Запрос = Новый Запрос(СтрСоединить(Части, Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС));
Таблица = Запрос.Выполнить().Выгрузить();

💡 В большой конфигурации запрос на сотни объединений долго компилируется. Режьте массив на порции по 50–100 объектов.

Типовые задачи: поиск по типу реквизита и проверка прав

Где используется справочник

Вопрос «в каких документах есть ссылка на Контрагентов» решается проверкой типа каждого реквизита. Свойство Тип у реквизита — это ОписаниеТипов, у него есть метод СодержитТип():

ИскомыйТип = Тип("СправочникСсылка.Контрагенты");
Найдено = Новый Массив;

Для Каждого ДокументМД Из Метаданные.Документы Цикл
    Для Каждого РеквизитМД Из ДокументМД.Реквизиты Цикл
        Если РеквизитМД.Тип.СодержитТип(ИскомыйТип) Тогда
            Найдено.Добавить(ДокументМД.Имя + "." + РеквизитМД.Имя);
        КонецЕсли;
    КонецЦикла;
    Для Каждого ТабличнаяЧастьМД Из ДокументМД.ТабличныеЧасти Цикл
        Для Каждого РеквизитМД Из ТабличнаяЧастьМД.Реквизиты Цикл
            Если РеквизитМД.Тип.СодержитТип(ИскомыйТип) Тогда
                Найдено.Добавить(ДокументМД.Имя + "." + ТабличнаяЧастьМД.Имя + "." + РеквизитМД.Имя);
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

Реквизиты составного типа тоже попадут в результат: СодержитТип проверяет вхождение типа, а не точное совпадение.

Какие объекты доступны роли

Функция ПравоДоступа() принимает право, объект метаданных и, при необходимости, роль или пользователя. Перебор справочников с проверкой права «Просмотр» для конкретной роли:

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

Имя роли здесь — пример. Подставьте имя роли из своей конфигурации: метод Найти() у коллекции вернёт Неопределено, если такой роли нет.

Карта движений документов

Цикл по ДокументМД.Движения даёт список регистров, куда пишет документ. Удобно при разборе, почему остаток в регистре не сходится: сразу видно, какие документы его меняют.

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

Диагностика: выберите симптом

Ещё одна ловушка — сравнение по строкам. Синоним меняют без последствий для кода, имя меняют редко, но тоже меняют. Сравнивайте объекты метаданных между собой (ОбъектМД = Метаданные.Справочники.Номенклатура) или по полному имени, но не по синониму.

Частые вопросы

Можно ли изменить метаданные программно, добавить реквизит из кода?
Нет. Коллекции метаданных доступны только для чтения. Структуру меняют в Конфигураторе или через расширение конфигурации.
Попадают ли в перебор объекты из расширений?
Да, если расширение подключено к базе в текущем сеансе: его собственные объекты появляются в соответствующих коллекциях.
Как посчитать, сколько всего справочников в конфигурации?
Вызовите Метаданные.Справочники.Количество(). Метод есть у любой коллекции объектов метаданных.
Чем имя отличается от полного имени?
Имя — это просто Номенклатура. ПолноеИмя() добавляет вид: Справочник.Номенклатура. Для поиска и текстов запросов нужно полное имя.
Можно ли сохранить ссылку на объект метаданных в реквизит?
Напрямую нет. В конфигурациях на БСП для этого служит справочник ИдентификаторыОбъектовМетаданных, в остальных храните полное имя строкой.