Quantcast
Channel: Customer Relationship Management (SAP CRM)
Viewing all 228 articles
Browse latest View live

step by step to enable your Genil component with AET - Part One

$
0
0
Note: normally the AET enablement is done by SAP or Partner but not for customer, since if customers are able to build their own component by themselves, they can enhance it freely whenever they want without the help of AET.
Recently we got a requirement from customer that they want a non-extensible UI to be supported by Application extension tool ( AET ).
Since I found currently there is no detailed step by step about how to achieve this, I jsut create one.
AET is a powerful tool which could allow a user without any technical background to create new extension fields on UI in just a few seconds.
Equipped with AET, your application becomes more flexible since it allows your customer to create new fields by themselves which are missing in your standard application.
For demonstration usage I just use a component ZCRMSM which is copied from SAP standard genil component CRMSM for example.
( by the way, CRMSM is developed by us in CRM7.0 EHP3 which exposes the functionality to integrate social channel like twitter and facebook into CRM.  )
The genil mode is very simple: It just have one Root object, one query object and one query result object.
clipboard1.png
step1.
Create a new structure as below. EEW standards for easy enhancement workbench and SMC for Social media cockpit.
This structure acts as a place holder to attach extension fields created by AET. the component EXT_SMC is just a dummy field tocircumvent the
restrict that netweaver does not allow you to activate an empty structure.
clipboard2.png
step2:
create a new dependent object SocialPostSetCRM_SMC to represent the extension fields belonging to a social post.
Of course a Aggregation must also be created to allow the navigation from SocialPost to this DO.
step2.png
Now we have to create the key structure and attribute structure for DO.
step3:
create key structure for DO. Post ID is the unique identifier to identify a social post. UUID could also be used. Here I use post id since it is more human readable compared with UUID, which makes my trouble shooting easier.
step3.png
for attribute structure, just include the key attribute above and the EEW structure created in step1, and that's all.
The EEW structure is included so that every time the extension fields are created, they will be attached to EEW structure and then would
also automatically appear in this attribute structure as well.
step31.png
Now the enhancement for Genil component is finished, of course we need to implement the access logic on the DO as well. We will do this later and let's continue with our modelling task.
step4:
create a database table ZCRM_SOC_FE to store the transaction data of extension fields. Just the same logic in step3 of attribute structure: include key structure  and EEW structure.
clipboard4.png
step5:
tcode AXTREG, register your genil component as extensible.
Create a new entry in "Extensible BOs". In this example I use ZSOCIALPOST. It is just an indicator, no need to equal with your Genil component name or model node name.
clipboard5.png
Navigate to BO Parts. Create a new entry. Specify "Part of a BO". Here I just use "HEADER", which tells AET framework I want to put my extension fields on "Header" level of the Social Post.
Maintain the Field Enhancement Place - the extension fields will be attached to the EEW structure maintained here.
So technically speaking, if customer choose HEADER in AET ui, the extension fields will be created on EEW structure ZINCL_EEW_SMC.
The remain settings could just be maintained as below:
clipboard51.png
create a new application group, or you can also reuse existing one if there is any which make senses for your business requirement.
Here I just create a new one "SOCIALMEDIA".
clipboard52.png
click "Places" and register your EEW structure ZINCL_EEW_SMC here. Special the max number of fields you allow to put into this structure.
And also the max bytes which are allowed for the total extension fields.
The sum of Percentage customer, partner and SAP should be 100, the rule of thumb value would be 60:20:20 ( customer : partner: SAP )
clipboard53.png
double click on the sub folder "BOL objects" and maintain the DO for extension fields and its BOL Path from Root object such as below:
clipboard54.png
You may observe there are some other folders like Search place and Table place. Those are places where the registration for table and search enhancement are done.
This is out of scope of this blog. Now all the necessary parts in AXTREG are done.
step6.
Tcode SM30, choose view BSPDLCV_OBJ_TYPE and click maintain button. Then create a new entry with object type CCRM_SMC
and maintain Genil component name, the root object name, the BOR object type and extension BO name, which is created in step 5.
clipboard6.png
step7: go to the view where you would like to display the extension fields, redefine method DO_CONFIG_DETERMINATION.
clipboard7.png
Just specify the ui object type created in step6:
method DO_CONFIG_DETERMINATION.    CHECK iv_first_time = abap_true.    me->set_config_keys( iv_object_type          = 'CCRM_SMC'                         iv_propagate_2_children = abap_false ).  endmethod.
Now launch your UI, click configure icon to reach AET page, you should see "Create Field" button is enabled, which means all registration parts done currently are correct. Now the left task is doing enhancement on Genil implementation, which will be discussed in detail in part2.
clipboard8.png

step by step to enable your Genil component with AET - Part Two

$
0
0
in part1 we discuss about:
1. How to enhance your genil component ( create a new Dependent object and corresponding DDIC structure ) to support AET
2. how to create a new UI object type
3. Create necessary EEW include structure and respective DB table to store FE transaction data.
In this part2 I will show you how to enhance the Genil component implementation class.
step1: support read access. If there is transaction data stored in DB table ZCRM_SOC_FE, it must be fetched and displayed in UI accordingly.
The read access is done in genil implementation class method IF_GENIL_APPL_INTLAY~GET_OBJECTS.
The original code before enhancement is using handler style. Every node has its corresponding handler class to do the job.
The handler class for SocialPost is ZCL_CRM_SOC_POST, which means in order to support read access of extension fields, we need to enhance ZCL_CRM_SOC_POST->READ_OBJECT.
 lr_object = go_handler_factory->get_handler( iv_object_name = lv_name ).
TRY.      lr_post ?= lr_object.
CATCH:cx_sy_move_cast_error.     EXIT.
ENDTRY.
CHECK lr_post IS NOT INITIAL.
CALL METHOD lr_post->read_object    EXPORTING        it_request_objects = it_request_objects        iv_ref             = iv_root_list   CHANGING        ct_bapireturn      = lt_bapireturn. 
This blog will not go deep into the Genil component implementation itself, but just focus what needs to be enhanced in Genil component implementation.
as you see, I just add the code surrounded with red to read the extension field data. I pass the reference of container object into method get_fe_data.
After that method is executed, the extension field data will be fetched and filled into the container object lo_post.
clipboard1.png

 

 

method GET_FE_DATA.    data: lv_guid         TYPE crmt_genil_object_guid,          lv_attr         TYPE ZCRM_EXT_SMC_DYNP_SV,          lv_convert_guid TYPE socialdata-socialdatauuid,          lv_post_id      TYPE socialdata-internal_id.    data(lv_children) = iv_container->GET_CHILDREN( IV_AS_COPY = ABAP_TRUE ).    data(lv_child) = LV_CHILDREN->GET_FIRST( ).    ASSERT lv_child IS BOUND.    iv_container->get_key( IMPORTING es_key = lv_guid ).    lv_convert_guid = lv_guid.    lv_attr = zcl_crm_soc_fe_buffer_access=>get( lv_convert_guid ).    lv_child->set_attributes( lv_attr ).    DATA(lv_attr_props) = lv_child->get_attr_props_obj( ).    lv_attr_props->set_all_properties( if_genil_obj_attr_properties=>changeable ).    SELECT SINGLE internal_id INTO lv_post_id FROM socialdata WHERE socialdatauuid = lv_convert_guid    ASSERT sy-subrc = 0.    data(ls_key) = VALUE ZCRM_EXT_SMC_KEY( client = sy-mandt post_id = lv_post_id ).    lv_child->set_key( ls_key ).  endmethod.
in get_fe_data, there is nothing special logic inside. The real data is returned via zcl_crm_soc_fe_buffer_access, which encapsulate the buffer handling of extension field data in current session.
so let's have a look at the buffer class zcl_crm_soc_fe_buffer_access's get method:
method GET.    DATA: ls_data TYPE ZCRM_SOC_FE,          ls_attr TYPE ZCRM_EXT_SMC_DYNP_SV.    READ TABLE st_buffer ASSIGNING FIELD-SYMBOL(<buffer>) WITH KEY uuid = iv_guid.    IF sy-subrc = 0.        rs_data = <buffer>-data.        RETURN.    ENDIF.    SELECT SINGLE * INTO CORRESPONDING FIELDS OF ls_data FROM ( ZCRM_SOC_FE AS A INNER JOIN  socialdata AS B        ON B~internal_id = A~post_id ) WHERE B~SOCIALDATAUUID = iv_guid.    CHECK ls_data IS NOT INITIAL.    MOVE-CORRESPONDING ls_data TO ls_attr.    DATA(ls_entry) = VALUE ty_buffer( uuid = iv_guid data = ls_attr ).    APPEND ls_entry TO st_buffer.    rs_data = ls_attr.  endmethod.
first check in the buffer table whether there is already an entry there for the current social post. If buffer hits, the bufferred data is directly returned.
If not, just load it from DB table. Why I use inner join is the type of buffer table has uuid as key, not internal id. And the uuid is the key field of hosting table of Social post, that is SOCIALDATA.
After the data is loaded from DB, never forget to insert it into buffer table.
types:    BEGIN OF ty_buffer,           uuid TYPE socialdata-socialdatauuid,           data TYPE ZCRM_EXT_SMC_DYNP_SV,         END OF ty_buffer .  types:    tt_buffer TYPE STANDARD TABLE OF ty_buffer WITH KEY uuid .
now the read access of extension fields are done.
step2: write access.
After we type some value in UI fields and click enter key, IF_GENIL_APPL_INTLAY~MODIFY_OBJECTS will be called. Just the same logic as step1, the call will be delegated to handler class ZCL_CRM_SOC_POST. In method MODIFY_OBJECT, I only add one more line to handle with extension fields by creating a new method process_children.
step2.png
for simplicity reasons I only react on the change mode. Again I pass the call to zcl_crm_soc_fe_api.
clipboard2.png
in set_fe_data I just put the list of changed attribute ( lt_changed_attr ) and the latest data typed by user ( ls_attributes ) to buffer class.
clipboard3.png
in set method of buffer class, again the similiar logic as done in read case. First check in the buffer table whether there exists an entry for current social post.
If buffer hits, I just to update the buffer with latest extension fields data. I loop the internal table it_changed_attr to make sure all changed attributes are handled.
clipboard4.png
clipboard5.png
if there is no entry in the buffer, the situation should be that such post has not any extension fields stored in DB table at the begining of this transaction.
The iv_new_data would come from the manual input typed by user in UI. In this case all I need is just to append the latest extension field data into the buffer table.
step3: save case.
No doubt the method SAVE_OBJECT of ZCL_CRM_SOC_POST must now take care the saving of both SocialPost itself and extension fields data as well.
I just add two more lines to original code.
I first save the extension field( done by zcl_crm_soc_fe_api=>save_fe_data), then the SocialPost itself ( via function module SOC_POST_SAVE ).
The condition "no value changed" is enhanced that only the situation neither Socialpost nor its extension fields are changed could be regarded as no value changed. If the line 18 is missing, you will receive message in UI "No value changed", even you have already modified some extension field but not touch standard field in social post itself.
clipboard6.png
The method save_fe_data is also very simple: just fetch the extension field buffer from buffer table and then modify database table.
  method SAVE_FE_DATA.
    DATA: ls_change TYPE ZCRM_SOC_FE.
    DATA(ls_buffer) = zcl_crm_soc_fe_buffer_access=>get( iv_guid ).
    CHECK ls_buffer IS NOT INITIAL.
    MOVE-CORRESPONDING ls_buffer TO ls_change.
    MODIFY zcrm_soc_fe FROM ls_change.
    rv_fe_changed = abap_true.
  endmethod.
Summary:
The genil component implementation is enhanced regarding following three parts:
1. enhance IF_GENIL_APPL_ALTERNATIVE_DSIL~SAVE_OBJECTS to support extension field save.
2. enhance IF_GENIL_APPL_ALTERNATIVE_DSIL~MODIFY_OBJECTS to support extension field change.
3. enhance IF_GENIL_APPL_ALTERNATIVE_DSIL~GET_OBJECTS to support extension field change.
Now it's time to enjoy our labour! Just create three extension fields to test. The three extension fields has type Quantity and sub type PRESS ( built-in type provided by SAP), so AET framework automatically creates two seperate fields for you, one for quantity value and the other for unit.
clipboard7.png
clipboard8.png
Those features like F4 help and validation logic for respective data type are automaitcally handled by AET framework. You do not need to code by yourself
Now have a look at the EEW structure created in part4. All extension fields are attached here.
clipboard9.png
And extension fields data are stored in DB.
clipboard10.png

maintain attachment in popup window

$
0
0
Hi friends,
you can see attachment ui everywhere in SAP CRM ui but most of them appear within assignment block.
This article just tells you how to put the attachment maintenance ui within pop up window.
The user can click hyperlink in table column "Post ID" to maintain the attachments for the selected post.
This article also shares the tip how to control the enablement of attachment maintenance dynamically.
As you see there is a checkbox "Allow Edit" in first table UI. If checked and navigate to attachment popup window, the attachment maintenance is enabled. Otherwise you will see all buttons in attachment popup window is disabled.
clipboard1.png
                                                                                 Figure1
The invovled UI component in this solution:
1. ZSMCQR - the first UI, contains search view with only one search parameter "Post Internal ID", the checkbox to control attachment
maintenance, abd the search result view.
2. ZSMCATT - our wrapper component to load the reuse attachment component.
3. GS_CM - standard UI component provided by SAP exposing attachment operation like create, save and delete. The benefit of using it is that now
it is not necessary for application developer to care about those CRUD operations on attachment - they are all taken over by content management
framework.
4. GSURLPOPUP - standard UI component about popup window implementation
step1:
in our first host UI component, declare one component usage to GSURLPOPUP.
clipboard2.png
                                                                 Figure2
step2:
we would like to make field INTERNAL_ID ( label in UI: Post ID) as hyperlink and trigger a pop up window, so we need to implement a
P-Getter on it:
METHOD GET_P_INTERNAL_ID.  CASE iv_property.      WHEN if_bsp_wd_model_setter_getter=>fp_fieldtype.        rv_value = cl_bsp_dlc_view_descriptor=>field_type_event_link.      WHEN if_bsp_wd_model_setter_getter=>fp_onclick.        rv_value = 'ATTACHMENT'.   ENDCASE.
ENDMETHOD.
Then create a event handler for event ATTACHMENT to pop up a window. In the event handler implementation, we plan to pop up the
attachment maintenance window. Before we write code, we need to define one more component usage:
clipboard3.png
                                                  Figure3
Now we are ready to implement the event handler:
  DATA: lr_popup TYPE REF TO IF_BSP_WD_POPUP,          lv_post TYPE REF TO cl_crm_bol_entity,          lv_bol_col TYPE REF TO CL_CRM_BOL_BO_COL.    lv_post = zcl_jerry_tool=>get_selected_post_by_event( iv_col_wrapper = me->typed_context->searchresult->collection_wrapper        iv_event = htmlb_event_ex->event_id ).    CREATE OBJECT lv_bol_col.    lv_bol_col->IF_BOL_BO_COL~add( lv_post ).  lr_popup = comp_controller->window_manager->create_popup(     iv_interface_view_name = 'ZSMCATT/MainWindow'     iv_usage_name = 'ZSMCATT'     iv_title = 'Social Post Attachment Maintenance' ).  lr_popup->set_on_close_event( iv_view = me iv_event_name =
'CLEAR').  lr_popup->open( iv_inbound_plug = 'FROM_SEARCH' iv_collection = lv_bol_col ).

all the content in pop up window is encapsulated in interface view MainWindow of ZSMCATT. Our next step will focus on its implementation.
the method get_selected_post_by_event is a handy method which just returns the selected post BOL entity via event:
DATA:
    lv_index_str TYPE string,    lv_idx       TYPE string,    lv_index     TYPE i,    lv_result    TYPE REF TO cl_crm_bol_entity.    lv_index_str = iv_event.  SHIFT lv_index_str UP TO '[' LEFT CIRCULAR.  SHIFT lv_index_str BY 1 PLACES.  WHILE lv_index_str(1) <> ']'.    CONCATENATE lv_idx lv_index_str(1) INTO lv_idx.    SHIFT lv_index_str BY 1 PLACES.  ENDWHILE.  lv_index = lv_idx.  CHECK lv_index >= 0.  lv_result ?= iv_col_wrapper->find( iv_index = lv_index ).  CHECK lv_result IS NOT INITIAL.  rv_result = lv_result->get_related_entity( 'SocialPostRel' ). "#EC NOTEXT
step3:
component ZSMCATT just contains one overview page and one post header page.
The overview page has two assignment blocks: one( headerWindow ) for post header which indicates the current post being maintained, the other one( attachmentWindow ) just displays the UI of reuse component GS_CM.
f4.png
                                                                      figure4
see picture below:
clipboard5.png
                                                                           Figure5
the view contained in the attachmentWindow is not developed by us, but reuse from GS_CM.
so we define one component usage AttachmentUploadDet and include its interfacre view MainWindow into our attachmentWindow, as shown below:
clipboard6.png
                                                                 Figure6
clipboard7.png
                                                                                          Figure7
Again the new pop up window shown in right part of screenshot above is also another standard UI of GS_CM, so we just define another component usage to include it into our component:
clipboard8.png
in figure5 you observed that there is a hyperlink "Properties", once clicked it will navigate to attachment property UI, which is also one standard part of GS_CM.
clipboard9.png
I call this window as "DetailWindow". So finally I have the following windows:
clipboard10.png
It means we need to define the third component usage on GS_CM to include that part into our own UI:
clipboard11.png
step4
you may find that in three component usage, the same interface view MainWindow of GS_CM is included. So the real page that would be displayed in GS_CM.MainWindow depends on which outbound plug from our own UI component is called and which inbound plug of target UI will catch that.
So in this step we will define navigate link to specify the navigation behavior of each.
clipboard12.png
clipboard13.png
step5
in component controller of ZSMCQR, do component context node binding for component usage ZSMCATTR:
clipboard14.png
method WD_USAGE_INITIALIZE.   DATA:          lv_usage  TYPE REF TO if_bsp_wd_component_usage,          lo_entity TYPE ref to cl_crm_bol_entity.    CHECK me->typed_context->socialpost->collection_wrapper IS NOT INITIAL.    lv_usage = me->comp_controller->get_component_usage( iv_usage->usage_name ).    IF lv_usage->usage_name = 'ZSMCATT'.      CALL METHOD lv_usage->bind_context_node         EXPORTING           iv_controller_type  = cl_bsp_wd_controller=>co_type_component           iv_target_node_name = 'SOCIALPOST'                   "#EC NOTEXT           iv_node_2_bind      = 'SOCIALPOST'.                  "#EC NOTEXT    ENDIF.  endmethod.
step6
in component ZSMCATTR, create a custom controller and create a context node CMBO. Implement its on_new_focus method:
custom.png
METHOD on_new_focus.
 DATA: lr_qs              TYPE REF TO cl_crm_bol_query_service,       lr_data            TYPE REF TO data,       ls_cmbo_prop       TYPE crmt_cmic_rfolder_attr,       lr_entity_col      TYPE REF TO if_bol_entity_col.  FIELD-SYMBOLS: <fs_guid> TYPE crmt_genil_object_guid.  CHECK focus_bo IS BOUND.  TRY.      lr_qs = cl_crm_bol_query_service=>get_instance( cl_crm_cm_genil_comp=>gc_query_bo_link ).    CATCH cx_root.                                      "#EC NO_HANDLER      RETURN.  ENDTRY.  CHECK lr_qs IS BOUND.  lr_data = focus_bo->get_property( 'UUID' ).  ASSIGN lr_data->* TO <fs_guid>.  ls_cmbo_prop-instid = <fs_guid>.  ls_cmbo_prop-typeid = 'BUS1006'. ------------- use your own BOR type here!  ls_cmbo_prop-catid = 'BO'.  lr_qs->set_properties( ls_cmbo_prop ).  lr_entity_col = lr_qs->get_query_result( ).  me->collection_wrapper->clear_collection( ).  me->collection_wrapper->set_collection( lr_entity_col ).  ENDMETHOD.
in component controller of ZSMCATTR, do the context node binding logic:
METHOD wd_usage_initialize.
DATA: lr_cn       TYPE REF TO cl_bsp_wd_context_node,           lr_property         TYPE REF TO if_bol_bo_property_access,           lr_cn_attr  TYPE REF TO if_bol_bo_property_access,           lr_cuco_attachement TYPE REF TO CL_ZSMCATT_CMBO_IMPL,           ls_cm_attr  TYPE crmt_cm_comp_st.   IF NOT me->context IS BOUND.     me->wd_create_context( ).   ENDIF.   CASE iv_usage->usage_name.     WHEN 'AttachmentUploadDet' or 'AttachmentUpload' or 'AttachmentProperty'.      CALL METHOD iv_usage->bind_context_node        EXPORTING          iv_controller_type  = cl_bsp_wd_controller=>co_type_custom          iv_name             = 'ZSMCATT/CMBO'          iv_target_node_name = 'CMBO'                "#EC NOTEXT          iv_node_2_bind      = 'CMBUSOBJ'.                "#EC NOTEXT       CALL METHOD iv_usage->bind_context_node        EXPORTING          iv_controller_type  = cl_bsp_wd_controller=>co_type_component
*         node at the calling component          iv_target_node_name = 'SOCIALPOST'                       "#EC NOTEXT
*         name of the called component          iv_name             = iv_usage->usage_name
*         node at the called component          iv_node_2_bind      = 'PARENTNODE'.   ENDCASE.
 ENDMETHOD.
never forget to call on_new_focus of custom controller context node manually:
lr_cuco_attachement ?= get_custom_controller( 'ZSMCATT/CMBO' ).
 IF lr_cuco_attachement IS BOUND.        lr_property = me->typed_context->socialpost->collection_wrapper->get_current( ).        lr_cuco_attachement->typed_context->cmbo->on_new_focus( lr_property ).        lr_cuco_attachement->typed_context->attribute->on_new_focus( lr_property ).
 ENDIF.
step7:
implement the inbound of outbound plug of window attachmentWindow,
clipboard16.png
and DetailWindow:
clipboard17.png
for source code of these two class please refer to attached document.
that's all.
Summary: in this solution we use three component usage on GS_CM, one for attachment list page in attachment assignblock, the second for Attachment upload page and the third for attachment property page. If you would like to know how to control the enablement of attachment maintenance dynamically, please continue to read the next blog.
clipboard18.png

dynamically control the enablement of attachment maintenance

$
0
0
in the previous blog we discussed how to maintain attachment in pop up window.
In production code it is ususally necessary to also have authorization control on attachment maintenance.
Most of the time it is done via authorization object.
In this example I just use a checkbox to control it during the whole session, in the screenshot below all buttons are disable since the "Allow Edit" checkbox is not selected.
clipboard1.png
The idea of control is, in GS_CM component controller there is a value node ATTRIBUTES which could be bound by consuming component. 
clipboard2.png
in the controller class below, Content management framework will firstly check whether the current BOR type is valid ( line 13 ~ line 18), an invalid type will lead to read only mode.
clipboard3.png
then the display mode flag will be set according to the field display of value node.
based on these findings, we can start development in our ui component.
step1:
create an event handler for checkbox status change and just use a global variable to store the flag:
clipboard4.png
step2:
create a new value node ATTRIBUTE in custom controller
clipboard5.png
redefine INIT.
here we buffer the collection wrapper reference into a global variable of component controller for later usage.
 method IF_BSP_MODEL~INIT.  super->if_bsp_model~init( id    = id                            owner = owner ).  mr_owner ?= owner.  DATA: lv_struct_ref TYPE REF TO crmt_cm_comp_st,        lv_value_node TYPE REF TO cl_bsp_wd_value_node,        lv_bo_coll    TYPE REF TO if_bol_bo_col.  CREATE DATA lv_struct_ref.  CREATE OBJECT lv_value_node    EXPORTING      iv_data_ref = lv_struct_ref.  CREATE OBJECT lv_bo_coll    TYPE      cl_crm_bol_bo_col.  lv_bo_coll->add( lv_value_node ).  set_collection( lv_bo_coll ).  CL_ZSMCATT_BSPWDCOMPONENT_IMPL=>GO_ATTR_WRAPPER = me->collection_wrapper.  endmethod.
redefine ON_NEW_FOCUS. The bufferred collection wrapper for ATTRIBUTE node is used here:
 method ON_NEW_FOCUS.     DATA:     lr_entity          TYPE REF TO cl_crm_bol_entity,     lr_parent          TYPE REF TO cl_crm_bol_entity,     lv_entity_name     TYPE string,     ls_cm_attr         TYPE crmt_cm_comp_st,     lv_product_type    TYPE comt_product_type,     lr_bo_attr         TYPE REF TO if_bol_bo_property_access,     ls_prtyp_cust      TYPE coms_prtyp_cust,     lv_object_guid     TYPE ib_recno_16,     lv_current         TYPE flag,     ls_button          TYPE crmt_thtmlb_button.  CHECK focus_bo IS BOUND.  lr_entity ?= focus_bo.  lv_entity_name = lr_entity->get_name( ).  IF CL_ZSMCATT_BSPWDCOMPONENT_IMPL=>gv_can_edit = 'X'.    ls_cm_attr-display = ''.  ELSE.    ls_cm_attr-display = 'X'.  ENDIF.  CHECK CL_ZSMCATT_BSPWDCOMPONENT_IMPL=>GO_ATTR_WRAPPER IS NOT INITIAL.  lr_bo_attr = CL_ZSMCATT_BSPWDCOMPONENT_IMPL=>GO_ATTR_WRAPPER->get_current( ).  CHECK lr_bo_attr IS BOUND.  lr_bo_attr->set_properties( ls_cm_attr ).  endmethod.
step3:
in WD_USAGE_INITIALIZE method of ZSMCATTR component controller, bind the context node of custom controller to GS_CM's attributes node.
CALL METHOD iv_usage->bind_context_node        EXPORTING          iv_controller_type  = cl_bsp_wd_controller=>co_type_custom          iv_name             = 'ZSMCATT/CMBO'          iv_target_node_name = 'ATTRIBUTE'                "#EC NOTEXT          iv_node_2_bind      = 'ATTRIBUTES'.                "#EC NOTEXT

lr_cuco_attachement ?= get_custom_controller( 'ZSMCATT/CMBO' ).
IF lr_cuco_attachement IS BOUND.
        lr_property = me>typed_context->socialpost->collection_wrapper->get_current( ).        lr_cuco_attachement->typed_context->cmbo->on_new_focus( lr_property ).        lr_cuco_attachement->typed_context->attribute->on_new_focus( lr_property ).

ENDIF. 

How to minimise Or prevent "Hard Coding" in SAP CRM custom developments

$
0
0

In every implementation of SAP CRM, We come across to the many challenges and one of these challenges is the prevention of hard coding in system enhancements.

 

For implementation point of view the hard coding of transaction types might cover current requirements, but maybe in the future a new transaction type will be created or removal of existing transaction type. In this case the transaction type was hard coded, a program change is required. So, Most of customer do not like such approach example the below code since it is not flexible.

m1.png

This blog is to describe ways of preventing hard coding, 

  • Use SAP Own way of parameter customizing option via the transaction STVARV

Go to Transaction STVARV and maintain the data. Create a parameter or select option data and fill in the values/constants that your program requires. 

m4.png

Use a select query in the beginning of your program to fetch the parameters you have maintained in the table.  At any point, any functional or support person can edit the entries in the TVARVC table and your program will consider the updated values without the need of modifying the code.

 

  • Object Oriented Approach By Creating a customizing table for these entries and Define an access class:-

Although, SAP provides it own parameter customizing option via the transaction STVARV. With this transaction it is possible to define your own parameter and set a value for this parameter. In my experience it is useful to have the option to deviate per sales organisation and even distribution channel or many more options based business. For Example, may be the French (FR) business has other requirements then the Dutch Business(NL). So in this example we have created our own table with the following key fields:

    • Usage Type – Parameter value
    • Sales Organisation – Sales Organisation
    • Distribution Channel – Distribution Channel

Define Customizing table
m5.png
Next to the Key fields we use the standard fields for a range table. This will make it easier to use the values in the calling code. For our example we now need to divine a parameter and create two entries in the table, one for each transaction type.

m6.png

Define Access class

To enable an easy access for calling programs we have created an access class that will build a range table. The method in this class will have the key fields of the table as import parameters. The export parameter will be a range table. One of the challenges here is that the range table should be different depending on the field that is in the value part. In our example this field should be of the same type as the field process type. We manage this by using a STANDARD TABLE and field symbols as follows.

m7.png

  DATA: lt_crm_customizing TYPE TABLE OF zcrm_customizing.

 

FIELD-SYMBOLS:

<lfs_crm_customizing> TYPE zcrm_customizing,

<lfs_custom_range>  TYPE any,

<lfs_sign> TYPE any,

<lfs_option> TYPE any,

<lfs_low> TYPE any,

<lfs_high> TYPE any.

 

CLEAR: et_custom_range.

SELECT * FROM zcrm_customizing INTO TABLE lt_crm_customizing

WHERE usage_type = iv_usage

AND sales_org = iv_sales_org

AND distr_channel = iv_distr_channel.

 

LOOP AT lt_crm_customizing ASSIGNING <lfs_crm_customizing>.

   APPEND INITIAL LINE TO et_custom_range ASSIGNING <lfs_custom_range>.

   ASSERT sy-subrc = 0.

   ASSIGN COMPONENT 'SIGN' OF STRUCTURE <lfs_custom_range> TO <lfs_sign>.

   ASSERT sy-subrc = 0.

   ASSIGN COMPONENT 'OPTION' OF STRUCTURE <lfs_custom_range> TO <lfs_option>.

   ASSERT sy-subrc = 0.

   ASSIGN COMPONENT 'LOW' OF STRUCTURE <lfs_custom_range> TO <lfs_low>.

   ASSERT sy-subrc = 0.

   ASSIGN COMPONENT 'HIGH' OF STRUCTURE <lfs_custom_range> TO <lfs_high>.

   ASSERT sy-subrc = 0.

   <lfs_sign> = <lfs_crm_customizing>-sign.

   <lfs_option>  = <lfs_crm_customizing>-opti.

   <lfs_low> = <lfs_crm_customizing>-low.

   <lfs_high> = <lfs_crm_customizing>-high.

ENDLOOP.

Calling Program

The calling program needs to define a range table of the type of the expected value. In our example we expect a process type in the value so we create the range table as follows.

DATA: lt_range_proces TYPE RANGE OF crmt_orderadm_h_wrk-process_type.

 

  "we will now call the access class with this table and will tell the class what field type it should use for the range table.

 

  zl_crm_customizing_access=>read_customizing( EXPORTING iv_usage  = 'CHECK_QUOTE_START_DATE'

                                                         iv_sales_org = lv_sales_org

                                                         iv_distr_channel  = lv_dis_channel

                                                 IMPORTING  et_custom_range = lt_range_proces ).

 

      The range table can now easily be used to check the transaction type.

CHECK ls_orderadm_h-process_type IN lt_range_proces.

     PS:  If the same check should be executed for more transaction types in the future, it is only a matter of creating a new entry in customizing table and the work is done.  

     Other scenario or examples where in the same table will/can be used are as follows-

    • ORDER_SAVE BAdI implementation with restriction on transaction type or check before save
    • Adding the description of an order/contract that is created via copy control.
    • Setting shipping condition based on sale organisation via CRM_SHIPPING_BADI




Reading the hierachy level in Multi-level Categorization

$
0
0

Multi-level Categorization is a kind of hierarchical categories which can be used in the business transaction for classification purposes. For example, in the service order, we can categorize the defect, reason, solutions into many hierarchical categories. One or more than one can be selected easily from the hierarchical tree in the service order header or item.

 

Multilevel categorization is available in varying scopes for the following applications:

  • Service orders
  • Service order templates
  • Confirmations
  • Complaints
  • In-house repair orders
  • Case management
  • Interaction record

 

In some cases if we need to know which level in the category is filled , then we need to calculate the node level which is filled and based on that we can determine whether the node level which we have to look for is filled or not. For e.g. in defect there are L1->L2->L3->L4->L5 , then if we need to find out if L5 is filled or not and if only level L3 is filled then the output that we would get is level 3 and we sure that L5 is not filled. In some business scenario we need to determine if a particular level is filled ( Level has to be filled mandatorily in the transaction). If the level is not filled we need to raise an error message. So the blog below would help to calcuate the level in a category. Using the APIs in the code below , you can twist and tweak as per your requirement.

 

 

data: lr_aspect   type ref to if_crm_erms_catego_aspect,      lr_category type ref to if_crm_erms_catego_category,      lv_ref_guid type guid_16 value '0050560600AD1ED2B8E18732B52E5D75',      lv_asp_guid type crm_erms_cat_guid,      lv_cat_guid type crm_erms_cat_guid,      lv_sel_level type int4,      ls_cat      type crmt_erms_cat_ca_buf.

call method cl_crm_ml_category_util=>get_categoryfirst  exporting    iv_ref_guid     = lv_ref_guid    iv_ref_kind     = 'A'
*   iv_catalog_type = 'C'"ORDER_READ->ET_SUBJECT->CAT_ID'  importing    er_aspect       = lr_aspect    er_category     = lr_category.

* get aspect guid
call method lr_aspect->get_asp_guid  receiving    rv_asp_guid = lv_asp_guid.
* get category guid
call method lr_category->get_details  importing    ev_cat = ls_cat.
lv_cat_guid = ls_cat-cat_guid.

cl_crm_ml_category_util=>get_selected_category_tree(  exporting    iv_selected_cat_guid = lv_cat_guid    " Selected Category GUID    iv_schema_guid       = lv_asp_guid    " Schema GUID  importing    ev_selected_level    = lv_sel_level    " Selected Category Level
*    ev_parent_cat_guid   =     " Parent Category GUID
*    ev_parent_cat_id     =     " Parent Category ID
*    ev_parent_level      =     " Parent Category level
).

The lv_sel_level would give you the level details. Based on business sceanrio , we can add validations .

Check , Clear and Catch to conquer

$
0
0

The topic of the blog may seem queer but as I would illustrate later it is very relevant in the lives of technical consultants. I would illustrate with examples how these approaches would help you succeed or if fail then fail gracefully ( read exception and dumps ) . Lets look into each of one at a time.

 

CHECK

 

  • For e.g. if you want to do something like this then this a recipe for sure shot disaster. If lv_entity is not bound then you would run into a dump/exception.You can use this in possibly many ways and you should use this as much as possible.
lv_bo ?= lv_entity.
lv_bo->set_property( iv_attr_name = ‘CITY’
iv_value = ‘Walldorf’ ).

 

the better way to the similar coding would be

lv_bo ?= lv_entity.
CHECK lv_bo is bound. 
lv_bo->set_property( iv_attr_name = ‘CITY’
iv_value = ‘Walldorf’ ).

or

lv_bo ?= lv_entity.
if lv_bo is bound.
lv_bo->set_property( iv_attr_name = ‘CITY’
iv_value = ‘Walldorf’ ).
endif. 

 

This is a very common failure scenario and is so common that we often tend to ignore this.

 

  • The second variety is when you write a new method/form etc. , then check whether the importing parameters are proper. This would mean if you expect a reference variable to be passed then it should be bound or if you have a mandatory parameter passed then it should be having some value. Similarly pass export only those parameters which are requested. ( This is not a disastrous case but can save performance).For e.g. if you are sending a collection as an importing parameter to a method then you can write the following statement. This way you know that the col is bound and contains entities

 

          CHECK ir_col is bound and ir_col->size(  ) > 0. 

 

 

CLEAR

 

This one is my personal favorite. Often uncleared variable could cause embarrassing problems and of magnitude you cannot even imagine. For e.g. you are in a conditional statement where you fill some variables based on condition being true and condition was false and since the variable were not cleared you still get data in variables. Or for e.g. you failed to clear an entity reference variable it would cause you lots of issues. I am providing some examples

 

lv_bo ?= lv_person.
lv_*** = lv_bo->get_property( ‘***’ ). “Key code
if lv_person->get_parent( ) is not initial.
lv_bo ?= lv_person->get_parent( ).
lv_*** = lv_bo->get_property( ‘***’ ). “Key code
ENDWHILE.

 

Now if the if condition is not true , then lv_*** is line no 5 should be blank but it would be filled with value fetched in line no 2. In this case it may not seem disastrous but in some cases it is extremely catastrophic.

 

The better way to do this would be following. As a general rule for methods which have returning or exporting parameters , clear the variables which are collecting values.

lv_bo ?= lv_person.
clear lv_***.
lv_*** = lv_bo->get_property( ‘***’ ). “Key code
if lv_person->get_parent( ) is not initial.
lv_bo ?= lv_person->get_parent( ).
clear lv_***.
lv_*** = lv_bo->get_property( ‘***’ ). “Key code
ENDWHILE.

 

In my case , I was trying to create multiple DOCFLOW entities but I not have cleared the variable through which I was passing the data to docflow and I was passing the same docflow instance every time hence the data was getting overwritten in same entity. 

 

CATCH

 

Another of my personal favorite. Exceptions are pretty common while we execute a scenario in WebUI. But they are handled in the framework and hence even though exceptions are raised , gracefully WebUI screen opens. If I call a method and it runs into an exception , then it could result in a dump or exception. Both of them are not appreciated by the users hence it makes a lot of sense to fail gracefully and provide exception handling. As a general rule if you are coding in WebUI , don't raise exceptions and don't fail to catch exceptions. Both of them are absolutely necessary.

 

while lr_current_entity is not initial and lr_entity is not initial .
 *  Add the entitiy
 try.                Clear lr_entity.
CHECK lr_ent is bound.
 lr_ent ?= lr_entity.
 if lr_ent->alive( ) = abap_true.
 Clear lr_adminh , clear lv_object_type.
 lr_adminh ?= lr_ent->get_related_entity( 'BTOrderHeader' ).
 if  (Your_Condition).
 else.  lr_current_entity ?= lr_iterator->get_next( ).
 lr_entity ?= ir_valuehelp_collection->get_next( ).
 endif.
 endif.
 catch cx_root.
 lr_current_entity ?= lr_iterator->get_next( ).
 lr_entity ?= ir_valuehelp_collection->get_next( ).
 endtry.
 endwhile.

Now this code serves two purposes

a) It handles the exception hence there would not be any disruption in WebUI.

b) In case of exception occurs , there is a handling in that case.

 

Some of these guidelines may appear didactic but in reality they help in avoiding embarrassing code mishaps.

A real case: how to open external url ( ABAP webdynpro ) via pop up window in CRM

$
0
0
Hi Friends,
in my recent customer project customer complains to me that they can not navigate from the workflow search result to the target UI.
The workflow work item is a document generated via SAP document builder. They would likt to navigate to the document detail page and then decide whether to approve the document or not. With SAP standard solution so far in CRM7.0 EHP3, this is not possible since this work item does not have any BOR type but instead modelled via ABAP class /IPRO/CL_WFL_DOCUMNT. What's more, the target document page is built via ABAP webdynpro which has not respective concept like UI object type, Genil component or BOL object name.
clipboard1.png
So in order to enable customer to see the document page, I just enhance the standard table to add a new column named "Document URL". By clicking the hyperlink of that column, it will create a new popup window to display the document page.
clipboard2.png
The solution could be concluded as below steps:
step1.
enhance a new attribute DOC_URL in standard context node SEARCHRESULTNODE, implement Getter and P getter.
clipboard3.png
in getter, just get the target document page url via utility class:
  method GET_DOC_URL.    value = zcl_document_utility=>get_display_value( iterator ).  endmethod.
in p getter, set the event handler for hyperlink click event:
METHOD GET_P_DOC_URL.
 CASE iv_property.    WHEN if_bsp_wd_model_setter_getter=>fp_fieldtype.        rv_value = cl_bsp_dlc_view_descriptor=>field_type_event_link.    WHEN if_bsp_wd_model_setter_getter=>fp_onclick.        rv_value = 'DISPLAY_DOC'.  ENDCASE.
ENDMETHOD.
step2.
define component usage GS_CMSRCH:
clipboard4.png
and in event handler for DISPLAY_DOC:
 method OPEN_URL_INTERNAL.    DATA(lv_title) = cl_wd_utilities=>get_otr_text_by_alias( 'Document(to be approved) Preview' ). "#EC NOTEXT    DATA(lr_popup) = io_wd_manager->create_popup(  iv_interface_view_name = 'GS_CMSRCH/CMDisplayContentWindow'                                                   iv_usage_name          = 'GS_CMSRCH'                                                   iv_title               = lv_title ).    DATA(lr_cn)  = lr_popup->get_context_node( 'PARAMS' ).    DATA(lr_obj) = lr_cn->collection_wrapper->get_current( ).    READ TABLE st_url_buffer ASSIGNING FIELD-SYMBOL(<buffer>) WITH KEY uuid = iv_guid.    CHECK sy-subrc = 0.    DATA(ls_params) = VALUE crmt_gsurlpopup_params( url = <buffer>-url height = '1000' ).    lr_obj->set_properties( ls_params ).    lr_popup->set_display_mode( if_bsp_wd_popup=>C_DISPLAY_MODE_PLAIN ).    lr_popup->set_window_width( 1000 ).    lr_popup->set_window_height( 10000 ).    lr_popup->open( ).  endmethod.
And that's all.
Interface window CMDisplayContentWindow is a new window created by SAP in EHP3 SP3. There is no magic inside it: just open the target window via javascript window.open.
clipboard5.png
you can also use the reuse component GSURLPOPUP, which can achieve the same result but with different html markup.
clipboard6.png
After I finish it, I soon realized that in this dedicated case, it is possible to "navigate" to the target ABAP webdynpro page via CRM navigation framework itself. I will explain how to do it in my next blog.

how to navigate to ABAP webdynpro page via CRM navigation framework

$
0
0
Hi Friends,
in one of my post, I talked about how to open ABAP webdynpro page via pop up window to resolve a real customer issue.
Now I have another solution to navigate to ABAP webdynpro page via CRM navigation framework.
The CRM navigation framework is using a so-called navigation object which can tell framwork what the target UI view and the respective operation ( Display, Edit, Search etc ). The navigation object relies heavily on CRM ui concept like UI object type, BOR and BOL type which do not make sense for a ABAP Webdynpro page.
So the main idea of this solution is, to use a wrapper CRM ui component which encapsulate ABAP webdynpro page and make configurations to achieve the navigation from source UI component to the wrapper component. After the wrapper component is opened, it will then open ABAP webdynpro page itself.
clipboard1.png
The finaly result would look like below:
clipboard2.png
compared with the solution in my previous blog, this solution does not need any enhancement on standard UI component, instead only several customizing work
( with corresponding mapper class development )and the development of a new wrapper UI component with tiny effort. The main steps are below:
step1:
create a simple wrapper UI component. ( Please ignore view main, which is for another blog )
clipboard3.png
create an inbound plug for window MainWindow and implement as below.
  method IP_FROM_WF.
    data(lo_entity) = iv_collection->get_first( ).
    CHECK lo_entity IS NOT INITIAL.
    data(lv_guid) = lo_entity->get_property_as_string( 'DOCMNT' ).
    cl_zmeta_mainwindow_impl=>gv_url = zcl_url_utility=>get_url( CONV #( lv_guid ) ).
  endmethod.
The idea is to get the target ABAP webdynpro page url from iv_collection which is passed by CRM navigation framwork and set it to a static attribute of window controller class. That gv_url is just used in view windowOpen.htm. So it means when this wrapper UI component view is about to be rendered, it will open the target ABAP webdynpro page via javascript window.open.
clipboard4.png
step2:
SM30, view name BSPDLCV_OBJ_TYPE create a new UI object type. please AlWAYS double check in the system whether there are already existing UI object type in the system!
Here I create a new UI object type CRM_WF. You can input any name in BOL Object name. There is no validation check, just input a meaningful name.
The Genil component specified here will be loaded automatically by framework in the runtime. In most time your ABAP webdynpro page does not have respective Genil component in CRM, you just can maintain a dummy one.
clipboard5.png
step3:
create a new entry in table CRMS_UI_OBJ_MAP. The object name comes from step2.
clipboard6.png
the mapping class must implement interface IF_CRM_UI_OBJ_MAPPER. If your scenario is just navigation from CRM ui to ABAP webdynpro,
you only need to implement GET_ENTITY_FROM_UI_OBJECT.
Here I just retrieve respective document data from database with document key passed in from framework ( iv_entty_key_value )
clipboard7.png
step4:
in SPRO-Customer Relationship Management-UI Framework-Technical Role Definition-Define Work Area Component Repository:
create a new entry for your wrapper UI component:
clipboard8.png
in inbound Plug Definition, create one entry:
clipboard9.png
in SPRO-Customer Relationship Management-UI Framework-Technical Role Definition-Define Navigation Bar Profile:
create an entry in "Define Generic Outbound Plug mapping" for your profile.
clipboard10.png
And that's all.

twitter(also facebook) is official integrated into CRM 7.0 EHP3

$
0
0

Hi friends,

 

I am CRM developer in SAP and our team has already integrated twitter ( also facebook ) into interaction center in CRM7.0 EHP3.

 

You can see an example below:

 

Create a tweet in twitter website:

 

 

clipboard1.png

 

Now I can see the tweet created just now in the web site.

 

clipboard2.png

 

And I can also see it in CRM IC agent inbox:

 

clipboard3.png

 

select the first row and I can directly reply it in CRM by clicking reply button:

 

clipboard4.png

 

just click the send button:

 

clipboard5.png

 

after click send button, I can see the reply also in twitter website:

 

clipboard6.png

 

if you would like to integrate other social channel than twitter and facebook, you just have to implement your own client class for the new social channel, and all left tasks are centrally done by us from framework side.

 

clipboard7.png

 

you can get more detail information from consulting note 1832462 about social integration achitecture. Implementation detail for facebook client class: note 1832481. And note 1832480 for twitter.

Welcome to post any questions you have here if you are interested with this topic.

Default text in notes

$
0
0

Hi All,

 

This one takes us back to the basics of CRM, power of text determination procedure. We have been using notes in CRM transactions very
frequently to capture various texts entered by different Users during the process. Through this blog I am trying to showcase one of the old features
available in text determination procedures i.e. access sequence to source the content in the note type.

 

 

In my recent project, I got a requirement from customer where they wanted a default text to appear in the notes. As this note type was to be
used for printing CRM generated letter, it was acting as template for the letter and also it was supposed to be changeable by user during the process and
the changed version of the template should go for printing. As this criteria came into picture, we couldn’t just create a smartform with hardcoded text
instead found the standard way to find solution.

 

Access sequence in text determination procedure can be used in this case.

 

 

Following are the steps to make this work:

 

 

  1. Create the text template using SAPscript Tcode SO10

 

1.png

  Maintain the default text here:

 

1.png

 

2.  Create you text determination procedure and create access sequence for the text type for which the default text has to be sourced:

 

1.png



3. The function has to be created by technical team which has a small code for calling the SAPscript created in step 1:

 

 

Function ZFM_UPLOAD_TEXT

 

 

CLEAR: es_reference.

  es_reference-tdobject = 'TEXT'.

  es_reference-tdid = 'ST'.

  es_reference-tdname = 'ZTEMPLATE'.

  es_reference-tdspras = 'E'.

 

 

Endfunction.

 

 

4. Once the access sequence is created, assign this access sequence to the text type:

 

 

1.png

 

Leave the changes column as empty as you want it to be editable and select value “A” in transfer column for this text type.

 

 

That’s it. When this procedure is assigned to a transaction and when user creates the transaction, the default text appears in the
respective note type (text type) which can be changed by user.

 

1.png

 

A small configuration changed helped us in meeting the requirement. Thought of sharing this as a lot of us sometimes miss these
excellent features available as part of std CRM.

 

Hope you enjoyed revisiting the basics. sometimes it really helps.

Three ways to control the visibility of an assignment block in overview page in web client UI

$
0
0
Hi Friends,
It is a common requirement that a certain assignment block in overview page should only be displayed under some condition, for example under control of a business switch.
clipboard1.png
Here are three ways which could control the visibility of one assignment block via code.
Approach1: dynamical view detachment
refine method DETACH_STATIC_OVW_VIEWS of the controller class of your overview page. All views put into rt_viewid will be hidden by UI framework in the runtime.The code below means if a switch is turned on, the assignment block SearchResult will be hidden.
clipboard2.png
Approach2: dynamic UI configuration load
Create a new configuration within which only Search view is displayed. The configuration is bound to a certain UI object type.
clipboard3.png
Now we have two UI configurations. The standard configuration will display two assignment blocks by default, the configuration with UI object type = CRM_SMC will only display Search view.
clipboard4.png
Implement DO_CONFIG_DETERMINATION:
clipboard5.png
  method DO_CONFIG_DETERMINATION.  " IF search result view should be hidden     me->set_config_keys( iv_object_type          = 'CRM_SMC'                          iv_propagate_2_children = abap_false ).  " ENDIF  endmethod.
Approach3:
This is a most elegant way, however it could only be used if you would like to control the visibility of your view via a business switch.
When you are assigning your assignment view to the view area of Overview page in runtime repository, a business switch can be assigned.
If the switch is turned off, the related view will be automatically hidden by UI Framework.
clipboard6.png

A collection of technical details about Content Management

$
0
0

Hi Friends,

 

recently I am working on one project which will make use of CRM content management. We will extract data from external web site and store them into CRM  system.

I will put something which I think is worth sharing to a series of blogs here. I hope they can help.

 

1. how to put the attachment view in a pop up window

2. how to dynamically control the enablement of attachment maintenance

3. how to create attachment for your business object via code

4. how to delete attachment belonging to a business object via code

5. how is attachment physically stored in database table

6. how to make your custom developed business object type also support content management - see note 1710485.

7. technical detail for attachment attributes

8. display thumbnail in CRM UI with the help of attachment

9. internal mode for Attachment creation

10. change CREATED_BY property by code

11.  the more to be added soon..

How is attachment physically stored in database table in CM framework

$
0
0

If you want to know more technical detail, please find my blog list here.

 

History:

Content Management(CM) was introduced in basis release 6.10 and implemented in CRM 3.0. Before CM was used, CRM applications was using the Business Document Service (BDS) or Generic Object Services (GOS) to implement Document management requirement. BDS was used in most applications like Business partners, products, product catalogues, solution database, campaigns. GOS was only used in the One Order objects like activities and opportunities. All BDS applications changed to CM in 3.0, the One Order objects only in 3.1.

 

A document in CM consists of several objects. The most important two are so called “logical info object” (LOIO) and a “physical info object” (PHIO). A LOIO as a logical folder or bracket which holds PHIOs together, while a PHIO represents the content of a specific version of a document. Let's use the word edit in the real world as example. If one works on a Word document and changes the document over time, each saved version would be represented by a PHIO. The LOIO would be the word document itself. This is much like if one looks at SE38 in ABAP: The program name is the LOIO and each transported version of the code is the PHIO.

Let's see an example. I have a product ZCM_DEMO with guid 0090FA0D8DC21ED395FD7C687F99BFF7, BOR type = BUS1178.

I create one attachment for it:

clipboard1.png

Go to table SKWG_BREL, input product guid 0090FA0D8DC21ED395FD7C687F99BFF7, and we see two entries belonging to this product. The first entry indicates a folder instance which is actually a logical container to hold all attachments for the given product. 

 

clipboard2.png

The content of column INSTID_B has naming convention as <type: F(older) or L(ogical object)><Logical object type name><guid>.

The guid in the first row can be found in table CRM_FOLDER:

clipboard3.png

The guid 0090FA0D8DC21ED395FD830F8DD9DFFF in the second row can be found in table BDSLOIO22, together with the name of attachment.

 

clipboard4.png

You are asking how I get to know the name of table BDSLOIO22? If one application wants to use CM to store document, it should have its dedicated class for physical object and logical object, or use the default one CRM_L_DOC. The relationship between application and its class is maintained in tcode DMWB:

clipboard5.png

in table BDSPHIO22, by specifying logical information object ID, we can get all phyiscal object lists.

clipboard6.png

once you get phyical object id 0090FA0D8DC21ED395FD830F8DD9FFFF, you can find the respective entries in table BDSCONT22.

clipboard7.png

The real content of attachment is stored in a claster way, so you cannot see its detail in SE16.

clipboard8.png

How do I know the database table name BDSCONT22?

1. Get the storage category BDS_DB22 by physical class name.

clipboard9.png

2. execute function module SCMS_SDOKSTRE_LIST to get all lists of database table. Then you can find the table name BDSCONT22 for your storage category BDS_DB22.

clipboard10.png

How to create attachment for your business object via code

$
0
0
For a complete list of all my blogs regarding content management, please seehere.
I create a utility class with method CREATE_DOC. It has following four input parameters:
iv_data type xstring - the binary data which you would like to store as attachment
iv_bor_type type string - the BOR type of your business object. You can view view business object model in tcode SWO1
iv_guid type raw16 - the guid of your business object instance
iv_file_name type string - the file name which will appear in attachment assignment block.
The source code of method below: ( in fact all attributes for an attachment could be available in the input parameters of this method. For simplicity reason I
just hard code them in the sample code )
DATA:
         ls_bo              TYPE sibflporb,
         ls_prop            TYPE LINE OF sdokproptys,
         lt_prop            TYPE sdokproptys,
         lt_properties_attr TYPE crmt_attr_name_value_t,
         ls_file_info       TYPE sdokfilaci,
         lt_file_info       TYPE sdokfilacis,
         lt_file_content    TYPE sdokcntbins,
         lv_length          TYPE i,
         lv_file_xstring    TYPE xstring,
         ls_loio            TYPE skwf_io,
         ls_phio            TYPE skwf_io,
         ls_error           TYPE skwf_error.
    ls_prop-name = 'DESCRIPTION'.
    ls_prop-value = 'created by Tool'. " replace it with your own description for attachment
    APPEND ls_prop TO lt_prop.
    ls_prop-name = 'KW_RELATIVE_URL'.
    ls_prop-value = iv_file_name. " in the sample code I just reuse file name as relative url
    APPEND ls_prop TO lt_prop.
    ls_prop-name = 'LANGUAGE'.
    ls_prop-value = sy-langu.
    APPEND ls_prop TO lt_prop.
    lv_file_xstring = iv_data.
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer        = lv_file_xstring
      IMPORTING
        output_length = lv_length
      TABLES
        binary_tab    = lt_file_content.
    ls_file_info-binary_flg = 'X'.
    ls_file_info-file_name = iv_file_name.
    ls_file_info-file_size = lv_length.
    ls_file_info-mimetype = 'image/jpeg'. "use the correct mime type for your attachment
    APPEND ls_file_info TO lt_file_info.
    ls_bo-INSTID = iv_guid.
    ls_bo-typeid = iv_bor_type.
    ls_bo-catid = 'BO'.
    CALL METHOD cl_crm_documents=>create_with_table
      EXPORTING
        business_object     = ls_bo
        properties          = lt_prop
        properties_attr     = lt_properties_attr
        file_access_info    = lt_file_info
        file_content_binary = lt_file_content
        raw_mode            = 'X'
      IMPORTING
        loio                = ls_loio
        phio                = ls_phio
        error               = ls_error. " evaluate if there is anything wrong during creation
COMMIT WORK.
I write a piece of code to test it. After report runs I could see the generated attachment.
clipboard1.png
clipboard2.png
You can also test whether the attachment is created successfully in the backend. Test class method get_info in SE24.
Specify importing parameter BUSINESS_OBJECT:
clipboard3.png
clipboard4.png
Execute and you should get result as below: one physical object and one logical object according to how-is-attachment-physically-stored-in-database-table-in-cm-framework.
clipboard5.png
Never forget to call COMMIT WORK in your code, since the persistence of the relationship between attachment and your business object are implemented via generic object service in a update process.You could easily find this via SAT trace on your code
clipboard7.png
or switch on update debugging in your debugger settings.
clipboard8.png

How to delete attachments belonging to a business object via code

$
0
0

For a complete list of my blogs regarding content management in CRM, please seehere.

In the beginning I consider it is very easy to delete attachments via code, just like below.

 

I have defined one method with following two importing parameters:

iv_bor_type type string - the BOR type of your business object

iv_uuid type raw16 - the guid of your business object instance

rv_successful type abap_bool - indicates whether the deletion is successful

 

DATA: ls_bo          TYPE SIBFLPORB,
          lt_loios    TYPE SKWF_IOS,          ls_loios    TYPE SKWF_IO,          ls_error    TYPE SKWF_ERROR,          lt_badios   TYPE SKWF_IOERRS,          lv_del_flag TYPE ABAP_BOOL.    ls_bo-instid = iv_uuid.    ls_bo-typeid = iv_bor_type.    ls_bo-catid  = 'BO'.    rv_successful = abap_false.    CALL METHOD cl_crm_documents=>get_info      EXPORTING        business_object = ls_bo      IMPORTING        loios           = lt_loios.    LOOP AT lt_loios INTO ls_loios.      CALL METHOD cl_crm_documents=>lock        EXPORTING          is_bo    = ls_bo          is_loio  = ls_loios        IMPORTING          es_error = ls_error.      IF ls_error IS NOT INITIAL.         RETURN.      ENDIF.    ENDLOOP.    CALL METHOD cl_crm_documents=>delete      EXPORTING         business_object = ls_bo         ios             = lt_loios      IMPORTING         bad_ios         = lt_badios         error           = ls_error.    IF ls_error IS INITIAL. " deletion failed       rv_successful = abap_true.    ENDIF.

 

Through testing I found it works perfectly well with most of BOR type like product, ibase, BP, and one order.

However, it does not work for my new BOR type CRMSOCPOST in CRM7.0 EHP3 - the relationship between my bo and the attachments is not really deleted until

an explicit call COMMIT WORK is written in the report. However, for any other standard CRM BOR type, the COMMIT WORK is not needed. Why?

 

After some debugging finally I find answer. Have you already observed the comments below? It is written quite clearly: if the business object exists in the database, the COMMIT WORK is not needed.

clipboard1.png

Back to my case, since CRMSOCPOST is a new BOR type created in CRM7.0 EHP3, the function module does not recoginize it, so the code reached line 225, and

then the bo instance is regarded as non-exist in DB. So an explicit COMMIT WORK is always necessary to make deletion work.

clipboard2.png

Technical details for attachment attributes

$
0
0

For a complete list of all my blogs regarding content management, please seehere.

When you create attachments for your business objects via cl_crm_documents=>create_with_table, you have to pass in an internal table for attachment property.
The namelist of attribute name could be got from attachment class via function module SDOK_PHIO_ATTRIBUTES_GET.

For example, attachment class CRM_P_DOC has 36 attributes.

 

http://farm8.staticflickr.com/7344/11187276154_5d528154be_o.png

The attributes could also be viewed in tcode DMWB

http://farm6.staticflickr.com/5547/11187275956_629a1f769f_o.png

The actual attribute value could be got via function module CRM_KW_PROPERTIES_GET:

http://farm8.staticflickr.com/7352/11187275846_c599721d2f_o.png

 

http://farm4.staticflickr.com/3700/11187275766_5006345943_o.png

 

The importing parameters are guid of logical/physical object instances:

http://farm8.staticflickr.com/7421/11187275844_b9e3bede8a_o.png

The attribute values are stored in PROPERTIES internal table.

http://farm4.staticflickr.com/3758/11187385233_729f26c7a5_o.png

 

Do you know why in the backend table, the property is stored in field PROP09?

http://farm4.staticflickr.com/3692/11187385283_ca182ecdc7_o.png

actually it is because I hard code the content of relative url as the attachment file name.

    ls_prop-name = 'KW_RELATIVE_URL'.

    ls_prop-value = iv_file_name.

    APPEND ls_prop TO lt_prop.

http://farm8.staticflickr.com/7325/11187229605_07b858c6df_o.png

 

The index of attribute could also be found in tcode DMWB.

http://farm3.staticflickr.com/2825/11187229635_9e2010bdd2_o.png

Do you believe the malfunctioned RFC destination will cause interaction center UI hang

$
0
0

  Yesterday when I am doing my development under interaction center environment, I suddenly find I cannot log on interaction center UI any more.

  

 

http://farm4.staticflickr.com/3754/11187610996_b557606515_o.png

After I choose my business role ZSOCIAL, the UI hangs there with only information "Starting". Ten minutes later, I got a time out error in UI.

http://farm3.staticflickr.com/2814/11187610926_c7dc301ca6_o.png

I did a quick check on my development objects and I can ensure this is not caused by my new development. I checked SM21 and SM50 and could not find any hints.

So again I have to debug to find the root cause. But where should I set breakpoint? Since in principle the component set for IC CRMIC_DEFAULT will always be loaded when IC UI is launched, it will be a good starting point to set breakpoint in method CL_CRM_BOL_CORE=>load_component_set. Soon I found the application will hang once that method is called, so I know I am not far from the truth.

http://farm3.staticflickr.com/2828/11187719943_5d1e891fbb_o.png

In this customizing you can find all components belonging to component set CRMIC_DEFAULT.

 

http://farm4.staticflickr.com/3799/11187719873_cbbe2ab206_o.png

The timeout must come from loading one of these components.

http://farm3.staticflickr.com/2859/11187719723_48fea9c3e9_o.png

Finally I found the component ERP is the trouble maker. The system hangs after I click F6 in line 101.

http://farm3.staticflickr.com/2854/11187609434_7dee258db1_o.png

 

The root cause is that RFC destination EBJCLNT001 does not work, so RFC call hangs until time out occurred.

http://farm6.staticflickr.com/5521/11187609304_b7eacfe6e7_o.png

 

This RFC destination is maintained in this customizing:

http://farm4.staticflickr.com/3756/11187609144_d9ac8e892d_o.png

http://farm4.staticflickr.com/3772/11187609034_590fe3ffae_o.png

Why clicking on attachment property hyperlink will lock hosting business object

$
0
0

For a complete list of all my blogs regarding content management, please see here.

 

During my recent project I found a strange behavior in attachments.

Click the hyperlink of Properties

http://farm3.staticflickr.com/2810/11200858386_bd70a61722_o.png

 

do not change anything, and click back

http://farm3.staticflickr.com/2843/11200843184_97e631df3c_o.png

After back to product overview page, the overview page looks exactly the same before you click Property hyperlink.

However, if you use SM12 to check in backend, you will find the current product is locked, although it still looks as under display mode in UI!

The same behavior could be observed in one order application. I would assume this as a bug.

 

http://farm3.staticflickr.com/2836/11200858266_ac466767ae_o.png

 

By debugging I found the parent node, Product instance, is explicitly locked by content management class.

http://farm4.staticflickr.com/3827/11200966453_9543f35a92_o.png

 

However, in my application I do not need this behavior - in my application, the whole attachment assignment block has read only mode, so I don't want my BO to be locked after property hyperlink is clicked. The solution is very easy: do not bind my BO node to PARENTNODE of content management UI component GS_CM by commenting out the related code:

http://farm3.staticflickr.com/2876/11200966293_91f42ef6a4_o.png

Display thumbnail in CRM UI with the help of attachment

$
0
0

For a complete list of all my blogs regarding content management, please seehere.

In CRM Product UI, A thumbnail could be displayed in Product header and Thumbmail assignment block.

clipboard1.png


clipboard2.png


The Thumbnail assignment block is not available in SAP predefined UI configuration. Customer needs to manually make it visible via UI configuration change:


clipboard3.png


The thumbnail view implementation itself is very simple, just use the lib thtmlb image tag.


clipboard4.png

The thumbnail view implementation itself is very simple, just use the lib thtmlb image tag.

clipboard5.png

If a product has multiple attachments, how does the UI know which one should be displayed as thumbnail? The default attachment with type BDS_IMAGE will be choosen.

clipboard6.png

However, these two fields are not available in default standard configuration of GS_CM/EditProp

clipboard7.png

if your application need also allow users to specify these two fields, you can create a new configuration by copying from the one with object type = PRD_MATSRV.

clipboard8.png

Viewing all 228 articles
Browse latest View live


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