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

Generic interaction layer for business transactions

$
0
0

Main Component

 

The component for business transactions is BT, you can see it in GENIL_MODEL_EDITOR :

 

Capture_BT.PNG

 

 

Objects

 

 

The object table contains all the Bol objects of the component :

 

Capture_Obj.PNG

 

First column is the Bol name of the object, the second one is the name of business transaction subobject (as defined in table CRMC_OBJECTS).

 

Third column is the type of Object :

A    Root Object

B    Access Object

C    Dependent Object

D    Search Object

E    Search Result Object

F    View Object

G    Dynamic Search Object

X    Abstract Modeling Object

 

Then there are two structures name, one for attributes of the object and one for the key (most of the time only a GUID).

 

A read only flag defined if the object can be modify or not.

 

The handler class define the runtime class for the object, but most of the time the field is empty and the name of the class is derived from the object name (we will see how later).

 

The flag WS_ENABLED defines if you can use this object to create web services with the wizard.

 

And the last columns are used to manage switch IDs.

 

Model

 

The model table contains all the relationships between these objetcs :

 

Capture_model.PNG

 

 

So we have the name of the first object, relationship name and name of the linked object.

 

Two fields for cardinality define how many relationships you can have between objects, if the relationship is mandatory (1 - 1) or unique (0 - 1).

 

Type of relation :

A    Association  : Binds child objects to a root object. Only access and dependent objects can
be aggregated.

B    Composite : Like an aggregation, but composed child objects must always exist.

C    Aggregation : Link between any kind of objects. Can also be defined across components with
root or access object as target.

 

The component name is the component from which the linked object it taken (mostly BT except for some associations).

 

Flag STOREL indicates wether the relation is a static one,

 

Runtime

 

The runtime class for the component is CL_CRM_INTLAY_BTIL, it is called for instance to read orders, maintain orders, execute query.

 

Then there is a runclass defined for each object, as we saw it can be defined in the object table or not, let's see how the system find the right class for each objecct :

 

Class CL_CRM_OBJ_FACTORY_BTIL is used to determine the runtime instance for an Object :

    • First the system try to find an entry in the customer objects table : CRMC_OBJ_BTIL_C
    • If nothing is found the standard table CRMC_OBJ_BTIL is read
    • In case an entry is found in any of these tables the name of the class if obtained by adding _RUN_BTIL after the Handler class name defined in the table.
    • If there is no entry in tables the class name is the concatenation of : 'CL_CRM' <INTOBJ_NAME> '_RUN_BTIL', where <INTOBJ_NAME> is the name of BOL object without the 2 first characters, for instance BTOrder become ORDER.

 

Read an order

 

This is what is called in order to read an order from the GENIL :

  • CL_CRM_BTIL->GET_OBJECTS
  • CL_CRM_INTLAY_BTIL->READ_ORDERS
  • Then the READ_ATTRIBUTES method for the root object runtime instance (CL_CRM_ORDER_RUN_BT) is called.
  • Then the children are read using relationships and runtime instances for the requested objects (recursively), examples : CL_CRM_DATESSET_RUN_BTIL->READ_ATTRIBUTES.

 

Modify an order

 

This is what is called in order to modify an order into the GENIL :

  • CL_CRM_BTIL->MODIFY_OBJECTS
  • CL_CRM_INTLAY_BTIL->MAINTAIN_ORDERS
  • CL_CRM_INTLAY_BTIL->FILL_MAINTAIN_STRUCTURE
  • Then runtime instance of Root object CL_CRM_ORDER_RUN_BTIL->MAINTAIN_ATTRIBUTES
  • Then the children runtime instances (recursively), for instance : CL_CRM_ADMINH_RUN_BTIL->MAINTAIN_ATTRIBUTES

 

Query execution

 

When using a dynamic search object :

  • CL_CRM_BTIL->GET_DYNAMIC_QUERY_RESULT
  • CL_CRM_INTLAY_BTIL->EXECUTE_DYNAMIC_QUERY
  • Then runtime instance defined for the bol object of the query, for instance CL_CRM_QSLSORD_RUN_BTIL->GET_DYNAMIC_QUERY_RESULT.

 

Method execution

 

In order to execute a method of the bol object :

  • CL_CRM_BTIL-> EXECUTE_OBJECT_METHOD
  • Runtime instance of the object, for instance CL_CRM_PARTNERSET_RUN_BTIL->(method_name)

 

Customer extensions

 

It's easy to extend the genil for business transaction using the customizing :

Capture_cust.PNG

Extend Model for Business Transactions with New Nodes :

 

In this activity, you can exend the object model for business transactions with additional nodes. All nodes used in business transactions must be available in this table.

 

You can also change existing nodes if you want for instance to change the attributes structure in order to add your own attributes (useful for dynamic querie).

 

When using AET entries are added automatically.

 

Extend Model for Business Transactions with New Relations :

 

In this activity, you can exend the object model for business transactions with additional relations. All relations used in business transactions must be available in this table.

 

You can also add object relations that are derived from Customizing entries. For example, you have created your own partner function <xxxxxx> and would like to read all partners with this partner function. To do this, you create a relation BTPartner_<xxxxxx>, it's the same for Date type as well.

 

When using AET entries are added automatically.

 

Define Custom Handler Classes for Business Transaction Model :

 

In this activity, you specify the handler classes you want to use for nodes you have added to the business transaction model, or the customer handler classes you want to use for  existing nodes in order to replace standard behaviour.

 

To do this, you specify the name of the relevant BOL object (node) and the name of the handler class. Handler classes you create should inherit the methods from the SAP handler class so that you only need to program the delta required. The settings you make override the handler classes used as standard and therefore affect the standard behavior of the objects.

 

Your class name should end with '_RUN_BTIL', but you must not put that in the Handler class name defines in the customer objects table, for instance if your class name is ZCL_CRM_ADMINH_RUN_BTIL, just put ZCL_CRM_ADMINH in the table.


Attaching a Smartform in documents tab of contract using Actions

$
0
0

Attaching a Smartform in documents tab of contract using Actions

 

In our requirement we have to attach a smartform in documents tab of contract page.There is a method - CRM_GRM_GAGCONT_EXEC_SMARTFORM in standard class -CL_DOC_PROCESSING_CRM_ORDER , which can execute the smartform and attach the same in the one-order object.However the same cannot be used if the importing parameters of your smart form function module and the FM used in this method are different. In such a case you have to create your own custom method and class to achieve the requirement.I have detailed the steps below.

1)      First you have to create your own class with CL_SF_PROCESSING_PPF as superclass.

1.JPG

This will inherit a method - EXEC_SMART_FORM , which is used later to write your own method.

2)      Copy this method EXEC_SMART_FORM and paste as a new method.(this will copy all the attributes and source code of inherited method to your own method.) You can change the name of method if you wish to.

2.JPG


3)      Double click on the new method and copy the code as below : (You can copy the parameters from method EXEC_SMART_FORM )

* generally used constants of generic order
INCLUDE: crm_direct.

DATA:
control_parameters
TYPE ssfctrlop.

* file data
DATAls_fileinfo    TYPE sdokfilaci,
lt_fileinfo   
TYPE sdokfilacis,
lt_content_bin
TYPE sdokcntbins,                   "#EC NEEDED
lv_timestamp  
TYPE timestampl.

DATA: ls_output_info     TYPE  ssfcrescl.

* document error
DATAls_error       TYPE skwf_error.

* property data
DATA: ls_property    TYPE sdokpropty,
lt_properties 
TYPE sdokproptys,
ls_business_object
TYPE sibflporb,
lv_object_guid    
TYPE crmt_object_guid.

* function name
DATA: function_name TYPE rs38l_fnam,
dummy
(254)        TYPE c,
ls_archive_index 
TYPE toa_dara,
ls_orderadm_h    
TYPE crmt_output_orderadm_h_com,
ls_activity_h    
TYPE crmt_output_activity_h_com,
ls_opport_h      
TYPE crmt_output_opport_h_com,
ls_orgman_h      
TYPE crmt_output_orgman_h_com,
lt_partner_h     
TYPE crmt_output_partner_h_comt,
ls_pricingdata_h 
TYPE crmt_output_pricingdata_h_com,
ls_sales_h       
TYPE crmt_output_sales_h_com,
ls_shipping_h    
TYPE crmt_output_shipping_h_com,
lt_payplan_d_h   
TYPE crmt_output_payplan_d_h_comt,
ls_customer_h    
TYPE crmt_output_customer_h_com,
ls_cumulat_h    
TYPE crmt_output_cumulat_h_com,
lt_billing_h    
TYPE   crmt_output_billing_h_comt,
lt_cancel_h     
TYPE   crmt_output_cancel_h_comt,
lt_appointment_h
TYPE   crmt_output_appointment_h_comt,
lt_billplan_d_h 
TYPE   crmt_output_billplan_d_h_comt,
lt_billplan_h   
TYPE   crmt_output_billplan_h_comt,
lt_status_d_h   
TYPE   crmt_output_status_d_h_comt,
lt_status_h     
TYPE   crmt_output_status_h_comt,
lt_srv_subject_h
TYPE   crmt_output_srv_subject_h_comt,
lt_srv_reason_h 
TYPE   crmt_output_srv_reason_h_comt,
lt_srv_result_h 
TYPE   crmt_output_srv_result_h_comt,
ls_acs_h        
TYPE   crmt_acs_h_com,
lt_orderadm_i   
TYPE   crmt_output_orderadm_i_comt,
lt_orgman_i     
TYPE   crmt_output_orgman_i_comt,
lt_pricingdata_i
TYPE   crmt_output_pricingdata_i_comt,
lt_pricing_i    
TYPE   crmt_output_pricing_i_comt,
lt_product_i    
TYPE   crmt_output_product_i_comt,
lt_sales_i      
TYPE   crmt_output_sales_i_comt,
lt_shipping_i   
TYPE   crmt_output_shipping_i_comt,
lt_schedlin_i   
TYPE   crmt_output_schedlin_i_comt,
lt_customer_i   
TYPE   crmt_output_customer_i_comt,
lt_partner_i    
TYPE   crmt_output_partner_i_comt,
lt_item_cstics_i
TYPE   crmt_item_cstics_tab,
lt_billing_i    
TYPE   crmt_output_billing_i_comt,
lt_cancel_i     
TYPE   crmt_output_cancel_i_comt,
lt_finprod_i    
TYPE   crmt_output_finprod_i_comt,
lt_ordprp_i     
TYPE   crmt_output_ordprp_i_comt,
lt_appointment_i
TYPE   crmt_output_appointment_i_comt,
lt_billplan_d_i 
TYPE   crmt_output_billplan_d_i_comt,
lt_billplan_i   
TYPE   crmt_output_billplan_i_comt,
lt_orderadm_i_qt
TYPE   crmt_output_orderadm_i_qt_comt,
lt_orderadm_i_in
TYPE   crmt_output_orderadm_i_in_comt,
lt_schedlin_i_cf
TYPE   crmt_output_schedlin_i_cf_comt,
lt_status_i     
TYPE   crmt_output_status_i_comt,
lt_working_set_e_s_bbp
TYPE /1cn/working_set_e_s_bbp_t,
lv_language
LIKE  sy-langu,
lo_order
TYPE REF TO cl_doc_crm_order,
lv_status
TYPE jstat,
lt_status
TYPE TABLE OF jstat,
lv_error_tab_wrk
TYPE ssferror,
ls_output_options
TYPE ssfcompop.

DATAlv_size    TYPE  i,
lv_pdf    
TYPE  xstring.

DATA  lt_contract_survey TYPE crmt_survey_api_contract_prnt2.
CONSTANTSlc_mimetype    TYPE w3conttype VALUE 'application/pdf'.


* fill internal structure for the output options
ls_output_options
= is_output_options.

lo_order ?= io_appl_object
.
lv_object_guid
= lo_order->get_crm_obj_guid( ).

* get the function name for this smart form
CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
EXPORTING
formname          
= ip_smart_form
IMPORTING
fm_name           
= function_name
EXCEPTIONS
no_form           
= 1
no_function_module
= 2
OTHERS             = 3.
IF sy-subrc <> 0.
*   add an error message to processing protocol
MESSAGE i015(sppf_media) WITH ip_smart_form INTO dummy.
CALL METHOD cl_log_ppf=>add_message
EXPORTING
ip_problemclass
= '1'
ip_handle      
= ip_application_log.
EXIT.
ENDIF.

* determine the tables of the application
CALL FUNCTION 'CRM_OUTPUT_SINGLE_READ'
EXPORTING
iv_object                
= io_appl_object
iv_smart_form            
= ip_smart_form
IMPORTING
es_output_orderadm_h     
= ls_orderadm_h
es_output_activity_h     
= ls_activity_h
es_output_opport_h       
= ls_opport_h
es_output_orgman_h       
= ls_orgman_h
es_output_pricingdata_h  
= ls_pricingdata_h
es_output_sales_h        
= ls_sales_h
es_output_shipping_h     
= ls_shipping_h
et_output_partner_h      
= lt_partner_h
et_output_payplan_d_h    
= lt_payplan_d_h
es_output_customer_h     
= ls_customer_h
es_output_cumulat_h      
= ls_cumulat_h
et_output_billing_h      
= lt_billing_h
et_output_cancel_h       
= lt_cancel_h
et_output_appointment_h  
= lt_appointment_h
et_output_billplan_d_h   
= lt_billplan_d_h
et_output_billplan_h     
= lt_billplan_h
et_output_status_d_h     
= lt_status_d_h
et_output_status_h       
= lt_status_h
et_output_srv_subject_h  
= lt_srv_subject_h
et_output_srv_reason_h   
= lt_srv_reason_h
et_output_srv_result_h   
= lt_srv_result_h
es_output_acs_h          
= ls_acs_h
et_output_orderadm_i     
= lt_orderadm_i
et_output_orgman_i       
= lt_orgman_i
et_output_pricingdata_i  
= lt_pricingdata_i
et_output_pricing_i      
= lt_pricing_i
et_output_product_i      
= lt_product_i
et_output_sales_i        
= lt_sales_i
et_output_shipping_i     
= lt_shipping_i
et_output_schedlin_i     
= lt_schedlin_i
et_output_partner_i      
= lt_partner_i
et_item_cstics_i         
= lt_item_cstics_i
et_output_customer_i     
= lt_customer_i
et_output_billing_i      
= lt_billing_i
et_output_cancel_i       
= lt_cancel_i
et_output_finprod_i      
= lt_finprod_i
et_output_ordprp_i       
= lt_ordprp_i
et_output_appointment_i  
= lt_appointment_i
et_output_billplan_d_i   
= lt_billplan_d_i
et_output_billplan_i     
= lt_billplan_i
et_output_orderadm_i_qt  
= lt_orderadm_i_qt
et_output_orderadm_i_in  
= lt_orderadm_i_in
et_output_schedlin_i_cf  
= lt_schedlin_i_cf
et_output_status_i       
= lt_status_i
et_output_wrk_set_e_s_bbp
= lt_working_set_e_s_bbp
ev_language              
= lv_language.


* fill the survey table.
CALL FUNCTION 'CRM_CONTRACT_SURVEY_PRINT_TC'
EXPORTING
iv_header_guid       
= ls_orderadm_h-guid
iv_language          
= lv_language
IMPORTING
et_survey_print_table
= lt_contract_survey.

* set mail title
SELECT SINGLE caption INTO ls_output_options-tdtitle
FROM stxfadmt WHERE
formname
= ip_smart_form AND
langu   
= lv_language.

IF ls_output_options-tdtitle  IS INITIAL.
SELECT SINGLE caption INTO ls_output_options-tdtitle
FROM stxfadmt WHERE
formname
= ip_smart_form.
ENDIF.

*CONCATENATE ' ' '&' into ls_output_options-tdtitle.

*  ls_output_options = is_output_options.
*  ls_output_options-tdtitle = text-001.
REPLACE '&' WITH ls_orderadm_h-object_id
INTO ls_output_options-tdtitle.


*---------- is_mail_appl_obj -------------------------------------------
* fill this parameter if your application object is a BOR object
* the output will be connected with the BOR object via SAP Office
* this is done for mail and fax but not for print outputs
*   ------>
* is_mail_appl_obj-LOGSYS    =
* is_mail_appl_obj-OBJTYPE   =
* is_mail_appl_obj-OBJKEY    =
* is_mail_appl_obj-DESCRIBE  =
*   ------>
*-----------------------------------------------------------------------

*-----------language of smart form--------------------------------------
* determin here the language of the smart form
control_parameters
= is_control_parameters.
control_parameters
-langu = lv_language.
control_parameters
-GETOTF = 'X'.
*-----------------------------------------------------------------------

*-----------fill archive parameters for archive link -------------------
IF is_output_options-tdarmod = '2' OR
is_output_options
-tdarmod = '3'.
*   archive_index_tab
READ TABLE ct_archive_index_tab INTO ls_archive_index INDEX 1.
ls_archive_index
-object_id      = ls_orderadm_h-guid.
IF ls_archive_index-object_id IS INITIAL.
DELETE ct_archive_index_tab INDEX 1.
ELSE.
MODIFY ct_archive_index_tab FROM ls_archive_index INDEX 1.
ENDIF.
ENDIF.
*-----------------------------------------------------------------------

* call function to process smart form
CALL FUNCTION function_name
EXPORTING
archive_index       
= is_archive_index
archive_index_tab   
= ct_archive_index_tab
archive_parameters  
= is_archive_parameters
control_parameters  
= control_parameters
mail_appl_obj       
= is_mail_appl_obj
mail_recipient      
= is_mail_recipient
mail_sender         
= is_mail_sender
output_options      
= ls_output_options
user_settings       
= ip_user_settings
orderadm_h          
= ls_orderadm_h
activity_h          
= ls_activity_h
opport_h            
= ls_opport_h
orgman_h            
= ls_orgman_h
partner_h           
= lt_partner_h
pricingdata_h       
= ls_pricingdata_h
sales_h             
= ls_sales_h
shipping_h          
= ls_shipping_h
payplan_d_h         
= lt_payplan_d_h
cumulat_h           
= ls_cumulat_h
customer_h          
= ls_customer_h
acs_h               
= ls_acs_h
billing_h           
= lt_billing_h
cancel_h            
= lt_cancel_h
appointment_h       
= lt_appointment_h
billplan_d_h        
= lt_billplan_d_h
billplan_h          
= lt_billplan_h
status_d_h          
= lt_status_d_h
status_h            
= lt_status_h
srv_subject_h       
= lt_srv_subject_h
srv_reason_h        
= lt_srv_reason_h
srv_result_h        
= lt_srv_result_h
orderadm_i          
= lt_orderadm_i
orderadm_i_qt       
= lt_orderadm_i_qt
orderadm_i_in       
= lt_orderadm_i_in
orgman_i            
= lt_orgman_i
pricingdata_i       
= lt_pricingdata_i
pricing_i           
= lt_pricing_i
product_i           
= lt_product_i
sales_i             
= lt_sales_i
schedlin_i          
= lt_schedlin_i
schedlin_i_cf       
= lt_schedlin_i_cf
shipping_i          
= lt_shipping_i
partner_i           
= lt_partner_i
item_cstics_i       
= lt_item_cstics_i
customer_i          
= lt_customer_i
billing_i           
= lt_billing_i
cancel_i            
= lt_cancel_i
finprod_i           
= lt_finprod_i
ordprp_i            
= lt_ordprp_i
appointment_i       
= lt_appointment_i
billplan_d_i        
= lt_billplan_d_i
billplan_i          
= lt_billplan_i
status_i            
= lt_status_i
working_set_e_s_bbp 
= lt_working_set_e_s_bbp
contract_main       
= lt_contract_survey
language             = lv_language
IMPORTING
document_output_info
= es_document_output_info
job_output_info     
= es_job_output_info
job_output_options  
= es_job_output_options
EXCEPTIONS
output_canceled     
= 1
parameter_error     
= 2
OTHERS               = 3.

* Generate OTF data
CALL METHOD cl_grm_gag_documents=>generate_otf_data
EXPORTING
lt_output_info
= es_job_output_info-otfdata
IMPORTING
it_ev_size    
= lv_size
ev_pdf        
= lv_pdf.


*/Set file info and file content
ls_fileinfo
-file_size  = lv_size.
ls_fileinfo
-binary_flg = 'X'.
ls_fileinfo
-first_line = 1.
ls_fileinfo
-file_name  = 'contract1'.
ls_fileinfo
-mimetype   = lc_mimetype.


*/Convert binary data
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer     = lv_pdf
TABLES
binary_tab
= lt_content_bin.

DESCRIBE TABLE lt_content_bin LINES ls_fileinfo-last_line.

*/Get filename
IF ls_fileinfo-file_name IS INITIAL.
GET TIME STAMP FIELD lv_timestamp.
ls_fileinfo
-file_name = lv_timestamp.
SHIFT ls_fileinfo-file_name LEFT DELETING LEADING space.
WRITE ls_fileinfo-file_name+15(7) TO ls_fileinfo-file_name+14.
CONCATENATE ls_fileinfo-file_name '.pdf' INTO ls_fileinfo-file_name.
ENDIF.
APPEND ls_fileinfo TO lt_fileinfo.

*/Set properties (description & title)
ls_property
-name  = skwfc_prop_description.

CALL METHOD cl_grm_gag_documents=>get_next_version_name
EXPORTING
iv_guid        
= lv_object_guid
iv_name        
= ls_fileinfo-file_name
RECEIVING
rv_next_version
= ls_property-value.

*  ls_property-value = cl_grm_gag_documents=>GET_NEXT_VERSION_NAME( lv_object_guid ).
APPEND ls_property TO lt_properties.
ls_property
-name  = skwfc_prop_relative_url.

CALL METHOD cl_grm_gag_documents=>get_next_version_name
EXPORTING
iv_guid        
= lv_object_guid
iv_name        
= ls_fileinfo-file_name
RECEIVING
rv_next_version
= ls_property-value.

*  ls_property-value =  cl_grm_gag_documents=>GET_NEXT_VERSION_NAME( lv_object_guid ).
APPEND ls_property TO lt_properties.

ls_business_object
-instid = lv_object_guid.
ls_business_object
-typeid = 'BUS2000112'.
ls_business_object
-catid  = 'BO'.   "instance of BOR object type


** create document by using cl_crm_documents
CALL METHOD cl_crm_documents=>create_with_table
EXPORTING
business_object    
= ls_business_object
properties         
= lt_properties
file_access_info   
= lt_fileinfo
*     FILE_CONTENT_ASCII  =
file_content_binary
= lt_content_bin
raw_mode           
= 'X'
*     TEXT_AS_STREAM      =
*     PARENT_FOLDER       =
IMPORTING
error              
= ls_error.
IF NOT ls_error IS INITIAL.
*    RAISE failed.
ENDIF.

4)      save and activate.

5)      Now you have to create your actions in action profile using below path :

SPRO->CRM->BASIC FUNCTION->ACTIONS->ACTIONS IN TRANSACTION->CREATE ACTION WITH WIZARD

6)      Wizard screen will open give the details as below or as per your needs

6.JPG


7)      I am using an existing action (You can create your action and assign it to the transaction for which you want the smartform to be attached.) Click on continue

8)      Give you are action definition name and description.

 

8.JPG

 

9)      Give processing time as “Processing when saving document” and check schedule automatically” if that is your requirement.

9.JPG

10)  Enter partner function for which you determination to happen. I have given sold to party – 00000001


10.JPG


11)  Now select the processing type as Smart Forms Print from the given options.

11.JPG

12)  Now comes the most important part. Here you have to give your smart form that you want to save, the class and method that we have created initially. This step links your code with the function settings of actions.

12.JPG

13)  Give Action description and navigate to complete.

13.JPG

14)  Schedule conditions and start conditions can be defined in the define conditions using path Actions->actions in transaction->change action and conditions->define condition

15)  Look for the action defined and double click. Here you can see your actions definition in right.

15.JPG

16)  Click on the schedule and start condition tabs to create your conditions. Shown below:

     i.e. action will start here when the above conditions are met.In my case when the contract is set to fully executed.

16.JPG

16b.JPG

17)  Now everything is done. We will check the output now.

18) log in to web ui and open the contract.

18.JPG

19) make the status fully executed ( for which the action has been scheduled) and save.

20) Eureka Eureka !!!!!!!!!!!! Document is attached in attachments block.

20.JPG

21) Click on the link and smart form will open in another tab.

21.JPG

Smart Input Help / Suggested values

$
0
0

Introduction

 

Smart value help allows to see the 5 last entries or a list of suggestion based on what you have entered.

 

You can see the application help for more details on the functionnalities :

Smart Input Help - Generic User Interface Functions - SAP Library

 

 

 

Smart value help script

 

First let's have a look in the BSP application THTMLB_SCRIPTS, page scripts_.oo_smart_value_help.js

 

Here is the javascript that handles this functionality :

 

<%--
  Smart Value Help(SVH) - Handles the last five values and suggested values for a F4 Inputfield.

  There are two main types of F4
  1) Simple F4 - Values are ready on the popup
  2) Complex F4 - Values are not ready on the popup until user clicks search button Ex: F4 opens an Advanced Search view

  In the first case(simple F4) both last entries and f4 suggested values are provided, in the complex F4 only the last
  entries will be provided.
  --%>


var thtmlbCSVHMangerSingleton = thtmlbClass.create();
thtmlbCSVHMangerSingleton.prototype = {
   alertTimerId:                 0,
   inputField:                   "",
   sicfPath:                     "/webcuif/uif_callback",
   lastFiveHandler:              "CL_THTMLB_SMART_VALUE_HELP",
   svhHandler:                   "CL_THTMLB_F4HELP",
   displayBackgroundSearch:      "backgroundSearch",
   displayLastFive:              "lastFive",
   displaySmartValueHelp:        "smartValueHelp",
   saveInputFieldId:             "saveId",
   timeDelayMS:                  200,
   helpId:                       "",
   helpInputMapping:             "", <%-- value retrived by the backend --%>
   helpInputMappingParser:       "", <%-- value passed to the backend for F4 --%>
   helpOutputMapping:            "",
   pageId:                       "",
   helpType:                     "",
   popupF4InputField:            "", <%-- inputfieldF4 used in a comples F4 popup --%>
   controller:                   "",
   view:                         "",
   svhType:                      "", <%-- backgroundSearch ,lastFive or both --%>
   numberOfCharacters:           1,
   tooManyRecords:               "(Too Many Records...)",

 

 

Here we can see the handling classes for last five entries : CL_THTMLB_SMART_VALUE_HELP and for the suggested values : CL_THTMLB_F4HELP.


The attribute "numberOfCharacters" define the number of characters that need to be entered before the function is activated.


Below in the page we see the code that handles behaviour on key entered :

 

       <%-- enter, shift or escape (hide the context menu if its opened)--%>
       <%-- hide the context menu for function keys, capslock and other keys --%>
       contextMenu = document.getElementById("thtmlb_contextMenu__items");
       if ((charCode < 47 && !(charCode == 8 || charCode == 46 || charCode == 38 || charCode == 40 )) || ( charCode >= 112 && charCode <= 123))
             thtmlb_hideContextMenu();
             <%-- backspace --%>
           else if (charCode == 8 ){

         ...



If you want suggested values to appear every time (and not only when you hit backspace) you have to change the code like this (code added in green) :

<%-- any other character --%>
                                    } else if (thtmlbCSVHManger.inputField.value.length >= thtmlbCSVHManger.numberOfCharacters ){
                                           <%--       thtmlbCSVHManger.alertTimerId = window.setTimeout('thtmlbCSVHManger.svhFrontEndSearch();',thtmlbCSVHManger.timeDelayMS);   --%>
                                           thtmlbCSVHManger.alertTimerId = window.setTimeout('thtmlbCSVHManger.ajaxCallRequest(thtmlbCSVHManger.displaySmartValueHelp);',thtmlbCSVHManger.timeDelayMS);
                                             }



Handling class for Smart value help


So the class  CL_THTMLB_F4HELP is called to handle the request :


2014-03-28_16-14-14.png

 

In this class we can see that the suggested values actually come from a search help (defined in SE11).


The search help has to use dialog type "Display value immediately".


So basically, every field can be assigned a search help in order to get suggested values, in order to do that you need to assign a search help to the data element of the field.

 

 

 

Example : Address check/completion using an external Web Service.


I have defined a search help based on an exit :

2014-03-28_16-47-22.png


In the function module ZDQE_SHLP_LIBRE I call the web service that will search for address.


I create a data element to enter a free text, with reference to this search help :

2014-03-28_16-26-25.png


Important : for my need the class CL_THTMLB_F4HELP, METHOD HANDLE_REQUEST has to be modified by removing the * in prefiltering value, else values are filtered according to value entered in the field by the user, so you only see them if they contain the pattern defined with the *.


Now I can use my field to enter free address and get suggested values (example with french address) :

2014-03-28_17-08-46.png


The web service I used return only one value (the closest to what the user entered), you can see here what happened after entering "rue jacques paris", but of course you can have a list of results to choose from.









How to implement a simple character counter

$
0
0


In the blog we talk about the social media integration into CRM Interaction center agent inbox, which allows end users to directly reply facebook posts & tweets in CRM system. In order to achieve this we implement a very simple character counter which has similar functionality as that in twitter website:


clipboard1.png

The counter described in this blog is delivered by SAP in CRM 7.0 EHP3. You can find it in UI component SMCOV, view ReplyMessageView.htm. Its function is slightly different from twitter: it does not display the left number of characters which is allowed to input, but the number of characters which are ALREADY typed by end user.

 

 

The following functionalities are supported by this simple counter:

 

1. Whenever you add, delete or change your input in text area, the number of characters you have typed will be automatically updated below the text area

2. The cut & paste event are also captured by the counter

 

Any other advanced functionalities like url automatically highlighted as hyperlink is not supported by this simple counter.


clipboard2.png

Here below is the implementation of this counter:

 

1. In your UI component view, draw the visual area of the counter which will show the text "The number of characters entered:XXX" via html p tag:

 

<p id="COUNTER_TXT"  style="margin-left: 8px; margin-top: 1px; margin-bottom: 4px; margin-right: 8px; color:rgb(102,102,102)"></p>

 

2. in line 32, the function handleClick acts as the event handler which will be called automatically whenever the content in the text area is changed.

 

In line 34 the final text in the visual area of the counter is populated.

For translation purpose, the text is not hardcoded but stored and got in Online Text Repository:

 

 

lv_title_prefix = cl_wd_utilities=>get_otr_text_by_alias( 'CRM_UIU_SOC_SMC/COUNTER_TXT' ). "#EC NOTEXT.

 

lv_title_prefix = lv_title_prefix && ':'.

 

In line 35 the current number of characters is updated in the visual area of the counter. The variable counter is a DOM variable which represents the html tag p with ID COUNTER_TXT, which is retrieved later.

clipboard3.png

 

The function fillTitle is designed to ensure the counter still displays the correct number of typed characters in text area when the UI is rendered for the first time ( at that time no event for the text area will be fired ).

 

Here the biggest challenge is how to get the ID of the text area, so that we can use document.getElementByID to get its DOM node and then get the actual content which has been typed in the text area.

 

As this text area is not manually created via native HTML code, it is impossible to get its ID directly in the view.

 

clipboard4.png

However, we could investigate on the naming convention of this ID via development tool ( just clikc F12 on web page) of IE.

 

Click Find->Select element by click, then click on the text area, and development tool will automatically locate the native html textarea element, and then we can observe that the naming convention for it is <component_id>_socialpost_struct.content.

clipboard5.png


The current component id is available in the attribute component id of view controller, so finally we can populate the ID of text area via the following code:

clipboard6.png

3. The left code is very easy to understand: retrieve the DOM node for input textarea ( line 47) and visual area of counter( line 48), and register the event handler for three events on text area element.

clipboard7.png

How to investigate BSP tag issue by yourself

$
0
0


The "BSP tag" I mentioned in the blog title means for example the tag chtmlb:configCellerator below which is pre-delivered by SAP and you could include it in your UI component view to draw various UI element.

clipboard1.png

In this blog I will share with you a real issue I meet with when I am using configCellerator and how I find the root cause. So sometimes if you find the behavior of BSP tag is not working as you expected, if time allowed, you can spend sometime to investigate by yourself. Such investigation will make you understand how native html is rendered based on those BSP tag more thoroughly.

 

 

Issue1 - Missing table toolbar

 

Although the entry for table toolbar is found in debugger,

clipboard2.png

However in the UI the table toolbar is missing.

clipboard3.png

I guess the issue might be related to attribute "editMode" set in line 12. I try to find documentation on this attribute in SE80 but unfortunately there is not.

clipboard4.png

Then double click on line 6 ( the first sceenshot in this blog) "configCellerator", in the new screen click tab "Attribute", now I get to know the possible value for editMode is NONE, SINGLE and ALL. However still I don't know the clear meaning of these three values.

clipboard5.png

So below is how I investigate on the usage of attribute "editMode":

 

1. Do observation on the callstack of UI rendering and I find framework is using CL_THTMLB_CELLERATOR to render the table defined via configCellerator.

clipboard6.png

2. run report RS_ABAP_SOURCE_SCAN,  search key word EDITMODE against class CL_THTMLB_CELLERATOR found in step1.

Navigate to the source code of the third hit:

clipboard7.png

it means if the editmode is set as NONE, the member attribute NOHEADER of class CL_THTMLB_CELLERATOR is set as TRUE.

clipboard8.png

2.1 rerun report RS_ABAP_SOURCE_SCAN but this time search keyword NOHEADER instead ( or you can also use where used list on member attribute NOHEADER in SE24 ). The hit shows that the string "TRUE"( or "FALSE") stored in member attribute NOHEADER is converted to abap boolean and stored in variable gv_no_header.


clipboard9.png

2.2 repeat the step 2 and 2.1, search keyword gv_no_header. The third hit demonstrates the table toolbar could only be rendered if gv_no_header is abap_false ( and other condition between line 10 and 14 are fulfilled ). The html source is the final native html code rendered by UI framework.


clipboard10.png

After I remove the editMode attribute and I could see the expected table toolbar.

clipboard11.png

I view the source code of my table view and I could find the hard coded html code in the line 17 of screenshot above. And the html code for toolbar title is just very next to it. So this issue is just resolved without debugging, but just pure source code analysis in the design time.


clipboard12.png

Issue2 - Do not expect the table cell editable

 

In my project I need to switch the BOL entity to change mode, however I do not want to make each table cell be editable, instead user will edit the locked object in another UI and see result after edit in the table view. In the table view I expect each cell is read only.

clipboard13.png

My BOL model has 40 attributes and I would not like to code "rv_disabled = TRUE" 40 times in each GET_I_<XXXX> method.

 

I plan to investigate the attribute usage = "ASSIGNMENTBLOCK"

Here below is my analysis process:

 

1. run report RS_ABAP_SOURCE_SCAN, search keyword ASSIGNMENTBLOCK, class CL_THTMLB_CELLERATOR - no result

2. double click tag configCellerator, then find the element handler class name:

clipboard14.png

then run report again and change the search class to CL_CHTMLB_CONFIG_CELLERATOR.

 

only one result which points to the commented out code. Just ignore it.

clipboard15.png

3. find all possible value for usage attribute here, and run report again with search keyword = "SEARCHRESULT":

Only one result hit:

clipboard16.png

and the code indicates that the usage attribute "SEARCHRESULT" will set the whole table to read only mode.

clipboard17.png

After I change the attribute and test the table cells are rendered as read only as I expect.

 

Summary

 

In some case it might be possible that you can not get quite promising result by the first RS_ABAP_SOURCE_SCAN execution.

 

The tip is how to specify search keyword and search class ( or package, report etc ) cleverly so that the search result are relevant and helpful for your further investigation. Ususally it would take several iterations before you reach your target, as are shown in my two examples in this blog.

 

 

My common experience to specify search selection for report RS_ABAP_SOURCE_SCAN is:

 

1. Try to find the very class ( or report, function group etc ) which is relevant. If it is difficult to identify the exact one, use * for example "CL_CHTMLB*".

2. Find the package name of the class( or report, function group etc ), and run report against the package instead.

clipboard18.png

Reference

 

there is a useful blog written by Andrei Vishnevsky about creating a new BSP tag and its corresponding element handler class. By reading it you will get a deeper understanding how the element handler class takes part in the UI render process.

Conditional formatting for Tables in WebUI

$
0
0

In comments to one of my blog posts I indicated that conditional formatting in tables was quite awaited feature for me. There are plenty of scenarios when we need to highlight or indicate or distinguish something in tables in WebUI. And previous tricks were quite difficult to implement.

Just take a look at these discussions:

How to set particular column or cell color of result view in CRM WEB UI ?

How to set background color of cell to red

 

I expected that standard implementation would finally take into account already existed P_STYLE and/or P_CLASS changing parameters from IF_HTMLB_TABLEVIEW_ITERATOR~RENDER_CELL_START method. But it does not. I've already described the issue with table iterator and those parameters in this message Re: How to set particular column or cell color of result view in CRM WEB UI ?

 

And here is the good news. As mentioned in SAP User Interface Technologies - Road Map (page 25) "Conditional formatting for Tables" is already available. Partially I think... Now it's much easier to format tables based on some conditions. But "formatting" here means only table cell coloring. I was really surprised because I (again) expected more flexibility in formatting like an option to set styles or CSS classes to cells or rows. Yes, setting background color covers most of the needs. But "coloring" is not "formatting".

 

To be completely sure that there is nothing else in shortly upcoming SPs I raised OSS message and received the oficial answer:

As per development there's no new development considered regarding "Conditional Formatting of the cellerator's cells" at this current time and there is no further information concerning the topic.

 

So how to do conditional formatting? The note 1937399 - Conditional formatting of the table cells introduces this feature. Need to admit that this feature is available starting from SAP CRM 7.0 Ehp1. How-To Guide is attached to the note. With this note we get one more property for table cell. Property name is

if_bsp_wd_model_setter_getter=>fp_bgcolor (or simply 'backgroundColor'). We need to return color name or RGB hexcode as a string (for example 'pink' or '#FFC0CB').

 

So some GET_P method should simply look like:

 

METHOD get_p_<your_cell>.  IF iv_property EQ if_bsp_wd_model_setter_getter=>fp_bgcolor  AND <your_condition>.    rv_value = 'pink'. " or '#FFC0CB'  ENDIF.
ENDMETHOD.

 

The result is:

Cells.png

 

Cool! But what about a row? Certainly we can go and define as many GET_P methods to return this property as many attributes we have. And implement new GET_P method for each and every new attribute we add in the future.

 

Here I would suggest another way.

  • Define static attribute in your context node class which represents table of pairs 'index; color' for buffering.
  • Redefine GET_P_T_TABLE method of your context node class (inherited from CL_BSP_WD_CONTEXT_NODE_TV class).
  • Handle the backgroundColor property in this method passing all other properties to super-method.
  • For each new index trigger your condition and store the result in the index-color table.
  • Retrieve the color from the table if possible.

 

Here is a code example:

 

METHOD get_p_t_table.  DATA: lr_current TYPE REF TO if_bol_bo_property_access.  FIELD-SYMBOLS: <fs_color> TYPE zts_color.  IF iv_property = if_bsp_wd_model_setter_getter=>fp_bgcolor.    READ TABLE gt_colors ASSIGNING <fs_color> WITH KEY index = iv_index.    IF <fs_color> IS NOT ASSIGNED.      APPEND INITIAL LINE TO gt_colors ASSIGNING <fs_color>.      <fs_color>-index = iv_index.      lr_current = me->get_bo_by_index(          iv_index        = iv_index          iv_change_focus = abap_false ).      IF lr_current IS BOUND.
* There are some logic and conditions
* Here it's based on current line entity in zcl_util_misc=>get_color as an example        <fs_color>-color = zcl_util_misc=>get_color( lr_current ).      ENDIF.    ENDIF.    rv_value = <fs_color>-color.  ELSE.    CALL METHOD super->get_p_t_table      EXPORTING        component       = component        iv_index        = iv_index        iv_property     = iv_property        iv_display_mode = iv_display_mode      RECEIVING        rv_value        = rv_value.  ENDIF.
ENDMETHOD.

 

Where types and attribute are defined as:

PRIVATE SECTION.  TYPES:    BEGIN OF zts_color,          index TYPE i,          color TYPE string,         END OF zts_color .  TYPES:    ztt_color TYPE TABLE OF zts_color .  DATA gt_colors TYPE ztt_color .

 

Also we have to clean up gt_colors somewhere after completing the output. Because otherwise there will be issues during next rendering. For example when sorting is applied to this UI table. I used method CLEAR_LAST_LINE in context node class which is also inherited from CL_BSP_WD_CONTEXT_NODE_TV. It's called from handler of CL_BSP_WD_VIEW_CONTROLLER~OUTPUT_RENDERED event and satisfied my needs.

 

And here is the result:

Rows.png

 

Sure enough, it's easier and much better than previous approaches. But still there is a lack of flexibility... Anyway it should cover most of requirements. At least I hope so.

Checking for Duplicate Orders in SAP CRM Webclient UI

$
0
0

1.   Introduction

 

While creating sales orders from SAP or in WUI it is often possible that similar sales orders are placed more than once. In that case it would be nice to have a preventive measure by checking in the system if there are any existing sales orders that are similar to the one that is being created. Finding one probable duplicate order and being warned about the same will ensure that before we save a new order.

 

1. MAJOR FEATURES

 

When a sales order is entered in CRM, before saving it, it may be necessary to perform a duplicate order check in BADI ORDER_SAVE, depending on the conditions described below.

The duplicate order check should be activated for the combination of transaction type, sales area and media type. (For example: for standard orders within France the check is activated, for standard orders within the UK the check is deactivated).

Sales Area – The sales area provides a mechanism to turn the functionality on or off per country.

Transaction Type – Should be checked to make the difference between sales orders. (For example: excluding debit or credit memo’s.

Media Type – E.g. Fax, E-mail, telephone. (For example: web orders and EDI will be excluded

Contact Person – The check on contact person should be turned on or off per activation area above. Example: When the check on contact person is switched ON the check will only consider orders created for the same ship-to and contact person. When the check is turned OFF there is a check between all orders for a certain ship-to.

 

Number of days – A number of days should be included to know how many days back in time should be checked. This can differ per activation area. EG for France 8 days, for UK 4 days.

Number of items – In the as-is situation 4 items are checked. If 4 items are identical in 2 orders, the duplicate check sees the order as a possible duplicate order. In the to-be situation it must be possible that the number of items is variable per activation area above.

1. TECHNICAL DETAILS

 

For the purpose of the duplicate order check, key figures like sales organization, distribution channel, division, transaction type and media type is looked into for the order that is to be saved.

 

This is done using a table where certain flags and ranges are stored. For example, if the flag duplicate_order_check is enabled this means duplicacy needs to be checked. Similarly, if contact_per_chk is enabled then the contact person has to be compared as well. The days_chk determines maximum how many days back we should check for duplicates and for items_chk determines how many items need to be similar in order to qualify as a duplicate.

 

scn1.jpg

 

Once a duplicate order is found, the customer would like to be warned that what they are trying to create might already exist in the system. This can be done simply by using a popup message box. This part we can come it later but the main requirement is to find the details of the current sales order that is about to be saved.

 

 

CRM Web UI

 

In case of CRM Web UI, this BADI will not work properly. Our goal is to create a popup message box on the UI which is not possible from this BADI. To do so, we have to make a component enhancement for BT115H_SLSO in the component workbench and redefine the method EH_ONSAVE in the view (SOHOverView) which is the event handler for the save button.

 

In this case we do not work with GUID, Instead, here we need to fetch the data from the context of the current view, or sometimes the context of related views. Capturing this data can be done by browsing the context and fetching the data. Here is how the required data was fetched in this case.

 

*----------------------------------------------------------------------
* Constants
*----------------------------------------------------------------------
CONSTANTS:
  lco_e TYPE c VALUE 'E',
  lco_applid_10         TYPE yesfapplicationid   VALUE '0000000010',
  lco_exitid_23         TYPE yesfexitid          VALUE '0000000023',

lco_btorderheader     TYPE crmt_relation_name  VALUE 'BTOrderHeader',
lco_btheaderitext     TYPE crmt_relation_name  VALUE 'BTHeaderItemsExt',
lco_btheaderorgset    TYPE crmt_relation_name  VALUE 'BTHeaderOrgmanSet',
lco_btheaderpartset   TYPE crmt_relation_name  VALUE 'BTHeaderPartnerSet',
  lco_btpart_1          TYPE crmt_relation_name  VALUE 'BTPartner_PFT_0001_MAIN',
  lco_btpart_7          TYPE crmt_relation_name  VALUE 'BTPartner_PFT_0007_MAIN',
lco_btorderitemsall   TYPE crmt_relation_name  VALUE 'BTOrderItemAll',
lco_btitemprodext     TYPE crmt_relation_name  VALUE 'BTItemProductExt',
  lco_detailsvs         TYPE string VALUE 'DetailsVS.do',
  lco_admin_cat         TYPE string VALUE 'btadminh_category'.


REFRESH lt_items.

  IF gv_save_after_chk EQ abap_false.
* Get admin entity
lrf_btadminh_entity ?= me->typed_context->btadminh->collection_wrapper->get_current( ).

* Check if order is new or existing
lrf_btadminh_entity->get_properties( IMPORTING es_attributes = lst_header ).
IF lst_header-object_id IS NOT INITIAL.
*    the material is old and so dont check for duplicates
CALL METHOD super->eh_onsave
EXPORTING
  htmlb_event    = htmlb_event
htmlb_event_ex = htmlb_event_ex.
RETURN.
ENDIF.

* Get order details
lrf_btorder_entity ?= me->typed_context->btorder->collection_wrapper->get_current( ).

IF lrf_btorder_entity IS BOUND.
lrf_btorder_header ?= lrf_btorder_entity->get_related_entities( iv_relation_name = lco_btorderheader ).
lrf_header_entity  ?= lrf_btorder_header->if_bol_bo_col~get_current( ).
IF lrf_header_entity IS BOUND.
  lrf_itemsext       ?= lrf_header_entity->get_related_entities( iv_relation_name = lco_btheaderitext ).
lrf_orgman ?= lrf_header_entity->get_related_entities( iv_relation_name = lco_btheaderorgset ).
IF lrf_orgman IS BOUND.
lrf_orgman_entity ?= lrf_orgman->if_bol_bo_col~get_current( ).
IF lrf_orgman_entity IS BOUND.
lrf_orgman_entity->get_properties( IMPORTING es_attributes = lst_orgman ).
ENDIF.
ENDIF.
* Get partner related information to get sold to party and contact person
  lrf_partnerset     ?= lrf_header_entity->get_related_entities( iv_relation_name = lco_btheaderpartset ).
IF lrf_partnerset IS BOUND.
lrf_partner_entity ?= lrf_partnerset->if_bol_bo_col~get_current( ).
IF lrf_partner_entity IS BOUND.
  lrf_partnerstp ?= lrf_partner_entity->get_related_entities( iv_relation_name = lco_btpart_1 ).
IF lrf_partnerstp IS BOUND.
  lrf_partnerstp_entity ?= lrf_partnerstp->if_bol_bo_col~get_first( ).
  IF lrf_partnerstp_entity IS BOUND.
  lrf_partnerstp_entity->get_properties( IMPORTING es_attributes = lst_partnerstp ).
  ENDIF.
ENDIF.
  lrf_partnercont ?= lrf_partner_entity->get_related_entities( iv_relation_name = lco_btpart_7 ).
IF lrf_partnercont IS BOUND.
  lrf_partnercont_entity ?= lrf_partnercont->if_bol_bo_col~get_first( ).
  IF lrf_partnercont_entity IS BOUND.
  lrf_partnercont_entity->get_properties( IMPORTING es_attributes = lst_partnercont ).
  ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.

LOOP AT m_subcontrollers INTO lst_controller.
lrf_control ?= lst_controller-instance.
IF lrf_control IS BOUND.
IF lrf_control->controller_name CS lco_detailsvs.

LOOP AT lst_controller-fields INTO lst_field.
IF lst_field-name CS lco_admin_cat.
  lv_category_str = lst_field-value.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.

ENDLOOP.

* get organization data

lv_sales_org_c        = lst_orgman-sales_org_short.
lv_dis_channel_c      = lst_orgman-dis_channel.
  lv_division_c         = lst_orgman-division.

* get partner data

* For partner data created through WEB UI
* Sold-to-party is same as ship-to-party same as bill-to-party

* get sold to party structure in lst_partner_stp
  lv_ship_to_c          = lst_partnerstp-partner_no.

* get contact details in lst_partnercont
  lv_contact_c          = lst_partnercont-partner_no.

 

lv_transaction_type_c = lst_header-process_type.
lv_posting_date       = lst_header-posting_date.
lv_media_type_c       = lv_category_str.

* get item data in loop

lrf_itemsext_entity ?= lrf_itemsext->if_bol_bo_col~get_current( ).
IF lrf_itemsext_entity IS BOUND.
lrf_itemsall ?= lrf_itemsext_entity->get_related_entities( iv_relation_name = lco_btorderitemsall ).
ENDIF.
IF lrf_itemsall IS BOUND.
lrf_item_entity ?= lrf_itemsall->if_bol_bo_col~get_first( ).
ENDIF.

WHILE lrf_item_entity IS BOUND.
CLEAR lwa_items.
*   Get material number in lst_itemdetail
lrf_item_entity->get_properties( IMPORTING es_attributes = lst_itemdetail ).
*   Get material quantity in lst_itemquant
lrf_itemprod   ?= lrf_item_entity->get_related_entities( iv_relation_name = lco_btitemprodext ).
lrf_itemquant  ?= lrf_itemprod->if_bol_bo_col~get_first( ).
lrf_itemquant->get_properties( IMPORTING es_attributes = lst_itemquant ).
* append the data in item table
lwa_items-ordered_prod = lst_itemdetail-ordered_prod.
lwa_items-net_weight   = lst_itemquant-net_weight.
APPEND lwa_items TO lt_items.
*   Go to next record
lrf_item_entity ?= lrf_itemsall->if_bol_bo_col~get_next( ).
ENDWHILE.

 

Once we have this data we can perform the duplicate checking with the existing data using the same logic as before. (Checking for Duplicate Sales Orders in SAP CRM )

 

Now coming to the popup to confirm in web ui I had to create two more methods for this. One method is for displaying the popup and the other method is for capturing the result of the popup messagebox.

 

When we find a duplicate order, we pass the duplicate order name to the method eh_onconfirm_popup. where lv_order is the duplicate order number.

 

CALL METHOD me->eh_onconfirm_popup( iv_order = lv_order ).

 

This method contains the following logic to show the popup message. We use the standard create_popup_2_confirm for this.

 

*----------------------------------------------------------------------
* Constants
*----------------------------------------------------------------------
CONSTANTS:
lco_conf_close TYPE string VALUE 'CONFIRM_POPUP_CLOSED'.
*----------------------------------------------------------------------
* Processing Logic
*----------------------------------------------------------------------
CLEAR: lv_save, lv_text.
IF grf_confirm_popup IS NOT BOUND.

lv_save = text-t02.
lv_text = text-t01.
REPLACE ALL OCCURRENCES OF '&' IN lv_text WITH iv_order.

CALL METHOD comp_controller->window_manager->create_popup_2_confirm
EXPORTING
  iv_title          = lv_save
  iv_text           = lv_text
iv_btncombination = if_bsp_wd_window_manager=>co_btncomb_yesno
RECEIVING
  rv_result         = grf_confirm_popup.

CALL METHOD grf_confirm_popup->set_on_close_event
EXPORTING
  iv_view       = me
iv_event_name = lco_conf_close.

  ENDIF.
*Open the popup message
grf_confirm_popup->open( ).

 

Now once the popup message is answered, the result should be available in the event handler method EH_ONCONFIRM_POPUP_CLOSED

 

Here we capture the result of the popup and act accordingly.

 

 

*----------------------------------------------------------------------
* Constants
*----------------------------------------------------------------------
CONSTANTS:
  lco_yes TYPE string VALUE 'YES'.
*----------------------------------------------------------------------
* Variables
*----------------------------------------------------------------------
DATA:
  lv_answer TYPE string.
*----------------------------------------------------------------------
* Processing Logic
*----------------------------------------------------------------------
* Retrieve the answer
* this is where you can tell which button has been hold
CLEAR lv_answer.
lv_answer = grf_confirm_popup->get_fired_outbound_plug( ).
IF lv_answer EQ lco_yes.
* Duplicate check is done and now just save
gv_save_after_chk = abap_true.

CALL METHOD me->eh_onsave
EXPORTING
htmlb_event    = htmlb_event
htmlb_event_ex = htmlb_event_ex.
ELSE.
CLEAR gv_save_after_chk.
ENDIF.

 

Here I have called the method eh_onsave (the one i redefined) after setting gv_save_after_check. In case this variable is set, the super method is called which performs the standard process of saving the order.

 

Here is the result of the enhancement on Web UI.

 

scn1.jpg

 

This topic is in continuation of the existing post for checking of Duplicate Sales Orders in CRM ABAP (link below)

 

Checking for Duplicate Sales Orders in SAP CRM

Creating Assignment Block with custom table in CRM Web UI (Part 1)

$
0
0
Creating Assignment block in CRM Web UI is a very common requirement in CRM Web UI development and I will try to be as much descriptive as possible giving details of every step. Your comments and suggestions for improvement are most welcome.

 

1.   Introduction

 

The development consists out of three parts:

  1. a) Create maintenance tables
  2. b) Add assignment block in web UI account overview page (new assignment block)
  3. c) Copying delivery service related data into sales order header

2. MAJOR FEATURES

 

For Web UI:

Web UI Component BP_HEAD has been enhanced and a new assignment block will be created.

3. TECHNICAL DETAILS

 

Web UI Component BP_HEAD has been enhanced and a new BSP application YBP_O2C_BP_HEAD has been created. The View AccountSdsOV has been added to it.

Web UI Component Name:

BP_HEAD

BSP Application Name:

YB_O2C_BP_HEAD

Class

YCL_O2C_DELIVERY_SPECIFICATION, other classes generated automatically by Web UI Wizard.

Details:

Web UI component BP_HEAD has been enhanced

 

scn1.jpg

 

A new view has been created in the enhancement in the BSP application YB_O2C_BP_HEAD called AccountSdsOV. This view is for the assignment block for YSDS maintenance. The new block had been added to BP_HEAD/BPHEADOverview.

 

scn1.jpg

 

For adding Meta data (YSDS) to newly created sales orders for a given ship to party has been done using the class YCL_O2C_DELIVERY_SPECIFICATIONS

 

scn1.jpg

 

After creating the necessary data elements the table YSDS is created to save the data maintained in the assignment block

 

scn1.jpg

Now the KEY tables are maintained

 

scn1.jpg

Creating the Assignment Block:

The Web UI component BP_HEAD was enhanced so as to create the new view for assignment block.

A new view has been created with 2 context nodes

 

scn1.jpg

 

The Context for BUILHEADER is mapped to model head Bol entity is BUILHEADER. The Binding is done with BUILHEADER.

For the value node YSDSVALNODE the Dictionary structure YSDS is used.

 

scn1.jpg

 

The Configuration is done in the following manner.

 

scn1.jpg

 

Three buttons for Edit, Save, Cancel has been added to the top menu of the assignment block

 

scn1.jpg

 

In the implementation class an attrbute has to be added for the button

 

scn1.jpg

 

To make the list of buttons and their functionality the following code has been written in the do_prepare_output method of the implementation class.

 

scn1.jpg

 

scn1.jpg

Also the data from YSDS table is fetched and displayed on the screen.

 

scn1.jpg

In order to have a drop down list, the methods of the attributes, namely GET_V_XXXX and GET_P_XXXX has been adjusted accordingly for all the fields where drop down list is required.

 

scn1.jpg

 

Adding the following code in GET_P_XXXX helps in creation of drop down list.

 

scn1.jpg

 

The records for the drop down list are added in GET_V_XXXX

 

scn1.jpg

 

This has been done for all the the fields of YSDS except PARTNER.

Three event handler methods are created for Edit, Save and Cancel functions in the assignment block menu.

 

scn1.jpg

 

The cancel event changes edit mode to display mode.

 

scn1.jpg

 

The edit event changes the assignment block to editable one.

 

scn1.jpg

 

The save event has the necessary logic for saving the changes to table.

In order to add this view as an assignment block, the view is added in the View area shown below.

 

scn1.jpg

 

Now we go to component structure brower and enhance the view BPHEADOverview,

 

scn1.jpg

 

After editing the component page we add the assignment block we had created to the available assignment blocks (left to right).

 

scn1.jpg

The new assignment block is now available for use.

 

scn1.jpg

 

Continued in Creating Assignment Block with custom table in CRM Web UI (Part 2)


Creating Assignment Block with custom table in CRM Web UI (Part 2)

$
0
0

Continued from Creating Assignment Block with custom table in CRM Web UI (Part 1)

 

When a new sales order is created using SAP GUI, the BADI ORDER_SAVE is called. In the method CHECK_BEFOR_SAVE which is already implemented, a call to the class YCL_O2C_DELIVERY_SPECIFICATION has been made where the necessary logic for adding the ship to party meta data (YSDS) is updated in the sales order header text. This had been done in the method CHECK_BEFORE_SAVE.

 

scn1.jpg

For the Web UI part, the method CHECK_BEFORE_SAVE_WUI has been called from the Event handler EH_ONSAVE.

 

scn1.jpg

Given below are the additional lines.

 

scn1.jpg

 

Given below is a sample text that are added to the newly created sales order.

 

scn1.jpg

 

Success/ Error messages are captured in the method EH_ONSAVE (event handler is the menu of assignment block) and displayed. Necessary validation logic is provided here before updating the table contents.

 

scn1.jpg

 

Test Run

 

scn1.jpg

 

Now we can maintain the delivery specifications in SM30

 

scn1.jpg

 

Create New Sales order using CRM Web UI

 

scn1.jpg

 

If Ship to party is maintained in YSDS then the header text is updated

 

scn1.jpg

Also visible in SAP GUI

 

scn1.jpg

BPath. Lesson 07. Model Check Mode, table of exceptions and attribute longtext

$
0
0

Hello everybody,

 

due to work on other things I had to stopped my blog series on BPath quite some time ago with lesson 6. Unfortunately there are - beside several nice additional features - some features pending which are quite useful when working with BPath. So I will give an insight on the main features for designing BPath statements here and may continue with additional features in some further lesson.

 

The old blog series was published under my personal blog, but starting with this blog I moved the blogs to the Webclient UI Framework blog as BPath is actually a feature realized together with BOL / GenIL.

 

All described features require BPath of version 2.3 at least, but I suppose there is hardly a lesser version out somewhere.

 

Before we take a look on the main part of this lesson, we do a brief look on a new feature which in a way rounds up the performance considerations described in lesson 6.

 

Parser Reuse

 

This is a change to improve performance in certain use cases.

 

When started, the parser has to build up the parse table and when the parser receives the source code a preparsing is done. The first step is unnecessary for all but the first execution if several BPATH statements are executed in a sequence, the second step is unecessary in case the source code has not changed.

 

To allow a reuse of the parser object and consequently an omitting of the unecessary steps, the parser object might be instantiated under the control of the application and handed over to the corresponding BOL method as in the following ABAP example:

 

Data MyParser type ref to CL_WCF_BPATH_PARSER.
create object MyParser.
LV_SOURCECODE = 'SearchResFlightRel/*$'.
MyResult1 = lv_object->GET_PROPERTIES_BY_BPATH(
   IV_BPATH_STATEMENT = LV_SOURCECODE
   IO_PARSER          = MyParser ).
MyResult2 = lv_other_object->GET_PROPERTIES_BY_BPATH( 
   IV_BPATH_STATEMENT = LV_SOURCECODE 
   IO_PARSER          = MyParser ).

 

The second call reuses the parsing tables of the first call and - since the source code is the same - the preparsing tables also.

 

Model Check Mode

 

There was demand for a mode which does not execute the query as such but ...

 

  • checks syntax
  • checks validity of used relations and attributes against model
  • returns the (empty) data structure

 

Till version 2.2 the parser knew two modi, 1 which checks the syntax of the source code (and does some preparsing) and 3 which executes the query. Now there is a new mode 2, which attempts to fulfill above requirements. Please note that the modi 2 and 3 do a preparsing before execution, this means internally a run in mode 1 is executed before.

 

Since mode 2 runs against the model and not against actual objects/collections, the object name (plus namespace which is otherwise defaulted with '') is required instead of object or collection instance. An example syntax looks as follows:

 

DATA: lv_bol_core TYPE REF TO cl_crm_bol_core.
lv_bol_core = cl_crm_bol_core=>get_instance( ).
MyResult = lv_bol_core->GET_PROPERTIES_BY_BPATH(
   IV_BPATH_STATEMENT = LV_SOURCECODE
   IV_BASE_NAMESPACE  = ''
   IV_BASE_OBJECTNAME = 'UIFSearchResFlight'
   IV_EVAL_MODE = 2).

 

Please note that the GET_PROPERTIES_BY_BPATH of BOL core is called, the above can not work on an instantiated BOL object or collection, as the signature of the corresponding methods do not have a IV_BASE_OBJECTNAME parameter. Btw, you may call GET_PROPERTIES_BY_BPATH on the BOL core directly also in execution mode (default or 3). You have to handover the object/collection using the IV_BASE_ENTITY or IV_BASE_COLLECTION parameter then.

 

Some technical comments to the mode:

  • the syntax check is the same in all three modi
  • in case the return type is a table, the table will contain exactly one row. The fields in this row should be empty except the cases described below. The same applies to returned structures.
  • The relation check as well as the data structure return has some limitations due to the flexibility of GenIL as well as BPATH:
    • The parent relation leads to an unambiguous object type only with an instantiated object. As for the model the relation may be ambiguous (the parent of an address might be a contact or a business partner). Since it is unclear with which object type to proceed and whether the relations / attribute accesses which are following are correct, the model checks stops whenever a parent relation is used with more then 1 possible parent. It is recommended to make the parent relation unique in these cases (see next chapter).
    • The return type of some functions may be unknown at design time, e.g. in the following example:
      ~*/SearchResFlightRel{!RetVal:=if(@CONNID=14,1,"Hello")}
      At run time the type is determined with the first access.
    • Dereferenced targets on the left side of an assignment are leading to fields where the name is not known at design time, as in:
      ~*/SearchResFlightRel{!1:="F_"+@FLDATE;!1^!=1}
      The model check generates a field named 'UNKNOWNFIELDNAME n ' into the result structure, the type information should be correct. Note that several dereferenced assignments will lead to several entries as above and that one dereferenced assignment may lead to more than one added field at run time but only to one within model check.
    • Dereferenced targets on the right side of an assignment are leading to fields where the type is not known at design time, as in:
      ~*/SearchResFlightRel{!1:=if(@CONNID=14,"CONNID","FLDATE");!Ret=!1^!}
      In these cases the model check creates a field with type string containing the value: 'UNKNOWN DATATYPE'.

 

To use the syntax check or model check the exceptions caused by snytax errors have to be caught.

 

Parser Exceptions

 

Exception
Syntax Check
Model Check
Execution
Description
Example
CX_CRM_GENIL_MODEL_ERROR2
X
attribute unknown
~*/./@NoAttribute
CX_CRM_UNSUPPORTED_RELATION
X
X
relation unknown
~*/PlumpaQuatsch
CX_WCF_BPATH_PARSING_ERROR
X
X
X
General parsing error
~*/+-*^1

 

The CX_WCF_BPATH_PARSING_ERROR exception provides more information with the TEXT_ID according to the following table:

 

TextID
1
2
3
Description
CX_WCF_BPATH_PARSING_ERROR
X
X
X
Current token is not allowed at this place
(possible entries will be listed)
Example: ~*/$
INCOMPATIBLE_OPERATION_TYPES
X
X
operation is not supported with
specified types
Example: ~*/.{!A=Today()*"Hello"}
NUMBER_OF_PARAMS_MISSMATCH
X
X
Number of parameters in statement
does not match function declaration
Example: ~*/.{!A=Upper()}
PARAMETER_WRONG
X
X
Parameter type does not match
function declaration
Example: ~*/.{!A=Upper(1)}
PARENT_OBJECT_NAME_INCORRECT
X
*)
specified parent object name is incorrect
Example: ~*/.._NoNsEnSe
PARENT_REL_NOT_UNAMBIGOUS
X
*)
parent is either not there (we are already
on root) or not unambiguous
Example: ~*/..
UNSUPP_TYPE_IN_TARGET_STRUCT
X
X
creation of a target of used type is
not supported
UNSUPPORTED_FUNCTION
X
X
There is no such thing as a function
with this name
Example: ~*/.{!A=Test(1)}

 

*) both dumps do not appear in code execution (more exactly PARENT_REL_NOT_UNAMBIGOUS only if no parent was found), but it is likely that the code is not fitting to the object structures and consequently it can not be guaranteed that the code is executed without problems and/or dumps.

 

Typically the dumps returns the code position as source code extract, where the program pointer is visualized with '_^_'.

 

Attributed parent relation

 

To avoid the problems described above, the parent relation ('..') may have an attribute which should hold the object name of the parent as in the following (hardly usable) example:

 

~*/SearchResFlightRel/FlightBookRel/.._UIFFlight/*$
Code Example 33, .._OBJECT, attributed parent relation

 

Please note, that this is not a syntax change, for BPATH the relation name is '.._UIFFlight'. The object name of the parent object is used to identify the object on which the check is proceeded during model check. As for normal statement execution, the object name information is ignored.

 

 

Attribute Longtext

 

Using a double @ as prefix, BPATH will return the result of the GET_TEXT method as result, instead of the attribute directly. This is the longtext, if available, otherwise an empty string. Result type will be always string.

 

~*/SearchResFlightRel/FlightBookRel{!A=@@CUSTTYPE;!B=@@CARRID;!D=@@CLASS;!E=@@FORCURKEY}$
Code Example 34, @@CARRID, attribute longtext

 

That's it for today. The content of the next lesson has to be assembled first. The remaining part of version 2.3 contains rather technical stuff. And with version 2.4 it gets really complicated, and also a bit in pretty unexplored universes.

 

Best regards, Juergen

Another small tip to deal with empty screen issue

$
0
0

I have already written one blog How to persist the ui exception so you can view them later, the idea is after you spend little effort to create your own ui error handler and a new transparent table, the UI exception will be recored into your own table, and you can view them at any time. ( just the similar logic as ST22 )


Sometimes once an UI exception occurs, you will not see an empty page but instead a time out error page. This blog explains why and how you will see the timeout error page.


This blog will show another small tip to accelerate your trouble shooting process against empty screen issue. In the end part of it I also raise an open question which is related to the discussed empty screen issue and I have not got answer so far.

 

Issue description

 

When clicking object ID hyperlink:

 

clipboard1.png


Then empty screen is displayed:

clipboard2.png

The tip

 

 

set breakpoint on method WRITE_ERROR_PAGE of class CL_BSP_WD_ERROR_PAGE_WRITER:

 

relaunch the application and breakpoint triggers. If current client is not set up as productive client, the code will go to the first IF branch.

The client type detection is done in the class constructor via FM TR_SYS_PARAMS.


clipboard3.png


Since normally the client type could only be changed by system admin, so you can put cursor on line 17 and click Shift + F12 to force the code to move to the second IF branch. Click F8 and the error information is displayed.

 

clipboard4.png

Open question

 

 

I develop a sample BSP and raise exactly the same exception deliberately there in another system.

clipboard5.png

Instead of seeing the empty screen, I get the following error page with detailed information:

clipboard6.png

I try to find the switch & logic to control how the detailed error page or empty screen is chosen to display.

 

Unfortunately I didn't find the answer so far. The only finding is that when the exception is raised in ABAP backend, I observed a HTTP 500 error in HTTP watch.

 

clipboard7.png

And there are lots of insert operation on ST22 table SNAP in ST05 trace.

clipboard8.png

however by clicking button "Display ABAP call location", I could not see the corresponding INSERT statement in ABAP editor, but just automatically navigate to the line below:

clipboard9.png

Perhaps the logic to make choice between error detail page and empty page for display is not done in ABAP side at all.


Using multiple blocks of multilevel categorization in complaints (or another object)

$
0
0

Here I have used the complaints component but you can do it with any order object.

 

Creation of component usages

 

So in the component BT120H_CPL I have created 2 new usages (you can use more) in the runtime repositery :

usages.png

You need to name your usage starting with "CUBTCategories_" and then a letter corresponding to the catalog category you want to use, or you will have to change the code I used in the method WD_USAGE_INITIALIZE (see below).

 

Then I put these usages in the viewset :

runtime_rep.png

 

You can then add the new viewareas in the configuration (but they won't work until coding and customizing is done).

 

Usage initialize in the component controller

 

I redefined the WD_USAGE_INITIALIZE method in the component controller :

 

 

You can see I used mr_cnode_binding_mgr which is the same than the one in the incident component controller :

usage_mgr.png

Several methods are redefine in order to create and manage this binding manager :

methods.png

 

You can find the added code for these methods in component controller of component SRQM_INCIDENT_H, I just copied them.

 

For instance :

do_init.png

 

Customizing of schemas

 

app_areas.png

 

In order to link our shemas with the application we will use a combination of transaction type and catalog category, this entry has to be added :

complaint_area.png

Then you just assign transaction type and catalog categories :

assign_trans.png

zass.png

Here you see why I put C and D in the usage names.

 

Then in your schema you need to use application areas corresponding to the catalog category :

schema_areas.png

Here I didn't put a description in the customizing for the parameter.

 

app_value.png

 

And then do the same for others schemas, using different category of catalog.

UI Configuration determination logic introduction

$
0
0

This blog will try to introduce the Webclient UI Configuration determination logic to those friends who are new for this topic.

 

Suppose I log on Webclient UI via Business role SALESPRO and have opened Sales Order overview page and F2 to open technical information page, what are the meaing of the left four fields ( Searched For) and the right four fields ( <DEFAULT>) ?

clipboard1.png

 

 

                                                 Figure1 Technical information page

 

In UI workbench, there are 8 standard configuration pre-delivered by SAP and 1 configuration done by customer.

clipboard2.png

                                   Figure2 View configuration list for Sales Order detail view

 

The SAP configuration data is storaged in table BSP_DL_XMLSTRX2 while customer configuration in BSPC_DL_XMLSTRX2.

 

The configuration determination logic could be summarized as the following:

 

1. The four fields config key, component usage, object type and object subtype are evaluated by framework to determine which configuration should be loaded. The priorities of these four fields are different and hard coded by SAP, see followng picture. These 22 table rows are called "Configuration Access sequence".

 

2. Customer configuration precedes SAP configuration. If a customer configuration is evaluated and accepted, the framework will stop the determination process and load it.

clipboard3.png

                                            

 

 

                                                       Figure3 Access sequnce table

 

Briefly speaking, the configuration determination logic could be concluded as below process:

 

1. framework will build the access sequence table gt_access_sequence, which has 22 entries as listed in figure3.

2. framework will load the 8 sap configuration information( config key, component usage, object type and object subtype ) to internal table it_config_sap

and 1 customer configuration to it_config_cus.

3. LOOP AT gt_access_sequence, within each loop, check it_config_cus whether there are matched configuration. If found, mark it as "Found" configuration and stop the process. If not found ( ie, the 11 times LOOP finish ), the framework will LOOP AT gt_access_sequence again from beginning, but this time the table it_config_sap is checked instead.

 

Let's use the SalesOrder detail view as example to make this process more clear:

 

1. Framework will try to load a configuration which matches the four fields displayed in debugger. These four fields are just considered as "Searched for" fields shown in figure 1.

clipboard5.png

                                                                                Figure4 search key

 

2. load the SAP and customer configuration data:

clipboard6.png

The internal table looks exactly the same as what we see in UI workbench ( Figure 2 ):

clipboard7.png

3. if the determination is being executed in a customer system, there are totally 22 ( 11 for customer configuration and 11 for SAP configuration ) loop until a match is found.

clipboard8.png

the gt_access_sequence is filled according to Figure3.

clipboard9.png

For each fields, X means the corresponding field in it_config_cus or it_config_sap is checked with the search key passed in ( figure4 ),

                      space means <DEFAULT>.

clipboard10.png

 

clipboard11.png

clipboard12.png

All 11 times LOOP for it_config_cus failed, so it_config_sap is now evaluated.

The matched one is finally found in the last attempt, and since it belongs to SAP configuration, so in figure1 "Standard Configuration" is marked for Configuration Origin.

 

clipboard13.png

clipboard14.png

BPath. Lesson 08. Data type and operation table. Smaller topics.

$
0
0
<< 07. Model Check, exceptions and attribute longtextupcoming.... >>

 

Hello everybody,

 

the last lesson caused some discussions on various difficulties to formulate correct BPath statements as some pieces of information were simply not yet communicated fully in this Blog Series. So before describing new content I want to go back some steps and clarify some topics and provide some tables with information.

 

A rule for in-between: 1+1+1 is not 3, but a BPath Error.

 

Keep in mind that the BPath Parser only deals with operations with 2 operands, so (1+1+1) will result in an error and should be rephrased to ((1+1)+1). This of course also applies to comparisons, so ((x=0)&(y=0)&(z=0)) cannot be parsed. Instead (((x=0)&(y=0))&(z=0)) should be used.

 

Available data types

 

There was a request in respect to raw data types, this is not supported. Raw fields might be read, but the content can not be compared to literals or other values as now corresponding data type exists.

 

Short

Name

Null

Description

S

String

""

Character strings, encapsulated with ". Every "-character in the string has to be escaped with "" (double double quotes).

N

Numeric

0

numeric values without sign, e.g 3.141. Negative literals have to be written as 0-value

D

Date

#19000101#

Date values in the following format #yyyymmdd#.

B

Boolean

false

true/false value

R

Data ref.

reference to data entity, typically returned by a sub function

O

Object ref.

reference to object

X

invalid

internally used for invalid objects, which are later handled by the GET() function


The Null value is the only value of the specific type where the function NOT(...) returns true.

 

Operation/Function Table

 

Meaning
String
Num
Date
Bool
&

and,

concat

S & S -> S (concat)
B & B -> B (and)
|
or
B | B -> B (or)
+

add, add days,

number to string

string to number

S + S -> S (concat)
S + N -> S *3
N + N -> N (add)
N + S -> N *4
D + N -> D *1
B + B -> B (or)
-

minus, substr

replace first,

substract days,

remove last chars

S - S -> S *6
S - N -> S *7
N - N -> N (substr.)
D - N -> D *2
B - B -> B *5
*
(string) multiply,if
S * N -> N *8
S * B -> S *9
N * N -> N (mlt)
N * B -> N *9
D * B -> D *9
B * B -> S (and / if)
%

divide, replace all,

remove first chars

S % S -> S *10
S % N -> S *11
N % N -> N (Div)
B % B -> B *12
Comp*13
comparison
S X S -> B
N X N -> B
D X D -> B
B X B -> B
NOT
not
NOT(S) -> B
NOT(N) -> B
NOT(D) -> B
NOT(B) -> B
IFF
? Operator
IFF(B,X,X)->X

 

 

1. Adds number of days to a date

2. Subtracts number of days from a date

3. Converts number into string (as provided by ABAP) and concatenates it to the given string

4. Converts string into number (as provided by ABAP) and adds it to number.

5. Op1 = True or Op2 = False

6. Replace first occurrence of op2 in Op1 with empty string

7. Removes last n characters from string (if n <= strlen)

8. String multiplication (e.g. "la" * 3 = "lalala" ) [if n >= 0]

9. Logical if. If Boolean value is true original element is returned otherwise NULL element ("" / 0 / #19000101# / FALSE ).

10. Replace all occurrences of op2 in Op1 with empty string

11. Removes first n characters from string (if n <= strlen)

12. Op1 = True and Op2 = False

13. Supported are the standard comparisons =, <, >, <=, >=, <>. The types of the compared elements must be equal.

14. NOT: returns true if element is equal to NULL element

15. IFF: returns Parameter 2 if Parameter 1 is true otherwise Parameter 3. Parameter 1 must be of type Boolean, the other parameters are not checked (theoretically P2 and P3 must not be of the same type, but logically they should)

 

Additional remark:

 

As BPath has no concept of returnings errors some of the functions are defined in a way that the error case is caught and a Special value is returned. This is the case for the simple divide operation X % 0, which will always return 0 if the second Operand is 0. Attempts to convert a string, which does not contain a number to a number, will also result in a 0. Example would be the Expression: 1+"test"

 

 

Numeric Values

 

With version 2.2 BPath was enhanced to Support full numeric values (instead of integer), internally they base on DECFLOAT34. Some remarks:

 

  • the return types of fields basing on internal calculations are also DECFLOAT34 now
  • numeric literals including decimal points as 0.5 and 23.001 are possible
  • The dot may not be the first character of the literal. Use 0.5 instead of .5
  • negative numeric literals as -1 can still not be expressed directly, since the the minus sign would be interpreted as minus. Use 0-1 instead.

 

~*/SearchResFlightRel/FlightBookRel[@LUGGWEIGHT>23.5]{!A=@LUGGWEIGHT;!B=@LUGGWEIGHT*2}$

Code Example 35, [@LUGGWEIGTH>23.5], numeric calculations

 

Direct Attribute Retrieval

 

In case you simply want to copy an attribute from the current structure you might use the syntax as specified in the example:

 

~*/SearchResFlightRel/FlightBookRel{@FORCURAM;@FORCURKEY;@@CUSTTYPE}$
Code Example 36,{@FORCURAM;@FORCURKEY;@@CUSTTYPE}, direct attribute retrieval

 

This should work for enhanceable structures as well as for non-enhanceable structures. For normal fields a target with the same name is used, for long text fields the suffix '_LONG' will be appended to the target name. The assignment block in the example above is simply a shortcut for the slightly longer syntax: {!FORCURAM:=@FORCURAM;!FORCURKEY:=@FORCURKEY;!CUSTTYPE_LONG:=@@CUSTTYPE}.

 

Non-reliable assignments using subs (note 1533088 in case your version is 2.3)

 

Typically any assignment based on expressions or functions has a well defined result. This may be no more true in case the right side of the assignment contains a sub-function, since the sub function returns initial if the BPATH query can not be executed till the end (typically if a relation is used which delivers no elements).

Originally the initial reference was assigned to the target structure, but this leads to problems (means dumps) in case a following entry returns a valid entry which is not of type 'data reference'.

This leads to the following change: Assignments to initial references are not executed. If there is a valid entry following afterwards, the column will be created then including the omitted entries. If there is no valid entry following the column will not be part of the target structure.

Please note that this handling has some side effects:

  • If the mentioned column is the only one returned in the query all entries till the first valid entries are omitted and not constructed afterwards. If this is a problem add a second field to the target structure with a dummy entry.
  • It is not guaranteed that a column using a non-reliable assignment is actually available in the result. If this is a problem add a initialization sequence before the sub ( e.g. {!X="";!X=SUB(...)}).
  • internally the value is still an initial reference. So if a sub-method is used within a calculation (e.g. !X=1+SUB(...) ) it is responsibility of the user that the initial case is never occurring. If this can not be guaranteed it is advisable to use results of sub-functions only after a assignment. A "safe" solution would be: {!X=0;!X=SUB(...);!Y=1+!X}.

 

Masking of slashes in relation names, structures, attributes and targets (note 1535509 if your version is 2.3)

 

As with the language definition it was overseen that it is allowed to have relation names, structure names and field names containing a slash. This is very uncommon in development (at least in the areas we developed) but may occur at customer site.

The change, which is available with the mentioned note (or with version 2.4 of course) allows to mask a slash with a preceeding backslash within the following entites:

  • relation names
  • target names
  • attribute names
  • structure names
  • function names (hardly useful, we still recommend to define function names without slashes).

This means, if your BPATH wants to make use of a relation CRM/CustomerRel with the attribute CRM/CustomerNumer the BPATH fragment in this respect looks like: /CRM\/CustomerRel/@CRM\/CustomerNumber.

All surrounding generic consumers of BPATH should be able to handle this accordingly.

 

As the following topics are of a bigger size, I will stop the lesson here. Next Topic is Groupings.

Using OTR text contexts to adapt texts in the SAP CRM Web UI

$
0
0

Introduction

Recently we performed a enhancement package upgrade of the SAP CRM system of a customer form EhP0 to EhP3. During the implementation of the system numerous OTR texts were changed by the implementation partner to adapt the texts visible in the Web UI. For example, the text "Account" was changed to "Geschäftspartner" (i.e. German for business partner) across the system. After the upgrade all these OTR texts where reset again to the SAP standard. As  result, the OTR text needed to be changed again. In this process I started looking for a better solution that would not require changing OTR texts after each upgrade.

While there are quite a view blogs related to OTR texts and SAP CRM Web UI I couldn't really find a solution for the problem. The best practices for SAP CRM Web UI on the SCN (Best Practices for SAP CRM Web UI Customization - CRM - SCN Wiki) also only states that SAP standard texts should not be changed. Through some playing around with the OTR transaction I finally found a nice solution within the SAP standard which I'd like to share here. The basic idea is to create a custom context for the OTR and use this context to adapt the necessary OTR texts.

Creating a custom context

In order to create a custom context one needs to implement the BAdI BTFR_CONTEXT. This BAdI consist of a single method CUSTOMER_CONTEXT that returns the custom context for the OTR. Below is the code of the simple implementation of this BAdI that I created. Note that I have used the |-operator to created the two strings that are returned from this method which is only available in the ABAP application server 7.40 and above.

 

METHOD if_ex_btfr_context~customer_context.     customer_context-country = |DE|.     customer_context-extension = |FACTUR|.
ENDMETHOD

.After implementing the BAdI BTFR_CONTEXT and activating it the custom context is immediately available. To check this open transaction SOTR_EDIT and choose the menu "Edit -> Context -> System Context".

2014-06-15 18_12_40-CVD(1)_100 OTR_ Maintain Initial Screen.png

This opens a pop up displaying the custom system context for OTR texts. If everything worked the country and extension that are set in the BAdI are displayed.

2014-06-15 18_13_53-.png

Creating OTR texts in the custom context

The next step is to create custom OTR texts in the custom context. As an example I'll use the view DetailAccount in the UI component IUICMD. This view contains a button with the label "Account bestätigen" (if the log on language is german).

2014-06-15 18_25_39-Identifikation - [Interaction Center ] - Internet Explorer bereitgestellt von Ih.png

The label for this button is stored in the OTR in the package CRM_IU_IC_6X_MD and is available via the alias CRM_IU_IC_6X_MD/CONFIRMBP.

To create a custom text open transaction SOTR_EDIT, enter CRM_IU_IC_6X_MD/CONFIRMBP as the alias value an choose the menu "Edit -> Context -> Create".

2014-06-15 18_31_51-.png

In the next step the system asks for the context in which the text should be created. The custom context provided by the BAdI implementation is already suggested.

2014-06-15 18_32_55-.png

The next screen is already populated with the SAP standard values of the OTR text CRM_IU_IC_6X_MD/CONFIRMBP. Except the context is set to the custom context that was selected in the pop up (in this case the context provided by the BAdI implementation). Now we can change the default text "Account bestätigen" to the custom text "Geschäftspartner bestätigen" and save the new context.

In order to add the new value to a transport request, click the transport icon.

2014-06-15 18_33_24-CVD(1)_100 OTR_ Change Concept.png

After saving the entry select "Edit -> Context -> Overview" to display an overview of the contexts available to this OTR text. The overview shows text values for different context. In this case the default context as well as the one that was just created.

2014-06-15 18_46_57-.png

As a final step the OTR buffer needs to be reset using the transaction code /$OTR. After restarting the SAP CRM Web UI the label for the button is changed to the new text.

2014-06-15 18_54_02-Identifikation - [Interaction Center ] - Internet Explorer bereitgestellt von Ih.png

Custom contexts and upgrade

Using this approach, the OTR text will not be overwritten in a future system upgrade. However, the system context will be reset in an upgrade. Therefore, the system context needs to be set to the custom context after an upgrade. This can be done in the transaction SOTR_EDIT via the menu "Edit -> Context -> Adjust".


Dropdown list issue in CRM Webclient UI - a very funny trouble shooting process

$
0
0

Issue description

 

 

Today I received a ticket with priority very high complaining that the drop down list for Business Role in Work center "Sales Operation" is empty:

clipboard1.png

However, it works perfectly for my own user:

clipboard2.png

Issue analysis

 

I didn't start debugging immediately but made the following analysis beforehand:

 

1. I could reproduce this issue in my laptop with my colleagues' user

 

Based on this finding I made the following comparison between my colleague's user and mine:

 

a. compare the authorization settings:

 

result: The two are exactly the same ( both copied from the same user group and we didn't have authorization to make changes ).

Also there is no corresponding entries found in SU53 when I test with my colleague's user.

 

b. compare the user parameter in tcode SU3

result: no difference found related to this issue

 

c. compare the business switch setting

result: the same setting for both user

 

2. compare the final native html source code

 

I do believe there must be differences when the page is rendered via different users. So I use the development tool ( click F12 ) in IE, and soon found the difference:

 

This one is the html source code generated by my user and it works perfectly:

 

clipboard3.png


And this below is the one generated by my colleague's user. The title are completely missing!


clipboard4.png

3. Use mini-system approach to isolate and locate root cause

 

 

Since this issue occurs in CRM Organizational Model management and the underlying business model is huge and complex. In this situation I always prefer to use mini-system approach I described in this blog to try to make the issue also reproducible in the small nutshell. At least I could have the following benefits from the nut shell:


 

a. the nut shell could eliminate all impacts from unrelated components so that I could concentrate on the key part of program which might lead to the issue

 

 

b. the nut shell makes debugging be more efficient. For example sometimes I have to debug how does webclient framework class reacts to my input for my UI component, the method is so generic so that it would be triggered too frequently( like method PROCESS_NAV_QUEUE of class CL_BSP_WD_VIEW_MANAGER, which have helped me to resolve many issues ). Using nutshell I do not need to press "deactivate all BPs again and again.

 

clipboard5.png


I just spent 5 minutes to build a nutshell, which contains a view, a value node and one attribute. The attribute contains GET_V and GET_P method whose source code are copied from standard Org management UI component.


clipboard6.png

The nutshell in the runtime looks like below. The issue could be reproduced within it by using my colleague's user.

clipboard7.png

4. Debug the nutshell

 

 

When I debug the GET_V, I soon found the root cause:

 

The following code plan to retrieve profile and description from table crmc_ui_prof_t:

 

clipboard8.png

profile will act as drop down list key and description as drop down list value:


clipboard9.png

Unfortunately between profile and description column in table crmc_ui_prof_t there is another column LANGUAGE:

clipboard10.png

As a result the select statement does not work as expected at all. The SELECT xxx INTO CORRESPONDING FIELDS OF TABLE must be used instead.

clipboard11.png

5. why I could see entries with my own user?

 

 

First of all, the entries displayed with my own user are not correct at all, they are just the wrong entries populated in internal table LT_ROLES, column PROFILE in screenshot above. In the wrong implementation, only drop down list key is filled with the incorrect entries.

 

 

For my user, I have enabled that key will also be displayed in dropdown list, so I could see the incorrect keys in UI. However my colleague didn't enable this so he could only see nothing there, since the drop down list value is not filled due to wrong implementation.

clipboard12.png

6. Why the native HTML source code is different based on different personalization setting?

 

Why my user could see the not initial title attribute in native html source code but my colleague's user could not?

 

It is related to your personalization settings regarding whether dropdown list key must be displayed or not.

 

Just set a breakpoint on method CL_THTMLB_PERSONALIZATION~GET_DDLB_KEY_MODE, start the nutshell.

 

It stops at the place where the drop down list UI element is to be rendered. The callstack is also very helpful if we would like to study how the UI element defined by SAP tag like chtmlb, thtmlb is finally converted to html source code.

clipboard13.png


This setting is evaluated here and populating drop down list entries accordingly:

clipboard14.png

All the entries to be displayed in drop down list is available in the current callstack:

clipboard15.png

Getting the SQL-Select-Statement of a Query in WebUI

$
0
0

Hello,

 

I often wondered how the SQL-Select-Statement of a Query in WebUI looks like. For this you could use the SQL-trace of transactions ST01 or ST05. Alternatively you could put a breakpoint in class CL_CRM_REPORT_ACC_DYNAMIC method DATABASE_ACCESS.

 

Situation: You have some kind of business query in WebUI and you wonder how the SQL_Select-Statement is

 

1.png

 

Step 1: You put a break-point in the mentionned method

 

2.png

 

Here you see the From- and Where-statements.

 

Step 1a: Add a dynamic break-point for select-statements by pressing F9

 

3.png

 

Of course this will give you much more positions in coding.

 

Good luck when investigating.

 

Best regards,

 

Thomas Wagner

BPath. Lesson 09. Groupings

$
0
0
Table of Contents ...... Table of Code Examples
<< Lesson 08. Data types, op. table, smaller topics.upcoming.... >>

 

With this lesson the content gets slightly more complex . And I have to admit: In a way more experimental as it is pretty uncovered ground.

 

Groupings

 

You probably know the term and the meaning from SQL-queries, so you can guess where we are heading to. For full usage of groupings aggregations are required also, but let us begin with basic groupings as with the following easy example:

 

~*/SearchResFlightRel/FlightBookRel$(@AGENCYNUM,UPPER(@SMOKER))
Code Example 37,$(@AGENCYNUM,UPPER(@SMOKER)), basic groupings

 

The $ is now attributed with a list of group elements (agencynum and smoker). Please note that the group element is not necessarily an attribute, any expression is valid. New entries are written to the result table only in case the combination of group elements in the actual data set did not already appear in the result table yet. The result table will contain the first entry of every group. In the chosen example a list of 4116 entries is condensed to about 70 entries.

 

The standard behaviour of the grouping adds the grouping conditions to the result table automatically. The naming of the group conditions follows the following rules:

 

  • if a the condition consists of an unchanged attribute, the name of the attribute is taken
  • if an attribute is used but changed by the usage of a function or an operator, but without usage of a second attribute or a target, the attribute name will be preceeded by a "F_".
  • in all other cases name will be EXPR_i. i is the number of the respective condition.


In the examples above the two attribute would be named AGENCYNUM and F_SMOKER.

 

In some cases it might be required to suppress the automatic adding of the group condition (e.g. it is added with different naming manually in the init part, see next chapter). This is possible with an exclamation mark after the dollar as in the following example:

 

~*/SearchResFlightRel/FlightBookRel$!(@AGENCYNUM,@SMOKER){!DEMO=@AGENCYNUM&@SMOKER}
Code Example 38,$!(@AGENCYNUM,UPPER(@SMOKER)), basic grouping without group condition takeover

 

Grouping with assignments (hand-made aggregations)

 

The grouping syntax is now extended with the possibilities to build assignments based on the group data.

The syntax is as follows: $(ListGroupIdentifier:Initblock:Loopblock)

 

  • ListGroupIdentifier is a non-empty comma separated list of expressions defining the groups as described in the chapter before.
  • Initblock is a block with assignments (separated with ; ) processed with the first element of every group. It is usually used to initialize target variables. The initblock may be empty (the two doublepoints have to be stated nevertheless).
  • Loopblock is a non-empty block with assignments processed with every element of a group.
  • Any used target variable in one of the two group assignment blocks refers to the "group" element.
  • As with normal assignments, an assignment might consist of an attribute only, abbreviating an assignment to a target with the same name. Since it is natural to have grouping attributes in the result, it may be added to the init block as in the example below.
  • Of course it is also possible to sort the result on any of the fields, including the ones built by aggregation.

 

The following example:


~*[!AVG]/SearchResFlightRel/FlightBookRel$(@FORCURKEY:!Sum=0;!Amt=0:!Sum=!Sum+1;!Amt=!Amt+@LOCCURAM;!AVG=!Amt%!Sum)
Code Example 39,$(@FORCURKEY:!X=0:!X=!X+1), hand made grouping aggregations

 

groups the bookings according to the foreign currency and calculates the number of bookings in this curreny, the complete amount (in USD) and the average amount (also in USD). Please note that !AVG never appears on the right side of assignment hence it must not be initialized.

 

Limitations:

 

  • sub() calls in the grouping assignments are not supported
  • dereferencing in grouping assingments may lead to problems
  • the syntax as above looks a bit over-crowded and too complex. It is targeted to solve this problem with standard aggregations without the need of initializations and incremental coding.
  • The usage of the GET() function is not sufficient in the loop block since it initializes only once.

 

Group to structure

 

~*/SearchResFlightRel/FlightBookRel$(*:!Sum=0:!Sum=!Sum+1)
Code Example 40,$(*:!X=0:!X=!X+1), group to a structure

 

As shown in the example above it is also possible to group the complete content to one row. There is a technical difference compared to the other examples, since the result here is a structure with the field sum, and not a table.

Note: the original example here tried to group to an INT2 field (see unstructured returns). Unfortunately, this is not working since it is not possible to access unstructured returns for reading, which is required for aggregations.

 

There is the plan to exploit this feature in future together with an enhanced sub()-handling.

 

Some new functions

 

 

List()

 

With the work on groups I realized that the way to realize enumerations is rather unhandy. Consequently I decided to spend a new feature aiming to assist the user in generating lists.

List has the following syntax: LIST(target,separator,content).
See also the example, note that the := assignment is required here:

 

~STRING/SearchResFlightRel/FlightBookRel{!Ret:=LIST(!Ret,",",@PASSNAME)}$(*)

Code Example 41,{!X:=List(!X,",",@Feld)}, LIST function

 

The list function has the following characteristics:

  • The separator separates two entries, it will not appear as first or last character.
  • As like the GET() function, the first parameter may not be available. If !X is not existing it is defaulted with an empty string value.
  • LIST considers empty fields (in the example @PASSNAME is "" ) as empty and they will not be added to the list.

 

Round() and Int()

 

Round and Int are using the ABAP round function with the modes ROUND_HALF_UP and ROUND_DOWN. The second parameter - if available - will be handed over as precision.

 

Log() and Exp()

 

Returns log respectively exponential value of parameter 1. If second parameter is provided it will be used as basis, otherwise e.

 

You want to know why the heck one needs logarithm in a business application? Well, I'll show you:

 

~*/SearchResFlightRel/FlightBookRel$(int(Log(@LOCCURAM,10)):!LMIN=round(exp(int(Log(@LOCCURAM,10)),10),2);!LMAX=round(exp(1+int(Log(@LOCCURAM,10)),10),2):!CNT=Count())

Code Example 42,$(int(Log(@LOCCURAM,10)): Log Function and logged grouping

 

It looks a bit more complex as it is. Try it out and and take a look on the result, which speaks for itself.Well, to explain it nevertheless. We group to the whole value of the decimal logarithm of our base value. This means nothing else, then the number of digits of the dollar amount. The LMIN and the LMAX value simply calculate the boarders of our area, in our example this is probable [100 .. 1000] and [1000 .. 10000]. Note that the border calculation is part of the init block, hence it will be calculated only one time for every group.The example here might be a bit artificial, nevertheless the concept might be useful to group customers/products/projects where the range is between some Pennys to millions of bucks.

 

Exists(), Safe() and Initial() and a word on comparisons on data references

 

Several functions deal with the possibility that a sub function may return no result, i.e. an initial reference. The above methods are designed to catch that problem or react on it.

 

  • exists has one parameter, typically a sub-call. It returns true if the parameter is not an initial reference, i.e. if the sub query returned data
  • safe has two parameters, the first one typically a sub-call, the second one a default value. The default value is chosen in case the first parameter is an initial reference. Please distinguish with the GET() functionality which has similar syntax but different semantics.
  • initial returns the initial value of the handed over type ('S', 'D', 'B', 'N', 'R', 'O') if set. If the function is used without parameter an initial reference will be returned.

 

The comparison functions were also enhanced to allow a comparison against the INITIAL-value. Please note the following two points:

  1. comparisions of references unequal to an initial reference return an undefined result. To the current point the extension is only designed to check against INITIAL, this means at least one parameter should be INITIAL().
  2. comparisions of values of different type are causing a parser error. Checks like sub(...)<>INITIAL() do only work in case the sub-query returns data references.

 

That's the content for lesson 9. In respect to lesson 10, I have to say: "no, we are not finished with aggregations. Not at all."

SAP CRM Web UI Simple Objects - Creating custom 1...n Bol entities

$
0
0

This document shows how to create custom 1...n entities in sap crm via bol browser, which can be linked to any other crm objects by
keeping the guid of that object in our simple object.There are multiple simple object genil classes provided by sap which provided the basic CRUD operations by default, we don't have to modify these genil classes for create/read/update/delete (CRUD) operations. In this e.g we had a requirement to create ZFORECAST as a table view which will be embebded in Sales opportunity page as a new assignment block.This table will store multiple Forecast data aginast opportunity header guid.


To start with...

 

  1. Created new table :

1.png

 

  2. Create a lock object "EZFORECAST" for our table ZFOREACT. (Lock mode E)

2.png

 

3. Define the customizing entry for simple objects  in :

 

"spro->crm->cross appl. comp->genil->comp. spe. Setting->define SO"

3.png

 

4 . Create a SO entry in the object definition.

Object Name  - 'ZFORECAST'

Implementation
Class  - 'CL_CRM_GENIL_GEN_TABLE_OBJ'

 

 

Att str --  ' ZFORECAST'  "Define attribute
structure of your SO, usually it would be similar to the DB table which will
store data"

Structure of mand.Field at create: ' ZFORECAST' -

 

Key Struc. - GUID (key structure would be "GUID" only as the genil class CL_CRM_GENIL_GEN_TABLE_OBJ

4.png

Create  entry in the search object definition under
simple object...Search Object name -- 'ZFORECAST_SRC'

 

Structure
name - '
ZFORECAST' 

 

4.1.png

4.1 .  Add the SO2 object to our ONEORDER Component
4.2.png4.3.png

 

4.4.png

 

5. Create a mapping entry for our "Simple object - 'ZFORECAST', table name - 'ZFORECAST'  and lock object - 'EZFORECAST " in the table "CRMC_TBLOBJ_MAP"

5.png

6 . Execute Genil_bol_browser
6.png

Create new root object... select 'ZFORECAST_SRC'-->'ZFORECAST'

6.1.png

Check the new entries in Table 'ZFORECAST':

6.2.png

So up till now we created the SO object for ZFORECAST table which is now
part of BOL in SO2 component and in the below steps now in below steps we will create
Table View In Web UI


7. Let's start with creating new view "ZFOREC" under "BT111H_OPPT" bsp component in BSP Workbench.

 

7.png

 

Specify Context node name "ZFOREC" and Bol entity as the simple object created from the previous "ZFORECAST".Click
next till last step, in the last screen select view type as "Table view", tick the Configurable and Change/Display check boxes

7.1.png
After adding ZFOREC node also add BTADMINH which will be needed further to read the opportunity header Guid

8. Declare these Button attributes in the attribute section of the ZFOREC view's implementation class.
8.png

9. Modify the code of "ZFOREC.htm" BSP page code with the below code.

<%@page language="abap" %>
<%@extension name="htmlb" prefix="htmlb" %>
<%@extension name="xhtmlb" prefix="xhtmlb" %>
<%@extension name="crm_bsp_ic" prefix="crmic" %>
<%@extension name="bsp" prefix="bsp" %>
<%@extension name="chtmlb" prefix="chtmlb" %>
<%@extension name="thtmlb" prefix="thtmlb" %>

<%
data lv_xml type string.

lv_xml =  controller->configuration_descr->get_config_data( ).
%>

<thtmlb:areaFrameSetter toolbarButtons =
"<%= controller->tb_button %>"

                        maxButtonNumber =
"2"

                        displayMode  =
"<%= controller->view_group_context->is_view_in_display_mode( controller ) %>"/>

<chtmlb:tableExtension tableId =
"Table1"

                        layout  =
"FIXED">

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

                   
id                    = "Table1"

                    navigationMode        =
"BYPAGE"

                    onRowSelection        =
"select"

                   
table                = "//ZFOREC/Table"

                   
width                = "100%"

                    displayMode          =
"<%= controller->view_group_context->is_view_in_display_mode( controller ) %>"

                    headerVisible        =
"FALSE"

                    hasLeadSelection      =
"TRUE"

                    usage                =
"ASSIGNMENTBLOCK"

                    personalizable        =
"FALSE"

                    actions              =
"<%= controller->gt_button %>"

                    allRowsEditable      =
"TRUE"

                    actionsMaxInRow      =
"3"

                    downloadToExcel      =
"TRUE"

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

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

                    selectionMode        =
"<%= ZFOREC->SELECTION_MODE %>"

                    visibleFirstRow      =
"<%= ZFOREC->VISIBLE_FIRST_ROW_INDEX %>"

                    visibleRowCount      =
"6"

                    />



</chtmlb:tableExtension>


10.Click on Configuration tab of  "ZFOREC" view move required field to be shown on the right side under displayed field.

10.png

11. Define three buttons Create, Delete, Edit List in do_prepare_output method of the implementation class.

11.png
DATA:  ls_button    TYPE crmt_thtmlb_button.

 
DATA : lr_entity    TYPEREFTO cl_crm_bol_entity,

        lv_locked   
TYPE char1,

        lv_enabled 
TYPE crmt_boolean VALUE abap_true,

        lr_coll     
TYPEREFTO if_bol_bo_col,

        lv_coll_size
TYPE sytabix,

        lv_display 
TYPE boolean.


*me->view_group_context->set_view_editable( me ).



  lv_display = me->view_group_context->is_view_in_display_mode( me ).
*lv_display = me->view_group_context->set_view_editable.



 
IF lv_display EQ abap_true.

    typed_context->zforec->set_selection_mode( iv_selection_mode = cl_bsp_wd_context_node_tv=>selmode_none ).

 
ELSE.

    typed_context->zforec->set_selection_mode( iv_selection_mode = cl_bsp_wd_context_node_tv=>selmode_lineedit ).

 
ENDIF.



 
CALLMETHOD super->do_prepare_output.



  lr_coll =  me->typed_context->zforec->collection_wrapper->get_marked( ).

 
IF lr_coll ISBOUND.

    lv_coll_size = lr_coll->size( ).

   
IF lv_coll_size = 0.

      lv_enabled = abap_false.

   
ELSE.

      lv_enabled = abap_true.

   
ENDIF.

 
ENDIF.
************************



 
CLEAR : tb_button, ls_button.

  ls_button-
text    = 'Edit List'.

  ls_button-on_click =
'EDIT'.                              "#EC NOTEXT

  ls_button-enabled  = me->view_group_context->is_view_in_display_mode( me ).

 
APPEND ls_button TO tb_button.



 
CLEAR gt_button.

  ls_button-
text    = 'Insert'.

  ls_button-on_click =
'CREATE'.                            "#EC NOTEXT

  ls_button-enabled  = abap_true.

 
APPEND ls_button TO gt_button.

 
CLEAR ls_button.



  ls_button-
type    = cl_thtmlb_util=>gc_icon_delete.

  ls_button-on_click =
'DELETE'.                            "#EC NOTEXT

  ls_button-enabled  = abap_true.     
"lv_enabled.

 
APPEND ls_button TO gt_button.

 
CLEAR ls_button.


12.Create three event's "CREATE","DELETE","EDIT" in "ZFOREC" view via wizard & add the required logic in each event method as per below.
12.png

 

 

method EH_ONCREATE.

   
DATA :  lr_entity TYPEREFTO cl_crm_bol_entity,

          lv_collection
TYPEREFTO if_bol_bo_col,

          lr_comp
TYPEREFTO cl_bt111h_o_bspwdcomponen_impl.
*          cl_bt111h_o_bspwdcompone0_impl.



 
DATA : lr_core    TYPEREFTO cl_crm_bol_core,

        lr_fac     
TYPEREFTO cl_crm_bol_entity_factory,

        lt_params 
TYPE        crmt_name_value_pair_tab,

        ls_params 
TYPE        crmt_name_value_pair,

        lr_ent     
TYPEREFTO cl_crm_bol_entity,

        lv_objid
TYPE crmd_orderadm_h-object_id,

        lv_quot_id
TYPE crmd_orderadm_h-object_id,

        lv_pred_guid 
TYPE        crmt_object_guid,

        lv_cmp_guid 
TYPE        crmt_object_guid,

        ls_borident 
TYPE borident,

        it_neighbor_objects
TYPETABLEOF relroles,

        is_neighbor_objects
TYPE relroles,

        lt_zforecast
TYPETABLEOF zforecast,

        lv_new_month
TYPE zforecast-z_month,

        lv_cur_zvalue
TYPE zforecast-z_value,

        lv_new_zvalue
TYPE zforecast-z_value,

        lr_coco
TYPEREFTO zl_bt111h_o_bspwdcomponen_impl,

        lr_entity_so 
TYPEREFTO cl_crm_bol_entity,

        lr_entity_cur 
TYPEREFTO cl_crm_bol_entity,

        ls_zforecast
TYPE zforecast.



  lr_comp ?= me->comp_controller.

 
CHECK lr_comp ISBOUND.

  lr_entity ?= lr_comp->typed_context->btadminh->collection_wrapper->get_current( ).
*"Reading the btadminh entity, which is bound to component controller's
*  btadminh context node.

  me->typed_context->zforec->deselect_all( ).
*    me->typed_context->ZFOREC->select_all( ).



 
IF lr_entity->is_changeable( ) = abap_true.

    lr_core = cl_crm_bol_core=>get_instance( ).

    lr_fac = lr_core->get_entity_factory(
'ZFORECAST' ).

    lt_params = lr_fac->get_parameter_table( ).

   
TRY.

        lr_ent = lr_fac->create( lt_params ).

       
IF lr_ent ISBOUND.

         
CHECK lr_ent->lock( ) = abap_true.
*****************

         
CLEAR:lv_cmp_guid,lv_objid,

                lv_pred_guid,lv_quot_id.
* Read the parent entity BTADMINH guid
* " Copy BtAdminh Guid to Forecast table Opportunity Guid

          lr_entity->get_property_as_value(
EXPORTING iv_attr_name = 'GUID'

                                           
IMPORTING ev_result = lv_cmp_guid ).

          lr_ent->set_property( iv_attr_name =
'Z_HD_OPPT_GUID'

                                iv_value = lv_cmp_guid ).


** Objectid/ Doc no is genearted only after save , so may be blank while in create mode.

          lr_entity->get_property_as_value(
EXPORTING iv_attr_name = 'OBJECT_ID'

                                           
IMPORTING ev_result = lv_objid ).

          lr_ent->set_property( iv_attr_name =
'Z_HDR_OPPT_NO'

                                iv_value = lv_objid ).


******************  Default the values for quot  no *******************************
*PREDECESSOR_GUID Quot Guid when create follow up doc is selected

          lr_entity->get_property_as_value(
EXPORTING iv_attr_name = 'PREDECESSOR_GUID'

                                           
IMPORTING ev_result = lv_pred_guid ).

          lr_ent->set_property( iv_attr_name =
'Z_HDR_QUOT_GUID'

                                iv_value = lv_pred_guid ).


** SOMETIMES PREDECESSOR_GUID is empty then get it from transaction history

         
IF lv_pred_guid ISINITIAL.

            ls_borident-objkey  = lv_cmp_guid.

            ls_borident-objtype =
'BUS2000111'.

            ls_borident-logsys  =
''.



           
CALLFUNCTION'Z_DISPLAY_LIST_OF_RELATIONS'

             
EXPORTING

                object              = ls_borident

                max_hops            =
'99'

             
TABLES

                it_neighbor_objects = it_neighbor_objects

             
EXCEPTIONS

                no_logsys          =
1

                internal_error      =
2

               
OTHERS              = 3.

           
IF sy-subrc = 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
*        WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

             
LOOPAT it_neighbor_objects INTO is_neighbor_objects

               
WHERE roletype = 'VORGAENGER'"Preceding doc

               
AND objtype = 'BUS2000115'"quotes

                lv_pred_guid = is_neighbor_objects-objkey.

             
ENDLOOP.

           
ENDIF.



         
ENDIF.
*Get PREDECESSOR Quot no when create follow up doc is selected

         
IF lv_pred_guid ISNOTINITIAL.

           
SELECTSINGLE object_id INTO lv_quot_id

             
FROM crmd_orderadm_h WHERE guid = lv_pred_guid.
*          lr_entity->get_property_as_value( EXPORTING iv_attr_name = 'PREDECESSOR_GUID'
*                                            IMPORTING ev_result = lv_pred_guid ).

            lr_ent->set_property( iv_attr_name =
'Z_HDR_QUOT_NO'

                                  iv_value = lv_quot_id ).

         
ENDIF.
*****************************************************************************
*Read the last record from collection wrapper

          lr_coco ?= me->comp_controller.

         
IF lr_coco ISBOUND.

           
CLEAR: lv_new_month,lv_new_zvalue.
* Get the Last entity

            lr_entity_so ?= lr_coco->ztyped_context->zforecast->collection_wrapper->get_last( ).



           
IF lr_entity_so ISBOUND.
**Get prev month

              lr_entity_so->get_property_as_value(
EXPORTING iv_attr_name = 'Z_MONTH'

                                               
IMPORTING ev_result = lv_new_month ).
*** Add 1 month for new entry

             
IF lv_new_month+4(2) = '12'"if december that add new yr

                lv_new_month+
0(4) = lv_new_month+0(4) + 1 .

                lv_new_month+
4(2) = '1'.

                lv_new_month = lv_new_month.

             
ELSE.

                lv_new_month = lv_new_month +
1" add 1 month

             
ENDIF.



              lr_ent->set_property( iv_attr_name =
'Z_MONTH'

                                                iv_value =  lv_new_month ).
**********
**** Get the current zvalue
*              lr_entity_cur ?= lr_coco->ztyped_context->zforecast->collection_wrapper->get_current( ).
*              IF lr_entity_cur IS BOUND.
*                lr_entity_cur->get_property_as_value( EXPORTING iv_attr_name = 'Z_VALUE'
*                                                IMPORTING ev_result = lv_cur_zvalue ).
*              ENDIF.
***

              lr_entity_so->get_property_as_value(
EXPORTING iv_attr_name = 'Z_VALUE'

                                               
IMPORTING ev_result = lv_new_zvalue ).
**new value =  curr qty  * ????
*              lr_ent->set_property( iv_attr_name = 'Z_VALUE'
*                                                iv_value =  lv_new_zvalue ).



              lr_entity_so->get_property_as_value(
EXPORTING iv_attr_name = 'Z_CUR_QTY'

                                               
IMPORTING ev_result = ls_zforecast-z_cur_qty ).

              lr_ent->set_property( iv_attr_name =
'Z_PREV_QTY'

                                            iv_value =  ls_zforecast-z_cur_qty ).



              lr_entity_so ?= lr_coco->ztyped_context->zforecast->collection_wrapper->get_next( ).



           
ENDIF.

         
ENDIF.
*****************************





          me->typed_context->zforec->collection_wrapper->add( iv_entity = lr_ent ).

          me->typed_context->zforec->visible_first_row_index = me->typed_context->zforec->collection_wrapper->size( ).

       
ENDIF.

     
CATCH cx_crm_genil_model_error.

       
EXIT.

     
CATCH cx_sy_ref_is_initial.

   
ENDTRY.

 
ENDIF.


endmethod.




 

 

 

 

 

 

 

method EH_ONDELETE.

 
DATA :    lr_entity TYPEREFTO cl_crm_bol_entity,

          lr_current
TYPEREFTO if_bol_bo_property_access,

          lr_col
TYPEREFTO if_bol_bo_col,

          lr_core
TYPEREFTO cl_crm_bol_core,

          lv_size
TYPEi.



  lr_col ?= typed_context->ZFOREC->collection_wrapper->get_marked( ).

  lv_size = lr_col->size( ).

 
IF lv_size > 0.

   
DO lv_size TIMES.

     
IF sy-index = 1.

        lr_current = lr_col->get_first( ).

     
ELSE.

        lr_current = lr_col->get_next( ).

     
ENDIF.

      lr_entity ?= lr_current.

      typed_context->ZFOREC->collection_wrapper->remove( lr_current ).

      lr_entity->delete( ).

   
ENDDO.

    lr_core = cl_crm_bol_core=>get_instance( ).

    lr_core->modify( ).

    typed_context->ZFOREC->deselect_all( ).



 
ENDIF.
endmethod.








method EH_ONEDIT.

   
DATA:    lr_tx          TYPEREFTO if_bol_transaction_context,

          lr_entity     
TYPEREFTO cl_crm_bol_entity,

          lr_comp
typeREFTO CL_BT111H_O_BSPWDCOMPONEN_IMPL.
*          cl_bt111h_o_bspwdcompone0_impl.


*lr_entity ?= me->typed_context->ZFOREC->collection_wrapper->get_first( ).
*lr_entity ?= me->typed_context->ZFOREC->collection_wrapper->get_next( ).


*me->view_group_context->set_view_editable( me ).





  lr_comp ?= me->comp_controller.

 
Check lr_comp isBOUND.

  lr_entity ?= lr_comp->typed_context->btadminh->collection_wrapper->get_current( ).

 
CHECK lr_entity ISBOUND.

 
IF lr_entity->lock( ) = abap_true.

    me->view_group_context->set_view_editable( me ).

 
ENDIF.



  lr_entity ?= me->typed_context->ZFOREC->collection_wrapper->get_first( ).

 
WHILE lr_entity ISBOUND.

    lr_entity->lock( ).

    lr_entity ?= me->typed_context->ZFOREC->collection_wrapper->get_next( ).

 
ENDWHILE.
endmethod.

 

 

Create On_new_focus method in the "ZFOREC" context node, specify it as a event handler for collection wrapper new_focus event.
12.1.png

12.2.png
Create the  event parameter
12.3.png

 

13. Set handler in Connect nodes method of the CTXT class.

13.png
  method CONNECT_NODES.
DATA: coll_wrapper TYPEREFTO cl_bsp_wd_collection_wrapper,         

      comp_controller TYPEREFTO ZL_BT111H_O_BSPWDCOMPONEN_IMPL.

coll_wrapper = me->BTADMINH->get_collection_wrapper( ).

setHANDLER me->ZFOREC->on_new_focus for coll_wrapper ACTIVATION iv_activate.
endmethod.

 

14. As Save event is handled in different view (BT111H_OPPT/OpportunityOVViewSet), ZFOREC data has to be bind to custom/component controller to access it in other views.

14.png

15. Now create new Custom controller "ZFORECAST" with ZFORECAST and BTADMINH node and perform the binding as shown below using the binding wizard.

15.png

15.1.png

 

 

 

So with this binding now ZFORECAST can be called in any page view in this component

16 .Further now also add the ZFOREC node to BT111H_OPPT/OpptDetailsCuCo custom component as per  below, so that ZFOREC can be now displayed in the opportunity details page.

 

 

 

 

16.png

 

17 . Redefine eh_onsave event of the save method in  "BT111H_OPPT/OpportunityOVViewSet" component/view.

17.png
method EH_ONSAVE.
*CALL METHOD SUPER->EH_ONSAVE
**  EXPORTING
**    htmlb_event    =
**    htmlb_event_ex =
*    .
 
DATA: lv_items TYPEREFTO cl_bt111h_o_itemslistov_impl.

 
DATA: lv_success TYPE crmt_boolean.

 
DATA: lv_success_save TYPE crmt_boolean.

 
DATA:lr_ent              TYPEREFTO      cl_crm_bol_entity,

      lr_tx             
TYPEREFTO      if_bol_transaction_context.

 
data: lr_controller TYPEREFTO cl_bsp_wd_appl_controller,

        lr_msg_srv
TYPEREFTO cl_bsp_wd_message_service.

 
data:lr_tx_ctxt TYPEREFTO if_bol_transaction_context, "lr_tx

      lr_coll_wr
TYPEREFTO cl_bsp_wd_collection_wrapper,

      my_tx_context 
TYPEREFTO cl_crm_bol_custom_tx_ctxt, " Z cntx

      lr_entity_so 
TYPEREFTO cl_crm_bol_entity,

      rv_success   
TYPE        abap_bool,

      lr_tx_manager
TYPEREFTO if_crm_uiu_bt_channel_aspects,"cl_crm_uiu_bt_channel_asp_fac,
*    lr_tx_manager TYPE REF TO cl_mktprj_transaction_mgr.

      lr_coco
TYPEREFTO ZL_BT111H_O_BSPWDCOMPONEN_IMPL.
* cancel save because of mandatory fiels

  lr_controller ?= comp_controller->m_parent.

  lr_msg_srv ?= me->view_manager->get_message_service( ).

 
IF cl_crm_uiu_bt_tools=>mandatory_flds_no_save_allowed( ir_controller = lr_controller

                                                          ir_msg_srv    = lr_msg_srv ) = abap_true.

   
EXIT.

 
ENDIF.

 
IF cl_crm_uiu_bt_partner_popup=>get_quick_create_status( ) = abap_true OR gc_abc EQ abap_true.
*Save business partner-accounts and related objects (contacts, conditions,..)

    lv_success = cl_crm_uiu_bp_tools=>save( ).

    cl_crm_uiu_bt_partner_popup=>set_quick_create_status(
EXPORTING iv_quick_create = abap_false ).

    gc_abc = abap_false.

 
ELSE.

    lv_success = abap_true.

 
ENDIF.



 
CALLMETHOD cl_crm_uiu_bt_tools=>save( EXPORTING ir_node = me->typed_context->btorder ).

 
CLEAR:lr_tx_ctxt,my_tx_context.



 
CREATE OBJECT my_tx_context.
  lr_coco ?= me->comp_controller.

 
IF lr_coco ISBOUND.
* Get the First entity

    lr_entity_so ?= lr_coco->ztyped_context->ZFORECAST->collection_wrapper->get_first( ).
* Loop on the collection

   
WHILE lr_entity_so ISBOUND.

      lr_tx_ctxt = lr_entity_so->get_transaction( ).

      my_tx_context->add_tx_context( lr_tx_ctxt ).

      my_tx_context->if_bol_transaction_context~save( ).

      my_tx_context->if_bol_transaction_context~
commit( ).
*      * Get the next entity

      lr_entity_so ?= lr_coco->ztyped_context->ZFORECAST->collection_wrapper->get_next( ).

   
ENDWHILE.
 
ENDIF.
  rv_success = cl_crm_uiu_bp_tools=>save( ).


 
IF rv_success = abap_true AND me->view_group_context ISNOTINITIAL.
*    eh_oncancel( ).

 
ENDIF.
****************


* remove and add entity in order to refresh all context nodes (selections, etc.)
* and lock registration is done

  lr_ent ?= me->typed_context->btorder->collection_wrapper->get_current( ).

  me->typed_context->btorder->collection_wrapper->clear( ).

 
IF lr_ent ISBOUND.

    me->typed_context->btorder->collection_wrapper->add( iv_entity    = lr_ent

                                                        iv_set_focus = abap_true ).

 
ENDIF.


*  ** get current

  lr_entity_so ?= lr_coco->ztyped_context->ZFORECAST->collection_wrapper->get_current( ).

  me->ztyped_context->ZFOREC->collection_wrapper->clear( ).
endmethod.

 

Add the following code in on_new_focus method,here logic is to retrieve the order entities collection related to Order only if parent entity opportinity changes

18.png

 

18.Add the ZFOREC view to runtime repository..

 

18.png


19. Expose "ZFOREC" view on BT111H_OPPT/OpportunityOVViewSet Configuration
page, maintain view title and save...
19.png

20. That's it ...this is how it appears within WebUi page of opportunity with Create/Edit/Delete functionality.



 

How to call Web Client UI Components via URL

$
0
0

Hello everyone,

 

I published a WIKI page which explains how to start the Web Client UI with a particular component and optional data. For example how a URL must be entered to display the overview page of a particular contact person in the workarea of the WebClient UI. Customers can benefit of this feature when it comes to using non SAP Portals where they want to have links that directly launch specific components.

Viewing all 188 articles
Browse latest View live


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