Обход метаданных конфигурации 1С 8.3: от простого цикла до рекурсии
Содержание
Перебрать объекты метаданных в 1С проще всего циклом Для Каждого ОбъектМД Из Метаданные.Справочники Цикл в серверной процедуре. Это первый способ из пяти. Второй — точечный поиск через Метаданные.НайтиПоПолномуИмени() и НайтиПоТипу(). Третий — рекурсивный обход подсистем, табличных частей и реквизитов. Четвёртый — ПолучитьСтруктуруХраненияБазыДанных(), когда нужны таблицы СУБД. Пятый — без кода: отчёт по конфигурации или выгрузка в файлы из Конфигуратора.
Цикл по коллекции берут для отчётов и проверок «по всем объектам». Поиск по имени — когда объект известен заранее. Рекурсия нужна для вложенных структур. Структура хранения — для администратора, который ищет, какой объект стоит за таблицей _Reference123.
Что лежит в свойстве Метаданные и где запускать код
Глобальное свойство Метаданные возвращает объект ОбъектМетаданныхКонфигурация. У него есть коллекции по видам объектов. Каждая коллекция перебирается циклом и содержит объекты типа ОбъектМетаданных.
Коллекции, которые нужны чаще всего:
- 📁
Справочники,Документы,Перечисления,Константы; - 📊
РегистрыСведений,РегистрыНакопления,РегистрыБухгалтерии,РегистрыРасчета; - 🗂️
ПланыВидовХарактеристик,ПланыСчетов,ПланыВидовРасчета,ПланыОбмена; - 🔄
БизнесПроцессы,Задачи,ЖурналыДокументов; - 🧩
Обработки,Отчеты,ОбщиеМодули,Роли,Подсистемы,ОбщиеРеквизиты.
У любого объекта метаданных есть базовые свойства: Имя, Синоним, Комментарий и метод ПолноеИмя(). Полное имя выглядит так: Справочник.Номенклатура, РегистрНакопления.ТоварыНаСкладах.
Свойство Метаданные недоступно в тонком клиенте и веб-клиенте. Код в процедуре с директивой &НаКлиенте упадёт с ошибкой Переменная не определена (Метаданные). Пишите обход в процедуре &НаСервере, &НаСервереБезКонтекста или в серверном общем модуле.
Для разовых экспериментов удобна внешняя обработка: форма с кнопкой и серверный обработчик. Запускается через Файл → Открыть в режиме «Предприятие», если у пользователя есть право на интерактивное открытие внешних обработок.
Способ 1. Перебор коллекций Метаданные циклом
Базовый пример — вывести все справочники с синонимами:
&НаСервереБезКонтекста
Процедура ВывестиСправочникиНаСервере()
Для Каждого ОбъектМД Из Метаданные.Справочники Цикл
Сообщить(ОбъектМД.ПолноеИмя() + " — " + ОбъектМД.Синоним);
КонецЦикла;
КонецПроцедуры
Чтобы обойти сразу несколько видов объектов, соберите коллекции в массив. Обращаться к Метаданные по строковому имени коллекции не нужно, достаточно перечислить их явно:
&НаСервереБезКонтекста
Функция СписокОбъектовКонфигурации()
Коллекции = Новый Массив;
Коллекции.Добавить(Метаданные.Справочники);
Коллекции.Добавить(Метаданные.Документы);
Коллекции.Добавить(Метаданные.РегистрыСведений);
Коллекции.Добавить(Метаданные.РегистрыНакопления);
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("Вид");
Результат.Колонки.Добавить("Имя");
Результат.Колонки.Добавить("Синоним");
Для Каждого Коллекция Из Коллекции Цикл
Для Каждого ОбъектМД Из Коллекция Цикл
Строка = Результат.Добавить();
Строка.Вид = СтрРазделить(ОбъектМД.ПолноеИмя(), ".")[0];
Строка.Имя = ОбъектМД.Имя;
Строка.Синоним = ОбъектМД.Синоним;
КонецЦикла;
КонецЦикла;
Возврат Результат;
КонецФункции
Вид объекта берём из первой части полного имени. ТипЗнч(ОбъектМД) здесь не поможет: для любого объекта он вернёт один и тот же тип ОбъектМетаданных.
Что перебирать внутри объекта
У каждого вида свой набор подчинённых коллекций:
| Вид объекта | Подчинённые коллекции |
|---|---|
| Справочник | Реквизиты, ТабличныеЧасти, СтандартныеРеквизиты, Владельцы |
| Документ | Реквизиты, ТабличныеЧасти, СтандартныеРеквизиты, Движения |
| Регистр сведений, накопления | Измерения, Ресурсы, Реквизиты |
| Перечисление | ЗначенияПеречисления |
| Подсистема | Подсистемы, Состав |
Движения документа — это коллекция регистров, по которым он делает записи. По ней легко собрать карту «документ → регистры».
Способ 2. Поиск объекта по имени и типу, доступ к менеджеру
Перебирать всю коллекцию, чтобы найти один объект, не нужно. Для этого есть два метода.
Метаданные.НайтиПоПолномуИмени("Справочник.Номенклатура")— вернёт объект метаданных илиНеопределено, если такого нет.Метаданные.НайтиПоТипу(ТипЗнч(Ссылка))— по типу ссылки находит её объект метаданных. Удобно в универсальном коде, который получает «какую-то ссылку».
Проверить, к какому виду относится объект, можно методом Содержит() у коллекции:
ОбъектМД = Метаданные.НайтиПоТипу(ТипЗнч(Ссылка));
Если ОбъектМД <> Неопределено И Метаданные.Документы.Содержит(ОбъектМД) Тогда
// это документ
КонецЕсли;
Как получить менеджер по объекту метаданных
От метаданных обычно переходят к данным. Глобальные коллекции менеджеров индексируются по имени:
МенеджерСправочника = Справочники[ОбъектМД.Имя];
Выборка = МенеджерСправочника.Выбрать();
В конфигурациях на БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) есть готовая функция ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя). Она сама разбирает вид по полному имени и возвращает нужный менеджер. В типовой конфигурации лучше пользоваться ею, чем писать свой разбор.
💡 В БСП есть ещё справочник ИдентификаторыОбъектовМетаданных. Он хранит ссылки на объекты конфигурации, их можно записывать в реквизиты и регистры. Сам объект метаданных в базу не записать.
Способ 3. Рекурсивный обход метаданных: подсистемы и реквизиты
В 1С нет рефлексии в привычном смысле: нельзя перечислить все свойства произвольного объекта метаданных одним методом. Рекурсия здесь — это обход заранее известных вложенных коллекций.
Классический пример — дерево подсистем. Подсистема содержит вложенные подсистемы, глубина не ограничена:
&НаСервереБезКонтекста
Процедура ОбойтиПодсистемы(Подсистемы, Уровень, Результат)
Для Каждого ПодсистемаМД Из Подсистемы Цикл
Строка = Результат.Добавить();
Строка.Уровень = Уровень;
Строка.Имя = ПодсистемаМД.Имя;
Строка.Объектов = ПодсистемаМД.Состав.Количество();
ОбойтиПодсистемы(ПодсистемаМД.Подсистемы, Уровень + 1, Результат);
КонецЦикла;
КонецПроцедуры
Вызов: создайте таблицу значений с колонками Уровень, Имя, Объектов и передайте Метаданные.Подсистемы, 1 и эту таблицу.
Обход реквизитов шапки и табличных частей
Второй частый случай — пройти по всем полям объекта, включая табличные части. Стандартные реквизиты (Код, Наименование, Дата, Номер) в коллекцию Реквизиты не входят, их перебирают отдельно через СтандартныеРеквизиты.
&НаСервереБезКонтекста
Процедура СобратьПоля(ОбъектМД, Результат)
Для Каждого РеквизитМД Из ОбъектМД.Реквизиты Цикл
Результат.Добавить(ОбъектМД.Имя + "." + РеквизитМД.Имя);
КонецЦикла;
Для Каждого ТабличнаяЧастьМД Из ОбъектМД.ТабличныеЧасти Цикл
Для Каждого РеквизитМД Из ТабличнаяЧастьМД.Реквизиты Цикл
Результат.Добавить(ОбъектМД.Имя + "." + ТабличнаяЧастьМД.Имя + "." + РеквизитМД.Имя);
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Способ 4 и 5. Перебор без бизнес-логики: структура хранения и Конфигуратор
ПолучитьСтруктуруХраненияБазыДанных
Функция возвращает таблицу значений: объект метаданных, его назначение (основная таблица, табличная часть, итоги) и имя таблицы в СУБД, с полями и индексами. Первым параметром передают массив объектов или их полных имён, без параметра — получают всю базу.
Объекты = Новый Массив;
Объекты.Добавить("Справочник.Номенклатура");
Структура = ПолучитьСтруктуруХраненияБазыДанных(Объекты);
Для Каждого Строка Из Структура Цикл
Сообщить(Строка.Метаданные + " | " + Строка.Назначение + " | " + Строка.ИмяТаблицыХранения);
КонецЦикла;
Так администратор понимает, что за таблица «распухла» в SQL или чей индекс тормозит.
Без кода
- 📄 В Конфигураторе:
Конфигурация → Отчет по конфигурации. Получите текстовый отчёт со всеми объектами и их составом. - 💾
Конфигурация → Выгрузить конфигурацию в файлы— дерево XML-файлов. По нему удобно искать grep-ом или в редакторе кода, сравнивать версии в Git.
Какой способ выбрать
| Способ | Когда | Ограничения |
|---|---|---|
| Цикл по коллекции | Отчёт или проверка по всем объектам вида | Только сервер |
НайтиПоПолномуИмени, НайтиПоТипу | Объект известен заранее | Нужно проверять Неопределено |
| Рекурсия | Подсистемы, табличные части, вложенные поля | Глубину и коллекции задаёте сами |
ПолучитьСтруктуруХраненияБазыДанных | Связь объектов с таблицами СУБД | Для всей базы — большой результат |
| Отчёт, выгрузка в файлы | Разовый анализ без программирования | Нужен доступ к Конфигуратору |
Как ускорить обход большой конфигурации
В Бухгалтерии 3.0 или ERP объектов метаданных — тысячи, а реквизитов — десятки тысяч. Сам перебор метаданных быстрый. Тормозит то, что делают внутри цикла.
- 🐢 Запрос на каждый объект. Посчитать записи во всех справочниках сотней отдельных запросов долго. Соберите один текст через
ОБЪЕДИНИТЬ ВСЕили пакет запросов. - 🐢 Повторный обход. Если список нужен часто, вынесите функцию в общий модуль с повторным использованием возвращаемых значений «На время сеанса». Возвращайте
ФиксированныйМассивилиФиксированнаяСтруктура: изменяемое значение из кэша можно случайно испортить. - 🐢 Поиск в цикле. Не ищите объект перебором, если знаете имя:
НайтиПоПолномуИмениотработает сразу. - 🐢 Лишние уровни. Не спускайтесь в табличные части и стандартные реквизиты, если задаче хватает шапки.
Пример подсчёта записей одним запросом:
Части = Новый Массив;
Для Каждого СправочникМД Из Метаданные.Справочники Цикл
Части.Добавить(СтрШаблон(
"ВЫБРАТЬ ""%1"" КАК Объект, КОЛИЧЕСТВО(*) КАК Записей ИЗ %1 КАК Т",
СправочникМД.ПолноеИмя()));
КонецЦикла;
Запрос = Новый Запрос(СтрСоединить(Части, Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС));
Таблица = Запрос.Выполнить().Выгрузить();
💡 В большой конфигурации запрос на сотни объединений долго компилируется. Режьте массив на порции по 50–100 объектов.
Типовые задачи: поиск по типу реквизита и проверка прав
Где используется справочник
Вопрос «в каких документах есть ссылка на Контрагентов» решается проверкой типа каждого реквизита. Свойство Тип у реквизита — это ОписаниеТипов, у него есть метод СодержитТип():
ИскомыйТип = Тип("СправочникСсылка.Контрагенты");
Найдено = Новый Массив;
Для Каждого ДокументМД Из Метаданные.Документы Цикл
Для Каждого РеквизитМД Из ДокументМД.Реквизиты Цикл
Если РеквизитМД.Тип.СодержитТип(ИскомыйТип) Тогда
Найдено.Добавить(ДокументМД.Имя + "." + РеквизитМД.Имя);
КонецЕсли;
КонецЦикла;
Для Каждого ТабличнаяЧастьМД Из ДокументМД.ТабличныеЧасти Цикл
Для Каждого РеквизитМД Из ТабличнаяЧастьМД.Реквизиты Цикл
Если РеквизитМД.Тип.СодержитТип(ИскомыйТип) Тогда
Найдено.Добавить(ДокументМД.Имя + "." + ТабличнаяЧастьМД.Имя + "." + РеквизитМД.Имя);
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЦикла;
Реквизиты составного типа тоже попадут в результат: СодержитТип проверяет вхождение типа, а не точное совпадение.
Какие объекты доступны роли
Функция ПравоДоступа() принимает право, объект метаданных и, при необходимости, роль или пользователя. Перебор справочников с проверкой права «Просмотр» для конкретной роли:
РольМД = Метаданные.Роли.Найти("ДобавлениеИзменениеНСИ");
Если РольМД <> Неопределено Тогда
Для Каждого СправочникМД Из Метаданные.Справочники Цикл
Если ПравоДоступа("Просмотр", СправочникМД, РольМД) Тогда
Сообщить(СправочникМД.Синоним);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Имя роли здесь — пример. Подставьте имя роли из своей конфигурации: метод Найти() у коллекции вернёт Неопределено, если такой роли нет.
Карта движений документов
Цикл по ДокументМД.Движения даёт список регистров, куда пишет документ. Удобно при разборе, почему остаток в регистре не сходится: сразу видно, какие документы его меняют.
Ошибки при обходе объектов метаданных
Ещё одна ловушка — сравнение по строкам. Синоним меняют без последствий для кода, имя меняют редко, но тоже меняют. Сравнивайте объекты метаданных между собой (ОбъектМД = Метаданные.Справочники.Номенклатура) или по полному имени, но не по синониму.
Частые вопросы
Можно ли изменить метаданные программно, добавить реквизит из кода?
Попадают ли в перебор объекты из расширений?
Как посчитать, сколько всего справочников в конфигурации?
Метаданные.Справочники.Количество(). Метод есть у любой коллекции объектов метаданных.Чем имя отличается от полного имени?
Имя — это просто Номенклатура. ПолноеИмя() добавляет вид: Справочник.Номенклатура. Для поиска и текстов запросов нужно полное имя.