Technology Blog Posts 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: 
ivo_skolek
Participant
7,075

Restful ABAP Programming Model... I went through openSAP course, I bought SAP press books. But somehow RAP seemed a bit scary.  I am a developer who likes to understand what I am writing and why. Looking at examples of RAP, the number of keywords in behaviour definition and the implemenation classes with EML just seemed a bit overwhelming... Is it just me?

I would start with a bit of a context - I am originally ABAP developer who learnt UI5 and got heavily into fullstack development. I spent a lot of time building apps on ECC on HANA db. That means not yet S/4 (but close!), ABAP 7.5, for oData using mostly what I would call BOPF-less ABAP Programming Model for Fiori => SEGW with CDS views as referenced data source but not using BOPF cause at that version BOPF object can't be generated out of CDS. For implementing CRUD I would just add annotations below to my CDS exposed as referenced data source and then redefine methods for saving stuff in DPC_EXT class.

@ObjectModel.createEnabled: true

@ObjectModel.updateEnabled: true

@ObjectModel.deleteEnabled: true

With that and a few other pieces of ABAP when CDS wouldn't be enough or for things like changesets or stream processing I was able to built any complex applications efficiently fully utilizing the power of SADL and CDS while having full flexibility of ABAP when needed.

So this blog posts will be about achieving something similar in RAP in most simple and lean way I could find. Alright, let's start with my key recommendations. They might sound radical so please take it as a way to start with RAP, not as a best practice (though in my experience following approach can work very well for productive developments).

1) do unamanged behaviour - you will understand exactly what you are doing

2) do behaviour definition and implementation directly on ZC* (consumption) view - skip added complexity of behaviour projection layer, it's optional

3) trim behaviour definition to bare minimum that you clearly understand. Remove everything that you don't need or don't understand

4) don't use EML - everything can be done with traditional ABAP, EML is optional - understand first what you are doing with traditional ABAP, later add EML if suitable

Now here comes the example, let's assume simple model of 2 CDS views - for Purchase Order Header and Purchase Order Items. And app that allows displaying and creating purchase orders

 

define root view entity ZC_LeanRAP_PurchaseOrder as select from I_PurchaseOrder
composition [1..*] of ZC_LeanRAP_PurchOrdItem as _Items
{
    key PurchaseOrder,
    PurchasingGroup,
    PurchasingOrganization,
    Supplier,
    CompanyCode,
    DocumentCurrency,
    PaymentTerms,
    
    _Items
}
define view entity ZC_LeanRAP_PurchOrdItem as select from I_PurchaseOrderItem
association to parent ZC_LeanRAP_PurchaseOrder as _Header on $projection.PurchaseOrder = _Header.PurchaseOrder
{
    key PurchaseOrder,
    key PurchaseOrderItem,
    Plant,
    PurchaseOrderItemText,
    NetPriceAmount,
    OrderQuantity,
    DocumentCurrency,
    PurchaseOrderQuantityUnit,
    
    _Header
}

 

Let's continue with behaviour definition - following the recommendations nr.1 and 2 we generate unmanaged behaviour definition directly for ZC_LeanRAP_PurchaseOrder. (please note that unmanaged here stands for the CUD operations! querying data with GET requests will be still managed by SADL pulling from CDS views so no coding there). When you generate it, it will look like this:

 

unmanaged implementation in class zbp_c_leanrap_purchaseorder unique;
strict ( 2 );

define behavior for ZC_LeanRAP_PurchaseOrder //alias <alias_name>
//late numbering
lock master
authorization master ( instance )
//etag master <field_name>
{
  create;
  update;
  delete;
  association _Items { create; }
}

define behavior for ZC_LeanRAP_PurchOrdItem //alias <alias_name>
//late numbering
lock dependent by _Header
authorization dependent by _Header
//etag master <field_name>
{
  update;
  delete;
  field ( readonly ) PurchaseOrder;
  association _Header;
}

 

Now follow recommendation nr.3 and trim it... we want to create purchase order with items. Let's remove everything not directly related to that and we get to something like this:

 

unmanaged implementation in class zbp_c_leanrap_purchaseorder unique;

define behavior for ZC_LeanRAP_PurchaseOrder
{
  create;
  association _Items { create; }
}

define behavior for ZC_LeanRAP_PurchOrdItem

{
  create;
}

 

Finally let's get to behaviour implementation that follows recommendation nr.4 - no EML. In a nutshell, just 3 methods are implemented there - methods for create on header and item level will be called first and take data coming from frontend and save it into static attributes of transactional buffer class - lhc_buffer. And later method save will find it there in buffer and call BAPI for creating Purchase Order. As simple as that - no EML, no special RAP commands. Just search and look at all places where lhc_buffer is used. It's simplified example but actually it's implementing deep insert - header and items collected and saved with single BAPI call.

 

CLASS lhc_buffer DEFINITION.
  PUBLIC SECTION.

    CLASS-DATA: ss_bapi_po_header  TYPE bapimepoheader,
                ss_bapi_po_headerx TYPE bapimepoheaderx,
                st_bapi_po_item    TYPE STANDARD TABLE OF bapimepoitem,
                st_bapi_po_itemx   TYPE STANDARD TABLE OF bapimepoitemx.
ENDCLASS.


CLASS lhc_ZC_LeanRAP_PurchaseOrder DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS create FOR MODIFY
      IMPORTING entities FOR CREATE ZC_LeanRAP_PurchaseOrder.

    METHODS read FOR READ
      IMPORTING keys FOR READ ZC_LeanRAP_PurchaseOrder RESULT result.

    METHODS rba_Items FOR READ
      IMPORTING keys_rba FOR READ ZC_LeanRAP_PurchaseOrder\_Items FULL result_requested RESULT result LINK association_links.

    METHODS cba_Items FOR MODIFY
      IMPORTING entities_cba FOR CREATE ZC_LeanRAP_PurchaseOrder\_Items.

ENDCLASS.

CLASS lhc_ZC_LeanRAP_PurchaseOrder IMPLEMENTATION.

  METHOD create.

    LOOP AT entities ASSIGNING FIELD-SYMBOL(<ls_header_create>).

      lhc_buffer=>ss_bapi_po_header = VALUE #( doc_type     = 'ZPO'
                                               purch_org    = <ls_header_create>-PurchasingOrganization
                                               pur_group    = <ls_header_create>-PurchasingGroup
                                               vendor       = <ls_header_create>-Supplier
                                               comp_code    = <ls_header_create>-CompanyCode
                                               currency     = <ls_header_create>-DocumentCurrency
                                               pmnttrms     = <ls_header_create>-PaymentTerms ).

      lhc_buffer=>ss_bapi_po_headerx = VALUE #( doc_type    = abap_true
                                                purch_org   = abap_true
                                                pur_group   = abap_true
                                                vendor      = abap_true
                                                comp_code   = abap_true
                                                currency    = abap_true
                                                pmnttrms    = abap_true ).

      EXIT. "we expect always 1 header to come
    ENDLOOP.

  ENDMETHOD.

  METHOD read.
  ENDMETHOD.

  METHOD rba_Items.
  ENDMETHOD.

  METHOD cba_Items.
  ENDMETHOD.

ENDCLASS.

CLASS lhc_ZC_LeanRAP_PurchOrdItem DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS read FOR READ
      IMPORTING keys FOR READ ZC_LeanRAP_PurchOrdItem RESULT result.

    METHODS rba_Header FOR READ
      IMPORTING keys_rba FOR READ ZC_LeanRAP_PurchOrdItem\_Header FULL result_requested RESULT result LINK association_links.

    METHODS create FOR MODIFY
      IMPORTING entities FOR CREATE ZC_LeanRAP_PurchOrdItem.

ENDCLASS.

CLASS lhc_ZC_LeanRAP_PurchOrdItem IMPLEMENTATION.

  METHOD read.
  ENDMETHOD.

  METHOD rba_Header.
  ENDMETHOD.

  METHOD create.
    LOOP AT entities ASSIGNING FIELD-SYMBOL(<ls_item_create>).

      APPEND VALUE bapimepoitem( po_item    = sy-tabix
                                item_cat    =  '0'
                                acctasscat  = 'C'
                                plant       = <ls_item_create>-Plant
                                short_text  = <ls_item_create>-PurchaseOrderItemText
                                quantity    = <ls_item_create>-OrderQuantity
                                po_unit     = <ls_item_create>-PurchaseOrderQuantityUnit
                                net_price   = <ls_item_create>-NetPriceAmount
                                matl_group  = 'TEST' )
                   TO lhc_buffer=>st_bapi_po_item.

      APPEND VALUE bapimepoitem( po_item    = sy-tabix
                               item_cat     =  abap_true
                               acctasscat   = abap_true
                               plant        = abap_true
                               short_text   = abap_true
                               quantity     = abap_true
                               po_unit      = abap_true
                               net_price    = abap_true
                               matl_group   = 'TEST' )
                  TO lhc_buffer=>st_bapi_po_itemx.

    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

CLASS lsc_ZC_LEANRAP_PURCHASEORDER 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_ZC_LEANRAP_PURCHASEORDER IMPLEMENTATION.

  METHOD finalize.
  ENDMETHOD.

  METHOD check_before_save.
  ENDMETHOD.

  METHOD save.

    DATA ls_new_po TYPE bapimepoheader.
    DATA lt_return TYPE bapiret2_t.

    CALL FUNCTION 'BAPI_PO_CREATE1'
      EXPORTING
        poheader  = lhc_buffer=>ss_bapi_po_header
        poheaderx = lhc_buffer=>ss_bapi_po_headerx
        testrun   = abap_false
      IMPORTING
        expheader = ls_new_po
      TABLES
        return    = lt_return
        poitem    = lhc_buffer=>st_bapi_po_item
        poitemx   = lhc_buffer=>st_bapi_po_itemx.

  ENDMETHOD.

  METHOD cleanup.
  ENDMETHOD.

  METHOD cleanup_finalize.
  ENDMETHOD.

ENDCLASS.

 

In the next part(s) of this blog series we will take a look at adding more features while sticking to "lean RAP" recommendations:

- implementing error handling and late numbering

- updating both header and item fields for existing Purchase Orders (changesets)

- adding new items to existing Purchase Order with "create by association" operation

- implementing concurrency handling with etags

Hope this blog series will help a few more fellow developers to transition from SEGW and DPC_EXT classes to RAP ;-).

 

7 Comments
Labels in this area