Как настроить расшифровку в отчете на СКД для открытия другого макета или детального отчета?

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

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

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

Понимание механизма расшифровки в СКД

Прежде чем переходить к написанию кода, выясним причину, почему при попытке получить данные в событии ОбработкаРасшифровки на клиенте мы видим лишь числовой идентификатор. Этот идентификатор — не что иное, как индекс элемента в специальной коллекции данных расшифровки.

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

Шаг 1. Перехват события расшифровки на клиенте и отключение стандартного меню

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

Для того чтобы при двойном клике не открывалось стандартное всплывающее окно 1С, мы должны принудительно отключить стандартную обработку события платформой.

Рассмотрим пример клиентской процедуры:


&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
    
    // Отключаем стандартное меню расшифровки 1С
    СтандартнаяОбработка = Ложь;
    
    // Получаем параметры расшифровки на сервере
    ПараметрыДетализации = ПолучитьПараметрыРасшифровкиНаСервере(Расшифровка);
    
    // Если параметры успешно получены, открываем детальный отчет
    Если ПараметрыДетализации <> Неопределено Тогда
        ОткрытьДетальныйОтчет(ПараметрыДетализации);
    КонецЕсли;

КонецПроцедуры

Шаг 2. Получение значений группировок на сервере (рекурсивный обход)

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

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


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

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

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

Шаг 3. Открытие детального отчета с передачей параметров

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

Для реализации этого логического шага добавим процедуру на клиенте:


&НаКлиенте
Процедура ОткрытьДетальныйОтчет(ПараметрыДетализации)
    
    ПараметрыФормы = Новый Структура;
    // Указываем системе автоматически выполнить компоновку при открытии формы
    ПараметрыФормы.Вставить("СформироватьПриОткрытии", Истина);
    
    // Передаем собранную структуру отбора
    ПараметрыФормы.Вставить("Отбор", ПараметрыДетализации.Отбор);
    
    // Если у вас в отчете несколько вариантов, можно сразу указать нужный вариант
    ПараметрыФормы.Вставить("КлючВарианта", "ДетализацияПоСотрудникам");
    
    // Открываем форму этого же отчета (или любого другого внешнего/встроенного отчета)
    ОткрытьФорму("Отчет.ИмяВашегоОтчета.Форма", ПараметрыФормы, ЭтаФорма);
    
КонецПроцедуры

Шаг 4. Программная замена основного макета СКД на другой макет

Что делать в ситуации, когда детальный отчет должен строиться по совершенно другой схеме компоновки данных (например, когда у нас в одном внешнем отчете добавлено два макета СКД: ОсновнаяСхемаКомпоновкиДанных и МакетДетализации)? Для более глубокого изучения этой темы можно рассмотреть пример использования двух разных схем компоновки в одном отчете.

В этом случае в форме открываемого детального отчета необходимо перехватить событие ПриСозданииНаСервере. Мы программно инициализируем компоновщик настроек новой схемой компоновки данных, загрузим настройки по умолчанию и применим переданные параметры отбора.

Рассмотрим, как это выглядит в модуле формы открываемого отчета:


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

Шаг 5. Альтернативный метод настройки расшифровки без написания кода

Если вы выводите отчет с помощью ручного макета оформления ячеек (вкладка «Макеты» в конструкторе СКД), задачу можно значительно упростить. Платформа позволяет декларативно сопоставить значение ячейки с параметром расшифровки:

  1. Перейдите на вкладку «Макеты» в схеме компоновки данных вашего отчета.
  2. Выделите ячейку, для которой вы хотите настроить собственное поведение расшифровки, и откройте ее свойства нажатием клавиш Ctrl + Enter.
  3. Найдите строку «Параметр расшифровки» и введите понятное имя переменной, например, ВыбранныйСотрудник.
  4. В поле выражения этого параметра укажите путь к данным схемы, например, Сотрудник.

При таком подходе при двойном клике на ячейку в обработчик расшифровки на клиенте в параметр Расшифровка вернется не абстрактный индекс элемента, а непосредственно готовая ссылка на объект информационной базы (например, элемент справочника ФизическиеЛица). Это полностью избавляет от необходимости рекурсивного обхода дерева данных расшифровки на сервере.

Важное предостережение: избегайте ошибок передачи типов

Разработчики часто сталкиваются с критической ошибкой: «Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа».

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

← На главную