Здравствуйте. Эта публикация является частью учебного процесса, так что не судите строго.
Задача - создать обработку для загрузки данных в учебную конфигурацию из файла с расширением CSV.
Учебная конфигурация - это простая база ведения домашней бухгалтерии.
Данные - это файл выгрузки из аналогичной андроид программы.
Итак приступим к решению.
Шаг 1. Прочитать файл с данными
Создаем обработку. В форме обработки создаем:
- реквизит - строку "Имя Файла"
- и команду "Прочитать".
Для команды "Прочитать" сразу сделаем процедуры ПрочитатьНаКлиенте и ПрочитатьНаСервере и пока отложим их.
Нам необходимо, чтобы строка "Имя Файла" позволяла выбрать файл. Для этого создадим дополнительную процедуру
&НаКлиенте
Процедура ИмяФайлаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
ДиалогВыбора = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогВыбора.Заголовок = "Выберите каталог";
Если ДиалогВыбора.Выбрать() Тогда
ИмяФайла = ДиалогВыбора.ПолноеИмяФайла;
КонецЕсли;
каталогCSV=ДиалогВыбора.Каталог;
файлCSV=ИмяФайла;
файлCSV_ansi = ПерекодироватьФайлUTF8_ANSI(каталогCSV,файлCSV);
ИмяФайла=файлCSV_ansi;
КонецПроцедуры
Как видно, начало процедуры вполне стандартное, однако в процессе работы над основной процедурой "Прочитать" я столкнулся с небольшой проблемой - файл был в не той кодировке и не хотел восприниматься алгоритмом, поэтому я добавил сюда строки:
файлCSV=ИмяФайла;
файлCSV_ansi = ПерекодироватьФайлUTF8_ANSI(каталогCSV,файлCSV);
ИмяФайла=файлCSV_ansi;
А также пришлось добавить на форму соответствующую функцию
Функция ПерекодироватьФайлUTF8_ANSI(каталогФайла, файлUTF8)
исходныйФайл = файлUTF8;
файлЧтение = Новый ЧтениеТекста(исходныйФайл,КодировкаТекста.UTF8);
txt=файлЧтение.Прочитать();
файлЧтение.Закрыть();
ФайлANSI = СтрЗаменить(файлUTF8,".","_ansi.");
файлЗапись = Новый ЗаписьТекста(ФайлANSI,КодировкаТекста.ANSI);
файлЗапись.Записать(txt);
файлЗапись.Закрыть();
Возврат ФайлANSI;
КонецФункции
Теперь вернемся к процедуре "Прочитать", а именно к процедуре "ПрочитатьНаСервере" которую мы создали выше. Код процедуры состоит из таких частей:
Первая
ПеременИмяФайла=ИмяФайла;
ЗагружаемыйФайл = Новый ТекстовыйДокумент;
ЗагружаемыйФайл.Прочитать(ПеременИмяФайла);
Разделитель = ",";
Шапка = ЗагружаемыйФайл.ПолучитьСтроку(1);
МассивКол = СтрРазделить(Шапка,",");
Таблица = Новый ТаблицаЗначений;
Для Каждого ИмяСтолбца Из МассивКол Цикл
ИмяБезПробелов = СтрЗаменить(ИмяСтолбца," ","");
Таблица.Колонки.Добавить(ИмяБезПробелов);
КонецЦикла;
Для НомерСтроки=2 по ЗагружаемыйФайл.КоличествоСтрок() Цикл
Строка = ЗагружаемыйФайл.ПолучитьСтроку(НомерСтроки);
МассивКол = СтрРазделить(Строка,Разделитель);
НоваяСтрочка= Таблица.Добавить();
Для НомерСтолбца= 1 по МассивКол.Количество() Цикл
ТекущееЗначение = МассивКол[НомерСтолбца-1];
ИмяКолонки = Таблица.Колонки[НомерСтолбца-1].Имя;
НоваяСтрочка[ИмяКолонки] = ТекущееЗначение;
КонецЦикла;
КонецЦикла;
Здесь мы читаем данные с файла и добавляем их в таблицу значений. Хочу сразу предупредить, что таблица значений не предусматривает одинаковое название колонок. У меня как раз были такие, поэтому я не стал долго мучатся с этим и просто переименовал их. Однако подозреваю, что если чуть подумать и поднапрячься, то можно доработать код и решить эту проблему.
Вторая часть
МассивТипаВыбора = Новый Массив;
МассивТипаВыбора.Добавить(Тип("ТаблицаЗначений"));
ОписаниеТипаВыбора = Новый ОписаниеТипов(МассивТипаВыбора);
МассивРеквизитов = Новый Массив;
МассивРеквизитов.Добавить(Новый РеквизитФормы("ТаблицаCSV",ОписаниеТипаВыбора,));
Для Каждого Ст ИЗ Таблица.Колонки Цикл
Если Ст.Имя="" Тогда Ст.Имя="Пустая";
КонецЕсли;
МассивРеквизитов.Добавить(Новый РеквизитФормы(Ст.Имя, Новый ОписаниеТипов("Строка") , "ТаблицаCSV"));
КонецЦикла;
ИзменитьРеквизиты(МассивРеквизитов);
Таб = Элементы.Добавить("ТаблицаCSV", Тип("ТаблицаФормы"));
Таб.ПутьКДанным = "ТаблицаCSV";
Для Каждого Ст ИЗ Таблица.Колонки Цикл
Рек = Элементы.Добавить("Колонка" + Ст.Имя, Тип("ПолеФормы"), Таб);
Рек.Вид = ВидПоляФормы.ПолеНадписи;
Рек.ПутьКДанным = "ТаблицаCSV" + "." + Ст.Имя;
Рек.Заголовок = Ст.Имя;
КонецЦикла;
ЗначениеВРеквизитФормы(Таблица,"ТаблицаCSV");
Захотелось немного покуражиться и программно добавить таблицу значений на форму. Хотя у этого была и рациональная причина - в дальнейшем не нужно будет каждый раз переделывать код, если файл с данными будет с другим количеством и названием колонок.
Итак, мы прочитали данные, переходим к следующему шагу.
Шаг 2. Загрузить данные в непосредственно в базу
Создаем на форме реквизит "ЧленСемьи", тип "СправочникСсылка.Семья" - если не забыли, у меня учебная база ведения домашней бухгалтерии.
А также команду "ЗагрузитьВБазу" и к ней соответствующие процедуры "ЗагрузитьВБазуНаКлиенте" и "ЗагрузитьВБазуНаСервере".
Рассмотрим код процедуры "ЗагрузитьВБазуНаСервере()".
Он также состоит из нескольких частей. Первая часть кода:
Процедура ЗагрузитьВБазуНаСервере()
ТаблицаСЫМ=РеквизитФормыВЗначение("ТаблицаCSV");
МенеджерВТ=Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| ВнешняяТабл1.date КАК Дата,
| ВнешняяТабл1.account КАК Счет,
| ВнешняяТабл1.category КАК Статья,
| ВнешняяТабл1.amount КАК Сумма,
| ВнешняяТабл1.currency КАК Валюта
|ПОМЕСТИТЬ ВнешняяТаблица
|ИЗ
| &ВнешняяТабл1 КАК ВнешняяТабл1";
Запрос.УстановитьПараметр("ВнешняяТабл1",ТаблицаСЫМ);
РезультатЗапроса = Запрос.Выполнить();
Здесь мы делаем запрос на чтение прочитанных данных на форме. Так как мы уже прочитали данные, то можем присвоить колонкам названия, используемые нами в базе.
Вторая часть кода
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст =
"ВЫБРАТЬ
| ВнешняяТаблица.Дата КАК Дата,
| ВнешняяТаблица.Счет КАК Счет,
| ВнешняяТаблица.Статья КАК Статья,
| ВнешняяТаблица.Сумма КАК Сумма,
| ВнешняяТаблица.Валюта КАК Валюта
|ИЗ
| ВнешняяТаблица КАК ВнешняяТаблица";
Запрос2.УстановитьПараметр("ЧленСемьи", ЧленСемьи);
РезультатЗапроса2 = Запрос2.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса2.Выгрузить();
Для Каждого Строка Из ВыборкаДетальныеЗаписи Цикл
СтркСум=СтрЗаменить(Строка.Сумма," ","");
СтркСС=СтрЗаменить(СтркСум, ".",",");
Если Число(СтркСС) > 0 Тогда
Дох=Документы.ДобавитьДоход.СоздатьДокумент();
Дох.Дата=ДатаИзСтроки(Строка.Дата);
Дох.Счет=ПолучитьСсылкуСтатья(Справочники.Счета, Строка.Счет) ;
Дох.СтатьяДоходов=ПолучитьСсылкуСтатья(Справочники.СтатьиДоходов, Строка.Статья);
Дох.ФИО=ЧленСемьи;
ДохТаб=Дох.ТабличнаяЧасть1.Добавить();
ДохТаб.Сумма=Число(СтркСС);
ДохТаб.Валюта=ПолучитьСсылкуСтатья(Справочники.Валюты, Строка.Валюта);
Дох.Записать(РежимЗаписиДокумента.Запись);
ИначеЕсли Число(СтркСС) < 0 Тогда
Расх=Документы.ДобавитьРасход.СоздатьДокумент();
Расх.Дата=ДатаИзСтроки(Строка.Дата);
Расх.Счет=ПолучитьСсылкуСтатья(Справочники.Счета, Строка.Счет);
Расх.СтатьяЗатрат=ПолучитьСсылкуСтатья(Справочники.СтатьиЗатрат, Строка.Статья);
Расх.ФИО=ЧленСемьи;
Сум=Число(СтркСС);
РасхТаб=Расх.ТабличнаяЧасть1.Добавить();
РасхТаб.Сумма=(0-Сум);
РасхТаб.Валюта=ПолучитьСсылкуСтатья(Справочники.Валюты, Строка.Валюта);
Расх.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
КонецЦикла;
Здесь мы уже непосредственно загружаем прочитанные данные в базу (конечно, второй запрос можно было не делать, но мне так показалось удобнее).
Рассмотрим некоторые строки кода детальнее.
Для Каждого Строка Из ВыборкаДетальныеЗаписи Цикл
СтркСум=СтрЗаменить(Строка.Сумма," ","");
СтркСС=СтрЗаменить(СтркСум, ".",",");
Так как, по сути, мы читали текстовый файл, то строки с суммами пришлось обработать - убрать лишние пробелы и заменить "." на ","
Если Число(СтркСС) > 0 Тогда
Данные, которые мы загружаем, имеют простую структуру: суммы + доходы суммы - расходы. Поэтому простое условие >0 позволило нам разделить данные для прихода и расхода.
Дох=Документы.ДобавитьДоход.СоздатьДокумент();
Программно создаем документ, куда будем заносить данные
Дох.Дата=ДатаИзСтроки(Строка.Дата);
Здесь пришлось повозиться - до меня не сразу дошло, что если программно создаешь документ, то и дату туда надо заносить в формате "год.месяц.день час:мин:сек" пришлось добавить функцию для даты
Функция ДатаИзСтроки(стрДата) экспорт // "01.12.2011" преобразует в '01.12.2011 0:00:00'
Попытка
возврат Дата(Сред(стрДата,7,4)+Сред(стрДата,4,2)+Лев(стрДата,2))
Исключение
возврат '00010101'
КонецПопытки;
КонецФункции
Сразу скажу - спешил, поэтому функцию нашел здесь же на форуме. Сейчас пишу и думаю, что можно было сделать то же самое, используя "СтрРазделить", но код был бы длиннее.
Дох.Счет=ПолучитьСсылкуСтатья(Справочники.Счета, Строка.Счет);
База, по сути, - пустая, и мне не хотелось вручную наполнять справочники, поэтому добавил функцию:
&НаСервере
Функция ПолучитьСсылкуСтатья(СправочникСтатья, СтатьяНаименование)
Перем СтатьяСсылка;
СтатьяСсылка = СправочникСтатья.НайтиПоНаименованию(СтатьяНаименование);
Если СтатьяСсылка = СправочникСтатья.ПустаяСсылка() Тогда
СтатьяНовая = СправочникСтатья.СоздатьЭлемент();
СтатьяНовая.Наименование = СтатьяНаименование ;
СтатьяНовая.Записать();
СтатьяСсылка = СтатьяНовая.Ссылка;
КонецЕсли;
Возврат СтатьяСсылка;
КонецФункции
Так как справочников много, сделал два параметра "СправочникСтатья" и "СтатьяНаименование".
Вроде основное пояснил.
Всем Спасибо.