Создание файла формата DBF в 1С

Задача: создать силами платформы 1c файл в формате DBF (выгрузка в формате dbf)

Решение: собственно в 1С всё есть. Будем использовать метод XBase. При работе с DBF важно помнить, что это очень старый формат хранения данных, но тем не менее до сих пор используется для различного вида обменов. Его ограничениями являются:

  • длина имени файлов не более 8 символов, поэтому при генерации имени временного файла, не получится использовать функцию ПолучитьИмяВременногоФайла()
  • файл не должен быть больше 2ггб
  • имя колонки не может быть длиннее 10 символов
  • файл создается НЕ в кодировке UTF-8 (он в такую не умеет)
  • файл нужно сначала создать, потом закрыть, потом открыть и записать в него данные

А так, файл создаётся достаточно просто:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	объект.РезультирующиеДанные.Очистить();
		нс=объект.РезультирующиеДанные.Добавить();
		нс.Код_фиас="1112232423";
		нс.НомерКвартиры="43";
		нс.Задолженость="20";
		нс.Переплата="12";
		нс.Услуга="КТВ+Домофон";
		нс.ЛС_База="235465";
		нс.ЛС_ЕПД="223234234";
		нс.ЛС_Электрика="445457223";
		нс.Адрес="г. Вологда,ул Лёнина 3";
		нс.АдресДоставки="г. Сокол, ул Лёнина 4";	
КонецПроцедуры

&НаСервере
Функция ВыгрузитьDBFНаСервере()    
	answer=Новый Структура("хранилище,имяфайла");
	НоваяТаблица = Новый XBase;
    НоваяТаблица.Кодировка = КодировкаXBase.ANSI;
	
    НоваяТаблица.Поля.Добавить("fias","S",50);
	НоваяТаблица.Поля.Добавить("flat","S",10);
	НоваяТаблица.Поля.Добавить("credit","N",10,2);
	НоваяТаблица.Поля.Добавить("debet","N",10,2);
	НоваяТаблица.Поля.Добавить("service","S",50);
	НоваяТаблица.Поля.Добавить("ls_base","S",10);
	НоваяТаблица.Поля.Добавить("ls_epd","S",10);
	НоваяТаблица.Поля.Добавить("ls_electro","S",10);
	НоваяТаблица.Поля.Добавить("address","S",100);
	НоваяТаблица.Поля.Добавить("delivery","S",100);

	имя_фр_файла=Лев(Новый УникальныйИдентификатор(),8)+".dbf";
	ПутьКНовомуDBF = КаталогВременныхФайлов()+"/"+имя_фр_файла;
    НоваяТаблица.СоздатьФайл(ПутьКНовомуDBF); 
    НоваяТаблица.ЗакрытьФайл();	
	
	Таблица = Новый XBase;
    Таблица.ОткрытьФайл(ПутьКНовомуDBF,, Ложь);    	
	
	для каждого стр из объект.РезультирующиеДанные цикл
	   Таблица.Добавить();
 	   Таблица.fias = стр.Код_фиас;
 	   Таблица.credit = стр.НомерКвартиры;
	   Таблица.debet = стр.Задолженость;
	   Таблица.service = стр.Переплата;
	   Таблица.ls_base = стр.Услуга;
	   Таблица.ls_epd = стр.ЛС_База;
	   Таблица.ls_electro = стр.ЛС_ЕПД;
	   Таблица.address = стр.address;
	   Таблица.delivery = стр.АдресДоставки;
 	   Таблица.Записать(); 
	конеццикла;	
	
	Таблица.ЗакрытьФайл();
	
	Двоичное=Новый ДвоичныеДанные(ПутьКНовомуDBF);
	answer.хранилище=ПоместитьВоВременноеХранилище(Двоичное,ЭтаФорма.УникальныйИдентификатор);	
	answer.имяфайла=имя_фр_файла;
	
	возврат answer;	
КонецФункции

&НаКлиенте
Процедура ВыгрузитьDBF(Команда)
	рез=ВыгрузитьDBFНаСервере();
	
		Двоичное=ПолучитьИзВременногоХранилища(рез.хранилище);			
		ИмяФайла = КаталогВременныхФайлов() + рез.имяфайла;		
		Двоичное.Записать(ИмяФайла);
		ЗапуститьПриложение(ИмяФайла);	
КонецПроцедуры
выгрузка в формате dbf

1С: Формирование отчёта в обработке из макета

Несколько статей уже на моём сайте на эту тему есть вывод отчета из макета), но они слегка протухли за прошедшее время, и почему-то отображаются не вполне корректно. Потому повторю.

Сначала нужно создать макет:

вывод отчета из макета

Нарисуем структуру отчета, присвоим имена блокам, обозначим ячейки которые будут заполнятся параметрами.

Далее на клиенте на форму выведем кнопку, и на неё повесим на неё обработчик заполнения отчета и вывода его на экран:

&НаКлиенте
Процедура СформироватьОтчёт(Команда)
	ТабличныйДокумент = Новый ТабличныйДокумент;
	ТабличныйДокумент=ПечатнаяФормаНаСервере();
	ТабличныйДокумент.Показать();
КонецПроцедуры

Формирование и вывод отчета из макета:

&НаСервере
Функция ПечатнаяФормаНаСервере()	                                            
 ТабДок=Новый ТабличныйДокумент;	
 Макет=РеквизитФормыВЗначение("Объект").ПолучитьМакет("МакетУдовлетворённость");
 ОбластьШапка=Макет.ПолучитьОбласть("Шапка");
 ТабДок.Вывести(ОбластьШапка);
   Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
     ОбластьТушка=Макет.ПолучитьОбласть("тушка");
     ОбластьТушка.Параметры.ид=ВыборкаДетальныеЗаписи.ИдентификаторОпроса;
     ТабДок.Вывести(ОбластьТушка);
   КонецЦикла;	
...
возврат ТабДок;
КонецФункции

Фоновая обработка больших данных в 1С с прогресс баром

Пару лет назад уже сталкивался с подобной задачей (Фоновая обработка больших данных в 1С). Чуть погуглил, что изменилось за это время. А фактически ничего. До сих пор для того чтобы показать обычный прогресс бар, приходится использовать велосипеды. Временные хранилища, для того чтобы передать в клиента результат работы фоновой функции/процедуры как нельзя было использовать, так и сейчас нельзя. Прогресс бар, как нельзя было использовать без костылей…так и сейчас нельзя. Ну я конечно не смотрел что в БСП, т.к. зачастую приходится делать дописки где БСП или нет, или она древняя

Общая «шаблонная» схема использования фонового выполнения функций в 1С можно организаовать следующим образом:

  1. Запускаем фоновое задание
  2. На клиенте запускаем периодическое задание которое отлавливает вывод «сообщить» на сервере
  3. В фоновом задании при помощи «сообщить» выводим всякую служебную информацию. Если нужно в «клиент» передать данные — записываем временный файл в формате например json, и имя его, опять же при помощи «Сообщить» передаём на клиент.
  4. По окончании фонового задания, закрываем выполнение периодического задания

Запуск фонового задания:

&НаКлиенте
...
ЗапускФоновойЗадачи();
ПодключитьОбработчикОжидания("ИндикаторВыполненияЗагрузки",1,ложь);
...
&НаСервере	
Процедура ЗапускФоновойЗадачи()
	МассивПараметров = Новый Массив;
	МассивПараметров.Добавить(параметры);
	ФЗ = ФоновыеЗадания.Выполнить("СК_ГР_ДлительныеОперации.СпарситьИсходныеДанныеНаСервер",МассивПараметров);	
	ЭтаФорма.ФоновоеИдентификатор = ФЗ.УникальныйИдентификатор;				
КонецПроцедуры	

Процедура или функция фонового задания обязательно должна находиться в общем модуле:

Функция СпарситьИсходныеДанныеНаСервер(параметры) экспорт;
...
Сообщить("Выполнено 10%");	
....
Сообщить("Выполнено 100%");
ИмяФайла=ПолучитьИмяВременногоФайла("json");
Текст = Новый ЗаписьТекста(ГдеИскать+ИмяФайла, КодировкаТекста.UTF8);
Текст.Записать(json_str);	
Сообщить("Результат:"+ИмяФайла);
...

Периодическая проверка и «отлов» серверного вывода «Сообщить»:

&НаКлиенте
Процедура ИндикаторВыполненияЗагрузки() Экспорт
	пр=ОпроситьФоновые();
	если пр<>неопределено тогда  
		объект.ИндВыполнения=пр;
		Состояние("Выполнено "+пр);
	конецесли;
КонецПроцедуры

&НаКлиенте
Процедура ИндикаторВыполненияЗагрузки() Экспорт
	пр=ОпроситьФоновые();
	если пр<>неопределено тогда  
		объект.ИндВыполнения=пр;
		Состояние("Выполнено "+пр);
	конецесли;
КонецПроцедуры

&НаСервере
Функция ПолучитьСообщенияФЗ(ФЗ, Состояние = Неопределено, УдалятьСообщения = Ложь) Экспорт
	Если Состояние = Неопределено Тогда
		Состояние = ФЗ.Состояние;
	КонецЕсли;
	МассивСообщений = Новый Массив;
	Сообщения = ФЗ.ПолучитьСообщенияПользователю(УдалятьСообщения);
	Если Сообщения <> Неопределено Тогда
		Для Каждого Сообщение Из Сообщения Цикл
			МассивСообщений.Добавить(Сообщение.Текст);
		КонецЦикла;
	КонецЕсли;
	Возврат МассивСообщений;
КонецФункции

&НаСервере
Функция ОпроситьФоновые()
	прог=неопределено;
	ФЗ = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ЭтаФорма.ФоновоеИдентификатор);
	если ФЗ=Неопределено тогда
		ОтключитьОбработчикОжидания("ИндикаторВыполненияЗагрузки");
		возврат ложь;
	конецесли;	
	ФСообщения=ПолучитьСообщенияФЗ(ФЗ,,истина);
	Если ФСообщения.Количество() > 0 Тогда
		Для Каждого Сообщение Из ФСообщения Цикл
			Сообщить(Сообщение);
			если найти(Сообщение,"%")>0 тогда
				прог=Число(СтрЗаменить(СтрЗаменить(Сообщение,"Выполнено ",""),"%",""));			
			конецесли;
			если найти(Сообщение,"Результат:")>0 тогда
				ИмяФайла=СтрЗаменить(Сообщение,"Результат:","");
				Текст = Новый ЧтениеТекста(КаталогВременныхФайлов()+ИмяФайла, КодировкаТекста.UTF8);
				СтрокаJson = JsonВСтруктуру(Текст.Прочитать());	
ОтключитьОбработчикОжидания("ИндикаторВыполненияЗагрузки");
			конецесли;				
		КонецЦикла;
	КонецЕсли;
	возврат прог;
КонецФункции

В результате будет что-то вроде:

Фоновая обработка больших данных в 1С

1С: Ускорение поиска в массиве структур

Задача: есть два массива структур. Один 500 записей, второй — порядка 900 тыс. Нужно для каждой из 500 записей, найти соответствующую запись из второй структуры.

ИсходныеДанные=Новый Структура("субабоненты,реестр_замен",Новый Массив(),Новый Массив(),Новый Массив(),Новый Массив());
  • Массив «субабоненты», заполнен структурами вида «то,лс,ипу,окпу,нс,улица,дом,квартира»
  • Массив «реестр_замен», заполнен структурами вида «лс,нп,улица,дом,квартира,ипу,дата_установки»

Решение 1: ищем и сопоставляем в «лоб»

	найдено=0;
	для каждого суб из ИсходныеДанные.субабоненты цикл
		    поз=поз+1;
			для каждого стр из ИсходныеДанные.реестр_замен цикл			
				если суб.лс=стр.лс тогда
					найдено=найдено+1;		
				конецесли;	
			конеццикла;
	конеццикла;	
	сообщить("-сопоставлено лс: "+найдено);	

Замеряем время выполнения…и устаём ждать.. Поиск и сопоставление длится по крайне мере несколько часов..

Решение 2:

А зачем нам в массиве субабонентов держать те данные, которых нет? Правильно, не зачем. Поэтому из массива «реестр_замен», сначала вычленим список л/с, и положим его в отдельный массив. И далее при заполнении из файла массива субабонентов, нужно штатно (функция Найти) проверять нужна такая строчка или нет в результирующем массиве? Функция «Найти» скажем работает ОЧЕНЬ быстро.

					если ИсходныеДанные.список_лс.Найти(инф.лс)<>неопределено или ИсходныеДанные.список_ипу.Найти(инф.ипу)<>неопределено тогда
						ИсходныеДанные.субабоненты.Добавить(инф);			                                                
					конецесли;

В итоге, в массиве «субабоненты» у нас ровно то количество записей, которое в «реестр_замен «, а вовсе не 900тыс.

И дальше пробуем снова сопоставить:

найдено=0;
	для каждого суб из ИсходныеДанные.субабоненты цикл
		    поз=поз+1;
			для каждого стр из ИсходныеДанные.реестр_замен цикл			
				если суб.лс=стр.лс тогда
					найдено=найдено+1;		
				конецесли;	
			конеццикла;
	конеццикла;	
	сообщить("-сопоставлено лс: "+найдено);	

Скрипт выполнился уже в приемлемые примерно 400 секунд

Чтение большого файла csv в 1С

Куда ни глянь, в интернете чаще всего предлагается обработка и чтение большого файла csv в 1С при помощи загрузки в «текстовыйДокумент». Примерно так:

ТекстДок = Новый ТекстовыйДокумент;
ТекстДок.Прочитать(ИмяВременногоФайлаХар);
Для Индекс = 2 По ТекстДок.КоличествоСтрок() Цикл
СтрокаФайла = ТекстДок.ПолучитьСтроку(Индекс);
конеццикла;

Что в корне не верно, при обработке большого csv файла, так как в этом случае весь файл вычитывается предварительно в память. А она не безразмерная в большинстве случаев. Правильный же способ обработки — построчное чтение файла. Примерно так:

		Текст = Новый ЧтениеТекста(ИмяВременногоФайлаХар, КодировкаТекста.ANSI);
		Стр = Текст.ПрочитатьСтроку();
		Пока Стр <> Неопределено Цикл 
		   Сообщить(Стр);
		   Стр = Текст.ПрочитатьСтроку();
			МассивПодстрок = СтрРазделить(Стр, ";");
			инф=Новый Структура("то,лс,ипу,окпу,нс,улица,дом,квартира");
			инф.то		=СокрЛП(МассивПодстрок[2]);
			инф.лс		=СокрЛП(СтрЗаменить(МассивПодстрок[3],"-",""));
			инф.ипу		=СокрЛП(МассивПодстрок[31]);
			инф.окпу	=СокрЛП(МассивПодстрок[92]);
			инф.нс		=СокрЛП(МассивПодстрок[8]);
			инф.улица	=СокрЛП(МассивПодстрок[9]);
			инф.дом		=СокрЛП(МассивПодстрок[10]);
			инф.квартира=СокрЛП(МассивПодстрок[11]);
			ИсходныеДанные.субабоненты.Добавить(инф);			
		   КонецЦикла; 		
	
1 5 6 7 8 9 37