Sunday, December 11, 2016

Run CRM_SOCM_SERVICE_REPORT in synchronized mode

The SAP report CRM_SOCM_SERVICE_REPORT runs by default in asynchronous mode. So it is hard to create appropriate batch to reach transition throughout more statuses at once. For this reason it is necessary to wait for completion of each single status change step before next status could be processed. What we need is to be ensured that very last status was correctly stored within database. It seems to be an easy job, but due to buffering of standard SAP framework (function group CRM_STATUS_DB) it becomes a bit tricky.

I found really very helpful the following site http://www.xn--hybrid-eichhrnchen-o3b.de/how-to-detect-status-changes/

So what Lego cubes do we need? 


  1. Read GUID internal id of ChaRM ticket
  2. Read initial actual status and next status according to direction
  3. Run CRM_STATUS_READ_DB to invoke next status 
  4. Read changed status once again in a loop to be ensured that it took a place 


GUID 

By exploration of CRM_STATUS_READ_DB you can find the needed algorithm within Logical database the name CRMLDB_SERVICE_MON (Use SE36 for exploration). The extracted logic could be found in below get_guid method.

ACTUAL STATUS 

Method get_guid already fills buffers of function group CRM_STATUS_DB. So the first time reading status could be realized either by FM CRM_STATUS_READ_OW or  CRM_STATUS_READ_DB, both reads from memory buffer.

Note:
  • CRM_STATUS_READ_OW - reads only from buffer
  • CRM_STATUS_READ_DB  - reads from buffer as well, only if buffer is empty it touches database

Game has changed when we want to read status again after execution of CRM_SOCM_SERVICE_REPORT. Everything is buffered and both mentioned FMs do not help. We have to flush SAP buffers before CRM_STATUS_READ_DB run. For this purpose check below method clear_sap_crm_status_buffer.

NEXT STATUS

That is easy part, just call FM SOCM_GET_NEXT_STATUS_VALUE. It returns next status to be set according to direction parameter (forward = "+" or backward "-").



Check whole solution

Main report 


REPORT  /YourOwnSolmanNameSpace/shift_phase_sync MESSAGE-ID 00.

INCLUDE crm_direct.
INCLUDE /YourOwnSolmanNameSpace/i_shift_phase_synctop.
INCLUDE /YourOwnSolmanNameSpace/i_shift_phase_sync_cl.


************************************************************************
* SELECTION SCREEN
************************************************************************
PARAMETER p_tx_id TYPE crmt_object_id_db OBLIGATORY" Solman - Ticket id
PARAMETER p_back  TYPE xfeld AS CHECKBOX.            " Checkbox controls next status direction
PARAMETER p_wait  TYPE DEFAULT 1.  "               " Number of sec. to wait before next status reading cycle
PARAMETER p_max   TYPE DEFAULT 60.                 " Max. number of loops to wait for status change


************************************************************************
AT SELECTION-SCREEN.
************************************************************************
  " Check transaction id / Solman id and Max loop treshold
  IF p_tx_id IS INITIAL OR p_max IS INITIAL ).
    MESSAGE e002.
  ENDIF.


************************************************************************
START-OF-SELECTION.
************************************************************************

  " Go back or forward with the status
  IF p_back abap_true.
    gv_direction '-'" backward
  ELSE.
    gv_direction '+'" forward
  ENDIF.

  " it reads GUID, actual + next status
  gr_status_check lcl_status_check=>factoryiv_tx_id     p_tx_id
                                               iv_direction gv_direction
  ).

  SUBMIT crm_socm_service_report
    WITH sobject EQ p_tx_id
    WITH drctn EQ gv_direction
    WITH called EQ 'X'
    WITH next EQ 'X'
    WITH noupd EQ space
    AND RETURN.

  WHILE gv_stop_flag EQ abap_false.
    IF gr_status_check->v_status_actual EQ gr_status_check->v_status_next.
      " Target status has been reached
      gv_stop_flag abap_true.
      MESSAGE s001 WITH 'Target status "' gr_status_check->v_status_next '" has been reached'.
    ELSE.
      " Check max. allowed execution time
      gv_loop_counter gv_loop_counter + 1.
      IF gv_loop_counter >= p_max.
        gv_stop_flag abap_true.
        MESSAGE e001 WITH 'Target status "' gr_status_check->v_status_next '" was not reached in given time limit'.
      ELSE.
        " Recheck status again after a small wait state
        WAIT UP TO p_wait SECONDS.
        gr_status_check->reload_status_actual).
      ENDIF.
    ENDIF.
  ENDWHILE.


Include /YourOwnSolmanNameSpace/i_shift_phase_synctop


TYPEStyr_object_id   TYPE RANGE OF crmt_object_id_db,
       tyr_object_type TYPE RANGE OF crmt_subobject_category_db,
       tyr_chanat      TYPE RANGE OF orderadm_h-changed_at.

CLASS  lcl_status_check DEFINITION DEFERRED.

DATA:  gv_direction    TYPE char1,
       gr_status_check TYPE REF TO lcl_status_check,
       gv_stop_flag    TYPE abap_bool VALUE abap_false,
       gv_loop_counter TYPE i.



Include /YourOwnSolmanNameSpace/i_shift_phase_sync_cl


CLASS lcl_status_check DEFINITION.

  PUBLIC SECTION.

    CLASS-DATA:
        o_status_check              TYPE REF TO lcl_status_check" Used just within factory method

    DATAv_tx_id                   TYPE crmt_object_id_db READ-ONLY" Solman ticket id
          v_guid                    TYPE crmt_object_guid READ-ONLY,  " internal GUID
          v_stsma                   TYPE j_stsma READ-ONLY,           " Status Profile e.g. 'ZMMJHEAD'
          v_direction               TYPE char1 READ-ONLY,             " + = forward, - = backward
          v_process_type            TYPE crmt_process_type READ-ONLY" Business Transaction Type e.g. 'ZMMJ'
          v_status_actual           TYPE crm_j_status READ-ONLY,      " Current status
          v_status_next             TYPE crm_j_status READ-ONLY.      " Nearest next status

    CLASS-METHODS:
      factory
        IMPORTING iv_tx_id          TYPE crmt_object_id_db
                  iv_direction      TYPE char1
        RETURNING value(ro_obj)     TYPE REF TO lcl_status_check.

    METHODS:
      constructor
        IMPORTING iv_tx_id          TYPE crmt_object_id_db
                  iv_direction      TYPE char1,
      get_status_actual
        IMPORTING iv_guid           TYPE crmt_object_guid
                  iv_read_from_db   TYPE abap_bool
        EXPORTING ev_status         TYPE crm_j_status
                  ev_stsma          TYPE j_stsma
        RAISING cx_ags_ejr_external_status_crm,
      get_status_next
        IMPORTING iv_process_type   TYPE crmt_process_type
                  iv_status_current TYPE crm_j_status
                  iv_direction      TYPE char1
                  iv_stsma          TYPE j_stsma
        RETURNING value(rv_status)  TYPE crm_j_status
        RAISING cx_ags_ejr_external_status_crm,
      reload_statuses,
      reload_status_actual.

  PRIVATE SECTION.

    TYPEStyt_crmt_jest_buf_tab_loc TYPE STANDARD TABLE OF crmt_jest_buf,
           tyt_crmt_jsto_buf_tab_loc TYPE STANDARD TABLE OF crmt_jsto_buf.

    DATAr_ex                      TYPE REF TO cx_ags_ejr_external_status_crm.

    METHODS:
      create_questions
        IMPORTING
          ir_sobject                TYPE tyr_object_id
          ir_objtype                TYPE tyr_object_type
          iv_pstatop                TYPE xfeld
        EXPORTING
          ev_quparts_ta             TYPE crmt_report_querypart_ta,

      clear_sap_crm_status_buffer,

      get_guid
        IMPORTING iv_tx_id          TYPE crmt_object_id_db
        RETURNING value(rv_guid)    TYPE crmt_object_guid.

ENDCLASS.                    "lcl_status_check DEFINITION


*----------------------------------------------------------------------*
*       CLASS lcl_status_check IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_status_check IMPLEMENTATION.

  METHOD get_status_actual.

*    Note: Status reading possible ways:
*     FM CRM_STATUS_READ_OW - FROM BUFFER only
*     FM CRM_STATUS_READ_DB - FROM BUFFER, if buffer is empty it reads from DATABASE
*     FM CRM_EVENT_PASS_INFO_EXE_OW
*     CL_HF_HELPER=>GET_ESTAT_OF_CHANGE_DOCUMENT

    IF iv_guid IS INITIAL ).
      RETURN.
    ENDIF.

    IF iv_read_from_db EQ abap_true.
      me->clear_sap_crm_status_buffer).
    ENDIF.

    DATA lt_crmt_status TYPE crmt_status_wrkt.
    CALL FUNCTION 'CRM_STATUS_READ_DB'
    EXPORTING
      iv_guid                iv_guid
*     IV_SINGLE_STATUS       =
*     IV_ONLY_CHANGED        = FALSE
    IMPORTING
      et_status_wrk          lt_crmt_status
*     EV_ACTIVE_OLD          =
*   EXCEPTIONS
*     ERROR_OCCURRED         = 1
*     OTHERS                 = 2
      .
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE cx_ags_ejr_external_status_crm
        EXPORTING
          textid  'GENERIC_MSG'
          msg_txt 'Error reading actual status via CRM_STATUS_READ_DB.'.
    ENDIF.

    FIELD-SYMBOLS<fs_crmt_status> LIKE LINE OF lt_crmt_status.
    LOOP AT lt_crmt_status ASSIGNING <fs_crmt_status>
      WHERE user_stat_proc IS NOT INITIAL AND active abap_true ).
      ev_status <fs_crmt_status>-status.
      ev_stsma  <fs_crmt_status>-user_stat_proc.
      EXIT.
    ENDLOOP.

  ENDMETHOD.                    "get_status_actual

  METHOD get_status_next.

    IF iv_process_type IS INITIAL OR iv_status_current IS INITIAL )
      OR iv_direction IS INITIAL OR iv_stsma IS INITIAL ).
      RETURN.
    ENDIF.

    DATA ls_status_next TYPE  vsocm_stat_propt.
    CALL FUNCTION 'SOCM_GET_NEXT_STATUS_VALUE'
      EXPORTING
        im_process_type         iv_process_type
        im_status_current       iv_status_current
        im_direction            iv_direction
        iv_stsma                iv_stsma
      IMPORTING
        ex_status_next          ls_status_next
* EXCEPTIONS
*   NO_NEXT_STATUS          = 1
*   OTHERS                  = 2
              .
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE cx_ags_ejr_external_status_crm
        EXPORTING
          textid  'GENERIC_MSG'
          msg_txt 'Error reading next status via SOCM_GET_NEXT_STATUS_VALUE.'.
    ENDIF.

    rv_status ls_status_next-user_status.

  ENDMETHOD.                    "get_status_next

  METHOD reload_statuses.

    o_status_check->reload_status_actual).

    TRY.
        v_status_next   o_status_check->get_status_next(
          iv_process_type   v_process_type
          iv_status_current v_status_actual
          iv_stsma          v_stsma
          iv_direction      v_direction
        ).
      CATCH cx_ags_ejr_external_status_crm INTO r_ex.
        MESSAGE r_ex->msg_txt TYPE 'E'.
    ENDTRY.

  ENDMETHOD.                    "reload_statuses

  METHOD reload_status_actual.
    TRY.
        o_status_check->get_status_actual(
          EXPORTING
            iv_guid           v_guid
            iv_read_from_db   abap_true
          IMPORTING
            ev_status v_status_actual
            ev_stsma  v_stsma
          ).
      CATCH cx_ags_ejr_external_status_crm INTO r_ex.
        MESSAGE r_ex->msg_txt TYPE 'E'.
    ENDTRY.
  ENDMETHOD.                    "reload_status_actual

  METHOD factory.

    IF o_status_check IS NOT BOUND.
      " create initial instance
      CREATE OBJECT o_status_check
        EXPORTING
          iv_tx_id     iv_tx_id
          iv_direction iv_direction.
    ELSE.
      IF iv_tx_id NE o_status_check->v_tx_id.
        " new object id came => reload all
        FREE o_status_check.

        CREATE OBJECT o_status_check
          EXPORTING
            iv_tx_id     iv_tx_id
            iv_direction iv_direction.
      ELSE.
        " we already have the right object id, it means guid would be also ok => just read statuses again
        o_status_check->reload_statuses).
      ENDIF.
    ENDIF.

    ro_obj o_status_check.
  ENDMETHOD.                    "factory

  METHOD constructor.

    v_tx_id     iv_tx_id.
    v_direction iv_direction.

    " get guid based on object id - ticket id
    v_guid get_guidiv_tx_id v_tx_id ).

    " get processing type
    cl_hf_helper=>get_proc_type_of_chng_doc(
      EXPORTING
        im_change_document_id v_guid
      RECEIVING
        return                v_process_type
    ).


    TRY.
        " get current ticket status
        get_status_actual(
          EXPORTING
            iv_guid           v_guid
            iv_read_from_db   abap_false " Buffers are filled already by method get_guid, Note: ('(SAPLCRMBSVA)JSTO_BUF[]') to <gt_jsto_buf>
          IMPORTING
            ev_status v_status_actual
            ev_stsma  v_stsma
        ).

        " get next ticket status
        v_status_next   get_status_next(
                            iv_process_type   v_process_type
                            iv_status_current v_status_actual
                            iv_stsma          v_stsma
                            iv_direction      v_direction
                          ).
      CATCH cx_ags_ejr_external_status_crm INTO r_ex.
        MESSAGE r_ex->msg_txt TYPE 'E'.
    ENDTRY.

  ENDMETHOD.                    "constructor

  METHOD create_questions.

    " Note: This method is derivation of subroutine f_qupartfill from logical database CRMLDB_SERVICE_MON

    DATA:
    lr_objtype         TYPE RANGE OF crmd_orderadm_h-object_type,
    ls_objtype         LIKE LINE OF lr_objtype,
    lv_qupart          LIKE LINE OF ev_quparts_ta,
    lv_qupart_and      LIKE LINE OF ev_quparts_ta,
    lv_subobj_category TYPE crmt_subobject_category,
    lr_chanat          TYPE tyr_chanat.

    " get log objets
    CALL METHOD cl_crm_report_qupart=>get_qupart_by_token
      EXPORTING
        iv_token    'AND'
        it_rangetab lr_chanat[]
      IMPORTING
        ev_qupart   lv_qupart_and-querypart.

    " For each range we supply an querypart. Check is done before calling
    " The framework by reading the range tables.

    IF iv_pstatop true.
      " Special status query for open transactions (open, in proc, released)
      CALL METHOD cl_crm_report_qupart=>get_qupart_by_token
        EXPORTING
          iv_entityname  gc_objectname_reporting-status
          iv_fieldname   'STAT'
          iv_token       'STA'
          iv_status      true
          iv_status_type 'OPN'
        IMPORTING
          ev_qupart      lv_qupart-querypart.
      APPEND lv_qupart TO ev_quparts_ta.
      APPEND lv_qupart_and TO ev_quparts_ta.
    ENDIF.

    " Ticket ids
    DESCRIBE TABLE ir_sobject LINES sy-tfill.
    IF sy-tfill > 0.

      CALL METHOD cl_crm_report_qupart=>get_qupart_by_token
        EXPORTING
          iv_entityname gc_objectname_reporting-orderadm_h
          iv_fieldname  'OBJECT_ID'
          iv_token      'RAN'
          it_rangetab   ir_sobject[]
        IMPORTING
          ev_qupart     lv_qupart-querypart.
      APPEND lv_qupart TO ev_quparts_ta.
      APPEND lv_qupart_and TO ev_quparts_ta.
    ENDIF.

    " A Neccessary parameter is put as the last part so that AND may not be
    " taken care of.

    CALL METHOD cl_crm_report_qupart=>get_qupart_by_token
      EXPORTING
        iv_entityname gc_objectname_reporting-orderadm_h
        iv_fieldname  'OBJECT_TYPE'
        iv_token      'RAN'
        it_rangetab   ir_objtype[]
      IMPORTING
        ev_qupart     lv_qupart-querypart.
    APPEND lv_qupart TO ev_quparts_ta.
    APPEND lv_qupart_and TO ev_quparts_ta.

    " if selection is done on item BO additionally specify header BO, so that
    " we do not get sales order items from sales order header

    DESCRIBE TABLE ir_objtype LINES sy-tfill.
    IF sy-tfill > 1.
*   check header business object also specified
      SELECT subobj_category INTO lv_subobj_category
      FROM crmc_subob_cat
      WHERE subobj_category IN ir_objtype.
        ls_objtype-sign   'I'.
        ls_objtype-option 'EQ'.
        ls_objtype-low    lv_subobj_category.
        APPEND ls_objtype TO lr_objtype.
      ENDSELECT.
      IF sy-subrc IS INITIAL.
        CALL METHOD cl_crm_report_qupart=>get_qupart_by_token
          EXPORTING
            iv_entityname gc_objectname_reporting-orderadm_h
            iv_fieldname  'OBJECT_TYPE_H'
            iv_token      'RAN'
            it_rangetab   lr_objtype[]
          IMPORTING
            ev_qupart     lv_qupart-querypart.
        APPEND lv_qupart TO ev_quparts_ta.
      ENDIF.
    ENDIF.

  ENDMETHOD.                    "create_questions

  METHOD clear_sap_crm_status_buffer.

    " Refresh SAP buffers from function group CRM_STATUS_DB

    FIELD-SYMBOLS<gt_jest_buf> TYPE tyt_crmt_jest_buf_tab_loc,
                   <gt_jsto_buf> TYPE tyt_crmt_jsto_buf_tab_loc.

    ASSIGN('(SAPLCRMBSVA)JSTO_BUF[]'TO <gt_jsto_buf>,
            ('(SAPLCRMBSVA)JEST_BUF[]'TO <gt_jest_buf>.

    IF <gt_jsto_buf> IS ASSIGNED.
      REFRESH <gt_jsto_buf>.
    ENDIF.

    IF <gt_jest_buf> IS ASSIGNED.
      REFRESH <gt_jest_buf>.
    ENDIF.

  ENDMETHOD.                    "clear_sap_crm_status_buffer

  METHOD get_guid.

    DATAlr_question TYPE REF TO cl_crm_report_question,
          lt_object_id    TYPE tyr_object_id,
          ls_object_id    LIKE LINE OF lt_object_id,
          lt_object_type  TYPE tyr_object_type,
          ls_object_type  LIKE LINE OF lt_object_type,
          lt_object_type_item   TYPE  crmt_bt_bti_assi_tab,
          lt_querypart_ta TYPE crmt_report_querypart_ta.

    " Ticket number
    ls_object_id-sign   'I'.
    ls_object_id-option 'EQ'.
    ls_object_id-low    iv_tx_id.
    APPEND ls_object_id TO lt_object_id.


    CALL FUNCTION 'CRM_ORDER_BT_BTI_ASSI_SEL_CB'
      EXPORTING
        iv_object_type      gc_object_type-service
      IMPORTING
        et_object_type_item lt_object_type_item.

    " header selection only, delete item business objects
    FIELD-SYMBOLS<fs_object_type_item>  TYPE  crmc_bt_bti_assi.
    LOOP AT lt_object_type_item ASSIGNING <fs_object_type_item>.
      DELETE lt_object_type WHERE low <fs_object_type_item>-object_type_item.
    ENDLOOP.

    " objecttype must always be specified (default service)
    IF lt_object_type[] IS INITIAL.
      ls_object_type-sign   'I'.
      ls_object_type-option 'EQ'.
      ls_object_type-low    gc_object_type-service.
      APPEND ls_object_type TO lt_object_type.
    ENDIF.

    create_questions(
      EXPORTING
        ir_sobject lt_object_id
        ir_objtype lt_object_type
        iv_pstatop abap_true
      IMPORTING
        ev_quparts_ta lt_querypart_ta
    ).

    CREATE OBJECT lr_question
      EXPORTING
        it_query           lt_querypart_ta[]
        iv_accessrule_name 'CL_CRM_REPORT_ACCRULE_ONEORDER'.

    lr_question->refresh).

    IF lr_question IS BOUND ).
      " get first guid
      FIELD-SYMBOLS<fs_guid_row> TYPE crmt_report_guidlist.
      LOOP AT lr_question->gt_guidlist ASSIGNING <fs_guid_row>.
        rv_guid <fs_guid_row>-guid.
        EXIT.
      ENDLOOP.
    ENDIF.

  ENDMETHOD.                    "get_guid

ENDCLASS.                    "lcl_status_check IMPLEMENTATION