Добрый день!
Помогите, пожалуйста!
Сразу говорю, конфигурация не типовая, формы обычные.
Есть запрос, который формирует следующие данные в виде:
Номенклатура | Магазин| Остаток| Сумма.
Например,
Гвозди - Магазин №1 - 100шт - 5000 руб
Гвозди - Магазин №2 - 200шт - 3000 руб
Молоток - Магазин №1 - 6шт - 10000 руб
Молоток - Магазин №2 - 7шт - 12000 руб
Далее этот запрос обрабатывается в следующий вид:
Гвозди - Магазин №1 - 100шт - 5000 руб - Магазин №2 - 200 шт- 3000 руб
Молоток - Магазин №1 - 6шт - 10000 руб - Магазин №2 - 7шт - 12000 руб
Т.е. он склеивает строки с одной номенклатурой в одну длинную строку вида Магазин-Остаток-Сумма.
Количество магазинов разное, в зависимости от того, какие выбрал пользователь галочками.

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

Номенклатуру выводим в виде иерархии из справочника "Номенлатура". - Это я могу сделать запросом и дальнейшей обработкой результата , соединив запрос со справочником номенклатуры и упорядочиванием ПО ИЕРАРХИИ.
Не могу определиться как вывести результат пользователю в виде дерева. Придумал 2 способа:
1) Выводить результат в дерево значений и динамически создавать колонки в дереве. Чтобы привязать отметку к Магазину (Магазины - это справочник "Подразделения"), создавать уникальные Имена, например "Остаток_Магазин1", "Сумма_Магазин1" и уже потом по этим именам получать магазины, на которые надо переместить товар.
2) Выводить результат в табличный документ с автогруппировкой строк. Колонки делать присоединением области. Пока разбираюсь, как в табличном документе сделать параметр, который пользователь сам может выбрать (Галочку поставить), но вроде бы так можно :)
Какой способ во вашему удачнее и можете дать советы или замечания?
---
Когда я реализовывал похожую задачу, то на форме рисовал шаблон группы колонок для одного "магазина". И программно их "размножал", копируя свойства шаблона.
Мне нужно было в таблице (не дереве) на форме в колонках работать с произвольным количеством дней в периоде планирования. По каждому дню было два показателя - расчетное значение плана и фактическое (пользователь указывает). В итоге что-то такое получилось:

```1c
    Процедура ДобавитьКолонкуДняПлана(НомерДня)

    ДатаПланирования = ДатаПланирования(НомерДня);

    КолонкиПлана = ТаблицаПлана.Колонки;
    КолонкиПланаИнтерфейс = ЭлементыФормы.ТаблицаПлана.Колонки;

    ГрупповаяКолонкаДня = КолонкиПланаИнтерфейс.Добавить("День" + НомерДня);
    ЗаполнитьЗначенияСвойств(ГрупповаяКолонкаДня, КолонкиПланаИнтерфейс.День, , "Имя,Видимость,ИзменятьВидимость");
    Если НомерДня = 0 Тогда // итоговая колонка
    ГрупповаяКолонкаДня.ТекстШапки = "Итого";
    Иначе
    ГрупповаяКолонкаДня.ТекстШапки = Формат(ДатаПланирования,"ДФ=ддд") + " - " + Формат(ДатаПланирования, "ДФ=dd.MM");
    КонецЕсли;

    СтруктураКолонок = Новый Структура("КвоРасчет,КвоПлан", ОбщегоНазначения.ПолучитьОписаниеТиповЧисла(12,3), ОбщегоНазначения.ПолучитьОписаниеТиповЧисла(12,3));
    Для Каждого Элемент Из СтруктураКолонок Цикл
    ИмяКолонки = Элемент.Ключ + НомерДня;
    Колонка = КолонкиПлана.Добавить(ИмяКолонки, Элемент.Значение);
    ЗаполнитьЗначенияСвойств(Колонка, КолонкиПлана[Элемент.Ключ], , "Имя");
    КолонкаФормы = КолонкиПланаИнтерфейс.Добавить(ИмяКолонки);
    ЗаполнитьЗначенияСвойств(КолонкаФормы, КолонкиПланаИнтерфейс[Элемент.Ключ], , "Имя,Видимость,ИзменятьВидимость");
    Если НомерДня = 0 Тогда // итоговая колонка
    КолонкаФормы.ТолькоПросмотр = Истина;
    КонецЕсли;
    КолонкаФормы.Данные = ИмяКолонки;
    Если Элемент.Ключ = "КвоПлан" Тогда
    КолонкаФормы.ЭлементУправления.УстановитьДействие("ПриИзменении", Новый Действие("ЯчейкаПланаПриИзменении"));
    КонецЕсли;
    КонецЦикла;

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

ЗЫ. Ну а хранилось это все в табличной части документа в стандартной табличной структуре - "День, Номенклатура, КвоРасчет, КвоПлан". При открытии "распаковывалось" на форму в нужном виде, при записи - "запаковывалось" в табличную часть.
---
zlakizla, а зачем тебе все в строку выводить? Тогда результат будет обычной ТЧ.

Если это не главный нюанс, то можно оставить дерево и его же и выводить.

Но если нужен именно такой замес, то рекомендую содрать с типовой конфы обработку прайс-лист. Там результат запроса дерево, а данные выводятся в отчете так как ты захотел.
На старых конфах это сделано через построитель, а на новых через СКД.
---
По мне так второй способ проще. С деревом мне сложнее работать.
И ещё, отчет строится в СКД?
---
Ну это не отчет будет, а обработка. А запрос строится через построитель с заданными пользователем отборами. Вообщем без использования СКД
---
Может есть еще способы какие? Как-то криво получается привязка к магазинам... По имени колонок мне не нравится, но пока ничего не могу придумать :(
---
zlakizla, В ТаблДок делай через метод
```1c
    join()
```
---
Полностью зависит от целей. Если для интерактива с пользователем - то в дерево на форме.
Если в основном для отображения и нужны расширенные оформительские возможности, а интерактив минимален даже в перспективе - то можно через табдок.
По первому варианту - нумерованные имена колонок это вполне нормально. Просто сразу нужно получить все магазины в отдельный массив, который и будет хранить однозначное соответствие индекса колонки реальному магазину.
---
Когда я реализовывал похожую задачу, то на форме рисовал шаблон группы колонок для одного "магазина". И программно их "размножал", копируя свойства шаблона.
Мне нужно было в таблице (не дереве) на форме в колонках работать с произвольным количеством дней в периоде планирования. По каждому дню было два показателя - расчетное значение плана и фактическое (пользователь указывает). В итоге что-то такое получилось:

```1c
    Процедура ДобавитьКолонкуДняПлана(НомерДня)

    ДатаПланирования = ДатаПланирования(НомерДня);

    КолонкиПлана = ТаблицаПлана.Колонки;
    КолонкиПланаИнтерфейс = ЭлементыФормы.ТаблицаПлана.Колонки;

    ГрупповаяКолонкаДня = КолонкиПланаИнтерфейс.Добавить("День" + НомерДня);
    ЗаполнитьЗначенияСвойств(ГрупповаяКолонкаДня, КолонкиПланаИнтерфейс.День, , "Имя,Видимость,ИзменятьВидимость");
    Если НомерДня = 0 Тогда // итоговая колонка
    ГрупповаяКолонкаДня.ТекстШапки = "Итого";
    Иначе
    ГрупповаяКолонкаДня.ТекстШапки = Формат(ДатаПланирования,"ДФ=ддд") + " - " + Формат(ДатаПланирования, "ДФ=dd.MM");
    КонецЕсли;

    СтруктураКолонок = Новый Структура("КвоРасчет,КвоПлан", ОбщегоНазначения.ПолучитьОписаниеТиповЧисла(12,3), ОбщегоНазначения.ПолучитьОписаниеТиповЧисла(12,3));
    Для Каждого Элемент Из СтруктураКолонок Цикл
    ИмяКолонки = Элемент.Ключ + НомерДня;
    Колонка = КолонкиПлана.Добавить(ИмяКолонки, Элемент.Значение);
    ЗаполнитьЗначенияСвойств(Колонка, КолонкиПлана[Элемент.Ключ], , "Имя");
    КолонкаФормы = КолонкиПланаИнтерфейс.Добавить(ИмяКолонки);
    ЗаполнитьЗначенияСвойств(КолонкаФормы, КолонкиПланаИнтерфейс[Элемент.Ключ], , "Имя,Видимость,ИзменятьВидимость");
    Если НомерДня = 0 Тогда // итоговая колонка
    КолонкаФормы.ТолькоПросмотр = Истина;
    КонецЕсли;
    КолонкаФормы.Данные = ИмяКолонки;
    Если Элемент.Ключ = "КвоПлан" Тогда
    КолонкаФормы.ЭлементУправления.УстановитьДействие("ПриИзменении", Новый Действие("ЯчейкаПланаПриИзменении"));
    КонецЕсли;
    КонецЦикла;

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

ЗЫ. Ну а хранилось это все в табличной части документа в стандартной табличной структуре - "День, Номенклатура, КвоРасчет, КвоПлан". При открытии "распаковывалось" на форму в нужном виде, при записи - "запаковывалось" в табличную часть.
---
где-то делал как в (7)
где-то делал через СКД + своя расшифровка для интерактивных изменений с фиксацией в таблице значений или табличной части
вот кусочек с примером, как в событии расшифровки находил значения группировок (в вашем случае это будет Номенклатура и Магазин)

```1c
    &НаСервере
    Функция ПолучитьСтруктуруДанныхДляФормыРаспределения(Расшифровка)

    СтруктураПараметров = Неопределено;

    //при клике на колонке Отправить или Получить нужно показывать форму для распределения количества по текущему Номенклатура + Цвет
    _ДанныеРасшифровки = ПолучитьИзВременногоХранилища(ДанныеРасшифровки);
    ЭлементРасшифровки = _ДанныеРасшифровки.Элементы[Расшифровка];
    ПолеРасшифровки = ЭлементРасшифровки.ПолучитьПоля()[0];
    Если ТипЗнч(ЭлементРасшифровки) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля")
    И (ПолеРасшифровки.Поле = "Отправить" ИЛИ ПолеРасшифровки.Поле = "Получить" ИЛИ ПолеРасшифровки.Поле = "Будет" ИЛИ ПолеРасшифровки.Поле = "Продажи" ИЛИ ПолеРасшифровки.Поле = "Остаток") Тогда
    МассивПолейРасшифровки = Новый Массив;
    ЗаполнитьМассивГруппировок(Расшифровка, _ДанныеРасшифровки, МассивПолейРасшифровки);
    _Номенклатура = Неопределено;
    _Цвет = Неопределено;
    Для каждого ТекПоле из МассивПолейРасшифровки Цикл
    Если ТипЗнч(ТекПоле) = Тип("ЗначениеПоляРасшифровкиКомпоновкиДанных") Тогда
    Если ТекПоле.Поле = "Номенклатура" Тогда
    _Номенклатура = ТекПоле.Значение;
    ИначеЕсли ТекПоле.Поле = "Цвет" Тогда
    _Цвет = ТекПоле.Значение;
    КонецЕсли;
    КонецЕсли;
    КонецЦикла;
    Если НЕ ЗначениеЗаполнено(_Номенклатура) ИЛИ НЕ ЗначениеЗаполнено(_Цвет) Тогда
    //Сообщить("Для ввода заказа необходимо выбирать уровень Номенклатура + Цвет!");
    Иначе
    СтруктураПараметров = Новый Структура;
    СтруктураПараметров.Вставить("Номенклатура", _Номенклатура);
    СтруктураПараметров.Вставить("Цвет", _Цвет);
    ДозаполнитьСтруктуруДанныхДляФормыРаспределенияПоНастройкамО­тчета(СтруктураПараметров);
    КонецЕсли;
    КонецЕсли;

    Возврат СтруктураПараметров;

    КонецФункции

    &НаСервере
    Процедура ЗаполнитьМассивГруппировок(ТекРасшифровка, _ДанныеРасшифровки, МассивГруппировок)

    МассивРодителей = _ДанныеРасшифровки.Элементы[ТекРасшифровка].ПолучитьРодителей();
    Для Сч = 1 По МассивРодителей.Количество() Цикл

    ПолеРодитель = МассивРодителей[Сч-1];
    Если Число(ПолеРодитель.Идентификатор) > 0 Тогда

    //Получим текущее поле
    Если ТипЗнч(ПолеРодитель) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
    Поля = ПолеРодитель.ПолучитьПоля();
    ИначеЕсли ТипЗнч(ПолеРодитель) = Тип("ЭлементРасшифровкиКомпоновкиДанныхГруппировка") Тогда
    Поля = ПолеРодитель.ПолучитьРодителей()[0].ПолучитьПоля();
    КонецЕсли;

    //Выведем значения текущей расшифровки
    Для каждого Поле из Поля Цикл
    МассивГруппировок.Добавить(Поле);
    КонецЦикла;

    //Рекурсивный вызов процедуры.
    Родители = ПолеРодитель.ПолучитьРодителей();
    Для каждого ТекЭлемент из Родители Цикл
    РасшифровкиВыше = ТекЭлемент.Идентификатор;
    ЗаполнитьМассивГруппировок(РасшифровкиВыше, _ДанныеРасшифровки, МассивГруппировок);
    КонецЦикла;

    КонецЕсли;
    КонецЦикла;

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

но если такого типа задачу делаете в первый раз, то я бы рекомендовал способ, описанный в (7). хранение в табличной части, а на форме отображение через табличное поле
---
sommid, ИМХО, каждый что-то подобное рисовал :)

```1c
    Функция ПолучитьМассивРодительскихЭлементовРасшифровки(ЭлементРасшифровки) Экспорт

    МассивРодителей = Новый Массив;

    МассивПрямыхРодителей = ЭлементРасшифровки.ПолучитьРодителей();

    Для Каждого РодительскийЭлементРасшифровки Из МассивПрямыхРодителей Цикл
    МассивВышестоящихРодителей = ПолучитьМассивРодительскихЭлементовРасшифровки(РодительскийЭлементРасшифровки);
    Для Каждого ЭлементРасшифровкиВышестоящегоРодителя Из МассивВышестоящихРодителей Цикл
    Если ТипЗнч(ЭлементРасшифровкиВышестоящегоРодителя) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
    МассивРодителей.Добавить(ЭлементРасшифровкиВышестоящегоРодителя);
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;

    Для Каждого ПрямойРодитель Из МассивПрямыхРодителей Цикл
    Если ТипЗнч(ПрямойРодитель) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
    МассивРодителей.Добавить(ПрямойРодитель);
    КонецЕсли;
    КонецЦикла;

    Возврат МассивРодителей;

    КонецФункции

    Функция ПолучитьСтруктуруПолейРасшифровки(ЭлементРасшифровки) Экспорт

    МассивЭлементовРасшифровки = ПолучитьМассивРодительскихЭлементовРасшифровки(ЭлементРасшифровки);
    МассивЭлементовРасшифровки.Добавить(ЭлементРасшифровки);

    СтруктураПолей = Новый Структура;

    Для Каждого текЭлементРасшифровки Из МассивЭлементовРасшифровки Цикл

    Для Каждого текПоле Из текЭлементРасшифровки.ПолучитьПоля() Цикл

    СтруктураПолей.Вставить(текПоле.Поле, текПоле.Значение);

    КонецЦикла;

    КонецЦикла;

    Возврат СтруктураПолей;

    КонецФункции

    Функция ПолучитьПараметрыРасшифровываемогоПоля(ИндексЭлементаРасшифровки, АдресДанныхРасшифровкиВоВременномХранилище) Экспорт

    СтруктураРезультата = Новый Структура;

    ДанныеРасшифровкиОтчета = ПолучитьИзВременногоХранилища(АдресДанныхРасшифровкиВоВременномХранилище);

    ЭлементРасшифровки = ДанныеРасшифровкиОтчета.Элементы[ИндексЭлементаРасшифровки];

    ИмяРасшифровываемогоПоля = Неопределено;
    ПоляЭлементаРасшифровки = ЭлементРасшифровки.ПолучитьПоля();
    Если ПоляЭлементаРасшифровки.Количество() > 0 Тогда
    ИмяРасшифровываемогоПоля = ПоляЭлементаРасшифровки[0].Поле;
    КонецЕсли;

    СтруктураРезультата.Вставить("ИмяПоля", ИмяРасшифровываемогоПоля);
    СтруктураРезультата.Вставить("ПоляГруппировок", УправлениеОтчетами.ПолучитьСтруктуруПолейРасшифровки(ЭлементРасшифровки));

    Возврат СтруктураРезультата;

    КонецФункции
```

Меня давно тянет к решениям по реализации взаимодействия с пользователем прямо из отчета СКД. Такие решения гораздо более удобные и естественные в использовании, хотя и сложнее в реализации. Одна беда - если требуется перестройка отчета в некоторой области, то нет особых вариантов кроме полного переформирования отчета. А это сплошь и рядом дает лаги, которые сводят на нет всю идею улучшения юзабилити.
---
это да.. хотят же сразу видеть чего накликали, да еще и итог актуальный, а переформировать отчет занимает время. Для больших отчетов, через которые заказы делают забивал на итог, либо намеренно не делал обновление отчета, оставляя это на пользователя
---
sommid, Итоги как раз решаемо, в принципе. Я пытался маршрутизацию заказов по машинам в отчете сделать перетаскиванием мышкой (просто в отчете удобно было кучу другой сопутствующей инфы красиво показывать, заодно анализировать и тут же печатать) и все отлично получилось. Но время на переформирование убило идею на корню.
---
ну если не надо постоянно получать обновленные данные извне, а все получено первичным формированием + та информация, которую пользователь вносит..
я при первом формировании условно постоянную информацию помещал во временное хранилище и при повторном формировании уже оттуда брал по адресу - т.е. исключил длительное получение остатков и тормозную передачу клиент-сервер (изначально пробовал поместить в табличную часть.. это было хуже, чем заново получить данные из БД)
---
Два цикла надо организовать по строкам и столбцам. И этими циклами обойти Таблицу значений с результатом запроса, сложностей особых не должно возникнуть.
---
Пока склоняюсь к ДеревуЗначений на форме, т.к. нужна интерактивность. Галочки расставлять будут, сортировать, отборы делать, выделение цветом. Весь результат буду сохранять в табличную часть вот такого вида:
Гвозди - Магазин №1 - 100шт - 5000 руб - Магазин №2 - 200 шт- 3000 руб
Молоток - Магазин №1 - 6шт - 10000 руб - Магазин №2 - 7шт - 12000 руб

Как можно преобразовать потом это в дерево? Количество колонок то не известно, да еще и программно они создаются. А дерево должно быть с иерархией из справочника номенклатуры, ну или хотя бы по первому Родителю.
---
так проблема должна еще возникнуть на создании табличной части, как вы ее заранее создадите, если не знаете, сколько колонок будет?
Либо это уже не табличная часть будет, а таблица значений как реквизит формы.
Но мне кажется табличная часть ваша должна хранить только значимые данные, например, Номенклатура|Магазин|Количество|Пометка. А вот уже при отрисовке дерева учитывать иерархию и то, что у вас магазины должны идти колонками. На события изменения в дереве напишите свои обработчики, чтобы в нужные строки табчасти вносить изменения
---
Огромное спасибо за идеи. Думаю дальше сам додумаю, но если что спрошу у вас)) Предыдущий вопрос все еще актуальный :)
---
zlakizla, см. вложение. Пример для УТ 10.
В левой таблице можно задать номенклатуру для отбора. В правой - результат.
---
ditp, Спасибо, пригодится при оформлении группы колонок! Но сейчас проблема, как преобразовать таблицу значений типа:
Гвозди - Магазин №1 - 100шт - 5000 руб - Магазин №2 - 200 шт- 3000 руб
Молоток - Магазин №1 - 6шт - 10000 руб - Магазин №2 - 7шт - 12000 руб
в дерево значений с иерархией по Номенклатуре. Количество колонок может быть разное, так как магазинов может быть не 2 , а 5-10-20. Данная таблица значений получается путем склеивания строк запроса. Я ее и передать в запрос теперь не пойму как, ведь не известно сколько колонок будет, а в запросе надо четко задать структуру внешней таблицы.
---
zlakizla, ваша исходная таблица значений откуда берется? нужна ли она? можно ли сразу в запросе получить нужные параметры?
---
ditp, берется из запроса.
Результат запроса вот в таком виде:

Гвозди - Магазин №1 - 100шт - 5000 руб
Гвозди - Магазин №2 - 200шт - 3000 руб
Молоток - Магазин №1 - 6шт - 10000 руб
Молоток - Магазин №2 - 7шт - 12000 руб

Далее этот запрос обрабатывается в следующий вид:
Гвозди - Магазин №1 - 100шт - 5000 руб - Магазин №2 - 200 шт- 3000 руб
Молоток - Магазин №1 - 6шт - 10000 руб - Магазин №2 - 7шт - 12000 руб
Т.е. он склеивает строки с одной номенклатурой в одну длинную строку вида Магазин-Остаток-Сумма. Обрабатывается просто обходом результата запроса и склеиванием.
---
zlakizla, а зачем создавать ТЗ из запроса, что бы потом ее опять передать в запрос?
Не проще ли обойтись без этой промежуточной np? и сразу получить нужные данные в нужном виде?

Посмотрите файл из (17), там и колонки создаются нормально, и иерархия есть.
---
Может быть в цикле обойти колонки ТЗ и задать структуру внешней таблицы в запросе?

Запрос = "ВЫБРАТЬ
 | МояТЗ.Номенклатура КАК Номенклатура";

 Для каждого КолонкаТЗ Из МояТЗ.Колонки Цикл
  Запрос = Запрос + "| МояТЗ." + КолонкаТЗ.Имя;
 КонецЦикла;

 Запрос = Запрос + "
 |ИЗ
 | &МояТЗ КАК МояТЗ
 |ИТОГИ ПО
 | Номенклатура ТОЛЬКО ИЕРАРХИЯ"