Quantcast
Channel: SCN : Blog List - SAP CRM: Webclient UI - Framework
Viewing all 195 articles
Browse latest View live

Some special points in Get_V methods in advance searches

$
0
0

GET_V methods locations:

 

We all know, if we want to add value help to a field, or replace standard value help to a field, we can make use of GET_V method. For example, in the sales order creation screen, the 'Status' field is a DDLB field, it has some values:

1. status ddlb screen.PNG

 

This status field comes from context node BTSTATUSH of view BT115H_SLSO/Details:

2. status get_v method.PNG

If we want to change standard value help, we can make enhancement on component BT115H_SLSO and view 'Details', enhance context node BTSTATUSH and redefine method CL_BT115H_S_DETAILS_CN05->GET_V_STATUS_CURRENT.  In the same way, we can also add value helps by adding GET_V methods to those fields which have no value help originally.

 

However, there are some special cases in which the GET_V methods are not attached to the context node classes. For example, search field 'Sales Org. ID' has a value help:

3. sales org field.PNG

This field is attribute STRUCT.SALES_ORG of context node BTQSLSORD in view BT115S_SLSO/SlsOrdSQ. This attribute has no GET_V method:

4 sales org.PNG

This is because there is a method GET_V_SALES_ORG in class CL_BT115S_S_SLSORDSQ_IMPL which actually locates in class CL_BTSLS_ADVS_CNTRL:

5. get_v in IMPL.PNG

Of course, class CL_BT115S_S_SLSORDSQ_IMPL can also redefine its own logics in the GET_V methods. For example, CL_BTSLS_ADVS_CNTRL->GET_V_EMPLOYEE_RESP is redefined into CL_BT115S_S_SLSORDSQ_IMPL->GET_V_EMPLOYEE_RESP.

6 own get_vmethods.PNG

 

In SERVICEPRO business role, in search service order, GET_V methods exist in CL_BT116S_S_ADVANCEDSP_IMPL/CL_BTSRV_ADVS_CNTRL too.

7 service pro.PNG

 

GET_V methods types:

Inside the GET_V methods, there are different types to render the value helps:

  1. make use of value help id. The example is CL_BT116S_S_ADVANCEDSP_IMPL/CL_BTSRV_ADVS_CNTRL->GET_V_SALES_ORG8-1 value help ID.PNG
  2. make use of ddlb. The example is CL_BT116S_S_ADVANCEDSP_IMPL/CL_BTSRV_ADVS_CNTRL->GET_V_SERVICE_ORG
    8-2 ddlb.PNG
  3. make use of event triggers. For example, in method CL_BT116S_S_ADVANCEDSP_IMPL/CL_BTSRV_ADVS_CNTRL->GET_V_CATEGORY_ID, it defines event 'SELECT_CATEGORY' (if_crm_srqm_uiu_const=>gc_event_select_category).
    8-4 trigger event.PNG
    Of course, the event should be handled somewhere. Here it is CL_BTSRV_ADVS_CNTRL->DO_HANDLE_EVENT.
    8-5 event methods.PNG

 

 

Another usage for the GET_V methods are controlling operaters: For example: in method CL_BT116S_S_ADVANCEDSP_IMPL/CL_BTSRV_ADVS_CNTRL->GET_V_SERVICE_ID.
8-3 operators.PNG


Customer fields created from AET don't generate change documents

$
0
0


After we find out that fields that were added into ORDERADM_H with AET are not generate a change log. I open an incident and after we got an answer "that's the way it is" but with a clue what can be done.

 

you can see the note that were created: 2248082 - Customer fields created from AET don't generate change documents

 

It's a REPAIR but at least it working

 

My change only record changes of ORDERADM_H.

 

1) Go to FM CRM_ORDER_CHANGE_DOCS_CREATE

2) Change to edit mode (remeber to get an access key)

3) After variables declaration INSERT:

 

   DATA:   lt_orderadm_h_new     TYPE vcrma_orderadm_h  OCCURS 0,
               lt_orderadm_h_old     TYPE vcrma_orderadm_h  OCCURS 0,
               lv_orderadm_h_wrk TYPE  crmt_orderadm_h_wrk,
               lv_orderadm_h LIKE LINE OF lt_orderadm_h_new.

 


4) line 344 (before changes) REPLACE:

 

    LOOP AT lt_objects INTO ls_objects
          WHERE chd_relevance NE space                  AND
                ( kind EQ gc_object_kind-extension_h OR
                  kind EQ gc_object_kind-set ) AND
                generated = false.

 

WITH:

 

      LOOP AT lt_objects INTO ls_objects
            WHERE chd_relevance NE space                  AND
                ( ( kind EQ gc_object_kind-extension_h OR
                  kind EQ gc_object_kind-set ) OR
                  NAME = gc_object_name-orderadm_h ) AND
                generated = false.

      IF ls_objects-name = gc_object_name-orderadm_h.
        MOVE-CORRESPONDING ls_orderadm_h_old TO lv_orderadm_h.
        APPEND lv_orderadm_h TO lt_orderadm_h_old .

        MOVE-CORRESPONDING ls_orderadm_h_new TO lv_orderadm_h.
        APPEND lv_orderadm_h TO lt_orderadm_h_new.
      ENDIF.


5) line 1301 (before changes) INSERT:

 

        ycrma_orderadm_h      = lt_orderadm_h_old
        xcrma_orderadm_h      = lt_orderadm_h_new

 

 

 

6) Activate and pray

 

Good luck

Eli

What is uif_callback doing

$
0
0

UIF_CALLBACK is a service, as seen from its description in SICF – ‘Generic callback’,  it is used to dispatch the requests into specific classes which are specified as value of parameter ‘crm_handler'. It’s handler class is: CL_CRM_WEB_UTILITY:
1. handler class for uif_callback.PNG

In this article, I will explain with some samples how this service runs.

 

Sample 1:

 

We all know, for performance purpose, when CRM webui is loaded, Java script files under BSP Application THTMLB_SCRIPTS will be merged into one scripts.js file:

2. scripts.js.png

But in this way, it is not convenient to debug when we have some java script problems. Thus user parameter BSP_DLC_DEBUG_PARAM is introduced(in SAP Note 1771865). When this user parameter is set to JS_ALL_FRAMES, things will be changed -- all java script files under BSP Application THTMLB_SCRIPTS will not be merged and loaded into one file, they will be loaded separately and one by one.

4 user parameter.PNG

3. all js.png

From the trace, we can see the service uif_callback is being used, for example:

…/SAP(====)/webcuif/uif_callback/crm_ui_frame/SCRIPTS_.GLOBALS.JS?CRM_HANDLER=CL_THTMLB_SCRIPTS_MANAGER…

 

Here, value for parameter CRM_HANDLER is CL_THTMLB_SCRIPTS_MANAGER.

 

How is it done?

 

In method CL_THTMLB_CONTENT-> IF_BSP_ELEMENT~DO_AT_BEGINNING, parameter BSP_DLC_DEBUG_PARAM is checked. If it is not set, the JS script will be ‘thtmlb_scripts/scripts.js’. But if it is set to value JS_ALL_FRAMES, requests of ‘…webcuif/uif_callback/…’ will be formed instead:

4-1 logics.PNG

 

 

 

Then how are these classes working?

 

 

Let’s set a breakpoint in methods CL_CRM_WEB_UTILITY-> IF_HTTP_EXTENSION~HANDLE_REQUEST and  CL_THTMLB_SCRIPTS_MANAGER-> IF_CRM_WEB_CALLBACK~HANDLE_REQUEST and see what happens.

 

  1. When the request is sent to backend system, CL_CRM_WEB_UTILITY-> IF_HTTP_EXTENSION~HANDLE_REQUEST parses the request parameters and gets the handler class, here it is CL_THTMLB_SCRIPTS_MANAGER:
    5. utility hander.PNG
  2. Object is created and initialized:
    6. initiate class.PNG
  3. Application handler is called back:
    7. call the handle_request of the class.PNG
  4. Inside the method CL_THTMLB_SCRIPTS_MANAGER->IF_CRM_WEB_CALLBACK~HANDLE_REQUEST, requests are processed, the .JS content is read and returned:
    8. load the JS.PNG
  5. From the httpwatch trace, we can see the JS content is returned:
    8-1 JS content is loaded into IE.PNG

Sample 2:

As introduced in SAP Note 2181937 ( Recent Items Filtering ), ‘Recent Items’ have filter functions added. Now, for the initial load of recent Items, for the filter dropdown list, for the items retrieving on a specific filter, they are using UIF_CALLBACK.

 

Take initial load of recent items as an example:

  • In crm_bsp_recobj/RecentObjects.htm, there are codes:
    1. initla load codes.PNG
  • 'Fetch' function is defined in THTMLB_SCRIPTS-> scripts_areaframe.js.
    2. fetch function.PNG
  • In this function, the request is sent out:
    3. submit url.PNG
    value for parameter ajaxURL is: "/sap(bD1FTiZjPTUwNCZpPTEmZT1TVEF6TXpVME1WOWZYMTlmTkRkZk1Ua3dBRkJXckViRUh1V21sQlNIX1ZJM2JnJTNkJTNk)/webcuif/uif_callback?crm_handler=CL_CRM_BSP__RECENTOBJECT0_IMPL&crm_controller=C6_W23_V24&wcf_request=display&wcf-request-ticket=CDB907FAF99F328F1997F34FF2C21322"
  • The same, if we set breakpoint in CL_CRM_BSP__RECENTOBJECT0_IMPL-> IF_CRM_WEB_CALLBACK~HANDLE_REQUEST, we will get the callstack:
    4. callstack.PNG

The same thing happens when click the triangle icon for ddlb and select an item to filter:

5. click ddlb.PNG6. select an item.PNG

 

Additional:

There are many other usages via this service uif_callback. For example, CL_THTMLB_COLOR_UTIL, CL_CRM_UI_SESSION_MANAGER, CL_THTMLB_COLOR_UTIL, CL_THTMLB_SMART_VALUE_HELP, CL_THTMLBX_SILVERLIGHT_ISLAND, CL_THTMLBX_FLASH_ISLAND, etc.

where do these names come from

$
0
0

In CRM WebUI pages, there are some tab names. For example:

-- in the business roles selection page, it shows ‘Select a business role: - [XXX XXX]’.

0. business role.PNG

-- After selecting a business role IC_AGENT, if the work area is in ‘Identify Account’, the IE tab name will be ‘Identify Account – [Interaction Center]’.

9. interaction center name.PNG

If work area is changed, the tab name will change accordingly:

9-0. another tab nanme.PNG

 

Where are these names coming from?

 

 

-- Business role selection page:

 

For this page, the title is set in BSP Application CRM_UI_START->default.htm.

1. business role title.PNG


How is the value of title set?  The title is set in OnCreate event for CRM_UI_START->default.htm:

2-2-2. get title.PNG

The detailed codes are in method CL_CRM_UI_WINDOW_TITLE_SRV->GET_FULL_TITLE:

2-2. get full title.PNG

Then let’s see how variants gv_title and gv_workarea_title come.

 

gv_title is set when title server is initialized:

2-4-1. gv_title calls.PNG

The source codes locate in CL_CRM_UI_WINDOW_TITLE_SRV->GET_INSTANCE:

2-4. get gv_title.PNG


gv_workarea_title is set in OnCreate event for CRM_UI_START->default.htm:

2-3-1 gv_workarea calls.PNG

The detailed codes are in CL_CRM_UI_WINDOW_TITLE_SRV->SET_WORKAREA_TITLE:

2-3. GV_workarea_title.PNG

To summarize:

0. summarize.PNG

 


-- In a work area:

 

Tab name is set in BSP Application CRM_UI_FRAME-> WorkAreaViewSet.htm:

9-3 set titles.PNG

Javascript Codes to set title is in CRM_UI_FRAME-> lscripts.htm:

9-3-2 set title JS codes.PNG

While to get full tile, it is in method CL_CRM_UI_WINDOW_TITLE_SRV->GET_FULL_TITLE:

9-2. codes for get full title.PNG

Let’s see how the values for gv_workarea_title and gv_title are set.

gv_workarea is set from breadcrumb BSPWD_BASICS->BREADCRUMBVIEW.HTM:

9-5 set workarea title.PNG

The detailed codes are in method CL_CRM_UI_WINDOW_TITLE_SRV->SET_WORKAREA_TITLE:

9-4 codes for set workarea.PNG

 

For gv_title, it is different between IC agent roles and non-IC roles:
-- for IC roles, it is set in _ONINITIALIZATION event of BSP Application crmcmp_ic_frame-> start.htm:
9-1. gv_title for IC.PNG
Detailed codes are in method CL_CRM_UI_WINDOW_TITLE_SRV-> SET_TITLE:
9-1-1 codes for set gv_title.PNG

-- for non-ic roles, it is set in CRM_UI_FRAME-> WORKAREAVIEWSET.HTM:
9-6 gv_title for non-ic.PNG
Detailed codes are in method CL_CRM_UI_WINDOW_TITLE_SRV-> GET_INSTANCE:
9-7 codes for gv_title in non-ic.PNG

How to resolve the error BSP_WD_TOOLS304: Subobject type &1 is not valid for object type &2

$
0
0

Sometimes when we create/copy a configuration, we may encounter error: BSP_WD_TOOLS 304 Subobject type &1 is not valid for object type &2 like following.

1Capture.PNG

 

 

Actually, The Object Sub Types that are regarded to be allowed for a certain UI-Object Type are determined by a Call Back Class, that is defined in the customizing for UI-Object Types and which implements IF_BSP_DLC_OBJ_TYPE_CALLBACK with method GET_OBJECT_SUB_TYPES( ).You can run T-code: bsp_dlc_sdesign to display the "UI Object type" settings to understand which call back class is used for standard UI Object types.

 

When you encounter the error "BSP_WD_TOOLS 304 Subobject type &1 is not valid for object type &2", the reason could be:

 

 

1.You are using a customer z-Object type ZPRD_Z001 does not have a Callback Class defined, which implies that no other Object SubTypes are allowed than <DEFAULT>.

 

2.The standard call back class doesn't allow your customer subobject type.

 

 

For above cause 1, the solution is following:

 

Please create a z-callback class for your z object type to retrieve your object subtype. For this you must create a call back class that is a Z class which must implement IF_BSP_DLC_OBJ_TYPE_CALLBACK with method GET_OBJECT_SUB_TYPES(), then implement method GET_OBJECT_SUB_TYPES() according to your requirement. You can refer to the standard call back class in T-code: bsp_dlc_sdesign。

 

Then go to following customizing and customize your call back class. [T-code: SPRO -> Customer Relationship Management -> UI Framework  ->UI Framework Definition -> Define UI Object Types.]

 

 

For above cause 2, you can use the same solution I mentioned above for point 1 to create your own call back class.

 

Or you can debug in the standard call back class to find and understand why standard logic doesn’t allow your z subobject type. If you find any setting problem, you can correct them to resolve the error.

 

Here I provide you following two examples.

 

Example Problem 1 for view: ICCMP_BTSHEAD/BTSHeader:

1Capture.PNG

 

1. In T-code: bsp_dlc_sdesign, we can see the Callback Class for Object Type BT126_INR(see above screenshot) is CL_CRM_UIU_BT_OBJTYPE_CALLBACK. Then we set breakpoint at method IF_BSP_DLC_OBJ_TYPE_CALLBACK~GET_OBJECT_SUB_TYPES of this class.

2Capture.PNG

 

2.When debugging at following FM, we can see the cause is that the sub object type is not read into lt_pr_type which is for on-line transaction types.

 

3Capture.PNG

3.Then we check transaction type ZLJI(sub object type) customizing, we find we set it as "Y Blocked for online Maintenance". The standard logic will not load any "X inactive" or "Y Blocked for online Maintenance" entries. As webUI is for online processes.

 

4 Capture.PNG

4.After we blank this field "Inactive", the error is resolved.

 

 

Example Problem 2 for view: SRQM_INCIDENT_H/IncidentHeaderEF:

 

5Capture.PNG

1. In T-code: bsp_dlc_sdesign, we can see the Callback Class for above Object Type CRM_SRQM_INCIDENT is CL_CRM_UIU_BT_OBJTYPE_CALLBACK. Then we set breakpoint at method IF_BSP_DLC_OBJ_TYPE_CALLBACK~GET_OBJECT_SUB_TYPES of this class.

 

6Capture.PNG

2.When debugging at following FM, we can see the cause is that the template type is not initial.

7Capture.PNG

 

3.Then we check transaction type ZRQT(sub object type) customizing, we find we set template type as D.

8Capture.PNG

 

4.After we blank this field "Template Type", the error is resolved.

 

 

Please note that inappropriate callback method and entries in customizing table BSPC_DLC_OBJ_TYP will cause errors. So you need to involve your abaper to create method IF_BSP_DLC_OBJ_TYPE_CALLBACK~GET_OBJECT_SUB_TYPES of your z call back class correctly and test it thoroughly. If you encounter any problem due to your call back class, then you can refer to following KBA, to change it back to a standard one as a workaround.

 

1908034 - Error message "Entity could not be determined" and "Entity cannot be determined" on WebUI

http://service.sap.com/sap/support/notes/1908034

 

Best Regards,

Bruce

Parallelization in Web UI - Part 1

$
0
0

Parallelization in Web UI


Part1

 

1.    General Idea

All modern web-browser applications support parallelization or asynchronous processing of the requests from the user. Nowadays, the backend systems are scalable and therefore the processing of several requests will not consume more system power (system time) than one big request which contains the logic of four requests. Of course, the load must be properly distributed across the processing units (servers) and in time, so that we never come to the system resources border. Implementation of the parallel request processing can significantly improve the performance and usably without a need in new hardware.

Initially, when SAP WebUI was designed, it did not implement this concept and it was done by intention. Also by intention, it is implemented as a stateful application. Only after some time several asynchronous features like Asynchronous Value Fetch (AVF), asynchronous table rendering (fake rendering), drag-and-drop were introduced.

You would like to implement parallel processing in your SAP WebUI and you are looking for more information regarding possible solutions.

 

2.    Possible variants

In this document, we will review some options, which can be implemented in SAP WebUI. Particularly we will review the use of HTML iframes, AJAX calls and asynchronous RFCs and their implementations in stateful and stateless applications. Later you will understand all possible options and choose the most appropriate one or the combination.

 

3.    iFrames

One of the oldest concept of enabling the parallelization of the requests in the browser-based applications is based on HTML iframes.


<iframesrc="http://www.w3schools.com"></iframe>

 

For more information, please refer to W3Schools’ web site: http://www.w3schools.com/tags/tag_iframe.asp


3.1.iFrames in WebUI


You can also implement this concept in WebUI. The following diagram explains how you can do it.

image001.png

                       

This approach can work with stateless applications or stateless frames only, and therefore the typical use case for this would be a customer fact sheet or similar applications. In our case, I created a Main Window, a main View Set and several views containing iframes. You can see that each iframe contains an independent HTML (in our case BSP) application. The parameters can be transported via URL, and it does not have to be static.

Most of the time the information in WebUI is shown in tables and you can do it as well with iframes.

 

image002.png

 

In our example opportunity frame PartnerOpportunity.htm looks as below.


<%@page language="abap" %>
 
<%@extension name="htmlb" prefix="htmlb" %>
 
<%@extension name="thtmlb" prefix="thtmlb" %>
 
<%@extension name="chtmlb" prefix="chtmlb" %>
  <htmlb:content design      =
"design2008"
                forceEncode =
"ENABLED" >
    <thtmlb:content renderBody =
"true"
                    renderHead =
"true"
                   
title      = "Test Page" >
      <htmlb:form>
        <chtmlb:pageType type=
"EDIT" >
          <thtmlb:cellerator actions                =
"<%= action_tab %>"
                            actionsMaxInRow        =
"4"
                            columnDefinitions      =
"<%= column_tab %>"
                            editMode                =
"NONE"
                            enableTableGraphics    =
"false"
                            fixedHeaders            =
"true"
                            hasExpander            =
"true"
                            hasPagerWhenCollapsed  =
"true"
                            headerText              =
"Opportunities"
                            horizontalScrolling    =
"false"
                           
id                      = "test1"
                            isExpanded              =
"false"
                            iterator                =
"<%= iterator %>"
                            pagerEntryCount        =
"10"
                            personalizable          =
"true"
                            scrollRowCount          =
"5"
                            selectedRowIndex        =
"1"
                            selectionMode          =
"NONE"
                            showEmptyTableText      =
"true"
                            showPersonalizeButton  =
"true"
                            showToolbarArea        =
"true"
                           
table                  = "<%= data_tab %>"
                            verticalScrolling      =
"false"
                            visibleRowCount        =
"5"
                            visibleRowCountExpanded =
"5" />
        </chtmlb:pageType>
      </htmlb:form>
    </thtmlb:content>
  </htmlb:content>

 

To get the data I am using event OnInitializationwith the following coding.


* event handler for data retrieval
 
 
*** Get the partner from the URL
  partner
= request->get_form_field( 'partner' ).
 
 
 
check partner is not initial.
 
 
call function 'CONVERSION_EXIT_ALPHA_INPUT'
   
exporting
     
input  = partner
   
importing
     
output = partner.
 
 
*** Check the partner for existance
 
select single partner from but000 into (partner) where partner = partner.
 
if sy-subrc <> 0.
   
clear partner.
   
exit.
 
endif.
 
 
 
*** Read the data into collection
 
data: lr_core          type ref to cl_crm_bol_core.
 
data: lr_entity        type ref to cl_crm_bol_entity.
 
data: lr_collection    type ref to if_bol_bo_col.
 
data: lv_obj_guid      type crmt_genil_object_guid.
 
data: lv_partner_guid  type  bu_partner_guid.
 
 
try.
      lr_core
= cl_crm_bol_core=>get_instance( ).
 
      lr_core
->start_up( 'BP_APPL ' ).
 
     
call function 'BUPA_NUMBERS_GET'
       
exporting
          iv_partner     
= partner
       
importing
          ev_partner_guid
= lv_partner_guid.
 
      lv_obj_guid
= lv_partner_guid.
 
      lr_entity
= lr_core->get_root_entity( iv_object_name = 'BuilHeader'
                                            iv_object_guid
= lv_obj_guid ).
 
     
check lr_entity is bound.
 
      lr_collection
= lr_entity->get_related_entities( iv_relation_name = 'BuilOpportunityRel' ).
 
     
check lr_collection is bound.
 
   
catch cx_root.
 
 
endtry.
 
 
*** Fill the data table
 
data: ls_data like line of data_tab.
 
 
clear data_tab[].
 
  lr_entity ?= lr_collection
->get_first( ).
 
 
while lr_entity is bound.
    lr_entity
->get_properties( importing es_attributes = ls_data ).
   
append ls_data to data_tab.
    lr_entity ?= lr_collection
->get_next( ).
 
endwhile.
 
 
 
*** Create Iterator
 
if iterator is not bound.
   
create object iterator.
   
iterator->data_tab[] = data_tab[].
   
iterator->runtime = runtime.
 
endif.

 

Take the HTTPWatch trace to see how the parallelization works.


image003.png

 

One can see that all our three main requests were triggered at the same time and were processed in parallel.


 

3.2.Limits of a iframes in WebUI


However, using this concept has one very big constraint. The WEBUI framework does not know tables visible on the screen and therefore you cannot easily jump into the object, e.g. opportunity, as it has no navigation link and cannot have it by definition. You can easily show static information, as plain text, pictures, external links etc., but navigation within WEBUI is almost impossible.

Why almost, because you can still use your own iterator (IF_HTMLB_TABLEVIEW_ITERATOR) and build a link to launch a certain object in WEBUI, e.g. incident; however this looks too brutal to me.


        concatenate '/sap/bc/bsp/sap/crm_ui_start/default.htm?'
                     
'crm-object-type='    'CRM_SRQM_INCIDENT'  '&'
                     
'crm-object-keyname='  'OBJECT_GUID'        '&'
 
                  'crm-object-value='    lv_guid
                     
into lv_url.
 
         
condense lv_url.
 
         
call method cl_thtmlb_link=>factory
           
exporting
             
id            = p_cell_id
             
reference    = lv_url
              target       
= '_top'
             
text          = lv_text
           
receiving
              element     
= p_replacement_bee.

 

 

Parallelization in Web UI - Part 2

$
0
0

Parallelization in Web UI

Part2

 

4.     AJAX


Concept

AJAX (short for asynchronous JavaScript and XML) is a set of web development techniques utilizing many web technologies used on the client-side to create asynchronous Web applications. With AJAX, web applications can send data to and retrieve from a server asynchronously (in the background) without interfering with the display and behavior of the existing page. By decoupling the data interchange layer from the presentation layer, AJAX allows for web pages, and by extension web applications, to change content dynamically without the need to reload the entire page. Data can be retrieved using the XMLHttpRequest object.


AJAX is not a technology, but a group of technologies. HTML and CSS can be used in combination to mark up and style information. The DOM is accessed with JavaScript to dynamically display – and allow the user to interact with – the information presented. JavaScript and the XMLHttpRequestobject provide a method for exchanging data asynchronously between browser and server to avoid full page reloads.

For more information, please refer to the Wiki’ and W3Schools’ pages: https://en.wikipedia.org/wiki/Ajax_(programming)  and http://www.w3schools.com/ajax/

 

AJAX in SAP CRM WebUI

As mentioned above some of the AJAX features are implemented in the standard CRM. One of the nice examples, where you can do a reverse engineering, is the Recent Items in CRM WebUI.


image004.png  image005.png
                                         

View: CRM_BSP_RECOBJ/RecentObjects

In this case, the AJAX call is implemented, right on the BSP page: RecentObjects.htm


    <!--this code performs a callback to refresh the items list. Only executed the first time when the BOL objects are not loaded yet-->
      <script>
        thAVFMgr.fetch(document.getElementById(
"<%= controller->COMPONENT_ID %>"),'onRecentItemsRefresh');
      </script>

 

In this example, it is using relatively old asynchronous value fetch (AVF) functionality. However, in newer releases, after SAP Note: 2181937, the call looks like below:


 

thShortCutMgr.fetch('<%=   controller->COMPONENT_ID %>','CL_CRM_BSP__RECENTOBJECT0_IMPL','display');

 

All the mentioned standard JS functions can be found in one of the JS files of THTMLB_SCRIPTS BSP application, e.g. scripts_areaframe.js, scripts_deltahandling_client.js. You can also search for the needed function right in the browser, e.g. in Chrome, activate Developer Tools (F12) à Sources à Menu à Search all Files (Ctrl + Shift + F).

How it works. A certain function (e.g. thAVFMgr.fetch, thShortCutMgr.fetch and many others) builds a proper AJAX URL, containing all necessary parameters. Then the function thtmlbAJAXCall.callBackend(AJAXUrl,CallBackEvent);is executed. The first parameter is our AJAX URL and the second is the call-back function that is registered to receive the result from the AJAX call.

Typical parameters that you need to provide to the back-end are the following: service handler, callback handler, DOM element id, sometimes type of the element.


Service Handler

Service handler, it’s the ICF service that you call on the backend. WebUI is using the standard service: /default_host/sap/webcuif/uif_callback, which has only one handler - CL_CRM_WEB_UTILITY. 


Callback Handler

Callback handler, it’s the class that implements a callback method, which will be called in the AJAX call. This class should implement the interface IF_CRM_WEB_CALLBACK. And the mandatory callback method is IF_CRM_WEB_CALLBACK~HANDLE_REQUEST. For the JS object thAVFMgr, its callbackhandler is hardcoded in the JS file and depend on the callback JS function:


case   "onRecentItemsRefresh":

if(!trigger) {

return;

}

var ajaxURL =   newSessionURL.URL + "/webcuif/uif_callback?crm_handler=CL_CRM_BSP__RECENTOBJECT0_IMPL";

ajaxURL +=   "&crm_controller=" + encodeURIComponent(trigger.id) + thtmlbBuildRequestTicketQuery();

}

 

DOM Element ID

When the AJAX callback handler is executed, it sends the HTML response back to the frontend. On the frontend, a corresponding JS function receives the response (reqObject) and processes it. Finally it needs to find a proper DOM element in order to do some actions with it. That’s when you need a DOM Element ID.


 

var myelement = document.getElementById(elementID);

See more: http://www.w3schools.com/js/js_htmldom_elements.asp


AJAX Call

When all parameters are known, you can perform an AJAX call. In WebUI a special THTMLB object is used for this.


 

thtmlbAJAXCall.callBackend(ajaxURL,   ajaxCallbackFunction);

You should be careful with visibility of the elements through different iframes and also in time.


JS Callback Function

You need a callback function to receive the response from your AJAX call, process it and update the corresponding DOM element. Below you can find an extract of a function used for asynchronous value fetch.


fetch: function(trigger,   avf_event, csvSourceFields, rowIndex, customRef, csvTargetFields){

thtmlbAJAXCall.callBackend(ajaxURL,   this.update);

}

 

 

update: function(reqObject) {

var responseText =   reqObject.request.responseText;

var responseBlocks =   responseText.split(";");

 

for(var i=0,   len=responseBlocks.length; i<len; i++) {

if(!responseBlocks[i]) {

continue;

}

 

var blockParts =   responseBlocks[i].split(",");

if(blockParts.length <   4) {

continue;

}

var tagclassname =   blockParts[0];

var elementid =   blockParts[1];

var attributeName =   blockParts[2];

 

var payload =   thtmlbDecodeBASE64(blockParts[3]);

var payloadValue =   thtmlb_decode_html(payload);

var targetElement =   thtmlbGetElement(elementid);

case 'tooltip':

if (targetElement.title   != payloadValue) {

targetElement.title =   payloadValue;

}

}

 

  Storing your JS Files

When developing your JS you need to make sure it is visible for your applications. In case you want to place a simple script to your page, you can do it right on the BSP page.


<!—Here you want to store some Javascript -->
      <script type=
"text/javascript">
       
var MyElement = document.getElementById("<%= controller->component_id %>");
        function WindowOpenPopup(varTxt)   { alert(varTxt); }

    </script>

 

You can also store it as a MIME object within your BSP application and include it on the page.


 

<script type="text/javascript" src="/sap/bc/bsp/sap/z<bspcmp>/scripts.js"></script>

 

For relatively big applications or if you want your JS code to be visible for the whole WebUI framework, you can store the reference to your JS file in eth table WCFC_ADD_JS.


IMG à Customer Relationship Management à UI Framework à UI Framework Definition à Define Path for JS Files

 

  image006.png

 

Note: Last option is tested only in SAP CRM 7.0 EHP3.

 

 

Simple case

Let us practice first on a simple case, lets update a simple object link (<thtmlb:link>). As usual, we need to create a WebUI component, a view containing the link and a viewset.  

 

image007.png

 

When working with AJAX in WebUI you have to generate the view content via corresponding FACTORY methods.

 

<%@page language="abap" %>
 
<%@extension name="thtmlb" prefix="thtmlb" %>
 
<%@extension name="chtmlb" prefix="chtmlb" %>
 
<%@extension name="bsp" prefix="bsp" %>
 
 
<%
   
data: lv_link_bee type ref to cl_thtmlb_link.
   
if controller->gv_thtmlb_element_link is initial.
   
" Create Link
   
call method cl_thtmlb_link=>factory
   
exporting
    id
            = 'dynamic_link'
    reference
     = 'javascript:void(0);'
    onclick      
= 'ZBTN_CLICK'
    text
          = 'Loading...'
   
receiving
    element      
= lv_link_bee.
   
" Store the link
    controller
->gv_thtmlb_element_link ?= lv_link_bee.
   
"   Post AJAX call to update content
 
%>
 
<!--this code performs a callback to refresh the items list. Only executed the first time when the BOL objects are not loaded yet-->
      <script type=
"text/javascript">
       
var MyElement = document.getElementById("<%= controller->COMPONENT_ID %>");
        zDMSHAVFMan.fetch(MyElement,
'ZDMSHAJAXCall');
      </script>
 
<%
   
endif.
   
" Get teh link
    lv_link_bee ?= controller
->gv_thtmlb_element_link.
   
" Set the text
   
if controller->gv_text is not initial.
    lv_link_bee
->text = controller->gv_text.
   
endif.
   
" Render html element
    lv_link_bee
->if_bsp_bee~render( _m_page_context ).
 
%>
  </br>
  </br>

 

In this case I just copied the standard JS AVF functionality (object thAVFMgr) into my own namespace and did desired modifications. You can also notice down that our DOM element is identified right on the page and the building of the right URL is a task for the copied JavaScript.

 

image008.png

 

Here I am using my view controller class as a callback handler. This means that it must implement callback method: IF_CRM_WEB_CALLBACK~HANDLE_REQUEST. Update JS function (this.update) has not been changed.

 

  method if_crm_web_callback~handle_request.
 
     
data: lv_link_tag       type ref to cl_thtmlb_link,
            lv_tag_class_name
type string,
            lv_tag_parent_id 
type string,
            lv_icon_mode     
type string,
            lv_html          
type string,
            lv_html_xstr     
type xstring,
            lv_response      
type string,
            lv_attribute_type
type string.
 
     
data: lr_controller type ref to zl_zdmsh_aj_mainframe_impl.
 
     
wait up to 3 seconds.
 
     
if ir_controller is bound.
 
        lr_controller ?= ir_controller
.
 
        lr_controller
->gv_text = 'Hello from AJAX !!!'.
 
       
try.
            lv_link_tag ?= lr_controller
->gv_thtmlb_element_link.
 
            lv_link_tag
->text = lr_controller->gv_text.
 
            lv_tag_class_name
= 'CL_THTMLB_LINK'.
            lv_tag_parent_id
= lv_link_tag->m_parent->id.
            lv_attribute_type
= 'something'.
 
            lv_html
= lv_link_tag->if_bsp_bee~render_to_string( page_context =

                                                  lr_controller->gv_thtmlb_element_link->m_page_context ).
            lv_tag_parent_id
= lv_link_tag->m_parent->id.
 
           
call function 'SCMS_STRING_TO_XSTRING'
             
exporting
               
text   = lv_html
             
importing
               
buffer = lv_html_xstr.
 
            lv_html
= cl_http_utility=>encode_x_base64( lv_html_xstr ).
 
           
concatenate lv_tag_class_name ',' lv_tag_parent_id ','

                      lv_attribute_type ',' lv_html ',' lv_link_tag->id

                 into lv_response.
 
            ir_server
->response->set_cdata(
             
exporting
               
data   lv_response   
           
).
         
catch cx_root.
       
endtry.
     
endif.
   
endmethod.

 

How does it look like:

 

image009.png  3 seconds later…  image010.png

 

HTTPWatch trace:

image011.png

 

It takes only 300 ms to build a page and 3 seconds later the link is updated.

 

 

Table Rendering

In WebUI most of the data is presented in tables, and therefore the option that is more desirable would be to render such kind of BSP elements. This topic however has been already described on SDN already, so see more at: http://scn.sap.com/community/crm/webclient-ui-framework/blog/2015/09/03/asynchronous-rendering-of-table-views . But here we will consider an option without any modification to SAP standard objects.

So, as usual, we need our WEBUI component, view set and the view. But also we need a context node, a value node in our case.

 

image012.pngimage013.png 

 

Our page TableView.htm looks like below:


<%@page language="abap" %>
 
<%@extension name="thtmlb" prefix="thtmlb" %>
 
<%@extension name="chtmlb" prefix="chtmlb" %>
 
<%@extension name="bsp" prefix="bsp" %>
 
<%
 
* Conversion Cnode SelectionMode to Tag
   
data: lv_cellerator_selectionmode type string,
    lv_cellerator_editmode 
type string,
    lv_cellerator_selectioncolumn
type string.
    cl_thtmlb_util
=>translate_selection_mode(
   
exporting
    iv_selection_mode   
= SOMEDATA->SELECTION_MODE
    iv_all_rows_editable
= space
   
importing
    ev_selection_mode  
= lv_cellerator_selectionmode
    ev_edit_mode       
= lv_cellerator_editmode
    ev_selection_column
= lv_cellerator_selectioncolumn ).
   
"
   
"
   
data: lv_table_bee type ref to cl_chtmlb_config_cellerator.
   
"
   
if controller->gv_config_table is initial.
   
"
   
" Create Table
   
call method cl_chtmlb_config_cellerator=>factory
   
exporting
   
id                    = controller->gc_table_id
    downloadToExcel      
= 'TRUE'
    editMode             
= 'NONE'
    onRowSelection       
= 'select'
    personalizable       
= 'TRUE'
    selectedRowIndex     
= somedata->selected_index
    selectedRowIndexTable
= somedata->selection_tab
    selectionColumn      
= lv_cellerator_selectioncolumn
    selectionMode        
= lv_cellerator_selectionmode
   
table                 = somedata->table
    _table               
= '//SOMEDATA/Table'
    usage                
= 'EDITLIST'
    visibleFirstRow      
= somedata->visible_first_row_index
    visibleRowCount      
= '6'
    width                
= '100%'
    xml                  
= controller->configuration_descr->get_config_data( )
    receiving
    element      
= lv_table_bee.
   
"
   
" Store the link
    controller
->gv_config_table ?= lv_table_bee.
 
%>
 
<!--this code performs a callback to refresh the table   -->
      <script type=
"text/javascript">
        debugger;
        thtmlbAJAXCall.callBackend(
"<%= controller->create_ajax_url( ) %>",   

                                                        thtmlbCCelleratorManager.createFastRowsCallback);
      </script>
 
<%
   
"
   
endif.
   
"
   
" Get the link
    lv_table_bee ?= controller
->gv_config_table.
   
"
   
" Render html element
    lv_table_bee
->if_bsp_bee~render( _m_page_context ).
 
%>
 
</br>

 

You can notice that in our case we perform an AJAX call directly (thtmlbAJAXCall.callBackend); the task to build a proper URL is addressed to ABAP method CREATE_AJAX_URL; and we use a standard callback function thtmlbCCelleratorManager.createFastRowsCallback to process the AJAX response.

 

How to build a proper URL for AJAX call? If you are going to use the standard AJAX service handler, there are many things that you need to consider: a handler class, a controller id, security session token, etc. But all this is considered in the method CL_CRM_WEB_UTILITY=> CREATE_SERVICE_URL. So finally our method looks very simple:

 

  method create_ajax_url.
 
     
data: lr_class_desc    type ref to cl_abap_typedescr.
 
      lr_class_desc
= cl_abap_classdescr=>describe_by_object_ref( me ).
 
     
call method cl_crm_web_utility=>create_service_url
       
exporting
          iv_handler_class_name
= lr_class_desc->get_relative_name( )
          iv_controller_id     
= me->component_id
       
receiving
          ev_url               
= rv_url.
 
   
endmethod.

 

In this case, we again use view controller class as a callback handler.

 

Next what we need is to implement IF_CRM_WEB_CALLBACK~HANDLE_REQUEST method. We do it in a very similar way as described here: http://scn.sap.com/community/crm/webclient-ui-framework/blog/2015/09/03/asynchronous-rendering-of-table-views. However, in our example, we are getting the data we need via parameter IR_CONTROLLER and therefore we do not need to change the standard SAP coding.

 

  method if_crm_web_callback~handle_request.
 
     
data: lr_controller                     type ref to zl_zdmsh_aj_tableview_impl.
     
data: lr_somedata                       type ref to zl_zdmsh_aj_tableview_cn00.
     
data: lr_wrapper                        type ref to cl_bsp_wd_collection_wrapper.
     
data: lv_html                           type string.
 
     
wait up to 3 seconds.
 
     
if ir_controller is bound.
       
try.
 
            lr_controller ?= ir_controller
.
            lr_somedata   ?= lr_controller
->typed_context->somedata.
            lr_wrapper    ?= lr_somedata
->collection_wrapper.
 
           
if lr_controller is bound and
               lr_somedata
is bound and
               lr_wrapper
is bound.
 
              lr_controller
->fill_context_node( ).
 
             
call method create_table_view_html_old
               
exporting
                  ir_server        
= ir_server
                  ir_controller    
= ir_controller
                  iv_binding_string
= '//SOMEDATA/TABLE'
                  iv_table_id      
= ir_controller->get_id( gc_table_id )
               
importing
                  ev_html          
= lv_html.
 
             
" Set Response
              ir_server
->response->set_cdata( lv_html ).
              ir_server
->response->set_header_field( name  = 'content-type'
                                                    
value = 'text/xml' ).
 
             
"Invalidate ´content
             
call method cl_ajax_utility=>invalidate_area_content
               
exporting
                  ir_controller
= ir_controller.
 
           
endif.
         
catch cx_root.
 
      endtry.
     
endif.
   
endmethod.

 

Method CREATE_TABLE_VIEW_HTML is implemented in exactly the same way as it was described on SDN already. I just repeat it here for consistency.

 

  method create_table_view_html.
 
 
    “ Constants
     
data: lc_separator type string value '__'.
 
 
    “ variables
     
data: lv_attribute_path     type string,
            lv_model_name        
type string,
            lv_lines             
type i,
            lv_string_lines      
type string,
            lv_count             
type i value 0,
            lv_row_id            
type string,
            lv_html              
type string,
            lv_template_row_tr_id
type string,
            lv_new_row_tr_id     
type string,
            lv_rows              
type string,
            lv_row_ids           
type string,
            lv_fixed_left_rows   
type string,
            lv_fixed_right_rows  
type string,
            lv_marked_rows       
type string.
 
 

    “ strucures
     
data: ls_area_content type crms_tajax_area_content.
 
 

   “ references
     
data: lo_page            type ref to cl_bsp_ctrl_adapter,
            lo_view_manager   
type ref to cl_bsp_wd_view_manager,
            lo_view_controller
type ref to cl_bsp_wd_view_controller,
            lo_model          
type ref to if_bsp_model_binding,
            lo_context_node   
type ref to cl_bsp_wd_context_node,
            lo_context_node_tv
type ref to cl_bsp_wd_context_node_tv.
 
 

    “ field symbols
     
field-symbols: <fs_page>  type bsprtip.
 
 
    “ create page instance
     
read table cl_bsp_context=>c_page_instances
     
with key page_name = cl_bsp_wd_appl_controller=>appl_controller_name
     
assigning <fs_page>.
 
 
    “ rendering
     
if sy-subrc is initial and <fs_page>-instance is bound.
        lo_page            ?= <fs_page>
-instance.
        lo_view_manager    ?= lo_page
->m_adaptee.
        lo_view_controller ?= ir_controller
.
        lo_view_manager
->render( iv_root_view = lo_view_controller ).
     
endif.
 
 
    “ get model
     
call method cl_bsp_model=>if_bsp_model_util~split_binding_expression
       
exporting
          binding_expression
= iv_binding_string
       
importing
          attribute_path    
= lv_attribute_path
          model_name        
= lv_model_name.


     
try.
          lo_model ?= ir_controller
->get_model( lv_model_name ).
          lo_context_node ?= lo_model
.
          lo_context_node_tv ?= lo_model
.
          lv_lines
= lo_context_node->collection_wrapper->size( ).
       
catch: cx_root.
         
exit.
     
endtry.
 
     
while lv_count < lv_lines.

 

      "Create AJAX content
        lv_count
= lv_count + 1.
        lv_string_lines
= lv_count.
       
condense lv_string_lines no-gaps.
       
concatenate iv_table_id '__' lv_string_lines '__1' into lv_row_id.
       
call method lo_view_controller->retrieve_ajax_area_content
         
exporting
            iv_area_id        
= lv_row_id
            iv_page_id        
= ir_controller->component_id
         
importing
            es_content_info   
= ls_area_content
            er_used_controller
= lo_view_controller.


       
"Covert HTML
       
if ls_area_content-area_content is not initial.
          lv_html
= cl_thtmlb_util=>escape_xss_javascript( ls_area_content-area_content ).
       
endif.
       
clear ls_area_content.


       
"Build table
        lo_context_node_tv
->build_table( ).


       
"Create Response
       
if lv_rows is initial.
         
concatenate `'` lv_html `'` into lv_rows.
         
concatenate `'` '' `'` into lv_fixed_left_rows.
         
concatenate `'` '' `'` into lv_fixed_right_rows.
         
concatenate `'` lv_row_id `'` into lv_row_ids.
         
concatenate `'` '' `'` into lv_marked_rows.
       
else.
         
concatenate lv_rows `,'` lv_html `'` into lv_rows.
         
concatenate lv_fixed_left_rows `,'` '' `'` into lv_fixed_left_rows.
         
concatenate lv_fixed_right_rows `,'` '' `'` into lv_fixed_right_rows.
         
concatenate lv_row_ids `,'` lv_row_id `'` into lv_row_ids.
         
concatenate lv_marked_rows `,'` '' `'` into lv_marked_rows.
       
endif.
     
endwhile.
 
     
concatenate `{ "rows": [ ` lv_rows ` ],  "fixedLeftRows": [ ` lv_fixed_left_rows

               ` ], "fixedRightRows": [ ` lv_fixed_right_rows ` ], "markedRows": [ ` lv_marked_rows

               ` ],  "tableId": [ '` iv_table_id `' ], "rowIds": [ ` lv_row_ids ` ]}`

          into ev_html.
 
   
endmethod.

 

In the method FILL_CONTEXT_NODE we just populate our context node.

 

  method fill_context_node.
 
     
data: lt_but000      type table of but000.
     
data: ls_but000      type but000.
     
data: lr_wrapper     type ref to cl_bsp_wd_collection_wrapper.
     
data: lr_valuenode   type ref to cl_bsp_wd_value_node.
     
data: lr_table_line  type ref to but000.
 
      lr_wrapper
= typed_context->somedata->get_collection_wrapper( ).
     
if lr_wrapper is bound.
 
        lr_wrapper
->clear( ).
 
       
select * from but000 into table lt_but000 up to 2 rows
         
where type = 1
           
and bu_sort1 = 'DMITRY'.
 
       
loop at lt_but000 into ls_but000.
 
         
create data lr_table_line.
 
         
create object lr_valuenode
           
exporting
              iv_data_ref
= lr_table_line.
 
          lr_valuenode
->set_properties( ls_but000 ).
 
          lr_wrapper
->add( lr_valuenode ).
 
       
endloop.
 
     
endif.
 
      typed_context
->somedata->build_table( ).
 
   
endmethod.

 

How does it look like:

 

image014.png

 

3 seconds after…

 

image015.png

 

 

Note that all your GET-methods are taken into consideration and you can navigate normally as you would do it in the normal WebUI table.

 

HTTPWatch trace:

 

image016.png

 

It takes only 300 ms to build a page and 3 seconds later the table is updated.  

Parallelization in Web UI - Part 3

$
0
0

Parallelization in Web UI

PART 3


AJAX (… continued)

 

 

Limits of a stateful application

Now let us place two samples from above onto the same page:

 

image017.png                        

 

 

You will notice down that the response time is significantly higher than before. In the HTTPWatch trace you will see that both AJAX calls are triggered simultaneously, but the first is finished within about 3 seconds, and the second one took significantly longer.

 

image018.png

 

This is caused by increased wait time. As long as our application is totally stateful, we are trying to access the same user context. In case of several requests, they have to wait till the context is unlocked with one work process, i.e. when the current task is finished.

 

image019.png

 

On SAP instance level we see that our session is waiting for a certain work process to be released.


image020.png   image021.png

     

This is a main limitation of stateful SAP applications. Stateful requests cannot be processed in parallel, even if they operate with different objects. Whenever the request is processed by Dialog Work Process (DIA), the whole user context get’s locked.  Considering this limitation, by implementing AJAX calls in stateful SAP applications, you can increase usability of your solution, but hardly performance (unless you are using just one call). 

 

 

 

 

1.     aRFC or Strength of Value Nodes


The last topic that I would like to review here is how asynchronous RFC can help you to speed up processing of requests from WebUI. You may wish to use this very old technique in order to speed up opening of the overview pages or customer fact sheets, or similar applications. In the standard, this technique is not used by intention and therefore you need to implement it on your own. Furthermore the standard, in most of the cases, is using Model Nodes, which provide a continuous processing from UI/BSP level down to GENIL/API level, and it’s not possible to break down this program execution flow easily.

 

From my prospective there are two ways how or where the parallelization through asynchronous RFC can be implemented:

 

The first way is to parallelize processing on GENIL/API level for each individual object. In this case you need to redefine GENIL implementation classes up to your needs. But even though it’s not always possible due to limitations of API functionality. So finally this approach does not look very attractive, however may still exist.

 

The second way is to make a parallelization through asynchronous RFC on WEBUI level, e.g. on the level of assignment blocks. However in this case we need to break down the standard processing flow mentioned above. This cannot be easily done for Model Nodes, but the Value Nodes do not have such limitation as they just need the data at certain time and certain place (e.g. ON_NEW_FOCUS), and they do not care how this data is retrieved.

 

Below we will review such a case, based on the partner overview page: BP_HEAD/BPHEADOverview.

 

Below two diagrams will help you to understand how the concept can be implemented and what is the difference comparing with the standard processing.

 

Standard program flow

 

image022.png

 

Processing of the overview page stats from DO_REQUEST and is followed by processing of individual views sequentially in the loop (BIND_VIEW). When the context of the view gets initialized normally ON_NEW_FOCUS event is triggered and at this point the data will be read for each context node that is assigned.

 

Program flow implementing asynchronous RFC processing

 

image023.png

 

One can see that the general timeline is shorter due to asynchronous RFC retrieving the data, executed in parallel. This of course requires more system resources, primarily work processes.

Processing of the overview page starts from DO_REQUEST, however at certain point, we split data extraction for our assignment blocks or context nodes. At this point you need to read the personalization to see which assignment blocks are opened and will be processed. It can be done in the method DO_CONFIG_DETERMINATION of your overview page. To get the assigned blocks, you can use the following coding.

 

        " get configuration in xml language
          me
->configuration_descr->get_config_data(
            receiving  rv_result
= lv_config_xml
           
exceptions foreign_lock= 1
                       config_not_found
= 2 ).
         
ifsy-subrc= 2.
           
exit.
         
endif.
 
         
" transform configuration xml into table definition
         
call method cl_bsp_dlc_config_ovw=>conv_xml_to_data(
           
exporting
              iv_xml 
= lv_config_xml
           
importing
              rt_data
= ls_assgnm_xml).
 
         
" get relevant data from config XML
          lt_view_area
= ls_assgnm_xml-views.

 

Once you know the assignment blocks, you should launch respective functions asynchronously. It is worth to write a general helper class in order to manage this process. You can also develop some customizing tables for your needs.

 

        data: lr_rfc_man    type ref to zdmsh_ab_rfc_manager.
 

          
" Get RFC Manager
          lr_rfc_man
= zdmsh_ab_rfc_manager=>get_instance( ).
 
         
iflr_rfc_manis bound and
             lr_partner
is bound.
 
           
" Set Current Partner
            lr_rfc_man
->set_partner( lr_partner).
 
           
" Register Necessary Blocks
            lr_rfc_man
->process_viewarea( it_viewarea = lt_view_area ).
 
           
" StartFunction
            lr_rfc_man
->start_functions( ).
 
 

        endif.

 

After this the process continues as normal.

Later at each relevant ON_NEW_FOCUS event you need to get the results of the executed functions and provide it to the respective context nodes.

 


 
     
data: lr_rfc_man     type ref to zdmsh_ab_rfc_manager.
     
data: lt_c_hist      type crmt_bupa_il_changedoc_t.
     
data: ls_c_hist      type crmt_bupa_il_changedoc.
     
data: lr_table_line  type ref to crmt_bupa_il_changedoc.
     
data: lr_wrapper     type ref to cl_bsp_wd_collection_wrapper.
     
data: lr_valuenode   type ref to cl_bsp_wd_value_node.
 
 
     
" Get RFC Manager
      lr_rfc_man
= zdmsh_ab_rfc_manager=>get_instance( ).
 
     
" Wait Till Our Block Is Done
      lr_rfc_man
->wait_for_task( iv_application= 'ZDMSH_RFC_TEST'
                                 iv_view       
= 'ChangeHistory' ).
 
     
" Get Data
     
call method lr_rfc_man->get_change_history
       
importing
          et_change_history
= lt_c_hist.
 
     
" Fill Context Node
      lr_wrapper
= get_collection_wrapper( ).
 
     
iflr_wrapperis bound.
 
        lr_wrapper
->clear( ).
 
       
loop at lt_c_histinto ls_c_hist.
 
         
create data lr_table_line.
 
         
createobject lr_valuenode
           
exporting
              iv_data_ref
= lr_table_line.
 
          lr_valuenode
->set_properties(ls_c_hist).
          lr_wrapper
->add(lr_valuenode).
       
endloop.
     
endif.

 

In my sample I selected two most expensive assignment blocks: Interaction History and Change History, and executed a data retrieval for them in parallel. 

 

image024.png

 

Note that we are not changing the logic of WEBUI framework and therefore all your GET-methods, iterators, events and navigation links will work as usual.

 

 

 

 


How to debug a modal dialog

$
0
0

As introduced in http://scn.sap.com/community/crm/webclient-ui-framework/blog/2015/12/26/logic-for-f4-help--1, F4 input help shows help values in a modal dialog. Without special tools, It is not possible to set breakpoint and make JS debug from this popped up modal dialog. In this document, I will show you how to debug using a workaround.

 

  1. Firstly, we need to get the URL link for requesting help values: make a httpwatch trace when clicking F4 help icon.
  2. From the httpwatch trace, find the line for 'CRM_THTMLB_UTIL/f4_help2.do' request. Right click and select menu 'Copy https://....'.
    0 to get the url.PNG
  3. Paste the url into another IE tab and Enter, a help values will be shown as a normal page.
    1 open the url in another tab.PNG
  4. Click F12, Developer tool will be launched. We can search 'thtmlbF4PickRow' and set a JS breakpoint.
    find and set bk in thtmlbF4PickRow.PNG
  5. Then select any entry, the JS bk will stop.
    JS when select one line -1.PNG
  6. If I select the 2nd entry, it will go into function dynshlp_sel_2.
    JS when select one line -2.PNG
  7. Inside function SetInput, thtmlbF4SetInput is called. Notice the callstack and the values for SelValue and keyInputfieldId.
    JS when select one line -3.PNG
  8. In function thtmlbF4SetInput, win is 'undefined'. This is because this value help screen has no parent window.
    JS when select one line -4 win is undefine.PNG
  9. In order to get through more codes, we make a change for 'win' manually -- input any string to make 'win' a defined object.
    JS when select one line -5 win is changed.PNG
  10. Here is the farthest place where we can debug. We cannot debug further because our 'win' is just a string object instead of a window object. But from here we can see 'visible' is object for 'C35_W162_V163_marketing_classific_text' which is the 'rating description' input field.
    JS when select one line -6 2 get text field element .PNG
  11. This is the place to set value.
    JS when select one line -6 3 set text field element .PNG

logics for F4 input help --- 1

$
0
0

Introduction:


F4 help is a very important function for WebUI input fields. Normally, when clicking ‘F4’ key or clicking on the icon attached to an input field, a dialog will pop up with all help values in an table. Once one of the entry is selected, the dialog will be closed, the selected value will be returned and filled into the input field. Even sometimes the description of the value will also be filled into the description field automatically.

 

Following are two samples (samples are made in CRM7.0 EHP3 with IE11 environment ):

Sample 1: For search field ‘Sales Org. ID’ in sales order search screen

 

When clicking the F4 help icon, the dialog pops up with all values for sales orgs:

0-2 Value help for field on saved search.PNG

Select an entry, the value will be filled into the search field:

0-3 Value help for field on saved search.PNG

Here is GET_V method for ‘Sales Org. ID’

1-get_v for sales org.PNG

Sample 2: For input field ‘Rating’ in ‘corporate account’ creation screen

 

When clicking the F4 help icon, the dialog pops up with all values for rating:

0-5 value help for field on a form.PNG

Select an entry, the rating field and its description field are filled automatically together:

0-6 value help for field on a form.PNG

Here is GET_V method for ‘Rating’

1-get_v for rate.PNG

Further more:

1-get_v for rate 2.PNG

 

I make above two samples together because they have three common points (even though there are some differences in the GET_V methods):

  1. Seen from httpwatch traces, they both have following requests :
    .../sap/bc/bsp/sap/crm_thtmlb_util/F4Frameset.html...
    .../sap/bc/bsp/sap/crm_thtmlb_util/f4_help2.do...
  2. In the pop up dialog, when selecting an entry, the value will be returned and set into input field. But there is no roundtrip event for the ‘select’ and 'set' actions. The selected values are set into input fields by JavaScript only.
    *For fields like 'rating’, when select an entry from the popup dialog, there is request sap/webcuif/uif_callback sent back to server which triggers a roundtrip. But it is not for the ‘select’ and ‘set' actions. It is for ‘Smart Value Help’ function. Please refer to the 'Additional' part for information regarding where this request is sent.
  3. For the request '.../sap/bc/bsp/sap/crm_thtmlb_util/f4_help2.do...', in the returned content, there are Javascript functions like  ‘function dynshlp_sel_1 () {SetInput….}’. When an entry is selected, the corresponding function will be called to parse parameters and set values.
    function dynshlp_sel_----new.png

 

In this blog, logics for this kind of F4 help will be introduced with above samples.

 

The process regarding how the dialog pops up (use ‘Sales Org. ID’ field as sample):

  1. Javascript for ‘onclick’ event for F4 help icon:
    When the input field is being rendered, since value of help_id is assigned, onclick function for clicking F4 icon will be formed in method CL_THTMLB_INPUTFIELD-> GENERATE_HELP_ID_CONTENT.

    The value for ME->ONVALUEHELP is:
    'thtmlbF4Help(null,null,null,document.getElementById('C34_W115_V116_V117_btqslsord_parameters[1].VALUE1'),null,'\x28CL_CRM_UIU_ORGSET_SLS_VH\x29','','','','C34_W115_V116_V117','','','')'
    9-1 to form JS codes for click F4 icon.PNG
  2. Now let’s see the html source code for this F4 icon:
    The whole codes are:
    <a title="Open Input Help" class="th-ip-h" id="C34_W115_V116_V117_btqslsord_parameters[1].VALUE1-btn" onclick="thtmlb_hideContextMenu(event);thtmlbF4Help(null,null,null,document.getElementById('C34_W115_V116_V117_btqslsord_parameters[1].VALUE1'),null,'\x28CL_CRM_UIU_ORGSET_SLS_VH\x29','','','','C34_W115_V116_V117','','','');" onfocus="thtmlb_InputIconFocus(this);thtmlb_hideContextMenu();" href="javascript:void(0)">
    9-2 JS source code for the F4 icon.PNG
  3. Source codes for function thtmlbF4Help locates in BSP Application THTMLB_SCRIPTS-> scripts_inputfield.js. It will be called when clicking on the F4 help icon:
    9-3 onclick method is called.PNG
  4. Inside function thtmlbF4Help, function thtmlbF4OpenModalWindow is called. While inside thtmlbF4OpenModalWindow, a modal dialog will be open. Html for this modal dialog comes from BSP application crm_thtmlb_util-> F4Frameset.html F4 Framese.

    value for modal_url is:
    "/sap(ZT1TVEF6TXpVME1WOWZYMTlmTkROZk1qTTRBRkJXckViRUh1V3EtUzZvbFd6ZXVnPT0=)/bc/bsp/sap/crm_thtmlb_util/f4_help2.do?HelpMode=ValueTable&fake=130953&KeyInputFieldId=C34_W115_V116_V117_btqslsord_parameters[1].VALUE1&HelpId=(CL_CRM_UIU_ORGSET_SLS_VH)&PageId=C34_W115_V116_V117&F4Level=1&HelpIdString=&TriggerSubmit=&helpType=&prefilteringValue=&F4Title=&OutFields="

    value for modal_frameset is:
    "/sap/bc/bsp/sap/crm_thtmlb_util/F4Frameset.html"
    9-4 window showmodaldialog is called.PNG
  5. Reading the source codes inside F4Frameset.html, we can see that modal_url is opened inside iframe "f4modalframe":
    9-5 F4frameset.html..PNG
  6. If we click F4 help icon of an input field, dialog pops up. From httpwatch trace, we can see the two requests clearly.
    9-6 httpwatch trace requests.PNG
  7. It is request crm_thtmlb_util/f4_help2.do which fetches all values actually. For f4_help2, the controller class is CL_THTMLB_F4HELP:
    9-7 controller class for f4_help2.do.PNG
  8. In method CL_THTMLB_F4HELP-> DO_REQUEST, parameter will be parsed, data will be retrieved in designed logics and then rendered as formatted in CRM_THTMLB_UTIL-> F4Help.htm.
    9-8 cl_thtmlb_f4help-do request.PNG
  9. Of course, the returned data will still be displayed in the same modaldialog.
    9-9 data in modal dialog.PNG

 

The process regarding setting values when select an entry from the modal dialog(use ‘rating’ field as sample):

  1. As we have talked previously, the data is rendered as formatted in CRM_THTMLB_UTIL-> F4Help.htm.
    9-9-1 thtmlbcellerator in F4helphtml.PNG
  2. From ‘controller->gr_iterator’, we can see the functions for every help value which are generated dynamically.
    9-9-2 Dynamic script for gr_iterator.PNG
  3. The details are:
    9-9-3 Dynamic script for gr_iterator.PNG
  4. From ‘controller->gr_iterator’, we can also see the help values.
    9-9-4 help values for gr_iterator.PNG
  5. The details are:
    9-9-5 help values for gr_iterator.PNG
  6. Because of line 'onClientRowSelection = "javascript:thtmlbF4PickRow(selectedRow)"', javascript function for selecting an entry is registered. The source codes are formed in method CL_THTMLB_CELLERATOR-> RENDER_TABLE.
    9-9-6-1 form the onselect method register.PNG
  7. These JS functions can be seen from httpwatch trace:
    9-9-7-1 dynamic JS functions in httpwatch trace.PNG

    9-9-7-2 selection register in httpwatch trace.PNG
  8. JS function ‘thtmlbF4PickRow’ locates in BSP Application THTMLB_SCRIPTS-> scripts_inputfield.js. It is to execute the corresponding JS functions like dynshlp_sel_2.
    9-9-7-3 thtmlbF4PickRow location.PNG
  9. JS function SetInput is called inside dynshlp_sel_2. SetInput locates in CRM_THTMLB_UTIL-> F4Help.htm. While JS function thtmlbF4SetInput is called inside SetInput which locates in THTMLB_SCRIPTS-> scripts_inputfield.js.
    9-9-7-4 thtmlbF4SetInputlocation.PNG
  10. In this thtmlbF4SetInput JS function, the selected values will be set to the field ‘Rating’ and its description field.
    9-9-8 set values.PNG

*As I have not found a way to debug from a modal dialog, debug details for JS function thtmlbF4SetInput are not possible. But there is workaround to make JS debug on modal dialog. As it is not a complete debug, I would like to introduce it in a separate document -- http://scn.sap.com/blogs-edit-post!default.jspa?blogPost=136263&blog=1082.

 

Additional:

As have said, there is no roundtrip triggered when selecting a help value and set this value to the input field. But if we select a help value from the dialog for 'rating', in httpwatch trace, we can find a ‘webcuif/uif_callback’ request sent out.

9-9-9-1 uif_callback.PNG

This request is for ‘Smart value help’ function, it is a delta handling request generated here:

9-9-9-2 SVH request.PNG

Accessing SAP CRM On-Premise from Mobile device

$
0
0

Accessing SAP CRM On-Premise from Mobile device

 

Recently I was working on a solution to access SAP CRM On-Premise System from Mobile Device such as iPad or iPhone, and I would like to share the solution to our community for further use. This solution was done in iOS device but for sure it is working in Android devices also.

 

Requirement

Customer has On-Premise system and they would like to access from mobile device but do not want to invest further in mobile client development, as they want to access Order or Activity for minimal use.

 

Solution:

I have seen SAP C4C (Cloud for Customer) App and wanted to give a flavor like that with minimal effort. So the thought process went for solution Step-By-Step

 

Connect to client network:

In our example client system can be connected to through Cisco VPN or Cisco Any client software. For VPN connectivity Apple device has pre-installed

 

VPN profiles, which can be found in General -> VPN -> Add configuration....IKEv2 is correct VPN profile for Cisco VPN

IMG_1024.jpg

username and password can be stored or given at access time

 

 

 

 

 

 

 

 




For Cisco Any Client, it should be downloaded from App Store and configuration should be manually added :

IMG_1025.jpg


Accessing Log-On URL:

URL can be manually typed into browser or opened through a link, and after successful network connection log-on page should be available for credentials. After normal credentials check the whole SAP CRM functionality is available at your hand.

 

 

Apps-like feeling

To give fine touch solution we can use functionality to create a so-called 'App' using: downloadCapture download.PNG -> add to Home Screen Capture again.PNG

 

Summary:

It is not alternative for mobile client or cloud based C4C as they have another purpose and also the screen does not adjust to device, but it is very cost effective way if client uses would like to use On-Premise System in 'on-field' without investing too much.

 

Please keep sharing your thoughts and comments.

SAP CRM web UI - session time out warning message

$
0
0

End user /Agents working on web UI with any of the transactions or pulling up business partner some time if they leave the screen idle will lead to time out hence losing of the data or need to rework hence this functionality will help to overcome this scenario.

 

We need to implement SAP NOTE – 2119701 which has again following pre-requisite notes needs to taken care 2092893, 2096030, 2097954, 2098586


1.Below screen shot shows the “times out limit” option available on the right hand side top corner before “Personalize” option.


SAP CRM1.png

2.Screen shot below shows the small window popping up as “Session Time Out Warning”

 

SAP CRM2.png

3.Once the time limit reached system ask for option “Restart” or “Offline Access”

Restart Option – Will reload the page to Business roles selection screen

Offline Access – Will keep alive on the same page

 

SAP CRM3.png

New update about Microsoft IE browser support

$
0
0

As you may be already aware of that there is big change for Internet Explorer support which had been already announced by Microsoft. Please read Microsoft page

Support for older versions of Internet Explorer ended on January 12th, 2016.

 

 

SAP WebCUIF cannot support browser versions that vendors no more support themselves. The corresponding SAP Note 1746385 - Main WEBCUIF Browser Support Notehas been updated. Following is an abstract from this note, but you should always check and follow SAP Note 1746385 which will be updated timely.

 

 

===>

WEBCUIF has to support different browser versions, as those come and go.

 

 

SAP WebCUIF cannot support browser versions that vendors no more support themselves.

 

Along that line, SAP recommendations follow those of Microsoft: on January 12th 2016, Microsoft recommends for security purposes that the latest version of IE supported by each platform to be run. From http://blogs.msdn.com/b/ie/archive/2014/08/07/stay-up-to-date-with-internet-explorer.aspx :

 

Beginning January 12, 2016, the following operating systems and browser version combinations will be supported:

      Windows Platform                  Internet Explorer Version

      Windows Vista SP2                 Internet Explorer 9

      Windows Server 2008 SP2      Internet Explorer 9

      Windows 7 SP1                      Internet Explorer 11

      Windows Server 2008 R2 SP1  Internet Explorer 11

      Windows 8.1                          Internet Explorer 11

      Windows Server 2012             Internet Explorer 10 

      Windows Server 2012 R2        Internet Explorer 11

 

After January 12, 2016, only the most recent version of Internet Explorer available for a supported operating system will receive technical support and security updates. For example, customers using Internet Explorer 8, Internet Explorer 9, or Internet Explorer 10 on Windows 7 SP1 should migrate to Internet Explorer 11 to continue receiving security updates and technical support."

 

 

●As soon as a vendor releases a new browser version, SAP WebCUIF commits to fix any issue as soon as possible, via Notes when possible, and SPs for complete solutions.

===>

 

Additionally, Please also check following blog about how to find if Safari and Google Chrome are supported or not by CRM Interaction Center.

How to find which Web Browsers are supported by SAP CRM Interaction Center?

 

 

Best Regards,

Bruce

Deletion of duplicate products from the SAP CRM / web UI :-

$
0
0

Problem :

 

There might be a scenario when an end user searches for product and found 2 similar records under result list

SAP CRM.png

 

SAP CRM Web client UI view :

SAP CRM1.png


We need to use 2 programs in order to delete the duplicate products from SAP CRM database.


Solution :

 

COMC_PR_TOOL_REG :

In this program we will maintain user name and program name details.some times due to client copy same products will be available twice need to take call and delete appropriate one.

SAP CRM2.png

 

COM_PRODUCT_DELETE_SINGLE :

 

In this program we specify the product,product type,logical system  needs to deleted

SAP CRM3.png


SAP CRM WEB UI :


Cross check if only a single product is visible under result list

SAP CRM5.png

Please let me know if you have any other points to be added on top of this piece of work,Thank you!

Update WebUI view fields via AJAX

$
0
0

 

 

Update view fields via AJAX call

 

Use Case

When doing some applications one may need to update certain fields on the screen without calling a back-end. In this case, the use of AJAX functionality of CRM WebUI can be useful. In this document, we will describe how one can update all the configured fields on the view.

Let us assume we have a view with Order Summary information, including Gross Value, Net Value, Tax Amount, Number of Items and Gross Weight.

image001.png


On BSP level, it look like below:

<%@page language="abap" %>
 
<%@extension name="chtmlb" prefix="chtmlb" %>
 
<%@extension name="thtmlb" prefix="thtmlb" %>
 
  <chtmlb:config displayMode =
"FALSE"
                 mode        =
"RUNTIME" />
 
 

 

Required JavaScript

As described before (see: http://scn.sap.com/community/crm/webclient-ui-framework/blog/2015/12/11/parallelization-in-web-ui--part2), we need a function which calls an AJAX request and the functionality which will be called after the request is executed, i.e. a callback function.

As before, we will be using a standard method thtmlbAJAXCall.callBackendin order to call a back-end handler.

<script language="javascript" type="text/javascript">
 
  function UpdateTotals()
  {
 
varajax_url = "<%= controller->create_ajax_url( ) %>";
  thtmlbAJAXCall.callBackend(ajax_url,UpdateTotalsCallback);
  }


  </script>

 

In this case, we will use our own callback function, which will be expecting the data in JSON format. Basically it expects a table with two columns: NAME and VALUE. NAME will be a unique id of the field (e.g. C18_W65_V67_V69_btcumulath_struct.gross_value) and VALUE is the text, which needs to be placed into the field.

<script language="javascript" type="text/javascript">
 
  function UpdateTotalsCallback(reqObj)
  {
  
varresponseText = reqObj.request.responseText;
  
varobj = JSON.parse(responseText);
  
varfnum = obj.TEXT.length;
  
for(vari= 0; i< fnum; i++) {
   
varelem = document.getElementById(obj.TEXT[i].NAME);
     if (elem){
      elem.title = obj.TEXT[i].VALUE;
      elem.value = obj.TEXT[i].VALUE;
     }
   }
  }
 
 

</script>

 

Required ABAP code

For consistency we also place a full ABAP code, including the one needed to generate the AJAX URL.

methodcreate_ajax_url.
 
   
data: lr_class_desc    type ref to cl_abap_typedescr.
   
data: lv_class_name    type string.
 
    lr_class_desc
= cl_abap_classdescr=>describe_by_object_ref(me ).
    lv_class_name
= lr_class_desc->get_relative_name( ).
 
   
call method cl_crm_web_utility=>create_service_url
     
exporting
        iv_handler_class_name
= lv_class_name
        iv_controller_id     
= me->component_id
      receiving
        ev_url               
= rv_url.
 
 
endmethod.

 

Below you can find a sample of callback handler.

  methodif_crm_web_callback~handle_request.
 
     
data: lr_controller      type ref to zl_zdmsh_pr_totals_impl.
     
data: lr_model           type ref to if_bsp_model_binding.
     
data: lr_context_node    type ref to cl_bsp_wd_context_node.
     
data: lr_descr           type ref to if_bsp_dlc_view_descriptor.
     
data: lv_xml             type bsp_dlc_xml.
     
data: lt_bsp_fields      type bsp_dlct_adv_conf_itm.
     
data: lt_field_list      type table of crmt_rtd_string_pair.
 
     
data: lv_node            type string.
     
data: lv_attribute_path  type string.
 
     
data: lr_writer          type ref to cl_sxml_string_writer.
     
data: lv_json            type xstring.
 
 
     
field-symbols:
        <fs_bsp_field
type bsp_dlcs_adv_conf_itm,
        <fs_field_list>
type crmt_rtd_string_pair.
 
 
     
checkir_controlleris bound.
 
     
try.
          lr_controller ?= ir_controller
.
       
catchcx_root.
     
endtry.
 
     
checklr_controlleris bound.
 
     
" Get Personalization
      lr_descr
= lr_controller->configuration_descr->get_property_descriptor( ).
      lv_xml  
= lr_controller->configuration_descr->get_config_data( ).
 
     
" Get Available BSP Fields
     
call method cl_bsp_dlc_config_util=>adv_conf_meta_to_table
       
exporting
          ir_view_descr       
= lr_descr
          iv_adv_conf_meta_xml
= lv_xml
       
importing
          et_adv_conf         
= lt_bsp_fields.
 
     
" Loop At BSP Fields
     
loop at lt_bsp_fields assigning <fs_bsp_field> where type = 'FIELD'.
 
       
" Get Model Data
       
call method cl_bsp_model=>if_bsp_model_util~split_binding_expression
         
exporting
            binding_expression
= <fs_bsp_field>-field_name
         
importing
            attribute_path    
= lv_attribute_path      " Property
            model_name        
= lv_node.               " Context Node
 
       
try.
           
" Get BSP Model
            lr_model ?= ir_controller
->get_model( lv_node).
 
           
" Check Context Node
            lr_context_node  ?= lr_model
.
 
           
append initial line to lt_field_list assigning <fs_field_list>.
 
            <fs_field_list>
-name  =lr_model->get_attribute_name( lv_attribute_path ).
            <fs_field_list>
-value = lr_model->get_attribute(attribute_path = lv_attribute_path ).
 
         
catch: cx_root.
           
continue.
       
endtry.
 
     
endloop.
 
     
"ABAP to JSON
      lr_writer
= cl_sxml_string_writer=>create(type = if_sxml=>co_xt_json ).
     
calltransformation id source text = lt_field_list[] result xml lr_writer.
      lv_json
= lr_writer->get_output( ).
 
 
     
" Set Response
      ir_server
->response->set_data( lv_json).
      ir_server
->response->set_header_field(name  = 'content-type'
                                            
value = 'text/xml' ).
 
 
   
endmethod.

 

One can note that our coding (we are talking about ABAP and JavaScript together) is very generic. It does not depend on the selected model and context nodes. It is also performance and logically optimized, as we are processing only the fields, which are present on the screen (as per WebUI configuration), and not the possible ones.

Hope it can help someone to build modern usability- and performance- optimized applications in WebUI.


Why does the navigation after clicking email hyperlink not happen in Account search result

$
0
0

Issue description

 

In Account search result view, by clicking the email hyperlink,

clipboard1.png


we expected that there is a navigation to email creation view:

clipboard2.png

However in some system, after clicking email hyperlink, nothing happens.

 

Issue Analysis

 

1. Select one row in search result, click F2 to review technical information, and get the UI component name BP_HEAD_SEARCH.

 

2. Open the component via tcode BSP_WD_CMPWB, check the email hyperlink implementation by double clicking GET_P_EMAIL

clipboard3.png

3. From the p getter implementation, we get to know the logic that once it is clicked, the event handler and the outbound plug TOEMAIL will be triggered.

clipboard4.png

4. Then we can double click on OP_TOEMAIL and set breakpoint there to start debug.

clipboard5.png

I launch two debuggers separately in system AG3 ( where navigation works ) and K9E ( where navigation fails ) to compare the execution logic.

 

4.1 The navigation execution in K9E terminates due to the failure of check in line 37. So I have to check why lt_proc is empty after line 34.

clipboard6.png


4.2 When I debug into the method in 4.1, it is because is_navigation_supported returns false for process type 0005.

clipboard7.png

4.3 Within method IS_NAVIGATION_SUPPORTED, first the UI object type is determined in method 38.

clipboard8.png

In system AG3, the result is BT126_MAIL, and for K9E it is CRM_ICM_EMAIL. This difference is caused by the different business switch status as displayed in line 183 and 184 below.

clipboard9.png

The reason why the navigation works in AG3: it successfully finds a navigation target from internal table gt_map_infos for UI object type BT126_MAIL.

clipboard10.png

The found navigation target:

clipboard11.png

This target is maintained in customizing below:

clipboard12.png

clipboard13.png

But in system K9E, the different UI object type CRM_ICM_EMAIL is used, and the corresponding customizing for its navigation target is missing, so the navigation does not work.

clipboard14.png

After the necessary navigation customizing is added, the navigation in K9E also works.

Create View on CRM Home Page

$
0
0

SAP CRM Web UI has information on the Home Page itself so that  specific information for logged in useris displayed on the Home Page. This information includes Today's Appointments, Tasks, Workflow Tasks etc.

 

Objective of this Blog is to lay down the steps to configure a similar View on Home Page. One of the case Example we can take is to Create a View on Home Page to Show all the Opportunities for which Logged in user is part of Sales team.

 

Components : CRMCMP_GS_WC ( Component for Work Center). This component contains standard Home Page views as well.

                         WCC_SLS_HOME  - Component for Home Page. This Window encapsulates all the views of Component CRMCMP_GS_WC.

 

STEPS:

  • Create Custom View in CRMCMP_GS_WC.

        Since we are planning to Read/Display the Opportunities in this view, the Model Entity for the context Node of View would be the Result of Query Object -

          BTQROpp.

        The View should be of 'Table View' Type.

 

  • Query the Opportunities (based on Pre-determined conditions) to Display.

          -     Create a Custom Method GET_OPP in Impl Class of View to Query the Opportunities. The logic to obtain the required data is written in this                               method. In this scenario, the opportunities where logged in user is entitled as employee responsible.

               Implement the Below Logic to Query Opportunities.

                              

Logic

                      lr_query_service = cl_crm_bol_dquery_service=>get_instance( 'BTQOpp' ).

                    * Get BP of Logged in User                

                         CALL FUNCTION 'COM_BPUS_BUPADATA_FOR_USER_GET'

                             EXPORTING

                               is_username        =    sy-uname

                             IMPORTING

                               ev_businesspartner = lv_login_user

 

                           If lv_login_user is not initial.

                               lv_query_service->add_selection_param( iv_attr_name = 'EMPLOYEE_RESP'  iv_sign = 'I' iv_option = 'EQ' iv_low = lv_low )

 

                        * Global variable for Collection of results gr_col

                         If lr_query_service IS BOUND.

                             gr_col =  lr_query_service->get_query_result( ).

                         Endif.

 

  • This method implemented to Query the results can be used on initiation of view. We are redefining DO_INIT_CONTEXT & calling custom method GET_OPP in this method to fetch the collection.

          lr_col = get_query_result( ).

          me->typedcontext->BTQROPP->collection_wrapper->set_collection( lr_col ).


  • Change View Layout of HTM page

         

HTM Page

<%

  data: lv_xml    type string.

  lv_xml    = controller->CONFIGURATION_DESCR->GET_CONFIG_DATA( ).

%>

 

<span class="appTab">

  <chtmlb:configCellerator  xml="<%= lv_xml %>"

                       design                = "TRANSPARENT"

                       id="ResTable"

                       onRowSelection="select"

                       table="//BTQROPP/Table"

                       horizontalScrolling="false"

                       actionsMaxInRow="5"

                       fillUpEmptyRows =" "

                       selectedRowIndex="<%=BTQROPP->SELECTED_INDEX%>"

                       selectedRowIndexTable="<%=BTQROPP->SELECTION_TAB%>"

                       selectionMode="SINGLE"

                       usage = "HOMEPAGE"

                       editMode = "NONE"

                       visibleRowCount = "10" />

</span>


  • Create a Window for displaying Custom View.

          Create Window using Wizard to encapsulate the custom view.

 

  • Redefine Get P Method for the field which is required to be used as hyperlink to navigate to other pages from home page.

          We are going to use Object ID field to be shown as hyperlink and generate the event on clicking.

 

  

Logic in Get P Method

Case iv_property.

             WHEN if_BSP_MODEL_SETTER_GETTER=>fp_fieldtype.

               rv_value = cl_bsp_dlc_view_descriptor=>field_type_event_link.

            WHEN if_BSP_MODEL_SETTER_GETTER=>fp_onclick.

               rv_value = 'OPP_OVP'.

 

  • Create Event in Custom view to navigate to Overview Page.

         Logic in Event OPP_OVP to Navigate to Overview page.

 

      

Event logic

* Get Event Index info

  cl_thtmlb_util=>get_event_info(

     exporting

       iv_event = htmlb_event_ex

     importing

       ev_index  = lv_index ).

 

 

* Feed Selected entity to outbound plug

  lr_current = me->typed_context->BTQROpp->collection_wrapper->find(

   CHECK lr_current is BOUND.

 

 

  CALL METHOD lr_current->get_property_as_value

     EXPORTING

       iv_attr_name = 'GUID'                              

     IMPORTING

       ev_result    = lv_guid.

 

 

  lr_core = cl_crm_bol_core=>get_instance( ).

 

 

  TRY.

      CALL METHOD lr_core->get_root_entity

        EXPORTING

          iv_object_name = 'BTOrder'                     

          iv_object_guid = lv_guid

        RECEIVING

          rv_result      = lr_entity.

    CATCH cx_crm_genil_model_error .

      RETURN.

  ENDTRY.

 

 

  create object lr_col.

  lr_collection->IF_BOL_BO_COL~ADD( lr_entity ).

  op_opp_ovp( lr_collection ).

 

  • Create Outbound Plug in Custom View.

        Call Window Outbound Plug and pass collection

        lr_window_ctr = me->get_window_controller( ).

        lr_window_ctr->call_outbound_plug( IV_OUTBOUND_PLUG   = 'OPP_OVP'

                                                                   IV_DATA_COLLECTION = IV_DATA_COLLECTION ).

 

  • Create an Outbound Plug in Custom Window as well.

          Write the below logic in O/B plug OPP_OVP in window.

          me->fire_outbound_plug( iv_outbound_plug = 'OPP_OVP'

                                                    iv_data_Collection = iv_data_collection ).

 

  • Go to Run time repository of CRMCMP_GS_WC component  & Edit

          - Add View to window

               Add Custom View to Newly created window. Also attach the O/B plug.

 

          - Add Component Interface

               Add Component interface for custom window. Also attach O/B plug as well.


  • Enhance component WCC_SLS_HOME in appropriate enhancement set

          Component WCC_SLS_HOME is the actual window for home page. So it should also be configured for the changes.

     -     Add Component Usage

          Component usage of custom view is to be added in the component WCC_SLS_HOME


     -     Create Outbound Plug in the component usage.

               Delegate Outbound plug to Window Outboung plug UIU_APPLICATION.


     -     Add Custom view in Viewset of WCC_SLS_HOME.


     -     Change UI Configuration of WCC_SLS_HOME to accomodate the new view.

               This will govern the positioning of the custom window on home page.


  • Change UI configuration of CRMCMP_GS_WC to control view display.

 

These steps should be a complete set of activities so that a custom view can be exposed on Home Page. We can always vary the changes based on requirement as to which Business Object or View type is to be displayed.

Sorting based on Custom Order

$
0
0

Objective: The need of enhancement is to Sort any field in BOL to perform Sorting with a Custom Order ( Not Alphabetically ).

 

Example Hypothetical Scenario:

               Custom Field (Customer Type) in Search Result View to be Sorted in an arbitrary order so that first Alphabets of description string has order as ( Direct > Internet Sales > Distributor ). This sort order is not alphabetical.

 

The sorting should be custom in nature & on the relevant field only.

 

STEPS:

  • In Implementation Class of View, Add interface IF_BOL_COL_SORTING.
  • Redefine DO_PREPARE_OUTPUT method of implementation class  of view & put the logic as

          me->typed_context->customers->collection_wrapper->sort(

                                                                                                              exporting         iv_attr_name       =   'DESCRIPTION'

                                                                                                                                       iv_sort_callback  =   me

                                                                                                                                       iv_sort_order       =   'D' ).

 

  • Method IF_BOL_COL_STRING~IS_A_GREATER_B

          Because of interface attached to class, a new method is now available i.e,

          IF_BOL_COL_SORTING~IS_A_GREATER_B. This method takes the two values IV_A & IV_B to compare & return rv_result as ‘X if IV_A is greater than B else  ‘  ‘.

 

  • Sorting

          Within this method we can use Simple Application of Bubble sort Algorithm to Sort in a custom order.


Screen Shot 2016-02-25 at 6.45.23 AM.png



 

Logic

  if value1 = 'Direct'.

 

    case value2.

 

      when 'Direct'.

 

        if lv_bu_partner2 > lv_bu_partner1.

 

          rv_result = ' '.

 

        else.

 

          rv_result = 'X'.

 

        endif.

 

      when 'Internet Sales'.

 

        rv_result = 'X'.

 

      when 'Distributor'.

 

        rv_result = 'X'.

 

      when 'Archive'.

 

        rv_result = ' '.

 

      when others.

 

        rv_result = 'X'.

 

    endcase.

 

  endif.

 

  if value1 = 'Internet Sales'.

 

    case value2.

 

      when 'Direct'.

 

        rv_result = ' '.

 

      when 'Internet Sales'.

 

        if lv_bu_partner2 > lv_bu_partner1.

 

          rv_result = ' '.

 

        else.

 

          rv_result = ' '.

 

        endif.

 

      when 'Distributor'.

 

        rv_result = 'X'.

 

      when 'Archive'.

 

        rv_result = ' '.

 

      when others.

 

        rv_result = 'X'.

 

    endcase.

 

  endif.

 

  if value1 = 'Distributor'.

 

    case value2.

 

      when 'Direct'.

 

        rv_result = ' '.

 

      when 'Internet Sales'.

 

        rv_result = ' '.

 

      when 'Distributor'.

 

        if lv_bu_partner2 > lv_bu_partner1.

 

          rv_result = ' '.

 

        else.

 

          rv_result = 'X'.

 

        endif.

 

      when 'Archive'.

 

        rv_result = ' '.

 

      when others.

 

        rv_result = 'X'.

 

    endcase.

 

  endif.



We can always Extend this Sorting solution for more fields or number if entries

Asynchronous Saved Search Fetch

$
0
0

Asynchronous Saved Search Fetch

 

I have noticed it could be a nice implementation variant for AJAX queries in WebUI. Everyone has it’s own saved search and everyone would like to know how many items are behind it. (Click a picture below to see how it's working ).

Saves_searches.gif

Implementation

 

Create Custom Controller

Saved searches are managed within GS_WC_QLINKS component. What we are going to need is a new custom-controller e.g. counter.do.

image003.png


This controller will be called stateless to calculate the number of entries behind each of the saved search.

image005.png

 

Perform AJAX request


The AJAX request is called right from the view GS_WC_QLINKS/savedsearches.htm.


<%@page language="abap" %>

<%@extension name="htmlb" prefix="htmlb"   %>

<%@extension name="xhtmlb"   prefix="xhtmlb" %>

<%@extension name="thtmlb" prefix="thtmlb"   %>

<%@extension name="crm_bsp_ic"   prefix="crmic" %>

<%@extension name="bsp" prefix="bsp"   %>

<%

  DATA  lr_ss_wrapper        TYPE REF TO cl_bsp_wd_collection_wrapper.

  DATA  lr_ss                TYPE REF TO cl_crm_bol_entity.

%>

<thtmlb:overflowContainerverticalScrollbar="NEVER" >

  <%

  data:   lv_search_id    type string.

  data:   lv_search_name     type string.

  lr_ss_wrapper  = savedsearches->get_collection_wrapper( ).

  lr_ss ?= lr_ss_wrapper->get_first( ).

  If lr_ss  is not bound.

  %>

   <img  class="th-clr-img" src="<%= CL_THTMLB_UTIL=>GET_DYN_ICON_URL(   EXPORTING iv_icon_name =   CL_THTMLB_UTIL=>GC_ICON_INFOMESSAGE iv_icon_type  = CL_THTMLB_UTIL=>GC_BUTTON_ICON ) %>" />&nbsp;

   <thtmlb:textView text = "<%= otr(CRM_UIU_BT/NORESULT)   %>" />

  <%

  Endif.

  while  lr_ss is bound.

  lv_search_id  = lr_ss->get_property_as_string(iv_attr_name = 'GUID' ).

  lv_search_name  = lr_ss->get_property_as_string(iv_attr_name = 'DESCRIPTION' ).

  %>

  <thtmlb:link id        = "<%= lv_search_id %>"

               onClick = "GO"

               onClientClick  = "thAdvSearchRegisterEvent('GO')"

               text    = "<%= lv_search_name  %>"

               tooltip = "<%= lv_search_name %>" />

  <%

  lr_ss ?= lr_ss_wrapper->get_next(  ).

  %>

  <br>

    <!-- -->

  <script language="javascript" type="text/javascript">

   thtmlbAJAXCall.callBackend("<%= controller->create_ajax_url(   iv_ss = lv_search_id )   %>",UpdateSavedSearchCallback);

  </script>

<!--   -->

  <%

  endwhile.

  %>

</thtmlb:overflowContainer>

 

Build Stateless AJAX URL


Saved search IDs are provided along with the AJAX URL, which is built as shown below.


  methodcreate_ajax_url.
 
     
data: lv_query     type string.
     
data: lv_url       type string.
 
     
" Build Query
     
concatenate lv_query 'crm_controller=' me->component_id into lv_query.
 
     
ifiv_ssis not initial.
       
concatenatelv_query'&saved_search=' iv_ssinto lv_query.
     
endif.
 
     
" Create State - Less URL
     
call method cl_crm_web_utility=>create_url
       
exporting
          iv_bsp_application
= me->application_name
          iv_bsp_page       
= 'counter.do'
          iv_query          
= lv_query
          iv_in_same_session
= abap_false
          iv_no_cookie      
= space
          iv_cache_busting  
= 'X'
        receiving
          ev_url            
= lv_url.
 
     
splitlv_urlat '?' into lv_urllv_query.
 
     
call method runtime->get_url_stateless
       
exporting
          application_name
= me->application_name
          page_name       
= 'counter.do'
        receiving
          url             
= lv_url.
 
     
concatenatelv_url'?' lv_queryinto rv_url.
 
   
endmethod.

 

Process AJAX Requests on the backend

The requests will be processed by our custom controller. There we need two methods: DO_REQUEST and GET_COUNT.

image007.jpg


DO_REQUEST:


  methoddo_request.
 
     
call method super->do_request.
 
     
" Data
     
data: lv_controller_id    type string.
     
data: lv_saved_search     type string.
     
data: lv_key              type guid_32.
     
data: lr_core             type ref to cl_crm_bol_core.
     
data: lr_shortcut_man     type ref to cl_crm_shortcut_manager.
     
data: lr_shortcut         type ref to cl_crm_bol_entity.
     
data: lt_field_list       type table of crmt_rtd_string_pair.
     
data: ls_field_list       type crmt_rtd_string_pair.
     
data: ls_count            type string.
 
     
" Containers
     
data: lr_writer           type ref to cl_sxml_string_writer.
     
data: lv_json             type xstring.
 
     
" Get URL parameters
      lv_controller_id 
= request->get_form_field( 'crm_controller' ).
      lv_saved_search  
= request->get_form_field('saved_search' ).
 
 
     
" Get Shortcut Data
      lr_core
= cl_crm_bol_core=>get_instance( ).
     
"lr_core->start_up('SO2').                                        " Needed to avoid ABAP dumps
      lr_core
->start_up('ALL').
 
      lr_shortcut_man
= cl_crm_shortcut_manager=>get_instance( ).
 
     
iflv_saved_search  is not initial and
         lv_controller_id
is not initial.
        lv_key
= lv_saved_search.
        lr_shortcut
= lr_shortcut_man->get_shortcut_by_key(iv_key = lv_key ).
 
       
iflr_shortcutis bound.
 
         
" Get the name
         
concatenate lv_controller_id lv_saved_search into ls_field_list-name separated by '_'.
         
translatels_field_list-name to upper case.
 
         
" Get the value
          ls_field_list
-value = lr_shortcut->get_property_as_string( 'DESCRIPTION' ).
 
          ls_count
= get_count(lr_shortcut).
         
ifls_countis initial.
            ls_count
= '?'.
         
endif.
         
concatenate'(' ls_count')' into ls_count.
         
condensels_countno-gaps.
 
         
concatenatels_field_list-value space ls_count
                
into ls_field_list-value respecting blanks.
 
       
else.
          ls_field_list
-name  ='ERROR_IN_SAVED_SEARCH_'.
          ls_field_list
-value= 'NO_OBJECT_'.
       
endif.
     
else.
        ls_field_list
-name  ='ERROR_IN_SAVED_SEARCH_'.
        ls_field_list
-value= 'NO_GUID_'.
     
endif.
 
 
     
" Collect Response
     
append ls_field_listto lt_field_list.
 
     
"ABAP to JSON
      lr_writer
= cl_sxml_string_writer=>create(type = if_sxml=>co_xt_json ).
     
calltransformation id source text = lt_field_list[] result xml lr_writer.
      lv_json
= lr_writer->get_output( ).
 
 
     
" Send response
      response
->set_header_field( name = 'content-type'  value = 'text/xml' ).
      response
->set_header_field( name = 'charset'       value = 'utf-8' ).
      response
->set_header_field( name = 'Pragma'        value = 'no-cache' ).
      response
->set_header_field( name = 'Expires'       value = '0' ).
      response
->set_header_field( name = 'Cache-Control' value = 'max-age=0' ).
 
     
response->set_data( lv_json).
 
   
endmethod.

 

GET_COUNT:


  methodget_count.
 
     
data: lr_dquery            type ref to cl_crm_bol_dquery_service.
     
data: id_of_query_templatetype genil_dquery_handle.
     
data: lr_res_col           type ref to if_bol_entity_col.
 
      rv_count
= ''.
 
     
check: ir_shortcutis bound.
 
     
try.
          id_of_query_template
= ir_shortcut->get_property_as_string('PARAMETER_' ).
          lr_dquery
= cl_crm_bol_dquery_service=>load_query_template( iv_query_id=

                                                                     id_of_query_template ).
 
          lr_res_col
= lr_dquery->get_query_result( ).
          rv_count
= lr_res_col->get_iterator( )->size( ).
 
       
catchcx_crm_genil_not_ready.
       
catchcx_sy_no_handler.
       
catchcx_crm_bol_not_ready.
       
catchcx_bol_exception.
     
endtry.
 
   
endmethod.

 

Writing AJAX call-back function

image008.png

 

 

function UpdateSavedSearchCallback(reqObj)

  {

   varresponseText = reqObj.request.responseText;

   varobj = JSON.parse(responseText);

   varfnum = obj.TEXT.length;

   for (vari = 0; i< fnum; i++) {

    varelem = document.getElementById(obj.TEXT[i].NAME);

     if (elem){

      elem.title = obj.TEXT[i].VALUE;

      elem.innerHTML  = obj.TEXT[i].VALUE;

     }

   }

  }

 

Publish a link to the JS file in the table WCFC_ADD_JS.

Fast Order Entry App (AJAX in WebUI example)

$
0
0

Hello,


Today I want to share with you one more example of AJAX implementation in WebUI. Particularly this is about how can we improve the usability and the business process performance by implementing asynchronous request processing in CRM WebUI.


I work in SAP Global Services and Support (GSS) and know that one of the most pain point for our customers is call-center applications as usability and performance are super important there. Along with responsive type-ahead account search, fast inbox search, reach customer fact sheets, which should show “everything”, “right now” and “right here”, our customers need Fast Order Entry applications to ensure non-disruptive order entering process.


In the standard (so far) BSP paradigm, the end-user, after she or he has done some actions, has to wait for the system response. This has a very negative effect on continuous processes, e.g. order entering. And this we will break down by using asynchronous stateful requests. Using our application one does not need to wait for the system response and can keep on entering the sales order. Nevertheless, all required fields get updated via AJAX callbacks. Just watch the video below.



All the technical aspects have been already discussed before, so from this prospective I have really nothing to add. Maybe some useful links:

http://scn.sap.com/community/crm/webclient-ui-framework/blog/2015/12/11/parallelization-in-web-ui--part2

http://scn.sap.com/community/crm/webclient-ui-framework/blog/2016/01/29/update-webui-view-fields-via-ajax

http://scn.sap.com/community/crm/webclient-ui-framework/blog/2016/02/29/asynchronous-saved-search-fetch

http://scn.sap.com/community/abap/connectivity/blog/2014/11/27/introduction-to-abap-channels

 

P.S. You may ask why I have placed the reference to the ABAP push channel.  It is just another asynchronous technique, which might be useful to get some (error) messages back, if you do not want to stick to certain AJAX callbacks and want to use some generic technique. 

 

P.P.S. If you or your customer needs some help in optimizing WebUI business process performance or usability, just contact your SAP account or support manager. SAP GSS will be happy to help.

 

Best Regards,

Dima



Viewing all 195 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>