Search This Blog

Thursday, November 11, 2010

ABAP Objects : ALV Grid Manipulations

The SAP List Viewer (ALV) is an integrated element of the ABAP Objects programming environment. It allows application developers to quickly implement the display of structured datasets .

With the ALV object model, you have the option of using almost unified programming techniques independent of the ALV tool in order to display various lists, tables or tree structures. The methods, parameters or classes only vary where tool-specific functions require a special procedure.

You are using the ALV object model with CL_SALV_TABLE class . you use the main ALV class to make all the settings that you need for the ALV output except one thing that you are not able to do which is "EDIT" your data inside the ALV grid.

If you about Modifying Subobjects of a Main Class CL_SALV_TABLE you'll see that the diagram looks like that :

When you see this diagram, you must be thinking to yourself that this is the final and absolute settlement of hierarchical objects that represent the Class of ALV But if we will deepen our research we find that beyond the this traditional object diagram hierarchy that is before us there's a number of hidden objects that can help us make that we can bring the CL_SALV_TABLE object into a situation where we can edit the information that is displayed on the ALV grid and record the changes into our internal table .

The official statement about useing the CL_SALV_TABLE object is: "Tables displayed with ALV are not ready for input".

Reverse engineering process:

we will take a look in the opposite order from bottom to top and we will found that CL_SALV_MODEL_LIST is the super class of the CL_SALV_TABLE class .

Another step up will bring us to:

Now we can see that we have a connection to CL_SALV_MODEL_BASE class .
Another step up will bring us to:

Another step up will bring us to:

If we get the ALV object from the ALV model class than we can easily make the ALV model ALV to "Set Ready for input". I have started looking the ALV object inside the ALV model.

At the first entry in the list of nodes ALV model CL_SALV_MODEL, I found the method GET_GRID  in class CL_SALV_FULLSCREEN_ADAPTER which can provide me the relevant instance of CL_GUI_ALV_GRID class.

But Class CL_SALV_MODEL has a protected attribute R_CONTROLLER which has everything we need to get to GRID object. and because of that we have a problem to get a direct reference for the CL_SALV_MODEL. At this point it's a bit problem to get access to a protected attribute since all of the components declared in the protected section are accessible to all methods of the classand of classes that inherit from it. Protected components form a special interface between a class and its subclasses.

Proposed Solution:
All of the components declared in the protected section are accessible to all methods of the class and of classes that inherit from it. this means that in our solution should based on inherited class that could have an access to the protected attribute R_CONTROLLER  of the class CL_SALV_MODEL .
The core target of the solution is to get an access to the GRID object . once we will have an access to the grid object we could make it editable and could handle every data change and respond to it as we want.

We start to build the solution with an inherited class that we should build . 
Inherited a local program class LCL_SALV_MODEL from the CL_SALV_MODEL_LIST.

Our local class will contain 2 main compoments one is for CL_SALV_CONTROLLER_MODEL class , and the second is for CL_SALV_ADAPTER class , by maintaining these attributes we could get an access to the CL_GUI_ALV_GRID class and get it's fieldcatalog table .

After getting the fieldcatalog table of CL_GUI_ALV_GRID object we could set the fields that we want to be editable by turn on the appropriate value within the edit field in the fieldcatalog structure.

After getting and setting the fieldcatalog table we need to take care about edit events . for that there's an regsitration method which should be called when we want to react to one of the edit events like when pressing enter or when calling an search help field , we also can do more things that related to the frontend fieldcatalog like check tables , search help etc.

The data syncronization taken care by the CL_ALV_CHANGED_DATA_PROTOCOL class
and therefor we need to create an local event handler class and actually tell to the CL_GUI_ALV_GRID object that we are about deal with the changed data .

Solution Diagram:

Solution Implementation:

Well , in my personal case i got a task to update PO profit segment with an appropriate billing document , so my example will contain some BAPI calls and data from EKKO & EKPO tables .


PROGRAM zzgmm_poprof_seg_update.
INCLUDE zzgmm_poprof_seg_update_top.
INCLUDE zzgmm_poprof_seg_update_sels.
INCLUDE zzgmm_poprof_seg_update_cls.

* Start of selection


CREATE OBJECT lo_report.
CHECK lo_report IS BOUND.
lo_report->get_data_from_db( ).



TABLES: ekpo, ekko.

TYPES: BEGIN OF ty_disp_action ,
             ebeln TYPE ekpo-ebeln,
             ebelp TYPE ekpo-ebelp,
             matnr TYPE ekpo-matnr,
             txz01 TYPE ekpo-txz01,
             menge TYPE ekpo-menge ,
            vbeln TYPE VBUK-VBELN,
TYPES: END OF ty_disp_action .

TYPES: tt_disp_action TYPE STANDARD TABLE OF zzgmm_poprof_seg_update.


* Global data

DATA: lo_report TYPE REF TO lcl_report,
            go_event_h TYPE REF TO lcl_event_handler.



SELECT-OPTIONS: so_ebeln FOR ekpo-ebeln,
                                so_ebelp FOR ekpo-ebelp.



CLASS lcl_salv_model DEFINITION INHERITING FROM cl_salv_model_list.


DATA: o_control TYPE REF TO cl_salv_controller_model,
           o_adapter TYPE REF TO cl_salv_adapter.

METHODS: set_model IMPORTING io_model TYPE REF TO cl_salv_model,


DATA: lo_model TYPE REF TO cl_salv_model.

* Event handler for the added buttons

CLASS lcl_event_handler DEFINITION.

METHODS: on_user_command FOR EVENT added_function OF cl_salv_events
                     IMPORTING e_salv_function,
                   data_changed FOR EVENT data_changed OF cl_gui_alv_grid
                    IMPORTING er_data_changed e_onf4 e_onf4_before e_onf4_after e_ucomm.


DATA: lv_edit_mode TYPE abap_bool VALUE abap_false,
           o_salv TYPE REF TO cl_salv_table,
           t_log TYPE STANDARD TABLE OF bapiret2.

METHODS : execute_bapi,


* Local Report class - Definition




DATA: t_data TYPE STANDARD TABLE OF zzgmm_poprof_seg_update,

o_salv TYPE REF TO cl_salv_table,

o_salv_model TYPE REF TO lcl_salv_model.






* Local Report class - Implementation



METHOD get_data_from_db.

SELECT ebeln ebelp matnr txz01 menge FROM ekpo


WHERE ebeln IN so_ebeln

AND ebelp IN so_ebelp

AND knttp = 'Y'

AND loekz <> 'L'.

IF LINES( me->t_data[] ) > 0.

me->generate_output( ).




ENDMETHOD. "get_data

METHOD generate_output.

DATA: lo_events TYPE REF TO cl_salv_events_table,

lo_msg TYPE REF TO cx_salv_msg,

lo_event_h TYPE REF TO lcl_event_handler,

lo_alv_mod TYPE REF TO cl_salv_model,

lv_msg TYPE string .


cl_salv_table=>factory( EXPORTING list_display = abap_false

IMPORTING r_salv_table = me->o_salv

CHANGING t_table = t_data[] ).

CATCH cx_salv_msg.

lv_msg = lo_msg->get_text( ).

MESSAGE lv_msg TYPE 'I'.



o_salv->set_screen_status( pfstatus = 'ZALV_STANDARD'


set_functions = o_salv->c_functions_all ).

lo_events = o_salv->get_event( ).

CREATE OBJECT lo_event_h.

SET HANDLER lo_event_h->on_user_command FOR lo_events.

lo_alv_mod ?= o_salv.

CREATE OBJECT o_salv_model.

IF o_salv_model IS BOUND .

o_salv_model->set_model( io_model = lo_alv_mod ).


* o_salv->set_screen_popup( start_column = 1

* end_column = 100

* start_line = 1

* end_line = 20 ).

o_salv->display( ).

ENDMETHOD. "generate_output



* LCL_SALV_MODEL implementation



* data encapsulation calss

METHOD set_model.

* save model

lo_model = io_model.

ENDMETHOD. "set_model

METHOD set_controller.

* save controller

o_control = lo_model->r_controller.

ENDMETHOD. "set_controller

METHOD set_adapter.

* save adapter from controller

o_adapter ?= lo_model->r_controller->r_adapter.

ENDMETHOD. "set_adapter



* Event Handler for the ALV


CLASS lcl_event_handler IMPLEMENTATION.

METHOD on_user_command.

DATA: lo_grid TYPE REF TO cl_gui_alv_grid,

lo_fullscreen_adapter TYPE REF TO cl_salv_fullscreen_adapter,

lt_fieldcatalog TYPE lvc_t_fcat,

ls_layout TYPE lvc_s_layo.


CASE e_salv_function.


lo_report->o_salv_model->set_controller( ).

lo_report->o_salv_model->set_adapter( ).

lo_fullscreen_adapter ?= lo_report->o_salv_model->o_adapter.

lo_grid = lo_fullscreen_adapter->get_grid( ).

IF lo_grid IS BOUND.

* lo_grid->get_frontend_layout( IMPORTING es_layout = ls_layout ).

* ls_layout-edit = 'X'.

* lo_grid->set_frontend_layout( is_layout = ls_layout ).

lo_grid->get_frontend_fieldcatalog( IMPORTING et_fieldcatalog = lt_fieldcatalog[] ).

READ TABLE lt_fieldcatalog[] ASSIGNING WITH KEY fieldname = 'VBELN'.


-checktable = 'VBRK'.

IF -edit = 'X'.

me->lv_edit_mode = -edit = space .


me->lv_edit_mode = -edit = 'X'.


lo_grid->set_frontend_fieldcatalog( EXPORTING it_fieldcatalog = lt_fieldcatalog[] ).

lo_grid->register_edit_event( EXPORTING i_event_id = cl_gui_alv_grid=>mc_evt_enter ).

lo_grid->register_edit_event( EXPORTING i_event_id = cl_gui_alv_grid=>mc_evt_modified ).

CREATE OBJECT go_event_h.

SET HANDLER go_event_h->data_changed FOR lo_grid.

lo_grid->refresh_table_display( ). "must after cahnge the fieldcatalog table



me->execute_bapi( ).


ENDMETHOD. "on_user_command

METHOD data_changed .


READ TABLE er_data_changed->mt_mod_cells[] ASSIGNING INDEX 1.


ENDMETHOD. "data_changed

METHOD execute_bapi.

CHECK me->lv_edit_mode = 'X'.

FIELD-SYMBOLS : TYPE zzgmm_poprof_seg_update.

DATA : lt_poaccount TYPE STANDARD TABLE OF bapimepoaccount,

lt_poaccountx TYPE STANDARD TABLE OF bapimepoaccountx,

lt_poaccountprofitsegment TYPE STANDARD TABLE OF bapimepoaccountprofitsegment,

ls_poaccount TYPE bapimepoaccount,

ls_poaccountx TYPE bapimepoaccountx,

ls_poaccountprofitsegment TYPE bapimepoaccountprofitsegment,

lt_return TYPE STANDARD TABLE OF bapiret2.

LOOP AT lo_report->t_data[] ASSIGNING WHERE vbeln IS NOT INITIAL.

ls_poaccountprofitsegment-po_item = ls_poaccountx-po_item = ls_poaccount-po_item =


ls_poaccountprofitsegment-serial_no = ls_poaccountx-serial_no =

ls_poaccount-serial_no = '01'.

APPEND ls_poaccount TO lt_poaccount[].

ls_poaccountx-profit_ctr = 'X'.

APPEND ls_poaccountx TO lt_poaccountx[].

ls_poaccountprofitsegment-fieldname = 'WW004'.

ls_poaccountprofitsegment-value = -vbeln.

APPEND ls_poaccountprofitsegment TO lt_poaccountprofitsegment[].



purchaseorder = -ebeln


return = lt_return[]

poaccount = lt_poaccount[]

poaccountprofitsegment = lt_poaccountprofitsegment[]

poaccountx = lt_poaccountx[].


APPEND LINES OF lt_return[] TO me->t_log[].


me->display_log( ) .

ENDMETHOD . "execute_bapi

METHOD display_log.

DATA: lexc_msg TYPE REF TO cx_salv_msg,

lv_msg_string TYPE string .

CHECK LINES( me->t_log[] ) > 0 .


cl_salv_table=>factory( EXPORTING list_display = abap_true

IMPORTING r_salv_table = me->o_salv

CHANGING t_table = me->t_log[] ).

CATCH cx_salv_msg INTO lexc_msg.

lv_msg_string = lexc_msg->get_text( ).

MESSAGE lv_msg_string TYPE 'I'.


me->o_salv->display( ).

ENDMETHOD . "display_log


Result Screen:

As you can see we have an ALV grid with editable fields .
Enjoy ,
Harel Gilor .

Wednesday, July 14, 2010

SAP CRM : How to use your custom BOL Object for webclient within assignment block

In the last Blog i showed how can you create your own custom BOL object that meet your personal requirements .

Now that we have our BOL Object we might want to use it for our purposes.

Let's say that we want to add several fileds to assignment block and display them on the screen ,for instance we take the fields from our Z table the we use in the creation of our BOL Object .
we also want to keep the connection for the original transaction context .
the steps for doing that are :
1) we need to add a filed to our Z table BP_GUID which will keep us connected to the main transaction context .
2) perform the adjustment for the Z Table
3) Search for the right BP_GUID that fit to our user account .
4) create new record that contain the details that we want to diasply for the spesific account

See the following screen shot.

 Here we should add the BP_GUID feild to our table and mark the field as a part of the table key .
See the following screen shot

Now we need to perform some adjustment for our table . choose the Database Utility from the menu
See the following screen shot

once you choose this option you'll get the next screen .

In our case we will choose the Delete data option becuse we want to create a new record, you also have to know that if you choose the Save data option there's no 100  percent guarantee that your data in the table will be saved , so you should consider the use of Database Utility very careful. 

After we perform the adjustment and activated the table we can step forward to the next step which is the creation of a new record with the details that we want to display on the screen . 

First to the record creation we should find the key which is the BP_GUID that will connect us to the account context . 
 to find the right BP_GUID enter transaction GENIL_BOL_BROWSER and insert the BP_APPL_COND  in the component set field.
See the following screen shot
when you enter to the component set choose the predefined search named BuilEmpSearch  
 See the following screen shot.

Now we can look for any partner , in my case i'll choose to search  my user and i'll get the compatible

BP_GUID which is : DF21EBC73A26F8F1A2B0001F29644FC0
See the following screen shot.

 Now let's create a new record in the DataBase table ZUSR_BOL_TB with user name, BP_GUID , First and last names.
See the following screen shot.

for now we finish with the database side and we wiil go to perform some improvement in our BOL object to support the different searching options.

so let's go to transaction SE24 and put ZCL_OWN_BOL in the input filed.
click on Local Types button and put the lines inside :

TYPES: BEGIN OF ty_general_rng ,
                sign TYPE bapisign,
                option TYPE bapioption,
                low TYPE string ,
               high TYPE string ,
END OF ty_general_rng .

TYPES: tt_general_rng TYPE STANDARD TABLE OF ty_general_rng . 
See the following screen shot:

Now go back to the method list and edit the method :IF_GENIL_APPL_INTLAY~GET_DYNAMIC_QUERY_RESULT
replace the exsiting code with the bellow lines:
METHOD if_genil_appl_intlay~get_dynamic_query_result.

DATA: lr_object TYPE REF TO if_genil_cont_root_object,
            lt_result TYPE TABLE OF zusr_bol_st,
            lv_dyn_where TYPE string,
            lv_len TYPE i ,
            ls_range TYPE ty_general_rng.

DATA: lt_bp_guid TYPE tt_general_rng,
            lt_bname_range TYPE tt_general_rng,
           lt_name_first TYPE tt_general_rng,
           lt_name_last TYPE tt_general_rng.

FIELD-SYMBOLS: "" TYPE zusr_bol_st,
                                "" TYPE genilt_selection_parameter.

* decomposition of selection parameters and build a dynamic where condition

LOOP AT it_selection_parameters[] ASSIGNING "".
   MOVE: ""-sign TO ls_range-sign ,
                ""-option TO ls_range-option ,
                ""-low TO ls_range-low ,
                ""-high TO ls_range-high.

CASE -attr_name.
     APPEND ls_range TO lt_bname_range[].
     CONCATENATE lv_dyn_where 'bname in lt_bname_range[] and'
         INTO lv_dyn_where  SEPARATED BY space .
 APPEND ls_range TO lt_bp_guid[].
CONCATENATE lv_dyn_where 'bp_guid in lt_bp_guid[] and'
   INTO lv_dyn_where SEPARATED BY space .

APPEND ls_range TO lt_name_first[].
CONCATENATE lv_dyn_where 'name_first in lt_name_first[] and'
  INTO lv_dyn_where SEPARATED BY space .


APPEND ls_range TO lt_name_last[].
CONCATENATE lv_dyn_where 'name_last in lt_name_last[] and'
  INTO lv_dyn_where SEPARATED BY space .




lv_len = STRLEN( lv_dyn_where ) - 4 . "remove the last 'AND' in the condition
lv_dyn_where = lv_dyn_where(lv_len) .

CHECK STRLEN( lv_dyn_where ) > 0.

SELECT * FROM zusr_bol_tb INTO TABLE lt_result[] WHERE (lv_dyn_where).

CHECK LINES( lt_result[] ) > 0.

LOOP AT lt_result[] ASSIGNING "".

lr_object = iv_root_list->add_object( iv_object_name = 'Root'
                                                             is_object_key = ""-bname ).

CHECK lr_object IS BOUND.
lr_object->set_query_root( abap_true ).

Save and Activate your code .

We might want to add the BNAME ,NAME_LAST,NAME_FIRST fileds to a view that we will create
personaly i choosed the BP_HEAD component but you can choose any component that you may use .

 OK. So let's get in and execute transaction BSP_WD_CMPWB_NEW and choose BP_HEAD component with our enhancement set which is spesificaly in my case called ZENH_CRM and hit the  display button and go to Runtime Repository Editor , open the MODELS folder to see with which BOL component set the BP_HEAD component use.

See the following screen shot.

we can see now that the component BP_HEAD use the Model BP_APPL_COND and we want that our BOL object will appear in the repository of the BP_HEAD component so that we can use it for our pupose.

To do that we need to go to the component set definition .
See the following screen shot.
now choose the appropriate component set which is in our case BP_APPL_COND.
See the following screen shot.
now click on component assignment make new entry and add our BOL object named ZBOLOB.
See the following screen shot.
don't forget to save the new entry .
after you saved it successfully enter again to BSP_WD_CMPWB_NEW transaction with BP_HEAD component and click on the BOL Model Browser tab , we want to check here that we can see our BOL entity ready for use .
See the following screen shot.

Now we can see that our BOL entity Root that we created is ready for use in the enhancement of BP_HEAD component.  We will go to Component Structure Browser tab and create our view .
See the following screen shot.

we'l choose a name for our view .
See the following screen shot.

 after we named the view we need to add 2 model nodes , one for BuilHeader and one
 for our Root BOL object .
 The first one is for getting the relevant BP_GUID from the custom controller and get the appropriate
 first name last name and user name accroding to our table .

 See the following screen shot.

The next step is to add the fields that we might use in our view to context which we working on .
we start with the BuilHeader fields .

See the following screen shot.

we use also attributes from the Root BOL object .
See the following screen shot.

we got the whole attributes as shown bellow.
See the following screen shot.

we move to the nest step .
here we need to define the view type and it's properties.
the view type we choose form view without buttons and in the properties we make it configurable 
and that for keep to our self the option to Add/Remove attributes from the view .
See the following screen shot.

Now we finish the view addtion with the wizard we will see that our view is listed with
the other views in the component.
See the following screen shot.

After we added the view we should create a binding to the custom controiller context node BUILHEADER.

so we put the mouse curser on the context node BUILHEADER pressing teh right mouse
click and create the binding .
See the following screen shot.

we will create the binding for our context node to the same context node in the custom controller.
See the following screen shot.

After we create the apropriate binding we need to add the attributes to the view .
so we move to the configuration tab when we working on the view and the we switch to edit mode .
we see that we have the 2 BOL objects that we added when we wroking with the wizard in time of the view creation .

in our case we will add the attributes from the Root BOL object .
See the following screen shot.

After we add the attributes to our view we need to add our view as an assignment block .
First we need to add our view to the BPHEADOverview viewset , to do this we will go to the
Runtime Repository Editor and we will add our view to the OverviewPage of
BPHEADOverview viewset .

See the following screen shot.

After we add the view to the BPHEADOverview viewset we will go to the configuration tab of
BPHEADOverview viewset and switch to edit mode and add our view TestBOLroot to the list of views
that appears as assignment blocks in the BPHEADOverview viewset by click on the arrow button
and moving it from the left side to the right side .

See the following screen shot.

After we handle the addition of adding our view as an assignment block we need to handle some code
addtions for the right treatment in the attributes in our view .

First we will go to the ROOT context node within our view and redefine
the method IF_BSP_MODEL~INIT.

See the following screen shot.

Put the next ilnes into the method save and aactivate the method.

TYPES: BEGIN OF ltype_attr_struct,
                       mandt TYPE mandt,
                       bp_guid TYPE bu_partner_guid,
                       bname TYPE xubname,
                      name_last TYPE bu_namep_l,
                      name_first TYPE bu_namep_f,
END OF ltype_attr_struct.

DATA: lrf_data_struct TYPE REF TO ltype_attr_struct,
           lrf_value_node TYPE REF TO cl_bsp_wd_value_node,
           lrf_bol_collection TYPE REF TO if_bol_bo_col.

super->if_bsp_model~init( id = id owner = owner ).

CREATE DATA lrf_data_struct.

CREATE OBJECT lrf_value_node
          iv_data_ref = lrf_data_struct.

CREATE OBJECT lrf_bol_collection TYPE cl_crm_bol_bo_col.
 lrf_bol_collection->add( lrf_value_node ).
 me->set_collection( lrf_bol_collection ).
After we add the above lines to the method we will go to the implementation class of our view,
which is in our case is ZL_ZBP_HEAD_TESTBOLROOT_IMPL.

we will double click on the class name and we will move to display mode of the class in a way that
all the method could be shown. 

The first thing to do is to write our own method for performing the right query when we need to
display the attributes on the screen .

After that we will redefine the method DO_PREPARE_OUTPUT.

so let's create a new method named root_query which is an Instance Method , and the visibility of
it is Private.

put the next lines to the method .

METHOD root_query.

DATA: lrf_advanced_query TYPE REF TO cl_crm_bol_dquery_service,
           lrf_root_result_collection TYPE REF TO if_bol_entity_col,
           lrf_root_result_bol_entity TYPE REF TO cl_crm_bol_entity,
           lrf_current_root TYPE REF TO if_bol_bo_property_access,
           lrf_current_builheader TYPE REF TO if_bol_bo_property_access,
           lt_params TYPE crmt_name_value_pair_tab,
           ls_params TYPE crmt_name_value_pair,
           ls_builheader_properties TYPE crmst_header_object_buil,
           lv_low_value TYPE string ,
           ls_root_properties TYPE zusr_bol_st.

ls_params-name = 'MAX_HITS' .
ls_params-value = '1'. "we don't need in this case more the single record for result
APPEND ls_params TO lt_params.

lrf_current_root ?= me->typed_context->root->collection_wrapper->get_current( ).
CHECK lrf_current_root IS BOUND .

lrf_current_root->get_properties( IMPORTING es_attributes = ls_root_properties ).

CHECK ls_root_properties IS INITIAL . " means that we don't have any values in our
                                                                 "fields and  therfore wi'll excute the query
lrf_current_builheader ?=
  me->typed_context->builheader->collection_wrapper->get_current( ).

CHECK lrf_current_builheader IS BOUND .

lrf_current_builheader->get_properties( IMPORTING es_attributes = ls_builheader_properties ).

CHECK ls_builheader_properties-bp_guid IS NOT INITIAL .

lv_low_value = ls_builheader_properties-bp_guid.
lrf_advanced_query = cl_crm_bol_dquery_service=>get_instance( 'Search' ).
lrf_advanced_query->set_query_parameters( it_parameters = lt_params ).
lrf_advanced_query->add_selection_param( iv_attr_name = 'BP_GUID'
                                                                iv_sign = 'I'
                                                                iv_option = 'EQ'
                                                                iv_low = lv_low_value
                                                                iv_high = '' ).

lrf_root_result_collection = lrf_advanced_query->get_query_result( ).

IF lrf_root_result_collection IS BOUND . "if we found something that match our selection

lrf_root_result_bol_entity = lrf_root_result_collection->get_first( ).
lrf_root_result_bol_entity->get_properties( IMPORTING es_attributes = ls_root_properties ).
lrf_current_root->set_properties( is_attributes = ls_root_properties ).

Save and activate your code .
the next step is to redefine the method DO_PREPARE_OUTPUT.

put the next lines into the method .



                  iv_first_time = iv_first_time.

me->ROOT_QUERY( ).



save and activate your code .

Now all job is done our BOL object is wroking like a Rolls-Royce and it's time to derive pleasure from .
We will go to the webclient , choose our account and display it .

we can see that we have our attributes shown on the screen .

See the following screen shot.

Wednesday, June 30, 2010

SAP CRM How to Create your own BOL Object for webclient

BOL :  Business Object Layer.

Using the BOL and its uniform application programming interface (API) to access business

data offers considerable advantages compared to the various APIs typically available for
business objects:

1) The object-oriented BOL API is simple, uniform, and easy to use.

2) The built-in buffer accelerates your applications.

3) You can isolate your programs from any interface changes in the underlying business
    object-specific APIs.

4) Development of SAP CRM applications is easy since the BOL has been designed to work hand-in-hand  with the UI parts of the

CRM WebClient UI framework.

It is possible to enhance the BOL to cover business data not yet supported. After the
corresponding business objects and query services have been modeled and implemented,
you can use them at runtime.

Usually we are not need to build a BOL object. We will end up changing the existing BOL. I came across a situation that I need a screen for a component and create a screen for add/ modify and search the Z table.

In this post let us see how we create a custom BOL object for a Z table. Once this bol is create we need to add this bol in the custom view and show that on the screen. For now let us see how we can create a custom BOL.

Let's create a z table . See the following screen shot for the table.

Let us use this table and create a bol for this table to add,modify and search data.

OK. Now we need to define two tables. One for the data elements that we are going to use in the BOL and another for the hierarchy of the data element that we are going to use in the BOL object. Once these table are defined then we need to mention the class module that we are going to use for the BOL object. All these information are configured in the spro for this bol object.

See the following screen shot.

In the above configuration if you enter the Basic setting you will see all the BOL object defined here.
OK for our custom let us define the bol object as ZBOLOB.
See the following screen.

Now select the component Definition(the first one) and click on the new entries. Let us define the BOL component and add the details.
See the following screen.
Add a Component Set Definition. See the follow screen shot.

 Now assign the Component definition to the component set definition.
See the following screen shot.

 Now the BOL component is assigned to the Component Set definition. You can assign More than one BOL component to the component set.

We need to give one implementation class and two tables one is Object table and another is Model table. Now let us create all these object.

One important thing with creating the implementation class is you need to specify super class for this implementation class and redefine specific methods.
For Creation a record, editing the record (modify) and for Searching you have redefine specific super class methods. While creating views (insert, edit and search) you need to specify specific Super class(not the methods).
See the following screen shots.

and ZBOL_MODEL table

Now we need to define a check table  .  See the following screen shots.

 don't forget to Activate your tables .

Now let us create the data structure and add the structure and the data structure model in this BOL object tables. For this example this is a plain simple data structure and I am going to add the hierarchy for search.

See the following screen shot for the structure.

Now add this information in the BOL object Tables. Look at the following infomration I entered in the table. Always the object table needs to have a root object and the other object that are associated to their parent. Here all the object are associated to the root. If you have complex hierarchy then it takes time to do the correct design and come up with the information. Always try to see the existing code in the sap component and try to use that , this way it will be always easy to follow.
See the following screen shot for the data.

In this example we have only one relation ship and all the result and search structure belongs to the root node.
Now let us create the implementation class for this BOL.
go to SE24 Transaction Create a new class ZCL_OWN_BOL using the class CL_CRM_GENIL_ABSTR_COMPONENT2 as super class. This is very important. If you didn’t inherit that super class they BOL object don’t understand your z class module. See the following screen shot for the bol implementation class module creation.


Now you need to redifine the methods from the super class. Because we are planning this bol object for search let us redefine all the methods that are associated for search.
See the following screen shot.

Now you need to change the code in all the above method to fit your bol objects. First let us put our bol tables in this Get_Objects and Get_Model. See the following code cut and paste in the right methods.

*--------------------------------Code For The Methods-----------------------------------------
METHOD if_genil_appl_intlay~get_dynamic_query_result.

DATA: lr_object TYPE REF TO if_genil_cont_root_object,
             lt_result TYPE TABLE OF zusr_bol_st.

FIELD-SYMBOLS: TYPE zusr_bol_st.

SELECT * FROM zusr_bol_tb INTO TABLE lt_result.


lr_object = iv_root_list->add_object( iv_object_name = 'Root'
                                                             is_object_key = -bname ).
CHECK lr_object IS BOUND.
lr_object->set_query_root( abap_true ).


METHOD if_genil_appl_intlay~get_objects.

DATA: lr_object TYPE REF TO if_genil_container_object,

lr_msg_cont TYPE REF TO cl_crm_genil_global_mess_cont,

lv_name_obj TYPE zusr_bol_tb-bname,

lr_attr_props TYPE REF TO if_genil_obj_attr_properties,

l_result TYPE zusr_bol_st,

lv_name TYPE crmt_ext_obj_name.

lr_object = iv_root_list->get_first( ).

lr_msg_cont ?= iv_root_list->get_global_message_container( ).

WHILE lr_object IS BOUND.

lv_name = lr_object->get_name( ).

IF lr_object->check_attr_requested( ) = abap_true.

lr_object->get_key( IMPORTING es_key = lv_name_obj ).

SELECT SINGLE * FROM zusr_bol_tb


l_result WHERE bname = lv_name_obj.

lr_object->set_attributes( l_result ).

lr_attr_props = lr_object->get_attr_props_obj( ).

lr_attr_props->set_all_properties( if_genil_obj_attr_properties=>read_only ).

lr_object = iv_root_list->get_next( ).


METHOD if_genil_appl_model~get_model.

SELECT * FROM zbol_model


OF TABLE rt_relation_det.

METHOD if_genil_appl_model~get_object_props.

SELECT * FROM zbol_object



*--------------------------------End Of Code-----------------------------------------------------

Now compile the code and let us go the the BOL browser and execute this object and see if this is working.

Go to the Transaction GENIL_BOL_BROWSER.
Now give the bol component name. Ours is ZBOLOB.
See the following screen shots.

See the following screen shot for the records in the z table. Double click on the Search.
you should see the Object browse and the dynamic parameter on the right hand side.
See the screen shots below.

i added the following lines to my z table and here you can do the same as you want to add.

Remove the line in the dynamic Query Parameter using the – sign and click fine this will return all the records. Double click on the values and you will see the rows.
See the following screen shots.

 Now you just hit the find button and get the result from the search .
See the following screen shots.

Once we got the results then this gives us that our BOL object is working.

Now if you hit the children button you'l get the predefined relation that we define eralier .
See the following screen shots.

Now we can attach the BOL object to the View in the webclient component and we can create a search and result view.