При работе с управляемыми формами в 1С часто возникает задача вывести данные из запроса с итогами в элемент формы типа ДеревоЗначений. Стандартная выгрузка результата запроса с группировками приводит к тому, что значение группировочного поля повторяется в каждой дочерней строке. Это выглядит неаккуратно и затрудняет чтение данных. Рассмотрим, как добиться классического древовидного представления, где значение группировки отображается только один раз в родительской строке.
Исходная ситуация: у нас есть запрос с итогами, который мы выгружаем на форму одной строкой кода.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док1.Справочник1 КАК Справочник1,
| Док1.Справочник2 КАК Справочник2,
| Док1.Количество КАК Количество,
| Док1.Цена КАК Цена,
| Док1.Сумма КАК Сумма
|ИЗ
| Документ.Док1.ТабЧасть КАК Док1
|ГДЕ
| Док1.Ссылка = &Ссылка
|ИТОГИ
| СУММА(Количество),
| СУММА(Сумма)
|ПО
| Справочник1";
Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
ДанныеДляДерева = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
ЗначениеВРеквизитФормы(ДанныеДляДерева, "ДеревоНаФорме");
В результате поле Справочник1 заполнено и для группировочных, и для детальных строк. Наша цель — очистить это поле в дочерних строках для наглядности.
Самый гибкий и контролируемый способ — это отказаться от прямой выгрузки через ЗначениеВРеквизитФормы и заполнить дерево на форме вручную, перебирая результат запроса в цикле. Это позволяет полностью управлять структурой и данными на каждом уровне иерархии.
Сначала выгружаем данные как есть, а затем вторым циклом "очищаем" лишние значения в дочерних строках. Этот метод прост для понимания, но может быть неоптимальным на больших объемах данных из-за двойной обработки.
Пошаговый алгоритм:
ДеревоЗначений.Справочник1) в Неопределено.Пример кода:
// Выполняем запрос и выгружаем с группировками (без иерархии)
ДанныеДляДерева = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
// Перебираем полученное дерево и очищаем значения в детальных строках
Для каждого Группировка Из ДанныеДляДерева.Строки Цикл
Для каждого ДетальнаяЗапись Из Группировка.Строки Цикл
ДетальнаяЗапись.Справочник1 = Неопределено;
КонецЦикла;
КонецЦикла;
// Отправляем готовое дерево в реквизит формы
ЗначениеВРеквизитФормы(ДанныеДляДерева, "ДеревоНаФорме");
Этот подход более производительный, так как мы не создаем промежуточных структур данных, а сразу формируем итоговое дерево в нужном виде, обходя выборку из результата запроса.
Пошаговый алгоритм:
ДеревоЗначений и очищаем его.ПоГруппировкам.ЗаполнитьЗначенияСвойств.Справочник1). Это ключевой момент данного метода.Пример кода:
// Получаем дерево с формы и очищаем его
ДанныеДляДерева = РеквизитФормыВЗначение("ДеревоНаФорме");
ДанныеДляДерева.Строки.Очистить();
// Выполняем запрос и получаем выборку по группировкам
ВыборкаПоГруппировкам = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоГруппировкам.Следующий() Цикл
// Добавляем строку верхнего уровня (группу)
ГруппаДерева = ДанныеДляДерева.Строки.Добавить();
ЗаполнитьЗначенияСвойств(ГруппаДерева, ВыборкаПоГруппировкам);
// Получаем выборку вложенных детальных записей
ВыборкаДетальныеЗаписи = ВыборкаПоГруппировкам.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Добавляем дочернюю строку
ДетальнаяЗапись = ГруппаДерева.Строки.Добавить();
// Заполняем ее свойства, пропуская поле "Справочник1"
ЗаполнитьЗначенияСвойств(ДетальнаяЗапись, ВыборкаДетальныеЗаписи, , "Справочник1");
КонецЦикла;
КонецЦикла;
// Возвращаем заполненное дерево на форму
ЗначениеВРеквизитФормы(ДанныеДляДерева, "ДеревоНаФорме");
Наиболее элегантный и производительный способ — подготовить данные в нужном виде еще на этапе выполнения запроса. Таким образом, мы избегаем циклов в коде 1С, перекладывая всю работу по формированию иерархии на СУБД. Для этого используются конструкции ВЫБОР КОГДА и ЕСТЬNULL в секции ИТОГИ.
Идея в том, что для итоговых (группировочных) строк поля, которые не агрегируются и не входят в группировку, имеют значение NULL. Мы можем использовать это условие, чтобы подставлять разные значения для разных уровней иерархии.
Пример сложного запроса:
В этом примере мы объединяем два поля (Заказ и Реализация) в одно поле ДокументТовар. Для строки группировки по заказу в это поле попадет сам заказ, а для детальной строки — документ реализации.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВтЗаказы.Заказ КАК Заказ,
| РеализацияТоваровУслугТовары.Ссылка КАК Реализация,
| РеализацияТоваровУслугТовары.Цена КАК Цена,
| РеализацияТоваровУслугТовары.Ссылка.Менеджер КАК Менеджер,
| РеализацияТоваровУслугТовары.Количество КАК Количество,
| РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
| РеализацияТоваровУслугТовары.Сумма КАК Сумма
|ИЗ
| // ... (здесь может быть временная таблица ВтЗаказы или прямое обращение)
|ИТОГИ
| СУММА(Количество),
| СУММА(Сумма),
| // Ключевая конструкция: если Реализация = NULL (это группировка по Заказу),
| // то в поле ДокументТовар подставляем Заказ, иначе - Реализацию.
| ЕСТЬNULL(Реализация, Заказ) КАК ДокументТовар,
| ВЫБОР
| КОГДА Реализация ЕСТЬ NULL
| ТОГДА ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
| ИНАЧЕ МАКСИМУМ(Менеджер)
| КОНЕЦ КАК Менеджер
|ПО
| Заказ,
| Реализация";
// После выполнения такого запроса результат можно сразу выгружать на форму
Результат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
ЗначениеВРеквизитФормы(Результат, "Дерево");
Этот метод требует более глубокого понимания языка запросов, но является самым эффективным. Для интерактивной отладки запросов с секцией итогов и проверки постобработки данных можно использовать консоль запросов для управляемых форм.
Часто требуется, чтобы длинное наименование в группировочной строке не обрезалось границей колонки, а занимало все доступное пространство. Стандартное дерево значений не поддерживает объединение ячеек, но этого эффекта можно достичь с помощью условного оформления.
Алгоритм решения:
ЭтоГруппа с типом Булево.ЭтоГруппа = Истина, а для дочерних — Ложь.ДеревоНаФорме.ЭтоГруппа = ИстинаСправочник1), установите свойство Отображать в значение Ложь. Можно также дополнительно выделить группировку, например, жирным шрифтом.В результате для строк, где ЭтоГруппа = Истина, все ячейки, кроме первой, будут скрыты, и текст из первой ячейки визуально займет всю ширину строки. Для быстрого переноса настроенных правил в код подойдёт обработка для декомпиляции условного оформления формы.
В процессе работы с деревом значений можно столкнуться с типичными ошибками. Разберем две из них.
Проблема: При выполнении кода Группировка = ДанныеДляДерева.Строки.Добавить() возникает ошибка "Несоответствие типов".
Причина: Чаще всего это происходит, когда имя переменной (в данном случае Группировка) совпадает с зарезервированным словом системы или именем реквизита самой формы. Система пытается интерпретировать это имя не как переменную, а как объект другого типа, что и вызывает конфликт.
Решение: Используйте уникальные имена для переменных. Например, переименуйте Группировка в НоваяСтрокаГруппы или ГруппаДерева. Это простое действие немедленно решает проблему.
Проблема: Выполнение ЗначениеВРеквизитФормы(ДанныеДляДерева, "ДеревоНаФорме") завершается ошибкой "Ошибка при вызове метода контекста (ЗначениеВРеквизитФормы) по причине: Недопустимое значение параметра (параметр номер '2')".
Причина: Эта ошибка однозначно указывает на несоответствие структуры дерева-источника (ДанныеДляДерева) и дерева-приемника (реквизита формы ДеревоНаФорме).
Решение: Тщательно проверьте:
КАК Справочник1) соответствуют именам колонок, созданных в конфигураторе для реквизита ДеревоНаФорме.Самый частый случай — опечатка в имени колонки или использование разных псевдонимов в запросе и имен в реквизите формы.