Early Numbering in RAP ABAP

Early numbering is a critical concept in SAP’s RAP (ABAP RESTful Application Programming Model) that determines when and how unique identifiers are generated for business objects. Unlike traditional approaches where numbers might be assigned during the save sequence, early numbering provides significant advantages by generating keys before the transaction reaches the database.

Why Early Numbering Matters

  • Immediate Feedback: Users see assigned numbers immediately after creation
  • Transaction Integrity: Prevents collisions in complex business processes
  • Downstream Processing: Enables reference to new objects before persistence
  • Better UX: Allows showing generated numbers in UI before saving

Implementing Early Numbering

Basic Implementation

@ObjectModel.modelEntity: {
  createEnabled: true,
  earlyNumbering: cue
}
define behavior for ZI_SalesOrder alias SalesOrder
{
  // Behavior implementation
}

Custom Early Numbering Handler

CLASS lhc_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.
    METHODS earlynumbering FOR NUMBERING
      IMPORTING entities FOR SalesOrder.
ENDCLASS.

CLASS lhc_salesorder IMPLEMENTATION.
  METHOD earlynumbering.
    DATA: max_so_id TYPE char10.
    
    " Get current maximum SO number
    SELECT MAX( sales_order ) FROM zsalesorders INTO @max_so_id.
    
    " Assign numbers to new entities
    LOOP AT entities INTO DATA(entity).
      IF entity-sales_order IS INITIAL.
        max_so_id = max_so_id + 1.
        APPEND VALUE #( %cid = entity-%cid 
                       sales_order = max_so_id ) 
              TO mapped-salesorder.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

Key Technical Aspects

The Numbering Phase

Early numbering occurs:

  1. During draft handling (for draft-enabled scenarios)
  2. Before modify operations reach the database
  3. After input validation but before save sequence

The Mapped Table

The mapped structure is crucial for early numbering:

  • Contains mapping between %cid (client instance ID) and generated key
  • Ensures consistency between UI and backend

Locking Considerations

For custom implementations:

METHOD earlynumbering.
  " Acquire lock
  CALL FUNCTION 'ENQUEUE_EZ_SO_NUM'.
  
  " Number generation logic
  
  " Release lock
  CALL FUNCTION 'DEQUEUE_EZ_SO_NUM'.
ENDMETHOD.

Hybrid Numbering Approach

METHOD earlynumbering.
  LOOP AT entities INTO DATA(entity).
    IF entity-sales_order IS NOT INITIAL.
      " Validate externally provided number
      IF NOT is_valid( entity-sales_order ).
        APPEND VALUE #( %cid = entity-%cid )
               TO failed-salesorder.
        APPEND VALUE #( %cid = entity-%cid
                       %msg = new_message(...) )
               TO reported-salesorder.
      ENDIF.
    ELSE.
      " Generate internal number
      entity-sales_order = generate_number( ).
      APPEND VALUE #( %cid = entity-%cid 
                     sales_order = entity-sales_order ) 
            TO mapped-salesorder.
    ENDIF.
  ENDLOOP.
ENDMETHOD.

Number Range Integration

METHOD earlynumbering.
  DATA: number_range TYPE nriv.
  
  CALL FUNCTION 'NUMBER_GET_NEXT'
    EXPORTING
      nr_range_nr = '01'
      object      = 'ZSO_NUM'
    IMPORTING
      number      = DATA(new_number).
  
  " Assign to entities...
ENDMETHOD.

End-to-End Example of Early Numbering in SAP RAP

We’ll create a Sales Order application with:

  • Automatic number generation when no number is provided
  • Validation for externally provided numbers
  • Integration with SAP number ranges
  • Full draft handling support

Data Model Definition

2.1 Base CDS View (ZC_SalesOrder)

@AbapCatalog.sqlViewName: 'ZCSALESORDER'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order CDS View'
define view ZC_SalesOrder as select from zsalesorder_db {
  key sales_order    : zsalesorder_id,
      customer       : zcustomer_id,
      order_date     : abap.dats,
      total_amount   : zamount,
      currency       : zcurrency,
      status         : zstatus
}

Projection View (ZC_SalesOrder_Projection)

@UI: {
  headerInfo: {
    typeName: 'Sales Order',
    typeNamePlural: 'Sales Orders'
  }
}
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
define view ZC_SalesOrder_Projection 
  as select from ZC_SalesOrder
{
  key sales_order,
      customer,
      order_date,
      total_amount,
      currency,
      status
}

Behavior Definition

Behavior Definition (ZC_SalesOrder)

managed implementation in class zbp_salesorder unique;
strict;
with draft;

define behavior for ZC_SalesOrder alias SalesOrder
persistent table zsalesorder_db
draft table zsalesorder_dft
lock master
authorization master ( instance )
early numbering
{
  create;
  update;
  delete;
  
  field ( readonly ) sales_order;
  
  determination EarlyNumbering on save { create; }
  
  validation ValidateCustomer on save { create; update; }
  
  draft action Edit;
  draft action Activate;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare;
}

Behavior Implementation

Behaviour Pool Class (ZBP_SalesOrder)

CLASS zbp_salesorder DEFINITION PUBLIC ABSTRACT FINAL 
                    FOR BEHAVIOR OF zc_salesorder.
ENDCLASS.

CLASS zbp_salesorder IMPLEMENTATION.
ENDCLASS.

Behavior Implementation Class (ZCL_SalesOrder)

CLASS zcl_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.
    
    METHODS earlynumbering FOR NUMBERING
      IMPORTING entities FOR SalesOrder.
      
    METHODS validatecustomer FOR VALIDATE ON SAVE
      IMPORTING keys FOR SalesOrder~validatecustomer.
      
    METHODS get_instance_features FOR INSTANCE FEATURES
      IMPORTING keys REQUEST requested_features FOR SalesOrder RESULT result.
ENDCLASS.

CLASS zcl_salesorder IMPLEMENTATION.
  
  METHOD earlynumbering.
    DATA: salesorder_numbers TYPE TABLE FOR NUMBERING zc_salesorder.
    
    " Get next available numbers from number range
    LOOP AT entities INTO DATA(entity).
      IF entity-sales_order IS INITIAL.
        CALL FUNCTION 'NUMBER_GET_NEXT'
          EXPORTING
            nr_range_nr = '01'
            object      = 'ZSO_NUM'
          IMPORTING
            number      = DATA(salesorder_number).
            
        APPEND VALUE #( %cid      = entity-%cid
                        sales_order = salesorder_number )
               TO mapped-salesorder.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.
  
  METHOD validatecustomer.
    " Validate customer exists
    SELECT customer_id FROM zcustomers
      FOR ALL ENTRIES IN @keys
      WHERE customer_id = @keys-customer
      INTO TABLE @DATA(valid_customers).
    
    LOOP AT keys INTO DATA(key).
      IF NOT line_exists( valid_customers[ customer_id = key-customer ] ).
        APPEND VALUE #( %tky = key-%tky ) TO failed-salesorder.
        APPEND VALUE #( %tky = key-%tky
                        %msg = new_message( id       = 'ZSO_MSG'
                                            number   = '001'
                                            severity = if_abap_behv_message=>severity-error ) )
               TO reported-salesorder.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.
  
  METHOD get_instance_features.
    " Implementation for UI controls
  ENDMETHOD.
  
ENDCLASS.

Number Range Configuration

Create Number Range Object

  1. Go to transaction SNRO
  2. Create object ZSO_NUM
  3. Define intervals (e.g., 1000000000-1999999999)

Number Range Maintenance

" Alternative in-code initialization (optional)
CALL FUNCTION 'NUMBER_RANGE_INITIALIZE'
  EXPORTING
    object = 'ZSO_NUM'.

UI Service Definition

Service Definition (ZSD_SalesOrder)

@EndUserText.label: 'Sales Order Service'
define service ZSD_SalesOrder {
  expose ZC_SalesOrder_Projection as SalesOrder;
}

Service Binding (ZSB_SalesOrder)

@EndUserText.label: 'Sales Order Service Binding'
service ZSB_SalesOrder {
  bind ZSD_SalesOrder;
}

Testing the Implementation

Create Operation

DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination(
  cl_http_destination_provider=>create_by_url( '.../SalesOrder' )).

DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_method( 'POST' ).
lo_request->set_content_type( 'application/json' ).

DATA(lv_payload) = `{
  "customer": "100001",
  "order_date": "20231015",
  "currency": "USD"
}`.

lo_request->set_text( lv_payload ).

DATA(lo_response) = lo_client->execute( lo_request ).

Adding Draft Support

METHOD earlynumbering.
  IF draft-enabled( ) AND is_draft( entities[ 1 ]-%is_draft ).
    " Special handling for draft numbering
  ELSE.
    " Regular numbering logic
  ENDIF.
ENDMETHOD.

Custom Number Formatting

METHOD format_salesorder_number.
  " Example: SO-2023-0000001
  CONCATENATE 'SO-' sy-datum(4) '-' lpad( iv_number, 7, '0' ) 
    INTO rv_formatted_number.
ENDMETHOD.

Early numbering in SAP RAP represents a significant evolution from traditional ABAP programming models. By generating identifiers early in the business object lifecycle, it enables:

  • Better user experience with immediate feedback
  • More robust transaction processing
  • Simplified integration with downstream systems
  • Flexible numbering schemes to meet business requirements

For developers transitioning to RAP, mastering early numbering is essential for building modern, responsive applications that meet today’s business demands.

1 thought on “Early Numbering in RAP ABAP”

Leave a Comment