При программном заполнении документов и справочников разработчики часто сталкиваются с ситуацией, когда данные поступают в виде обычной строки (например, наименование или код из файла загрузки), а целевой реквизит имеет ссылочный тип данных — например, СправочникСсылка.Контрагенты. Попытка прямого присвоения строки ссылочному полю не приводит к результату, так как платформа 1С строго контролирует соответствие типов данных.
Проанализируем ситуацию: чтобы значение успешно записалось в реквизит ссылочного типа, переменная должна содержать не строковое представление, а конкретную ссылку на объект базы данных (тип СправочникСсылка, ДокументСсылка и т. д.). Разберем по шагам основные способы получения ссылки из строки в зависимости от формата входных данных.
Если во входной строке содержится наименование элемента справочника, наиболее простым решением является использование стандартного метода менеджера справочника НайтиПоНаименованию().
Рассмотрим пример кода:
ИмяРеквизита = "Контрагент";
ЗначениеРеквизита = "ООО Ромашка";
// Выполняем точный поиск по наименованию
СсылкаКонтрагент = Справочники.Контрагенты.НайтиПоНаименованию(СокрЛП(ЗначениеРеквизита), Истина);
Если Не СсылкаКонтрагент.Пустая() Тогда
МойДок[ИмяРеквизита] = СсылкаКонтрагент;
Иначе
// Обработка ситуации, когда элемент не найден в базе данных
ВызватьИсключение "Контрагент с наименованием """ + ЗначениеРеквизита + """ не найден!";
КонецЕсли;
Обратим внимание на второй параметр метода НайтиПоНаименованию(): установка значения Истина включает режим точного соответствия. Перед поиском рекомендуется использовать функцию СокрЛП(), чтобы исключить случайные пробелы по краям строки.
Поиск по наименованию не всегда надежен, так как в базе могут существовать дубли с одинаковыми названиями. Если в строке передан код элемента или уникальный реквизит (например, ИНН для контрагента или артикул для номенклатуры), лучше использовать специализированные методы поиска.
Посмотрим на примеры:
// Поиск по коду справочника
СсылкаПоКоду = Справочники.Контрагенты.НайтиПоКоду(СокрЛП(ЗначениеРеквизита));
// Поиск по произвольному реквизиту (например, ИНН)
СсылкаПоИНН = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", СокрЛП(ЗначениеРеквизита));
Если Не СсылкаПоИНН.Пустая() Тогда
МойДок.Контрагент = СсылкаПоИНН;
КонецЕсли;
Если строка представляет собой строковое представление GUID (например, "e1cbb3d8-1234-5678-9abc-def012345678"), ссылку можно получить напрямую без выполнения поискового запроса к таблице.
Разберем два варианта реализации:
УникальныйИдентификатор:
СтрокаGUID = "e1cbb3d8-1234-5678-9abc-def012345678";
УИД = Новый УникальныйИдентификатор(СтрокаGUID);
Ссылка = Справочники.Контрагенты.ПолучитьСсылку(УИД);
XMLЗначение():
Ссылка = XMLЗначение(Тип("СправочникСсылка.Контрагенты"), СтрокаGUID);
Важное ограничение: Метод ПолучитьСсылку() формирует ссылку в памяти. Если объекта с таким идентификатором в информационной базе нет, будет получена «битая» ссылка. Чтобы убедиться в существовании объекта, необходимо проверить его запросом или обращением к реквизиту через точку.
Когда имя реквизита и тип значения заранее не зафиксированы и обрабатываются динамически (например, в универсальных обработках загрузки), жестко прописывать обращение к Справочники.Контрагенты нельзя. Выясним причину и алгоритм решения этой задачи через метаданные.
Рассмотрим универсальный алгоритм:
ИмяРеквизита = "Контрагент";
ЗначениеРеквизита = "ООО Ромашка";
// 1. Получаем описание типов реквизита из метаданных объекта
ОписаниеТиповРеквизита = МойДок.Метаданные().Реквизиты[ИмяРеквизита].Тип;
МассивТипов = ОписаниеТиповРеквизита.Типы();
Если МассивТипов.Количество() > 0 Тогда
ТипРеквизита = МассивТипов[0];
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипРеквизита);
// Проверяем, что реквизит относится к справочникам
Если Метаданные.Справочники.Содержит(ОбъектМетаданных) Тогда
МенеджерСправочника = Справочники[ОбъектМетаданных.Имя];
НайденнаяСсылка = МенеджерСправочника.НайтиПоНаименованию(СокрЛП(ЗначениеРеквизита), Истина);
Если Не НайденнаяСсылка.Пустая() Тогда
МойДок[ИмяРеквизита] = НайденнаяСсылка;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если требуется обработать не один документ, а массив строк (например, при импорте прайс-листа или загрузке банковской выписки), вызывать методы НайтиПоНаименованию() или НайтиПоКоду() в цикле крайне не рекомендуется. Каждый такой вызов формирует отдельный запрос к СУБД, что сильно замедляет работу.
В таких случаях эффективнее собрать все строковые значения в таблицу значений и выполнить пакетный поиск одним запросом к базе данных.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТаблицаЗначений.Наименование КАК Наименование
|ПОМЕСТИТЬ ВТ_ДанныеДляПоиска
|ИЗ
| &ТаблицаДанных КАК ТаблицаЗначений
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ.Наименование КАК Наименование,
| Контрагенты.Ссылка КАК КонтрагентСсылка
|ИЗ
| ВТ_ДанныеДляПоиска КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО ВТ.Наименование = Контрагенты.Наименование
| И НЕ Контрагенты.ПометкаУдаления";
Запрос.УстановитьПараметр("ТаблицаДанных", ИсходнаяТаблица);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
С помощью полученной таблицы сопоставления можно мгновенно заполнять документы по ключу наименования без лишней нагрузки на сервер базы данных.