Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 

This article explains how to overcome the difficulties when there are multiple developers and projects working simultaneously on Customer Exit variables.  It addresses the collision issues of developers in line of their development. It is intended for the developers and support staff who work on enhancements or complex developments as part of their projects.Explanation includes the sample code and implementation strategy

Problem Statement:


Conventionally, as a developer, adding the code for a new customer exit variable is as easy as going to CMOD, finding the right project for component EXIT_SAPLRRS0_001 and editing the include ZXRSRU01. Moving that code to Quality and later to Production should be even easier.

But the difficulties start when there are multiple developers and projects working simultaneously. As we all know, the include ZXRSRU01 is a single piece of code that holds logic for all the customer exit variables in the system and this means the following:

       Only one developer can work on the code at a given time.

       Transporting the code for a variable means transporting all the code for all variables.

       Work-in-progress variables require removing the unwanted code when a transport is required.

       In large implementations, the include can have several thousand lines of code.

       Etc.

And the most important common issue:

        Any syntax error on the code renders all variables unusable.

To better illustrate the last point made above,  any simple error such as referencing a data type or attempting to obtain data from a table not available on the system (because it was not transported or was deleted), from any variable that may or may not even be on your report, will make the code for all variables erroneous.

To the users this means that all the reports that use customer exit variables will not work.

The solution to the above stated difficulties is explained in the next section in detail.

The Workaround:


In order to overcome most of the issues described on the previous section of this document, a method for developing and executing the code for customer exit variables from independent function modules has been implemented.

Using function modules brings the following benefits (as opposed to the standard CASE method):

       All developers can work on their own code at any time.

       Transporting the code for a variable means transporting only a function module.

       Work-in-progress variables are isolated (they have their own function module) from the finished variables.

       In large implementations, the include remains untouched, there is no need to add code to the shared include to add new variables.

And the most important benefit-->

       A syntax error on a variable code renders only that variable unusable, all the rest work as usual.

Besides the benefits explained above, it’s good to note that the standard method still works along with this new method. A system can have both Function Module customer exit variables and CASE statement customer exit variables working simultaneously, even on the same report.

The approach we are going to adapt is a twofold approach.

     1. First Level Implementation (base code):

     2. Second Level Implementation (function module code):

First Level Implementation (base code):

The following code has to be added, just once, to ZXRSRU01 to be able to use function module based customer exit variables.

The aim of this code is to generate the corresponding function module name (Z_BW + [Variable Technical Name]), check for shared code for multiple variables (explained later) and finally to execute the function module.

The code has to be added just above the CASE i_vnam line.

DATA: BEGIN OF lwa_variables,
function_name       TYPE tvarvc-name,
type                TYPE tvarvc-type,
vnam                TYPE rszglobv-vnam,
END OF lwa_variables.
DATA: ltb_variables         LIKE TABLE OF lwa_variables.
DATA: BEGIN OF lwa_tvarvc,
name                TYPE tvarvc-name,
type                TYPE tvarvc-type,
low                 TYPE tvarvc-low,
END OF lwa_tvarvc.
DATA: ltb_tvarvc            LIKE TABLE OF lwa_tvarvc.

DATA: lw_function_name      TYPE tvarvc-name.
CONSTANTS: lc_p             TYPE tvarvc-type    VALUE 'P',
lc_z_bw(7)     TYPE c              VALUE 'Z_BW_'.

CLEAR ltb_variables.

* step 3 we need to validate all variables to insure they are not in error so
* load all variables into the custom function table.  Otherwise just process
* the variable being sent in.


IF i_step = 3.
LOOP AT i_t_var_range
INTO loc_var_range.

CONCATENATE lc_z_bw
loc_var_range-vnam
INTO lwa_variables-function_name.
lwa_variables-type       = lc_p.
lwa_variables-vnam       = loc_var_range-vnam.
APPEND lwa_variables    TO ltb_variables.

ENDLOOP.

*   check if a custom edit function was created for the query name.
CONCATENATE lc_z_bw
i_s_rkb1d-compid
INTO lwa_variables-function_name.
lwa_variables-type         = lc_p.
lwa_variables-vnam         = loc_var_range-vnam.
APPEND lwa_variables      TO ltb_variables.

ELSE.

CONCATENATE lc_z_bw
i_vnam
INTO lwa_variables-function_name.
lwa_variables-type         = lc_p.
lwa_variables-vnam         = i_vnam.
APPEND lwa_variables      TO ltb_variables.

ENDIF.

* check if shared function module exists by looking for entry on tvarv.

IF ltb_variables IS NOT INITIAL.

SELECT name type low
FROM tvarvc
INTO TABLE ltb_tvarvc
FOR ALL ENTRIES IN ltb_variables
WHERE name                = ltb_variables-function_name
AND  type                = ltb_variables-type.

SORT ltb_tvarvc BY name low.

ENDIF.

* process all of the varibles in the table.  Call either the common
* function or the function based on variable name depending on if
* tvarv entry is found or not.
LOOP AT ltb_variables
INTO lwa_variables.

CLEAR lwa_tvarvc.
READ TABLE ltb_tvarvc
INTO lwa_tvarvc
WITH KEY name            = lwa_variables-function_name
BINARY SEARCH.

IF sy-subrc = 0.
lw_function_name         = lwa_tvarvc-low.
ELSE.
lw_function_name         = lwa_variables-function_name.
ENDIF.

TRY.
CALL FUNCTION lw_function_name
EXPORTING
i_vnam        = lwa_variables-vnam
i_vartyp      = i_vartyp
i_iobjnm      = i_iobjnm
i_s_cob_pro   = i_s_cob_pro
i_s_rkb1d     = i_s_rkb1d
i_periv       = i_periv
i_t_var_range = i_t_var_range
i_step        = i_step
IMPORTING
e_t_range     = e_t_range
e_meeht       = e_meeht
e_mefac       = e_mefac
e_waers       = e_waers
e_whfac       = e_whfac
CHANGING
c_s_customer  = c_s_customer
EXCEPTIONS
OTHERS        = 1.

*     if functino does not exist, the catch statement will force the
*     continue to be executed and the variable will be left on the
CATCH cx_sy_dyn_call_illegal_func.

CONTINUE.

ENDTRY.

*   custom functino was called so remove the variable from the table, it
*   has been processed.
DELETE ltb_variables.

ENDLOOP.

* if ltb_variables is initial, then all variables have been processed, no
* further processing is required.  Otherwise, variable will be processed
* by logic coded below.
CHECK ltb_variables IS NOT INITIAL.

Second Level Implementation (function module code):


Most variables should share the same base, depending on the step for which the variable is required:

  • I_STEP = 1

          Call is made directly before variable entry.

  • I_STEP = 2

          Call is made directly after variable entry. This step is only executed if the same variable is not input-ready and could not be filled for I_STEP = 1.

  • I_STEP = 3

          In this call, you can check the values of the variables. When an exception (RAISE) is triggered, the variable screen appears again. I_STEP = 2 is                then also called again.

  • I_STEP = 0

          The enhancement is not called from the variable screen. The call can originate from the authorization check or from the monitor.

To accommodate most developments, a Function Module template based on EXIT_SAPLRRS0_001 has been created to handle them all. (Note: the template is not designed to handle step 0 functions):

Z_BW_VAR_TEMPLATE.

FUNCTION z_bw_var_template.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(I_VNAM) TYPE  RSZGLOBV-VNAM
*"     VALUE(I_VARTYP) TYPE  RSZGLOBV-VARTYP
*"     VALUE(I_IOBJNM) TYPE  RSZGLOBV-IOBJNM
*"     VALUE(I_S_COB_PRO) TYPE  RSD_S_COB_PRO
*"     VALUE(I_S_RKB1D) TYPE  RSR_S_RKB1D
*"     VALUE(I_PERIV) TYPE  RRO01_S_RKB1F-PERIV
*"     VALUE(I_T_VAR_RANGE) TYPE  RRS0_T_VAR_RANGE
*"     VALUE(I_STEP) TYPE  I DEFAULT 0
*"  EXPORTING
*"     VALUE(E_T_RANGE) TYPE  RSR_T_RANGESID
*"     VALUE(E_MEEHT) TYPE  RSZGLOBV-MEEHT
*"     VALUE(E_MEFAC) TYPE  RSZGLOBV-MEFAC
*"     VALUE(E_WAERS) TYPE  RSZGLOBV-WAERS
*"     VALUE(E_WHFAC) TYPE  RSZGLOBV-WHFAC
*"  CHANGING
*"     VALUE(C_S_CUSTOMER) TYPE  RRO04_S_CUSTOMER OPTIONAL
*"----------------------------------------------------------------------
*Make sure you've added TYPE-POOLS: rro01, rrs0, rsr. to Global Data.

DATA: w_range TYPE rsr_s_rangesid.
DATA: w_var   TYPE rrs0_s_var_range.
DATA: l_msgv1 TYPE symsgv.
DATA: l_msgv2 TYPE symsgv.
DATA: l_msgv3 TYPE symsgv.

CASE i_step.

WHEN '1'. "Default Initial Values to Variable

*      w_range-sign  = 'I'.
*      w_range-opt   = 'BT'.
*      w_range-low   = sy-datlo.
*      w_range-high  = sy-datlo.
*
*      APPEND w_range TO e_t_range.

WHEN '2'. "Derivate Values From Other Variable

*      READ TABLE i_t_var_range
*      INTO w_var
*      WITH KEY vnam = 'VAR_NAME'.
*
*      IF sy-subrc = 0.
*
*        w_range-sign = 'I'.
*        w_range-opt  = 'BT'.
*        CONCATENATE w_var-low(4) '001' INTO w_range-low.
*        CONCATENATE w_var-low(4) '012' INTO w_range-high.
*
*        APPEND w_range TO e_t_range.
*
*      ENDIF.

WHEN '3'. "Validate User Entered Values

*      READ TABLE i_t_var_range
*      INTO w_var
*      WITH KEY vnam = 'VAR_NAME'.
*
*      IF sy-subrc = 0.
*
*        IF sy-datlo < '20111001'.
*
*          l_msgv1 = l_msgv1.
*          l_msgv2 = l_msgv2.
*          l_msgv3 = l_msgv3.
*
*          CALL FUNCTION 'RRMS_MESSAGE_HANDLING'
*            EXPORTING
*              i_class  = 'R9'
*              i_type   = 'E'
*              i_number = '000'
*              i_msgv1  = l_msgv1
*              i_msgv2  = l_msgv2
*              i_msgv3  = l_msgv3
*            EXCEPTIONS
*              dummy    = 0
*              OTHERS   = 0.
*
*          CALL FUNCTION 'RRMS_MESSAGES_SHOW'.
*
*          CALL FUNCTION 'RRMS_MESSAGES_DELETE'.
*
*          sy-subrc = 4.
*
*          RAISE again.
*
*        ENDIF.
*
*      ENDIF.

ENDCASE.

ENDFUNCTION.

Creating this template Function Module and having it on the system is useful as it can be copied to use it as a base for creating or migrating customer exit variables.

Please remember to add the following statement to the Global Data of the function module.

  TYPE-POOLS: rro01, rrs0, rsr.

Step by step guide for creating a new variable:

1)  Make sure you have the base code implemented on the include ZXRSRU01 (described earlier on this document).

2)  Create a Function Group to hold the new Function Module (SE80).

3)  Create a function module based on EXIT_SAPLRRS0_001 (this is to have all the parameters and structures needed to interface with ZXRSRU01). The template described earlier is based on the same function module and is already modified to ease the creation of variables, it’s advisable to create it on the first place and then use it as a base to be copied when creating the new variables (Copy button on SE37).

The naming convention is Z_BW_[VARIABLE TECHNICAL NAME].

4)  Having in mind the expected behavior of the variable, implement the code for the required step (1, 2 or 3.). If you use the template as a base, add your code into the corresponding WHEN statement.

5)  Please remember to add the following statement to the Global Data of the function module: TYPE-POOLS: rro01, rrs0, rsr.

6)  Activate.

Shared function module for multiple variables:

It’s not so unusual to have the same code executing for multiple variables. The code will look something like this on ZXRSRU01:

  WHEN 'ZBWV1OTC' OR 'ZBWV2OTC'.

In this case the same logic will run for both variables. Two independent function modules with the same code on them could be created, but if the requirement is for 10 or 20, the procedure should be a little different.

Go through the same 6 steps to create a new function module based variable, but keep in mind that the name of this function module should not be the same as one of the variables but a more generic one.

And now the new step required:

Go to table TVARVC (SE16) and add the required records, as explained below.

For example, if you had three variables that would execute the same code, then you will need to add three entries, with a final result looking like this:


Now, for the variables VAR_TESTING_2, 3 and 4 the same Z_BW_VAR_TESTING function module will be executed.

Related Content / References

http://help.sap.com/saphelp_nw04/helpdata/en/f1/0a56f5e09411d2acb90000e829fbfe/content.htm

http://help.sap.com/saphelp_nw70/helpdata/en/c0/9803ade58611d194cc00a0c94260a5/content.htm

www.sdn.sap.com/irj/sdn/howtoguides

Labels in this area