Механизм многоколоночной печати (1Cv8)

Материал из КинтВики
Перейти к: навигация, поиск


Задача

Иногда возникает задача печати "в несколько колонок", то есть подобно тому как верстаются газеты/журналы.

При этом на одной странице может быть 2-3-.. колонок данных, причем группировки/детальные данные сначала идут сверху вниз по первой колонке, потом продолжаются на второй колонке и так далее.

Вроде бы простая с точки зрения клиента вещь, но встроенными средствами 1С это сделать нельзя. СКД тоже не умеет так делать. Клиенту это нужно для экономии бумаги и создания очень "плотных" документов типа графиков, прайсов и так далее.

Такую печать можно сделать "руками", но код получается очень запутанный и каждый раз новый.

См. также

Механизм

Все кардинально упрощается, если разделить задачу на две части - группировку данных и собственно печать.

Функция группировки получает на вход таблицу данных и создает таблицу значений, подготовленную для печати - для каждой строки уже задан номер колонки, номер страницы, имя группировки.

Процедура печати просто читает построчно подготовленную таблицу и формирует колонки. При смене страницы выводятся все сформированные колонки.

Механизм универсальный, работает с любыми группировками данных и любым количеством колонок.

Код механизма:

// Функция выполняет группировку таблицы данных для многоколоночной печати 
//
// Параметры
// 	тзДанные - 
// 	тзГруппировки -
// 	СтрокНаСтранице -
// 	КолонокНаСтранице -
//
// Возвращаемое значение: 
// 	- сгруппированная таблица данных
//
Функция МногоколоночнаяГруппировкаТаблиц(тзДанные, тзГруппировки, СтрокНаСтранице, КолонокНаСтранице) Экспорт 
	тзРезультат = тзДанные.СкопироватьКолонки();
	тзРезультат.Колонки.Добавить("НомерСтраницы");
	тзРезультат.Колонки.Добавить("НомерКолонки");
	тзРезультат.Колонки.Добавить("НомерСтроки");
	тзРезультат.Колонки.Добавить("НомерСтрокиВКолонке");
	тзРезультат.Колонки.Добавить("ИмяГруппировки");
	
	стПараметры = Новый Структура("СтрокНаСтранице,КолонокНаСтранице,НомерСтроки,НомерКолонки,НомерСтраницы,НомерСтрокиВКолонке",
		СтрокНаСтранице, КолонокНаСтранице, 0, 0, 0, 0);
	
	ВывестиГруппировку(тзГруппировки, 0, тзДанные, тзРезультат, стПараметры);
	
	Возврат тзРезультат;
КонецФункции

Процедура ВывестиГруппировку(тзГруппировки, УровеньГруппировки, тзДанные, тзРезультат, стПараметры)
	Если УровеньГруппировки >= тзГруппировки.Количество() Тогда
		Возврат;
	КонецЕсли; 
	ИмяГруппировки = тзГруппировки[УровеньГруппировки].ИмяГруппировки;
	
	Если ИмяГруппировки = "Заголовок" Тогда
		ВывестиСтрокуГруппировки(тзГруппировки, УровеньГруппировки, Новый Структура, тзРезультат, стПараметры);
		ВывестиГруппировку(тзГруппировки, УровеньГруппировки + 1, тзДанные, тзРезультат, стПараметры);
		Возврат;
	КонецЕсли; 
	
	тзЗначенияГруппировки = тзДанные.Скопировать();
	Если УровеньГруппировки < тзГруппировки.Количество() - 1  Тогда
		тзЗначенияГруппировки.Свернуть(ИмяГруппировки);
	Иначе
		тзЗначенияГруппировки.Сортировать(ИмяГруппировки);
	КонецЕсли; 
	
	Для каждого строкаГруппировка из тзЗначенияГруппировки  Цикл
		тзСтроки = тзДанные.СкопироватьКолонки();
		мзСтроки = тзДанные.НайтиСтроки(Новый Структура(ИмяГруппировки, строкаГруппировка[ИмяГруппировки]));
		Для каждого строка из мзСтроки  Цикл
			новаяСтрока = тзСтроки.Добавить();
			ЗаполнитьЗначенияСвойств(новаяСтрока, строка);
		КонецЦикла;
		ВывестиСтрокуГруппировки(тзГруппировки, УровеньГруппировки, строкаГруппировка, тзРезультат, стПараметры);
		ВывестиГруппировку(тзГруппировки, УровеньГруппировки + 1, тзСтроки, тзРезультат, стПараметры);
	КонецЦикла;
	
КонецПроцедуры

Процедура ВывестиСтрокуГруппировки(тзГруппировки, УровеньГруппировки, строкаГруппировка, тзРезультат, стПараметры)
	новаяСтрока = тзРезультат.Добавить();
	ЗаполнитьЗначенияСвойств(новаяСтрока, строкаГруппировка);
	строкВМакетеГруппировки = тзГруппировки[УровеньГруппировки].КоличествоСтрок;
	
	Если стПараметры.НомерСтрокиВКолонке + строкВМакетеГруппировки > стПараметры.СтрокНаСтранице Тогда
		стПараметры.НомерСтрокиВКолонке = 0;
		стПараметры.НомерКолонки = стПараметры.НомерКолонки + 1;
		Если стПараметры.НомерКолонки = стПараметры.КолонокНаСтранице Тогда
			стПараметры.НомерКолонки = 0;
			стПараметры.НомерСтраницы = стПараметры.НомерСтраницы + 1;
		КонецЕсли; 
	КонецЕсли; 
	
	новаяСтрока.ИмяГруппировки = тзГруппировки[УровеньГруппировки].ИмяГруппировки;
	новаяСтрока.НомерСтраницы = стПараметры.НомерСтраницы;
	новаяСтрока.НомерКолонки = стПараметры.НомерКолонки;
	новаяСтрока.НомерСтрокиВКолонке = стПараметры.НомерСтрокиВКолонке;
	новаяСтрока.НомерСтроки = стПараметры.НомерСтроки;
	
	стПараметры.НомерСтроки = стПараметры.НомерСтроки + 1;
	стПараметры.НомерСтрокиВКолонке = стПараметры.НомерСтрокиВКолонке + строкВМакетеГруппировки;
КонецПроцедуры

// Процедура многоколоночной печати
//
// Параметры
// 	табДок - 
// 	тзГруппировки - 
// 	тзДанные - 
// 	Макет - 
// 	СтрокНаСтранице - 
// 	КолонокНаСтранице - 
// 	стЗаголовок - 
//
Процедура МногоколоночнаяПечать(табДок, тзГруппировки, тзДанные, Макет, СтрокНаСтранице, КолонокНаСтранице, стЗаголовок) Экспорт 
	тзМногоколоночная = МедСервис.МногоколоночнаяГруппировкаТаблиц(тзДанные, тзГруппировки, СтрокНаСтранице, КолонокНаСтранице);
	строкаБарьер = тзМногоколоночная.Добавить();
	строкаБарьер.НомерСтраницы = -1;

	стОбласти = Новый Структура;
	Для каждого строка из тзГруппировки  Цикл
		стОбласти.Вставить(строка.ИмяГруппировки, Макет.ПолучитьОбласть(строка.ИмяГруппировки)); 
	КонецЦикла; 
	
	ОбластьОтступ = Макет.ПолучитьОбласть("Отступ");
	ОбластьШапка = Макет.ПолучитьОбласть("Шапка");

	мзТабКолонки = Новый Массив(КолонокНаСтранице);
	
	Для номер = 0 По КолонокНаСтранице - 1 Цикл
		табКолонка = Новый ТабличныйДокумент;
		мзТабКолонки[номер] = табКолонка;
		Если номер = 0 И тзГруппировки.Найти("Заголовок", "ИмяГруппировки") <> Неопределено Тогда
			Продолжить;
		КонецЕсли; 
		табКолонка.Вывести(ОбластьШапка);
	КонецЦикла; 
	
	табДок.Вывести(ОбластьОтступ);
	НомерСтраницы = 0;
	
	Для каждого строкаДанные из тзМногоколоночная Цикл
		
		Если строкаДанные.НомерСтраницы <> НомерСтраницы Тогда
			Для каждого табКолонка из мзТабКолонки  Цикл
				табДок.Присоединить(табКолонка);
			КонецЦикла; 
			
			НомерСтраницы = строкаДанные.НомерСтраницы;
			Если НомерСтраницы = -1 Тогда
				Прервать;
			КонецЕсли; 
			
			табДок.ВывестиГоризонтальныйРазделительСтраниц();
			табДок.Вывести(ОбластьОтступ);
			
			Для номер = 0 По КолонокНаСтранице - 1 Цикл
				табКолонка = Новый ТабличныйДокумент;
				табКолонка.Вывести(ОбластьШапка);
				мзТабКолонки[номер] = табКолонка;
			КонецЦикла; 
			
		КонецЕсли; 
		
		ИмяГруппировки = строкаДанные.ИмяГруппировки;
		область = стОбласти[ИмяГруппировки];
		
		Если ИмяГруппировки = "Заголовок" Тогда
			Для каждого элемент из стЗаголовок  Цикл
				область.Параметры[элемент.Ключ] = элемент.Значение;
			КонецЦикла;
		Иначе
			ЗаполнитьЗначенияСвойств(область.Параметры, строкаДанные);
		КонецЕсли;
		
		табКолонка = мзТабКолонки[строкаДанные.НомерКолонки];
		табКолонка.Вывести(область);
		
		Если ИмяГруппировки = "Заголовок" И строкаДанные.НомерКолонки = 0 Тогда
			табКолонка.Вывести(ОбластьШапка);
		КонецЕсли;
		
	КонецЦикла; 
	
КонецПроцедуры

Тесты механизма:

Макет, используемый для печати:

Файл:Макет многоколоночной печати 1.png

Процедура Тест_МногоколоночнаяПечать() Экспорт
	тзДанные = Новый ТаблицаЗначений;
	тзДанные.Колонки.Добавить("Дата");
	тзДанные.Колонки.Добавить("Кабинет");
	тзДанные.Колонки.Добавить("ЛечебноеМесто");
	тзДанные.Колонки.Добавить("Сеанс");
	тзДанные.Колонки.Добавить("Пациент");
	
	ДобавитьСеансы(тзДанные, '2010.02.10', "Грязевые ванны", "Ванна 1");
	ДобавитьСеансы(тзДанные, '2010.02.10', "Грязевые ванны", "Ванна 2");
	
	тзГруппировки = СоздатьТЗГруппировки();
	СтрокНаСтранице = 10;
	КолонокНаСтранице = 3;
	
	макет = ПолучитьМакет("ГрафикСеансов");
	
	стЗаголовок = Новый Структура("ГруппаЛечебныхМест,ДатаС,ДатаПо", ссЛечебноеОтделение, Формат('20100101', "ДФ='dd.MM.yy'") , Формат('20100131', "ДФ='dd.MM.yy'"));
	табДок = Новый ТабличныйДокумент;
	МедСервис.МногоколоночнаяПечать(табДок, тзГруппировки, тзДанные, макет, СтрокНаСтранице, КолонокНаСтранице, стЗаголовок);
	
	Если Интерактивно Тогда
		табДок.Показать();
	КонецЕсли; 
	
КонецПроцедуры


Процедура Тест_МногоколоночнаяГруппировкаТаблиц() Экспорт 
	тзДанные = Новый ТаблицаЗначений;
	тзДанные.Колонки.Добавить("Дата");
	тзДанные.Колонки.Добавить("Кабинет");
	тзДанные.Колонки.Добавить("ЛечебноеМесто");
	тзДанные.Колонки.Добавить("Сеанс");
	тзДанные.Колонки.Добавить("Пациент");
	
	ДобавитьСеансы(тзДанные, '2010.02.10', "Грязевые ванны", "Ванна 1");
	ДобавитьСеансы(тзДанные, '2010.02.10', "Грязевые ванны", "Ванна 2");
	
	тзГруппировки = СоздатьТЗГруппировки();
	СтрокЗаголовок = 3; 
	СтрокНаСтранице = 10;
	КолонокНаСтранице = 2;
	
	тзМногоколоночная = МедСервис.МногоколоночнаяГруппировкаТаблиц(тзДанные, тзГруппировки, СтрокНаСтранице, КолонокНаСтранице);
	
	ПроверитьСтроку(тзМногоколоночная, 0, 0, 0, 0, "Заголовок");
	ПроверитьСтроку(тзМногоколоночная, 1, 0, 0, 3, "Дата", '2010.02.10');
	ПроверитьСтроку(тзМногоколоночная, 2, 0, 0, 6, "Кабинет", "Грязевые ванны");
	ПроверитьСтроку(тзМногоколоночная, 3, 0, 0, 7, "ЛечебноеМесто", "Ванна 1");
	ПроверитьСтроку(тзМногоколоночная, 4, 0, 0, 8, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 5, 0, 0, 9, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 6, 0, 1, 0, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 15, 0, 1, 9, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 16, 1, 0, 0, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 23, 1, 0, 7, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 24, 1, 0, 8, "ЛечебноеМесто", "Ванна 2");
	ПроверитьСтроку(тзМногоколоночная, 25, 1, 0, 9, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 26, 1, 1, 0, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 35, 1, 1, 9, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 36, 2, 0, 0, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 44, 2, 0, 8, "Сеанс");
	
КонецПроцедуры

Процедура Тест_МногоколоночнаяГруппировкаТаблиц2() Экспорт 
	тзДанные = Новый ТаблицаЗначений;
	тзДанные.Колонки.Добавить("Дата");
	тзДанные.Колонки.Добавить("Кабинет");
	тзДанные.Колонки.Добавить("ЛечебноеМесто");
	тзДанные.Колонки.Добавить("Сеанс");
	тзДанные.Колонки.Добавить("Пациент");
	
	ДобавитьСеансы(тзДанные, '2010.02.10', "Грязевые ванны", "Ванна 1");
	ДобавитьСеансы(тзДанные, '2010.02.10', "Грязевые ванны", "Ванна 2");
	
	ДобавитьСеансы(тзДанные, '2010.02.11', "Грязевые ванны", "Ванна 1");
	ДобавитьСеансы(тзДанные, '2010.02.11', "Грязевые ванны", "Ванна 2");
	
	тзГруппировки = СоздатьТЗГруппировки();
	
	СтрокНаСтранице = 15;
	КолонокНаСтранице = 2;
	
	тзМногоколоночная = МедСервис.МногоколоночнаяГруппировкаТаблиц(тзДанные, тзГруппировки, СтрокНаСтранице, КолонокНаСтранице);
	
	ПроверитьСтроку(тзМногоколоночная, 0, 0, 0, 0, "Заголовок");
	ПроверитьСтроку(тзМногоколоночная, 1, 0, 0, 3, "Дата", '2010.02.10');
	ПроверитьСтроку(тзМногоколоночная, 2, 0, 0, 6, "Кабинет", "Грязевые ванны");
	ПроверитьСтроку(тзМногоколоночная, 3, 0, 0, 7, "ЛечебноеМесто", "Ванна 1");
	ПроверитьСтроку(тзМногоколоночная, 4, 0, 0, 8, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 10, 0, 0, 14, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 11, 0, 1, 0, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 23, 0, 1, 12, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 24, 0, 1, 13, "ЛечебноеМесто", "Ванна 2");
	ПроверитьСтроку(тзМногоколоночная, 25, 0, 1, 14, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 26, 1, 0, 0, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 40, 1, 0, 14, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 41, 1, 1, 0, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 44, 1, 1, 3, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 45, 1, 1, 4, "Дата", '2010.02.11');
	ПроверитьСтроку(тзМногоколоночная, 46, 1, 1, 7, "Кабинет", "Грязевые ванны");
	ПроверитьСтроку(тзМногоколоночная, 47, 1, 1, 8, "ЛечебноеМесто", "Ванна 1");
	ПроверитьСтроку(тзМногоколоночная, 48, 1, 1, 9, "Сеанс");
	ПроверитьСтроку(тзМногоколоночная, 53, 1, 1, 14, "Сеанс");
	
	ПроверитьСтроку(тзМногоколоночная, 54, 2, 0, 0, "Сеанс");
	
КонецПроцедуры



Функция СоздатьТЗГруппировки()
	тзГруппировки = Новый ТаблицаЗначений;
	тзГруппировки.Колонки.Добавить("ИмяГруппировки");
	тзГруппировки.Колонки.Добавить("КоличествоСтрок");
	
	строка = тзГруппировки.Добавить();
	строка.ИмяГруппировки = "Заголовок";
	строка.КоличествоСтрок = 3;
	
	строка = тзГруппировки.Добавить();
	строка.ИмяГруппировки = "Дата";
	строка.КоличествоСтрок = 3;
	
	строка = тзГруппировки.Добавить();
	строка.ИмяГруппировки = "Кабинет";
	строка.КоличествоСтрок = 1;
	
	строка = тзГруппировки.Добавить();
	строка.ИмяГруппировки = "ЛечебноеМесто";
	строка.КоличествоСтрок = 1;
	
	строка = тзГруппировки.Добавить();
	строка.ИмяГруппировки = "Сеанс";
	строка.КоличествоСтрок = 1;
	
	Возврат тзГруппировки;
КонецФункции

Процедура ПроверитьСтроку(тзМногоколоночная, НомерСтроки, НомерСтраницы, НомерКолонки, НомерСтрокиВКолонке, ИмяГруппировки, ЗначениеГруппировки = Неопределено)
	строка = тзМногоколоночная[НомерСтроки];
	Тестирование.ПроверитьРавенство(НомерСтраницы, строка.НомерСтраницы);
	Тестирование.ПроверитьРавенство(НомерКолонки, строка.НомерКолонки);
	Тестирование.ПроверитьРавенство(НомерСтрокиВКолонке, строка.НомерСтрокиВКолонке);
	Тестирование.ПроверитьРавенство(ИмяГруппировки, строка.ИмяГруппировки);
	Если ЗначениеГруппировки <> Неопределено Тогда
		Тестирование.ПроверитьРавенство(ЗначениеГруппировки, строка[ИмяГруппировки]);
	КонецЕсли; 
КонецПроцедуры


Процедура ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, Сеанс, Пациент = Неопределено)
	строка = тзДанные.Добавить();
	строка.Дата = Дата;
	строка.Кабинет = Кабинет;
	строка.ЛечебноеМесто = ЛечебноеМесто;
	строка.Сеанс = Сеанс;
	Если Пациент <> Неопределено Тогда
		строка.Пациент = Пациент;
	КонецЕсли; 
КонецПроцедуры 

Процедура ДобавитьСеансы(тзДанные, Дата, Кабинет, ЛечебноеМесто)
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "09:00 - 09-30", ссИванов);
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "09:30 - 10-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "10:00 - 10-30", ссПетров);
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "10:30 - 11-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "11:00 - 11-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "11:30 - 12-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "12:00 - 12-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "12:30 - 13-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "13:00 - 13-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "13:30 - 14-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "14:00 - 14-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "14:30 - 15-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "15:00 - 15-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "15:30 - 16-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "16:00 - 16-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "16:30 - 17-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "17:00 - 17-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "17:30 - 18-00");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "18:00 - 18-30");
	ДобавитьСтроку(тзДанные, Дата, Кабинет, ЛечебноеМесто, "18:30 - 19-00");
КонецПроцедуры

Развитие

  • Сейчас момент переноса на следующую колонку/страницу произвольный. Может быть, для улучшения внешнего вида новые колонки должны по возможности начинаться с вывода группировки.