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

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

При разработке нестандартных обменов данными, например, при выгрузке и загрузке документов через текстовые файлы (TXT) без использования уникальных идентификаторов (GUID), XML или JSON, мы часто сталкиваемся с интересной задачей. Проанализируем типичную ситуацию: у нас есть текстовый файл, в котором хранятся имя реквизита, его строковое представление (наименование или синоним) и тип данных в виде строки (например, Справочник.Организации). Нам необходимо прочитать этот файл, найти соответствующие объекты в базе данных и заполнить ими создаваемый документ.

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

Разбор проблемы: Метаданные против Менеджеров объектов

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


ОбъектМетаданных = Метаданные.НайтиПоПолномуИмени(Стр.Тип); 
Ссылка = ОбъектМетаданных[Стр.Значение]; // Ошибка!
НовыйДокумент[Стр.Имя] = Ссылка;

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

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

Решение 1: Универсальное получение менеджера объекта

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

Рассмотрим, как правильно реализовать универсальный поиск:


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

Если ЗначениеЗаполнено(Ссылка) Тогда
    НовыйДокумент[Стр.Имя] = Ссылка;
КонецЕсли;

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

Особенности поиска для разных типов объектов

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

  1. Справочники и Планы видов характеристик: Имеют методы НайтиПоНаименованию(), НайтиПоКоду() и НайтиПоРеквизиту(). Важно: поиск по наименованию — самый ненадежный способ. Если в базе есть два элемента с одинаковым именем, система вернет первую попавшуюся ссылку. В идеале выгружать еще и код или родителя элемента.
  2. Документы: У документов нет метода поиска по наименованию. Их нужно искать с помощью НайтиПоНомеру(), передавая номер и, желательно, дату документа, чтобы избежать проблем с периодичностью нумерации.
  3. Составные типы: Если реквизит имеет составной тип, перед поиском строку типа нужно программно разобрать (например, отделить «Справочник» от «Контрагенты» по символу точки), чтобы понять, к какой коллекции менеджеров обращаться.

Решение 2: Особый случай — работа с перечислениями

Перечисления (тип ПеречислениеСсылка) сильно отличаются от справочников и документов. Они не хранятся в базе как пользовательские записи. Значения перечислений задаются в конфигураторе.

Часто в текстовый файл при выгрузке попадает Синоним значения перечисления (то, что видит пользователь, например "Реализация товаров"), а для программного получения ссылки нам нужно его системное Имя (например "РеализацияТоваров"). Обычный поиск тут не сработает.

Напишем специальную функцию, которая составит соответствие синонимов и реальных ссылок перечисления, чтобы мы могли легко находить их при загрузке:


&НаСервереБезКонтекста
Функция ПолучитьСписокЗначенийПеречисления(НаименованиеПеречисления)
    
    СоответствиеЗначенийПеречисления = Новый Соответствие;
    
    // Перебираем значения метаданных конкретного перечисления
    Для каждого ЭлементПеречисления Из Метаданные.Перечисления[НаименованиеПеречисления].ЗначенияПеречисления Цикл
        
        // Ключ - синоним (строка из файла), Значение - реальная ссылка на перечисление
        СинонимЗначения = СокрЛП(ЭлементПеречисления.Синоним);
        СсылкаНаЗначение = Перечисления[НаименованиеПеречисления][ЭлементПеречисления.Имя];
        
        СоответствиеЗначенийПеречисления.Вставить(СинонимЗначения, СсылкаНаЗначение);
        
    КонецЦикла;
    
    Возврат СоответствиеЗначенийПеречисления;
    
КонецФункции

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

Продвинутые техники и альтернативные подходы

Посмотрим на дополнительные методы, которые могут быть полезны при реализации нестандартных загрузок данных.

Функция Вычислить()
В некоторых случаях, когда нужно собрать сложный код «на лету», используют встроенную функцию Вычислить(). Она позволяет выполнить строковое выражение как код 1С.


СтрокаКода = "Справочники." + ИмяСправочника + ".НайтиПоНаименованию(""" + Стр.Значение + """)";
Ссылка = Вычислить(СтрокаКода);

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

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


// Стр.Значение должно содержать полный путь, например: "Справочник.Склады.Основной"
Ссылка = ПредопределенноеЗначение(Стр.Значение);

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

← На главную