Разработка внешних печатных форм (ВПФ) — одна из наиболее частых задач для программиста 1С. Использование механизма ВПФ позволяет изменять печатные формы документов без изменения самой конфигурации, что существенно облегчает последующие обновления системы. Однако у начинающих разработчиков часто возникают вопросы при анализе кода типовых ВПФ. В частности: откуда берется загадочный параметр ДанныеПечати, где физически находится запрос к базе данных и почему в некоторых обработках используется реквизит СсылкаНаОбъект, а в других его нет?
В этой статье мы подробно разберем внутреннее устройство механизмов печати Библиотеки стандартных подсистем (БСП), выясним логику работы платформы и научимся правильно проектировать собственные внешние печатные формы.
При изучении кода заполнения печатных форм (например, ТОРГ-12, М-15 или УПД) мы часто встречаем процедуры следующего вида:
Процедура ЗаполнитьРеквизитыШапкиТОРГ12(ДанныеПечати, Макет, ТабличныйДокумент)
Внутри этой процедуры происходит обращение к свойствам параметра ДанныеПечати так, будто это сам документ: ДанныеПечати.Организация, ДанныеПечати.Грузополучатель, ДанныеПечати.НомерДокумента и так далее. Из-за этого может возникнуть ошибочное мнение, что в параметр передается ссылка на сам документ или его объектная форма. Но это не так.
На самом деле параметр ДанныеПечати — это строка выборки из результата запроса (объект ВыборкаИзРезультатаЗапроса) либо строка таблицы значений (или структура), содержащая подготовленные к выводу данные. Давайте разберем подробнее, почему разработчики БСП используют именно такой подход:
СсылкаНаДокумент.Организация.ИНН), то при печати группового пакета документов из 100 штук платформа совершила бы сотни или даже тысячи отдельных запросов к базе данных. Это привело бы к критическому снижению производительности.Таким образом, в процедуру ЗаполнитьРеквизитыШапкиТОРГ12 передается уже готовый набор данных по конкретному документу из общего пула запроса, что минимизирует обращения к серверу СУБД.
Часто при открытии кода ВПФ разработчики обнаруживают, что в модуле объекта самой обработки вообще нет ни одного SQL-подобного запроса к базе данных. Возникает вопрос: каким образом печатная форма получает данные, если самого запроса внутри нее нет?
Секрет кроется в архитектуре БСП и принципе повторного использования кода. Разработчики типовых конфигураций выносят логику формирования данных для печати в специальные экспортные методы, которые располагаются в модуле менеджера исходного документа в самой конфигурации.
Проанализируем типичный сценарий работы ВПФ "ТОРГ-12" для документа "Реализация товаров и услуг":
ДанныеДляПечати = Документы.РеализацияТоваровУслуг.ПолучитьДанныеДляПечатиТОРГ12(МассивОбъектов);
Преимущества такого подхода: Внешняя печатная форма получается очень "легкой" — она содержит только макет и код вывода областей. Если логика хранения данных в конфигурации изменится (например, адрес контрагента начнет храниться в другом регистре сведений), разработчикам 1С достаточно обновить общий запрос в модуле менеджера документа. Ваша ВПФ при этом продолжит работать без сбоев и ошибок, так как она по-прежнему вызывает тот же экспортный метод конфигурации.
Недостатки: Если вам требуется кардинально изменить состав выводимых данных (например, добавить во ВПФ реквизиты, которых вообще нет в стандартной выборке модуля менеджера), вам придется отказаться от переиспользования типового метода и написать собственный запрос к базе данных внутри модуля объекта ВПФ.
В сообщениях на форумах часто упоминается реквизит СсылкаНаОбъект. Разберем, откуда он берется и почему его поведение различается в зависимости от версии БСП.
Здесь мы сталкиваемся с эволюцией стандартов разработки подсистемы "Печать" в Библиотеке стандартных подсистем.
В устаревших конфигурациях механизм подключения ВПФ требовал обязательного создания реквизита на уровне метаданных внешней обработки. Этот реквизит должен был называться строго СсылкаНаОбъект (тип ЛюбаяСсылка).
Алгоритм работы платформы выглядел следующим образом:
СсылкаНаОбъект программно записывалась ссылка на печатаемый документ.Печать() модуля объекта ВПФ, которая не принимала параметров. Внутри кода этой функции разработчик обращался к данным через конструкцию:
ДокументСсылка = ЭтотОбъект.СсылкаНаОбъект;
В современных конфигурациях 1С (ERP, УТ 11, КА 2, БП 3.0, ЗУП 3) механизм полностью переведен на передачу параметров через вызовы процедур на сервере без использования реквизитов обработки. Создавать реквизит СсылкаНаОбъект в метаданных обработки больше не требуется.
Теперь в модуле объекта ВПФ обязательно должна присутствовать экспортная процедура Печать с жестко регламентированной сигнатурой:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
Рассмотрим назначение ключевых параметров этой процедуры:
Выяснив теоретические аспекты работы механизмов печати, рассмотрим пошаговый алгоритм проектирования собственной внешней печатной формы для конфигураций на управляемых формах.
Перед написанием кода нам необходимо решить, откуда мы будем брать данные для заполнения макета:
МассивОбъектов в качестве источника параметров запроса.Создаем новую внешнюю обработку в конфигураторе. Нам не нужно создавать никаких реквизитов в метаданных. Переходим сразу в модуль объекта обработки и объявляем обязательную функцию регистрации внешней обработки в системе БСП:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидДополнительногоОтчетаИОбработкиПечатнаяФорма();
ПараметрыРегистрации.Версия = "1.0";
ПараметрыРегистрации.Наименование = "Внешняя печатная форма ТОРГ-12 (Кастомная)";
ПараметрыРегистрации.БезопасныйРежим = Ложь; // Если используем сложные запросы или внешние компоненты
// Указываем, к каким документам конфигурации привязывается печатная форма
ПараметрыРегистрации.Назначение.Добавить("Документ.РеализацияТоваровУслуг");
// Описываем команду печати
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = "ТОРГ-12 (внешняя кастомная)";
НоваяКоманда.Идентификатор = "ТОРГ12_Кастомная";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
НоваяКоманда.Модификатор = "ПечатьMXL";
Возврат ПараметрыРегистрации;
КонецФункции
Добавим в модуль объекта процедуру Печать, которая будет вызываться системой БСП при нажатии пользователем кнопки на панели команд:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Проверяем, запущена ли именно наша команда
ПараметрыПечати = ПолучитьПараметрыПечати(КоллекцияПечатныхФорм);
// Создаем результирующий табличный документ
ТабличныйДокумент = Новый ТабличныйДокумент;
ТабличныйДокумент.ИмяПараметровПечати = "ПАРАМЕТРЫ_ПЕЧАТИ_РеализацияТоваровУслуг_ТОРГ12_Кастомная";
// Вызываем процедуру формирования печатной формы
СформироватьПечатнуюФорму(МассивОбъектов, ТабличныйДокумент, ОбъектыПечати);
// Заполняем коллекцию печатных форм для возврата в БСП
ПечатнаяФорма = ДополнительныеОтчетыИОбработкиКлиентСервер.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "ТОРГ12_Кастомная");
Если ПечатнаяФорма <> Неопределено Тогда
ПечатнаяФорма.ТабличныйДокумент = ТабличныйДокумент;
КонецЕсли;
КонецПроцедуры
Рассмотрим вариант Б, когда мы пишем собственный запрос для получения данных на основе переданного массива ссылок документов. Создадим процедуру формирования печатной формы:
Процедура СформироватьПечатнуюФорму(МассивОбъектов, ТабличныйДокумент, ОбъектыПечати)
// Получаем макет из нашей внешней обработки
Макет = ПолучитьМакет("МакетТОРГ12");
// Инициализируем запрос к базе данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Ссылка,
| Реализация.Номер КАК НомерДокумента,
| Реализация.Дата КАК ДатаДокумента,
| Реализация.Организация КАК Организация,
| Реализация.Контрагент КАК Контрагент,
| Реализация.Организация.НаименованиеПолное КАК ОрганизацияПредставление,
| Реализация.Контрагент.НаименованиеПолное КАК КонтрагентПредставление
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Ссылка В (&МассивОбъектов)";
Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
// Выполняем запрос и обходим результаты
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДокументов = РезультатЗапроса.Выбрать();
ПервыйДокумент = Истина;
Пока ВыборкаДокументов.Следующий() Цикл
Если Не ПервыйДокумент Тогда
// Разрываем страницы между документами
ТабличныйДокумент.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
ПервыйДокумент = Ложь;
// Запоминаем начальную строку для объектов печати
НомерСтрокиНачало = ТабличныйДокумент.ВысотаТаблицы + 1;
// Здесь ВыборкаДокументов и выступает в роли "ДанныеПечати"
ВывестиШапкуТОРГ12(ВыборкаДокументов, Макет, ТабличныйДокумент);
ВывестиТабличнуюЧастьТОРГ12(ВыборкаДокументов.Ссылка, Макет, ТабличныйДокумент);
ВывестиПодвалыТОРГ12(ВыборкаДокументов, Макет, ТабличныйДокумент);
// Регистрируем область печати для БСП
УправлениеПечатью.ЗадатьОбластьПечатиДокумента(ТабличныйДокумент, НомерСтрокиНачало, ОбъектыПечати, ВыборкаДокументов.Ссылка);
КонецЦикла;
КонецПроцедуры
Проанализировав данный пример, мы четко видим происхождение всех переменных. Переменная ВыборкаДокументов, являясь результатом выборки запроса, передается в подчиненные процедуры вывода секций макета. Внутри этих процедур она будет фигурировать под именем ДанныеПечати.
Самым сложным этапом при разработке ВПФ часто становится ее отладка. Поскольку внешняя обработка запускается во временном хранилище через механизм БСП, стандартная точка останова в конфигураторе может не сработать напрямую. Выделим несколько эффективных приемов для отладки ВПФ:
&НаКлиенте
Процедура ТестПечати(Команда)
МассивДокументов = Новый Массив;
МассивДокументов.Добавить(ПолучитьСсылкуНаТестовыйДокумент());
ТабличныйДокумент = Новый ТабличныйДокумент;
ОбъектыПечати = Новый Соответствие;
СформироватьПечатнуюФорму(МассивДокументов, ТабличныйДокумент, ОбъектыПечати);
ТабличныйДокумент.Показать();
КонецПроцедуры
Применяя данные подходы, мы сможем создавать отказоустойчивые, быстрые и легко обновляемые внешние печатные формы, которые будут корректно работать в любой конфигурации 1С на платформе 8.3.