Как программно вывести макет на печать из обработки в 1С:Предприятие

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

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

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

Анализ проблемы и разбор типичных ошибок

Рассмотрим исходную задачу. Разработчик хочет выбрать элементы справочника ХХ_Закупки_Materials, у которых реквизит ВыводитьНаПечать имеет значение ИСТИНА, и распечатать для них макет с именем Макет123.

В ходе обсуждения на форуме участники столкнулись с классическими ошибками проектирования кода. Посмотрим на ошибочный вариант кода, который часто пишут новички:


// ОШИБОЧНЫЙ КОД! НЕ ИСПОЛЬЗОВАТЬ В РЕАЛЬНЫХ ПРОЕКТАХ!
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	ХХ_Закупки_Материалы.ВыводитьНаПечать КАК ВыводитьНаПечать
|ИЗ
|	Справочник.ХХ_Закупки_Материалы КАК ХХ_Закупки_Материалы";
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса = Запрос.Выполнить().Выбрать() Тогда
	РезультатЗапроса.Следующий();
	Объект = РезультатЗапроса.Ссылка.ПолучитьОбъект();
	Объект.Печать("Макет123");
КонецЕсли;

Выясним, почему этот код не будет работать и какие архитектурные ошибки он содержит:

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

Решение проблемы: Базовый рабочий вариант

Для исправления вышеописанных ошибок участники форума предложили рабочий вариант кода. Разберем его структуру подробнее:


Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	ХХ_Закупки_Материалы.ВыводитьНаПечать КАК ВыводитьНаПечать,
|	ХХ_Закупки_Материалы.Ссылка КАК Ссылка
|ИЗ
|	Справочник.ХХ_Закупки_Материалы КАК ХХ_Закупки_Материалы";

РезультатЗапроса = Запрос.Выполнить().Выбрать();
Пока РезультатЗапроса.Следующий() Цикл
	Если РезультатЗапроса.ВыводитьНаПечать Тогда
		Объект = РезультатЗапроса.Ссылка.ПолучитьОбъект();
		Объект.Печать("Макет123");
	КонецЕсли;
КонецЦикла;

Этот код является полностью рабочим, однако его можно и нужно существенно оптимизировать.

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

Проанализируем ситуацию: зачем выбирать из базы данных абсолютно все элементы справочника и проверять флаг ВыводитьНаПечать на клиенте или сервере 1С, если фильтрацию можно переложить на СУБД? Перенос условий в секцию ГДЕ запроса — это базовое правило оптимизации производительности СУБД.

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

Рассмотрим оптимизированный вариант программного кода:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	Материалы.Ссылка КАК Ссылка
|ИЗ
|	Справочник.ХХ_Закупки_Материалы КАК Материалы
|ГДЕ
|	Материалы.ВыводитьНаПечать = ИСТИНА";

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	// Вызываем процедуру печати, передавая ссылку на элемент справочника
	// Сама процедура печати должна быть определена в модуле менеджера или объекта справочника
	Выборка.Ссылка.Печать("Макет123");
КонецЦикла;

Пакетная печать вместо печати в цикле

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

Лучшая практика 1С предписывает собирать все ссылки, которые подлежат выводу на печать, в единый массив, а затем передавать этот массив в процедуру печати один раз. Процедура печати вернет один общий табличный документ, где каждый элемент будет расположен на новой странице.

Разберем, как реализовать пакетный сбор данных и вывод на печать:


// Шаг 1. Собираем ссылки на элементы, которые нужно распечатать
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	Материалы.Ссылка КАК Ссылка
|ИЗ
|	Справочник.ХХ_Закупки_Материалы КАК Материалы
|ГДЕ
|	Материалы.ВыводитьНаПечать = ИСТИНА";

СписокОбъектов = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	СписокОбъектов.Добавить(Выборка.Ссылка);
КонецЦикла;

// Шаг 2. Если массив не пустой, отправляем его на печать
Если СписокОбъектов.Количество() > 0 Тогда
	// Вызываем процедуру формирования печатных форм
	ТабличныйДокумент = Справочники.ХХ_Закупки_Материалы.СформироватьПечатнуюФорму(СписокОбъектов, "Макет123");
	
	// Выводим полученный документ пользователю
	ТабличныйДокумент.Показать("Печать материалов");
КонецЕсли;

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

Чтобы понять, как сформировать и вернуть табличный документ (объект типа ТабличныйДокумент), заглянем "под капот" процедуры формирования печати. Разберем по шагам классический алгоритм заполнения макета:

  1. Инициализация нового табличного документа: ТабДок = Новый ТабличныйДокумент;
  2. Получение структуры макета: Макет = ПолучитьМакет("Макет123"); (или из менеджера справочника).
  3. Определение областей вывода (например, "Шапка", "Строка", "Подвал").
  4. Заполнение параметров областей макета данными из базы.
  5. Вывод заполненных областей в итоговый табличный документ с помощью метода Вывести().

Посмотрим на пример реализации такой функции в модуле менеджера справочника ХХ_Закупки_Материалы:


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

Интеграция с Библиотекой стандартных подсистем (БСП)

В современных конфигурациях 1С (таких как ERP, КА, УТ, БП) механизмы печати стандартизированы. Рекомендуется подключать подобные обработки в качестве Внешних печатных форм (ВПФ) через подсистему "Дополнительные отчеты и обработки".

Это дает массу преимуществ: пользователям становятся доступны функции отправки сформированного документа по электронной почте, сохранения в форматы PDF/Excel, редактирования макета "на лету" без изменения конфигурации.

Для создания полноценной внешней печатной формы, совместимой с БСП, в модуле объекта внешней обработки необходимо разместить две обязательные экспортные процедуры:


// Функция регистрирует обработку в справочнике "Дополнительные отчеты и обработки"
Функция СведенияОВнешнейОбработке() Экспорт
	
	ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("3.1.2.0");
	
	ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
	ПараметрыРегистрации.Назначение.Добавить("Справочник.ХХ_Закупки_Материалы");
	ПараметрыРегистрации.Наименование = "Печать материалов (пакетная)";
	ПараметрыРегистрации.Версия = "1.0";
	ПараметрыРегистрации.БезопасныйРежим = Истина;
	
	НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
	НоваяКоманда.Представление = "Распечатать отмеченные макеты";
	НоваяКоманда.Идентификатор = "Макет123";
	НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
	НоваяКоманда.Модификатор = "ПечатьMXL";
	
	Возврат ПараметрыРегистрации;
	
КонецФункции

// Процедура вызывается БСП при нажатии пользователем кнопки печати
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
	
	ПараметрыВывода.ДоступнаПечатьПоНоминалу = Истина;
	
	// Проверяем, нужно ли выводить наш макет
	Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "Макет123") Тогда
		
		// Формируем табличный документ
		ТабличныйДокумент = СформироватьПечатнуюФорму(МассивОбъектов, "Макет123");
		
		// Регистрируем результат в БСП
		УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
			КоллекцияПечатныхФорм, 
			"Макет123", 
			"Печатная форма материалов", 
			ТабличныйДокумент);
			
	КонецЕсли;
	
КонецПроцедуры

Полезные советы по отладке внешних обработок печати

Разработчики часто сталкиваются с проблемой: точки останова в модуле объекта подключенной ВПФ не срабатывают при вызове печати из интерфейса программы. Это происходит потому, что БСП загружает код обработки в безопасном режиме во временное хранилище, и отладчик теряет связь с исходным файлом .epf.

Рекомендуем использовать следующие методы для упрощения процесса отладки:

  1. Использование тестовой формы: Создайте внутри вашей внешней обработки обычную форму. Разместите на ней поле ввода для выбора ссылки на элемент справочника и кнопку "Выполнить тест". В обработчике нажатия кнопки пропишите прямой вызов процедур печати. Запустив обработку через Файл -> Открыть, вы сможете легко отлаживать код построчно.
  2. Использование внешних обработок отладки ВПФ: В сообществе разработчиков популярны готовые универсальные обработки для тестирования внешних печатных форм. Они позволяют загрузить ваш файл .epf, сымитировать передачу параметров из БСП и запустить печать с возможностью полноценной отладки в Конфигураторе.
  3. Ключ запуска отладки: Убедитесь, что ваш сеанс "1С:Предприятие" запущен с поддержкой отладки (параметр /Debug), а в настройках отладки Конфигуратора включен флаг автоматического подключения фоновых заданий и внешних соединений.

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

← На главную