This is continuation of the Unmanaged RAP Scenario from the RAP series.
The previous posts –
ABAP RESTful Application Programming Model [4] – Unmanaged Scenario Part 1 covered creation of CDS View Entities, Projection Entities and Metadata Extensions.
ABAP RESTful Application Programming Model [5] – Unmanaged Scenario Part 2 covered creation of Behavior Definition, Service Definition, Service Binding and Testing the service.
Now comes probably the most important part – Implementing the behavior in the class.
Let us look at the behavior definition one more time.
unmanaged implementation in class zbp_i_jp_travel unique;
//strict;
define behavior for ZI_JP_TRAV_01 alias Travel
//late numbering
lock master
//authorization master(global)
etag master LastChangedAt
{
field ( readonly ) TravelID, TotalPrice;
field ( mandatory ) AgencyID, CustomerID, BeginDate, EndDate;
create;
update;
delete;
action ( features : instance ) set_status_booked result [1] $self;
association _Booking { create ( features : instance ); }
mapping for /dmo/travel control /dmo/s_travel_intx
{
AgencyID = agency_id;
BeginDate = begin_date;
BookingFee = booking_fee;
CurrencyCode = currency_code;
CustomerID = customer_id;
EndDate = end_date;
Status = status;
TotalPrice = total_price;
Description = description;
TravelID = travel_id;
}
//mapping for zjp_rap_trav_01 control
}
define behavior for zi_JP_book_01 alias Booking
implementation in class zbp_i_jp_booking unique
//late numbering
lock dependent by _Travel
//authorization dependent by _Travel
etag dependent by _Travel
{
field ( readonly ) TravelID, BookingID;
field ( mandatory ) BookingDate, CustomerID, AirlineID, ConnectionID, FlightDate;
update;
delete;
association _Travel;
mapping for /dmo/booking control /dmo/s_booking_intx
{
BookingID = booking_id;
AirlineID = carrier_id;
BookingDate = booking_date;
ConnectionID = connection_id;
CurrencyCode = currency_code;
CustomerID = customer_id;
FlightDate = flight_date;
FlightPrice = flight_price;
TravelID = travel_id;
}
}
The service has two Entities i.e. Travel and Booking. Each will be implemented in related class. The classes are mentioned in the below statements from the behavior code.
unmanaged implementation in class zbp_i_jp_travel unique;
define behavior for zi_JP_book_01 alias Booking
implementation in class zbp_i_jp_booking unique
Let us take a look at the classes.
ZBP_I_JP_TRAVEL
The global class is created as an abstract class and the actual methods are implemented in the Local Types tab highlighted below.

The Local Types has a behavior implementer and behavior saver classes as shown below. Initially all the methods will be empty.


ZBP_I_JP_BOOKING
The booking class only has behavior handler and not the saver as saver is only in parent entity.

Travel Entity Behavior Implementation
Create Travel
Where the operation is defined in behavior definition?

Method to be implemented – Create
Method definition shows that it imports parameter entities.
METHODS create FOR MODIFY
IMPORTING entities FOR CREATE Travel.
Now, we come to the code explanation. The code is written using ABAP expressions / new syntax / ABAP 7.4+ syntax. So if you do not understand the syntax head over to page ABAP Expressions (7.4+) for help.
- Create method is going to be called for one travel at a time, however the data is sent in a table entities so we need to loop through.
- We then have to map the data to target structure using key word MAPPING FROM ENTITY USING CONTROL
- After this we can call BAPI / FM or any other custom logic to create the entity. Here FM /DMO/FLIGHT_TRAVEL_CREATE is used.
- Once the data is created, output is updated in table mapped-travel. Here mapped is going to be constant in all such classes you will implement. This is required so that the Travel Id that is created is passed back to front end and the new Travel Id can be shown on the app.
- It is important to pass back field %cid from the input and the key fields of the entity. In this case the key is travelid.

- In case there are errors and the data is not created, the messages can be passed back using tables failed-travel and reported-travel. Similar to mapped, the failed and reported parameters will be constant.
- This is required to inform the frontend that there is a failure (failed) and to show the actual messages (reported)
- failed-travel – here, %cid is mandatory. reported-travel additionally has %create parameter where we need to pass ‘X’.
Here is the complete code for the method to try out.
METHOD create.
DATA : ls_travel TYPE /dmo/travel,
lt_msg TYPE /dmo/t_message.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<lfs_travel_entity>).
ls_travel = CORRESPONDING #( <lfs_travel_entity> MAPPING FROM ENTITY USING CONTROL ).
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_CREATE'
EXPORTING
is_travel = CORRESPONDING /dmo/s_travel_in( ls_travel )
IMPORTING
es_travel = ls_travel
et_messages = lt_msg.
IF lt_msg IS INITIAL.
mapped-travel = VALUE #( BASE mapped-travel
( %cid = <lfs_travel_entity>-%cid
travelid = ls_travel-travel_id
) ).
ELSE.
LOOP AT lt_msg INTO DATA(ls_msg).
APPEND VALUE #( %cid = <lfs_travel_entity>-%cid
travelid = <lfs_travel_entity>-TravelID )
TO failed-travel.
APPEND VALUE #( %msg = new_message( id = ls_msg-msgid
number = ls_msg-msgno
v1 = ls_msg-msgv1
v2 = ls_msg-msgv2
v3 = ls_msg-msgv3
v4 = ls_msg-msgv4
severity = if_abap_behv_message=>severity-error )
%key-TravelID = <lfs_travel_entity>-TravelID
%cid = <lfs_travel_entity>-%cid
%create = 'X'
TravelID = <lfs_travel_entity>-TravelID )
TO reported-travel.
ENDLOOP.
ENDIF.
ENDLOOP.
Once the code is added move to the saver local class towards the end and add below code to method save.
METHOD save.
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_SAVE'.
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_INITIALIZE'.
ENDMETHOD.
Activate the class and then test the Create Travel behavior.
1. Click create.

2. Notice the blank travel id. Enter data in all fields and click Create.

3. The Travel id is created. Currently there are no bookings but the booking section appears where bookings can be added.

4. In case you need to debug, put the breakpoint by double clicking at the highlighted place.

5. The debug perspective is presented in Eclipse.

Note : Don’t forget to add code in saver class in save method – else the data will not be saved.

Now, it is not possible to explain everything in detail in the blog, so here is the rest of the code that you can try out. Do reach out in case explanation is needed. I have tried to highlight important points.
Update Travel
- Data comes in entities
- mapped-travel is not required to be filled as all the data is available in frontend already
- failed-travel & reported-travel to be filled if error occurs
- Input’s %cid_ref field is to be used to pass data to failed-travel-%cid
METHOD update.
DATA : ls_travel TYPE /dmo/travel,
ls_travelx TYPE /dmo/s_travel_inx,
lt_msg TYPE /dmo/t_message.
DATA ls_message TYPE REF TO if_abap_behv_message.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<lfs_travel_entity>).
ls_travel = CORRESPONDING #( <lfs_travel_entity> MAPPING FROM ENTITY ).
ls_travelx-travel_id = <lfs_travel_entity>-TravelID.
ls_travelx-_intx = CORRESPONDING #( <lfs_travel_entity> MAPPING FROM ENTITY ).
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_UPDATE'
EXPORTING
is_travel = CORRESPONDING /dmo/s_travel_in( ls_travel )
is_travelx = ls_travelx
IMPORTING
et_messages = lt_msg.
IF lt_msg IS NOT INITIAL.
LOOP AT lt_msg INTO DATA(ls_msg) WHERE msgty CA 'EA'.
APPEND VALUE #( %cid = <lfs_travel_entity>-%cid_ref
travelid = <lfs_travel_entity>-TravelID )
TO failed-travel.
APPEND VALUE #( %msg = new_message( id = ls_msg-msgid
number = ls_msg-msgno
v1 = ls_msg-msgv1
v2 = ls_msg-msgv2
v3 = ls_msg-msgv3
v4 = ls_msg-msgv4
severity = if_abap_behv_message=>severity-error )
%key-TravelID = <lfs_travel_entity>-TravelID
%cid = <lfs_travel_entity>-%cid_ref
%update = 'X'
TravelID = <lfs_travel_entity>-TravelID )
TO reported-travel.
ENDLOOP.
ENDIF.
ENDLOOP.
ENDMETHOD.
Delete Travel
- Input is keys table, as for delete you don’t need complete data
- mapped-travel is not required to be filled as all the data is available in frontend already
- failed-travel & reported-travel to be filled if error occurs
- Input’s %cid_ref field is to be used to pass data to failed-travel-%cid
METHOD delete.
DATA : lt_msg TYPE /dmo/t_message.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<lfs_del_keys>).
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_DELETE'
EXPORTING
iv_travel_id = <lfs_del_keys>-TravelID
IMPORTING
et_messages = lt_msg.
IF lt_msg IS NOT INITIAL.
LOOP AT lt_msg INTO DATA(ls_msg) WHERE msgty CA 'EA'.
APPEND VALUE #( %cid = <lfs_del_keys>-%cid_ref
travelid = <lfs_del_keys>-TravelID
) TO failed-travel.
APPEND VALUE #( %msg = new_message( id = ls_msg-msgid
number = ls_msg-msgno
v1 = ls_msg-msgv1
v2 = ls_msg-msgv2
v3 = ls_msg-msgv3
v4 = ls_msg-msgv4
severity = if_abap_behv_message=>severity-error )
%key-TravelID = <lfs_del_keys>-TravelID
%cid = <lfs_del_keys>-%cid_ref
%delete = 'X'
TravelID = <lfs_del_keys>-TravelID
) TO reported-travel.
ENDLOOP.
ENDIF.
ENDLOOP.
ENDMETHOD.
Set to book
This is additional button, which needs to be active only for the rows where status is not already booked i.e. ‘B’.

Behavior defines this as below. The action is defined as set_status_booked. Result is [1] i.e. single record which is $self i.e. the record that is passed is updated.
( features : instance ) – Instance Feature Control can be defined for fields, operation or action. This is a way to enable or disable certain field/operation/action based on a state of the entity.
If booking status is other than ‘B’, enable the button else disable the button.

Here is how the Instance Feature Control is implemented in the class.
Implement method read –
We need to populate the output table result based on input table keys.
METHOD read.
SELECT * FROM zi_rap_trav_01
FOR ALL ENTRIES IN @keys
WHERE TravelId = @keys-TravelId
into CORRESPONDING FIELDS OF table @result.
ENDMETHOD.
Implement method get_instance_features –
Here, we first need to call READ ENTITIES which in turn calls the method read and then populate table result.
%features-%action-set_status_booked is set to enabled or disabled. Similar logic can be used to enable / disable other actions as well.
METHOD get_instance_features.
READ ENTITIES OF ZI_RAP_TRAV_01 IN LOCAL MODE
ENTITY travel
FIELDS ( travelID status )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel_result)
FAILED failed.
result =
VALUE #( FOR ls_travel IN lt_travel_result
( %key = ls_travel-%key
%features-%action-set_status_booked = COND #( WHEN ls_travel-status = 'B'
THEN if_abap_behv=>fc-o-disabled
ELSE if_abap_behv=>fc-o-enabled )
) ).
ENDMETHOD.
Booking Entity Behavior Implementation
Booking Create
The booking operations are split into two classes. The create is implemented in travel related class itself. The create operation for this association _Booking is in the Travel behavior.

Method cba_Booking
- This is a lot of code. In short, we get a travel id here, based on the travel id we fetch all bookings.
- Pick up the largest booking number and add new booking numbers with incrementing by 1 and put the data
- Then call FM /DMO/FLIGHT_TRAVEL_UPDATE to update/add the booking and pass the booking id to mapped-booking / failed-booking depending on whether or not the booking update is successful.
METHOD cba_Booking.
DATA : lt_booking TYPE /dmo/t_booking,
lt_msg TYPE /dmo/t_message,
lt_msg_b TYPE /dmo/t_message.
LOOP AT entities_cba ASSIGNING FIELD-SYMBOL(<lfs_travel_booking>).
DATA(lv_travel_id) = <lfs_travel_booking>-TravelId.
"Get Travel and Booking Data
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_READ'
EXPORTING
iv_travel_id = lv_travel_id
IMPORTING
et_booking = lt_booking
et_messages = lt_msg.
IF lt_msg IS INITIAL.
IF lt_booking IS NOT INITIAL.
DATA(lv_last_booking_id) = lt_booking[ lines( lt_booking ) ]-booking_id.
ELSE.
CLEAR lv_last_booking_id.
ENDIF.
LOOP AT <lfs_travel_booking>-%target ASSIGNING FIELD-SYMBOL(<lfs_booking>).
DATA(ls_booking) = CORRESPONDING /dmo/booking( <lfs_booking> MAPPING FROM ENTITY USING CONTROL ).
lv_last_booking_id += 1.
ls_booking-booking_id = lv_last_booking_id.
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_UPDATE'
EXPORTING
is_travel = VALUE /dmo/s_travel_in( travel_id = lv_travel_id )
is_travelx = VALUE /dmo/s_travel_inx( travel_id = lv_travel_id )
it_booking = VALUE /dmo/t_booking_in( ( CORRESPONDING #( ls_booking ) ) )
it_bookingx = VALUE /dmo/t_booking_inx( ( booking_id = ls_booking-booking_id
action_code = /dmo/if_flight_legacy=>action_code-create ) )
IMPORTING
et_messages = lt_msg_b.
"Pass data back to UI
INSERT VALUE #( %cid = <lfs_booking>-%cid
travelid = lv_travel_id
bookingid = ls_booking-booking_id
) INTO TABLE mapped-booking.
LOOP AT lt_msg_b INTO DATA(ls_msg) WHERE msgty CA 'EA'.
APPEND VALUE #( %cid = <lfs_booking>-%cid
travelid = lv_travel_id
bookingid = ls_booking-booking_id
) TO failed-booking.
APPEND VALUE #( %msg = new_message( id = ls_msg-msgid
number = ls_msg-msgno
v1 = ls_msg-msgv1
v2 = ls_msg-msgv2
v3 = ls_msg-msgv3
v4 = ls_msg-msgv4
severity = if_abap_behv_message=>severity-error )
%key-TravelID = lv_travel_id
%key-bookingid = ls_booking-booking_id
%cid = <lfs_booking>-%cid
TravelID = lv_travel_id
bookingid = ls_booking-booking_id
) TO reported-booking.
ENDLOOP.
ENDLOOP.
ELSE.
LOOP AT lt_msg INTO ls_msg WHERE msgty CA 'EA'.
APPEND VALUE #( %cid = <lfs_travel_booking>-%cid_ref
travelid = lv_travel_id
) TO failed-travel.
APPEND VALUE #( %msg = new_message( id = ls_msg-msgid
number = ls_msg-msgno
v1 = ls_msg-msgv1
v2 = ls_msg-msgv2
v3 = ls_msg-msgv3
v4 = ls_msg-msgv4
severity = if_abap_behv_message=>severity-error )
%key-TravelID = lv_travel_id
%cid = <lfs_travel_booking>-%cid_ref
TravelID = lv_travel_id
) TO reported-travel.
ENDLOOP.
ENDIF.
ENDLOOP.
ENDMETHOD.
Booking update and delete are implemented in class zbp_i_jp_booking. In both methods, FM /DMO/FLIGHT_TRAVEL_UPDATE is called with update / delete flags. In many actual scenarios, delete would mean setting up deletion flag or delete would not be an option at all.
Booking update
METHOD update.
DATA : lt_msg TYPE /dmo/t_message.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<lfs_booking>).
DATA(ls_booking) = CORRESPONDING /dmo/booking( <lfs_booking> MAPPING FROM ENTITY ).
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_UPDATE'
EXPORTING
is_travel = VALUE /dmo/s_travel_in( travel_id = <lfs_booking>-TravelID )
is_travelx = VALUE /dmo/s_travel_inx( travel_id = <lfs_booking>-TravelID )
it_booking = VALUE /dmo/t_booking_in( ( CORRESPONDING #( ls_booking ) ) )
it_bookingx = VALUE /dmo/t_booking_inx( ( booking_id = <lfs_booking>-BookingID
_intx = CORRESPONDING #( <lfs_booking> MAPPING FROM ENTITY )
action_code = /dmo/if_flight_legacy=>action_code-update ) )
IMPORTING
et_messages = lt_msg.
"Pass data back to UI
INSERT VALUE #( %cid = <lfs_booking>-%cid_ref
travelid = <lfs_booking>-TravelID
bookingid = <lfs_booking>-BookingID
) INTO TABLE mapped-booking.
LOOP AT lt_msg INTO DATA(ls_msg) WHERE msgty CA 'EA'.
APPEND VALUE #( %cid = <lfs_booking>-%cid_ref
travelid = <lfs_booking>-TravelID
bookingid = <lfs_booking>-BookingID
) TO failed-booking.
APPEND VALUE #( %msg = new_message( id = ls_msg-msgid
number = ls_msg-msgno
v1 = ls_msg-msgv1
v2 = ls_msg-msgv2
v3 = ls_msg-msgv3
v4 = ls_msg-msgv4
severity = if_abap_behv_message=>severity-error )
%key-TravelID = <lfs_booking>-TravelID
%key-bookingid = ls_booking-booking_id
%cid = <lfs_booking>-%cid_ref
%update = 'X'
TravelID = <lfs_booking>-TravelID
bookingid = <lfs_booking>-BookingID
) TO reported-booking.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
Booking delete
METHOD delete.
DATA : lt_msg TYPE /dmo/t_message.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<lfs_booking>).
CALL FUNCTION '/DMO/FLIGHT_TRAVEL_UPDATE'
EXPORTING
is_travel = VALUE /dmo/s_travel_in( travel_id = <lfs_booking>-TravelID )
is_travelx = VALUE /dmo/s_travel_inx( travel_id = <lfs_booking>-TravelID )
it_booking = VALUE /dmo/t_booking_in( ( booking_id = <lfs_booking>-BookingID ) )
it_bookingx = VALUE /dmo/t_booking_inx( ( booking_id = <lfs_booking>-BookingID
action_code = /dmo/if_flight_legacy=>action_code-delete ) )
IMPORTING
et_messages = lt_msg.
IF lt_msg IS NOT INITIAL.
LOOP AT lt_msg INTO DATA(ls_msg) WHERE msgty CA 'EA'.
APPEND VALUE #( %cid = <lfs_booking>-%cid_ref
travelid = <lfs_booking>-TravelID
bookingid = <lfs_booking>-BookingID
) TO failed-booking.
APPEND VALUE #( %msg = new_message( id = ls_msg-msgid
number = ls_msg-msgno
v1 = ls_msg-msgv1
v2 = ls_msg-msgv2
v3 = ls_msg-msgv3
v4 = ls_msg-msgv4
severity = if_abap_behv_message=>severity-error )
%key-TravelID = <lfs_booking>-TravelID
%key-bookingid = <lfs_booking>-BookingID
%cid = <lfs_booking>-%cid_ref
%delete = 'X'
TravelID = <lfs_booking>-TravelID
bookingid = <lfs_booking>-BookingID
) TO reported-booking.
ENDLOOP.
ENDIF.
ENDLOOP.
ENDMETHOD.
Now test all the functionality and you have a working service. This marks end of the 3 part series on unmanaged scenario. In next post we will see how the unmanaged service can be used to create a Fiori element app in Business Application Studio.
This has become a long post and I have tried to explain as much as I can. I know I have skipped few key words / concepts like strict, late numbering, etag master etc, which I will try to explain in some post later.
For now, try this, if you face issues – let me know in comments and I will try to get back as soon as I can.
Visit ABAP RESTful Application Programming Model to explore all articles on ABAP RAP Model.
If you like the content, please subscribe…
Hello, thank you for perfect post. Regarding unmanaged scenario, I have a question about custom query. Is it possible to have managed scenario and implement custom query (data selection from DB) or is it necessary to go through unmanaged scenario?
Example: I have two fields in DB: validity_from, validity_to. But in the Fiori, users would like to select data on the basis of entered “key date” and the selection should select data, where “validity_from < key_date < validity_to". And additionally… how to add "virtual" selection parametr "key date" to the List report, when it is not persisted field in DB?
Thank You
JJ
LikeLike
Hi JJ
I will also have to explore this. I will try to address these in the following posts.
LikeLike
Hello Jagadish,
Thanks for detailed explanation..how can we create a Web Api for the above RAP to post a json payload for deep insert scenario using the same travel booking model.
LikeLike
Hi Subba
While creating the service binding select the option ‘OData V4 – Web API’ instead of ‘OData V4 – UI’ for the Binding Type.
LikeLike