Как программно работать с дополнительными реквизитами при загрузке данных из Excel?

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

При разработке механизмов импорта данных из внешних файлов, таких как Excel, часто возникает ситуация, когда типовых реквизитов справочника недостаточно для идентификации объектов. В таких случаях на помощь приходят дополнительные реквизиты. Однако работа с ними отличается от работы с обычными реквизитами объекта, так как они хранятся в подчиненных табличных частях или отдельных регистрах. Рассмотрим подробно, как реализовать поиск по значению дополнительного реквизита и как правильно записывать в него данные.

Постановка задачи и анализ ситуации

Представим сценарий: нам необходимо загрузить список основных средств из файла Excel. В файле указан инвентарный номер, который является уникальным идентификатором, но в самой конфигурации 1С для справочника ОсновныеСредства этот номер вынесен в дополнительные реквизиты. Нам нужно проанализировать каждую строку файла: если объект с таким инвентарным номером уже существует — пропустить или обновить его, если нет — создать новый элемент и заполнить его свойства.

Способ 1: Прямое обращение через запрос и табличную часть

Рассмотрим самый наглядный метод, который часто используют начинающие разработчики. Он заключается в написании запроса к табличной части справочника, где хранятся дополнительные реквизиты. Разберем этот процесс по шагам.

Во-первых, нам необходимо найти ссылку на само свойство (характеристику) в плане видов характеристик ДополнительныеРеквизитыИСведения. Проанализируем пример кода для поиска:


// Находим ссылку на определение нашего дополнительного реквизита
СвойствоИдентификатора = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "ИнвНомераИзФайлаExcel");

Если СвойствоИдентификатора.Пустая() Тогда
    Сообщить("Дополнительный реквизит не найден! Проверьте имя в ПВХ.");
    Возврат;
КонецЕсли;

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ОсновныеСредстваДополнительныеРеквизиты.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.ОсновныеСредства.ДополнительныеРеквизиты КАК ОсновныеСредстваДополнительныеРеквизиты
    |ГДЕ
    |	ОсновныеСредстваДополнительныеРеквизиты.Свойство = &Свойство
    |	И ОсновныеСредстваДополнительныеРеквизиты.Значение = &Значение";

Запрос.УстановитьПараметр("Свойство", СвойствоИдентификатора);
Запрос.УстановитьПараметр("Значение", ПолученныйИнвНомерИзExcel);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Если Выборка.Следующий() Тогда
    // Объект найден
    ОбъектОС = Выборка.Ссылка.ПолучитьОбъект();
Иначе
    // Объект не найден, создаем новый
    ОбъектОС = Справочники.ОсновныеСредства.СоздатьЭлемент();
    ОбъектОС.Наименование = "Новое ОС (" + ПолученныйИнвНомерИзExcel + ")";
КонецЕсли;

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


// Проверяем, есть ли уже строка с таким свойством в объекте
СтруктураПоиска = Новый Структура("Свойство", СвойствоИдентификатора);
НайденныеСтроки = ОбъектОС.ДополнительныеРеквизиты.НайтиСтроки(СтруктураПоиска);

Если НайденныеСтроки.Количество() > 0 Тогда
    СтрокаРеквизита = НайденныеСтроки[0];
Иначе
    СтрокаРеквизита = ОбъектОС.ДополнительныеРеквизиты.Добавить();
    СтрокаРеквизита.Свойство = СвойствоИдентификатора;
КонецЕсли;

СтрокаРеквизита.Значение = ПолученныйИнвНомерИзExcel;
// Поле ТекстоваяСтрока важно для строковых доп. реквизитов для корректного отображения в списках
СтрокаРеквизита.ТекстоваяСтрока = Строка(ПолученныйИнвНомерИзExcel);

ОбъектОС.Записать();

Способ 2: Использование механизмов БСП (УправлениеСвойствами)

Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), крайне рекомендуется использовать встроенный программный интерфейс. Это позволит избежать ошибок при работе с типами данных и обеспечит совместимость при обновлениях системы. Рассмотрим подробнее функции общего модуля УправлениеСвойствами.

Для записи свойств объекта удобно использовать процедуру УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(). Она автоматически проверяет наличие строк в табличной части, создает их при необходимости и обновляет значения. Разберем пример формирования набора свойств:


// Создаем таблицу для передачи в БСП
ТаблицаСвойств = Новый ТаблицаЗначений;
ТаблицаСвойств.Колонки.Добавить("Свойство");
ТаблицаСвойств.Колонки.Добавить("Значение");

НоваяСтрока = ТаблицаСвойств.Добавить();
НоваяСтрока.Свойство = СвойствоИдентификатора;
НоваяСтрока.Значение = ПолученныйИнвНомерИзExcel;

// Записываем свойства для конкретной ссылки объекта
// Параметр Ложь указывает на работу с доп. реквизитами (а не сведениями)
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(СсылкаНаОС, ТаблицаСвойств);

Преимущество этого метода в том, что он берет на себя всю рутину по работе с коллекциями строк. Кроме того, если вам нужно просто прочитать значение, используйте функцию УправлениеСвойствами.ЗначенияСвойств(), которая вернет структуру со всеми заполненными доп. реквизитами объекта.

Оптимизация производительности: работа в цикле

Проанализируем ситуацию: если в вашем файле Excel 10 000 строк, выполнять запрос на каждой итерации цикла будет крайне неэффективно. Это классическая ошибка "Запрос в цикле". Выясним, как сделать загрузку быстрее.

Рекомендуемый алгоритм:

  1. Прочитать все инвентарные номера из Excel в массив МассивИнвНомеров.
  2. Выполнить один запрос ко всей табличной части справочника с условием ГДЕ Значение В (&МассивИнвНомеров).
  3. Выгрузить результат запроса в соответствие (Соответствие), где ключом будет инвентарный номер, а значением — ссылка на объект.
  4. При обходе строк Excel проверять наличие номера в этом Соответствии. Это моментальная операция в оперативной памяти.

Рассмотрим пример кода для такой оптимизации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ОсновныеСредстваДополнительныеРеквизиты.Значение КАК ИнвНомер,
    |	ОсновныеСредстваДополнительныеРеквизиты.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.ОсновныеСредства.ДополнительныеРеквизиты КАК ОсновныеСредстваДополнительныеРеквизиты
    |ГДЕ
    |	ОсновныеСредстваДополнительныеРеквизиты.Свойство = &Свойство";

Запрос.УстановитьПараметр("Свойство", СвойствоИдентификатора);
ТаблицаСуществующих = Запрос.Выполнить().Выгрузить();

// Создаем индекс для быстрого поиска
КэшОбъектов = Новый Соответствие;
Для Каждого Стр Из ТаблицаСуществующих Цикл
    КэшОбъектов.Вставить(Строка(Стр.ИнвНомер), Стр.Ссылка);
КонецЦикла;

// Теперь в цикле по Excel:
СсылкаНаОбъект = КэшОбъектов.Получить(ИнвНомерИзФайла);
Если СсылкаНаОбъект = Неопределено Тогда
    // Создаем новый...
КонецЕсли;

Важные технические нюансы

При работе с доп. реквизитами важно помнить о следующих моментах:

Подводя итог, отметим, что выбор метода зависит от ваших задач. Для простых разовых обработок подойдет прямое обращение к табличной части через запрос. Для серьезных системных решений лучше использовать возможности БСП и уделять внимание кэшированию данных при массовой загрузке.

← На главную