Skip to Content
Technical Articles

Add an Approval Workflow to your Custom Business Coding in SAP S/4HANA Cloud

Use Case

In this post I would like to give you an overview on the steps you need to setup a hybrid extension scenario with In-App and Side-by-Side Extensions for SAP S/4HANA Cloud. We have provided this scenario also as Mini Code Jam on the TechEd 2019 as session CAA604, where a detailed step-by-step guide is available.

The use case shown here is a Bonus Plan build as a Custom Business Object in SAP S/4HANA where you trigger an approval workflow for the manager on the SAP Cloud Platform.

The Custom Business Object and its ABAP Business Logic is developed as an In-App Extension running on the SAP S/4HANA stack, whereas the Workflow is running on the SAP Cloud Platform.

The Bonus Plan app shown in the exercises is for educational purpose only and not meant for productive usage.
It is not ensured that the data used in this extension scenario is managed in accordance with applicable legal requirements for all countries and industries and in accordance with the business needs, such as data life cycle requirements.

First of all let’s have a look at the objects on the SAP S/4HANA side, you need to have for this scenario:

  1. The definition of the bonus plan with a UI and business logic consisting of
    • A Custom Business Object, where you can define the bonus plan, request the approval from the manager and finally perform the calculation of the bonus.
    • A Custom Catalog Extension to link the tile of the bonus plan app to a certain Fiori catalog, which implicitly also defines the authorization you need for the app.
    • A Custom Reusable Elements library for the manager determination
  2. The setup of the communication (inbound and outbound) between SAP S/4HANA and the SAP Cloud Platform consisting of
    • A Custom Communication Scenario which defines the
      • inbound OData service of the bonus plan object
      • outbound call of the Workflow service on the SAP Cloud Platform
    • A technical Communication User for the inbound call
    • A Communication System, where you define the host name of the workflow on the SAP Cloud Platform and the inbound and outbound communication users.
    • A Communication Arrangement, which brings together the scenario, users and system.
  3. To easily access the Workflow Inbox, running on the SAP Cloud Platform we have created a Custom Tile in the Fiori Launchpad of the SAP S/4HANA.

On the SAP Cloud Platform you need:

  1. The enabling and configuration of the
    • Workflow Service
    • WebIDE
    • Portal Service
  2. A SAPUI5-UI project as UI for the Workflow inbox
  3. A Workflow definition
  4. A Destination to the SAP S/4HANA
  5. A technical communication user

Let’s go step by step through these points, where I will show you how to create the corresponding objects, what coding is necessary or how to setup and configure certain features.

SAP S/4HANA Cloud

Let’s start on the SAP S/4HANA Cloud side with the definition of the Custom Business Object and the communication settings.

Custom Business Object

As a prerequisite your user must have a business role assigned, which contains the business catalogs SAP_CORE_BC_EXT and SAP_CORE_BC_EXT_TST. For the communication settings you need in addition the business catalog SAP_CORE_BC_COM.

Start the app Custom Business Objects from the Extensibility group in your Fiori Launchpad.

There you have to create the Custom Business Object BonusPlan with the root node BONUSPLAN and a subnode Products.  As Features Determination and Validation, UI Generation and Service Generation have to be selected.

Switch to the Fields tab. On the BONUSPLAN node we need the following fields:

The Release Status is linked to the following Custom Code List:

On the subnode Products of the Custom Business Object BonusPlan we have the two text fields Product ID (Text 20) and Product Description Text 255).

With these settings you have defined the data structure or the Custom Business Object out of which the corresponding database tables and CDS Views will be created during Publish.

But before you publish the object you have to define the business logic (Actions). Switch to the Logic tab, where you find already the events Before Save and After Modification.

Additionally we need the two Actions Calculate and Request Approval which you have to create here.

In this blog we will focus on the Request Approval action, which triggers the workflow on the SAP Cloud Platform.

You have to publish the object now to generate all the backend objects like tables, CDS views, classes, services or UI, before you can start your coding.

After publishing you also have to create the Fiori tile via the link Maintain Catalog Extension on the General Information tab.

For additional information on this topic and also on the calculation logic, please check out the tutorials on Custom Business Objects ( https://blogs.sap.com/?p=391857 ).

 

You will find the tile with the generated Fiori Elements UI in the extended catalog, or if you follow the link Go to Generated UI on the General tab of the Custom Business Object.

In this BonusPlan UI you can create now a bonus plan instance:

Let’s have a look at the Request Approval action and add there some coding.

Go back to you Custom Business Object and open the ABAP editor for the Request Approval action:

Initially the action is empty, so you have to add the following coding.

Start after some declarations with the central TRY block, where at the beginning the manager of the employee is determined. This coding is stored in the Custom Library yy1_business_partners. You can find the coding in the appendix of this blog.

* Action RequestApproval for Node ID bonusplan
*
* Importing Parameter : association (Navigation to Parent/Child/Associated Node Instances)
*                       write (API for creating and updating Custom Business Object Node Instances)
* Changing Parameter  : bonusplan (Current Node Data)
* Exporting Parameter : message (Message with Severity S(uccess), W(arning), E(rror))

 DATA: iv_input  TYPE string.
 DATA: cv_output TYPE string.
 DATA: msg_body  TYPE string.
 DATA: token     TYPE string.
 DATA: sRank     TYPE string.

 TRY.
    DATA(lv_bupa)         = yy1_business_partners=>get_bp_for_user( bonusplan-employeeid ).
    DATA(lv_manager_id)   = yy1_business_partners=>get_current_manager( lv_bupa ).
    DATA(lv_manager_user) = yy1_business_partners=>get_user_for_id( lv_manager_id ).
    DATA(lv_manager_name) = yy1_business_partners=>get_name_for_id( lv_manager_id ).

The next part is the outbound service handling, where a http request is send to the Workflow on the SAP Cloud Platform.

You need to create an http-client based on a Communication Scenario and a Communication Arrangement, which you will create later.

Using this http-client you then can prepare and send out a GET-request to receive the XSRF-Token you need to trigger the workflow.

If you need some more information from the requests you can use the tracing feature, which is part of the extensibility in SAP S/4HANA.

    DATA(lo_client) = cl_ble_http_client=>create(
        communication_scenario = 'YY1_BPWORKFLOW'
        outbound_service       = 'YY1_BPAPPROVAL_REST'
    ).

    DATA(request) = cl_ble_http_request=>create( ).

    request->set_method( 'GET' ).
    request->set_resource_extension( '/xsrf-token' ).
    request->set_body( data = '' ).
    request->set_header_parameter(
      EXPORTING
        name    = 'X-CSRF-Token'
        value   = 'Fetch'
    ).

    DATA(response) = lo_client->send( request ).


*   cl_ble_trace_writer=>write_info_message( response->get_body( ) ).

    token = response->get_header_parameter( name = 'x-csrf-token' ).

*   cl_ble_trace_writer=>write_info_message( token ).

The last part is the preparation of the POST request and some error handling.

The message body contains the structure you will define for the workflow (we come to that later) with the employee, manager and bonus data.

To send back the approval decision of the manager from the SAP Cloud Platform, you need the guid of this bonusplan instance for the PATCH request. Since this is not an importing parameter for this action, you have to select it using the CDS-View of the bonusplan object.

    request->set_method( 'POST' ).
    request->set_resource_extension( '/workflow-instances' ).
    request->set_header_parameter(
      EXPORTING
        name    = 'X-CSRF-Token'
        value   = token
    ).
    request->set_header_parameter(
      EXPORTING
        name    = 'Content-Type'
        value   = 'application/json'
    ).

    SELECT SINGLE from yy1_bonusplan FIELDS sap_uuid
    WHERE id = @bonusplan-id
    INTO @DATA(uuid).

    new cl_system_uuid( )->convert_uuid_x16_static(
       EXPORTING
          uuid     = uuid
       IMPORTING
          uuid_c36 = data(lv_c36) ).


    msg_body =
        '{ "definitionId": "bonusplan_wf", "context": { '             &&
            '"id": "'               && bonusplan-id                   && '",' &&
            '"suid": "'             && lv_c36                         && '",' &&
            '"manager": "'          && lv_manager_user                && '",' &&
            '"employeeId": "'       && bonusplan-employeeid           && '",' &&
            '"employeeName": "'     && bonusplan-employeename         && '",' &&
            '"targetAmount": "'     && bonusplan-targetrevenue_v      && '",' &&
            '"targetAmount_C": "'   && bonusplan-targetrevenue_c      && '"'  &&
         '} }'.

    request->set_body( data = msg_body ).

    response = lo_client->send( request ).

*   cl_ble_trace_writer=>write_info_message( msg_body ).

    cv_output = response->get_body( ).

    message = VALUE #(
      severity = co_severity-success
      text     = | 'Approval from ' { lv_manager_user } - { lv_manager_name } | ).


  CATCH cx_ble_http_exception INTO DATA(lx).
     cl_ble_trace_writer=>write_info_message( lx->get_text( ) ).
     cl_ble_trace_writer=>write_info_message( conv #( lx->status_code ) ).

     message = VALUE #(
       severity = co_severity-error
       text     = lx->get_text( ) ).

 ENDTRY.

Custom Communication Scenario

The setup of the communication (inbound and outbound) between SAP S/4HANA and the SAP Cloud Platform starts with the definition of a Custom Communication Scenario.

The Custom Communication Scenario contains the information for both inbound and outbound http calls.

Open the app Custom Communication Scenarios from the Extensibility group and create a new one with the name YY1_BPWORKFLOW (same as in the ABAP coding of the action).

On the tab Inbound Services you have to create an entry for your Custom Business Object:

On the Outbound Services tab you have to create an entry for the workflow service on the SAP Cloud Platform with the URL-Path  /workflow-service/rest/v1

The Outbound ID must be the same as in the ABAP coding of the action, in our case YY1_BPAPPROVAL_REST.

Communication User

If you want to access the Bonusplan OData service (Inbound) you need also to setup an authorization method. You can use several methods like e.g. Basic Authentication, Certificate or OAuth.

We are choosing here in this example Basic Authentication for which you need to setup a Communication User.

Open the app Maintain Communication Users from the Communication Management group and create there a new users with user name, description and password.

Communication System

In the Custom Communication Scenario you have defined the relative URL-part of the service, the system, where this service runs, is defined as a Communication System.

Open the app Communication Systems from the Communication Management group and create there a new one with ID (e.g. CAA604), name and a host name.

The host name points to the workflow service. As soon as you have enabled the Workflow service there (see the corresponding chapter later in this blog), a Destination is created with the name bpmworkflowruntime. Copy the URL of this destination (without the https://) into the Host Name field.

In addition to the host name you have to define the users for inbound and outbound communication.

Add in the section Users for Inbound Communication the Communication User you just have created with the method User ID and Password, and in the section Users for Inbound Communication a SAP Cloud Platform users with the WorkflowInitiator role assigned (see the workflow setup chapter later in this blog)

Communication Arrangement

As the last part the Custom Communication Arrangement brings all these previously created information together. It contains the information for both inbound and outbound http calls.

Open the app Communication Arrangements from the Communication Management group and create there a new one based on the previously created Custom Communication Scenario YY1_BPWORKFLOW.

Add the name of the Communication System, all other information will be filled in the automatically.

 

With that you have completed the SAP S/4HANA Cloud side.

SAP Cloud Platform

The next step is the enabling and configuration of the SAP Cloud Platform services

  • Workflow Service
  • WebIDE
  • Portal Service

For the configuration of the SAP Cloud Platform parts you have to switch to the SAP Cloud Platform Cockpit. The following description is based on the Neo development environment and the usage of the SAP ID service.

Workflow Service Configuration

In the Services menu search for the Workflow tile.

If the service is not enabled yet, you have to enable it. After you have enabled the service you can find the destination bpmworkflowruntime which you need for the Communication System.

Click on the Workflow tile and select Configure Service > Roles

Assign the following roles to your user:

  • WorkflowInitiator
  • WorkflowAdmin
  • WorkflowMessageSender
  • WorkflowViewer
  • WorkflowDeveloper
  • WorkflowTenantOperator
  • WorkflowContextAdmin
  • WorkflowParticipant
  • WorkflowContextViewer

WebIDE Configuration

In the Services menu search for the WebIDE Full Stack tile.

If the service is not enabled yet, you have to enable it.

Click on the WebIDE tile and select Configure Service > Roles

Assign the following roles to your user:

  • DIDeveloper

Portal Service Configuration

In the Services menu search for the Portal tile.

If the service is not enabled yet, you have to enable it.

Click on the Portal tile and select Configure Service > Roles

Assign the following roles to your user:

  • TENANT_ADMIN
  • WEB_CONTENT_EDITOR

After that select Go to Service in the Portal tile  and select there the menu item Site Directory.

Select Edit for Default in the Site Directory. In the configuration of your Default Site select Content Management > Catalogs and assign the catalog Workflow to the role Everyone.

Back on the Site Directory, the Site has to be published. Check then that the site is Set as default.

Create a SAPUI5-UI project as UI for the Workflow inbox

I describe here the usage of a SAPUI5-UI for the workflow UI-task. Alternatively you can create a simple form based UI directly from the workflow definition, which is less flexible than the SAPUI5 variant.

Select Go to Service from the WebIDE tile and create a new SAPUI5 Application with the project name BonusPlanWorkflow, the name space bpwf and an initial XML view ApproveBonusPlan via the WebIDE-menu File > New > Project from Template.

Adopt the UI so that it shows the fields which you have defined in the message body in the RequestApproval action before.

 

As next step add in the init function of the component.js the approve and reject handling:

sap.ui.define([
	"sap/ui/core/UIComponent",
	"sap/ui/Device",
	"bpwf/BonusPlanWorkflow/model/models"
], function (UIComponent, Device, models) {
	"use strict";

	return UIComponent.extend("bpwf.BonusPlanWorkflow.Component", {

		metadata: {
			manifest: "json"
		},

		/**
		 * The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
		 * @public
		 * @override
		 */
		init: function () {
			// call the base component's init function
			UIComponent.prototype.init.apply(this, arguments);

			// enable routing
			this.getRouter().initialize();

			// set the device model
			this.setModel(models.createDeviceModel(), "device");

			// get task data
			var startupParameters = this.getComponentData().startupParameters;
			var taskModel = startupParameters.taskModel;
			var taskData = taskModel.getData();
			var taskId = taskData.InstanceID;
			
			// initialize model
			var contextModel = new sap.ui.model.json.JSONModel("/bpmworkflowruntime/rest/v1/task-instances/" + taskId + "/context");
			contextModel.setDefaultBindingMode(sap.ui.model.BindingMode.OneWay);
			this.setModel(contextModel);			

	    	//add actions
            startupParameters.inboxAPI.addAction({
                    action: "Approve",
                    label: "Approve"
            }, function(button) {
                    this._completeTask(taskId, true);
            }, this);
            startupParameters.inboxAPI.addAction({
                    action: "Reject",
                    label: "Reject"
            }, function(button) {
                    this._completeTask(taskId, false);
            }, this);

		},

		_completeTask: function(taskId, approvalStatus) {
			var token = this._fetchToken();
			$.ajax({
				url: "/bpmworkflowruntime/rest/v1/task-instances/" + taskId,
				method: "PATCH",
				contentType: "application/json",
				async: false,
				data: "{\"status\": \"COMPLETED\", \"context\": {\"approved\":\"" + approvalStatus + "\"}}",
				headers: {
					"X-CSRF-Token": token
				}
			});
			this._refreshTask(taskId);
		}
	
		, _fetchToken: function() {
			var token;
			$.ajax({
				url: "/bpmworkflowruntime/rest/v1/xsrf-token",
				method: "GET",
				async: false,
				headers: {
					"X-CSRF-Token": "Fetch"
				},
				success: function(result, xhr, data) {
					token = data.getResponseHeader("X-CSRF-Token");
				}
			});
			return token;
		},
	
		_refreshTask: function(taskId) {
			this.getComponentData().startupParameters.inboxAPI.updateTask("NA", taskId);
		}

	});
});

You can then deploy the UI.

Create a Destination to the SAP S/4HANA

The destination CAA604 which you have entered in the SendApproval task needs to be defined as a destination in the SAP Cloud Platform Cockpit.

Select the menu Connectivity > Destinations and create a new destination with the name CAA604, the URL of your SAP S/4HANA Cloud tenant and User and Password of the Communication User you have created before. Authentication is BasicAuthentication and ProxyType is Internet.

As URL you must take the host name from the Service URL in the Communication Arrangement, e.g. http://my3xxxxx-api.s4hana.ondemand.com.

Create a Workflow definition

Before you can define the workflow, you have to enable the WebIDE for workflow. Click on the Preferences menu icon and select then Extensions.

Search for Workflow and switch on the Workflow Editor.

You can go back to the Development section and create a new workflow project.

Select File > New > Project from Template and switch there the Category to Business Process Management. Now you can see the Workflow Project tile.

Enter BonusPlan_WF as Project Name and as Name. The name you enter here must correspond in lower case to the definition_id, which you have entered in the message body in the RequestApproval action before.

Initially the workflow has only a start and an ed event.

Add a User Task ApproveBonusPlan, a Script Task SetBody and a Service Task SendApproval.

Click on ApproveBonusPlan and enter ${context.manager} as recipient User on the DETAILS tab. With context you address the fields of the message body you have sent to the workflow.

On the USER INTERFACE tab you link the SAPUI5 UI you have defined before to the workflow. Enter bonusplanworkflow as HTML5 App Name and bpwf.BonusPlanWorkflow as SAPUI5 Component.

You could create here as already mentioned the form based UI as an alternative to the described SAPUI5 UI. For that you must switch the type to Form, and define the form, the used fields and the decisions.

 

As next step you have to configure the script task.

Click on the SetBody task. Here you have to map the approval decision to the defined values of the Custom Business Object:

// read from existing workflow context 
if ($.context.approved === "true") {
    $.context.response = { ReleaseStatus: "02" };
}
else {
    $.context.response = { ReleaseStatus: "05" };
}

If you would have refered to a form based UI in the user task before, you have to change the condition in the if-statement to $.usertask.usertasks1.last.decision === “approve” .

 

As last step you have to send back the decision to the OData service of the Custom Business Object in SAP S/4HANA Cloud.

Click on the SendApproval task and enter the following parameter:

Destination: CAA604

Path: /sap/opu/odata/sap/YY1_BONUSPLAN_CDS/YY1_BONUSPLAN(guid’${context.suid}’)

HTTP-Method: PATCH

Path to XSRF-Token: /sap/opu/odata/sap/YY1_BONUSPLAN_CDS

Request Variable: ${context.response}

Response Variable: ${context.s4.response}

Select in the WebIDE project tree the file BonusPlan_WF.workflow and deploy it to the SAP Cloud Platform Workflow.

Create a technical communication user

Similar to the Communication User on the SAP S/4HANA Cloud side we need a user on the SAP Cloud Platform, which can be used for the authorization of the workflow POST request.

This user must be added to the Communication System on the SAP S/4HANA Cloud side as outbound user. We use here also BasicAuthentication.

Create this user (in our example P019421) and assign the role WorkflowInitiator in the Services section of the SAP Cloud Platform Cockpit on the Workflow tile > Configure Service > Roles.

End to End Test

After finalizing all configuration steps you can test the scenario end to end.

Start the test on the Fiori UI of the Bonusplan, where you already have created a Bonus Plan instance. which should still have the Release Status Not Released.

Trigger the approval workflow via the button Request Approval. This sends out the http-request to the SAP Cloud Platform.

You can see the result on the workflow inbox tile, where the counter is increased by one.

You can reach this UI via the Services section of the SAP Cloud Platform Cockpit and there on the Workflow tile, where you have to follow the link Fiori Launchpad (Default Site).

If you want to simplify the access to this UI, you can also imbed a Custom Tile in the Fiori Launchpad of the SAP S/4HANA Cloud, like you see it in the first screenshot of this blog.

This can be defined via the app Custom Tiles in the Extensibility group. There you just have to enter the URL, an icon and the catalog (in our example Extensibility) where the tile should show up.

If you click on the My Inbox tile, you can see the UI you have defined before with the data you have sent in the message body to the workflow service (workflow context).

If you click on the Monitor Workflow – Workflow Instances tile, you can see all information of this instance:

Back on the SAP S/4HANA Cloud side you have to refresh the BonusPlan UI or start the app again.

There you see the changed Release Status which is now Released.

 

With this example you have seen how to build a hybrid extension scenario between SAP S/4HANA Cloud and the SAP Cloud Platform.

But you can not only implement manager approvals like this, but also automated scenarios, where you trigger the creation of other (standard) Business Object instances (e.g. Purchase Orders, Sales Orders) of SAP S/4HANA Cloud or send updates to other objects.

 

Appendix: Custom Libraries

In the coding of the RequestApproval action I have referred to the Custom Library yy1_business_partners for the manager determination.

You can find the code snippets of these libraries and their methods here.

 

Business Partners

 

GET_BP_FOR_USER:

select single from I_businessuser
       fields BusinessPartner
       where BPIdentificationNumber = @IV_USER
       into @data(lv_bupa).

* cl_ble_trace_writer=>write_info_message( conv #( lv_bupa ) ).

RV_BP = lv_bupa.

 

GET_CURRENT_MANAGER:

data(lv_current_wa)         = yy1_work_agreements=>get_current_wa_for_bp( iv_bp ).

data(lv_current_manager_wa) = yy1_work_agreements=>get_current_manager_wa_for_wa( lv_current_wa ).

rv_manager_bp = yy1_work_agreements=>get_bp_for_wa( lv_current_manager_wa ).

 

GET_USER_FOR_ID:

  select single from I_businessuser
       fields BPIdentificationNumber
       where BusinessPartner = @IV_BP
       into @data(lv_mgr_empid).

*    cl_ble_trace_writer=>write_info_message( conv #( lv_mgr_empid ) ).

  RT_USER = lv_mgr_empid.

 

GET_NAME_FOR_USER:

  SELECT SINGLE FROM i_businessuser
       FIELDS personfullname
       WHERE bpidentificationnumber = @iv_bu
       INTO @DATA(lv_personfullname).

* cl_ble_trace_writer=>write_info_message( CONV #( lv_personfullname ) ).

  rt_name = lv_personfullname.

 

Work Agreements

 

GET_CURRENT_WA_FOR_BP:

data(lv_current_data) = cl_abap_context_info=>get_system_date( ).

select single from i_personworkagreement_1 fields personworkagreement
    where \_businesspartner-businesspartner = @iv_bp
    and \_personwrkagrmtforkeydate-keydate = @lv_current_data
    into @data(lv_personworkagreement).

cl_ble_trace_writer=>write_info_message( conv #( lv_personworkagreement ) ).

rt_wa = lv_personworkagreement.

 

GET_CURRENT_MANAGER_WA_FOR_WA:

select single from i_perswrkagrmtmanagerforkeydte( ) fields managerpersonworkagreement
    where personworkagreement = @iv_wa
    into @data(lv_managerpersonworkagreement).

cl_ble_trace_writer=>write_info_message( conv #( lv_managerpersonworkagreement ) ).

rt_wa = lv_managerpersonworkagreement.

 

GET_BP_FOR_WA:

data(lv_current_data) = cl_abap_context_info=>get_system_date( ).

select single from i_personworkagreement_1 fields \_businesspartner-businesspartner
    where personworkagreement = @iv_wa
    and \_personwrkagrmtforkeydate-keydate = @lv_current_data
    into @data(lv_businesspartner).

cl_ble_trace_writer=>write_info_message( conv #( lv_businesspartner ) ).

rv_bp = lv_businesspartner.
Be the first to leave a comment
You must be Logged on to comment or reply to a post.