Change Request Management Downgrade Protection Handbuch

Handbuch zur Steuerung, Überwachung und Vermeidung von Parallelentwicklungen

Systemübergreifende Objektsperre / Cross System Object Lock / CSOL

Die „Systemübergreifende Objektsperre / Cross System Object Lock / CSOL“ ist eine Funktion des SAP Solution Manager Change Request Management. Sie gewährleistet, dass ein Objekt bei Änderung in einem verwalteten System im zentralen SAP-Solution-Manager-System gesperrt wird. Spätestens beim Speichern dieser Änderung wird der Entwickler/Customizer über aktuell laufende parallele Anpassungen am selben Objekt informiert und kann seine Änderung ggf. gar nicht oder nur im fremden Änderungsvorgang speichern. Hierdurch werden Importprobleme von vornherein ausgeschlossen bzw. der Implementierer wird im Vorfeld über die Gefahr informiert.

Downgrade-Schutz / Downgrade Protection / DGP

Der „Downgrade-Schutz / Downgrade Protection / DGP“ ist eine Funktion des SAP Solution Manager Change Request Management. Die Funktion dient der Nachverfolgung von Objektänderungen und deren Transport durch die gesamte Lösungslandschaft. Konflikte und daraus resultierende potentielle und tatsächliche Downgrade-Risiken (z.B. Überholen eines Vorgängers oder Überschreiben eines Nachfolgers) werden identifiziert, bewertet, gemeldet und müssen explizit vom Change- / Release-Manager genehmigt werden. Hierdurch werden Importprobleme proaktiv verhindert bzw. der Change- / Release-Manager wird im Vorfeld über die Gefahr informiert.

Projektschnittmengenprüfung / Project Intersection Check / PIC

Wenn Sie in einer Systemlandschaft mit mehreren parallelen Projekten arbeiten, die Projekte aber nicht vollständig disjunkt halten können, dann müssen Sie Abhängigkeitsbeziehungen zwischen den Transportaufträgen definieren. Beim Import von Transportaufträgen werden die Abhängigkeitsbeziehungen ausgewertet und das System weist die Transport­administration automatisch darauf hin, dass sie die Menge der zu importierenden Aufträge vergrößern muss, um die korrekte Importreihenfolge zu erhalten. Die Funktion „Projekt­schnitt­mengenprüfung / Project Intersection Check / PIC“ dient der automatischen Identifikation technisch bedingten Transportabhängigkeiten, der manuellen Vormerkung von logischen Transportabhängigkeiten und der Überwachung der Abhängigkeiten beim Import.

Änderungs-und-Transport-System-Analyse / Change- and Transport System Analysis / CTSA

Die „Änderungs-und-Transport-System-Analyse / Change- and Transport System Analysis / CTSA“ dient der Ad-Hoc-Analyse einer Menge von Transportaufträgen, der darin enthaltenen Objekte und deren Umfeld. Mit Hilfe dieser Analyse können vor dem Projektimport Abhängigkeiten zu anderen Änderungen / Transportaufträge, deren Produktivsetzungen aktuell noch nicht anstehen, identifiziert werden. Der Umfang der zu importierenden Transportaufträge ist anschließend um diese Transportaufträge zu erweitern, da es ansonsten zu kritischen Objektversionsunterschieden zwischen Quell- und Zielsystem und damit einhergehend zu abweichendem Funktionsverhalten, Inkonsistenzen oder Systemabbrüchen kommen kann.

Transportnachverfolgungsmonitor / Transport Tracking Monitor / TRMO

Nach einem GoLive eines größeren Releases bzw. Projektes besteht häufig der Wunsch eines Nachweises der Transport- und Objektkonsistenz. Für eine Schnellanalyse kann die Funktion „Transportnachverfolgungsmonitor / Transport Tracking Monitor / TRMO“ genutzt werden. Sie können hiermit Transportaufträge von dem System, in dem sie angelegt und freigegeben wurden, bis zu dem System, in das sie importiert wurden, nachverfolgen. Dabei lassen sich sowohl Transportdeltas als auch Reihenfolgeverletzungen identifizieren.

Transportausführungsanalyse / Transport Execution Analysis / TEA

Eine ausführliche Analyse der Lösungslandschaft oder des Releases / Projektes ist mit dem Self-Service „Transportausführungsanalyse (für Projekte) / Transport Execution Analysis (for Project) / TEA(P)“ möglich. Diese Analyse kann vor und/oder nach einem GoLive ausgeführt werden. Sie kann auch unabhängig von einem GoLive ausgeführt werden. In jedem Fall erhalten Sie als Ergebnis einen ausführlichen Analysebericht (Word / HTML / PDF) mit konkreten Handlungsanweisungen zur nachhaltigen Sicherung bzw. Verbesserung der Transport- und Objektkonsistenz.

Importqueue-Web-UI – Importprüfungen / Import Queue Web UI – Import Checks / IQIC

Sämtliche zuvor beschriebenen Funktionalitäten sind ausschließlich für ABAP-Änderungen nutzbar. Für Non-ABAP-Änderungen sind sie nicht anwendbar. Ergänzend zur Funktion „Downgrade-Schutz“ und „Projektschnittmengenprüfung“ ist die Funktion „Importqueue-Web-UI – Importprüfungen / Import Queue Web UI – Import Checks / IQIC“ anzuwenden. Diese Funktion unterstützt die Prüfarten „Prüfung auf Vorgänger“ und „Prüfung auf Downgrades“ für alle ABAP- und fast alle Non-ABAP-Transporte. Es erfolgt ausschließlich eine Analyse der aktuell in der Importqueue zum Import anstehenden oder bereits importierten Transportaufträge, weshalb Überholer nur ein einziges Mal erkannt werden – z.B. nur beim Import in das Qualitätssicherungssystem aber nicht beim Import in das Produktionssystem.

Importhistorie-Web-UI – Analysemodus / Import History Web UI – Analysis Mode / IHAM

Ergänzend zur Funktion „Transportnachverfolgungsmonitor“ ist die Funktion „Importhistorie-Web-UI – Analysemodus / Import History Web UI – Analysis Mode / IHAM“ anzuwenden. Diese Funktion identifiziert Reihenfolgeverletzungen, Downgrades und reparierte Downgrades für alle ABAP- und fast alle NonABAP-Transporte. Es erfolgt ausschließlich eine Analyse der Importhistorie, weshalb unter anderem Transportdeltas nicht erkannt werden können.

Download

Change Request Management Downgrade Protection Handbuch.pdf

Änderung des Internetbrowsers für SAP CRM Web UI funktioniert nicht mit Single Sign On

  1. Bitte stelle sicher, dass Firefox, Google Chrome, Opera etc. dein Standardbrowser ist. Wie das geht ist hier erklärt: https://support.google.com/chrome/answer/95417?hl=en
  2. Ab jetzt sollten die Transaktionen WUI, CRM_UI und SM_CRM diesen Internet Browser und nicht mehr den Microsoft Internet Explorer (IE) nutzen.
  3. Falls die Transaktionen WUI und CRM_UI funktionieren, SM_CRM aber nicht, dann liegt das am Single Sign On (SSO). Es sieht so aus, also ob bei aktivem Single Sign On automatisch der IE genutzt wird. Konkret verifizieren kannst du das mit der Transaktion START_BSP und der BSP-Anwendung „CRM_UI_START“. Der Folgende Artikel bestätigt, dass bei SSO ein IE-ActiveX-Control verwendet wird, um den Aufruf zu realisieren: https://scn.sap.com/thread/1406302

Lösung:

  1. Bitte erstelle einen SAP Incident. Die Erfolgswahrscheinlichkeit ist allerdings sehr gering, da es sich streng genommen um keinen Bug sondern eine Funktionsverbesserung handelt.
  2. Verwende nicht den SAP GUI. Hinterlege und nutze stattdessen den SAP CRM Web UI Link als Windows-Verknüpfung, Browser-Favorit oder Portallink. Teilweise kannst du Username und Passwort als Klartextparameter der URL mitgeben (Verknüpfung, Favorit). Teilweise wird SSO unterstützt (Portallink).

Creating, changing and reading business transaction data using class cl_ags_crm_1o_api

To create, change or read business transaction data in SAP CRM or SAP Solution Manager, you can use low level API function modules CRM_ORDER_READ, CRM_ORDER_MAINTAIN, CRM_*_OW or high level layer BOL/GenIL.

However, sometimes you want to use a simple API which is optimized for single transaction processing. In that case, you should use classes cl_ags_crm_1o_api (and cl_crm_bsp_maintain_1o).

*"----------------------------------------------------------------------
*"Methoden Signatur:
*"  Importing
*"    IV_HEADER_GUID TYPE CRMT_OBJECT_GUID
*"    IV_PROCESS_TYPE TYPE CRMT_PROCESS_TYPE
*"  Exporting
*"    EV_HEADER_GUID TYPE CRMT_OBJECT_GUID
*"----------------------------------------------------------------------
METHOD create_transaction.

*We have to use class cl_crm_bsp_maintain_1o instead of cl_ags_crm_1o_api
*because class cl_ags_crm_1o_api don't support creation of follow-ups yet.
*After creation we need to save or we need to handover buffer from cl_crm_bsp_maintain_1o to cl_ags_crm_1o_api.
*Both ways are not possible. Therefore we free our instance of cl_crm_bsp_maintain_1o and hope the best.
"However this a dirty solution because the instance is still in buffer and marked as "to be saved".
*Therefore we might have side effects when someone uses class cl_crm_bsp_maintain_1o afterwards in same session.
*At the moment that isn't the fact.

  DATA:
    lr_maintain_1o TYPE REF TO cl_crm_bsp_maintain_1o.

  CLEAR ev_header_guid.


*Get Instance of 1O Manager
  CALL METHOD cl_crm_bsp_maintain_1o=>get_instance
    RECEIVING
      rr_maintain_1o = lr_maintain_1o.

*Create Follow Up.
  CALL METHOD lr_maintain_1o->copy_order
    EXPORTING
      iv_process_type    = iv_process_type
      iv_old_header_guid = iv_header_guid
      iv_vona_kind       = 'A' "Use Copy Control
*     iv_template_type   =
    IMPORTING
      ev_new_header_guid = ev_header_guid
    EXCEPTIONS
      error_occurred     = 1
      OTHERS             = 2.

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

  FREE lr_maintain_1o.

ENDMETHOD.

*"----------------------------------------------------------------------
*"Methoden Signatur:
*"  Importing
*"    IV_HEADER_GUID TYPE CRMT_OBJECT_GUID
*"    IV_DESCRIPTION TYPE CRMT_PROCESS_DESCRIPTION
*"    IV_TEXTTYPE TYPE TDID
*"    IV_TEXTID TYPE DOK_ID
*"    IV_TEXTOBJ TYPE DOKU_OBJ
*"    IS_TRACK TYPE /TMWFLOW/TRACK
*"    IS_CONTEXT TYPE ZCIEH_CONTEXT_S
*"----------------------------------------------------------------------
METHOD adjust_short_text.

  DATA:
    lr_instance   TYPE REF TO cl_ags_crm_1o_api,
*   ls_orderadm_h TYPE crmt_orderadm_h_wrk,
    lv_on_database_flag TYPE abap_bool,
    lv_short_text LIKE iv_description.

*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.

*Check if Transaction already exists on Database.
*Getting MODE of ORDERADM_H don't work correct.
  CALL FUNCTION 'CRM_ORDERADM_H_ON_DATABASE_OW'
    EXPORTING
      iv_orderadm_h_guid  = iv_header_guid
    IMPORTING
      ev_on_database_flag = lv_on_database_flag.

*We want to change the description.
  IF iv_description IS NOT INITIAL.

*Get Short Text
    CALL METHOD lr_instance->get_short_text
      IMPORTING
        ev_short_text        = lv_short_text
      EXCEPTIONS
        document_not_found   = 1
        error_occurred       = 2
        document_locked      = 3
        no_change_authority  = 4
        no_display_authority = 5
        no_change_allowed    = 6
        OTHERS               = 7.

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

*Set Short Text if it is empty yet or if Transaction will be created.
    IF lv_short_text IS INITIAL OR lv_on_database_flag = abap_false. "ls_orderadm_h-mode = 'A'.

      CALL METHOD lr_instance->set_short_text
        EXPORTING
          iv_short_text     = iv_description
        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.

    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.

*"----------------------------------------------------------------------
*"Methoden Signatur:
*"  Importing
*"    IV_HEADER_GUID TYPE CRMT_OBJECT_GUID
*"----------------------------------------------------------------------
METHOD save_transaction.

  DATA:
    lr_instance      TYPE REF TO cl_ags_crm_1o_api,
    lt_exception     TYPE crmt_exception_t,
    lt_msg           TYPE /tmwflow/mo_tt_msg,
    lt_saved_objects TYPE crmt_return_objects,
    lv_log_handle TYPE balloghndl.

*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               = 'B'
    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.

*Save Transaction changes to database.
  CALL METHOD lr_instance->save
    EXPORTING
      iv_unlock                  = abap_true
*     iv_save_only_external_data = SPACE
      iv_init                    = abap_true
    IMPORTING
      et_exception               = lt_exception[]
      et_msg                     = lt_msg[]
      et_saved_objects           = lt_saved_objects[]
    CHANGING
      cv_log_handle              = lv_log_handle
    EXCEPTIONS
      error_occurred             = 1
      OTHERS                     = 2.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ELSEIF lt_saved_objects[] IS INITIAL.
    MESSAGE x151(00).
  ENDIF.

  FREE: lr_instance.

ENDMETHOD.

Change user status using method HF_SET_STATUS

*"----------------------------------------------------------------------
*"Methoden Signatur:
*"  Importing
*"    IV_HEADER_GUID TYPE CRMT_OBJECT_GUID
*"    IV_STATUS TYPE J_ESTAT
*"    IV_RESET TYPE OAX
*"----------------------------------------------------------------------
METHOD change_transaction_status.

*Simulate execution of PPF action method HF_SET_STATUS.
*This solution was inspired by report CRM_SOCM_SERVICE_REPORT and action method HF_SET_STATUS.

  DATA:
    lr_methodcall  TYPE REF TO if_ex_exec_methodcall_ppf,
    lr_object      TYPE REF TO object,
    lr_appl_object TYPE REF TO cl_doc_crm_order,
    lr_partner     TYPE REF TO cl_partner_ppf,
    lv_log_handle  TYPE balloghndl,
    lr_container   TYPE REF TO if_swj_ppf_container,
    lv_status      TYPE ppfdtstat.

*Create PPF instance
  CALL METHOD cl_exithandler=>get_instance
    EXPORTING
      exit_name                     = 'EXEC_METHODCALL_PPF'
      null_instance_accepted        = cl_socm_integration=>true
    CHANGING
      instance                      = lr_methodcall
    EXCEPTIONS
      no_reference                  = 1
      no_interface_reference        = 2
      no_exit_interface             = 3
      class_not_implement_interface = 4
      single_exit_multiply_active   = 5
      cast_error                    = 6
      exit_not_existing             = 7
      data_incons_in_exit_managem   = 8
      OTHERS                        = 9.

*Unexpected error...
  IF sy-subrc <> 0 OR lr_methodcall IS NOT BOUND.
    MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

*Get application object.
  CALL FUNCTION 'CRM_ACTION_GET_APPL_OBJECT'
    EXPORTING
      iv_ref_guid    = iv_header_guid
      iv_ref_kind    = 'A'
    IMPORTING
      ev_appl_object = lr_object.

*Unexpected error...
  IF lr_object IS NOT BOUND.
    MESSAGE ID sy-msgid TYPE 'X' NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

  lr_appl_object ?= lr_object.

*We have and need no partner.
  CLEAR:
    lr_partner.

*Create log handle.
  CALL METHOD cl_log_ppf=>create_log
*  EXPORTING
*    ip_object    = 'PPF'
*    ip_subobject = 'PROCESSING'
*    ip_ext_no    =
    RECEIVING
      ep_handle    = lv_log_handle.

*Create and build up PPF Container
  CREATE OBJECT lr_container TYPE cl_swj_ppf_container.

*Unexpected error...
  IF lr_container IS NOT BOUND.
    MESSAGE x151(00).
  ENDIF.

  CALL METHOD lr_container->set_value
    EXPORTING
      element_name = 'USER_STATUS'
      data         = iv_status.

  CALL METHOD lr_container->set_value
    EXPORTING
      element_name = 'RESET_STATUS'
      data         = iv_reset.

*Lock Change Transaction if not already done.
  CALL FUNCTION 'CRM_ORDER_ENQUEUE'
    EXPORTING
      iv_guid           = iv_header_guid
    EXCEPTIONS
      foreign_lock      = 1
      system_failure    = 2
      distributed_lock  = 3
      no_change_allowed = 4
      transferring      = 5
      OTHERS            = 6.

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

*Change Status...
  TRY.
      CALL METHOD lr_methodcall->execute
        EXPORTING
          flt_val            = 'HF_SET_STATUS'
          io_appl_object     = lr_appl_object
          io_partner         = lr_partner
          ip_application_log = lv_log_handle
          ip_preview         = space
          ii_container       = lr_container
          ip_action          = '' "'RESET_BY_CHECK' "'SET_STATUS_BY_PROGRAM'
        RECEIVING
          rp_status          = lv_status.
    CATCH cx_socm_condition_violated.
      MESSAGE x151(00).
  ENDTRY.

*Free objects.
  CALL METHOD cl_log_ppf=>refresh_log
    EXPORTING
      ip_handle = lv_log_handle.

  FREE:
    lr_container,
    lr_partner,
    lr_appl_object,
    lr_methodcall.

*Status was not changed.
*We don't know why. Maybe authority maybe check condition. Maybe bigfoot. ...
  IF lv_status <> 1.
    RAISE no_change_allowed.
  ENDIF.

ENDMETHOD.

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.