1. Introduction
While creating sales orders from SAP or in WUI it is often possible that similar sales orders are placed more than once. In that case it would be nice to have a preventive measure by checking in the system if there are any existing sales orders that are similar to the one that is being created. Finding one probable duplicate order and being warned about the same will ensure that before we save a new order.
1. MAJOR FEATURES
When a sales order is entered in CRM, before saving it, it may be necessary to perform a duplicate order check in BADI ORDER_SAVE, depending on the conditions described below.
The duplicate order check should be activated for the combination of transaction type, sales area and media type. (For example: for standard orders within France the check is activated, for standard orders within the UK the check is deactivated).
Sales Area – The sales area provides a mechanism to turn the functionality on or off per country.
Transaction Type – Should be checked to make the difference between sales orders. (For example: excluding debit or credit memo’s.
Media Type – E.g. Fax, E-mail, telephone. (For example: web orders and EDI will be excluded
Contact Person – The check on contact person should be turned on or off per activation area above. Example: When the check on contact person is switched ON the check will only consider orders created for the same ship-to and contact person. When the check is turned OFF there is a check between all orders for a certain ship-to.
Number of days – A number of days should be included to know how many days back in time should be checked. This can differ per activation area. EG for France 8 days, for UK 4 days.
Number of items – In the as-is situation 4 items are checked. If 4 items are identical in 2 orders, the duplicate check sees the order as a possible duplicate order. In the to-be situation it must be possible that the number of items is variable per activation area above.
1. TECHNICAL DETAILS
For the purpose of the duplicate order check, key figures like sales organization, distribution channel, division, transaction type and media type is looked into for the order that is to be saved.
This is done using a table where certain flags and ranges are stored. For example, if the flag duplicate_order_check is enabled this means duplicacy needs to be checked. Similarly, if contact_per_chk is enabled then the contact person has to be compared as well. The days_chk determines maximum how many days back we should check for duplicates and for items_chk determines how many items need to be similar in order to qualify as a duplicate.
Once a duplicate order is found, the customer would like to be warned that what they are trying to create might already exist in the system. This can be done simply by using a popup message box. This part we can come it later but the main requirement is to find the details of the current sales order that is about to be saved.
CRM Web UI
In case of CRM Web UI, this BADI will not work properly. Our goal is to create a popup message box on the UI which is not possible from this BADI. To do so, we have to make a component enhancement for BT115H_SLSO in the component workbench and redefine the method EH_ONSAVE in the view (SOHOverView) which is the event handler for the save button.
In this case we do not work with GUID, Instead, here we need to fetch the data from the context of the current view, or sometimes the context of related views. Capturing this data can be done by browsing the context and fetching the data. Here is how the required data was fetched in this case.
*----------------------------------------------------------------------
* Constants
*----------------------------------------------------------------------
CONSTANTS:
lco_e TYPE c VALUE 'E',
lco_applid_10 TYPE yesfapplicationid VALUE '0000000010',
lco_exitid_23 TYPE yesfexitid VALUE '0000000023',
lco_btorderheader TYPE crmt_relation_name VALUE 'BTOrderHeader',
lco_btheaderitext TYPE crmt_relation_name VALUE 'BTHeaderItemsExt',
lco_btheaderorgset TYPE crmt_relation_name VALUE 'BTHeaderOrgmanSet',
lco_btheaderpartset TYPE crmt_relation_name VALUE 'BTHeaderPartnerSet',
lco_btpart_1 TYPE crmt_relation_name VALUE 'BTPartner_PFT_0001_MAIN',
lco_btpart_7 TYPE crmt_relation_name VALUE 'BTPartner_PFT_0007_MAIN',
lco_btorderitemsall TYPE crmt_relation_name VALUE 'BTOrderItemAll',
lco_btitemprodext TYPE crmt_relation_name VALUE 'BTItemProductExt',
lco_detailsvs TYPE string VALUE 'DetailsVS.do',
lco_admin_cat TYPE string VALUE 'btadminh_category'.
REFRESH lt_items.
IF gv_save_after_chk EQ abap_false.
* Get admin entity
lrf_btadminh_entity ?= me->typed_context->btadminh->collection_wrapper->get_current( ).
* Check if order is new or existing
lrf_btadminh_entity->get_properties( IMPORTING es_attributes = lst_header ).
IF lst_header-object_id IS NOT INITIAL.
* the material is old and so dont check for duplicates
CALL METHOD super->eh_onsave
EXPORTING
htmlb_event = htmlb_event
htmlb_event_ex = htmlb_event_ex.
RETURN.
ENDIF.
* Get order details
lrf_btorder_entity ?= me->typed_context->btorder->collection_wrapper->get_current( ).
IF lrf_btorder_entity IS BOUND.
lrf_btorder_header ?= lrf_btorder_entity->get_related_entities( iv_relation_name = lco_btorderheader ).
lrf_header_entity ?= lrf_btorder_header->if_bol_bo_col~get_current( ).
IF lrf_header_entity IS BOUND.
lrf_itemsext ?= lrf_header_entity->get_related_entities( iv_relation_name = lco_btheaderitext ).
lrf_orgman ?= lrf_header_entity->get_related_entities( iv_relation_name = lco_btheaderorgset ).
IF lrf_orgman IS BOUND.
lrf_orgman_entity ?= lrf_orgman->if_bol_bo_col~get_current( ).
IF lrf_orgman_entity IS BOUND.
lrf_orgman_entity->get_properties( IMPORTING es_attributes = lst_orgman ).
ENDIF.
ENDIF.
* Get partner related information to get sold to party and contact person
lrf_partnerset ?= lrf_header_entity->get_related_entities( iv_relation_name = lco_btheaderpartset ).
IF lrf_partnerset IS BOUND.
lrf_partner_entity ?= lrf_partnerset->if_bol_bo_col~get_current( ).
IF lrf_partner_entity IS BOUND.
lrf_partnerstp ?= lrf_partner_entity->get_related_entities( iv_relation_name = lco_btpart_1 ).
IF lrf_partnerstp IS BOUND.
lrf_partnerstp_entity ?= lrf_partnerstp->if_bol_bo_col~get_first( ).
IF lrf_partnerstp_entity IS BOUND.
lrf_partnerstp_entity->get_properties( IMPORTING es_attributes = lst_partnerstp ).
ENDIF.
ENDIF.
lrf_partnercont ?= lrf_partner_entity->get_related_entities( iv_relation_name = lco_btpart_7 ).
IF lrf_partnercont IS BOUND.
lrf_partnercont_entity ?= lrf_partnercont->if_bol_bo_col~get_first( ).
IF lrf_partnercont_entity IS BOUND.
lrf_partnercont_entity->get_properties( IMPORTING es_attributes = lst_partnercont ).
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
LOOP AT m_subcontrollers INTO lst_controller.
lrf_control ?= lst_controller-instance.
IF lrf_control IS BOUND.
IF lrf_control->controller_name CS lco_detailsvs.
LOOP AT lst_controller-fields INTO lst_field.
IF lst_field-name CS lco_admin_cat.
lv_category_str = lst_field-value.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.
ENDLOOP.
* get organization data
lv_sales_org_c = lst_orgman-sales_org_short.
lv_dis_channel_c = lst_orgman-dis_channel.
lv_division_c = lst_orgman-division.
* get partner data
* For partner data created through WEB UI
* Sold-to-party is same as ship-to-party same as bill-to-party
* get sold to party structure in lst_partner_stp
lv_ship_to_c = lst_partnerstp-partner_no.
* get contact details in lst_partnercont
lv_contact_c = lst_partnercont-partner_no.
lv_transaction_type_c = lst_header-process_type.
lv_posting_date = lst_header-posting_date.
lv_media_type_c = lv_category_str.
* get item data in loop
lrf_itemsext_entity ?= lrf_itemsext->if_bol_bo_col~get_current( ).
IF lrf_itemsext_entity IS BOUND.
lrf_itemsall ?= lrf_itemsext_entity->get_related_entities( iv_relation_name = lco_btorderitemsall ).
ENDIF.
IF lrf_itemsall IS BOUND.
lrf_item_entity ?= lrf_itemsall->if_bol_bo_col~get_first( ).
ENDIF.
WHILE lrf_item_entity IS BOUND.
CLEAR lwa_items.
* Get material number in lst_itemdetail
lrf_item_entity->get_properties( IMPORTING es_attributes = lst_itemdetail ).
* Get material quantity in lst_itemquant
lrf_itemprod ?= lrf_item_entity->get_related_entities( iv_relation_name = lco_btitemprodext ).
lrf_itemquant ?= lrf_itemprod->if_bol_bo_col~get_first( ).
lrf_itemquant->get_properties( IMPORTING es_attributes = lst_itemquant ).
* append the data in item table
lwa_items-ordered_prod = lst_itemdetail-ordered_prod.
lwa_items-net_weight = lst_itemquant-net_weight.
APPEND lwa_items TO lt_items.
* Go to next record
lrf_item_entity ?= lrf_itemsall->if_bol_bo_col~get_next( ).
ENDWHILE.
Once we have this data we can perform the duplicate checking with the existing data using the same logic as before. (Checking for Duplicate Sales Orders in SAP CRM )
Now coming to the popup to confirm in web ui I had to create two more methods for this. One method is for displaying the popup and the other method is for capturing the result of the popup messagebox.
When we find a duplicate order, we pass the duplicate order name to the method eh_onconfirm_popup. where lv_order is the duplicate order number.
CALL METHOD me->eh_onconfirm_popup( iv_order = lv_order ).
This method contains the following logic to show the popup message. We use the standard create_popup_2_confirm for this.
*----------------------------------------------------------------------
* Constants
*----------------------------------------------------------------------
CONSTANTS:
lco_conf_close TYPE string VALUE 'CONFIRM_POPUP_CLOSED'.
*----------------------------------------------------------------------
* Processing Logic
*----------------------------------------------------------------------
CLEAR: lv_save, lv_text.
IF grf_confirm_popup IS NOT BOUND.
lv_save = text-t02.
lv_text = text-t01.
REPLACE ALL OCCURRENCES OF '&' IN lv_text WITH iv_order.
CALL METHOD comp_controller->window_manager->create_popup_2_confirm
EXPORTING
iv_title = lv_save
iv_text = lv_text
iv_btncombination = if_bsp_wd_window_manager=>co_btncomb_yesno
RECEIVING
rv_result = grf_confirm_popup.
CALL METHOD grf_confirm_popup->set_on_close_event
EXPORTING
iv_view = me
iv_event_name = lco_conf_close.
ENDIF.
*Open the popup message
grf_confirm_popup->open( ).
Now once the popup message is answered, the result should be available in the event handler method EH_ONCONFIRM_POPUP_CLOSED
Here we capture the result of the popup and act accordingly.
*----------------------------------------------------------------------
* Constants
*----------------------------------------------------------------------
CONSTANTS:
lco_yes TYPE string VALUE 'YES'.
*----------------------------------------------------------------------
* Variables
*----------------------------------------------------------------------
DATA:
lv_answer TYPE string.
*----------------------------------------------------------------------
* Processing Logic
*----------------------------------------------------------------------
* Retrieve the answer
* this is where you can tell which button has been hold
CLEAR lv_answer.
lv_answer = grf_confirm_popup->get_fired_outbound_plug( ).
IF lv_answer EQ lco_yes.
* Duplicate check is done and now just save
gv_save_after_chk = abap_true.
CALL METHOD me->eh_onsave
EXPORTING
htmlb_event = htmlb_event
htmlb_event_ex = htmlb_event_ex.
ELSE.
CLEAR gv_save_after_chk.
ENDIF.
Here I have called the method eh_onsave (the one i redefined) after setting gv_save_after_check. In case this variable is set, the super method is called which performs the standard process of saving the order.
Here is the result of the enhancement on Web UI.
This topic is in continuation of the existing post for checking of Duplicate Sales Orders in CRM ABAP (link below)