https://raw.githubusercontent.com/ajmaradiaga/feeds/main/scmt/topics/ABAP-RESTful-Application-Programming-Model-blog-posts.xml SAP Community - ABAP RESTful Application Programming Model 2026-04-17T20:00:09.447749+00:00 python-feedgen ABAP RESTful Application Programming Model blog posts in SAP Community https://community.sap.com/t5/abap-blog-posts/how-to-create-a-rap-application-using-a-parameterized-cds-view/ba-p/14332860 How to create a RAP application using a parameterized CDS view 2026-03-17T10:38:18.358000+01:00 _Kayky_Mattos_ https://community.sap.com/t5/user/viewprofilepage/user-id/885395 <P>Hello everyone, recently I came across a somewhat unusual requirement where I needed to use a parameter in a RAP application. As we know, this isn’t natively supported with Fiori Elements, so I had to look for a way to make it work. After some research and a few tests, I ended up with a solution that I consider quite innovative: using a Fiori Freestyle approach.</P><P>The best part is that it requires only a minimal amount of code, something that any ABAP developer can implement without much effort, and it becomes extremely useful whenever you need to work with parameters that are not directly linked to the data exposed by the CDS view.</P><P>The idea behind this solution is simple:</P><P>Since projection views in RAP do not support CDS parameters directly, we expose a parameterized interface CDS view through a service definition and service binding.</P><P>Then, using a Fiori Freestyle application with a Smart Table, we dynamically inject the parameter into the table binding path before the data request is sent to the backend.</P><P>This allows the RAP service to consume the parameterized CDS view without requiring complex workarounds.</P><P>&nbsp;</P><P>Let’s go through the required steps:</P><P>&nbsp;</P><P>1)&nbsp;&nbsp;Since it’s not possible to add <EM>WITH PARAMETERS</EM> to a projection view, we need to create an interface CDS view and use it as our “projection.”</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_2-1771590856902.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374759i98B6303DAD9181A7/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_2-1771590856902.png" alt="matttosz_2-1771590856902.png" /></span></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: '&lt;your cds description&gt;' @Metadata.ignorePropagatedAnnotations: false @Metadata.allowExtensions: true define view entity &lt;your_parameterized_interface_cds&gt; with parameters p_date : abap.char(8) as select from &lt;your_source_cds_or_table&gt; ( p_date : $parameters.p_date ) { key &lt;your_key_field&gt;, &lt;your_field_1&gt;, &lt;your_field_2&gt;, &lt;your_field_3&gt; }</code></pre><P>2) Create our service definition normally</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_4-1771591575874.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374773iA5AEE40CC9D85AE3/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_4-1771591575874.png" alt="matttosz_4-1771591575874.png" /></span></P><P>3) Create Service Binding ( Odata V2 )</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_5-1771591628061.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374774i451EFA45F05042DF/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_5-1771591628061.png" alt="matttosz_5-1771591628061.png" /></span></P><P>4) Now we have to create the freestyle app with the fiori generator application</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_6-1771591700764.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374775i8579D8356C446085/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_6-1771591700764.png" alt="matttosz_6-1771591700764.png" /></span></P><P>5) Now just connect to the S4 and select the service binding created earlier</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_7-1771591755516.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374776iED1814F045E94A2F/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_7-1771591755516.png" alt="matttosz_7-1771591755516.png" /></span></P><P>At this point, you can see the project structure in the Explorer panel on the left. We’ll focus only on the <EM>view</EM> and <EM>controller</EM> folders.</P><P>&nbsp;</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_2-1771594013215.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374794i835B647EA4E7E861/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_2-1771594013215.png" alt="matttosz_2-1771594013215.png" /></span></P><P>&nbsp;</P><P>6)</P><P>First, we need to create our Smart Table in the <STRONG>view</STRONG> folder. Inside the <STRONG>SmartFilterBar</STRONG>, we must set the <CODE>entitySet</CODE> from our service binding. After that, we add a <CODE>customControl</CODE>&nbsp;in my case, I used a <STRONG>DatePicker</STRONG>, but you can use any control that fits your requirement. I also attached an <CODE>onDateChange</CODE> event to the DatePicker so we can capture and manipulate the selected date whenever it changes.</P><P>Within the <STRONG>Smart Table</STRONG>, we also need to define the same <CODE>entitySet</CODE>. Another very important point here is the <CODE>beforeRebindTable</CODE> event. This is the hook that allows us to pass the parameter to the CDS view. It will trigger the <CODE>onBeforeRebindTable</CODE> method in the controller, where we can dynamically inject the parameter into the binding before the data is requested.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_3-1771594045964.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374795i890A0F22CFECFBAA/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_3-1771594045964.png" alt="matttosz_3-1771594045964.png" /></span></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_10-1771592364211.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374781i3F840A283FD3C8AD/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_10-1771592364211.png" alt="matttosz_10-1771592364211.png" /></span></P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View xmlns:table="sap.ui.table" xmlns="sap.ui.comp.smartfilterbar" xmlns:f="sap.f" xmlns:m="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:smartTable="sap.ui.comp.smarttable" controllerName="&lt;your.app.namespace&gt;.controller.Main" height="100%"&gt; &lt;f:DynamicPage id="dynamicPage" fitContent="true" title="{i18n&gt;appTitle}"&gt; &lt;f:header&gt; &lt;f:DynamicPageHeader&gt; &lt;SmartFilterBar id="smartFilterBar" entitySet="&lt;your_entity_set&gt;"&gt; &lt;controlConfiguration&gt; &lt;ControlConfiguration key="&lt;your_parameter_field&gt;" visibleInAdvancedArea="true" label="&lt;your_filter_label&gt;" mandatory="true"&gt; &lt;customControl&gt; &lt;m:DatePicker id="datePicker" displayFormat="dd/MM/yyyy" valueFormat="yyyyMMdd" placeholder="dd/mm/yyyy" width="100%" required="true" change="onDateChange"/&gt; &lt;/customControl&gt; &lt;/ControlConfiguration&gt; &lt;/controlConfiguration&gt; &lt;/SmartFilterBar&gt; &lt;/f:DynamicPageHeader&gt; &lt;/f:header&gt; &lt;f:content&gt; &lt;smartTable:SmartTable id="smartTable" beforeRebindTable="onBeforeRebindTable" showRowCount="true" enableAutoBinding="false" entitySet="&lt;your_entity_set&gt;" smartFilterId="smartFilterBar" useVariantManagement="true" tableType="Table" initiallyVisibleFields="&lt;your_fields&gt;" enableAutoColumnWidth="true" showTablePersonalisation="true"&gt; &lt;smartTable:customToolbar&gt; &lt;m:OverflowToolbar design="Transparent"&gt; &lt;m:ToolbarSpacer/&gt; &lt;/m:OverflowToolbar&gt; &lt;/smartTable:customToolbar&gt; &lt;/smartTable:SmartTable&gt; &lt;/f:content&gt; &lt;/f:DynamicPage&gt; &lt;/mvc:View&gt;</code></pre><P>7) In this step, we implement the logic inside the <STRONG><CODE>onBeforeRebindTable</CODE></STRONG> method, which is responsible for dynamically passing the parameter to the CDS view before the Smart Table triggers the data request.</P><P>First, the code retrieves the value entered in the <STRONG>DatePicker</STRONG> using <CODE>byId</CODE>. This value will be used as the parameter for the CDS. A local variable (<CODE>sDate</CODE>) is initialized to store the selected date.</P><P>Next, a validation is performed to ensure that the date field is filled in. Since this parameter is mandatory for the query, if no value is provided the DatePicker is set to the <STRONG>Error</STRONG> state and a validation message is displayed. At the same time, the table binding is stopped using:</P><P><CODE>oBindingParams.preventTableBind = true;</CODE></P><P>This prevents the Smart Table from calling the backend without the required parameter, avoiding unnecessary requests and possible errors.</P><P>If the date is correctly filled in, any previous error state is cleared by setting the ValueState back to <STRONG>None</STRONG>.</P><P>Finally, the most important part happens: the table binding path is updated dynamically. Here we pass the selected date to the parameterized CDS by changing the binding path of the Smart Table:</P><P><CODE>setTableBindingPath("/&lt;EntitySet&gt;(p_date='${sDate}')/Set")</CODE></P><P>This is what allows the RAP application to consume a <STRONG>CDS with parameters</STRONG>, because the parameter is injected at runtime just before the data is requested from the backend.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_12-1771593060014.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374786iF7D8D4780A379928/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_12-1771593060014.png" alt="matttosz_12-1771593060014.png" /></span></P><pre class="lia-code-sample language-javascript"><code>onBeforeRebindTable: function (oEvent) { let oDatePicker = this.byId("datePicker"); let sDate = ""; if (oDatePicker) { sDate = oDatePicker.getValue(); } if (!sDate) { if (oDatePicker) { oDatePicker.setValueState("Error"); oDatePicker.setValueStateText("Please inform the date."); } let oBindingParams = oEvent.getParameter("bindingParams"); if (oBindingParams) { oBindingParams.preventTableBind = true; } return; } if (oDatePicker) { oDatePicker.setValueState("None"); } this.getView() .byId("smartTable") .setTableBindingPath("/&lt;your_entity_set&gt;(p_date='" + sDate + "')/Set"); }</code></pre><P>And&nbsp;<SPAN>Vooooalá!</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="matttosz_13-1771593754921.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/374791iB89691EFDEECC4B4/image-size/medium?v=v2&amp;px=400" role="button" title="matttosz_13-1771593754921.png" alt="matttosz_13-1771593754921.png" /></span></P><P>Conclusion</P><P>Although RAP and Fiori Elements provide powerful tools for building enterprise applications, there are still scenarios where certain features are not directly supported, such as consuming parameterized CDS views in projection layers.</P><P>By combining RAP services with a lightweight Fiori Freestyle approach, we can overcome this limitation in a simple and flexible way. Using events like beforeRebindTable and dynamically modifying the binding path allows parameters to be passed to the CDS view at runtime.</P><P>This approach requires only a small amount of additional UI code and can be implemented quickly by ABAP developers who are already familiar with RAP and Fiori.</P><P>I hope this solution helps others facing similar challenges when working with parameterized CDS views in RAP applications.</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2026-03-17T10:38:18.358000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/rap-unmanaged-draft-early-numbering-and-associations-for-unique-ids/ba-p/14351767 RAP Unmanaged Draft: Early Numbering and Associations for Unique IDs 2026-03-19T15:23:41.063000+01:00 vidyadharsp https://community.sap.com/t5/user/viewprofilepage/user-id/1913267 <P><FONT size="5" color="#808080"><STRONG>INTRODUCTION</STRONG></FONT></P><P>In RAP,&nbsp;Implementing&nbsp;numbering&nbsp;mechanisms during these operations is essential to ensure unique identifiers for newly created entities.<BR />This blog is exposure the best for numbering during Creation and Creation by Association, in Unmanaged Scenario with Draft. How to handle number creation with Draft Scenario's.</P><P><U><STRONG><FONT size="4">Some Information about Numbering<BR /></FONT></STRONG></U>Basically, Numbering is the concept to process assigning unique values to the key fields of business entities during Create and Create By Association Operations.<BR /><BR /><U><STRONG>Unmanaged Early Numbering:</STRONG></U></P><UL><LI>Developers need to implement custom code logic to create and assign keys values during the creation and create by association operation.&nbsp;</LI><LI><STRONG><SPAN>%is_draft</SPAN></STRONG><SPAN>&nbsp;– It is a&nbsp;component&nbsp;of the transactional key (%tky) in RAP.</SPAN><SPAN>&nbsp;</SPAN><UL><LI><SPAN>It tells the framework whether to&nbsp;operate&nbsp;on the draft table or the active persistent table for a given operation.</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>%is_draft&nbsp;=&nbsp;mk-on reads from draft table</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>%is_draft&nbsp;=&nbsp;mk-off reads from active table</SPAN><SPAN>&nbsp;</SPAN></LI></UL></LI></UL><P><FONT color="#FF0000"><U><STRONG>Problem Statement</STRONG></U></FONT></P><P><FONT color="#000000">A sales clerk begins creating a Sales Order in Fiori. The order header is saved as a draft, but items need to be added immediately under that draft. To ensure consistency, the system must generate a unique Sales Order ID right away and assign incremental item numbers without waiting for activation.</FONT></P><P><SPAN><STRONG>Importance</STRONG>:</SPAN></P><UL><LI><P><SPAN>Early numbering guarantees <STRONG>unique identification</STRONG> across drafts and active records.</SPAN></P></LI><LI><P><SPAN>Associations ensure <STRONG>header–item linkage</STRONG> is preserved.</SPAN></P></LI><LI><P><SPAN>Draft handling allows users to <STRONG>save incomplete transactions safely</STRONG> and activate later without losing data integrity.</SPAN></P></LI></UL><P><U><STRONG>Behavior Definition of Root Entity</STRONG></U></P><P><CODE>early numbering</CODE> → enables key generation during root creation.</P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_i_vd_head unique; strict ( 2 ); with draft; define behavior for ZI_VD_HEAD alias Header draft table zdft_vd_head lock master total etag CreatedOn authorization master ( instance ) early numbering // Early numbering declaration in root entity { create; update; delete; association _item { create; } field ( readonly : update ) CustNo; draft action Edit; draft action Activate optimized; draft action Resume; draft action Discard; draft determine action Prepare; } mapping for zvd_t_hsale { CustNo = cust_no; SalesOrder = sales_order; CreatedOn = created_on; EntryTime = entry_time; Name = name; DocCat = doc_cat; }</code></pre><P><BR /><U><STRONG>Behavior Definition of Item Entity</STRONG></U></P><P><CODE>early numbering</CODE> → enables key generation during item creation.</P><pre class="lia-code-sample language-abap"><code>define behavior for ZI_VD_ITEM alias Item draft table zdft_vd_item lock dependent by _head authorization dependent by _head early numbering // Early numbering declaration in item entity { update; delete; field ( readonly ) CustNo; association _head; mapping for zvd_t_isale { CustNo = cust_no; SalesOrder = sales_order; DocItem = doc_item; MatNumber = mat_number; MatGroup = mat_group; CreatedOn = created_on; } }</code></pre><P><U><STRONG>Behavior Pool Implementation</STRONG></U></P><P>This method ensures that every new entity gets a unique identifier (<CODE>CustNo</CODE>) during creation if it wasn’t already provided.</P><P><STRONG>UUID Generation:</STRONG></P><UL><LI><P><CODE>cl_system_uuid=&gt;create_uuid_x16_static( )</CODE> generates a globally unique key.</P></LI><LI><P>This avoids collisions and guarantees uniqueness across drafts and active records.</P></LI></UL><P><U><STRONG>Mapped Root:</STRONG></U></P><UL><LI><P><CODE>%cid</CODE> → correlates client-side temporary IDs with backend entities.</P></LI><LI><P><CODE>%key</CODE> → holds the generated or existing key.</P></LI><LI><P><CODE>%is_draft</CODE> → ensures draft state is preserved.</P></LI><LI><P><CODE>CustNo</CODE> → the actual business key stored in the entity.</P></LI></UL><pre class="lia-code-sample language-abap"><code>METHOD earlynumbering_create. LOOP AT entities INTO DATA(entity). DATA(lv_custno) = entity-custno. IF lv_custno IS INITIAL. TRY. lv_custno = cl_system_uuid=&gt;create_uuid_x16_static( ). CATCH cx_uuid_error. "handle exception ENDTRY. ENDIF. APPEND VALUE #( %cid = entity-%cid %key = VALUE #( custno = lv_custno ) %is_draft = entity-%is_draft custno = lv_custno ) TO mapped-header. ENDLOOP. ENDMETHOD.</code></pre><P><STRONG>Mapped Item:</STRONG></P><P>This method handles early numbering when creating items via association (<CODE>CREATE-BY-ASSOCIATION</CODE>).</P><UL><LI><P>First, it retrieves the maximum <CODE>cust_no</CODE> from the database table <CODE>zvd_t_isale</CODE> for the current entity.</P></LI></UL><UL><LI><P><CODE>%cid</CODE> → client-side correlation ID.</P></LI><LI><P><CODE>%key</CODE> → generated key with <CODE>CustNo</CODE>.</P></LI><LI><P><CODE>%is_draft</CODE> → preserves draft state.</P></LI><LI><P><CODE>CustNo</CODE> → ensures linkage to the root entity.</P></LI></UL><pre class="lia-code-sample language-abap"><code>METHOD earlynumbering_cba_item. LOOP AT entities INTO DATA(entity). " Get current max Customer Number from DB SELECT MAX( cust_no ) INTO (lv_max) FROM zvd_t_isale WHERE cust_no = -%key-custno. LOOP AT entity-%target INTO DATA(t_item). APPEND VALUE #( %cid = t_item-%cid %key = VALUE #( custno = entity-%key-custno ) %is_draft = t_item-%is_draft custno = entity-%key-custno ) TO mapped-item. ENDLOOP. ENDLOOP. ENDMETHOD.</code></pre><UL><LI><P>Item inherits lock and authorization from the header.</P></LI><LI><P>Early numbering ensures item keys are generated consistently.</P></LI><LI><P>Association links items back to the header BO.</P></LI></UL><P><U><STRONG>Persistence Methods</STRONG></U></P><UL class="lia-list-style-type-square"><LI><U><STRONG>Create Method</STRONG></U></LI></UL><pre class="lia-code-sample language-abap"><code>METHOD create. LOOP AT entities INTO DATA(entity). DATA(header) = CORRESPONDING zvd_t_hsale( entity MAPPING FROM ENTITY ). header-created_on = sy-datum. header-entry_time = sy-datum. header-name = sy-uname. APPEND header TO gt_hsale. ENDLOOP. ENDMETHOD.</code></pre><UL><LI><U><STRONG>Create-By-Association Method</STRONG></U></LI></UL><pre class="lia-code-sample language-abap"><code>METHOD cba. LOOP AT entities_cba INTO DATA(entity_cba). LOOP AT entity_cba-%target INTO DATA(target_item). DATA(item) = CORRESPONDING zvd_t_isale( target_item MAPPING FROM ENTITY ). item-cust_no = entity_cba-%key-CustNo. item-created_on = sy-datum. APPEND item TO gt_isale. ENDLOOP. ENDLOOP. ENDMETHOD.</code></pre><P><U><STRONG>Result</STRONG></U></P><P><U><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="o1.png" style="width: 975px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/385288i3FC295D0198CCAE8/image-size/large?v=v2&amp;px=999" role="button" title="o1.png" alt="o1.png" /></span></STRONG></U></P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId-1792267916">&nbsp;</H2><H2 id="toc-hId-1595754411">&nbsp;</H2><H2 id="toc-hId-1399240906">&nbsp;</H2><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><FONT size="5" color="#999999"><STRONG>Conclusion</STRONG></FONT></P><P><SPAN>By combining <STRONG>Draft Handling, Associations, and Early Numbering</STRONG>, RAP unmanaged scenarios can support complex business objects like Sales Orders, Purchase Orders. This approach ensures flexibility, control, and consistency in transactional applications.<BR /><BR />Thank You...</SPAN></P> 2026-03-19T15:23:41.063000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/rap-factory-action-to-copy-billing-header-and-its-item-s-using-create-by/ba-p/14356589 RAP: Factory Action to copy Billing Header and its Item(s) using Create by association 2026-03-25T10:31:08.744000+01:00 SinghMohit https://community.sap.com/t5/user/viewprofilepage/user-id/1892782 <P>In this blog, I am going to explain how we can use <STRONG>create by association</STRONG> with <STRONG>factory action</STRONG> in SAP RAP environment for complex use case like <EM>copying duplicate billing header with its associated child entities</EM>.</P><P>Let's understand what action in RAP is, and what is factory action first.</P><P>If you want to perform any operation other than those four basic operations (CRUD), we can use action keyword in RAP. It helps us to do custom action like approve billing doc or change any field value like status.</P><P>We have three types of actions in RAP:</P><OL><LI>Instance Action: we need to select a record to perform action.</LI><LI>Static Actions: that are <STRONG>not tied to a specific instance</STRONG>.</LI><LI>Factory Actions: used for creating the copy of already existing instance. We will see this more in details.</LI></OL><P><STRONG>Create by Association:&nbsp;</STRONG>In scenario where we cannot create child entity as it is depend on header, we need to use create by association. Used to create deep entity.</P><P>So, let's start with the steps to generate our basic application. Please have a look.</P><P>1. My header table look like this:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Billing Header' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table ztb_billheader { key client : abap.clnt not null; key bill_id : abap.numc(10) not null; bill_type : abap.char(4); bill_date : abap.dats; customer_id : kunnr; @Semantics.amount.currencyCode : 'ztb_billheader.currency' net_amount : abap.curr(15,2); currency : waers; sales_org : vkorg; createdby : syuname; createdat : timestamp; lastchangedby : syuname; lastchangedat : timestamp; locallastchangedat : timestamp; status : abap.char(1); statustext : abap.char(10); }</code></pre><P>I am not using managed numbering here as I want custom Bill Id.</P><P>2. My Item table look like this:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Billing Item' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table ztb_billitem { key client : abap.clnt not null; key bill_id : abap.char(10) not null; key item_no : abap.numc(6) not null; material_id : matnr; description : abap.char(40); @Semantics.quantity.unitOfMeasure : 'ztb_billitem.uom' quantity : abap.quan(13,3); @Semantics.amount.currencyCode : 'ztb_billitem.currency' item_amount : abap.curr(15,2); currency : waers; uom : meins; createdby : syuname; createdat : timestamp; lastchangedby : syuname; lastchangedat : timestamp; locallastchangedat : timestamp; }</code></pre><P>3. I have created interface view on top of header table: You can skip that also. It looks like this:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface View for Bill header' @Metadata.ignorePropagatedAnnotations: true define view entity ZI_BillHeader as select from ztb_billheader { key bill_id as BillId, bill_type as BillType, bill_date as BillDate, customer_id as CustomerId, @Semantics.amount.currencyCode: 'Currency' net_amount as NetAmount, currency as Currency, sales_org as SalesOrg, createdby as Createdby, createdat as Createdat, lastchangedby as Lastchangedby, lastchangedat as Lastchangedat, locallastchangedat as Locallastchangedat, status as status, statustext as StatusText }</code></pre><P>4. I have created root view on top of header interface view:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Root View Bill Header' @Metadata.ignorePropagatedAnnotations: true define root view entity ZR_BillHeader as select from ZI_BillHeader composition[0..*] of ZI_BillItem as _Item { key BillId, BillType, BillDate, CustomerId, @Semantics.amount.currencyCode: 'Currency' NetAmount, Currency, SalesOrg, Createdby, Createdat, Lastchangedby, Lastchangedat, Locallastchangedat, status, StatusText, _Item // Make association public }</code></pre><P>5. I have created interface view on top of item table:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface View for Bill Item' @Metadata.ignorePropagatedAnnotations: true define view entity ZI_BillItem as select from ztb_billitem association to parent ZR_BillHeader as _header on $projection.BillId = _header.BillId { key bill_id as BillId, key item_no as ItemNo, material_id as MaterialId, description as Description, @Semantics.quantity.unitOfMeasure: 'Uom' quantity as Quantity, @Semantics.amount.currencyCode: 'Currency' item_amount as ItemAmount, currency as Currency, uom as Uom, createdby as Createdby, createdat as Createdat, lastchangedby as Lastchangedby, lastchangedat as Lastchangedat, locallastchangedat as Locallastchangedat, _header }</code></pre><P>6. Created projection view on top of root view:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection View Bill Header' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true define root view entity ZP_BillHeader provider contract transactional_query as projection on ZR_BillHeader { key BillId, BillType, BillDate, CustomerId, @Semantics.amount.currencyCode: 'Currency' NetAmount, Currency, SalesOrg, Createdby, Createdat, Lastchangedby, Lastchangedat, Locallastchangedat, @ObjectModel.text.element: ['StatusText'] status, StatusText, /* Associations */ _Item : redirected to composition child ZP_BillItem }</code></pre><P>7. Similarly created projection view for item entity:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection View Bill Item' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true define view entity ZP_BillItem as projection on ZI_BillItem { key BillId, key ItemNo, MaterialId, Description, @Semantics.quantity.unitOfMeasure: 'Uom' Quantity, @Semantics.amount.currencyCode: 'Currency' ItemAmount, Currency, Uom, Createdby, Createdat, Lastchangedby, Lastchangedat, Locallastchangedat, /* Associations */ _header : redirected to parent ZP_BillHeader }</code></pre><P>8. My metadata looks like this:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span> (Please make sure I haven't given labels for each field, so in UI for few field it will missing. My core agenda is to elaborate factory action)&nbsp;</P><P>Header:&nbsp;</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE @UI.headerInfo:{ typeName: 'Billing Document', typeNamePlural: 'Billing Documents', title: { type: #STANDARD, label: 'Biiling Document', value: 'BillId' }} annotate entity ZP_BillHeader with { @UI.facet: [{ //id: 'BillDocHeader', label: 'Billing header', position: 10, purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE }, { //id: 'BillDocItem', label: 'Bill Item', position: 20, purpose: #STANDARD, type: #LINEITEM_REFERENCE, targetElement: '_Item' }] @UI.lineItem: [{ position:10 }, { position: 10, type: #FOR_ACTION, dataAction: 'ApproveBill', label: 'Approve Bill' }, { position: 20, type: #FOR_ACTION, dataAction: 'CopyBilldoc', label: 'Copy Bill' }] @UI.selectionField: [{position: 1 }] @UI.identification: [{ position: 1 }] BillId; @UI.lineItem: [{ position:20 }] @UI.identification: [{ position: 2 }] BillType; @UI.lineItem: [{ position:30 }] BillDate; @UI.lineItem: [{ position:40 }] @UI.identification: [{ position: 3 }] CustomerId; @UI.lineItem: [{ position:50 }] @UI.identification: [{ position: 4 }] NetAmount; @UI.lineItem: [{ position:60 }] @UI.identification: [{ position: 5 }] Currency; @UI.lineItem: [{ position:70 }] @UI.identification: [{ position: 6 }] SalesOrg; @UI.lineItem: [{ position:80 }] @UI.identification: [{ position: 7 }] @UI.textArrangement: #TEXT_ONLY status; }</code></pre><P>Item:&nbsp;</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity ZP_BillItem with { <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.facet: [ { id: 'BillDocItem', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Billing Doc Item', position: 10 }] @UI.lineItem: [{ position:10 }] @UI.identification: [{ position: 1 }] BillId; @UI.lineItem: [{ position:20 }] @UI.identification: [{ position: 1 }] ItemNo; @UI.lineItem: [{ position:30 }] @UI.identification: [{ position: 1 }] MaterialId; @UI.lineItem: [{ position:40 }] Description; @UI.lineItem: [{ position:50 }] Quantity; @UI.lineItem: [{ position:70 }] ItemAmount; @UI.lineItem: [{ position:80 }] Currency; @UI.lineItem: [{ position:60 }] Uom; @UI.lineItem: [{ position:90 }] Createdby; @UI.lineItem: [{ position:100 }] Createdat; @UI.lineItem: [{ position:110 }] /* Associations */ _header; }</code></pre><P>Now we have basic structure ready !!! Main steps for our topic start from here.</P><P>9. Create behavior definition on top of root entity. Just right click on the root view entity and select new behavior definition as shown below&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SinghMohit_0-1774326723292.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/388127iE8EB6E4A780DEAE1/image-size/medium?v=v2&amp;px=400" role="button" title="SinghMohit_0-1774326723292.png" alt="SinghMohit_0-1774326723292.png" /></span></P><P>Once behavior definition is generated, use quick fix to implement behavior class.&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SinghMohit_1-1774326927563.png" style="width: 576px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/388128iB3A52BE7047C4E30/image-dimensions/576x59?v=v2" width="576" height="59" role="button" title="SinghMohit_1-1774326927563.png" alt="SinghMohit_1-1774326927563.png" /></span></P><P>10. Now add below syntax in behavior definition:</P><pre class="lia-code-sample language-abap"><code> factory action CopyBilldoc [1];</code></pre><P>The whole behavior definition looks like this:&nbsp;</P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_r_billheader unique; strict ( 2 ); with draft; define behavior for ZR_BillHeader //alias &lt;alias_name&gt; persistent table ztb_billheader draft table ztb_billhdr_drf lock master total etag Lastchangedat authorization master ( instance, global ) etag master Locallastchangedat { create; update; delete; draft action Edit; draft action Resume; draft determine action Prepare; draft action Activate optimized; draft action Discard; field ( readonly : update, mandatory : create ) BillId; action ( features : instance ) ApproveBill result [1] $self; factory action CopyBilldoc [1]; side effects { action CopyBilldoc affects $self; } association _Item { create; with draft; } mapping for ztb_billheader { BillId = bill_id; BillType = bill_type; BillDate = bill_date; CustomerId = customer_id; NetAmount = net_amount; Currency = currency; SalesOrg = sales_org; CreatedBy = createdby; CreateDat = createdat; LastChangedBy = lastchangedby; LastChangeDat = lastchangedat; LocalLastChangeDat = locallastchangedat; status = status; StatusText = statustext; } } define behavior for ZI_BillItem //alias &lt;alias_name&gt; persistent table ztb_billitem draft table ztb_billitem_drf lock dependent by _header authorization dependent by _header //etag master &lt;field_name&gt; { update; delete; field ( readonly : update, mandatory : create ) BillId, ItemNo; association _header { with draft; } mapping for ztb_billitem { BillId = bill_id; ItemNo = item_no; MaterialId = material_id; Description = description; Quantity = quantity; ItemAmount = item_amount; Currency = currency; Uom = uom; CreatedBy = createdby; CreateDat = createdat; LastChangedBy = lastchangedby; LastChangeDat = lastchangedat; LocalLastChangeDat = locallastchangedat; } }</code></pre><P>Here, I have added extra things like draft, custom action and side effects. But for now, just focus on the line number 24.</P><P>11. Activate and use quick fix to generate this method in behavior class.&nbsp;</P><P>12. Use below code inside the generated factory action method.&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>METHOD CopyBilldoc. " Declaration of internal tables. DATA: lt_bill TYPE TABLE FOR CREATE ZR_BillHeader\\ZR_BillHeader, lt_billitem TYPE TABLE FOR CREATE ZR_BillHeader\_item. DATA: lv_billId TYPE c LENGTH 10. " Reading highest existing bill id and incrementing it by 1 to use as new bill id SELECT BillId FROM ZR_BillHeader ORDER BY BillId DESCENDING INTO _billid UP TO 1 ROWS. ENDSELECT. lv_billId = lv_billId + 1. " Read by association READ ENTITIES OF ZR_BillHeader IN LOCAL MODE ENTITY ZR_BillHeader ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_data1) ENTITY ZR_BillHeader BY \_item ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_data2) FAILED failed. " Appending to create a new structure LOOP AT lt_data1 ASSIGNING FIELD-SYMBOL(&lt;ls_data1&gt;). APPEND VALUE #( %cid = keys[ KEY entity %key = &lt;ls_data1&gt;-%key ]-%cid "%is_draft = keys[ KEY entity %key = &lt;ls_data1&gt;-%key ]-%param-%is_draft %data = CORRESPONDING #( &lt;ls_data1&gt; EXCEPT BillId ) ) TO lt_bill ASSIGNING FIELD-SYMBOL(&lt;new_bill_doc&gt;). &lt;new_bill_doc&gt;-BillId = CONV #( lv_billId ). &lt;new_bill_doc&gt;-BillDate = cl_abap_context_info=&gt;get_system_date( ). LOOP AT lt_data2 INTO DATA(ls_data2). APPEND VALUE #( %cid_ref = &lt;new_bill_doc&gt;-%cid "BillId = &lt;new_bill_doc&gt;-BillId %target = VALUE #( ( %cid = &lt;new_bill_doc&gt;-%cid BillId = &lt;new_bill_doc&gt;-BillId MaterialId = ls_data2-MaterialId ItemNo = ls_data2-ItemNo ) ) ) TO lt_billitem ASSIGNING FIELD-SYMBOL(&lt;new_bill_item&gt;). ENDLOOP. ENDLOOP. "Create BO instance by copy. MODIFY ENTITIES OF ZR_BillHeader IN LOCAL MODE ENTITY ZR_BillHeader CREATE FIELDS ( BillId BillDate BillType Currency CustomerId NetAmount SalesOrg ) WITH VALUE #( ( %cid = &lt;new_bill_doc&gt;-%cid BillId = &lt;new_bill_doc&gt;-BillId BillDate = &lt;new_bill_doc&gt;-BillDate Currency = &lt;new_bill_doc&gt;-Currency CustomerId = &lt;new_bill_doc&gt;-CustomerId NetAmount = &lt;new_bill_doc&gt;-NetAmount SalesOrg = &lt;new_bill_doc&gt;-SalesOrg ) ) CREATE BY \_item FIELDS ( ItemNo MaterialId ) WITH VALUE #( ( %cid_ref = &lt;new_bill_doc&gt;-%cid %target = VALUE #( FOR i IN lt_billitem FOR j IN i-%target ( %cid = j-ItemNo ItemNo = j-ItemNo MaterialId = j-MaterialId ) ) ) ) MAPPED DATA(mapped_create). " Set values to front end. mapped-ZR_BillHeader = mapped_create-ZR_BillHeader. ENDMETHOD.</code></pre><P>The whole my behavior class looks like this:&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>CLASS lhc_ZR_BillHeader DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR ZR_BillHeader RESULT result. METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION IMPORTING REQUEST requested_authorizations FOR ZR_BillHeader RESULT result. METHODS ApproveBill FOR MODIFY IMPORTING keys FOR ACTION ZR_BillHeader~ApproveBill RESULT result. METHODS get_instance_features FOR INSTANCE FEATURES IMPORTING keys REQUEST requested_features FOR ZR_BillHeader RESULT result. METHODS copybilldoc FOR MODIFY IMPORTING keys FOR ACTION zr_billheader~copybilldoc. ENDCLASS. CLASS lhc_ZR_BillHeader IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD get_global_authorizations. ENDMETHOD. METHOD ApproveBill. MODIFY ENTITIES OF ZR_BillHeader IN LOCAL MODE ENTITY ZR_BillHeader UPDATE FROM VALUE #( FOR key IN keys ( %tky = key-%tky status = 'A' " Approved StatusText = 'Approved' %control-status = if_abap_behv=&gt;mk-on %control-StatusText = if_abap_behv=&gt;mk-on ) ) FAILED failed REPORTED reported. "Read changed data for action result READ ENTITIES OF ZR_BillHeader IN LOCAL MODE ENTITY ZR_BillHeader ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(billheaders). result = VALUE #( FOR billheader IN billheaders ( %tky = billheader-%tky %param = billheader ) ). ENDMETHOD. METHOD get_instance_features. READ ENTITIES OF ZR_BillHeader IN LOCAL MODE ENTITY ZR_BillHeader FIELDS ( BillId status ) WITH CORRESPONDING #( keys ) RESULT DATA(BillHeaders) FAILED failed. result = VALUE #( FOR BillHeader IN BillHeaders ( %key = BillHeader-%key %features-%action-ApproveBill = COND #( WHEN BillHeader-status = 'A' THEN if_abap_behv=&gt;fc-o-disabled ELSE if_abap_behv=&gt;fc-o-enabled ) ) ). ENDMETHOD. METHOD CopyBilldoc. " Copy records assigned to internal table. DATA: lt_bill TYPE TABLE FOR CREATE ZR_BillHeader\\ZR_BillHeader, lt_billitem TYPE TABLE FOR CREATE ZR_BillHeader\_item. DATA: lv_billId TYPE c LENGTH 10. SELECT BillId FROM ZR_BillHeader ORDER BY BillId DESCENDING INTO _billid UP TO 1 ROWS. ENDSELECT. lv_billId = lv_billId + 1. READ ENTITIES OF ZR_BillHeader IN LOCAL MODE ENTITY ZR_BillHeader ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_data1) ENTITY ZR_BillHeader BY \_item ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_data2) FAILED failed. LOOP AT lt_data1 ASSIGNING FIELD-SYMBOL(&lt;ls_data1&gt;). APPEND VALUE #( %cid = keys[ KEY entity %key = &lt;ls_data1&gt;-%key ]-%cid "%is_draft = keys[ KEY entity %key = &lt;ls_data1&gt;-%key ]-%param-%is_draft %data = CORRESPONDING #( &lt;ls_data1&gt; EXCEPT BillId ) ) TO lt_bill ASSIGNING FIELD-SYMBOL(&lt;new_bill_doc&gt;). &lt;new_bill_doc&gt;-BillId = CONV #( lv_billId ). &lt;new_bill_doc&gt;-BillDate = cl_abap_context_info=&gt;get_system_date( ). LOOP AT lt_data2 INTO DATA(ls_data2). APPEND VALUE #( %cid_ref = &lt;new_bill_doc&gt;-%cid "BillId = &lt;new_bill_doc&gt;-BillId %target = VALUE #( ( %cid = &lt;new_bill_doc&gt;-%cid BillId = &lt;new_bill_doc&gt;-BillId MaterialId = ls_data2-MaterialId ItemNo = ls_data2-ItemNo ) ) ) TO lt_billitem ASSIGNING FIELD-SYMBOL(&lt;new_bill_item&gt;). ENDLOOP. ENDLOOP. "Create BO instance by copy. MODIFY ENTITIES OF ZR_BillHeader IN LOCAL MODE ENTITY ZR_BillHeader CREATE FIELDS ( BillId BillDate BillType Currency CustomerId NetAmount SalesOrg ) WITH VALUE #( ( %cid = &lt;new_bill_doc&gt;-%cid BillId = &lt;new_bill_doc&gt;-BillId BillDate = &lt;new_bill_doc&gt;-BillDate Currency = &lt;new_bill_doc&gt;-Currency CustomerId = &lt;new_bill_doc&gt;-CustomerId NetAmount = &lt;new_bill_doc&gt;-NetAmount SalesOrg = &lt;new_bill_doc&gt;-SalesOrg ) ) CREATE BY \_item FIELDS ( ItemNo MaterialId ) WITH VALUE #( ( %cid_ref = &lt;new_bill_doc&gt;-%cid %target = VALUE #( FOR i IN lt_billitem FOR j IN i-%target ( %cid = j-ItemNo ItemNo = j-ItemNo MaterialId = j-MaterialId ) ) ) ) MAPPED DATA(mapped_create). " Set values to front end. mapped-ZR_BillHeader = mapped_create-ZR_BillHeader. ENDMETHOD. ENDCLASS.</code></pre><P>13. Create behavior projection on top of root projection view. It will look like this&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); use draft; define behavior for ZP_BillHeader //alias &lt;alias_name&gt; { use create; use update; use delete; use action Edit; use action Activate; use action Discard; use action Resume; use action Prepare; use action ApproveBill; use action CopyBilldoc; use association _Item { create; with draft;} } define behavior for ZP_BillItem //alias &lt;alias_name&gt; { use update; use delete; use association _header { with draft; } }</code></pre><P>14. Now create the service definition and expose both header and item projection:&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Service Definition for Billing Doc' define service ZSD_BillingDocument { expose ZP_BillHeader as BillingHeader; expose ZP_BillItem as BillingItem; }</code></pre><P>15. On top of service definition, create a service binding. I have use Odata V4 as I am using draft functionality.&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SinghMohit_2-1774328092749.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/388129i3F002C5161BFBF94/image-size/medium?v=v2&amp;px=400" role="button" title="SinghMohit_2-1774328092749.png" alt="SinghMohit_2-1774328092749.png" /></span></P><P>16. Finally, it's a result time. Have a first look of our application.&nbsp;<span class="lia-unicode-emoji" title=":backhand_index_pointing_down:">👇</span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SinghMohit_3-1774328288719.png" style="width: 633px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/388130i323F6AD395B25E2C/image-dimensions/633x304?v=v2" width="633" height="304" role="button" title="SinghMohit_3-1774328288719.png" alt="SinghMohit_3-1774328288719.png" /></span></P><P>17. Now select any record and click on copyBill. I am selecting second record.&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SinghMohit_4-1774328376670.png" style="width: 627px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/388131iD0F0B064DD1FA3F6/image-dimensions/627x315?v=v2" width="627" height="315" role="button" title="SinghMohit_4-1774328376670.png" alt="SinghMohit_4-1774328376670.png" /></span></P><P>18. Once I clicked on copyBill, We can see a new BillId is generated with same line items as of source record.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SinghMohit_5-1774328485372.png" style="width: 637px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/388132i402D372E50CFD5C7/image-dimensions/637x297?v=v2" width="637" height="297" role="button" title="SinghMohit_5-1774328485372.png" alt="SinghMohit_5-1774328485372.png" /></span></P><P><STRONG>Conclusion:&nbsp;</STRONG>So, we have come to the end of our blog. We have seen how we can implement an use case related to factory action. I want to summarise important steps again required to implement factory action:&nbsp;</P><OL><LI>Declare factory action in behavior definition and activate it.</LI><LI>Use quick fix to generate the method in implementation class.&nbsp;</LI><LI>Use Read by association to read both header and child entity.</LI><LI>Append the new structure.</LI><LI>Create new duplicate BO entry by using create by association.</LI></OL><P>Thank You for reading my blog. Do comment if you have any doubt or if you know any better way to do this. Good Bye&nbsp;<span class="lia-unicode-emoji" title=":waving_hand:">👋</span></P> 2026-03-25T10:31:08.744000+01:00 https://community.sap.com/t5/abap-blog-posts/dump-when-checking-faulty-rap-object-based-on-union-cds-view-onpremise-2023/ba-p/14357124 Dump When Checking Faulty RAP Object Based On Union CDS-View (OnPremise 2023 SP01) 2026-03-25T11:29:21.883000+01:00 maschulze https://community.sap.com/t5/user/viewprofilepage/user-id/709638 <P>Dear Community,</P><P>Recently I found a bug when checking/activating a RAP object which is based on a union CDS-View and has an error across multiple artifacts. I would like to describe and summarize this for reporting and to save other people’s time during analysis.</P><H3 id="toc-hId-1921437002" id="toc-hId-1921523488">Initial Situation</H3><P>Let's say I want to consolidate different findings from our beloved sflight. For this purpose, I created the following union view:</P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [ #NONE ] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Unifies different validation findings' @Metadata.ignorePropagatedAnnotations: true define root view entity ZI_FLIGHT_FINDINGS_UNION as select from ZI_DELAYED_FLIGHTS { key Carrid, key Connid, key Fldate, ValidationResult, ValidationCategory, DelayInMinutes, -1 as MaxPassengers, -1 as BookedPassengers } union select from ZI_OVERBOOKED_FLIGHTS { key Carrid, key Connid, key Fldate, ValidationResult, ValidationCategory, -1 as DelayInMinutes, MaxPassengers, BookedPassengers }</code></pre><P>Based on that union, I want to create a simple Fiori Elements app in which the user can see all the different findings and is able to create comments via an action. For the action we create the following Behavior Definition:</P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_i_flight_findings_union unique; strict ( 2 ); define behavior for ZI_FLIGHT_FINDINGS_UNION lock master authorization master ( instance ) { action addComment parameter char200; }</code></pre><P>&nbsp;I decided to go for an unmanaged implementation, because unions do not easily support CRUD operations (and they are not needed for the business case yet). The handler class in the class pool looks like this:</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_ZI_FLIGHT_FINDINGS_UNION DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zi_flight_findings_union RESULT result. METHODS addComment FOR MODIFY IMPORTING keys FOR ACTION zi_flight_findings_union~addComment. METHODS read FOR READ IMPORTING keys FOR READ zi_flight_findings_union RESULT result. ENDCLASS. CLASS lhc_ZI_FLIGHT_FINDINGS_UNION IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD addComment. "Create comment by EML .... ENDMETHOD. METHOD read. ENDMETHOD. ENDCLASS.</code></pre><P>Everything works, everything can be activated. But now silly me wants to save additional data from our&nbsp;<SPAN>ZI_FLIGHT_FINDINGS_UNION object. Reading with EML isn't possible yet, because read isn't implemented. To avoid implementing the read method myself, I got the idea to change the Behavior Definition to&nbsp;<STRONG>managed with unmanaged save</STRONG>&nbsp;(would this even work?):</SPAN></P><pre class="lia-code-sample language-abap"><code>managed with unmanaged save implementation in class zbp_i_flight_findings_union unique; strict ( 2 ); define behavior for ZI_FLIGHT_FINDINGS_UNION lock master authorization master ( instance ) { action addComment parameter char200; }</code></pre><P>Now I introduced some inconsistencies, because the handler still contains the read method.<BR /><STRONG>But these errors are hidden now.</STRONG></P><P>There is no warning inside the behavior definition, but activating leads to the following error:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="maschulze_0-1774250654430.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/387468iDD8094790D2F304E/image-size/medium?v=v2&amp;px=400" role="button" title="maschulze_0-1774250654430.png" alt="maschulze_0-1774250654430.png" /></span></P><P>&nbsp;</P><P>By checking ST22 we can see that we get for every syntax check and every activation the following dump:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="maschulze_1-1774250654482.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/387467iC5485524F59F8B46/image-size/medium?v=v2&amp;px=400" role="button" title="maschulze_1-1774250654482.png" alt="maschulze_1-1774250654482.png" /></span></P><P>&nbsp;</P><P>If we don't know the mistake we made inside the behavior definition and its implementation we have no indication of what went wrong.</P><H3 id="toc-hId-1724923497" id="toc-hId-1725009983">How this dump happens</H3><P>The dump happens inside the Class&nbsp;<STRONG><FONT color="#000000">CL_CSP_MD_ANNOTATION_READER<SPAN>&nbsp;</SPAN></FONT></STRONG>in method<SPAN>&nbsp;</SPAN><STRONG><FONT color="#000000">create_by_metadata</FONT></STRONG>:</P><pre class="lia-code-sample language-abap"><code> lo_instance-&gt;mt_element_annotation = CORRESPONDING #( is_cds_annotations_md-elements MAPPING element_name = elementname annotation_name = annoname annotation_value = value ).</code></pre><P>This statement tries to write values from&nbsp;<SPAN><STRONG><FONT color="#000000">is_cds_annotations_md-elements&nbsp;</FONT></STRONG>to&nbsp;<FONT color="#FF9900"><STRONG><FONT color="#000000">lo_instance-&gt;mt_element_annotation</FONT></STRONG><FONT color="#000000">.&nbsp; That table expects a unique key with&nbsp;<STRONG>annotation_name&nbsp;</STRONG>and&nbsp;<STRONG>element_name</STRONG>.<BR /></FONT></FONT></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="maschulze_2-1774250654460.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/387469iBA913739ADF59FB9/image-size/medium?v=v2&amp;px=400" role="button" title="maschulze_2-1774250654460.png" alt="maschulze_2-1774250654460.png" /></span></P><P>&nbsp;</P><P>But this uniqueness is not given:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="maschulze_3-1774250654493.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/387471iAFBDDDD45C6DBF5F/image-size/medium?v=v2&amp;px=400" role="button" title="maschulze_3-1774250654493.png" alt="maschulze_3-1774250654493.png" /></span></P><P>&nbsp;</P><P>The reason is in table&nbsp;<STRONG>DDFIELDANNO&nbsp;</STRONG>in which the assigned annotations are stored. The annotation&nbsp;<STRONG>ABAPCATALOG.INTERNAL.ISMANDT&nbsp;</STRONG>is stored twice, because of our union:&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="maschulze_4-1774250654488.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/387470i9DCA85CC37F2FB3C/image-size/medium?v=v2&amp;px=400" role="button" title="maschulze_4-1774250654488.png" alt="maschulze_4-1774250654488.png" /></span></P><H3 id="toc-hId-1528409992" id="toc-hId-1528496478">Workaround</H3><P><STRONG>Caution: Be careful when working in this area, especially when debugging or activating the object. I cannot guarantee that it won’t lead to inconsistencies or other issues in your system. You do everything at your own risk.</STRONG></P><P>At the end of method <STRONG>IF_CSP_WRP_DD_ANNOTATION~GET_ANNOS_MASS</STRONG><SPAN>&nbsp;in class&nbsp;<STRONG>CL_CSP_WRP_DD_ANNOTATION</STRONG> I placed a Breakpoint, triggered the check (<STRONG>not the activation!</STRONG>) and was able to remove the duplicate line. After that, I was able to get the real finding:</SPAN></P><P><EM>"Class can't be analyzed during BDEF implementation check. Inconsistencies or errors exist: "ZI_FLIGHT_FINDINGS_UNION" has the implementation type "managed", hence the implementation type "READ" is superfluous."</EM></P><H3 id="toc-hId-1528409992" id="toc-hId-1331982973">Conclusion</H3><P>With this workaround it's possible to&nbsp;avoid the dump and retrieve the actual finding.</P><P>To fix the dump, the duplicate entry must be prevented. I assume that either the selection (method <STRONG>_GET_DRCT_ANNS_4_ENTT_ELMNTS_M&nbsp;</STRONG>in class <STRONG>CL_DD_DDL_ANNOTATION_SERVICE)&nbsp;</STRONG>or the merging of the annotations (method <STRONG>MERGE_ENTITIES_ELMNTS_ANNOS</STRONG> in&nbsp;class&nbsp;<STRONG>CL_DD_DDL_ANNOTATION_MERGER</STRONG>) might be the relevant place to fix it.</P><P>I'm also creating an incident and keep you updated. If it's already fixed after 2023 SP01, please let me know.</P><P>Thank you for reading and best regards<BR />Marc</P> 2026-03-25T11:29:21.883000+01:00 https://community.sap.com/t5/abap-blog-posts/fiori-list-report-the-quot-process-all-quot-dilemma-parsing-odata-filter-in/ba-p/14359159 Fiori List Report: The "Process All" Dilemma — Parsing OData $filter in ABAP 2026-03-26T18:27:35.306000+01:00 AlessandroSpadoni https://community.sap.com/t5/user/viewprofilepage/user-id/141302 <H3 id="toc-hId-1921583168">The Problem</H3><P class="">This scenario is more common than you might think.</P><P class="">In the old SAP GUI world, ALV lists frequently had buttons like "Execute All" or "Process All" defined in the GUI Status, or users could simply "Select All" rows and trigger an action. The entire dataset was right there in memory — stateful, accessible, simple.</P><P class="">Unfortunately, the same approach is still often requested in Fiori Elements List Reports. The reason? There's still a widespread difficulty in understanding the fundamental difference between stateful and stateless, between a classic ALV list and a Fiori List Report.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AlessandroSpadoni_0-1774545069380.png" style="width: 566px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/389401iAE06BFFD8424D291/image-dimensions/566x156?v=v2" width="566" height="156" role="button" title="AlessandroSpadoni_0-1774545069380.png" alt="AlessandroSpadoni_0-1774545069380.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AlessandroSpadoni_1-1774545139469.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/389403i2FBEFF5C8EF959B1/image-size/large?v=v2&amp;px=999" role="button" title="AlessandroSpadoni_1-1774545139469.png" alt="AlessandroSpadoni_1-1774545139469.png" /></span></P><P class="">When we try to explain that there's an OData protocol with pagination behind the scenes, that "Select All" doesn't truly select all rows, or that an "Execute All" action has no knowledge of which records to process — unlike in ALV where the entire internal table was available — we can often steer the design away from this pattern. But sometimes, we can't.</P><P class="">The Fiori warning message helps users understand that "Select All" is only partial — it's no longer the same as it was with ALV lists.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AlessandroSpadoni_2-1774545196426.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/389404iDDCFC40BEF6A9545/image-size/large?v=v2&amp;px=999" role="button" title="AlessandroSpadoni_2-1774545196426.png" alt="AlessandroSpadoni_2-1774545196426.png" /></span></P><P class="">There are legitimate use cases where, after filtering a dataset, reviewing it, validating it, the user needs to "confirm" an action or a status change to be applied to that specific result set they're currently looking at.</P><P class="">I won't go into the details of how the action should be implemented in ABAP — there could be timeout concerns, and often these actions are executed via background tasks or jobs. I want to focus specifically on the approach for capturing the "snapshot" of what the user has filtered.</P><P class="">As we said, "Select All" is not a viable option. In the modern world we have pagination, and we can't expect the user to scroll all the way to the bottom of the list. Retrieving all row identifiers from the frontend is simply not feasible.</P><H3 id="toc-hId-1725069663">Our Approach</H3><P class="">The best solution we've implemented (together with&nbsp;<a href="https://community.sap.com/t5/user/viewprofilepage/user-id/137528">@SimoneLicciardi</a>&nbsp;) is to pass the applied filter criteria back to a Static Action — essentially the same filters that were active when the user pressed "Go".</P><P class="">We can define an Abstract Entity as the import parameter for the RAP action. If the action dialog already has other parameters, this is simply an additional hidden parameter (@UI.hidden: true) that we can call FilterString.</P><pre class="lia-code-sample language-abap"><code>define abstract entity ZA_ProcessAllParams { @UI.hidden: true FilterString : abap.string(4000); // ... other action parameters if needed }</code></pre><H3 id="toc-hId-1528556158">The Fiori Elements Extension</H3><P class="">On the Fiori Elements List Report side, we need custom action logic that does two things when the user triggers the action: capture the current $filter string and inject it into the hidden parameter.</P><P class="">This can be achieved through a custom action defined in the manifest.json (or via a controller extension, depending on your app setup).&nbsp;</P><P class="">The _getFilterString method extracts the $filter from the current table row binding — this is exactly the same filter the framework sent to the backend when the user pressed "Go".</P><pre class="lia-code-sample language-javascript"><code>function _getFilterString(oView) { // Table ID follows Fiori Elements convention: // "&lt;appId&gt;::&lt;ListReportPage&gt;--fe::table::&lt;EntitySet&gt;::LineItem" var oTable = oView.byId("&lt;your-table-control-id&gt;"); if (oTable) { var oRowBinding = oTable.getRowBinding(); if (oRowBinding) { var sPath = oRowBinding.getDownloadUrl(); if (sPath) { var iFilterStart = sPath.indexOf("$filter="); if (iFilterStart &gt; -1) { var sFilter = sPath.substring(iFilterStart + 8); var iNextParam = sFilter.indexOf("&amp;"); if (iNextParam &gt; -1) { sFilter = sFilter.substring(0, iNextParam); } return decodeURIComponent(sFilter); } } } } return ""; }</code></pre><P>Then in the action handler, we invoke the static action and pass the $filter string as the hidden parameter:</P><pre class="lia-code-sample language-javascript"><code>onPressExecuteAll: function () { var oView = this.editFlow.getView(); var sActionName = "&lt;your action bound context&gt;"; var sFilterString = _getFilterString(oView); var oInvokeParams = { controlId: "&lt;your-table-control-id&gt;", bStaticAction: true, invocationGrouping: "Isolated", parameterValues: [{ name: "FilterString", value: sFilterString }] }; this.editFlow.invokeAction(sActionName, oInvokeParams) .then(function (oResult) { // handle success }).catch(function (oError) { console.error("Action error:", oError); }); }</code></pre><P>The key point: the parameterValues array injects the filter string into the hidden FilterString field of the Abstract Entity. The user never sees it — the action dialog shows only the visible parameters (if any), while the filter criteria travel silently to the backend.</P><P><EM>Note:&nbsp; this was tested on S/4HANA 2023 with OData V4 and SAPUI5 1.120.</EM></P><H3 id="toc-hId-1332042653">The ABAP Parser</H3><P class="">On the backend, the action handler receives the $filter string and needs to parse it into ABAP range tables to re-execute the query with the same criteria.</P><P class="">We looked for standard solutions — for example, the method ODATA_FILTER2SELECT_OPTION of class CL_CLB2_TOOLS — but it only supports OData V2.</P><P class="">So we built a custom reusable class: ZCL_ODATA_FILTER_PARSER, which handles both V2 and V4 syntax and converts the $filter string directly into ABAP range tables.</P><pre class="lia-code-sample language-abap"><code>DATA(lo_parser) = NEW zcl_odata_filter_parser( iv_filter = lv_filter_string iv_version = zcl_odata_filter_parser=&gt;gc_version-v4 ). DATA(lt_r_plant) = lo_parser-&gt;get_range( 'Plant' ). DATA(lt_r_mat) = lo_parser-&gt;get_range( 'Material' ). DATA(lt_r_date) = lo_parser-&gt;get_range( 'DeliveryDate' ).</code></pre><P class="">The class is open source, includes ABAP Unit tests&nbsp; and is installable via abapGit. You can find the&nbsp; usage examples in the repository:</P><P class=""><A href="https://github.com/alespad/abap-odata-filter-parser" target="_self" rel="nofollow noopener noreferrer">https://github.com/alespad/abap-odata-filter-parser</A>&nbsp;</P><P class="">This is how we approached solving this problem. If you know of better approaches, closer to standard functionality, we'd love to hear about them — please share in the comments!</P><P class="">&nbsp;</P> 2026-03-26T18:27:35.306000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/custom-business-configurations-f4579-define-default-grouping-and-sorting/ba-p/14359823 Custom Business Configurations (F4579): Define default grouping and sorting 2026-03-27T13:50:03.260000+01:00 patrick_winkler https://community.sap.com/t5/user/viewprofilepage/user-id/729521 <H1 id="toc-hId-887480474" id="toc-hId-1663424358">Introduction</H1><P><SPAN>The&nbsp;</SPAN><A href="https://help.sap.com/docs/btp/sap-business-technology-platform/custom-business-configurations-app" target="_blank" rel="noopener noreferrer"><SPAN class="">Custom Business Configurations</SPAN></A><SPAN>&nbsp;(CUBCO) app serves as an entry point to the&nbsp;</SPAN><A class="" title="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/business-configuration-maintenance-object?version=Cloud" href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/business-configuration-maintenance-object?version=Cloud" target="_blank" rel="noopener noreferrer">Business Configuration Maintenance Object</A><SPAN>&nbsp;(SMBC) provided by custom applications or partners.<BR /><BR />This blog explains how to define the default grouping and sorting for the tables of the business configuration object.<BR />The basic procedure is already <A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/06_object_page_content.md#presentation-variant---object-page" target="_self" rel="nofollow noopener noreferrer">explained here</A>,&nbsp;but we apply it directly to the result of the ADT wizard.<BR /></SPAN></P><P>This blog is relevant for</P><UL><LI><a href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Cloud+Public+Edition/pd-p/08e2a51b-1ce5-4367-8b33-4ae7e8b702e0" class="lia-product-mention" data-product="1199-1">SAP S/4HANA Cloud Public Edition</a>&nbsp;</LI><LI><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>&nbsp;</LI><LI><a href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Cloud+Private+Edition/pd-p/5c26062a-9855-4f39-8205-272938b6882f" class="lia-product-mention" data-product="1198-1">SAP S/4HANA Cloud Private Edition</a>&nbsp;</LI></UL><P>Further reading:</P><UL><LI><A href="https://community.sap.com/t5/tag/business%20configuration%20maintenance%20object/tg-p/board-id/technology-blog-sap" target="_blank">Related blog posts</A></LI><LI>Learn how you can use<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap?url_id=text-sapcommunity-prdteng-ABAP" target="_blank" rel="noopener noreferrer">ABAP technology</A><SPAN>&nbsp;</SPAN>to develop innovative applications and business solutions across SAP’s portfolio on<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap" target="_blank" rel="noopener noreferrer">SAP Learning Site</A>.</LI></UL><H1 id="toc-hId-1439888495" id="toc-hId-1466910853">Example scenario</H1><P>Company codes are defined in the configuration table ZDEMO_CC. In the CUBCO app, the rows are to be grouped and sorted by country.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Company Code' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #C @AbapCatalog.dataMaintenance : #ALLOWED define table zdemo_cc { key client : abap.clnt not null; key company_code : abap.numc(4) not null; country : land1; last_changed_at : abp_lastchange_tstmpl; local_last_changed_at : abp_locinst_lastchange_tstmpl; }</code></pre><H1 id="toc-hId-1243374990" id="toc-hId-1270397348">Define presentation variant</H1><P>First you use the<SPAN>&nbsp;</SPAN><A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/generating-business-configuration-maintenance-object-with-generate-abap-repository-objects-wizard" target="_blank" rel="noopener noreferrer">ADT wizard to generate a Business Configuration Maintenance Object</A><SPAN>&nbsp;</SPAN>for this table.</P><P>In the generated CDS view ZI_COMPANYCODE_S of the Singleton entity, you change the facet type from #LINEITEM_REFERENCE to #<SPAN>PRESENTATIONVARIANT_REFERENCE and add the targetQualifier attribute:</SPAN></P><pre class="lia-code-sample language-abap"><code> .facet: [ { id: 'ZI_CompanyCode', purpose: #STANDARD, type: #PRESENTATIONVARIANT_REFERENCE, targetQualifier: 'myVariant', label: 'Company Codes', position: 1 , targetElement: '_CompanyCode' } ] .lineItem: [ { position: 1 } ] key 1 as SingletonID,</code></pre><P>In the metadata extension ZI_COMPANYCODE of the company code entity, add the following presentation variant:</P><pre class="lia-code-sample language-abap"><code>.presentationVariant: [ { qualifier: 'myVariant', sortOrder: [ { by: 'Country', direction: #ASC } ], groupBy: [ 'Country' ], visualizations: [{type: #AS_LINEITEM}] } ] annotate view ZI_CompanyCode with</code></pre><P>Finally, open the generated SMBC object ZCOMPANYCODE in ADT and open the table settings section. Change the table type attribute from Grid Table to Responsive Table because&nbsp;<A href="https://help.sap.com/docs/ABAP_PLATFORM_NEW/468a97775123488ab3345a0c48cadd8f/d344c5aa5c81483482dbbed5b3abb142.html?version=202510.001" target="_self" rel="noopener noreferrer">grouping is not supported for grid tables</A>. If you only need the default sorting, you can continue to use the grid table.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="patrick_winkler_1-1774615697786.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/389766iEE96390108EFA388/image-size/medium?v=v2&amp;px=400" role="button" title="patrick_winkler_1-1774615697786.png" alt="patrick_winkler_1-1774615697786.png" /></span></P> 2026-03-27T13:50:03.260000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/our-2026-roadmap-for-joule-for-developers-abap-ai-capabilities/ba-p/14360358 Our 2026 Roadmap for Joule for Developers ABAP AI capabilities 2026-03-28T20:13:48.908000+01:00 JanMatthes https://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"><BR /><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="J4D_Agentic.png" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/390078iDD4D1E72EFCEDA09/image-size/small?v=v2&amp;px=200" role="button" title="J4D_Agentic.png" alt="J4D_Agentic.png" /></span></A><A href="https://www.sap.com/events/sapphire/orlando/flow/sap/so26/catalog/page/catalog?search=ABAP&amp;tab.sessionplanned=1692641568884001cZ9d" target="_blank" rel="noopener noreferrer"><STRONG>Sapphire 2026 </STRONG>is approaching quickly and as usual it will bring lots of new announcements.</A> So now is the right point in time to recap what has been announced already until Q2/2026 and prepare for the <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/joule-for-developers-abap-ai-sapphire-and-asug-2026-sessions/ba-p/14365782" target="_blank">ABAP AI sessions</A>.</P><P class="lia-indent-padding-left-30px" data-unlink="true" style="padding-left : 30px;">The <STRONG>ABAP Platform&nbsp;AI</STRONG> (covering both SAP BTP ABAP Environment and SAP S/4HANA) transitions from a set of independent AI skills to full-scale <STRONG>Agentic AI</STRONG>. <SPAN class="">The roadmap focuses heavily on increasing developer productivity via <STRONG>agents in Joule for Developers ABAP (J4D ABAP)</STRONG> and the modernization of ABAP development tools.<BR /><BR />The key benefits for customers and partners of the&nbsp;<STRONG>roadmap until Q2/2026 are:</STRONG></SPAN></P><OL><LI><SPAN class="">ABAP </SPAN><SPAN class="">development&nbsp;</SPAN><SPAN class="">tools with <STRONG>AI Agents</STRONG></SPAN><STRONG>&nbsp;<SPAN class="">in</SPAN><SPAN class="">&nbsp;VS Code with ABAP MCP Server.</SPAN></STRONG></LI><LI><SPAN class="">ABAP AI </SPAN><SPAN class="">f</SPAN><SPAN class="">or lower releases of </SPAN><STRONG><SPAN class="">SAP S/4HANA Cloud Private Edition (i.e. 2021, 2022, 2023 and 2025).</SPAN></STRONG></LI><LI><STRONG><SPAN class="">Simplified customer on-boarding of ABAP AI&nbsp;</SPAN></STRONG><SPAN class="">via SAP for Me and Central Business Configuration</SPAN><STRONG><SPAN class="">.</SPAN></STRONG></LI><LI><SPAN class=""><STRONG>Faster innovation with Side-by-Side consumption of SAP and 3rd party Agentic IDEs and LLMs</STRONG><BR />(e.g. Microsoft, Amazon, IBM, OpenAI, Anthropic, Google, Mistral…)</SPAN></LI></OL><DIV class="">&nbsp;</DIV><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="J4D_Agentic_sbs.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/390119i0AFD64FE139637CA/image-size/large?v=v2&amp;px=999" role="button" title="J4D_Agentic_sbs.png" alt="J4D_Agentic_sbs.png" /></span></DIV><DIV class=""><P><SPAN class="">The biggest shift in 2026 will be the <STRONG>move toward Agentic AI</STRONG>, where the existing ABAP AI skills doesn't just explain, complete or generate code (e.g. RAP, CDS, Tests) <STRONG>independently but acts as an autonomous collaborator</STRONG>.&nbsp;</SPAN><SPAN class="">This includes built-in <STRONG>ABAP MCP (Model Context Protocol) server.</STRONG> The introduction of VS Code as IDE can only be a start as the depth and breath of Eclipse which we achieved in so many years cannot be reached with one or two steps in VS Code. It'll be a journey which starts with first providing&nbsp;End-to-end UI services development with agents.</SPAN></P><P><SPAN class="">The new ABAP AI side-by-side architecture is designed to provide a scalable and future‑proof foundation for <STRONG>ABAP AI capabilities across heterogeneous SAP landscapes</STRONG>. It enables customers to consume ABAP AI services side‑by‑side, independent of the underlying S/4HANA release, ensuring access even <STRONG>for lower‑release Private Edition systems</STRONG>.&nbsp;</SPAN></P><P><SPAN class="">A core benefit of the architecture is also its simplified enablement model through SAP for Me and Central Business Configuration, reducing on-boarding effort and ensuring consistent lifecycle management across environments.&nbsp;<BR />In addition, the architecture allows us to accelerate innovation speed by allowing to <STRONG>leverage SAP‑provided ABAP AI models alongside leading third‑party LLMs</STRONG> (e.g., Microsoft, Amazon, IBM, OpenAI, Anthropic, Google, Mistral) <STRONG>more independently from releases</STRONG>. This flexibility ensures rapid adoption of new capabilities without dependency on individual release cycles.</SPAN></P><P><SPAN class="">If you missed the <STRONG>highlights we delivered in Q1/2026</STRONG> have a look into what <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/custom-code-migration-to-sap-s-4hana-powered-by-sap-joule-for-developers/ba-p/14329094" target="_blank">Olga Dolinskaja shared about ABAP AI ATC finding explain and custom code explain and proposals</A>. Although this will be a focus also for our future roadmap we will also not stop investing into AI app generators like the&nbsp;<A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/cds-analytical-model-generation-powered-by-ai" target="_blank" rel="noopener noreferrer">Embedded Analytics model generator</A>.&nbsp;</SPAN></P></DIV><DIV class="">&nbsp;</DIV><DIV class=""><STRONG>Here you can learn more:</STRONG></DIV><DIV class=""><div class="video-embed-center video-embed"><iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FaU3Eu2SlvxE%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DaU3Eu2SlvxE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FaU3Eu2SlvxE%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="400" height="225" scrolling="no" title="ABAP AI Roadmap, CAP Jan 2026 Release, Brazil CodeJams, CAP Roundtable, DYK #3 | SAP Developer News" frameborder="0" allow="autoplay; fullscreen; encrypted-media; picture-in-picture;" allowfullscreen="true"></iframe></div></DIV><OL><LI><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></LI><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/your-2026-roadmap-to-getting-started-with-abap-ai-and-abap-1/ba-p/14312060" target="_blank"><SPAN class="">Your Roadmap for getting started with ABAP AI</SPAN></A></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&amp;PRODUCT=73555000100800001164&amp;range=CURRENT-LAST" target="_blank" rel="noopener noreferrer">SAP Roadmap Explorer</A></SPAN></LI><LI><SPAN class="">Help &amp; Guides</SPAN><OL><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><SPAN class=""><SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/custom-code-migration-to-sap-s-4hana-powered-by-sap-joule-for-developers/ba-p/14329094" target="_blank">Custom code migration to SAP S/4HANA powered by SAP Joule for Developers, ABAP AI capabilities</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://github.com/SAP-samples/abap-platform-rap120" target="_blank" rel="noopener nofollow noreferrer">Build SAP Fiori Apps with ABAP Cloud and SAP Joule for developers (RAP120)</A>&nbsp;</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&amp;version=LATEST" target="_blank" rel="noopener noreferrer"><SPAN class="">ABAP-1 Help</SPAN></A></LI></OL></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&nbsp;here</A>&nbsp;. Here you find <A href="https://www.sap.com/products/artificial-intelligence/sap-abap.html" target="_blank" rel="noopener noreferrer">more on ABAP-&nbsp;1</A>,</SPAN></P> 2026-03-28T20:13:48.908000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/the-agentic-revolution-is-here-and-your-abap-code-is-the-foundation/ba-p/14358726 The Agentic Revolution Is Here — And Your ABAP Code Is the Foundation 2026-04-03T18:00:00.017000+02:00 Faaiez https://community.sap.com/t5/user/viewprofilepage/user-id/44178 <H1 id="toc-hId-1663393609">Three Waves in Two Years</H1><P><SPAN>To understand where we are, you need to understand the three inflection points Jensen described. They happened faster than anyone expected.</SPAN></P><H3 id="toc-hId-1725045542">Wave 1: Generative AI (2022–2023)</H3><P><SPAN>ChatGPT changed computing from retrieval-based to generative. Instead of a database returning a record, a model generates an answer. Jensen put it bluntly: "Computing used to be retrieval-based. Now it's generative." The architecture implications of that sentence are enormous and still rippling through every layer of enterprise software.</SPAN></P><H3 id="toc-hId-1528532037">Wave 2: Reasoning AI (2024)</H3><P><SPAN>OpenAI's o1 model introduced something that made generative AI trustworthy: the ability to think. To reflect. To decompose a problem it couldn't solve into steps it could. To ground itself on facts before answering. Reasoning AI made large language models genuinely useful for complex, multi-step work — not just creative writing and summarisation.</SPAN></P><H3 id="toc-hId-1332018532">Wave 3: Agentic AI (2025–2026)</H3><P><SPAN>This is the one that changes your job. Jensen cited Claude Code — the very AI assistant many of us now use daily — as the breakthrough moment:</SPAN></P><P class="lia-align-center" style="text-align: center;"><EM>"<FONT color="#008000">Claude Code has revolutionised software engineering. There is not one software engineer at NVIDIA today who is not assisted by an AI coding agent."</FONT></EM></P><P><SPAN>The shift is fundamental. With generative AI, you ask a question. With agentic AI, you give a task. The agent reads your files, writes code, compiles it, tests it, finds the failures, fixes them, and iterates — autonomously, in a loop, until the work is done or it needs your input.</SPAN></P><P><SPAN>Computing demand, Jensen said, has grown </SPAN><FONT color="#FF0000"><STRONG>one million times </STRONG></FONT><SPAN>in the last two years. That is not a typo. The explosion is driven by agentic inference: every time an agent thinks, plans, reads a file, calls a tool, or generates an output, it burns tokens.</SPAN></P><P><A href="https://www.nvidia.com/gtc/keynote/?regcode=em-even-504-en-gb-1-l1&amp;ncid=em-even-504-en-gb-1-l1" target="_blank" rel="noopener nofollow noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faaiez_0-1774510570417.png" style="width: 937px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/389071i9259D9899541470A/image-dimensions/937x607?v=v2" width="937" height="607" role="button" title="Faaiez_0-1774510570417.png" alt="Faaiez_0-1774510570417.png" /></span></A></P><P><EM>GTC 2026 Keynote — Jensen Huang's enterprise agentic architectures slide (02:02:57). SAP is bottom-left. Each diagram is a production blueprint, not a roadmap.</EM></P><H1 id="toc-hId-877339589">&nbsp;</H1><H1 id="toc-hId-680826084">What Jensen Showed at 02:02:57</H1><P><SPAN>At just over two hours into the keynote, Jensen showed a slide he called a renaissance. Each of the twelve architecture diagrams was a real, production blueprint. Not a roadmap. Not a vision. Each company had already rebuilt its product around AI agents.</SPAN></P><P class="lia-align-center" style="text-align: center;"><FONT color="#008000"><EM>"This is a reinvention. A renaissance of enterprise IT. Every single SaaS company will become an Agentic as a Service company. No question about it."</EM></FONT></P><P><SPAN>He compared it to three previous platform shifts:</SPAN></P><UL><LI><SPAN>When Linux arrived → every company needed a Linux strategy</SPAN></LI><LI><SPAN>When HTML/HTTP arrived → every company needed a web strategy</SPAN></LI><LI><SPAN>When Kubernetes arrived → every company needed a cloud-native strategy</SPAN></LI><LI><STRONG><SPAN>Now </SPAN></STRONG><SPAN>Now OpenClaw (the open-source agentic OS) has arrived → every company needs an agentic strategy</SPAN></LI></UL><H1 id="toc-hId-484312579">NemoClaw and SAP: A Security Partnership</H1><P><SPAN>Jensen announced NemoClaw as NVIDIA's enterprise-hardened version of OpenClaw. To understand where it fits in the SAP picture, it helps to be precise about what it is — and what it is not.</SPAN></P><P><STRONG>OpenClaw </STRONG><SPAN>is, in Jensen's words, </SPAN><EM>"the operating system of agentic computers." </EM><SPAN>It handles task decomposition, sub-agent spawning, tool calling, file access, cron scheduling, multi-modal I/O, and LLM orchestration. It reached more GitHub stars in a few weeks than Linux achieved in thirty years.</SPAN></P><P><SPAN>The enterprise problem with a system that can "access sensitive information, execute code, and communicate externally" is obvious. You cannot deploy raw OpenClaw in an SAP landscape and have it reach your vendor master, financial postings, and HR records without controls.</SPAN></P><P><SPAN>NemoClaw solves this by adding three security layers on top of OpenClaw:</SPAN></P><TABLE><TBODY><TR><TD width="173"><P><STRONG>Layer</STRONG></P></TD><TD width="451"><P><STRONG>What It Does</STRONG></P></TD></TR><TR><TD width="173"><P><STRONG>OpenShell</STRONG></P></TD><TD width="451"><P>Sandboxes agent execution; controls what it can read, write, and call</P></TD></TR><TR><TD width="173"><P><STRONG>Policy Engine</STRONG></P></TD><TD width="451"><P>Enforces the enterprise's own business rules</P></TD></TR><TR><TD width="173"><P><STRONG>Network Guardrail<BR />+ Privacy Router</STRONG></P></TD><TD width="451"><P>Prevents data leaving the enterprise boundary</P></TD></TR></TBODY></TABLE><P>&nbsp;</P><P><STRONG>Important clarification: </STRONG><SPAN>NemoClaw is not the foundation SAP Joule is built on. Joule is SAP's own product, with its own architecture, models, and AI infrastructure running on BTP and SAP AI Core. NemoClaw is NVIDIA technology that SAP — as a launch enterprise partner — is integrating as an <STRONG>optional security and governance layer </STRONG>within BTP, tied specifically to Joule Studio. It is available for customers who require the strongest possible compliance controls when agents interact with sensitive ERP, financial, or supply chain data.</SPAN></P><P><SPAN>The architecture looks like this:</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Faaiez_2-1774511587745.png" style="width: 935px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/389079i98A3CF5B3FBB0149/image-dimensions/935x325?v=v2" width="935" height="325" role="button" title="Faaiez_2-1774511587745.png" alt="Faaiez_2-1774511587745.png" /></span></P><P><SPAN>The bottom line for ABAP developers remains unchanged regardless of which security envelope is in play: when an agent needs to do something in SAP — read a purchase order, create a vendor, trigger a workflow, query a cost centre — it calls <STRONG>your ABAP</STRONG>.</SPAN></P><P><STRONG>Your BAPIs. Your CDS views. Your OData services.</STRONG></P><H1 id="toc-hId-287799074">&nbsp;</H1><H1 id="toc-hId-91285569">What This Means in Practice</H1><P><SPAN>Let me make this concrete with an example that exists today.</SPAN></P><P><EM>A procurement manager asks Joule: "Which of our top twenty suppliers have delivery performance below 90% this quarter, and what is the total open invoice value against them?"</EM></P><P><SPAN>In the pre-agentic world, someone writes a report in SE38, schedules it, exports to Excel, and manually cross-references two tables. Elapsed time: hours, or days if it hits a queue.</SPAN></P><P><SPAN>In the agentic world, <FONT color="#3366FF">Joule</FONT>:</SPAN></P><OL><LI><SPAN>Decomposes the question into sub-tasks</SPAN></LI><LI><STRONG><SPAN>Calls a procurement agent → queries I_SupplierDeliveryPerformance</SPAN></STRONG><SPAN> CDS view via OData</SPAN></LI><LI><STRONG><SPAN>Calls a finance agent → queries I_SupplierOpenItemMD</SPAN></STRONG><SPAN> CDS view via OData</SPAN></LI><LI><SPAN>Synthesises both results</SPAN></LI><LI><SPAN>Returns a structured answer in seconds</SPAN></LI></OL><P><SPAN>The entire chain runs through NemoClaw's policy engine and your ABAP authorization framework. The agent's service user has only the authorizations you have granted. Every CDS view it accesses must have the proper access control annotation and a corresponding DCL.</SPAN></P><P>&nbsp;</P><P><STRONG><FONT size="6">The Four Things Your ABAP Must Get Right</FONT></STRONG><BR />If agents are going to call your code — and they are — four things matter more than they ever did before.</P><H2 id="toc-hId-23854783"><BR />1. Clean, Annotated CDS Views</H2><P><BR />CDS views are the primary structured data interface for AI agents in SAP. An agent does not navigate transaction codes. It calls OData endpoints backed by CDS. If your CDS views lack semantic annotations, have incomplete access control, or expose data structures that predate any logical data model, they become a liability.<STRONG>The minimum bar for agent-ready CDS:</STRONG></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #CHECK @EndUserText.label: 'Supplier Delivery Performance' @VDM.viewType: #CONSUMPTION @OData.publish: true define view entity I_SupplierDeliveryPerformance as select from ... { @Semantics.businessPartner.role: #SUPPLIER SupplierID, @Semantics.quantity.unitOfMeasure: 'DeliveryUnit' ConfirmedDeliveryQty, @Semantics.percentage OnTimeDeliveryRate }</code></pre><P><SPAN>Semantic annotations tell the LLM what each field means. Without them, the agent is guessing from column names. With them, it can reason correctly.</SPAN></P><P>&nbsp;</P><H2 id="toc-hId-174595635">2. Authority Checks on Every Agent-Callable Interface</H2><P><BR />NemoClaw's policy engine delegates final security enforcement to ABAP. This is not a weakness — it is the correct architecture. The LLM should not be trusted with authorization decisions. ABAP should.<BR /><STRONG>Every function module, BAPI, and method that an agent can call must perform an explicit authority check:</STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD get_supplier_invoice_data. AUTHORITY-CHECK OBJECT 'F_LFA1_BUK' ID 'BUKRS' FIELD iv_company_code ID 'ACTVT' FIELD '03'. IF sy-subrc &lt;&gt; 0. RAISE EXCEPTION TYPE cx_sy_authorization_check EXPORTING textid = cx_sy_authorization_check=&gt;no_authorization. ENDIF. " ... proceed with data retrieval ENDMETHOD.</code></pre><P>An agent running as a service user with overly broad authorizations is an insider threat waiting to happen. Lock it down at the ABAP layer.</P><H2 id="toc-hId--21917870">3. Machine-Speed Performance</H2><P><BR />A human clicks a button every thirty seconds. An agent may call your BAPI ten thousand times per hour. An interface that performs acceptably under human load may collapse completely under agent load.<BR /><STRONG>Review your agent-callable interfaces against these criteria:</STRONG><BR />• No SELECT * in performance-critical paths — agents fetch data in loops, field-level selects only.<BR />• Buffer vendor master, material master, and configuration data where appropriate.<BR />• Avoid dialogue modules and unnecessary commits in agent-exposed interfaces — agents expect atomic, stateless calls.<BR />• Consider ABAP Cloud compatibility — if you are on S/4HANA, agent integrations will increasingly come via BTP.</P><H2 id="toc-hId--218431375"><BR />4. Reliable, Structured Error Returns</H2><P><BR />Agents chain multiple tool calls into a single business process. If step three of a five-step chain fails silently — no exception raised, just a blank result set — the agent may proceed with corrupted state and complete a transaction based on missing data.<BR /><STRONG>Your interfaces need to fail loudly, clearly, and catchably:</STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD create_purchase_requisition. TRY. " ... BAPI call IF lt_return[] IS NOT INITIAL. LOOP AT lt_return INTO ls_return WHERE type = 'E' OR type = 'A'. RAISE EXCEPTION TYPE cx_pr_creation_failed EXPORTING message = ls_return-message message_id = ls_return-id number = ls_return-number. ENDLOOP. ENDIF. CATCH cx_sy_authorization_check INTO DATA(lx_auth). RAISE EXCEPTION TYPE cx_pr_creation_failed EXPORTING message = 'Authorization check failed'. ENDTRY. ENDMETHOD.</code></pre><P>A well-structured exception gives the orchestration layer the information it needs to retry, compensate, or escalate. A dump gives it nothing.</P><H1 id="toc-hId--121541873">&nbsp;</H1><H1 id="toc-hId--318055378">Physical AI: Why EWM and IBP Developers Should Pay Close Attention</H1><P><SPAN>Jensen spent a significant portion of the keynote on physical AI — robots, autonomous vehicles, digital twins. At first glance this might seem distant from ABAP. &nbsp;<STRONG>It is not.</STRONG></SPAN></P><P><SPAN>He announced partnerships with </SPAN><STRONG>ABB, KUKA, Universal Robots, BYD, Hyundai, Nissan, and Geely</STRONG><SPAN> — collectively representing the automation of manufacturing floors and logistics networks at a scale never seen before. These physical systems will generate transactions. Inventory movements, goods receipts, production confirmations, delivery completions — triggered by machines at machine speed, not by warehouse workers scanning barcodes.</SPAN></P><P><SPAN>If you work in SAP Extended Warehouse Management, SAP Integrated Business Planning, or SAP Manufacturing Integration and Intelligence, the interfaces you build today will be called by robots tomorrow. The same four principles apply — and the <STRONG>performance requirement</STRONG> becomes even more acute.</SPAN></P><H1 id="toc-hId--514568883">&nbsp;</H1><H1 id="toc-hId--711082388">Your Learning Roadmap</H1><P><SPAN>The field is moving fast. But not everything needs to be learned at once. Here is how I would prioritise it.</SPAN></P><H2 id="toc-hId--1200998900">Start Now — Zero Extra Investment Required</H2><UL><LI><STRONG><SPAN>AI tools today.</SPAN></STRONG><SPAN> Use AI coding tools on real ABAP work — every unit test, every code review, every CDS view.</SPAN></LI><LI><STRONG><SPAN>Learn what SAP Joule can already do.</SPAN></STRONG><SPAN> Log into your BTP account and explore the Joule capabilities available today.</SPAN></LI><LI><STRONG><SPAN>Audit your CDS views.</SPAN></STRONG><SPAN> Run a quick check across your landscape for CDS views with @AccessControl.authorizationCheck: #NOT_REQUIRED — these are potential agent-accessible data leaks.</SPAN></LI></UL><H2 id="toc-hId--1397512405">Next Three to Six Months</H2><UL><LI><STRONG><SPAN>ABAP RAP (RESTful Application Programming Model).</SPAN></STRONG><SPAN> If you have been deferring it, stop. RAP is the model that produces the OData V4 services that Joule agents consume.</SPAN></LI><LI><STRONG><SPAN>SAP AI Core and SAP BTP basics.</SPAN></STRONG><SPAN> You do not need to become a BTP developer. Understand the platform well enough to design ABAP interfaces that integrate cleanly with it.</SPAN></LI><LI><STRONG><SPAN>Python literacy.</SPAN></STRONG><SPAN> Not fluency — literacy. Most AI tooling and agent orchestration frameworks are Python-first. Being able to read a Python agent script is now a professional responsibility.</SPAN></LI></UL><H2 id="toc-hId--1594025910">Strategic Awareness</H2><UL><LI><STRONG><SPAN>OpenClaw and MCP (Model Context Protocol).</SPAN></STRONG><SPAN> OpenClaw is the open-source agent OS. MCP is the protocol that defines how agents discover and call tools. SAP Joule implements MCP under the hood.</SPAN></LI><LI><STRONG><SPAN>HANA Vector Engine.</SPAN></STRONG><SPAN> HANA Cloud's vector engine brings unstructured data (PDFs, emails, contracts) directly into your SAP data layer.</SPAN></LI></UL><H1 id="toc-hId--1328952717">&nbsp;</H1><H1 id="toc-hId--1525466222">The Part Nobody Is Saying Loudly Enough</H1><P><SPAN>Jensen made a prediction that deserves more attention than it received.</SPAN></P><P><EM><FONT color="#339966">In the near future, every engineer at NVIDIA will receive an annual token budget — roughly equivalent to half their salary in additional compensation </FONT>— because AI-amplified engineers are demonstrably ten times more productive. Engineers are already asking: "<FONT color="#339966">How many tokens come with this job?"</FONT></EM></P><P><SPAN>Here is the part that matters for us specifically:</SPAN></P><P><STRONG><FONT color="#3366FF">AI tokens without SAP domain expertise produce plausible-looking but wrong ABAP.</FONT> </STRONG><SPAN>An LLM that does not understand the difference between a financial posting document and a parked document will generate code that compiles, passes basic tests, and silently corrupts your general ledger. It will call RFC_READ_TABLE in production because it found an example online. It will bypass the credit check in a sales order because it did not know the check existed.</SPAN></P><P><SPAN>Your twenty years of knowing where the bodies are buried in SAP — that is not a liability in an AI-augmented world. </SPAN><STRONG>It is the irreplaceable ingredient that makes the AI's output correct and trustworthy.</STRONG></P><P><SPAN>The developers who will thrive are not the ones who <STRONG>resist this shift</STRONG>. And they are not the <STRONG>ones who hand everything to an LLM</STRONG> and hope for the best. They are the ones who <STRONG>treat AI as a powerful amplifier of their own judgment</STRONG> — <FONT color="#3366FF">who know enough to direct it well, verify its output, and design the systems it integrates with</FONT>.</SPAN></P><P><SPAN>That combination — </SPAN><STRONG>deep SAP domain knowledge plus AI tool fluency</STRONG><SPAN> — is genuinely rare right now. The window to build it is open. It will not stay open long.</SPAN></P><H1 id="toc-hId--1721979727">&nbsp;</H1><H1 id="toc-hId--1918493232">Wrapping Up</H1><P><SPAN>GTC 2026 was not a technology conference for GPU enthusiasts. It was a signal that the enterprise software industry has passed an inflection point. Jensen's slide showing SAP, Salesforce, ServiceNow, and Siemens side by side — each with a production agentic architecture — was not a prediction of what is coming. It was a photograph of what has already happened.</SPAN></P><P><SPAN>SAP has placed its bet. Joule is real, BTP is the platform, and the structured data of your SAP landscape — managed, modelled, and secured by ABAP code — is what gives every agent running on top of it the ground truth it needs to be useful.</SPAN></P><TABLE width="935px"><TBODY><TR><TD width="934px"><P><STRONG>Action Items</STRONG></P><P>✓<SPAN>&nbsp; </SPAN>Write clean CDS views.</P><P>✓<SPAN>&nbsp; </SPAN>Lock down your authority checks.</P><P>✓<SPAN>&nbsp; </SPAN>Tune for machine-speed consumption.</P><P>✓<SPAN>&nbsp; </SPAN>Raise clear exceptions.</P><P>✓<SPAN>&nbsp; </SPAN>Learn RAP.</P><P>✓<SPAN>&nbsp; </SPAN>Open Claude Code.</P><P><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR></TBODY></TABLE><P class="lia-align-center" style="text-align: center;"><SPAN>The revolution is not coming for your job. It is coming for the parts of your job that should have been automated years ago. What remains — and what becomes more valuable — is everything that requires judgment, domain knowledge, and the ability to see when the AI is confidently wrong.</SPAN></P><P class="lia-align-center" style="text-align: center;"><FONT color="#3366FF"><STRONG>That is the ABAP developer of 2026.</STRONG></FONT></P> 2026-04-03T18:00:00.017000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/moving-from-hub-gateway-to-direct-s-4hana-odata-v4-a-customized-reusable/ba-p/14359430 Moving from Hub Gateway to Direct S/4HANA OData V4: A Customized Reusable RAP Processing Framework 2026-04-07T05:11:24.260000+02:00 AnslemArnolda https://community.sap.com/t5/user/viewprofilepage/user-id/490939 <P>In SAP landscapes using a <STRONG>Gateway Hub architecture</STRONG>, OData services are typically exposed through a separate Gateway system, with data processing handled in the backend <STRONG>S/4HANA system via RFC calls</STRONG>. While this architecture has been widely used, it introduces additional system dependencies and layers of communication.</P><P>With the adoption of <STRONG>ABAP RESTful Application Programming Model (RAP)</STRONG> and <STRONG>OData V4</STRONG>, services can now be exposed <STRONG>directly from S/4HANA</STRONG>, allowing developers to simplify the architecture and implement more structured service processing patterns.</P><P>In this article, I share a <STRONG>framework concept I have used in my developments</STRONG> to organize how requests to <STRONG>RAP Custom Entities</STRONG> are handled. The approach introduces a <STRONG>reusable processing structure</STRONG> for request handling, data extraction, transformation, and response construction.</P><P>This concept is intended to improve <STRONG>maintainability, extensibility, and consistency</STRONG> when building RAP-based services, using existing ABAP objects.<BR /><BR /></P><H1 id="toc-hId-1663420542">End-to-End Implementation</H1><P>The framework is implemented as a <STRONG>base processing class</STRONG> that contains the common request handling logic such as entity configuration loading, request context extraction, data transformation, and response construction.</P><P>This class is <STRONG>not intended to be used directly</STRONG>. Instead, it is designed to be <STRONG>subclassed for each specific service implementation</STRONG>, where entity-specific logic such as data extraction and transformations can be implemented.</P><P>To better understand how the framework works, let's walk through the <STRONG>runtime execution flow when a client calls the OData V4 service.</STRONG></P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H2 id="toc-hId-1595989756">Custom Entity Implementation</H2></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Sales Order Details (OData V4)' @AccessControl.authorizationCheck: #NOT_REQUIRED @Metadata.allowExtensions: true @ObjectModel.query: { implementedBy: 'ABAP:ZCL_V4_SALESORDER_QUERY' } define root custom entity ZCE_SalesOrderDetails { key SalesOrder : vbeln; key SalesOrderItem : posnr; Material : matnr; Customer : kunnr; OrderQuantity : abap.quan(13,3); OrderUnit : meins; NetPrice : abap.curr(15,2); Currency : waers; CreatedBy : syuname; CreatedOn : abap.dats; DeliveryDate : abap.dats; _ScheduleLines : composition [0..*] of ZCE_SalesOrderSchedule; }</code></pre><P>&nbsp;</P><H2 id="toc-hId-1399476251">Using the Framework Class as a Superclass</H2><P>The class <STRONG><CODE>ZSV00CL_CE_V2TOV4_CONVERSION</CODE></STRONG> acts as a <STRONG>generic processing framework</STRONG> for RAP Custom Entities. It provides reusable logic for handling the complete request lifecycle, including request handling, data retrieval orchestration, payload extraction, entity transformation, and response construction.</P><P>To use this framework for a specific business entity, a <STRONG>child class must be created that inherits from the framework class</STRONG>. For example:&nbsp;</P><pre class="lia-code-sample language-abap"><code>ZCL_V4_SALESORDER_QUERY</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><SPAN>This class is defined as:</SPAN></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>CLASS zcl_v4_salesorder_query DEFINITION PUBLIC INHERITING FROM zsv00cl_ce_v2tov4_conversion FINAL CREATE PUBLIC.</code></pre><P>By inheriting from the framework class, the child class automatically gains access to the entire processing pipeline implemented in the superclass, including the implementation of the RAP interface method:</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>IF_RAP_QUERY_PROVIDER~SELECT</code></pre><P>This means the child class <STRONG>does not need to reimplement the standard request processing logic</STRONG>.</P><H3 id="toc-hId-1332045465">RAP Custom Entity Processing Framework – Method Responsibilities</H3><P>&nbsp;</P><P><STRONG>Method</STRONG><STRONG>Layer</STRONG><STRONG>When It Is Called</STRONG><STRONG>Purpose</STRONG><STRONG>Key Actions</STRONG></P><TABLE width="931px"><TBODY><TR><TD><STRONG>Method</STRONG></TD><TD><STRONG>Layer</STRONG></TD><TD><STRONG>When its called</STRONG></TD><TD><STRONG>Purpose</STRONG></TD><TD><STRONG>Key Actions</STRONG></TD></TR><TR><TD width="305px" height="85px"><STRONG>IF_RAP_QUERY_PROVIDER~SELECT</STRONG></TD><TD width="127px" height="85px">RAP Entry Layer</TD><TD width="139px" height="85px">Triggered when OData V4 service is called</TD><TD width="148px" height="85px">Entry point for RAP query processing</TD><TD width="212px" height="85px">Checks if data is requested, loads entity configurations, and starts request handling</TD></TR><TR><TD width="305px" height="112px"><STRONG>LOAD_ENTITY_CONFIGURATIONS</STRONG></TD><TD width="127px" height="112px">Configuration Layer</TD><TD width="139px" height="112px">Immediately after SELECT</TD><TD width="148px" height="112px">Loads metadata configuration for entities</TD><TD width="212px" height="112px">Defines entity mapping, source entity set, source/target types, and field mappings</TD></TR><TR><TD width="305px" height="112px"><STRONG>HANDLE_DATA_REQUEST</STRONG></TD><TD width="127px" height="112px">Processing Controller</TD><TD width="139px" height="112px">After configurations are loaded</TD><TD width="148px" height="112px">Main orchestrator for request execution</TD><TD width="212px" height="112px">Calls context extraction, determines entity type, and triggers fetch + transformation logic</TD></TR><TR><TD width="305px" height="85px"><STRONG>GET_REQUEST_CONTEXT</STRONG></TD><TD width="127px" height="85px">Context Layer</TD><TD width="139px" height="85px">Early in request handling</TD><TD width="148px" height="85px">Extracts request parameters from RAP request</TD><TD width="212px" height="85px">Reads filters, paging info, entity name, and key values</TD></TR><TR><TD width="305px" height="85px"><STRONG>IS_ROOT_ENTITY</STRONG></TD><TD width="127px" height="85px">Routing Layer</TD><TD width="139px" height="85px">After context extraction</TD><TD width="148px" height="85px">Determines whether request is root or child entity</TD><TD width="212px" height="85px">Controls whether data must be fetched or extracted from existing payload</TD></TR><TR><TD width="305px" height="140px"><STRONG>HANDLE_DATA_FETCH</STRONG></TD><TD width="127px" height="140px">Data Retrieval Layer</TD><TD width="139px" height="140px">When root entity is requested</TD><TD width="148px" height="140px">Retrieves data from backend sources</TD><TD width="212px" height="140px">Builds request keys, identifies main source entity set, and triggers backend retrieval (RFC / OData V2 / API)</TD></TR><TR><TD width="305px" height="85px"><STRONG>CALL_RFC_FOR_POST</STRONG> <EM>(optional)</EM></TD><TD width="127px" height="85px">Integration Layer</TD><TD width="139px" height="85px">During data fetch if backend logic needed</TD><TD width="148px" height="85px">Calls external logic such as RFC or API</TD><TD width="212px" height="85px">Used for backend system communication</TD></TR><TR><TD width="305px" height="85px"><STRONG>EXTRACT_DATA</STRONG></TD><TD width="127px" height="85px">Payload Layer</TD><TD width="139px" height="85px">After backend data is retrieved</TD><TD width="148px" height="85px">Extracts entity-specific data from payload container</TD><TD width="212px" height="85px">Dynamically unwraps source entity data into ABAP structures/tables</TD></TR><TR><TD width="305px" height="112px"><STRONG>HANDLE_ENTITY_CONVERSIONS_GET</STRONG></TD><TD width="127px" height="112px">Transformation Controller</TD><TD width="139px" height="112px">After data extraction</TD><TD width="148px" height="112px">Controls transformation process</TD><TD width="212px" height="112px">Determines if source data is structure or table and calls appropriate conversion method</TD></TR><TR><TD width="305px" height="85px"><STRONG>TRANSFORM_STRUCTURE_DATA</STRONG></TD><TD width="127px" height="85px">Transformation Layer</TD><TD width="139px" height="85px">When source payload is a structure</TD><TD width="148px" height="85px">Converts single record structure from V2 → V4</TD><TD width="212px" height="85px">Applies field mapping and conversion exits</TD></TR><TR><TD width="305px" height="85px"><STRONG>TRANSFORM_TABLE_DATA</STRONG></TD><TD width="127px" height="85px">Transformation Layer</TD><TD width="139px" height="85px">When source payload is a table</TD><TD width="148px" height="85px">Converts multiple records from V2 → V4</TD><TD width="212px" height="85px">Iterates over rows and applies field mapping</TD></TR><TR><TD width="305px" height="85px"><STRONG>CONVERSION_EXIT</STRONG></TD><TD width="127px" height="85px">Business Logic Layer</TD><TD width="139px" height="85px">During transformation</TD><TD width="148px" height="85px">Applies custom logic for specific fields</TD><TD width="212px" height="85px">Handles timestamp conversion, code mapping, calculated fields</TD></TR><TR><TD width="305px" height="85px"><STRONG>SET_RESPONSE_DATA</STRONG></TD><TD width="127px" height="85px">Response Layer</TD><TD width="139px" height="85px">Final step before returning result</TD><TD width="148px" height="85px">Sends processed data back to RAP framework</TD><TD width="212px" height="85px">Sets data and total record count in response object</TD></TR></TBODY></TABLE><H2 id="toc-hId-1006449241">&nbsp;</H2><H2 id="toc-hId-809935736">High-Level Runtime Flow</H2><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="ChatGPT Image Apr 7, 2026, 10_26_36 AM.png" style="width: 893px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/393914i7BC2271E2A6253CB/image-size/large?v=v2&amp;px=999" role="button" title="ChatGPT Image Apr 7, 2026, 10_26_36 AM.png" alt="ChatGPT Image Apr 7, 2026, 10_26_36 AM.png" /></span></P><H2 id="toc-hId-613422231">&nbsp;</H2><H2 id="toc-hId-416908726">Detailed Method Execution Sequence</H2><P>Below is the <STRONG>exact order of method execution when the OData V4 service is called.</STRONG></P><H3 id="toc-hId-349477940">Step 1 — RAP Query Trigger</H3><P>When a consumer calls the <STRONG>Custom Entity OData V4 service, RAP invokes :</STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD if_rap_query_provider~select. </code></pre><H4 id="toc-hId-282047154">&nbsp;<SPAN>Example implementation:</SPAN></H4><pre class="lia-code-sample language-abap"><code>METHOD if_rap_query_provider~select. IF io_request-&gt;is_data_requested( ). load_entity_configurations( ). handle_data_request( ii_request = io_request ii_response = io_response ). ENDIF. ENDMETHOD.</code></pre><H4 id="toc-hId--412183446">What happens here?</H4><OL><LI>RAP checks if data is requested.</LI><LI>Entity mappings are loaded.</LI><LI>The main processing method is called.</LI></OL><H3 id="toc-hId--315293944">Step 2 - Loading Configurations</H3><P>The method initializes the processing setup for the requested entity. It determines the <STRONG>entity being requested</STRONG> and loads the relevant configuration that defines how the framework should <STRONG>retrieve, process, and transform the data</STRONG>.</P><P>This step allows the framework to <STRONG>dynamically control entity-specific logic</STRONG> while keeping the main request handling flow <STRONG>generic and reusable</STRONG>.</P><H4 id="toc-hId--805210456">Method:&nbsp;</H4><pre class="lia-code-sample language-abap"><code>LOAD_ENTITY_CONFIGURATIONS</code></pre><H4 id="toc-hId--1001723961">Example:</H4><pre class="lia-code-sample language-abap"><code>gt_entity_config = VALUE #( ( entity_id = 'oDataV4 Entity 1 ' entity_set = 'oDataV2 Entity Set' src_type = 'Table Type for oDataV2 Entity 1' trg_type = 'Table Type for oDataV4 Entity 1' src_type_structure = abap_true src_is_entity_set = abap_true field_map = VALUE ty_abap_map_tab( ( src_field = 'oData V2 Field 1' trg_field = 'oData V4 Field 1' ) ( src_field = 'oData V2 Field 2' trg_field = 'oData V4 Field 2' ) ) ) ( entity_id = 'oDataV4 Entity 2 ' entity_set = 'oDataV2 Entity Set' src_type = 'Table Type for oDataV2 Entity 2' trg_type = 'Table Type for oDataV4 Entity 2' src_type_structure = abap_true src_is_entity_set = abap_true field_map = VALUE ty_abap_map_tab( ( src_field = 'oData V2 Field 1' trg_field = 'oData V4 Field 1' ) ( src_field = 'oData V2 Field 2' trg_field = 'oData V4 Field 2' ) ) ) .</code></pre><H3 id="toc-hId--904834459">Step 2 — Request Context Extraction</H3><P>The framework extracts important request parameters.</P><H4 id="toc-hId--1394750971">Method called:</H4><pre class="lia-code-sample language-abap"><code>GET_REQUEST_CONTEXT</code></pre><H4 id="toc-hId--1591264476">Example:</H4><pre class="lia-code-sample language-abap"><code>DATA(ls_context) = get_request_context( ii_request ).</code></pre><H5 id="toc-hId--2081180988">The context contains:</H5><UL><LI>Filters</LI><LI>Page size</LI><LI>Entity ID</LI><LI>Key values</LI></UL><H5 id="toc-hId-2017272803">Structure example:</H5><pre class="lia-code-sample language-abap"><code>Entity ID Filter Conditions Paging Information Keys</code></pre><H3 id="toc-hId--1887401984">&nbsp;</H3><H3 id="toc-hId--1915731798">Step 3 — Identify Root Entity</H3><P>The framework checks whether the request is for a <STRONG>root entity or a child entity. You can write your own logic here on how the root can be determined.&nbsp;</STRONG></P><H4 id="toc-hId-1889318986">Method:&nbsp;</H4><pre class="lia-code-sample language-abap"><code>IS_ROOT_ENTITY</code></pre><H5 id="toc-hId-1399402474">Why is this needed?</H5><P>Because <STRONG>root entities trigger the initial data retrieval, while child entities use already available payload data.</STRONG></P><P>&nbsp;</P><H3 id="toc-hId-1789694983">Step 4 — Fetch Source Data</H3><P>If the request is for the <STRONG>root entity, the following method is executed:</STRONG></P><pre class="lia-code-sample language-abap"><code>HANDLE_DATA_FETCH</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H4 id="toc-hId-1299778471">&nbsp;<SPAN>Example implementation:</SPAN></H4></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>METHOD handle_data_fetch. build_keys( iv_fieldname = 'FIELD_ID' iv_value = 'FIELD_VALUE' ). is_main_source_entity_set( ). ENDMETHOD.</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H5 id="toc-hId-809861959">&nbsp;<SPAN>Responsibilities of this method:</SPAN></H5><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><UL><LI>Build request keys</LI><LI>Determine the main source entity set</LI><LI>Trigger backend data retrieval (custom implementation)<P>This method is typically <STRONG>redefined in subclasses.</STRONG></P></LI></UL><H3 id="toc-hId-1200154468">Step 5 — Convert Entity Data</H3><P>Next, the framework converts the source payload to the target entity.</P><H4 id="toc-hId-710237956">Method:</H4></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>HANDLE_ENTITY_CONVERSIONS_GET</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><P>This method performs the main transformation workflow.</P><H5 id="toc-hId-220321444">Example:&nbsp;</H5></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code> FIELD-SYMBOLS: &lt;lfs_t_output_data&gt; TYPE STANDARD TABLE. FIELD-SYMBOLS: &lt;lfs_t_source_data&gt; TYPE STANDARD TABLE. FIELD-SYMBOLS: &lt;lfs_source_data&gt; TYPE any. FIELD-SYMBOLS: &lt;lfs_output_data&gt; TYPE any. DATA: lr_data TYPE REF TO data. " Extract source data for the entity DATA(lr_child_data) = extract_data( is_context-entity_id ). CHECK lr_child_data IS BOUND. READ TABLE gt_entity_config INTO DATA(ls_entity_config) WITH KEY entity_id = is_context-entity_id src_is_entity_set = mv_main_source_entity_set. CHECK sy-subrc = 0. IF ls_entity_config-src_type_structure = abap_true. ASSIGN lr_child_data-&gt;* TO &lt;lfs_source_data&gt;. ELSE. ASSIGN lr_child_data-&gt;* TO &lt;lfs_t_source_data&gt;. ENDIF. DATA(lr_transformed_data) = COND #( WHEN ls_entity_config-src_type_structure = abap_true AND &lt;lfs_source_data&gt; IS ASSIGNED THEN transform_structure_data( iv_entity_id = is_context-entity_id iv_source_data = &lt;lfs_source_data&gt; ) WHEN &lt;lfs_t_source_data&gt; IS ASSIGNED THEN transform_table_data( iv_entity_id = is_context-entity_id iv_source_data = &lt;lfs_t_source_data&gt; ) ). CHECK lr_transformed_data IS BOUND. IF ls_entity_config-src_type_structure = abap_true. CREATE DATA lr_data TYPE STANDARD TABLE OF (ls_entity_config-trg_type). ASSIGN lr_transformed_data-&gt;* TO &lt;lfs_output_data&gt;. ASSIGN lr_data-&gt;* TO &lt;lfs_t_output_data&gt;. CHECK &lt;lfs_output_data&gt; IS ASSIGNED AND &lt;lfs_t_output_data&gt; IS ASSIGNED. APPEND &lt;lfs_output_data&gt; TO &lt;lfs_t_output_data&gt;. ELSE. ASSIGN lr_transformed_data-&gt;* TO &lt;lfs_t_output_data&gt;. CHECK &lt;lfs_t_output_data&gt; IS ASSIGNED. ENDIF. set_response_data( ii_response = ii_response it_data = &lt;lfs_t_output_data&gt; ). IF &lt;lfs_source_data&gt; IS ASSIGNED. UNASSIGN &lt;lfs_source_data&gt;. ENDIF. IF &lt;lfs_t_output_data&gt; IS ASSIGNED. UNASSIGN &lt;lfs_t_output_data&gt;. ENDIF. IF &lt;lfs_t_output_data&gt; IS ASSIGNED. UNASSIGN &lt;lfs_t_output_data&gt;. ENDIF.</code></pre><H5 id="toc-hId-23807939">Execution inside the method:</H5><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Image 2.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/389498i0785E069FD24318C/image-size/medium?v=v2&amp;px=400" role="button" title="Image 2.png" alt="Image 2.png" /></span>&nbsp;</DIV><H3 id="toc-hId-582284139">Step 6 — Extract Payload Data</H3><P>Source data is extracted from the payload container.</P><H4 id="toc-hId-92367627">Method:</H4></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>EXTRACT_DATA</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H4 id="toc-hId--104145878">Example:</H4></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code> FIELD-SYMBOLS: &lt;lfs_t_reftable&gt; TYPE any. DATA(lv_type) = VALUE #( gt_entity_config[ entity_id = iv_entity_id src_is_entity_set = mv_main_source_entity_set ]-src_type OPTIONAL ). DATA(lv_entity_set) = VALUE #( gt_entity_config[ entity_id = iv_entity_id src_is_entity_set = mv_main_source_entity_set ]-entity_set OPTIONAL ). CREATE DATA rr_data TYPE (lv_type). ASSIGN rr_data-&gt;* TO &lt;lfs_t_reftable&gt;. IF &lt;lfs_t_reftable&gt; IS ASSIGNED. unwrap( EXPORTING it_data = VALUE #( gt_payload[ entity_set_name = lv_entity_set ]-data OPTIONAL ) iv_entity_set = lv_entity_set IMPORTING ev_data = &lt;lfs_t_reftable&gt; ). UNASSIGN &lt;lfs_t_reftable&gt;. ENDIF.</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;<SPAN>This returns a </SPAN><STRONG>dynamic data reference.</STRONG><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H3 id="toc-hId--7256376">Step 7 — Transform the Data</H3><P>The framework determines whether the payload is:</P><UL><LI>A <STRONG>structure</STRONG></LI><LI>A <STRONG><STRONG>table</STRONG></STRONG></LI></UL><H4 id="toc-hId--497172888">Structure Conversion</H4><H5 id="toc-hId--987089400">Method:</H5></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>TRANSFORM_STRUCTURE_DATA</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H5 id="toc-hId--1183602905">Example:</H5></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code> FIELD-SYMBOLS: &lt;lfs_source&gt; TYPE any, &lt;lfs_target&gt; TYPE any. DATA: lr_source_ref TYPE REF TO data, lr_target_ref TYPE REF TO data. READ TABLE gt_entity_config INTO DATA(ls_entity_cfg) WITH KEY entity_id = iv_entity_id src_is_entity_set = mv_main_source_entity_set. IF sy-subrc &lt;&gt; 0. RETURN. ENDIF. CREATE DATA lr_source_ref TYPE (ls_entity_cfg-src_type)."Creation of Reference Structure for V2 Structure CREATE DATA lr_target_ref TYPE (ls_entity_cfg-trg_type)."Creation of Reference Structure for V4 Structure ASSIGN lr_source_ref-&gt;* TO &lt;lfs_source&gt;. &lt;lfs_source&gt; = CORRESPONDING #( iv_source_data ). ASSIGN lr_target_ref-&gt;* TO &lt;lfs_target&gt;. LOOP AT ls_entity_cfg-field_map ASSIGNING FIELD-SYMBOL(&lt;lfs_field_map&gt;). ASSIGN COMPONENT &lt;lfs_field_map&gt;-src_field OF STRUCTURE &lt;lfs_source&gt; TO FIELD-SYMBOL(&lt;lfs_src_val&gt;). ASSIGN COMPONENT &lt;lfs_field_map&gt;-trg_field OF STRUCTURE &lt;lfs_target&gt; TO FIELD-SYMBOL(&lt;lfs_trg_val&gt;). IF &lt;lfs_src_val&gt; IS ASSIGNED AND &lt;lfs_trg_val&gt; IS ASSIGNED. DATA(lv_conv_val) = conversion_exit( EXPORTING iv_source = &lt;lfs_source&gt; iv_entity_id = iv_entity_id iv_field_name = &lt;lfs_field_map&gt;-trg_field ). &lt;lfs_trg_val&gt; = COND #( WHEN lv_conv_val IS NOT INITIAL THEN lv_conv_val ELSE &lt;lfs_src_val&gt; ). ENDIF. ENDLOOP. CREATE DATA rr_data LIKE &lt;lfs_target&gt;. rr_data-&gt;* = &lt;lfs_target&gt;.</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;<DIV class=""><DIV class=""><DIV class=""><DIV class=""><H4 id="toc-hId--1086713403">Table Conversion</H4><DIV class=""><DIV class=""><H5 id="toc-hId--1576629915">Method:<SPAN>&nbsp;</SPAN></H5></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>TRANSFORM_TABLE_DATA​</code></pre><DIV class=""><DIV class=""><H5 id="toc-hId--1773143420">Example:</H5></DIV></DIV><pre class="lia-code-sample language-abap"><code> FIELD-SYMBOLS: &lt;lfs_t_target_table&gt; TYPE STANDARD TABLE, &lt;lfs_source_row&gt; TYPE any, &lt;lfs_target_row&gt; TYPE any. DATA: lr_source_table_ref TYPE REF TO data, lr_target_table_ref TYPE REF TO data. READ TABLE gt_entity_config INTO DATA(ls_entity_config) WITH KEY entity_id = iv_entity_id src_is_entity_set = mv_main_source_entity_set. IF sy-subrc &lt;&gt; 0. RETURN. ENDIF. CREATE DATA lr_source_table_ref TYPE (ls_entity_config-src_type)."Creation of Reference Table for V2 Structure CREATE DATA lr_target_table_ref TYPE (ls_entity_config-trg_type)."Creation of Reference Table for V4 Structure ASSIGN lr_source_table_ref-&gt;* TO &lt;lfs_t_target_table&gt;. &lt;lfs_t_target_table&gt; = CORRESPONDING #( iv_source_data ). ASSIGN lr_target_table_ref-&gt;* TO &lt;lfs_t_target_table&gt;. LOOP AT lr_source_table_ref-&gt;* ASSIGNING &lt;lfs_source_row&gt;. CREATE DATA lr_target_table_ref LIKE LINE OF &lt;lfs_t_target_table&gt;. ASSIGN lr_target_table_ref-&gt;* TO &lt;lfs_target_row&gt;. LOOP AT ls_entity_config-field_map ASSIGNING FIELD-SYMBOL(&lt;lfs_field_mapping&gt;). "#EC CI_NESTED. ASSIGN COMPONENT &lt;lfs_field_mapping&gt;-src_field OF STRUCTURE &lt;lfs_source_row&gt; TO FIELD-SYMBOL(&lt;lfs_source_value&gt;). ASSIGN COMPONENT &lt;lfs_field_mapping&gt;-trg_field OF STRUCTURE &lt;lfs_target_row&gt; TO FIELD-SYMBOL(&lt;lfs_target_value&gt;). DATA(lv_converted_value) = conversion_exit( EXPORTING iv_source = &lt;lfs_source_row&gt; iv_entity_id = iv_entity_id iv_field_name = &lt;lfs_field_mapping&gt;-src_field ). &lt;lfs_target_value&gt; = COND #( WHEN lv_converted_value IS NOT INITIAL THEN lv_converted_value ELSE &lt;lfs_source_value&gt; ). ENDLOOP. APPEND &lt;lfs_target_row&gt; TO &lt;lfs_t_target_table&gt;. ENDLOOP. CREATE DATA rr_data LIKE &lt;lfs_t_target_table&gt;. rr_data-&gt;* = &lt;lfs_t_target_table&gt;.</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H3 id="toc-hId--1214667220"><SPAN>Step 8 — Apply Custom Field Conversions</SPAN></H3><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><P>Before assigning values, the framework checks for <STRONG>custom conversions.</STRONG></P><H4 id="toc-hId--1704583732">Method:</H4></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>CONVERSION_EXIT</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H5 id="toc-hId-2100467052">Example:</H5></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code> "Redefine and write your own conversions. Example:If any logic was written in D24 for a field, you can write it here. CASE iv_entity_id. WHEN 'Entity ID 1'. ASSIGN COMPONENT iv_field_name OF STRUCTURE iv_source TO FIELD-SYMBOL(&lt;lfs_source_value&gt;). IF iv_field_name = 'FIELD NAME'. rv_field_value = 'Custom Logic' ##NO_TEXT. ENDIF. WHEN OTHERS. ENDCASE.</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><P>This allows transformations such as:</P><UL><LI>Timestamp conversion</LI><LI>Code translation</LI><LI>Business logic fields</LI></UL><H3 id="toc-hId--1804207735">Step 9 — Send Response Back to RAP</H3><P>Finally, the transformed data is sent to the RAP response object.</P><H4 id="toc-hId-2000843049">Method:</H4></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code>SET_RESPONSE_DATA</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H5 id="toc-hId-1510926537">&nbsp;<SPAN>Example:</SPAN></H5></DIV></DIV></DIV></DIV></DIV></DIV></DIV><pre class="lia-code-sample language-abap"><code> ii_response-&gt;set_total_number_of_records( lines( it_data ) ). ii_response-&gt;set_data( it_data ).</code></pre><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;<SPAN>RAP then returns the result to the </SPAN><STRONG>OData V4 service consumer.</STRONG><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><P>&nbsp;</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><H2 id="toc-hId--2100345243"><SPAN>Key Takeaway</SPAN></H2><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><P>The framework follows a <STRONG>layered execution approach:</STRONG></P><P>&nbsp;</P><TABLE border="1" width="100%"><TBODY><TR><TD width="50%" height="30px"><STRONG>Layer</STRONG></TD><TD width="50%" height="30px"><STRONG>Responsibility</STRONG></TD></TR><TR><TD width="50%" height="30px">RAP Layer</TD><TD width="50%" height="30px">Entry point for OData V4 request</TD></TR><TR><TD width="50%" height="35px">Context Layer</TD><TD width="50%" height="35px">Extract request parameters</TD></TR><TR><TD width="50%" height="30px">Data Layer</TD><TD width="50%" height="30px">Retrieve V2 payload</TD></TR><TR><TD width="50%" height="30px">Transformation Layer</TD><TD width="50%" height="30px">Convert V2 → V4</TD></TR><TR><TD width="50%" height="30px">Response Layer</TD><TD width="50%" height="30px">Return RAP response</TD></TR></TBODY></TABLE><DIV class="">&nbsp;</DIV><DIV class=""><SPAN>This design ensures the framework is:</SPAN></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV><UL><LI><STRONG>Reusable</STRONG></LI><LI><STRONG>Extensible</STRONG></LI><LI><STRONG>Entity independent</STRONG></LI><LI><STRONG>Compatible with RAP architecture</STRONG></LI></UL><P>&nbsp;</P> 2026-04-07T05:11:24.260000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/custom-business-configurations-f4579-collaborative-draft/ba-p/14368541 Custom Business Configurations (F4579): Collaborative draft 2026-04-08T10:55:35.536000+02:00 patrick_winkler https://community.sap.com/t5/user/viewprofilepage/user-id/729521 <H1 id="toc-hId-887480474" id="toc-hId-1664315265">Introduction</H1><P><SPAN>The&nbsp;</SPAN><A href="https://help.sap.com/docs/btp/sap-business-technology-platform/custom-business-configurations-app" target="_blank" rel="noopener noreferrer"><SPAN class="">Custom Business Configurations</SPAN></A><SPAN>&nbsp;(CUBCO) app serves as an entry point to the&nbsp;</SPAN><A class="" title="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/business-configuration-maintenance-object?version=Cloud" href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/business-configuration-maintenance-object?version=Cloud" target="_blank" rel="noopener noreferrer">Business Configuration Maintenance Object</A><SPAN>&nbsp;(SMBC) provided by custom applications or partners.<BR /><BR />When you use the&nbsp;<A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/generating-business-configuration-maintenance-object-with-generate-abap-repository-objects-wizard" target="_blank" rel="noopener noreferrer">ADT wizard to generate a Business Configuration Maintenance Object</A></SPAN>, the singleton pattern is used when generating the draft enabled RAP BO. One <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/rap-transactional-app-performance-considerations-for-singleton-pattern-in/ba-p/14140680" target="_blank">characteristic of this pattern</A> is that all child entities are locked together, so it is not possible for multiple users to work with the same configuration at the same time.<BR /><BR />This blog describes how to avoid this restriction by using <A href="https://help.sap.com/docs/abap-cloud/abap-rap/collaborative-draft" target="_blank" rel="noopener noreferrer">collaborative draft</A>.</P><P>This blog is relevant for</P><UL><LI><a href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Cloud+Private+Edition/pd-p/5c26062a-9855-4f39-8205-272938b6882f" class="lia-product-mention" data-product="1198-1">SAP S/4HANA Cloud Private Edition</a>&nbsp;(2025)</LI><LI><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>&nbsp;</LI><LI><a href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Cloud+Public+Edition/pd-p/08e2a51b-1ce5-4367-8b33-4ae7e8b702e0" class="lia-product-mention" data-product="1199-1">SAP S/4HANA Cloud Public Edition</a>&nbsp;</LI></UL><P>Further reading:</P><UL><LI><A href="https://community.sap.com/t5/tag/business%20configuration%20maintenance%20object/tg-p/board-id/technology-blog-sap" target="_blank">Related blog posts</A></LI><LI>Learn how you can use<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap?url_id=text-sapcommunity-prdteng-ABAP" target="_blank" rel="noopener noreferrer">ABAP technology</A><SPAN>&nbsp;</SPAN>to develop innovative applications and business solutions across SAP’s portfolio on<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap" target="_blank" rel="noopener noreferrer">SAP Learning Site</A>.</LI></UL><H1 id="toc-hId-1439888495" id="toc-hId-1467801760">Example scenario</H1><P>In the configuration table ZDEMO_CC, the company codes are stored.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Company Code' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #C @AbapCatalog.dataMaintenance : #ALLOWED define table zdemo_cc { key client : abap.clnt not null; key company_code : abap.numc(4) not null; country : land1; last_changed_at : abp_lastchange_tstmpl; local_last_changed_at : abp_locinst_lastchange_tstmpl; }</code></pre><H1 id="toc-hId-1271288255">Enable collaborative draft</H1><P>In the generated behavior definition, add "collaborative" to the draft annotation:</P><pre class="lia-code-sample language-abap"><code>managed with additional save implementation in class ZBP_I_COMPANYCODE_S unique; strict; with collaborative draft;</code></pre><P>Add the draft query annotation and the share action to the singleton entity definition:</P><pre class="lia-code-sample language-abap"><code>define behavior for ZI_CompanyCode_S alias CompanyCodeAll draft table ZDEMO_CC_D_S query ZR_DEMO_CC_CD [...] { draft action Share; [...] }</code></pre><P>&nbsp;<SPAN>Right-click the singleton draft table and choose New Data Definition to create the CDS view for the draft query:<BR /></SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Draft Query View' define view entity ZR_DEMO_CC_CD as select from zdemo_cc_d_s { key singletonid as Singletonid, lastchangedatmax as Lastchangedatmax, transportrequestid as Transportrequestid, draftentitycreationdatetime as Draftentitycreationdatetime, draftentitylastchangedatetime as Draftentitylastchangedatetime, draftadministrativedatauuid as Draftadministrativedatauuid, draftentityoperationcode as Draftentityoperationcode, hasactiveentity as Hasactiveentity, draftfieldchanges as Draftfieldchanges }</code></pre><P>You need to <A href="https://help.sap.com/docs/abap-cloud/abap-rap/adding-collaboration-capability-to-draft-enabled-managed-business-object?version=LATEST&amp;locale=en-US&amp;state=PRODUCTION#prerequisites" target="_self" rel="noopener noreferrer">enhance the authorization of the involved users</A>. In SAP BTP ABAP environment and SAP S/4HANA Public Cloud Edition you assign business catalog&nbsp;<SPAN>SAP_CORE_BC_RAP_DRAFT_PC to the user, in SAP S/4HANA Private Cloud Edition authorization object S_DRAFT.</SPAN></P><H1 id="toc-hId-1074774750"><SPAN>Collaboration</SPAN></H1><P><SPAN>User TB is creating a new company code 400 while at the same time user PW is editing company code 100. All changes are recorded on the same transport request.<BR /></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="patrick_winkler_0-1775638407159.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394868i8F2BB89D6419285F/image-size/large?v=v2&amp;px=999" role="button" title="patrick_winkler_0-1775638407159.png" alt="patrick_winkler_0-1775638407159.png" /></span></P><P>Since the collaborative draft is enabled for the singleton root entity, the save and discard draft action are applied to all object changes. In this example, users PW and TB process two different company codes, which are logically different objects. Technically, however, they process the same singleton root entity. In practice, this means that discarding the draft or saving must be aligned between the users involved, as the save action persists the changes in all company codes.<BR />For completely independent editing of individual objects, you must develop a RAP BO without a singleton pattern.</P> 2026-04-08T10:55:35.536000+02:00 https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/transaction-cannot-be-revived-after-delayed-abortion-rap-runtime-019-must/ba-p/14346818 Transaction cannot be revived after delayed abortion(RAP_RUNTIME 019) - Must ROLLBACK After Failures 2026-04-08T11:52:21.989000+02:00 Xavier_Newbie https://community.sap.com/t5/user/viewprofilepage/user-id/1492998 <H2 id="toc-hId-1791494157">1. Symptom (ST22 / Dump Keywords)</H2><P>A typical dump looks like this (same category as in your case):</P><UL><LI>Runtime Error:<SPAN>&nbsp;</SPAN><CODE>RAISE_SHORTDUMP</CODE></LI><LI>Exception:<SPAN>&nbsp;</SPAN><CODE>CX_SADL_DUMP_APPL_MODEL_ERROR</CODE></LI><LI>T100:<SPAN>&nbsp;</SPAN><CODE>RAP_RUNTIME</CODE><SPAN>&nbsp;</SPAN>/<SPAN>&nbsp;</SPAN><CODE>019</CODE></LI><LI>Short Text:<SPAN>&nbsp;</SPAN><CODE>Transaction cannot be revived after delayed abortion (BO: I_ENTERPRISEPROJECTTP_2)</CODE></LI></UL><P>When debugging you may also notice RAP internal state like:</P><UL><LI><CODE>CL_RAP_BHV_PROCESSOR-&gt;IF_RAP_LEGACY_TRANSACTION~MV_ABORT = abap_true</CODE></LI></UL><P>Meaning: RAP has already marked the logical transaction as<SPAN>&nbsp;</SPAN><STRONG>aborted</STRONG><SPAN>&nbsp;</SPAN>(delayed abortion). It must not be reused.<BR /><BR /></P><H2 id="toc-hId-1594980652">2. Root Cause: RAP Transaction State + Delayed Abortion</H2><P>A RAP EML “logical transaction” typically consists of two phases:</P><OL><LI><STRONG>Transaction phase</STRONG></LI></OL><UL><LI><CODE>MODIFY ENTITIES</CODE><SPAN>&nbsp;</SPAN>runs validations/determinations/checks.</LI><LI>Errors are usually returned in<SPAN>&nbsp;</SPAN><CODE>FAILED</CODE><SPAN>&nbsp;</SPAN>/<SPAN>&nbsp;</SPAN><CODE>REPORTED</CODE></LI></UL><OL><LI><STRONG>Save phase</STRONG></LI></OL><UL><LI><CODE>COMMIT ENTITIES</CODE><SPAN>&nbsp;</SPAN>triggers the save sequence (finalize/save, etc.).</LI><LI>Errors again come back via<SPAN>&nbsp;</SPAN><CODE>FAILED</CODE><SPAN>&nbsp;</SPAN>/<SPAN>&nbsp;</SPAN><CODE>REPORTED</CODE><SPAN>&nbsp;</SPAN>(in the commit response this is the<SPAN>&nbsp;</SPAN><STRONG>LATE</STRONG><SPAN>&nbsp;</SPAN>response).</LI></UL><P>If anything fails in either phase, RAP can enter<SPAN>&nbsp;</SPAN><STRONG>delayed abortion</STRONG>:<BR />Your ABAP code continues running,but the RAP logical transaction becomes<SPAN>&nbsp;</SPAN><STRONG>unusable</STRONG>.<BR />Any subsequent EML using that aborted transaction context may lead to<SPAN>&nbsp;</SPAN><CODE>RAP_RUNTIME 019</CODE>.</P><pre class="lia-code-sample language-abap"><code>MODIFY ENTITIES OF &lt;BO&gt; ... FAILED ls_failed_tx REPORTED ls_reported_tx. COMMIT ENTITIES ... FAILED ls_failed_save REPORTED ls_reported_save. " If commit failed but you don't rollback here, " next EML call can dump (RAP_RUNTIME 019) MODIFY ENTITIES OF &lt;BO&gt; ...</code></pre><P><SPAN>Key point:&nbsp;</SPAN><STRONG>Never continue with another EML step after a failure without rollback.<BR /><BR /></STRONG></P><H2 id="toc-hId-1398467147">5. Correct Example&nbsp;Using<SPAN>&nbsp;</SPAN><CODE>I_EnterpriseProjectTP_3</CODE></H2><pre class="lia-code-sample language-abap"><code>*&amp;---------------------------------------------------------------------* *&amp; Report zdemo_rap_eml_epp3 *&amp;---------------------------------------------------------------------* *&amp; *&amp;---------------------------------------------------------------------* REPORT zdemo_rap_eml_epp3. PARAMETERS: p_uuid TYPE sysuuid_c36 OBLIGATORY, p_desc TYPE string DEFAULT 'Demo update from EML'. START-OF-SELECTION. DATA lv_uuid_x16 TYPE sysuuid_x16. TRY. cl_system_uuid=&gt;convert_uuid_c36_static( EXPORTING uuid = p_uuid IMPORTING uuid_x16 = lv_uuid_x16 ). CATCH cx_uuid_error. WRITE: / 'Invalid UUID format. Use 36-char UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).'. RETURN. ENDTRY. DATA: ls_failed_tx TYPE RESPONSE FOR FAILED i_enterpriseprojecttp_3, ls_reported_tx TYPE RESPONSE FOR REPORTED i_enterpriseprojecttp_3, ls_failed_save TYPE RESPONSE FOR FAILED LATE i_enterpriseprojecttp_3, ls_reported_save TYPE RESPONSE FOR REPORTED LATE i_enterpriseprojecttp_3. TRY. " Transaction phase MODIFY ENTITIES OF i_enterpriseprojecttp_3 ENTITY EnterpriseProject UPDATE FIELDS ( ProjectDescription ) WITH VALUE #( ( %key-ProjectUUID = lv_uuid_x16 ProjectDescription = p_desc ) ) FAILED ls_failed_tx REPORTED ls_reported_tx. IF ls_failed_tx IS NOT INITIAL. WRITE: / 'MODIFY ENTITIES failed (transaction phase) -&gt; ROLLBACK ENTITIES'. ROLLBACK ENTITIES. RETURN. ENDIF. " Save phase COMMIT ENTITIES BEGIN RESPONSE OF i_enterpriseprojecttp_3 FAILED ls_failed_save REPORTED ls_reported_save. COMMIT ENTITIES END. IF ls_failed_save IS NOT INITIAL. WRITE: / 'COMMIT ENTITIES failed (save phase) -&gt; ROLLBACK ENTITIES'. ROLLBACK ENTITIES. RETURN. ENDIF. WRITE: / 'COMMIT ENTITIES OK.'. CATCH cx_root INTO DATA(lx_root). WRITE: / 'Unexpected exception:', lx_root-&gt;get_text( ). " Keep the EML contract: if anything goes wrong, end the logical transaction. ROLLBACK ENTITIES. ENDTRY.</code></pre><H3 id="toc-hId-1331036361">Two important details in this example</H3><OL><LI><CODE>COMMIT ENTITIES</CODE><SPAN>&nbsp;</SPAN>requires<SPAN>&nbsp;</SPAN><STRONG>LATE</STRONG><SPAN>&nbsp;</SPAN>response types</LI></OL><UL><LI><CODE>RESPONSE FOR FAILED LATE i_enterpriseprojecttp_3</CODE></LI><LI><CODE>RESPONSE FOR REPORTED LATE i_enterpriseprojecttp_3</CODE></LI></UL><OL><LI>“Fail fast” with rollback</LI></OL><UL><LI>If<SPAN>&nbsp;</SPAN><CODE>FAILED</CODE><SPAN>&nbsp;</SPAN>is not initial in either phase, rollback and end the logical transaction.</LI><LI>Start the next business step in a<SPAN>&nbsp;</SPAN><STRONG>new</STRONG><SPAN>&nbsp;</SPAN>logical transaction context.</LI></UL><P>&nbsp;</P><H2 id="toc-hId-1005440137">8. FAQ: Why not<SPAN>&nbsp;</SPAN><CODE>ROLLBACK WORK</CODE>?</H2><P>In RAP EML, use the EML-compatible statement:</P><UL><LI>Errors in EML logical transaction →<SPAN>&nbsp;</SPAN><CODE>ROLLBACK ENTITIES</CODE></LI></UL><P><CODE>ROLLBACK WORK</CODE><SPAN>&nbsp;</SPAN>is lower-level LUW handling and can lead to semantic mismatches with RAP’s logical transaction handling.<SPAN>&nbsp;</SPAN><SPAN>The dump you saw is fundamentally about the RAP logical transaction being aborted and still being reused.</SPAN></P> 2026-04-08T11:52:21.989000+02:00 https://community.sap.com/t5/enterprise-architecture-blog-posts/cap-rap-or-both-stop-hiding-behind-it-depends/ba-p/14363902 CAP, RAP, or Both: Stop Hiding Behind 'It Depends' 2026-04-09T02:05:37.595000+02:00 walteraguerom https://community.sap.com/t5/user/viewprofilepage/user-id/141635 <P>I've read every major article on this topic. They all end the same way:</P><P><EM>"Both models are complementary. Choose based on your context. Use each for the right scenario."</EM></P><P>That is a technically accurate, professionally safe, and completely useless answer.</P><P>This article gives you the framework I actually use with clients — including the uncomfortable parts most posts skip: the staffing bias that impact architecture decisions, what to do when your client has thousands of Z-objects, why migrating RAP to CAP is not a refactor, and why CAP is the only model that enforces the Clean Core promise architecturally.</P><P>One thing upfront:<SPAN>&nbsp;</SPAN><STRONG>this is not an anti-ABAP article.</STRONG><SPAN>&nbsp;</SPAN>ABAP knowledge — deep understanding of business processes, transactional models, and SAP data structures accumulated over years — is one of the most valuable assets in enterprise</P><H2 id="toc-hId-1793252750">What We're Actually Debating</H2><P><STRONG>RAP (ABAP RESTful Application Programming Model)</STRONG><SPAN>&nbsp;</SPAN>is SAP's extension model for the S/4HANA core and ABAP Cloud environments. It's ABAP-native, optimized for extending standard SAP Business Objects with transactional consistency. SAP's answer to "how do I extend S/4HANA without modifying standard code."</P><P><STRONG>CAP (Cloud Application Programming Model)</STRONG><SPAN>&nbsp;</SPAN>is SAP's framework for building cloud-native services on SAP BTP. Node.js and Java, BTP Cloud Foundry and Kyma, multitenancy, eventing, and OData/GraphQL out of the box. SAP's answer to "how do I build new business capabilities that live outside the S/4HANA core."</P><P>Same goal — Clean Core, BTP-native development. Fundamentally different execution contexts.</P><H2 id="toc-hId-1596739245">The Uncomfortable Truth First</H2><P>Most teams choosing between CAP and RAP are not making a pure architectural decision.</P><P>They're making a decision shaped by team composition.</P><P>An ABAP-heavy organization will present RAP as the natural choice for almost any BTP extension. The justification will sound architectural: "We need access to S/4HANA data," "We want to leverage existing ABAP logic," "RAP is Clean Core approved." All of those statements can be true and still be a rationalization. The real driver is often simpler: the team knows ABAP, so RAP wins.</P><P>This is a systemic pattern worth naming explicitly — because when staffing drives architecture, the decisions look reasonable one at a time and catastrophic in aggregate. The job of a principal architect is to separate these two questions before the decision is made.</P><H2 id="toc-hId-1400225740">When RAP Is Genuinely the Right Answer</H2><H3 id="toc-hId-1332794954">1. Extending Standard SAP Business Objects</H3><P>If you need to extend standard S/4HANA entities — Sales Orders, Purchase Requisitions, Business Partners, Material Documents — RAP is purpose-built for this. Proper lock handling, transactional consistency, draft management, clean integration with standard SAP workflows. Trying to replicate this via CAP calling S/4HANA APIs introduces latency, complexity, and consistency gaps that are difficult to manage at scale. For tight S/4HANA core extensions, RAP's native access is a genuine advantage, not just familiarity.</P><H3 id="toc-hId-1136281449">2. On-Premise + Cloud Parity Requirements</H3><P>RAP runs on both on-premise ABAP Platform and ABAP Cloud. CAP is cloud-only. Hybrid deployment, phased cloud migration, regulated industries with on-premise constraints — RAP is the only viable option when parity is required.</P><H3 id="toc-hId-939767944">3. Maximizing Existing ABAP Investment</H3><P>Complex, well-tested ABAP business logic that needs to be exposed as a service can be wrapped with a Clean Core API facade in RAP without rewriting it. Legitimate — with the caveat covered in the Z section below.</P><H3 id="toc-hId-743254439">4. Simple, Bounded In-App Extensions</H3><P>A single-source, single-consumer extension with no external integrations and no scalability concerns does not need the infrastructure overhead of CAP. Don't over-engineer.</P><H2 id="toc-hId-417658215">When CAP Is the Right Answer — Which Is More Often Than You Think</H2><H3 id="toc-hId-350227429">1. Multi-Source Data Scenarios</H3><P>The moment your extension needs data from more than one system, CAP is the correct choice. CAP's service layer was designed for data federation. RAP operating across multiple external sources becomes a manual integration exercise.</P><H3 id="toc-hId-153713924">2. Independent Scalability</H3><P>Side-by-side BTP extensions with variable load profiles should not be constrained by ABAP runtime characteristics. CAP on BTP Cloud Foundry or Kyma scales horizontally.</P><H3 id="toc-hId--118030950">3. Talent Pool and Long-Term Maintainability</H3><P>The global Node.js and Java developer pool is orders of magnitude larger than ABAP Cloud specialists. But the real point is different: the scarce resource in SAP programs is not syntax knowledge — it's SAP business process knowledge. An architecture that requires ABAP Cloud specialists to maintain it creates organizational dependency risk. An ABAP architect who already has the process knowledge and adds CAP to their toolkit is the most valuable person in the room.</P><H3 id="toc-hId--314544455">4. Multi-Tenant SaaS and Reusable Services</H3><P>CAP's multitenancy model is production-grade out of the box. RAP was not designed for this.</P><H3 id="toc-hId--511057960">5. Non-SAP Ecosystem Integration</H3><P>When your extension lives at the intersection of SAP and non-SAP systems, CAP's polyglot nature is a structural advantage. Building this in RAP means engineering brittle ABAP-to-external-API bridges.</P><H3 id="toc-hId--707571465">6. Survival of SAP Upgrade Cycles</H3><P>CAP extensions communicate with S/4HANA via stable, versioned APIs. They have no ABAP lifecycle. This connects directly to the Clean Core argument below.</P><H2 id="toc-hId--610681963">The Big Z Problem</H2><P>Most clients don't arrive at this debate with a clean slate. They arrive with 1,000, 2,000, sometimes 5,000 Z-objects accumulated over decades of ECC customization. The first proposal is almost always:<SPAN>&nbsp;</SPAN><EM>"Let's migrate the Zs to RAP."</EM></P><P>This is the wrong answer — and it is expensive.</P><P>The right question, before any migration decision, is:<SPAN>&nbsp;</SPAN><STRONG>why does each Z-object exist?</STRONG></P><P><STRONG>Category 1 — SAP now covers it natively (retire it)</STRONG><BR />A significant portion of Z-objects from ECC exist because SAP didn't have that functionality in 2005. S/4HANA and BTP may have it natively today. Before migrating anything, check whether SAP standard now covers the need. If it does: retire the Z. Don't migrate what you should delete.&nbsp;</P><P><STRONG>Category 2 — Genuine core business object extension (evaluate RAP)</STRONG><BR />Some Z-objects encode real business logic tightly coupled to SAP transactional objects — custom validations on Sales Orders, derivations on Financial documents, extensions to MM flows. These are legitimate RAP candidates because the coupling is intentional and the transactional integrity requirement is real.</P><P><STRONG>Category 3 — Side-by-side logic that never belonged in the core (evaluate CAP with bias)</STRONG><BR />Many Z-objects are reporting utilities, data extraction routines, integration helpers, or workflow logic placed inside the ABAP stack out of convenience, not architecture. These should be evaluated for CAP first — finally living where they should have lived from the beginning. One qualifier: if the logic requires HANA-native pushdown on large datasets (financial close, ATP, margin analysis at scale), or participates in SAP's transactional determination chain, evaluate before rebuilding. The default is still CAP. The exception needs justification.</P><P>The Big Z is not a RAP migration backlog. It's a forcing function to finally answer which of your customizations your business actually needs, which SAP now covers natively, and which were architectural accidents waiting to be corrected.</P><P>And for the Zs that survive the triage but can't be rewritten this quarter — wrap them. A clean API boundary around legacy logic is not technical debt; it's the first act of modernization. What you cannot afford is leaving Z-code directly exposed. That's how "temporary" becomes permanent.</P><H2 id="toc-hId--807195468">The CAP First Decision Protocol</H2><P><STRONG>CAP First</STRONG><SPAN>&nbsp;</SPAN>means the burden of proof shifts. The default is CAP. RAP must earn its place by satisfying a specific gate.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="diagram-cap-rap-decision (3).png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/392170iA2CA13F50E174F9B/image-size/large?v=v2&amp;px=999" role="button" title="diagram-cap-rap-decision (3).png" alt="diagram-cap-rap-decision (3).png" /></span></P><P><STRONG>The only gate that stops CAP:</STRONG><SPAN>&nbsp;</SPAN>transactional consistency with a standard SAP Business Object, bounded to S/4HANA data, with no external systems, no multi-tenancy, and no independent scalability requirements.</P><P>One honest prerequisite:<SPAN>&nbsp;</SPAN><STRONG>CAP First is the right posture for organizations that have already invested in BTP platform maturity</STRONG><SPAN>&nbsp;</SPAN>— an operational subaccount, CI/CD for Node.js or Java, and the operational capability for Cloud Foundry or Kyma workloads. For organizations that haven't built that foundation yet, CAP First is still the right destination — it comes with a deliberate enablement journey first. The posture doesn't change. The timeline does.</P><P>Why does the default posture matter? Because the cost of the wrong decision is asymmetric.</P><H2 id="toc-hId--1003708973">The Real Cost of Getting the Model Wrong</H2><P>A clarification worth making explicitly, because ABAP architects will correctly challenge a weaker version of this argument:</P><P><STRONG>Upgrading S/4HANA with existing RAP extensions is relatively low cost</STRONG><SPAN>&nbsp;</SPAN>— if your RAP uses only ABAP Cloud Released APIs (C1), which is what Clean Core RAP should do. SAP guarantees stability on released APIs across versions. ABAP teams who make this point are correct.</P><P>The cost argument I'm making is different. It's about<SPAN>&nbsp;</SPAN><STRONG>switching models</STRONG><SPAN>&nbsp;</SPAN>— what happens when you built in RAP and later discover that CAP was the right choice: multi-source data appeared, scalability became a requirement, the business needs this as a multi-tenant service.</P><P>RAP is ABAP. CAP is Node.js or Java. These are not different versions of the same language — different runtimes, different deployment models, different ecosystems entirely. When you migrate a RAP service to CAP:</P><UL><LI>The CDS data model needs to be rewritten in CAP CDS (similar syntax, different runtime behavior)</LI><LI>All business logic needs to be reimplemented in Node.js or Java — not ported, reimplemented</LI><LI>The deployment pipeline, CI/CD, monitoring, and operational model all change</LI><LI>The ABAP artifacts are retired, not converted</LI></UL><P>For a medium-complexity service: weeks to months. For a large one: a quarter or more. For a portfolio that grew because nobody pushed back: a program, not a project.</P><P><STRONG>The cost of the wrong default is asymmetric.</STRONG><SPAN>&nbsp;</SPAN>Not because RAP upgrades are expensive — they're not. But because choosing the wrong model and needing to switch is. CAP First reduces the risk of landing on that side.</P><P><EM>One variable that changes this equation: AI. Claude Code connected to SAP ADT via MCP can analyze RAP logic and generate CAP equivalents faster than before — it lowers the code generation cost. It does not replace the authorization model revalidation, the test suite rebuild, or the operational cutover, which is where most of the real effort lives. The fear factor drops. The effort doesn't disappear. That full conversation is a follow-up article.</EM></P><H2 id="toc-hId--1200222478">CAP Enforces the Clean Core Promise. RAP Requires Discipline to Maintain It.</H2><P>SAP's official definition of Clean Core: don't modify standard code, use the approved extensibility models. RAP is on that list — and a RAP extension using only ABAP Cloud Released APIs (C1) is genuinely Clean Core compliant by SAP's own definition. Let's be precise about this.</P><P>But Clean Core has a purpose beyond compliance:<SPAN>&nbsp;</SPAN><STRONG>the ability to upgrade your SAP core without touching your business logic.</STRONG><SPAN>&nbsp;</SPAN>Faster upgrades. Lower risk. Smaller impact windows.</P><P>Here is the real distinction between RAP and CAP on this point — and it is not about compliance. It is about enforcement.</P><P>Both models can satisfy the Clean Core compliance definition when implemented with discipline. The difference is what happens when discipline breaks down. In RAP, a team under delivery pressure can find paths to unreleased ABAP internals — friend class patterns, dynamic calls, legacy function module invocations. The architectural boundary exists as a rule, not a wall. In CAP, you are physically outside the ABAP stack. You cannot access unreleased internals because there is no path to them from a Node.js or Java runtime. The boundary is enforced by the architecture itself.</P><P><STRONG>RAP requires discipline to stay Clean Core. CAP makes it structurally difficult to leave.</STRONG></P><P>This is not a criticism of ABAP developers. It is a recognition that architectural guardrails matter most under pressure — and pressure is the normal operating condition of enterprise SAP programs.</P><P>A valid counterpoint here: ABAP Cloud on BTP — the ABAP Environment as a side-by-side deployment — does break the S/4HANA lifecycle dependency. The toolchain enforces Released APIs syntactically via the language version check, so the "discipline" argument partially weakens. But there is a distinction worth making: ABAP Cloud on BTP decouples you from S/4HANA's runtime lifecycle. CAP decouples you from the ABAP architecture itself. If your constraints are about upgrade survival, both work. If your constraints are about talent pool, integration patterns, and long-term architectural independence away from the ABAP stack — only one of them gets you there.</P><P>A CAP service communicates with S/4HANA through stable, versioned OData or API endpoints. It does not know what version of S/4HANA is running underneath. It has no ABAP lifecycle. It can be upgraded, redeployed, and scaled completely independently. RAP is the right direction. CAP is the destination. The difference is whether Clean Core survives contact with reality.</P><H2 id="toc-hId--1396735983">This Is Not Anti-ABAP. It's a Call to Deploy ABAP Knowledge Differently.</H2><P>Deep ABAP expertise — understanding transactional models, business process flows, SAP data structures built over decades — is irreplaceable. The scarce resource in SAP programs is not syntax knowledge. It is SAP business process knowledge. A Node.js developer learns the CAP framework in weeks. They do not learn how SAP processes an intercompany stock transfer in weeks. ABAP architects already have the hard part.</P><P>The problem is not ABAP knowledge. The problem is when that knowledge becomes a reason to avoid CAP rather than a foundation for it.</P><P>In 2026, with Claude Code connected to SAP ADT via MCP and Joule for Developers embedded in the SAP ecosystem, an experienced ABAP developer can build a production-grade CAP service with AI assistance in a fraction of the time it would have taken two years ago. The language gap has never been smaller to bridge. The "we don't have CAP developers" argument has never been weaker.</P><P>And the most mature SAP architectures don't choose one model. They use both deliberately:<SPAN>&nbsp;</SPAN><STRONG>RAP as the API layer of S/4HANA — the clean, stable interface that exposes core business objects. CAP as the orchestration and composition layer above it — multi-source, scalable, independently deployable.</STRONG><SPAN>&nbsp;</SPAN>That is not a compromise. That is the target state. ABAP architects who understand both are positioned to design it.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="diagram-cap-rap-decision-matrix (2).png" style="width: 744px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/392171i69B130241A8759B1/image-dimensions/744x618?v=v2" width="744" height="618" role="button" title="diagram-cap-rap-decision-matrix (2).png" alt="diagram-cap-rap-decision-matrix (2).png" /></span></P><H2 id="toc-hId--1593249488">A Challenge for Your Next Architecture Review</H2><P>Before your team decides on RAP — or commits to a Z-to-RAP migration program — answer four questions publicly in the room:</P><OL><LI><STRONG>For Z-objects:</STRONG><SPAN>&nbsp;</SPAN>Does each Z exist because SAP lacked functionality that it now has natively? If yes: retire it, don't migrate it.</LI><LI><STRONG>For new extensions:</STRONG><SPAN>&nbsp;</SPAN>Does this require transactional consistency with a standard SAP Business Object, bounded to S/4HANA data only? If yes: RAP is correct.</LI><LI><STRONG>For new extensions:</STRONG><SPAN>&nbsp;</SPAN>Does this require data from outside S/4HANA, independent scalability, or multi-tenant deployment? If yes: CAP is correct.</LI><LI><STRONG>Before committing to either:</STRONG><SPAN>&nbsp;</SPAN>Is RAP on the table because of architectural justification — or because of who is in the room?</LI></OL><P>Question 4 is the one that changes the outcome. And it takes organizational courage to ask it out loud.</P><P>The CAP vs. RAP debate is not a technology debate. It's an organizational clarity debate — and more often than not, an organizational courage debate. The teams that get this right are the ones willing to name what's actually driving the decision, separate ABAP expertise from ABAP default, and deploy both models where they genuinely belong.</P><H2 id="toc-hId--1621579302">References</H2><UL><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/rap-vs-cap-key-differences-between-the-two-programming-models/ba-p/13572799" target="_blank">RAP vs CAP - Key Differences between the Two Programming Models — SAP Community</A></LI><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-members/cap-vs-rap-navigating-the-choice-of-sap-programming-models-for-your/ba-p/13579573" target="_blank">CAP vs. RAP: Navigating the Choice of SAP Programming Models — SAP Community</A></LI><LI><A href="https://help.sap.com/docs/abap-cloud/abap-rap/abap-restful-application-programming-model" target="_blank" rel="noopener noreferrer">ABAP RESTful Application Programming Model — SAP Help Portal</A></LI><LI><A href="https://pages.community.sap.com/topics/cloud-application-programming" target="_blank" rel="noopener noreferrer">SAP Cloud Application Programming Model — SAP Community</A></LI><LI><A href="https://qmacro.org/blog/posts/2024/11/07/five-reasons-to-use-cap/" target="_blank" rel="noopener nofollow noreferrer">Five Reasons to Use CAP — qmacro.org</A></LI><LI><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/clean-core-extensibility-balancing-standardization-and-differentiation/ba-p/14260149" target="_blank">Clean Core Extensibility: Balancing Standardization and Differentiation — SAP Community</A></LI></UL><P><EM>Walter Aguero Machado —SAP Enterprise Architect | SAP BTP SME at Globant | SAP Certified Solution Architect &amp; BTP Administrator | ASUG Speaker</EM></P> 2026-04-09T02:05:37.595000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/xml-annotations-the-essentials-before-adapting-documentation-samples-to/ba-p/14369241 XML Annotations: The Essentials Before Adapting Documentation Samples to Your Project 2026-04-09T23:10:50.984000+02:00 MioYasutake https://community.sap.com/t5/user/viewprofilepage/user-id/789 <H2 id="toc-hId-1793424892">The Problem</H2><P>When building Fiori Elements apps, you sometimes need to add XML annotations on the UI side. The usual approach is to search for examples in the UI5 Demo Kit. For instance, you might find something like this:</P><P><A href="https://ui5.sap.com/#/topic/3cdebeebb04b4205908140242c9d6817" target="_self" rel="noopener noreferrer">Disabling the Search filter</A></P><pre class="lia-code-sample language-markup"><code>&lt;Annotations Target="SAP__self.Container/SalesOrder"&gt; &lt;Annotation Term="SAP__capabilities.SearchRestrictions"&gt; &lt;Record&gt; &lt;PropertyValue Property="Searchable" Bool="false" /&gt; &lt;/Record&gt; &lt;/Annotation&gt; &lt;/Annotations&gt;</code></pre><P><SPAN>However, simply copying this into your </SPAN><CODE>annotation.xml</CODE><SPAN> won't work. To figure out what needs to be changed, you need to understand the basic structure of annotations.</SPAN></P><P>&nbsp;</P><H2 id="toc-hId-1596911387">What You Need to Know</H2><H3 id="toc-hId-1529480601">1. Annotation Targets</H3><P>The target of an annotation varies depending on the annotation you want to apply. Here are the most common ones:</P><TABLE border="1" width="100%"><TBODY><TR><TD width="22%" height="30px"><STRONG>Target</STRONG></TD><TD width="44%" height="30px"><STRONG>Syntax for Target attribute</STRONG></TD><TD width="34%" height="30px"><STRONG>Example in UI5 Documentation</STRONG></TD></TR><TR><TD>EntityType</TD><TD>{Namespace}.{EntityType}</TD><TD>Common for UI annotations (<CODE>UI.FieldGroup</CODE>, <CODE>UI.HeaderInfo</CODE>, etc.). Note: documentation examples often show only the inner <CODE>&lt;Annotation&gt;</CODE> element, omitting the outer <CODE>&lt;Annotations Target="..."&gt;</CODE> wrapper.</TD></TR><TR><TD>EntitySet</TD><TD>{Namespace}.{EntityContainer}/{EntitySet}</TD><TD>TravelService.EntityContainer/Travel [1]</TD></TR><TR><TD>Property</TD><TD>{Namespace}.{EntityType}/{Property}</TD><TD>Z0020_CDS.Z0020Type/CountryID [3]</TD></TR><TR><TD>NavigationProperty</TD><TD>{Namespace}.{EntityContainer}/{EntitySet}/{NavigationProperty}</TD><TD>myService.EntityContainer/SalesOrderManage/_Item [2]</TD></TR><TR><TD>Action (Bound)</TD><TD>{Namespace}.{Action}({BindingParameterType})</TD><TD>com.c_salesordermanage_sd.CreateWithSalesOrderType(com.c_salesordermanage_sd.SalesOrderManage) [2]</TD></TR><TR><TD>Action (Unbound)</TD><TD>{Namespace}.{Action}</TD><TD>SAP__self.CreateWithSalesOrderType [2]</TD></TR><TR><TD>Action Parameter</TD><TD>{Namespace}.{Action}/{Parameter}</TD><TD>SalesOrder.CreateWithSalesOrderType/SalesOrganization [2]</TD></TR><TR><TD>EntityContainer</TD><TD>{Namespace}.{EntityContainer}</TD><TD>namespace.EntityContainer [4]</TD></TR></TBODY></TABLE><P>[1] <A href="https://ui5.sap.com/#/topic/cd319e9f36474844a4960c6f0b9adee5" target="_blank" rel="noopener noreferrer">Disabling the Editing Status Filter</A><BR />[2] <A href="https://ui5.sap.com/#/topic/cbf16c599f2d4b8796e3702f7d4aae6c" target="_blank" rel="noopener noreferrer">Actions</A><BR />[3] <A href="https://ui5.sap.com/#/topic/4de40b31324e4876a8421f6f642e0140" target="_blank" rel="noopener noreferrer">In / Out Mappings in the ValueList Annotation</A><BR />[4] <A href="https://ui5.sap.com/#/topic/609c39a7498541559dbef503c1ffd194" target="_self" rel="noopener noreferrer">Adapting the Filter Bar</A></P><P>The Namespace, EntityType, EntitySet, and other names used in the <CODE>Target</CODE> attribute can all be found in your service's <CODE>metadata.xml</CODE> document. When developing a Fiori Elements app, this file is typically located in your project folder.</P><P><STRONG>CAP Example</STRONG></P><P>In a CAP service, open <CODE>metadata.xml</CODE> and look for the following:</P><pre class="lia-code-sample language-markup"><code>&lt;Schema Namespace="QuoteService" ...&gt; &lt;!-- Namespace --&gt; &lt;EntityContainer Name="EntityContainer"&gt; &lt;!-- EntityContainer name --&gt; &lt;EntitySet Name="Quotations" EntityType="QuoteService.Quotations"&gt; &lt;!-- EntitySet name --&gt; &lt;NavigationPropertyBinding Path="attachments" Target="Quotations_attachments"/&gt; &lt;/EntitySet&gt; &lt;/EntityContainer&gt; &lt;EntityType Name="Quotations"&gt; &lt;!-- EntityType name --&gt; &lt;Property Name="Description" Type="Edm.String"/&gt; &lt;!-- Property name --&gt; &lt;NavigationProperty Name="attachments" .../&gt; &lt;!-- NavigationProperty name --&gt; &lt;/EntityType&gt; &lt;/Schema&gt;</code></pre><P>From this, you can construct your target values:</P><TABLE border="1"><TBODY><TR><TD><STRONG>Target</STRONG></TD><TD><STRONG>Value</STRONG></TD></TR><TR><TD>EntityType</TD><TD>QuoteService.Quotations</TD></TR><TR><TD>EntitySet</TD><TD>QuoteService.EntityContainer/Quotations</TD></TR><TR><TD>Property</TD><TD>QuoteService.Quotations/Description</TD></TR><TR><TD>NavigationProperty</TD><TD>QuoteService.Quotations/attachments</TD></TR><TR><TD>EntityContainer</TD><TD>QuoteService.EntityContainer</TD></TR></TBODY></TABLE><P><STRONG>RAP Example</STRONG></P><P>In a RAP service, the structure is similar, but notice the <CODE>Alias</CODE> attribute on the <CODE>Schema</CODE> element:</P><pre class="lia-code-sample language-markup"><code>&lt;Schema Namespace="com.sap.gateway.srvd.dmo.flight_r.v0001" Alias="SAP__self"&gt; &lt;!-- Namespace &amp; Alias --&gt; &lt;EntityContainer Name="Container"&gt; &lt;!-- EntityContainer name --&gt; &lt;EntitySet Name="Connection" EntityType="...ConnectionType"&gt; &lt;NavigationPropertyBinding Path="_Flight" Target="Flight"/&gt; &lt;/EntitySet&gt; &lt;/EntityContainer&gt; &lt;EntityType Name="ConnectionType"&gt; &lt;!-- EntityType name --&gt; &lt;Property Name="AirlineID" Type="Edm.String"/&gt; &lt;!-- Property name --&gt; &lt;NavigationProperty Name="_Flight" Type="Collection(...FlightType)"/&gt; &lt;!-- NavigationProperty name --&gt; &lt;/EntityType&gt; &lt;/Schema&gt;</code></pre><P>In RAP, the <CODE>Target</CODE> attribute uses the <STRONG>Alias</STRONG> (<CODE>SAP__self</CODE>) instead of the full Namespace. The Alias is always <CODE>SAP__self</CODE> in RAP services, so you can use it as-is.</P><TABLE border="1"><TBODY><TR><TD><STRONG>Target</STRONG></TD><TD><STRONG>Value</STRONG></TD></TR><TR><TD>EntityType</TD><TD>SAP__self.FlightType</TD></TR><TR><TD>EntitySet</TD><TD>SAP__self.Container/Flight</TD></TR><TR><TD>Property</TD><TD>SAP__self.FlightType/AirlineID</TD></TR><TR><TD>NavigationProperty</TD><TD>SAP__self.ConnectionType/_Flight</TD></TR><TR><TD>EntityContainer</TD><TD>SAP__self.Container</TD></TR></TBODY></TABLE><P><STRONG>Note:</STRONG> The UI5 documentation does not indicate whether an annotation example is based on CAP or RAP. This can be a source of confusion — for example, <CODE>SAP__self</CODE> and <CODE>Container</CODE> in the <CODE>Target</CODE> attribute are RAP conventions, while CAP uses the full Namespace and <CODE>EntityContainer</CODE>. Always check your own service's <CODE>metadata.xml</CODE> to determine the correct values.</P><P>&nbsp;</P><H3 id="toc-hId-1332967096">2. Annotations require a Reference definition</H3><P>This is why simply copying a snippet from the documentation into your <CODE>annotation.xml</CODE> won't work.</P><P>For example, to use the annotation shown at the beginning of this post, you need to add the following Reference:</P><pre class="lia-code-sample language-markup"><code>&lt;edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"&gt; &lt;edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml"&gt; &lt;edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="Capabilities"/&gt; &lt;/edmx:Reference&gt; ... &lt;/edmx:Edmx&gt;</code></pre><P>OData vocabularies used in Fiori Elements are defined in two locations:</P><UL><LI><STRONG>sap.github.io</STRONG>: <A href="https://sap.github.io/odata-vocabularies/" target="_blank" rel="noopener noreferrer nofollow">https://sap.github.io/odata-vocabularies/</A></LI><LI><STRONG>oasis-tcs.github.io</STRONG>: <A href="https://oasis-tcs.github.io/odata-vocabularies/" target="_blank" rel="noopener noreferrer nofollow">https://oasis-tcs.github.io/odata-vocabularies/</A></LI></UL><P>Here are the vocabularies you will encounter most often when working with Fiori Elements.</P><P><STRONG>SAP vocabularies</STRONG></P><TABLE border="1"><TBODY><TR><TD><STRONG>Alias</STRONG></TD><TD><STRONG>Namespace</STRONG></TD><TD><STRONG>URI</STRONG></TD></TR><TR><TD>UI</TD><TD>com.sap.vocabularies.UI.v1</TD><TD><A href="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/odata-vocabularies/vocabularies/UI.xml</A></TD></TR><TR><TD>Common</TD><TD>com.sap.vocabularies.Common.v1</TD><TD><A href="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/odata-vocabularies/vocabularies/Common.xml</A></TD></TR><TR><TD>HTML5</TD><TD>com.sap.vocabularies.HTML5.v1</TD><TD><A href="https://sap.github.io/odata-vocabularies/vocabularies/HTML5.xml" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/odata-vocabularies/vocabularies/HTML5.xml</A></TD></TR></TBODY></TABLE><P><STRONG>OASIS vocabularies</STRONG></P><TABLE border="1"><TBODY><TR><TD><STRONG>Alias</STRONG></TD><TD><STRONG>Namespace</STRONG></TD><TD><STRONG>URI</STRONG></TD></TR><TR><TD>Core</TD><TD>Org.OData.Core.V1</TD><TD><A href="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml" target="_blank" rel="noopener nofollow noreferrer">https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml</A></TD></TR><TR><TD>Capabilities</TD><TD>Org.OData.Capabilities.V1</TD><TD><A href="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml" target="_blank" rel="noopener nofollow noreferrer">https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml</A></TD></TR></TBODY></TABLE><P><STRONG>Tip:</STRONG> If you want to dig deeper, the vocabulary specification files are available at the URIs listed above. Each annotation term definition includes an <CODE>AppliesTo</CODE> attribute, which tells you exactly which target types the annotation can be applied to.</P><P>For example, in <CODE>Org.OData.Capabilities.V1.xml</CODE>:</P><pre class="lia-code-sample language-markup"><code>&lt;Term Name="SearchRestrictions" Type="Capabilities.SearchRestrictionsType" Nullable="false" AppliesTo="EntitySet Collection"&gt; &lt;Annotation Term="Core.AppliesViaContainer"/&gt; &lt;Annotation Term="Core.Description" String="Restrictions on search expressions"/&gt; &lt;/Term&gt;</code></pre><P>This tells you that <CODE>SearchRestrictions</CODE> can be applied to an <CODE>EntitySet</CODE> target.</P><P>&nbsp;</P><H2 id="toc-hId-1007370872">Step-by-step: Applying a Documentation Sample to Your annotation.xml</H2><P>Let's walk through how to actually apply the following annotation — the same one from the beginning of this post — to your own project.</P><pre class="lia-code-sample language-markup"><code>&lt;Annotations Target="SAP__self.Container/SalesOrder"&gt; &lt;Annotation Term="SAP__capabilities.SearchRestrictions"&gt; &lt;Record&gt; &lt;PropertyValue Property="Searchable" Bool="false" /&gt; &lt;/Record&gt; &lt;/Annotation&gt; &lt;/Annotations&gt;</code></pre><P>&nbsp;</P><H3 id="toc-hId-939940086">Step 1: Understand the annotation target</H3><P><CODE>Target="SAP__self.Container/SalesOrder"</CODE></P><P>This follows the format <CODE>{Namespace or Alias}.{EntityContainer}/{EntitySet}</CODE>.</P><P>You can look up the exact values in your service's <CODE>metadata.xml</CODE>:</P><UL><LI>Replace <CODE>SAP__self</CODE> with your service's Namespace (CAP) or keep it as-is (RAP)</LI><LI>Replace <CODE>Container</CODE> with your EntityContainer name</LI><LI>Replace <CODE>SalesOrder</CODE> with your EntitySet name</LI></UL><P>Result:</P><pre class="lia-code-sample language-markup"><code>&lt;Annotations Target="MyService.EntityContainer/MyEntity"&gt; &lt;Annotation Term="SAP__capabilities.SearchRestrictions"&gt; &lt;Record&gt; &lt;PropertyValue Property="Searchable" Bool="false" /&gt; &lt;/Record&gt; &lt;/Annotation&gt; &lt;/Annotations&gt;</code></pre><P>&nbsp;</P><H3 id="toc-hId-743426581">Step 2: Add the Reference</H3><P>The term <CODE>SAP__capabilities</CODE> tells you this is a Capabilities vocabulary annotation. Add the following Reference inside the root <CODE>&lt;edmx:Edmx&gt;</CODE> element of your <CODE>annotation.xml</CODE>, before the <CODE>&lt;Annotations&gt;</CODE> block:</P><pre class="lia-code-sample language-markup"><code>&lt;edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"&gt; &lt;!-- Add the Reference here --&gt; &lt;edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml"&gt; &lt;edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="Capabilities"/&gt; &lt;/edmx:Reference&gt; &lt;!-- Your annotations go below --&gt; &lt;Annotations Target="..."&gt; ... &lt;/Annotations&gt; &lt;/edmx:Edmx&gt;</code></pre><P>Also, replace the <CODE>SAP__capabilities</CODE> prefix in the annotation term with the <CODE>Alias</CODE> you defined in the Reference. In this case, <CODE>SAP__capabilities</CODE> becomes <CODE>Capabilities</CODE>. The final annotation will look like this:</P><pre class="lia-code-sample language-markup"><code>&lt;edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"&gt; &lt;edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml"&gt; &lt;edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="Capabilities"/&gt; &lt;/edmx:Reference&gt; &lt;Annotations Target="MyService.EntityContainer/MyEntity"&gt; &lt;Annotation Term="Capabilities.SearchRestrictions"&gt; &lt;Record&gt; &lt;PropertyValue Property="Searchable" Bool="false" /&gt; &lt;/Record&gt; &lt;/Annotation&gt; &lt;/Annotations&gt; &lt;/edmx:Edmx&gt;</code></pre><P>&nbsp;</P><H2 id="toc-hId-417830357">Conclusion</H2><P>XML annotations can feel intimidating at first, but there are really just two things you need to get right before applying any annotation from the documentation to your project:</P><OL><LI><STRONG>The Target</STRONG> — identify your Namespace, EntityContainer, EntitySet, and other names from your service's <CODE>metadata.xml</CODE> file</LI><LI><STRONG>The Reference</STRONG> — add the correct <CODE>edmx:Reference</CODE> for the vocabulary you are using</LI></OL><P>Once you have these two in place, any annotation snippet you find in the UI5 documentation can be adapted to your project. Happy annotating!</P> 2026-04-09T23:10:50.984000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/custom-sorting-for-virtual-elements-in-rap/ba-p/14367132 Custom Sorting for Virtual Elements in RAP 2026-04-10T00:06:47.289000+02:00 sharathtm https://community.sap.com/t5/user/viewprofilepage/user-id/1874965 <H2 id="toc-hId-1793364319"><STRONG>Introduction</STRONG></H2><P>In the ABAP RESTful Application Programming Model (RAP), virtual elements are essential for displaying computed data that doesn't exist in the database—such as deriving a month or year from a timestamp. However, because these elements are calculated at runtime, they lack standard OData capabilities like server-side sorting and filtering. In this article, we will explore how to overcome these limitations by implementing a <STRONG>SADL exit</STRONG>. We’ll walk through how to enable full sorting and calculation capabilities for virtual elements, ensuring a seamless experience for the end user.</P><H2 id="toc-hId-1596850814">What are Virtual Elements in RAP?</H2><P>In the <STRONG>ABAP RESTful Application Programming Model (RAP)</STRONG>, a virtual element is a field defined in a CDS (Core Data Services) projection view that does not have a corresponding column in the underlying database table.</P><H2 id="toc-hId-1400337309">Real-Time Business Scenario: Sales Period Reporting</H2><P>In many SAP implementations, the business requires reports segmented by <STRONG>Month</STRONG> and <STRONG>Year</STRONG> (e.g., for Monthly Sales Performance). While the underlying database table stores a precise (Created On) date, it does not store the Month or Year as separate columns.</P><H3 id="toc-hId-1332906523">The Problem</H3><P>If we create a Fiori Elements app and want to show "Sales Month" and "Sales Year" as separate columns:</P><OL><LI><P><STRONG>Simple Virtual Elements</STRONG> can calculate these strings (e.g., "January", "2024").</P></LI><LI><P><STRONG>The Failure:</STRONG> When a user tries to sort the "Sales Month" column, the UI will throw an error or do nothing because the OData service doesn't know how to sort a field that doesn't exist in the database.</P></LI></OL><H3 id="toc-hId-1136393018">The Solution</H3><P>By using a <STRONG>SADL Exit</STRONG>, we "map" the virtual Month/Year fields back to the physical Sales date field. This allows the database to perform the sort on the actual date, while the user sees the sorted Month or Year on their screen.</P><H2 id="toc-hId-810796794">Implementation Steps</H2><H4 id="toc-hId-872448727">Step 1: Create a database table</H4><P>Define the physical storage. This is where the raw data lives before any calculations happen.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Sales table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zsales_table { key client : abap.clnt not null; key sales_uuid : sysuuid_x16 not null; sales_id : abap.char(10); sales_date : abap.dats; @Semantics.amount.currencyCode : 'zsales_table.currency' total_amount : abap.curr(15,2); currency : abap.cuky; last_changed_at : timestampl; }</code></pre><H3 id="toc-hId-546852503">Step 2: Create a interface view on top of database table.</H3><P>This layer projects the database fields into a clean, business-oriented structure.</P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface view for sales table' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define root view entity zi_sales_table as select from zsales_table { key sales_uuid as SalesUuid, sales_id as SalesId, sales_date as SalesDate, @Semantics.amount. currencyCode: 'Currency' total_amount as TotalAmount, currency as Currency, last_changed_at as LastChangedAt }</code></pre><H3 id="toc-hId-350338998">&nbsp;Step 3: Create a Consumption view</H3><P>This is where the Virtual Elements are defined. We use annotations to link the UI to the specific ABAP logic for calculation and sorting.</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection view for sales table' @Metadata.ignorePropagatedAnnotations: true define root view entity ZP_SALESPROJ provider contract transactional_query as projection on zi_sales_table { .facet: [{ type: #IDENTIFICATION_REFERENCE, position: 10, label: 'Salestable', targetQualifier: 'GENERAL', purpose: #STANDARD }] .lineItem: [{ position: 10, label: 'Sales Uuid' }] key SalesUuid, .lineItem: [{ position: 20, label: 'Sales Id' }] .identification: [{ position: 20, label: 'Sales Id' }] SalesId, .lineItem: [{ position: 30, label: 'Sales order date' }] .identification: [{ position: 30, label: 'Sales order date', qualifier: 'GENERAL' }] SalesDate, .lineItem: [{ position: 40, label: 'Sales order year' }] .identification: [{ position: 40, label: 'Sales order year', qualifier: 'GENERAL' }] @ObjectModel.virtualElement: true @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_SALES_VE_HANDLER' @ObjectModel.sort.transformedBy: 'ABAP:ZCL_SALES_VE_HANDLER' virtual SalesYear : abap.char(4), .lineItem: [{ position: 50, label: 'Sales order month' }] .identification: [{ position: 50, label: 'Sales order month' , qualifier: 'GENERAL' }] @ObjectModel.virtualElement: true @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_SALES_VE_HANDLER' @ObjectModel.sort.transformedBy: 'ABAP:ZCL_SALES_VE_HANDLER' virtual SalesMonth : abap.char(2), .lineItem: [{ position: 60, label: 'Total amount' }] .identification: [{ position: 60, label: 'Total amount' }] @Semantics.amount.currencyCode: 'Currency' TotalAmount, Currency }</code></pre><H4 id="toc-hId-282908212">Step 5:&nbsp;The Handler Class Logic</H4><P>This class implements two critical interfaces: <U><STRONG>if_sadl_exit_calc_element_read</STRONG></U>&nbsp;for the calculation and <U><STRONG>if_sadl_exit_sort_transform&nbsp;</STRONG></U>&nbsp;to enable sorting.</P><pre class="lia-code-sample language-abap"><code>CLASS zcl_sales_ve_handler DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_sadl_exit_calc_element_read. INTERFACES if_sadl_exit_sort_transform. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_sales_ve_handler IMPLEMENTATION. METHOD if_sadl_exit_calc_element_read~get_calculation_info. IF line_exists( it_requested_calc_elements[ table_line = 'SALESYEAR' ] ) OR line_exists( it_requested_calc_elements[ table_line = 'SALESMONTH' ] ). APPEND 'SALESDATE' TO et_requested_orig_elements. ENDIF. ENDMETHOD. METHOD if_sadl_exit_calc_element_read~calculate. DATA lt_data TYPE TABLE OF zp_salesproj WITH DEFAULT KEY. lt_data = CORRESPONDING #( it_original_data ). LOOP AT lt_data ASSIGNING FIELD-SYMBOL(&lt;fs_row&gt;). &lt;fs_row&gt;-SALESYEAR = &lt;fs_row&gt;-salesdate(4). &lt;fs_row&gt;-SALESMONTH = &lt;fs_row&gt;-salesdate+4(2). ENDLOOP. ct_calculated_data = CORRESPONDING #( lt_data ). ENDMETHOD. METHOD if_sadl_exit_sort_transform~map_element. CASE iv_element. WHEN 'SALESYEAR' OR 'SALESMONTH'. " Map the UI sort request to the database field 'SALESDATE' APPEND VALUE #( name = 'SALESDATE' ) TO et_sort_elements. ENDCASE. ENDMETHOD. ENDCLASS.</code></pre><H3 id="toc-hId--117919381">Result:</H3><P><BR />With the handler class implemented, the <STRONG>Sales order month and Sales order year</STRONG> ( Virtual Elements) now behaves like a standard database fields.<BR /><BR /></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sharathtm_0-1775541139507.png" style="width: 0px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394078i308E9AD112A3DFDF/image-size/small?v=v2&amp;px=200" width="0" height="0" role="button" title="sharathtm_0-1775541139507.png" alt="sharathtm_0-1775541139507.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sharathtm_1-1775541190320.png" style="width: 624px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394080i8A95E257B9CF5997/image-dimensions/624x256?v=v2" width="624" height="256" role="button" title="sharathtm_1-1775541190320.png" alt="sharathtm_1-1775541190320.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sharathtm_0-1775714874405.png" style="width: 599px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/395268i96C104E304776015/image-dimensions/599x226?v=v2" width="599" height="226" role="button" title="sharathtm_0-1775714874405.png" alt="sharathtm_0-1775714874405.png" /></span></P><P>&nbsp;</P> 2026-04-10T00:06:47.289000+02:00 https://community.sap.com/t5/abap-blog-posts/implementing-determination-using-association-in-sap-rap/ba-p/14367270 Implementing Determination Using Association in SAP RAP 2026-04-13T07:22:47.901000+02:00 nikhilgl7 https://community.sap.com/t5/user/viewprofilepage/user-id/1874838 <P><STRONG>Introduction</STRONG></P><P>In SAP RESTful Application Programming Model (RAP), determinations are a powerful mechanism that automatically derive or calculate field values based on changes to other fields within a business object. They play a central role in enforcing business logic, keeping data consistent, and reducing manual input errors.</P><P>While determinations are commonly implemented using direct field references within the same entity, a more advanced and often necessary pattern involves leveraging <STRONG>associations</STRONG> — navigational links between related business object nodes or external CDS entities. This approach becomes essential when the value you need to derive depends not on the current entity alone, but on data that lives in an associated entity.<BR /><BR />This blog walks through the concept and practical implementation of determinations that use associations in SAP RAP.</P><P>I have implemented a scenario where the non-editable Total Amount field in the header entity is automatically determined based on the Price and Quantity fields maintained in the associated item entity.</P><P>Below is the Header entity, which includes the <STRONG>Total Amount</STRONG> field</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2026-04-07 162842.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394372i6E2FA7539A67FFC7/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-07 162842.png" alt="Screenshot 2026-04-07 162842.png" /></span></P><P>&nbsp;</P><P>Below is the item entity, which includes <STRONG>quantity </STRONG>and <STRONG>price </STRONG>&nbsp;field<BR /><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="item entity.jpg" style="width: 771px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394373i7B0C1747B57794C2/image-size/large?v=v2&amp;px=999" role="button" title="item entity.jpg" alt="item entity.jpg" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>In the behavior definition, the <STRONG>Total Amount</STRONG> field is configured as non-editable using feature control, ensuring that it cannot be explicitly modified by the user.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2026-04-07 170240.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394335iB42FF0D1FDBEBA12/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-07 170240.png" alt="Screenshot 2026-04-07 170240.png" /></span></P><P>&nbsp;</P><P>Determination has been defined in the behavior definition of the Item entity.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2026-04-07 170613.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394339i5D29245D01AFE043/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-07 170613.png" alt="Screenshot 2026-04-07 170613.png" /></span></P><P>The determination logic is implemented in the local handler class of the Item entity.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2026-04-07 170927.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394345i51E6D19712D96224/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-07 170927.png" alt="Screenshot 2026-04-07 170927.png" /></span></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code> METHOD calculate_total_price. DATA lt_header_update TYPE TABLE FOR UPDATE zi_nk_header. *Step 1 : Read changed items READ ENTITIES OF zi_nk_header IN LOCAL MODE ENTITY zi_nk_item FIELDS ( soid quantity price currency ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_items) FAILED DATA(lf). DATA lt_header_keys TYPE TABLE FOR UPDATE zi_nk_header. LOOP AT lt_items ASSIGNING FIELD-SYMBOL(&lt;item&gt;). *Step 2 :Navigate to parent headers using back-association READ ENTITIES OF zi_nk_header IN LOCAL MODE ENTITY zi_nk_item BY \_header FIELDS ( soid totalamount currency ) WITH VALUE #( ( %tky = &lt;item&gt;-%tky ) ) RESULT DATA(lt_header_result) FAILED DATA(lf_h). LOOP AT lt_header_result ASSIGNING FIELD-SYMBOL(&lt;hdr&gt;). *Step 3 : Read all items under those headers READ ENTITIES OF zi_nk_header IN LOCAL MODE ENTITY zi_nk_header BY \_salesitem FIELDS ( quantity price currency ) WITH VALUE #( ( %tky = &lt;hdr&gt;-%tky ) ) RESULT DATA(lt_all_items) FAILED DATA(lf2). *Step 4 : Calculate the total amount *Here i have used decfloat34 to avoid the loss of decimal values and incorrect totals DATA(lv_total) = REDUCE decfloat34( INIT sum = CONV decfloat34( '0' ) FOR &lt;it&gt; IN lt_all_items NEXT sum = sum + ( &lt;it&gt;-quantity * &lt;it&gt;-price ) ). APPEND VALUE #( %tky = &lt;hdr&gt;-%tky totalamount = lv_total currency = &lt;item&gt;-currency %control = VALUE #( totalamount = if_abap_behv=&gt;mk-on ) ) TO lt_header_update. ENDLOOP. ENDLOOP. *Step 5 : Update the header entity MODIFY ENTITIES OF zi_nk_header IN LOCAL MODE ENTITY zi_nk_header UPDATE FIELDS ( totalamount currency ) WITH lt_header_update FAILED DATA(lf3) REPORTED DATA(lr). ENDMETHOD.</code></pre><P>&nbsp;</P><P>The Total Amount and Currency fields are rendered as read-only, preventing any direct user modification.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2026-04-07 173102.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394355i1F8269B4DD0AFF44/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-07 173102.png" alt="Screenshot 2026-04-07 173102.png" /></span></P><P>In the Item entity, users provide input for <STRONG>Quantity </STRONG>and <STRONG>Price</STRONG>, based on which the <STRONG>Total Amount</STRONG> is determined and automatically updated in the associated Header entity.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2026-04-07 173421.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394367iFB3FDB689250BF84/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-07 173421.png" alt="Screenshot 2026-04-07 173421.png" /></span></P><P>&nbsp;</P><P><STRONG>Output:-</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2026-04-07 173629.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/394368iCF970BCF480B0249/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-07 173629.png" alt="Screenshot 2026-04-07 173629.png" /></span></P><P>&nbsp;</P><P><STRONG>Conclusion:-</STRONG></P><P>This approach ensures data consistency, reduces redundancy, and keeps the business logic modular and maintainable. It is especially useful in scenarios like calculating header totals based on item data, where changes in one entity automatically reflect in another.</P> 2026-04-13T07:22:47.901000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/rap-validation-extension-in-sap-step-by-step-guide/ba-p/14370511 RAP Validation Extension in SAP – Step-by-Step Guide 2026-04-15T11:23:15.567000+02:00 nvtnext https://community.sap.com/t5/user/viewprofilepage/user-id/1867561 <H2 id="toc-hId-1794083084"><STRONG><SPAN>Introduction</SPAN></STRONG><SPAN>&nbsp;</SPAN></H2><P><SPAN>In the SAP RAP (RESTful ABAP Programming Model), extensibility is a powerful concept that allows developers to enhance standard Business Objects (BOs) without modifying the original code.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>One such enhancement is the&nbsp;</SPAN><STRONG><SPAN>Validation Extension</SPAN></STRONG><SPAN>, which enables you to add custom validation logic during the save process.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>This is especially useful when:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI>You&nbsp;do not own the standard Business Object&nbsp;</LI></UL><UL><LI>You want to&nbsp;enforce additional business rules&nbsp;</LI></UL><UL><LI>You must&nbsp;avoid modifying the base implementation&nbsp;</LI></UL><H2 id="toc-hId-1597569579"><STRONG><SPAN>Scenario</SPAN></STRONG><SPAN>&nbsp;</SPAN></H2><P><SPAN>Assume you are working with a standard RAP Business Object, and you want to validate a field:</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>The&nbsp;“Name”&nbsp;</SPAN>field should not contain numeric values&nbsp;</P><P>Even though you don’t own the base BO, you can still implement this validation using&nbsp;RAP Validation Extension.&nbsp;</P><H2 id="toc-hId-1401056074"><STRONG><SPAN>RAP Extensibility Overview</SPAN></STRONG><SPAN>&nbsp;</SPAN></H2><P><SPAN>RAP extensibility involves&nbsp;</SPAN>two main roles:&nbsp;</P><OL><LI><STRONG><SPAN>Extensibility Enabler</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI></OL><UL><LI><SPAN>The original developer of the BO</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Enables the BO for extension</SPAN><SPAN>&nbsp;</SPAN></LI></UL><OL><LI><STRONG><SPAN>Extensibility Provider</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI></OL><UL><LI><SPAN>The developer who creates extensions</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Adds custom logic like:</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Fields (Data Model Extension)</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Validations / Determinations (Behavior Extension)</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Nodes</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Note:</SPAN></STRONG><SPAN>&nbsp;Extensibility must be enabled first; otherwise, extensions are not possible.</SPAN><SPAN>&nbsp;</SPAN></P><H3 id="toc-hId-1333625288"><STRONG><SPAN>Step 1: Extensibility Enabler (Base BO Preparation)</SPAN></STRONG><SPAN>&nbsp;</SPAN></H3><OL><LI><STRONG>Enable Extensibility in Base Behavior Definition</STRONG>&nbsp;</LI></OL><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_nvt_i_act1 unique; strict ( 2 ); extensible { with additional save; with determinations on modify; with determinations on save; with validations on save; }</code></pre><UL><LI><STRONG><SPAN class="">Enable Node-Level Extensibility</SPAN></STRONG></LI></UL><pre class="lia-code-sample language-abap"><code>define behavior for znvt_i_act1 //alias &lt;alias_name&gt; persistent table znvt_dt_act1 lock master authorization master ( instance ) extensible { create; update; delete;</code></pre><UL><LI><STRONG><SPAN class="">Maintain Field Mapping (Extensible)</SPAN><SPAN class="">&nbsp;</SPAN></STRONG></LI></UL><pre class="lia-code-sample language-abap"><code> mapping for znvt_dt_act1{ Name = name; Orderid = orderid; Quantity = quantity; Status = zzstatus; } }</code></pre><P><SPAN class="">Note:<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">If new fields have the same name in CDS and DB table, no mapping extension is<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">required</SPAN><SPAN class="">.</SPAN></P><UL><LI><STRONG><SPAN class="">Enable Projection Behavior Extensibility&nbsp;</SPAN></STRONG></LI></UL><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); extensible; define behavior for znvt_c_act1 //alias &lt;alias_name&gt; extensible { use create; use update; use delete; }</code></pre><H3 id="toc-hId-1137111783"><SPAN class="">Step 2: Extensibility Provider (Create Validation Extension)</SPAN><SPAN class="">&nbsp;</SPAN></H3><P><STRONG><SPAN class="">1. Create Behavior Definition Extension</SPAN></STRONG></P><UL><LI><SPAN>Go to base BDEF:&nbsp;</SPAN><SPAN>znvt_i_act1</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Right-click →&nbsp;</SPAN><STRONG><SPAN>New Behavior Extension</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Provide:</SPAN><SPAN>&nbsp;</SPAN><UL><LI><SPAN>Package</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Extension Name (e.g.,&nbsp;</SPAN><SPAN>znvt_ext1</SPAN><SPAN>)</SPAN></LI><LI><SPAN>Description</SPAN></LI></UL></LI></UL><P><STRONG>2. System-Generated Template, for extending the Base Behavior</STRONG></P><pre class="lia-code-sample language-abap"><code>extension implementation in class zbp_nvt_ext1 unique; extend behavior for znvt_i_act1 { }</code></pre><P><STRONG>3. Add the Validation Logic</STRONG></P><P><SPAN>Inside the extension block, define a validation with:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI>Trigger:&nbsp;on save&nbsp;</LI></UL><UL><LI>Field:&nbsp;Name&nbsp;</LI><LI>Operation:&nbsp;create&nbsp;</LI></UL><pre class="lia-code-sample language-abap"><code>extension implementation in class zbp_nvt_ext1 unique; extend behavior for znvt_i_act1 { validation val_method on save { field Name; create; } }</code></pre><H3 id="toc-hId-940598278"><SPAN class="">Step 3: Implement Validation Logic</SPAN><SPAN class="">&nbsp;</SPAN></H3><P><STRONG><SPAN class="">1. Create&nbsp;Behavior&nbsp;Implementation Class&nbsp;</SPAN></STRONG></P><UL><LI><SPAN>Use&nbsp;</SPAN><SPAN>quick fix (Ctrl + 1)&nbsp;</SPAN><SPAN>on the implementation class</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>This will generate class:&nbsp;</SPAN><SPAN>zbp_nvt_ext1</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Implement your validation logic inside this class</SPAN><SPAN>&nbsp;</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>CLASS lhc_znvt_i_act1 DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS val_method FOR VALIDATE ON SAVE IMPORTING keys FOR znvt_i_act1~val_method. ENDCLASS.</code></pre><P><STRONG>Implementation:&nbsp;</STRONG></P><pre class="lia-code-sample language-abap"><code>CLASS lhc_znvt_i_act1 IMPLEMENTATION. METHOD val_method. read entity in local mode znvt_i_act1 aLL FIELDS WITH corrESPONDING #( keys ) reSULT data(lt_data). data(ls_data) = lt_data[ 1 ]. if ls_data-Name ca '1234567890'. appEND vaLUE #( %tky = ls_data-%tky %element-Name = if_abap_behv=&gt;mk-on %msg = new_message_with_text( severity = if_abap_behv_message=&gt;severity-error text = 'the Name field can not contain numbers' ) ) to reported-znvt_i_act1. endIF. ENDMETHOD. ENDCLASS.</code></pre><P><STRONG><SPAN>Optional: Projection Behavior Extension</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>You can also extend the projection layer</SPAN><SPAN>&nbsp;if needed.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>However, for&nbsp;</SPAN>validation scenarios<SPAN>, this step is usually&nbsp;</SPAN>not&nbsp;required.&nbsp;</P><H3 id="toc-hId-744084773"><STRONG><SPAN>Output</SPAN></STRONG><SPAN>&nbsp;</SPAN></H3><UL><LI><SPAN>When a user enters numeric values in the&nbsp;"</SPAN>Name" field&nbsp;</LI></UL><UL><LI><SPAN>And tries to save the data</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>The system throws an&nbsp;</SPAN>error message<SPAN>:&nbsp;</SPAN><I><SPAN>"The Name field cannot contain numbers"</SPAN></I><SPAN>&nbsp;.</SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-04-09 155345.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/395457i2D25949D5A9F6E1F/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-09 155345.png" alt="Screenshot 2026-04-09 155345.png" /></span></SPAN></P><H3 id="toc-hId-547571268"><STRONG><SPAN>Conclusion</SPAN></STRONG><SPAN>&nbsp;</SPAN></H3><P><SPAN>RAP Validation Extensions provide a&nbsp;</SPAN>clean and upgrade-safe way<SPAN>&nbsp;to implement custom validations without modifying the original Business Object.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>By properly using:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Extensibility Enabler</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Behavior Extensions</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Validation Methods</SPAN><SPAN>&nbsp;</SPAN></LI></UL> 2026-04-15T11:23:15.567000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/triggering-background-jobs-from-rap-actions-a-practical-guide/ba-p/14371274 Triggering Background Jobs from RAP Actions – A Practical Guide 2026-04-15T11:24:21.616000+02:00 Arshiya2 https://community.sap.com/t5/user/viewprofilepage/user-id/1912702 <H2 id="toc-hId-1794110181"><STRONG>Introduction:</STRONG></H2><P><BR />In the SAP ABAP RESTful Application Programming Model (RAP), actions are typically executed within a single transactional request. However, resource-intensive operations are not suitable for synchronous execution as they impact performance and user experience. To handle this, background job processing can be used to execute heavy logic asynchronously. This blog demonstrates how a RAP action can trigger a background job to keep the main transaction lightweight.</P><P><STRONG>What is a RAP Action?</STRONG><BR />An action in RAP is a non-standard operation used to change data or trigger specific business logic. By default, these are synchronous, meaning the UI waits for the logic to finish before the user can continue.</P><P><STRONG>What is Background Processing (BG)?</STRONG><BR />Background processing allows the system to run tasks in a separate session without user interaction. It is ideal for "fire-and-forget" scenarios or resource-intensive tasks that would otherwise cause a timeout in a web session.</P><P><STRONG>Scenario: Bulk Asynchronous Processing in RAP</STRONG><BR />A business user needs to process multiple records simultaneously in a SAP Fiori application—such as updating statuses for a large batch of background job entries. While a simple status change is quick, the underlying business logic (like complex data validation or external system integration) is resource-intensive and long-running.</P><P><STRONG>Problem Statement</STRONG><BR />Executing heavy synchronous logic directly within RAP actions causes poor user experience, performance degradation, and potential transaction timeouts.</P><P><STRONG>Solution</STRONG><BR />Trigger an asynchronous background job from the RAP action. The action marks the records as "In Progress," and the Saver Class calls a Function Module IN BACKGROUND TASK to schedule a background report. This offloads the processing to a separate session, allowing the main UI transaction to remain lightweight and responsive.</P><P>&nbsp;</P><H2 id="toc-hId-810796794" id="toc-hId-1597596676">Implementation Steps</H2><H4 id="toc-hId-872448727" id="toc-hId-1659248609">Step 1: Create a database table</H4><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'DATABASE TABLE FOR BACKGROUND JOB' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zaf_dt_backjob { key id : uuid not null; description : abap.char(50); status : abap.char(10); job_name : abap.char(32); job_count : abap.char(10); start_time : timestampl; end_time : timestampl; error_message : abap.char(255); created_by : abap.char(12); created_at : abp_creation_tstmpl; last_changed_at : abp_lastchange_tstmpl; } </code></pre><H3 id="toc-hId-546852503" id="toc-hId-1333652385">Step 2: Create a interface view on top of database table.</H3><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'BACKGROUND job interface view' @Metadata.ignorePropagatedAnnotations: true define root view entity zi_AF_backjob as select from zaf_dt_backjob { key id as Id, description as Description, status as Status, job_name as JobName, job_count as JobCount, start_time as StartTime, end_time as EndTime, error_message as ErrorMessage, created_by as CreatedBy, created_at as CreatedAt, last_changed_at as LastChangedAt, case status when 'DONE' then 3 when 'INPR' then 2 when 'NEW' then 1 when 'ERR' then 4 else 0 end as StatusCriticality } </code></pre><P>&nbsp;</P><H3 id="toc-hId-350338998" id="toc-hId-1137138880">&nbsp;Step 3: Create a Consumption view</H3><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Background job projection view' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true @UI.headerInfo: { typeName: 'Background Job', typeNamePlural: 'Background Jobs', title: { type: #STANDARD, value: 'Description' } } define root view entity zc_af_backjob provider contract transactional_query as projection on zi_AF_backjob { key Id, Description, Status, JobName, JobCount, StartTime, EndTime, ErrorMessage, CreatedAt, LastChangedAt, StatusCriticality }</code></pre><H3 id="toc-hId-350338998" id="toc-hId-940625375">Step 4: Metadata Extension file&nbsp;</H3><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity zc_af_backjob with { .facet: [ { id: 'General', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'General Information', position: 10 } ] .lineItem: [{ position: 10, label: 'UUID' }] .identification: [{ position: 10, label: 'UUID' }] .selectionField: [{ position: 10 }] Id; .lineItem: [{ position: 20, label: 'Description' }] .identification: [{ position: 20, label: 'Description' }] Description; .lineItem: [ { position: 30, label: 'Status' }, { type: #FOR_ACTION, dataAction: 'StartProcessing', label: 'Start Job' } ] .identification: [{ position: 30, label: 'Status' }] .selectionField: [{ position: 30 }] .dataPoint: { title: 'Status', criticality: 'StatusCriticality' } Status; .lineItem: [{ position: 40, label: 'Job Name' }] .identification: [{ position: 40, label: 'Job Name' }] .selectionField: [{ position: 40 }] JobName; .lineItem: [{ position: 50, label: 'Job Count' }] .identification: [{ position: 50, label: 'Job Count' }] JobCount; .lineItem: [{ position: 60, label: 'Start Time' }] .identification: [{ position: 60, label: 'Start Time' }] StartTime; .lineItem: [{ position: 70, label: 'End Time' }] .identification: [{ position: 70, label: 'End Time' }] EndTime; .lineItem: [{ position: 80, label: 'Error Message' }] .identification: [{ position: 80, label: 'Error Message' }] ErrorMessage; .hidden: true CreatedAt; .hidden: true LastChangedAt; .hidden: true StatusCriticality; }</code></pre><H3 id="toc-hId-350338998" id="toc-hId-744111870">Step 5:&nbsp; Create a Behavior Definition on top of interface View</H3><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_i_af_backjob unique; strict ( 2 ); with draft; define behavior for zi_AF_backjob lock master total etag LastChangedAt draft table zaf_draft_bg authorization master ( instance ) early numbering { create; update; delete; draft action activate optimized; draft action resume; draft action Edit; draft action Discard; draft determine action Prepare; action StartProcessing result [1] $self; determination SetInitialStatus on modify { create; } field ( readonly ) Id; field ( readonly ) Status, JobName, JobCount, StartTime, EndTime, ErrorMessage; field ( readonly ) CreatedAt, LastChangedAt, CreatedBy; field ( mandatory ) Description; mapping for zaf_dt_backjob{ CreatedAt = created_at; CreatedBy = created_by; Description = description; EndTime = end_time; ErrorMessage = error_message; Id = id; JobCount = job_count; JobName = job_name; LastChangedAt = last_changed_at; StartTime = start_time; Status = status; } }</code></pre><H3 id="toc-hId-350338998" id="toc-hId-547598365">Step 6: Create implementation class</H3><pre class="lia-code-sample language-abap"><code>CLASS lhc_zi_af_backjob DEFINITION INHERITING FROM cl_abap_behavior_handler. PUBLIC SECTION. TYPES: tt_backjob_db TYPE STANDARD TABLE OF zaf_dt_backjob WITH EMPTY KEY. " Buffer for CRUD and Job Scheduling CLASS-DATA: mt_buffer_upsert TYPE tt_backjob_db, mt_buffer_delete TYPE tt_backjob_db, gt_jobs TYPE STANDARD TABLE OF zaf_dt_backjob-id. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zi_af_backjob RESULT result. METHODS earlynumbering_create FOR NUMBERING IMPORTING entities FOR CREATE zi_af_backjob. METHODS create FOR MODIFY IMPORTING entities FOR CREATE zi_af_backjob. METHODS update FOR MODIFY IMPORTING entities FOR UPDATE zi_af_backjob. METHODS delete FOR MODIFY IMPORTING keys FOR DELETE zi_af_backjob. METHODS read FOR READ IMPORTING keys FOR READ zi_af_backjob RESULT result. METHODS lock FOR LOCK IMPORTING keys FOR LOCK zi_af_backjob. METHODS StartProcessing FOR MODIFY IMPORTING keys FOR ACTION zi_af_backjob~StartProcessing RESULT result. METHODS SetInitialStatus FOR DETERMINE ON MODIFY IMPORTING keys FOR zi_af_backjob~SetInitialStatus. ENDCLASS. CLASS lhc_zi_af_backjob IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD earlynumbering_create. LOOP AT entities INTO DATA(ls_entity). IF ls_entity-id IS NOT INITIAL. CONTINUE. ENDIF. DATA(lv_uuid) = cl_system_uuid=&gt;create_uuid_x16_static( ). APPEND VALUE #( %cid = ls_entity-%cid %is_draft = ls_entity-%is_draft id = lv_uuid ) TO mapped-zi_af_backjob. ENDLOOP. ENDMETHOD. METHOD create. LOOP AT entities INTO DATA(ls_entity). APPEND VALUE #( id = ls_entity-id description = ls_entity-Description status = 'NEW' created_by = sy-uname ) TO mt_buffer_upsert. ENDLOOP. ENDMETHOD. METHOD update. LOOP AT entities INTO DATA(ls_entity). " In unmanaged, we must fetch current state to apply partial updates SELECT SINGLE * FROM zaf_dt_backjob WHERE id = _entity-id INTO (ls_db). IF ls_entity-%control-Description = if_abap_behv=&gt;mk-on. ls_db-description = ls_entity-Description. ENDIF. IF ls_entity-%control-Status = if_abap_behv=&gt;mk-on. ls_db-status = ls_entity-Status. ENDIF. GET TIME STAMP FIELD ls_db-last_changed_at. APPEND ls_db TO mt_buffer_upsert. ENDLOOP. ENDMETHOD. METHOD delete. LOOP AT keys INTO DATA(ls_key). APPEND VALUE #( id = ls_key-id ) TO mt_buffer_delete. ENDLOOP. ENDMETHOD. METHOD read. IF keys IS INITIAL. RETURN. ENDIF. SELECT * FROM zaf_dt_backjob FOR ALL ENTRIES IN WHERE id = -id INTO TABLE (lt_data). result = CORRESPONDING #( lt_data MAPPING Id = id Description = description Status = status JobName = job_name JobCount = job_count StartTime = start_time EndTime = end_time ErrorMessage = error_message CreatedBy = created_by CreatedAt = created_at LastChangedAt = last_changed_at ). ENDMETHOD. METHOD lock. ENDMETHOD. METHOD StartProcessing. LOOP AT keys INTO DATA(ls_key). " 1. Mark status as 'INPR' immediately in the database UPDATE zaf_dt_backjob SET status = 'INPR' WHERE id = _key-id. " 2. Add to global job queue for the SAVE phase INSERT ls_key-id INTO TABLE gt_jobs. ENDLOOP. result = VALUE #( FOR key IN keys ( Id = key-id %param = CORRESPONDING #( key ) ) ). ENDMETHOD. METHOD SetInitialStatus. LOOP AT keys INTO DATA(ls_key). SELECT SINGLE * FROM zaf_dt_backjob WHERE id = _key-id INTO (ls_db). IF ls_db-status IS INITIAL. ls_db-status = 'NEW'. APPEND ls_db TO mt_buffer_upsert. ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. " --- Saver Class Implementation --- CLASS lsc_zi_af_backjob DEFINITION INHERITING FROM cl_abap_behavior_saver. PROTECTED SECTION. METHODS finalize REDEFINITION. METHODS check_before_save REDEFINITION. METHODS save REDEFINITION. METHODS cleanup REDEFINITION. METHODS cleanup_finalize REDEFINITION. ENDCLASS. CLASS lsc_zi_af_backjob IMPLEMENTATION. METHOD finalize. ENDMETHOD. METHOD check_before_save. ENDMETHOD. METHOD save. " 1. Standard DB Persistence IF lhc_zi_af_backjob=&gt;mt_buffer_delete IS NOT INITIAL. DELETE zaf_dt_backjob FROM TABLE @lhc_zi_af_backjob=&gt;mt_buffer_delete. ENDIF. IF lhc_zi_af_backjob=&gt;mt_buffer_upsert IS NOT INITIAL. MODIFY zaf_dt_backjob FROM TABLE @lhc_zi_af_backjob=&gt;mt_buffer_upsert. ENDIF. " 2. Trigger Async logic using the reference pattern LOOP AT lhc_zi_af_backjob=&gt;gt_jobs INTO DATA(lv_id). CALL FUNCTION 'ZAF_FM_BACKJOB' IN BACKGROUND TASK DESTINATION 'NONE' " Critical for separate LUW execution EXPORTING iv_id = lv_id. ENDLOOP. ENDMETHOD. METHOD cleanup. " Clear all buffers after the LUW is complete CLEAR: lhc_zi_af_backjob=&gt;mt_buffer_upsert, lhc_zi_af_backjob=&gt;mt_buffer_delete, lhc_zi_af_backjob=&gt;gt_jobs. ENDMETHOD. METHOD cleanup_finalize. ENDMETHOD. ENDCLASS.</code></pre><H3 id="toc-hId-350338998" id="toc-hId-351084860">Step 7: Create behavior definition on Consumption view</H3><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); use draft; define behavior for zc_af_backjob //alias &lt;alias_name&gt; { use create; use update; use delete; use action StartProcessing; use action activate; use action resume; use action Edit; use action Discard; use action Prepare; }</code></pre><H3 id="toc-hId-350338998" id="toc-hId-154571355">Step 8: Create service Definition on top of Consumption view</H3><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'service defination for background job' define service Zsevd_bg_job { expose zc_af_backjob; }</code></pre><H3 id="toc-hId-350338998" id="toc-hId--117173519">Step 9: Create service binding on top of service defination</H3><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arshiya2_0-1775890344719.png" style="width: 723px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/396279iB45CDAC61736EFBC/image-dimensions/723x270?v=v2" width="723" height="270" role="button" title="Arshiya2_0-1775890344719.png" alt="Arshiya2_0-1775890344719.png" /></span></P><H3 id="toc-hId-350338998" id="toc-hId--313687024">Step 10: Create Function Module</H3><pre class="lia-code-sample language-abap"><code>FUNCTION zaf_fm_backjob. *"---------------------------------------------------------------------- *" IMPORTING VALUE(IV_ID) TYPE ZAF_DT_BACKJOB-ID *"---------------------------------------------------------------------- DATA: lv_jobname TYPE tbtcjob-jobname, lv_jobcount TYPE tbtcjob-jobcount, lv_timestamp TYPE timestampl. " 1. Dynamic Job Name for better monitoring in SM37 lv_jobname = |ZAF_BG_{ iv_id+0(8) }|. CALL FUNCTION 'JOB_OPEN' EXPORTING jobname = lv_jobname IMPORTING jobcount = lv_jobcount. IF sy-subrc = 0. GET TIME STAMP FIELD lv_timestamp. " 2. Update DB with system metadata immediately UPDATE zaf_dt_backjob SET job_name = _jobname, job_count = _jobcount, status = 'INPR', start_time = _timestamp WHERE id = _id. " 3. Push data to UI so it's visible while report processes COMMIT WORK. ENDIF. " 4. Schedule the report SUBMIT zaf_bg_job_report WITH p_id = iv_id VIA JOB lv_jobname NUMBER lv_jobcount AND RETURN. " 5. Release job CALL FUNCTION 'JOB_CLOSE' EXPORTING jobname = lv_jobname jobcount = lv_jobcount strtimmed = 'X'. ENDFUNCTION.</code></pre><H3 id="toc-hId-350338998" id="toc-hId--510200529">Step 11: Create a Report Program</H3><pre class="lia-code-sample language-abap"><code>REPORT zaf_bg_job_report. PARAMETERS: p_id TYPE zaf_dt_backjob-id. DATA: lv_timestamp TYPE timestampl. START-OF-SELECTION. IF p_id IS INITIAL. RETURN. ENDIF. WAIT UP TO 5 SECONDS. GET TIME STAMP FIELD lv_timestamp. " Update final status to match the reference 'DONE' UPDATE zaf_dt_backjob SET status = 'DONE', end_time = _timestamp, error_message = '' WHERE id = @p_id. COMMIT WORK.</code></pre><P><STRONG>Result :</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arshiya2_1-1775891590814.png" style="width: 756px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/396284iFDBDB624975EBC0E/image-dimensions/756x238?v=v2" width="756" height="238" role="button" title="Arshiya2_1-1775891590814.png" alt="Arshiya2_1-1775891590814.png" /></span></P><P>Select<STRONG> Multiple Records</STRONG> then click Action Button <STRONG>Start Job</STRONG>.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arshiya2_3-1775891777189.png" style="width: 759px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/396287iC7D568C508521124/image-dimensions/759x205?v=v2" width="759" height="205" role="button" title="Arshiya2_3-1775891777189.png" alt="Arshiya2_3-1775891777189.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arshiya2_4-1775891833126.png" style="width: 763px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/396288i28F7193F5FE702AE/image-dimensions/763x246?v=v2" width="763" height="246" role="button" title="Arshiya2_4-1775891833126.png" alt="Arshiya2_4-1775891833126.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId--413311027"><STRONG>Conclusion :</STRONG></H2><P><FONT size="4">This approach demonstrates how the SAP ABAP RESTful Application Programming Model (RAP) can be extended beyond standard transactional processing to meet demanding real-world requirements. By leveraging the Saver Class to trigger asynchronous background jobs, you ensure that resource-intensive operations—such as bulk status updates in SD or MM—are executed efficiently without affecting the performance of the main transaction.</FONT></P><H3 id="toc-hId--903227539">&nbsp;</H3><P>&nbsp;</P><P>&nbsp;</P> 2026-04-15T11:24:21.616000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/from-idea-to-app-in-minutes-generating-sap-fiori-elements-applications-with/ba-p/14373342 From Idea to App in Minutes: Generating SAP Fiori Elements Applications with the Project Accelerator 2026-04-15T12:00:49.174000+02:00 berin_ https://community.sap.com/t5/user/viewprofilepage/user-id/2148756 <P class="lia-align-justify" style="text-align : justify;"><SPAN>The SAP Fiori tools Project Accelerator is an AI-assisted capability that transforms business requirements provided as text, image, or a combination of both into a SAP Fiori elements application. The generated application can be backed by either the SAP Cloud Application Programming Model (CAP) or ABAP RESTful Application Programming Model (RAP).</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>The Project Accelerator automates the generation of data models, services, UI scaffolding, and mock data, enabling you to quickly turn ideas from brainstorming or whiteboarding sessions into tangible applications that can be immediately previewed and iterated upon.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H1 id="toc-hId-1665087910"><SPAN>Where it fits in the SAP Fiori Tools ecosystem </SPAN></H1><P class="lia-align-justify" style="text-align : justify;"><SPAN>The Project Accelerator is built directly into SAP Fiori tools and can be used within SAP BAS. </SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>It complements existing SAP Fiori tools capabilities such as project generation, application preview, and annotation editing. By generating initial project artifacts directly from business requirements, the Project Accelerator helps you quickly establish a solid project structure.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H1 id="toc-hId-1468574405"><SPAN>What the Project Accelerator Generates</SPAN></H1><P class="lia-align-justify" style="text-align : justify;"><SPAN>Depending on the selected programming model, the Project Accelerator generates the following artifacts:</SPAN></P><H3 id="toc-hId-1530226338"><SPAN>CAP:</SPAN></H3><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><SPAN>For CAP-based applications, the Project Accelerator generates:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>An SAP Fiori elements application</SPAN></LI><LI><SPAN>Data models and services</SPAN></LI><LI><SPAN>Local mock data</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H3 id="toc-hId-1333712833"><SPAN>RAP :</SPAN></H3><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><SPAN>For RAP-based applications, the Project Accelerator generates:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>An SAP Fiori elements application</SPAN></LI><LI><SPAN>A RAP back-end artifact</SPAN></LI><LI><SPAN>An OData V4 service endpoint</SPAN></LI><LI><SPAN>Local mock data</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><STRONG><SPAN>Important note</SPAN></STRONG><SPAN>: Business logic is not automatically generated in either CAP or RAP projects. Developers must implement the application logic manually after the initial scaffolding is created.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H1 id="toc-hId-879033890"><SPAN>Step-by-step guide for the RAP Accelerator Through Business Application Studio (BAS)</SPAN></H1><P class="lia-align-justify" style="text-align : justify;">In this guide, we will generate an SAP Fiori elements app for managing sports games using the RAP Accelerator with image input. This app will allow users to view game schedules, team information, and game details. Later, we will modify the application to target soccer.</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><A href="https://community.sap.com/source-Ids-list" target="1_4s7i9x5e" rel="nofollow noopener noreferrer">&nbsp;</A></P><H2 id="toc-hId-811603104">&nbsp;</H2><H2 id="toc-hId-615089599"><SPAN>1) Prepare your input</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>Before starting the generation process, gather your business requirements, which are the inputs you prove to the Project Accelerator. </SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>The Input can be in the following formats:</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H3 id="toc-hId-547658813"><SPAN>Text</SPAN></H3><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><SPAN>You can write a clear description of your application, including:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>The purpose of the app</SPAN></LI><LI><SPAN>Entities and their relationships</SPAN></LI><LI><SPAN>Fields and properties</SPAN></LI><LI><SPAN>Labels and restrictions</SPAN></LI><LI><SPAN>Desired UI layout</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;"><STRONG><SPAN>Tip:</SPAN></STRONG><SPAN> Field values, logic, and restrictions are usually easier to describe using text.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>&nbsp;</SPAN></P><H3 id="toc-hId-351145308"><SPAN>Images</SPAN></H3><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><SPAN>You can upload images to describe the desired structure or layout, such as: </SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>A data model diagram</SPAN></LI><LI><SPAN>A UI mockup</SPAN></LI><LI><SPAN>A List Report layout showing columns</SPAN></LI><LI><SPAN>Object Page sections or tables</SPAN></LI><LI><SPAN>Screenshots of an existing application</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;"><STRONG><SPAN>Tip:</SPAN></STRONG><SPAN> Layouts and visual structures are easier to describe using images.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><SPAN>I'll use the following image to build a sports game management scenario. Users will be able to see which teams are playing against each other, the league and age group, and where and when the game takes place. The app will also display team details, including the trainer and players.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 1. Database model for sports game management scenario" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397620iD1C8DA31764B4FCF/image-size/medium?v=v2&amp;px=400" role="button" title="test 1.jpeg" alt="Figure 1. Database model for sports game management scenario" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 1. Database model for sports game management scenario</span></span></P><P class="lia-align-justify" data-unlink="true" style="text-align : justify;"><SPAN>When I first generated the application without specifying a sport type, the underlying LLM inferred the context and generated soccer-related mock data. To see how changes to the input such as specifying the sport type affects the output, I regenerated the application with several modifications to the original image. The results, shown in the <A href="#h_294247627661776175250148" target="_self" rel="nofollow noopener noreferrer">Appendix</A>&nbsp;, demonstrate how the AI adapts the generated data based on more specific input.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H3 id="toc-hId-154631803"><SPAN>Using Multimedia Input </SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><SPAN>It is important to keep in mind that the accelerator accepts a single file as input. If you want to upload both text and images, or multiple images, you should first combine them into a single document, either as a word document file (.docx) or as a markdown (.md) file and later upload the file to the accelerator as the business requirement. </SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>If you are using a .md file, you should also upload the images to your workspace folder.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>&nbsp;</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-176289936">2) <SPAN>Set up your environment:</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>To access the Project Accelerator, you need to be in Dev Space with the necessary extensions installed. </SPAN></P><H3 id="toc-hId--313626576"><SPAN>Prerequisites</SPAN></H3><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>You need to have access to SAP Build Code (<A href="https://help.sap.com/docs/build_code/d0d8f5bfc3d640478854e6f4e7c7584a/504854f457cc4fbf9f79136dbc773618.html" target="_blank" rel="noopener noreferrer">What is SAP Build Code | SAP Help Portal</A>)&nbsp; or signed in for (<A href="https://developers.sap.com/mission.sap-build-code-test-drive.html?sap-outbound-id=4E44C2A19D38B160BF5539329FA7ECC83942C1AD" target="_blank" rel="noopener noreferrer">SAP Build Code Test Drive | SAP Tutorials</A>)</SPAN></LI><LI><SPAN>You need to use SAP Business Application Studio as your integrated development environment</SPAN></LI></OL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H3 id="toc-hId--510140081"><SPAN>Required Extensions</SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><SPAN>Based on your programming model, ensure your Dev Space includes the appropriate extensions:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>(If you are creating a new Dev Space, you can select an application type that includes the extensions you need: <A href="#h_261880477161776121599309" target="_self" rel="nofollow noopener noreferrer">creating a new Dev Space</A>.)</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H4 id="toc-hId--1000056593"><STRONG>For CAP:</STRONG></H4><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><SPAN>Dev Space must include:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>CAP Tools</SPAN></LI><LI><SPAN>SAP Fiori Tools</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H4 id="toc-hId--1196570098">For RAP</H4><P class="lia-align-justify" style="text-align : justify;"><SPAN>Dev must include:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>SAP Fiori Tools </SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;"><SPAN>&nbsp;</SPAN></P><H4 id="toc-hId--1393083603">RAP Backend Requirements:</H4><P class="lia-align-justify" style="text-align : justify;"><SPAN>For RAP projects, ensure the following prerequisites are met:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>RAP backend should be version 2602 or higher</SPAN></LI><LI><SPAN>You should be connected to the backend system by <A href="#h_806563897501776121612019" target="_blank" rel="noopener nofollow noreferrer">configuring the destination</A> in SAP Business Application Studio</SPAN></LI><LI><SPAN>S_DEVELOP authorization</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H3 id="toc-hId--1296194101"><SPAN>Creating a Dev Space</SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><SPAN>Now, moving on with the creation of a Dev Space:</SPAN></P><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>&nbsp;</SPAN><SPAN>Open SAP Build</SPAN><OL><LI><SPAN>Click the icon with small squares on the top-right corner</SPAN></LI><LI><SPAN>Select Dev Space Manager</SPAN><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 2. How to access the Dev Space Manager" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397628i1FB409A769BC0C43/image-size/medium?v=v2&amp;px=400" role="button" title="image 2.jpg" alt="Figure 2. How to access the Dev Space Manager" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 2. How to access the Dev Space Manager</span></span></SPAN></LI></OL></LI><LI><SPAN>Create a New Dev Space</SPAN><UL><LI><SPAN>Enter a name for your Dev Space</SPAN></LI><LI><SPAN>Select the application type&nbsp;</SPAN><UL><LI>Each application type includes a predefined set of extensions.</LI><LI>Select the application type based on the extensions it provides and whether they are required for your programming model or not.</LI><LI>For this example, I selected the Full-Stack ABAP Application.<P>&nbsp;</P></LI></UL></LI><LI><SPAN>Click on “Create Dev S<FONT size="3">pace”&nbsp;</FONT><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 3. How to create a Dev Space" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397629iB311C43DA0D44423/image-size/medium?v=v2&amp;px=400" role="button" title="image 3.jpg" alt="Figure 3. How to create a Dev Space" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 3. How to create a Dev Space</span></span></SPAN></LI></UL></LI><LI><SPAN>After creating your dev space, you will be redirected to the Dev Space Manager, where you can see the newly created dev space and access any other dev spaces you may have created.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 4. Dev Space with status &quot;starting&quot;" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397631iF2A1B1D73345330B/image-size/medium?v=v2&amp;px=400" role="button" title="image 4.jpg" alt="Figure 4. Dev Space with status &quot;starting&quot;" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 4. Dev Space with status "starting"</span></span></SPAN></LI><LI>Wait until the status changes to "Running"</LI><LI>Click on the Dev Space name to start developing</LI></OL><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Image 5. Dev Space with status &quot;running&quot;" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397632i8F00B34750660A58/image-size/medium?v=v2&amp;px=400" role="button" title="image 5.jpg" alt="Image 5. Dev Space with status &quot;running&quot;" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Image 5. Dev Space with status "running"</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId--1199304599">3)&nbsp; <SPAN>Launching the Project Accelerator</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>Once you are in your Dev Space, you can launch the Project Accelerator in multiple ways. One of them being:</SPAN></P><H3 id="toc-hId--1689221111"><SPAN>Using the Template Wizard</SPAN></H3><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Choose “New Project from Template”<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 6. Step 1 of launching the Project Accelerator" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397635i80555E8FA7B8EC32/image-size/medium?v=v2&amp;px=400" role="button" title="image 6.jpg" alt="Figure 6. Step 1 of launching the Project Accelerator" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 6. Step 1 of launching the Project Accelerator</span></span></SPAN></LI><LI><SPAN>Select “SAP Fiori Generator" and click "Start"<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 7 . Step 2 of launching the Project Accelerator" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397637iE78CF9EF0753F1AC/image-size/medium?v=v2&amp;px=400" role="button" title="image 7.jpg" alt="Figure 7 . Step 2 of launching the Project Accelerator" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 7 . Step 2 of launching the Project Accelerator</span></span></SPAN></LI><LI><SPAN><SPAN>On the next screen, click on the “Project Accelerator” to begin&nbsp;</SPAN></SPAN></LI></OL><P class="lia-align-justify" style="text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 8. Step 3 of launching the Project Accelerator" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397638iC7DC2CEE70E847AF/image-size/medium?v=v2&amp;px=400" role="button" title="image 8.jpg" alt="Figure 8. Step 3 of launching the Project Accelerator" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 8. Step 3 of launching the Project Accelerator</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId--1592331609"><SPAN>4) Choose your programming model accelerator</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>When using the Project Accelerator, you can either select CAP Accelerator or RAP Accelerator tab depending on your programming model.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H3 id="toc-hId--1914064430"><SPAN>RAP Configuration</SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><SPAN>For RAP projects, you must:</SPAN></P><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Specify the Destination:</SPAN><OL><LI><SPAN>Select a destination system from the dropdown</SPAN></LI><LI><SPAN>Verify your user authentication (user and password for the backend system)</SPAN><UL><LI>OAuth2 authentication can also be configured via the SAP BTP Cockpit<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 9. Accessing the RAP Accelerator" style="width: 244px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397646i2FC8F51BABC44464/image-dimensions/244x293?v=v2" width="244" height="293" role="button" title="image 9.jpg" alt="Figure 9. Accessing the RAP Accelerator" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 9. Accessing the RAP Accelerator</span></span></LI></UL></LI></OL></LI><LI><SPAN>Configure Backend Connection&nbsp;</SPAN></LI></OL><P class="lia-indent-padding-left-60px lia-align-justify" style="padding-left : 60px; text-align : justify;">&nbsp;After successful authentication, additional fields will appear:</P><P class="lia-indent-padding-left-60px lia-align-justify" style="padding-left : 60px; text-align : justify;"><SPAN>Here, you should specify your SAPUI5 repository, the package for RAP backend artifact creation, and the transport request if it is required.</SPAN></P><P class="lia-indent-padding-left-60px lia-align-justify" style="padding-left : 60px; text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 10. Configuring the backend connection" style="width: 372px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397648i59C937A533635F17/image-size/medium?v=v2&amp;px=400" role="button" title="image 10.jpg" alt="Figure 10. Configuring the backend connection" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 10. Configuring the backend connection</span></span></SPAN></P><P>&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><SPAN>&nbsp;</SPAN><SPAN>For more information regarding these fields, please refer to: <A href="https://help.sap.com/docs/bas/developing-sap-fiori-app-in-sap-business-application-studio/generating-application-using-image" target="_blank" rel="noopener noreferrer">Generating an Application Using an Image | SAP Help Portal</A></SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId--1817174928">5)&nbsp;&nbsp; <SPAN>Enter Your Business Requirements</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>After choosing your accelerator and specifying your destination, you can upload the input prepared in Step 1 into the business requirements field.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>You can either:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Upload files to the workspace and then select them using "Choose File" under the business requirements input area</SPAN></LI><LI><SPAN>Drag and drop documents directly from the file explorer </SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;"><SPAN>Hold the Shift key while dragging</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 11. Entering the Business Requirements" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397663i9B21B59099713412/image-size/medium?v=v2&amp;px=400" role="button" title="image 11.jpg" alt="Figure 11. Entering the Business Requirements" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 11. Entering the Business Requirements</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><STRONG><SPAN>Note:</SPAN></STRONG><SPAN> The clearer and more structured your input is, the better the generated result will be.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId--2013688433">6)&nbsp;&nbsp; <SPAN>Generating the Project</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><STRONG><SPAN>Note:</SPAN></STRONG><SPAN> By default, the application is generated in the currently open folder. If you want the project to be created in a different location, you can specify a <EM>Project Folder Path</EM> before starting the generation.</SPAN></P><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Click Generate below the input field:</SPAN><UL><LI><SPAN>The tool will interpret your requirements and generate the project artifacts.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 12. Generating the project" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397659i578A7EA0BEB0DC6D/image-size/medium?v=v2&amp;px=400" role="button" title="image 12.jpg" alt="Figure 12. Generating the project" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 12. Generating the project</span></span></SPAN></LI><LI>The interface switches to a generation view displaying progress.&nbsp;Generation may take some time depending on the complexity of your requirements<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 13. Generation view" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397665i8760645A8A2F485B/image-size/medium?v=v2&amp;px=400" role="button" title="image 13.jpg" alt="Figure 13. Generation view" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 13. Generation view</span></span><P>&nbsp;</P></LI><LI><SPAN>Once the generation is complete, the <EM>Application Information</EM> page will appear.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 14. Application Information page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397669iB08E107AE8360E63/image-size/medium?v=v2&amp;px=400" role="button" title="image 14.png" alt="Figure 14. Application Information page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 14. Application Information page</span></span></SPAN><P>&nbsp;</P></LI><LI>If the project is not in a workspace folder, BAS may display a dialog asking whether to add it after the generation is complete.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 15. Adding the project into a workspace" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397670i24AEBB3F075518BC/image-size/medium?v=v2&amp;px=400" role="button" title="image 15.jpg" alt="Figure 15. Adding the project into a workspace" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 15. Adding the project into a workspace</span></span><P><SPAN>In this case, you can choose either:</SPAN></P><UL><LI><SPAN>“Open Folder”, which creates a new folder for the project, or</SPAN></LI><LI><SPAN>“Add Project to Workplace”, adding the project to the existing folder. Selecting one of these options adds the project to the workspace and lands you to the Application Information page.</SPAN></LI></UL></LI></UL></LI></OL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-2084765358">7)&nbsp;&nbsp; <SPAN>Preview of the Application</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>To preview the application:</SPAN></P><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN><SPAN>Select “Preview Application”<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 16. Previewing the application" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397677i0CD9629E214E9589/image-size/medium?v=v2&amp;px=400" role="button" title="image 16.jpg" alt="Figure 16. Previewing the application" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 16. Previewing the application</span></span></SPAN></SPAN></LI><LI><SPAN>Choose “Start Fiori Run” from the context menu</SPAN></LI><LI><SPAN><SPAN>This launches the generated UI.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 17. Preview of the application" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397678i72404A2FF88F5DF5/image-size/medium?v=v2&amp;px=400" role="button" title="image 17.jpg" alt="Figure 17. Preview of the application" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 17. Preview of the application</span></span></SPAN></SPAN><P>&nbsp;</P></LI></OL><P class="lia-align-justify" style="text-align : justify;"><STRONG><SPAN>Note:</SPAN></STRONG><SPAN> At this stage, the preview only shows the application structure, without any data.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H3 id="toc-hId-1594848846"><SPAN>Edge Case: Running the Preview from the ABAP Backend</SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><SPAN>Make sure that you run the preview directly from SAP Business Application Studio (BAS) when previewing the generated application.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>If you launch the preview from the ABAP backend instead of BAS, the UI will not render correctly. This is expected behavior as the Project Accelerator stores UI annotations in the local “annotation.xm” file of your Fiori project, not in backend Metadata Extensions. Therefore, the backend cannot render the UI properly without these local annotations.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>If the preview is executed from the ABAP backend, the result will appear similar to the following:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 18. Preview of the application when rendered from the ABAP backend example 1" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397684iBF7F0703CEB0B419/image-size/medium?v=v2&amp;px=400" role="button" title="image 19.jpg" alt="Figure 18. Preview of the application when rendered from the ABAP backend example 1" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 18. Preview of the application when rendered from the ABAP backend example 1</span></span></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 19. Preview of the application when rendered from the ABAP backend example 2" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397685i08256877FD99A0B6/image-size/medium?v=v2&amp;px=400" role="button" title="image 20.jpg" alt="Figure 19. Preview of the application when rendered from the ABAP backend example 2" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 19. Preview of the application when rendered from the ABAP backend example 2</span></span></P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId-1691738348"><SPAN>8 ) Generate and Preview with Local Mock Data</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>You can also preview the application with locally generated mock data:</SPAN></P><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Open the terminal</SPAN></LI><LI><SPAN>Enter Ctrl + C to terminate the current process</SPAN></LI><LI><SPAN>Select “Preview Application”</SPAN></LI><LI>Choose “Start-Mock Fiori Run” from the context menu<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 20. Previewing the application with locally generated mock data" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398134iA243AEBEC26D435F/image-size/medium?v=v2&amp;px=400" role="button" title="image 18.jpg" alt="Figure 20. Previewing the application with locally generated mock data" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 20. Previewing the application with locally generated mock data</span></span></LI><LI>After restarting, you may need to log in again for system authorization.</LI><LI>The UI will now display generated mock data, allowing you to:</LI></OL><P class="lia-align-justify" style="text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 21. Preview of the application with mock data" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397687i402ABCF96DC1F75C/image-size/medium?v=v2&amp;px=400" role="button" title="image 21.jpg" alt="Figure 21. Preview of the application with mock data" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 21. Preview of the application with mock data</span></span></SPAN><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 22. Preview of the application with mock data - game object page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397688iFB6754334ED44229/image-size/medium?v=v2&amp;px=400" role="button" title="image 22.jpg" alt="Figure 22. Preview of the application with mock data - game object page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 22. Preview of the application with mock data - game object page</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 23. Preview of the application with mock data - team object page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397689i6C21D806BE9F772D/image-size/medium?v=v2&amp;px=400" role="button" title="image 23.png" alt="Figure 23. Preview of the application with mock data - team object page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 23. Preview of the application with mock data - team object page</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-1495224843">9)&nbsp;&nbsp; <SPAN>Review and Refine</SPAN></H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>After generation, review the application and refine the configuration as needed. You can adjust field names, labels, page sections, and UI layout using the Page Map: </SPAN></P><OL class="lia-align-justify" style="text-align : justify;"><LI>Click on "Open Page Map"<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 24. Navigating to Page Map" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397706iAE09C1F9E94903E1/image-size/medium?v=v2&amp;px=400" role="button" title="image 24.jpg" alt="Figure 24. Navigating to Page Map" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 24. Navigating to Page Map</span></span><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Select the pencil icon next to the location you want to modify&nbsp;</SPAN></LI><LI><SPAN>Make your changes.</SPAN></LI><LI><SPAN>The preview will automatically refresh and update</SPAN></LI></OL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H4 id="toc-hId-711905324"><STRONG>Example 1: Editing Column Names</STRONG></H4><P class="lia-align-justify" style="text-align : justify;"><SPAN>Since the business requirement was for general sports, and soccer uses fields (not courts), let's change the column name from "court" to "field."</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><STRONG><SPAN>Scenario:</SPAN></STRONG><SPAN> Change the field name “Home Court” to “Home Field”</SPAN></LI></UL><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Click on <EM>Open Page Map</EM></SPAN></LI><LI><SPAN><SPAN><SPAN><SPAN>Click on the pencil icon of “Teams Object Page”<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 25. Navigating to &quot;Teams&quot; Object Page" style="width: 294px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397730i0554E4727F93B850/image-size/medium?v=v2&amp;px=400" role="button" title="image 24.jpg" alt="Figure 25. Navigating to &quot;Teams&quot; Object Page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 25. Navigating to "Teams" Object Page</span></span></SPAN></SPAN></SPAN></SPAN></LI><LI>Expand the “Table” section</LI><LI><SPAN>Expand the “Columns” subsection</SPAN></LI><LI><SPAN>Click on the “Home Court” field</SPAN></LI><LI><SPAN><SPAN><SPAN><SPAN>On the right-hand side (RHS), find the “Label” field and change it to “Home Field”<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 26. How to change the field name" style="width: 377px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397732i41317CC8D6348DCD/image-size/medium?v=v2&amp;px=400" role="button" title="image 26.jpg" alt="Figure 26. How to change the field name" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 26. How to change the field name</span></span></SPAN></SPAN></SPAN></SPAN></LI><LI>The preview will automatically refresh and update</LI></OL><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 27. Changing the field name in &quot;Team&quot; Object Page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397736i74203E934EAF2D08/image-size/medium?v=v2&amp;px=400" role="button" title="image 28.jpg" alt="Figure 27. Changing the field name in &quot;Team&quot; Object Page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 27. Changing the field name in "Team" Object Page</span></span></P><P>&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><STRONG>Note:</STRONG> Renaming the column in the Teams Object Page only changes how it appears on the Teams Object Page. The “Home Court” column on the Games Object Page won’t update automatically:</P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 28. Display of unchanged field name in &quot;Game&quot; Object Page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397735i604AD67C8086C487/image-size/medium?v=v2&amp;px=400" role="button" title="image 27.jpg" alt="Figure 28. Display of unchanged field name in &quot;Game&quot; Object Page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 28. Display of unchanged field name in "Game" Object Page</span></span></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>To change it on the Games Object Page as well, you’ll need to rename the column in the Game Object Page. You can do this just by repeating the steps above for Games Object Page.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H4 id="toc-hId-515391819"><SPAN>Example 2: Adding a Description to an Object Page</SPAN></H4><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>&nbsp;</SPAN><STRONG>Scenario</STRONG>: Add the description “Game Information” to the Game Object Page.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 29. Empty description space in &quot;Game&quot; Object Page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397744i9B02D7B48AE27831/image-size/medium?v=v2&amp;px=400" role="button" title="image 29.jpg" alt="Figure 29. Empty description space in &quot;Game&quot; Object Page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 29. Empty description space in "Game" Object Page</span></span></LI></UL><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Click on <EM>Open Page Map</EM></SPAN></LI><LI><SPAN><SPAN>Select “Games Object Page”<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 30. Navigating to &quot;Game&quot; Object Page" style="width: 294px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397745i86F0B8D44DDDAB5A/image-size/medium?v=v2&amp;px=400" role="button" title="image 30.jpg" alt="Figure 30. Navigating to &quot;Game&quot; Object Page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 30. Navigating to "Game" Object Page</span></span></SPAN></SPAN></LI><LI><SPAN>Click on “Header”.</SPAN></LI><LI><SPAN><SPAN>On the RHS, find <EM>Description Type</EM> and select “Text” from the dropdown.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 31. Navigating to Description Type dropdown" style="width: 391px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397746iE2ADABCFAA3BCF8C/image-size/medium?v=v2&amp;px=400" role="button" title="image 31.jpg" alt="Figure 31. Navigating to Description Type dropdown" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 31. Navigating to Description Type dropdown</span></span></SPAN></SPAN></LI></OL><P class="lia-indent-padding-left-60px lia-align-justify" style="padding-left : 60px; text-align : justify;"><SPAN>Choosing “Text” allows you to enter custom free text. Alternatively, you can select “Property” to pick from predefined fields or objects. </SPAN></P><P class="lia-indent-padding-left-60px lia-align-justify" style="padding-left : 60px; text-align : justify;"><SPAN>&nbsp;</SPAN><SPAN>In this case, I selected the “Text” option because “Game Information” is not a predefined feature of any existing object in the application.</SPAN></P><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;">5. Type “Game Information” into the Description field.</P><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 32. How to add a text to the description space" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397748iCD521E73FCBA8E74/image-size/medium?v=v2&amp;px=400" role="button" title="image 32.jpg" alt="Figure 32. How to add a text to the description space" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 32. How to add a text to the description space</span></span></P><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><SPAN>6. </SPAN><SPAN>The UI will automatically refresh and update</SPAN></P><P class="lia-indent-padding-left-30px lia-align-justify" style="padding-left : 30px; text-align : justify;"><SPAN><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 33. Preview of &quot;Game&quot; Object Page with description text" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397749iA249593312445D47/image-size/medium?v=v2&amp;px=400" role="button" title="image 33.jpg" alt="Figure 33. Preview of &quot;Game&quot; Object Page with description text" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 33. Preview of "Game" Object Page with description text</span></span></SPAN></SPAN></P><P>&nbsp;</P><H4 id="toc-hId-318878314"><SPAN>Example 3: Adding an Icon to an Object Page</SPAN></H4><UL class="lia-align-justify" style="text-align : justify;"><LI><STRONG><SPAN>Scenario:</SPAN></STRONG><SPAN> Adding a soccer icon to the Game object page </SPAN></LI></UL><OL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>Click on <EM>Open Page Map</EM></SPAN></LI><LI><SPAN>Select the “Games Object Page” </SPAN></LI><LI><SPAN><SPAN><SPAN>Find the icon section<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 34. Navigating to the Icon Explorer" style="width: 295px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397758i850CFA4101DCE321/image-size/medium?v=v2&amp;px=400" role="button" title="image 34.jpg" alt="Figure 34. Navigating to the Icon Explorer" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 34. Navigating to the Icon Explorer</span></span></SPAN></SPAN></SPAN></LI><LI><SPAN>Click the link to the <EM>Icon Explorer</EM> </SPAN></LI><LI>Search for “soccer”<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Image 35. View of the Icon Explorer and searching for the term &quot;soccer&quot;" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397759iF64413399DC15BE1/image-size/medium?v=v2&amp;px=400" role="button" title="Screenshot 2026-03-20 094255.png" alt="Image 35. View of the Icon Explorer and searching for the term &quot;soccer&quot;" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Image 35. View of the Icon Explorer and searching for the term "soccer"</span></span><SPAN>You can also browse the available icons and select the one that fits your goals</SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 36. Overview of the existing SAPUI5-supported icons" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397760i1B13870DC4265239/image-size/medium?v=v2&amp;px=400" role="button" title="image 36.jpg" alt="Figure 36. Overview of the existing SAPUI5-supported icons" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 36. Overview of the existing SAPUI5-supported icons</span></span></LI><LI><SPAN>Copy the soccer ball icon’s URL</SPAN></LI></OL><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 37. Copying the soccer ball icon's URL" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397761i4A3F6FB76CB4B437/image-size/medium?v=v2&amp;px=400" role="button" title="image 37.jpg" alt="Figure 37. Copying the soccer ball icon's URL" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 37. Copying the soccer ball icon's URL</span></span></SPAN></P><P class="lia-indent-padding-left-30px" style="padding-left : 30px;">7. Switch to your Dev Space and paste the URL into the icon field<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 38. Pasting the copied URL into the Icon field" style="width: 327px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397762i0BF4AD0EDAB658C3/image-size/medium?v=v2&amp;px=400" role="button" title="image 38.jpg" alt="Figure 38. Pasting the copied URL into the Icon field" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 38. Pasting the copied URL into the Icon field</span></span><SPAN><SPAN>8. The UI will refresh automatically and display the icon<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 39. Preview of the &quot;Game&quot; Object Page with the soccer ball icon" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397763i7CA5BD06063AAFF7/image-size/medium?v=v2&amp;px=400" role="button" title="image 39.jpg" alt="Figure 39. Preview of the &quot;Game&quot; Object Page with the soccer ball icon" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 39. Preview of the "Game" Object Page with the soccer ball icon</span></span></SPAN></SPAN></P><P>&nbsp;</P><H1 id="toc-hId-1170757521"><SPAN>Next steps: </SPAN></H1><P class="lia-align-justify" style="text-align : justify;"><SPAN>After you are satisfied with the UI Layout;</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><STRONG><SPAN>For CAP:</SPAN></STRONG><SPAN> Add business logic in service handlers; enrich annotations; refine data model.</SPAN></LI><LI><STRONG><SPAN>RAP:</SPAN></STRONG><SPAN> Implement behavior logic, validation, determinations; adjust annotations; refine UI.</SPAN></LI></UL><H1 id="toc-hId-974244016">For more information:</H1><UL class="lia-align-justify" style="text-align : justify;"><LI>Official documentation page:</LI></UL><P class="lia-align-justify" style="text-align : justify;"><SPAN><A href="https://help.sap.com/docs/SAP_FIORI_tools/bdf9573a206b492382cc747e731cf34b/6845fedbb38c4da7a54a2c76081f3abb.html?version=DEV&amp;state=DRAFT" target="_blank" rel="noopener noreferrer">Generating an Application with the Project Accelerator Using SAP Fiori Tools AI | SAP Help Portal</A></SPAN></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-484327504"><SPAN>Appendix</SPAN></H2><H3 id="toc-hId--5589008">1)&nbsp;&nbsp;&nbsp; <SPAN>Specifying the sport type as volleyball</SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><BR />&nbsp;To see how specifying the sport type affects the generated mock data, I added additional requirements database model I originally used<SPAN>:</SPAN><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 40. Updated database model, sport type specified as volleyball" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398109iC8825118BEA93956/image-size/medium?v=v2&amp;px=400" role="button" title="test8.jpeg" alt="Figure 40. Updated database model, sport type specified as volleyball" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 40. Updated database model, sport type specified as volleyball</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>The generated mock data became more volleyball-oriented, as reflected in the “Team” and “Place” names:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 41. Preview with volleyball-specific mock data" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398102i6D6F9F5DA842055D/image-size/medium?v=v2&amp;px=400" role="button" title="image 40.jpg" alt="Figure 41. Preview with volleyball-specific mock data" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 41. Preview with volleyball-specific mock data</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>I was also interested to see if the number of players would change based on the specified sport type, for example:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>a volleyball team having at least 6 players</SPAN></LI><LI><SPAN>a soccer team having 11</SPAN></LI><LI><SPAN>a basketball team having 5</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;"><SPAN>However, the number of players did not change compared to the original input.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 42. &quot;Team&quot; Object Page displaying players" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398105iB21085520AA2931B/image-size/medium?v=v2&amp;px=400" role="button" title="image 42.jpg" alt="Figure 42. &quot;Team&quot; Object Page displaying players" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 42. "Team" Object Page displaying players</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;<SPAN>If you want to have specific number of instances, you can explicitly include this in your business requirements:</SPAN><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 43. Data model diagram with amount of team members specified" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398107iE0121E715D4C63D8/image-size/medium?v=v2&amp;px=400" role="button" title="image 42.jpeg" alt="Figure 43. Data model diagram with amount of team members specified" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 43. Data model diagram with amount of team members specified</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 44. &quot;Team&quot; Object Page with 6 players" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398110iEBB1FA9FBF19D4C5/image-size/medium?v=v2&amp;px=400" role="button" title="image 43.jpg" alt="Figure 44. &quot;Team&quot; Object Page with 6 players" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 44. "Team" Object Page with 6 players</span></span></P><P>&nbsp;</P><H3 id="toc-hId--202102513">2)&nbsp;&nbsp;&nbsp; <SPAN>Using Mock UI</SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><SPAN>For the Mock UI, I uploaded an image intended to represent the list of all games and the corresponding object page:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 45. Mock UI - First attempt" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398118iF7032BFD30170314/image-size/medium?v=v2&amp;px=400" role="button" title="test 3.jpeg" alt="Figure 45. Mock UI - First attempt" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 45. Mock UI - First attempt</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>However, the LLM did not recognize this image as one of its supported input types and returned an error:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>If you are providing a Mock UI, the input should be clear and focused to avoid such errors.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 46. Error message for unsupported images" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398119i338954D24B946BA7/image-size/medium?v=v2&amp;px=400" role="button" title="image 45.png" alt="Figure 46. Error message for unsupported images" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 46. Error message for unsupported images</span></span></SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>In my experience, Mock UIs produce the best results when the input is as simple and clear as possible. For example, the image below worked well:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 47. Mock UI - Second attempt" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398122i0E0133D20470CE86/image-size/medium?v=v2&amp;px=400" role="button" title="test 15.jpeg" alt="Figure 47. Mock UI - Second attempt" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 47. Mock UI - Second attempt</span></span></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>The Project Accelerator successfully generated the list report and even added “Players” and “Teams” objects to the object page, which was a positive surprise</SPAN><SPAN>:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 48. Successfully generated application with Mock UI" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398124i21F211DFA2DBB741/image-size/medium?v=v2&amp;px=400" role="button" title="berin__5-1776173776196.png" alt="Figure 48. Successfully generated application with Mock UI" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 48. Successfully generated application with Mock UI</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Image 49. Successfully generated application with Mock UI - Object Page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398125i36F26F4A6E6C1F03/image-size/medium?v=v2&amp;px=400" role="button" title="image 48.jpg" alt="Image 49. Successfully generated application with Mock UI - Object Page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Image 49. Successfully generated application with Mock UI - Object Page</span></span></P><P>&nbsp;</P><H3 id="toc-hId--398616018">3)&nbsp;&nbsp;&nbsp; <SPAN>Using Multimedia Input: Multiple images</SPAN></H3><P class="lia-align-justify" style="text-align : justify;"><SPAN>I combined the input material into a single .docx document and uploaded it to the accelerator:</SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 50. The two images, combined in a single document" style="width: 352px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398128iFA4D96847AA97F22/image-size/medium?v=v2&amp;px=400" role="button" title="image 49.jpg" alt="Figure 50. The two images, combined in a single document" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 50. The two images, combined in a single document</span></span></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>You can upload the .docx or .md files the same way as you would for images.</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>The following application was generated:</SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 51. Preview of the application generated with multi-media input" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398129iCEB391D8351858F6/image-size/medium?v=v2&amp;px=400" role="button" title="image 50.jpg" alt="Figure 51. Preview of the application generated with multi-media input" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 51. Preview of the application generated with multi-media input</span></span></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 52. Preview of the application generated with multi-media input - Object Page" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398130i86424D53A02B7C4F/image-size/medium?v=v2&amp;px=400" role="button" title="image 51.jpg" alt="Figure 52. Preview of the application generated with multi-media input - Object Page" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 52. Preview of the application generated with multi-media input - Object Page</span></span></P><P>&nbsp;</P><H4 id="toc-hId--888532530"><SPAN>Mismatch in object fields</SPAN><SPAN> &nbsp;</SPAN></H4><P class="lia-align-justify" style="text-align : justify;"><SPAN>When combining multiple input sources, such as database tables and Mock UI images, it is important that these models are consistent. Inconsistencies between the data model and the UI definition can lead to incorrect or incomplete application generation:</SPAN></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 53. Multi-media input with mismatching fields" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398132iBC7CED12745AF87A/image-size/medium?v=v2&amp;px=400" role="button" title="image 52.jpg" alt="Figure 53. Multi-media input with mismatching fields" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 53. Multi-media input with mismatching fields</span></span></P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 54. Preview of the application generated with the input shown in Figure 53" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/398133iE153C24D2BF34862/image-size/medium?v=v2&amp;px=400" role="button" title="image 53.jpg" alt="Figure 54. Preview of the application generated with the input shown in Figure 53" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 54. Preview of the application generated with the input shown in Figure 53</span></span></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>&nbsp;The following issue occurred due to such a mismatch:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><SPAN>The “Game” entity contains additional fields in the database model that are not represented in the UI. </SPAN></LI><LI><SPAN>The “Team” entity is missing a role field in the database model that is expected by the UI.</SPAN></LI></UL><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P> 2026-04-15T12:00:49.174000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/handling-dynamic-fields-in-sap-rap-using-json-payload-in-cds-custom-entity/ba-p/14370796 Handling Dynamic Fields in SAP RAP using JSON Payload in CDS Custom Entity 2026-04-15T22:52:03.905000+02:00 hasan123 https://community.sap.com/t5/user/viewprofilepage/user-id/1928123 <H2 id="toc-hId-1794085259"><STRONG>Introduction<BR /><BR /></STRONG></H2><P>Core Data Services (CDS) entities in the ABAP RESTful Application Programming Model (RAP) are usually constructed using statically set parameters. Although this guarantees robust typing and clarity, it becomes restrictive when programs have to deal with dynamic inputs like changing fields, tables, or runtime-driven filters.</P><P>Such adaptability is crucial in many real-world situations. Nevertheless, dynamic parameter structures are not supported by CDS natively, which frequently results in complicated and difficult-to-maintain designs.</P><P>This blog describes an adaptable method that passes dynamic field data as a single parameter and processes it at the ABAP layer using a JSON payload inside a CDS Custom Entity. This keeps the CDS design straightforward while enabling more flexible and scalable RAP solutions.<BR /><BR /></P><H2 id="toc-hId-1596850814" id="toc-hId-1597571754">What are CDS Custom Entities in RAP?</H2><P>A CDS Custom Entity is a unique kind of CDS entity that does not read data directly from the database in the ABAP RESTful Application Programming Model (RAP). Rather, an ABAP class is used to accomplish it, and the developer has complete control over the data retrieval mechanism.</P><P>In contrast to Standard CDS Views:</P><UL><LI>Custom Entities enable ABAP's dynamic data processing.</LI><LI>When logic cannot be managed only at the database level, they are usually employed.</LI><LI>They offer the freedom to execute intricate or unconventional scenarios, including external integrations or dynamic queries.</LI></UL><P>Because the data is fetched and processed in the ABAP layer, Custom Entities are well-suited for scenarios where:</P><UL><LI>Input parameters need to be interpreted dynamically</LI><LI>Data sources or structures vary at runtime</LI><LI>Standard CDS capabilities are not sufficient</LI></UL><H2 id="toc-hId-1400337309" id="toc-hId-1401058249">Real-Time Business Scenario: Dynamic Field Validation</H2><P>In some SAP applications, there is a requirement to validate fields dynamically based on user input.</P><P>For instance, a user or an outside system might submit a request such as:</P><UL><LI>Verify if a table contains the specified fields.</LI><LI>During runtime, dynamically provide field values</LI><LI>Tables and fields are not predetermined.</LI></UL><P>One Sample input would be:</P><UL><LI>Table: SFLIGHT</LI><LI>Fields: FLDATE and CARRID</LI><LI>Values: LH, 20260313&nbsp;</LI></UL><P>The table and fields can be entirely different in a different request. This means that any table and any field must be handled dynamically by the application without the need for preset parameters.</P><H3 id="toc-hId-1332906523" id="toc-hId-1333627463">The Problem</H3><P>If we attempt to use normal CDS to implement this:</P><UL><LI>Each field would require its own set of parameters.</LI><LI>As fields alter, the number of parameters would increase.</LI><LI>Unknown or dynamic fields cannot be handled.</LI></UL><P>The Restrictions:<BR />Flexible structures like a "list of fields" cannot be dynamically accepted by CDS. Because of this, the design is inflexible and challenging to scale.</P><H3 id="toc-hId-1332906523" id="toc-hId-1137113958">The Solution</H3><P>We use a single argument (FieldValuesJson) to deliver all field details as a JSON payload in order to resolve this.</P><P>There are other entries in this JSON, such as:</P><UL><LI>Table name</LI><LI>field name</LI><LI>field value</LI></UL><P>In the ABAP Implementation:</P><UL><LI>An internal table is created by parsing the JSON.</LI><LI>Every field is dynamically verified using the Data Dictionary (DD03L).</LI><LI>The existence of the field is indicated by the result that is returned.</LI></UL><P>Without altering the CDS definition, this method enables the application to dynamically handle any table and any field.</P><H2 id="toc-hId-810796794" id="toc-hId-811517734">Implementation Using CDS Custom Entity</H2><H4 id="toc-hId-872448727" id="toc-hId-873169667">Step 1: Create CDS Custom Entity</H4><P>First, define a CDS Custom Entity to accept input parameter and return results.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Dynamic Field Filter Custom Entity' @ObjectModel.query.implementedBy: 'ABAP:ZCL_DYN_FIELD_QUERY' define root custom entity ZI_DYN_FIELD_ENTITY { .selectionField: [{ position: 10 }] @EndUserText.label: 'Object Type' key ObjectType : abap.char(30); .selectionField: [{ position: 20 }] @EndUserText.label: 'Policy Type' key PolicyType : abap.char(20); @EndUserText.label: 'Table Name' key Tabname : abap.char(30); @EndUserText.label: 'Field Name' key Fieldname : abap.char(30); .selectionField: [{ position: 30 }] @EndUserText.label: 'Get Destructible' GetDestructible : abap.char(1); .selectionField: [{ position: 40 }] @EndUserText.label: 'Field Values JSON' FieldValuesJson : abap.char(1333); @EndUserText.label: 'Field Value' FieldValue : abap.char(100); @EndUserText.label: 'Result Info' ResultInfo : abap.char(200); }</code></pre><P>Explanation:</P><UL><LI>FieldValuesJson is used to pass dynamic field data as JSON</LI><LI>Output fields (Tabname, Fieldname, FieldValue, ResultInfo) hold processed results</LI><LI>The annotation @ObjectModel.query.implementedBy links to the ABAP class</LI></UL><H3 id="toc-hId-546852503" id="toc-hId-547573443">Step 2: Create Behavior Definition (Unmanaged)</H3><P>Define a behavior Definition for the custom entity:</P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zcl_dyn_field_query unique; strict ( 2 ); define behavior for ZI_DYN_FIELD_ENTITY authorization master ( global ) lock master { field ( readonly ) ObjectType; field ( readonly ) PolicyType; field ( readonly ) Tabname; field ( readonly ) Fieldname; }</code></pre><P>Explanation:</P><UL><LI>Unmanaged behavior is used since logic is fully handled in ABAP</LI><LI>Fields are marked as readonly because this is a read-only scenario</LI><LI>No CRUD operations are required</LI></UL><H3 id="toc-hId-546852503" id="toc-hId-351059938">Step 3: Implement Query Provider Class</H3><P>Create the ABAP Class and implement <STRONG>IF_RAP_QUERY_PROVIDER:</STRONG></P><pre class="lia-code-sample language-abap"><code>CLASS zcl_dyn_field_query DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. " RAP Query Provider Interface for Custom Entity INTERFACES if_rap_query_provider. " Structure to hold dynamic field input from JSON TYPES: BEGIN OF ty_s_field_input, tabname TYPE string, fieldname TYPE string, value TYPE string, END OF ty_s_field_input, ty_t_field_input TYPE STANDARD TABLE OF ty_s_field_input WITH DEFAULT KEY. PRIVATE SECTION. " Result structure based on CDS Custom Entity TYPES ty_t_result TYPE STANDARD TABLE OF zi_dyn_field_entity WITH DEFAULT KEY. " Method to parse JSON payload into internal table METHODS parse_field_values_json IMPORTING iv_json TYPE string RETURNING VALUE(rt_fv) TYPE ty_t_field_input. " Method to apply validation logic on dynamic fields METHODS apply_rules IMPORTING it_field_values TYPE ty_t_field_input iv_object_type TYPE string iv_policy_type TYPE string iv_destructible TYPE string RETURNING VALUE(rt_result) TYPE ty_t_result. ENDCLASS. CLASS zcl_dyn_field_query IMPLEMENTATION. METHOD if_rap_query_provider~select. " Variables to hold filter inputs DATA lv_object_type TYPE string. DATA lv_policy_type TYPE string. DATA lv_destructible TYPE string. DATA lv_json TYPE string. DATA lt_result TYPE ty_t_result. " 1. Extract filters from the Odata request TRY. DATA(lo_filter) = io_request-&gt;get_filter( ). DATA(lt_filter_cond) = lo_filter-&gt;get_as_ranges( ). LOOP AT lt_filter_cond INTO DATA(ls_filter). CASE ls_filter-name. WHEN 'OBJECTTYPE'. lv_object_type = ls_filter-range[ 1 ]-low. WHEN 'POLICYTYPE'. lv_policy_type = ls_filter-range[ 1 ]-low. WHEN 'GETDESTRUCTIBLE'. lv_destructible = ls_filter-range[ 1 ]-low. WHEN 'FIELDVALUESJSON'. lv_json = ls_filter-range[ 1 ]-low. ENDCASE. ENDLOOP. CATCH cx_root. " Optional: Handle Invalid filter scenario ENDTRY. " 2. Convert JSON payload into internal table DATA(lt_fv) = parse_field_values_json( lv_json ). " 3. Execute dynamic validation logic lt_result = apply_rules( it_field_values = lt_fv iv_object_type = lv_object_type iv_policy_type = lv_policy_type iv_destructible = lv_destructible ). " 4. Handle case where no valid input is provided IF lt_result IS INITIAL. APPEND VALUE #( objecttype = lv_object_type policytype = lv_policy_type getdestructible = lv_destructible fieldvaluesjson = lv_json resultinfo = 'No valid JSON fields provided' ) TO lt_result. ENDIF. " 5. Handle total record count (for paging support) DATA lv_total TYPE int8. lv_total = lines( lt_result ). IF io_request-&gt;is_total_numb_of_rec_requested( ). io_response-&gt;set_total_number_of_records( lv_total ). ENDIF. " 6. Apply paging and return response data IF io_request-&gt;is_data_requested( ). DATA(lo_paging) = io_request-&gt;get_paging( ). DATA(lv_offset) = lo_paging-&gt;get_offset( ). DATA(lv_limit) = lo_paging-&gt;get_page_size( ). IF lv_limit = if_rap_query_paging=&gt;page_size_unlimited. io_response-&gt;set_data( lt_result ). ELSE. DATA lt_paged LIKE lt_result. " Apply offset + limit slicing APPEND LINES OF lt_result FROM ( lv_offset + 1 ) TO ( lv_offset + lv_limit ) TO lt_paged. io_response-&gt;set_data( lt_paged ). ENDIF. ENDIF. ENDMETHOD. METHOD parse_field_values_json. " Return empty if no JSON is provided IF iv_json IS INITIAL. RETURN. ENDIF. " Deserialize JSON into ABAP structure /ui2/cl_json=&gt;deserialize( EXPORTING json = iv_json CHANGING data = rt_fv ). ENDMETHOD. METHOD apply_rules. " Loop through each dynamic field input LOOP AT it_field_values INTO DATA(ls_fv). DATA ls_result TYPE zi_dyn_field_entity. CLEAR ls_result. " Check if field exists in Data Dictionary SELECT SINGLE fieldname FROM dd03l WHERE tabname = _fv-tabname AND fieldname = _fv-fieldname INTO (lv_fname). " Map input and context data to result structure ls_result-objecttype = iv_object_type. ls_result-policytype = iv_policy_type. ls_result-getdestructible = iv_destructible. ls_result-tabname = ls_fv-tabname. ls_result-fieldname = ls_fv-fieldname. ls_result-fieldvalue = ls_fv-value. " Set validation result message IF sy-subrc = 0. ls_result-resultinfo = |Found: { ls_fv-tabname }-{ ls_fv-fieldname }|. ELSE. ls_result-resultinfo = |Error: { ls_fv-tabname }-{ ls_fv-fieldname } not in DDIC|. ENDIF. APPEND ls_result TO rt_result. ENDLOOP. ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><H3 id="toc-hId--117919381" id="toc-hId-154546433">Result:</H3><P>Field inputs given as a JSON payload can be dynamically processed by the service once the CDS Custom Entity and query provider logic have been implemented.</P><P>When the following request is made to the service:</P><pre class="lia-code-sample language-abap"><code>/sap/opu/odata4/.../ZI_XXXXX? $filter= ObjectType eq 'BC_SFLIGHT' and PolicyType eq 'DEMO' and GetDestructible eq 'X' and FieldValuesJson eq '[{"tabname":"SFLIGHT","fieldname":"CARRID","value":"LH"},{"tabname":"SFLIGHT","fieldname":"FLDATE","value":"20260313"}]'</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="hasan123_0-1775816388959.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/396009i90C4BCBB27B6615E/image-size/large?v=v2&amp;px=999" role="button" title="hasan123_0-1775816388959.png" alt="hasan123_0-1775816388959.png" /></span></P><H3 id="toc-hId--117919381" id="toc-hId--117198441">Conclusion</H3><P>In this blog, we explored the challenge of handling dynamic fields in RAP when working with statically defined CDS parameters. We discussed a real-time scenario where field names, tables, and values are not fixed and need to be processed dynamically.</P><P>To address this, we implemented a solution using a CDS Custom Entity and passed dynamic input as a JSON payload, which is parsed and processed in the ABAP query provider class. This allowed us to validate fields against the Data Dictionary without modifying the CDS structure.</P><P>Overall, this approach provides a flexible and reusable solution for dynamic field handling in RAP, making it easier to adapt to changing requirements while keeping the design simple and maintainable.</P> 2026-04-15T22:52:03.905000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/handling-cross-business-object-transactions-in-rap-using-actions/ba-p/14373581 Handling Cross Business Object Transactions in RAP Using Actions 2026-04-16T12:34:56.887000+02:00 Tejashree__K https://community.sap.com/t5/user/viewprofilepage/user-id/2293605 <H2 id="toc-hId-1794172674">Introduction</H2><P>In the <SPAN class=""><SPAN class="">SAP RAP</SPAN></SPAN>, most examples focus on single Business Object (BO) operations. However, real-world enterprise applications rarely operate in isolation. A single business transaction often impacts multiple BOs—for example, creating a Delivery should trigger Billing, updating inventory, and possibly financial postings.<BR />In this blog, we explore how to implement <STRONG>cross-BO interaction using RAP Actions</STRONG>, where:</P><UL><LI>A <STRONG>Delivery BO</STRONG> triggers creation in a <STRONG>Billing BO</STRONG></LI><LI>Both operations are executed within the same <STRONG>LUW (Logical Unit of Work)</STRONG></LI><LI>Data consistency is preserved using RAP’s transactional buffer</LI></UL><H3 id="toc-hId-1726741888">Business Scenario</H3><P>Consider a typical <STRONG>Order-to-Cash process</STRONG>:</P><OL><LI>A <STRONG>Delivery</STRONG> is created for a customer.</LI><LI>Once the delivery is confirmed, a <STRONG>Billing document</STRONG> must be generated.</LI><LI>The system should:<UL><LI>Automatically create a Billing record</LI><LI>Update the Delivery status to <EM>DELIVERED</EM></LI><LI>Ensure both operations succeed or fail together</LI></UL></LI></OL><H4 id="toc-hId-1659311102">Implementation Steps</H4><P><SPAN>STEP 1:&nbsp;Create a Data base Table</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Data base&nbsp; table For Delivery Details&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'delivery details' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zte_t_delivery_s { key delivery_id : abap.char(10) not null; customer_id : abap.char(5); material : abap.char(20); price : abap.char(10); delivery_date : abap.dats; delivery_status : abap.char(15); } </code></pre><P>Data Base table for Billing Details</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Billing Details' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zte_t_billing_s { key billing_id : abap.char(10) not null; customer_id : abap.char(5); delivery_id : abap.char(10); material : abap.char(20); price : abap.char(10); } </code></pre><P>STEP 2: Create root CDS views for both tables</P><P><SPAN>Root view For Delivery Details</SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Delivery Details' @Metadata.ignorePropagatedAnnotations: true define root view entity ZI_T_DELIVERY_S as select from zte_t_delivery_s { key delivery_id as DeliveryId, customer_id as CustomerId, material as Material, price as Price, delivery_date as DeliveryDate, delivery_status as DeliveryStatus } </code></pre><P>&nbsp;<SPAN>Root view For Billing Details</SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Billing Details' @Metadata.ignorePropagatedAnnotations: true define root view entity ZI_T_BILLING_S as select from zte_t_billing_s { key billing_id as BillingId, customer_id as CustomerId, delivery_id as DeliveryId, material as Material, price as Price } </code></pre><P>STEP 3: Create Projection View&nbsp;</P><UL><LI>Add UI Annotations</LI><LI>Add Action Button In Delivery Projection</LI></UL><P><SPAN>Projection View For Delivery Details</SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Delivery Details' @Metadata.ignorePropagatedAnnotations: true define root view entity ZC_T_DELIVERY_S provider contract transactional_query as projection on ZI_T_DELIVERY_S { @UI.facet: [{ id: 'deliverdetails', position: 1, label: 'Order Details', type: #IDENTIFICATION_REFERENCE } ] @UI.lineItem: [ { importance: #MEDIUM}, { position: 10, type : #FOR_ACTION, label : 'Available', dataAction : 'create_delivery' }, { position: 1, label : 'Delivery ID' }] @UI.identification: [{ position: 1, label : 'Delivery ID' }] key DeliveryId, @UI.lineItem: [{ position: 2, label : 'Customer ID' }] @UI.identification: [{ position: 2, label : 'Customer ID' }] CustomerId, @UI.lineItem: [{ position: 3, label : 'Material' }] @UI.identification: [{ position: 3, label : 'Material' }] Material, @UI.lineItem: [{ position: 4, label : 'Price' }] @UI.identification: [{ position: 4, label : 'Price' }] Price, @UI.lineItem: [{ position: 5, label : 'Delivery Date' }] @UI.identification: [{ position: 5, label : 'Delivery Date' }] DeliveryDate, @UI.lineItem: [{ position: 6, label : 'Delivery Status' }] @UI.identification: [{ position: 6, label : 'Delivery Status' }] DeliveryStatus } </code></pre><P>Projection View for Billing Details</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Billing Details' @Metadata.ignorePropagatedAnnotations: true define root view entity ZC_T_BILLING_S provider contract transactional_query as projection on ZI_T_BILLING_S { @UI.facet: [{ id: 'Billingdetails', position: 1, label: 'Billing Details', type: #IDENTIFICATION_REFERENCE } ] @UI.lineItem: [{ position: 1, label : 'Billing Id' }] @UI.identification: [{ position: 1, label : 'Billing Id' }] key BillingId, @UI.lineItem: [{ position: 2, label : 'Customer Id' }] @UI.identification: [{ position: 2, label : 'Custoomer Id' }] CustomerId, @UI.lineItem: [{ position: 3, label : 'Delivery Id' }] @UI.identification: [{ position: 3, label : 'Delivery Id' }] DeliveryId, @UI.lineItem: [{ position: 4, label : 'Material' }] @UI.identification: [{ position: 4, label : 'Material' }] Material, @UI.lineItem: [{ position: 5, label : 'Price' }] @UI.identification: [{ position: 5, label : 'Price' }] Price } </code></pre><P>STEP 4: Define Behavior Definitions<BR />&nbsp;Delivery BO behavior</P><UL><LI>Enable CRUD operation</LI><LI>Declare a non-factory action</LI></UL><P>This Action is responsible for creating Billing data and updating delivery status</P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_t_delivery_s unique; strict ( 2 ); define behavior for ZI_T_DELIVERY_S //alias &lt;alias_name&gt; persistent table zte_t_delivery_s lock master authorization master ( instance ) //etag master &lt;field_name&gt; { create; update; delete; action create_delivery result [1] $self; mapping for zte_t_delivery_s { DeliveryId = delivery_id; CustomerId = customer_id; Material = material; Price = price; DeliveryDate = delivery_date; DeliveryStatus = delivery_status; } } </code></pre><P>Billing BO behavior</P><UL><LI>Enable CRUD operations</LI><LI>use Late Numbering for auto-generating Billing Id</LI></UL><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_t_billing_s unique; strict ( 2 ); define behavior for ZI_T_BILLING_S //alias &lt;alias_name&gt; persistent table zte_t_billing_s lock master authorization master ( instance ) late numbering //etag master &lt;field_name&gt; { create; update; delete; mapping for zte_t_billing_s { BillingId = billing_id; DeliveryId = delivery_id; CustomerId = customer_id; Material = material; Price = price; } } </code></pre><P>STEP 5: Define Projection behavior&nbsp;</P><P>Delivery BO projection behavior</P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); define behavior for ZC_T_DELIVERY_S //alias &lt;alias_name&gt; { use create; use update; use delete; use action create_delivery; } </code></pre><P>Billing BO projection behavior</P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); define behavior for ZC_T_BILLING_S //alias &lt;alias_name&gt; { use create; use update; use delete; } </code></pre><P>STEP 6:&nbsp;&nbsp;Implement Behavior Logic</P><P>Delivery Class</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_ZI_T_DELIVERY_S DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zi_t_delivery_s RESULT result. METHODS create_delivery FOR MODIFY IMPORTING keys FOR ACTION zi_t_delivery_s~create_delivery RESULT result. ENDCLASS. CLASS lhc_ZI_T_DELIVERY_S IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD create_delivery. READ ENTITIES OF ZI_T_DELIVERY_S IN LOCAL MODE ENTITY ZI_T_DELIVERY_S FIELDS ( DeliveryId CustomerId Material Price ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_delivery). LOOP AT lt_delivery INTO DATA(ls_delivery). "Create Billing Entry MODIFY ENTITIES OF ZI_T_BILLING_S ENTITY ZI_T_BILLING_S CREATE FIELDS ( DeliveryId CustomerId Material Price ) WITH VALUE #( ( %cid = 'BILL1' DeliveryId = ls_delivery-DeliveryId CustomerId = ls_delivery-CustomerId Material = ls_delivery-Material Price = ls_delivery-Price ) ) FAILED DATA(ls_failed) REPORTED DATA(ls_reported). IF ls_failed IS NOT INITIAL. reported = CORRESPONDING #( ls_reported ). RETURN. ENDIF. "Update Delivery Status MODIFY ENTITIES OF ZI_T_DELIVERY_S IN LOCAL MODE ENTITY ZI_T_DELIVERY_S UPDATE FIELDS ( DeliveryStatus ) WITH VALUE #( ( DeliveryId = ls_delivery-DeliveryId DeliveryStatus = 'DELIVERED' ) ). ENDLOOP. ENDMETHOD. ENDCLASS. </code></pre><P>Billing Class</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_ZI_T_BILLING_S DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zi_t_billing_s RESULT result. ENDCLASS. CLASS lhc_ZI_T_BILLING_S IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. ENDCLASS. CLASS lsc_ZI_T_BILLING_S DEFINITION INHERITING FROM cl_abap_behavior_saver. PROTECTED SECTION. METHODS adjust_numbers REDEFINITION. METHODS cleanup_finalize REDEFINITION. ENDCLASS. CLASS lsc_ZI_T_BILLING_S IMPLEMENTATION. METHOD adjust_numbers. SELECT FROM ZTE_T_Billing_S FIELDS MAX( billing_id ) INTO (ls_mat_num). DATA(lv_num) = ls_mat_num+3(4). LOOP AT mapped-zi_t_billing_s ASSIGNING FIELD-SYMBOL(&lt;fs_material&gt;). lv_num += 1. &lt;fs_material&gt;-BillingId = |BIL{ lv_num ALPHA = IN WIDTH = 4 }|. ENDLOOP. ENDMETHOD. METHOD cleanup_finalize. ENDMETHOD. ENDCLASS. </code></pre><P>STEP 7: Service Defination</P><P>Expose Both Behavior objects</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Delivery and billing details' define service ZSD_T_DETAILS { expose ZC_T_DELIVERY_S; expose ZC_T_BILLING_S; } </code></pre><P>STEP 8 : Service Binding</P><UL><LI>create service binding</LI><LI>Activate and publish</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-04-14 111025.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397823i8476A4E2AE84645F/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-14 111025.png" alt="Screenshot 2026-04-14 111025.png" /></span></P><P>STEP 9: RESULT</P><P>Create a record and click on the Action Button the delivery Status will be updated and Billing records will be created&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-04-14 111910.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397825iA4D9E2B07A2155B9/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-14 111910.png" alt="Screenshot 2026-04-14 111910.png" /></span></P><P>Billing details when Action is trigged and Delivery status updated to Delivery</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-04-14 112132.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/397826i0200461ABD756B77/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-04-14 112132.png" alt="Screenshot 2026-04-14 112132.png" /></span></P><DIV class=""><DIV class=""><DIV class=""><H2 id="toc-hId-1204632159">Conclusion</H2><P>Cross Business Object operations in RAP are essential for building real-world enterprise applications. While RAP abstracts much of the complexity, designing such interactions requires a clear understanding of:</P><UL><LI>Transactional behavior</LI><LI>BO independence vs coordination</LI><LI>Framework-driven data consistency By leveraging RAP Actions and EML, we can safely orchestrate multi-BO updates without compromising on clean architecture or data integrity.</LI></UL><P>This example demonstrates how a simple Delivery trigger can drive Billing creation, showcasing a scalable pattern that can be extended to more complex business processes.</P></DIV></DIV></DIV> 2026-04-16T12:34:56.887000+02:00