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

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-м релизе. Может, баг, и потом поправят...
Кто наткнётся на косяки - пишите, постараюсь оперативно исправить.

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

См. также

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

21.05.2024    17342    dimanich70    81    

135

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

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

1 стартмани

18.03.2024    3847    3    John_d    11    

57

Универсальные функции Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

12.02.2024    14442    atdonya    23    

56

Универсальные функции Программист Платформа 1С v8.3 Бесплатно (free)

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

30.11.2023    5248    ke.92@mail.ru    16    

65

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    13679    YA_418728146    7    

165

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    3331    52    progmaster    8    

4

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    18115    160    sapervodichka    112    

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

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

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

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

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

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

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


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

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

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

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

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

Функция ДеревоПравее(Знач Текущий)
	Если ТипЗнч(Текущий) = Тип("ДеревоЗначений") Тогда
		Возврат Неопределено;
	КонецЕсли;
	Выше = Текущий.Родитель;
	Если Выше = Неопределено Тогда Выше = Текущий.Владелец(); КонецЕсли; 
	инд = Выше.Строки.Индекс(Текущий);
	Если инд < Выше.Строки.Количество() - 1 Тогда
		Возврат Выше.Строки[инд+1];
	КонецЕсли; 
КонецФункции
Показать
9. Yashazz 4780 04.02.22 12:18 Сейчас в теме
(7) Можно, наверное, правда, не очень понятно, чем рекурсия помешала. Сейчас не те стеки и не та вложенность обычно, чтоб рекурсия была плоха.
10. mity1982 04.02.22 13:44 Сейчас в теме
(9)асинхронный обход дерева
11. Yashazz 4780 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 Сейчас в теме
Просто спасибо!! Хоть группы в УО и не работают, но и без групп это абсолютный шедевр!
Оставьте свое сообщение