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

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

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

Анализ проблемы типизации в Таблице Значений

Рассмотрим ситуацию, когда мы загружаем данные из Excel в промежуточную ТаблицуЗначений. Если загрузка происходит через ПостроительЗапроса или прямое чтение ТабличногоДокумента, система 1С автоматически назначает колонкам тип Строка. Когда мы пытаемся записать в такую колонку найденную ссылку на справочник Номенклатура, 1С принудительно преобразует ссылку в её строковое представление (наименование). В итоге при попытке заполнить документ мы получаем ошибки или пустые значения, так как документу нужна именно Ссылка, а не текст.

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

Шаг 1. Получение данных из Excel в Таблицу Значений

Проанализируем базовый алгоритм получения данных. Мы используем временный файл для чтения данных из двоичных данных и помещаем их в объект ТабличныйДокумент. Далее с помощью ПостроителяЗапроса мы быстро выгружаем область ячеек в таблицу.


Функция EXCEL_ВТаблицуЗначений(ДвоичДан, Расширение)
    ФайлEXCELНаСервере = ПолучитьИмяВременногоФайла(Расширение);
    ДвоичДан.Записать(ФайлEXCELНаСервере);
    
    ТабличныйДокумент = Новый ТабличныйДокумент;
    ТабличныйДокумент.Прочитать(ФайлEXCELНаСервере);
    УдалитьФайлы(ФайлEXCELНаСервере);
    
    // Определяем область данных (например, с 24 строки по 85)
    ОбластьТаблицы = ТабличныйДокумент.Область(24, 2, 85, ТабличныйДокумент.ШиринаТаблицы);
    
    ПостроительЗапроса = Новый ПостроительЗапроса;
    ПостроительЗапроса.ИсточникДанных = Новый ОписаниеИсточникаДанных(ОбластьТаблицы);
    ПостроительЗапроса.Выполнить();
    
    Возврат ПостроительЗапроса.Результат.Выгрузить();
КонецФункции

Шаг 2. Подготовка таблицы для хранения ссылок

После того как мы получили ТаблицаДанных, где все значения строковые, нам нужно добавить колонку, которая сможет хранить ссылки. Выясним причину, почему это важно: без явного указания типа СправочникСсылка.Номенклатура мы не сможем корректно заполнить табличную часть документа методом Загрузить() или обычным присваиванием.

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


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

ТаблицаДанных.Колонки.Добавить("НоменклатураСсылка", ОписаниеТипаНоменклатура, "Номенклатура (ссылка)");

Шаг 3. Поиск номенклатуры по артикулу через запрос

Вместо того чтобы искать номенклатуру в цикле для каждой строки (что крайне неэффективно и замедляет работу системы при больших объемах данных), мы соберем все артикулы в массив и выполним один запрос к базе данных. Проанализируем этот подход подробнее.


Функция НайтиНоменклатуруПоАртикулам(МассивАртикулов)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Ссылка КАК Ссылка,
        |   Номенклатура.Артикул КАК Артикул
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |   Номенклатура.Артикул В(&МассивАртикулов)";
    
    Запрос.УстановитьПараметр("МассивАртикулов", МассивАртикулов);
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Шаг 4. Сопоставление данных (Заполнение ссылок)

Теперь у нас есть две таблицы: исходная ТаблицаДанных (из Excel) и ТЗРезультатЗапроса (из базы данных). Нам нужно перенести ссылки из второй в первую, сопоставляя их по полю Артикул. Для ускорения поиска в большой таблице мы обязательно добавим индекс по колонке поиска.


Процедура СопоставитьДанные(ТаблицаДанных, ТЗРезультатЗапроса)
    // Индексируем для быстрого поиска методом НайтиСтроки
    ТЗРезультатЗапроса.Индексы.Добавить("Артикул");
    
    Для Каждого СтрокаЭксель Из ТаблицаДанных Цикл
        ПараметрыПоиска = Новый Структура("Артикул", СтрокаЭксель.Артикул);
        НайденныеСтроки = ТЗРезультатЗапроса.НайтиСтроки(ПараметрыПоиска);
        
        Если НайденныеСтроки.Количество() > 0 Тогда
            // Записываем ссылку в нашу типизированную колонку
            СтрокаЭксель.НоменклатураСсылка = НайденныеСтроки[0].Ссылка;
        Иначе
            Сообщить("Не найдена номенклатура с артикулом: " + СтрокаЭксель.Артикул);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Оптимизированный метод: Использование Временных Таблиц

Рассмотрим еще более профессиональный способ решения задачи, который часто применяется в крупных конфигурациях. Вместо ручного перебора таблиц в цикле, мы можем передать всю ТаблицуДанных в запрос, поместить её во временную таблицу и соединить со справочником средствами SQL. Это обеспечит максимальную производительность.

Разберем алгоритм по шагам:

  1. Передаем ТаблицуЗначений в запрос.
  2. Используем менеджер временных таблиц.
  3. Выполняем ЛЕВОЕ СОЕДИНЕНИЕ по полю Артикул.

МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Передаем ТЗ в запрос
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_ДанныеЭксель ИЗ &ТЗ_Эксель КАК ТЗ_Эксель";
Запрос.УстановитьПараметр("ТЗ_Эксель", ТаблицаДанных);
Запрос.Выполнить();

// Основной запрос на сопоставление
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВТ.Артикул,
    |   ВТ.Количество,
    |   ЕСТЬNULL(СпрНоменклатура.Ссылка, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК НоменклатураСсылка
    |ИЗ
    |   ВТ_ДанныеЭксель КАК ВТ
    |       ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНоменклатура
    |       ПО ВТ.Артикул = СпрНоменклатура.Артикул";

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

Важный нюанс: Чтобы передать ТаблицуЗначений в запрос, её колонки должны быть строго типизированы. Если после ПостроителяЗапроса у вас остались колонки с неопределенным типом, запрос выдаст ошибку. Именно поэтому предварительная типизация, которую мы обсуждали в Шаге 2, критически важна.

Шаг 5. Заполнение документа

Когда мы получили итоговую таблицу, в которой есть колонка НоменклатураСсылка с типом СправочникСсылка.Номенклатура, заполнение документа становится тривиальной задачей. Если имена колонок в вашей ТЗ совпадают с именами реквизитов в табличной части (ТЧ) документа, можно использовать метод Загрузить().

Посмотрим на пример заполнения вручную для большей гибкости:


Для Каждого СтрокаТЗ Из ТаблицаРезультат Цикл
    Если ЗначениеЗаполнено(СтрокаТЗ.НоменклатураСсылка) Тогда
        НоваяСтрока = ДокументОбъект.Товары.Добавить();
        НоваяСтрока.Номенклатура = СтрокаТЗ.НоменклатураСсылка;
        НоваяСтрока.Количество = СтрокаТЗ.Количество;
        // Автоматический расчет цены и суммы, если это предусмотрено логикой конфигурации
        // ОбработкаРедактированияНоменклатуры(НоваяСтрока); 
    КонецЕсли;
КонецЦикла;

Подведение итогов

Мы проанализировали процесс загрузки данных от момента чтения файла Excel до заполнения документа 1С. Основные выводы, которые помогут вам избежать ошибок:

Следуя этим правилам, вы сможете создавать надежные и быстрые механизмы импорта данных, которые корректно обрабатывают объекты 1С и экономят ресурсы системы.

← На главную