Skip to Content
Technical Articles
Author's profile photo Anmol Bhat

RAP( Restful ABAP programming) for OnPrem S4HANA

RestFul ABAP programming model for S4HANA

Since 1909 release SAP came up with new innovation in the programming model of SAP for Services.

That is RESTFul programming Model ( RAP),  this is huge improvement over current BOPF framework and has lots of advantages. There is some blogs explaining more about RAP some of them are listed below.

https://developers.sap.com/tutorials/abap-environment-persistence.html

https://help.sap.com/doc/59d5e6b765eb49c5884a94de512e8259/LATEST/en-US/ABAP_RESTful_Programming_Model.pdf

In above link the concept is very well explained. Please go through these blogs to get deeper knowledge about the this exciting concept

The purpose of this document is to show the capabilities of RAP with on Premise setup and see how exactly this is different than BOPF.

Basically there are 3 implementation scenarios possible for RAP

  • Managed ( Green field development )
    1. Managed Scenarios are more like BOPF where you define what tables will be updated at end of the process.
    2. We have options for implementing actions, determination and validations
    3. Only available on S4HANA cloud
  • Managed with save ( Self implemented )
    1. These are more sophisticated implementation types where data is not directly updated into tables
    2. BAPIs, update function modules can be called in the implementation to save the data
    3. As of now it is for SAP internal purpose and not delivered to Customers
  • Unmanaged
    1. In this scenario the implementation decides business logic
    2. In the implementation developer can process the input data and update the respective tables or also call BAPIs, functions etc.
    3. Available for S4HANA 1909 OnPremise customers as well
    4. We cannot use validation and determination on field levels.

As you can see that RAP is still in its evaluation state but its worth already start working with it and exploring it.

The major benefit of RAP is first it now follows global development paradigm for Web development i.e. REST services.  This makes it uniform with all other programming languages and also makes integration with system much easy.

Another benefit is, it does not create bunch of objects like BOPF. The implantation is very LEAN.

It does not create new classes with every implementation, does not create lots of structures etc. also SEWG project is not created for RAP.

It also cuts the annotation assignments to CDS views (Hence we have to remember less annotation keywords).

As we proceed with the example I will try to highlight the difference between current (BOPF) and RAP

In this document we will consider Unmanaged Scenario as this is also available for OnPrem (S4HANA1909) customers.

Our example scenario is as follows

We would like to create, update, delete, display the Service entry for the Auto Service industry (Please note this is taken only for example)

P.S Since covering all these actions in this single blog will be to much information, I will only cover Create and Display actions in this blog and publish another one with other actions and options.

Basic setup

 

We would need 2 tables (header and item) to store the Service order and also a basic CDS view.

Header Table

@EndUserText.label : 'Service order header table'
@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #LIMITED
define table zserv_ordhd {
  key client : abap.clnt not null;
  key sord   : char10 not null;
  partner    : bu_partner;
  status     : char1;
  priority   : char1;
  @Semantics.amount.currencyCode : 'zserv_ordhd.currkey'
  estcost    : abap.curr(5,2);
  currkey    : abap.cuky;
  include /bobf/s_lib_admin_data;
}

Item Table

@EndUserText.label : 'Service Order item table'
@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #LIMITED
define table zserv_orditm {
  key client   : abap.clnt not null;
  key sord     : char10 not null;
  key sitm     : numc3 not null;
  servcode     : char10;
  servdescr    : char50;
  itemstatus   : char1;
  @Semantics.amount.currencyCode : 'zserv_ordhd.currkey'
  partcost     : abap.curr(5,2);
  labourcode   : char10;
  labourcat    : char1;
  labstarttime : timestampl;
  labendtime   : timestampl;
}

CDS view for Header table

@AbapCatalog.sqlViewName: 'ZISERVORDHDR'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Service order header interface view'
@VDM.viewType: #BASIC
define view ZI_SERV_ORDHRD as select from zserv_ordhd 
association [0..*] to ZI_SERV_ORDITM as _Item on $projection.ServiceOrd = _Item.ServiceOrd{
  key sord   as ServiceOrd,
  partner    as Customer,
  status     as Status,
  priority   as Priority,
  //zserv_ordhd
  @Semantics.amount.currencyCode: 'Currency'
  estcost as EstimatedCost,
  currkey as Currency,
/*--Admin data */  
@Semantics.systemDate.createdAt: true
  crea_date_time as CreatedOn,
@Semantics.user.createdBy: true  
  crea_uname as CreatedBy,
@Semantics.systemDate.lastChangedAt: true  
  lchg_date_time as ChangedOn,
@Semantics.user.lastChangedBy: true  
  lchg_uname as ChangedBy,
  //Associations
  _Item 
}

CDS view for Item table

@AbapCatalog.sqlViewName: 'ZISERVORDITEM'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Service order item interface view'
@VDM.viewType: #BASIC
define view ZI_SERV_ORDITM
  as select from zserv_orditm
  association [1..1] to ZI_SERV_ORDHRD as _Header on $projection.ServiceOrd = _Header.ServiceOrd
{
      //zserv_orditm
      //zserv_orditm
  key sord         as ServiceOrd,
  key sitm         as ServiceItem,
      servcode     as ServiceCode,
      servdescr    as ServiceDescription,
      itemstatus   as ItemStatus,
      @Semantics.amount.currencyCode: '_Header.Currency'
      partcost     as PartCost,
      labourcode   as LabourCode,
      labourcat    as LabourCategory,
      labstarttime as LabourStartTime,
      labendtime   as LabourEndTime,
      //Associations
      _Header
}

 

Until this step there is no difference with our usual procedure and RAP.

Next step for us is to create the Consumption views.

 

Consumption Views

While doing this we should define which View is Parent View ( Root View ) and connect all Children to this view.

Let’s create the Root Consumption view for our Header table

@AbapCatalog.sqlViewName: 'ZCSERVORDHDRR'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Service Order Cons View with RAP'
@VDM.viewType: #CONSUMPTION
@Search.searchable: true
@UI.headerInfo: { typeName: 'Service Order', typeNamePlural: 'Service Orders', title: { type: #STANDARD, value: 'ServiceOrd' } }
define root view ZC_SERV_ORDHDR_REST
  as select from ZI_SERV_ORDHRD
  composition [0..*] of ZC_SERV_ORDITEM_REST as _Item
{
      //ZI_SERV_ORDHRD
      @UI.facet: [
                  {
                  id: 'ServiceHeader',
                  type: #COLLECTION,
                  position: 10,
                  label: 'Service Orders'
                  },
                  {
                  type: #FIELDGROUP_REFERENCE,
                  position: 10,
                  targetQualifier: 'GeneralData1',
                  parentId: 'ServiceHeader',
                  isSummary: true,
                  isPartOfPreview: true
                  },
                  {
                  type: #FIELDGROUP_REFERENCE,
                  position: 20,
                  targetQualifier: 'GeneralData2',
                  parentId: 'ServiceHeader',
                  isSummary: true,
                  isPartOfPreview: true
                  },
                  {
                  id: '_Item',
                  purpose: #STANDARD,
                  type: #LINEITEM_REFERENCE,
                  label: 'Item details',
                  position: 10,
                  targetElement: '_Item'
                  }
              ]

      @UI.lineItem: [{position: 10, importance: #HIGH, label: 'Service Order' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData1',position: 10,importance: #HIGH, label: 'Service Order' }]
      @UI.selectionField: [{position: 10 }]
      @Search.defaultSearchElement: true
      @Search.fuzzinessThreshold: 0.9
      @UI.hidden: #(CreateAction)
  key ServiceOrd,
      case when ServiceOrd is initial then cast ( 'X' as bool )
           else cast( ' ' as bool ) end                      as CreateAction,
      @UI.lineItem: [{position: 20, importance: #HIGH, label: 'Customer' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData1',position: 20,importance: #HIGH, label: 'Customer' }]
      @UI.selectionField: [{position: 20 }]
      @Search.defaultSearchElement: true
      @Search.fuzzinessThreshold: 0.9
      Customer,
      @ObjectModel.text.element: ['StatusTxt']
      @UI.lineItem: [{position: 30, importance: #HIGH, label: 'Status', criticality: 'StatusCriticality' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData2',position: 10,importance: #HIGH, criticality: 'StatusCriticality', label: 'Status' }]
      @UI.selectionField: [{position: 30 }]
      @UI.hidden: #(CreateAction)
      Status,
      @Semantics.text: true
      case when Status = '1' then 'Open'
           when Status = '2' then 'Approval'
           when Status = '3' then 'In Progress'
           when Status = '4' then 'Completed' else '' end    as StatusTxt,
      case when Status = '1' then 1
           when Status = '2' then 2
           when Status = '3' then 2
           when Status = '4' then 3 else 1 end               as StatusCriticality,
      @ObjectModel.text.element: ['PriorityTxt']
      @UI.lineItem: [{position: 40, importance: #HIGH, label: 'Priority' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData2',position: 20,importance: #HIGH, label: 'Priority' }]
      @UI.selectionField: [{position: 40 }]
      Priority,
      @Semantics.text: true
      case when Priority = '1' then 'Urgent'
           when Priority = '2' then 'Major'
           when Priority = '3' then 'Medium'
           when Priority = '4' then 'Minor' else 'Minor' end as PriorityTxt,
      @UI.lineItem: [{position: 50, importance: #HIGH, label: 'Estimated Cost', criticality: 'StatusCriticality' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData1',position: 30,importance: #HIGH, label: 'Estimated Cost', criticality: 'StatusCriticality' }]
      EstimatedCost,
      case when EstimatedCost > 500 then 'X'
           else '' end                                       as approvalNeeded,

      @UI.lineItem: [{position: 60, importance: #HIGH, label: 'Actual Cost' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData1',position: 40,importance: #HIGH, label: 'Actual Cost' }]
      @Semantics.amount.currencyCode: 'Currency'
      @UI.hidden: #(CreateAction)
      cast(0 as abap.curr(5,2) )                             as ActualCost,
      @UI.lineItem: [{position: 70, importance: #MEDIUM, label: 'Currency' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData1',position: 50,importance: #MEDIUM, label: 'Currency' }]
      Currency,
      @UI.lineItem: [{position: 80, importance: #MEDIUM, label: 'Created On' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData2',position: 30,importance: #MEDIUM,label: 'Created On' }]
      @UI.selectionField: [{position: 50 }]
      @UI.hidden: #(CreateAction)
      CreatedOn,
      @UI.lineItem: [{position: 90, importance: #MEDIUM, label: 'Created By' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData2',position: 40,importance: #MEDIUM,label: 'Created By'}]
      @UI.selectionField: [{position: 60 }]
      @UI.hidden: #(CreateAction)
      CreatedBy,
      @UI.lineItem: [{position: 100, importance: #LOW, label: 'Changed On' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData2',position: 50,importance: #LOW,label: 'Changed On' }]
      @UI.hidden: #(CreateAction)
      ChangedOn,
      @UI.lineItem: [{position: 110, importance: #LOW, label: 'Changed By' }]
      @UI.fieldGroup: [{qualifier: 'GeneralData2',position: 60,importance: #LOW,label: 'Changed By' }]
      @UI.hidden: #(CreateAction)
      ChangedBy,
      /* Associations */
      //ZI_SERV_ORDHDR_TP
      _Item
}

Now same way create Item consumption view

@AbapCatalog.sqlViewName: 'ZCSERORDITR'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Service order Item cons view RAP'
@UI.headerInfo: { typeName: 'Service Order Item', typeNamePlural: 'Service Order Items', title: { type: #STANDARD, value: 'ServiceOrd' } }

define view ZC_SERV_ORDITEM_REST
  as select from ZI_SERV_ORDITM
  association to parent ZC_SERV_ORDHDR_REST as _Header on $projection.ServiceOrd = _Header.ServiceOrd
{
      //ZI_SERV_ORDITM
      @UI.facet: [
                {type: #COLLECTION, position: 10, id: '_Item', label: 'Idoc details'},
                {type: #FIELDGROUP_REFERENCE, position: 10, targetQualifier: 'Item1',parentId: '_Item', isSummary: true, isPartOfPreview: true},
                {type: #FIELDGROUP_REFERENCE, position: 20, targetQualifier: 'Item2',parentId: '_Item', isSummary: true, isPartOfPreview: true}
                ]
      @UI.lineItem: [{position:10,importance: #HIGH, label:'Service Order' }]
      @UI.fieldGroup: [{qualifier: 'Item1', label:'Service Order', position:10,importance: #HIGH}]
  key ServiceOrd,
      @UI.lineItem: [{position:20,importance: #HIGH, label: 'Order Item' }]
      @UI.fieldGroup: [{qualifier: 'Item1', position:20, label: 'Order Item',importance: #HIGH}]
  key ServiceItem,
      @UI.lineItem: [{position:30,importance: #MEDIUM , label: 'Service code'}]
      @UI.fieldGroup: [{qualifier: 'Item1', position:30, label: 'Service code',importance: #HIGH}]
      ServiceCode,
      @UI.lineItem: [{position:40,importance: #MEDIUM , label:'Service description'}]
      @UI.fieldGroup: [{qualifier: 'Item1', position:40, label:'Service description',importance: #HIGH}]
      ServiceDescription,
      @UI.lineItem: [{position:50,importance: #HIGH, label: 'Item status', criticality: 'ItemCriticality' }]
      @UI.fieldGroup: [{qualifier: 'Item2', position:10,importance: #HIGH, label: 'Item status', criticality: 'ItemCriticality'}]
      @ObjectModel.text.element: ['ItemStatusTxt']
      ItemStatus,
      @Semantics.text: true
      case when ItemStatus = '1' then 'Open'
           when ItemStatus = '2' then 'In Progress'
           when ItemStatus = '3' then 'Complete'
           else '' end       as ItemStatusTxt,
      case when ItemStatus = '1' then 1
                 when ItemStatus = '2' then 2
                 when ItemStatus = '3' then 3
                 else 1  end as ItemCriticality,
      @UI.lineItem: [{position:60,importance: #MEDIUM, label:'Labour code' }]
      @UI.fieldGroup: [{qualifier: 'Item1', position:50, label:'Labour code',importance: #HIGH}]
      LabourCode,
      @UI.lineItem: [{position:70,importance: #MEDIUM, label:'Labour Category' }]
      @UI.fieldGroup: [{qualifier: 'Item1', position:60, label:'Labour Category',importance: #HIGH}]
      LabourCategory,
      @UI.fieldGroup: [{qualifier: 'Item1', position:70, label:'Part cost',importance: #MEDIUM}]
      @Semantics.amount.currencyCode: '_Header.Currency'
      PartCost,
      //_Header.Currency as Currency,
      @UI.fieldGroup: [{qualifier: 'Item1', position:80, label:'Labour starttime',importance: #MEDIUM}]
      LabourStartTime,
      @UI.fieldGroup: [{qualifier: 'Item1', position:90, label:'Labour Endtime',importance: #MEDIUM}]
      LabourEndTime,
      /* Associations */
      //ZI_SERV_ORDHITEM_TP
      _Header
}

The Notable difference with this consumption views are, we cut lots of Annotations.

In older CDS view for BOPF we have to define many ObjectModel Annotations in the Interface and Consumptions Views.

with%20BOPF

RAP

 

The Parent and Child relationship in BOPF was defined at the bottom of the CDS view where we declare the Associations

All of these annotations are not required now as the RAP does not create the BOPF like framework in the background out of these Annotations.

The Difference we see here is how we define the view and how we write the select statement.

To declare the view as Parent we have to use DEFINE ROOT keyword, with this statement the RAP consider this view ad Parent and automatically create the Relationships for all the Views declared with COMPOSITION statement

Other than these 2 differences we don’t need any other special declaration to set up RAP.

While creating Child view we have to use keyword association to parent. This is sufficient for RAP to link these views and create Relationship.

 

That’s it. The basic setup for creating the RAP is completed.

We have usual UI Annotations in these CDS view for UI Representation.

Now let’s go further and create Behavior Definition

 

Behavior Definition

Behavior Definition is used to (name suggest )  define the Behavior of our service. Which means what kind of database activities are allowed, what actions this service should do, Validations etc?

To create the Behavior Definition right click on the Cons View and select “Create Behavior Definition”

Please note: Create Behavior Definition only for the Root View.

In the next screen select the implementation type Unmanaged as this is the only possibility for OnPrem S4HANA1909 to create RAP.

unmanaged implementation in class ZCL_C_I_SERVORD_REST unique;

define behavior for ZC_SERV_ORDHDR_REST alias ServOrd
//late numbering
//lock master
//authorization master
etag ChangedOn
{
  field ( read only ) ServiceOrd, ActualCost, Status, CreatedOn, CreatedBy, ChangedOn, ChangedBy;
  field ( mandatory ) Customer;
  create;
  update;
  delete;
  association _Item { create; }
//  action (features : instance) Approved result [1] $self;
}

define behavior for ZC_SERV_ORDITEM_REST alias ServItem
//late numbering
//lock dependent( <local_field_name> = <target_field_name> )
//authorization dependent( <local_field_name> = <target_field_name> )
//etag <field_name>
{
  field ( read only ) ServiceOrd, ServiceItem, ItemStatus;
  field ( mandatory ) LabourCode, LabourCategory;
  create;
  update;
  delete;
}

In the above code we wrote that some fields are read only, some are mandatory. What database actions can be performed. We can also define custom actions( will be explained in details in next Blog )

We also have to create the Class defined in the Behavior with following Signature

class ZCL_C_I_SERVORD_REST definition
  public abstract final for behavior of ZC_SERV_ORDHDR_REST.
endclass.

class ZCL_C_I_SERVORD_REST implementation.
endclass.

 

Don’t worry for the implementation of the class methods for now.  We will look at this later.

Now lets create Service Definition.

Service Definition

In Service Definition, we define what View are exposed in this service.

@EndUserText.label: 'Service Definition for Service Order'
define service ZUI_ZC_SERV_ORDHDR_REST {
  expose ZC_SERV_ORDHDR_REST as ServHeader;
  expose ZC_SERV_ORDITEM_REST as ServItem;
}

Next step is to create the Service Binding

 

Service Binding

In Service Binding we define what kind of service we want to create. The service can be simple Odata Service which can be consumed by other services or external systems using REST calls or it could be UI service which then creates FIORI element UI5 service.

Let’s create UI service as this is more fun.

Now let’s click on the Activate Service.

And after this the magic begins.

Our service is activated with automatically create Associations Navigation for the Views exposed in the previous step.

Also we can now already see how our UI Service will look like when implemented in WebIDE.

Click on the Preview button and we can see the preview of our FIORI UI5 application already.

In older frameworks, this was not possible we first have to create UI5 application, consume this service and then check the output but here we can Preview it much earlier and make adjustments if needed.

Not only this, you can navigate to next page to check the Item Association.

 

By this point our service with FIORI element is ready, now we have to implement the logic for database activities and any external actions which we defined in Behavior Definition.

So let’s go back to our class ZCL_C_I_SERVORD_REST and add the necessary methods.

We declare and implement these methods in Local Types (don’t ask me exactly why, probably as we have to inherit from 2 classes and ABAP do not support this )

In this class we have to declare local class inheriting from CL_ABAP_BEHAVIOR_HANDLER and declare and implement all the database activity methods as below

*"* use this source file for the definition and implementation of
*"* local helper classes, interface definitions and type
*"* declarations
class LCL_BUFFER definition.

  public section.

    types: begin of TY_BUFFER_HDR.
             include type ZSERV_ORDHD as DATA.
    types:   FLAG type C length 1,
           end of TY_BUFFER_HDR.

    types: begin of TY_BUFFER_ITM.
             include type ZSERV_ORDITM as DATA.
    types:   FLAG type C length 1,
           end of TY_BUFFER_ITM.
    types: tt_SERVORD type sorted table of TY_BUFFER_HDR with unique key SORD.
    types: tt_SERVORD_ITM type sorted table of TY_BUFFER_ITM with unique key SORD SITM.

    class-data MT_BUFFER_HDR type tt_SERVORD.
    class-data MT_BUFFER_ITM type tt_SERVORD_ITM.
endclass.

class LHC_SERVORD definition inheriting from CL_ABAP_BEHAVIOR_HANDLER.
  private section.
    methods CREATE_HEADER for modify importing ENTITIES for create ServOrd.
    methods UPDATE_HEADER for modify importing ENTITIES for update ServOrd.
    methods DELETE_HEADER for modify importing ENTITIES for delete ServOrd.
*    methods LOCK_HEADER for lock importing KEYS for lock ServOrd.
    methods READ_HEADER for read importing KEYS for read ServOrd result RESULT.
    methods CREATE_ITEM for modify importing ENTITIES for create ServItem.
    methods UPDATE_ITEM for modify importing ENTITIES for update ServItem.
    methods DELETE_ITEM for modify importing ENTITIES for delete ServItem.
*    methods LOCK_ITEM for lock importing KEYS for lock ServItem.
    methods READ_ITEM for read importing KEYS for read ServItem result RESULT.

endclass.

class LHC_SERVORD implementation.

  method CREATE_HEADER.
    get time stamp field data(LV_TSL).
    loop at ENTITIES into data(LS_CREATE).
      select max( SORD ) from ZSERV_ORDHD into @data(LV_MAX_SORD).   "get last servord num
      LV_MAX_SORD = LV_MAX_SORD + 1.
      LS_CREATE-%DATA-ServiceOrd = LV_MAX_SORD.   "calculate field

      LS_CREATE-%DATA-Status = 1.   "set default values
      LS_CREATE-%DATA-CreatedOn = LV_TSL.
      LS_CREATE-%DATA-CreatedBy = SY-UNAME.
      append initial line to lcl_buffer=>mt_buffer_hdr assigning field-symbol(<buffer>).
      <buffer>-data = corresponding #( ls_create-%DATA ).
      <buffer>-data-sord = ls_create-%DATA-ServiceOrd.
      <buffer>-data-Partner = ls_create-%DATA-Customer.
      if LS_CREATE-%CID is not initial.
        insert value #( %CID = LS_CREATE-%CID ServiceOrd = LS_CREATE-ServiceOrd ) into table MAPPED-SERVORD.
      endif.
    endloop.
  endmethod.


  method READ_HEADER.

  endmethod.

  method UPDATE_HEADER.
    "nothing to do here

  endmethod.

  method DELETE_HEADER.
    "nothing to do in this method
  endmethod.

  method CREATE_ITEM.
    get time stamp field data(LV_TSL).
    loop at ENTITIES into data(ls_itemCreate).
      ls_itemCreate-%DATA-ItemStatus = 1.
      ls_itemCreate-%DATA-LabourStartTime = LV_TSL.
      insert value #( FLAG = 'C' DATA = corresponding #( ls_itemCreate-%DATA ) ) into table LCL_BUFFER=>MT_BUFFER_ITM.
      if ls_itemCreate-%CID is not initial.
        insert value #( %CID = ls_itemCreate-%CID ServiceOrd = ls_itemCreate-ServiceOrd ServiceItem = ls_itemCreate-ServiceItem ) into table MAPPED-SERVITEM.
      endif.
    endloop.
  endmethod.

  method DELETE_ITEM.

  endmethod.

  method READ_ITEM.

  endmethod.

  method UPDATE_ITEM.

  endmethod.

endclass.

( In this blog only Create methods are implemented, others will follow in next Blog )

In the method implementation, you can add normal ABAP code to perform actions (isn’t this cool).

You can calculate and populate certain fields, add default values etc..

After this we have to declare and implement another local class inheriting from CL_ABAP_BEHAVIOR_SAVER.

This class has methods

CHECK_BEFORE_SAVE – For before save validations

FINALIZE – Final adjustments

SAVE – Actual commit.

In this class we actual write ABAP code to perform the database action.

You can call standard BAPIs or function modules to commit the transactions or also direct update to your custom tables or any other action like creating file on application server etc..

class lsc_ZI_SERV_ORDHDR_TP definition inheriting from CL_ABAP_BEHAVIOR_SAVER.
  protected section.
    methods CHECK_BEFORE_SAVE redefinition.
    methods FINALIZE redefinition.
    methods SAVE redefinition.
endclass.

class LSC_ZI_SERV_ORDHDR_TP implementation.

  method CHECK_BEFORE_SAVE.

  endmethod.

  method FINALIZE.

  endmethod.

  method SAVE.
    data: LT_DATA_HDR type standard table of ZSERV_ORDHD.
    data: LT_DATA_ITM type standard table of ZSERV_ORDITM.

    LT_DATA_HDR = value #( for ROW in LCL_BUFFER=>MT_BUFFER_HDR where ( FLAG = 'C' ) ( ROW-DATA ) ).
    if LT_DATA_HDR is not initial.
      insert ZSERV_ORDHD from table @LT_DATA_HDR.
    endif.
    LT_DATA_ITM = value #( for ROW_ITM in LCL_BUFFER=>MT_BUFFER_ITM where ( FLAG = 'C' ) ( ROW_ITM-DATA ) ).
    if LT_DATA_ITM is not initial.
      insert ZSERV_ORDITM from table @LT_DATA_ITM.
    endif.

  endmethod.

endclass.

 

Last step is to consume this newly created service in our SAP WebIDE and created FIORI element application.

Create FIORI Element application

 

Follow same steps like we consume other services

 

 

At the end of this our service is ready with read and write action along with custom logic for writing the data back to database. You can now enhance the class implementation of the Behavior definition and add further logic. Also we have all the possibilities to enhance the FIORI element app like any other FIORI element development.

 

Summery

RAP ( RestFul ABAP Programming ) Model is very promising, it is very flexible but at the same time reliable. There will be more development and changes coming in this direction in future releases. It leaves less database footprint as it does not create lots of artifacts for every application. We will keep looking for the updates on this topic.

Assigned Tags

      19 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Joachim Rees
      Joachim Rees

      Thanks for the detailed example!

      Author's profile photo girdhari mondal
      girdhari mondal

      Nice blog ! Thanks!

      Author's profile photo Andreas Schaller
      Andreas Schaller

      Hi Anmol,

      Great blog post about RAP!

      Author's profile photo Nikhil Thakare
      Nikhil Thakare

      Very useful blog. Thanks for the blog. Waiting for your next blog. 🙂

      Author's profile photo Manjeet Dang
      Manjeet Dang

      Anmol Bhat : If we are having existing standard BOPF, and we are working on BOPF,CDS, Odata framework and we have to switch to RAP. Then we will have to go for unmanaged BIMP? and how will we be using existing BOPF in this case?

      Author's profile photo Anmol Bhat
      Anmol Bhat
      Blog Post Author

      Hi Manjeet Dang,

      I think replacement of existing BOPF application for SAP and for customers will a long term exercise.

      I hope SAP will keep both solutions working for longer time.

      Regarding which Implementation should be followed - It completely dependent on Scenario.

      Most BOPF application with less enhancements and code might be going towards Managed Implementation. Managed Implementation also offers validation, user actions etc...

      Unmanaged and Managed with Save gives us more options to write plane ABAP code and have custom logic while saving the data in the DB or call standard BAPI etc.

      Regarding  Reusing objects from BOPF - I am not sure which components of BOPF can be reuse in RAP. Probably only the Code from our Validations and action only.

      I hope you get what you are looking for.

      Regards,

      Anmol

      Author's profile photo Manjeet Dang
      Manjeet Dang

      Thanks. Yes it answers my question and any views about draft functionality ? How can we use BOPF draft functionality in RAP?

      Author's profile photo Neelesh Deshpande
      Neelesh Deshpande

      Hi Anmol,

       

      Very interesting and nice Blog. Also you have explained the steps and their purpose very clearly. Thanks for such a good blog.

      Any clue on when will you be publishing the next blog in the series.

       

      Neelesh

      Author's profile photo Alvin Fan
      Alvin Fan

      Hi Anmol,

      You have explained the steps and their purpose very clearly.Thanks for sharing.

      Looking forward to the next blog in this series.

      Author's profile photo RAMAKRISHNAN MURUGAN
      RAMAKRISHNAN MURUGAN

      Hi Anmol,

      Even though I had defined behaviour definition 'CREATE' for items section, am not able to see any item fields in CREATE screen. Only I can able to see the HEADER fields?

      Is there any thing, am I missing?

      Author's profile photo iyankannu Iyankannu
      iyankannu Iyankannu

      Hi,

       

      I try to reproduce the case. But I couldn't make. I have issue with line item to be displayed on detail page.

      I done all the steps explained above. In details page, I can able to se the Service Order table, But there is no Item table . My item information is not shown.

      Can you please help to solve the issue. is there any special parameter or UI annotation do I have to make in CDS view display the Item details..

      Author's profile photo Pramod Kumar
      Pramod Kumar

      Thanks Anmol !!   Good stuff to know .

      Author's profile photo Anilkumar Hari
      Anilkumar Hari

      Hello All,

      I am trying to create an entry in the header table using the Create Header Method.

      I am getting the error

      Short Text  Assignment error: Overwriting of a protected field.
      Runtime Error  MOVE_TO_LIT_NOTALLOWED_NODATA

      Field "<BUFFER>-CLIENT" was to assigned a new value but this field is at least partly
      protected against changes.

      Did anyone faced this issue when posting the data to the DB table

      Author's profile photo iMRO DEV TEAM
      iMRO DEV TEAM

      Hi Anmol Bhat ,

      I am trying to use standard BAPI which does an internal commit and updates a Revision.
      when I am using this in SAVE method, the RAP framework gives me a dump.

      Can you please help?
      Regards,

      Udita

      Author's profile photo dinesh k
      dinesh k

      Hi, Can you let us know if you have found out any workaround to this issue ...

      Author's profile photo Anmol Bhat
      Anmol Bhat
      Blog Post Author

      Hi Udita,

      I dont know if you still have the problem. If yes I think the problem could be that there is a commit work. RAP does not expect to have exclusive commit work.

       

      Author's profile photo Martin Carl
      Martin Carl

      I  tried out the example. After fixing some bugs i finally can create a header-entry. Then i can go to the detail page. When i try to create a detail-entry there are two errors i get:

      1. The partCost fileld will not convert and has always three number after decimal also by the currency EUR (will the semantic-key ignored?)
      2. When i leave the field for partCost empty and click on save then i get a sadl-short-dump What's wrong? There is no hint which makes sense.

      Can anyone help please?

      Regards,

      Martin

      Author's profile photo Himanshu Kawatra
      Himanshu Kawatra

      Hi Anmol Bhat,

       

      Can you please tell me, How to handle Warning or Error messages in an Unmanaged Scenario?

       

       

      Regards,

      Himanshu Kawatra

      Author's profile photo Vinoth Thangaraj
      Vinoth Thangaraj

      Hi Himanshu,

      Were you able to implement error handling in unmanaged scenario?