Since 2006 we use ChaRM to manage our SAP ABAP changes. But the dark side of our IT department, the infrastructure team, always successfully avoided the contact with it as well with SAP at all. Time passes and a new infrastructure boss decided that also the non-SAP fraction has to document changes by change documents.
In order to mitigate this existential shock I customized a strongly simplified business role for my colleagues (“YSOLMAN_INFS“) and set up ready to be used Request for Change (RfC) templates (YMCT) with already initialized partners and the category id the infrastructure team owns (ITFS) and a scope assignment block with a ready to be used general change (YMCG), too. To avoid chaos we have a naming convention for the description of the RfC templates, and the templates of infrastructure teams should start with “ITFS“.
The non-SAP colleagues are expected to use the “Create Request for Change from Template” functionality. Sadly this opens at first a pop-up with an empty search for RfC templates. To make things really really easy I thought to initialize this search pop-up such that the proper templates are only one click on the search button away by searching for these attributes:
- status IS “Released“
- Category ID IS “ITFS“
- Description STARTS WITH “ITFS“
First Try: Initialize Using GET_VALUE1
With F2 it is easy to find the component of the search: AIC_CMCR_S, View AIC_CMCR_S/CMSR. I enhance this view so that I can reach the attributes:
To initialize a search with default values you have to redefine the getter of two special attributes: VALUE1 for the low value and VALUE2 for the high value. For our goal the low value is enough:
The coding is very simple. If the infrastructure business role YSOLMAN_INFS is active, then catch the attributes to be initialized and do it:
METHOD get_value1. * 2018-05.16 r.escher created - prefill when infrastructure business role CALL METHOD super->get_value1 EXPORTING attribute_path = attribute_path iterator = iterator RECEIVING value = value. CHECK value IS INITIAL. " determine the business role, but only once " cv_profile_name will be empty at every new search view instance IF cv_profile_name IS INITIAL. DATA(lv_profile) = cl_crm_ui_profile=>get_instance( ). IF lv_profile IS BOUND. cv_profile_name = lv_profile->get_profile( ). ELSE. cv_profile_name = abap_true. "dummy to jump out the next time RETURN. ENDIF. ENDIF. * CASE cv_profile_name. WHEN cc_business_role_infrastructur. TRY. IF iterator IS BOUND. DATA(lr_current) = iterator->get_current( ). ELSE. lr_current = me->parameter_collection->get_current( ). ENDIF. value = COND #( LET att = lr_current->get_property_as_string( iv_attr_name = 'ATTR_NAME' ) IN WHEN att = 'USER_STATUS_KEY' THEN 'YMCT:E0006' "RfC Template Status Released WHEN att = 'DESCRIPTION_UC' THEN 'ITFS' "Naming convention WHEN att = 'CATEGORY_ID' THEN 'ITFS (INCIDENT_CATEGOR)' "1st level catetory (<Catego Schema>) ). CATCH cx_root. ENDTRY. ENDCASE. ENDMETHOD.
The operands are set in the configuration.
But then I discovered that this search is also used by the normal searches of the ChaRM workcenter:
and here the initial value of the status “Released” is disturbing, because you may want to search for templates that are not yet released but in editing mode.
So we need a switch. If in pop-up mode initialize also the status, else keep it empty. But how to check in the getter method if I am inside the search pop-up or not? I didn’t find a way. So I decided to initialize the pop-up from the calling side, where I definitely know that I am in pop-up mode.
I deleted the row with the initialization of the user status and set out on my way to find the source of the rainbow.
Second Try: Initialize Using The Inbound Plug of the Pop-Up
Where Is My PopUp Plug?
First we have to find the way in the thicket of links and relations.There is a very nice blog from Ashish Walke explaining how to create navigation links, but here we have to go the reverse way, we need to discover how an existing link works. And with the help of our CRM guru Hendrik Beck I left layer seven where I usually move and lion-heartedly plunged into the deeper layer of the Web UI.
We start with the workcenter. We call transaction CRMC_UI_NBLINKS “Define NavBar Profile” (SPRO -> CRM -> UI Framework -> Technical Role Definition -> Define Navigation Bar Profile) and open our minimized profile YSOLMANPRO_MIN to determine the workcenter id “SM-CHANGE”
With this value we determine the ChaRM “Create” group id “SM-CM-CR“:
Now we are able to find out the logical link id “SM-CRF-CR” of the Create RfC from Template functionality:
Now it gets tricky. We look up the logical link definition, which targets the id TCRFCR and open the F4 search for this target id:
Found! The component is AIC_CMCR_M (obviously naming convention experts would immediately look at _M components for _S components, but I wanted to show how the links work).
We call transaction CRMC_UI_WA_COMP_REP (SPRO -> CRM -> UI Framework -> Technical Role Definition -> Define Work Area Component Repository) and look up this component to see which inbound plug is used:
Yes! The plug name looks very promising. Let’s open it in BSP_WD_CMPWB:
METHOD ip_create_from_tmpl. * Create Request for Change from Template me->gv_ui_object_tmpl_type = if_ags_crm_const=>gc_ui_aic_ob_cmcr_templ. me->view_manager->navigate( source_rep_view = me->rep_view outbound_plug = 'FromRFCMToRFCSTemplatePopupNavbar' data_collection = iv_collection ). ENDMETHOD.
Look at the commentary: very hot! Now we open the runtime repository editor and look at the navigation link ‘FromRFCMToRFCSTemplatePopupNavbar‘ finding the inbound plug of the target:
As the target is in another component, the navigation adds a ComponentUsage to the target window.
Here is the target plug:
This seems to be enough for our enhancements. Let’s go!
Enhance Inbound Plug
We open component AIC_CMCR_S in the component workbench and enhance AIC_CMCR_S/MainWindow.
In the resulting implementation class we add the public class attribute ZCT_DEFAULT_PARAMS of type GENILT_SELECTION_PARAMETER_TAB so that we have a place where we can store the default values:
Then we redefine IP_TEMPLATE_POPUP_NAVBAR:
METHOD ip_template_popup_navbar. set_default_params( iv_collection ). "prepare the prefilled attributes CALL METHOD super->ip_template_popup_navbar EXPORTING iv_collection = iv_collection. ENDMETHOD.
The private method set_default_params is unspectacular:
METHOD set_default_params. * store default params globaly zct_default_params = VALUE #( ( attr_name = 'USER_STATUS_KEY' sign = 'I' option = 'EQ' low = 'YMCT:E0006' ) ). ENDMETHOD.
(obviously I will replace the literals with interface constants 😉 )
We don’t switch at the business role here because this use case is valid for all RfC template pop-ups: you shall always search only for released templates!
We have already enhanced the view AIC_CMCR_S/CMSR in our first try. Now we redefine DO_PREPARE_OUTPUT method to set the default attributes. First we have to navigate the parentship upwards and jump over the view set, so that we can consume the prepared zct_default_params table by adding its values to the query object:
METHOD do_prepare_output. * 2018-06-07 r.escher created DATA: lr_parent_window TYPE REF TO ycl_1s_aic_cmcr_mainwind0_impl, lr_parent_viewset TYPE REF TO cl_aic_cmcr_cmviewset_impl. TRY. DATA(lr_parent) = me->m_parent. CHECK lr_parent IS BOUND. lr_parent_window ?= lr_parent. CATCH cx_sy_move_cast_error. "not our custom class, a viewset? Exactly one seond try TRY. lr_parent_viewset ?= lr_parent. lr_parent_window ?= lr_parent_viewset->m_parent. CATCH cx_sy_move_cast_error. ENDTRY. ENDTRY. IF lr_parent_window IS BOUND AND lr_parent_window->zct_default_params IS NOT INITIAL. TRY. "eh_onclear( ). DATA(lr_qs) = me->get_current_dquery( ). lr_qs->delete_empty_selection_params( ). LOOP AT lr_parent_window->zct_default_params ASSIGNING FIELD-SYMBOL(<lf_param>). lr_qs->add_selection_param( iv_attr_name = <lf_param>-attr_name iv_sign = <lf_param>-sign iv_option = <lf_param>-option iv_low = <lf_param>-low iv_high = <lf_param>-high ). ENDLOOP. CATCH cx_root. ENDTRY. ENDIF. CALL METHOD super->do_prepare_output EXPORTING iv_first_time = abap_false. CLEAR lr_parent_window->zct_default_params. ENDMETHOD.
But big surprise! It does not work, the user status search attribute remains empty. In the debugger we see that the zct_default_params table is empty. So what?
Enhance The Second Inbound Plug
Did you really thought we were done with the first inbound plug we found? The search window we enhanced is not yet the pop-up window, it will wait invisible in the background and become visible only if you cancel the pop-up. The pop-up window is a second instance of our class, so we have to find out which additional plug we have to enhance.
Our jorney goes on. To discover the next navigation, we have to look at the coding to find our way. super->ip_template_popup_navbar (cl_aic_cmcr_mainwindow0_impl) looks like this:
METHOD ip_template_popup_navbar. CALL METHOD me->ip_template_popup EXPORTING iv_collection = iv_collection. me->view_manager->navigate( source_rep_view = me->rep_view outbound_plug = 'CreateFromTemplate' data_collection = iv_collection ). ENDMETHOD.
In me->ip_template_popup it simply flags for a pop-up window:
method IP_TEMPLATE_POPUP. set_flags( iv_popup = abap_true iv_template = abap_true ). endmethod.
And this navigation link ‘CreateFromTemplate‘ will navigate to the search result view AIC_CMCR_S/CMSRL inbound plug CREATE_FROM_TEMPLATE, which is in the same component and therefore has not need for a component usage:
This inbound plug IP_CREATE_FROM_TEMPLATE will create a new instance of the window as pop-up identified by:
- gv_interface_view_name = ‘AIC_CMCR_S/MainWindow‘
- gv_search_usage_for_template = ‘CUAICChangeMS‘
These values are intialized in the CONSTRUCTOR of the result view class CL_AIC_CMCR_CMSRL_IMPL:
method CONSTRUCTOR. CALL METHOD SUPER->CONSTRUCTOR. me->view_name = 'CMSRL.htm'. include crm_object_types_con. me->gv_otr_alias_for_template = if_ags_crm_const=>gc_otr_alias_for_cmcr. me->gv_interface_view_name = 'AIC_CMCR_S/MainWindow'. me->gv_search_usage_for_template = 'CUAICChangeMS'. endmethod.
This is the way how the inbound plug of the result view creates the new instance as a pop-up and calls another inbound plug of our window:
METHOD ip_create_from_template . * me->eh_onnew_from_template( ). * initialize it once IF gv_template_search_popup IS INITIAL. DATA: lv_title TYPE string. lv_title = cl_wd_utilities=>get_otr_text_by_alias( gv_otr_alias_for_template ). gv_template_search_popup = comp_controller->window_manager->create_popup( iv_interface_view_name = gv_interface_view_name iv_usage_name = gv_search_usage_for_template iv_title = lv_title ). ENDIF. gv_template_search_popup->set_on_close_event( iv_view = me iv_event_name = 'TEMPLATESEL_CLOSED' ). gv_template_search_popup->set_display_mode( if_bsp_wd_popup=>c_display_mode_surrounded ). gv_template_search_popup->open( 'TEMPLATE_POPUP' ). ENDMETHOD.
If we look at the first import parameter of the open method we see it is named iv_inbound_plug. And effectively we find in AIC_CMCR_S/MainWindow this window inbound plug, which we redefine:
Same coding as the first inbound plug we did redefine:
METHOD ip_template_popup. * 2018-06-07 r.escher created set_default_params( iv_collection ). "prepare the prefilled attributes CALL METHOD super->ip_template_popup EXPORTING iv_collection = iv_collection. ENDMETHOD.
We are done! This time our do_prepare_output routine finds the default values and can initialize the search attribute for user status! And this happens only in pop-up mode.
A happy user again: