ABAP Restful Application Programming is an efficient and cloud-compatible development model that enables rapid creation of Fiori apps.
This programming model facilitates both Managed and Unmanaged Implementation approaches, although the core data source must originate from a CDS view or a table within the same system in both scenarios. However, the non-key fields can be calculated on the fly using virtual elements.
When retrieving data from a remote source through an API or performing complex calculations, managing CDS view entities becomes challenging. Further complexities arise when compositions are involved.
Aim of this blog post is to demonstrate the abilities of custom entities in RAP and create a Fiori application with composition relationships.
The basic implementation steps of a custom entity can be found in the documentation and not demonstrated here in detail.
Lets take a Business data model for Shipment request and its items.
@EndUserText.label: 'Shipment Request'
@ObjectModel.query.implementedBy: 'ABAP:ZRK_CL_CE_SHIPMENT_REQ'
@UI.headerInfo: {
typeName: 'Shipment Request',
typeNamePlural: 'Shipment Requests',
title: {
type: #STANDARD,
value: 'ShipReqNo'
},
description: {
type: #STANDARD,
value: 'Description'
}
}
define root custom entity ZRK_CE_I_ShipReq
// with parameters parameter_name : parameter_type
{
@UI.facet : [{
id : 'General',
purpose : #STANDARD,
parentId : '',
position : 10,
isPartOfPreview: true,
label : 'General',
type : #COLLECTION,
targetQualifier: 'General'
},
{
id : 'BasicInfo',
purpose : #STANDARD,
parentId : 'General',
position : 10,
isPartOfPreview: true,
label : 'Basic Info',
type : #FIELDGROUP_REFERENCE,
targetQualifier: 'QFBasicInfo'
},
{
id : 'SenderAddress',
purpose : #QUICK_VIEW,
parentId : 'General',
position : 20,
isPartOfPreview: true,
label : 'Sender Address',
type : #FIELDGROUP_REFERENCE,
targetQualifier: 'QFSenderAddress'
},
{
id : 'Items',
purpose : #STANDARD,
position : 30,
label : 'Items',
type : #LINEITEM_REFERENCE,
targetElement: '_ShipReqItems'
}]
@EndUserText.label: 'Shipment Req.No.'
@UI.selectionField: [{position: 10 }]
@UI.lineItem : [{ position: 10 }]
@UI.identification: [{ position: 10 }]
key ShipReqNo : zrk_ship_req;
@EndUserText.label: ''
@UI.lineItem : [{ position: 20 }]
@UI.identification: [{ position: 20 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 10 , qualifier: 'QFBasicInfo' } ]
Description : /dmo/description;
@EndUserText.label: 'Submission Date'
@Consumption.filter.selectionType: #INTERVAL
@UI.selectionField: [{position: 40 }]
@UI.lineItem : [{ position: 30 }]
@UI.identification: [{ position: 30 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 20 , qualifier: 'QFBasicInfo' } ]
SubmissionDate : abap.dats;
@EndUserText.label: 'Name'
@UI.lineItem : [{ position: 40 }]
@UI.identification: [{ position: 40 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 10 , qualifier: 'QFSenderAddress' } ]
SenderName : abap.char( 40 );
@EndUserText.label: 'Company'
@UI.identification: [{ position: 10 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 20 , qualifier: 'QFSenderAddress' } ]
SenderCompany : abap.char( 40 );
@EndUserText.label: 'Street No'
@UI.lineItem : [{ position: 50 }]
@UI.identification: [{ position: 50 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 30 , qualifier: 'QFSenderAddress' } ]
SenderStreetNo : abap.char( 40 );
@EndUserText.label: 'City'
@UI.lineItem : [{ position: 60 }]
@UI.selectionField: [{position: 20 }]
@UI.identification: [{ position: 60 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 40 , qualifier: 'QFSenderAddress' } ]
SenderCity : abap.char( 20 );
@EndUserText.label: 'Zip Code'
@UI.identification: [{ position: 70 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 50 , qualifier: 'QFSenderAddress' } ]
SenderZipCode : abap.numc( 5 );
@EndUserText.label: 'Country'
@UI.lineItem : [{ position: 60 }]
@UI.selectionField: [{position: 30 }]
@UI.identification: [{ position: 80 }]
@UI.fieldGroup: [ { type: #STANDARD, position: 60 , qualifier: 'QFSenderAddress' } ]
SenderCountry : abap.char( 20 );
LocalLastChangedOn : abp_locinst_lastchange_tstmpl;
}
@EndUserText.label: 'Shipment Request Item'
@ObjectModel.query.implementedBy: 'ABAP:ZRK_CL_CE_SHIPMENT_REQ'
@UI.headerInfo: {
typeName: 'Shipment Request Item',
typeNamePlural: 'Shipment Request items',
title: {
type: #STANDARD,
value: 'ShipReqItemNo'
},
description: {
type: #STANDARD,
value: 'Description'
}
}
define custom entity ZRK_CE_I_ShipReqItem
{
@UI.facet : [{
id : 'General',
purpose : #STANDARD,
position : 10,
label : 'General',
type : #IDENTIFICATION_REFERENCE
}]
@UI.hidden : true
key ShipReqNo : zrk_ship_req;
@UI.lineItem : [{ position: 10 }]
key ShipReqItemNo : abap.numc( 3 );
@UI.lineItem : [{ position: 20 }]
@UI.identification : [{ position: 10 }]
Description : /dmo/description;
@UI.lineItem : [{ position: 30 }]
@UI.identification : [{ position: 20 }]
PackageSize : abap.char( 2 );
@UI.lineItem : [{ position: 40 }]
@UI.identification : [{ position: 30 }]
PackageQuantity : abap.numc( 2 );
@UI.lineItem : [{ position: 50 }]
@UI.identification : [{ position: 40 }]
ShipmentStatus : abap.char(15);
@UI.lineItem : [{ position: 60 }]
@UI.identification : [{ position: 50 }]
DispatchDate : abap.dats;
@UI.lineItem : [{ position: 70 }]
@UI.identification : [{ position: 60 }]
DeliveryDate : abap.dats;
@UI.lineItem : [{ position: 80 }]
@UI.identification : [{ position: 70 }]
RecipientName : abap.char( 40 );
@UI.identification : [{ position: 80 }]
RecipientCompany : abap.char( 40 );
@UI.identification : [{ position: 90 }]
RecipientStreetNo : abap.char( 40 );
@UI.identification : [{ position: 100 }]
RecipientCity : abap.char( 20 );
@UI.identification : [{ position: 110 }]
RecipientZipCode : abap.numc( 5 );
@UI.identification : [{ position: 120 }]
RecipientCountry : abap.char( 20 );
}
define root custom entity ZRK_CE_I_ShipReq
// with parameters parameter_name : parameter_type
{
...
...
...
_ShipReqItems : composition [0..*] of ZRK_CE_I_ShipReqItem ;
}
define custom entity ZRK_CE_I_ShipReqItem
{
...
...
...
_ShipReq : association to parent ZRK_CE_I_ShipReq
on $projection.ShipReqNo = _ShipReq.ShipReqNo ;
}
CLASS zrk_cl_ce_shipment_req DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_query_provider.
PRIVATE SECTION.
ENDCLASS.
CLASS zrk_cl_ce_shipment_req IMPLEMENTATION.
METHOD if_rap_query_provider~select.
CASE io_request->get_entity_id( ).
WHEN 'ZRK_CE_I_SHIPREQ'.
" Core logic is wrapped as it is not agenda for this blog
zrk_cl_ce_manage_shipreq=>get_instance( )->get_shipment_requests(
EXPORTING io_request = io_request
IMPORTING et_shipreq_resp = DATA(lt_shipreq_resp) ).
IF io_request->is_data_requested( ).
io_response->set_data( lt_shipreq_resp ).
ENDIF.
IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lines( lt_shipreq_resp ) ).
ENDIF.
WHEN 'ZRK_CE_I_SHIPREQITEM'.
" Core logic is wrapped as it is not agenda for this blog
zrk_cl_ce_manage_shipreq=>get_instance( )->get_shipment_request_items(
EXPORTING io_request = io_request
IMPORTING et_shipreqitem_resp = DATA(lt_shipreqitem_resp) ).
IF io_request->is_data_requested( ).
io_response->set_data( lt_shipreqitem_resp ).
ENDIF.
IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lines( lt_shipreqitem_resp ) ).
ENDIF.
ENDCASE.
ENDMETHOD.
ENDCLASS.
@EndUserText.label: 'SD for Shipment Req'
define service ZRK_UI_CE_ShipReq {
expose ZRK_CE_I_ShipReq as ShipReq;
expose ZRK_CE_I_ShipReqItem as ShipReqItem;
}
unmanaged implementation in class zrk_bp_ce_i_shipreq unique;
strict ( 2 );
define behavior for ZRK_CE_I_ShipReq alias ShipReq
late numbering
lock master
authorization master ( instance )
etag master LocalLastChangedOn
{
field ( readonly) ShipReqNo ;
create ;
update;
delete;
association _ShipReqItems { create ; }
}
define behavior for ZRK_CE_I_ShipReqItem alias ShipReqItem
late numbering
lock dependent
authorization master ( instance )
//etag dependent
{
field ( readonly) ShipReqNo , ShipReqItemNo ;
update;
delete;
association _ShipReq ;
}
For more information on RAP, please follow on community page and developers.com