https://raw.githubusercontent.com/ajmaradiaga/feeds/main/scmt/topics/ABAP-RESTful-Application-Programming-Model-blog-posts.xml SAP Community - ABAP RESTful Application Programming Model 2025-05-15T17:00:06.254337+00:00 python-feedgen ABAP RESTful Application Programming Model blog posts in SAP Community https://community.sap.com/t5/application-development-and-automation-blog-posts/global-feature-contron-in-rap-feature-control-in-rap/ba-p/14056926 Global Feature Contron in RAP, Feature control in RAP 2025-04-08T10:11:48.601000+02:00 Krishna_karale https://community.sap.com/t5/user/viewprofilepage/user-id/1451081 <P><SPAN>Hello Everyone,&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN><SPAN>In this blog post we are going to Learn about Global Feature Control in RAP.&nbsp; </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Global feature control in the RAP (RESTful Application Programming) model is used to enable or disable specific features for all users at once, regardless of individual data records or instances. It defines whether a particular operation (like create, update, or delete) is globally allowed or restricted. To do this, we extend the operation in the behavior definition as we did earlier for the instance feature.&nbsp; </SPAN><SPAN>&nbsp;</SPAN></P><P><EM><STRONG>Delete (features: global);&nbsp;</STRONG></EM></P><P><SPAN>To implement a global feature, the </SPAN><STRONG><SPAN>"get_global_features"</SPAN></STRONG><SPAN> method must be implemented. As previously described, </SPAN><STRONG><SPAN>there is no transfer of the keys from the outside.</SPAN></STRONG><SPAN> The derivation of the various features must be based on other criteria.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN class=""><SPAN class="">Procedure:</SPAN></SPAN></STRONG></P><P><SPAN>Here we are defining a Custom table (ZKK_DT_TRAVEL).</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>The table zkk_dt_travel is a transparent table in SAP that stores travel-related information. It includes details such as travel ID, agency ID, customer ID, travel dates, booking fees, total price, currency, status and a description. It also tracks the creation and modification details like created by, created at, last changed by, and last changed at.</SPAN><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Travel table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zkk_dt_travel { key client : abap.clnt not null; key travel_id : /dmo/travel_id not null; agency_id : /dmo/agency_id; customer_id : /dmo/customer_id; begin_date : /dmo/begin_date; end_date : /dmo/end_date; @Semantics.amount.currencyCode : '/dmo/travel_m.currency_code' booking_fee : abap.curr(16,2); @Semantics.amount.currencyCode : '/dmo/travel_m.currency_code' total_price : /dmo/total_price; currency_code : /dmo/currency_code; description : /dmo/description; status : abap_boolean; created_by : abp_creation_user; created_at : abp_creation_tstmpl; last_changed_by : abp_locinst_lastchange_user; last_changed_at : abp_locinst_lastchange_tstmpl; }</code></pre><P><SPAN>The Above table holds the records which are given below</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Krishna_karale_0-1742974513709.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/242615iE27001DA15B3BA11/image-size/large?v=v2&amp;px=999" role="button" title="Krishna_karale_0-1742974513709.png" alt="Krishna_karale_0-1742974513709.png" /></span></P><P><SPAN>For this database table(ZKK_DT_TRAVEL) we are defining the Basic view/Interface view.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>The </SPAN><STRONG><SPAN>ZKK_I_TRAVEL </SPAN></STRONG><SPAN>is a CDS view based on the </SPAN><STRONG><SPAN>zkk_dt_travel&nbsp; </SPAN></STRONG><SPAN>table. It provides a structured way to access travel data, including fields like travel ID, agency ID, customer ID, travel dates, booking fees, total price, currency, and timestamps. It uses annotations to enhance usability, such as semantic tags for amounts, currencies, and metadata for created/changed users and timestamps.</SPAN><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface view for Travelling' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define root view entity ZKK_I_TRAVEL as select from zkk_dt_travel { key travel_id as TravelId, agency_id as AgencyId, customer_id as CustomerId, begin_date as BeginDate, end_date as EndDate, @Semantics.amount.currencyCode: 'CurrencyCode' booking_fee as bookingfee, @Semantics.amount.currencyCode: 'CurrencyCode' total_price as TotalPrice, currency_code as CurrencyCode, description as Description, status as status, @Semantics.user.createdBy: true created_by as CreatedBy, @Semantics.systemDateTime.createdAt: true created_at as CreatedAt, @Semantics.user.localInstanceLastChangedBy: true last_changed_by as LastChangedBy, @Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt }</code></pre><P><SPAN>&nbsp;<SPAN class=""><SPAN class="">Now I am defining the Projection view on top of interface view.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'projection view for Travel' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define root view entity ZKK_C_TRAVEL as projection on ZKK_I_TRAVEL as Travel { key TravelId, AgencyId, CustomerId, BeginDate, EndDate, @Semantics.amount.currencyCode: 'CurrencyCode' bookingfee, @Semantics.amount.currencyCode: 'CurrencyCode' TotalPrice, CurrencyCode, Description, status, CreatedBy, CreatedAt, LastChangedBy, LastChangedAt }</code></pre><P><SPAN class=""><SPAN class=""><SPAN class="">Now I am defining the Metadata Extension for projection View.</SPAN></SPAN></SPAN></P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #PARTNER annotate entity ZKK_C_TRAVEL with { <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.facet: [{ purpose: #STANDARD, position: 10, type : #IDENTIFICATION_REFERENCE, label: 'Travel Details' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 10, label: 'Travel Id' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Travel Id' }] TravelId; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 20, label: 'Agency Id' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Agency Id' }] AgencyId; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 30, label: 'Customer Id' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Customer Id' }] CustomerId; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 40, label: 'Begin Date' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Begin Date' }] BeginDate; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 50, label: 'End Date' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'End Date' }] EndDate; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 60, label: 'Booking Fee' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Booking Fee' }] bookingfee; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 70, label: 'Total Price' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Total Price' }] TotalPrice; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 80, label: 'Status Confirm' }, { type: #FOR_ACTION, dataAction: 'status', label: 'Status Confirm' } ] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Status' }, { type: #FOR_ACTION, dataAction: 'status', label: 'Confirm Status' }] status; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 100, label: 'Currency' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Currency' }] CurrencyCode; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 90, label: 'Description' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Description' }] Description; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 110, label: 'Created By' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Created By' }] CreatedBy; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 120, label: 'Created At' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Created At' }] CreatedAt; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 130, label: 'Last Changed By' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Last Changed By' }] LastChangedBy; <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification: [{ position: 140, label: 'Last Changed At' }] <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem: [{ label: 'Last Chnaged at' }] LastChangedAt; }</code></pre><P><SPAN>Lets Define the Behaviour Definition.</SPAN></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_kk_i_travel unique; strict ( 2 ); define behavior for ZKK_I_TRAVEL alias Travel //alias &lt;alias_name&gt; persistent table zkk_dt_travel lock master authorization master ( instance ) //etag master &lt;field_name&gt; { create; update; delete ( features : global ); field ( readonly : update ) TravelId; field ( readonly ) LastChangedAt, LastChangedBy, CreatedAt, CreatedBy; field ( mandatory : create ) AgencyId, CustomerId, BeginDate, EndDate, bookingfee, CurrencyCode; action ( features : instance ) status result [1] $self; mapping for ZKK_DT_TRAVEL { TravelId = travel_id; AgencyId = agency_id; CustomerId = customer_id; BeginDate = begin_date; EndDate = end_date; bookingfee = booking_fee; TotalPrice = total_price; CurrencyCode = currency_code; Description = description; status = status; CreatedBy = created_by; CreatedAt = created_at; LastChangedBy = last_changed_by; LastChangedAt = last_changed_at; } }</code></pre><P><SPAN>Here in Behavior definition, we have to specify</SPAN><SPAN>&nbsp;</SPAN></P><P><FONT color="#800000"><U><STRONG>delete (features: global);&nbsp;</STRONG></U></FONT></P><P><SPAN>as we are performing delete operation&nbsp;</SPAN></P><P><SPAN>After that we will get a warning as given below</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Krishna_karale_1-1742974809833.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/242616i9F09BD01E2E63836/image-size/large?v=v2&amp;px=999" role="button" title="Krishna_karale_1-1742974809833.png" alt="Krishna_karale_1-1742974809833.png" /></span></P><P><SPAN>When we double click on that warning GET_GLOBAL_FEATURE method will get automatically.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Krishna_karale_2-1742974895719.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/242617iA589882B365E0FCE/image-size/large?v=v2&amp;px=999" role="button" title="Krishna_karale_2-1742974895719.png" alt="Krishna_karale_2-1742974895719.png" /></span></P><P><SPAN>You can observe there is no KEYS parameter, t</SPAN><SPAN>hat means we cannot read the details of selected record from here, we will get only the global data.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Now we have to implement our custom logic inside the above given method.</SPAN><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code> METHOD get_global_features. if requested_features-%delete = if_abap_behv=&gt;mk-on. Data(lv_result) = COND #( when cl_abap_context_info=&gt;get_user_alias( ) = 'xyz@gmail.com' then if_abap_behv=&gt;mk-on else if_abap_behv=&gt;mk-off ). result-%delete = lv_result. ENDIF. ENDMETHOD.</code></pre><P><SPAN>&nbsp;Here we have to take the username or mail id which is registed on your SAP Universal ID, so for example i have taken 'xyz@gmail.com'.</SPAN></P><P><SPAN>Above logic is to make delete button enable or disable</SPAN><SPAN>&nbsp;</SPAN></P><P><I><SPAN>When the user is <STRONG>xyz@gmail.com</STRONG></SPAN></I><I><SPAN>&nbsp;</SPAN></I><I><SPAN>then the </SPAN></I><STRONG><I><SPAN>delete</SPAN></I></STRONG><I><SPAN> button will be </SPAN></I><STRONG><I><SPAN>enabled</SPAN></I></STRONG><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>(Requested features contains the actions which are specified with </SPAN><STRONG><SPAN>(features: global))</SPAN></STRONG><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Preview the data</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>The user logged-in is xyz@gmail.com</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Krishna_karale_3-1742975170990.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/242618i4A3ABE1C70967FD5/image-size/large?v=v2&amp;px=999" role="button" title="Krishna_karale_3-1742975170990.png" alt="Krishna_karale_3-1742975170990.png" /></span></P><P><SPAN>The delete button is<EM> enabled</EM></SPAN><EM>&nbsp;</EM></P><P><FONT face="arial black,avant garde"><SPAN>Rewriting the code</SPAN><SPAN>&nbsp;</SPAN></FONT></P><P><I><SPAN>When the user is xyz@gmail.com then i want the</SPAN></I><STRONG><I><SPAN> delete</SPAN></I></STRONG><I><SPAN> button to be</SPAN></I><STRONG><I><SPAN>&nbsp;disabled</SPAN></I></STRONG><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code> METHOD get_global_features. if requested_features-%delete = if_abap_behv=&gt;mk-on. Data(lv_result) = COND #( when cl_abap_context_info=&gt;get_user_alias( ) = 'xyz@gmail.com' then if_abap_behv=&gt;mk-off else if_abap_behv=&gt;mk-on ). result-%delete = lv_result. ENDIF. ENDMETHOD.</code></pre><P><SPAN>When we RUN the application.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>We dont get Delete option as enables, here it is disable for that particular user.</SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Krishna_karale_4-1742975387698.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/242619i4967A1A609949C25/image-size/large?v=v2&amp;px=999" role="button" title="Krishna_karale_4-1742975387698.png" alt="Krishna_karale_4-1742975387698.png" /></span></P><P><STRONG>Conclusion:&nbsp;</STRONG></P><P><SPAN>Global feature control in RAP provides a </SPAN><STRONG><SPAN>centralized way to enable or disable specific operations</SPAN></STRONG><SPAN> across the entire application, regardless of individual records or user roles. It ensures </SPAN><STRONG><SPAN>consistent enforcement of business rules</SPAN></STRONG><SPAN> by globally restricting actions like create, update, or delete. This approach simplifies </SPAN><STRONG><SPAN>feature management</SPAN></STRONG><SPAN>, enhances </SPAN><STRONG><SPAN>data security</SPAN></STRONG><SPAN>, and ensures uniform behavior, making it an effective mechanism for controlling access and operations at the application level.</SPAN><SPAN>&nbsp;</SPAN></P><P>&nbsp;</P> 2025-04-08T10:11:48.601000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/rap-managed-vs-unmanaged-rap/ba-p/14067526 RAP: MANAGED VS UNMANAGED RAP 2025-04-08T10:33:54.672000+02:00 Arunagirish1 https://community.sap.com/t5/user/viewprofilepage/user-id/889803 <P><STRONG>Managed RAP</STRONG><BR />• Greenfield Implementation<BR />• CRUD operations are handled by the framework.<BR />• Transactional buffer is managed by the framework.<BR />• The framework takes care of the save sequence.</P><P><BR /><STRONG>Unmanaged RAP</STRONG><BR />• Brownfield Implementation<BR />• CRUD requires custom implementation.<BR />• Transactional buffer is managed with a global singleton/static class.<BR />• Custom save sequence logic must be written.</P><P><BR /><STRONG>Managed with Unmanaged Save:</STRONG><BR />• The interactive phase remains the same, but the save sequence is handled by the developer.<BR />• Transactional buffer is managed by RAP architecture.<BR />• No persistence table in unmanaged save.</P><P><BR /><STRONG>Additional Save (Enhancing Managed RAP)</STRONG><BR />• Allows developers to implement additional steps for the save logic of a managed RAP BO,<BR />enhancing the standard save sequence.<BR />• Can have a persistence table, as the RAP framework takes care of persisting to the database.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_0-1743925295605.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/247346i6CBD0B30559DEF75/image-size/large?v=v2&amp;px=999" role="button" title="Arunagirish1_0-1743925295605.png" alt="Arunagirish1_0-1743925295605.png" /></span></P><P><STRONG>Restrictions:</STRONG><BR />You can’t use a BAPI with a commit statement, as the RAP LUW takes care of commit and<BR />rollback. Additionally, there’s no persistence table in Unmanaged Save, as the developer writes their<BR />own logic for persisting. Please refer to the attachment for complete restrictions.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_1-1743925377318.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/247347i1F05CF0987449BD7/image-size/large?v=v2&amp;px=999" role="button" title="Arunagirish1_1-1743925377318.png" alt="Arunagirish1_1-1743925377318.png" /></span></P><P>When using a function module (on-premise), always create a wrapper around it and<BR />release it with C1 to maintain cloud compatibility.</P><P><BR /><STRONG>Best Practices &amp; Recommendations</STRONG>:<BR />• Managed RAP with Unmanaged Save is often the recommended option, combining the best<BR />of both approaches.<BR />• Unmanaged RAP should usually be a last resort.</P><P><BR /><STRONG>Key Use Cases:</STRONG><BR />• No Custom Code: Go for Managed RAP.<BR />• Need to Utilize Update Function Module: opt for Managed RAP with Unmanaged Save.<BR />• Reusable Code/Loose Coupling with Pre-existing Functionality: Choose Unmanaged<BR />RAP.<BR />• Consume Custom APIs (on-premise) in BTP: Use Custom Entities + Unmanaged RAP.<BR />• Unmanaged Queries: Combine Custom Entities + Unmanaged RAP.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_3-1743925753359.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/247349i3DA0C69A93D319F3/image-size/large?v=v2&amp;px=999" role="button" title="Arunagirish1_3-1743925753359.png" alt="Arunagirish1_3-1743925753359.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2025-04-08T10:33:54.672000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/blog-series-intitial-loads-to-sap-advanced-event-mesh/ba-p/14074847 Blog Series - Intitial Loads to SAP Advanced Event Mesh 2025-04-14T06:34:57.975000+02:00 AlexPfeil https://community.sap.com/t5/user/viewprofilepage/user-id/165526 <P>In this blog series we focus on a Push Mechanism with the two Event Enabling Technologies&nbsp;</P><UL><LI>RAP (RESTful Application Programming Model):&nbsp;<A href="https://community.sap.com/t5/technology-blogs-by-sap/sap-business-events-initial-loads-using-rap-events/ba-p/14065678" target="_self">SAP Business Events: Initial Loads using RAP Events</A>&nbsp;</LI><LI>AIFAEM:&nbsp;<A href="https://community.sap.com/t5/technology-blogs-by-sap/how-to-perform-initial-loads-amp-bulk-init-loads-in-aifaem-triggered-by-an/ba-p/14027123" target="_self">How to perform Initial Loads &amp; Bulk Init Loads in AIFAEM triggered by an ABAP Program</A>&nbsp;</LI></UL><P>as well as a Pull Mechanism by using Odata APIs from SAP BTP Integration Suite:&nbsp;<A href="https://community.sap.com/t5/technology-blogs-by-sap/how-to-perform-initial-loads-from-s-4hana-to-advanced-event-mesh-with-odata/ba-p/14034749" target="_self">How to Perform Initial Loads from S/4HANA to Advanced Event Mesh with Odata Poll</A>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2025-04-14T06:34:57.975000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-do-background-scheduling-with-clean-core-including-fiori-apps/ba-p/14069752 How to do background scheduling with clean core, including Fiori apps, replacing se38 reports 2025-04-14T12:39:46.843000+02:00 mlauber https://community.sap.com/t5/user/viewprofilepage/user-id/157846 <P>Running certain functionality in background is a common requirement for ERP systems and no news for SAP. But with the increased importance of Clean Core and ABAP Cloud, how do we do it now? If you know a little about clean core, then you know that an se38 ABAP report, that is then scheduled in background, is <U>not</U> clean core, so that's not the answer. But does that mean we can schedule Fiori apps in background? Well, not exactly, but sort of&nbsp;<span class="lia-unicode-emoji" title=":smiling_face_with_smiling_eyes:">😊</span>. Let me explain.</P><P>&nbsp;</P><H1 id="toc-hId-1578459557">Background and SAP Fiori</H1><P>SAP Fiori is our design system at SAP, coming with guidelines, principals, buildings blocks and much, much, more. But in the end, the result of Fiori is visual; when you open a Fiori app, you see the web-based UI on your device that is based on Fiori. Thus, Fiori in itself, cannot "run in the background". But we can <STRONG>give background running capabilities to Fiori</STRONG>: either by adding it directly to a Fiori app, for example giving it a button a user can click, which then triggers certain functionality to run in the background. Or we can simply design a background job (now called <STRONG>Application Job Template</STRONG>) which we can run or schedule to run in the background via the SAP delivered Fiori app&nbsp;<A href="https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps('F1240')/S30PCE" target="_blank" rel="noopener nofollow noreferrer">Application Jobs (F1240)</A>. So in that sense we can say yes, we can have background jobs combined with Fiori apps.</P><P>If you are still confused, and no worries if you are, I completely understand, let me illustrate this by making an example for both options:</P><UL><LI>Creating an Application Job Template that can be run/scheduled in the <FONT color="#3366FF">Application Jobs</FONT> Fiori app, which allows us to run "any ABAP code" in background.&nbsp;</LI><LI>A RAP based Fiori app, in which a user can click a button to schedule a background job (using the Application Job Template as well)</LI></UL><P>&nbsp;</P><H1 id="toc-hId-1381946052">1 RAP business object</H1><P>First of, I need my RAP BO. Instead of using our beloved flight scenario, I opted to create a full custom example on my own. I may not go into full detail on every aspect of the RAP BO itself, as this blog focuses on the background running capability, so if you wonder anything, feel free to ask in the comments.</P><H2 id="toc-hId-1314515266">1.1 RAP BO data model</H2><P>In total I have following RAP BOs for my complete example:</P><UL><LI><STRONG>ZZ1_Author</STRONG> a <FONT color="#3366FF">Custom Business Object</FONT> built in Custom Business Object Fiori app containing name, birthdate and other fields for an author.</LI><LI><STRONG>ZR_Books</STRONG> containing simple fields such as the title of a book, number of pages, a price and more. And of course every book needs an author, so we associate ZZ1_Author (I didn't do parent-child composite relationship here for simpleness sake - if your scenario has parent-child relationship, everything will still work)</LI><LI><STRONG>ZR_Bookstores</STRONG> for my imaginary bookstores that sells ZR_Books. Again, for simpleness sake we just have fields for the bookstore itself, such as its name and address.</LI><LI><STRONG>ZR_BookstoreStockTP</STRONG> this now is my main BO combing bookstores and books. Please note, create/change/delete bookstores is only done with ZR_Bookstores and create/change/delete books with ZR_Books (I have behavior definitions with draft for both of them, projection views and finally OData V4 services, with simple Fiori Elements apps, one for each). The ZR_BookstoreStockTP&nbsp;BO as said combines these and also adds two stock values; one for how many of a certain book are available in the store currently, in shelves or on display, and how many of a certain book are in storage of that bookstore. To be perfectly transparent here, you could just use ZR_Bookstores as the main TP BO, having books as composite child and including the stock values that way. But these BOs have been used for other demo and use-cases by me, so I opted to reuse them as they are. So in conclusion, build your BO in the way it makes most sense.&nbsp; Back to ZR_BookstoreStockTP; we also have behavior definition, projections and finally an OData V4 service for my "Manage Bookstores" Fiori elements app.</LI></UL><P>Now if my stock gets low for some books in one of my stores, in my "Manage Bookstores" app I want to schedule a background job for creating new purchase orders for restocking (for demo purpose and because my books aren't real products in the system for which I can create a PO, I will simply increase the stock, but I hope you get the idea). Now before we get into how this is done on the BO behavior, we first need to prepare this application job template.</P><P>&nbsp;</P><H1 id="toc-hId-988919042">2 Setup custom Application Job Template</H1><P>First of, we need to create a new ABAP Cloud class in ADT. Now we already have a slight difference depending on your current system. Since Public Cloud release 2502, there is interface&nbsp;<STRONG>IF_APJ_RT_RUN</STRONG> available. This will be made available with the next full release of 2025 for private cloud and on-premise. So if you are not on public cloud, for now use 2 interfaces instead:&nbsp;<STRONG>IF_APJ_DT_EXEC_OBJECT</STRONG> and&nbsp;<STRONG>IF_APJ_RT_EXEC_OBJECT</STRONG>. If your Tier 1 development package doesn't default ABAP for Cloud Development language version, be sure to switch it for your class to be clean core compliant. With the interface(s) added to your class, we have 2 methods to implement (my demo system is on-premise so you will continue to see that version, but everything should work the same/very similar should you be on 2502).</P><H2 id="toc-hId-921488256">2.1 Define job parameters</H2><P>First of we need to define the possible parameters that could be entered by the Fiori app, or when we schedule the job via the Application Jobs Fiori app. Think of it as the selection-screen you had for the se38 report. It also works in the same way as we need to define either select-options or parameters. Below is my code as an example for the bookstore restocking scenario.</P><pre class="lia-code-sample language-abap"><code> method if_apj_dt_exec_object~get_parameters. et_parameter_def = value #( ( selname = 'P_STORID' kind = if_apj_dt_exec_object=&gt;parameter component_type = 'ZBOOKSTORE_ID' length = 3 changeable_ind = abap_true mandatory_ind = abap_true ) ( selname = 'S_BOOKID' kind = if_apj_dt_exec_object=&gt;select_option component_type = 'ZBOOK_ID' length = 6 changeable_ind = abap_true mandatory_ind = abap_true ) ( selname = 'P_REORQT' kind = if_apj_dt_exec_object=&gt;parameter datatype = 'I' length = 3 param_text = 'Reorder Qty' changeable_ind = abap_true mandatory_ind = abap_true ) ( selname = 'P_SIMUL' kind = if_apj_dt_exec_object=&gt;parameter datatype = 'C' length = 1 param_text = 'Simulate Only' changeable_ind = abap_true checkbox_ind = abap_true ) ). " Return default parameter values, if any et_parameter_val = value #( ( selname = 'P_REORQT' kind = if_apj_dt_exec_object=&gt;parameter sign = 'I' option = 'EQ' low = '10' ) ). endmethod.</code></pre><P>Few comments on above:</P><UL><LI>The names of the parameters can be max 8 letters, just like selection-screens.</LI><LI>When not using <STRONG>component_type</STRONG>, give <STRONG>datatype</STRONG> (max 4 letters), length and label text</LI><LI>The 2nd part, returning default values. is optional</LI><LI>In the older version as I'm using, if you want your job parameters to offer a search help in the final Fiori app for scheduling your job, it must be done via <STRONG>component_type</STRONG> field, which points to a DDIC data element, with a DDIC search help on it. I have done this data element and search-help assignment for my bookstore and my book. Now, in the newer version, we will be able to enter CDS value help views. But if you are not yet on 2502 as myself, my recommendation is this:<UL><LI>Create a CDS value help as per usual practice (this you can then directly assign in the 2502 version as CDS value help view)</LI><LI>Before 2502, next also create a DDIC value help, using the CDS value help as selection method: refer to the example below<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_0-1744276692365.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/249225i52A351963A052972/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_0-1744276692365.png" alt="mlauber_0-1744276692365.png" /></span></LI></UL></LI></UL><H2 id="toc-hId-724974751">2.2 Code the job logic</H2><P>Just like in an se38 report, all the logic that the job needs to do is coded into the 2nd, the <STRONG>EXECUTE</STRONG> method. It is highly recommended to also work with application logs, so that potential troubleshooting gets easy. The application log, when existing, is displayed in the <FONT color="#3366FF">Application Jobs</FONT> Fiori app.<BR />Now let's get to the meat of things. Here is my bookstore restocking code, which uses of course my RAP BO to do the restocking (or, at this point you could create real purchase orders or requisitions or whatever your process may be. You can also do as many checks as you need etc. etc. - write your ABAP code, but clean and in ABAP Cloud). Now don't get scared, but this code is quite a big bigger. I'll explain <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></P><pre class="lia-code-sample language-abap"><code>method if_apj_rt_exec_object~execute. data: lrt_book type range of zbook_id, lrs_book like line of lrt_book, lv_bookstore type zbookstore_id, lv_reorder_qty type zbooks_in_stock, lv_simulate type c length 1, lt_update type table for update zr_bookstorestocktp\\bookstorestock, lt_create type table for create zr_bookstorestocktp\\bookstorestock, ls_update type structure for update zr_bookstorestocktp\\bookstorestock, ls_create type structure for create zr_bookstorestocktp\\bookstorestock, lt_reported type response for reported early zr_bookstorestocktp, lt_failed type response for failed early zr_bookstorestocktp, ls_bookstore_book type zi_bookstorestock, lv_create type c length 1. try. " handle log if sy-batch = abap_true. " if we are running in background, we create application log go_log = cl_bali_log=&gt;create_with_header( header = cl_bali_header_setter=&gt;create( object = 'ZBOOKSTORE_01_LOG' subobject = 'ZBOOKSTORE_01_SUB' ) ). add_text_to_log( 'start job' ). final(lv_log_handle) = go_log-&gt;get_handle( ). add_text_to_log( |log handle: { lv_log_handle }| ). else. add_text_to_log( 'start job' ). add_text_to_log( |foreground run, no log created| ). endif. " Going through job parameter values loop at it_parameters into data(ls_parameter). case ls_parameter-selname. when 'S_BOOKID'. append value #( sign = ls_parameter-sign option = ls_parameter-option low = conv #( ls_parameter-low ) high = conv #( ls_parameter-high ) ) to lrt_book. when 'P_STORID'. lv_bookstore = conv #( ls_parameter-low ). when 'P_REORQT'. lv_reorder_qty = conv #( ls_parameter-low ). when 'P_SIMUL'. lv_simulate = ls_parameter-low. endcase. endloop. " get bookstore for which reorder was requested select single * from zr_bookstores where bookstoreid = _bookstore into (ls_bookstore). if sy-subrc &lt;&gt; 0. add_text_to_log( |bookstore not found with id: { lv_bookstore }| ). else. " go through books... loop at lrt_book into lrs_book. clear: lv_create, ls_bookstore_book, ls_update, ls_create. " check if bookstore already has the current book if lrs_book-option = 'EQ'. " only handling EQ! add_text_to_log( |processing book with id: { lrs_book-low }| ). select single * from zi_bookstorestock where bookstoreid = _bookstore and bookid = _book-low into _bookstore_book. if sy-subrc &lt;&gt; 0. " book is not yet in bookstore, check if book exists... select single from zi_books where bookid = _book-low into (lv_exists). if sy-subrc = 0. " book needs to be added (created) to bookstore lv_create = abap_true. ls_bookstore_book = value #( bookstoreid = lv_bookstore bookid = lrs_book-low booksinstore = 0 booksinstock = 0 ). else. add_text_to_log( |book does not exit and is ignored: { lrs_book-low }| ). continue. endif. endif. add_text_to_log( |stock qty. before: { ls_bookstore_book-booksinstock }| ). ls_bookstore_book-booksinstock += lv_reorder_qty. add_text_to_log( |stock qty. after: { ls_bookstore_book-booksinstock }| ). " set if we create or update the bookstore stock RAP BO if lv_create = abap_true. ls_create = corresponding #( ls_bookstore_book ). append ls_create to lt_create. else. ls_update = corresponding #( ls_bookstore_book ). append ls_update to lt_update. endif. endif. endloop. if sy-subrc &lt;&gt; 0. add_text_to_log( 'no books to reorder specified' ). endif. endif. " UPDATE or SIMULATION ---------------------------------------------- if lv_simulate = abap_true. add_text_to_log( 'SIMULATION only, no changes saved' ). else. " change data ----------------------------------------------------- if lt_update is not initial. modify entities of zr_bookstorestocktp entity bookstorestock update fields ( booksinstock ) with lt_update reported data(update_reported) failed data(update_failed). if update_failed is initial. " commit change commit entities response of zr_bookstorestocktp reported data(commit_reported1) failed data(commit_failed1). " set errors if commit failed lt_reported = corresponding #( deep commit_reported1 ). lt_failed = corresponding #( deep commit_failed1 ). else. " set error lt_reported = corresponding #( update_reported ). lt_failed = corresponding #( update_failed ). endif. endif. " create data ----------------------------------------------------- if lt_create is not initial. modify entities of zr_bookstorestocktp entity bookstorestock create fields ( bookstoreid bookid booksinstock booksinstore ) auto fill cid with lt_create reported data(create_reported) failed data(create_failed). if create_failed is initial. " commit created commit entities response of zr_bookstorestocktp reported data(commit_reported2) failed data(commit_failed2). " set errors if commit failed lt_reported = corresponding #( deep commit_reported2 ). lt_failed = corresponding #( deep commit_failed2 ). else. " set error lt_reported = corresponding #( create_reported ). lt_failed = corresponding #( create_failed ). endif. endif. endif. if lt_failed is initial. " SUCCESS ------------------------------- add_text_to_log( |commit success, nothing in failed| ). else. " ERRORS -------------------------------- loop at lt_failed-bookstorestock into data(ls_failed). add_text_to_log( |failed to modify bookstore { ls_failed-bookstoreid } with book { ls_failed-bookid }| ). add_text_to_log( |reason: { ls_failed-%fail-cause }| ). endloop. endif. add_text_to_log( |job finished| ). catch cx_bali_runtime into data(lx_bali_exception). data(lv_log_exception) = lx_bali_exception-&gt;get_text( ). raise exception type cx_apj_rt_content exporting previous = lx_bali_exception. endtry. endmethod.</code></pre><P>And here the comments for above, going top to bottom:</P><UL><LI>We start by handling the <STRONG>application log</STRONG>. Now because I wanted to test this class in foreground running, I added foreground support, but this is of course not needed. We have the instance attribute go_log which is a reference to&nbsp;<SPAN><SPAN><STRONG>if_bali_log</STRONG>. In order to create the object, we must enter an <FONT color="#3366FF">application log object and sub-object</FONT>, so a prerequisite for this, is that these are already created in your system. This can simply be done in ADT, via <STRONG>New - Other - search for "Application Log Object"</STRONG>. Here a screenshot of mine:<BR /></SPAN></SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_1-1744278536107.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/249243i5B3A99F4CE4EDD45/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_1-1744278536107.png" alt="mlauber_1-1744278536107.png" /></span></LI><LI>Next we loop over all parameters which were passed into the background job run, and filling internal tables for further checking/selecting data.</LI><LI>Now we start using the RAP BO for checks, such as reading the bookstore and also all books are checked if they already exist. This job cannot create new books, but it can add an existing book to an existing bookstore, even if that bookstore does not yet have that book. This is done during the lrt_books loop, when we read from the interface view&nbsp;<SPAN><STRONG>zi_bookstorestock</STRONG> to check if the current looped book is already in the store. If not, we add everything into an EML create table, otherwise an EML update table.</SPAN></LI><LI><SPAN>Lastly, we check if the simulation checkbox is true (this is one of the job parameters - again not mandatory; you can create a job template without a simulation checkbox, but I added it), in which case we don't proceed with any change to our RAP BO. If this is not a simulation run, then we use EML to create and/or update ZR_BookstoreStockTP. All these EML are checked for fails and only if everything goes well, we commit our changes.</SPAN></LI><LI><SPAN>At the very end after all create/update has been completed, we check whether we had errors and add the errors into the application log. If we had no errors, we write a success message in the log.</SPAN></LI><LI><SPAN>Note that the full execution is basically in a try-catch-block. This is because the application job object may raise an exception, so we have to catch it.</SPAN></LI><LI><SPAN>You may noticed the method call add_text_to_log, which again is a simple method I built that allows me to handle both foreground and background log. Here the code for that:</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code> method add_text_to_log. " running in background for foreground... if sy-batch = abap_true. " background - create text object for log data(o_go_log_free_text) = cl_bali_free_text_setter=&gt;create( severity = if_bali_constants=&gt;c_severity_status text = i_text ). o_go_log_free_text-&gt;set_detail_level( detail_level = '1' ). " add text object to log go_log-&gt;add_item( item = o_go_log_free_text ). " save log cl_bali_log_db=&gt;get_instance( )-&gt;save_log( log = go_log assign_to_current_appl_job = abap_true ). else. " foreground - simply add text to instace attribute, which can be fetched by caller program gs_fglog-no += 1. gs_fglog-text = i_text. append gs_fglog to gt_fglog. endif. endmethod.</code></pre><P>That's is! Logic and coding part completed. But how can we now trigger this code to run in the background? Now we get into what we call <STRONG>Application Job Template</STRONG>.</P><H2 id="toc-hId-528461246">2.3 Application Job Catalog Entry and Template</H2><P>Creating the APJ class is a must, before we can create the actual Application Job Template. And the job template requires a Job Catalog Entry. All is again done in ADT. Via File - New - Other - and then search for "application job":</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_0-1744617587183.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250266i01CA2DCA79667C76/image-size/medium?v=v2&amp;px=400" role="button" title="mlauber_0-1744617587183.png" alt="mlauber_0-1744617587183.png" /></span></P><P>We start with <STRONG>Application Job Catalog Entry</STRONG>. Here we must now enter the ABAP class, which has the EXECUTE method. Now depending on if your are in public cloud or on-premise/private, the next screen will look quite a bit different. I'll showcase a screenshot of both for reference:</P><UL><LI>On-premise/private cloud - what you see when you <EM>double click</EM> the job catalog entry in ADT:<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_2-1744617826367.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250268i9BE9497645245636/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_2-1744617826367.png" alt="mlauber_2-1744617826367.png" /></span></LI><LI>On-premise/private cloud - what you see when you <EM>right-click</EM> the job catalog entry in ADT and choose <EM>Open With - SAP GUI</EM>:<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_3-1744617913160.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250271iF941338A67A23225/image-size/medium?v=v2&amp;px=400" role="button" title="mlauber_3-1744617913160.png" alt="mlauber_3-1744617913160.png" /></span><P>&nbsp;</P></LI><LI>Public cloud:<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="job cat entry.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250272i20B3F4D32CF0EC62/image-size/large?v=v2&amp;px=999" role="button" title="job cat entry.jpg" alt="job cat entry.jpg" /></span></LI></UL><P>For this demo, this is all we do here: we create the job catalog entry and give our ABAP class and we are done. At this point you would assign CDS value helps on parameters as shown in the public cloud screenshot, but as mentioned, this is not yet available before 2502.</P><P>The 2nd step is the <STRONG>Application Job Template</STRONG>, which then can be used in Fiori app <FONT color="#3366FF">Application Jobs</FONT>. As before, in ADT choose New - File - Other and this time choose "Application Job Template". Here now we give the Application Job Catalog Entry we just created, and that's it. If you like, you can default parameter values here. This is only taken into account if you create a new "job" via&nbsp;<FONT color="#3366FF">Application Jobs</FONT> Fiori app, where these parameters are then suggested as default, but can be changed. If we schedule the background run programmatically, no parameters are set by default.</P><P>With this we are now ready. We can already schedule a background job in&nbsp;<FONT color="#3366FF">Application Jobs</FONT> Fiori app and we can code an action for our RAP BO.</P><P>All we've discussed so far, you&nbsp;may also refer to SAP Help:&nbsp;<A href="https://help.sap.com/docs/ABAP_PLATFORM_NEW/b5670aaaa2364a29935f40b16499972d/0837d1ea0b0b4d3892f66e8533b654cb.html?version=202310.003" target="_blank" rel="noopener noreferrer">Application Jobs | SAP Help Portal</A>&nbsp;and&nbsp;<A href="https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/working-with-application-job-objects" target="_blank" rel="noopener noreferrer">Working with Application Job Objects | SAP Help Portal</A>.</P><P>&nbsp;</P><H1 id="toc-hId-202865022">3 Scheduling/running in background</H1><H2 id="toc-hId-135434236">3.1 Using Application Jobs Fiori app (to run immediately or schedule)</H2><P>Let's start with the simpler option; scheduling our bookstore restocking to run in the background, via <FONT color="#3366FF">Application Jobs</FONT> Fiori app. Maybe a new book has just come out and we know for the next five weeks we want to reorder that book once a week. Go to your Fiori Launchpad and make sure you have a role with access to the app. Open Application Jobs app. From the start screen, select <STRONG>Create</STRONG> button. A wizard opens and it always defaults the alphabetically first Application Job Template. Clear the field and open the search help and find the Application Job Template we created in step 2. This is mine:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_4-1744619638308.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250294iE262B9C416A07671/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_4-1744619638308.png" alt="mlauber_4-1744619638308.png" /></span></P><P>Optionally, you can rename the current Job we are creating, such as weekly restock or something. Click on Step 2 to continue the wizard.</P><P>Here you can enter the recurring pattern, just like you would for se38 reports. Be sure to setup bigger jobs with heavy processing logic to run outside of office hours, as per usual practice. For now I picked "start immediately" to showcase that my application job template is running:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_5-1744619946778.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250297iB6F82DD6DD5B4C35/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_5-1744619946778.png" alt="mlauber_5-1744619946778.png" /></span></P><P>Continue by clicking Step 3. Here finally we enter the parameters and if you defined value helps for your parameters, this should be user friendly to use.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_6-1744620187794.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250298i002B5B1DCF6ECE8C/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_6-1744620187794.png" alt="mlauber_6-1744620187794.png" /></span></P><P>Lastly when ready, click on <STRONG>Schedule</STRONG>.</P><P>Now because I told my job to start immediately, I can see the result of the first background run:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_7-1744620271205.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250303i4DA6C27D96E2DCF3/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_7-1744620271205.png" alt="mlauber_7-1744620271205.png" /></span></P><P>Note the green checkmark button under Log, meaning we do have an application log for this job. Let's click on it:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_8-1744620341339.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250304iBD9C77AECF9DE8CA/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_8-1744620341339.png" alt="mlauber_8-1744620341339.png" /></span></P><P>And lo and behold, our log exactly as we coded it, linked to our just scheduled application job.</P><H2 id="toc-hId--61079269">3.2 Running background job from our own custom, RAP based, Fiori app</H2><P>Now that the application job template is ready, we can also schedule a background job from a Fiori app. I will be using my RAP BO as explained in step 1. Now just as a little information, this is how my Fiori app looks like for ZR_BookstoreStockTP:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_9-1744621824816.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250321i8E43D111F94878C4/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_9-1744621824816.png" alt="mlauber_9-1744621824816.png" /></span></P><P>We can clearly see our stock values, now let's add a button to run the restocking in background.</P><H3 id="toc-hId--203741424">3.2.1 Abstract CDS entity for action parameters</H3><P>I create a super simple abstract entity, containing all parameters I need to run the background job:</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Abstract entity to reorder books' define root abstract entity ZD_Reorder_Books { .defaultValue : '10' @EndUserText.label: 'Quantity to order' ReorderQty : zbooks_in_stock; }</code></pre><P>Secondly, I have a behavior for the same abstract entity, to ensure mandatory parameters:</P><pre class="lia-code-sample language-abap"><code>abstract; define behavior for ZD_Reorder_Books //alias &lt;alias_name&gt; { field ( mandatory ) ReorderQty; }</code></pre><H3 id="toc-hId--400254929">3.2.2 Action in behavior definition</H3><P>Next we add the action to our RAP BO behavior (I'm only copying the action line here, not the whole behavior):</P><PRE><SPAN>action (lock:none) reorderBooks parameter ZD_Reorder_Books;</SPAN></PRE><P><SPAN>Few comments: the <FONT face="courier new,courier">(lock:none)</FONT> is needed as I will run this action for all selected instances at once, instead of per instance (I explain that further on&nbsp; the UI annotation part 3.2.3). Otherwise, quite straightforward action with input parameter.</SPAN></P><H3 id="toc-hId--596768434"><SPAN>3.2.3 UI annotation to display action button (and control how it calls the RAP framework)</SPAN></H3><P><SPAN>Complicated title, small thing. First of, my action is an <STRONG><U>instance</U> action</STRONG> (because I did not define it as a static action in the behavior definition) and it's also not a factory action, so it requires that in the result list in Fiori, at least 1 instance is selected through the checkboxes, for the action to run. Now to display the button in the table header, we need to annotate it at the first table field:</SPAN></P><pre class="lia-code-sample language-abap"><code> <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>: { lineItem: [ { position: 10, importance: #HIGH }, { type: #FOR_ACTION, dataAction: 'addBook', label: 'Add Book to Bookstore' }, { type: #FOR_ACTION, dataAction: 'reorderBooks', label: 'Reorder Books', invocationGrouping: #CHANGE_SET }, { type: #FOR_ACTION, dataAction: 'setInventory', label: 'Adjust Store Inventory', inline: true } ] } BookstoreId;</code></pre><P>The 2nd <FONT face="courier new,courier">#FOR_ACTION</FONT> is our reorderBooks action we just added to the behavior definition.&nbsp; With this, a button with label "Reorder Books" will now be displayed on the table.</P><P>Secondly, I defined <FONT face="courier new,courier">invocationGrouping: #CHANGE_SET</FONT>.&nbsp; This means that when more than 1 instance is selected when the action button is pressed, that instead of calling the action once per instance, the action groups all instances into one single call. That is why I had to define the <FONT face="courier new,courier">(lock:none)</FONT> in the behavior definition, because otherwise all instances would be locked by the time the action runs. Meaning I need to take care of any potential locking (which is not needed in this case, as we will schedule a background job).</P><H3 id="toc-hId--793281939">3.2.4 Action behavior implementation</H3><P>The 2nd to last step is the coding of our action, of course in the behavior implementation class. Here is mine:</P><pre class="lia-code-sample language-abap"><code> method reorderbooks. data: lv_bookstore type zbookstore_id, lv_stop type abap_bool, ls_job_parameter type cl_apj_rt_api=&gt;ty_job_parameter_value, ls_book_job_param type cl_apj_rt_api=&gt;ty_job_parameter_value, ls_value_range type cl_apj_rt_api=&gt;ty_value_range. check keys is not initial. " reset global static job parameter table zbp_r_bookstorestocktp=&gt;gt_job_parameters = value #( ). " go through all selected books (and bookstore) loop at keys into data(ls_key). if lv_bookstore is initial. " set bookstore (ONCE) -------------------------------- lv_bookstore = ls_key-bookstoreid. ls_job_parameter-name = zcl_apj_test=&gt;gc_para_store. ls_value_range-sign = 'I'. ls_value_range-option = 'EQ'. ls_value_range-low = lv_bookstore. append ls_value_range to ls_job_parameter-t_value. " add to global static table, used in save modified append ls_job_parameter to zbp_r_bookstorestocktp=&gt;gt_job_parameters. clear: ls_job_parameter, ls_value_range. " set reorder quantity (ONCE) ------------------------- if ls_key-%param-reorderqty &lt;= 0. lv_stop = abap_true. append value #( %tky = ls_key-%tky ) to failed-bookstorestock. append value #( %msg = new_message_with_text( severity = ms-error text = |Please set reorder quantity to 1 or higher| ) ) to reported-bookstorestock. else. ls_job_parameter-name = zcl_apj_test=&gt;gc_para_qty. ls_value_range-sign = 'I'. ls_value_range-option = 'EQ'. ls_value_range-low = conv #( ls_key-%param-reorderqty ). append ls_value_range to ls_job_parameter-t_value. " add to global static table, used in save modified append ls_job_parameter to zbp_r_bookstorestocktp=&gt;gt_job_parameters. clear: ls_job_parameter, ls_value_range. endif. elseif lv_bookstore &lt;&gt; ls_key-bookstoreid. " make sure we have only 1 bookstore selected lv_stop = abap_true. append value #( %tky = ls_key-%tky ) to failed-bookstorestock. append value #( %msg = new_message_with_text( severity = ms-error text = |Please select books for only 1 bookstore| ) ) to reported-bookstorestock. exit. endif. " set selected book check lv_stop = abap_false. ls_value_range-sign = 'I'. ls_value_range-option = 'EQ'. ls_value_range-low = ls_key-bookid. append ls_value_range to ls_book_job_param-t_value. clear ls_value_range. endloop. check lv_stop = abap_false. " set all books parameter (range table filled in above loop) ls_book_job_param-name = zcl_apj_test=&gt;gc_para_book. " add to global static table, used in save modified append ls_book_job_param to zbp_r_bookstorestocktp=&gt;gt_job_parameters. endmethod.</code></pre><P>Now if you read above code, you will notice it doesn't do much yet; it performs some checks (only one bookstore selected) and eventually fills an application job parameters table which is a global class static variable, and that's it. The reason is that when we schedule the background job via&nbsp;<SPAN><STRONG>cl_apj_rt_api</STRONG>, unfortunately as of right now, this class eventually calls a DB INSERT. For those more familiar with RAP know that it's not allowed to have any commits or hard DB changes during action processing. So we cannot actually schedule the background job from the the action itself, we can only prepare it because at this points we have all the instances that were selected on the Fiori UI. So to schedule the job, we need one final step...</SPAN></P><H3 id="toc-hId--989795444"><SPAN>3.2.5 Schedule background job programmatically from RAP additional save</SPAN></H3><P><SPAN>We need "additional save". This can be included in a behavior definition. We simply add "with additional save" at the beginning of the behavior definition:</SPAN></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_r_bookstorestocktp unique; strict ( 2 ); define behavior for ZR_BookstoreStockTP alias BookstoreStock with additional save</code></pre><P>Using ADT quick fix, you can add the corresponding method and class to your implementation class:</P><pre class="lia-code-sample language-abap"><code>class lsc_zr_bookstorestocktp definition inheriting from cl_abap_behavior_saver. protected section. methods save_modified redefinition. endclass.</code></pre><P>We get a new local class that instead inherits from <STRONG>cl_abap_behavior_saver</STRONG> and has method <STRONG>save_modified</STRONG>.</P><P>Now finally in this method, we can call our application job template to run in the background:</P><pre class="lia-code-sample language-abap"><code> method save_modified. data: lv_job_template_name type cl_apj_rt_api=&gt;ty_template_name value 'ZBOOK_APJ_TEMPL', ls_job_parameter type cl_apj_rt_api=&gt;ty_job_parameter_value, ls_value_range type cl_apj_rt_api=&gt;ty_value_range, ls_job_start_info type cl_apj_rt_api=&gt;ty_start_info, lv_job_name type cl_apj_rt_api=&gt;ty_jobname, lv_job_count type cl_apj_rt_api=&gt;ty_jobcount. check zbp_r_bookstorestocktp=&gt;gt_job_parameters is not initial. " get booktsore id from job parameters ls_job_parameter-name = zcl_apj_test=&gt;gc_para_store. read table zbp_r_bookstorestocktp=&gt;gt_job_parameters from ls_job_parameter using key name into ls_job_parameter. check sy-subrc = 0. read table ls_job_parameter-t_value index 1 into ls_value_range. check sy-subrc = 0. " reorder background job ------------------------------------------------ try. ls_job_start_info-start_immediately = abap_true. " schedule job cl_apj_rt_api=&gt;schedule_job( exporting iv_job_template_name = lv_job_template_name iv_job_text = |Restocking bookstore { ls_value_range-low }| is_start_info = ls_job_start_info it_job_parameter_value = zbp_r_bookstorestocktp=&gt;gt_job_parameters " built during action implemention importing ev_jobname = lv_job_name ev_jobcount = lv_job_count ). " success message append value #( %msg = new_message_with_text( severity = ms-success text = |Reorder job scheduled (job name: { lv_job_name })| ) ) to reported-bookstorestock. catch cx_apj_rt into data(lx_job_scheduling_error). append value #( %msg = new_message( id = 'ZBOOKSTORE' number = 000 severity = ms-error v1 = lx_job_scheduling_error-&gt;get_longtext( ) v2 = lx_job_scheduling_error-&gt;bapimsg-message ) ) to reported-bookstorestock. catch cx_root into data(lx_root_exception). append value #( %msg = new_message( id = 'ZBOOKSTORE' number = 001 severity = if_abap_behv_message=&gt;severity-error v1 = lx_root_exception-&gt;get_longtext( ) ) ) to reported-bookstorestock. endtry. endmethod.</code></pre><P>Very simple code really. We need of course to refer to an Application Job Template, the one we created. We define the job starting info and lastly the parameters, which we already prepared in he action implementation.&nbsp;</P><P>And that's all. Now in my Fiori app I can select some instances and click on the button for Reorder and eventually a new background job is run, which I can also monitor in the Application Jobs Fiori app, including of course the log.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_10-1744624117797.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250370i4091007BE30D8D29/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_10-1744624117797.png" alt="mlauber_10-1744624117797.png" /></span></P><P>&nbsp;</P><H1 id="toc-hId--599502935">Conclusion</H1><P>Any kind of background capability can be provided clean core compliant via Fiori (even if no foreground application at all is needed - we can do the recurring background scheduling in <FONT color="#3366FF">Application Jobs</FONT> Fiori app, as shown in step 3.1), so let's get away from our old-school se38 reports and instead work with RAP and Application Job Templates. We can also make use of SAP standard RAP, such as creating PO or other.</P><P>And do keep in mind the switch to the newer APJ version with 2025 release!</P><P>Hope this helps and let me know if there are any open questions regarding this topic.</P> 2025-04-14T12:39:46.843000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/abap-cloud-landing-page-in-abap-development-tools-adt/ba-p/14079929 ABAP Cloud Landing Page in ABAP Development Tools (ADT) 2025-04-19T08:05:51.235000+02:00 pavankumar_reddy90 https://community.sap.com/t5/user/viewprofilepage/user-id/189954 <P><STRONG><SPAN class=""><SPAN class="">I am an ABAP developer&nbsp; How can I learn about ABAP Cloud?</SPAN></SPAN></STRONG><SPAN class=""><STRONG>&nbsp;</STRONG><BR /></SPAN></P><P><STRONG><SPAN>"Learn About ABAP Cloud Landing Page"</SPAN></STRONG><SPAN>&nbsp;in ADT is one way of learning. The ABAP Cloud Landing Page serves as a comprehensive gateway that caters to developers across all proficiency levels - from newcomers to seasoned ABAP experts. </SPAN></P><P><SPAN>This centralized platform offers a meticulously organized collection of SAP-validated, trusted, repository, resources, making it effortless to navigate through essential information. The page features distinct sections including "Get Started," "ABAP Cloud wizards for each Development Scenarios," documentation links, Roadmaps, What's New Information, available tutorials, ensuring a smooth learning journey.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG>How to navigate to Learn About ABAP Cloud Landing Page in ADT?<BR /></STRONG>ADT (ABAP in Eclipse )-&gt;Select Project -&gt; Help -&gt; Learn About About ABAP Cloud</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1744975730313.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252381iF98DC7AE49043600/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1744975730313.png" alt="pavankumar_reddy90_0-1744975730313.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_1-1744975893190.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252382iD7D7A6279A7F4132/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_1-1744975893190.png" alt="pavankumar_reddy90_1-1744975893190.png" /></span></P><P>This page features several sections:</P><OL><LI>Get Started</LI><LI>News</LI><LI>Transactional Apps</LI><LI>Analytical Apps</LI><LI>Integration Services</LI><LI>More about cloud development Concepts.</LI></OL><P><STRONG>1. Get Started - Learn More Section</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_3-1744976494662.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252384i0A1C233F06330513/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_3-1744976494662.png" alt="pavankumar_reddy90_3-1744976494662.png" /></span></P><P>When you click on "<STRONG>Learn more</STRONG>"&nbsp; it navigate to SAP help documentation where you get the most important information about ABAP Cloud in one page.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_4-1744977933161.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252389iAAEA487826865E63/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_4-1744977933161.png" alt="pavankumar_reddy90_4-1744977933161.png" /></span></P><P><STRONG>1.1. Get Started - Resources</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_6-1744978045261.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252391i2AD9E335E5C79AFB/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_6-1744978045261.png" alt="pavankumar_reddy90_6-1744978045261.png" /></span></P><P>The resource overview includes documentation which provides detailed technical information about ABAP, tutorials that guide users through specific tasks or processes, blog posts that offer insights and tips from experienced users and developers, and videos that visually explain and demonstrate ABAP concepts and programming techniques.&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1744991135682.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252472i5EBAD2AA662A97F6/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1744991135682.png" alt="pavankumar_reddy90_0-1744991135682.png" /></span></P><P>You will also find "Resources" in every section where corresponding blogs, how to guides, tutorials, events sessions are linked accordingly.</P><P>2. <STRONG>News - Roadmap</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_2-1744992987020.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252483iEC7FAFAFAAABA815/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_2-1744992987020.png" alt="pavankumar_reddy90_2-1744992987020.png" /></span></P><P><SPAN>News Roadmap section gives about the ABAP Cloud roadmap, planned and upcoming features across all solutions that offer ABAP Cloud.<BR /></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_1-1744991986597.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252475iFE57143245A335E1/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_1-1744991986597.png" alt="pavankumar_reddy90_1-1744991986597.png" /></span></P><P><STRONG>3. Transactional Apps - Open Generators&nbsp;</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_3-1744993267192.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252484iEEC28192C9B6EF31/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_3-1744993267192.png" alt="pavankumar_reddy90_3-1744993267192.png" /></span></P><P>Generators help you to develop transactional applications with low code. Through wizard in ADT you can develop a transaction applications.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_4-1744993429986.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252485i165C3C175823D7BD/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_4-1744993429986.png" alt="pavankumar_reddy90_4-1744993429986.png" /></span></P><P><STRONG>Learn More Section </STRONG></P><P>Learn More section navigates to help documentation for how to develop transactional, analytical and Integration services in ABAP Cloud.</P><P><STRONG>4. Analytical Apps</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1745036917092.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252522iF33BA821572E72DD/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_0-1745036917092.png" alt="pavankumar_reddy90_0-1745036917092.png" /></span></P><P>In this section you will find help documentation and resources related to how develop analytical apps in ABAP Cloud.</P><P><STRONG>5. Integration Services - Open Generators</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_3-1745037156119.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252525iB06B6B83263233B3/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_3-1745037156119.png" alt="pavankumar_reddy90_3-1745037156119.png" /></span></P><P>Click Open Generators -&gt;&nbsp; Choose a Project from Project Select -&gt; Click OK</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_1-1745037055592.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252523i3DCABEB5A7EAC0E5/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_1-1745037055592.png" alt="pavankumar_reddy90_1-1745037055592.png" /></span>Generators help you to develop Integration services with low code.&nbsp;</P><P><STRONG>6 More Section</STRONG></P><P><STRONG>Localization and Internalization</STRONG></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_5-1745037437697.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252527i528AB65F851C5031/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_5-1745037437697.png" alt="pavankumar_reddy90_5-1745037437697.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1745041831854.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252538i4EC60DB206BCAC0E/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1745041831854.png" alt="pavankumar_reddy90_0-1745041831854.png" /></span></P><P><SPAN>This sections given information about how apps and services can be translated and adapted to country/region-specific requirements.</SPAN></P><P><STRONG>Access Management</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_6-1745037615597.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252528i1854F6DA9258BBF7/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_6-1745037615597.png" alt="pavankumar_reddy90_6-1745037615597.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_1-1745041892953.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252539i6192DEE7C65C0E34/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_1-1745041892953.png" alt="pavankumar_reddy90_1-1745041892953.png" /></span></P><P>This section gives&nbsp;<SPAN>a overview of the important points about identity and access management considerations for SAP S/4HANA Cloud, public edition and SAP BTP, ABAP environment.</SPAN></P><P><STRONG>Extensibility:</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_7-1745037784843.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252529i8524345288152089/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_7-1745037784843.png" alt="pavankumar_reddy90_7-1745037784843.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_2-1745041948301.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252540iFB68C849680AA5C7/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_2-1745041948301.png" alt="pavankumar_reddy90_2-1745041948301.png" /></span></P><P>This section give information about extensibility options in ABAP Cloud such as:</P><OL><LI><SPAN>Key User Extensibility</SPAN></LI><LI><SPAN>Developer Extensibility</SPAN></LI><LI><SPAN>Side-by-Side Extensibility</SPAN></LI><LI><SPAN>Difference between these extensibility options when to which extensibility.&nbsp;</SPAN></LI><LI><SPAN>Extensibility Tools for Key users and developers.</SPAN></LI></OL><P><STRONG>More ABAP Cloud Development</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_3-1745041987557.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252541iF3ACAB30F2C35EA5/image-size/medium?v=v2&amp;px=400" role="button" title="pavankumar_reddy90_3-1745041987557.png" alt="pavankumar_reddy90_3-1745041987557.png" /></span></P><P><STRONG>Test:&nbsp;&nbsp;</STRONG>This section gives overview of&nbsp;<SPAN>requirements and considerations during the test phase of application development in ABAP cloud.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_4-1745042048424.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252542i86449791B0B80256/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_4-1745042048424.png" alt="pavankumar_reddy90_4-1745042048424.png" /></span></P><P><STRONG>Extend:&nbsp;</STRONG>This section gives information about&nbsp;<SPAN>how to extend existing services and apps,&nbsp; background information about the release contract framework and the ABAP language.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_5-1745042099883.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252543iF1204C6B31166AD9/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_5-1745042099883.png" alt="pavankumar_reddy90_5-1745042099883.png" /></span></P><P><STRONG>Deploy:&nbsp;</STRONG>This section gives information about&nbsp;<SPAN>how to deploy and transport your app or service with ABAP lifecycle management.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_6-1745042154415.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252544iBEE8A35CC6DDA4E3/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_6-1745042154415.png" alt="pavankumar_reddy90_6-1745042154415.png" /></span></P><P><STRONG>Administrator, Configure and Monitor: </STRONG>This section gives information about the requirements and tools involved with, for example, monitoring an app or service that you've developed,&nbsp; Analyzing SQL statements, Identify and Access Management, Communication Management, security and Transport Management in ABAP Cloud.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_7-1745042211401.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252545iB56181A74FBE6B2A/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_7-1745042211401.png" alt="pavankumar_reddy90_7-1745042211401.png" /></span></P><P><SPAN class="">This centralized landing page in ADT offers a meticulously organized collection of SAP-validated, trusted, repository, resources, making it effortless to navigate through essential information at single page&nbsp; which&nbsp;<SPAN>caters to developers across all proficiency levels - from newcomers to seasoned ABAP experts.</SPAN></SPAN></P><P><SPAN class=""><SPAN>ABAP Cloud Landing Page will be updated with new content by SAP every BTP, S/4HANA release. Then tiles, links, generators shown in this blogs, page would be updated accordingly with new releases.&nbsp; Update you ADT to latest version when a new update comes up to see new changes to this page.&nbsp;<BR /></SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1745043496508.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/252546i5BC0EBA656D7D868/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1745043496508.png" alt="pavankumar_reddy90_0-1745043496508.png" /></span></P><P>&nbsp;</P><P><SPAN class=""><SPAN><BR /><BR /><BR /><BR /><BR /></SPAN></SPAN></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><SPAN>&nbsp;</SPAN></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><SPAN>&nbsp;</SPAN></P><P>&nbsp;</P> 2025-04-19T08:05:51.235000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/rap-numbering-early-numbering-late-numbering-managed-numbering/ba-p/14075168 RAP: Numbering | Early numbering | Late numbering | Managed numbering 2025-04-21T09:38:17.384000+02:00 Arunagirish1 https://community.sap.com/t5/user/viewprofilepage/user-id/889803 <P class="lia-align-center" style="text-align: center;"><FONT size="5"><STRONG>Numbering</STRONG></FONT></P><H1 id="toc-hId-1579258185">&nbsp;</H1><H1 id="toc-hId-1382744680"><FONT size="5"><SPAN>Road Map for Learning Numbering in RAP</SPAN></FONT></H1><P class="lia-indent-padding-left-60px" style="padding-left : 60px;"><FONT size="5"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_0-1744621645483.jpeg" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250312i9CA6C20071EDDE48/image-size/medium?v=v2&amp;px=400" role="button" title="Arunagirish1_0-1744621645483.jpeg" alt="Arunagirish1_0-1744621645483.jpeg" /></span><STRONG><SPAN>&nbsp;</SPAN></STRONG></FONT></P><P>&nbsp;</P><P><FONT size="6"><STRONG><SPAN>Early numbering:</SPAN></STRONG></FONT></P><P><SPAN>The numbering type early numbering refers to an early value assignment for the primary key field. In this case, the final key value is available in the transactional buffer instantly after the MODIFY request for CREATE.</SPAN></P><H1 id="toc-hId-1186231175">&nbsp;</H1><H1 id="toc-hId-989717670"><FONT size="5"><SPAN>1.&nbsp; </SPAN><SPAN>External Early Numbering:</SPAN></FONT></H1><UL><LI><SPAN>consumer provides key</SPAN></LI><LI><SPAN>Key should be unique</SPAN></LI><LI><SPAN>key should be editable in create, and non-editable in modify. syntax:</SPAN></LI></UL><P class="lia-indent-padding-left-30px" style="padding-left : 30px;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_1-1744621645484.jpeg" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250311iDB4663EAA5B84F93/image-size/medium?v=v2&amp;px=400" role="button" title="Arunagirish1_1-1744621645484.jpeg" alt="Arunagirish1_1-1744621645484.jpeg" /></span></P><H1 id="toc-hId-793204165">&nbsp;</H1><H1 id="toc-hId-596690660"><FONT size="5"><SPAN>2.&nbsp; </SPAN><SPAN>Managed Internal Early Numbering:</SPAN></FONT></H1><UL><LI><SPAN>Managed early numbering is only possible for key fields with ABAP type raw (16) (UUID) of BOs with</SPAN></LI></UL><P><SPAN>implementation type managed.</SPAN></P><UL><LI><SPAN>UUID is read-only. syntax:</SPAN></LI></UL><P class="lia-indent-padding-left-30px" style="padding-left : 30px;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_2-1744621645486.jpeg" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250313i7A99DA75D6F0C593/image-size/medium?v=v2&amp;px=400" role="button" title="Arunagirish1_2-1744621645486.jpeg" alt="Arunagirish1_2-1744621645486.jpeg" /></span></P><H1 id="toc-hId-400177155">&nbsp;</H1><H1 id="toc-hId-203663650"><FONT size="5"><SPAN>3.&nbsp; </SPAN><SPAN>Unmanaged Internal Early Numbering:</SPAN></FONT></H1><UL><LI><SPAN>Unmanaged Early Numbering is available in Managed and Unmanaged Implementation Scenarios with Draft Capabilities</SPAN></LI><LI><SPAN>Key is read-only. Syntax:</SPAN></LI></UL><P>Bdef:</P><P class="lia-indent-padding-left-30px" style="padding-left : 30px;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_3-1744621645487.jpeg" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250315iE66488042F386417/image-size/medium?v=v2&amp;px=400" role="button" title="Arunagirish1_3-1744621645487.jpeg" alt="Arunagirish1_3-1744621645487.jpeg" /></span></P><P><SPAN>Local handler class:</SPAN></P><P class="lia-indent-padding-left-30px" style="padding-left : 30px;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_4-1744621645487.jpeg" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250314i45DC29B4CE4C1454/image-size/medium?v=v2&amp;px=400" role="button" title="Arunagirish1_4-1744621645487.jpeg" alt="Arunagirish1_4-1744621645487.jpeg" /></span></P><H1 id="toc-hId-7150145">&nbsp;</H1><H1 id="toc-hId--189363360"><FONT size="6"><SPAN>Late numbering:</SPAN></FONT></H1><UL><LI><SPAN>The RAP runtime engine assigns values to the primary key fields.</SPAN></LI><LI><SPAN>In the ABAP behaviour pool, the RAP saver method adjust_numbers must be implemented to assign a final primary key value for each entity instance.</SPAN></LI><LI><SPAN>The key value for an instance is assigned just before the instance is saved on the database. Thus, a gapless assignment of unique keys is ensured.</SPAN></LI><LI><SPAN>Late numbering is available for managed and unmanaged implementation scenarios with and without draft.</SPAN></LI></UL><P class="lia-indent-padding-left-30px" style="padding-left : 30px;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_5-1744621645488.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250316iEE64CE7F7474D794/image-size/medium?v=v2&amp;px=400" role="button" title="Arunagirish1_5-1744621645488.png" alt="Arunagirish1_5-1744621645488.png" /></span></P><H1 id="toc-hId-383863218"><FONT size="5">Note:</FONT></H1><UL><LI><SPAN>late numbering can't be external, since a consumer can't influence the SAVE sequence after the point of no return.</SPAN></LI><LI><SPAN>If draft enabled, it is mandatory that the RAP draft table has an additional key field DRAFTUUID of data type raw (16).</SPAN></LI></UL><H1 id="toc-hId-187349713">&nbsp;</H1><H1 id="toc-hId--9163792"><FONT size="5">Summary:</FONT></H1><UL><LI><SPAN>passed externally by the application user: External numbering</SPAN></LI><LI><SPAN>set internally by the framework: Managed internal</SPAN></LI><LI><SPAN>set internally by developer (implementation the FOR-NUMBERING method): Unmanaged Internal</SPAN></LI></UL><P class="lia-indent-padding-left-30px" style="padding-left : 30px;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Arunagirish1_6-1744621645489.jpeg" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250317iCA8AF127EADEA070/image-size/medium?v=v2&amp;px=400" role="button" title="Arunagirish1_6-1744621645489.jpeg" alt="Arunagirish1_6-1744621645489.jpeg" /></span></P><P>&nbsp;</P> 2025-04-21T09:38:17.384000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/augmentation-operation-in-rap/ba-p/14082109 Augmentation Operation in RAP 2025-04-21T10:41:27.204000+02:00 pavankumar_reddy90 https://community.sap.com/t5/user/viewprofilepage/user-id/189954 <P><STRONG>Ever wondered how to set default values for the object page fields during Create Operation in Fiori using RAP at the projection level?</STRONG></P><P>With an augmentation implementation you can add data or modify incoming requests on the projection layer before data reaches the transactional buffer. You can add data to a modify request or issue additional requests by augmenting the operation before the request is passed to the base business object.</P><DIV class=""><H3 id="toc-hId-1838257586">Use case</H3></DIV><P>• Defaulting for incoming requests.&nbsp;For example: Populate default values for fields of object page on create operation in Fiori</P><P>• Behavior-enabling denormalized fields, for example enabling editing of language dependent fields.<BR /><BR /></P><DIV class=""><H4 id="toc-hId-1770826800">Definition</H4></DIV><P>Augmentation is defined in the <STRONG>projection behavior definition</STRONG> on the relevant operation with the following syntax:&nbsp;</P><pre class="lia-code-sample language-abap"><code>define behavior for Entity [alias AliasedName] .. { use create (augment); use update (augment); use association AssocName { create (augment); } ... }</code></pre><P>Below steps will result you how to populate default values for the object page fields on create operation especially at project layer.&nbsp; That means this default values population is specific to projection not for entire BO.&nbsp;</P><OL><LI>In your projection behavior<SPAN>&nbsp;</SPAN><STRONG>ZC_TRAVEL_PROCESSOR_M_XX</STRONG><SPAN>&nbsp;</SPAN>definition, define augmentation for the create operation.</LI></OL><pre class="lia-code-sample language-abap"><code>use create (augment);</code></pre><P>2. Add behavior implementation class to the project behavior.</P><pre class="lia-code-sample language-abap"><code>… define behavior for ZC_TRAVEL_PROCESSOR_M_XX alias Travel implementation in class zcl_bp_c_travelm_xx ….​</code></pre><P>3. Click the activation button or use the shortcut<SPAN>&nbsp;</SPAN>Ctrl + F3<SPAN>&nbsp;</SPAN>to activate the behavior definition.</P><P>4. The ADT Quick Fix feature can be used to create implementation class for travel entity.&nbsp;</P><P><SPAN>For this, set the cursor on the class name, and press&nbsp;</SPAN>Ctrl+1<SPAN>&nbsp;to star the Quick Fix dialog.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1745219773658.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/253035iD1BC926F81F1BEDB/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1745219773658.png" alt="pavankumar_reddy90_0-1745219773658.png" /></span>&nbsp;<SPAN>Click on create behavior implementation to create the class.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_1-1745221092360.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/253049i59D70AFC3E7548AC/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_1-1745221092360.png" alt="pavankumar_reddy90_1-1745221092360.png" /></span></P><P>Click <STRONG>Next</STRONG> and <STRONG>Finish</STRONG> to create the class. In <STRONG>Local Types</STRONG> of Class you will see a augmentation operation method created where you need to implement logic to populate default values.</P><P>5.<SPAN>In the method implementation paste the below code.&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>METHOD augment_create. DATA: travel_create TYPE TABLE FOR CREATE zi_travel_m_xx. travel_create = CORRESPONDING #( entities ). LOOP AT travel_create ASSIGNING FIELD-SYMBOL(&lt;travel&gt;). &lt;travel&gt;-agency_id = '070012'. &lt;travel&gt;-status = 'O'. &lt;travel&gt;-%control-agency_id = if_abap_behv=&gt;mk-on. &lt;travel&gt;-%control-status = if_abap_behv=&gt;mk-on. ENDLOOP. MODIFY AUGMENTING ENTITIES OF zi_travel_m_xx ENTITY Travel CREATE FROM travel_create. ENDMETHOD.</code></pre><P>"<STRONG>MODIFY AUGMENTING ENTITIES</STRONG>"&nbsp; is a EML statement with which you can modify entities of base business object. In the above example "<STRONG>ZI_TRAVEL_M_XX</STRONG>" is the base business object and "<STRONG>ZC_TRAVEL_PROCESSOR_M_XX"&nbsp;</STRONG>is the project business object.</P><P>6. Save and Activate the Class.</P><P>7.Test the app now using preview tool. On Click of<SPAN>&nbsp;</SPAN>Create<SPAN>&nbsp;</SPAN><STRONG>Agency ID</STRONG> and <STRONG>Status</STRONG> fields are defaulted.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_2-1745221376616.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/253051i4C4C3C28E055C009/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_2-1745221376616.png" alt="pavankumar_reddy90_2-1745221376616.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_3-1745221416057.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/253052i07ECCB6CBE5E1CB0/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_3-1745221416057.png" alt="pavankumar_reddy90_3-1745221416057.png" /></span></P><P><SPAN>Same behavior can be achieved with determination with draft as well. But what is the difference ?</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1745223890753.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/253066i47B3E2DEEDEF7C47/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1745223890753.png" alt="pavankumar_reddy90_0-1745223890753.png" /></span></P><P><STRONG>Note:</STRONG> Only when draft is enabled in BO above feature can be achieved.&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2025-04-21T10:41:27.204000+02:00 https://community.sap.com/t5/application-development-and-automation-blog-posts/function-module-in-rap/ba-p/14073132 Function module in rap 2025-04-22T10:50:11.208000+02:00 Dadapeer https://community.sap.com/t5/user/viewprofilepage/user-id/1451098 <P><SPAN class=""><STRONG>Introduction&nbsp;</STRONG></SPAN></P><P><SPAN class="">This blog provides a step-by-step guide on creating a Product Management App&nbsp; using RAP in an unmanaged scenario. It explains how to create a function group and a function module using ABAP in Eclipse and integrate them into the RAP framework to perform CRUD (Create, Read, Update, Delete) operations on product data. The app will allow users to manage product details, including fields such as Product ID, Product Name, Quantity, and Unit Price.</SPAN></P><P><SPAN class=""><STRONG><SPAN>Procedure:&nbsp;</SPAN></STRONG></SPAN></P><P><SPAN class=""><STRONG><U>Created one database table 'ZDP_DT_UTCL_PDT</U></STRONG></SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'utcl products' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zdp_dt_utcl_pdt { key product_id : zdp_de_pdt_id not null; product_name : zdp_de_product_name; product_qty : zdp_de_pdt_quantity; product_unit_price : zdp_de_unit_price_pdt; } </code></pre><P><U><SPAN class="">Now we need to create interface view on top of DB table.</SPAN></U></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory:[#NONE] @AccessControl.authorizationCheck:#NOT_REQUIRED @EndUserText.label:'utcl prdt interface view' @Metadata.ignorePropagatedAnnotations:true @ObjectModel.usageType:{ serviceQuality:#X, sizeCategory:#S, dataClass:#MIXED } definerootviewentityzdp_I_cds_utcl_prdt asselectfromZDP_DT_UTCL_PDT { keyproduct_idasProductId, product_nameasProductName, product_qtyasProductQty, product_unit_priceasProductUnitPrice } </code></pre><P><U><SPAN class="">Now we need to create a projection view on top of Interface view.&nbsp;</SPAN></U></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'projection view on utcl prdt' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true define root view entity zdp_P_cds_utcl_prdt provider contract transactional_query as projection on zdp_I_cds_utcl_prdt { key ProductId, ProductName, ProductQty, ProductUnitPrice } </code></pre><P><STRONG><SPAN class="">Now we need to create the behavior definition on top of interface view</SPAN><SPAN class="">.</SPAN></STRONG></P><P><SPAN class=""><SPAN class=""><SPAN class="">I selected the </SPAN></SPAN><SPAN class=""><SPAN class="">unmanaged scenario</SPAN></SPAN><SPAN class=""><SPAN class=""> in the behavior definition because, in my case, the CRUD operations are performed using a </SPAN></SPAN><SPAN class=""><SPAN class="">function module</SPAN></SPAN><SPAN class=""><SPAN class="">. If I had chosen the </SPAN></SPAN><SPAN class=""><SPAN class="">managed scenario</SPAN></SPAN><SPAN class=""><SPAN class="">, the RAP framework would have handled the CRUD operations automatically, but since my logic is custom and implemented through a function module,</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></P><P><U><STRONG>ABAP in Eclipse Create Function Group and Function Module.</STRONG></U></P><P><SPAN>Type func in the search box and select ABAP Function Group from the suggestions. Click Next.&nbsp;Give the package name, function group name and description.</SPAN></P><P><SPAN>&nbsp;type function and select Abap function module.&nbsp;Click Next. Provide Name, description and function group name.&nbsp;We get an empty function module.</SPAN></P><P><SPAN>In this function module, we need to code the import, exporting, changing or exceptions parameters as required.</SPAN></P><P><SPAN><STRONG><U>&nbsp;Code of Behavior Definition of Interface:&nbsp;</U></STRONG></SPAN></P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_dp_i_cds_utcl_prdt unique; strict ( 2 ); define behavior for zdp_I_cds_utcl_prdt alias utcl_prdt //late numbering lock master authorization master ( instance ) //etag master &lt;field_name&gt; { create; update; delete; field ( readonly ) ProductId; mapping for Zdp_dt_utcl_pdt control zdp_s_prdt_c { ProductId = product_id; ProductName = product_name; ProductQty = product_qty; ProductUnitPrice = product_unit_price; } } </code></pre><P><STRONG><I><SPAN>Now create a behavior definition on top of consumption view:</SPAN></I></STRONG><STRONG><SPAN>&nbsp;</SPAN></STRONG> <SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Code of Behavior Definition of Projection:</SPAN></STRONG><SPAN>&nbsp;</SPAN> <SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); define behavior for zdp_P_cds_utcl_prdt alias utcl_prdt { use create; use update; use delete; } </code></pre><P>&nbsp;<STRONG><SPAN><U><SPAN class="">Behavior implementation.</SPAN></U></SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>CLASS lhc_zdp_I_cds_utcl_prdt DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zdp_I_cds_utcl_prdt RESULT result. METHODS create FOR MODIFY IMPORTING entities FOR CREATE utcl_prdt. METHODS update FOR MODIFY IMPORTING entities FOR UPDATE zdp_I_cds_utcl_prdt. METHODS delete FOR MODIFY IMPORTING keys FOR DELETE zdp_I_cds_utcl_prdt. METHODS read FOR READ IMPORTING keys FOR READ zdp_I_cds_utcl_prdt RESULT result. METHODS lock FOR LOCK IMPORTING keys FOR LOCK zdp_I_cds_utcl_prdt. TYPES: tt_failed TYPE TABLE FOR FAILED EARLY zdp_I_cds_utcl_prdt\\utcl_prdt, tt_reported TYPE TABLE FOR REPORTED EARLY zdp_I_cds_utcl_prdt\\utcl_prdt. TYPES tt_prdt_failed TYPE TABLE FOR FAILED zdp_I_cds_utcl_prdt. TYPES tt_prdt_reported TYPE TABLE FOR REPORTED zdp_I_cds_utcl_prdt. TYPES : tt_read TYPE TABLE FOR READ RESULT zdp_I_cds_utcl_prdt\\utcl_prdt. DATA : gs_prdt TYPE zdp_dt_utcl_pdt, gt_prdt TYPE TABLE OF zdp_dt_utcl_pdt. CLASS-DATA : gt_tab TYPE zdp_dt_utcl_pdt. METHODS map_messages IMPORTING cid TYPE abp_behv_cid OPTIONAL product_id TYPE zdp_de_pdt_id OPTIONAL messages TYPE char5 EXPORTING failed_added TYPE abap_boolean CHANGING failed TYPE tt_failed reported TYPE tt_reported. ENDCLASS. CLASS lhc_zdp_I_cds_utcl_prdt IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD create. DATA lv_msg TYPE char5. DATA : ls_tab1 TYPE zdp_dt_utcl_pdt. LOOP AT entities INTO DATA(ls_e). ls_tab1 = CORRESPONDING #( ls_e MAPPING FROM ENTITY USING CONTROL ). * LS_TAB1 = CORRESPONDING #( LT_PRODUCT[ 1 ] ). CALL FUNCTION 'ZDP_FM_PRDT_CREATE' EXPORTING ls_tab = ls_tab1 IMPORTING msg = lv_msg. me-&gt;map_messages( EXPORTING cid = ls_e-%cid product_id = ls_tab1-product_id messages = lv_msg * IMPORTING * failed_added = CHANGING failed = failed-utcl_prdt reported = reported-utcl_prdt ). ENDLOOP. ENDMETHOD. METHOD update. DATA : ls_update TYPE zdp_dt_utcl_pdt, ls_c TYPE zdp_s_prdt_c, lv_msg TYPE char20. LOOP AT entities ASSIGNING FIELD-SYMBOL(&lt;fs_u&gt;). ls_update = CORRESPONDING #( &lt;fs_u&gt; MAPPING FROM ENTITY ). ls_c = CORRESPONDING #( &lt;fs_u&gt; MAPPING FROM ENTITY ). CALL FUNCTION 'ZDP_FM_PRDT_UPDATE' EXPORTING ls_prd = ls_update ls_ctr = ls_c IMPORTING ls_prd_n = gs_prdt msg = lv_msg. * ENDLOOP. ENDMETHOD. METHOD delete. DATA(lv_id) = keys[ 1 ]-ProductId. CALL FUNCTION 'ZDP_FM_PRDT_DELETE' EXPORTING lv_prd_id = lv_id. ENDMETHOD. METHOD read. LOOP AT keys ASSIGNING FIELD-SYMBOL(&lt;fs_e&gt;) GROUP BY &lt;fs_e&gt;-%key. CALL FUNCTION 'ZDP_FM_PRDT_READ1' EXPORTING lv_pid = &lt;fs_e&gt;-ProductId IMPORTING lt_tab = gs_prdt. INSERT CORRESPONDING #( gs_prdt MAPPING TO ENTITY ) INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD lock. ENDMETHOD. METHOD map_messages. IF messages IS NOT INITIAL. reported = VALUE #( ( %cid = cid ProductId = product_id %msg = new_message( id = 'Zdp_message_class' number = '001' severity = if_abap_behv_message=&gt;severity-error v1 = product_id ) ) ). ENDIF. ENDMETHOD. ENDCLASS. CLASS lsc_ZDP_I_CDS_UTCL_PRDT 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_ZDP_I_CDS_UTCL_PRDT IMPLEMENTATION. METHOD finalize. ENDMETHOD. METHOD check_before_save. ENDMETHOD. METHOD save. CALL FUNCTION 'ZDP_FM_SAVE_1'. ENDMETHOD. METHOD cleanup. ENDMETHOD. METHOD cleanup_finalize. ENDMETHOD. ENDCLASS. </code></pre><P><STRONG>&nbsp;<SPAN class=""><SPAN class="">create operation.</SPAN></SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD create. DATA lv_msg TYPE char5. DATA : ls_tab1 TYPE zdp_dt_utcl_pdt. LOOP AT entities INTO DATA(ls_e). ls_tab1 = CORRESPONDING #( ls_e MAPPING FROM ENTITY USING CONTROL ). * LS_TAB1 = CORRESPONDING #( LT_PRODUCT[ 1 ] ). CALL FUNCTION 'ZDP_FM_PRDT_CREATE' EXPORTING ls_tab = ls_tab1 IMPORTING msg = lv_msg. me-&gt;map_messages( EXPORTING cid = ls_e-%cid product_id = ls_tab1-product_id messages = lv_msg CHANGING failed = failed-utcl_prdt reported = reported-utcl_prdt ). ENDLOOP. ENDMETHOD. </code></pre><P><STRONG><SPAN class=""><SPAN class="">Function module product create.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>FUNCTION zdp_fm_prdt_create IMPORTING ls_tab TYPE zdp_dt_utcl_pdt EXPORTING msg TYPE char5. DATA(ls_tab1) = lS_tab. SELECT MAX( product_id ) FROM zdp_dt_utcl_pdt INTO (lv_pid). IF sy-subrc EQ 0. lv_pid += 1. zcl_dp_utcl_prdt_um_helper=&gt;gt_tab = ls_tab1. ENDIF. ENDFUNCTION. </code></pre><P><SPAN class=""><SPAN class="">Here </SPAN><STRONG><SPAN class="">gt_tab</SPAN></STRONG><SPAN class=""> is </SPAN><SPAN class="">a global</SPAN><SPAN class=""> internal table which </SPAN><SPAN class="">I</SPAN><SPAN class=""> have </SPAN><SPAN class="">created</SPAN><SPAN class=""> in global class.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></P><P><STRONG><SPAN class=""><SPAN class=""><SPAN class="">Update operation logic.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD update. DATA : ls_update TYPE zdp_dt_utcl_pdt, ls_c TYPE zdp_s_prdt_c, lv_msg TYPE char20. LOOP AT entities ASSIGNING FIELD-SYMBOL(&lt;fs_u&gt;). ls_update = CORRESPONDING #( &lt;fs_u&gt; MAPPING FROM ENTITY ). ls_c = CORRESPONDING #( &lt;fs_u&gt; MAPPING FROM ENTITY ). CALL FUNCTION 'ZDP_FM_PRDT_UPDATE' EXPORTING ls_prd = ls_update ls_ctr = ls_c IMPORTING ls_prd_n = gs_prdt msg = lv_msg. ENDLOOP. ENDMETHOD. </code></pre><P><STRONG><SPAN class=""><SPAN class="">Update function module logic.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>FUNCTION zdp_fm_prdt_update IMPORTING ls_prd TYPE zdp_dt_utcl_pdt ls_ctr TYPE zdp_s_prdt_c EXPORTING ls_prd_n TYPE zdp_dt_utcl_pdt msg TYPE char20. SELECT SINGLE FROM zdp_dt_utcl_pdt FIELDS * WHERE product_id = _prd-product_id INTO (ls_p_old). ls_prd_n-product_id = ls_prd-product_id. ls_prd_n-product_name = COND #( WHEN ls_ctr-product_name IS NOT INITIAL THEN ls_prd-product_name ELSE ls_p_old-product_name ). ls_prd_n-product_qty = COND #( WHEN ls_ctr-product_qty IS NOT INITIAL THEN ls_prd-product_qty ELSE ls_p_old-product_qty ). ls_prd_n-product_unit_price = COND #( WHEN ls_ctr-product_unit_price IS NOT INITIAL THEN ls_prd-product_unit_price ELSE ls_p_old-product_unit_price ). IF ls_prd_n IS NOT INITIAL. zcl_dp_utcl_prdt_um_helper=&gt;gs_tab_u = ls_prd_n. msg = 'Sucessfully Updated'. ENDIF. ENDFUNCTION. </code></pre><P><STRONG>&nbsp;<SPAN class=""><SPAN class="">Delete operation logic.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD delete. DATA(lv_id) = keys[ 1 ]-ProductId. CALL FUNCTION 'ZDP_FM_PRDT_DELETE' EXPORTING lv_prd_id = lv_id. ENDMETHOD. </code></pre><P><STRONG>&nbsp;<SPAN class=""><SPAN class="">Delete function module logic.</SPAN></SPAN></STRONG><SPAN class="">&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>FUNCTION zdp_fm_prdt_delete IMPORTING lv_prd_id TYPE zdp_de_pdt_id. zcl_dp_utcl_prdt_um_helper=&gt;prt_id = lv_prd_id. ENDFUNCTION. </code></pre><P><STRONG><SPAN class=""><SPAN class="">Read operation logic.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD read. LOOP AT keys ASSIGNING FIELD-SYMBOL(&lt;fs_e&gt;) GROUP BY &lt;fs_e&gt;-%key. CALL FUNCTION 'ZDP_FM_PRDT_READ1' EXPORTING lv_pid = &lt;fs_e&gt;-ProductId IMPORTING lt_tab = gs_prdt. INSERT CORRESPONDING #( gs_prdt MAPPING TO ENTITY ) INTO TABLE result. ENDLOOP. ENDMETHOD. </code></pre><P>&nbsp;<STRONG><SPAN class=""><SPAN class="">Read function module logic.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>FUNCTION zdp_fm_prdt_read1 IMPORTING lv_pid TYPE zdp_de_product_id EXPORTING lt_tab TYPE zdp_dt_utcl_pdt. SELECT SINGLE FROM zdp_dt_utcl_pdt FIELDS product_id, product_name,product_qty, product_unit_price WHERE product_id = _pid INTO _tab. ENDFUNCTION. </code></pre><P>&nbsp;<STRONG><SPAN class=""><SPAN class="">save operation logic.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD save. CALL FUNCTION 'ZDP_FM_SAVE_1'. ENDMETHOD. </code></pre><P><STRONG><SPAN class=""><SPAN class="">Save function module logic.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN>&nbsp;</STRONG></P><pre class="lia-code-sample language-abap"><code>FUNCTION zdp_fm_save_1. IF zcl_dp_utcl_prdt_um_helper=&gt;gt_tab IS NOT INITIAL. INSERT zdp_dt_utcl_pdt FROM @zcl_dp_utcl_prdt_um_helper=&gt;gt_tab. ENDIF. if zcl_dp_utcl_prdt_um_helper=&gt;gs_tab_u is NOT INITIAL. UPDATE zdp_dt_utcl_pdt FROM @zcl_dp_utcl_prdt_um_helper=&gt;gs_tab_u. ENDIF. if zcl_dp_utcl_prdt_um_helper=&gt;prt_id is not initial. DELETE FROM zdp_dt_utcl_pdt WHERE product_id = @zcl_dp_utcl_prdt_um_helper=&gt;prt_id. ENDIF. ENDFUNCTION. </code></pre><P><SPAN class=""><SPAN class="">Output:</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></P><P>&nbsp;<SPAN>Go to front-end</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Dadapeer_0-1744356506451.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/249623i331198383493BECA/image-size/large?v=v2&amp;px=999" role="button" title="Dadapeer_0-1744356506451.png" alt="Dadapeer_0-1744356506451.png" /></span></P><P><SPAN>Here I will create one new record:&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Dadapeer_0-1744356709313.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/249625iAF854C80FA024896/image-size/large?v=v2&amp;px=999" role="button" title="Dadapeer_0-1744356709313.png" alt="Dadapeer_0-1744356709313.png" /></span></P><P><SPAN class=""><SPAN class="">Delete product</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Dadapeer_0-1744356869698.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/249626i34E9337C9A83DDBA/image-size/large?v=v2&amp;px=999" role="button" title="Dadapeer_0-1744356869698.png" alt="Dadapeer_0-1744356869698.png" /></span></P><P><SPAN class="">Here we are </SPAN><SPAN class="">delete</SPAN><SPAN class=""> super cement</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Dadapeer_0-1744356991909.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/249627i087D280919932C6A/image-size/large?v=v2&amp;px=999" role="button" title="Dadapeer_0-1744356991909.png" alt="Dadapeer_0-1744356991909.png" /></span></P><P><SPAN class="">Update gold cement product </SPAN><SPAN class="">quantity.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Dadapeer_1-1744357034108.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/249628i9F3605336427E0A1/image-size/large?v=v2&amp;px=999" role="button" title="Dadapeer_1-1744357034108.png" alt="Dadapeer_1-1744357034108.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2025-04-22T10:50:11.208000+02:00 https://community.sap.com/t5/application-development-and-automation-blog-posts/value-help-with-additional-binding-in-rap/ba-p/14069344 Value Help with Additional Binding in RAP 2025-04-22T10:50:52.084000+02:00 Vivek_Sahu_21 https://community.sap.com/t5/user/viewprofilepage/user-id/1451075 <P><STRONG><SPAN><SPAN class="">Value Help with Additional Binding in RAP – Filtering Storage Locations by Plant in Material Stock Management</SPAN><SPAN class="">&nbsp;</SPAN></SPAN></STRONG></P><P><FONT size="6"><STRONG><SPAN>Introduction:</SPAN></STRONG></FONT></P><P><SPAN>In this blog, I will demonstrate how to implement </SPAN><STRONG><SPAN>Value Help with Additional Binding</SPAN></STRONG><SPAN> in a </SPAN><STRONG><SPAN>RAP (RESTful ABAP Programming)</SPAN></STRONG><SPAN> application for the </SPAN><STRONG><SPAN>MM module</SPAN></STRONG><SPAN>. The scenario involves managing </SPAN><STRONG><SPAN>Material Stock Information</SPAN></STRONG><SPAN> by providing value help for </SPAN><STRONG><SPAN>Plant (WERKS)</SPAN></STRONG><SPAN> and </SPAN><STRONG><SPAN>Storage Location (LGORT)</SPAN></STRONG><SPAN>. The key feature is filtering the storage location list dynamically based on the selected plant, enhancing the user experience.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>When the user selects a </SPAN><STRONG><SPAN>Plant</SPAN></STRONG><SPAN>, the system automatically filters the </SPAN><STRONG><SPAN>Storage Location (LGORT)</SPAN></STRONG><SPAN> list to show only the </SPAN><STRONG><SPAN>storage locations related to the selected plant</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>The requirement is to build a </SPAN><STRONG><SPAN>Fiori-based RAP application</SPAN></STRONG><SPAN> that enables users to:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Select a </SPAN><STRONG><SPAN>Plant</SPAN></STRONG><SPAN> from the dropdown.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Dynamically filter the </SPAN><STRONG><SPAN>Storage Locations</SPAN></STRONG><SPAN> based on the selected Plant.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Display and manage </SPAN><STRONG><SPAN>material stock details</SPAN></STRONG><SPAN> such as material number, plant, storage location, stock quantity, and unit of measure.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><FONT size="6"><SPAN>&nbsp;<STRONG>Procedure:</STRONG>&nbsp;</SPAN></FONT></P><P><STRONG><SPAN>Key Tables Involved</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><FONT color="#808080"><U><STRONG><EM>1. I have created three custom tables, where Storage and plant table have records:&nbsp;</EM></STRONG></U></FONT></P><UL><LI><STRONG><SPAN>zvs_dt_storage_l</SPAN></STRONG><SPAN> → Storage Locations</SPAN><SPAN>&nbsp;table with data</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Table for storage location' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zvs_dt_storage_l { key client : abap.clnt not null; key lgort : lgort_d not null; key lgobe : abap.char(16) not null; werks : werks_d; }</code></pre><P>Table Data:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_0-1743766832846.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/246814i13C997E5EBDF3A97/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_0-1743766832846.png" alt="Vivek_Sahu_21_0-1743766832846.png" /></span></P><UL><LI><STRONG><SPAN>zvs_dt_plant_dt</SPAN></STRONG><SPAN> → Plant Data</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Table for plant data' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zvs_dt_plant_dt { key client : abap.clnt not null; key werks : werks_d not null; name1 : abap.char(30); }</code></pre><P>Table Data:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_1-1743767225404.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/246817i3900B93B8E8CE754/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_1-1743767225404.png" alt="Vivek_Sahu_21_1-1743767225404.png" /></span></P><UL><LI><STRONG><SPAN>zvs_dt_mat_stock</SPAN></STRONG><STRONG><SPAN> → </SPAN></STRONG><SPAN>Material Stock Data</SPAN><SPAN>&nbsp;</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'table to store the material stock data.' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zvs_dt_mat_stock { key client : abap.clnt not null; key matnr : matnr not null; werks : werks_d; lgort : lgort_d; @Semantics.quantity.unitOfMeasure : 'zvs_dt_mat_stock.meins' labst : abap.quan(13,3); meins : meins; }</code></pre><P>&nbsp;</P><P><U><FONT color="#808080"><STRONG><EM>2.1&nbsp;<SPAN class="">Now create one interface<SPAN>&nbsp;</SPAN>view<SPAN>&nbsp;</SPAN>‘ZVS_I_PLANT_DT’<SPAN>&nbsp;</SPAN>on top of DB table 'ZVS_DT_PLANT_DT': -</SPAN><SPAN class="">&nbsp;</SPAN></EM></STRONG></FONT></U></P><P><SPAN>This view provides the </SPAN><STRONG><SPAN>value help for Plant (WERKS)</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>I have used the annotation </SPAN><SPAN>"@ObjectModel.resultSet.sizeCategory: #XS</SPAN><SPAN>”</SPAN><SPAN> which makes the value help appear as a dropdown.</SPAN><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.sqlViewName: 'ZVS_PLANT_DT' @AbapCatalog.compiler.compareFilter: true @AbapCatalog.preserveKey: true @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'interface view for plant data' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.resultSet.sizeCategory: #XS define view zvs_i_plant_dt as select from zvs_dt_plant_dt { key werks as Werks, name1 as Name1 }</code></pre><P><SPAN><U><FONT color="#808080"><STRONG><EM>2.2&nbsp;<SPAN class="">Now create interface view ‘ZVS</SPAN></EM></STRONG></FONT><FONT color="#808080"><STRONG><EM><SPAN class="">_I_STORAGE_LC</SPAN></EM></STRONG></FONT><FONT color="#808080"><STRONG><EM><SPAN class="">’ on top of DB table 'ZVS_DT_STORAGE_L': -</SPAN></EM></STRONG></FONT></U>&nbsp;</SPAN></P><P><SPAN><SPAN class=""><SPAN class="">This view filters </SPAN></SPAN><SPAN class=""><SPAN class="">Storage Locations</SPAN></SPAN><SPAN class=""><SPAN class=""> by </SPAN></SPAN><SPAN class=""><SPAN class="">Plant</SPAN></SPAN><SPAN class=""><SPAN class=""> using </SPAN></SPAN><SPAN class=""><SPAN class="">additional</SPAN><SPAN class=""> binding</SPAN></SPAN><SPAN class=""><SPAN class="">.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></P><UL><LI><SPAN>This view selects </SPAN><STRONG><SPAN>Storage Location</SPAN></STRONG><SPAN>, </SPAN><STRONG><SPAN>Description</SPAN></STRONG><SPAN>, and </SPAN><STRONG><SPAN>Plant</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>The </SPAN><STRONG><SPAN>Plant field</SPAN></STRONG><SPAN> acts as the filter when the user selects a Plant.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>@AbapCatalog.sqlViewName: 'ZVS_STORAGE_LC' @AbapCatalog.compiler.compareFilter: true @AbapCatalog.preserveKey: true @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'interface view for storage location' @Metadata.ignorePropagatedAnnotations: true define view zvs_i_storage_lc as select from zvs_dt_storage_l { key lgort as StorageLocation, lgobe as StorageLocDesc, werks as Plant }</code></pre><P><SPAN><U><FONT color="#808080"><STRONG><EM>2.3&nbsp;<SPAN class="">Now create interface view ‘ZVS</SPAN></EM></STRONG></FONT><FONT color="#808080"><STRONG><EM><SPAN class="">_I_MAT_STOCK</SPAN></EM></STRONG></FONT><FONT color="#808080"><STRONG><EM><SPAN class="">’ on top of DB table 'ZVS_DT_MAT_STOCK': -</SPAN></EM></STRONG></FONT></U>&nbsp;</SPAN></P><P><SPAN class=""><SPAN class="">An </SPAN></SPAN><SPAN class=""><SPAN class="">Interface View</SPAN></SPAN><SPAN class=""><SPAN class=""> (</SPAN></SPAN><SPAN class=""><SPAN class="">zvs_i_mat_stock</SPAN></SPAN><SPAN class=""><SPAN class="">) for fetching material stock data with associations to plant and storage location details.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></P><UL><LI><SPAN>This view maps to the custom table </SPAN><SPAN>zvs_dt_mat_stock</SPAN><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>It includes fields for </SPAN><STRONG><SPAN>Material Number, Plant, Storage Location, Stock Quantity,</SPAN></STRONG><SPAN> and </SPAN><STRONG><SPAN>Unit of Measure</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI></UL><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'interface view for material stock' @Metadata.ignorePropagatedAnnotations: true define root view entity zvs_i_mat_stock as select from zvs_dt_mat_stock association[0..*] to zvs_i_plant_dt as _plant on $projection.Werks = _plant.Werks association[0..*] to zvs_i_storage_lc as _storage on $projection.Lgort = _storage.StorageLocation { key matnr as Matnr, werks as Werks, lgort as Lgort, @Semantics.quantity.unitOfMeasure: 'meins' labst as Labst, meins as Meins, _plant, _storage.StorageLocDesc // Expose description from Storage association }</code></pre><P><SPAN class=""><SPAN class=""><SPAN><U><FONT color="#808080"><STRONG><EM>3.&nbsp;<SPAN class="">Now create one consumption view ‘ZVS</SPAN></EM></STRONG></FONT><FONT color="#808080"><STRONG><EM><SPAN class="">_C_MAT_STOCK</SPAN></EM></STRONG></FONT><FONT color="#808080"><STRONG><EM><SPAN class="">’ on top of Interface view 'ZVS_I_MAT_STOCK': -</SPAN></EM></STRONG></FONT></U>&nbsp;</SPAN></SPAN></SPAN></P><P><SPAN class=""><SPAN class="">The consumption view </SPAN></SPAN><SPAN class=""><SPAN class="">zvs_c_mat_stock</SPAN></SPAN><SPAN class=""><SPAN class=""> projects the data from the interface view </SPAN></SPAN><SPAN class=""><SPAN class="">zvs_</SPAN><SPAN class="">i</SPAN><SPAN class="">_mat_stock</SPAN></SPAN><SPAN class=""><SPAN class="">. This view is intended to expose the data for transactional operations, such as create, update, and </SPAN><SPAN class="">delete</SPAN><SPAN class="">. The consumption view allows clients to interact with the data, but the actual persistence and behavior are managed through the behavior definition</SPAN><SPAN class="">.&nbsp;</SPAN></SPAN></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'consumption view for material stock' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define root view entity zvs_c_mat_stock provider contract transactional_query as projection on zvs_i_mat_stock { key Matnr, Werks, Lgort, @Semantics.quantity.unitOfMeasure: 'meins' Labst, Meins, /* Associations */ _plant, StorageLocDesc }</code></pre><P><SPAN class="">&nbsp;<U><EM><FONT size="4" color="#808080"><STRONG>4.&nbsp;<SPAN class=""><SPAN class="">Define Metadata Extension</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></FONT></EM></U></SPAN></P><P><SPAN class=""><SPAN class=""><SPAN class="">Add </SPAN></SPAN><SPAN class=""><SPAN class="">Value Help with Additional Binding</SPAN></SPAN><SPAN class=""><SPAN class=""> to your RAP Business Object.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_1-1744106066804.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248025iE8A0E5D7570987F5/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_1-1744106066804.png" alt="Vivek_Sahu_21_1-1744106066804.png" /></span></P><P><EM><SPAN class=""><SPAN class="">Here</SPAN><SPAN class="">:</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></EM></P><UL><LI><SPAN>This “</SPAN><EM>@Consumption.valueHelpDefinition: [{ entity : { element: 'Werks' , name: 'zvs_i_plant_dt' } }]</EM><SPAN>” Annotation to get the dropdown for WERKS(Plant) Field.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>This “</SPAN><EM>@Consumption.valueHelpDefinition: [{ entity : { element: 'StorageLocation' , name: 'zvs_i_storage_lc' }, additionalBinding: [{ element: 'Plant' ,localElement: 'Werks' }]&nbsp;}]</EM><SPAN>” Annotation here, for additional binding. So based on selected plant it will dynamically filter the storage location.&nbsp;</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN><SPAN class=""><SPAN class="">The </SPAN></SPAN><EM><SPAN class=""><SPAN class="">@Consumption.valueHelpDefinition</SPAN></SPAN></EM><SPAN class=""><SPAN class=""> annotation is used to define </SPAN></SPAN><SPAN class=""><SPAN class="">value help</SPAN></SPAN><SPAN class=""><SPAN class=""> (</SPAN><SPAN class="">similar to</SPAN><SPAN class=""> F4 help in SAP GUI) for a field. It allows you to provide dropdown-style suggestions or search help for users when they enter data into specific fields.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></P><UL><LI><SPAN><SPAN><SPAN class=""><SPAN class=""><SPAN class="">entity</SPAN></SPAN><SPAN class=""><SPAN class="">:</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></SPAN></SPAN><OL class="lia-list-style-type-lower-alpha"><LI><SPAN>Specifies the </SPAN><STRONG><SPAN>data source</SPAN></STRONG><SPAN> for the value help.</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>element:</SPAN></STRONG><SPAN> 'StorageLocation'</SPAN><SPAN>: The field in the current CDS view where the value help is applied.&nbsp;</SPAN></LI><LI><STRONG><SPAN>name:</SPAN></STRONG><SPAN> 'zvs_i_storage_lc'</SPAN><SPAN>: The </SPAN><STRONG><SPAN>CDS view</SPAN></STRONG><SPAN> providing the value help data.</SPAN><SPAN>&nbsp;</SPAN></LI></OL></LI></UL><UL><LI><SPAN><SPAN><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class="">additionalBinding:&nbsp;</SPAN></SPAN></SPAN></SPAN></SPAN></SPAN><OL class="lia-list-style-type-lower-alpha"><LI><SPAN>Defines additional filtering criteria.</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>element:</SPAN></STRONG><SPAN> 'Plant'</SPAN><SPAN>: The field in the </SPAN><STRONG><SPAN>value help entity</SPAN></STRONG><SPAN> used for filtering.</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>localElement:</SPAN></STRONG><SPAN> 'Werks'</SPAN><SPAN>: The field in the </SPAN><STRONG><SPAN>current view</SPAN></STRONG><SPAN> that is used as the filtering key.</SPAN><SPAN>&nbsp;</SPAN></LI></OL><SPAN><SPAN class=""><SPAN class=""><SPAN class="">&nbsp;</SPAN></SPAN></SPAN></SPAN></LI></UL><P><FONT size="3"><EM><SPAN class=""><SPAN class=""><SPAN class="">Metadata Extension Code for your reference:</SPAN></SPAN></SPAN></EM></FONT></P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity zvs_c_mat_stock with { .facet: [{ id: 'MaterialStock', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Material ID', position: 10 }] : { lineItem: [{ position: 10, label: 'Material no.' }], identification: [{ position: 10, label: 'Material no.' }] } Matnr; : { lineItem: [{ position: 20, label: 'Plant' }], identification: [{ position: 20, label: 'Plant' }] } @Consumption.valueHelpDefinition: [{ entity : { element: 'Werks' , name: 'zvs_i_plant_dt' } }] Werks; : { lineItem: [{ position: 30, label: 'Storage location' }], identification: [{ position: 30, label: 'Storage location' }] } @Consumption.valueHelpDefinition: [{ entity : { element: 'StorageLocation' , name: 'zvs_i_storage_lc' } ,additionalBinding: [{ element: 'Plant' ,localElement: 'Werks' }] }] Lgort; : { lineItem: [{ position: 40, label: 'Stock quantity' }], identification: [{ position: 40, label: 'Stock quantity' }] } Labst; : { lineItem: [{ position: 60, label: 'Storage location Description' }], identification: [{ position: 60, label: 'Storage location Description' }] } StorageLocDesc; }</code></pre><P><U><EM><FONT size="4"><STRONG><FONT color="#808080"><SPAN class=""><SPAN class="">5. Define Behavior Definition</SPAN></SPAN></FONT></STRONG></FONT></EM></U></P><UL><LI><SPAN>The </SPAN><STRONG><SPAN>labst</SPAN></STRONG><SPAN> (Stock Quantity) and </SPAN><STRONG><SPAN>meins</SPAN></STRONG><SPAN> (Unit of Measure) fields are </SPAN><STRONG><SPAN>read-only</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>The behavior definition supports </SPAN><STRONG><SPAN>CRUD operations</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><U><STRONG><FONT color="#808080"><I>Now create a behavior definition on top of interface view 'ZVS_I_MAT_STOCK':</I></FONT></STRONG>&nbsp;</U></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_vs_i_mat_stock unique; strict ( 2 ); define behavior for zvs_i_mat_stock //alias &lt;alias_name&gt; persistent table zvs_dt_mat_stock lock master authorization master ( instance ) //etag master &lt;field_name&gt; early numbering { create; update; delete; field ( readonly ) Matnr, StorageLocDesc; mapping for zvs_dt_mat_stock{ Matnr = matnr; Werks = werks; Lgort = lgort; Labst = labst; Meins = meins; } }</code></pre><P><STRONG><FONT color="#808080"><U><I>Now create a behavior definition on top of consumption view 'ZVS_C_MAT_STOCK':</I></U>&nbsp;</FONT></STRONG></P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); define behavior for zvs_c_mat_stock //alias &lt;alias_name&gt; { use create; use update; use delete; }</code></pre><P><SPAN>Go to front-end:</SPAN></P><P><SPAN>Here I will create one new record:</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_0-1744109346370.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248049iF9D66D66026E1A58/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_0-1744109346370.png" alt="Vivek_Sahu_21_0-1744109346370.png" /></span></P><P><SPAN>You can see here, all the storage location are coming if we didn’t provide plant:</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_1-1744109346370.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248051i961DE5A5C0872B19/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_1-1744109346370.png" alt="Vivek_Sahu_21_1-1744109346370.png" /></span></P><P><SPAN>So we'll select plant </SPAN><STRONG><SPAN>1000</SPAN></STRONG><SPAN> from dropdown:</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_2-1744109346371.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248050iC2E30833BE26AF06/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_2-1744109346371.png" alt="Vivek_Sahu_21_2-1744109346371.png" /></span></P><P><SPAN>You can see here storage location got filtered based on the selected plant:</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_3-1744109346372.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248053iF34BA87DDDECF399/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_3-1744109346372.png" alt="Vivek_Sahu_21_3-1744109346372.png" /></span></P><P><SPAN>If I select plant 1400, so based on that we are getting 0006 storage location:</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_4-1744109346372.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248054i209E16CAA0080CD6/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_4-1744109346372.png" alt="Vivek_Sahu_21_4-1744109346372.png" /></span></P><P><SPAN>After saving record you can see the result:</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_5-1744109346373.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248052iE798D327BE125C47/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_5-1744109346373.png" alt="Vivek_Sahu_21_5-1744109346373.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Vivek_Sahu_21_6-1744109346373.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/248056i671AEADC7BB0F251/image-size/large?v=v2&amp;px=999" role="button" title="Vivek_Sahu_21_6-1744109346373.png" alt="Vivek_Sahu_21_6-1744109346373.png" /></span></P><P><FONT size="6"><STRONG><SPAN class=""><SPAN class="">Conclusion:</SPAN></SPAN></STRONG></FONT></P><P>This blog showed how to create a <STRONG>RAP-based Fiori app</STRONG> with value help that <STRONG>filters</STRONG> <STRONG>storage locations</STRONG> <STRONG>based on the selected</STRONG> <STRONG>plant</STRONG>. Using CDS views, associations, and annotations, the app becomes user-friendly, efficient, and easy to maintain.</P><P><SPAN>&nbsp;</SPAN></P> 2025-04-22T10:50:52.084000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/adobe-form-smartform-with-rap-based-odata-service-without-segw/ba-p/14080834 Adobe Form/Smartform with RAP-Based OData Service Without SEGW 2025-04-22T18:43:19.937000+02:00 UsamaKhan https://community.sap.com/t5/user/viewprofilepage/user-id/1479051 <P>Hello, everyone!</P><P>This is my first blog, and I’m excited to share my experience while working with RAP-based OData services. As a learner diving into the RAP world, I struggled to find a method to directly display forms in RAP applications .During my exploration, I discovered a workaround to render Adobe forms using a RAP-based OData service—without relying on SEGW-generated services.</P><P>Here’s the code that I used:</P><pre class="lia-code-sample language-abap"><code> " Create object reference for HTTP response DATA: lo_cached_response TYPE REF TO if_http_response. " Set the output parameters for the Adobe form ls_outputparams = VALUE #( getpdf = 'X' nodialog = 'X' ). CALL FUNCTION 'FP_JOB_OPEN' CHANGING ie_outputparams = ls_outputparams. " Get the name of the generated function module for the Adobe form CALL FUNCTION 'FP_FUNCTION_MODULE_NAME' EXPORTING i_name = 'YUS_DEMO' "Your Adobe Form IMPORTING e_funcname = lv_fmname. IF sy-subrc = 0. " Call the generated function module to render the Adobe form CALL FUNCTION lv_fmname EXPORTING /1bcdwb/docparams = ls_docparams t_bar = lt_adobe_bar i_preview = abap_true IMPORTING /1bcdwb/formoutput = ls_form_output EXCEPTIONS usage_error = 1 system_error = 2 internal_error = 3. " Close the spool job for the Adobe form CALL FUNCTION 'FP_JOB_CLOSE'. ENDIF. " Create an HTTP response object for hosting the PDF CREATE OBJECT lo_cached_response TYPE cl_http_response EXPORTING add_c_msg = 1. " Generate a unique GUID for the PDF file CALL FUNCTION 'GUID_CREATE' IMPORTING ev_guid_32 = lv_guid. " Set the PDF data in the HTTP response lo_cached_response-&gt;set_data( data = ls_form_output-pdf length = xstrlen( ls_form_output-pdf ) ). " Set the content type to PDF lv_app_type = 'application/pdf'. lo_cached_response-&gt;set_header_field( name = if_http_header_fields=&gt;content_type value = lv_app_type ). " Set the content disposition to inline with the generated filename lo_cached_response-&gt;set_header_field( name = if_http_header_fields=&gt;content_disposition value = |inline; filename="{ lv_guid }.pdf"| ). " Allow cross-origin requests lo_cached_response-&gt;set_header_field( name = 'Access-Control-Allow-Origin' value = '*' ). " Set the HTTP response status lo_cached_response-&gt;set_status( code = 200 reason = 'OK' ). " Set the cache timeout for the response lo_cached_response-&gt;server_cache_expire_rel( expires_rel = 120 ). " Construct the base URL for the OData service CALL METHOD cl_wd_utilities=&gt;construct_wd_url EXPORTING application_name = 'YUS_SR_DEMO_MAS_V2' "Your RAP Based Odata Service IMPORTING out_host = lv_host out_port = lv_port out_protocol = lv_protocol. " Build the full URL for accessing the Adobe PDF lv_link = |http://{ lv_host }:{ lv_port }|. lv_url = |{ lv_link }/sap/opu/odata/sap/YUS_SR_DEMO_MAS_V2/{ lv_guid }.PDF|. " Cache the constructed URL with the HTTP response object cl_http_server=&gt;server_cache_upload( url = lv_url scope = 1 response = lo_cached_response ). CONCATENATE '/opu/odata/sap/YUS_SR_DEMO_MAS_V2/' lv_guid '.' 'PDF' INTO e_url.</code></pre><P>&nbsp;</P><P><STRONG>How Does It Work?</STRONG> This method dynamically generates a PDF URL for the Adobe form and hosts it using an HTTP response object. The <STRONG>RAP-based OData service </STRONG>YUS_SR_DEMO_MAS_V2&nbsp;is used here. <EM>You can replace this service with any RAP-based OData service you are using in your application.</EM></P><P>&nbsp;</P><P><SPAN>Furthermore, once you have the URL of the PDF, you can integrate it into your UI5 application. Here's how you can do it:</SPAN></P><UL><LI><P><SPAN>Use this code in your custom entity cds, virtual element or RAP action to get the URL.</SPAN></P></LI><LI><P><SPAN>In your UI5 app, show the PDF in an iframe.</SPAN></P></LI></UL><P><SPAN>Here’s how you can configure your data source in the manifest file:</SPAN></P><pre class="lia-code-sample language-json"><code>"DEMO_DEV": { "uri": "sap", "type": "OData", "settings": { "annotations": [], "odataVersion": "2.0" } }</code></pre><P><EM>In the controller, resolve the destination URL and combine it with the PDF URL like this:</EM></P><pre class="lia-code-sample language-javascript"><code>var sDestination = that.getOwnerComponent().getManifestObject().resolveUri(that.getOwnerComponent().getManifestEntry("sap.app").dataSources.DEMO_DEV.uri); //DEMO_DEV is a datasource in manifest var sPdfUrl = sDestination + oData.preview.Url.toString(); //PDF Url</code></pre><P><SPAN>This approach allows you to seamlessly display the Adobe form PDF in your UI5 application using an iframe.</SPAN></P><P><SPAN><STRONG>We can do the same with Smartforms as well.</STRONG> This method can also be adapted to render Smartforms in a similar manner.</SPAN></P><P><SPAN><STRONG>Feedback and Best Practices</STRONG> While this method achieves the goal, I am unsure if it fully aligns with SAP’s best practices. If you have suggestions or alternative approaches for displaying forms directly in RAP applications, please share them. Your feedback would be immensely helpful!</SPAN></P><P><SPAN><STRONG>Conclusion</STRONG> In this approach, we are hosting the Adobe form on our RAP-based OData service. Additionally, this workaround makes it easy to integrate the PDF into your UI5/RAP application. The same method works for Smartforms as well, offering a consistent and effective solution for both technologies.</SPAN></P><P><SPAN>Thank you for reading my first blog!</SPAN></P><P>&nbsp;</P><P><SPAN><a href="https://community.sap.com/t5/c-khhcw49343/ABAP+RESTful+Application+Programming+Model/pd-p/7e44126e-7b27-471d-a379-df205a12b1ff" class="lia-product-mention" data-product="909-1">ABAP RESTful Application Programming Model</a>&nbsp;</SPAN></P><P><SPAN><a href="https://community.sap.com/t5/c-khhcw49343/SAP+Interactive+Forms+by+Adobe/pd-p/582573882271271216439685697820265" class="lia-product-mention" data-product="1065-1">SAP Interactive Forms by Adobe</a>&nbsp;&nbsp;</SPAN></P><P>&nbsp;</P><P>&nbsp;</P> 2025-04-22T18:43:19.937000+02:00 https://community.sap.com/t5/technology-blog-posts-by-sap/filtering-of-rap-business-events-in-sap-btp-abap-environment-and-in-sap/ba-p/14075529 Filtering of RAP Business Events in SAP BTP ABAP Environment and in SAP Advanced Event Mesh 2025-04-23T13:18:38.307000+02:00 SafaBahoosh https://community.sap.com/t5/user/viewprofilepage/user-id/763514 <P>In this blog post, I show you how you can do source side filtering of events in SAP BTP ABAP environment, what Dynamic Topics are and how we can use them for routing and filtering of events in SAP Integration Suite Advanced Event Mesh.<BR />Here, I represent two different ways of filtering of RAP business events in SAP BTP ABAP environment. On the left-hand side, you can see the SAP BTP ABAP environment system that produces some RAP business events. These events are then sent as CloudEvents to the right-hand side, to the Advanced Event Mesh instance.<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter1.jpg" style="width: 567px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250484iF3EFDE1B468425A6/image-dimensions/567x321?v=v2" width="567" height="321" role="button" title="filter1.jpg" alt="filter1.jpg" /></span></P><P>In the first part, we discuss<SPAN>&nbsp;</SPAN><SPAN><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/0f69f8fb28ac4bf48d2b57b9637e81fa/fa8bf5e202234a2cae60d5560f92111d.html?&amp;state=DRAFT" target="_blank" rel="noopener noreferrer"><STRONG>Source Side Filtering</STRONG></A></SPAN><SPAN>&nbsp;</SPAN>of events which is available from release 2402 in SAP S/4HANA Cloud and SAP BTP ABAP environment and in SAP S/4HANA and SAP S/4HANA &nbsp;Cloud Private Edition since release 2023 SP00. For a RAP business event, then you can define filterable properties from the actual payload of that event. This means, if there's a field called for example “country code”, you can make it filterable by exposing it as CloudEvents context attribute. Then you can define filter conditions as well, like the country code must match the amount<SPAN>&nbsp;</SPAN><EM>US</EM><SPAN>&nbsp;</SPAN>or<SPAN>&nbsp;</SPAN><EM>DE.<SPAN>&nbsp;</SPAN></EM>&nbsp;If this filter condition is not met, then the event is not sent out of the system. If you want to prevent some events to go out due to the data privacy protection issues or reduce traffic due to unnecessary events, then you can use event filtering.</P><P>&nbsp;In the second part, we discuss<SPAN>&nbsp;</SPAN><SPAN><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/0f69f8fb28ac4bf48d2b57b9637e81fa/bc8e4abf72494e4b9c5c27cd5a7fdf5b.html?&amp;state=DRAFT" target="_blank" rel="noopener noreferrer"><STRONG>Dynamic Topic Filtering</STRONG></A></SPAN><SPAN>&nbsp;</SPAN>and routing of events with the integration with the Advanced Event Mesh. As a further remark, this feature is available from release 2408 in SAP S/4HANA Cloud Public Edition and SAP BTP ABAP environment and<SPAN>&nbsp;</SPAN><SPAN>in&nbsp;</SPAN>SAP S/4HANA and<SPAN>&nbsp;</SPAN><SPAN>SAP S/4HANA Cloud Private Edition since release 2023 SP02.</SPAN></P><H2 id="toc-hId-1708344625"><STRONG>Source side filtering of RAP business events</STRONG></H2><P>Assume you are in your BTP ABAP environment system, and you have a RAP business event. This RAP business event has a corresponding abstract entity where you define your actual payload. Let's say you have a payload that contains a field “country code”:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter2.jpg" style="width: 383px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250485iF5782AE691AAD30D/image-dimensions/383x172?v=v2" width="383" height="172" role="button" title="filter2.jpg" alt="filter2.jpg" /></span></P><P>This is a payload of a RAP business event without predefined filters. In the data part, the country code has the value DE and in the upper part you can see the header fields of the CloudEvent.<BR />You want to know how you can make this property filterable? quite easy! As you define the event, you need to add the annotation “@Event.context.attribute” before the field you want to use as a filter and then give it a name like “x<EM>sapcountrycode</EM>” for example:</P><pre class="lia-code-sample language-abap"><code>.context. attribute:'xsapcountrycode' CountryCode: abap.char(2);</code></pre><P>Then, during the runtime, the country code value “DE” is exposed inside the CloudEvent Context as an additional attribute with the name you have defined as the value of the annotation explained above:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter3.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250491iFD7B0B6962AE1D43/image-size/large?v=v2&amp;px=999" role="button" title="filter3.jpg" alt="filter3.jpg" /></span></P><P>With that the payload field ‘CountryCode’ becomes a new filter attribute. &nbsp;the source side filtering is available for every event infrastructure that we currently support. However, there are some restrictions which must be considered, for example not all properties are allowed for exposing in header, e.g., amount fields or cf. annotation, more information can be found in&nbsp;Maintain Filters for Outbound Event Topics | SAP Help Portal&nbsp;<SPAN><A href="https://help.sap.com/docs/abap-cloud/abap-rap/event-annotations" target="_blank" rel="noopener noreferrer">Event Annotations | SAP Help Portal</A></SPAN>. This <SPAN><A href="https://me.sap.com/notes/3343266" target="_blank" rel="noopener noreferrer">SAP Note</A></SPAN> describes in more detail what must be considered when creating filters and which restrictions apply.</P><P>The next step would be going to filter configuration UI and there you can select all the filterable fields like <EM>xsapcountrycode</EM> and define filter conditions like ‘eq ‘DE’ ’. You can also define filters containing multiple fields and add additional filter conditions for those fields.</P><P>&nbsp;Let’s look at the runtime, for example if you have an event with country code “DE”, this would be published because it would match the filter condition we already defined. Whereas if you have an event with country code “US”, this does not match the filter condition and would therefore be filtered out. This means if you want to prevent events from going out, for example due to data privacy protection or any other reason, then you can define corresponding filter conditions.</P><H3 id="toc-hId-1640913839"><STRONG>Define source side filters in your application </STRONG></H3><P>I already build a very simple application, an online shop as it is described in this <SPAN><A href="https://developers-qa-blue.wcms-nonprod.c.eu-de-2.cloud.sap/tutorials/abap-environment-create-rap-business-events.html" target="_blank" rel="noopener nofollow noreferrer">tutorial</A></SPAN>. I added the field country code as well to use it later as a filter, so when I create a new item, I can assign a country code for the country I want to ship the item to.</P><P>&nbsp;Now, we want to make sure that only events created for items that are shipped to “DE” are also published to the event exchange infrastructure. I create the event that should be raised whenever an item is created and the parameter or the fields that should be in this event are defined in this abstract entity and it looks like this:</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Event parameter' define abstract entity ZD_ItemOrdered_0000 { ItemName : abap.char(25); @Event.context. attribute:'xsapcountrycode' CountryCode: abap.char(2); }</code></pre><P>To make this event filterable, I just add this simple annotation “@Event.context.attribute” and I call it <EM>xsapcountrycode</EM>. when I activate this entity, for all events that are raised in this system, the field “CountryCode” is propagated into the Cloud Event context and contains the values that is in the payload. As a next step, we must define the filter criteria for the given event channel as well.&nbsp;</P><P>After creating your communication arrangement, go to “Configure Channel Binding” application to configure the related channel and add your outbound topic which is created during event binding creation as is explained in the <SPAN><A href="https://developers-qa-blue.wcms-nonprod.c.eu-de-2.cloud.sap/tutorials/abap-environment-create-rap-business-events.html" target="_blank" rel="noopener nofollow noreferrer">tutorial</A></SPAN>. &nbsp;As you see, there is no event filters defined yet. Keep in mind that you can only add filters to the existing&nbsp;outbound&nbsp;bindings.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter4.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250493i8464E4A6DBF3D134/image-size/large?v=v2&amp;px=999" role="button" title="filter4.jpg" alt="filter4.jpg" /></span></P><P>You can see here we have an outbound topic which is the event from the application. Now I want to define the filter. You can create filters on each topic individually by clicking on it. Then in event filter tab, click on ‘Create’ and choose the filter property. By clicking on “Create” the filter will be generated successfully.<SPAN>&nbsp;</SPAN> All defined filters are displayed under <STRONG>Event Filter Expression.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter5.jpg" style="width: 530px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250494iE70E708892B36FFD/image-dimensions/530x325?v=v2" width="530" height="325" role="button" title="filter5.jpg" alt="filter5.jpg" /></span></P><P>We can head back to the channel and see that there is event filters configured:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter6.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250496iB7E8D217ADC201BF/image-size/large?v=v2&amp;px=999" role="button" title="filter6.jpg" alt="filter6.jpg" /></span></P><P>Now let’s try to see if the filter works properly. For that, we will use our event monitor application which is a tool where you can monitor the status of your in/outbound events. First, you can see there are no events on this channel, we want to change that now.</P><P>&nbsp;I go into my Fiori application and press “Create” to create an order, a laptop for example, and I want to send that to Germany (DE). If you remember, this will match our filter criteria and therefore it would be sent out. An event can be monitored as expected, in the launchpad open the application “Enterprise Event Enablement-Event Monitor”, you can see an event reach your channel:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter7.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250497i08E7CAFACC5900FA/image-size/large?v=v2&amp;px=999" role="button" title="filter7.jpg" alt="filter7.jpg" /></span></P><P>If we look at the payload, we see the country code “DE” in the header as well</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter8.jpg" style="width: 424px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250498i71F6FD4A4B34F936/image-dimensions/424x401?v=v2" width="424" height="401" role="button" title="filter8.jpg" alt="filter8.jpg" /></span></P><P>Now to check if this filter works, I want to create another object in here, let's say I want to ship a tablet to Spain. I create this order and head back to our Event Monitor application. You can see the number of events hasn't changed, so the event with country code “ES” did not match our filter criteria and therefore was filtered. Note that you cannot monitor or log events that are filtered out. Such event behaves as if the event was never raised!</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter9.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250500i9017FFFE1BAD6DE6/image-size/large?v=v2&amp;px=999" role="button" title="filter9.jpg" alt="filter9.jpg" /></span></P><H2 id="toc-hId-1315317615"><STRONG>Dynamic topic filtering in Advanced Event Mesh</STRONG></H2><P>Dynamic Topics enable detailed event filtering and routing, ensuring that events are delivered only to the relevant components, improving efficiency and reducing unnecessary processing. The feature of Dynamic Topics is only available for channels integrating with the Advanced Event Mesh (AEM). For more information regarding AEM, please follow <SPAN><A href="https://help.pubsub.em.services.cloud.sap/Get-Started/get-started-lp.htm" target="_blank" rel="noopener nofollow noreferrer">Get Started with SAP Integration Suite, Advanced Event Mesh</A></SPAN>.</P><P>Imagine you have your RAP business event in BTP ABAP environment system, with the same abstract entity, that represents the actual event payload with the country code field. Similarly, you must add an additional annotation to enable Dynamic Topic or to enable that the country code is added as an additional segment to the Dynamic Topic. The first part of the annotation is the same; we have the event context attribute and then we give it a name, and additionally, you also need to define a position. This is because you could have multiple Dynamic Topic segments, and we need to know at which position each one should be put, so you can order them in the way you need them.</P><pre class="lia-code-sample language-abap"><code>.context:{attribute:'xsapcountrycode',position: 1} CountryCode: abap.char(2);</code></pre><P>As you see below, the country code is exposed to the header part, but the difference is, you can see at the end of the standard topic the value of the country code field is added as well. Since you could define multiple properties, the position is so important to be added to the Dynamic Topic because we need to know at which position after the standard topic, the Dynamic Topic segment for the given property should be added</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Picture2.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254068iED9D9CBC2723375F/image-size/large?v=v2&amp;px=999" role="button" title="Picture2.jpg" alt="Picture2.jpg" /></span></P><P>Besides, we want to use this field for routing and filtering the events in Advanced Event Mesh and for that, you also need to know the exact location of the specific property you want to use as a filter.</P><P>This is already everything you must do in BTP ABAP environment for your RAP business event.&nbsp; Assume you want to have two filters, one for the country code “DE” and the other one for the country code “US”. For that, you must define two queues in your Advanced Event Mesh.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter11.jpg" style="width: 612px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250502i4DBA362A0F7DD276/image-dimensions/612x126?v=v2" width="612" height="126" role="button" title="filter11.jpg" alt="filter11.jpg" /></span></P><P>Let's say for example in the “US” queue you only want to have events of the country code “US”. Then, you define this topic subscription accordingly and you need to do the same thing for the second queue where you want to receive the events with the country code “DE”.</P><P>Now I will show you how to route your events in practice. I have reverted all the changes I've made to enable source side filtering, so there is no event filters defined. Now we want to send out all the events and then put them into different queues depending on their properties. As it is explained before, this is how our abstract entity must look like:</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Event parameter' define abstract entity ZD_ItemOrdered_0000 { ItemName : abap.char(25); @Event.context:{attribute:'xsapcountrycode',position: 1} CountryCode: abap.char(2); }</code></pre><P>Create a channel for the integration with AEM, either by using the SAP GUI transaction in SAP S/4HANA, SAP S/4HANA Cloud Private Edition or respective communication arrangements in SAP S/4HANA Cloud Public Edition or SAP BTP ABAP Environment (as described <A href="https://help.pubsub.em.services.cloud.sap/Get-Started/get-started-lp.htm" target="_blank" rel="noopener nofollow noreferrer">Get Started with SAP Integration Suite, Advanced Event Mesh</A>). You can enable Dynamic Topics by using the button “Enable Dynamic Topics” which is only enabled for Advanced Event Mesh channels. By clicking on this button and enabling the Dynamic Topics, you can see that something has changed, namely the topic now contains this Dynamic Topic segment “xsapcountrycode”<EM>:</EM></P><P><EM><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter12.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250504i5FA733018749D102/image-size/large?v=v2&amp;px=999" role="button" title="filter12.jpg" alt="filter12.jpg" /></span></EM></P><P>Now I go to the connected Advanced Event Mesh broker and configure two queues, one for country code “DE” and another one for “US”:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter13.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250505iB222F401F7CA4484/image-size/large?v=v2&amp;px=999" role="button" title="filter13.jpg" alt="filter13.jpg" /></span></P><P>In subscriptions section of “DE” queue for example, you can see the topic we have for “DE” country code</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter14a.jpg" style="width: 732px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254069i39E54891242B2CF3/image-dimensions/732x149?v=v2" width="732" height="149" role="button" title="filter14a.jpg" alt="filter14a.jpg" /></span></P><P>and for the “US” it's comparable. That's everything that needs to be done to route events.</P><P>&nbsp;Let's create a new order to be shipped to “US”. &nbsp;Then in our event monitor application, there is an event here and you can see also the outbound event tab has changed to signify this topic schema, so you know there are Dynamic Topics in place.</P><P>If we look at this event, we can see the Dynamic Topic segment is indeed “US” and if we check the payload, it's the same as before, we have our <EM>xsapcountrycode </EM>which is lifted to the header and the country code in there:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter14.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250511iADB064015EA35641/image-size/large?v=v2&amp;px=999" role="button" title="filter14.jpg" alt="filter14.jpg" /></span></P><P>Now let's look at our queues and you can see one message has reached the US queue:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="filter15.jpg" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/250512iE79284EDB5B91E03/image-size/large?v=v2&amp;px=999" role="button" title="filter15.jpg" alt="filter15.jpg" /></span></P><P>With that, you have a simple but powerful routing tool for your events.</P><H2 id="toc-hId-1118804110"><STRONG>Summary</STRONG></H2><P>Here I shortly presented the newly developed possibilities of event filtering and routing for ABAP Cloud. Source side filtering prevents events from being published &nbsp;based on the filters we define using the payload fields which are exposed to the CloudEvent context. This can be used to satisfy data privacy and protection restrictions or also in case you wanted to distribute the load among different connections.</P><P>&nbsp;By means of the Dynamic Topic feature we are now able to route and filter the events in Advanced Event Mesh broker. Both features are already available in the current SAP S/4HANA Cloud Public Edition, SAP S/4HANA, &nbsp;SAP S/4 HANA &nbsp;Cloud Private Edition as well as SAP BTP ABAP environment releases.</P><P>In the near future, the customer should also able to choose any valid payload fields of the given RAP event type as an event filter attribute without using annotation “@Event.context.attribute”.&nbsp; Please follow the <SPAN><A href="https://pages.community.sap.com/topics/abap-connectivity" target="_blank" rel="noopener noreferrer">community page</A></SPAN> to get informed regarding updates in Enterprise Event Enablement content.</P><P>&nbsp;</P> 2025-04-23T13:18:38.307000+02:00 https://community.sap.com/t5/application-development-and-automation-blog-posts/bypassing-cds-filters-in-sadl-based-odata-services-for-flexible-fiori/ba-p/14086326 Bypassing CDS Filters in SADL-Based OData Services for Flexible Fiori Applications 2025-04-29T18:03:45.228000+02:00 Shyam4U https://community.sap.com/t5/user/viewprofilepage/user-id/1512208 <P><STRONG><SPAN>In modern SAP Fiori applications, developers often rely on CDS views exposed via SADL (Service Adaptation Definition Language) to build quick and efficient OData services. While this architecture promotes reusability and low-code development, it can become restrictive when CDS views include hardcoded filters or mandatory parameters.&nbsp;</SPAN></STRONG></P><P><FONT face="arial black,avant garde"><STRONG><SPAN>IO_TECH_REQUEST_CONTEXT</SPAN></STRONG><STRONG><SPAN> in </SPAN></STRONG><STRONG><SPAN>GET_EXPANDED_ENTITYSET</SPAN></STRONG></FONT><SPAN> is an interface that provides:</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN><span class="lia-unicode-emoji" title=":keycap_1:">1️⃣</span></SPAN><STRONG><SPAN>Request details</SPAN></STRONG><SPAN> (keys, headers, and </SPAN><SPAN>$expand</SPAN><SPAN> navigation properties)</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN><span class="lia-unicode-emoji" title=":keycap_2:">2️⃣</span></SPAN><STRONG><SPAN>Access to OData query options</SPAN></STRONG><SPAN> like filters and expansions</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN><span class="lia-unicode-emoji" title=":keycap_3:">3️⃣</span></SPAN><STRONG><SPAN>Contextual control</SPAN></STRONG><SPAN> for fetching a main entity with its related data in one call </SPAN><SPAN>&nbsp;</SPAN></P><P><I><SPAN>(Think of it as a "request assistant" that tells your OData service exactly what data to return and how.)</SPAN></I><SPAN>&nbsp;</SPAN></P><P><SPAN><A href="https://help.sap.com/doc/saphelp_nw75/7.5.5/en-US/0e/9b2451f8c0266ee10000000a445394/content.htm?no_cache=true" target="_self" rel="noopener noreferrer">/IWBEP/IF_MGW_REQ_ENTITYSET </A>&nbsp;&nbsp;</SPAN></P><P><STRONG><SPAN><span class="lia-unicode-emoji" title=":magnifying_glass_tilted_left:">🔍</span>Implementing Query Options Using </SPAN></STRONG><STRONG><SPAN>/IWBEP/IF_MGW_REQ_ENTITYSET</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>The interface </SPAN><SPAN>/IWBEP/IF_MGW_REQ_ENTITYSET</SPAN><SPAN> is a core component of SAP NetWeaver Gateway's code-based OData service implementation. It is primarily used within the </SPAN><SPAN>GET_ENTITYSET</SPAN><SPAN> method of the Data Provider Extension Class (</SPAN><SPAN>DPC_EXT</SPAN><SPAN>) to handle </SPAN><STRONG><SPAN>query options</SPAN></STRONG><SPAN> such as filtering, sorting, and paging.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN><span class="lia-unicode-emoji" title=":pushpin:">📌</span>Key Query Option Methods</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>This interface provides several methods to retrieve and handle query parameters passed from the client:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><STRONG><SPAN>get_filter_string( )</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Retrieves the raw filter string from the request URI. Useful for logging or manual parsing if needed.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>get_filter( )</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Returns a structured filter object that can be further analyzed using standard methods.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>get_filter_select_options( )</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Converts the filter into a select-option compatible internal table (</SPAN><SPAN>/iwbep/t_mgw_select_option</SPAN><SPAN>). This is particularly useful when passing filters to BAPIs, function modules, or for building dynamic WHERE clauses in ABAP.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>get_top( )</SPAN></STRONG><STRONG><SPAN> and </SPAN></STRONG><STRONG><SPAN>get_skip( )</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Retrieve the </SPAN><SPAN>$top</SPAN><SPAN> and </SPAN><SPAN>$skip</SPAN><SPAN> values used for client-side paging.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>get_orderby( )</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Provides sorting information from the </SPAN><SPAN>$orderby</SPAN><SPAN> clause.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>has_filter( )</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Checks whether a filter condition was provided in the request.</SPAN></LI></UL><P><SPAN><SPAN class="">If &nbsp;want</SPAN> <SPAN class="">know</SPAN><SPAN class=""> more&nbsp;<A href="https://learning.sap.com/learning-journeys/building-odata-services-with-sap-gateway/implementing-query-options" target="_self" rel="noopener noreferrer">Implementing Query Options</A>&nbsp;</SPAN></SPAN></P><P><SPAN><SPAN class=""><SPAN class="">To enable value help functionality in a CDS view, we first define the necessary annotations. Specifically, we use:</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></P><P><STRONG><SPAN><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1692417">@search</a>.defaultSearchElement:</SPAN></STRONG> <STRONG><SPAN>true</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>@Consumption.valueHelpDefinition:[{</SPAN></STRONG> <STRONG><SPAN>entity:{</SPAN></STRONG> <STRONG><SPAN>element:</SPAN></STRONG> <STRONG><SPAN>'MATNR'</SPAN></STRONG> <STRONG><SPAN>,name:</SPAN></STRONG> <STRONG><SPAN>'ZI_SEARCH_MARA2'</SPAN></STRONG> <STRONG><SPAN>}</SPAN></STRONG> <STRONG><SPAN>}]</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN><SPAN class=""><SPAN class="">In this context, </SPAN></SPAN><SPAN class=""><SPAN class="">ZI_SEARCH_MARA2</SPAN></SPAN><SPAN class=""><SPAN class=""> is a separate CDS view that serves as the </SPAN></SPAN><SPAN class=""><SPAN class="">value help entity</SPAN></SPAN><SPAN class=""><SPAN class="">.</SPAN></SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745578692829.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254688iBE6F497469253EA0/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745578692829.png" alt="Shyam4U_0-1745578692829.png" /></span></P><P><SPAN class=""><SPAN class="">In the CDS view </SPAN></SPAN><SPAN class=""><SPAN class="">ZI_SEARCH_MARA2</SPAN></SPAN><SPAN class=""><SPAN class="">, the annotation </SPAN></SPAN><SPAN class=""><SPAN class="">@ObjectModel.dataCategory: #VALUE_HELP</SPAN></SPAN><SPAN class=""><SPAN class=""> is </SPAN></SPAN><SPAN class=""><SPAN class="">mandatory</SPAN></SPAN><SPAN class=""><SPAN class=""> for enabling the view to function as a </SPAN></SPAN><SPAN class=""><SPAN class="">value help source</SPAN></SPAN><SPAN class=""><SPAN class=""> in Fiori applications. Without this annotation, the CDS view will </SPAN></SPAN><SPAN class=""><SPAN class="">not be recognized</SPAN></SPAN><SPAN class=""><SPAN class=""> by the Fiori framework as a valid value help provider, and the associated value help functionality will not work as expected</SPAN></SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745578880464.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254689i5D8C216A7CC27328/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745578880464.png" alt="Shyam4U_0-1745578880464.png" /></span></P><P><STRONG>Registering CDS as OData Data Source</STRONG><SPAN>&nbsp;</SPAN></P><OL><LI><STRONG><SPAN>Right-click on your OData project</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Select&nbsp;</SPAN><STRONG><SPAN>Referenced Data Sources</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>In the dialog:</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Choose&nbsp;</SPAN><STRONG><SPAN>"Core Data Services"</SPAN></STRONG><SPAN>&nbsp;tab</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Click&nbsp;</SPAN><STRONG><SPAN>"Add..."</SPAN></STRONG><SPAN>&nbsp;button</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Search for your CDS view (Z_MATERIALMASTER)</SPAN><SPAN>&nbsp;</SPAN></LI><LI>Select and confirm</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_1-1745578977772.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254691iB9DB0BDD739981F7/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_1-1745578977772.png" alt="Shyam4U_1-1745578977772.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745579222815.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254694i6F44621231C00348/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745579222815.png" alt="Shyam4U_0-1745579222815.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_1-1745579250947.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254695iCFEAF29F223BF924/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_1-1745579250947.png" alt="Shyam4U_1-1745579250947.png" /></span></P><P><FONT face="arial black,avant garde"><STRONG><SPAN class=""><SPAN class="">Now consuming the OData in SAP Fiori Generator by using the VS code </SPAN><SPAN class="">by </SPAN><SPAN class="">choosing</SPAN><SPAN class=""> the list report page </SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></FONT><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745579376712.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254697iBCBE6FD8494C85E4/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745579376712.png" alt="Shyam4U_0-1745579376712.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745579635803.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254704i50A6BE6AE387B6DA/image-size/medium?v=v2&amp;px=400" role="button" title="Shyam4U_0-1745579635803.png" alt="Shyam4U_0-1745579635803.png" /></span></P><P><FONT face="arial black,avant garde"><STRONG><SPAN class=""><SPAN class="">We can Preview </SPAN><SPAN class="">Application .</SPAN></SPAN></STRONG></FONT><SPAN class="">&nbsp;</SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745579811260.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254709iB592CAB969894808/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745579811260.png" alt="Shyam4U_0-1745579811260.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_1-1745579832033.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254710i684D27DCFA9AB5EE/image-size/medium?v=v2&amp;px=400" role="button" title="Shyam4U_1-1745579832033.png" alt="Shyam4U_1-1745579832033.png" /></span></P><P><FONT face="arial black,avant garde"><STRONG>Find the Entity Name From UI&nbsp;</STRONG></FONT></P><UL><LI><STRONG><SPAN>Open Browser Developer Tools</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Press&nbsp;F12&nbsp;or&nbsp;Ctrl+Shift+I&nbsp;(Chrome/Edge)</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Go to the&nbsp;</SPAN><STRONG><SPAN>Network</SPAN></STRONG><SPAN>&nbsp;tab</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>Trigger the Value Help</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Click on the value help icon in your Fiori app</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Watch for new network requests to appear</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>Locate the Batch Request ($batch)</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Filter requests by typing <STRONG>"$batch"</STRONG> in the search box</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Look for the most recent POST request to&nbsp;.../$batch</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>Inspect the Payload</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Click on the&nbsp;$batch&nbsp;request</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Go to the&nbsp;</SPAN><STRONG><SPAN>"Payload"</SPAN></STRONG><SPAN>&nbsp;tab (or "Request Payload")</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Look for lines starting with&nbsp;GET&nbsp;followed by entity names like:</SPAN><SPAN>&nbsp;</SPAN><STRONG>GET EntitySetName?$filter=...</STRONG></LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745580118030.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254715i559FE3105DD23F11/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745580118030.png" alt="Shyam4U_0-1745580118030.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_1-1745580176165.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254717iA1B9B6DF6EBF2056/image-size/medium?v=v2&amp;px=400" role="button" title="Shyam4U_1-1745580176165.png" alt="Shyam4U_1-1745580176165.png" /></span></P><P><FONT size="5"><STRONG><SPAN>Locating the EntitySet Method for Value Help in OData Projects</SPAN></STRONG><SPAN>&nbsp;</SPAN></FONT></P><P><STRONG><SPAN>Step-by-Step Guide to Find the Correct Method</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><STRONG><SPAN>After Identifying the Entity Name</SPAN></STRONG><SPAN> (from browser network inspection)</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Example: You found entity </SPAN><SPAN>Z_C_EntityName </SPAN><SPAN>in the $batch payload</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>Navigate to Your OData Project in ADT/Eclipse</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Open your OData service project</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Expand the "Runtime Artifacts" folder</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>Open the DPC_EXT Class</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Look for </SPAN><SPAN>ZCL_[YourServiceName]_DPC_EXT</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>This is your custom extension class that inherits from the generated DPC class</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>Find the EntitySet Method</SPAN></STRONG><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Search for methods with naming pattern:</SPAN><SPAN>[ENTITYSET_NAME]_GET_ENTITYSET</SPAN><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>For our example </SPAN><SPAN>Z_C_ProductVH</SPAN><SPAN>, look for:</SPAN><SPAN>Z_C_PRODUCTVH_GET_ENTITYSET</SPAN></LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745580487750.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254718i8549D1F6D6462E09/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745580487750.png" alt="Shyam4U_0-1745580487750.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_2-1745580600213.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254720iBF7E8FB91AF30C10/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_2-1745580600213.png" alt="Shyam4U_2-1745580600213.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_1-1745580566318.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254719iD519AA3395BDE867/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_1-1745580566318.png" alt="Shyam4U_1-1745580566318.png" /></span></P><P><STRONG><SPAN>1. Set Breakpoints</SPAN></STRONG><SPAN>:</SPAN><SPAN>&nbsp;</SPAN></P><OL><LI><SPAN>Put a breakpoint in the method and trigger the value help again</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Examine the </SPAN><SPAN>io_tech_request_context</SPAN><SPAN> contents during debugging</SPAN><SPAN>&nbsp;</SPAN></LI></OL><P><STRONG><SPAN>2.Check Base Class</SPAN></STRONG><SPAN>:</SPAN><SPAN>&nbsp;</SPAN></P><OL><LI><SPAN>If not found in DPC_EXT, check the parent </SPAN><SPAN>DPC</SPAN><SPAN> class</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Generated methods might be in the base class</SPAN><SPAN>&nbsp;</SPAN></LI></OL><P><STRONG><SPAN>3. Common Patterns</SPAN></STRONG><SPAN>:</SPAN><SPAN>&nbsp;</SPAN></P><OL><LI><SPAN>Value help methods often:</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Join multiple tables</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Implement special search algorithms</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Have </SPAN><SPAN>_GET_ENTITYSET</SPAN><SPAN> suffix</SPAN><SPAN>&nbsp;</SPAN></LI></OL><P><STRONG><SPAN>4. Related Methods</SPAN></STRONG><SPAN>:</SPAN><SPAN>&nbsp;</SPAN></P><OL><LI><SPAN>Sometimes value help uses both:</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>..._GET_ENTITYSET</SPAN><SPAN> for the list</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>..._GET_ENTITY</SPAN><SPAN> for single record validation</SPAN></LI></OL><P><SPAN><SPAN class=""><SPAN class="">This approach helps you quickly </SPAN><SPAN class="">locate</SPAN><SPAN class=""> and modify the backend logic powering your Fiori app's value help functionality.</SPAN></SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745582052495.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254725iD316569B6AEAB6DC/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745582052495.png" alt="Shyam4U_0-1745582052495.png" /></span></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_1-1745582100484.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254726i2F92AB60B25A4B91/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_1-1745582100484.png" alt="Shyam4U_1-1745582100484.png" /></span></P><P><FONT size="6"><STRONG><SPAN class=""><SPAN class="">Now we</SPAN><SPAN class=""> can</SPAN> <SPAN class="">implement</SPAN><SPAN class=""> code </SPAN><SPAN class="">here.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></FONT></P><P><FONT size="6"><SPAN class=""><SPAN class=""><SPAN class="">By Redefending the method </SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></FONT></P><pre class="lia-code-sample language-abap"><code> data(lv_filter) = io_tech_request_context-&gt;get_filter( )-&gt;get_filter_string( ). if lv_filter is NOT INITIAL. filter_string_url( EXPORTING iv_filter_string = lv_filter IMPORTING et_entityset = ET_ENTITYSET ). </code></pre><pre class="lia-code-sample language-abap"><code>data(lv_filter) = io_tech_request_context-&gt;get_filter( )-&gt;get_filter_string( ). </code></pre><OL><LI><STRONG>io_tech_request_context</STRONG><SPAN><STRONG>&nbsp;</STRONG><BR /></SPAN><SPAN>The gateway request object containing all OData call details (URL, headers, queries).</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG>-&gt;get_filter()</STRONG><SPAN><STRONG>&nbsp;</STRONG><BR /></SPAN><SPAN>Accesses the&nbsp;$filter&nbsp;clause from the request, returning a filter object reference.</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG>-&gt;get_filter_string()</STRONG><SPAN><STRONG>&nbsp;</STRONG><BR /></SPAN><SPAN>Extracts the raw filter condition as a string (e.g.,&nbsp;"(Price gt 100) and (Category eq 'Electronics')").</SPAN><SPAN>&nbsp;</SPAN></LI><LI><STRONG><SPAN>Key Purpose</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Gets the unprocessed filter exactly as sent by the client for debugging or custom parsing.</SPAN><SPAN>&nbsp;</SPAN></LI></OL><P><STRONG><SPAN>filter_string_url </SPAN></STRONG><SPAN>This method is used to custom filtering implementation is written which returns the entityset based on the lv_filters</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Lv_filter gives &nbsp;values like ( &nbsp; ( MATNR = ‘ 1 ’ &nbsp;) &nbsp;or ( MATNR = ‘ 7 ’ &nbsp;) )&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code> METHOD filter_string_url. DATA:lt_Matnr_range TYPE RANGE OF mara-Matnr, ls_Matnr_range LIKE LINE OF lt_Matnr_range. IF iv_filter_string IS NOT INITIAL. DATA:lv_string TYPE string. DATA:lt_string TYPE TABLE OF string. lv_string = iv_filter_string. REPLACE ALL OCCURRENCES OF '(' IN lv_string WITH ''. REPLACE ALL OCCURRENCES OF ')' IN lv_string WITH ''. REPLACE ALL OCCURRENCES OF `'` IN lv_string WITH ''. REPLACE ALL OCCURRENCES OF '&amp;' IN lv_string WITH 'AND'. REPLACE ALL OCCURRENCES OF 'and' IN lv_string WITH 'AND'. REPLACE ALL OCCURRENCES OF 'le' IN lv_string WITH ''. REPLACE ALL OCCURRENCES OF 'ge' IN lv_string WITH ''. REPLACE ALL OCCURRENCES OF 'MATNR' IN lv_string WITH ''. REPLACE ALL OCCURRENCES OF '=' IN lv_string WITH ''. TRANSLATE lv_string TO UPPER CASE. SPLIT lv_string AT 'AND' INTO TABLE lt_string. SPLIT lv_string AT 'OR' INTO TABLE lt_string. SORT lt_string. CLEAR lv_string. READ TABLE lt_string INTO lv_string INDEX 1. IF sy-subrc = 0. CONDENSE lv_string. CALL FUNCTION 'CONVERSION_EXIT_MATN1_INPUT' EXPORTING input = lv_string IMPORTING output = ls_Matnr_range-low EXCEPTIONS length_error = 1 OTHERS = 2. ENDIF. CLEAR lv_string. IF lines( lt_string ) &gt; 1. READ TABLE lt_string INTO lv_string INDEX lines( lt_string ). IF sy-subrc = 0. CONDENSE lv_string. CALL FUNCTION 'CONVERSION_EXIT_MATN1_INPUT' EXPORTING input = lv_string IMPORTING output = ls_Matnr_range-high EXCEPTIONS length_error = 1 OTHERS = 2. ENDIF. ENDIF. IF ls_Matnr_range-high IS NOT INITIAL. ls_Matnr_range-sign = 'I'. ls_Matnr_range-option = 'BT'. ELSE. ls_Matnr_range-sign = 'I'. ls_Matnr_range-option = 'EQ'. ENDIF. APPEND ls_Matnr_range TO lt_Matnr_range. CLEAR ls_Matnr_range. IF lt_Matnr_range IS NOT INITIAL. SELECT Matnr FROM mara INTO TABLE et_entityset WHERE Matnr IN lt_Matnr_range. ENDIF. ENDIF. ENDMETHOD. </code></pre><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_0-1745583176729.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254735i773AC1560CB2E56B/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_0-1745583176729.png" alt="Shyam4U_0-1745583176729.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Shyam4U_1-1745583204243.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254736iD452267013545A27/image-size/large?v=v2&amp;px=999" role="button" title="Shyam4U_1-1745583204243.png" alt="Shyam4U_1-1745583204243.png" /></span></P><P><FONT size="5"><STRONG>Conclusion:-</STRONG></FONT></P><P><SPAN>This approach provides the flexibility to:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Bypass restrictive CDS view filters when justified</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Implement custom business logic for value help</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Maintain control over data exposure</SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Improve user experience with dynamic filtering</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>Remember that while these techniques offer powerful customization options, they should be used judiciously to maintain system stability and security.</SPAN><SPAN>&nbsp;</SPAN></P> 2025-04-29T18:03:45.228000+02:00 https://community.sap.com/t5/application-development-and-automation-blog-posts/consuming-external-api-tax-rate-information-in-rap/ba-p/14087053 Consuming External API Tax Rate Information in RAP 2025-04-29T18:39:34.578000+02:00 Pramod_Hiremath2 https://community.sap.com/t5/user/viewprofilepage/user-id/1456813 <P><STRONG><SPAN>Introduction</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>With the increasing need for real-time data integration, consuming external APIs directly within the ABAP RESTful Application Programming Model (RAP) is becoming a critical requirement. This blog post demonstrates how to integrate external tax rate data into a RAP-based application using unmanaged query implementation.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Scenario Overview</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>The goal is to expose tax rate information retrieved from an external API within an SAP Fiori application, structured using RAP. This example uses two custom entities:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>ZI_TAXRATE_UQ (Root View)</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>ZI_TAXRATE_ITEM_UQ (Item View)</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>The backend implementation is handled in an ABAP class ZCL_TAXRATE_UQ that implements the interface IF_RAP_QUERY_PROVIDER.</SPAN><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Data Model Definition</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>The data model consists of:</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Root Entity: ZI_TAXRATE_UQ</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Interface View for Unmanaged scenario' @Metadata.allowExtensions: true define root custom entity ZI_TAXRATE_UQ { key Id : abap.int1; key Country : abap.char(2); key State : abap.char(2); key City : abap.char(20); key Postcode : abap.char(5); key Address1 : abap.char(50); key Address2 : abap.char(50); key TaxClass : abap.char(20); // Return Fields TaxRateLabel : abap.char(20); Shipping : abap.char(3); Rate : abap.dec(16, 4); Compound : abap.char(3); CompoundCriticality : abap.int1; Combined : abap.char(3); CombinedCriticality : abap.int1; CombineError : abap.char(3); CombineErrorCriticality : abap.int1; // This does not work with Custom Entities: _TaxrateItem : ... _TaxrateItem : composition [1..*] of zi_taxrate_item_uq; } </code></pre><P><STRONG>&nbsp;<SPAN class=""><SPAN class="">Item Entity: </SPAN><SPAN class="">ZI_TAXRATE_ITEM_UQ</SPAN></SPAN><SPAN class="">&nbsp;<BR /></SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Interface View from item details' //@ObjectModel.query.implementedBy: 'ABAP:ZCL_TAXRATE_UQ' @Metadata.allowExtensions: true define custom entity ZI_TAXRATE_ITEM_UQ { // Filter Fields key TaxrateId : abap.int1; key Id : abap.int1; key Country : abap.char(2); key State : abap.char(2); key City : abap.char(20); key Postcode : abap.char(5); key Address1 : abap.char(50); key Address2 : abap.char(50); key TaxClass : abap.char(20); // Return Fields TaxRateLabel : abap.char(20); Shipping : abap.char(3); Rate : abap.dec(16, 4); Compound : abap.char(3); _TaxRate : association to parent zi_taxrate_uq on $projection.taxrateid = _TaxRate.id and $projection.country = _TaxRate.country and $projection.state = _TaxRate.state and $projection.city = _TaxRate.city and $projection.postcode = _TaxRate.postcode and $projection.address1 = _TaxRate.address1 and $projection.address2 = _TaxRate.address2 and $projection.taxclass = _TaxRate.taxclass; } </code></pre><P><STRONG><SPAN class=""><SPAN class=""><SPAN class="">Custom Logic Implementation</SPAN></SPAN><SPAN class="">&nbsp;<BR /></SPAN></SPAN></STRONG></P><pre class="lia-code-sample language-abap"><code>CLASS zcl_taxrate_uq DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_rap_query_provider . METHODS: constructor. PROTECTED SECTION. METHODS: build_taxrates_query IMPORTING sql_filter TYPE string, get_tax_rates RETURNING VALUE(tax_rates_combined) TYPE zif_gtax_rates=&gt;ts_tax_rates_combined, sanitize_fields_for_insert, handle_paging IMPORTING io_request TYPE REF TO if_rap_query_request, get_value_from_sql_string IMPORTING VALUE(i_string) TYPE string " Input string with key-value pairs VALUE(i_key) TYPE string " Key to search for RETURNING VALUE(e_value) TYPE string. " Corresponding value PRIVATE SECTION. DATA: parameters TYPE if_rap_query_request=&gt;tt_parameters, sql_filter TYPE string, offset TYPE int8, page_size TYPE int8, max_rows TYPE int8. DATA: tr_query TYPE zif_gtax_rates=&gt;ts_taxrates_query, use_mockdata TYPE abap_bool, tax_rates_combined TYPE zif_gtax_rates=&gt;ts_tax_rates_combined, tax_rate TYPE zif_gtax_rates=&gt;ts_tax_rates. DATA: compound_criticality TYPE i, combined TYPE string, combined_critically TYPE i, combine_error TYPE string, combine_error_critically TYPE i. ENDCLASS. CLASS zcl_taxrate_uq IMPLEMENTATION. METHOD constructor. use_mockdata = abap_false. ENDMETHOD. METHOD if_rap_query_provider~select. TRY. CASE io_request-&gt;get_entity_id( ). WHEN 'ZI_TAXRATE_UQ'. " Read parameters and filters parameters = io_request-&gt;get_parameters( ). "Parameters not used sql_filter = io_request-&gt;get_filter( )-&gt;get_as_sql_string( ). " Get tax rates from remote API build_taxrates_query( sql_filter = sql_filter ). tax_rates_combined = get_tax_rates( ). sanitize_fields_for_insert( ). " Append the received tax rates to internal table DATA lt_taxrate TYPE STANDARD TABLE OF zi_taxrate_uq. INSERT VALUE #( Id = 1 Country = tr_query-country State = tr_query-state City = tr_query-city Postcode = tr_query-postcode Address1 = tr_query-address1 Address2 = tr_query-address2 TaxClass = tr_query-tax_class TaxRateLabel = tax_rates_combined-tax_rate-label Shipping = tax_rates_combined-tax_rate-shipping Rate = tax_rates_combined-tax_rate-rate Compound = tax_rates_combined-tax_rate-compound CompoundCriticality = compound_criticality Combined = combined CombinedCriticality = combined_criticality CombineError = combine_error CombineErrorCriticality = combine_error_criticality ) INTO TABLE lt_taxrate. " Return requested data IF io_request-&gt;is_data_requested( ). handle_paging( io_request ). io_response-&gt;set_data( lt_taxrate ). ENDIF. " Return total number of records IF io_request-&gt;is_total_numb_of_rec_requested( ). io_response-&gt;set_total_number_of_records( lines( lt_taxrate ) ). ENDIF. WHEN 'ZI_TAXRATE_ITEM_UQ' . parameters = io_request-&gt;get_parameters( ). sql_filter = io_request-&gt;get_filter( )-&gt;get_as_sql_string( ). " Get tax rates from remote API build_taxrates_query( sql_filter = sql_filter ). tax_rates_combined = get_tax_rates( ). " Append the received tax rates to internal table DATA lt_taxrate_item TYPE STANDARD TABLE OF zi_taxrate_item_uq. DATA(lv_id) = 1. LOOP AT tax_rates_combined-tax_rates INTO tax_rate. INSERT VALUE #( TaxrateId = 1 Id = lv_id Country = tr_query-country State = tr_query-state City = tr_query-city Postcode = tr_query-postcode Address1 = tr_query-address1 Address2 = tr_query-address2 TaxClass = tr_query-tax_class TaxRateLabel = tax_rate-label Shipping = tax_rate-shipping Rate = tax_rate-rate Compound = tax_rate-compound ) INTO TABLE lt_taxrate_item. lv_id += 1. ENDLOOP. " Query table with id AND previous query if specific item is requested (by click on an item in the Tax Rate Items - Listview) DATA(id) = get_value_from_sql_string( i_string = sql_filter i_key = 'ID' ). IF id IS NOT INITIAL. SELECT * FROM _taxrate_item AS taxrates WHERE (sql_filter) ORDER BY Id INTO TABLE (lt_result). lt_taxrate_item = lt_result. ENDIF. " Return requested data IF io_request-&gt;is_data_requested( ). handle_paging( io_request ). io_response-&gt;set_data( lt_taxrate_item ). ENDIF. ENDCASE. CATCH cx_rap_query_provider. ENDTRY. ENDMETHOD. METHOD build_taxrates_query. IF use_mockdata = abap_false. tr_query-country = get_value_from_sql_string( i_string = sql_filter i_key = 'COUNTRY' ). tr_query-state = get_value_from_sql_string( i_string = sql_filter i_key = 'STATE' ). tr_query-city = get_value_from_sql_string( i_string = sql_filter i_key = 'CITY' ). tr_query-postcode = get_value_from_sql_string( i_string = sql_filter i_key = 'POSTCODE' ). tr_query-address1 = get_value_from_sql_string( i_string = sql_filter i_key = 'ADDRESS1' ). tr_query-address2 = get_value_from_sql_string( i_string = sql_filter i_key = 'ADDRESS2' ). tr_query-tax_class = get_value_from_sql_string( i_string = sql_filter i_key = 'TAXCLASS' ). ELSE. tr_query-country = 'US'. tr_query-state = 'CA'. tr_query-city = 'Mountain View'. tr_query-postcode = '94043'. tr_query-address1 = '1600 Amphitheatre Parkway'. tr_query-address2 = ''. tr_query-tax_class = ''. ENDIF. ENDMETHOD. METHOD get_tax_rates. DATA: gtax_client TYPE REF TO zcl_gtax_client, jsonpath TYPE REF TO zcl_jsonpath, tax_rates_json TYPE string. gtax_client = NEW zcl_gtax_client( ). jsonpath = NEW zcl_jsonpath( ). TRY. tax_rates_json = gtax_client-&gt;get_tax_rates( tr_query = tr_query use_mockdata = use_mockdata ). jsonpath-&gt;set( tax_rates_json ). jsonpath-&gt;get_data( EXPORTING query_string = '$' CHANGING data = tax_rates_combined ). CATCH cx_http_dest_provider_error cx_web_http_client_error INTO DATA(lx_error). "out-&gt;write( lx_error-&gt;get_text( ) ). "EXIT. ENDTRY. ENDMETHOD. METHOD sanitize_fields_for_insert. compound_criticality = 3. "Green IF tax_rates_combined-tax_rate-compound = 'yes'. compound_criticality = 2. "Orange ENDIF. combined = 'no '. combined_criticality = 3. "Green IF tax_rates_combined-combined = abap_true. combined = 'yes'. combined_criticality = 5. "Blue ENDIF. combine_error = 'no '. combine_error_criticality = 3. "Green IF tax_rates_combined-combine_error = abap_true. combine_error = 'yes'. combine_error_criticality = 2. "Orange ENDIF. ENDMETHOD. METHOD handle_paging. " Get Paging to prevent "NOT IMPLEMENTED" error offset = io_request-&gt;get_paging( )-&gt;get_offset( ). page_size = io_request-&gt;get_paging( )-&gt;get_page_size( ). max_rows = COND #( WHEN page_size = if_rap_query_paging=&gt;page_size_unlimited THEN 0 ELSE page_size ). ENDMETHOD. METHOD get_value_from_sql_string. DATA: lt_parts TYPE TABLE OF string, lv_field TYPE string, lv_value TYPE string. " Replace all occurrences of 'AND' in i_string with ';' REPLACE ALL OCCURRENCES OF 'AND' IN i_string WITH ';'. " Split the transformed string into individual key-value pairs SPLIT i_string AT ';' INTO TABLE lt_parts. LOOP AT lt_parts INTO DATA(lv_part). " Split each key-value pair into key and value using '=' as the delimiter SPLIT lv_part AT '=' INTO lv_field lv_value. " Check if the current key matches the input key (i_key) IF lv_field = i_key. e_value = lv_value. RETURN. " Exit method once matching key is found ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. </code></pre><P><STRONG><SPAN>External API Consumption</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>The </SPAN><SPAN>get_tax_rates</SPAN><SPAN> method uses a client class </SPAN><SPAN>ZCL_GTAX_CLIENT</SPAN><SPAN> to retrieve data from the remote endpoint. The JSON response is parsed using a helper class </SPAN><SPAN>ZCL_JSONPATH</SPAN><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Query Preparation</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>build_taxrates_query</SPAN><SPAN> extracts input parameters from SQL filters and constructs the API request payload. Mock data can be used optionally for testing.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Result Formatting</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>The </SPAN><SPAN>sanitize_fields_for_insert</SPAN><SPAN> method enriches the result by computing criticality indicators for compound status, combination, and error handling.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Paging Support</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Handled by </SPAN><SPAN>handle_paging</SPAN><SPAN>, which uses standard RAP query paging mechanisms to limit result sets.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Utility for SQL Filter Parsing</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>get_value_from_sql_string</SPAN><SPAN> helps extract individual filter values (like Country, State, etc.) from a SQL string.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Service Definition and Binding</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>The first screenshot shows the </SPAN><STRONG><SPAN>Service Definition (ZUI_TAXRATE_O4)</SPAN></STRONG><SPAN> where both custom entities ZI_TAXRATE_UQ and ZI_TAXRATE_ITEM_UQ are exposed using the OData V4 protocol. The service is later bound in the Service Binding screen where the service endpoint is generated for UI consumption.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_0-1745645999091.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255114i887CB0AB4124AF5F/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_0-1745645999091.png" alt="Pramod_Hiremath2_0-1745645999091.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_1-1745645999092.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255115i2581D37A348B9782/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_1-1745645999092.png" alt="Pramod_Hiremath2_1-1745645999092.png" /></span></P><P><STRONG><SPAN>Fiori Preview </SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>The </SPAN><STRONG><SPAN>initial screen</SPAN></STRONG><SPAN> before search execution shows an empty table, prompting the user to input filter values and trigger the tax rate retrieval.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_2-1745645999094.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255116iE37B6AD1557F12DF/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_2-1745645999094.png" alt="Pramod_Hiremath2_2-1745645999094.png" /></span></P><P><SPAN>This example shows the tax rate lookup for </SPAN><STRONG><SPAN>Austria (AT)</SPAN></STRONG><SPAN>, where a VAT of 20% is retrieved based on the given input parameters. It highlights that the service is capable of handling international addresses.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_3-1745645999096.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255117i1EBBE4A2A4711DEF/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_3-1745645999096.png" alt="Pramod_Hiremath2_3-1745645999096.png" /></span></P><P><SPAN>The </SPAN><STRONG><SPAN>filter bar</SPAN></STRONG><SPAN> allows users to input specific address details like Country, State, City, Postcode, Address1, and Tax Class to trigger the API call and retrieve appropriate tax rate information dynamically.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_4-1745645999099.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255118i6311AD1A55F4156C/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_4-1745645999099.png" alt="Pramod_Hiremath2_4-1745645999099.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_5-1745645999101.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255119i8BFCB550AB845895/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_5-1745645999101.png" alt="Pramod_Hiremath2_5-1745645999101.png" /></span></P><P><SPAN>The </SPAN><STRONG><SPAN>Address tab</SPAN></STRONG><SPAN> displays the country, state, city, postcode, and address information entered by the user, which is used to filter and query the correct tax rates from the external API.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_6-1745645999102.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255121i6F6F1B31C6FF7DCA/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_6-1745645999102.png" alt="Pramod_Hiremath2_6-1745645999102.png" /></span></P><P><SPAN>Below image shows the </SPAN><STRONG><SPAN>list of Taxrate Items</SPAN></STRONG><SPAN> for a given address. Each line item represents a tax type like "CA COUNTY TAX", "CA STATE TAX", and "CA SPECIAL TAX", along with the respective tax rate and compound information.</SPAN><SPAN>&nbsp;</SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_7-1745645999103.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255122i3B4DCD5612B871AA/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_7-1745645999103.png" alt="Pramod_Hiremath2_7-1745645999103.png" /></span><BR /><SPAN>Below image&nbsp;presents a </SPAN><STRONG><SPAN>detailed view of a single Taxrate Item</SPAN></STRONG><SPAN>. It displays the tax label, rate, and compound status retrieved from the external API.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pramod_Hiremath2_8-1745645999105.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255120i492DFE5A314CE778/image-size/large?v=v2&amp;px=999" role="button" title="Pramod_Hiremath2_8-1745645999105.png" alt="Pramod_Hiremath2_8-1745645999105.png" /></span></P><P><STRONG><SPAN>Conclusion</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>This implementation demonstrates how RAP can be extended to consume and expose data from external APIs using unmanaged custom entities and ABAP logic. Key advantages of this design include:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Full control over API communication and data transformation.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Reusability and testability using mock data.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Seamless Fiori integration via RAP and CDS.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN class=""><SPAN class="">&nbsp;</SPAN></SPAN></STRONG></P><P>&nbsp;</P> 2025-04-29T18:39:34.578000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/implementing-deep-action-for-bulk-approval-in-rap/ba-p/14086471 Implementing Deep Action for Bulk Approval In Rap 2025-04-29T18:56:26.024000+02:00 Umesh_Gauda https://community.sap.com/t5/user/viewprofilepage/user-id/1451063 <P>Introduction</P><P>In this blog, we will explore how to implement a deep action in the ABAP RESTful Application Programming Model (RAP). Our use case involves bulk approval of Purchase Orders (POs) along with their respective line items.</P><P>Deep Action :</P><P>A Deep Action in the ABAP RESTful Application Programming Model (RAP) is a custom behavior action that allows processing and manipulation of a hierarchical structure of entities—including the root entity and its related child entities—in a single transactional call.</P><P>&nbsp;</P><P>Steps for end-to-end implementation,</P><P>CDS view creation</P><P>Behavior definitions</P><P>Abstract entities</P><P>Deep action logic</P><P>Metadata extensions</P><P>Service definition</P><P>Testing</P><P>Business scenario.</P><P>multiple purchase orders (POs) for procuring raw materials and equipment. Each PO contains several line items detailing the materials, quantities, and suppliers. Streamline operations, the company wants to implement a feature that allows bulk approval of selected POs along with their respective line items.</P><P>Implementation Steps:</P><P>Define the Data Model:</P><P>&nbsp;</P><P>Behavior Definition:</P><P>Action Definition: In the behavior definition (behavior definition), define a deep action Approves that operates on PO Header and its associated PO Item entities.</P><P>Behavior Implementation:</P><P>Purchase Order Header Entity (PO Header):</P><P>Represents the main details of the purchase order, such as PO number, supplier, and approval status.</P><P>Database Table.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'purchase order header' @AbapCatalog.enhancement.category : #EXTENSIBLE_ANY @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zug_dt_poheader { key client : abap.clnt not null; key ebeln : ebeln not null; bukrs : bukrs; lifnr : lifnr; ekorg : ekorg; ekgrp : ekgrp; bstyp : bstyp; status : zug_postatus; }</code></pre><P>Purchase Order Item Entity (PO Item):</P><P>Contains details of each line item within a PO, including material ID, quantity, price, and item-specific approval status.</P><P><BR />POitem.</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'purchase order item' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zug_dt_poitem { key client : abap.clnt not null; @AbapCatalog.foreignKey.screenCheck : false key ebeln : ebeln not null with foreign key [0..*,1] zug_dt_poheader where ebeln = zug_dt_poitem.ebeln; key ebelp : ebelp not null; matnr : matnr; munit : abap.unit(3); @Semantics.quantity.unitOfMeasure : 'zug_dt_poitem.munit' menge : menge_d; cunit : abap.cuky; @Semantics.amount.currencyCode : 'zug_dt_poitem.cunit' netpr : abap.curr(10,2); status : zug_postatus; }</code></pre><P><BR />Behavior Definition:</P><P>Action Definition: In the behavior definition (behavior definition), define a deep action Approves that operates on PO Header and its associated PO Item entities.</P><P>Interface entity.</P><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_ug_i_poheader2 unique; strict ( 2 ); define behavior for zug_i_poheader1 lock master authorization master ( instance ) { static action Bulkorderapprove deep parameter zug_s_poheader; create; update; delete; association _poitem { create; } mapping for zug_dt_poheader { Bstyp = bstyp; Bukrs = bukrs; Ebeln = ebeln; Ekgrp = ekgrp; Ekorg = ekorg; Lifnr = lifnr; Status = status; } } define behavior for zug_i_poitem1 lock dependent by _poheader authorization dependent by _poheader { update; delete; field ( readonly ) Ebeln; association _poheader; mapping for zug_dt_poitem { Ebeln = ebeln; Ebelp = ebelp; Matnr = matnr; Menge = menge; Munit = munit; Netpr = netpr; Status = status; } }</code></pre><P>Association: Establish a composition association between PO Header and PO Item to model the hierarchical relationship.</P><P>Projection view.</P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); define behavior for zug_p_poheader1 //alias &lt;alias_name&gt; { use create; use update; use delete; use action Bulkorderapprove; use association _poitem { create; } } define behavior for zug_p_poitem //alias &lt;alias_name&gt; { use update; use delete; use association _poheader; }</code></pre><P>&nbsp;</P><P>Abstract entity.</P><pre class="lia-code-sample language-abap"><code>abstract; strict ( 2 ); with hierarchy; define behavior for zug_s_poheader //alias &lt;alias_name&gt; { association _poitem; } define behavior for zug_s_poitem //alias &lt;alias_name&gt; { } Abstract Entities @EndUserText.label: 'abstract entity' define root abstract entity zug_s_poheader { key ebeln : ebeln ; status : zug_postatus ; last_changed_by : abp_locinst_lastchange_user; last_changed_at : abp_locinst_lastchange_tstmpl; _poitem : composition [0..*] of zug_s_poitem; } For item @EndUserText.label: 'poitem abstarct entity' define abstract entity zug_s_poitem { key ebeln : ebeln ; key ebelp : ebelp ; status : zug_postatus ; last_changed_by : abp_locinst_lastchange_user; last_changed_at : abp_locinst_lastchange_tstmpl; _poheader : association to parent zug_s_poheader on $projection.ebeln = _poheader.ebeln; }</code></pre><P>Meta data extension for header</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity zug_p_poheader1 with { @UI.facet: [{ id: 'Poheader', purpose: #STANDARD, position: 10 , label: 'Poheader', type: #IDENTIFICATION_REFERENCE }, { id: 'Poitem', purpose: #STANDARD, position: 20 , label: 'Poitem', type: #LINEITEM_REFERENCE, targetElement: '_poitem' } ] @UI.lineItem: [{ position: 10 }] @UI.identification: [{ position: 10 }] Ebeln; @UI.lineItem: [{ position: 20 }] @UI.identification: [{ position: 20 }] Bukrs; @UI.lineItem: [{ position: 20 }] @UI.identification: [{ position: 30 }] Lifnr; @UI.lineItem: [{ position: 40 }] @UI.identification: [{ position: 40 }] Ekorg; @UI.lineItem: [{ position: 50 }] @UI.identification: [{ position: 50 }] Ekgrp; @UI.lineItem: [{ position: 60 }] @UI.identification: [{ position: 60 }] Bstyp; @UI.lineItem: [{ position: 70 } , { type: #FOR_ACTION , dataAction: 'Bulkorderapprove' , label : 'Bulkorderapprove' } ] @UI.identification: [{ position: 70 }] Status; }</code></pre><P><BR />Action Handler: Implement the Approves action in the behavior implementation class. This method will loop through the selected POs and their items, updating their approval statuses.</P><P>Implementing class</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_zug_i_poheader1 DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zug_i_poheader1 RESULT result. METHODS create FOR MODIFY IMPORTING entities FOR CREATE zug_i_poheader1. METHODS update FOR MODIFY IMPORTING entities FOR UPDATE zug_i_poheader1. METHODS delete FOR MODIFY IMPORTING keys FOR DELETE zug_i_poheader1. METHODS read FOR READ IMPORTING keys FOR READ zug_i_poheader1 RESULT result. METHODS lock FOR LOCK IMPORTING keys FOR LOCK zug_i_poheader1. METHODS rba_Poitem FOR READ IMPORTING keys_rba FOR READ zug_i_poheader1\_Poitem FULL result_requested RESULT result LINK association_links. METHODS cba_Poitem FOR MODIFY IMPORTING entities_cba FOR CREATE zug_i_poheader1\_Poitem. METHODS Bulkorderapprove FOR MODIFY IMPORTING keys FOR ACTION zug_i_poheader1~Bulkorderapprove. ENDCLASS. CLASS lhc_zug_i_poheader1 IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD create. data ls_c_o TYPE zug_dt_poheader. data lt_c_o TYPE TABLE of zug_dt_poheader. lt_c_o = CORRESPONDING #( entities mapping from ENTITY ). ls_c_o = lt_c_o[ 1 ]. zcl_ug_h=&gt;gs_tab_c = ls_c_o ENDMETHOD. METHOD update. data ls_u_o TYPE zug_dt_poheader data lt_u_o TYPE TABLE of zug_dt_poheader. lt_u_o = CORRESPONDING #( entities mapping from ENTITY ). ls_u_o = lt_u_o[ 1 ]. zcl_ug_h=&gt;gs_tab_u = ls_u_o. ENDMETHOD. METHOD delete. ENDMETHOD. METHOD read. SELECT from zug_dt_poheader FIELDS * INTO TABLE (lt_porders). result = CORRESPONDING #( lt_porders mapping to entity ). ENDMETHOD. METHOD lock. ENDMETHOD. METHOD rba_Poitem. ENDMETHOD. METHOD cba_Poitem. ENDMETHOD. METHOD Bulkorderapprove. DATA lt_order TYPE TABLE for UPDATE zug_i_poheader1. DATA lt_item TYPE TABLE for UPDATE zug_i_poitem1. data(ls_s) = VALUE #( keys[ 1 ] OPTIONAL ). lt_order = value #( ( ebeln = ls_s-%param-ebeln status = ls_s-%param-status ) ). lt_item = value #( for ls_i in ls_s-%param-_poitem ( ebeln = ls_i-ebeln ebelp = ls_i-ebelp ) ). MODIFY ENTITIES OF zug_i_poheader1 IN LOCAL MODE ENTITY zug_i_poheader1 UPDATE from lt_order ENTITY zug_i_poitem1 UPDATE FROM lt_item FAILED DATA(lt_failed) REPORTED data(lt_reported). ENDMETHOD. ENDCLASS. CLASS lhc_zug_i_poitem1 DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS update FOR MODIFY IMPORTING entities FOR UPDATE zug_i_poitem1. METHODS delete FOR MODIFY IMPORTING keys FOR DELETE zug_i_poitem1. METHODS read FOR READ IMPORTING keys FOR READ zug_i_poitem1 RESULT result METHODS rba_Poheader FOR READ IMPORTING keys_rba FOR READ zug_i_poitem1\_Poheader FULL result_requested RESULT result LINK association_links ENDCLASS. CLASS lhc_zug_i_poitem1 IMPLEMENTATION. METHOD update. ENDMETHOD. METHOD delete. ENDMETHOD. METHOD read. ENDMETHOD. METHOD rba_Poheader. ENDMETHOD. ENDCLASS. CLASS lsc_ZUG_I_POHEADER1 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_ZUG_I_POHEADER1 IMPLEMENTATION. METHOD finalize. ENDMETHOD. METHOD check_before_save. ENDMETHOD. METHOD save. if zcl_ug_h=&gt;gs_tab_c is NOT INITIAL. INSERT zug_dt_poheader from @zcl_ug_h=&gt;gs_tab_c. ELSEIF zcl_ug_h=&gt;gs_tab_u Is NOT INITIAL. INSERT zug_dt_poheader from @zcl_ug_h=&gt;gs_tab_u. ENDIF. ENDMETHOD. METHOD cleanup. ENDMETHOD. METHOD cleanup_finalize. ENDMETHOD. ENDCLASS. DATA lt_order TYPE TABLE for UPDATE zug_i_poheader1. DATA lt_item TYPE TABLE for UPDATE zug_i_poitem1 data(ls_s) = VALUE #( keys[ 1 ] OPTIONAL ). lt_order = value #( ( ebeln = ls_s-%param-ebeln status = ls_s-%param-status ) ). lt_item = value #( for ls_i in ls_s-%param-_poitem ( ebeln = ls_i-ebeln ebelp = ls_i-ebelp ) ). MODIFY ENTITIES OF zug_i_poheader1 IN LOCAL MODE ENTITY zug_i_poheader1 UPDATE from lt_order ENTITY zug_i_poitem1 UPDATE FROM lt_item FAILED DATA(lt_failed) REPORTED data(lt_reported).</code></pre><P><SPAN>Service Exposure:</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Service Definition: Expose the PO Header and PO Item entities through a service definition to make them available for consumption.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Service Binding: Create a service binding to expose the service via OData.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Service binding&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Umesh_Gauda_0-1745585766202.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254759iD6C22DD33D74C86A/image-size/large?v=v2&amp;px=999" role="button" title="Umesh_Gauda_0-1745585766202.png" alt="Umesh_Gauda_0-1745585766202.png" /></span></P><P><SPAN>Steps to test&nbsp;</SPAN></P><P><SPAN>Set Auth&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Umesh_Gauda_1-1745585766203.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254760i73806A35D1811BBF/image-size/large?v=v2&amp;px=999" role="button" title="Umesh_Gauda_1-1745585766203.png" alt="Umesh_Gauda_1-1745585766203.png" /></span></P><P>&nbsp;</P><P><SPAN>&nbsp;</SPAN><SPAN>In header you can pass&nbsp; these the click on get&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Umesh_Gauda_2-1745585766204.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254758i04FCF919483532C8/image-size/large?v=v2&amp;px=999" role="button" title="Umesh_Gauda_2-1745585766204.png" alt="Umesh_Gauda_2-1745585766204.png" /></span></P><P><SPAN>After that we can post request and get the result</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Umesh_Gauda_3-1745585766205.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254763iFAF5B853F68A55DF/image-size/large?v=v2&amp;px=999" role="button" title="Umesh_Gauda_3-1745585766205.png" alt="Umesh_Gauda_3-1745585766205.png" /></span></P><P>&nbsp;</P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Umesh_Gauda_4-1745585766207.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254762i8B282FC86CBB7E2E/image-size/large?v=v2&amp;px=999" role="button" title="Umesh_Gauda_4-1745585766207.png" alt="Umesh_Gauda_4-1745585766207.png" /></span></P><P><SPAN>Header.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Umesh_Gauda_5-1745585766208.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254761iA678A47F4A095026/image-size/large?v=v2&amp;px=999" role="button" title="Umesh_Gauda_5-1745585766208.png" alt="Umesh_Gauda_5-1745585766208.png" /></span></P><P>&nbsp;</P><P><SPAN>&nbsp;</SPAN><SPAN>Po item.</SPAN><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Umesh_Gauda_6-1745585766208.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/254764i749087B80348377F/image-size/large?v=v2&amp;px=999" role="button" title="Umesh_Gauda_6-1745585766208.png" alt="Umesh_Gauda_6-1745585766208.png" /></span></P><P>&nbsp;</P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P> 2025-04-29T18:56:26.024000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/enable-multi-input-field-on-the-object-page-using-rap/ba-p/14089415 Enable Multi-Input Field on the Object Page using RAP 2025-04-29T20:46:31.881000+02:00 pavankumar_reddy90 https://community.sap.com/t5/user/viewprofilepage/user-id/189954 <P><STRONG>How to enable Multi-Input Field on the Object Page using RAP?</STRONG><BR /><BR />Lets take a example of Procurement Project Business Object. In the BO there is a field "Company Code" for which multi-input needs to enabled in object page.&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1745925601110.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/256030iE4401D40E3A30733/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1745925601110.png" alt="pavankumar_reddy90_0-1745925601110.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_1-1745952492661.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/256343i1F3624A027302A3E/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_1-1745952492661.png" alt="pavankumar_reddy90_1-1745952492661.png" /></span></P><P>Below are the steps to enable above functionality using RAP:</P><P>1. Create a custom table with fields "company code" (here companycode is the field of root entity project ) and "<SPAN>procurementprojectuuid" ( here project uuid is the key field of root entity&nbsp; project)</SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Procurement Project Company Code' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zprocproj_cc { key client : abap.clnt not null; key companycode : bukrs not null; procurementprojectuuid : vdm_procurementprojectuuid; }</code></pre><P>2. Create a Interface view for&nbsp;custom table create at step 1.</P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Company COde' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define view entity ZI_Company_Code_XX as select from zprocproj_cc association to parent ZI_PROC_PROJ_XX as _procproj on $projection.procurementprojectuuid = _procproj.Procurementprojectuuid { key companycode, procurementprojectuuid, _procproj }</code></pre><P>Here "<SPAN>ZI_Company_Code_XX" is the Interface View for CompanyCode custom table and "ZI_PROC_PROJ_XX" is the Root Interface View for Procurement Project BO.</SPAN></P><P><SPAN>You can also find <STRONG>association to parent&nbsp;</STRONG>relationship from companycode Interface view to root view procurement project.</SPAN></P><P><SPAN>3. Create 1:N composition relation from root entity to company code interface view which is created in step 2</SPAN></P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Procument Project Interface View' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define root view entity ZI_PROC_PROJ_XX as select from mmprocmtproj composition [0..*] of zi_proc_plant_xx as _plant composition [0..*] of ZI_Company_Code_XX as _CompanyCode { key procurementprojectuuid as Procurementprojectuuid, procurementproject as Procurementproject, procurementprojectname as Procurementprojectname, externalprojectreference as Externalprojectreference, companycode as Companycode, @Semantics.user.createdBy: true createdbyuser as Createdbyuser, @Semantics.user.lastChangedBy: true lastchangedbyuser as Lastchangedbyuser, @Semantics.systemDateTime.createdAt: true creationdatetime as Creationdatetime, @Semantics.systemDateTime.lastChangedAt: true lastchangedatetime as Lastchangedatetime, procurementprojectactvtnsts as Procurementprojectactvtnsts, procmtplanningsubprojectuuid as Procmtplanningsubprojectuuid, sourcingorigin as Sourcingorigin, sourcingscenario as Sourcingscenario, iseopblocked as Iseopblocked, _plant, _CompanyCode }</code></pre><P>Here root entity is&nbsp;<SPAN>ZI_PROC_PROJ_XX and it has 1:N composition relationship to child entity&nbsp;ZI_Company_Code_XX.</SPAN></P><P><SPAN>Make sure you activate&nbsp;ZI_PROC_PROJ_XX and&nbsp;ZI_Company_Code_XX together when you form the business object relationship.</SPAN></P><P>4. Enhance &nbsp;<SPAN>ZI_Company_Code_XX behavior in base BO&nbsp;ZI_PROC_PROJ_XX</SPAN></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_proc_proj_xx unique; strict ( 2 ); with draft; define behavior for ZI_PROC_PROJ_XX alias Project persistent table mmprocmtproj draft table zprocproj_dxx lock master total etag Lastchangedatetime authorization master ( instance, global ) etag master Lastchangedatetime with additional save early numbering { create; update; delete ( features : global ); draft action Edit; draft action Discard; draft action Resume; draft action Activate; draft determine action Prepare { validation validate_companycode; } //field ( numbering : managed ) Procurementprojectuuid; // field ( mandatory ) Companycode; field ( readonly : update ) Externalprojectreference; field ( readonly ) Sourcingscenario; //field ( mandatory : create, readonly : update ) Procurementproject; //V4 field ( features : instance ) Procurementproject; association _plant { create; with draft; } association _CompanyCode { create; with draft; } action ( features : instance ) setactive parameter zabstract_project_xx result [1] $self; validation validate_companycode on save { field Companycode; } determination set_status on modify { create; } determination set_project_key on save { create; } mapping for mmprocmtproj corresponding { Lastchangedatetime = lastchangedatetime; } } define behavior for zi_proc_plant_xx alias Plant //persistent table mmpprojplant draft table zprocplantd_xx lock dependent by _project authorization dependent by _project //etag master &lt;field_name&gt; with unmanaged save { update; delete; field ( numbering : managed ) procmtprojectplantuuid; field ( readonly ) Procurementprojectuuid; association _project { with draft; } } define behavior for ZI_Company_Code_XX alias CompanyCode persistent table zprocproj_cc draft table zprocproj_ccd lock dependent by _procproj authorization dependent by _procproj { update; delete; field ( readonly ) Procurementprojectuuid; association _procproj { with draft; } }</code></pre><P>&nbsp;What additions are added in the above behavior:</P><P>1.&nbsp;<SPAN>define behavior for ZI_Company_Code_XX</SPAN></P><P><SPAN>2.&nbsp;association _CompanyCode { create; with draft; } in root behavior&nbsp;ZI_PROC_PROJ_XX</SPAN></P><P><SPAN>3. draft table&nbsp;zprocproj_ccd created using quick fix.</SPAN></P><P>&nbsp;</P><P><SPAN>5. Create a projection view ZC_PROC_CC_XX for interface view ZI_Company_Code_XX&nbsp; created in step 2</SPAN></P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Procement Project CC Projection View' @Metadata.ignorePropagatedAnnotations: true define view entity ZC_PROC_CC_XX as projection on ZI_Company_Code_XX { key companycode, procurementprojectuuid, /* Associations */ _procproj : redirected to parent ZC_PROC_PROJ_XX }</code></pre><P><SPAN>Here "_procproj : redirected to parent ZC_PROC_PROJ_XX" relation is formed to root projection entity ZC_PROC_PROJ_XX</SPAN></P><P><SPAN>6. Create a relation from root project projection view&nbsp;ZC_PROC_PROJ_XX to&nbsp; company code project view zc_proc_cc_xx.</SPAN></P><pre class="lia-code-sample language-abap"><code>_CompanyCode : redirected to composition child zc_proc_cc_xx,</code></pre><P><SPAN>Make sure you activate ZC_PROC_PROJ_XX and zc_proc_cc_xx together when you form the business object relationship.</SPAN></P><P><SPAN><BR />7.&nbsp; Enhance projection Company code behavior ZC_PROC_CC_XX in projection</SPAN></P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); use draft; define behavior for ZC_PROC_PROJ_XX //alias &lt;alias_name&gt; implementation in class zcl_proc_proj_c_xx unique use etag { use create ( augment, precheck ); use update; use delete; use action Edit; use action Resume; use action Activate; use action Prepare; use action Discard; use action setactive; use association _plant { create; with draft; } use association _CompanyCode { create; with draft; } } define behavior for ZC_PROC_PLANT_XX //alias &lt;alias_name&gt; { use update; use delete; use association _project { with draft; } } define behavior for ZC_PROC_CC_XX { use update; use delete; use association _procproj { with draft; } }</code></pre><P>What additions are added in the above behavior:</P><P>1.&nbsp;<SPAN>define behavior for&nbsp;ZC_PROC_CC_XX</SPAN></P><P><SPAN>2. use association _CompanyCode { create; with draft; } in root behavior&nbsp;ZC_PROC_PROJ_XX</SPAN></P><P>&nbsp;</P><P>8. Expose company code projection view&nbsp;<SPAN>ZC_PROC_CC_XX service definition.</SPAN></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Service Binding for Procurement Project' define service ZSD_PROC_PROJ_XX { expose ZC_PROC_PROJ_XX as ProcurementProject; expose ZC_PROC_PLANT_XX as ProcurementPlant; expose ZC_PROC_CC_XX as ProcurementCC; }</code></pre><P>Here&nbsp;<SPAN>&nbsp;"expose ZC_PROC_CC_XX as ProcurementCC;" added.</SPAN></P><P>9. Enhance the metadata extension of root view&nbsp;<SPAN>ZC_PROC_PROJ_XX&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code> : { lineItem: [ { position: 70, importance: #HIGH, value: '_CompanyCode.companycode' } ] } .identification: [{ position: 70, importance: #HIGH, value: '_CompanyCode.companycode' }] _CompanyCode;</code></pre><P><SPAN>Here association&nbsp;_CompanyCode; is exposed in metadata extension and value property '_CompanyCode.companycode'&nbsp; is added to&nbsp;<a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.linetem and&nbsp;<a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.identification annotation.</SPAN></P><P><SPAN>10. Now preview the UI.</SPAN></P><P><SPAN>Now multi-input is enabled for <STRONG>company code</STRONG> field in Object page.</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_0-1745951665376.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/256337i9BA486F2D1D379F0/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_0-1745951665376.png" alt="pavankumar_reddy90_0-1745951665376.png" /></span></P><P>On save in display mode it appears like below:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_1-1745951839802.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/256338iCAEED2226D15828A/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_1-1745951839802.png" alt="pavankumar_reddy90_1-1745951839802.png" /></span></P><P>In list page field it displays like below:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pavankumar_reddy90_2-1745951925181.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/256339i103D3A625311214B/image-size/large?v=v2&amp;px=999" role="button" title="pavankumar_reddy90_2-1745951925181.png" alt="pavankumar_reddy90_2-1745951925181.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2025-04-29T20:46:31.881000+02:00 https://community.sap.com/t5/application-development-and-automation-blog-posts/introduction-to-privilegedaccess-annotation-in-rap-with-real-time-scenario/ba-p/14090149 Introduction to @PrivilegedAccess Annotation in RAP with Real-Time Scenario 2025-05-05T10:37:47.676000+02:00 Ravikumar_H https://community.sap.com/t5/user/viewprofilepage/user-id/1386926 <P><SPAN><STRONG>1.Introduction</STRONG><BR />In </SPAN><STRONG><SPAN>SAP ABAP RESTful Application Programming Model (RAP)</SPAN></STRONG><SPAN>, access control is crucial for ensuring </SPAN><STRONG><SPAN>data security and controlled access</SPAN></STRONG><SPAN> to business operations. While </SPAN><STRONG><SPAN>regular users</SPAN></STRONG><SPAN> follow standard authorization rules, certain </SPAN><STRONG><SPAN>privileged users (e.g., Admins, Managers)</SPAN></STRONG><SPAN> may require </SPAN><STRONG><SPAN>special permissions</SPAN></STRONG><SPAN> to override these rules in exceptional scenarios.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>To achieve this, SAP provides the </SPAN><STRONG><SPAN>@PrivilegedAccess</SPAN></STRONG><SPAN> annotation, which allows specific actions to be executed only by users with special permissions.</SPAN></P><P><SPAN>2.<STRONG>Why&nbsp;</STRONG></SPAN><STRONG><SPAN>Use @PrivilegedAccess?</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><STRONG><SPAN>Allows special access</SPAN></STRONG><SPAN> to privileged users for actions that normal users cannot perform.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Enhances security</SPAN></STRONG><SPAN> by keeping sensitive operations restricted to authorized personnel.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Provides flexibility</SPAN></STRONG><SPAN> in business scenarios where exceptions or urgent modifications are required.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Ensures compliance</SPAN></STRONG><SPAN> with business rules while allowing controlled overrides.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>3.Real-Time Scenario</SPAN></STRONG></P><P><SPAN>&nbsp;</SPAN><STRONG><SPAN>Use Case: Purchase Order (PO) Approval System</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>A company has a </SPAN><STRONG><SPAN>Purchase Order (PO) approval system</SPAN></STRONG><SPAN> with the following requirements:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><STRONG><SPAN>Regular Users</SPAN></STRONG><SPAN> can only </SPAN><STRONG><SPAN>submit POs</SPAN></STRONG><SPAN> for approval.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Managers (Privileged Users)</SPAN></STRONG><SPAN> should be able to </SPAN><STRONG><SPAN>override approvals</SPAN></STRONG><SPAN> in case of business urgency.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>Using </SPAN><SPAN>@PrivilegedAccess</SPAN><SPAN>, we ensure that only managers can </SPAN><STRONG><SPAN>approve or override PO approvals</SPAN></STRONG><SPAN> while regular users </SPAN><STRONG><SPAN>cannot</SPAN></STRONG><SPAN> bypass the approval process.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;<STRONG>4.Implementation:&nbsp;<SPAN class=""><SPAN class=""> Step-by-Step Guide with Code and Explanation</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></SPAN></P><P><STRONG><SPAN>Step 1: Create a CDS Entity for Purchase Orders</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>We define a </SPAN><STRONG><SPAN>CDS view entity</SPAN></STRONG><SPAN> that represents the </SPAN><STRONG><SPAN>Purchase Order table</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-sql"><code>@EndUserText.label: 'Purchase Order' define root view entity ZI_PURCHASE_ORDER { key purchase_order_id : UUID; supplier : String(50); total_amount : Currency(10,2); status : String(20); // Values: 'PENDING', 'APPROVED', 'REJECTED' } </code></pre><P><STRONG><SPAN>Explanation:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>This defines a table with attributes like </SPAN><STRONG><SPAN>supplier, total amount, and status</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>The </SPAN><SPAN>status</SPAN><SPAN> field stores the approval state (</SPAN><SPAN>PENDING</SPAN><SPAN>, </SPAN><SPAN>APPROVED</SPAN><SPAN>, </SPAN><SPAN>REJECTED</SPAN><SPAN>).</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>&nbsp;</SPAN><STRONG><SPAN>Step 2: Define the Behavior Definition with Privileged Access</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>We define </SPAN><STRONG><SPAN>two actions</SPAN></STRONG><SPAN>:</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>approve</SPAN><SPAN>: Available to </SPAN><STRONG><SPAN>regular users</SPAN></STRONG><SPAN> for normal approval.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>override_approval</SPAN><SPAN>: Available </SPAN><STRONG><SPAN>only to privileged users</SPAN></STRONG><SPAN> using </SPAN><SPAN>@PrivilegedAccess</SPAN><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><pre class="lia-code-sample language-sql"><code>managed implementation in class ZBP_PURCHASE_ORDER unique; strict ( 2 ); define behavior for ZI_PURCHASE_ORDER alias PurchaseOrder { create; update; delete; action approve; // Normal Approval Action @PrivilegedAccess action override_approval; // Only for privileged users } </code></pre><P><STRONG><SPAN>Explanation:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>The </SPAN><SPAN>approve</SPAN><SPAN> action is accessible to </SPAN><STRONG><SPAN>all authorized users</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>The </SPAN><SPAN>override_approval</SPAN><SPAN> action is </SPAN><STRONG><SPAN>restricted</SPAN></STRONG><SPAN> to </SPAN><STRONG><SPAN>privileged users only</SPAN></STRONG><SPAN> via </SPAN><SPAN>@PrivilegedAccess</SPAN><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>&nbsp;</SPAN><STRONG><SPAN>Step 3: Implement the Behavior in ABAP Class</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>The </SPAN><STRONG><SPAN>business logic</SPAN></STRONG><SPAN> for the </SPAN><SPAN>approve</SPAN><SPAN> and </SPAN><SPAN>override_approval</SPAN><SPAN> actions is written in the RAP behavior class.</SPAN><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>CLASS zbp_purchase_order DEFINITION PUBLIC FINAL FOR BEHAVIOR OF zi_purchase_order. PUBLIC SECTION. METHODS: approve FOR ACTION approve ON MODIFY, override_approval FOR ACTION override_approval ON MODIFY. ENDCLASS. CLASS zbp_purchase_order IMPLEMENTATION. METHOD approve. " Regular approval process MODIFY entities OF zi_purchase_order ENTITY purchaseorder SET status = 'APPROVED' WHERE purchase_order_id = _order_id. WRITE: / 'Purchase Order Approved Successfully'. ENDMETHOD. METHOD override_approval. " Only privileged users can execute this action MODIFY entities OF zi_purchase_order ENTITY purchaseorder SET status = 'FORCED_APPROVAL' WHERE purchase_order_id = _order_id. WRITE: / 'Purchase Order Approved with Privileged Access'. ENDMETHOD. ENDCLASS. </code></pre><P><STRONG><SPAN>Explanation:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>The </SPAN><SPAN>approve</SPAN><SPAN> method updates the </SPAN><STRONG><SPAN>status</SPAN></STRONG><SPAN> to </SPAN><SPAN>'APPROVED'</SPAN><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>The </SPAN><SPAN>override_approval</SPAN><SPAN> method updates the </SPAN><STRONG><SPAN>status</SPAN></STRONG><SPAN> to </SPAN><SPAN>'FORCED_APPROVAL'</SPAN><SPAN> but can be </SPAN><STRONG><SPAN>executed only by privileged users</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>The </SPAN><SPAN>@PrivilegedAccess</SPAN><SPAN> annotation </SPAN><STRONG><SPAN>prevents unauthorized users</SPAN></STRONG><SPAN> from executing </SPAN><SPAN>override_approval</SPAN><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>&nbsp;<STRONG>5.</STRONG></SPAN><STRONG><SPAN>Testing the Implementation with Output</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Test Case 1: Regular User Approves PO</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Input:</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>User clicks on </SPAN><STRONG><SPAN>Approve</SPAN></STRONG><SPAN> button in Fiori App.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Execution:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-abap"><code>CALL FUNCTION 'approve' EXPORTING purchase_order_id = '12345'. </code></pre><P><SPAN class=""><SPAN class="">Output in Database:</SPAN></SPAN></P><TABLE><TBODY><TR><TD><P><STRONG><SPAN>PO ID</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Supplier</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Total Amount</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Status</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD><P><SPAN>12345</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>ABC Ltd</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>5000</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>APPROVED</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR></TBODY></TABLE><P><SPAN><span class="lia-unicode-emoji" title=":white_heavy_check_mark:">✅</span></SPAN><STRONG><SPAN>Message:</SPAN></STRONG> <I><SPAN>"Purchase Order Approved Successfully"</SPAN></I><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Test Case 2: Privileged User Overrides Approval</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Input:</SPAN></STRONG><SPAN>&nbsp;<BR /></SPAN><SPAN>Manager clicks </SPAN><STRONG><SPAN>Override Approval</SPAN></STRONG><SPAN> in Fiori App.</SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Execution:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-sql"><code>CALL FUNCTION 'override_approval' EXPORTING purchase_order_id = '12345'.</code></pre><P><SPAN class=""><SPAN class="">Output in Database:</SPAN></SPAN></P><TABLE><TBODY><TR><TD><P><STRONG><SPAN>PO ID</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Supplier</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Total Amount</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Status</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD><P><SPAN>12345</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>ABC Ltd</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>5000</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>FORCED_APPROVAL</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR></TBODY></TABLE><P><SPAN><span class="lia-unicode-emoji" title=":white_heavy_check_mark:">✅</span></SPAN><STRONG><SPAN>Message:</SPAN></STRONG> <I><SPAN>"Purchase Order Approved with Privileged Access"</SPAN></I><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>6.Code Explanation: How It Works</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><TABLE><TBODY><TR><TD><P><STRONG><SPAN>Step</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Code Component</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD><P><STRONG><SPAN>Purpose</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD><P><SPAN>1</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>ZI_PURCHASE_ORDER</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>Defines the PO table structure.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD><P><SPAN>2</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>@PrivilegedAccess</SPAN><SPAN> in behavior definition</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>Restricts </SPAN><SPAN>override_approval</SPAN><SPAN> action to privileged users only.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD><P><SPAN>3</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>approve</SPAN><SPAN> method</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>Allows regular users to approve POs.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD><P><SPAN>4</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>override_approval</SPAN><SPAN> method</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>Allows only </SPAN><STRONG><SPAN>privileged users</SPAN></STRONG><SPAN> to approve POs in special cases.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD><P><SPAN>5</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>Fiori App Action Execution</SPAN><SPAN>&nbsp;</SPAN></P></TD><TD><P><SPAN>Users interact with the Fiori UI to trigger the actions.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR></TBODY></TABLE><P><SPAN><STRONG>7.</STRONG>&nbsp;</SPAN><STRONG><SPAN>Key Learnings</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><TABLE><TBODY><TR><TD width="196.083px" height="50px"><P><STRONG><SPAN>Feature</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD width="487.365px" height="50px"><P><STRONG><SPAN>Explanation</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD width="196.083px" height="50px"><P><STRONG><SPAN>@PrivilegedAccess</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD width="487.365px" height="50px"><P><SPAN>Grants special permissions to execute restricted actions.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD width="196.083px" height="50px"><P><STRONG><SPAN>Use Case</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD width="487.365px" height="50px"><P><SPAN>Allows Managers to override standard approval processes.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD width="196.083px" height="50px"><P><STRONG><SPAN>Security Considerations</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD width="487.365px" height="50px"><P><SPAN>Ensures only privileged users can perform critical actions.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR><TR><TD width="196.083px" height="50px"><P><STRONG><SPAN>Business Benefit</SPAN></STRONG><SPAN>&nbsp;</SPAN></P></TD><TD width="487.365px" height="50px"><P><SPAN>Provides flexibility while maintaining strict security and compliance.</SPAN><SPAN>&nbsp;</SPAN></P></TD></TR></TBODY></TABLE><P><SPAN>&nbsp;<STRONG>8.</STRONG></SPAN><STRONG><SPAN>Conclusion</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>The </SPAN><SPAN>@PrivilegedAccess</SPAN><SPAN> annotation in RAP </SPAN><STRONG><SPAN>enables secure and controlled access</SPAN></STRONG><SPAN> to business-critical actions while maintaining the standard approval process for regular users. It is useful for </SPAN><STRONG><SPAN>managing exceptions, handling administrative actions, and ensuring compliance with business rules</SPAN></STRONG><SPAN>.</SPAN><SPAN>&nbsp;</SPAN></P> 2025-05-05T10:37:47.676000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/part-2-local-handler-class-draft-functionality-and-attachment-handling/ba-p/14088218 Part 2: Local Handler Class, Draft Functionality, and Attachment Handling 2025-05-08T09:35:03.828000+02:00 Srivatsa https://community.sap.com/t5/user/viewprofilepage/user-id/1392861 <P><STRONG>Local Handler Class</STRONG></P><P>In an unmanaged RAP scenario, the local handler class is where you write the ABAP code to implement the behavior defined in the behavior definition. This includes implementing the standard CRUD (Create, Read, Update, Delete) operations and any custom actions.</P><UL><LI><P><STRONG>Role of the Local Handler Class:</STRONG></P><UL><LI>It acts as the "brain" of your RAP business object, containing the code that interacts with the database and enforces business rules.</LI><LI>It processes requests from the service layer and performs the necessary actions.</LI><LI>It handles data manipulation, validation, and authorization.</LI><LI><P><STRONG>Keys in Local Handler Class:</STRONG></P><UL><LI>Keys are crucial within the local handler class because they are used to identify specific instances of the business object.</LI><LI>For example, when updating a customer, the customer_id key is used to locate the correct customer record in the database.</LI><LI>When creating a new entity (like an attachment), the handler might need to generate a new unique key.</LI><LI>Key fields are often used in WHERE clauses in database operations to ensure that the correct data is being accessed or modified.</LI><LI><P><STRONG>Entities:</STRONG></P><UL><LI>Within the local handler class, the term "entities" refers to the data being processed—typically, a table of instances.</LI><LI>When creating new customers, the "entities" parameter would hold the data for the new customer records.</LI><LI>When updating customers, "entities" would contain the modified data for the customers being updated.</LI><LI>When deleting, "entities" would identify which customer instances to delete.</LI><LI><P><STRONG>CRUD Operation Examples:</STRONG></P><UL><LI><P><STRONG>Create:</STRONG></P><UL><LI>This method handles the creation of new customer records.</LI><LI>It iterates through the entities to be created.</LI><LI>It generates a new customer_id (if it's not provided).</LI><LI>It inserts the new customer data into the zztmfcustomer database table.</LI><LI>It handles error situations (e.g., if the database insertion fails).</LI><LI>It returns the created customer IDs with %cid (client ID) to the framework.</LI><LI><P><STRONG>Read:</STRONG></P><UL><LI>This method retrieves customer data based on the provided keys.</LI><LI>It iterates through the keys (which contain the customer_id).</LI><LI>It selects the customer data from the zztmfcustomer table using the customer_id in the WHERE clause.</LI><LI>It appends the retrieved data to the result.</LI><LI>It handles the case where a customer with the given ID is not found.</LI><LI><P><STRONG>Update:</STRONG></P><UL><LI>This method updates existing customer records.</LI><LI>It iterates through the entities containing the updated data.</LI><LI>It uses the MODIFY statement to update the corresponding record in the zztmfcustomer table, using the customer_id in the WHERE clause to identify the record.</LI><LI>It handles potential errors during the update.</LI><LI><P><STRONG>Delete:</STRONG></P><UL><LI>This method deletes customer records.</LI><LI>It iterates through the keys (containing the customer_id of the records to be deleted).</LI><LI>It deletes the records from the zztmfcustomer table using the customer_id in the WHERE clause.</LI><LI>It includes error handling.</LI></UL></LI></UL></LI></UL></LI></UL></LI></UL></LI></UL></LI></UL></LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code> METHODS create FOR MODIFY IMPORTING entities FOR CREATE Customer. METHODS modify FOR MODIFY IMPORTING entities FOR UPDATE Customer. METHODS delete FOR MODIFY IMPORTING keys FOR DELETE Customer. METHODS read FOR READ IMPORTING keys FOR READ Customer RESULT result.​</code></pre><P><STRONG>ASSOCIATION :</STRONG></P><P>The Associated Child entities should be created only from a Parent Entity, in the above they are specified as Root\_Associated Entity , however the other operations of the class can be handled directly by declaring a method.</P><pre class="lia-code-sample language-abap"><code>METHODS create_ContactMedium FOR MODIFY IMPORTING entities FOR CREATE Customer\_ContactMedium. METHODS create_CreditProfile FOR MODIFY IMPORTING entities FOR CREATE Customer\_CreditProfile. METHODS create_EventInput FOR MODIFY IMPORTING entities FOR CREATE Customer\_EventInput. METHODS create_EventSubscription FOR MODIFY IMPORTING entities FOR CREATE Customer\_EventSubscription. METHODS create_Characteristic FOR MODIFY IMPORTING entities FOR CREATE Customer\_Characteristic. METHODS create_PaymentMethod FOR MODIFY IMPORTING entities FOR CREATE Customer\_PaymentMethod. METHODS create_RelatedParty FOR MODIFY IMPORTING entities FOR CREATE Customer\_RelatedParty. METHODS create_TimePeriod FOR MODIFY IMPORTING entities FOR CREATE Customer\_TimePeriod</code></pre><P><STRONG>Custom Action: copy</STRONG></P><P>The behavior definition allows defining custom actions.</P><P>We defined a copy action for the Customer entity.</P><P>Why use it?</P><P>Efficiency: Quickly create similar records</P><P>Consistency: Maintain default values.</P><P>User Experience: Convenient for repetitive data entry.</P><P>Implementing the copy Action:&nbsp;Add a method in the local handler class to implement the copy logic.</P><pre class="lia-code-sample language-abap"><code>define behavior for ZC_TMF_Customer alias Customer authorization master ( instance ) draft table ZZTMFCUSTOMER_d etag master last_changed_at lock master total etag last_changed_at early numbering { // Root entity create; update; delete; action copy;</code></pre><P>Implement the Method logic:</P><pre class="lia-code-sample language-abap"><code>METHOD copy. DATA: lv_new_id TYPE zztmfcustomer-id, ls_customer TYPE zztmfcustomer, lt_accountref TYPE TABLE OF zztmfaccountref, lt_agreementre TYPE TABLE OF zztmfagreementre, lt_contactmedium TYPE TABLE OF zztmfcontactme, lt_creditprofile TYPE TABLE OF zztmfcreditpro, lt_eventinput TYPE TABLE OF zztmfeventinput, lt_eventsubscr TYPE TABLE OF zztmfeventsubscr, lt_characteris TYPE TABLE OF zztmfcharacteris, lt_paymentme TYPE TABLE OF zztmfpaymentme, lt_relatedpart TYPE TABLE OF zztmfrelatedpart, lt_timeperiod TYPE TABLE OF zztmftimeperiod. LOOP AT entities ASSIGNING FIELD-SYMBOL(&lt;ls_entity&gt;). " 1. Read original customer SELECT SINGLE * FROM zztmfcustomer WHERE id = @&lt;ls_entity&gt;-customer_id INTO _customer. IF sy-subrc &lt;&gt; 0. CONTINUE. ENDIF. " 2. Get new customer id SELECT MAX( id ) FROM zztmfcustomer INTO _new_id. lv_new_id = lv_new_id + 1. " 3. Insert new customer (copy, but new id) ls_customer-id = lv_new_id. INSERT zztmfcustomer FROM _customer. " 4. Copy AccountRef SELECT * FROM zztmfaccountref WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _accountref. LOOP AT lt_accountref ASSIGNING FIELD-SYMBOL(&lt;ls_acc&gt;). &lt;ls_acc&gt;-id = lv_new_id. ENDLOOP. IF lt_accountref IS NOT INITIAL. INSERT zztmfaccountref FROM TABLE _accountref. ENDIF. " 5. Copy AgreementRe SELECT * FROM zztmfagreementre WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _agreementre. LOOP AT lt_agreementre ASSIGNING FIELD-SYMBOL(&lt;ls_agr&gt;). &lt;ls_agr&gt;-id = lv_new_id. ENDLOOP. IF lt_agreementre IS NOT INITIAL. INSERT zztmfagreementre FROM TABLE _agreementre. ENDIF. " 6. Copy ContactMedium SELECT * FROM zztmfcontactme WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _contactmedium. LOOP AT lt_contactmedium ASSIGNING FIELD-SYMBOL(&lt;ls_con&gt;). &lt;ls_con&gt;-id = lv_new_id. ENDLOOP. IF lt_contactmedium IS NOT INITIAL. INSERT zztmfcontactme FROM TABLE _contactmedium. ENDIF. " 7. Copy CreditProfile SELECT * FROM zztmfcreditpro WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _creditprofile. LOOP AT lt_creditprofile ASSIGNING FIELD-SYMBOL(&lt;ls_cred&gt;). &lt;ls_cred&gt;-id = lv_new_id. ENDLOOP. IF lt_creditprofile IS NOT INITIAL. INSERT zztmfcreditpro FROM TABLE _creditprofile. ENDIF. " 8. Copy EventInput SELECT * FROM zztmfeventinput WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _eventinput. LOOP AT lt_eventinput ASSIGNING FIELD-SYMBOL(&lt;ls_evi&gt;). &lt;ls_evi&gt;-id = lv_new_id. ENDLOOP. IF lt_eventinput IS NOT INITIAL. INSERT zztmfeventinput FROM TABLE _eventinput. ENDIF. " 9. Copy EventSubscription SELECT * FROM zztmfeventsubscr WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _eventsubscr. LOOP AT lt_eventsubscr ASSIGNING FIELD-SYMBOL(&lt;ls_evs&gt;). &lt;ls_evs&gt;-id = lv_new_id. ENDLOOP. IF lt_eventsubscr IS NOT INITIAL. INSERT zztmfeventsubscr FROM TABLE _eventsubscr. ENDIF. " 10. Copy Characteristic SELECT * FROM zztmfcharacteris WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _characteris. LOOP AT lt_characteris ASSIGNING FIELD-SYMBOL(&lt;ls_char&gt;). &lt;ls_char&gt;-id = lv_new_id. ENDLOOP. IF lt_characteris IS NOT INITIAL. INSERT zztmfcharacteris FROM TABLE _characteris. ENDIF. " 11. Copy PaymentMethod SELECT * FROM zztmfpaymentme WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _paymentme. LOOP AT lt_paymentme ASSIGNING FIELD-SYMBOL(&lt;ls_pay&gt;). &lt;ls_pay&gt;-id = lv_new_id. ENDLOOP. IF lt_paymentme IS NOT INITIAL. INSERT zztmfpaymentme FROM TABLE _paymentme. ENDIF. " 12. Copy RelatedParty SELECT * FROM zztmfrelatedpart WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _relatedpart. LOOP AT lt_relatedpart ASSIGNING FIELD-SYMBOL(&lt;ls_rel&gt;). &lt;ls_rel&gt;-id = lv_new_id. ENDLOOP. IF lt_relatedpart IS NOT INITIAL. INSERT zztmfrelatedpart FROM TABLE _relatedpart. ENDIF. " 13. Copy TimePeriod SELECT * FROM zztmftimeperiod WHERE id = @&lt;ls_entity&gt;-customer_id INTO TABLE _timeperiod. LOOP AT lt_timeperiod ASSIGNING FIELD-SYMBOL(&lt;ls_tim&gt;). &lt;ls_tim&gt;-id = lv_new_id. ENDLOOP. IF lt_timeperiod IS NOT INITIAL. INSERT zztmftimeperiod FROM TABLE _timeperiod. ENDIF. " 14. Return new key APPEND VALUE #( %tky = VALUE #( customer_id = lv_new_id ) ) TO mapped-customer. ENDLOOP.</code></pre><P><STRONG>Draft Functionality</STRONG></P><P>Draft functionality enhances the user experience by allowing users to create or modify business objects without immediately saving the changes to the database. This is particularly useful for complex objects or when multiple steps are involved in creating or editing data.</P><P><STRONG>Draft Lifecycle:</STRONG></P><UL><LI><STRONG>Edit: The user starts editing an existing object or creates a new one, and the data is saved to a draft.</STRONG></LI><LI><STRONG><STRONG>Activate: The user confirms the changes, and the draft data is saved to the active database table.</STRONG></STRONG></LI><LI><STRONG><STRONG><STRONG>Discard: The user cancels the changes, and the draft data is deleted.</STRONG></STRONG></STRONG></LI><LI><STRONG><STRONG><STRONG><STRONG>Resume: The user returns to a previously started draft to continue editing.</STRONG></STRONG></STRONG></STRONG></LI><LI><P><STRONG>Draft Implementation Examples:</STRONG></P><UL><LI><P><STRONG>Activate:</STRONG></P><UL><LI>This method activates a draft, saving the changes to the active table.</LI><LI>It reads the draft data from the zztmfcustomer_d (draft table).</LI><LI>It copies the data to a structure (ls_customer).</LI><LI>It updates the zztmfcustomer (active table) with the data.</LI><LI>It deletes the draft record from zztmfcustomer_d.</LI><LI>It includes error handling for database operations.</LI><LI><P><STRONG>Discard:</STRONG></P><UL><LI>This method discards a draft, deleting the draft data.</LI><LI>It deletes the draft record from the zztmfcustomer_d table.</LI><LI>It includes error handling.</LI><LI>Draft functions is handled automatically in the RAP framework, all you need to is create a table like below and specify it in Behaviour Definition.</LI></UL></LI></UL></LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Table to Store Customer details' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zztmfcustomer_d { key customer_id : char10 not null; link : char100; name : char20 not null; status : char20; statusreason : char30; validfor : datab; last_changed_at : timestampl; locallastchangedat : timestampl; "%admin" : include sych_bdl_draft_admin_inc; } ​</code></pre><P><STRONG>ATTACHMENT HANDLING in RAP</STRONG></P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Customer Attachment' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zztmcustatt { key attach_id : char10 not null; key customer_id : char10 not null; file_name : char100 not null; file_type : char20 not null; file_content : abap.rawstring(999999); created_on : timestampl; last_changed_at : timestampl; } METHOD create_Attachment. DATA: lt_attach TYPE TABLE OF zztmcustatt, lv_max_id TYPE zztmcustatt-attach_id. * " Determine next attachment id * LOOP AT entities ASSIGNING FIELD-SYMBOL(&lt;ls_entity&gt;). DATA(lt_target) = &lt;ls_entity&gt;-%target. SELECT MAX( attach_id ) FROM zztmcustatt where customer_id = @&lt;ls_entity&gt;-customer_id INTO _max_id. IF sy-subrc = 0. " Convert lv_max_id to a number, increment lv_max_id = lv_max_id + 1. ELSE. lv_max_id = '0000000001'. " initial value, formatted as needed ENDIF. LOOP AT lt_target ASSIGNING FIELD-SYMBOL(&lt;ls_target&gt;). APPEND VALUE #( attach_id = lv_max_id customer_id = &lt;ls_entity&gt;-customer_id " Parent key from the Customer file_name = &lt;ls_target&gt;-FileName file_type = &lt;ls_target&gt;-FileType file_content = &lt;ls_target&gt;-Attachment created_on = sy-datum &amp;&amp; sy-uzeit " or use GET TIME STAMP ) TO lt_attach. " Increment attachment key (format as needed) lv_max_id = lv_max_id + 1. ENDLOOP. ENDLOOP. IF lt_attach IS NOT INITIAL. INSERT zztmcustatt FROM TABLE _attach. " Map created attachments back LOOP AT lt_attach ASSIGNING FIELD-SYMBOL(&lt;ls_att&gt;). APPEND VALUE #( %tky = VALUE #( AttachId = &lt;ls_att&gt;-attach_id ) ) TO mapped-Attachment. ENDLOOP. ENDIF. ENDMETHOD.</code></pre><P>&nbsp;Create a Service Definition and Service Binding to test the application using FIORI ELEMENTS.</P><UL><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srivatsa_5-1745836461754.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255468i7A8C65E39999017A/image-size/medium?v=v2&amp;px=400" role="button" title="Srivatsa_5-1745836461754.png" alt="Srivatsa_5-1745836461754.png" /></span></LI><LI><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srivatsa_8-1745837073298.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/255479i77AA408E944A358E/image-size/medium?v=v2&amp;px=400" role="button" title="Srivatsa_8-1745837073298.png" alt="Srivatsa_8-1745837073298.png" /></span><P>Creation of New Record with Draft Functionality &amp; Attachment</P><BR /><P>&nbsp;</P><UL><LI>The Records is not Created but its in Draft Status<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srivatsa_2-1746443022000.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/257626i2083634092BBA06C/image-size/medium?v=v2&amp;px=400" role="button" title="Srivatsa_2-1746443022000.png" alt="Srivatsa_2-1746443022000.png" /></span><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srivatsa_3-1746443021976.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/257628i99CB057FBCF3C3CE/image-size/medium?v=v2&amp;px=400" role="button" title="Srivatsa_3-1746443021976.png" alt="Srivatsa_3-1746443021976.png" /></span></LI><LI><P>A new Attachment is uploaded and Created</P><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Srivatsa_4-1746443022086.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/257629iE2C267DAF0BA481D/image-size/medium?v=v2&amp;px=400" role="button" title="Srivatsa_4-1746443022086.png" alt="Srivatsa_4-1746443022086.png" /></span><P>&nbsp;</P><P>&nbsp;</P></LI></UL></LI></UL><P><STRONG>Conclusion</STRONG></P><P>In Part 2, we covered the implementation details of our Customer Management application. We explored the role of the local handler class in processing business logic, implemented draft functionality to enhance the user experience, and enabled attachment handling to manage files.</P><P>This two-part series provided a comprehensive guide to building a Customer Management application with unmanaged RAP. By understanding the concepts and examples presented here, you can create your own robust and user-friendly RAP applications. Remember to adapt the code and techniques to the specific requirements of your projects.</P><P>&nbsp;</P><P>With this you can integrate your RAP application with Process Build Automation for Approval. A workflow handler class should be called in the determination of RAP to call the created Workflow.&nbsp;</P><P>As of now, SAP BTP Trial has not provided integration part for free, it can be done only in Licensed mode.</P> 2025-05-08T09:35:03.828000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/sap-segw-vs-rap-vs-cap-making-the-most-of-function-imports-actions-and/ba-p/14093471 SAP SEGW vs RAP vs CAP: Making the most of Function Imports, Actions and Functions (Part 1) 2025-05-08T09:35:18.963000+02:00 valentincadart https://community.sap.com/t5/user/viewprofilepage/user-id/786198 <P><SPAN>After working on several SAP projects, I have noticed a common trend consisting in a plethora of SEGW projects full of function imports that are generally misused or unnecessary. With my experience—and after delving into the SAP documentation—I have decided to write this blog to shed some light on function imports. I will explain what they are, what they are used for, when to use them and when to avoid using them, and will do so not only for SAP SEGW projects, but also for their equivalents within the more recent SAP RAP and CAP frameworks—since I am actively diving into these modern development approaches and working on business projects that leverage them. To make this more concrete, I’ll walk through a practical example, which I’ve made available on GitHub.</SPAN></P><P><SPAN>Let's clear up the confusion together. So, grab your coffee, and let’s dive in !&nbsp;</SPAN></P><UL><LI>Part 1. SEGW Context</LI><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-segw-vs-rap-vs-cap-making-the-most-of-function-imports-actions-and/ba-p/14095075" target="_self">Part 2. SAP RAP and SAP CAP + github project</A></LI></UL><P>&nbsp;</P><H2 id="toc-hId-1710131271"><STRONG>SAP SEGW Context</STRONG></H2><P><SPAN>A function import in OData is a way to define and call custom operations in one’s service that goes beyond the standard data handling actions—like creating, reading, updating, or deleting records (CRUD). These standard actions are typically handled through the HTTP methods POST, GET, PUT/MERGE, and DELETE.</SPAN></P><P><SPAN>However, if you need to perform a more specialized task that does not neatly fit into the standard CRUD operations, you can create a function import in your data model using Service Builder. These custom operations can then be triggered using either the GET or POST HTTP methods.</SPAN></P><P><SPAN>But first, a small word of caution:&nbsp; these specific function imports are only to be used for actions that cannot be done with standard traditional CRUD operations. If the task can be handled through regular create, read, update, or delete, then a function import is not required.&nbsp;</SPAN></P><P><SPAN>Let’s look at an example to understand.</SPAN></P><P>&nbsp;</P><H3 id="toc-hId-1642700485"><SPAN><STRONG>Example : Flight management system with CRUD and custom operations</STRONG></SPAN></H3><P><U>Core CRUD operations :&nbsp;</U></P><P><I><SPAN>These operations cover the basic data management needs for flight records</SPAN></I></P><UL><LI><SPAN>Create: Add new flight (e.g., POST /flight with origin, destination, departure, arrival..).</SPAN></LI><LI><SPAN>Read: Retrieve existing flight (e.g., GET /flight/{id}).</SPAN></LI><LI><SPAN>Update: Modify flight (e.g., PUT /flight/{id} to adjust departure time).</SPAN></LI><LI><SPAN>Delete: Remove canceled flight (e.g., DELETE /flight/{id}).</SPAN></LI></UL><P><U>Custom operations :&nbsp;</U></P><P><I><SPAN>These are business-specific operations that go beyond standard CRUD</SPAN></I></P><UL><LI><SPAN>Print details: Generate and print comprehensive flight information (e.g., schedule, aircraft, and seating).</SPAN></LI><LI><SPAN>Generate report: Create operational or statistical reports for flights over a specified date range.</SPAN></LI><LI><SPAN>…</SPAN></LI></UL><P><SPAN>Custom operations are separated from CRUD because they encapsulate domain-specific behavior that cannot be addressed by simple data manipulation alone. While CRUD operations focus on creating, reading, updating, or deleting records, custom actions often perform calculations, trigger workflows, or apply business rules across multiple entities.&nbsp;</SPAN></P><P><SPAN>This distinction supports a cleaner and more maintainable architecture—where CRUD handles core data persistence, and custom operations handle the more nuanced logic of the application domain.</SPAN></P><P>&nbsp;</P><H3 id="toc-hId-1446186980"><STRONG>Implementation example&nbsp;</STRONG></H3><P><SPAN>Below is an implementation example of how to call a function import defined in SEGW.&nbsp;</SPAN></P><P><SPAN>On the front-end, the function import is triggered via an OData request using callFunction(), for example: /sap/opu/odata/SERVICE_NAME/FunctionImportName(...).&nbsp;</SPAN></P><P><SPAN>On the back-end, it is handled by redefining the EXECUTE_ACTION method in the *_DPC_EXT class. Inside this method, the iv_action_name parameter is used to identify the specific function being called. Input parameters are accessed through the it_parameter table, and the corresponding backend logic is implemented accordingly.</SPAN></P><P><SPAN>Front-end code: </SPAN></P><pre class="lia-code-sample language-javascript"><code>let oModel = this.getView().getModel(); let mParameters = { FlightID: "12345" }; oModel.callFunction("/PrintFlightDetails", { method: "POST", urlParameters: mParameters, success: function(oData, response) { MessageToast.show(oData.Message); }, error: function(oError) { MessageToast.show("Error printing flight details"); } });</code></pre><P><SPAN>Back-end code :&nbsp;&nbsp;</SPAN><I><SPAN>(Assuming we have previously defined the function import in the SEGW project and an ABAP OO class to manage the business object Flight)</SPAN></I></P><pre class="lia-code-sample language-abap"><code>METHOD execute_action. " Check if the action name is 'PrintFlightDetails' IF iv_action_name = 'PrintFlightDetails'. " Retrieve input parameter READ TABLE it_parameter INTO DATA(ls_parameter) WITH KEY name = 'FlightID'. IF sy-subrc = 0. DATA(lv_flightid) = ls_parameter-value. ELSE. " Manage error ENDIF. " Instantiate the flight manager class DATA(lo_flight) = NEW zcl_flight( lv_flightid ). " Call the method to update the flight status DATA(lv_message) = lo_flight-&gt;print_flight_details( ). ... ENDIF. ENDMETHOD.</code></pre><H2 id="toc-hId-1120590756">&nbsp;</H2><H2 id="toc-hId-924077251"><SPAN><STRONG>Conclusion</STRONG></SPAN></H2><P><SPAN>Function imports are a powerful tool, but like any tool, they must be used wisely. In SEGW, it is best to reserve them for operations that do not fit naturally into the CRUD. Misuse can lead to bloated, unclear services and unnecessary complexity.</SPAN></P><P><SPAN>In the next part of this series, I'll explore how similar use cases are handled in RAP and CAP - with a GitHub project to illustrate it all in action.</SPAN></P><P><SPAN>Stay tuned!</SPAN></P> 2025-05-08T09:35:18.963000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/sap-segw-vs-rap-vs-cap-making-the-most-of-function-imports-actions-and/ba-p/14095075 SAP SEGW vs RAP vs CAP: Making the most of Function Imports, Actions and Functions (Part 2) 2025-05-08T09:36:59.881000+02:00 valentincadart https://community.sap.com/t5/user/viewprofilepage/user-id/786198 <P>After working on several SAP projects, I have noticed a common trend consisting in a plethora of SEGW projects full of function imports that are generally misused or unnecessary. With my experience—and after delving into the SAP documentation—I have decided to write this blog to shed some light on function imports. I will explain what they are, what they are used for, when to use them and when to avoid using them, and will do so not only for SAP SEGW projects, but also for their equivalents within the more recent SAP RAP and CAP frameworks—since I am actively diving into these modern development approaches and working on business projects that leverage them. To make this more concrete, I’ll walk through a practical example, which I’ve made available on GitHub.</P><P>Let's clear up the confusion together. So, grab your coffee, and let’s dive in !</P><UL><LI><A href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-segw-vs-rap-vs-cap-making-the-most-of-function-imports-actions-and/ba-p/14093471" target="_self">Part 1. SEGW Context</A></LI><LI>Part 2. SAP RAP and SAP CAP + github project</LI></UL><P>&nbsp;</P><H2 id="toc-hId-1710187013">SAP’s new frameworks</H2><P><SPAN>SAP RAP (RESTful ABAP Programming Model) and SAP CAP (Cloud Application Programming Model) are the two modern frameworks from SAP designed for building applications.&nbsp;</SPAN></P><P><SPAN>Both frameworks support extending standard CRUD operations by allowing developers to define custom logic—commonly known as actions or functions. In RAP, these are introduced in behavior definitions, while in CAP, they're modeled in the service layer. This functional similarity makes it natural to discuss them together.</SPAN></P><P><SPAN>As a quick note for context: this blog won’t delve into the differences between static and instance-based implementations, bound and unbound actions and functions.. as those topics are beyond the scope of this post&nbsp; — although they might be the subject of a future blog post.</SPAN></P><P>&nbsp;</P><H3 id="toc-hId-1642756227">SAP RAP Context</H3><P><SPAN>SAP RAP is SAP’s modern framework for developing OData services and business applications using ABAP. It serves the same core purpose as SAP SEGW (Service Gateway), which is to expose data and operations from SAP systems via OData.&nbsp;</SPAN></P><P><SPAN>Unlike SEGW, which relies heavily on manual coding and separate models, RAP uses a streamlined, model-driven approach with better integration into the ABAP RESTful environment, leveraging CDS (Core Data Services) to define and manage the data models in a more declarative way.</SPAN></P><H3 id="toc-hId-1446242722">&nbsp;</H3><H3 id="toc-hId-1249729217">SAP CAP Context</H3><P><SPAN>SAP CAP is SAP’s modern development framework for building services and applications, particularly optimized for the SAP Business Technology Platform (BTP). It provides a streamlined, full-stack development experience with built-in support for defining data models, service APIs, and custom logic using a combination of Core Data Services (CDS), Node.js, or Java.</SPAN></P><P><SPAN>While SAP RAP is more ABAP-centric and suited for on-premise or S/4HANA systems, CAP is cloud-native and ideal for developing applications in the BTP environment.</SPAN></P><P><SPAN>Unlike traditional approaches like SEGW or even ABAP RAP, which are deeply rooted in the ABAP stack, CAP is open and service-oriented by design. It promotes strong integration with SAP HANA, SAP Fiori, and external services through open standards like OData and REST.</SPAN></P><H4 id="toc-hId-1182298431">&nbsp;</H4><H4 id="toc-hId-985784926">Standard vs Non-Standard Operations in RAP and CAP</H4><P><SPAN>Both SAP RAP and SAP CAP distinguish between standard and non-standard OData operations. Standard operations refer to the CRUD functionalities, which are automatically handled by the frameworks. In addition to CRUD, both frameworks also manage transactional behavior, draft handling, validation, and persistence with minimal configuration.&nbsp;</SPAN></P><P><SPAN>Non-standard operations, on the other hand, are used to implement custom business logic beyond CRUD, following the same conceptual model as Function Imports in the classical SEGW approach.&nbsp;</SPAN></P><H4 id="toc-hId-789271421">&nbsp;</H4><H4 id="toc-hId-592757916">Actions and Functions in RAP and CAP</H4><P><SPAN>In both RAP and CAP, Actions and Functions represent non-standard operations that integrate custom logic into the application. Actions are used for logic that may change data or cause side effects while Functions are read-only operations that return data.</SPAN></P><P><SPAN>In RAP, these are defined in the behavior definition and implemented in the behavior pool class, ensuring proper transactional and authorization handling.&nbsp;</SPAN></P><P><SPAN>In CAP, they are defined in the CDS service definition, and the implementation resides in service handlers (Node.js or Java).&nbsp;</SPAN></P><P><SPAN>Both frameworks ensure these operations are fully OData V4-compliant and seamlessly consumable by Fiori Elements or other OData clients.</SPAN></P><H3 id="toc-hId-267161692">&nbsp;</H3><H3 id="toc-hId-70648187">Fundamental difference between SEGW and SAP RAP/CAP</H3><P><SPAN>SAP SEGW (SAP Gateway Service Builder) is based on a flexible approach for managing CRUD operations. When creating or updating a service, the framework generates standard methods such as CREATE_ENTITY, UPDATE_ENTITY, DELETE_ENTITY, etc., which developers can redefine to incorporate more complex business logic tailored to functional requirements.</SPAN></P><P><SPAN>When a batch call is made from the front end, for example via a SubmitChanges operation, the grouped operations (creating entry C, updating entry A, deleting entry B, etc.) are dispatched individually in the backend. In other words, each operation is handled separately by the corresponding method (CREATE_ENTITY, UPDATE_ENTITY, etc.).</SPAN></P><P><SPAN>However, this approach introduces certain limitations, particularly in error handling within batch processing. Since each operation is processed independently, managing scenarios involving partial rollbacks or consistent error propagation across the batch becomes more complex.&nbsp;</SPAN></P><P><SPAN>SAP RAP and SAP CAP adopt a structured, model-based approach for managing data and business logic. Both rely on OData v4 and Core Data Services (CDS) to define entities, their relationships, and their behaviors.</SPAN></P><P><SPAN>CRUD operations such as CREATE and UPDATE are defined at the entity level and are designed to be handled individually, meaning they are executed on a single instance at a time. To manage more complex processing or scenarios involving multiple entities, RAP and CAP recommend the use of actions or functions, which allow business logic to be encapsulated without being directly embedded in the standard operations.</SPAN></P><P><SPAN>However, if no specific logic is required, it is still possible to perform batch processing (bulk update) using standard CRUD operations. That said, this approach is not recommended, as CRUD operations are intended for single-record processing, with validations, lifecycle hooks (such as before, after, check, etc.), and line-by-line error handling. Using batch processing via CRUD can lead to inconsistent behavior, difficult-to-control partial rollbacks, and a loss of control over overall business and transactional logic.</SPAN></P><P><SPAN>In addition, using actions offers many other advantages, such as:</SPAN></P><UL><LI><SPAN>Automatic interface generation: Actions are automatically exposed as buttons or menu items in Fiori Elements interfaces. They can be triggered on multiple selections (multi-select) with consistent UX behavior, and they allow for explicit naming of specific business processes (e.g., "Validate All", "Send Invoices").</SPAN></LI><LI><SPAN>Precise control over business logic: An action encapsulates business logic clearly, separate from generic CRUD operations. You can easily include custom validations, complex rules, calls to other services, and more.</SPAN></LI><LI><SPAN>Robust transaction and error handling: Actions enable centralized exception management, the return of structured user messages, and support for global rollbacks when needed.</SPAN></LI><LI><SPAN>...</SPAN></LI></UL><P><SPAN>A core principle in RAP and CAP is the clear separation between basic CRUD operations and business logic. This distinction enhances data consistency, code readability, and, most importantly, long-term maintainability of applications. Each action represents a clear business intent, integrated within a well-defined service structure.</SPAN></P><P><SPAN>In summary, SAP SEGW offers greater implementation freedom, which can be suitable for simple use cases with minimal business logic. However, it lacks a structured framework for handling complex scenarios or bulk processing. In contrast, SAP RAP and CAP enforce a more rigorous architecture, ensuring more reliable and scalable application management—both on the backend and frontend.</SPAN></P><P><SPAN>Now that we've covered the theory, let's explore some practical examples to see how this approach is implemented in these modern frameworks.</SPAN></P><H3 id="toc-hId--201096687">&nbsp;</H3><H3 id="toc-hId--397610192"><SPAN><STRONG>RAP implementation example</STRONG></SPAN></H3><P><SPAN>Below is an implementation example of how to call an action and a static function defined in SAP RAP.&nbsp;</SPAN></P><P><SPAN>In the front-end (e.g., Fiori Elements), the action setStatusToCanceled is triggered via the UI using the OData POST call to /EntitySet(...)/setStatusToCanceled. This is handled in the back-end by redefining the setStatusToCanceled method in the behavior implementation class (lhc_ZFM_C_FLIGHTS), where the logic modifies the entity’s status.&nbsp;</SPAN></P><P><SPAN>For the static function GetNumberOfDelayedFlights, it is triggered by a GET call like /EntitySet/GetNumberOfDelayedFlights, and its logic is implemented in the getnumberofdelayedflights method, returning a result structure with the computed data.</SPAN></P><P><I><SPAN>Flight Root Entity CDS View</SPAN></I></P><pre class="lia-code-sample language-abap"><code>define root view entity ZFM_C_FLIGHTS as select from ZFM_I_FLIGHTS { key Flight, Origin, Destination, Departure, Arrival, Delay, .lineItem: [{ position: 70 }, { type: #FOR_ACTION, dataAction: 'setStatusToCanceled' , label: 'Cancel flight'}] Status }</code></pre><P><I><SPAN>Behavior Definition</SPAN></I></P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_fm_c_flights unique; strict ( 2 ); define behavior for ZFM_C_FLIGHTS persistent table zfm_flights lock master authorization master ( instance ) { create; update; delete; field ( numbering : managed, readonly ) Flight; action setStatusToCanceled result [1] $self; static function GetNumberOfDelayedFlights result [1] ZD_GETDELAYEDFLIGHTS_RESULT; mapping for zfm_flights { Flight = flight_id; Origin = origin_code; Destination = destination_code; Departure = departure; Arrival = arrival; Delay = delay; Status = status_code; } }</code></pre><P><I><SPAN>Behavior Projection</SPAN></I></P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); define behavior for ZFM_P_FLIGHTS { use create; use update; use delete; use action setStatusToCanceled; use function GetNumberOfDelayedFlights; }</code></pre><P><I><SPAN>Behavior Implementation Class (ABAP)</SPAN></I></P><pre class="lia-code-sample language-abap"><code>CLASS lhc_ZFM_C_FLIGHTS DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zfm_c_flights RESULT result. METHODS setstatustocanceled FOR MODIFY IMPORTING keys FOR ACTION zfm_c_flights~setstatustocanceled RESULT result. METHODS getnumberofdelayedflights FOR READ IMPORTING keys FOR FUNCTION zfm_c_flights~getnumberofdelayedflights RESULT result. ENDCLASS. CLASS lhc_ZFM_C_FLIGHTS IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD setStatusToCanceled. MODIFY ENTITIES OF zfm_c_flights IN LOCAL MODE ENTITY zfm_c_flights UPDATE FROM VALUE #( FOR key IN keys ( Flight = key-Flight Status = 'CAN' " Cancelled %control-Status = if_abap_behv=&gt;mk-on ) ) FAILED failed REPORTED reported. "Read changed data for action result READ ENTITIES OF zfm_c_flights IN LOCAL MODE ENTITY zfm_c_flights ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(flights). result = VALUE #( FOR flight IN flights ( %tky = flight-%tky %param = flight ) ). ENDMETHOD. METHOD getNumberOfDelayedFlights. DATA: ls_delayedflights TYPE zd_getdelayedflights_result. " Read all flights where status_code = 'CAN' SELECT COUNT( * ) FROM zfm_flights WHERE delay &gt; 0 INTO _delayedflights . " Count how many were found APPEND VALUE #( %param = CORRESPONDING #( ls_delayedflights ) ) TO result. ENDMETHOD. ENDCLASS.</code></pre><H3 id="toc-hId--594123697">&nbsp;</H3><H3 id="toc-hId--790637202">CAP implementation example</H3><P><SPAN>If you’ve made it this far, it means you’re really interested in the topic — great!</SPAN></P><P><SPAN>To illustrate the fundamental differences between SEGW and CAP/RAP, I have implemented a more comprehensive CAP project. In this implementation, the use of an action instead of a batch update clearly proves its value through its seamless integration with Fiori Elements, as well as its ability to encapsulate business logic and handle errors effectively during mass processing.</SPAN></P><P><SPAN>To make things a bit more fun, here are three example use cases that I implemented in the following CAP example. Try to guess for each one whether it should be handled with a function or an action. The detailed answers are provided just below the implementation — but give it a try first!</SPAN></P><OL><LI><SPAN>Add delay to a flight – This updates the flight's delay and adjusts the arrival and/or departure time depending on the flight status.</SPAN><SPAN><BR /><BR /></SPAN></LI><LI><SPAN>Add delay to a list of flights – Same rules apply, but now you're looping through several flights, recalculating times, updating them, and optionally showing success or warning messages.</SPAN><SPAN><BR /><BR /></SPAN></LI><LI><SPAN>Retrieve the number of delayed flights – returns how many flights are currently delayed.</SPAN></LI></OL><P><I><SPAN>CDS Service Definition (srv/service.cds)</SPAN></I></P><pre class="lia-code-sample language-javascript"><code>using { flightManagement as fm } from '../db/schema'; service FlightsService @(path: '/flights') { .draft.enabled entity Flights as projection on fm.Flights actions { action addDelay( delay: Integer @label: '{i18n&gt;addDelayAction_delayParameter}' ) returns Flights; }; function getNbOfDelayedFlights() returns Integer; }</code></pre><P><I><SPAN>Service Implementation (srv/service.js)</SPAN></I></P><pre class="lia-code-sample language-javascript"><code>const cds = require('@sap/cds'); const TextBundle = require('@sap/textbundle').TextBundle; module.exports = cds.service.impl(srv =&gt; { const { Flights } = srv.entities; // Validate and adjust flight data before update srv.before('UPDATE', 'Flights.drafts', async (req) =&gt; { // Recalculate departure/arrival if delay is updated if ('delay' in req.data) { try { let { departure, arrival } = await this._recalculate_departure_arrival(req.data.ID, req.data.delay); if (departure === undefined &amp;&amp; arrival === undefined) { req.reject(400, 'ERR_UPDATING_CAN_OR_LAN', 'delay'); } req.data.departure = departure; req.data.arrival = arrival; } catch (error) { req.reject(400, 'ERR_UPDATING_DELAY', 'delay'); } } }); srv.on('addDelay', async (req) =&gt; { const locale = req.locale; const bundle = new TextBundle('./i18n/i18n', locale); // Loop through all flights for (let flight of req.params) { let { departure, arrival } = await this._recalculate_departure_arrival(flight.ID, req.data.delay); if (departure === undefined &amp;&amp; arrival === undefined) { // If calculation failed, send a failure notification for this flight req.warn({ message: bundle.getText('WARNING_UPDATING_DELAY_CAN_OR_LAN', [flight.ID]) }) continue; // Skip updating this flight } // Update the flight with the new delay, departure, and arrival times const result = await UPDATE(Flights) .where({ ID: flight.ID }) .set({ delay: req.data.delay, departure, arrival }); if (result === 1) { // Success: 1 row was affected req.notify({ code: 1, message: bundle.getText('SUCCESS_UPDATING_DELAY', [flight.ID, req.data.delay, departure, arrival]) }); } else { // Failure: no rows were updated req.error({ message: bundle.getText('FAILURE_UPDATING_DELAY', [flight.ID]) }); } } }); // Function: Return the number of delayed flights srv.on('getNbOfDelayedFlights', async () =&gt; { const result = await SELECT `count(*) as count` .from(Flights) .where `delay &gt; 0`; return (Array.isArray(result) &amp;&amp; result.length &gt; 0) ? result[0].count : 0; }); };</code></pre><P><SPAN>You’ve probably already figured out the answers by looking at the implementation, but let me walk you through the reasoning behind each choice:</SPAN></P><P><SPAN>For the first use case, “Add delay to a flight”, this one was a bit of a trick — it can be neatly handled within a before UPDATE hook, making it a natural candidate for standard CRUD logic.</SPAN></P><P><SPAN>In contrast, the action “Add delay to a list of flights” introduces more complexity. Since it involves applying business logic across multiple records as well as front-end integration, it is best implemented as a custom action. This approach also enables the generation of a button directly in the table header, along with an input popup, improved error handling, and, at the end of the process, a summary popup listing the operations that were processed in the user interface.</SPAN></P><P class="lia-align-center" style="text-align: center;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="valentincadart_0-1746602434253.png" style="width: 501px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/258300i1A6B66058392B996/image-dimensions/501x213?v=v2" width="501" height="213" role="button" title="valentincadart_0-1746602434253.png" alt="valentincadart_0-1746602434253.png" /></span></P><P class="lia-align-left" style="text-align : left;"><SPAN>It is possible to enable standard bulk deletion management in Fiori Elements, handled by the framework. This is because it can be sent in batch without any business logic, as in the application I developed. We can also see the ease of integrating the action into a Fiori Elements through annotations. Finally, with the business complexity and error handling, we get feedback regardless of whether it's a success or failure, something that would have been complicated with a batch send. This example illustrates why these new frameworks recommend actions for such cases.</SPAN></P><P class="lia-align-center" style="text-align: center;"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="valentincadart_1-1746602461077.png" style="width: 500px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/258301i5B939C96B0627D52/image-dimensions/500x332?v=v2" width="500" height="332" role="button" title="valentincadart_1-1746602461077.png" alt="valentincadart_1-1746602461077.png" /></span></P><P><SPAN>Lastly, the most straightforward case, “Retrieve the number of delayed flights”, simply returns data with no side effects. This makes it a perfect fit for a function.</SPAN></P><P class="lia-align-left" style="text-align : left;"><SPAN>The complete project is available on my GitHub repository =&gt; </SPAN><A href="https://github.com/ValentinCadart/FlightManagement" target="_blank" rel="noopener nofollow noreferrer"><SPAN>https://github.com/ValentinCadart/FlightManagement</SPAN></A><SPAN>.</SPAN></P><H2 id="toc-hId--693747700">&nbsp;</H2><H2 id="toc-hId--890261205"><STRONG>Conclusion</STRONG></H2><P>In a nutshell<SPAN>, Function Imports, Actions, and Functions are key components across SAP SEGW, RAP, and CAP that enable you to implement custom business logic, transcending basic CRUD functionality. While each platform offers its unique features and methods of defining and invoking these operations, understanding when and how to use them is essential for building efficient, scalable SAP applications.</SPAN></P><P><SPAN>Sooo.. one more misuse of function imports, actions or functions and I’m calling Basis to revoke your service deployment privileges. You’ve been warned ! </SPAN></P> 2025-05-08T09:36:59.881000+02:00 https://community.sap.com/t5/technology-blog-posts-by-members/unlocking-customer-management-in-rap-a-deep-dive-part-1-data-model-and/ba-p/14088159 Unlocking Customer Management in RAP: A Deep Dive (Part 1 - Data Model and Behavior) 2025-05-12T22:14:11.348000+02:00 Srivatsa https://community.sap.com/t5/user/viewprofilepage/user-id/1392861 <P><STRONG>Data Model</STRONG></P><P>The data model defines the structure of our application's data. In our Customer Management scenario, we have a central <STRONG>Customer</STRONG> entity and several related child entities. This parent-child relationship allows us to organize and manage customer information effectively.</P><UL><LI><STRONG>Entities:</STRONG><UL><LI><STRONG>Customer (Parent):</STRONG> Stores core customer information.</LI><LI><STRONG>Child Entities:</STRONG><UL><LI>Account Reference</LI><LI>Agreement Reference</LI><LI>Contact Medium</LI><LI>Payment Method</LI><LI>Customer Attachment</LI><LI>Characteristic</LI><LI>Credit Profile</LI><LI>Event Input</LI><LI>Event Subscription</LI><LI>Related Party</LI><LI>Time Period</LI></UL></LI></UL></LI></UL><P>We use database tables to persist the data for these entities.</P><UL><LI><P><STRONG>Database Table Examples:</STRONG></P><UL><LI><P><STRONG>Customer Table (zztmfcustomer):</STRONG></P><UL><LI>id: Primary key, uniquely identifies each customer. The not null constraint ensures that every customer record has an ID.</LI><LI>name: Customer's name.</LI><LI>status: Current status of the customer.</LI><LI>href: A general link or reference field.</LI><LI>last_changed_at: A timestamp to track when the record was last modified, useful for concurrency control.</LI></UL></LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Table to Store Customer details' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zztmfcustomer { key id : char10 not null; href : char100; name : char20 not null; status : char20; statusreason : char30; validfor : datab; last_changed_at : timestampl; }​</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><UL><LI><P><STRONG>Account Reference Table (zztmfaccountref):</STRONG></P><UL><LI>id, name: Combined primary key, ensuring each account reference has a unique ID and name combination.</LI><LI>description: Additional details about the account reference.</LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Account Reference' @AbapCatalog.enhancement.category : #EXTENSIBLE_ANY @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zztmfaccountref { key id : char10 not null; key name : char40 not null; href : char100; description : char255; last_changed_at : timestampl; }​</code></pre><P>&nbsp;</P><P>&nbsp;</P><UL><LI><P><STRONG>Primary Keys:</STRONG></P><UL><LI>Primary keys are essential for uniquely identifying each record in a database table. They ensure data integrity and are used to establish relationships between tables.</LI><LI>In zztmfcustomer, id is the primary key.</LI><LI>In zztmfaccountref, the combination of id and name forms the primary key.</LI></UL></LI></UL><P><STRONG>CDS Views</STRONG></P><P>CDS (Core Data Services) views are virtual data models defined on top of database tables. They simplify data access and provide features like data aggregation, calculations, and annotations. In RAP, CDS views are crucial for exposing data to the service layer.</P><UL><LI><P><STRONG>CDS View Examples:</STRONG></P><UL><LI><P><STRONG>Basic Customer View (ZC_TMF_Customer):</STRONG></P><UL><LI>This view selects fields from the zztmfcustomer table and exposes them as the ZC_TMF_Customer entity.</LI><LI>The key annotation marks the id field as the key, which is essential for identifying customer records.</LI></UL></LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Customer' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true define root view entity ZC_TMF_Customer as select from zztmfcustomer as Customer composition [0..*] of ZC_TMF_AccountRef as _AccountRef // on _AccountRef.customer_id = Customer.id composition [0..*] of ZC_TMF_AgreementRe as _AgreementRe // on _AgreementRe.customer_id = Customer.id composition [0..*] of ZC_TMF_ContactMedium as _ContactMedium // on _ContactMedium.customer_id = Customer.id composition [0..*] of ZC_TMF_CreditProfile as _CreditProfile //on _CreditProfile.customer_id = Customer.id composition [0..*] of ZC_TMF_EventInput as _EventInput //on _EventInput.customer_id = Customer.id composition [0..*] of ZC_TMF_Characteristic as _Characteristic //on _EventInput.customer_id = Customer.id composition [0..*] of ZC_TMF_EventSubscription as _EventSubscription // on _EventSubscription.customer_id = Customer.id composition [0..*] of ZC_TMF_PaymentMethod as _PaymentMethod // on _PaymentMethod.customer_id = Customer.id composition [0..*] of ZC_TMF_RelatedParty as _RelatedParty // on _RelatedParty.customer_id = Customer.id composition [0..*] of ZC_TMF_TimePeriod as _TimePeriod //on _TimePeriod.customer_id = Customer.id composition [0..*] of ZC_TMF_CustomerAttachment as _Attachments { key Customer.id as customer_id, Customer.name as Name, Customer.href as Link, Customer.status as Status, last_changed_at, _AccountRef, _AgreementRe, _ContactMedium, _CreditProfile, _EventInput, _EventSubscription, _Characteristic, _PaymentMethod, _RelatedParty, _TimePeriod, _Attachments } ​</code></pre><P>&nbsp;</P><P>&nbsp;</P><UL><LI><P><STRONG>Customer View with Associations (ZC_TMF_Customer):</STRONG></P><UL><LI>This view includes associations (_AccountRef, _AgreementRe, etc.) to child entities. These associations define the relationships between the Customer entity and its related data.</LI></UL></LI><LI><P><STRONG>Associations:</STRONG></P><UL><LI>Associations in CDS views define relationships between entities. They allow you to navigate from one entity to another, retrieving related data.</LI><LI>In our example, the associations in the ZC_TMF_Customer view enable you to access a customer's account references, agreements, and other related information.</LI><LI>Associations are crucial for creating a hierarchical data structure and simplifying data retrieval.</LI><LI><STRONG>Why use associations to the parent?</STRONG> Associations from child entities back to the parent (_Customer) are used to establish the context of the child data. This is essential for:<UL><LI><STRONG>Data integrity:</STRONG> Ensuring child records are linked to a valid parent.</LI><LI><STRONG>Navigation:</STRONG> Allowing the UI and service to easily navigate from a child record to its corresponding customer.</LI><LI><STRONG>Data retrieval:</STRONG> Simplifying queries to fetch child data in the context of a specific customer.</LI></UL></LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'CDS view for Account Ref' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true //@AbapCatalog.sqlViewName: 'ZCTMFV_ACCR' define view entity ZC_TMF_AccountRef as select from zztmfaccountref as accou association to parent ZC_TMF_Customer as _Customer on $projection.Id = _Customer.customer_id { key id as Id, key name as name, accou.href as Href, description as Description, last_changed_at, _Customer } ​</code></pre><P>&nbsp;</P><P>&nbsp;</P><UL><LI><P><STRONG>Composition:</STRONG></P><UL><LI>Composition is a stronger form of association that implies ownership. When you compose child entities within a parent, the lifecycle of the child is dependent on the parent. If you delete the parent, the children are typically deleted as well.</LI><LI>While the provided CDS views use associations, composition would be suitable if the child entities <EM>exclusively</EM> belong to the parent Customer and should not exist independently.</LI><LI><STRONG>Why composition is used:</STRONG> Composition is used to model strong ownership relationships. It ensures data consistency and simplifies data management by automatically handling the lifecycle of child entities.</LI></UL></LI></UL><P><STRONG>Metadata Annotations</STRONG></P><P>Metadata annotations are used to enrich CDS views with semantic information that controls how the data is presented and processed by the UI and service layer.</P><UL><LI><P><STRONG>Annotation Examples:</STRONG></P><UL><LI><P><STRONG><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.lineItem:</STRONG></P><UL><LI>This annotation specifies how the customer_Id field should be displayed in a list (e.g., in a table).</LI><LI>position: Determines the order of the column.</LI><LI>label: Provides a user-friendly column header.</LI><LI>importance: Controls the column's visibility based on screen size.</LI></UL></LI><LI><P><STRONG><a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1445379">@ui</a>.facet:</STRONG></P><UL><LI>This annotation defines sections or groups of fields in the UI (e.g., in an object page).</LI><LI>type: #IDENTIFICATION_REFERENCE: Creates an identification facet (header section).</LI><LI>type: #FIELDGROUP_REFERENCE: References a field group to display fields together.</LI><LI>position: Determines the order of the facet.</LI><LI>label: Provides a user-friendly header for the facet.</LI></UL></LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CUSTOMER @UI: { headerInfo: { typeName: 'Customer', typeNamePlural: 'Customers', title: { type: #STANDARD, label: 'Customer ID', value: 'customer_id' }, description: { label: 'Name', value: 'name' } } } annotate view ZC_TMF_Customer with { @UI.facet: [ { id: 'General', type: #IDENTIFICATION_REFERENCE, position: 10, label: 'Customer Data' }, { type: #FIELDGROUP_REFERENCE, position: 10, targetQualifier: 'BasicInfo', parentId: 'General', isSummary: true, isPartOfPreview: true }, { id: 'AccountRefsFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Account References', position: 20, targetElement: '_AccountRef' }, { id: 'AgreementReFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Agreements', position: 30, targetElement: '_AgreementRe' }, { id: 'ContactMediumFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Contact Mediums', position: 40, targetElement: '_ContactMedium' }, { id: 'CreditProfileFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Credit Profiles', position: 50, targetElement: '_CreditProfile' }, { id: 'EventInputFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Event Inputs', position: 60, targetElement: '_EventInput' }, { id: 'EventSubscriptionFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Event Subscriptions', position: 70, targetElement: '_EventSubscription' }, { id: 'CharacteristicFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Characteristics', position: 80, targetElement: '_Characteristic' }, { id: 'PaymentMethodFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Payment Methods', position: 90, targetElement: '_PaymentMethod' }, { id: 'RelatedPartyFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Related Parties', position: 100, targetElement: '_RelatedParty' }, { id: 'CustAttFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Attachment', position: 110, targetElement: '_Attachments' }, { id: 'TimePeriodFacet', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Time Periods', position: 120, targetElement: '_TimePeriod' } ] // List and Identification fields @UI.lineItem: [{ position: 10, label: 'Customer ID', importance: #HIGH }] @UI.identification: [{ position: 10, label: 'Customer ID' }] @UI.selectionField: [{ position: 10 }] @EndUserText.label: 'Customer ID' customer_id; @UI.lineItem: [{ position: 20, label: 'Name', importance: #HIGH }] @UI.identification: [{ position: 20, label: 'Name' }] @UI.fieldGroup: [{ qualifier: 'BasicInfo', position: 10, importance: #HIGH }] Name; @UI.lineItem: [{ position: 30, label: 'Link', importance: #HIGH }] @UI.identification: [{ position: 30, label: 'Link' }] @UI.fieldGroup: [{ qualifier: 'BasicInfo', position: 20, importance: #HIGH }] Link; @UI.lineItem: [ { position: 40, label: 'Status', importance: #HIGH }, { type: #FOR_ACTION, dataAction: 'copy', label: 'Copy', position: 100 } ] @UI.identification: [{ position: 40, label: 'Status' }] @UI.fieldGroup: [{ qualifier: 'BasicInfo', position: 30, importance: #HIGH }] Status; // @UI.lineItem: [{ position: 40, label: 'Status', importance: #HIGH }] // @UI.identification: [{ position: 40, label: 'Status' }] // @UI.fieldGroup: [{ qualifier: 'BasicInfo', position: 30, importance: #HIGH }] // // Status; }​</code></pre><P>&nbsp;</P><P>&nbsp;</P><UL><LI>Associated Child Entities :&nbsp;</LI></UL><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CUSTOMER annotate entity ZC_TMF_AgreementRe with { @UI.facet: [ { type: #IDENTIFICATION_REFERENCE, position: 10, id: 'AgrHdr', label: 'Agreement' }, { type: #FIELDGROUP_REFERENCE, position: 10, targetQualifier: 'AgrBasic', parentId: 'AgrHdr', isSummary: true, isPartOfPreview: true } ] @UI.lineItem: [{ position: 10, label: 'ID', importance: #HIGH }] @UI.identification: [{ position: 10, label: 'ID' }] Id; @UI.lineItem: [{ position: 20, label: 'Href', importance: #HIGH }] @UI.identification: [{ position: 20, label: 'Href' }] @UI.fieldGroup: [{ qualifier: 'AgrBasic', position: 10, importance: #HIGH }] Href; @UI.lineItem: [{ position: 30, label: 'Name', importance: #HIGH }] @UI.identification: [{ position: 30, label: 'Name' }] @UI.fieldGroup: [{ qualifier: 'AgrBasic', position: 20, importance: #HIGH }] Name; }​</code></pre><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>Behavior Definition</STRONG></P><P>The behavior definition defines the behavior and properties of a RAP business object. It specifies the standard operations (CRUD - Create, Read, Update, Delete) and other actions that can be performed on the entity.</P><UL><LI><P><STRONG>Behavior Definition Example (ZC_TMF_Customer):</STRONG></P><UL><LI>unmanaged implementation in class zbp_c_tmf_customer unique;: Specifies that we are responsible for implementing the behavior (CRUD operations) in the zbp_c_tmf_customer class.</LI><LI>with draft;: Enables draft functionality for the entity.</LI><LI>define behavior for ZC_TMF_Customer alias Customer: Defines the behavior for the ZC_TMF_Customer entity.</LI><LI>authorization master ( instance <span class="lia-unicode-emoji" title=":disappointed_face:">😞</span> Specifies authorization control.</LI><LI>draft table ZZTMFCUSTOMER_d: Links the entity to its draft table.</LI><LI>create; update; delete;: Enables the standard CRUD operations.</LI><LI>action copy;: Defines a custom action.</LI><LI>draft action ...: Enables draft-specific actions.</LI><LI>field ( readonly ) customer_Id;: Makes the customer_Id field read-only.</LI><LI>association ... { create; }: Defines associations to child entities and allows creating them.</LI><LI>// validation ...: A commented-out example of validation logic.</LI></UL></LI><LI><P><STRONG>Unmanaged Scenario:</STRONG></P><UL><LI>In an unmanaged scenario, the developer is responsible for writing the ABAP code to implement the standard CRUD operations and any custom business logic.</LI><LI><STRONG>Why use an unmanaged scenario?</STRONG><UL><LI><STRONG>Flexibility:</STRONG> Provides maximum control over the implementation.</LI><LI><STRONG>Complex logic:</STRONG> Suitable for scenarios with intricate business rules or performance-critical operations.</LI><LI><STRONG>Legacy integration:</STRONG> Can be necessary when integrating with existing, non-RAP code.</LI></UL></LI></UL></LI><LI><P><STRONG>ETag:</STRONG></P><UL><LI>etag master last_changed_at: ETags (Entity Tags) are used for optimistic locking. They help prevent data loss when multiple users edit the same data concurrently.</LI><LI>The last_changed_at field is used as the ETag. When a user updates a Customer, the last_changed_at value is checked against the value that the user originally read. If they don't match, it means someone else has changed the data in the meantime, and the update is prevented.</LI></UL></LI><LI><P><STRONG>Entities:</STRONG></P><UL><LI>In the context of the behavior definition, "entities" refers to the instances of the business object (e.g., Customer records) that are being processed by the defined operations (Create, Update, Delete). For example, in a CREATE operation, "entities" would contain the data for the new Customer records being created.</LI></UL></LI></UL><pre class="lia-code-sample language-abap"><code>unmanaged implementation in class zbp_c_tmf_customer unique; with draft; define behavior for ZC_TMF_Customer alias Customer authorization master ( instance ) draft table ZZTMFCUSTOMER_d etag master last_changed_at lock master total etag last_changed_at early numbering { // Root entity create; update; delete; action copy; // Draft handling draft action Edit; draft action Activate; draft action Discard; draft action Resume; draft determine action Prepare; field ( readonly ) customer_Id; association _AccountRef { create; } association _AgreementRe { create; } association _ContactMedium { create; } association _CreditProfile { create; } association _EventInput { create; } association _EventSubscription { create; } association _Characteristic { create; } association _PaymentMethod { create; } association _RelatedParty { create; } association _TimePeriod { create; } association _Attachments{ create; } // Draft validation // validation validateName on save { create; update; } } define behavior for ZC_TMF_CUSTOMERATTACHMENT alias Attachment implementation in class zbp_c_tmf_customer unique draft table zztmcustatt_d lock dependent { field ( readonly ) AttachId, CustomerId; // Parent key field (mandatory ) Attachment; //create; update; delete; association _Customer; } define behavior for ZC_TMF_AccountRef alias AccountRef implementation in class zbp_c_tmf_customer unique draft table zztmfaccount_d lock dependent { field ( readonly ) Id; // Parent key field (mandatory ) name; //create; update; delete; association _Customer; } define behavior for ZC_TMF_AgreementRe alias AgreementRe implementation in class zbp_c_tmf_customer unique draft table zztmfagreem_d lock dependent { field ( readonly ) Id; // Parent key field ( mandatory) Name; //create; delete; update; association _Customer; } define behavior for ZC_TMF_ContactMedium alias ContactMedium implementation in class zbp_c_tmf_customer unique draft table zztmfcontactme_d lock dependent { field ( readonly ) customer_id; // Parent key field ( mandatory ) Mediumtype; update; //create; delete; association _Customer; } define behavior for ZC_TMF_CreditProfile alias CreditProfile implementation in class zbp_c_tmf_customer unique draft table zztmfcreditpro_d lock dependent { field ( readonly ) customer_id; // Parent key field ( mandatory ) Creditprofiledate; update; //create; delete; association _Customer; } define behavior for ZC_TMF_Characteristic alias Characteristic implementation in class zbp_c_tmf_customer unique draft table zztmfcharacte_d lock dependent { field ( readonly ) id, customer_id; // Parent key field ( mandatory ) Name, Value; update; //create; delete; association _Customer; } define behavior for ZC_TMF_EventInput alias EventInput implementation in class zbp_c_tmf_customer unique draft table zztmfeventinp_d lock dependent { field ( readonly ) customer_id; // Parent key field ( mandatory ) Callback; update; //create; delete; association _Customer; } define behavior for ZC_TMF_EventSubscription alias EventSubscription implementation in class zbp_c_tmf_customer unique draft table zztmfeventsubs_d lock dependent { field ( readonly ) customer_Id; // Parent key field ( mandatory ) Callback; update; //create; delete; association _Customer; } define behavior for ZC_TMF_PaymentMethod alias PaymentMethod implementation in class zbp_c_tmf_customer unique draft table zztmfpaymentme_d lock dependent { field ( readonly ) customer_Id; // Parent key field ( mandatory ) Name; update; //create; delete; association _Customer; } define behavior for ZC_TMF_RelatedParty alias RelatedParty implementation in class zbp_c_tmf_customer unique draft table zztmfrelatedpa_d lock dependent { field ( readonly ) customer_Id; // Parent key field ( mandatory ) Name, Role; update; //create; delete; association _Customer; } define behavior for ZC_TMF_TimePeriod alias TimePeriod implementation in class zbp_c_tmf_customer unique draft table zztmftimeperi_d lock dependent { field ( readonly ) customer_id; // Parent key field ( mandatory ) Startdatetime; update; delete; //create; association _Customer; }​</code></pre><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>Conclusion</STRONG></P><P>In Part 1, we defined the foundation of our Customer Management application by creating the data model, CDS views, and behavior definition. We explored key concepts like primary keys, associations, metadata annotations, and the unmanaged RAP scenario.</P> 2025-05-12T22:14:11.348000+02:00