This post talks about adding determinations to a RAP BO along with side effects.

Determinations

A determination modifies instances of business objects based on trigger conditions. Trigger conditions can be modify operations such as create, update, and delete and modified fields.

The determination can calculate field values, modify entity instances and return messages to the consumer by passing them to the corresponding table in the REPORTED structure.

Example

The total price in a travel BO is calculated using Determination based on a booking fee and flight prices. This scenario is explained in the earlier post Travel Service – Miscellaneous Improvements as well. However, this post enhances the same example with Side Effects.

Side Effects

Side effects are used to trigger data, permission, or message changes based on data changes in UI scenarios with draft-enabled BOs.

This is the latest feature that became available with the latest version i.e. 2302 of the SAP BTP ABAP Environment and SAP S/4HANA ABAP Environment.

Example

The above determination to calculate the total price can be triggered using Side Effects as soon as a value changes for any of the dependent fields.

Add determinations and side effects to Behavior Definition

ZI_FE_Travel_001811

Travel Entity

  • Determination to calculate the Total Price when Booking Fee has changed is added.
  • Side Effect to update the UI element Total Price when Booking Fee has changed is added
  • Internal Action to re-Calculate Total Price is added. This helps to reuse the same code from Travel and Booking Entities.

Booking Entity

  • Determination to calculate the Total Price when FlightPrice is changed, a new booking is created, or a booking is deleted
  • Side Effect to update the UI element Total Price when the field FlightPrice is changed

Important: The side effects will work only with mode strict ( 2 );

managed;
strict ( 2 );
with draft;

define behavior for ZI_FE_Travel_001811 alias Travel
...

{
  ...

  determination CalculateTravelID on save { create; }  //This is implemented earlier
  determination CalculateTotalPrice on modify { field BookingFee; }

  side effects { field BookingFee affects field TotalPrice; }
  internal action ReCalcTotalPrice;

}

define behavior for ZI_FE_Booking_001811 alias Booking
...

{
  ...

  determination CalculateBookingID on save { create; }  //This is implemented earlier
  determination CalculateTotalPrice on modify { create; delete; field FlightPrice; }

  side effects { field FlightPrice affects field _Travel.TotalPrice; }

}
Expand to see complete code for ZI_FE_Travel_001811

managed;
strict ( 2 );
with draft;

define behavior for ZI_FE_Travel_001811 alias Travel
implementation in class ZBP_I_FE_Travel_001811 unique
persistent table zfe_atrav_001811
draft table zfe_dtrav_001811
etag master LocalLastChangedAt
authorization master ( global )
lock master total etag LastChangedAt

{
  field ( readonly ) TravelID, TravelUUID, TotalPrice;
  field ( numbering : managed ) TravelUUID;

  create;
  update;
  delete;

  draft action Edit;
  draft action Activate;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare;
  mapping for zfe_atrav_001811
    {
      TravelUUID         = TRAVEL_UUID;
      TravelID           = TRAVEL_ID;
      AgencyID           = AGENCY_ID;
      CustomerID         = CUSTOMER_ID;
      BeginDate          = BEGIN_DATE;
      EndDate            = END_DATE;
      BookingFee         = BOOKING_FEE;
      TotalPrice         = TOTAL_PRICE;
      CurrencyCode       = CURRENCY_CODE;
      Description        = DESCRIPTION;
      OverallStatus      = OVERALL_STATUS;
      CreatedBy          = CREATED_BY;
      CreatedAt          = CREATED_AT;
      LastChangedBy      = LAST_CHANGED_BY;
      LastChangedAt      = LAST_CHANGED_AT;
      LocalLastChangedAt = LOCAL_LAST_CHANGED_AT;
    }



  association _Booking { create; with draft; }
  determination CalculateTravelID on save { create; }
  determination CalculateTotalPrice on modify { field BookingFee; }

  side effects { field BookingFee affects field TotalPrice; }
  internal action ReCalcTotalPrice;

}

define behavior for ZI_FE_Booking_001811 alias Booking
implementation in class ZBP_I_FE_Booking_001811 unique
persistent table zfe_abook_001811
draft table zfe_dbook_001811
etag master LocalLastChangedAt
authorization dependent by _Travel
lock dependent by _Travel

{
  field ( readonly ) TravelUUID, BookingID, BookingUUID;

  field ( numbering : managed ) BookingUUID;

  update;
  delete;
  mapping for zfe_abook_001811
    {
      BookingUUID        = BOOKING_UUID;
      TravelUUID         = TRAVEL_UUID;
      BookingID          = BOOKING_ID;
      BookingDate        = BOOKING_DATE;
      CustomerID         = CUSTOMER_ID;
      CarrierID          = CARRIER_ID;
      ConnectionID       = CONNECTION_ID;
      FlightDate         = FLIGHT_DATE;
      FlightPrice        = FLIGHT_PRICE;
      CurrencyCode       = CURRENCY_CODE;
      CreatedBy          = CREATED_BY;
      LastChangedBy      = LAST_CHANGED_BY;
      LocalLastChangedAt = LOCAL_LAST_CHANGED_AT;
    }


  association _Travel { with draft; }

  determination CalculateBookingID on save { create; }
  determination CalculateTotalPrice on modify { create; delete; field FlightPrice; }

  side effects { field FlightPrice affects field _Travel.TotalPrice; }

}

Activate and use quick-fix functionality to create methods in the respective classes.

Implement the determination and action methods

Travel Entity Class – ZBP_I_FE_TRAVEL_001811

Implement the methods with the below code.

  METHOD calculatetotalprice.

    MODIFY ENTITIES OF zi_fe_travel_001811 IN LOCAL MODE
      ENTITY travel
        EXECUTE recalctotalprice
        FROM CORRESPONDING #( keys ).

  ENDMETHOD.

Here, simply the internal action is triggered. The actual calculation is implemented under the internal action.

  METHOD recalctotalprice.

    READ ENTITIES OF zi_fe_travel_001811 IN LOCAL MODE
      ENTITY travel
        FIELDS ( bookingfee )
           WITH CORRESPONDING #( keys )
         RESULT DATA(lt_travels).

    LOOP AT lt_travels ASSIGNING FIELD-SYMBOL(<fs_travel>).
      <fs_travel>-totalprice = <fs_travel>-bookingfee.

      READ ENTITIES OF zi_fe_travel_001811 IN LOCAL MODE
          ENTITY travel
          BY \_booking
          FIELDS ( flightprice )
           WITH VALUE #( ( %tky = <fs_travel>-%tky ) )
           RESULT DATA(lt_bookings).

      LOOP AT lt_bookings ASSIGNING FIELD-SYMBOL(<fs_booking>).
        <fs_travel>-totalprice = <fs_travel>-totalprice + <fs_booking>-flightprice.
      ENDLOOP.

    ENDLOOP.

    MODIFY ENTITIES OF zi_fe_travel_001811 IN LOCAL MODE
      ENTITY travel
        UPDATE FIELDS ( totalprice )
        WITH VALUE #( FOR travel IN lt_travels  (
                           %tky      = travel-%tky
                           totalprice  = travel-totalprice ) ).

  ENDMETHOD.

First, we read the travels into the internal table lt_travels. Then, For each travel, we read associated booking instances and loop on all the bookings. The booking fee from the travel and flight price from the booking are added up.

Finally, the total price field is modified for related entities.

Booking Entity Class – ZBP_I_FE_BOOKING_001811

  METHOD calculatetotalprice.

    READ ENTITIES OF zi_fe_travel_001811 IN LOCAL MODE
        ENTITY booking
        BY \_travel
        FIELDS ( traveluuid )
         WITH CORRESPONDING #( keys )
         RESULT DATA(lt_travels).

    IF lt_travels IS INITIAL.   "For delete booking scenario - this table is empty
      SELECT SINGLE * FROM zfe_abook_001811 WHERE booking_uuid = @( keys[ 1 ]-bookinguuid )
        INTO @DATA(ls_book).
      IF sy-subrc EQ 0.
        lt_travels = VALUE #( (  %is_draft = '01' traveluuid = ls_book-travel_uuid ) ).
      ENDIF.
    ENDIF.

    "update involved instances
    MODIFY ENTITIES OF zi_fe_travel_001811 IN LOCAL MODE
      ENTITY travel
        EXECUTE recalctotalprice
        FROM VALUE #( FOR <fs_key> IN lt_travels ( %tky = <fs_key>-%tky ) ).

  ENDMETHOD.

Here, a similar code is written except for the delete scenario. When a booking is deleted, the associated travel entity is not read by the READ ENTITIES statement. Hence, a select query is used to get the data from the persistent table and populate the internal table lt_travel.

This code may be enhanced in the future depending on how SAP improves the RAP framework (or I improve my knowledge).

Activate everything and test the application to observe the side effects.

Important: As the side effects are applied on the field changes and not on the operations, it can be observed that the side-effects work for updated in the fields.

It appears to work for create operation as on the screen we updated the price. We also get back on the Travel page from Booking page and the UI changes anyways.

But for delete, operation it works only after save as the field price is not changing.

This is how the application should behave with the side effects implemented.

How the determinations and side-effects work in RAP BO

Visit ABAP RESTful Application Programming Model to explore all articles on ABAP RAP Model.


If you like the content, please subscribe…

Join 4,016 other subscribers

Discovering ABAP YouTube Channel