Загрузка данных из внешних файлов Excel в систему компоновки данных (СКД) — это классическая задача, с которой сталкиваются разработчики при необходимости сопоставить данные из учетной системы с внешними прайсами, планами или отчетами из других баз. Основная сложность заключается в архитектуре управляемого приложения: файл выбирается на клиенте, читается на сервере, а компоновка результата происходит в модуле объекта отчета, куда данные нужно передать в структурированном виде.
В этой статье мы подробно разберем, как реализовать этот механизм, используя временное хранилище, внешние наборы данных и особенности работы с платформенными механизмами 1С.
Прежде всего, нам необходимо организовать выбор файла пользователем на стороне клиента. Поскольку чтение данных напрямую с диска клиента сервером невозможно, мы должны прочитать файл и передать его содержимое в виде двоичных данных.
Рассмотрим пример кода на клиенте. Мы будем использовать ДиалогВыбораФайла для получения пути и последующего преобразования файла в объект ДвоичныеДанные.
&НаКлиенте
Процедура ЗагрузитьФайлExcel(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Excel файлы (*.xlsx, *.xls)|*.xlsx;*.xls";
Диалог.Заголовок = "Выберите файл для загрузки в отчет";
Если Диалог.Выбрать() Тогда
ИмяФайла = Диалог.ПолноеИмяФайла;
ФайлНаДиске = Новый Файл(ИмяФайла);
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
// Передаем данные на сервер для обработки
АдресХранилища = ОбработатьФайлНаСервере(ДвоичныеДанныеФайла, ФайлНаДиске.Расширение);
// Сохраняем адрес во временных свойствах компоновщика настроек
ЭтаФорма.Отчет.КомпоновщикНастроек.ДополнительныеСвойства.Вставить("АдресДанныхExcel", АдресХранилища);
Сообщить("Файл успешно прочитан и готов к формированию отчета!");
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию на сервере. Самый эффективный и современный способ чтения Excel без использования COM-объектов (которые требуют установки Excel на сервер и работают медленно) — это метод Прочитать() объекта ТабличныйДокумент.
Важный нюанс: при загрузке данных из Excel важно сразу типизировать колонки. Если 1С получит «чистую» таблицу из ПостроительЗапроса, типы колонок могут быть неопределенными (строка или число вперемешку), что вызовет ошибку при соединении наборов данных в СКД.
&НаСервере
Функция ОбработатьФайлНаСервере(ДвоичныеДанные, Расширение)
// Создаем временный файл на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(Расширение);
ДвоичныеДанные.Записать(ИмяВременногоФайла);
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(ИмяВременногоФайла);
Исключение
ВызватьИсключение "Не удалось прочитать файл Excel: " + ОписаниеОшибки();
КонецПопытки;
// Удаляем временный файл сразу после чтения
УдалитьФайлы(ИмяВременногоФайла);
// Используем ПостроительЗапроса для быстрого преобразования области в ТЗ
Построитель = Новый ПостроительЗапроса;
// Предполагаем, что данные начинаются с 1-й строки и 1-й колонки
Область = ТабДок.Область(1, 1, ТабДок.ВысотаТаблицы, ТабДок.ШиринаТаблицы);
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(Область);
Построитель.Выполнить();
ТаблицаРезультат = Построитель.Результат.Выгрузить();
// Здесь необходимо выполнить типизацию данных, если это требуется
// Например, принудительно преобразовать колонку 'Артикул' в строку
// и колонку 'Количество' в число.
// Помещаем готовую ТЗ во временное хранилище
// Привязываем к уникальному идентификатору формы, чтобы данные жили до закрытия формы
Возврат ПоместитьВоВременноеХранилище(ТаблицаРезультат, ЭтаФорма.УникальныйИдентификатор);
КонецФункции
Для того чтобы СКД «увидела» нашу таблицу, разберем настройку самой схемы компоновки:
ВнешниеДанныеExcel.ТаблицеЗначений.Выясним причину, почему данные не попадают в отчет автоматически. Механизм внешних наборов данных требует ручного вмешательства в процесс компоновки. Это делается в модуле объекта отчета в процедуре ПриКомпоновкеРезультата.
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Достаем адрес временного хранилища из дополнительных свойств
АдресХранилища = "";
КомпоновщикНастроек.ДополнительныеСвойства.Свойство("АдресДанныхExcel", АдресХранилища);
Если ЗначениеЗаполнено(АдресХранилища) Тогда
ТаблицаExcel = ПолучитьИзВременногоХранилища(АдресХранилища);
Иначе
// Если файла нет, создаем пустую таблицу с нужной структурой, чтобы СКД не выдала ошибку
ТаблицаExcel = Новый ТаблицаЗначений;
// Добавьте колонки согласно набору данных
КонецЕсли;
// Формируем структуру внешних наборов данных
ВнешниеНаборы = Новый Структура;
ВнешниеНаборы.Вставить("ВнешниеДанныеExcel", ТаблицаExcel);
// Инициализируем компоновку
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборы, ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Рассмотрим подробнее ситуации, когда стандартный метод Прочитать() может не подойти:
SELECT * FROM [Sheet1$]). Это самый быстрый способ для файлов объемом в сотни тысяч строк.ОбщегоНазначения.ТаблицаЗначенийИзДвоичныхДанных() или специализированные обработки загрузки данных, которые уже содержат механизмы сопоставления колонок.Совет: Если вы загружаете данные для сопоставления с номенклатурой, не пытайтесь искать ссылки (Справочники.Номенклатура.НайтиПоКоду) в цикле при обходе таблицы Excel. Это создаст огромную нагрузку на базу. Вместо этого передайте ТЗ в запрос как параметр, создайте временную таблицу и выполните соединение с физической таблицей справочника одним запросом. Это ускорит работу в десятки раз.
Таким образом, мы проанализировали полный цикл передачи данных: от выбора файла пользователем до вывода готового отчета СКД. Использование ДополнительныеСвойства компоновщика — это наиболее чистый способ передачи контекста из формы в модуль объекта, сохраняющий типизацию и структуру данных.