In the world of SAP RAP (RESTful Application Programming), Determinations and Side Effects play a crucial role in delivering responsive and intelligent user experiences. While determinations are used to implement logic that reacts to changes in data during specific RAP phases (such as on modify or on save), side effects ensure that related fields or entities stay consistent and updated on the UI without additional user actions.
In his blog, I will demonstrate a practical scenario where determinations are used to dynamically calculate values based on user input, and side effects are configured to immediately reflect these changes on the UI. This not only improves the data integrity but also enhances usability in Fiori applications. By the end of this post, you will have a clearer understanding of how to leverage determinations and side effects together effectively within a RAP-based application.
DETERMINATION :
Pre requisites :
During modification we change/modify some fields / requirements and we can compute the values of particular fields
Determination can be called during Transactional phase/Save sequence where as validations always called during the save sequence
Once a determination has been triggered , it must run independently from other determinations
During determination result must change if we execute detrmination several time under same condition
Scenario :
Here the determination calculateTotalPrice which is defined for all the three entities
(Travel, booking and booking supplements) handles the calculation of total price of one travel.
Based on the booking Fee total price must be calculated for the travel
SIDE-EFFECTS:
Side effects have two parts :
Important points:
But in the front end screen older values will be there .
Requirement :
And whenever we are updating booking fee the reload of total price should happen
Based on the trigger condition --> it should reload the values / fields
Here the source --> Booking fee
Target --> totalprice
Syntax:
Side effects { filed MyField affects Target }
{ $self affects Targets ; }
{ action MyAction affects Tragets ;}
{ determine action MyDetermineAction
Executed on sources
Affects Tagets } .
In the target we can specify :
Side effects { field MyField affects Field1 , Field2 , action Action1 ,
Action Action 2 [..] }
RESTRICTIONS :
However fields from the associated entity can be defined using path expressions
Side Effect In BDL:
Behaviour definition
1. Create db table for travel
@EndUserText.label : 'travel table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zpd_dt_travel {
key client : abap.clnt not null;
key travel_id : /dmo/travel_id not null;
agency_id : /dmo/agency_id;
customer_id : /dmo/customer_id;
begin_date : /dmo/begin_date;
end_date : /dmo/end_date;
@Semantics.amount.currencyCode : 'zpd_dt_travel.currency_code'
booking_fee : /dmo/booking_fee;
@Semantics.amount.currencyCode : 'zpd_dt_travel.currency_code'
total_price : /dmo/total_price;
currency_code : /dmo/currency_code;
description : /dmo/description;
overall_status : /dmo/overall_status;
created_by : abp_creation_user;
created_at : abp_creation_tstmpl;
last_changed_by : abp_locinst_lastchange_user;
last_changed_at : abp_locinst_lastchange_tstmpl;
}
2.Create db table for Booking
@EndUserText.label : 'tbale for booking details'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zpd_dt_booking {
key client : abap.clnt not null;
@AbapCatalog.foreignKey.label : 'Travel'
@AbapCatalog.foreignKey.screenCheck : false
key travel_id : /dmo/travel_id not null
with foreign key [0..*,1] zpd_dt_travel
where travel_id = zpd_dt_booking.travel_id;
key booking_id : /dmo/booking_id not null;
booking_date : /dmo/booking_date;
customer_id : /dmo/customer_id;
carrier_id : /dmo/carrier_id;
connection_id : /dmo/connection_id;
flight_date : /dmo/flight_date;
@Semantics.amount.currencyCode : 'zpd_dt_booking.currency_code'
flight_price : /dmo/flight_price;
currency_code : /dmo/currency_code;
booking_status : /dmo/booking_status;
last_changed_at : abp_locinst_lastchange_tstmpl;
}
3.Crete a db table for Booking suppliment
@EndUserText.label : 'booking supplimnet'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zpd_dt_bsupplmnt {
key client : abap.clnt not null;
@AbapCatalog.foreignKey.label : 'Travel'
@AbapCatalog.foreignKey.screenCheck : false
key travel_id : /dmo/travel_id not null
with foreign key [0..*,1] zpd_dt_travel
where travel_id = zpd_dt_bsupplmnt.travel_id;
@AbapCatalog.foreignKey.label : 'Booking'
@AbapCatalog.foreignKey.screenCheck : false
key booking_id : /dmo/booking_id not null
with foreign key [0..*,1] zpd_dt_booking
where travel_id = zpd_dt_bsupplmnt.travel_id
and booking_id = zpd_dt_bsupplmnt.booking_id;
key booking_supplement_id : /dmo/booking_supplement_id not null;
supplement_id : /dmo/supplement_id;
@Semantics.amount.currencyCode : 'zpd_dt_bsupplmnt.currency_code'
price : /dmo/supplement_price;
currency_code : /dmo/currency_code;
last_changed_at : abp_locinst_lastchange_tstmpl;
}
4.Create a root view entity for travel
AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'root view for travel'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define root view entity zi_travel_detail as select from zpd_dt_travel
composition [0..*] of ZI_BOOKING_DETAIL as _booking
association [0..1] to /DMO/I_Agency as _agency on $projection.AgencyId = _agency.AgencyID
association [0..1] to /DMO/I_Customer as _customer on $projection.CustomerId = _customer.CustomerID
association [1..1] to I_Currency as _currency on $projection.CurrencyCode = _currency.Currency
association [1..1] to /DMO/I_Overall_Status_VH as _status on $projection.OverallStatus = _status.OverallStatus
{
key travel_id as TravelId,
agency_id as AgencyId,
customer_id as CustomerId,
begin_date as BeginDate,
end_date as EndDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
booking_fee as BookingFee,
@Semantics.amount.currencyCode: 'CurrencyCode'
total_price as TotalPrice,
currency_code as CurrencyCode,
description as Description,
overall_status as OverallStatus,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.localInstanceLastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at as LastChangedAt,
_agency,
_customer,
_currency,
_status,
_booking
}
5.Create interface view for booking
@EndUserText.label: 'root view for booking'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity ZI_BOOKING_DETAIL as select from zpd_dt_booking association to parent zi_travel_detail as _travel
on $projection.TravelId = _travel.TravelId
composition [0..*] of zi_booking_supp as _bookingsuppl
association [1..1] to /DMO/I_Carrier as _carrier on $projection.CarrierId = _carrier.AirlineID
association [1..1] to /DMO/I_Customer as _customer on $projection.CustomerId = _customer.CustomerID
association [1..1] to /DMO/I_Connection as _connection on $projection.CarrierId = _connection.AirlineID
and $projection.ConnectionId = _connection.ConnectionID
association[1..1] to /DMO/I_Booking_Status_VH as _booking_status on $projection.BookingStatus = _booking_status.BookingStatus
{
key travel_id as TravelId,
key booking_id as BookingId,
booking_date as BookingDate,
customer_id as CustomerId,
carrier_id as CarrierId,
connection_id as ConnectionId,
flight_date as FlightDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
flight_price as FlightPrice,
currency_code as CurrencyCode,
booking_status as BookingStatus,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at as LastChangedAt,
_carrier,
_customer,
_connection,
_booking_status,
_travel,
_bookingsuppl
}
6.Create Interface view for Booking suppliment
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'root view for booking suppliment'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity zi_booking_supp as select from zpd_dt_bsupplmnt
association to parent ZI_BOOKING_DETAIL as _booking on $projection.TravelId = _booking.TravelId
and $projection.BookingId = _booking.BookingId
association[1..1] to zi_travel_detail as _travel on $projection.TravelId = _travel.TravelId
association [1..1] to /DMO/I_Supplement as _suppliment on $projection.SupplementId = _suppliment.SupplementID
association[1..*] to /DMO/I_SupplementText as _supplimentText on $projection.SupplementId = _supplimentText.SupplementID
{
key travel_id as TravelId,
key booking_id as BookingId,
key booking_supplement_id as BookingSupplementId,
supplement_id as SupplementId,
@Semantics.amount.currencyCode: 'CurrencyCode'
price as Price,
currency_code as CurrencyCode,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at as LastChangedAt,
_travel,
_supplimentText,
_suppliment,
_booking
}
7.Define projection for travel root entity
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'consumption view for travel'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define root view entity zc_travel_det provider contract transactional_query
as projection on zi_travel_detail
{
key TravelId,
@ObjectModel.text.element: [ 'AgencyName' ]
AgencyId,
_Agency.Name as AgencyName,
@ObjectModel.text.element: [ 'CustomerName' ]
CustomerId,
_Customer.LastName as CustomerName,
BeginDate,
EndDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
BookingFee,
@Semantics.amount.currencyCode: 'CurrencyCode'
TotalPrice,
CurrencyCode,
Description,
@ObjectModel.text.element: [ 'OverallStatusText' ]
OverallStatus,
_Status._Text.Text as OverallStatusText : localized,
CreatedBy,
CreatedAt,
LastChangedBy,
LastChangedAt,
/* Associations */
_Agency,
_booking : redirected to composition child zc_booking_det,
_Currency,
_Customer,
_Status
}
8.Define projection for booking entity
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'consumption view for booking'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define view entity zc_booking_det
as projection on ZI_BOOKING_DETAIL
{
key TravelId,
key BookingId,
BookingDate,
@ObjectModel.text.element: [ 'CustomerName' ]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Customer',
element: 'CustomerID'
} }]
CustomerId,
_customer.LastName as CustomerName,
@ObjectModel.text.element: [ 'CarrierName' ]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Carrier',
element: 'AirlineId'
} }]
CarrierId,
_carrier.Name as CarrierName,
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Flight',
element: 'Connection Id'
}
}]
ConnectionId,
FlightDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
FlightPrice,
CurrencyCode,
@ObjectModel.text.element: [ 'BookingStatusText' ]
_booking_status._Text.Text as BookingStatusText : localized,
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Booking_Status_VH',
element: 'Booking_Status'
} }]
BookingStatus,
LastChangedAt,
/* Associations */
_bookingsuppl : redirected to composition child zc_booking_supp,
_booking_status,
_carrier,
_connection,
_customer,
_travel : redirected to parent zc_travel_det
}
9.Define projection for booking suppliment entity
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'consumption view for booking suppliment'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define view entity zc_booking_supp
as projection on zi_booking_supp
{
key TravelId,
key BookingId,
key BookingSupplementId,
@ObjectModel.text.element: [ 'SupplemenDesc' ]
SupplementId,
_SupplementText.Description as SupplemenDesc : localized,
@Semantics.amount.currencyCode: 'CurrencyCode'
Price,
CurrencyCode,
LastChangedAt,
/* Associations */
_Travel : redirected to zc_travel_det,
_Booking : redirected to parent zc_booking_det,
_Supplement,
_SupplementText
}
10.provide metada extension for projected views
@Metadata.layer: #CORE
@Search.searchable: true
@UI.headerInfo: {
typeName: 'Travel',
typeNamePlural: 'Travels',
title: {
type: #STANDARD,
label: 'Travel',
value: 'TravelId'
}
}
annotate view zc_travel_det
with
{
@UI.facet: [{
id: 'Travel',
purpose: #STANDARD,
position: 10 ,
label: 'Travel',
type: #IDENTIFICATION_REFERENCE
},
{
id: 'Booking',
purpose: #STANDARD,
position: 20 ,
label: 'Booking',
type: #LINEITEM_REFERENCE,
targetElement: '_booking'
}
]
@UI.lineItem: [{ position: 10 }, { type:#FOR_ACTION, dataAction: 'CopyTravel', label: 'Copy Travel' }
,{ type:#FOR_ACTION, dataAction: 'AcceptTravel', label: 'AcceptTravel' }
,{ position: 30 },{ type:#FOR_ACTION, dataAction: 'RejectTravel', label: 'RejectTravel' }]
@UI.identification: [{ position: 10 }]
@Search.defaultSearchElement: true
TravelId;
@UI: { lineItem: [{ position: 40 }],
selectionField: [{ position: 40 }],
identification: [{ position: 40 }]
}
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Agency',
element: 'AgencyID'
} }]
AgencyId;
// AgencyName;
@UI: { lineItem: [{ position: 50 }],
selectionField: [{ position: 50 }],
identification: [{ position: 50 }]
}
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Customer',
element: 'CustomerID'
} }]
CustomerId;
// CustomerName;
@UI.lineItem: [{ position: 60 }]
@UI.identification: [{ position: 60 }]
BeginDate;
@UI.lineItem: [{ position: 70 }]
@UI.identification: [{ position: 70 }]
EndDate;
@UI.identification: [{ position: 75 }]
BookingFee;
@UI.lineItem: [{ position: 80 }]
@UI.identification: [{ position: 80 }]
TotalPrice;
@Consumption.valueHelpDefinition: [{ entity: {
name: 'I_Currency',
element: 'Currency'
} }]
CurrencyCode;
@UI.identification: [{ position: 85 }]
Description;
@UI: { lineItem: [{ position: 90 }],
selectionField: [{ position: 60 }],
identification: [{ position: 90 }],
textArrangement: #TEXT_ONLY
}
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Overall_Status_VH',
element: 'OverallStatus'
} }]
OverallStatus;
// OverallStatusText;
// .hidden: true
// LastChangedAt;
}
11. metadata extension for booking entity
@Metadata.layer: #CORE
@Search.searchable: true
@UI.headerInfo: {
typeName: 'Booking',
typeNamePlural: 'Bookings',
title: {
type: #STANDARD,
label: 'Booking',
value: 'BookingId'
}
}
annotate view zc_booking_det
with
{
@UI.facet: [{
id: 'Booking',
purpose: #STANDARD,
position: 10 ,
label: 'Booking',
type: #IDENTIFICATION_REFERENCE
},
{
id: 'BookingSuppl',
purpose: #STANDARD,
position: 20 ,
label: 'Booking Suppliments',
type: #LINEITEM_REFERENCE,
targetElement: '_bookingsuppl'
}
]
// .defaultSearchElement: true
// TravelId;
@UI.lineItem: [{ position: 10 }]
@UI.identification: [{ position: 10 }]
@Search.defaultSearchElement: true
BookingId;
@UI.lineItem: [{ position: 20 }]
@UI.identification: [{ position: 20 }]
BookingDate;
@UI.lineItem: [{ position: 30 }]
@UI.identification: [{ position: 30 }]
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Customer',
element: 'CustomerID'
} }]
CustomerId;
@UI.lineItem: [{ position: 40 }]
@UI.identification: [{ position: 40 }]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Carrier',
element: 'AirlineID'
} }]
CarrierId;
@UI.lineItem: [{ position: 50 }]
@UI.identification: [{ position: 50 }]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Flight',
element: 'ConnectionId'
},
additionalBinding: [{ element: 'ConnectionId' ,
localElement: 'ConnectionID'},
{ element: 'AirlineID' ,
localElement: 'CarrierId'},
{ element: 'CurrencyCode' ,
localElement: 'CurrencyCode'},
{ element: 'Price' ,
localElement: 'FlightPrice'} ]
}]
ConnectionId;
@UI.lineItem: [{ position: 60 }]
@UI.identification: [{ position: 60 }]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Flight',
element: 'FlightDate'
},
additionalBinding: [{ element: 'FlightDate' ,
localElement: 'FlightDate'},
{ element: 'AirlineID' ,
localElement: 'CarrierId'},
{ element: 'CurrencyCode' ,
localElement: 'CurrencyCode'},
{ element: 'Price' ,
localElement: 'FlightPrice'} ]
}]
FlightDate;
@UI.lineItem: [{ position: 70 }]
@UI.identification: [{ position: 70 }]
FlightPrice;
// @Consumption.valueHelpDefinition: [{ entity: {
// name: 'I_Currency',
// element: 'Currency'
// } }]
// CurrencyCode;
@UI.lineItem: [{ position: 80 }]
@UI.identification: [{ position: 80 }]
@UI.textArrangement: #TEXT_ONLY
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Booking_Status_VH',
element: 'BookingStatus'
} }]
BookingStatus;
// LastChangedAt;
}
12. Metadata extension for booking entity
@Metadata.layer: #CORE
@Search.searchable: true
@UI.headerInfo: {
typeName: 'Booking',
typeNamePlural: 'Bookings',
title: {
type: #STANDARD,
label: 'Booking',
value: 'BookingId'
}
}
annotate view zc_booking_det
with
{
@UI.facet: [{
id: 'Booking',
purpose: #STANDARD,
position: 10 ,
label: 'Booking',
type: #IDENTIFICATION_REFERENCE
},
{
id: 'BookingSuppl',
purpose: #STANDARD,
position: 20 ,
label: 'Booking Suppliments',
type: #LINEITEM_REFERENCE,
targetElement: '_bookingsuppl'
}
]
// .defaultSearchElement: true
// TravelId;
@UI.lineItem: [{ position: 10 }]
@UI.identification: [{ position: 10 }]
@Search.defaultSearchElement: true
BookingId;
@UI.lineItem: [{ position: 20 }]
@UI.identification: [{ position: 20 }]
BookingDate;
@UI.lineItem: [{ position: 30 }]
@UI.identification: [{ position: 30 }]
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Customer',
element: 'CustomerID'
} }]
CustomerId;
@UI.lineItem: [{ position: 40 }]
@UI.identification: [{ position: 40 }]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Carrier',
element: 'AirlineID'
} }]
CarrierId;
@UI.lineItem: [{ position: 50 }]
@UI.identification: [{ position: 50 }]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Flight',
element: 'ConnectionId'
},
additionalBinding: [{ element: 'ConnectionId' ,
localElement: 'ConnectionID'},
{ element: 'AirlineID' ,
localElement: 'CarrierId'},
{ element: 'CurrencyCode' ,
localElement: 'CurrencyCode'},
{ element: 'Price' ,
localElement: 'FlightPrice'} ]
}]
ConnectionId;
@UI.lineItem: [{ position: 60 }]
@UI.identification: [{ position: 60 }]
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Flight',
element: 'FlightDate'
},
additionalBinding: [{ element: 'FlightDate' ,
localElement: 'FlightDate'},
{ element: 'AirlineID' ,
localElement: 'CarrierId'},
{ element: 'CurrencyCode' ,
localElement: 'CurrencyCode'},
{ element: 'Price' ,
localElement: 'FlightPrice'} ]
}]
FlightDate;
@UI.lineItem: [{ position: 70 }]
@UI.identification: [{ position: 70 }]
FlightPrice;
// @Consumption.valueHelpDefinition: [{ entity: {
// name: 'I_Currency',
// element: 'Currency'
// } }]
// CurrencyCode;
@UI.lineItem: [{ position: 80 }]
@UI.identification: [{ position: 80 }]
@UI.textArrangement: #TEXT_ONLY
@Consumption.valueHelpDefinition: [{ entity: {
name: '/DMO/I_Booking_Status_VH',
element: 'BookingStatus'
} }]
BookingStatus;
// LastChangedAt;
}
13. Metadata extension for booking suppliment entity
@Metadata.layer: #CORE @Search.searchable: true @UI.headerInfo: { typeName: 'BookingSuppl', typeNamePlural: 'BookingSuppls', title: { type: #STANDARD, label: 'Booking Suppliments', value: 'BookingSupplementId' } } annotate view zc_booking_supp with { @UI.facet: [{ id: 'BookingSuppl', purpose: #STANDARD, position: 10 , label: 'Booking Suppliments', type: #IDENTIFICATION_REFERENCE } ] // @Search.defaultSearchElement: true // TravelId; @Search.defaultSearchElement: true BookingId; @UI.lineItem: [{ position: 10 }] @UI.identification: [{ position: 10 }] BookingSupplementId; @UI.lineItem: [{ position: 20 }] @UI.identification: [{ position: 20 }] @Consumption.valueHelpDefinition: [{ entity: { name: '/DMO/I_SUPPLEMENT', element: 'SupplementID' }, additionalBinding: [{ element: 'SupplementID' , localElement: 'SupplementId'}, { element: 'Price' , localElement: 'Price'}, { element: 'CurrencyCode' , localElement: 'CurrencyCode'} ] }] SupplementId; @UI.lineItem: [{ position: 30 }] @UI.identification: [{ position: 30 }] Price; // @Consumption.valueHelpDefinition: [{ entity: { // name: 'I_Currency', // element: 'Currency' // } }] // CurrencyCode; // @UI.hidden: true // LastChangedAt; }
14. Create behaviour definition travel root entity
managed;
strict ( 2 );
define behavior for zi_travel_detail //alias <alias_name>
implementation in class zcl_bp_travel_maa unique
persistent table zpd_dt_travel
lock master
authorization master ( global )
etag master LastChangedAt
early numbering
{
field ( readonly ) TravelId;
action AcceptTravel result [1] $self;
action RejectTravel result [1] $self;
factory action CopyTravel [1];
internal action RecalcTotalPrice;
determination CalcTotalPrice on modify { create; field BookingFee, CurrencyCode; }
// authorization ( global ),
create;
update;
delete;
// field ( readonly ) TravelId;
association _booking { create; }
mapping for zpd_dt_travel
{
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;
}
}
define behavior for ZI_BOOKING_DETAIL //alias <alias_name>
implementation in class zcl_bp_booking_ma unique
persistent table zpd_dt_booking
lock dependent by _Travel
//authorization dependent by _travel
authorization master ( global )
etag master LastChangedAt
early numbering
{
update;
delete;
field ( readonly ) TravelId, BookingId;
determination CalcTotalPrice on modify { create; field FlightPrice, CurrencyCode; }
association _travel;
association _bookingsuppl { create; }
mapping for zpd_dt_booking
{
TravelId = travel_id;
BookingId = booking_id;
BookingDate = booking_date;
CustomerId = customer_id;
CarrierId = carrier_id;
ConnectionId = connection_id;
FlightDate = flight_date;
FlightPrice = flight_price;
CurrencyCode = currency_code;
BookingStatus = booking_status;
LastChangedAt = last_changed_at;
}
}
define behavior for zi_booking_supp //alias <alias_name>
implementation in class zcl_bp_booking_ma unique
persistent table zpd_dt_bsupplmnt
lock dependent by _Travel
//authorization dependent by _Travel
authorization master ( global )
etag master LastChangedAt
early numbering
{
update;
delete;
field ( readonly ) TravelId, BookingId, BookingSupplementId;
determination CalcTotalPrice on modify { create; field Price, CurrencyCode; }
association _Travel;
association _Booking;
mapping for zpd_dt_bsupplmnt
{
SupplementId = supplement_id;
TravelId = travel_id;
BookingId = booking_id;
BookingSupplementId = booking_supplement_id;
Price = price;
CurrencyCode = currency_code;
LastChangedAt = last_changed_at;
}
}
15. Define behavior definition for projection root entity
projection;
strict ( 2 );
define behavior for zc_travel_det //alias <alias_name>
{
use create;
use update;
use delete;
use action AcceptTravel;
use action RejectTravel;
use action CopyTravel;
use association _booking { create; }
}
define behavior for zc_booking_det //alias <alias_name>
{
use update;
use delete;
use association _travel;
use association _bookingsuppl { create; }
}
define behavior for zc_booking_supp //alias <alias_name>
{
use update;
use delete;
use association _Travel;
use association _Booking;
}
16. Create Service Definition
@EndUserText.label: 'service def for travel'
define service Zsr_travel_det {
expose zc_travel_det;
expose zc_booking_det;
expose zc_booking_supp;
expose /DMO/I_Supplement_StdVH as Supplement;
expose /DMO/I_SupplementCategory_VH as SupplementCategory;
expose /DMO/I_Customer_StdVH as Passenger;
expose /DMO/I_Agency_StdVH as TravelAgency;
expose /DMO/I_Carrier_StdVH as Airline;
expose /DMO/I_Connection_StdVH as FlightConnection;
expose /DMO/I_Flight_StdVH as Flight;
expose /DMO/I_Airport_StdVH as Airport;
expose /DMO/I_Overall_Status_VH as OverallStatus;
expose /DMO/I_Booking_Status_VH as BookingStatus;
expose I_CurrencyStdVH as Currency;
expose I_CountryVH as Country;
}
17. Create service binding
18. Implement the class
CLASS lhc_zi_travel_detail DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION
IMPORTING REQUEST requested_authorizations FOR zi_travel_detail RESULT result.
METHODS accepttravel FOR MODIFY
IMPORTING keys FOR ACTION zi_travel_detail~accepttravel RESULT result.
METHODS copytravel FOR MODIFY
IMPORTING keys FOR ACTION zi_travel_detail~copytravel.
METHODS recalctotalprice FOR MODIFY
IMPORTING keys FOR ACTION zi_travel_detail~recalctotalprice.
METHODS rejecttravel FOR MODIFY
IMPORTING keys FOR ACTION zi_travel_detail~rejecttravel RESULT result.
METHODS calctotalprice FOR DETERMINE ON MODIFY
IMPORTING keys FOR zi_travel_detail~calctotalprice.
METHODS earlynumbering_cba_booking FOR NUMBERING
IMPORTING entities FOR CREATE zi_travel_detail\_booking.
METHODS earlynumbering_create FOR NUMBERING
IMPORTING entities FOR CREATE zi_travel_detail.
ENDCLASS.
CLASS lhc_zi_travel_detail IMPLEMENTATION.
METHOD get_global_authorizations.
ENDMETHOD.
METHOD earlynumbering_create.
DATA(lt_entities) = entities.
DELETE lt_entities WHERE TravelId IS NOT INITIAL .
TRY.
cl_numberrange_runtime=>number_get(
EXPORTING
nr_range_nr = '01'
object = '/DMO/TRV_M'
quantity = CONV #( lines( lt_entities ) )
IMPORTING
number = DATA(lv_latest_num)
returncode = DATA(lv_code)
returned_quantity = DATA(lv_qty)
).
CATCH cx_nr_object_not_found.
CATCH cx_number_ranges INTO DATA(lo_error).
LOOP AT lt_entities INTO DATA(ls_entities).
APPEND VALUE #( %cid = ls_entities-%cid
%key = ls_entities-%key )
TO failed-zi_travel_detail.
APPEND VALUE #( %cid = ls_entities-%cid
%key = ls_entities-%key
%msg = lo_error )
TO reported-zi_travel_detail.
ENDLOOP.
EXIT.
ENDTRY..
ASSERT lv_qty = lines( lt_entities ).
DATA(lv_curr_num) = lv_latest_num - lv_qty.
LOOP AT lt_entities INTO ls_entities.
lv_curr_num = lv_curr_num + 1.
APPEND VALUE #( %cid = ls_entities-%cid
TravelId = lv_curr_num )
TO mapped-zi_travel_detail.
ENDLOOP.
ENDMETHOD.
METHOD earlynumbering_cba_Booking.
DATA : lv_max_booking TYPE /dmo/booking_id.
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail BY \_booking
FROM CORRESPONDING #( entities )
LINK DATA(lt_link_data).
LOOP AT entities ASSIGNING FIELD-SYMBOL(<ls_group_entity>)
GROUP BY <ls_group_entity>-TravelId.
lv_max_booking = REDUCE #( INIT lv_max = CONV /dmo/booking_id( '0' )
FOR ls_link IN lt_link_data USING KEY entity
WHERE ( source-TravelId = <ls_group_entity>-TravelId )
NEXT lv_max = COND /dmo/booking_id( WHEN lv_max < ls_link-target-BookingId
THEN ls_link-target-BookingId
ELSE lv_max ) ).
lv_max_booking = REDUCE #( INIT lv_max = lv_max_booking
FOR ls_entity IN entities USING KEY entity
WHERE ( TravelId = <ls_group_entity>-TravelId )
FOR ls_booking IN ls_entity-%target
NEXT lv_max = COND /dmo/booking_id( WHEN lv_max < ls_booking-BookingId
THEN ls_booking-BookingId
ELSE lv_max ) ).
LOOP AT entities ASSIGNING FIELD-SYMBOL(<ls_entities>)
USING KEY entity
WHERE TravelId = <ls_group_entity>-TravelId.
LOOP AT <ls_entities>-%target ASSIGNING FIELD-SYMBOL(<ls_booking>).
IF <ls_booking>-BookingId IS INITIAL.
lv_max_booking += 10.
APPEND CORRESPONDING #( <ls_booking> ) TO mapped-zi_booking_detail ASSIGNING FIELD-SYMBOL(<ls_new_map_book>).
<ls_new_map_book>-BookingId = lv_max_booking.
ENDIF.
ENDLOOP.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
METHOD AcceptTravel.
MODIFY ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR ls_keys IN keys ( %tky = ls_keys-%tky
OverallStatus = 'A') ).
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_result).
result = VALUE #( FOR ls_result IN lt_result ( %tky = ls_result-%tky
%param = ls_result ) ).
ENDMETHOD.
METHOD CopyTravel.
ENDMETHOD.
METHOD RecalcTotalPrice.
TYPES : BEGIN OF ty_total,
price TYPE /dmo/total_price,
curr TYPE /dmo/currency_code,
END OF ty_total.
DATA : lt_total TYPE TABLE OF ty_total,
lv_conv_price TYPE ty_total-price.
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
FIELDS ( BookingFee CurrencyCode )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
DELETE lt_travel WHERE CurrencyCode IS INITIAL.
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail BY \_booking
FIELDS ( FlightPrice CurrencyCode )
WITH CORRESPONDING #( lt_travel )
RESULT DATA(lt_ba_booking) .
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_booking_detail BY \_bookingsuppl
FIELDS ( Price CurrencyCode )
WITH CORRESPONDING #( lt_ba_booking )
RESULT DATA(lt_ba_bookingsupp) .
LOOP AT lt_travel ASSIGNING FIELD-SYMBOL(<ls_travel>).
lt_total = VALUE #( ( price = <ls_travel>-BookingFee curr = <ls_travel>-CurrencyCode ) ).
LOOP AT lt_ba_booking ASSIGNING FIELD-SYMBOL(<ls_booking>)
USING KEY entity
WHERE TravelId = <ls_travel>-TravelId
AND CurrencyCode IS NOT INITIAL.
APPEND VALUE #( price = <ls_booking>-FlightPrice curr = <ls_booking>-CurrencyCode )
TO lt_total.
LOOP AT lt_ba_bookingsupp ASSIGNING FIELD-SYMBOL(<ls_bookingsupp>)
USING KEY entity
WHERE TravelId = <ls_booking>-TravelId
AND BookingId = <ls_booking>-BookingId
AND CurrencyCode IS NOT INITIAL.
APPEND VALUE #( price = <ls_bookingsupp>-Price curr = <ls_bookingsupp>-CurrencyCode )
TO lt_total.
ENDLOOP.
ENDLOOP.
LOOP AT lt_total ASSIGNING FIELD-SYMBOL(<ls_total>).
IF <ls_total>-curr = <ls_travel>-CurrencyCode .
lv_conv_price = <ls_total>-price .
ELSE.
/dmo/cl_flight_amdp=>convert_currency(
EXPORTING
iv_amount = <ls_total>-price
iv_currency_code_source = <ls_total>-curr
iv_currency_code_target = <ls_travel>-CurrencyCode
iv_exchange_rate_date = cl_abap_context_info=>get_system_date( )
IMPORTING
ev_amount = lv_conv_price
).
ENDIF.
<ls_travel>-TotalPrice = <ls_travel>-TotalPrice + lv_conv_price.
ENDLOOP.
MODIFY ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
UPDATE FIELDS ( TotalPrice )
WITH CORRESPONDING #( lt_travel ).
ENDLOOP.
ENDMETHOD.
METHOD RejectTravel.
MODIFY ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR ls_keys IN keys ( %tky = ls_keys-%tky
OverallStatus = 'X') )
REPORTED DATA(lt_travel).
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_result).
result = VALUE #( FOR ls_result IN lt_result ( %tky = ls_result-%tky
%param = ls_result ) ).
ENDMETHOD.
METHOD CalcTotalPrice.
MODIFY ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
EXECUTE RecalcTotalPrice
FROM CORRESPONDING #( keys ).
ENDMETHOD.
ENDCLASS.
18. Here we are calculating the total price , whenever somebody changes the total Price and currency manually this determination should call on transactional phase(on modify)
METHOD CalcTotalPrice.
MODIFY ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
EXECUTE RecalcTotalPrice
FROM CORRESPONDING #( keys ).
ENDMETHOD.
19. will call RecacTotalPrice for each determination
All the travel id which we modify will trigger when we are calculating total price
RecacTotalPrice--> will be called . We need to write the logic to modify Our BO instance based on travel ID And it will read and updae the price
Here the action will take care of the process – when we change the price ( based on determination ) action will be called
And as soon as price filed changes, determination happens And we need to read the travel entity with fields booking fee and currency code And we need to provide travel IDs which is present in keys Result is taken in LT_TRAVEL
Same thing we need to do it for boking and booking suppliment
METHOD RecalcTotalPrice.
TYPES : BEGIN OF ty_total,
price TYPE /dmo/total_price,
curr TYPE /dmo/currency_code,
END OF ty_total.
DATA : lt_total TYPE TABLE OF ty_total,
lv_conv_price TYPE ty_total-price.
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
FIELDS ( BookingFee CurrencyCode )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
DELETE lt_travel WHERE CurrencyCode IS INITIAL.
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail BY \_booking
FIELDS ( FlightPrice CurrencyCode )
WITH CORRESPONDING #( lt_travel )
RESULT DATA(lt_ba_booking) .
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_booking_detail BY \_bookingsuppl
FIELDS ( Price CurrencyCode )
WITH CORRESPONDING #( lt_ba_booking )
RESULT DATA(lt_ba_bookingsupp) .
LOOP AT lt_travel ASSIGNING FIELD-SYMBOL(<ls_travel>).
lt_total = VALUE #( ( price = <ls_travel>-BookingFee curr = <ls_travel>-CurrencyCode ) ).
LOOP AT lt_ba_booking ASSIGNING FIELD-SYMBOL(<ls_booking>)
USING KEY entity
WHERE TravelId = <ls_travel>-TravelId
AND CurrencyCode IS NOT INITIAL.
APPEND VALUE #( price = <ls_booking>-FlightPrice curr = <ls_booking>-CurrencyCode )
TO lt_total.
LOOP AT lt_ba_bookingsupp ASSIGNING FIELD-SYMBOL(<ls_bookingsupp>)
USING KEY entity
WHERE TravelId = <ls_booking>-TravelId
AND BookingId = <ls_booking>-BookingId
AND CurrencyCode IS NOT INITIAL.
APPEND VALUE #( price = <ls_bookingsupp>-Price curr = <ls_bookingsupp>-CurrencyCode )
TO lt_total.
ENDLOOP.
ENDLOOP.
LOOP AT lt_total ASSIGNING FIELD-SYMBOL(<ls_total>).
IF <ls_total>-curr = <ls_travel>-CurrencyCode .
lv_conv_price = <ls_total>-price .
ELSE.
/dmo/cl_flight_amdp=>convert_currency(
EXPORTING
iv_amount = <ls_total>-price
iv_currency_code_source = <ls_total>-curr
iv_currency_code_target = <ls_travel>-CurrencyCode
iv_exchange_rate_date = cl_abap_context_info=>get_system_date( )
IMPORTING
ev_amount = lv_conv_price
).
ENDIF.
<ls_travel>-TotalPrice = <ls_travel>-TotalPrice + lv_conv_price.
ENDLOOP.
MODIFY ENTITIES OF zi_travel_detail in LOCAL MODE
ENTITY zi_travel_detail
UPDATE FIELDS ( TotalPrice )
WITH CORRESPONDING #( lt_travel ).
ENDLOOP.
ENDMETHOD.
20. Class for booking suppliment
CLASS lhc_zi_booking_supp DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS CalcTotalPrice FOR DETERMINE ON MODIFY
IMPORTING keys FOR zi_booking_supp~CalcTotalPrice.
METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION
IMPORTING REQUEST requested_authorizations FOR zi_booking_supp RESULT result.
ENDCLASS.
CLASS lhc_zi_booking_supp IMPLEMENTATION.
METHOD CalcTotalPrice.
ENDMETHOD.
METHOD get_global_authorizations.
ENDMETHOD.
ENDCLASS.
CLASS lhc_zi_booking_detail DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS earlynumbering_cba_Bookingsupp FOR NUMBERING
IMPORTING entities FOR CREATE zi_booking_detail\_Bookingsuppl.
METHODS CalcTotalPrice FOR DETERMINE ON MODIFY
IMPORTING keys FOR zi_booking_detail~CalcTotalPrice.
METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION
IMPORTING REQUEST requested_authorizations FOR zi_booking_detail RESULT result.
ENDCLASS.
CLASS lhc_zi_booking_detail IMPLEMENTATION.
METHOD earlynumbering_cba_Bookingsupp.
DATA: max_booking_suppl_id TYPE /dmo/booking_supplement_id .
READ ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_booking_detail BY \_Bookingsuppl
FROM CORRESPONDING #( entities )
LINK DATA(booking_supplements).
" Loop over all unique tky (TravelID + BookingID)
LOOP AT entities ASSIGNING FIELD-SYMBOL(<booking_group>) GROUP BY <booking_group>-%tky.
" Get highest bookingsupplement_id from bookings belonging to booking
max_booking_suppl_id = REDUCE #( INIT max = CONV /dmo/booking_supplement_id( '0' )
FOR booksuppl IN booking_supplements USING KEY entity
WHERE ( source-TravelId = <booking_group>-TravelId
AND source-BookingId = <booking_group>-BookingId )
NEXT max = COND /dmo/booking_supplement_id( WHEN booksuppl-target-BookingSupplementId > max
THEN booksuppl-target-BookingSupplementId
ELSE max )
).
" Get highest assigned bookingsupplement_id from incoming entities
max_booking_suppl_id = REDUCE #( INIT max = max_booking_suppl_id
FOR entity IN entities USING KEY entity
WHERE ( TravelId = <booking_group>-TravelId
AND BookingId = <booking_group>-BookingId )
FOR target IN entity-%target
NEXT max = COND /dmo/booking_supplement_id( WHEN target-BookingSupplementId > max
THEN target-BookingSupplementId
ELSE max )
).
" Loop over all entries in entities with the same TravelID and BookingID
LOOP AT entities ASSIGNING FIELD-SYMBOL(<booking>) USING KEY entity WHERE TravelId = <booking_group>-TravelId
AND BookingId = <booking_group>-BookingId.
" Assign new booking_supplement-ids
LOOP AT <booking>-%target ASSIGNING FIELD-SYMBOL(<booksuppl_wo_numbers>).
APPEND CORRESPONDING #( <booksuppl_wo_numbers> ) TO mapped-zi_booking_supp ASSIGNING FIELD-SYMBOL(<mapped_booksuppl>).
IF <booksuppl_wo_numbers>-BookingSupplementId IS INITIAL.
max_booking_suppl_id += 1 .
<mapped_booksuppl>-BookingSupplementId = max_booking_suppl_id .
ENDIF.
ENDLOOP.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
21. Based on the booking fee we need to calculate the total price , So we need to write the logic in calcTotal price method
METHOD CalcTotalPrice.
DATA : it_travel TYPE STANDARD TABLE OF zpd_dt_travel WITH UNIQUE HASHED KEY key COMPONENTS travel_id.
it_travel = CORRESPONDING #( keys DISCARDING DUPLICATES MAPPING travel_id = TravelId ).
MODIFY ENTITIES OF zi_travel_detail IN LOCAL MODE
ENTITY zi_travel_detail
EXECUTE RecalcTotalPrice
FROM CORRESPONDING #( it_travel ).
ENDMETHOD.
22. OUTPUT
23. Edit--> changing booking fee from 140 USD to 240 USD
24. Click on save << after saving , the total price is still the same ---> not updated at the screen level
25. When we refresh the page—the total price gets updated
26. Now we need to use the side effects to make the data refreshed on save at screen level , So now it should not only be updated in the transactional buffer – it should also be updated in the front end
27. Change the booking fee 258 USD and click on save
28. As soon as you click on save
Thanks and regards
-PRADEEP ISHWAR DEVADIGA
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
8 | |
5 | |
4 | |
3 | |
3 | |
3 | |
3 | |
2 | |
2 | |
2 |