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