Mark Peeters
Read all my blogsThe CRM WEB UI is a flexible user interface that lets you alter based on a user’s Business role. This leads to many user requirement during our CRM implementations. However if we look at all the views that we alter we see that there is a big overlap in views. In other words, for several business roles the same custom view will be required while for other views the same business roles require a different view. Until now this meant creating a new role configuration key and copying all custom views. Since this can be a time consuming activity and it leads to high maintenance cost in case of changes, this blog will show an easy way to dynamically find the correct view using an access sequence.
Business case with standard customizing
Changing views in the CRM Web UI is done using a Role Configuration Key. The key is linked to one or more business roles that use the same views. So if you have the requirement for 4 custom views for the business role Sales and the same 4 views for the role Sales Executive, only for this role one of the 4 views needs a change. (note that in most implementations the number of custom views per business role is much higher). You will then create two business roles and two role configuration keys. Now you can create the custom views, 8 in total.
It is clear that in case of more business roles and more customized views per business role, the number of copied views with the same configuration can grow rapidly.
Let’s see how to prevent this.
Business case with enhencement
We will still need two business roles each with its own role configuration key. For the Sales role we customize all 4 view lay-outs. For the Sales Exec. role we only customize the deviating 4th view.
Now we need to create an enhencement that will refer to teh Sales role if there is no custom view found for the Sales Exec role. This will be done by using a custom access sequence tabel and a BAdI implementation.
Creating the Access Sequence Tables
In order to register the access sequence we use a custom table. We register the main role configuration key the access number and the related role configuration key. By doing this, we can create a standard role for the entire business, one for sales and one for service. As shown in the picture.
BAdI Implementation
SAP CRM provides a BAdI to influence the determined view, this is the enhancement spot BSP_DLC_ACCESS_ENHENCEMENT. In our implementation we would like to call SAP standard determination for each role configuration key. Unfortunately the SAP standard code is in a private class, class CL_BSP_DLC_XML_STORAGE2. In order to still use this functionality we copy two methods of this class to the class that is created with our BAdI implementation. Below we will post the entire code.
- BUILD_ACCESS_SEQUENCE – we optimized the SAP code, result is the same
- CHOOSE_CONFIG_BY_AC_SEQ – we changed some data types to match the data types of the BAdI.
In the BAdI we first check if there are any custom views for this component and if the role access key of the business role is in the customizing table. If one of these checks fails we return to the SAP standard code.
Next step is to check if our copy of the SAP Standard codes returns a Custom view configuration for the provided role config key. If this is the case we select this view.
If no Custom config was found we start a loop over the access defined in the access sequence table.
We each step we check if a custom view configuration is found and if so use that and stop the loop, otherwise continue to the next step.
In case we do not find any custom configuration, SAP standard takes over and will determine the standard view.
We do recommend not to build too large access sequences due to possible performance issues but 3 or 4 steps should not be a problem.
The Code
CLASS zaccess_seqeunce DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
*”* public components of class ZACCESS_SEQEUNCE
*”* do not include other source files here!!!
INTERFACES if_badi_interface .
INTERFACES if_dlc_config_access .
METHODS choose_config_by_ac_seq
IMPORTING
!is_search_key TYPE bsp_dlcs_conf_sem_key_var_part
!it_config_cus TYPE bsp_dlct_context_semantic_key
!it_config_sap TYPE bsp_dlct_context_semantic_key
EXPORTING
!es_config_data TYPE bsp_dlcs_context_semantic_key
!es_successfull_key TYPE bsp_dlcs_conf_sem_key_var_part
!ev_config_data_origin TYPE bsp_dlc_config_data_origin .
METHODS build_access_sequence
RETURNING
value(result) TYPE bsp_dlct_conf_access_sequence .
PROTECTED SECTION.
*”* protected components of class ZACCESS_SEQEUNCE
*”* do not include other source files here!!!
PRIVATE SECTION.
*”* private components of class ZACCESS_SEQEUNCE
*”* do not include other source files here!!!
CLASS-DATA gt_access_sequence TYPE bsp_dlct_conf_access_sequence .
ENDCLASS.
CLASS ZACCESS_SEQEUNCE IMPLEMENTATION.
* <SIGNATURE>——————————————————————————–+
* | Instance Public Method ZACCESS_SEQEUNCE->BUILD_ACCESS_SEQUENCE
* +——————————————————————————————+
* | [<-()] RESULT TYPE BSP_DLCT_CONF_ACCESS_SEQUENCE
* +————————————————————————————–</SIGNATURE>
METHOD build_access_sequence.
TYPES: BEGIN OF ty_seq_options,
options TYPE char4,
END OF ty_seq_options.
DATA: lt_seq_options TYPE STANDARD TABLE OF ty_seq_options,
ls_access_sequence TYPE bsp_dlcs_conf_access_keys.
FIELD-SYMBOLS <sequence_options> TYPE ty_sequence_options.
“Sequence options role_key/component_usage/object_type/object_sub_type
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘XXXX’.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘XXX ‘.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘X XX’.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘ XXX’.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘ XX’.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘X X ‘.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘ X ‘.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘XX ‘.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘ X ‘.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘X ‘.
APPEND INITIAL LINE TO lt_seq_options ASSIGNING <sequence_options>. <sequence_options> = ‘ ‘.
LOOP AT lt_seq_options ASSIGNING <sequence_options>.
ls_access_sequence–role_key = <sequence_options>(1).
ls_access_sequence–component_usage = <sequence_options>+1(1).
ls_access_sequence–object_type = <sequence_options>+2(1).
ls_access_sequence–object_sub_type = <sequence_options>+3(1).
APPEND ls_access_sequence TO result.
ENDLOOP.
ENDMETHOD. “build_access_sequence
* <SIGNATURE>——————————————————————————–+
* | Instance Public Method ZACCESS_SEQEUNCE->CHOOSE_CONFIG_BY_AC_SEQ
* +——————————————————————————————+
* | [—>] IS_SEARCH_KEY TYPE BSP_DLCS_CONF_SEM_KEY_VAR_PART
* | [—>] IT_CONFIG_CUS TYPE BSP_DLCT_CONTEXT_SEMANTIC_KEY
* | [—>] IT_CONFIG_SAP TYPE BSP_DLCT_CONTEXT_SEMANTIC_KEY
* | [<—] ES_CONFIG_DATA TYPE BSP_DLCS_CONTEXT_SEMANTIC_KEY
* | [<—] ES_SUCCESSFULL_KEY TYPE BSP_DLCS_CONF_SEM_KEY_VAR_PART
* | [<—] EV_CONFIG_DATA_ORIGIN TYPE BSP_DLC_CONFIG_DATA_ORIGIN
* +————————————————————————————–</SIGNATURE>
METHOD choose_config_by_ac_seq.
DATA: lv_found TYPE boole_d,
ls_key TYPE bsp_dlcs_conf_sem_key_var_part,
ls_access_key TYPE bsp_dlcs_conf_access_keys,
lv_to_read TYPE string,
lv_i TYPE int4.
CLEAR: es_config_data, es_successfull_key, ev_config_data_origin.
IF cl_bsp_dlc_config_util=>is_sap_system( ) EQ abap_true.
lv_to_read = ‘SAP’.
lv_i = 1.
ELSE.
lv_to_read = ‘CUS’.
lv_i = 2.
ENDIF.
gt_access_sequence = build_access_sequence( ).DO lv_i TIMES.
LOOP AT gt_access_sequence INTO ls_access_key.
ls_key = is_search_key.
IF ls_access_key–role_key IS INITIAL.
ls_key–role_key = ‘<DEFAULT>’.
ENDIF.
IF ls_access_key–component_usage IS INITIAL.
ls_key–component_usage = ‘<DEFAULT>’.
ENDIF.
IF ls_access_key–object_type IS INITIAL.
ls_key–object_type = ‘<DEFAULT>’.
ENDIF.
IF ls_access_key–object_sub_type IS INITIAL.
ls_key–object_sub_type = ‘<DEFAULT>’.
ENDIF.
IF lv_to_read = ‘CUS’.
READ TABLE it_config_cus WITH KEY role_key = ls_key–role_key
component_usage = ls_key–component_usage
object_type = ls_key–object_type
object_sub_type = ls_key–object_sub_type
INTO es_config_data.
IF sy–subrc = 0.
lv_found = abap_true.
* select config data / customer
es_successfull_key = ls_key.
ev_config_data_origin = ‘CUS’.
EXIT.
ENDIF.
ELSE.
READ TABLE it_config_sap WITH KEY role_key = ls_key–role_key
component_usage = ls_key–component_usage
object_type = ls_key–object_type
object_sub_type = ls_key–object_sub_type
INTO es_config_data.
* select config data / SAP
IF sy–subrc EQ 0.
lv_found = abap_true.
es_successfull_key = ls_key.
ev_config_data_origin = ‘SAP’.
EXIT.
ENDIF.
ENDIF.
ENDLOOP.
IF lv_found EQ abap_true.
EXIT.
ENDIF.
lv_to_read = ‘SAP’.
ENDDO.
ENDMETHOD. “choose_config_by_ac_seq
* <SIGNATURE>——————————————————————————–+
* | Instance Public Method ZACCESS_SEQEUNCE->IF_DLC_CONFIG_ACCESS~CHOOSE_CONFIG
* +——————————————————————————————+
* | [—>] IV_COMPONENT TYPE BSP_WD_COMPONENT_NAME
* | [—>] IV_VIEWNAME TYPE O2PAGEEXT
* | [—>] IS_CONFIG_SEARCH_KEY TYPE BSP_DLCS_CONF_SEM_KEY_VAR_PART
* | [—>] IT_CONFIG_SAP TYPE BSP_DLCT_CONTEXT_SEMANTIC_KEY
* | [—>] IT_CONFIG_CUS TYPE BSP_DLCT_CONTEXT_SEMANTIC_KEY
* | [<—] ES_CONFIG_CHOOSEN TYPE BSP_DLCS_CONTEXT_SEMANTIC_KEY
* | [<—] EV_CONFIG_CHOOSEN_ORIGIN TYPE BSP_DLC_CONFIG_DATA_ORIGIN
* +————————————————————————————–</SIGNATURE>
METHOD if_dlc_config_access~choose_config.
DATA: lt_access_config TYPE STANDARD TABLE OF zaccess_config,
ls_config_search_key TYPE bsp_dlcs_conf_sem_key_var_part,
ls_access_config TYPE zaccess_config,
ls_successfull_key TYPE bsp_dlcs_conf_sem_key_var_part,
ls_config_data_origin TYPE bsp_dlc_config_data_origin.
CLEAR: es_config_choosen, ev_config_choosen_origin.
“If no Customer configuration exist end search.
IF it_config_cus IS INITIAL. RETURN. ENDIF.
“Read values from customizing table.
“If Role Config Key even exists in customizing table.
SELECT * FROM zaccess_config INTO TABLE lt_access_config
WHERE config_key = is_config_search_key–role_key.
IF sy–subrc NE 0. RETURN. ENDIF.
“Check Config Key of user
choose_config_by_ac_seq( EXPORTING is_search_key = is_config_search_key
it_config_cus = it_config_cus
it_config_sap = it_config_sap
IMPORTING es_successfull_key = ls_successfull_key
ev_config_data_origin = ls_config_data_origin ).“Customer configuration found for role_key
IF ls_config_data_origin = ‘C’.
READ TABLE it_config_cus INTO es_config_choosen
WITH KEY role_key = ls_successfull_key–role_key
component_usage = ls_successfull_key–component_usage
object_type = ls_successfull_key–object_type
object_sub_type = ls_successfull_key–object_sub_type.
ev_config_choosen_origin = ‘CUS’.
RETURN.
ENDIF.
ls_config_search_key = is_config_search_key.
“Now try and find customer configuration with lower level config_key.
LOOP AT lt_access_config INTO ls_access_config.
ls_config_search_key–role_key = ls_access_config–step_config.
choose_config_by_ac_seq( EXPORTING is_search_key = ls_config_search_key
it_config_cus = it_config_cus
it_config_sap = it_config_sap
IMPORTING es_successfull_key = ls_successfull_key
ev_config_data_origin = ls_config_data_origin ).“Customer configuration found for role_key
IF ls_config_data_origin = ‘C’.
READ TABLE it_config_cus INTO es_config_choosen
WITH KEY role_key = ls_successfull_key–role_key
component_usage = ls_successfull_key–component_usage
object_type = ls_successfull_key–object_type
object_sub_type = ls_successfull_key–object_sub_type.
ev_config_choosen_origin = ‘CUS’.
RETURN.
ENDIF.
ENDLOOP.
ENDMETHOD. “if_dlc_config_access~choose_config
ENDCLASS.