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