Разработка отчетов на системе компоновки данных (СКД) — стандартная задача для разработчика 1С. Однако стандартное поведение платформы при двойном клике на ячейку отчета устраивает пользователей далеко не всегда. Вместо системного меню с предложением отфильтровать или сгруппировать данные часто требуется открыть детализированный отчет (например, подробную информацию по сотрудникам, контрагентам или документам-регистраторам) или вывести данные по совершенно другому макету компоновки — для этой задачи есть готовый механизм связывания отчетов и динамической расшифровки.
В этой статье мы подробно разберем, как перехватить событие расшифровки, программно извлечь значения всех родительских группировок из временного хранилища, передать их в другую форму и сформировать отчет на основе альтернативного макета СКД.
Прежде чем переходить к написанию кода, выясним причину, почему при попытке получить данные в событии ОбработкаРасшифровки на клиенте мы видим лишь числовой идентификатор. Этот идентификатор — не что иное, как индекс элемента в специальной коллекции данных расшифровки.
Сами же данные расшифровки (сведения о полях, группировках и их значениях для каждой ячейки сформированной таблицы) хранятся во временном хранилище на сервере. Адрес этого хранилища содержится в реквизите формы отчета ДанныеРасшифровки. Чтобы вытащить реальные ссылки на объекты (например, конкретного сотрудника или подразделение), нам потребуется обратиться к этому хранилищу на сервере и выполнить обход дерева родительских элементов.
Первым делом нам необходимо открыть форму нашего отчета, перейти к свойствам поля табличного документа (в котором выводится результат отчета) и создать обработчик события РезультатОбработкаРасшифровки.
Для того чтобы при двойном клике не открывалось стандартное всплывающее окно 1С, мы должны принудительно отключить стандартную обработку события платформой.
Рассмотрим пример клиентской процедуры:
&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
// Отключаем стандартное меню расшифровки 1С
СтандартнаяОбработка = Ложь;
// Получаем параметры расшифровки на сервере
ПараметрыДетализации = ПолучитьПараметрыРасшифровкиНаСервере(Расшифровка);
// Если параметры успешно получены, открываем детальный отчет
Если ПараметрыДетализации <> Неопределено Тогда
ОткрытьДетальныйОтчет(ПараметрыДетализации);
КонецЕсли;
КонецПроцедуры
Теперь реализуем серверную часть. Нам необходимо извлечь коллекцию данных расшифровки из временного хранилища по адресу ДанныеРасшифровки и программно подняться вверх по иерархии группировок, чтобы собрать все ключевые поля, к которым относится кликнутая ячейка. В качестве альтернативного варианта можно взять за основу готовый пример отчета для получения значений группировок в СКД.
Напишем функции для рекурсивного сбора параметров. Обратите внимание на то, что на клиент мы должны возвращать исключительно простую структуру с примитивными типами или ссылками, избегая передачи тяжелых типов СКД.
&НаСервере
Функция ПолучитьПараметрыРасшифровкиНаСервере(Знач ИндексЭлементаРасшифровки)
АдресДанныхРасшифровки = ДанныеРасшифровки;
ДанныеРасшифровкиОтчета = ПолучитьИзВременногоХранилища(АдресДанныхРасшифровки);
Если ДанныеРасшифровкиОтчета = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ЭлементРасшифровки = ДанныеРасшифровкиОтчета.Элементы[ИндексЭлементаРасшифровки];
СтруктураРезультата = Новый Структура;
// Получаем имя поля, по которому непосредственно кликнул пользователь
ИмяРасшифровываемогоПоля = Неопределено;
ПоляЭлементаРасшифровки = ЭлементРасшифровки.ПолучитьПоля();
Если ПоляЭлементаРасшифровки.Количество() > 0 Тогда
ИмяРасшифровываемогоПоля = ПоляЭлементаРасшифровки[0].Поле;
КонецЕсли;
СтруктураРезультата.Вставить("ИмяПоля", ИмяРасшифровываемогоПоля);
СтруктураРезультата.Вставить("Отбор", ПолучитьСтруктуруПолейРасшифровки(ЭлементРасшифровки));
Возврат СтруктураРезультата;
КонецФункции
&НаСервере
Функция ПолучитьСтруктуруПолейРасшифровки(ЭлементРасшифровки)
МассивЭлементовРасшифровки = ПолучитьМассивРодительскихЭлементовРасшифровки(ЭлементРасшифровки);
МассивЭлементовРасшифровки.Добавить(ЭлементРасшифровки);
СтруктураПолей = Новый Структура;
Для Каждого текЭлементРасшифровки Из МассивЭлементовРасшифровки Цикл
Для Каждого текПоле Из текЭлементРасшифровки.ПолучитьПоля() Цикл
// Убираем точки из имен полей (например, "ФизическоеЛицо.Код" превращаем в рабочий ключ структуры)
ИмяКлюча = СтрЗаменить(текПоле.Поле, ".", "");
СтруктураПолей.Вставить(ИмяКлюча, текПоле.Значение);
КонецЦикла;
КонецЦикла;
Возврат СтруктураПолей;
КонецФункции
&НаСервере
Функция ПолучитьМассивРодительскихЭлементовРасшифровки(ЭлементРасшифровки)
МассивРодителей = Новый Массив;
МассивПрямыхРодителей = ЭлементРасшифровки.ПолучитьРодителей();
Для Каждого РодительскийЭлементРасшифровки Из МассивПрямыхРодителей Цикл
МассивВышестоящихРодителей = ПолучитьМассивРодительскихЭлементовРасшифровки(РодительскийЭлементРасшифровки);
Для Каждого ЭлементРасшифровкиВышестоящегоРодителя Из МассивВышестоящихРодителей Цикл
Если ТипЗнч(ЭлементРасшифровкиВышестоящегоРодителя) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
МассивРодителей.Добавить(ЭлементРасшифровкиВышестоящегоРодителя);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Для Каждого ПрямойРодитель Из МассивПрямыхРодителей Цикл
Если ТипЗнч(ПрямойРодитель) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
МассивРодителей.Добавить(ПрямойРодитель);
КонецЕсли;
КонецЦикла;
Возврат МассивРодителей;
КонецФункции
После того как структура отборов успешно собрана на сервере и возвращена на клиент, нам нужно открыть форму детального отчета. При этом мы передадим в параметры формы признак автоматического формирования и саму структуру отборов.
Для реализации этого логического шага добавим процедуру на клиенте:
&НаКлиенте
Процедура ОткрытьДетальныйОтчет(ПараметрыДетализации)
ПараметрыФормы = Новый Структура;
// Указываем системе автоматически выполнить компоновку при открытии формы
ПараметрыФормы.Вставить("СформироватьПриОткрытии", Истина);
// Передаем собранную структуру отбора
ПараметрыФормы.Вставить("Отбор", ПараметрыДетализации.Отбор);
// Если у вас в отчете несколько вариантов, можно сразу указать нужный вариант
ПараметрыФормы.Вставить("КлючВарианта", "ДетализацияПоСотрудникам");
// Открываем форму этого же отчета (или любого другого внешнего/встроенного отчета)
ОткрытьФорму("Отчет.ИмяВашегоОтчета.Форма", ПараметрыФормы, ЭтаФорма);
КонецПроцедуры
Что делать в ситуации, когда детальный отчет должен строиться по совершенно другой схеме компоновки данных (например, когда у нас в одном внешнем отчете добавлено два макета СКД: ОсновнаяСхемаКомпоновкиДанных и МакетДетализации)? Для более глубокого изучения этой темы можно рассмотреть пример использования двух разных схем компоновки в одном отчете.
В этом случае в форме открываемого детального отчета необходимо перехватить событие ПриСозданииНаСервере. Мы программно инициализируем компоновщик настроек новой схемой компоновки данных, загрузим настройки по умолчанию и применим переданные параметры отбора.
Рассмотрим, как это выглядит в модуле формы открываемого отчета:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартноеОформление)
// Проверяем, переданы ли параметры отбора для детализации
Если Параметры.Свойство("Отбор") Тогда
// Получаем альтернативный макет СКД из нашего отчета
ОбъектОтчет = РеквизитФормыВЗначение("Отчет");
АльтернативнаяСхема = ОбъектОтчет.ПолучитьМакет("МакетДетализации");
// Инициализируем компоновщик настроек новой схемой компоновки
ИсточникНастроек = Новый ИсточникДоступныхНастроекКомпоновкиДанных(АльтернативнаяСхема);
Отчет.КомпоновщикНастроек.Инициализировать(ИсточникНастроек);
// Загружаем дефолтные настройки альтернативной схемы
Отчет.КомпоновщикНастроек.ЗагрузитьНастройки(АльтернативнаяСхема.НастройкиПоУмолчанию);
// Применяем переданные отборы к новому компоновщику
Для Каждого ЭлементОтбора Из Параметры.Отбор Цикл
НовыйОтбор = Отчет.КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйОтбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(ЭлементОтбора.Ключ);
НовыйОтбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
НовыйОтбор.ПравоеЗначение = ЭлементОтбора.Значение;
НовыйОтбор.Использование = Истина;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Если вы выводите отчет с помощью ручного макета оформления ячеек (вкладка «Макеты» в конструкторе СКД), задачу можно значительно упростить. Платформа позволяет декларативно сопоставить значение ячейки с параметром расшифровки:
Ctrl + Enter.ВыбранныйСотрудник.Сотрудник.При таком подходе при двойном клике на ячейку в обработчик расшифровки на клиенте в параметр Расшифровка вернется не абстрактный индекс элемента, а непосредственно готовая ссылка на объект информационной базы (например, элемент справочника ФизическиеЛица). Это полностью избавляет от необходимости рекурсивного обхода дерева данных расшифровки на сервере.
Разработчики часто сталкиваются с критической ошибкой: «Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа».
Запомните ключевое правило: объекты типов ЭлементРасшифровкиКомпоновкиДанныхПоля, ДанныеРасшифровкиКомпоновкиДанных и сопутствующие им коллекции СКД могут существовать только в контексте сервера. Попытка передать их напрямую на клиент вызовет сбой работы платформы. Все операции по анализу расшифровки производите строго внутри серверных функций, возвращая на клиент только простые структуры, массивы и ссылки.