SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

dicksonjyl560101發表於2019-07-11


SAP FIORI My Inbox App – Custom Purchase Requisition Workflow


https://blogs.sap.com/2018/07/23/fiori-my-inbox-app-custom-purchase-requisition-workflow/


Requirements and Corresponding Solutions .

One of our clients has a requirement to use FIORI and SAP GUI for their PO and PR approval process, client is using SAP S4HANA 1610.

Purchase order workflow is pretty standard so we will not talk about it in this blog.

Purchase requisition workflow is heavily customized and we had to follow certain steps to make it work on FIORI My Inbox App.

Duplicate Level of Approval

Usually for each level in release strategy separate work item goes to the corresponding approver(s) inbox. However here business requirement was little different. If the same approver(s) is maintained at consecutive levels, then he/she should able to approve the levels in one go. System should check who is maintained at the next level and accordingly he/she should able to process those many levels. All these levels get enabled to release based on the roles provided to the approver. If approver doesn't have a role for any particular level then he/she will not able to see the release button enabled .

Example: If approver X is maintained at levels Y1,Y2,Y3 then X should able to release the PR from Y1 to Y3 once he open the PR for release and SAVE only once. After the SAVE next workflow for next release strategy should trigger i.e Y4.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

PS: This multilevel approval in one go can only be done in SAP GUI and not in My Inbox APP or at least we did not find a way to handle it, because My Inbox App is replication of SBWP and when you have same approver at consecutive levels, the approver will receive a new work item for each level, and those will be shown one by one in My Inbox App.

Processor Name in PR Header

Business requirement was to have the release strategy window at PR header level. We could have achieved this just by configuring the approvers along with the release strategy and have them automatically populated on the header level once the PR gets created. However in current business requirement multiple or single approver for any particular level so the config was not possible, to achieve this we have maintained a custom matrix table.

Business wanted to see the approvers in the header in the field called 'PROCESSOR'. This field gets populated only if there is some value else this column will not be visible. So we required to find out a way which will populate the values for processor. If there are multiple approver's then populate MULTIPLE for that level else for single approver populate the name of the approver.

Standard Function Module ME_REL_GET_RESPONSIBLE_EBAN is enhanced for this purpose. Export parameter ex_actors needs to be populated with the desired processors/approvers.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Processor field will now populate dynamically as shown below:

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Passing FIORI Links in Email.

Business requirement was to get a fiori link in the SBWP work item and in an email so that approver will click on the link and get an access to the Fiori Launchpad directly.

Solution is provided by using the dictionary element ZCFX_HYPERLINK.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Work item in SBWP would look like :

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Email going to approver would look like :

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

We need to declare a workflow container element of below type.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Piece of code required to appear the given link as hyperlink and which ideally should open the internet explorer or your default web browser.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

To populate GV_LINKS we have used standard texts (SO10) to maintain links for different systems – Dev, QA and Production. We have fetched the links in workflow step using function module read_text. You can have your own logic to fetch FIORI links in email.

FIORI Configuration

FrontEnd System

Check your release task in workflow and maintain it in scenario definition.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Make sure to use the System Alias as assigned to the TaskProcessing Service in transaction  /iwfnd/maint_services  and assign it to Custom Release Task, in our case its TS90100009.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

BackEnd System

In our scenario we have 2 step ids in workflow which are dialog tasks for different levels of approvers based on the workflow logic.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

So we need to maintain both 28 and 59 in Task Names and Decision Options, so both can appear in My Inbox App

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Both Task should have same decision keys.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Transaction SWFVISU (Define Visualization Parameter)

Copy the settings of standard task TS20000159 and add our custom workflow release task TS90100009. (even though we have 2 separate release steps in workflow Node 28 and 59 PFTC task for both is same)

BADI /IWWRK/BADI_WF_BEFORE_UPD_IB to read release codes and FIORI rejection and approval comments.

For Implementation – Make sure to add correct filter values

For below we have WS90100004 (PR Approval Workflow) with Task 28 and 59 as decision tasks for different levels based on workflow logic.

WS90100006 and Task 5 is rejection workflow which triggers Rejection Notification to the PR creator.

SAP FIORI My Inbox App – Custom Purchase Requisition Workflow

Code block of Method Before Update – you may need to tweak it based on your scenario.

· Below we are showing on how to capture release codes – mandatory step to make sure approve / reject works as required.

· Also we have captured Approve and Reject comments and are appending to those texts in Purchase requisition header.

" Copied from CL_MM_PUR_REQ_APPR_ACTION_BADI*---------------------------------------------------------------------*
* Program Name : ZCL_P2P_PR_APP
* Author : KCHANGRANI
* Creation Date : June 19 2018* RICEF ID : INC0080003
* Description : Capture user decision.
* :
* Purpose(optional) :
*---------------------------------------------------------------------*
* Change History
* Date Programmer Transport Chg Req# Description
* 06/11/2018 DShingare HADK908472 INC0080003 Changes regarding texts
*----------------------------------------------------------------------*
 method /iwwrk/if_wf_wi_before_upd_ib~before_update.
 data ls_object type swr_obj_2.
* DATA lv_objtype TYPE swr_struct-object_typ.
* DATA lv_objkey TYPE swr_struct-object_key.
 data lv_retcode type sy-subrc.
 data lt_container type table of swr_cont.
 data ls_container_line type swr_cont.
 data ls_container_line2 type swr_cont.
 data formnumber type swxformabs-formnumber.
 data ls_formabs type swxformabs.
 data lv_release_code type frgco.
 data ls_contianer type swr_cont.
 data: lv_pr_num type banfn,
 lv_pr_itm type bnfpo,
 lv_user_id type syuname,
 ls_wiid_boident type mmpur_utils_workitem_boident,
 ls_wfl_inb type mmpur_utils_workflow_task,
 lo_wf_api type ref to cl_mm_pur_util_apv_wf_api,
 lv_bo_itm type swo_objtyp value cl_mmpur_constants=>if_mmpur_constants_archive~bus2009,
 lv_bo_hdr type swo_objtyp value cl_mmpur_constants=>if_mmpur_constants_archive~bus2105,
 lo_const type ref to cl_mmpur_constants,
 lt_swwwihead type standard table of swwwihead,
 ls_swwwihead type swwwihead,
 lv_index type sy-index.
 data lt_msg_lines type sapi_msg_lines.
 data: lt_msg_struc type sapi_msg_struc,
 lt_subcontainer_bor type /iwwrk/tt_wf_container,
 lt_subcontainer_all type /iwwrk/tt_wf_container,
 lt_object type swrtobject.
 data:
 lv_td_name type tdobname,
 lv_td_object type tdobject value 'PROC_WFL',
 lt_line type tline_t,
 lt_lines type tline_t,
 ls_line type tline,
 ls_header type thead.
* DATA lv_retcode TYPE sy-subrc.
* Get PR object ID and item number from UTIl class.
 lo_wf_api = cl_mm_pur_util_apv_wf_api=>get_instance( ).
 lv_user_id = sy-uname.
 ls_wfl_inb-workitem_id = is_wi_details-wi_id.
* CREATE OBJECT lo_const. if lo_wf_api is not initial.
* Get the Object Information from WorkItem. call method lo_wf_api->get_boident_for_workitem
 exporting
 iv_user_id = lv_user_id
 is_workflow_inbox = ls_wfl_inb
 importing
 es_wiid_boident = ls_wiid_boident.
* Check, that the PR is found for the given Workitem ID if ls_wiid_boident is not initial.
 lv_pr_num = ls_wiid_boident-object_id.
 lv_pr_itm = ls_wiid_boident-object_line. else.
* Handle error return. endif. endif. "Access the workflow data
 call function 'SAP_WAPI_GET_OBJECTS'
 exporting
 workitem_id = is_wi_details-wi_id
 importing
 leading_object_2 = ls_object. "Get the formnumber which is the key to the absence table
 move ls_object-instid to formnumber. "Select the details of the absence from the table SWXFORMABS
 select single * from swxformabs
 into ls_formabs
 where formnumber = formnumber. " get the work item id where we can get the release code.
 " 1 get related work item id Function Module SWI_GET_RELATED_WORKITEMS
 " 2 get release code Function module SAP_WAPI_READ_CONTAINER
 call function 'SWI_GET_RELATED_WORKITEMS'
 exporting
 wi_id = is_wi_details-wi_id
 tables
 related_wis = lt_swwwihead. if sy-subrc = 0. " Keep looking for
 loop at lt_swwwihead into ls_swwwihead. "Read the workflow's container data
 clear lt_container.
 refresh lt_container. call function 'SAP_WAPI_READ_CONTAINER'
 exporting
 workitem_id = ls_swwwihead-wi_id
* LANGUAGE = SY-LANGU
* USER = SY-UNAME
* BUFFERED_ACCESS = 'X'
 importing
 return_code = lv_retcode
* IFS_XML_CONTAINER =
* IFS_XML_CONTAINER_SCHEMA =
 tables
 simple_container = lt_container
 message_lines = lt_msg_lines
 message_struct = lt_msg_struc
 subcontainer_bor_objects = lt_subcontainer_bor
 subcontainer_all_objects = lt_subcontainer_all. call function 'SAP_WAPI_GET_ATTACHMENTS'
 exporting
 workitem_id = ls_swwwihead-wi_id
* USER = SY-UNAME
* LANGUAGE = SY-LANGU
* COMMENT_SEMANTIC_ONLY = ' '
 importing
 return_code = lv_retcode
 tables
 attachments = lt_object
 message_lines = lt_msg_lines
 message_struct = lt_msg_struc. " Check which decision was selected and set the data
 " values appropriately
 read table lt_container into ls_contianer with key element = 'GV_REL_CODE'. if sy-subrc eq 0.
 lv_release_code = ls_contianer-value. exit. " exit the loop as we found the release code we were looking for.
 endif.
 endloop. endif. if lv_pr_num is initial.
 lv_pr_num = ls_object-instid(10). endif.
 case iv_decision_key.
 when 0001. "Approved
 ls_container_line-value = 'A'.
 ls_formabs-procstate = 'A'. try. call method me->set_decision_release
 exporting
 iv_pr_num = lv_pr_num
 iv_pr_itm_num = lv_pr_itm
 iv_release_code = lv_release_code. catch /iwbep/cx_mgw_busi_exception . "Handle error
 return. endtry. "Once approval is done update FIORI Approval Notes in PR.
 read table it_wf_container_tab into data(ls_comment)
 with key element = 'ACTION_COMMENTS'. if sy-subrc = 0. if lv_pr_itm is initial.
 lv_td_name = lv_pr_num. else.
 concatenate lv_pr_num lv_pr_itm into lv_td_name. endif.
 concatenate sy-uname ':' lv_release_code ':' ls_comment-value into ls_line-tdline.
* ls_line-tdline = ls_comment-value.
 ls_line-tdformat = `/`. call function 'READ_TEXT'
 exporting
* CLIENT = SY-MANDT
 id = 'B02' "Approve Notes(FIORI)
 language = sy-langu
 name = lv_td_name
 object = 'EBANH'
 tables
 lines = lt_lines
 exceptions
 id = 1
 language = 2
 name = 3
 not_found = 4
 object = 5
 reference_check = 6
 wrong_access_to_archive = 7
 others = 8. if lt_lines is not initial. append ls_line to lt_lines.
 ls_header-tdobject = 'EBANH'.
 ls_header-tdname = lv_td_name.
 ls_header-tdid = 'B02'. "Approve Notes(FIORI)
 ls_header-tdspras = sy-langu. call function 'INSERT_TEXT_AFTER_COMMIT'
 exporting
 header = ls_header
 tables
 lines = lt_lines. if sy-subrc eq 0. endif. elseif lt_lines is initial. " no previous text
 append ls_line to lt_lines. " new approval notes.
 ls_header-tdobject = 'EBANH'.
 ls_header-tdname = lv_td_name.
 ls_header-tdid = 'B02'. "Approve Notes(FIORI)
 ls_header-tdspras = sy-langu. call function 'INSERT_TEXT_AFTER_COMMIT'
 exporting
 header = ls_header
 tables
 lines = lt_lines. if sy-subrc eq 0. endif. else.
* do nothing endif. endif.
 when 0002. "Rejected
 ls_container_line-value = 'R'.
 ls_formabs-procstate = 'R'. try. call method me->set_decision_reject
 exporting
 iv_pr_num = lv_pr_num
 iv_pr_itm_num = lv_pr_itm
 iv_release_code = lv_release_code. catch /iwbep/cx_mgw_busi_exception . "Handle error
 return. endtry. " get rejection comments.
 read table it_wf_container_tab into ls_comment
 with key element = 'ACTION_COMMENTS'. if sy-subrc = 0. if lv_pr_itm is initial.
 lv_td_name = lv_pr_num. else.
 concatenate lv_pr_num lv_pr_itm into lv_td_name. endif.
 concatenate sy-uname ':' lv_release_code ':' ls_comment-value into ls_line-tdline.
* ls_line-tdline = ls_comment-value.
 ls_line-tdformat = `/`. call function 'READ_TEXT'
 exporting
* CLIENT = SY-MANDT
 id = 'B03'
 language = sy-langu
 name = lv_td_name
 object = 'EBANH'
 tables
 lines = lt_lines
 exceptions
 id = 1
 language = 2
 name = 3
 not_found = 4
 object = 5
 reference_check = 6
 wrong_access_to_archive = 7
 others = 8. if lt_lines is not initial. append ls_line to lt_lines.
 ls_header-tdobject = 'EBANH'.
 ls_header-tdname = lv_td_name.
 ls_header-tdid = 'B03'.
 ls_header-tdspras = sy-langu. call function 'INSERT_TEXT_AFTER_COMMIT'
 exporting
 header = ls_header
 tables
 lines = lt_lines. if sy-subrc eq 0. endif. elseif lt_lines is initial. append ls_line to lt_lines.
 ls_header-tdobject = 'EBANH'.
 ls_header-tdname = lv_td_name.
 ls_header-tdid = 'B03'.
 ls_header-tdspras = sy-langu. call function 'INSERT_TEXT_AFTER_COMMIT'
 exporting
 header = ls_header
 tables
 lines = lt_lines. if sy-subrc eq 0. endif. else.
* do nothing endif. endif.
 endcase. "_WI_RESULT is what the workflow keys off to determine
 "which path to follow - Approve or Reject path
 ls_container_line-element = '_WI_RESULT'. "Modify the workflow's container data - we are updating the row that
 "holds _WI_RESULT which will be in the second row of the table
 read table lt_container into ls_container_line2 with key element = '_WI_RESULT'. if sy-subrc = 0.
 lv_index = sy-tabix.
 modify lt_container index lv_index from ls_container_line . else. append ls_container_line to lt_container. endif. call function 'SAP_WAPI_WRITE_CONTAINER'
 exporting
 workitem_id = is_wi_details-wi_id language = sy-langu
 actual_agent = sy-uname
 do_commit = 'X'* IFS_XML_CONTAINER =
* OVERWRITE_TABLES_SIMPLE_CONT = ' '* CHECK_INBOX_RESTRICTION = ' '
 importing
 return_code = lv_retcode
 tables
 simple_container = lt_container
 message_lines = lt_msg_lines
 message_struct = lt_msg_struc. "Update the Absence table with the updated data
 ls_formabs-approvdate = sy-datum.
 ls_formabs-approvby = sy-uname. update swxformabs from ls_formabs. " Commit workitem.* trigger update task and persistency layer
 go_factory->commit( ). "Complete the task
 call function 'SAP_WAPI_WORKITEM_COMPLETE'
 exporting
 workitem_id = is_wi_details-wi_id
 actual_agent = sy-uname language = sy-langu
* SET_OBSOLET = ' '
 do_commit = 'X'* DO_CALLBACK_IN_BACKGROUND = 'X'* IFS_XML_CONTAINER =
* CHECK_INBOX_RESTRICTION = ' '
 importing
 return_code = lv_retcode
* NEW_STATUS =
 tables
* SIMPLE_CONTAINER =
 message_lines = lt_msg_lines
 message_struct = lt_msg_struc. "This task requires a confirm to be fully completed
 "and start the next step in the workflow
 call function 'SAP_WAPI_WORKITEM_CONFIRM'
 exporting
 workitem_id = is_wi_details-wi_id
 actual_agent = sy-uname language = sy-langu
 do_commit = 'X'* CHECK_INBOX_RESTRICTION = ' '* DO_CALLBACK_IN_BACKGROUND = 'X'
 importing
 return_code = lv_retcode
* NEW_STATUS = 'STARTED'
 tables
 message_lines = lt_msg_lines
 message_struct = lt_msg_struc.
*Below code is not necessary if you are using a simple workflow with only 1 dialog step for approval.
*start of change INC0080003-06/11/2018{ Dshingare
* Data declarations:
 data: lv_objkey type sweinstcou-objkey,
 lt_cont type table of swr_cont,
 ls_cont type swr_cont,
 lt_rel_users type table of zpreq_rel_users,
 lt_rel_temp type table of zpreq_rel_users,
 ls_rel_users type zpreq_rel_users,
 ls_preq_wfuser type zpreq_wfuser,
 ls_rel_temp type zpreq_rel_users,
 lv_curr_code type frgco,
 lv_ernam type ernam,
 lv_kostl type kostl,
 lv_werks type ewerk,
 lv_lines type i,
 lv_tabix type sytabix,
 lv_next_code type frgco,
 lv_last_code type frgco.
 clear: lv_kostl,
 lv_werks,
 lv_tabix,
 lv_lines,
 lv_last_code,
 lv_next_code. if lv_release_code eq 'Y0'.
 lv_next_code = 'Y1'. else.
 select single kostl
 from ebkn
 into lv_kostl
 where banfn = lv_pr_num. if sy-subrc eq 0.
 select single werks
 from eban
 into lv_werks
 where banfn = lv_pr_num. if sy-subrc eq 0.
 select *
 from zpreq_rel_users
 into table lt_rel_users
 where kostl = lv_kostl and werks = lv_werks. if sy-subrc eq 0. sort lt_rel_users[] by alevel.
 lt_rel_temp[] = lt_rel_users[]. sort lt_rel_temp[] by alevel. delete adjacent duplicates from lt_rel_temp[] comparing alevel. sort lt_rel_temp[] by alevel ascending . read table lt_rel_temp[] into ls_rel_temp with key alevel = lv_release_code.
 describe table lt_rel_temp[] lines lv_lines. if sy-subrc eq 0. if lv_lines gt sy-tabix.
 lv_tabix = sy-tabix + 1. else.
 lv_tabix = sy-tabix. read table lt_rel_temp into ls_rel_temp index lv_lines. if sy-subrc eq 0. if ls_rel_temp-alevel eq lv_release_code.
 lv_last_code = 'X'. exit. endif. endif. endif. read table lt_rel_temp into ls_rel_temp index lv_tabix. if sy-subrc eq 0.
 lv_next_code = ls_rel_temp-alevel. if not lv_next_code is initial. delete from zpreq_wfuser where banfn = lv_pr_num.
 loop at lt_rel_users into ls_rel_users where alevel = lv_next_code.
 ls_preq_wfuser-banfn = lv_pr_num.
 ls_preq_wfuser-bname = ls_rel_users-bname.
 ls_preq_wfuser-alevel = ls_rel_users-alevel. insert zpreq_wfuser from ls_preq_wfuser.
 commit work.
 clear: ls_preq_wfuser,
 ls_preq_wfuser.
 endloop. endif. endif. endif. endif. endif. endif. endif.
 select single ernam
 from eban
 into lv_ernam
 where banfn = lv_pr_num.
 ls_cont-element = 'REL_CODE_NEW'.
 ls_cont-value = lv_next_code. append ls_cont to lt_cont.
 ls_cont-element = 'PR_CREATOR'.
 ls_cont-value = lv_ernam. append ls_cont to lt_cont.
 lv_objkey = lv_pr_num. if iv_decision_key ne '0002'. "Event should not triggere when Rejection happens
 if lv_last_code ne 'X'.
* Raise a workflow call function 'SAP_WAPI_CREATE_EVENT'
 exporting
 object_type = 'BUS2105'
 object_key = lv_objkey
 event = 'Z_RELEASE_PR'
 tables
 input_container = lt_cont. if sy-subrc eq 0.
 clear lv_next_code. endif. endif. endif.
*} End of change INC0080003-06/11/2018
 endmethod.

Hope our attempt to share knowledge will help someone in community while using custom PR workflow and My Inbox FIORI App, please feel free to provide your comments so we can update the blog or write a better blog next time.

Acknowledgements

This blog and work was not possible without great contribution of all mentors of SAP FIORI Community and especially experts who are providing their guidance in My Inbox Wiki : Jocelyn Dart, Masayuki Sekihara, Tejas Chouhan and many more people who are researching everyday and providing contributions on this wiki.

Special thanks to my friend and mentor Krishna Kishor Kammaje for helping with initial steps.

Thanks to Hitachi SAP experts Kishore Kunadharaju and Avinash Bhapkar for continued support.

Last but no least my partner Dinesh Shingare, thanks for all the hard-work to achieve all business requirements.


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29829936/viewspace-2650255/,如需轉載,請註明出處,否則將追究法律責任。

相關文章