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-07-24T20:00:07.023083+00:00 python-feedgen ABAP RESTful Application Programming Model blog posts in SAP Community https://community.sap.com/t5/technology-blog-posts-by-members/implementing-rap-action-parameter-value-help-using-cds-abstract-entity-in/ba-p/14413064 Implementing RAP Action Parameter Value Help Using CDS Abstract Entity in SAP RAP 2026-06-09T09:39:02.577000+02:00 hasan123 https://community.sap.com/t5/user/viewprofilepage/user-id/1928123 <H2 id="toc-hId-1817255835"><SPAN>Introduction</SPAN></H2><P class=""><SPAN>The ABAP RESTful Application Programming Model (RAP) provides a powerful way to build modern SAP Fiori applications using behavior definitions, CDS views, and OData services.</SPAN></P><P class=""><SPAN>One common requirement in business applications is allowing users to execute actions on one or multiple records while providing additional input through a dialog popup. In many scenarios, this input should be selected from a predefined list of values rather than entered manually.</SPAN></P><P class=""><SPAN>A typical example is updating the category of one or more products. Users should be able to trigger an action, choose a valid category from a value help dialog, and apply the selected category to all chosen records.</SPAN></P><P class=""><SPAN>This blog demonstrates how to implement an RAP Action Parameter with Value Help using a CDS Abstract Entity, CDS Value Help View, and Behavior Action. The result is a Fiori Elements application where users can execute an action and select values through standard SAP F4 help.</SPAN></P><DIV><HR /></DIV><H2 id="toc-hId-1620742330"><SPAN>What are RAP Action Parameters?</SPAN></H2><P class=""><SPAN>Actions in RAP allow developers to execute custom business logic that goes beyond standard Create, Update, and Delete operations.</SPAN></P><P class=""><SPAN>An action can:</SPAN></P><UL><LI><SPAN>Execute custom business logic</SPAN></LI><LI><SPAN>Update one or multiple records</SPAN></LI><LI><SPAN>Trigger validations</SPAN></LI><LI><SPAN>Open parameter dialogs for user input</SPAN></LI></UL><P class=""><SPAN>When an action requires user input, RAP automatically generates a dialog popup using an Abstract Entity.</SPAN></P><P class=""><SPAN>This makes actions ideal for scenarios such as:</SPAN></P><UL><LI><SPAN>Approving business documents</SPAN></LI><LI><SPAN>Assigning categories</SPAN></LI><LI><SPAN>Updating statuses</SPAN></LI><LI><SPAN>Triggering business processes</SPAN></LI></UL><DIV><HR /></DIV><H2 id="toc-hId-1424228825"><SPAN>Real-Time Business Scenario: Mass Category Assignment</SPAN></H2><P class=""><SPAN>Consider a product management application.</SPAN></P><P class=""><SPAN>Business users maintain products and their categories.</SPAN></P><P class=""><SPAN>Examples:</SPAN></P><P>Product ID Product Name Category</P><TABLE><TBODY><TR><TD><SPAN>P1001</SPAN></TD><TD><SPAN>Laptop</SPAN></TD><TD><SPAN>ELEC</SPAN></TD></TR><TR><TD><SPAN>P1002</SPAN></TD><TD><SPAN>Office Chair</SPAN></TD><TD><SPAN>FURN</SPAN></TD></TR><TR><TD><SPAN>P1003</SPAN></TD><TD><SPAN>T-Shirt</SPAN></TD><TD><SPAN>CLTH</SPAN></TD></TR></TBODY></TABLE><P class=""><SPAN>A user selects multiple products and wants to update their category simultaneously.</SPAN></P><P class=""><SPAN>Instead of editing each product individually:</SPAN></P><OL><LI><SPAN>Select one or more products</SPAN></LI><LI><SPAN>Click </SPAN><STRONG>Set Category</STRONG></LI><LI><SPAN>Choose a category from F4 Help</SPAN></LI><LI><SPAN>Confirm the action</SPAN></LI></OL><P class=""><SPAN>All selected products are updated automatically.</SPAN></P><DIV><HR /></DIV><H2 id="toc-hId-1227715320"><SPAN>The Problem</SPAN></H2><P class=""><SPAN>If the category field is entered manually:</SPAN></P><UL><LI><SPAN>Users may enter invalid values</SPAN></LI><LI><SPAN>Validation becomes difficult</SPAN></LI><LI><SPAN>User experience is poor</SPAN></LI><LI><SPAN>Data consistency cannot be guaranteed</SPAN></LI></UL><P class=""><SPAN>Example:</SPAN></P><PRE><CODE><SPAN>ELEC ✔ Valid FURN ✔ Valid ABCD ✖ Invalid XYZ1 ✖ Invalid</SPAN></CODE></PRE><P class=""><SPAN>Without a value help mechanism, users can easily enter incorrect data.</SPAN></P><DIV><HR /></DIV><H2 id="toc-hId-1031201815"><SPAN>The Solution</SPAN></H2><P class=""><SPAN>To solve this problem, we implement:</SPAN></P><UL><LI><SPAN>A Category Master table</SPAN></LI><LI><SPAN>A CDS Value Help View</SPAN></LI><LI><SPAN>A CDS Abstract Entity for action parameters</SPAN></LI><LI><SPAN>A RAP Action with parameter support</SPAN></LI><LI><SPAN>A Fiori Elements Value Help dialog</SPAN></LI></UL><P class=""><SPAN>The action parameter field is linked to the value help CDS entity using:</SPAN></P><PRE><CODE><SPAN>@Consumption.valueHelpDefinition</SPAN></CODE></PRE><P><SPAN>This automatically generates:</SPAN></P><UL><LI><SPAN>F4 Help</SPAN></LI><LI><SPAN>Search dialog</SPAN></LI><LI><SPAN>Value validation</SPAN></LI><LI><SPAN>User-friendly selection experience</SPAN></LI></UL><H1 id="toc-hId-705605591"><SPAN>Implementation Using RAP Action Parameter Value Help</SPAN></H1><H2 id="toc-hId-638174805"><SPAN>Step 1: Create Category Master Table</SPAN></H2><P><SPAN>Create a database table to store available product categories.</SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Product Category Table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zprod_category { key client : abap.clnt not null; key category_id : abap.char(4) not null; category_name : abap.char(40); }</code></pre><P>This table acts as the master source for the value help.</P><P>&nbsp;</P><H2 id="toc-hId-441661300"><SPAN>Step 2: Create Product Table</SPAN></H2><P><SPAN>Create a product table containing:</SPAN></P><P>&nbsp;</P><UL><LI><SPAN>Product ID</SPAN></LI><LI><SPAN>Product Name</SPAN></LI><LI><SPAN>Category</SPAN></LI><LI><SPAN>Audit fields</SPAN></LI></UL><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Product Table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #ALLOWED define table zproduct_s { key client : abap.clnt not null; key product_id : abap.char(10) not null; product_name : abap.char(40); category : abap.char(4); created_by : abp_creation_user; created_at : abp_creation_tstmpl; last_changed_by : abp_locinst_lastchange_user; last_changed_at : abp_locinst_lastchange_tstmpl; local_last_changed_at : abp_locinst_lastchange_tstmpl; }</code></pre><P>This table stores the transactional product data.</P><P>&nbsp;</P><H2 id="toc-hId-245147795"><SPAN>Step 3: Create Value Help CDS View</SPAN></H2><P><SPAN>Create a CDS View Entity that exposes category information.</SPAN></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Category Value Help' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define view entity ZI_CategoryVH as select from zprod_category { key category_id as CategoryId, category_name as CategoryName }</code></pre><P class=""><SPAN>Explanation:</SPAN></P><UL><LI><SPAN>Provides the source for value help</SPAN></LI><LI><SPAN>Supplies category IDs and descriptions</SPAN></LI><LI><SPAN>Used by both RAP and Fiori Elements</SPAN></LI></UL><H2 id="toc-hId-48634290"><SPAN>Step 4: Create Root Interface View</SPAN></H2><P><SPAN>Create the RAP Root Interface View.</SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Product Interface View' @Metadata.ignorePropagatedAnnotations: true define root view entity ZI_PRODUCT_S as select from zproduct_s { key product_id as ProductId, product_name as ProductName, category as Category, @Semantics.user.createdBy: true created_by as CreatedBy, @Semantics.systemDateTime.createdAt: true created_at as CreatedAt, @Semantics.user.lastChangedBy: true last_changed_by as LastChangedBy, @Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt, @Semantics.systemDateTime.localInstanceLastChangedAt: true local_last_changed_at as LocalLastChangedAt }</code></pre><P>This view represents the Product business object.</P><P>&nbsp;</P><H2 id="toc-hId-199375142"><SPAN>Step 5: Create Projection View</SPAN></H2><P><SPAN>Create the Projection View exposed to Fiori.</SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Product Projection View' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true define root view entity ZC_PRODUCT provider contract transactional_query as projection on ZI_PRODUCT_S { key ProductId, ProductName, @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_CATEGORYVH', element: 'CategoryId' } }] Category, CreatedBy, CreatedAt, LastChangedBy, LastChangedAt, LocalLastChangedAt }</code></pre><P>This enables F4 Help on the Category field.<BR /><BR /></P><H2 id="toc-hId-2861637"><SPAN>Step 6: Create Abstract Entity for Action Parameters</SPAN></H2><P><SPAN>The action dialog parameters are defined using a CDS Abstract Entity.</SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Parameter for Set Category Action' define abstract entity ZA_SETCATEGORY_PARAM { @EndUserText.label: 'New Category' @Consumption.valueHelpDefinition: [{ entity : { name : 'ZI_CATEGORYVH', element : 'CategoryId' }, useForValidation: true }] NewCategory : abap.char(4); }</code></pre><P class=""><SPAN>Explanation:</SPAN></P><UL><LI><SPAN>Generates the popup field</SPAN></LI><LI><SPAN>Provides F4 help</SPAN></LI><LI><SPAN>Enables automatic validation</SPAN></LI></UL><P class=""><SPAN>Important:</SPAN></P><PRE><CODE><SPAN>useForValidation: true</SPAN></CODE></PRE><P><SPAN>prevents invalid values from being entered.</SPAN></P><P>&nbsp;</P><H2 id="toc-hId--193651868"><SPAN>Step 7: Create Interface Behavior Definition</SPAN></H2><P><SPAN>Define the RAP Action.</SPAN></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_product_s unique; strict ( 2 ); define behavior for ZI_PRODUCT_S alias Product persistent table zproduct_s lock master authorization master ( instance ) etag master LastChangedAt { create; update; delete; field ( readonly ) ProductId, CreatedBy, CreatedAt, LastChangedBy, LastChangedAt, LocalLastChangedAt; field ( mandatory ) ProductName; action setCategory parameter ZA_SETCATEGORY_PARAM result [1] $self; mapping for zproduct_s corresponding { ProductId = product_id; ProductName = product_name; Category = category; CreatedBy = created_by; CreatedAt = created_at; LastChangedBy = last_changed_by; LastChangedAt = last_changed_at; LocalLastChangedAt = local_last_changed_at; } }</code></pre><P>&nbsp;</P><H2 id="toc-hId--390165373"><SPAN>Step 8: Implement Action Logic</SPAN></H2><P class=""><SPAN>In the behavior implementation class:</SPAN></P><PRE><CODE><SPAN>METHOD setcategory.</SPAN></CODE></PRE><P class=""><SPAN>The implementation:</SPAN></P><OL><LI><SPAN>Reads selected products</SPAN></LI><LI><SPAN>Retrieves action parameter value</SPAN></LI><LI><SPAN>Updates category field</SPAN></LI><LI><SPAN>Returns updated records</SPAN></LI></OL><P class=""><SPAN>The selected category is obtained from:</SPAN></P><PRE><CODE><SPAN>keys<SPAN class="">[ ... ]</SPAN>-%param-NewCategory</SPAN></CODE></PRE><pre class="lia-code-sample language-abap"><code>CLASS lhc_Product DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR Product RESULT result. METHODS setCategory FOR MODIFY IMPORTING keys FOR ACTION Product~setCategory RESULT result. ENDCLASS. CLASS lhc_Product IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD setCategory. " Read current product records READ ENTITIES OF zi_product_s IN LOCAL MODE ENTITY Product FIELDS ( ProductId Category ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_products). " Update category field for each selected product MODIFY ENTITIES OF zi_product_s IN LOCAL MODE ENTITY Product UPDATE FIELDS ( Category ) WITH VALUE #( FOR ls_prod IN lt_products ( %tky = ls_prod-%tky Category = keys[ KEY entity %key = ls_prod-%key ]-%param-NewCategory ) ) REPORTED DATA(lt_reported). " Return updated records as action result READ ENTITIES OF zi_product_s IN LOCAL MODE ENTITY Product ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result). result = VALUE #( FOR ls_res IN lt_result ( %tky = ls_res-%tky %param = ls_res ) ). ENDMETHOD. ENDCLASS.</code></pre><H2 id="toc-hId--586678878">&nbsp;</H2><H2 id="toc-hId--783192383"><SPAN>Step 9: Create Projection Behavior Definition</SPAN></H2><P><SPAN>Expose the action:</SPAN></P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); define behavior for ZC_PRODUCT //alias &lt;alias_name&gt; { use create; use update; use delete; use action setCategory; }</code></pre><P>&nbsp;</P><H2 id="toc-hId--979705888"><SPAN>Step 10: Create Metadata Extension</SPAN></H2><P><SPAN>Add UI annotations.</SPAN></P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity ZC_PRODUCT with { <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.facet: [ { id: 'ProductInfo', type: #IDENTIFICATION_REFERENCE, label: 'Product Information', position: 10 } ] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [ { position: 10, label: 'Product ID' }, { type: #FOR_ACTION, dataAction: 'setCategory', label: 'Set Category', position: 40 } ] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 10 }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.selectionField: [{ position: 10 }] ProductId; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position: 20, label: 'Product Name' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 20 }] ProductName; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position: 30, label: 'Category' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 30 }] Category; }</code></pre><H2 id="toc-hId--1176219393">&nbsp;</H2><H2 id="toc-hId--1372732898"><SPAN>Step 11: Create Service Definition</SPAN></H2><P><SPAN>Expose both entities.</SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Product Service' define service ZSD_PRODUCT { expose ZC_PRODUCT as Product; expose ZI_CategoryVH as CategoryVH; }</code></pre><P class=""><SPAN>Important:</SPAN></P><P class=""><SPAN>Many developers forget to expose the value help entity.</SPAN></P><P class=""><SPAN>Without exposing:</SPAN></P><PRE><CODE><SPAN>ZI_CATEGORY_VH</SPAN></CODE></PRE><P><SPAN>the F4 dialog will not display any data.</SPAN></P><P>&nbsp;</P><H2 id="toc-hId--1569246403"><SPAN>Step 12: Create Service Binding</SPAN></H2><P class=""><SPAN>Create:</SPAN></P><PRE><CODE><SPAN>OData V4 - UI</SPAN></CODE></PRE><P class=""><SPAN>service binding.</SPAN></P><P><SPAN>Activate and Publish the service.</SPAN></P><P>&nbsp;</P><H2 id="toc-hId--1597576217"><SPAN>Result</SPAN></H2><P class=""><SPAN>Once the application is launched, users can:</SPAN></P><H3 id="toc-hId--2087492729"><SPAN>1. Select Products</SPAN></H3><PRE><CODE><SPAN>☑ P1001 Laptop ☑ P1002 Office Chair</SPAN></CODE></PRE><H3 id="toc-hId-2010961062"><SPAN>2. Click Set Category</SPAN></H3><PRE><CODE><SPAN><SPAN class="">[ Set Category ]</SPAN></SPAN></CODE></PRE><H3 id="toc-hId-1814447557"><SPAN>3. Action Dialog Opens</SPAN></H3><PRE><CODE><SPAN>Set Category New Category <SPAN class="">[ ]</SPAN> 🔍</SPAN></CODE></PRE><H3 id="toc-hId-1617934052"><SPAN>4. Open Value Help</SPAN></H3><PRE><CODE><SPAN>ELEC Electronics FURN Furniture CLTH Clothing FOOD Food &amp; Beverages</SPAN></CODE></PRE><H3 id="toc-hId-1421420547"><SPAN>5. Choose a Category</SPAN></H3><P class=""><SPAN>Example:</SPAN></P><PRE><CODE><SPAN>ELEC</SPAN></CODE></PRE><H3 id="toc-hId-1224907042"><SPAN>6. Confirm</SPAN></H3><PRE><CODE><SPAN>OK</SPAN></CODE></PRE><H3 id="toc-hId-1028393537"><SPAN>7. RAP Action Executes</SPAN></H3><P class=""><SPAN>Selected records are updated automatically.</SPAN></P><H3 id="toc-hId-831880032"><SPAN>8. List Refreshes</SPAN></H3><PRE><CODE><SPAN>P1001 Laptop ELEC P1002 Office Chair ELEC</SPAN></CODE></PRE><P class=""><SPAN>Success message:</SPAN></P><PRE><CODE><SPAN>Category updated successfully</SPAN></CODE></PRE><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="hasan123_0-1780903219380.png" style="width: 915px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/418834iC56C898503374F72/image-dimensions/915x514?v=v2" width="915" height="514" role="button" title="hasan123_0-1780903219380.png" alt="hasan123_0-1780903219380.png" /></span></P><H2 id="toc-hId-928769534">&nbsp;</H2><H2 id="toc-hId-900439720"><SPAN>Conclusion</SPAN></H2><P class=""><SPAN>In this blog, we explored how to implement RAP Action Parameters with Value Help using a CDS Abstract Entity.</SPAN></P><P class=""><SPAN>We discussed a real-world business scenario where users need to update product categories for multiple records while ensuring only valid values can be selected.</SPAN></P><P class=""><SPAN>By combining a CDS Value Help View, Abstract Entity, RAP Action, and Fiori Elements annotations, we were able to create a user-friendly action dialog with built-in F4 help and validation.</SPAN></P><P><SPAN>This approach provides a reusable pattern for implementing parameterized actions in RAP and can easily be extended for approval processes, status updates, assignment actions, and many other business scenarios while maintaining a clean and scalable design.</SPAN></P> 2026-06-09T09:39:02.577000+02:00 https://community.sap.com/t5/abap-blog-posts/using-domain-fixed-values-as-value-help-in-sap-rap-fiori-elements/ba-p/14398955 Using Domain Fixed Values as Value Help in SAP RAP Fiori Elements 2026-06-09T12:32:30.310000+02:00 Banasiddha_Patil https://community.sap.com/t5/user/viewprofilepage/user-id/1832133 <H2 id="toc-hId-1796172426">Introduction</H2><P>In SAP RAP Fiori applications, value help improves the user experience by providing predefined selectable values for filter and input fields.</P><P>One simple and efficient approach is to use <STRONG>ABAP Domain Fixed Values as value help instead of creating separate CDS value help entities.</STRONG></P><P>In this blog, we will see how to implement value help in Fiori Elements using Domain Fixed Values in SAP RAP.</P><P><FONT size="5"><STRONG>Business Requirement</STRONG></FONT></P><DIV><DIV><P>In a SAP RAP Fiori Elements application, users should be able to select predefined status values through Value Help instead of entering values manually.</P><P>The Status field should display fixed values maintained in an ABAP Domain, such as:</P><UL><LI>100 – Not Prepared</LI><LI>101 – In Preparation</LI><LI>102 – Completed<P>When the user opens the value help in the Fiori application, the system should automatically display these fixed domain values along with their descriptions.</P><P><FONT size="5">Step 1:&nbsp;&nbsp;Create a Domain with Fixed Values</FONT></P><P>Before building the Value Help CDS View, we first need to create an ABAP Domain that contains the fixed status values.</P><P>The domain will act as the source for the Value Help values displayed in the Fiori application.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="value_range.png" style="width: 997px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/411438iFAE5771F2DB9C106/image-size/large?v=v2&amp;px=999" role="button" title="value_range.png" alt="value_range.png" /></span></P><DIV><DIV>&nbsp;<DIV><FONT size="5"><SPAN>Step 2: Build the Value Help CDS View</SPAN></FONT><DIV><DIV><DIV><SPAN>SAP provides two standard tables that expose domain fixed values:</SPAN><DIV><SPAN>&nbsp; &nbsp;1. DD07L&nbsp; - Contains the fixed values of the domains.</SPAN><DIV><SPAN><SPAN>&nbsp; &nbsp;2. DD07T -&nbsp;<SPAN>Language-dependent description texts.</SPAN></SPAN></SPAN><DIV><DIV><DIV><SPAN>We join these two views and pass our domain name as a parameter. The join on <SPAN>`$session.system_language`<SPAN> is important — without it, you'd get one row per language in the system, causing duplicates in the dropdown.</SPAN></SPAN></SPAN></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></LI></UL></DIV></DIV><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Status' @Metadata.ignorePropagatedAnnotations: true define view entity ZI_BP_STATUS_VH as select from dd07l as values inner join dd07t as texts on values.domname = texts.domname and values.domvalue_l = texts.domvalue_l and texts.ddlanguage = $session.system_language { @ObjectModel.text.element: [ 'StatusText' ] key values.domvalue_l as StatusCode, texts.ddtext as StatusText } where values.domname = 'ZDO_VD_STATUS' and texts.domname = 'ZDO_VD_STATUS'.</code></pre><H1 id="toc-hId-1470576202">&nbsp;</H1><H1 id="toc-hId-1274062697">Step 3: Attach the Value Help to the Main CDS View</H1><P>Now go to the Projection View (Consumption CDS View) where the <CODE>Status</CODE> field is defined.</P><P>Use the annotation <CODE>@Consumption.valueHelpDefinition</CODE> to connect the field with the Value Help CDS View.</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Header' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true @Search.searchable: true @ObjectModel.semanticKey: [ 'ExternalId' ] define root view entity ZC_BP_REQHDR provider contract transactional_query as projection on ZI_BP_REQHDR { key RequestUuid, <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1692417">@search</a>.defaultSearchElement: true ExternalId, <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1692417">@search</a>.defaultSearchElement: true @ObjectModel.text.element: [ 'RequesterName' ] @Consumption.valueHelpDefinition: [{ entity: { name: 'I_BusinessPartnerVH', element: 'BusinessPartner' } }] RequesterId, _Requester.BusinessPartnerName as RequesterName, <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1692417">@search</a>.defaultSearchElement: true @ObjectModel.text.element: [ 'StatusText' ] @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_BP_STATUS_EDIT_VH', element: 'StatusCode' } }] Status, @ObjectModel.text.element: [ 'StatusText' ] @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_BP_STATUS_VH', element: 'StatusCode' } }] // Status as StatusFilter, _status.StatusText as StatusText, StatusCriticality, <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1692417">@search</a>.defaultSearchElement: true @ObjectModel.text.element: [ 'PriorityText' ] @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_BP_PRIORITY_VH', element: 'PriorityCode' } }] @Consumption.filter.selectionType: #SINGLE Priority, _priority.PriorityText as PriorityText, DeadlineDate, CancelReason, @ObjectModel.virtualElement: true @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_BP_CANCEL_REQUEST' virtual CancelReasonHidden : abap_boolean, LastChangedAt, _items : redirected to composition child ZC_BP_REQITEM, _Requester }</code></pre><H1 id="toc-hId-1077549192">&nbsp;</H1><H1 id="toc-hId-881035687">Value Help Configuration in RAP</H1><P>The below annotations are used to enable Value Help for the <CODE>Status</CODE> field in SAP RAP Fiori Elements.</P><H3 id="toc-hId-942687620"><SPAN><CODE>@Consumption.valueHelpDefinition</CODE></SPAN></H3><P>This annotation connects the field with a CDS Value Help entity.</P><H3 id="toc-hId-746174115"><SPAN><CODE>ZI_BP_STATUS_VH</CODE></SPAN></H3><P>This CDS View acts as the Value Help provider and supplies the fixed domain values.</P><H3 id="toc-hId-549660610"><SPAN><CODE>element: 'StatusCode'</CODE></SPAN></H3><P>Specifies which field from the Value Help entity should be returned to the main field.</P><H3 id="toc-hId-353147105"><SPAN><CODE>@ObjectModel.text.element</CODE></SPAN></H3><P>This annotation helps display the description text together with the code value in the Fiori UI.</P><P>With this configuration, users can select predefined status values directly from the Fiori Value Help dialog instead of entering values manually.</P><H1 id="toc-hId--101531838">&nbsp;</H1><H1 id="toc-hId-471694740">Step 4: Expose the CDS View in Service Definition</H1><P>After creating the Value Help CDS View and Projection View, the next step is to expose the CDS entity through a Service Definition.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Request Services' define service ZUI_BP_REQUEST { expose ZC_BP_REQHDR; expose ZC_BP_REQITEM; }</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="services.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/411444iCC465564CF9A09AC/image-size/large?v=v2&amp;px=999" role="button" title="services.png" alt="services.png" /></span></P><H1 id="toc-hId-275181235">&nbsp;</H1><H1 id="toc-hId-78667730">Output</H1><P>After activating the CDS Views, Service Definition, and Service Binding, the <CODE>Status</CODE> field in the Fiori Elements application will display a Value Help popup containing the fixed values maintained in the ABAP Domain.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="result.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/411445iFD6E753EEF3B933E/image-size/large?v=v2&amp;px=999" role="button" title="result.png" alt="result.png" /></span></P><H1 id="toc-hId--117845775">&nbsp;</H1><H1 id="toc-hId--314359280">Conclusion</H1><P>In this blog, we saw how to implement <STRONG>Value Help using ABAP Domain Fixed Values</STRONG> in a SAP RAP Fiori Elements application without creating custom database tables or complex Value Help logic.</P><P>By using the standard tables <CODE>DD07L</CODE> and <CODE>DD07T</CODE>, we were able to:</P><UL><LI>Expose domain fixed values directly in the Fiori UI</LI><LI>Display meaningful descriptions alongside technical keys</LI><LI>Improve the user experience with predefined selections</LI><LI>Avoid unnecessary custom Value Help entities</LI><LI>Build a lightweight and reusable solution</LI></UL><P>This approach is simple, efficient, and easy to maintain. Any changes made to the domain fixed values are automatically reflected in the Fiori application without additional development effort.</P><P>I hope this blog helps you implement Value Help efficiently in your SAP RAP applications.</P> 2026-06-09T12:32:30.310000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/simple-rap-managed-app-with-abstract-entity-popup/ba-p/14410119 Simple RAP managed app with Abstract entity popup. 2026-06-11T10:09:06.732000+02:00 Jaithera08 https://community.sap.com/t5/user/viewprofilepage/user-id/1416401 <P><SPAN>This post is to help those who are interested in learning SAP RAP application development. I have tried to cover the concepts of Validation, Determination, Actions, Draft handling, Authorization control, Late numbering and Side effects in this application.</SPAN></P><P>This is a application to manage student data covering the below concepts.</P><P><STRONG>Validation:</STRONG> It verifies the data before saving the same to the database. In our example, we will be checking if the marks entered is positive.</P><P><STRONG>Determination: </STRONG>It automatically calculates or derives field values. It gets executed before Validation in RAP lifecycle. In our example, we will be filling the "created by" and "percentage" fields using determination logic.&nbsp;</P><P><STRONG>Actions:</STRONG> It is used to achieve custom business operations that are beyond standard CRUD(Create, Read, Update, Delete) operations. In our example, we will be showing a popup for data update using action.</P><P><STRONG>Draft handling:</STRONG> It is used to save work-in-progress without committing changes to the persistent database table. In our example, draft handling would be enabled.</P><P><STRONG>Authorization control:</STRONG> It is used to control who is allowed to access the BO application. In our example, we will see how Global authorization is handled.</P><P><STRONG>Numbering:</STRONG> It is used to generate key of business object. In our example, we will see "Later numbering" concept implemented.</P><P><STRONG>Etag: </STRONG>It is used for Optimistic locking to provide concurrency control.&nbsp;</P><P><STRONG>Popup: </STRONG>Creating POPUP using Abstract entity.</P><P>Lets get started with the application.</P><P><STRONG>Step 1:&nbsp; Create database table in eclipse.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="s1.png" style="width: 486px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/418809iBEA46C24A6B79002/image-dimensions/486x290?v=v2" width="486" height="290" role="button" title="s1.png" alt="s1.png" /></span></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Student table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zjd_student_tbl { key client : abap.clnt not null; key id : sysuuid_x16 not null; name : abap.char(30); address : abap.char(50); marks : abap.int1; percentage : abap.int1; createdby : abap.char(15); createdon : abp_locinst_lastchange_tstmpl; }</code></pre><P>&nbsp;</P><P><STRONG>Step 2. Create Feeder class to populate data in the database table.</STRONG></P><pre class="lia-code-sample language-abap"><code>CLASS zcl_student_feeder DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES if_oo_adt_classrun . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_student_feeder IMPLEMENTATION. METHOD if_oo_adt_classrun~main. DATA: lv_count TYPE i VALUE 1, lv_name TYPE string, lv_timestamp TYPE timestampl. DELETE FROM zjd_student_tbl. WHILE lv_count &lt;= 5. GET TIME STAMP FIELD lv_timestamp. lv_name = |Test_| &amp;&amp; lv_count . DATA(lt_Data) = VALUE zjd_student_tbl( id = lv_count name = lv_name marks = '100' percentage = '100' address = 'Sion' createdby = sy-uname createdon = lv_timestamp ). INSERT zjd_student_tbl FROM <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1398638">@LT</a>_Data. CLEAR: lt_data, lv_name. lv_count += 1. ENDWHILE. ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P><STRONG>Step 3: Create&nbsp;Interface root CDS view entity.</STRONG></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface view for Student table' @Metadata.ignorePropagatedAnnotations: true define root view entity ZI_STUDENT_TBL as select from zjd_student_tbl { key id as Id, name as Name, address as Address, marks as Marks, percentage as Percentage, createdby as Createdby, @Semantics.systemDateTime.localInstanceLastChangedAt: true createdon as Createdon }</code></pre><P>&nbsp;</P><P><STRONG>Step 4: Create projection root CDS view entity on top of Interface root view.</STRONG></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection view on ZI_STUDENT_TBL' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true define root view entity ZC_STUDENT_TBL provider contract transactional_query as projection on ZI_STUDENT_TBL { key Id, Name, Address, Marks, Percentage, Createdby, @Semantics.systemDateTime.lastChangedAt: true Createdon }</code></pre><P>&nbsp;</P><P><STRONG>Step 5: Create Abstract entity. The same will be used for displaying popup to the user for editing the records.</STRONG></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Student POPUP abstract entity' @Metadata.allowExtensions: true define abstract entity ZA_STUD_EDIT { name : abap.char(30); address : abap.char(50); marks : abap.int1; }</code></pre><P>&nbsp;</P><P><STRONG>Step 6: Create database table "zjd_student_log" to demonstrate additional save with managed application.</STRONG></P><PRE><CODE>@EndUserText.label : 'Student log table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zjd_student_log { key client : abap.clnt not null; key id : sysuuid_x16 not null; name : abap.char(30); address : abap.char(50); marks : abap.int1; percentage : abap.int1; createdby : abap.char(15); createdon : abp_locinst_lastchange_tstmpl; changing_operation : abap.char(20); created_at : timestampl; }</CODE></PRE><P>&nbsp;</P><P><STRONG>Step 7: Create behavior definition for Interface view.</STRONG></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_student_tbl unique; strict ( 2 ); with draft; //For Draft define behavior for ZI_STUDENT_TBL persistent table zjd_student_tbl draft table zjd_student_d //For Draft with additional save// For additional save lock master total etag Createdon //For Draft authorization master ( global, instance ) etag master Createdon late numbering { create ( authorization : global ); update; delete; draft action Edit; //For Draft draft action Activate optimized; //For Draft draft action Discard; //For Draft draft action Resume; //For Draft draft determine action Prepare; //For Draft action updatestud parameter ZA_STUD_EDIT result [1] $self; determination setcreatedby on save { field id; create; } determination setPercentage on modify { field marks; update; } validation validatepercentage on save { field id; update; create; } side effects { field Marks affects field Percentage; } field ( readonly ) Id; field ( readonly ) Createdby; field ( readonly ) Createdon; field ( readonly ) Percentage; mapping for zjd_student_tbl { Id = id; Name = name; Address = address; Marks = marks; Percentage = percentage; Createdby = createdby; Createdon = createdon; } }</code></pre><P>&nbsp;</P><P><STRONG>Step 8: Create Behavior implementation class(Quick fix CTRL + 1) will create the class. Go to 'Local Types' tab to see the Handler and Saver class.</STRONG></P><P><STRONG>i. Late numbering code will be done in Saver class method "adjust numbers".</STRONG></P><pre class="lia-code-sample language-abap"><code>CLASS lsc_zi_student_tbl DEFINITION INHERITING FROM cl_abap_behavior_saver. PROTECTED SECTION. METHODS adjust_numbers REDEFINITION. METHODS save_modified REDEFINITION. ENDCLASS. CLASS lsc_zi_student_tbl IMPLEMENTATION. METHOD adjust_numbers. SELECT MAX( id ) FROM zjd_student_tbl INTO <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1407137">@DATA</a>(lv_id). IF sy-subrc EQ 0. lv_id += 1. ELSE. lv_id = 1. ENDIF. LOOP AT mapped-zi_student_tbl REFERENCE INTO DATA(ls_num). ls_num-&gt;Id = lv_id. ENDLOOP. ENDMETHOD. METHOD save_modified. DATA: lt_student_tbl TYPE STANDARD TABLE OF zjd_student_tbl, lt_student_log TYPE STANDARD TABLE OF zjd_student_log. IF create-zi_student_tbl IS NOT INITIAL. lt_student_log = CORRESPONDING #( create-zi_student_tbl ). LOOP AT lt_student_log ASSIGNING FIELD-SYMBOL(&lt;ls_student_log&gt;). &lt;ls_student_log&gt;-changing_operation = 'CREATE'. GET TIME STAMP FIELD &lt;ls_student_log&gt;-created_at. ENDLOOP. INSERT zjd_student_log FROM TABLE <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1398638">@LT</a>_student_log. ENDIF. ENDMETHOD. ENDCLASS.</code></pre><P><STRONG>ii.&nbsp; The following methods in Handler class will help us in Action, Determination, Validation, Authorization handling.</STRONG></P><pre class="lia-code-sample language-abap"><code>CLASS lhc_ZI_STUDENT_TBL DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zi_student_tbl RESULT result. METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION IMPORTING REQUEST requested_authorizations FOR zi_student_tbl RESULT result. METHODS updatestud FOR MODIFY IMPORTING keys FOR ACTION zi_student_tbl~updatestud RESULT result. METHODS setcreatedby FOR DETERMINE ON SAVE IMPORTING keys FOR zi_student_tbl~setcreatedby. METHODS validatepercentage FOR VALIDATE ON SAVE IMPORTING keys FOR zi_student_tbl~validatepercentage. METHODS setpercentage FOR DETERMINE ON MODIFY IMPORTING keys FOR zi_student_tbl~setpercentage. ENDCLASS.</code></pre><P><STRONG>Authorization: For demo, I have just restricted based on username.</STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD get_global_authorizations. IF requested_authorizations-%create EQ if_abap_behv=&gt;mk-on AND sy-uname EQ &lt;Your_user_name&gt;. result-%create = if_abap_behv=&gt;auth-unauthorized. ENDIF. ENDMETHOD.</code></pre><P><STRONG>Update student data based on popup.</STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD updatestud. DATA(lv_new_name) = keys[ 1 ]-%param-name. DATA(lv_new_address) = keys[ 1 ]-%param-address. DATA(lv_new_percentage) = keys[ 1 ]-%param-marks. MODIFY ENTITIES OF zi_student_tbl IN LOCAL MODE ENTITY zi_student_tbl UPDATE FIELDS ( Name Address Percentage ) WITH VALUE #( ( %tky = keys[ 1 ]-%tky Name = lv_new_name Address = lv_new_address Marks = lv_new_percentage Percentage = lv_new_percentage ) ). READ ENTITIES OF zi_student_tbl IN LOCAL MODE ENTITY zi_student_tbl ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_res). result = VALUE #( FOR &lt;lfs_data&gt; IN lt_res ( %tky = &lt;lfs_data&gt;-%tky %param = &lt;lfs_data&gt; ) ). ENDMETHOD.</code></pre><P>&nbsp;<STRONG>Determination to set 'Created by' and 'Percentage':&nbsp;</STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD setcreatedby. READ ENTITIES OF zi_student_tbl IN LOCAL MODE ENTITY zi_student_tbl ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result). MODIFY ENTITIES OF zi_student_tbl IN LOCAL MODE ENTITY zi_student_tbl UPDATE FIELDS ( Createdby ) WITH VALUE #( ( %tky = keys[ 1 ]-%tky Createdby = sy-uname ) ). ENDMETHOD.</code></pre><pre class="lia-code-sample language-abap"><code>METHOD setPercentage. READ ENTITIES OF zi_student_tbl IN LOCAL MODE ENTITY zi_student_tbl ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result). LOOP AT lt_result ASSIGNING FIELD-SYMBOL(&lt;ls_result&gt;). IF &lt;ls_result&gt;-Percentage NE &lt;ls_result&gt;-marks . MODIFY ENTITIES OF zi_student_tbl IN LOCAL MODE ENTITY zi_student_tbl UPDATE FIELDS ( Marks Percentage ) WITH VALUE #( ( %tky = keys[ 1 ]-%tky marks = lt_result[ 1 ]-Marks Percentage = lt_result[ 1 ]-Marks ) ). ENDIF. ENDLOOP. ENDMETHOD.</code></pre><P><STRONG>Validatation:</STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD validatepercentage. READ ENTITIES OF zi_student_tbl IN LOCAL MODE ENTITY zi_student_tbl ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result). IF lt_result[ 1 ]-Marks &lt; 20. APPEND VALUE #( %tky = lt_result[ 1 ]-%tky ) TO failed-zi_student_tbl. APPEND VALUE #( %tky = lt_result[ 1 ]-%tky %msg = new_message_with_text( severity = if_abap_behv_message=&gt;severity-error text = 'validation failed' ) ) TO reported-zi_student_tbl. ENDIF. ENDMETHOD.</code></pre><P><STRONG>Step 9: Create Metadata extension file for the projection view(By right clicking the projection view "ZC_STUDENT_TBL)".</STRONG></P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity ZC_STUDENT_TBL with { <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.facet: [{ id: 'StudentPage', purpose: #STANDARD, label: 'Student Overview', type: #IDENTIFICATION_REFERENCE }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ type: #FOR_ACTION, dataAction: 'updatestud', label: 'Update Student', position: 10 }] //this piece of code is for displaying popup <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 10, label: 'ID' }] Id; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position:10, label: 'Name' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 20, label: 'Name' }] Name; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position:20, label: 'Address' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 30, label: 'Address' }] Address; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position:30, label: 'Marks' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 40, label: 'Marks' }] Marks; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position:35, label: 'Percentage' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 45, label: 'Percentage' }] Percentage; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position:40, label: 'Created By' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 50, label: 'Created By' }] Createdby; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position:50, label: 'Created On' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 60, label: 'Created On' }] Createdon; }</code></pre><P>&nbsp;</P><P><STRONG>Step 10: Create Metadata extension file for Abstract view.</STRONG></P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity ZA_STUD_EDIT with { <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position: 10 }] name; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position: 20 }] address; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ position: 30 }] marks; }</code></pre><P>&nbsp;</P><P><STRONG>Step 11: Behavior definition for consumption view.</STRONG></P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); use draft; //For Draft use side effects; define behavior for ZC_STUDENT_TBL //alias &lt;alias_name&gt; { use create; use update; use delete; use action updatestud; use action Activate; //For Draft use action Discard; //For Draft use action Edit; //For Draft use action Prepare; //For Draft use action Resume; //For Draft }</code></pre><P>&nbsp;</P><P><STRONG>Step 12: Create Service Definition.</STRONG></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Student service definition' define service ZSD_STUDENT_DEMO { expose ZC_STUDENT_TBL; }</code></pre><P>&nbsp;</P><P><STRONG>Step 13: Create Service Binding.</STRONG></P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-06-01 170516.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/418816i492830404347D3C0/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-06-01 170516.png" alt="Screenshot 2026-06-01 170516.png" /></span></STRONG></P><P><SPAN>After all the above steps are performed. The below components should be ready.</SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-06-01 170629.png" style="width: 434px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/418817iBC5C3572B5A4C7DA/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-06-01 170629.png" alt="Screenshot 2026-06-01 170629.png" /></span></SPAN></P><P>&nbsp;</P><P><STRONG>Step 14:&nbsp;Publish and preview the application.</STRONG></P><P><STRONG>Output:&nbsp;<SPAN>Line item display.</SPAN></STRONG></P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-06-01 170811.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/418818i715C45C60C1E751B/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-06-01 170811.png" alt="Screenshot 2026-06-01 170811.png" /></span></STRONG></P><P><STRONG>Popup to update data</STRONG></P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="popup.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/418819i238713751773986D/image-size/large?v=v2&amp;px=999" role="button" title="popup.png" alt="popup.png" /></span></STRONG></P><P class=""><SPAN>In this blog, we explored the development of a SAP RAP application by implementing several important RAP concepts within a Student Data Management scenario. We covered how validations ensure data integrity, determinations automate field derivation, actions enable custom business operations, and draft handling manages work-in-progress data. We also demonstrated global authorization control, late numbering for key generation, ETag-based optimistic locking for concurrency control, and popup implementation using abstract entities.</SPAN></P><P class=""><SPAN>By combining these features, SAP RAP enables developers to build modern, cloud-ready, and enterprise-grade applications with less custom code while following SAP's clean-core principles. Understanding these concepts is essential for designing robust and scalable RAP applications in SAP S/4HANA and SAP BTP environments.</SPAN></P><P><SPAN>I hope this blog provides a practical starting point for your RAP learning journey.</SPAN></P><P>Keep exploring! Keep learning<SPAN>&nbsp;<span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></SPAN></P> 2026-06-11T10:09:06.732000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/etag-entity-tag-in-rap/ba-p/14415434 Etag(Entity Tag) in RAP 2026-06-11T10:17:11.558000+02:00 Jaithera08 https://community.sap.com/t5/user/viewprofilepage/user-id/1416401 <P>Etag is used to achieve Optimistic concurrency control.</P><P>Now comes the question : What is Optimistic Concurrency?</P><P>Optimistic concurrency control ensures data consistency by preventing lost updates when multiple users read the same data simultaneously and attempt to modify it.</P><P><STRONG>Scenario without ETag: </STRONG></P><P>User&nbsp; A&nbsp; and user B reading the same record from database</P><P>User A update the data and save the record in database.</P><P>User B without knowing data has been already updated, updates the same record and saves the data , at this time data saved by user A will be overwritten by data of user B. &nbsp;This will result in data inconsistency. To handle such scenario we will go for ETag (Entity tag) .<BR /><BR /></P><P><STRONG>Scenario with ETag:</STRONG></P><P>For implementing ETag,&nbsp; we will include a ETag field which will uniquely&nbsp; identify each updating of the record whenever its updated.</P><P>User&nbsp; A&nbsp; and user B reading the same record from database. Both get&nbsp;ETag value E1.</P><P>User A update the data and save the record in database. Sets the&nbsp;ETag value to E2.</P><P>User B without knowing data has been already updated, tries to update the same record but this time the Etag value in database is E2 which is not matching with ETag value in E1. So, update fails for User B with error code 412. This will ensure data consistency.</P><P>Steps to implement ETag in RAP:</P><P><STRONG>Step1: Take one field of the database table with datatype&nbsp;‘abp_locinst_lastchange_tstmpl’. This would be our etag field.</STRONG></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Student table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zjd_student_tbl { key client : abap.clnt not null; key id : sysuuid_x16 not null; name : abap.char(30); address : abap.char(50); marks : abap.int1; percentage : abap.int1; createdby : abap.char(15); createdon : abp_locinst_lastchange_tstmpl; }</code></pre><P><STRONG>Step2: Add “etag master Createdon” in behaviour definition</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Jaithera08_0-1781085565720.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/419978i938B581A25C66513/image-size/medium?v=v2&amp;px=400" role="button" title="Jaithera08_0-1781085565720.png" alt="Jaithera08_0-1781085565720.png" /></span></P><P><STRONG>Step3: Make the field read only.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Jaithera08_1-1781085595897.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/419979i5CC5C28138F168AB/image-size/medium?v=v2&amp;px=400" role="button" title="Jaithera08_1-1781085595897.png" alt="Jaithera08_1-1781085595897.png" /></span></P><P>Once the above three steps are done, RAP ensures optimistic concurrency control.</P><P>By leveraging ETags, SAP RAP provides an efficient and scalable approach to concurrency management. Understanding and implementing ETags is essential for building robust, reliable, and enterprise-ready RAP applications that can safely handle concurrent user updates.</P><P>Keep learning! Keep exploring <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></P> 2026-06-11T10:17:11.558000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/abap-development-tools-for-visual-studio-code-patch-1-0-1-released/ba-p/14416731 ABAP development tools for Visual Studio Code: Patch 1.0.1 released 2026-06-12T07:41:59.469000+02:00 thomasalexander_ritter https://community.sap.com/t5/user/viewprofilepage/user-id/185696 <DIV><H1 id="toc-hId-1688269120"><SPAN>What happened since the first release?</SPAN></H1><P><SPAN>First of all, thanks to all the ABAP developers who have already tried out the new <A href="https://marketplace.visualstudio.com/items?itemName=SAPSE.adt-vscode" target="_self" rel="nofollow noopener noreferrer">extension</A>! You provided a lot of positive feedback &amp; constructive criticism over the last two weeks and can be assured that we read every single one. When looking at the overall feedback three things stand out:</SPAN></P><P><SPAN>1.</SPAN><SPAN>&nbsp;<STRONG>Trouble with setting up system connections.</STRONG> Because the extension ships with a new way for configuring system destinations, some of you had trouble configuring ABAP system access. We have already published <A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-for-visual-studio-code/establishing-system-connection-8e91041894104c5b8567274954dbed1a" target="_self" rel="noopener noreferrer">improved documentation</A> and we are also looking into how we can further improve the usability of these user flows in the upcoming releases. </SPAN></P><P><SPAN>2.</SPAN><SPAN><STRONG>&nbsp;Missing support for Windows ARM hardware</STRONG>. We added this to our backlog and already made some progress. However, further testing needs to be done until we can release it to the public.</SPAN></P><P><SPAN>3.</SPAN><SPAN><STRONG>&nbsp;Missing support for RFC + password</STRONG>. We communicated openly and proactively across multiple channels that RFC + password support would arrive in the next major release, and we were aware of its importance. Even so, we were still surprised by how many users are relying on classic passwords. That’s why we reacted quickly. Since the mandatory security review of such a critical change was already done, we are pleased to add support for passwords via a patch.</SPAN></P><P><SPAN>We also received general feedback that the community would appreciate more how-to guides. Here are some links for getting started and learning more about the extension. We are planning to provide more guides in the future. </SPAN></P><UL><LI><SPAN><A href="https://github.com/SAP-samples/abap-platform-rap130" target="_self" rel="nofollow noopener noreferrer">RAP130 tutorial</A>. A tutorial that walks you through using the ADT MCP server together with an AI agent.</SPAN></LI><LI><SPAN><A href="https://software-heroes.com/en/blog/abap-tools-vs-code-agentic-ai-en" target="_self" rel="nofollow noopener noreferrer">Setup guide how to get started with GitHub Copilot</A>. <A href="https://community.sap.com/t5/user/viewprofilepage/user-id/488953" target="_self">Björn Schulz</A> published a nice guide for configuring the initial setup.</SPAN></LI><LI><SPAN><A href="https://www.youtube.com/watch?v=e2LRPHO8A0E" target="_self" rel="nofollow noopener noreferrer">Podcast on the ADT for VS Code extension</A>. Rich Heilman did a podcast with some members of the ADT team. It covers a lot of technical aspects of the extension but also provides some insights into the plan for the next releases. </SPAN></LI></UL><H1 id="toc-hId-1491755615"><SPAN>What gets shipped with this release?</SPAN></H1><H2 id="toc-hId-1424324829"><SPAN>RFC System logon via password</SPAN></H2><P><SPAN>You can create RFC destinations with password access.</SPAN></P><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="rfc_password.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/420917i57E7F4EF2CB206EB/image-size/medium?v=v2&amp;px=400" role="button" title="rfc_password.png" alt="rfc_password.png" /></span></DIV><H2 id="toc-hId-1227811324"><SPAN>AGENTS.md template</SPAN></H2><P><SPAN>The documentation already provides a template for an <A href="https://agents.md/" target="_self" rel="nofollow noopener noreferrer">AGENTS.md</A>&nbsp;file. With the update the same file gets shipped with the VS Code extension. This makes it easier to get started. We still recommend duplicating the template and adding your own company/team/project-specific instructions. The more time you spend on onboarding the agent into your project the better results it will produce.</SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="copilot_agent.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/420927iB24EC69943519D27/image-size/medium?v=v2&amp;px=400" role="button" title="copilot_agent.png" alt="copilot_agent.png" /></span></SPAN></P><H1 id="toc-hId-902215100"><SPAN>What's next?</SPAN></H1><P><SPAN>The next release is currently planned for next month. The planned features remain unchanged from the previous blog post:</SPAN></P><UL><LI><SPAN>Support of more core development object types: RAP behavior extensions, DDIC tables, DDIC structures, DDIC enqueue objects, DDIC type groups, CDS entity buffers, programs/includes, function groups and modules, ...</SPAN></LI><LI><SPAN>MCP tools: Unified diff tool for transports, ABAP test cockpit tools, …</SPAN></LI></UL><P><SPAN>Follow the <A href="https://help.sap.com/docs/abap-cross-product/roadmap-info/abap-cloud-roadmap-information" target="_self" rel="noopener noreferrer">ABAP Cloud Roadmap</A> if you are interested in knowing what comes next.</SPAN></P><H2 id="toc-hId-834784314">Provide feedback and influence our product backlog!</H2><P><SPAN>We welcome your feedback! Feel free to use the comment section or <A href="https://influence.sap.com/sap/ino/#/campaign/2911" target="_self" rel="noopener noreferrer">create a customer influence request</A> to let us know which features you are missing the most. Your input will help us prioritize the feature backlog for the next releases.</SPAN></P></DIV> 2026-06-12T07:41:59.469000+02:00 https://community.sap.com/t5/abap-blog-posts/unmanaged-rap-application-for-excel-file-upload-and-cds-custom-entity-with/ba-p/14418230 Unmanaged RAP application for Excel File Upload and CDS Custom Entity with Unmanaged Query Implement 2026-06-14T15:02:55.897000+02:00 IgorPrudnecionok https://community.sap.com/t5/user/viewprofilepage/user-id/479156 <H2 id="toc-hId-1817406615">Intro</H2><P>Nowadays, finding information is easier than ever. Although, the experience still remains the key differentiator. And that’s what I would like to share through this blog – my experience in implementing the Excel File Upload in a RAP application in BTP ABAP environment. RAP application was implemented with an unmanaged behavior, supporting also the editing of multiple items at the same time. &nbsp;Additionally, the data model is based on custom entities with unmanaged queries.</P><P>&nbsp;</P><H2 id="toc-hId-1620893110">Use-case</H2><P>RAP application for maintaining Bonus Plans; allow the editing of multiple records at the same time; allow mass upload from an Excel file.</P><P>&nbsp;</P><H2 id="toc-hId-1424379605">Data Model</H2><P>The data model for my scenario is based on 2 DB tables (ZIP_BONUS_PLAN and ZIP_BP_UPLOAD) and 3 CDS Custom Entities (ZCE_BP_SINGLETON, ZCE_BONUS_PLAN and ZCE_BP_UPLOAD). Refer to <A href="https://help.sap.com/docs/abap-cloud/abap-data-models/cds-custom-entities" target="_blank" rel="noopener noreferrer">Custom Entities | SAP Help Portal</A>.</P><P>The root entity is a singleton entity aiming to implement multi-inline-edit. Refer to <A href="https://help.sap.com/docs/abap-cloud/abap-rap/developing-transactional-apps-with-multi-inline-edit-capabilities" target="_blank" rel="noopener noreferrer">Developing Transactional Apps with Multi-Inline-Edit Capabilities | SAP Help Portal</A>.</P><P>All 3 Custom Entities implement the retrieval logic in the same ABAP class ZCL_BP_UNMNG_QUERY.</P><P>In the attachment you can find the files containing the most interesting parts of coding:</P><UL><LI><EM>2 Database Tables</EM></LI><LI><EM>3 CDS views</EM></LI><LI><EM>Unmanage Query implementation</EM></LI><LI><EM>Processing logic</EM></LI><LI><EM>RAP Behavior Definition</EM></LI><LI><EM>RAP Behavior Implementation</EM></LI></UL><P>&nbsp;</P><H3 id="toc-hId-1356948819">Implementation of Excel Upload</H3><P>The implementation of File Upload is based on large objects (LOBs), providing end users the option to incorporate external files when editing entity instances. To make it work with Excel files you have to implement XCO_CP_XLSX. Refer to&nbsp;<A href="https://help.sap.com/docs/abap-cloud/abap-rap/working-with-large-objects" target="_blank" rel="noopener noreferrer">Working with Large Objects | SAP Help Portal</A> and <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/xlsx" target="_blank" rel="noopener noreferrer">XLSX | SAP Help Portal</A>.</P><P>&nbsp;</P><H2 id="toc-hId-1031352595">Service Binding</H2><P>For Service Binding I’ve chosen “OData V2 – UI” because if I go for “OData V4 – UI” some parts are missing and I get the following notification in the Service Binding itself “oData V4 services with no draft capability will be primarily READ-ONLY”.</P><P>&nbsp;</P><H2 id="toc-hId-834839090">Fiori UI Preview</H2><P>The preview starts with the root entity ZCE_BP_SINGLETON for which the if_rap_query_provider builds a dummy record during runtime:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_0-1781376826905.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421480i272550994B08574D/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_0-1781376826905.png" alt="IgorPrudnecionok_0-1781376826905.png" /></span></P><P>&nbsp;</P><P>Here I have implemented a custom action “Drop all Data” to be able to clear my demo tables.</P><P>When I select the singleton record, the application navigates to the page with Bonus Plan Grid:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_1-1781376826909.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421482i254941564632BC45/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_1-1781376826909.png" alt="IgorPrudnecionok_1-1781376826909.png" /></span></P><P>&nbsp;</P><P>Here we start with the “Mass Upload” functionality, by executing the “Create New Upload Template”. As a result, a new record is created in the table ZIP_BP_UPLOAD, containing a template file attachment.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_2-1781376826911.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421481i27F7F3147BC0F991/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_2-1781376826911.png" alt="IgorPrudnecionok_2-1781376826911.png" /></span></P><P>&nbsp;</P><P>When you enter into the details of this record, you can download the template:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_3-1781376826913.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421483i1EC99E8DE655585C/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_3-1781376826913.png" alt="IgorPrudnecionok_3-1781376826913.png" /></span></P><P>&nbsp;</P><P>Maintain the Excel file data and upload it back:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_4-1781376826916.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421484iDDAF0D9A5A92764B/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_4-1781376826916.png" alt="IgorPrudnecionok_4-1781376826916.png" /></span></P><P>&nbsp;</P><P>Now, the file is uploaded and the record is updated:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_5-1781376826918.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421485i7244A01F8085737D/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_5-1781376826918.png" alt="IgorPrudnecionok_5-1781376826918.png" /></span></P><P>&nbsp;</P><P>Once the file is uploaded, it can be processed:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_6-1781376826922.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421486iD6D8060351905BA2/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_6-1781376826922.png" alt="IgorPrudnecionok_6-1781376826922.png" /></span></P><P>&nbsp;</P><P>Successfully processed file will create records in the table ZIP_BONUS_PLAN. The UI allows editing multiple records:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="IgorPrudnecionok_7-1781376826924.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/421487i66A255B19B8A0E19/image-size/medium?v=v2&amp;px=400" role="button" title="IgorPrudnecionok_7-1781376826924.png" alt="IgorPrudnecionok_7-1781376826924.png" /></span></P><P>&nbsp;</P><H2 id="toc-hId-638325585">Final words</H2><P><SPAN>In an era where AI helps us find answers faster than ever, the craft still lies in how we turn those answers into working solutions. This Excel upload journey—unmanaged behavior for bulk editing, custom entities with unmanaged queries—was my way of shaping RAP to the problem, not the other way around. If my experience saves you a few hours, a few pitfalls, or inspires a cleaner design, then sharing it was worth it. The rest is practice—and I’m looking forward to hearing about your experience in the comments below.</SPAN></P> 2026-06-14T15:02:55.897000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/building-an-inventory-system-with-rap-on-btp-trial-cds-views-explained/ba-p/14420890 Building an Inventory System with RAP on BTP Trial — CDS Views Explained 2026-06-17T11:55:35.487000+02:00 rmz01 https://community.sap.com/t5/user/viewprofilepage/user-id/725901 <H3 id="toc-hId-1947180479">Introduction</H3><P class="">When people talk about RAP (RESTful ABAP Programming Model), the conversation usually jumps straight to behavior definitions, actions, and determinations. The CDS layer gets a brief mention and everyone moves on.</P><P class="">That's a mistake.</P><P class="">The CDS view architecture is the contract your entire application is built on. Get it right and everything above it — behaviors, OData services, Fiori UIs — falls into place naturally. Get it wrong and you'll spend hours debugging problems that were actually design issues.</P><P class="">In this post I'll walk through the complete CDS layer of a Mini Inventory &amp; Stock Movement System I built on SAP BTP Trial, covering four entities across three business objects. I'll explain the design decisions, not just the code.</P><H3 id="toc-hId-1750666974">The System at a Glance</H3><P class="">The application manages warehouse stock movements. Three business objects cover the full domain:</P><DIV class="">Business Object Interface View Database Table <TABLE><TBODY><TR><TD>Material Master</TD><TD>ZI_MiniMM_Material</TD><TD>ZRMZ_MM_MATERIAL</TD></TR><TR><TD>Storage Location</TD><TD>ZI_MiniMM_Storage</TD><TD>ZRMZ_MM_STORAGE</TD></TR><TR><TD>Stock Document (Header)</TD><TD>ZI_MiniMM_StockDocument</TD><TD>ZRMZ_MM_DOC_HDR</TD></TR><TR><TD>Stock Document (Item)</TD><TD>ZI_MiniMM_StockDocumentItem</TD><TD>ZRMZ_MM_DOC_ITEM</TD></TR><TR><TD>Current Stock (read-only)</TD><TD>ZI_MiniMM_CurrentStock</TD><TD>ZRMZ_MM_STOCK</TD></TR></TBODY></TABLE></DIV><P class="">Each entity has a matching projection view (<CODE>ZC_</CODE> prefix) used to expose the OData service.</P><HR /><H3 id="toc-hId-1554153469">The Two-Layer Architecture</H3><P class="">RAP enforces a clean separation between two CDS view types. Understanding why this separation exists is more important than understanding the syntax.</P><P class=""><STRONG>Interface views (<CODE>ZI_</CODE>)</STRONG> are the authoritative source of truth. They sit directly on the database tables and carry everything the framework needs to understand your data: semantic annotations, associations, compositions, and quantity/currency references. These views should be stable — changing them has downstream impact.</P><P class=""><STRONG>Projection views (<CODE>ZC_</CODE>)</STRONG> consume the interface views and add presentation concerns: the <CODE>provider contract</CODE>, <CODE>@Metadata.allowExtensions</CODE> for metadata extensions, and the redirection of compositions so that OData navigation points to the correct projection layer. Projection views can evolve more freely.</P><P class="">A useful mental model: <CODE>ZI_</CODE> is the API contract, <CODE>ZC_</CODE> is the UI adapter.</P><HR /><H3 id="toc-hId-1357639964">Composition: Stock Document Header and Item</H3><P class="">The most important structural relationship in this model is the header-item composition between <CODE>ZI_MiniMM_StockDocument</CODE> and <CODE>ZI_MiniMM_StockDocumentItem</CODE>.</P><P class="">In the header (root entity):</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV><DIV class="">abap</DIV><DIV class=""><PRE><CODE><SPAN>composition [<SPAN class="">0</SPAN><SPAN class="">.</SPAN><SPAN class="">.</SPAN>*] <SPAN class="">of</SPAN> ZI_MiniMM_StockDocumentItem</SPAN><SPAN> <SPAN class="">as</SPAN> _Items</SPAN></CODE></PRE></DIV></DIV><P class="">In the item (child entity):</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV><DIV class="">abap</DIV><DIV class=""><PRE><CODE><SPAN><SPAN class="">association</SPAN> <SPAN class="">to</SPAN> parent ZI_MiniMM_StockDocument</SPAN><SPAN> <SPAN class="">as</SPAN> _Header</SPAN><SPAN> <SPAN class="">on</SPAN> $projection<SPAN class="">.</SPAN>DocumentID <SPAN class="">=</SPAN> _Header<SPAN class="">.</SPAN>DocumentID</SPAN></CODE></PRE></DIV></DIV><P class="">The <CODE>[0..*]</CODE> cardinality means a document can have zero or more items — appropriate here since a newly created document starts empty. The parent association on the item side creates the back-navigation link.</P><P class="">Why does this matter? Composition in RAP is not just a modelling convenience. It has runtime implications: the managed runtime treats the composition tree as a single transactional unit. When you delete a header, items are automatically deleted. When you lock a header for editing, the entire tree is locked. This is the correct behaviour for a stock document.</P><HR /><H3 id="toc-hId-1161126459">Associations: Two Aliases, One Target View</H3><P class="">The item view introduces an interesting challenge: a stock movement can have both a source storage location and a target storage location, and both must be validated against the same <CODE>ZI_MiniMM_Storage</CODE> entity.</P><P class="">The solution is two separate associations pointing to the same underlying view:</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV><DIV class="">abap</DIV><DIV class=""><PRE><CODE><SPAN><SPAN class="">association</SPAN> [<SPAN class="">1</SPAN><SPAN class="">.</SPAN><SPAN class="">.</SPAN><SPAN class="">1</SPAN>] <SPAN class="">to</SPAN> ZI_MiniMM_Material <SPAN class="">as</SPAN> _Material</SPAN><SPAN> <SPAN class="">on</SPAN> $projection<SPAN class="">.</SPAN>MaterialID <SPAN class="">=</SPAN> _Material<SPAN class="">.</SPAN>MaterialID</SPAN> <SPAN><SPAN class="">association</SPAN> [<SPAN class="">0</SPAN><SPAN class="">.</SPAN><SPAN class="">.</SPAN><SPAN class="">1</SPAN>] <SPAN class="">to</SPAN> ZI_MiniMM_Storage <SPAN class="">as</SPAN> _SourceStorage</SPAN><SPAN> <SPAN class="">on</SPAN> $projection<SPAN class="">.</SPAN>SourceStorage <SPAN class="">=</SPAN> _SourceStorage<SPAN class="">.</SPAN>StorageID</SPAN> <SPAN><SPAN class="">association</SPAN> [<SPAN class="">0</SPAN><SPAN class="">.</SPAN><SPAN class="">.</SPAN><SPAN class="">1</SPAN>] <SPAN class="">to</SPAN> ZI_MiniMM_Storage <SPAN class="">as</SPAN> _TargetStorage</SPAN><SPAN> <SPAN class="">on</SPAN> $projection<SPAN class="">.</SPAN>TargetStorage <SPAN class="">=</SPAN> _TargetStorage<SPAN class="">.</SPAN>StorageID</SPAN></CODE></PRE></DIV></DIV><P class="">A few design points worth noting here.</P><P class="">The <CODE>_Material</CODE> association is <CODE>[1..1]</CODE> — every item line must reference a valid material. <CODE>_SourceStorage</CODE> and <CODE>_TargetStorage</CODE> are both <CODE>[0..1]</CODE> because not every movement type uses both. A goods receipt (GR) has no source; a goods issue (GI) has no target. Making both optional at the CDS level correctly reflects the business reality.</P><P class="">The alias names (<CODE>_SourceStorage</CODE>, <CODE>_TargetStorage</CODE>) are different even though both navigate to <CODE>ZI_MiniMM_Storage</CODE>. This is required by the framework — CDS does not allow two associations with identical names on the same view, even if the target differs.</P><HR /><H3 id="toc-hId-964612954">Semantic Annotations: Free Administrative Data</H3><P class="">RAP's managed runtime will auto-populate administrative fields if you annotate them correctly. No custom code required.</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV><DIV class="">abap</DIV><DIV class=""><PRE><CODE><SPAN>@Semantics<SPAN class="">.</SPAN><SPAN class="">user</SPAN><SPAN class="">.</SPAN>createdBy<SPAN class="">:</SPAN> true</SPAN><SPAN>created_by <SPAN class="">as</SPAN> CreatedBy<SPAN class="">,</SPAN> </SPAN> <SPAN>@Semantics<SPAN class="">.</SPAN>systemDateTime<SPAN class="">.</SPAN>createdAt<SPAN class="">:</SPAN> true</SPAN><SPAN>created_at <SPAN class="">as</SPAN> CreatedAt<SPAN class="">,</SPAN> </SPAN> <SPAN>@Semantics<SPAN class="">.</SPAN><SPAN class="">user</SPAN><SPAN class="">.</SPAN>lastChangedBy<SPAN class="">:</SPAN> true</SPAN><SPAN>last_changed_by <SPAN class="">as</SPAN> ChangedBy<SPAN class="">,</SPAN> </SPAN> <SPAN>@Semantics<SPAN class="">.</SPAN>systemDateTime<SPAN class="">.</SPAN>lastChangedAt<SPAN class="">:</SPAN> true</SPAN><SPAN>last_changed_at <SPAN class="">as</SPAN> ChangedAt</SPAN></CODE></PRE></DIV></DIV><P class="">These annotations go on the <STRONG>interface view</STRONG>, not the projection. The framework reads them at runtime and fills the mapped database columns on every create and update operation.</P><P class="">The only requirement is that the underlying database table columns have compatible data types (<CODE>SYUNAME</CODE>, <CODE>TIMESTAMPL</CODE> respectively) and that you declare <CODE>with_draft</CODE> or standard managed behaviour in the behavior definition. If those conditions are met, this is genuinely zero-effort audit trail data.</P><HR /><H3 id="toc-hId-768099449">Projection Views: What Most Tutorials Skip</H3><P class="">Every interface view has a corresponding projection view. The projection view adds three things that the interface view cannot provide: the <CODE>provider contract transactional_query</CODE> declaration (required for OData V4 exposure), <CODE>@Metadata.allowExtensions: true</CODE> (required for metadata extensions to attach), and — crucially — the composition redirect.</P><P class="">The composition redirect is the step that most beginner tutorials either skip or explain poorly.</P><P class="">In the projection header:</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV><DIV class="">abap</DIV><DIV class=""><PRE><CODE><SPAN>_Items <SPAN class="">:</SPAN> redirected <SPAN class="">to</SPAN> composition child ZC_MiniMM_StockDocumentItem</SPAN></CODE></PRE></DIV></DIV><P class="">In the projection item:</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV><DIV class="">abap</DIV><DIV class=""><PRE><CODE><SPAN>_Header <SPAN class="">:</SPAN> redirected <SPAN class="">to</SPAN> parent ZC_MiniMM_StockDocument</SPAN></CODE></PRE></DIV></DIV><P class="">Without this redirect, OData navigation from the header to its items would traverse back to the <STRONG>interface</STRONG> entities (<CODE>ZI_</CODE>), not the projection entities (<CODE>ZC_</CODE>). The service binding would still activate, but navigation in Fiori would either fail silently or return data without the metadata annotations your UI depends on.</P><HR /><H3 id="toc-hId-571585944">Value Help Annotations</H3><P class="">The final piece of the CDS layer is the value help definitions, which live in the <STRONG>metadata extension</STRONG> of the item projection view.</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class="">&nbsp;</DIV><DIV class=""><DIV class="">&nbsp;</DIV></DIV></DIV></DIV></DIV><DIV class="">abap</DIV><DIV class=""><PRE><CODE><SPAN>@Consumption<SPAN class="">.</SPAN>valueHelpDefinition<SPAN class="">:</SPAN> [<SPAN class="">{</SPAN> </SPAN><SPAN> entity<SPAN class="">.</SPAN><SPAN class="">name</SPAN><SPAN class="">:</SPAN> <SPAN class="">'ZC_MiniMM_Material'</SPAN><SPAN class="">,</SPAN> </SPAN><SPAN> entity<SPAN class="">.</SPAN>element<SPAN class="">:</SPAN> <SPAN class="">'MaterialID'</SPAN> <SPAN class="">}</SPAN>]</SPAN><SPAN>MaterialID;</SPAN> <SPAN>@Consumption<SPAN class="">.</SPAN>valueHelpDefinition<SPAN class="">:</SPAN> [<SPAN class="">{</SPAN> </SPAN><SPAN> entity<SPAN class="">.</SPAN><SPAN class="">name</SPAN><SPAN class="">:</SPAN> <SPAN class="">'ZC_MiniMM_Storage'</SPAN><SPAN class="">,</SPAN> </SPAN><SPAN> entity<SPAN class="">.</SPAN>element<SPAN class="">:</SPAN> <SPAN class="">'StorageID'</SPAN> <SPAN class="">}</SPAN>]</SPAN><SPAN>SourceStorage;</SPAN> <SPAN>@Consumption<SPAN class="">.</SPAN>valueHelpDefinition<SPAN class="">:</SPAN> [<SPAN class="">{</SPAN> </SPAN><SPAN> entity<SPAN class="">.</SPAN><SPAN class="">name</SPAN><SPAN class="">:</SPAN> <SPAN class="">'ZC_MiniMM_Storage'</SPAN><SPAN class="">,</SPAN> </SPAN><SPAN> entity<SPAN class="">.</SPAN>element<SPAN class="">:</SPAN> <SPAN class="">'StorageID'</SPAN> <SPAN class="">}</SPAN>]</SPAN><SPAN>TargetStorage;</SPAN></CODE></PRE></DIV></DIV><P class="">The annotation always points to the <STRONG>projection</STRONG> view (<CODE>ZC_</CODE>), not the interface view. This is because the value help dropdown in Fiori is itself an OData call, and OData services are generated from projection views. Pointing at <CODE>ZI_</CODE> would fail at activation or produce no results.</P><P class="">Both <CODE>SourceStorage</CODE> and <CODE>TargetStorage</CODE> reference the same <CODE>ZC_MiniMM_Storage</CODE> projection — same entity, same element, different field. The framework handles this correctly.</P><HR /><H3 id="toc-hId-375072439">Running This on BTP Trial</H3><P class="">Everything described in this post runs on a free SAP BTP Trial account with the ABAP environment service instance. No S/4HANA system required. No license. The ABAP Development Tools (ADT) in Eclipse is the only tool you need locally.</P><P class="">If you've been waiting for the right time to start learning ABAP Cloud and RAP, a BTP trial account is genuinely the right place to start. The constraints of the cloud environment (no classic ABAP, no access to SAP standard tables directly) force you to learn the correct patterns from the beginning.<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Architecture" style="width: 267px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/422691i989C56D06EEEFB8D/image-size/medium?v=v2&amp;px=400" role="button" title="architecture.png" alt="Architecture" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Architecture</span></span><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Material View Interface" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/422688i312BF10C0BE2A355/image-size/medium?v=v2&amp;px=400" role="button" title="Material Interface.png" alt="Material View Interface" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Material View Interface</span></span><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Material Create Interface" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/422689i9DADB511AF72396D/image-size/medium?v=v2&amp;px=400" role="button" title="Material Interface2.png" alt="Material Create Interface" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Material Create Interface</span></span><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Stock Movement Interface" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/422690iE5A475405C6322F0/image-size/medium?v=v2&amp;px=400" role="button" title="movement1.png" alt="Stock Movement Interface" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Stock Movement Interface</span></span></P> 2026-06-17T11:55:35.487000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/on-the-fly-excel-download-in-a-rap-fiori-app-no-database-table-no/ba-p/14422845 On‑the‑Fly Excel Download in a RAP Fiori App — No Database Table, No Persistence, Pure CDS + ABAP 2026-06-18T21:45:02.090000+02:00 hemanthmj https://community.sap.com/t5/user/viewprofilepage/user-id/2311910 <P><STRONG>Author's note:</STRONG> <EM>Views are my own. Object names in this post are illustrative placeholders (<CODE>ZC_*</CODE>, <CODE>ZCL_*</CODE>) — adapt them to your own naming convention. The pattern is what matters, not the names.</EM></P><P><STRONG>Tags:</STRONG> ABAP RAP · CDS Custom Entity · Fiori Elements · Open XML / XLSX · <CODE>cl_abap_zip</CODE> · <CODE>cl_salv_table</CODE> · <CODE>IF_RAP_QUERY_PROVIDER</CODE> · ABAP Cloud</P><HR /><H2 id="toc-hId-1818157192">TL;DR</H2><P>You can serve an Excel file from a Fiori Elements list <STRONG>without ever creating a database table for it</STRONG>. Combine a <STRONG>CDS Custom Entity</STRONG> (whose data is delivered by an ABAP class), a <STRONG>query provider</STRONG> that implements <CODE>IF_RAP_QUERY_PROVIDER</CODE>, and the standard <STRONG><CODE>@Semantics.largeObject</CODE></STRONG> annotation — and the binary you build at runtime is streamed straight to the user's browser as a <CODE>.xlsx</CODE>.</P><P>This blog shows the end‑to‑end pattern with two flavours of XLSX generation:</P><OL><LI><STRONG>Multi‑sheet workbook</STRONG><SPAN>&nbsp;</SPAN>assembled by hand from Open XML parts using<SPAN>&nbsp;</SPAN><CODE>cl_abap_zip</CODE>.</LI><LI><STRONG>Single‑sheet workbook</STRONG><SPAN>&nbsp;</SPAN>generated in one line via<SPAN>&nbsp;</SPAN><CODE>cl_salv_table=&gt;to_xml( c_type_xlsx )</CODE>.</LI></OL><P>Both are 100% ABAP Cloud–compatible, both use only released APIs, and neither persists a single byte.</P><HR /><H2 id="toc-hId-1621643687">1. The problem</H2><P>Every RAP project eventually hits this requirement:</P><BLOCKQUOTE><P><EM>"Put a Download button on the Fiori list page — when the user clicks, hand them an Excel file."</EM></P></BLOCKQUOTE><P>The default reflex is to:</P><OL><LI>Create a Z‑table with a<SPAN>&nbsp;</SPAN><CODE>RAWSTRING</CODE><SPAN>&nbsp;</SPAN>column.</LI><LI>Build the binary in a background job.</LI><LI>Expose it via a transactional CDS view.</LI><LI>Add a clean‑up job for stale rows.</LI><LI>Transport the table, the data, and the cleanup job through every system.</LI></OL><P>That works — but it's wasteful when the file is <STRONG>derivable from master data</STRONG>. You're persisting something that goes stale the moment master data changes, you're transporting binaries, and you're paying storage for content you could regenerate in milliseconds.</P><P>There is a stateless alternative.</P><HR /><H2 id="toc-hId-1425130182">2. The pattern in one picture</H2><DIV class=""><PRE><CODE>Fiori Elements List Report │ GET …/ZC_TmplDownload?$filter=TemplateType eq 'TYPE_A' ▼ CDS Custom Entity ZC_TmplDownload @ObjectModel.query.implementedBy: 'ABAP:ZCL_TMPL_DWNLD_PROVIDER' │ ▼ ABAP Class ZCL_TMPL_DWNLD_PROVIDER IF_RAP_QUERY_PROVIDER~SELECT │ builds XLSX as XSTRING in memory at request time ▼ @Semantics.largeObject ⇒ Browser "Save as…" dialog</CODE></PRE></DIV><P>No database table sits on this path. The binary lives in memory only as long as the HTTP request takes to complete.</P><HR /><H2 id="toc-hId-1228616677">3. The CDS Custom Entity</H2><P>A <STRONG>custom entity</STRONG> is a CDS view whose data is <EM>not</EM> fetched by the SADL runtime — it is delivered by an ABAP class you write. That makes it the perfect anchor for runtime‑computed binaries.</P><DIV class=""><PRE><CODE>@EndUserText.label: 'Template Download' @ObjectModel.query.implementedBy: 'ABAP:ZCL_TMPL_DWNLD_PROVIDER' define custom entity ZC_TmplDownload { @UI.hidden: true key TemplateType : abap.char(20); @UI.lineItem: [{ position: 20, label: 'Filename' }] Filename : abap.char(255); @UI.lineItem: [{ position: 30, label: 'Download' }] @Semantics.largeObject: { mimeType : 'MimeType', fileName : 'Filename', acceptableMimeTypes : [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ], contentDispositionPreference: #ATTACHMENT } Attachment : abap.rawstring(0); @Semantics.mimeType: true @UI.hidden: true MimeType : abap.char(100); }</CODE></PRE></DIV><H3 id="toc-hId-1161185891">What each piece does</H3><P>Annotation / Field Why it matters</P><TABLE><TBODY><TR><TD><CODE>@ObjectModel.query.implementedBy: 'ABAP:ZCL_TMPL_DWNLD_PROVIDER'</CODE></TD><TD>Tells the RAP runtime: don't query the DB — call this class.</TD></TR><TR><TD><CODE>key TemplateType</CODE></TD><TD>Lets the same entity ship N different files. We'll return one row per template type.</TD></TR><TR><TD><CODE>Attachment : abap.rawstring(0)</CODE></TD><TD>The binary payload column.<SPAN>&nbsp;</SPAN><CODE>rawstring(0)</CODE><SPAN>&nbsp;</SPAN>= unbounded length.</TD></TR><TR><TD><CODE>@Semantics.largeObject</CODE></TD><TD>The<SPAN>&nbsp;</SPAN><STRONG>magic annotation</STRONG>. Fiori Elements renders this column as a Download link, fetches it lazily as a separate<SPAN>&nbsp;</SPAN><CODE>$value</CODE><SPAN>&nbsp;</SPAN>request, and pushes the bytes to the browser with the right MIME type and filename.</TD></TR><TR><TD><CODE>mimeType</CODE><SPAN>&nbsp;</SPAN>/<SPAN>&nbsp;</SPAN><CODE>fileName</CODE><SPAN>&nbsp;</SPAN>reference fields</TD><TD>Per‑row file name and content type — each row downloads as its own correctly‑named<SPAN>&nbsp;</SPAN><CODE>.xlsx</CODE>.</TD></TR><TR><TD><CODE>contentDispositionPreference: #ATTACHMENT</CODE></TD><TD>Forces the browser to<SPAN>&nbsp;</SPAN><EM>save</EM>, not to render the file in‑tab.</TD></TR></TBODY></TABLE><BLOCKQUOTE><P><span class="lia-unicode-emoji" title=":light_bulb:">💡</span> <STRONG>Tip:</STRONG> Even on download‑only entities, populate <CODE>acceptableMimeTypes</CODE>. Some Fiori Elements versions use it to harden the download link.</P></BLOCKQUOTE><HR /><H2 id="toc-hId-835589667">4. The query provider class — skeleton</H2><DIV class=""><PRE><CODE>CLASS zcl_tmpl_dwnld_provider DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES if_rap_query_provider. PRIVATE SECTION. METHODS build_template_a RETURNING VALUE(rv_xlsx) TYPE xstring. METHODS build_template_b RETURNING VALUE(rv_xlsx) TYPE xstring. ENDCLASS.</CODE></PRE></DIV><P><CODE>IF_RAP_QUERY_PROVIDER</CODE> has a single method that matters here: <CODE>SELECT</CODE>. RAP calls it for every GET against the entity.</P><H3 id="toc-hId-768158881"><CODE>IF_RAP_QUERY_PROVIDER~SELECT</CODE><SPAN>&nbsp;</SPAN>— the dispatcher</H3><DIV class=""><PRE><CODE>METHOD if_rap_query_provider~select. DATA lt_result TYPE STANDARD TABLE OF ZC_TmplDownload. DATA(lt_filter) = io_request-&gt;get_filter( )-&gt;get_as_ranges( ). DATA(lt_requested) = io_request-&gt;get_requested_elements( ). " Optional pushdown: only build the file the user actually asked for. DATA(lv_wants_a) = abap_true. DATA(lv_wants_b) = abap_true. LOOP AT lt_filter INTO DATA(ls_filter) WHERE name = 'TEMPLATETYPE'. lv_wants_a = COND #( WHEN 'TYPE_A' IN ls_filter-range THEN abap_true ELSE abap_false ). lv_wants_b = COND #( WHEN 'TYPE_B' IN ls_filter-range THEN abap_true ELSE abap_false ). ENDLOOP. IF lv_wants_a = abap_true. APPEND VALUE #( templatetype = 'TYPE_A' filename = 'TemplateA.xlsx' attachment = build_template_a( ) mimetype = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ) TO lt_result. ENDIF. IF lv_wants_b = abap_true. APPEND VALUE #( templatetype = 'TYPE_B' filename = 'TemplateB.xlsx' attachment = build_template_b( ) mimetype = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ) TO lt_result. ENDIF. io_response-&gt;set_total_number_of_records( lines( lt_result ) ). io_response-&gt;set_data( lt_result ). ENDMETHOD.</CODE></PRE></DIV><P>Two ideas worth flagging:</P><OL><LI><STRONG>The XLSX is generated at every GET.</STRONG><SPAN>&nbsp;</SPAN>That is the whole point — the file is always fresh.</LI><LI><STRONG>Filter pushdown is honoured manually.</STRONG><SPAN>&nbsp;</SPAN><CODE>io_request-&gt;get_filter( )-&gt;get_as_ranges( )</CODE><SPAN>&nbsp;</SPAN>returns the OData<SPAN>&nbsp;</SPAN><CODE>$filter</CODE><SPAN>&nbsp;</SPAN>as ABAP ranges. Use it to decide<SPAN>&nbsp;</SPAN><EM>which</EM><SPAN>&nbsp;</SPAN>template to build, so a click on row A never computes row B.</LI></OL><HR /><H2 id="toc-hId-442562657">5. Technique A — Hand‑build a multi‑sheet XLSX with<SPAN>&nbsp;</SPAN><CODE>cl_abap_zip</CODE></H2><P>An <CODE>.xlsx</CODE> file is <STRONG>a ZIP archive of XML files</STRONG> that follow the Open XML SpreadsheetML specification. If you control the XML, you control the file — no SAP Office, no DOI, no OLE, and no third‑party library.</P><H3 id="toc-hId-375131871">5.1 The minimum parts you need</H3><DIV class=""><PRE><CODE>TemplateA.xlsx (zip) ├── [Content_Types].xml ← MIME map for every part inside ├── _rels/.rels ← root relationship → workbook.xml └── xl/ ├── workbook.xml ← declares the sheets ├── styles.xml ← fonts / borders / cellXfs (we use a "bold header" xf) ├── _rels/ │ └── workbook.xml.rels ← workbook → sheet1..N + styles └── worksheets/ ├── sheet1.xml ├── sheet2.xml ├── sheet3.xml └── sheet4.xml</CODE></PRE></DIV><H3 id="toc-hId-178618366">5.2 Build the package skeleton</H3><DIV class=""><PRE><CODE>DATA: lo_zip TYPE REF TO cl_abap_zip, lv_xml TYPE string, lv_xstr TYPE xstring, lv_zip_xstr TYPE xstring. lo_zip = NEW cl_abap_zip( ).</CODE></PRE></DIV><P>Each XML part is built as a string, converted to UTF‑8 <CODE>xstring</CODE>, then dropped into the zip.</P><H3 id="toc-hId--93126508">5.3<SPAN>&nbsp;</SPAN><CODE>[Content_Types].xml</CODE><SPAN>&nbsp;</SPAN>— the MIME map</H3><DIV class=""><PRE><CODE>lv_xml = `&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;` &amp;&amp; `&lt;Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"&gt;` &amp;&amp; `&lt;Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/&gt;` &amp;&amp; `&lt;Default Extension="xml" ContentType="application/xml"/&gt;` &amp;&amp; `&lt;Override PartName="/xl/workbook.xml"` &amp;&amp; ` ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/&gt;` &amp;&amp; `&lt;Override PartName="/xl/worksheets/sheet1.xml"` &amp;&amp; ` ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/&gt;` &amp;&amp; `&lt;Override PartName="/xl/worksheets/sheet2.xml"` &amp;&amp; ` ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/&gt;` &amp;&amp; `&lt;Override PartName="/xl/worksheets/sheet3.xml"` &amp;&amp; ` ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/&gt;` &amp;&amp; `&lt;Override PartName="/xl/worksheets/sheet4.xml"` &amp;&amp; ` ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/&gt;` &amp;&amp; `&lt;Override PartName="/xl/styles.xml"` &amp;&amp; ` ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/&gt;` &amp;&amp; `&lt;/Types&gt;`. lv_xstr = cl_abap_codepage=&gt;convert_to( source = lv_xml codepage = 'UTF-8' ). lo_zip-&gt;add( name = '[Content_Types].xml' content = lv_xstr ).</CODE></PRE></DIV><H3 id="toc-hId--289640013">5.4 The root relationship</H3><DIV class=""><PRE><CODE>lv_xml = `&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;` &amp;&amp; `&lt;Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"&gt;` &amp;&amp; `&lt;Relationship Id="rId1"` &amp;&amp; ` Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"` &amp;&amp; ` Target="xl/workbook.xml"/&gt;` &amp;&amp; `&lt;/Relationships&gt;`. lv_xstr = cl_abap_codepage=&gt;convert_to( source = lv_xml codepage = 'UTF-8' ). lo_zip-&gt;add( name = '_rels/.rels' content = lv_xstr ).</CODE></PRE></DIV><H3 id="toc-hId--486153518">5.5<SPAN>&nbsp;</SPAN><CODE>workbook.xml</CODE><SPAN>&nbsp;</SPAN>— wire up four sheets</H3><DIV class=""><PRE><CODE>lv_xml = `&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;` &amp;&amp; `&lt;workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"` &amp;&amp; ` xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"&gt;` &amp;&amp; `&lt;sheets&gt;` &amp;&amp; `&lt;sheet name="Header" sheetId="1" r:id="rId1"/&gt;` &amp;&amp; `&lt;sheet name="Header_Text" sheetId="2" r:id="rId2"/&gt;` &amp;&amp; `&lt;sheet name="Item" sheetId="3" r:id="rId3"/&gt;` &amp;&amp; `&lt;sheet name="Item_Text" sheetId="4" r:id="rId4"/&gt;` &amp;&amp; `&lt;/sheets&gt;` &amp;&amp; `&lt;/workbook&gt;`.</CODE></PRE></DIV><P>Each <CODE>r:id</CODE> resolves through <CODE>xl/_rels/workbook.xml.rels</CODE> to a real <CODE>worksheets/sheetN.xml</CODE> file. Get this wrong and Excel refuses the file with the dreaded <EM>"We found a problem with some content"</EM> dialog.</P><H3 id="toc-hId--682667023">5.6 The workbook's relationships and styles</H3><DIV class=""><PRE><CODE>" xl/_rels/workbook.xml.rels — workbook → sheets + styles lv_xml = `&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;` &amp;&amp; `&lt;Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"&gt;` &amp;&amp; `&lt;Relationship Id="rId1" Type=".../worksheet" Target="worksheets/sheet1.xml"/&gt;` &amp;&amp; `&lt;Relationship Id="rId2" Type=".../worksheet" Target="worksheets/sheet2.xml"/&gt;` &amp;&amp; `&lt;Relationship Id="rId3" Type=".../worksheet" Target="worksheets/sheet3.xml"/&gt;` &amp;&amp; `&lt;Relationship Id="rId4" Type=".../worksheet" Target="worksheets/sheet4.xml"/&gt;` &amp;&amp; `&lt;Relationship Id="rId5" Type=".../styles" Target="styles.xml"/&gt;` &amp;&amp; `&lt;/Relationships&gt;`. " (Type URLs abbreviated for readability — use the full openxmlformats.org URLs in real code.)</CODE></PRE></DIV><DIV class=""><PRE><CODE>" xl/styles.xml — xf 0 = normal, xf 1 = bold (used by header rows via s="1") lv_xml = `&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;` &amp;&amp; `&lt;styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"&gt;` &amp;&amp; `&lt;fonts count="2"&gt;` &amp;&amp; `&lt;font&gt;&lt;sz val="11"/&gt;&lt;name val="Calibri"/&gt;&lt;/font&gt;` &amp;&amp; `&lt;font&gt;&lt;b/&gt;&lt;sz val="11"/&gt;&lt;name val="Calibri"/&gt;&lt;/font&gt;` &amp;&amp; `&lt;/fonts&gt;` &amp;&amp; `&lt;fills count="2"&gt;` &amp;&amp; `&lt;fill&gt;&lt;patternFill patternType="none"/&gt;&lt;/fill&gt;` &amp;&amp; `&lt;fill&gt;&lt;patternFill patternType="gray125"/&gt;&lt;/fill&gt;` &amp;&amp; `&lt;/fills&gt;` &amp;&amp; `&lt;borders count="1"&gt;` &amp;&amp; `&lt;border&gt;&lt;left/&gt;&lt;right/&gt;&lt;top/&gt;&lt;bottom/&gt;&lt;diagonal/&gt;&lt;/border&gt;` &amp;&amp; `&lt;/borders&gt;` &amp;&amp; `&lt;cellStyleXfs count="1"&gt;` &amp;&amp; `&lt;xf numFmtId="0" fontId="0" fillId="0" borderId="0"/&gt;` &amp;&amp; `&lt;/cellStyleXfs&gt;` &amp;&amp; `&lt;cellXfs count="2"&gt;` &amp;&amp; `&lt;xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/&gt;` &amp;&amp; `&lt;xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0"/&gt;` &amp;&amp; `&lt;/cellXfs&gt;` &amp;&amp; `&lt;/styleSheet&gt;`.</CODE></PRE></DIV><H3 id="toc-hId--879180528">5.7 A worksheet — bold header + data row</H3><DIV class=""><PRE><CODE>" Sheet 1 — Header (KEY_FIELD | GROUP_FIELD | STATUS) " s="1" → cellXfs index 1 (bold) defined in styles.xml " t="inlineStr" → string lives inside the cell (no shared-strings table needed) lv_xml = `&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;` &amp;&amp; `&lt;worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"&gt;` &amp;&amp; `&lt;sheetData&gt;` &amp;&amp; `&lt;row r="1"&gt;` &amp;&amp; `&lt;c r="A1" t="inlineStr" s="1"&gt;&lt;is&gt;&lt;t&gt;Key Field&lt;/t&gt;&lt;/is&gt;&lt;/c&gt;` &amp;&amp; `&lt;c r="B1" t="inlineStr" s="1"&gt;&lt;is&gt;&lt;t&gt;Group Field&lt;/t&gt;&lt;/is&gt;&lt;/c&gt;` &amp;&amp; `&lt;c r="C1" t="inlineStr" s="1"&gt;&lt;is&gt;&lt;t&gt;Status&lt;/t&gt;&lt;/is&gt;&lt;/c&gt;` &amp;&amp; `&lt;/row&gt;` &amp;&amp; `&lt;row r="2"&gt;` &amp;&amp; `&lt;c r="A2" t="inlineStr"&gt;&lt;is&gt;&lt;t&gt;KEY01&lt;/t&gt;&lt;/is&gt;&lt;/c&gt;` &amp;&amp; `&lt;c r="B2" t="inlineStr"&gt;&lt;is&gt;&lt;t&gt;GRP01&lt;/t&gt;&lt;/is&gt;&lt;/c&gt;` &amp;&amp; `&lt;c r="C2" t="inlineStr"&gt;&lt;is&gt;&lt;t&gt;X&lt;/t&gt;&lt;/is&gt;&lt;/c&gt;` &amp;&amp; `&lt;/row&gt;` &amp;&amp; `&lt;/sheetData&gt;` &amp;&amp; `&lt;/worksheet&gt;`.</CODE></PRE></DIV><P>Two pragmatic tricks worth knowing:</P><UL><LI><STRONG><CODE>t="inlineStr"</CODE></STRONG><SPAN>&nbsp;</SPAN>— store strings<SPAN>&nbsp;</SPAN><EM>inside</EM><SPAN>&nbsp;</SPAN>the cell. The alternative is a shared‑strings part, more efficient for large repeated text but it doubles the moving parts. For a download template, inline is the right trade‑off.</LI><LI><STRONG><CODE>s="1"</CODE></STRONG><SPAN>&nbsp;</SPAN>— refer to<SPAN>&nbsp;</SPAN><CODE>cellXfs</CODE><SPAN>&nbsp;</SPAN>index 1, which we defined as bold. Headers come out bold without further effort.</LI></UL><H3 id="toc-hId--1075694033">5.8 Finalize</H3><DIV class=""><PRE><CODE>lo_zip-&gt;save( RECEIVING zip = lv_zip_xstr ). rv_xlsx = lv_zip_xstr. " ← this xstring IS the .xlsx file</CODE></PRE></DIV><P>That <CODE>xstring</CODE> flows up through <CODE>set_data( )</CODE> into the <CODE>Attachment</CODE> column of the custom entity, and the <CODE>@Semantics.largeObject</CODE> runtime serves it to the browser as a real <CODE>.xlsx</CODE>.</P><HR /><H2 id="toc-hId--978804531">6. Technique B — Single‑sheet XLSX via<SPAN>&nbsp;</SPAN><CODE>cl_salv_table</CODE><SPAN>&nbsp;</SPAN>(the easy route)</H2><P>When you only need <STRONG>one worksheet</STRONG>, skip the manual XML and let SALV do it:</P><DIV class=""><PRE><CODE>METHOD build_template_b. TYPES: BEGIN OF ty_row, field_a TYPE char20, field_b TYPE char10, field_c TYPE char10, END OF ty_row. DATA lt_data TYPE TABLE OF ty_row. DATA lo_table TYPE REF TO cl_salv_table. DATA lo_columns TYPE REF TO cl_salv_columns_table. DATA lo_column TYPE REF TO cl_salv_column. lt_data = VALUE #( ( field_a = 'SAMPLE_KEY_001' field_b = 'CODE01' field_c = 'GRP01' ) ). TRY. cl_salv_table=&gt;factory( IMPORTING r_salv_table = lo_table CHANGING t_table = lt_data ). lo_columns = lo_table-&gt;get_columns( ). TRY. lo_column = lo_columns-&gt;get_column( 'FIELD_A' ). lo_column-&gt;set_long_text( 'Field A' ). lo_column-&gt;set_output_length( 20 ). CATCH cx_salv_not_found. ENDTRY. " ★ The single line that turns a SALV table into a complete .xlsx xstring rv_xlsx = lo_table-&gt;to_xml( xml_type = if_salv_bs_xml=&gt;c_type_xlsx ). CATCH cx_salv_msg. RETURN. ENDTRY. ENDMETHOD.</CODE></PRE></DIV><P><CODE>cl_salv_table=&gt;to_xml( c_type_xlsx )</CODE> returns a fully‑formed XLSX <CODE>xstring</CODE>. The catch: it produces <STRONG>exactly one sheet</STRONG>. That is why the multi‑sheet template needs Technique A.</P><HR /><H2 id="toc-hId--1175318036">7. Wire it up in Fiori Elements</H2><P>In your service definition:</P><DIV class=""><PRE><CODE>@EndUserText.label: 'Template Downloads' define service ZUI_TEMPLATE_DOWNLOADS { expose ZC_TmplDownload; }</CODE></PRE></DIV><P>Bind it to a service binding (OData V4 → UI), generate a Fiori Elements <STRONG>List Report</STRONG>, and you'll see one row per template type, each with a <STRONG>download icon</STRONG> in the Download column. Click → the browser receives <CODE>TemplateA.xlsx</CODE> or <CODE>TemplateB.xlsx</CODE> directly from memory.</P><P>That's it. <STRONG>Zero DB tables. Zero persistence.</STRONG></P><HR /><H2 id="toc-hId--1371831541">8. Why this pattern is worth adopting</H2><P>Concern Stateless Custom Entity (this blog) Persisted "blob in a Z‑table"</P><TABLE><TBODY><TR><TD>Freshness</TD><TD>Always live — built per request</TD><TD>Goes stale; needs a refresh job</TD></TR><TR><TD>Storage</TD><TD>None</TD><TD>RAWSTRING in DB, every release</TD></TR><TR><TD>Transport / cutover</TD><TD>Just code</TD><TD>Code<SPAN>&nbsp;</SPAN><STRONG>and</STRONG><SPAN>&nbsp;</SPAN>data</TD></TR><TR><TD>Authorization</TD><TD>Enforced in the class</TD><TD>Same, plus DB‑level checks</TD></TR><TR><TD>Testability</TD><TD>Plain ABAP unit test on the helper methods</TD><TD>Needs DB fixtures</TD></TR><TR><TD>Performance</TD><TD>One ZIP build per click</TD><TD>One DB read — but you paid for the build earlier, in a job</TD></TR></TBODY></TABLE><P>For <EM>templates</EM> and other derivable artifacts the trade is one‑sided in favour of statelessness. Reach for the persisted variant only when (a) the file is genuinely expensive to build (think tens of MB / millions of rows) <STRONG>and</STRONG> (b) it doesn't need to reflect the latest master data.</P><HR /><H2 id="toc-hId--1568345046">9. Pitfalls (so you don't have to learn them the hard way)</H2><OL><LI><STRONG>UTF‑8 everywhere.</STRONG><SPAN>&nbsp;</SPAN><CODE>cl_abap_codepage=&gt;convert_to( … codepage = 'UTF-8' )</CODE><SPAN>&nbsp;</SPAN>for<SPAN>&nbsp;</SPAN><EM>every</EM><SPAN>&nbsp;</SPAN>XML part. Excel will reject mixed encodings.</LI><LI><STRONG>Relationship IDs (<CODE>rId…</CODE>) must match across<SPAN>&nbsp;</SPAN><CODE>workbook.xml</CODE><SPAN>&nbsp;</SPAN>and<SPAN>&nbsp;</SPAN><CODE>xl/_rels/workbook.xml.rels</CODE>.</STRONG><SPAN>&nbsp;</SPAN>Easiest way to corrupt the file.</LI><LI><STRONG>Don't forget<SPAN>&nbsp;</SPAN><CODE>styles.xml</CODE><SPAN>&nbsp;</SPAN>in BOTH<SPAN>&nbsp;</SPAN><CODE>[Content_Types].xml</CODE><SPAN>&nbsp;</SPAN>AND<SPAN>&nbsp;</SPAN><CODE>workbook.xml.rels</CODE>.</STRONG><SPAN>&nbsp;</SPAN>If you reference<SPAN>&nbsp;</SPAN><CODE>s="1"</CODE><SPAN>&nbsp;</SPAN>without a registered styles part, Excel opens with a "repair" prompt.</LI><LI><STRONG><CODE>@Semantics.largeObject</CODE><SPAN>&nbsp;</SPAN>requires<SPAN>&nbsp;</SPAN><CODE>mimeType</CODE><SPAN>&nbsp;</SPAN>and<SPAN>&nbsp;</SPAN><CODE>fileName</CODE><SPAN>&nbsp;</SPAN>to point at columns</STRONG><SPAN>&nbsp;</SPAN>— not literal strings. The annotation references<SPAN>&nbsp;</SPAN><EM>field names</EM>.</LI><LI><STRONG>Don't mix shared strings with inline strings unless you really need to.</STRONG><SPAN>&nbsp;</SPAN><CODE>t="inlineStr"</CODE><SPAN>&nbsp;</SPAN>keeps each sheet self‑contained.</LI><LI><STRONG>Filter pushdown costs nothing.</STRONG><SPAN>&nbsp;</SPAN>Build only the row Fiori asked for — your download click should never compute the<SPAN>&nbsp;</SPAN><EM>other</EM><SPAN>&nbsp;</SPAN>template.</LI><LI><STRONG>Avoid stacks/classes that aren't released for ABAP Cloud</STRONG><SPAN>&nbsp;</SPAN>(some XLSX helper classes aren't).<SPAN>&nbsp;</SPAN><CODE>cl_abap_zip</CODE>,<SPAN>&nbsp;</SPAN><CODE>cl_abap_codepage</CODE>, and<SPAN>&nbsp;</SPAN><CODE>cl_salv_table</CODE><SPAN>&nbsp;</SPAN>are the safe choices.</LI><LI><STRONG>Authorization checks belong in the class.</STRONG><SPAN>&nbsp;</SPAN>A custom entity has no built‑in DB‑level access controls — your<SPAN>&nbsp;</SPAN><CODE>SELECT</CODE><SPAN>&nbsp;</SPAN>is the gatekeeper. Call your authorization object before<SPAN>&nbsp;</SPAN><CODE>set_data( )</CODE>.</LI><LI><STRONG>Watch the response size.</STRONG><SPAN>&nbsp;</SPAN><CODE>@Semantics.largeObject</CODE><SPAN>&nbsp;</SPAN>streams, but the gateway still buffers — keep templates under a few MB. For very large files, switch to a dedicated streaming endpoint.</LI></OL><HR /><H2 id="toc-hId--1596674860">10. Closing</H2><P>The combination of <STRONG>CDS Custom Entity + <CODE>IF_RAP_QUERY_PROVIDER</CODE> + <CODE>@Semantics.largeObject</CODE></STRONG> is one of the most under‑used corners of RAP. Once you internalise that the <CODE>Attachment</CODE> column is just an <CODE>xstring</CODE> you build at runtime, every "give me a download" requirement becomes a one‑class problem — no schema change, no transport drama, no stale binaries.</P><P>The same wiring works for:</P><UL><LI><STRONG>PDF previews</STRONG><SPAN>&nbsp;</SPAN>— build the bytes with the form runtime and serve them.</LI><LI><STRONG>CSV exports</STRONG><SPAN>&nbsp;</SPAN>— return UTF‑8 text (with BOM if Excel users will open them).</LI><LI><STRONG>ZIP archives of multiple files</STRONG><SPAN>&nbsp;</SPAN>— keep<SPAN>&nbsp;</SPAN><CODE>add( )</CODE>‑ing into<SPAN>&nbsp;</SPAN><CODE>cl_abap_zip</CODE>.</LI><LI><STRONG>Image thumbnails</STRONG><SPAN>&nbsp;</SPAN>— computed from master data.</LI></UL><P>Same plumbing. Different <CODE>xstring</CODE>.</P><P>If this pattern saved you a Z‑table, drop a <span class="lia-unicode-emoji" title=":thumbs_up:">👍</span> and tell me what you used it for in the comments.</P><P><STRONG>Happy RAP‑ing.</STRONG> <span class="lia-unicode-emoji" title=":hammer_and_wrench:">🛠</span>️</P><H3 id="toc-hId--2086591372">Personal disclaimer</H3><P><EM>The views and techniques expressed here are my own and do not represent those of my employer or SAP. Code samples are illustrative — validate them in your own development system before adopting them in productive code.</EM></P> 2026-06-18T21:45:02.090000+02:00 https://community.sap.com/t5/abap-blog-posts/understanding-field-suppress-in-sap-rap-and-how-it-differs-from-ui-hidden/ba-p/14409246 Understanding field(suppress) in SAP RAP and How It Differs from @UI.hidden and @Consumption.hidden 2026-06-25T20:52:31.333000+02:00 vidyadharsp https://community.sap.com/t5/user/viewprofilepage/user-id/1913267 <P class="lia-align-justify" style="text-align : justify;"><FONT face="arial,helvetica,sans-serif" color="#000000"><U><FONT size="5"><STRONG>INTRODUCTION</STRONG></FONT></U></FONT></P><P><FONT color="#000000">When working with SAP RAP (RESTful Application Programming Model), the Behavior Definition (BDEF) offers several keywords to control how data is exposed and handled. One of the less-discussed but powerful ones is field(suppress).</FONT></P><P><FONT color="#000000">This blog is not just about syntax — it’s about what actually happens at runtime, the edge cases I hit, and why I chose suppression over <STRONG>readonly</STRONG>.</FONT></P><P class="lia-align-justify" style="text-align : justify;"><FONT color="#000000"><FONT face="arial,helvetica,sans-serif" size="3"><STRONG>Requirement:</STRONG> In had one requirement, customer service reps needed to update Priority, Deadline Date, Cancel Reason, but they should never touch Created Time or Created User. These audit fields must remain intact, even during draft saves or mass updates.</FONT></FONT></P><P><FONT color="#000000"><U><STRONG>What I Tried First</STRONG></U></FONT><BR /><FONT color="#000000"><STRONG>@UI.hidden:</STRONG> Hid the fields in Fiori, but they were still exposed in OData payloads.</FONT></P><P><FONT color="#000000"><STRONG>field(readonly):</STRONG> Made the fields immutable, but they still appeared in EML structures — meaning a payload could attempt to overwrite them.</FONT></P><P><FONT color="#000000">Both approaches failed to fully protect the audit fields.</FONT></P><P><FONT color="#000000"><STRONG>What is field(suppress)?</STRONG></FONT><BR /><FONT color="#000000">field(suppress) is a static feature control in the BDEF that removes a field from RAP's transactional behavior layer. When a field is marked as suppressed, RAP excludes it from:</FONT></P><UL><LI><FONT color="#000000">EML (Entity Manipulation Language) structures — such as TYPE TABLE FOR CREATE, TYPE STRUCTURE FOR UPDATE</FONT></LI><LI><FONT color="#000000">RAP-generated behavior APIs</FONT></LI><LI><FONT color="#000000">Behavior-related metadata</FONT></LI></UL><P><FONT color="#000000"><U><STRONG>Runtime Behavior of Suppressed Fields</STRONG></U></FONT><BR /><FONT color="#000000">When I switched to <STRONG>field(suppress)</STRONG>, here’s what I observed:</FONT></P><P><FONT color="#000000">In EML statements, suppressed fields simply don’t exist. If you try to reference them, RAP silently ignores them, no runtime dump, no short dump.</FONT></P><P><FONT color="#000000">If you mistakenly add them in the mapping section, RAP throws a framework error at activation time.</FONT></P><P><FONT color="#000000">In draft handling, suppressed fields are still maintained internally, but you can’t overwrite them. This was critical for audit consistency.</FONT></P><P><FONT color="#000000">In determinations, you cannot reference suppressed fields — the compiler blocks it. I observed this when trying to auto-populate Created By and Created At.</FONT></P><P class="lia-align-justify" style="text-align : justify;"><FONT color="#000000"><U><FONT face="arial,helvetica,sans-serif"><STRONG>Syntax:</STRONG></FONT></U></FONT></P><pre class="lia-code-sample language-abap"><code>define behavior for ZI_REQHDR { field ( suppress ) CreatedAt, CreatedBy; }</code></pre><P class="lia-align-justify" style="text-align : justify;"><FONT color="#000000"><SPAN><STRONG>Attempting to map them in the behavior pool raises:</STRONG><BR />Field "CreatedBy" cannot be suppressed.</SPAN></FONT></P><P class="lia-align-justify" style="text-align : justify;"><FONT color="#000000"><SPAN><STRONG>Key insight:</STRONG> <CODE>field(suppress)</CODE> is a behavior-layer protection tool — it removes a field from RAP-generated transactional types and APIs. It is <STRONG>not</STRONG> a UI visibility control or an OData filter</SPAN></FONT></P><P><U><STRONG>Why Suppress Instead of Readonly</STRONG></U><BR /><FONT color="#000000"><STRONG>Readonly:</STRONG> Field is visible but immutable — still exposed in payloads.</FONT></P><P><FONT color="#000000"><STRONG>Suppress:</STRONG> Field is completely removed from RAP’s transactional model — safest option for audit fields.</FONT></P><P><FONT color="#000000"><STRONG>Note:</STRONG> Key fields (primary key fields) cannot be suppressed.</FONT></P><UL class="lia-align-justify" style="text-align : justify;"><LI><FONT face="arial,helvetica,sans-serif" color="#000000">Suppressed fields must also be excluded from the field mapping. Attempting to include them raises a framework error:</FONT></LI></UL><pre class="lia-code-sample language-abap"><code>mapping for zdt_vd_reqhdr corresponding { RequestUuid = request_uuid; Status = status; Priority = priority; ExternalId = external_id; DeadlineDate = deadline_date; RequesterId = requester_id; CancelReason = cancel_reason; * // If i try to add the below fields error will get raised * CreatedBy = created_by; // Field "CreatedBy" cannot suppressed * CreatedAt = created_at; // Field "CreatedAt" cannot suppressed LastChangedBy = last_changed_by; LastChangedAt = last_changed_at; }</code></pre><P class="lia-align-justify" style="text-align : justify;"><FONT face="arial,helvetica,sans-serif" color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="vidyadharsp_2-1780378958696.png" style="width: 614px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/417230iFE6675E3B0137B06/image-dimensions/614x217?v=v2" width="614" height="217" role="button" title="vidyadharsp_2-1780378958696.png" alt="vidyadharsp_2-1780378958696.png" /></span></FONT></P><P class="lia-align-justify" style="text-align : justify;"><FONT face="arial,helvetica,sans-serif" color="#000000"><U><STRONG>Behavior Pool: Effect on EML Strutcure</STRONG></U></FONT></P><UL class="lia-align-justify" style="text-align : justify;"><LI><FONT face="arial,helvetica,sans-serif" color="#000000">Once suppressed, the fields are no longer available in EML operations. For example, in a <CODE>MODIFY ENTITIES</CODE> call, suppressed fields do not appear in the <CODE>FIELDS ( )</CODE> selector:</FONT></LI></UL><pre class="lia-code-sample language-abap"><code> MODIFY ENTITIES OF zi_vd_reqhdr IN LOCAL MODE ENTITY RequestHeader UPDATE FIELDS ( ) * i try add the fields which i have done suppressed those not visible here WITH VALUE #( FOR ls_key IN keys ( %key = ls_key-%key Status = 100 ) ) REPORTED DATA(lt_report).</code></pre><P class="lia-align-justify" style="text-align : justify;"><FONT face="arial,helvetica,sans-serif" color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="vidyadharsp_4-1780380402428.png" style="width: 608px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/417278iEE0A7B71282654C4/image-dimensions/608x259?v=v2" width="608" height="259" role="button" title="vidyadharsp_4-1780380402428.png" alt="vidyadharsp_4-1780380402428.png" /></span></FONT></P><UL class="lia-align-justify" style="text-align : justify;"><LI><FONT color="#000000"><FONT face="arial,helvetica,sans-serif">This confirms that suppressed fields are cleanly removed from the generated <CODE>TYPE STRUCTURE FOR UPDATE</CODE> and equivalent EML types.</FONT></FONT></LI></UL><H4 id="toc-hId-2074678360"><FONT color="#000000">How Does It Differ from <CODE><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.hidden</CODE> and <CODE>@Consumption.hidden</CODE>?</FONT></H4><P class=""><FONT color="#000000">All three mechanisms can "hide" a field, but they operate at different layers:</FONT></P><DIV class="">Mechanism Layer Effect <TABLE><TBODY><TR><TD><FONT color="#000000"><CODE>field(suppress)</CODE></FONT></TD><TD><FONT color="#000000">RAP behavior / EML</FONT></TD><TD><FONT color="#000000">Removes field from RAP transactional APIs and EML structures</FONT></TD></TR><TR><TD><FONT color="#000000"><CODE><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.hidden: true</CODE></FONT></TD><TD><FONT color="#000000">UI rendering</FONT></TD><TD><FONT color="#000000">Hides the field from generated Fiori screens; field may still appear in OData metadata and API payloads</FONT></TD></TR><TR><TD><FONT color="#000000"><CODE>@Consumption.hidden: true</CODE></FONT></TD><TD><FONT color="#000000">OData consumption</FONT></TD><TD><FONT color="#000000">Hides the field from OData consumers and Fiori Elements scenarios</FONT></TD></TR></TBODY></TABLE></DIV><UL><LI><FONT size="3" color="#000000"><FONT face="arial,helvetica,sans-serif">Use <CODE>field(suppress)</CODE> to protect a field from the RAP behavior layer entirely.</FONT></FONT></LI><LI><FONT size="3" color="#000000"><FONT face="arial,helvetica,sans-serif">Use <CODE><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.hidden</CODE> to keep a field out of the UI while it remains accessible in APIs.</FONT></FONT></LI><LI><FONT size="3" color="#000000"><FONT face="arial,helvetica,sans-serif">Use <CODE>@Consumption.hidden</CODE> to hide a field from external OData consumers.</FONT></FONT></LI></UL><P class="lia-align-justify" style="text-align : justify;"><FONT face="arial,helvetica,sans-serif" color="#000000"><U><STRONG><FONT size="5">Conclusion</FONT></STRONG></U></FONT></P><P class="lia-align-justify" style="text-align : justify;"><FONT face="arial,helvetica,sans-serif" color="#000000"><CODE>field(suppress)</CODE>, <CODE><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.hidden</CODE>, and <CODE>@Consumption.hidden</CODE> serve distinct purposes in the RAP stack and should not be treated as interchangeable. When you have internal technical fields — timestamps, UUIDs, control fields — that RAP should never expose through its transactional model, <CODE>field(suppress)</CODE> is the right tool. Pair it with <CODE>@Consumption.hidden</CODE> to lock down the field at both the behavior and OData layers.</FONT></P> 2026-06-25T20:52:31.333000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/updating-sales-order-header-and-sales-order-item-using-business-object/ba-p/14427750 Updating Sales Order Header and Sales Order Item using Business Object Framework(Cleancore Approach) 2026-06-26T07:11:15.507000+02:00 PM_iiits https://community.sap.com/t5/user/viewprofilepage/user-id/2165681 <P>In RAP, business processes are encapsulated within <STRONG>Business Objects (BOs)</STRONG>, which define the data model and business logic. Developers interact with these Business Objects using the <STRONG>Entity Manipulation Language (EML)</STRONG>, a modern ABAP syntax that supports operations such as <STRONG>CREATE, UPDATE, DELETE, and EXECUTE</STRONG>. By leveraging RAP and EML, applications become more maintainable, extensible, and upgrade-safe while adhering to SAP's Clean Core strategy and cloud-ready development standards.</P><P>Example : -&nbsp;</P><P>&nbsp;</P><DIV><DIV><P><SPAN>MODIFY ENTITIES OF </SPAN><SPAN>i_salesordertp</SPAN></P><P><SPAN>ENTITY </SPAN><SPAN>salesorder</SPAN></P><P><SPAN>UPDATE FIELDS ( </SPAN><SPAN>zz1_testdata_sdh</SPAN><SPAN> )</SPAN></P><P><SPAN>WITH VALUE #( (</SPAN></P><P><SPAN>%key</SPAN><SPAN>-</SPAN><SPAN>salesorder</SPAN><SPAN> = </SPAN><SPAN>keys</SPAN><SPAN>[ </SPAN><SPAN>1</SPAN><SPAN> ]-</SPAN><SPAN>salesorder</SPAN></P><P><SPAN>zz1_testdata_sdh</SPAN><SPAN> = </SPAN><SPAN>'TestingData'</SPAN></P><P><SPAN>) )</SPAN></P><P><SPAN>ENTITY </SPAN><SPAN>salesorderitem</SPAN></P><P><SPAN>UPDATE FIELDS ( </SPAN><SPAN>zz1_status1_sdi</SPAN><SPAN> )</SPAN></P><P><SPAN>WITH VALUE #(</SPAN></P><P><SPAN>(</SPAN></P><P><SPAN>%key</SPAN><SPAN>-</SPAN><SPAN>salesorder</SPAN><SPAN> = </SPAN><SPAN>keys</SPAN><SPAN>[ </SPAN><SPAN>1</SPAN><SPAN> ]-</SPAN><SPAN>salesorder</SPAN></P><P><SPAN>%key</SPAN><SPAN>-</SPAN><SPAN>salesorderitem</SPAN><SPAN> = </SPAN><SPAN>keys</SPAN><SPAN>[ </SPAN><SPAN>1</SPAN><SPAN> ]-</SPAN><SPAN>itemno</SPAN></P><P><SPAN>zz1_status1_sdi</SPAN><SPAN> = </SPAN><SPAN>keys</SPAN><SPAN>[ </SPAN><SPAN>1</SPAN><SPAN> ]-</SPAN><SPAN>%param</SPAN><SPAN>-</SPAN><SPAN>status</SPAN></P><P><SPAN>)</SPAN></P><P><SPAN>).</SPAN></P><P>&nbsp;</P></DIV></DIV><P class=""><STRONG><SPAN>Note:</SPAN></STRONG><SPAN> The approach described here is </SPAN><STRONG><SPAN>not limited to custom fields</SPAN></STRONG><SPAN>. It applies to </SPAN><STRONG><SPAN>any field</SPAN></STRONG><SPAN> exposed by the </SPAN><STRONG><SPAN>SalesOrder</SPAN></STRONG><SPAN> or </SPAN><STRONG><SPAN>SalesOrderItem</SPAN></STRONG><SPAN> Business Object, regardless of whether the field is standard or custom.</SPAN></P><P class=""><SPAN>If you introduce a custom field by directly appending standard database tables (for example, </SPAN><STRONG><SPAN>VBAK</SPAN></STRONG><SPAN> or </SPAN><STRONG><SPAN>VBAP</SPAN></STRONG><SPAN>), you must manually extend the entire data exposure layer. This typically involves extending CDS views, updating OData services, enhancing APIs, and ensuring the field is propagated across all relevant artifacts, which increases development effort and maintenance complexity.</SPAN></P><P class=""><SPAN>A more Clean Core–compliant approach is to create custom fields using the </SPAN><STRONG><SPAN>Custom Fields and Logic (CFL)</SPAN></STRONG><SPAN> framework. When configured and enabled correctly, CFL automatically extends the relevant CDS views, OData services, and Business Objects, significantly reducing manual development. To ensure end-to-end availability of the custom field, enable the required </SPAN><STRONG><SPAN>UIs</SPAN></STRONG><SPAN>, </SPAN><STRONG><SPAN>SAP GUI</SPAN></STRONG><SPAN>, </SPAN><STRONG><SPAN>CDS Views</SPAN></STRONG><SPAN>, and </SPAN><STRONG><SPAN>APIs</SPAN></STRONG><SPAN> during field creation. For implementing custom business logic, use </SPAN><STRONG><SPAN>Cloud BAdIs</SPAN></STRONG><SPAN> instead of classical enhancement techniques.</SPAN></P><P><SPAN>For a detailed walkthrough on enabling CFL artifacts and implementing Cloud BAdIs, please refer to my previous blog.</SPAN></P><P>-&nbsp;<A href="https://community.sap.com/t5/technology-blog-posts-by-sap/custom-field-and-logic-from-scratch/ba-p/14426888" target="_blank">https://community.sap.com/t5/technology-blog-posts-by-sap/custom-field-and-logic-from-scratch/ba-p/14426888</A></P> 2026-06-26T07:11:15.507000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/custom-root-entity-use-case-along-with-custom-messages-in-rap/ba-p/14429799 Custom Root Entity Use case along with custom messages in RAP 2026-06-30T06:27:11.282000+02:00 PM_iiits https://community.sap.com/t5/user/viewprofilepage/user-id/2165681 <H1 id="toc-hId-1689282208">A Custom Entity</H1><P>A <STRONG>Custom Entity</STRONG> has no real table linked to it. RAP cannot read it automatically. Instead, it creates an empty structure and hands over the control to you. You then write your own ABAP code inside a <STRONG>Query Provider Class (QPC)</STRONG>.</P><H2 id="toc-hId-1621851422">Custom Root Entity Example</H2><PRE><CODE>@EndUserText.label: 'Custom Root Entity' @ObjectModel: { query: { implementedBy: 'ABAP:ZCL_SALES_STATUS' } } @UI: { headerInfo: { typeName: 'SalesData', typeNamePlural: 'SalesData', title: { value: 'Salesorder' }, description: { value: 'Itemno' } } } define root custom entity ZCR_CUSTSO_VIEW { @UI.lineItem: [{ position: 10 , label: 'Sales Order' }] @Consumption.filter.mandatory: true @Search.defaultSearchElement: true @UI.identification:[{ position: 10 , label: 'Sales Order'}] key Salesorder : vbeln_va; @Consumption.filter.mandatory: true @Search.defaultSearchElement: true @UI.lineItem: [{ position: 20 , label: 'Sales Order Item' }] @UI.identification: [{ position: 20 , label: 'Sales Order Item' }] key Itemno : posnr_va; @Consumption.valueHelpDefinition: [{ entity: { name: 'ZStatus_VH', // Your Value Help View element: 'Value' // Source field for Status }, additionalBinding: [{ localElement: 'Statusdesc', // Target field in your app element: 'Description', // Source field from Value Help View usage: #RESULT // Passes data on selection }] }] @UI.lineItem: [{ position: 30 , label: 'Sales Order Status' }] @UI.identification: [{ position: 30 , label: 'Sales Order Status' }] Status : abap.char( 1 ); @UI.lineItem: [{ position: 40 , label: 'Time Stamp' }] @UI.identification: [{ position: 40 , label: 'Time Stamp' }] Timestamp : timestampl; @UI.identification: [{ position: 50 , label: 'Status Description' }] @UI.lineItem: [{ position: 50 , label: 'Status Description' }] Statusdesc : abap.char(20); }</CODE></PRE><HR /><P>As you have created a <STRONG>Custom Root Entity</STRONG>, you are not forced to fetch data from a database source.</P><P>You can validate the input before fetching the data, or you can write your validation logic along with the data retrieval because you are implementing the query yourself.</P><P>You can loop over the data, modify it, apply your own business logic, or perform any operation you require before returning the response.</P><PRE><CODE>CLASS zcl_sales_status DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_rap_query_provider . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_sales_status IMPLEMENTATION. METHOD if_rap_query_provider~select. DATA(lv_top) = io_request-&gt;get_paging( )-&gt;get_page_size( ). IF lv_top &lt; 0. lv_top = 1. ENDIF. DATA(lv_skip) = io_request-&gt;get_paging( )-&gt;get_offset( ). DATA(lt_sort) = io_request-&gt;get_sort_elements( ). DATA : lv_orderby TYPE string. LOOP AT lt_sort INTO DATA(ls_sort). IF ls_sort-descending = abap_true. lv_orderby = |'{ lv_orderby } { ls_sort-element_name } DESCENDING |. ELSE. lv_orderby = |'{ lv_orderby } { ls_sort-element_name } ASCENDING |. ENDIF. ENDLOOP. IF lv_orderby IS INITIAL. lv_orderby = 'Salesorder'. ENDIF. DATA(lv_conditions) = io_request-&gt;get_filter( )-&gt;get_as_sql_string( ). SELECT FROM zebst_status_log FIELDS salesorder, itemno, status, statusdesc, timestamp WHERE (lv_conditions) ORDER BY (lv_orderby) INTO TABLE @DATA(lt_status) UP TO @lv_top ROWS OFFSET @lv_skip. IF lt_status IS INITIAL. IF lv_conditions IS NOT INITIAL. RAISE EXCEPTION TYPE zcx_rap_exception_provider EXPORTING textid = VALUE scx_t100key( * HOW TO ADD CUSTOM MESSAGE CREATED IN SE91 (ZMSG_STATUS) msgid = 'ZMSG_STATUS' msgno = '000' ) previous = NEW cx_sadl_contract_violation( ). ELSEIF lv_conditions IS INITIAL. RAISE EXCEPTION TYPE zcx_rap_exception_provider EXPORTING textid = VALUE scx_t100key( * HOW TO ADD CUSTOM MESSAGE CREATED IN SE91 (ZMSG_STATUS) msgid = 'ZMSG_STATUS' msgno = '001' ) previous = NEW cx_sadl_contract_violation( ). ENDIF. ELSE. IF io_request-&gt;is_total_numb_of_rec_requested( ). io_response-&gt;set_total_number_of_records( lines( lt_status ) ). io_response-&gt;set_data( lt_status ). ENDIF. ENDIF. ENDMETHOD. ENDCLASS.</CODE></PRE><HR /><P>Using this approach, you can display a custom message when the user clicks the <STRONG>Go</STRONG> button.</P><P>In <STRONG>Managed</STRONG> and <STRONG>Unmanaged</STRONG> RAP scenarios, it is not straightforward to display this type of validation message during the read operation because the framework handles the data retrieval automatically. However, with a <STRONG>Custom Root Entity</STRONG> and <STRONG>Query Provider Class</STRONG>, you have complete control over the query execution and can perform validations before returning the data.</P><H2 id="toc-hId-1425337917">Custom Exception Class</H2><P>To display custom messages, you need to create a custom exception class by inheriting from the standard RAP Query Provider exception class.</P><PRE><CODE>CLASS zcx_rap_exception_provider DEFINITION PUBLIC INHERITING FROM cx_rap_query_provider FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS constructor IMPORTING !textid LIKE if_t100_message=&gt;t100key OPTIONAL !previous LIKE previous OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcx_rap_exception_provider IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. CALL METHOD super-&gt;constructor EXPORTING previous = previous. CLEAR me-&gt;textid. IF textid IS INITIAL. if_t100_message~t100key = if_t100_message=&gt;default_textid. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD. ENDCLASS.</CODE></PRE><P>After creating this custom exception class, you can use your custom <STRONG>SE91 message class</STRONG> (for example, <STRONG>ZMSG_STATUS</STRONG>) to display meaningful validation messages directly in the Fiori application when the user clicks the <STRONG>Go</STRONG> button.</P><P>This approach gives you complete control over the read operation. You can validate user input, fetch data from any source, modify the retrieved data, implement custom business logic, and raise custom exceptions whenever required.</P> 2026-06-30T06:27:11.282000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/bypassing-the-list-report-in-sap-fiori-apps-bcmo-static-keys-and-dynamic/ba-p/14432559 Bypassing the List Report in SAP Fiori Apps: BCMO, Static Keys, and Dynamic Keys 2026-07-03T09:09:19.145000+02:00 SachinArtani https://community.sap.com/t5/user/viewprofilepage/user-id/168423 <P>If you've ever generated a Business Configuration Maintenance Object (BCMO) app or built a standard List Report Fiori app for a table with just one relevant record, you've probably run into the same annoyance: the app insists on showing you a list page first, even when there's really only one record, or one record per user, that anyone should ever land on.</P><P>In this post, I'll walk through the complete flow: generating a BCMO app for a custom table, getting rid of the pointless singleton list report, and then bypassing the list page in a regular List Report app using both a static key and a dynamic key, such as the logged-in username.</P><HR /><H2 id="toc-hId-1819077865">What is a BCMO App, and How Does It Relate to Table Maintenance Generator (TMG)?</H2><P>Before we jump into the how, a quick primer for anyone new to this.</P><P>A Table Maintenance Generator (TMG) is the classic SAP GUI way of generating a maintenance screen for a customizing table, using transaction SE11/SE54. You get a generated SM30 style screen where users can create, edit, and delete entries in a table.</P><P>BCMO, Business Configuration Maintenance Object, is essentially the RAP and Fiori based evolution of the same idea. Instead of generating an SM30 style dynpro, ADT generates a full RAP based application stack, behavior definition, behavior implementation, service definition, and service binding, for your custom table, and exposes it as a modern Fiori app. You get the same core purpose as TMG, maintaining configuration or master data in a table, but with a UI5 based List Report and Object Page instead of a classic table control screen.</P><P>So think of BCMO as: you want the SM30/TMG experience, but as a proper Fiori Elements app, generated automatically from your table.</P><P>You can access a BCMO app in two ways:</P><UL><LI>Through the Custom Business Configurations (CBC) app, where all the configuration and settings are automatically applied.</LI><LI>As a standalone app, published separately to your Fiori Launchpad or SAP Build Work Zone. In this case, the actual table maintenance appears on the object page, but you also get a singleton list report page in front of it, which is usually unnecessary.</LI></UL><P>That useless singleton list report is exactly what we're going to eliminate below.</P><HR /><H2 id="toc-hId-1622564360">Part 1: Creating a BCMO App</H2><H3 id="toc-hId-1555133574">Prerequisites on the Database Table</H3><P>Before ADT will let you generate a BCMO app, your custom table needs to satisfy a few conditions. If you skip these, generation will fail, and you'll typically see errors pointing you toward three things:</P><UL><LI>The table's delivery class needs to be set to C, since BCMO is meant for customizing tables, not regular application data.</LI><LI>Data maintenance needs to be explicitly allowed on the table, via the AbapCatalog.dataMaintenance annotation.</LI><LI>Character-like key fields need to be based on a domain, rather than a plain built-in type, so the generator can derive proper value help and type information.</LI></UL><P>Once these three conditions are met, ADT will happily generate the BCMO stack for you. Here's an example table definition that satisfies all of them:</P><PRE><CODE>@EndUserText.label : 'Product Industry Mapping' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #C @AbapCatalog.dataMaintenance : #ALLOWED define table zsac_t_prod_ind { key client : abap.clnt not null; key industry_sector : mbrsh not null; unit_of_measure : meins; currency_code : abap.cuky; }</CODE></PRE><H3 id="toc-hId-1358620069">Generating the BCMO Objects</H3><P>Right-click on the table in ADT, click Generate ABAP Repository Objects, select Business Configuration Maintenance Object, and click Next.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="1.png" style="width: 700px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428694iACC7E3B5CC189D70/image-size/large?v=v2&amp;px=999" role="button" title="1.png" alt="1.png" /></span></P><P>Enter the package.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="2.png" style="width: 832px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428695i4416A5937F671E9E/image-size/large?v=v2&amp;px=999" role="button" title="2.png" alt="2.png" /></span></P><P>In the Configure Generator step, you decide the naming of the generated objects and which features to enable. For this example, we'll set Transport Selection to No Transport and uncheck "Enable Transport Selection Strip" to keep things simple. Depending on your landscape, you may want this enabled instead.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="3.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428696iF9850A54BE31DE02/image-size/large?v=v2&amp;px=999" role="button" title="3.png" alt="3.png" /></span></P><P>Here's the full list of objects that get generated for us:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="4.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428697i478250E461C87C27/image-size/large?v=v2&amp;px=999" role="button" title="4.png" alt="4.png" /></span></P><P>After selecting your transport request, or choosing No Transport, click Finish, and sip a cup of tea while ADT does its thing.</P><P>Once generation completes, this is what you get in ADT:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="5.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428698iF0953DAFEA2E3601/image-size/large?v=v2&amp;px=999" role="button" title="5.png" alt="5.png" /></span></P><H3 id="toc-hId-1162106564">Accessing the BCMO App</H3><P>You can access your BCMO app in two ways:</P><OL><LI>Through the Custom Business Configurations (CBC) app, where all the standard settings are already applied for you.</LI><LI>As a standalone app, published separately to your SAP Fiori Launchpad or SAP Build Work Zone.</LI></OL><P>If you go the standalone route, the actual table maintenance will appear correctly on the object page, but you'll also get an unnecessary singleton list report page in front of it, which is exactly the problem we're solving in the next section.</P><P>Now open the service binding, publish it, and test it.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="6.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428699iEB670150E9C044EE/image-size/large?v=v2&amp;px=999" role="button" title="6.png" alt="6.png" /></span></P><H3 id="toc-hId-965593059">A Note on Authorizations</H3><P>You may notice that an access control object gets automatically generated as part of BCMO. If the authorization object S_TABU_NAM is not assigned to your user role, you may see no data in the output, or the Edit button may be disabled.</P><P>In that case, you have two options:</P><UL><LI>Delete the access control on your root entity, or</LI><LI>Assign the required authorizations to your user ID.</LI></UL><P>While testing this, I also had to comment out the global authorization check in the generated behavior implementation just to see data, due to missing authorizations in my BTP trial account. Obviously, don't do this in a real system, it's purely a local testing workaround.</P><HR /><H2 id="toc-hId-639996835">Part 2: Getting Rid of the Singleton List Report for a BCMO App</H2><P>Now that our BCMO app is ready, let's get rid of the singleton list report. It doesn't add any value, there's only ever one relevant "list," so why make the user click through it every time?</P><H3 id="toc-hId-572566049">Step 1: Create a New Project in BAS</H3><P>Go to SAP Business Application Studio and create a new project from the available templates.</P><P>Select SAP Fiori Generator.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="7.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428701iED4BA2F5765ED0C9/image-size/large?v=v2&amp;px=999" role="button" title="7.png" alt="7.png" /></span></P><P>Select List Report Page and click Next.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="8.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428702iE5902211E072B416/image-size/large?v=v2&amp;px=999" role="button" title="8.png" alt="8.png" /></span></P><P>Select the Data Source and Service.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="9.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428703iBB6F398408BD1751/image-size/large?v=v2&amp;px=999" role="button" title="9.png" alt="9.png" /></span></P><P>Note: If your system isn't logged in yet, log in first. If you're on an SAP BTP trial account, you'll need to connect BAS to Cloud Foundry before you can see the available Service Bindings.</P><P>To do that, open the top search bar and type:<BR /><BR /></P><PRE><CODE>&gt;CF: Login to Cloud Foundry</CODE></PRE><P>Log in with your credentials or SSO, then reopen the Data Source and Service Selection step.</P><P>Once logged in, you should now be able to see the service. Look for your Service Binding for the BCMO app and select it.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="10.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428704i52E6FACCEE61A22D/image-size/large?v=v2&amp;px=999" role="button" title="10.png" alt="10.png" /></span></P><P>Select the entity.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="11.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428705iB1972C4CDFA694B1/image-size/large?v=v2&amp;px=999" role="button" title="11.png" alt="11.png" /></span></P><P>If you're planning to deploy the app immediately, make sure to check "Add Deployment Configuration" and "Add SAP Fiori Launchpad Configuration." For this demo, I'm keeping both unchecked since we're only focused on getting rid of the list report page, not deployment.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="12.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428706iB0792DD831DCCF8E/image-size/large?v=v2&amp;px=999" role="button" title="12.png" alt="12.png" /></span></P><P>Click Finish, and go have another cup of tea while the project scaffolds.</P><H3 id="toc-hId-376052544">Step 2: Remove the List Report Route from the Manifest</H3><P>With the project created in BAS, let's get to the main task.</P><P>Open manifest.json under the webapp folder and remove the list report route and its corresponding target.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="13.png" style="width: 817px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428707iCA741D36DA52D998/image-size/large?v=v2&amp;px=999" role="button" title="13.png" alt="13.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="14.png" style="width: 827px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428708iC1C2D12C5CA77B6C/image-size/large?v=v2&amp;px=999" role="button" title="14.png" alt="14.png" /></span></P><P>We do this because the default routing configuration always points the app to the list report as the landing page first. By removing this route and target pair, we prevent the router from ever resolving to the list report on initial load, which sets us up to redirect straight to the object page instead.</P><H3 id="toc-hId-179539039">Step 3: Auto-Navigate to the Object Page in Component.js</H3><P>Now go to Component.js under the webapp folder and add the following code so the app routes directly to the object page of our singleton record whenever it loads:<BR /><BR /></P><PRE><CODE>init: function () { Component.prototype.init.apply(this, arguments); this.getRouter().navTo("ProductIndSingletonObjectPage", { key: "(1)" }, true); }</CODE></PRE><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="15.png" style="width: 980px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428709i93B07EE3597010E0/image-size/large?v=v2&amp;px=999" role="button" title="15.png" alt="15.png" /></span></P><H3 id="toc-hId--92205835">Step 4: Test It</H3><P>Run the following command in the terminal to preview the app:</P><PRE><CODE>npm run start</CODE></PRE><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="16.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428710i9D745DC7BA7F99C1/image-size/large?v=v2&amp;px=999" role="button" title="16.png" alt="16.png" /></span></P><P>And voilà, the first page that opens is no longer the singleton list report, but the actual object page we wanted users to land on directly.</P><P>From here, you can continue with deployment as per your requirement, including Fiori Launchpad configuration and transport.</P><HR /><H2 id="toc-hId-4683667">Part 3: Bypassing the List Report for a Standard List Report App</H2><P>The technique above works great for a true singleton, a table with essentially one record. But what if you're working with a regular List Report app on a table with multiple records, and you want to skip the list page in favor of a specific object page, based on a static key or a dynamic key like the logged-in user? Let's look at both.</P><H3 id="toc-hId--485232845">Deploying the Base List Report App</H3><P>As before, start by creating a new project in SAP BAS or VS Code, selecting SAP Fiori Application, then List Report as the template.</P><P>In the template wizard, select your Data Source, System, and Service, then click Next. Select the correct main entity and navigation entity, and choose Yes to automatically add table columns and a section to the object page.</P><P>Enter your project attribute details, choose Yes for deployment configuration, click Finish, and wait for the dependencies to install. Don't refresh the page while you see "Installing Dependencies" at the bottom right of VS Code.</P><P>Once deployed, remove the singleton list report the same way described above: strip the route and target from the manifest, and add the navTo call in Component.js.</P><P>The difference this time is what key you pass into that navTo call.</P><H3 id="toc-hId--681746350">Option A: Bypassing with a Static Key</H3><P>If you always want the app to open a specific, known record, hardcode the key directly:</P><PRE><CODE>init: function () { Component.prototype.init.apply(this, arguments); this.getRouter().navTo("TableGroupAllObjectPage", { key: "(1)" }, true); }</CODE></PRE><P>This is the simplest approach and works well when there's genuinely one record everyone should see, similar to the BCMO singleton pattern above, but applied manually to a regular List Report app.</P><H3 id="toc-hId--878259855">Option B: Bypassing with a Dynamic Key (Logged-in Username)</H3><P>Often, though, you don't want everyone landing on the same record. You want each user routed to their own record, based on who's logged in. For this, we fetch the current username first, then use it to construct the key dynamically before navigating.</P><PRE><CODE>init: function () { Component.prototype.init.apply(this, arguments); var sUser = sap.ushell.Container ? sap.ushell.Container.getUser().getId() : "DEFAULT_USER"; this.getRouter().navTo("TableGroupAllObjectPage", { key: "('" + sUser + "')" }, true); }</CODE></PRE><P>A few things to keep in mind with the dynamic key approach:</P><UL><LI>Make sure the key field in your entity actually corresponds to something like a username, or a field you can reliably derive from the logged-in user, for example, mapped via a custom table or CDS view.</LI><LI>If no record exists for that user yet, decide upfront how your app should behave: show an empty object page in create mode, show a "no data" message, or fall back to a default record.</LI><LI>Test this thoroughly with multiple user IDs before deploying, since the whole point is that different users see different landing records.</LI></UL><H3 id="toc-hId--1074773360">For Multi-Level Apps</H3><P>If your app has more than two navigation levels, for example, a list report page, then an object page showing a related sub-entity's list, then a further object page for that sub-entity's own details, you'll need to add another navigation node to the page map.</P><P>To do this, right-click on the app's folder in BAS and select Show Page Map. You'll see the existing levels, with a plus icon enabled on the deepest Object Page. Click it, select the correct navigation property, and add the new page.</P><P>Once your routing is set up correctly, deploy using:</P><PRE><CODE>npm run deploy</CODE></PRE><HR /><H2 id="toc-hId--977883858">Wrapping Up</H2><P>Whether you're working with a BCMO app generated straight from ADT, or a hand-built List Report Fiori app, the pattern for bypassing the list page is fundamentally the same:</P><OL><LI>Remove the list report's route and target from the manifest.</LI><LI>Add a navTo call in Component.js's init function that routes straight to the object page.</LI><LI>Decide whether that route needs a static key, same record for everyone, or a dynamic key, per-user record, typically derived from the logged-in username.</LI><LI>If you have deeper navigation levels, extend the page map accordingly before deploying.</LI></OL><P>This small change removes a genuinely pointless click for your end users, and it's one of those RAP and Fiori details that separates a generated-and-shipped app from a genuinely polished one.</P><P>If deployment configuration for RAP apps to SAP BTP is something you'd like a refresher on before trying this end to end, it's worth <A href="https://sachinartani.com/blog/how-to-deploy-sap-rap-app-to-sap-btp" target="_blank" rel="noopener nofollow noreferrer">reading up</A> on that step separately once you're ready to move beyond local testing.</P> 2026-07-03T09:09:19.145000+02:00 https://community.sap.com/t5/abap-blog-posts/how-to-scan-custom-abap-for-security-issues-in-eclipse-step-by-step/ba-p/14432681 How to Scan Custom ABAP for Security Issues in Eclipse (Step-by-Step) 2026-07-03T10:20:40.741000+02:00 vahagn https://community.sap.com/t5/user/viewprofilepage/user-id/760188 <P>Disclosure: I work on this tool at RedRays. This post is a straightforward setup guide, not a product pitch.</P><P>This post walks through connecting Eclipse to a static analysis backend that scans custom ABAP for common security issues (SQL injection, missing authorization checks, hard-coded credentials, weak crypto, and similar) and returns findings directly in the IDE.</P><P>Prerequisites</P><P>- Eclipse 2024-09 or newer<BR />- SAP ABAP Development Tools (ADT) installed<BR />- Java 17<BR />- Network access to the backend you plan to scan against (in this walkthrough, a demo instance)</P><P>Step 1: Get an API key</P><P>The plugin needs an API key to authenticate against a scan backend. For testing, there's a self-service demo instance:</P><P>1. Open get.abap-security.com.<BR />2. Enter a name and email address.<BR />3. Click Create my Eclipse API key.<BR />4. Copy the key immediately - it is displayed once.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="vahagn1_0-1783066920118.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428776i8BB52724E950C78B/image-size/large?v=v2&amp;px=999" role="button" title="vahagn1_0-1783066920118.png" alt="vahagn1_0-1783066920118.png" /></span></P><P>&nbsp;</P><P>Notes on the demo instance:<BR />- Limited to one key per IP address per time window.<BR />- It is a shared instance - do not submit confidential or production code to it. For real use, point the plugin at your own tenant or an on-premise instance instead (see "Beyond the demo" below).</P><P>Step 2: Install the plugin</P><P>In Eclipse:</P><P>1. Help → Install New Software…<BR />2. Click Add… and enter the update site: plugin.abap-security.com<BR />3. Select RedRays ABAP Scanner from the list.<BR />4. Finish the wizard and restart Eclipse when prompted.</P><P>Step 3: Configure the connection</P><P>1. Window → Preferences → RedRays Scanner<BR />2. Set Working mode to RedRays.<BR />3. Set RedRays URL to <A href="https://demo.abap-security.com:8443/" target="_blank" rel="noopener nofollow noreferrer">https://demo.abap-security.com:8443/</A> (or your own instance's URL).<BR />4. Paste the API key from Step 1 into RedRays API key.<BR />5. Click Test connection and confirm it succeeds before continuing.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="vahagn1_0-1783066782307.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428775iD8C8BE3FF61E2273/image-size/large?v=v2&amp;px=999" role="button" title="vahagn1_0-1783066782307.png" alt="vahagn1_0-1783066782307.png" /></span></P><P>&nbsp;</P><P>Step 4: Run a scan</P><P>1. Right-click any ABAP object in the Project Explorer.<BR />2. Select Scan with RedRays.<BR />3. Choose the Quick scan profile for a first run.<BR />4. Findings appear in a dedicated Eclipse view as the scan completes.</P><P>Reading the results</P><P>- Findings are grouped by severity: Critical, High, Medium, Low.<BR />- Double-clicking a finding opens the source at the exact line.<BR />- Each finding includes a CVSS score and an automated exploitability check, intended to reduce false positives that would otherwise need manual triage.</P><P>Categories currently covered include: SQL/ADBC injection, OS command execution, dynamic WHERE/ORDER BY clauses, path traversal on OPEN DATASET, RFC trust issues, missing AUTHORITY-CHECK, hard-coded credentials, and weak cryptographic algorithms (MD5/SHA-1), among others.</P><P>Beyond the demo</P><P>The same plugin can point at a privately provisioned tenant or an on-premise instance instead of the shared demo, with isolated access per subaccount and no source retention (code is scanned in memory and discarded; only findings are stored). There is also a REST API for scanning from CI/CD, including an endpoint that returns an allow/block decision for a transport based on a severity threshold - useful as a pre-import gate.</P><P>Resources</P><P>- Plugin overview: redrays.io/abap-scanner-eclipse-plugin<BR />- Demo / API key: get.abap-security.com</P> 2026-07-03T10:20:40.741000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/chat-based-rap-business-object-generation-in-vs-code/ba-p/14432950 Chat-based RAP Business Object Generation in VS Code 2026-07-03T14:28:10.192000+02:00 berin_ https://community.sap.com/t5/user/viewprofilepage/user-id/2148756 <P class="lia-align-justify" style="text-align : justify;">With ABAP Development Tools (ADT) for VS Code, you can now generate complete RAP Business Objects directly from chat prompts. Describe your business scenario in natural language, and GitHub Copilot generates the corresponding RAP artifacts in your ABAP system.</P><P class="lia-align-justify" style="text-align : justify;">The generated artifacts include:</P><UL class="lia-align-justify" style="text-align : justify;"><LI>Database tables</LI><LI>CDS views</LI><LI>Behavior definitions</LI><LI>Service definitions</LI><LI>Service bindings</LI></UL><P class="lia-align-justify" style="text-align : justify;">This feature is enabled by the extension’s built-in MCP (Model Context Protocol) server, which exposes ABAP backend APIs as tools that AI agents can invoke directly. This lets them create and connect the necessary RAP artifacts on your behalf.</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H1 id="toc-hId-1689998981">Getting Started</H1><P class="lia-align-justify" style="text-align : justify;"><SPAN>For more information about setting up ADT for VS Code, configuring GitHub Copilot Agent Mode, using the ADT MCP server, and troubleshooting common issues, refer to the <A href="https://github.com/SAP-samples/abap-platform-rap130/blob/main/README.md" target="_blank" rel="noopener nofollow noreferrer">guide</A>.</SPAN></P><H2 id="toc-hId-1622568195">Prerequisites:</H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>Before you begin, ensure that you have the following:</SPAN></P><UL class="lia-align-justify" style="text-align : justify;"><LI><STRONG><EM><SPAN>ABAP Development Tools for VS Code</SPAN></EM></STRONG> installed from the VS Code Marketplace</LI><LI>An ABAP system connection configured in the extension (steps shown below)</LI></UL><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 1. Adding an ABAP system to the workspace" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428814iA26D12D1C775F1E2/image-size/medium?v=v2&amp;px=400" role="button" title="berin__0-1783076596034.png" alt="Figure 1. Adding an ABAP system to the workspace" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 1. Adding an ABAP system to the workspace</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 2. Selecting a destination" style="width: 399px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428817i2D57F53B3DB2C8D7/image-size/medium?v=v2&amp;px=400" role="button" title="berin__1-1783076608986.png" alt="Figure 2. Selecting a destination" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 2. Selecting a destination</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 3. Log on with your credentials" style="width: 398px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428852i0916ED32C9877D69/image-size/medium?v=v2&amp;px=400" role="button" title="berin__16-1783077408364.png" alt="Figure 3. Log on with your credentials" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 3. Log on with your credentials</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><UL class="lia-align-justify" style="text-align : justify;"><LI>GitHub Copilot with Agent Mode enabled</LI></UL><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 4. GitHub Copilot in agent mode" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428823i75C7A3D92B21B241/image-size/medium?v=v2&amp;px=400" role="button" title="berin__3-1783076688817.png" alt="Figure 4. GitHub Copilot in agent mode" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 4. GitHub Copilot in agent mode</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-1426054690">Enable the ADT MCP Server</H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>First, verify that the ADT MCP server is enabled. To do so:</SPAN></P><OL class="lia-align-justify" style="text-align : justify;"><LI>Open the Settings and Search for <EM>ADT MCP Server </EM>in the search bar</LI><LI>Check the box <EM>Enable ADT MCP Server</EM></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 5. Enabling ADT MCP Server in settings" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428853iCE2B20E9556B7F70/image-size/medium?v=v2&amp;px=400" role="button" title="berin__17-1783077561214.png" alt="Figure 5. Enabling ADT MCP Server in settings" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 5. Enabling ADT MCP Server in settings</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-1229541185">Configuring ADT MCP Server</H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>The ADT MCP server is not automatically configured for GitHub Copilot</SPAN>. To configure the ADT MCP Server, open the Command Palette and run <STRONG>MCP: List Servers</STRONG>, then start the <EM>ADT MCP server</EM>.</P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 6. Listing the available MCP servers" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428854i5E07C43C59720096/image-size/medium?v=v2&amp;px=400" role="button" title="berin__18-1783077623409.png" alt="Figure 6. Listing the available MCP servers" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 6. Listing the available MCP servers</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 7. Starting the ADT MCP Server (part 1)" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428855i366AA1430A6BF97F/image-size/medium?v=v2&amp;px=400" role="button" title="berin__19-1783077648174.png" alt="Figure 7. Starting the ADT MCP Server (part 1)" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 7. Starting the ADT MCP Server (part 1)</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 8. Starting the ADT MCP Server (part 2)" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428858iF16051BC6453BC4D/image-size/medium?v=v2&amp;px=400" role="button" title="berin__21-1783077674662.png" alt="Figure 8. Starting the ADT MCP Server (part 2)" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 8. Starting the ADT MCP Server (part 2)</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">When the ADT MCP server is running and successfully connected, the following notification appears in the lower-right corner:</P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 9. Success message" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428859i25290912DD11AD68/image-size/medium?v=v2&amp;px=400" role="button" title="berin__22-1783077716163.png" alt="Figure 9. Success message" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 9. Success message</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">Once connected, the available ABAP MCP tools appear in GitHub Copilot. You can verify this by opening Configure Tools in the GitHub Copilot chat, where you can view and select the tools that are active for your session.</P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 10. Verifying the connection to the ABAP MCP Server (part 1)" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428860i06C3B90E749ADA1D/image-size/medium?v=v2&amp;px=400" role="button" title="berin__23-1783077743606.png" alt="Figure 10. Verifying the connection to the ABAP MCP Server (part 1)" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 10. Verifying the connection to the ABAP MCP Server (part 1)</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 11. Verifying the connection to the ADT MCP Server (part 2)" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428861iE6D1B620F54484ED/image-size/medium?v=v2&amp;px=400" role="button" title="berin__24-1783077755057.png" alt="Figure 11. Verifying the connection to the ADT MCP Server (part 2)" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 11. Verifying the connection to the ADT MCP Server (part 2)</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H4 id="toc-hId-1291193118"><SPAN>If MCP tools don't appear after setup please refer to </SPAN><SPAN><A href="https://github.com/SAP-samples/abap-platform-rap130/blob/main/exercises/ex01/README.md#troubleshooting" target="_blank" rel="noopener nofollow noreferrer">troubleshooting</A></SPAN></H4><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-836514175">Generating a RAP Business Object</H2><P class="lia-align-justify" style="text-align : justify;">Note: Before running the generation, ensure that your target package already exists. Currently, new packages must be created in ADT for Eclipse. Creating packages directly from ADT for VS Code will be supported in a future release.</P><P class="lia-align-justify" style="text-align : justify;">With the ADT MCP server running, open GitHub Copilot Chat in Agent Mode and describe the RAP Business Object you want to generate. For best results you can take a look at <SPAN><A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-for-visual-studio-code/best-practices-for-agent-prompts" target="_blank" rel="noopener noreferrer">agent prompt best practices</A></SPAN>.</P><P class="lia-align-justify" style="text-align : justify;">An example prompt would be:</P><P class="lia-align-justify" style="text-align : justify;">“ Generate a RAP Business Object for conference and tech event management, including a database table, CDS views,</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;behavior definition, and an OData V4 UI service.</P><UL class="lia-align-justify" style="text-align : justify;"><LI>Use the Package TEST_BB_CONFERENCE_MGT</LI><LI>Use the following specification:<UL><LI>Entity 1: Event (root entity)</LI><LI>Entity 2: Session, child of Event</LI><LI>Entity 3: Speaker, child of Event</LI><LI>Entity 4: Registration, child of Session“</LI></UL></LI></UL><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 12. Entering a prompt in the chat" style="width: 184px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428862i5851A702DA19A540/image-size/medium?v=v2&amp;px=400" role="button" title="berin__25-1783077880579.png" alt="Figure 12. Entering a prompt in the chat" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 12. Entering a prompt in the chat</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 13. GitHub Copilot response to the prompt" style="width: 185px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428870iE26A060FD99ABA9B/image-size/medium?v=v2&amp;px=400" role="button" title="berin__28-1783078038157.png" alt="Figure 13. GitHub Copilot response to the prompt" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 13. GitHub Copilot response to the prompt</span></span></P><P class="lia-align-justify" style="text-align : justify;">After your prompt, Copilot will:</P><UL class="lia-align-justify" style="text-align : justify;"><LI>List available destinations (abap_list_destinations)</LI><LI>Retrieve the schema (abap_generators-get_schema)</LI><LI>Validate (abap_creation-run_validation)</LI><LI>Create a transport request (abap_transport-create)</LI><LI>Generate all objects (abap_generators-generate_objects)</LI><LI>Activate objects (abap_activate_objects)</LI></UL><P class="lia-align-justify" style="text-align : justify;">After the generation completes, refresh your ABAP project by opening the Command Palette (Ctrl+Shift+P) and running ABAP: Refresh. The generated RAP artifacts will then appear in your workspace under the target package.</P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 14. RAP artifacts displayed under the defined package" style="width: 341px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428871i07256B5EE3C8398B/image-size/medium?v=v2&amp;px=400" role="button" title="berin__29-1783078129722.png" alt="Figure 14. RAP artifacts displayed under the defined package" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 14. RAP artifacts displayed under the defined package</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;"><SPAN>Once the service binding appears in your workspace, use the <STRONG>Publish</STRONG> CodeLens action to publish the service directly from VS Code. After publishing, the OData service is available for consumption.</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 15. Publishing the service binding" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428872i31C59B443B0200BD/image-size/medium?v=v2&amp;px=400" role="button" title="berin__30-1783078231073.png" alt="Figure 15. Publishing the service binding" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 15. Publishing the service binding</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 16. Success message" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428873iCD77E4458E6F9849/image-size/medium?v=v2&amp;px=400" role="button" title="berin__31-1783078249284.png" alt="Figure 16. Success message" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 16. Success message</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">Then, use the Preview CodeLens to launch a live preview of your Fiori elements application directly in your browser:</P><P class="lia-align-justify" style="text-align : justify;"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 17. Launching the application preview" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428874i9D37E0B80E49CE0E/image-size/medium?v=v2&amp;px=400" role="button" title="berin__32-1783078289398.png" alt="Figure 17. Launching the application preview" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 17. Launching the application preview</span></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Figure 18. Selecting the entity for preview" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428875i44D1E574149C7766/image-size/medium?v=v2&amp;px=400" role="button" title="berin__33-1783078324219.png" alt="Figure 18. Selecting the entity for preview" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 18. Selecting the entity for preview</span></span></P><P class="lia-align-justify" style="text-align : justify;"><SPAN>&nbsp;</SPAN></P><P class="lia-align-justify" style="text-align : justify;">Your generated application is then displayed in the browser:</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. Application preview" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428876i9F2D82B4254ADA33/image-size/medium?v=v2&amp;px=400" role="button" title="berin__34-1783078343105.png" alt="Figure 19. Application preview" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 19. Application preview</span></span></P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><P class="lia-align-justify" style="text-align : justify;">All generated artifacts are created directly in your ABAP system as standard repository objects. They are identical to the RAP artifacts you would create manually, fully editable in ADT for VS Code, and can be extended using the standard RAP development workflow.</P><P class="lia-align-justify" style="text-align : justify;">&nbsp;</P><H2 id="toc-hId-640000670">Summary</H2><P class="lia-align-justify" style="text-align : justify;"><SPAN>Chat-based RAP Business Object generation enables you to create complete RAP Business Objects directly from natural language prompts in ADT for VS Code. Combined with GitHub Copilot Agent Mode and the ADT MCP server, it automates the creation of the required RAP artifacts while keeping them fully editable as standard ABAP repository objects.</SPAN></P><P class="lia-align-justify" style="text-align : justify;">For more information, including setup instructions and configuration details, see the <SPAN><A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-for-visual-studio-code/abap-development-tools-for-visual-studio-code" target="_blank" rel="noopener noreferrer">ABAP Development Tools for Visual Studio Code | SAP Help Portal</A></SPAN></P> 2026-07-03T14:28:10.192000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/building-a-rap-application-with-external-sap-hana-cloud-using-cds-external/ba-p/14428145 Building a RAP Application with External SAP HANA Cloud using CDS External Entities – Part 1 2026-07-08T10:49:55.174000+02:00 Sivakumar_Subramaniam https://community.sap.com/t5/user/viewprofilepage/user-id/188613 <H1 id="toc-hId-1689246492">Why External Entities?</H1><P>CDS external entities provide a modern way to handle secondary database connections in ABAP Cloud. They allow applications to retrieve and modify data from other databases using SQL. Sounds interesting? This blog post covers how to create a RAP application in SAP BTP ABAP Environment&nbsp; with a secondary HANA database connection.</P><P>&nbsp;</P><H1 id="toc-hId-1492732987">Business scenario:</H1><P><BR /><SPAN>Many organizations already have business data stored in a separate </SPAN><STRONG>SAP HANA Cloud database</STRONG><SPAN> and want to build modern </SPAN><STRONG>RAP (ABAP RESTful Application Programming Model)</STRONG><SPAN> applications on SAP BTP ABAP Environment without replicating that data into the ABAP system.</SPAN></P><DIV><P>The blog demonstrates this using a <STRONG>Loyalty Management</STRONG> application.</P><H4 id="toc-hId-1683467639">Scenario</H4><UL><LI>Customer loyalty information (Memberships) is stored in an external SAP HANA Cloud database.</LI><LI>Transaction data related to each membership is also stored in the same external database.</LI><LI>The business wants a Fiori application where users can:<UL><LI>Create loyalty memberships</LI><LI>Maintain loyalty transactions</LI><LI>Update and delete records</LI><LI>View accumulated loyalty points</LI><LI>Persist all changes directly in the external HANA database</LI></UL></LI></UL></DIV><P><A href="https://community.sap.com/source-Ids-list" target="1_ytw4qqjq" rel="nofollow noopener noreferrer">&nbsp;</A></P><H1 id="toc-hId-1099705977">Prerequisites:</H1><OL><LI>SAP BTP Sub-Account</LI><LI>SAP HANA Cloud Instance</LI><LI>SAP BTP ABAP Environment</LI></OL><H1 id="toc-hId-903192472">Steps:</H1><OL><LI>Setup&nbsp;SAP&nbsp;HANA Cloud instance</LI><LI>Create Schema and Tables</LI><LI>Configure external connection to External HANA database from SAP BTP ABAP Environment using External Entities</LI><LI>Create External Entities</LI></OL><H1 id="toc-hId-706678967">Setup SAP HANA Cloud instance</H1><P>Let’s start on with the creation of <A href="https://help.sap.com/docs/hana-cloud/sap-hana-cloud-administration-guide/subscribing-to-sap-hana-cloud-administration-tools" target="_blank" rel="noopener noreferrer">SAP HANA Cloud instance</A>.</P><OL><LI>Open SAP BTP&nbsp;cockpit and add the following Entitlements.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sivakumar_Subramaniam_1-1784629294230.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/435449iC66B60271A4BCA58/image-size/large?v=v2&amp;px=999" role="button" title="Sivakumar_Subramaniam_1-1784629294230.png" alt="Sivakumar_Subramaniam_1-1784629294230.png" /></span><BR /><BR /><BR /></LI><LI>Create HANA cloud subscription.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="2.jpg" style="width: 800px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427802iC14D37786CA6E788/image-size/large?v=v2&amp;px=999" role="button" title="2.jpg" alt="2.jpg" /></span><BR /><BR /></LI><LI>Create HANA Cloud Instance<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="3.jpg" style="width: 800px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427804iBB423502FA834C80/image-size/large?v=v2&amp;px=999" role="button" title="3.jpg" alt="3.jpg" /></span><BR /><BR />Specify Instance name, Set DBADMIN password and choose Allow all&nbsp;IP&nbsp;address<BR /><div class="lia-spoiler-container"><a class="lia-spoiler-link" href="#" rel="nofollow noopener noreferrer">Spoiler</a><noscript> (Highlight to read)</noscript><div class="lia-spoiler-border"><div class="lia-spoiler-content"><STRONG>Disclaimer on Database IP Access Configuration</STRONG><BR />Allowing unrestricted access (e.g., permitting all IP addresses such as 0.0.0.0/0) to a database that contains production data introduces significant security risks. This approach should only be used in strictly controlled environments.</div><noscript><div class="lia-spoiler-noscript-container"><div class="lia-spoiler-noscript-content">Disclaimer on Database IP Access ConfigurationAllowing unrestricted access (e.g., permitting all IP addresses such as 0.0.0.0/0) to a database that contains production data introduces significant security risks. This approach should only be used in strictly controlled environments.</div></div></noscript></div></div><DIV><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="4.jpg" style="width: 800px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427806i71BE31BF802E68C1/image-size/large?v=v2&amp;px=999" role="button" title="4.jpg" alt="4.jpg" /></span></DIV><BR /><BR /></LI><LI>Once the Instance status turns into Running state and click on Actions three dots and&nbsp;Open in&nbsp;SAP&nbsp;HANA Database Explorer.&nbsp;<BR />Provide the DBADMIN credentials from the previous step to connect to the HANA database.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="5.jpg" style="width: 800px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427807iD85EE7115FA464A0/image-size/large?v=v2&amp;px=999" role="button" title="5.jpg" alt="5.jpg" /></span><BR /><BR /></LI></OL><P class="lia-indent-padding-left-30px" style="padding-left : 30px;">&nbsp;</P><H1 id="toc-hId-510165462">Create Schema and Tables<BR /><BR /></H1><DIV>Open SQL Console and perform the following steps to create the schema and tables.</DIV><UL><LI>Create Schema<BR /><BR /></LI></UL><pre class="lia-code-sample language-sql"><code>CREATE SCHEMA LOYALTY;​</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="6.jpg" style="width: 801px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427810i35D6C0266FEEC4C1/image-size/large?v=v2&amp;px=999" role="button" title="6.jpg" alt="6.jpg" /></span></P><UL><LI>Create Membership table: zlm_membship_ee <SPAN>&nbsp;.</SPAN></LI></UL><P class="lia-indent-padding-left-90px" style="padding-left : 90px;">Stores membership details<BR />Primary key: client + membershipuuid</P><pre class="lia-code-sample language-sql"><code>create column table loyalty."zlm_membship_ee" ( "client" nvarchar (000003) default '000' not null, "membershipuuid" varbinary (000016) default x'00000000000000000000000000000000' not null , "membershipid" nvarchar (000010) default '0000000000' not null, "customer" nvarchar (000010) default '' not null, "createdby" nvarchar (000012) default '' not null, "createdat" decimal (000021, 000007) default 0 not null, "lastchangedby" nvarchar (000012) default '' not null, "locallastchangedat" decimal (000021, 000007) default 0 not null, "lastchangedat" decimal (000021, 000007) default 0 not null, primary key ("client", "membershipuuid" ) ) column loadable ​</code></pre><UL><LI>Create Transactions table: zlm_transctns_ee</LI></UL><P class="lia-indent-padding-left-90px" style="padding-left : 90px;">Stores transactions linked to membership.<BR />Contains transaction value and loyalty points.</P><pre class="lia-code-sample language-sql"><code>create column table loyalty."zlm_transctns_ee" ( "client" nvarchar (000003) default '000' not null, "transactionuuid" varbinary (000016) default x'00000000000000000000000000000000' not null , "membershipuuid" varbinary (000016) default x'00000000000000000000000000000000' not null , "transactiondate" NVARCHAR (000008) DEFAULT '00000000' NOT NULL, "transactionvalue" DECIMAL (000010, 000002) DEFAULT 0 NOT NULL, "transactioncurrency" NVARCHAR (000005) DEFAULT '' NOT NULL, "loyaltypoints" BIGINT DEFAULT 0 NOT NULL, "createdby" nvarchar (000012) default '' not null, "createdat" decimal (000021, 000007) default 0 not null, "lastchangedby" nvarchar (000012) default '' not null, "locallastchangedat" decimal (000021, 000007) default 0 not null, "lastchangedat" decimal (000021, 000007) default 0 not null, primary key ("client", "transactionuuid" ) ) column loadable ​</code></pre><H1 id="toc-hId-313651957"><BR />Configure connectivity to an external SAP HANA database from Steampunk for data access using External Entities<BR /><BR /></H1><OL><LI>Establish outbound SQL access using an external entity using ABAP Development Tools for Eclipse<BR /><BR /><OL class="lia-list-style-type-lower-roman"><LI>Define and activate a logical external schema whose configuration specifies the connection details for the outbound communication. Create a <A href="https://help.sap.com/docs/ABAP_PLATFORM_NEW/c238d694b825421f940829321ffa326a/cfaa37a4c0054c5cad00c03f7774a7ac.html" target="_blank" rel="noopener noreferrer">logical external schema</A> in ABAP Development Tools (ADT) using the creation wizard.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="7.jpg" style="width: 801px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427911i20CE33AAB5695F36/image-size/large?v=v2&amp;px=999" role="button" title="7.jpg" alt="7.jpg" /></span><BR /><BR /></LI><LI>&nbsp;Create an Outbound Service of the type Outbound SQL Access for your logical external schema.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="8.jpg" style="width: 799px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427912iF67DAB3CD7A5D5CC/image-size/large?v=v2&amp;px=999" role="button" title="8.jpg" alt="8.jpg" /></span><BR /><BR /></LI><LI>Create a Communication Scenario.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="9.jpg" style="width: 801px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427913iF26524F7C38B4E00/image-size/large?v=v2&amp;px=999" role="button" title="9.jpg" alt="9.jpg" /></span><BR /><BR /></LI><LI>Include the outbound service in the communication scenario.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="10.jpg" style="width: 801px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427914i8F7176169F86D171/image-size/large?v=v2&amp;px=999" role="button" title="10.jpg" alt="10.jpg" /></span><BR /><BR /></LI></OL></LI><LI>Configure External Connection<BR />In the Fiori Launchpad, Logon as Administrator and perform the following steps.<OL class="lia-list-style-type-lower-roman"><LI>Create a Communication Arrangement for scenario ZLM_COM_EXT_SCHEMA_EE.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="11.jpg" style="width: 800px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427915i88E761244CA7D646/image-size/large?v=v2&amp;px=999" role="button" title="11.jpg" alt="11.jpg" /></span><BR /><BR /><BR /><BR /></LI><LI>&nbsp;Create a Communication System<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="12.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427916i778ED34E800C4909/image-size/large?v=v2&amp;px=999" role="button" title="12.jpg" alt="12.jpg" /></span><BR /><BR /><BR />Toggle Remote SQL Access and choose Adapter Name as HANA (ODBC)&nbsp;<BR />Adapter Configuration :Driver=<A href="http://libodbchdb.so/" target="_blank" rel="noopener nofollow noreferrer">libodbcHDB.so</A>;ServerNode=<A href="https://*.hanacloud.ondemand.com/" target="_blank" rel="noopener nofollow noreferrer">*.hanacloud.ondemand.com</A>(URL of the External Database):443;<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="13.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427917i64AB6D94EF3E270D/image-size/large?v=v2&amp;px=999" role="button" title="13.jpg" alt="13.jpg" /></span><BR /><BR /><BR /></LI><LI>Create an Outbound User. Specify the HANA DB user which contains the necessary authorizations for accessing the External Schema.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="14.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/427918i91602AC106B25A84/image-size/large?v=v2&amp;px=999" role="button" title="14.jpg" alt="14.jpg" /></span><BR /><BR /><BR /><BR /></LI></OL></LI></OL><H1 id="toc-hId-117138452">Create CDS External Entities</H1><P>Two types:<BR />1. Writable External Entity → Used for INSERT/UPDATE/DELETE<BR />2. Static External Entity → Used for SELECT (read only)<BR /><BR />These entities map external tables to ABAP CDS.</P><P>In ABAP Development Tools for Eclipse: As an ABAP Developer, here’s what you need to do to establish outbound SQL access using a CDS external entity.</P><UL class="lia-list-style-type-lower-roman"><LI>Define and activate an external entity that represents the external database object.As we are going to perform CUD operation on external database,&nbsp;we need to create Writable External Entity for table zlm_membship_ee which will be referred by external name zlm_membship_ee.</LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Writable EE for HANA tab zlm_membship_ee' define writable external entity zlm_membship_cud_ee external name "zlm_membship_ee" { key client : abap.char(3) external name "client"; key membershipuuid : sysuuid_x16 external name "membershipuuid"; membershipid : zlm_membershipid_ee external name "membershipid"; customer : zlm_customer_ee external name "customer"; @Semantics.user.createdBy: true createdby : abp_creation_user external name "createdby"; @Semantics.systemDateTime.createdAt: true createdat : abp_creation_tstmpl external name "createdat"; @Semantics.user.lastChangedBy: true lastchangedby : abp_lastchange_user external name "lastchangedby"; @Semantics.systemDateTime.localInstanceLastChangedAt: true locallastchangedat : abp_locinst_lastchange_tstmpl external name "locallastchangedat"; @Semantics.systemDateTime.lastChangedAt: true lastchangedat : abp_lastchange_tstmpl external name "lastchangedat"; } with federated data provided at runtime ​ ​</code></pre><UL class="lia-list-style-type-lower-roman"><LI>We need to create Writable External Entity for table zlm_transctns_ee</LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Writable EE for HANAtab zlm_transctns_ee' define writable external entity zlm_transctns_cud_ee external name "zlm_transctns_ee" { key client : abap.char(3) external name "client"; key transactionuuid : sysuuid_x16 external name "transactionuuid"; membershipuuid : sysuuid_x16 external name "membershipuuid"; transactiondate : zlm_transactiondate_ee external name "transactiondate"; @Semantics.amount.currencyCode : 'transactioncurrency' transactionvalue : zlm_transactionvalue_ee external name "transactionvalue"; transactioncurrency : zlm_transactioncurrency_ee external name "transactioncurrency"; loyaltypoints : zlm_loyaltypoints_ee external name "loyaltypoints"; @Semantics.user.createdBy: true createdby : abp_creation_user external name "createdby"; @Semantics.systemDateTime.createdAt: true createdat : abp_creation_tstmpl external name "createdat"; @Semantics.user.lastChangedBy: true lastchangedby : abp_lastchange_user external name "lastchangedby"; @Semantics.systemDateTime.localInstanceLastChangedAt: true locallastchangedat : abp_locinst_lastchange_tstmpl external name "locallastchangedat"; @Semantics.systemDateTime.lastChangedAt: true lastchangedat : abp_lastchange_tstmpl external name "lastchangedat"; } with federated data provided at runtime ​ ​</code></pre><UL><LI>To securely map a remote physical database object so it can be queried directly, we need to use STATIC EXTERNAL ENTITIES to fetch data from table zlm_membship_ee .</LI></UL><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Static EE for HANA tab zlm_membship_ee' define external entity zlm_membship_ee external name "zlm_membship_ee" { key client : abap.char(3) external name "client"; key membershipuuid : sysuuid_x16 external name "membershipuuid"; membershipid : zlm_membershipid_ee external name "membershipid"; customer : zlm_customer_ee external name "customer"; @Semantics.user.createdBy: true createdby : abp_creation_user external name "createdby"; @Semantics.systemDateTime.createdAt: true createdat : abp_creation_tstmpl external name "createdat"; @Semantics.user.lastChangedBy: true lastchangedby : abp_lastchange_user external name "lastchangedby"; @Semantics.systemDateTime.localInstanceLastChangedAt: true locallastchangedat : abp_locinst_lastchange_tstmpl external name "locallastchangedat"; @Semantics.systemDateTime.lastChangedAt: true lastchangedat : abp_lastchange_tstmpl external name "lastchangedat"; } with federated data provided by zlm_ext_schema_ee ​ ​</code></pre><UL><LI>We need another STATIC EXTERNAL ENTITY for table&nbsp;zlm_transctns_ee</LI></UL><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Static EE for HANA tab zlm_transctns_ee' define external entity zlm_transctns_ee external name "zlm_transctns_ee" { key client : abap.char(3) external name "client"; key transactionuuid : sysuuid_x16 external name "transactionuuid"; membershipuuid : sysuuid_x16 external name "membershipuuid"; transactiondate : zlm_transactiondate_ee external name "transactiondate"; @Semantics.amount.currencyCode : 'transactioncurrency' transactionvalue : zlm_transactionvalue_ee external name "transactionvalue"; transactioncurrency : zlm_transactioncurrency_ee external name "transactioncurrency"; loyaltypoints : zlm_loyaltypoints_ee external name "loyaltypoints"; @Semantics.user.createdBy: true createdby : abp_creation_user external name "createdby"; @Semantics.systemDateTime.createdAt: true createdat : abp_creation_tstmpl external name "createdat"; @Semantics.user.lastChangedBy: true lastchangedby : abp_lastchange_user external name "lastchangedby"; @Semantics.systemDateTime.localInstanceLastChangedAt: true locallastchangedat : abp_locinst_lastchange_tstmpl external name "locallastchangedat"; @Semantics.systemDateTime.lastChangedAt: true lastchangedat : abp_lastchange_tstmpl external name "lastchangedat"; } with federated data provided by zlm_ext_schema_ee ​</code></pre><H1 id="toc-hId--79375053">Key Takeaways.</H1><P>External entities enable real-time integration with external databases without replication.<BR /><BR /></P><DIV>In <A title="Building a RAP Application with External SAP HANA Cloud using CDS External Entities – Part 2" href="https://community.sap.com/t5/technology-blog-posts-by-sap/building-a-rap-application-with-external-sap-hana-cloud-using-cds-external/ba-p/14430950" target="_self">Part 2</A> of this series, we will build a RAP application using the data model created here.</DIV><P>&nbsp;</P><H1 id="toc-hId-493851525">&nbsp;</H1><H1 id="toc-hId-297338020">&nbsp;</H1><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2026-07-08T10:49:55.174000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/building-a-rap-application-with-external-sap-hana-cloud-using-cds-external/ba-p/14430950 Building a RAP Application with External SAP HANA Cloud using CDS External Entities – Part 2 2026-07-08T10:50:41.923000+02:00 Sivakumar_Subramaniam https://community.sap.com/t5/user/viewprofilepage/user-id/188613 <H1 id="toc-hId-1492732987" id="toc-hId-1689939399">Business scenario:</H1><P><SPAN>Many organizations already have business data stored in a separate&nbsp;</SPAN><STRONG>SAP HANA Cloud database</STRONG><SPAN>&nbsp;and want to build modern&nbsp;</SPAN><STRONG>RAP (ABAP RESTful Application Programming Model)</STRONG><SPAN>&nbsp;applications on SAP BTP ABAP Environment without replicating that data into the ABAP system.</SPAN></P><DIV><P>The blog demonstrates this using a<SPAN>&nbsp;</SPAN><STRONG>Loyalty Management</STRONG><SPAN>&nbsp;</SPAN>application.</P><H4 id="toc-hId-1683467639" id="toc-hId-1880674051">Scenario</H4><UL><LI>Customer loyalty information (Memberships) is stored in an external SAP HANA Cloud database.</LI><LI>Transaction data related to each membership is also stored in the same external database.</LI><LI>The business wants a Fiori application where users can:<UL><LI>Create loyalty memberships</LI><LI>Maintain loyalty transactions</LI><LI>Update and delete records</LI><LI>View accumulated loyalty points</LI><LI>Persist all changes directly in the external HANA database</LI></UL></LI></UL></DIV><P><A href="https://community.sap.com/source-Ids-list" target="1_ytw4qqjq" rel="nofollow noopener noreferrer">&nbsp;</A></P><H1 id="toc-hId-1296912389"><BR />Prerequisites</H1><P><A title="Building a RAP Application with External SAP HANA Cloud using CDS External Entities – Part 1" href="https://community.sap.com/t5/technology-blog-posts-by-sap/building-a-rap-application-with-external-sap-hana-cloud-using-cds-external/ba-p/14428145" target="_self">Building a RAP Application with External SAP HANA Cloud using CDS External Entities – Part 1</A></P><H1 id="toc-hId-1100398884">Build CDS Data Model</H1><P>For creating Loyalty Management Application, we need to have customer master data. For our reference scenario and to show only required fields, we will create a custom CDS entity over I_BusinessPartner .</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Value help for Business Partner' @Metadata.ignorePropagatedAnnotations: true define view entity zlm_i_businesspartner_vh as select from I_BusinessPartner as BusinessPartner { @ObjectModel.text.element: [ 'BusinessPartnerName' ] @Consumption.filter.hidden: true key BusinessPartner.BusinessPartner, @ui.hidden: true BusinessPartnerCategory, @EndUserText.label: 'Business Partner Name' @search.defaultSearchElement: true @search.fuzzinessThreshold: 0.8 @search.ranking: #HIGH BusinessPartner.BusinessPartnerName }</code></pre><P>Ceate root entity (Membership) and child entity (Transactions).<BR />Add composition:&nbsp;&nbsp;One membership can have multiple transactions.<BR />Use associations for linking data like Business Partner.</P><P>Create root entitity zlm_r_membship_ee</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'CDS on Static EE for Loyalty Membership' define root view entity zlm_r_membship_ee as select from zlm_membship_ee composition [1..*] of zlm_r_transctns_ee as _MembershipTransactions association [1..1] to zlm_i_businesspartner_vh as _BusinessPartner on $projection.Customer = _BusinessPartner.BusinessPartner { key membershipuuid as Membershipuuid, membershipid as Membershipid, customer as Customer, createdby as Createdby, createdat as Createdat, lastchangedby as Lastchangedby, locallastchangedat as Locallastchangedat, lastchangedat as Lastchangedat, _BusinessPartner, _MembershipTransactions } where zlm_membship_ee.client = $session.client</code></pre><P>Save the cds zlm_r_membship_ee and don’t activate.<BR />Create Child Entity zlm_r_transctns_ee.</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'CDS on Static EE for transactions' define view entity zlm_r_transctns_ee as select from zlm_transctns_ee association to parent zlm_r_membship_ee as _LoyaltyMembership on $projection.Membershipuuid = _LoyaltyMembership.Membershipuuid { key transactionuuid as Transactionuuid, membershipuuid as Membershipuuid, transactiondate as Transactiondate, @Semantics.amount.currencyCode : 'transactioncurrency' transactionvalue as Transactionvalue, transactioncurrency as Transactioncurrency, loyaltypoints as Loyaltypoints, createdby as Createdby, createdat as Createdat, lastchangedby as Lastchangedby, locallastchangedat as Locallastchangedat, lastchangedat as Lastchangedat, _LoyaltyMembership } where zlm_transctns_ee.client = $session.client</code></pre><P>Save and activate both the entities using "<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sivakumar_Subramaniam_0-1782879649619.png" style="width: 44px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428008i8F273E0B696DA78E/image-dimensions/44x44?v=v2" width="44" height="44" role="button" title="Sivakumar_Subramaniam_0-1782879649619.png" alt="Sivakumar_Subramaniam_0-1782879649619.png" /></span>Activate inactive development objects".&nbsp;<BR />To calculate the loyalty points at runtime along with draft mode, we create the following CDS view entities.</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Total Loyalty Points-Without draft' @Metadata.ignorePropagatedAnnotations: true define view entity zlm_c_loyaltypoints_total_ee as select from zlm_r_transctns_ee { key Membershipuuid, sum ( Loyaltypoints ) as Total } group by zlm_r_transctns_ee.Membershipuuid</code></pre><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Loyalty Pts in draft-existing Membership' @Metadata.ignorePropagatedAnnotations: true define view entity zlm_c_loyaltypoints_dfttot_ee as select from zlm_trans_ee_d { key membershipuuid, sum ( loyaltypoints ) as Total } group by zlm_trans_ee_d.membershipuuid</code></pre><DIV>Merge the CDS views and compute the total loyalty points per membership.</DIV><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Effective Loyalty Pts per Membership' @Metadata.ignorePropagatedAnnotations: true define view entity zlm_c_membship_loyaltypts_ee as select from zlm_c_loyaltypoints_total_ee as actual left outer join zlm_c_loyaltypoints_dfttot_ee as draft on actual.Membershipuuid = draft.membershipuuid { key actual.Membershipuuid as Membershipuuid, coalesce ( draft.Total, actual.Total ) as Total } union all select from zlm_c_loyaltypoints_dfttot_ee as draft left outer join zlm_c_loyaltypoints_total_ee as actual on draft.membershipuuid = actual.Membershipuuid { key draft.membershipuuid as Membershipuuid, draft.Total as Total } where actual.Membershipuuid is null;</code></pre><H1 id="toc-hId-903885379">Define Behaviour</H1><P>Enable CRUD operations:<BR />- Create<BR />- Update<BR />- Delete<BR />Add determination logic:<BR />- Automatically calculate loyalty points when transaction value changes.</P><P>To perform CRUD operations, we need to define corresponding behaviour definitions and implementations for root and child entities.In ABAP Development Tools for eclipse, right click on zlm_r_membship_ee and create behaviour definition.</P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_lm_membship_ee unique; strict ( 2 ); with draft; define behavior for zlm_r_membship_ee alias LoyaltyMembership draft table zlm_memb_ee_d lock master total etag Lastchangedat authorization master ( instance ) etag master Locallastchangedat { create ( precheck , authorization : global ); update; delete; field ( numbering : managed ) Membershipuuid; field ( readonly ) Membershipuuid; draft action Activate optimized; draft action Discard; draft action Edit; draft action Resume; draft determine action Prepare; association _MembershipTransactions { create{default function GetDefaultsForCBA;} with draft; } } define behavior for zlm_r_transctns_ee alias MembershipTransactions draft table zlm_trans_ee_d lock dependent by _LoyaltyMembership authorization dependent by _LoyaltyMembership etag master Locallastchangedat { update; delete; field ( numbering : managed ) Transactionuuid; field ( readonly ) Transactionuuid, Membershipuuid; //calculate loyalty points when a transaction is created or relevant fields are modified determination CalculateLoyaltyPoints on modify { create; field Transactionvalue; } side effects { field Transactionvalue affects field Loyaltypoints, entity _LoyaltyMembership; } association _LoyaltyMembership { with draft; } }</code></pre><H1 id="toc-hId-707371874"><BR />Implement Business Logic</H1><P>Create number range object ZLM_MID_EE via ADT and by using Fiori application “Manage Number range Intervals” , maintain the number range.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="1.jpg" style="width: 800px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428078iF0A412F7DDA1CC38/image-size/large?v=v2&amp;px=999" role="button" title="1.jpg" alt="1.jpg" /></span></P><P>Create message class ZLM_MESSAGES_EE and add the following messages.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="2.jpg" style="width: 799px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428080i8DFAD13938E3C999/image-size/large?v=v2&amp;px=999" role="button" title="2.jpg" alt="2.jpg" /></span><BR />Create Interface zlm_if_constants_ee for maintaining constants.</P><pre class="lia-code-sample language-abap"><code>INTERFACE zlm_if_constants_ee PUBLIC . CONSTANTS: nr_obj_value TYPE cl_numberrange_runtime=&gt;nr_object VALUE 'ZLM_MID_EE', nr_range_nr TYPE cl_numberrange_runtime=&gt;nr_interval VALUE '01', draft TYPE abp_behv_flag VALUE '00', cid TYPE abp_behv_cid VALUE '01'. ENDINTERFACE.</code></pre><P>Create class zcx_lm_messages_ee to handle exceptions in the application.</P><pre class="lia-code-sample language-abap"><code>CLASS zcx_lm_messages_ee DEFINITION PUBLIC INHERITING FROM cx_static_check FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: if_t100_message, if_abap_behv_message. CONSTANTS: message_class TYPE symsgid VALUE 'ZLM_MESSAGES_EE', BEGIN OF extdb_cud_failure, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE '', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_cud_failure, BEGIN OF extdb_mcrt_success, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '002', attr1 TYPE scx_attrname VALUE 'MV_MEMBERSHIPID', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_mcrt_success, BEGIN OF extdb_mupd_success, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '003', attr1 TYPE scx_attrname VALUE 'MV_MEMBERSHIPID', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_mupd_success, BEGIN OF extdb_mdel_success, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '004', attr1 TYPE scx_attrname VALUE 'MV_MEMBERSHIPID', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_mdel_success, BEGIN OF extdb_tcrt_success, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '005', attr1 TYPE scx_attrname VALUE 'MV_LOYALTYPOINTS', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_tcrt_success, BEGIN OF extdb_tupd_success, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '006', attr1 TYPE scx_attrname VALUE 'MV_LOYALTYPOINTS', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_tupd_success, BEGIN OF extdb_tdel_success, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '007', attr1 TYPE scx_attrname VALUE '', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_tdel_success, BEGIN OF extdb_mdel_failure, msgid TYPE symsgid VALUE message_class, msgno TYPE symsgno VALUE '008', attr1 TYPE scx_attrname VALUE '', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF extdb_mdel_failure. DATA: mv_membershipid TYPE zlm_membershipid_ee, mv_loyaltypoints TYPE zlm_loyaltypoints_ee. METHODS: constructor IMPORTING textid LIKE if_t100_message=&gt;t100key OPTIONAL previous LIKE previous OPTIONAL severity TYPE if_abap_behv_message=&gt;t_severity DEFAULT if_abap_behv_message=&gt;severity-error membershipid TYPE zlm_membershipid_ee OPTIONAL loyaltypoints TYPE zlm_loyaltypoints_ee OPTIONAL PREFERRED PARAMETER textid . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcx_lm_messages_ee IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. super-&gt;constructor( previous = previous ). me-&gt;if_abap_behv_message~m_severity = severity. me-&gt;mv_membershipid = membershipid. me-&gt;mv_loyaltypoints = loyaltypoints. CLEAR me-&gt;textid. IF textid IS INITIAL. if_t100_message~t100key = if_t100_message=&gt;default_textid. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD. ENDCLASS.</code></pre><P>Behaviour Implementation.</P><pre class="lia-code-sample language-abap"><code>CLASS zbp_lm_membship_ee DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zlm_r_membship_ee. ENDCLASS. CLASS ZBP_LM_MEMBSHIP_EE IMPLEMENTATION. ENDCLASS.</code></pre><P>In the local types tab of class <SPAN>zbp_lm_membship_ee</SPAN>, add the following .</P><pre class="lia-code-sample language-abap"><code>CLASS lcl_buffer DEFINITION CREATE PRIVATE. PUBLIC SECTION. TYPES: membship_crt TYPE TABLE FOR CREATE zlm_c_membship_ee. TYPES: transactns_crt TYPE TABLE FOR CREATE zlm_c_membship_ee\_membershiptransactions. TYPES: membship_upd TYPE TABLE FOR UPDATE zlm_c_membship_ee. TYPES: transactns_upd TYPE TABLE FOR UPDATE zlm_c_transctns_ee. TYPES: membship_del TYPE TABLE FOR DELETE zlm_c_membship_ee. TYPES: transactns_del TYPE TABLE FOR DELETE zlm_c_transctns_ee. CLASS-METHODS get_instance RETURNING VALUE(ro_instance) TYPE REF TO lcl_buffer. DATA create_memberships TYPE membship_crt READ-ONLY. DATA create_transactions TYPE transactns_crt READ-ONLY. DATA update_memberships TYPE membship_upd READ-ONLY. DATA update_transactions TYPE transactns_upd READ-ONLY. DATA delete_memberships TYPE membship_del READ-ONLY. DATA delete_transactions TYPE transactns_del READ-ONLY. METHODS set_create_memberships IMPORTING memberships TYPE membship_crt. METHODS set_create_transactions IMPORTING transactions TYPE transactns_crt. METHODS set_update_memberships IMPORTING memberships TYPE membship_upd. METHODS set_update_transactions IMPORTING transactions TYPE transactns_upd. METHODS set_delete_memberships IMPORTING memberships TYPE membship_del. METHODS set_delete_transactions IMPORTING transactions TYPE transactns_del. PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO lcl_buffer. ENDCLASS. CLASS lcl_buffer IMPLEMENTATION. METHOD get_instance. IF go_instance IS NOT BOUND. go_instance = NEW #( ). ENDIF. ro_instance = go_instance. ENDMETHOD. METHOD set_create_memberships. me-&gt;create_memberships = CORRESPONDING #( memberships ). ENDMETHOD. METHOD set_create_transactions. me-&gt;create_transactions = CORRESPONDING #( transactions ) . ENDMETHOD. METHOD set_delete_memberships. me-&gt;delete_memberships = CORRESPONDING #( memberships ). ENDMETHOD. METHOD set_delete_transactions. me-&gt;delete_transactions = CORRESPONDING #( transactions ). ENDMETHOD. METHOD set_update_memberships. me-&gt;update_memberships = CORRESPONDING #( memberships ). ENDMETHOD. METHOD set_update_transactions. me-&gt;update_transactions = CORRESPONDING #( transactions ). LOOP AT me-&gt;update_transactions ASSIGNING FIELD-SYMBOL(&lt;upd_tran&gt;). &lt;upd_tran&gt;-Lastchangedby = sy-uname. &lt;upd_tran&gt;-Lastchangedat = &lt;upd_tran&gt;-Locallastchangedat. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS lhc_loyaltymembership DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR loyaltymembership RESULT result. METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION IMPORTING REQUEST requested_authorizations FOR loyaltymembership RESULT result. METHODS create FOR MODIFY IMPORTING entities FOR CREATE loyaltymembership. METHODS update FOR MODIFY IMPORTING entities FOR UPDATE loyaltymembership. METHODS delete FOR MODIFY IMPORTING keys FOR DELETE loyaltymembership. METHODS read FOR READ IMPORTING keys FOR READ loyaltymembership RESULT result. METHODS lock FOR LOCK IMPORTING keys FOR LOCK loyaltymembership. METHODS rba_membershiptransactions FOR READ IMPORTING keys_rba FOR READ loyaltymembership\_membershiptransactions FULL result_requested RESULT result LINK association_links. METHODS cba_membershiptransactions FOR MODIFY IMPORTING entities_cba FOR CREATE loyaltymembership\_membershiptransactions. METHODS getdefaultsforcba FOR READ IMPORTING keys FOR FUNCTION loyaltymembership~getdefaultsforcba RESULT result. METHODS precheck_create FOR PRECHECK IMPORTING entities FOR CREATE loyaltymembership. ENDCLASS. CLASS lhc_loyaltymembership IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD get_global_authorizations. ENDMETHOD. METHOD create. lcl_buffer=&gt;get_instance( )-&gt;set_create_memberships( memberships = CORRESPONDING #( entities ) ). mapped-loyaltymembership = CORRESPONDING #( lcl_buffer=&gt;get_instance( )-&gt;create_memberships ). ENDMETHOD. METHOD update. lcl_buffer=&gt;get_instance( )-&gt;set_update_memberships( memberships = CORRESPONDING #( entities ) ). mapped-loyaltymembership = CORRESPONDING #( lcl_buffer=&gt;get_instance( )-&gt;update_memberships ). ENDMETHOD. METHOD delete. lcl_buffer=&gt;get_instance( )-&gt;set_delete_memberships( memberships = CORRESPONDING #( keys ) ). ENDMETHOD. METHOD read. DATA lylmem_uuids TYPE RANGE OF xsduuid_raw. lylmem_uuids = VALUE #( FOR key IN keys LET s = 'I' o = 'EQ' IN sign = s option = o ( low = key-Membershipuuid ) ). SELECT FROM zlm_r_membship_ee FIELDS * WHERE "Clnt = @SY-mandt AND Membershipuuid IN @lylmem_uuids INTO TABLE @DATA(memberships) . result = CORRESPONDING #( memberships MAPPING Membershipuuid = Membershipuuid Membershipid = Membershipid createdat = Createdat createdby = Createdby lastchangedat = Lastchangedat lastchangedby = Lastchangedby Locallastchangedat = Locallastchangedat ). ENDMETHOD. METHOD lock. ENDMETHOD. METHOD rba_membershiptransactions. DATA lylmem_uuids TYPE RANGE OF xsduuid_raw. lylmem_uuids = VALUE #( FOR key IN keys_rba LET s = 'I' o = 'EQ' IN sign = s option = o ( low = key-Membershipuuid ) ). SELECT FROM zlm_r_transctns_ee FIELDS * WHERE Membershipuuid IN @lylmem_uuids INTO TABLE @DATA(transactions) . result = CORRESPONDING #( transactions MAPPING Transactionuuid = Transactionuuid Membershipuuid = Membershipuuid Transactiondate = Transactiondate Transactionvalue = Transactionvalue Transactioncurrency = Transactioncurrency Loyaltypoints = Loyaltypoints createdat = Createdat createdby = Createdby lastchangedat = Lastchangedat lastchangedby = Lastchangedby Locallastchangedat = Locallastchangedat ). ENDMETHOD. METHOD cba_membershiptransactions. lcl_buffer=&gt;get_instance( )-&gt;set_create_transactions( transactions = CORRESPONDING #( entities_cba MAPPING %target = %target ) ). mapped-membershiptransactions = CORRESPONDING #( lcl_buffer=&gt;get_instance( )-&gt;create_transactions ). ENDMETHOD. METHOD GetDefaultsForCBA. result = VALUE #( FOR key IN keys ( %tky = key-%tky %param-Transactiondate = cl_abap_context_info=&gt;get_system_date( ) %param-Transactioncurrency = 'INR' ) ). ENDMETHOD. METHOD precheck_create. LOOP AT entities INTO DATA(ls_entity). "-------------------------------------------- " Duplicate Check "-------------------------------------------- SELECT SINGLE FROM zlm_R_membship_ee FIELDS Membershipid , Customer WHERE Customer = @LS_entity-Customer INTO @DATA(membership) . IF membership IS NOT INITIAL. APPEND VALUE #( %key = ls_entity-%key %msg = new_message( id = 'ZLM_MESSAGES_EE' number = '009' v1 = membership-Membershipid v2 = membership-Customer severity = if_abap_behv_message=&gt;severity-error ) ) TO reported-loyaltymembership. APPEND VALUE #( %key = ls_entity-%key ) TO failed-loyaltymembership. CONTINUE. ENDIF. CLEAR membership. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS lhc_membershiptransactions DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS update FOR MODIFY IMPORTING entities FOR UPDATE membershiptransactions. METHODS delete FOR MODIFY IMPORTING keys FOR DELETE membershiptransactions. METHODS read FOR READ IMPORTING keys FOR READ membershiptransactions RESULT result. METHODS rba_loyaltymembership FOR READ IMPORTING keys_rba FOR READ membershiptransactions\_loyaltymembership FULL result_requested RESULT result LINK association_links. METHODS calculateloyaltypoints FOR DETERMINE ON MODIFY IMPORTING keys FOR membershiptransactions~calculateloyaltypoints. ENDCLASS. CLASS lhc_membershiptransactions IMPLEMENTATION. METHOD update. lcl_buffer=&gt;get_instance( )-&gt;set_update_transactions( transactions = CORRESPONDING #( entities ) ). mapped-membershiptransactions = CORRESPONDING #( lcl_buffer=&gt;get_instance( )-&gt;update_transactions ). ENDMETHOD. METHOD delete. lcl_buffer=&gt;get_instance( )-&gt;set_delete_transactions( transactions = CORRESPONDING #( keys ) ). ENDMETHOD. METHOD read. ENDMETHOD. METHOD rba_loyaltymembership. ENDMETHOD. METHOD CalculateLoyaltyPoints. " Calculates loyalty points for a transaction based on its value, then updates the Loyaltypoints field in MembershipTransactions. DATA updateTransactions TYPE TABLE FOR UPDATE zlm_r_transctns_ee. " Read all relevant transaction instances. READ ENTITIES OF zlm_r_membship_ee IN LOCAL MODE ENTITY MembershipTransactions FIELDS ( Transactionuuid Membershipuuid Transactionvalue Loyaltypoints ) WITH CORRESPONDING #( keys ) RESULT DATA(lylpointstransactions) FAILED DATA(failed). IF lylpointstransactions IS NOT INITIAL. lylpointstransactions[ 1 ]-Loyaltypoints = lylpointstransactions[ 1 ]-Transactionvalue * '0.10' . updateTransactions = CORRESPONDING #( lylpointstransactions ). MODIFY ENTITIES OF zlm_r_membship_ee IN LOCAL MODE ENTITY MembershipTransactions UPDATE FIELDS ( Loyaltypoints ) WITH updateTransactions MAPPED DATA(updated_transactions) FAILED DATA(update_failed) REPORTED DATA(update_reported). IF reported IS NOT INITIAL OR update_failed IS NOT INITIAL. *Fill reported reported = CORRESPONDING #( DEEP update_reported ). *Set failed keys APPEND VALUE #( %tky = lylpointstransactions[ 1 ]-%tky ) TO update_failed-membershiptransactions. ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS lsc_zlm_r_membship_ee 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_zlm_r_membship_ee IMPLEMENTATION. METHOD finalize. ENDMETHOD. METHOD check_before_save. ENDMETHOD. METHOD save. DATA(create_memberships) = lcl_buffer=&gt;get_instance( )-&gt;create_memberships. DATA(create_transactions) = lcl_buffer=&gt;get_instance( )-&gt;create_transactions. DATA(update_memberships) = lcl_buffer=&gt;get_instance( )-&gt;update_memberships. DATA(update_transactions) = lcl_buffer=&gt;get_instance( )-&gt;update_transactions. DATA(delete_memberships) = lcl_buffer=&gt;get_instance( )-&gt;delete_memberships. DATA(delete_transactions) = lcl_buffer=&gt;get_instance( )-&gt;delete_transactions. IF create_memberships IS INITIAL AND create_transactions IS INITIAL AND update_memberships IS INITIAL AND update_transactions IS INITIAL AND delete_memberships IS INITIAL AND delete_transactions IS INITIAL. RETURN. ENDIF. TRY. FINAL(ext_con) = cl_abap_sql_connection_builder=&gt;write_enabled_4_logical_schema( i_connection_name = 'R/3*SERVICE' i_logical_schema_name = 'ZLM_EXT_SCHEMA_EE' )-&gt;create( ). IF create_memberships IS NOT INITIAL. " Request sequential numbers from Number Range Object TRY. cl_numberrange_runtime=&gt;number_get( EXPORTING nr_range_nr = zlm_if_constants_ee=&gt;nr_range_nr object = zlm_if_constants_ee=&gt;nr_obj_value quantity = CONV #( lines( create_memberships ) ) IMPORTING number = DATA(number_range_key) ). CATCH cx_number_ranges INTO DATA(nr_allocation_failed). INSERT VALUE #( %msg = new_message_with_text( severity = if_abap_behv_message=&gt;severity-error text = nr_allocation_failed-&gt;get_text( ) ) ) INTO TABLE reported-loyaltymembership. EXIT. ENDTRY. INSERT zlm_membship_cud_ee PROVIDED BY zlm_ext_schema_ee CONNECTION @ext_con FROM TABLE @( VALUE #( FOR memb IN create_memberships ( client = sy-mandt membershipuuid = memb-membershipuuid membershipid = number_range_key "memb-membershipid customer = memb-customer createdby = memb-createdby createdat = memb-createdat lastchangedby = memb-lastchangedby lastchangedat = memb-lastchangedat locallastchangedat = memb-locallastchangedat ) ) ). IF sy-subrc = 0. reported-loyaltymembership = CORRESPONDING #( create_memberships ). LOOP AT reported-loyaltymembership ASSIGNING FIELD-SYMBOL(&lt;mem&gt;). DATA(membershipuuid) = &lt;mem&gt;-membershipuuid . &lt;mem&gt;-%msg = NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_mcrt_success severity = if_abap_behv_message=&gt;severity-success membershipid = CONV #( number_range_key ) )."create_memberships[ KEY entity COMPONENTS Membershipuuid = &lt;mem&gt;-membershipuuid ]-Membershipid ). ENDLOOP. ELSE. reported-%other = VALUE #( ( NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_cud_failure severity = if_abap_behv_message=&gt;severity-error ) ) ). RAISE EXCEPTION TYPE zcx_lm_messages_ee. ENDIF. ENDIF. IF update_memberships IS NOT INITIAL. UPDATE zlm_membship_cud_ee PROVIDED BY zlm_ext_schema_ee CONNECTION @ext_con FROM TABLE @( VALUE #( FOR memb_upd IN update_memberships ( lastchangedby = memb_upd-lastchangedby lastchangedat = memb_upd-lastchangedat locallastchangedat = memb_upd-locallastchangedat ) ) ). IF sy-subrc = 0. reported-loyaltymembership = CORRESPONDING #( update_memberships ). LOOP AT reported-loyaltymembership ASSIGNING FIELD-SYMBOL(&lt;mem_upd&gt;). membershipuuid = &lt;mem_upd&gt;-membershipuuid. &lt;mem_upd&gt;-%msg = NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_mupd_success severity = if_abap_behv_message=&gt;severity-success membershipid = update_memberships[ KEY entity membershipuuid = &lt;mem_upd&gt;-membershipuuid ]-membershipid ). ENDLOOP. ELSE. reported-%other = VALUE #( ( NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_cud_failure severity = if_abap_behv_message=&gt;severity-error ) ) ). RAISE EXCEPTION TYPE zcx_lm_messages_ee. . ENDIF. ENDIF. IF delete_memberships IS NOT INITIAL. DELETE zlm_membship_cud_ee PROVIDED BY zlm_ext_schema_ee CONNECTION @ext_con FROM TABLE @( VALUE #( FOR memb_del IN delete_memberships ( client = sy-mandt membershipuuid = memb_del-membershipuuid ) ) ). IF sy-subrc = 0. reported-loyaltymembership = CORRESPONDING #( delete_memberships ). LOOP AT reported-loyaltymembership ASSIGNING FIELD-SYMBOL(&lt;mem_del&gt;). &lt;mem_del&gt;-%msg = NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_mdel_success severity = if_abap_behv_message=&gt;severity-success ). ENDLOOP. ELSE. reported-%other = VALUE #( ( NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_cud_failure severity = if_abap_behv_message=&gt;severity-error ) ) ). RAISE EXCEPTION TYPE zcx_lm_messages_ee. ENDIF. ENDIF. IF create_transactions IS NOT INITIAL. INSERT zlm_transctns_cud_ee PROVIDED BY zlm_ext_schema_ee CONNECTION @ext_con FROM TABLE @( VALUE #( FOR txns IN create_transactions FOR txn IN txns-%target ( client = sy-mandt transactionuuid = txn-transactionuuid membershipuuid = txn-membershipuuid transactiondate = txn-transactiondate transactionvalue = txn-transactionvalue transactioncurrency = txn-transactioncurrency loyaltypoints = txn-loyaltypoints createdby = txn-createdby createdat = txn-createdat lastchangedby = txn-lastchangedby locallastchangedat = txn-locallastchangedat lastchangedat = txn-lastchangedat ) ) ). IF sy-subrc = 0. LOOP AT create_transactions ASSIGNING FIELD-SYMBOL(&lt;txns&gt;). LOOP AT &lt;txns&gt;-%target ASSIGNING FIELD-SYMBOL(&lt;txn&gt;). membershipuuid = &lt;txn&gt;-membershipuuid. reported-membershiptransactions = VALUE #( BASE reported-membershiptransactions ( Transactionuuid = &lt;txn&gt;-Transactionuuid %msg = NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_tcrt_success severity = if_abap_behv_message=&gt;severity-success loyaltypoints = &lt;txn&gt;-Loyaltypoints ) ) ). ENDLOOP. ENDLOOP. ELSE. reported-%other = VALUE #( ( NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_cud_failure severity = if_abap_behv_message=&gt;severity-error ) ) ). RAISE EXCEPTION TYPE zcx_lm_messages_ee. ENDIF. ENDIF. IF update_transactions IS NOT INITIAL. UPDATE zlm_transctns_cud_ee PROVIDED BY zlm_ext_schema_ee CONNECTION @ext_con FROM TABLE @( VALUE #( FOR txns_upd IN update_transactions ( client = sy-mandt transactionuuid = txns_upd-transactionuuid membershipuuid = txns_upd-membershipuuid transactiondate = txns_upd-transactiondate transactionvalue = txns_upd-transactionvalue transactioncurrency = txns_upd-transactioncurrency loyaltypoints = txns_upd-loyaltypoints createdby = txns_upd-createdby createdat = txns_upd-createdat lastchangedby = txns_upd-lastchangedby locallastchangedat = txns_upd-locallastchangedat lastchangedat = txns_upd-lastchangedat ) ) ). IF sy-subrc = 0. LOOP AT update_transactions ASSIGNING FIELD-SYMBOL(&lt;txn_upd&gt;). reported-membershiptransactions = VALUE #( BASE reported-membershiptransactions ( Transactionuuid = &lt;txn_upd&gt;-Transactionuuid %msg = NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_tupd_success severity = if_abap_behv_message=&gt;severity-success loyaltypoints = &lt;txn_upd&gt;-Loyaltypoints ) ) ). ENDLOOP. ELSE. reported-%other = VALUE #( ( NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_cud_failure severity = if_abap_behv_message=&gt;severity-error ) ) ). RAISE EXCEPTION TYPE zcx_lm_messages_ee. ENDIF. ENDIF. IF delete_transactions IS NOT INITIAL. DELETE zlm_transctns_cud_ee PROVIDED BY zlm_ext_schema_ee CONNECTION @ext_con FROM TABLE @( VALUE #( FOR trans_del IN delete_transactions ( client = sy-mandt transactionuuid = trans_del-transactionuuid ) ) ). IF sy-subrc = 0. LOOP AT delete_transactions ASSIGNING FIELD-SYMBOL(&lt;txn_del&gt;). reported-membershiptransactions = VALUE #( BASE reported-membershiptransactions ( Transactionuuid = &lt;txn_del&gt;-Transactionuuid %msg = NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_tdel_success severity = if_abap_behv_message=&gt;severity-success ) ) ). ENDLOOP. ELSE. reported-%other = VALUE #( ( NEW zcx_lm_messages_ee( textid = zcx_lm_messages_ee=&gt;extdb_cud_failure severity = if_abap_behv_message=&gt;severity-error ) ) ). RAISE EXCEPTION TYPE zcx_lm_messages_ee. ENDIF. ENDIF. COMMIT CONNECTION @ext_con. ext_con-&gt;close( ). CATCH cx_root INTO FINAL(exc). APPEND VALUE #( %msg = new_message_with_text( severity = if_abap_behv_message=&gt;severity-error text = exc-&gt;get_text( ) ) ) TO reported-loyaltymembership. ENDTRY. READ ENTITIES OF zlm_r_membship_ee IN LOCAL MODE ENTITY LoyaltyMembership ALL FIELDS WITH VALUE #( ( membershipuuid = membershipuuid ) ) RESULT DATA(loyaltymembership). ENDMETHOD. METHOD cleanup. ENDMETHOD. METHOD cleanup_finalize. ENDMETHOD. ENDCLASS.</code></pre><H1 id="toc-hId-510858369">Projection Layer</H1><P>Create projection views for UI consumption</P><P>Purpose:</P><P>- Simplify backend data model</P><P>- Expose only required fields<BR /><BR />We now create the corresponding consumption view zlm_c_membship_ee .</P><pre class="lia-code-sample language-abap"><code>@Metadata.allowExtensions: true @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection View' define root view entity zlm_c_membship_ee provider contract transactional_query as projection on zlm_r_membship_ee { key Membershipuuid, Membershipid, Customer, _BusinessPartner.BusinessPartnerName as CustomerName, _Loyaltypoints.total as Loyaltypoints, Createdby, Createdat, Lastchangedby, Locallastchangedat, Lastchangedat, /* Associations */ _MembershipTransactions : redirected to composition child zlm_c_transctns_ee }</code></pre><P>Save the cds entity zlm_c_membship_ee&nbsp; and don’t activate.</P><P>Create consumption view zlm_c_transctns_ee.</P><pre class="lia-code-sample language-abap"><code>@Metadata.allowExtensions: true @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection View' define view entity zlm_c_transctns_ee as projection on zlm_r_transctns_ee { key Transactionuuid, Membershipuuid, Transactiondate, @Semantics.amount.currencyCode : 'Transactioncurrency' Transactionvalue, @Consumption.valueHelpDefinition: [{ entity: { name: 'I_Currency', element: 'Currency' }, useForValidation: true }] Transactioncurrency, Loyaltypoints, Createdby, Createdat, Lastchangedby, Locallastchangedat, Lastchangedat, /* Associations */ _LoyaltyMembership : redirected to parent zlm_c_membship_ee // Make association public }</code></pre><P>Save and activate both the entities using “<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Icon.png" style="width: 36px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428118iFE3F47A8084D2877/image-size/large?v=v2&amp;px=999" role="button" title="Icon.png" alt="Icon.png" /></span>Activate inactive ABAP development objects”.</P><P>We now create behaviour projection zlm_c_membship_ee .</P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); use draft; use side effects; define behavior for zlm_c_membship_ee alias LoyaltyMembership { use create; use update; use delete; field ( readonly ) Membershipuuid,Membershipid; field (readonly:update) Customer; use action Activate; use action Discard; use action Edit ; use action Resume; use action Prepare; //use association _MembershipTransactions { create; with draft;} use association _MembershipTransactions { create ; with draft; } use function GetDefaultsForCBA; } define behavior for zlm_c_transctns_ee alias MembershipTransactions { use update; use delete; field ( readonly ) Loyaltypoints; use association _LoyaltyMembership { with draft;} }</code></pre><H1 id="toc-hId-314344864">UI Annotations</H1><P>Add annotations:<BR />- <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem → table columns<BR />- <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification → object page fields<BR />- @Consumption.valueHelp → dropdown values</P><P>These control Fiori UI behavior.<BR />Create&nbsp;Metadata extension for zlm_c_membship_ee .</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE @Search.searchable: true @UI: { headerInfo: { typeName: 'Loyalty Management', typeNamePlural: 'Loyalty Management ', title: { type: #STANDARD, label: 'Loyalty Management', value: 'Membershipid' }}, presentationVariant: [{ sortOrder: [{ by: 'Membershipid', direction: #ASC }] }] } annotate view zlm_c_membship_ee with { @ui.facet: [ { id: 'LoyaltyMembership', purpose: #STANDARD, label: 'Membership Details', type: #IDENTIFICATION_REFERENCE, position: 10 }, { id: 'Transactions', label: 'Transactions', type: #LINEITEM_REFERENCE, position: 20, targetElement: '_MembershipTransactions' }] @ui.hidden: true Membershipuuid; @ui: { identification : [ { position: 10, label: 'Membership ID' } ], lineItem : [ { position: 10, label: 'Membership ID' }] } Membershipid; @ui: { identification : [ { position: 20, label: 'Customer ID' } ], lineItem : [ { position: 20, label: 'Customer ID' } ] } @Consumption.valueHelpDefinition: [{ entity: { name: 'zlm_i_businesspartner_vh' , element: 'BusinessPartner' } }] Customer; @ui: { identification : [ { position: 30, label: 'Customer Name' } ], lineItem : [ { position: 30, label: 'Customer Name' } ], selectionField : [ { position: 30 } ] } @search.defaultSearchElement: true CustomerName; @ui: { identification : [ { position: 40, label: 'Loyalty Points' } ], lineItem : [ { position: 40, label: 'Loyalty Points' } ], selectionField : [ { position: 40 } ] } Loyaltypoints; @ui.hidden: true Createdby; @ui.hidden: true Createdat; @ui.hidden: true Lastchangedby; @ui.hidden: true Locallastchangedat; @ui.hidden: true Lastchangedat; }</code></pre><P><BR />Metadata extension for zlm_c_transctns_ee.</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE @UI: { headerInfo: { typeName: 'Loyalty Membership Transaction', typeNamePlural: 'Loyalty Membership Transactions', title: { type: #STANDARD, label: 'Loyalty Membership Transactions', value: 'Transactiondate' } } } annotate entity zlm_c_transctns_ee with { @ui.facet: [ { id: 'Transactions', purpose: #STANDARD, label: 'Transactions', type: #IDENTIFICATION_REFERENCE } ] @ui.hidden: true Transactionuuid; @ui.hidden: true Membershipuuid; @ui.identification: [ { position: 10 , label: 'Transaction date' } ] @ui.lineItem: [ { position: 10 , label: 'Transaction date' } ] @ui.selectionField: [ { position: 10 } ] Transactiondate; @ui.identification: [ { position: 20 , label: 'Transaction value' } ] @ui.lineItem: [ { position: 20 , label: 'Transaction value' } ] @ui.selectionField: [ { position: 20 } ] Transactionvalue; @ui.identification: [ { position: 30 , label: 'Loyalty points' } ] @ui.lineItem: [ { position: 30 , label: 'Loyalty points' } ] @ui.selectionField: [ { position: 30 } ] Loyaltypoints; @ui.hidden: true Createdby; @ui.hidden: true Createdat; @ui.hidden: true Lastchangedby; @ui.hidden: true Locallastchangedat; @ui.hidden: true Lastchangedat; }</code></pre><H1 id="toc-hId-117831359">Service Definition &amp; Binding</H1><P>Create OData V4 service:<BR />- Expose projection views<BR />- Create service binding<BR />- Publish service<BR /><BR />This enables UI consumption.</P><P>Create service definition and service binding.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'OData V4 UI loyalty membership' define service ZLY_UI_MEMBERSHIP_EE_O4 { expose zlm_c_membship_ee as LoyaltyMembership; expose zlm_c_transctns_ee as MembershipTransactions; }</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="3.jpg" style="width: 800px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/428130i1DF549388D6A12DC/image-size/large?v=v2&amp;px=999" role="button" title="3.jpg" alt="3.jpg" /></span></P><P>&nbsp;</P><H1 id="toc-hId--78682146">Testing</H1><OL><LI>Open binding</LI><LI>Launch Fiori app</LI><LI>Create membership</LI><LI>&nbsp;Add transaction</LI><LI>&nbsp;Verify loyalty points calculation</LI><LI>Check that data is saved in external HANA database.</LI></OL><H1 id="toc-hId-494544432">End-to-End Flow</H1><OL><LI>User enters data in UI</LI><LI>RAP behavior triggers</LI><LI>Data stored temporarily in buffer</LI><LI>Save method writes to external DB</LI><LI>Commit completes transaction</LI><LI>UI displays updated data</LI></OL><H1 id="toc-hId-298030927">Key Takeaways.</H1><OL><LI>RAP model separates data, behavior, and UI</LI><LI>Logical schema handles secure connectivity</LI><LI>Business logic is implemented in ABAP classes</LI><LI>No data replication required</LI></OL><H2 id="toc-hId--549970305" id="toc-hId--191885585">Further info</H2><UL><LI><A href="https://help.sap.com/docs/abap-cloud/abap-data-models/cds-external-entities?version=s4hana_cloud" rel="noopener noreferrer" target="_blank">External Entities | SAP Help Portal</A></LI><LI><A href="https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/ABENCDS_EXTERNAL_ENTITY.html" rel="noopener noreferrer" target="_blank">External Entities (ABAP Keyword Documentation)</A></LI><LI><A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/working-with-logical-external-schemas?version=sap_btp" target="_self" rel="noreferrer noopener">Working with Logical External Schemas | SAP Help Portal</A></LI><LI><A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/outbound-services?version=s4hana_cloud" rel="noopener noreferrer" target="_blank">Working with Outbound Services | SAP Help Portal</A></LI><LI><A href="https://help.sap.com/docs/sap-btp-abap-environment/abap-environment/about-communication-management?version=Cloud" target="_self" rel="noreferrer noopener">Communication Management | SAP Help Portal</A></LI></UL><P>&nbsp;</P> 2026-07-08T10:50:41.923000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/start-remote-process-actions-in-btp-abap-via-task-chains-from-sap/ba-p/14440248 Start remote Process/Actions in BTP ABAP via Task Chains from SAP Datasphere 2026-07-14T16:00:19.634000+02:00 stefan_geiselhart2 https://community.sap.com/t5/user/viewprofilepage/user-id/200897 <P>Modern cloud architectures allow for versatile and open interoperability. So does SAP Datasphere and SAP BTP ABAP, too. This example outlines some essentials how Datasphere can kick-off actions defined on the ABAP Cloud side, beyond system boundaries.</P><P>Starting a Task/Process in BTP ABAP remotely from SAP Datasphere using Task Chains opens up new scenarios for interoperability and integration. The goal is to orchestrate remote tasks&nbsp;initiated by SAP Datasphere — securely, programmatically, and fully automated.</P><P><U>We will cover the below points:</U></P><UL><LI>Creating a Task Chain in Datasphere including an API Call to ABAP Cloud</LI><LI>Connection in Datasphere to ABAP Cloud (for direct API call)</LI><LI>Setting up Communication System &amp; Arrangement in ABAP Cloud</LI><LI>Executing the Task Chain in Datasphere: Trigger actions in ABAP Cloud</LI></UL><P>&nbsp;</P><H1 id="toc-hId-1690856170">1 Architecture Overview</H1><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Architecture" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432878i8F8239641A83E39B/image-size/large?v=v2&amp;px=999" role="button" title="abap_dsp_integration.png" alt="Architecture" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Architecture</span></span></P><P>The SAP Datasphere&nbsp;Task Chain API Task: Datasphere acts as the caller. ABAP Cloud exposes an endpoint (inbound Web Api) to trigger an action.</P><P>Option (2) and (3) complete the picture of possible solution approaches. Main differences as well as pros &amp; cons are outlined further down in one of the last sections.</P><P><I>The Solution Architecture displays 3 different options:</I></P><UL><LI><STRONG>(1) Use Direct API Integration</STRONG>&nbsp;</LI><UL><LI>Straightforward</LI><LI>Minimal cost and complexity</LI><LI>Perfect for nightly batch orchestration, event-driven data loads, or simple API-driven analytical data refresh</LI></UL><LI><STRONG>(2) Integration Suite</STRONG></LI><UL><LI>Integration involves complex message transformations, protocol mediation, multiple target systems, or when centralized API governance and monitoring across the enterprise are required.</LI><LI>Especially appropriate where iFlow reusability justifies the additional cost and setup effort or when integration suite is already in place and a common tool.</LI></UL><LI><STRONG>(3) CAP Application </STRONG></LI><UL><LI>Custom application logic is needed beyond what a direct API call/iFlow can provide</LI><LI>E.g. multi-source data aggregation, advanced parsing, enrichments</LI><LI>Additional persistence required</LI><LI>Well-suited for reusable microservice-style integrations on BTP</LI></UL></UL><P>&nbsp;</P><H1 id="toc-hId-1494342665">2 Prerequisites</H1><P>You need</P><OL><LI>SAP Datasphere tenant with developer + admin access</LI><LI>Task Chain to design the flow of events</LI><LI>SAP BTP ABAP Environment (ABAP Cloud) with developer access</LI><LI>BTP ABAP: Authorization for OAuth clients, Communication Arrangements, ABAP development of Web APIs</LI></OL><P>&nbsp;</P><H1 id="toc-hId-1297829160">&nbsp;3 Creating Communication Artifacts in ABAP Cloud</H1><P><I>Navigation:&nbsp;</I>ABAP Environment Web Access → Communication Management → Communication Systems/Arrangements</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AC01.png" style="width: 671px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432907iB6C9E9BC32BA5CD0/image-size/large?v=v2&amp;px=999" role="button" title="AC01.png" alt="AC01.png" /></span></P><P>Communication Arrangement</P><P>&nbsp;</P><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Communication Arrangement" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432919iAEFA982FB72455BD/image-size/large?v=v2&amp;px=999" role="button" title="AC_arr_02.png" alt="Communication Arrangement" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Communication Arrangement</span></span></DIV><P>Communication System</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="comm_system2.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/433139i221029C9DC96B65D/image-size/large?v=v2&amp;px=999" role="button" title="comm_system2.png" alt="comm_system2.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Communication System 1" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432920i37EA414883B4DF96/image-size/large?v=v2&amp;px=999" role="button" title="AC_cs_03.png" alt="Communication System 1" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Communication System 1</span></span></P><P><STRONG>Communication/Connection User:</STRONG></P><P>Create a User for Inbound Communication and use the same in the subsequent step on Datasphere level for the creation of a generic HTTP connection.</P><P>&nbsp;</P><H1 id="toc-hId-1101315655">4 ABAP Cloud Artifacts</H1><P>For the sake of simplicity I won't go into the details of all of the development artifacts and configuration settings.</P><P>For sure, you will create:</P><UL><LI>Inbound Service</LI><LI>Service Binding</LI><LI>Communication Scenario BTP ABAP</LI><LI>Service Definition</LI><LI>Entity</LI><LI>Class implementation</LI></UL><P>An example of an action definition processData (ZI_DEMO_SERVICE):</P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_demo_service unique; define behavior for ZI_DEMO_SERVICE alias DemoService persistent table zdemoservice lock master { field ( readonly ) Id; field ( numbering : managed ) Id; create; update; delete; action processData result [1] zdemoservice; mapping for zdemoservice { Id = id; Name = name; Description = description; Status = status; CreatedBy = created_by; CreatedAt = created_at; LastChangedBy = last_changed_by; LastChangedAt = last_changed_at; LocalLastChanged = local_last_changed; } }</code></pre><P>An example of an ABAP Class with an action implementation:</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_DemoService DEFINITION INHERITING FROM cl_abap_behavior_handler. PUBLIC SECTION. PRIVATE SECTION. METHODS processData FOR MODIFY IMPORTING keys FOR ACTION DemoService~processData result result. ENDCLASS. CLASS lhc_DemoService IMPLEMENTATION. METHOD processData. " Your business logic implementation her ENDMETHOD. ENDCLASS.</code></pre><P>Service Definition:</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Demo Service Definition' define service ZUI_DEMO_SERVICE { expose ZC_DEMO_SERVICE as DemoService; }</code></pre><P>Inbound Service:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="inbound_serv.png" style="width: 464px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432934i5E0A8AD179502CAF/image-size/large?v=v2&amp;px=999" role="button" title="inbound_serv.png" alt="inbound_serv.png" /></span></P><P>Communication Scenario:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="comm_scen.png" style="width: 856px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432932i4BA90F026F4EDA33/image-size/large?v=v2&amp;px=999" role="button" title="comm_scen.png" alt="comm_scen.png" /></span></P><P>&nbsp;</P><H1 id="toc-hId-904802150">5 Create a Connection</H1><P>The first step is creating a generic HTTP connection to the endpoint exposed by ABAP Cloud. In order to do so, consider the following details. Take the connection host from the BTP ABAP instance key. The credentials must be taken from the connection user as defined in the communication arrangement.</P><P>&nbsp;</P><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="connection_dsp.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432911iDB1F955E4271E5E1/image-size/medium?v=v2&amp;px=400" role="button" title="connection_dsp.png" alt="connection_dsp.png" /></span></DIV><P>An alternative to Username/Password is an OAuth based authentication mechanism.&nbsp;</P><P>&nbsp;</P><H1 id="toc-hId-708288645">6 Creating a Task Chain in SAP Datasphere</H1><P>The first step is defining a Task Chain that encapsulates the processing logic you want to execute .</P><P>In the given example:</P><UL><LI>The Task Chain starts with a&nbsp;API Task</LI><LI>Sends an email in case of success</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Task Chain" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432899i09D40BB9BF22D881/image-size/medium?v=v2&amp;px=400" role="button" title="TC01.png" alt="Task Chain" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Task Chain</span></span></P><P><I>In the API Task details, define the following:</I></P><UL><LI><I>Method POST</I></LI><LI><I>API Path - the path to the service endpoint of your ABAP Web API that you want to trigger</I></LI><LI><I>Mode Synchronous</I></LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="API Task Details 1" style="width: 440px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432900iFF0930533C259D5F/image-size/large?v=v2&amp;px=999" role="button" title="TC02.png" alt="API Task Details 1" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">API Task Details 1</span></span></P><DIV class="">&nbsp;</DIV><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="TC03.png" style="width: 460px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432937iF3BCF25B02797E5E/image-size/large?v=v2&amp;px=999" role="button" title="TC03.png" alt="TC03.png" /></span></P><P>Make sure to fetch the CSRF Token. The URL which I've used is simply the $metadata path of the service endpoint:</P><P><A href="https://%3cABAP_HOST%3e.abap.eu10.hana.ondemand.com/sap/opu/odata/sap/ZUI_DEMO_SERVICE_O4/$metadata" target="_blank" rel="noopener nofollow noreferrer">https://&lt;ABAP_HOST&gt;.abap.eu10.hana.ondemand.com/sap/opu/odata/sap/ZUI_DEMO_SERVICE_O4/$metadata</A></P><P>&nbsp;</P><H1 id="toc-hId-511775140">7 Execution Results</H1><P>The Task Chain in Datasphere successfully executed the ABAP Web API call:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Task Chain API successfully executed" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432916i7B2C20887B641D53/image-size/large?v=v2&amp;px=999" role="button" title="TC_EXEC.png" alt="Task Chain API successfully executed" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Task Chain API successfully executed</span></span></P><P>For monitoring and troubleshooting purpose, individual API calls can be traced from BTP ABAP side. The HTTP trace can be turned on for granular levels.</P><P>&nbsp;</P><H1 id="toc-hId-315261635">8 Solution Options - Comparisons</H1><P>The following comparison outlines the key aspects per solution/architecture option as depicted in the initial architecture diagram. I have prototyped all of the below and all options are working flawlessly. Especially w.r.t. to the CAP based solution, there is a wide open room to play with.</P><P>&nbsp;</P><TABLE><TBODY><TR><TD><P>Option</P></TD><TD><P>Pros (+)</P></TD><TD><P>Cons (-)</P></TD></TR><TR><TD><P><STRONG>(1) Direct API</STRONG></P></TD><TD><UL><LI>Simplest setup &amp; fewest components &amp; lowest cost</LI><LI>No middleware latency</LI><LI>Lightweight and developer-friendly</LI></UL></TD><TD><UL><LI>Limited transformation capability</LI><LI>Less suitable for multi-step or conditional routing logic</LI></UL></TD></TR><TR><TD><P><STRONG>(2) Integration Suite</STRONG></P></TD><TD><UL><LI>Supports complex transformations, multiple protocols and error handling in iFlows</LI><LI>Centralized API governance via API Management</LI><LI>Recommended for complex mediated flows</LI></UL></TD><TD><UL><LI>Two-step connectivity required (Integration Suite + Datasphere)</LI><LI>Additional licensing cost</LI><LI>Higher setup and maintenance effort</LI></UL></TD></TR><TR><TD><P><STRONG>(3) CAP CF App</STRONG></P></TD><TD><UL><LI>Maximum flexibility for custom logic (parsing, filtering, enrichment, retry)</LI><LI>CAP integrates natively with HANA Cloud/Datasphere HDI</LI><LI>Supports secure credential management</LI><LI>Reusable microservice pattern</LI></UL></TD><TD><UL><LI>Requires application development and BTP CF deployment skills</LI><LI>Higher operational overhead</LI><LI>More BTP resource consumption</LI><LI>CAP app lifecycle must be managed independently</LI></UL></TD></TR></TBODY></TABLE><P>&nbsp;</P><H1 id="toc-hId-118748130">9 Conclusion/Outlook &amp; Further aspects:</H1><P>Overview of the major building blocks:</P><TABLE><TBODY><TR><TD><P>Capability</P></TD><TD><P>Result</P></TD></TR><TR><TD><P>No hardcoded secrets</P></TD><TD><P>via Communication Arrangements/System</P></TD></TR><TR><TD><P>Remote orchestration</P></TD><TD><P>Creating an end-to-end orchestrated chain of tasks, bidirectional if required</P></TD></TR><TR><TD><P>Cloud-native ABAP</P></TD><TD><P>Using appropriate ABAP Class APIs</P></TD></TR><TR><TD><P>Datasphere automation</P></TD><TD><P>Using Task Chain API</P></TD></TR></TBODY></TABLE><P>&nbsp;</P><P><STRONG>Use Cases that the implementation enables:</STRONG></P><UL><LI>Nightly batch orchestration</LI><LI>Event-driven data loads</LI><LI>Cross-system workflows (this was our ultimate focus)</LI><LI>API-driven (transactional/analytical) data refresh</LI></UL><P><STRONG>Limitations:</STRONG></P><UL><LI>The Task Chain/API Task can't be parameterized dynamically with runtime parameters to e.g. initiate calls that have a variable payload in header or body</LI><LI>Moreover, the write-back/storing of HTTP responses is limited too</LI></UL><P><STRONG>References:</STRONG></P><UL><LI>SAP Datasphere Task Chain API Documentation:&nbsp; <A href="https://api.sap.com/api/DatasphereTasks/overview" target="_blank" rel="noopener noreferrer">https://api.sap.com/api/DatasphereTasks/overview</A></LI><LI>SAP Datasphere Task Chain Help Page:&nbsp;<A href="https://help.sap.com/docs/SAP_DATASPHERE/c8a54ee704e94e15926551293243fd1d/274f2736465c4c48a091c675880502a2.html?locale=en-US" target="_blank" rel="noopener noreferrer">https://help.sap.com/docs/SAP_DATASPHERE/c8a54ee704e94e15926551293243fd1d/274f2736465c4c48a091c67588...</A></LI></UL><P>This blog entry complements my other blog on <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/triggering-sap-datasphere-bdc-task-chains-from-sap-btp-abap-cloud-using-the/ba-p/14325528" target="_blank">Triggering SAP Datasphere/BDC Task Chains from SAP BTP ABAP Cloud using the Task Chain REST API</A>.</P><P>In case of questions feel free to raise them and please leave a comment if you found something incorrect - thanks for any help on that. I'm hoping this blog can support somebody out there and may facilitate some problem solving.</P> 2026-07-14T16:00:19.634000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/rap-augmentation-in-managed-scenario-enriching-transactional-requests/ba-p/14409354 RAP Augmentation in Managed Scenario – Enriching Transactional Requests Before BO Processing 2026-07-17T11:22:43.643000+02:00 Abhi_0118 https://community.sap.com/t5/user/viewprofilepage/user-id/1874843 <H2 id="toc-hId-1816513912"><FONT face="arial,helvetica,sans-serif">Introduction</FONT></H2><P class="">The RESTful Application Programming Model (RAP) provides several extension points for implementing business logic, such as Determinations, Validations, Actions, and Prechecks. <STRONG>RAP Augmentation</STRONG> adds another extension point that allows developers to enrich incoming create or update requests before the framework starts processing the business object.</P><P class="lia-align-justify" style="text-align : justify;">This early execution makes augmentation useful for scenarios where default values, audit information, or business-specific data must be available before any further RAP processing takes place. In this blog, we will understand RAP Augmentation, compare it with Determinations, and implement it using an Employee Management example.</P><HR /><H4 id="toc-hId-1878165845"><FONT face="arial,helvetica,sans-serif">Business Requirement</FONT></H4><P class="">I had a requirement&nbsp; an Employee Management application where users create and maintain employee records. During record creation, the application should automatically populate business-specific information instead of relying on users to enter every value manually.</P><P class="lia-align-justify" style="text-align : justify;">The application should:</P><UL class="lia-align-justify" style="text-align : justify;"><LI>Set the employee status to <STRONG>PENDING_APPROVAL</STRONG>.</LI><LI>Determine whether approval is required based on the selected department.</LI><LI>Capture the current user as the creator.</LI><LI>Maintain audit information during updates.</LI></UL><P class="lia-align-justify" style="text-align : justify;">Instead of implementing all this logic inside the business object, we will use RAP Augmentation to enrich the incoming request before standard processing begins.</P><HR /><H5 id="toc-hId-1810735059"><FONT face="arial,helvetica,sans-serif">Problem Statement</FONT></H5><P class="">Determinations are commonly used in RAP to derive or update field values. However, they execute after the request has already entered the transactional buffer.</P><P class="lia-align-justify" style="text-align : justify;">In some business scenarios, values must be available even before the business object starts processing. For example, default values or audit fields may be required before validations or other business logic execute. In such cases, Augmentation provides a cleaner and more suitable solution by enriching the request at the beginning of the transaction.</P><HR /><H3 id="toc-hId-1356056116"><FONT face="arial,helvetica,sans-serif">What is RAP Augmentation?</FONT></H3><P class="">RAP Augmentation is an extension mechanism that runs during the Interactive Phase of a RAP transaction. It allows developers to enrich incoming create or update requests before the framework processes the business object.</P><P class="lia-align-justify" style="text-align : justify;">Rather than modifying persisted data, augmentation focuses on preparing the request by supplying additional values or applying business-specific logic. Once the request is enriched, RAP continues with its normal processing flow.</P><HR /><H5 id="toc-hId-1417708049"><U><FONT face="arial,helvetica,sans-serif">Augmentation Flow</FONT></U></H5><P class="">The request passes through augmentation before reaching the business object implementation. During this stage, additional values can be added or modified, ensuring the business object receives a complete request for further processing.</P><P>The overall execution flow is:</P><UL><LI>User sends a create or update request.</LI><LI>RAP invokes the registered augmentation.</LI><LI>The request is enriched with additional data.</LI><LI>Standard RAP processing continues.</LI><LI>Data is persisted to the database.</LI></UL><HR /><H4 id="toc-hId-1092111825"><U><FONT face="arial,helvetica,sans-serif">Augmentation vs Determination</FONT></U></H4><TABLE width="852px"><TBODY><TR><TD width="443.575px" height="57px">Defined at the <STRONG>Projection Behavior Definition</STRONG> using use create/update (augment).</TD><TD width="407.625px" height="57px">Defined at the <STRONG>Interface Behavior Definition</STRONG> using&nbsp; <STRONG>determination</STRONG>.</TD></TR><TR><TD width="443.575px" height="57px">Implemented in a Projection Behavior Implementation Class&nbsp;</TD><TD width="407.625px" height="57px">Implemented in the Behavior Pool&nbsp; class.</TD></TR><TR><TD width="443.575px" height="57px">Runs during the <STRONG>Interactive Phase</STRONG>, before the BO implementation processes the request.</TD><TD width="407.625px" height="57px">Runs during ON MODIFY or ON SAVE, after data enters the transactional buffer.</TD></TR><TR><TD width="443.575px" height="57px">Used to <STRONG>enrich or modify the incoming request</STRONG> before BO processing.</TD><TD width="407.625px" height="57px">Used to derive or recalculate values based on data in the transactional buffer.</TD></TR></TBODY></TABLE><H4 id="toc-hId-895598320">When Execution timing matter?</H4><P class="lia-align-justify" style="text-align : justify;">The main advantage of Augmentation is <STRONG>when</STRONG> it executes. Since it runs before business object processing, any values added during augmentation are available to the rest of the RAP processing flow.</P><UL><LI>If the application should automatically set an employee's status or determine whether approval is required, these values are already present when the framework performs subsequent processing. If the same logic were implemented in a Determination, the values would only be available later in the transaction.</LI></UL><H4 id="toc-hId-699084815"><U><FONT face="arial,helvetica,sans-serif">When to Use Augmentation?</FONT></U></H4><P class="">Augmentation is a good choice when the incoming request needs to be enriched before RAP starts processing the business object.</P><P class="lia-align-justify" style="text-align : justify;">Typical use cases include:</P><UL class="lia-align-justify" style="text-align : justify;"><LI>Setting default values.</LI><LI>Populating audit fields.</LI><LI>Deriving values from user input.</LI><LI>Performing lightweight request enrichment.</LI><LI>Preparing data required by later processing steps.</LI></UL><P class="lia-align-justify" style="text-align : justify;">It is not intended to replace Determinations, which remain the preferred option for business logic that should execute after the request has entered the transactional buffer.</P><P>&nbsp;</P><H4 id="toc-hId-502571310"><FONT size="4"><U><FONT face="arial,helvetica,sans-serif">BDEF of Interface</FONT></U></FONT></H4><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_aky_i_emp unique; strict ( 2 ); define behavior for zaky_i_emp //alias &lt;alias_name&gt; persistent table zaky_t_emp lock master authorization master ( instance ) //etag master &lt;field_name&gt; { create( authorization : global ) ; update; delete; action Approve result [1] $self; mapping for zaky_t_emp { Employeeid = employeeid; Firstname = firstname; Lastname = lastname; Department = department; Status = status; Approvalrequired = approvalrequired; Createdby = createdby; Createdat = createdat; Lastchangedby = lastchangedby; Lastchangedat = lastchangedat; } }</code></pre><HR /><H4 id="toc-hId-306057805"><FONT size="4"><U><FONT face="arial,helvetica,sans-serif">BDEF of Projection</FONT></U></FONT></H4><pre class="lia-code-sample language-abap"><code>projection implementation in class zbp_aky_c_emp unique; strict ( 2 ); define behavior for zaky_c_emp //alias &lt;alias_name&gt; { use create( augment ); use update( augment ); use delete; field ( readonly ) Status; field ( readonly ) Approvalrequired; field ( readonly ) Createdby; field ( readonly ) Createdat; field ( readonly ) Lastchangedby; field ( readonly ) Lastchangedat; use action Approve; }</code></pre><HR /><H4 id="toc-hId--388172795"><U><FONT face="arial, helvetica, sans-serif" size="4">BP of Projection</FONT></U></H4><pre class="lia-code-sample language-abap"><code>CLASS lhc_zaky_c_emp DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS augment_create FOR MODIFY IMPORTING entities FOR CREATE zaky_c_emp. METHODS augment_update FOR MODIFY IMPORTING entities FOR UPDATE zaky_c_emp. ENDCLASS. CLASS lhc_zaky_c_emp IMPLEMENTATION. METHOD augment_create. LOOP AT entities INTO DATA(entity). IF entity-Department IS INITIAL. APPEND VALUE #( %cid = entity-%cid %msg = new_message_with_text( severity = if_abap_behv_message=&gt;severity-error text = 'Department is mandatory' ) ) TO reported-zaky_c_emp. APPEND VALUE #( %cid = entity-%cid ) TO failed-zaky_c_emp. CONTINUE. ENDIF. MODIFY AUGMENTING ENTITIES OF zaky_i_emp ENTITY zaky_i_emp CREATE FIELDS ( Status Approvalrequired Createdby Createdat ) WITH VALUE #( ( %cid = entity-%cid Status = 'PENDING_APPROVAL' Approvalrequired = COND #( WHEN entity-Department = 'HR' OR entity-Department = 'FIN' THEN abap_true ELSE abap_false ) Createdby = sy-uname Createdat = cl_abap_context_info=&gt;get_system_date( ) ) ). ENDLOOP. ENDMETHOD. METHOD augment_update. LOOP AT entities INTO DATA(entity). MODIFY AUGMENTING ENTITIES OF zaky_i_emp ENTITY zaky_i_emp UPDATE FIELDS ( Lastchangedby Lastchangedat ) WITH VALUE #( ( Employeeid = entity-Employeeid Lastchangedby = sy-uname Lastchangedat = cl_abap_context_info=&gt;get_system_date( ) ) ). ENDLOOP. ENDMETHOD. ENDCLASS.</code></pre><H4 id="toc-hId--584686300"><FONT size="4"><U><FONT face="arial,helvetica,sans-serif">BP of interface</FONT></U></FONT></H4><pre class="lia-code-sample language-abap"><code>METHOD Approve. " Read current status of the employee READ ENTITIES OF zaky_i_emp IN LOCAL MODE ENTITY zaky_i_emp FIELDS ( Status Employeeid ) WITH CORRESPONDING #( keys ) RESULT DATA(employees) FAILED DATA(read_failed). " Loop and update status LOOP AT employees INTO DATA(employee). " Only approve if current status is PENDING_APPROVAL IF employee-Status &lt;&gt; 'PENDING_APPROVAL'. APPEND VALUE #( %tky = employee-%tky %msg = new_message_with_text( severity = if_abap_behv_message=&gt;severity-error text = 'Only records with PENDING_APPROVAL status can be approved' ) ) TO reported-zaky_i_emp. APPEND VALUE #( %tky = employee-%tky ) TO failed-zaky_i_emp. CONTINUE. ENDIF. " Update status to APPROVED MODIFY ENTITIES OF zaky_i_emp IN LOCAL MODE ENTITY zaky_i_emp UPDATE FIELDS ( Status ) WITH VALUE #( ( %tky = employee-%tky Status = 'APPROVED' ) ) FAILED DATA(update_failed) REPORTED DATA(update_reported). " Pass back result APPEND VALUE #( %tky = employee-%tky %param = employee ) TO result. ENDLOOP. ENDMETHOD.</code></pre><P><U><FONT face="arial,helvetica,sans-serif" size="4"><STRONG>Output</STRONG></FONT></U></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Abhi_0118_1-1782208500025.png" style="width: 657px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/425239i3E505C640B9BC34F/image-dimensions/657x304?v=v2" width="657" height="304" role="button" title="Abhi_0118_1-1782208500025.png" alt="Abhi_0118_1-1782208500025.png" /></span></P><P><STRONG>Click on the create&nbsp;</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Abhi_0118_2-1782208568634.png" style="width: 654px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/425242i6AE4CACBEC0358D3/image-dimensions/654x310?v=v2" width="654" height="310" role="button" title="Abhi_0118_2-1782208568634.png" alt="Abhi_0118_2-1782208568634.png" /></span></P><P>After adding the value click on the create and put the debugger on the create-augment method to check it is triggering or not because we are doing managed application where creation is handled by framework</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Abhi_0118_0-1783921767429.png" style="width: 532px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/432182i0467F4A6A38B1515/image-dimensions/532x456?v=v2" width="532" height="456" role="button" title="Abhi_0118_0-1783921767429.png" alt="Abhi_0118_0-1783921767429.png" /></span></P><P><STRONG>Execute</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Abhi_0118_4-1782209444122.png" style="width: 628px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/425252iC67537BA421DD23C/image-dimensions/628x153?v=v2" width="628" height="153" role="button" title="Abhi_0118_4-1782209444122.png" alt="Abhi_0118_4-1782209444122.png" /></span></P><P><STRONG>Currently it is in pending state click on the Approve employee.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Abhi_0118_5-1782209514677.png" style="width: 622px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/425254i1971B2B62521F508/image-dimensions/622x148?v=v2" width="622" height="148" role="button" title="Abhi_0118_5-1782209514677.png" alt="Abhi_0118_5-1782209514677.png" /></span></P><P><STRONG>After clicking on action</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Abhi_0118_6-1782209548205.png" style="width: 622px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/425255i279C217B0C0C4680/image-dimensions/622x70?v=v2" width="622" height="70" role="button" title="Abhi_0118_6-1782209548205.png" alt="Abhi_0118_6-1782209548205.png" /></span></P><P><STRONG>If you not adding the department</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Abhi_0118_0-1782210556795.png" style="width: 630px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/425261i394039D97B6C97F1/image-dimensions/630x326?v=v2" width="630" height="326" role="button" title="Abhi_0118_0-1782210556795.png" alt="Abhi_0118_0-1782210556795.png" /></span></P><P>&nbsp;</P><H2 id="toc-hId--194393791"><U><FONT face="arial,helvetica,sans-serif">Conclusion</FONT></U></H2><P class="">RAP Augmentation provides a simple and effective way to enrich transactional requests before the RAP framework begins processing the business object. It is particularly useful for populating default values, maintaining audit information, and preparing request data that should be available throughout the transaction.</P><P class="lia-align-justify" style="text-align : justify;">In this example, we used augmentation to automatically populate employee details while keeping the business object implementation clean and maintainable. Understanding when to use Augmentation instead of Determinations helps developers choose the right extension point and build more maintainable RAP applications</P> 2026-07-17T11:22:43.643000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/abap-development-tools-for-visual-studio-code-version-1-1-released/ba-p/14421143 ABAP development tools for Visual Studio Code: Version 1.1 released 2026-07-21T13:53:34.813000+02:00 thomasalexander_ritter https://community.sap.com/t5/user/viewprofilepage/user-id/185696 <H1 id="toc-hId-1688269120" id="toc-hId-1689037953"><SPAN>What happened since the last release?</SPAN></H1><P><A href="https://marketplace.visualstudio.com/items?itemName=SAPSE.adt-vscode" target="_self" rel="nofollow noopener noreferrer"><SPAN>Link to the extension.</SPAN></A></P><P><SPAN>First of all, thanks for the positive feedback regarding the 1.0.1 patch which added the much requested RFC + password support! Please keep providing feedback as it helps us to plan the next releases. A lot of users are asking us how they can provide feedback. We have recently added a <A href="https://influence.sap.com/sap/ino/#campaign/2911" target="_self" rel="noopener noreferrer">new customer influence channel</A> just for the VS Code extension. We recommend using that one.</SPAN></P><P><SPAN>Last month,&nbsp;<A href="https://abapconf.org/abapconf2026/" target="_self" rel="nofollow noopener noreferrer">ABAPConf 2026</A> took place in Mannheim. We want to highlight two talks:</SPAN></P><OL><LI><SPAN>Tobias Hofmann provided public feedback regarding the extension and compiled a nice to-do list. I can report that a lot of these to-do items are already part of our internal to-do list. The talk can be found <A href="https://www.youtube.com/watch?v=hzRQuEzMLhY" target="_self" rel="nofollow noopener noreferrer">here</A>.</SPAN></LI><LI><SPAN>Anne Keller and I gave a talk on the overall ABAP IDE strategy. Of course, it touches the VS Code extension but also aims to provide a broader picture on how we want to support ABAP IDEs via a single technology stack in the future and shares some insights on how the ADT MCP server will evolve in the future. The talk can be found <A href="https://www.youtube.com/watch?v=2a7hzuw3woM" target="_self" rel="nofollow noopener noreferrer">here</A>.</SPAN></LI></OL><P>&nbsp;</P><H1 id="toc-hId-1491755615" id="toc-hId-1492524448"><SPAN>What gets shipped with this release?</SPAN></H1><H3 id="toc-hId-1554176381"><SPAN>Usability improvements for system connection handling</SPAN></H3><P><SPAN>After creating a new destination there was no visual feedback that the destination was created successfully. We added a notification pop-up with a button which directly adds it as a folder to your workspace.</SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="destination_add_as_folder.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/434898i10292816BFAD1A46/image-size/medium?v=v2&amp;px=400" role="button" title="destination_add_as_folder.png" alt="destination_add_as_folder.png" /></span></SPAN></P><P><SPAN>We added a "Log on to Destination..." command. While it was possible to log on to a destination via a right-click menu in the explorer view there was no command available for it. </SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="logon_menu_item.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/434931iB7F9BF5A47889FC6/image-size/medium?v=v2&amp;px=400" role="button" title="logon_menu_item.png" alt="logon_menu_item.png" /></span></SPAN></P><P><SPAN>Now, you can easily log on to a destination using the keyboard via the new command.</SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="logon1.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/434910i9D9212B5A0BB6C21/image-size/medium?v=v2&amp;px=400" role="button" title="logon1.png" alt="logon1.png" /></span></SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="logon2.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/434914i14577214BB68CBEE/image-size/medium?v=v2&amp;px=400" role="button" title="logon2.png" alt="logon2.png" /></span></SPAN></P><H3 id="toc-hId-1357662876">A significant improvement of object type support</H3><P>A lot of requested object types get enabled by this release. Here is the list of added object types:</P><H5 id="toc-hId-1419314809">Classic ABAP source code</H5><UL><LI><SPAN>Create and Manage Function Groups and Modules</SPAN></LI><LI><SPAN>Create and Manage ABAP Programs and Includes</SPAN></LI><LI><SPAN>Create and Manage Lock Objects</SPAN></LI></UL><H5 id="toc-hId-1222801304"><SPAN>Data Dictionary</SPAN></H5><UL><LI><SPAN>Create and Manage Database Tables</SPAN></LI><LI><SPAN>Create and Manage Structures</SPAN></LI><LI><SPAN>Create and Manage Type Groups</SPAN></LI></UL><H5 id="toc-hId-1026287799">RESTful Application Programming Model</H5><UL><LI><SPAN>Creating Behavior Definition Extensions</SPAN></LI><LI><SPAN>Enabling more OData Binding Types in Service Binding</SPAN></LI><LI><SPAN>Create and Manage CDS Entity Buffers</SPAN></LI></UL><H3 id="toc-hId-571608856">New MCP tools</H3><H5 id="toc-hId-633260789">Unified diff tool for transports</H5><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="thomasalexander_ritter_0-1784628047832.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/435436iB59B911CF866B6C9/image-size/medium?v=v2&amp;px=400" role="button" title="thomasalexander_ritter_0-1784628047832.png" alt="thomasalexander_ritter_0-1784628047832.png" /></span></P><P>The unified diff tool generates a <A href="https://git-scm.com/docs/diff-format" target="_self" rel="nofollow noopener noreferrer">git like diff</A> for a given transport request. The tool enables token efficient analysis of the changes contained in a transport request. This enables AI agents to fulfill requests such as:</P><UL><LI>"Explain transport XYZ"</LI><LI>"Generate a description for transport XYZ documenting what got changed and why"</LI><LI>"How much impact does transport XYZ has on the system? How dangerous is this transport?"</LI></UL><H5 id="toc-hId-436747284">Tools for ABAP Test Cockpit (ATC)</H5><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="thomasalexander_ritter_0-1784633951380.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/435557iD397E3BC3A126A06/image-size/medium?v=v2&amp;px=400" role="button" title="thomasalexander_ritter_0-1784633951380.png" alt="thomasalexander_ritter_0-1784633951380.png" /></span></P><P>The ATC MCP tools give AI agents access to running the ATC checks for a given change.&nbsp;This enables AI agents to fulfill requests such as:</P><UL><LI>"Implement [XYZ]. Afterwards, run the ATC checks and fix the reported findings."</LI><LI>"Run ATC checks for object XYZ"</LI></UL><H3 id="toc-hId--93163028">Additional UX improvements</H3><P>We added a command which allows you to quickly open the SAP Help documentation.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="abap_vscode_documentation.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/434927iD70EE3151616CDD8/image-size/medium?v=v2&amp;px=400" role="button" title="abap_vscode_documentation.png" alt="abap_vscode_documentation.png" /></span></P><P>From each occurrence of a CDS annotation in a CDS data definition or CDS metadata extension, you can now navigate to its CDS annotation definition. To navigate, press F3 on the relevant CDS annotation. This helps you to investigate and to understand the use of a CDS annotation.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="thomasalexander_ritter_0-1784557648564.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/435160iC57E82F6E5DBC314/image-size/medium?v=v2&amp;px=400" role="button" title="thomasalexander_ritter_0-1784557648564.png" alt="thomasalexander_ritter_0-1784557648564.png" /></span></P><P>For more information see the new <A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-for-visual-studio-code/what-s-new" target="_self" rel="noopener noreferrer">"What's New"</A> page.</P><P>&nbsp;</P><H1 id="toc-hId-297129481">Sidenote: first version of ABAP cleaner VS Code extension available</H1><P>Lots of community members asked for ABAP cleaner support in VS Code. We are happy to announce that the first version of the <A href="https://marketplace.visualstudio.com/items?itemName=SAPOSS.abap-cleaner" target="_self" rel="nofollow noopener noreferrer">ABAP cleaner extension</A> is available, now!</P><P>&nbsp;</P><H1 id="toc-hId-902215100" id="toc-hId-100615976"><SPAN>What's next?</SPAN></H1><P data-unlink="true"><SPAN>From now on, we will stop listing upcoming features directly in the release blogs. Instead we have cleaned up and refreshed our <A href="https://help.sap.com/docs/abap-cross-product/roadmap-info/tools" target="_self" rel="noopener noreferrer">ABAP Cloud Roadmap page</A>. The planned features are listed there</SPAN><SPAN>.</SPAN></P><H2 id="toc-hId-834784314" id="toc-hId--389300536">Provide feedback and influence our product backlog!</H2><P><SPAN>We welcome your feedback! Feel free to use the comment section or <A href="https://influence.sap.com/sap/ino/#/campaign/2911" target="_blank" rel="noopener noreferrer">create a customer influence request</A> to let us know which features you are missing the most. Your input will help us prioritize the feature backlog for the next releases.</SPAN></P> 2026-07-21T13:53:34.813000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-run-different-versions-of-the-abap-development-tools-for-visual/ba-p/14448385 How to run different versions of the ABAP development tools for Visual Studio Code in parallel? 2026-07-24T20:16:18.829000+02:00 Andre_Fischer https://community.sap.com/t5/user/viewprofilepage/user-id/55 <H1 id="toc-hId-1691095580">Introduction</H1><P>Having now released a second version of the&nbsp;ABAP development tools for Visual Studio Code the question may arise how to run different versions of this plugin in parallel in order to test the latest version or to check differences in the behavior of the new and an older version.</P><P>At least SAP internally this is a question that is of interest for me since I want to run the public available version and the next upcoming version before this is going to be released officially&nbsp;<span class="lia-unicode-emoji" title=":winking_face:">😉</span>.</P><P>For the ABAP Development Tools for Eclipse you have to install a second Eclipse so that you can test a second version there.</P><H1 id="toc-hId-1494582075">How to section</H1><P>Co-Pilot will provide various options to this question from which I chose the IMHO most easiest one, namely to create different profiles and install a different extension version in each profile.</P><P>You can either click on the gear icon and select Profiles.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="profiles_010.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/437046iF0015F5F9A1B01E1/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="profiles_010.png" alt="profiles_010.png" /></span></P><P>Or you can o<SPAN>pen </SPAN><STRONG>Command Palette</STRONG><SPAN> (</SPAN><CODE>Ctrl+Shift+P</CODE><SPAN>) and then enter <STRONG>Profiles</STRONG> and select&nbsp;</SPAN><STRONG>Profiles: New Profile</STRONG><SPAN>.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Andre_Fischer_0-1784916175582.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/437048i740A3DA27F12DCC2/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Andre_Fischer_0-1784916175582.png" alt="Andre_Fischer_0-1784916175582.png" /></span></P><P>This will open the following dialogue where you can select the default profile from the drop down box.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Andre_Fischer_1-1784916297217.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/437050i71C3B37FB1A8EF58/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Andre_Fischer_1-1784916297217.png" alt="Andre_Fischer_1-1784916297217.png" /></span></P><P>This let you swith to the newly created profile.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Andre_Fischer_0-1784917974757.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/437103iAAAD4DACEEA63ABE/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Andre_Fischer_0-1784917974757.png" alt="Andre_Fischer_0-1784917974757.png" /></span></P><P>&nbsp;</P><P>Using the drop down arrow of the "uninstall" button you are offered to install a specific version of the selected extension in the copied profile.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Andre_Fischer_5-1784916612769.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/437055i39E4B4CAA5C4BBE3/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Andre_Fischer_5-1784916612769.png" alt="Andre_Fischer_5-1784916612769.png" /></span></P><P>Here you are able to select an older version</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Andre_Fischer_6-1784916658027.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/437056i8E3F37D0EAD2AF6A/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Andre_Fischer_6-1784916658027.png" alt="Andre_Fischer_6-1784916658027.png" /></span></P><P>As a result you can activate this older version by restarting the extension.</P><P>Please note that the <EM>Auto Update</EM> option has been deactivated conveniently.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Andre_Fischer_7-1784916746749.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/437057iE4C29BE921D9FF57/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Andre_Fischer_7-1784916746749.png" alt="Andre_Fischer_7-1784916746749.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P> </P><P>&nbsp;</P><DIV>&nbsp;</DIV><P>&nbsp;</P><P>&nbsp;</P> 2026-07-24T20:16:18.829000+02:00