In Part I, we covered the concept of Model View Controller in OOPs ABAP. In Part II, we added another class to add more feature to the User Interface. Now in this concluding part is the final real time requirement, when the user needs only specific fields and additional information from multiple tables to be displayed.
This is a practical project scenario, where there is a change request for an already delivered object. In the initial development there was some business logic, but now the flow has changed and hence the business logic has changed. We need to enhance the old OOPs program and update our Model.
You have guessed it right. In this part, we will be changing only our MODEL i.e. our class CL_FETCH
We are selecting our data from multiple tables and eventually, we will be displaying the data using our final table.
We have three methods here including CONSTRUCTOR.
FETCH_DATA: will return the respective data from different tables.
ARRANGE_DATA: will append our data from the respective table into our final table.
So we have final table IT_FINAL which we will pass to our factory method.
Now if you observe our all scenarios we have changed the classes independently without disturbing the other classes.
The logic behind decoupling is simple. When we change our View (CL_ALV) we don’t have to bother about the MODEL i.e. our data class (CL_FETCH) and vice-versa.
Final Program:
DATA : lv_vbeln TYPE vbap-vbeln .
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-000.
PARAMETERS : p_werks TYPE vbap-werks .
SELECT-OPTIONS : s_vbeln1 FOR lv_vbeln .
SELECTION-SCREEN END OF BLOCK b1.
SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE text-001.
PARAMETERS: p_vari LIKE disvariant-variant.
SELECTION-SCREEN END OF BLOCK b2.
CLASS cl_variant DEFINITION FINAL.
PUBLIC SECTION.
TYPES : p_vari1 TYPE disvariant-variant.
DATA : g_layout TYPE disvariant-variant .
TYPES : g_repid TYPE sy-repid .
METHODS: f4_variant CHANGING cv_layout TYPE disvariant-variant.
CLASS-METHODS: get_default CHANGING cv_layout TYPE disvariant-variant .
ENDCLASS .
*&---------------------------------------------------------------------*
*& Class (Implementation) VARIANT
*&---------------------------------------------------------------------*
* Text
*----------------------------------------------------------------------*
CLASS cl_variant IMPLEMENTATION.
METHOD f4_variant.
DATA: ls_layout TYPE salv_s_layout_info,
ls_key TYPE salv_s_layout_key.
ls_key-report = sy-repid.
ls_layout = cl_salv_layout_service=>f4_layouts(
s_key = ls_key
restrict = if_salv_c_layout=>restrict_none ).
cv_layout = ls_layout-layout.
g_layout = cv_layout.
ENDMETHOD.
METHOD get_default .
DATA: ls_layout TYPE salv_s_layout_info,
ls_key TYPE salv_s_layout_key.
ls_key-report = sy-repid.
ls_layout = cl_salv_layout_service=>get_default_layout(
s_key = ls_key
restrict = if_salv_c_layout=>restrict_none ).
cv_layout = ls_layout-layout.
ENDMETHOD .
ENDCLASS. "VARIANT
************************************SEL CLASS FOR SELECTING FIELDS*********************************
CLASS cl_sel DEFINITION FINAL .
PUBLIC SECTION .
TYPES : t_vbeln TYPE RANGE OF vbeln .
DATA : s_vbeln TYPE t_vbeln .
DATA : s_werks TYPE werks_ext .
METHODS : check_plant IMPORTING lp_werks TYPE werks_ext .
ENDCLASS .
*&---------------------------------------------------------------------*
*& CLASS (IMPLEMENTATION) SEL
*&---------------------------------------------------------------------*
* TEXT
*----------------------------------------------------------------------*
CLASS cl_sel IMPLEMENTATION.
METHOD check_plant .
IF lp_werks IS NOT INITIAL .
SELECT COUNT(*) UP TO 1 ROWS
FROM t001w
WHERE werks = lp_werks.
IF sy-subrc NE 0.
MESSAGE 'PLEASE ENTER A VALID WERKS' TYPE 'E'.
ENDIF.
ENDIF .
ENDMETHOD.
ENDCLASS. "SEL
****************************FETCH CLASS FOR DATA FETCH******************************
CLASS cl_fetch DEFINITION .
PUBLIC SECTION .
TYPES : BEGIN OF ty_final,
vbeln TYPE vbeln_va,
posnr TYPE posnr_va,
vkorg TYPE vkorg,
spart TYPE spart,
vkgrp TYPE vkgrp,
matnr TYPE matnr,
arktx TYPE arktx,
pstyv TYPE pstyv,
sp_name TYPE ad_name1,
sh_name TYPE ad_name1,
END OF ty_final .
TYPES : BEGIN OF ty_vbak,
vbeln TYPE vbeln_va,
vkorg TYPE vkorg,
spart TYPE spart,
vkgrp TYPE vkgrp,
END OF ty_vbak.
TYPES : BEGIN OF ty_vbpa,
vbeln TYPE vbeln_va,
posnr TYPE posnr_va,
parvw TYPE parvw,
kunnr TYPE kunnr,
adrnr TYPE adrnr,
END OF ty_vbpa .
TYPES : BEGIN OF ty_adrc,
addrnumber TYPE ad_addrnum,
name1 TYPE ad_name1,
END OF ty_adrc .
DATA : it_final TYPE STANDARD TABLE OF ty_final.
DATA : wa_final TYPE ty_final .
DATA : it_vbak TYPE STANDARD TABLE OF ty_vbak,
wa_vbak TYPE ty_vbak.
DATA : it_vbpa TYPE STANDARD TABLE OF ty_vbpa,
wa_vbpa TYPE ty_vbpa.
DATA : it_adrc TYPE STANDARD TABLE OF ty_adrc,
wa_adrc TYPE ty_adrc.
DATA : it_vbap TYPE STANDARD TABLE OF vbap,
it_vbap_tem TYPE STANDARD TABLE OF vbap,
wa_vbap TYPE vbap.
DATA : sel_obj TYPE REF TO cl_sel .
METHODS : constructor IMPORTING ref_sel TYPE REF TO cl_sel .
METHODS : fetch_data .
METHODS : arrange_data .
ENDCLASS .
*&---------------------------------------------------------------------*
*& CLASS (IMPLEMENTATION) FETCH
*&---------------------------------------------------------------------*
* TEXT
*----------------------------------------------------------------------*
CLASS cl_fetch IMPLEMENTATION.
METHOD constructor.
me->sel_obj = ref_sel .
ENDMETHOD .
METHOD fetch_data .
SELECT * FROM vbap INTO TABLE me->it_vbap UP TO 100 ROWS WHERE
vbeln IN me->sel_obj->s_vbeln AND werks EQ me->sel_obj->s_werks .
me->it_vbap_tem = me->it_vbap.
DELETE ADJACENT DUPLICATES FROM me->it_vbap_tem COMPARING vbeln.
SORT me->it_vbap_tem BY vbeln posnr .
IF me->it_vbap_tem IS NOT INITIAL .
SELECT vbeln
vkorg
spart
vkgrp FROM vbak INTO TABLE me->it_vbak
FOR ALL ENTRIES IN me->it_vbap_tem
WHERE vbeln = me->it_vbap_tem-vbeln .
SELECT vbeln
posnr
parvw
kunnr
adrnr
FROM vbpa INTO TABLE me->it_vbpa
FOR ALL ENTRIES IN me->it_vbap_tem
WHERE vbeln = me->it_vbap_tem-vbeln AND parvw IN ('AG','WE').
SELECT addrnumber name1 FROM adrc INTO TABLE me->it_adrc
FOR ALL ENTRIES IN me->it_vbpa
WHERE addrnumber = me->it_vbpa-adrnr .
ENDIF.
ENDMETHOD .
METHOD arrange_data.
CALL METHOD me->fetch_data( ).
LOOP AT me->it_vbap INTO me->wa_vbap.
me->wa_final-vbeln = me->wa_vbap-vbeln.
me->wa_final-posnr = me->wa_vbap-posnr .
me->wa_final-matnr = me->wa_vbap-matnr .
me->wa_final-arktx = me->wa_vbap-arktx .
me->wa_final-pstyv = me->wa_vbap-pstyv .
READ TABLE me->it_vbak INTO me->wa_vbak WITH KEY
vbeln = me->wa_vbap-vbeln .
IF sy-subrc EQ 0.
me->wa_final-vkorg = me->wa_vbak-vkorg .
me->wa_final-spart = me->wa_vbak-spart .
me->wa_final-vkgrp = me->wa_vbak-vkgrp .
ENDIF .
READ TABLE me->it_vbpa INTO me->wa_vbpa WITH KEY
vbeln = me->wa_vbap-vbeln parvw = 'AG'.
IF sy-subrc EQ 0.
READ TABLE me->it_adrc INTO me->wa_adrc WITH KEY
addrnumber = me->wa_vbpa-adrnr .
me->wa_final-sp_name = me->wa_adrc-name1 .
ENDIF.
READ TABLE me->it_vbpa INTO me->wa_vbpa WITH KEY
vbeln = me->wa_vbap-vbeln parvw = 'WE'.
IF sy-subrc EQ 0.
READ TABLE me->it_adrc INTO me->wa_adrc WITH KEY
addrnumber = me->wa_vbpa-adrnr .
me->wa_final-sh_name = me->wa_adrc-name1 .
ENDIF.
APPEND me->wa_final TO me->it_final .
ENDLOOP.
ENDMETHOD .
ENDCLASS. "FETCH
*******************************DISPLAY DATA ******************************
CLASS cl_alv DEFINITION .
PUBLIC SECTION .
DATA : fetch_obj TYPE REF TO cl_fetch .
DATA : variant_obj TYPE REF TO cl_variant .
DATA : o_alv TYPE REF TO cl_salv_table.
METHODS : constructor IMPORTING ref_fetch TYPE REF TO cl_fetch
ref_var TYPE REF TO cl_variant.
METHODS : get_object .
METHODS : layout_dis .
METHODS : display_alv .
ENDCLASS .
*&---------------------------------------------------------------------*
*& Class (Implementation) CL_ALV
*&---------------------------------------------------------------------*
* Text
*----------------------------------------------------------------------*
CLASS cl_alv IMPLEMENTATION.
METHOD constructor .
me->fetch_obj = ref_fetch .
me->variant_obj = ref_var.
ENDMETHOD .
METHOD get_object .
DATA: lx_msg TYPE REF TO cx_salv_msg.
TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = me->o_alv
CHANGING
t_table = me->fetch_obj->it_final ).
CATCH cx_salv_msg INTO lx_msg.
ENDTRY.
ENDMETHOD .
METHOD layout_dis.
DATA : ls_key TYPE salv_s_layout_key,
lo_layout TYPE REF TO cl_salv_layout.
DATA: lo_functions TYPE REF TO cl_salv_functions_list.
ls_key-report = sy-repid .
* GET DEFAULT TOOLBAR ICONS
lo_functions = me->o_alv->get_functions( ).
lo_functions->set_default( abap_true ).
* GET LAYOUT OBUJECT
lo_layout = me->o_alv->get_layout( ).
* ALLOW SAVING LAYOUT
lo_layout->set_save_restriction( if_salv_c_layout=>restrict_none ).
lo_layout->set_key( ls_key ).
* Allow default layout check box
lo_layout->set_default( abap_true ).
*setting the layout from my class CL_VARIANT
IF me->variant_obj->g_layout IS NOT INITIAL .
lo_layout->set_initial_layout( me->variant_obj->g_layout ) .
ENDIF .
ENDMETHOD .
METHOD display_alv .
* DATA: lx_msg TYPE REF TO cx_salv_msg.
* TRY.
* cl_salv_table=>factory(
* IMPORTING
* r_salv_table = me->o_alv
* CHANGING
* t_table = ME->FETCH_OBJ->IT_VBAP ).
* CATCH cx_salv_msg INTO lx_msg.
* ENDTRY.
CALL METHOD me->get_object( ) .
CALL METHOD me->layout_dis( ) .
me->o_alv->display( ).
ENDMETHOD.
ENDCLASS. "CL_ALV
DATA: o_sel TYPE REF TO cl_sel,
o_var TYPE REF TO cl_variant,
o_fetch TYPE REF TO cl_fetch,
o_display TYPE REF TO cl_alv.
INITIALIZATION .
CREATE OBJECT : o_sel,
o_var,
o_fetch EXPORTING ref_sel = o_sel ,
o_display EXPORTING ref_fetch = o_fetch ref_var = o_var .
cl_variant=>get_default( CHANGING cv_layout = p_vari ).
AT SELECTION-SCREEN .
o_sel->check_plant( p_werks ).
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_vari.
o_var->f4_variant( CHANGING cv_layout = p_vari ).
START-OF-SELECTION .
o_sel->s_werks = p_werks .
o_sel->s_vbeln = s_vbeln1[] .
.
* O_FETCH->FETCH_DATA( ).
o_fetch->arrange_data( ) .
END-OF-SELECTION .
o_display->display_alv( ).
Here is the output of the program.
Hope we were able to explain the Model View Controller concept and now you can use this in your real project. In these short 3 Parts series, we tried to showcase the benefits of OOPs and how we can decouple the Model, View, and Controller. ABAP mentors always say that OOPs if used correctly would make every ABAPer’s life easier. It is easier to maintain and support. But, until and unless we implement OOPs in real projects, we cannot learn enough.
So, dare to come out of the procedural programming language and venture into OOPs ABAP.