SAP Business Application Studio, ABAP RESTful Application Programming Model, SAP Fiori, SAP Fiori Elements, SAPUI5

Invoking Static BOPF Action From Custom Pop up in Fiori Elements App

Introduction:

Many times while working with Transactional List Report Apps developed using SAP Fiori Elements we might have requirements to get dynamic input from the end user, like providing a popup where user makes selection and then we need to take actions accordingly.

In order to develop transactional App SAP provided us 2 programming model which are as follows.

  1. ABAP Programming model for Fiori ( Developed using Business Object Processing Framework-BOPF)
  2. RestFul Application Programming Model(RAP)

In this blog we will see how to invoke API Actions using ABAP Programming model for Fiori

Scenario:

Let us consider an e.g., of Sales Order processing, where the end user after entering Item details needs an option to select additional Charges.

The additional charges could be combination of one or more of the below charges.

  1. Freight Charges
  2. Miscellaneous charges
  3. Other Charges

So below screen shot shows that the user can enter Additional Charges from the popup by pressing the Additional charges Button and in the backend, we need to add these charges to the Total of gross amount for the item selected and the value should be reflected in the front-end Fiori App.

In order to achieve this, we will follow below steps as mentioned in Flowchart.

So let us see each step.

Implementation Steps:

Step 1: Creating Sales Order Header and Item Database table

We create Custom Sales header Table ZSOH, where we use SoKey which is 16-digit GUID and primary Key for table. The same will be used to generate Draft key in Draft Table.

@EndUserText.label : 'Sales Order Header'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zsoh {
  @AbapCatalog.foreignKey.screenCheck : false
  key client : mandt not null
    with foreign key [0..*,1] t000
      where mandt = zsoh.client;
  key sokey  : snwd_node_key not null;
  soid       : snwd_so_id;
  bpid       : snwd_partner_id;
  include /bobf/s_lib_admin_data;

}

Next we create Item table ZSOI where we use SOITMKEY as 16 digit GUID and we include fields to get product id, Gross Amount, Currency and Additional Charges as shown below.

@EndUserText.label : 'SO Item Table'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zsoi {
  @AbapCatalog.foreignKey.screenCheck : false
  key client   : mandt not null
    with foreign key [0..*,1] t000
      where mandt = zsoi.client;
  key soitmkey : snwd_node_key not null;
  @AbapCatalog.foreignKey.screenCheck : false
  sokey        : snwd_node_key
    with foreign key [0..*,1] zsoh
      where sokey = zsoi.sokey;
  soitm        : snwd_so_item_pos;
  product      : snwd_product_id;
  @Semantics.amount.currencyCode : 'zsoi.currencycode'
  grossamount  : snwd_ttl_gross_amount;
  currencycode : snwd_curr_code;
  quantity     : int4;
  @Semantics.amount.currencyCode : 'zsoi.currencycode'
  addcharge    : snwd_ttl_gross_amount;
  include /bobf/s_lib_admin_data;

}

Step 2: Create Interface View for Header and Item Tables, also generate the BOPF object and Draft table using @ObjectModel Annotation.

We Create ZI_SOH interface view to generate BOPF object and we also mark the Create, Update and Delete Enabled as true. We define ZSOH_DRAFT as Draft Table. Below is the code for the same

@AbapCatalog.sqlViewName: 'ZISOH'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Interface view for BO with Draft'
@ObjectModel: {
    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true,
    draftEnabled: true,    
    modelCategory:#BUSINESS_OBJECT ,    
    semanticKey: ['Soid'],   
    compositionRoot: true,
    transactionalProcessingEnabled: true,        
    writeDraftPersistence: 'ZSOH_DRAFT',  
    writeActivePersistence: 'ZSOH',
    entityChangeStateId: 'Lchg_Date_Time'    
}
define view ZI_SOH as select from ZSOH as soh
association [1..*] to ZI_SOI as _itm on $projection.Sokey = _itm.Sokey
association [0..1] to SEPM_I_BusinessPartner as _bpa on $projection.Bpid = _bpa.BusinessPartner
{
@ObjectModel.readOnly: true    
    key soh.sokey as Sokey,
@Search.defaultSearchElement: true    
    soh.soid as Soid,
@ObjectModel.foreignKey.association: '_bpa'    
    soh.bpid as Bpid,
@Semantics.systemDateTime.createdAt: true    
    soh.crea_date_time as Crea_Date_Time,
@Semantics.user.createdBy: true    
    soh.crea_uname as Crea_Uname,
@Semantics.systemDateTime.lastChangedAt: true    
    soh.lchg_date_time as Lchg_Date_Time,
@Semantics.user.lastChangedBy: true
    soh.lchg_uname as Lchg_Uname,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]    
    _itm    
}

Similarly we create Item Interface view as shown below and use ZSOI_DRAFT as draft table

@AbapCatalog.sqlViewName: 'ZISOI'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Item BO Draft'
@ObjectModel: {
    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true,          
    semanticKey: ['Sokey','Soitm'],
    writeDraftPersistence: 'ZSOI_DRAFT',
    writeActivePersistence: 'ZSOI'    
}
define view ZI_SOI as select from zsoi as soi
association [1..1] to ZI_SOH           as _soh  on $projection.Sokey = _soh.Sokey 
association [0..1] to SEPM_I_Product_E as _pd   on $projection.Product = _pd.Product 
association [0..1] to SEPM_I_Currency  as _curr on $projection.Currencycode = _curr.Currency         
{     
    key soi.soitmkey as Soitmkey,    
    soi.sokey as Sokey,
@ObjectModel.readOnly: true
    soi.soitm as Soitm,
@ObjectModel.foreignKey.association: '_pd'   
    soi.product as Product,
@ObjectModel.readOnly: true
@Semantics.amount.currencyCode: 'Currencycode'
    soi.grossamount as Grossamount,    
@ObjectModel.foreignKey.association: '_curr'  
@Semantics.currencyCode: true
    soi.currencycode as Currencycode,
    soi.quantity as Quantity,  
@Semantics.amount.currencyCode: 'Currencycode'
@ObjectModel.readOnly: true
    soi.addcharge as AddCharge,  
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT,#TO_COMPOSITION_ROOT]    
    _soh,    
    _pd,
    _curr

}

We get the BOPF generated below is the screen shot for the same with Parent Child Relationship.

Step 3: Create Consumption View, in which we use UI annotations to create Fiori Elements App.

We Create 2 Consumption CDS view ZC_SOH and ZC_SOI and use UI annotations so that it will create all the required controls in fiori elements app.

@AbapCatalog.sqlViewName: 'ZCSOH'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Consumption SO Hdr BO Draft'
@ObjectModel: {
    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true,
    draftEnabled: true,
    
    semanticKey: ['Soid'],   
    compositionRoot: true,  
    transactionalProcessingDelegated: true   
}
@UI.headerInfo: {
    typeName: 'Sales Order',
    typeNamePlural: 'Sales Orders',    
    title: {
        type: #STANDARD,        
        value: 'Bpid'       
    },
    description: {
        type: #STANDARD,        
        value: 'Soid'        
    }
}
@VDM.viewType: #CONSUMPTION
@OData.publish: true
define view ZC_SOH as select from ZI_SOH 
association [0..*] to ZC_SOI as _itm on $projection.Sokey = _itm.Sokey
association [0..1] to SEPM_I_BusinessPartner as _bpa on $projection.Bpid = _bpa.BusinessPartner
{
@UI.facet: [
{   
    id: 'Hdr',
    purpose: #HEADER,    
    position: 10,   
    type: #FIELDGROUP_REFERENCE,
    targetQualifier: 'HdrInfo'
},
{   
    id: 'SoHdr',
    purpose: #STANDARD,    
    position: 10,   
    label: 'Header Details',
    type: #IDENTIFICATION_REFERENCE    
},
{   
    id: 'SoItm',
    purpose: #STANDARD,    
    position: 20,   
    label: 'Item Details',
    type: #LINEITEM_REFERENCE,
    targetElement: '_itm'
}]
@UI.hidden: true
    key Sokey,
@UI: {
    lineItem: [ { position: 10, label: 'Sales Order ID', importance: #HIGH } ],
    selectionField: [ { position: 10 } ],
    identification:[ { position: 10 } ]
    }    
@UI.fieldGroup: [{ qualifier: 'HdrInfo' , position: 10 }]  
@ObjectModel.readOnly: true  
    Soid,
@UI: {
    lineItem: [ { position: 20, label: 'Customer', importance: #HIGH } ],
    identification: [{ position: 20 }]
    }    
    Bpid,    
    Crea_Date_Time,
    Crea_Uname,
    Lchg_Date_Time,
    Lchg_Uname,
    /* Associations */ 
    @ObjectModel.association.type: [#TO_COMPOSITION_CHILD]     
    _itm,
    _bpa
}
@AbapCatalog.sqlViewName: 'ZCSOI'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'consumption view for BOD SOI'
@ObjectModel: 
{
    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true,      
    semanticKey: ['Sokey', 'Soitm']
  }
@UI: {
    headerInfo: {
        typeName: 'Sales Order Item',
        typeNamePlural: 'Sales Order Items',
        title: { type: #STANDARD, value: '_soh.Bpid' },
        description:{ type: #STANDARD, value: '_soh.Soid'  }
    }
 }
  
define view ZC_SOI as select from ZI_SOI as soi
association [1..1] to ZC_SOH as _soh
    on $projection.Sokey = _soh.Sokey 
association [0..1] to SEPM_I_Product_E as _pd
    on $projection.Product = _pd.Product 
association [0..1] to SEPM_I_Currency as _curr 
    on $projection.Currencycode = _curr.Currency    
{
@UI.facet: [{    
    id: 'Item',
    purpose: #STANDARD,    
    position: 10,   
    label: 'Item Details',
    type: #IDENTIFICATION_REFERENCE   
},
{    
    id: 'IHdr',
    purpose: #HEADER,    
    position: 10,       
    type: #FIELDGROUP_REFERENCE,
    targetQualifier: 'IHdrItm'
}]
@UI.hidden: true
    key soi.Soitmkey as Soitmkey,
@UI.hidden: true    
    soi.Sokey as Sokey,
@UI.fieldGroup: [{ qualifier: 'IHdrItm' , position: 10 }]    
@UI.lineItem: [{position: 10 , importance: #HIGH}]  
@UI.identification: [{position: 10 }]   
@ObjectModel.readOnly: true    
    soi.Soitm as Soitm,
@UI.lineItem: [{position: 20 , importance: #HIGH}]  
@UI.identification: [{position: 20 , importance: #HIGH }] 
@ObjectModel.foreignKey.association: '_pd'   
    soi.Product as Product,
@UI.lineItem: [{position: 40 ,importance: #HIGH}]
@ObjectModel.readOnly: true
@Semantics.amount.currencyCode: 'Currencycode'    
    soi.Grossamount as Grossamount,
@ObjectModel.foreignKey.association: '_curr'  
@Semantics.currencyCode: true    
    soi.Currencycode as Currencycode,
@UI.lineItem: [{position: 30 , label : 'Quantity', importance: #HIGH}]    
@UI.identification: [{position: 40, label : 'Quantity' }]    
    soi.Quantity as Quantity,
@UI.lineItem: [{position: 40, importance: #HIGH, label: 'Additional Charges' }]
@ObjectModel.readOnly: true
    soi.AddCharge,    
    /* Associations */
    _curr,
    _pd,
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT,#TO_COMPOSITION_ROOT]    
    _soh       
}

Step 4: Create 2 determinations in BOPF as follows

1. Generate the Sales order number in Sales order Header Node:

This Determination will generate Sales Order number for create Scenario. Below is the code.

Below is the code implemented to generate the SO Id number.

class ZCL_I_D_CALC_SO_ID definition
  public
  inheriting from /BOBF/CL_LIB_D_SUPERCL_SIMPLE
  final
  create public .

public section.

  methods /BOBF/IF_FRW_DETERMINATION~EXECUTE
    redefinition .
protected section.
private section.
ENDCLASS.


CLASS ZCL_I_D_CALC_SO_ID IMPLEMENTATION.


  method /BOBF/IF_FRW_DETERMINATION~EXECUTE.
  DATA: itab TYPE ZTISOH.

    CALL METHOD io_read->retrieve
      EXPORTING
        iv_node = is_ctx-node_key
        it_key  = it_key
      IMPORTING
        et_data = itab.
    LOOP AT itab REFERENCE INTO DATA(lr_tab).
      IF lr_tab->soid IS INITIAL.
        CALL FUNCTION 'NUMBER_GET_NEXT'
          EXPORTING
            nr_range_nr             = '01'
            object                  = 'RV_BELEG'
          IMPORTING
            number                  = lr_tab->soid
          EXCEPTIONS
            interval_not_found      = 1
            number_range_not_intern = 2
            object_not_found        = 3
            quantity_is_0           = 4
            quantity_is_not_1       = 5
            interval_overflow       = 6
            buffer_overflow         = 7
            OTHERS                  = 8.
        IF sy-subrc <> 0.
*             MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
*               WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
        ENDIF.
        CALL METHOD io_modify->update
          EXPORTING
            iv_node           = is_ctx-node_key
            iv_key            = lr_tab->key
            is_data           =  lr_tab
            it_changed_fields =
            VALUE #( ( ZIF_I_SOH_C=>sc_node_attribute-zi_soh-soid ) )
          .
*        CATCH /bobf/cx_frw_contrct_violation.
      ENDIF.
    ENDLOOP.

  endmethod.
ENDCLASS.

We now implement code to generate item number and also, we implement code to set the gross item = product price * Quantity entered by the user.

Below is the code for the same.

CLASS zcl_i_d_calc_soitm_cal_gross DEFINITION
  PUBLIC
  INHERITING FROM /bobf/cl_lib_d_supercl_simple
  FINAL
  CREATE PUBLIC .

PUBLIC SECTION.

  METHODS /bobf/if_frw_determination~execute
    REDEFINITION .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS zcl_i_d_calc_soitm_cal_gross IMPLEMENTATION.


  METHOD /bobf/if_frw_determination~execute.
  DATA:
    itab TYPE ztisoi,
    itab1 TYPE ztisoh,
    li_key TYPE /bobf/t_frw_key.
*   We Retrive the Items of the Sales Order
    CALL METHOD io_read->retrieve
      EXPORTING
        iv_node                 = is_ctx-node_key
        it_key                  = it_key
      IMPORTING
        et_data                 = itab.

  DATA(lw_itm) = VALUE #( itab[ soitm = space ] OPTIONAL ).
  READ TABLE itab WITH KEY soitm = space TRANSPORTING NO FIELDS.
* Get the parent node to get the sales order number because Item does not have Sales order number
  IF sy-subrc = 0.
  CALL METHOD io_read->retrieve_by_association
    EXPORTING
      iv_node                 = zif_i_soh_c=>sc_node-zi_soi
      it_key                  = it_key
      iv_association          = zif_i_soh_c=>sc_association-zi_soi-to_root
      iv_fill_data            = abap_true
    IMPORTING
      et_data                 = itab1
      et_target_key           = li_key  .

  READ TABLE itab1 INTO DATA(lw_tab1) INDEX 1.
  SELECT SINGLE sokey FROM zsoh INTO @DATA(lv_sokey) WHERE soid = @lw_tab1-soid.
  IF sy-subrc <> 0.
    SELECT SINGLE sokey FROM ZSOH_draft INTO lv_sokey WHERE soid = lw_tab1-soid.
  ENDIF.

  WITH +both AS (  SELECT soitm FROM zsoi WHERE sokey = @lv_sokey
    UNION ALL
    SELECT soitm FROM zsoi_draft WHERE sokey = @lv_sokey )
        SELECT SINGLE
        FROM +both
        FIELDS MAX( soitm ) AS salesorderitem
    INTO @DATA(lv_max_salesorderitem).


    " If there are no entries, set a start value
    IF lv_max_salesorderitem IS INITIAL.
        lv_max_salesorderitem = '0000000000'.
    ENDIF.
    ENDIF.

*   Get the Item and also Gross amount
    LOOP AT itab REFERENCE INTO DATA(lr_tab).
    IF lr_tab->soitm IS INITIAL.
      lr_tab->soitm = lv_max_salesorderitem = lv_max_salesorderitem + 10.
      lr_tab->soitm = |{ lr_tab->soitm ALPHA = IN }|.
    ENDIF.


    if lr_tab->quantity IS NOT INITIAL AND lr_tab->product IS NOT INITIAL.
      SELECT SINGLE * FROM snwd_pd INTO @Data(lw_snwd_pd) WHERE product_id = @lr_tab->product.
*     We add the Add Charge + quantity * price
      lr_tab->grossamount = ( lr_tab->quantity * lw_snwd_pd-price ) + lr_tab->addcharge.
      lr_tab->currencycode = lw_snwd_pd-currency_code.
    ENDIF.
    CALL METHOD io_modify->update
      EXPORTING
        iv_node           = is_ctx-node_key
        iv_key            = lr_tab->key
*        iv_root_key       =
        is_data           = lr_tab
        it_changed_fields =
        VALUE #( ( zif_i_soh_c=>sc_node_attribute-zi_soi-soitm )
                 ( zif_i_soh_c=>sc_node_attribute-zi_soi-grossamount )
                 ( zif_i_soh_c=>sc_node_attribute-zi_soi-currencycode ) ).
*    CATCH /bobf/cx_frw_contrct_violation.

   ENDLOOP.

  ENDMETHOD.
ENDCLASS.

We now also activate the OData Service using /IWFND/MAINT_SERVICE TCODE in SAP.

Step 5: Create List Report Elements App

  1. Open VS Code and use command pallet to create Fiori Elements App
  2. View->Command Pallet ->Open Template Wizard
  3. Select Fiori Elements List Report Floor plan, select your S/4 HANA system and give the Service name and project Details.

Next We preview the app

We click Create button on item to create Items.

In the next step we will add Additional Charges Button to allow users to enter additional charges. In order to achieve the same we will be using Extension and Fiori Free Style coding.

Step 6: Using Extension to Create an Additional Charges Button

In order to get additional charges from the user we will perform below steps

  1. Use Extension to create Additional Charges Button
  2. Create a Fragment for creating Table where user will Enter Details
  3. Invoke the Fragment from the Controller.

Create Additional Charges Button we will use Guided Development tool to add Custom button.

View->Command Pallet->Open Guided Development

We give below Details in step 1 and click on Insert Snippet Button so that it creates ObjectPageExt.controller

Page Object Page
Function Name   getAdditionalCharge 

 

In Step 2 we give below Details and click on Insert Snippet, so that we get the details updated in Manifest JSON.

Entity Set ZC_SOH
Action Position Section
Facets Annotation Term ID Item Details
Action ID idGetAddChrg
Button Text Additional Charge
Row Selection Yes

 

Below Code Gets generated in Manifest JSON.

We can see that ext folder is created inside that we get ObjectPageExt.controller.controller.js file as shown below. We will implement code for Invoking Pop up later. In the next step we will create fragment.

Step 7: Create Fragment which we will invoke it from the Extension Function.

  1. This fragment will have responsive table with multiselect option
  2. Two Buttons Copy and Cancel Action.
  3. We do Aggregation Binding on the table using Named JSON Model “Charge”

Below is the XML code for the fragment.

<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc"
	 height="100%">
	<Dialog id="idExtraCharges" title="Other Charges" class="sapUiNoContentPadding">
		<content>
			<VBox id="idChargeVbox" class="sapUiSmallMarginBeginEnd">
				<Table id="idChargeTable" inset="false" items="{Charge>/ExtraChrg}" mode="MultiSelect">
					<columns>
						<Column width="12em">
							<Text text="Charge Type"/>
						</Column>
						<Column minScreenWidth="Tablet" demandPopin="true">
							<Text text="Amount"/>
						</Column>
						<Column minScreenWidth="Desktop" demandPopin="true" hAlign="End">
							<Text text="Currency"/>
						</Column>
					</columns>
					<items>
						<ColumnListItem vAlign="Middle">
							<cells>
								<Text text="{Charge>ChargeType}"/>
								<Input value="{Charge>Amount}"/>								
							</cells>
						</ColumnListItem>
					</items>
				</Table>
			</VBox>
		</content>
		<buttons>
			<Button id="btncopy" text="Copy" press="onActionCopy" />
			<Button id="btncancel" text="Cancel" press="onActionCancel"/>
		</buttons>
	</Dialog>
</core:FragmentDefinition>

Step 8: Implement Logic in Controller to Trigger Fragment as Pop up.

Next we implement the ObjectPageExt Controller.

onInit Method: In this method we define the JSON Model “Charge” which will have 2 fields Charge Type and Amount.Since we have 3 types of Charges we will define Freight Charge, Miscellaneous Charge and Other Charge.Amount would be 0 we will bind fill it from Popup Table. Below is the Code for the same

getAdditionalCharge: In this method we validate if the user is in Edit or Create mode, if not we give error or trigger Pop up. We also get the Details of the line item selected because we will need to send this details to backend to modify values.

In the Pop up we had 2 Fragment we have 2 Buttons Copy and Cancel, when the user clicks Cancel we should come out of the popup and do nothing so below is the code to trigger

Close Pop up.

When the user clicks on Copy, we need to get the details of charges entered by the user and then we need to invoke the BOPF Action from backend, we will come back to this later once we finish the next step of creating Static Action.

Step 9: Create Static BOPF Action

We will create Static Action in BOPF to get the Additional Charges modify the Additional charges amount. We will do below Steps. We create static action in the Item node in BOPF as shown below

We then define the structure for ZADD_CHRG in SAP, this will have 2 parameters Draft ID and Total Coming from Front end system.

We now implement the changes in class ZCL_I_A_SET_ADD_CHRG, because we need to update the additional charge. Below is the code, we get the parameter from the front end, then we add up the gross amount and update the BO.

class ZCL_I_A_SET_ADD_CHRG definition
  public
  inheriting from /BOBF/CL_LIB_A_SUPERCL_SIMPLE
  final
  create public .

public section.

  methods /BOBF/IF_FRW_ACTION~EXECUTE
    redefinition .
protected section.
private section.
ENDCLASS.


CLASS ZCL_I_A_SET_ADD_CHRG IMPLEMENTATION.


  method /BOBF/IF_FRW_ACTION~EXECUTE.
  DATA : ls_parameters TYPE ZADD_CHRG,
         lt_key        TYPE /bobf/t_frw_key,
         ls_key        TYPE /bobf/s_frw_key,
         itab          TYPE ZTISOI.

    "Retrive Source data from Frontend
    DATA(ls_parameters_ptr) = CAST zadd_chrg( is_parameters ).

    MOVE ls_parameters_ptr->* TO ls_parameters.
    ls_key-key = ls_parameters-draftuuid.
    APPEND ls_key TO lt_key.

    call METHOD io_read->retrieve
      EXPORTING
        iv_node                 = ZIF_I_SOH_C=>sc_node-zi_soi
        it_key                  = lt_key
      IMPORTING
        et_data                 =  itab.

   loop at itab REFERENCE INTO data(lr_tab).
    lr_tab->addcharge   = ls_parameters-amount + lr_tab->addcharge.
    lr_tab->grossamount = lr_tab->grossamount + ls_parameters-amount.

    call METHOD io_modify->update
      EXPORTING
        iv_node           = ZIF_I_SOH_C=>sc_node-zi_soi
        iv_key            = lr_tab->key
        is_data           = lr_tab
        it_changed_fields =
        VALUE #( ( ZIF_I_SOH_C=>sc_node_attribute-zi_soi-grossamount )
                 ( ZIF_I_SOH_C=>sc_node_attribute-zi_soi-addcharge )
                 ( ZIF_I_SOH_C=>sc_node_attribute-zi_soi-currencycode ) ).
     ev_static_action_failed = abap_false.
   ENDLOOP.


  endmethod.
ENDCLASS.

It is to be noted that whenever we create Action in BOPF it creates a Function Group in OData Service as shown below in Metadata.

Step 10: Invoking the Action from Front End

We will now implement the final changes in fiori app to trigger the backend static action ZC_SOISet_add_chrg for this we will use Invoke API.

Below is the code for the same.

Below is the final code of the Controller.

sap.ui.controller("fe.so.fesaleorder.ext.controller.ObjectPageExt", {
    onInit: function () {

        var oJson = new sap.ui.model.json.JSONModel({
            "ExtraChrg": []
        });

        var oExtraChrg = [];
        var oCharges = {
            "ChargeType": "Freight",
            "Amount": 0,
        };

        oExtraChrg.push(JSON.parse(JSON.stringify(oCharges)));

        oCharges.ChargeType = "Miscellaneous Charge";
        oExtraChrg.push(JSON.parse(JSON.stringify(oCharges)));

        oCharges.ChargeType = "Other Charge";
        oExtraChrg.push(JSON.parse(JSON.stringify(oCharges)));
        this.getOwnerComponent().setModel(oJson, "Charge");
        this.getOwnerComponent().getModel("Charge").setProperty("/ExtraChrg", oExtraChrg);

    },

    getAdditionalCharge: function (oEvent) {
        debugger;
        // Below is how we get the ID of the Item table
        this._oTable = this.getView().byId(oEvent.getSource().getParent().getParent().getParent().getId()).getTable();
        this._oDetails = this.getView().getModel().getProperty(this._oTable.getSelectedContextPaths()[0]);
        var u = oEvent.getSource().getModel("ui").getData();
        if (u.editable === true || u.createMode === true) {
            if (!this._DialogGenerate) {
                this._DialogGenerate = sap.ui.xmlfragment("fe.so.fesaleorder.ext.fragment.extraCharge", this);
                this.getView().addDependent(this._DialogGenerate);
            }
            this._DialogGenerate.open();
        } else {
            // var i = this._oResourceBundle.getText('@copyInfo');
            sap.m.MessageBox.information("Changes Possible only in Create and Edit Mode");
        }
    },

    onActionCancel: function () {
        this._DialogGenerate.close();
        this._DialogGenerate.destroy();
        this.getView().removeDependent(this._DialogGenerate);
        this._DialogGenerate = null;
    },    

    onActionCopy: function (oEvent) {
        var oPromise;
        var that = this;
        var oApi = this.extensionAPI;
        var oParameter = {};
        //    Get Selected row in item table
        var ochargeDet = sap.ui.getCore().byId("idChargeTable").getSelectedContextPaths();
        var oTot = 0;
        for (var i = 0; i < ochargeDet.length; i++) {
            var oCharges = this.getOwnerComponent().getModel("Charge").getProperty(ochargeDet[i]);
            oTot = parseFloat(oCharges.Amount) + oTot;
        }        
        // ***************Implment Invoke Action after Creating Static BOPF Action*********
        oParameter = {
            "Draftuuid": this._oDetails.Soitmkey,
            "Amount": oTot,
            "Currency" :  'EUR'
        };
        oPromise = this.extensionAPI.invokeActions("ZC_SOH_CDS.ZC_SOH_CDS_Entities/ZC_SOISet_add_chrg", [],
            oParameter);
        oPromise.then(function (r) {
            that.getView().getModel().refresh(true, false, "CopyRefresh");
            sap.m.MessageToast.show("Charges Added Successfully to Item" + that._oDetails.Soitm);

        });
        oPromise.catch(function (r) {
            var E = jQuery.parseJSON(r[0].error.response.responseText);
            sap.m.MessageBox.error(E.error.message.value);
        });

        this.onActionCancel();
    }

});

Let us test the application as follows.

We click on Create Sales Order

We create item

Click on Addtional Charges Button by selecting item.