В SAP NetWeaver есть функционал для использования длинных текстов (более 100 символов и даже более 1000 ?). более технически корректное название sapscript text или, иногда, стандартные тексты.
Длинный текст может использоваться в качестве шаблонов в письмах, печатных формах, формировании договоров и любых других сущностей, где может быть «многабукав».
В одной из следующих статей мы детально разберем, как можно использовать шаблонизацию (это было пожелание одного из слушателей ABAP FIleOS) для писем на основе длинных текстов, а в этой статей покажем, как можно создать длинный текст с вложенными частями и как их прочитать в ABAP (по сути, базовые операции с сапскрипт).
Итак, пусть у нас имеется следующий текст, разделенный на логические части (а точнее 4 части).
Вводная часть текста, поясняющая про чего этот текст (например, тип договора и предмет договора; приветствие в письме и т.д.). Какой-то блок, который по форме одинаков для многих текстов, но отличается лишь некоторыми данными (например, участники договора купли-продажи; письмо, содержащее информацию о заказе/клиенте и т.д.) Поясняющая часть (которая может быть вообще разная для разных даже однотипных текстов). Завершающая часть в тексте, которая может повторяться для нескольких текстов. |
Работа с составным длинным текстом (INCLUDE и копирование текстов)
Теперь я сделаю длинный текст в транзакции SO10, разбив текст также на 4 части. Для этого запускаю транзакцию SO10. Текст назову ZSD_MAIL_ORDER_CREATED_ALL.

Для наполнения содержим я буду использовать старый редактор (и, да я считаю его очень удобным для данных целей). Чтобы сменить/установить редактор используйте пункт меню Goto -> Change Editor/Configure Editor.

Для разделения текста на части я использую команду :/ (двоеточие и слэш) и укажу тексты, которые будут являться составными частями моего составного текста (самих текстов еще нет; я их сделаю следующим шагом также через транзакцию SO10).

Следующим шагом я создам каждый из указанных текстов через транзакцию SO10: ZSD_MAIL_ORDER_CREATED_01, ZSD_MAIL_ORDER_CREATED_02, ZSD_MAIL_ORDER_CREATED_03, ZSD_MAIL_ORDER_CREATED_04. На скриншотах ниже показано наполнение каждого из этих текстов.




В моем случае было удобно вводить текст простым вводом, однако полезно иметь ввиду функцию загрузки и выгрузки текста из файла, из другого текста и из буфера.
Для вставки текста из файла, меню: Text -> Upload/Download

Для загрузки текста из другого стандартного текста, меню: Insert -> Text -> Standard

Затем указываем параметры текста (можем вставить как ссылку на текст, так и его содержимое через галку «Expand Immediately»).

С помощью кнопки Выделить / F2, мы можем выделить строки текста и отправить его в буфер (в один из 5 буферов – то есть мы можем сделать за один раз сразу 5 выделений текста – ворду и не снилось…; относительно недавно с этим начала справляться windows и то только с помощью доп.ПО ? ).

После выделения нужных строк – они посинеют (цвет SAP). И их можно запомнить в буфере.

А затем (в другом тексте) мы можем извлечь скопированные строки из буфера

Просмотреть общий итог всего составного текста можно через предпросмотр.

Видим, что система отображает именно содержимое вложенных текстов, а не INCLUDE с командами. И это полезно.

Чтение составных длинных текстов через ABAP
Итак, мы сделали составной текст, а также наполнили каждую часть содержимым. Как считать этот текст через ABAP/4 ?
Полный текст программы. Нам нужно 3 шага:
1) Считаем длинные тексты при помощи функ.модуля READ_TEXT
2) Заменим INCLUDE в составном тексте через функц.модуль TEXT_INCLUDE_REPLACE
3) Затем заменим управляющие конструкции (если таковые есть) с помощью ФМа TEXT_CONTROL_REPLACE.
На экран выведем получившуюся строку через CL_ABAP_BROWSER.
DATA ls_txt_head_in TYPE thead.
DATA ls_txt_head_out TYPE thead.
DATA lt_txt_lines TYPE tline_tab.
DATA lv_txt_full_str TYPE string.
DATA lv_txt_full_html TYPE string.
ls_txt_head_in-tdid = p_tdid.
ls_txt_head_in-tdspras = p_langu.
ls_txt_head_in-tdobject = p_tdobj.
ls_txt_head_in-tdname = p_tdname.
_read_text_lines( EXPORTING is_txt_head = ls_txt_head_in
IMPORTING es_txt_head = ls_txt_head_out
et_txt_lines = lt_txt_lines ).
CASE abap_true.
WHEN do_html.
_convert2html( EXPORTING is_txt_head = ls_txt_head_out
CHANGING ct_txt_lines = lt_txt_lines
cv_txt_str = lv_txt_full_html ).
_show_string( EXPORTING iv_str_show = lv_txt_full_html
iv_no_wrap = abap_true ).
WHEN OTHERS.
_replace_include_n_controls( CHANGING cs_txt_head = ls_txt_head_out
ct_txt_lines = lt_txt_lines ).
_convert2string( CHANGING ct_txt_lines = lt_txt_lines
cv_txt_str = lv_txt_full_str ).
_show_string( lv_txt_full_str ).
ENDCASE.
METHOD _replace_include_n_controls.
DATA lv_was_changed_replace TYPE abap_bool.
DATA lv_was_changed_control TYPE abap_bool.
IF ct_txt_lines IS INITIAL.
RETURN.
ENDIF.
CALL FUNCTION 'TEXT_INCLUDE_REPLACE'
EXPORTING
header = cs_txt_head
IMPORTING
changed = lv_was_changed_replace
newheader = cs_txt_head
TABLES
lines = ct_txt_lines.
CALL FUNCTION 'TEXT_CONTROL_REPLACE'
EXPORTING
header = cs_txt_head
IMPORTING
changed = lv_was_changed_control
newheader = cs_txt_head
TABLES
lines = ct_txt_lines.
ENDMETHOD.
Также хотел бы обратить внимание на конвертацию длинного текста в формат HTML. Если HTML – это то, для чего Вы хотите использовать шаблон, то можете использовать ФМ CONVERT_ITF_TO_HTML. Подстановку и замену управляющих конструкций этот ФМ делает автоматически (при указании параметра ? ) (листинг3).
METHOD _convert2html.
"CHANGING ct_txt_lines TYPE tline_tab
" cv_txt_str TYPE string.
DATA lt_htmlline TYPE htmltable.
FIELD-SYMBOLS <fs_html_line> TYPE htmlline.
CLEAR cv_txt_str .
CALL FUNCTION 'CONVERT_ITF_TO_HTML'
EXPORTING
* i_codepage = " Target character set
i_header = is_txt_head " Text header of input text
* i_page = space " Page specification for page window format
* i_window = space " Window specification for page window format
* i_syntax_check = space " Activating the ITF syntax check
* i_replace = 'X' " Expanding symbols and includes
* i_print_commands = space " Outputting commands and text elements
* i_html_header = 'X' " Output of the HTML header tag
* i_funcname = space " Exit module for link interpretation
* i_title = space " Title in HTML header
* i_background = space " File name for HTML image as background
* i_bgcolor = space " Background color of text
* i_unescape_formats = " Exception Formats for Masking Special Characters
* i_escape_spaces = space " Masking of Blank Characters
* i_encoding = space " Define Character Set Coding in Header
* IMPORTING
* e_html_text = " Text Content in HTML as Xstring
TABLES
t_itf_text = ct_txt_lines " Text lines in ITF (input)
t_html_text = lt_htmlline " Text lines in HTML (output)
* t_conv_charformats = " Table for character formats
* t_conv_parformats = " Table for paragraph formats
EXCEPTIONS
syntax_check = 1 " Incorrect inout text ITF syntax
replace = 2 " Errors expanding includes and symbols
illegal_header = 3 " Text header incorrect
OTHERS = 4.
IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
LOOP AT lt_htmlline ASSIGNING <fs_html_line>.
IF cv_txt_str IS INITIAL.
cv_txt_str = <fs_html_line>-tdline.
ELSE.
cv_txt_str = cv_txt_str && <fs_html_line>-tdline.
ENDIF.
ENDLOOP.
ENDMETHOD.
Установка переменных
Конечно, никакие шаблоны не используются как статичные тексты. Одна из важных причин существования шаблонов (а в данном случае мы рассматриваем сапскрипт как шаблоны) – это возможность наполняться переменными данными. Давайте попробуем наполнить наш текст переменными. Переменные в текстах сапскрипт бывают трёх видов: системные, программные (класс, группа функций, исполняемая программа – это программы) и стандартные.
Давайте добавим в наш текст переменные. Синтаксис символов-переменных такой &SOME_VAR&. (подробнее тут). При использовании переменных полезно иметь ввиду опции форматирования: опции позволяют использовать нужный формат даты, времени; не выводить пустые переменные, добавлять пре- и пост- тексты к переменным и другие возможности (подробнее тут).
Добавим в текст ZSD_MAIL_ORDER_CREATED_02 переменные:
* |
Какой-то блок, который по форме одинаков для многих текстов |
* |
, но отличается лишь некоторыми данными (например, |
* |
участники договора купли-продажи; письмо, содержащее информацию |
* |
о заказе/клиенте и т.д.) |
* |
|
* |
Текущая дата: &DATE& |
* |
SYREPID: &SY-REPID& |
* |
|
* |
Переменная из объекта: |
* |
S-V1: &S-V1& |
* |
&'S-V2: 'S-V2& |
/: |
IF &S-V3& EQ 'СОЛОВЕЙ'. |
* |
LLM-алгоритм говорит, что речь о птицах. |
/: |
ENDIF. |
В данном тексте мы вывели дату через переменную &DATE& (она доступна в любом тексте); вывели имя отчета через &SY-REPID&, а также вывели значения переменных из той, программы, которая будет читать текст.
Переменная в тексте |
Пояснение |
&S-V1& |
Выводит значение переменной S-V1. То есть где-то в программе есть структура S (одна буква для краткости), а в ней есть поле V1. Строка будет выведена в любом случае даже если переменная пустая |
&'S-V2: 'S-V2& |
В этом случае мы имеем пре-текст, то есть строчка не будет выведена, если переменная пустая |
IF &S-V3& EQ 'СОЛОВЕЙ'. |
А такие конструкции позволяет дорабатывать части шаблона с помощью проверки условий через IF. |
Обратим внимание, что для замены именно переменных мы используем функциональный модуль: TEXT_SYMBOL_REPLACE. В качестве параметра передаем ему имя той программы (как правило, вызывающей, которая содержит переменные для шаблона).
REPORT zrep_c8a014_long_txt_symb.
TYPES: BEGIN OF ts_model
, v1 TYPE string
, v2 TYPE string
, v3 TYPE string
, v4 TYPE string
, END OF ts_model
.
PARAMETERS: p_tdname TYPE thead-tdname DEFAULT 'ZSD_MAIL_ORDER_CREATED_ALL'
, p_tdobj TYPE thead-tdid DEFAULT 'TEXT'
, p_tdid TYPE thead-tdid DEFAULT 'ST'
, p_langu TYPE sylangu DEFAULT 'RU'
.
START-OF-SELECTION.
DATA lv_txt TYPE string.
DATA ls_txt_h TYPE thead.
data s TYPE ts_model.
ls_txt_h-tdid = p_tdid.
ls_txt_h-tdspras = p_langu.
ls_txt_h-tdobject = p_tdobj.
ls_txt_h-tdname = p_tdname.
s-v1 = 'Переменная V1'.
s-v2 = 'Вторая переменная'.
s-v3 = 'СОЛОВЕЙ'.
lv_txt = NEW zcl_c8a014_long_text( sy-cprog )->r( ls_txt_h ).
DATA lv_as_html_out TYPE string.
lv_as_html_out =
| <html dir="ltr" lang="ru"> |
&& |<head> <meta charset="UTF-8">|
&& |<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">|
&& |<title>ABAP String Show </title>|
&& |</head> <body>|
&& |<div> { lv_txt } </div>|
&& |</body> </html>|
.
cl_abap_browser=>show_html(
EXPORTING
html_string = lv_as_html_out " HTML String
).
Код класса ZCL_C8A014_LONG_TEXT доступен по ссылке. Текст программе в той же папке на github.
Таким образом, мы можем использовать длинные тексты в качестве шаблона.

Перенос текстов через транспортный запрос
Длинные тексты автоматически не вносятся в запрос. Однако в случае необходимости, их можно положить в запрос принудительно, сделав следующую запись в транспортном запросе.
R3TR TEXT TEXT, ZSD_MAIL_ORDER_CREATED_02,ST,R

В документации также, в качестве альтернативы ручному добавлению, обозначены две программы ( RSTXR3TR и RSTXSCRP) и рекомендация обратиться к справке.
Заключение
Сапскрипт-тексты можно использовать в качестве шаблонов. С их помощью можно сделать вложенный шаблон, наполнить его переменными и условными конструкциями. Однако, не стоит забывать про перенос текстов – автоматически тексты в запрос не добавляются. А также факт того, что сапскрипт-тексты хранятся в неструктурированном виде в рамках оперативной базы. В следующих статьях мы рассмотрим, как можно нарисовать «стилизованное письмо с картинками» на основе сапскрипта, а также альтернативы сапскрипту для ведения шаблонов.