Numbering in ABAP RESTful Application Programming means setting up values for the primary key fields of the entity. The primary key can consist of one or more fields.

Earlier posts cover

  1. the details of the options available for Numberin – External Numbering and Managed Early Numbering
  2. Unmanaged Early Numbering

This post talks about Unmanaged Late Numbering.

Unmanaged Late Numbering

This scenario is also referred to as ‘Unmanaged Internal Late Numbering’. This scenario is useful for a gapless assignment of unique keys.

Internal late numbering means:

  • The RAP runtime engine assigns values to the primary key fields.
  • The RAP saver method adjust_numbers must be implemented to assign a final primary key value.
  • The key value for an instance is assigned just before the instance is saved on the database.

Note that this posts explains how the methods can be utilized and more research is required for implementation of actual project like scenario.

Database table & structure

Persistent Table – zjp_carrier_uln

@EndUserText.label : 'Unmanaged Late Numbering (ULN)'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zjp_carrier_uln {

  key client      : abap.clnt not null;
  key intid       : abap.int4 not null;
  carrier_id      : /dmo/carrier_id;
  name            : /dmo/carrier_name;
  last_changed_at : abp_lastchange_tstmpl;

}

Draft table – zjp_d_carr_uln

@EndUserText.label : 'Unmanaged Late Numbering (ULN) Draft Table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zjp_d_carr_uln {

  key client    : abap.clnt not null;
  key intid     : abap.int4 not null;
  key draftuuid : sysuuid_x16 not null;
  carrierid     : /dmo/carrier_id;
  name          : /dmo/carrier_name;
  lastchangedat : abp_lastchange_tstmpl;
  "%admin"      : include sych_bdl_draft_admin_inc;

}

Control Structure – ZJP_ULN_CARRIER

@EndUserText.label : 'ULN Carrier'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure zjp_uln_carrier {

  int_id          : xsdboolean;
  carrier_id      : xsdboolean;
  name            : xsdboolean;
  last_changed_at : xsdboolean;

}

CDS View Entities

Root view entity – Z_I_CARRIER_ULN

@EndUserText.label: 'Carrier ULN Example'
@AccessControl.authorizationCheck: #NOT_REQUIRED
define root view entity Z_I_CARRIER_ULN
  as select from zjp_carrier_uln
{
  key intid           as IntId,
      carrier_id      as CarrierId,
      name            as Name,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at as LastChangedAt
}

Projection Entity – Z_C_CARRIER_ULN

Here, the annotations are added in the projection entity itself, but it is also possible to create metadata extensions.

@EndUserText.label: 'Carrier ULN Example'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ObjectModel.semanticKey: [ 'IntId' ]

@UI: {
    headerInfo: {
        typeName: 'Carrier',
        typeNamePlural: 'Carrier'
    }
}

define root view entity Z_C_CARRIER_ULN
  as projection on Z_I_CARRIER_ULN
{

      @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 IntId,
      @UI.lineItem: [{ position: 20 }]
      @UI.identification: [{ position: 20 }]
      CarrierId,
      @UI.lineItem: [{ position: 30 }]
      @UI.identification: [{ position: 30 }]
      Name,
      @UI.hidden: true
      LastChangedAt
}

Behavior Definition

Unmanaged Behavior Definition – Z_I_CARRIER_ULN

The late numbering in unmanaged can be implemented with or without a draft. This example considers a scenario with the draft as the V4 service will show create button only with draft functionality.

unmanaged implementation in class zbp_i_carrier_uln unique;
strict ( 2 );
with draft;

define behavior for Z_I_CARRIER_ULN alias Carrier
late numbering
draft table zjp_d_carr_uln
lock master
total etag LastChangedAt
authorization master ( instance )
{

  field ( mandatory ) CarrierId, Name;
  field ( readonly ) IntId, LastChangedAt;

  create;
  update;
  delete;

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

  mapping for zjp_carrier_uln control zjp_uln_carrier corresponding
    {
      IntId         = intid;
      CarrierId     = carrier_id;
      Name          = name;
      LastChangedAt = last_changed_at;
    }


}

Create the projection behavior definition.

projection;
strict ( 2 );
use draft;

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

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

}

Implementation Class

Create the class using the quick assist feature.

Class is generated. No code is implemented at this point. We will first generate the service, test the display, and implement the code.

Service Definition and Service Binding

Service Definition – ZSD_CARRIER_ULN

@EndUserText.label: 'Unmanaged Late Numbering'
define service ZSD_CARRIER_ULN {
  expose Z_C_CARRIER_ULN as Carrier;
}

Service Binding – ZSB_CARRIER_ULN

Activate the service binding, and publish the service. Preview and service and ensure that the display part is working.

The preview will be displayed

This gets the base service ready for us to implement.

Support Class to save data buffer – zcl_carrier_uln

CLASS zcl_carrier_uln DEFINITION
  PUBLIC
  FINAL
  CREATE PRIVATE.

  PUBLIC SECTION.
    TYPES : tty_carriers TYPE STANDARD TABLE OF zjp_carrier_uln WITH EMPTY KEY.

    CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_carrier_uln.
    METHODS : create_carrier IMPORTING is_carrier TYPE zjp_carrier_uln,
      adjust_number RETURNING VALUE(rt_carriers) TYPE tty_carriers,
      save,
      initialize.
  PROTECTED SECTION.
  PRIVATE SECTION.
    CLASS-DATA go_instance TYPE REF TO zcl_carrier_uln.

    "Buffer tables to save data
    DATA : gt_carrier_create TYPE STANDARD TABLE OF zjp_carrier_uln.


ENDCLASS.

CLASS zcl_carrier_uln IMPLEMENTATION.

  METHOD get_instance.
    go_instance = COND #( WHEN go_instance IS BOUND THEN go_instance ELSE NEW #( ) ).
    ro_instance = go_instance.
  ENDMETHOD.

  METHOD create_carrier.
    APPEND is_carrier TO gt_carrier_create.
  ENDMETHOD.

  METHOD adjust_number.

    "Number range object can be called here
    "For demo, highest number from table is used

    SELECT MAX( intid )
        FROM zjp_carrier_uln
        INTO @DATA(lv_max_id).

    LOOP AT gt_carrier_create ASSIGNING FIELD-SYMBOL(<lfs_carr>).
      lv_max_id += 1.
      <lfs_carr>-intid = lv_max_id.
    ENDLOOP.

    rt_carriers = gt_carrier_create.

  ENDMETHOD.

  METHOD initialize.
    CLEAR gt_carrier_create.
  ENDMETHOD.

  METHOD save.
    INSERT zjp_carrier_uln FROM TABLE @gt_carrier_create.
    initialize( ).
  ENDMETHOD.

ENDCLASS.

Implement unmanaged create

  METHOD create.

    DATA : ls_carrier TYPE zjp_carrier_uln.

    LOOP AT entities INTO DATA(ls_entity).

      DATA(lo_carrier) = zcl_carrier_uln=>get_instance( ).
      ls_carrier = CORRESPONDING #( ls_entity MAPPING FROM ENTITY USING CONTROL ).

      "Implement Validations and field determinations here - if needed

      lo_carrier->create_carrier( ls_carrier ).
      mapped-carrier = VALUE #( BASE mapped-carrier
                          ( %cid      = ls_entity-%cid
                            %key      = ls_entity-%key
                            %is_draft = ls_entity-%is_draft
                           ) ).

    ENDLOOP.

  ENDMETHOD.

Implement Numbering Method: adjust_numbers

For late numbering, the method adjust_numbers is implemented in the behavior-saver class.

The goal of the method code is to determine key values, set the keys and then fill mapped-entity. As the actual numbering logic is implemented in the helper class – this code looks simpler.

  METHOD adjust_numbers.
    DATA(lo_carrier) = zcl_carrier_uln=>get_instance( ).
    data(lt_carriers) = lo_carrier->adjust_number( ).

    mapped-carrier = VALUE #( for ls_carrier in lt_carriers
                             ( intid = ls_carrier-intid )
                            ).

  ENDMETHOD.

Implement Save method

  METHOD save.
    DATA(lo_carrier) = zcl_carrier_uln=>get_instance( ).
    lo_carrier->save( ).
  ENDMETHOD.

Test the application for Numbering

When create is clicked, the key field is 0.

Similarly, update and delete operations can be implemented using separate buffer tables for create, update, and delete.

For reference, the complete class code is as below.

ZBP_I_CARRIER_ULN

CLASS lhc_carrier DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR carrier RESULT result.

    METHODS create FOR MODIFY
      IMPORTING entities FOR CREATE carrier.

    METHODS update FOR MODIFY
      IMPORTING entities FOR UPDATE carrier.

    METHODS delete FOR MODIFY
      IMPORTING keys FOR DELETE carrier.

    METHODS read FOR READ
      IMPORTING keys FOR READ carrier RESULT result.

    METHODS lock FOR LOCK
      IMPORTING keys FOR LOCK carrier.

ENDCLASS.

CLASS lhc_carrier IMPLEMENTATION.

  METHOD get_instance_authorizations.
  ENDMETHOD.

  METHOD create.

    DATA : ls_carrier TYPE zjp_carrier_uln.

    LOOP AT entities INTO DATA(ls_entity).

      DATA(lo_carrier) = zcl_carrier_uln=>get_instance( ).
      ls_carrier = CORRESPONDING #( ls_entity MAPPING FROM ENTITY USING CONTROL ).

      "Implement Validations and field determinations here - if needed

      lo_carrier->create_carrier( ls_carrier ).
      mapped-carrier = VALUE #( BASE mapped-carrier
                          ( %cid      = ls_entity-%cid
                            %key      = ls_entity-%key
                            %is_draft = ls_entity-%is_draft
                           ) ).

    ENDLOOP.

  ENDMETHOD.

  METHOD update.
  ENDMETHOD.

  METHOD delete.
  ENDMETHOD.

  METHOD read.
  ENDMETHOD.

  METHOD lock.
  ENDMETHOD.

ENDCLASS.

CLASS lsc_z_i_carrier_uln DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.

    METHODS finalize REDEFINITION.

    METHODS check_before_save REDEFINITION.

    METHODS adjust_numbers REDEFINITION.

    METHODS save REDEFINITION.

    METHODS cleanup REDEFINITION.

    METHODS cleanup_finalize REDEFINITION.

ENDCLASS.

CLASS lsc_z_i_carrier_uln IMPLEMENTATION.

  METHOD finalize.
  ENDMETHOD.

  METHOD check_before_save.
  ENDMETHOD.

  METHOD adjust_numbers.
    DATA(lo_carrier) = zcl_carrier_uln=>get_instance( ).
    data(lt_carriers) = lo_carrier->adjust_number( ).

    mapped-carrier = VALUE #( for ls_carrier in lt_carriers
                             ( intid = ls_carrier-intid )
                            ).

  ENDMETHOD.

  METHOD save.
    DATA(lo_carrier) = zcl_carrier_uln=>get_instance( ).
    lo_carrier->save( ).
  ENDMETHOD.

  METHOD cleanup.
  ENDMETHOD.

  METHOD cleanup_finalize.
  ENDMETHOD.

ENDCLASS.

Test the application

Create a new carrier.

After creation, the key is visible.

Also read:

  1. External Numbering and Managed Early Numbering
  2. Unmanaged Early Numbering

References:

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


If you like the content, please subscribe…

Join 4,017 other subscribers

Discovering ABAP YouTube Channel