Tuesday, March 4, 2014

Vendor filtering - The Collective search help exit for KRED

Intro

This article describes one really common task, how to filter vendors of too much generous search help based on authorization object. The solution is search help exit. Ok, here we focus on the solution for SAP collective search help the name KRED, which is widely used in many transactions e.g. FK03, XK03, MK03 and so on. The main idea for below solution is catched from Brad Bohn suggestion, see SAP forum at  http://scn.sap.com/thread/1831345.

Filtered LIFNR and it's search help exit



The collective search help KRED

contains quite a lot of elementary search helps. We can assign to every sub search help it is own search help exit. Ok, it would work, but lose central management point and it brings furthermore some more work.




The nice way seems to be usage of search help exit directly on the KRED search help itself.

When you try to establish your own function module for search help exit, you will be asked for Access key for your installation. Here ask your BC team.



The elementary search help exit injection 

If you do that you will discover one unpleasant thing. The return data are not processed through your custom search help exit on collective level. So, what to do? Fortunately we are given the list of elementary search helps inside the collective one. See internal table SHLP_TAB[]. Each search help contains structure intdescr and it has the field selmexit, what is the name of the search help exit. We can programmatically inject our own search help exit for each one search help in a loop.




See the code below:

  constantscv_root_shlp_name type shlpname value 'KRED',
             cv_shlp_exit_name type string value '/YourNamespace/H_FI_F4UT_KRED'.
  
field-symbols<fs_shlp> type line of shlp_desct.

  
if shlp-shlpname eq cv_root_shlp_name and shlp_tab[] is not initial ).
    
loop at shlp_tab[] assigning <fs_shlp>.
      
if not <fs_shlp>-intdescr is initial.
        <fs_shlp>
-intdescr-selmexit cv_shlp_exit_name.
      
endif.
    
endloop.
  
endif.

The post selection filtering

Now comes the task of filtering the data. We used here the same func. module for collective search help and it is elementary search helps. So filtering has to be active just for elementary helps.

There is more events triggering our search help exit. We need to focus on just one, the POSTSEL. The post selection is the right place to filter data returned to user before displaying.

Here is just worthy to mention that looping over authorization object check is quite slow solution. The better and faster way is to load all fields of authorization object into internal table and process it by yourself.

Authorization object



Just action number 3 the displaying is what we are looking for.



Classic slow/simple code example

    DATA: i TYPE i.
    LOOP AT results_tab.
      i = sy-tabix.
      AUTHORITY-CHECK OBJECT 'F_LFA1_GRP'
               ID 'KTOKK' FIELD results_tab-ktokk
               ID 'ACTVT' FIELD '03'. "For Display
      IF sy-subrc <> 0.
*       Delete the corresponding entry in RECORD_TAB.
        DELETE record_tab INDEX i.
*       Keep SY-TABIX of record_tab and results_tab synchronous
        DELETE results_tab.
      ENDIF.
    ENDLOOP.


Here we need to find a bit faster solution over classic check above.

Read authorization object at once

      CONSTANTS     cv_auth_object TYPE ust12-objct VALUE 'F_LFA1_GRP'.
      FIELD-SYMBOLS <fs_usvalues> TYPE usvalues.
      DATA: lt_usvalues TYPE STANDARD TABLE OF usvalues.

      CALL FUNCTION 'SUSR_USER_AUTH_FOR_OBJ_GET'
        EXPORTING
          user_name  = sy-uname
          sel_object = cv_auth_object
        TABLES
          values     = lt_usvalues.



Action based filtering at first

        constants cv_allowed_actvt type value 3.
        loop at lt_usvalues assigning <fs_usvalues>.
          " Check ACTVT ( later filter KTOKK by ACTVT)
          if <fs_usvalues>-field eq 'ACTVT'.
            if <fs_usvalues>-von eq '*'.
              lv_actvt_ok abap_true.
              exit.
            elseif <fs_usvalues>-von is not initial and <fs_usvalues>-bis is not initial ).
              move <fs_usvalues>-von to lv_actvt.
              if cv_allowed_actvt eq lv_actvt.
                lv_actvt_ok abap_true.
                exit.
              elseif cv_allowed_actvt ge lv_actvt.
                move <fs_usvalues>-bis to lv_actvt.
                if cv_allowed_actvt le lv_actvt.
                  lv_actvt_ok abap_true.
                  exit.
                endif.
              endif.
            elseif <fs_usvalues>-von is not initial ).
              move <fs_usvalues>-von to lv_actvt.
              if cv_allowed_actvt eq lv_actvt.
                lv_actvt_ok abap_true.
                exit.
              endif.
            endif.
          endif.
        endloop.

Next step is to fill out custom select option table and used it in further SQL selection with table LFA1.

Selection of allowed vendors as the second step

The result itab lt_usvalues is later converted into select-option based itab for SQL select.

        DATA: lt_ktokk_selopt TYPE STANDARD TABLE OF ers_range_option,
              ls_ktokk_selopt LIKE LINE OF lt_ktokk_selopt.

        IF lv_actvt_ok EQ abap_true.
          LOOP AT lt_usvalues ASSIGNING <fs_usvalues>.
            " Pickup KTOKK patterns in case of positive ACTVT
            " Prepare select option intab for SQL query
            IF <fs_usvalues>-field EQ 'KTOKK'.
              CLEAR ls_ktokk_selopt.
              ls_ktokk_selopt-sign = 'I'.
              ls_ktokk_selopt-low = <fs_usvalues>-von.
              IF <fs_usvalues>-bis IS NOT INITIAL.
                ls_ktokk_selopt-high = <fs_usvalues>-bis.
                ls_ktokk_selopt-option = 'BT'.
              ELSEIF  ( <fs_usvalues>-von CS '*' ) OR  ( <fs_usvalues>-von CS '?' ).
                ls_ktokk_selopt-option = 'CP'.
              ELSE.
                ls_ktokk_selopt-option = 'EQ'.
              ENDIF.
              APPEND ls_ktokk_selopt TO lt_ktokk_selopt.
            ENDIF.
          ENDLOOP.
        ENDIF.


The data of select-option table would look like




        " Get allowed LIFNR based on KTOKK patterns
        data lt_lifnr_ok type standard table of lfa1-lifnr.

        if lt_ktokk_selopt is not initial.
          select lifnr from lfa1
            into table lt_lifnr_ok
            where ktokk in lt_ktokk_selopt
            order by ktokk.
        endif.

Now we have all possible LIFNRs which can be offered to user based on authorization object. The last point is just to exclude all unauthorized vendors before displaying.

Exclude unallowed LIFNRs from result

    datalv_i type i,
          ls_lifnr_ok like line of lt_lifnr_ok.

    loop at results_tab.
      lv_i sy-tabix.
      read table lt_lifnr_ok into ls_lifnr_ok with key results_tab-lifnr.
      if sy-subrc <> 0.
*       Delete the corresponding entry in RECORD_TAB.
        delete record_tab index lv_i.
*       Keep SY-TABIX of record_tab and results_tab synchronous
        delete results_tab.
      endif.
    endloop.

Now all vendors of any sub-elementary search help are going to be filtered via our exit function module.



See the whole FM's code for convenience


FUNCTION /YourNameSpace/h_fi_f4ut_kred.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  TABLES
*"      SHLP_TAB TYPE  SHLP_DESCT
*"      RECORD_TAB STRUCTURE  SEAHLPRES
*"  CHANGING
*"     VALUE(SHLP) TYPE  SHLP_DESCR
*"     VALUE(CALLCONTROL) LIKE  DDSHF4CTRL STRUCTURE  DDSHF4CTRL
*"----------------------------------------------------------------------

  TYPES:
    abap_bool TYPE LENGTH 1.

* constants for abap_bool
  CONSTANTS:
    abap_true      TYPE abap_bool VALUE 'X',
    abap_false     TYPE abap_bool VALUE ' ',
    abap_undefined TYPE abap_bool VALUE '-'.

  DATABEGIN OF results_tab OCCURS 0,
          lifnr TYPE lifnr,
        END OF results_tab.

* Enable the postprocessing of selected values
  CALL FUNCTION 'F4UT_POST_SELECTION_PROCESSING'
    TABLES
      shlp_tab    shlp_tab
      record_tab  record_tab
    CHANGING
      shlp        shlp
      callcontrol callcontrol.

* Assign search help exit func. module injection to every child
* elementary sub search helps of main collective search help the name KRED
  CONSTANTScv_root_shlp_name TYPE shlpname VALUE 'KRED',
             cv_shlp_exit_name TYPE string VALUE '/YourNameSpace/H_FI_F4UT_KRED'.
  FIELD-SYMBOLS<fs_shlp> TYPE LINE OF shlp_desct.

  IF shlp-shlpname EQ cv_root_shlp_name AND shlp_tab[] IS NOT INITIAL ).
    LOOP AT shlp_tab[] ASSIGNING <fs_shlp>.
      IF NOT <fs_shlp>-intdescr IS INITIAL.
        <fs_shlp>-intdescr-selmexit cv_shlp_exit_name.
      ENDIF.
    ENDLOOP.
  ENDIF.

* Do selection before displaying the results to user
* (It is triggered on elementary child search helps)
  IF callcontrol-step 'POSTSEL'.

    " Get result data to be filtered
    CALL FUNCTION 'F4UT_PARAMETER_VALUE_GET'
      EXPORTING
        parameter   'LIFNR' "Searchhelp-Parameter
        fieldname   'LIFNR' "Fieldname in RESULTS_TAB
      TABLES
        shlp_tab    shlp_tab
        record_tab  record_tab
        results_tab results_tab
      CHANGING
        shlp        shlp
        callcontrol callcontrol.

    IF results_tab[] IS NOT INITIAL.
      " We have some data to be filtered
      CONSTANTS     cv_auth_object TYPE ust12-objct VALUE 'F_LFA1_GRP'.
      FIELD-SYMBOLS <fs_usvalues> TYPE usvalues.
      DATAlt_usvalues TYPE STANDARD TABLE OF usvalues.

      CALL FUNCTION 'SUSR_USER_AUTH_FOR_OBJ_GET'
        EXPORTING
          user_name  sy-uname
          sel_object cv_auth_object
        TABLES
          values     lt_usvalues.

      IF lt_usvalues IS INITIAL.
        " 1) No auth object at all --> no result
        REFRESH results_tab[].
      ELSE.
        " 2) Filtering part

        DATAlv_actvt TYPE i,
              lv_actvt_ok TYPE abap_bool VALUE abap_false.

        CONSTANTS cv_allowed_actvt TYPE VALUE 3.
        LOOP AT lt_usvalues ASSIGNING <fs_usvalues>.
          " 2 A) Check ACTVT ( later filter KTOKK by ACTVT)
          IF <fs_usvalues>-field EQ 'ACTVT'.
            IF <fs_usvalues>-von EQ '*'.
              lv_actvt_ok abap_true.
              EXIT.
            ELSEIF <fs_usvalues>-von IS NOT INITIAL AND <fs_usvalues>-bis IS NOT INITIAL ).
              MOVE <fs_usvalues>-von TO lv_actvt.
              IF cv_allowed_actvt EQ lv_actvt.
                lv_actvt_ok abap_true.
                EXIT.
              ELSEIF cv_allowed_actvt GE lv_actvt.
                MOVE <fs_usvalues>-bis TO lv_actvt.
                IF cv_allowed_actvt LE lv_actvt.
                  lv_actvt_ok abap_true.
                  EXIT.
                ENDIF.
              ENDIF.
            ELSEIF <fs_usvalues>-von IS NOT INITIAL ).
              MOVE <fs_usvalues>-von TO lv_actvt.
              IF cv_allowed_actvt EQ lv_actvt.
                lv_actvt_ok abap_true.
                EXIT.
              ENDIF.
            ENDIF.
          ENDIF.
        ENDLOOP.

        DATAlt_ktokk_selopt TYPE STANDARD TABLE OF ers_range_option,
              ls_ktokk_selopt LIKE LINE OF lt_ktokk_selopt.

        IF lv_actvt_ok EQ abap_true.
          LOOP AT lt_usvalues ASSIGNING <fs_usvalues>.
            " Pickup KTOKK patterns in case of positive ACTVT
            " Prepare select option intab for SQL query
            IF <fs_usvalues>-field EQ 'KTOKK'.
              CLEAR ls_ktokk_selopt.
              ls_ktokk_selopt-sign 'I'.
              ls_ktokk_selopt-low <fs_usvalues>-von.
              IF <fs_usvalues>-bis IS NOT INITIAL.
                ls_ktokk_selopt-high <fs_usvalues>-bis.
                ls_ktokk_selopt-option 'BT'.
              ELSEIF  <fs_usvalues>-von CS '*' OR  <fs_usvalues>-von CS '?' ).
                ls_ktokk_selopt-option 'CP'.
              ELSE.
                ls_ktokk_selopt-option 'EQ'.
              ENDIF.
              APPEND ls_ktokk_selopt TO lt_ktokk_selopt.
            ENDIF.
          ENDLOOP.
        ENDIF.

        " 2 B) Get allowed LIFNR based on KTOKK patterns
        DATA lt_lifnr_ok TYPE STANDARD TABLE OF lfa1-lifnr.

        IF lt_ktokk_selopt IS NOT INITIAL.
          SELECT lifnr FROM lfa1
            INTO TABLE lt_lifnr_ok
            WHERE ktokk IN lt_ktokk_selopt
            ORDER BY ktokk.
        ENDIF.
      ENDIF.
    ENDIF.

    " 3) Exclude unallowed LIFNRs from result
    DATAlv_i TYPE i,
          ls_lifnr_ok LIKE LINE OF lt_lifnr_ok.

    LOOP AT results_tab.
      lv_i sy-tabix.
      READ TABLE lt_lifnr_ok INTO ls_lifnr_ok WITH KEY results_tab-lifnr.
      IF sy-subrc <> 0.
*       Delete the corresponding entry in RECORD_TAB.
        DELETE record_tab INDEX lv_i.
*       Keep SY-TABIX of record_tab and results_tab synchronous
        DELETE results_tab.
      ENDIF.
    ENDLOOP.

  ENDIF.

ENDFUNCTION.

No comments:

Post a Comment