Handling Cross Business Object Transactions in RAP Using Actions

Estimated read time 14 min read

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 BOBoth 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 recordUpdate the Delivery status to DELIVEREDEnsure 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 AnnotationsAdd 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 operationDeclare 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 operationsuse Late Numbering for auto-generating Billing Idmanaged 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 bindingActivate 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 behaviorBO independence vs coordinationFramework-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.

 

​ IntroductionIn 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 BOBoth operations are executed within the same LUW (Logical Unit of Work)Data consistency is preserved using RAP’s transactional bufferBusiness ScenarioConsider 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 recordUpdate the Delivery status to DELIVEREDEnsure both operations succeed or fail togetherImplementation StepsSTEP 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 tablesRoot 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 AnnotationsAdd Action Button In Delivery ProjectionProjection 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 behaviorEnable CRUD operationDeclare a non-factory actionThis Action is responsible for creating Billing data and updating delivery statusmanaged 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 behaviorEnable CRUD operationsuse Late Numbering for auto-generating Billing Idmanaged 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 behaviorprojection;
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 behaviorprojection;
strict ( 2 );
define behavior for ZC_T_BILLING_S //alias <alias_name>
{
use create;
use update;
use delete;
} STEP 6:  Implement Behavior LogicDelivery ClassCLASS 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 ClassCLASS 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 DefinationExpose 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 Bindingcreate service bindingActivate and publishSTEP 9: RESULTCreate 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 DeliveryConclusionCross 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 behaviorBO independence vs coordinationFramework-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 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author