Skip to Content

This is the fourteenth and last 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 chapter, we mimicked the backend logic.

In this ninth chapter, we refreshed the master and detail screen.

In this tenth chapter, we implemented code to block the screen for further input, for example, to prevent a user from approving the same purchase order twice, and we also created an extension point.

In this eleventh chapter, we implemented the OData service used for the purchase approval app and created CDS views.

In this twelfth chapter, we registered the current user as an Enterprise Procurement Model (EPM) user.

In this thirteenth chapter, we implemented the missing approve / reject functions as part of our OData service.

In this fourteenth and last chapter, we’ll take a look at the occurrences of entity set names in this tutorial should you have to adapt them.

Change Entity Set Names

The following highlights changes that need to be done to frontend coding in case you need to create the required CDS views with different names. For example, you might need to create CDS views with different names because someone else did this tutorial in the same system. This tutorial uses the CDS view names Z_C_PurchaseOrder and Z_C_Purchaseorderitem. These names are also reflected as entity set names of the OData service. If you create CDS views with different names, you have to adapt the frontend code accordingly.

CodeExample.png
localService/metadata.xml

  • FromRole_assoc_ECD2BAA0F8BF5B8410B95699E8ED23A2
  • ToRole_assoc_ECD2BAA0F8BF5B8410B95699E8ED23A2
            <Association Name="assoc_ECD2BAA0F8BF5B8410B95699E8ED23A2" sap:content-version="1">
                <End Type="Z_PO_TUTORIAL_SRV.Z_C_PurchaseorderType" Multiplicity="1" Role="FromRole_assoc_ECD2BAA0F8BF5B8410B95699E8ED23A2"/>
                <End Type="Z_PO_TUTORIAL_SRV.Z_C_PurchaseorderitemType" Multiplicity="*" Role="ToRole_assoc_ECD2BAA0F8BF5B8410B95699E8ED23A2"/>
                	
                <ReferentialConstraint>
                    <Principal Role="FromRole_assoc_ECD2BAA0F8BF5B8410B95699E8ED23A2">
                        <PropertyRef Name="POId"/>
                    </Principal>
                    <Dependent Role="ToRole_assoc_ECD2BAA0F8BF5B8410B95699E8ED23A2">
                        <PropertyRef Name="POId"/>
                    </Dependent>
                </ReferentialConstraint>
                	     	
            </Association>

 

CodeExample.png
Master.view.xml

  • /Z_C_Purchaseorder
<List
		id="list"
		items="{
			path: '/Z_C_Purchaseorder', 
			sorter: [
				{path: 'ChangedAt', descending: true}, 
				{path: 'POId', descending: false}], 
			parameters:{select: 'POId,OrderedByName,SupplierName,GrossAmount,CurrencyCode,ChangedAt,ItemCount'}
		}"

 

CodeExample.png
Detail.view.xml

  • /#Z_C_PurchaseorderType
<l:VerticalLayout width="100%">
	<ObjectHeader id="objectHeader"
		intro="{parts:[{path: 'i18n>xfld.orderedBy'}, {path: 'OrderedByName'}], formatter: 'jQuery.sap.formatMessage'}"
		number="{parts: [ {path: 'GrossAmount'}, {path: 'CurrencyCode'}], type : 'sap.ui.model.type.Currency', formatOptions: { showMeasure: false }}"
		numberUnit="{CurrencyCode}" responsive="true" title="{SupplierName}">
		<statuses>
			<ObjectStatus id="status"
				text="{path: 'ChangedAt', type: 'sap.ui.model.type.Date', formatOptions: { style: 'medium', strictParsing: true, relative: true }}"
							title="{/#Z_C_PurchaseorderType/ChangedAt/@sap:label}"/>
		</statuses>
	</ObjectHeader>
	<core:ExtensionPoint name="extensionAfterObjectHeader"/>
	<form:SimpleForm class="sapUiForceWidthAuto sapUiResponsiveMargin" columnsL="1" columnsM="1" emptySpanL="5" emptySpanM="5"
		id="attributesSimpleForm" labelSpanL="3" labelSpanM="3" layout="ResponsiveGridLayout" maxContainerCols="2" minWidth="1024"
		title="{i18n>xtit.formTitle}">
		<Label id="poIdLabel" text="{/#Z_C_PurchaseorderType/POId/@sap:label}"/>
		<Text id="poIdText" text="{POId}"/>
		<Label id="deliveryDateLabel" text="{/#Z_C_PurchaseorderType/DeliveryDateEarliest/@sap:label}"/>
		<Text id="deliveryDateText"
			text="{=${LaterDelivDateExist} > 1 ? ${parts:[{path: 'i18n>xfld.andLater'}, {path: 'DeliveryDateEarliest', type: 'sap.ui.model.type.Date', formatOptions: { style: 'medium'}}], formatter: 'jQuery.sap.formatMessage'} : ${path:'DeliveryDateEarliest', type: 'sap.ui.model.type.Date', formatOptions: { style: 'medium'}} }"/>
		<Label id="deliveryAddressLabel" text="{/#Z_C_PurchaseorderType/DeliveryAddress/@sap:label}"/>
		<Text id="deliveryAddressText" text="{DeliveryAddress}"/>
				</form:SimpleForm>
				<core:ExtensionPoint name="extensionAfterForm"/>
</l:VerticalLayout>

 

CodeExample.png
Detail.view.xml

  • to_PurchaseOrderItems
  • /#Z_C_PurchaseorderitemType
<Table busyIndicatorDelay="{detailView>/lineItemTableDelay}" class="sapUiResponsiveMargin" id="lineItemsList" items="{to_PurchaseOrderItems}"
	noDataText="{i18n>detailLineItemTableNoDataText}" updateFinished="onListUpdateFinished" width="auto">
	<headerToolbar>
		<Toolbar id="lineItemsToolbar">
			<Title id="lineItemsHeader" text="{detailView>/lineItemListTitle}"/>
		</Toolbar>
	</headerToolbar>
	<columns>
		<!--Product description-->
		<Column id="productColumn" width="30%">
			<header>
				<Label id="productLabel" text="{/#Z_C_PurchaseorderitemType/Product/@sap:label}"/>
			</header>
		</Column>
		<!--Delivery Date-->
		<Column demandPopin="true" id="deliveryDateColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="15%">
			<header>
				<Label id="itemDeliveryDateLabel" text="{/#Z_C_PurchaseorderitemType/DeliveryDate/@sap:label}"/>
			</header>
		</Column>
		<!--Ordered Quantity-->
		<Column demandPopin="true" hAlign="Right" id="quantityColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="15%">
			<header>
				<Label id="quantityLabel" text="{/#Z_C_PurchaseorderitemType/Quantity/@sap:label}"/>
			</header>
		</Column>
		<!--Item price-->
		<Column demandPopin="true" hAlign="Right" id="priceColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="20%">
			<header>
				<Label id="priceLabel" text="{/#Z_C_PurchaseorderitemType/Price/@sap:label}"/>
			</header>
		</Column>
		<!--Total value of the item-->
		<Column demandPopin="true" hAlign="Right" id="grossAmountColumn" minScreenWidth="Tablet" popinDisplay="Inline" width="20%">
			<header>
				<Label id="grossAmountLabel" text="{/#Z_C_PurchaseorderitemType/GrossAmount/@sap:label}"/>
			</header>
		</Column>
</columns>

 

CodeExample.png
Detail.controller.js

  • /Z_C_Purchaseorder
_onObjectMatched : function (oEvent) {
	var sObjectId =  oEvent.getParameter("arguments").objectId;
	this.getModel().metadataLoaded().then( function() {
		var sObjectPath = this.getModel().createKey("/Z_C_Purchaseorder", {
			POId :  sObjectId
		});
		this.getGlobalModel().setProperty("/currentPOId", sObjectId); // NEW
		this._bindView("/" + sObjectPath);
	}.bind(this));
},

 

CodeExample.png
Approver.js

  • Z_C_Purchaseorder
if (this._iOpenCallsCount === 0) {
	var oResourceBundle = oView.getModel("i18n").getResourceBundle(),
		sSuccessMessage = "";
	if (aPOIds.length === 1) {
		var sSupplier = oModel.getProperty("/Z_C_Purchaseorder('" + aPOIds[0] + "')").SupplierName;
		sSuccessMessage = oResourceBundle.getText(bApprove ? "ymsg.approvalMessageToast" : "ymsg.rejectionMessageToast", [sSupplier]);
	} else {
		sSuccessMessage = oResourceBundle.getText(bApprove ? "ymsg.massApprovalMessageToast" : "ymsg.massRejectionMessageToast");
	}
	sap.ui.require(["sap/m/MessageToast"], function(MessageToast) {
		MessageToast.show(sSuccessMessage);
	});
}

You’ve just reached the end of this tutorial. I hope the tutorial has sparked your interest in SAP Fiori development.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply