Как найти реквизит табличной части по типу значения в 1С без перебора всех метаданных?

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

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

Анализ стандартного подхода через перебор метаданных

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


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

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

Оптимизация производительности: Кэширование результатов поиска

Если нам необходимо обрабатывать много документов, самым правильным решением будет «запомнить» структуру нужных нам документов один раз. Проанализируем ситуацию: состав метаданных в режиме «Предприятие» не меняется динамически. Это значит, что если мы один раз определили, что в документе РеализацияТоваровУслуг в табличной части Товары искомый реквизит называется СтавкаНДС, мы можем сохранить эту информацию и использовать её повторно.

Разберем, как реализовать такое кэширование с помощью соответствия или структуры. Посмотрим на пример кода:


// Создаем кэш для хранения имен найденных реквизитов
Перем КэшМетаданных; // Инициализируется как Соответствие

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

Используя такой подход, мы перебираем метаданные каждого вида документа только один раз за весь цикл обработки. Это в десятки раз ускоряет выполнение кода при массовой обработке данных.

Использование функций с повторным использованием значений

Платформа 1С предоставляет встроенный механизм кэширования — модули с признаком «Повторное использование возвращаемых значений». Мы можем вынести логику поиска имен реквизитов в такой модуль. Рассмотрим преимущества этого метода:

  1. Платформа сама управляет памятью и очисткой кэша.
  2. Результат поиска доступен во всех сессиях (если установлено «На время сеанса»).
  3. Код становится чище и разделяется на логические уровни.

Проанализируем пример функции в таком модуле:


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

Работа с Библиотекой Стандартных Подсистем (БСП)

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

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

Тонкости сравнения типов данных

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

Неэффективно: Вызывать Тип("...") внутри вложенных циклов.

Эффективно:


НужныйТип = Новый ОписаниеТипов("ПеречислениеСсылка.СтавкиНДС");
// Далее в циклах используем:
Если Реквизит.Тип.СодержитТип(НужныйТип.Типы()[0]) Тогда
    // ...
КонецЕсли;

Альтернативный метод: Глобальный поиск в конфигураторе

Если задача не программная, а аналитическая (вам нужно один раз узнать, где используется перечисление СтавкиНДС), не обязательно писать код. Рассмотрим шаги поиска в конфигураторе:

  1. Найдите в дереве метаданных объект Перечисление.СтавкиНДС.
  2. Нажмите на нем правой кнопкой мыши и выберите пункт «Поиск ссылок на объект».
  3. Система выдаст полный список всех документов, табличных частей и реквизитов, где используется это перечисление.
  4. Результат можно выгрузить в текстовый файл или список для дальнейшего анализа.

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

Подведение итогов

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

← На главную