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
Manage Credit Accounts: https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps(‘F4596’)/S23OP
- 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
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": {}
}
}
}
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 .
Create a New Fragment as shown below
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
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 .
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
- 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);
}
}
- 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);
});
Thus you can create read-only Facet Extensions with Modification Free Extension technique .