This post talks about creating a list report with ABAP RESTful Application Programming a.k.a RAP.

Background

Editing data in a list is required if you want the end user to be able to add, update, and delete data without going to the object page for each item separately.

In SAP terms, this is called Multi-inline-edit capabilities.

This can be compared to table maintenance through SM30 or activities similar to configurations.

Example Scenario

Application to edit flight carrier information. The data is maintained in the table /dmo/carrier.

As ABAP RESTful Application Programming services can be used to create a List-Object application where only the object page is editable and not the list page, we need to create one entity to be displayed on the list page and then link actual data to it. This is called as singleton entity.

Let us start creating the RAP service. This is created on the HANA trial account. It is assumed that you are aware of the process to create the various RAP artifacts like CDS View Entities, Behaviour Definition, Metadata Extension, etc.

Visit ABAP RESTful Application Programming Model to explore all articles on ABAP RAP Model.

Create CDS view Entities

Both Entities should be activated together. For the singleton entity, we need to ensure that it returns one record only. So, a CDS view such as I_Language is chosen with where condition on system language. As it’s a single value we will get one record.

As the language CDS and Carrier table do not have a common key, a condition like 1 = 1 is used which would be true and hence the join would work.

Max changed_at date is selected from the carrier table which can be used as etag master. We can not use the Carrier table itself to get a single record as it can be empty initially.

Activate both entities together.

Code reference

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Singleton entity for Carrier Data'
define root view entity ZI_JP_CarrierSinglton
  as select from    I_Language
    left outer join /dmo/carrier as carr on 1 = 1
  composition [0..*] of ZI_JP_Carrier as _Airline

{
  key 1                          as SingletonID,
      max (carr.last_changed_at) as LastChangedAtMax,
      _Airline
}

where
  I_Language.Language = $session.system_language
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Carrier data'
@Metadata.ignorePropagatedAnnotations: true


define view entity ZI_JP_Carrier
  as select from /DMO/I_Carrier as Airline
  association to parent ZI_JP_CarrierSinglton 
  as _Singleton on $projection.SingletonID = _Singleton.SingletonID

{
  key AirlineID,
      1 as SingletonID,
      Name,
      CurrencyCode,
      @Semantics.user.createdBy: true
      LocalCreatedBy,
      @Semantics.systemDateTime.createdAt: true
      LocalCreatedAt,
      @Semantics.user.lastChangedBy: true
      LocalLastChangedBy,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      LocalLastChangedAt,

      //total etag field
      @Semantics.systemDateTime.lastChangedAt: true
      LastChangedAt,

      /* Associations */
      _Singleton
}

Create CDS Projection view Entities

Activate both the projection entities together.

Code reference

@EndUserText.label: 'Singleton entity for Carrier Projection'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
@ObjectModel.semanticKey: ['SingletonID']

define root view entity ZC_JP_CarrierSinglton
  provider contract transactional_query
  as projection on ZI_JP_CarrierSinglton
{
  key SingletonID,

      @Consumption.hidden: true
      LastChangedAtMax,
      /* Associations */
      _Airline : redirected to composition child ZC_JP_Carrier
}
@EndUserText.label: 'Carrier data projection'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
@ObjectModel.semanticKey: ['AirlineID']

define view entity ZC_JP_Carrier
  as projection on ZI_JP_Carrier
{
  key AirlineID,

      @Consumption.hidden: true
      SingletonID,

      Name, 

      @Consumption.valueHelpDefinition: [
        { entity:
          { name: 'I_CurrencyStdVH',
            element: 'Currency' },
          useForValidation: true
        }]
      CurrencyCode,

      LocalLastChangedAt,

      /* Associations */
      _Singleton : redirected to parent ZC_JP_CarrierSinglton
}

Create Metadata Extensions

Important to note that the actual data table is added to the singleton object page. The singleton object page does not display any properties of its own except the singleton it which is always 1.

@Metadata.layer: #CORE

@UI: { headerInfo: { typeName: 'Manage Carriers',
                     typeNamePlural: 'Manage Carriers',
                     title: { type: #STANDARD, value: 'SingletonID' } } }

annotate entity ZC_JP_CarrierSinglton with
{

  @UI.facet: [
      {
        purpose:  #STANDARD,
        type:     #LINEITEM_REFERENCE,
        label:    'Carrier Multi Inline Edit',
        position: 10,
        targetElement: '_Airline'
      }
  ]

  @UI.lineItem: [{ position: 10 }]
  SingletonID;
}
@Metadata.layer: #CORE

@UI: { headerInfo: { typeName:       'Carrier',
                     typeNamePlural: 'Carriers',
                     title:          { type: #STANDARD,
                                       label: 'Airline',
                                       value: 'AirlineID' } } }
annotate entity ZC_JP_Carrier with
{
  @UI.facet: [{ type: #IDENTIFICATION_REFERENCE }]

  @UI.lineItem: [{ position: 10 }]
  @UI.identification: [{ position: 10 }]
  AirlineID;

  @UI.lineItem: [{ position: 20 }]
  @UI.identification: [{ position: 20 }]
  Name;

  @UI.lineItem: [{ position: 30 }]
  @UI.identification: [{ position: 30 }]
  CurrencyCode;
}

Behavior Definition

Important points to note for Behavior Definition

  • Here, we need a behavior definition as Managed with unmanaged save. Unmanaged save is needed to implement this scenario as if you do not use unmanaged save, you need to specify persistent table. As the header entity is created a singleton and it is not going to be updated, we can not provide a persistent table.
  • There are no create, update and delete operations for the header singlton entity as well.
  • However, draft is enabled. This is becuase without draft edit action is not enabled in the element based application. In fact, the field LastChangedAtMax is added to the entity so that it can be specified as total etag field which is mandatory in draft scenario.

ZI_JP_CarrierSinglton

managed implementation in class zbp_i_jp_carriersinglton unique;
strict ( 2 );
with draft;

define behavior for ZI_JP_CarrierSinglton alias Singlton
with unmanaged save
draft table ZJP_D_CAR_SIN_S
lock master
total etag LastChangedAtMax
authorization master ( global )

{

  draft action Edit;
  draft action Activate;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare;

  field ( readonly ) SingletonID;
  association _Airline { create; with draft; }
}

define behavior for ZI_JP_Carrier alias Carrier
persistent table /dmo/carrier
draft table ZJP_D_CARRIER_S
lock dependent by _Singleton
authorization dependent by _Singleton
etag master LocalLastChangedAt
{
  update;
  delete;

  field ( readonly ) SingletonID;
  field ( mandatory : create, readonly : update ) AirlineID;
  field ( mandatory ) Name, CurrencyCode;
  association _Singleton { with draft; }

  mapping for /dmo/carrier
  { AirlineID = carrier_id;
    CurrencyCode = currency_code;
    Name = name;
    LocalCreatedBy = local_created_by;
    LocalCreatedAt = local_created_at;
    LocalLastChangedBy = local_last_changed_by;
    LocalLastChangedAt = local_last_changed_at;
    LastChangedAt = last_changed_at;
  }

}

Class generated from Behavior Definition does not have any code implemented. However, if required validations, determinations etc can be added in the Behavior Definition and implemented in this class.

The draft tables from behavior definitions should be generated using the Eclipse quick-fix feature and the draft definition will look like the below – Click on the below bulb icon and use the action that is suggested. Do not create the draft tables manually.

zjp_d_car_sin_s

@EndUserText.label : 'Draft table for entity ZI_JP_CARRIERSINGLTON'
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zjp_d_car_sin_s {

  key mandt        : mandt not null;
  key singletonid  : abap.int1 not null;
  lastchangedatmax : abap.dec(21,7);
  "%admin"         : include sych_bdl_draft_admin_inc;

}
zjp_d_carrier_s

@EndUserText.label : 'Draft table for entity ZI_JP_CARRIER'
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zjp_d_carrier_s {

  key mandt          : mandt not null;
  key airlineid      : /dmo/carrier_id not null;
  singletonid        : abap.int1;
  name               : /dmo/carrier_name;
  currencycode       : /dmo/currency_code;
  localcreatedby     : abp_creation_user;
  localcreatedat     : abp_creation_tstmpl;
  locallastchangedby : abp_locinst_lastchange_user;
  locallastchangedat : abp_locinst_lastchange_tstmpl;
  lastchangedat      : abp_lastchange_tstmpl;
  "%admin"           : include sych_bdl_draft_admin_inc;

}

Behavior Definition Projection

ZC_JP_CarrierSinglton

projection;
strict ( 2 );
use draft;

define behavior for ZC_JP_CarrierSinglton alias Singlton
{

  use action Activate;
  use action Discard;
  use action Edit;
  use action Prepare;
  use action Resume;

  use association _Airline { create; with draft; }

}

define behavior for ZC_JP_Carrier alias Carrier
use etag
{
  use update;
  use delete;

  use association _Singleton { with draft; }
}

Service Definition

ZSD_JP_Carrier_Singlton

@EndUserText.label: 'Carrier_Singlton Service Def'
define service ZSD_JP_Carrier_Singlton {
  expose ZC_JP_CarrierSinglton as Singleton;
  expose ZC_JP_Carrier as Carrier;
}

Service Binding

Create a new Service Binding with Service definition as a reference and select the Binding Type as OData V4 – UI. Activate and Publish the binding.

Published service binding

Click on Preview with Singleton node selected.

Click Go.

Click on the only line displayed.

The edit is enabled.

Now, SM30 like screen is available for managing the data. There are coupld of extra clicks needed to reach to the screen in preview. In actual UI5 application the data can be preloaded on the list page.

The data can be changed.

Reference: https://help.sap.com/docs/btp/sap-abap-restful-application-programming-model/developing-transactional-apps-with-multi-inline-edit-capabilities

Visit ABAP RESTful Application Programming Model to explore all articles on ABAP RAP Model.


If you like the content, please subscribe…

Join 4,013 other subscribers

Discovering ABAP YouTube Channel