Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
Showing results for 
Search instead for 
Did you mean: 
0 Kudos
This is the eighth  part of a tutorial series about how to build your own SAP Fiori Approve Purchase Order app.

The purpose of this tutorial is to show you step-by-step how to build your own SAP Fiori Approve Purchase Orders app and provides additional insights into why certain aspects have been developed as they are.

Please see the introductory post (Tutorial: Build Your Own SAP Fiori Approve Purchase Order App) for background information regarding this tutorial.

Previously posted chapters can be find here:

In this chapter, we set up the app using the SAP Web IDE and ran the app using mock data.

In this chapter, we adapted the list screen of the app.

In this chapter, we adapted the detail screen of the app.

In this chapter, we enabled approve and reject buttons.

In this sixth chapter, we set up a new model that stores the global app state.

In this seventh chapter,  we encapsulated approve / reject service calls.

In this eighth part, we’re going mimic the backend logic.

Mimic the backend logic
As mentioned previously, the mock server can intercept HTTP calls and provide fake output to the client without involving any backend system, but the behavior of special backend logic, such as that performed by function imports, isn’t automatically known to the mock server.

As defined in Approver.js, our application uses two OData function imports - /ApprovePurchaseOrder and /RejectPurchaseOrder:

File: model/Approver.js

approve: function(bApprove, bFromSwipe, oView, aPOIds, sApprovalNote) {

return new Promise(function(fnResolve, fnReject) {

var sFunction = bApprove ? "/ApprovePurchaseOrder" : "/RejectPurchaseOrder"

We need to implement a logic that mimics the backend behavior. In this case, this can be done by simply removing all approved / rejected purchase orders from the list that the mock server handles.

To do so, we’ll implement our own request that is able to handle these calls.


In the localService folder, we’ll create a new file called MockRequests.js and past the following code:

File: localService/MockRequests.js

sap.ui.define(["sap/ui/base/Object"], function(Object) {

"use strict";


return Object.extend("acme.purchaseorder.approve.localService.MockRequests", {


constructor: function(oMockServer) {

// service URL -> to be replaced

this._srvUrl = "/sap/opu/odata/sap/SEPMRA_PO_APV/";

this._bError = false; //true if a this._sjax request failed

this._sErrorTxt = ""; //error text for the oXhr error response

this._oMockServer = oMockServer;


getRequests: function() {

return [






mockApprovePo: function() {

return {

// This mock request simulates the function import

// "ApprovePurchaseOrder", which is triggered when the

// user chooses the "Approve" button.

// It removes the approved purchase order from the mock

// data.

method: "POST",

path: new RegExp("ApprovePurchaseOrder\\?POId='(.*)'&Note='(.*)'"),

response: this.deletePo.bind(this)




mockRejectPo: function() {

return {

// This mock request simulates the function import

// "RejectPurchaseOrder",

// which is triggered when the user chooses the "Reject"

// button.

// It removes the rejected purchase order from the mock

// data.

method: "POST",

path: new RegExp("RejectPurchaseOrder\\?POId='(.*)'&Note='(.*)'"),

response: this.deletePo.bind(this)




deletePo: function(oXhr, sPOId, sNote) {

var aPurchaseOrders = this._oMockServer.getEntitySetData("Z_C_Purchaseorder"),

aPurchaseOrderItems = this._oMockServer.getEntitySetData("Z_C_Purchaseorderitem"),

filterPurchaseOrder = function(oPurchaseOrderOrPOItem) {

return oPurchaseOrderOrPOItem.POId !== sPOId;



//removes the approved/rejected PurchaseOrders and

// PurchaseOrderItems from the entity set data

aPurchaseOrders = aPurchaseOrders.filter(filterPurchaseOrder.bind(this));

this._oMockServer.setEntitySetData("Z_C_Purchaseorder", aPurchaseOrders);

aPurchaseOrderItems = aPurchaseOrderItems.filter(filterPurchaseOrder.bind(this));

this._oMockServer.setEntitySetData("Z_C_Purchaseorderitem", aPurchaseOrderItems);


oXhr.respondJSON(200, {}, JSON.stringify({

d: {

results: []






Our mock request responds to every request with http status 200 (ok) indicating that the business function is successfully executed in the backend. Using different http status codes, we could also mimic the errors.

We now need to register our response handler in the generated mock server.

In mockserver.js, we’ll add a reference to our response handler:


File: localService/mockserver.js





], function (MockServer, MockRequests ) {

and register the mock request in the init function:

File: localService/mockserver.js


// Handling request errors

if (sErrorParam) {

aRequests.forEach( function ( aEntry ) {

if (aEntry.path.toString().indexOf(sEntity) > -1) {

fnResponse(iErrorCode, sErrorParam, aEntry);





var oRequests = new MockRequests(oMockServer);



Mimic backend response times

We’ve implemented the busy logic, but once we approve a purchase order, we don’t recognize a busy indicator. This is because with standard settings, a busy indicator is displayed with a delay. Since our business logic (the mock server) processes the request faster than the delay, no busy indicator is displayed.

Using the mock server, we can mimic different backend response times.

In mockserver.js, in the init function, you can configure the response time, or you get use the URL parameter serverDelay to specify the response delay

File: localService/mockserver.js


autoRespond : true,

autoRespondAfter : (oUriParameters.get("serverDelay") || 0)


Our new mock request handler now intercepts the calls and adapt the model accordingly. As a result, you should notice if you approve a purchase order.

You might also notice that even though the purchase order is successfully processed, it still remains in the list on the left and in the detail screen. So, the view needs to be refreshed and the next purchase order to be processed by the user needs to be selected.

I hope the eighth part of the tutorial has sparked your interest in the chapters to come. Next time, we’ll refresh the master and detail screen.