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. An earlier post, External Numbering and Managed Early Numbering covers the details of the options available for Numbering.

This post talks about Unmanaged Early Numbering.

Unmanaged Early Numbering

This scenario is also referred to as ‘Unmanaged Internal Early Numbering’. In unmanaged BOs, the CREATE operation implements the assignment of a primary key value during the CREATE modification. If we have specified early numbering, a method implemented FOR NUMBERING is triggered before the create method to handle the numbering.

Database Table

Persistent Table – zjp_carrier_uen

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

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

}

Draft table – zjp_carrier_uend

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

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

}

CDS View Entities

Root view entity – Z_I_CARRIER_UEN

@EndUserText.label: 'Carrier UEN Example'
@AccessControl.authorizationCheck: #NOT_REQUIRED
define root view entity Z_I_CARRIER_UEN
  as select from zjp_carrier_uen
{
  key cuuid           as Cuuid,
      carrier_id      as CarrierId,
      name            as Name,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at as LastChangedAt
}

Projection Entity – Z_C_CARRIER_UEN

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

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

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

define root view entity Z_C_CARRIER_UEN
  as projection on Z_I_CARRIER_UEN
{

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

Behavior Definition

Unmanaged Behavior Definition – Z_I_CARRIER_UEN

Note that early numbering in unmanaged needs to be implemented with the draft.

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

define behavior for Z_I_CARRIER_UEN alias Carrier
early numbering
draft table zjp_carrier_uend
lock master
total etag LastChangedAt
authorization master ( instance )
{

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

  create;
  update;
  delete;

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

  mapping for zjp_carrier_uen control zjp_x_carrier corresponding
    {
      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_UEN 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 fix feature.

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

Service Definition and Service Binding

Service Definition – ZSD_CARRIER_UEN

@EndUserText.label: 'Unmanaged Early Numbering'
define service ZSD_CARRIER_UEN {
  expose Z_C_CARRIER_UEN as Carrier;
}

Service Binding – ZSB_CARRIER_UEN

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

Sometimes, we may get the below error.

Application could not be started due to technical issues. Service group ‘ZSB_CARRIER_UEN’ not published. In this case, we may have to wait for some time and then try again.

The preview will be displayed

This gets the base service ready for us to implement.

Create a message class with a message for error handling.

Implement Numbering Method: earlynumbering_create FOR NUMBERING

The goal of the method code is to set keys for the entities and to fill either mapped-entity or failed-entity depending on whether the numbering is successful or failed.

  METHOD earlynumbering_create.
    
    "Keep only the entities that do not have the key
    DATA(entities_wo_id) = entities.
    DELETE entities_wo_id WHERE cuuid IS NOT INITIAL.

    DATA(lo_uuid) = cl_uuid_factory=>create_system_uuid( ).
    LOOP AT entities_wo_id INTO DATA(ls_entity).   
      TRY.
          "Generate UUID. Here, number range FM can be called 
          ls_entity-cuuid = lo_uuid->create_uuid_c32( ).
          
          "Add entity to mapped entity, note the draft key
          APPEND VALUE #( %cid      = ls_entity-%cid
                          %key      = ls_entity-%key
                          %is_draft = ls_entity-%is_draft
                        ) TO mapped-carrier.
        CATCH cx_uuid_error INTO DATA(lx_uuid_error).
          "In case of error, append to reported and failed
          APPEND VALUE #( %cid      = ls_entity-%cid
                          %key      = ls_entity-%key
                          %is_draft = ls_entity-%is_draft
                          %msg      = new_message(
                                        id       = 'ZJP_MSG'
                                        number   = '001'
                                        severity = if_abap_behv_message=>severity-error )
                        ) TO reported-carrier.

          APPEND VALUE #( %cid      = ls_entity-%cid
                          %key      = ls_entity-%key
                          %is_draft = ls_entity-%is_draft
                        ) TO failed-carrier.

      ENDTRY.
    ENDLOOP.

  ENDMETHOD.

Test the application for Numbering

When create is clicked, the key field UUID is filled, and create screen appears.

At this point, the create/save would not work. For that, we would need to implement the create and save methods as well. The way RAP BO works is the INSERT/UPDATE/DELETE statements can be written only in the saver class, so we need to create a helper class to pass data from the create method to the save method.

ZCL_CARRIER_GLOBAL

This class has a method to create an instance, another method to create a carrier which stores the carriers to be created as a private data table, and then

CLASS zcl_carrier_global DEFINITION
  PUBLIC
  FINAL
  CREATE PRIVATE.

  PUBLIC SECTION.
    CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_carrier_global.
    METHODS : create_carrier IMPORTING is_carrier TYPE zjp_carrier_uen,
      save,
      initialize.
  PROTECTED SECTION.
  PRIVATE SECTION.
    CLASS-DATA go_instance TYPE REF TO zcl_carrier_global.

    "Buffer tables to save data
    DATA : gt_carrier TYPE STANDARD TABLE OF zjp_carrier_uen.

ENDCLASS.

CLASS zcl_carrier_global 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.
  ENDMETHOD.

  METHOD initialize.
    CLEAR gt_carrier.
  ENDMETHOD.

  METHOD save.
    INSERT zjp_carrier_uen FROM TABLE @gt_carrier.
    initialize( ).
  ENDMETHOD.

ENDCLASS.

Create

DATA : ls_carrier TYPE zjp_carrier_uen,
       lt_carrier TYPE STANDARD TABLE OF zjp_carrier_uen.

LOOP AT entities INTO DATA(ls_entity).

  DATA(lo_carrier) = zcl_carrier_global=>get_instance( ).

  ls_carrier = CORRESPONDING #( ls_entity MAPPING FROM ENTITY USING CONTROL ).

  "Implement Validations and field determinations - here, numbers are not allowed
  IF ls_carrier-carrier_id CA '0123456789'.
    failed-carrier = VALUE #( BASE failed-carrier
                       ( %cid  = ls_entity-%cid
                         %key      = ls_entity-%key
                         %is_draft = ls_entity-%is_draft
                       )
                     ).
    reported-carrier = VALUE #( BASE reported-carrier
                              (  %cid  = ls_entity-%cid
                                 %key  = ls_entity-%key
                                 %is_draft = ls_entity-%is_draft
                                 %msg  = new_message(
                                     id       = 'ZJP_MSG'
                                     number   = '003'
                                     severity = if_abap_behv_message=>severity-error )
                              )
                              ).
  ELSE.
    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
                          cuuid = ls_entity-cuuid )
                      ).
  ENDIF.
ENDLOOP.

Save

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

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_UEN

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 earlynumbering_create FOR NUMBERING
      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_uen,
           lt_carrier TYPE STANDARD TABLE OF zjp_carrier_uen.

    LOOP AT entities INTO DATA(ls_entity).

      DATA(lo_carrier) = zcl_carrier_global=>get_instance( ).

      ls_carrier = CORRESPONDING #( ls_entity MAPPING FROM ENTITY USING CONTROL ).

      "Implement Validations and field determinations
      IF ls_carrier-carrier_id CA '0123456789'.
        failed-carrier = VALUE #( BASE failed-carrier
                           ( %cid  = ls_entity-%cid
                             %key      = ls_entity-%key
                             %is_draft = ls_entity-%is_draft
                           )
                         ).
        reported-carrier = VALUE #( BASE reported-carrier
                                  (  %cid  = ls_entity-%cid
                                     %key  = ls_entity-%key
                                     %is_draft = ls_entity-%is_draft
                                     %msg  = new_message(
                                         id       = 'ZJP_MSG'
                                         number   = '003'
                                         severity = if_abap_behv_message=>severity-error )
                                  )
                                  ).
      ELSE.
        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
                              cuuid = ls_entity-cuuid )
                          ).
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD earlynumbering_create.

    DATA(entities_wo_id) = entities.
    DELETE entities_wo_id WHERE cuuid IS NOT INITIAL.

    LOOP AT entities_wo_id INTO DATA(ls_entity).
      DATA(lo_uuid) = cl_uuid_factory=>create_system_uuid( ).
      TRY.
          ls_entity-cuuid = lo_uuid->create_uuid_c32( ).
          APPEND VALUE #( %cid      = ls_entity-%cid
                          %key      = ls_entity-%key
                          %is_draft = ls_entity-%is_draft
                        ) TO mapped-carrier.
        CATCH cx_uuid_error INTO DATA(lx_uuid_error).

          APPEND VALUE #( %cid      = ls_entity-%cid
                          %key      = ls_entity-%key
                          %is_draft = ls_entity-%is_draft
                          %msg      = new_message(
                                        id       = 'ZJP_MSG'
                                        number   = '001'
                                        severity = if_abap_behv_message=>severity-error )
                        ) TO reported-carrier.

          APPEND VALUE #( %cid      = ls_entity-%cid
                          %key      = ls_entity-%key
                          %is_draft = ls_entity-%is_draft
                        ) TO failed-carrier.

      ENDTRY.
    ENDLOOP.

  ENDMETHOD.

  METHOD update.
  ENDMETHOD.

  METHOD delete.
  ENDMETHOD.

  METHOD read.
  ENDMETHOD.

  METHOD lock.
  ENDMETHOD.

ENDCLASS.

CLASS lsc_z_i_carrier_uen DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.

    METHODS finalize REDEFINITION.

    METHODS check_before_save REDEFINITION.

    METHODS save REDEFINITION.

    METHODS cleanup REDEFINITION.

    METHODS cleanup_finalize REDEFINITION.

ENDCLASS.

CLASS lsc_z_i_carrier_uen IMPLEMENTATION.

  METHOD finalize.
  ENDMETHOD.

  METHOD check_before_save.
  ENDMETHOD.

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

  METHOD cleanup.
  ENDMETHOD.

  METHOD cleanup_finalize.
  ENDMETHOD.

ENDCLASS.

Test Create end to end

When a numeric id is entered, the application throws an error.

Change the id to match the validation.

Click on create.

The object is created and can be also seen on the list page.

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