Application Development and Automation Blog Posts articles Application Development and Automation Blog Posts articles
-
SAP Developer News April 24th, 2025
by Eberenwaobiora on April 24, 2025 at 7:10 pm
This week's episode contains segments on SAP Build news, New Basic Trial available, and Event Mesh bridge and Developer Hub.
-
RedRays ABAP Security Scanner - Free Registration Now Open!
by vahagn1 on April 24, 2025 at 11:29 am
We're excited to announce that registration for the RedRays ABAP Security Scanner platform is now open - completely free of charge.Enhance Your SAP Security TodayVisit https://dashboard.redrays.io/developer today and receive 30 complimentary credits to start scanning your ABAP code directly from our platform immediately. Our advanced security scanning tool helps you detect vulnerabilities, provides detailed analysis, and protects your SAP applications effectively.What RedRays ABAP Security Scanner Offers:RedRays provides automated vulnerability detection in your ABAP code with comprehensive security analysis and severity ratings. You'll receive detailed recommendations for fixing identified issues through our user-friendly interface, all hosted on a secure cloud-based platform with enterprise-grade protection.Why Join Now?Completely free - 30 credits at no costImmediate access - start scanning right awayNo commitments - try the platform with no strings attachedProfessional-grade security tools for the SAP communityDon't miss this opportunity to enhance your SAP security posture with professional-grade tools at no cost. Limited-time offer for the SAP community!How to Get Started:Simply visit our registration page at https://dashboard.redrays.io/developer, create your free account, and receive 30 complimentary credits to start scanning your ABAP code immediately.We look forward to helping you secure your SAP applications!
-
Ensuring Performance in CDS Views with ATC: Tools & Test System Strategies
by harsh_raj1 on April 24, 2025 at 11:28 am
In modern ABAP development, Core Data Services (CDS) views play a pivotal role in defining the data model and pushing logic closer to the database. To ensure that these views meet SAP’s performance and quality standards, ATC (ABAP Test Cockpit) analysis is a critical step in the lifecycle.
-
DRAFT TABLE IN RAP USING UN-MANAGED SCENARIO
by Khan-Muskan on April 24, 2025 at 11:26 am
Introduction In this blog post, we’ll walk through the end-to-end development of a Vehicle Management Application using the RESTful ABAP Programming Model (RAP). The goal is to manage vehicle data with draft capabilities and dynamically calculate the total price based on user input. This application demonstrates CRUD operations, draft handling, early numbering, and determinations—all implemented using RAP in ABAP. What is Draft table in SAP: -A Draft Table in RAP ABAP allows you to temporarily save incomplete or intermediate user input during transactional processing. This draft data is stored separately from the final, persistent business data, ensuring that users can work on their changes incrementally without immediately committing them to the database.Note: -Early numbering must be implemented in case of Draft tables in Un-managed scenario. Determination or Validation functionality must be implemented to perform draft capability. Data ModelWe begin with the creation of a transparent table ZMUK_DT_VEHICLE to store vehicle master data.Database Table: ZMUK_DT_VEHICLEThe table includes:Basic fields like brand and model. Price-related fields with currency semantics.A computed field total_price which is dynamically updated based on quantity and unit price.@EndUserText.label : 'Vehicle table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zmuk_dt_vehicle { key clnt : abap.clnt not null; key veh_id : abap.char(8) not null; veh_brand : abap.char(20); veh_model : abap.char(20); @Semantics.amount.currencyCode : 'zmuk_dt_vehicle.curkey_field' veh_price : abap.curr(7,2); curkey_field : abap.cuky; quantity : int1; @Semantics.amount.currencyCode : 'zmuk_dt_vehicle.curkey_field' total_price : abap.curr(7,2); last_changed_at : abp_locinst_lastchange_tstmpl; } Interface View This CDS view acts as the interface view and serves as the foundation for the RAP business object. It exposes all relevant fields and includes annotations like: @Semantics.amount.currencyCode @Semantics.systemDateTime.localInstanceLastChangedAt @AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface view for vehicle draft' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define root view entity ZMUK_I_VEHICLE as select from zmuk_dt_vehicle { key veh_id as VehId, veh_brand as VehBrand, veh_model as VehModel, @Semantics.amount.currencyCode : 'CurkeyField' veh_price as VehPrice, curkey_field as CurkeyField, quantity as Quantity, @Semantics.amount.currencyCode : 'CurkeyField' total_price as TotalPrice, @Semantics.systemDateTime.localInstanceLastChangedAt: true last_changed_at as LastChangedAt } Projection View The projection view ZMUK_P_VEHICLE defines what is exposed to the consumer via the OData service. It includes annotations like @AccessControl.authorizationCheck: #NOT_REQUIRED and enables draft support with use draft. @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection view for vehicle' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true define root view entity ZMUK_P_VEHICLE provider contract transactional_query as projection on ZMUK_I_VEHICLE { key VehId, VehBrand, VehModel, @Semantics.amount.currencyCode : 'CurkeyField' VehPrice, CurkeyField, Quantity, @Semantics.amount.currencyCode : 'CurkeyField' TotalPrice, LastChangedAt } Behavior Definitions Behavior Definition (Interface Layer) Key points: with draft enables draft handling. Determination total_price is triggered on modify when Quantity or VehPrice changes. Early numbering is used to assign unique VehIds. Side effects are defined to ensure UI refreshes on dependent field updates. unmanaged implementation in class zbp_muk_i_vehicle unique; strict ( 2 ); with draft; define behavior for ZMUK_I_VEHICLE //alias <alias_name> draft table ZMUK_DT_DR_VEH lock master total etag LastChangedAt authorization master ( instance ) //etag master <field_name> early numbering { create; update; delete; determination total_price on modify { field Quantity; field VehPrice; } side effects { field quantity affects field TotalPrice; field VehPrice affects field TotalPrice; } draft determine action Prepare { } draft action Activate optimized; draft action Discard; draft action Edit; draft action Resume; field ( readonly ) TotalPrice, VehId; mapping for zmuk_dt_vehicle { VehId = veh_id; VehBrand = veh_brand; VehModel = veh_model; VehPrice = veh_price; CurkeyField = curkey_field; Quantity = quantity; TotalPrice = total_price; LastChangedAt = last_changed_at; } } Behavior Definition (Projection Layer) A simple projection of the behavior from the interface layer, inheriting draft and CRUD capabilities. projection; strict ( 2 ); use draft; use side effects; define behavior for ZMUK_P_VEHICLE //alias <alias_name> { use create; use update; use delete; use action Prepare; use action Activate; use action Discard; use action Edit; use action Resume; }Behavior Implementation Class lhc_ZMUK_I_VEHICLE This local handler class contains the actual implementation logic for: CRUD operations Determination for total_price Early numbering logic for generating vehicle IDs Early numbering Example METHOD earlynumbering_create. DATA: lv_num(5) TYPE n. DATA: lv_vehid(8) TYPE n. SELECT MAX( veh_id ) FROM zmuk_dt_vehicle INTO (lv_id). if lv_id IS NOT INITIAL. lv_num = lv_id+2. lv_num += 1. ELSE. lv_num = 00001. endif. lv_vehid = |VEH{ lv_num }|. DATA(ls_entities) = VALUE #( entities[ 1 ] OPTIONAL ). ls_entities-VehId = lv_vehid. mapped-zmuk_i_vehicle = VALUE #( ( %cid = ls_entities-%cid %is_draft = ls_entities-%is_draft %key = ls_entities-%key ) ). ENDMETHOD.Determination of Total Price METHOD total_price. READ ENTITY IN LOCAL MODE ZMUK_I_VEHICLE ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result). DATA(ls_result) = VALUE #( lt_result[ 1 ] OPTIONAL ). if ls_result-Quantity Is NOT INITIAL. ls_result-TotalPrice = ls_result-Quantity * ls_result-VehPrice. MODIFY ENTITY IN LOCAL MODE ZMUK_I_VEHICLE UPDATE FIELDS ( TotalPrice ) WITH VALUE #( ( %tky = ls_result-%tky TotalPrice = ls_result-TotalPrice ) ). endif. ENDMETHOD. Calling the CRUD operations implemented from Helper class(zmuk_vehicle_helper). Create method METHOD create. DATA(lo_vehicle) = zmuk_vehicle_helper=>get_instance( ). lo_vehicle->create_vehicle( EXPORTING entities = entities CHANGING mapped = mapped failed = failed repored = reported ). ENDMETHOD.Update method METHOD update. zmuk_vehicle_helper=>get_instance( )->updata_vehicle( EXPORTING entities = entities CHANGING mapped = mapped failed = failed reported = reported ). ENDMETHOD.Delete method METHOD delete. DATA(lo_vehicle) = zmuk_vehicle_helper=>get_instance( ). lo_vehicle->delete_vehicle( EXPORTING keys = keys CHANGING mapped = mapped failed = failed reported = reported ). ENDMETHOD.Helper Class: ZMUK_VEHICLE_HELPER To decouple logic and improve reusability, all database operations are encapsulated in a helper class. This class handles: Mapping entities Performing actual inserts, updates, and deletes Reading and adjusting numbers It follows the Singleton design pattern to ensure a single instance is reused. CLASS zmuk_vehicle_helper DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. TYPES : tt_veh_create TYPE TABLE FOR CREATE zmuk_i_vehicle, tt_veh_delete TYPE TABLE FOR DELETE zmuk_i_vehicle, tt_veh_update TYPE TABLE FOR UPDATE zmuk_i_vehicle, tt_mapped_early TYPE RESPONSE FOR MAPPED EARLY zmuk_i_vehicle, tt_mapped_late TYPE RESPONSE FOR MAPPED LATE zmuk_i_vehicle, tt_failed_early TYPE RESPONSE FOR FAILED EARLY zmuk_i_vehicle, tt_reported_early TYPE RESPONSE FOR REPORTED EARLY zmuk_i_vehicle, tt_reported_late TYPE RESPONSE FOR REPORTED LATE zmuk_i_vehicle, tt_keys_read TYPE TABLE FOR READ IMPORT zmuk_i_vehicle, tt_read_result TYPE TABLE FOR READ RESULT zmuk_i_vehicle. CLASS-METHODS get_instance RETURNING VALUE(ro_vehicle) TYPE REF TO zmuk_vehicle_helper. CLASS-DATA : go_vehicle TYPE REF TO zmuk_vehicle_helper, gt_vehicle_create TYPE TABLE OF zmuk_dt_vehicle, gt_vehicle_update TYPE TABLE OF zmuk_dt_vehicle, gt_vehicle_delete TYPE RANGE OF zmuk_dt_vehicle-veh_id, gs_vehicle TYPE zmuk_dt_vehicle. METHODS: create_vehicle IMPORTING entities TYPE tt_veh_create CHANGING mapped TYPE tt_mapped_early failed TYPE tt_failed_early repored TYPE tt_reported_early, delete_vehicle IMPORTING keys TYPE tt_veh_delete CHANGING mapped TYPE tt_mapped_early failed TYPE tt_failed_early reported TYPE tt_reported_early, updata_vehicle IMPORTING entities TYPE tt_veh_update CHANGING mapped TYPE tt_mapped_early failed TYPE tt_failed_early reported TYPE tt_reported_early, read IMPORTING keys TYPE tt_keys_read CHANGING result TYPE tt_read_result failed TYPE tt_failed_early reported TYPE tt_reported_early, save_data CHANGING reported TYPE tt_reported_late. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zmuk_vehicle_helper IMPLEMENTATION. METHOD create_vehicle. gt_vehicle_create = CORRESPONDING #( entities MAPPING FROM ENTITY ). ENDMETHOD. METHOD get_instance. go_vehicle = ro_vehicle = COND #( WHEN go_vehicle IS BOUND THEN go_vehicle ELSE NEW #( ) ). ENDMETHOD. METHOD save_data. if gt_vehicle_create IS NOT INITIAL. INSERT zmuk_dt_vehicle FROM TABLE _vehicle_create. ENDIF. IF gt_vehicle_delete Is NOT INITIAL. DELETE FROM zmuk_dt_vehicle WHERE veh_id IN _vehicle_delete. ENDIF. if gt_vehicle_update IS NOT INITIAL. UPDATE zmuk_dt_vehicle FROM TABLE _vehicle_update. ENDIF. ENDMETHOD. METHOD delete_vehicle. DATA : lt_vehicle_delete TYPE TABLE OF zmuk_dt_vehicle. lt_vehicle_delete = CORRESPONDING #( keys MAPPING FROM ENTITY ). gt_vehicle_delete = VALUE #( FOR ls_delete IN lt_vehicle_delete sign = 'I' option = 'EQ' ( low = ls_delete-veh_id ) ). ENDMETHOD. METHOD updata_vehicle. gt_vehicle_update = CORRESPONDING #( entities MAPPING FROM ENTITY ). DATA(ls_vehicle_update) = VALUE #( gt_vehicle_update[ 1 ] OPTIONAL ). SELECT SINGLE FROM zmuk_dt_vehicle FIELDS * WHERE veh_id = _vehicle_update-veh_id INTO (lv_student_up). gt_vehicle_update = VALUE #( ( veh_id = ls_vehicle_update-veh_id veh_brand = COND #( WHEN ls_vehicle_update-veh_brand IS INITIAL THEN ls_vehicle_update-veh_brand ELSE ls_vehicle_update-veh_brand ) veh_model = COND #( WHEN ls_vehicle_update-veh_model IS INITIAL THEN ls_vehicle_update-veh_model ELSE ls_vehicle_update-veh_model ) veh_price = COND #( WHEN ls_vehicle_update-veh_price IS INITIAL THEN ls_vehicle_update-veh_price ELSE ls_vehicle_update-veh_price ) curkey_field = COND #( WHEN ls_vehicle_update-curkey_field IS INITIAL THEN ls_vehicle_update-curkey_field ELSE ls_vehicle_update-curkey_field ) quantity = COND #( WHEN ls_vehicle_update-quantity IS INITIAL THEN ls_vehicle_update-quantity ELSE ls_vehicle_update-quantity ) total_price = COND #( WHEN ls_vehicle_update-total_price IS INITIAL THEN ls_vehicle_update-total_price ELSE ls_vehicle_update-total_price ) last_changed_at = COND #( WHEN ls_vehicle_update-last_changed_at IS INITIAL THEN ls_vehicle_update-last_changed_at ELSE ls_vehicle_update-last_changed_at ) ) ). ENDMETHOD. METHOD read. SELECT FROM zmuk_dt_vehicle FIELDS * FOR ALL ENTRIES IN WHERE veh_id = -VehId INTO TABLE (lt_read_vehicle). result = CORRESPONDING #( lt_read_vehicle MAPPING to ENTITY ). ENDMETHOD. ENDCLASS.Metadata Extension Metadata extensions are used to add UI annotations (e.g., labels, grouping, UI types) without cluttering the main CDS view. ZMUK_P_VEHICLE (Metadata Extension for Projection View) @Metadata.layer: #CORE annotate entity ZMUK_P_VEHICLE with { @ui.facet: [{ purpose: #STANDARD, position: 10, label: 'VEHICLE DETAILS', type: #IDENTIFICATION_REFERENCE }] @ui.lineItem: [{ position: 10, label: 'Vehicle Id' }] @ui.identification: [{ position: 10, label: 'Vehicle Id' }] VehId; @ui.lineItem: [{ position: 20, label: 'Vehicle Brand' }] @ui.identification: [{ position: 20, label: 'Vehicle Brand' }] VehBrand; @ui.lineItem: [{ position: 30, label: 'Vehicle Model' }] @ui.identification: [{ position: 30, label: 'Vehicle Model' }] VehModel; @ui.lineItem: [{ position: 40, label: 'Vehicle Price' }] @ui.identification: [{ position: 40, label: 'Vehicle Price' }] VehPrice; @ui.lineItem: [{ position: 60, label: 'Vehicle Quantity' }] @ui.identification: [{ position: 60, label: 'Vehicle Quantity' }] Quantity; @ui.lineItem: [{ position: 70, label: 'Total Price' }] @ui.identification: [{ position: 70, label: 'Total Price' }] TotalPrice; }Service Definition Service definition exposes your projection view (ZMUK_P_VEHICLE) for consumption in OData. @EndUserText.label: 'Service def for vehicle draft' define service ZMUK_VEHICLE_SRV_DEF { expose ZMUK_P_VEHICLE; }Service Binding Service binding binds your service definition to a specific protocol (in this case, OData V2 - UI). OUTPUT: - While creating a record.If user click on enter, the determination logic will get triggered based ON MODIFY and side effects statements: After clicking on 'Create' button the record will store in database.Below i have not clicked on create button, but the record has been saves as Draft in the draft table. And if i go back and check the record has been created.Select Own draft from the editing status.It will display the record which was saved as a draft into the draft table.The draft table which is storing the draft records.@EndUserText.label : 'Draft table for entity ZMUK_I_VEHICLE' @AbapCatalog.enhancement.category : #EXTENSIBLE_ANY @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zmuk_dt_dr_veh { key mandt : mandt not null; key vehid : abap.char(8) not null; vehbrand : abap.char(20); vehmodel : abap.char(20); @Semantics.amount.currencyCode : 'zmuk_dt_dr_veh.curkeyfield' vehprice : abap.curr(7,2); curkeyfield : abap.cuky; quantity : int1; @Semantics.amount.currencyCode : 'zmuk_dt_dr_veh.curkeyfield' totalprice : abap.curr(7,2); lastchangedat : abp_locinst_lastchange_tstmpl; "%admin" : include sych_bdl_draft_admin_inc; }ConclusionThis blog demonstrated a complete RAP implementation to manage vehicle data using modern ABAP. We implemented a draft-enabled BO with calculated fields, early numbering, and proper layering using CDS views, behavior definitions, and helper classes. With this pattern, you can build scalable and maintainable applications using RAP and expose them as OData V4 or OData V2 services on BTP.
-
ABAPers: how did YOU start with Clean ABAP?!
by joachimrees1 on April 24, 2025 at 11:26 am
Clean ABAP ( https://github.com/SAP/styleguides/tree/main/clean-abap ) has been around for some time now.How did you implement it into you development routines?
-
Now Define Variables from Pre-existing Schemas
by Dan_Wroblewski on April 23, 2025 at 6:23 am
SAP Build Apps now has the ability to create page variables based on existing schemas, like from other variables, data entities, and most interestingly, process input and output schemas. This is really helpful.
-
Value Help with Additional Binding in RAP
by Vivek_Sahu_21 on April 22, 2025 at 8:50 am
Value Help with Additional Binding in RAP – Filtering Storage Locations by Plant in Material Stock Management Introduction:In this blog, I will demonstrate how to implement Value Help with Additional Binding in a RAP (RESTful ABAP Programming) application for the MM module. The scenario involves managing Material Stock Information by providing value help for Plant (WERKS) and Storage Location (LGORT). The key feature is filtering the storage location list dynamically based on the selected plant, enhancing the user experience. When the user selects a Plant, the system automatically filters the Storage Location (LGORT) list to show only the storage locations related to the selected plant. The requirement is to build a Fiori-based RAP application that enables users to: Select a Plant from the dropdown. Dynamically filter the Storage Locations based on the selected Plant. Display and manage material stock details such as material number, plant, storage location, stock quantity, and unit of measure. Procedure: Key Tables Involved 1. I have created three custom tables, where Storage and plant table have records: zvs_dt_storage_l → Storage Locations table with data@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; }Table Data:zvs_dt_plant_dt → Plant Data@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); }Table Data:zvs_dt_mat_stock → Material Stock Data @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; } 2.1 Now create one interface view ‘ZVS_I_PLANT_DT’ on top of DB table 'ZVS_DT_PLANT_DT': - This view provides the value help for Plant (WERKS). I have used the annotation "@ObjectModel.resultSet.sizeCategory: #XS” which makes the value help appear as a dropdown. @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 }2.2 Now create interface view ‘ZVS_I_STORAGE_LC’ on top of DB table 'ZVS_DT_STORAGE_L': - This view filters Storage Locations by Plant using additional binding. This view selects Storage Location, Description, and Plant. The Plant field acts as the filter when the user selects a Plant. @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 }2.3 Now create interface view ‘ZVS_I_MAT_STOCK’ on top of DB table 'ZVS_DT_MAT_STOCK': - An Interface View (zvs_i_mat_stock) for fetching material stock data with associations to plant and storage location details. This view maps to the custom table zvs_dt_mat_stock. It includes fields for Material Number, Plant, Storage Location, Stock Quantity, and Unit of Measure @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 }3. Now create one consumption view ‘ZVS_C_MAT_STOCK’ on top of Interface view 'ZVS_I_MAT_STOCK': - The consumption view zvs_c_mat_stock projects the data from the interface view zvs_i_mat_stock. This view is intended to expose the data for transactional operations, such as create, update, and delete. The consumption view allows clients to interact with the data, but the actual persistence and behavior are managed through the behavior definition. @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 } 4. Define Metadata Extension Add Value Help with Additional Binding to your RAP Business Object. Here: This “@Consumption.valueHelpDefinition: [{ entity : { element: 'Werks' , name: 'zvs_i_plant_dt' } }]” Annotation to get the dropdown for WERKS(Plant) Field. This “@Consumption.valueHelpDefinition: [{ entity : { element: 'StorageLocation' , name: 'zvs_i_storage_lc' }, additionalBinding: [{ element: 'Plant' ,localElement: 'Werks' }] }]” Annotation here, for additional binding. So based on selected plant it will dynamically filter the storage location. The @Consumption.valueHelpDefinition annotation is used to define value help (similar to 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. entity: Specifies the data source for the value help. element: 'StorageLocation': The field in the current CDS view where the value help is applied. name: 'zvs_i_storage_lc': The CDS view providing the value help data. additionalBinding: Defines additional filtering criteria. element: 'Plant': The field in the value help entity used for filtering. localElement: 'Werks': The field in the current view that is used as the filtering key. Metadata Extension Code for your reference:@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; }5. Define Behavior DefinitionThe labst (Stock Quantity) and meins (Unit of Measure) fields are read-only. The behavior definition supports CRUD operations. Now create a behavior definition on top of interface view 'ZVS_I_MAT_STOCK': managed implementation in class zbp_vs_i_mat_stock unique; strict ( 2 ); define behavior for zvs_i_mat_stock //alias <alias_name> persistent table zvs_dt_mat_stock lock master authorization master ( instance ) //etag master <field_name> 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; } }Now create a behavior definition on top of consumption view 'ZVS_C_MAT_STOCK': projection; strict ( 2 ); define behavior for zvs_c_mat_stock //alias <alias_name> { use create; use update; use delete; }Go to front-end:Here I will create one new record: You can see here, all the storage location are coming if we didn’t provide plant: So we'll select plant 1000 from dropdown: You can see here storage location got filtered based on the selected plant: If I select plant 1400, so based on that we are getting 0006 storage location: After saving record you can see the result: Conclusion:This blog showed how to create a RAP-based Fiori app with value help that filters storage locations based on the selected plant. Using CDS views, associations, and annotations, the app becomes user-friendly, efficient, and easy to maintain.
-
Function module in rap
by Dadapeer on April 22, 2025 at 8:50 am
Introduction This blog provides a step-by-step guide on creating a Product Management App 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.Procedure: Created one database table 'ZDP_DT_UTCL_PDT@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; } Now we need to create interface view on top of DB table.@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 } Now we need to create a projection view on top of Interface view. @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 } Now we need to create the behavior definition on top of interface view.I selected the unmanaged scenario in the behavior definition because, in my case, the CRUD operations are performed using a function module. If I had chosen the managed scenario, the RAP framework would have handled the CRUD operations automatically, but since my logic is custom and implemented through a function module, ABAP in Eclipse Create Function Group and Function Module.Type func in the search box and select ABAP Function Group from the suggestions. Click Next. Give the package name, function group name and description. type function and select Abap function module. Click Next. Provide Name, description and function group name. We get an empty function module.In this function module, we need to code the import, exporting, changing or exceptions parameters as required. Code of Behavior Definition of Interface: 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 <field_name> { 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; } } Now create a behavior definition on top of consumption view: Code of Behavior Definition of Projection: projection; strict ( 2 ); define behavior for zdp_P_cds_utcl_prdt alias utcl_prdt { use create; use update; use delete; } Behavior implementation.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->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(<fs_u>). ls_update = CORRESPONDING #( <fs_u> MAPPING FROM ENTITY ). ls_c = CORRESPONDING #( <fs_u> 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(<fs_e>) GROUP BY <fs_e>-%key. CALL FUNCTION 'ZDP_FM_PRDT_READ1' EXPORTING lv_pid = <fs_e>-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=>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. create operation.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->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. Function module product create. 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_tab = ls_tab1. ENDIF. ENDFUNCTION. Here gt_tab is a global internal table which I have created in global class. Update operation logic. 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(<fs_u>). ls_update = CORRESPONDING #( <fs_u> MAPPING FROM ENTITY ). ls_c = CORRESPONDING #( <fs_u> 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. Update function module logic. 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=>gs_tab_u = ls_prd_n. msg = 'Sucessfully Updated'. ENDIF. ENDFUNCTION. Delete operation logic. METHOD delete. DATA(lv_id) = keys[ 1 ]-ProductId. CALL FUNCTION 'ZDP_FM_PRDT_DELETE' EXPORTING lv_prd_id = lv_id. ENDMETHOD. Delete function module logic. FUNCTION zdp_fm_prdt_delete IMPORTING lv_prd_id TYPE zdp_de_pdt_id. zcl_dp_utcl_prdt_um_helper=>prt_id = lv_prd_id. ENDFUNCTION. Read operation logic. METHOD read. LOOP AT keys ASSIGNING FIELD-SYMBOL(<fs_e>) GROUP BY <fs_e>-%key. CALL FUNCTION 'ZDP_FM_PRDT_READ1' EXPORTING lv_pid = <fs_e>-ProductId IMPORTING lt_tab = gs_prdt. INSERT CORRESPONDING #( gs_prdt MAPPING TO ENTITY ) INTO TABLE result. ENDLOOP. ENDMETHOD. Read function module logic. 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. save operation logic. METHOD save. CALL FUNCTION 'ZDP_FM_SAVE_1'. ENDMETHOD. Save function module logic. FUNCTION zdp_fm_save_1. IF zcl_dp_utcl_prdt_um_helper=>gt_tab IS NOT INITIAL. INSERT zdp_dt_utcl_pdt FROM @zcl_dp_utcl_prdt_um_helper=>gt_tab. ENDIF. if zcl_dp_utcl_prdt_um_helper=>gs_tab_u is NOT INITIAL. UPDATE zdp_dt_utcl_pdt FROM @zcl_dp_utcl_prdt_um_helper=>gs_tab_u. ENDIF. if zcl_dp_utcl_prdt_um_helper=>prt_id is not initial. DELETE FROM zdp_dt_utcl_pdt WHERE product_id = @zcl_dp_utcl_prdt_um_helper=>prt_id. ENDIF. ENDFUNCTION. Output: Go to front-endHere I will create one new record: Delete product Here we are delete super cementUpdate gold cement product quantity.
-
Deploying a CAP app with Fiori app standalone (HTML5 Repo Host)
by Christopher_Ketzler on April 22, 2025 at 8:49 am
Looking to deploy a CAP app with a Fiori UI—without SAP Workzone or Launchpad? Discover a practical, step-by-step guide to deploying your Fiori-enabled CAP application as a standalone app using the HTML5 Application Repository. Learn how to configure your approuter and extend configurations.
-
Unlocking the Potential of SAP Build Process Automation with UiPath
by Joanna_Drinnan on April 17, 2025 at 10:28 pm
The rise of artificial intelligence (AI) and robotic process automation (RPA) technologies have empowered companies to leave behind manual, error-prone, and time-consuming work for streamlined operations, customized processes, and end-to-end automated solutions. The most competitive enterprises today are developing a lasting edge by taking advantage of these capabilities.
-
SAP Developer News April 17th, 2025
by Eberenwaobiora on April 17, 2025 at 7:10 pm
This week's episode contains segments on Upcoming Removal of SAP Fiori Themes Belize and Blue Crystal, UI5 Renovate Preset Config Tool, New SQL on Files Use Cases with SAP HANA Cloud QRC 01,SAP BTP Cloud Foundry switching to higher security level Root Certificate Authority,Migration Tooling now supporting migration via the pipeline approach.
-
Introducing the New Project Creation Wizard within SAP Build Lobby
by schat on April 17, 2025 at 12:20 pm
We are pleased to announce the launch of an enhanced Project Creation Wizard in the SAP Build Lobby, designed to accelerate and streamline your project initiation processes. This new tool reflects our commitment to incorporating user feedback and optimizing your interaction with SAP Build.
-
New Basic Trial available: Get your hands on Enterprise Automation with SAP
by Manuel_Namyslo on April 16, 2025 at 8:25 am
In today’s fast-paced business landscape, automation has emerged as a crucial competitive differentiator. While most stakeholders understand its strategic value and potential benefits, many organizations still face significant challenges on the path to becoming truly autonomous.Enterprise Automation with SAP is the only solution that offers a comprehensive, integrated suite of tools for process mining, automation, integration, monitoring, and continuous optimization of workflows. It empowers businesses to streamline operations and drive intelligent decision-making—without adding unnecessary complexity. With SAP Signavio, SAP Build, and SAP Integration Suite, customers gain access to best-in-class capabilities. These tools work in harmony, enabling users to quickly uncover insights, automate processes, and integrate seamlessly with existing applications to deliver the right outcomes.Now, there’s a powerful new way to explore the full potential of Enterprise Automation with SAP. The new Basic Trial gives you hands-on access to SAP Build, SAP Integration Suite, and SAP Signavio—all in one end-to-end scenario. It’s the perfect opportunity to experience the value of automation in action. In order to activate your basic trial, you need to navigate to the Enterprise Automation webpage on SAP.com:Access to Basic Trial for Enterprise AutomationAs the next step you need to click on the ,,Start your free 30-day trial" button that will forward you to the next page. You are now being forwarded to the Basic Trial page of SAP Build Process Automation, here you simply click on ,,Try now". This process will generate log-in credentials which you can use right away to start your hands-on tutorial. After logging in you will be redirected to the tutorial landing page. As a last step you need to select the Enterprise Automation workbook and start your tutorial: This includes a 2 hour hands-on exercise with 5 different units, which you can discover at your own pace: If you want to learn more about the Basic Trial and the use case, that is being covered in this hands-on feel free to listen to our webcast, which you can find here. Happy hacking!
-
SAP Community Escape House for April – SAP Build 👷♀️🏠
by Dan_Wroblewski on April 14, 2025 at 6:29 am
The SAP Community Escape House has a new addition!!We've built a garage where we have set up a workshop to build all kinds of stuff using SAP Build. Use your wits to try to escape.
-
SAP Developer News April 10th, 2025
by Eberenwaobiora on April 10, 2025 at 7:16 pm
This week's episode contains segments on Shaping the Future of Data and AI with SAP HANA Cloud,SAP Open Source Webinar Apeiro Reference Architecture,CAP March 8.9 Release,Free SAP Certification and practice systems for students and lecturers,Welcome to SAP BusinessObjects BI 2025.
-
A journey from Netweaver 7.50 to S/4HANA - Part 2: What's Cooking?
by BaerbelWinkler on April 10, 2025 at 2:26 pm
This is the 2nd part outlining our journey to S/4HANA in which I'll explain some of the preparatory tasks to be able to hit the road running once the development systems has been converted and we can start the manual code conversion.
-
Providing initial data to your tenants in your multi-tenant application (CAP JAVA)
by NicholasArefta on April 9, 2025 at 6:21 am
🟥 The ProblemWe all know that we can use .csv files to populate our tables, but as recommended by capire doc, we should only use .csv files as ‘configuration data’. This is because when it comes to deploying to SAP HANA these .csv files completely overwrite all previous data as per the Danger Label on capire. capire tip capire danger label This poses the question, how do we provide initial data to our tenants while also letting them modifying the data for their use?📘InstructionsNavigate to your tenant upgrade hook, for this example we will run the upgrade task as a Cloud Foundry hook located in the sidecar module of your mta.yaml file.https://cap.cloud.sap/docs/guides/multitenancy/?impl-variant=node#update-database-schemaInstead of using the default cds-mtx upgrade command, we will use the exclude_filter option to prevent your CSV files from being included in the upgrade. This option will allow us to choose the csv files that we would like to exclude from future upgrades in-order to prevent overwriting data.Refer to https://www.npmjs.com/package/@sap/hdi-deploy for more details.Make sure to update your configuration as follows:# --- HOOKS ------ hooks: # ---------------- - name: upgrade-tenants type: task parameters: name: upgrade memory: 512M disk-quota: 768M command: cds-mtx upgrade "*" --body '{"_":{"hdi":{"deploy":{"exclude_filter":["src/gen/data/"] } } } }'Ensure that you copy the upgrade options accurately!Once you have deployed the latest version of your application with new initial data, all newly subscribing tenants will receive the updated initial data. Meanwhile, existing tenants using the upgrade hook command will retain their initial data along with any modifications they have made.What if you have created a brand new table that you want to deliver to existing tenants?Simple, we shall use the include_filter option to select both the .hdbtabledata and .csv files that you want to add. Be careful not to include any tables or csv files that already exist as you will just overwrite that data.In the diagram above Tenant 1 is subscribed before the 2nd deploy (this deploy includes changes to schema + data). Tenant 1 will only receive the database schema changes and none of the data, while Tenant 2 will receive the latest changes (schema changes + data). sequence diagramThe scenario above shows the same sequence of events as described as previously.
-
New documentation available: Extend your SAP S/4HANA Processes with SAP Build Process Automation
by Manuel_Namyslo on April 8, 2025 at 8:16 am
"Intelligent Processes Are Integrated Processes"If you're already automating and extending business processes on the SAP Business Technology Platform, you're likely thinking about the best way to connect your new process extensions to the relevant systems of record. This also includes SAP Build Process Automation, which serves as our central development environment for creating intelligent automations and orchestrating end-to-end business processes across complex IT landscapes.The good news is that there is now a comprehensive new guide available on the SAP Help Portal to help with this process:Extend and Integrate Your SAP S/4HANA Processes with SAP Build Process AutomationThis guide walks you through the integration setup and management of SAP Build Process Automation with SAP S/4HANA or SAP S/4HANA Cloud. It is primarily aimed at system administrators and technical consultants who support customers using SAP S/4HANA or SAP S/4HANA Cloud and wish to extend their ERP capabilities by integrating with SAP Build Process Automation.Covering the most crucial activities for successful integration, this guide is your go-to resource for connecting SAP Build Process Automation with SAP S/4HANA or SAP S/4HANA Cloud.But first of all what is SAP Build Process Automation?SAP Build Process Automation is a solution designed for citizen developers to adapt, enhance, and innovate business processes through no-code workflow management and robotic process automation.It empowers both business users and technologists to take on the role of citizen developers. With its powerful, yet user-friendly low-code and no-code features, SAP Build Process Automation allows you to drive automation by leveraging the expertise of your citizen developer community. It allows you to:Digitalize, extend, or adapt existing processes and orchestrate new end-to-end processesSeamlessly leverage UiPath capabilities with SAP Build Process Automation add-on by UiPathBuild RPA bots to handle repetitive tasks so users can focus on higher-value goalsAccelerate document-heavy processes with intelligent document processingConnect process and task automations to any SAP or non-SAP application What can you learn in this new documentation?You should certainly bookmark this documentation to learn how to integrate your custom processes and automations with SAP S/4HANA, and extend standardized workflows to meet specific business requirements.The runtime setup involves several configurations that must be executed in both the SAP Business Technology Platform (BTP) cockpit and the SAP Build Lobby. In this guide, we’ll walk through a common use case: pushing and retrieving business partner master data into an SAP S/4HANA system, whether on-premise or in the cloud.Below, you can review the final architectures that you can achieve once you’ve completed the documentation. First, here’s the technical architecture for integrating SAP Build Process Automation with SAP S/4HANA on-premise: Next, here’s the technical architecture for integrating SAP Build Process Automation with SAP S/4HANA Cloud: ConclusionIt’s important to note that this documentation focuses on the integration between SAP Build Process Automation and SAP S/4HANA. However, the same approach can be applied to integrate with other SAP applications, such as SAP SuccessFactors, SAP Ariba, or SAP Customer Experience, as well as third-party systems. The setup can also be reused for processing various types of transactional or master data within a system of record. I hope this new asset will help you to simplify and standardize the connectivity o custom processes and automations to your existing backend applications.
-
Global Feature Contron in RAP, Feature control in RAP
by Krishna_karale on April 8, 2025 at 8:11 am
Hello Everyone, In this blog post we are going to Learn about Global Feature Control in RAP. 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. Delete (features: global); To implement a global feature, the "get_global_features" method must be implemented. As previously described, there is no transfer of the keys from the outside. The derivation of the various features must be based on other criteria. Procedure:Here we are defining a Custom table (ZKK_DT_TRAVEL). 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. @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; }The Above table holds the records which are given below For this database table(ZKK_DT_TRAVEL) we are defining the Basic view/Interface view. The ZKK_I_TRAVEL is a CDS view based on the zkk_dt_travel 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. @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 } Now I am defining the Projection view on top of interface view. @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 }Now I am defining the Metadata Extension for projection View.@Metadata.layer: #PARTNER annotate entity ZKK_C_TRAVEL with { @ui.facet: [{ purpose: #STANDARD, position: 10, type : #IDENTIFICATION_REFERENCE, label: 'Travel Details' }] @ui.identification: [{ position: 10, label: 'Travel Id' }] @ui.lineItem: [{ label: 'Travel Id' }] TravelId; @ui.identification: [{ position: 20, label: 'Agency Id' }] @ui.lineItem: [{ label: 'Agency Id' }] AgencyId; @ui.identification: [{ position: 30, label: 'Customer Id' }] @ui.lineItem: [{ label: 'Customer Id' }] CustomerId; @ui.identification: [{ position: 40, label: 'Begin Date' }] @ui.lineItem: [{ label: 'Begin Date' }] BeginDate; @ui.identification: [{ position: 50, label: 'End Date' }] @ui.lineItem: [{ label: 'End Date' }] EndDate; @ui.identification: [{ position: 60, label: 'Booking Fee' }] @ui.lineItem: [{ label: 'Booking Fee' }] bookingfee; @ui.identification: [{ position: 70, label: 'Total Price' }] @ui.lineItem: [{ label: 'Total Price' }] TotalPrice; @ui.identification: [{ position: 80, label: 'Status Confirm' }, { type: #FOR_ACTION, dataAction: 'status', label: 'Status Confirm' } ] @ui.lineItem: [{ label: 'Status' }, { type: #FOR_ACTION, dataAction: 'status', label: 'Confirm Status' }] status; @ui.identification: [{ position: 100, label: 'Currency' }] @ui.lineItem: [{ label: 'Currency' }] CurrencyCode; @ui.identification: [{ position: 90, label: 'Description' }] @ui.lineItem: [{ label: 'Description' }] Description; @ui.identification: [{ position: 110, label: 'Created By' }] @ui.lineItem: [{ label: 'Created By' }] CreatedBy; @ui.identification: [{ position: 120, label: 'Created At' }] @ui.lineItem: [{ label: 'Created At' }] CreatedAt; @ui.identification: [{ position: 130, label: 'Last Changed By' }] @ui.lineItem: [{ label: 'Last Changed By' }] LastChangedBy; @ui.identification: [{ position: 140, label: 'Last Changed At' }] @ui.lineItem: [{ label: 'Last Chnaged at' }] LastChangedAt; }Lets Define the Behaviour Definition.managed implementation in class zbp_kk_i_travel unique; strict ( 2 ); define behavior for ZKK_I_TRAVEL alias Travel //alias <alias_name> persistent table zkk_dt_travel lock master authorization master ( instance ) //etag master <field_name> { 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; } }Here in Behavior definition, we have to specify delete (features: global); as we are performing delete operation After that we will get a warning as given below When we double click on that warning GET_GLOBAL_FEATURE method will get automatically. You can observe there is no KEYS parameter, that means we cannot read the details of selected record from here, we will get only the global data. Now we have to implement our custom logic inside the above given method. METHOD get_global_features. if requested_features-%delete = if_abap_behv=>mk-on. Data(lv_result) = COND #( when cl_abap_context_info=>get_user_alias( ) = 'xyz@gmail.com' then if_abap_behv=>mk-on else if_abap_behv=>mk-off ). result-%delete = lv_result. ENDIF. ENDMETHOD. 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'.Above logic is to make delete button enable or disable When the user is xyz@gmail.com then the delete button will be enabled (Requested features contains the actions which are specified with (features: global)) Preview the data The user logged-in is xyz@gmail.com The delete button is enabled Rewriting the code When the user is xyz@gmail.com then i want the delete button to be disabled METHOD get_global_features. if requested_features-%delete = if_abap_behv=>mk-on. Data(lv_result) = COND #( when cl_abap_context_info=>get_user_alias( ) = 'xyz@gmail.com' then if_abap_behv=>mk-off else if_abap_behv=>mk-on ). result-%delete = lv_result. ENDIF. ENDMETHOD.When we RUN the application. We dont get Delete option as enables, here it is disable for that particular user.Conclusion: Global feature control in RAP provides a centralized way to enable or disable specific operations across the entire application, regardless of individual records or user roles. It ensures consistent enforcement of business rules by globally restricting actions like create, update, or delete. This approach simplifies feature management, enhances data security, and ensures uniform behavior, making it an effective mechanism for controlling access and operations at the application level.
-
SAP Developer News April 3rd, 2025
by Eberenwaobiora on April 4, 2025 at 9:50 am
This week's episode contains segments on Developer Challenge, Generative AI in SAP Build Process Automation, Generative AI in SAP Build Apps, SAP BTP Usability Event – HANA Cloud.