[[{“value”:”
Introduction
In the SAP RAP, most examples focus on single Business Object (BO) operations. However, real-world enterprise applications rarely operate in isolation. A single business transaction often impacts multiple BOs—for example, creating a Delivery should trigger Billing, updating inventory, and possibly financial postings.
In this blog, we explore how to implement cross-BO interaction using RAP Actions, where:
- A Delivery BO triggers creation in a Billing BO
- Both operations are executed within the same LUW (Logical Unit of Work)
- Data consistency is preserved using RAP’s transactional buffer
Business Scenario
Consider a typical Order-to-Cash process:
- A Delivery is created for a customer.
- Once the delivery is confirmed, a Billing document must be generated.
- The system should:
- Automatically create a Billing record
- Update the Delivery status to DELIVERED
- Ensure both operations succeed or fail together
Implementation Steps
STEP 1: Create a Data base Table
Data base table For Delivery Details
@EndUserText.label : ‘delivery details’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zte_t_delivery_s {
key delivery_id : abap.char(10) not null;
customer_id : abap.char(5);
material : abap.char(20);
price : abap.char(10);
delivery_date : abap.dats;
delivery_status : abap.char(15);
}
Data Base table for Billing Details
@EndUserText.label : ‘Billing Details’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zte_t_billing_s {
key billing_id : abap.char(10) not null;
customer_id : abap.char(5);
delivery_id : abap.char(10);
material : abap.char(20);
price : abap.char(10);
}
STEP 2: Create root CDS views for both tables
Root view For Delivery Details
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Delivery Details’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_T_DELIVERY_S as select from zte_t_delivery_s
{
key delivery_id as DeliveryId,
customer_id as CustomerId,
material as Material,
price as Price,
delivery_date as DeliveryDate,
delivery_status as DeliveryStatus
}
Root view For Billing Details
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Billing Details’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_T_BILLING_S as select from zte_t_billing_s
{
key billing_id as BillingId,
customer_id as CustomerId,
delivery_id as DeliveryId,
material as Material,
price as Price
}
STEP 3: Create Projection View
- Add UI Annotations
- Add Action Button In Delivery Projection
Projection View For Delivery Details
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Delivery Details’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZC_T_DELIVERY_S
provider contract transactional_query as projection on ZI_T_DELIVERY_S
{
@UI.facet: [{ id: ‘deliverdetails’,
position: 1,
label: ‘Order Details’,
type: #IDENTIFICATION_REFERENCE
} ]
@UI.lineItem: [
{ importance: #MEDIUM},
{ position: 10, type : #FOR_ACTION, label : ‘Available’, dataAction : ‘create_delivery’ },
{ position: 1, label : ‘Delivery ID’ }]
@UI.identification: [{ position: 1, label : ‘Delivery ID’ }]
key DeliveryId,
@UI.lineItem: [{ position: 2, label : ‘Customer ID’ }]
@UI.identification: [{ position: 2, label : ‘Customer ID’ }]
CustomerId,
@UI.lineItem: [{ position: 3, label : ‘Material’ }]
@UI.identification: [{ position: 3, label : ‘Material’ }]
Material,
@UI.lineItem: [{ position: 4, label : ‘Price’ }]
@UI.identification: [{ position: 4, label : ‘Price’ }]
Price,
@UI.lineItem: [{ position: 5, label : ‘Delivery Date’ }]
@UI.identification: [{ position: 5, label : ‘Delivery Date’ }]
DeliveryDate,
@UI.lineItem: [{ position: 6, label : ‘Delivery Status’ }]
@UI.identification: [{ position: 6, label : ‘Delivery Status’ }]
DeliveryStatus
}
Projection View for Billing Details
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Billing Details’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZC_T_BILLING_S
provider contract transactional_query as projection on ZI_T_BILLING_S
{
@UI.facet: [{ id: ‘Billingdetails’,
position: 1,
label: ‘Billing Details’,
type: #IDENTIFICATION_REFERENCE
} ]
@UI.lineItem: [{ position: 1, label : ‘Billing Id’ }]
@UI.identification: [{ position: 1, label : ‘Billing Id’ }]
key BillingId,
@UI.lineItem: [{ position: 2, label : ‘Customer Id’ }]
@UI.identification: [{ position: 2, label : ‘Custoomer Id’ }]
CustomerId,
@UI.lineItem: [{ position: 3, label : ‘Delivery Id’ }]
@UI.identification: [{ position: 3, label : ‘Delivery Id’ }]
DeliveryId,
@UI.lineItem: [{ position: 4, label : ‘Material’ }]
@UI.identification: [{ position: 4, label : ‘Material’ }]
Material,
@UI.lineItem: [{ position: 5, label : ‘Price’ }]
@UI.identification: [{ position: 5, label : ‘Price’ }]
Price
}
STEP 4: Define Behavior Definitions
Delivery BO behavior
- Enable CRUD operation
- Declare a non-factory action
This Action is responsible for creating Billing data and updating delivery status
managed implementation in class zbp_i_t_delivery_s unique;
strict ( 2 );
define behavior for ZI_T_DELIVERY_S //alias <alias_name>
persistent table zte_t_delivery_s
lock master
authorization master ( instance )
//etag master <field_name>
{
create;
update;
delete;
action create_delivery result [1] $self;
mapping for zte_t_delivery_s
{
DeliveryId = delivery_id;
CustomerId = customer_id;
Material = material;
Price = price;
DeliveryDate = delivery_date;
DeliveryStatus = delivery_status;
}
}
Billing BO behavior
- Enable CRUD operations
- use Late Numbering for auto-generating Billing Id
managed implementation in class zbp_i_t_billing_s unique;
strict ( 2 );
define behavior for ZI_T_BILLING_S //alias <alias_name>
persistent table zte_t_billing_s
lock master
authorization master ( instance )
late numbering
//etag master <field_name>
{
create;
update;
delete;
mapping for zte_t_billing_s
{
BillingId = billing_id;
DeliveryId = delivery_id;
CustomerId = customer_id;
Material = material;
Price = price;
}
}
STEP 5: Define Projection behavior
Delivery BO projection behavior
projection;
strict ( 2 );
define behavior for ZC_T_DELIVERY_S //alias <alias_name>
{
use create;
use update;
use delete;
use action create_delivery;
}
Billing BO projection behavior
projection;
strict ( 2 );
define behavior for ZC_T_BILLING_S //alias <alias_name>
{
use create;
use update;
use delete;
}
STEP 6: Implement Behavior Logic
Delivery Class
CLASS lhc_ZI_T_DELIVERY_S DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR zi_t_delivery_s RESULT result.
METHODS create_delivery FOR MODIFY
IMPORTING keys FOR ACTION zi_t_delivery_s~create_delivery RESULT result.
ENDCLASS.
CLASS lhc_ZI_T_DELIVERY_S IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
METHOD create_delivery.
READ ENTITIES OF ZI_T_DELIVERY_S
IN LOCAL MODE
ENTITY ZI_T_DELIVERY_S
FIELDS ( DeliveryId CustomerId Material Price )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_delivery).
LOOP AT lt_delivery INTO DATA(ls_delivery).
“Create Billing Entry
MODIFY ENTITIES OF ZI_T_BILLING_S
ENTITY ZI_T_BILLING_S
CREATE
FIELDS ( DeliveryId CustomerId Material Price )
WITH VALUE #(
(
%cid = ‘BILL1’
DeliveryId = ls_delivery-DeliveryId
CustomerId = ls_delivery-CustomerId
Material = ls_delivery-Material
Price = ls_delivery-Price
)
)
FAILED DATA(ls_failed)
REPORTED DATA(ls_reported).
IF ls_failed IS NOT INITIAL.
reported = CORRESPONDING #( ls_reported ).
RETURN.
ENDIF.
“Update Delivery Status
MODIFY ENTITIES OF ZI_T_DELIVERY_S
IN LOCAL MODE
ENTITY ZI_T_DELIVERY_S
UPDATE FIELDS ( DeliveryStatus )
WITH VALUE #(
(
DeliveryId = ls_delivery-DeliveryId
DeliveryStatus = ‘DELIVERED’
)
).
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Billing Class
CLASS lhc_ZI_T_BILLING_S DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR zi_t_billing_s RESULT result.
ENDCLASS.
CLASS lhc_ZI_T_BILLING_S IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
ENDCLASS.
CLASS lsc_ZI_T_BILLING_S DEFINITION INHERITING FROM cl_abap_behavior_saver.
PROTECTED SECTION.
METHODS adjust_numbers REDEFINITION.
METHODS cleanup_finalize REDEFINITION.
ENDCLASS.
CLASS lsc_ZI_T_BILLING_S IMPLEMENTATION.
METHOD adjust_numbers.
SELECT FROM ZTE_T_Billing_S FIELDS MAX( billing_id ) INTO (ls_mat_num).
DATA(lv_num) = ls_mat_num+3(4).
LOOP AT mapped-zi_t_billing_s ASSIGNING FIELD-SYMBOL(<fs_material>).
lv_num += 1.
<fs_material>-BillingId = |BIL{ lv_num ALPHA = IN WIDTH = 4 }|.
ENDLOOP.
ENDMETHOD.
METHOD cleanup_finalize.
ENDMETHOD.
ENDCLASS.
STEP 7: Service Defination
Expose Both Behavior objects
@EndUserText.label: ‘Delivery and billing details’
define service ZSD_T_DETAILS {
expose ZC_T_DELIVERY_S;
expose ZC_T_BILLING_S;
}
STEP 8 : Service Binding
- create service binding
- Activate and publish
STEP 9: RESULT
Create a record and click on the Action Button the delivery Status will be updated and Billing records will be created
Billing details when Action is trigged and Delivery status updated to Delivery
Conclusion
Cross Business Object operations in RAP are essential for building real-world enterprise applications. While RAP abstracts much of the complexity, designing such interactions requires a clear understanding of:
- Transactional behavior
- BO independence vs coordination
- Framework-driven data consistency By leveraging RAP Actions and EML, we can safely orchestrate multi-BO updates without compromising on clean architecture or data integrity.
This example demonstrates how a simple Delivery trigger can drive Billing creation, showcasing a scalable pattern that can be extended to more complex business processes.
“}]]
Read More Technology Blog Posts by Members articles
#abap