Пример универсальной функции для создания схемы компоновки данных

Программирование - Практика программирования

СКД Схема компоновки данных Программное создание схемы компоновки данных СКД программно

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

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

  •  Структура ресурсов: содержит перечень полей ресурсов, где ключ - ИмяПоля, Значение(Строка) - Агрегатная функция ("Сумма", "Среднее", "Количество",  и т.д.);
  •  Флаг АвтоЗаполнениеДоступныхПолей;
  •  Строковое имя макета оформления.
// НаборДанных (Типы: Строка, запрос, ТаблицаЗначений, ДеревоЗначений)
// Искомый набор данных
//
// СтруктураРесурсов (Тип: Структура) - Структура полей ресурсов, где ключ - ИмяПоля, Значение(Строка) - Агрегатная функция
//
// ВноситьПоляВыбора (Тип: Булево) - Флаг добавление полей набора
//
// ИмяСтандартногоМакетаОформления (Тип: Строка) - Имя макета оформления 
//
Функция СоздатьСхемуКомпоновкиДанных(НаборДанных, СтруктураРесурсов = Неопределено, АвтоЗаполнениеДоступныхПолей = Истина, ИмяСтандартногоМакетаОформления = "")
	
	СКД = Новый СхемаКомпоновкиДанных;
	
	// Заполнение основных данных схемы
	ИсточникДанных = СКД.ИсточникиДанных.Добавить();
	ИсточникДанных.Имя = "ИсточникДанных";
	ИсточникДанных.ТипИсточникаДанных = "Local";
	
	Если ТипЗнч(НаборДанных) = Тип("Строка") или ТипЗнч(НаборДанных) = Тип("Запрос") Тогда
		ТекущийНаборДанных = СКД.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
		ТекущийНаборДанных.Имя = "ОсновнойНабор";
		ТекущийНаборДанных.Запрос = ?(ТипЗнч(НаборДанных) = Тип("Строка"),НаборДанных,НаборДанных.Текст);
		ТекущийНаборДанных.ИсточникДанных = "ИсточникДанных";			
		ТипНабора = "Запрос";
		ТекущийНаборДанных.АвтоЗаполнениеДоступныхПолей = АвтоЗаполнениеДоступныхПолей;
	ИначеЕсли ТипЗнч(НаборДанных) = Тип("ТаблицаЗначений") или ТипЗнч(НаборДанных) = Тип("ДеревоЗначений") Тогда
		ТекущийНаборДанных = СКД.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
		ТекущийНаборДанных.Имя = "ОсновнойНабор";
		ТекущийНаборДанных.ИмяОбъекта = "ТаблицаИсточник";	
		ТекущийНаборДанных.ИсточникДанных = "ИсточникДанных";			
		ТипНабора = "Объект";
	Иначе			
		Возврат Неопределено;
	КонецЕсли;	
			
	НастройкиПоУмолчанию = СКД.НастройкиПоУмолчанию;
	
	// Создание структуры.
	// Группировка, детальные записи и автовыбранное поле
	Группировка = НастройкиПоУмолчанию.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	Группировка.Использование = Истина;			
	АвтоПоле = Группировка.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
	АвтоПоле.Использование = Истина;
	
	КоллекцияКолонок = Новый ТаблицаЗначений;
	КоллекцияКолонок.Колонки.Добавить("Имя");
	КоллекцияКолонок.Колонки.Добавить("ТипЗначения");
	КоллекцияКолонок.Колонки.Добавить("Заголовок");
	
	Если ТипНабора = "Запрос" Тогда
		ПостроительЗапроса = Новый ПостроительЗапроса;
		Если ТипЗнч(НаборДанных) = Тип("Строка") Тогда
			ПостроительЗапроса.Текст = СокрЛП(НаборДанных);
		Иначе 
			ПостроительЗапроса.Текст = СокрЛП(НаборДанных.Текст);
		КонецЕсли;
		ПостроительЗапроса.ЗаполнитьНастройки();
		
		Для каждого ВыбранноеПоле Из ПостроительЗапроса.ВыбранныеПоля Цикл	
			НоваяКолонка = КоллекцияКолонок.Добавить();
			НоваяКолонка.Имя = ВыбранноеПоле.Имя;			
			НоваяКолонка.ТипЗначения = ПостроительЗапроса.ДоступныеПоля[ВыбранноеПоле.ПутьКДанным].ТипЗначения;
			НоваяКолонка.Заголовок = ВыбранноеПоле.Представление
		КонецЦикла;
		
	ИначеЕсли ТипНабора = "Объект" Тогда
		
		Для каждого Колонка Из НаборДанных.Колонки Цикл			
			НоваяКолонка = КоллекцияКолонок.Добавить();
			НоваяКолонка.Имя = Колонка.Имя;
			НоваяКолонка.ТипЗначения = Колонка.ТипЗначения;
			НоваяКолонка.Заголовок = Колонка.Имя;									
		КонецЦикла;
		
	КонецЕсли;
	
	// Добавление ресурсов
	Если ТипЗнч(СтруктураРесурсов) = Тип("Структура") Тогда			
		Для Каждого ЭлСтруктуры Из СтруктураРесурсов Цикл
			// Проверка, а существует ли поле ресурса среди полей набора
			Если КоллекцияКолонок.Найти(ЭлСтруктуры.Ключ) <> Неопределено Тогда
				// Проверка на правильность указания агрегатной функции
				Если    ЭлСтруктуры.Значение = "Сумма" 
					ИЛИ ЭлСтруктуры.Значение = "Среднее"
					ИЛИ ЭлСтруктуры.Значение = "Максимум"
					ИЛИ ЭлСтруктуры.Значение = "Минимум"
					ИЛИ ЭлСтруктуры.Значение = "Количество" Тогда
					
					ПолеРесурса             = СКД.ПоляИтога.Добавить();
					ПолеРесурса.ПутьКДанным = ЭлСтруктуры.Ключ;
					ПолеРесурса.Выражение   = ЭлСтруктуры.Значение + "(" + ЭлСтруктуры.Ключ + ")";					
					
				ИначеЕсли ЭлСтруктуры.Значение = "КоличествоРазличные" Тогда                      
					
					ПолеРесурса             = СКД.ПоляИтога.Добавить();
					ПолеРесурса.ПутьКДанным = ЭлСтруктуры.Ключ;
					ПолеРесурса.Выражение   = "Количество(Различные " + ЭлСтруктуры.Ключ + ")";
					
				КонецЕсли;
			КонецЕсли;	
		КонецЦикла;                             			
	КонецЕсли;
		
	// Добавление полей в набор		
	Для каждого НоваяКолонка Из КоллекцияКолонок Цикл			
		ПолеНабора = ТекущийНаборДанных.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
		ПолеНабора.Заголовок = СокрЛП(НоваяКолонка.Заголовок);
		ПолеНабора.Поле = СокрЛП(НоваяКолонка.Имя);
		ПолеНабора.ПутьКДанным = СокрЛП(НоваяКолонка.Имя);
		
		// Удалим неопределено и NULL
		Массив = Новый Массив;
		Для каждого ТекущийТип Из НоваяКолонка.ТипЗначения.Типы() Цикл
			Если ТекущийТип = Тип("Неопределено") или ТекущийТип = Тип("NULL") или ТекущийТип = Неопределено или ТекущийТип = Null Тогда 
				Продолжить; 
			КонецЕсли;
			Массив.Добавить(ТекущийТип);
		КонецЦикла;
		ПолеНабора.ТипЗначения = Новый ОписаниеТипов(Массив,НоваяКолонка.ТипЗначения.КвалификаторыЧисла,НоваяКолонка.ТипЗначения.КвалификаторыСтроки,НоваяКолонка.ТипЗначения.КвалификаторыДаты);
						
		ВыбранноеПолеКомпоновкиДанных = НастройкиПоУмолчанию.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));			
		ВыбранноеПолеКомпоновкиДанных.Поле = Новый ПолеКомпоновкиДанных(ПолеНабора.ПутьКДанным);
		ВыбранноеПолеКомпоновкиДанных.Использование = Истина;
	КонецЦикла;
		
	// Оформление
	Если не ПустаяСтрока(ИмяСтандартногоМакетаОформления) Тогда
		ЗначениеПараметраВывода = НастройкиПоУмолчанию.ПараметрыВывода.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("МакетОформления"));
		ЗначениеПараметраВывода.Значение = ИмяСтандартногоМакетаОформления;
		ЗначениеПараметраВывода.Использование = Истина;
	КонецЕсли;
		
	Возврат СКД;
	
КонецФункции

В обработке наглядный пример использования функции. Поместите в функцию "ПолучитьТекстЗапроса" текст своего запроса и сформируйте.

В интерфейс выведен отбор компоновщика настроек с помощью которого пользователь сможет настроить нужный ему отбор. Функция создания схемы компоновки данных используется два раза: первый при открытии формы для инициализации полей пользователю. Второй раз непосредственно при формировании результата. При формировании результата запросом выбираются данные из базы и помещаются в таблицу значений - модель создания СКД по внешнему набору данных. Я взял этот вариант как наиболее интересный. С помощью его легко продемонстрировать открывающиеся возможности этого подхода. На разработку этого примера у меня ушло 5-7 минут, что позволяет достаточно оперативно решать подобного рода задачи.

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

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

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

Обработка протестирована в УПП 1.3.79.2. Платформа 8.2.19.130

185

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

Наименование Файл Версия Размер
Пример использования универсальной функции создания СКД
.epf 12,80Kb
07.03.17
26
.epf 12,80Kb 26 Скачать

См. также

Комментарии
Избранное Подписка Сортировка: Древо
1. glek 112 13.01.17 09:08 Сейчас в теме
2. Evil Beaver 5294 13.01.17 10:26 Сейчас в теме
А теперь бы все это выложить в gist на гитхабе, чтобы можно было допилить всем желающим
gortol; dj_serega; Berckk; kambl; alprk; +5 Ответить
4. kambl 195 13.01.17 12:18 Сейчас в теме
3. pbazeliuk 1478 13.01.17 10:44 Сейчас в теме
Пройдусь по явным недочетам:
1.Описание процедур и функций;
2. Не хватает обработки Настроек.ПараметровДанных по умолчанию;
3. Недопустимо перехватывать любые исключения, бесследно для системного администратора.

В остальном бесспорно плюс.
grachev1c; tux; artbear; Evil Beaver; brr; kambl; +6 Ответить
5. kambl 195 13.01.17 12:19 Сейчас в теме
(3) Конструктивная критика. Спасибо
6. Evil Beaver 5294 13.01.17 14:02 Сейчас в теме
(3) а внутри github эти замечания были бы еще и в виде код-ревью оформлены, и отслежено исправление :)
gortol; artbear; JohnyDeath; pbazeliuk; kambl; +5 Ответить
7. hromovanton 4 13.01.17 14:42 Сейчас в теме
Очень хорошая функция. Спасибо вам ...
8. yurii_host 1837 15.01.17 14:08 Сейчас в теме
Идея интересная.

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

Если же ошибку нужно скрыть от пользователя, то в попытку нужно оборачивать сам вызов данной функции.
JohnyDeath; +1 Ответить
9. kambl 195 15.01.17 17:33 Сейчас в теме
(8) Функция не претендует на абсолютную самодостаточность. Логика такая - функция возвращает либо СКД, либо неопределенно. Если вы получаете неопределенно - действуете по усмотрению в месте вызова.
Если хотите оборачивать вызов в попытку - это нужно будет делать при каждом вызове. "Лаконичнее" будет вынести попытку в саму функцию
10. yurii_host 1837 15.01.17 17:57 Сейчас в теме
(9) дело ваше.
Но на всякий случай порекомендую ознакомиться с системой стандартов и методик на эту тему.

http://its.1c.ru/db/v8std#content:2149184148:hdoc

см пункты 1, 2 и 3.4

А по поводу лаконичности: лучше передавать в функцию правильные параметры, тогда попытка вообще не понадобится
11. artbear 1084 16.01.17 12:55 Сейчас в теме
(0) (2) Создал спец.гист на гитхабе
https://gist.github.com/artbear/e1cfd0f5dbf39373f4424a4b250fb690
Указал ссылку на данную публикацию
12. kambl 195 16.01.17 13:59 Сейчас в теме
(11) Как раз планировал приобщить сообщество git по рекомендации уважаемого (2) "Evil Beaver". Благодарю за оперативность и приглашаю присоединиться всех желающих
13. adsl 16.01.17 15:48 Сейчас в теме
Чтобы порядок колонок был как в тексте запроса, стоит исправить примерно так:
	//Для каждого ПолеОтбора Из ПостроительЗапроса.Отбор.ПолучитьДоступныеПоля() Цикл
		Для каждого ПолеОтбора Из ПостроительЗапроса.ВыбранныеПоля Цикл
			НоваяКолонка = КоллекцияКолонок.Добавить();
			НоваяКолонка.Имя = ПолеОтбора.Имя;
			//НоваяКолонка.ТипЗначения = ПолеОтбора.ТипЗначения;
			НоваяКолонка.ТипЗначения = ПостроительЗапроса.ДоступныеПоля[ПолеОтбора.ПутьКДанным].ТипЗначения;
			НоваяКолонка.Заголовок = ПолеОтбора.Представление
		КонецЦикла;
Показать
gortol; igormiro; artbear; kambl; +4 Ответить
14. kambl 195 16.01.17 16:25 Сейчас в теме
(13) Отличное замечание. Изменение внес
15. Nebiros777 5 16.01.17 16:30 Сейчас в теме
Блин, и где я лазил все это время? Утащил в нору!
16. AlmazBur01 18.01.17 13:18 Сейчас в теме
//
// ВноситьПоляВыбора (Тип: Булево) - Флаг добавление полей набора
//

В комментариях перед функцией эта строка сообщает нам: о чём?
17. AlmazBur01 18.01.17 13:20 Сейчас в теме
//
// ВноситьПоляВыбора (Тип: Булево) - Флаг добавление полей набора
//

Перед функцией эта строка в комментариях сообщает нам: о чём?
18. proger1c81 18.01.17 15:40 Сейчас в теме
Может мой вопрос покажется странным, но можно привести практические примеры применения программного получения СКД? Как мы можем дальше использовать этот объект и для чего? Возможно я проникнусь этим тоже буду юзать СКД для каких-то задач.
Мне по роду моих задач приходится только программно обрабатывать всякие события СКД, варианты схем, виды, группировки, оформления и т.д.. При этом саму схему (сам отчет на СКД) разрабатываю вручную в конфигураторе.
20. daho 8 19.01.17 12:31 Сейчас в теме
(18) Я так понимаю создаешь пустую СКД в отчете а в модуле объекта подсовываешь ее процедуре ПриКомпоновкеРезультата().. или как правильно?
26. kambl 195 07.03.17 10:58 Сейчас в теме
22. the1 320 15.02.17 17:03 Сейчас в теме
(18) Для нас эта публикация представляет скорее художественный, нежели практический интерес.
23. kambl 195 16.02.17 13:27 Сейчас в теме
(22) Отлично! Главное, что интересно!
27. kabanoff 41 05.03.18 18:16 Сейчас в теме
(22) Автору большое спасибо! Как раз искал вариант программного создания СКД по таблице значений.

Вот конкретная производственная необходимость: отчет формируется по большой таблице (порядка 20 ТБ). Сформировать его в одном потоке за короткое время невозможно. Поэтому его приходится формировать по частям.

Логику дробления и запуска пакетов (фоновых заданий) описывать не буду. Скажу лишь, что каждое ФЗ возвращает порцию данных в виде таблицы значений.

Так вот для того, чтобы не создавать еще одну СКД с внешним набором данных и сложной структурой колонок, надо просто вызвать функцию, которую предложил автор, и присвоить ей настройки компоновщика, который указал пользователь. Бинго!
gortol; kambl; +2 Ответить
19. logarifm 1015 18.01.17 21:38 Сейчас в теме
Большое тебе спасибо... Реально сэкономил мне время.
21. sudmorsh 54 25.01.17 17:43 Сейчас в теме
Интересная идея, спасибо
24. German_Tagil 6 05.03.17 06:00 Сейчас в теме
Можно попросить выложить вариант работающего отчета или обработки
25. kambl 195 07.03.17 10:57 Сейчас в теме
28. user774630 13.05.18 21:48 Сейчас в теме
А вообщем-то нет пределов вашей фантации

или в общем-то, или вообще
:)
Оставьте свое сообщение