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

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

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

Анализ основной проблемы: почему данные «рассовываются» неверно

Рассмотрим ситуацию, описанную автором темы. В запросе используются итоги только по полю ВидНоменклатуры. При печати одного документа это работает, но при групповой печати (когда в массив Ссылка передано несколько документов) запрос собирает все строки из всех документов и группирует их только по виду (Товары или Услуги). В результате товары из Документа №1 и Документа №2 оказываются в одной таблице, что недопустимо.

Чтобы решить эту проблему, мы должны выстроить четкую иерархию в секции ИТОГИ запроса. Групповая печать всегда требует, чтобы верхним уровнем группировки была сама Ссылка на документ. Только в этом случае система сможет отделить данные одного экземпляра от другого.

Корректная структура запроса для групповой печати

Проанализируем, как должен выглядеть текст запроса. Нам необходимо получить данные шапки и табличной части, сгруппированные сначала по документу, а затем по категории номенклатуры. Для этого используем конструкцию ИТОГИ ПО:


ВЫБРАТЬ
    ОказаниеУслуги.Ссылка КАК Ссылка,
    ОказаниеУслуги.Номер КАК Номер,
    ОказаниеУслуги.Дата КАК Дата,
    ОказаниеУслугиПереченьНоменклатуры.Номенклатура КАК Номенклатура,
    ОказаниеУслугиПереченьНоменклатуры.Количество КАК Количество,
    ОказаниеУслугиПереченьНоменклатуры.Цена КАК Цена,
    ОказаниеУслугиПереченьНоменклатуры.Сумма КАК Сумма,
    ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
ИЗ
    Документ.ОказаниеУслуги КАК ОказаниеУслуги
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры
        ПО ОказаниеУслуги.Ссылка = ОказаниеУслугиПереченьНоменклатуры.Ссылка
ГДЕ
    ОказаниеУслуги.Ссылка В(&Ссылка)
УПОРЯДОЧИТЬ ПО
    Ссылка,
    ВидНоменклатуры
ИТОГИ ПО
    Ссылка,
    ВидНоменклатуры

Такая структура позволяет нам последовательно обходить результат: сначала переключаться между документами, затем внутри каждого документа переключаться между разделами «Товары» и «Услуги».

Алгоритм вывода данных и управление разделителем страниц

Одной из главных жалоб автора была некорректная работа метода ВставлятьРазделительСтраниц. В 1С для этих целей лучше всего использовать прямой вызов метода ТабДок.ВывестиГоризонтальныйРазделительСтраниц(). Однако, если просто вызывать его в конце цикла, мы получим лишний пустой лист в конце печати. Рассмотрим правильный алгоритм с использованием флага ПервыйДокумент.

Выясним причину, по которой разделитель «капризничает»: он должен выводиться ПЕРЕД вторым и последующими документами, но не перед первым. Посмотрим, как реализовать это в коде:


Процедура Печать(ТабДок, Ссылка) Экспорт
    
    Макет = Документы.ОказаниеУслуги.ПолучитьМакет("Печать");
    Запрос = Новый Запрос;
    // ... (текст запроса с итогами по Ссылка и ВидНоменклатуры) ...
    Запрос.Параметры.Вставить("Ссылка", Ссылка);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Обход самого верхнего уровня — Документы
    ВыборкаДокумент = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    
    ПервыйДокумент = Истина;
    
    Пока ВыборкаДокумент.Следующий() Цикл
        
        // Если это не первый документ в пачке, добавляем разрыв страницы
        Если НЕ ПервыйДокумент Тогда
            ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
        КонецЕсли;
        ПервыйДокумент = Ложь;
        
        // Выводим заголовок и шапку конкретного документа
        ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
        ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
        
        ОбластьШапка.Параметры.Заполнить(ВыборкаДокумент);
        ТабДок.Вывести(ОбластьЗаголовок);
        ТабДок.Вывести(ОбластьШапка);
        
        // Вложенный обход по Видам Номенклатуры (Товары / Услуги)
        ВыборкаВид = ВыборкаДокумент.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
        
        Пока ВыборкаВид.Следующий() Цикл
            
            Если ВыборкаВид.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Товар Тогда
                // Выводим секцию товаров
                ТабДок.Вывести(Макет.ПолучитьОбласть("ШапкаТовары"));
                
                ВыборкаСтрок = ВыборкаВид.Выбрать();
                СуммаИтого = 0;
                Пока ВыборкаСтрок.Следующий() Цикл
                    ОбластьСтрока = Макет.ПолучитьОбласть("НоменклатураТовары");
                    ОбластьСтрока.Параметры.Заполнить(ВыборкаСтрок);
                    ТабДок.Вывести(ОбластьСтрока);
                    СуммаИтого = СуммаИтого + ВыборкаСтрок.Сумма;
                КонецЦикла;
                
                ОбластьИтог = Макет.ПолучитьОбласть("ВсегоТовары");
                ОбластьИтог.Параметры.ВсегоПоТоварам = СуммаИтого;
                ТабДок.Вывести(ОбластьИтог);
                
            ИначеЕсли ВыборкаВид.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Услуга Тогда
                // Выводим секцию услуг аналогичным образом
                // ...
            КонецЕсли;
            
        КонецЦикла;
        
    КонецЦикла;

КонецПроцедуры

Важные замечания по методам работы с Табличным Документом

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

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

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

← На главную