Вполне бытовая задача при разработке ERP-системы — настройка оповещений посредством электронной почты.
Соответственно сразу возникает несколько идей, куда ложить текст шаблона:
- непосредственно в тексте программы, хардкод никто не отменял
- в хранилищах OAOR/SMW0 — по обстоятельстам
- в текстах SO10, если они довольно простые и/или ведутся прямо в продуктивной системе
… хотя можно встретить и более экзотические варианты.
Задача может стать чуть более изощренной, если потребуется в письмо добавить красоты в виде HTML. В первом приближении HTML — это простой текст со специальной разметкой в виде тегов. Поэтому можно не отказываться от старых вариантов и мириться с неудобствами в виде редактирования plain-text и отсутствия проверок.
1 2 3 4 5 6 7 |
<html> <body> <H3>Тревога, тревога:</H3> <p>Волк унёс зайчат!</p> <p>Последнее местоположение зайчат: [LGORT]</p> </body> <html> |
Однако задача может стать еще сложнее, если появляется динамичный контент в виде таблиц. И тут появляется развилка:
- дополнительно нагружать текстовый шаблон особой разметкой и реализовать её обработку на ABAP, что не технологично
- искать альтернативные технологии
И как вы уже поняли из заголовка статьи — далее речь пойдёт о втором варианте в подробностях.
Трансформации — одновременно и хранилище шаблонов, и метод их обработки. Два-в-одном!
Допустим, что мы сложили данные в структуру вида:
1 2 3 4 |
DATA: BEGIN OF ls_mail, hdr type string, list type standard table of fld1_and_fld2, END OF ls_mail. |
Вот пример простой трансформации (ST, Simple Transformation):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?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-преобразований, которые требуют меньших знаний и обладают большей интегрированностью в систему.
Запустить преобразование можно при помощи такой конструкции:
1 2 3 4 5 |
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, только хардкор. Из более-менее применимого форматирования есть только обрезка лидирующих нулей:
1 2 |
<tt:value option="format(alpha)" ref="NUMTEXT"/> |
Соответственно появляется выбор:
- добавить поля в исходную структуру, не захотелось засорять
- добавить вызов метода прямо в трансформацию, не сложилось
- уходить от простых трансформаций к обычным
Вызов abap-метода из трансформации делается прмерно так:
1 2 3 4 5 6 7 8 9 |
<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. При случае надо вернуться к этой теме]
В результате я ушел в обычные широкоизвестные трансформации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<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 не поддерживается в моей системе. Редактор и отладка ошибок оставляют желать лучшего.
Всю отправку почты я завернул под капот небольшого помогательного класса:
1 2 3 4 5 6 7 |
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/. Делает ровно то, что мне нужно.