При работе с массовой печатью документов в конфигурациях на базе 1С:Предприятие 8.3, таких как ЗУП 3.1, программисты часто сталкиваются с задачей оптимизации вывода. Одной из типичных проблем является корректная настройка двусторонней печати для комплектов документов (например, табелей учета рабочего времени, расчетных листков или приказов). Если просто объединить все документы в один большой ТабличныйДокумент, то при включении опции двусторонней печати в настройках принтера система начнет печатать начало второго документа на обратной стороне последнего листа первого документа. Это недопустимо с точки зрения документооборота.
В этой статье мы подробно разберем, как решить эту задачу несколькими способами, проанализируем их преимущества и недостатки, а также рассмотрим конкретные примеры кода.
Прежде всего выясним причину такого поведения системы. Когда мы выводим множество документов в один объект типа ТабличныйДокумент, принтер воспринимает его как единое задание и единую непрерывную последовательность страниц. Драйверу принтера «все равно», где заканчивается один приказ и начинается другой — он просто заполняет лицевую и оборотную стороны листов по порядку. Чтобы заставить принтер начать новый документ с нового листа, нам нужно либо программно «добить» предыдущий документ до четного количества страниц, либо отправлять каждый документ как отдельное задание на печать.
Рассмотрим наиболее технологичный и правильный способ решения проблемы — использование системного объекта ПакетОтображаемыхДокументов. Этот механизм специально предназначен для объединения нескольких табличных документов в одну очередь печати, сохраняя при этом их логическую обособленность.
Разберем алгоритм работы с пакетом по шагам. Основная сложность здесь заключается в том, что формирование документов происходит на сервере, а объект ПакетОтображаемыхДокументов и команда печати должны отрабатывать на клиенте. Для передачи данных мы будем использовать временное хранилище.
Проанализируем пример кода для серверной части:
// Функция на сервере, которая формирует массив адресов во временном хранилище
&НаСервере
Функция СформироватьПакетДокументовНаСервере()
МассивАдресов = Новый Массив;
СписокДокументов = ПолучитьСписокНужныхДокументов(); // Ваш запрос к БД
Для Каждого ТекущийДокумент Из СписокДокументов Цикл
// Формируем печатную форму для каждого документа
ТабДок = СформироватьПечатнуюФорму(ТекущийДокумент);
// Устанавливаем параметры страницы, чтобы избежать разъезжания верстки
ТабДок.АвтоМасштаб = Истина;
ТабДок.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
// Помещаем сформированный документ во временное хранилище
Адрес = ПоместитьВоВременноеХранилище(ТабДок, ЭтаФорма.УникальныйИдентификатор);
МассивАдресов.Добавить(Адрес);
КонецЦикла;
Возврат МассивАдресов;
КонецФункции
Теперь рассмотрим, как собрать эти данные в пакет на стороне клиента и отправить на печать:
&НаКлиенте
Процедура НапечататьПакет(Команда)
МассивАдресов = СформироватьПакетДокументовНаСервере();
Пакет = Новый ПакетОтображаемыхДокументов;
Для Каждого Адрес Из МассивАдресов Цикл
ЭлементПакета = Пакет.Состав.Добавить();
ЭлементПакета.Данные = Адрес;
КонецЦикла;
// Выводим пакет на печать.
// Большинство драйверов при печати пакета начинают каждый элемент с нового листа.
Пакет.Напечатать(РежимИспользованияДиалогаПечати.Использовать);
КонецПроцедуры
Важный момент: использование объекта ПакетОтображаемыхДокументов позволяет избежать «ручного» подсчета страниц. Современные драйверы принтеров воспринимают элементы состава пакета как разделы задания, что автоматически решает проблему двусторонней печати.
Если по каким-то причинам использование пакетов невозможно (например, требуется строгая склейка в один файл для выгрузки в PDF), рассмотрим другой вариант. Мы можем продолжить использовать один ТабличныйДокумент, но будем проверять количество страниц после вывода каждого логического блока (документа).
Проанализируем ситуацию: если документ занимает 1, 3 или 5 страниц (нечетное количество), то следующий за ним документ начнется на «спине» последнего листа. Следовательно, нам нужно добавить пустую страницу-заглушку.
Посмотрим на пример реализации этой логики:
ОбщийТабДок = Новый ТабличныйДокумент;
Для Каждого Док Из МассивДокументов Цикл
ТекущийТабДок = СформироватьПечатнуюФорму(Док);
// Выводим текущий документ в общий
ОбщийТабДок.Вывести(ТекущийТабДок);
// Проверяем количество страниц в общем документе
// Внимание: метод КоличествоСтраниц() может работать медленно на больших объемах
Если ОбщийТабДок.КоличествоСтраниц() % 2 <> 0 Тогда
// Если страниц нечетное количество, принудительно вставляем разрыв
ОбщийТабДок.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
КонецЦикла;
Важное предостережение: Метод КоличествоСтраниц() крайне чувствителен к настройкам принтера по умолчанию, установленным в сессии пользователя. Если масштаб документа или поля изменятся в последний момент перед печатью, расчет может оказаться неверным. Чтобы минимизировать риски, всегда явно задавайте параметры АвтоМасштаб, ПолеСверху, ПолеСнизу и другие свойства страницы перед вызовом этого метода.
В современных конфигурациях, таких как ЗУП 3.1 или ERP 2.5, задача печати комплектов уже решена разработчиками фирмы «1С» в рамках БСП. Вместо изобретения велосипеда, мы рекомендуем использовать стандартную подсистему «Печать».
Рассмотрим, как это работает. БСП предоставляет программный интерфейс, позволяющий передать массив объектов на печать. Система сама сформирует коллекцию табличных документов и корректно отправит их на принтер. Для этого используется метод УправлениеПечатьюКлиент.ВыполнитьКомандуПечати() или более низкоуровневый УправлениеПечатьюКлиент.ПечатьДокументов().
Основные преимущества использования БСП:
Разберем еще один метод, который часто называют «методом грубой силы». Если программисту сложно работать с пакетами или временными хранилищами, можно отправить каждый документ на печать по отдельности прямо из цикла на клиенте.
Выясним, как это выглядит в коде:
&НаКлиенте
Процедура ПечатьПоОчереди(МассивТабДоков)
Для Каждого ТабДок Из МассивТабДоков Цикл
// Метод Напечатать() без параметров выводит диалог,
// с параметром Неиспользовать — сразу отправляет в очередь.
ТабДок.Напечатать(РежимИспользованияДиалогаПечати.Неиспользовать);
КонецЦикла;
КонецПроцедуры
Этот способ гарантирует, что каждый документ начнется с нового листа, так как для ОС Windows это будут разные задания печати. Однако у этого метода есть существенный недостаток: если документов 50, то в очереди печати принтера появится 50 отдельных заданий. Это может замедлить работу принтера (паузы между заданиями) и создаст неудобства, если печать нужно будет экстренно отменить.
Выберем оптимальный подход в зависимости от условий задачи:
При реализации любого из этих методов помните о производительности. Формирование 500 страниц — это ресурсоемкая операция. Рассмотрим несколько советов:
ТабличныйДокумент с сервера на клиент напрямую (через параметры функции) происходит сериализация, которая может «отъесть» много оперативной памяти. Передача через ПоместитьВоВременноеХранилище работает эффективнее.Подводя итог, можно сказать, что наиболее современным и надежным способом реализации двусторонней печати комплекта документов в 1С является использование ПакетОтображаемыхДокументов или встроенных функций БСП. Эти методы позволяют соблюсти требования к оформлению документов и обеспечивают комфортную работу пользователя с очередью печати.