Как в отчете на СКД дать пользователю выбрать тип объекта из определяемого типа?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Рассмотрим распространенную задачу: у нас есть отчет, который анализирует данные из регистра сведений, где одно из измерений имеет составной тип, определенный через объект метаданных ОпределяемыйТип. В нашем примере это отчет по наличию прикрепленных файлов, который работает с регистром НаличиеФайлов и его измерением ОбъектСФайлами, чей тип — ОпределяемыйТип.ВладелецПрисоединенныхФайлов.

Основная проблема заключается в том, чтобы предоставить пользователю удобный интерфейс для выбора конкретного типа (например, только документы «Сверка взаиморасчетов» или справочники «Контрагенты»), а не заставлять его выбирать конкретный элемент из списка. Попытка добавить на форму реквизит с типом самого определяемого типа приводит к нежелательному поведению — открывается форма выбора конкретного документа или элемента справочника, а не форма выбора типа.

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

Решение 1: Быстрый способ через параметр СКД и функцию ТИПЗНАЧЕНИЯ()

Этот подход основан на идее из обсуждения на форуме. Он позволяет быстро получить результат, хотя и с небольшим компромиссом в удобстве для пользователя. Суть метода — использовать в запросе СКД функцию ТИПЗНАЧЕНИЯ() для сравнения типа данных в регистре с типом, который выберет пользователь.

Посмотрим на реализацию по шагам:

  1. Настройка параметра в СКД. В отчете на СКД создайте новый параметр, например, с именем ВыбранныйТипОбъекта. В качестве типа данных для этого параметра укажите сам определяемый тип — в нашем случае это ОпределяемыйТип.ВладелецПрисоединенныхФайлов.
  2. Модификация запроса. В тексте запроса СКД добавьте условие в секцию ГДЕ, которое будет фильтровать записи по типу.
    
    ВЫБРАТЬ
        НаличиеФайлов.ОбъектСФайлами,
        НаличиеФайлов.ЕстьФайлы
    ИЗ
        РегистрСведений.НаличиеФайлов КАК НаличиеФайлов
    ГДЕ
        ТИПЗНАЧЕНИЯ(НаличиеФайлов.ОбъектСФайлами) = ТИПЗНАЧЕНИЯ(&ВыбранныйТипОбъекта)
    
  3. Как это работает для пользователя. Когда пользователь откроет настройки отчета, он увидит поле для выбора параметра ВыбранныйТипОбъекта. При нажатии на кнопку выбора платформа сначала предложит выбрать тип из списка (например, «Документ.СверкаВзаиморасчетов», «Справочник.Контрагенты» и т.д.). После выбора типа откроется второе окно — форма выбора конкретного экземпляра этого типа. Важный момент: пользователю нужно просто закрыть это второе окно, не выбирая конкретный документ. В результате в параметре сохранится «пустая ссылка» нужного типа, и функция ТИПЗНАЧЕНИЯ() в запросе отработает корректно.

Этот метод рабочий и очень прост в реализации, но может смутить неопытных пользователей необходимостью «отказаться от выбора» на втором шаге.

Решение 2: Корректный пользовательский интерфейс в СКД

Чтобы избежать путаницы для пользователя и предоставить ему стандартный и понятный диалог выбора типа, можно немного усовершенствовать предыдущий метод. Вместо того чтобы задавать параметру тип определяемого типа, мы используем системный тип Тип.

Разберем по шагам:

  1. Настройка параметра в СКД. Создайте параметр, например, ВыбранныйТип. В окне выбора типа данных для этого параметра найдите и выберите системный тип Тип. Он находится в разделе «Системные перечисления и значения».
  2. Ограничение доступных типов. Чтобы пользователь не мог выбрать абсолютно любой тип из конфигурации, на закладке «Параметры» для нашего параметра ВыбранныйТип в колонке «Тип значения параметра» нажмите кнопку выбора. В открывшемся окне «Редактирование типа данных» укажите, что это составной тип, и перечислите все типы, входящие в ваш определяемый тип ОпределяемыйТип.ВладелецПрисоединенныхФайлов. Это можно сделать вручную или программно при инициализации отчета.
  3. Текст запроса. Условие в запросе изменится незначительно. Теперь мы сравниваем результат ТИПЗНАЧЕНИЯ() напрямую с параметром, который уже хранит тип, а не ссылку.
    
    ВЫБРАТЬ
        НаличиеФайлов.ОбъектСФайлами,
        НаличиеФайлов.ЕстьФайлы
    ИЗ
        РегистрСведений.НаличиеФайлов КАК НаличиеФайлов
    ГДЕ
        ТИПЗНАЧЕНИЯ(НаличиеФайлов.ОбъектСФайлами) = &ВыбранныйТип
    

Теперь при выборе значения для параметра ВыбранныйТип платформа откроет стандартное, удобное окно «Выбор типа данных», где все доступные типы будут сгруппированы по видам метаданных (Справочники, Документы и т.д.). Это наиболее правильный и дружелюбный к пользователю способ реализации в рамках СКД.

Решение 3: Оптимальный подход с точки зрения производительности

Методы, использующие фильтрацию по ТИПЗНАЧЕНИЯ(), идеально подходят для небольших объемов данных. Однако, если в регистре НаличиеФайлов хранятся миллионы записей, производительность такого запроса может сильно упасть. Это связано с тем, что СУБД (например, MS SQL Server) не может эффективно использовать индексы по полям составного типа при фильтрации по типу значения, что часто приводит к полному сканированию таблицы (Table Scan).

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

Проанализируем этот метод:

  1. Настройка выбора типа. Используем подход из Решения 2: создаем в СКД параметр ВыбранныйТип с системным типом Тип.
  2. Программная обработка. Вместо фильтрации через ТИПЗНАЧЕНИЯ() мы будем программно изменять текст запроса в обработчике события ПриКомпоновкеРезультата.

    Изначальный текст запроса во временную таблицу может выглядеть так:

    
    ВЫБРАТЬ
        ДокументСверкаВзаиморасчетов.Ссылка КАК Ссылка,
        ДокументСверкаВзаиморасчетов.Менеджер КАК Менеджер
    ПОМЕСТИТЬ ВременнаяТаблица
    ИЗ
        Документ.СверкаВзаиморасчетов КАК ДокументСверкаВзаиморасчетов
    ГДЕ
        ДокументСверкаВзаиморасчетов.Проведен
        И НЕ ДокументСверкаВзаиморасчетов.ПометкаУдаления
        И ДокументСверкаВзаиморасчетов.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
    

    В обработчике события мы заменим `Документ.СверкаВзаиморасчетов` на имя таблицы, соответствующее типу, который выбрал пользователь.

  3. Пример кода в модуле отчета.
    
    Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
        СтандартнаяОбработка = Ложь;
    
        // Получаем выбранный пользователем тип
        Настройки = КомпоновщикНастроек.ПолучитьНастройки();
        ВыбранныйТип = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("ВыбранныйТип")).Значение;
    
        Если ВыбранныйТип = Неопределено Тогда
            // Обработка случая, когда тип не выбран
            ВывестиОшибку("Необходимо выбрать тип объекта для формирования отчета!");
            Возврат;
        КонецЕсли;
    
        // Получаем имя таблицы метаданных
        ИмяТаблицы = Метаданные.НайтиПоТипу(ВыбранныйТип).ПолноеИмя(); // Например, "Документ.СверкаВзаиморасчетов"
    
        // Получаем схему компоновки данных
        СхемаКомпоновкиДанных = ЭтотОбъект.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
        
        // Получаем текст запроса из набора данных
        НаборДанных = СхемаКомпоновкиДанных.НаборыДанных[0]; // Предполагаем, что у нас один набор данных
        ТекстЗапроса = НаборДанных.Запрос;
        
        // Заменяем "заглушку" в тексте запроса на реальное имя таблицы
        // Предполагается, что в исходном запросе используется плейсхолдер, например, {ИмяТаблицыИсточника}
        ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{ИмяТаблицыИсточника}", ИмяТаблицы);
        НаборДанных.Запрос = ТекстЗапроса;
    
        // Выполняем компоновку с измененной схемой
        КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
        МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
        ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
        ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
    
        ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
        ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    
        ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
    КонецПроцедуры
    

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

← На главную