This post talks about Numbering in RAP BO. Numbering means setting up values for the primary key fields of the entity. The primary key can consist of one or more fields.

Options to handle Numbering

Options are determined by when the numbering is handled and who handles it.

Early and Late Numbering (When it is handled?)

Early Numbering

  • As the name suggests, the numbering is handled early i.e. the primary key value is set as soon as the request for operation create is executed.
  • The key values can be
    1. passed externally by the consumer or
    2. set internally by the framework or
    3. set internally by an implementation of the FOR NUMBERING method.

Late Numbering

  • The numbering is handled late i.e. just before the data is saved.
  • The key values are always assigned internally
  • Late numbering can be used to ensure gap-free numbering.

Managed and Unmanaged Numbering (Who handles it?)

Managed Numbering

  • A UUID is fetched automatically during the CREATE request by the framework
  • Managed early numbering is only possible for key fields with ABAP type raw(16) (UUID)
  • The implementation type of the RAP BO has to be managed.

Unmanaged Numbering

  • The numbering is managed by the developer in the method implemented FOR NUMBERING 
  • This can be early or late

Based on the scenarios discussed earlier, it is important to note that Late numbering is not possible in the managed scenario.

Early NumberingLate Numbering
ExternalYesNo
ManagedYesFrom version 2111
UnmanagedYesYes

Now, we will discuss the examples of 4 possible numbering combinations mentioned in the above table.

External Early Numbering

  • Here, the consumer hands over the primary key values for the CREATE operation.
  • As the value for the key field is passed by the consumer, the uniqueness has to be checked explicitly. We will discuss this in another post in more detail.
  • The primary key fields should not be read-only at create and read-only on update
  • One of the variants in External Early Numbering is Optional External Early Numbering where both external and internal numbering is possible i.e. if the consumer passes the key it will be used, else the framework will generate a key.

Persistent Table Used

@EndUserText.label : 'Flight Reference Scenario: Carrier'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zjp_carrier {

  key client            : abap.clnt not null;
  key carrier_id        : /dmo/carrier_id not null;
  name                  : /dmo/carrier_name;
  currency_code         : /dmo/currency_code;
  is_complete           : abap.char(1);
  no_of_connections     : abap.int1;
  local_created_by      : abp_creation_user;
  local_created_at      : abp_creation_tstmpl;
  local_last_changed_by : abp_locinst_lastchange_user;
  local_last_changed_at : abp_locinst_lastchange_tstmpl;
  last_changed_at       : abp_lastchange_tstmpl;

}

Create Root CDS View Entity and Projection

Z_I_CARRIER_N

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Carrier Data'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define root view entity Z_I_CARRIER_N
  as select from zjp_carrier
{
  key carrier_id            as CarrierId,
      name                  as Name,
      currency_code         as CurrencyCode,
      is_complete           as IsComplete,
      no_of_connections     as NoOfConnections,
      @Semantics.user.createdBy: true
      local_created_by      as LocalCreatedBy,
      @Semantics.systemDateTime.createdAt: true
      local_created_at      as LocalCreatedAt,
      @Semantics.user.lastChangedBy: true
      local_last_changed_by as LocalLastChangedBy,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      local_last_changed_at as LocalLastChangedAt,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at       as LastChangedAt
}

Z_C_CARRIER_N

The projection CDS is annotated directly here. Metadata Extension can also be used for the same.

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

@UI: {
    headerInfo: {
        typeName: 'Carrier',
        typeNamePlural: 'Carrier',
        title: {
            type: #STANDARD, value: 'Name'
        },
        description: {
            value: 'CarrierID'
        }
    }
}

define root view entity Z_C_CARRIER_N
  provider contract transactional_query
  as projection on Z_I_CARRIER_N
{

      @UI.facet:
        [ {
              label: 'Carrier Details',
              id: 'CarrierInfo',
              type: #COLLECTION,
              position: 10
           },
           {
              label: 'Carrier',
              id: 'Carrier',
              type: #IDENTIFICATION_REFERENCE,
              purpose: #STANDARD,
              parentId: 'CarrierInfo',
              position: 10
           } ]


      @UI.lineItem: [{ position: 10 }]
      @UI.identification: [{ position: 10 }]
  key CarrierId,
      @UI.lineItem: [{ position: 20 }]
      @UI.identification: [{ position: 20 }]
      Name,
      @UI.lineItem: [{ position: 30 }]
      @UI.identification: [{ position: 30 }]
      CurrencyCode,
      @UI.lineItem: [{ position: 40 }]
      @UI.identification: [{ position: 40 }]
      LastChangedAt
}

Behavior Definition and Projection

Create a managed definition. Generate the class but do not add any code.

Z_I_CARRIER_N

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

define behavior for Z_I_CARRIER_N alias Carrier
persistent table zjp_carrier
draft table zjp_d_carrier
lock master
total etag LastChangedAt
authorization master ( instance )

{

  field ( mandatory ) Name, CurrencyCode;
  field (readonly : update) CarrierId;
  field (readonly) LastChangedAt;

  create;
  update;
  delete;

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

  mapping for zjp_carrier control zjp_x_carrier corresponding
    {
      CarrierId          = carrier_id;
      Name               = name;
      CurrencyCode       = currency_code;
      LastChangedAt      = last_changed_at;
    }


}

Note that the behavior definition field does not mention early numbering or late numbering explicitly.

Draft table generated

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

  key mandt          : mandt not null;
  key carrierid      : /dmo/carrier_id not null;
  name               : /dmo/carrier_name;
  currencycode       : /dmo/currency_code;
  iscomplete         : abap.char(1);
  noofconnections    : abap.int1;
  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;

}

Z_C_CARRIER_N

projection;
strict ( 2 );
use draft;

define behavior for Z_C_CARRIER_N alias Carrier
{
  use create;
  use update;
  use delete;

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

Create Service Definition

ZSD_Carrier_N

@EndUserText.label: 'Numbering Demo'
define service ZSD_Carrier_N {
  expose Z_C_CARRIER_N as Carrier;
}

Create Service Binding

Create service binding, activate it, and then publish the binding.

Preview the Application and Test the External Numbering

Click on create.

The application will ask for the key field value. Enter the value and click Continue.

Provide other details and click on Create.

The Carrier ID gets created.

The key field is not editable in update mode.

If you try to create another record with the same key, the application will show an error.

As this is managed scenario, the error is shown by the RAP framework and we do not have to implement anything.

Managed Early Numbering

If we add UUID field to the same scenario, it will be an example of Managed Early Numbering. For simplicity, all objects from the above section are copied and a field cUUID is added. Changes are highlighted.

Persistent Table Used

@EndUserText.label : 'Flight Reference Scenario: Carrier'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zjp_carrier_uuid {

  key client            : abap.clnt not null;
  key cuuid             : sysuuid_x16 not null;
  carrier_id            : /dmo/carrier_id;
  name                  : /dmo/carrier_name;
  currency_code         : /dmo/currency_code;
  is_complete           : abap.char(1);
  no_of_connections     : abap.int1;
  local_created_by      : abp_creation_user;
  local_created_at      : abp_creation_tstmpl;
  local_last_changed_by : abp_locinst_lastchange_user;
  local_last_changed_at : abp_locinst_lastchange_tstmpl;
  last_changed_at       : abp_lastchange_tstmpl;

}

Create Root CDS View Entity and Projection

Z_I_CARRIER_UUID

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Carrier Data'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
    serviceQuality: #X,
    sizeCategory: #S,
    dataClass: #MIXED
}
define root view entity Z_I_CARRIER_UUID
  as select from zjp_carrier_uuid
{
  key cuuid                 as cUUID,
      carrier_id            as CarrierId,
      name                  as Name,
      currency_code         as CurrencyCode,
      is_complete           as IsComplete,
      no_of_connections     as NoOfConnections,
      @Semantics.user.createdBy: true
      local_created_by      as LocalCreatedBy,
      @Semantics.systemDateTime.createdAt: true
      local_created_at      as LocalCreatedAt,
      @Semantics.user.lastChangedBy: true
      local_last_changed_by as LocalLastChangedBy,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      local_last_changed_at as LocalLastChangedAt,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at       as LastChangedAt
}

Z_C_CARRIER_UUID

The projection CDS is annotated directly here. Metadata Extension can also be used for the same.

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

@UI: {
    headerInfo: {
        typeName: 'Carrier',
        typeNamePlural: 'Carrier',
        title: {
            type: #STANDARD, value: 'Name'
        },
        description: {
            value: 'CarrierID'
        }
    }
}

define root view entity Z_C_CARRIER_UUID

  provider contract transactional_query
  as projection on Z_I_CARRIER_UUID

{

      @UI.facet:
        [ {
              label: 'Carrier Details',
              id: 'CarrierInfo',
              type: #COLLECTION,
              position: 10
           },
           {
              label: 'Carrier',
              id: 'Carrier',
              type: #IDENTIFICATION_REFERENCE,
              purpose: #STANDARD,
              parentId: 'CarrierInfo',
              position: 10
           } ]


      @UI.lineItem: [{ position: 05 }]
      @UI.identification: [{ position: 05 }]
  key cUUID,

      @UI.lineItem: [{ position: 10 }]
      @UI.identification: [{ position: 10 }]
      CarrierId,
      @UI.lineItem: [{ position: 20 }]
      @UI.identification: [{ position: 20 }]
      Name,
      @UI.lineItem: [{ position: 30 }]
      @UI.identification: [{ position: 30 }]
      CurrencyCode,
      @UI.lineItem: [{ position: 40 }]
      @UI.identification: [{ position: 40 }]
      LastChangedAt
}

Behavior Definition and Projection

Create a managed definition. Generate the class but do not add any code.

Z_I_CARRIER_UUID

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

define behavior for Z_I_CARRIER_UUID alias Carrier
persistent table zjp_carrier_uuid

draft table zjp_d_cuuid

lock master
total etag LastChangedAt
authorization master ( instance )

{

  field ( mandatory ) CarrierId, Name, CurrencyCode;
  field ( readonly ) cUUID, LastChangedAt;
  field ( numbering : managed ) cUUID;

  create;
  update;
  delete;

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

  mapping for zjp_carrier_uuid control zjp_x_carrier corresponding
    {
      CarrierId     = carrier_id;
      Name          = name;
      CurrencyCode  = currency_code;
      LastChangedAt = last_changed_at;
    }
}

Note that the behavior definition field does mention numbering is managed.

field ( numbering : managed ) cUUID;

Draft table created

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

  key mandt          : mandt not null;
  key cuuid          : sysuuid_x16 not null;
  carrierid          : /dmo/carrier_id not null;
  name               : /dmo/carrier_name;
  currencycode       : /dmo/currency_code;
  iscomplete         : abap.char(1);
  noofconnections    : abap.int1;
  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;

}

Z_C_CARRIER_UUID

projection;
strict ( 2 );
use draft;

define behavior for Z_C_CARRIER_UUID alias Carrier
{
  use create;
  use update;
  use delete;

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

Create Service Definition

ZSD_Carrier_N

@EndUserText.label: 'Numbering Demo Managed'
define service ZSD_Carrier_UUID
{
    expose Z_C_CARRIER_UUID as Carrier;
    
}

Create Service Binding

Create service binding, activate it, and then publish the binding.

Preview the Application and Test the External Numbering

As soon as you enter the create mode the UUID number is generated. This demonstrates the managed early numbering.

Reference: https://help.sap.com/docs/btp/sap-abap-restful-application-programming-model/numbering

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


If you like the content, please subscribe…

Join 4,016 other subscribers

Discovering ABAP YouTube Channel