This Weblog is the part II is an "HowTo Guide" to implement the Service Desk Scenario of Solution Manager.
In this Part II, I will show you how to
I need to add new data in the service transaction. Those data are :
Those two new fields should be store in the header of the transaction and should appear in the transaction as List boxes.
We will use the standard tool to implement this extension: The Easy Enhancement Workbench (EEWB).
To use this tool, we have first to check its configuration. To configure it, go to transaction EEWC. Be sure that the system is well configured. Verify that you have:
Then launch the EEWB, by executing the transaction EEWB.
Create a new Project, give it a name, for example: BLOG_PARTII_PROJECT and a description: Project for adding custom data. Give it a package (a dev class) and save it.
Into that project we will add one extension. So create an extension:
Select the project (Double click on it) and create an extension by using the F6 key, the "create extension" button, the menu Project->Create Extension or the context menu.
Fill the following dialog box with:
Then the wizard starts:
After the system ends the processing of the extension you should have something like that:
in the left part of the screen :
in the right part of the screen :
For the next step look at the first line (in the right part): you should have something like:
ShortDescrip. | Prog. ID | Obj. Type | Object Name |
Screen | LIMU | DYNP | SAPLZCRM_BTX_EEW_UI0X 0100 |
For me, the Object Name is SAPLZCRM_BTX_EEW_UI02 0100; It depends on the number of extensions already created in the system.
We have to modify the generated Screen to meet our requirements.
Go to the Screen Painter (SE51), enter the program name (SAPLZCRM_BTX_EEW_UI02) and the screen number (0100). Select Element List and go on Change.
Go to the Tab: "Text/ I/O Templates", change the "Dropdown" to List Box for the two Fields.
A strange error should occur when activating the screen. to quickly correct it put a value (label of the field in the screen) in the Text or I/O field for the lines *ZCRMT_0100_BTX_UI01-ZZCUSTOME.
In se11, create a new Domain ZEEW_DATAELEMENT0201 with:
Value range :
Fix. Val. | Short Description |
1 | Low |
2 | Medium |
3 | High |
Activate it. Then go back to eewb and select the two Data element ZEEW_DATAELEMENT0201 and ZEEW_DATAELEMENT0202. Assign them the newly created Domain, save and activate them.
The customer fields are not displayed in the standard service transaction. To be able to display it, we must change the customizing of the screen sequence. Go to transaction CRMV_SSC, screen profile type: SRVO, Complete Tabstrip Assignment of panel.
Create an entry with:
Save it. That's done.
There is a bug (missing customizing) that leads to a duplicate tab name in the transaction data. In fact, the tab that holds the context data get the name of the preceding tab. For my system, this is the "documents" tab, so I have two tabs named "documents" instead of having one named "documents" and the other named "context".
To solve that go to transaction CRMV_SSC, Select "Def of Customer-Specific Program".
Select the function "Edit", and insert the following new entry:
Scrprftyp | Programmname |
SRVO | SAPLCONTEXT_MAINTAIN_GUI_SOCM |
Save the entry, that's all. Check the result.
Implement the interface with an external service Desk System.
For this blogs I will give you some tips to build a fake external system.
First you need some tools:
I will use the J2EE server to implement some dummy functions described in the Web Service API, that give the good response. The SoapUI will be used to call the Solution Manager Web Service.
The standard interface for the scenario service desk is based on a Web Service. SAP provide a documentation for the API of the Web Service.
This API should be implemented in the external service Desk, as Solution Manager use this API to call the external service Desk.
The documentation could be found in the SAP Marketplace the document name is:
In this example I will use only three functions to exchange a message:
I will give you some more details on actions & conditions later in this blogs.
I will not describe the overall interface API, but we must take care of some little things. First let me summarize how the interface works:
First thing to know is the role of each system in a message exchange. A system could have two roles: Requester or Provider for a message exchange. Example: When Solution Manager sends the first time a message to an external system, solution manager has the role Requester (R) and the external system the role Provider (P). For an individual message exchange, the system stores the role of solution manger internally.
Secondly, the system also stores information concerning the status of the message in the interface context. Those interface status are (Beware those status are NOT the User status neither the system status of the message).
For an individual message exchange, solution manager sets the status to the correct value depending on the preceding interface status, the solution manager role and the function used.
For the outbound process (out of solution manager) the Solution Manager system manages the function to be used in the API depending on the function used and context already existing in the interface.
This could be summarized as this:
Outbound (from the solution Manager point of view)
Function used | Solution Manager Role | Actual stored interface status for the message | Operation executed | New interface Status |
SEND | R | BS | PROCESS | PP |
| R | PP |
|
|
| R | RP | ACCEPT | PP |
| R | SP | REJECT | PP |
| R | CL |
|
|
Function used | Solution Manager Role | Actual stored interface status for the message | Operation executed | New interface Status |
UPDATE | R | BS | REPLICATE | RP |
| R | PP | ADDINFO | PP |
| R | RP | ADDINFO | RP |
| R | SP | ADDINFO | SP |
| R | CL |
|
|
Function used | Solution Manager Role | Actual stored interface status for the message | Operation executed | New interface Status |
CLOSE | R | BS |
|
|
| R | PP | CLOSE | CL |
| R | RP | CLOSE | CL |
| R | SP | CLOSE | CL |
| R | CL |
|
|
NOTE : PROCESS = ProcessIncident, REPLICATE = ReplicateIncident, ACCEPT = AcceptIncidentProcessing, ADDINFO = AddInfo, VERIFY = VerifyIncidentSolution, REJECT = RejectIncidentSolution, CLOSE = CloseIncident.
You should read this array like that:
When I send a message through the action SLFN0001_ADVANCED_SEND_THIRD, solution manager sets its role to Requester (R). Then it reads the actual interface status for this message. And it deals with it: if the actual status is BS then Solman will use the API function PROCESS and will set the new status for this message to ProviderProcessing (PP). Red cells mean ERROR.
For the inbound process, solution manager verify the API function allowed depending on the existing interface context (actual interface status and its role).
Inbound (from the solution Manager point of view) when solman has the Requester Role:
API Function | Actual Interface Status | |||
BS | PP | RP | SP | |
PROCESS | RP |
|
|
|
REPLICATE | PP |
|
|
|
ADDINFO |
| PP | RP | SP |
VERIFY |
| SP |
|
|
ACCEPT |
| RP |
|
|
This should be read like that:
When Solman receive a request, it gets its role, the message id and the Api function called from the request content. If its role is requester, it reads the interface context of the message, and test: if the actual interface status is BS then the allowed functions are PROCESS or REPLICATE, if the actual interface status is PP then the allowed functions are ADDINFO, VERIFY and ACCEPT, etc... Then solman checks that the Api function called is permitted. If ok it executes the function and sets the new interface status with the corresponding value.
This is quite complicated, but this should be understood to well manage the interface.
The last API function I will talk about is the REQUEST (= RequestSystemGuid). This API function should be implemented in the external system as it is called by solution in the configuration process. This function should give a unique GUID identifying the external system.
After understanding that, we could stands the global exchange process of message like that :
|
| SOLUTION MANAGER |
| EXTERNAL SYSTEM | ||||
Step # | Dir. | SOLMAN internal process | SOLMAN Function Used | SOLMAN User Status | Interface Function really called |
| External function called | External system subprocess |
1 | Handling of the message | Set status to "In process" | "In processing" |
|
|
|
| |
2 | -> | Sending the message to external system | Action "Send to external system" | "Sent to external system" |
|
| PROCESS | Handling of the message |
3 | <- |
|
| "User Action" | ACCEPT |
|
| Question or information to be sent back. |
4 | -> | Answer the question | Action "Send to external system" | "User Action" |
|
| ACCEPT | Handling of the answer |
5 | <- | Handling of the proposed solution |
| "Solution proposed" | VERIFY |
|
| Propose solution |
6-1 | -> | Reject Solution | Action "Send to external system" |
|
|
| REJECT | Handling of the rejection and loop to step 3. |
6-2 | -> | Accept Solution | Set user status to "Confirmed" |
|
|
| CLOSE |
|
To achieve that we must implement the PROCESS, ACCEPT, REJECT, CLOSE and REQUEST API functions in a web service at the external system, and we have to deals with requests that call Solution Manager with the ACCEPT and VERIFY API functions.
We also have to:
We have to release the internal Solution Manager Web Service to permit Solution manager to be called.
Transaction: WS_CONFIG.
We have to create a user which will be used by the external system to connect to Solution Manager. So create a user with the type "Communication", and assign it the following standard roles :
Transaction: LPCONFIG.
Transaction: ICTCONF.
Click on "Add" button.
Enter an External System Name
Choose the logical Port created in the preceding Step.
And Confirm.
The pre-requisite to achieve this step is to have the RequestSystemGuid API function implemented in the external system. Solution manager call this API function to get the unique GUID of the external system. If no answer is received then an error occurred. If everything goes well, then you should see the new entry on the screen.
Now you must:
Transaction: CRMBS02.
Copy the SLFN0001 status profile to ZSLFN001 with all the dependencies.
Then modify the label accordingly:
Save it and assign the new profile to the transaction type SLFN.
Now I will discuss a little about Actions and conditions.
In the standard the system used the action profile: SLFN0001_ADVANCED. In this chapter we are going to copy this standard profile and adapt it to our needs.
First go to transaction CRMC_ACTION_DEF. Copy the profile SLFN0001_ADVANCED to Z_SLFN0001_ADVANCED with all the dependencies. Rename all actions with a leading Z. You should have 38 copied entries.
First we are going to deactivate unnecessary actions. Deactivate the following actions by flagging the Inactive Box:
Assign the new created profile to the transaction type SLFN.
Now we are going to create a new action:
ZSLFN0001_ADVANCED_SEND_EXT : this action will send the message to the external system and set the user status to "Sent to External System".
Copy the "ZSLFN0001_ADVANCED_SEND_THIRD" action a new action named ZSLFN0001_ADVANCED_SEND_EXT with all the dependencies.
Select the newly created action and go to "Processing Types".
Delete the method call, and recreate a new one with a new implementation called for example ZTEST. Enter a new filter value: ZPASS_ICT_SEND_TO_THIRD.
Name the implementation class: ZCL_IM_TEST, and go to the code. Enter the following code:
method IF_EX_EXEC_METHODCALL_PPF~EXECUTE.
INCLUDE crm_object_names_con.
INCLUDE crm_object_kinds_con.
DATA lc_action_execute TYPE REF TO cl_action_execute.
DATA lv_header_guid TYPE crmt_object_guid.
DATA lv_third_party_guid TYPE ict_system_guid.
DATA lt_third_party_guid TYPE ict_system_guids.
DATA lv_order_guid TYPE ict_order_guid.
DATA lv_error_occured TYPE ict_boolean.
DATA lr_outbound_processor TYPE REF TO cl_sol_ict_outbound_processor.
DATA lr_ict_error TYPE REF TO cx_sol_ict_error.
DATA l_msg TYPE hier_mess.
DATA lv_application_log TYPE balloghndl.
DATA lv_action TYPE crmt_action.
DATA lv_object_type_ref TYPE swo_objtyp.
DATA lt_header_guid TYPE crmt_object_guid_tab.
DATA lt_status TYPE crmt_status_wrkt.
DATA lv_status TYPE crm_j_status.
DATA lv_user_stat_proc TYPE crm_j_stsma.
DATA: ls_container TYPE swcont.
DATA: lt_container TYPE swconttab.
DATA: lv_user_status TYPE crm_j_status.
DATA ls_log TYPE balloghndl.
DATA: lv_return TYPE j_vorgang.
rp_status = 1.
lv_application_log = ip_application_log.
CALL METHOD cl_sol_ict_tools=>get_third_party_guid
IMPORTING
e_third_party_guid = lv_third_party_guid
e_third_party_guids = lt_third_party_guid.
IF lv_third_party_guid IS INITIAL.
lv_error_occured = 'X'.
ELSE.
CREATE OBJECT lc_action_execute.
CALL METHOD lc_action_execute->get_ref_object
EXPORTING
io_appl_object = io_appl_object
IMPORTING
ev_guid_ref = lv_header_guid
ev_object_type_ref = lv_object_type_ref.
IF ii_container IS BOUND.
lt_container = ii_container->get_values( ).
READ TABLE lt_container INTO ls_container
WITH KEY element = 'USER_STATUS'.
IF sy-subrc IS INITIAL.
lv_user_status = ls_container-value.
ENDIF.
ENDIF.
lv_order_guid = lv_header_guid.
TRY.
CREATE OBJECT lr_outbound_processor
EXPORTING
i_crm_guid = lv_order_guid.
CATCH cx_sol_ict_error INTO lr_ict_error.
lv_error_occured = 'X'.
ENDTRY.
IF lv_error_occured IS INITIAL.
CASE flt_val.
WHEN 'ZPASS_ICT_SEND_TO_THIRD'.
TRY.
CALL METHOD lr_outbound_processor->forward_incident
EXPORTING
i_third_party_guid = lv_third_party_guid.
CALL METHOD cl_hf_helper=>set_status_acc_container
EXPORTING
im_guid = lv_header_guid
im_application_log = ls_log
im_container = lt_container
RECEIVING
status = lv_user_status.
CALL METHOD cl_hf_helper=>execute_activity_acc_status
EXPORTING
im_guid = lv_header_guid
RECEIVING
return = lv_return.
CATCH cx_sol_ict_error INTO lr_ict_error.
lv_error_occured = 'X'.
ENDTRY.
WHEN 'ZPASS_ICT_PROPOSE_SOL_TO_THIRD'.
TRY.
CALL METHOD lr_outbound_processor->propose_solution
EXPORTING
i_third_party_guid = lv_third_party_guid.
CATCH cx_sol_ict_error INTO lr_ict_error.
lv_error_occured = 'X'.
ENDTRY.
WHEN 'ZPASS_ICT_SYNC_TO_THIRD'.
TRY.
CALL METHOD lr_outbound_processor->replicate_incident
EXPORTING
i_third_party_guid = lv_third_party_guid.
CATCH cx_sol_ict_error INTO lr_ict_error.
lv_error_occured = 'X'.
ENDTRY.
WHEN OTHERS.
ENDCASE.
ENDIF.
ENDIF.
IF lv_error_occured IS NOT INITIAL.
CALL FUNCTION 'CRM_MESSAGES_REGISTER'
EXPORTING
iv_log_class = 'GUEST'
CHANGING
cv_log_handle = lv_application_log.
IF lr_ict_error IS NOT INITIAL AND lr_ict_error->message IS NOT INITIAL.
l_msg = lr_ict_error->message.
ELSE.
l_msg-msgno = sy-msgno.
l_msg-msgid = sy-msgid.
l_msg-msgty = sy-msgty.
l_msg-msgv1 = sy-msgv1.
l_msg-msgv2 = sy-msgv2.
l_msg-msgv3 = sy-msgv3.
l_msg-msgv4 = sy-msgv4.
ENDIF.
lv_action = ip_action.
CALL FUNCTION 'CRM_MESSAGE_COLLECT'
EXPORTING
iv_caller_name = gc_object_name-orderadm_h
iv_ref_object = lv_header_guid
iv_ref_kind = gc_object_kind-orderadm_h
* IV_LOGICAL_KEY =
iv_msgno = l_msg-msgno
iv_msgid = l_msg-msgid
iv_msgty = l_msg-msgty
iv_msgv1 = l_msg-msgv1
iv_msgv2 = l_msg-msgv2
iv_msgv3 = l_msg-msgv3
iv_msgv4 = l_msg-msgv4
* IV_MSGLEVEL =
* IV_FIELDNAME =
* iv_action = lv_action
* iv_log_handle =
* IV_DOCNUMBER =
* iv_cumulate = FALSE
* IV_USE_FRAME_LOG = FALSE
* IT_LONGTEXT =
* IV_EXTERNAL_CALL = FALSE
* IMPORTING
* EV_MSGTY =
* ES_MSG_HANDLE =
EXCEPTIONS
not_found = 1
appl_log_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.
ENDIF.
rp_status = 0.
ELSE.
DATA l_t_msg TYPE bal_r_msid.
DATA l_s_msg TYPE bal_s_msid.
l_s_msg-sign = 'I'.
l_s_msg-option = 'EQ'.
l_s_msg-low = aiict_c_msg_class.
l_s_msg-high = aiict_c_msg_class.
APPEND l_s_msg TO l_t_msg.
CALL FUNCTION 'CRM_MESSAGES_DELETE'
EXPORTING
it_r_msgid = l_t_msg
* IT_R_MSGIDNO =
iv_ref_object = lv_header_guid
iv_ref_kind = gc_object_kind-orderadm_h
iv_caller_name = gc_object_name-orderadm_h
* IT_LOGICAL_KEYS =
* IV_LOG_HANDLE =
* IV_DOCNUMBER =
* IV_ONLY_ERRORS_ON_OBJECT = FALSE
* IV_FRAME_LOG = FALSE
* IT_MSGH =
* IV_DELETE_LOG = FALSE
* IV_EXTERNAL_CALL = FALSE
EXCEPTIONS
appl_log_error = 1
OTHERS = 2.
IF sy-subrc <> 0.
ENDIF.
ENDIF.
Endmethod.
This code is a part of the standard with some adding for changing the status. Save and activate everything.
Create a new container element with the following values:
This container will be used in the code to set the new user status to E0004 which is the "Sent to External System" status.
NOTE: To see the internal code of a status go to table TJ30.
Now we have created a new action that will send the message to the external system and set the status to "Sent to external system".
Now it's time to take a look at conditions.
Go to transaction: CRMC_ACTION_CONF.
Select the Action profile Z_SLFN0001_ADVANCED.
Create the standard conditions for the following actions:
For the Action Z_SLFN0001_ADVANCED_CLOSE_THIRD create a Schedule condition:
&CRM Service Process.User Status& = E0008ZSLFN001
Now this action will be executed when the status is set to "Confirmed" when saving the message.
To be able to exchange the new data (Impact and severity) into the interface, we are going to use a trick. We are going to exchange those new data into the IctIncidentAdditionnalInfo structure. (see the API documentation).
To do so, we are going to use the Enhancement Spot BADI_SOL_ICT_MAPPING of the package AI_SOLAR_INTERFACE_SD (Service desk Interface).
For the inbound process, create a new Badi implementation with the following code :
METHOD if_sol_ict_mapping_inbound~after_mapping.
DATA l_sap_ict_id TYPE ict_incident_guid.
DATA l_impact TYPE zeew_dataelement0201.
DATA ls_impact TYPE crmd_customer_h.
DATA lt_add_infos TYPE ict_incident_additional_infos.
DATA ls_add_info TYPE ict_incident_additional_info.
l_sap_ict_id = i_incoming_data->a_ict_head-incident_guid.
lt_add_infos = i_incoming_data->a_ict_additional_infos.
SELECT SINGLE * INTO ls_impact
FROM crmd_customer_h
WHERE guid = l_sap_ict_id.
IF sy-subrc = 0.
LOOP AT lt_add_infos INTO ls_add_info.
CASE ls_add_info-add_info_attribute.
WHEN 'Z_IMPACT'.
IF ls_add_info-add_info_value NE ls_impact-zzcustomer_h0201.
TRY.
UPDATE crmd_customer_h
SET zzcustomer_h0201 = ls_add_info-add_info_value
WHERE guid = l_sap_ict_id.
CATCH cx_sy_dynamic_osql_error.
MESSAGE `Error in update!` TYPE 'I'.
ENDTRY.
ENDIF.
WHEN 'Z_SEVERITE'.
IF ls_add_info-add_info_value NE ls_impact-zzcustomer_h0202.
TRY.
UPDATE crmd_customer_h
SET zzcustomer_h0202 = ls_add_info-add_info_value
WHERE guid = l_sap_ict_id.
CATCH cx_sy_dynamic_osql_error.
MESSAGE `Error in update!` TYPE 'I'.
ENDTRY.
ENDIF.
WHEN OTHERS.
ENDCASE.
ENDLOOP.
ENDIF.
ENDMETHOD.
This code is quite brutal, but it works. It just update the value of the incoming Impact and severity directly in the DB.
For the outbound process, create a new Badi implementation with the following code:
METHOD if_sol_ict_mapping_outbound~after_mapping.
DATA l_sap_ict_id TYPE ict_incident_guid.
DATA l_impact TYPE zeew_dataelement0201.
DATA ls_impact TYPE crmd_customer_h.
DATA lt_add_infos TYPE ict_incident_additional_infos.
DATA ls_add_info TYPE ict_incident_additional_info.
l_sap_ict_id = i_outgoing_data->a_ict_guid.
SELECT SINGLE * INTO ls_impact
FROM crmd_customer_h
WHERE guid = l_sap_ict_id.
ls_add_info-add_info_attribute = 'Z_IMPACT'.
ls_add_info-add_info_value = ls_impact-zzcustomer_h0201.
APPEND ls_add_info TO lt_add_infos.
ls_add_info-add_info_attribute = 'Z_SEVERITE'.
ls_add_info-add_info_value = ls_impact-zzcustomer_h0202.
APPEND ls_add_info TO lt_add_infos.
CALL METHOD i_mapped_data->change_additional_infos
EXPORTING
i_add_infos_to_be_added = lt_add_infos.
ENDMETHOD.
This code just integrate the value of the Impact and severity fields into the additionalinfos structure.
If you are running an SP12 Solution manager System, you have to implement the following SAP Notes:
This corrected in SP15.
Last Word if you want to check all the XML data exchange in the interface, you have to enter the following data into the ICT_CUSTOM table:
TRACE_IN X
TRACE_OUT X
Then you'll have all the snapshots in the ICT_SNAPSHOT table. Those snapshots could be downloaded to your client via the program ICT_DOWNLOAD_SNAPSHOT.
If you want some more details on the WebService development or on other topic please feel free to send me questions, I will try to answer them...
That's it for the part II. I know I was a little long to write this part II...but here it is...
I would like to thank two of my colleagues Dan and Guillaume.