Как программно получить менеджер объекта 1С и найти ссылку по наименованию?

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

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

Проблема: Почему ТипЗнч и Метаданные не возвращают менеджер?

Рассмотрим стандартную ситуацию: мы перебираем реквизиты документа программно и пытаемся определить их тип. Часто начинающие разработчики пытаются использовать ТипЗнч() или напрямую обращаться к объекту метаданных, ожидая получить менеджер справочника, у которого можно вызвать метод НайтиПоНаименованию(). Однако объект метаданных — это лишь описание структуры, а не инструмент для работы с данными — для этого есть анализ структуры метаданных и консоль кода для 1С. Нам же нужен Менеджер объекта.

Если мы напишем Метаданные.НайтиПоТипу(ТипЗнч(Значение)), мы получим объект типа ОбъектМетаданных, который «знает» всё о структуре (имя, синоним, состав реквизитов), но не умеет искать элементы в таблицах базы данных. Для поиска нам нужно обратиться к глобальным коллекциям Справочники, Документы, ПланыВидовРасчета и так далее.

Решение 1: Прямое обращение через имя из метаданных

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


// Предположим, у нас есть ОбъектМетаданныхРеквизита
ИмяРеквизита = РеквизитМетаданных.Имя;
// Получаем типы, которые может принимать данный реквизит
МассивТипов = РеквизитМетаданных.Тип.Типы();

// Для простоты возьмем первый тип из описания (если тип не составной)
ТекущийТип = МассивТипов[0];
ОбъектМетаданныхТипа = Метаданные.НайтиПоТипу(ТекущийТип);

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

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

Решение 2: Универсальное получение менеджера по полному имени

Разберем более продвинутый метод. Каждый объект метаданных имеет метод ПолноеИмя(), который возвращает строку вида "Catalog.Organizations" (в англоязычной нотации) или "Справочник.Организации". Мы можем использовать эту строку для программного вычисления пути к менеджеру.

Посмотрим на пример функции, которая возвращает менеджер объекта на основе его типа:


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

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

Решение 3: Использование Библиотеки стандартных подсистем (БСП)

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

Проанализируем доступные инструменты:

  1. ОбщегоНазначения.МенеджерОбъектаПоСсылке(Ссылка) — возвращает менеджер, если у вас уже есть хотя бы пустая ссылка данного типа.
  2. ОбщегоНазначения.МенеджерОбъектаПоТипу(Тип) — наиболее подходящий нам метод.
  3. ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя) — позволяет получить менеджер по строке "Справочник.Организации".

Пример использования БСП в коде загрузки:


Для Каждого РеквизитМетаданных Из МетаданныеДокумента.Реквизиты Цикл
    ТипыРеквизита = РеквизитМетаданных.Тип.Типы();
    Если ТипыРеквизита.Количество() > 0 Тогда
        // Берем первый доступный тип
        Менеджер = ОбщегоНазначения.МенеджерОбъектаПоТипу(ТипыРеквизита[0]);
        
        Если Менеджер <> Неопределено И ОбщегоНазначения.ЭтоСправочник(Метаданные.НайтиПоТипу(ТипыРеквизита[0])) Тогда
            Ссылка = Менеджер.НайтиПоНаименованию(ЗначениеИзФайла);
            НовыйДокумент[РеквизитМетаданных.Имя] = Ссылка;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Работа с составными типами и ограничения поиска

Рассмотрим ситуацию, когда реквизит имеет составной тип данных. Например, реквизит Владелец может быть и СправочникСсылка.Контрагенты, и СправочникСсылка.ФизическиеЛица. В этом случае поиск «в лоб» через первый попавшийся тип может привести к ошибке.

В такой ситуации рекомендуется:

Также стоит помнить об ограничениях метода НайтиПоНаименованию(). По умолчанию он ищет по точному соответствию начала строки. Если в справочнике есть два элемента «Газпром» и «Газпром трансгаз», поиск по строке «Газпром» вернет первый найденный элемент. Для более точного поиска всегда лучше использовать поиск по ИНН/КПП или уникальному идентификатору (GUID), если они доступны в источнике данных. Для решения этой задачи есть улучшенные алгоритмы поиска объектов по фрагментам.

Резюме

Выясним причину, почему универсальный код загрузки часто дает сбои: дело в попытке работать с метаданными как с данными. Для успешного решения задачи:

  1. Всегда определяйте Тип реквизита через Метаданные.
  2. Используйте имя объекта для обращения к глобальным коллекциям (Справочники, Документы).
  3. По возможности используйте функции БСП для сокращения объема кода и повышения его надежности.
  4. Всегда делайте проверку на Ссылка.Пустая() после поиска, чтобы избежать записи некорректных данных в документ.

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

← На главную