Задача: нередко при разработке отчетов возникает необходимость многоуровневой нумерации элементов отчета в соответствии с его структурой. Система компоновки данных не предоставляет средств для решения этой задачи. Проблема усугубляется в случае отчетов с иерархической группировкой где количество уровней вложенности произвольно и заранее неизвестно.
Попытки решения данной задачи только средствами СКД успехом не увенчались. Поиск существующих решений желаемых результатов не принес:
- вариант Ильи Васильева (swimdog) имеет ограничение по уровням вложенности и требует анализа выходного табличного документа для определения размещения номера;
-
вариант Алексея А (Isonic) с произвольной иерархией представляется неудобным в реализации так как вложенность заранее неизвестна и при изменении группировок придется переопределять значение поля иерархической нумерации;
Решение. Предлагаемое решение позволяет решить задачу многоуровневой нумерации путем формирования номера при поэлементном программном выводе отчета. Преимуществом решения является отсутствие привязки к структуре данных, уровень иерархии определяется вложенностью элементов поступающих от процессора компоновки. Для нумерации достаточно создать вычисляемое поле и добавить несложный код вычисления номера в вывод отчета. Настройка размещения и отображения поля осуществляется стандартными средствами СКД.
Фрагмент кода формирования иерархического номера:
///...
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.НачатьВывод();
ЭлементРезультата = ПроцессорКомпоновки.Следующий();
КэшМакетов = Новый Структура;
//{{Инициализация переменных и создание структур для расчета иерархической нумерации
НомерПоИерархии = 0;
Уровень = 0;
СтекНомеров = Новый ТаблицаЗначений;
СтекНомеров.Колонки.Добавить("Уровень", Новый ОписаниеТипов("Число"));
СтекНомеров.Колонки.Добавить("Номер", Новый ОписаниеТипов("Число"));
//}}
Пока ЭлементРезультата <> Неопределено Цикл
//{{Расчет номера по иерархии и его вывод
Если ЭлементРезультата.ТипЭлемента = ТипЭлементаРезультатаКомпоновкиДанных.Начало Тогда
Уровень = Уровень + 1;
ИначеЕсли (ЭлементРезультата.ТипЭлемента = ТипЭлементаРезультатаКомпоновкиДанных.Конец) Тогда
Уровень = Уровень - 1;
КонецЕсли;
Если ЭлементРезультата.ЗначенияПараметров.Количество() > 0 Тогда
МассивИменПараметров = ПолучитьИменаПараметровВМакетеКомпоновкиПоИмениПоля("НомерПоИерархии", ЭлементРезультата.Макет, МакетКомпоновкиДанных, КэшМакетов);
Если МассивИменПараметров.Количество() > 0 Тогда
НомерПоИерархии = ПолучитьМногоуровневыйНомерПоИерархии(Уровень, СтекНомеров);
Для Каждого ИмяПараметра из МассивИменПараметров Цикл
ЭлементРезультата.ЗначенияПараметров[ИмяПараметра].Значение = НомерПоИерархии;
КонецЦикла
КонецЕсли
КонецЕсли;
//}}
ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
ЭлементРезультата = ПроцессорКомпоновки.Следующий();
КонецЦикла;
ПроцессорВывода.ЗакончитьВывод()
КонецПроцедуры
Код функции ПолучитьМногоуровневыйНомерПоИерархии:
Функция ПолучитьМногоуровневыйНомерПоИерархии(УровеньОтчетаСКД, СтекНомеров);
НомерНаУровне = СтекНомеров.Найти(УровеньОтчетаСКД, "Уровень");
Если НомерНаУровне = Неопределено Тогда
Стр = СтекНомеров.Добавить();
Стр.Уровень = УровеньОтчетаСКД;
Стр.Номер = 1;
Иначе
НомерНаУровне.Номер = НомерНаУровне.Номер + 1
Конецесли;
СтрНомерПоИерархии = "";
НомераКУдалению = Новый Массив;
Для Каждого Номер Из СтекНомеров Цикл
Если Номер.Уровень <= УровеньОтчетаСКД Тогда
СтрНомерПоИерархии = СтрНомерПоИерархии + Номер.Номер + ".";
Иначе
//Чистим элементы стека нижних уровней
НомераКУдалению.Добавить(Номер);
КонецЕсли
КонецЦикла;
Для каждого Номер Из НомераКУдалению Цикл
СтекНомеров.Удалить(Номер);
КонецЦикла;
Возврат СтрНомерПоИерархии;
КонецФункции
Также вам потребуется функция "ПолучитьИменаПараметровВМакетеКомпоновкиПоИмениПоля" которую можно взять из статьи о установке собственных значений полей при программном выводе отчета СКД либо скачать пример отчета в котором "все включено".
P.S. Алгоритм разработан для иерархической нумерации по строкам отчета, но работает и при наличии группировок колонок. Вывод номера по колонкам не предусмотрен.
Огромная благодарность Денису Урянскому (dhurricane) за вдумчивое тестирование и подсказки.
Внешний отчет с примером иерархической нумерации тестировался на платформе 8.3.13.1644.
Обновление 16.01.2020: Перезагружен файл с примером. Просьба скачавшим связаться с автором для получения новой версии, либо самостоятельно обновить код функции ПолучитьИменаПараметровВМакетеКомпоновкиПоИмениПоля() из текста статьи про нее. В прежней реализации возможен несистематический пропуск параметров.