Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Ramjee_korada
Active Contributor
24,143

Introduction:


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.

Implementation:


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.

  1. Create a root entity for Shipment request and an entity for items.

  2. Enrich the metadata for UI in both the entities.
    @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 );

    }​


  3. Edit the entities as below for Composition and Parent relationship. This is a very important steps as it is different syntax .
    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 ;
    }


  4. Implement the logic to read the data from API / AMDP / Complex queries
    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.​


  5. Expose these entities in service definition.
    @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;
    }​


  6. Generate the service binding for this service definition to check preview.

  7. Create the Behavior definition to add transactional operations such as CRUD operations and other features.
    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 ;
    }​


  8. Now the application is ready for usage.

  9. Note that

    1. Unmanaged implementation is only supported and hence implement CRUD methods in behaviour bool.

    2. Late numbering is only possible.

    3. Draft is not possible on custom entities.

    4. If transactional capabilities are required, OData V2 is only possible and ODataV4 does not support non-draft create operation.




For more information on RAP, please follow on community page and developers.com

 
35 Comments
Labels in this area