При написании различных обменов данными, загрузок из текстовых файлов или Excel мы часто сталкиваемся с типичной задачей: у нас есть строковое наименование элемента (например, «Магазин №23»), и нам нужно программно записать его в реквизит документа, который имеет тип ссылки на справочник (например, СправочникСсылка.Организации).
Рассмотрим эту ситуацию подробнее. Если попытаться напрямую присвоить строку реквизиту-ссылке, как показано в примере ниже, платформа 1С просто проигнорирует такое действие или выдаст ошибку несоответствия типов:
// Так делать неправильно, если реквизит ссылочный!
ДокВозврат[ИмяРеквизита] = "Магазин №23";
Проанализируем по шагам, как правильно решать эту задачу, начиная от простого поиска элементов и заканчивая полностью динамическим определением типа реквизита через метаданные.
Выясним причину, почему прямое присвоение не работает: каждый реквизит в 1С имеет строго заданный тип. Чтобы поместить значение в поле типа СправочникСсылка, необходимо передать туда именно ссылку. Для получения ссылки из строки мы должны выполнить поиск в соответствующем справочнике базы данных.
Посмотрим на классический пример использования метода НайтиПоНаименованию:
СтроковоеЗначение = "Магазин №23";
ИскомаяСсылка = Справочники.Организации.НайтиПоНаименованию(СтроковоеЗначение);
ДокВозврат["Организация"] = ИскомаяСсылка;
Однако здесь кроется одна неочевидная проблема. Разберем ситуацию: что произойдет, если организация с таким именем отсутствует в базе? Метод не вызовет ошибку, он вернет пустую ссылку. Присвоение пустой ссылки реквизиту приведет к тому, что поле останется визуально пустым. Поэтому всегда проверяем результат поиска с помощью функции ЗначениеЗаполнено():
ИскомаяСсылка = Справочники.Организации.НайтиПоНаименованию(СтроковоеЗначение);
Если ЗначениеЗаполнено(ИскомаяСсылка) Тогда
ДокВозврат["Организация"] = ИскомаяСсылка;
Иначе
Сообщить("Внимание! Элемент с наименованием '" + СтроковоеЗначение + "' не найден.");
КонецЕсли;
Рассмотрим более сложный случай. Представим, что мы пишем универсальную загрузку. У нас есть имя реквизита (переменная Ключ) и строковое значение. Мы не знаем заранее, на какой именно справочник ссылается этот реквизит. Нам нужно получить эту информацию динамически "на лету".
Для этого обратимся к метаданным документа. Проанализируем алгоритм динамического поиска:
Посмотрим на пример универсального кода:
// Ключ - имя реквизита, например "Организация"
// Значение - строка для поиска
ТипРеквизита = ДокВозврат.Метаданные().Реквизиты[Ключ].Тип;
МассивТипов = ТипРеквизита.Виды();
Если МассивТипов.Количество() > 0 Тогда
// Берем первый доступный тип
ОбъектМД = Метаданные.НайтиПоТипу(МассивТипов[0]);
Если ОбъектМД <> Неопределено И Метаданные.Справочники.Содержит(ОбъектМД) Тогда
ИмяСправочника = ОбъектМД.Имя;
// Динамическое обращение к нужному справочнику
НайденнаяСсылка = Справочники[ИмяСправочника].НайтиПоНаименованию(Значение);
Если ЗначениеЗаполнено(НайденнаяСсылка) Тогда
ДокВозврат[Ключ] = НайденнаяСсылка;
Иначе
Сообщить("Значение " + Значение + " не найдено в справочнике " + ИмяСправочника);
КонецЕсли;
КонецЕсли;
КонецЕсли;
Важно: Если ваш реквизит имеет составной тип данных (может ссылаться на несколько разных справочников одновременно), вам потребуется обойти массив МассивТипов в цикле и попытаться выполнить поиск в каждом из них до первого успешного совпадения.
Разберем ситуацию, когда при загрузке данных нужного элемента в базе не оказалось, но бизнес-логика требует его обязательного присутствия. Вместо вывода сообщения об ошибке мы можем создать элемент программно.
Дополним наш предыдущий код блоком создания нового элемента:
НайденнаяСсылка = Справочники[ИмяСправочника].НайтиПоНаименованию(Значение);
Если ЗначениеЗаполнено(НайденнаяСсылка) Тогда
ДокВозврат[Ключ] = НайденнаяСсылка;
Иначе
// Создаем новый элемент справочника
НовыйЭлемент = Справочники[ИмяСправочника].СоздатьЭлемент();
НовыйЭлемент.Наименование = Значение;
Попытка
НовыйЭлемент.Записать();
ДокВозврат[Ключ] = НовыйЭлемент.Ссылка;
Сообщить("Автоматически создан элемент: " + Значение);
Исключение
Сообщить("Ошибка при создании элемента: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
Часто задача поиска по наименованию возникает из-за того, что данные выгрузили в текстовый файл "как есть" с помощью функции Строка(). Ручная склейка строк символами вроде тильды (~) — это устаревший и нестабильный подход.
Рассмотрим более надежные способы переноса ссылочных данных между базами (если базы идентичны). Чтобы избежать проблем с поиском элементов по наименованию (особенно если имена могут дублироваться), передавайте Уникальный Идентификатор (UUID).
Пример выгрузки UUID:
УИД_Строкой = Строка(Документ[Ключ].УникальныйИдентификатор());
Текст.ЗаписатьСтроку(Ключ + "=" + УИД_Строкой);
Пример загрузки по UUID:
УИД = Новый УникальныйИдентификатор(УИД_Строкой);
ИскомаяСсылка = Справочники.Организации.ПолучитьСсылку(УИД);
ДокВозврат[Ключ] = ИскомаяСсылка;
Для сложных структур данных настоятельно рекомендуем использовать Сериализатор XDTO или функции работы с JSON (например, ЗаписатьJSON). Они позволяют автоматически корректно преобразовывать любые ссылки и документы в текст и восстанавливать их обратно без потери типизации. Если же задача стоит в передаче таблиц и списков значений через файлы, отличным выбором будут платформенные функции ЗначениеВФайл() и ЗначениеИзФайла().