Вывод дерева в табличный документ СКД

09.07.20

Разработка - Универсальные функции

Вывод содержимого элемента формы "ДанныеФормыДерево" в табличный документ средствами СКД.

Задача возникла потому, что ... в панельке "Вывести список" не оказалось флажка "С подчинёнными". Решать надо было срочно, вдобавок клиент хотел результат со всеми бантиками условного оформления, а это платформа не умеет. В итоге немного обычного программного создания и настройки СКД, и получилась универсальная механика, пригодная к копипасту и немедленному использованию.
Что умеет: выводит дерево из элемента формы, связанного с реквизитом формы или реквизитом объекта формы, точно сохраняя иерархию и условное оформление.
Чего не умеет: сворачивать группировки, как это в элементе формы; и ещё в этой версии я не публикую вывод данных шапки и подвала дерева (хотя можно сделать). Ну и хитрые пути к данным в разыменованных колонках не факт, что все возьмёт (хотя в таких случаях и сам-то элемент формы не всегда корректно отрабатывает).

Чем ещё интересна - пожалуй, программной работой с СКД и, в частности, соединением набора с самим собой, что описано, например, https://start1c.blogspot.com/2017/01/blog-post.html и на ИС тоже встречалось. Таким образом, всё это чуть допиленный баян.
 

 
 Собственно механизм

 



// Вспомогательная для ВывестиДеревоВТабличныйДокумент
&НаСервере
Процедура РекурсивноЗаполнитьКолонкиПоЭлементам(элфРодитель,рПрефиксПути,рИмяРеквизитаДерева,рКолонкиДерева,рКолонкиТаблицы,соотРеквизитовДерева,рЭлементыВыбораСКД)
	// Выводим только видимые колонки
	Для каждого элфЭлемент Из элфРодитель.ПодчиненныеЭлементы Цикл
		Если не элфЭлемент.Видимость Тогда Продолжить КонецЕсли;
		Если ТипЗнч(элфЭлемент)=Тип("ПолеФормы") Тогда
			рПутьКДанным=СтрЗаменить(элфЭлемент.ПутьКДанным,рПрефиксПути,"");
			Если СтрНачинаетсяС(рПутьКДанным,рИмяРеквизитаДерева+".") Тогда // основная колонка или её разыменование
				мИмени=СтрРазделить(рПутьКДанным,".",Ложь);
				рИмя=мИмени.Получить(1);
				Если рИмя="_ParentKey" или рИмя="_BranchKey" Тогда Продолжить КонецЕсли;
				// вносим
				колДерева=рКолонкиДерева.Найти(рИмя);
				рекДерева=соотРеквизитовДерева.Получить(рИмя);
				Если колДерева<>Неопределено и рекДерева<>Неопределено Тогда
					рЗаголовок=элфЭлемент.Заголовок; // он приоритетнее, но если пуст, берём из реквизита
					Если ПустаяСтрока(рЗаголовок) Тогда
						Если мИмени[мИмени.Количество()-1]=рИмя Тогда
							рЗаголовок=рекДерева.Заголовок;
						Иначе
							рЗаголовок=мИмени[мИмени.Количество()-1];
						КонецЕсли;
					КонецЕсли;						
					Если ПустаяСтрока(рЗаголовок) Тогда рЗаголовок=рИмя КонецЕсли;
					//
					Если рКолонкиТаблицы.Найти(рИмя)=Неопределено Тогда
						рТипЗначения=колДерева.ТипЗначения; // так более строго и правильно, особенно для конкретизируемых вроде субконто
						рКолонкиТаблицы.Добавить(рИмя,рТипЗначения,рЗаголовок);
					КонецЕсли;
					//
					рВыбПоле=рЭлементыВыбораСКД.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
					рВыбПоле.Заголовок=рЗаголовок; // а это может быть и поле, и разыменование поля
					рВыбПоле.Поле=Новый ПолеКомпоновкиДанных(СтрЗаменить(рПутьКДанным,рИмяРеквизитаДерева+".",""));
					рВыбПоле.Использование=Истина;
					//
				КонецЕсли;				
			Иначе
				// к счастью, коллекция формы не поддерживает поле, чей путь к данным лежит к чему-то вне этой коллекции
				// а варианты Строка[0] отработаем в другой версии
			КонецЕсли;
			// также, в другой версии:
			//элфЭлемент.ПутьКДаннымКартинкиСтроки,
			//элфЭлемент.ПутьКДаннымПодвала
		ИначеЕсли ТипЗнч(элфЭлемент)=Тип("ГруппаФормы") Тогда
			рВыбГруппа=рЭлементыВыбораСКД.Добавить(Тип("ГруппаВыбранныхПолейКомпоновкиДанных"));
			рВыбГруппа.Заголовок=элфЭлемент.Заголовок;
			Если элфЭлемент.Группировка=ГруппировкаКолонок.Вертикальная Тогда
				рВыбГруппа.Расположение=РасположениеПоляКомпоновкиДанных.Вертикально;
			ИначеЕсли элфЭлемент.Группировка=ГруппировкаКолонок.Горизонтальная Тогда
				рВыбГруппа.Расположение=РасположениеПоляКомпоновкиДанных.Горизонтально;
			ИначеЕсли элфЭлемент.Группировка=ГруппировкаКолонок.ВЯчейке Тогда
				рВыбГруппа.Расположение=РасположениеПоляКомпоновкиДанных.Вместе;
			КонецЕсли;			
			рВыбГруппа.Использование=Истина;
			РекурсивноЗаполнитьКолонкиПоЭлементам(элфЭлемент,рПрефиксПути,рИмяРеквизитаДерева,рКолонкиДерева,рКолонкиТаблицы,соотРеквизитовДерева,рВыбГруппа.Элементы);
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

// Вспомогательная для ВывестиДеревоВТабличныйДокумент
&НаСервере
Процедура РекурсивноЗаполнитьТаблицуПоДереву(рВетка,тДанных,рКлючРодителя=0)
	Для каждого рПодветка Из рВетка.Строки Цикл
		стро=тДанных.Добавить();
		ЗаполнитьЗначенияСвойств(стро,рПодветка);
		стро._ParentKey=рКлючРодителя;
		стро._BranchKey=тДанных.Количество();
		РекурсивноЗаполнитьТаблицуПоДереву(рПодветка,тДанных,стро._BranchKey);
	КонецЦикла;
КонецПроцедуры

// Выводит содержимое дерева значений, размещённого на форме (т.е. коллекцию ДанныеФормыДерево) в табличный документ; возвращает табличный документ.
// При ошибке возвращает пустой таб.документ.
// Содержит внутри себя упакованную рекурсию для заполнения служебной промежуточной таблицы значений.
//
// Параметры:
//    ИмяЭлементаФормы - обязательный, строка; имя элемента формы, являющегося выводимым деревом значений;
//    ИмяВладельца - необязательный, строка; имя родительского реквизита формы, дочерним к которому может являться дерево значений
//        применяется для случаев, когда дерево является реквизитом не формы, а объекта, связанного с формой (и не обязательно основного);
//    рВетка, тДанных, рКлюРодителя - служебные параметры для рекурсии, указывать их при вызове функции НЕ следует.
//
&НаСервере
Функция ВывестиДеревоВТабличныйДокумент(ИмяЭлементаФормы, ИмяВладельца="")
Попытка
	// собственно основной механизм		
	т=Новый ТабличныйДокумент;
	
	элфВсёДерево=Элементы.Найти(ИмяЭлементаФормы);
	Если элфВсёДерево=Неопределено Тогда 
		Сообщить("Элемент формы с именем """+ИмяЭлементаФормы+""" не найден!");
		Возврат т;
	КонецЕсли;
	
	#Область ПолучениеРабочихОбъектовДерева
	Если СтрНачинаетсяС(элфВсёДерево.ПутьКДанным,ИмяВладельца+".") Тогда
		рПрефиксПути=ИмяВладельца+".";
		рИмяРеквизитаДерева=СтрЗаменить(элфВсёДерево.ПутьКДанным,рПрефиксПути,"");
		знчВсёДерево=Объект[рИмяРеквизитаДерева];			
	Иначе
		рПрефиксПути="";
		рИмяРеквизитаДерева="";
		мРеквизитовФормы=ЭтотОбъект.ПолучитьРеквизиты();
		Для каждого рекФормы Из мРеквизитовФормы Цикл
			Если рекФормы.Имя=элфВсёДерево.ПутьКДанным Тогда рИмяРеквизитаДерева=рекФормы.Имя; Прервать КонецЕсли;
		КонецЦикла;
		Если ПустаяСтрока(рИмяРеквизитаДерева) Тогда
			Сообщить("Реквизит, соответствующий элементу формы с именем """+ИмяЭлементаФормы+""", не найден!");
			Возврат т;
		КонецЕсли;
		знчВсёДерево=ЭтотОбъект[рИмяРеквизитаДерева];
	КонецЕсли;
	
	Если ТипЗнч(знчВсёДерево)<>Тип("ДанныеФормыДерево") Тогда
		Сообщить("Реквизит, соответствующий элементу формы с именем """+ИмяЭлементаФормы+""", имеет неверный тип (не ""ДанныеФормыДерево"")!");
		Возврат т;
	КонецЕсли;
	
	рДерево=ДанныеФормыВЗначение(знчВсёДерево,Тип("ДеревоЗначений"));
	
	мРеквизитовДерева=ЭтотОбъект.ПолучитьРеквизиты(элфВсёДерево.ПутьКДанным);
	соотРеквизитовДерева=Новый Соответствие;
	Для каждого рекДерева Из мРеквизитовДерева Цикл
		соотРеквизитовДерева.Вставить(рекДерева.Имя,Новый Структура("Заголовок,Путь,ТипЗначения",рекДерева.Заголовок,рекДерева.Путь,рекДерева.ТипЗначения));
	КонецЦикла;		
	#КонецОбласти
	
	#Область ПервичнаяПодготовка
	тДанных=Новый ТаблицаЗначений;
	тДанных.Колонки.Добавить("_ParentKey",Новый ОписаниеТипов("Число",Новый КвалификаторыЧисла(10,0,ДопустимыйЗнак.Любой)));
	тДанных.Колонки.Добавить("_BranchKey",Новый ОписаниеТипов("Число",Новый КвалификаторыЧисла(10,0,ДопустимыйЗнак.Любой)));
	
	рСКД=Новый СхемаКомпоновкиДанных;
	//
	рИсточникДанных=рСКД.ИсточникиДанных.Добавить();
	рИсточникДанных.Имя="ОсновнойИсточник";
	рИсточникДанных.ТипИсточникаДанных="Local"; // вариант External для кубов и других БД не делаем
	//
	рНабор=рСКД.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
	рНабор.Имя="ОсновнойНабор";
	рНабор.ИмяОбъекта="ТаблицаИсточник";
	рНабор.ИсточникДанных=рИсточникДанных.Имя;
	//
	рНастройка=рСКД.НастройкиПоУмолчанию;
	#КонецОбласти
	
	Для каждого колДерева из рДерево.Колонки Цикл
		рекДерева=соотРеквизитовДерева.Получить(колДерева.Имя);
		рЗаголовок=рекДерева.Заголовок;
		//
		Если тДанных.Колонки.Найти(колДерева.Имя)=Неопределено Тогда
			рТипЗначения=колДерева.ТипЗначения; // так более строго и правильно, особенно для конкретизируемых вроде субконто
			тДанных.Колонки.Добавить(колДерева.Имя,рТипЗначения,рЗаголовок);
		КонецЕсли;
	КонецЦикла;
	
	// Заполнение колонок таблицы и настроек СКД по элементам формы и дереву
	РекурсивноЗаполнитьКолонкиПоЭлементам(элфВсёДерево,рПрефиксПути,рИмяРеквизитаДерева,рДерево.Колонки,тДанных.Колонки,соотРеквизитовДерева,рНастройка.Выбор.Элементы);
	
	// Заполнение строк таблицы по дереву
	РекурсивноЗаполнитьТаблицуПоДереву(рДерево,тДанных);
	
	// Заполнение полей набора СКД
	Для каждого колТаблицы Из тДанных.Колонки Цикл
		рПолеНаб=рНабор.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
		рПолеНаб.Заголовок=колТаблицы.Заголовок;
		рПолеНаб.Поле=колТаблицы.Имя;
		рПолеНаб.ПутьКДанным=колТаблицы.Имя;
		// фильтруем "пустые" типы
		рОписТипов=колТаблицы.ТипЗначения;
		мТипов=Новый Массив;
		Для каждого рТип Из рОписТипов.Типы() Цикл
			Если рТип=Тип("Неопределено") или рТип=Тип("NULL") или рТип=Неопределено или рТип=Null Тогда Продолжить КонецЕсли;
			мТипов.Добавить(рТип);
		КонецЦикла;		
		рПолеНаб.ТипЗначения=Новый ОписаниеТипов(мТипов,рОписТипов.КвалификаторыЧисла,рОписТипов.КвалификаторыСтроки,рОписТипов.КвалификаторыДаты);
	КонецЦикла;
	
	// Организация иерархического показа
	рСвязь=рСКД.СвязиНаборовДанных.Добавить();
	рСвязь.НаборДанныхИсточник=рНабор.Имя;
	рСвязь.НаборДанныхПриемник=рНабор.Имя;
	рСвязь.ВыражениеИсточник="_BranchKey";
	рСвязь.ВыражениеПриемник="_ParentKey";
	рСвязь.НачальноеВыражение="0"; // это должно быть вычисляемое значение, результат вычисления - число
	рСвязь.УсловиеСвязи="";
	
	// Автовыбранные поля, структура простейшая
	рГруппировкаКД=рСКД.НастройкиПоУмолчанию.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	рГруппировкаКД.Использование=Истина;	
	рВыбПолеГр=рГруппировкаКД.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
	рВыбПолеГр.Использование=Истина;
	
	#Область НастройкаУсловногоОформления
	скдУО=рСКД.НастройкиПоУмолчанию.УсловноеОформление;
	Для каждого элУО ИЗ ЭтотОбъект.УсловноеОформление.Элементы Цикл
		мПутейПолей=Новый Массив;
		Для каждого элПоле Из элУО.Поля.Элементы Цикл
			рПутьИмя=Строка(элПоле.Поле);
			Если не элПоле.Использование или не СтрНачинаетсяС(рПутьИмя,рИмяРеквизитаДерева) Тогда Продолжить КонецЕсли;
			рПутьИмя=СтрЗаменить(рПутьИмя,рИмяРеквизитаДерева,"");
			Если ПустаяСтрока(рПутьИмя) Тогда // вносим все поля
				Для каждого рВыбПоле Из рСКД.НастройкиПоУмолчанию.Выбор.Элементы Цикл
					мПутейПолей.Добавить(Строка(рВыбПоле.Поле));
				КонецЦикла;
			Иначе
				Если мПутейПолей.Найти(рПутьИмя)=Неопределено Тогда
					мПутейПолей.Добавить(рПутьИмя);
				КонецЕсли;
			КонецЕсли;			
		КонецЦикла;
		Если мПутейПолей.Количество()=0 Тогда Продолжить КонецЕсли;
		//
		элСкдУО=скдУО.Элементы.Добавить();
		//
		// поля (если некое поле касается всего дерева, то вносим для всех полей)
		Для каждого рПутьИмя Из мПутейПолей Цикл
			элСкдПоле=элСкдУО.Поля.Элементы.Добавить();
			элСкдПоле.Поле=Новый ПолеКомпоновкиДанных(рПутьИмя);
			элСкдПоле.Использование=Истина;
		КонецЦикла;
		//
		// само УО
		Для каждого рОформление Из элУО.Оформление.Элементы Цикл
			Если не рОформление.Использование Тогда Продолжить КонецЕсли;
			элСкдУО.Оформление.УстановитьЗначениеПараметра(рОформление.Параметр,рОформление.Значение);
		КонецЦикла;
		//		
		// отборы
		Для каждого элОтбор Из элУО.Отбор.Элементы Цикл
			Если не элОтбор.Использование Тогда Продолжить КонецЕсли;
			рПутьКДанным=СтрЗаменить(Строка(элОтбор.ЛевоеЗначение),рПрефиксПути+рИмяРеквизитаДерева+".","");
			//
			Если ТипЗнч(элОтбор.ПравоеЗначение)=Тип("ПолеКомпоновкиДанных") Тогда
				рПравоеЗначениеСкдОтбораУО=Неопределено;
				Попытка рПравоеЗначениеСкдОтбораУО=Вычислить(элОтбор.ПравоеЗначение) Исключение КонецПопытки; // на момент вывода дерева
				Если рПравоеЗначениеСкдОтбораУО=Неопределено Тогда Продолжить КонецЕсли;
			Иначе
				рПравоеЗначениеСкдОтбораУО=элОтбор.ПравоеЗначение;
			КонецЕсли;
			//
			элСкдОтбор=элСкдУО.Отбор.Элементы.Добавить(ТипЗнч(элОтбор));
			элСкдОтбор.ЛевоеЗначение=Новый ПолеКомпоновкиДанных(рПутьКДанным);
			элСкдОтбор.ВидСравнения=элОтбор.ВидСравнения;
			элСкдОтбор.ПравоеЗначение=рПравоеЗначениеСкдОтбораУО;
			элСкдОтбор.Использование=Истина;
		КонецЦикла;
		//
		ЗаполнитьЗначенияСвойств(элСкдУО,элУО); // общие настройки (в первую очередь области использования)
	КонецЦикла;
	
	рСКД.НастройкиПоУмолчанию.ПараметрыВывода.УстановитьЗначениеПараметра("РасположениеРеквизитов",РасположениеРеквизитовКомпоновкиДанных.Отдельно);
	#КонецОбласти
	
	#Область ВыводВТабДокумент
	компМакета=Новый КомпоновщикМакетаКомпоновкиДанных;
	рМакетКомпоновки=компМакета.Выполнить(рСКД,рСКД.НастройкиПоУмолчанию,,,Тип("ГенераторМакетаКомпоновкиДанных"));
	//
	рПроцессорКД=Новый ПроцессорКомпоновкиДанных;
	рПроцессорКД.Инициализировать(рМакетКомпоновки,Новый Структура(рНабор.ИмяОбъекта,тДанных)); // разумеется без внешних функций
	//
	рПроцессорВывода=Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
	рПроцессорВывода.УстановитьДокумент(т);
	т=рПроцессорВывода.Вывести(рПроцессорКД,Истина);
	#КонецОбласти	
	
	Возврат т;
	
Исключение
	Сообщить("ВывестиДеревоВТабличныйДокумент, общая ошибка: "+ОписаниеОшибки());
	Возврат Новый ТабличныйДокумент;
КонецПопытки;
КонецФункции

 

 

Соответственно, вызов:

табДок=ВывестиДеревоВТабличныйДокумент("МоеДерево","Объект"); // если оно Объект.МоеДерево
табДок=ВывестиДеревоВТабличныйДокумент("МоеДерево"); // если оно реквизит формы

Кому пригодится - хорошо. Тестировалось на 8.3.16.1224, но должно работать и на более ранних. А сама проблема, породившая задачу, наблюдалась на 8.3.17-м релизе. Может, баг, и потом поправят...
Кто наткнётся на косяки - пишите, постараюсь оперативно исправить.

Вступайте в нашу телеграмм-группу Инфостарт

Вывод дерева дерево значений СКД программная работа

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3397    Abysswalker    8    

45

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

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

14.05.2025    6320    DeerCven    15    

57

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

21.05.2024    48646    dimanich70    83    

169

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7288    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    60822    atdonya    31    

69

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

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

30.11.2023    9078    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. dkoder 6 09.07.20 10:31 Сейчас в теме
Отличная работа! Только на больших деревьях лучше делать псевдо рекурсию внутри процедуры, особенно, когда рекурсивно вызываются запросы. Производительность возрастает до порядка

Псевдо рекурсия делается, примерно, так
Процедура ВнутренняяРекурсия(Дерево)
                МассивСтрок = Новый Массив;
                Для Каждого СтрДерева Из Дерево.Строки Цикл
                               МассивСтрок.Добавить(СтрДерева);
                КонецЦикла;

                Пока МассивСтрок.Количество() > 0 Цикл
                               // тут можно вызвать общий запрос для всего уровня дерева... 

                               МассивСтрокНовый = Новый Массив;
                               Для Каждого СтрДерева Из МассивСтрок Цикл
                                               // обработка данных элемента дерева ....

                                               // Тут делаем псевдо рекурсию
                                               Для Каждого СтрДереваНовый Из СтрДерева.Строки Цикл
                                                               МассивСтрокНовый.Добавить(СтрДереваНовый);
                                               КонецЦикла;
                               КонецЦикла;

                               МассивСтрок = Новый Массив;
                               Для Каждого СтрДерева Из МассивСтрокНовый Цикл
                                               МассивСтрок.Добавить(СтрДерева);
                               КонецЦикла;
                КонецЦикла;
КонецПроцедуры 
Показать
3. Yashazz 4891 09.07.20 12:58 Сейчас в теме
(1) да знаю) можно ещё всяко циклом поизвращаться.
2. Yashazz 4891 09.07.20 12:57 Сейчас в теме
Ахтунг! Замечена недоработка: если в дереве как в элементе формы есть группы колонок, то лажа, т.к. внутрь этих групп не заходит и колонки не добавляет. Допилю - обновлю.
8. Seaflame 04.02.22 10:50 Сейчас в теме
(2)
Ахтунг! Замечена недоработка: если в дереве как в элементе формы есть группы колонок, то лажа, т.к. внутрь этих групп не заходит и колонки не добавляет. Допилю - обновлю

Сейчас в приложенном коде эта проблема не отработана?
4. Yashazz 4891 14.07.20 09:09 Сейчас в теме
Сделал всё-таки без рекурсии. Заодно допилил вывод групп колонок, вывод реквизитов, ещё кое-что по мелочи.
5. mihanov 11 23.10.20 10:44 Сейчас в теме
Обнаружилась проблемка:
Не учтен случай когда в отборах "условного оформления" формы есть группы условий (И/ИЛИ...).
6. Yashazz 4891 23.10.20 11:39 Сейчас в теме
(5) А у меня это вроде учтено в более поздних доработках. Возможно, себе допилил, а сюда не выложил... Посмотрю.
sivur777; +1 Ответить
7. mity1982 24.11.20 02:34 Сейчас в теме
Обход дерева без рекурсии:
Надо понимать что, если алгоритм предполагает формирование результата "от листьев к корню" то нужна рекурсия.
Если это последовательный вывод то можно так. Можно использовать для всяких интерактивно-асинхронных задач.


Процедура Обход()
.....
Текущий = Дерево;
Пока ДеревоСледующий(Текущий) Цикл
       .......  
КонецЦикла;
......
КонецПроцедуры;

Функция ДеревоСледующий(Текущий)
	
	Перем УзелНиже;
	
 	УзелНиже = ДеревоНиже(Текущий);	
	Если НЕ УзелНиже = Неопределено Тогда
		Текущий = УзелНиже;
		Возврат Истина;
	ИначеЕсли УзелНиже = Неопределено И ДеревоВыше(Текущий) = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;	 

	Если НЕ ДеревоПравее(Текущий) = Неопределено Тогда
		Текущий = ДеревоПравее(Текущий);
		Возврат Истина;		
	КонецЕсли;
			
	//нет ни ниже ни правее
	Пока Истина Цикл
				
		Если ДеревоВыше(Текущий) = Неопределено Тогда Возврат Ложь; КонецЕсли;
		
		Текущий = ДеревоВыше(Текущий);
						
		Если НЕ ДеревоПравее(Текущий) = Неопределено И НЕ ДеревоВыше(Текущий) = Неопределено Тогда Прервать;КонецЕсли;
		
	КонецЦикла;
	
	Текущий = ДеревоПравее(Текущий);
	
	Возврат Истина;
	
КонецФункции

Функция ДеревоВыше(Знач Текущий)
	Если ТипЗнч(Текущий) = Тип("ДеревоЗначений") Тогда
		Возврат Неопределено;
	КонецЕсли;
	Если Текущий.Родитель = Неопределено Тогда
		Возврат Текущий.Владелец();
	КонецЕсли; 
	Возврат Текущий.Родитель; 
КонецФункции

Функция ДеревоНиже(Знач Текущий)
	Если Текущий.Строки.Количество() > 0 Тогда
		Возврат Текущий.Строки[0];
	КонецЕсли; 	
КонецФункции

Функция ДеревоПравее(Знач Текущий)
	Если ТипЗнч(Текущий) = Тип("ДеревоЗначений") Тогда
		Возврат Неопределено;
	КонецЕсли;
	Выше = Текущий.Родитель;
	Если Выше = Неопределено Тогда Выше = Текущий.Владелец(); КонецЕсли; 
	инд = Выше.Строки.Индекс(Текущий);
	Если инд < Выше.Строки.Количество() - 1 Тогда
		Возврат Выше.Строки[инд+1];
	КонецЕсли; 
КонецФункции
Показать
9. Yashazz 4891 04.02.22 12:18 Сейчас в теме
(7) Можно, наверное, правда, не очень понятно, чем рекурсия помешала. Сейчас не те стеки и не та вложенность обычно, чтоб рекурсия была плоха.
10. mity1982 04.02.22 13:44 Сейчас в теме
(9)асинхронный обход дерева
11. Yashazz 4891 08.02.22 12:14 Сейчас в теме
(10) Если вы это считаете асинхронностью, то - флаг в руки.
12. jmi 2 19.07.22 12:40 Сейчас в теме
сэкономил куча времени. Чел спасибо )
Гель; +1 Ответить
13. Ranetka 23 24.10.23 21:10 Сейчас в теме
Тэкс... Сейчас затестим.
14. Ranetka 23 24.10.23 22:03 Сейчас в теме
Просто спасибо!! Хоть группы в УО и не работают, но и без групп это абсолютный шедевр!
Для отправки сообщения требуется регистрация/авторизация