Программная генерация PDF форм на ABAP или как избавиться от проблем со SPOOL

от автора

С чего все началось

Специфика компании, в которой я работаю, подразумевает тесный контакт и сотрудничество с нашими клиентами. Одним из таких бизнес-процессов является рассылка различной документации как по почте, так и на бумажных носителях по наше не любимой почте в конвертах. Стандартный функционал, который позволяет генерировать печатные формы PDF и выводить их на печать или публиковать куда-либо в бинарном виде использует фоновые задачи и SPOOL данных печати.
Поначалу все было прекрасно, данные формировались, клиенты были довольны. Но в один момент все накрылось «медным тазом», объемы генерируемых печатных форм значительно выросли, SPOOL стал сильно «засераться», что приводило к жутким тормозам всей серверной части. Об одном из способов решения этой проблемы я и хочу рассказать в данной статье.

Поехали

Как это обычно бывает, первым делом все бегут с подобными проблемами к базистам, которые отвечают за работу серверной части и оптимизацию кучи различных настроек в конфигурационных файлах. Что в результате: прочитана куча SAP Notes, проштудированы форумы, потом начались изменения параметров, что-то дало небольшой прирост производительности, где-то наоборот. Но в конечном результате нужного эффекта так и не получили. Само собой давление руководства и недовольных клиентов возрастало, так как формирование документов занимало все больше и больше времени и о какой либо клиентоориентированность не могло быть и речи, что пагубно влияло на престиж компании. В результате было принято решение попробовать разобраться с проблемой на программной уровне.
Хватит уже прелюдий, перейдем к технической стороне вопроса.

Анализ базисного кода

Поначалу я решил разобраться как же все таки работает стандартный функционал генерации PDF форм, в результате Drill down, я натолкнулся на пакет SAFP, который раскрыл мне глаза на все происходящее и кажется пол дела было уже решено. Проанализировав примеры программ я выяснил, что для меня встали следующие основные задачи:

  1. Создать XFT файл формуляра;
  2. Сгенерировать XFD файл, содержащий данные;
  3. Получить бинарный файл и 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.

ссылка на оригинал статьи http://habrahabr.ru/post/201280/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *