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: 
PavolF
Explorer

In BW there are scenarios where you have to enhance an existing Datasource with one or more fields and program a logic to fill those fields in or change the extraction in any other way. Traditionally a developer can use a SAP Enhancement RSAP0001 in a custom project Include in transaction CMOD and put all coding inside:

Function ModuleIncludeDescription

EXIT_SAPLRSAP_001

ZXRSAU01Transaction data
EXIT_SAPLRSAP_002ZXRSAU02Attributes, texts
EXIT_SAPLRSAP_003ZXRSAU03Texts
EXIT_SAPLRSAP_004ZXRSAU04Hierarchies

This approach can create a lot of mess when you have multiple datasource enhancements like that inside one Include.

I was searching for a better solution which would be flexible and separate datasource enhancements in a better way. I stumbled upon a white paper from Flávio Peres which gave me an idea how to do that:

http://scn.sap.com/docs/DOC-11246

There were some blank spots where I was not sure how to approach it so I decided to enhance it and document the whole solution again here. I hope it can be quickly used by any developer without having to think about the framework itself.

Everything starts from an SAP classical BaDI RSU5_SAPI_BADI. You can see it in transaction SE19. There we start defining a new implementation:

When you specify your own implementation and its description you will see the system added an interface IF_EX_RSU5_SAPI_BADI into the implementation. This Interface adds 2 methods into the implementation class:

Method NameDescription
DATA_TRANSFORMMethod for General Data Transfer
HIER_TRANSFORMMethod for Hierarchy Data Transfer

The method DATA_TRANSFORM is responsible for handling all classical datasources. The method HIER_TRANSFORM handles Hierarchy extractions which will be covered in detail in some of my future blogs.

First we copy the method DATA_TRANSFORM with all its parameters into a new method called _TEMPLATE_DATASOURCE and make it a "Static method". That will be used as a pattern for all datasource enhancements.

Now double-click on the original DATA_TRANSFORM method and insert following code:


METHOD if_ex_rsu5_sapi_badi~data_transform.
**********************************************************
* To implement an exit for a
* datasource create your own method by copying the
* method _TEMPLATE_DATASOURCE and rename it to the name
* of your datasource. In case you enhance a Business
* Content datasource skip the 0 at the beginning (e.g.
* Datasource 0FI_AR_3 -> Method FI_AR_3
* The method is then called by the Exit Framework
*********************************************************
   DATA:          ls_oltpsource   TYPE rsaot_s_osource,
                  lo_data         TYPE REF TO data,
                  lv_method       TYPE seocmpname.
   FIELD-SYMBOLS: <lt_data>       TYPE STANDARD TABLE.
* check if any data is extracted
   CHECK c_t_data IS NOT INITIAL.
*retrieve information about the Datasource
   CALL FUNCTION 'RSA1_SINGLE_OLTPSOURCE_GET'
     EXPORTING
       i_oltpsource   = i_datasource
       i_objvers      = 'A'
     IMPORTING
       e_s_oltpsource = ls_oltpsource
     EXCEPTIONS
       no_authority   = 1
       not_exist      = 2
       inconsistent   = 3
       OTHERS         = 4.
   IF sy-subrc <> 0.
     EXIT.
   ENDIF.
* create data for Extract Structure
   CREATE DATA lo_data TYPE TABLE OF (ls_oltpsource-exstruct).
   ASSIGN lo_data->* TO <lt_data>.
   ASSIGN c_t_data TO <lt_data>.
* get method name for datasource
   lv_method = i_datasource.
CASE lv_method(1).
     WHEN '0' OR '2'.
* shift by one character as methods can't start with a number
       SHIFT lv_method.
WHEN OTHERS.
"Do nothing
   ENDCASE.
* check method is implemented
   CHECK check_method_exists( lv_method ) = 'X'.
*execute the Datasource enhancement
   CALL METHOD (lv_method)
     EXPORTING
       i_datasource = i_datasource
       i_updmode    = i_updmode
       i_t_select   = i_t_select
       i_t_fields   = i_t_fields
     CHANGING
       c_t_data     = <lt_data>
       c_t_messages = c_t_messages.









ENDMETHOD.

This method will always be called when an extraction starts but if there is no enhancement for the extraction it will end doing nothing.

If you check the syntax of the code you will end up with an error of unknown method CHECK_METHOD_EXISTS. We will create it in the next moment.

First I created some dictionary objects to have my own table in the class:

  1. Create Domain ZD_METHOD with CHAR 30.
  2. Create Data Element ZE_METHOD (Name of a method in a class) with Domain ZD_METHOD.
  3. Create Structure ZSTR_METHODS with one component CMPNAME type ZE_METHOD.
  4. Create a Table Type ZTT_METHODS with Liny Type ZSTR_METHODS which will be a Sorted Table with a key field CMPNAME.

Now with this we can create an attribute GT_METHODS inside our class ZBW_EXTRACTOR_BADI to store the names of methods there:

Create an Instance Public method CHECK_METHOD_EXISTS with following parameters:

With all this we can place a simple code inside the method:


METHOD check_method_exists.
*reads global attribute table with all methods and retrieves a flag if a match is found
   READ TABLE gt_methods WITH KEY cmpname = iv_method TRANSPORTING NO FIELDS.
   IF sy-subrc = 0.
     rv_exist = 'X'. "method found
   ELSE.
     CLEAR rv_exist.
   ENDIF.
ENDMETHOD.




The global table with method names will be filled in a class constructor which you need to create with following code.



METHOD constructor.
   DATA: lo_class          TYPE REF TO   cl_oo_object,
         lt_methods        TYPE          seo_methods,
         ls_methods_global LIKE LINE OF  gt_methods,
         lo_descr_ref      TYPE REF TO   cl_abap_typedescr,
         lv_class          TYPE seoclsname.
   FIELD-SYMBOLS: <fs_methods> LIKE LINE OF lt_methods.
*retrieve description of the class
   lo_descr_ref = cl_abap_classdescr=>describe_by_object_ref( me ).
   lv_class = lo_descr_ref->get_relative_name( ).
   TRY.
       CALL METHOD cl_oo_object=>get_instance
         EXPORTING
           clsname = lv_class
         RECEIVING
           result  = lo_class.
     CATCH cx_class_not_existent.
   ENDTRY.
   IF sy-subrc = 0.
*Read all methods
     CALL METHOD lo_class->get_methods
       EXPORTING
         public_methods_only   = seox_false
         instance_methods_only = seox_false
       RECEIVING
         result                = lt_methods.
     LOOP AT lt_methods ASSIGNING <fs_methods>.
       ls_methods_global-cmpname = <fs_methods>-cmpname.
       INSERT ls_methods_global INTO TABLE gt_methods.
     ENDLOOP.
   ENDIF.
ENDMETHOD.




The last thing is to insert a sample code into the _TEMPLATE_DATASOURCE method:


METHOD _template_datasource.
**********************************************************
* To implement an exit for a
* datasource create your own method by copying the
* method _TEMPLATE_DATASOURCE and rename it to the name
* of your datasource. In case you enhance a Business
* Content datasource skip the 0 at the beginning (e.g.
* Datasource 0FI_AR_3 -> Method FI_AR_3
* The method is then called by the Exit Framework
*********************************************************
** Data Definition
*  DATA: lt_sourcepackage TYPE TABLE OF zoxnsp0107. "change the structure according to DS
*
*  FIELD-SYMBOLS: <fs_sourcepackage> TYPE zoxnsp0107.
*
*  lt_sourcepackage[] = c_t_data[].
*
**Custom coding starts from here.
*  CLEAR c_t_data.
*
*  DELETE lt_sourcepackage WHERE carrid(1) NE 'A'.
*
*  SELECT a~carrid
*  a~carrname
*  a~currcode
*  a~url
*  b~connid
*    INTO CORRESPONDING FIELDS OF TABLE c_t_data
*    FROM scarr AS a LEFT OUTER JOIN spfli AS b ON
*    a~carrid = b~carrid
*    FOR ALL ENTRIES IN lt_sourcepackage WHERE
*    a~carrid = lt_sourcepackage-carrid.
*  IF sy-subrc = 0.
*
*  ELSE.
*    "Error handling
*  ENDIF.
ENDMETHOD.




Now you should have everything needed for a successful activation of the class and the framework. The class should look like this:

The framework is now complete and we can test it by using any existing Datasource or with a simple one shown below.


As a test I created a simple datasource ZSCARR to extract data from the SAP FLIGHT demo database:



It is a simple datasource over table SCARR (which you can fill using SAP report SAPBC_DATA_GENERATOR). As a sample enhancement I added a field CONNID from table SPFLI as an append Structure.


Then in transaction RSA6 I unhid the new filed so it is visible to BW:

You need to copy the ExtractStruct. from here to the method. The method is simply a copy of the _TEMPLATE_DATASOURCE method to a method ZSCARR. Once you adjust the local table inside and activate you can see the result in transaction RSA3:

I hope this can help you create clean and flexible enhancements for all your extractions. Feel free to suggest any improvements to this approach.

Best regards,

Pavol

5 Comments
Labels in this area