Spend Management Blogs by Members
Check out community member blog posts about spend management and SAP Ariba, SAP Fieldglass, and SAP Concur solutions. Post or comment about your experiences.
cancel
Showing results for 
Search instead for 
Did you mean: 
RicardoRomero_1
Active Contributor
4,358

Some time ago I created a document to show how can display some custom data in a SRM document adding a custom button and calling a custom WebDynpro:
http://scn.sap.com/docs/DOC-25271

But this example only shows how to display some data, not how to update the data. In this new document we’re going to see other way to do it and with the possibility to update the data when you are editing the document.

Other posibilities
If you only want to add custom fields to your document you can follow this document:
http://scn.sap.com/docs/DOC-25692

If you want to extend a document with only a custom table at item and/or header level you can use this one:
http://scn.sap.com/docs/DOC-28602

In this new example we’re going to create a custom table to save some data related with the items of our SRM document. You can enhance this solution adding other tables or custom fields.



1. First thing to do is create a custom table to store the data


We have as Key fields the following fields:

- OBJECT_ID: Id of the SRM Document.
- NUMBER_INT: Id of the SRM Document item.
- CUSTOM_POS: A counter with the “sub-item” number.

In this case, as example, we have the fields Quantity and Unit.

We’re going to create also a table type for this table:


2. Function Group to save and retrieve the data


We are going to use a FG to get, set and save the data. The reason for this is that while the caller program of any FM in this FG is alive the data stored in the include TOP is alive.


So we can store temporary data in the TOP and use any FM in this FG to retrieve or save the data from our custom WebDynpro but also from the badis BBP_DOC_CHANGE_BADI, BBP_DOC_CHECK_BADI, BBP_DOC_SAVE_BADI, etc.

Goto SE80 and create a FG ZZCUSTOM_DATA. Go to the include TOP and create the internal table to store the data.

Create the following function modules:

2.1 Function Module ZZCUSTOM_DATA_SELECT

We will use this FM to select the data stored in the database.


ZZCUSTOM_DATA_SELECT

FUNCTION ZZCUSTOM_DATA_SELECT.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IV_OBJECT_ID) TYPE  CRMT_OBJECT_ID_DB
*"----------------------------------------------------------------------

* Select the data only if we don't have it yet.
IF gt_custom_data IS INITIAL.

  SELECT *
   INTO TABLE gt_custom_data
   FROM zzcustom_data
   WHERE object_id EQ iv_object_id.

ENDIF.

ENDFUNCTION
.


2.2 Function Module ZZCUSTOM_DATA_GET

We will use this FM to get the data related with one of the document items.


ZZCUSTOM_DATA_GET
FUNCTION zzcustom_data_get.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IV_NUMBER_INT) TYPE  CRMT_ITEM_NO
*"  EXPORTING
*"     REFERENCE(ET_CUSTOM_DATA) TYPE  ZZCUSTOM_DATA_T
*"----------------------------------------------------------------------

FIELD-SYMBOLS: <ls_custom_data> TYPE zzcustom_data.

LOOP AT gt_custom_data
  ASSIGNING <ls_custom_data>
  WHERE number_int EQ iv_number_int.

  APPEND <ls_custom_data> TO et_custom_data.
ENDLOOP.

ENDFUNCTION
.



2.3 Function Module ZZCUSTOM_DATA_SET


We will use this FM to update the data stored in our TOP include.


ZZCUSTOM_DATA_SET

FUNCTION zzcustom_data_set.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IV_NUMBER_INT) TYPE  CRMT_ITEM_NO
*"  CHANGING
*"     REFERENCE(CT_CUSTOM_DATA) TYPE  ZZCUSTOM_DATA_T
*"----------------------------------------------------------------------

DATA: lv_counter TYPE sytabix VALUE 1.

FIELD-SYMBOLS: <ls_custom_data> TYPE zzcustom_data.

* Before to update delete the previous data:
DELETE gt_custom_data WHERE number_int EQ iv_number_int.

* Update the item counter;
LOOP AT ct_custom_data

ASSIGNING <ls_custom_data>.


  <ls_custom_data>
-custom_pos = lv_counter.
  ADD 1 TO lv_counter.


ENDLOOP.

APPEND LINES OF
ct_custom_data TO gt_custom_data.

ENDFUNCTION.



2.4 Function Module ZZCUSTOM_DATA_SAVE

We will use this FM to save the data in database.


ZZCUSTOM_DATA_SAVE
FUNCTION zzcustom_data_save.
*"--------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(IV_OBJECT_ID) TYPE  CRMT_OBJECT_ID_DB
*"--------------------------------------------------------------------

* Delete old data, in case some item was deleted:
DELETE FROM zzcustom_data WHERE object_id EQ iv_object_id.

* Insert the data updated;
INSERT zzcustom_data FROM TABLE gt_custom_data.

ENDFUNCTION.




3. Creating the custom WebDynpro


Goto SE80 and create the Web Dynpro Component ZZCUSTOM_DATA.


3.1 Go to the Component Controller and create an interface node INFO with the following attributes:



We will use this interface node to pass some data from the standard component.



3.2 Create also a table node to store the custom data:


3.3 Get and save the data

We are going to get the data every time the window is opened and set the data every time the window is closed.

Map the nodes created in the component controller within the window context:



Go to the method WDDOONOPEN of the window and add the following code:


With this we obtain the data related with one of the items every time the window is opened.


WDDOONOPEN
method WDDOONOPEN .

DATA:
ls_info          
TYPE wd_this->element_info,
lo_nd_info       
TYPE REF TO if_wd_context_node,
lo_el_info       
TYPE REF TO if_wd_context_element,
lo_nd_custom_data
TYPE REF TO if_wd_context_node,
lt_custom_data   
TYPE wd_this->elements_custom_data.

* Get the info passed through the interface node;
lo_nd_info
= wd_context->get_child_node( name = wd_this->wdctx_info ).
lo_el_info
= lo_nd_info->get_element( ).
lo_el_info
->get_static_attributes(
   IMPORTING
    static_attributes
= ls_info ).

* Select the data stored in the database;
CALL FUNCTION 'ZZCUSTOM_DATA_SELECT'
  EXPORTING
   iv_object_id
= ls_info-object_id.

* Get the data for this item;
CALL FUNCTION 'ZZCUSTOM_DATA_GET'
  EXPORTING
   iv_number_int
= ls_info-number_int
  IMPORTING
   et_custom_data
= lt_custom_data.

lo_nd_custom_data
= wd_context->get_child_node( name = wd_this->wdctx_custom_data ).
lo_nd_custom_data
->bind_table( new_items = lt_custom_data set_initial_elements = abap_true ).

endmethod.


Add the following code in the method WDDOEXIT:


With this we update the internal table we have as a "buffer" in the include TOP of our FG.


WDDOEXIT
DATA:
ls_info          
TYPE wd_this->element_info,
lt_custom_data   
TYPE wd_this->elements_custom_data,
lo_nd_custom_data
TYPE REF TO if_wd_context_node,
lo_nd_info       
TYPE REF TO if_wd_context_node,
lo_el_info       
TYPE REF TO if_wd_context_element.

*
lo_nd_custom_data
= wd_context->get_child_node( name = wd_this->wdctx_custom_data ).
lo_nd_custom_data
->get_static_attributes_table( IMPORTING table = lt_custom_data ).

lo_nd_info
= wd_context->get_child_node( name = wd_this->wdctx_info ).
lo_el_info
= lo_nd_info->get_element( ).
lo_el_info
->get_static_attributes(
  IMPORTING
   static_attributes
= ls_info ).

CALL FUNCTION 'ZZCUSTOM_DATA_SET'
  EXPORTING
   iv_number_int      
= ls_info-number_int
  CHANGING
   ct_custom_data     
= lt_custom_data.


3.4 Go to the Main View and create a table to display the data:

First you need to map the nodes in the context.


Create the table and a toolbar with two buttons to add and remove rows. Create an action for each button.

Create also an input field to display the item number and check the property READ_ONLY of this field.



Bind the property READ_ONLY of the table with the attribute READ_ONLY we have in the context:


In this way we control when the table will be editable depending on the value of this attribute.

Bind the property VISIBLE of the toolbar with the Inverse of the attribute READ_ONLY to not display the toolbar when we enter in Display mode:


Add the following code to the action ADD_ROW:


ONACTIONADD_ROW
METHOD onactionadd_row .

DATA:
ls_info          
TYPE wd_this->element_info,
ls_custom_data   
TYPE wd_this->element_custom_data,
lt_custom_data   
TYPE wd_this->elements_custom_data,
lo_nd_info       
TYPE REF TO if_wd_context_node,
lo_nd_custom_data
TYPE REF TO if_wd_context_node,
lo_el_info       
TYPE REF TO if_wd_context_element.

lo_nd_custom_data
= wd_context->get_child_node( name = wd_this->wdctx_custom_data ).
lo_nd_custom_data
->get_static_attributes_table( IMPORTING table = lt_custom_data ).

lo_nd_info
= wd_context->get_child_node( name = wd_this->wdctx_info ).
lo_el_info
= lo_nd_info->get_element( ).
lo_el_info
->get_static_attributes(
  IMPORTING
   static_attributes
= ls_info ).

* Add an initial row;
ls_custom_data
-object_id    = ls_info-object_id.
ls_custom_data
-number_int   = ls_info-number_int.
APPEND ls_custom_data TO lt_custom_data.

lo_nd_custom_data
->bind_table( new_items = lt_custom_data set_initial_elements = abap_true ).

ENDMETHOD.


And to the action REMOVE_ROW


ONACTIONREMOVE_ROW

method ONACTIONREMOVE_ROW .

DATA:
lo_nd_custom_data
TYPE REF TO if_wd_context_node,
lo_el_custom_data
TYPE REF TO if_wd_context_element.

lo_nd_custom_data
= wd_context->get_child_node( name = wd_this->wdctx_custom_data ).

lo_el_custom_data
= lo_nd_custom_data->get_element( ).


IF lo_el_custom_data IS NOT INITIAL.

  lo_nd_custom_data
->remove_element( lo_el_custom_data ).

ENDIF.

endmethod.



4 Adding the button to the standard component


  We are going to create a new button at the SC item level.

  You can also use the same custom component to show a popup in other docuents, PO, RFx, etc.

  Create a new SC and check the technical help to find the standard webdynpro component:


In this case we can see the component /SAPSRM/WDC_UI_SC_DOTC_BD.



4.1 Go to that component and create a new Enhancent point



4.2 Add as Used Component our custom component



4.3 Go to the view V_SC_DOTC_BASIC and Add the used component


4.4 Map the interface node INFO in the context



4.5 Add a new button to the table toolbar to call our component and create an action to it



4.6 Adding code to the button

we are going to show a popup with our component. To do that we are going to use the Code wizard:



We need to pass to our component some data through the interface node, like the item number or the display mode, this the entire code of the button:


ONACTIONZZCUSTOM_DATA

METHOD onactionzzcustom_data .

DATA:
lv_mode              
TYPE /sapsrm/pdo_inst_mode,
lv_message           
TYPE string,
lv_message_id        
TYPE string,

ls_info               TYPE wd_this->element_info,
ls_canc_action       
TYPE wdr_popup_button_action,
ls_item              
TYPE bbp_pds_sc_item_d,

lt_set                TYPE wdr_context_element_set,

lo_api_controller     TYPE REF TO if_wd_controller,
lo_message_manager   
TYPE REF TO if_wd_message_manager,
lo_nd_items          
TYPE REF TO if_wd_context_node,
lo_el_items          
TYPE REF TO if_wd_context_element,
lo_pdo_base           TYPE REF TO /sapsrm/cl_pdo_base,
lo_pdo               
TYPE REF TO /sapsrm/if_pdo_xo,
lo_window_manager    
TYPE REF TO if_wd_window_manager,
lo_api_component     
TYPE REF TO if_wd_component,
lo_window            
TYPE REF TO if_wd_window,
lt_buttons           
TYPE wdr_popup_button_list,
lo_nd_info           
TYPE REF TO if_wd_context_node,
lo_el_info           
TYPE REF TO if_wd_context_element.

FIELD-SYMBOLS:
<ls_set>             
TYPE REF TO if_wd_context_element.

lo_nd_items
= wd_context->path_get_node( path = `COMP_CONTEXT.ITEMS` ).

CALL METHOD lo_nd_items->get_selected_elements
  EXPORTING
   including_lead_selection
= abap_true
  RECEIVING
   set                      = lt_set.

* Only one item must be selected;
IF lt_set IS INITIAL OR lines( lt_set ) GT 1.

  lo_api_controller ?= wd_this
->wd_get_api( ).
  CALL METHOD lo_api_controller->get_message_manager
    RECEIVING
      message_manager
= lo_message_manager.

  lv_message
= 'Choose an unique item'.

CALL METHOD lo_message_manager->report_warning
  EXPORTING
   message_text 
= lv_message
   show_as_popup
= abap_true
  RECEIVING
   message_id   
= lv_message_id.

  RETURN.
ENDIF.

READ TABLE lt_set
ASSIGNING <ls_set>
INDEX 1.
IF sy-subrc NE 0.
  RETURN.
ENDIF.

CALL METHOD <ls_set>->get_static_attributes
IMPORTING
  static_attributes
= ls_item.

ls_info-number_int = ls_item-number_int.

CALL METHOD wd_comp_controller->mo_bom_sc->/sapsrm/if_cll_bo_mapper~get_header_object_id
RECEIVING
  rv_h_object_id
= ls_info-object_id.

* Obtain display mode;
TRY.
CALL METHOD wd_comp_controller->mo_bom_sc->/sapsrm/if_cll_xo_mapper~get_pdo
  RECEIVING
   ro_pdo
= lo_pdo.

lo_pdo_base ?= lo_pdo.
lv_mode
= lo_pdo_base->/sapsrm/if_pdo_base~get_mode( ).

CATCH cx_root.

ENDTRY.

IF lv_mode EQ 'DISPLAY'.


  ls_info
-read_only = abap_true.


ELSE.


*  This is necessary to force to pass for the badi BBP_DOC_SAVE in

*  case you don't modify nothing but the custom data;
  CALL METHOD <ls_set>->set_changed_by_client
   EXPORTING
   flag
= abap_true.

ENDIF.

* Pass the data to the interface node;
lo_nd_info
= wd_context->get_child_node( name = wd_this->wdctx_info ).
lo_el_info
= lo_nd_info->get_element( ).
lo_el_info
->set_static_attributes(
static_attributes
= ls_info ).

* Call our custom component as a popup;
lo_api_component          
= wd_comp_controller->wd_get_api( ).
lo_window_manager         
= lo_api_component->get_window_manager( ).
ls_canc_action
-action_name = '*'.
lt_buttons                
= lo_window_manager->get_buttons_ok(  ).

lo_window                 
= lo_window_manager->create_and_open_popup(
window_name         
= 'ZZCUSTOM_DATA'
component_usage_name
= 'ZZCUSTOM_DATA'
*    title                =
message_type        
= if_wd_window=>co_msg_type_none
message_display_mode
= if_wd_window=>co_msg_display_mode_selected
buttons             
= lt_buttons
cancel_action       
= ls_canc_action ).

ENDMETHOD.



5 Saving the data


At this point you can use the button and you can display and modify the custom data:



But we need to save this data in the database, to do that we are going to use the badi BBP_DOC_SAVE_BADI.


5.1 Create a new implementation of the badi


Set the filter value for the Shopping Cart


Call the FM we’ve created to save the data


SAVE BADI

DATA:

     lv_action           TYPE /sapsrm/pdo_action_type,

     lo_tran_context  TYPE REF TO /sapsrm/if_transaction_context,

   ls_header   TYPE bbp_pds_header.

  CALL FUNCTION 'BBP_PROCDOC_GETDETAIL'
   EXPORTING
    i_guid      
= iv_doc_guid
   IMPORTING
    e_header    
= ls_header.


    lo_tran_context = /sapsrm/cl_transaction_context=>/sapsrm/if_transaction_context~get_instance( ) .

    lv_action = lo_tran_context->get_current_action( ).


IF lv_action EQ 'ORDER' OR lv_action EQ 'SAVE'.
  CALL FUNCTION 'ZZCUSTOM_DATA_SAVE'

      EXPORTING
     iv_object_id      
= ls_header-object_id.

ENDIF.


Now you can save the data. If you save the document and enter in display mode you can see you cannot modify the data. You can only modify the data if you enter in Edit mode.

You can enhance this solution adding other custom tables or custom fields to the custom webdynpro and creating the proper FM in the FG.

You can add some validation when you are saving the document creating an implementation of the badi BBP_DOC_CHECK_BADI and using the FM to get the data we have in our FG.




Top kudoed authors