При разработке в среде «1С:Предприятие» начинающие программисты часто сталкиваются с ошибкой несоответствия типов. Типичная ситуация: у вас есть массив строк (например, названия товаров из Excel), и вы хотите записать их в реквизит справочника. Однако реквизит имеет тип СправочникСсылка.Номенклатура, а вы пытаетесь передать ему тип Строка. Напрямую это сделать невозможно, так как система ожидает уникальный идентификатор записи из базы данных, а не просто текст.
В этой статье мы подробно разберем, как правильно преобразовать строку в ссылку, найти существующий элемент в базе или создать новый, если поиск не дал результатов.
Самый простой и очевидный способ — попытаться найти элемент в справочнике по его текстовому названию. Для этого у менеджера справочника существует стандартный метод НайтиПоНаименованию().
Рассмотрим пошагово, как реализовать этот алгоритм в коде:
Для Каждого ТекущееНаименование Из МассивСтрок Цикл
// Пытаемся найти ссылку по тексту из массива
НайденнаяСсылка = Справочники.Номенклатура.НайтиПоНаименованию(ТекущееНаименование, Истина);
// Проверяем, удалось ли найти элемент
Если НЕ НайденнаяСсылка.Пустая() Тогда
НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
// Теперь мы присваиваем Ссылку, а не Строку
НовыйОбъект.ВариантОбъект = НайденнаяСсылка;
НовыйОбъект.Наименование = "Новый товар";
НовыйОбъект.Записать();
КонецЕсли;
КонецЦикла;
Важный нюанс: Обратите внимание на второй параметр функции НайтиПоНаименованию(ТекущееНаименование, Истина). Значение Истина означает «поиск по полному соответствию». Если установить Ложь, система может вернуть первый попавшийся элемент, название которого просто начинается на искомую строку, что часто приводит к логическим ошибкам.
В реальных задачах часто требуется не просто найти элемент, но и создать его, если в базе его еще нет. Это стандартная логика при импорте данных. Проанализируем следующий пример кода, который объединяет поиск и создание:
Для Каждого НазваниеДляПоиска Из МассивСтрок Цикл
// Обязательно удаляем лишние пробелы в начале и в конце строки
ЧистоеНаименование = СокрЛП(НазваниеДляПоиска);
Если ПустаяСтрока(ЧистоеНаименование) Тогда
Продолжить; // Пропускаем пустые строки
КонецЕсли;
СсылкаНаВариант = Справочники.ВариантыОбъектов.НайтиПоНаименованию(ЧистоеНаименование, Истина);
// Если ссылка пустая, значит такого элемента в справочнике нет
Если СсылкаНаВариант.Пустая() Тогда
НовыйВариант = Справочники.ВариантыОбъектов.СоздатьЭлемент();
НовыйВариант.Наименование = ЧистоеНаименование;
// Здесь можно заполнить другие обязательные реквизиты
НовыйВариант.Записать();
СсылкаНаВариант = НовыйВариант.Ссылка;
КонецЕсли;
// Теперь используем полученную ссылку для заполнения основного объекта
НовыйТовар = Справочники.Номенклатура.СоздатьЭлемент();
НовыйТовар.Наименование = "Запись на основании " + ЧистоеНаименование;
НовыйТовар.ВариантОбъект = СсылкаНаВариант; // Типы совпадают!
НовыйТовар.Записать();
КонецЦикла;
В данном примере мы используем метод Пустая(). Это критически важно, так как метод поиска всегда возвращает объект типа «Ссылка», но если элемент не найден, эта ссылка будет указывать в «никуда» (так называемая битая или пустая ссылка).
Если в вашем массиве тысячи элементов, использование метода НайтиПоНаименованию внутри цикла будет работать медленно, так как программа будет обращаться к базе данных на каждой итерации. Проанализируем ситуацию: правильнее будет одним запросом проверить сразу все строки из массива.
Рассмотрим, как это сделать правильно:
ТаблицаЗначений.ЛЕВОЕ СОЕДИНЕНИЕ нашей таблицы со справочником.
// Создаем таблицу значений для поиска
ТЗ_Поиска = Новый ТаблицаЗначений;
ТЗ_Поиска.Колонки.Добавить("НаименованиеДляПоиска", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(150)));
Для Каждого ЭлементМассива Из МассивСтрок Цикл
НоваяСтрокаТЗ = ТЗ_Поиска.Добавить();
НоваяСтрокаТЗ.НаименованиеДляПоиска = СокрЛП(ЭлементМассива);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.НаименованиеДляПоиска КАК Наименование
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТЗ КАК ТЗ
|;
|
|ВЫБРАТЬ
| ВТ_Данные.Наименование КАК Наименование,
| ЕСТЬNULL(Справочник.Ссылка, ЗНАЧЕНИЕ(Справочник.ВариантыОбъектов.ПустаяСсылка)) КАК Ссылка
|ИЗ
| ВТ_Данные КАК ВТ_Данные
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВариантыОбъектов КАК Справочник
| ПО ВТ_Данные.Наименование = Справочник.Наименование";
Запрос.УстановитьПараметр("ТЗ", ТЗ_Поиска);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
// Теперь обходим результат и создаем только то, чего не хватает
Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
Если СтрокаРезультата.Ссылка.Пустая() Тогда
// Код создания нового элемента (см. Способ 2)
Иначе
// Используем готовую Ссылку
КонецЕсли;
КонецЦикла;
Такой подход значительно ускоряет работу системы, так как основная нагрузка по сопоставлению данных перекладывается на SQL-сервер.
При выполнении подобных задач всегда помните о следующих правилах:
СокрЛП() к входящим строкам. Пробел в конце строки (например, "Товар ") сделает её уникальной для базы, и поиск по "Товар" не сработает.НачатьТранзакцию() ... ЗафиксироватьТранзакцию(). Это ускорит запись в разы.УникальныйИдентификатор (GUID). Это исключит проблемы с дублями названий.Подведем итог: чтобы присвоить строковое значение ссылочному реквизиту, мы должны сначала «найти» этот объект в базе данных с помощью функций поиска или запросов, получить его Ссылку, и только после этого присвоить её нужному полю. Выбор метода зависит от объема ваших данных и требований к производительности системы.