Read text template, resolve placeholders and maintain text

*"----------------------------------------------------------------------
*"Methoden Signatur:
*"  Importing
*"    IV_HEADER_GUID TYPE CRMT_OBJECT_GUID
*"    IV_TEXTTYPE TYPE TDID
*"    IV_TEXTID TYPE DOK_ID
*"    IV_TEXTOBJ TYPE DOKU_OBJ
*"----------------------------------------------------------------------
METHOD maintain_transaction_text.

  DATA:
    lr_instance   TYPE REF TO cl_ags_crm_1o_api,
    ls_head       TYPE thead,
    lt_line       TYPE TABLE OF tline,
    lt_symbol     TYPE TABLE OF itcst,
    lt_text       TYPE crmt_text_comt,
    ls_text       LIKE LINE OF lt_text[].

  FIELD-SYMBOLS:
    <fs_symbol> LIKE LINE OF lt_symbol[],
    <fv_value>  TYPE any.

*Get Instance of Change Transaction
  CALL METHOD cl_ags_crm_1o_api=>get_instance
    EXPORTING
*     iv_language                   = SY-LANGU
      iv_header_guid                = iv_header_guid
*     iv_process_type               =
      iv_process_mode               = 'C' "We want to Change the Change Transaction. But Exception can not tell that document is just locked.
    IMPORTING
*     et_msg                        =
      eo_instance                   = lr_instance
    EXCEPTIONS
      invalid_parameter_combination = 1
      error_occurred                = 2
      OTHERS                        = 3.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

*We want add a text block...
*The following code was inspired by
*  CL_WD_FORMATTED_TEXT->CREATE_FROM_SAPSCRIPT
*  CL_SSF_XSF_UTILITIES
  IF iv_texttype IS NOT INITIAL AND iv_textid IS NOT INITIAL AND iv_textobj IS NOT INITIAL.

*Read text block previously created using transaction SE61.
    CALL FUNCTION 'DOCU_GET'
      EXPORTING
*       EXTEND_EXCEPT          = ' '
        id                     = iv_textid
        langu                  = sy-langu
        object                 = iv_textobj
*       TYP                    = 'E'
*       VERSION                = 0
*       VERSION_ACTIVE_OR_LAST = 'L'
*       PRINT_PARAM_GET        = 'X'
      IMPORTING
*       DOKSTATE               =
*       DOKTITLE               =
        head                   = ls_head
*       DOKTYP                 =
      TABLES
        line                   = lt_line[]
      EXCEPTIONS
        no_docu_on_screen      = 1
        no_docu_self_def       = 2
        no_docu_temp           = 3
        ret_code               = 4
        OTHERS                 = 5.

*Expected & unexpected error...
    CASE sy-subrc.
      WHEN 0.
      WHEN OTHERS. MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDCASE.

*Resolve Text block includes.
    CALL FUNCTION 'TEXT_INCLUDE_REPLACE'
      EXPORTING
*   ALL_LEVEL        = 'X'
*   ENDLINE          = 99999
        header           = ls_head
*   STARTLINE        = 1
*   PROGRAM          = ' '
* IMPORTING
*   CHANGED          =
*   ERROR_TYPE       =
*   NEWHEADER        =
      TABLES
        lines            = lt_line[].

*Resolve text block control statements.
    CALL FUNCTION 'TEXT_CONTROL_REPLACE'
      EXPORTING
        header                = ls_head
*   PROGRAM               = ' '
*   REPLACE_COMMENT       = 'X'
* IMPORTING
*   CHANGED               =
*   NEWHEADER             =
      TABLES
        lines                 = lt_line[].

*Clear buffer for text symbols.
    CALL FUNCTION 'INIT_TEXTSYMBOL'
      EXPORTING
*       PROGRAM = ' '
*       TEST    = ' '
        type    = 'TEXT'.

*Get all used symbols.
    CALL FUNCTION 'TEXT_SYMBOL_COLLECT'
      TABLES
        lines   = lt_line[]
        symbols = lt_symbol[].

*Set values for used symbols.
    LOOP AT lt_symbol[] ASSIGNING <fs_symbol>.

      ASSIGN (<fs_symbol>-name) TO <fv_value>.

      IF sy-subrc = 0.

*An alternative solution would be the use of global variables in a program or function group.
        CALL FUNCTION 'TEXT_SYMBOL_SETVALUE'
          EXPORTING
            name            = <fs_symbol>-name
            value           = <fv_value>
*           VALUE_LENGTH    = 0
*           REPLACE_SYMBOLS = ' '
          .

      ELSE.

*There is a wrong symbol in text block.
        MESSAGE x151(00).

      ENDIF.

    ENDLOOP.

*Replace symbols by value.
    CALL FUNCTION 'TEXT_SYMBOL_REPLACE'
      EXPORTING
*   ENDLINE                = 99999
        header                 = ls_head
*   INIT                   = ' '
*   OPTION_DIALOG          = ' '
*   PROGRAM                = ' '
        replace_program        = ' ' "We don^t use global variables of a program or function group
*       REPLACE_STANDARD       = 'X' "What's that???
        replace_system         = 'X' "Replace system variables (date, time, username, ...)
        replace_text           = 'X' "Replace all variables pubslished by TEXT_SYMBOL_SETVALUE
*   STARTLINE              = 1
* IMPORTING
*   CHANGED                =
*   NEWHEADER              =
      TABLES
        lines                  = lt_line[].

*Add Text block.
    CLEAR:
      ls_text,
      lt_text[].

    MOVE:
      iv_texttype TO ls_text-tdid,
      'I'         TO ls_text-mode,
      lt_line[]   TO ls_text-lines[].

    APPEND ls_text TO lt_text[].

    CALL METHOD lr_instance->set_texts
      EXPORTING
        it_text           = lt_text[]
*  CHANGING
*    cv_log_handle     =
      EXCEPTIONS
        error_occurred    = 1
        document_locked   = 2
        no_change_allowed = 3
        no_authority      = 4
        OTHERS            = 5.

*Expected & unexpected error...
    CASE sy-subrc.
      WHEN 0.
      WHEN 2. RAISE document_locked.
      WHEN 3. RAISE no_change_allowed.
      WHEN 4. RAISE no_authority.
      WHEN OTHERS. MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDCASE.

**I dont really understand it.
**The Standard did not post this event on create/change.
**But on save he detects it.
**We need to post it manually to save the text changes.
**I think this is a bug and should be resolved by SAP.
**Maybe it only occurs when we only change texts.
*    IF sy-subrc = 0.
*      CALL FUNCTION 'CRM_EVENT_PUBLISH_OW'
*        EXPORTING
*          iv_obj_name = 'TEXTS'
*          iv_guid_hi  = iv_header_guid
*          iv_kind_hi  = 'A'
*          iv_event    = 'SAVE'
*        EXCEPTIONS
*          OTHERS      = 2.
*    ENDIF.

  ENDIF.

*This is a little bit strange but necessary.
*Status changes and long text changes bypass the One Order Framework.
*Therefore when we save the Change Transaction via CRM_ORDER_SAVE it might detect nothing to do.
*But this is wrong, we still want a commit work.

*Therefore we simulate a change on ORDERADM_H.
  CALL FUNCTION 'CRM_EVENT_PUBLISH_OW'
    EXPORTING
      iv_obj_name = 'ORDERADM_H'
      iv_guid_hi  = iv_header_guid
      iv_kind_hi  = 'A'
      iv_event    = 'SAVE'.

*Free Instance.
  FREE: lr_instance.

ENDMETHOD.