Как реализовать эффективную пакетную печать документов во внешней печатной форме в 1С:УПП

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

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

Постановка задачи и основная проблема

Рассмотрим стандартную ситуацию: у нас есть внешняя печатная форма, которая отлично работает из карточки документа. Однако при попытке напечатать список документов из внешней обработки или реестра, разработчики часто допускают ошибку, вызывая функцию печати в цикле для каждого документа по отдельности. Это приводит к тому, что на каждый документ порождается отдельное задание на принтер, многократно выполняется один и тот же запрос к базе данных и инициализируются макеты. Наша цель — реализовать один общий запрос ко всем выбранным объектам и сформировать единый ТабличныйДокумент.

Оптимизация запроса через массив ссылок

Проанализируем ключевой технический нюанс: как передать список документов в запрос. Вместо того чтобы ограничивать выборку одной ссылкой через условие ГДЕ Ссылка = &Ссылка, мы должны использовать массив. Рассмотрим, как это выглядит на практике.

Когда мы работаем с пакетом документов, в функцию печати передается параметр МассивОбъектов. Разберем по шагам, как правильно подготовить данные для выборки:


// В функции Печать модуля внешней печатной формы
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   РеализацияТоваровУслуг.Ссылка КАК Ссылка,
    |   РеализацияТоваровУслуг.Номер КАК Номер,
    |   РеализацияТоваровУслуг.Дата КАК Дата,
    |   РеализацияТоваровУслуг.Контрагент КАК Контрагент
    |ИЗ
    |   Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
    |ГДЕ
    |   РеализацияТоваровУслуг.Ссылка В (&МассивСсылок)
    |УПОРЯДОЧИТЬ ПО
    |   Ссылка";

Запрос.УстановитьПараметр("МассивСсылок", МассивОбъектов);
РезультатЗапроса = Запрос.Выполнить();

Важно помнить, что если объем печатаемых документов исчисляется сотнями, использование оператора В (&МассивСсылок) может быть не самым оптимальным решением для СУБД. В таких случаях мы рекомендуем использовать временные таблицы. Проанализируем ситуацию: передача массива во временную таблицу позволяет SQL-серверу более эффективно построить план запроса и выполнить соединение с основными таблицами данных.

Использование временных таблиц для больших объемов данных

Рассмотрим подробнее схему работы с временными таблицами. Это гарантирует стабильную работу при любых объемах выборки. Выясним причину: временная таблица индексируется, что ускоряет доступ к полям документов.


МенеджерВТ = Новый МенеджерВременныхТаблиц;
ЗапросВТ = Новый Запрос;
ЗапросВТ.МенеджерВременныхТаблиц = МенеджерВТ;

// Создаем таблицу из массива ссылок
ТЗ_Ссылок = Новый ТаблицаЗначений;
ТЗ_Ссылок.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("ДокументСсылка.РеализацияТоваровУслуг"));
Для Каждого СсылкаИзМассива Из МассивОбъектов Цикл
    НоваяСтрока = ТЗ_Ссылок.Добавить();
    НоваяСтрока.Ссылка = СсылкаИзМассива;
КонецЦикла;

ЗапросВТ.Текст = 
    "ВЫБРАТЬ
    |   ТЗ.Ссылка КАК Ссылка
    |ПОМЕСТИТЬ ВТ_Объекты
    |ИЗ
    |   &ТЗ КАК ТЗ
    |;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   Реализация.Ссылка КАК Ссылка,
    |   Реализация.Номер КАК Номер,
    |   Реализация.Товары.(
    |       Номенклатура,
    |       Количество,
    |       Цена
    |   )
    |ИЗ
    |   Документ.РеализацияТоваровУслуг КАК Реализация
    |ГДЕ
    |   Реализация.Ссылка В (ВЫБРАТЬ Ссылка ИЗ ВТ_Объекты)";

ЗапросВТ.УстановитьПараметр("ТЗ", ТЗ_Ссылок);
РезультатЗапроса = ЗапросВТ.Выполнить();

Формирование единого табличного документа

Теперь разберем процесс вывода данных. Для реализации пакетной печати крайне важно один раз получить макет через метод ПолучитьМакет() до начала цикла обхода результата запроса. Это существенно экономит ресурсы процессора. Посмотрим на алгоритм вывода областей:

  1. Создаем основной ТабличныйДокумент.
  2. Получаем макет печатной формы.
  3. Запускаем цикл по выборке из результата запроса.
  4. Внутри цикла проверяем смену ссылки на документ.
  5. Выводим области (Шапка, Строки, Подвал).
  6. Критически важный шаг: вставляем разделитель страниц между документами.

ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("Макет");
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");

ВыборкаДокументов = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

ПервыйДокумент = Истина;

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

Возврат ТабДок;

Особенности работы в УПП и работа с разделителями страниц

В конфигурации Управление производственным предприятием механизмы печати часто завязаны на общие модули, такие как УниверсальныеМеханизмы. При написании внешней печатной формы необходимо следить за тем, чтобы ваша функция Печать() возвращала именно заполненный ТабличныйДокумент.

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

Дополнительные параметры: принтер и двусторонняя печать

При массовой печати часто требуется программная настройка параметров вывода. Рассмотрим наиболее полезные свойства объекта ТабличныйДокумент:


ТабДок.ДвусторонняяПечать = ТипДвустороннейПечати.ПереворотСлева;
ТабДок.ИмяПринтера = "HP_LaserJet_Office_505";
ТабДок.АвтоМасштаб = Истина;

Заключение

Мы выяснили, что пакетная печать — это не просто вызов одной и той же функции в цикле. Это архитектурное изменение подхода к выборке данных. Использование массивов, временных таблиц и правильная работа с горизонтальными разделителями страниц позволяют создавать профессиональные инструменты для пользователей УПП, которые экономят время и ресурсы сервера. Помните о важности выноса ПолучитьМакет() за пределы циклов и всегда проверяйте тип передаваемого параметра в функцию печати, чтобы обеспечить универсальность кода как для одиночной, так и для групповой обработки документов.

← На главную