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