Как в СКД при расшифровке вывести сразу несколько группировок (например, Контрагент и Регистратор)

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

При работе с Системой компоновки данных (СКД) в 1С разработчики часто сталкиваются со стандартным поведением расшифровки. Когда пользователь дважды кликает на показатель отчета и выбирает пункт "Расшифровать...", платформа предлагает детализировать данные только по одному конкретному полю (например, по регистратору). Однако на практике клиентам часто требуется более сложная структура: они хотят видеть иерархическую цепочку группировок, например, сначала получить группировку по контрагенту, а внутри нее — список документов-регистраторов.

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

Постановка задачи и анализ механизмов

Представим типовой отчет по продажам. В исходном виде он выводит общие суммы продаж. При двойном клике на сумму продажи пользователь хочет выполнить расшифровку. Вместо простого плоского списка документов системы (регистраторов) нам необходимо построить дерево: Контрагент -> Регистратор.

Для решения этой задачи мы можем пойти двумя путями:

  1. Клиентский метод (перехват в форме отчета). Мы полностью берем под контроль событие обработки расшифровки в табличном документе формы, перестраиваем настройки компоновщика и открываем новую форму отчета с нужной нам структурой.
  2. Серверный метод (перехват в модуле объекта). Мы используем стандартный обработчик ПриКомпоновкеРезультата в модуле объекта отчета, определяем, что сейчас выполняется расшифровка, и подменяем структуру группировок на лету перед выводом результата.

Давайте подробно рассмотрим каждый из этих вариантов с готовыми примерами кода.

Вариант 1. Программный перехват расшифровки на форме отчета (Клиентский метод)

Этот способ считается наиболее гибким и красивым с точки зрения интерфейса. Мы перехватываем стандартное действие пользователя в поле табличного документа, в которое выводится отчет.

Для реализации этого метода выполним следующие шаги:

  1. Откроем форму отчета и выделим элемент формы, отображающий результат (обычно это поле табличного документа с именем Результат).
  2. В свойствах элемента найдем событие РезультатОбработкаРасшифровки и создадим для него обработчик на клиенте.
  3. Внутри обработчика установим параметр СтандартнаяОбработка в значение Ложь, чтобы отключить базовое поведение платформы.
  4. Используем объект ОбработкаРасшифровкиКомпоновкиДанных для генерации нашего собственного диалогового окна и последующей модификации настроек компоновщика.

Рассмотрим подробный код реализации этого механизма:


&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
    
    // Шаг 1. Отключаем стандартный механизм расшифровки платформы
    СтандартнаяОбработка = Ложь; 
    
    // Шаг 2. Инициализируем объект обработки расшифровки
    ОбработкаРасшифровки = Новый ОбработкаРасшифровкиКомпоновкиДанных(
        ДанныеРасшифровки, 
        Новый ИсточникДоступныхНастроекКомпоновкиДанных(АдресСхемыКомпоновкиДанных)
    );
    
    // Шаг 3. Задаем доступные пользователю действия
    ДоступныеДействия = Новый Массив;
    ДоступныеДействия.Добавить(ДействиеОбработкиРасшифровкиКомпоновкиДанных.Расшифровать);
    ДоступныеДействия.Добавить(ДействиеОбработкиРасшифровкиКомпоновкиДанных.ОткрытьЗначение);
    
    Перем ВыполненноеДействие;
    
    // Шаг 4. Вызываем стандартный диалог выбора поля расшифровки
    Настройки = ОбработкаРасшифровки.Выполнить(Расшифровка, ВыполненноеДействие, ДоступныеДействия);
    
    Если Настройки <> Неопределено Тогда
        
        // Шаг 5. Проверяем, что пользователь выбрал именно действие "Расшифровать"
        Если ВыполненноеДействие = ДействиеОбработкиРасшифровкиКомпоновкиДанных.Расшифровать Тогда
            
            // Проверяем, есть ли в структуре настроек хотя бы одна группировка
            Если Настройки.Структура.Количество() > 0 
                И ТипЗнч(Настройки.Структура[0]) = Тип("ГруппировкаКомпоновкиДанных") Тогда
                
                Группировка = Настройки.Структура[0];
                
                // Проверяем, выбрал ли пользователь расшифровку по полю "Регистратор"
                Если Группировка.ПоляГруппировки.Элементы.Количество() > 0 
                    И Группировка.ПоляГруппировки.Элементы[0].Поле = Новый ПолеКомпоновкиДанных("Регистратор") Тогда
                    
                    // Шаг 6. Динамически перестраиваем структуру настроек СКД
                    Настройки.Структура.Очистить();
                    
                    // Создаем верхний уровень нашей новой иерархии - группировку по Контрагенту
                    ГруппировкаКонтрагент = Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
                    ГруппировкаКонтрагент.Использование = Истина;
                    
                    ПолеКонтрагент = ГруппировкаКонтрагент.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
                    ПолеКонтрагент.Поле = Новый ПолеКомпоновкиДанных("Контрагент");
                    ПолеКонтрагент.Использование = Истина;
                    
                    // Создаем подчиненный уровень - группировку по Регистратору внутри Контрагента
                    ГруппировкаРегистратор = ГруппировкаКонтрагент.Structure.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
                    // Обратите внимание: в платформе свойство Структура пишется на английском (Structure) или на русском (Структура)
                    // Для надежности используем стандартную коллекцию подчиненных группировок:
                    ГруппировкаРегистратор = ГруппировкаКонтрагент.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
                    ГруппировкаРегистратор.Использование = Истина;
                    
                    ПолеРегистратор = ГруппировкаРегистратор.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
                    ПолеРегистратор.Поле = Новый ПолеКомпоновкиДанных("Регистратор");
                    ПолеРегистратор.Использование = Истина;
                    
                    // Шаг 7. Обеспечиваем автоматический выбор ресурсов для корректного отображения числовых колонок
                    ГруппировкаКонтрагент.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
                    ГруппировкаРегистратор.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
                    
                КонецЕсли;
            КонецЕсли;
            
        КонецЕсли;
        
        // Шаг 8. Формируем параметры и открываем новое окно отчета с измененными настройками
        ПараметрыОтчета = Новый Структура;
        ПараметрыОтчета.Вставить("СформироватьПриОткрытии", Истина);
        ПараметрыОтчета.Вставить("Расшифровка", Новый ОписаниеРасшифровки(ДанныеРасшифровки, Расшифровка, Настройки));
        
        // Укажите имя вашего отчета вместо "Продажи"
        ОткрытьФорму("Отчет.Продажи.Форма", ПараметрыОтчета, , Истина);
        
    КонецЕсли;
КонецПроцедуры

Давайте проанализируем, что происходит в этом коде. При нажатии кнопки расшифровки мы перехватываем управление. Создавая объект ОбработкаРасшифровкиКомпоновкиДанных, мы передаем ему текущие данные расшифровки исходного отчета. С помощью метода Выполнить() мы открываем стандартное диалоговое окно платформы, где пользователь привычно выбирает поле "Регистратор". Как только выбор сделан, мы перехватываем объект настроек, полностью очищаем его структуру группировок и вручную конструируем иерархию: сначала создаем группировку Контрагент, а внутрь нее вкладываем группировку Регистратор. В конце мы запускаем формирование нового отчета с этими настройками.

Вариант 2. Обработка расшифровки в модуле объекта (Серверный метод)

Иногда логику отчета не хочется выносить в модуль формы (например, если отчет должен одинаково работать при вызове из разных мест системы или в регламентных заданиях). В таком случае мы можем использовать предопределенную процедуру ПриКомпоновкеРезультата в модуле объекта отчета.

Основной вопрос, который возникает у разработчиков при этом подходе: как в коде модуля объекта понять, что текущая компоновка вызвана именно механизмом расшифровки, а не обычным формированием отчета?

Выяснить это достаточно просто. В типовых конфигурациях 1С (особенно построенных на базе Библиотеки Стандартных Подсистем - БСП) при активации расшифровки платформа генерирует новую структуру настроек. При этом создаваемой группировке автоматически присваивается имя "Расшифровка". Мы можем проверить это имя, либо проанализировать состав полей первой группировки в настройках компоновщика.

Рассмотрим пример серверного кода в модуле объекта нашего отчета:


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

Разберем этот вариант подробнее. Здесь ключевым моментом является ручной вызов процессора компоновки и процессора вывода. Так как мы установили СтандартнаяОбработка = Ложь, платформа ожидает, что разработчик сам заполнит переданный ДокументРезультат. Мы генерируем макет компоновки с нашими новыми настройками (где структура заменена на Контрагент -> Регистратор), инициализируем процессор компоновки и выводим данные в табличный документ.

Вариант 3. Добавление пользовательского пункта меню расшифровки

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

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


&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    ОбработкаРасшифровки = Новый ОбработкаРасшифровкиКомпоновкиДанных(
        ДанныеРасшифровки, 
        Новый ИсточникДоступныхНастроекКомпоновкиДанных(АдресСхемыКомпоновкиДанных)
    );
    
    // Формируем список стандартных действий
    ДоступныеДействия = Новый Массив;
    ДоступныеДействия.Добавить(ДействиеОбработкиРасшифровкиКомпоновкиДанных.ОткрытьЗначение);
    
    // Создаем дополнительное настраиваемое действие
    ДополнительныеДействия = Новый СписокЗначений;
    ДополнительныеДействия.Добавить("ДетализацияКонтрагентРегистратор", "Детализировать по Контрагенту и Регистратору");
    
    Перем ВыполненноеДействие;
    
    // Вызываем диалог с добавлением нашего пункта меню
    Настройки = ОбработкаРасшифровки.Выполнить(
        Расшифровка, 
        ВыполненноеДействие, 
        ДоступныеДействия, 
        ДополнительныеДействия
    );
    
    // Проверяем, выбрал ли пользователь наше собственное действие
    Если ВыполненноеДействие = "ДетализацияКонтрагентРегистратор" Тогда
        
        // Получаем текущие базовые настройки отчета
        НовыеНастройки = Объект.КомпоновщикНастроек.ПолучитьНастройки();
        НовыеНастройки.Структура.Очистить();
        
        // Строим структуру группировок
        ГруппировкаКонтрагент = НовыеНастройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
        ГруппировкаКонтрагент.Использование = Истина;
        ПолеК = ГруппировкаКонтрагент.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
        ПолеК.Поле = Новый ПолеКомпоновкиДанных("Контрагент");
        ПолеК.Использование = Истина;
        
        ГруппировкаРегистратор = ГруппировкаКонтрагент.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
        ГруппировкаРегистратор.Использование = Истина;
        ПолеР = ГруппировкаРегистратор.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
        ПолеР.Поле = Новый ПолеКомпоновкиДанных("Регистратор");
        ПолеР.Использование = Истина;
        
        ГруппировкаКонтрагент.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
        ГруппировкаРегистратор.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
        
        // Открываем новую форму отчета с сформированными настройками
        ПараметрыОтчета = Новый Структура;
        ПараметрыОтчета.Вставить("СформироватьПриОткрытии", Истина);
        ПараметрыОтчета.Вставить("Расшифровка", Новый ОписаниеРасшифровки(ДанныеРасшифровки, Расшифровка, НовыеНастройки));
        
        ОткрытьФорму("Отчет.Продажи.Форма", ПараметрыОтчета, , Истина);
        
    ИначеЕсли Настройки <> Неопределено Тогда
        
        // Если выбрано стандартное действие (например, Открыть значение)
        ПараметрыОтчета = Новый Структура;
        ПараметрыОтчета.Вставить("СформироватьПриОткрытии", Истина);
        ПараметрыОтчета.Вставить("Расшифровка", Новый ОписаниеРасшифровки(ДанныеРасшифровки, Расшифровка, Настройки));
        
        ОткрытьФорму("Отчет.Продажи.Форма", ПараметрыОтчета, , Истина);
        
    КонецЕсли;
    
КонецПроцедуры

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

Важные нюансы при работе с автополями

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

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

← На главную