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