Skip to Content

Table of contents

This blog is part of blog series about OData V4 code based development

OData V4 code based implementation – Overview

OData V4 code based implementation I (basic interface, read access)

OData V4 code based implementation I (basic interface, create&update)

Demo system ES5

In order to access the source code below you have to register in the new ES5 demo system

Sign up for a demo account on ES5 here

More details about the ES5 demo system as such you will find in my following blog

New SAP Gateway Demo System available

Source code

If you have a user in ES5 you will be able to access the ABAP code via the following links.

Data provider class – zcl_e2e001_odata_v4_so_data

Model provider class – zcl_e2e001_odata_v4_so_model

Exception class – zcx_e2e001_odata_v4_so

Interface – zif_e2e001_odata_v4_so_types

Consumption view – sales order – ze2e001_c_salesorder

Consumption view sales order items – ze2e001_c_salesorderitem

Interface view – ze2e001_i_salesorderitem_e

Introduction

In this second blog about OData V4 code based implementation I want to show how to enable create and updated capabilities in the demo service, so that it is possible to perform the following actions.

  • create sales orders
  • update sales orders
  • delete sales orders
  • create sales order items
  • update sales order items
  • delete sales order items

Data Provider class

The data provider class contains the generic implementation of the basic interface methods for creating, updating and deleting data. These are the following methods.

  • /iwbep/if_v4_dp_basic~create_entity
  • /iwbep/if_v4_dp_basic~update_entity
  • /iwbep/if_v4_dp_basic~delete_entity

that  have to be redefined.

The complete code can be found in ES5:

https://sapes5.sapdevcenter.com/sap/bc/adt/oo/classes/zcl_e2e001_odata_v4_so_data/source/main?version=active&sap-client=002

How to create such useful links is described in Thomas Fiedlers blog.

In addition entity set specific impelmentations are added for both entity sets Salesorder and Salesorderitems. These are the following methods:

  • create_salesorder
  • create_salesorderitem
  • update_salesorder
  • update_salesorderitem
  • delete_salesorder
  • delete_salesorderitem

I will explain the functionality of these methods in the following. The complete code is shown in the following section.

iwbep/if_v4_dp_basic~create_entity

We start with the method /iwbep/if_v4_dp_basic~create_entity which is for example called if a client accesses an entity set with a Post request, for example Post ….<service root>/Salesorder.

As we can see the generic part of the coding is fairly small compared to the read operations. The coding basically checks which entity set has been accessed using the method io_request->get_entity_type. The name of the entity sets of our service have been defined in the interface zif_e2e001_odata_v4_so_types.

  method /iwbep/if_v4_dp_basic~create_entity.
    data: lv_entity_type_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.


    io_request->get_entity_type( importing ev_entity_type_name = lv_entity_type_name ).

    case lv_entity_type_name.

      when gcs_entity_type_names-internal-salesorder.
        create_salesorder(
            io_request  = io_request
            io_response = io_response ).

      when gcs_entity_type_names-internal-salesorderitem.
        create_salesorderitem(
            io_request  = io_request
            io_response = io_response ).

      when others.

        super->/iwbep/if_v4_dp_basic~create_entity(
            exporting
              io_request  = io_request
              io_response = io_response ).

    endcase.

  endmethod.

create_salesorder

The entity type specifc methods create_salesorder and create_salesorderitem both start with a definition of entity type specific data types that will hold the data being returned of the newly created entity and the data that has been provided by the client via the http payload.

We first have to retrieve the todo list via the io_request object.

  method create_salesorder.
    "entity type specific data types
    data: ls_salesorder         type gty_cds_views-salesorder,
          ls_salesorder_rfc     type bapi_epm_so_header,
          ls_salesorder_rfc_key type bapi_epm_so_id.
    "generic data types
    data:
      ls_todo_list              type /iwbep/if_v4_requ_basic_create=>ty_s_todo_list,
      ls_done_list              type /iwbep/if_v4_requ_basic_create=>ty_s_todo_process_list,
      lt_bapi_return            type table of bapiret2,
      ls_bapi_return            type bapiret2,
      lo_message_container      type ref to /iwbep/if_v4_message_container,
      lv_names_of_missing_props type string.

    io_request->get_todos( importing es_todo_list = ls_todo_list ).

With this todo list we can check whether our service implementation has to retrieve business data from the payload of the request.

    if ls_todo_list-process-busi_data = abap_true.
      io_request->get_busi_data( importing es_busi_data = ls_salesorder ).
      ls_done_list-busi_data = abap_true. "business data processed
    endif.

Next is to check whether the payload contains all properties that are necessary to create the new entitity. If the payload does not contain all properties of the entity type the SAP Gateway framwork sets the flag ls_todo_list-process-partial_busi_data in the todo list.

Since the sales order entity type contains properties that are being calculated after creation of a sales order (for example the creation date) the partial_busi_data flag will always be set. So we have to check for the properties that are mandatory when calling our BAPI seperately.

If a mandatory property is missing an exception is raised telling the client that either the property Customer or TransactionCurrency is missing.

    if ls_todo_list-process-partial_busi_data = abap_true.
      "check if the mandatory properties have been provided
      "do not check for properties that are not mandatory      

      if ls_salesorder-customer is not initial and
      ls_salesorder-transactioncurrency is not initial.
        ls_salesorder_rfc-buyer_id = ls_salesorder-customer.
        ls_salesorder_rfc-currency_code  = ls_salesorder-transactioncurrency.
        ls_done_list-partial_busi_data = abap_true.
      else.
        lv_names_of_missing_props = 'Customer or TransactionCurrency'.
        raise exception type zcx_e2e001_odata_v4_so
          exporting
            textid                 = zcx_e2e001_odata_v4_so=>missing_properties
            http_status_code       = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-bad_request
            edm_entity_set_name    = gcs_entity_set_names-edm-salesorder
            names_of_missing_props = lv_names_of_missing_props.
      endif.
    endif.

Now we can call the Bapi BAPI_EPM_SO_CREATE to create a new sales order. If an error occurs the return message of our Bapi is added to a message container and sent back to the SAP Gateway framework by raising an exception.

    call function 'BAPI_EPM_SO_CREATE'
      exporting
        headerdata   = ls_salesorder_rfc
      importing
        salesorderid = ls_salesorder_rfc_key
      tables
        return       = lt_bapi_return.

    " Error handling
    if lt_bapi_return is not initial.
      "check if an error message is in lt_bapi_return
      loop at lt_bapi_return into ls_bapi_return.
        if ls_bapi_return-type = 'E'.

          lo_message_container = io_response->get_message_container( ).

          loop at lt_bapi_return into ls_bapi_return.
            lo_message_container->add_t100(
              exporting
                iv_msg_type                 =     ls_bapi_return-type
                iv_msg_id                   =     ls_bapi_return-id
                iv_msg_number               =     ls_bapi_return-number
                iv_msg_v1                   =     ls_bapi_return-message_v1
                iv_msg_v2                   =     ls_bapi_return-message_v2
                iv_msg_v3                   =     ls_bapi_return-message_v3
                iv_msg_v4                   =     ls_bapi_return-message_v4 ).
          endloop.

          "raise exception
          raise exception type zcx_e2e001_odata_v4_so
            exporting
              message_container = lo_message_container.

        endif.
      endloop.

    endif.

If everything worked fine we can read the data of our newly created sales order from the CDS view that we have created and send it back to the framework using the method io_response->set_busi_data.

    if ls_todo_list-return-busi_data = abap_true.
      "   Read data again and set the response.
      clear ls_salesorder.
      select single * from ze2e001_c_salesorder
      into corresponding fields of @ls_salesorder
      where salesorder = @ls_salesorder_rfc_key-so_id.
      io_response->set_busi_data( ls_salesorder ).
    endif.

 

As a final step we have to inform the framework which tasks our implementation has taken care of.

  io_response->set_is_done( ls_done_list ).

create_salesorderitem

For creating a salesorder item we have to use the Bapi BAPI_EPM_SO_CHANGE.

The complete coding can be found in ES5:

https://sapes5.sapdevcenter.com/sap/bc/adt/oo/classes/zcl_e2e001_odata_v4_so_data/source/main?version=active&sap-client=002

/iwbep/if_v4_dp_basic~update_entity

The generic implementationpart of the method /iwbep/if_v4_dp_basic~update_entity is similar to the basic create method. After checking which entity set has been accessed the entity set specific update method is being called.

update_salesorder

First again entity type specific and genric data types have to be defined. In addition to the data types that are based on the CDS views we have to define data types to send the request payload to the Bapi and the structre that is used to tell the Bapi which fields have been provided by the caller.

  method update_salesorder.

    "entity type specific data types
    data: ls_salesorder         type gty_cds_views-salesorder,
          ls_key_salesorder     type gty_cds_views-salesorder,
          ls_salesorder_rfc     type bapi_epm_so_header,
          ls_salesorder_x_rfc   type bapi_epm_so_headerx,
          ls_salesorder_rfc_key type bapi_epm_so_id,
          lv_salesorder_key_edm type string,
          lv_helper_int         type i.
    "generic data types
    data: ls_todo_list         type /iwbep/if_v4_requ_basic_update=>ty_s_todo_list,
          ls_done_list         type /iwbep/if_v4_requ_basic_update=>ty_s_todo_process_list,
          lt_bapi_return       type table of bapiret2,
          lo_message_container type ref to /iwbep/if_v4_message_container.

    io_request->get_todos( importing es_todo_list = ls_todo_list ).
    if ls_todo_list-process-busi_data = abap_true.
      io_request->get_busi_data( importing es_busi_data = ls_salesorder ).
      ls_done_list-busi_data = abap_true. "business data processed
    endif.

The todo list can contain the following flags: process-busi_data, process-key_data and process-process-partial_busi_data as well as return-busi_data. The process-partial_busi_data flag will not be set most of the time since the recommended method for updating data in OData V4 is the Patch method.

The business data is moved from the structure ls_salesorder to the Bapi specific data structure ls_salesorder_rfc  and the appropriate fields in the corresponding x-structure ls_salesorder_x_rfc.

" Update single entity using classic API.

    " fill structure for header data
    ls_salesorder_rfc-so_id = ls_key_salesorder-salesorder.
    ls_salesorder_rfc-buyer_id = ls_salesorder-customer.
    ls_salesorder_rfc-currency_code  = ls_salesorder-transactioncurrency.
    " @TODO Sales order description is not yet a property of the CDS view

    " Map constant values to function module parameters
    ls_salesorder_x_rfc-so_id = ls_key_salesorder-salesorder.
    ls_salesorder_x_rfc-buyer_id = 'X'.
    ls_salesorder_x_rfc-currency_code  = 'X'.
    " @TODO Sales order description is not yet a property of the CDS view

    ls_salesorder_rfc_key-so_id = ls_key_salesorder-salesorder.

* update data

    call function 'BAPI_EPM_SO_CHANGE'
      exporting
        so_id         = ls_salesorder_rfc_key
        soheaderdata  = ls_salesorder_rfc
        soheaderdatax = ls_salesorder_x_rfc
      tables
        return        = lt_bapi_return.

    " Error handling
    if lt_bapi_return is not initial.
      lo_message_container = io_response->get_message_container( ).

      loop at lt_bapi_return into data(ls_bapi_return).
        lo_message_container->add_t100(
          exporting
            iv_msg_type                 =     ls_bapi_return-type
            iv_msg_id                   =     ls_bapi_return-id
            iv_msg_number               =     ls_bapi_return-number
            iv_msg_v1                   =     ls_bapi_return-message_v1
            iv_msg_v2                   =     ls_bapi_return-message_v2
            iv_msg_v3                   =     ls_bapi_return-message_v3
            iv_msg_v4                   =     ls_bapi_return-message_v4 ).
      endloop.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          message_container = lo_message_container.

    endif.

 

 

 

 

 

A new thing in ODat V4 is that an update request will contain the data of the updated entity by default unless being told otherwise by setting the Prefefer header return=minimal. We can check this using the flag ls_todo_list-return-busi_data and return data using the method io_response->set_busi_data if being requested to do so.

    if ls_todo_list-return-busi_data = abap_true.
      "   Read data again and set the response.
      select single * from ze2e001_c_salesorder
      into corresponding fields of @ls_salesorder
      where salesorder = @ls_salesorder_rfc_key-so_id.
      io_response->set_busi_data( ls_salesorder ).
    endif.
    io_response->set_is_done( ls_done_list ).

The following screen shot shows an example where no response is sent since the Prefer header return=minimal is used.

update_salesorderitem

For updating the sales order item the coding can be found in ES5:

https://sapes5.sapdevcenter.com/sap/bc/adt/oo/classes/zcl_e2e001_odata_v4_so_data/source/main?version=active&sap-client=002

/iwbep/if_v4_dp_basic~delete_entity

In the genric part we are checking in which entiy set an entity shall be deleted like it is done in the methods /iwbep/if_v4_dp_basic~create_entity and /iwbep/if_v4_dp_basic~update_entity. Here the entiy set specifc methods delete_salesorder or delete_salesorderitem are called.

delete_salesorder

The delete methods are much simpler than the create and update methods. There is only one todo flag process-key_data.

If the deletion of the sales order fails the bapi return message is sent to the client via raising an exception using the exception class.

  method delete_salesorder.
    "entity type specific data types
    data: ls_key_salesorder type gty_cds_views-salesorder,
          ls_so_id          type bapi_epm_so_id.

    "generic data types
    data:
      ls_todo_list         type /iwbep/if_v4_requ_basic_delete=>ty_s_todo_list,
      ls_done_list         type /iwbep/if_v4_requ_basic_delete=>ty_s_todo_process_list,
      lt_bapi_return       type table of bapiret2,
      lo_message_container type ref to /iwbep/if_v4_message_container.



    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    " read the key data
    io_request->get_key_data( importing es_key_data = ls_key_salesorder ).
    ls_done_list-key_data = abap_true.

    ls_so_id-so_id = ls_key_salesorder-salesorder.

    " Delete data
    call function 'BAPI_EPM_SO_DELETE'
      exporting
        so_id  = ls_so_id
      tables
        return = lt_bapi_return.

    " Error handling
    if lt_bapi_return is not initial.
      lo_message_container = io_response->get_message_container( ).

      loop at lt_bapi_return into data(ls_bapi_return).
        lo_message_container->add_t100(
          exporting
            iv_msg_type                 =     ls_bapi_return-type
            iv_msg_id                   =     ls_bapi_return-id
            iv_msg_number               =     ls_bapi_return-number
            iv_msg_v1                   =     ls_bapi_return-message_v1
            iv_msg_v2                   =     ls_bapi_return-message_v2
            iv_msg_v3                   =     ls_bapi_return-message_v3
            iv_msg_v4                   =     ls_bapi_return-message_v4 ).
      endloop.

      "custom exception
      raise exception type zcx_e2e001_odata_v4_so
        exporting
          message_container = lo_message_container.

    endif.

    io_response->set_is_done( ls_done_list ).
  endmethod.

 

 

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply