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.