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