Это старая версия документа.


Отчеты, СКД, построитель

Размещаем на форме отчета поле выбора «ПолеВыбораВарианта». В модуль формы добавляем:

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

Для автоматического формирования отчета на СКД при открытии его формы надо добавить в модуль формы в процедуру ПриСозданииНаСервере:

Параметры.СформироватьПриОткрытии = Истина;

а для автоматического формирования из команды надо добавить в модуль команды в процедуру ОбработкаКоманды:

ПараметрыФормы = Новый Структура("СформироватьПриОткрытии", ИСТИНА);
ОткрытьФорму("Отчет.ОстатокДенежныхСредств.Форма", ПараметрыФормы, ПараметрыВыполненияКоманды.Источник, ПараметрыВыполненияКоманды.Уникальность, ПараметрыВыполненияКоманды.Окно);

Что бы избежать запроса на сохранение варианта отчета при его закрытии надо в клиентскую процедуру ПередЗакрытием вставить:

ВариантМодифицирован = Ложь;

Первоначальные пользовательские настройки устанавливаются в процедуре ПриЗагрузкеПользовательскихНастроекНаСервере:

ЭтаФорма.Отчет.Период = ТекущаяДата();
Параметр = ЭтаФорма.Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Период"));
Параметр.Значение = ЭтаФорма.Отчет.Период;
Параметр.Использование = ИСТИНА;

Передача периода из формы отчета в СКД

НачалоПериода и КонецПериода - заданные в компановщике параметры

Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("НачалоПериода"));
Параметр.Значение = НачалоДня(НачПериода);
Параметр.Использование = ИСТИНА;

Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("КонецПериода"));
Параметр.Значение = КонецДня(КонПериода);
Параметр.Использование = ИСТИНА;

Передача реквизита СкладОтбор из формы отчета в СКД

ПолеСклад = Новый ПолеКомпоновкиДанных("Склад");

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

Если ЭлементОтбора = Неопределено Тогда
	ЭлементОтбора = КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
КонецЕсли; 

ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ЛевоеЗначение = ПолеСклад;
ЭлементОтбора.ПравоеЗначение = СкладОтбор;
ЭлементОтбора.Использование = НЕ СкладОтбор.Пустая();

Отчет на СКД можно сформировать программно в процедуре «ПриКомпоновкеРезультата()» модуля отчета:

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

Программный вывод отчета и расшифровки к нему

    НастройкиСКД = КомпоновщикНастроек.Настройки;
    
    СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
    
    ДанныеРасшифровкиОтчета = Новый ДанныеРасшифровкиКомпоновкиДанных;
    
    НастройкиСКД.ПараметрыДанных.Элементы.Найти("Номенклатура").Значение = Справочники.Номенклатура.НайтиПоКоду("1"); // заполняем какие-то параметры

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

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

ДанныеРасшифровкиОтчета - это переменная модуля формы отчета (то, что объявляется через ключевое слово Перем). ЭлементыФормы.Результат - Результат это имя табличного документа куда выводится отчет.

Вывод расшифровок:

Вариант 1.

На табличном документе идет в обработчики событий и делаем обработчик на событие ОбработкаРасшифровки. Процедура обработчик будет иметь вид похожий на это:

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

    МассивДоступныхДействий = Новый Массив;
    МассивДоступныхДействий.Добавить(ДействиеОбработкиРасшифровкиКомпоновкиДанных.ОткрытьЗначение);

    НастройкиРасшифровки = ОбработкаРасшифровки.Выполнить(Расшифровка,, МассивДоступныхДействий);
    
КонецПроцедуры

Результат работы этого когда: двойной клик на любой ячейке ссылочного типа производит открытие этого элемента. Будь то документ, элемент справочника или ещё что-нибудь.

Вариант 2.

Все так же идем в обработчики события поля табличного документа и устанавливаем обработчик на ОбработкаРасшифровки:

Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
    
    СтандартнаяОбработка=Ложь;
    ОбработкаРасшифровки = Новый ОбработкаРасшифровкиКомпоновкиДанных(ДанныеРасшифровкиОтчета, Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных));

    ПолеРасшифровки = ДанныеРасшифровкиОтчета.Элементы[Расшифровка].ПолучитьПоля()[0];
    Если ПолеРасшифровки.Поле = "Контрагент" ИЛИ
        ПолеРасшифровки.Поле = "Документ" Тогда
        
        Ссылка = ПолеРасшифровки.Значение;
        ОткрытьЗначение(Ссылка);
    КонецЕсли;
    
КонецПроцедуры

Тут получаем информацию о том куда пользователь щелкнул. Зная это мы можем получить значение, которое находится в расшифровке. Дальше с ним можем делать что угодно. В этом примере результат не отличается от варианта 1, т.к. и в том и в том случае мы просто хотим открыть значения каких-то ссылок. Но если бы, например, мы получали текст или число, которое нужно было как-то хитро обработать, то в варианте №2 мы можем это сделать.

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


При программном выводе отчета возможно вмешаться в выводимою форму. В примере ниже показано добавление наименования Родителя (/Родитель/) к тексту выводимого поля Номенклатура:

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

При программном формировании отчета можно добавить свою область шапки или подвала в формируемый отчет:

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

ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ИмяОбъектаСодержащегоДанные", ТаблицаЗначений);

СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпановки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);

ПроцессорКомпановкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпановкиДанных.Инициализировать(МакетКомпановки, ВнешниеНаборыДанных);

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

Существует ряд ограничений. Главное из которых: в настройках должны присутствовать только группировки и поля (без таблиц и т.п.).

Макет компановки задается:

МакетКомпановки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

Для процессора вывода используется метод УстановитьОбъект:

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

Вывод производится: в цикле:

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

Весь код:

СхемаКомпоновкиДанных = ПолучитьМакет("МакетКомпановкиДанных");
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпановки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

ПроцессорКомпановкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпановкиДанных.Инициализировать(МакетКомпановки);

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

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

Вывод в таблицу значений:

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


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

при программном выполнении отчета надо инициализировать так:

ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки,,,Истина);

Вычислить(Выражение, Группировка, ТипРасчета)

Параметры: Выражение (Строка). Cодержит вычисляемое выражение; Группировка (Строка). Содержит имя группировки, в контексте которой необходимо вычислить выражение. В случае если в качестве имени группировки используется пустая строка, вычисление будет выполнено в контексте текущей группировки. В случае если в качестве имени группировки будет использована строка ОбщийИтог, вычисление будет выполнено в контексте общего итога. В остальных случаях вычисление будет выполняться в контексте родительской группировки с таким именем.

Например:

Сумма(СуммаОборот) / Вычислить("Сумма(СуммаОборот)", "ОбщийИтог")

В данном примере в результате получится отношение суммы по полю «СуммаОборот» записи группировки к сумме того же поля во всей компоновке. ТипРасчета (Строка). В случае если данный параметр имеет значение «ОбщийИтог», выражение будет вычисляться для всех записей группировки. В случае если значение параметра «Группировка», значения будут вычисляться для текущей групповой записи группировки.


В СКД можно создавать пользовательские поля.

пример вывода в детальных записях процента Суммы договора от итога по вышестоящей группировке (Проект):

Выбор
	Когда Вычислить("Сумма(СуммаДоговора)", "Проект") > 0
		Тогда Формат(СуммаДоговора / Вычислить("Сумма(СуммаДоговора)", "Проект") * 100, "ЧДЦ=2")
	Иначе 0
Конец

Полезно использовать для конца периода в СКД в параметрах в поле Выражение следующую конструкцию:

ВЫБОР
	КОГДА &Период.ДатаОкончания=ДАТАВРЕМЯ(1,1,1) ИЛИ &Период.ДатаОкончания<&Период.ДатаНачала ТОГДА
		ДАТАВРЕМЯ(3999,12,31,23,59,59)
 	ИНАЧЕ
 		&Период.ДатаОкончания
КОНЕЦ

В параметрах виртуальной таблицы регистра накопления периодичность выбирается «АВТО». В выбранные поля добавляется Регистратор и обязательно - ПериодСекунда.


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


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

&НаСервере
Функция ПолучитьТабличныйДокументИзТабличнойЧасти(НаименованиеТабЧасти = "ТаблицаРезультат_ОбщПравилаОстатки")

	ТабДокумент = Новый ТабличныйДокумент;
	ОбработкаОбъект = РеквизитФормыВЗначение("Отчет");
	ТаблицаСДанными = Неопределено;
	Выполнить("ТаблицаСДанными = ОбработкаОбъект." + НаименованиеТабЧасти + ".Выгрузить()");
	Если ТипЗнч(ТаблицаСДанными) <> Тип("ТаблицаЗначений") Тогда
		Возврат ТабДокумент;
	КонецЕсли; 
	
	Построитель = Новый ПостроительОтчета;
	Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТаблицаСДанными);
	Построитель.Выполнить();
	Построитель.Вывести(ТабДокумент);
	Возврат ТабДокумент;

КонецФункции

Статья Печенкина Романа на Mista.ru Ссылка на статью