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