Tuesday, May 20, 2014

How to get VIA carrier/forwarder address by shipment number?

I just want to share simple topic for the others. This is simple query how to get address of VIA forwarder. You know, some shipments does not contain just receiving end address but are delivered by more than one leg. Sometime we need to pickup address of let's say middle forwarders and this is our case.

Open SQL


  DATA gv_address_via_carrier TYPE adrc.

  CLEAR gv_address_via_carrier.
  IF vttkvb-tknum IS NOT INITIAL" Shipment Number
    SELECT SINGLE *
    FROM vtpa
    INNER JOIN adrc
      ON vtpa~adrnr adrc~addrnumber
    INTO CORRESPONDING FIELDS OF gv_address_via_carrier
    WHERE vbeln  vttkvb-tknum
    AND   posnr '000000'
    AND   parvw  'SV'.
  ENDIF.

Of course in case of more than one VIA forwarder do not use SELECT SINGLE, but fill internal table with just SELECT clause.


SAP SCRIPT form


In my case address had to be displayed by some old fashion SAP script, by window of following content.

/: ADDRESS PARAGRAPH AS
/:  NAME     &GV_ADDRESS_VIA_CARRIER-NAME1&, &GV_ADDRESS_VIA_CARRIER-NAME2
/:  STREET   &GV_ADDRESS_VIA_CARRIER-STREET&
/:  CITY     &GV_ADDRESS_VIA_CARRIER-CITY1(29)&
/:  POSTCODE &GV_ADDRESS_VIA_CARRIER-POST_CODE1(10)&
/:  COUNTRY  &GV_ADDRESS_VIA_CARRIER-COUNTRY&
/:  REGION   &GV_ADDRESS_VIA_CARRIER-REGION&

/: ENDADDRESS


Thursday, April 24, 2014

SAP Smartforms + KYOCERA printer = DMC, QR codes work

Once I had to solve following task, print DMC square codes on Kyocera printers without any third party software. It is a reasonable step to replace all line bar codes with one square code containing all needed.

Because SAP does not support internally these codes, it is a bit challenge. The task did not allow usage of any software due to CPU time consumption during code rendering on server side.
Kyocera printers comes with interesting "option". You can print QR, DMC and really long list of various codes on their printers IF, If you buy small additional piece of hardware so called flash module. It allows you to trigger printer internal PCL commands producing bar codes as you wish. OK, it seems to be a solution. And you can suspect here, if there is some article concerning the topic, the way is not blind. Yes, it works! :)

So, what's needed?


At first we need plug a flash module into the printer. Kyocera produces various types of compact flash cards, types A, B, C... Here be careful, there is a list of supported cards for every printer. It means, you need to have proper card for your Kyocera printer. Note: flash card can be possibly flashed to upper version.

You need device driver supporting used printer language. You can choose standard PCL or Kyocera proprietary language called PRESCRIBE. This article is focused on usage of Prescribe.

In my case I used KYOAAA1C driver for my Kyocera FS-2020D.

List of suitable drivers:

  •       KYOAAA1C   KYO FS2000/3900/4000 PCL POSS
  •       KYOAAB1C   KYO FS95XX PCL POSS
  •       KYOAAC1C   KYO KMX050 PCL POSS
  •       KYOAAD1C   KYO FSC5400/FSC5300 PCL POSS
  •       KYOAAE1C   KYO FS1028/1128MFP PCL POSS
  •       KYOAAF1C   KYO TASKalfa XXXci PCL POSS  


For full list look at: http://www.stechno.net/sap-notes.html?view=sapnote&id=1135057#sthash.EMTwTPm8.dpuf

Now we can move on


Thing is not too complex. Some base explanation of DMC parameters is given just here. I recommend to download PDF from Kyocera website to understand usage of wanted code in depth. By the same way you can use QRs, DMCs and so on...

In general we need just some text element with proper font style. Style is very important, because SAP text element automatic line breaks destroy Prescribe command. So keep in mind you need to have:

  1. Window with enough space, usually simply MAIN window is a good option.
  2. Whole command must be in preview visible on ONE line. No breaks are possible. That's why we use for text element special very small font size as protection against automatic line breaks. 

Setup style


Style is setup in transaction Smartforms. Use font size 1 pt to save a space.


Setup global variables for DMC code


We can include whole DMC code string into one variable. But SAP limits length of text string used in SF up to 255 characters. That's why is better choice to separate prefix and suffix of the code to gain more space for the content itself.


Preparation of DMC content in initialization part


You can call some custom routines placed directly in SF to make a content of the code. Here we are preparing also Prescribe command itself, I mean prefix and suffix.



Whole code of init can looks like:


* DMC code properties (printed via Prescribe)
DATAlv_code_type       TYPE string" type of square code
      lv_dmc_symbol_from TYPE string,
      lv_dmc_symbol_to   TYPE string,
      lv_dmc_position_x  TYPE string,
      lv_dmc_position_y  TYPE string,
      lv_dmc_module_size TYPE string.

* DMC dimensions
lv_code_type        '100'" 100 means DMC
lv_dmc_module_size  '13'.  " size of the small squares with dimension of 13 dots (pixels)
lv_dmc_symbol_from  '11'.  " min. size
lv_dmc_symbol_to    '19'.  " max size
lv_dmc_position_x   '738'" horizontal in px
lv_dmc_position_y   '960'" vertical in px

PERFORM get_dmccode_content
  USING    label_data2
  CHANGING gv_dmc_content.

" Prescribe DMC prefix + suffix definition
CONCATENATE '!R! UNIT D; MAP ' " Note: UNIT D = pixels, MAP = position
            lv_dmc_position_x ','
            lv_dmc_position_y
            '; BARC ' lv_code_type " BARC = print barcode
            ',' lv_dmc_module_size
            ',' lv_dmc_symbol_from
            ',' lv_dmc_symbol_to
            ',0,0,"'
INTO gv_dmc_prefix RESPECTING BLANKS.
gv_dmc_suffix   '";EXIT;'.


DMC size on paper


The "module size" is the size of the small squares from which the bar code is built. If you use the command "UNIT D;" the unit is set to dots (pixels), e.g. the value 15 here means 52 x 52 rows. Final size of the DMC code depends on the length of content. If you set up min - max range for symbol attribute of DMC, system  tries to use the smallest possible one according to content length.

Display DMC code


Display gained global variables in text element via proper style. Prescribe command should be the very first thing sent to the printer, see hierarchy below.


Remember the used style above is too small to understand the idea. If I would enlarged version of picture above and view it by bigger font style, it would look something like below. In real we are inserting three different lines in order prefix, next row of content and finally suffix row.



How to overcome the limits


With system bar code (SE73) you can encode max. 255 characters in Smart Forms and max. 70 characters in SAPScript. With bar code formatted as text, the case of here described solution,  you can encode as much as you can place in one line. Maximum length of a field in Smart Forms is 255 characters.
Therefore you have to split the barcode data into several fields, if you want to encode more than 255 characters. 

Text element content

&DMC_PREFIX&
&DMC_DATA_FIELD_1&&DMC_DATA_FIELD_2&&DMC_DATA_FIELD_3&
&DMC_SUFFIX& 

Addition
See the SAP note Sap note 497380 - Maximum bar code length

Troubleshooting part 


You can test the setting of printer including the message  "codes not loaded" by following Prescribe command: "!R! CALL BSTP; EXIT;" Simply place the command into new text element and try to print out. By this command you would able to see any printer start up error. It would be nice to see if module is working properly.

The proper print driver could be tested by following commands. If print out would be successful, you know, driver works. But it is not telling you anything about bar code module yet.

Example 1

!R! UNIT C; MRP 3, 3;
SFNT "Univers-MdCd", 12;
MRP 3, 3;
CIR .35;

Example 2

!R! RES; PSRC 1;
SFNT "Helvetica", 10;
MZP 3, 3;
RTTX 15, " ----- Text1...";
RTTX 45, " ----- Text2...";
RTTX 75, " ----- Text3...";
PAGE; EXIT;

Finally many thanks to Mr. Norbert Volk-Klee for his precious Prescribe support.

Wish you happy printing cool codes!

Tuesday, April 1, 2014

SAP Smartforms - Error Message no. VN073

This is really short article describing how to find root cause of quite common error message VN073 occuring within Smart forms.

When you click to see the content of the error message in e.g. transaction VL74, there is nothing what can help you. So what to do? You just know, there is something wrong with the Smart form.



There is too many thing what can be wrong. And it is even bigger nightmare in case of really complex Smart forms. The worst thing is, the Smart form could be successfully compiled even if it contains errors!

The solution is to use just "Test" button inside the "Smarforms" transaction.



As you can see below, it gives you a clue where the error could be found. In this case some line of template has been deleted, but smart form contains reference to that line in text field. So this button is the salvation ;)


Wednesday, March 19, 2014

How to pack Delivery x HUs by ABAP?

Once I had to face the following problem, how to programmatically  pack all the goods of some delivery including creation of handling units. I have spent quite considerable time finding working solution. You know there is a certain set of various function modules which leads to the solution, but there is big number of blind ways. Even when you pick up proper FMs, you have to use them in correct order and provide correct input parameters. Finally we have decided to pack every delivery item/position into each new HU, not to pack them all into one HU. Below there is offered a solution working in our case. I hope it could save time to someone else. If you would give me your feedback, it would be very nice.

I already tested following FMs as BAPI_HU_CREATE, BAPI_HU_CHANGE_HEADER, BAPI_HU_PACK, SD_DELIVERY_UPDATE_PACKING, WS_DELIVERY_UPDATE, HU_PACKING_AND_UNPACKING.

I discovered that even the transaction VL02N internally calls FM HU_PACKING_AND_UNPACKING. Usage of BAPI_HU_CREATE and BAPI_HU_PACK with itempropsal is Ok unless you do not need to act with delivery. In such case you need to involve some of those FMs as SD_DELIVERY_UPDATE_PACKING or  WS_DELIVERY_UPDATE or HU_PACKING_AND_UNPACKING. But HU with already filled item proposals causes doubled items inside the every HU after usage FMs working with the delivery. Later on I came to the folowing solution, ommit every item proposals and let the job on FM WS_DELIVERY_UPDATE with suitable VERPO (VEPO) itab given as parameter.

The following code should be commented enough to follow the main idea.

Some data of top include

TYPES:
  abap_bool   TYPE LENGTH 1,
  gtty_result TYPE STANDARD TABLE OF bapiret2.

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

CONSTANTScv_vbtyp_notification LIKE likp-vbtyp VALUE '7',
           cv_in_delivery   LIKE verko-object VALUE '03'" inbound
           cv_out_delivery LIKE verko-object VALUE '01'" outbound
           cv_velin LIKE vepo-velin VALUE '1',
           cv_pksta_packed TYPE pksta VALUE 'C'.

DATAgv_vbuk  TYPE vbuk" vbuk-pkstk = Overall packing status of all items
      gs_likp  TYPE likp,
      gv_shipment_already_created TYPE abap_bool VALUE abap_false,
      gv_pack_mat TYPE mara-matnr.


The main routine

*&---------------------------------------------------------------------*
*&      Form  pack_delivery
*&---------------------------------------------------------------------*
*       Pack whole delivery = all positions
*----------------------------------------------------------------------*
*      -->PS_LIKP      contains vbeln, vbtyp
*      -->PV_PACK_MAT  packaging material
*----------------------------------------------------------------------*
FORM pack_delivery
    USING    ps_likp     TYPE likp " contains vbeln, vbtyp
             pv_pack_mat TYPE mara-matnr.

  DATA:  lt_lips TYPE STANDARD TABLE OF lips.

  " add leading zeros
  UNPACK ps_likp-vbeln TO ps_likp-vbeln.

  IF ps_likp-vbeln IS NOT INITIAL.
    SELECT " posnr werks lgort meins lfimg matnr charg ...
      FROM lips
      INTO CORRESPONDING FIELDS OF TABLE lt_lips
      WHERE vbeln ps_likp-vbeln.
  ENDIF.
  IF lt_lips IS INITIAL.
    MESSAGE e000 WITH text-017.
  ENDIF.

  DATAlt_hu    LIKE STANDARD TABLE OF hum_rehang_hu,
        ls_hu    LIKE LINE OF lt_hu,
        lv_pksta TYPE vbup-pksta.

  DATAlt_vekp  TYPE STANDARD TABLE OF vekp,
        lt_vepo  TYPE STANDARD TABLE OF vepo,
        lv_exidv TYPE exidv.

  DATAls_vbkok LIKE vbkok,
        lt_hus   LIKE STANDARD TABLE OF vekpvb,
        ls_hus   LIKE LINE OF lt_hus.

  " Handling-Unit Confirmation: Header Data
  DATAlt_verko  LIKE STANDARD TABLE OF verko,
        ls_verko  TYPE verko,
        " Handling Unit Confirmation: Content Data
        lt_verpo  LIKE STANDARD TABLE OF verpo,
        ls_verpo  TYPE verpo,
        lt_prot   LIKE STANDARD TABLE OF prott,
        ls_prot   TYPE prott.

  FIELD-SYMBOLS<fs_vekp> LIKE vekp,
                 <fs_vepo> LIKE vepo,
                 <fs_lips> LIKE lips.

  " Pack each non empty delivery position into its own new HU
  LOOP AT lt_lips ASSIGNING <fs_lips>.
    IF <fs_lips>-lfimg IS NOT INITIAL AND <fs_lips>-lfimg > ).

      " Filter already packed items
      CLEAR lv_pksta.
      SELECT SINGLE pksta
        FROM vbup
        INTO lv_pksta
        WHERE vbeln <fs_lips>-vbeln
          AND
              posnr <fs_lips>-posnr.
      IF NOT sy-subrc EQ AND lv_pksta NE cv_pksta_packed ).
        CONTINUE.
      ENDIF.

      " Create HU and assign it to the delivery (no items are yet created)
      CLEAR ls_hu.
      PERFORM get_assigned_hu
      USING     <fs_lips>
                pv_pack_mat
                ps_likp-vbtyp
      CHANGING  ls_hu.

      " Get data needed for delivery packing
      IF ls_hu IS NOT INITIAL.
        APPEND ls_hu TO lt_hu.

        " FM HU_CREATE alredy made record in VEKP table
        SELECT FROM vekp
          INTO TABLE lt_vekp
          WHERE venum ls_hu-venum.

        LOOP AT lt_vekp ASSIGNING <fs_vekp>.
          CLEAR ls_verko.
          MOVE-CORRESPONDING <fs_vekp> TO ls_verko.
          ls_verko-ernam sy-uname.
          ls_verko-object cv_out_delivery.   " Outbound Delivery
          ls_verko-objkey ps_likp-vbeln.     " Object to Which the HU is Assigned
          ls_verko-exidv <fs_vekp>-exidv.
          APPEND ls_verko TO lt_verko.
        ENDLOOP.

        " Prepare item row (later stored in VEPO)
        " in our case 1x HU = 1 x item row = no loop needed

        " new HU
        ls_verpo-exidv_ob ls_hu-top_hu_external.
        ls_verpo-venum    =  ls_hu-venum.
        " other lips fields
        MOVE-CORRESPONDING <fs_lips> TO ls_verpo.
        " quantity + units
        ls_verpo-tmeng =  <fs_lips>-lfimg.
        ls_verpo-vrkme =  <fs_lips>-meins.
        APPEND ls_verpo TO lt_verpo.

      ENDIF.
    ENDIF.
  ENDLOOP.

  IF lt_verko IS NOT INITIAL AND lt_verpo IS NOT INITIAL ).

    ls_vbkok-vbeln    <fs_lips>-vbeln.
    ls_vbkok-vbeln_vl <fs_lips>-vbeln.
    ls_vbkok-vbtyp_vl ps_likp-vbtyp.

    " Save Handling unit data, pack, update delivery
    CALL FUNCTION 'WS_DELIVERY_UPDATE'
      EXPORTING
        vbkok_wa      ls_vbkok
        synchron      abap_true
        commit        abap_true
        delivery      <fs_lips>-vbeln
        nicht_sperren space
      TABLES
        verko_tab     lt_verko
        verpo_tab     lt_verpo
        prot          lt_prot.

    IF NOT lt_prot[] IS INITIAL.
      CLEARls_prot.
      READ TABLE lt_prot INDEX INTO ls_prot.
      MESSAGE ID ls_prot-msgid
             TYPE ls_prot-msgty
             NUMBER ls_prot-msgno
             WITH ls_prot-msgv1 ls_prot-msgv2
                  ls_prot-msgv3 ls_prot-msgv4.
    ENDIF.
  ENDIF.

ENDFORM.                    " PACK_DELIVERY


*&---------------------------------------------------------------------*
*&      Form  GET_ASSIGNED_HU
*&---------------------------------------------------------------------*
*       Create empty HU and assign it to the delivery
*----------------------------------------------------------------------*
*      -->PV_VBELN     text
*      -->PV_PACK_MAT  text
*      -->PV_VBTYP     text
*----------------------------------------------------------------------*
FORM get_assigned_hu
    USING    ps_lips     TYPE lips       " delivery position data
             pv_pack_mat TYPE mara-matnr
             pv_vbtyp    TYPE vbtyp
    CHANGING ps_hu       TYPE hum_rehang_hu.

  DATAlt_return LIKE STANDARD TABLE OF bapiret2,
        ls_return LIKE LINE OF lt_return,
        lv_hukey TYPE  bapihukey-hu_exid,
        ls_huheader  TYPE  bapihuheader,
        ls_headerproposal TYPE  bapihuhdrproposal,
        lt_itemproposal TYPE STANDARD TABLE OF bapihuitmproposal,
        ls_itemproposal LIKE LINE OF lt_itemproposal.

  " Subprocess 1: Create HU

  " HU header proposal
  ls_headerproposal-plant    ps_lips-werks.
  MOVE ps_lips-lgort TO ls_headerproposal-stge_loc" storage location
  UNPACK pv_pack_mat TO pv_pack_mat.
  ls_headerproposal-pack_mat pv_pack_mat.

  " creates record in VEKP (with item proposal given also in VEPO)
  CALL FUNCTION 'BAPI_HU_CREATE'
    EXPORTING
      headerproposal ls_headerproposal
    IMPORTING
      huheader       ls_huheader
      hukey          lv_hukey
    TABLES
      itemsproposal  lt_itemproposal
      return         lt_return.

  IF lt_return[] IS INITIAL.
    CLEAR ls_return.
    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
      EXPORTING
        wait   'X'
      IMPORTING
        return ls_return.
  ELSE.
    PERFORM get_bapi_error USING lt_return.
  ENDIF.

  " Wait to avoid lock in production environment
  WAIT UP TO SECONDS.

  " Set connection to the outbound delivery type (pack_mat_object)
  MOVE cv_out_delivery TO ls_huheader-pack_mat_object.
  MOVE ps_lips-vbeln   TO ls_huheader-pack_mat_obj_key.

  REFRESH lt_return.
  CALL FUNCTION 'BAPI_HU_CHANGE_HEADER'
    EXPORTING
      hukey     ls_huheader-hu_exid
      huchanged ls_huheader
    IMPORTING
      huheader  ls_huheader
    TABLES
      return    lt_return.
  IF lt_return[] IS INITIAL.
    CLEAR ls_return.
    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
      EXPORTING
        wait   'X'
      IMPORTING
        return ls_return.
  ELSE.
    PERFORM get_bapi_error USING lt_return.
  ENDIF.

  CLEAR ps_hu.
  " note: hu_exid + hu_id are stored in VEKP - Handling Unit - Header Table
  ps_hu-top_hu_external ls_huheader-hu_exid" External Handling Unit Identification of new HU
  ps_hu-top_hu_internal ls_huheader-hu_id.   " Internal Handling Unit Number of new HU
  ps_hu-venum ls_huheader-hu_id.             " Internal Handling Unit Number of new HU
  ps_hu-rfbel ps_lips-vbeln.
  ps_hu-rfpos ps_lips-posnr.

ENDFORM.                    " GET_ASSIGNED_HU

*&---------------------------------------------------------------------*
*&      Form  GET_BAPI_ERROR
*&---------------------------------------------------------------------*
*       Checks errors in result table
*----------------------------------------------------------------------*
*      -->P_LT_RETURN  text
*----------------------------------------------------------------------*
FORM get_bapi_error
  USING pt_return TYPE gtty_result.

  FIELD-SYMBOLS<fs_return> LIKE bapiret2.

  LOOP AT pt_return ASSIGNING <fs_return>.
    IF <fs_return>-type 'E' OR <fs_return>-type 'A'.
      MESSAGE ID <fs_return>-id
              TYPE <fs_return>-type
              NUMBER <fs_return>-number
              WITH <fs_return>-message_v1 <fs_return>-message_v2
                   <fs_return>-message_v3 <fs_return>-message_v4.
      LEAVE SCREEN.
    ENDIF.
  ENDLOOP.

ENDFORM.                    " GET_BAPI_ERROR








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.