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