Skip to Content
Technical Articles
Author's profile photo Andre Fischer

How to develop an unmanaged RAP business object with draft and semantic keys

There have been several questions in the community how unmanaged scenarios with draft are handled in the ABAP RESTful Application Programming Model (RAP).

An important fact that is not well known is, that the draft that is added to an unmanaged scenario itself is managed.

Therefore I was able to reuse the implementation of the managed Flight sample /DMO/I_Travel_M that also uses semantic keys and early numbering.

I plan to publish several examples based on the on well known on premise Enterprise Procurement Data Model that comes with Products, Business Partners, Sales Orders and Sales Order Items where I would like to address the special requirements when you have to handle BAPI calls in on premise systems.

However to answer the most pressing question that was how semantic key fields are populated in a draft enabled scenario I started to implement a first small sample based on the travel sample.

1. Handling of semantic key fields

The problem with semantic key fields in draft scenarios is that when creating several draft entities they all will be created with an empty key. Already when trying to create a second item a short dump will be raised which indicates that it was tried to insert an entry into the draft table with a duplicated key.

A duplicate primary key '00000000' was detected when inserting data into persistence 'ZTRAVELSEM00D_01'.

Solution : Early Numbering

The documentation about early numbering can be found here:

Internal Early Numbering – SAP Help Portal

To demonstrate the use of internal early numbering in an unmanaged scenario I have created the following Z-table based on the flight reference scenario:

@EndUserText.label : 'Flight Reference Scenario: Managing Travels'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table ztravel_sem_01 {
  key client    : abap.clnt not null;
  key travel_id : /dmo/travel_id not null;
  description_2 : abap.char(50);
  gr_data       : include /dmo/travel_data;
  gr_admin      : include /dmo/travel_admin;

}

 

In the behavior definition (BDEF) we have to specify that early numbering is used.

 

unmanaged;
with draft;

define behavior for ZI_TRAVELSEMANTIC_01 alias TravelSemantic
implementation in class ZBP_I_TRAVELSEMANTIC_01 unique
draft table ztravelsem00d_01
etag master Lastchangedat
lock master total etag Lastchangedat
early numbering
authorization master ( global )

{
  field ( readonly )
  TravelID;


  create;
  update;
  delete;

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

  mapping for ZTRAVEL_SEM_01 control ZSTravelSemantic_X_01
  {
    TravelID = TRAVEL_ID;
    Description2 = DESCRIPTION_2;
    AgencyID = AGENCY_ID;
    CustomerID = CUSTOMER_ID;
    BeginDate = BEGIN_DATE;
    EndDate = END_DATE;
    BookingFee = BOOKING_FEE;
    TotalPrice = TOTAL_PRICE;
    CurrencyCode = CURRENCY_CODE;
    Description = DESCRIPTION;
    Status = STATUS;
    Createdby = CREATEDBY;
    Createdat = CREATEDAT;
    Lastchangedby = LASTCHANGEDBY;
    Lastchangedat = LASTCHANGEDAT;
  }
}

 

In the behavior implementation class we have to add a method of type FOR NUMBERING.

METHODS earlyNumbering_Create FOR NUMBERING
IMPORTING entities FOR CREATE TravelSemantic.

In the implementation of this method (code see below) we have first to check for which entities which have been handed over to us the key field is not yet set.

Then we calculate the travelid that should be used. Here we use a poor mans approach by simply selecting the highest value for a travelid from the normal as well as from the draft table.

Since we are in the create phase the %tky is not yet filled. So we have to populate the mapped return table with values for %cid, %key and %is_draft that were handed over to us by the RAP framework.

Usually you will use a number range here or if the semantic key values are provided by the BAPI you would use those.

Result

After having added the early numbering statement in the BDEF and after having implemented the method earlyNumbering_Create the app works correctly and provides TravelID’s for each newly created draft elements.

 

Pitfalls

When you simply start with entering the method for early numbering in your behavior implementation class you get the error message

The operation 'CREATE' is not activated for entity "ZI_TRAVELSEMANTIC_01".

though the create method is part of the behavior definition. What was actually missing in the BDEF was the early numbering statement .

Code

CLASS lhc_travelsemantic DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.
    METHODS:
      get_global_authorizations FOR GLOBAL AUTHORIZATION
        IMPORTING
        REQUEST requested_authorizations FOR TravelSemantic
        RESULT result,
      create FOR MODIFY
        IMPORTING
          entities FOR CREATE  TravelSemantic,
      update FOR MODIFY
        IMPORTING
          entities FOR UPDATE  TravelSemantic,
      delete FOR MODIFY
        IMPORTING
          keys FOR DELETE  TravelSemantic,
      lock FOR LOCK
        IMPORTING
          keys FOR LOCK  TravelSemantic,
      read FOR READ
        IMPORTING
                  keys   FOR READ  TravelSemantic
        RESULT    result.

    METHODS earlyNumbering_Create  FOR NUMBERING
      IMPORTING entities FOR CREATE TravelSemantic.

ENDCLASS.

CLASS lhc_travelsemantic IMPLEMENTATION.
  METHOD get_global_authorizations.
  ENDMETHOD.
  METHOD create.
  ENDMETHOD.
  METHOD update.
  ENDMETHOD.
  METHOD delete.
  ENDMETHOD.
  METHOD lock.
  ENDMETHOD.
  METHOD read.
  ENDMETHOD.


  METHOD earlynumbering_create.

    DATA:
*      entity        TYPE STRUCTURE FOR CREATE /DMO/I_Travel_M,
      travel_id_max TYPE /dmo/travel_id.

    " Ensure Travel ID is not set yet (idempotent)- must be checked when BO is draft-enabled
    LOOP AT entities INTO DATA(entity) WHERE travelid IS NOT INITIAL.
      APPEND CORRESPONDING #( entity ) TO mapped-travelsemantic.
    ENDLOOP.

    DATA(entities_wo_travelid) = entities.
    DELETE entities_wo_travelid WHERE travelid IS NOT INITIAL.

    "Get max travelID
    SELECT SINGLE FROM ztravel_sem_01 FIELDS MAX( travel_id ) INTO @DATA(max_travelid).

    " select from draft table
    SELECT SINGLE FROM ztravelsem00d_01 FIELDS MAX( travelid ) INTO @DATA(max_travelid_draft).

    IF max_travelid < max_travelid_draft.
      max_travelid = max_travelid_draft.
    ELSEIF max_travelid = 0.
      max_travelid += 1.
    ENDIF.


    " Set Travel ID
    LOOP AT entities_wo_travelid INTO entity.
      max_travelid += 1.
      entity-travelid = max_travelid .

      APPEND VALUE #( %cid  = entity-%cid
                      "%tky is not available since we are in create
                      %key  = entity-%key
                      "needed because we are using draft
                      %is_draft = entity-%is_draft
                    ) TO mapped-travelsemantic.
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.
CLASS lcl_zi_travelsemantic_01 DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.
    METHODS:
      finalize REDEFINITION,
      check_before_save REDEFINITION,
      save REDEFINITION,
      cleanup REDEFINITION,
      cleanup_finalize REDEFINITION.
ENDCLASS.

CLASS lcl_zi_travelsemantic_01 IMPLEMENTATION.
  METHOD finalize.
  ENDMETHOD.
  METHOD check_before_save.
  ENDMETHOD.
  METHOD save.
  ENDMETHOD.
  METHOD cleanup.
  ENDMETHOD.
  METHOD cleanup_finalize.
  ENDMETHOD.
ENDCLASS.

 

 

 

Assigned Tags

      7 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Pavan Golesar
      Pavan Golesar

      Thanks Andre, Will give this a try soon.

      Warm Regards,

      Pavan Golesar

      Author's profile photo Matthias Wagenbreth
      Matthias Wagenbreth

      Hi Andre,

      Thanks for your blog. Can you please explain how to use early numberig to realize the requirement that the key values are not determined by the application but entered manually by the user in the UI?

      I am searching already a long time for a solution, but do not find one.

       

      Kind regards,
      Matthias

      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      If you follow this exercise the application that is initially generated will behave such that you can enter any key value for travel data.

      Only later in Exercise number 2 internal numbering is implemented.

      abap-platform-rap-workshops/rap1xx/rap100 at main · SAP-samples/abap-platform-rap-workshops (github.com)

       

      Kind regards,

      Andre

      Author's profile photo Dhananjay Hegde
      Dhananjay Hegde

      Hello Andre

       

      Thank you for the post. Very interesting.

      I tried Unmanaged Draft with Semantic Keys scenario with Late Numbering.  This works as well.  Tried this in BTP Trial today.  More details here in this comment on another post on this topic.

      Late Numbering might be particularly useful when numbers are drawn from number range.

      But, I noticed, in BTP Trial, EDIT does not work as of now.  POST request to call EDIT action fails. It fails to generate a draft.

      With ODATA V2, I get an error "No Data Found".

      With ODATA V4, I get an error "Unspecified provider error occurred. See Error Context and Call Stack".

      As far as I know, "EDIT" action for Unmanaged with Draft should work without needing any "additional implementation".

      Is this not fully supported yet on BTP Trial?

       

      Thank you,

      Dhananjay

      Author's profile photo Sandeep Sarikonda
      Sandeep Sarikonda

      Hello Dhananjay,

      you have to give implementation for read method to solve that 'No Data Found' error.

      Regards,

      Sandeep.

      Author's profile photo Shavneet Singh
      Shavneet Singh

      Hello Sandeep / Andre Fischer .

      I am also Stuck in this 'No data found error' when clicking on Edit button. My requirement is , Actual data is already coming from legacy system. there is no create or delete.

      Just few actions and Update of data , on Edit and Save.

      Sandeep Sarikonda : can you please give more details.

       

      Regards ,

      Shavneet

      Author's profile photo Kishore Gokara
      Kishore Gokara

      Hello Andre

      I have a requirement where I need to pull the data from different standard tables. On these records I should provide an option to update few fields. On saving this, we need to make a standard FM/BAPI call to update these values.

      This case doesn't have any base table so I cannot have have a field to mention for "total etag". What should be the recommended approach for draft handling in this case or is it possible to have a draft scenario here?

      My requirement is only to update. I don't need create and delete options.

      Thanks,

      Kishore.