SAP Fiori and SAP ESS/MSS Leave Request Approval – A Bad Bad Romance – Part 3
Welcome back to this bad romance series of Approve Leave Request from ESS/MSS and Fiori UX. In Part 1 and Part 2. We have established a case and a workflow to run synchronously from both sides.
In this part I will explain you the technical part of the workflow we have just discussed. So get set go!
Technical Part of Leave Request Multi Level Workflow
To cater the customized changes in the workflow, I have created a customized class with few methods to get my logic working.
This is my customized class with a few methods, lets go hold of these quickly.
1. Activity: Set initial approver (I have a method for that) Importing: Request ID, Exporting: Agent (initial approver). Same import export binding is done from method to task and from task to Workflow. Code is as below:
METHOD set_initial_approver. DATA: lr_request TYPE REF TO cl_pt_req_request, ls_owner TYPE ptreq_actor_struc_flat, ls_owner_uname TYPE sysid, lv_oid TYPE os_guid, lt_approvers_temp TYPE ptreq_approver_tab, ls_approvers_temp TYPE ptreq_approver_struc, lv_item_list_id TYPE ptreq_header-item_list_id, lv_item_ins TYPE ptreq_items-item_ins, lv_infotype TYPE infty, lv_pernr TYPE pernr_d, lv_awart TYPE pa2001-awart, lv_uname TYPE syst_uname, lv_pernr_nxtappr TYPE objektid, ls_attribs TYPE ptreq_attribs_struc_flat. * Move request to Globally Unique Identifier lv_oid = iv_request_id. * Enqueue the request CALL FUNCTION 'ENQUEUE_EPTREQ' EXPORTING mode_ptreq_header = 'S' mandt = sy-mandt request_id = iv_request_id EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3. * Get the request object instance lr_request ?= ca_pt_req_header=>agent->if_os_ca_persistency~get_persistent_by_oid( lv_oid ). * Get Owner Details ls_owner = lr_request->if_pt_req_request~get_owner( ). lv_pernr = ls_owner-pernr. ls_owner_uname = ls_owner-user. CONDENSE ls_owner_uname NO-GAPS. MOVE ls_owner_uname TO lv_uname. * Get Att./abs. type SELECT SINGLE item_list_id FROM ptreq_header INTO lv_item_list_id WHERE request_id = iv_request_id. IF sy-subrc = 0. SELECT SINGLE item_ins FROM ptreq_items INTO lv_item_ins WHERE item_list_id = lv_item_list_id. IF sy-subrc = 0. SELECT SINGLE infotype subty FROM ptreq_attabsdata INTO (lv_infotype, lv_awart) WHERE item_id = lv_item_ins AND pernr = lv_pernr. ENDIF. ENDIF. * Get all leave request approvers CALL FUNCTION 'ZWF_GET_ALL_APPROVERS_LEAVE' EXPORTING pernr = lv_pernr awart = lv_awart initiator = lv_uname IMPORTING et_approvers = lt_approvers_temp. READ TABLE lt_approvers_temp INTO ls_approvers_temp INDEX 1. IF sy-subrc = 0. lv_pernr_nxtappr = ls_approvers_temp-pernr. CONCATENATE 'US' ls_approvers_temp-SYS_USER INTO ev_nxtappr. CONDENSE ev_nxtappr NO-GAPS. * Set Next Approver CALL METHOD lr_request->if_pt_req_request~set_next_processor EXPORTING im_actor_type = 'P' im_plvar = '01' im_otype = 'P' im_objid = lv_pernr_nxtappr. " PERNR of Next Approver IF sy-subrc = 0. * Provides all the attributes of the request CALL METHOD lr_request->if_pt_req_request~workarea_version->get_all_attribs IMPORTING ex_all_attribs = ls_attribs. * Create Persistent Database Version CALL METHOD lr_request->clone_to_old. * Commit work and wait COMMIT WORK AND WAIT. ENDIF. ENDIF. * Dequeue the request CALL FUNCTION 'DEQUEUE_EPTREQ' EXPORTING mode_ptreq_header = 'S' request_id = iv_request_id. ENDMETHOD.
Code is self-explanatory, ask me in comments if you don’t get anything. I am NOT going to explain FM: ZWF_GET_ALL_APPROVERS_LEAVE because I have my own logic to fetch next approver. You may use your own.
2. Activity: Getting Employee Name is easy. Go, get it from PA0002, pass PERNR.
3. Activity: Approval Process is a standard task TS12300097 SAP use to approve leave request.
4. Condition: Check Approve: Request Status is REJECTED, WITHDRAWN or APPROVED
5. In case of APPROVED: Activity: Get Next Approver (Method: SET_NEXT_APPROVER in my class). Code is well documented, please go through. Import: Request ID, Current Agent) Exporting: Next Approver. Same binding is done from Method to Task and from Task to WF.
METHOD set_next_approver. DATA: lr_request TYPE REF TO cl_pt_req_request, ls_owner TYPE ptreq_actor_struc_flat, ls_owner_uname TYPE sysid, lv_oid TYPE os_guid, lt_approvers_temp TYPE ptreq_approver_tab, ls_approvers_temp TYPE ptreq_approver_struc, lv_item_list_id TYPE ptreq_header-item_list_id, lv_item_ins TYPE ptreq_items-item_ins, lv_infotype TYPE infty, lv_n_agent TYPE swp_agent, lv_index TYPE sy-index, lv_pernr TYPE pernr_d, lv_awart TYPE pa2001-awart, lv_uname TYPE syst_uname, lv_pernr_nxtappr TYPE objektid, lv_req_notice TYPE tim_req_notice, ls_attribs TYPE ptreq_attribs_struc_flat. ** wait for 60 Seconds for completion of all DB commits WAIT UP TO 60 SECONDS. * Move request to Globally Unique Identifier lv_oid = iv_request_id. * Enqueue the request CALL FUNCTION 'ENQUEUE_EPTREQ' EXPORTING mode_ptreq_header = 'S' mandt = sy-mandt request_id = iv_request_id EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3. * Get the request object instance lr_request ?= ca_pt_req_header=>agent->if_os_ca_persistency~get_persistent_by_oid( lv_oid ). * Get Owner Details ls_owner = lr_request->if_pt_req_request~get_owner( ). lv_pernr = ls_owner-pernr. ls_owner_uname = ls_owner-user. MOVE iv_n_agent TO lv_n_agent. REPLACE FIRST OCCURRENCE OF 'US' IN lv_n_agent WITH space. CONDENSE: ls_owner_uname NO-GAPS, lv_n_agent NO-GAPS. MOVE ls_owner_uname TO lv_uname. * Get Att./abs. type SELECT SINGLE item_list_id FROM ptreq_header INTO lv_item_list_id WHERE request_id = iv_request_id. IF sy-subrc = 0. SELECT SINGLE item_ins FROM ptreq_items INTO lv_item_ins WHERE item_list_id = lv_item_list_id. IF sy-subrc = 0. SELECT SINGLE infotype subty FROM ptreq_attabsdata INTO (lv_infotype, lv_awart) WHERE item_id = lv_item_ins AND pernr = lv_pernr. ENDIF. ENDIF. * Get all leave request approvers CALL FUNCTION 'ZWF_GET_ALL_APPROVERS_LEAVE' EXPORTING pernr = lv_pernr awart = lv_awart initiator = lv_uname IMPORTING et_approvers = lt_approvers_temp. READ TABLE lt_approvers_temp TRANSPORTING NO FIELDS WITH KEY sys_user = lv_n_agent. lv_index = sy-tabix. ADD 1 TO lv_index. READ TABLE lt_approvers_temp INTO ls_approvers_temp INDEX lv_index. IF sy-subrc = 0. CONCATENATE 'Request forwarded to' ls_approvers_temp-name INTO lv_req_notice SEPARATED BY space. lv_pernr_nxtappr = ls_approvers_temp-pernr. CONCATENATE 'US' ls_approvers_temp-sys_user INTO ev_nxtappr. CONDENSE ev_nxtappr NO-GAPS. * Set Next Approver CALL METHOD lr_request->if_pt_req_request~set_next_processor EXPORTING im_actor_type = 'P' im_plvar = '01' im_otype = 'P' im_objid = lv_pernr_nxtappr. " PERNR of Next Approver IF sy-subrc = 0. * Provides all the attributes of the request CALL METHOD lr_request->if_pt_req_request~workarea_version->get_all_attribs IMPORTING ex_all_attribs = ls_attribs. * Create Persistent Database Version CALL METHOD lr_request->clone_to_new. * Commit work and wait COMMIT WORK AND WAIT. ENDIF. ENDIF. * Dequeue the request CALL FUNCTION 'DEQUEUE_EPTREQ' EXPORTING mode_ptreq_header = 'S' request_id = iv_request_id. ENDMETHOD.
6. Activity: Set Status Send. Importing: Request ID, Exporting: New Status
7. Activity: Set Status Approved. Importing: Request ID, Exporting: New Status
8. Activity: Get Current Status. Latest status of the request from PTREQ_HEADER
9. Loop Condition
Rest is standard. Email and SMS logic is all over internet. Find and send them.
Now we are done with the Workflow development and explanation. Technical part is done as well, if you have any question, please feel free to comment.
Leave Request Approved but dump in ST22
Since, we are done with the workflow. Pretty easy yeah? But where is the challenge.
Go to Step number 6: Change Status to SEND. Somehow SAP don’t change this status and throws and dump. This dump comes whenever the Approve button is pressed either from Fiori or ESS/MSS. Why this dump occurs? Let’s catch that! I have analyzed this thing and I have concrete answer to this (this is the pain area for most of us).
Dump details are as follows:
Runtime Errors: UNCAUGHT_EXCEPTION Exception: CX_OS_DB_INSERT ABAP Program: CA_PT_REQ_HEADER==============CP
Just putting in a screenshot just to be sure.
Above dump says that there an expectation when inserting in some table? Which table? Let’s analyze.
Dump occurred in this method: MAP_SAVE_TO_DATABASE line: 152. This dump occurred when I tried to change the Status from APPROVE to SENT in my custom (CHANGE_STATUS_SENT) method.
Scenario for CX_OS_DB_INSERT Dump
- Status didn’t get changed
- There was no next approver
- Loop found the current status of request as SENT, loop condition met.
- Leave Request again sent to the same approver again!
How irritating for the user who has just approved the leave request and came back to him! Wow! Great! Amazing! Imagine this happening with 1800 users!
Why Dump Occurred
Why this dump came? Because by default sometimes I don’t know how. SAP put an entry of status SENT when leave is approved (specially in case of multi-level). That SENT row is already there in table PTREQ_HEADER and I am trying to insert the same row. So, it says I have that row already which you are trying to insert and dumps! Let me show you the table.
You see above? All rows are set to SENT. Why? Because of that nasty dump!
Solution to dump: Runtime Errors: UNCAUGHT_EXCEPTION. Exception: CX_OS_DB_INSERT. ABAP Program: CA_PT_REQ_HEADER==============CP
You could have noticed that I have put a WAIT statement in my methods, yeah because let SAP do its entries in the said table then I will do my trick. So, let SAP put a SENT status in the table after the request is approved. Then I am creating a new row with status APPROVED then SENT again.
Leave Request posted but the Document in PTARQ is in Error
This is an easy catch. If any document goes in to error (remember, not talking about workflow).
Just run the following two reports:
- RPTARQERR for less than 50 employees
- RPTARQERR_ALL for more
That’s all folks! I hope you have enjoyed this series and learned a lot. Please let me know if you have any queries.
Thank you!
New NetWeaver Information at SAP.com
Very Helpfull