SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

FIORI My Inbox App – Custom Purchase Requisition Workflow

Requirements and Corresponding Solutions.

One of our clients has a requirement to use FIORI My Inbox App 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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

 

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

Processor field will now populate dynamically as shown below:

SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

Work item in SBWP would look like :

SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

Email going to approver would look like :

SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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

SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

Both Task should have same decision keys.

SAP Fiori for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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 for SAP S/4HANA , SAP Business Workflow, SAP Fiori

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.

Leave a Reply

Your email address will not be published. Required fields are marked *