SAP FIORI My Inbox App – Custom Purchase Requisition Workflow
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.
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.
Processor field will now populate dynamically as shown below:
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.
Work item in SBWP would look like :
Email going to approver would look like :
We need to declare a workflow container element of below type.
Piece of code required to appear the given link as hyperlink and which ideally should open the internet explorer or your default web browser.
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.
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.
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.
So we need to maintain both 28 and 59 in Task Names and Decision Options, so both can appear in My Inbox App
Both Task should have same decision keys.
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.
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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Enabling Flexible SAP Workflow in Purchase RequisitionFlexUI
- SAP Fiori 應用 Manage Workflows for Purchase RequisitionsUI
- 在 Microsoft Dynamics NAV 2018 Setting Up and Using a Purchase Approval WorkflowROSAPP
- SAP MM Purchase Order History CategoryGo
- SAP Workflow常用TCODE
- An introduction to SAP Business Workflow
- SAP MM Return Purchase Order之使用
- SAP Fiori Launchpad url 引數 sap-app-origin-hint 的含義APP
- SAP Fiori + Vue = ?Vue
- SAP Workflow Tcodes ( Transaction Codes )
- SAP Fiori 簡介
- SAP Fiori Elements 概述
- My suggestions on SAP ABAP transformationORM
- SAP Fiori應用索引大全工具和 SAP Fiori Tools 的使用介紹索引
- 如何分辨 SAP Fiori Launchpad 裡的真假 Fiori 應用
- SAP Fiori應用索引大全索引
- 什麼是 SAP Fiori Tools
- In-App Purchase 內購丟單、串單處理APP
- SAP Fiori image的顯示原理
- 如何評價 SAP Fiori Design Guidelines?GUIIDE
- SAP Fiori 的 UI 新主題 HorizonUI
- SAP CRM Fiori應用和SAP JAM的整合配置
- 雲音樂中 In-App Purchase 實踐總結篇APP
- SAP Fiori 的學習路線指南
- SAP Fiori 的附件處理(Attachment handling)
- SAP CRM Fiori 應用 My Opportunity 的分頁讀取邏輯,在 GM4 - AG3 無法正常工作Unity
- 如何在 SAP BTP 上 手動執行 workflow
- SAP BTP 上 workflow 和 Business Service 的 project 管理Project
- SAP Fiori Elements 應用裡和 Fiori 3 相關的外觀設定
- Fiori Fundamentals和SAP UI5 Web ComponentsUIWeb
- SAP Fiori應用的搜尋問題
- SAP Fiori裡的兩種鎖機制
- SAP Fiori SSL 和 SAML 2.0 配置文件
- SAP CRM Fiori Simulation Pipeline 設計介紹
- SAP Cloud Application Programming bookshop 例子的 Fiori PreviewCloudAPPView
- SAP Fiori Elements controller extension 的載入原理Controller
- 如何讓SAP S/4HANA的Material Fiori應用配置到Fiori Launchpad裡
- SAP Cloud for Customer和SAP Fiori系統裡的OData測試工具Cloud