Why We Use Virtual Elements in RAP?

Why We Use Virtual Elements in RAP?

SAP RAP

In RAP applications, not every field should be stored in the database.

Sometimes, we need a field that:

  • Is calculated at runtime

  • Depends on other entities

  • Should not be persisted

  • Should be displayed in UI

  • Should not affect the database schema

This is where Virtual Elements come into the picture.

In this blog, we will:

  • Understand what virtual elements are

  • See where they can be defined

  • Implement a virtual field TotalAmount

  • Write the calculation exit class

  • Understand how the exit class works


What Are Virtual Elements?

A virtual element is a calculated field in a CDS projection/consumption view.

It:

  • Does not exist in the database table

  • Is calculated dynamically at runtime

  • Is not persisted

  • Is read-only by design

Virtual elements are calculated using a global ABAP class that implements a SADL exit interface.

Virtual elements can only be defined in projection (consumption) views, not in interface views.


Our Use Case

In our Billing Document app:

  • Header entity has NetAmount

  • Item entity has ItemAmount

  • One billing document can have multiple items

We want to calculate:

TotalAmount = Sum of all ItemAmount values of that Billing Document

We do not want to:

  • Store this in database

  • Maintain it manually

  • Use determinations

Instead, we compute it dynamically.


Steps to implement Virtual Element

Step 1 – Define Virtual Field in Projection View

We add TotalAmount in the Billing Document Header consumption view zsac_c_bill_header.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Consumption view for bill doc head'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@VDM.viewType: #CONSUMPTION
define root view entity zsac_c_bill_header 
  provider contract transactional_query
  as projection on zsac_r_bill_header 
{
  key BillId,
      BillType,
      BillDate,
      CustomerId,

      @Semantics.amount.currencyCode: 'Currency'
      NetAmount,

      Currency,
      SalesOrg,

      @Semantics.amount.currencyCode: 'Currency'
      @ObjectModel.virtualElement: true
      @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZSAC_CL_AMOUNT_VE_EXIT'
      virtual TotalAmount : abap.curr(15,2),

      CreatedBy,
      CreateDat,
      LastChangedBy,
      LastChangeDat,
      LocalLastChangeDat,

      _Item : redirected to composition child zsac_c_bill_item
}

Important Annotations

@ObjectModel.virtualElement: true Marks the field as virtual.

@ObjectModel.virtualElementCalculatedBy Specifies the ABAP class responsible for calculation. The class must:

  • Be global

  • Implement SADL exit interface


Item Consumption View (Reference)

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Consumption view for bill doc item'
@VDM.viewType: #CONSUMPTION
@Metadata.allowExtensions: true
define view entity zsac_c_bill_item 
  as projection on ZSAC_R_BILL_ITEM 
{
  key BillId,
  key ItemNo,
      MaterialId,
      Description,
      Quantity,
      ItemAmount,
      Currency,
      Uom,
      CreatedBy,
      CreateDat,
      LastChangedBy,
      LastChangeDat,
      LocalLastChangeDat,
      
      _Header : redirected to parent zsac_c_bill_header
}

We will sum ItemAmount from this entity.


Step 2 – Create Virtual Element Exit Class

A virtual element requires a global ABAP class implementing: IF_SADL_EXIT_CALC_ELEMENT_READ

Here is the implementation code:

CLASS zsac_cl_amount_ve_exit DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_sadl_exit_calc_element_read.
ENDCLASS.


CLASS zsac_cl_amount_ve_exit IMPLEMENTATION.

  METHOD if_sadl_exit_calc_element_read~calculate.

    "Loop over all requested virtual elements
    LOOP AT it_requested_calc_elements INTO DATA(lv_virtual_field_name).

      "Loop over each row returned by the main query
      LOOP AT ct_calculated_data ASSIGNING FIELD-SYMBOL().

        DATA(lv_index) = sy-tabix.

        "Assign the virtual field dynamically
        ASSIGN COMPONENT lv_virtual_field_name
          OF STRUCTURE <ls_calculated_row>
          TO FIELD-SYMBOL(<lv_total_amount>).

        "Read corresponding original header data
        DATA(ls_header_data) =
          CORRESPONDING zsac_r_bill_header( it_original_data[ lv_index ] ).

        "Read associated item entities for current Billing Document
        READ ENTITIES OF zsac_r_bill_header
          ENTITY zsac_r_bill_header
          BY \_Item
          ALL FIELDS
          WITH VALUE #( ( BillId = ls_header_data-BillId ) )
          RESULT DATA(lt_item_data).

        "Initialize total amount
        CLEAR <lv_total_amount>.

        "Sum all ItemAmount values
        LOOP AT lt_item_data INTO DATA(ls_item_row).
          <lv_total_amount> =
            <lv_total_amount> + ls_item_row-ItemAmount.
        ENDLOOP.

      ENDLOOP.

    ENDLOOP.

  ENDMETHOD.

  METHOD if_sadl_exit_calc_element_read~get_calculation_info.
    "This method is called by the SADL framework prior to the retrieval of data from the database to ensure that all fields necessary for calculation are filled with data
  ENDMETHOD.

ENDCLASS.

Understanding the Exit Class

Let’s understand what is happening.

1. Interface Used

We implement: IF_SADL_EXIT_CALC_ELEMENT_READ

This interface is used for calculating virtual elements during read operations.


2. calculate Method

This method is triggered when:

  • RAP reads data

  • Virtual element is requested

Parameters:

  • it_original_data → Original CDS result

  • ct_calculated_data → Result table to modify

  • it_requested_calc_elements → List of virtual fields


3. Core Logic Flow

For each header row:

  1. Get Billing Document ID

  2. Read associated _Item entities

  3. Sum all ItemAmount values

  4. Assign result to TotalAmount

The calculation happens dynamically at runtime. Nothing is written to database.

Virtual Element in SAP RAP


Other Virtual Element Exit Interfaces

Depending on use case, you can implement:

  • IF_SADL_EXIT_CALC_ELEMENT_READ → Used for runtime calculation 

  • IF_SADL_EXIT_FILTER_TRANSFORM → Used for custom filter transformation

  • IF_SADL_EXIT_SORT_TRANSFORM → used for custom sorting logic


Important Points about Virtual Elements

According to SAP documentation:

  • Virtual elements are read-only

  • They cannot be persisted

  • They are calculated only during read

  • They should not contain heavy performance logic

They are ideal for:

  • Derived values

  • Aggregations

  • Runtime display fields

Thanks for reading.