Extension of standard RAP based Fiori Object Page facet using Custom entity ODATA and UI5 Adaptation project

Hello Experts,

Since the newly delivered SAP applications from Version 2021 S4 On-Premise will be based on the RAP based Fiori application,there could be multiple extension scenariosof the Fiori applications out of which adding a new facet in the object page will be most commonly requested scenario.

Below are the different steps to be followed for the RAP based scenario.

If the scenario is to extend a classical Odata based applciation you can follow the below blog :

http://www.hanaexam.com/2021/05/adaptation-project-new-facet-with-smart.html

Extensibility features of RAP, such as extending behavior definition is not available as part of S/4HANA 2021 any-premise release .

We have to follow the below procedure :

For example we will be analyzing adding a new facet to show the open items in the Fiori application

https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps(‘F4596’)/S23OP

List%20Report

List Report

Object%20Page

Object Page

  • Create  a Custom Odata RAP based service ,In this example i have used a custom Entity to fetch the open items
@Metadata: { allowExtensions: true }
@ObjectModel: { query: { implementedBy: 'ABAP:ZCLOPENITEMS' } }
@EndUserText.label: 'Open Items'
define custom entity ZcopenItems
{ key CompanyCode : fis_bukrs; AccountingDocument : farp_belnr_d; FiscalYear : fis_gjahr; AccountingDocumentItem : fis_buzei; ClearingDate : fis_augdt; ClearingAccountingDocument : fis_augbl; PostingKey : fis_bschl; FinancialAccountType : farp_koart; Supplier : md_supplier; Customer: kunnr; GLAccount : fis_racct; PostingDate : fis_budat; CompanyCodeCurrency : fis_hwaer; @Semantics.amount.currencyCode : 'CompanyCodeCurrency' AmountInCompanyCodeCurrency : fis_hsl;
}

Implementation of the query Class :

class ZCLOPENITEMS definition public inheriting from CL_RAP_QUERY_PROVIDER_SELECTOR final create public .
public section. interfaces IF_SADL_EXIT . interfaces IF_RAP_QUERY_PROVIDER . data COMP_CODE type BUKRS . data CUSTOMER type KUNNR . data: output TYPE STANDARD TABLE OF ZcopenItems WITH EMPTY KEY .
protected section.
private section. methods GET_OPEN_ITEMS importing !OFFSET type INT8 !PAGE_SIZE type INT8 .
ENDCLASS.
CLASS ZCLOPENITEMS IMPLEMENTATION. METHOD get_open_items. DATA : openitems_o TYPE ZcopenItems, table_size TYPE count. DATA(max_rows) = COND #( WHEN page_size = if_rap_query_paging=>page_size_unlimited THEN 0 ELSE page_size ). SELECT CompanyCode, AccountingDocument, FiscalYear, ClearingDate , ClearingAccountingDocument, PostingDate, FinancialAccountType, Supplier, Customer, GLAccount, PostingKey, CompanyCodeCurrency, AmountInCompanyCodeCurrency FROM I_OperationalAcctgDocItem WHERE FiscalYear EQ '2022' AND customer EQ @customer AND ClearingAccountingDocument IS INITIAL AND AccountingDocumentCategory NE 'D' AND AccountingDocumentCategory NE 'M' INTO TABLE @DATA(open_items). LOOP AT open_items ASSIGNING FIELD-SYMBOL(<open_items>). IF sy-tabix > offset. MOVE-CORRESPONDING <open_items> TO openitems_o . APPEND openitems_o TO output. CLEAR openitems_o. table_size = lines( output ). IF max_rows IS NOT INITIAL AND sy-tabix >= max_rows. EXIT. ENDIF. ENDIF. ENDLOOP. ENDMETHOD. METHOD if_rap_query_provider~select. TRY. DATA(filter) = io_request->get_filter( )->get_as_ranges( ). LOOP AT filter ASSIGNING FIELD-SYMBOL(<filter>). CASE <filter>-name. WHEN 'CUSTOMER'. Customer = VALUE #( <filter>-range[ 1 ]-low OPTIONAL ). WHEN 'COMPANYCODE'. comp_code = VALUE #( <filter>-range[ 1 ]-low OPTIONAL ). WHEN OTHERS. ENDCASE. ENDLOOP. get_open_items( offset = io_request->get_paging( )->get_offset( ) page_size = io_request->get_paging( )->get_page_size( ) ). IF io_request->is_total_numb_of_rec_requested( ). DATA(count) = CONV int8( lines( output ) ). io_response->set_total_number_of_records( count ). ENDIF. IF io_request->is_data_requested( ). io_response->set_data( output ). ENDIF. CATCH cx_rap_query_response_set_twic. CATCH cx_rap_query_filter_no_range.
* Forward exeption RAISE EXCEPTION TYPE cx_rap_message_error EXPORTING textid = VALUE #( msgid = 'SY' msgno = '499' attr1 = |Filter must be provided| ). CATCH cx_rap_query_provider. ENDTRY. ENDMETHOD.
ENDCLASS. 

Generated Service biding from Service definition

  • Now Create the Adaptation project for extending the Object Page with new section as shown in the below steps :
    • From the BAS go to File – > New Project from Template

adaptation%20project%20step1

adaptation project

Standard%20Application%20selection

Standard Application selection

Now add the Custom ODATA RAP based Service in the manifest.json

"changeType": "appdescr_app_setTitle", "content": { "dataSource": { "customer.openitems": { "uri": "/sap/opu/odata/sap/ZUI_OPENITEMS/", "type": "OData", "settings": { "odataVersion": "2.0" } } }, "model": { "customer.openitems": { "dataSource": "customer.openitems", "settings": {} } } }

Manifest.json

Manifest.json

Now preview the application by right clicking on the manifest.json and open with Visual Editor ,

navigate to the object page and add a new section by selection whole page in edit mode and also create a  controllerextension .

Add%20a%20section%20in%20Object%20Page

Add a section in Object Page

Create a New Fragment as shown below Fragment%20Creation

Fragment Creation

Add the below Smart table in the Extended Fragment

<!-- Use stable and unique IDs!-->
<core:FragmentDefinition xmlns:core='sap.ui.core' xmlns:uxap='sap.uxap' xmlns='sap.m' xmlns:table="sap.ui.table" xmlns:mvc="sap.ui.core.mvc" xmlns:u="sap.ui.unified" xmlns:smartFilterBar="sap.ui.comp.smartfilterbar" xmlns:smartTable="sap.ui.comp.smarttable" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:app="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1" controllerName="customer.zz1mangcreacctext.OpenItems" height="100%"> <uxap:ObjectPageSection id="sample.Id" title="Open Items"> <uxap:subSections> <uxap:ObjectPageSubSection id="idOpen_Items" title="Open Items"> <uxap:blocks> <VBox id="idVboxMain" fitContainer="true"> <Text id="idTextSection"></Text> <smartTable:SmartTable id="idOpenItems" entitySet="ZcopenItems" tableType="Table" useVariantManagement="true" useTablePersonalisation="true" header="Open Items" showRowCount="true" enableAutoBinding="true" class="sapUiResponsiveContentPadding" showFullScreenButton="true" placeToolbarInTable="true" initiallyVisibleFields="CompanyCode,AccountingDocument,FiscalYear,AccountingDocumentItem,Customer " initialNoDataText="Loading..." requestAtLeastFields="CompanyCode,AccountingDocument,FiscalYear,AccountingDocumentItem,Customer"> <smartTable:layoutData > <FlexItemData growFactor="1" baseSize="0%" id="idFid1"/> </smartTable:layoutData> </smartTable:SmartTable> </VBox> </uxap:blocks> </uxap:ObjectPageSubSection> </uxap:subSections> </uxap:ObjectPageSection>
</core:FragmentDefinition>

Open Items section added as shown below, you can also change the sequence of the section

Fragment%20Display

Fragment Display

However the view model is not set from the model defined in the manifest.json. To Bind the same extend the controller as shown below using the visual editor and add the binding method .

Controller%20Extension

Controller Extension

ID mentioned should be from the Smart Table in the fragment

onBeforeRendering: function() { let oComponent = this.getView().getController().getOwnerComponent(), oModel = oComponent.getModel("customer.openitems"); this.byId("idOpen_Items").setModel(oModel); }, onAfterRendering: function() { },

Next Challenge would be to pass the Filter to the Service from the List Page or Object page Header .

Since smart table is auto binded to oData service it will return complete data of entityset. So here Odata has to be filtered while loading the data .

For this you can use the below two approaches by overriding the OnInit() function as shown below

ExtensionAPI:

SAP ListReport Fiori elements base controller is accessed by this.base templateBaseExtension provides public API for SAP Fiori elements extensions, like the extensionAPI

  1. Use Extension API and call beforeRebindTable  inside attachPageDataLoaded function
  • Define the below function in the Smart table

beforeRebindTable=”.extension.customer.zz1mangcreacctext.OpenItems.onBeforeRebindTable”

and implement below in the controller extension

 onBeforeRebindTable: function (oEvent) { var binding = oEvent.getParameter("bindingParams"); let oComponent = this.getView().getController().getOwnerComponent(); let oCustdata = oComponent.getModel("filterModel").getData(); if (oCustdata) { var oFilter = new sap.ui.model.Filter({ filters: [ new sap.ui.model.Filter("Customer", "EQ", oCustdata.Customer), ], and: true }); binding.filters.push(oFilter); } }
  1. Use method attachBeforeRebindTable in the Extension API attachPageDataLoaded function
onInit: function() { let that = this; this.oExtensionAPI = this.base.templateBaseExtension.getExtensionAPI(); this.oExtensionAPI.attachPageDataLoaded(function(oEvent) { let oSmartTable = that.getView().byId("fin.fscm.cr.creditaccounts.manage::sap.suite.ui.generic.template.ObjectPage.view.Details::CrdtMBusinessPartner--customer.zz1mangcreacctext.idOpenItems"); if (oSmartTable) { oSmartTable.attachBeforeRebindTable(function(oEvent1) { var oCustdata = oEvent.context.getObject(); var oFilter = new sap.ui.model.Filter({ filters: [ new sap.ui.model.Filter("Customer", "EQ", oCustdata.Customer), ], and: true }); oEvent1.getParameter("bindingParams").filters.push(oFilter); }); } oSmartTable.rebindTable(true); });

Extended%20Facet

Extended Facet

Thus you can create read-only Facet Extensions with Modification Free Extension technique .