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