Symptom
This Wiki Page will show you how to Customize the ESS Leave Approval process using SAP Business Workflow.
Solution
We will configure the Leave Approval process with Standard Workflow Template WS12300111, which provides a single level of approval.
Additionally, we will also discuss the possibility of scaling this to a Workflow with multiple levels of approval.
Activating the Leave Workflow
As you would have already read on page Transactions and Reports in PT on ESS, the entire Leave Framework can be simulated via Transaction code PTARQ.
These steps should be followed in order to activate the Approval Workflow for Leaves.
- First, maintain Visualization Parameters for tasks of WS12300111 in SWFVISU .
The two Tasks of the Workflow, TS12300097 and TS12300116 are to be visualized to Java Webdynpro Applications.
If there is a Task TS12300104, this Task should be deleted according to SAP note 779075
I found this Wiki page on SWFVISU , which might be helpful if you require to customize and visualize your own tasks.
- Next, Activate Approval via Workflow for the leave types.
This can be done via PTARQ > Customizing > Employee Self-Service > Service-Specific Settings > Working Time > Leave Request > Processing Processes > Specify Processing Processes for Types of Leave > Define Absences/Processing Processes
( You can also reach the customizing setting from SPRO via SAP Customizing Implementation Guide > Personnel Management > Employee Self-Service > Service-Specific Settings > Working Time > Leave Request > Processing Processes > Specify Processing Processes for Types of Leave > Define Absences/Processing Processes )
These are the same tables like in the old ITS based version - and can also be reached over the customizing for the old ITS based version. However, the above path also provides the correct documentation.
Select or Add a Leave Type, then click then edit button/or double click it to go to the next screen which displays more parameters.
Check the box that says " Process Request using Workflow".
Once you check this option, 3 new parameters appear :
a. WF ID of New Request
b. WF ID of Cancellation Request
c. WF ID of Change Request
You should fill out the Workflow Template number "12300111" (or any custom workflow that you'd developed for the purpose) against each of these, ofcourse based on scenarios you would want to handle with the Workflow.
This allows us to define which actions use which workflow for a given Leave Type. If you keep one of the fields mentioned above empty, the standard workflow is taken - please fill all the fields all the time.
In case you do not want an approval for a special leave type, you may uncheck the checkbox 'Request Have To Be Approved'.
- Next, You should run/schedule the report RPTARQPOST to post the Approved Leaves (For Approved Leaves, PTREQ_HEADER-STATUS = APPROVED).
On successful execution, RPTARQPOST updates the Leave Infotypes with the approved Leave data.
Good to know stuff: |
---|
1. BADI PT_GEN_REQ can give you more control on the processing process like Filtering Agents, Starting the Workflow, etc. |
Scaling the Workflow for Multiple Approval Levels
An object 'Req' of the CL_PT_REQ_WF_ATTRIBS is passed to the Workflow container when it is being triggered.
Note that 'Req' references the Persistent Object of the Leave Request. Any change to the Persistent Instance of the Request means that the change will be visible in 'Req'.
You can always reference the Persistent Instance of the Request by calling the method CL_PT_REQ_WF_ATTRIBS=>BI_PERSISTENT~FIND_BY_LPOR (Note that : LPOR-INSTID = REQUEST_ID).
To scale the Approval process to multiple levels, you should copy the existing template WS12300111 and modify the new template introducing steps for further levels of approval.
You would have noticed that once approved, the Request status would go from SENT to APPROVED. In the Workflow this would affect the value of REQ.STATUS.
We need to introduce a couple of steps to facilitate the next level Approval process via ESS.
Step 1. Reseting the Status to SENT
In WS12300111 this was the Status change that we checked for at Step 000158 ('Request Approved?'). Now, for the new template, if the outcome of this step is 'Approved',
we should re-set the status to SENT. Sample code for state transition from APPROVED to SENT is given below:
DATA: lcl_request type ref to if_pt_req_request. *-- Enqueue the request call function 'ENQUEUE_EPTREQ' exporting mode_ptreq_header = 'S' mandt = sy-mandt request_id = Request_id exceptions foreign_lock = 1 system_failure = 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. *-- Get the Persistent Leave Object call method cl_pt_req_badi=>get_request exporting im_req_id = Request_id importing ex_request = lcl_request. *-- Initiate the State Transition to SENT call method cl_pt_req_badi=>initiate_state_transition exporting im_request = lcl_request im_event = cl_pt_req_const=>c_reqtrans_send importing ex_new_status = new_status. commit work and wait. *-- Dequeue call function 'DEQUEUE_EPTREQ' exporting mode_ptreq_header = 'S' request_id = Request_id.
Step 2: Setting the Next Processor
After the Agent Determination for the Next Level of Approval, it is essential that you update the Request with this information.
The CL_PT_REQ_WF_ATTRIBS class has an Attribute called N_PROCESSOR which needs to be set so that this is visible in ESS.
The sample code to achieve this is given below :
*--Enqueue the request callfunction'ENQUEUE_EPTREQ' exporting mode_ptreq_header='S' mandt=sy-mandt request_id=Request_id exceptions foreign_lock=1 system_failure=2 others=3. *-- Get the request object instance call method cl_pt_req_badi=>get_request exporting im_req_id =Request_ID importing ex_request = lcl_request. call method lcl_request->set_next_processor exporting im_actor_type = 'P' im_plvar = '01' im_otype = 'P' im_objid = NextApprover_PERNR. " PERNR of Next Approver if sy-subrc = 0. commit work and wait. endif. *-- Dequeue the request callfunction'DEQUEUE_EPTREQ' exporting mode_ptreq_header='S' request_id=Request_id.
The new Workflow Template can now have any number of Approvals with the above Step 1 and Step 2 being inserted after each approval (Task TS12300097) and only in case if there is another Level of Approval .
Do not forget to set this New Approver as the Agent for TS12300097 as well.
See Also
If you are new to PT on ESS, you should consider reading these pages on this Wiki:
- Getting Started in ESS
- Getting Started on PT in ESS
- Transactions and Reports in PT on ESS
- Leave Request Various Configuraton Steps
Got any problems with this solution? You can read related articles in the Forums on this:
- Issue with two level ESS leave request workflow
- Customization Done for 2 level approval of leave in ESS but Facing Problems
Author: Vimal Vidyadharan
Submitted: 27-10-2011
26 Comments
Shankar Agarwal
Nice article
Unknown User (wxtag87)
I tried to adapt your solution to my current project but i found some problem when I tried to reset status in table PTREQ_HEADER using your code.
I put your code to one background task after approval step to reset status to be "SENT" but sometime status don't change.
If i change this task to foreground mode, this problem never occurred.
I'm not sure, do you face this problem or not ?
Vimal Vidyadharan
I believe you are facing problem not in resetting the status, but with the status not correctly reflecting in the workflow.
All you need to do is, if the reset is successful, update the Status variable in Workflow container with the latest status.
Do not forge the commit work and wait when updating the DB as in case there is a DB update delay you might not get the right status in the Workflow.
Unknown User (t039e9l)
Good write up!
Vimal Vidyadharan
-deleted-
Vimal Vidyadharan
Sorry about the late response. But did you resolve this? Just in case you did not, please place a COMMIT WORK AND WAIT before you complete the Work Item.
Bhaskar Tripathi
Hi Vimal,
Where to add all the code that you have mentioned. Enhancing class CL_PT_REQ_WF_ATTRIBS with new method couldn't be an option as those method wouldn't show up during method assignment in Task maintenance. And if we copy this class and create a new method then I am unable to pass on the workflow container element REQ to this custom class instance (for obvious reason of compatibility).
How we do this then?
Best Regards,
Bhaskar
Vimal Vidyadharan
Hello Bhaskar,
There are multiple approaches to this - including redefining the methods you would want to enhance, or adding post or pre methods .
However, IMHO, a less disruptive one/an approach with little modification, would be to include a Business Object for the custom process tasks, such as resetting the approval, determination of approver, etc.This helps as all you need to do with standard is to configure the new WF in PTARQ and then handle all the custom scenario via your custom WF and BO.
The points I enhanced were where ESS Leave connects to the Workflow. For everything else, i.e. Workflow steps, I had the new Business object.
A few Scenarios that needed enhancing CL_PT_REQ* / RFCs were for instance -
CL_PT_REQ_WF_ATTRIBS happens to be a persistent class for ESS Leave. This is also the class that is referenced in your ESS Leave java component. If you decide to copy and customize, you might have to handle a lot of changes at integration points yourself.
Hope this helps.
Do feel free to discuss your issues with this solution.
We have this ESS WF live and running fine at some customer sites already. I'll be interested to know of any scenario this solution can not handle, we could always improvise.
Thanks,
Vimal
Mohamed Samir
Hi Vimal ,
how can we change approver and determine another approver based on some conditions we have ??
Nurullah Rüstem
Hi Vimal,
Where could we add the code that you give following step 1?
"Step 1. Reseting the Status to SENT In WS12300111 this was the Status change that we checked for at Step 000158 ('Request Approved?'). Now, for the new template, if the outcome of this step is 'Approved', we should re-set the status to SENT. Sample code for state transition from APPROVED to SENT is given below:"
Thanks is advance
Vimal Vidyadharan
Hi Nurullah Rüstem - This code is in the Background workitem, which is the next step in workflow after the Approval - in case you want to handle multiple levels.
Apologies for the late reply.
Barin DESAI
hi all
good blog.
one question here.i have two approvers and the requirement is that initiator applies for request and first approver approves.
here you are changing the status to sent.
now the initiator cancells and the requirement is that as the first approver should receive the cancellation workitem. however as the status is sent the workflow doesn't ge initiated and the record is deleted without workflow.
please advise on above.
thank you.
Vimal Vidyadharan
Hi Barin,
If the initiator cancels the Leave -
Hope this helps.
Apologies for the delayed response.
Anonymous
hi @BARIN DESAI,
when employee applies leave he can see the first level approver name in the Next processor .....and when first approver approves , it will go to second approver , and when employee tries to cancel teh leave he cannot delete the leave becoz he cannot see the approver name , so it will not cancel the leave if first level approves , if first level is not approved he can cancel the leave ...try once this ....if both level approves then employee can cancel the leave and it will go to first level approver the cancellation request from workflow .....if both level approves it will get delted from 2001
Vimal Vidyadharan
Nurullah Rüstem - This code is in the Background workitem, which is the next step in workflow after the Approval - in case you want to handle multiple levels.
Apologies for the late reply.
Former Member
I encountered a problem with the setting of the next approver with the coding above.
While in the method the request was updated correct, the commit didn't update the request in the database.
I was able to solve the problem like following:
Instead of
call method lcl_request->set_next_processor
exporting
im_actor_type =
'P'
im_plvar =
'01'
im_otype =
'P'
im_objid = NextApprover_PERNR. " PERNR of Next Approver
if
sy-subrc =
0
.
commit work and wait.
endif.
CALL METHOD lcl_request->if_pt_req_request~set_next_processor
EXPORTING
im_actor_type = 'P'
im_plvar = '01'
im_otype = 'P'
im_objid = iv_pernr. " PERNR of Next Approver
IF sy-subrc = 0.
CALL METHOD lcl_request->if_pt_req_request~workarea_version->get_all_attribs
IMPORTING
ex_all_attribs = ls_attribs.
CALL METHOD lcl_request->clone_to_old.
commit_work and wait.
ENDIF.
Maybe someone has the same trouble like me.
Vimal Vidyadharan
Thank you for pointing this out Jens, Yes I too have seen the problem in one system - could be something to do with patch level - they behave slightly differently.
chaitanya kumar
Hi Jens,
I got the same issue that Next Processor is not getting updated in the Database.
Can you please give me the complete code how you solved the issue.
How to get the LCL_request.
Thanks
Former Member
Hi,
sure I can post the complete coding:
I got REQ_ID Type TIM_REQ_ID and PERNR Type PERNR_D as Importing Parameter in my class.
Method coding is as followed:
DATA: lr_request TYPE REF TO cl_pt_req_request,
lv_oid TYPE os_guid,
ls_attribs type PTREQ_ATTRIBS_STRUC_FLAT.
lv_oid = req_id.
*--Enqueue the request
CALL FUNCTION 'ENQUEUE_EPTREQ'
EXPORTING
mode_ptreq_header = 'S'
mandt = sy-mandt
request_id = req_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 ).
CALL METHOD lr_request->if_pt_req_request~set_next_processor
EXPORTING
im_actor_type = 'P'
im_plvar = '01'
im_otype = 'P'
im_objid = pernr.
IF sy-subrc = 0.
CALL METHOD lr_request->if_pt_req_request~workarea_version->get_all_attribs
IMPORTING
ex_all_attribs = ls_attribs.
CALL METHOD lr_request->clone_to_old.
ENDIF.
*-- Dequeue the request
CALL FUNCTION 'DEQUEUE_EPTREQ'
EXPORTING
mode_ptreq_header = 'S'
request_id = req_id.
chaitanya kumar
Hi Jens,
Thanks a lot . The issue got resolved.
Former Member
Hi ,
Can you guide me how to change workflow to fit for multilevel Approval?
suggest me workflow steps details
Thanks.
Former Member
Hi Experts,
I have implemented BADI : PT_GEN_REQ.
I used method START_WF for Workflow part.
I am getting below error message at CALL METHOD message_handler->add_message.
Error Message:
Access using a 'ZERO' object reference is not possible. (termination: RABAX_STATE)
Can anyone please let me know if any notes need to be implemented for this?
Code in Method: START_WF
method IF_EX_PT_GEN_REQ~START_WF.
DATA: im_agent_container TYPE REF TO if_swf_ifs_parameter_container,
lc_workitem_container TYPE REF TO if_swf_cnt_container,
agents TYPE TABLE OF swhactor INITIAL SIZE 0,
l_creator TYPE swwwihead-wi_creator,
workitem_id TYPE sww_wiid,
lc_workflow TYPE REF TO cl_wfd_adhoc_workflow_start,
lc_step TYPE REF TO if_wfd_adhoc_step,
l_tabix TYPE sy-tabix,
l_agent TYPE swdaagnt,
l_n_processor TYPE ptreq_actor_struc_flat,
lc_agent_container TYPE REF TO if_swf_cnt_container,
lcl_attribs TYPE ptreq_request_struc_flat,
lcl_wf_attribs TYPE REF TO cl_pt_req_wf_attribs,
lcl_lpor TYPE sibflpor,
lcl_bi_persistent TYPE REF TO bi_persistent,
lcl_workarea TYPE REF TO cl_pt_req_header,
lt_workflow_container TYPE swrtcont,
lt_wf_container_obj TYPE swrtcont,
lt_wf_container_por TYPE swrtcont,
lt_errors TYPE swft100tab.
DATA: lt_wf_error_tab TYPE TABLE OF swr_mstruc,
lt_wf_error_line TYPE swr_mstruc.
DATA: exit_gen TYPE REF TO pt_gen_req.
DATA: re_absent TYPE boole_d.
DATA: user TYPE sy-uname.
DATA: ex_subst_str TYPE struc_t.
DATA: subst_settings TYPE padd2.
DATA: wa_ex_subst TYPE LINE OF struc_t.
DATA: application TYPE REF TO cl_pt_req_application.
DATA: ex_subst_obj TYPE objec_t.
DATA: wa_ex_subst_obj TYPE LINE OF objec_t.
DATA: l_index type sy-tabix.
* RETURN_CODE logic:
* = 1 agent missing error
* = 2 data container error
* = 3 WF start failed
* = 4 some other error
application = cl_pt_req_application=>get_instance( ).
* call exit
exit_gen = application->functional_exit_gen.
*create workflow-start object
CREATE OBJECT lc_workflow
EXPORTING
task = im_main_task
EXCEPTIONS
task_without_adhoc_capability = 1
OTHERS = 2
.
IF sy-subrc <> 0.
return_code = 4.
EXIT.
ENDIF.
* get all step objects and display the assigned agents
DO.
ADD 1 TO l_tabix.
* get ad-hoc-workflow-step
CALL METHOD lc_workflow->step_by_index_get
EXPORTING
index = l_tabix
RECEIVING
step = lc_step
EXCEPTIONS
invalid_index = 1
OTHERS = 2.
IF sy-subrc <> 0.
EXIT.
ENDIF.
* set user for ad-hoc-agent --> NEXT PROCESSOR
CALL METHOD cl_pt_gen_badi=>get_next_processor
EXPORTING
im_request = im_request
IMPORTING
ex_actor_attribs = l_n_processor.
CALL BADI exit_gen->check_if_actor_absent
EXPORTING
im_pernr = l_n_processor-pernr
RECEIVING
result = re_absent.
* If the next processor is absent for xx days, then determine substitute for him/her.
IF re_absent = 'X'.
user = l_n_processor-user.
CALL BADI exit_gen->get_actor_substitutes
EXPORTING
im_user = user
IMPORTING
ex_subst_str = ex_subst_str
ex_subst_obj = ex_subst_obj.
CHECK NOT ex_subst_str IS INITIAL.
LOOP AT ex_subst_str INTO wa_ex_subst.
subst_settings = wa_ex_subst-vadata.
CHECK NOT subst_settings-active IS INITIAL. "Consider only active substitute
IF wa_ex_subst-otype = 'US'.
CLEAR l_agent-agent.
READ TABLE ex_subst_obj INTO wa_ex_subst_obj INDEX l_index.
CONCATENATE wa_ex_subst-otype wa_ex_subst_obj-realo INTO l_agent-agent.
CONCATENATE wa_ex_subst-otype wa_ex_subst-objid INTO l_agent-agent.
CALL METHOD lc_step->agent_append
EXPORTING
agent = l_agent
EXCEPTIONS
already_exists = 1
locked = 2
OTHERS = 3.
IF sy-subrc <> 0.
return_code = 1.
EXIT.
ENDIF.
ENDIF.
ENDLOOP.
ELSE.
CONCATENATE 'US' l_n_processor-user INTO l_agent-agent.
* append agent to processor-list
CALL METHOD lc_step->agent_append
EXPORTING
agent = l_agent
EXCEPTIONS
already_exists = 1
locked = 2
OTHERS = 3.
IF sy-subrc <> 0.
return_code = 1.
EXIT.
ENDIF.
ENDIF.
ENDDO.
* get agents (container format)
CALL METHOD lc_workflow->agents_as_container_get
IMPORTING
container = im_agent_container
EXCEPTIONS
agents_missing = 1
OTHERS = 2.
IF sy-subrc <> 0.
return_code = 1.
EXIT.
ENDIF.
TRY.
CALL METHOD cl_swf_cnt_factory=>create_task_container
EXPORTING
im_task_id = im_main_task
IMPORTING
ex_task_container = lc_workitem_container.
CATCH cx_swf_utl_obj_create_failed
cx_swf_utl_no_plan_variant
cx_swf_utl_task_not_found
cx_swf_utl_obj_invalid_ref .
return_code = 2.
EXIT.
ENDTRY.
* merge agents to container
IF im_agent_container IS BOUND.
lc_agent_container ?= im_agent_container.
TRY.
CALL METHOD lc_workitem_container->merge
EXPORTING
other_container = lc_agent_container.
CATCH cx_swf_cnt_container.
return_code = 2.
EXIT.
ENDTRY.
ENDIF.
*---Set workflow attributes
lcl_lpor-instid = im_request->if_pt_req_request~request_id.
CALL METHOD cl_pt_req_wf_attribs=>bi_persistent~find_by_lpor
EXPORTING
lpor = lcl_lpor
RECEIVING
result = lcl_bi_persistent.
lcl_wf_attribs ?= lcl_bi_persistent.
TRY.
CALL METHOD lc_workitem_container->element_set
EXPORTING
name = 'Req'
value = lcl_wf_attribs.
CATCH cx_swf_cnt_cont_access_denied
cx_swf_cnt_elem_not_found
cx_swf_cnt_elem_access_denied
cx_swf_cnt_elem_type_conflict
cx_swf_cnt_unit_type_conflict
cx_swf_cnt_elem_def_invalid
cx_swf_cnt_container .
return_code = 2.
EXIT.
ENDTRY.
*---Fill simple workflow container
CALL METHOD lc_workitem_container->to_simple_container
EXPORTING
import_param = 'X'
export_param = 'X'
changing_param = 'X'
returning_param = 'X'
no_system_elements = space
no_initial_elements = 'X'
IMPORTING
ex_container_values = lt_workflow_container
ex_container_sibflporbs = lt_wf_container_por
ex_t_messages = lt_errors.
APPEND LINES OF lt_wf_container_por TO lt_workflow_container.
*---Start workflow
CALL FUNCTION 'SAP_WAPI_START_WORKFLOW'
EXPORTING
task = im_main_task
do_commit = space
start_asynchronous = 'X'
IMPORTING
return_code = return_code
workitem_id = workitem_id
TABLES
input_container = lt_workflow_container
message_struct = lt_wf_error_tab.
IF return_code NE 0.
LOOP AT lt_wf_error_tab INTO lt_wf_error_line.
*---Add message to message handler
CALL METHOD message_handler->add_message
EXPORTING
im_type = lt_wf_error_line-msgty
im_cl = lt_wf_error_line-msgid
im_number = lt_wf_error_line-msgno
im_par1 = lt_wf_error_line-msgv1
im_par2 = lt_wf_error_line-msgv2
im_par3 = lt_wf_error_line-msgv3
im_par4 = lt_wf_error_line-msgv4
im_classname = 'CL_DEF_IM_PT_GEN_REQ'
im_methodname = 'IF_EX_PT_GEN_REQ~START_WF'.
ENDLOOP.
return_code = 3.
ENDIF.
CALL METHOD im_request->set_workitem_id
EXPORTING
im_workitem_id = workitem_id.
endmethod.
Thanks,
KR.
Former Member
Hi KR,
Excellent work thanks bro
Om Awasthi
a good work Vimal and Jagadessh
Former Member
Dear All,
Thanks for very useful blog..
I am facing one issue. Once i change the status to sent there is duplication of Note value. how can avoid duplication of note field.
Atul
Barin DESAI
Hi
I have a scenario here. As I understand you are changing the status of the request to sent after first approver approves.
Now in a two level request when the first approver has approved what will happen if employee decides to cancel the request? Will cancellation go to the first approver who has approved the leave request?
Thanks
barin