Как определить, по какому полю выполнена группировка в конкретном узле дерева значений СКД

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

При программной работе с результатом компоновки данных разработчики часто сталкиваются с необходимостью выгрузить отчет в дерево значений для последующего анализа или вывода на форму. Все идет гладко, пока мы работаем с линейными группировками. Но стоит нам включить тип группировки "Иерархия" (например, для иерархических справочников «Контрагенты» или «Номенклатура»), как стандартные механизмы позиционирования начинают давать сбой.

Основная проблема заключается в следующем: стандартный метод дерева значений Уровень() возвращает физический индекс строки в дереве. Если у нас настроена иерархическая группировка, то папки и элементы одного и того же справочника будут находиться на разных уровнях вложенности. В результате визуальный третий уровень дерева может относиться как к подчиненному элементу первой группировки, так и к началу второй группировки. Метод Уровень() здесь нам не помощник.

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

Способ 1. Анализ заполнения колонок (Обход справа налево)

Давайте проанализируем поведение Системы компоновки данных при выводе группировок. В СКД действует жесткое правило: для узла вышестоящей группировки значения всех нижележащих группировок всегда будут равны NULL (или Неопределено после выгрузки в дерево значений).

Представим, что пользователь настроил в отчете следующую структуру группировок:

ВидДеятельности (Иерархия) -> НоменклатурнаяГруппа (Иерархия) -> Номенклатура

Когда СКД формирует строку дерева для группировки "ВидДеятельности", колонки "НоменклатурнаяГруппа" и "Номенклатура" гарантированно останутся незаполненными. Для строки группировки "НоменклатурнаяГруппа" будет заполнено поле "ВидДеятельности" и "НоменклатурнаяГруппа", но поле "Номенклатура" будет содержать Неопределено.

Таким образом, мы можем написать универсальную функцию для определения текущей группировки строки дерева путем проверки колонок с конца (справа налево). Разберем пример реализации этого алгоритма:


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

Этот метод чрезвычайно прост в реализации, не требует изменения схемы компоновки данных и отлично работает в динамических отчетах, где состав и порядок группировок определяются пользователем на лету.

Способ 2. Маркировка группировок через вычисляемые поля и ресурсы

Если структура отчета относительно статична или вы хотите переложить логику определения уровней на саму СКД, можно использовать мощный инструмент — расчет ресурсов по различным группировкам. Рассмотрим, как настроить эту схему по шагам:

  1. Перейдем на вкладку "Вычисляемые поля" в конструкторе схемы компоновки данных. Добавим новое поле с именем ИмяТекущейГруппировки. Выражение поля оставим пустым.
  2. Перейдем на вкладку "Ресурсы". Добавим поле ИмяТекущейГруппировки в список ресурсов столько раз, сколько группировок используется в вашем отчете.
  3. Для каждой строки ресурса укажем собственное выражение расчета и привяжем его к конкретному полю группировки в колонке "Рассчитывать по...":
    • Для группировки ВидДеятельности в выражении укажем: "ВидДеятельности" (обязательно в двойных кавычках, чтобы СКД восприняла это как строку).
    • Для группировки НоменклатурнаяГруппа в выражении укажем: "НоменклатурнаяГруппа".
  4. В настройках компоновки добавим наше вычисляемое поле ИмяТекущейГруппировки в список выбранных полей.

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

Программный анализ такой строки дерева становится тривиальной задачей:


ИмяГруппировки = СтрокаДерева.ИмяТекущейГруппировки;
Если ИмяГруппировки = "ВидДеятельности" Тогда
    // Обрабатываем первый уровень группировки
ИначеЕсли ИмяГруппировки = "НоменклатурнаяГруппа" Тогда
    // Обрабатываем второй уровень группировки
КонецЕсли;

Способ 3. Использование системного поля "Уровень в группировке"

В платформе 1С:Предприятие 8 предусмотрено встроенное системное поле, о котором разработчики часто забывают. Помимо стандартной функции Уровень(), возвращающей абсолютную глубину элемента в иерархическом дереве, СКД поддерживает поле СистемныеПоля.УровеньВГруппировке.

Разница между ними принципиальна:

Вы можете добавить это поле в настройки отчета ("Выбранные поля") и использовать его значение для точного позиционирования при обходе дерева значений.

Способ 4. Получение информации через Данные Расшифровки

Если дерево значений выведено на форму и пользователь интерактивно взаимодействует с ним (например, осуществляет клик по строке для установки отбора в соседнем динамическом списке), наиболее надежным и профессиональным решением будет использование данных расшифровки СКД.

Рассмотрим пример программной компоновки результата с заполнением данных расшифровки и последующим анализом активной строки:


// 1. Код формирования дерева значений с расшифровкой
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВДеревоЗначений;
ДеревоРезультата = Новый ДеревоЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоРезультата);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

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


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

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

Этот способ является наиболее универсальным и платформенным, так как он гарантирует 100% точность определения метаданных строки дерева независимо от того, насколько сложной является структура отчета и сколько уровней иерархии содержится в исходных справочниках.

Резюме

Мы подробно разобрали различные варианты решения проблемы определения полей группировки в дереве СКД. Выбор конкретного метода зависит от архитектуры вашего решения:

← На главную