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