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