Как вывести итоги по кварталам в печатной форме 1С при обходе выборки в цикле

Программист 1С v8.3 (Обычные формы)
← На главную

При разработке печатных форм в системе 1С:Предприятие часто возникает задача вывода промежуточных итогов (по месяцам, кварталам или группам товаров). Сложность заключается в том, что структура кода может быть жестко задана существующими общими процедурами, и внедрение механизмов типа СКД (Системы компоновки данных) требует полной переработки логики. В данной статье мы разберем, как грамотно встроить вывод итогов по кварталам в существующий цикл обхода выборки, используя классические алгоритмические приемы.

Анализ проблемы: почему итоги выводятся некорректно

Типичная ошибка при ручном формировании табличного документа — попытка вывести итоги вне контекста смены группировочного поля. Если мы просто добавим вывод секции итогов в конце цикла по документам, мы получим общие итоги, но не увидим разбивки внутри данных. Чтобы реализовать вывод после каждого квартала, нам необходимо научить программу определять момент, когда данные одного квартала закончились и начались данные другого.

Решение 1: Метод контрольного значения (алгоритм с флагом смены периода)

Рассмотрим наиболее универсальный способ, который не требует изменения структуры запроса (при условии, что данные в выборке уже отсортированы по периоду). Основная идея заключается в использовании вспомогательной переменной, которая хранит значение квартала из предыдущей итерации цикла.

Разберем по шагам логику реализации этого алгоритма:

  1. Инициализация переменных: Перед началом цикла необходимо создать переменные для накопления итогов (ДнейЗаКвартал, ЧасовЗаКвартал) и переменную для хранения текущего номера квартала.
  2. Проверка смены квартала: На каждой итерации цикла мы сравниваем номер квартала из текущей строки выборки с сохраненным значением.
  3. Вывод секции итогов: Если значения не совпадают, значит, начался новый квартал. В этот момент мы выводим область итогов для предыдущего квартала в ТабличныйДокумент и обнуляем накопительные счетчики.
  4. Накопление данных: Внутри каждой итерации прибавляем текущие значения к квартальным и годовым итогам.
  5. Вывод последнего блока: Это критически важный момент — после завершения цикла нужно еще раз принудительно вывести итоги, так как для последнего квартала условие смены значения не сработает.

Проанализируем пример кода, реализующий данный подход:


// Инициализируем переменные перед циклом
ПеременнаяТекущийКвартал = Неопределено;
ДнейЗаКвартал = 0;
ЧасовЗаКвартал = 0;

Пока Выборка.Следующий() Цикл
    
    // Проверяем, не сменился ли квартал (пропускаем проверку для самой первой записи)
    Если ПеременнаяТекущийКвартал <> Неопределено И ПеременнаяТекущийКвартал <> Выборка.КварталНомер Тогда
        // Выводим итоги за завершенный квартал
        ОбластьИтогоЗаКвартал.Параметры.ДнейЗаКвартал = ДнейЗаКвартал;
        ОбластьИтогоЗаКвартал.Параметры.ЧасовЗаКвартал = ЧасовЗаКвартал;
        ТабДок.Вывести(ОбластьИтогоЗаКвартал);
        
        // Обнуляем квартальные счетчики
        ДнейЗаКвартал = 0;
        ЧасовЗаКвартал = 0;
    КонецЕсли;
    
    // Запоминаем текущий квартал для следующего сравнения
    ПеременнаяТекущийКвартал = Выборка.КварталНомер;
    
    // Накапливаем данные
    ДнейЗаКвартал = ДнейЗаКвартал + Выборка.Дней;
    ЧасовЗаКвартал = ЧасовЗаКвартал + Выборка.Часов;
    
    // Вывод детальной строки
    // ... Заполнение параметров ОбластьСтрока ...
    ТабДок.Вывести(ОбластьСтрока);
    
КонецЦикла;

// ВАЖНО: Выводим итоги за самый последний квартал в выборке
Если ПеременнаяТекущийКвартал <> Неопределено Тогда
    ОбластьИтогоЗаКвартал.Параметры.ДнейЗаКвартал = ДнейЗаКвартал;
    ОбластьИтогоЗаКвартал.Параметры.ЧасовЗаКвартал = ЧасовЗаКвартал;
    ТабДок.Вывести(ОбластьИтогоЗаКвартал);
КонецЕсли;

Решение 2: Использование иерархической выборки (Обход по группировкам)

Если у нас есть возможность минимально подправить запрос, добавив в него конструкцию ИТОГИ ПО, мы можем использовать более «чистый» с точки зрения 1С подход — метод Выбрать с параметром ОбходРезультатаЗапроса.ПоГруппировкам.

Рассмотрим, как это упрощает код. В данном случае нам не нужно вручную сравнивать значения и обнулять переменные. Мы строим вложенные циклы: внешний цикл обходит группировки по кварталам, а внутренний — детальные записи внутри каждого квартала.

Посмотрим на пример реализации такого обхода:


// В запросе должно быть: ИТОГИ СУММА(Дни), СУММА(Часы) ПО КварталНомер
ВыборкаКвартал = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "КварталНомер");

Пока ВыборкаКвартал.Следующий() Цикл
    
    // Обходим детальные записи внутри квартала
    ВыборкаДетали = ВыборкаКвартал.Выбрать();
    Пока ВыборкаДетали.Следующий() Цикл
        // Выводим строки документа/месяца
        // ...
        ТабДок.Вывести(ОбластьДанных);
    КонецЦикла;
    
    // После завершения внутреннего цикла выводим итог квартала
    // Данные для итогов берутся сразу из ВыборкаКвартал
    ЗаполнитьЗначенияСвойств(ОбластьИтогоЗаКвартал.Параметры, ВыборкаКвартал);
    ТабДок.Вывести(ОбластьИтогоЗаКвартал);
    
КонецЦикла;

Важные технические нюансы

При программном выводе итогов следует обратить внимание на несколько критических моментов, которые часто упускают разработчики:

  1. Сортировка данных: Для корректной работы первого метода (сравнение с предыдущим значением) данные в Выборка обязательно должны быть отсортированы по полю КварталНомер. Если сортировки не будет, итоги будут выводиться хаотично при каждом изменении номера квартала в строке.
  2. Проверка вывода на страницу: Чтобы итог за квартал не «отрывался» от своих данных и не переносился на следующую страницу в одиночестве, рекомендуется использовать метод ТабДок.ПроверитьВывод(). Если секция итогов не помещается, лучше принудительно вставить разрыв страницы перед началом нового блока данных.
  3. Заполнение параметров: Если имена колонок в запросе и имена параметров в макете совпадают (например, ДнейЗаКвартал), используйте функцию ЗаполнитьЗначенияСвойств(). Это сократит количество строк кода и уменьшит вероятность опечатки.

Выясним причину, по которой автор вопроса столкнулся с трудностями: в его исходном коде циклы были разделены логикой обработки разных объектов (Документ и его Выборка), что мешало корректно вставить условие прерывания. Использование контрольной переменной, описанной в Решении 1, позволяет "склеить" эти сущности и выводить итоги именно там, где меняется значение квартала, независимо от того, к какому документу относятся записи.

Таким образом, мы проанализировали два основных подхода. Первый — максимально гибкий и встраивается в любой существующий код. Второй — более профессиональный и надежный, так как использует встроенные механизмы платформы 1С для работы с итогами. Выбор зависит от того, насколько сильно вы ограничены в изменении исходного запроса.

← На главную