Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
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.

 

 

 
8 Comments