Программно добавить реквизит в дерево значений на форме

Перейти к содержимому

Для примера возьмем существующее на форме дерево с именем реквизита «Дерево», пусть в него надо добавить еще одну колонку с названием «Узел» тип строка. С динамическим добавлением элементов на управляемую форму можно разобраться на примере следующего алгоритма действий, все происходит на сервере:

  1. Получить значение реквизита — ДеревоЗначений
  2. Добавить колонку в дерево значений
  3. Добавить реквизит (колонку) дерева на форму (невидимая часть, справа в редакторе форм)
  4. Выгрузить дерево значений в реквизит формы
  5. Добавить на форму элемент колонку дерева (видимая часть, слева в редакторе форм)

Пример кода:

// 1. получаем дерево как програмный объект
ДеревоОбъект = РеквизитФормыВЗначение(«Дерево»);
// 2. добавляем колонку
ДеревоОбъект.Колонки.Добавить(«Узел», Новый ОписаниеТипов(«Строка»));
// 3. создаем РЕКВИЗИТ КОЛОНКИ (невидимая часть, справа в редакторе форм)
МассивДобавляемыхРеквизитов = Новый Массив;
// реквизит принадлежит дереву
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(«Узел», Новый ОписаниеТипов(«Строка»), «Дерево»));
// добавляем реквизит в форму (невидимая часть, справа в редакторе форм)
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);

// 4. заполняем дерево (реквизит формы) занчением
ЗначениеВРеквизитФормы(ДеревоОбъект, «Дерево»);
// 5. добавляем НА ФОРМУ (видимая часть, слева в редакторе форм)
// назовем ДеревоУзел, принадлежит элементу дерева, связан с добавленной колонкой «Узел»
НовыйЭлемент = Элементы.Добавить(«ДеревоУзел», Тип(«ПолеФормы»), Элементы.Дерево);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = «Дерево.Узел»;

   s-n-a-y

11 — 28.10.15 — 08:08

(9), (10) я изменяю код стандартной конфигурации. Вот полный код, мои добавления обрамлены в //н+ комз и //н- комз:

    &НаСервере

    Процедура ОбновитьДеревоЗадачМнеСервер()

        
        ДеревоЗадач = РеквизитФормыВЗначение(«ЗадачиМне»);

        ДеревоЗадач.Строки.Очистить();

        
        СформироватьДеревоЗадачМне(ДеревоЗадач);

        
        СформироватьГруппировкиЗадачМне(ДеревоЗадач);

        
        Если ПустаяСтрока(ЗадачиМнеСортироватьПо) Тогда

            ЗадачиМнеСортироватьПо = «СрокИсполнения»;

            ЗадачиМнеНаправлениеСортировки = «Возр»;

        КонецЕсли;

        
        ДеревоЗадач.Строки.Сортировать(

            ЗадачиМнеСортироватьПо + » » + ЗадачиМнеНаправлениеСортировки,

            Ложь);

        
        ЗначениеВДанныеФормы(ДеревоЗадач, ЗадачиМнеПолный);

        
    КонецПроцедуры

    &НаСервере

    Процедура СформироватьГруппировкиЗадачМне(ДеревоЗадач = Неопределено)

        
        Если ЗадачиМнеВидГруппировки = «Автор» Тогда

            СформироватьСписокАвторов(ДеревоЗадач);

        ИначеЕсли ЗадачиМнеВидГруппировки = «Проект» Тогда

            СформироватьСписокПроектов(ДеревоЗадач);

        ИначеЕсли ЗадачиМнеВидГруппировки = «ТочкаМаршрута» Тогда

            СформироватьСписокТочекМаршрута(ДеревоЗадач);

        ИначеЕсли ЗадачиМнеВидГруппировки = «Флаг» Тогда

            СформироватьСписокФлагов(ДеревоЗадач);

        КонецЕсли;

        
    КонецПроцедуры

    // Формирует дерево задач в списке задачи мне

    //

    &НаСервере

    Процедура СформироватьДеревоЗадачМне(Дерево)

        
        Запрос = Новый Запрос;

        Запрос.Текст =

            «ВЫБРАТЬ РАЗРЕШЕННЫЕ

            |    ЗадачиИсполнителя.Ссылка КАК Ссылка,

            |    ВЫБОР

            |        КОГДА НЕ ЕСТЬNULL(ДанныеБизнесПроцессов.Завершен, ИСТИНА)

            |            ТОГДА 1

            |        ИНАЧЕ 0

            |    КОНЕЦ КАК АктивныйПроцесс

            |ПОМЕСТИТЬ ЗадачиСАктивнымиПроцессами

            |ИЗ

            |    Задача.ЗадачаИсполнителя КАК ЗадачиИсполнителя

            |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДанныеБизнесПроцессов КАК ДанныеБизнесПроцессов

            |        ПО (ДанныеБизнесПроцессов.ВедущаяЗадача = ЗадачиИсполнителя.Ссылка)

            |ГДЕ

            |    %УсловиеОтбора%

            |

            |ОБЪЕДИНИТЬ ВСЕ

            |

            |ВЫБРАТЬ

            |    ЗадачиИсполнителя.Ссылка,

            |    ВЫБОР

            |        КОГДА НЕ ЕСТЬNULL(ДанныеБизнесПроцессов.Завершен, ИСТИНА)

            |            ТОГДА 1

            |        ИНАЧЕ 0

            |    КОНЕЦ

            |ИЗ

            |    Задача.ЗадачаИсполнителя КАК ЗадачиИсполнителя

            |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДанныеБизнесПроцессов КАК ДанныеБизнесПроцессов

            |        ПО (ДанныеБизнесПроцессов.ГлавнаяЗадача = ЗадачиИсполнителя.Ссылка)

            |ГДЕ

            |    %УсловиеОтбора%

            |;

            |

            |////////////////////////////////////////////////////////////////////////////////

            |ВЫБРАТЬ

            |    Задачи.Ссылка КАК Ссылка,

            |    СУММА(Задачи.АктивныйПроцесс) КАК КоличествоАктивныхПроцессов

            |ПОМЕСТИТЬ ЗадачиСКоличествомАктивныхПроцессов

            |ИЗ

            |    ЗадачиСАктивнымиПроцессами КАК Задачи

            |

            |СГРУППИРОВАТЬ ПО

            |    Задачи.Ссылка

            |

            |ИНДЕКСИРОВАТЬ ПО

            |    Ссылка

            |;

            |

            |////////////////////////////////////////////////////////////////////////////////

            |ВЫБРАТЬ РАЗРЕШЕННЫЕ

            |    ЗадачиИсполнителя.Ссылка,

            |    ЗадачиИсполнителя.Дата КАК Дата,

            |    ЗадачиИсполнителя.Наименование,

            |    ЗадачиИсполнителя.СрокИсполнения,

            |    ЗадачиИсполнителя.ПринятаКИсполнению,

            |    ЗадачиИсполнителя.Выполнена,

            |    ВЫБОР

            |        КОГДА ЗадачиИсполнителя.СрокИсполнения <> ДАТАВРЕМЯ(1, 1, 1)

            |                И ЗадачиИсполнителя.СрокИсполнения < &ТекущаяДата

            |            ТОГДА ИСТИНА

            |        ИНАЧЕ ЛОЖЬ

            |    КОНЕЦ КАК Просрочена,

            |    ЗадачиИсполнителя.Автор,

            |    ВЫБОР

            |        КОГДА ЗадачиИсполнителя.Важность = ЗНАЧЕНИЕ(Перечисление.ВариантыВажностиЗадачи.Низкая)

            |            ТОГДА 0

            |        КОГДА ЗадачиИсполнителя.Важность = ЗНАЧЕНИЕ(Перечисление.ВариантыВажностиЗадачи.Высокая)

            |            ТОГДА 2

            |        ИНАЧЕ 1

            |    КОНЕЦ КАК Важность,

            //SS++

            |    ЗадачиИсполнителя.Корреспондент КАК Корреспондент,

            |    ВЫБОР

            |    КОГДА ЕСТЬNULL(ВложенныйЗапрос.Количество, 0) = 0

            |        ТОГДА -1

            |    ИНАЧЕ ВЫБОР

            |            КОГДА ВложенныйЗапрос.НеВыполнена > 0

            |                ТОГДА 0

            |            ИНАЧЕ 2

            |        КОНЕЦ

            |    КОНЕЦ КАК КартинкаГлавнаяЗадача,          

            //SS—

            |    ВЫБОР

            |        КОГДА ЗадачиИсполнителя.Исполнитель = &ТекущийПользователь

            |            ТОГДА -1

            |        КОГДА ЗадачиИсполнителя.Исполнитель В (&Исполнители)

            |            ТОГДА 3

            |        ИНАЧЕ 4

            |    КОНЕЦ КАК Картинка,

            |    ЗадачиИсполнителя.БизнесПроцесс,

            |    ЗадачиИсполнителя.ТочкаМаршрута,

            |    ЗадачиИсполнителя.ПредметСтрокой,

            |    ЗадачиИсполнителя.Проект,

            |    ВЫБОР

            |        КОГДА ЗадачиИсполнителя.Проект = ЗНАЧЕНИЕ(Справочник.Проекты.ПустаяСсылка)

            |            ТОГДА &ИмяГруппыБезПроекта

            |        ИНАЧЕ ЗадачиИсполнителя.Проект.Представление

            |    КОНЕЦ КАК ПредставлениеПроекта,

            |    ИСТИНА КАК Показывать,

            |    ВЫБОР

            |        КОГДА ЗадачиСКоличествомАктивныхПроцессов.КоличествоАктивныхПроцессов > 0

            |            ТОГДА ИСТИНА

            |        ИНАЧЕ ЛОЖЬ

            |    КОНЕЦ КАК ЕстьАктивныеПроцессы,

            |    ЕСТЬNULL(ФлагиОбъектов.Флаг, ЗНАЧЕНИЕ(Перечисление.ФлагиОбъектов.ПустаяСсылка)) КАК Флаг,

            |    ВЫБОР

            |        КОГДА ФлагиОбъектов.Флаг = ЗНАЧЕНИЕ(Перечисление.ФлагиОбъектов.Красный)

            |            ТОГДА 1

            |        КОГДА ФлагиОбъектов.Флаг = ЗНАЧЕНИЕ(Перечисление.ФлагиОбъектов.Синий)

            |            ТОГДА 2

            |        КОГДА ФлагиОбъектов.Флаг = ЗНАЧЕНИЕ(Перечисление.ФлагиОбъектов.Желтый)

            |            ТОГДА 3

            |        КОГДА ФлагиОбъектов.Флаг = ЗНАЧЕНИЕ(Перечисление.ФлагиОбъектов.Зеленый)

            |            ТОГДА 4

            |        КОГДА ФлагиОбъектов.Флаг = ЗНАЧЕНИЕ(Перечисление.ФлагиОбъектов.Оранжевый)

            |            ТОГДА 5

            |        КОГДА ФлагиОбъектов.Флаг = ЗНАЧЕНИЕ(Перечисление.ФлагиОбъектов.Лиловый)

            |            ТОГДА 6

            |        ИНАЧЕ 0

            |    КОНЕЦ КАК НомерФлага,

            |    ЗадачиИсполнителя.Исполнитель КАК Исполнитель,

            |    ВЫБОР

            |        КОГДА КешИнформацииОбОбъектах.СостояниеКонтроля = ЗНАЧЕНИЕ(Перечисление.СостоянияКонтроля.НаКонтроле)

            |            ТОГДА ВЫБОР

            |                    КОГДА КешИнформацииОбОбъектах.СрокИсполнения = ДАТАВРЕМЯ(1, 1, 1)

            |                            ИЛИ КешИнформацииОбОбъектах.СрокИсполнения >= &ТекущаяДата

            |                        ТОГДА 1

            |                    ИНАЧЕ 3

            |                КОНЕЦ

            |        КОГДА КешИнформацииОбОбъектах.СостояниеКонтроля = ЗНАЧЕНИЕ(Перечисление.СостоянияКонтроля.СнятсКонтроля)

            |            ТОГДА 4

            |        КОГДА КешИнформацииОбОбъектах.СостояниеКонтроля = ЗНАЧЕНИЕ(Перечисление.СостоянияКонтроля.НаКонтролеНесколько)

            |            ТОГДА ВЫБОР

            |                    КОГДА КешИнформацииОбОбъектах.СрокИсполнения = ДАТАВРЕМЯ(1, 1, 1)

            |                            ИЛИ КешИнформацииОбОбъектах.СрокИсполнения >= &ТекущаяДата

            |                        ТОГДА 5

            |                    КОГДА КешИнформацииОбОбъектах.СрокИсполненияОбщий = ДАТАВРЕМЯ(1, 1, 1)

            |                            ИЛИ КешИнформацииОбОбъектах.СрокИсполненияОбщий >= &ТекущаяДата

            |                        ТОГДА 8

            |                    ИНАЧЕ 6

            |                КОНЕЦ

            |        КОГДА КешИнформацииОбОбъектах.СостояниеКонтроля = ЗНАЧЕНИЕ(Перечисление.СостоянияКонтроля.СнятсКонтроляНесколько)

            |            ТОГДА 7

            |        КОГДА КешИнформацииОбОбъектах.СостояниеКонтроля = ЗНАЧЕНИЕ(Перечисление.СостоянияКонтроля.СмешанноНесколько)

            |            ТОГДА ВЫБОР

            |                    КОГДА КешИнформацииОбОбъектах.СрокИсполнения = ДАТАВРЕМЯ(1, 1, 1)

            |                            ИЛИ КешИнформацииОбОбъектах.СрокИсполнения >= &ТекущаяДата

            |                        ТОГДА 9

            |                    ИНАЧЕ 10

            |                КОНЕЦ

            |        ИНАЧЕ 0

            |    КОНЕЦ КАК СостояниеКонтроля

            |ПОМЕСТИТЬ Задачи

            |ИЗ

            |    ЗадачиСКоличествомАктивныхПроцессов КАК ЗадачиСКоличествомАктивныхПроцессов

            |        ЛЕВОЕ СОЕДИНЕНИЕ Задача.ЗадачаИсполнителя КАК ЗадачиИсполнителя

            |            ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ФлагиОбъектов КАК ФлагиОбъектов

            |            ПО ЗадачиИсполнителя.Ссылка = ФлагиОбъектов.Объект

            |                И (ФлагиОбъектов.Пользователь = &ТекущийПользователь)

            |        ПО ЗадачиСКоличествомАктивныхПроцессов.Ссылка = ЗадачиИсполнителя.Ссылка

            //SS++

            
                    |        ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ

            |ЗадачаИсполнителя.ГлавнаяЗадача КАК ГлавнаяЗадача,

            |СУММА(1) КАК Количество,

            |СУММА(ВЫБОР

            |        КОГДА ЗадачаИсполнителя.Выполнена

            |            ТОГДА 0

            |        ИНАЧЕ 1

            |    КОНЕЦ) КАК НеВыполнена

            |ИЗ

            |    Задача.ЗадачаИсполнителя КАК ЗадачаИсполнителя

            |ГДЕ

            |    (ЗадачаИсполнителя.ТочкаМаршрута В (&ТочкаМаршрутаПодЗадачи)

            //н+ комз    по просьбам пользователей

            |    или &МухаметовОС <> Неопределено И

            |    ЗадачаИсполнителя.Исполнитель = &МухаметовОС

            |    )

            //н- комз    

            |

            |СГРУППИРОВАТЬ ПО

            |    ЗадачаИсполнителя.ГлавнаяЗадача) КАК ВложенныйЗапрос

            |        ПО ЗадачиСКоличествомАктивныхПроцессов.Ссылка = ВложенныйЗапрос.ГлавнаяЗадача

            |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КешИнформацииОбОбъектах КАК КешИнформацииОбОбъектах

            |        ПО ЗадачиСКоличествомАктивныхПроцессов.Ссылка = КешИнформацииОбОбъектах.Объект

            |;

            |

            |////////////////////////////////////////////////////////////////////////////////

            |ВЫБРАТЬ

            |    Задачи.Ссылка,

            |    Задачи.Дата,

            |    Задачи.Наименование,

            |    Задачи.СрокИсполнения,

            |    Задачи.ПринятаКИсполнению,

            |    Задачи.Выполнена,

            |    Задачи.Просрочена,

            |    Задачи.Автор,

            |    Задачи.Важность,

            //  ss++

            |    Задачи.Корреспондент,

            |    Задачи.КартинкаГлавнаяЗадача,

            |    Задачи.Картинка,

            |    Задачи.БизнесПроцесс,

            |    Задачи.ТочкаМаршрута,

            |    Задачи.ПредметСтрокой,

            |    Задачи.Проект,

            |    Задачи.ПредставлениеПроекта,

            |    Задачи.Показывать,

            |    Задачи.ЕстьАктивныеПроцессы,

            |    Задачи.Флаг,

            |    Задачи.НомерФлага,

            |    Задачи.Исполнитель,

            |    Задачи.СостояниеКонтроля,

            |    «»234″» КАК ННомер

            |ИЗ

            |    Задачи КАК Задачи

            |

            |УПОРЯДОЧИТЬ ПО

            |    Задачи.Дата

            |;

            |

            |////////////////////////////////////////////////////////////////////////////////

            |ВЫБРАТЬ

            |    КОЛИЧЕСТВО(*) КАК ЗадачиМнеВсего,

            |    ЕСТЬNULL(СУММА(ВЫБОР

            |                КОГДА Задачи.ПринятаКИсполнению = ЛОЖЬ

            |                    ТОГДА 1

            |                ИНАЧЕ 0

            |            КОНЕЦ), 0) КАК ЗадачиМнеНовые,

            |    ЕСТЬNULL(СУММА(ВЫБОР

            |                КОГДА Задачи.Выполнена = ЛОЖЬ

            |                        И Задачи.СрокИсполнения <> ДАТАВРЕМЯ(1, 1, 1)

            |                        И Задачи.СрокИсполнения < &ТекущаяДата

            |                    ТОГДА 1

            |                ИНАЧЕ 0

            |            КОНЕЦ), 0) КАК ЗадачиМнеПросроченные

            |ИЗ

            |    Задачи КАК Задачи»;

        Запрос.УстановитьПараметр(«Исполнители»,

            БизнесПроцессыИЗадачиСервер.ИсполнителиЗадачПользователя(ТекущийПользователь));

        Запрос.УстановитьПараметр(«ТекущийПользователь», ТекущийПользователь);

        Запрос.УстановитьПараметр(«ТекущаяДата», НачалоДня(ТекущаяДатаСеанса()));

        Запрос.УстановитьПараметр(«ИмяГруппыБезПроекта», НСтр(«ru = ‘Без проекта'»));

        //н+ комз

        ФизЛицо = Справочники.ФизическиеЛица.НайтиПоНаименованию(«Мухаметов Олег Сергеевич»);    

        //ФизЛицо = Справочники.ФизическиеЛица.НайтиПоНаименованию(«Сулимов Нияз Ильдарович»);    

        МухаметовОС = Неопределено;

        Если ЗначениеЗаполнено(ФизЛицо) И

             ТипЗнч(ТекущийПользователь) = Тип(«СправочникСсылка.Пользователи») И

             ЗначениеЗаполнено(ТекущийПользователь) И

             ТекущийПользователь.ФизЛицо = ФизЛицо Тогда

            МухаметовОС = ТекущийПользователь;

        КонецЕсли;    

        Запрос.УстановитьПараметр(«МухаметовОС», МухаметовОС);

        //н- комз

        //SS++ 27/08/2014

        СписокТочкаМаршрута =Новый Списокзначений;

        СписокТочкаМаршрута.Добавить(Бизнеспроцессы.Исполнение.ТочкиМаршрута.Исполнить);

        СписокТочкаМаршрута.Добавить(Бизнеспроцессы.Поручение.ТочкиМаршрута.Выполнить);

        Запрос.УстановитьПараметр(«ТочкаМаршрутаПодЗадачи»,СписокТочкаМаршрута);

        //SS—

        УсловиеОтбора = УсловиеОтбораЗадачМне();

        Запрос.Текст = СтрЗаменить(Запрос.Текст, «%УсловиеОтбора%», УсловиеОтбора);

        
        Результат = Запрос.ВыполнитьПакет();

        
        ТаблицаЗадач = Результат[3].Выгрузить();

        
        ТаблицаВложенныхЗадач = Новый ТаблицаЗначений;

        ТаблицаВложенныхЗадач.Колонки.Добавить(«Задача»);

        ТаблицаВложенныхЗадач.Колонки.Добавить(«ВедущаяЗадача»);

        ТаблицаВложенныхЗадач.Колонки.Добавить(«ГлавнаяЗадача»);

        // Заполнение корневых задач

        Для каждого Строка Из ТаблицаЗадач Цикл

        
            Задача = Строка.Ссылка;

            
            ЭтоКорневаяЗадача = Ложь;

            
            Если ЗначениеЗаполнено(Строка.БизнесПроцесс) Тогда

                РеквизитыПроцесса = ОбщегоНазначения.ПолучитьЗначенияРеквизитов(

                    Строка.БизнесПроцесс,

                    «ВедущаяЗадача, ГлавнаяЗадача»);

                Если Не ЗначениеЗаполнено(РеквизитыПроцесса.ВедущаяЗадача)

                    И Не ЗначениеЗаполнено(РеквизитыПроцесса.ГлавнаяЗадача) Тогда

                    
                    СтрокаДерева = Дерево.Строки.Добавить();

                    ЗаполнитьЗначенияСвойств(СтрокаДерева, Строка);

                    //н+ комз

                    //темп = Строка.Ссылка.Наименование;

                    //нач = Найти(темп, «(№»);

                    //Если нач > 0 Тогда

                    //    нач = нач + 2;

                    //    темп = Прав(темп, СтрДлина(темп) — нач);

                    //    кон = Найти(темп, » от «);

                    //    Если кон > 0 Тогда

                    //        темп = Лев(темп, кон — 1);

                    //    иначе

                    //        темп = «»;

                    //    КонецЕсли;

                    //иначе

                    //    темп = «»;

                    //КонецЕсли;

                    //СтрокаДерева.ННомер = темп;

                    СтрокаДерева.ННомер = «123»;

                    //н- комз

                    ЭтоКорневаяЗадача = Истина;

                КонецЕсли;

            КонецЕсли;

            
            Если Не ЭтоКорневаяЗадача Тогда

                
                СтрокаВложеннойЗадачи = ТаблицаВложенныхЗадач.Добавить();

                СтрокаВложеннойЗадачи.Задача = Задача;

                СтрокаВложеннойЗадачи.ВедущаяЗадача = РеквизитыПроцесса.ВедущаяЗадача;

                СтрокаВложеннойЗадачи.ГлавнаяЗадача = РеквизитыПроцесса.ГлавнаяЗадача;

            КонецЕсли;

            
        КонецЦикла;

            
        // Заполнение вложенных задач

        Для каждого СтрокаВложеннойЗадачи Из ТаблицаВложенныхЗадач Цикл

            ГлавнаяЗадача = Неопределено;

            
            Если ЗначениеЗаполнено(СтрокаВложеннойЗадачи.ВедущаяЗадача) Тогда

                ГлавнаяЗадача = СтрокаВложеннойЗадачи.ВедущаяЗадача;

            ИначеЕсли ЗначениеЗаполнено(СтрокаВложеннойЗадачи.ГлавнаяЗадача) Тогда

                ГлавнаяЗадача = СтрокаВложеннойЗадачи.ГлавнаяЗадача;

            КонецЕсли;

            
            Ветвь = Дерево.Строки.Найти(ГлавнаяЗадача, «Ссылка», Истина);

            Если Ветвь <> Неопределено Тогда

                СтрокаДерева = Ветвь.Строки.Добавить();

                
                ЗаполнитьЗначенияСвойств(

                    СтрокаДерева,

                    ТаблицаЗадач.Найти(СтрокаВложеннойЗадачи.Задача, «Ссылка»));

            Иначе

                СтрокаДерева = Дерево.Строки.Добавить();

                ЗаполнитьЗначенияСвойств(

                    СтрокаДерева,

                    ТаблицаЗадач.Найти(СтрокаВложеннойЗадачи.Задача, «Ссылка»));

                    //н+ комз

                    //темп = Строка.Ссылка.Наименование;

                    //нач = Найти(темп, «(№»);

                    //Если нач > 0 Тогда

                    //    нач = нач + 2;

                    //    темп = Прав(темп, СтрДлина(темп) — нач);

                    //    кон = Найти(темп, » от «);

                    //    Если кон > 0 Тогда

                    //        темп = Лев(темп, кон — 1);

                    //    иначе

                    //        темп = «»;

                    //    КонецЕсли;

                    //иначе

                    //    темп = «»;

                    //КонецЕсли;

                    //СтрокаДерева.нНомерВНаименовании = темп;

                    СтрокаДерева.ННомер = «123»;

                    //н- комз

            КонецЕсли;

            
        КонецЦикла;

        
        // Заполнение статистики по задачам

        Выборка = Результат[4].Выбрать();

        Выборка.Следующий();

        
        ЗаполнитьЗначенияСвойств(ЭтаФорма, Выборка);

    ЗначениеВРеквизитФормы(Дерево, «ЗадачиМне»);    

    КонецПроцедуры

Дерево значений 1С – это универсальная коллекция значений, в которой данные можно расположить в иерархическом порядке в виде дерева. По сути, дерево значений в 1С 8.3 — это таблица значений, где у строк могут быть подчиненные строки. Одна строка дерева значений может иметь несколько подчиненных строк, но в тоже время она может иметь только одну строку, которой подчинена, строку-родителя, или не иметь родителя вообще. Строка без родителя – это строка верхнего уровня, или первая строка.

В программном модуле дерево значений создается при помощи конструктора Новый.

ДеревоРегионов = Новый ДеревоЗначений;

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

То есть, такой код, при выполнении на тонком клиенте, работать не будет:

&НаКлиенте
Процедура СоздатьДерево(Команда)
   ДеревоРегионов = Новый ДеревоЗначений;
КонецПроцедуры

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

&НаСервере
Процедура СоздатьДеревоНаСервере()
	ДеревоРегионов = Новый ДеревоЗначений;
КонецПроцедуры

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

Колонки дерева значений 1С

Само по себе дерево значений не имеет смысла, для того, чтобы можно было с ним работать у этого дерева нужно создать колонки. Для этого, нужно использовать свойство дерева значений Колонки,  которое имеет тип КоллекцияКолонокДереваЗначений, у объекта данного типа имеется ряд методов, с которыми вы можете познакомиться в синтакс-помощнике, а нас конкретно интересует метод Добавить. Этот метод добавляет новые колонки в дерево значений.  Он имеет следующий синтаксис:

Добавить(<Имя>, <Тип>, <Заголовок>, <Ширина>)

Где Имя – это имя колонки (уникальное в рамках этого дерева); Тип – в этом параметре описывают возможные типы колонки, делается при помощи объекта ОписаниеТипов; Заголовок – название колонки и Ширина  — ширина колонки в символах.

Например, добавим нашему дереву регионов три колонки:

ТипСтрока = Новый ОписаниеТипов("Строка");
ТипЧисло  = Новый ОписаниеТипов("Число");

ДеревоРегионов.Колонки.Добавить("Регион",ТипСтрока);
ДеревоРегионов.Колонки.Добавить("РуководительРегиона",ТипСтрока,"Руководитель региона");
ДеревоРегионов.Колонки.Добавить("Бюджет",ТипЧисло);

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

Строки дерева значений 1С

Строки дерева значений 1С бывают двух видов. Это строки верхнего (или первого) уровня. Те строки, у которых нет ни каких родителей, и которые ни кому не подчинены.  И подчиненные строки – это строки более низких уровней. У подчиненных строк заполнено свойство Родитель, в котором указывается родительская строка, т.е. та строка, которой подчинена эта текущая строка (у строк верхнего уровня свойство Родитель пустое). Для того, чтобы создать строку верхнего (или первого) уровня, нужно воспользоваться свойством Строки дерева значений. Это свойство имеет тип КоллекцияСтрокДереваЗначений. И у объекта этого типа есть метод Добавить, который создает новую строку верхнего (первого) уровня. Причем, этот метод является функцией, которая возвращает строку дерева значений. А у этой строки мы, как к свойствам, можем обращаться к колонкам дерева значений.

СтрокаВерхнего = ДеревоРегионов.Строки.Добавить();
СтрокаВерхнего.Регион = "Приволжский округ";
СтрокаВерхнего.РуководительРегиона = "Петров";
СтрокаВерхнего.Бюджет = 1000000;

СтрокаВерхнего = ДеревоРегионов.Строки.Добавить();
СтрокаВерхнего.Регион = "Сибирский округ";
СтрокаВерхнего.РуководительРегиона = "Борисов";
СтрокаВерхнего.Бюджет = 1000000;

В этом коде добавлено две строки верхнего уровня дерева регионов.

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

Например, так мы создаем две строки второго уровня:

СтрокаВторогоОдин = СтрокаВерхнего.Строки.Добавить();
СтрокаВторогоОдин.Регион = "Удмуртия";
СтрокаВторогоОдин.РуководительРегиона = "Бречалов";
СтрокаВторогоОдин.Бюджет = 500000;

СтрокаВторогоДва = СтрокаВерхнего.Строки.Добавить();
СтрокаВторогоДва.Регион = "Татарстан";
СтрокаВторогоДва.РуководительРегиона = "Шаймиев";
СтрокаВторогоДва.Бюджет = 500000;

А так, мы создаем строки третьего уровня (две для Удмуртии и одну для Татарстана):

СтрокаТретьегоОдин = СтрокаВторогоОдин.Строки.Добавить();

СтрокаТретьегоОдин.Регион = "Завьяловский район";
СтрокаТретьегоОдин.РуководительРегиона = "Иванов";
СтрокаТретьегоОдин.Бюджет = 100000;

СтрокаТретьегоДва = СтрокаВторогоОдин.Строки.Добавить();

СтрокаТретьегоДва.Регион = "Ярский район";
СтрокаТретьегоДва.РуководительРегиона = "Сидоров";
СтрокаТретьегоДва.Бюджет = 100000;


СтрокаТретьегоТри  = СтрокаВторогоДва.Строки.Добавить();

СтрокаТретьегоТри.Регион = "Мензилинский район";
СтрокаТретьегоТри.РуководительРегиона = "Шайхметов";
СтрокаТретьегоТри.Бюджет = 100000;

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

Для обхода дерева значений необходимо обращаться к свойству Строки или самого дерева, или строки дерева значений. Так мы обходим строки первого или верхнего уровня:

Для Каждого СтрПервогоУровня из ДеревоРегионов.Строки Цикл 
	Сообщить(СтрПервогоУровня.Регион + "/" + СтрПервогоУровня.Руководитель);
КонецЦикла;

Поскольку свойство Строки дерева значений является коллекцией, то к ней применим цикл Для Каждого Цикл. Где в значении очередного элемента коллекции находится строка верхнего уровня (первого) дерева значений, у этой строки можно обращаться к колонкам как к свойствам.

Если нам нужно обойти строку следующего уровня, то в этот раз следует уже циклом Для каждого Цикл обходить свойство Строки текущей строки дерева значений.

Для Каждого СтрПервогоУровня из ДеревоРегионов.Строки Цикл 
	Сообщить(СтрПервогоУровня.Регион + "/" + СтрПервогоУровня.Руководитель);
	Для Каждого стрВторогоУровня из СтрПервогоУровня.Строки Цикл 
		Сообщить(стрВторогоУровня.Регион + "/" + стрВторогоУровня.Руководитель);
	КонецЦикла;	
КонецЦикла;	

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

&НаСервере
Процедура ВывестиДерево(СтрокиДерева)
	
	Для Каждого стрДерева из СтрокиДерева Цикл 
		
		Сообщить(стрДерева.Регион + "/" + стрДерева.РуководительРегиона + "/");
		Если стрДерева.Строки.Количество() <> 0 Тогда 
			ВывестиДерево(стрДерева.Строки);
		КонецЕсли;	
	КонецЦикла;	
	
КонецПроцедуры

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

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

ВывестиДерево(ДеревоРегионов.Строки);
Дерево значений 1С

Ну и для того, чтобы наш список выглядел более наглядно, будем при выводе строк второго и третьего уровня добавлять тире.  Для этого создадим функцию, которая добавляет тире, количество которых зависит от уровня вложенности. А узнать уровень вложенности строки можно при помощи метода строки дерева значений Уровень(), который возвращает уровень вложенности строки начиная с 0.

&НаСервере
Процедура ВывестиДерево(СтрокиДерева)
	
	Для Каждого стрДерева из СтрокиДерева Цикл 
		Пробелы = СформироватьТире(стрДерева.Уровень());
		
		Сообщить(Пробелы + стрДерева.Регион + "/" + стрДерева.РуководительРегиона + "/");
		Если стрДерева.Строки.Количество() <> 0 Тогда 
			ВывестиДерево(стрДерева.Строки);
		КонецЕсли;	
	КонецЦикла;	
	
КонецПроцедуры	

Функция  СформироватьТире(Уровень)
	
	Если Уровень = 0 Тогда 
		Возврат "";
	КонецЕсли;	
	
	Пробелы = "";
	Для н = 1 по Уровень Цикл 
		Пробелы = Пробелы + " - ";
	КонецЦикла;	
	
	Возврат Пробелы;
	
КонецФункции
Дерево значений 1С

Дерево значений 1С на управляемой форме

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

Дерево значений на управляемой форме 1С

Само по себе такое дерево бессмысленно. Нужно добавить ему колонки.  Если мы хотим дереву значений на форме добавить колонку, то необходимо сначала выделить дерево на форме, а потом нажать на кнопку «Добавить колонку реквизита».

Дерево значений на управляемой форме 1С

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

Дерево значений на управляемой форме 1С

В моей учебной конфигурации имеется справочник Регионы, который является иерархическим (иерархия элементов).

Иерархический справочник на управляемой форме 1С

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

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	               |	Регионы.Ссылка КАК Регион,
	               |	Регионы.ВидРегиона КАК ВидРегиона
	               |ИЗ
	               |	Справочник.Регионы КАК Регионы
	               |
	               |УПОРЯДОЧИТЬ ПО
	               |	Регионы.Ссылка ИЕРАРХИЯ";
	
КонецПроцедуры

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

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	               |	Регионы.Ссылка КАК Регион,
	               |	Регионы.ВидРегиона КАК ВидРегиона
	               |ИЗ
	               |	Справочник.Регионы КАК Регионы
	               |
	               |УПОРЯДОЧИТЬ ПО
	               |	Регионы.Ссылка ИЕРАРХИЯ";
	

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

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

Второй способ  проще в плане набора кода, но он неоптимальный с точки зрения производительности: мы просто получим выгрузку из запроса и загрузим выгрузку в дерево значений на форме.

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	               |	Регионы.Ссылка КАК Регион,
	               |	Регионы.ВидРегиона КАК ВидРегиона
	               |ИЗ
	               |	Справочник.Регионы КАК Регионы
	               |
	               |УПОРЯДОЧИТЬ ПО
	               |	Регионы.Ссылка ИЕРАРХИЯ";
	
	
	Дерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

	ЗначениеВРеквизитФормы(Дерево,"ДеревоЗначений");
	
КонецПроцедуры

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

Дерево значений на управляемой форме 1С

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

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

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

&НаКлиенте
Процедура Очистить(Команда)
	ОбойтиИОчиститьДеревоНаФорме(Регионы.ПолучитьЭлементы());
КонецПроцедуры

Дерево значений на управляемой форме 1С

Соответствия в 1С

Массивы в 1С

Список значений в 1С

Структура в 1С

Более подробно о работе с другими интересными объектами языка программирования в 1С читайте в моей книге «Программировать в 1С за 11 шагов»

Книга «Программировать в 1С за 11 шагов»

Изучайте программирование в 1С в месте с моей книги «Программировать в 1С за 11 шагов»

  1. Книга написана понятным и простым языком — для новичка.
  2. Книга посылается на электронную почту в формате PDF. Можно открыть на любом устройстве!
  3. Научитесь понимать архитектуру 1С;
  4. Станете писать код на языке 1С;
  5. Освоите основные приемы программирования;
  6. Закрепите полученные знания при помощи задачника;

Книга «Основы разработки в 1С: Такси»

Отличное пособие по разработке в управляемом приложении 1С, как для начинающих разработчиков, так и для опытных программистов.

  1. Очень доступный и понятный язык изложения
  2. Книга посылается на электронную почту в формате PDF. Можно открыть на любом устройстве!
  3. Поймете идеологию управляемого приложения 1С
  4. Узнаете, как разрабатывать управляемое приложение;
  5. Научитесь разрабатывать управляемые формы 1С;
  6. Сможете работать с основными и нужными элементами управляемых форм
  7. Программирование под управляемым приложением станет понятным

Промо-код на скидку в 15% — 48PVXHeYu


Если Вам помог этот урок решить какую-нибудь проблему, понравился или оказался полезен, то Вы можете поддержать мой проект, перечислив любую сумму:

можно оплатить вручную:

Яндекс.Деньги — 410012882996301

Вступайте в мои группы:

Вконтакте: https://vk.com/1c_prosto
Фейсбуке: https://www.facebook.com/groups/922972144448119/
ОК: http://ok.ru/group/52970839015518
Твиттер: https://twitter.com/signum2009

 0 

   

Распечатать

Как отобразить дерево значений на управляемой форме

Создадим новую внешнюю обработку.

Добавляем управляемую форму и создаем обработчик ПриСозданииНаСервере(). Никаких реквизитов и элементов “вручную” не добавляем, будем делать это программно.

Код 1C v 8.2 УП

 &НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Создание и заполнение "обычного" объекта прикладного типа ДеревоЗначений,
    // который будет отображен на управляемой форме
    ДеревоОбъект = Новый ДеревоЗначений;
    ДеревоОбъект.Колонки.Добавить("Узел", Новый ОписаниеТипов("Строка"));
    Для к1 = 1 По 3 Цикл
        СтрокаУ1 = ДеревоОбъект.Строки.Добавить();
        СтрокаУ1.Узел = "Узел"+к1;
        Для к2 = 1 По 3 Цикл
            СтрокаУ2 = СтрокаУ1.Строки.Добавить();
            СтрокаУ2.Узел = СтрокаУ1.Узел+к2;
            Для к3 = 1 По 3 Цикл
                СтрокаУ3 = СтрокаУ2.Строки.Добавить();
                СтрокаУ3.Узел = СтрокаУ2.Узел+к3;
            КонецЦикла;
        КонецЦикла;
    КонецЦикла;

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

    // Преобразование объекта прикладного типа ДеревоЗначений 
    // в реквизит управляемой формы (данные формы)
    ЗначениеВРеквизитФормы(ДеревоОбъект, "Дерево");

    // Создание элемента формы типа ТаблицаФормы для отображения дерева
    ЭлементДерево = Элементы.Добавить("Дерево", Тип("ТаблицаФормы"));
    ЭлементДерево.ПутьКДанным = "Дерево";
    ЭлементДерево.Отображение = ОтображениеТаблицы.Дерево;

    Для Каждого Колонка Из ДеревоОбъект.Колонки Цикл
        НовыйЭлемент = Элементы.Добавить(Колонка.Имя, Тип("ПолеФормы"), 
            ЭлементДерево);
        НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
        НовыйЭлемент.ПутьКДанным = "Дерево." + Колонка.Имя;
    КонецЦикла;

КонецПроцедуры

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

Как и таблица значений, дерево состоит из строк и колонок. Строки располагаются вниз по вертикали, а колонки — вправо по горизонтали. Однако, в отличие от таблицы значений, строки дерева значений могут иметь подчиненные строки любой глубины вложенности.

Строки и колонки имеют индексы, по которым к ним можно обращаться напрямую (начинаются с 0). Кроме этого, к колонкам можно обращаться по идентификатору.

Пересечения строк и колонок образуют ячейки, в которых содержатся значения. Тип значения определяется типом значения колонки.

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

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

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

Как и большинство объектов встроенного языка, новая дерево значений может быть создано с помощью оператора Новый:

дз = Новый ДеревоЗначений;

Свойства и методы дерева значений

Имя Тип Описание
Свойства
Колонки КоллекцияКолонокДереваЗначений содержит коллекцию колонок дерева значений
Строки КоллекцияСтрокДереваЗначений содержит коллекцию корневых строк дерева значений
Методы
ВыбратьСтроку() СтрокаДереваЗначений открывает диалог для интерактивного выбора строки дерева значений
Скопировать() ДеревоЗначений создает новый объект копированием текущего (копируются все колонки и строки)

Колонки дерева значений

Прежде чем начать работу с деревом значений, необходимо создать структуру колонок. Каждая колонка характеризуется следующими свойствами:

Имя Тип Описание
Имя Строка символьный идентификатор колонки, по которому к ней можно обращаться из кода. Может содержать только алфавитные символы, цифры и знаки подчеркивания. Причем, начинаться имя колонки может только с буквы или символа подчеркивания
Заголовок Строка строковое представление колонки на форме. Может содержать произвольные символы
ТипЗначения ОписаниеТипов тип значения содержимого ячеек в этой колонке. Свойство ограничивает пространство доступных значений, которые можно указать в данной колонке
Ширина Число ширина колонки на форме (выражается в количестве символов)

Доступ к колонкам производится через свойство Колонки объекта ДеревоЗначений. Для добавления новой колонки используется метод Колонки.Добавить():

дз.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
дз.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
дз.Колонки.Добавить("Свойство");//можно хранить произвольные данные

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

найдКолонка = дз.Колонки.Найти("Наименование"); 
Если найдКолонка = Неопределено Тогда 
    Сообщить("Колонка не найдена!");
Иначе
    Сообщить("Индекс колонки = " + дз.Колонки.Индекс(найдКолонка));
КонецЕсли;

Перебор колонок выполняется следующим образом:

Для каждого Колонка Из дз.Колонки Цикл
    Сообщить(Колонка.Имя + ": " + Колонка.ТипЗначения);
КонецЦикла;
//Результат:
//    Наименование: Строка
//    Количество: Число
//    Свойство:

Для удаления колонки используется метод Колонки.Удалить():

найдКолонка = дз.Колонки.Найти("Свойство"); 
Если НЕ найдКолонка = Неопределено Тогда 
    дз.Колонки.Удалить(найдКолонка); 
КонецЕсли;

Методы коллекции колонок дерева значений

Вставить() Вставляет новую колонку в указанную позицию коллекции
Добавить() Добавляет новую колонку в конец коллекции
Индекс() Возвращает индекс колонки в коллекции колонок
Получить() Возвращает колонку по ее индексу
Количество() Возвращает количество колонок в коллекции
Найти() Ищет колонку в коллекции по имени
Очистить() Удаляет все колонки из коллекции
Сдвинуть() Сдвигает колонку влево или вправо
Удалить() Удаляет колонку из коллекции

Строки дерева значений

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

В отличие от таблицы значений, доступ к строкам дерева значений производится через свойство Строки объекта ДеревоЗначений или объекта СтрокаДереваЗначений. Каждая строка является узлом иерархии, поэтому обладает следующими свойствами и методами:

Имя Тип Описание
Свойства
Родитель СтрокаДереваЗначений ссылается на строку верхнего уровня. Для корневой строки имеет значение Неопределено
Строки КоллекцияСтрокДереваЗначений содержит коллекцию подчиненных строк
Методы
Владелец() ДеревоЗначений возвращает ссылку на дерево значений, которому принадлежит строка
Уровень() Число возвращает уровень вложенности строки в иерархии дерева. Для строки, не имеющей родителя, уровень будет равен 0 (верхний уровень или корневой уровень). Подчиненная ей строка будет иметь уровень 1 и т.д.

Добавление и удаление строк

Для добавления новой строки используется метод Строки.Добавить(). Метод возвращает объект СтрокаДереваЗначений, с которым доступны дальнейшие манипуляции:

СтрокаДЗ = дз.Строки.Добавить();

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

СтрокаДЗ.Наименование = "Стул деревянный";
СтрокаДЗ.Количество = 1; 
СтрокаДЗ.Свойство = ТекущаяДата();

Для удаления строки используется метод Строки.Удалить(). Строку можно удалить передав методу либо саму строку, либо ее индекс:

//удаление строки
дз.Строки.Удалить(СтрокаДЗ);
//удаление по индексу
ИндексСтроки = дз.Строки.Индекс(СтрокаДЗ);
дз.Строки.Удалить(ИндексСтроки);
//безопасное удаление
НайдСтрока = дз.Строки.Найти("Стул деревянный", "Наименование");
Если НЕ НайдСтрока = Неопределено Тогда
    дз.Строки.Удалить(НайдСтрока);
КонецЕсли;

Перебор строк дерева значений

Для перебора строк удобнее всего использовать оператор цикла Для Каждого. В редких случаях оправдано применение цикла Для:

Для Каждого СтрокаДЗ Из дз.Строки Цикл 
    ИндСтроки = дз.Строки.Индекс(СтрокаДЗ);
    Сообщить("Строка[" + ИндСтроки + "]: " + СтрокаДЗ.Наименование);
КонецЦикла;
//в редких случаях 
Для ИндСтроки = 0 По дз.Строки.Количество() - 1 Цикл
    СтрокаДЗ = дз.Строки.Получить(ИндСтроки);
    Сообщить("Строка[" + ИндСтроки + "]: " + СтрокаДЗ.Наименование);
КонецЦикла;

Поиск строк

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

//удаление первой найденной строки
НайдСтрока = дз.Строки.Найти("Стул деревянный", "Наименование");
Если НЕ НайдСтрока = Неопределено Тогда
    дз.Строки.Удалить(НайдСтрока);
КонецЕсли;
//удаление всех найденных строк
ПараметрыПоиска = Новый Структура("Наименование", "Стул деревянный");
мНайдСтроки = дз.Строки.НайтиСтроки(ПараметрыПоиска);
Для каждого НайдСтрока Из мНайдСтроки Цикл
    дз.Строки.Удалить(НайдСтрока);
КонецЦикла;

Поиск возможен даже среди вложенных строк. Внимательно изучите документацию к методам Строки.Найти() и Строки.НайтиСтроки().

Все методы коллекции строк дерева значений:

Вставить() Вставляет строку на указанное место коллекции строк
ВыгрузитьКолонку() Выгружает значения ячеек указанной колонки из текущей коллекции строк в массив значений
Добавить() Добавляет новую строку в коллекцию строк
ЗагрузитьКолонку() Загружает значения из массива в ячейки указанной колонки коллекции строк
Индекс() Возвращает индекс строки дерева значений в коллекции
Итог() Возвращает просуммированный итог по колонке дерева значений
Количество() Возвращает количество строк в коллекции строк
Найти() Выполняет поиск строки по значению
НайтиСтроки() Выполняет поиск строк по указанным параметрам
Очистить() Удаляет все строки из текущей коллекции строк
Получить() Возвращает строку по ее индексу
Сдвинуть() Сдвигает строку вверх или вниз по коллекции строк
Сортировать() Выполняет сортировку строк в коллекции по указанным колонкам
Удалить() Удаляет строку из коллекции строк

Иерархию свойств и типов значений, связанных с деревом значений, схематически можно представить в виде дерева:

Материал дополняется…

Читайте также:

Поделиться страницей в соц.сетях

Объект «ДеревоЗначений» в 1С 8.3 представляет собой динамический набор значений любого типа. Для такого набора доступны свойства «колонки» и «строки» где каждая строка может иметь набор подчинённых строк, а каждая подчинённая строка набор своих подчинённых строк и т.д.  Программная инициализация дерева значений в 1С осуществляется с помощью оператора НОВЫЙ.

Пример:

ДеревоЗначений = Новый ДеревоЗначений;

Визуально, объект «ДеревоЗначений» похож на дерево групп справочника. К примеру, можно открыть справочник «Номенклатура» любой типовой конфигурации выставив форму списка в «Режим просмотра» — «Дерево».

Полезно понимать, что объект «ДеревоЗначений» схож с объектом «ТаблицаЗначений». Основной отличительный признак наличие дополнительного реквизита «Родитель» у объекта «ДеревоЗначений», с помощью которого и организовывается иерархия и подчинённость.

Наша команда предоставляет услуги по консультированию, настройке и внедрению 1С.

Связаться с нами можно по телефону +7 499 350 29 00.

Услуги и цены можно увидеть по ссылке.

Будем рады помочь Вам!

Содержание

  • Дерево значений на управляемой форме
  • Заполнение дерева значений
    • Ручное заполнение реквизита формы с типом «ДеревоЗначений»
    • Заполнение дерева значений из результата запроса
  • Поиск строк в дереве значений
    • Метод «Найти()»
    • Метод «НайтиСтроки()»
  • Очистка дерева значений или строк
  • Обход дерева значений в 1С
  • Как преобразовать дерево значений в таблицу значений

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

Если появляется необходимость показать данные в виде дерева пользователю, следует на форме создать реквизит с типом «ДеревоЗначений» и определится с составом колонок.

Для отображения на форме добавим реквизит в раздел элементов формы простым перетаскиванием.

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

Заполнить данными дерево значений в 1С можно программно (вручную) или получить из результата запроса. Рассмотрим на примерах оба способа.

Ручное заполнение реквизита формы с типом «ДеревоЗначений»

Простой пример:

&НаКлиенте
Процедура ДЗ_НаФорме(Команда)
ДЗ_НаФормеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ДЗ_НаФормеНаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

Корень = ДеревоЗначений.Строки.Добавить();
Корень.Наименование = "Корневая группа";

Группа1 = Корень.Строки.Добавить();
Группа1.Наименование = "Первая группа";

Элемент1 = Группа1.Строки.Добавить();
Элемент1.Наименование = "Элемент №1";                               

Группа2 = Корень.Строки.Добавить();
Группа2.Наименование = "Втровая группа";

Элемент1 = Группа2.Строки.Добавить();
Элемент1.Наименование = "Элемент №1";

Элемент2 = Группа2.Строки.Добавить();
Элемент2.Наименование = "Элемент №2";
        
// Преобразуем объект ДеревоЗначений в реквизит формы         
ЗначениеВРеквизитФормы(ДеревоЗначений,"ДЗ");                                                                     

КонецПроцедуры

Результат запроса на форме:

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

Сделаем простой запрос к справочнику «Номенклатура». Результат запроса преобразуем в дерево значений с помощью метода «Выгрузить», установив для его параметра «ТипОбхода» в значение «ПоГруппировкамСИерархией».  Для передачи сформированного дерева значений на форму, важно чтобы имена колонок совпадали с именами колонок реквизита на форме.

&НаКлиенте
Процедура ДЗ_НаФорме(Команда)
ДЗ_НаФормеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ДЗ_НаФормеНаСервере()

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|          Номенклатура.Ссылка КАК Наименование
|ИЗ
|          Справочник.Номенклатура КАК Номенклатура
|
|УПОРЯДОЧИТЬ ПО
|          Наименование ИЕРАРХИЯ";
ДеревоЗначений = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

ЗначениеВРеквизитФормы(ДеревоЗначений, "ДЗ");

КонецПроцедуры

Результат запроса на форме может выглядеть так:

Поиск строк в дереве значений

Для поиска строк в дереве значений существует два способа, точнее говоря, метода.

Метод «Найти()»

  • Найти(<Значение>, <Колонки>, <ВключатьПодчиненные>)

Возвращает строку дерева значений, если строка не найдена, вернёт «Неопределено». Поиск можно ограничивать, указав в параметре <Колонки> нужные колонки для поиска. Также расширять, указав для параметра <ВключатьПодчиненные> значение «Истина», тогда в поиске будут участвовать строки подчинённых коллекций.

Для примера найдём строку со значением «Элемент №1» в дереве значений вида:

Пример кода:

&НаКлиенте
Процедура НайтиСтрокуДЗ(Команда)
            НайтиСтрокуДЗ_НаСервере();
КонецПроцедуры

&НаСервере
Процедура НайтиСтрокуДЗ_НаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

// Ищем строку
НайденнаяСтрока = ДеревоЗначений.Строки.Найти("Элемент №1","Наименование", Истина);

// Анализ результата поиска
Если НайденнаяСтрока = Неопределено Тогда
Сообщить("Строка не найдена");
Иначе
Сообщить(НайденнаяСтрока.Наименование + " - " + НайденнаяСтрока.Родитель.Наименование);
КонецЕсли;
          
КонецПроцедуры

Результат:

Из результата видно, что метод вернул первую попавшуюся строку, и поиск был прекращён.

Метод «НайтиСтроки()»

Если необходимо найти все строки со значением «Элемент №1» тогда следует использовать метод НайтиСтроки()

  • НайтиСтроки(<ПараметрыОтбора>, <ВключатьПодчиненные>)

Где <ПараметрыОтбора> — это условия для поиска в виде структуры, а параметр <ВключатьПодчиненные> с помощью значений Ложь/Истина контролирует возможность поиска в подчинённых коллекциях.

Пример кода:

&НаКлиенте
Процедура НайтиСтрокиДЗ(Команда)
НайтиСтрокиДЗНаСервере();
КонецПроцедуры

&НаСервере
Процедура НайтиСтрокиДЗНаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

// Условие для поиска
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Наименование", "Элемент №1");

// Поиск строк
МассивСтрок = ДеревоЗначений.Строки.НайтиСтроки(ПараметрыОтбора, Истина);

// Проверяем найдены ли строки
Если МассивСтрок.Количество() = 0 Тогда
Сообщить("Не найдено ни одной строки, удовлетворяющей условия поиска.");
КонецЕсли;

// Перебор массива строк
Для Каждого Строка Из МассивСтрок Цикл

Если Строка.Родитель = Неопределено Тогда
Сообщить(Строка.Наименование + " - Корень дерева значений.");
Иначе
Сообщить(Строка.Наименование + " - " + Строка.Родитель.Наименование);
КонецЕсли

КонецЦикла;

КонецПроцедуры

Результат:

Очистка дерева значений или строк

Для очистки дерева значений пригодятся методы  Очистить(), Удалить(). Пример кода:

&НаСервере
Процедура УдалитьСтрокуНаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

ДеревоЗначений.Строки.Очистить();

// Или по индексу
ДеревоЗначений.Строки.Удалить(0);

// Преобразуем объект ДеревоЗначений в реквизит формы
ЗначениеВРеквизитФормы(ДеревоЗначений, "ДЗ");

КонецПроцедуры

С помощью данных методов возможно удаление конкретных строк дерева значений.

!!!При удалении либо очистки строки все её подчинённые строки удалятся!!!

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

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

Пример:

&НаСервере
Процедура ОбойтиДЗ_НаСервере()
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");
ОбходДереваЗначений(ДеревоЗначений);
КонецПроцедуры

&НаСервере
Процедура ОбходДереваЗначений(Данные)

Для Каждого Строка Из Данные.Строки Цикл

Сообщить(Строка.Наименование);

Если Строка.Строки.Количество()>0 Тогда
ОбходДереваЗначений(Строка);
КонецЕсли;

КонецЦикла;

КонецПроцедуры

Как преобразовать дерево значений в таблицу значений

Состав элементов и реквизитов формы:

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

&НаСервере
Процедура ВТЗНаСервере()


ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");
ТаблицаЗначений = РеквизитФормыВЗначение("ТЗ");

ПреобразоватьВ_ТЗ(ДеревоЗначений, ТаблицаЗначений, Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000"));
ДеревоЗначений.Строки.Очистить();

ЗначениеВРеквизитФормы(ТаблицаЗначений, "ТЗ");
ЗначениеВРеквизитФормы(ДеревоЗначений, "ДЗ");
КонецПроцедуры

&НаСервере
Процедура ПреобразоватьВ_ТЗ(Данные, ТаблицаЗначений, ГУИД)

Для Каждого Строка Из Данные.Строки Цикл

НовСтрока = ТаблицаЗначений.Добавить();
НовСтрока.Наименование = Строка.Наименование;
НовСтрока.Родитель = ГУИД;
НовСтрока.ГУИД = Новый УникальныйИдентификатор();

Если Строка.Строки.Количество()>0 Тогда
ПреобразоватьВ_ТЗ(Строка, ТаблицаЗначений, НовСтрока.ГУИД);
КонецЕсли;

КонецЦикла;

КонецПроцедуры

Используя данный способ можно легко преобразовать таблицу значений обратно в дерево значений.  Из примера «ГУИД» это уникальный идентификатор строки, а «Родитель» уникальный идентификатор родителя. Если обратного преобразования не требуется можно исключить использование колонок «Родитель» и  «ГУИД».

Содержание:

1.     Что такое Дерево значений в 1С

2.     Как создать дерево значений в 1C 8.3 

1.   Что такое Дерево значений в 1С

Дерево значений в 1С (тип значения «ДеревоЗначений») – это Универсальная коллекция значений в системе 1С, где Элементы упорядочены по Индексу (нумерация начинается с 0). Каждое значение Элемента содержит в себе Коллекцию подчиненных Строк, где у каждой Строки коллекции указывается Родитель – Элемент, находящийся на уровень выше. У корневого Элемента (элемент первого уровня) Родитель отсутствует. Доступ к Элементам осуществляется через Индекс, по номеру которого можно получить доступ к значению Элемента и коллекции подчиненных ему Строк.

Описание из Синтакс-помощника: используется для доступа к свойствам и методам дерева значений 1С в целом. Объект, представляющий собой древовидную структуру, обладает сходной функциональностью с таблицей значений, а также обладает возможностью добавлять подчиненные строки к какой-либо строке дерева.

Внимание! В толстом клиенте (обычное приложение) передача значений данного типа с клиента на сервер и обратно приводит к возникновению исключения.

Доступность: сервер, толстый клиент, внешнее соединение, мобильное приложение (сервер), мобильный автономный сервер.

Сериализуется: данный объект может быть сериализован в/из XDTO. Тип XDTO, соответствующий данному объекту, определяется в пространстве имен {http://v8.1c.ru/8.1/data/core}. Имя типа XDTO: ValueTree.  

2.     Как создать дерево значений в 1C 8.3

Так как Дерево значений в 1С 8.3 является усложненной конструкцией с точки зрения программной реализации, и достаточно ресурсоемкой, её не часто используют в разработках. Однако, она очень удобна для реализации нестандартных задач, в том числе, когда требуется отобразить множество значений с возможностью группировки по Родителю.

Необходимо однозначно разделять Дерево значений как объявленную переменную (через конструктор «Новый ДеревоЗначений») и как Реквизит Формы 1С 8.3 (обратите внимание, что ДЗ нельзя выбрать как тип данных Реквизита Объекта).

Если объявленное (далее – ОП) ДЗ можно читать НаКлиенте, передавать НаСервере для обработки и возвращать обратно, то ДЗ как Реквизит Формы 1С 8.3 (далее – РФ) будет читабельным только НаКлиенте. НаСервере даже при попытке чтения дерево выдаст Исключение, а изменять данные, конечно же, не позволит вообще.

Далее рассмотрим поведение одинаковых действий при условии объявления переменной (в рамках одной процедуры/функции) и условиях добавленного реквизита формы 1С 8.3. Варианты работы с глобальными переменными, экспортными или не экспортными переменными модуля мы рассматривать не будем, так как применение зависит от контекста задачи и Вашей веры в объявление глобальных и внеконтекстных переменных.

У ДЗ как РФ есть свое преимущество: вы можете задать колонки и тип данных в самом РФ и программно обращаться к данным из любой процедуры или функции модуля формы (не забывая о директивах компиляции). Для ДЗ как ОП требуется описать все необходимые колонки (для начинающих это может быть проблематично, если нет четкого представления того, что это за «зверь» такой), а также понимать, что по окончании процедуры/функции кончается и жизненный цикл переменной (конечно, если мы не объявили ее глобальной, но это уже совсем другая история).

Пример описания ДЗ программе 1С Предприятие 8:

Пример описания ДЗ в 1С

Как видно из примера, в ДЗ задаются колонки, где будут размещаться данные, но чтобы добавить это данные, необходимо обратиться к Коллекции Строк: ДеревоДанных.Строки.Добавить(). Таким образом добавляются строки первого уровня, после чего в строку первого уровня добавляем коллекцию строк и так далее. При этом, используя метод Добавить(), Родитель присваивается автоматически, но, конечно, его можно изменить. Так и строится дерево, однако не стоит забывать, что чем больше данных, тем больше потребление ресурсов.

Пример ДЗ как РФ:

ДЗ в программе 1С Предприятие 8

Добавление данных происходит, в сущности, таким же образом, как при описании, но есть программные различия. Опишем основные:

программные различия ОП РФ

*Пример преобразования:

Пример преобразования

** Пример работы «преобразование-запись-преобразование»:

преобразование-запись-преобразование

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

Поэтому если требуется результат запроса (где данные сгруппированы) загрузить в ДЗ, необходимо:

1.     (необязательно) преобразовать из реквизита формы;

2.     загрузить результат;

3.     преобразовать в реквизит формы.

загрузить результат в 1С

При необходимости загрузки данных в ДЗ из табличной части 1С можно воспользоваться несколькими способами:

1. Запись с помощью циклов: тут нужно помнить, что есть строки «корневые», и надо их каким-то образом найти, чтобы правильно построить связь Корень-Подчиненный.

2. Загрузить с помощью Запроса (и здесь есть два варианта):

a.      выгрузить табличную часть 1С во временную переменную, передать ее как параметр, сформировать результат с использованием иерархии и загрузить результат в дерево;

 выгрузить табличную часть 1С

b.     сформировать запрос, в который передаем как параметр ссылку принятого к обработке документа в 1С 8.3, после чего формируем результат с использованием иерархии и загружаем его в дерево.

 сформировать запрос в 1С

*Варианты 2а и 2b кажутся похожими. Однако разница между ними есть. Она заключается в производительности, которая зависит от контекста использования (количество данных, количество пользователей, нагрузка на сервер и другое). С одной стороны, при использовании директивы НаСервере у нас уже есть табличная часть 1С (конечно, на стороне сервера, но она уже загружена), которую мы можем без потерь и других действий передать в Запрос и выполнить его. С другой стороны, обращаясь по ссылке в результате выполнения Запроса, система повторно обратится к БД, но, возможно, сделает это быстрее, чем в первом случае. Этот момент требует исследования уже на конкретной БД.

Для записи данных в табличную часть 1С или Таблицу значений потребуется построить цикл, который будет выполнять запись в Приемник по следующим правилам:

1. обход дерева выполняется от Корня к Подчиненным до последнего уровня вложенности;

2. если у Корня есть Подчиненные элементы, то количество результирующих строк в Таблице высчитывается по формуле: , где n – количество элементов на заданном уровне, m — количество уровней (глубина вложенности дерева);

3. рекомендуется записывать Корень – Подчиненный элемент, в связи с этим требуется запись рекурсивным методом.

Пример рекурсивной записи из ДЗ в Табличную часть Товары:

Пример рекурсивной записи из ДЗ

Для чтения данных все немного проще: читать можно как НаКлиенте, так и НаСервере. Разница только в том, что для директивы НаКлиенте потребуется сначала вызвать метод ПолучитьЭлемент(), чего не требуется для серверной директивы. Внимание! Не забудьте, что чтение в процедуре/функции с директивой НаКлиентеНаСервереБезКонтекста компилируется одновременно на клиенте и на сервере, а это значит, что описав Чтение как для клиента при вызове исполнения из процедуры/функции с директивой сервера, программа выдаст исключение (и наоборот). При необходимости вызова чтения как НаКлиенте, так и НаСервере рекомендуется создавать обработчики с соответствующими директивами.

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

Реализованный пример такой задачи:

Реализованный пример ДЗ

*Естественно, текущие данные тестовые. Задачей предполагалось, что корневых уровней может быть до 15, второй уровень – примерно так же, а вот на последнем уровне может быть до 50 строк. В такой компоновке удобно видеть все элементы нижнего уровня, переключаясь между Родителями.

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

Специалист компании ООО «Кодерлайн»

Каплин Иван Александрович

Понравилась статья? Поделить с друзьями:

Другие крутые статьи на нашем сайте:

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии