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