gifts2017

Полезные процедуры по работе с СКД и табличными документами (часть 2)

Опубликовал Пишу код как картины (yurii_host) в раздел Программирование - Практика программирования

Еще несколько универсальных процедур

Прошлая публикация по СКД показала, что тема актуальная, поэтому решил написать еще одну статью с универсальными процедурами для построения отчетов из моей копилки.

1. Работа с расшифровками. Получение значений группировок для текущей ячейки

На данную тему уже есть публикации. Но универсальных процедур на эту тему я не нашел. 

Имеем отчет следующего вида

Когда пользователь кликает по ячейке требуется:

  • Определить, например, Подразделение, соответствующее текущей ячейке
  • Определить значения всех группировок, соответствующих текущей ячейке

Предлагаемые процедуры решают данную задачу за несколько строк, например, так:

Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	ОчиститьСообщения();
	
	// 1
	ТекущееПодразделение = ПолучитьЗначениеПоИмениГруппировкиВРасшифровке(
								"Подразделение", 
								ДанныеРасшифровки, 
								Расшифровка);
	Сообщить("Подразделение : " + ТекущееПодразделение);
	
	// 2
	ПоляРасшифровкиВВидеТаблицы = ПолучитьПоляРасшифровкиВВидеТаблицы(
								Расшифровка, 
								ДанныеРасшифровки);
	Для Каждого Строка Из ПоляРасшифровкиВВидеТаблицы Цикл
		ТекстСообщения = 
			"Уровень: " + Строка.Уровень + ";	" + 
			"Поле: " + Строка.Поле + ";	" + 
			"Значение: " + Строка.Значение + ";	";
			
			Сообщить(ТекстСообщения);
	КонецЦикла;
	
КонецПроцедуры

При выполнении данного кода получим примерно следующий вывод

Подразделение : Пискаревка

Уровень: 0;     Поле: Количество;       Значение: ;	
Уровень: 1;     Поле: Склад;            Значение: Розничный;	
Уровень: 2;     Поле: Подразделение;    Значение: Пискаревка;	
Уровень: 3;     Поле: Регион;           Значение: Северный р-н;	
Уровень: 1;     Поле: ВидТовара;        Значение: Аксессуары;	
Уровень: 1;     Поле: Товар;            Значение: Термос;

Отмечу несколько моментов:

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

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

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

2. Получение области шапки в отчете СКД

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

Рассмотрим типовую задачу. Есть отчет следующего вида.


Требуется все заголовки в шапке выровнять по центру. Данную задачу можно выполнить с помощью макетов СКД. Но в этом случае возникают проблемы, когда пользователь изменяет состав и порядок полей в отчете. Намного проще решить эту задачу при постобработке результата:

Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	ВывестиСКДВТабличныйДокумент(
					СхемаКомпоновкиДанных, 
					ДокументРезультат, 
					КомпоновщикНастроек);
	
	МассивОбластейШапок = ПолучитьМассивОбластейШапокТабличногоДокументаСКД(ДокументРезультат);
	МассивОбластейШапок[0].ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
	
КонецПроцедуры
Сами процедуры
Функция ПолучитьМассивОбластейШапокТабличногоДокументаСКД(ДокументРезультат) Экспорт
	МассивОбластей = Новый Массив;
	
	Для НомерСтроки = 1 По ДокументРезультат.ВысотаТаблицы Цикл
		Если НЕ ЭтоЯчейкаШапки(ДокументРезультат.Область(НомерСтроки, 1)) Тогда
			Продолжить;
		КонецЕсли;
		
		КоординатыПравогоНижнегоУглаШапки = ПолучитьКоординатыПравогоНижнегоУглаШапки(ДокументРезультат, НомерСтроки);
		МассивОбластей.Добавить(
				ДокументРезультат.Область(
							НомерСтроки,
							1, 
							КоординатыПравогоНижнегоУглаШапки.Верх,
							КоординатыПравогоНижнегоУглаШапки.Лево )
								);
		
		НомерСтроки = КоординатыПравогоНижнегоУглаШапки.Верх;
	КонецЦикла;
	
	Возврат МассивОбластей;
КонецФункции
Функция ЭтоЯчейкаШапки(Ячейка)
	Возврат Ячейка.РежимИзмененияРазмераКолонки = РежимИзмененияРазмера.БыстроеИзменение;
КонецФункции
Функция ПолучитьКоординатыПравогоНижнегоУглаШапки(ТабличныйДокумент, ПерваяСтрокаШапки)
	
	ПоследняяСтрокаШапки = ПерваяСтрокаШапки;
	Для НомерСтроки = ПерваяСтрокаШапки+1 По ТабличныйДокумент.ВысотаТаблицы Цикл
		Если НЕ ЭтоЯчейкаШапки(ТабличныйДокумент.Область(НомерСтроки,1)) Тогда
			Прервать;
		КонецЕсли;
		ПоследняяСтрокаШапки = НомерСтроки;
	КонецЦикла;
	
	Для НомерКолонки = -ТабличныйДокумент.ШиринаТаблицы По -1 Цикл
		Если ЭтоЯчейкаШапки(ТабличныйДокумент.Область(ПерваяСтрокаШапки, -НомерКолонки)) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Новый Структура("Верх,Лево", ПоследняяСтрокаШапки, -НомерКолонки);
	
КонецФункции

3. Печать нескольких табличных документов с различными параметрами.

Предлагаемая процедура помещает массив табличных документов в один файл PDF с использованием объекта ПакетОтображаемыхДокументов. Такой подход удобно использовать, когда, например, нужно несколько отчетов собрать в один файл, причем для каждого необходимо использовать отдельный масштаб 

Процедура ОбъединитьМассивТабличныхДокументовВФайлПДФ(МассивТабличныхДокументов, ИмяФайла) Экспорт
	
	Пакет = Новый ПакетОтображаемыхДокументов;
	
	Для каждого ТабличныйДокумент из МассивТабличныхДокументов Цикл
		ЭлементПакета = Пакет.Состав.Добавить();
		
		ДокументРезультат = Новый ТабличныйДокумент;
		ДокументРезультат.Вывести(ТабличныйДокумент); // если не делать такой финт, то в некоторых случаях программа падает с системной ошибкой
		ЗаполнитьЗначенияСвойств(ДокументРезультат, ТабличныйДокумент);
		
		ЭлементПакета.Данные = ПоместитьВоВременноеХранилище(ДокументРезультат);
	КонецЦикла;
	
	Пакет.ЗаписатьФайлДляПечати(ИмяФайла);
	
КонецПроцедуры

Заключение

Все процедуры тестировались на версии платформы 8.3. 

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

Уточнение 

Хочу особо отметить следующее: в этой и предыдущей статье я привел несколько процедур по постобработке табличного документа СКД. НО я также, как и остальные негативно высказавшиеся комментаторы стараюсь и очень рекомендую всем разработчикам ПО МАКСИМУМУ задействовать возможности СКД, и не использовать доработку кодом, если можно обойтись без этого. 

Постобработка табличного документа СКД - это зло. ОДНАКО, существуют случаи, когда без нее не обойтись. И тогда постобработка уже становится не "злом", а "крайней мерой". В нашей среде принято ссылаться на популярных авторов, которые описывают возможности СКД. Но при этом упускается из вида, что эти возможности покрывают не все возможные случаи, с которыми приходится сталкиваться на практике. Когда напишешь 30-40 отчетов на СКД для капризных клиентов, понимаешь что у этого инструмента есть ограничения. Так вот эти ограничения НИГДЕ не описаны. Сталкиваясь с ними каждый разработчик начинает придумывать свой велосипед. Я постарался восполнить данный пробел и описал решения таких задач, которые нельзя (или очень трудоемко) решать настройками. Существенным недостатком моих двух публикаций является то, что я предлагаю решение проблемы без ее подробного описания. Из-за этого возникает ряд непониманий со стороны других разработчиков, которые с данными проблемами не сталкивались, или им удавалось их обойти (но не  решить)

Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
Пример к публикации 8.3 (ОФ+УФ).erf
.erf 18,75Kb
26.09.16
16
.erf 18,75Kb 16 Скачать

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Дмитрий (r0610201) 27.09.16 08:51
Выровнять заголовки по центру можно вообще без кода при помощи условного оформления.
2. Пишу код как картины (yurii_host) 27.09.16 09:15
(1) r0610201, спасибо за комментарий. Но предполагаю, что вы невнимательно прочитали публикацию.
Существует ряд случаев, когда условным оформлением это сделать проблематично. Об этом сказано в статье.
German_Tagil; +1 Ответить
3. John Bolshakov (soulsteps) 27.09.16 11:14
В этой процедуре

Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ОчиститьСообщения();

// 1
ТекущееПодразделение = ПолучитьЗначениеПоИмениГруппировкиВРасшифровке(
"Подразделение",
ДанныеРасшифровки,
Расшифровка);
Сообщить("Подразделение : " + ТекущееПодразделение);

// 2
ПоляРасшифровкиВВидеТаблицы = ПолучитьПоляРасшифровкиВВидеТаблицы(
Расшифровка,
ДанныеРасшифровки);
Для Каждого Строка Из ПоляРасшифровкиВВидеТаблицы Цикл
ТекстСообщения =
"Уровень: " + Строка.Уровень + "; " +
"Поле: " + Строка.Поле + "; " +
"Значение: " + Строка.Значение + "; ";

Сообщить(ТекстСообщения);
КонецЦикла;

КонецПроцедуры

не обнаружил передачу (локальное определение) переменной ДанныеРасшифровки? Хотя идет обращение по коду:
ТекущееПодразделение = ПолучитьЗначениеПоИмениГруппировкиВРасшифровке(
"Подразделение",
ДанныеРасшифровки,
Расшифровка);
Обработку не скачивал, смотрю только код. Если это какая-то глобальная переменная или параметр формы, просьба уточнить, возможно ДанныеРасшифровки = Расшифровка?
4. Пишу код как картины (yurii_host) 27.09.16 11:18
(3) soulsteps,
Расширение формы отчета (Report form extension)
ДанныеРасшифровки (DetailsData)
Использование:

Чтение и запись.
Описание:

Тип: ДанныеРасшифровкиКомпоновкиДанных.
Содержит данные расшифровки последнего выполненного отчета.

Доступность:

Толстый клиент.

В тексте приведен пример для обычных форм. В управляемых формах в этой переменной хранится не сам объект, а ссылка на него во временном хранилище. Эти нюансы учтены в приложенном к статье примере
5. John Bolshakov (soulsteps) 27.09.16 13:42
(4) yurii_host, я это и хотел услышать (уточнение))
6. Сергей Ожерельев (Поручик) 27.09.16 19:59
(0) Не обращай внимания на критиканов.
7. Вадим Миляев (PrinzOfMunchen) 29.09.16 05:19
Недавно писал нечто подобное, описанному в первом примере. Только мне надо было получить ВСЕ значения полей группировки и детальных записей (без ресурсов) в виде плоской таблицы, где колонки - это выбранные поля. Знатные извращения...))
8. Андрей Лещанов (Nuuq) 02.11.16 13:13
А есть пример по образу сделать получение области итогов СКД по вертикали и общие по горизонтали - программно добавить туда новое поле и заполнить его?
9. Яков Коган (Yashazz) 07.11.16 21:01
Забавно. Тоже буквально вчера понадобилось выгрести значения текущей расшифровки, всех полей уровня и, естесссно, родительских группировок. Тоже сделал через таблицу значений. Подумывал сериализовать "ДанныеРасшифровки" и поиграться с её xdto, но там не оказалось ничего интересного. Остальное - тоже, в общем, очевидные вещи.

Автор прав, пост-обработка это зло. Но иногда зла не хватает)) Могу от себя добавить, что ввиду непредсказуемости внешнего вида результатного табличного документа делаю такие пост-обработки, привязываясь к специально заданным расшифровкам, перегоняя моксель в его сериализованный вид xml и орудуя уже в нём. Так хоть меньше шансов обломаться, если юзверь перенастроит СКД. Так что рекомендую отказаться от дикости вроде "ВысотаТаблицы" и работать через xpath или объекты DOM. Ей-ей, надёжнее)

10. rasswet (rasswet) 09.11.16 08:07
спасибо, интересные вещи.
11. Игорь Герман (German_Tagil) 10.11.16 20:33
12. Максим Кузнецов (Makushimo) 11.11.16 06:31
13. Андрей Сябренко (AzagTot) 11.11.16 12:00
Спасибо! Очень полезный цикл статей.
А кто знает как можно сдвинуть таблицу на несколько колонок? Чтобы вывод начинался не с первой колонки, а, например, с 3-й. При этом другие таблицы в отчете должны остаться без изменения.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа