Wednesday, September 29, 2010

Intent-Driven Interactions

In complex multi-tasking call centers, it can be helpful if users are guided through the system using context based rules. In other words, it can be helpful to implement rules based on which the user is notified or navigated through the system.

You can use Intent driven interaction, also known as IDI to support users in the interaction center.

Intent driven interaction is part of the rule modeler. The Rule Modeler is a collection of rule based functionalities supporting ERMS (Email Response Management System), IDI, Case Routing, Lead & Opportunity distribution and Order Routing.

In Intent Driven Interaction, when configured correctly, the IC Manager is able to activate the following functionalities:
  • Navigate to a screen automatically
  • Trigger Alerts
  • Delete Alerts
  • Add objects to the wrap-up list
  • Confirm an account
  • Launch an interactive script
  • Raise an IC event
All of these actions are context sensitive, meaning that they can be triggered based on conditions.

For each action, you can determine which event it listens to, for which business roles it applies and what the conditions are that the situation must comply to.
So for instance, you can implement the event 'navigate' to the customer overview, when the event 'BPCONFIRMED' is raised. This directs the agent to the account overview screen when the customer is confirmed. You can also call a script if this is needed.

Other useful events are:
  • ALERTCLICKED (when the user clicks one of the alerts in the top of the screen)
  • AUI_Interact
  • BDCCurrentCustomerChanged
  • BeginInteraction
  • CategoryChanged
  • Dial
  • EndContact
  • EndInteraction
  • InteractionEnded
  • OrderSaved
  • SERVICETICKETESCALATED

Some examples of Intent Driven Interactions are the following:
  • Navigating to a screen when a businesspartner is confirmed
  • Navigating to the Agent Inbox when the interaction is ended and the last opened contact is older then one day. (this enables the one by one processing of items in the agent inbox.
  • Triggering an alert with customer details (name, phone, email) when the businesspartner is confirmed.
  • Triggering an alert stating how many times the customer has called in the last month when the businesspartner is confirmed.
You can also implement your own events using the following code:
DATA: lr_event TYPE REF TO cl_crm_ic_event.
DATA: lr_event_srv TYPE REF TO if_crm_ic_event_srv.

CREATE OBJECT lr_event.
lr_event->set_name( value = '[your event name' ).
lr_event->add_param( name = 'source' value = 'B' ).
lr_event_srv = cl_crm_ic_services=>get_event_srv_instance( ).
lr_event_srv->raise( event = lr_event ).

Before you can subscribe to this event, you also need to add it to the Repository in the IDI customizing in the IMG under:
IMG --> Customer Relationship Management --> Interaction Center WebClient --> Additional Functions --> Intent-Driven Interactions --> Define Events in Repository

Wednesday, September 22, 2010

Adding default values to a search screen

Often, users have to search the system for objects such as customers, activities, opportunities, orders, products etc. 

In many cases, users would like to have some values defaulted when the screen opens. This article describes how you can add default values per user group.

In this example, the assignment of the values to the fields is hardcoded. You can of course make the logic more flexible by selecting information from a customizing table, and using this to add the values to the fields.

1. Determine the component and view to be changed.
In the CRM webclient navigate to the search screen to be adjusted, click in a field and press <F2>. Note the component and the viewname.
 
2. Enhance the component and view
First enhance the component and enhance the view. How to enhance a component and a view can be found here.
 
3. Redefine the method DO_INIT_CONTEXT.
Redefine the DO_INIT_CONTEXT Remember to always call the super class. How to redefine a method can be found here.
 
4. Do the coding
First we will be reading the name of the current profile. This enables us to have different default values per profile. After that, in the case statement, we assign default values to one or more profiles.

The trick is to first remove the fields from the screen, and after that insert them with the new values. This way, we will also add the fields if they are not customized, or if the user has deleted them from the screen himself. This also enables us to add a field more then once, resulting in OR statements in the query.
 
Explanation of the coding:
 
* First we do the data declaration

DATA: lv_profile TYPE REF TO IF_CRM_UI_PROFILE,
          lv_profile_name TYPE BSP_DLC_OBJECT_TYPE,
          lv_selection_parameter type ref to if_bol_bo_property_access,
          lr_qs TYPE REF TO cl_crm_bol_dquery_service,
          lv_iterator type ref to if_bol_bo_col_iterator,
          lr_col_params TYPE REF TO if_bol_bo_col,
          lv_value type string,
          lv_cnode type ref to cl_bsp_wd_context_node_asp.
 
* Then we read the current user profile
 
LV_PROFILE = CL_CRM_UI_PROFILE=>GET_INSTANCE( ).
LV_PROFILE_NAME = LV_PROFILE->GET_PROFILE( ).
 
* Then, using the case statement we add different values 
* for different profiles. In case you would like to do 
* the more flexible solution, this is where you 
* would enter the select statement to select 
* the fields and values based on the component, 
* view and profile values.
* We do not want to do this for all profiles.
 
me->set_init_qs( abap_true ). "Initialise the query

CASE LV_PROFILE_NAME.
     when '[PROFILE_NAME]', '[PROFILE_NAME2]', '[...ETC]'.
 

* We read the current information from the 
* screen as well as the query information

* check if QS already exist
lv_cnode = get_dquery_cnode( ).
lr_qs ?= lv_cnode->collection_wrapper->get_first( ).
if lr_qs is not bound.

* add empty QS in order to allow application to prefill values
    assign lv_cnode->('BASE_ENTITY_NAME') to <gs_name>.
    lr_qs = cl_crm_bol_dquery_service=>get_instance(
<gs_name> ).
    lv_cnode->collection_wrapper->add( lr_qs ).
endif.

lr_col_params = lr_qs->get_selection_params( ).

lv_selection_parameter = LR_COL_PARAMS->get_first( ).
IF lv_selection_parameter is bound.
    EH_ONCLEAR( ). "CLEAR the screen.
 

* Delete the fields that we will be adding from the screen to avoid doubles. 
 WHILE lv_selection_parameter is not initial.
  DATA: lv_low type ref to string,
         lv_name type ref to name_komp,
         lv_operator type ref to bapioption.
 

     lv_name ?= lv_selection_parameter->get_property( 'ATTR_NAME' ).
     IF lv_name->* = '[FIELDNAME1]'
     OR lv_name->* = '[FIELDNAME2]'
     OR lv_name->* = '[... etc]'.
         lr_col_params->remove( lv_selection_parameter ).
         lv_selection_parameter = LR_COL_PARAMS->get_current( ).
     ELSE.
         lv_selection_parameter = LR_COL_PARAMS->get_next( ).
     ENDIF.

 ENDWHILE.
ENDIF.
DATA: new_selection_parameter type ref to if_bol_selection_param,
         lv_index type sytabix value 1.
 

* Now we add fields per profile (or several profiles)

CASE LV_PROFILE_NAME.
WHEN [PROFILE_NAME].
    new_selection_parameter =
    lr_qs->insert_selection_param( iv_attr_name = '[FIELDNAME1]'
    iv_sign = 'I'
    iv_option = '[EQ|NE|BT|NB|CP|NP|LT|LE|GT|GE]'
    iv_low = '[VALUE]'
    iv_index = lv_index ). add 1 to lv_index.
    new_selection_parameter =
    lr_qs->insert_selection_param( iv_attr_name = '[FIELDNAME2]'
    iv_sign = 'I'
    iv_option = '[EQ|NE|BT|NB|CP|NP|LT|LE|GT|GE]'
    iv_low = '[VALUE]'
    iv_index = lv_index ). add 1 to lv_index.

WHEN [PROFILE_NAME2], [...ETC].
   new_selection_parameter =
    ...
WHEN OTHERS.
    ...
ENDCASE.

* We call the super class.
call method super->DO_INIT_CONTEXT.
 

ENDMETHOD.

Additional comments
Of course, any variants on this can be implemented. For instance you can calculate a date (for instance one year back) in a search screen for activities. This way you can create a default search screen for activities created in the last year.
You can also read from the BDC. By doing so, you can add for instance the customer number of the last viewed customer into a search screen.
You can also read the username of the current user. This could be used for instance in the search employee view if users would particularly searching for their own user. This could particularly be the case for account management, where they can view their customer portfolio on their own employee screen.
If you wish to have a customizing table where you define the default values, I would recommend the following table layout:
• Component
• View
• Profile
• Fieldname
• Fieldsign
• Fieldoption
• Fieldvalue_low
• Fieldvalue_high

All fields should be part of the key of the table, as you might want to add a field more than once to a certain screen in order to get the 'OR' statement in the searchquery.
You would now be able to implement standard coding that reads the table, and then loops over the found items. First per field, you should delete them from the searchquery, then per line, add the fields including the default values.

Wednesday, September 15, 2010

How to add buttons to the IC Webclient Toolbar

In the Interaction Center, the agent usually has several buttons in the top of the screen to handle the communication channels like phone and email.

This toolbar consists of several standard deliverd buttons.

This article explains how you can add buttons which launch your own ABAP code (like navigating to a screen, calling ERP transactions, changing the status of the interaction record...)

The implementation of a new button consists of 5 steps:

 1. Creating a new button
 2. Adding the button to the profile
 3. Adding logic in the javascript that handles the buttons
 4. Subscribing to the event
 5. Implementing logic to be called when the event is raised.


1. Creating a new button
The IMG enables you to define your own toolbar buttons.

- SAP Customizing Implementation Guide
  - Customer Relationship Management
    - Interaction Center WebClient
      - Customer-Specific System Modifications
        - Define Toolbar Buttons

2. Adding the button to the profile
In the IMG you have the ability to activate or deactivate buttons in the toolbar profile.

- SAP Customizing Implementation Guide
  - Customer Relationship Management
    - Interaction Center WebClient
      - Basic Functions
        - Communication Channels
          - Define Toolbar Profiles

These new buttons can now be assigned to the toolbar profile, and thus will show up on the screen.

The idea of new buttons is to add DTMF-Tone buttons. A specified DTMF-tone would then be sent when the agent clicks on the button. We will not be discussing this now.

Let's for the example create a button called 'ZGG' and assign it to the profile.
The new button now shows up as expected.

3. Adding logic in the javascript that handles the buttons
In the BSP Workbench (BSP_WD_CMPWB), you should enhance component CRMCMP_IC_FRAME.
This component contains pages with flow logic, one of which we will be changing. This is an enhancement, so let's keep it as general as possible.

Open page header_jscripts.js, and change the logic.
A change needs to be made in function wsb_handler. This function handles all the buttons. If you take a look at it, you will notice that the buttons are handled here one by one.

After the following code:
------------------------------------------
else if( value == "Logon" )




{


mcmPublish( cAppEvtLogon, value, null, null ,null , null );


return;


}
---------------------------------------------------
We will insert our own code. As this is JavaScript, bear in mind that statements are different from ABAP. For instance // is comment.
--------------------------------------------
//INS If the value is Z*, raise our event Z_TOOLBAR_EVENT.

else if(value.charAt(0)=="Z"){ // lets just forward all z-buttons as events :-)
                forwardCall( cAppEventCode, "Z_TOOLBAR_EVENT", value, "", "", "", "", "", "", "", "")
                                       }

//ENDINS
--------------------------------------------
 
So now, if a button is clicked where the technical name starts with a Z, the function forwardcall is raised with parameters Z_TOOLBAR_EVENT and the technical name of the button (in our case ZGG).
 
The forwardcall function will raise the event Z_TOOLBAR_EVENT, and will supply the event with the Parameter1, which is occupied with the name of the button (ZGG).
 
4. Subscribing to the event
Now the event is raised, still nothing really happens, because there are no subscribers yet for the event. Our new event Z_TOOLBAR_EVENT is now raised like other events like 'END_CONTACT'  or 'INTERACTION_ENDED'.
We will be subscribing to the event in component CRMCMP_IC_FRAME (we were already enhancing it).
 
In order to implement the subscription, we should enhance CRMCMP_IC_FRAME/HiddenView.

If the system tells you an enhancement is not possible because class CL_CRM_IC_CONTEXTAREAVIEW_CTXT is marked final, implement SAP 
Note 1142110

 
Now in our newly created subclass of CL_CRMCMP_I_HIDDENVIEW_IMPL, we will redefine method DO_INIT, and add the following code:

CALL METHOD SUPER->DO_INIT.

DATA: esrv TYPE REF TO if_crm_ic_event_srv.
* Subscribe to the z_toolbar_event.
esrv = cl_crm_ic_services=>get_event_srv_instance( ).
esrv->subscribe( event_name = 'Z_TOOLBAR_EVENT'
                        listener        = me ).

We have now subscribed to the event.

5. Implementing logic to be called when the event is raised.
The logic to be called when the event is raised, will be implemented in the redefinition of the IF_CRM_IC_EVENT_LISTENER~HANDLE_EVENT of our created subclass of CL_CRMCMP_I_HIDDENVIEW_IMPL.


--------------------------------------------
CALL METHOD super->if_crm_ic_event_listener~handle_event
         EXPORTING
              event = event.

CHECK event->get_name( ) = 'Z_TOOLBAR_EVENT'.

CALL METHOD event->get_param
    EXPORTING
        name = 'Parameter1'
    IMPORTING
        value = lv_param.

CASE lv_param.

    WHEN 'ZGG'.
        ..............
        ..............
        ..............
    WHEN ....

    WHEN OTHERS.
      .........
ENDCASE.
--------------------------------------------


As the above class is within the IC Framework, we have the ability to read the GDC for instance, to check if (and which) businesspartner is selected, or to read the current interactionrecord.


This allows us to implement complex logic.


You can also implement navigation to a certain screen if you want.

Wednesday, September 8, 2010

Escalate button in the Interaction Record

When working on inbound phonecalls or emails in an interaction center, it might occur that an agent cannot resolve the customer's request immediately. The system requirement would now be that the customer's request would be forwarded to a different department within the company. There are several ways to achieve this:

•    Creating a 'service ticket' and forwarding this to another group
•    Creating a 'case' and forwarding this to another group
•    Enable forwarding of interaction records

Benefit of the first two options, is that these items are meant to be forwarded in the standard system.
Benefit of the last option is that you can keep all information together on one document, and forward this to departments throughout the company.

To support the forwarding of an item, you can enable the escalate button using a simple modification. After this modification, the functionality that is normally only available on service tickets, will now also become available on the interaction record. The contact log of the call can now, based on the context in the interaction record be escalated to different departments in the company. The routing is handled by routing rules in the rule modeler

In order to enable the escalate button in the interaction record, enhance class CL_ICCMP_BT_BSPWDCOMPONE6_IMPL method WD_USAGE_INITIALIZE, and add the following code:

  If iv_usage->usage_name = 'InrButtonBarId'.

          lr_node  ?=   iv_usage->get_context_node( iv_cnode_name = 'BUTTON').

          lv_button-id = 'ROUTE'.   
          lv_button-enabled = abap_false.
          lr_node->add_button( is_thtmlb_button = lv_button ).

  Endif.

You will now see the escalate button in the interaction record screen as well. The button can be used to automatically assign an organisational unit to the interaction record as the responsible group.

You can also implement the automatic triggering of the escalation when the status of the interaction record has changed. This way, users do not have to click the button while they already changed the status of the interaction record from closed to open.
This can be implemented by enhancing the component ICCMP_BTSHEAD view BTSHeader, redefining the event EH_ONSTATUSCHANGE. 

In the event, add the following coding:

call method super->EH_ONSTATUSCHANGE
  EXPORTING
    HTMLB_EVENT = HTMLB_EVENT
    HTMLB_EVENT_EX = HTMLB_EVENT_EX.

data:
      lr_cucobt      type ref to cl_iccmp_cucobt_impl,
      lr_btadminh   type ref to if_bol_bo_property_access,
      lr_btstatush  type ref to if_bol_bo_property_access,
      dref             type ref to data.

FIELD-SYMBOLS
                        <lv_status>             type any,
                        <lv_process_type>    type any.

TRY.
  lr_cucobt ?= get_custom_controller( 

       controller_id = if_iccmp_global_controller_con=>cucobt ).
  catch cx_root.
ENDTRY.

IF lr_cucobt IS NOT BOUND.
  RETURN.
ENDIF.

lr_btadminH = typed_context->BTAdminH->collection_wrapper->get_current( ).

if lr_btadminH is bound.
  dref = lr_btadminH->get_property( 'PROCESS_TYPE' ).
  ASSIGN dref->* to <lv_process_type>.
  CHECK <lv_process_type> = '[your process type]'.

  lr_BTSTATUSH = typed_context->BTSTATUSH->collection_wrapper->get_current( ).
  dref = lr_btstatusH->get_property( 'ACT_STATUS' ).
  assign dref->* to <lv_status> .
  check <lv_status> = '[the E**** status]'.

  lr_cucobt->route(  ir_btorder = lr_btadminH ).
endif.


The coding checks on whether it concerns the interaction record process type, and will only escalate if the correct status is set.

Wednesday, September 1, 2010

Disabling fields using the GET_I

Fields can be disabled in customizing. In some cases though, this might not be sufficient. If for instance the requirement is that the field is in some cases disabled, for instance depending on another field or checkbox or radiobutton, you can redefine the GET_I method of this field.

If you wish to disable the field, insert the statement
rv_disabled = 'TRUE'.
If you wish to enable the field, insert the statement
rv_disabled = 'FALSE'.

Of course you can call other methods to determine the context and put some if-statements around the rv_disabled = ... statements to have the system determine the enabling/disabling of the fields correctly.