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