Каталог решений - По-настоящему свои макеты в отчетах СКД. Исследование процесса компоновки и генерация кода отчета

По-настоящему свои макеты в отчетах СКД. Исследование процесса компоновки и генерация кода отчета

По-настоящему свои макеты в отчетах СКД. Исследование процесса компоновки и генерация кода отчета

В наличии

Как скрестить формирование отчетных данных с помощью СКД и вывод в табличный документ с помощью Макет.ПолучитьОбласть(…) и ТабДок.Вывести(Секция)? А также сделать этот процесс простым и удобным? Об этом в статье ниже.

Категория:

Описание

 

Предыстория, которой не было, но которая вполне могла случиться

Очень Важный Заказчик (ОВЗ) поставил задачу: сделать отчет по начислениям и удержаниям сотрудников с группировками по организациям и подразделениям в разрезе кварталов.

Отчет должен быть реализован на основе вот такого регистра:

 

 

Что может быть проще? Создаем схему компоновки:

 

        

 

и с чувством выполненного долга показываем результат ОВЗ:

 

 

Но к нашему удивлению…

ОВЗ: Что это за $&^*%$% ?!. У меня раньше стояла 7.7, и там мои программисты за полчаса-час сделали такую форму:

 

 

ОВЗ: нужна такая же.

Мы: но у нас СКД — современный стандарт построения отчетов нового поколения…

ОВЗ: Да? почему же получилась такая $&^*%$% ? 

Мы: Ну как же, смотрите — вот отборы, вот структура, варианты и даже условное оформление!

ОВЗ: $#%!   *&$%!   &*$!

ОВЗ: На %#& мне все это надо?

ОВЗ: Мне надо завтра на стол Генеральному положить отчет, а ваш даже на страницу не помещается!

ОВЗ: Так, сроку вам до вечера. Напомнить, сколько мы вам платим? Все, свободны.

 

Итак, получив такой мотивирующий пинок импульс, команда приступила к работе.

Тимлид: какие у нас есть варианты?

Разработчик1: Может отказаться от СКД? Сделаем через запрос с итогами и выборкой с группировками.

Тимлид: Как крайний вариант подойдет, но не хотелось бы отказываться от СКД, надо же ОВЗ как-то приучать к новым технологиям…

Разработчик2: Можно результат компоновки выгрузить в ТЗ или в ДЗ, и потом вывести в документ.

Тимлид: Можно, но таблицу в структуре отчета так выгрузить нельзя. А без неё сложно — надо как-то решать вопрос с разреженностью данных в колонках. Что там говорит коллективный разум Инфостарта? 

Оставим эту команду, и посмотрим, какие у нас есть вообще способы кастомизировать отчеты?

 

Способы кастомизации отчетов:

  • Можно посмотреть в сторону макетов полей и группировок СКД. При их использовании может потребоваться ввести новые элементы в структуру отчета или добавить в набор данных дополнительные строки (изменив текст запроса) или дополнительные поля, например для итогов, а также сделать дополнительные настройки для каждого элемента структуры. Примеры.
  • Можно выполнить постобработку полученного табличного документа — в цикле перебрать строки/ячейки, при необходимости их добавить/удалить/объединить/оформить. 
  • Можно на лету менять макеты в объекте МакетКомпоновкиДанных или ЭлементРезультатаКомпоновкиДанных. Очень хорошо такой подход раскрыт в этой статье.
  • Можно применять комбинацию этих способов.

Но есть и другой путь

Как у нас обычно происходит программное формирование отчета?

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

	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
	ПроцессорВывода.УстановитьДокумент(ДокументРезультат);

	// 1 вариант
	ПроцессорВывода.Вывести(ПроцессорКомпоновки);
	// 2 вариант
	ПроцессорВывода.НачатьВывод();	
	ЭлементРезультата = ПроцессорКомпоновки.Следующий();
	Пока ЭлементРезультата <> Неопределено  Цикл
		ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
		ЭлементРезультата = ПроцессорКомпоновки.Следующий();
	КонецЦикла; 
	ПроцессорВывода.ЗакончитьВывод();
КонецПроцедуры

И вот во втором варианте, внутри цикла вывода элемента результата компоновки, мы можем использовать не стандартный вывод через ПроцессорВывода, а старый добрый вывод в табличный документ на основе макета с именованными секциями.

	ЭлементРезультата = ПроцессорКомпоновки.Следующий();
	Пока ЭлементРезультата <> Неопределено  Цикл
		
		Секция = МакетОтчета.ПолучитьОбласть(ИмяСекции);
		Секция.Параметры.Заполнить(ЗначенияПараметров);	
		// 1
		ДокументРезультат.Вывести(Секция);
		// или 2
		ДокументРезультат.Присоединить(Секция);

		ЭлементРезультата = ПроцессорКомпоновки.Следующий();
	КонецЦикла; 

Дело за малым — определить, какие секции в какой момент выводить и какими значениями заполнять их параметры.

Чем нам здесь может помочь ЭлементРезультата?

 

 

Макет содержит внутреннее имя макета, выводимого в табличный документ, с его помощью мы можем определить ту часть отчета, которая в данный момент выводится. ПроцентВывода нас пока не интересует, а ТипЭлемента и РасположениеВложенныхЭлементов пригодятся — с их помощью определим, надо ли секцию выводить с новой строки, или же присоединить к текущей. ЗначенияПараметров, как несложно догадаться, содержит коллекцию параметров для вывода элемента отчета:

 

 

В нашем случае "П1" — сумма по конкретному сотруднику за определенный период, "П2" — ИдентификаторРасшифровкиКомпоновкиДанных.

Код при этом может выглядеть примерно так:

Если ЭлементРезультата.Макет = "Макет38" Тогда			
	Секция = МакетОтчета.ПолучитьОбласть("Сотрудник|Период");
	Секция.Параметры.Сумма = ЗначениеПараметра(ЭлементРезультата, "П1");	
	Секция.Параметры.Расшифровка = ЗначениеПараметра(ЭлементРезультата, "П2");	
	ДокументРезультат.Присоединить(Секция);
КонецЕсли; 

где функция ЗначениеПараметра:

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

Чтобы не делать каскад условий для проверки имен макета, можно заранее определить список соответствий имен макетов СКД ("Макет38") и имен секций макета отчета ("Сотрудник|Период"). Заодно там же можно указать и признак вывода с новой строки. А чтобы не прописывать установку отдельных параметров секции макета, можно эти параметры заполнять из заранее созданной структуры (конечно же в этом случае параметры секций макета должны называться "П1", "П2" и т.д.).

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

 

 

Вот тот минимальный объем кода, который сформирует нам табличный документ с отчетом, и который можно в дальнейшем дорабатывать и усложнять. 

Значения расшифровки

При работе со значениями параметров макета компоновки данных выясняется одна неожиданная, но вполне объяснимая особенность. Если со значениями примитивных типов все нормально, то данные ссылочных типов хранятся в виде представления:

 

 

 

Для вывода в отчет этого вполне достаточно, а если нам надо как-то эти данные дополнительно обработать? Казалось бы, можно получить ссылку по представлению (найти элемент справочника по наименованию или коду), но к счастью есть другой, более цивилизованный способ. Дело в том, что необходимые нам данные хранятся в данных расшифровки, осталось их оттуда только достать. Ниже приведена функция, которая это делает:

Сотрудник = ЗначениеРасшифровки(ДанныеРасшифровки, ЭлементРезультата, "П2", "Сотрудник");

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

Условное оформление

А как у нас обстоят дела с условным оформлением? Элементы условного оформления, например ЦветФона (при их наличии конечно же), тоже имеются в составе ЭлементРезультата.ЗначенияПараметров, откуда их можно извлечь и применить к оформлению ячейки.

 

 

Вот только сложно понять к оформлению чего(фона, текста…) относится этот цвет. Не самый эффективный, но вполне рабочий способ решения этой проблемы заключается в следующем:

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

 

Ячейка = ПолучитьЯчейкуОбласти(ДокументРезультатДляУО, 3, 2, 1, 1);
Секция.Область().ЦветФона = Ячейка.ЦветФона;

Функция ПолучитьЯчейкуОбласти(Таблица, ВысотаМакета, ШиринаМакета, СтрокаЯчейки, КолонкаЯчейки)
	Результат = Неопределено;
	Если Таблица.ВысотаТаблицы > 0 Тогда
		Область = Таблица.ПолучитьОбласть(Таблица.ВысотаТаблицы - ВысотаМакета + 1,, Таблица.ВысотаТаблицы);
		Результат = Область.Область(СтрокаЯчейки, Область.ШиринаТаблицы - ШиринаМакета + КолонкаЯчейки);					
	КонецЕсли; 
	Возврат Результат;
КонецФункции 

Обработка расшифровки в модуле формы

Что мы еще упустили? Очевидно, что наш отчет настроен под определенную структуру. А пользователь при работе с отчетом вполне может "расшифровать" ячейку и получить непредсказуемый результат. Надо бы лишить его этой возможности.

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

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

Инструмент анализа процесса компоновки

Вроде бы мы рассмотрели все аспекты, кроме одного: а как нам получить соответствие имен макетов СКД  и имен секций нашего макета отчета?

Для этого был создан специальный инструмент в виде внешней обработки.

Здесь надо упомянуть о том, что при первой итерации цикла получения элементов результата, в ЭлементРезультата.Макеты содержится список описаний макетов областей компоновки данных. Из них можно взять и список параметров, и свойства самого макета, а потом связать все это с именем.

 

 

Для облегчения этого процесса в обработке предусмотрен вывод списка макетов и их параметров:

 

 

Понятно, что результат — табличный документ — состоит из этих областей макетов. А как они располагаются в отчете относительно друг друга? Для просмотра этого тоже есть средство:

 

 

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

Дополнительно данный инструмент генерирует шаблон кода построения отчета для модуля объекта. В полученном шаблоне необходимо заполнить имена секций и, при необходимости, дописать содержимое цикла обработки ЭлементРезультата. Это необходимо сделать если вид полученного отчета посложнее простой таблицы (как в примере в начале статьи).

 

 

Подведем итоги

Рассмотренный способ обладает целым рядом преимуществ:

  • Максимальное соответствие внешнего вида шаблона и полученной формы отчета, WYSIWYG — наше всё! И даже можно устанавливать ширину колонок и она будет такой же в отчете!)).
  • Относительно небольшой объем кода, его простота, концентрация кода в одном месте (в отличие от настроек СКД, разбросанных по разным разделам)
  • Возможность пошаговой отладки.
  • Четкое разделение "зон ответственности" — вот здесь мы получаем некоторый объем данных, а вот здесь — выводим данные в отчет. (Добавление в запрос генерации названий колонок — зло!).
  • Полный контроль за выводом отдельных ячеек отчета — легко реализовать вывод разных секций в зависимости от выводимых данных (например, для контрагентов — юр.лиц выводим одну секцию, для физ.лиц — другую). Некоторые строки можно вообще пропускать (вспомним про знаменитую проблему вывода иерархического справочника).
  • Объединение произвольной группы ячеек, как горизонтальное, так и вертикальное — так же, как в примере отчета в статье. 
  • Возможность проведения расчетов, которые нельзя провести с помощью языка запросов и выражений СКД.
  • Возможность реализации нестандартного условного оформления, например, которое зависит не только от данных строки.
  • Простой и логичный способ получения данных накопления — нарастающий итог, иерархическая нумерация, разница текущего значения с последним из непустых предыдущих.
  • Такой же простой способ вывести картинки строк или другие внедренные в табличный документ объекты, например, диаграммы.
  • Возможность использовать ПроверитьВывод() и ПроверитьПрисоединение(), например для расчета итогов по странице при переменной высоте строк отчета (привет, товарная накладная!).
  • Вывод отчета в несколько "колонок". Пример из жизни: прайс-лист компьютерной фирмы: материнские платы выводятся одной таблицей на всю ширину страницы, память — двумя (по половине ширины страницы), манипуляторы — тремя.
  • Возможность использовать свою расшифровку произвольного вида — хоть ссылки, хоть структуру, хоть текст с подсказкой. Также можно убрать расшифровку из тех ячеек, где она не нужна.
  • Возможность использовать свою систему групп строк и колонок, определять свой список свернутых групп при открытии отчета

Конечно же есть и недостатки (я насчитал аж 3: один весомый и два малозначимых):

  • Самый главный недостаток — "заточенность" под конкретную структуру отчета. Хотя недостатком это является весьма условно — есть много отчетов, для которых менять структуру пользователям просто не нужно.
  • Поскольку при построении отчета мы ориентируемся на внутренние имена макетов СКД, возможна ситуация, когда при смене версии платформы механизм именования изменится и наше соответствие имен макетов "испортится". Но мне кажется этот риск имеет достаточно малое значение, поскольку он, во-первых — маловероятен, а во-вторых — его последствия легко устраняются.
  • Чисто теоретически алгоритм вывода отчета на языке 1С может работать медленнее, чем платформенный механизм вывода. Но, как правило, основные временные затраты идут на выполнение запроса и на клиент-серверный обмен, а не на формирование табличного документа. 

Методика, описанная в статье, была протестирована на платформе 8.3.18.1363.

На этом все. Как обычно приветствуются замечания / дополнения / комментарии.

К публикации приложены файлы: внешняя обработка — АнализПроцессаКомпоновки.epf  и архив с этой обработкой и выгрузкой информационной базы с рассмотренным в статье примером.

has been added to your cart:
Оформление заказа