Применение рекурсивных функций при построении отчета с иерархией
Особенностью справочников в восьмерке является возможность их неограниченной иерархии. Тот, кто имел дело только с 7.7, столкнувшись с теоретической возможностью сколь угодного числа вложений, может стать в тупик: как организовать обход группировок, когда заранее не известно их число…
- Описание
- Подробнее
Описание
Особенностью справочников в восьмерке является возможность их неограниченной иерархии. Тот, кто имел дело только с 7.7, столкнувшись с теоретической возможностью сколь угодного числа вложений, может стать в тупик: как организовать обход группировок, когда заранее не известно их число?
Не буду рассматривать вариант с построителем отчета: делаем все вручную.
Посмотрим на задачу в общем: для каждого товара его иерархия – это некая последовательность данных, которую необходимо всю обойти, не зная заранее ее длины. Собственно, это классическая задача рекурсивного обхода. К сожалению, молодые программисты порой не понимают и боятся рекурсивных вызовов, но не мы с вами!
Строим процедуру на кнопке «Сформировать»:
Процедура
ДействияФормыСформировать
(
Кнопка
)
ТекстЗапроса
= «ВЫБРАТЬ
| ПартииТоваровНаСкладахОстатки
.
Номенклатура
,
| ПартииТоваровНаСкладахОстатки
.
Номенклатура
.
Родитель
,
| ПартииТоваровНаСкладахОстатки
.
КоличествоОстаток |ИЗ | РегистрНакопления
.
ПартииТоваровНаСкладах
.
Остатки КАК ПартииТоваровНаСкладахОстатки
«; Запрос =
Новый
Запрос(
ТекстЗапроса
)
; Товары
=
З
.
Выполнить
().
Выгрузить
()
;
Итак, у нас имеется таблица остатков товаров. Добавим в нее и заполним свою колонку «Уровень»
Товары
.
Колонки
.
Добавить
(«Уровень»)
;
Для
Каждого Стр Из Товары
Цикл
Стр
.
Уровень
=
Стр
.
Номенклатура
.
Уровень;
КонецЦикла
;
Подготовим такую же таблицу, только с остатками для всевозможных групп товаров:
Группы
=
Товары
.
СкопироватьКолонки
()
;
Для
Каждого Стр Из Товары
Цикл
Родитель
=
Стр
.
Номенклатура
.
Родитель;
Пока
Родитель
<>
Справочники
.
Номенклатура
.
ПустаяСсылка
() Цикл
Нов
=
Группы
.
Добавить
()
;
Нов.
Номенклатура
=
Родитель;
Нов.
Родитель
=
Родитель
.
Родитель;
Нов.
Уровень
=
Родитель
.
Уровень
()
;
Нов.
КоличествоОстаток
=
Стр
.
КоличествоОстаток;
Родитель=
Родитель
.
Родитель;
КонецЦикла
;
КонецЦикла
;
(Понимаем, что этот модуль можно было объединить с предыдущим, но для наглядности разделяем)
Теперь стоит выполнить свертку, как у нас получится достаточно интересная таблица, в строках которой будут указаны всевозможные группы товаров с точным количеством остатков по ним.
Группы
.
Свернуть
(«Номенклатура,Родитель,Уровень»,«КоличествоОстаток»)
;
Что ж, вот тут и появляется наша волшебная рекурсивная функция, позволяющая смоделировать обход по произвольной иерархической структуре. Ее вызов завершит тело главной процедуры.
Макет
=
ПолучитьМакет
(«Макет»)
; Область
=
Макет
.
ПолучитьОбласть
(«Шапка»)
; ЭлементыФормы
.Таблица.
Вывести
(
Область
)
; Родитель
=
Справочники
.
Номенклатура
.
ПустаяСсылка
()
; ВывестиНоменклатуру
(
Макет
,0,
Родитель
,
Группы
,
Товары
)
; Область
=
Макет
.
ПолучитьОбласть
(«Подвал»)
; ЭлементыФормы
.Таблица.
Вывести
(
Область
)
;
КонецПроцедуры
Сама функция «ВывестиНоменклатуру», как и подобает рекурсии, состоит всего из нескольких строк. Структура ее проста. Предполагается, что на текущем уровне рекурсии мы находимся в начале некоторой группы, содержащей элементы и другие подгруппы. Вначале мы выводим все товары из нашей группы, затем — рекурсивно все подгруппы.
Процедура
ВывестиНоменклатуру
(
Макет
,
Уровень
,
Родитель
,
Группы
,
Товары
) // выводит товары для группы Родитель на текущем уровне Для
Каждого Стр Из Рез
Цикл Если (
Стр
.
Уровень
=
Уровень
)
и
(
Стр
.
Родитель
=
Родитель
) Тогда
ВывестиСтрокуТовара
(
Макет
,
Стр
)
;
КонецЕсли
;
КонецЦикла // выводит рекурсивно подгруппы товаров для группы Родитель на текущем уровне Для
Каждого Стр Из Группы
Цикл Если (
Стр
.
Уровень
=
Уровень
)
и
(
Стр
.
Родитель
=
Родитель
) Тогда
ВывестиСтрокуГруппы
(
Макет
,
Стр
)
;
ВывестиНоменклатуру (
Макет
,
Уровень
,
Родитель
,
Группы
,
Товары
)
;
КонецЕсли
;
КонецЦикла
;
КонецПроцедуры
Стоит доопределить элементарные функции вывода строк таблицы на печать «ВывестиСтрокуТовара» и «ВывестиСтрокуГруппы», и задача решена полностью.
Напоследок, в качестве изюминки хочу предложить обратить внимание, что достаточно легко связать окраску строки группы товаров, ее название в макете с уровнем вложенности, как, например, показано на этом рисунке:

