Вполне бытовая задача при разработке 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/. Делает ровно то, что мне нужно.