Gyan Factory

Gyan Factory
SAP Technical Project Support

Tuesday, February 16, 2016

Object Oriented Approach for Reports with multiple Datasource

Lets checkout the ABAP object oriented approach for reports when you want to get data from different data sources – Database and/or Archive .

Introduction

Almost all clients use archiving to control the primary DB size and to make sure it doesn’t grow too big. If primary DB would be big, it would have its own performance impact. But this not the topic for this article. In this article, I want to give you a pattern which you can follow in your developments where you have more than one data source – Database and Archive.
In this example, we would take PBS as the archive add-on as example. But, in reality you can implement this approach in any multiple data selection.

Report Requirement

This report is very basic report to display data from DB as well as archive. The selection screen has other reporting criteria along with these options:
Multiple_Options
As name suggest, the data can be selected from DB, archive or both

Procedural Design

In simple procedural design, this report selection would be using the IF conditions to restrict the selection based on the selected option. It is not necessarily an issue, but this design can be modified to be more modular, following more design principles, etc.
I’m providing only the part where there is a data selection and also there is different logic.

 
* Block 1 of selection screen
SELECTION-SCREEN BEGIN OF BLOCK block1 WITH FRAME TITLE text-001.
SELECT-OPTIONS : s_vendor   FOR v_vendor.
SELECTION-SCREEN END OF BLOCK block1.
 
* Display Data Retrieval Options
SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE text-049.
 
PARAMETERS: p_rb_all TYPE c RADIOBUTTON GROUP rd2,
            p_rb_arc TYPE c RADIOBUTTON GROUP rd2 ,
            p_rb_db TYPE c RADIOBUTTON GROUP rd2 DEFAULT 'X'.
 
SELECTION-SCREEN END OF BLOCK b2.
 
 
 
START-OF-SELECTION.
 
  IF p_rb_db  = 'X'
  OR p_rb_all = 'X'.
    SELECT       ebeln
                 bukrs
                 bsart
                 lifnr
                 waers
                 submi
            INTO TABLE i_ekko
            FROM ekko
            WHERE lifnr IN s_vendor[].
  ENDIF.
 
 
  IF p_rb_ar  = 'X'
  OR p_rb_all = 'X'.
 
    DATA: tab93 TYPE STANDARD TABLE OF ekko.
    DATA: ls_tab93 LIKE LINE OF tab93.
    DATA: ls_ekko  LIKE LINE OF i_ekko.
 
    CALL FUNCTION '/PBS/SELECT_INTO_TABLE'
      EXPORTING
        archiv     = 'CMM'
        OPTION     = "
        tabname    = 'EKKO'
        clr_itab   = 'X'
        schl1_name = 'LIFNR'
      TABLES
        i_tabelle  = tab93
        schl1_in   = s_vendor[]
      EXCEPTIONS
        eof        = 4
        OTHERS     = 2.
    LOOP AT tab93 INTO ls_tab93.
      MOVE ls_tab93-ebeln TO ls_ekko-ebeln .
      MOVE ls_tab93-bukrs TO ls_ekko-bukrs .
      MOVE ls_tab93-bsart TO ls_ekko-bsart .
      MOVE ls_tab93-lifnr TO ls_ekko-lifnr .
      MOVE ls_tab93-waers TO ls_ekko-waers .
      MOVE ls_tab93-submi TO ls_ekko-submi .
      APPEND ls_ekko TO i_ekko .
    ENDLOOP.
    DESCRIBE TABLE i_ekko LINES sy-tfill.
    IF sy-tfill GT 0. sy-subrc = 0. ENDIF.
 
  ENDIF.
 
  IF p_rb_db  = 'X'
  OR p_rb_all = 'X'.
    SELECT        ekpo~ebeln
                  ekpo~ebelp
                  ekpo~matnr
                  ekpo~bukrs
                  ekpo~werks
                  ekpo~menge
                  ekpo~netwr
                  ekkn~ps_psp_pnr
                  eket~eindt
                  ekpo~mwskz
                  ekpo~loekz
            INTO TABLE i_ekpo
            FROM ekpo
              JOIN ekkn
                ON ( ekpo~ebeln =  ekkn~ebeln
                 AND ekpo~ebelp =  ekkn~ebelp  )
              JOIN eket
                ON  ( ekpo~ebeln =  eket~ebeln
                  AND ekpo~ebelp =  eket~ebelp  )
            FOR ALL ENTRIES IN i_ekko
            WHERE ekpo~ebeln EQ i_ekko-ebeln.
  ENDIF.
 
  IF p_rb_ar  = 'X'
  OR p_rb_all = 'X'.
     "use same PBS FM to get the data from EKPO, EKKN, EKET
  ENDIF.
 
 
  "Vendor name selection
 
 
  "fill up final table
 
 
  "display data in ALV
 
 

Object Oriented Approach

To avoid the IF conditions and to have separate classes for the different data sources, I use these design. This design is very much dependent on the Decorator Design Pattern. If you are not yet familiar with decorator, I suggest you make yourself familiar with decorator before continue reading here.
The design is based on following idea:
  • Create the class for the primary data selection.
  • Using this class, inherit classes for each data source.
  • At time of instantiating the object, establish the decorator relationship
  • Within the methods which are redefined, call the decorator methods.
  • Get back the data from decorator objects into the primary data object.
UML_OO_report_multiple_Datasource
Here you are not fully using the decorator design pattern. But, you can if you have more than couple different data-sources.
Another problem I’m facing is to make sure same data selection doesn’t get reselected over and over

Redesigned Code Lines

Again, this is smaller version of the original program with only relevant parts. You can create the full blown solution using ABAP Object Oriented Approach for Reports – Redesign

 
*
CLASS zcl_cda_db DEFINITION.
  PUBLIC SECTION.
 
    METHODS:
      constructor
        IMPORTING
          io_next TYPE REF TO zcl_cda_db OPTIONAL.
 
    METHODS:
      get_data.
 
  PROTECTED SECTION.
    TYPES:
      tt_ekko TYPE STANDARD TABLE OF ty_ekko,
      tt_ekpo TYPE STANDARD TABLE OF ty_ekpo.
 
    METHODS:
      select_ekko,
      select_ekpo_ekkn,
      merge_data,
      select_vendor_names,
      fill_final_table.
 
    DATA:
        i_ekko       TYPE tt_ekko,
        i_ekpo            TYPE tt_ekpo.
 
 
    DATA: o_decorator TYPE REF TO zcl_cda_db.
 
ENDCLASS.                    "zcl_cda_db DEFINITION
*
CLASS zcl_cda_arch DEFINITION
  INHERITING FROM zcl_cda_db.
 
  PUBLIC SECTION.
    METHODS:
      constructor
        IMPORTING
          io_next TYPE REF TO zcl_cda_db OPTIONAL.
 
  PROTECTED SECTION.
    METHODS:
      select_ekko REDEFINITION,
      select_ekpo_ekkn REDEFINITION.
 
ENDCLASS.                    "zcl_cda_arch DEFINITION
 
 
**__SELECTION-SCREEN_____________________________________________________*
 
DATA: v_vendor TYPE ekko-lifnr.
 
* Block 1 of selection screen
SELECTION-SCREEN BEGIN OF BLOCK block1 WITH FRAME TITLE text-001.
SELECT-OPTIONS : s_vendor   FOR v_vendor.
SELECTION-SCREEN END OF BLOCK block1.
 
* Display Data Retrieval Options
SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE text-049.
 
PARAMETERS: p_rb_all TYPE c RADIOBUTTON GROUP rd2,
            p_rb_arc TYPE c RADIOBUTTON GROUP rd2 ,
            p_rb_db TYPE c RADIOBUTTON GROUP rd2 DEFAULT 'X'.
 
SELECTION-SCREEN END OF BLOCK b2.
 
**__START-OF-SELECTION_________________________________________________*
 
START-OF-SELECTION.
 
  DATA: o_data    TYPE REF TO zcl_cda_db.
 
  DATA: lo_data_a  TYPE REF TO zcl_cda_arch.
 
  IF p_rb_db  = 'X'.
    CREATE OBJECT o_data.
  ENDIF.
 
  IF p_rb_arc = 'X'.
    CREATE OBJECT o_data
      TYPE zcl_cda_arch.
  ENDIF.
 
  IF p_rb_all = 'X'.
    CREATE OBJECT lo_data_a.
    CREATE OBJECT o_data
      EXPORTING
        io_next = lo_data_a.
  ENDIF.
 
  o_data->get_data( ).
 
*
CLASS zcl_cda_db IMPLEMENTATION.
*
  METHOD constructor.
    o_decorator = io_next.
  ENDMETHOD.                    "constructor
*
  METHOD get_data.
 
    " data from data source
    me->select_ekko(  ).
    me->select_ekpo_ekkn( ).
 
    " merge data from decorator objects into here
    me->merge_data( ).
 
    "Always from primary data source
    me->select_vendor_names( ).
 
    "fill up the final table
    me->fill_final_table( ).
 
  ENDMETHOD.                    "get_Data
*
  METHOD select_ekko.
 
    SELECT       ebeln
                 bukrs
                 bsart
                 lifnr
                 waers
                 submi
            INTO TABLE me->i_ekko
            FROM ekko
            WHERE lifnr IN s_vendor[].
 
    CHECK o_decorator IS BOUND.
    o_decorator->select_ekko( ).
 
 
  ENDMETHOD.                    "select_ekko
*
  METHOD select_ekpo_ekkn.
 
 
    SELECT        ekpo~ebeln
                  ekpo~ebelp
                  ekpo~matnr
                  ekpo~bukrs
                  ekpo~werks
                  ekpo~menge
                  ekpo~netwr
                  ekkn~ps_psp_pnr
                  eket~eindt
                  ekpo~mwskz
                  ekpo~loekz
            INTO TABLE me->i_ekpo
            FROM ekpo
              JOIN ekkn
                ON ( ekpo~ebeln =  ekkn~ebeln
                 AND ekpo~ebelp =  ekkn~ebelp  )
              JOIN eket
                ON  ( ekpo~ebeln =  eket~ebeln
                  AND ekpo~ebelp =  eket~ebelp  )
            FOR ALL ENTRIES IN i_ekko
            WHERE ekpo~ebeln EQ i_ekko-ebeln.
 
    CHECK o_decorator IS BOUND.
    o_decorator->select_ekpo_ekkn(  ).
 
  ENDMETHOD.                    "select_ekpo_ekkn
*
  METHOD merge_data.
 
    CHECK o_decorator IS BOUND.
    APPEND LINES OF: o_decorator->i_ekko TO me->i_ekko,
                     o_decorator->i_ekpo TO me->i_ekpo.
 
  ENDMETHOD.                    "merge_data
*
  METHOD select_vendor_names.
    "get NAME1 from LFA1 for all entries in I_EKKO
  ENDMETHOD.
*
  METHOD fill_final_Table.
    "move data from all tables into final table for display
  ENDMETHOD.
ENDCLASS.                    "zcl_cda_db IMPLEMENTATION
*
CLASS zcl_cda_arch IMPLEMENTATION.
*
  METHOD constructor.
    super->constructor( io_next ).
  ENDMETHOD.                    "constructor
*
  METHOD select_ekko.
 
    DATA: tab93 TYPE STANDARD TABLE OF ekko.
    DATA: ls_tab93 LIKE LINE OF tab93.
    DATA: ls_ekko  LIKE LINE OF me->i_ekko.
 
    CALL FUNCTION '/PBS/SELECT_INTO_TABLE'
      EXPORTING
        archiv     = 'CMM'
        OPTION     = "
        tabname    = 'EKKO'
        clr_itab   = 'X'
        schl1_name = 'LIFNR'
      TABLES
        i_tabelle  = tab93
        schl1_in   = s_vendor[]
      EXCEPTIONS
        eof        = 4
        OTHERS     = 2.
    LOOP AT tab93 INTO ls_tab93.
      MOVE ls_tab93-ebeln TO ls_ekko-ebeln .
      MOVE ls_tab93-bukrs TO ls_ekko-bukrs .
      MOVE ls_tab93-bsart TO ls_ekko-bsart .
      MOVE ls_tab93-lifnr TO ls_ekko-lifnr .
      MOVE ls_tab93-waers TO ls_ekko-waers .
      MOVE ls_tab93-submi TO ls_ekko-submi .
      APPEND ls_ekko TO me->i_ekko .
    ENDLOOP.
    DESCRIBE TABLE me->i_ekko LINES sy-tfill.
    IF sy-tfill GT 0. sy-subrc = 0. ENDIF.
 
  ENDMETHOD.                    "select_ekko
 
  METHOD select_ekpo_ekkn.
 
    "similarly select the data from PBS
    "for the tables EKPO, EKET, EKKN
 
  ENDMETHOD.                    "select_ekpo_ekkn
 
ENDCLASS.                    "zcl_cda_arch IMPLEMENTATION
 
When RB_ALL is selected and primary DB object is being instantiated
Program_Debug_1
When method SELECT_EKKO is being called
Program_Debug_2
Do let me know your thoughts in the comments.


No comments:

Post a Comment