С чего все началось
Специфика компании, в которой я работаю, подразумевает тесный контакт и сотрудничество с нашими клиентами. Одним из таких бизнес-процессов является рассылка различной документации как по почте, так и на бумажных носителях по наше не любимой почте в конвертах. Стандартный функционал, который позволяет генерировать печатные формы PDF и выводить их на печать или публиковать куда-либо в бинарном виде использует фоновые задачи и SPOOL данных печати.
Поначалу все было прекрасно, данные формировались, клиенты были довольны. Но в один момент все накрылось «медным тазом», объемы генерируемых печатных форм значительно выросли, SPOOL стал сильно «засераться», что приводило к жутким тормозам всей серверной части. Об одном из способов решения этой проблемы я и хочу рассказать в данной статье.
Поехали
Как это обычно бывает, первым делом все бегут с подобными проблемами к базистам, которые отвечают за работу серверной части и оптимизацию кучи различных настроек в конфигурационных файлах. Что в результате: прочитана куча SAP Notes, проштудированы форумы, потом начались изменения параметров, что-то дало небольшой прирост производительности, где-то наоборот. Но в конечном результате нужного эффекта так и не получили. Само собой давление руководства и недовольных клиентов возрастало, так как формирование документов занимало все больше и больше времени и о какой либо клиентоориентированность не могло быть и речи, что пагубно влияло на престиж компании. В результате было принято решение попробовать разобраться с проблемой на программной уровне.
Хватит уже прелюдий, перейдем к технической стороне вопроса.
Анализ базисного кода
Поначалу я решил разобраться как же все таки работает стандартный функционал генерации PDF форм, в результате Drill down, я натолкнулся на пакет SAFP, который раскрыл мне глаза на все происходящее и кажется пол дела было уже решено. Проанализировав примеры программ я выяснил, что для меня встали следующие основные задачи:
- Создать XFT файл формуляра;
- Сгенерировать XFD файл, содержащий данные;
- Получить бинарный файл и PDL файл, который понимает принтер;
Создать XFT файл формуляра
Тут для меня было два вариант решения, либо самому создать этот файл и хранить его где-то на сервере приложений, что было бы неразумно с точки зрения поддержки и актуализации, либо не придумывать велосипед и сразу же получать ссылку на готовый формуляр, который удобно редактировать и тестировать через транзакцию SFP. Пойдем по пути наименьшего сопротивления:
DATA:
l_xdp TYPE fpwbformname VALUE 'ZTEST', " Имя формуляра
l_xft TYPE string, " Путь к формуляру на сервере приложений
l_except TYPE REF TO cx_fp_api_repository. " Для обработки данных
TRY.
cl_fp_wb_helper=>form_layout_exists( i_name = l_xdp ).
CATCH cx_fp_api_usage. "#EC NO_HANDLER
CATCH cx_fp_api_repository INTO l_except.
IF l_except->textid = cx_fp_api_repository=>object_already_exists.
l_xft = cl_fp_wb_helper=>form_layout_url( i_name = l_xdp
i_dest_path = 'X' ).
ELSE.
MESSAGE ID 'FPRUNX' TYPE 'E' NUMBER '050' WITH sy-langu.
ENDIF.
ENDTRY.
Здесь мы проверяем существует ли формуляр в системе с заданным именем и если получаем положительный ответ, то считываем путь к этому формуляру.
Генерируем XFD файл, содержащий данные
Сформировать PDF файл — это еще пол дела. Нам необходимо наполнить его данными, чтобы это сделать нужно сгенерировать файл с данными XFD, который представляет из себя обычный xml файла. Лучшее для меня решения было — это использовать трансформации. Итак приступим.
Как нам узнать как должен выглядеть файл после трансформации, чтобы он успешно применился к нашему формуляру? Сделать это очень просто, заходим в транзакцию SFP, открываем нужный нам формуляр и включаем отладку, как показано на рисунках ниже:
Далее ставим режим отладки, это позволит нам после вывода печатной формы получить файлы во вложении
Запускаем печатную форму на тест и получаем необходимые файлы во вложении PDF, нас интересует XFD.xml
Итак, мы получили файл представления данных для печатной формы, теперь нам не составит труда создать трансформацию и вызывать ее в дальнейшем:
CALL TRANSFORMATION ztest_trans
SOURCE
is_data = it_data
RESULT XML xstr.
Получить бинарный файл и PDL файл, который понимает принтер
Итак, у нас есть все необходимое для того, чтобы сформировать печатную форму. Правда тут есть один нюанс. В природе как выяснилось существует различные типы принтеров, одни цветные, другие нет. Для определенной группы принтеров применяются так называемые шаблоны XSD, применяемые для генерации PDF файлов. Более подробно о их типах и классификации написано тут.
Мы будем использовать hppcl5c.xdc, так как он идеально подходит для нашей задачи, в том числе позволяет распечатывать на цветном принтере. Что мы получили:
DATA: l_fp TYPE REF TO if_fp,
l_pdfobj TYPE REF TO if_fp_pdf_object,
pdfresult TYPE xstring,
pdlresult TYPE xstring.
* получаем ADS-соединение
MOVE cl_fp=>get_ads_connection( ) TO l_dest.
* получаем FP reference
l_fp = cl_fp=>get_reference( ).
TRY.
* создаем объект PDF
l_pdfobj = l_fp->create_pdf_object( connection = l_dest ).
* указываем наш шаблон, который мы нашли ранее
l_pdfobj->set_template( xftfile = l_xft ).
* задам данные для шаблона
l_pdfobj->set_data( formdata = l_xfd ).
* говорим объекту PDF создать PDF
* так же в классе есть другие задачи, которые можно глянуть в описании класса
l_pdfobj->set_task_renderpdf( ).
* говорим объекту PDF создать PDL файл
CALL METHOD l_pdfobj->set_task_renderpdl
EXPORTING
pdltype = 'pcl'
pdlfile = ''
xdcname = 'hppcl5c.xdc'.
DATA: form TYPE string.
form = i_fpwbformname.
l_pdfobj->set_application_form_identity( application = 'SAFP'
form = form ).
* запускаем наши задачи, вызвав ADS
TRY.
l_pdfobj->execute( ).
CATCH cx_fp_runtime_internal
cx_fp_runtime_system
cx_fp_runtime_usage. "#EC NO_HANDLER
ENDTRY.
* получаем результат в формате XSTRING
l_pdfobj->get_pdf( IMPORTING pdfdata = pdfresult ).
CALL METHOD l_pdfobj->get_pdl
IMPORTING
pdldata = pdlresult.
ENDTRY.
Итоги
В результате после применения данного подхода, удалось вообще исключить SPOOL как таковой в цепочке. Это позволило нам создавать довольно крупные объемы печатных форм в фоновом режиме не загружая сервер, на текущий момент порядка 5000 документов за 3 часа. Стоит обратить внимание, что такой подход позволяет так же совершать другие операции с PDF, например цифровую подпись со стороны сервера. Более детально можно изучить примеры в пакете, о котором я говорил выше в статье SAFP.
Автор: KaaPex