Skip to Content
Technical Articles

Simple OData V4 service using Code Based implementation

I got a requirement to build OData service to fetch the data from DFKKKO and DFKKOP tables using OData V4. Using SEGW we can build OData V2 and finish it within the given time. After spending some time, I came to know that we should not use SEGW to create an OData V4. It is not recommended by SAP as of now. We have to use Code Based Implementation approach to create an OData V4 service. Thanks to Andre Fischer for writing 3 blogs on OData V4 Code Based Implementation. I’m writing this blog to help the beginners to easily understand OData V4 Code Based Implementation.

Here are steps to create an OData V4 service using Code Based Implementation.

1. Create 2 CDS views one for header and other one for items.

1.1 ZFICA_DFKKKO_V – Header CDS View

@AbapCatalog.sqlViewName: 'ZFICA_DFKKKO_V'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Header Data in Open Item Accounting Document'
define view ZFICA_DFKKKO as select from dfkkko
  association [1..*] to ZFICA_DFKKOP as _Item on _Item.opbel = $projection.opbel
{
key opbel,
fikey,
applk,
blart,
herkf,
ernam,
cpudt,
cputm,
      /* Associations */
      _Item
}

1.2 ZFICA_DFKKOP_V – Items CDS View.

@AbapCatalog.sqlViewName: 'ZFICA_DFKKOP_V'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Items in contract account document'
define view ZFICA_DFKKOP as select from dfkkop
key opbel,
key opupw,
key opupk,
key opupz,
bukrs,
gsber,
bupla,
segment
}

2. Create an Interface using the CDS views.

interface ZIF_DFKKOP_ODATA_V4_TYPES
  public .
 types:
    begin of gty_cds_views,
      accItem    type ZFICA_DFKKOP,
      accHeader  type ZFICA_DFKKKO,
    end of gty_cds_views.

 types: begin of gty_s_ko_kop .
      include type gty_cds_views-accheader.
  types:
    _item type standard table of gty_cds_views-accitem with default key,
    end of GTY_S_KO_KOP .

   types:
    begin of gt_key_range,
      opbel  type range of gty_cds_views-accheader-opbel,
      opupw type range of gty_cds_views-accitem-opupw,
    end of gt_key_range.

 constants:

    begin of gcs_cds_view_names,
      accItem    type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'ZFICA_DFKKOP',
      accHeader  type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'ZFICA_DFKKKO',
    end of gcs_cds_view_names,

    begin of gcs_entity_type_names,
      begin of internal,
        accItem    type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'ZFICA_DFKKOP',
        accHeader     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'ZFICA_DFKKKO',
      end of internal,
      begin of edm,
        accItem type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'AccItemType',
        accHeader    type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'AccHeaderType',
      end of edm,
    end of gcs_entity_type_names,

     begin of gcs_entity_set_names,
      begin of internal,
        accItem type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'ZFICA_DFKKOP',
        accHeader     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'ZFICA_DFKKKO',
      end of internal,
      begin of edm,
        accItem type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'AccItem',
        accHeader     type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'AccHeader',
      end of edm,
    end of gcs_entity_set_names ,

    begin of gcs_nav_prop_names,
      begin of internal,
        accheader_to_items type /iwbep/if_v4_med_element=>ty_e_med_internal_name value '_ITEM',
      end of internal,
      begin of edm,
        accheader_to_items type /iwbep/if_v4_med_element=>ty_e_med_edm_name value '_Item',
      end of edm,
    end of gcs_nav_prop_names.

endinterface.

3. Create a Model Provider class.

CLASS zcl_dfkkkop_odata_v4_model DEFINITION
  PUBLIC
  inheriting from /iwbep/cl_v4_abs_model_prov
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    interfaces zif_DFKKOP_odata_v4_types.
      methods /iwbep/if_v4_mp_basic~define redefinition.

  PROTECTED SECTION.
  PRIVATE SECTION.

   aliases gty_cds_views
      for zif_DFKKOP_odata_v4_types~gty_cds_views.
    aliases gcs_entity_set_names
      for zif_DFKKOP_odata_v4_types~gcs_entity_set_names .
    aliases gcs_entity_type_names
      for zif_DFKKOP_odata_v4_types~gcs_entity_type_names .
    aliases gcs_nav_prop_names
      for zif_DFKKOP_odata_v4_types~gcs_nav_prop_names.

    methods define_accHeader
      importing
        io_model type ref to /iwbep/if_v4_med_model
      raising
        /iwbep/cx_gateway .

    methods define_accItem
      importing
        io_model type ref to /iwbep/if_v4_med_model
      raising
        /iwbep/cx_gateway .

ENDCLASS.



CLASS zcl_dfkkkop_odata_v4_model IMPLEMENTATION.

  method /iwbep/if_v4_mp_basic~define.
    define_accHeader( io_model ).
    define_accItem( io_model ).
  endmethod.

   method  define_accHeader.
    data: lt_primitive_properties type /iwbep/if_v4_med_element=>ty_t_med_prim_property,
          lo_entity_set           type ref to /iwbep/if_v4_med_entity_set,
          lo_nav_prop             type ref to /iwbep/if_v4_med_nav_prop,
          lo_entity_type          type ref to /iwbep/if_v4_med_entity_type,
          lv_referenced_cds_view  type gty_cds_views-accheader  . " As internal ABAP name we use the name of the CDS view

  """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create entity type
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type = io_model->create_entity_type_by_struct(
                      exporting
                        iv_entity_type_name          = gcs_entity_type_names-internal-accheader
                        is_structure                 = lv_referenced_cds_view
                        iv_add_conv_to_prim_props    = abap_true
                        iv_add_f4_help_to_prim_props = abap_true
                        iv_gen_prim_props            = abap_true ).

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Set external EDM name for entity type
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type->set_edm_name( gcs_entity_type_names-edm-accheader ).

        """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Rename external EDM names of properties so that CamelCase notation is used
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type->get_primitive_properties( importing et_property = lt_primitive_properties ).

    loop at lt_primitive_properties into data(lo_primitive_property).
      lo_primitive_property->set_edm_name( to_mixed( val = lo_primitive_property->get_internal_name( ) ) ).
    endloop.

     """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Set key field(s)
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_primitive_property = lo_entity_type->get_primitive_property( 'OPBEL' ).
    lo_primitive_property->set_is_key( ).


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create navigation property
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_nav_prop = lo_entity_type->create_navigation_property( gcs_nav_prop_names-internal-accheader_to_items ).
    lo_nav_prop->set_edm_name( gcs_nav_prop_names-edm-accheader_to_items ).

    lo_nav_prop->set_target_entity_type_name( gcs_entity_type_names-internal-accitem ).
    lo_nav_prop->set_target_multiplicity( /iwbep/if_v4_med_element=>gcs_med_nav_multiplicity-to_many_optional ).
    lo_nav_prop->set_on_delete_action( /iwbep/if_v4_med_element=>gcs_med_on_delete_action-none ).

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create entity set
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_set = lo_entity_type->create_entity_set( gcs_entity_set_names-internal-accheader ).
    lo_entity_set->set_edm_name( gcs_entity_set_names-edm-accheader ).

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Add the binding of the navigation path
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_set->add_navigation_prop_binding( iv_navigation_property_path = conv #( gcs_nav_prop_names-internal-accheader_to_items )
                                                iv_target_entity_set        = gcs_entity_set_names-internal-accitem ).

  endmethod.

  method define_accItem.
    data: lo_entity_type          type ref to /iwbep/if_v4_med_entity_type,
          lo_entity_set           type ref to /iwbep/if_v4_med_entity_set,
          lt_primitive_properties type /iwbep/if_v4_med_element=>ty_t_med_prim_property,
          lv_referenced_cds_view  type gty_cds_views-accitem  . " As internal ABAP name we use the name of the CDS view


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Create entity type
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type = io_model->create_entity_type_by_struct(
                      exporting
                        iv_entity_type_name          = gcs_entity_type_names-internal-accitem
                        is_structure                 = lv_referenced_cds_view
                        iv_add_conv_to_prim_props    = abap_true
                        iv_add_f4_help_to_prim_props = abap_true
                        iv_gen_prim_props            = abap_true ).

    lo_entity_type->set_edm_name( gcs_entity_type_names-edm-accitem ).

     lo_entity_type->get_primitive_properties( importing et_property = lt_primitive_properties ).

    loop at lt_primitive_properties into data(lo_primitive_property).
      lo_primitive_property->set_edm_name( to_mixed( val = lo_primitive_property->get_internal_name( ) ) ).
    endloop.

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Set key field(s)
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_primitive_property = lo_entity_type->get_primitive_property( 'OPBEL' ).
    lo_primitive_property->set_is_key( ).

    lo_primitive_property = lo_entity_type->get_primitive_property( 'OPUPW'  ).
    lo_primitive_property->set_is_key( ).


      """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create entity set
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_set = lo_entity_type->create_entity_set( gcs_entity_set_names-internal-accitem ).
    lo_entity_set->set_edm_name( gcs_entity_set_names-edm-accitem ).

  endmethod.
ENDCLASS.

4. Create a Data Provider Class.

CLASS zcl_dfkkkop_odata_v4_data DEFINITION
  PUBLIC
  inheriting from /iwbep/cl_v4_abs_data_provider
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    interfaces zif_dfkkop_odata_v4_types.

    methods /iwbep/if_v4_dp_basic~read_entity redefinition .
    methods /iwbep/if_v4_dp_basic~read_entity_list redefinition.
    methods /iwbep/if_v4_dp_basic~read_ref_target_key_data_list redefinition .

  PROTECTED SECTION.
  PRIVATE SECTION.

   aliases gcs_entity_set_names
       for zif_dfkkop_odata_v4_types~gcs_entity_set_names .
    aliases gcs_entity_type_names
      for zif_dfkkop_odata_v4_types~gcs_entity_type_names .
    aliases gty_cds_views
      for zif_dfkkop_odata_v4_types~gty_cds_views.
    aliases gcs_nav_prop_names
      for zif_dfkkop_odata_v4_types~gcs_nav_prop_names.

      methods read_list_accheader
      importing
        io_request        type ref to /iwbep/if_v4_requ_basic_list
        io_response       type ref to /iwbep/if_v4_resp_basic_list
        iv_orderby_string type string
        iv_where_clause   type string
        iv_select_string  type string
        iv_skip           type i
        iv_top            type i
        is_done_list      type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list
      raising
        /iwbep/cx_gateway.


          methods read_entity_accheader
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_read
        io_response type ref to /iwbep/if_v4_resp_basic_read
      raising
        /iwbep/cx_gateway.

        methods read_ref_key_list_accheader
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_ref_l
        io_response type ref to /iwbep/if_v4_resp_basic_ref_l
      raising
        /iwbep/cx_gateway.

    methods read_list_accitem
      importing
        io_request        type ref to /iwbep/if_v4_requ_basic_list
        io_response       type ref to /iwbep/if_v4_resp_basic_list
        iv_orderby_string type string
        iv_where_clause   type string
        iv_select_string  type string
        iv_skip           type i
        iv_top            type i
        is_done_list      type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list
      raising
        /iwbep/cx_gateway.

           methods read_entity_accitem
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_read
        io_response type ref to /iwbep/if_v4_resp_basic_read
      raising
        /iwbep/cx_gateway.




ENDCLASS.



CLASS zcl_dfkkkop_odata_v4_data IMPLEMENTATION.



  method /iwbep/if_v4_dp_basic~read_entity.

    data: lv_entityset_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.


    io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-accheader.
        read_entity_accheader(
          exporting
            io_request  = io_request
            io_response = io_response ).

      when gcs_entity_set_names-internal-accitem.
        read_entity_accitem(
          exporting
            io_request  = io_request
            io_response = io_response ).

      when others.
        super->/iwbep/if_v4_dp_basic~read_entity(
          exporting
            io_request  = io_request
            io_response = io_response ).

    endcase.

  endmethod.

  method /iwbep/if_v4_dp_basic~read_entity_list.

    data lv_entityset_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.

    data: ls_todo_list         type /iwbep/if_v4_requ_basic_list=>ty_s_todo_list,
          ls_done_list         type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list,
          lv_where_clause      type string,
          lv_select_string     type string,
          lv_orderby_string    type string,
          lt_selected_property type /iwbep/if_v4_runtime_types=>ty_t_property_path,
          lv_skip              type i value 0,
          lv_top               type i value 0,
          lt_orderby_property  type abap_sortorder_tab.


    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    if ls_todo_list-process-orderby = abap_true.
      ls_done_list-orderby = abap_true.

      io_request->get_orderby( importing et_orderby_property = lt_orderby_property ).
      clear lv_orderby_string.
      loop at lt_orderby_property into data(ls_orderby_property).
        if ls_orderby_property-descending = abap_true.
          concatenate lv_orderby_string ls_orderby_property-name 'DESCENDING' into lv_orderby_string separated by space.
        else.
          concatenate lv_orderby_string ls_orderby_property-name 'ASCENDING' into lv_orderby_string separated by space.
        endif.
      endloop.

    else.
      " lv_orderby_string must not be empty.
      lv_orderby_string = 'PRIMARY KEY'.
    endif.


     " $skip / $top handling
    if ls_todo_list-process-skip = abap_true.
      ls_done_list-skip = abap_true.
      io_request->get_skip( importing ev_skip = lv_skip ).
    endif.
    if ls_todo_list-process-top = abap_true.
      ls_done_list-top = abap_true.
      io_request->get_top( importing ev_top = lv_top ).
    endif.


    " $select handling
    if ls_todo_list-process-select = abap_true.
      ls_done_list-select = abap_true.
      io_request->get_selected_properties(  importing et_selected_property = lt_selected_property ).
      concatenate lines of lt_selected_property into lv_select_string  separated by ','.
    else.
      "check coding. If no columns are specified via $select retrieve all columns from the model instead?
      lv_select_string = '*'.
      "or better to throw an exception instead?
    endif.


        " specific sales orders based on $filter?
    if ls_todo_list-process-filter = abap_true.
      ls_done_list-filter = abap_true.
      io_request->get_filter_osql_where_clause( importing ev_osql_where_clause = lv_where_clause ).
    endif.

   io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-accheader.

        read_list_accheader(
          exporting
            io_request        = io_request
            io_response       = io_response
            iv_orderby_string = lv_orderby_string
            iv_select_string  = lv_select_string
            iv_where_clause   = lv_where_clause
            iv_skip           = lv_skip
            iv_top            = lv_top
            is_done_list      = ls_done_list ).

      when gcs_entity_set_names-internal-accitem.

              read_list_accitem(
          exporting
            io_request        = io_request
            io_response       = io_response
            iv_orderby_string = lv_orderby_string
            iv_select_string  = lv_select_string
            iv_where_clause   = lv_where_clause
            iv_skip           = lv_skip
            iv_top            = lv_top
            is_done_list      = ls_done_list ).

      when others.

        super->/iwbep/if_v4_dp_basic~read_entity_list( io_request  = io_request
                                                       io_response = io_response ).
    endcase.


     endmethod.

 method /iwbep/if_v4_dp_basic~read_ref_target_key_data_list.

    data: lv_source_entity_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.


    io_request->get_source_entity_type( importing ev_source_entity_type_name = lv_source_entity_name ).

    case lv_source_entity_name.

      when gcs_entity_type_names-internal-accheader.
        read_ref_key_list_accheader(
           exporting
            io_request  = io_request
            io_response = io_response ).

      when others.
        super->/iwbep/if_v4_dp_basic~read_ref_target_key_data_list(
          exporting
            io_request  = io_request
            io_response = io_response ).

    endcase.

  endmethod.

  method read_entity_accheader.

    "entity type specific data types
    data: ls_accheader         type gty_cds_views-accheader,
          ls_key_accheader     type gty_cds_views-accheader,
          lv_accheader_key_edm type string,
          lv_helper_int         type i.
    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_process_list.

    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_accheader ).
    ls_done_list-key_data = abap_true.

    select single * from zFICA_DFKKKO
    into corresponding fields of @ls_accheader
    where opbel = @ls_key_accheader-opbel.

    if ls_accheader is not initial.
      io_response->set_busi_data( is_busi_data = ls_accheader ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_accheader_key_edm = lv_helper_int = ls_key_accheader-opbel.

   endif.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).
  endmethod.

   method read_entity_accitem.
    "entity type specific data types
    data: ls_accitem         type gty_cds_views-accitem,
          ls_key_accitem     type gty_cds_views-accitem,
          lv_key_edm_accitem type string,
          lv_helper_int      type i.
    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_process_list.

    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_accitem ).
    ls_done_list-key_data = abap_true.

    select single * from ZFICA_DFKKOP
    into corresponding fields of @ls_accitem
    where opbel = @ls_key_accitem-opbel
    and  OPUPW = @ls_key_accitem-OPUPW.

    if ls_accitem is not initial.
      io_response->set_busi_data( is_busi_data = ls_accitem ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_key_edm_accitem = lv_helper_int = ls_key_accitem-opbel.
      lv_key_edm_accitem = lv_key_edm_accitem && ','.
      lv_helper_int = ls_key_accitem-opbel.
      lv_key_edm_accitem = lv_key_edm_accitem && lv_helper_int.

    endif.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).
  endmethod.

   method read_ref_key_list_accheader.

    "entity type specific data types
    data: ls_accheader_key_data     type  gty_cds_views-accheader,
          lt_accitem_key_data    type standard table of gty_cds_views-accitem,
          ls_todo_list               type /iwbep/if_v4_requ_basic_ref_l=>ty_s_todo_list.
    "generic data types
    data: ls_done_list         type /iwbep/if_v4_requ_basic_ref_l=>ty_s_todo_process_list,
          lv_nav_property_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.

    " Get the request options the application should/must handle
    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    if ls_todo_list-process-source_key_data = abap_true.
      io_request->get_source_key_data( importing es_source_key_data =  ls_accheader_key_data ).
      ls_done_list-source_key_data = abap_true.
    endif.

    io_request->get_navigation_prop( importing ev_navigation_prop_name = lv_nav_property_name ).

    case lv_nav_property_name.
      when gcs_nav_prop_names-internal-accheader_to_items.

        select opbel, OPUPW from zfica_dfkkop
        into corresponding fields of table @lt_accitem_key_data
        where opbel = @ls_accheader_key_data-opbel.

        io_response->set_target_key_data( lt_accitem_key_data ).

      when others.

   endcase.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).

  endmethod.


   method read_list_accheader.

    "entity type specific data types
    data : lt_key_range_accheader type zif_dfkkop_odata_v4_types=>gt_key_range-opbel,
           ls_key_range_accheader type line of zif_dfkkop_odata_v4_types=>gt_key_range-opbel,
           lt_accheader           type standard table of gty_cds_views-accheader,
           lt_key_accheader       type standard table of gty_cds_views-accheader.

    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list,
          lv_count     type i,
          lv_max_index type i.

    " Get the request options the application should/must handle
    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    " Get the request options the application has already handled
    ls_done_list = is_done_list.


        " specific sales orders based on navigation?
    if ls_todo_list-process-key_data = abap_true.
      io_request->get_key_data( importing et_key_data = lt_key_accheader ).
      loop at lt_key_accheader into data(ls_key_entity).
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-opbel ) to lt_key_range_accheader.
      endloop.
      ls_done_list-key_data = abap_true.
    endif.


    "================================================================
    " read_list must either be called with a filter or via navigation
    " or $top has to be used to avoid a full table scan
    if  ls_todo_list-process-filter = abap_false
    and ls_todo_list-process-key_data = abap_false
    and iv_top = 0.
    endif.

    " Return business data if requested
    if ls_todo_list-return-busi_data = abap_true.

      " read data from the CDS view
      "value for max_index must only be calculated if the request also contains a $top
      if iv_top is not initial.
        lv_max_index = iv_top + iv_skip.
      else.
        lv_max_index = 0.
      endif.

      select (iv_select_string) from zfica_dfkkko
      where (iv_where_clause)
      and   opbel in @lt_key_range_accheader
      order by (iv_orderby_string)
      into corresponding fields of table @lt_accheader
      up to @lv_max_index rows.

      "skipping entries specified by $skip
      "not needed as of NW751 where OFFSET is supported in Open SQL
      if iv_skip is not initial.
        delete lt_accheader to iv_skip.
      endif.

         io_response->set_busi_data( it_busi_data = lt_accheader ).

          else.
      "if business data is requested count will be calculated by
      "the framework
      if ls_todo_list-return-count = abap_true.

        select count( * ) from zfica_dfkkko
            where (iv_where_clause) and
           opbel in @lt_key_range_accheader
            into @lv_count.

        io_response->set_count( lv_count ).
      endif.
    endif.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).
  endmethod.

  method read_list_accitem.

    "entity type specific data types
    data : lt_key_range_accheader     type zif_dfkkop_odata_v4_types=>gt_key_range-opbel,
           ls_key_range_accheadder     type line of zif_dfkkop_odata_v4_types=>gt_key_range-opbel,
           lt_key_range_accitem type zif_dfkkop_odata_v4_types=>gt_key_range-opupw,
           ls_key_range_accitem type line of zif_dfkkop_odata_v4_types=>gt_key_range-opupw,
           lt_accitem           type standard table of gty_cds_views-accitem,
           lt_key_accitem       type standard table of gty_cds_views-accitem.

    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list,
          lv_count     type i,
          lv_max_index type i.

    " Get the request options the application should/must handle
    io_request->get_todos( importing es_todo_list = ls_todo_list ).

     " Get the request options the application has already handled
    ls_done_list = is_done_list.

    " specific sales orders based on navigation?
    if ls_todo_list-process-key_data = abap_true.
      io_request->get_key_data( importing et_key_data = lt_key_accitem ).
      loop at lt_key_accitem into data(ls_key_entity).
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-opbel ) to lt_key_range_accheader.
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-opupw ) to lt_key_range_accitem.
      endloop.

      "the first key field (salesoder) is always the same
      delete adjacent duplicates from lt_key_range_accheader.
      ls_done_list-key_data = abap_true.
    endif.


        "================================================================
    " read_list must either be called with a filter or via navigation
    " or $top has to be used to avoid a full table scan
    if  ls_todo_list-process-filter = abap_false
    and ls_todo_list-process-key_data = abap_false
    and iv_top = 0.
    endif.

    " Return business data if requested
    if ls_todo_list-return-busi_data = abap_true.

     "value for max_index must only be calculated if the request also contains a $top
      if iv_top is not initial.
        lv_max_index = iv_top + iv_skip.
      else.
        lv_max_index = 0.
      endif.

      select (iv_select_string) from zfica_dfkkop
      where (iv_where_clause)
      and   opbel in @lt_key_range_accheader
      and   opupw in @lt_key_range_accitem
      order by (iv_orderby_string)
      into corresponding fields of table @lt_accitem
      up to @lv_max_index rows.


        "skipping entries specified by $skip
      "not needed as of NW751 where OFFSET is supported in Open SQL
      if iv_skip is not initial.
        delete lt_accitem to iv_skip.
      endif.

      io_response->set_busi_data( it_busi_data = lt_accitem ).

    else.
      "if business data is requested count will be calculated by
      "the framework
      if ls_todo_list-return-count = abap_true.

        select count( * ) from zfica_dfkkop
            where (iv_where_clause)
            and   opbel in @lt_key_range_accheader
            and   opupw in @lt_key_range_accitem
            into @lv_count.

        io_response->set_count( lv_count ).
      endif.
    endif.

        " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).

  endmethod.
ENDCLASS.

5. Final project Structure in your Eclipse should be like this.

  • CDS Views

CDS%20Views

  • Classes

Classes

  • Interface

6. We are done with coding part. Now, we must register our OData V4 service using the TCode: /iwbep/v4_admin – Backend System.

6.1 Register a service group ZSG_DKKKOP.

 6.2 Register the service as shown below by giving created Model and Data Provider classes.

6.3 Assign service to our service group as shown below.

6.4 Publish the service group using transaction /iwfnd/v4_admin – (Gateway System)

7. Display Metadata in Browser and you can test the service using the Service Test option.

8. Test the service using POSTMAN.

/sap/opu/odata4/sap/zsg_dkkkop/default/sap/zsid_dfkkop/0001/AccHeader(‘100000000’)?$expand=_Item

 

I’m sure, this blog will be useful to all the beginners who are going to start developing OData services in V4 🙂 . If anyone have suggestion on OData V4, please comment on it.

 

To find out the difference between OData V2 and V4. Please go through the following Blog.

https://blogs.sap.com/2021/02/05/is-it-time-to-switch-to-odata-v4/

Reference Blog:

https://blogs.sap.com/2017/12/12/odata-v4-code-based-implementation-overview/

 

Thank you,

Gangadhar

1 Comment
You must be Logged on to comment or reply to a post.
  • Nice to see that you are showing how to build "real" OData V4 services using code based implementation whereas I always use demo data 😉.