eXXcellent solutions tech blog logoabout

SAP Fiori Notifications – Part 2

Cover Image for SAP Fiori Notifications – Part 2
Posted
by Igor Muntoreanu

In this blog we will explain how to connect SAP Fiori Notifications with the classic SAP Workflow. We will explain it along with a Real Business Scenario Case in a step-by-step example with the entire code snippets.

Prerequisites

To such functionality work, you need before do the customizing of the Fiori Notification. To do so, follow the steps discussed in the Part 1 of this blog: https://www.exxcellent.de/tech-blog/2021-01-25-sapfiori_notifications1/SAP-Fiori-Notifications-Part-1/

Real Business Case Scenario 3

In this Scenario we are going to send the Fiori Notification based on the SAP Workflow result that we are going to create from scratch. The scope: everytime a Purchase Order is created or changed in transaction ME21N/ME22N with price bigger or equal to 10.000 EUR a Fiori Notification will be sent to the Head of Department. Then, the Head of Department is going to approve or reject the Purchase Order. When it is rejected, the Purchase Order items will be blocked. Below we have a big picture of the flow of the process, which is going to start once we create or change a Purchase Order with price over 10.000 EUR:

An image shows a picture of the Workflow Process;

To do this, first we need to create a custom workflow for this criteria. First, lets create a Purchase Order via transaction ME21N and trace the workflow Object Types in background. Before saving the Purchase Order lets activate the workflow trace trough SWELS transaction and check the results:

An image shows a the trace of Workflow switched on;

An image shows a created purchase order in ME21n;

Checking the trace in transaction SWEL, we can see that the following Object Types were called:

An image shows the trace log in SWEL;

Now let’s change the Purchase Order to 10.000 EUR via ME22n and check the trace again:

An image shows the changing in the PO;

Trace:

An image shows the workflow trace;

We have then the Business Object BUS2012 as the main Object Type for the Purchase Orders. Another way to find the correct Object Type, would be by the transaction itself:

An image shows the navigation in ME23n;

An image show the result of the navigation;

Now, copy this Object Type and create a custom Object Type via transaction SWO1. Use the BUS2012 as the super type: SWO1:

An image shows the SWO1 transaction;

An image shows the creation of the Object Type;

In the next screen make sure to always change the release status of the Object Type and Object Type Component to „implemented“ after you change or create something. Set the release status to „released“ when you want to transport it to the test system or production system.

Create a new event in the object type Z_PO_FIORI:

An image shows the creation of an event;

Then update the status to implemented:

An image shows the change of status to implemented;

Now lets create 3 new attributes like below:

An image shows the creation of attributes;

An image shows the creation of attributes;

An image shows the creation of attributes;

An image shows the creation of attributes;

Now lets code the „GET“ of these attributes, by clicking in „Program“:

CreatedBy:

get_property createdby changing container. SELECT SINGLE ernam FROM ekko INTO @DATA(lv_ernam) WHERE ebeln = @object-key-purchaseorder. IF sy-subrc = 0. SELECT SINGLE adrp~name_text INTO @DATA(lv_name_text) FROM usr21 JOIN adrp ON usr21~persnumber = adrp~persnumber WHERE usr21~bname = @lv_ernam. ENDIF. object-createdby = lv_name_text. swc_set_element container 'CreatedBy' object-createdby. end_property.

FullPrice:

get_property fullprice changing container. DATA: lv_fullprice TYPE ekpo-netwr. SELECT a~ebeln, a~ebelp, a~netwr, a~menge, b~waers, b~ernam FROM ekpo AS a INNER JOIN ekko AS b ON ( a~ebeln = b~ebeln ) INTO TABLE @DATA(lt_ekpo) WHERE a~ebeln = @object-key-purchaseorder. IF sy-subrc = 0. LOOP AT lt_ekpo ASSIGNING FIELD-SYMBOL(<fs_ekpo>). lv_fullprice = <fs_ekpo>-netwr + lv_fullprice. ENDLOOP. swc_set_element container 'FullPrice' lv_fullprice. ENDIF. end_property.

The Currency is automatically inherited from object type BUS2012, so it is not necessry to do a customizing code for it.

Now, delegate the BUS2012 to our Object Type Z_PO_FIORI trough transaction SWO1:

An image shows delegation in SWO1;

To send the notification to the Head of Department, we need to create a Rule. This rule will get the workflow initiator, in other words, the user who created or changed the Purchase Order and then get the Head of Department, who is responsible for this user. For example:

An image shows the Organization Unit Hierarchy;

If the user Anja or Derick, create or change a Purchase Order over 10.000, then the Head of Department Igor will receive a notification to approve or reject the Purchase Order. We create the rule through transaction PFAC:

An image shows the rule;

Create a new import element called "Initiator" in the Container tab:

An image shows the element in the Container;

Now lets code the Function Module "Z_RULE_PO_PRICE":

An image shows the function module tables;

An image shows the function module exceptions;

FUNCTION z_rule_po_price. *"---------------------------------------------------------------------- *"*"Local Interface: *" TABLES *" ACTOR_TAB STRUCTURE SWHACTOR *" AC_CONTAINER STRUCTURE SWCONT *" EXCEPTIONS *" NOBODY_FOUND *"---------------------------------------------------------------------- INCLUDE <cntn01>. DATA: lt_objec TYPE STANDARD TABLE OF objec, lv_initiator TYPE wfsyst-initiator, lv_user TYPE sy-uname. swc_get_element ac_container 'Initiator' lv_initiator. lv_user = lv_initiator+2. "The Notification will be sent only to the Head of the Department "Get the Organization Unit CALL FUNCTION 'RH_STRUC_GET' EXPORTING act_otype = 'O' act_objid = '50000077' " eXXcellent Solutions Organization Unit act_wegid = 'AI_ORGUS' " All users of the Organization Unit TABLES result_objec = lt_objec EXCEPTIONS no_plvar_found = 1 no_entry_found = 2 OTHERS = 3. IF sy-subrc = 0. "Send notification only to the Head of Department READ TABLE lt_objec TRANSPORTING NO FIELDS WITH KEY objid = '50000079'. IF sy-subrc = 0. DATA(lv_tabix) = sy-tabix + 1. TRY. DATA(ls_object) = lt_objec[ lv_tabix ]. CHECK lv_user <> ls_object-realo. "Avoid the Head of sending the notification to himself READ TABLE lt_objec TRANSPORTING NO FIELDS WITH KEY realo = lv_user. "Make sure that the user is in the Organization Unit IF sy-subrc = 0. actor_tab-otype = 'US'. actor_tab-objid = ls_object-realo. APPEND actor_tab. ENDIF. CATCH cx_sy_itab_line_not_found. ENDTRY. ENDIF. ENDIF. IF actor_tab IS INITIAL. RAISE nobody_found. ENDIF. ENDFUNCTION.

Now lets simulate the rule:

An image shows the rule simulation;

As we can see, it is working.

Now enter in the Workflow Builder through PFTC or SWDD and lets create the flow. The flow is going to have a task where the user will decide to approve or not the Purchase Order:

An image shows the workflow builder;

An image shows the Task maintenance;

Create the element "PurchaseOrder" in the container and link it to the Object Type "Z_PO_FIORI":

An image shows the creation of the element;

Do the connection between the Task and the Object type at the "Triggering Events" Tab:

An image shows the triggering events tab;

Navigate to the workflow builder through the button at the „Basic data“ tab or through the transaction SWDD:

An image shows the workflow builder;

Click on the Header, through the Hat button and configure the Start Events like below:

An image shows the workflow builder header;

Do the Binding:

An image shows the Binding;

Now lets create a User Decision task:

An image shows the creation of a User Decision Task;

An image shows the actual flow;

Give it a Title, set the parameters, give the rule created and write the decision options:

An image shows the set up of the decision task;

Now do the Binding between the Workflow and the Rule:

An image shows the binding;

Go to the Control tab and do the automatic Binding between the Workflow and the Task:

An image shows automatic binding;

Now, go to the Notification tab, give the Workflow initiator as the Recipient of Completion Message and click on the Push Notification:

An image shows the notification tab;

An image shows push notification;

Now, fill the Actions and Texts. Save it:

An image shows action and text texts;

An image shows action and text texts;

Now we need to create 2 Tasks, one to approve and another one to reject the Purchase Order:

An image shows the creation of Tasks;

Reject Task:

An image shows the creation of rejection Task;

Enter in the task and fill the Basic data:

An image shows the Basic Data;

The method "BLOCKPO" should be created in the Object Type Z_PO_FIORI in transaction SWO1:

An image shows the BLOCKPO method creation;

In this Method we will block the Purchase Order:

begin_method blockpo changing container. DATA: lt_poitem TYPE STANDARD TABLE OF bapimepoitem, lt_poitemx TYPE STANDARD TABLE OF bapimepoitemx, ls_poitemx TYPE bapimepoitemx, lt_return TYPE STANDARD TABLE OF bapiret2. CALL FUNCTION 'BAPI_PO_GETDETAIL1' EXPORTING purchaseorder = object-key-purchaseorder TABLES return = lt_return poitem = lt_poitem. LOOP AT lt_return TRANSPORTING NO FIELDS WHERE type CA 'EAX'. DATA(lv_error) = abap_true. ENDLOOP. IF lv_error IS INITIAL. LOOP AT lt_poitem ASSIGNING FIELD-SYMBOL(<fs_poitem>). <fs_poitem>-delete_ind = 'S'. "Block ls_poitemx-po_item = <fs_poitem>-po_item. ls_poitemx-po_itemx = abap_true. ls_poitemx-delete_ind = abap_true. APPEND ls_poitemx TO lt_poitemx. CLEAR ls_poitemx. ENDLOOP. REFRESH lt_return. CALL FUNCTION 'BAPI_PO_CHANGE' EXPORTING purchaseorder = object-key-purchaseorder TABLES return = lt_return poitem = lt_poitem poitemx = lt_poitemx. LOOP AT lt_return TRANSPORTING NO FIELDS WHERE type CA 'EAX'. lv_error = abap_true. ENDLOOP. IF lv_error IS INITIAL. CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'. ENDIF. ENDIF. end_method.

Fill the Completion Text as below:

An image shows the Completion Text;

Create an import element in the Container:

An image shows the Creation of an import element;

Save it, return to the Task in SWDD and do the binding as below:

An image shows the Creation of the Binding;

Fill the Recipient of Completion Message:

An image shows Recipient of Completion Message;

Now, we must do the same steps as above for the Approve task:

An image shows the steps for the Approve Task;

An image shows the steps for the Approve Task;

Code for the "APPROVEPO" Method:

begin_method approvepo changing container. DATA: lt_poitem TYPE STANDARD TABLE OF bapimepoitem, lt_poitemx TYPE STANDARD TABLE OF bapimepoitemx, ls_poitemx TYPE bapimepoitemx, lt_return TYPE STANDARD TABLE OF bapiret2. CALL FUNCTION 'BAPI_PO_GETDETAIL1' EXPORTING purchaseorder = object-key-purchaseorder TABLES return = lt_return poitem = lt_poitem. LOOP AT lt_return TRANSPORTING NO FIELDS WHERE type CA 'EAX'. DATA(lv_error) = abap_true. ENDLOOP. IF lv_error IS INITIAL. LOOP AT lt_poitem ASSIGNING FIELD-SYMBOL(<fs_poitem>). <fs_poitem>-delete_ind = space. " Free ls_poitemx-po_item = <fs_poitem>-po_item. ls_poitemx-po_itemx = abap_true. ls_poitemx-delete_ind = abap_true. APPEND ls_poitemx TO lt_poitemx. CLEAR ls_poitemx. ENDLOOP. REFRESH lt_return. CALL FUNCTION 'BAPI_PO_CHANGE' EXPORTING purchaseorder = object-key-purchaseorder TABLES return = lt_return poitem = lt_poitem poitemx = lt_poitemx. LOOP AT lt_return TRANSPORTING NO FIELDS WHERE type CA 'EAX'. lv_error = abap_true. ENDLOOP. IF lv_error IS INITIAL. CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'. ENDIF. ENDIF. end_method.

Fill the Completion Text as below:

An image shows the Completion Text;

Create the import element in the task Container:

An image shows creation of import element;

Do the Binding:

An image shows the Binding;

Fill the Recipient of Completion Message:

An image shows the Recipient of Completion Message;

Finally as a final step, we need to call the workflow event through abap code. But where to do it exactly? Well, when we save the Purchase Order in the transaction ME21N or ME22N. That’s why we have chosen the BADI "ME_PURCHDOC_POSTED" , if you want to learn more about this step and how to find the correct BADI for your process, please follow the link: https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=133758980

Implement the BADI "ME_PURCHDOC_POSTED" through se18 transaction:

An image shows the BADI implementation;

An image shows the BADI implementation;

An image shows the BADI implementation;

An image shows the BADI implementation;

Code the method like below and activate the code. Note that here we will call our workflow event only when the total price of the Purchase Order is equal or more expensive than 10.000.

METHOD if_ex_me_purchdoc_posted~posted. DATA: lv_ebeln TYPE swr_struct-object_key, lv_total_price TYPE ekpo-netwr, lt_msg_lines TYPE STANDARD TABLE OF swr_messag. LOOP AT im_ekpo ASSIGNING FIELD-SYMBOL(<fs_ekpo>). lv_total_price = lv_total_price + <fs_ekpo>-netwr. ENDLOOP. CHECK lv_total_price >= 10000. lv_ebeln = im_ekko-ebeln. CALL FUNCTION 'SAP_WAPI_CREATE_EVENT' EXPORTING object_type = 'Z_PO_FIORI' object_key = lv_ebeln event = 'SENDNOTIFICATION' TABLES message_lines = lt_msg_lines. ENDMETHOD.

Testing

With the user ABC, we are going to create a Purchase Order through ME21N transaction with a price over 10.000 EUR:

An image shows the creation of a Purchase Order in ME21n;

Notification in transaction SBWP from the Head of Department:

An image shows the notification in SBWP;

Notification in Fiori Launchpad from the Head of Department:

An image shows the notification in the Fiori Launchpad;

Now lets press Reject and check what happens with the Purchase Order:

An image shows the Rejection button clicked;

As we can see, the Purchase Order was blocked:

An image shows the Rejection button clicked;

Then the user who created or changed the Purchase Order, receive also a notification of Completion:

An image shows the Completion notification;

We could also send a Fiori Notification as feedback instead of only an entry in SBWP. To do it, you can use what you learn in the Part 1 of this Blog Post and implement it inside the Task „Approve“ and „Reject“. Since this 2 methods are executed in background, then you need to send the notification manually by calling the method: /iwngw/cl_notification_api=>create_notifications And by configuring a new Notification Channel Provider in the SPRO transaction.

Conclusion

As we can see the SAP Fiori Notification is very well integrated with the classic SAP Workflow and both technologies complement each other.

Image sources

The cover image used in this post was created by Unsplash under the following license. All other images on this page were created by eXXcellent solutions under the terms of the Creative Commons Attribution 4.0 International License