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

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)

 

Source code


The source code can be found on GitHub.

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

Consumption view - ze2e001_c_salesorder

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?versio...

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?versio...

/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?versio...

/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.

 

 
5 Comments