12 февр. 2017 г.

Произвольная иерархия в СКД: вывод дерева значений


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

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

Для демонстрации создам обработку, содержащую дерево значений, которое будет редактироваться на форме и табличный документ для вывода результата.



Реализуем заполнение служебных полей. ИД родителя для корневой строки буду считать равным нулю, поэтому все остальные ИД строк сохраняю с увеличением на единицу.
&НаКлиенте
Процедура Т_ПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
 
 Если НоваяСтрока Тогда
  
  ТекущиеДанные = Элемент.ТекущиеДанные;
  ТекущиеДанные.Ид = ТекущиеДанные.ПолучитьИдентификатор() + 1;
  ТекущиеДанныеРодитель = ТекущиеДанные.ПолучитьРодителя();
  ТекущиеДанные.РодительИд = ?(ТекущиеДанныеРодитель = Неопределено, 0, ТекущиеДанныеРодитель.ПолучитьИдентификатор() + 1);
  
 КонецЕсли;
 
КонецПроцедуры
Теперь сделаем вывод дерева в компоновку. Сначала хотел показать, как настроить схему компоновки в редакторе, но потом решил добавить немного универсальности и генерировать ее программно на основании дерева значений.
&НаСервере
Процедура СформироватьТабличныйДокументНаСервере()
 
 ТабДокумент = Новый ТабличныйДокумент;
 
 ДанныеДерева = РеквизитФормыВЗначение("Т");
 
 ИсточникиДанных = Новый Структура("ДанныеДерева", ДанныеДерева);
 СхемаКомпоновки = ПолучитьСхемуКомпоновкиПоДереву(ДанныеДерева);
 
 КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
 МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновки, СхемаКомпоновки.НастройкиПоУмолчанию);
 
 ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных; 
 ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ИсточникиДанных);
 
 ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
 ПроцессорВывода.УстановитьДокумент(ТабДокумент);
 ПроцессорВывода.Вывести(ПроцессорКомпоновки);
 
КонецПроцедуры

&НаСервере
Функция ПолучитьСхемуКомпоновкиПоДереву(ДанныеДерева, ВыбранныеПоляДерева = Неопределено, ПолеИД = "ИД", ПолеРодительИД = "РодительИД")
 
 Если ВыбранныеПоляДерева = Неопределено Тогда
  ВыбранныеПоляДерева = Новый Массив;
 КонецЕсли;
 ЗаполнитьВыбранныеПоляДерева = НЕ ВыбранныеПоляДерева.Количество();
 
 СхемаКомпоновки = Новый СхемаКомпоновкиДанных;
 НастройкиКомпоновки = СхемаКомпоновки.НастройкиПоУмолчанию;
 
 ИсточникДанных = СхемаКомпоновки.ИсточникиДанных.Добавить();
 ИсточникДанных.Имя = "ИсточникДанных";
 ИсточникДанных.ТипИсточникаДанных = "Local";  
 
 НаборДанных = СхемаКомпоновки.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
 НаборДанных.ИмяОбъекта = "ДанныеДерева";
 НаборДанных.Имя = "НаборДанных";
 НаборДанных.ИсточникДанных = "ИсточникДанных"; 
 
 Для Каждого КолонкаДерева Из ДанныеДерева.Колонки Цикл
  
  ИмяПоля = КолонкаДерева.Имя;
  ЗаголовокПоля = КолонкаДерева.Заголовок;
  
  ПолеНабора = НаборДанных.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
  ПолеНабора.Заголовок = ЗаголовокПоля;
  ПолеНабора.Поле = ИмяПоля;
  
  Если ЗаполнитьВыбранныеПоляДерева И НЕ (ИмяПоля = ПолеИД ИЛИ ИмяПоля = ПолеРодительИД)Тогда
   ВыбранныеПоляДерева.Добавить(Новый Структура("Имя, Заголовок", ИмяПоля, ЗаголовокПоля));
  КонецЕсли;
  
 КонецЦикла;
 
 СвязьНабора = СхемаКомпоновки.СвязиНаборовДанных.Добавить();
 СвязьНабора.НаборДанныхИсточник = "НаборДанных";
 СвязьНабора.НаборДанныхПриемник = "НаборДанных";
 СвязьНабора.ВыражениеИсточник = ПолеИД;
 СвязьНабора.ВыражениеПриемник = ПолеРодительИд;
 СвязьНабора.НачальноеВыражение = "0";
 
 ВыбранныеПоля = НастройкиКомпоновки.Выбор.Элементы;
 Для Каждого ПолеДерева Из ВыбранныеПоляДерева Цикл
  ПолеОтчет = ВыбранныеПоля.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
  ПолеОтчет.Поле = Новый ПолеКомпоновкиДанных(ПолеДерева.Имя);
  ПолеОтчет.Заголовок = ПолеДерева.Заголовок;
 КонецЦикла;
 
  СтруктураГруппировки = НастройкиКомпоновки.Структура; 
 ЭлементГруппировки = СтруктураГруппировки.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
  ЭлементГруппировки.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
 
 Возврат СхемаКомпоновки; 
 
КонецФункции// ПолучитьСхемуКомпоновкиПоДереву()

&НаКлиенте
Процедура СформироватьТабличныйДокумент(Команда)
 
 СформироватьТабличныйДокументНаСервере();
 
КонецПроцедуры
Проверяем результат, заполняем дерево:

Получаем табличный документ:


Сама обработка, включает макет схемы компоновки для демонстрации настроек.

8 комментариев:

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

    ОтветитьУдалить
    Ответы
    1. Из руководстве разработчика "Набор данных – объект используется для вывода в отчет информации из некоторого объекта встроенного языка: таблицы значений, результата запроса, текущего документа и т. п." https://its.1c.ru/db/v8317doc#bookmark:dev:TI000000553
      Ничего насчет ограничения по типу не сказано.

      Удалить
    2. Руководство это теория. На практике - не работает и никогда не работало. Не дезинформируйте читателей.

      Удалить
    3. Как насчет скачать и проверить? Ссылка в конце публикации.

      Удалить
    4. Ссылка не работает, ещё позавчера убедился. Тупо виснет.

      Удалить
  2. Огромное спасибо за статью!!!
    Так намного легче и проще чем свой вывод рисовать!

    ОтветитьУдалить
  3. Тоже спасиба! За идею, как дерево с СКД дружить.

    ОтветитьУдалить