Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Ramjee_korada
Active Contributor

Introduction :



In this blog post, we will see one of the common problems and possible solutions in the Unmanaged Draft scenario in ABAP RAP. As we know, the keys are automatically drawn for draft records. Imagine as a case, we have a legacy data model (EKKO, VBAK) whose keys are not GUID based.

Problem statement:




 

If we develop a RAP application based on it. Application will not let any user create another draft in the whole system. If it is based on GUID Keys, then we could have marked the key as a numbering field to draw automatic keys. In this case, the application already consumed default keys ( '     ' for CHAR, '00..0' for NUMC ) and returned the error below.



​Error analysis is indicated as below :
The application has indicated exception "CX_CSP_ACT_INTERNAL" as the reason for the
termination:
A duplicate primary key '' was detected when inserting data into persistence 'ZRKT_DT_EKKO'.
The exception is related to the previous exception "CX_SY_OPEN_SQL_DB" that occurred in
program "CL_CSP_SQL_ACCESS=============CP", in line 12 of include "CL_CSP_SQL_ACCESS=============CM003". The reason for this exception
was:
A data record is to be inserted even though a data record with the same primary key or same unique secondary key exists.

Technical representation of the business requirement:

  1. Unmanaged Implementation

  2. Draft handling

  3. Late numbering ( number can only be generated by BAPI during saving )

  4. Non-Guid key based legacy tables ( Ex: EKKO , VBAK )

  5. Number range to be consumed for actual documents but not for draft documents


​Solution option:


This looks to be a limitation in RAP as this question has not been answered for a year . I hope SAP will publish a perfect solution soon. Meanwhile we can apply the solution below to achieve the functionality with RAP -

Key points :

  1. ” Draft table does not need to have the same keys as base table but need to have same as base/interface view”. 

  2. Append additional structure with GUID field to the base table.


Below are the steps:


  1. Check the base table is Extensible.

  2. If yes, then append a structure with temp_guid field

  3. This would be a non-key field in the existing structure so that existing entries or the legacy data model is not impacted.

  4. ​Adjust base/interface and projection view with this new field as key so that it becomes key for your application data model.

  5. Adjust Behavior definition with new field with numbering

  6. Alter the draft table with this adjustment.

  7. Implement the methods for Create/Update/Delete/Read being unmanaged scenarios.

  8. On SAVE during Create, New key can be generated with a number range object and the developer has full control on persisting the actual records into existing tables.


​Implementation:


  1. Base table is extensible
    @EndUserText.label : 'Proc doc'
    @AbapCatalog.enhancement.category : #EXTENSIBLE_CHARACTER_NUMERIC
    @AbapCatalog.tableCategory : #TRANSPARENT
    @AbapCatalog.deliveryClass : #A
    @AbapCatalog.dataMaintenance : #RESTRICTED
    define table zrkt_ekko {
    key client : abap.clnt not null;
    key object_id : zrk_pur_con_id not null;
    description : zrk_description;
    buyer : zrk_buyer_id;
    supplier : zrk_sup_no;
    sup_con_id : zrk_sup_con_id;
    send_via : zrk_send_via_code;
    comp_code : zrk_company_code;
    stat_code : zrk_stat_code;
    fiscl_year : zrk_fiscal_year;
    valid_from : zrk_valid_from;
    valid_to : zrk_valid_to;
    created_by : abp_creation_user;
    created_at : abp_creation_tstmpl;
    last_changed_by : abp_locinst_lastchange_user;
    last_changed_at : abp_lastchange_tstmpl;
    locl_last_changed_at : abp_locinst_lastchange_tstmpl;

    }​


  2. Append the structure having temp_guid to base table
    @EndUserText.label : 'Strcuture include for ZRKT_EKKO'
    @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
    extend type zrkt_ekko with zzrkt_exten_incld {
    zztemp_guid : sysuuid_x16;

    }​


  3. This would be a non-key field in the existing structure so that existing entries or the legacy data model is not impacted.

  4. ​Adjust base/interface view with this new field as key so that it becomes key for your application data model.
    @AccessControl.authorizationCheck: #CHECK
    @EndUserText.label: '##GENERATED Test proc doc'
    define root view entity ZRKT_I_PROC_DOC
    as select from zrkt_proc_doc as ProcDoc
    {
    key zztemp_guid as TempGuid,
    object_id as ObjectID,
    description as Description,
    buyer as Buyer,
    supplier as Supplier,
    sup_con_id as SupConID,
    send_via as SendVia,
    comp_code as CompCode,
    stat_code as StatCode,
    fiscl_year as FisclYear,
    valid_from as ValidFrom,
    valid_to as ValidTo,
    @Semantics.user.createdBy: true
    created_by as CreatedBy,
    @Semantics.systemDateTime.createdAt: true
    created_at as CreatedAt,
    @Semantics.user.localInstanceLastChangedBy: true
    last_changed_by as LastChangedBy,
    @Semantics.systemDateTime.lastChangedAt: true
    last_changed_at as LastChangedAt,
    @Semantics.systemDateTime.localInstanceLastChangedAt: true
    locl_last_changed_at as LoclLastChangedAt

    }​

    @AccessControl.authorizationCheck: #CHECK
    @Metadata.allowExtensions: true
    @EndUserText.label: 'Projection View for ZRKT_I_EKKO'
    define root view entity ZRKT_C_EKKO
    as projection on ZRKT_I_EKKO
    {
    key TempGuid,
    objectid,
    description,
    buyer,
    supplier,
    supconid,
    sendvia,
    compcode,
    statcode,
    fisclyear,
    validfrom,
    validto,
    locllastchangedat

    }


  5. Adjust Behavior definition with new field with numbering
    unmanaged implementation in class ZRKT_BP_EKKO unique;
    strict;
    with draft;

    define behavior for ZRKT_I_EKKO alias Ekko
    //persistent table zrkt_ekko
    draft table ZRKT_DT_EKKO
    etag master LoclLastChangedAt
    lock master total etag LastChangedAt
    authorization master( global )

    {

    field ( readonly ) ObjectID;
    field( numbering : managed , readonly) TempGuid;

    field ( readonly ) CreatedAt,
    CreatedBy,
    LastChangedAt,
    LoclLastChangedAt,
    LastChangedBy;

    create;
    update;
    delete;

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

    }​


  6. Alter/recreate the draft table with this adjustment.
    @EndUserText.label : 'Draft table for entity ZRKT_I_EKKO'
    @AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
    @AbapCatalog.tableCategory : #TRANSPARENT
    @AbapCatalog.deliveryClass : #A
    @AbapCatalog.dataMaintenance : #RESTRICTED
    define table zrkt_dt_ekko {
    key mandt : mandt not null;
    key tempguid : sysuuid_x16 not null;
    objectid : zrk_pur_con_id;
    description : zrk_description;
    buyer : zrk_buyer_id;
    supplier : zrk_sup_no;
    supconid : zrk_sup_con_id;
    sendvia : zrk_send_via_code;
    compcode : zrk_company_code;
    statcode : zrk_stat_code;
    fisclyear : zrk_fiscal_year;
    validfrom : zrk_valid_from;
    validto : zrk_valid_to;
    createdby : abp_creation_user;
    createdat : abp_creation_tstmpl;
    lastchangedby : abp_locinst_lastchange_user;
    lastchangedat : abp_lastchange_tstmpl;
    locllastchangedat : abp_locinst_lastchange_tstmpl;
    "%admin" : include sych_bdl_draft_admin_inc;

    }​


  7. Implement the methods for Create/Update/Delete/Read being unmanaged scenarios.

  8. On SAVE during Create, New key can be generated with a number range object and the developer has full control on persisting the actual records into existing tables.
      METHOD create.

    DATA ls_pur_con TYPE zrkt_proc_doc.

    READ TABLE entities ASSIGNING FIELD-SYMBOL(<fs_entity>) INDEX 1.
    IF sy-subrc EQ 0.

    TRY.
    cl_numberrange_runtime=>number_get(
    EXPORTING
    * ignore_buffer =
    nr_range_nr = '01'
    object = 'ZRK_NR_PR'
    quantity = 1
    * subobject =
    * toyear =
    IMPORTING
    number = DATA(number_range_key)
    returncode = DATA(number_range_return_code)
    returned_quantity = DATA(number_range_returned_quantity)
    ).
    CATCH cx_number_ranges.
    "handle exception
    ENDTRY.
    DATA(max_id) = number_range_key - number_range_returned_quantity.

    ls_pur_con-client = sy-mandt.
    ls_pur_con-zztemp_guid = <fs_entity>-TempGuid.
    ls_pur_con-object_id = |PC{ max_id }| .
    ls_pur_con-description = <fs_entity>-Description .
    ls_pur_con-buyer = <fs_entity>-Buyer .
    ls_pur_con-supplier = <fs_entity>-Supplier .
    ls_pur_con-sup_con_id = <fs_entity>-SupConId .
    ls_pur_con-comp_code = <fs_entity>-CompCode .
    ls_pur_con-stat_code = <fs_entity>-StatCode .
    ls_pur_con-valid_from = <fs_entity>-Validfrom .
    ls_pur_con-valid_to = <fs_entity>-ValidTo .
    ls_pur_con-fiscl_year = <fs_entity>-FisclYear .
    ls_pur_con-created_at = <fs_entity>-CreatedAt .
    ls_pur_con-created_by = <fs_entity>-CreatedBy .

    INSERT zrkt_proc_doc FROM @ls_pur_con.

    ENDIF.

    ENDMETHOD.​


  9. Implement Behavior projection, Service definition and publish Service Binding

  10. Fiori application is ready for preview and test

    Fiori app preview:.

    Table entries in draft table:

    Table entries in Active table:




​Advantages:



  1. Almost out of the box implementation and no custom development behind the " Keep Draft" to see if it is a fresh draft ( no active ) or delta draft ( active entry exists ).

  2. No need to consume number range without user explicit SAVE . Otherwise, if we need to store draft records with original key field, we have to consume it.


Restrictions:



  1. Base table must be extensible to append an include with temp_guid. Ex: EKKO also extensible


Note:


​If you have already existing transactional data, then you have to plan for a cutover activity to fill this temp_guid ( simply generate ) in the base table to reflect into the Fiori application. ​

Conclusion:


​Application is ready for usage and we can see a fiori application with unmanaged draft scenario​ with the fact  " Draft table does not need to have the same keys as base table but need to have same as base/interface view".

Please share your views and comment if you have any other alternative proposals to achieve this.

Revision on 17.03.2022 : Added section "Technical representation of the business requirement".

Revision on 05.04.2022 : Added screen shots for final results.
31 Comments
Labels in this area