Рассмотрим распространенную задачу: у нас есть отчет, который анализирует данные из регистра сведений, где одно из измерений имеет составной тип, определенный через объект метаданных ОпределяемыйТип. В нашем примере это отчет по наличию прикрепленных файлов, который работает с регистром НаличиеФайлов и его измерением ОбъектСФайлами, чей тип — ОпределяемыйТип.ВладелецПрисоединенныхФайлов.
Основная проблема заключается в том, чтобы предоставить пользователю удобный интерфейс для выбора конкретного типа (например, только документы «Сверка взаиморасчетов» или справочники «Контрагенты»), а не заставлять его выбирать конкретный элемент из списка. Попытка добавить на форму реквизит с типом самого определяемого типа приводит к нежелательному поведению — открывается форма выбора конкретного документа или элемента справочника, а не форма выбора типа.
Проанализируем несколько способов решения этой задачи, от самого быстрого до наиболее корректного и производительного.
Этот подход основан на идее из обсуждения на форуме. Он позволяет быстро получить результат, хотя и с небольшим компромиссом в удобстве для пользователя. Суть метода — использовать в запросе СКД функцию ТИПЗНАЧЕНИЯ() для сравнения типа данных в регистре с типом, который выберет пользователь.
Посмотрим на реализацию по шагам:
ВыбранныйТипОбъекта. В качестве типа данных для этого параметра укажите сам определяемый тип — в нашем случае это ОпределяемыйТип.ВладелецПрисоединенныхФайлов.
ГДЕ, которое будет фильтровать записи по типу.
ВЫБРАТЬ
НаличиеФайлов.ОбъектСФайлами,
НаличиеФайлов.ЕстьФайлы
ИЗ
РегистрСведений.НаличиеФайлов КАК НаличиеФайлов
ГДЕ
ТИПЗНАЧЕНИЯ(НаличиеФайлов.ОбъектСФайлами) = ТИПЗНАЧЕНИЯ(&ВыбранныйТипОбъекта)
ВыбранныйТипОбъекта. При нажатии на кнопку выбора платформа сначала предложит выбрать тип из списка (например, «Документ.СверкаВзаиморасчетов», «Справочник.Контрагенты» и т.д.). После выбора типа откроется второе окно — форма выбора конкретного экземпляра этого типа. Важный момент: пользователю нужно просто закрыть это второе окно, не выбирая конкретный документ. В результате в параметре сохранится «пустая ссылка» нужного типа, и функция ТИПЗНАЧЕНИЯ() в запросе отработает корректно.
Этот метод рабочий и очень прост в реализации, но может смутить неопытных пользователей необходимостью «отказаться от выбора» на втором шаге.
Чтобы избежать путаницы для пользователя и предоставить ему стандартный и понятный диалог выбора типа, можно немного усовершенствовать предыдущий метод. Вместо того чтобы задавать параметру тип определяемого типа, мы используем системный тип Тип.
Разберем по шагам:
ВыбранныйТип. В окне выбора типа данных для этого параметра найдите и выберите системный тип Тип. Он находится в разделе «Системные перечисления и значения».
ВыбранныйТип в колонке «Тип значения параметра» нажмите кнопку выбора. В открывшемся окне «Редактирование типа данных» укажите, что это составной тип, и перечислите все типы, входящие в ваш определяемый тип ОпределяемыйТип.ВладелецПрисоединенныхФайлов. Это можно сделать вручную или программно при инициализации отчета.
ТИПЗНАЧЕНИЯ() напрямую с параметром, который уже хранит тип, а не ссылку.
ВЫБРАТЬ
НаличиеФайлов.ОбъектСФайлами,
НаличиеФайлов.ЕстьФайлы
ИЗ
РегистрСведений.НаличиеФайлов КАК НаличиеФайлов
ГДЕ
ТИПЗНАЧЕНИЯ(НаличиеФайлов.ОбъектСФайлами) = &ВыбранныйТип
Теперь при выборе значения для параметра ВыбранныйТип платформа откроет стандартное, удобное окно «Выбор типа данных», где все доступные типы будут сгруппированы по видам метаданных (Справочники, Документы и т.д.). Это наиболее правильный и дружелюбный к пользователю способ реализации в рамках СКД.
Методы, использующие фильтрацию по ТИПЗНАЧЕНИЯ(), идеально подходят для небольших объемов данных. Однако, если в регистре НаличиеФайлов хранятся миллионы записей, производительность такого запроса может сильно упасть. Это связано с тем, что СУБД (например, MS SQL Server) не может эффективно использовать индексы по полям составного типа при фильтрации по типу значения, что часто приводит к полному сканированию таблицы (Table Scan).
Для высоконагруженных систем рекомендуется комбинированный подход: предоставить пользователю удобный выбор типа (как в Решении 2), а затем динамически сформировать текст запроса для максимальной производительности.
Проанализируем этот метод:
ВыбранныйТип с системным типом Тип.
ТИПЗНАЧЕНИЯ() мы будем программно изменять текст запроса в обработчике события ПриКомпоновкеРезультата.
Изначальный текст запроса во временную таблицу может выглядеть так:
ВЫБРАТЬ
ДокументСверкаВзаиморасчетов.Ссылка КАК Ссылка,
ДокументСверкаВзаиморасчетов.Менеджер КАК Менеджер
ПОМЕСТИТЬ ВременнаяТаблица
ИЗ
Документ.СверкаВзаиморасчетов КАК ДокументСверкаВзаиморасчетов
ГДЕ
ДокументСверкаВзаиморасчетов.Проведен
И НЕ ДокументСверкаВзаиморасчетов.ПометкаУдаления
И ДокументСверкаВзаиморасчетов.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
В обработчике события мы заменим `Документ.СверкаВзаиморасчетов` на имя таблицы, соответствующее типу, который выбрал пользователь.
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем выбранный пользователем тип
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
ВыбранныйТип = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("ВыбранныйТип")).Значение;
Если ВыбранныйТип = Неопределено Тогда
// Обработка случая, когда тип не выбран
ВывестиОшибку("Необходимо выбрать тип объекта для формирования отчета!");
Возврат;
КонецЕсли;
// Получаем имя таблицы метаданных
ИмяТаблицы = Метаданные.НайтиПоТипу(ВыбранныйТип).ПолноеИмя(); // Например, "Документ.СверкаВзаиморасчетов"
// Получаем схему компоновки данных
СхемаКомпоновкиДанных = ЭтотОбъект.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
// Получаем текст запроса из набора данных
НаборДанных = СхемаКомпоновкиДанных.НаборыДанных[0]; // Предполагаем, что у нас один набор данных
ТекстЗапроса = НаборДанных.Запрос;
// Заменяем "заглушку" в тексте запроса на реальное имя таблицы
// Предполагается, что в исходном запросе используется плейсхолдер, например, {ИмяТаблицыИсточника}
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{ИмяТаблицыИсточника}", ИмяТаблицы);
НаборДанных.Запрос = ТекстЗапроса;
// Выполняем компоновку с измененной схемой
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Этот подход является самым сложным в реализации, но он обеспечивает как превосходный пользовательский интерфейс, так и максимальную производительность на больших базах данных, так как запросы к СУБД будут выполняться по конкретным таблицам с использованием всех доступных индексов.