В БСП есть методы загрузки данных в табличную часть объекта. Это довольно запутанный способ, но если разобраться, то можно пользоваться. Просто написано универсально и поэтому сложно. Мы же решим задачу загрузки данных в регистры сведений, по возможности, максимально эффективно. Итак, алгоритм работ такой:
Создаем команду и кнопку на форме списка регистра. По нажатию кнопки открывается форма с возможностью вставить данные из буфера обмена или выбрать файл электронной таблицы. А так же, сохранить макет для импорта данных вручную или из других программ.
Обрабатываем событие закрытия формы откуда узнаем адрес с загружаемыми данными.
Передаем в процедуру загрузки из модуля менеджера регистра.
Создадим регламентное задание, которое будет загружать данные.
Нам нужна общая форма. Пусть она будет называться «эпФормаЗагрузкиРегистров».
Вариант загрузки определяет видимость группы Шаблон или Файл. При создании формы мы выводим шаблон в табличный документ:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ИмяШаблона = Параметры.ИмяШаблона;
Макет = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(Параметры.ИмяВладельца).ПолучитьМакет(Параметры.ИмяМакета);
ШаблонСДанными.Вывести(Макет);
ВариантЗагрузки = 1;
Элементы.ГруппаШаблон.Видимость = Истина;
Элементы.ГруппаФайл.Видимость = Ложь;
КонецПроцедуры
Самое простое – это сохранить шаблон в файл для дальнейшего использования в других программах или заполнения руками.
&НаКлиенте
Процедура КомандаСохранитьШаблон(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайлаДляСохранения", ЭтотОбъект);
ИмяФайла = ИмяШаблона;
ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
ДиалогВыбораФайла.Фильтр = НСтр("ru = 'Книга Excel 97 (*.xls)|*.xls|Книга Excel 2007 (*.xlsx)|*.xlsx|Электронная таблица OpenDocument (*.ods)|*.ods|Табличный документ (*.mxl)|*.mxl'");
ДиалогВыбораФайла.Расширение = "xls";
ДиалогВыбораФайла.МножественныйВыбор = Ложь;
ДиалогВыбораФайла.ИндексФильтра = 0;
ДиалогВыбораФайла.ПолноеИмяФайла = ИмяФайла;
ФайловаяСистемаКлиент.ПоказатьДиалогВыбора(Оповещение, ДиалогВыбораФайла);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораФайлаДляСохранения(Результат, ДополнительныеПараметры) Экспорт
Если Результат <> Неопределено Тогда
ПутьКФайлу = Результат[0];
ВыбранныйФайл = ОбщегоНазначенияКлиентСервер.РазложитьПолноеИмяФайла(ПутьКФайлу);
РасширениеФайла = ОбщегоНазначенияКлиентСервер.РасширениеБезТочки(ВыбранныйФайл.Расширение);
Если ЗначениеЗаполнено(ВыбранныйФайл.Имя) Тогда
Если РасширениеФайла = "xlsx" Тогда
ТипФайла = ТипФайлаТабличногоДокумента.XLSX;
ИначеЕсли РасширениеФайла = "mxl" Тогда
ТипФайла = ТипФайлаТабличногоДокумента.MXL;
ИначеЕсли РасширениеФайла = "xls" Тогда
ТипФайла = ТипФайлаТабличногоДокумента.XLS;
ИначеЕсли РасширениеФайла = "ods" Тогда
ТипФайла = ТипФайлаТабличногоДокумента.ODS;
Иначе
ПоказатьПредупреждение(, НСтр("ru = 'Шаблон файла не был сохранен.'"));
Возврат;
КонецЕсли;
Оповещение = Новый ОписаниеОповещения("ПослеЗаписиТабличногоДокументаВФайл", ЭтотОбъект);
ШаблонСДанными.НачатьЗапись(Оповещение, ПутьКФайлу, ТипФайла);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если мы скопировали данные в шаблон, то загрузка из шаблона выглядит так:
&НаКлиенте
Процедура КомандаЗагрузитьИзШаблона(Команда)
Результат = КомандаЗагрузитьИзШаблонаНаСервере();
Закрыть(Результат);
КонецПроцедуры
&НаСервере
Функция КомандаЗагрузитьИзШаблонаНаСервере()
ИмяРасширения = "xls";
ИмяФайла = ПолучитьИмяВременногоФайла(ИмяРасширения);
ШаблонСДанными.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLS);
Попытка
Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяФайла));
Исключение
Описание = ОписаниеОшибки();
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(Описание);
Возврат Неопределено;
КонецПопытки;
Возврат Новый Структура("Адрес,Расширение", Адрес, ИмяРасширения);
КонецФункции
Загрузка из файла выглядит так:
&НаКлиенте
Процедура КомандаЗагрузитьИзФайла(Команда)
Фильтр = НСтр("ru = 'Книга Excel 97 (*.xls)|*.xls|Книга Excel 2007 (*.xlsx)|*.xlsx|Книга Excel 2007 (*.xlsx)|*.xlsx|Электронная таблица OpenDocument (*.ods)|*.ods|Табличный документ (*.mxl)|*.mxl'");
Расширение = "xls";
ИмяФайла =ОткрытьФайлыНаДиске("Открыть файлы MTC(Шаг 1):", Фильтр, Ложь, Расширение);
Если ИмяФайла = Неопределено Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Файл не выбран. Загрузка прервана.");
Возврат;
КонецЕсли;
ВыбранныйФайл = Новый Файл(ИмяФайла);
Если Не ВыбранныйФайл.Существует() Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Файл не существует: " + ИмяФайла);
Возврат;
КонецЕсли;
ИмяРасширения = ВыбранныйФайл.Расширение;
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(ИмяРасширения);
КопироватьФайл(ИмяФайла, ИмяВременногоФайла);
Попытка
Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяВременногоФайла));
Исключение
Описание = ОписаниеОшибки();
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(Описание);
Возврат;
КонецПопытки;
Закрыть(Новый Структура("Адрес,Расширение", Адрес, ИмяРасширения));
КонецПроцедуры
Функция ОткрытьФайлыНаДиске(Заголовок = "Открыть файлы", Фильтр = "Excel(*.xlsx, *.xls)|*.xlsx; *.xls", МножественныйВыбор = Ложь, Расширение = "xls") Экспорт
ОкноДиалога = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ОкноДиалога.Заголовок = Заголовок;
ОкноДиалога.Фильтр = Фильтр;
ОкноДиалога.Расширение = Расширение;
ОкноДиалога.МножественныйВыбор = МножественныйВыбор;
Результат = Неопределено;
Если ОкноДиалога.Выбрать() Тогда
Если МножественныйВыбор Тогда
Результат = Новый Массив;
Для Каждого ПолноеИмяФайла Из ОкноДиалога.ВыбранныеФайлы Цикл
Результат.Добавить(ПолноеИмяФайла);
КонецЦикла;
Иначе
Результат = ОкноДиалога.ПолноеИмяФайла;
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
Как этим пользоваться? Как уже писали, в форме списка регистра создаем команду загрузки и кнопку.
&НаКлиенте
Процедура КомандаЗагрузить(Команда)
ПараметрыФормы = Новый Структура("ИмяВладельца,ИмяМакета,ИмяШаблона", "РегистрСведений.ИмяЗагружаемогоРегистра", "МакетЗагрузки", "Шаблон загрузки данных");
Оповещение = Новый ОписаниеОповещения("ЗагрузитьДанныеЗавершение", ЭтотОбъект);
ОткрытьФорму("ОбщаяФорма.эпФормаЗагрузкиРегистров", ПараметрыФормы, Оповещение.Модуль,,,, Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузитьДанныеЗавершение (Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
Если ТипЗнч(Результат) = Тип("Структура") Тогда
ЗагрузитьДанныеНаСервере(Результат.Адрес, Результат.Расширение);
КонецЕсли;
Элементы.Список.Обновить();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ЗагрузитьДанныеНаСервере (Адрес, Расширение)
РегистрыСведений. ИмяЗагружаемогоРегистра. ЗагрузитьДанныеНаСервере (Адрес, Расширение);
КонецПроцедуры
Все. Все остальное пишется в модуле менеджера регистра, который загружаем. Здесь очевидно, что нет сопоставления данных загрузки и данных базы. По моему опыту, если программно невозможно без участия пользователя сопоставить данные, то игра не стоит дальнейших свеч и надо прекращать загрузку, затем либо исправлять загружаемые данные, либо менять алгоритм загрузки. Вы же не загружаете десяток записей, правда? Десяток можно и руками вбить. А если данных много, то сопоставлять их руками не эффективно. Макет загрузки может выглядеть как-то вот так:
Что у нас в модуле менеджера регистра?
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
#Область ПрограммныйИнтерфейс
Процедура ЗагрузитьДанныеНаСервере(АдресДанных, РасширениеФайла) Экспорт
ИмяФайла = ПолучитьИмяВременногоФайла(РасширениеФайла);
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресДанных);
ДвоичныеДанные.Записать(ИмяФайла);
ТабличныйДокумент = Новый ТабличныйДокумент;
Попытка
ТабличныйДокумент.Прочитать(ИмяФайла);
Исключение
ОписаниеОшибки = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("ЗагрузкаДанныхРегистра.ИмяЗагружаемогоРегистра", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ОписаниеОшибки);
Возврат;
КонецПопытки;
ТабДокСтраницы = Новый ТабличныйДокумент;
Макет = РегистрыСведений.ИмяНашегоРегистра.ПолучитьМакет("МакетСтраницы");
ТабДокСтраницы.Вывести(МакетСтраницы);
ТабДокСтраницы.ВставитьОбласть(ТабличныйДокумент.Области.ИмяСтраницы, ТабДокСтраницы.Области.ОбластьДанных);
ТабличныйДокумент = ТабДокСтраницы;
ОбластьДанных = ТабличныйДокумент.Область(
6, 1, ТабличныйДокумент.ВысотаТаблицы, ТабличныйДокумент.ШиринаТаблицы);
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ОбластьДанных);
Построитель.Выполнить();
ЗагружаемыеДанные = Построитель.Результат.Выгрузить();
НомерСтроки = 1;
Для Каждого ТекСтр Из ЗагружаемыеДанные Цикл
ДанныеСтолбикаВтекущейСтроке = СокрЛП(ИмяСтолбика);
…
НомерСтроки = НомерСтроки + 1;
КонецЦикла;
КонецПроцедуры
#КонецОбласти
#КонецЕсли
Обратите внимание, что мы сначала сохраняем данные во временный файл из временного хранилища. Этот файл уже на сервере(!) Это важно. Затем, загружаем данные в табличный документ. Затем, мы в табличный документ ТабДокСтраницы копируем данные одной конкретной страницы электронной таблицы. МакетСтраницы выглядит так:
Это просто пустой макет, у которого есть одна самая верхняя область поименованная все равно как. Я назвал «ОбластьДанных». Эта область нужна только для того, чтобы указать куда копировать данные страницы.
Далее, мы создаем область данных импорта. Здесь область начинается с шестой строки (в ней должны быть названия столбиков), и построителем создаем из неё таблицу значений. А уже эту таблицу значений можно загружать. Надо отметить, что имена столбиков ЗагружаемыхДанных лучше посмотреть отладкой. Но, общий принцип прост. Если пробел, то он пропускается, а следующий символ в верхнем регистре, если специальный символ, то подчеркивание. «Мой столбик для импорта» преобразуется в «МойСтолбикДляИмпорта». А вот «Столбик (очень важный), да (!!!)» преобразуется в «Столбик_оченьВажный__Да_____».
Регламентное задание будет загружать данные по расписанию:
Процедура всЗагрузкаДанных() Экспорт
ИмяФайла = Константы.всПутьДляЗагрузкиДанных.Получить();
Если ИмяФайла = "" Тогда
ОписаниеОшибки = "Не задано имя файла для импорта данных!";
ЗаписьЖурналаРегистрации("ИмпортДанных.ИмяРегистра", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
Возврат;
КонецЕсли;
ВыбранныйФайл = Новый Файл(ИмяФайла);
Если Не ВыбранныйФайл.Существует() Тогда
ОписаниеОшибки = "Файл не существует: " + ИмяФайла;
ЗаписьЖурналаРегистрации("ИмпортДанных.ИмяРегистра", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
Возврат;
КонецЕсли;
ИмяРасширения = ВыбранныйФайл.Расширение;
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(ИмяРасширения);
КопироватьФайл(ИмяФайла, ИмяВременногоФайла);
Попытка
Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяВременногоФайла));
Исключение
ОписаниеОшибки = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("ИмпортДанных.ИмяРегистра", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
Возврат;
КонецПопытки;
РегистрыСведений.ИмяНашегоРегистра.ЗагрузитьДанныеНаСервере(Адрес, ИмяРасширения);
КонецПроцедуры
Вот как-то так. Желаю удачи и отсутствия трудовых подвигов в виду отсутствия в их необходимости. Работайте по плану, без авралов и нервотрепки. Заходите ко мне, может еще что узнаете.
#1С #Загрузка данных