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 Model
We begin with the creation of a transparent table ZMUK_DT_VEHICLE to store vehicle master data.
Database Table: ZMUK_DT_VEHICLE
The 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;
}
Conclusion
This 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.
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. Read More Application Development and Automation Blog Posts articles
#SAP