Skip to Content

Updates

  • 02.06.2016 Added link to blog that describes how to implement updates
  • 09.06.2016 Added section how to create the CDS view used for generating an OData service via Referenced Data Sources
  • 26.04.2017 Replaced screen shots that now show the latest version of ADT tools

 

Introduction

 

In a previous blog OData service development with SAP Gateway – code-based service development – Part I I described how to implement as basic OData Service using code based implementation.

Since the recommended development approach as of SAP NetWeaver 750 is to use CDS views I would like to show how a service having the same capabilities as the service in the blog mentioned above can be generated based on CDS views.

 

It will turn out that from a development perspective in the ABAP stack this is much less effort if appropriate CDS views are in place and if your backend system is based on SAP NetWeaver 750.

 

If however no appropriate CDS view is in place instead of having the effort of developing an OData service via code based implementation you would have the effort to develop the appropriate DDL source code.

 

Prerequisites

As a prerequisite we are using a consumption view ZSEPM_C_SALESORDER_<#> since it is not good practice to access core CDS views directly.

This consumption view is then also not annotated as an analytical view as the underlying core CDS view SEPM_I_SALESORDER_E.

How to create this consumption view ZSEPM_C_SALESORDER_<#> is shown in the following.

 

If you are using a system for a SAP CodeJam you can skip this step or create your own CDS view by replacing the hash tag <#> with your group number.

 


Creating the consumption view

 

Please note:

The creation of a consumption view is only necessary if you want to follow this how-to-guide on your own system or if you want to use your own CDS view.

For a SAP CodeJam or other SAP event we will use a system where this view has already been created.

 

 

 

  • Start the ABAP Development Tools

 

From the menu choose New –> ABAP Project

Select the system (here A4H) from the list of system connections and choose Next02 Select A4H from the list of system connections.png

  • In the “Connection Settings” screen press Next
    System Connection Details.png
  • Enter your credentials
    04 logon to system.png
  • Right click on your system connection and select New –> Other …

Select Data Definition and press Next

  • Enter the following details for the new DDL source and press Finish

    Package: $TMP
    Name: ZSEPM_C_SALESORDER_<#>
    Description: SalesOrders – EPM Demo Data

Cut and paste the source code and press the Activate button. This will create the CDS view that we will use in this how to guide

You will have to adapt the name of the SQL view, the name of the CDS view and you should maintain the annotation for @EndUserText.label so that it contains your group number.

 

 

DDL source code

@AbapCatalog.sqlViewName: 'ZSEPM_ISOE_<#>'

@AbapCatalog.compiler.compareFilter: true

@AccessControl.authorizationCheck: #CHECK

@EndUserText.label: 'SalesOrders – EPM Demo Data'

define view Zsepm_C_Salesorder_<#>
  as select from SEPM_I_SalesOrder_E
{

      @ObjectModel.text.association: '_Text'

  key SalesOrder,

      @ObjectModel.readOnly: true

      CreationDateTime,

      @ObjectModel.readOnly: true

      LastChangedDateTime,

      @ObjectModel.readOnly: true

      IsCreatedByBusinessPartner,

      @ObjectModel.readOnly: true

      IsLastChangedByBusinessPartner,

      @ObjectModel.readOnly: true

      Customer,

      @ObjectModel.readOnly: true

      TransactionCurrency,

      @ObjectModel.readOnly: true

      GrossAmountInTransacCurrency,

      @ObjectModel.readOnly: true

      NetAmountInTransactionCurrency,

      @ObjectModel.readOnly: true

      TaxAmountInTransactionCurrency,

      @ObjectModel.readOnly: true

      SalesOrderLifeCycleStatus,

      @ObjectModel.readOnly: true

      SalesOrderBillingStatus,

      @ObjectModel.readOnly: true

      SalesOrderDeliveryStatus,

      @ObjectModel.readOnly: true

      SalesOrderOverallStatus,

      @ObjectModel.readOnly: true

      Opportunity,

      /* Associations */

      _Customer,

      _Item,

      _Text


} 
  
 

 

 

 

 

Generating an OData service via Referenced Data Source

 

  • We first have to start with creating a new Service Builder project called ZE2E100_<#>_2 since the project in the previous blog mentioned above was called ZE2E100_<#>

    Note:
    Replace <#> with your group number
  • Right click on the folder Data Model.Choose Reference –> Modeled Data Source Reference from the context menu

/wp-content/uploads/2016/06/image082_966055.png

  • The Reference Data Source Wizard opens.

On the first screen choose:
CDS Core Data Services for the field Modeled Data Source Type.
ZSEPM_C_SALESORDER_<#> for the field Modeled Data Source Name

/wp-content/uploads/2016/06/rds01_966286.jpg

  • In the second window of the wizard select the following associations’_CUSTOMER and _ITEM.Please note that the cds views SEPM_I_BUSINESSPATNER_E and SEPM_I_SALESORDERITEM_E are automatically selected as well.Press Finish

/wp-content/uploads/2016/06/rds02_966356.jpg

 

  • Press the Generate Runtime Objects button

 

  • In the Model and Service Definition screen leave the default values unchanged and press Continue.

/wp-content/uploads/2016/06/image086_966097.png

  • In the Create Object Directory Entry dialogue press Local Object or enter $TMP.

 

  • Expand the folder Service Maintenance right click on the entry GW_HUB and choose Register.

/wp-content/uploads/2016/06/image088_966104.png

  • In the Add Service dialogue enter $TMP for the package assignment or press Local Object.

/wp-content/uploads/2016/06/image089_966105.png

  • Expand the folder Service Maintenance right click on the entry GW_HUB and choose SAP Gateway Client.Press <Execute>

/wp-content/uploads/2016/06/image090_966106.png

  • Check the Service Document

    It shall contain three entity sets:
    1. Zsepm_C_Salesorder_<#>
    2. SEPM_I_SalesOrderItem_E
    3. SEPM_I_BusinessPartner_E

/wp-content/uploads/2016/06/rds04_966357.jpg

  • Check the metadata document by selecting <Add URI option> and select $metadata

Check the Metadata Document
Please note that the entity type ZSEPM_I_SalesOrder_EType contains:

    • Two navigation properties to_Customer and to_Item
    • A property SalesOrder_Text that has been generated by the SADL framework based on the annotation @ObjectModel.text.association: ‘_Text’.

      Please note this property is annotated as sap:updatable=”false”;

 

/wp-content/uploads/2016/06/rds06_966109.jpg

  • Now we can test the service and we will see that it supports various query options, $expand and selecting single entities out of the box.

    To do so execute the following URI’s in the SAP Gateway Client (transaction /n/IWFND/GW_CLIENT):
  • /sap/opu/odata/SAP/ZE2E100__2_SRV/Zsepm_C_Salesorder_?$filter=GrossAmountInTransacCurrency ge 100000&$select=SalesOrder,GrossAmountInTransacCurrency,TransactionCurrency&$format=json

 

    • $skip and $top together with $inlinecount work out of the box as well

      /sap/opu/odata/SAP/ZE2E100__2_SRV/Zsepm_C_Salesorder_?$filter=GrossAmountInTransacCurrency ge 100000&$select=SalesOrder,GrossAmountInTransacCurrency,TransactionCurrency&$top=2&$skip=1&$inlinecount=allpages&$format=json

 

Please note that via &$inlinecount=allpages we retrieve the number of entries that would be returned without using $skip and $top

/wp-content/uploads/2016/06/rds07_966358.jpg

    • Read a single entity from the list of the sales order/sap/opu/odata/SAP/ZE2E100_<#>_2_SRV/Zsepm_C_Salesorder_<#>(‘5000000<X>‘)?$format=jsonPlease note that the weird looking key stems from the fact that the CDS view is annotated as an analytical view.
    • Read the list of items of a single sales order via the navigation property to_Item /sap/opu/odata/SAP/ZE2E100_<#>_2_SRV/Zsepm_C_Salesorder_<#>(‘5000000<#>‘)/to_Item?$format=json

 

 

 

How to implement updates in a service that has been generated using referenced data sources will be described in the following blog

 

OData service development with SAP Gateway using CDS via Referenced Data Sources – How to implement updates

 

 

To report this post you need to login first.

12 Comments

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

  1. Jibin Joy

    Hi Andre ,

      Thanks for Information !!!

    Please share ur valuable suggestion .

        Consider 2 User can access this Sales Order oData Service “User1” and “User2” . Now “User1” can view Sales Order of particular set of Customer like “C1” and “C2” and “User2” can view other set of Customer like “C3” and “C4”.

      how can we achieve like this Authorization !!!

    Regards,

    Jibin Joy

    (0) 
  2. Eugen Soydas

    Hi Andre,

    e.g. i decide to use Gateway with the deployment option “Hub architecture with development on the server” as you described here. Will the automatic generation via an ABAP CDS View by using the annotation @OData.publish work?

    I’m asking because i’m not sure whether the description here with “..SAP Gateway hub system. …” considers both possible Hub deployment options.

    BR
    Eugen

    (0) 
  3. Mynyna Chau

    Hi Andre, thanks for your blog post.

    I got notified that comments weren’t possible, so I wanted to check back and see if it is possible to post comments now, since there might be people who would like to give additional feedback or get questions on your blog cleared. Have a nice day!

    Regards, Mynyna

    (0) 
  4. Gregor Wolf

    Hi André,

    I’ve followed the described steps in a SAP NetWeaver ABAP 7.50 SP5 system and can query the service. But I want to make the service subscribable. For this I’ve followed the steps described in Configurations on the SAP Backend System for Push Oriented Scenarios. In the MPC class I’ve used the following coding to set the subscribable flag for the Entity Set:

    CLASS ZCL_ZSEPM_C_SALESORDER_MPC_EXT IMPLEMENTATION.
    
      METHOD define.
        super->define( ).
        DATA(lo_so) = model->get_entity_set( 'Zsepm_C_Salesorder_Tpl' ).
        lo_so->set_subscribable( ).
      ENDMETHOD.
    
    ENDCLASS.

    When I now call the Service via the URL “/sap/opu/odata/sap/ZSEPM_C_SALESORDER_SRV/?$format=json” I get the following result:

    {
      "d" : {
        "EntitySets" : [
          "SEPM_I_SalesOrderItem_E",
          "Zsepm_C_Salesorder_Tpl",
          "SEPM_I_BusinessPartner_E"
        ]
      }
    }

    So you see that the Entities that should be returned for a subscribable service:

    NotificationCollection
    SubscriptionCollection

    do not exist. Hope you can give some guidance.

    Best regards
    Gregor

    (0) 
    1. Andre Fischer Post author

       

      Hi Gregor,

      you have to enter a few more lines of coding that I have copied from the define method of /IWBEP/CL_MGW_PUSH_ABS_MODEL.

      Reason is that the DEFINE method of the generated MPC class

        method DEFINE.
      *&---------------------------------------------------------------------*
      *&           Generated code for the MODEL PROVIDER BASE CLASS         &*
      *&                                                                     &*
      *&  !!!NEVER MODIFY THIS CLASS. IN CASE YOU WANT TO CHANGE THE MODEL  &*
      *&        DO THIS IN THE MODEL PROVIDER SUBCLASS!!!                   &*
      *&                                                                     &*
      *&---------------------------------------------------------------------*
      
      model->set_schema_namespace( 'ZE2E100_XX_2_SRV' ).
      
      define_rds_4( ).
      get_last_modified_rds_4( ).
        endmethod.

      does not call the DEFINE method of the Superclass.

      So if your DEFINE method looks like this the two entity sets

      NotificationCollection

      SubscriptionCollection

      will show up and can be called since the CRUD methods are handled by the framework for example by calling /IWBEP/IF_MGW_CORE_SRV_RUNTIME~READ_ENTITYSET in the base class /IWBEP/CL_MGW_PUSH_ABS_DATA.

      Please note that the methods of the interface /IWBEP/IF_MGW_CORE_SRV_RUNTIME must not be redefined since they belong to the framework.

      Only methods of the application interface /IWBEP/IF_MGW_APPL_SRV_RUNTIME may be redefined

        super->define( ).
      
             DATA(lo_so) = model->get_entity_set( 'Zsepm_C_Salesorder_Tpl' ).
          lo_so->set_subscribable( ).
      
      
      
         DATA:
              lo_data_object  TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
              lo_do_conv      TYPE REF TO /iwbep/if_mgw_odata_etype_conv,
              lo_property     TYPE REF TO /iwbep/if_mgw_odata_property.
      
      
        lo_data_object = model->create_entity_type( 'Subscription' ). "#EC NOTEXT
        lo_data_object->set_creatable( abap_true ).
        lo_data_object->set_updatable( abap_true ).
        lo_data_object->set_deletable( abap_true ).
      
        " todo consumer type + connectivity server
        lo_do_conv ?=  lo_data_object.
      
        lo_do_conv->create_properties(
           iv_comma_sep_abap_names =
            'ID,USERNAME,CHANGED_TIMESTMP,TITLE,DELIVERY_ADDRESS,PERSIST_NOTIF,COLLECTION_NAME,FILTER_PARAMETER,SELECT_PARAMETER,CHANGE_TYPE'
           iv_comma_sep_ext_names =
            'ID,user,updated,title,deliveryAddress,persistNotifications,collection,filter,select,changeType'
        ).
      
        lo_data_object->bind_structure( '/IWBEP/S_MGW_SUB_DATA' ).
        lo_property =  lo_data_object->get_property( 'ID' ).
        lo_property->set_is_key( ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_updatable( abap_false ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'updated' ).
        lo_property->set_as_updated( abap_false ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'user' ).
        lo_property->set_as_author( abap_false ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'title' ).
        lo_property->set_as_title( abap_false ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'deliveryAddress' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'persistNotifications' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'collection' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'filter' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'select' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'changeType' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
      
      *------set semantics
        lo_data_object->set_semantic( 'subscriptions' ).
      
      
      * ------Notification----------------------------------------------------------------------
        lo_data_object = model->create_entity_type( 'Notification' ). "#EC NOTEXT
        lo_data_object->set_creatable( abap_false ).
        lo_data_object->set_updatable( abap_false ).
        lo_data_object->set_deletable( abap_false ).
        lo_data_object->set_addressable( abap_false ).
      
        lo_do_conv ?=  lo_data_object.
      
        lo_do_conv->create_properties(
           iv_comma_sep_abap_names =
            'ID,COLLECTION,TEXT,UPDATED,OPERATION_TYPE,ENTRIES_OF_INTEREST'
           iv_comma_sep_ext_names =
            'ID,collection,title,updated,changeType,entriesOfInterest'
        ).
        lo_data_object->bind_structure( '/IWBEP/S_MGW_NOTIF_DATA' ).
      
        lo_property = lo_data_object->create_property( 'recipient' ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_updatable( abap_false ).
        lo_property->bind_data_element( '/IWBEP/MGW_NOTIF_RECIPIENT' ).
      
        lo_property =  lo_data_object->get_property( 'ID' ).
        lo_property->set_is_key( ).
      
        lo_property = lo_data_object->get_property( 'title' ).
        lo_property->set_as_title( abap_false ).
      
        lo_property = lo_data_object->get_property( 'updated' ).
        lo_property->set_as_updated( abap_false ).
      
      *-set semantics
        lo_data_object->set_semantic( 'notifications' ).
      
      
      (0) 
      1. Gregor Wolf

        Hi André,

        I’ve just tested your suggestion in my system and yes it solves my issue. Thank you!

        But is there a valid reason why the define method does not call it’s super class?

        Best regards
        Gregor

        (0) 
      2. Gregor Wolf

        Hi André,

        unfortunately I’ve never tried the URI that I get back from the Path:

        /sap/opu/odata/iwfnd/NOTIFICATIONSTORE/NotificationCollection

        The Path part of the URI looks like that:

        /sap/opu/odata/sap/ZMATERIALS_SRV;mo/ZDDL_MATERIALS(SAP__Origin=’LOCAL’,MaterialNumber=’000000000000004711′)

        When I call this URL the following error is returned:

        {
          "code": "CX_SADL_VIEW_ELEMENT_UNDEFINED",
          "message": "'SAP__ORIGIN' is not a view element",
          "propertyref": "",
          "severity": "error",
          "target": ""
        },
        {
          "code": "CX_SADL_GW_PROPERTY_NOT_MAPPED",
          "message": "Property 'SAP__ORIGIN' is not mapped to a SADL data source and must be handled programmatically",
          "propertyref": "",
          "severity": "error",
          "target": ""
        }

        What changes do I have to add to the coding so that this issue does not occur?

        When I use the Path:

        /sap/opu/odata/sap/ZMATERIALS_SRV/ZDDL_MATERIALS(‘000000000000004711’)

        everything is just fine.

        Best regards
        Gregor

        (0) 
        1. Andre Fischer Post author

          you could use the following URL:

          /sap/opu/odata/sap/ZMATERIALS_SRV;o=LOCAL/ZDDL_MATERIALS(‘000000000000004711’)

          So adding the name of the system alias of your backend after the root URL of your service.

          Best Regards,

          Andre

           

           

          (0) 
          1. Gregor Wolf

            Hi André,

            but shouldn’t the URI in the Notification be correctly generated or evaluated by SAP Gateway? I think it’s the best to raise an incident in the service marketplace anyway. What do you think?

            Best regards
            Gregor

            (0) 
            1. Andre Fischer Post author

              Hi Gregor,

              you are right. This seems to be a bug.

              In my Sandbox System however it works to run the following requests:
              /sap/opu/odata/sap/ZE2E100_XX_2_SRV;mo/Zsepm_C_Salesorder_Tpl(SAP__Origin=’LOCAL2′,SalesOrder=’500000000′)

              /sap/opu/odata/sap/ZE2E100_XX_2_SRV;mo/Zsepm_C_Salesorder_Tpl(SAP__Origin=’LOCAL’,SalesOrder=’500000000′)

              after having created a second system alias LOCAL2.

              (0) 

Leave a Reply