Work with SAPUI5 Adaptation Projects to extend the source code of an application variant to be deployed to SAP S/4HANA Cloud Public Edition (2 of 3)
You will learn how to work with SAPUI5 Adaptation Projects to extend a standard SAP Fiori application by adding custom UI elements and controller logic to an application variant in SAP S/4HANA Cloud Public Edition.
Overview
You will learn
- How to extend the source code of your application variant.
- How to add a new section.
- How to visualize Payment Trends as a simple chart.
Prerequisites
Prerequisites
Steps
Intro
Important information
This group of three tutorials focuses on SAPUI5 Adaptation Projects in SAP S/4HANA Cloud Public Edition. The same basic principles and features of SAPUI5 Adaptation Projects similarly apply to SAP S/4HANA and SAP S/4HANA Cloud Private Edition. While the first and second tutorials also largely apply to SAP S/4HANA and SAP S/4HANA Cloud Private Edition, the third tutorial applies specifically to SAP S/4HANA Cloud Public Edition.
To learn more about SAPUI5 Adaptation Projects in SAP S/4HANA, please see the SAP Help Portal:
Please note: the documentation is also valid for SAP S/4HANA Cloud Private Edition.
When to extend
SAP S/4HANA Cloud Public Edition delivers the latest industry best practices, innovations, and business applications to you as a service. If a standard SAP Fiori application doesn’t fully support your business process or the specific needs of some of your business users, you can extend SAP S/4HANA Cloud Public Edition by creating and adapting an application variant, while the original SAP Fiori application remains available and unchanged. In an application variant, you can extend the original code to define changes that fulfill the specific business requirements of different business roles, user groups, organizational units, and countries. For example, you might want to create an application variant to simplify and streamline a specific process by hiding fields not required by casual users, visualizing data as a chart, or changing a responsive table to a grid table to make information easier to process.
New for SAP S/4HANA Cloud Public Edition 2402
As of SAP S/4HANA Cloud Public Edition 2402, developers can work with SAPUI5 Adaptation Projects in SAP Business Application Studio (BAS) to create application variants. Developers efficiently reuse the original SAP Fiori application together with its OData service and application logic to define changes that are specific to the application variant only.
After deployment, both the source application and the newly created application variant exist with distinct titles and can be made available to the relevant business users through uniquely named application tiles in SAP Fiori launchpad: My Home. After a Cloud upgrade, developers can test the application variant in SAP Business Application Studio and, if there are no issues, they can deploy it again to benefit from the latest SAP enhancements to the original application (see Check Whether the Base App of an Adaptation Project Is Up-To-Date).
Use case
Let’s imagine you’ve received feedback from a group of Credit Controllers at your company who occasionally work with the SAP Fiori application Manage Credit Accounts: (https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps('F4596')/S31). You’ve been informed that the standard application does not fully support their needs. By creating an application variant, as opposed to developing a new custom application from scratch, you can efficiently make the changes for this group of Credit Controllers, while other Credit Controllers at your company can continue to use the original application.
The Manage Credit Accounts application is based on the List Report Object Page floorplan. You will simplify the user experience for this specific group of Credit Controllers by:
Object Page:
- Adding a new section for Payment Trends
- Displaying payment trends in a chart
Log onto SAP Business Application Studio (BAS) and access your SAP Fiori development space.
Expand the Projects folder and right-click on the adaptation project you created in Tutorial 1 and select Open Adaptation Editor from the context menu.
The List Report of the Manage Credit Accounts application is displayed in UI Adaptation mode. Switch to Navigation mode.
Choose the
Gobutton to populate the table with data.Select a business partner by choosing the chevron button to navigate to the Object Page.

Working with Adaptation Editor:
On the Object Page, switch from Navigation to UI Adaptation mode. Choose from the Object Page Quick Actions the action Add Controller to Page.

Object Page Quick Actions In the Extend With Controller popup, enter a name in the Controller Name field (e.g. OPExtensionController) and choose Create.
- A corresponding .js file is generated in your workspace in the webapp folder under changes –> coding –> OPExtensionController.js.

Object Page Extension Controller Before you edit the .js file generated in step 2, add a coding fragment to the Object Page layout. Choose from the Object Page Quick Actions the action Add Custom Section.
- You want to add a new section to the Object Page in which you can include a chart visualization of the Payment Trends. New sections are defined in the sections aggregation of the ObjectPageLayout control, so you’re going to add a new XML coding fragment to this aggregation.
In the Add Custom Section popup, enter a name for the fragment (e.g. extension_chart) and choose Create.
Press Save.
A corresponding .xml file is generated in your workspace in the webapp folder under changes –> fragments –> extension_chart.fragment.xml.
In the extension_chart.fragment.xml file generated in step 5, enter the following code:
XML<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:objectPage="sap.uxap" xmlns:viz="sap.viz.ui5.controls" xmlns:viz.feeds="sap.viz.ui5.controls.common.feeds" xmlns:viz.data="sap.viz.ui5.data"> <objectPage:ObjectPageSection id="mysection" title="{i18n>EXTENSION_SECTION1_TITLE}" titleUppercase="false"> <objectPage:ObjectPageSubSection id="mysubsection" title="{i18n>EXTENSION_SECTION1_TITLE}"> <viz:VizFrame id="AdlChartID" uiConfig="{applicationSet:'fiori'}" vizType='column' vizProperties="{ title: {visible: false}, valueAxis: {title: {visible: 'true'}, label: {}}, categoryAxis: {title: {visible: true}}, plotArea: {dataLabel: {visible: true}}, legend: {visible: true}, general:{groupData: false} }" width="100%"> <viz:dataset> <viz.data:FlattenedDataset id="flattened1" data="{json>/AdlAdpItems}"> <viz.data:dimensions> <viz.data:DimensionDefinition id="Date" name="Rolling12Months" value="{json>AgingPeriod}"/> </viz.data:dimensions> <viz.data:measures> <viz.data:MeasureDefinition id="ADL" name="AverageDaysLate" value="{json>AverageDaysLate}" /> <viz.data:MeasureDefinition id="ADP" name="AverageDaysToPayment" value="{json>AverageDaysToPayment}" /> </viz.data:measures> </viz.data:FlattenedDataset> </viz:dataset> <viz:feeds> <viz.feeds:FeedItem id="ADLMeasure" uid="valueAxis" type="Measure" values="AverageDaysLate,AverageDaysToPayment" /> <viz.feeds:FeedItem id="DateDimension" uid="categoryAxis" type="Dimension" values="Rolling12Months" /> </viz:feeds> </viz:VizFrame> </objectPage:ObjectPageSubSection> </objectPage:ObjectPageSection> </core:FragmentDefinition>
In the OPExtensionController.js file generated in step 2, enter the following code:
<your initials>needs to be replaced by the initials chosen for your project name. E.g., if the project name is creditaccounts.appvar.cl1 then you need to replace<your initials>with cl1.JavaScript/*** @controller Name:sap.suite.ui.generic.template.ObjectPage.view.Details, *@viewId:fin.fscm.cr.creditaccounts.manage::sap.suite.ui.generic.template.ObjectPage.view.Details::CrdtMBusinessPartner */ /*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define([ 'sap/ui/core/mvc/ControllerExtension', 'sap/ui/model/json/JSONModel', 'customer/creditaccounts/appvar/<your initials>/changes/coding/utils/FunctionCallExecutor', 'customer/creditaccounts/appvar/<your initials>/changes/coding/utils/ModelGenerator' ], function ( ControllerExtension, JSONModel, FunctionCallExecutor, ModelGenerator ) { "use strict"; return ControllerExtension.extend("customer.creditaccounts.appvar.<your initials>.OPExtensionController", { _pageDataLoaded: function() { const _sUrl = this.getView().getBindingContext().getPath() + "/to_CrdtMAcctPaytBehaviorTrend"; FunctionCallExecutor.getChartData(this, _sUrl) .then( function (oRes) { const oChartModel = ModelGenerator.generateDataModelForAdlChart( oRes ); const oModel = new JSONModel(oChartModel); const oExtension_chart = this.getView().byId( "customer.creditaccounts.appvar.<your initials>.mysection" ); oExtension_chart.setModel(oModel, "json"); const maxADPADL = FunctionCallExecutor.getMax( oModel.getData().AdlAdpItems ); const oChart = this.getView().byId( "customer.creditaccounts.appvar.<your initials>.AdlChartID" ); oChart.setVizProperties({ valueAxis: { title: { text: this.getView().getModel("i18n").getResourceBundle().getText("AVERAGE_DAYS") } }, categoryAxis: { title: { text: this.getView().getModel("i18n").getResourceBundle().getText("ROLLING_12_MONTHS") } }, plotArea: { primaryScale: { fixedRange: true, maxValue: maxADPADL, }, dataLabel: { renderer: function (oLabel) { if (oLabel.val === 9999) { oLabel.text = ""; } }, visible: true, }, dataPointStyle: { rules: [ { dataContext: { "AverageDaysLate": "*", }, properties: { color: "sapUiChartPaletteQualitativeHue2", }, displayName: this.getView().getModel("i18n").getResourceBundle().getText("MEASURE1"), }, { dataContext: { "AverageDaysToPayment": "*", }, properties: { color: "sapUiChartPaletteQualitativeHue1", }, displayName: this.getView().getModel("i18n").getResourceBundle().getText("MEASURE2"), }, { dataContext: [ { "AverageDaysLate": { min: 9999, max: 9999 }, }, { "AverageDaysToPayment": { min: 9999, max: 9999, }, }, ], properties: { color: "sapUiChartPaletteSemanticNeutralLight3", }, displayName: this.getView().getModel("i18n").getResourceBundle().getText("NO_DATA"), }, ], }, }, }); }.bind(this) ); }, override: { onInit: function() { const oExtensionAPI = this.base.templateBaseExtension.getExtensionAPI(); oExtensionAPI.attachPageDataLoaded( this._pageDataLoaded.bind(this) ); } } }); });
OPExtensionController code snippet
To ensure that the texts for the payment trend chart are displayed properly, you must provide the following texts in your workspace, under webapp –> i18n –> ObjectPage –> CrdtMBusinessPartner –> i18n.properties:

I18n:
#Make sure you provide a unique prefix to the newly added keys in this file, to avoid overriding of SAP Fiori application keys.
#XTIT
EXTENSION_SECTION1_TITLE=Payment Trend
#XTIT
FEED_ITEM1=Average Days Late,Average Days To Payment
#XTIT
FEED_ITEM2=Rolling 12 Months
#XTIT
DIMENSION1=Rolling 12 Months
#XTIT
MEASURE1=Average Days Late
#XTIT
MEASURE2=Average Days To Payment
#XTIT
AVERAGE_DAYS=Average Days
#XTIT
ROLLING_12_MONTHS=Rolling 12 Months
#XTIT
NO_DATA=No DataIn your workspace, under webapp –> changes –> coding, you need to create a utils subfolder with a FunctionCallExecutor.js file and a ModelGenerator.js file. To create the new folder, right-click the coding folder and select
New Folderand enter the name utils.In your newly created utils folder, you need to create two new .js files FunctionCallExecutor.js and ModelGenerator.js. To do this, right-click the utils folder and select
New Fileand type in the name FunctionCallExecutor.js and repeat the same for ModelGenerator.js.
In the FunctionCallExecutor.js file, insert the following code:
JavaScriptsap.ui.define([], function () { return { getChartData: function (oController, sURL) { const _oModel = oController.getView().getModel(); return new Promise(function (resolve, reject) { _oModel.read(sURL, { urlParameters: { $top: 100, $skip: 0, }, success: function (oResponse) { resolve(oResponse); }, }); }); }, getMax: function (aData) { const aRelevant = []; let iMaxrounded; aData.forEach(data => { if (data.AverageDaysToPayment != 9999) { aRelevant.push(data.AverageDaysToPayment); } if (data.AverageDaysLate != 9999) { aRelevant.push(data.AverageDaysLate); } }); const iMax = Math.max(...aRelevant); if (!(iMax > 0)) { iMaxrounded = 0; } else if (iMax > 100) { iMaxrounded = Math.ceil(iMax / 100) * 100; } else if (iMax < 100) { iMaxrounded = Math.ceil(iMax / 10) * 10; } return iMaxrounded; } }; });
FunctionCallExecutor.js snippet In the ModelGenerator.js file, insert the following code:
JavaScriptsap.ui.define([], function () { return { generateDataModelForAdlChart: function (oResponse) { const _oData = oResponse.results; let oResult = {}, aResult = [], sADL = "", sADP = "", sPeriod = "", sPeriodConcatenated = "", sYear = "", sMonth = ""; _oData.forEach(data => { sADL = data.AverageDaysLate; sADP = data.AverageDaysToPayment; sPeriod = data.AgingPeriod; sYear = sPeriod.substring(0, 4); sMonth = sPeriod.substring(4, 6); if (sPeriod !== "") { sPeriodConcatenated = [sYear, sMonth].join("/"); } if (data.AverageDaysLateIsNotAvailable === true) { sADL = 9999; } if (data.AvgDaysToPaymentIsNotAvailable === true) { sADP = 9999; } oResult = data; oResult.AverageDaysLate = sADL; oResult.AverageDaysToPayment = sADP; oResult.AgingPeriod = sPeriodConcatenated; aResult.push(oResult); }); return { AdlAdpItems: aResult, }; } }; });
ModelGenerator.js snippet
For an overview of all the coding changes you’ve made, see the project folder structure for your application variant, under changes –> coding.

For more information, see
- Add Fragments to an Aggregation or Extension Point
- Controller Extensions
- Upgrade Safe Compatibility Rules
- Internationalization
After you’ve finished making the requested changes to your application variant, you can preview the changes made. To do this, expand the webapp folder in your SAP Business Application workspace and right-click on the manifest.appdescr_variant file. In the context menu, choose Preview Application.
Preview the Adaptation Project
The Object Page of our newly created application variant:
- Includes new section

- Displays payment trends in a chart

In this developer tutorial, you have learned:
- How to extend the source code of your application variant.
- How to add a new section.
- How to visualize Payment Trends as a simple chart.
Test your knowledge
After completing this tutorial, you can learn more about the end-to-end process with:
Additional information
You can define additional changes in application variants (not covered in this blog):
- Add local annotation files to overwrite annotations of the original SAP Fiori application.
See Adding local annotation files.
- Extend the RAP-based OData service of the original SAP Fiori application. By extending the original OData service additional changes are possible (for example, define new actions, visualize additional data, change input help, change check logic).
See Extend.
Supported applications
To determine if a specific application can be extended by UI Adaptation Project, see SAP Fiori Apps Reference Library (ondemand.com) and check the application’s implementation information under Extensibility (extensibility of the UI5 application in the front-end server).
If the SAP-delivered application you want to extend with an application variant is not yet supported, please submit a request here: Influence Opportunity Homepage - Customer Influence (sap.com)
If you are a partner and want to ensure that customers can extend your partner-developed application by UI Adaptation Project, you must follow the steps as outlined in SAP Help Portal:
Resources
Discussion
Share feedback on this tutorial or join the conversation in SAP Community.