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







Saturday, September 24, 2016

SELECTION SCREEN and events like ONFOCUS ONBLUR for Search help?

Are you coming to ABAP from web development folks? Welcome! :) Maybe you would become a bit confused with limited set of events for ABAP Selection screens.

If you previously used to work with web form's events like onBlur, onFocus you will be here a bit disappointed. I will not promise any miracle, these events simply do not exists here at all.

But the situation how to react between form inputs is not so hopeless.

AT first there is a list of real supported events:



What we can do if we want to make a search help based on the value of previous input?

We have very limited possibilities, but luckily for this case there exists a magic workaround.

Imagine a situation on the picture below. You have a simple input for database table name. And you want to offer to user directly the list of its fields for sort. This is not possible to reach with existing events. Ok, you can call something like: "AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_sort" and then call some your nice logic with table name as a parameter. But during this call table name (previous above input) would be still empty, no table name is filled at this time, sorry :(




What to do??


You can use existing event "AT SELECTION SCREEN" event. Ok, it works partially, because it would demand some user action like hit the ENTER key or click on somewhat on the toolbar. It is not the best choice.

Or?


We can use just "AT SELECTION-SCREEN ON VALUE" but with a bit tuned coding using FM the name RS_SELECTIONSCREEN_READ. See the below code.  I removed some checks and logic to keep the coding simple.

The miracle came. We obtain a search help directly by clicking on its icon based on the value of the field above.


Code snippets:

Screen:


  SELECTION-SCREEN BEGIN OF BLOCK frame1.
    " Name of SQL table
    SELECTION-SCREEN BEGIN OF LINE.
      SELECTION-SCREEN COMMENT 1(25TEXT-001.
      SELECTION-SCREEN POSITION 26"POS_LOW
      PARAMETERSp_tab LIKE dd02l-tabname.
    SELECTION-SCREEN END OF LINE.
    " Sort by table column
    SELECTION-SCREEN BEGIN OF LINE.
      SELECTION-SCREEN COMMENT 1(25TEXT-005.
      SELECTION-SCREEN POSITION 26.
      PARAMETERSp_sort TYPE slis_fieldname.
    SELECTION-SCREEN END OF LINE.
    " Filter one column
    SELECTION-SCREEN BEGIN OF LINE.
      SELECTION-SCREEN COMMENT 1(25TEXT-006.
      SELECTION-SCREEN POSITION 26.
      PARAMETERSp_filtr TYPE slis_fieldname.
      SELECTION-SCREEN COMMENT 60(10TEXT-007.
      SELECTION-SCREEN POSITION 71.
      PARAMETERSp_c_val TYPE string.
    SELECTION-SCREEN END OF LINE.
  SELECTION-SCREEN END OF BLOCK frame1.

Events:

Its a simple call of class method for both inputs (sorting + filtering) in this case.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_sort.
  gr_table->f4_for_colnameiv_scr_field_name 'P_SORT' ).

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_filtr.
  gr_table->f4_for_colnameiv_scr_field_name 'P_FILTR' ).



Search help code:


Method declaration

  PRIVATE SECTION.

    TYPESBEGIN OF sty_col,
      fieldname TYPE dd03l-fieldname,
    END OF sty_col.
    TYPEStty_cols TYPE STANDARD TABLE OF sty_col.
    METHODS:
      f4_for_colname
        IMPORTING iv_scr_field_name TYPE help_info-dynprofld.

Method implementation


  METHOD f4_for_colname.

    DATAit_cols      TYPE STANDARD TABLE OF sty_col,
          it_scr_field TYPE STANDARD TABLE OF rsselread,
          ls_scr_field LIKE LINE OF it_scr_field.

    MOVE 'P_TAB' TO ls_scr_field-name.
    MOVE 'P' TO ls_scr_field-kind" P for parameter, S for select option
    APPEND ls_scr_field TO it_scr_field.

    CALL FUNCTION 'RS_SELECTIONSCREEN_READ'
      EXPORTING
        program     sy-repid
        dynnr       sy-dynnr
      TABLES
        fieldvalues it_scr_field.

    READ TABLE it_scr_field INTO ls_scr_field INDEX 1.

    IF ls_scr_field-fieldvalue IS NOT INITIAL.
      TRANSLATE ls_scr_field-fieldvalue TO UPPER CASE" table name
      SELECT fieldname
        FROM dd03l
        INTO CORRESPONDING FIELDS OF TABLE it_cols
        WHERE tabname ls_scr_field-fieldvalue.
    ENDIF.

    CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST'
      EXPORTING
        retfield    'fieldname' " from itab
        dynpprog    sy-repid
        dynpnr      sy-dynnr
        dynprofield iv_scr_field_name " e.g. 'P_SORT'
        value_org   'S' " structure
      TABLES
        value_tab   it_cols.

  ENDMETHOD.                    "f4_for_colname


Sources:


Sunday, July 31, 2016

Source code exploration using regular expressions


Once I faced a situation when I need to remove hard coded values from a huge program. There was no way to do it manually. There was really enough various combinations of these numbers. And to use "Find" from SE80 for each one was simply a nightmare. I came to a conclusion to create a small tool reading source code of any program with all its includes with ability to search them by regex. And here it comes.

Usage example


To use the tool you have to understand regular expressions a bit. The tool would be as much powerful as are your regular expressions skills.


Output could look like e.g.


Regex examples for source code exploration tool

Below you can find input regular expressions to reach in source code:

Any 4 digit number
^.+(\d{4}).+$

Any 4 digit number within apostrophes
^.+(')(\d{4})(').+$
  
Any 4 digit number within apostrophes but not in commented rows
^[^\*].+(')(\d{4})(').+$

Any 4 digit number within apostrophes but in commented rows only
^[*].+(')(\d{4})(').+$

Any word (not a number) within apostrophes – e.g. some non numeric literals
^[^\*].+(')(\w+)(').+$

Any 4 digit number 
1. within apostrophes 
2. not in commented rows 
3. avoiding words “bildschirm” or “screen” or “dynnr” preceding the digit
^[^\*]((?!bildschirm|screen|dynnr).)*(')(\d{4})(').+$
  
A four digit number 
starting with “09”
and continues with any two digits number
it includes sub strings
^.+(09)(\d{2}).+$

Search for usernames 
starting with “IT”
and continues with any five digits number
note: it can discover hardcoded behavior for certain users
^.+(IT)(\d{5}).+$

  

Source code 

*&---------------------------------------------------------------------*
*& Report  Y_REPORT_REGEX_SEARCH
*&
*&---------------------------------------------------------------------*
*&
*& Search source code by regex
*&---------------------------------------------------------------------*

REPORT y_report_regex_search.

TABLEStstctrdir.

*******************************************************************
*   SCREENS - choose transaction or program
*******************************************************************
SELECTION-SCREEN BEGIN OF BLOCK frame1 WITH FRAME TITLE text-001.
" transaction
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERSp_trans  RADIOBUTTON GROUP grp1 USER-COMMAND flagcommand" DEFAULT 'X'.
SELECTION-SCREEN COMMENT 3(60text-002" Transaction
SELECTION-SCREEN END OF LINE.
" program
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERSp_prog  RADIOBUTTON GROUP grp1 DEFAULT 'X'.
SELECTION-SCREEN COMMENT 3(60text-003" Program
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN SKIP.
SELECT-OPTIONS so_tcode FOR tstc-tcode NO INTERVALS" Transaction Code
SELECT-OPTIONS so_prog FOR trdir-name NO INTERVALS.  " Program name

SELECTION-SCREEN END OF BLOCK frame1.


SELECTION-SCREEN BEGIN OF BLOCK frame2 WITH FRAME TITLE text-004.
PARAMETERS p_regex TYPE LENGTH 100 LOWER CASE" Regular expression
SELECTION-SCREEN END OF BLOCK frame2.

*******************************************************************
AT SELECTION-SCREEN OUTPUT " PBO action
*******************************************************************
  " Set parameter according to radio
  LOOP AT SCREEN.
    IF p_prog <> 'X'.
      IF screen-name CS 'so_prog'.
        screen-active 0.
        MODIFY SCREEN.
        CONTINUE.
      ENDIF.
    ELSEIF p_trans <> 'X'.
      IF screen-name CS 'so_tcode'.
        screen-active 0.
        MODIFY SCREEN.
        CONTINUE.
      ENDIF.
    ENDIF.
  ENDLOOP.


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

* Get program name for transaction
  CONDENSE so_tcode-low NO-GAPS.
  IF p_trans EQ 'X'.
    CLEAR so_prog.
    SELECT SINGLE pgmna
      FROM tstc
      INTO so_prog-low
      WHERE tcode so_tcode-low.
    IF sy-subrc NE 0.
      WRITE/'Transaction does not exist : 'so_tcode-low.
      EXIT.
    ENDIF.
  ELSEIF p_prog EQ 'X'.
    SELECT SINGLE name
    FROM trdir
    INTO so_prog-low
    WHERE name so_prog-low.
    IF sy-subrc NE 0.
      WRITE/'Program does not exist : 'so_prog-low.
      EXIT.
    ENDIF.
  ENDIF.

  CONDENSE so_prog-low NO-GAPS.
  IF so_prog-low IS INITIAL.
    WRITE/'There is no program given.'.
    EXIT.
  ENDIF.

* Internal table holding include names
  TYPES:
  BEGIN OF ts_includes,
  prgname(40),
  END OF ts_includes.

  DATAlt_includes TYPE STANDARD TABLE OF ts_includes,
        ls_includes LIKE LINE OF lt_includes,
        lt_lines    TYPE TABLE OF string,
        ls_line     LIKE LINE OF lt_lines,
        lv_line_num        TYPE i,
        lv_lines_total_num TYPE i.

  DATAlc_matcher TYPE REF TO cl_abap_matcher,
        lv_match   TYPE LENGTH 1,
        lv_regex   TYPE string,
        lv_exception_detail TYPE string,
        oref   TYPE REF TO cx_root.

* Get include list
  CALL FUNCTION 'GET_INCLUDETAB'
    EXPORTING
      progname so_prog-low " '/FDSEU/CP01_OVERVIEW' 'SAPMM07M'
    TABLES
      incltab  lt_includes.

  MOVE p_regex TO lv_regex.

* Add body of main program into itab to be explored
  APPEND so_prog-low TO lt_includes.

  LOOP AT lt_includes INTO ls_includes .

    REFRESH lt_lines.
    READ REPORT ls_includes-prgname INTO lt_lines.

    CLEAR lv_line_num.

    LOOP AT lt_lines INTO ls_line.

      lv_line_num lv_line_num + 1.

      TRY.
          " Does string match the regex pattern?
          FREE lc_matcher.
          lc_matcher cl_abap_matcher=>create(
                      pattern     lv_regex
                      ignore_case abap_true
                      text        ls_line ).
          lv_match lc_matcher->match).
        CATCH cx_root INTO oref.
          lv_exception_detail oref->get_text).
          WRITE:'Regex error occured : 'lv_exception_detail.
          STOP.
      ENDTRY.
      IF lv_match EQ abap_true.
        WRITE:'INCLUDE: 'ls_includes-prgname'| LINE: 'lv_line_num'| CODE: 'ls_line.
        CLEAR lv_match.
      ENDIF.
    ENDLOOP" lines

    lv_lines_total_num lv_lines_total_num + lv_line_num.

  ENDLOOP" includes

  WRITE:/,'Total number of explored lines: 'lv_lines_total_num.



Regular expressions sources

Regular expressions are common technique used in many various programming languages. These are also included within ABAP.

Basic understanding
http://zevolving.com/2013/10/abap-regex-regular-expressions/

SAP/SDN comprehensive PDF document
http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/902ce392-dfce-2d10-4ba9-b4f777843182?overridelayout=true&49533857875589

https://help.sap.com/abapdocu_70/en/ABENREGEX_SYNTAX.htm

Regex toy – Regular expressions tester 

You can test your own patterns in standard SAP program the name “DEMO_REGEX_TOY”.

Sources: http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/1f9cd701-0b01-0010-87b8-f86f9b7b823f?QuickLink=index&overridelayout=true&5003637741589

For example pick up all lines not containing words “cute” or “fast”, do not care for any apostrophes.
Regex: ^((?!cute|fast).)*$




Other sources: