Skip to Content
Author's profile photo Former Member

Create ODATA service and implement CRUD methods using ABAP 7.4

In this article, I have described step-by-step procedure to create ODATA service and implement method for CRUD(Read) operations. In this article I have tried using ABAP 7.4 syntax as much as I can in the methods to write business logic. An attempt to show how we can reduce code lines using ABAP 7.4.

Go to SEGW to create a project: ZEMP_MODEL_ODATA using create project button.

Right click on Data Model node and select Import->DDIC structure to create entity type and entityset.

In the next screen enter below inputs to create Soheaderdata entitytype and SoheaderdataSet entityset

Select all fields in the next screen

Select Key field in the next screen which is SO_ID in this case.

Press Finish button to create entitytype and entityset.

Repeat above steps to create entity type and entity set for item and products using structures: BAPI_EPM_SO_ITEM and BAPI_EPM_PRODUCT_HEADER.

Select SO_ID and SO_ITEM_POS as key fields for item entity type

Select PRODUCT_ID as key for product entity type

Now we have created 3 entity types and entity sets:

You can also see service implementations created for 3 entity sets.

Click on Generate run time objects button to create run time artifacts to implement CRUD operations

Classed generated:

Now expand service Maintenance folder and register ODATA service. And Then save project.

Alternatively you can also perform registering service manually using tcode: /IWFND/MAINT_SERVICE.

You will notice signal light turns to green in below screen shot

To Test ODATA Service, click on Maintain button on same screen:

Select ODATA row and click on call browser:

Now we will create associations and navigation property between Soheaderdata and Soitemdata and Soitemdata and ProductHeaderdata.

Specify association name, principal entity type name, dependent entity type name, Cardinality and Navigation Property in below window.

Specify referential constraints between Soheaderdata and Soitemdata and specify key relation in below window.

Finish setup in next window to create association and navigation property

Now create association and navigation property between Soitemdata and ProductHeaderdata similar way.

Delete below referential Constraints because there is no key relation

Now we are done with ODATA setup. In the next steps, we will implement data provider class methods to retrieve data.

In service implementation folder, for SoheaderdataSet entity set, we will implement method for GetEntitySet(Query) to retrieve all sales orders header data.

To do this, go to Implementation class: ZCL_ZEPM_MODEL_ODATA_DPC_EXT in SE24 tcode and redefine method: SOHEADERDATASET_GET_ENTITYSET.

This method will be called for below URL to retrieve sales orders header data:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet

We will also implement URI parameters Filter, Skip, Top and Orderby in the above URL.

To learn more about URI parameter in odata services, refer to below link:

http://www.odata.org/documentation/odata-version-2-0/uri-conventions/

Example URL’s with URI paramters:

For Filter URI on CurrencyCode field:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$filter=CurrencyCode eq ‘EUR’

For Skip and Top URI parameter: This will skip for 2 entries and display next top 10 sales orders.

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$skip=2&$top=10

For orderby URI parameter:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$orderby=GrossAmount

Copy below code in the method: SOHEADERDATASET_GET_ENTITYSET

Data declarations:

    TYPES: tt_soorder TYPE STANDARD TABLE OF  bapi_epm_so_header WITH EMPTY KEY.
    TYPES: BEGIN OF ty_where,
             cond(70),
           END OF ty_where.
    DATA: lt_where          TYPE TABLE OF ty_where.
    DATA: lt_sort_order TYPE STANDARD TABLE OF abap_sortorder.

Get Filter and orderby URI parameter values. Filter or selection URI parameters can retrieved using below method.

  IF io_tech_request_context IS BOUND.
      DATA(t_orderby) = io_tech_request_context->get_orderby( ).
  ENDIF.
* Get filter or select option information
    DATA(lo_filter) = io_tech_request_context->get_filter( ).
    DATA(lt_filter_select_options) = lo_filter->get_filter_select_options( ).
    DATA(lv_filter_str) = lo_filter->get_filter_string( ).

Build dynamic where condition and select sale orders header data

* Build dynamic where condition
    DATA(lw_where) =  CONV ty_where( lv_filter_str ).
    APPEND lw_where TO lt_where.
    SELECT *
      FROM snwd_so
      INTO TABLE @DATA(t_soheader)
      WHERE (lt_where).

Handling page and skip:

* paging and skip
    DATA(lw_end_row) = is_paging-top + 1.
    IF is_paging-skip IS NOT INITIAL.
      DELETE t_soheader FROM 1 TO is_paging-skip.
    ENDIF.
    IF is_paging-top IS NOT INITIAL..
      DELETE t_soheader FROM lw_end_row.
    ENDIF.

Perform orderby on result data set and populate et_entityset:

    lt_sort_order = VALUE abap_sortorder_tab( FOR lw_orderby IN t_orderby LET lw_order = abap_true IN
                                            ( name = lw_orderby-property descending = lw_order )  ).
    SORT t_soheader BY (lt_sort_order).
    MOVE-CORRESPONDING t_soheader TO et_entityset.

To retrieve specific sales order data using below URL, we will implement method: SOHEADERDATASET_GET_ENTITY for GetEntity(Read).

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)

Redefine method in same class and copy below code in the method:

    DATA: w_soheader TYPE bapi_epm_so_header.
    DATA: w_so_id TYPE bapi_epm_so_id.
    DATA(w_salord) =  it_key_tab[ name = 'SoId' ]-value.
    DATA(w_vbeln) = CONV vbeln( |{ w_salord ALPHA = in }| ).
    w_so_id-so_id = w_vbeln.
    CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
      EXPORTING
        so_id      = w_so_id
      IMPORTING
        headerdata = w_soheader.
    MOVE-CORRESPONDING w_soheader TO er_entity.

Now we will implement methods related to SoitemdataSet:

Method: SOITEMDATASET_GET_ENTITYSET to get all sales items for sales order using URL: http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)/SalesOrderItem

Alternatively we can use URL: $expand=SalesOrderItem(Navigation property from Soheaderdata) http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)?$expand=SalesOrderItem

Redefine method SOITEMDATASET_GET_ENTITYSET in the class: ZCL_ZEPM_MODEL_ODATA_DPC_EXT

And Copy below code:

    DATA: w_soheader TYPE bapi_epm_so_header.
    DATA: w_so_id     TYPE bapi_epm_so_id,
          w_item_data TYPE STANDARD TABLE OF bapi_epm_so_item.
    DATA(w_salord) =  it_key_tab[ name = 'SoId' ]-value.
    DATA(w_vbeln) = CONV vbeln( |{ w_salord ALPHA = IN }| ).
    w_so_id-so_id = w_vbeln.
    CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
      EXPORTING
        so_id      = w_so_id
      IMPORTING
        headerdata = w_soheader
      TABLES
        itemdata   = w_item_data.
    et_entityset = CORRESPONDING zcl_zepm_model_odata_mpc=>tt_soitemdata( w_item_data ).

Below URL will calls methods SOHEADERDATASET_GET_ENTITYSET and SOITEMDATASET_GET_ENTITYSET in loop to retrieve all line items related to all sales orders

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$expand=SalesOrderItem

Next we will implement SOITEMDATASET_GET_ENTITY to display sales item data for specific sales order and sales item. We can use below URL:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoitemdataSet(SoId=’500000000′,SoItemPos=’10’)

Alternatively we can use URL with navigation property:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000′)/SalesOrderItem(SoId=’500000000′,SoItemPos=’10’)

Redefine method SOITEMDATASET_GET_ENTITY and copy below code:

    DATA: w_soheader TYPE bapi_epm_so_header.
    DATA: w_so_id   TYPE bapi_epm_so_id,
          t_so_item TYPE STANDARD TABLE OF bapi_epm_so_item.
* If navigation property used in URL, get key values from it_navigation_path importing parameter with path: SalesOrderItem
* Else use it_key_tab from importing parameter
    IF it_navigation_path[] IS NOT INITIAL.
      DATA(it_key_tab_tmp) = it_navigation_path[ nav_prop = 'SalesOrderItem' ]-key_tab.
    ELSE.
      it_key_tab_tmp = it_key_tab.
    ENDIF.
    DATA(w_salord) =  it_key_tab_tmp[ name = 'SoId' ]-value.
    DATA(w_vbeln) = CONV vbeln( |{ w_salord ALPHA = IN }| ).
    w_so_id-so_id = w_vbeln.
    DATA(w_salitem) =  it_key_tab_tmp[ name = 'SoItemPos' ]-value.
    DATA(w_posnr) = CONV vbeln( |{ w_salitem ALPHA = IN }| ).
    CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
      EXPORTING
        so_id      = w_so_id
      IMPORTING
        headerdata = w_soheader
      TABLES
        itemdata   = t_so_item.
    er_entity = CORRESPONDING zcl_zepm_model_odata_mpc=>ts_soitemdata( t_so_item[ so_item_pos = w_posnr ] ).

Below URL also retrieves all items related to single sales order “500000000”: This will call methods SOHEADERDATASET_GET_ENTITY and SOITEMDATASET_GET_ENTITYSET .

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)?$expand=SalesOrderItem

Next we will implement: PRODUCTHEADERDAT_GET_ENTITYSET method to retrieve product details:

Below URL retrieves product details related to sales order 500000000 and Item 10:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoitemdataSet(SoId=’500000000′,SoItemPos=’10’)/SalesOrderProduct

This URL calls Methods SOITEMDATASET_GET_ENTITY and then PRODUCTHEADERDAT_GET_ENTITYSET

Alternatively we can also use URL:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘5000000000′)/SalesOrderItem(SoId=’500000000′,SoItemPos=’10’)/SalesOrderProduct

This URL calls SOHEADERDATASET_GET_ENTITY and SOITEMDATASET_GET_ENTITY and then PRODUCTHEADERDAT_GET_ENTITYSET.

Below URL will retrieve all items and product info related to order: 5000000000

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)?$expand=SalesOrderItem/SalesOrderProduct

This URL calls SOHEADERDATASET_GET_ENTITY and SOITEMDATASET_GET_ENTITYSET and then PRODUCTHEADERDAT_GET_ENTITYSET.

Below URL will retrieve all products info:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/ProductHeaderdataSet

This URL calls PRODUCTHEADERDAT_GET_ENTITYSET.

Now redefine method: PRODUCTHEADERDAT_GET_ENTITYSET and copy below code:

Check for entries in it_key_tab and it_navigation_path to see if we need to retrieve product details related to particular order/item or need to display all product details. If both importing tables it_key_tab and it_navigation_path are empty then retrieve all products using BAPI_EPM_PRODUCT_GET_LIST and populate records to et_entityset.

    DATA: lw_product        TYPE bapi_epm_product_id,
          lw_header_product TYPE bapi_epm_product_header,
          lw_header_so      TYPE bapi_epm_so_header,
          lw_orderno        TYPE snwd_so_id,
          lw_itemno         TYPE snwd_so_item_pos,
          lw_so_id          TYPE bapi_epm_so_id.
    DATA: lt_itemso   TYPE STANDARD TABLE OF bapi_epm_so_item,
          lt_products TYPE STANDARD TABLE OF bapi_epm_product_header.
    IF it_key_tab[] IS INITIAL AND
       it_navigation_path[] IS INITIAL.
      CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_LIST'
        TABLES
          headerdata = lt_products.
      MOVE-CORRESPONDING lt_products TO et_entityset.
    ELSE.

In the else condition, check if URL has navigation property: SalesOrderItem, then get sales order and item number from it_navigation_path-key_tab OR else use it_key_tab importing parameter.

* We can use below read(commented out) instead of conventional read statement, but if no navigation *property with
* SalesOrderItem is used in URL, below read will raise an exception: cx_sy_itab_line_not_found.
* With ABAP 7.5, we can use clause OPTIONAL along with below read so that it will not raise an
* exception and it would also set syst-subrc = 0. My current abap system is 7.4
*        DATA(it_key_tab_tmp) = it_navigation_path[ nav_prop = 'SalesOrderItem' ]-key_tab.
      READ TABLE it_navigation_path INTO DATA(w_navigation_path) WITH KEY nav_prop = 'SalesOrderItem'.
      IF syst-subrc = 0.
        DATA(it_key_tab_tmp) =  w_navigation_path-key_tab.
      ELSEIF it_key_tab IS NOT INITIAL.
        it_key_tab_tmp = it_key_tab.
      ENDIF.
      DATA(lw_order_val) = it_key_tab_tmp[ name = 'SoId' ].
      DATA(lw_item_val) = it_key_tab_tmp[ name = 'SoItemPos' ].
      lw_orderno = |{ lw_order_val-value ALPHA = IN }|.
      lw_itemno  = |{ lw_item_val-value ALPHA = IN }|.
      lw_so_id-so_id  = lw_orderno.
      CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
        EXPORTING
          so_id      = lw_so_id
        IMPORTING
          headerdata = lw_header_so
        TABLES
          itemdata   = lt_itemso.

      lw_product = lt_itemso[ so_id = lw_orderno so_item_pos = lw_itemno ]-product_id.

      CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'
        EXPORTING
          product_id = lw_product
        IMPORTING
          headerdata = lw_header_product.
      APPEND lw_header_product TO et_entityset.
    ENDIF.

In the above code using sales order number and item number, getting corresponding product number. Use BAPI: BAPI_EPM_PRODUCT_GET_DETAIL to retrieve product details for particular product and populate et_entityset.

Next we will redefine and implement method: PRODUCTHEADERDAT_GET_ENTITY to get product details related to particular product using below URL

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/ProductHeaderdataSet(‘HT-1000’)

Copy below code:

    DATA: lw_product        TYPE bapi_epm_product_id,
          lw_header_product TYPE bapi_epm_product_header.
    lw_product-product_id =  it_key_tab[ name = 'ProductId' ]-value.
    CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'
      EXPORTING
        product_id = lw_product
      IMPORTING
        headerdata = lw_header_product.
    MOVE-CORRESPONDING lw_header_product TO er_entity.

Hope this article helpful.

Assigned Tags

      3 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Good Article Santhosh. Quite detailed.

      Author's profile photo Shakul Jugran
      Shakul Jugran

      Hello Santhosh,

       

      Thanks for sharing in such great detail. I did learn quite a lot from your blog.

      However, when I was trying to navigate from the SalesOrderHeader->SalesOrderItem->Product using a URI like Soheaderdataset(SoId='500000000')/SalesOrderItem(SoId='500000000',SoItemPos='10')/SalesOrderProduct I was getting run-time error.

      This appears to happen due to the code written in the PRODUCTHEADERDAT_GET_ENTITY( ) where it is expected it gets a product based on service request. However, for the scenario stated above, it would never have a product and we should deduce the product from the information specified in the navigation path.

      So I re-used the code that you provided in the GET_ENTITYSET() and it worked perfectly.

      DATA: lw_header_so      TYPE bapi_epm_so_header,
                lw_orderno        TYPE snwd_so_id,
                lw_itemno         TYPE snwd_so_item_pos,
                lw_so_id          TYPE bapi_epm_so_id,
                lw_product        TYPE bapi_epm_product_id,
                lw_header_product TYPE bapi_epm_product_header,
                lt_itemso         TYPE STANDARD TABLE OF bapi_epm_so_item.
      
          IF it_navigation_path[] IS INITIAL.
            lw_product-product_id =  it_key_tab[ name = 'ProductId' ]-value.
          ELSE.
      * We can use below read(commented out) instead of conventional read statement, but if no navigation *property with
      * SalesOrderItem is used in URL, below read will raise an exception: cx_sy_itab_line_not_found.
      * With ABAP 7.5, we can use clause OPTIONAL along with below read so that it will not raise an
      * exception and it would also set syst-subrc = 0. My current abap system is 7.4
      *        DATA(it_key_tab_tmp) = it_navigation_path[ nav_prop = 'SalesOrderItem' ]-key_tab.
            READ TABLE it_navigation_path INTO DATA(w_navigation_path) WITH KEY nav_prop = 'SalesOrderItemAssoc'.
            IF syst-subrc = 0.
              DATA(it_key_tab_tmp) =  w_navigation_path-key_tab.
            ELSEIF it_key_tab IS NOT INITIAL.
              it_key_tab_tmp = it_key_tab.
            ENDIF.
            DATA(lw_order_val) = it_key_tab_tmp[ name = 'SoId' ].
            DATA(lw_item_val) = it_key_tab_tmp[ name = 'SoItemPos' ].
            lw_orderno = |{ lw_order_val-value ALPHA = IN }|.
            lw_itemno  = |{ lw_item_val-value ALPHA = IN }|.
            lw_so_id-so_id  = lw_orderno.
            CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
              EXPORTING
                so_id      = lw_so_id
              IMPORTING
                headerdata = lw_header_so
              TABLES
                itemdata   = lt_itemso.
      
            lw_product = lt_itemso[ so_id = lw_orderno so_item_pos = lw_itemno ]-product_id.
          ENDIF.
      
      * Fetch the Product Details
          CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'
            EXPORTING
              product_id = lw_product
            IMPORTING
              headerdata = lw_header_product.
          MOVE-CORRESPONDING lw_header_product TO er_entity.

       

      Best Regards,

      Shakul.

      Author's profile photo Former Member
      Former Member

      Nice article Santhosh.

      ??

      Thanks for posting it.