ZDOC_2679

Трансформации, простые и не очень

Вполне бытовая задача при разработке ERP-системы – настройка оповещений посредством электронной почты.

Соответственно сразу возникает несколько идей, куда ложить текст шаблона:

  • непосредственно в тексте программы, хардкод никто не отменял
  • в хранилищах  OAOR/SMW0 – по обстоятельстам
  • в текстах SO10, если они довольно простые и/или ведутся прямо в продуктивной системе

… хотя можно встретить и более экзотические варианты.

Задача может стать чуть более изощренной, если потребуется в письмо добавить красоты в виде HTML. В первом приближении HTML – это простой текст со специальной разметкой в виде тегов. Поэтому можно не отказываться от старых вариантов и мириться с неудобствами в виде редактирования plain-text и отсутствия проверок.

<html>
  <body>
    <H3>Тревога, тревога:</H3>
    <p>Волк унёс зайчат!</p>
    <p>Последнее местоположение зайчат: [LGORT]</p>
  </body>
<html>

Однако задача может стать еще сложнее, если появляется динамичный контент в виде таблиц. И тут появляется развилка:

  • дополнительно нагружать текстовый шаблон особой разметкой и реализовать её обработку на ABAP, что не технологично
  • искать альтернативные технологии

И как вы уже поняли из заголовка статьи – далее речь пойдёт о втором варианте в подробностях.

Трансформации – одновременно и хранилище шаблонов, и метод их обработки. Два-в-одном!

Допустим, что мы сложили данные в структуру вида:

DATA: BEGIN OF ls_mail,
         hdr type string,
         list type standard table of fld1_and_fld2,
      END OF ls_mail.

Вот пример простой трансформации (ST, Simple Transformation):

<?sap.transform simple?>
<tt:transform xmlns:tt="http://www.sap.com/transformation-templates">
   <tt:root name="MAIL" />
   <tt:template>
      <html>
         <body>
            <H3>
               Please see list (<HDR tt:value-ref="MAIL.HDR" />):
            </H3>
            <table border="2">
               <tr>
                  <td>
                     <b>Field 1</b>
                  </td>
                  <td>
                     <b>Field 2</b>
                  </td>
               </tr>
               <tt:loop ref="MAIL.LIST">
                  <tr>
                     <td>
                        <FLD1 tt:value-ref="FLD1" />
                     </td>
                     <td>
                        <FLD2 tt:value-ref="FLD2" />
                     </td>
                  </tr>
               </tt:loop>
            </table>
         </body>
      </html>
   </tt:template>
</tt:transform>

ST – это такая личная саповская реализация упрощенных XSLT-преобразований, которые требуют меньших знаний и обладают большей интегрированностью в систему.

Запустить преобразование можно при помощи такой конструкции:

CALL TRANSFORMATION 'Z_MY_TEMPLATE'
    SOURCE mail = ls_data
    RESULT XML lv_body
    OPTIONS value_handling = 'move'
            xml_header     = abap_false.

И вот мне понадобилось добавить малюсенькую деталь: дату в человеческом формате. ST такого не умеет. Только YYYY-MM-DD, только хардкор. Из более-менее применимого форматирования есть только обрезка лидирующих нулей:

<tt:value option="format(alpha)" 
             ref="NUMTEXT"/>

Соответственно появляется выбор:

  • добавить поля в исходную структуру, не захотелось засорять
  • добавить вызов метода прямо в трансформацию, не сложилось
  • уходить от простых трансформаций к обычным

Вызов abap-метода из трансформации делается прмерно так:

<tt:call-method class="class" [s-|d-]name="meth" 
                              [writer = "writer_para"] 
                              [reader = "reader_para"] 
  [<tt:with-parameter [s-|d-]name="para1" 
                      [ref="node1"|val="val1"|var="var1"] /> 
   <tt:with-parameter [s-|d-]name="para2" 
                      [ref="node2"|val="val2"|var="var2"] /> 
   ...] 
</tt:call-method>

Я попытался быстро встроить вызов своего класса, но не сложилось. [UPD. При случае надо вернуться к этой теме]

В результате я ушел в обычные широкоизвестные трансформации:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
               xmlns:asx="http://www.sap.com/abapxml" version="1.0">
  <xsl:output encoding="utf-8" indent="yes" method="xml" version="1.0"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <html>
      <body>
        <H3>Vendors and dates:</H3>
        <table border="2">
          <tr>
            <td>
              <b>Vendor</b>
            </td>
            <td>
              <b>Date</b>
            </td>
          </tr>
          <xsl:for-each select="/asx:abap/asx:values/MAIL/LIST/Z_LINE_TYPE">
            <tr>
              <td>
                <xsl:value-of select="format-number(LIFNR, '#')"/>
              </td>
              <td>
                <!--xsl:value-of select="EXDAT"/-->
                <!--xsl:value-of select="format-date(EXDAT,'[D01].[M01].[Y0001]')" /-->
                <xsl:value-of select="substring(EXDAT, 9, 2)"/>.<xsl:value-of select="substring(EXDAT, 6, 2)"/>.<xsl:value-of select="substring(EXDAT, 1, 4)"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:transform>

Работает. Кратко о впечатлениях:

Простоты поменьше. Появляются неочевидные вещи. Желательно глядеть исходный XML(результат трансформации данных ABAP), доступен через отладку. Функция format-date не поддерживается в моей системе. Редактор и отладка ошибок оставляют желать лучшего.

Всю отправку почты я завернул под капот небольшого помогательного класса:

DATA(lo_mail) = NEW zcl_mailer( iv_template_type = 'XSLT'
                                iv_template_name = 'Z_FOO_MAIL_BODY'
                                iv_sender        = 'alpha@abc.corp'
                                iv_receiver      = lv_receiver
                                iv_subject       = 'Warning: Волк унёс зайчат!' ).
lo_mail->fill( is_data = ls_mail ).
lo_mail->send( ).

 

PS. Транзакция для отдельного доступа к трансформациям – STRANS

PPS. Надо запомнить урл хорошей помогалки в разработке http://xsltransform.net/. Делает ровно то, что мне нужно.