Quantcast
Channel: SAP CRM: Webclient UI - Framework
Viewing all 188 articles
Browse latest View live

Asynchronous Rendering of Table Views

$
0
0

1 Requirement

In WebUI scenarios specific requests and queries often take a long time until the data is available. In particular, RFC calls to external systems like the ERP can slow down performance. Due to the WebUI framework where visualization and rendering is done after server side processes are finished the user requires dedicated parallel processing. Table Views in WebUI include usually a large amount of different data and therefore different request origins. The ambition is to separate slow requests from the other request in order to fasten the entire visualization.


1.1 Preview

Take a look at the asynchronous rendering in action.



2 Proposal

Using a combination of server implementation and client side scripting Table Views can be rendered asynchronously where required. At first rendering the data is not received but a loading information is displayed instead.1.png

After an AJAX-Callback is started, the data is received and the response is sent back to the client in order to display the Table View entries at the moment they’re getting available.

2.png

3 Implementation Steps

Both server side and client side implementation needs to be done in order to achieve this functionality.

 

3.1 Server Side

The implementation class that handles the callback needs to be the same instance as the one the table view is rendered. Therefore, a modification in the method IF_HTTP_EXTENSION~HANDLE_REQUEST of class CL_CRM_WEB_UTILITY needs to be done. Similar to SAP Note 1968050 where the instance is not created again.

 

3.1.1 Modification in Web Utility Class

The modification is not critical from my perspective. It might be usefull to integrate a customizing there if more than one class needs to be included for different Table Views.

 

* -> create handler class
 TRY.
 IF LV_HANDLER_CLASS_NAME = 'CL_CRM_BSP__RECENTOBJECT0_IMPL'
 OR LV_HANDLER_CLASS_NAME = 'ZL_ZWEBUI_B_ZACCOUNTOPPOR_IMPL'.
 lv_handler_class ?= lv_target_controller.
 ELSE.
 CREATE OBJECT lv_handler_class
 TYPE
 (LV_HANDLER_CLASS_NAME).
 ENDIF.
 * -> call back application handler
 lv_handler_class->handle_request(
 ir_server     = server
 ir_controller = lv_target_controller ).
 CATCH cx_root.
 ENDTRY.

 

3.1.2 HTML

The rendering of the Table View itself must be done manually without using BSP tags to save the created BEE in the respective implementation class. Otherwise, the logic would be called after every roundtrip.

 

<%
 DATA: lr_config_table TYPE REF TO cl_chtmlb_config_cellerator.
 IF controller->gr_config_table IS INITIAL.
 CALL METHOD cl_chtmlb_config_cellerator=>factory
 EXPORTING
 id             = 'Table'
 table          = controller->typed_context->btqropp->table
 _table         = '//BTQROPP/Table'
 xml            = controller->configuration_descr->get_config_data( )
 width          = '100%'
 selectionmode  = cl_thtmlb_cellerator=>gc_selection_mode_none
 personalizable = 'FALSE'
 RECEIVING
 element        = lr_config_table.
 controller->gr_config_table ?= lr_config_table.
 %><script>
 thtmlbAJAXCall.callBackend("<%= controller->create_ajax_url( ) %>",thtmlbCCelleratorManager.asynchronousRenderingCallback);</script><%
 lr_config_table->emptytabletext = cl_wd_utilities=>get_otr_text_by_alias( 'CRM_BSP_UI_FRAME_RECOBJ/LOADING' ).
 ENDIF.
 lr_config_table ?= controller->gr_config_table.
 lr_config_table->if_bsp_bee~render( _m_page_context ).
 %>

3.1.3 Clear on new focus

Depending on the usage it may be required to handle the logic whenever the focus is changed. For instance, in an assignment block that is included in the BP_HEAD. If the business partner is changed the search must be done again. I guess, there can be done some additional investigation to handle the partner change itself and in those cases just take the data out of the BOL. So the performance can be improved even more.

 

3.1.3.1 Method: IF_BSP_MODEL~INIT

Get the instance of the controller implementation class.

 

CALL METHOD super->if_bsp_model~init
 EXPORTING
 id    = id
 owner = owner.
 gv_owner ?= owner.

 

3.1.3.2 Method: ON_NEW_FOCUS

Clear the BSP bee.

 

CLEAR gv_owner->gr_config_table.

3.1.4 AJAX-URL

The URL which calls the backend via JavaScript can be created in the implementation class directly.

 

CALL METHOD cl_crm_web_utility=>create_service_url
 EXPORTING
 iv_handler_class_name = 'ZL_ZWEBUI_B_ZACCOUNTOPPOR_IMPL'
 iv_controller_id      = me->component_id
 RECEIVING
 ev_url                = ev_url.

3.1.5 Callback

The implementation class needs the Interface IF_CRM_WEB_CALLBACK in order to receive the previously callback.

 

3.1.5.1 Method: IF_CRM_WEB_CALLBACK~HANDLE_REQUEST

The method handles the callback, retrieves the data and creates the HTML code for the response.


*|-- References  DATA: lr_result          TYPE REF TO if_bol_bo_col,
 lr_query_service   TYPE REF TO cl_crm_bol_dquery_service.
*|-- Variables
 DATA: lv_partner         TYPE string,
 lv_html            TYPE string.
 *|-- BP number
 lv_partner = me->typed_context->builheader->collection_wrapper->get_current( )->get_property_as_string( iv_attr_name = 'BP_NUMBER' ).
 CHECK lv_partner IS NOT INITIAL.
 TRY.
 *|-- Query Service for Opportunities
 lr_query_service = cl_crm_bol_dquery_service=>get_instance( 'BTQOpp' ).
 *|-- Query-Parameter
 CALL METHOD lr_query_service->add_selection_param
 EXPORTING
 iv_attr_name = 'PROSPECT'
 iv_sign      = 'I'
 iv_option    = 'EQ'
 iv_low       = lv_partner.
 *|-- Result
 lr_result = lr_query_service->get_query_result( ).
 CATCH cx_crm_genil_general_error.
 RETURN.
 ENDTRY.
 *|-- Sorting
 IF lr_result IS BOUND.
 lr_result->sort(
 EXPORTING
 iv_attr_name  = 'EXP_REVENUE'
 iv_sort_order = if_bol_bo_col=>sort_descending ).
 ENDIF.
 *|-- Set Result to Context & build table
 me->typed_context->btqropp->collection_wrapper->set_collection( lr_result ).
 me->typed_context->btqropp->build_table( ).
 *|-- Clear text
 CLEAR me->gr_config_table->emptytabletext.
 *|-- Create HTML
 CALL METHOD create_table_view_html
 EXPORTING
 ir_server         = ir_server
 ir_controller     = ir_controller
 iv_binding_string = '//BTQROPP/TABLE'
 iv_table_id       = me->get_id( 'Table' )
 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.

3.1.5.2 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.

3.2 Client Side

The response that is created on the server side now must be handled on the client side for direct rendering without the need of a round trip.

Therefore the JavaScript scripts_.oo_tableview.js of BSP application THTMLB_SCRIPTS must be modified.

 

3.2.1 Modification in JavaScript

Basically the logic of the new Javascript function is the same as the function createFastRowsCallback where the system creates new empty rows in a Table View.

 

3.2.2 Function: asynchronousRenderingCallback

This function has following changes compared to the fast row creation logic:

 

  • Change the loading text of the no result line to take cases without any results into consideration
  • Hide the no result line in cases where we get at least one result

 

3.2.2.1 Change text

 

var noResultRow = document.getElementById(values.tableId[0]+"_noresult");
 var regexExp = new RegExp ("<%= cl_wd_utilities=>get_otr_text_by_alias( 'CRM_BSP_UI_FRAME_RECOBJ/LOADING' ) %>","g");
 var noResultText = "<%= cl_wd_utilities=>get_otr_text_by_alias( 'BSP_DYN_CONFIG_TAG_LIB/NO_RESULT_FOUND' ) %>";
 noResultRow.firstChild.innerHTML = noResultRow.firstChild.innerHTML.replace(regexExp,noResultText);

 

3.2.2.2 Hide line

 

for (var i = 0, length = values.rows.length; i < length; i++) {<%-- Hide no result line --%>
 noResultRow.className += ' th-displayNone' ;<%-- check if the row already exists in the case where the table has inserted empty rows --%>
 rowFound = true;
 row = document.getElementById(values.rowIds[i]);
 if (values.fixedLeftRows[i] != "")
 rowFixedLeft = document.getElementById(values.rowIds[i]+"_left");
 if (values.fixedRightRows[i] != "")
 rowFixedRight = document.getElementById(values.rowIds[i]+"_right");

 

4 Summary

This logic provides a good opportunity in scenarios where dedicated requests take a long time and therefore the entire visualization is slow. However, the modification in the JavaScript is highly critical. One mistake leads to heavy errors in the WebUI as this file is used in almost every view. As a result, I can't recommend this for productive systems. I hope, that this can help the SAP to create an improment note to support this functionality.

 

Cheers,

Sebastian


Edit webUI log in screen Layout html

$
0
0

1.go to transaction SICF
    execute then got default host -> sap->bc->bsp->sap->crm_ui_start

 

 


2. click on configuration button of tab error pages

 

 

click on adjust links and images

 

 

 

here you can specify different images to be shown . but changing these settings will not make any effect in standard layout of login page.

 

 

how to change the complete layout of login page

click on custom implementation radio button.

 

 

here we need to specify a class which should be subclass of CL_ICF_SYSTEM_LOGIN

You can use the following classes as a reference to implementation

  • CL_ICF_BASIC_LOGIN
  • CL_ICF_IDES_LOGIN
  • CL_ICF_NW04_LOGIN


here i have copied CL_ICF_BASIC_LOGIN to ZCL_ICF_BASIC_LOGIN

goto metod RENDER_PAGE
there we can see compleate html. make necessary changes here

 

 

upload any image in the MIME repository at above path

 

 

save and test the output

 

New-Login page for Fiori.jpg

UPLOAD A FILE IN SAP WEB UI

$
0
0

UPLOAD A FILE IN SAP WEB UI

1.Add component usage ‘GS_FILE_UPLOAD’.

 

 

 

 

 

2.create a TABLE view with  button upload.

 

 

               

 

 

 

3.call component usage as popup on clicking that button.

 

 

 

 

 

 

 

 

create event handler onpopup_closed.

 

METHOD eh_onpopup_closed.
“structure corresponds to view attributes
TYPES :BEGIN OF ls_struct,
emp_id TYPE char10,
name TYPE char30,
END OF ls_struct.
FIELD-SYMBOLS <fs_wa_data> TYPE ls_struct.
DATA : lr_value_node   TYPE REF TO cl_bsp_wd_value_node,
lr_bo_col       TYPE REF TO if_bol_bo_col,
lr_struct       TYPE REF TO ls_struct,
lr_output_node  TYPE REF TO cl_bsp_wd_context_node,
lr_property     TYPE REF TO if_bol_bo_property_access,
lv_file_content TYPE string,
lt_file_content TYPE stringtab,
lv_dummy        TYPE string.

"CLEAR EXISTING VALUES
me->typed_context->filecontent->collection_wrapper->clear( ).
"GET UPLOADED VALUES
lr_property  = gr_popup->get_context_node( iv_cnode_name = 'FILE' )->get_collection_wrapper( )->get_current( ).
lr_property->get_property_as_value(
EXPORTING
iv_attr_name = 'FILE_CONTENT_STRING'    " Component Name
IMPORTING
ev_result    = lv_file_content
).
IF lv_file_content IS NOT INITIAL.
CREATE DATA lr_struct.
ASSIGN lr_struct->* TO <fs_wa_data>.
"SPLIT EACH LINE TO TABLE
SPLIT lv_file_content AT cl_abap_char_utilities=>cr_lf INTO TABLE lt_file_content.
CLEAR lv_file_content.
IF lr_bo_col IS NOT BOUND.
CREATE OBJECT lr_bo_col TYPE cl_crm_bol_bo_col.
ENDIF.
LOOP AT lt_file_content INTO lv_file_content .

“ignore the title
IF sy-tabix <> 1.
"SPLIT VALUES FROM STRING
SPLIT lv_file_content AT ',' INTO <fs_wa_data>-emp_id <fs_wa_data>-name lv_dummy.
"CREATE VALUE NODE OBJECT
CREATE OBJECT lr_value_node
EXPORTING
iv_data_ref = lr_struct.
"ADD TO COLLECTION
lr_bo_col->add(
EXPORTING
iv_entity    = lr_value_node     " Access Interface for BO Attributes
*      iv_set_focus = ABAP_FALSE    " Indicator: Set Focus on New Element
).
ENDIF.
ENDLOOP.
me->typed_context->filecontent->collection_wrapper->add_collection( iv_collection = lr_bo_col  ).
ENDIF.
ENDMETHOD.

 

 

Output

 

 

 

 

 

 

 

Upload the file

 

 

 

How to find the relevant AET database table for your AET fields

$
0
0

Someone asked me that how to find the relevant AET tables for his created AET fields. When we created the AET fields from the object part with which we are familiar, then we can easily find it. For example, if we create an AET field from object part ORDERADM_H, then we can easily guess that the relevant database table will be CRMD_ORDERADM_H.

 

But if we created the AET fields from an unfamiliar component/view, object part, then what is the easy way to find the relevant database AET table? Actually you can follow the following 3 steps. In example below, we created an AET field under component PRDHS and its enhancement place is INCL_EEW_PROD_MAT.

 

1. Open AET by configuration and note down the enhancement place(ex.INCL_EEW_PROD_MAT) of your AET field.

1.JPG

 

2. Run t-code:se11, then enter enhancement place INCL_EEW_PROD_MAT in field Data type and display it. Then click button “Display Object List”.

2.JPG

 

3.Then you can find relevant AET table under folder “Database Tables”. As we enhanced the field on header, So it is easy to find the table is CRM_EXT_MATH.

 

3.JPG

How to find out who calls a function module in update task in CRM

$
0
0

Purpose


In CRM, when saving a transaction(T_code CRMD_ORDER or CRM webui), there are function modules executed in update task. Sometimes, we get errors from these function modules. The typical sample is: SAPSQL_ARRAY_INSERT_DUPREC which happens in function modules like CRM_SERVICE_OS_UPD_SUB_DU.

 

Most of time, it happens because the same function module is executed twice with the same records to insert. If we know from where the function module is called, we can understand the whole logic more clearly and find out the reason why it runs in this way.

 

Normally, updates use database table VBMOD and VBDATA to record the update tasks. So we can use T-code SM13 to get information for the update function modules. But for CRMD_ORDER and CRM webui, we cannot find the update information in SM13. It is because both T-code CRMD_ORDER and CRM webui use ABAP Memory technology instead of VBMOD and VBDATA.

 

We can consider setting a breakpoint in the update function module. But when the breakpoint stops, it is already inside this function module. We still don't know who call it.


Of couse, we can also set breakpoint in the command 'CALL FUNCTION' or 'CALL FUNCTION IN UPDATE TASK'. It will stop at every 'CALL FUNCTION' statement. If we are lucky enough, we can find out quickly the line where the expected function module is called. But for me, I cannot say so.


Then, how can we locate quickly the place where the update function module is called? In this article, I will explain with a sample how I do.


How is the update information stored and retrieved

 

If we display program SAPMSSY0 in T-code SE38, we can find export to memory id statement. It writes %_vb_calls into ABAP memory with ID %%vbkey while %_vb_calls contains the update function modules information.
1 export statement.PNG


Let's display program SAPMSSY4. We will see import from memory statement. Now the update information in the ABAP memory is imported to %_vb_calls.

2 import statement.PNG

Sample

I want to find out where the update function module CRM_SERVICE_OS_UPD_OST_DU is called.

 

steps in T-code CRMD_ORDER

1. Set breakpoint in line 575 of program SAPMSSY0 and line 31 of program SAPMSSY4.

2. Create a transaction, input values. Before clicking 'save' button, input /h and click Enter first.

3 create a transaction to save.PNG
3. Click 'save' button, it will go into debug mode. Check from menu 'Settings'->'Change Debugger Profile/Settings', make sure system debug is switch on since SAPMSSY0 and SAPMSSY4 are system programs.

4 into debug mode.PNG

5 debug setting.PNG

4. Click F8, the breakpoint will stop at the line 575 of program SAPMSSY0. Double click the variant vb_func_name, the value now is, for example, CRM_ACTIVITY_H_UPDATE_DU.

6 stop at export.PNG

5. From the callstack, if we double click on the previous program, we can see how FM CRM_ACTIVITY_H_UPDATE_DU is called in FM CRM_ORDER_TABLE_SAVE.

6-2 stop at export.PNG

6. If we click F8, we can get all callstacks and notice that the variant VB_FUNC_NAME is the update function module name which is being called.

7 update tasks calling.PNG

7. Of course, I just want to find out where FM CRM_SERVICE_OS_UPD_OST_DU is called. If I click F8 everytime, I have to stop many times. In order to make it fast, in step 5, I will set a condition for the breakpoint. Go to 'Break/Watchpoints' tab, click 'new' to create a condition.

8-1 set condition.PNG

8. The condition is VB_FUNC_NAME = 'CRM_SERVICE_OS_UPD_OST_DU'.

8-2 set condition.PNG

9. Go back to 'standard' tab, click F8. This time, it will stop only when the update function module 'CRM_SERVICE_OS_UPD_OST_DU' is called. In this way, I can find out where the expected function module is called.

8-3 back to bk.PNG

The entries of %_vb_calls change every time when we stop here. At the moment, its content is:

8-4 vb tab exported.PNG

10. If we continue to click F8, finally, we will come to the line 31 of SAPMSSY4.

9-1 vb table.PNG

Content of %_vb_calls will be:

9-2 vb table.PNG

After this, system will loop %_vb_calls, execute the update function modules one by one.

 

steps in CRM webui

The steps in CRM webui are similiar to the ones in T-code CRMD_ORDER. For example, if I create an interaction record, all the steps will be:

1. Set breakpoint at line 575 of program SAPMSSY0 and line 31 of program SAPMSSY4. Please notice the breakpoints are 'external breakpoint'.

2. Set breakpoint at method CL_ICCMP_BT_BUTTONBAR_IMPL->EH_ONSAVE.

3. Create an interaction record and click 'save' button.

P-1 web save.PNG

4. Breakpoint will stop at line 575 of program SAPMSSY0. Also make sure system debug is switched on. Set condition VB_FUNC_NAME = 'CRM_SERVICE_OS_UPD_OST_DU', then click F8 to continue.

5. Breakpoint will stop when FM CRM_SERVICE_OS_UPD_OST_DU is called.

New WEBCUIF Standard Theme: Blue Crystal

$
0
0

The Web Client UI Framework (WEBCUIF) is now offering the new standard SAP Blue Crystal theme. SAP Blue Crystal is the new visual design theme, introduced by SAP Fiori, which succeeds the older Corbu design. It is based on the same interaction design and UI controls as Corbu, but uses different colors. This new visual design offers a modern and fresh look.

 

SAP Blue Crystal can also be used to integrate existing user interfaces into SAP Fiori scenarios. It thus aims to reduce visual disruption between the different technologies. it harmonizes the coloring and design of different controls, but does not resemble Fiori's user interaction, navigation and the like.

 

Characteristics of the "SAP Blue Crystal" design for Unified Rendering:

  • very light design
  • less use of grey colors
  • harmonized coloring with "SAP Blue Crystal" for SAPUI5
  • based on the metrics of "SAP Corbu"
  • uses CSS3 technology

 

01_home.png09_leads_search.png12_leads_search_bar.png13_leads_ovp.png

 

For more information, see SAP Note 2227135 "New visual design Blue Crystal for the Web Client User Interface" and 2239424 "Blue Crystal Theme in Interaction Center".

Common issue when open excel/URL / Cache issue

$
0
0

Hello everyone,

 

Recently we got chance to work on excel download/upload/open functionality from CRM UI.

In result table view of custom application we have some field of type hyperlink, on click of these hyperlink fields we have to display different data of customers

in excel form. The issue we were facing that the open of excel was working only first time means when we were opening excel  on click of hyperlink field it was opening perfectly only for first time but second time none of hyperlink field was working.

Event if we were opening same excel twice , it was not working.

 

I searched this issue on SCN and find number of incomplete threads on the same , so just sharing a small solution that is working fine to remove this issue.

We are using callback class approach to open excel and using window.open() method in .HTM page to oprn excel URL.

 

After preparing all data in callback class when we were opening the URL from HTM page with Window.open() , system was considering the every URL as same one. So to remove this one we have to pass unique URL every time on click of any hyperlink field.

 

so in HTM page we change our final URL as::

 

 

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

 

<%   IF lv_our_URL is not initial.  %>

 

 

Window.open("<% lv_our_url  %> , <%   sy-uzeit %> ");

 

 

<%

 

Clear lv_our_url.

 

endif .  %>

 

 

</script>

 

 

Above code made URL unique each time and open excel without any issue.

 

Hope it will Help.

 

Regards,

Harish kumar.

WEBCUIF EhP4 SP01 - Release Notes

$
0
0

These Release Notes focus on new features and enhancements delivered as part of the Web Client UI Framework EhP4 Support Package 01.

 

The WEBCUIF development team members are doing their best to provide those new developments on older releases. So if you are interested in a specific feature, verify its availability by checking the corresponding SAP Note.

 

New Features and Enhancements

 

New visual design Blue Crystal for the Web Client User Interface

 

SAP Blue Crystal is the new visual design theme, introduced by SAP Fiori, which succeeds the older Corbu design. It is based on the same interaction design and UI controls as Corbu, but uses different colors. This new visual design offers a modern and fresh look.

 

SAP Blue Crystal can also be used to integrate existing user interfaces into SAP Fiori scenarios. It thus aims to reduce visual disruption between the different technologies. it harmonizes the coloring and design of different controls, but does not resemble Fiori's user interaction, navigation and the like.

 

See blog post New WEBCUIF Standard Theme: Blue Crystal.

 

See SAP Note: 2227135

 

 

Direct URL for Saved Search

 

A new button "Open Saved Search with a persistent URL" has been added to the Central Search Launcher. When the user clicks on this button, the selected Saved Search will be launched in a new window (or tab depending on the user's browser settings) with a stable URL.

 

direct_url_ss.png

 

See SAP Note: 2211185

 

 

Recent Items filtered on object type

 

This feature allows users to quickly filter out their recent activity without having to go through the whole list. Also, the limits on how many recent items that are stored has increased, allowing each object type to have its own limit.

 

For example, before this feature, if the total Recent Items limit was 5 items, the 6th item is deleted. Now, there will be 5 of each type that is stored in the backend. This way, when the filter is applied, the last 5 of that object type are displayed.

 

recent_items.png

 

See SAP Note: 2181937

 

 

Custom help documents

 

In order to further help and guide the user, it is possible to provide Custom Help content via integration with the Workforce Performance Builder.

 

wpb_integration.png

 

See SAP Note: 2218828

 

 

If you missed them, check out the WEBCUIF EhP3 SP10 - Release Notes


BPath. Lesson 11. Enhancements on sub-queries

$
0
0
Table of Contents ...... Table of Code Examples
<< Lesson 10. Aggregations

 

Hello everybody,

 

first of all I have to apologize pretty much, we simply missed out that there was one lesson pending. So here comes lesson 11 after a quite long waiting period.


As suggested before the chapter mainly deals with the sub function. But enough talking, let's concentrate on the details:

 

Copy data from sub queries using * = sub(...)

 

Upto now it was possible to add data returned by a subquery, either if it was a single field or as reference to a structure or a table. In case it was required to add two fields (e.g. Average and Standard Deviation), this would require two distinct sub queries harming performance and readability.

Now it is possible to using the star operator "as target". In this case the data returned by the sub query is examined. If it is a table or an unstructured field, an error is risen. In case it is a structure all fields of the structure are copied to the result.

 

~*/SearchResFlightRel{@CARRID;@CONNID;@FLDATE;*=SUB(~*/FlightBookRel[@CLASS="Y"]$(*::!Count=Count();!Average=AVG(@LOCCURAM,2);!Median=Median(@LOCCURAM)))}$
Code Example 50,*=SUB(), copy data from sub query

 

Well ok, the example got a bit complicated. But it is worth taking a look at. On the outer side we have list of flights from which we want to return the keys and some data from the sub query. The inner side / sub query works on the bookings using the "aggregate to one value" feature to ensure that the result is a structure and no table. The sub query filters to Class = Y (Economy Class) and determines the three values Count, Average and Median, speaking for themselves. Interestingly Median is mostly the same, guess that's caused by the fact, that it is generated data. Please note also that some of the flights have no bookings, hence the fields remain empty / null.

 

The result of the query is a list of keys plus number, average and median amount (in local currency) of the economy class bookings of the respective flight.

A small remark, in case the complexity above is still not enough for you. I tried to work with a second grouping on the outer level. Actually this should work, except that you do not have the feature you probably want: Access to the data generated within the sub. Every access to a target within the group code is interpreted as access to the group item and not to the actual item.

 

Addings rows from a sub query using $ = sub() [ the OR feature ]

 

Sometimes data might be reached on different pathes and it is wishful to return the data from several pathes instead of a distinctive path. Although it was possible with sub queries to return the data either as linked structure or (using dereferencing) in columns, it was not possible to simply "or" the data assembled by the various sub-queries, meaning to add a new row for every dataset found.

This is now possible using the $= feature which realizes kind of OR. For every element returned by the sub query a new row is added and a move-corresponding is executed. This means that no new columns were added by this statement (in contrast to *= ).

 

See below a non-trivial example of the usage.

 

~*CRMS_BOL_UIF_TRAVEL_CONN_ATT/SearchResFlightRel{!SOURCE:="MAIN"}/FlightConnectionRel{$=sub(~*CRMS_BOL_UIF_TRAVEL_CONN_ATT/.{!SOURCE="SUBQUERY"}/*);!1=@CONNID}/ConnectionCarrierRel/CarrierConnectionRel[@CONNID<>!1]/*$
Code Example 51,$=SUB(), adding single rows from sub query


Data is collected for the Connection object on two distinct pathes. The first is directly related to the connection and is fetched by the sub-query. The other is executing the relation to the carrier and checks which other connections are available, which are also added to the return data. In fact we are returning data as "my connection" OR "other connections". In this example case here, the task could be resolved with standard tools also, but there exist typical examples in real life (fetching emails from contacts, which may be placed under contact, relation ship or address details) requiring this feature.
Please note that in above example only a single element is returned by the subquery (no dollar appears in sub query). If we turn the logic around, we have to return and add a full table, so the code would look like:

 

~*CRMS_BOL_UIF_TRAVEL_CONN_ATT/SearchResFlightRel{!SOURCE:="MAIN"}/FlightConnectionRel{!1=@CONNID;$=sub(~*CRMS_BOL_UIF_TRAVEL_CONN_ATT/ConnectionCarrierRel/CarrierConnectionRel{!SOURCE="SUBQUERY"}[@CONNID<>!1]/*$)}/*$
Code Example 52,$=SUB(...$), adding rows from sub query

 

Note that the subquery accesses the global variable !1, which is set by the embedding query.

Sub queries can also be used (either returning structures or tables) in this manner, in case the embedding queries returns only the first element (as structure). In this case the inner sub query should also return only the first element, since anyway all others are ignored, but syntactically it would be also supported to return a table.

 

Merging structures

 

This enhancement allows to merge structures as in the following self-explanatory example:

 

~*CRMS_BOL_UIF_TRAVEL_BOOK_ATT+~CRMS_BOL_UIF_TRAVEL_FLIGHT_ATT/SearchResFlightRel{$:=sub(*)}/FlightBookRel/*$
<Code Example 53,~STRUCT1+~STRUCT2/./..., merging structures


 

Textsearch()

 

~*/SearchResFlightRel/FlightBookRel[TEXTSEARCH("*ller*")]/*$
Code Example 54,!x:=Textsearch("*"), text search on complete structure

 

Text search provides the feasibility to search the given pattern on all string fields of the structure which have at least a certain size. The size might be specified as second, optional parameter. If not specified only strings with size 9 or bigger are encountered.

 

 

 

 

Well I guess with this last feature we are through with our series.

 

I thank all readers for their interest and the patience as the whole series spawned over a pretty long period.

 

Best Regards, Juergen

Sort Ascending/ Descending on a field in a table view

$
0
0

Purpose

 

Sometimes on a table view, you want to sort the list by a field, you select “Sort Ascending” or “Sort Descending” on the field, but it does not provide the accurate result you expect.

 

One example: you log on with SALESPRO business role, open an account, go to the “ERP Sales Order” assignment block, click on field “Sales Order ID”, select “Sort Ascending”, the result looks like:

Screenshot1.png

This is not the result you expect. I will explain here why the result looks like this.

 

Get the model information

 

1. Press F2 button on the Sales Order ID field and get the technical data:

    component/view: BP_ERPBT/ERPTransactionsOV

    context node: ERPQRORDER

    attribute: VBELN

2. Go to component workbench (TCode BSP_WD_CMPWB), open component/view BP_ERPBT/ERPTransactionsOV, find the context node ERPQRORDER, as shown in the screenshot the base entity of the context node is ERPQROrder. This is the object model used.

component workbentch.png

 

Get the data type information

 

Run TCode GENIL_MODEL_BROWSER, check the radio button Component and enter ERP, then click Display button, expand Query Result Objects, and find the model ERPQROrder, expand the attribute structure, and locate the VBELN attribute

model.png

 

Double click on the VBELN attribute, see the Date Type is CHAR.

data element.png


So even the value of the Sales Order ID field are numbers, the date type used for Sales Order ID field is CHAR therefor it will be sorted as TEXT. This is why you see the result as shown in the first screenshot.

 

You can also set a breakpoint in method SORT_INTERNAL of class CL_CRM_BOL_BO_COL, and check how the sorting works. In this method it will get the date type of the attribute you select to sort, and then execute different coding for different data type, like Date, Time, CHAR, etc.

coding.png

Combining SAP CRM and SAP Screen Personas

$
0
0

 

This is going to be a rather short blog. However, as I didn't read anything related to the topic so far I think is worthwhile sharing my experience.

 

Introduction

Since the first time I saw a demo of SAP Screen Personas on SCN I really liked the idea of being able to change the look and feel of existing ERP transactions. Therefore, I immediately installed the add on on one of our internal development system and tried it out. However, as earlier versions of SAP Screen Personas required the installation of Silverlight at the client computer I never started to roll it out for our customers.

In version 3 SAP Screen Personas was redesigned to no longer require Silverlight at the client computer. Therefore, I started to look at possible usage scenarios for SAP Screen Personas again. One scenario that immediately came to my mind was the simplification of ERP transaction integrated into SAP CRM using the transaction launcher technology.

 

Note that in this blog I won't give any introduction to the SAP Screen Personas functionality. A comprehensive collection of demo's and learning content for SAP Screen Personas is available here: SAP Screen Personas – Getting Started

 

SAP CRM Transaction Launcher

In short, the SAP CRM transaction launcher is a technology to integrate ERP transactions into SAP CRM. This is require if, for example, if no suitable SAP CRM functionality is available but a process needs to be executed from with SAP CRM. The integration of the ERP transaction into the SAP CRM Web UI is achieved using SAP GUI for HTML. If you are interested in further details regarding the transaction launcher technology as well as other usage scenarios I reccomand reading the two excellent document Almost Everything About Transaction Launcher - Part I and Almost Everything About Transaction Launcher - Part II by Hasan Zubairi.

One of the processes where we use the the integration of ERP transactions into SAP CRM is changing the contract account in the future via transaction CAA2. The layout of the transaction when displayed in the transaction launcher is shown in the screen shot below. As usually for ERP transaction it contains quite a number of fields that are not required in all processes variants.

2015-11-16 10_03_21-Vertragskonto ändern (IS-U) - [Interaction Center ] - Internet Explorer bereitge.png

Using SAP Screen Personas we could simplify the transactions to the required set of fields. An example of such a simplified transaction is shown in the following screen shot. From the screen shot it is immediate obvious, that it is possible to combine SAP Screen Personas and SAP CRM.

2015-11-16 13_36_07-Unbenannt - paint.net v4.0.5.png

Configuring Transaction Launcher with SAP Screen Personas

So the question now is what is required to integrate SAP CRM transaction launcher and SAP Screen Personas. After executing the necessary post installation steps for screen personas (cf. Pre- and post-installation Checklist) the configuration is quite straight forward. Using transaction CRMS_IC_CROSS_SYS it is only necessary to change the URL for ERP_ISU from http://<server>:<port>/sap/bc/gui/sap/its/webgui/!?~transaction=IC_LTXE&~okcode=ICEXECUTE&sap-client=100 to http://<server>:<port>/sap/bc/personas/!?~transaction=IC_LTXE&~okcode=ICEXECUTE&sap-client=100 (cf. the following screenshot).

2015-11-16 10_14_12-Sicht _Logische Systeme und URLs für Transaktionsstarter definieren_ a.png

After this, all ERP transaction integrated into SAP CRM via the transaction launcher technology are rendered using Screen Personas. Furthermore, it is even possible to adjust the transaction using Screen Personas directly from within the SAP CRM session.

 

Christian

those things about framework enhancement

$
0
0

Purpose:

In this article, I will show how the framework enhancement is managed, where to find related information, the navigations between SE18 enhancement spot tool and enhancement implementation tool, and some other related information.


Introduction

We have known about new Framework enhancement technology.

 

In CRM system, the mostly used technology is Kernel-BADI enhancement. For example, BADI component_loading is integrated into enhancement spot COMPONENT_HANDLING. BADI CRM_BP_UIU_BT is part of enhancement spot CRM_UIU_BP_ENHANCEMENT.

 

Even though I don’t use it, but I also found Source Code enhancement spot es_crm_btx_eew_wzd in function group CRM_BTX_EEW_WZD.

 

Of course, implicit enhancement interfaces are also provided in classes, function modules, subroutines, etc.

 

We can find lots of documents regarding framework enhancement - about the advantages, about the four technologies which are used for different purposes, about how to create implementations,  etc. For example:

The new Enhancement Framework and the new kernel-based BAdI

Enhancement Framework in ABAP - Blog 1

...

 

Prerequisite:

  • Understand the framework enhancement technology
  • Basic knowledge on how to create implementations for an enhancement spots


Objects:

In order to make it clear, the objects I will create are:

  • A package ZPACK_CORRI.
  • My own program Z_REP_CORRI, and include file Z_REP_INCL under package ZPACK_CORRI.
  • Enhancement spot ZENH_CORRI_SPOT1 with enhancement point Z_POINT1_1 in program Z_REP_CORRI,
    Enhancement spot ZENH_CORRI_SPOT1 with enhancement point Z_POINT1_2 in include file Z_REP_INCL,
    Enhancement spot ZENH_CORRI_SPOT2 with enhancement point Z_POINT2_1 in program Z_REP_CORRI.
  • Another package ZPACK_CORRI_TMP.
  • Enhancement Z_POINT11_SPOT1 for enhancement point Z_POINT1_1 for enhancement spot ZENH_CORRI_SPOT1.
  • Enhancement Z_POINT12_SPOT1 for enhancement point Z_POINT1_2 for enhancement spot ZENH_CORRI_SPOT1.

 

Steps:

  1. In Z_REP_CORRI, put cursor in the expected line, then go to menu 'Edit'->'Enhancement Operations'->'Create Option'. In the pop up dialog, input enhancement point Z_POINT1_1, enhancement spot ZENH_CORRI_SPOT1, package ZPACK_CORRI.
    1 add a point and spot.PNG
  2. In the same way, create enhancement point Z_POINT2_1 for enhancement spot ZENH_CORRI_SPOT2.
    2 add a point and spot 2.PNG
  3. After that, enhancement spot ZENH_CORRI_SPOT2 is also created.
    3 after report points added.PNG
  4. Create enhancement point Z_POINT1_2 for enhancement spot ZENH_CORRI_SPOT1 in file Z_REP_INCL. This time, since enhancement spot ZENH_CORRI_SPOT1 has already been created, we just need to select the entry.
    4 add 2nd point for spot 1.PNG
  5. After added the enhancement point in file Z_REP_INCL.
    5 after add point in inclu.PNG
  6. Double click enhancement spot ZENH_CORRI_SPOT1 on the left side, it will navigate to the enhancement spot tool.
    6 spot 1.PNG
    Actually, this enhancement spot tool is SE18 itself.
    7 se18 for spot 1.PNG
    click 'Display' button, it goes into the same screen.
    8 se18 inside.PNG
  7. Let's go to 'Enhancem. Implementations' tab. There is nothing because there is no any enhancement yet.
    9 enhancement impl..PNG
  8. Now, create an enhancement for enhancement spot ZENH_CORRI_SPOT1 in point Z_POINT1_1. The enhancement's name is Z_POINT11_SPOT1. I put this enhancement under the 2nd package ZPACK_CORRI_TMP(Of course, you can put it under any package even ZPACK_CORRI itself also. But as I'm simulating the process, I pretend that package ZPACK_CORRI is standard developer package while ZPACK_CORRI_TMP is customer package).
    9-1 create an enhancement for point 1-1.PNG
  9. Let's check the implementation tool under package ZPACK_CORRI_TMP. From here, we can also go to enhancement spot tool.
    9-2 implement tool.PNG
  10. Of course, if we goto 'Enhancem. Implementations' tab again, an entry is added. If we double click the cell of 'Z_POINT11_SPOT1', it will navigate to the same implementation tool as in above point 9.
    9-1.5.PNG
  11. From implementation tool, if click 'Editor' button, it will navigate to the ABAP editor. If click 'Source' button, the enhancement source codes will be displayed in the same screen.
    9-3 check source.PNG


 

 

Additional information:

I made above samples from my own Z programs. Actually, the normal way to use enhancement spots are:

  1. SAP developers create enhancement spots, provide enhancement points in SAP delivered standard programs.
  2. Then enhancements will be created by SAP developers for utility purposes. In this situation, the enhancements will be provided to customers as a part of SAP standard. Or the enhancements can be created by customer per their business requirements. At this time, the enhancements’ names of course will start with Z or Y.

 

For example:

enhancements by different purpose.PNG

From this source codes, double click ES_SAPMV45A, it navigates to enhancement spot tool. Go to 'Enhancem. Implementations' tab, the customer enhancement ZMY_IMP_CORRI can be found. Of course, we can find ECO_HBS_SAPMV45A, /SAPMP/SALES_ORDER_V_SAPMV45A, etc also.

9-4.PNG

 

If you are interested, please check the same information in DB table

9-5 db table.PNG

 

Related DB tables:

ENHSPOTHEADER, ENHOBJ, ENHLOADINVAL, ENHHEADER, ENHSPOTNAME_TYPE, ENHSPOTOBJ, ENHOBJCONTRACT, ENHINCINX, ENHLOG, ENHTAB, ENHDEPENDENT

How transaction launcher determines the remote system

$
0
0

Purpose

Sometime you faced the problem that the remote system is not determined correctly while calling a transaction launcher. This article will show how transaction launcher determines the remote system.

 

Introduction

Basically there is a static way to determine the remote system but also with release CRM 7.0 and higher, it comes with a feature to determine the remote system dynamically as business need it. for e.g. documents of different ERP systems should be displayed.


Determine the logical system from transaction CRMS_IC_CROSS_SYS

While configuring the transaction launcher, a Logical System is specified.

configuretransactionlauncher.PNG

System reads the mapped logical system in transaction CRMS_IC_CROSS_SYS.

transaction CRMS_IC_CROSS_SYS.PNG

This is a static way of determining the remote system.


Determine the logical system from Global Data Context (GDC)

Sometime business needs to use multiple remote systems. You can check the “MultiSys” checkbox in transaction CRMS_IC_CROSS_SYS.

multiplesys.png


Then the transaction launcher will read the attribute LOGICAL_SYSTEM from the GDC which can mainly happen in the interaction center.

You can check method CL_CRM_IC_ACTION_UTIL=>GET_LOGSYS_FROM_MAPPING from technical point of view.


Determine the logical system using enhancement spot WCF_LTX

System also has the enhancement spot WCF_LTX which contains the BAdI WCF_LTX_BADI with its method ADJUST_LOGSYS. This method will be called during the initialization phase of the transaction launcher. You can set a breakpoint in method INIT of the transaction launcher

handler class to check the BADI.

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

 

Additional information:

There is a special function for search field 'Categorization': Normally, when we click on a field, the cursor will focus on it. If we want to use the F4 help function, we must click on the 'F4 Help' icon attached to this field. But for 'Categorization' field, if we click on the field itself, the F4 help dialog will also pop up like we click on the 'F4 help' icon directly. Why?

This is because the attribute cs_result-INPUT_HELP_MANDATORY is set as 'X'.

 

Just like 'operators', 'server_event', 'ddlb_options', 'output_mapping', 'help_id', attribute 'input_help_mandatory' can also be assign values in CL_..._ADVS_CNTRL->GET_V_XXX methods.

 

But of course, there are other places to set these attributes. For example:

  • For business role SERVICEPRO, search 'service orders', attribute for input_help_mandatory for field 'Categorization' is set in method CL_BSP_WD_CONTEXT_NODE_ASP->GET_DQUERY_VALUEHELPS according to the returned value from method CL_CRM_UIU_BT_ADVS_CN->GET_P_CATEGORY_ID.

 

  • For business role SALESPRO, search 'Leads', in method CL_BT108S_L_SEARCH_IMPL->GET_DQUERY_DEFINITIONS, operators, server_event are controlled.

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.  

Viewing all 188 articles
Browse latest View live


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