При разработке механизмов импорта данных из внешних файлов, таких как Excel, часто возникает ситуация, когда типовых реквизитов справочника недостаточно для идентификации объектов. В таких случаях на помощь приходят дополнительные реквизиты. Однако работа с ними отличается от работы с обычными реквизитами объекта, так как они хранятся в подчиненных табличных частях или отдельных регистрах. Рассмотрим подробно, как реализовать поиск по значению дополнительного реквизита и как правильно записывать в него данные.
Представим сценарий: нам необходимо загрузить список основных средств из файла Excel. В файле указан инвентарный номер, который является уникальным идентификатором, но в самой конфигурации 1С для справочника ОсновныеСредства этот номер вынесен в дополнительные реквизиты. Нам нужно проанализировать каждую строку файла: если объект с таким инвентарным номером уже существует — пропустить или обновить его, если нет — создать новый элемент и заполнить его свойства.
Рассмотрим самый наглядный метод, который часто используют начинающие разработчики. Он заключается в написании запроса к табличной части справочника, где хранятся дополнительные реквизиты. Разберем этот процесс по шагам.
Во-первых, нам необходимо найти ссылку на само свойство (характеристику) в плане видов характеристик ДополнительныеРеквизитыИСведения. Проанализируем пример кода для поиска:
// Находим ссылку на определение нашего дополнительного реквизита
СвойствоИдентификатора = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "ИнвНомераИзФайлаExcel");
Если СвойствоИдентификатора.Пустая() Тогда
Сообщить("Дополнительный реквизит не найден! Проверьте имя в ПВХ.");
Возврат;
КонецЕсли;
Во-вторых, подготовим запрос, который проверит наличие значения в базе. Выясним причину использования запроса: это самый быстрый способ проверить существование записи, не загружая объект целиком в память.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОсновныеСредстваДополнительныеРеквизиты.Ссылка КАК Ссылка
|ИЗ
| Справочник.ОсновныеСредства.ДополнительныеРеквизиты КАК ОсновныеСредстваДополнительныеРеквизиты
|ГДЕ
| ОсновныеСредстваДополнительныеРеквизиты.Свойство = &Свойство
| И ОсновныеСредстваДополнительныеРеквизиты.Значение = &Значение";
Запрос.УстановитьПараметр("Свойство", СвойствоИдентификатора);
Запрос.УстановитьПараметр("Значение", ПолученныйИнвНомерИзExcel);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
// Объект найден
ОбъектОС = Выборка.Ссылка.ПолучитьОбъект();
Иначе
// Объект не найден, создаем новый
ОбъектОС = Справочники.ОсновныеСредства.СоздатьЭлемент();
ОбъектОС.Наименование = "Новое ОС (" + ПолученныйИнвНомерИзExcel + ")";
КонецЕсли;
После того как мы определились с объектом (нашли старый или создали новый), нам нужно записать значение инвентарного номера в его табличную часть ДополнительныеРеквизиты. Посмотрим на пример реализации:
// Проверяем, есть ли уже строка с таким свойством в объекте
СтруктураПоиска = Новый Структура("Свойство", СвойствоИдентификатора);
НайденныеСтроки = ОбъектОС.ДополнительныеРеквизиты.НайтиСтроки(СтруктураПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
СтрокаРеквизита = НайденныеСтроки[0];
Иначе
СтрокаРеквизита = ОбъектОС.ДополнительныеРеквизиты.Добавить();
СтрокаРеквизита.Свойство = СвойствоИдентификатора;
КонецЕсли;
СтрокаРеквизита.Значение = ПолученныйИнвНомерИзExcel;
// Поле ТекстоваяСтрока важно для строковых доп. реквизитов для корректного отображения в списках
СтрокаРеквизита.ТекстоваяСтрока = Строка(ПолученныйИнвНомерИзExcel);
ОбъектОС.Записать();
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), крайне рекомендуется использовать встроенный программный интерфейс. Это позволит избежать ошибок при работе с типами данных и обеспечит совместимость при обновлениях системы. Рассмотрим подробнее функции общего модуля УправлениеСвойствами.
Для записи свойств объекта удобно использовать процедуру УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(). Она автоматически проверяет наличие строк в табличной части, создает их при необходимости и обновляет значения. Разберем пример формирования набора свойств:
// Создаем таблицу для передачи в БСП
ТаблицаСвойств = Новый ТаблицаЗначений;
ТаблицаСвойств.Колонки.Добавить("Свойство");
ТаблицаСвойств.Колонки.Добавить("Значение");
НоваяСтрока = ТаблицаСвойств.Добавить();
НоваяСтрока.Свойство = СвойствоИдентификатора;
НоваяСтрока.Значение = ПолученныйИнвНомерИзExcel;
// Записываем свойства для конкретной ссылки объекта
// Параметр Ложь указывает на работу с доп. реквизитами (а не сведениями)
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(СсылкаНаОС, ТаблицаСвойств);
Преимущество этого метода в том, что он берет на себя всю рутину по работе с коллекциями строк. Кроме того, если вам нужно просто прочитать значение, используйте функцию УправлениеСвойствами.ЗначенияСвойств(), которая вернет структуру со всеми заполненными доп. реквизитами объекта.
Проанализируем ситуацию: если в вашем файле Excel 10 000 строк, выполнять запрос на каждой итерации цикла будет крайне неэффективно. Это классическая ошибка "Запрос в цикле". Выясним, как сделать загрузку быстрее.
Рекомендуемый алгоритм:
МассивИнвНомеров.ГДЕ Значение В (&МассивИнвНомеров).Соответствие), где ключом будет инвентарный номер, а значением — ссылка на объект.Соответствии. Это моментальная операция в оперативной памяти.Рассмотрим пример кода для такой оптимизации:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОсновныеСредстваДополнительныеРеквизиты.Значение КАК ИнвНомер,
| ОсновныеСредстваДополнительныеРеквизиты.Ссылка КАК Ссылка
|ИЗ
| Справочник.ОсновныеСредства.ДополнительныеРеквизиты КАК ОсновныеСредстваДополнительныеРеквизиты
|ГДЕ
| ОсновныеСредстваДополнительныеРеквизиты.Свойство = &Свойство";
Запрос.УстановитьПараметр("Свойство", СвойствоИдентификатора);
ТаблицаСуществующих = Запрос.Выполнить().Выгрузить();
// Создаем индекс для быстрого поиска
КэшОбъектов = Новый Соответствие;
Для Каждого Стр Из ТаблицаСуществующих Цикл
КэшОбъектов.Вставить(Строка(Стр.ИнвНомер), Стр.Ссылка);
КонецЦикла;
// Теперь в цикле по Excel:
СсылкаНаОбъект = КэшОбъектов.Получить(ИнвНомерИзФайла);
Если СсылкаНаОбъект = Неопределено Тогда
// Создаем новый...
КонецЕсли;
При работе с доп. реквизитами важно помнить о следующих моментах:
Строка() для приведения типов данных из Excel.Имени, заданному разработчиком, так как Наименование (заголовок) может изменить пользователь в интерфейсе.Подводя итог, отметим, что выбор метода зависит от ваших задач. Для простых разовых обработок подойдет прямое обращение к табличной части через запрос. Для серьезных системных решений лучше использовать возможности БСП и уделять внимание кэшированию данных при массовой загрузке.