https://raw.githubusercontent.com/ajmaradiaga/feeds/main/scmt/topics/ABAP-RESTful-Application-Programming-Model-blog-posts.xmlSAP Community - ABAP RESTful Application Programming Model2026-02-11T12:00:07.453474+00:00python-feedgenABAP RESTful Application Programming Model blog posts in SAP Communityhttps://community.sap.com/t5/technology-blog-posts-by-members/deep-insert-payload-into-a-single-table-in-rap-abap/ba-p/14175272Deep Insert Payload into a Single table in RAP ABAP2025-12-24T06:55:42.141000+01:00BharathKumarVaddepallihttps://community.sap.com/t5/user/viewprofilepage/user-id/1989797<P>This document demonstrates how to design and implement a <STRONG>RAP-based create scenario with a nested payload</STRONG> using the RESTful ABAP Programming Model (RAP). The example focuses on inserting data into a single custom database table using a <STRONG>root entity with a composition child</STRONG>, allowing a structured payload that includes both product and inventory-related information. The solution covers the full RAP flow: creating the database table, defining Interface Views and Projection Views with compositions, implementing managed behavior with an unmanaged save, handling the payload in the behavior saver class, and finally exposing the data through a service definition and service binding. The scenario is validated by consuming the OData service with a JSON payload that creates the record successfully.</P><P><STRONG><SPAN>Payload</SPAN></STRONG><SPAN> <STRONG>to be passed to insert the record</STRONG></SPAN><STRONG> </STRONG></P><pre class="lia-code-sample language-abap"><code>{
"ProductId" : "00000000-0000-0000-0000-000000000987",
"Name" : "Kumar111",
"to_InventoryReceipt" :
{
"Category" : "Transport",
"Price" : 200,
"Currency" : "USD"
},
"Discount" : 30
} </code></pre><P><SPAN>Here all the fields' data are from the same table.</SPAN></P><P> </P><P>1. Create your table in which your payload is to be inserted.</P><P>2. Create Interface Views as given below.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_0-1754629443013.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298147iE74CAC24B4780503/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_0-1754629443013.png" alt="BharathKumarVaddepalli_0-1754629443013.png" /></span><SPAN> </SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'product root view'
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_vbProduct_tmp_vw
as select from zvb_product_tmp as product
composition [0..1] of ZI_InventoryReceipt_IFD_SEG as _InventoryReceipt
{
key product.product_id as ProductId,
product.name as Name,
product.discount as Discount,
product.createdby as CreatedBy,
_InventoryReceipt
} </code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_1-1754629443020.png" style="width: 484px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298146i181E761913D26A24/image-dimensions/484x196?v=v2" width="484" height="196" role="button" title="BharathKumarVaddepalli_1-1754629443020.png" alt="BharathKumarVaddepalli_1-1754629443020.png" /></span></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'ZI_InventoryReceipt_IFD_SEG'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity ZI_InventoryReceipt_IFD_SEG
as select from zvb_product_tmp
association to parent ZI_vbProduct_tmp_vw as _Product
on $projection.ProductId = _Product.ProductId
{
key product_id as ProductId,
category as Category,
price as Price,
currency as Currency,
_Product
}</code></pre><P><STRONG><SPAN>Projection Views</SPAN></STRONG><SPAN> </SPAN></P><P><SPAN>Create the projection views for the Interface Views.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_2-1754629443024.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298148iFEBA12358E4D9857/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_2-1754629443024.png" alt="BharathKumarVaddepalli_2-1754629443024.png" /></span></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'product projection view'
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZP_Product as projection on ZI_vbProduct_tmp_vw
{
@UI.facet: [{
purpose: #STANDARD,
position: 10,
label: 'Product',
type: #IDENTIFICATION_REFERENCE
},
{
purpose: #STANDARD,
position: 20,
label: 'Inventory',
type: #LINEITEM_REFERENCE,
targetElement: '_InventoryReceipt'
}]
@UI.lineItem: [{ position: 10, label: 'ProductId' }]
@UI.identification: [{ position: 10, label: 'ProductId' }]
key ProductId,
.lineItem: [{ position: 20, label: 'Name' }]
@UI.identification: [{ position: 20, label: 'Name' }]
Name,
.lineItem: [{ position: 30, label: 'Discount' }]
@UI.identification: [{ position: 30, label: 'Discount' }]
Discount,
.lineItem: [{ position: 40, label: 'CreatedBy' }]
@UI.identification: [{ position: 40, label: 'CreatedBy' }]
CreatedBy,
/* Associations */
_InventoryReceipt : redirected to composition child ZP_inventory
} </code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_3-1754629836472.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298150i3C7C047550C9BD0C/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_3-1754629836472.png" alt="BharathKumarVaddepalli_3-1754629836472.png" /></span></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'ZP_inventory'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZP_inventory as projection on ZI_InventoryReceipt_IFD_SEG
{
@UI.facet: [{
purpose: #STANDARD,
position: 10,
label: 'Inventory',
type: #IDENTIFICATION_REFERENCE
}]
@UI.lineItem: [{ position: 50, label: 'ProductId' }]
@UI.identification: [{ position: 50, label: 'ProductId' }]
key ProductId,
.lineItem: [{ position: 60, label: 'Category' }]
@UI.identification: [{ position: 60, label: 'Category' }]
Category,
.lineItem: [{ position: 70, label: 'Price' }]
@UI.identification: [{ position: 70, label: 'Price' }]
Price,
.lineItem: [{ position: 80, label: 'Currency' }]
@UI.identification: [{ position: 80, label: 'Currency' }]
Currency,
/* Associations */
_Product : redirected to parent ZP_Product
} </code></pre><P><SPAN> </SPAN></P><P><STRONG><SPAN>Create Behaviour definition on Interface View.</SPAN></STRONG><SPAN> </SPAN></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_4-1754629908125.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298151i0AF077AFE98DFB5D/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_4-1754629908125.png" alt="BharathKumarVaddepalli_4-1754629908125.png" /></span></P><P><STRONG><SPAN>Behaviour Definition for projection views</SPAN></STRONG><SPAN> </SPAN></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_5-1754629961680.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298152iDC9166B198E01BBE/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_5-1754629961680.png" alt="BharathKumarVaddepalli_5-1754629961680.png" /></span></P><pre class="lia-code-sample language-abap"><code>managed with unmanaged save implementation in class zbp_i_vbproduct_tmp_vw unique;
strict ( 2 );
define behavior for ZI_VBPRODUCT_tmp_VW alias product_tmp
//late numbering
lock master
authorization master ( instance )
//etag master <field_name>
//early numbering
{
create;
update;
delete;
association _InventoryReceipt { create ; }
}
define behavior for ZI_INVENTORYRECEIPT_IFD_SEG
lock dependent by _Product
//early numbering
authorization dependent by _Product
{
association _Product;
update;
delete;
field ( readonly ) productid;
}</code></pre><P><SPAN> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_6-1754630048978.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298153i7F78BB6E8060D6F8/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_6-1754630048978.png" alt="BharathKumarVaddepalli_6-1754630048978.png" /></span></P><pre class="lia-code-sample language-abap"><code>projection;
strict ( 2 );
define behavior for ZP_Product alias Product
{
use create;
use update;
use delete;
use association _InventoryReceipt { create; }
}
define behavior for ZP_inventory alias Inventory
{
use update;
use delete;
use association _Product;
} </code></pre><P><SPAN> </SPAN><STRONG><SPAN>Behaviour Implementation</SPAN></STRONG><SPAN> </SPAN></P><pre class="lia-code-sample language-abap"><code>CLASS lhc_product_tmp DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR product_tmp RESULT result.
* METHODS earlynumbering_create FOR NUMBERING
* IMPORTING entities FOR CREATE ZI_VBPRODUCT_tmp_VW.
ENDCLASS.
CLASS lhc_product_tmp IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
* METHOD earlynumbering_create.
* endmethod.
ENDCLASS.
CLASS lsc_ZI_VBPRODUCT_TMP_VW DEFINITION INHERITING FROM cl_abap_behavior_saver.
PROTECTED SECTION.
METHODS save_modified REDEFINITION.
METHODS cleanup_finalize REDEFINITION.
ENDCLASS.
CLASS lsc_ZI_VBPRODUCT_TMP_VW IMPLEMENTATION.
METHOD save_modified.
* create-product_tmp
data: lt_product_tmp type table for change zi_vbproduct_tmp_vw\\product_tmp.
data: lt_zi_inventoryreceipt_ifd_seg type table for change zi_inventoryreceipt_ifd_seg.
data: lt_final type table of zvb_product.
MOVE-CORRESPONDING create-product_tmp to lt_product_tmp.
MOVE-CORRESPONDING create-zi_inventoryreceipt_ifd_seg to lt_zi_inventoryreceipt_ifd_seg.
LOOP AT lt_product_tmp into data(ls_product).
read TABLE lt_zi_inventoryreceipt_ifd_seg into data(ls_invent) index 1.
append value #( product_id = cl_system_uuid=>create_uuid_x16_static( )
name = ls_product-Name
Discount = ls_product-Discount
Category = ls_invent-Category
currency = ls_invent-Currency
price = ls_invent-Price ) to lt_final.
endloop.
modify zvb_product from TABLE lt_final.
ENDMETHOD.
METHOD cleanup_finalize.
ENDMETHOD.
ENDCLASS.</code></pre><P><STRONG><SPAN>Service Definition</SPAN></STRONG><SPAN> </SPAN></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_7-1754630415803.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298155iA8C6A113E03111A8/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_7-1754630415803.png" alt="BharathKumarVaddepalli_7-1754630415803.png" /></span></P><P> </P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'SD FOR PRODUCT'
define service ZSD_PRODUCT_TMP {
expose ZP_Product;
expose ZP_inventory;
} </code></pre><P> </P><P> </P><P><STRONG><SPAN>Service Binding</SPAN></STRONG><SPAN> </SPAN></P><P><SPAN> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_8-1754630476599.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298156i593521CC7DAEA017/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_8-1754630476599.png" alt="BharathKumarVaddepalli_8-1754630476599.png" /></span></P><P><SPAN> </SPAN></P><P><STRONG><SPAN>Payload</SPAN></STRONG><SPAN> <STRONG>to be passed to insert the record</STRONG></SPAN><STRONG> </STRONG></P><pre class="lia-code-sample language-abap"><code>{
"ProductId" : "00000000-0000-0000-0000-000000000987",
"Name" : "Kumar111",
"to_InventoryReceipt" :
{
"Category" : "Transport",
"Price" : 200,
"Currency" : "USD"
},
"Discount" : 30
} </code></pre><P><SPAN> </SPAN></P><P><STRONG><SPAN>URI</SPAN></STRONG><SPAN> </SPAN><SPAN> </SPAN></P><P><STRONG><SPAN>/sap/opu/odata/sap/ZSB_PRODUCT_TEMP_V2/ZP_Product</SPAN></STRONG><SPAN> </SPAN></P><P><STRONG>OUTPUT</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="BharathKumarVaddepalli_0-1754631573555.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/298158i2CEB1239F038684D/image-size/medium?v=v2&px=400" role="button" title="BharathKumarVaddepalli_0-1754631573555.png" alt="BharathKumarVaddepalli_0-1754631573555.png" /></span></P><P><STRONG>Conclusion</STRONG></P><P> By using RAP compositions and managed behavior with an unmanaged save implementation, this approach enables handling <STRONG>complex, nested payloads</STRONG> in a clean and extensible way. Even though all fields originate from the same database table, the logical separation into a root entity and a composition child improves API design and aligns with RAP best practices. The service definition and binding expose the entities for OData consumption, making it easy to create records using a single POST request with a structured payload. This design provides flexibility for future enhancements, ensures clear separation of responsibilities, and integrates seamlessly with SAP Fiori applications and external consumers.</P><P><SPAN>Thank You </SPAN></P><P><SPAN>Bharath Kumar VADDEPALLI</SPAN></P><P><SPAN> </SPAN></P>2025-12-24T06:55:42.141000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/creating-material-master-data-in-sap-s-4hana-using-rap-and-eml/ba-p/14300852Creating Material Master Data in SAP S/4HANA Using RAP and EML .2026-01-08T14:06:13.252000+01:00Sudhanshu07https://community.sap.com/t5/user/viewprofilepage/user-id/1675459<P>In SAP S/4HANA, <STRONG>Article (Material) Master Data</STRONG> is one of the most critical master data objects, serving as the foundation for logistics, sales, inventory management, and financial processes. Every procurement transaction, sales order, stock movement, or valuation posting ultimately depends on the correctness and consistency of material master data.</P><P>Traditionally, material master records are created using standard transactions such as <STRONG>MM01</STRONG> or by leveraging classic approaches like <STRONG>BAPIs and IDocs</STRONG>. While these methods are reliable, they are not always well-suited for modern application development, especially when building custom, extensible, and clean-core–compliant solutions.</P><P>With the introduction of the <STRONG>RESTful ABAP Programming Model (RAP)</STRONG>, SAP provides a standardized and future-ready way to build transactional applications using CDS views, behavior definitions, and <STRONG>Entity Manipulation Language (EML)</STRONG>. RAP allows developers to model business objects declaratively while handling transactional consistency, validations, and authorizations in a structured manner.</P><P>In this article, we focus on <STRONG>creating Article (Material) Master Data using RAP and EML</STRONG>, by building an <STRONG>unmanaged RAP Business Object</STRONG> on top of standard SAP interface views such as I_Product, I_ProductDescription, and I_ProductPlant. Instead of directly calling classic BAPIs like BAPI_MATERIAL_SAVEDATA, the solution leverages <STRONG>EML operations on I_ProductTP</STRONG> to create and extend product data in a clean and controlled way.</P><P>The implementation demonstrates:</P><UL><LI>How to model a <STRONG>root CDS view entity</STRONG> for product creation</LI><LI>How to enrich it with <STRONG>UI annotations and value helps</STRONG></LI><LI>How to define <STRONG>behavior logic</STRONG> for create, update, and custom actions</LI><LI>How to use <STRONG>EML to create product header data, descriptions, and plant extensions</STRONG></LI><LI>How to handle validations, updates, and controlled operations within RAP behavior handlers</LI></UL><P>This approach enables the creation of a <STRONG>modern, API-driven, and extensible material master solution</STRONG>, which can easily be consumed by custom UIs, Fiori Elements, or external integrations—while remaining aligned with SAP’s clean-core strategy.</P><P>At the end of our development our RAP UI app will look like :</P><P>Note: <STRONG>Data is blurred out .</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhanshu07_0-1767968573014.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359881i02DBFB4AFA8C664F/image-size/medium?v=v2&px=400" role="button" title="Sudhanshu07_0-1767968573014.png" alt="Sudhanshu07_0-1767968573014.png" /></span></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhanshu07_2-1767449599803.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357915i83CDE45417090C36/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="Sudhanshu07_2-1767449599803.png" alt="Sudhanshu07_2-1767449599803.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhanshu07_3-1767968770680.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359884i0C86999F91C90658/image-size/medium?v=v2&px=400" role="button" title="Sudhanshu07_3-1767968770680.png" alt="Sudhanshu07_3-1767968770680.png" /></span></P><P>Step 1: Create a root CDS view entity</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Product demo with EML'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@Search.searchable: true
define root view entity ZI_product_demo as select from I_Product as product
association [0..1] to I_ProductDescription as _descrp
on product.Product = _descrp.Product
association [0..*] to I_ProductPlant as _plante
on product.Product = _plante.Product
{
key Product,
IndustrySector,
ProductType,
BaseUnit,
ProductGroup,
PackagingMaterialGroup,
_plante.Plant,
_descrp.Language,
_descrp.ProductDescription // Make association public
}</code></pre><P>Step 2: Create a metadata extension for UI</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CUSTOMER
@UI: {
headerInfo: {
typeName: 'Manage Products',
typeNamePlural: 'Products',
title: { type: #STANDARD, value: 'Product' },
description: { value: 'ProductDescription' }
}
}
annotate entity ZI_product_demo with
{
.facet: [
{ id: 'ProductData', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Product Details', position: 10 }
]
: { lineItem: [ { position: 10 },
{ type: #FOR_ACTION, dataAction: 'extendplant', label: 'Extend Plant' } ],
identification: [ { position: 10, label: 'Product' } ],
selectionField: [ { position: 10 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_PRODUCT_VH' ,
element: 'Product' } } ]
Product;
: { lineItem: [ { position: 20 } ],
identification: [ { position: 20, label: 'Industry Sector' } ],
selectionField: [ { position: 20 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_IndustrySector_VH' ,
element: 'IndustrySector' } } ]
IndustrySector;
: { lineItem: [ { position: 30 } ],
identification: [ { position: 30, label: 'Product Type' } ],
selectionField: [ { position: 30 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_ProductType_VH' ,
element: 'ProductType' } } ]
ProductType;
: { lineItem: [ { position: 40 } ],
identification: [ { position: 40, label: 'Base Unit' } ],
selectionField: [ { position: 40 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_BaseUnit_VH' ,
element: 'BaseUnit' } } ]
BaseUnit;
: { lineItem: [ { position: 50 } ],
identification: [ { position: 50, label: 'Product Group' } ],
selectionField: [ { position: 50 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_ProductGroup_VH' ,
element: 'ProductGroup' } } ]
ProductGroup;
: { lineItem: [ { position: 60 } ],
identification: [ { position: 60, label: 'Product Description' } ],
selectionField: [ { position: 60 } ] }
ProductDescription;
: { lineItem: [ { position: 70 } ],
identification: [ { position: 70, label: 'Packaging Material Group' } ],
selectionField: [ { position: 70 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_PackMaterialGroup_VH' ,
element: 'PackagingMaterialGroup' } } ]
PackagingMaterialGroup;
: { lineItem: [ { position: 80 } ],
identification: [ { position: 80, label: 'Plant' } ],
selectionField: [ { position: 80 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_PLANT_VH' ,
element: 'Plant' } } ]
Plant;
: { lineItem: [ { position: 90 } ],
identification: [ { position: 90, label: 'Language' } ],
selectionField: [ { position: 90 } ] }
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_Language_VH' ,
element: 'Language' } } ]
Language;
}</code></pre><P>Since we are adding a extension to plant feature in our app we will need an abstract entity for plant.</P><P>Step 3: create abstract entity for plant</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'abstract entity fo plant'
@Metadata.allowExtensions: true
define abstract entity ZI_PLANT_PARAM
{
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZC_MAT_PLANT_VH' ,
element: 'Plant' } } ]
plant : werks_d;
}</code></pre><P>Step 4: Behavior definition will look like this, note that in action we have to specify the abstract entity name for plant extension feature :</P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_i_product_demo unique;
strict ( 2 );
define behavior for ZI_product_demo //alias <alias_name>
late numbering
lock master
authorization master ( global )
{
create;
update;
delete;
action extendplant parameter ZI_PLANT_PARAM result [1] $self;
field ( readonly ) Plant;
field ( readonly : update ) Product, ProductType, IndustrySector , PackagingMaterialGroup, Language;
field ( mandatory : create ) BaseUnit , Language , ProductDescription, PackagingMaterialGroup,
IndustrySector, ProductType;
}</code></pre><P>Step 5 : In the behavior implementation class we have to implement create , update, delete & extendplant method .</P><pre class="lia-code-sample language-abap"><code>METHOD create.
DATA lt_create_product TYPE TABLE FOR CREATE I_ProductTP_2.
*-- Read the data from app
DATA(ls_entity) = entities[ 1 ].
lv_product_type = ls_entity-ProductType.
lt_create_product = VALUE #( ( %cid = 'product1'
Product = ls_entity-Product
ProductType = ls_entity-ProductType
BaseUnit = ls_entity-BaseUnit
IndustrySector = ls_entity-IndustrySector
PackagingProductGroup = ls_entity-PackagingMaterialGroup
%control-Product = if_abap_behv=>mk-on
%control-ProductType = if_abap_behv=>mk-on
%control-BaseUnit = if_abap_behv=>mk-on
%control-IndustrySector = if_abap_behv=>mk-on
%control-PackagingProductGroup = if_abap_behv=>mk-on ) ).
*-- Call EML to replace BAPI_MATERIAL_SAVEDATA.
MODIFY ENTITIES OF I_ProductTP_2 PRIVILEGED
ENTITY Product
CREATE FROM lt_create_product
CREATE BY \_ProductDescription
FIELDS ( Language ProductDescription ) WITH VALUE #( ( %cid_ref = 'product1'
Product = ls_entity-Product
%target = VALUE #( (
%cid = 'desc1'
Product = ls_entity-Product
Language = ls_entity-Language
ProductDescription = ls_entity-ProductDescription
) ) ) )
MAPPED DATA(ls_mapped)
REPORTED DATA(ls_reported)
FAILED DATA(ls_failed)..
ENDMETHOD.
METHOD update.
DATA(ls_update_entity) = entities[ 1 ].
*-- Fetch existing data from database.
SELECT SINGLE a~ProductType,
a~BaseUnit,
a~ProductGroup,
a~IndustrySector,
a~PackagingMaterialGroup,
b~ProductDescription,
b~Language
FROM I_Product AS a
INNER JOIN i_productdescription AS b
ON a~Product = b~Product
WHERE a~product EQ _update_entity-%key-Product
INTO (ls_exist_product).
*-- Call EML to modify Product header data
MODIFY ENTITIES OF I_ProductTP_2 PRIVILEGED
ENTITY Product
UPDATE FIELDS ( BaseUnit ProductGroup )
WITH VALUE #( ( %key-Product = ls_update_entity-Product
BaseUnit = COND #( WHEN ls_update_entity-BaseUnit IS INITIAL THEN ls_exist_product-BaseUnit
ELSE ls_update_entity-BaseUnit )
ProductGroup = COND #( WHEN ls_update_entity-ProductGroup IS INITIAL THEN ls_exist_product-ProductGroup
ELSE ls_update_entity-ProductGroup )
) )
*-- Call EML to modify product description
ENTITY ProductDescription
UPDATE FIELDS ( ProductDescription )
WITH VALUE #( ( %key-Product = ls_update_entity-Product
%key-Language = ls_exist_product-Language
ProductDescription = COND #( WHEN ls_update_entity-ProductDescription IS INITIAL THEN ls_exist_product-ProductDescription
ELSE ls_update_entity-ProductDescription ) ) )
MAPPED DATA(ls_mapped)
REPORTED DATA(ls_reported)
FAILED DATA(ls_failed).
ENMETHOD.
METHOD delete.
*-- Turn off delete functionality
APPEND VALUE #( %tky = keys[ 1 ]-%tky ) TO failed-zi_product_demo.
APPEND VALUE #( %tky = keys[ 1 ]-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text = 'Delete functionality is not enabled' )
) TO reported-zi_product_demo.
ENDMETHOD.
METHOD read.
ENDMETHOD.
METHOD lock.
ENDMETHOD.
METHOD extendplant.
DATA : lt_product TYPE TABLE FOR READ RESULT zi_product_demo.
DATA(lt_keys) = keys.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<lfs_keys>).
DATA(lv_plant) = <lfs_keys>-%param-plant.
DATA(lv_product) = <lfs_keys>-%key-Product.
ENDLOOP.
*-- Modify the entity with plant
MODIFY ENTITY I_ProductTP_2
CREATE BY \_ProductPlant AUTO FILL CID WITH VALUE #( ( Product = lv_product
%target = VALUE #( ( Product = lv_product
Plant = lv_plant ) ) ) )
MAPPED DATA(ls_mapped)
REPORTED DATA(ls_reported)
FAILED DATA(ls_failed).
*-- Read the changed data.
READ ENTITIES OF zi_product_demo IN LOCAL MODE
ENTITY ZI_product_demo
ALL FIELDS WITH
CORRESPONDING #( lt_keys )
RESULT DATA(lt_result).
result = VALUE #( FOR ls IN lt_result ( %tky = ls-%tky
%param = ls ) ).
ENDMETHOD.
ENDCLASS.</code></pre><P> </P>2026-01-08T14:06:13.252000+01:00https://community.sap.com/t5/abap-blog-posts/how-to-consume-external-apis-in-s-4hana-private-cloud-edition-pce-2023-via/ba-p/14297278How To Consume External APIs in S/4HANA Private Cloud Edition (PCE) 2023 via RAP Framework2026-01-08T14:08:04.222000+01:00shasankgupta024https://community.sap.com/t5/user/viewprofilepage/user-id/877731<P><STRONG>Scenario:</STRONG><BR />Suppose we need to expose images related to a "PickTicket" from an external system, making them available in SAP via a custom OData service.</P><P><STRONG>Steps Overview</STRONG></P><P>1. Create a Custom Entity CDS View<BR />2. Create Service Definition<BR />3. Create Service Binding<BR />4. Implement ABAP Class to Consume the External API<BR />5. Test the OData Service</P><P><STRONG>1. Create a Custom Entity (CDS View)</STRONG></P><P>Start by defining a **custom entity** to represent the data structure for the API response:</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Custom Entity for Image Data'
@ObjectModel: {
query: {
implementedBy: 'ABAP:ZCL_IMAGESET'
}
}
define root custom entity ZCE_IMAGESET
{
key pickticketid : abap.char(20);
key urlshortlivedthumbnail : abap.char(255);
trigger_ : abap.char(20);
source : abap.char(40);
imageid : abap.char(20);
urlthumbnail : abap.char(255);
urlshortlived : abap.char(255);
}</code></pre><P>This entity acts as a **virtual view**—data retrieval is handled by an ABAP class (see `implementedBy` above).</P><P><STRONG>2. Create a Service Definition</STRONG></P><P>Expose the custom entity in your service definition:</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'zsd_imageset'
define service Zsd_imageset {
expose ZCE_IMAGESET;
}</code></pre><P><STRONG>3. Create a Service Binding</STRONG></P><P>Next, create a **service binding**—for example, `ZSB_IMAGESET` for OData V4—which generates the OData endpoint for your application. <EM>*Use the standard RAP tools in Eclipse or ADT to create this binding from the CDS service definition.*</EM></P><P><STRONG>4. Implement the ABAP Class to Handle API Call</STRONG></P><P>Define the ABAP class (`zcl_imageset`) to fetch and provide data from the external API.<BR />Use the **RAP Query Provider Interface** (`if_rap_query_provider`) for custom data provisioning:</P><P><U>Class Definition</U></P><pre class="lia-code-sample language-abap"><code>CLASS zcl_imageset DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES: BEGIN OF ty_imageset,
trigger TYPE char20,
pickticketid TYPE char20,
source TYPE char40,
imageid TYPE char20,
urlthumbnail TYPE char255,
urlshortlived TYPE char255,
urlshortlivedthumbnail TYPE char255,
END OF ty_imageset.
TYPES: tt_imageset TYPE STANDARD TABLE OF ty_imageset WITH EMPTY KEY.
INTERFACES if_rap_query_provider.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.</code></pre><P><U>Class Implementation</U></P><pre class="lia-code-sample language-abap"><code>CLASS ZCL_IMAGESET IMPLEMENTATION.
METHOD if_rap_query_provider~select.
DATA lt_images TYPE tt_imageset.
DATA lv_pickticketid TYPE string.
" Get filter (PickTicketId)
DATA(lo_filter) = io_request->get_filter( )->get_as_sql_string( ).
DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
DATA(lv_max_rows) = COND #( WHEN lv_page_size = if_rap_query_paging=>page_size_unlimited THEN 0
ELSE lv_page_size ).
DATA(lt_sort_table) = VALUE string_table( FOR ls_sort_element IN io_request->get_sort_elements( )
( ls_sort_element-element_name && COND #( WHEN ls_sort_element-descending = abap_true THEN ` descending` ELSE ` ascending` ) ) ).
CONCATENATE 'PICKTICKETID ' 'SOURCE'
INTO DATA(lv_default_sort) SEPARATED BY ',' .
DATA(lv_sort_string) = COND #( WHEN lt_sort_table IS INITIAL THEN lv_default_sort
ELSE concat_lines_of( table = lt_sort_table sep = ',' ) ).
IF io_request IS BOUND.
DATA(lt_expressions) = io_request->get_filter( )->get_as_ranges( ).
LOOP AT lt_expressions INTO DATA(ls_expr).
IF ls_expr-name = 'PICKTICKETID'.
LOOP AT ls_expr-range INTO DATA(ls_range).
IF ls_range-option = 'EQ'.
lv_pickticketid = ls_range-low.
EXIT.
ENDIF.
ENDLOOP.
EXIT.
ENDIF.
ENDLOOP.
ENDIF.
" Build target URL for the external request
DATA(lv_url) = |https://<external domain url>/pickTickets/{ lv_pickticketid }/images|.
" Create HTTP client and set headers
DATA lo_http_client TYPE REF TO if_http_client.
cl_http_client=>create_by_url(
EXPORTING url = lv_url
IMPORTING client = lo_http_client
).
lo_http_client->request->set_header_field( name = 'client_id' value = '<your_client_id>' ).
lo_http_client->request->set_header_field( name = 'client_secret' value = '<your_client_secret>' ).
lo_http_client->send( ).
lo_http_client->receive( ).
DATA(lv_response) = lo_http_client->response->get_cdata( ).
lo_http_client->close( ).
" Parse JSON response to ABAP table
DATA(lo_json) = NEW /ui2/cl_json( ).
lo_json->deserialize(
EXPORTING json = lv_response
CHANGING data = lt_images
).
" Return the data
io_response->set_total_number_of_records( lines( lt_images ) ).
io_response->set_data( lt_images ).
ENDMETHOD.
ENDCLASS.</code></pre><P><U><EM>*Replace* `<external domain url>`, `<your_client_id>`, and `<your_client_secret>` *with real values.*</EM></U></P><P><STRONG>5. Publish and Test Your OData Service</STRONG></P><UL><LI>Use `/IWFND/MAINT_SERVICE` or the service binding UI in Eclipse/ADT to publish your OData service.</LI><LI>Test the endpoint with a filter on `PickTicketId`:<BR />Example: <SPAN>/sap/opu/odata/sap/ZSB_IMAGESET/ZCE_IMAGESET?$filter= pickticketid eq '123456'</SPAN></LI></UL><P><U><STRONG>Key Takeaways</STRONG></U></P><UL><LI>Custom Entities in RAP allow you to virtualize data from external sources as standard OData.</LI><LI>You can leverage the **HTTP client functionalities** in ABAP to fetch data from any REST API.</LI><LI>RAP and ABAP Cloud stack are open for API-driven architectures.</LI><LI>Make sure to treat secrets and API credentials securely—avoid hardcoding in productive code.</LI></UL><P><STRONG>Conclusion</STRONG></P><P>You now have a **custom OData service** in SAP S/4HANA PCE, powered by the RAP framework, which brings in dynamic data from an **external API**. You can enhance, secure, and extend this pattern for many integration use cases—be it analytics dashboards, mobile apps, or UI5/Fiori apps!</P><P>Let me know your feedback, or reach out for specific questions.</P>2026-01-08T14:08:04.222000+01:00https://community.sap.com/t5/abap-blog-posts/creating-a-rest-api-web-service-in-sap-for-web-app-consumption/ba-p/14278981Creating a REST API (web service) in SAP for Web App Consumption.2026-01-08T18:27:24.050000+01:00Srikanta_Kumar_Goudahttps://community.sap.com/t5/user/viewprofilepage/user-id/1459051<P><STRONG>Step by step process</STRONG> for creating a rest API (Web Service) in sap, which will be consume by<BR />web based application:</P><P>1. Create Table- ZDT_WEB_COMPLAIN</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_0-1763041199392.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340121i769D44F739B384A1/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_0-1763041199392.png" alt="Srikanta_Kumar_Gouda_0-1763041199392.png" /></span></P><P>2. Create SNRO Number range object: ZSNRO_WEB</P><P>Transaction code: SNRO</P><P>Give SNRO name and click on create </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_2-1763041367838.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340127i4E51345D9192E6EE/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_2-1763041367838.png" alt="Srikanta_Kumar_Gouda_2-1763041367838.png" /></span></P><P>enter short text, long text, number length domain, % warning and save.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_1-1763041267557.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340122iC5042BD362A59CB8/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_1-1763041267557.png" alt="Srikanta_Kumar_Gouda_1-1763041267557.png" /></span></P><P>click on Ranges--> click on change intervals--> click on new entries.</P><P>insert the number range and save.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_3-1763041516000.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340135i27694B4A93D9A812/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_3-1763041516000.png" alt="Srikanta_Kumar_Gouda_3-1763041516000.png" /></span></P><P>3. Create request handler class : ZCL_WEBCOMPLAINT_REQHANDLR</P><P>Go to Transaction Code - SE24 and create request handler class.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_4-1763041612509.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340137i195ECA3CCDEE55F0/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_4-1763041612509.png" alt="Srikanta_Kumar_Gouda_4-1763041612509.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_5-1763041700880.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340138i3C6AEFCE48045A67/image-size/large/is-moderation-mode/true?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_5-1763041700880.png" alt="Srikanta_Kumar_Gouda_5-1763041700880.png" /></span></P><P><SPAN class="">enter the superclass as </SPAN><STRONG><SPAN class="">cl_rest_http_handler</SPAN></STRONG><SPAN class=""> and click on save.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_6-1763041718291.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340139i16C0D354FF4E7548/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_6-1763041718291.png" alt="Srikanta_Kumar_Gouda_6-1763041718291.png" /></span></P><P>Go to method section to see super class methods.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_7-1763041805586.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340140iB1A80FFEEEF8FB4D/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_7-1763041805586.png" alt="Srikanta_Kumar_Gouda_7-1763041805586.png" /></span></P><P>select the <STRONG>get_root_handler</STRONG> and click on redefine. Add the below code in between method and endmethod.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_8-1763041875753.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340141i486DF563A3AC3500/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_8-1763041875753.png" alt="Srikanta_Kumar_Gouda_8-1763041875753.png" /></span><BR /><BR /></P><pre class="lia-code-sample language-abap"><code>DATA(lo_router) = NEW cl_rest_router( ).
lo_router->attach( EXPORTING
iv_template = '/webcomplaint' " Unified Name for Reso
iv_handler_class = 'ZCL_WEBCOMPLAINT_REQHANDLR' " Object Type Name
" it_parameter = " Resource contructor parameters </code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_10-1763041998349.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340143i9477F4FC23C1B544/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_10-1763041998349.png" alt="Srikanta_Kumar_Gouda_10-1763041998349.png" /></span></P><P><SPAN class=""><SPAN class="">save and activate the class</SPAN></SPAN><SPAN class=""> .</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_9-1763041913576.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340142i241A191778E3DD78/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_9-1763041913576.png" alt="Srikanta_Kumar_Gouda_9-1763041913576.png" /></span></P><P>4. Create resource provider class: ZCL_WEBCOMPLAINT_REQPROVIDER.</P><P>Go to Transaction Code - SE24 and create request provider class.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_3-1763043027077.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340160i9F4B2A838BDC1E83/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_3-1763043027077.png" alt="Srikanta_Kumar_Gouda_3-1763043027077.png" /></span></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_0-1763042941293.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340153i71B334504B2352D5/image-size/large/is-moderation-mode/true?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_0-1763042941293.png" alt="Srikanta_Kumar_Gouda_0-1763042941293.png" /></span></P><P><SPAN class="">enter the superclass as <STRONG> cl_rest_resource </STRONG></SPAN><SPAN class=""> and click on save.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_4-1763043198086.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340165i6DCC52EA462C8B7D/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_4-1763043198086.png" alt="Srikanta_Kumar_Gouda_4-1763043198086.png" /></span></P><P>Go to method section to see super class methods.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_5-1763043230879.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340166iC3E1BAF62982A06C/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_5-1763043230879.png" alt="Srikanta_Kumar_Gouda_5-1763043230879.png" /></span></P><P>select the method 'GET' and click on redefine . Add the below code.</P><pre class="lia-code-sample language-abap"><code>DATA : lv_string1 TYPE vbeln, "string,
lv_string2 TYPE string,
gs_complaint TYPE zdt_web_complain.
lv_string1 = mo_request->get_uri_query_parameter( iv_name = 'DOC_ID' ).
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = lv_string1
IMPORTING
output = lv_string1.
SELECT SINGLE * FROM zdt_web_complain INTO CORRESPONDING FIELDS OF gs_complaint
WHERE doc_id = lv_string1.
/ui2/cl_json=>serialize(
EXPORTING
data = gs_complaint " Data to serialize
* COMPRESS = ABAP_FALSE " Skip empty elements
* NAME = " Object name
* PRETTY_NAME = " Pretty Print property names
* TYPE_DESCR = " Data descriptor
RECEIVING
r_json = lv_string2 " JSON string
).
mo_response->create_entity( )->set_string_data( iv_data = lv_string2 ).
mo_response->set_header_field(
EXPORTING
iv_name = 'Content-Type' " Header Name
iv_value = 'application/json' " Header Value
).
save and activate .
similarly redefine the post method and add the below code.
METHOD if_rest_resource~post.
*CALL METHOD SUPER->IF_REST_RESOURCE~POST
* EXPORTING
* IO_ENTITY =
* .
DATA : lv_string1 TYPE vbeln, "string,
lv_string2 TYPE string,
lv_response TYPE string,
gs_complaint TYPE zdt_web_complain.
DATA(lo_entity) = mo_request->get_entity( ).
DATA(lo_response) = mo_response->create_entity( ).
"read string data i.e json
DATA(lv_data) = lo_entity->get_string_data( ).
/ui2/cl_json=>deserialize(
EXPORTING
json = lv_data " JSON string
* PRETTY_NAME = " Pretty Print property names
CHANGING
data = gs_complaint " Data to serialize
).
* CATCH CX_SY_MOVE_CAST_ERROR. "
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZSNRO_WEB'
quantity = '1'
* SUBOBJECT = ' '
* TOYEAR = '0000'
* IGNORE_BUFFER = ' '
IMPORTING
number = gs_complaint-doc_id
* QUANTITY =
* RETURNCODE =
EXCEPTIONS
interval_not_found = 1
number_range_not_intern = 2
object_not_found = 3
quantity_is_0 = 4
quantity_is_not_1 = 5
interval_overflow = 6
buffer_overflow = 7
OTHERS = 8.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
gs_complaint-createdby = sy-uname.
gs_complaint-createdon = sy-datum.
gs_complaint-time = sy-uzeit.
INSERT INTO zdt_web_complain VALUES gs_complaint.
/ui2/cl_json=>serialize(
EXPORTING
data = gs_complaint " Data to serialize
* COMPRESS = ABAP_FALSE " Skip empty elements
* NAME = " Object name
* PRETTY_NAME = " Pretty Print property names
* TYPE_DESCR = " Data descriptor
RECEIVING
r_json = lv_response
).
" JSON string
lo_response->set_string_data( iv_data = lv_response ).
ENDMETHOD.</code></pre><P> </P><P><SPAN>save and activate. </SPAN><SPAN> </SPAN></P><P><SPAN>similarly redefine the <STRONG>post</STRONG> method and add the below code.</SPAN><SPAN> </SPAN></P><pre class="lia-code-sample language-abap"><code>METHOD if_rest_resource~post.
*CALL METHOD SUPER->IF_REST_RESOURCE~POST
* EXPORTING
* IO_ENTITY =
* .
DATA : lv_string1 TYPE vbeln, "string,
lv_string2 TYPE string,
lv_response TYPE string,
gs_complaint TYPE zdt_web_complain.
DATA(lo_entity) = mo_request->get_entity( ).
DATA(lo_response) = mo_response->create_entity( ).
"read string data i.e json
DATA(lv_data) = lo_entity->get_string_data( ).
/ui2/cl_json=>deserialize(
EXPORTING
json = lv_data " JSON string
* PRETTY_NAME = " Pretty Print property names
CHANGING
data = gs_complaint " Data to serialize
).
* CATCH CX_SY_MOVE_CAST_ERROR. "
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZSNRO_WEB'
quantity = '1'
* SUBOBJECT = ' '
* TOYEAR = '0000'
* IGNORE_BUFFER = ' '
IMPORTING
number = gs_complaint-doc_id
* QUANTITY =
* RETURNCODE =
EXCEPTIONS
interval_not_found = 1
number_range_not_intern = 2
object_not_found = 3
quantity_is_0 = 4
quantity_is_not_1 = 5
interval_overflow = 6
buffer_overflow = 7
OTHERS = 8.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
gs_complaint-createdby = sy-uname.
gs_complaint-createdon = sy-datum.
gs_complaint-time = sy-uzeit.
INSERT INTO zdt_web_complain VALUES gs_complaint.
/ui2/cl_json=>serialize(
EXPORTING
data = gs_complaint " Data to serialize
* COMPRESS = ABAP_FALSE " Skip empty elements
* NAME = " Object name
* PRETTY_NAME = " Pretty Print property names
* TYPE_DESCR = " Data descriptor
RECEIVING
r_json = lv_response
).
" JSON string
lo_response->set_string_data( iv_data = lv_response ).
ENDMETHOD.</code></pre><P>save and activate.</P><P>5. Create SICF service:</P><P>Execute Transaction code SICF.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_6-1763043575157.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340172iFBD1AB77B18E3408/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_6-1763043575157.png" alt="Srikanta_Kumar_Gouda_6-1763043575157.png" /></span></P><P><SPAN class=""><SPAN class="">click on execute.</SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_7-1763043591676.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340173i51D16A685E3DB830/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_7-1763043591676.png" alt="Srikanta_Kumar_Gouda_7-1763043591676.png" /></span></P><P> </P><P><SPAN class=""><SPAN class="">give the service element name and press enter.</SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_9-1763043616789.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340175i7375CA18CA8E3E75/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_9-1763043616789.png" alt="Srikanta_Kumar_Gouda_9-1763043616789.png" /></span></P><P><SPAN class=""><SPAN class="">enter description and request handler class name [ <STRONG><SPAN class="">zcl_webcomplaint_reqhandlr</SPAN></STRONG><SPAN class=""> </SPAN> ]and save. </SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_10-1763043799234.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340177i8F51EE3302DE360C/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_10-1763043799234.png" alt="Srikanta_Kumar_Gouda_10-1763043799234.png" /></span></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_11-1763043895392.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340179i147691150A570A5B/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_11-1763043895392.png" alt="Srikanta_Kumar_Gouda_11-1763043895392.png" /></span></P><P>on SICF main screen activate the service.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_12-1763043944346.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340180iC89DB21ED47DE514/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_12-1763043944346.png" alt="Srikanta_Kumar_Gouda_12-1763043944346.png" /></span></P><P>now before testing add some record in table, then test the service.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_13-1763043960343.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/340181i5405F611FF2E76A6/image-size/large?v=v2&px=999" role="button" title="Srikanta_Kumar_Gouda_13-1763043960343.png" alt="Srikanta_Kumar_Gouda_13-1763043960343.png" /></span></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srikanta_Kumar_Gouda_0-1764241570181.png" style="width: 844px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/345907i79B2C45BD1687039/image-dimensions/844x53/is-moderation-mode/true?v=v2" width="844" height="53" role="button" title="Srikanta_Kumar_Gouda_0-1764241570181.png" alt="Srikanta_Kumar_Gouda_0-1764241570181.png" /></span></P>2026-01-08T18:27:24.050000+01:00https://community.sap.com/t5/abap-blog-posts/dynamic-language-dependent-text-retrieval-in-rap-with-managed/ba-p/14276344Dynamic Language-Dependent Text Retrieval in RAP with Managed Implementation2026-01-10T19:57:33.033000+01:00raghu_pshttps://community.sap.com/t5/user/viewprofilepage/user-id/150897<P><SPAN>In today’s globalized business environment, applications must cater to users from different linguistic backgrounds. The SAP ABAP RESTful Application Programming (RAP) model provides a robust way to implement dynamic multi-language support in managed scenarios. In this blog, we’ll explore how to achieve this step-by-step, complete with coding examples and output illustrations. </SPAN><SPAN> </SPAN></P><P><SPAN>Overview of Multi-Language Support in RAP </SPAN><SPAN> </SPAN></P><P><SPAN>RAP applications use language-dependent text tables to store translations. These texts are fetched dynamically based on the user's logon language. Managed scenarios in RAP handle database operations automatically, simplifying the implementation of language-specific features. </SPAN><SPAN> </SPAN></P><P><SPAN>Prerequisites </SPAN><SPAN> </SPAN></P><OL><LI><STRONG><SPAN>SAP BTP ABAP Environment </SPAN></STRONG><SPAN> </SPAN></LI></OL><OL><LI><STRONG><SPAN>ABAP Development Tools (ADT) </SPAN></STRONG><SPAN> </SPAN></LI></OL><OL><LI><STRONG><SPAN>Basic RAP Knowledge </SPAN></STRONG><SPAN> </SPAN></LI></OL><OL><LI><STRONG><SPAN>Authorization to create CDS views and service definitions </SPAN></STRONG><SPAN> </SPAN></LI></OL><P><SPAN> Scenario </SPAN><SPAN> </SPAN></P><P><SPAN>We will create an application that manages product descriptions in multiple languages using RAP managed scenarios. </SPAN><SPAN> </SPAN></P><P><I><SPAN>Tables Required: </SPAN></I><SPAN> </SPAN></P><OL><LI><STRONG><SPAN>Main Table: ZPRODUCT Contains product details. </SPAN></STRONG><SPAN> </SPAN></LI></OL><OL><LI><STRONG><SPAN>Text Table: ZPRODUCT_TEXT Stores language-dependent product descriptions. </SPAN></STRONG><SPAN> </SPAN></LI></OL><P><SPAN>Step-by-Step Implementation </SPAN><SPAN> <BR /></SPAN><I><SPAN>Create Database Tables </SPAN></I><SPAN> <BR /></SPAN><SPAN class=""><SPAN class="">Main Table: ZPRODUCT</SPAN></SPAN><SPAN class=""> </SPAN><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="raghu_ps_0-1764001593416.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/344534iCCF4137B07DB04B7/image-size/large?v=v2&px=999" role="button" title="raghu_ps_0-1764001593416.png" alt="raghu_ps_0-1764001593416.png" /></span></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Product table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zna_t_product {
key product_id : abap.char(10) not null;
category : abap.char(20);
price : abap.dec(15,2);
} </code></pre><P><BR /><BR /><SPAN class=""><SPAN class="">Text Table: ZPRODUCT_TEXT</SPAN></SPAN><SPAN class=""> </SPAN><BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="raghu_ps_1-1764001693518.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/344535i7B6124D4F8E12C6B/image-size/large?v=v2&px=999" role="button" title="raghu_ps_1-1764001693518.png" alt="raghu_ps_1-1764001693518.png" /></span></P><P> </P><P><BR /><BR /></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Product text table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zproduct_text {
key product_id : abap.char(10) not null;
key language : abap.lang not null;
description : abap.string(0);
} </code></pre><P><I><SPAN>Create CDS Views </SPAN></I><SPAN> </SPAN></P><P><SPAN> Define View for Text Table (ZPRODUCT_TEXT) </SPAN><SPAN> <BR /></SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Product root view'
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZC_NA_PRODUCT as select from zna_t_product
{
key product_id as ProductId,
category as Category,
price as Price
} </code></pre><P><SPAN class=""><SPAN class="">Combine Root and Text Views </SPAN></SPAN><SPAN class=""> <BR /></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="raghu_ps_2-1764001838674.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/344536iEF0B59B5CEB8F3E6/image-size/large?v=v2&px=999" role="button" title="raghu_ps_2-1764001838674.png" alt="raghu_ps_2-1764001838674.png" /></span></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'projection view for product text'
@Metadata.ignorePropagatedAnnotations: true
define root view entity zna_productwithtext
provider contract transactional_query as projection on ZC_NA_PRODUCT as _texts
association [0..*] to ZC_NA_PRODUCT_TEXT on $projection.ProductId = _texts.ProductId
{
key ProductId,
Category,
Price
} </code></pre><P><I><SPAN>Implement Multi-Language Logic in Behavior Definition </SPAN></I><SPAN> </SPAN></P><P><SPAN>Behavior Definition for Root Entity:</SPAN><SPAN> <BR /></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="raghu_ps_3-1764001927375.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/344537i3241B3F7FFEC65FE/image-size/large?v=v2&px=999" role="button" title="raghu_ps_3-1764001927375.png" alt="raghu_ps_3-1764001927375.png" /></span></P><pre class="lia-code-sample language-abap"><code>managed implementation in class ZBP_Product unique;
define behavior for ZC_Product alias Product
persistent table zproduct
lock master
authorization master
{
create;
update;
delete;
association _Texts { create; }
}
define behavior for ZC_ProductText alias ProductText
persistent table zproduct_text
lock dependent by parent Product
{
create; update; delete;
}
"Implement Behavior Pool Class
"Develop the behavior pool class to handle business logic.
CLASS zbp_product DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES: if_abap_behavior_handler.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zbp_product IMPLEMENTATION.
METHOD if_abap_behavior_handler~create.
DATA: ls_product TYPE zproduct,
ls_product_text TYPE zproduct_texts,
lt_product_text TYPE TABLE OF zproduct_texts.
" Loop through incoming data for product creation
LOOP AT entities INTO DATA(ls_data).
" Step 1: Create Product in Main Table
ls_product-product_id = ls_data-product_id.
ls_product-category = ls_data-category.
ls_product-price = ls_data-price.
INSERT INTO zproduct VALUES ls_product.
" Step 2: Create Product Texts (in multiple languages if needed)
LOOP AT ls_data-_Texts INTO DATA(ls_text_data).
CLEAR ls_product_text.
ls_product_text-product_id = ls_data-product_id.
ls_product_text-spras = ls_text_data-spras.
ls_product_text-product_name = ls_text_data-product_name.
ls_product_text-description = ls_text_data-description.
INSERT INTO zproduct_texts VALUES ls_product_text.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
ENDCLASS. </code></pre><P><SPAN>Create Service Definition and Binding </SPAN><SPAN> </SPAN></P><P><SPAN>Expose the CDS view through a service definition and bind it to an OData service.</SPAN><SPAN> <BR /></SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Product Service Definition'
define service ZUI_Product {
expose ZC_Product;
} </code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="raghu_ps_4-1764001998784.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/344538i6D21DF08531ED09A/image-size/large?v=v2&px=999" role="button" title="raghu_ps_4-1764001998784.png" alt="raghu_ps_4-1764001998784.png" /></span></P><P><SPAN>English Session</SPAN><SPAN>:</SPAN> <SPAN> <BR /></SPAN><SPAN>Product </SPAN><SPAN>ID</SPAN><SPAN>:</SPAN> <SPAN>00001</SPAN> <SPAN> <BR /></SPAN><SPAN>Category</SPAN><SPAN>:</SPAN><SPAN> Electronics </SPAN><SPAN> <BR /></SPAN><SPAN>Product </SPAN><SPAN>Name</SPAN><SPAN>:</SPAN><SPAN> Laptop </SPAN><SPAN> <BR /></SPAN><SPAN>Description</SPAN><SPAN>:</SPAN><SPAN> High</SPAN><SPAN>-</SPAN><SPAN>performance laptop </SPAN><SPAN> <BR /></SPAN><SPAN> <BR /></SPAN><SPAN>German Session</SPAN><SPAN>:</SPAN> <SPAN> <BR /></SPAN><SPAN>Produkt</SPAN><SPAN>-</SPAN><SPAN>ID</SPAN><SPAN>:</SPAN> <SPAN>00001</SPAN> <SPAN> <BR /></SPAN><SPAN>Kategorie</SPAN><SPAN>:</SPAN><SPAN> Elektronik </SPAN><SPAN> <BR /></SPAN><SPAN>Produktname</SPAN><SPAN>:</SPAN><SPAN> Laptop </SPAN><SPAN> <BR /></SPAN><SPAN>Beschreibung</SPAN><SPAN>:</SPAN><SPAN> Hochleistungs</SPAN><SPAN>-</SPAN><SPAN>Laptop</SPAN><SPAN> </SPAN></P><P><SPAN> </SPAN></P><P><SPAN>Conclusion </SPAN><SPAN> </SPAN></P><P><SPAN>This blog demonstrated how to implement dynamic multi-language support in RAP managed scenarios. By leveraging CDS views, associations, and behavior definitions, you can build applications that cater to a diverse user base. This approach ensures scalability, performance, and an excellent user experience.</SPAN><SPAN> </SPAN></P><P><SPAN> </SPAN></P>2026-01-10T19:57:33.033000+01:00https://community.sap.com/t5/abap-blog-posts/print-preview-from-rap-action-with-v4-odata-and-multiple-selection/ba-p/14280581Print Preview from RAP Action with v4 OData and Multiple Selection2026-01-10T20:04:09.214000+01:00Gxalmethttps://community.sap.com/t5/user/viewprofilepage/user-id/905<H2 id="toc-hId-1766377671"><SPAN>Business Requirements</SPAN></H2><P class=""><SPAN>The business needs to implement a print preview functionality that:</SPAN></P><UL><LI><P class=""><SPAN>Allows users to select multiple documents in a Fiori report</SPAN></P></LI><LI><P class=""><SPAN>Merges all selected documents into a single PDF file</SPAN></P></LI><LI><P class=""><SPAN>Enables users to print all selected documents at once</SPAN></P></LI></UL><P> </P><H2 id="toc-hId-1569864166"><SPAN>Implementation Steps</SPAN></H2><H2 id="toc-hId-1373350661"><SPAN>Backend Implementation: RAP Action with PDF Generation</SPAN></H2><UL><LI><SPAN>Behavior Definition</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>action PrintPreview result [1] zspdfgeneration ;</code></pre><UL><LI><SPAN>Action Implementation</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>METHOD PrintPreview.
**** Generate the pdf
....
....
....
LOOP AT keys into data(keyLine).
CALL FUNCTION 'FP_JOB_OPEN'
CHANGING ie_outputparams = ls_outputparams
EXCEPTIONS cancel = 1
usage_error = 2
system_error = 3
internal_error = 4
OTHERS = 5.
" Call the Function Module Name for the Adobe Form
TRY.
CALL FUNCTION 'FP_FUNCTION_MODULE_NAME'
EXPORTING i_name = lv_function
IMPORTING e_funcname = lv_funcname.
CALL FUNCTION lv_funcname
EXPORTING /1bcdwb/docparams = ls_docparams
....
IMPORTING /1bcdwb/formoutput = ls_formout
EXCEPTIONS usage_error = 1
system_error = 2
internal_error = 3
OTHERS = 4.
ls_meta-title = |YourPrintFileName| & |{ key }|.
DATA(lv_field_name) = |NameOfYourFile| & |{ {key} }_| & |{ sy-datum }_{ sy-uzeit }.pdf|.
lv_field_name = condense( val = lv_field_name
from = ` `
to = `` ).
ls_lheader-name = 'Content-Disposition'.
ls_lheader-value = |inline; filename={fileName}| & |{ ls_transport_order-TransportationOrderUUID }_|.
TRY.
" Create PDF Object.
lo_fp = cl_fp=>get_reference( ).
lo_pdfobj = lo_fp->create_pdf_object( connection = 'ADS' ).
lo_pdfobj->set_document( pdfdata = ls_formout-pdf ).
lo_pdfobj->set_metadata( metadata = ls_meta ).
lo_pdfobj->execute( ).
" Get the PDF content back with title
lo_pdfobj->get_document( IMPORTING pdfdata = DATA(lv_value) ).
lo_merger->add_document( ls_formout-pdf ).
CLEAR lv_value.
CATCH cx_fp_runtime_internal
cx_fp_runtime_system
cx_fp_runtime_usage INTO lx_fpex.
ENDTRY.
CATCH cx_root.
ENDTRY.
ENDLOOP.
TRY.
" Merge both documents and receive the result
DATA(lv_merged_PDF) = lo_merger->merge_documents( ).
CATCH cx_rspo_pdf_merger INTO DATA(l_exception). " TODO: variable is assigned but never used (ABAP cleaner)
" Add a useful error handling here
ENDTRY.
DATA lv_base64 TYPE string.
CALL FUNCTION 'SCMS_BASE64_ENCODE_STR'
EXPORTING input = lv_merged_PDF
IMPORTING output = lv_base64.
READ TABLE keys INTO DATA(keyOne) INDEX 1.
result = VALUE #( ( %key-DOcType = keyOne-DOcType
%key-WBOriginDoc = keyOne-WBOriginDoc
%key-WBOriginDocItem = keyOne-WBOriginDocItem
%param = VALUE zspdfgenration( field_name = lv_field_name
mime_type = 'application/pdf'
pdf_content = lv_base64 ) ) ).
ENDMETHOD.</code></pre><UL><LI><SPAN>Metadata Annotation with ChangeSet</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>: {
lineItem: [{
type: #FOR_ACTION,
dataAction: 'PrintPreview',
label: 'Print Preview',
invocationGrouping: #ChangeSet
}]
}</code></pre><H3 id="toc-hId-1305919875"><SPAN>Frontend Implementation: Controller Extension</SPAN></H3><UL><LI><SPAN>Generate a controller extension and a custom action to call our print preview v4 action.</SPAN></LI></UL><pre class="lia-code-sample language-javascript"><code>PrintPreviewV4: async function () {
const oExtensionAPI = this.base.getExtensionAPI();
const aSelectedContexts = oExtensionAPI.getSelectedContexts();
// Message if no rows selected
if (!aSelectedContexts || aSelectedContexts.length === 0) {
MessageToast.show("No rows selected.");
return;
}
// Call the action with the rows selected in only one call
try {
const oPageController = this.base.getView().getController();
const oResultContext = await oPageController.editFlow.invokeAction(
"com.sap.gateway.srvd.yourService.YourAction",
contexts: aSelectedContexts ,
invocationGrouping: "ChangeSet"
}
);
const oData = oResultContext[0]?.value?.oBinding?.oCachePromise?.getResult()?.oPromise?.getResult();
// With the file then you can allow preview to the user or download directly
},</code></pre><H2 id="toc-hId-980323651"><SPAN>Conclusion</SPAN></H2><P class=""><SPAN>This implementation provides a solution for generating merged PDF previews from multiple selected documents in Fiori applications. The combination of RAP actions with ChangeSet grouping and proper frontend handling ensures good performance and user experience.</SPAN></P><P> </P>2026-01-10T20:04:09.214000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/custom-entity-with-actions/ba-p/14298105Custom Entity With Actions2026-01-11T23:45:05.274000+01:00kalmeshhttps://community.sap.com/t5/user/viewprofilepage/user-id/1490139<P><STRONG><SPAN>What is a Custom Entity in RAP?</SPAN></STRONG><SPAN> </SPAN></P><P><SPAN>In the </SPAN><STRONG><SPAN>ABAP RESTful Application Programming Model (RAP)</SPAN></STRONG><SPAN>, a Custom Entity is a CDS entity that does not map to a database table. Instead, it represents virtual data provided by custom ABAP logic. It is used when:</SPAN><SPAN> </SPAN></P><UL><LI><SPAN>You need to expose calculated or external data.</SPAN><SPAN> </SPAN></LI></UL><UL><LI><SPAN>You want to integrate non-database sources (e.g., APIs, external systems).</SPAN><SPAN> </SPAN></LI></UL><UL><LI><SPAN>You require custom operations beyond standard CRUD.</SPAN><SPAN> </SPAN></LI></UL><P><SPAN>Adding an Action to a Custom Entity allows you to trigger specific business operations directly from the UI.</SPAN><SPAN> </SPAN></P><P><SPAN>Now we will look at one practical example for</SPAN><STRONG><SPAN> Custom Entity with an Action</SPAN></STRONG><SPAN> in RAP, where the action will be performed based on the user click on the button called </SPAN><STRONG><SPAN>CustomAction</SPAN></STRONG><SPAN> , when user clicks on it the icon will be changed.</SPAN><SPAN> </SPAN></P><P><SPAN><SPAN class=""><SPAN class="">First, we will begin by creating a database table with the fields shown in the screenshot below.</SPAN></SPAN><SPAN class=""> </SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="kalmesh_0-1766832080906.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356220iC0945965AA796F9D/image-size/large?v=v2&px=999" role="button" title="kalmesh_0-1766832080906.png" alt="kalmesh_0-1766832080906.png" /></span></P><P><SPAN class=""><SPAN class="">Next, we will create a Custom Entity and define its implementation class, as the Custom Entity will be realized through the logic implemented in that class<SPAN> <STRONG>"</STRONG></SPAN></SPAN></SPAN><STRONG><SPAN class=""><SPAN class="">ZGA_CL_QRY_PRVD_C_ACT"</SPAN></SPAN><SPAN class=""><SPAN class="">.</SPAN></SPAN><SPAN class=""> </SPAN></STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="kalmesh_1-1766832144208.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356221iF68A33E3914A42CE/image-size/large?v=v2&px=999" role="button" title="kalmesh_1-1766832144208.png" alt="kalmesh_1-1766832144208.png" /></span></P><P><SPAN class=""><SPAN class="">Now, we will implement the logic in the class and use the mandatory interface<SPAN> </SPAN></SPAN></SPAN><STRONG><SPAN class=""><SPAN class="">IF_RAP_QUERY_PROVIDER</SPAN></SPAN></STRONG><SPAN class=""><SPAN class=""><SPAN> </SPAN>to handle data retrieval for the Custom Entity.</SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="kalmesh_2-1766832199325.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356222i55919012BA7B99BD/image-size/large?v=v2&px=999" role="button" title="kalmesh_2-1766832199325.png" alt="kalmesh_2-1766832199325.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="kalmesh_3-1766832228505.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356223iC293655E1C46FFBC/image-size/large?v=v2&px=999" role="button" title="kalmesh_3-1766832228505.png" alt="kalmesh_3-1766832228505.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="kalmesh_4-1766832260644.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356224iDA33478AA477F830/image-size/large?v=v2&px=999" role="button" title="kalmesh_4-1766832260644.png" alt="kalmesh_4-1766832260644.png" /></span></P><P> </P><P> </P><P> </P><P> </P><P> </P><P> </P><P> </P><P> </P><P> </P><P><SPAN class=""><SPAN class="">To adjust the data, we have implemented another class named<SPAN> </SPAN></SPAN></SPAN><SPAN class=""><STRONG><SPAN class="">ZGA_CL_BS_DEMO_ADJUST_DATA</SPAN></STRONG><SPAN class="">.</SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="kalmesh_5-1766832307318.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356225iCFBAEE44B32120F2/image-size/large?v=v2&px=999" role="button" title="kalmesh_5-1766832307318.png" alt="kalmesh_5-1766832307318.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="kalmesh_6-1766832329748.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356226i00CB29C2A5AC119E/image-size/large?v=v2&px=999" role="button" title="kalmesh_6-1766832329748.png" alt="kalmesh_6-1766832329748.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="kalmesh_7-1766832350606.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356227i388347BF2A873C50/image-size/large?v=v2&px=999" role="button" title="kalmesh_7-1766832350606.png" alt="kalmesh_7-1766832350606.png" /></span></P><P><SPAN class=""><SPAN class="">Our next task is to create a Behavior Definition for the Custom Entity<STRONG> </STRONG></SPAN></SPAN><STRONG><SPAN class=""><SPAN class="">ZGA_I_CUST_ACT_DEMO</SPAN></SPAN></STRONG><SPAN class=""><SPAN class=""><SPAN> </SPAN>to enable actions and logic implementation.</SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="kalmesh_8-1766832393668.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356228iAECD7C2410EF7417/image-size/large?v=v2&px=999" role="button" title="kalmesh_8-1766832393668.png" alt="kalmesh_8-1766832393668.png" /></span></P><P><SPAN class=""><SPAN class="">Next, we dive into implementing the Behavior Definition logic in the class<SPAN> </SPAN></SPAN></SPAN><STRONG><SPAN class=""><SPAN class="">ZBP_GA_I_CUST_ACT_DEMO</SPAN></SPAN><SPAN class=""><SPAN class="">.</SPAN></SPAN><SPAN class=""> </SPAN></STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="kalmesh_9-1766832426916.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356229iF70A9CC482E83532/image-size/large?v=v2&px=999" role="button" title="kalmesh_9-1766832426916.png" alt="kalmesh_9-1766832426916.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_10-1766832456178.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356230i2885317CFCD98496/image-size/large?v=v2&px=999" role="button" title="kalmesh_10-1766832456178.png" alt="kalmesh_10-1766832456178.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_11-1766832498326.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356231i9B1588FFEF011058/image-size/large?v=v2&px=999" role="button" title="kalmesh_11-1766832498326.png" alt="kalmesh_11-1766832498326.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_12-1766832520415.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356232iDD4420BF98C30195/image-size/large?v=v2&px=999" role="button" title="kalmesh_12-1766832520415.png" alt="kalmesh_12-1766832520415.png" /></span></P><P><SPAN class=""><SPAN class="">The class shown below serves as the buffer class, which we have<SPAN> </SPAN></SPAN><SPAN class="">utilized</SPAN><SPAN class=""><SPAN> </SPAN>in our Behavior Implementation class.</SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_13-1766832562156.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356233iE4E056DD4B8B7064/image-size/large?v=v2&px=999" role="button" title="kalmesh_13-1766832562156.png" alt="kalmesh_13-1766832562156.png" /></span></P><P><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class="">Our next task is to create the Service Definition and Service Binding, enabling the UI to consume the Custom Entity and its actions.</SPAN></SPAN></SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_14-1766832825025.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356234i2AB488EE42C37280/image-size/large?v=v2&px=999" role="button" title="kalmesh_14-1766832825025.png" alt="kalmesh_14-1766832825025.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_15-1766832846396.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356235i62F457A5489BD6D4/image-size/large?v=v2&px=999" role="button" title="kalmesh_15-1766832846396.png" alt="kalmesh_15-1766832846396.png" /></span></P><P> </P><P><STRONG><SPAN class=""><SPAN class="">Output:</SPAN></SPAN></STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_16-1766832881804.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356236i57FFFB99665254AC/image-size/large?v=v2&px=999" role="button" title="kalmesh_16-1766832881804.png" alt="kalmesh_16-1766832881804.png" /></span></P><P><SPAN class=""><SPAN class="">Once an instance is selected, clicking<SPAN> </SPAN></SPAN></SPAN><STRONG><SPAN class=""><SPAN class="">CustomAction</SPAN></SPAN></STRONG><SPAN class=""><SPAN class=""><SPAN> </SPAN>will dynamically update the icon, as displayed in the screenshot.</SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_17-1766832948228.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356237iD501E9D2A9E5D609/image-size/large?v=v2&px=999" role="button" title="kalmesh_17-1766832948228.png" alt="kalmesh_17-1766832948228.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_18-1766832988928.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356238i4234920EFFA49F20/image-size/large?v=v2&px=999" role="button" title="kalmesh_18-1766832988928.png" alt="kalmesh_18-1766832988928.png" /></span></P><P><SPAN class=""><SPAN class="">The<SPAN> </SPAN></SPAN></SPAN><STRONG><SPAN class=""><SPAN class="">Reset</SPAN></SPAN></STRONG><SPAN class=""><SPAN class=""><SPAN> </SPAN>button is used to restore all icons to their original state.</SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kalmesh_19-1766833046155.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356239i562018DBC6C0CD67/image-size/large?v=v2&px=999" role="button" title="kalmesh_19-1766833046155.png" alt="kalmesh_19-1766833046155.png" /></span></P><P><STRONG><SPAN>Conclusion</SPAN></STRONG><SPAN> </SPAN></P><P><SPAN>Custom Entities with Actions in SAP RAP provide a powerful way to implement dynamic, user-driven functionality without relying on database persistence. In this example, we demonstrated how to create a Custom Entity and enhance it with two actions.</SPAN><SPAN> </SPAN></P><P><SPAN>By leveraging actions, you can enrich your applications with flexible business logic, improve user engagement, and maintain a clean separation between UI and backend logic. This approach is ideal for scenarios where you need virtual data handling combined with interactive operations.</SPAN><SPAN> </SPAN></P>2026-01-11T23:45:05.274000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/research-unmanaged-transactional-buffer-for-restful-application-programming/ba-p/14289323Research: Unmanaged Transactional Buffer For RESTful Application Programming Model2026-01-19T12:23:34.134000+01:00PrusakouIlyahttps://community.sap.com/t5/user/viewprofilepage/user-id/1769851<P class="lia-align-center" style="text-align: center;"><FONT size="5"><FONT face="georgia,palatino"><STRONG>RESEARCH ON UNMANAGED IMPLEMENTATION OF TRANSACTIONAL BUFFER FOR RESTFUL APPLICATION PROGRAMMING MODEL</STRONG></FONT></FONT></P><P class="lia-align-center" style="text-align: center;"><FONT size="2"><EM><FONT face="georgia,palatino"><STRONG>Ilya Prusakou<BR />ABAP Developer & Researcher</STRONG></FONT></EM></FONT></P><P class="lia-align-center" style="text-align: center;"> </P><P class="lia-align-center" style="text-align: center;"><U><FONT face="georgia,palatino" size="5"><STRONG>Abstract</STRONG>.</FONT></U></P><P><FONT size="3">Implementing unmanaged scenarios in the ABAP RESTful Application Programming Model (RAP) remains a significant hurdle for developers due to the opinionated nature of the framework and a lack of production-level examples.<BR />The research addresses the challenge by providing comprehensive blueprint for the unmanaged transactional buffer. This article serves as a practical guide for mastering the internal state management of RAP business objects.</FONT></P><P><FONT size="3"><STRONG>KEYWORDS:</STRONG> <EM>Unmanaged Implementation, Transactional Buffer, Buffer Tables, Complex Data Source, Buffer Preparation, Buffer Flags, Draft Buffer, Buffer Access, Transactional Phase, Save Phase, Late Numbering.</EM></FONT></P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="5"><U><STRONG>Examples</STRONG>.</U></FONT></P><P><FONT face="arial,helvetica,sans-serif" size="3">You can find example of buffer implementation on my github: <A href="https://github.com/IlyaPrusakou/rap-unmanaged-buffer-example.git" target="_blank" rel="noopener nofollow noreferrer">https://github.com/IlyaPrusakou/rap-unmanaged-buffer-example.git</A></FONT></P><P><FONT face="arial,helvetica,sans-serif" size="3">The specific path is <STRONG>src/zpru_transactional_buffer/zpru_cl_example_buffer.clas.abap<BR /></STRONG>You can also review the buffer class code at the bottom of this article. In this post, I will use term "<STRONG>blueprint</STRONG>" referring to the full code of the example implementation.</FONT></P><P><FONT face="arial,helvetica,sans-serif" size="3">Additionally, you can visit my another github rep: <A href="https://github.com/IlyaPrusakou/abap-rap-example.git" target="_blank" rel="noopener nofollow noreferrer">https://github.com/IlyaPrusakou/abap-rap-example.git</A></FONT></P><P><FONT face="arial,helvetica,sans-serif" size="3">My unmanaged imlementation class on which I have provided my research is under the path: <STRONG>src/zpru_um_po/zpru_um_po_implmtns/zbp_pru_u_purcorderhdr_tp.clas.locals_imp.abap</STRONG></FONT></P><P><FONT face="arial,helvetica,sans-serif" size="3">There, you can look onto the local class <STRONG>LCL_BUFFER</STRONG>.</FONT><BR /><FONT face="arial,helvetica,sans-serif" size="3">To check out how to use the buffer for standard operations, you can visit the local handler class <STRONG>LHC_ORDERTP,</STRONG> specifically methods: </FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ORDERTP~CREATE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ORDERTP~READ</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ORDERTP~UPDATE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ORDERTP~DELETE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ORDERTP~RBA_ITEMS_TP</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ORDERTP~CBA_ITEMS_TP</FONT></LI></UL><P><FONT face="arial,helvetica,sans-serif" size="3">For investigation of the child node, you can open the local handler <STRONG>LHC_ITEMTP</STRONG>, specifically methods:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ITEMTP~READ</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ITEMTP~UPDATE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ITEMTP~DELETE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif" size="3">LHC_ITEMTP~RBA_HEADER_TP</FONT></LI></UL><P><FONT face="arial,helvetica,sans-serif" size="3">It is recomended to have a glance into the local saver class <STRONG>LSC_ZPRU_U_PURCORDERHDR_TP</STRONG> to understand how a trigger calculation works and what role method FINALIZE, CHECK_BEFORE_SAVE and SAVE play.</FONT></P><P><FONT face="arial,helvetica,sans-serif" size="3">I also recommend visiting the packages <STRONG>SABAP_DEMOS_RAP</STRONG> and <STRONG>SABAP_DEMOS_RAP_CLOUD</STRONG> to check out various RAP examples, especially BDEF <SPAN><STRONG>DEMO_RAP_UNMANAGED_DRAFT_ROOT</STRONG> and its associated class.</SPAN></FONT></P><P><FONT face="arial,helvetica,sans-serif" size="3">For the <STRONG>blueprint implementation</STRONG> I have used a RAP business object with an <STRONG>unmanaged implementation type</STRONG> and with four nested levels.</FONT><BR /><FONT face="arial,helvetica,sans-serif" size="3">Moreover, I used a late numbering because it requires the maximum implementation effort. I also defined keys using simple character types rather than GUID fields.</FONT><BR /> </P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="5"><U><STRONG>Buffer class</STRONG>.</U></FONT></P><P><FONT face="arial,helvetica,sans-serif">From an implementation perspective a <STRONG>transactinal buffer is ABAP class</STRONG>, usually <STRONG>local class</STRONG> placed in local implementation include of ABAP behavior implementation global class (ABP class). It contains <STRONG>entity buffer tables.<BR /></STRONG>Buffer class can be an ABAP global class as well. However, if you choose this way of implementation you must be aware that global class is considered as external to the ABP class and you will not be able to access <STRONG>internal derived types</STRONG>.</FONT></P><P> </P><TABLE border="1" width="100%"><TBODY><TR><TD height="30px"><FONT face="arial,helvetica,sans-serif"><STRONG>Accessible types</STRONG></FONT></TD><TD height="30px"><FONT face="arial,helvetica,sans-serif"><STRONG>Internal types</STRONG></FONT></TD></TR><TR><TD width="50%" height="30px"><FONT face="arial,helvetica,sans-serif">keys type <STRONG>table for read import</STRONG> bdef\\entity</FONT></TD><TD width="50%" height="30px"><FONT face="arial,helvetica,sans-serif">keys type <STRONG>table for validation</STRONG> bdef\\entity~validation</FONT></TD></TR><TR><TD width="50%" height="57px"><FONT face="arial,helvetica,sans-serif">entitie type <STRONG>table for create</STRONG> bdef\\entity</FONT></TD><TD width="50%" height="57px"><FONT face="arial,helvetica,sans-serif">keys type <STRONG>table for determination</STRONG> bdef\\entity~determination</FONT></TD></TR><TR><TD width="50%" height="30px"><FONT face="arial,helvetica,sans-serif">keys type <STRONG>table for action import</STRONG> bdef\\entity~action</FONT></TD><TD width="50%" height="30px"><FONT face="arial,helvetica,sans-serif">event type <STRONG>table for event</STRONG> bdef~event</FONT></TD></TR><TR><TD width="50%" height="30px"><FONT face="arial,helvetica,sans-serif">etc.</FONT></TD><TD width="50%" height="30px"><FONT face="arial,helvetica,sans-serif">etc.</FONT></TD></TR></TBODY></TABLE><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Entity buffer tables</STRONG>.</U></FONT></P><P><FONT face="arial,helvetica,sans-serif">Entity buffer tables are <STRONG>internal tables</STRONG> defined as <STRONG>static attributes</STRONG> in a buffer class. Their rows contain <STRONG>entity fields</STRONG> from the transactional CDS, <STRONG>flags</STRONG> (changed, deleted) and other <STRONG>components</STRONG>(CID, PID etc.)</FONT><BR /><FONT face="arial,helvetica,sans-serif">In the snippet of code below, you can observe entity buffer tables on rows 42 - 45. Besides the tables themselves, there are <STRONG>types for entity keys</STRONG> and <STRONG>types for the buffer rows</STRONG> as well.</FONT></P><pre class="lia-code-sample language-abap"><code>CLASS zpru_cl_example_buffer DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
" types for entity keys
TYPES: BEGIN OF ts_first_node_db_keys,
firstkey TYPE zr_pru_1st_node-firstkey,
END OF ts_first_node_db_keys.
TYPES: BEGIN OF ts_second_node_db_keys,
firstkey TYPE zr_pru_2nd_node-firstkey,
secondkey TYPE zr_pru_2nd_node-secondkey,
END OF ts_second_node_db_keys.
*****
*****
" types for buffer row
TYPES: BEGIN OF ts_first_node,
instance TYPE zr_pru_1st_node,
cid TYPE abp_behv_cid,
pid TYPE abp_behv_pid,
final_key TYPE ts_first_node_db_keys,
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_first_node.
TYPES: BEGIN OF ts_second_node,
instance TYPE zr_pru_2nd_node,
cid TYPE abp_behv_cid,
pid TYPE abp_behv_pid,
pidparent TYPE abp_behv_pid,
final_key TYPE ts_second_node_db_keys,
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_second_node.
*****
*****
" entity buffer tables
TYPES tt_first_node TYPE TABLE OF ts_first_node WITH EMPTY KEY.
TYPES tt_second_node TYPE TABLE OF ts_second_node WITH EMPTY KEY.
TYPES tt_third_node TYPE TABLE OF ts_third_node WITH EMPTY KEY.
TYPES tt_fourth_node TYPE TABLE OF ts_fourth_node WITH EMPTY KEY.
CLASS-DATA st_first_node TYPE tt_first_node.
CLASS-DATA st_second_node TYPE tt_second_node.
CLASS-DATA st_third_node TYPE tt_third_node.
CLASS-DATA st_fourth_node TYPE tt_fourth_node.
*****
*****
ENDCLASS.</code></pre><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Buffer preparation</STRONG>.</U></FONT></P><P><FONT face="arial,helvetica,sans-serif">The <STRONG>buffer preparation</STRONG> is the process of <STRONG>loading entities</STRONG> from a complex data source (in my case, it is just a transactional CDS) into <STRONG>entity buffer tables</STRONG>, usually executed via <STRONG>static methods</STRONG> (e.g. PREP_ENTITY_NAME) in the buffer class.</FONT><BR /><FONT face="arial,helvetica,sans-serif">Each <STRONG>standard RAP operations</STRONG> will start with the <STRONG>buffer preparation methods</STRONG>. You may know these operations by names such as preloading, fetching, selecting data into the buffer, or even prepopulating the buffer.</FONT><BR /><FONT face="arial,helvetica,sans-serif">Moreover, I want to draw attention to type definitions for the input parameters of the preparation methods - especially to the flag FULL_KEY. The flag belongs to child entities and is used to distinguish between accessing them <STRONG>via association</STRONG> or <STRONG>direct access</STRONG>.</FONT></P><pre class="lia-code-sample language-abap"><code>CLASS zpru_cl_example_buffer DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
*****
*****
"structure types for input parameters for buffer preparation methods
TYPES: BEGIN OF ts_first_node_keys.
INCLUDE TYPE ts_first_node_db_keys.
TYPES: END OF ts_first_node_keys.
TYPES: BEGIN OF ts_second_node_keys.
INCLUDE TYPE ts_second_node_db_keys.
TYPES: full_key TYPE abap_bool,
END OF ts_second_node_keys.
*****
*****
"table types for input parameters for buffer preparation methods
TYPES tt_first_node_keys TYPE TABLE OF ts_first_node_keys WITH EMPTY KEY.
TYPES tt_second_node_keys TYPE TABLE OF ts_second_node_keys WITH EMPTY KEY.
*****
*****
" buffer preparation methods
CLASS-METHODS prep_first_node_buffer
IMPORTING !keys TYPE tt_first_node_keys.
CLASS-METHODS prep_second_node_buffer
IMPORTING !keys TYPE tt_second_node_keys.
CLASS-METHODS prep_third_node_buffer
IMPORTING !keys TYPE tt_third_node_keys.
CLASS-METHODS prep_fourth_node_buffer
IMPORTING !keys TYPE tt_fourth_node_keys.
ENDCLASS.</code></pre><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Root preparation</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">Let's check code inside the preparation method for root entity.</FONT> </P><pre class="lia-code-sample language-abap"><code> METHOD prep_first_node_buffer.
DATA lt_keys_2_read LIKE keys.
DATA lt_entity_result TYPE STANDARD TABLE OF zr_pru_1st_node WITH EMPTY KEY.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key>).
IF line_exists( zpru_cl_example_buffer=>st_first_node[ instance-firstkey = <ls_key>-firstkey ] ).
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_key_2_read>).
<ls_key_2_read>-firstkey = <ls_key>-firstkey.
ENDIF.
ENDLOOP.
IF lt_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_1st_node
FOR ALL ENTRIES IN lt_keys_2_read
WHERE firstkey = lt_keys_2_read-firstkey
INTO TABLE lt_entity_result.
IF sy-subrc = 0.
LOOP AT lt_entity_result ASSIGNING FIELD-SYMBOL(<ls_result>).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_first_node ASSIGNING FIELD-SYMBOL(<ls_buffer>).
<ls_buffer>-instance = <ls_result>.
ENDLOOP.
ENDIF.
ENDIF.
ENDMETHOD.</code></pre><P><FONT face="arial,helvetica,sans-serif"><STRONG>Root preparation logic</STRONG> is the following:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif">check if the entry exists in the buffer using the full key</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">if yes:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">do nothing</FONT></LI></UL></LI><LI><FONT face="arial,helvetica,sans-serif">if no:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">collect keys to select data from the data source</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">select data using the full key</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">insert entry into the buffer</FONT></LI></UL></LI></UL></LI></UL><P><FONT face="arial,helvetica,sans-serif"><STRONG>Checking existence</STRONG> and <STRONG>reading data</STRONG> based on<STRONG> buffer keys</STRONG>, which can be <STRONG>full</STRONG> or<STRONG> partial</STRONG> and depending on whether we are reading a <STRONG>root </STRONG>or<STRONG> child entity</STRONG>, and whether the access is <STRONG>direct</STRONG> or <STRONG>via association</STRONG>. In the case of a <STRONG>root entity</STRONG>, it is always a <STRONG>full key</STRONG>. Even when we try to access the <STRONG>parent node</STRONG> (especially the root node) from a child entity, we use "<STRONG>to-parent</STRONG>" <STRONG>association</STRONG> and provide the <STRONG>full key</STRONG>. I will explain buffer keys in separate chapter.</FONT></P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Child preparation</STRONG></U></FONT></P><P>Let's check code inside the preparation method for child entity. This is where the flag FULL_KEY comes into play.</P><pre class="lia-code-sample language-abap"><code> METHOD prep_second_node_buffer.
DATA lt_full_keys_2_read LIKE keys.
DATA lt_part_keys_2_read LIKE keys.
DATA lt_full_entity_result TYPE STANDARD TABLE OF zr_pru_2nd_node WITH EMPTY KEY.
DATA lt_part_entity_result TYPE STANDARD TABLE OF zr_pru_2nd_node WITH EMPTY KEY.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key_child>).
IF <ls_key_child>-full_key = abap_true.
IF line_exists( zpru_cl_example_buffer=>st_second_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey ] ).
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_full_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_full_key_2_read>).
<ls_full_key_2_read>-firstkey = <ls_key_child>-firstkey.
<ls_full_key_2_read>-secondkey = <ls_key_child>-secondkey.
ENDIF.
ELSE.
IF line_exists( zpru_cl_example_buffer=>st_first_node[ instance-firstkey = <ls_key_child>-firstkey ] )
AND VALUE #( zpru_cl_example_buffer=>st_first_node[ instance-firstkey = <ls_key_child>-firstkey ]-deleted OPTIONAL ) IS NOT INITIAL.
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_part_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_part_key_2_read>).
<ls_part_key_2_read>-firstkey = <ls_key_child>-firstkey.
ENDIF.
ENDIF.
ENDLOOP.
IF lt_full_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_2nd_node
FOR ALL ENTRIES IN _full_keys_2_read
WHERE firstkey = lt_full_keys_2_read-firstkey
AND secondkey = lt_full_keys_2_read-secondkey
INTO TABLE lt_full_entity_result.
IF sy-subrc = 0.
LOOP AT lt_full_entity_result ASSIGNING FIELD-SYMBOL(<ls_result>).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_second_node ASSIGNING FIELD-SYMBOL(<ls_buffer>).
<ls_buffer>-instance = <ls_result>.
ENDLOOP.
ENDIF.
ENDIF.
IF lt_part_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_2nd_node
FOR ALL ENTRIES IN lt_part_keys_2_read
WHERE firstkey = lt_part_keys_2_read-firstkey
INTO TABLE lt_part_entity_result.
IF sy-subrc = 0.
LOOP AT lt_part_entity_result ASSIGNING <ls_result>.
IF NOT line_exists( zpru_cl_example_buffer=>st_second_node[ instance-firstkey = <ls_result>-firstkey instance-secondkey = <ls_result>-secondkey ] ).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_second_node ASSIGNING <ls_buffer>.
<ls_buffer>-instance = <ls_result>.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.
ENDMETHOD.</code></pre><P><FONT face="arial,helvetica,sans-serif">Let's explore logic.</FONT></P><UL class="lia-list-style-type-circle"><LI><FONT face="arial,helvetica,sans-serif">check if the key is a full key:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">if it is a full key: </FONT><UL><LI><FONT face="arial,helvetica,sans-serif">check if the entry exists in the buffer using the full key:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">if yes:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">do nothing </FONT></LI></UL></LI><LI><FONT face="arial,helvetica,sans-serif">if no:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">collect the key to select data from the data source</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">select data using the full key</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">insert the child entry into the buffer</FONT></LI></UL></LI></UL></LI></UL></LI><LI><FONT face="arial,helvetica,sans-serif">if it is a partial key:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">check if the parent node is marked for deletion in the buffer </FONT><UL><LI><FONT face="arial,helvetica,sans-serif">if yes:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">do nothing</FONT></LI></UL></LI><LI><FONT face="arial,helvetica,sans-serif">if no:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">collect the key to select data from the data source</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">select one or multiple child entities using the partial key</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">loop through the selection result:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">check if each processing child entity already exists in the buffer</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">if it does not exist: add it into the buffer table</FONT></LI></UL></LI></UL></LI></UL></LI></UL></LI></UL></LI></UL></LI></UL><P><FONT face="arial,helvetica,sans-serif">You may ask why we check if the <STRONG>parent node</STRONG>, marked for <STRONG>deletion</STRONG>, exists in the buffer in the <STRONG>partial key logic</STRONG>. The answer is that when we mark for deletion parent node, we must <STRONG>mark for deletion all its children (cascading deletion).</STRONG></FONT><BR /><FONT face="arial,helvetica,sans-serif">This is the only scenario where we can be certain that <STRONG>all children are already present</STRONG> in the buffer. In other cases, we can't guarantee that all children are buffered; for example only four out of five children might currently be loaded.</FONT></P><P><FONT face="arial,helvetica,sans-serif">You may also ask why we check if a child entry exists before inserting it into the buffer table in the partial key logic. Let me give you an example. We have one parent node and four children:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif">parent1</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">child1</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">child2</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">child3</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">child4</FONT></LI></UL></LI></UL><P><FONT face="arial,helvetica,sans-serif">Firstly, you do operation "read child1"; as a result you have child1 inside the buffer table. Then you perform a "<STRONG>read by associations</STRONG>" for the children of parent1.</FONT><BR /><FONT face="arial,helvetica,sans-serif">During <STRONG>child preparation method</STRONG> you will have only the parent key (the partial key logic) and you will select all children of parent1 (child1, child2, child3 and child4). So the next step is to insert all of these children into <STRONG>the buffer table</STRONG>, but child1 is already presented. To <STRONG>avoid duplicates</STRONG> in the buffer table, we <STRONG>check for the existence</STRONG> of the child entity before inserting. </FONT></P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Full Buffer key vs Partial Buffer key.</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">During <STRONG>checking existence in the buffer</STRONG> or <STRONG>reading data from data source</STRONG> we use either the full key or the partial key of the entity:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif"><SPAN><STRONG>The partial key logic</STRONG>: used when we access the entity <STRONG>via composition</STRONG>. The partial key logic take place when we <STRONG>move from parent to child</STRONG>.</SPAN></FONT></LI><LI><FONT face="arial,helvetica,sans-serif"><SPAN><STRONG>The full key logic</STRONG>: used when we access the entity <STRONG>directly</STRONG> or by association inversed direction <STRONG>from child to parent</STRONG>.</SPAN></FONT></LI></UL><P><FONT face="arial,helvetica,sans-serif">While maintaining the buffer is primarily required for compositions, it may also be neccessary in cross-BO scenario where external associations are involved.</FONT></P><P><FONT face="arial,helvetica,sans-serif">Impact of key field data type on buffer handling:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif"><STRONG>Semantic key (e.g. character, numeric etc. fields)</STRONG>: you must handle entire set of the key fields for each level and the set may grow when you are moving dipper in RAP business object tree.</FONT></LI><LI><FONT face="arial,helvetica,sans-serif"><STRONG>GUID keys(raw16):</STRONG> you only need to process the only technical GUID field for reading, checking entity in the buffer tables.</FONT></LI></UL><P><FONT face="arial,helvetica,sans-serif"><STRONG>The full key representation.</STRONG></FONT><BR /><FONT face="arial,helvetica,sans-serif">This represents the <STRONG>complete primary key</STRONG> required to identify a specific instance.</FONT></P><TABLE border="1" width="100%"><TBODY><TR><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif"><STRONG>Level</STRONG></FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif"><STRONG>Semantic keys</STRONG></FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif"><STRONG>GUID keys</STRONG></FONT></TD></TR><TR><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">first node</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">FirstKey</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">FirstGuid</FONT></TD></TR><TR><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">second node</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">FirstKey, SecondKey</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">SecondGuid</FONT></TD></TR><TR><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">third node</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">FirstKey, SecondKey, ThirdKey</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">ThirdGuid</FONT></TD></TR><TR><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">fourth node</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">FirstKey, SecondKey, ThirdKey, FourthKey</FONT></TD><TD width="33.333333333333336%"><FONT face="arial,helvetica,sans-serif">FourthGuid</FONT></TD></TR></TBODY></TABLE><P><FONT face="arial,helvetica,sans-serif"><STRONG>The partial key representation.</STRONG></FONT><BR /><FONT face="arial,helvetica,sans-serif">This represents the <STRONG>keys of the imediate parent</STRONG>, used to fetch all associated children. </FONT></P><TABLE border="1" width="100%"><TBODY><TR><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif"><STRONG>Level</STRONG></FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif"><STRONG>Semantic keys</STRONG></FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif"><STRONG>GUID keys</STRONG></FONT></TD></TR><TR><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">first node</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">N/A</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif"> N/A</FONT></TD></TR><TR><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">second node</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">FirstKey</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">FirstGuid</FONT></TD></TR><TR><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">third node</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">FirstKey, SecondKey</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">SecondGuid</FONT></TD></TR><TR><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">fourth node</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">FirstKey, SecondKey, ThirdKey</FONT></TD><TD width="33.333333333333336%" height="30px"><FONT face="arial,helvetica,sans-serif">ThirdGuid</FONT></TD></TR></TBODY></TABLE><P><FONT face="arial,helvetica,sans-serif">Schematically it can be expressed in the following way:</FONT></P><pre class="lia-code-sample language-abap"><code>FULL KEY
NOT GUID
node / key field
1 / 1
2 / 1, 2
3 / 1, 2, 3
4 / 1, 2, 3, 4
GUID
node / key field
1 / 1
2 / 2
3 / 3
4 / 4
PARENT KEY
NOT GUID
node / key field
1 / N/A
2 / 1
3 / 1, 2
4 / 1, 2, 3
GUID
node / key field
1 / N/A
2 / 1
3 / 2
4 / 3 </code></pre><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Complex Data source</STRONG>.</U></FONT></P><P><FONT face="arial,helvetica,sans-serif">In a <STRONG>managed scenario</STRONG>, data source is defined as a <STRONG>persisted table</STRONG> in the BDEF. In an <STRONG>unmanaged scenario</STRONG>, the data source has a <STRONG>complex structure</STRONG> from which you load entries into the buffer and save entries from the buffer. 'Complex' means it can not be reduced to a single database table. It might be a <STRONG>BAPI</STRONG>, an <STRONG>HTTP response</STRONG>, a <STRONG>legacy API class</STRONG> or a <STRONG>combination</STRONG> of them.</FONT><BR /><FONT face="arial,helvetica,sans-serif">This complexity requires choosing an <STRONG>unmanaged implementation</STRONG>. You will also notice that you can not use keyword 'persistent table' in BDEF for <STRONG>unmanaged implementation type</STRONG>. And that is fair; your data source is not a just database table.</FONT></P><pre class="lia-code-sample language-abap"><code>METHOD prep_first_node_buffer.
*****
*****
IF lt_keys_2_read IS NOT INITIAL.
" SELECT * FROM zr_pru_1st_node
" CALL EXTERNAL SOURCE VIA HTTP
" CALL BAPI
" CALL LEGACY API
" UPLOAD FILE
" COMBINATION OF THEM AND ETC.
IF sy-subrc = 0.
LOOP AT lt_entity_result ASSIGNING FIELD-SYMBOL(<ls_result>).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_first_node ASSIGNING FIELD-SYMBOL(<ls_buffer>).
<ls_buffer>-instance = <ls_result>.
ENDLOOP.
ENDIF.
ENDIF.
ENDMETHOD.</code></pre><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Buffer Components</STRONG></U></FONT></P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="3"><U><STRONG>Buffer flags</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">Each row of the <STRONG>entity buffer table</STRONG> may contain any flags you require, but the minimum necessary are<STRONG> 'changed'</STRONG> and <STRONG>'deleted'</STRONG> flags. When you initialize a buffer entry, <STRONG>both flags are false</STRONG>. When you update an entry, you setup the changed flag to true. When you delete an entry, you setup the delete flag to true. Optionally, during deletion, you may set both flags as true, but I don't prefer such way. Finally, in the method SAVE you will sort all buffer entries into two separate internal local tables: one for <STRONG>modifying database</STRONG>, another for <STRONG>deleting from the database</STRONG>.</FONT></P><pre class="lia-code-sample language-abap"><code>CLASS zpru_cl_example_buffer DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
*****
*****
TYPES: BEGIN OF ts_first_node,
instance TYPE zr_pru_1st_node,
*****
*****
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_first_node.
TYPES: BEGIN OF ts_second_node,
instance TYPE zr_pru_2nd_node,
*****
*****
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_second_node.
*****
*****
ENDCLASS.</code></pre><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="3"><U><STRONG>Buffer additional components</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">The <STRONG>entity buffer table</STRONG> row may contain components as <STRONG>CID</STRONG> for handling field <STRONG>%CID</STRONG>, and <STRONG>PID</STRONG> for field <STRONG>%PID</STRONG> field in late numbering scenarios. Since I am using a <STRONG>late nubmering scenario</STRONG>, I have added <STRONG>component FINAL</STRONG><SPAN><STRONG>_KEY</STRONG> to preserve the result from ADJUST_NUMBERS method.</SPAN></FONT></P><pre class="lia-code-sample language-abap"><code>CLASS zpru_cl_example_buffer DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
*****
*****
TYPES: BEGIN OF ts_first_node,
instance TYPE zr_pru_1st_node,
cid TYPE abp_behv_cid,
pid TYPE abp_behv_pid,
final_key TYPE ts_first_node_db_keys,
*****
*****
END OF ts_first_node.
TYPES: BEGIN OF ts_second_node,
instance TYPE zr_pru_2nd_node,
cid TYPE abp_behv_cid,
pid TYPE abp_behv_pid,
pidparent TYPE abp_behv_pid,
final_key TYPE ts_second_node_db_keys,
*****
*****
END OF ts_second_node.
*****
*****
ENDCLASS.</code></pre><P> </P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="5"><U><STRONG>Generic pattern of usage.</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">Buffer handled in the following way:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif">In <STRONG>transactinal phase</STRONG> in method READ, CREATE, UPDATE, DELETE, RBA AND CBA:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">Prepare buffer using input keys using methods ZPRU_CL_EXAMPLE_BUFFER=>PREP_FIRST_NODE_BUFFER</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">read, insert, update, delete(set deleted flag) entry from, in entity buffer table</FONT></LI></UL></LI><LI><FONT face="arial,helvetica,sans-serif">In <STRONG>save phase</STRONG>:</FONT><UL><LI><FONT face="arial,helvetica,sans-serif">Determine values for entries from entity buffer table in method FINALIZE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">Validate values for entries from entity buffer table in method CHECK_BEFORE_SAVE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">Save entries from entity buffer tables to complex data source in method SAVE</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">Clean-up buffer tables in method CLEANUP</FONT></LI></UL></LI></UL><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Buffer access</STRONG>.</U></FONT></P><P>From handling perspective <STRONG>transactinal buffer</STRONG> can be accesed <STRONG>directly</STRONG> and <STRONG>indirectly</STRONG> in your unmanaged implementation during different phase.</P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="3"><U><STRONG>Buffer access in Transactional phase.</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">Basically during<STRONG> transactional phase</STRONG> we can access buffer in two flawors in ABP class:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif"><STRONG>Direct access</STRONG> is used in such methods like read, read by association, create by association, create, update, delete(<STRONG>standard operations</STRONG>).</FONT></LI><LI><FONT face="arial,helvetica,sans-serif"><STRONG>Indirect access</STRONG> is used in such methods for instance feature, instance authorization, actions, functions and etc. via <STRONG>EML</STRONG> read, modify(<STRONG>non-standard operations</STRONG>)</FONT></LI></UL><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino"><U><STRONG>Buffer access in Save phase</STRONG></U></FONT></P><P><FONT face="tahoma,arial,helvetica,sans-serif"><FONT face="arial,helvetica,sans-serif">During <STRONG>save phase</STRONG> we access data <STRONG>directly from buffer</STRONG>, the main reason is that methods FINALIZE, CHECK_BEFORE_SAVE, ADJUST_NUMBERS, SAVE, CLEANUP and CLEANUP_FINALIZE are not provided with input entity keys. The only source of eventual keys to process is the buffer tables itself. Also we can use EML to read data taking keys from buffer tables.</FONT></FONT></P><P><FONT face="arial,helvetica,sans-serif"><STRONG>Types of access</STRONG> in save phase:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif">predominantly direct access in methods FINALIZE, CHECK_BEFORE_SAVE, ADJUST_NUMBERS, SAVE, CLEANUP and CLEANUP_FINALIZE.</FONT></LI><LI><FONT face="arial,helvetica,sans-serif">indirect access via EML read.</FONT></LI></UL><pre class="lia-code-sample language-abap"><code> " No input keys
" To find out what to process we must have a look in the buffer.
" Finalize execute determinations, be aware that determiantion for draft
" and active instances are different things. Finalize is mainly
" about active data
methods FINALIZE
changing
!FAILED type DATA
!REPORTED type DATA .
" No input keys
" This method for validation and again mainly for active data.
methods CHECK_BEFORE_SAVE
changing
!FAILED type DATA
!REPORTED type DATA .
" input keys
" This method will save active data to source of active data
" Usually the data source is complex, not just one table or even two tables
methods SAVE
changing
!REPORTED type DATA optional
!FAILED type DATA optional .</code></pre><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Transactional buffer vs Draft buffer</STRONG>.</U></FONT></P><P><FONT face="arial,helvetica,sans-serif">If you business object has draft capabilities, don't conflate <STRONG>transactional buffer</STRONG> implemented by developer and <STRONG>draft buffer</STRONG> handled by managed runtime. They live <STRONG>in parallel</STRONG> and sometimes need to be synchronized via additional implementation for EDIT, ACTIVATE, DISCARD, RESUME. All in all draft processing is carried on by managed runtime despite that you have unmanaged implementation of business object.</FONT></P><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Operation aggregation.</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">Transactional buffer must handle <STRONG>operation aggregation</STRONG>. Operation aggregation means that user can create, update and update one more time, execute action <STRONG>in the same LUW,</STRONG> more over even <STRONG>in one EML statement</STRONG>. One more prominant example of operation aggregation is when user create, delete and then create again entry for RAP business object.</FONT></P><pre class="lia-code-sample language-abap"><code>*All CRUD operations must support aggreggation of operations.
*Just for your undestanding there are a lot possible sequences like:
*1. C 2. U 3. D 4. C + U 5. C + D 6. D + C 7. C + U + D 8. U + D + C and etc.
*The following level of complexity is that operations can be repeate multiple *times for the same instance(set of key fields)
*C + C + U + U + U + D and etc.
*There are more caviats that you can think, eg.
*C1 + D + C2 => create with some particalar fields, then mark it as deleted
*and it is still in our buffer, then create again with the same key but
*another set of data fields
</code></pre><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="4"><U><STRONG>Delete vs Discard vs Remove entry from buffer.</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">So let's define couple moments:</FONT></P><UL><LI><FONT face="arial,helvetica,sans-serif"><STRONG>Delete</STRONG> means to set flag <STRONG>'deleted'</STRONG> in method DELETE of ABP pool class. Real deletion happens in method SAVE, based on this flag.</FONT></LI><LI><FONT face="arial,helvetica,sans-serif"><STRONG>Discard</STRONG> - basically the same as delete but it is handled by <STRONG>draft runtime</STRONG> and is <STRONG>provided out-of-box</STRONG>. Developer isn't responsible for discard implementation. We only can add <STRONG>additional implementation</STRONG> to provide additional logic for the method DISCARD.</FONT></LI><LI><FONT face="arial,helvetica,sans-serif"><STRONG>Remove</STRONG> - sometimes can happend, e.g. in the method CLEANUP or in the method CREATE during attempt to recreate entry with the same keys as previously marked as deleted(<STRONG>delete then create again</STRONG>).</FONT></LI></UL><P class="lia-align-center" style="text-align: center;"><FONT face="georgia,palatino" size="5"><U><STRONG>Unmanaged Transactional Buffer Blueprint</STRONG></U></FONT></P><P><FONT face="arial,helvetica,sans-serif">At last, we can conclude our research with the publication of the code of the buffer class, the so called <STRONG>unmanaged trasnactional buffer blueprint</STRONG>. You are welcom to utilize and adapt this code for your specific requirements.</FONT></P><pre class="lia-code-sample language-abap"><code>CLASS zpru_cl_example_buffer DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ts_first_node_db_keys,
firstkey TYPE zr_pru_1st_node-firstkey,
END OF ts_first_node_db_keys.
TYPES: BEGIN OF ts_second_node_db_keys,
firstkey TYPE zr_pru_2nd_node-firstkey,
secondkey TYPE zr_pru_2nd_node-secondkey,
END OF ts_second_node_db_keys.
TYPES: BEGIN OF ts_third_node_db_keys,
firstkey TYPE zr_pru_3rd_node-firstkey,
secondkey TYPE zr_pru_3rd_node-secondkey,
thirdkey TYPE zr_pru_3rd_node-thirdkey,
END OF ts_third_node_db_keys.
TYPES: BEGIN OF ts_fourth_node_db_keys,
firstkey TYPE zr_pru_4th_node-firstkey,
secondkey TYPE zr_pru_4th_node-secondkey,
thirdkey TYPE zr_pru_4th_node-thirdkey,
fourthkey TYPE zr_pru_4th_node-fourthkey,
END OF ts_fourth_node_db_keys.
TYPES: BEGIN OF ts_first_node,
instance TYPE zr_pru_1st_node,
cid TYPE abp_behv_cid,
pid TYPE abp_behv_pid,
final_key TYPE ts_first_node_db_keys,
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_first_node.
TYPES: BEGIN OF ts_second_node,
instance TYPE zr_pru_2nd_node,
cid TYPE abp_behv_cid,
pid TYPE abp_behv_pid,
pidparent TYPE abp_behv_pid,
final_key TYPE ts_second_node_db_keys,
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_second_node.
TYPES: BEGIN OF ts_third_node,
instance TYPE zr_pru_3rd_node,
cid TYPE abp_behv_cid,
pid TYPE abp_behv_pid,
pidparent TYPE abp_behv_pid,
final_key TYPE ts_third_node_db_keys,
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_third_node.
TYPES: BEGIN OF ts_fourth_node,
instance TYPE zr_pru_4th_node,
pid TYPE abp_behv_pid,
pidparent TYPE abp_behv_pid,
final_key TYPE ts_fourth_node_db_keys,
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF ts_fourth_node.
TYPES tt_first_node TYPE TABLE OF ts_first_node WITH EMPTY KEY.
TYPES tt_second_node TYPE TABLE OF ts_second_node WITH EMPTY KEY.
TYPES tt_third_node TYPE TABLE OF ts_third_node WITH EMPTY KEY.
TYPES tt_fourth_node TYPE TABLE OF ts_fourth_node WITH EMPTY KEY.
CLASS-DATA st_first_node TYPE tt_first_node.
CLASS-DATA st_second_node TYPE tt_second_node.
CLASS-DATA st_third_node TYPE tt_third_node.
CLASS-DATA st_fourth_node TYPE tt_fourth_node.
TYPES: BEGIN OF ts_first_node_keys.
INCLUDE TYPE ts_first_node_db_keys.
TYPES: END OF ts_first_node_keys.
TYPES: BEGIN OF ts_second_node_keys.
INCLUDE TYPE ts_second_node_db_keys.
TYPES: full_key TYPE abap_bool,
END OF ts_second_node_keys.
TYPES: BEGIN OF ts_third_node_keys.
INCLUDE TYPE ts_third_node_db_keys.
TYPES: full_key TYPE abap_bool,
END OF ts_third_node_keys.
TYPES: BEGIN OF ts_fourth_node_keys.
INCLUDE TYPE ts_fourth_node_db_keys.
TYPES: full_key TYPE abap_bool,
END OF ts_fourth_node_keys.
TYPES tt_first_node_keys TYPE TABLE OF ts_first_node_keys WITH EMPTY KEY.
TYPES tt_second_node_keys TYPE TABLE OF ts_second_node_keys WITH EMPTY KEY.
TYPES tt_third_node_keys TYPE TABLE OF ts_third_node_keys WITH EMPTY KEY.
TYPES tt_fourth_node_keys TYPE TABLE OF ts_fourth_node_keys WITH EMPTY KEY.
CLASS-METHODS prep_first_node_buffer
IMPORTING !keys TYPE tt_first_node_keys.
CLASS-METHODS prep_second_node_buffer
IMPORTING !keys TYPE tt_second_node_keys.
CLASS-METHODS prep_third_node_buffer
IMPORTING !keys TYPE tt_third_node_keys.
CLASS-METHODS prep_fourth_node_buffer
IMPORTING !keys TYPE tt_fourth_node_keys.
ENDCLASS.
CLASS zpru_cl_example_buffer IMPLEMENTATION.
METHOD prep_first_node_buffer.
DATA lt_keys_2_read LIKE keys.
DATA lt_entity_result TYPE STANDARD TABLE OF zr_pru_1st_node WITH EMPTY KEY.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key>).
IF line_exists( zpru_cl_example_buffer=>st_first_node[ instance-firstkey = <ls_key>-firstkey ] ).
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_key_2_read>).
<ls_key_2_read>-firstkey = <ls_key>-firstkey.
ENDIF.
ENDLOOP.
IF lt_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_1st_node
FOR ALL ENTRIES IN lt_keys_2_read
WHERE firstkey = lt_keys_2_read-firstkey
INTO TABLE lt_entity_result.
IF sy-subrc = 0.
LOOP AT lt_entity_result ASSIGNING FIELD-SYMBOL(<ls_result>).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_first_node ASSIGNING FIELD-SYMBOL(<ls_buffer>).
<ls_buffer>-instance = <ls_result>.
ENDLOOP.
ENDIF.
ENDIF.
ENDMETHOD.
METHOD prep_second_node_buffer.
DATA lt_full_keys_2_read LIKE keys.
DATA lt_part_keys_2_read LIKE keys.
DATA lt_full_entity_result TYPE STANDARD TABLE OF zr_pru_2nd_node WITH EMPTY KEY.
DATA lt_part_entity_result TYPE STANDARD TABLE OF zr_pru_2nd_node WITH EMPTY KEY.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key_child>).
IF <ls_key_child>-full_key = abap_true.
IF line_exists( zpru_cl_example_buffer=>st_second_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey ] ).
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_full_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_full_key_2_read>).
<ls_full_key_2_read>-firstkey = <ls_key_child>-firstkey.
<ls_full_key_2_read>-secondkey = <ls_key_child>-secondkey.
ENDIF.
ELSE.
IF line_exists( zpru_cl_example_buffer=>st_first_node[ instance-firstkey = <ls_key_child>-firstkey ] )
AND VALUE #( zpru_cl_example_buffer=>st_first_node[ instance-firstkey = <ls_key_child>-firstkey ]-deleted OPTIONAL ) IS NOT INITIAL.
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_part_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_part_key_2_read>).
<ls_part_key_2_read>-firstkey = <ls_key_child>-firstkey.
ENDIF.
ENDIF.
ENDLOOP.
IF lt_full_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_2nd_node
FOR ALL ENTRIES IN lt_full_keys_2_read
WHERE firstkey = lt_full_keys_2_read-firstkey
AND secondkey = lt_full_keys_2_read-secondkey
INTO TABLE lt_full_entity_result.
IF sy-subrc = 0.
LOOP AT lt_full_entity_result ASSIGNING FIELD-SYMBOL(<ls_result>).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_second_node ASSIGNING FIELD-SYMBOL(<ls_buffer>).
<ls_buffer>-instance = <ls_result>.
ENDLOOP.
ENDIF.
ENDIF.
IF lt_part_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_2nd_node
FOR ALL ENTRIES IN lt_part_keys_2_read
WHERE firstkey = lt_part_keys_2_read-firstkey
INTO TABLE lt_part_entity_result.
IF sy-subrc = 0.
LOOP AT lt_part_entity_result ASSIGNING <ls_result>.
IF NOT line_exists( zpru_cl_example_buffer=>st_second_node[ instance-firstkey = <ls_result>-firstkey
instance-secondkey = <ls_result>-secondkey ] ).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_second_node ASSIGNING <ls_buffer>.
<ls_buffer>-instance = <ls_result>.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.
ENDMETHOD.
METHOD prep_third_node_buffer.
DATA lt_full_keys_2_read LIKE keys.
DATA lt_part_keys_2_read LIKE keys.
DATA lt_full_entity_result TYPE STANDARD TABLE OF zr_pru_3rd_node WITH EMPTY KEY.
DATA lt_part_entity_result TYPE STANDARD TABLE OF zr_pru_3rd_node WITH EMPTY KEY.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key_child>).
IF <ls_key_child>-full_key = abap_true.
IF line_exists( zpru_cl_example_buffer=>st_third_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey
instance-thirdkey = <ls_key_child>-thirdkey ] ).
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_full_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_full_key_2_read>).
<ls_full_key_2_read>-firstkey = <ls_key_child>-firstkey.
<ls_full_key_2_read>-secondkey = <ls_key_child>-secondkey.
<ls_full_key_2_read>-thirdkey = <ls_key_child>-thirdkey.
ENDIF.
ELSE.
IF line_exists( zpru_cl_example_buffer=>st_second_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey ] )
AND VALUE #( zpru_cl_example_buffer=>st_second_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey ]-deleted OPTIONAL ) IS NOT INITIAL.
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_part_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_part_key_2_read>).
<ls_part_key_2_read>-firstkey = <ls_key_child>-firstkey.
ENDIF.
ENDIF.
ENDLOOP.
IF lt_full_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_3rd_node
FOR ALL ENTRIES IN _full_keys_2_read
WHERE firstkey = lt_full_keys_2_read-firstkey
AND secondkey = lt_full_keys_2_read-secondkey
AND thirdkey = lt_full_keys_2_read-thirdkey
INTO TABLE lt_full_entity_result.
IF sy-subrc = 0.
LOOP AT lt_full_entity_result ASSIGNING FIELD-SYMBOL(<ls_result>).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_third_node ASSIGNING FIELD-SYMBOL(<ls_buffer>).
<ls_buffer>-instance = <ls_result>.
ENDLOOP.
ENDIF.
ENDIF.
IF lt_part_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_3rd_node
FOR ALL ENTRIES IN lt_part_keys_2_read
WHERE firstkey = lt_part_keys_2_read-firstkey
AND secondkey = lt_part_keys_2_read-secondkey
INTO TABLE lt_part_entity_result.
IF sy-subrc = 0.
LOOP AT lt_part_entity_result ASSIGNING <ls_result>.
IF NOT line_exists( zpru_cl_example_buffer=>st_third_node[ instance-firstkey = <ls_result>-firstkey
instance-secondkey = <ls_result>-secondkey
instance-thirdkey = <ls_result>-thirdkey ] ).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_third_node ASSIGNING <ls_buffer>.
<ls_buffer>-instance = <ls_result>.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.
ENDMETHOD.
METHOD prep_fourth_node_buffer.
DATA lt_full_keys_2_read LIKE keys.
DATA lt_part_keys_2_read LIKE keys.
DATA lt_full_entity_result TYPE STANDARD TABLE OF zr_pru_4th_node WITH EMPTY KEY.
DATA lt_part_entity_result TYPE STANDARD TABLE OF zr_pru_4th_node WITH EMPTY KEY.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key_child>).
IF <ls_key_child>-full_key = abap_true.
IF line_exists( zpru_cl_example_buffer=>st_fourth_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey
instance-thirdkey = <ls_key_child>-thirdkey
instance-fourthkey = <ls_key_child>-fourthkey ] ).
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_full_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_full_key_2_read>).
<ls_full_key_2_read>-firstkey = <ls_key_child>-firstkey.
<ls_full_key_2_read>-secondkey = <ls_key_child>-secondkey.
<ls_full_key_2_read>-thirdkey = <ls_key_child>-thirdkey.
<ls_full_key_2_read>-fourthkey = <ls_key_child>-fourthkey.
ENDIF.
ELSE.
IF line_exists( zpru_cl_example_buffer=>st_third_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey
instance-thirdkey = <ls_key_child>-thirdkey ] )
AND VALUE #( zpru_cl_example_buffer=>st_third_node[ instance-firstkey = <ls_key_child>-firstkey
instance-secondkey = <ls_key_child>-secondkey
instance-thirdkey = <ls_key_child>-thirdkey ]-deleted OPTIONAL ) IS NOT INITIAL.
CONTINUE.
ELSE.
APPEND INITIAL LINE TO lt_part_keys_2_read ASSIGNING FIELD-SYMBOL(<ls_part_key_2_read>).
<ls_part_key_2_read>-firstkey = <ls_key_child>-firstkey.
ENDIF.
ENDIF.
ENDLOOP.
IF lt_full_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_4th_node
FOR ALL ENTRIES IN lt_full_keys_2_read
WHERE firstkey = lt_full_keys_2_read-firstkey
AND secondkey = lt_full_keys_2_read-secondkey
AND thirdkey = lt_full_keys_2_read-thirdkey
AND fourthkey = lt_full_keys_2_read-fourthkey
INTO TABLE lt_full_entity_result.
IF sy-subrc = 0.
LOOP AT lt_full_entity_result ASSIGNING FIELD-SYMBOL(<ls_result>).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_fourth_node ASSIGNING FIELD-SYMBOL(<ls_buffer>).
<ls_buffer>-instance = <ls_result>.
ENDLOOP.
ENDIF.
ENDIF.
IF lt_part_keys_2_read IS NOT INITIAL.
SELECT * FROM zr_pru_4th_node
FOR ALL ENTRIES IN lt_part_keys_2_read
WHERE firstkey = lt_part_keys_2_read-firstkey
AND secondkey = lt_part_keys_2_read-secondkey
AND thirdkey = lt_part_keys_2_read-thirdkey
INTO TABLE lt_part_entity_result.
IF sy-subrc = 0.
LOOP AT lt_part_entity_result ASSIGNING <ls_result>.
IF NOT line_exists( zpru_cl_example_buffer=>st_fourth_node[ instance-firstkey = <ls_result>-firstkey
instance-secondkey = <ls_result>-secondkey
instance-thirdkey = <ls_result>-thirdkey
instance-fourthkey = <ls_result>-fourthkey ] ).
APPEND INITIAL LINE TO zpru_cl_example_buffer=>st_fourth_node ASSIGNING <ls_buffer>.
<ls_buffer>-instance = <ls_result>.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.
ENDMETHOD.
ENDCLASS.</code></pre><P> </P><P> </P><P> </P>2026-01-19T12:23:34.134000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/clean-core-building-a-flexible-custom-business-configuration-for-http/ba-p/14310423Clean Core: Building a Flexible Custom Business Configuration for HTTP Endpoints in SAP S/4HANA2026-01-20T02:51:55.321000+01:00Louis-Arnaudhttps://community.sap.com/t5/user/viewprofilepage/user-id/15467<H2 id="toc-hId-1788541030"><STRONG>Introduction</STRONG></H2><DIV class="">This post presents a technique for creating configuration in SAP S/4HANA that fully respects clean‑core principles, while offering more flexibility than traditional ECC customizing (SM30 or SPRO).</DIV><DIV class="">The use case is particularly interesting because it also touches on best practices for connecting SAP S/4HANA to Integration Suite — or more generally, to any public API.<BR />However, the primary goal here is to explain <STRONG>how to build a Custom Business Configuration (CBC)</STRONG> — the clean‑core replacement for SPRO entries — and, more importantly, <STRONG>how to adapt it so that only selected fields are transport‑controlled while others remain editable directly in each environment</STRONG>.</DIV><DIV class="">The result is a clean‑core‑compliant solution that is <STRONG>more powerful than legacy SPRO customizing</STRONG>.</DIV><DIV class=""><DIV><H2 id="toc-hId-1592027525"><STRONG>Use Case</STRONG></H2><DIV class="">Connections from S/4HANA to external APIs are increasing significantly with SAP’s “API First” strategy. This is very true on my project: we call Integration Suite, which then calls public APIs while managing authentication and potential transformations.</DIV><DIV class="">On S/4HANA on‑premise or private cloud, the recommended practice is to use <STRONG>destinations</STRONG> (transaction <STRONG>SM59</STRONG>, type <STRONG>G</STRONG> for HTTP).<BR />In public cloud, this would instead be done using <STRONG>Communication Arrangements</STRONG>.</DIV><DIV class="">A typical ABAP call looks like:</DIV><DIV class=""> </DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>DATA(get_device_auth_destination) =
cl_http_destination_provider=>create_by_destination(
i_name = 'DESTINATION_NAME' ).
DATA(http_client) =
cl_web_http_client_manager=>create_by_http_destination(
i_destination = get_device_auth_destination ).
http_client->get_http_request( )->set_uri_path(
i_uri_path = '/path/endpoint' ).
DATA(http_response) =
http_client->execute( i_method = if_web_http_client=>get ).
DATA(response_text) = http_response->get_text( ).</code></pre><DIV class=""> <SPAN>In this simple example, both the destination and the endpoint path are hard‑coded.</SPAN></DIV><DIV class=""><DIV><H2 id="toc-hId-1395514020"><STRONG>The Problem</STRONG></H2><DIV class="">Good practice suggests that:</DIV><UL><LI>The API endpoint URL <STRONG>should not</STRONG> be stored in the program.</LI><LI>Ideally, there should be <STRONG>one dedicated destination per API</STRONG>, allowing:<UL><LI>API‑level call authorization</LI><LI>Authentication configuration (e.g., principal propagation)</LI><LI>Timeout settings</LI><LI>Emergency deactivation of an API by disabling the destination</LI></UL></LI></UL><DIV class="">In practice, because most APIs go through Integration Suite, <STRONG>we want to avoid creating dozens of destinations</STRONG>.<BR />We therefore use <STRONG>one common destination</STRONG> but still need the ability to override it when necessary.</DIV><DIV class="">To achieve this, we create a <STRONG>custom configuration</STRONG>, allowing us to define — per flow type:</DIV><UL><LI>HTTP destination</LI><LI>Endpoint path</LI><LI>English description</LI><LI>Active / inactive flag</LI></UL><DIV class="">Here is the table:</DIV><DIV class=""> </DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'HTTP endpoint configuration'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C
@AbapCatalog.dataMaintenance : #ALLOWED
define table zbct_a_httpendp {
key client : abap.clnt not null;
key flow_id : zhttp_endpoint_flow_id not null;
rfc_destination : rfcdest;
path : zhttp_endpoint_path;
is_active : zhttp_endpoint_is_active;
description : zhttp_endpoint_description;
}</code></pre><DIV class=""><DIV class="">The requirement is:</DIV><UL><LI>Creating a <STRONG>flow ID</STRONG> with its initial settings must be possible <STRONG>only in the reference client</STRONG>.</LI><LI>But fields like <STRONG>path</STRONG> and <STRONG>active flag</STRONG> must be editable <STRONG>in all environments</STRONG>.</LI></UL><DIV><H2 id="toc-hId-1199000515"><STRONG>Creating the Custom Business Configuration</STRONG></H2><DIV class="">Once the table is created, generating a CBC object is straightforward:<BR />open the ABAP Object Generator and select <STRONG>"Maintenance Object"</STRONG>.</DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_0-1768844270039.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363015i4E77411E6C08A0C3/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="LouisArnaud_0-1768844270039.png" alt="LouisArnaud_0-1768844270039.png" /></span></DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_1-1768844294418.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363016i6D33BD8FFFB88F4B/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="LouisArnaud_1-1768844294418.png" alt="LouisArnaud_1-1768844294418.png" /></span></DIV><DIV class=""><SPAN>This automatically generates </SPAN><STRONG>RAP objects (Restful ABAP Programming Model)</STRONG><SPAN>.</SPAN></DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_2-1768844308858.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363017iCCBC2E736F92C82C/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="LouisArnaud_2-1768844308858.png" alt="LouisArnaud_2-1768844308858.png" /></span></DIV><DIV class="">This is where it gets powerful: we can adjust these objects to control precisely <STRONG>which fields are editable depending on the environment</STRONG>.</DIV><DIV class=""><H2 id="toc-hId-1002487010"><STRONG>Adjusting the Behavior Definition</STRONG></H2><DIV class="">Open the generated behavior definition (in my case <CODE>ZBP_I_HTTPENDPOINTCONFIGU_S</CODE>).</DIV><DIV class="">Our goal is:</DIV><UL><LI>The <STRONG>Edit</STRONG> button must always be enabled.</LI><LI>Some fields must be <STRONG>read‑only</STRONG> outside the reference client:<UL><LI>RFC Destination</LI><LI>Description</LI></UL></LI></UL><DIV class="">We simply define this logic in the behavior definition.</DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_3-1768844354335.png" style="width: 472px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363018iC5BDEC6B0860B1D5/image-dimensions/472x323/is-moderation-mode/true?v=v2" width="472" height="323" role="button" title="LouisArnaud_3-1768844354335.png" alt="LouisArnaud_3-1768844354335.png" /></span></DIV><DIV class="">Next, open the implementation class (<CODE>ZBP_…</CODE>) and go to <STRONG>Local Types</STRONG>.</DIV><DIV class="">We remove the default "Edit" restrictions and implement our own logic to disable selected fields when the configuration is opened in a non‑reference environment.</DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_4-1768844372436.png" style="width: 693px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363019i951239E72AB7E133/image-dimensions/693x191/is-moderation-mode/true?v=v2" width="693" height="191" role="button" title="LouisArnaud_4-1768844372436.png" alt="LouisArnaud_4-1768844372436.png" /></span></DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_5-1768844380786.png" style="width: 693px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363020i4E64EEEA18BAE812/image-dimensions/693x70/is-moderation-mode/true?v=v2" width="693" height="70" role="button" title="LouisArnaud_5-1768844380786.png" alt="LouisArnaud_5-1768844380786.png" /></span></DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_6-1768844397652.png" style="width: 688px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363021iD4E87854F677AB04/image-dimensions/688x98/is-moderation-mode/true?v=v2" width="688" height="98" role="button" title="LouisArnaud_6-1768844397652.png" alt="LouisArnaud_6-1768844397652.png" /></span></DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_0-1770129151579.png" style="width: 694px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368607iA5EF49ED343E4679/image-dimensions/694x122?v=v2" width="694" height="122" role="button" title="LouisArnaud_0-1770129151579.png" alt="LouisArnaud_0-1770129151579.png" /></span><P> </P></DIV><DIV class="">This ensures:</DIV><UL><LI>In production (or any non‑reference client):<BR />→ only <EM>path</EM> and <EM>active flag</EM> are editable</LI><LI>In the reference client:<BR />→ everything is editable</LI></UL><DIV><H2 id="toc-hId-805973505"><STRONG>Testing the Result</STRONG></H2><DIV class="">Open the <STRONG>Custom Business Configuration</STRONG> app and select your configuration object.</DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_7-1768844452623.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363022i076C46227BC4F99F/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="LouisArnaud_7-1768844452623.png" alt="LouisArnaud_7-1768844452623.png" /></span></DIV><DIV class="">In a productive environment, you will see the expected restrictions: only the intended fields are editable.</DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_8-1768844466261.png" style="width: 543px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363023i0CC78D5AD531F10E/image-dimensions/543x256/is-moderation-mode/true?v=v2" width="543" height="256" role="button" title="LouisArnaud_8-1768844466261.png" alt="LouisArnaud_8-1768844466261.png" /></span></DIV><DIV class="">In the reference client, all fields remain modifiable.</DIV><DIV class=""> </DIV></DIV></DIV></DIV></DIV>2026-01-20T02:51:55.321000+01:00https://community.sap.com/t5/abap-blog-posts/step-by-step-guide-creating-your-first-rap-application-using-odata-v4-and/ba-p/14308388Step-By-Step Guide: Creating Your First RAP Application using OData V4 and Transporting to Quality2026-01-20T07:14:41.239000+01:00RajkumarGoudahttps://community.sap.com/t5/user/viewprofilepage/user-id/1768161<P><STRONG>Introduction</STRONG></P><P>In this blog, we will walk through the detailed steps to create your<SPAN> </SPAN><STRONG>first RAP (RESTful ABAP Programming Model) application and correctly move it to the Quality system. This blog also highlights mandatory steps before transport and common mistakes that beginners usually make, based on real project experience.</STRONG></P><P><STRONG>Target Audience & Prerequisites</STRONG></P><UL><LI>ABAP developers who are<SPAN> </SPAN><STRONG>new to RAP</STRONG></LI><LI>Basic knowledge of<SPAN> </SPAN><STRONG>CDS Views and ABAP Development Tools (ADT)</STRONG></LI><LI>System used:<SPAN> </SPAN><STRONG>SAP S/4HANA (On-Premise / Private Cloud)</STRONG></LI><LI>Exposure type:<SPAN> </SPAN><STRONG>OData V4</STRONG></LI><LI>Scenario covered:<SPAN> </SPAN><STRONG>Basic Read-only RAP application</STRONG><P><STRONG>Mandatory Objects in a RAP Application<SPAN>:</SPAN></STRONG></P><UL><LI>Package</LI><LI><SPAN>Interface CDS View</SPAN></LI><LI>Projection CDS View (Optional we can directly create service definition on Interface view also)</LI><LI><SPAN>Service Definition</SPAN></LI><LI><SPAN>Service Binding</SPAN></LI></UL></LI></UL><OL><LI><SPAN><STRONG>Create Package: </STRONG></SPAN><P>Create a package in ADT:</P><UL><LI>Right-click on project → New → ABAP Package</LI><LI>Enter package name and description</LI><LI>Do not create it as a local package ($TMP)</LI><LI>Capture it in a transport request </LI><LI><SPAN>Only transportable packages can be moved to Quality and Production systems.</SPAN></LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_0-1768540851654.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361795i80BEFC048D01BE3F/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_0-1768540851654.png" alt="RajkumarGouda_0-1768540851654.png" /></span><P> </P></LI></UL></LI><LI><P><STRONG>Interface CDS View:</STRONG> A CDS View is a virtual data model in ABAP that defines how data from database tables is accessed and represented. In RAP, it often serves as the “Interface View.”</P><OL class="lia-list-style-type-lower-roman"><LI><P><STRONG>Why It Matters:</STRONG></P><UL><LI>Reusable logic: Centralizes data access for transactional and analytical purposes.</LI><LI>Foundation: Serves as the starting point for business object modeling in RAP.</LI></UL></LI><LI><P><STRONG>Steps:</STRONG></P><UL><LI>Right-click your database table (e.g., ZTRAVEL_RAJ_M), select New Data Definition.</LI><LI>Provide a name, description, and use the Define View Entity template.</LI><LI>Assign to transport, activate, and check for errors.</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_1-1768540851889.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361797i21F33B8BD3337DAE/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_1-1768540851889.png" alt="RajkumarGouda_1-1768540851889.png" /></span><P> </P></LI></UL></LI></OL></LI><LI><P><STRONG>Service Definition:</STRONG><SPAN> </SPAN>The Service Definition specifies which data elements (entities) of a RAP business object are exposed as OData services.</P><OL class="lia-list-style-type-lower-roman"><LI><P><STRONG>Why It Matters:</STRONG></P><UL><LI>Exposure Layer: Decides what consumers (e.g., Fiori apps, external APIs) can interact with.</LI><LI>It’s the contract between your backend logic and external consumers.</LI></UL><STRONG> </STRONG></LI><LI><P><STRONG>Steps:</STRONG></P><UL><LI>Right-click on the projection view, select New Service Definition.</LI><LI>Name, description, assign transport, and activate.</LI><LI>This exposes your RAP objects as a service.</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_2-1768540851660.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361796iB05AE8FEED3D5387/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_2-1768540851660.png" alt="RajkumarGouda_2-1768540851660.png" /></span><P> </P></LI></UL></LI></OL></LI><LI><STRONG>Service Binding: </STRONG>Service Binding links a service definition to an actual communication protocol (such as OData V4) and activation state.<OL class="lia-list-style-type-lower-roman"><LI><STRONG>Why It Matters:</STRONG><UL><LI>Enables Consumption: Without binding, your service cannot be called or used.</LI><LI>Protocol Choice: Determines how front ends communicate with your service (e.g., OData V4 for Fiori/UI5 apps).</LI></UL></LI><LI><P><STRONG>Steps:</STRONG></P><UL><LI>Right-click on the service definition, select New Service Binding.</LI><LI>Choose OData V4 protocol (for most Fiori/RAP apps).</LI><LI>Assign transport, describe, finish, and activate.</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_3-1768540851892.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361799i93A775EDADFD75DB/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_3-1768540851892.png" alt="RajkumarGouda_3-1768540851892.png" /></span><P> </P></LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_4-1768540851678.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361798i2CDE5542D59CBFC4/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_4-1768540851678.png" alt="RajkumarGouda_4-1768540851678.png" /></span><P> </P></LI></UL></LI></OL></LI><LI><STRONG>Why You Should NOT Publish from ADT: </STRONG>Publishing directly from ADT is meant only for local testing.<OL class="lia-list-style-type-lower-roman"><LI><P><STRONG>Issues with ADT Publishing</STRONG></P><UL><LI>Service is registered as a local ($TMP) object</LI><LI>Cannot be transported to Quality or Production</LI><LI>Not supported for productive landscapes</LI></UL></LI><LI><P><STRONG>Correct Way: Publish via /IWFND/V4_ADMIN</STRONG></P><UL><LI><P>Transaction /IWFND/V4_ADMIN is the recommended way to publish RAP services.</P></LI></UL></LI><LI><STRONG>Steps: </STRONG><UL><LI>Open /IWFND/V4_ADMIN and click Publish Service Groups</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_5-1768540851905.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361800i3FDA30E4725FB5E9/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_5-1768540851905.png" alt="RajkumarGouda_5-1768540851905.png" /></span><P> </P></LI><LI>Enter System Alias = LOCAL, Service Binding name click on get service groups</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_6-1768540851979.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361801i4B52A6A134A3EB33/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_6-1768540851979.png" alt="RajkumarGouda_6-1768540851979.png" /></span><P> </P></LI><LI>Select service and Publish</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_7-1768540851966.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361802i92F33C5A6583AA89/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_7-1768540851966.png" alt="RajkumarGouda_7-1768540851966.png" /></span><P> </P></LI><LI>From the service group list, right click on your service group and click on transport publishing. It will prompt for customizing TR enter the transport and save. After that Click on transport routing assignment and capture in the customizing TR.</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_8-1768540852149.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361803i130B341E8A389C81/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_8-1768540852149.png" alt="RajkumarGouda_8-1768540852149.png" /></span><P> </P></LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_9-1768540852154.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361806i63A955753D40677D/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_9-1768540852154.png" alt="RajkumarGouda_9-1768540852154.png" /></span><P> </P></LI><LI>Service is now published</LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_10-1768540852425.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361804i00101429ECA3147F/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_10-1768540852425.png" alt="RajkumarGouda_10-1768540852425.png" /></span><P> </P></LI></UL></LI></OL></LI><LI><STRONG>Preview: </STRONG>Finally, let’s preview the application to verify that the service is working as expected. <UL><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RajkumarGouda_11-1768540852445.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361805i72DD1E4FFCF22D6F/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="RajkumarGouda_11-1768540852445.png" alt="RajkumarGouda_11-1768540852445.png" /></span><P> </P></LI></UL></LI></OL><P><STRONG>Conclusion: </STRONG></P><UL><LI>In this blog, we learned how to create a basic RAP application and the correct approach to publish and transport OData V4 services to the Quality system.</LI><LI>Using /IWFND/V4_ADMIN ensures transport safety and helps avoid common deployment issues that beginners often face.</LI></UL><P><STRONG>Note: </STRONG></P><P>To customize the user interface of your RAP application, add appropriate UI annotations in the CDS view (interface or projection, depending on your design). Common annotations include<SPAN> </SPAN><A href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379" target="_blank">@ui</A>.headerInfo,<SPAN> </SPAN><A href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379" target="_blank">@ui</A>.lineItem, and<SPAN> </SPAN><A href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379" target="_blank">@ui</A>.selectionField.</P><P>After publishing the service, share the service URL (available in the Service Binding) with the Fiori development team so they can create the required Fiori tile.<BR />After transporting to the Quality system, SICF activation is a manual step. Without activating the relevant SICF nodes, the service will not be accessible. Coordinate with your Basis team to complete this step.</P>2026-01-20T07:14:41.239000+01:00https://community.sap.com/t5/technology-blog-posts-by-sap/your-2026-roadmap-to-getting-started-with-abap-ai-and-abap-1/ba-p/14312060Your 2026 Roadmap to Getting Started with ABAP AI and ABAP-12026-01-21T18:01:36.956000+01:00JanMattheshttps://community.sap.com/t5/user/viewprofilepage/user-id/194386<P data-unlink="true"><A title="ABAP AI Capabilities" href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/joule-for-developers-abap-ai-capabilities-f14ebffef77b41bfb0746c33dcb70e84" target="_blank" rel="noopener noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="ABAP_AI.png" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363821iFBD41BCBF30A65A8/image-size/small/is-moderation-mode/true?v=v2&px=200" role="button" title="ABAP_AI.png" alt="ABAP_AI.png" /></span></A>The era of "Wait and See" for AI in ABAP development is officially over. With the general availability of <STRONG>Joule for Developers (J4D)</STRONG> and the <A href="https://www.linkedin.com/posts/sonjalienard_introducing-the-next-era-of-abap-development-activity-7391805653905993728-J9a-?utm_source=share&utm_medium=member_desktop&rcm=ACoAAAAwC8YBn3fP-40jJLXucycHYaj7szVogRw" target="_blank" rel="noopener nofollow noreferrer">planned availability of ABAP agentic AI frameworks in 2026</A>, the question isn't <EM>if</EM> you should use AI, but <EM>where</EM> you should start. </P><P data-unlink="true"><STRONG>The "Start Here" Recommendation: </STRONG>For 90% of developers, the best starting point is <STRONG><SPAN class=""><SPAN><A class="" href="https://www.sap.com/products/erp/s4hana-cloud-public-edition-joule-for-developers-abap-ai-capabilities.html" target="_blank" rel="noopener noreferrer">SAP Joule for Developers</A></SPAN></SPAN></STRONG> integrated within <STRONG>ABAP Development Tools (ADT) for Eclipse</STRONG>. In addition you can also experiment with the first versions of <A href="https://help.sap.com/docs/sap-ai-core/generative-ai/sap-abap-1?locale=en-US&version=LATEST" target="_blank" rel="noopener noreferrer">ABAP-1 model to <STRONG>analyze and explain code</STRONG></A> on your own - but be aware that this is <STRONG>currently not for code generation </STRONG>and only for those which want to be independent of J4D<STRONG>.</STRONG> Therefore starting with J4D is the fastest but also the most powerful way to start with ABAP AI.</P><DIV class=""> </DIV><DIV class=""><STRONG>Why should I start with Joule for Developers (J4D)?</STRONG></DIV><UL class=""><LI><SPAN class=""><STRONG>Contextual Intelligence:</STRONG> Unlike generic LLMs, Joule for Developers is additionally trained on over <STRONG>250 million lines of proprietary SAP code</STRONG> and is aware of your specific <STRONG>ABAP Cloud context.</STRONG> In general J2D is powered by a combination of third-party foundation models (e.g. OpenAI, Gemini, Anthropic, Mistral AI), alongside SAP-centric models like the LLMs, trained specifically on ABAP and purpose-built for cloud-based SAP development.</SPAN></LI><LI><SPAN class=""><STRONG>It’s Free (For Now):</STRONG> SAP has extended the free promotional period for Joule for Developers until <STRONG>September 2026</STRONG>.</SPAN></LI><LI><SPAN class=""><STRONG>Native Skills:</STRONG> It handles predictive code generation (e.g. RAP generation), "Explain" skills for legacy code, and unit test generation directly within your IDE. So it can do way more then ABAP-1 right now.</SPAN></LI><LI><SPAN class=""><STRONG>J4D </STRONG>is out of the box <STRONG>integrated in Eclipse ADT.</STRONG></SPAN></LI></UL><DIV class=""><STRONG>The Three Paths for ABAP AI Evolution:</STRONG></DIV><DIV class=""><STRONG>1. The Standard Path: Joule for Developers in ADT</STRONG></DIV><UL class=""><LI><SPAN class=""><STRONG>Best for:</STRONG> Daily coding, documentation, and unit testing.</SPAN></LI><LI><SPAN class=""><STRONG>Action:</STRONG> Request access through your administrator (Material 8019124 for customers and 8019541 for partners) and activate it in ADT.</SPAN></LI></UL><DIV class=""><STRONG>2. The Innovation Path: ABAP AI SDK & BTP Free Tier</STRONG></DIV><UL class=""><LI><SPAN class=""><STRONG>Best for:</STRONG> Building your own AI-powered business applications (e.g., automated invoice processing).</SPAN></LI><LI><SPAN class=""><STRONG>Action:</STRONG> Use the <STRONG>SAP BTP Free Tier</STRONG> to access the Generative AI Hub and the <STRONG>ABAP AI SDK</STRONG> to connect your custom ABAP code to external foundation models.</SPAN></LI></UL><DIV class=""><STRONG>3. The Modernization Path: VS Code with ABAP Cloud (Roadmap Q2/2026)</STRONG></DIV><UL class=""><LI><SPAN class=""><STRONG>Best for:</STRONG> Developers looking for a lightweight, file-based experience and for those which want to be the first to leverage agentic ABAP AI in side-by-side approach.</SPAN></LI><LI><STRONG>Action</STRONG>: Explore the new side-by-side ABAP Cloud Extension for <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/introducing-the-next-era-of-abap-development/ba-p/14260522" target="_blank">VS Code (GA planned for Q2 2026) with built-in Agentic AI</A> assistance powered by an <STRONG>ABAP MCP server</STRONG>. This will support a <STRONG>broader range of SAP S/4HANA Private Editions</STRONG> and provide <STRONG>faster update cycles</STRONG>.</LI></UL><DIV class=""><STRONG>Key Takeaway for 2026:</STRONG><BR />Don't get bogged down trying to "prompt engineer" experimental models like ABAP-1 for production code generation. Instead, leverage <STRONG>Joule for Developers</STRONG> mature "Explain" and "Generate" skills to maintain a <STRONG>Clean Core</STRONG> and accelerate your migration to SAP S/4HANA.</DIV><P><STRONG>Here you can learn more:</STRONG></P><OL><LI><P><SPAN class=""><A class="" href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-joule-for-developers-expands-to-private-cloud-accelerating-abap/ba-p/14237958" target="_blank">SAP Joule for Developers (J4D) Expands to Private Cloud: Accelerating ABAP Innovation and Transformation </A></SPAN></P></LI><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/abap-ai-revolution-accelerates-the-abap-developer-who-built-enterprise-apps/ba-p/14216073" target="_blank"><SPAN class="">ABAP AI Revolution Accelerates: The ABAP Developer Who Built Enterprise Apps in Minutes </SPAN></A></LI><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/introducing-the-next-era-of-abap-development/ba-p/14260522" target="_blank"><SPAN class="">Joule for Developers and ABAP AI capabilities are coming to SAP S/4HANA Private Edition 2021, 2022, and 2023</SPAN></A></LI><LI><SPAN class="">Public Roadmap: <A href="https://help.sap.com/docs/abap-cross-product/roadmap-info/genai" target="_blank" rel="noopener noreferrer">SAP Help</A> / <A href="https://roadmaps.sap.com/board?PRODUCT=73554900100800001562&PRODUCT=73555000100800001164&range=CURRENT-LAST" target="_blank" rel="noopener noreferrer">SAP Roadmap Explorer</A></SPAN></LI><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-joule-for-developers-abap-ai-capabilities-for-sap-s-4hana-cloud-private/ba-p/14236954" target="_blank"><SPAN class="">Step-by-step guide how to activate SAP Joule for Developers, ABAP AI capabilities for your SAP S/4HANA Cloud Private Edition</SPAN></A></LI><LI><SPAN class=""><SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/joule-for-developers-with-sap-s-4hana-public-cloud-edition-setup-guide/ba-p/14209989" target="_blank">Joule for Developers (J4D) for S/4HANA Public Cloud Setup Guide</A></SPAN></SPAN></LI><LI><A href="https://discovery-center.cloud.sap/search/abap%20ai" target="_blank" rel="noopener nofollow noreferrer"><SPAN class=""><SPAN>SAP ABAP AI Discovery Center</SPAN></SPAN></A></LI><LI><SPAN class=""><SPAN><A class="" href="https://discovery-center.cloud.sap/search/Free-tier" target="_blank" rel="noopener nofollow noreferrer">SAP BTP Discovery Center (Free Tier Services)</A></SPAN></SPAN></LI><LI><A href="http://www.youtube.com/playlist?list=PL6RpkC85SLQAt9lvPw0gF4E3nwbJD0EUe" target="_blank" rel="noopener nofollow noreferrer"><SPAN class=""><SPAN>YouTube playlist for Joule for Developers (J4D)</SPAN></SPAN></A></LI><LI><A href="https://help.sap.com/docs/abap-ai" target="_blank" rel="noopener noreferrer"><SPAN class=""><SPAN>Joule for Developers Help (J4D)</SPAN></SPAN></A></LI><LI><A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/joule-for-developers-abap-ai-capabilities-f14ebffef77b41bfb0746c33dcb70e84" target="_blank" rel="noopener noreferrer"><SPAN class=""><SPAN>ADT Eclipse AI Capabilities Help</SPAN></SPAN></A></LI><LI><SPAN class=""><SPAN><A class="" href="https://help.sap.com/docs/abap-ai/generative-ai-in-abap-cloud/set-up-abap-ai-sdk-powered-by-intelligent-scenario-lifecycle-management" target="_blank" rel="noopener noreferrer">ABAP AI SDK Help</A></SPAN></SPAN></LI><LI><A href="https://help.sap.com/docs/sap-ai-core/generative-ai/sap-abap-1?locale=en-US&version=LATEST" target="_blank" rel="noopener noreferrer"><SPAN class="">ABAP-1 Help</SPAN></A></LI></OL><P data-unlink="true"><SPAN class="">More high-level insights about <A href="https://www.sap.com/sea/products/artificial-intelligence/joule-for-developers.html" target="_blank" rel="noopener noreferrer">Joule for Developers/ABAP AI</A> and <A href="https://www.sap.com/products/technology-platform/abap/environment.html" target="_blank" rel="noopener noreferrer">ABAP platform can be found here</A> . Here you find <A href="https://www.sap.com/products/artificial-intelligence/sap-abap.html" target="_blank" rel="noopener noreferrer">more on ABAP- 1</A>,</SPAN></P>2026-01-21T18:01:36.956000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/mass-update-through-custom-action-in-rap/ba-p/14312466Mass Update Through Custom Action In RAP2026-01-23T06:31:27.262000+01:00harsha_reddy24https://community.sap.com/t5/user/viewprofilepage/user-id/2263071<P><STRONG>Introduction</STRONG>:</P><P>With the increasing adoption of the RESTful ABAP Programming Model (RAP), SAP developers are encouraged to move away from classic procedural approaches and design applications that are behavior-driven, scalable, and Fiori-ready. While RAP provides powerful standard operations such as create, update, and delete, real-world business scenarios often require <STRONG>mass processing or bulk updates</STRONG> triggered by user actions.</P><P>In this blog, I will walk through a <STRONG>practical implementation of mass update using a custom action in RAP</STRONG>. This approach is especially useful when you need to update multiple records based on business logic that cannot be handled by standard CRUD operations alone.</P><P><STRONG>Solution Overview:</STRONG></P><P>The blog covers:</P><UL><LI><P>Why custom actions are required for mass updates in RAP</P></LI><LI><P>How to define a custom action in the Behavior Definition</P></LI><LI><P>Implementing the action logic in the Behavior Implementation class</P></LI><LI><P>Triggering the mass update from a Fiori Elements UI</P></LI><LI><P>Key considerations and best practices</P></LI></UL><P><BR /><STRONG>Database Table:</STRONG></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'travel uuid table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zuuid_table {
key client : abap.clnt not null;
key travel_id : sysuuid_x16 not null;
description : /dmo/description;
status : /dmo/travel_status;
@Semantics.amount.currencyCode : 'zuuid_table.currency_code'
booking_fee : /dmo/booking_fee;
@Semantics.amount.currencyCode : 'zuuid_table.currency_code'
total_price : /dmo/total_price;
currency_code : /dmo/currency_code;
createdby : syuname;
createdat : timestampl;
lastchangedby : syuname;
lastchangedat : abp_locinst_lastchange_tstmpl;
}</code></pre><P><STRONG>Root Entity:<BR /></STRONG></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #MANDATORY
@Metadata.allowExtensions: true
@ObjectModel.sapObjectNodeType.name: 'ZUUID_TABLE'
@EndUserText.label: '###GENERATED Core Data Service Entity'
define root view entity ZR_UUID_TABLE
as select from zuuid_table as R_UUID_TABLE
{
key travel_id as TravelID,
description as Description,
status as Status,
@Semantics.amount.currencyCode: 'CurrencyCode'
booking_fee as BookingFee,
@Semantics.amount.currencyCode: 'CurrencyCode'
total_price as TotalPrice,
currency_code as CurrencyCode,
createdby as Createdby,
createdat as Createdat,
lastchangedby as Lastchangedby,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
lastchangedat as Lastchangedat
}</code></pre><P><BR /><STRONG>Projection View:</STRONG></P><pre class="lia-code-sample language-abap"><code>@Metadata.allowExtensions: true
@Metadata.ignorePropagatedAnnotations: true
@EndUserText: {
label: '###GENERATED Core Data Service Entity'
}
@ObjectModel: {
sapObjectNodeType.name: 'ZUUID_TABLE'
}
@AccessControl.authorizationCheck: #MANDATORY
define root view entity ZC_UUID_TABLE
provider contract transactional_query
as projection on ZR_UUID_TABLE
association [1..1] to ZR_UUID_TABLE as _BaseEntity on $projection.TravelID = _BaseEntity.TravelID
{
key TravelID,
Description,
Status,
@Semantics: {
amount.currencyCode: 'CurrencyCode'
}
BookingFee,
@Semantics: {
amount.currencyCode: 'CurrencyCode'
}
TotalPrice,
CurrencyCode,
Createdby,
Createdat,
Lastchangedby,
@Semantics: {
systemDateTime.localInstanceLastChangedAt: true
}
Lastchangedat,
_BaseEntity
}</code></pre><P><STRONG>Metadata Extension:<BR /></STRONG></P><pre class="lia-code-sample language-abap"><code><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [ {
position: 10 ,
label: 'Modify Status', type: #FOR_ACTION, dataAction: 'UpdateStatus'
} ]
<a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.selectionField: [ {
position: 10
} ]
</code></pre><P><STRONG> </STRONG></P><P><STRONG>Root Behavior Definition:</STRONG></P><UL><LI>define action in BDEF.</LI><LI>parameter which defines inputs for our actions.</LI></UL><pre class="lia-code-sample language-abap"><code>managed implementation in class ZBP_R_UUID_TABLE unique;
strict ( 2 );
with draft;
extensible;
define behavior for ZR_UUID_TABLE alias R_UUID_TABLE
persistent table ZUUID_TABLE
extensible
draft table ZUUID_TABLE_D
etag master Lastchangedat
lock master total etag Lastchangedat
authorization master( global )
{
field ( readonly )
TravelID,
Lastchangedat;
field ( numbering : managed )
TravelID;
create;
update(features : instance );
delete(features : global);
action(features : global) UpdateStatus parameter zstatus_str;
draft action Activate optimized;
draft action Discard;
draft action Edit;
draft action Resume;
draft determine action Prepare;
mapping for ZUUID_TABLE corresponding extensible
{
TravelID = travel_id;
Description = description;
Status = status;
BookingFee = booking_fee;
TotalPrice = total_price;
CurrencyCode = currency_code;
Createdby = createdby;
Createdat = createdat;
Lastchangedby = lastchangedby;
Lastchangedat = lastchangedat;
}
}</code></pre><P><STRONG>Parameter For Action:</STRONG></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'staus structure'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure zstatus_str {
status : /dmo/travel_status;
}</code></pre><P><BR /><STRONG>Implementation:<BR /></STRONG></P><pre class="lia-code-sample language-abap"><code> METHOD UpdateStatus.
MODIFY ENTITIES OF zr_uuid_table IN LOCAL MODE ENTITY r_uuid_table
UPDATE FIELDS ( Status ) WITH VALUE #( for key in keys
( %key-TravelID = key-TravelID
Status = key-%param-status ) )
REPORTED data(lt_reported)
FAILED data(lt_failed).
ENDMETHOD.</code></pre><P> </P><P><STRONG>Explanation:<BR /></STRONG></P><UL><LI><STRONG>MODIFY ENTITIES</STRONG> is the RAP statement used to change data in a behavior-managed entity.</LI><LI><STRONG>IN LOCAL MODE</STRONG> ensures the operation runs within the current LUW without immediately committing to the database.</LI><LI><STRONG>UPDATE FIELDS</STRONG> Status explicitly restricts the update to the Status field only, improving clarity and performance.</LI><LI><STRONG>keys</STRONG> contains all selected instances for which the action is triggered.</LI><LI>The <STRONG>FOR</STRONG> expression loops over each selected key, enabling mass processing in a single statement.</LI><LI><STRONG>%key-TravelID</STRONG> identifies the record to be updated.</LI><LI><STRONG>key-%param-status</STRONG> retrieves the action parameter value passed from the UI for ex, a new status selected by the user.</LI><LI><STRONG>lt_reported</STRONG> captures informational, warning, or success messages generated during the update.</LI><LI><STRONG>lt_failed</STRONG> contains records that could not be updated due to validation errors, authorization issues, or business rule violations.</LI><LI>These tables are automatically processed by RAP and reflected back to the UI, ensuring proper user feedback.</LI></UL><P><STRONG>Projection BDEF:</STRONG></P><UL><LI>expose our custom action in projection behavior definition.</LI></UL><pre class="lia-code-sample language-abap"><code>projection implementation in class ZBP_C_UUID_TABLE unique;
strict ( 2 );
extensible;
use draft;
use side effects;
define behavior for ZC_UUID_TABLE alias R_UUID_TABLE
extensible
use etag
{
use create;
use update;
use delete;
use action Edit;
use action Activate;
use action Discard;
use action Resume;
use action Prepare;
use action UpdateStatus;
}</code></pre><P><STRONG><BR /><BR />Preview</STRONG>:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="harsha_reddy24_0-1769070515390.png" style="width: 524px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364004iD9317102B5D59FE8/image-dimensions/524x155/is-moderation-mode/true?v=v2" width="524" height="155" role="button" title="harsha_reddy24_0-1769070515390.png" alt="harsha_reddy24_0-1769070515390.png" /></span></P><UL><LI>select records and click on <STRONG>modify status</STRONG> button.</LI><LI>Assign any travel status and it will be updated.</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="harsha_reddy24_1-1769070896884.png" style="width: 504px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364008i51970E15FE7EA866/image-dimensions/504x155/is-moderation-mode/true?v=v2" width="504" height="155" role="button" title="harsha_reddy24_1-1769070896884.png" alt="harsha_reddy24_1-1769070896884.png" /></span></P><P> </P><P><STRONG>Conclusion:</STRONG></P><P>Custom actions in RAP provide a clean and scalable way to handle mass update scenarios that go beyond standard CRUD operations. By leveraging MODIFY ENTITIES, action parameters, and RAP-managed messaging, developers can implement efficient bulk updates while fully adhering to RAP principles. This approach ensures better performance, cleaner code, and seamless integration with Fiori Elements, making it a recommended pattern for real-world business requirements.<BR /><BR /><a href="https://community.sap.com/t5/c-khhcw49343/ABAP+Connectivity/pd-p/266264953119842772207986043063520" class="lia-product-mention" data-product="313-1">ABAP Connectivity</a> <a href="https://community.sap.com/t5/c-khhcw49343/ABAP+Extensibility/pd-p/338571334339306322581424656448659" class="lia-product-mention" data-product="315-1">ABAP Extensibility</a> <a href="https://community.sap.com/t5/c-khhcw49343/SAP+BTP+ABAP+environment/pd-p/73555000100800001164" class="lia-product-mention" data-product="11-1">SAP BTP ABAP environment</a> </P>2026-01-23T06:31:27.262000+01:00https://community.sap.com/t5/artificial-intelligence-blogs-posts/developing-ai-enabled-applications-with-abap-ai-sdk-powered-by-islm/ba-p/14252514Developing AI Enabled Applications with ABAP AI SDK powered by ISLM2026-01-23T08:05:00.042000+01:00Swathy_Shttps://community.sap.com/t5/user/viewprofilepage/user-id/1972836<P><A href="https://www.sap.com/documents/2025/02/b4f714de-f57e-0010-bca6-c68f7e60039b.html" target="_self" rel="noopener noreferrer">SAP Joule for Developers</A>, ABAP AI Capabilities is your intelligent coding companion, bringing deep expertise in ABAP Cloud and AI-driven development. When we talk about ABAP AI use cases, they revolve around three key pillars — <EM>Accelerate, Empower, </EM>and <EM>Transform</EM>. Each AI capability is delivered as part of these pillars, helping developers enhance productivity, build smarter applications, and drive innovation. To learn more about SAP Joule for Developers, refer to the blog <SPAN class=""><SPAN>post </SPAN></SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/joule-speaks-abap/ba-p/14018226" target="_self">Joule speaks ABAP!</A> by Dr. Alexander Rother.</P><P>Have you ever wondered how to infuse intelligence into your ABAP-based business applications and move beyond standard transactional scenarios? If yes, you’ve landed at the right place. In this blog, we’ll explore how you can build AI-enabled applications using the ABAP AI SDK powered by <A href="https://help.sap.com/docs/ABAP_PLATFORM_NEW/7989a582039547ae91d8f483e487058d/436151b128614f0e84024015136043d3.html" target="_self" rel="noopener noreferrer">Intelligent Scenario Lifecycle Management (ISLM)</A>, the main deliverable of the Empower use case.</P><P><STRONG>The Scenario</STRONG><SPAN><STRONG> </STRONG><BR /></SPAN><SPAN>The goal of this scenario is to </SPAN><STRONG><SPAN>infuse generative AI into an ABAP Cloud application</SPAN></STRONG><SPAN> to enhance user experience and automate content creation.</SPAN><SPAN> </SPAN></P><P><STRONG><SPAN>Use Case:</SPAN></STRONG><SPAN> Build an </SPAN><STRONG><SPAN>ABAP Cloud application</SPAN></STRONG><SPAN> to generate a </SPAN><STRONG><SPAN>travel itinerary</SPAN></STRONG><SPAN> for a given city based on user input, along with a </SPAN><STRONG><SPAN>budget breakdown, </SPAN></STRONG><SPAN>using </SPAN><STRONG><SPAN>ABAP AI SDK </SPAN></STRONG><SPAN>powered by</SPAN><STRONG><SPAN> ISLM</SPAN></STRONG><SPAN>.</SPAN><SPAN> </SPAN></P><P><STRONG>Which products are supported? </STRONG></P><P><SPAN>The ABAP AI SDK powered by ISLM is available in:</SPAN><SPAN> </SPAN></P><UL><LI><A href="https://pages.community.sap.com/topics/btp-abap-environment" target="_self" rel="noopener noreferrer"><SPAN>SAP BTP ABAP Environment</SPAN></A></LI></UL><UL><LI><A href="https://www.sap.com/uk/products/erp/s4hana.html" target="_self" rel="noopener noreferrer"><SPAN>SAP S/4HANA Cloud Public Edition</SPAN></A></LI></UL><UL><LI><SPAN><A href="https://www.sap.com/india/products/erp/s4hana-private-edition.html" target="_self" rel="noopener noreferrer">SAP S/4HANA Cloud Private Edition</A> and SAP S/4HANA OnPrem as of release 2025 via standard delivery. Lower releases of SAP S/4HANA Cloud Private Edition and SAP S/4HANA OnPrem are supported via Transport-based Correction Instructions (TCI), see details in </SPAN><A href="https://me.sap.com/notes/3513374" target="_blank" rel="noopener noreferrer"><SPAN>SAP Note 3513374</SPAN></A><SPAN> and the SAP Notes and instructions linked therein.</SPAN><SPAN> </SPAN></LI></UL><P><STRONG><SPAN>Overview</SPAN></STRONG><SPAN> </SPAN></P><P><SPAN>To build this application, we’ll bring together several key components:</SPAN><SPAN> </SPAN></P><UL><LI><A href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/what-is-sap-ai-core" target="_blank" rel="noopener noreferrer"><STRONG><SPAN>SAP AI Core</SPAN></STRONG></A><SPAN> – for managing AI scenarios and accessing generative AI capabilities via the generative AI hub.</SPAN><SPAN> </SPAN></LI></UL><UL><LI><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/a630d57fc5004c6383e7a81efee7a8bb/16d3b0ed27b64bebb4f1ccdac7663ba0.html?version=2508.VAL" target="_blank" rel="noopener noreferrer"><STRONG><SPAN>Communication Arrangement</SPAN></STRONG></A><SPAN> – to establish secure integration between ABAP and AI Core.</SPAN><SPAN> </SPAN></LI></UL><UL><LI><A href="https://help.sap.com/docs/ABAP_PLATFORM_NEW/7989a582039547ae91d8f483e487058d/436151b128614f0e84024015136043d3.html" target="_blank" rel="noopener noreferrer"><STRONG><SPAN>ISLM (Intelligent Scenario Lifecycle Management)</SPAN></STRONG></A><SPAN> and </SPAN><STRONG><SPAN>ABAP AI SDK</SPAN></STRONG><SPAN> – for seamless integration with Generative AI Hub, SAP BTP AI services</SPAN><SPAN> </SPAN></LI></UL><UL><LI><STRONG><SPAN>Custom ABAP Cloud Application</SPAN></STRONG><SPAN> – to embed and expose AI capabilities through a user-friendly application.</SPAN><SPAN> </SPAN></LI></UL><P><SPAN> </SPAN><STRONG><SPAN>Implementation Steps</SPAN></STRONG><SPAN> </SPAN></P><OL><LI><STRONG><SPAN>Set Up the Environment</SPAN></STRONG><SPAN> <BR /></SPAN><SPAN>Configure the </SPAN><STRONG><SPAN>AI Core service plan</SPAN></STRONG><SPAN> and establish </SPAN><STRONG><SPAN>communication arrangements</SPAN></STRONG><SPAN> between the ABAP system and AI Core.</SPAN><SPAN><BR /></SPAN></LI><LI><STRONG>Build Custom Travel Plan ABAP Cloud application. </STRONG></LI><LI><STRONG><SPAN>Create and Deploy Intelligent Scenarios</SPAN></STRONG><SPAN> <BR /></SPAN><SPAN>Define the AI scenario in </SPAN><STRONG><SPAN>ISLM</SPAN></STRONG><SPAN>, train or connect your model, and deploy it for consumption.</SPAN><SPAN> </SPAN></LI><LI><STRONG><SPAN>Consume Intelligent Scenarios in ABAP</SPAN></STRONG><SPAN> <BR /></SPAN><SPAN>Integrate the deployed scenario within a </SPAN><STRONG><SPAN>custom ABAP Cloud application</SPAN></STRONG><SPAN> to generate AI-driven outputs - in this case, travel itineraries and budget breakdown.</SPAN><SPAN> </SPAN></LI></OL><P><STRONG><SPAN>1. Set Up the Environment</SPAN></STRONG><SPAN> </SPAN><SPAN> </SPAN></P><OL><LI><SPAN>Configure the required entitlements to make SAP AI Core accessible in your subaccount. To use </SPAN><STRONG><SPAN>Generative AI capabilities</SPAN></STRONG><SPAN> (including the Generative AI Hub and LLM access), choose the </SPAN><STRONG><SPAN>extended</SPAN></STRONG><SPAN> service plan. To add a Service Plan, see:</SPAN><SPAN> </SPAN><A href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/add-service-plan" target="_blank" rel="noopener noreferrer"><SPAN>Add a Service Plan | SAP Help Portal</SPAN></A><SPAN> </SPAN><SPAN> </SPAN></LI><LI><SPAN>Download the client default certificate of the ABAP system to create the X.509 service key in AI Core instance. Follow the steps here : </SPAN><A href="https://help.sap.com/docs/btp/sap-business-technology-platform/download-certificate-3645813291be47839e72ab08d8a31ac9?utm_source=chatgpt.com" target="_blank" rel="noopener noreferrer"><SPAN>Download Certificate | SAP Help Portal</SPAN></A><SPAN> </SPAN><SPAN> </SPAN></LI><LI><SPAN>Create a service instance and a service key to access SAP AI Core, where the large language models in the generative AI hub are hosted. The X.509 service key is created with the downloaded client default certificate of the ABAP system passed as parameter. Follow</SPAN><SPAN> the steps from here:</SPAN><SPAN> </SPAN><A href="https://help.sap.com/docs/btp/sap-business-technology-platform/create-sap-btp-service-instance-and-key?utm_source=chatgpt.com" target="_blank" rel="noopener noreferrer"><SPAN>Create SAP BTP Service Instance and Key | SAP Help Portal</SPAN></A><SPAN><BR /></SPAN></LI><LI><SPAN>Configure the communication system for </SPAN><STRONG><SPAN>SAP_COM_0A69</SPAN></STRONG><SPAN>. Currently, you can create one instance of the communication scenario per client. Follow the steps from here: </SPAN><A href="https://help.sap.com/docs/btp/sap-business-technology-platform/how-to-configure-communication-system-for-sap-com-0a69-7d691a07627442a3b1d07000417c8056?utm_source=chatgpt.com" target="_blank" rel="noopener noreferrer"><SPAN>How to Configure the Communication System for SAP_COM_0A69 | SAP Help Portal</SPAN></A><SPAN> </SPAN></LI><LI><SPAN>To create the Communication Arrangement for </SPAN><STRONG><SPAN>SAP_COM_0A69. </SPAN></STRONG><SPAN>Follow the steps from here: </SPAN><A href="https://help.sap.com/docs/btp/sap-business-technology-platform/how-to-create-communication-arrangement-for-sap-com-0a69-20014a0910124c18aca114d4477c797e?utm_source=chatgpt.com" target="_blank" rel="noopener noreferrer"><SPAN>How to Create the Communication Arrangement for SAP_COM_0A69 | SAP Help Portal</SPAN></A><SPAN> <BR /></SPAN></LI><LI><SPAN>After the communication system and communication arrangement are configured, create an intelligent scenario and continue managing the lifecycle of the intelligent scenario. You are required to create an intelligent scenario for your custom-owned Gen AI use cases.</SPAN><SPAN> </SPAN></LI></OL><P><STRONG>2. Create a custom ABAP Cloud Application to infuse AI Capabilities</STRONG><SPAN><STRONG> </STRONG></SPAN></P><P><STRONG><SPAN>2.1 Create Package</SPAN></STRONG></P><OL><LI>In ABAP Development Tools (ADT), go to the <STRONG>Project Explorer</STRONG>, right-click on the package <STRONG>ZLOCAL</STRONG>, and select <STRONG>New</STRONG> <STRONG>→</STRONG> <STRONG>ABAP Package</STRONG> from the context menu.</LI><LI>Maintain the required information: <OL class="lia-list-style-type-lower-alpha"><LI>Name: <EM>ZTRAVEL_ITINERARY</EM></LI><LI>Description: <EM>ABAP AI Capabilities</EM><BR />Select the box <STRONG>Add to favorites package</STRONG>. Choose <STRONG>Next</STRONG> and <STRONG>Finish</STRONG>.</LI></OL></LI></OL><P><STRONG> 2.2 Create database table </STRONG></P><P>Create a database table to store the Travel Itinerary data.</P><OL><LI>Right-click on your ABAP package <EM>ZTRAVEL_ITINERARY</EM> and select <STRONG>New</STRONG> <STRONG>→</STRONG> <STRONG>Other ABAP Repository Object</STRONG> from the context menu.</LI><LI>Search for <STRONG>database table</STRONG>, select it, and choose <STRONG>Next</STRONG>.</LI><LI>Maintain the required information: <STRONG>Name:</STRONG> <EM>ZRAP_TRAVEL_DATA <STRONG> </STRONG></EM><STRONG>Description:</STRONG> Database Table for Travel Itinerary</LI><LI>Choose <STRONG>Next.</STRONG><STRONG> </STRONG>Choose <STRONG>Finish</STRONG> and replace the default code with the following code snippet: <EM><BR /></EM></LI></OL><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Database table for travel Itinerary'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zrap_travel_data {
key client : abap.clnt not null;
key travel_id : sysuuid_x16 not null;
traveler_name : abap.char(80);
starting_point : abap.char(100);
destination : abap.char(100);
start_date : /dmo/begin_date;
end_date : /dmo/end_date;
@Semantics.amount.currencyCode : 'zrap_travel_data.budget_curr'
budget : abap.curr(15,2);
budget_curr : abap.cuky;
transport_mode : abap.char(40);
accommodation_type : abap.char(50);
ai_itinerary : abap.sstring(1333);
ai_budget_breakdown : abap.sstring(1000);
status : abap.char(20);
local_created_by : abp_creation_user;
local_created_at : abp_creation_tstmpl;
local_last_changed_by : abp_locinst_lastchange_user;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
last_changed_at : abp_lastchange_tstmpl;
}</code></pre><P> 5. Save<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_0-1765162371312.png" style="width: 21px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349463i9681B46ABE2AA9AA/image-dimensions/21x21?v=v2" width="21" height="21" role="button" title="Swathy_S_0-1765162371312.png" alt="Swathy_S_0-1765162371312.png" /></span> and activate <span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_1-1765162371313.png" style="width: 26px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349462i0A0A551F373ABAC3/image-dimensions/26x28?v=v2" width="26" height="28" role="button" title="Swathy_S_1-1765162371313.png" alt="Swathy_S_1-1765162371313.png" /></span>the changes in the table <EM>ZRAP_TRAVEL_DATA</EM></P><P><STRONG>2.3 Generate transactional UI service</STRONG></P><P>Create your OData V4-based UI service with the built-in ADT generator. The generated business service will be transactional, draft-enabled, and enriched with UI semantics for the generation of the SAP Fiori elements app.</P><OL><LI>Right-click your database table<EM> ZRAP_TRAVEL_DATA</EM> and select <STRONG>Generate ABAP Repository Objects</STRONG> from the context menu.<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_5-1765162371331.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349465i6CA029F7668CA164/image-size/medium?v=v2&px=400" role="button" title="Swathy_S_5-1765162371331.png" alt="Swathy_S_5-1765162371331.png" /></span><BR /><BR /></LI><LI>Select <STRONG>OData UI Service</STRONG> and choose <STRONG>Next </STRONG>and provide the package details<STRONG>.</STRONG><STRONG><BR /><BR /></STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_6-1765162371335.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349467i8D71F00086B9D8DD/image-size/medium?v=v2&px=400" role="button" title="Swathy_S_6-1765162371335.png" alt="Swathy_S_6-1765162371335.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_7-1765162371339.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349468i22747EDFF7B27BE4/image-size/medium?v=v2&px=400" role="button" title="Swathy_S_7-1765162371339.png" alt="Swathy_S_7-1765162371339.png" /></span><BR /><BR /></LI><LI> Maintain the required information on the <STRONG>Configure Generator</STRONG> dialog to provide the name of your data model and generate them.</LI><LI>For that, navigate through the wizard tree <STRONG>(Business Objects, Data Model, etc…)</STRONG>, maintain the artefact names provided and choose <STRONG>Next</STRONG>.</LI><LI>Verify the maintained entries and choose <STRONG>Next </STRONG>to confirm and choose <STRONG>Finish.<BR /></STRONG><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_8-1765162371347.png" style="width: 449px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349469iBC29B24051D14FE5/image-dimensions/449x585?v=v2" width="449" height="585" role="button" title="Swathy_S_8-1765162371347.png" alt="Swathy_S_8-1765162371347.png" /></span><BR /><BR /></LI><LI>The artifacts needed will be generated. Go to the <STRONG>Project Explorer</STRONG>, select your package <EM>ZRAP_TRAVEL_ITENERARY</EM>, refresh it by choosing <STRONG>F5</STRONG>, and check all generated ABAP repository objects.<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_9-1765162371350.png" style="width: 606px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349470i4A9AB8DE5D19237D/image-dimensions/606x162?v=v2" width="606" height="162" role="button" title="Swathy_S_9-1765162371350.png" alt="Swathy_S_9-1765162371350.png" /></span><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_10-1765162371352.png" style="width: 606px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349472i0DCE41A107C8AAAC/image-dimensions/606x206?v=v2" width="606" height="206" role="button" title="Swathy_S_10-1765162371352.png" alt="Swathy_S_10-1765162371352.png" /></span><BR /><BR /><span class="lia-unicode-emoji" title=":information:">ℹ️</span> NOTE: The names of the artifacts may differ from those shown in this blog post, as they are generated by the wizard.</LI></OL><P><STRONG>2.4 Adjust meta data extensions</STRONG><BR />Open the metadata extension<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_11-1765162371353.png" style="width: 20px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349471iD8E99027FF714C03/image-dimensions/20x19?v=v2" width="20" height="19" role="button" title="Swathy_S_11-1765162371353.png" alt="Swathy_S_11-1765162371353.png" /></span> <STRONG>ZC_RAP_TRAVEL_DATA</STRONG> and make the adjustments as shown in the provided code snippet. <EM>ZC_RAP_TRAVEL_DATA</EM> should look like this:</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE
@UI.headerInfo.title.type: #STANDARD
@UI.headerInfo.title.value: 'TravelID'
@UI.headerInfo.description.type: #STANDARD
@UI.headerInfo.description.value: 'TravelID'
annotate view ZC_RAP_TRAVEL_DATA with
{
.facet: [
{ label: 'General Information', id: 'GeneralInfo', purpose: #STANDARD, position: 10, type: #IDENTIFICATION_REFERENCE },
{ label: 'AI Generated Itinerary', id: 'AIItineraryFacet', purpose: #STANDARD, position: 200, type: #FIELDGROUP_REFERENCE, targetQualifier: 'AI_ITIN' },
{ label: 'AI Budget Breakdown', id: 'AIBudgetFacet', purpose: #STANDARD, position: 210, type: #FIELDGROUP_REFERENCE, targetQualifier: 'AI_BUDGET' }
]
@EndUserText.label: 'TravelID'
.identification: [ { position: 10, label: 'TravelID' } ]
.lineItem: [ { position: 10, label: 'TravelID' } ]
.selectionField: [ { position: 10 } ]
TravelID;
@EndUserText.label: 'TravelerName'
.identification: [ { position: 20, label: 'TravelerName' } ]
.lineItem: [ { position: 20, label: 'TravelerName' } ]
.selectionField: [ { position: 20 } ]
TravelerName;
@EndUserText.label: 'StartingPoint'
.identification: [ { position: 30, label: 'StartingPoint' } ]
.lineItem: [ { position: 30, label: 'StartingPoint' } ]
.selectionField: [ { position: 30 } ]
StartingPoint;
@EndUserText.label: 'Destination'
.identification: [ { position: 40, label: 'Destination' } ]
.lineItem: [ { position: 40, label: 'Destination' } ]
.selectionField: [ { position: 40 } ]
Destination;
.identification: [ { position: 50 } ]
.lineItem: [ { position: 50 } ]
.selectionField: [ { position: 50 } ]
StartDate;
.identification: [ { position: 60 } ]
.lineItem: [ { position: 60 } ]
.selectionField: [ { position: 60 } ]
EndDate;
@EndUserText.label: 'Budget'
.identification: [ { position: 70, label: 'Budget' } ]
.lineItem: [ { position: 70, label: 'Budget' } ]
.selectionField: [ { position: 70 } ]
Budget;
@EndUserText.label: 'TransportMode'
.identification: [ { position: 80, label: 'TransportMode' } ]
.lineItem: [ { position: 80, label: 'TransportMode' } ]
.selectionField: [ { position: 80 } ]
TransportMode;
@EndUserText.label: 'AccommodationType'
.identification: [ { position: 90, label: 'AccommodationType' } ]
.lineItem: [ { position: 90, label: 'AccommodationType' } ]
.selectionField: [ { position: 90 } ]
AccommodationType;
// FULL-WIDTH AI ITINERARY
@EndUserText.label: 'Generated Itinerary'
.fieldGroup: [ { qualifier: 'AI_ITIN', position: 10, label: 'AI Generated Itinerary' } ]
.multiLineText: true
AiItinerary;
// FULL-WIDTH AI BUDGET
@EndUserText.label: 'Budget Breakdown'
.fieldGroup: [ { qualifier: 'AI_BUDGET', position: 10, label: 'AI Budget Breakdown' } ]
.multiLineText: true
AiBudgetBreakdown;
@EndUserText.label: 'Status'
.identification: [ { position: 120, label: 'Status' } ]
.lineItem: [ { position: 120, label: 'Status' } ]
.selectionField: [ { position: 120 } ]
Status;
@EndUserText.label: '_BaseEntity'
.identification: [ {
position: 180 ,
label: '_BaseEntity'
} ]
.lineItem: [ {
position: 180 ,
label: '_BaseEntity'
} ]
.selectionField: [ {
position: 180
} ]
_BaseEntity;
}</code></pre><P><SPAN> </SPAN><STRONG><SPAN>2.5 Preview travel app</SPAN></STRONG></P><P>1. Publish the local service endpoint of your service binding and start the <STRONG>Fiori elements App Preview</STRONG>. Open your service binding <EM>ZUI_RAP_TRAVEL_DATA_O4</EM> and choose <STRONG>Publish</STRONG>.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_14-1765162371357.png" style="width: 582px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349475iE42FAB080724BCB1/image-dimensions/582x182?v=v2" width="582" height="182" role="button" title="Swathy_S_14-1765162371357.png" alt="Swathy_S_14-1765162371357.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_15-1765162371363.png" style="width: 575px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349476iD2ECEB8B64CE2A7A/image-dimensions/575x210?v=v2" width="575" height="210" role="button" title="Swathy_S_15-1765162371363.png" alt="Swathy_S_15-1765162371363.png" /></span></P><P>2. Double-click on the Entity <EM>ZC_RAP_TRAVEL_DATA</EM> in the <STRONG>Entity Set and Association</STRONG> section to open the <STRONG>Fiori elements App Preview</STRONG>.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_16-1765162371373.png" style="width: 616px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349477i244638D2AA8A44D9/image-dimensions/616x339?v=v2" width="616" height="339" role="button" title="Swathy_S_16-1765162371373.png" alt="Swathy_S_16-1765162371373.png" /></span></P><P>For reference: <A href="https://developers.sap.com/tutorials/abap-environment-rap100-generate-ui-service.html#top" target="_blank" rel="noopener noreferrer">Create Database Table and Generate UI Service | SAP Tutorials</A></P><H3 id="toc-hId-1892749195"><STRONG>3. Create an Intelligent Scenario (Generative AI)</STRONG><SPAN> </SPAN></H3><P>You first need to create an intelligent scenario for your custom Generative AI use cases. This scenario acts as the central place to manage the entire lifecycle of your AI asset from setup to generating inference results. All lifecycle steps can be executed through the Intelligent Scenario Management app. Here are the key steps to create an intelligent scenario:<STRONG> </STRONG></P><P><STRONG>3.1 Create ISLM Scenario</STRONG><SPAN><BR /></SPAN>An intelligent scenario is an ABAP representation of a predictive business-specific use case. These intelligent scenarios are AI-powered workflows or processes designed to automate and optimize business tasks. <SPAN> </SPAN></P><OL><LI>Select your package, right-click, <STRONG>New → Other → Intelligent Scenario → ZINTS_RAP120<BR /></STRONG><SPAN><BR /></SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_0-1765163757248.png" style="width: 406px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349480i55F5697A6E4B79FD/image-dimensions/406x345?v=v2" width="406" height="345" role="button" title="Swathy_S_0-1765163757248.png" alt="Swathy_S_0-1765163757248.png" /></span><STRONG><BR /><BR /></STRONG></LI><LI>Enter a name and description. Select <EM>SIDEBYSIDE </EM>for <STRONG>Scenario Technology</STRONG> field. Choose <STRONG>Next.<BR /></STRONG><SPAN><BR /></SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_1-1765163757250.png" style="width: 408px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349479i51B899FB8444FDF5/image-dimensions/408x426?v=v2" width="408" height="426" role="button" title="Swathy_S_1-1765163757250.png" alt="Swathy_S_1-1765163757250.png" /></span><STRONG><BR /><BR /></STRONG></LI><LI>Set the following parameters for the created Intelligent Scenario.</LI></OL><P>Intelligent Scenario Lifecycle Management supports two primary types of intelligent scenarios:</P><P><STRONG>Embedded Scenarios</STRONG></P><P>Embedded scenarios are built and executed within SAP S/4HANA using the ABAP platform. They rely on the in-database machine learning capabilities of SAP HANA, such as: HANA Predictive Analysis Library (PAL), Automated Predictive Library (APL)</P><P><STRONG>Side-by-Side Scenarios</STRONG></P><P>Side-by-side scenarios, where SAP S/4HANA runs in a separate stack than its machine learning infrastructure provider, such as SAP Business Technology Platform (SAP BTP). These scenarios use external ML services or custom ML models.<BR />Generative AI, SAP AI Core, Data Attribute Recommendation</P><UL><LI>For our scenario, we will be using the <STRONG>side by side</STRONG> scenarios type, sap <STRONG>SAPGEN AI</STRONG><SPAN><BR /></SPAN><STRONG>Turnkey Enablement</STRONG>: Automatically enable SAP-delivered (turnkey) GenAI use cases with minimal configuration effort.</LI></UL><P> </P><TABLE width="362"><TBODY><TR><TD width="211.438px"><P><STRONG>Field</STRONG></P></TD><TD width="149.762px"><P><STRONG>Value</STRONG></P></TD></TR><TR><TD width="211.438px"><P>Intelligent Scenario Type</P></TD><TD width="149.762px"><P>SAPGENAI</P></TD></TR><TR><TD width="211.438px"><P>Automate Turnkey Switch On</P></TD><TD width="149.762px"><P>Check the checkbox</P></TD></TR><TR><TD width="211.438px"><P>Usage Type</P></TD><TD width="149.762px"><P>CUSTOMER</P></TD></TR></TBODY></TABLE><P><SPAN> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_2-1765163757260.png" style="width: 551px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349481iC31EE9125E6C91D7/image-dimensions/551x223?v=v2" width="551" height="223" role="button" title="Swathy_S_2-1765163757260.png" alt="Swathy_S_2-1765163757260.png" /></span></P><OL><LI>Activate the object.</LI></OL><P><SPAN><STRONG>3.2 Create ISLM Model<BR /></STRONG><BR /></SPAN>1. Select your package, right-click, <STRONG>New → Other → Intelligent Scenario Model </STRONG><SPAN><BR /></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_3-1765163757269.png" style="width: 470px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349484i7C5B73AEBFA29D1B/image-dimensions/470x334?v=v2" width="470" height="334" role="button" title="Swathy_S_3-1765163757269.png" alt="Swathy_S_3-1765163757269.png" /></span></P><P>2. Fill the <EM>Description </EM>and <EM>Model Name </EM>fields. Assign the previously created scenario in <EM>Intelligent Scenario Name.</EM> Choose <STRONG>Next</STRONG>. <SPAN><BR /></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_4-1765163757271.png" style="width: 460px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349482iD9DA1FBC5A3267A7/image-dimensions/460x488?v=v2" width="460" height="488" role="button" title="Swathy_S_4-1765163757271.png" alt="Swathy_S_4-1765163757271.png" /></span></P><P>3. Enter the <EM>ExecutableID </EM>and <EM>Large Language Model Name </EM>and <EM>Large Language Model Version</EM></P><P> </P><TABLE width="633"><TBODY><TR><TD width="156"><P><STRONG>Field </STRONG></P></TD><TD width="477"><P><STRONG>Possible values</STRONG></P></TD></TR><TR><TD width="156"><P><EM>Executable Id</EM></P></TD><TD width="477"><P>o aicore-sap</P><P>o aws-bedrock</P><P>o azure-openai</P><P>o gcp-vertexai</P></TD></TR><TR><TD width="156"><P><EM>Large Language Model Name</EM></P></TD><TD width="477"><P>It depends on the <EM>Executable Id</EM></P></TD></TR><TR><TD width="156"><P><EM>Large Language Model Version</EM></P></TD><TD width="477"><P>One option is to leave the version field free, then the latest version will be used. This helps to avoid issues in case that the model has been deprecated in AI Core<SPAN><BR /></SPAN> More info: <SPAN><A href="https://me.sap.com/notes/3437766" target="_blank" rel="noopener noreferrer">https://me.sap.com/notes/3437766</A></SPAN></P></TD></TR></TBODY></TABLE><P><SPAN> </SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_5-1765163757273.png" style="width: 627px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349483i53F17BECA5FE852D/image-dimensions/627x312?v=v2" width="627" height="312" role="button" title="Swathy_S_5-1765163757273.png" alt="Swathy_S_5-1765163757273.png" /></span></P><P><SPAN> </SPAN><SPAN>4. Through the Prompt Library API, we can use predefined prompt templates to generate new prompts. Currently prompt templates are defined through ISLM. To get an instance of the prompt template you want to use, you need to provide the ISLM scenario and the ID of the prompt template. Click on <STRONG>Add</STRONG> and provide the following details:</SPAN><SPAN> <BR /><BR /></SPAN><STRONG><SPAN>Prompt Template Name: </SPAN></STRONG><SPAN>SYSTEM_PROMPT_ITIN</SPAN><SPAN> <BR /></SPAN><STRONG><SPAN>Prompt Template: </SPAN></STRONG><SPAN>System prompt for generating Itinerary</SPAN><SPAN> <BR /></SPAN><STRONG><SPAN>Prompt:</SPAN></STRONG><SPAN> </SPAN><SPAN>You are a professional travel planner. Create a </SPAN><STRONG><SPAN>{ISLM_days} </SPAN></STRONG><SPAN>day itinerary for a given destination, including daily highlights, must-visit spots and activities. Keep it under 1000 characters.</SPAN><SPAN> <BR /></SPAN><SPAN> <BR /></SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_0-1766724835633.png" style="width: 656px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/355642i3856831DBD7CD8A9/image-dimensions/656x250?v=v2" width="656" height="250" role="button" title="Swathy_S_0-1766724835633.png" alt="Swathy_S_0-1766724835633.png" /></span></P><P><SPAN> 5. </SPAN><SPAN>The <STRONG>Prompt Parameters</STRONG> is captured from the prompt template after activation.</SPAN> <BR /><SPAN> <BR /></SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_1-1766724835634.png" style="width: 666px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/355643i589E0A109B01685A/image-dimensions/666x248?v=v2" width="666" height="248" role="button" title="Swathy_S_1-1766724835634.png" alt="Swathy_S_1-1766724835634.png" /></span></P><P>6. Add another prompt template for User prompt:<BR /><SPAN> <BR /></SPAN><STRONG><SPAN>Prompt Template Name: </SPAN></STRONG><SPAN>USER_PROMPT_ITIN</SPAN><SPAN> <BR /></SPAN><STRONG><SPAN>Prompt Template: </SPAN></STRONG><SPAN>User prompt template</SPAN><SPAN> <BR /></SPAN><STRONG><SPAN>Prompt:</SPAN></STRONG><SPAN> </SPAN><SPAN>Destination: {ISLM_destination} Departure: {ISLM_startingpoint} Travel Dates: {ISLM_startdate} to {ISLM_enddate} Mode of Transport: </SPAN><SPAN>{ISLM_transportmode} Accommodation Type: {ISLM_accommodationtype} Generate the itinerary now</SPAN><SPAN> <BR /></SPAN><SPAN> </SPAN><SPAN> <BR /></SPAN><SPAN> </SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_2-1766724835635.png" style="width: 651px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/355644i2BFB6CEE8EA7F1A1/image-dimensions/651x257?v=v2" width="651" height="257" role="button" title="Swathy_S_2-1766724835635.png" alt="Swathy_S_2-1766724835635.png" /></span></P><P>7. Activate the object<BR /><STRONG>⚠ WARNING</STRONG><SPAN> : Before you try the ABAP AI SDK out in an ABAP Class, please make sure that the deployment is running.</SPAN> </P><P><STRONG><SPAN>3.3 In Fiori Launchpad</SPAN></STRONG></P><P>To use the Intelligent Scenarios and Intelligent Scenario Management apps, you must have the appropriate access rights to the SAP Fiori Launchpad. The access for ISLM is added to the business role of an analytics specialist; <STRONG>BR_ANALYTICS_SPECIALIST. </STRONG><SPAN><BR /></SPAN><STRONG> </STRONG>You can create and manage ISLM Scenarios through Intelligent Scenario Management app.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_6-1765163757282.png" style="width: 492px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349487i26F7E91D89270AA2/image-dimensions/492x235?v=v2" width="492" height="235" role="button" title="Swathy_S_6-1765163757282.png" alt="Swathy_S_6-1765163757282.png" /></span></P><P>You can check your previously created scenario in the <STRONG>Intelligent Scenario Management </STRONG>Fiori App.<SPAN><BR /></SPAN>In the next step instantiate ABAP AI SDK using the created ISLM Scenario<SPAN> </SPAN></P><P><STRONG><SPAN>3.4. Consume Intelligent Scenarios in ABAP</SPAN></STRONG></P><P><SPAN><STRONG><A href="https://help.sap.com/docs/abap-ai/generative-ai-in-abap-cloud/learn-9d5945155315417b9d9a4b76b9074870" target="_blank" rel="noopener noreferrer">ABAP AI SDK</A><BR /></STRONG>The ABAP AI SDK powered by Intelligent Scenario Lifecycle Management (ISLM) is an ABAP reuse library that supports you in interacting with large language models (LLMs) hosted on the generative AI hub in SAP AI Core. Using the ABAP AI SDK, you can build your own AI-based features in ABAP.</SPAN></P><P><SPAN><A href="https://help.sap.com/docs/abap-ai/generative-ai-in-abap-cloud/completion-api" target="_blank" rel="noopener noreferrer"><STRONG>Completion API</STRONG></A><BR />This API provides access to the completion API functionality of Large Language Models (LLMs), meaning that it uses an LLM to generate a text response from a prompt.<BR /><BR /></SPAN><STRONG><A href="https://help.sap.com/docs/abap-ai/generative-ai-in-abap-cloud/prompt-library-api" target="_blank" rel="noopener noreferrer">Prompt Library API</A><BR /></STRONG>Using the prompt library API, you can use prompt templates predefined via ISLM to generate new prompts.</P><P><A href="https://help.sap.com/docs/abap-ai/generative-ai-in-abap-cloud/catchable-exceptions" target="_blank" rel="noopener noreferrer"><STRONG>Catchable Exceptions</STRONG></A><BR />The execution of the completion API may cause an error, and therefore, <EM>CX_AIC_COMPLETION_API</EM> can be caught as a result. When the error is triggered by the LLM itself or by the AI service provider, <EM>CX_AIC_COMPLETION_API</EM> provides further information about the error.<BR /><BR />Add a determination to the Travel Business Object and enhance it with the ABAP AI SDK powered by ISLM. Define the determination <STRONG>generateAIItinerary</STRONG> in the behavior definition <STRONG>ZR_RAP_TRAVEL_DATA</STRONG> and implement it in the behavior implementation class, aka behavior pool <STRONG>Z</STRONG><STRONG>BP_R_RAP_TRAVEL_DATA</STRONG>.</P><P>1. Add the mandatory fields. Go to the <STRONG>Project Explorer</STRONG> and open your behaviour definition <STRONG>ZR_RAP_TRAVEL_DATA</STRONG>.</P><pre class="lia-code-sample language-abap"><code>field ( readonly )
TravelID,
Status,
AiBudgetBreakdown,
AiItinerary,
LocalCreatedBy,
LocalCreatedAt,
LocalLastChangedBy,
LocalLastChangedAt,
LastChangedAt;
field ( mandatory : create )
TravelerName,
EndDate,
StartDate,
Budget,
AccommodationType,
StartingPoint,
TransportMode,
Destination;
determination generateAIItinerary on modify { create; }</code></pre><P> Define the <STRONG>generateAIItinerary</STRONG> determination:<BR /><STRONG><BR /></STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_10-1765163757290.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349490iCA676CFDF3A00A53/image-size/medium?v=v2&px=400" role="button" title="Swathy_S_10-1765163757290.png" alt="Swathy_S_10-1765163757290.png" /></span></P><OL><LI>Save and activate the changes in <STRONG>ZR_RAP_TRAVEL_DATA</STRONG>.</LI><LI>Declare the required method in the behavior implementation class <STRONG>ZBP_RAP_TRAVEL_DATA</STRONG> using ADT Quick Fix <EM>Ctrl/Cmd + 1</EM>.<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_15-1765163757298.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349497iA2EF4505BD6D7EA2/image-size/medium?v=v2&px=400" role="button" title="Swathy_S_15-1765163757298.png" alt="Swathy_S_15-1765163757298.png" /></span> <span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_16-1765163757304.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349495i343AC65C8EF36FFF/image-size/medium?v=v2&px=400" role="button" title="Swathy_S_16-1765163757304.png" alt="Swathy_S_16-1765163757304.png" /></span><P> </P></LI><LI>Save and activate the changes in <STRONG>ZBP_R_RAP_TRAVEL_DATA</STRONG>.</LI><LI>Let's implement the determination <STRONG>generateAIItinerary</STRONG>. In your implementation class <STRONG>ZBP_R_RAP_TRAVEL_DATA</STRONG>, in <STRONG>generateAIItinerary</STRONG> method implementation.</LI><LI>Replace the following code. Your code should look like this:</LI></OL><pre class="lia-code-sample language-abap"><code> METHOD generateAIItinerary.
LOOP AT keys INTO DATA(ls_key).
READ ENTITIES OF zr_rap_travel_data IN LOCAL MODE
ENTITY ZrRapTravelData
FIELDS ( destination startdate enddate transportmode accommodationtype BudgetCurr ) WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
LOOP AT lt_travel INTO DATA(ls_travel).
DATA(days) = ls_travel-enddate - ls_travel-startdate + 1.
DATA(itinerary) = ``.
DATA(budget) = ``.
IF ls_travel-AiItinerary IS INITIAL OR ls_travel-AiBudgetBreakdown IS INITIAL.
TRY.
*Calling the Prompt Library API.Through the Prompt Library API, we can use predefined prompt templates to generate new prompts.
FINAL(sys_prompt_template_instance) = cl_aic_islm_prompt_tpl_factory=>get( )->create_instance(
islm_scenario = 'ZINTS_RAP120' template_id = 'SYSTEM_PROMPT_ITIN' ).
" Add a system prompt based on an ISLM prompt template with one parameter
FINAL(sys_prompt) = sys_prompt_template_instance->get_prompt( parameters = VALUE #( ( name = 'ISLM_days' value = days ) ) ).
" Add a user prompt based on an ISLM prompt template with parameters
FINAL(user_prompt_template_instance) = cl_aic_islm_prompt_tpl_factory=>get( )->create_instance(
islm_scenario = 'ZINTS_RAP120' template_id = 'USER_PROMPT_ITIN' ).
FINAL(user_prompt) = user_prompt_template_instance->get_prompt( parameters = VALUE #(
( name = 'ISLM_startdate'
value = |{ ls_travel-startdate }| )
( name = 'ISLM_enddate'
value = |{ ls_travel-enddate }| )
( name = 'ISLM_startingpoint'
value = ls_travel-StartingPoint )
( name = 'ISLM_destination'
value = ls_travel-Destination )
( name = 'ISLM_transportmode'
value = ls_travel-transportMode )
( name = 'ISLM_accommodationtype'
value = ls_travel-accommodationtype )
) ) .
FINAL(api_itin) = cl_aic_islm_compl_api_factory=>get( )->create_instance(
islm_scenario = 'ZINTS_RAP120' ).
*Set the LLM parameters
FINAL(params) = api_itin->get_parameter_setter( ).
params->set_maximum_tokens( 800 ).
FINAL(msg_itin) = api_itin->create_message_container( ).
msg_itin->set_system_role( sys_prompt ).
msg_itin->add_user_message( user_prompt ).
itinerary = api_itin->execute_for_messages( msg_itin )->get_completion( ).
CATCH cx_aic_api_factory INTO DATA(lx_api_itin).
itinerary = |AI instance creation failed for itinerary.|.
CATCH cx_aic_completion_api INTO DATA(lx_comp_itin).
itinerary = |AI completion call failed for itinerary.|.
ENDTRY.
IF itinerary IS NOT INITIAL.
TRY.
* To pass prompts directly without prompt parameters
* Your ISLM scenario for itinerary
DATA(system_prompt_budget) =
|You are a travel finance advisor. Based on the Itenary , destination, duration, | &&
|and total budget, estimate how much to allocate for travel, hotel, | &&
|food, sightseeing, and miscellaneous expenses. Output under 500 characters.|.
DATA(user_prompt_budget) =
|Itenerary: { itinerary }| &&
|Departure: { ls_travel-StartingPoint }| &&
|Destination: { ls_travel-destination }| &&
|\nTravel Dates: { ls_travel-startdate } to { ls_travel-enddate }| &&
|\nTotal Budget: ₹{ ls_travel-Budget }| &&
|\nTotal Budget Currency: ₹{ ls_travel-BudgetCurr }| &&
|\nProvide a short, realistic breakdown.|.
FINAL(api_budget) = cl_aic_islm_compl_api_factory=>get( )->create_instance(
islm_scenario = 'ZINTS_RAP120' ).
" Your ISLM scenario for budget
FINAL(msg_budget) = api_budget->create_message_container( ).
msg_budget->set_system_role( system_prompt_budget ).
msg_budget->add_user_message( user_prompt_budget ).
budget = api_budget->execute_for_messages( msg_budget )->get_completion( ).
CATCH cx_aic_api_factory INTO DATA(lx_api_budget).
budget = |AI instance creation failed for budget.|.
CATCH cx_aic_completion_api INTO DATA(lx_comp_budget).
budget = |AI completion call failed for budget.|.
ENDTRY.
ENDIF.
MODIFY ENTITIES OF zr_rap_travel_data IN LOCAL MODE
ENTITY ZrRapTravelData
UPDATE FIELDS ( aiitinerary aibudgetbreakdown status )
WITH VALUE #(
( %tky = ls_travel-%tky
aiitinerary = itinerary
aibudgetbreakdown = budget
status = 'Generated' )
).
ENDIF.
ENDLOOP.
ENDLOOP.
ENDMETHOD.</code></pre><OL><LI>Save and activate the changes in <EM>ZBP_R_RAP_TRAVEL_DATA</EM>.</LI><LI><SPAN>Preview and test the enhanced Travel Itinerary App with Generative AI Capabilities.</SPAN></LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_24-1765163757319.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349506iEC934CA5E9004E75/image-size/large?v=v2&px=999" role="button" title="Swathy_S_24-1765163757319.png" alt="Swathy_S_24-1765163757319.png" /></span></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_25-1765163757335.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349505iB8BA7F6EDA1D051A/image-size/large?v=v2&px=999" role="button" title="Swathy_S_25-1765163757335.png" alt="Swathy_S_25-1765163757335.png" /></span></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_26-1765163757351.png" style="width: 1004px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349504i369FC24D9F5E102C/image-dimensions/1004x545?v=v2" width="1004" height="545" role="button" title="Swathy_S_26-1765163757351.png" alt="Swathy_S_26-1765163757351.png" /></span></P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Swathy_S_27-1765163757357.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/349507iF78911A7655C1FE4/image-size/large?v=v2&px=999" role="button" title="Swathy_S_27-1765163757357.png" alt="Swathy_S_27-1765163757357.png" /></span></P><P>For the latest updates, subscribe to the blog posts below : </P><P><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/abap-ai-chapter-2/ba-p/14210568" target="_blank">ABAP AI - Chapter 2 - SAP Community</A><BR /><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/introducing-the-next-era-of-abap-development/ba-p/14260522" target="_blank">Introducing the Next Era of ABAP Development - SAP Community</A><BR /><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/joule-speaks-abap/ba-p/14018226" target="_blank">Joule speaks ABAP! - SAP Community</A><BR /><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/from-legacy-to-ai-powered-rise-transformation-revolution-with-joule-for/ba-p/14267448" target="_blank">From Legacy to AI-Powered: RISE Transformation Rev... - SAP Community</A></P><P>Happy Learning!<SPAN> </SPAN></P>2026-01-23T08:05:00.042000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/rap-draft-action-edit-contract-violation-failed-not-allowed-cl-dsp-act-comn/ba-p/14313575RAP Draft Action EDIT Contract Violation: Failed not allowed cl_dsp_act_comn_exec_add_impl - Solved2026-01-23T13:30:38.986000+01:00Timo_John1https://community.sap.com/t5/user/viewprofilepage/user-id/15452<P>Scenarion: Custom RAP unmanaged behavior implementation with draft - strict </P><pre class="lia-code-sample language-abap"><code> draft action Edit with additional implementation; // additional implementation to provide virtual elements </code></pre><P>In this Implementation of EDIT in the Z-behavior pool we checked if a delivery on this items exists, if so we raise a FAILED an a REPOTED Message <BR /><BR />Due to changes in the RAP Framework it looks like in the method EDIT with additional behavior, no FAILED is allowed anymore ( See comment below )<BR />The HTTP-Error is the result of a dump: <STRONG>“Contract Violation: Failed not allowed” </STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Timo_John1_4-1769170294015.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364578iFA2509860EC0BC20/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="Timo_John1_4-1769170294015.png" alt="Timo_John1_4-1769170294015.png" /></span></P><DIV class=""><DIV class=""><DIV class=""><DIV><DIV class=""><DIV class=""><DIV class=""><DIV><P>The issue is a change in the RAP framework that is not compatible with our current implementation <EM> Edit (with additional implementation)</EM>. During the <STRONG>EDIT</STRONG> operation, an error is detected because the tour has deliveries and the fields <STRONG>FAILED</STRONG> and <STRONG>REPORTED</STRONG> are filled.<BR />However, RAP no longer allows this.</P><P><SPAN>Cass<STRONG> cl_dsp_act_comn_exec_add_impl</STRONG> -> lcl_implementation -> Method <STRONG>lif_implementation~execute</STRONG> are these new lines</SPAN></P></DIV><DIV> </DIV><DIV><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Timo_John1_1-1769170021541.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364573i11252F8FE2C9C798/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="Timo_John1_1-1769170021541.png" alt="Timo_John1_1-1769170021541.png" /></span></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><P>These lines raise the short dump if there are failed messages: </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Timo_John1_2-1769170055719.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364574i1B496D6B91438929/image-size/medium/is-moderation-mode/true?v=v2&px=400" role="button" title="Timo_John1_2-1769170055719.png" alt="Timo_John1_2-1769170055719.png" /></span></P><H1 id="toc-hId-1659548802"><STRONG>Solution</STRONG></H1><P>Business Requirement is to do checks right before an entity will be put into edit mode, such that an draft for an active item is created. Using the provided RAP Feature Control is not an option, as:<BR />1. We do not want to check that upfront for all items of the list <BR />2. We wand to do the check in that moment a user requests the draft.</P><P><STRONG>We moved the check into the framework method LOCK, here we are allowed to raise FAILED an Reported. </STRONG><BR /><BR />I am not 100% sure that this is the best place for "Draft Pre-Checks" but I did not find any better. <BR />Pre-Checks are not possible for EDIT actions. </P><P><BR />Thanks go out to <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/37925">@Sschlegel</a> & Björn Schulz </P>2026-01-23T13:30:38.986000+01:00https://community.sap.com/t5/abap-blog-posts/why-document-like-it-s-1999/ba-p/14319106Why Document Like it's 19992026-01-31T19:19:27.708000+01:00Mattiashttps://community.sap.com/t5/user/viewprofilepage/user-id/11415<DIV class=""><H2 id="your-first-day-on-the-new-project" id="toc-hId-1788806207">Your First Day on the New Project</H2><P>You open the documentation repository. There's an Excel sheet titled “RICEFW<EM>Inventory</EM>v3<EM>FINAL</EM>2.xlsx”</P><P>You scan through:</P><UL><LI>Row 47: “Enhancement ZENH<EM>MAT</EM>001 – Material validation”</LI><LI>Row 89: “Report ZMM<EM>STOCK</EM>013 – Stock overview”<BR />Row 134: “Form ZSD<EM>ORDER</EM>PRINT – Order output”</LI><LI>Row 201: “Interface ZIF<EM>PRICING</EM>002 – Pricing calculation”</LI></UL><P>Your task: “Fix the credit check bug in sales order creation.”</P><P>You stare at the screen. <EM>Which row is that?</EM></P><P>You search for “credit”. Eight matches. Three are reports. Two are enhancements. One is a workflow you've never heard of.</P><P>You ask your colleague: “Where's the credit check logic?”</P><P>She sighs. “Check the validation BADI in ZFICO_CUST. Or is it the user exit? Actually, I think there's also a workflow step... let me check my notes from six months ago...”</P><P><STRONG>Three hours later, you still haven't found it.</STRONG></P><P>Sound familiar?</P><HR /><H2 id="part-1-the-problem-with-ricefw-documentation" id="toc-hId-1592292702">The Problem with RICEFW Documentation</H2><H3 id="ricefw-thinks-in-technical-artifacts" id="toc-hId-1524861916">RICEFW Thinks in Technical Artifacts</H3><P>RICEFW (Reports, Interfaces, Conversions, Enhancements, Forms, Workflows) was invented when SAP development meant: – Modifying a monolithic ECC system – Creating standalone reports – Adding enhancements to standard transactions – Building separate interfaces</P><P>Documentation followed the same pattern: categorize by <EM>technical artifact type</EM>.</P><P>This made sense in 1999.</P><H3 id="rap-thinks-in-business-capabilities" id="toc-hId-1328348411">RAP Thinks in Business Capabilities</H3><P>Fast-forward to 2026. You're building a Sales Order business object in RAP:</P><PRE><CODE>ZI_SALESORDER_M (Business Object)
├── Data Model (CDS views)
│ ├── ZI_SALESORDER_M (interface view)
│ └── ZC_SALESORDER_M (projection view)
├── Behavior Definition
│ ├── Validations (validateCustomer, validateCredit)
│ ├── Determinations (calculatePrice)
│ └── Actions (releaseOrder, cancelOrder)
├── Business Events
│ └── OrderCreated, OrderReleased
└── Service
└── ZUI_SALESORDER_O4 (OData V4)</CODE></PRE><P><STRONG>Everything about Sales Orders in one cohesive business object.</STRONG></P><P>Yet your documentation still says: – “Check Enhancement ZENHSD001” – “See Report ZSDORDERLIST” – “Refer to Form ZSDPRINTORDER”</P><P><STRONG>The disconnect is killing you.</STRONG></P><HR /><H2 id="part-2-what-rap-actually-gave-us" id="toc-hId-1002752187">What RAP Actually Gave Us - besides RAP itself</H2><P>Here's what's often missed in RAP discussions:</P><P><STRONG>RAP isn't just a new technical framework.</STRONG> It's SAP finally embracing domain-driven design principles that the rest of the software world has used for years.</P><UL><LI>Business objects that model business capabilities</LI><LI>Clean separation of concerns</LI><LI>Event-driven architecture</LI><LI>Clear service boundaries</LI></UL><P><STRONG>This is how modern software should be built.</STRONG></P><P>And when you build this way, your documentation can finally match how people hopefully think.</P><P>Not “Which enhancement modifies field X?”</P><P>But “How does the Sales Order capability work?”</P><P><STRONG>RAP enables the documentation style we should have been using all along.</STRONG></P><HR /><H2 id="part-3-three-documentation-layers-for-rap" id="toc-hId-806238682">Now the good stuff - Three Documentation Layers for RAP</H2><H3 id="layer-1-business-object-canvas" id="toc-hId-738807896">Layer 1: Business Object Canvas</H3><P><STRONG>The idea:</STRONG> One wiki page per RAP business object. Everything about that capability in one place.</P><P><STRONG>Here's what it actually looks like:</STRONG></P><HR /><P><STRONG>Business Object:</STRONG> Sales Order</P><P><STRONG>Business Capability:</STRONG> Complete sales order lifecycle from customer inquiry through order fulfillment</P><P><STRONG>Location in System:</STRONG> – Package: <CODE>ZRAP_SD_ORDER</CODE> – Root Entity: <CODE>ZI_SALESORDER_M</CODE> – Projection: <CODE>ZC_SALESORDER_M</CODE> – Service Binding: <CODE>ZUI_SALESORDER_O4</CODE> (OData V4, Published) – Fiori App: Sales Order Management – Main Transport: <CODE>ZTRK900123</CODE></P><P><STRONG>Team & Ownership:</STRONG> – Development Team: Retail Sales Team – Product Owner: Jane Smith – Technical Lead: John Doe – Last Major Update: 2025-01-27</P><HR /><P><STRONG>Behavior & Operations:</STRONG></P><P><EM>Standard Operations:</EM> – Create, Update, Delete (all enabled) – Draft handling: Enabled – ETag field: LastChangedAt</P><P><EM>Validations (run on save):</EM></P><UL><LI><CODE>validateCustomer</CODE> Checks customer exists and is not blocked</LI><LI><CODE>validateCreditLimit</CODE> – Verifies customer credit limit not exceeded</LI><LI><CODE>validateMaterial</CODE> – Ensures material exists and valid for sales</LI><LI><CODE>validateDeliveryDate</CODE> – Must be in the future</LI></UL><P><EM>Determinations (auto-calculations):</EM></P><UL><LI><CODE>calculateNetAmount</CODE> – Runs when quantity or price changes</LI><LI><CODE>setDefaultPaymentTerms</CODE> – Runs on create (from customer master)</LI><LI><CODE>determineDeliveryDate</CODE> – Calculates based on material availability</LI></UL><P><EM>Actions (user-triggered):</EM></P><UL><LI><CODE>releaseOrder</CODE> – Releases order to warehouse (publishes OrderReleased event)</LI><LI><CODE>cancelOrder</CODE> – Cancels entire order (publishes OrderCancelled event)</LI><LI><CODE>splitOrderItem</CODE> – Splits line item into multiple delivery schedules</LI></UL><HR /><P><STRONG>Events & Integration:</STRONG></P><P><EM>Events Published:</EM></P><UL><LI><CODE>OrderCreated</CODE> – When order activated from draft – Subscribers: Inventory BO (reserves stock), Analytics Service</LI><LI><CODE>OrderReleased</CODE> – When release action executed – Subscribers: Warehouse BO (starts picking), Billing BO (creates invoices</LI><LI><CODE>OrderCancelled</CODE> – When cancel action executed – Subscribers: Inventory BO (releases reservation)</LI></UL><P><EM>Events Subscribed:</EM></P><UL><LI><CODE>StockReserved</CODE> (from Inventory BO) – Updates order status to “Ready for Release”</LI><LI><CODE>StockShortage</CODE> (from Inventory BO) – Marks order as “Partially Allocated”</LI></UL><HR /><P><STRONG>Dependencies:</STRONG></P><P><EM>Upstream (What We Consume):</EM></P><P><STRONG>Material Master</STRONG> (I_Product) – Owner: Master Data Team – Access: Read-only via association – Fields used: Product, ProductType, BaseUnit, SalesText – Impact if unavailable: Order creation blocked (validation fails) – Contact: master-data-team@company.com</P><P><STRONG>Customer Master</STRONG> (I_Customer</P><UL><LI>Owner: Master Data Team</LI><LI>Access: Read-only via association</LI><LI>Fields used: Customer, CustomerName, CreditLimit, PaymentTerms</LI><LI>Impact if unavailable: Order creation blocked (validation fails)</LI><LI>Contact: master-data-team@company.com</LI></UL><P><STRONG>Pricing Engine</STRONG> (ZZPRICE<EM>ENGINE – Function Module) </EM></P><UL><LI><EM>Owner: Pricing Team – Access: Synchronous RFC call (wrapped in ZCL</EM>PRICING_ACL)</LI><LI>Called when: During determination (calculateNetAmount)</LI><LI>Timeout: 5 seconds</LI><LI>Impact if unavailable: Order creation blocked, error shown to user</LI><LI>Why synchronous: [See ADR-018]</LI><LI>Contact: pricing-team@company.com</LI></UL><P><EM>Downstream (Who Consumes Us):</EM> </P><UL><LI>Inventory BO (subscribes to OrderCreated, OrderCancelled)</LI><LI>Warehouse BO (subscribes to OrderReleased)</LI><LI>Billing BO (reads order data via association, subscribes to OrderReleased)</LI><LI>Analytics Dashboard (reads via OData for reporting)</LI><LI>Mobile Sales App (reads/writes via OData)</LI></UL><HR /><P><STRONG>Key Architecture Decisions:</STRONG> </P><UL><LI>[ADR-015] Why we chose managed RAP implementation </LI><LI>[ADR-018] Why pricing happens in determinations (not actions)</LI><LI>[ADR-023] How we handle external pricing service integration</LI><LI> [ADR-032] Why credit check is synchronous (blocks save)</LI></UL><P><STRONG>Related Documentation:</STRONG> </P><UL><LI>API Documentation: <A href="https://blog.nacka.io/link" target="_blank" rel="noopener nofollow noreferrer">OData Service Spec</A> </LI><LI>Process Documentation: <A href="https://blog.nacka.io/link" target="_blank" rel="noopener nofollow noreferrer">Order-to-Cash Process</A> <EM>(coming in Part 2 of this series)</EM> </LI><LI>Test Data: Customer 1000001, Material MAT-TEST-001</LI></UL><HR /><P><STRONG>That's the canvas.</STRONG> One page. ~5 minutes to scan. Everything about Sales Orders.</P><P>Compare this to hunting through a RICEFW spreadsheet with 200 rows.</P><H3 id="layer-2-architecture-decision-records-adrs" id="toc-hId-542294391">Layer 2: Architecture Decision Records (ADRs)</H3><P>The hard part isn't writing RAP code. It's deciding <EM>why</EM> to write it a certain way, and later remembering why that decision was taken.</P><P class=""><STRONG>The Industry Solution: Architecture Decision Records</STRONG></P><P class="">This problem isn't unique to SAP or RAP. It's why organizations like AWS and IBM actively recommend using Architecture Decision Records (ADRs) to capture the reasoning behind technical decisions.</P><P class="">AWS Prescriptive Guidance recommends using architectural decision records to streamline technical decision-making for a software development project. <SPAN class=""><A class="" href="https://adr.github.io/" target="_blank" rel="noopener nofollow noreferrer"><SPAN class=""><SPAN class="">Architectural Decision Records</SPAN></SPAN></A></SPAN> IBM has documented their use of ADRs in production systems, showing how they help teams maintain context over time.</P><P class="">The principle is simple: when you make a significant technical decision, document why you made it, what alternatives you considered, and what consequences you accepted.</P><P class="">Six months later, when someone asks "Why?", you point them to the ADR.</P><H3 id="toc-hId-345780886"><STRONG>Example ADR:</STRONG></H3><HR /><P><STRONG>ADR-018: Calculate Pricing in Determinations (Not Actions)</STRONG></P><P><EM>Date:</EM> 2025-01-15<BR /><EM>Status:</EM> <span class="lia-unicode-emoji" title=":white_heavy_check_mark:">✅</span> Accepted<BR /><EM>Deciders:</EM> Tech Lead, Solution Architect, Senior Developer</P><P><STRONG>Context:</STRONG></P><P>We need to calculate item pricing in Sales Order BO. Two options:</P><P><STRONG>Option A: Determination (automatic)</STRONG> – Runs automatically during save sequence – User sees price immediately when quantity changes – Can't be skipped</P><P><STRONG>Option B: Action (manual)</STRONG> – User must click “Calculate Price” button – More control over when calculation happens – Can be skipped if needed</P><P>Current situation: – Pricing engine (ZZPRICE_ENGINE) is legacy FM, ~2 second response time – Business requires instant price feedback for sales reps – Complex pricing rules (discounts, surcharges, customer agreements)</P><P><STRONG>Decision:</STRONG></P><P>Use <STRONG>determination</STRONG> to calculate pricing automatically on quantity/material changes.</P><P><STRONG>Rationale:</STRONG></P><P><EM>Why determination:</EM></P><UL><LI>Sales reps need instant price visibility</LI><LI>Prevents orders being saved with wrong/missing prices</LI><LI>Aligns with UX requirement: “price updates as you type”</LI><LI>Consistent with standard SAP behavior</LI></UL><P><EM>Why not action:</EM> </P><UL><LI>Extra click burdens sales reps (average 50 orders/day/rep)</LI><LI>Risk of forgetting to calculate = wrong invoices</LI><LI>User testing showed confusion with manual pricing button</LI></UL><P><EM>Risks accepted:</EM> </P><UL><LI>2-second save time (acceptable per UX testing)</LI><LI>Pricing service must be highly available (99.9% SLA)</LI></UL><P><STRONG>Consequences:</STRONG></P><P><EM>Positive:</EM> </P><UL><LI>Instant price feedback for sales reps</LI><LI>No risk of missing price calculation</LI><LI>Reduced training time (automatic behavior)</LI><LI>Consistent UX across all sales channels</LI></UL><P><EM>Negative:</EM> </P><UL><LI>Every save calls pricing engine (load concern)</LI><LI>Can't skip pricing for “draft” scenarios</LI><LI>Pricing service downtime blocks all orders</LI></UL><P><EM>Mitigations:</EM> </P><UL><LI>Pricing service has 5-second timeout (fail-fast)</LI><LI>Fallback: save order with “Price Pending” flag, batch re-price later</LI><LI>Pricing team committed to 99.9% SLA</LI><LI>Monitoring alert if pricing calls >3 seconds</LI></UL><P><STRONG>Implementation Notes:</STRONG></P><PRE><CODE>determination calculateNetAmount on modify {
field Quantity;
field Material;
field Customer;
}</CODE></PRE><P>In behavior implementation: – Call ZCL<EM>PRICING</EM>ACL wrapper (not direct FM call) – Wrapper implements circuit breaker pattern – Logs all pricing calls for debugging</P><P><STRONG>Related:</STRONG> – [ADR-023] Anti-corruption layer for pricing service – [ADR-040] Pricing service timeout handling</P><HR /><P><STRONG>The principle:</STRONG> Six months from now, when someone asks “Why does save take 2 seconds?”, you point to ADR-018. Everything is documented.</P><P><STRONG>The principle:</STRONG> When Master Data announces “We're changing Material descriptions from 40 to 80 characters,” you know instantly that 12 BOs need coordination.</P><HR /><H2 id="part-4-why-this-works" id="toc-hId-20184662">Part 4: Why This Works</H2>RICEFW Docs BO Canvas Approach <TABLE><TBODY><TR><TD>ZTRK900123 – Enhancement for credit check</TD><TD>Sales Order BO → validateCreditLimit</TD></TR><TR><TD>5 spreadsheet tabs</TD><TD>1 wiki page</TD></TR><TR><TD>Technical artifact types</TD><TD>Business capabilities</TD></TR><TR><TD>“Why?” is in meeting notes</TD><TD>“Why?” is in ADRs</TD></TR><TR><TD>Dependencies invisible</TD><TD>Dependency matrix</TD></TR><TR><TD>3 hours to find credit check</TD><TD>3 minutes to find credit check</TD></TR></TBODY></TABLE><P><STRONG>Real impact:</STRONG></P><P><EM>Onboarding time:</EM> </P><UL><LI>RICEFW: weeks to understand one domain</LI><LI>BO Canvas: days to understand one domain</LI></UL><P><EM>Incident response:</EM> </P><UL><LI>RICEFW: “Where's the logic?” → hours–</LI><LI>BO Canvas: “Check Sales Order BO canvas” → minutes</LI></UL><P><EM>Impact analysis:</EM> </P><UL><LI>RICEFW: “Who uses Material Master?” → “¯\<EM>(ツ)</EM>/¯”</LI><LI>BO Canvas: “Check dependency matrix” → 12 BOs listed</LI></UL><HR /><H2 id="toc-hId-170925514">Part 5: What This Doesn't Solve (Yet)</H2><P>You've documented your Sales Order BO beautifully. Dependencies are clear. Decisions are captured.</P><P>Then someone asks: “Walk me through what happens when a customer creates an order. Step by step. Including credit check, stock reservation, pricing, and warehouse handoff.”</P><P>You point them to four BO Canvases:</P><UL><LI>Sales Order BO</LI><LI>Credit Check BO</LI><LI>Inventory BO<BR />Shipping BO</LI></UL><P>They read all four. They check dependencies. They still don't understand the <EM>sequence</EM> of events , what happens if credit check fails etc.</P><P>Business Object Canvases document capabilities, ADRs document decisions, Dependency matrices document connections.</P><P>But none of them document the actual business process flow, ant that's why that's the topic for the next post in this series. </P><HR /><P><STRONG>Next in this series:</STRONG> <EM>Part 2: Documenting Business Processes – From BOs to Flows</EM></P><P><EM>What's your biggest documentation challenge? Drop a comment below.</EM></P></DIV><P> </P>2026-01-31T19:19:27.708000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/updating-records-using-dialog-box-in-rap/ba-p/14318971Updating records using Dialog Box in RAP2026-02-03T07:30:32.746000+01:00Faizan_khan1https://community.sap.com/t5/user/viewprofilepage/user-id/1451076<P><SPAN><STRONG><SPAN class=""><SPAN class="">Description :</SPAN></SPAN></STRONG> In this blog, we implement a RAP-based solution to </SPAN><STRONG><SPAN>update existing records using a Dialog Box (popup)</SPAN></STRONG><SPAN> instead of navigating to a separate page.</SPAN><SPAN> </SPAN></P><P><SPAN>In this scenario user need to update the records in list page itself instead of going of going to object page using a dialog box in RAP application</SPAN></P><P><SPAN><STRONG><SPAN class=""><SPAN class="">Step </SPAN><SPAN class="">1 :</SPAN><SPAN class=""> Create a Data base table </SPAN></SPAN></STRONG><SPAN class=""> </SPAN></SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'DB table for Numbering'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zfa_dt_num {
key managed_num : sysuuid_x16 not null;
key unmanged_id : char10 not null;
name : char10;
age : char2;
attendance_status : char10;
lastchangedat : abp_locinst_lastchange_tstmpl;
} </code></pre><P> </P><P><STRONG><SPAN class=""><SPAN class="">Step</SPAN><SPAN class="">2 :</SPAN><SPAN class=""> create </SPAN><SPAN class="">a</SPAN><SPAN class=""> interface view and projection view upon data base </SPAN></SPAN><SPAN class=""> </SPAN></STRONG></P><P><STRONG><SPAN class="">Interface View</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'interface view for numbering'
@Metadata.ignorePropagatedAnnotations: true
define root view entity zi_zfa_num as select from zfa_dt_num
{
key managed_num as ManagedNum,
key unmanged_id as UnmangedId,
name as Name,
age as Age,
attendance_status as AttendanceStatus,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
lastchangedat as LastChangeAt
} </code></pre><P><STRONG>Projection View</STRONG></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projection view num'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define root view entity zc_zfa_num
as projection on zi_zfa_num
{
key ManagedNum,
key UnmangedId,
Name,
Age,
AttendanceStatus,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
LastChangeAt </code></pre><P><STRONG><SPAN class=""><SPAN class="">Step</SPAN><SPAN class="">3 :</SPAN><SPAN class=""> create a behavior definition for both interface view and projection view </SPAN></SPAN><SPAN class=""> </SPAN></STRONG></P><P><STRONG><SPAN class="">Interface View Behavior definition </SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_zfa_num unique;
strict ( 2 );
define behavior for zi_zfa_num //alias <alias_name>
persistent table zfa_dt_num
lock master
authorization master ( instance )
etag master LastChangeAt
late numbering
{
action Updateatt parameter za_zfa_num result[1] $self;
create ( authorization : global );
update;
delete;
field ( readonly ) ManagedNum , UnmangedId;
mapping for zfa_dt_num{
ManagedNum = managed_num;
UnmangedId = unmanged_id;
Name = name;
Age = age;
AttendanceStatus = attendance_status;
LastChangeAt = lastchangedat;
}
} </code></pre><P><STRONG>Projection view Behavior defination</STRONG></P><pre class="lia-code-sample language-abap"><code>projection;
strict ( 2 );
define behavior for zc_zfa_num //alias <alias_name>
use etag
{
use create;
use delete;
use action Updateatt;
} </code></pre><P><SPAN class=""><STRONG><SPAN class="">Step</SPAN><SPAN class="">4 :</SPAN><SPAN class=""> implement the </SPAN><SPAN class="">behavior</SPAN></STRONG><SPAN class=""><STRONG> pool class</STRONG> </SPAN></SPAN><SPAN class=""> </SPAN></P><pre class="lia-code-sample language-abap"><code>CLASS lhc_zi_zfa_num DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR zi_zfa_num RESULT result.
METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION
IMPORTING REQUEST requested_authorizations FOR zi_zfa_num RESULT result.
METHODS updateatt FOR MODIFY
IMPORTING keys FOR ACTION zi_zfa_num~updateatt RESULT result.
ENDCLASS.
CLASS lhc_zi_zfa_num IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
METHOD get_global_authorizations.
ENDMETHOD.
METHOD Updateatt.
data(lt_keys) = keys.
read eNTITIES OF zi_zfa_num in LOCAL MODE
ENTITY zi_zfa_num
fiELDS ( AttendanceStatus Name Age ) wiTH corRESPONDING #( keys )
reSULT data(lt_res).
data(lv_new_status) = lt_keys[ 1 ]-%param-attendance_status.
data(lv_new_Name) = lt_keys[ 1 ]-%param-name.
data(lv_new_age) = lt_keys[ 1 ]-%param-age.
modIFY enTITIES OF zi_zfa_num in LOCAL MODE
entity zi_zfa_num
uPDATE fiELDS ( AttendanceStatus Name Age )
wITH vaLUE #( (
%tky = lt_res[ 1 ]-%tky
AttendanceStatus = lv_new_status
Name = lv_new_name
age = lv_new_age
) ).
read eNTITIES OF zi_zfa_num in LOCAL MODE
ENTITY zi_zfa_num
all fiELDS wiTH corRESPONDING #( keys )
reSULT data(lt_res1).
result = value #(
for <lfs_num> in lt_res1 (
%tky = <lfs_num>-%tky
%param = <lfs_num>
)
).
ENDMETHOD.
ENDCLASS.
CLASS lsc_ZI_ZFA_NUM DEFINITION INHERITING FROM cl_abap_behavior_saver.
PROTECTED SECTION.
METHODS adjust_numbers REDEFINITION.
METHODS cleanup_finalize REDEFINITION.
ENDCLASS.
CLASS lsc_ZI_ZFA_NUM IMPLEMENTATION.
METHOD adjust_numbers.
DATA(lt_managedid) = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ).
SELECT MAX( unmanged_id ) FROM zfa_dt_num INTO (lv_num).
IF sy-subrc = 0.
lv_num += 1 .
ELSE.
lv_num = 000000001 .
ENDIF.
LOOP AT mapped-zi_zfa_num REFERENCE INTO DATA(ls_num).
ls_num->ManagedNum = lt_managedid.
ls_num->UnmangedId = lv_num.
ENDLOOP.
ENDMETHOD.
METHOD cleanup_finalize.
ENDMETHOD.
ENDCLASS. </code></pre><P><STRONG><SPAN class=""><SPAN class="">Step </SPAN><SPAN class="">5 :</SPAN><SPAN class=""> For achieving the Dialog box we </SPAN><SPAN class="">have to</SPAN><SPAN class=""> make use of </SPAN><SPAN class="">a</SPAN><SPAN class=""> a</SPAN><SPAN class="">bstract </SPAN><SPAN class="">entity ,</SPAN><SPAN class=""> in this abstract entity declare the fields which you </SPAN><SPAN class="">have to</SPAN><SPAN class=""> update </SPAN></SPAN></STRONG><SPAN class=""> </SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Abstract entity for numbering'
@Metadata.allowExtensions: true
define abstract entity za_zfa_num
// with parameters parameter_name : parameter_type
{
name : char10;
age : char2;
attendance_status : char10;
} </code></pre><P><STRONG><SPAN class="">Step </SPAN><SPAN class="">6 :</SPAN><SPAN class=""> create a </SPAN><SPAN class="">metadata</SPAN><SPAN class=""> for this abstract entity</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE
annotate entity za_zfa_num with
{
@EndUserText.label: ' Update Name'
name;
@EndUserText.label: ' Update Age'
age;
@EndUserText.label: 'Set Student Status'
attendance_status;
} </code></pre><P><STRONG><SPAN class=""><SPAN class="">Step</SPAN><SPAN class="">7 :</SPAN><SPAN class=""> now for displaying the popup we </SPAN><SPAN class="">have to</SPAN><SPAN class=""> make use of custom </SPAN><SPAN class="">actions ,</SPAN><SPAN class=""> declare </SPAN><SPAN class="">a</SPAN><SPAN class=""> actions with </SPAN><SPAN class="">parameters ,</SPAN><SPAN class=""> in parameter give the abstract ent</SPAN><SPAN class="">ity name </SPAN></SPAN><SPAN class=""> </SPAN></STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faizan_khan1_0-1769852117176.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367577iA2AB9F15AE056BD3/image-size/large?v=v2&px=999" role="button" title="Faizan_khan1_0-1769852117176.png" alt="Faizan_khan1_0-1769852117176.png" /></span></P><P><SPAN class=""><STRONG><SPAN class="">Step</SPAN><SPAN class="">8 :</SPAN><SPAN class=""> now implement the logic for updating the records in </SPAN></STRONG><SPAN class=""><STRONG>action method</STRONG> </SPAN></SPAN><SPAN class=""> </SPAN></P><pre class="lia-code-sample language-abap"><code>METHOD Updateatt.
data(lt_keys) = keys.
read eNTITIES OF zi_zfa_num in LOCAL MODE
ENTITY zi_zfa_num
fiELDS ( AttendanceStatus Name Age ) wiTH corRESPONDING #( keys )
reSULT data(lt_res).
data(lv_new_status) = lt_keys[ 1 ]-%param-attendance_status.
data(lv_new_Name) = lt_keys[ 1 ]-%param-name.
data(lv_new_age) = lt_keys[ 1 ]-%param-age.
modIFY enTITIES OF zi_zfa_num in LOCAL MODE
entity zi_zfa_num
uPDATE fiELDS ( AttendanceStatus Name Age )
wITH vaLUE #( (
%tky = lt_res[ 1 ]-%tky
AttendanceStatus = lv_new_status
Name = lv_new_name
age = lv_new_age
) ).
read eNTITIES OF zi_zfa_num in LOCAL MODE
ENTITY zi_zfa_num
all fiELDS wiTH corRESPONDING #( keys )
reSULT data(lt_res1).
result = value #(
for <lfs_num> in lt_res1 (
%tky = <lfs_num>-%tky
%param = <lfs_num>
)
).
ENDMETHOD. </code></pre><P><STRONG><SPAN class="">Step</SPAN><SPAN class="">9 :</SPAN><SPAN class=""> Create a service definition and service binding for the projection view and expose it</SPAN></STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faizan_khan1_0-1769853368459.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367578i1E6FFFB90706F0CA/image-size/large?v=v2&px=999" role="button" title="Faizan_khan1_0-1769853368459.png" alt="Faizan_khan1_0-1769853368459.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faizan_khan1_1-1769853394588.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367579i7319517636A157FA/image-size/large?v=v2&px=999" role="button" title="Faizan_khan1_1-1769853394588.png" alt="Faizan_khan1_1-1769853394588.png" /></span></P><P><SPAN class=""><SPAN class="">Here we can see we have 2 records and a button is visible UPDATE RECORDS </SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faizan_khan1_2-1769853423270.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367580i2DDDD1CCB2B45149/image-size/large?v=v2&px=999" role="button" title="Faizan_khan1_2-1769853423270.png" alt="Faizan_khan1_2-1769853423270.png" /></span></P><P><SPAN class=""><SPAN class="">Select the button and click on the update record action </SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faizan_khan1_3-1769853564349.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367581i79AE18A415BA14B9/image-size/large?v=v2&px=999" role="button" title="Faizan_khan1_3-1769853564349.png" alt="Faizan_khan1_3-1769853564349.png" /></span></P><P><SPAN class=""><SPAN class="">Edit the records </SPAN></SPAN><SPAN class=""> </SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faizan_khan1_4-1769853601785.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367582i36248796BB6508E9/image-size/medium?v=v2&px=400" role="button" title="Faizan_khan1_4-1769853601785.png" alt="Faizan_khan1_4-1769853601785.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faizan_khan1_5-1769853632182.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367583i778A21EFAF923424/image-size/large?v=v2&px=999" role="button" title="Faizan_khan1_5-1769853632182.png" alt="Faizan_khan1_5-1769853632182.png" /></span></P><P><SPAN>Here you can see the records have been updated </SPAN><SPAN> </SPAN></P><P><SPAN>Conclusion : Using a Dialog Box in RAP makes record updates faster and more user-friendly by keeping the user on the same screen while still following RAP’s behavior, validations, and transactional processing</SPAN><SPAN> </SPAN></P><P> </P><P> </P>2026-02-03T07:30:32.746000+01:00https://community.sap.com/t5/technology-blog-posts-by-sap/tweaks-for-rap-bo-events-addressing-filtering-before-send-and-too-small/ba-p/14320767Tweaks for RAP BO events addressing filtering (before send) and too small payload (callbacks needed)2026-02-03T15:18:52.529000+01:00mlauberhttps://community.sap.com/t5/user/viewprofilepage/user-id/157846<P>Are you working on modernizing your SAP integrations, or looking for better ways to integrate with clean core compliance? But when you've looked at "Event Objects" from the SAP Business Accelerator Hub, you have found that it's not exactly matching your needs? Too many events are fired (can I filter?). The callback API is too time-consuming/unwanted. Or maybe you are simply interested in how ABAP RESTful Programming Model (RAP) Events work, how you can use and/or extend SAP Standard events?</P><P>In this blog I'll go through first some basic info and then start to showcase some tips / tweaks / workarounds you may or may not be aware about. Let's go.</P><H1 id="toc-hId-1660384843">1 ABAP RESTful Programming Model (RAP) Events Intro</H1><P>I'll keep this very brief as the meat of this blog is to go into the nitty-gritty details. But, for the uninitiated, RAP BO events are events that have been built with the RAP framework which can easily integrate with event brokers, such as SAP Business Technology Platform's Event Mesh or Advanced Event Mesh. They are a sort of modern version of BTEs. You may refer to a previous blog of mine where I go into one full end-to-end example, showcasing how to find SAP standard events and then use them: <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/working-with-rap-bo-events-end-to-end-event-driven-architecture-event/ba-p/14018775" target="_blank">Working with RAP BO Events end-to-end</A> . The short version is that all events can be found on the <A href="https://hub.sap.com/" target="_blank" rel="noopener noreferrer">SAP Business Accelerator Hub</A> -> Your SAP Product -> Events.</P><H1 id="toc-hId-1463871338">2 Too many events are fired / I want to filter</H1><P>This is probably one of the most common discussion points when I work with my customers. More often than not a "Created" or even more interestingly, a "Changed" event is simply fired too often for the actual integration needs. So what can we do here? In essence we have 3 main options:</P><OL><LI><STRONG>Integration middleware handles filtering</STRONG> (= no changes in SAP system, use event as is)<OL><LI>This can be desirable if there are lots of receiving systems that need to receive information for a certain object, and maybe each of them would like to filter a little different. The middleware receives the event as it happens and can then handle the distribution accordingly. This way 1 single event could be the foundation of several functioning integrations (instead of several point-to-point integrations). The quote-on-quote downside to this is that you would of course increase the complexity in the middleware itself, but again if the same event benefits several receivers, this could be a good option.<BR /><BR /></LI></OL></LI><LI><STRONG>Extend SAP standard event</STRONG> to include filtering fields (= extension development in your SAP system, use extended event, not standard)<OL><LI>This can be used if you know for sure you always want to filter out certain types of objects, that none of the integrated systems would want to be informed about. The quote-on-quote downside here is that we need to create an extension. Secondly, we first have to check if the SAP Standard RAP BO in question even allows behavior extensions. If not, we cannot use this option at all.<BR /><BR /></LI></OL></LI><LI><STRONG>Use custom event in combination with SAP Standard event</STRONG> as a workaround (= custom development in your SAP system, use custom event, not standard)<OL><LI>Same as for the previous one, this option can be used if we know for a fact we always want to filter away certain objects. We can use this over the extend option, if for example the extension was not possible, or if the extension possibilities do not suffice (for example we need to add fields that are not easily associated in the RAP BO, or we need some complex logic and checking before we send an event)<BR /><BR /></LI></OL></LI></OL><P>As this blog focuses on RAP, I'll skip the first option and let the integration gurus tackle that one. Let's take a closer look at the other two...</P><H2 id="toc-hId-1396440552">2.1 Extend SAP standard event</H2><P>If we found a suitable SAP standard RAP BO event and all we want is to add a few filter fields, we can do that with what is called Derived Events. Please refer to this page for details: <A href="https://help.sap.com/docs/abap-cloud/abap-rap/derived-business-events?version=s4hana_cloud" target="_blank" rel="noopener noreferrer">Derived Business Events | SAP Help Portal</A></P><P>For my showcase I'll be using <STRONG>R_SalesOrderTP~Changed</STRONG> event. First of, we need to check if the behavior definition of the BO allows extensions. Open it and check for the word "<FONT color="#FF6600"><STRONG>extensible</STRONG></FONT>" somewhere at the beginning of the definition + on the entity which has the event (so we need <U><STRONG>two</STRONG></U> <FONT color="#FF6600"><STRONG>extensible</STRONG></FONT>). </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_0-1770118376066.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368508i197CA85BDC2C2AC5/image-size/large?v=v2&px=999" role="button" title="mlauber_0-1770118376066.png" alt="mlauber_0-1770118376066.png" /></span></P><P>If it's there, we can proceed. Next we can also check the event itself and the payload (if no <FONT color="#FF6600"><STRONG>parameter</STRONG> </FONT>statement is written there, the payload is the key field(s) of the BO alone).</P><H3 id="toc-hId-1329009766">2.1.1 Create derived event payload with filter fields</H3><P>Before we extend the behavior, we need a custom payload that includes our filter fields. Create a new data definition root entity and define your custom payload. For example:</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Ord. ext. event for derived event'
define root view entity ZP_SalesOrder_EventExt as select from I_SalesOrder
{
key SalesOrder,
.context.attribute: 'xsapordtype'
SalesOrderType,
.context.attribute: 'xsapslsorg'
SalesOrganization,
.context.attribute: 'xsapbillplc'
_BillingPlan.BillingPlanCategory,
DistributionChannel,
OrganizationDivision,
SoldToParty,
CustomerGroup,
ShippingCondition,
IncotermsClassification,
OverallSDProcessStatus
}</code></pre><P>Few points:</P><UL><LI>Key fields must match the RAP BO you are extending</LI><LI>You can access fields just like the standard RAP BO and also associated fields (I showcase this with BillingPlanCategory above). Note, we focus only flat structures, so the associated field needs to be 1:1 (for deep structures, please refer to chapter 3).</LI><LI>With above, we created exactly 3 filter fields: Sales Order Type, Sales Organization, and Billing Plan Category. Filters must have the annotation "<STRONG>Event.context.attribute</STRONG>" and <U>must start with</U> "<STRONG>xsap</STRONG>"!</LI></UL><H3 id="toc-hId-1132496261">2.1.2 Extend standard RAP BO behavior to create a derived event (with our custom payload)</H3><P>Now we are ready to extend the behavior definition as per usual practice: find/display it in the project explorer - right click - New Behavior Extension. Here type out your derived event, for example:</P><pre class="lia-code-sample language-abap"><code>extension
//using interface i_salesordertp
;
extend behavior for SalesOrder {
managed event ZZChangedExtended on Changed parameter ZP_SalesOrder_EventExt;
}</code></pre><P>Few notes on the above:</P><UL><LI>First of I want to start with the <STRONG>interface</STRONG> commented away above. Bear with me, this is important to understand!<BR />In RAP it is always best to interact with RAP BOs via their interface, if there is one (not to be confused with a basic interface view, such as I_SalesOrder or I_Customer etc., which reads from the DB table). In the case of R_SalesOrderTP, it has a transactional interface called <STRONG>I_SalesOrderTP</STRONG>. This interface is meant as a stable "API" to interact with the BO, and is usually released with C1/C0 contracts for clean core compliance. So in truth, we should be using the interface as we are extending this BO, but as you see above, it's commented away. That is because we must, in this case. I_SalesOrderTP does not include any of the events, and because we need those, we cannot use the interface in this case. What this means is that my extension behavior definition, as of right now, must be saved as "<STRONG>Standard ABAP</STRONG>" and cannot be assigned to a package with an ABAP Cloud software component. HOWEVER, <U><STRONG>this is clean core compliant</STRONG></U>, because R_SalesOrderTP is released with a C0 contract for extension, thus allowing such extensions. The problem is that the syntax check for <STRONG>ABAP for Cloud Development</STRONG> only checks for C1 contracts (which is missing on R_SalesOrderTP behavior), which is why, as of right now, we have to save this as Standard ABAP, meaning a "level B" extension which is clean core compliant (it would have been "level C", if R_SalesOrderTP hadn't had the C0).</LI><LI>The rest is quite straight forward. We say which entity we are extending (<STRONG>SalesOrder</STRONG>, using the alias in the behavior definition) and we are stating the <STRONG>Changed</STRONG> event to be extended with <STRONG>ZZChangedExtended</STRONG> with our own payload we previously created.</LI></UL><H3 id="toc-hId-935982756">2.1.3 Create Event Binding for derived event</H3><P>Next up we need an Event Binding for our derived event. As a small side note: if you only intend to consume your own derived within your SAP system (the same system) and not send it out to an event broker, then you would not need an event binding.<BR />Create a new Event Binding object (Business Service - Event Binding or just search for event binding in the new ABAP object dialog) and add the event we just created to it:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_1-1770120045083.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368517iADDE8D17051D9C5B/image-size/large?v=v2&px=999" role="button" title="mlauber_1-1770120045083.png" alt="mlauber_1-1770120045083.png" /></span></P><P>Note that we of course refer to the SAP Standard RAP BO, which now includes our derived event thanks to the extension we just did.</P><H3 id="toc-hId-739469251">2.1.4 Configure Event Channel to include your derived event</H3><P>Lastly, to send our derived event to an event broker, we need to add it to our event channel. Assuming you already prepared an event channel (refer to my other blog linked above for more info on that), go to <STRONG>/n/IWXBE/CONFIG</STRONG>, select your event channel and go to <STRONG>Outbound Bindings</STRONG>.</P><P>Click the create button to add the event binding you just created. It could look something like this:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_2-1770120572456.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368521i9EAB0A3A0D0D0D9B/image-size/large?v=v2&px=999" role="button" title="mlauber_2-1770120572456.png" alt="mlauber_2-1770120572456.png" /></span></P><P>The above means that my Event Mesh on my BTP subaccount receives the SAP standard Sales Order Created event, and my own derived Sales Order Changed event.</P><P>And that's it: now we are ready for filtering or for simply getting the larger payload as per our derived event.</P><H3 id="toc-hId-542955746">2.1.5 Adding Filters</H3><P>In the same location as we were (/n/IWXBE/CONFIG -> Event Channel -> Outbound Bindings) click on the button <STRONG>Filters</STRONG> at the top. Open up the folder <STRONG>Outbound Bindings</STRONG> for your event channel and click on your derived event. Clicking on the plus-button shows all available filter fields, in this example:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_3-1770120808327.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368528iFC46CE4591114DC4/image-size/medium?v=v2&px=400" role="button" title="mlauber_3-1770120808327.png" alt="mlauber_3-1770120808327.png" /></span></P><P>Create a filter, for example that only Sales Order Type "OR" should be sent to out:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_4-1770120857823.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368529i86EE30B213E86B56/image-size/large?v=v2&px=999" role="button" title="mlauber_4-1770120857823.png" alt="mlauber_4-1770120857823.png" /></span></P><H3 id="toc-hId-346442241">2.1.6 Subscribe to the event on your event broker</H3><P>To receive this derived event for example in Event Mesh on SAP BTP, you need of course a corresponding queue and then add the subscription for this new derived event. In my example:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_5-1770121089544.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368531i011AEB159CE6B0E4/image-size/medium?v=v2&px=400" role="button" title="mlauber_5-1770121089544.png" alt="mlauber_5-1770121089544.png" /></span></P><H3 id="toc-hId-149928736">2.1.6 Test example</H3><P>I changed a sales order in my system and saved. This now triggered my derived changed event, which I can see in the events monitoring transaction <STRONG>/n/IWXBE/EVENT_MONITOR</STRONG> (select event channel, click on Outbound Events, double-click on derived event)...</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="img1.png" style="width: 321px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368878i3A7336D1CA08EA56/image-size/medium?v=v2&px=400" role="button" title="img1.png" alt="img1.png" /></span></P><P>...and on SAP BTP Event Mesh where I added the subscription:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_7-1770121288238.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368533i6721A20876B09CD9/image-size/medium?v=v2&px=400" role="button" title="mlauber_7-1770121288238.png" alt="mlauber_7-1770121288238.png" /></span></P><P>Here we can only see what's sitting in the queue. So let me consume the message in the test tool to show the same payload as in our monitor:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="img2.png" style="width: 789px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368882i1C15646C036E5838/image-size/large?v=v2&px=999" role="button" title="img2.png" alt="img2.png" /></span></P><P>Awesome! We succeeded and this concludes the extend event option.</P><H2 id="toc-hId-171586869">2.2 Use custom event in combination with SAP Standard event</H2><P>This is an interesting one and in my experience comes with some confusion attached to it. I have heard sentences such as "But can't we just create a custom event instead of the standard one and use that?" And the simple answer to that is yes, but no <span class="lia-unicode-emoji" title=":smiling_face_with_smiling_eyes:">😊</span>. Let me explain...</P><P>Now of course if we create our own, custom RAP BOs, we can add events to them. That is clear. And within that custom BO you decide when that event is fired. Clear and no problem. But this is all custom.</P><P>And now, if you want "your custom event" <STRONG>to be trigged <U>during</U> SAP standard processing</STRONG>, well no, SAP standard can't do that, as it does not know your custom event exists. And if you are thinking to use user exits or BAdIs to do this; think again, because it would be wrong to do so. User exits and BAdIs are called while the processing has not yet completed. Meaning at this point, in theory, something could still go wrong (no update in DB), but you already triggered your event. So, do not do this, ever! The point of the event framework is to inform about a state change. So you only want to do that, if the state change has actually occurred, meaning after the DB is fully updated.</P><P>So what else then, you wonder? That's where the SAP standard event comes in (which yes, is fired AFTER the state change occurred and all processing has completed). The SAP Standard event <U>will trigger</U>, and thus if you want more flexibility than just explored in the previous option with the extension of the SAP standard event, that's when we can try to combine standard with custom event.</P><P>Essentially, this is the pattern:</P><OL><LI>SAP standard process does something, which triggers an event for a certain object. This triggering happens at the exact time you want, but the SAP Standard event does not fulfill your requirement (and we can't / don't want to do a derived event)</LI><LI>You will create a custom event on a "dummy" RAP BO which includes the exact payload you need (flat structure for EM, it can be a deep one for AEM).</LI><LI>You will create a local event listener (within your SAP system) for the SAP standard event that triggers at the right moment. Within this local consumption you can now do any ABAP logic you may need. Complex filtering. Complex data collection (calling BAPIs or other things) etc.<BR />Once all your data validation and collection is done, you fill the payload format of your custom event and finally you trigger your custom event (with a slight workaround, as events can only be raised from with the RAP BO itself)</LI><LI>You only configure your custom event in the event channel for the event broker. The event arriving there is ready to be consumed with all needed fields and only events are that are really expected are arriving there.</LI></OL><P>Let's do this example now. We stick with <STRONG>R_SalesOrderTP</STRONG>, this time using <STRONG>Created</STRONG> as the SAP Standard event.</P><P>Let's assume I don't just want to send all sales order that are created. Sales order type filtering is not good enough. In fact, it matters whether a product is sold that has certain characteristics, for example. As a simplified example here, I will only check the product type of the product, but hopefully this gives you some ideas; we could check anything at this point.</P><H3 id="toc-hId--318329643">2.2.1 Create custom event</H3><P>Our custom event doesn't need much of a BO. Meaning we don't want to (re)create read/update/delete etc. functionality, but this is purely for defining how the event payload should look like and for triggering the custom event. </P><P>In my example I took it a step further: what if I create a <STRONG>generic "event forwarder"</STRONG>, that could potentially be used together with many SAP standard objects? Now, this isn't to say that I recommend this approach, in fact quite the opposite. And depending on your exact requirement, this may definitely the wrong approach. But I did this also in an effort to answer if this is at all feasible.</P><P>If having an event trigger with only key field information and then having a subsequent callback API later in the integration is a good pattern for you, then this could potentially help you, as you could reuse it for several use cases.</P><P>But, if instead you want something very specific, for example only forward my object under certain circumstances and including already all data needed, then of course it makes more sense to create a tailored custom event, with the exact payload of that object you want.</P><P>So please bear that in mind as we continue with this example. <STRONG>With this example I handle only a generic key-field based payload</STRONG>.</P><H4 id="toc-hId--808246155">2.2.1.1 Custom entity data definition</H4><P>We start of course with the data definition. Custom entity is in my opinion the best, as again we don't actually need any read or update functionality, but we simply describe our event payload at this point (and here you could have a large flat/deep structure):</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Event Forwarder BO'
define root custom entity ZR_EventForwarder
{
key ObjectKey : abap.string;
key ObjectType : zevent_fwd_object;
}</code></pre><H4 id="toc-hId--1004759660">2.2.1.2 Custom entity behavior definition</H4><P>Next we need the behavior that will include our custom event:</P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_r_eventforwarder unique;
strict ( 2 );
define behavior for ZR_EventForwarder
lock master
authorization master ( instance )
{
field ( readonly ) ObjectKey, ObjectType;
event ForwardEvent;
}</code></pre><P>Finally, let ADT generate the BP implementation for you (we do not need to fill any of the methods but so it's properly created (again, we need no read/save methods etc.)). So I will not paste the whole Class/or Local Types.</P><P>Instead, what we need to do here is now offer an option to "raise" our custom event "<STRONG>ForwardEvent</STRONG>" from outside this BO. The "<FONT color="#3366FF"><STRONG>raise entity event</STRONG></FONT>" command is not allowed outside the RAP BO itself. So instead, we need to encapsulate it in a way that ABAP code from outside this behavior pool can call it. For this we need 2 things:</P><OL><LI>"Local event handler" (not actually inheriting anything => chose this to separate it from other, standard RAP BP behavior methods)</LI><LI>Method in the global class that can be called from other ABAP code</LI></OL><P>Let's start with the "local event handler". Within the section <STRONG>Local Types</STRONG> of your behavior implementation class, add something this:</P><pre class="lia-code-sample language-abap"><code>class lcl_event_handler definition friends zbp_r_eventforwarder.
public section.
class-methods on_forward_event importing it_events type zbp_r_eventforwarder=>tt_events.
endclass.
class lcl_event_handler implementation.
method on_forward_event.
raise entity event zr_eventforwarder~forwardevent from it_events.
endmethod.
endclass.</code></pre><P>Note the "<FONT color="#0000FF"><STRONG>friends</STRONG></FONT>" command in the class definition. This is needed to be able to access private/protected class variables from the behavior pool (raising our entity event).</P><P>And in the <STRONG>Global Class</STRONG>, we change it as follow (full code copied):</P><pre class="lia-code-sample language-abap"><code>class zbp_r_eventforwarder definition public abstract final for behavior of zr_eventforwarder.
types tt_events type table for event zr_eventforwarder~forwardevent.
public section.
constants:
co_type_salesorder type zevent_fwd_object value 'SALESORDER'.
class-methods raise_forward_event
importing it_events type tt_events.
endclass.
class zbp_r_eventforwarder implementation.
method raise_forward_event.
" tell RAP framework that we are in a SAVE sequence (otherwise it thinks
" we are in the middle of a MODIFY)
cl_abap_tx=>save( ).
" trigger event (only allowed during SAVE)
lcl_event_handler=>on_forward_event( it_events ).
endmethod.
endclass.</code></pre><P>We did 3 main things: a global type for the event payload table, a constant to distinguish different types of RAP BO that I mean to handle with my generic one (right now only one) and, most importantly, a global class method that can be called from other ABAP code.</P><H4 id="toc-hId--1201273165">2.2.1.3 Event Binding</H4><P>As before, we need an event binding if we want to forward this to an event broker:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_9-1770124792302.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368569iA7F765998E574E12/image-size/large?v=v2&px=999" role="button" title="mlauber_9-1770124792302.png" alt="mlauber_9-1770124792302.png" /></span></P><P>(Namespace, object type etc. can be freely chosen.)</P><P>And that's all for our custom event. Remember that the only purpose of this custom event is to offer a way to define the payload and to trigger it. Any functionality, such as filtering, complex validation or data selection etc. we do when the real SAP standard event is triggered. Meaning in the local consumption. </P><H3 id="toc-hId--1104383663">2.2.2 Local consumption of SAP Standard event</H3><P>Now I won't go into step-by-step on this one, as I have done this already on the above linked blog post of mine. Please check there if the below steps are not enough for you.</P><P>I have an ABAP Class, abstract, final with the "<STRONG><FONT color="#3366FF">for events of</FONT></STRONG>" functionality, where once again we must type R_SalesOrderTP here (and not I_SalesOrderTP, which would have been the preferred option). This means my class has to be again saved as "Standard ABAP" and cannot be in an ABAP for Cloud Development package. But this is clean core compliant, as explained above.</P><P>In Local Types I have my local class that inherits from <STRONG>cl_abap_behavior_event_handler</STRONG> and I add a method for the Created event:</P><pre class="lia-code-sample language-abap"><code>class lcl_event_cons definition inheriting from cl_abap_behavior_event_handler.
private section.
methods:
consume_created for entity event instances for salesorder~created.
endclass.</code></pre><P>And here the implementation of that method (we'll go through it one by one below):</P><pre class="lia-code-sample language-abap"><code> data: lt_fwd_event type zbp_r_eventforwarder=>tt_events.
" go through RAP BO entity instances that triggerd the event
" (should only be 1 as we listend to sales order (header) created)
loop at instances into data(instance).
" get data needed for complex filtering
select salesorderitem, product, \_product-producttype
from i_salesorderitem
where salesorder = @instance-salesorder
into table (lt_items).
" check if sales order should be handled (forwarded to event broker)
if line_exists( lt_items[ producttype = 'HAWA' ] ).
" this sales order is now filtered OK and will be added to the forwarding instances
append initial line to lt_fwd_event assigning field-symbol(<fs_event>).
<fs_event> = value #( objectkey = instance-salesorder
objecttype = zbp_r_eventforwarder=>co_type_salesorder ).
else.
continue. " we are not interested in this Sales Order
endif.
endloop.
" now that we checked all instances, only those that we filtered will be forwarded
if lt_fwd_event is not initial.
zbp_r_eventforwarder=>raise_forward_event( lt_fwd_event ).
endif.</code></pre><UL><LI>We create an internal table of the payload type that is the custom event (we created a global type in the behavior definition)</LI><LI>We loop through all instances that were sent by the SAP standard event (since this is a sales order (header), this will only ever include 1)</LI><LI>As we loop, we select some additional data to do "some complex logic" for checking if this sales order should be forwarded or filtered out.</LI><LI>Once all requirements are met for forwarding the sales order, we fill the custom event payload table.</LI><LI>When the looping is done, we check if any sales orders remained (if we have any custom event payload) and if yes, we now call the method we offered on the custom event RAP BO behavior as global class method and give it the payload we just filled.</LI></UL><P> And that's all <span class="lia-unicode-emoji" title=":astonished_face:">😲</span><span class="lia-unicode-emoji" title=":grinning_face:">😀</span></P><H3 id="toc-hId--1300897168">2.2.3 Finishing</H3><P>As before, we need to configure our custom event (binding) in the event channel in order to forward it to the event broker:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_10-1770125042635.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368582iD3055B5DF16B25C3/image-size/large?v=v2&px=999" role="button" title="mlauber_10-1770125042635.png" alt="mlauber_10-1770125042635.png" /></span></P><P>On the event broker, we need to subscribe to that topic, to receive it there:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_12-1770125246503.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368585iCBCED5DFB3A60DDE/image-size/medium?v=v2&px=400" role="button" title="mlauber_12-1770125246503.png" alt="mlauber_12-1770125246503.png" /></span></P><P>To summarize, this is what will happen:</P><OL><LI>A user creates any sales order</LI><LI>R_SalesOrderTP-Created is triggered</LI><LI>Our local consumption of the SAP standard event is called: our local consumption checks if the sales order has any items with products of product type HAWA. If yes, we add it to be forwarded. If no, we disregard it.</LI><LI>If we had any sales order to forward, the ZR_EventForwarder behavior is called via workaround (a little bit nasty by just calling the global class but I could not think of any other way. For example we can't use an action, as actions are invoked via EML MODIFY, which in turn does not allow for RAISE EVENT). This triggers the ZR_EventForwarder-EventForwarded event with our payload</LI><LI>The custom event is forwarded to event broker</LI></OL><H3 id="toc-hId--1497410673">2.2.4 Testing</H3><P>Let's test this. I create a new sales order and I include 1 item that has a product of type HAWA. Meaning my filtering will allow this sales order and eventually trigger my custom event. </P><P>Let's see what the event monitor says:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="img3.png" style="width: 369px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368883i54C7B49E5FC7E40A/image-size/medium?v=v2&px=400" role="button" title="img3.png" alt="img3.png" /></span></P><P>And consuming this message in BTP Event Mesh:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="img4.png" style="width: 780px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368884i5357FD956B2A568C/image-size/large?v=v2&px=999" role="button" title="img4.png" alt="img4.png" /></span></P><H1 id="toc-hId--1107118164">3 I want a larger payload</H1><P>This is without a doubt the 2nd most discussed topic when talking event based integrations. In order to avoid callback APIs, maybe this is the way to go for you. As of right now, SAP Standard delivers notification events only, meaning small payloads to allow for the creation of fast, simple, integrations that can be easily reused.</P><P>You may already noticed that we already covered this in chapter 2. Both when extending the SAP standard event or when creating a custom event to work in combination with standard: I showcased how we can decide our own payload. So far I have only mentioned <STRONG>flat structures</STRONG>, as that is the only type that is supported by Event Mesh on SAP BTP. If you have a need for <STRONG>deep structure payloads</STRONG>, you may want to consider to upgrade to Advanced Event Mesh instead. And instead of creating a brand new example, please check out this blog post by a colleague that goes into deep payload: <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/rap-business-events-with-advanced-event-mesh-3-creating-events-with-deep/ba-p/13914285" target="_self">RAP Events with AEM 3 - Creating Events with deep payload</A>.</P><H1 id="toc-hId--1303631669">Conclusion / Final Words</H1><P>I hope this blog post helps and gives some ideas how far you can take RAP BO events and influence your integration landscape, in a positive way (of course, that's the end goal).</P><P>Is there anything I've missed? Another requirement that you would like to solve with eventing? Or any other questions or comments/feedback? Let me know in the comments and let's see <span class="lia-unicode-emoji" title=":smiling_face_with_smiling_eyes:">😊</span></P>2026-02-03T15:18:52.529000+01:00https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537How to Deliver Customizing as a SaaS Provider using Business Configuration Sets2026-02-05T09:59:15.458000+01:00peterpersielhttps://community.sap.com/t5/user/viewprofilepage/user-id/14643<DIV>In our previous blog post, <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-provide-customizing-as-a-saas-provider/ba-p/14009081" target="_blank">How to Provide Customizing as a SaaS Provider</A>, we described a programmatic approach that used EML statements with explicitly coded object templates in application jobs to deliver customizing entries. With release 2511, you can now use <A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/business-configuration-sets-2adb1bfdb1404a7497a4424e0411e2a6?version=Cloud" target="_self" rel="noopener noreferrer">Business Configuration Sets</A> instead, removing the need to include explicitly coded customizing data in your business logic.</DIV><P>This blog illustrates how to maintain customizing data in a <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/fiori-apps-business-configuration?version=Cloud" target="_self" rel="noopener noreferrer">Business Configuration Maintenance Object</A>, how to maintain customizing translations, importing the created customizing data/translations into a BC Set, implementing an application job for the BC Set activation and finally how to deploy the BC Set content to the relevant customizing table.<BR />We will also have a look into how BC Sets can be updated programmatically for the case when the customizing data was changed and BC Set data needs to be reimported.</P><P>This blog presents an alternative method to the Fiori app Upload Business Configuration. One <A href="https://influence.sap.com/sap/ino/#/idea/338295/?section=sectionDetails" target="_blank" rel="noopener noreferrer">restriction</A> of the Upload Business Configuration app is that it does not validate the customizing data being uploaded.<BR />In contrast, customizing data imported into Business Configuration (BC) Sets via ADT originates from existing customizing requests. This data is created within custom business configuration objects, where it is automatically validated through the underlying BO behavior (for example, error codes greater than 500 are rejected):</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_0-1770205854371.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368896i5858C39B446622DF/image-size/medium?v=v2&px=400" role="button" title="peterpersiel_0-1770205854371.png" alt="peterpersiel_0-1770205854371.png" /></span></P><P>This blog covers the following topics:</P><P><ul =""><li style="list-style-type:square; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537#toc-hId-1660412619">Create Business Configuration Maintenance Object</a></li><li style="list-style-type:square; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537#toc-hId-1463899114">Providing Authorization Control for a Business Configuration Maintenance Object</a></li><li style="list-style-type:square; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537#toc-hId-1267385609">Creating customizing data & translations</a></li><li style="list-style-type:square; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537#toc-hId-1070872104">Creating a BC Set and importing customizing data & translations</a></li><li style="list-style-type:square; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537#toc-hId-874358599">Creating an Application Job for the BC Set activation</a></li><li style="list-style-type:square; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537#toc-hId-677845094">Deploying the BC Set content by running the application job</a></li><li style="list-style-type:square; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-deliver-customizing-as-a-saas-provider-using-business-configuration/ba-p/14321537#toc-hId-481331589">Maintaining customizing data programmatically in BC Sets</a></li></ul></P><H1 id="toc-hId-1660412619"><STRONG>Create Business Configuration Maintenance Object</STRONG></H1><P>The Business Configuration Maintenance Object discussed in this blog can be developed using the following tutorial, which guides you in creating a table for storing error codes:</P><P><A href="https://developers.sap.com/tutorials/abap-environment-business-configuration-object.html" target="_blank" rel="noopener noreferrer">Create Business Configuration Maintenance Object | SAP Tutorials</A></P><H1 id="toc-hId-1463899114"><STRONG>Providing Authorization Control for a Business Configuration Maintenance Object</STRONG></H1><P>To use the Business Configuration Maintenance Object, the required authorization must be set up. How to do this is explained in this tutorial:</P><P><A href="https://developers.sap.com/tutorials/abap-environment-authorization-control.html" target="_blank" rel="noopener noreferrer">Providing Authorization Control for a Business Configuration Maintenance Object | SAP Tutorials</A></P><H1 id="toc-hId-1267385609"><STRONG>Creating customizing data & translations</STRONG></H1><P>Maintain following customizing data as described in tutorial <A href="https://developers.sap.com/tutorials/abap-environment-maintain-bc-app.html" target="_blank" rel="noopener noreferrer">Use Custom Business Configurations app</A> on a new customizing request:</P><TABLE width="271"><TBODY><TR><TD width="105"><P><STRONG>Error Code</STRONG></P></TD><TD width="165"><P><STRONG>Description</STRONG></P></TD></TR><TR><TD width="105"><P>400</P></TD><TD width="165"><P>Bad Request</P></TD></TR><TR><TD width="105"><P>401</P></TD><TD width="165"><P>Unauthorized</P></TD></TR><TR><TD width="105"><P>403</P></TD><TD width="165"><P>Forbidden</P></TD></TR></TBODY></TABLE><P>Afterwards your customizing request should include the primary customizing data as <EM>Customizing: Table Contents</EM>. Such customizing requests can be used for importing into a BC Set. Corresponding data records and translations will be extracted automatically during the process.</P><P>Maintain customizing translations for source language<EM> English United States</EM> and target language, for example <EM>German Germany,</EM> for the error codes using the <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/maintain-customizing-translations?version=Cloud" target="_blank" rel="noopener noreferrer">Maintain Customizing Translations</A> app.</P><P>Select the text table <EM>ZERRCODET_###</EM> that was created earlier as text source in your translation project.</P><H1 id="toc-hId-1070872104"><STRONG>Creating a BC Set and importing customizing data & translations</STRONG></H1><P>Follow the steps provided in <A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/creating-business-configuration-sets" target="_blank" rel="noopener noreferrer">Creating Business Configuration Sets</A> to create a new business configuration set and use the customizing request that was created in the previous step for importing the data.</P><P>After successful import from the customizing request the error code data records as well as translations are included:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_1-1770206570634.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368900i16C97DB6762B6F87/image-size/large?v=v2&px=999" role="button" title="peterpersiel_1-1770206570634.png" alt="peterpersiel_1-1770206570634.png" /></span></P><P>We can now proceed to implement an application job that can be used activate this BC Set.</P><P><STRONG>Note: </STRONG>To import customizing data into a BC Set, the customizing data needs to be maintained for customizing objects of type Individual Transaction Object. A business configuration maintenance object (CUBCO) is configured as such by defining a <A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/working-with-transport-object-definitions" target="_blank" rel="noopener noreferrer">transport object definition (TOBJ)</A> in the service configuration settings of the CUBCO. By using the business configuration maintenance object wizard as described in <A href="https://developers.sap.com/tutorials/abap-environment-business-configuration-object.html" target="_blank" rel="noopener noreferrer">Create Business Configuration Maintenance Object | SAP Tutorials</A>, this prerequisite is fulfilled. For more information, please refer to <A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/generating-business-configuration-maintenance-object-with-generate-abap-repository-objects-wizard?version=Cloud#bc-management" target="_blank" rel="noopener noreferrer">Generating a Business Configuration Maintenance Object with the Generate ABAP Repository Objects Wizard</A> <SPAN>→</SPAN> BC Management. Configuration changes are then recorded with object type TDAT instead of a TABU.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_2-1770206570637.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368899iBCA9DA4E870B838B/image-size/large?v=v2&px=999" role="button" title="peterpersiel_2-1770206570637.png" alt="peterpersiel_2-1770206570637.png" /></span></P><P>Those business configuration maintenance objects without existing transport object definition must be migrated, and a transport object definition needs to be created manually. How this can be done is described in the blog post <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-enable-bc-set-support-for-bc-app/ba-p/14313371" target="_blank">How to enable BC Set support for BC app</A>.</P><H1 id="toc-hId-874358599"><STRONG>Creating an Application Job for the BC Set activation</STRONG></H1><P>In this example, an <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/application-jobs" target="_blank" rel="noopener noreferrer">Application Job</A> is used for creating and updating the customizing entries in a SaaS tenant. The customizing entries are deployed within the Application Job's implementation by activating the business configuration set using the corresponding <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/runtime-api-986855193ceb4e06862ac78466327928" target="_blank" rel="noopener noreferrer">runtime API</A>.</P><P>As a first step, implement the BC Set activation as the <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/implementing-business-logic" target="_blank" rel="noopener noreferrer">business logic of the of the application job</A> with a parameter for the BC Set to be activated and while saving activation messages in the application log:</P><pre class="lia-code-sample language-abap"><code>CLASS zcl_errorcode_set_activate_000 DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
"! <p class="shorttext synchronized">Error Codes BC Set</p>
DATA errorcode_bc_set TYPE cl_scpr_cd_bcset_activation=>ty_bcset_id VALUE 'Z_ERROR_CODES_SET_000'.
INTERFACES if_apj_rt_run.
INTERFACES if_apj_dt_defaults.
PRIVATE SECTION.
CONSTANTS log_object TYPE cl_bali_header_setter=>ty_object VALUE 'Z_ERRORCODE_SET_000'.
CONSTANTS log_subobject TYPE cl_bali_header_setter=>ty_subobject VALUE 'ACTIVATE'.
ENDCLASS.
CLASS zcl_errorcode_set_activate_000 IMPLEMENTATION.
METHOD if_apj_dt_defaults~fill_attribute_defaults.
errorcode_bc_set = 'Z_ERROR_CODES_SET_000'.
ENDMETHOD.
METHOD if_apj_rt_run~execute.
TRY.
DATA(lo_bali_log) = cl_bali_log=>create_with_header(
header = cl_bali_header_setter=>create( object = log_object
subobject = log_subobject ) ).
TRY.
DATA(lo_bcset_activate) = cl_scpr_cd_bcset_activation=>of( errorcode_bc_set ).
CATCH cx_scpr_object_not_permitted INTO DATA(lx_scpr_object_not_permitted).
lo_bali_log->add_item(
item = cl_bali_exception_setter=>create( severity = if_bali_constants=>c_severity_error
exception = lx_scpr_object_not_permitted ) ).
ENDTRY.
DATA(lo_activation_handler) = lo_bcset_activate->get_activate_handler( ).
DATA(lo_activation_result) = lo_activation_handler->local( )->add_activation_options( )->start( ).
DATA(lt_activation_messages) = lo_activation_result->get_messages( ).
LOOP AT lt_activation_messages INTO DATA(ls_msg).
lo_bali_log->add_item( item = cl_bali_message_setter=>create_from_bapiret2( message_data = ls_msg ) ).
ENDLOOP.
cl_bali_log_db=>get_instance( )->save_log_2nd_db_connection( log = lo_bali_log
assign_to_current_appl_job = abap_true ).
CATCH cx_bali_runtime INTO DATA(lx_bali_runtime).
RAISE EXCEPTION TYPE cx_apj_rt_content USING MESSAGE
EXPORTING previous = lx_bali_runtime.
ENDTRY.
ENDMETHOD.
ENDCLASS.</code></pre><P>Then create a catalog entry and job template for the class with the business logic: <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/creating-job-catalog-entry-and-job-template-in-adt" target="_blank" rel="noopener noreferrer">Creating a Job Catalog Entry and a Job Template</A>. Also create an application log object and sub object according to the job logic implementation: <A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/working-with-application-log-objects?version=sap_btp" target="_blank" rel="noopener noreferrer">Working with Application Log Objects</A>.</P><P>To run the application job for BC Set activation, users must have the authorization provided by a business catalog. In this case we will reuse the business catalog <EM>Z_ERROR_CODES_###</EM> that was already created as part of the tutorial <A href="https://developers.sap.com/tutorials/abap-environment-authorization-control.html" target="_blank" rel="noopener noreferrer">Providing Authorization Control for a Business Configuration Maintenance Object</A>.</P><P>The business catalog <EM>Z_ERROR_CODES_###</EM> already includes the <A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/iam-app-types?version=Cloud" target="_blank" rel="noopener noreferrer">IAM App of type</A> <EM>MBC</EM> (Business Configuration App) that was created for the CUBCO. During creation of the job catalog entry for the BC Set activation job, another IAM app with name <EM><job catalog entry name>_SAJC</EM> is automatically created (refer to <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/setting-up-authorizations?version=Cloud" target="_blank" rel="noopener noreferrer">Setting up the Authorizations</A>). This IAM app, to start the application job, is added to the existing business catalog so that the catalog provides authorizations to maintain error codes in the CUBCO as well as to start the BC Set activation job:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_3-1770207589713.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368904iE77B14E4AEE8D2BA/image-size/large?v=v2&px=999" role="button" title="peterpersiel_3-1770207589713.png" alt="peterpersiel_3-1770207589713.png" /></span></P><P>The BC Set activation implemented in the activation job using the <EM>CL_SCPR_CD_BCSET_ACTIVATION</EM> API requires table access for all entities used in the BC Set, so for both the primary tables as well as text tables. This can be achieved by adding the names of the tables in the <EM>TABLE</EM> authorization field of the existing <EM>S_TABU_NAM</EM> authorization in the <EM>_MBC</EM> IAM app: in this case <EM>ZERRCODET_000</EM> (text table) and <EM>ZERRCODE_000</EM> (primary table):</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_4-1770207786852.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368909i70BCA9687904C610/image-size/large?v=v2&px=999" role="button" title="peterpersiel_4-1770207786852.png" alt="peterpersiel_4-1770207786852.png" /></span></P><P>Hint: If you plan to run the BC Set activation job in your development system, do not forget to publish your changes to the business catalog locally.</P><H1 id="toc-hId-677845094"><STRONG>Deploying the BC Set content by running the application job</STRONG></H1><P>Make sure that the user that shall run the BC Set activation job has following business roles/catalogs assigned in the ABAP tenant (for example your SaaS consumer tenant):</P><TABLE><TBODY><TR><TD width="267.996px" height="50px"><P><STRONG>Business Role</STRONG></P></TD><TD width="232.992px" height="50px"><P><STRONG>Business Catalog</STRONG></P></TD><TD width="123px" height="50px"><P><STRONG>Purpose</STRONG></P></TD></TR><TR><TD width="267.996px" height="132px"><P>SAP_BR_ADMINISTRATOR</P></TD><TD width="232.992px" height="132px"><P>SAP_CORE_BC_APJ_JCE</P></TD><TD width="123px" height="132px"><P>For scheduling jobs in the <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/application-jobs-3?version=Cloud" target="_blank" rel="noopener noreferrer">Application Jobs</A> app</P></TD></TR><TR><TD width="267.996px" height="159px"><P>SAP_BR_BPC_EXPERT</P></TD><TD width="232.992px" height="159px"><P>SAP_CORE_BC_BCT_MBC_PC</P></TD><TD width="123px" height="159px"><P>To use the <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/custom-business-configurations-app?version=Cloud" target="_blank" rel="noopener noreferrer">Custom Business Configurations app</A></P></TD></TR><TR><TD width="267.996px" height="488px"><P>ZBR_ERROR_CODES_EXPERT_###</P></TD><TD width="232.992px" height="488px"><P>Z_ERROR_CODES_###</P></TD><TD width="123px" height="488px"><P>To maintain error codes in the Custom Business Configurations app and to start the BC Set activation job.<BR />As created in tutorial <A href="https://developers.sap.com/tutorials/abap-environment-authorization-control.html" target="_blank" rel="noopener noreferrer">Providing Authorization Control for a Business Configuration Maintenance Object</A></P></TD></TR></TBODY></TABLE><P>Open the Application Job App and select "Create" to create a new Application Job, then<BR />choose the Application Job Template. Maintain the scheduling options and provide a valid BC Set in the parameter. Schedule the Application Job by clicking "Schedule".</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_5-1770208025521.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368918i02D953A69DFD883A/image-size/large?v=v2&px=999" role="button" title="peterpersiel_5-1770208025521.png" alt="peterpersiel_5-1770208025521.png" /></span></P><P>Check the Application Job Log to determine if the BC Set content was deployed correctly:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_6-1770208059336.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368921i6DB487C8FB390C6F/image-size/large?v=v2&px=999" role="button" title="peterpersiel_6-1770208059336.png" alt="peterpersiel_6-1770208059336.png" /></span></P><P>Users assigned to the required business roles (see above) can now check in the Custom Business Configurations App whether the customizing has been inserted and updated correctly:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="peterpersiel_7-1770208099315.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368922iE07C527C2359B269/image-size/medium?v=v2&px=400" role="button" title="peterpersiel_7-1770208099315.png" alt="peterpersiel_7-1770208099315.png" /></span></P><H1 id="toc-hId-481331589"><STRONG>Maintaining customizing data programmatically in BC Sets</STRONG></H1><P>You might want to automate the lifecycle of your customizing data. For instance, you can update a BC Set with the latest customizing data after some changes. To do this, maintain your changes in EML within the configuration object on a customizing request. Then read the latest data from the underlying customizing tables and finally update the BC Set.</P><P>You can programmatically create a customizing request using the <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/correction-and-transport-system?version=Cloud" target="_blank" rel="noopener noreferrer">XCO CTS module</A>. EML will then be used to maintain the data on that customizing request. Additionally, you have the option to programmatically release the customizing request afterward.</P><pre class="lia-code-sample language-abap"><code>CLASS zcl_errorcode_generate_000 DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
TYPES tyt_errorcodes TYPE STANDARD TABLE OF zerrcode_000 WITH KEY error_code.
TYPES tyt_errorcodestext TYPE STANDARD TABLE OF zerrcodet_000 WITH KEY langu error_code.
TYPES tyt_errorcodes_update TYPE TABLE FOR UPDATE ZI_ErrorCode000.
TYPES tyt_errorcodestext_update TYPE TABLE FOR UPDATE ZI_ErrorCode000Text.
METHODS delete_errorcodes
IMPORTING transportrequest TYPE trkorr.
METHODS create_errorcodes
IMPORTING transportrequest TYPE trkorr
it_errorcodes_crt TYPE tyt_errorcodes
it_errorcodestext_crt TYPE tyt_errorcodestext.
METHODS update_errorcodes
IMPORTING transportrequest TYPE trkorr
it_errorcodes_update TYPE tyt_errorcodes_update
it_errorcodestext_update TYPE tyt_errorcodestext_update.
CONSTANTS co_cust_transport_target TYPE if_xco_transport_target=>tv_value VALUE ''. " LOCAL
CONSTANTS co_cust_transport_description TYPE sxco_ar_short_description VALUE 'Maintain new error codes programmatically'.
ENDCLASS.
CLASS zcl_errorcode_generate_000 IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" Create customizing transport request
DATA(lo_customizing_request) = xco_cp_cts=>transports->customizing( co_cust_transport_target )->create_request(
co_cust_transport_description ).
" Programmatically create new error codes using EML
create_errorcodes( transportrequest = lo_customizing_request->value
it_errorcodes_crt = VALUE tyt_errorcodes( ( error_code = '404' )
( error_code = '405' )
( error_code = '406' ) )
it_errorcodestext_crt = VALUE tyt_errorcodestext(
( langu = 'D' error_code = '404' description = 'Not Found' )
( langu = 'E' error_code = '404' description = 'Nicht gefunden' )
( langu = 'D' error_code = '405' description = 'Method Not Allowed' )
( langu = 'E' error_code = '405' description = 'Methode nicht erlaubt' )
( langu = 'D' error_code = '406' description = 'Not Acceptable' )
( langu = 'E' error_code = '406' description = 'Nicht akzeptabel' ) ) ).
" Release customizing tasks and customizing request
DATA(lt_transport_tasks) = lo_customizing_request->get_tasks( ).
LOOP AT lt_transport_tasks INTO DATA(lo_transport_task).
IF lo_transport_task->get_status( ) <> xco_cp_transport=>status->modifiable.
CONTINUE.
ENDIF.
lo_transport_task->release( ).
ENDLOOP.
lo_customizing_request->release( ).
ENDMETHOD.
METHOD create_errorcodes.
DATA lt_errorcode_create TYPE TABLE FOR CREATE ZI_ErrorCode000_S\_ErrorCode000.
TYPES tys_errorcode_create LIKE LINE OF lt_errorcode_create.
DATA ls_errorcode_create TYPE tys_errorcode_create.
DATA lt_errorcodestext_create TYPE TABLE FOR CREATE ZI_ErrorCode000\_ErrorCode000Text.
TYPES tys_errorcodestext_create LIKE LINE OF lt_errorcodestext_create.
DATA ls_errorcodestext_create TYPE tys_errorcodestext_create.
LOOP AT it_errorcodes_crt INTO DATA(wa_errorcode_crt).
ls_errorcode_create = VALUE #( SingletonID = 1
%target = VALUE #( ( ErrorCode = wa_errorcode_crt-error_code
%control-ErrorCode = if_abap_behv=>mk-on ) ) ).
APPEND ls_errorcode_create TO lt_errorcode_create.
ENDLOOP.
LOOP AT it_errorcodestext_crt INTO DATA(wa_errorcodestext_crt).
ls_errorcodestext_create = VALUE #(
%tky-ErrorCode = wa_errorcodestext_crt-error_code
%target = VALUE #( ( Langu = wa_errorcodestext_crt-langu
%control-Langu = if_abap_behv=>mk-on
Description = wa_errorcodestext_crt-description
%control-Description = if_abap_behv=>mk-on ) ) ).
APPEND ls_errorcodestext_create TO lt_errorcodestext_create.
ENDLOOP.
MODIFY ENTITIES OF ZI_ErrorCode000_S
ENTITY ErrorCode000All
UPDATE
FIELDS ( TransportRequestID )
WITH VALUE #( ( SingletonId = 1
TransportRequestID = transportrequest ) )
ENTITY ErrorCode000All
CREATE BY \_ErrorCode000 FROM lt_errorcode_create
REPORTED DATA(r_errorcodes_crt)
FAILED DATA(f_errorcodes_crt)
MAPPED DATA(m_errorcodes_crt) ##NEEDED.
MODIFY ENTITIES OF ZI_ErrorCode000_S
ENTITY ErrorCode000
CREATE BY \_ErrorCode000Text FROM lt_errorcodestext_create
REPORTED DATA(r_errorcodestext_crt)
FAILED DATA(f_errorcodestext_crt)
MAPPED DATA(m_errorcodestext_crt) ##NEEDED.
COMMIT ENTITIES
BEGIN RESPONSE OF ZI_ErrorCode000_S
FAILED DATA(failed_create)
REPORTED DATA(reported_create) ##NEEDED.
COMMIT ENTITIES END.
ENDMETHOD.
METHOD delete_errorcodes.
MODIFY ENTITIES OF ZI_ErrorCode000_S
ENTITY ErrorCode000All
UPDATE
FIELDS ( TransportRequestID )
WITH VALUE #( ( SingletonId = 1
TransportRequestID = transportrequest ) )
FAILED DATA(f_delete_tr)
MAPPED DATA(m_delete_tr)
REPORTED DATA(r_delete_tr) ##NEEDED.
MODIFY ENTITIES OF ZI_ErrorCode000_S
ENTITY ErrorCode000All
CREATE FIELDS ( TransportRequestID ) WITH VALUE #( ( TransportRequestID = transportrequest ) )
DELETE FROM VALUE #( ( SingletonID = 1 ) )
FAILED DATA(f_delete)
MAPPED DATA(m_delete)
REPORTED DATA(r_delete) ##NEEDED.
COMMIT ENTITIES
BEGIN RESPONSE OF ZI_ErrorCode000_S
FAILED DATA(failed_delete)
REPORTED DATA(reported_delete) ##NEEDED.
COMMIT ENTITIES END.
ENDMETHOD.
METHOD update_errorcodes.
DATA lt_errorcodestext_update_i TYPE TABLE FOR UPDATE ZI_ErrorCode000Text.
IF it_errorcodes_update IS NOT INITIAL OR it_errorcodestext_update IS NOT INITIAL.
LOOP AT it_errorcodestext_update INTO DATA(ls_errorcodetext_update).
ls_errorcodetext_update-%control-Description = if_abap_behv=>mk-on.
APPEND ls_errorcodetext_update TO lt_errorcodestext_update_i.
ENDLOOP.
ENDIF.
MODIFY ENTITIES OF ZI_ErrorCode000_S
ENTITY ErrorCode000All
UPDATE
FIELDS ( transportrequestid )
WITH VALUE #( ( SingletonID = 1
TransportRequestID = transportrequest ) )
ENTITY ErrorCode000Text
UPDATE FROM lt_errorcodestext_update_i
REPORTED DATA(r_errorcodes_upd)
FAILED DATA(f_errorcodes_upd)
MAPPED DATA(m_errorcodes_upd) ##NEEDED.
COMMIT ENTITIES
BEGIN RESPONSE OF ZI_ErrorCode000_S
FAILED DATA(failed_update)
REPORTED DATA(reported_update) ##NEEDED.
COMMIT ENTITIES END.
ENDMETHOD.
ENDCLASS.</code></pre><P>The advantage of using EML to create, update, and delete customizing entries is that all validations of the business configuration object (BCO) are performed. This approach also supports both customizing entries of type table data (TABU) and individual transaction object (TDAT) since API adapters are available for both cases to be used in the behavior implementation of the BCO (for example, <EM>mbc_cp_api=>rap_tdat_cts()).</EM> For further information, refer to <A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/transport-handling?version=Cloud" target="_blank" rel="noopener noreferrer">Transport Handling</A>.</P><P>After updating the customizing data in the Business Configuration Object (BCO) using EML, the latest customizing data can be retrieved from the underlying customizing tables for further processing. Once the up-to-date customizing data is obtained, it can be directly maintained in the BC Set. This process utilizes the <A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/business-configuration-sets?version=Cloud" target="_self" rel="noopener noreferrer">XCO generation APIs for BC Sets</A>.</P><pre class="lia-code-sample language-abap"><code>CLASS zcl_errorcode_generate_000 DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
CONSTANTS co_workbench_request TYPE trkorr VALUE ''. " ZLOCAL
CONSTANTS co_error_codes_set_name TYPE if_xco_business_cnfgrtn_set=>tv_name VALUE 'Z_ERROR_CODES_SET_000'.
CONSTANTS co_error_codes_entity_primary TYPE if_xco_bcs_entity=>tv_name VALUE 'ZERRCODE_000'.
CONSTANTS co_error_codes_entity_text TYPE if_xco_bcs_entity=>tv_name VALUE 'ZERRCODET_000'.
CONSTANTS co_error_codes_tobj TYPE if_xco_bcs_customizing_object=>tv_name VALUE 'ZERRORCODE000'.
ENDCLASS.
CLASS zcl_errorcode_generate_000 IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" Read latest error codes from customizing tables
SELECT * FROM zerrcode_000 WHERE error_code < 500 INTO TABLE (lt_errorcodes).
ASSERT sy-subrc = 0.
SELECT * FROM zerrcodet_000 WHERE error_code < 500 INTO TABLE (lt_errorcodes_text).
ASSERT sy-subrc = 0.
" Retrieve error codes BC set and make sure it exists
DATA(lo_business_configuration_set) = xco_cp_abap_repository=>object->scp1->for( co_error_codes_set_name ).
ASSERT lo_business_configuration_set->if_xco_ar_object~exists( ).
" Create BC Set patch operation and change specification to remove existing error codes customizing object (all its Entities and corresponding records)
DATA(lo_patch_operation_delete) = xco_cp_generation=>environment->dev_system( co_workbench_request )->for-scp1->create_patch_operation( ).
DATA(lo_change_specification_delete) = lo_patch_operation_delete->add_object( co_error_codes_set_name )->create_change_specification( ).
lo_change_specification_delete->for-delete->add_individual_transaction_obj( co_error_codes_tobj ).
" Create BC Set patch operation and change specification to insert latest error codes using latest customizing data
DATA(lo_patch_operation_insert) = xco_cp_generation=>environment->dev_system( co_workbench_request )->for-scp1->create_patch_operation( ).
DATA(lo_change_specification_insert) = lo_patch_operation_insert->add_object( co_error_codes_set_name )->create_change_specification( ).
DATA(lo_tobj_insert) = lo_change_specification_insert->for-insert->add_individual_transaction_obj(
co_error_codes_tobj ).
lo_tobj_insert->for-insert->add_entity( co_error_codes_entity_primary )->for-insert->add_records(
REF #( lt_errorcodes[] ) ).
lo_tobj_insert->for-insert->add_entity( co_error_codes_entity_text )->for-insert->add_records(
REF #( lt_errorcodes_text[] ) ).
" After preparing the change specifications, execute the PATCH operations
TRY.
DATA(lo_result_delete) = lo_patch_operation_delete->execute( ) ##NEEDED.
DATA(lo_result_insert) = lo_patch_operation_insert->execute( ) ##NEEDED.
CATCH cx_xco_gen_patch_exception INTO DATA(lo_xco_gen_patch_exception).
LOOP AT lo_xco_gen_patch_exception->findings->get( ) ASSIGNING FIELD-SYMBOL(<fs_finding>) ##NEEDED.
" Process errors/exceptions raised from the PATCH operation
ENDLOOP.
ENDTRY.
ENDMETHOD.
ENDCLASS.</code></pre><P>This section of using the XCO generation API for BC Sets to update BC set data requires customizing entries of type individual transaction object (TDAT). This is because it’s a requirement for importing any data into a BC Set. BC Sets are workbench objects and can be delivered alongside other development objects in your development software components.</P><P>Hint: In productive scenarios remove the <EM>##NEEDED</EM> pragma from the code examples and implement proper error handling!</P>2026-02-05T09:59:15.458000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/prefilling-a-value-help-filter-from-the-parent-entity-in-rap/ba-p/14324820Prefilling a Value Help Filter from the Parent Entity in RAP2026-02-10T01:23:19.551000+01:00Louis-Arnaudhttps://community.sap.com/t5/user/viewprofilepage/user-id/15467<P>In a RAP application, I recently encountered a common requirement: <STRONG>pre‑filter a Value Help on a child entity field using a value coming from the parent entity</STRONG> (in my case, the <CODE>CompanyCode</CODE> from the header).</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_0-1770682875602.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/370619iBE7D1EDCE09736C2/image-size/medium?v=v2&px=400" role="button" title="LouisArnaud_0-1770682875602.png" alt="LouisArnaud_0-1770682875602.png" /></span></P><P>The scenario looks simple, but RAP currently cannot evaluate an association inside an <CODE>additionalBinding</CODE>. I tried the expected annotation:</P><pre class="lia-code-sample language-abap"><code>@Consumption.valueHelpDefinition: [
{
entity: { name : 'I_PurchaseOrderAPI01', element : 'PurchaseOrder' },
additionalBinding: [
{ element: 'CompanyCode', localElement: '_Header.CompanyCode', usage: #FILTER }
]
}
]
PurchaseOrder;</code></pre><DIV>Unfortunately, <STRONG>the framework does not resolve the <CODE>_Header</CODE> association</STRONG>, and the filter is not applied.</DIV><DIV>There is an even more fundamental issue: <STRONG>When creating a child record, the child entity simply does not exist yet. </STRONG><SPAN>So you </SPAN><EM>cannot</EM><SPAN> push </SPAN><CODE>CompanyCode</CODE><SPAN> into the child entity and bind from there—there’s no child instance at that moment.</SPAN></DIV><DIV><DIV>Because of this, annotations alone cannot solve the problem.</DIV><DIV> </DIV><H2 id="toc-hId-1789587556">Workaround: Inject the Filter from a Fiori Elements Controller Extension</H2><DIV> <SPAN>The most pragmatic solution is to extend the </SPAN><STRONG>Object Page controller</STRONG><SPAN> and add the filter programmatically when the Value Help opens. This avoids backend limitations and works well during create flows.</SPAN></DIV><DIV><DIV><H4 id="toc-hId-1851239489">Why my case is simpler</H4><P>In my application, the <CODE>PurchaseOrder</CODE> field is a <STRONG>multi‑input on the header</STRONG>, not inside a table. That means:</P><UL><LI>There is <STRONG>one single generated field</STRONG>,</LI><LI>Easy to identify,</LI><LI>Easy to attach the <CODE>opened</CODE> event to its Value Help.</LI></UL><P>For tables, the logic is still possible but more dynamic (details below).</P><H2 id="toc-hId-1396560546">Implementation – Header Multi‑Input Scenario</H2><H3 id="toc-hId-1329129760">Step 1 — Add a controller extension</H3><DIV>In the Object Page, add a controller extension (via the page map in Fiori Tools).</DIV><DIV><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_0-1770681599198.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/370615iA7A8EA8518908074/image-size/medium?v=v2&px=400" role="button" title="LouisArnaud_0-1770681599198.png" alt="LouisArnaud_0-1770681599198.png" /></span><P> </P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="LouisArnaud_1-1770681617762.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/370616i162AF53B1E2D827B/image-size/medium?v=v2&px=400" role="button" title="LouisArnaud_1-1770681617762.png" alt="LouisArnaud_1-1770681617762.png" /></span><DIV>The generated extension usually provides an <CODE>onInit</CODE> method override. Insert a call to your custom method:</DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-javascript"><code>onInit: function () {
this._extendPurchaseOrderValueHelp();
}</code></pre><DIV><H3 id="toc-hId-1132616255"><STRONG>Step 2 — Locate the generated field + attach a handler</STRONG></H3><P>Use Chrome DevTools to retrieve the generated ID for the multi‑input field. Then:</P></DIV><pre class="lia-code-sample language-javascript"><code>_extendPurchaseOrderVH: function () {
const oView = this.base.getView();
// 1) Get generated MDC field for PurchaseOrder
const sPurchaseOrderId = "my.app::DocumentObjectPage--fe::FormContainer::FileTemplate::FormElement::DataField::_TemplatePurchaseOrder::PurchaseOrder::MultiValueField";
let oField = oView.byId(sPurchaseOrderId);
if (!oField) {
return; // not rendered yet
}
// 2) Get MDC value help attach method to opened event
const sPurchaseOrderValueHelpId = oField.getValueHelp && oField.getValueHelp(); // can return an id
const oPurchaseOrderValueHelp = sPurchaseOrderValueHelpId ? oView.byId(sPurchaseOrderValueHelpId) : null;
if (!oPurchaseOrderValueHelp) {
return;
}
oPurchaseOrderValueHelp.attachOpened(this._onOpenPurchaseOrderValueHelp);
}</code></pre><DIV><P>This function:</P><OL><LI>Retrieves the generated MDC field,</LI><LI>Retrieves its Value Help,</LI><LI>Hooks into the <CODE>opened</CODE> event.</LI></OL><H3 id="toc-hId-936102750">Step 3 — Inject the parent’s CompanyCode into the Value Help</H3></DIV><pre class="lia-code-sample language-javascript"><code> _onOpenPurchaseOrderValueHelp: function (oEvent) {
// Read company code from parent
const oCtx = oEvent.getSource().getBindingContext();
const sCompanyCode = oCtx && oCtx.getObject() && oCtx.getObject().CompanyCode;
if (!sCompanyCode) return;
//Get value help object
const oContainer = oEvent.getParameter("container");
const aContents = oContainer && oContainer.getContent ? oContainer.getContent() : [];
const oFilterBar = aContents
.map(c => c.getFilterBar && c.getFilterBar())
.find(Boolean);
if (!oFilterBar) return;
//Apply condition on the model
const sCM = oFilterBar.getConditionModelName ? oFilterBar.getConditionModelName() : "$filters";
const oCM = oFilterBar.getModel(sCM);
const Condition = sap.ui.mdc && sap.ui.mdc.condition && sap.ui.mdc.condition.Condition;
if (!oCM || !Condition) return;
oCM.removeAllConditions && oCM.removeAllConditions("CompanyCode");
oCM.addCondition && oCM.addCondition("CompanyCode", Condition.createCondition("EQ", [sCompanyCode]));
oCM.checkUpdate && oCM.checkUpdate(true);
//trigger search again
oFilterBar.triggerSearch?.();
}</code></pre><DIV><H2 id="toc-hId-610506526"><STRONG>A note about the table (item) scenario</STRONG></H2><P>In many applications, the Value Help is not in the header but <STRONG>inside a table row</STRONG>. This is actually the more common case.</P><P>However, the logic becomes a bit different because:</P><UL><LI>Each row generates its <STRONG>own instance</STRONG> of the field,</LI><LI>These controls appear dynamically as the table renders,</LI><LI>IDs are not static like a header field,</LI><LI>You must find each row’s field and attach the event individually,</LI><LI>You cannot rely on a fixed ID.</LI></UL><P><STRONG>This makes the implementation slightly more complex</STRONG>, but still entirely doable.</P><P> </P></DIV>2026-02-10T01:23:19.551000+01:00https://community.sap.com/t5/technology-blog-posts-by-sap/triggering-sap-datasphere-bdc-task-chains-from-sap-btp-abap-cloud-using-the/ba-p/14325528Triggering SAP Datasphere/BDC Task Chains from SAP BTP ABAP Cloud using the Task Chain REST API2026-02-10T16:44:24.061000+01:00stefan_geiselhart2https://community.sap.com/t5/user/viewprofilepage/user-id/200897<P>Our modern data landscapes thrive on automation. In this post, we’ll walk end-to-end through a real-life integration scenario that tackles one of the building blocks in this context:</P><P class="lia-align-center" style="text-align: center;"><STRONG>Starting a SAP Datasphere Task Chain remotely from SAP BTP ABAP Cloud using OAuth 2.0 Client Credentials and the Datasphere Task Chain REST API</STRONG></P><P>The goal is to orchestrate Datasphere data processing flows directly from ABAP Cloud — securely, programmatically, and fully automated.</P><P>We will cover the below points:</P><UL><LI>Creating a Task Chain in Datasphere</LI><LI>Configuring OAuth clients in Datasphere</LI><LI>Setting up Communication System & Arrangement in ABAP Cloud</LI><LI>Executing the Task Chain remotely: Consume REST API from ABAP Cloud</LI></UL><P><FONT size="5"><STRONG>1 Architecture Overview</STRONG></FONT></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Solution Architecture" style="width: 550px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371166iB8C9DA6C7F6550B3/image-dimensions/550x406?v=v2" width="550" height="406" role="button" title="image.png" alt="Solution Architecture" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Solution Architecture</span></span></P><P>ABAP Cloud Task Chain Execution: ABAP Cloud acts as the caller. Datasphere exposes the Task Chain API protected by OAuth.</P><P><FONT size="5"><STRONG>2 Prerequisites</STRONG></FONT></P><P>You must have:</P><UL><LI>SAP Datasphere tenant with developer + admin access</LI><LI>A Task Chain to trigger</LI><LI>SAP BTP ABAP Environment (ABAP Cloud) with developer access</LI><LI>BTP ABAP: Authorization to configure OAuth clients and Communication Arrangements</LI></UL><P><FONT size="5"><STRONG>3 Creating a Task Chain in SAP Datasphere</STRONG></FONT></P><P>The first step is defining a Task Chain that encapsulates the processing logic you want to execute remotely.</P><P>In the given example:</P><UL><LI>The Task Chain starts with a <STRONG>Begin</STRONG> node</LI><LI>Executes a <STRONG>Replication Flow</STRONG> for loading dimension data</LI><LI>(Can be extended with transformations, views, or further flows)</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SAP Datasphere Task Chain with Replication Flow inside" style="width: 592px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371084i85FF39979A8A1F3B/image-dimensions/592x320?v=v2" width="592" height="320" role="button" title="image.png" alt="SAP Datasphere Task Chain with Replication Flow inside" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">SAP Datasphere Task Chain with Replication Flow inside</span></span></P><P><EM>Main takeaways:</EM></P><UL><LI>Task Chain must be <EM>Deployed</EM></LI><LI>Note the <EM>Technical Name</EM> (used later by API)</LI><LI>Verify it runs manually before you automate via the API<STRONG> </STRONG></LI></UL><P><FONT size="5"><STRONG>4 Configuring OAuth Client in SAP Datasphere</STRONG></FONT></P><P>To allow ABAP Cloud to call Datasphere APIs securely, we create an OAuth Client in Datasphere Administration.</P><P><EM>Navigation Path</EM></P><P>Datasphere → Administration → App Integration → OAuth Clients</P><P><EM>OAuth Endpoints Provided</EM></P><P>Amongst others, SAP Datasphere automatically exposes:</P><UL><LI>Authorization URL</LI><LI>Token URL</LI></UL><P>These are required for OAuth2 token retrieval. Moreover you must create a technical user that has access to the Datasphere space and is able to run the task chains that are in scope. Typically this is done by assigning a scoped role.</P><P><EM>Configured OAuth Client (based on Technical User)</EM></P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Menu Path Datasphere" style="width: 172px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371014i26654901577A6E67/image-size/large?v=v2&px=999" role="button" title="image.png" alt="Menu Path Datasphere" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Menu Path Datasphere</span></span></STRONG></P><P>This is where you see the OAuth Client specific URLs - take note of them for later configuration in ABAP BTP:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="OAuth Client Generation" style="width: 764px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371054i4BB7492B30B71B30/image-dimensions/764x458?v=v2" width="764" height="458" role="button" title="image.png" alt="OAuth Client Generation" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">OAuth Client Generation</span></span></P><P>Your specific OAuth client for consumption from ABAP BTP. The secret key is only shown once upon creation - keep this in mind!</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="OAuth Client Details" style="width: 404px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371055i19C15B5494614179/image-dimensions/404x580?v=v2" width="404" height="580" role="button" title="image.png" alt="OAuth Client Details" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">OAuth Client Details</span></span></P><P><EM>Important settings:</EM></P><TABLE><TBODY><TR><TD><P><STRONG>Parameter</STRONG></P></TD><TD><P><STRONG>Value</STRONG></P></TD></TR><TR><TD><P>Grant Type</P></TD><TD><P>Client Credentials</P></TD></TR><TR><TD><P>Purpose</P></TD><TD><P>Technical User</P></TD></TR><TR><TD><P>Roles</P></TD><TD><P>space access must be given (e.g. by scope role in DSP)</P></TD></TR><TR><TD><P>Token Lifetime</P></TD><TD><P>e.g. 60 minutes</P></TD></TR></TBODY></TABLE><P>Save the <EM>Client ID</EM> and <EM>Client Secret </EM>— this is used in ABAP Cloud later on.</P><P><FONT size="5"><STRONG> 5 </STRONG><STRONG>Creating a Communication System in ABAP Cloud</STRONG></FONT></P><P>Now we configure the outbound technical connection from ABAP Cloud to Datasphere.</P><P><EM>Navigation: </EM>ABAP Environment Web Access → Communication Management → Communication Systems</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ABAP Cloud Web Access" style="width: 671px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371057i5B672EC77D24D22E/image-dimensions/671x269?v=v2" width="671" height="269" role="button" title="image.png" alt="ABAP Cloud Web Access" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">ABAP Cloud Web Access</span></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ABAP Cloud Communication Systems" style="width: 672px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371058i1807DF17EA76120F/image-dimensions/672x113?v=v2" width="672" height="113" role="button" title="image.png" alt="ABAP Cloud Communication Systems" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">ABAP Cloud Communication Systems</span></span></P><P>The hostname is your Datasphere tenant URL. Outbound OAuth URLs (like Token/Authentication) can be found on the Datasphere side under Administration -> App Integration (previous step).</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ABAP Cloud Communication Systems Details" style="width: 673px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371059iF14B49A880B877F1/image-dimensions/673x497?v=v2" width="673" height="497" role="button" title="image.png" alt="ABAP Cloud Communication Systems Details" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">ABAP Cloud Communication Systems Details</span></span></P><P>You have to add a user "OAuth 2.0 (Basic)":</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ABAP Cloud Communication Systems Users" style="width: 672px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371061i0EC89B23ACC3A7F5/image-dimensions/672x269?v=v2" width="672" height="269" role="button" title="image.png" alt="ABAP Cloud Communication Systems Users" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">ABAP Cloud Communication Systems Users</span></span></P><P><EM>Key fields:</EM></P><TABLE><TBODY><TR><TD><P><EM>Field</EM></P></TD><TD><P><EM>Example</EM></P></TD></TR><TR><TD><P>System ID</P></TD><TD><P>DSP_REST</P></TD></TR><TR><TD><P>Host Name</P></TD><TD><P><your-datasphere-host> (tenant URL)</P></TD></TR><TR><TD><P>Port</P></TD><TD><P>443</P></TD></TR><TR><TD><P>OAuth 2.0 Endpoints</P></TD><TD><P>From Datasphere for Token & Authorization URL (Audience URL not required)</P></TD></TR><TR><TD><P>Client ID</P></TD><TD><P>Datasphere OAuth Client ID</P></TD></TR><TR><TD><P>Client Secret</P></TD><TD><P>Datasphere Secret (you must take note of this when creating the OAuth Client)</P></TD></TR></TBODY></TABLE><P><FONT size="5"><STRONG>6 Creating a Communication Arrangement in SAP ABAP Cloud</STRONG></FONT></P><P>Now we bind the Communication System to a Communication Scenario that allows REST calls.</P><P><EM>Navigation: </EM>Communication Management → Communication Arrangements</P><P><EM>Arrangement Example: ZDSP_CS_REST</EM></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ABAP Cloud Communication Arrangements" style="width: 675px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371062i5D6E57F823E5F6EB/image-dimensions/675x138?v=v2" width="675" height="138" role="button" title="image.png" alt="ABAP Cloud Communication Arrangements" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">ABAP Cloud Communication Arrangements</span></span></P><P>Note the service path: The dynamic parts of the path are set from ABAP (this is to keep the space + task chain flexible and controllable from the actual caller: ABAP).</P><P><EM><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ABAP Cloud Communication Arrangements Detailed Configuration" style="width: 673px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371063i82A3F766212E7AA8/image-dimensions/673x334?v=v2" width="673" height="334" role="button" title="image.png" alt="ABAP Cloud Communication Arrangements Detailed Configuration" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">ABAP Cloud Communication Arrangements Detailed Configuration</span></span></EM></P><P><EM>What this provides:</EM></P><UL><LI>Preconfigured service URL</LI><LI>OAuth token handling by ABAP runtime</LI><LI>Secure outbound API consumption</LI></UL><P><FONT size="5"><STRONG>7 Calling the Datasphere Task Chain API from ABAP Cloud</STRONG></FONT></P><P>Now comes the fun part: consuming the REST API. Datasphere exposes endpoints such as:</P><UL><LI>POST /api/v1/datasphere/tasks/chains/<space_id>/run/<task_chain_technical_name></LI><LI>GET /api/v1/datasphere/tasks/logs/<space_id>/<log_id></LI></UL><P>Which triggers a new execution or reads the status of a task chain run.</P><P><EM>ABAP Cloud Implementation Pattern</EM></P><P>In ABAP Cloud when working with REST API to integrate external services, you typically:</P><OL><LI>Create HTTP destination automatically from the created Communication Arrangement</LI><LI>Use CL_WEB_HTTP_CLIENT_MANAGER</LI><LI>Perform POST/GET requests</LI><LI>Parse response (run ID, status)</LI></OL><P>The code snippet performs the following parts in more detail:</P><OL><LI><EM>Run as a class-run program</EM> (IF_OO_ADT_CLASSRUN) -> you can execute directly from ADT and print to the console.</LI><LI><EM>Locate the configured Communication Arrangement</EM> (bound to our Communication System DSP_REST) using CL_COM_ARRANGEMENT_FACTORY.</LI><LI><EM>Create an HTTP destination</EM> from the Communication Arrangement using CL_HTTP_DESTINATION_PROVIDER=>CREATE_BY_COMM_ARRANGEMENT.</LI><LI><EM>Create an HTTP client</EM> from the destination (CL_WEB_HTTP_CLIENT_MANAGER).</LI><LI><EM>POST</EM> request to the Task Chain “run” endpoint:</LI><UL><LI>triggers a new run</LI><LI>receives a JSON response containing a <EM>LogId</EM></LI></UL><LI><EM>WAIT</EM> a few seconds.</LI><LI><EM>GET</EM> the log endpoint using the returned <EM>LogId</EM>:</LI><UL><LI>retrieves the execution status (RUNNING, later SUCCESS / FAILED, etc.)</LI></UL><LI>Print everything in the <EM>ABAP Console</EM>.</LI></OL><P>ABAP-Code Snippet:</P><pre class="lia-code-sample language-abap"><code>CLASS zcl_dsp_rest_api DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
CLASS-DATA:
out TYPE REF TO if_oo_adt_classrun_out.
CLASS-METHODS:
call_dsp
RAISING
cx_http_dest_provider_error
cx_web_http_client_error.
ENDCLASS.
CLASS zcl_dsp_rest_api IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
zcl_dsp_rest_api=>out = out.
TRY.
call_dsp( ).
CATCH cx_web_http_client_error
cx_http_dest_provider_error INTO DATA(exception).
out->write( exception->get_text( ) ).
ENDTRY.
ENDMETHOD.
METHOD call_dsp.
TYPES: BEGIN OF ty_log,
logId TYPE i,
END OF ty_log.
DATA: ls_log TYPE ty_log.
DATA(communication_system) = 'DSP_REST'.
DATA(arrangement_factory) = cl_com_arrangement_factory=>create_instance( ).
DATA(comm_arrangement_range) = VALUE if_com_arrangement_factory=>ty_query-cs_id_range(
( sign = 'I' option = 'EQ' low = communication_system ) ).
arrangement_factory->query_ca(
EXPORTING
is_query = VALUE #( cs_id_range = comm_arrangement_range )
IMPORTING
et_com_arrangement = DATA(arrangements) ).
DATA(arrangement) = arrangements[ 1 ].
DATA(destination) = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'ZDSP_CS_REST'
service_id = 'ZDSP_REST_SRV_REST'
comm_system_id = arrangement->get_comm_system_id( ) ).
DATA(http_client) = cl_web_http_client_manager=>create_by_http_destination( destination ).
DATA(request) = http_client->get_http_request( ).
request->set_uri_path( '/chains/BDCPOC/run/TC_DIM2_A001' ).
DATA(response) = http_client->execute( if_web_http_client=>post ).
CALL METHOD /ui2/cl_json=>deserialize
EXPORTING
json = response->get_text( )
CHANGING
data = ls_log.
data(log_str) = CONV string( ls_log-logId ).
out->write( 'Task Chain started. LogId created:' && log_str ).
WAIT UP TO 10 SECONDS.
CONCATENATE '/logs/BDCPOC/' log_str INTO DATA(uri).
clear: request, response, http_client.
http_client = cl_web_http_client_manager=>create_by_http_destination( destination ).
request = http_client->get_http_request( ).
request->set_uri_path( uri ).
response = http_client->execute( if_web_http_client=>get ).
out->write( 'Task Chain Log REST API - via GET - Task Chain Status:' && response->get_text( ) ).
ENDMETHOD.
ENDCLASS.</code></pre><P>Once triggered:</P><UL><LI>Datasphere creates a new Task Chain run</LI><LI>Execution can be monitored in Datasphere UI</LI><LI>API returns run ID and status</LI></UL><P>Follow-up calls can fetch:</P><UL><LI>Execution status</LI><LI>Logs</LI><LI>Completion result</LI></UL><P><FONT size="5"><STRONG>8 Execution Results</STRONG></FONT></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ABAP Cloud Console Log: Results" style="width: 964px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371044iA43931A0457AB770/image-size/large?v=v2&px=999" role="button" title="image.png" alt="ABAP Cloud Console Log: Results" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">ABAP Cloud Console Log: Results</span></span></P><P>The Task Chain in Datasphere is in Running status:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SAP Datasphere Task Chain Running Status" style="width: 871px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/371046i89C5349410537884/image-size/large?v=v2&px=999" role="button" title="image.png" alt="SAP Datasphere Task Chain Running Status" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">SAP Datasphere Task Chain Running Status</span></span></P><P><FONT size="5"><STRONG>9 Conclusion/Outlook & Further aspects:</STRONG></FONT></P><P><STRONG>Overview of the major building blocks:</STRONG></P><TABLE><TBODY><TR><TD><P><STRONG>Capability</STRONG></P></TD><TD><P><STRONG>Result</STRONG></P></TD></TR><TR><TD><P>Secure Auth</P></TD><TD><P>OAuth 2.0 Client Credentials</P></TD></TR><TR><TD><P>No hardcoded secrets</P></TD><TD><P>via Communication Arrangements/System</P></TD></TR><TR><TD><P>Remote orchestration</P></TD><TD><P>Creating an end-to-end orchestrated chain of tasks</P></TD></TR><TR><TD><P>Cloud-native ABAP</P></TD><TD><P>Using appropriate ABAP Class APIs</P></TD></TR><TR><TD><P>Datasphere automation</P></TD><TD><P>Using Task Chain API</P></TD></TR></TBODY></TABLE><P><STRONG>Use Cases that the implementation enables:</STRONG></P><UL><LI>Nightly batch orchestration</LI><LI>Event-driven data loads</LI><LI>Cross-system workflows (this was our ultimate focus)</LI><LI>CI/CD data pipelines</LI><LI>API-driven analytical data refresh</LI></UL><P><STRONG>References</STRONG></P><UL><LI><SPAN>SAP Datasphere Task Chain API Documentation: </SPAN><A href="https://api.sap.com/api/DatasphereTasks/overview" target="_blank" rel="noopener noreferrer"><SPAN>https://api.sap.com/api/DatasphereTasks/overview</SPAN></A></LI><LI><SPAN>SAP Datasphere Task Chain Help Page: </SPAN><A href="https://help.sap.com/docs/SAP_DATASPHERE/c8a54ee704e94e15926551293243fd1d/274f2736465c4c48a091c675880502a2.html?locale=en-US" target="_blank" rel="noopener noreferrer"><SPAN>https://help.sap.com/docs/SAP_DATASPHERE/c8a54ee704e94e15926551293243fd1d/274f2736465c4c48a091c675880502a2.html?locale=en-US</SPAN></A></LI><LI><SPAN>ABAP Cloud - how to call external APIs: </SPAN><A href="https://jacekw.dev/blog/2022/oauth-client-credentials-from-abap-cloud/" target="_blank" rel="noopener nofollow noreferrer"><SPAN>https://jacekw.dev/blog/2022/oauth-client-credentials-from-abap-cloud/</SPAN></A></LI></UL><P><STRONG>These are your main takeaways to further <U>sharpen</U> the solution:</STRONG></P><UL><LI>Use short token lifetimes</LI><LI>Separate technical OAuth users</LI><LI>Monitor task runs via API from ABAP</LI><LI>Handle retries & failures in ABAP</LI><LI>Log run IDs for observability</LI></UL><P>If you have any aspects, comments and/or concerns, please raise them and let's discuss. I'm happy and proud that this article was written by at least 90% human and only 10% AI <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></P>2026-02-10T16:44:24.061000+01:00