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?
- Read GUID internal id of ChaRM ticket
- Read initial actual status and next status according to direction
- Run CRM_STATUS_READ_DB to invoke next status
- 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
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 i DEFAULT 1. " " Number of sec. to wait before next status reading cycle
PARAMETER p_max TYPE i 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=>factory( iv_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
TYPES: tyr_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.
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
DATA: v_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.
TYPES: tyt_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.
DATA: r_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_guid( iv_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.
DATA: lr_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