Skip to Content
Technical Articles
Author's profile photo Vijay Chintarlapalli

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

Manage Credit Accounts: 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 .

 

Assigned Tags

      6 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Syambabu Allu
      Syambabu Allu

      Hi Vijay,

      Thanks for sharing..it’s really helpful!!

      Thank you,

      Syam

      Author's profile photo Prabhjot Bhatia
      Prabhjot Bhatia

      Hi Vijay,

      Very well explained.

      Just want to add an important aspect from performance point -of-view, we should always use lazy loading concept for additional/extended sections so that data for that section will be fetched on demand when it is requested.

      Thanks and regards,

      Prabhjot

       

      Author's profile photo Philipp Hoffmann
      Philipp Hoffmann

      Great Blog Vijay - helped us a lot at a customer implementation.

      One remark:

      The step "Now add the Custom ODATA RAP based Service in the manifest.json" wasn't working for us when we tried to update the datasource and model directly in the "manifest.json" - it resulted in some error.

      When adding them via the specific menu entries of the BAS everything worked fine.

      Cheers, Philipp

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hello Vijay Chintarlapalli,

      isn't there also an option to re-define the Standard OData Service and add the custom entity there? Then you could replace the datasource of the default model and the extension to bind to another model would not be needed.

      Best Regards
      Gregor

      Author's profile photo Vijay Chintarlapalli
      Vijay Chintarlapalli
      Blog Post Author

      Hi Gregor Wolf ,

      The above examples are extensions of the standard Fiori applications which are already delivered using the RAP model and no RAP Extensibility is provided .

      And yes we can re-define the standard Odata Service if its SEGW based ones.
      Thanks,
      Vijay
      Author's profile photo Mohamed Medhat
      Mohamed Medhat

      unfortionatlly not able to navigate to object page any more using BAS UI5 Visual Editor