Проверялось на: Бухгалтерия Предприятия 2.0.65.3, Зарплата и Управление Персоналом 2.5.98.1.
В рамках проекта по переводу клиента с Инфо-Предприятие 1.91 на 1С:БП2.0+1С:ЗУП2.5 возникла задача по автоматическому переносу основных справочников. Готовое решение не нашёл, поэтому написал данную обработку.
Обработка умеет загружать справочники Контрагенты, Номенклатура, ОсновныеСредства и ФизическиеЛица как из DB-файлов Инфо-Предприятие (СУБД Paradox), так и из DB-файлов, сохранённых в формате MS Excel (XLS/XLSX-файлов).
Чтение DB-файлов работает на порядок быстрее, чем XLS/XLSX, но у меня оно отработало не на всех компьютерах, на которых пробовал запускать обработку. Не знаю точно, действительно ли это оказало влияние, но на тех компьютерах, на которых чтение DB-файлов отработало, был установлен BDE.
Теперь немного про код самой обработки.
Была написана пара универсальных функций для чтения данных: 1) из DB-файла (через ADO) и 2) из файла Excel. Обе функции в качестве параметра получают полное имя файла и возвращают таблицу значений, которая содержит все данные, прочитанные из этого DB-/XLS(X)-файла. Имена колонок возвращаемой таблицы значений совпадают с именами колонок DB-файла. В случае чтения из файла MS Excel, имена колонок берутся из первой строки таблицы.
Функция чтения из DB-файла в таблицу значений:
Функция ПолучитьТаблицуИзФайлаПарадокс(ИмяФайла)
ТЗ = Новый ТаблицаЗначений;
Файл = Новый Файл(ИмяФайла);
СтрокаПодключения = "Driver={Microsoft Paradox Driver (*.db )};DefaultDir=" + Файл.Путь + ";CollatingSequence=ASCII";
ПодключениеАДОДБ = Новый COMОбъект("ADODB.Connection");
ПодключениеАДОДБ.Open(СтрокаПодключения);
ТекстЗапроса = "SELECT * FROM " + Файл.ИмяБезРасширения;
ВыборкаАДОДБ = ПодключениеАДОДБ.Execute(ТекстЗапроса);
ВыборкаАДОДБ.MoveFirst();
КолвоКолонок = ВыборкаАДОДБ.Fields.Count;
Если КолвоКолонок = 0 Тогда
ВызватьИсключение "Ошибка чтения файла " + ИмяФайла + ": таблица не содержит колонок!";
КонецЕсли;
СоответствиеКолонок = Новый Соответствие;
Для НомКол = 0 По КолвоКолонок - 1 Цикл
ИмяКолонки = ВыборкаАДОДБ.Fields(НомКол).Name;
ТЗ.Колонки.Добавить(ИмяКолонки);
СоответствиеКолонок.Вставить(НомКол, ИмяКолонки);
КонецЦикла;
Пока НЕ ВыборкаАДОДБ.EOF Цикл
НоваяСтрока = ТЗ.Добавить();
Для НомКол = 0 По КолвоКолонок - 1 Цикл
НоваяСтрока[СоответствиеКолонок.Получить(НомКол)] = ВыборкаАДОДБ.Fields(НомКол).Value;
КонецЦикла;
ВыборкаАДОДБ.MoveNext();
КонецЦикла;
Возврат ТЗ;
КонецФункции
Функция чтения из XLS/XLSX-файла в таблицу значений:
Функция ПолучитьТаблицуИзФайлаЕксель(ИмяФайла)
ТЗ = Новый ТаблицаЗначений;
Ексель = Новый COMОбъект("Excel.Application");
КнигаЕксель = Ексель.Workbooks.Open(ИмяФайла, Ложь, Истина);
ЛистЕксель = КнигаЕксель.Worksheets(1);
// читаем первую строку с заголовками столбцов
НомКол = 1;
СоответствиеКолонок = Новый Соответствие;
Пока Истина Цикл
ТекЗнач = СокрЛП(ЛистЕксель.Cells(1, НомКол).Value);
Если ПустаяСтрока(ТекЗнач) Тогда
Прервать;
КонецЕсли;
ТЗ.Колонки.Добавить(ТекЗнач);
СоответствиеКолонок.Вставить(НомКол, ТекЗнач);
НомКол = НомКол + 1;
КонецЦикла;
КолвоКолонок = ТЗ.Колонки.Количество();
Если КолвоКолонок = 0 Тогда
ВызватьИсключение "Ошибка чтения таблицы Excel " + ИмяФайла + ": в первой строке не найдены заголовки столбцов!";
КонецЕсли;
НомСтр = 2;
Пока Истина Цикл
ЕстьДанные = Ложь;
СтруктураДанных = Новый Структура;
Для НомКол = 1 По КолвоКолонок Цикл
ИмяКолонки = СоответствиеКолонок.Получить(НомКол);
ТекЗнач = ЛистЕксель.Cells(НомСтр, НомКол).Value;
Если НЕ ПустаяСтрока(ТекЗнач) Тогда
ЕстьДанные = Истина;
КонецЕсли;
СтруктураДанных.Вставить(ИмяКолонки, ТекЗнач);
КонецЦикла;
Если НЕ ЕстьДанные Тогда
Прервать;
КонецЕсли;
НоваяСтрока = ТЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтруктураДанных);
НомСтр = НомСтр + 1;
КонецЦикла;
Возврат ТЗ;
КонецФункции
Для унификации вызвова функции чтения данных используется функция ПолучитьТаблицуИзФайла(), которая в зависимости от расширения файла вызывает одну из вышеприведённых функций:
Функция ПолучитьТаблицуИзФайла(ИмяФайла)
Попытка
Файл = Новый Файл(ИмяФайла);
Если Лев(ВРег(Файл.Расширение), 4) = ".XLS" Тогда
Возврат ПолучитьТаблицуИзФайлаЕксель(ИмяФайла);
ИначеЕсли ВРег(Файл.Расширение) = ".DB" Тогда
Возврат ПолучитьТаблицуИзФайлаПарадокс(ИмяФайла);
Иначе
Сообщить("ПолучитьТаблицуИзФайла(): загрузка из файла " + ИмяФайла
+ " невозможна! Неизвестный тип файла: " + Файл.Расширение + "!", СтатусСообщения.Важное);
КонецЕсли;
Исключение
Сообщить("Ошибка загрузки данных из файла " + ИмяФайла + ": " + ОписаниеОшибки(), СтатусСообщения.Внимание);
КонецПопытки;
Возврат Неопределено;
КонецФункции
Далее на основе прочитанных данных создаются необходимые элементы справочников и заполняются данные в регистрах сведений.
Для примера рассмотрим загрузку справочника ФизическиеЛица. При нажатии соответствующей кнопки "Загрузить" (см.скриншот) вызывается процедура-обработчик КнопкаЗагрузитьСотрудникиНажатие():
Процедура КнопкаЗагрузитьСотрудникиНажатие(Элемент)
ИмяФайла = ИмяФайлаСотрудники;
НазваниеФайла = "Сотрудники";
Если НЕ ПроверитьИмяФайла(ИмяФайла, НазваниеФайла) Тогда
Возврат;
КонецЕсли;
Сообщить("Начало загрузки: " + ТекущаяДата());
Состояние("Чтение данных из файла...");
ТаблицаДанных = ПолучитьТаблицуИзФайла(ИмяФайла);
Колво = 0;
КолвоВсего = ТаблицаДанных.Количество();
Для Каждого СтрокаТаблицы Из ТаблицаДанных Цикл
ОбработкаПрерыванияПользователя();
Колво = Колво + 1;
ПроцентЗагружено = Цел(Колво / КолвоВсего * 100);
Состояние("[" + Строка(ПроцентЗагружено) + "%] " + СтрокаТаблицы.famSotr);
ЗагрузитьФизЛицо(СтрокаТаблицы, ФлагПерезаписыватьСуществующие);
КонецЦикла;
Сообщить("Окончание загрузки: " + ТекущаяДата());
КонецПроцедуры
В этой процедуре вызывается ранее рассмотренная функция ПолучитьТаблицуИзФайла(ИмяФайла), которая возвращает таблицу значений с данными для загрузки, а затем в цикле по строкам полученной таблицы значений вызывается процедура ЗагрузитьФизЛицо(), которая непосредственно осуществляет создание элемента справочника ФизическиеЛица и сопутствующих ему записей в регистрах сведений (эта процедура работает как в БП2.0, так и в ЗУП2.5):
Процедура ЗагрузитьФизЛицо(Данные, Перезаписывать) Экспорт
Комментарий = СформироватьКомментарий(Данные, "ID");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФизическиеЛица.Ссылка
|ИЗ
| Справочник.ФизическиеЛица КАК ФизическиеЛица
|ГДЕ
| ФизическиеЛица.Комментарий ПОДОБНО &Комментарий";
Запрос.УстановитьПараметр("Комментарий", Комментарий);
РезультатЗапроса = Запрос.Выполнить();
ТекОбъект = Неопределено;
Если НЕ РезультатЗапроса.Пустой() Тогда
Если НЕ Перезаписывать Тогда
Возврат;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ТекОбъект = Выборка.Ссылка.ПолучитьОбъект();
Иначе
ТекОбъект = Справочники.ФизическиеЛица.СоздатьЭлемент();
КонецЕсли;
ТекОбъект.Комментарий = Комментарий;
ФамилияФЛ = СПрописной(СокрЛП(Данные.famSotr));
ИмяФЛ = СПрописной(СокрЛП(Данные.imyaSotr));
ОтчествоФЛ = СПрописной(СокрЛП(Данные.otchSotr));
ФИО = ФамилияФЛ
+ ?(ПустаяСтрока(ИмяФЛ), "", " " + ИмяФЛ)
+ ?(ПустаяСтрока(ОтчествоФЛ), "", " " + ОтчествоФЛ);
ТекОбъект.Наименование = ФИО;
ТекОбъект.ДатаРождения = ПолучитьДатуИзДанных(Данные.dataR22);
// 1с: МестоРожденияСтрокойПослеРедактирования = ""+СокрЛП(Особое)+","+СокрЛП(Город)+","+СокрЛП(Район)+","+СокрЛП(Область)+","+СокрЛП(Страна);
ТекОбъект.МестоРождения = "0," + СокрЛП(Данные.gorodJ2) + "," + СокрЛП(Данные.raiionJ2) + "," + СокрЛП(Данные.nazvRegJ2);
ТекОбъект.ИНН = СокрЛП(Данные.innSotr2);
ТекОбъект.КодИМНС = СокрЛП(Данные.kodGNISotr);
ТекОбъект.СтраховойНомерПФР = СокрЛП(Данные.regNomerPF);
КодПола = Лев(ВРег(СокрЛП(Данные.pol)), 1);
Если КодПола = "Ж" Тогда
ТекОбъект.Пол = Перечисления.ПолФизическихЛиц.Женский;
ИначеЕсли КодПола = "М" Тогда
ТекОбъект.Пол = Перечисления.ПолФизическихЛиц.Мужской;
Иначе
// оно?
ТекОбъект.Пол = Перечисления.ПолФизическихЛиц.ПустаяСсылка();
КонецЕсли;
ТекОбъект.Записать();
ФизЛицоСсылка = ТекОбъект.Ссылка;
// ФИО физ.лиц
ПериодЗаписи = ТекОбъект.ДатаРождения;
НЗ = РегистрыСведений.ФИОФизЛиц.СоздатьНаборЗаписей();
НЗ.Отбор.ФизЛицо.Установить(ФизЛицоСсылка);
НЗ.Отбор.Период.Установить(ПериодЗаписи);
Запись = НЗ.Добавить();
Запись.ФизЛицо = ФизЛицоСсылка;
Запись.Период = ПериодЗаписи;
Запись.Фамилия = ФамилияФЛ;
Запись.Имя = ИмяФЛ;
Запись.Отчество = ОтчествоФЛ;
НЗ.Записать();
// паспортные данные
ДатаВыдачи = ПолучитьДатуИзДанных(Данные.data_vyidachi);
ПериодЗаписи = ?(ЗначениеЗаполнено(ДатаВыдачи), ДатаВыдачи, ТекОбъект.ДатаРождения);
НЗ = РегистрыСведений.ПаспортныеДанныеФизЛиц.СоздатьНаборЗаписей();
НЗ.Отбор.ФизЛицо.Установить(ФизЛицоСсылка);
НЗ.Отбор.Период.Установить(ПериодЗаписи);
Запись = НЗ.Добавить();
Запись.ФизЛицо = ФизЛицоСсылка;
Запись.Период = ПериодЗаписи;
Если ЗначениеЗаполнено(Данные.kodDokumenta) Тогда
Запись.ДокументВид = Справочники.ДокументыУдостоверяющиеЛичность.НайтиПоРеквизиту("КодИМНС", СокрЛП(Данные.kodDokumenta));
КонецЕсли;
Запись.ДокументНомер = СокрЛП(Данные.seriya);
Запись.ДокументДатаВыдачи = ДатаВыдачи;
Запись.ДокументКемВыдан = СокрЛП(Данные.pasportnyie_dannyie);
НЗ.Записать();
// контактная информация
СтруктураАдреса = Новый Структура;
СтруктураАдреса.Вставить("Индекс" , СокрЛП(Данные.indeks));
СтруктураАдреса.Вставить("Регион" , СокрЛП(Данные.nazvReg));
СтруктураАдреса.Вставить("Район" , СокрЛП(Данные.raiion));
СтруктураАдреса.Вставить("Город" , СокрЛП(Данные.gorod));
СтруктураАдреса.Вставить("НаселенныйПункт" , СокрЛП(Данные.nasPunkt));
СтруктураАдреса.Вставить("Улица" , СокрЛП(Данные.ulica));
СтруктураАдреса.Вставить("Дом" , СокрЛП(Данные.dom2));
СтруктураАдреса.Вставить("Корпус" , СокрЛП(Данные.korpus));
СтруктураАдреса.Вставить("Квартира" , СокрЛП(Данные.kvartira22));
МЗ = РегистрыСведений.КонтактнаяИнформация.СоздатьМенеджерЗаписи();
УправлениеКонтактнойИнформацией.ЗаполнитьОбъектРедактированияАдресаПоСтруктуре(МЗ, СтруктураАдреса);
МЗ.Объект = ФизЛицоСсылка;
МЗ.Тип = Перечисления.ТипыКонтактнойИнформации.Адрес;
МЗ.Вид = Справочники.ВидыКонтактнойИнформации.ЮрАдресФизЛица;
МЗ.Записать();
КонецПроцедуры
Проверка существует ли уже такой элемент справочника осуществляется по реквизиту Комментарий. У загружаемых объектов в этом реквизите указывается уникальная строка, возвращаемая функцией СформироватьКомментарий(), которая помимо всего прочего содержит в себе значение ID объекта системы Инфо-предприятие.
Вот код функции СформироватьКомментарий():
Функция СформироватьКомментарий(Данные, ПереченьПолей)
Стр = "#Загружено из Инфо-Предприятие {";
ДанныеКомментария = Новый Структура(ПереченьПолей);
СписокПолей = Новый СписокЗначений;
Для Каждого КлючИЗначение Из ДанныеКомментария Цикл
СписокПолей.Добавить(КлючИЗначение.Ключ);
КонецЦикла;
СписокПолей.СортироватьПоЗначению();
ЗаполнитьЗначенияСвойств(ДанныеКомментария, Данные);
Для Каждого ЭлементСписка Из СписокПолей Цикл
ИмяПоля = ЭлементСписка.Значение;
Стр = Стр + ИмяПоля + "=" + ДанныеКомментария[ИмяПоля] + ";";
КонецЦикла;
Стр = Стр + "}#";
Возврат Стр;
КонецФункции
Вот, собственно, и всё. Процедуры загрузки остальных справочников (Контрагенты, Номенклатура, ОсновныеСредства) аналогичны приведённой здесь процедуре ЗагрузитьФизЛицо(). Если кому-то они будут нужны - пишите, вышлю код.
Весь код под GPLv3.