Monday 26 February 2018

SAP Cloud Platform Workflow Integration with Back-end OData Service

Prerequisites:

◈ SAP Cloud Platform access
◈ Destination(s) setup in SAP Cloud Platform
◈ SAP Cloud Connector Setup
◈ Back-end system access for OData Creation

Scenario:

1. User logs on to SAP fiori launchpad and runs a custom UI5 app running on SAP cloud platform to enter 3 fields which are user first name, last name and email address.
2. After submission, user gets a message about submission of user entry.
3. Submission of this entry triggers SAP cloud platform custom workflow.
4. Once workflow administrator approves the workflow, it will call an OData backend service which is running in S4HANA on-premise environment. This OData Service will create an entry in a Z table.
5. If workflow administrator chooses to reject the workflow, it will reject the submission and sends an email to email id provided on the user registration box.

Picture Representation:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Development Tasks:

1. Create OData Service

I have created a Z table which will get updated with userid, user firstname, lastname and email address along with timestamp

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Go to SEGW and create a new project for your OData Service.

This is how my OData service looks like:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

I had to implement class ****_DPC_EXT because for some reason mapping of insert reason wasn’t working , here is the code for my DPC_EXT insert operation:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

In above code I have created a number object for userid assignment.

After this please activate and publish your OData service and you can run some tests using SAP Gateway Client.

2. Create UI5 application for user registration which will serve as front end for trigger of SAP Cloud Platform workflow

I have used SAP WebIDE to develop my UI5 application.

Design a layout for your input form. I have chosen vertical grid layout, please feel free to arrange the UI fields the way you like. Here’s how my layout looks like:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Here you can see the code generated behind for above view, if this helps

<App id="idAppControl">
<pages>
<Page title="{i18n>title}">
<content>
    <sap.ui.layout:VerticalLayout xmlns:sap.ui.layout="sap.ui.layout" width="100%" id="__layout0">
    <sap.ui.layout:content><sap.ui.layout:Grid id="__grid0">
        <sap.ui.layout:content>
            <sap.ui.layout.form:Form xmlns:sap.ui.layout.form="sap.ui.layout.form" editable="true" id="__form0">
                <sap.ui.layout.form:formContainers>
                    <sap.ui.layout.form:FormContainer title="{i18n>persInfo}" id="__container0">
                        <sap.ui.layout.form:formElements>
                            <sap.ui.layout.form:FormElement label="{i18n>firstName}" id="firstName_f">
                                <sap.ui.layout.form:fields>
                                    <Input width="100%" id="firstname" required="true" value="{user>/firstname}"/>
                                </sap.ui.layout.form:fields>
                            </sap.ui.layout.form:FormElement>
                            <sap.ui.layout.form:FormElement label="{i18n>lastName}" id="lastName_f">
                                <sap.ui.layout.form:fields>
                                    <Input width="100%" id="lastname" required="true" value="{user>/lastname}"/>
                                </sap.ui.layout.form:fields>
                            </sap.ui.layout.form:FormElement>
                            <sap.ui.layout.form:FormElement label="{i18n>emailID}" id="email_f">
                                <sap.ui.layout.form:fields>
                                    <Input width="100%" id="email" required="true" value="{user>/email}"/>
                                    <Button text="Submit" width="100px" id="Submit_Form" press="submitRequest"/>
                                </sap.ui.layout.form:fields>
                            </sap.ui.layout.form:FormElement>                             
                        </sap.ui.layout.form:formElements>
                    </sap.ui.layout.form:FormContainer>
                </sap.ui.layout.form:formContainers>
                <sap.ui.layout.form:layout>
                    <sap.ui.layout.form:ResponsiveGridLayout id="__layout1"/>
                </sap.ui.layout.form:layout></sap.ui.layout.form:Form>
        </sap.ui.layout:content>
        </sap.ui.layout:Grid>
    </sap.ui.layout:content>
    </sap.ui.layout:VerticalLayout>
</content>
<headerContent>
</headerContent>
</Page>
</pages>
</App>

Here is the code for Component.JS file for your custom UI5 application:

return UIComponent.extend("amiran.ey.amiranshowcasefinal.Component", {

metadata: {
manifest: "json",
publicMethods: [ "updateBinding" ]
},

/**
* 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");
this.setModel(models.createUserModel(), "user");
}
});

Here is the code for model.js file:

return {

createDeviceModel: function() {
var oModel = new JSONModel(Device);
oModel.setDefaultBindingMode("OneWay");
return oModel;
},
createUserModel: function() {
var oModel = new JSONModel();
oModel.setDefaultBindingMode("TwoWay");
return oModel;
}

};

Here is the code for your view’s controller.js file

return Controller.extend("amiran.ey.amiranshowcasefinal.controller.user_register_form", {
submitRequest: function() {
//var userModel = this.getView().getModel("user");
var userModel = this.getView().getModel("user");

var context = JSON.stringify({
"definitionId": "amiran_test_workflow",
"context": {
"userData": {
"input": {},
"output": {},
"firstname": userModel.oData.firstname,
"lastname": userModel.oData.lastname,
"email": userModel.oData.email,
}
}
});

$.ajax({
type: "GET",
url: "/bpmworkflowruntime/rest/v1/xsrf-token",
headers: {
"X-CSRF-Token": "Fetch"
},
success: function(data, statusText, xhr) {
var token = xhr.getResponseHeader("X-CSRF-Token");

$.ajax({
type: "POST",
url: "/bpmworkflowruntime/rest/v1/workflow-instances",
data: context,
headers: {
"X-CSRF-Token": token
},
success: function() {
sap.m.MessageToast.show("Your user creation request has been submitted for approval!");
},
error: function(errMsg) {
sap.m.MessageToast.show("XSRF token request didn't work: " + errMsg.statusText);
},
dataType: "json",
contentType: "application/json"
});
},
error: function(errMsg) {
sap.m.MessageToast.show("Didn't work: " + errMsg.statusText);
},
contentType: "application/json"
});
}
});

In above code lies the magic of integration between your SAPUI5 application and your SAP cloud platform workflow. The important things to look out are following:

◈ In submitrequest function which is called when submit button is pressed on your UI5 application has got a variable defined as “context”, please make sure the “definitionId” is the name of your “custom workflow application” and the “context” of workflow has got parameters “input”,”output” and the UI elements from your UI screen
◈ You can also see there is an ajax call with GET to get CSRF token from your SAP Cloud platform url and a POST call to post data from UI application to context of workflow
◈ I have added a message to show success of POST call in following way:

sap.m.MessageToast.show(“Your user creation request has been submitted for approval!”);

Now your UI application is ready and you are ready to Deploy it to SAP Cloud platform by right clicking on application and choose to deploy as shown below:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

If you have got your portal site up and running on SAP Cloud Platform, you can also choose to ‘Register to SAP Fiori Launchpad’.

Once you have done this, you can go to SAP Fiori Launchpad running on your SAP cloud platform and can see tile for custom UI application.

Following is the screen-shot of my user registration application running on SAP Cloud Platform.

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

3. Now let’s create custom workflow on SAP Cloud platform.

Go back to SAP WebIDE and choose File->New->Project from Template, in category choose ‘Business Process Management’ and you will see the template as per following screen-shot:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

If you don’t see as mentioned above please make sure

◈ You are running SAP WebIDE Full Stack from your SAP Cloud platform
◈ In SAP WebIDE Full Stack ->tools->features->Workflow Editor is ON

After giving name to your workflow project (please make sure that your workflow id is same as you used in your custom UI5 application for submission of workflow. For example, in my case it was “amiran_test_workflow”).

Now you are in workflow editor, which is graphical tool and very much simple to use.

As per following screen-shot I have used 2 service tasks, one script task, one exclusive gateway.

In my case first service task calls custom UI screen to approve the user registration. After that exclusive gateway checks the outcome of approval i.e whether it was rejected or accepted. Script task makes sure the context of workflow has got data. The last service task does the call to SAP backend Service to create user in our Z table.

Please find below screen-shot of the workflow:


Settings for Approve User Service Task:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

◈ Please note that in above screen-shot the ‘Users’ and ‘Groups’ is blank. I have removed it for obvious reasons (hide personal data), but you need to provide users who will get this workflow for approval, this information you can find on SAP Cloud Platform->Services->workflow->configure service->roles, you need to have workflow approval role assigned to your s-userid if you are using your own userid to approve the workflow task
◈ Also you can see in above screen-shot there is HTML5 app name and SAPUI5 Component name have been given (The logic behind this is that by default SAP cloud platform workflow is integrate with SAP Fiori Inbox app running on SAP Cloud platform so when your workflow entry goes to SAP Fiori Inbox it doesn’t show any information as there is NO UI. However if you have built a custom UI screen for approval then that screen will be shown when you click on workflow approval entry in SAP fiori My Inbox app. I have described steps to create that UI further down in blog at step number 4)

Settings for Exclusive Gateway:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

In the screen-shot for workflow which is shown after point f, you can see from exclusive gateway there are 2 paths, one is showing approval and other is showing ‘rejected’. This is basically like If<->else condition of workflow. Here one important thing is to have one of branches selected as ‘default’. This is a checkbox as per following screen-shot. It’s basically to tell workflow the default behaviour of your workflow.

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Here is the setting for rejection, the point to note here is that you must provide a condition, the condition tells workflow which path to take. As per below screen-shot I am checking if user selected ‘rejection’.

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Settings for Convertinput service task:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

In above screen-shot you can see there is convertinput.js file. A JS file is mandatory for this task type. Following is the simple code which I am doing for my Script file

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Settings for createuserid task:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Here you can see I am using POST method as this task will call back-end of ODATA service which is running on-premise. For security reason CSRF token functionality is available here. So you can give path to your ODATA service again here. SAP Cloud platform workflow automatically uses ‘path to XSRF token’ url to firstly do a ‘GET’ call to fetch CSRF token from backend and then automatically append CSRF token to POST header. I quite frankly like the beauty of this function.

You can open workflow editor in code editor as well, following is code representation of my workflow:

{
"contents": {
"326c26a0-ac59-4ac2-a2c0-1780fba15276": {
"classDefinition": "com.sap.bpm.wfs.Model",
"id": "amiran_test_workflow",
"subject": "amiran_test_workflow",
"name": "amiran_test_workflow",
"documentation": "Workflow For User Registration",
"lastIds": "e358ed89-374f-4866-a2e8-97e4309214c3",
"events": {
"00409a22-fa0c-4584-a365-c61086e16aed": {
"name": "StartEvent1"
},
"b3dac5df-c682-4bbb-9e3b-d6bcaab6170a": {
"name": "Complete"
}
},
"activities": {
"07a3c547-1ab4-49dc-9080-700f804131a9": {
"name": "ApproveUser"
},
"16b85e4a-2800-41b3-8e8e-323229ce5d62": {
"name": "checkapprovalstatus"
},
"d13c0a1f-7939-4330-89c7-4d364665050e": {
"name": "createuserid"
},
"6766f508-51bc-4c3d-96a7-2db5386f998c": {
"name": "convertinput"
}
},
"sequenceFlows": {
"78813c21-0c1c-46b4-97c4-f7708e83355e": {
"name": "SequenceFlow1"
},
"bee44173-c5e4-4af7-8f8b-9ec9575abebb": {
"name": "SequenceFlow32"
},
"aff972a9-0b75-46b7-880b-47bfd8ef9010": {
"name": "approved"
},
"69f3a1d9-f981-48fa-b640-abae15a4c737": {
"name": "SequenceFlow34"
},
"9d264671-8990-4e55-98f4-850c2d5c873d": {
"name": "rejected"
},
"8aaff4b9-d464-4b76-b438-f5b6c9d74646": {
"name": "SequenceFlow37"
}
},
"diagrams": {
"1f8998ef-cdc3-45d4-b8a8-39c046b59287": {}
}
},
"00409a22-fa0c-4584-a365-c61086e16aed": {
"classDefinition": "com.sap.bpm.wfs.StartEvent",
"id": "startevent1",
"name": "StartEvent1"
},
"b3dac5df-c682-4bbb-9e3b-d6bcaab6170a": {
"classDefinition": "com.sap.bpm.wfs.EndEvent",
"id": "endevent1",
"name": "Complete"
},
"07a3c547-1ab4-49dc-9080-700f804131a9": {
"classDefinition": "com.sap.bpm.wfs.UserTask",
"subject": "Approve userid for  ${context.userData.firstname} ${context.userData.lastname}",
"priority": "MEDIUM",
"isHiddenInLogForParticipant": false,
"userInterface": "sapui5://html5apps/amirannewuseruiapprove/NewUserUIApprove.AmiranNewUserUIApprove",
"recipientUsers": "S0017648388",
"id": "usertask1",
"name": "ApproveUser"
},
"16b85e4a-2800-41b3-8e8e-323229ce5d62": {
"classDefinition": "com.sap.bpm.wfs.ExclusiveGateway",
"id": "exclusivegateway7",
"name": "checkapprovalstatus",
"default": "aff972a9-0b75-46b7-880b-47bfd8ef9010"
},
"d13c0a1f-7939-4330-89c7-4d364665050e": {
"classDefinition": "com.sap.bpm.wfs.ServiceTask",
"destination": "KS4_300",
"path": "/sap/opu/odata/sap/ZZAG_USER_SCP_SRV/UserIDSet?",
"httpMethod": "POST",
"xsrfPath": "/sap/opu/odata/sap/ZZAG_USER_SCP_SRV/UserIDSet?$format=json",
"requestVariable": "${context.userData.input}",
"responseVariable": "${context.userData.output}",
"id": "servicetask10",
"name": "createuserid"
},
"6766f508-51bc-4c3d-96a7-2db5386f998c": {
"classDefinition": "com.sap.bpm.wfs.ScriptTask",
"reference": "/scripts/amiran_test_workflow/convertinput.js",
"id": "scripttask1",
"name": "convertinput"
},
"78813c21-0c1c-46b4-97c4-f7708e83355e": {
"classDefinition": "com.sap.bpm.wfs.SequenceFlow",
"id": "sequenceflow1",
"name": "SequenceFlow1",
"sourceRef": "00409a22-fa0c-4584-a365-c61086e16aed",
"targetRef": "07a3c547-1ab4-49dc-9080-700f804131a9"
},
"bee44173-c5e4-4af7-8f8b-9ec9575abebb": {
"classDefinition": "com.sap.bpm.wfs.SequenceFlow",
"id": "sequenceflow32",
"name": "SequenceFlow32",
"sourceRef": "07a3c547-1ab4-49dc-9080-700f804131a9",
"targetRef": "16b85e4a-2800-41b3-8e8e-323229ce5d62"
},
"aff972a9-0b75-46b7-880b-47bfd8ef9010": {
"classDefinition": "com.sap.bpm.wfs.SequenceFlow",
"id": "sequenceflow33",
"name": "approved",
"sourceRef": "16b85e4a-2800-41b3-8e8e-323229ce5d62",
"targetRef": "6766f508-51bc-4c3d-96a7-2db5386f998c"
},
"69f3a1d9-f981-48fa-b640-abae15a4c737": {
"classDefinition": "com.sap.bpm.wfs.SequenceFlow",
"id": "sequenceflow34",
"name": "SequenceFlow34",
"sourceRef": "d13c0a1f-7939-4330-89c7-4d364665050e",
"targetRef": "b3dac5df-c682-4bbb-9e3b-d6bcaab6170a"
},
"9d264671-8990-4e55-98f4-850c2d5c873d": {
"classDefinition": "com.sap.bpm.wfs.SequenceFlow",
"condition": "${context.userData.Rejected == 'X'}",
"id": "sequenceflow35",
"name": "rejected",
"sourceRef": "16b85e4a-2800-41b3-8e8e-323229ce5d62",
"targetRef": "b3dac5df-c682-4bbb-9e3b-d6bcaab6170a"
},
"8aaff4b9-d464-4b76-b438-f5b6c9d74646": {
"classDefinition": "com.sap.bpm.wfs.SequenceFlow",
"id": "sequenceflow37",
"name": "SequenceFlow37",
"sourceRef": "6766f508-51bc-4c3d-96a7-2db5386f998c",
"targetRef": "d13c0a1f-7939-4330-89c7-4d364665050e"
},
"1f8998ef-cdc3-45d4-b8a8-39c046b59287": {
"classDefinition": "com.sap.bpm.wfs.ui.Diagram",
"symbols": {
"f9241189-4a1a-4a81-8bd0-fbf5a0ca34f3": {},
"c6078b39-7f64-424c-ac47-a7c7704dce52": {},
"9f512907-f706-4d0c-8b4a-813383b803a0": {},
"9b1f7730-cc0a-413f-af7f-9c3e2d0177d7": {},
"f5c67205-9447-4c79-bf72-6d63d591ed0f": {},
"62fe8d15-031f-42a5-9690-e225123540fd": {},
"65a53bed-6f5e-4f95-9da5-abc5a9cf6bd2": {},
"cb4ad202-7172-4917-8816-b2fc63c871d1": {},
"cbb50edd-0a2d-4f0c-91ae-96f9b1e186fb": {},
"be0a0b4e-51b5-477f-b1d2-7ba679c77aa0": {},
"3e9f7f76-edc3-43c7-82b5-90616152af85": {},
"85c0be08-b4da-492a-bb8e-2ef3f8c951d7": {}
}
},
"f9241189-4a1a-4a81-8bd0-fbf5a0ca34f3": {
"classDefinition": "com.sap.bpm.wfs.ui.StartEventSymbol",
"x": 31,
"y": 96,
"width": 32,
"height": 32,
"object": "00409a22-fa0c-4584-a365-c61086e16aed"
},
"c6078b39-7f64-424c-ac47-a7c7704dce52": {
"classDefinition": "com.sap.bpm.wfs.ui.EndEventSymbol",
"x": 722,
"y": 96,
"width": 32,
"height": 32,
"object": "b3dac5df-c682-4bbb-9e3b-d6bcaab6170a"
},
"9f512907-f706-4d0c-8b4a-813383b803a0": {
"classDefinition": "com.sap.bpm.wfs.ui.SequenceFlowSymbol",
"points": "47,116.5 153,116.5",
"sourceSymbol": "f9241189-4a1a-4a81-8bd0-fbf5a0ca34f3",
"targetSymbol": "9b1f7730-cc0a-413f-af7f-9c3e2d0177d7",
"object": "78813c21-0c1c-46b4-97c4-f7708e83355e"
},
"9b1f7730-cc0a-413f-af7f-9c3e2d0177d7": {
"classDefinition": "com.sap.bpm.wfs.ui.UserTaskSymbol",
"isAdjustToContent": false,
"x": 101,
"y": 87,
"width": 104,
"height": 68,
"object": "07a3c547-1ab4-49dc-9080-700f804131a9"
},
"f5c67205-9447-4c79-bf72-6d63d591ed0f": {
"classDefinition": "com.sap.bpm.wfs.ui.SequenceFlowSymbol",
"points": "153,118.75 267.25,118.75",
"sourceSymbol": "9b1f7730-cc0a-413f-af7f-9c3e2d0177d7",
"targetSymbol": "62fe8d15-031f-42a5-9690-e225123540fd",
"object": "bee44173-c5e4-4af7-8f8b-9ec9575abebb"
},
"62fe8d15-031f-42a5-9690-e225123540fd": {
"classDefinition": "com.sap.bpm.wfs.ui.ExclusiveGatewaySymbol",
"x": 246.25,
"y": 95.5,
"object": "16b85e4a-2800-41b3-8e8e-323229ce5d62"
},
"65a53bed-6f5e-4f95-9da5-abc5a9cf6bd2": {
"classDefinition": "com.sap.bpm.wfs.ui.SequenceFlowSymbol",
"points": "267.25,116.75 519,116.75",
"sourceSymbol": "62fe8d15-031f-42a5-9690-e225123540fd",
"targetSymbol": "3e9f7f76-edc3-43c7-82b5-90616152af85",
"object": "aff972a9-0b75-46b7-880b-47bfd8ef9010"
},
"cb4ad202-7172-4917-8816-b2fc63c871d1": {
"classDefinition": "com.sap.bpm.wfs.ui.ServiceTaskSymbol",
"x": 581.8226228040958,
"y": 87.25,
"width": 100,
"height": 60,
"object": "d13c0a1f-7939-4330-89c7-4d364665050e"
},
"cbb50edd-0a2d-4f0c-91ae-96f9b1e186fb": {
"classDefinition": "com.sap.bpm.wfs.ui.SequenceFlowSymbol",
"points": "631.8226228040958,114.625 738,114.625",
"sourceSymbol": "cb4ad202-7172-4917-8816-b2fc63c871d1",
"targetSymbol": "c6078b39-7f64-424c-ac47-a7c7704dce52",
"object": "69f3a1d9-f981-48fa-b640-abae15a4c737"
},
"be0a0b4e-51b5-477f-b1d2-7ba679c77aa0": {
"classDefinition": "com.sap.bpm.wfs.ui.SequenceFlowSymbol",
"points": "267.25,137 267.25,187.5 738,187.5 738,127.5",
"sourceSymbol": "62fe8d15-031f-42a5-9690-e225123540fd",
"targetSymbol": "c6078b39-7f64-424c-ac47-a7c7704dce52",
"object": "9d264671-8990-4e55-98f4-850c2d5c873d"
},
"3e9f7f76-edc3-43c7-82b5-90616152af85": {
"classDefinition": "com.sap.bpm.wfs.ui.ScriptTaskSymbol",
"x": 469,
"y": 87,
"width": 100,
"height": 60,
"object": "6766f508-51bc-4c3d-96a7-2db5386f998c"
},
"85c0be08-b4da-492a-bb8e-2ef3f8c951d7": {
"classDefinition": "com.sap.bpm.wfs.ui.SequenceFlowSymbol",
"points": "519,117.125 631.8226228040958,117.125",
"sourceSymbol": "3e9f7f76-edc3-43c7-82b5-90616152af85",
"targetSymbol": "cb4ad202-7172-4917-8816-b2fc63c871d1",
"object": "8aaff4b9-d464-4b76-b438-f5b6c9d74646"
},
"e358ed89-374f-4866-a2e8-97e4309214c3": {
"classDefinition": "com.sap.bpm.wfs.LastIDs",
"terminateeventdefinition": 1,
"messageeventdefinition": 1,
"hubapireference": 1,
"sequenceflow": 38,
"startevent": 1,
"intermediatemessageevent": 1,
"endevent": 3,
"usertask": 1,
"servicetask": 13,
"scripttask": 1,
"exclusivegateway": 7,
"parallelgateway": 1
}
}
}

4. The final piece of this puzzle is the custom UI to approve/reject the workflow.

Create another custom UI5 application. I have again used SAP WebIDE as my favourite tool to create this application.

Create a view as per following screen-shot:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

Here is the code behind my UI5 screen:

<App height="90%">
<pages>
<Page showHeader="false" showFooter="false">
<content>
<l:VerticalLayout width="100%" id="__layout0">
<l:content>
<ObjectHeader title="{/context/task/Title}" titleActive="false" id="__header0">
<attributes>
<ObjectAttribute title="{i18n>createdOn}" text="{/context/task/CreatedOn}"/>
</attributes>
<statuses>
<ObjectStatus text="{/context/task/Status}" state="None"/>
<ObjectStatus text="{/context/task/Priority}" state="{/context/task/PriorityState}"/>
</statuses>
</ObjectHeader>
<l:HorizontalLayout id="__layout1">
<l:content>
<IconTabBar selectedKey="__filter0" id="__bar0">
<items>
<IconTabFilter icon="sap-icon://hint" id="__filter0">
<content>
<l:HorizontalLayout id="hLayout11">
<FlexBox width="100%" id="__box0">
<items>
<l:VerticalLayout id="hLayout12">
<HBox width="100%" id="__hbox0">
<l:HorizontalLayout id="hLayoutJob1">
<f:Form>
<f:layout>
<f:GridLayout singleColumn="true"/>
</f:layout>
<f:FormContainer expanded="true" title="{i18n>persInfo}" expandable="false">
<f:formElements>
<f:FormElement label="First name">
<Text text="{/context/userData/firstname}"/>
</f:FormElement>
<f:FormElement label="Last name">
<Text text="{/context/userData/lastname}"/>
</f:FormElement>
<f:FormElement label="Email ID">
<Text text="{/context/userData/email}" id="email"/>
</f:FormElement>
</f:formElements>
</f:FormContainer>
</f:Form>
</l:HorizontalLayout>
</HBox>
</l:VerticalLayout>
</items>
</FlexBox>
</l:HorizontalLayout>
</content>
</IconTabFilter>
<IconTabFilter icon="sap-icon://employee-lookup" id="__filter1" visible="false"/>
</items>
</IconTabBar>
</l:content>
</l:HorizontalLayout>
</l:content>
</l:VerticalLayout>
</content>
</Page>
</pages>
</App>

Here is the code from my Component.js file

metadata: {
manifest: "json",
publicMethods: ["updateBinding"]
},

/**
* 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);
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;
var processContext = new sap.ui.model.json.JSONModel();

var that = this;
var jsonModel = new sap.ui.model.json.JSONModel();
that.setModel(jsonModel);

$.ajax({
type: "GET",
url: "/bpmworkflowruntime/rest/v1/task-instances/" + taskId + "/context",
contentType: "application/json",
dataType: "json",
success: function(result, xhr, data) {

var processContext = new sap.ui.model.json.JSONModel();
processContext.context = data.responseJSON;

processContext.context.task = {};
processContext.context.task.Title = taskData.TaskTitle;
processContext.context.task.Priority = taskData.Priority;
processContext.context.task.Status = taskData.Status;

if (taskData.Priority === "HIGH") {
processContext.context.task.PriorityState = "Warning";
} else if (taskData.Priority === "VERY HIGH") {
processContext.context.task.PriorityState = "Error";
} else {
processContext.context.task.PriorityState = "Success";
}

processContext.context.task.CreatedOn = taskData.CreatedOn.toDateString();
// get task description and add it to the model
startupParameters.inboxAPI.getDescription("NA", taskData.InstanceID).done(function(dataDescr) {
processContext.context.task.Description = dataDescr.Description;
jsonModel.setProperty("/context/task/Description", dataDescr.Description);
}).
fail(function(errorText) {
jQuery.sap.require("sap.m.MessageBox");
sap.m.MessageBox.error(errorText, {
title: "Error"
});
});
jsonModel.setData(processContext);
that.setModel(jsonModel);
}
});
startupParameters.inboxAPI.addAction({
action: "Approve",
label: "Approve"
}, function(button) {
this._completeTask(taskId, true);
},
this);
startupParameters.inboxAPI.addAction({
action: "Reject",
label: "Reject"
}, function(button) {
var email = jsonModel.getProperty("/context/userData/email");
jsonModel.setProperty("/context/userData/Rejected", "X");
sap.m.URLHelper.triggerEmail(email, "Request to create userid rejected",
"Request to create userid has been rejected. Please contact helpdesk.");
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);
}
});

In above code you can see the code behind Approve and Reject buttons. It is important to understand the code here.

Now please make sure all 3 development pieces i.e custom workflow, custom UI for user registration and custom UI for approval are all deployed on SAP cloud platform.

Time to run some tests now and see if it all works together.

User submit request

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

In Monitor workflow app you can see your workflow instance running:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

If you go to My Inbox app, you can see your workflow is ready for approval there:

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

After user has clicked on ‘Approve’ and it will call OData service and create entry in Z table in S4HANA system on premise.

SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Guides,

1 comment:

  1. Hi,

    thanks for the blog.
    screen shots are not visible in th eblog. could you please upload it again

    ReplyDelete