Enterprise Resource Planning Blogs by Members
Gain new perspectives and knowledge about enterprise resource planning in blog posts from community members. Share your own comments and ERP insights today!
cancel
Showing results for 
Search instead for 
Did you mean: 
PedroGuarita
Active Contributor
0 Kudos

So, i guess that at one time or another, all of us were faced with the daunting task of adding Z fields onto learning infotypes. Seems pretty easy, and in fact it's a picnic, we must admit. You have the tools to do it, the place, the whole "enchilada". Regarding the backend, there's no problem at all. But when you start talking about the portal, then we are talking about a whole different story. So, here is a case scenario that we faced and how we solved it in a simple way.

First, we will be looking into a particular functionality, the Change/Create course, but this solution will be applicable to other WD/Components. Let's have a look at the LSO_VC_COURSE_BASICDATA WD Component :

The field we are talking about in this case is the Interal Order field. This field was added to Infotype 1026 as shown below :

As you can see, we also have more Z fields added that are in another TAB (Control Options) but after you do it for one, since we are in the same framework, doesn't matter which TAB you are in.

So, first a few technical terms and knowledge on this WD. The used components defined in this WD include the MAIN view of LSO_MC_COURSE, which is the model component for Course Template. Basically, all of the data (SET/GET/SAVE) will be handled in the LSO_MC_COURSE. For us this is good to know, since we can see how it's done, but given the limitations of Enhancing the INTERFACECONTROLLER, we will have to go around this component to achieve what we want.

The prior solution implemented was working but it wasn't the most reliable. We plugged in some code in the COMPONENTCONTROLLER in the PROCESS_EVENT method. Basically, after invoking the SAVE method of the INTERFACECONTROLLER and getting the record from HRP1026 and updating the values. And this is where i kept banging my head, why can't we access the instantiated object of the Infotype and inject our values there before the save occured and that way not have to worry about the handling of the errors, the data inconsistency, etc, etc.

Therefore, investigating a bit on the LSO_MC_COURSE, we can see that there there is a instantiation of a specific class CL_LSO_BO_COURSE in method START_COURSE_OIF (along with instantiation of COURSETYPE class as parent class, in case you want to use it). And this is what we really want, this class is where all the objects for the infotypes are instantiated. So, from this point on, the solutions is pretty straight forward. From CL_LSO_BO_COURSE you reach CL_LSO_BO_PERSISTENT_PD through interfaces IF_LSO* and from this class we instantiate an object of the type CL_LSO_P_INFTY_NNNN (being NNNN the number of the infotype) through the methods GET_INFTY_NNNN_INSTANCE. Finally, in class CL_LSO_P_INFTY_1026 (in our specific case) we can access the bufferized object that has been created in the PD Framework and inject our values there.

So, after the "mambo jambo", let's see the practical application.

First we need to create an Interface in order to communicate our values to the Framework. So, i created the Interface with 2 methods (ok, this one has 3, the first was just for proof of concept) :

The methods we are implementing in the classes are ZSET_ADDITIONAL_DATA and ZGET_ADDITIONAL_DATA. After we created the interface, we need to add it to the classes we discussed earlier. So, i enhanced class CL_LSO_BO_COURSE, CL_LSO_BO_PERSISTENT_PD and added the interface :

I will just add one of the images here, the other class is basically the same implementation. Now, in the class CL_LSO_BO_COURSE i implement the methods locally with the following code :


METHOD Z6H_IF_LSO_ADDITIONAL_DATA~ZSET_ADITIONAL_DATA .



   DATA: lr_infotype TYPE REF TO object.


   DATA: lv_infty_class TYPE abap_classname,


         lv_infty_meth TYPE string VALUE 'SET_ADITIONAL_DATA',


         lv_persist_meth TYPE string,


         ptab TYPE abap_parmbind_tab,


         ptab_line TYPE abap_parmbind,


         etab TYPE abap_excpbind_tab,


         etab_line TYPE abap_excpbind.


   DATA: LR_HRBAS_PD_OBJECT TYPE REF TO IF_HRBAS_PD_OBJECT,


         LV_COURSE_STATUS TYPE LSO_COURSE_STATUS.


   DATA: exc_ref TYPE REF TO cx_sy_dyn_call_error,


         exc_text TYPE string.



   CONCATENATE 'CL_LSO_P_INFTY_' IV_INFTY INTO lv_infty_class.



   CLEAR : ptab_line, ptab[].


   ptab_line-name = 'IR_PD_OBJECT'.


   ptab_line-kind = cl_abap_objectdescr=>exporting.


   GET REFERENCE OF LR_HRBAS_PD_OBJECT INTO ptab_line-value.


   INSERT ptab_line INTO TABLE ptab.



   ptab_line-name = 'IV_STATUS'.


   ptab_line-kind = cl_abap_objectdescr=>exporting.


   GET REFERENCE OF LV_COURSE_STATUS INTO ptab_line-value.


   INSERT ptab_line INTO TABLE ptab.



   TRY.


   create object lr_infotype type (LV_INFTY_CLASS) parameter-table ptab.


     CATCH cx_sy_dyn_call_error INTO exc_ref.


       exc_text = exc_ref->get_text( ).


       MESSAGE exc_text TYPE 'I'.


   ENDTRY.



   CONCATENATE 'GET_INFTY_' IV_INFTY '_INSTANCE' INTO lv_persist_meth.


   TRY.


       CALL METHOD me->(lv_persist_methRECEIVING rr_return = lr_infotype.



     CATCH cx_sy_dyn_call_error INTO exc_ref.


       exc_text = exc_ref->get_text( ).


       MESSAGE exc_text TYPE 'I'.


   ENDTRY.



   CLEAR : ptab_line, ptab[].


   ptab_line-name = 'IT_FIELDS'.


   ptab_line-kind = cl_abap_objectdescr=>exporting.


   GET REFERENCE OF IT_FIELDS INTO ptab_line-value.


   INSERT ptab_line INTO TABLE ptab.



   ptab_line-name = 'IR_MESSAGE_HANDLER'.


   ptab_line-kind = cl_abap_objectdescr=>exporting.


   GET REFERENCE OF ir_message_handler INTO ptab_line-value.


   INSERT ptab_line INTO TABLE ptab.



   ptab_line-name = 'EV_SUCCESSFUL'.


   ptab_line-kind = cl_abap_objectdescr=>importing.


   GET REFERENCE OF ev_successful INTO ptab_line-value.


   INSERT ptab_line INTO TABLE ptab.



   TRY.


       CALL METHOD lr_infotype->(lv_infty_meth)


         PARAMETER-TABLE


         ptab.



     CATCH cx_sy_dyn_call_error.



   ENDTRY.



ENDMETHOD.



In this method we basic get the instance of the infotype class and then invoke the method SET_ADDITIONAL_DATA which we will create in the infotype class. Of course, i wanted to leave this as flexible as possible, so instead of using fixed names i used a bit of dynamic logic and i send the infotype as a parameter and construct the methods and objects i want to instantiate dynamically. I know i could have the GET and SET also dynamic and have just one method, but hey, nobody is perfect :wink:

Now, and like i stated before, we enhance class CL_LSO_P_INFTY_1026 and create both methods invoked in the previous step :

And the code implemented in the method :


METHOD SET_ADITIONAL_DATA .



   DATA: ls_buffer TYPE p1026.


   DATA: lv_object_begin_date TYPE begdatum.


   DATA: lv_object_end_date TYPE enddatum.


   DATA: lv_object_istat TYPE istat_d.


   DATA: ls_aditional_data TYPE z6h_st_lso_aditional_data.


   DATA: lv_fieldname TYPE string.


   FIELD-SYMBOLS: <field> TYPE any.



   ev_successful = true.



   read_buffered( ). "fill buffer if necessary



   IF buffer IS INITIAL.


     get_object_validity(


       IMPORTING


         ev_begin_date = lv_object_begin_date


         ev_end_date   = lv_object_end_date


         ev_istat      = lv_object_istat


     ).



     ls_buffer-mandt = sy-mandt.



     ls_buffer-plvar = pd_object->if_hrbas_universal_object~a_plvar.


     ls_buffer-otype = pd_object->if_hrbas_universal_object~a_otype.


     ls_buffer-objid = pd_object->a_objid.


     ls_buffer-infty = infotype.


     ls_buffer-istat = lv_object_istat.


     ls_buffer-begda = lv_object_begin_date.


     ls_buffer-endda = lv_object_end_date.



   ELSE.


     READ TABLE buffer INTO ls_buffer INDEX 1.


     REFRESH buffer.


   ENDIF.



   LOOP AT it_fields INTO ls_aditional_data.


     CONCATENATE 'LS_BUFFER-' ls_aditional_data-fieldname INTO lv_fieldname.


     ASSIGN (lv_fieldname) TO <field>.


     if <field> is ASSIGNED.


       <field> = ls_aditional_data-value.


     endif.


   ENDLOOP.


*  ls_buffer-zz_int_ord = iv_int_order.


   APPEND ls_buffer TO buffer.



   set_changed( ).



ENDMETHOD.



There, the hard part is done, we now have an easy way to access the bufferized object of the infotype instantiated in the PD framework within the portal, now it's just a matter of using it. Getting back to our LSO_VC_COURSE_BASICDATA. We created an overwrite method of PROCESS_EVENT in the COMPONENTCONTROLLER (not just for this) and we invoke a new method before the SAVE or in case of a  FPM_VIEW_SWITCH event :

And here is the code in the method :


METHOD set_internal_order .



   DATA lo_nd_zinternal_order TYPE REF TO if_wd_context_node.


   DATA lo_el_zinternal_order TYPE REF TO if_wd_context_element.


   DATA ls_zinternal_order    TYPE wd_this->element_zinternal_order.


   DATA lv_drp_key            TYPE wd_this->element_zinternal_order-drp_key.


   DATA lo_nd_basicdata       TYPE REF TO if_wd_context_node.


   DATA lo_el_basicdata       TYPE REF TO if_wd_context_element.


   DATA ls_basicdata          TYPE wd_this->element_basicdata.


   DATA lv_course_id          TYPE wd_this->element_basicdata-course_id.


   DATA lv_infty              TYPE infty.


   DATA ls_ad_data            TYPE z6h_st_lso_aditional_data.


   DATA lt_ad_data            TYPE z6h_tt_lso_aditional_data.


   DATA lv_zz_int_ord         TYPE hrp1026-zz_int_ord.



* navigate from <CONTEXT> to <ZINTERNAL_ORDER> via lead selection


   lo_nd_zinternal_order = wd_context->get_child_node( name = wd_this->wdctx_zinternal_order ).



* get element via lead selection


   lo_el_zinternal_order = lo_nd_zinternal_order->get_element( ).



* get single attribute


   lo_el_zinternal_order->get_attribute(


     EXPORTING


       name `DRP_KEY`


     IMPORTING


       value = lv_drp_key ).



   lv_zz_int_ord = lv_drp_key.



* navigate from <CONTEXT> to <BASICDATA> via lead selection


   lo_nd_basicdata = wd_context->get_child_node( name = wd_this->wdctx_basicdata ).



* get element via lead selection


   lo_el_basicdata = lo_nd_basicdata->get_element( ).



* get single attribute


   lo_el_basicdata->get_attribute(


     EXPORTING


       name `COURSE_ID`


     IMPORTING


       value = lv_course_id ).



   DATA: lo_instance  TYPE REF TO if_lso_bo_generic.


   DATA: lw_obj TYPE hrobject.



   lo_instance = wd_this->interfacecontroller->get_bo_instance( ).



   DATA: lr_additional TYPE REF TO Z6H_IF_LSO_ADDITIONAL_DATA.


   DATA: lr_message TYPE HRBAS_MSG_HANDLER_IF_REF,


         lv_sucess TYPE boole_d.


   TRY.


       lr_additional ?= lo_instance.


     CATCH cx_sy_move_cast_error.


   ENDTRY.



   lv_infty = '1026'.


   CLEAR : ls_ad_data, lt_ad_data[].



   ls_ad_data-fieldname = 'ZZ_INT_ORD'.


   ls_ad_data-value     = lv_zz_int_ord.


   APPEND ls_ad_data to lt_ad_data.



   lr_additional->zset_aditional_data(


     EXPORTING


       iv_infty           = lv_infty


       it_fields          = lt_ad_data


       ir_message_handler = lr_message


     IMPORTING


       ev_successful      =     lv_sucess ).




ENDMETHOD.


You can see how we use the instantiated class of CL_LSO_BO_COURSE to achieve all this. After this, i am sure that when there is a save that the information is "tucked" away in a safe place and i don't need to worry about all the hassles we all know that this type of updates involve.

I don't really know if there are other ways to achieve this, easier, cleaner, but until 3 months ago i had never heard of LSO, also WDA was something i just knew that existed but never worked with, so i am sure that it's possible. Any suggestions, feedback feel free to do so.

Labels in this area