Сохранение дерева значений 1с

Преобразование дерева значений в таблицу значений и обратно

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

Дерево значений в таблицу значений

Дано: дерево значений (2 колонки) и таблица значений (4 колонки). Две дополнительные колонки в таблице значений нужны для обратного преобразования, если оно не требуется то и колонки не нужны.

ПреобразоватьВТЗРекурсия(тДерево, тТаблица, Новый УникальныйИдентификатор(«00000000-0000-0000-0000-000000000000»));

В колонку «ГУИД» таблицы значений записывается уникальный идентификатор строки (он просто генерируется), а в колонку «Родитель» записывается уникальный идентификатор строки-родителя (для строк верхнего уровня — нулевой уникальный идентификатор).

Таблица значений в дерево значений

Обратное преобразование производится при помощи двух лишних колонок («ГУИД» и «Родитель») если из таблицы значений была удалена какая-то строка, то все подчиненные ей строки также будут удалены.

ПреобразоватьВДЗРекурсия(тДерево, тТаблица, Новый УникальныйИдентификатор(«00000000-0000-0000-0000-000000000000»));

Работу приведенного выше кода можно посмотреть в маленькой обработке.

На этом все, надеюсь статья Вам помогла.

Если Вы нашли ошибку или неточность, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

УжасноПлохоНеплохоХорошо Отлично(оценок: 18, средняя оценка: 4,44 из 5)

Источник

Дерево значений в таблицу значений или в табличную часть и обратно

Распечатать

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

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

Для реализации такого механизма в ТЧ был добавлен реквизит “КлючСвязи” (обязательный реквизит) с типом число, куда помещался “НомерСтроки” (стандартный реквизит ТЧ) родителя (верхний уровень). А самый верхний элемент дерева имеет “КлючСвязи” равный 0.

Читайте также:  Дерево без листьев простым карандашом

На картинке ниже видно структуру дерева и структуру ТЧ

Нагрузка на сервер осуществляется при окрытии и при сохранении документа. А с деревом работают уже на клиенте.

На рабочей базе обрабатывается около 200 строк в одном документе.

Открытие и сохранение документа происходит моментально. С большем количеством строк не тестировалось.

Чтобы алгорит правильно работал, у рекизита фомы с типом дерево значений должны быть все реквизиты табличной части, кроме “КлючСвязи” и “НомерСтроки”. Иначе платформа выдаст ошибку. В дерево можо добавлять свои реквизиты отличные от ТЧ, они будут использоваться только в дереве.

Ниже приведен код преобразования дерева в таблицу и обратно.

 &НаКлиенте 
Процедура КомандаТаблицуВДерево(Команда)
КомандаТаблицуВДеревоНаСервере();
КонецПроцедуры


&НаСервере
Процедура КомандаТаблицуВДеревоНаСервере()
Дерево = ТаблицаВДерево(РеквизитФормыВЗначение("Объект"), "Товары");//Товары - имя табличной части
ЗначениеВРеквизитФормы(Дерево, "ДеревоЗначений");//ДеревоЗначений - реквизит формы с типом дерево значений
Элементы.ДеревоЗначений.Обновить();
КонецПроцедуры


&НаКлиенте
Процедура КомандаДеревоВТаблицу(Команда)
КомандаДеревоВТаблицуНаСервере();
КонецПроцедуры


&НаСервере
Процедура КомандаДеревоВТаблицуНаСервере()
Объект.Товары.Очистить();
ДОбъект = РеквизитФормыВЗначение("Объект");
ДеревоВТаблицу(ДОбъект, РеквизитФормыВЗначение("ДеревоЗначений"), "Товары");
ЗначениеВРеквизитФормы(ДОбъект, "Объект");
КонецПроцедуры

Формирование дерева из таблицы значений

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

Если КлючСвязи = NULL И ЭлементРодитель = NULL Тогда
//ПЕРВЫЙ ВЫЗОВ ПРОЦЕДУРЫ (КОРНЕВЫЕ ЭЛЕМЕНТЫ)
ИсточникВыборки = ДеревоЗначений2.Строки;
КлючСвязи = 0; // ЭЛЕМЕНТ ВЕРХНЕГО УРОВНЯ ИМЕЕТ НОМЕР СТРОКИ РОДИТЕЛЯ 0 (ОБЯЗАТЕЛЬНЫЙ РЕКВИЗИТ)
Иначе
//ВНУТРЕННИЙ ВЫЗОВ ПРОЦЕДУРЫ (ПОДЧИНЕННЫЕ ЭЛЕМЕНТЫ)
ИсточникВыборки = ЭлементРодитель.Строки;
КонецЕсли;
Фильтр = Новый Структура("КлючСвязи", КлючСвязи);
М = ДокОбъект[НаименованиеТабличнойЧастиДокумента].НайтиСтроки(Фильтр);
Если М.Количество() = 0 Тогда
Возврат ДеревоЗначений2;
КонецЕсли;
Для каждого Стр из М Цикл
Элемент = ИсточникВыборки.Добавить();
Для каждого Кол из КолонкиТаблицы Цикл
Если Кол.Имя = "НомерСтроки" ИЛИ Кол.Имя = "КлючСвязи" Тогда
Продолжить;
Иначе
Элемент[Кол.Имя] = Стр[Кол.Имя];
КонецЕсли;
КонецЦикла;
ТаблицаВДерево(ДокОбъект, НаименованиеТабличнойЧастиДокумента, Стр.НомерСтроки, Элемент); //ДОБАВЛЕНИЕ ПОДЧИНЁННЫХ ЭЛЕМЕНТОВ В ДЕРЕВО
КонецЦикла;
Возврат ДеревоЗначений2;
КонецФункции

Формирование таблицы из дерева

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

Источник

Читайте также:  Рисовать деревья разными способами

Дерево значений в управляемой форме. Заполнение и хранение

Хранить в документе (или справочнике, не суть важно) дерево значений. Дерево должно сохраняться в объекте, как и любой другой реквизит или табличная часть.

У нас дерево будет вот такое:

Одним из способов решить поставленную задачу является «хранилище значений». Я же решил хранить дерево в табличной части. У этого пути есть преимущества:

  • Можно выводить на форму автоматические итоги;
  • Итоги по группировкам можно считать в запросе;
  • В дальнейшем в динамический список документов можно тащить данные из табличной части. Например, тот же итог по одной из колонок.

Заполнение дерева из табличной части

В документ добавлена табличная часть «ПланФакт» с колонками, соответствующими колонкам дерева.

Так как для построения дерева мы будем использовать запрос, то нам понадобится получать типизированную таблицу значений с данными из табличной части. Для этого используем функцию ПолучитьТипизированнуюТаблицу().

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

Как видно, эта функция создает таблицу значений нужной структуры и заполняет ее данными табличной части документа. Теперь давайте посмотрим, что с этим добром делать.

В форме при открытии вызывается процедура ИзвлечьДерево().

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

Да, наш запрос принял данные из табличной части и сформировал результат с итогами по группировкам, который мы и загрузили в дерево. Да, само собой, «ДеревоПланФакт» — это реквизит управляемой формы с типом «ДеревоЗначений».

Читайте также:  Объекты диаграммы дерева узлов

Как это выглядит на форме:

Сохранение дерева

Понятно, что мало выводить дерево, надо еще и записывать его — после изменения пользователем. В нашем конкретном случае нужно еще и пересчитывать итоги по группировкам.

Можно было бы написать процедуру, которая выполнит обратное преобразование дерева в таблицу. Но в нашем случае это было бы долго: для пересчета итогов каждый раз пришлось бы перезаполнять всю таблицу. Я решил пойти другим путем и при изменении строки дерева пересчитывать только нужные итоги и обновлять только нужные строки в табличной части.

При этом я учел 2 особенности моей задачи:

  • Пользователь может менять только колонку «План»;
  • Поддерживается только изменение строк нижнего уровня. Изменение итогов по группировкам следует запретить.

Вот как выглядит результат:

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

Источник

Оцените статью