Skip to Content
Technical Articles
Author's profile photo Umut Yazici

ABAP Restful Application Programming Model with Legacy BAPI

Summary

The research I have done for three months has shown that there is not much content produced on this subject(RAP with BAPIs). Because of this reason I decided to prepare a BAPI-based development example using RAP model over a real scenario.

While preparing this article, I have benefited greatly from this tutorial of Julie Plummer, which is the best resource I could access about unmanaged rap scenario. Thanks Julie.

To summarize, the restful programming logic works with a focus on Core Data Services. This CDS-focused structure can be configured with standard functions (such as BAPIs) that we have used in the legacy logics(brownfield scenario).

Unmanaged Or Managed?
While Sap was constructing the new RAP model, it made a distinction between managed and unmanaged scenarios. Managed means completely new and Greenfield implementation(no legacy logic).
If you have to use existing BAPI or function, SAP is recommended to turn to the unmanaged scenario in RAP model.

I suggest you look at the references below to get a good understanding of how the new RAP model works . In addition check out this resource to understand Save Sequence Runtime in ABAP Restful Application Programming Model.

If you don’t know anything about the ABAP Restful Application Programming Model . This openSap course is good starting point.

What will you learn?

In this example I will create “Unmanaged Restful Application Programming Model Scenario” with Legacy Appropriation Requests BAPIs and structures.

You’ll learn

1- How to create custom cds entity and implement a query provider class include BAPI_APPREQUEST_GETDETAIL’

2- How to create behavior definition and implementation include BAPI_APPREQUEST_CREATE function.

3- How to create service definition and service binding.

 

Legacy Codes Summary

  • GET Appropriation Request Data through Legacy BAPI : ‘BAPI_APPREQUEST_GETDETAIL’
  • CREATE Appropriation Request through BAPI: ‘BAPI_APPREQUEST_CREATE’

RAP Codes Summary

Get Appropriation Requests

  • Create a Custom CDS Entity: ZHVL_IC_APPROPREQ
  • Implement a query provider class: ZCL_APPROP_VIA_RFC->interface>if_rap_query_provider

Create Appropriation Request

  • Create Behavior Definition for ZHVL_IC_APPROPREQ Custom CDS Entity
  • Create Control Structure Name: ZHVL_CS_APPROP
  • In Behavior Definition->create mapping for ZHVL_IC_APPROPREQ using control structure ZHVL_CS_APPROP
  • Create Behavior Implementation->unmanaged implementation in class ZBP_HVL_IC_APPROPREQ

Create ODATA Service

  • Create Service Definition
  • Create Service Binding

 

1- Create Custom CDS Entity

 

 

@EndUserText.label: 'Approp Request Custom Cds Entity'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_APPROP_VIA_RFC'

@UI: {
headerInfo: {
typeName: 'Approp Request',
typeNamePlural: 'Approp Requests'
}
}


define root custom entity ZHVL_IC_APPROPREQ

{


      @UI.facet        : [
             {
               id      :       'AppropRequest',
               purpose :  #STANDARD,
               type    :     #IDENTIFICATION_REFERENCE,
               label   :    'Approp Request',
               position: 10 }
           ]


      @UI              : {
      lineItem         : [{position: 10, importance: #HIGH}],
      identification   : [{position: 10}],
      selectionField   : [{position: 10}]
      }
  key externalnumber   : abap.char( 24 );
      //MASTER DATA
      @UI              : {
      lineItem         : [{position: 20, importance: #HIGH}],
      identification   : [{position: 20}],
      selectionField   : [{position: 20}]
      }
      REQ_TXT          : ima_txt50;
      @UI              : {
      lineItem         : [{position: 30, importance: #HIGH}],
      identification   : [{position: 30}],
      selectionField   : [{position: 30}]
      }
      RSP_COST_CENTER  : ima_vkostl;
      @UI              : {
      lineItem         : [{position: 40, importance: #HIGH}],
      identification   : [{position: 40}],
      selectionField   : [{position: 40}]
      }
      APPR_DATE        : ima_gdatu;
      @UI              : {
      lineItem         : [{position: 50, importance: #HIGH}],
      identification   : [{position: 50}],
      selectionField   : [{position: 50}]
      }
      ORIG_APPR_YEAR   : ima_gjahr;
      @UI              : {
      lineItem         : [{position: 60, importance: #HIGH}],
      identification   : [{position: 60}],
      selectionField   : [{position: 60}]
      }

      //Variant Data
      DESCRIPTION      : ima_txv50;
      @UI              : {
      lineItem         : [{position: 70, importance: #HIGH}],
      identification   : [{position: 70}],
      selectionField   : [{position: 70}]
      }
      COMPLETION_DATE  : ima_fdatu;
      START_UP_DATE    : ima_idatu;

      //PLAN TOTAL
      OVERHEAD_COSTS   : bapicurr_d;

      // ORG_UNITS
      REQ_COST_CENTER  : ima_akostl;
      PERCENTAGE       : ima_aproz;

      //PARTNER DATA
      PARTNER_FUNCTION : abap.char( 2 );
      PARTNER          : i_parnr;

      //PLAN_YEAR
      FISCAL_YEAR      : gjahr;


      INV_PROG         : im_prnam;
      APPR_YEAR        : im_gnjhr;
      PROGRAM_POS      : abap.char( 24 );
      PERCENT_PROG_POS : im_prozu;

}

 

 

2- Implement a query provider class to get the data, using a BAPI (Business Application Programming Interface)

 

 

 

CLASS zcl_approp_via_rfc DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    INTERFACES if_rap_query_provider .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_approp_via_rfc IMPLEMENTATION.
  METHOD if_rap_query_provider~select.
    TYPES: BEGIN OF ty_approp,
             externalnumber   TYPE ima_posid,
             req_txt          TYPE ima_txt50,
             rsp_cost_center  TYPE ima_vkostl,
             appr_date        TYPE ima_gdatu,
             orig_appr_year   TYPE ima_gjahr,
             description      TYPE ima_txv50,
             completion_date  TYPE ima_fdatu,
             start_up_date    TYPE ima_idatu,
             overhead_costs   TYPE bapicurr_d,
             req_cost_center  TYPE ima_akostl,
             percentage       TYPE ima_aproz,
             partner_function TYPE parvw,
             partner          TYPE i_parnr,
             fiscal_year      TYPE gjahr,
             inv_prog         TYPE im_prnam,
             appr_year        TYPE im_gnjhr,
             program_pos      TYPE im_posid,
             percent_prog_pos TYPE im_prozu,
           END OF ty_approp.
    DATA: ls_approp_req TYPE ty_approp.
    DATA: lt_approp_req TYPE STANDARD TABLE OF ty_approp.
    DATA: ls_master_data TYPE bapiappreqmaster,
          ls_controlling TYPE bapi_appreq_id-cntrl_area,
          lt_org         TYPE TABLE OF bapiappreqorgunit,
          lt_partner     TYPE TABLE OF bapiappreqpartnerout,
          lt_pos         TYPE TABLE OF bapiappreqexpprogassgn,
          lt_variant     TYPE TABLE OF bapiappreqvarntmulti,
          lt_plantot     TYPE TABLE OF bapiappreqplantotalmulti,
          lt_planyear    TYPE TABLE OF bapiappreqplanyearmulti,
          lt_return      TYPE TABLE OF bapiret2.
    "Set RFC destination
    TRY.
        "data(lo_rfc_dest) = cl_rfc_destination_provider=>proxy_type_onpremise(
        "i_name = ''
        ").
        "DATA(lv_rfc_dest_name) = lo_rfc_dest->get_destination_name(  ).
        "Check if data is requested
        IF io_request->is_data_requested(  ).
*          DATA lv_maxrows TYPE int4.
*          DATA(lv_skip) = io_request->get_paging( )->get_offset(  ).
*          DATA(lv_top) = io_request->get_paging( )->get_page_size(  ).
*          lv_maxrows = lv_skip + lv_top.
          io_request->get_paging( )->get_offset(  ).
          io_request->get_paging( )->get_page_size(  ).
          "Call BAPI
          CALL FUNCTION 'BAPI_APPREQUEST_GETDETAIL'
            EXPORTING
              externalnumber    = '2.018E11-016288'
*             language          =
*             language_iso      =
            IMPORTING
              master_data       = ls_master_data
*             user_fields       =
              "controlling_area  = ls_controlling
            TABLES
              org_units         = lt_org
*             division          =
*             material_group    =
*             invest_reason     =
*             environmnt_invest =
*             assets_equis      =
*             order             =
*             wbs_element       =
              partner           = lt_partner
              assignment_to_pos = lt_pos
*             assignment_to_budg_categ =
              variant           = lt_variant
*             variant_to_version       =
*             assigned_apprequests     =
              plan_total        = lt_plantot
              plan_year         = lt_planyear
*             plan_total_obj    =
*             plan_year_obj     =
              return            = lt_return.
        ENDIF.
        ls_approp_req-externalnumber = '2.018E11-016288'.
        ls_approp_req-req_txt = ls_master_data-req_txt.
        ls_approp_req-rsp_cost_center = ls_master_data-rsp_cost_center.
        ls_approp_req-appr_date = ls_master_data-appr_date.
        ls_approp_req-orig_appr_year = ls_master_data-orig_appr_year.

        READ TABLE lt_org INTO DATA(ls_org) INDEX 1.
        ls_approp_req-req_cost_center = ls_org-req_cost_center.
        ls_approp_req-percentage = ls_org-percentage.

        READ TABLE lt_partner INTO DATA(ls_partner) INDEX 1.
        ls_approp_req-partner_function = ls_partner-partner_function.
        ls_approp_req-partner = ls_partner-partner.

        READ TABLE lt_pos INTO DATA(ls_pos) INDEX 1.
        ls_approp_req-inv_prog = ls_pos-inv_prog.
        ls_approp_req-appr_year = ls_pos-appr_year.
        ls_approp_req-percent_prog_pos = ls_pos-percent_prog_pos.
        ls_approp_req-program_pos = ls_pos-program_pos.

        READ TABLE lt_variant INTO DATA(ls_variant) INDEX 1.
        ls_approp_req-req_cost_center = ls_variant-description.
        ls_approp_req-start_up_date = ls_variant-start_up_date.

        READ TABLE lt_plantot INTO DATA(ls_plantot) INDEX 1.
        ls_approp_req-overhead_costs = ls_plantot-overhead_costs.

        READ TABLE lt_planyear INTO DATA(ls_planyear) INDEX 1.
        ls_approp_req-fiscal_year = ls_planyear-fiscal_year.

        APPEND ls_approp_req TO lt_approp_req.
        CLEAR ls_approp_req.
        "Set total no. of records
        io_response->set_total_number_of_records( lines( lt_approp_req ) ).
        "Output data
        io_response->set_data( lt_approp_req ).
      CATCH  cx_rfc_dest_provider_error INTO DATA(lx_dest).
    ENDTRY.

  ENDMETHOD.

ENDCLASS.

 

3- Create Behavior Definition for ZHVL_IC_APPROPREQ

 

 

unmanaged implementation in class zbp_hvl_ic_appropreq unique;

define behavior for ZHVL_IC_APPROPREQ alias approp
//late numbering
//lock master
//etag master <field_name>


{
  create;
  update;
  delete;


  mapping for ZHVL_IC_APPROPREQ control zhvl_cs_approp

  {
    externalnumber = externalnumber;
    req_txt = req_txt;
    rsp_cost_center = rsp_cost_center;
    appr_date = appr_date;
    orig_appr_year = orig_appr_year;
    description = description;
    completion_date = completion_date;
    start_up_date = start_up_date;
    overhead_costs = overhead_costs;
    req_cost_center = req_cost_center;
    percentage = percentage;
    partner_function = partner_function;
    partner = partner;
    fiscal_year = fiscal_year;
    inv_prog = inv_prog;
    appr_year = appr_year;
    program_pos = program_pos;
    percent_prog_pos = percent_prog_pos;
  }



}

3.1- Create control structure: zhvl_cs_approp

@EndUserText.label : 'Control Structure For Approp Req'
@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE
define structure zhvl_cs_approp {
  externalnumber   : xsdboolean;
  req_txt          : xsdboolean;
  rsp_cost_center  : xsdboolean;
  appr_date        : xsdboolean;
  orig_appr_year   : xsdboolean;
  description      : xsdboolean;
  completion_date  : xsdboolean;
  start_up_date    : xsdboolean;
  overhead_costs   : xsdboolean;
  req_cost_center  : xsdboolean;
  percentage       : xsdboolean;
  partner_function : xsdboolean;
  partner          : xsdboolean;
  fiscal_year      : xsdboolean;
  inv_prog         : xsdboolean;
  appr_year        : xsdboolean;
  program_pos      : xsdboolean;
  percent_prog_pos : xsdboolean;

}

 

4- Create a custom RFC function module and call legacy BAPI in this function module.**IMPORTANT

Important Note: 

RAP framework does not allow Commit/Rollback statements, as the framework itself internally takes care for committing/roll backing entities. If you try calling BAPI directly in behavior implementation class methods. You’ll get a dump.Because of this reason,We will call this RFC enabled function with destination none in behavior implementation class.(this may not be the best method. But I couldn’t find a better way,If anyone can find a better way, I would appreciate it if they write in the comments section.)

 

FUNCTION zhvl_approp_req
  IMPORTING
    VALUE(legacy_entity_in) TYPE zhvl_ic_appropreq
  EXPORTING
    VALUE(gv_ext) TYPE bapi_appreq_id-appreq
    VALUE(gv_appr) TYPE bapi_appreq_id-appreqvrnt.

 DATA messages   TYPE bapiret2.


    DATA : ls_masterdata TYPE bapiappreqmaster.
    DATA: lt_org      TYPE TABLE OF bapiappreqorgunit,
          lt_partner  TYPE TABLE OF bapiappreqpartner,
          lt_pos      TYPE TABLE OF bapiappreqexpprogassgn,
          lt_variant  TYPE TABLE OF bapiappreqvarntmulti,
          lt_plantot  TYPE TABLE OF bapiappreqplantotalmulti,
          lt_planyear TYPE TABLE OF bapiappreqplanyear,
          lt_return   TYPE TABLE OF bapiret2.

    DATA: ls_org      TYPE  bapiappreqorgunit,
          ls_partner  TYPE  bapiappreqpartner,
          ls_pos      TYPE  bapiappreqexpprogassgn,
          ls_variant  TYPE  bapiappreqvarnt,
          ls_plantot  TYPE  bapiappreqplantotal,
          ls_planyear TYPE  bapiappreqplanyear,
          ls_return   TYPE  bapiret2.


      ls_masterdata-req_txt = legacy_entity_in-req_txt.
      ls_masterdata-rsp_cost_center = legacy_entity_in-rsp_cost_center.
      ls_masterdata-appr_date  = legacy_entity_in-appr_date.
      ls_masterdata-orig_appr_year = legacy_entity_in-orig_appr_year.


      ls_variant-description = legacy_entity_in-description.
      ls_variant-completion_date = legacy_entity_in-completion_date.
      ls_variant-start_up_date = legacy_entity_in-start_up_date.

      ls_plantot-overhead_costs = legacy_entity_in-overhead_costs.

      ls_org-req_cost_center = legacy_entity_in-req_cost_center.
      ls_org-percentage = legacy_entity_in-percentage.
      APPEND ls_org TO lt_org.


*      ls_partner-partner_function = legacy_entity_in-partner_function.
*      ls_partner-partner = legacy_entity_in-partner.
*      APPEND ls_partner TO lt_partner.

      ls_partner-partner_function = 'I1'.
      ls_partner-partner = 'user1'.
      APPEND ls_partner TO lt_partner.
      ls_partner-partner_function = 'I2'.
      ls_partner-partner = 'user1'.
      APPEND ls_partner TO lt_partner.
      ls_partner-partner_function = 'I3'.
      ls_partner-partner = 'user2'.
      APPEND ls_partner TO lt_partner.
      ls_partner-partner_function = 'I4'.
      ls_partner-partner = 'user2'.
      APPEND ls_partner TO lt_partner.
      ls_partner-partner_function = 'I5'.
      ls_partner-partner = 'user2'.
      APPEND ls_partner TO lt_partner.
      ls_partner-partner_function = 'I6'.
      ls_partner-partner = 'user3'.
      APPEND ls_partner TO lt_partner.

      ls_planyear-fiscal_year = legacy_entity_in-fiscal_year.
      ls_planyear-overhead_costs = legacy_entity_in-overhead_costs.
      APPEND ls_planyear TO lt_planyear.


      ls_pos-inv_prog = legacy_entity_in-inv_prog.
      ls_pos-appr_year = legacy_entity_in-appr_year.
      ls_pos-program_pos = legacy_entity_in-program_pos.
      ls_pos-percent_prog_pos = legacy_entity_in-percent_prog_pos.
      APPEND ls_pos TO lt_pos.


    CALL FUNCTION 'BAPI_APPREQUEST_CREATE'
      EXPORTING
        apprequest_type                = '10'
        appropriationrequest_in        = legacy_entity_in-externalnumber
        appropriationrequestvariant_in = ''
        controlling_area               = 'A000'
        master_data                    = ls_masterdata
        "user_fields                    = ls_user
        variant                        = ls_variant
        plan_total                     = ls_plantot
        test_run                       = ''
      IMPORTING
        externalnumber                 = gv_ext
        appropriationrequestvariantout = gv_appr
      TABLES
        org_units                      = lt_org
        "division                       = lt_division
        "material_grp                   = lt_matgroup
        "investment_reason              = lt_investrea
        "environmnt_invest              = lt_environve
        "orders                         = lt_order
        "wbs_element                    = lt_wbselement
        "variant_to_version             = lt_variant
        partner                        = lt_partner
        assignment_to_pos              = lt_pos
        plan_year                      = lt_planyear
        return                         = lt_return.


    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.

ENDFUNCTION.

 

5- Create Behavior Implementation: class ZBP_HVL_IC_APPROPREQ.

CLASS lhc_ZHVL_IC_APPROPREQ DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS create FOR MODIFY
      IMPORTING entities FOR CREATE approp.

    METHODS delete FOR MODIFY
      IMPORTING keys FOR DELETE approp.

    METHODS update FOR MODIFY
      IMPORTING entities FOR UPDATE approp.

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

ENDCLASS.

CLASS lhc_ZHVL_IC_APPROPREQ IMPLEMENTATION.

  METHOD create.

    DATA legacy_entity_in  TYPE zhvl_ic_appropreq.
    DATA: gv_ext  TYPE bapi_appreq_id-appreq,
          gv_appr TYPE bapi_appreq_id-appreqvrnt.

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

      legacy_entity_in = CORRESPONDING #( <entity> MAPPING FROM ENTITY USING CONTROL ).

    CALL FUNCTION 'ZHVL_APPROP_REQ' DESTINATION 'NONE'
      EXPORTING
        legacy_entity_in = legacy_entity_in
      IMPORTING
        gv_ext           = gv_ext
        gv_appr          = gv_appr.
      
    ENDLOOP.

  ENDMETHOD.

  METHOD delete.
  ENDMETHOD.

  METHOD update.
  ENDMETHOD.

  METHOD read.
  ENDMETHOD.

ENDCLASS.

CLASS lsc_ZHVL_IC_APPROPREQ DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.

    METHODS check_before_save REDEFINITION.

    METHODS finalize          REDEFINITION.

    METHODS save REDEFINITION.


ENDCLASS.

CLASS lsc_ZHVL_IC_APPROPREQ IMPLEMENTATION.

  METHOD check_before_save.
  ENDMETHOD.

  METHOD finalize.
  ENDMETHOD.

  METHOD save.

ENDMETHOD.
ENDCLASS.

 

6- Create Service Definition

 

 

 

7- Create Service Binding and Publish ODATA Service

 

 

8- Preview your App

8 .1- GET Appropriation Request Data through Legacy BAPI : ‘BAPI_APPREQUEST_GETDETAIL’ using custom CDS entity.

 

 

8.2 CREATE Appropriation Request through BAPI: ‘BAPI_APPREQUEST_CREATE’ using behavior implementation class.

 

 

 

Important Note:

The codes in this example are purely for illustrative purposes. No performance testing has been done. No development has been made regarding the return parameters. May not be suitable for production system.

If you think there is a mistake in this method, please contribute in the comment section.

Please submit questions in the respective Q&A area in SAP Community.

References

1- OpenSap Course-Building Apps with Restful Application Programming Model

2- Sap Documents-Tutorials

3- Using Custom Cds Entity For Data Modeling

Assigned Tags

      23 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo wagner castro
      wagner castro

      Muito Bom...

      Author's profile photo Paul Hardy
      Paul Hardy

      As I understand it the handler class in the behavior definition is designed to update data in memory (the so called transactional buffer)

      The saver class is designed to update the database. That would be why the framework does not like you doing a COMMIT WORK in the class designed to update memory only.

      If you look at the SAP tutorials for the unmanaged scenario they have function modules which update internal tables in the methods of the handler class, and call a function module which does the database update from those internal tables in the saver class. That is just like a traditional DYNPRO really. The user changes things on the screen which updates structures and internal tables, and the the user presses SAVE and the program updates the database.

      Cheersy Cheers

      Paul

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Hi Paul , thanks for your comment. I understand theory behind the rap framework but I can’t use this methodology with BAPI create scenario. Sap Tutorials have simple db update in save method, this example is not enough for BAPI usage.

      According to my example How can we use best practice you said to creation scenario with only BAPI call.

      Please describe us and share your knowledge with coding example? Thank you very much!🙏

      Maybe Julie Plummer help us, I learned rap unmanaged scenario through her examples and try to modify for my requirements.

      I saw many questions about BAPI usage in RAP Framework but these questions havent any answers yet in SAP community, I wrote this blog post because of this reason and I want to get ideas from experts like you and start a discussion.

      Cheers🥃

      Author's profile photo Paul Hardy
      Paul Hardy

      I am in the midst of writing the fourth edition of my A2TF book and so have been playing around with the RAP a lot lately, both managed and unmanaged. The unmanaged will be heavily used due to the vast amount of existing code people will not want to throw away.

      The RAP takes a really big mindset change in that instead of a series of related tables e.g. VBAK / VBAP / VBEP / VBKD etc. you instead have associated entities with (in this example) VBAK being the root entity which has associations to the other entities (order items, schedule lines, sales data).

      The BOPF had a similar concept and that also had separate methods for updating the memory and updating the database.

      Now let us say your example is something like which only has a header level i.e. only stored in one table. That's not very realistic but lets just say. Let us call this entity a "widget" and you had a BAPI_CREATE_WIDGET.

      In the UI you have a list of widgets and you press the CREATE button and enter all the details and then press SAVE. The RAP mechanism would call the CREATE method then the SAVE method, one after the other.

      So at first glance you would say to yourself - why should I fill an internal table or something in the CREATE method and then have the database update (e.g. call to a BAPI) in the SAVE method using that internal tale? Why bother splitting it up if the two methods are called directly after each other during SAVE?

      In that example things appear to make no sense. However as I said in real life you usually have a complex hierarchy of entities. You will note in the RAP behavior class that even if there are multiple entities (hence multiple local behavior classes) there is only one saver class.

      The customer phones up and says there requirements have changed. This means you open an existing order, add one item, change an existing item, and delete one existing item. Then the customer phones up just before you are going to press SAVE and says they have changed their mind again, keep everything the way it was so you press CANCEL instead of SAVE.

      In that scenario you would not want the database to be updated as you add an item or delete an item as you have not pressed SAVE yet. Hence the logical unit of work (LUW) concept where either all related tables are updated together or none are.

      So in the RAP as you perform a CUD operation on an item the CUD method in the behavior class for that item just updates the memory. Only when SAVE is pressed does the SAVE method in the saver class get called and the database is updated.

      As I said it really isn't that different to a DYNPRO program. There if I only had a header I would fill a structure in memory as the user enters the detail and then call the BAPI upon save using the structure in memory. If later the data model changed and their were items I would store the items in an internal table that got changed during the transaction, and then change the SAVE method so that the call to the BAPI now had an item table passed in as well. Thus - if traditional programs have a clear separation between changing data in memory and changing data in the database why in the world should the RAP not be the same?

      Cheersy Cheers

      Paul

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Thanks for your detailed explanation Paul.

      Author's profile photo Stephan Boecker
      Stephan Boecker

      Thanks Umut for highlighting the restrictions in RAP with respect to BAPI usage.  I just worked through a similar example for PM Work orders with the same outcome as the one you have described. In short:

      • calling a BAPI in the behavior implementation 'for modify' won't work; but this would be the  place you want to use it since you can still provide user feedback for the checks done by the BAPI and also wait for the commit by the framework that will persist your data (since you pressed save in the UI)
      • calling a BAPI in the save method might work, but you are already beyond the point of no return, and a dump is not a valid user feedback

      It looks also to me that the definition of an LUW for RAP where a memory image is built-up incrementally and then saved to DB like the flight demo scenario does not fit the classical LUW used in SAP legacy code where a memory image is created after validations and registered for the update process in one step - or even committed in the same step. Ultimately I ran the scenario in a separate LUW like you did, but that is a workaround around a framework, not the usage of one. But I also think that ultimately unmanaged RAP will support BAPIs by easing the restrictions on callable statements because most people will associate 'legacy code' with 'SAP legacy code'

      thanks Stephan

       

      Author's profile photo Wouter Peeters
      Wouter Peeters

      As mentioned in the comments by Paul Hardy , we expect the unmanaged will be commonly used. I wonder what is the added value of going with the RAP Unmanaged approach in comparison to the alternative that we're used to - being implementing code based implementation DPC_EXT.
      Edit: just to state, for Read we always have CDS as priority 1, but CUD was always code based. 

      Before going into detail, I'd like to refer to a blogpost of Andre Fischer that discusses quite a lot on OData service development options:  https://blogs.sap.com/2017/12/12/odata-service-development-options/

      From what I can conclude from the blogpost, reduced TCO for OData V4 enablement is the main driver to use RAP. But in comparison to code based implementation, this looks like additional overhead/complexity of the framework. I view it a bit as BOPF, I always opted for a clean code, code based implementation instead of BOPF to avoid complexity. Then again, I have no real life experience with BOPF nor RAP, can anyone share their throughts?

      Are there any other added values for using RAP? What would the main drivers and added value be for OData V4? When upgrading, would there be any reason to upgrade your Code based and OData V2 to V4?

      Author's profile photo Martin Carl
      Martin Carl

      Hi, I only have 1909 onPrem for developing. There i have following problems:

                CALL FUNCTION 'BAPI_APPREQUEST_GETDETAIL'
                  EXPORTING
                    externalnumber    = '2.018E11-016288'

      will cause an error in my case: wrong input length, because there is a point and the mask in the bapi is catching this. '-016288' for example works for me.

      The second problem is:

          CALL FUNCTION 'ZHVL_APPROP_REQ' DESTINATION 'NONE'

      causes a 'call_function_not_remote' short dump. Who can help? I dont find a solution yet. I've copied the example 1 to 1 but the names of the objects.Is the example only for 2020 or should it run on 1909? Is the example only for Cloud? Who can help please? Whats wrong? I shall test, if we can call BAPIs in RAP on 1909 onPrem, so i need an explanation.

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Hi Martin,

      1- I tried this example on S4Hana Onpremise 2020. I think that “custom cds entity” feature work only 2020 onpremise.

      2- you must create ZHVL_APPROP _REQ function with “Remote Function Module” feature in se37.

      Author's profile photo Martin Carl
      Martin Carl

      Hi, Umut. Thank you. It works. but now I have a problem with my own example. The Fiori preview cache does not work properly. When I do changes in the CDS-Annotations after creating the Service, the preview show these changes for the first time. But after this the preview does not recognize any other changes.  I must create a new CDS with these changes and build a Service on thes CDS. Then it works for a while.Do you know the problem?

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Hi Martin,

      1- if you redefine the annotations again in Annotation Modeler in WebIDE, try clearing browser cache.

      2- if your annotation coming from backend, your changes automatically display in your app.

      Maybe you will try to go /IWFND/MAINT_SERVICE and go to Service Implementation and click on Cleanup Cache.

       

       

      Author's profile photo Martin Carl
      Martin Carl

      Hi, thank you for your response. but the problem is the Fiori preview in eclipse. I cleaned the browser cache and all the backend caches. But when i use the preview in eclipse, the changes will not dispaly automatically. I must create a new cds-entity with changes in eclipse, a new service definition and a new servie binding. Then the fiori preview in eclipse shows it correctly. I cleaned also the cache in /IWFND/MAINT_SERVICE without success. I also deleted eclipse and reinstalled without success. Here the objects:

       

       

      Author's profile photo Gene Madikrama
      Gene Madikrama

      Hi Umut,

      Thank you for this blog. We are creating an app using the ABAP RAP Method. We are also using an ABAP Class for this (Unmanaged app)

      We have two questions regarding your blog:

      1. We are wondering how to receive and show all the partners in the listreport (Fiori Elements)?
      2. Can you also tell us how to show header/items on the next page (Object View)? So how can we use a [1..*] in this CDS View template?

      // Kind Regards,

      Gene

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Hi Gene,

      You can check the answer given by  Andre Fischer in the post below.

      https://answers.sap.com/questions/12859717/abap-in-scp-custom-entity-cds-views.html

       

       

      Author's profile photo Gene Madikrama
      Gene Madikrama

      Hi Umut,

      Thank you for your information.

      I've created two CDS Views. These CDS Views are connected by using association [0..*].

      But how can I get a list of items on my Object Page? I am still getting this output

       

      // Kind regards,

      Gene

       

       

       

       

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Hi Gene, I understand the problem you are having. I don't think we can simply do this with using custom cds entity. 

      After writing this article, I decided that it is too early to use the RAP model for the complex unmanaged scenario includes custom cds entity, BAPIs, external services etc. 

      I am waiting a complex RAP unmanaged scenario example by Andre Fischer or Julie Plummer to describe limitation of RAP model with Complex Multi Dimensional Legacy Scenarios.

      Until these examples arrive, I think the following method is the best for us.

      Creating a draft enabled Sales Order Fiori App using the new ABAP Programming Model – Part 1: Overview

      Note: You can try to do my example without using custom cds entity, just using behavior implementation. Because we have serious restrictions on the use of custom cds entities.

      Again, I wrote this article to bring people into the discussion who can better answer these types of questions than I do. I hope they hear our voices. 🙂

      Thanks.

       

       

       

       

      Author's profile photo Julie Plummer
      Julie Plummer

      Hi Umut,

      Thanks for this very interesting blog post, and for the positive feedback.

      Just one request: By linking to the Github repo, you limit the users to existing Github users.

      We have purposely made these tutorials available in SAP Tutorial Navigator, so that anyone (well, anyone with a browser 😉 ) can access them. The link is here:

      https://developers.sap.com/mission.abap-env-connect-onpremise.html

      Could you please update your blog post accordingly?

      Thanks again and best wishes,

      Julie.

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Hi, Julia

      Thanks for your feedback. I updated link in my blogpost.

      Thank you also for the nice tutorials you have prepared.

      Author's profile photo shailaja ainala
      shailaja ainala

      Thankyou for the tutorlal.

      Shailaja Ainala.

      Author's profile photo Bhaskar Nagula
      Bhaskar Nagula

      Thank you Umut Yazici for your post!

      Can't we call BAPI in Managed scenarios with unmanaged save?

      Author's profile photo Umut Yazici
      Umut Yazici
      Blog Post Author

      Yes you can, this article does not contain strictly correct methods.

      Brownfield (unmanaged) scenarios are recommended by SAP.(Managed scenarios with unmanaged save or Unmanaged)

      https://help.sap.com/docs/BTP/923180ddb98240829d935862025004d6/bb7122a430f94f59b36081414ffd8a95.html

      Author's profile photo Marcel Hermanns
      Marcel Hermanns

      Hi everyone and thanks for the blog, Umut.

      Please find also further blogs related to this topic here:

      Calling a BAPI is (in general) not that easy in order to be SAP LUW compliant (e.g. no data inconsistencies) and user friendly. The two blogs provide some more background information 🙂

       

      Author's profile photo Sebastian Lube
      Sebastian Lube

      Hi and thanks for the blog. If i understand the comments correctly i should use a BAPI with internal update task in the save() method.

      1. how do I distinguish in the save method whether I am coming from CREATE, UPDATE, DELETE or from an action?
      2. how do I get access to the data of my action in the save method to fill the parameters of the BAPI?