Программная генерация 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.
  • +2
  • 11,3k
  • 2
Поделиться публикацией

Похожие публикации

Комментарии 2

    0
    Спасибо за статью.
    Интересный подход.
    P.S.
    Может стоит открыть отдельный хаб «SAP & ABAP» на хабре?
      0
      Не совсем понял зачем такие сложности. Чем стандартные ФМ FP_JOB* не устраивают, если вы не предполагаете данные брать из spool?

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое