Механизм многоколоночной печати (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");
КонецПроцедуры
Развитие
- Сейчас момент переноса на следующую колонку/страницу произвольный. Может быть, для улучшения внешнего вида новые колонки должны по возможности начинаться с вывода группировки.