Skip to Content
|

Category Archives: Uncategorized

Diesen Beitrag gibt es auch auf Deutsch.

On May 25, 2018, the General Data Protection Regulation (GDPR) 2016/679 will become effective. It introduces new directives regulating the processing of personal data by private companies and public authorities throughout the European Union. This makes it necessary to adjust the terms of use for the SAP ONE Support Launchpad and the SAP Support Portal.

To give you enough lead time to familiarize yourself with the new terms and conditions, starting January 15, 2018, a pop-up in the launchpad will periodically alert you. You then have two options:

  • You can immediately accept the new conditions. The pop-up will then no longer be displayed.
  • You can request to be reminded again two weeks later. In this case, if you have accepted the current version of the terms of use, you can simply continue to work as usual. Acceptance of the new terms and conditions will become mandatory only as of May 25, 2018.

Newly generated S-users who enter the launchpad for the very first time after January 15, 2018, will be prompted to accept the new terms of use.

When you start a new eCommerce business it needs to be built on a strong foundation. If you don’t have this in place it doesn’t matter how good you are at connecting with customers and driving traffic to your website. Luckily, there are lots of great eCommerce platforms on the market.

On the other hand, there are many substandard options out there too. If you want to choose the right one you’ll need to know exactly what you’re looking for. We’re going to look at a couple of big categories you should focus on.

1. You Should Be Able To Afford It

Remember it takes a while before you’ll see any returns once you’ve started a new business. You should never run into any difficulties trying to pay your monthly fee. You’ll have too many other things to worry about.

If you look hard enough you can always find good coupons for software. Just make sure you don’t opt for anything too cheap. You don’t want to make any huge sacrifices to save a little extra per month.

2. Performance Levels Should Be High

When you’re looking at the performance levels of different eCommerce platforms concentrate on the crucial things. Speed is (a big one<https://blog.kissmetrics.com/speed-is-a-killer/>) because your conversion rates will always be higher if your website is faster.

Search engine optimization is a massive one too. If your articles and product pages rank in the search engines you’ll get more traffic. Lastly, your site needs to work well on mobile devices in this day and age.

3. Hopefully It Has Lots Of Features

It’s always nice to choose a platform with lots of features. You won’t have to pay a developer every time you want it to do something. Sometimes you’ll need to pay extra for special ones, but the basics should always be free.

You’ll want to start a nice blog to generate more traffic. If you can send cart abandonment emails you’ll increase your conversion rates. Even features like the ability to zoom into product images is worthwhile.

4. You’ll Need The Ability To Scale Up

You won’t be running a small business forever. There will come a time when you’ll need to scale up. When that time comes you’ll want to keep using the same platform. It will be a tremendous hassle switching over to something else.

Will you be able to use multiple payment processors instead of sticking with PayPal? Will you be able to work with multiple companies if you run a drop shipping store? When making your decision plan for the future too.

5. It’s Pointless If You Can’t Master It

Do you know how to write computer code? If not, it’s probably not a good idea to choose a platform that will require you to code. Some options out there will be almost impossible to use unless you’re tech savvy.

Even if you do know your way around computers it’s nice to use the easiest platform that suits your needs. It will give you less to think about, which means you’ll have more time to concentrate on growing your business.

You’ll Be More Likely To Succeed

Building a success eCommerce store isn’t easy, so you need all the help you can get. Choosing the right platform if the first place to start. You’ll have work to do because there are lots of options available.

Dear Community,

It’s my pleasure to announce the release of Phase 7 of SAP BI Pattern Books project – a pattern book on SAP Lumira 2.0. 

Key highlights of the book:

  • This phase of the Pattern Book focuses two main aspects around adopting SAP Lumira 2.0 release – – updating the server add-ons and converting documents from 1.x to 2.0 release
  • The step-by-step workflows not only explains the important things on updating server add-ons and document conversion but also discusses the known challenges, guidelines and best practices
  • The relevant SAP Notes and Blog Posts are also documented in this pattern book

Here are the links to the SAP BI Pattern Books;

Link to the NEW Pattern Books Home Page

Link to the PB on SAP Lumira 2.0

Please help me spread the news.

Best,

Sathish

@srajagpl

You know your business’s software choices affect your budget, your productivity, and sometimes even the range of what you can accomplish, but you may not know it can also affect your employees’ levels of stress. Though not an often considered variable, it could be an important factor—and metric—to dictate your eventual success.

Why Does It Matter?

Functionality and price may still be your primary concerns, but if you can reduce stress along the way, you can:

  • Improve engagement and productivity. More than half of workers in the UK and US who experienced high stress also reported feeling disengaged. Disengagement drives productivity down, so reducing stress levels could help drive productivity back up.
  • Improve sleep and mental health. Stress is a leading cause of insomnia, so reducing stress with the right software can help your employees sleep better. It can also reduce their chances of developing anxiety, depression, and other mental health problems.
  • Reduce sick days. Lower stress could even lead to better physical health, which could then lead to higher attendance.

How Software Affects Stress

Clearly, decreasing stress levels is a good thing. But how can your choice in software affect stress so much?

  • Ease of use. If the system is difficult to learn or hard to use, it may cause employees more stress than it eliminates. Struggling with a non-responsive system, or needing to learn counterintuitive processes to do something that was easy before can be frustrating, and lead to increased stress.
  • Software that enables seamless, ongoing communication can make employees feel less stressed too. They should be able to ask questions, find answers, and engage with others without issues—if they feel isolated or confused, it could make things worse.
  • Simplification or automation. Any piece of software that actively makes a task easier (or eliminates one entirely) should cause a reduction in stress. The trick is, the work needs to be simplified or automated without the creation of new tasks or more difficult processes.
  • Studies show that workplaces with high rates of transparency tend to have higher employee engagement (and lower employee stress), which makes sense; most people like to know what’s going on at any given point. An open system, which provides lots of information, should reduce workplace stress overall.
  • How practical is it to implement this new software? If it requires a major change, such as switching over to a new operating system or abandoning previous software, it could cause more stress than it saves.

Key Variables to Consider

If stress is a high priority for your business to manage, make sure you consider these variables when choosing a new piece of software:

  • Primary uses. How is this software going to be used? What problems is it going to solve? What problems is it going to introduce?
  • User interface. How easy is it to learn this software from scratch? Is it easy to use on a regular basis? Does it save time?
  • Can this software work for multiple people, and can it be expanded upon even further in the future?
  • How does this software work with your existing software products? Is it compatible with all devices and operating systems?

If you’re interested in choosing new software that can make your employees’ lives easier, or if you want a piece of software that can directly measure your employees’ stress, job satisfaction, and output, consider investing in SAP’s workforce planning and HR analytics software. It could be one of the most important investments you make.

How to Sell Services in Sales Order:

 

Services contribute to the significant potential of company’s overall success and profit. For this reason, product-related services are sold together with their products.

 

The three main categories of services are

  1. Time and Material
  2. Fixed Price
  3. Fixed Price without actuals

Time and Material in Sales Order (SECO)

 

When you want use the Item Type –Time and Material in the Sales Order, then you must ensure that the relevant question mentioned below is scoped in the Business Configuration.

You can navigate to the Question by going to Business Configuration=> Select the respective Implementation Project and click on the Edit Project Scope button. Navigate to Step 4 Questions. Select the Path: Sales => Product and Service Portfolio for Sales=> Sell Services. Check the Group: Service Types for Sales with Tracking Actuals and enable the question:

Do you sell services for a price based on the actual time and material needed?

 

Process Flow of Time and Material in Sales Order

There are two options to record time for a Service with item type Time and Material, depending on your scoping settings.

  1. You can record the time by creating a Service Confirmation manually. When you add a Service of the item type time and in the Sales Order, release it to Service Execution and when you then click Confirm Service Execution, the Service Confirmation will be created immediately. You can then release the Service Confirmation with or without Order Completion
  2. You can record the time via the time recording in the time sheet of the service performer.

If you want to record the time for the performed Service via the time sheet of the performer you have to enable the Question: Do you want to record time for sales orders?

In the Business Configuration=> Select the respective Implementation Project and click on the Edit Project Scope button. Navigate to Step 4 Questions. Select the Path: Sales=> Selling Products and Services=> Sales Order=> Check for the Group=> Sales Order Processing with Time Recording

 

In this case the Service Confirmation is automatically created when the employee records his/her time for the respective Sales Order Item. The Service Performer can record the time by going to the Home work center=>Self Services overview=>Time=>Edit my Time Sheet. He/She can enter the time and upon the “Release” action the Service Confirmation is automatically created by the system.

 

When the time recording is scoped in the system and you try to create a Service Confirmation via the action “Confirm Service Execution” in the Sales Order manually, the system will display the following warning message and the Service is not automatically populated in the Service Confirmation. The system also does not allow to release the Service Confirmation if this is the only Service in the Sales Order for which the Service Confirmation is created since no item is present.

 

Invoicing Process:

The invoicing for the item type Time and Material will be done via the Service Confirmation Invoice Request and the invoicing of the confirmed quantity is derived from the Service Confirmation.

Cost Accounting Process:

  • determination of cost of service rate
  • actual time needed for service execution derived from service confirmation document (duration)
  • cost of services assigned to order after confirmation

Fixed Price in Sales Order (SERV)

 

To use item type Fixed Price, the following question needs to be in scope in the Business Configuration:

Business Configuration=> Select the respective Implementation Project and click on the Edit Project Scope button. Navigate to Step 4 Questions. Select the Path: Sales=> Product and Service Portfolio for Sales=> Sell Services and check the Group: Service Types for Sales with Tracking Actuals

Question: Do you sell or provide services for a fixed price, but track the actual time and materials needed for reporting purposes?

 

The Process flow is similar than for Time and Material. The Time Recording can also be done for the Item Type Fixed Price. The Service Confirmation is mandatory for Item Type Fixed Price.

Invoicing Process:

The invoicing is triggered via the Service Confirmation but the invoicing is done via the respective Sales Order Invoice Request and the invoiced quantity is derived from the Sales Order.

Cost Accounting Process:

  • determination of cost of service rate
  • actual time needed for service execution derived from service confirmation document (duration)
  • cost of services assigned to order after confirmation

 

Fixed Price without Actuals (SEFL)

 

To use item type Fixed Price without Actuals, the following question needs to be in scope in the Business Configuration:

Business Configuration=> Select the respective Implementation Project and click on the Edit Project Scope button. Navigate to Step 4 Questions. Select the Path: Sales=> Product and Service Portfolio for Sales=> Sell Services and check the Group: Service Types for Sales without Tracking Actuals

Questions: Do you sell or provide services for a fixed price, but do not track the actual time and materials needed?

In this case the Service is confirmed directly in the Sales Order. No Service Confirmation is needed.

Invoicing Process:

The invoicing is triggered when the service execution is completed in the Sales Order, the invoicing is done via the respective Sales Order Invoice Request and the invoiced quantity is derived from the Sales Order.

Cost Accounting Process:

  • determination of cost of service rate
  • actual time needed for service execution derived from service confirmation document (duration)
  • cost of services assigned to order after confirmation

Below is the table for the service confirmation and Invoicing reference with respect to each Item Type.

Item type Service Confirmation Invoicing reference
Time & Material Mandatory Service Confirmation
Fixed price Mandatory Sales Order
Fixed price without actuals Not Mandatory Sales Order

 

Regards,

Shubha

 

Introduction

In this first blog about OData V4 code based implementation I want to show how to build a simple service that shows sales order header data alongside with its items.

The service implementation will leverage two CDS consumption views ZE2E001_C_SalesOrder and ZE2E001_C_SalesOrderItem that read their data from the CDS interface views SEPM_I_SalesOrder_E and SEPM_I_SalesOrderItem_E respectively.

Please note:

By leveraging CDS views you will be able to reuse the data modelling part to a large extent in the new ABAP programming model once this supports OData V4 even if you are currently using AS ABAP 750. In addition your service will support most query options out of the box by using the code samples shown in this blog.

In addition to the data provider class and the model provider class which are mandatory for an OData V4 service we will in create three additional repository objects for convenience (an interface) and as a best practice (an error and an message class).

The interface is used to define types and constants that are used in both, the data provider class as well as in the model provider class. Examples are the types of the two CDS consumption views mentioned above or the ABAP internal and external names of our entity types, entity sets, navigation property, etc.

 

How to test the service

This sample service has been implemented for your convenience in the new demo system ES5. System details can be found in my following blog.New SAP Gateway Demo System available.

The $metadata document of the service can than be called via the following URL

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/$metadata?sap-statistics=true

Using the implementation of the basic interface methods our service already supports the following requests:

Read a single sales order

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder(‘500000000’)?sap-ds-debug=true

Get the first three sales orders

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder?$top=3&sap-ds-debug=true

Navigate from the sales order head to the items and filter the result set for items with a grossamount larger than 1100$.

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder(‘500000000’)/_Item?$filter=Grossamountintransaccurrency ge 1100 and Transactioncurrency eq ‘USD’&sap-ds-debug=true

Read a sales order header and expand the items therby filtering on the expanded items.

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder(‘500000000’)?$expand=_Item($filter=Grossamountintransaccurrency ge 1100 and Transactioncurrency eq ‘USD’)&sap-ds-debug=true

Repository objects

(listed in the order of implementation)

In the end our ABAP project will contain the following 7 repository objects:

 # name role Details:
 1 ZE2E001_C_SalesOrder CDS consumption view – sales order reads data from SEPM_I_SALESORDER_E
 2 ZE2E001_C_SalesOrderItem CDS consumption view – sales order item reads data from SEPM_I_SALESORDERITEM_E
 3 zif_e2e001_odata_v4_so_types Interface  is used by the data provider class as well as the model provider class
4 zcm_e2e001odatav4_so Message class contains 2 messages
5 zcx_e2e001_odata_v4_so Exception class inherits from:
/iwbep/cx_gateway
 6 zcl_e2e001_odata_v4_so_model Model provider class inherits from:
/iwbep/cl_v4_abs_model_prov
 7 zcl_e2e001_odata_v4_so_data Data provider class inherits from:
/iwbep/cl_v4_abs_data_provider

And our project explorer structure in the ABAP Developmen Tools in Eclipse will look like follows:

 

CDS consumption views ZE2E001_C_SalesOrder and ZE2E001_C_SalesOrderItem

We will start to create two CDS consumption views on top of two existing CDS Interface views that are delivered by SAP as part of the EPM demo data model.

@AbapCatalog.sqlViewName: 'ZE2E001CSO'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OData V4 Demo Service - SalesOrder Root'
define view ZE2E001_C_SalesOrder
  as select from SEPM_I_SalesOrder_E

  association [1..*] to ZE2E001_C_SALESORDERITEM as _Item on _Item.SalesOrder = $projection.SalesOrder
  association [0..*] to SEPM_I_SalesOrderText_E  as _Text on $projection.SalesOrder = _Text.SalesOrder


{
      //SEPM_I_SalesOrder_E
  key SalesOrder,
      CreatedByUser,
      CreationDateTime,
      LastChangedByUser,
      LastChangedDateTime,
      IsCreatedByBusinessPartner,
      IsLastChangedByBusinessPartner,
      Customer,
      CustomerContact,
      TransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      GrossAmountInTransacCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      NetAmountInTransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      TaxAmountInTransactionCurrency,
      SalesOrderLifeCycleStatus,
      SalesOrderBillingStatus,
      SalesOrderDeliveryStatus,
      SalesOrderOverallStatus,
      Opportunity,
      SalesOrderPaymentMethod,
      SalesOrderPaymentTerms,
      BillToParty,
      BillToPartyRole,
      ShipToParty,
      ShipToPartyRole,
      /* Associations */
      _Item,
      _Text

}

 

@AbapCatalog.sqlViewName: 'ZE2E001CSOI'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OData V4 Demo Service - SalesOrder Items'
define view Ze2e001_C_Salesorderitem 
 as select from SEPM_I_SalesOrderItem_E
  association [0..*] to SEPM_I_SalesOrderItemText_E as _Text on  $projection.SalesOrder     = _Text.SalesOrder
                                                             and $projection.SalesOrderItem = _Text.SalesOrderItem
{
      //SEPM_I_SalesOrderItem_E
  key SalesOrder,
  key SalesOrderItem,
      Product,
      TransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      GrossAmountInTransacCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      NetAmountInTransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      TaxAmountInTransactionCurrency,
      ProductAvailabilityStatus,
      OpportunityItem,
      /* Associations */
      _Text
} 
 
 

Please note that you have to activate both views together (Ctrl+Shift+F3). Afterwards you can select the sales order view and use (F8) to get a preview of the data.

Interface – zif_e2e001_odata_v4_so_types

In the second step we create an interface zif_e2e001_odata_v4_so_types that will be used to store data types and constants that will be used in both, the model provider and the data provider class.

It can also be used for other service implementations that reside in the same service group and that might want to leverage the information being defined in the interface to implement cross service references.

interface zif_e2e001_odata_v4_so_types
  public .
  types:
    begin of gty_cds_views,
      salesorderitem type ze2e001_c_salesorderitem,
      salesorder     type ze2e001_c_salesorder,
    end of gty_cds_views.

  types: begin of gty_s_so_soi .
      include type gty_cds_views-salesorder.
  types:
    _item type standard table of gty_cds_views-salesorderitem with default key,
    end of gty_s_so_soi .

  types:
    begin of gt_key_range,
      salesorder   type range of gty_cds_views-salesorder-salesorder,
      itemposition type range of gty_cds_views-salesorderitem-salesorderitem,
    end of gt_key_range.

  constants:

    begin of gcs_cds_view_names,
      salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDERITEM',
      salesorder     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDER',
    end of gcs_cds_view_names,

    begin of gcs_entity_type_names,
      begin of internal,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDERITEM',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDER',
      end of internal,
      begin of edm,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrderItemType',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrderType',
      end of edm,
    end of gcs_entity_type_names,

    begin of gcs_entity_set_names,
      begin of internal,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDERITEM',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDER',
      end of internal,
      begin of edm,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrderItem',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrder',
      end of edm,
    end of gcs_entity_set_names ,

    begin of gcs_nav_prop_names,
      begin of internal,
        salesorder_to_items type /iwbep/if_v4_med_element=>ty_e_med_internal_name value '_ITEM',
      end of internal,
      begin of edm,
        salesorder_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.

Message class

The message class contains three messages that are used in the exception class to raise error messages specific to your OData service.

 Message Number Short Text  Self Explanatory
050  Entity &1 not found in entiy set &2  X
051  filter, top or navigation must be used to access entity set &1  X
052  Entity key &1 does not match key in payload  X

The message class will look like follows in ADT.

Exception class

It is a good practice to use your own exception classes. The class inherits from the basic exception class provided by the SAP Gateway framework and contains exceptions that are raised if the following errors occur:

  • the application tries to read header data of a sales order that does not exist
  • the application tries to read a list of sales orders or sales order items without providing any query options to avoid a full table scan

Please note:

SAP Fiori applications for example by default use query options such as $top and $skip. A single read on a sales order will also use the correct key that has been retrieved beforehand.

class zcx_e2e001_odata_v4_so definition
  public
  inheriting from /iwbep/cx_gateway
  final
  create public .

  public section.

    constants:
      begin of entity_not_found,
        msgid type symsgid value 'ZCM_E2E001ODATAV4_SO',
        msgno type symsgno value '050',
        attr1 type scx_attrname value 'ENTITY_KEY',
        attr2 type scx_attrname value 'EDM_ENTITY_SET_NAME',
        attr3 type scx_attrname value '',
        attr4 type scx_attrname value '',
      end of entity_not_found.
    constants:
      begin of use_filter_top_or_nav,
        msgid type symsgid value 'ZCM_E2E001ODATAV4_SO',
        msgno type symsgno value '051',
        attr1 type scx_attrname value 'EDM_ENTITY_SET_NAME',
        attr2 type scx_attrname value '',
        attr3 type scx_attrname value '',
        attr4 type scx_attrname value '',
      end of use_filter_top_or_nav .
    constants:
      begin of entity_keys_do_not_match,
        msgid type symsgid value 'ZCM_E2E001ODATAV4_SO',
        msgno type symsgno value '052',
        attr1 type scx_attrname value 'EDM_ENTITY_KEY',
        attr2 type scx_attrname value '',
        attr3 type scx_attrname value '',
        attr4 type scx_attrname value '',
      end of entity_keys_do_not_match .

    data: entity_set_name     type /iwbep/if_v4_med_element=>ty_e_med_internal_name,
          edm_entity_set_name type /iwbep/if_v4_med_element=>ty_e_med_edm_name,
          user_name           type syuname,
          entity_key          type string read-only.



    methods constructor
      importing
        !textid              like if_t100_message=>t100key optional
        !previous            like previous optional
        !exception_category  type ty_exception_category default gcs_excep_categories-provider
        !http_status_code    type ty_http_status_code default gcs_http_status_codes-sv_internal_server_error
        !is_for_user         type abap_bool default abap_true
        !message_container   type ref to /iwbep/if_v4_message_container optional
        !sap_note_id         type ty_sap_note_id optional
        !edm_entity_set_name type /iwbep/if_v4_med_element=>ty_e_med_edm_name optional
        !entity_set_name     type /iwbep/if_v4_med_element=>ty_e_med_internal_name optional
        !user_name           type syuname optional
        !entity_key          type string optional.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS ZCX_E2E001_ODATA_V4_SO IMPLEMENTATION.


METHOD constructor ##ADT_SUPPRESS_GENERATION.
    CALL METHOD super->constructor
      EXPORTING
        previous           = previous
        exception_category = exception_category
        http_status_code   = http_status_code
        is_for_user        = is_for_user
        message_container  = message_container
        sap_note_id        = sap_note_id.

    me->entity_key = entity_key.
    me->user_name = user_name.
    me->entity_set_name = entity_set_name .
    me->edm_entity_set_name = edm_entity_set_name.

    CLEAR me->textid.
    IF textid IS INITIAL.
      if_t100_message~t100key = if_t100_message=>default_textid.
    ELSE.
      if_t100_message~t100key = textid.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

Model provider class – zcl_e2e001_odata_v4_so_model – coding explained

Now that we have worked on the prerequisites we can finally start to implement our model provider class. As in V2 the model provider class contains a DEFINE method that is amongst others responsible to create the definition of our entity types and entity sets.

The define method will call two entity type specific methods define_salesorder and define_salesorderitem that create the entity types and entity sets for sales order header and items.

  method /iwbep/if_v4_mp_basic~define.
    define_salesorder( io_model ).
    define_salesorderitem( io_model ).
  endmethod.

The most important method call is to call the method create_entity_type_by_struct which works similar to the DDIC import in SEGW in V2.

Please note that we make use of our interface here since the ABAP internal name of the entity type is retrieved via the constant gcs_entity_type_names-internal-salesorder and the DDIC type from the type gty_cds_views-salesorder.

method define_salesorder.
    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-salesorder  . 
          " 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-salesorder
                        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 ).

Here we also set the key field of our entity type.

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

And we create a navigation property in our entity type that points to the sales order items. Also here we use our interface to retrieve the ABAP internal and the external name of the navigation property gcs_nav_prop_names-internal-salesorder_to_items and  gcs_nav_prop_names-edm-salesorder_to_items as well as the ABAP internal name of the target entity type gcs_entity_type_names-internal-salesorderitem.

lo_nav_prop = lo_entity_type->create_navigation_property( gcs_nav_prop_names-internal-salesorder_to_items ).
lo_nav_prop->set_edm_name( gcs_nav_prop_names-edm-salesorder_to_items ).

lo_nav_prop->set_target_entity_type_name( gcs_entity_type_names-internal-salesorderitem ).
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 ).

Model provider class – zcl_e2e001_odata_v4_so_model – Complete source code

The complete code of the model provider classs can be found here:

class zcl_e2e001_odata_v4_so_model definition
  public
  inheriting from /iwbep/cl_v4_abs_model_prov
  final
  create public .

  public section.
    interfaces zif_e2e001_odata_v4_so_types.

    methods /iwbep/if_v4_mp_basic~define redefinition.
  protected section.
  private section.

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

    methods define_salesorder
      importing
        io_model type ref to /iwbep/if_v4_med_model
      raising
        /iwbep/cx_gateway .
    methods define_salesorderitem
      importing
        io_model type ref to /iwbep/if_v4_med_model
      raising
        /iwbep/cx_gateway .
endclass.



class zcl_e2e001_odata_v4_so_model implementation.


  method /iwbep/if_v4_mp_basic~define.
    define_salesorder( io_model ).
    define_salesorderitem( io_model ).
  endmethod.


  method define_salesorder.
    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-salesorder  . " 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-salesorder
                        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-salesorder ).



    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " 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( 'SALESORDER' ).
    lo_primitive_property->set_is_key( ).


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

    lo_nav_prop->set_target_entity_type_name( gcs_entity_type_names-internal-salesorderitem ).
    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-salesorder ).
    lo_entity_set->set_edm_name( gcs_entity_set_names-edm-salesorder ).

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

  endmethod.


  method define_salesorderitem.
    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-salesorderitem  . " 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-salesorderitem
                        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-salesorderitem ).


    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( 'SALESORDER' ).
    lo_primitive_property->set_is_key( ).

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


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

  endmethod.
endclass.

Data Provider class

The data provider class contains the generic implementation of the basic interface methods for read access. These are the following methods.

  • /iwbep/if_v4_dp_basic~read_entity_list
  • /iwbep/if_v4_dp_basic~read_entity
  • /iwbep/if_v4_dp_basic~read_ref_target_key_data_list

that  have to be redefined.

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

  • read_entity_salesorder
  • read_entity_salesorderitem
  • read_list_salesorder
  • read_list_salesorderitem
  • read_ref_key_list_salesorder

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~read_entity_list.

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

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

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 parameters such as $top or $skip.

    " $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.

The implementation /iwbep/if_v4_dp_basic~read_entity_list contains generic coding to retrieve the following query options:

  • $orderby
  • $select
  • $filter
  • $skip
  • $top

for all entity sets, so that they can be passed as parameters to the entity set specific private methods.read_list_salesorder and read_list_salesorderitem,

Please note that the name of the entity set is retrieved via the method io_request->get_entity_set and checked against the constant gcs_entity_set_names-internal-salesorder that has been defined in our interface.

    io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-salesorder.

        read_list_salesorder(
          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 ).

 

read_list_salesorder

The entity type specifc methods read_list_salesorder and read_list_salesorderitem both start with a definition of entity type specific data types that will hold the data being returned and (if being provided) the list of key fields retrieved via navigation.

lt_key_range_salesorder type zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
ls_key_range_salesorder type line of zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
lt_salesorder type standard table of gty_cds_views-salesorder,
lt_key_salesorder  type standard table of gty_cds_views-salesorder.

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

The code also uses our exception class since an exception will be raised if  the client would try to send a Get request   GET…/Salesorder without limiting the result set using either $filter, navigation or $top.

    if  ls_todo_list-process-filter = abap_false
    and ls_todo_list-process-key_data = abap_false
    and iv_top = 0.
      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>use_filter_top_or_nav
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-bad_request
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder.
    endif.

In the end we are able to run a generic OpenSQL statement against our CDS view which supports most query options out of the box.

An exception is for example $skip since offset is only supported in OpenSQL as of AS ABAP 751. If you are using AS ABAP 750  you have to work with first selecting  lv_max_index = iv_top + iv_skip table entries and later delete those from the response that have to be skipped (see complete coding at the end of this section)

Please note that the V4 API uses the method io_response->set_busi_data to return the business data to the SAP Gateway framework.

      "OFFSET is only supported as of NW751
      select (iv_select_string) from ze2e001_c_salesorder
      where (iv_where_clause)
      and   salesorder in @lt_key_range_salesorder
      order by (iv_orderby_string)
      into corresponding fields of table @lt_salesorder
      up to @iv_top rows
      offset @iv_skip.

      io_response->set_busi_data( it_busi_data = lt_salesorder ).

 

/iwbep/if_v4_dp_basic~read_entity

We continue with the method /iwbep/if_v4_dp_basic~read_entity which is for example called if a client accesses a single entity with a Get request, for example
GET ….<service root>/Salesorder(‘50000000’).

This time we first retrieve the name of the entity set that has been accessed using the method io_request->get_entity_set.

    io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-salesorder.
        read_entity_salesorder(
          exporting
            io_request  = io_request
            io_response = io_response ).

read_entiy_salesorder and  read_entiy_salesorderitem

Here we have to retrieve the todo list via the io_request object. This todo list will contain different flags than the todo list used by the read_entity_list method. We can for example retrieve the key from the incoming Get request in the ABAP internal representation. We then can simply read the data via a select single statement from the CDS view. If data has been found it is returned to the framework via the method io_response->set_busi_data. If no table entry is found we raise an error message.using our exception class.

Please note that our coding is very generic so that  it can easily be adapted to the CDS viewyou will use.

 

  method read_entity_salesorder.

    "entity type specific data types
    data: ls_salesorder         type gty_cds_views-salesorder,
          ls_key_salesorder     type gty_cds_views-salesorder,
          lv_salesorder_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_salesorder ).
    ls_done_list-key_data = abap_true.

    select single * from ze2e001_c_salesorder
    into corresponding fields of @ls_salesorder
    where salesorder = @ls_key_salesorder-salesorder.

    if ls_salesorder is not initial.
      io_response->set_busi_data( is_busi_data = ls_salesorder ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_salesorder_key_edm = lv_helper_int = ls_key_salesorder-salesorder.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>entity_not_found
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-not_found
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder
          entity_key          = lv_salesorder_key_edm.

    endif.

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

/iwbep/if_v4_dp_basic~read_ref_target_key_data_list

This method is called by the framework to determine the list of key fields in case navigation is used.

  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-salesorder.
        read_ref_key_list_salesorder(
           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.

Here we check for the source entity type io_request->get_source_entity_type and call an entity type specific method read_ref_key_list_salesorder.

read_ref_key_list_salesorder

This method retrieves the ABAP internal value of the key field of our source entity (here 0500000000), if a requests such as

SalesOrder(‘500000000’)/_Item or

SalesOrder(‘500000000’)?$expand=_Item 

have been called.

Please  note that we are following the naming convention that uses the same name for the navigation property that is used as an association in the CDS view.

After having checked the ABAP internal name of the navigation property (here: _ITEM) that is defined in the interface we retrieve the key fields (salesorder and salesorderitem) from the CDS view ze2e001_c_salesorderitem where the key field salesorder is equal to the source key field (here 0500000000).

The key data is sent back to the framework which provides it to the read_entity_list method.

  method read_ref_key_list_salesorder.

    "entity type specific data types
    data: ls_salesorder_key_data     type  gty_cds_views-salesorder,
          lt_salesorderitem_key_data type standard table of gty_cds_views-salesorderitem,
          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_salesorder_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-salesorder_to_items.

        select salesorder , salesorderitem from ze2e001_c_salesorderitem
        into corresponding fields of table @lt_salesorderitem_key_data
        where salesorder = @ls_salesorder_key_data-salesorder.

        io_response->set_target_key_data( lt_salesorderitem_key_data ).

      when others.

        raise exception type zcx_e2e001_odata_v4_so
          exporting
            http_status_code = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-sv_not_implemented.

    endcase.

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


  endmethod.

Data provider class – zcl_e2e001_odata_v4_so_data – Complete source code

class zcl_e2e001_odata_v4_so_data definition
  public
  inheriting from /iwbep/cl_v4_abs_data_provider
  final
  create public .

  public section.
    interfaces zif_e2e001_odata_v4_so_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 .
    methods /iwbep/if_v4_dp_basic~create_entity redefinition.
    methods /iwbep/if_v4_dp_basic~update_entity redefinition.
    methods /iwbep/if_v4_dp_basic~delete_entity redefinition.

  protected section.
  private section.
    aliases gcs_entity_set_names
       for zif_e2e001_odata_v4_so_types~gcs_entity_set_names .
    aliases gcs_entity_type_names
      for zif_e2e001_odata_v4_so_types~gcs_entity_type_names .
    aliases gty_cds_views
      for zif_e2e001_odata_v4_so_types~gty_cds_views.
    aliases gcs_nav_prop_names
      for zif_e2e001_odata_v4_so_types~gcs_nav_prop_names.

    methods read_list_salesorder
      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_salesorder
      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_salesorder
      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_salesorderitem
      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_list_adv_salesorderitem
*      IMPORTING
*        io_request   TYPE REF TO /iwbep/if_v4_requ_adv_list
*        io_response  TYPE REF TO /iwbep/if_v4_resp_adv_list
*        is_done_list TYPE /iwbep/if_v4_requ_adv_list=>ty_s_todo_process_list
*      RAISING
*        /iwbep/cx_gateway.

    methods read_entity_salesorderitem
      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 create_salesorder
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_create
        io_response type ref to /iwbep/if_v4_resp_basic_create
      raising
        /iwbep/cx_gateway.
    methods create_salesorderitem
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_create
        io_response type ref to /iwbep/if_v4_resp_basic_create
      raising
        /iwbep/cx_gateway.
    methods update_salesorder
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_update
        io_response type ref to /iwbep/if_v4_resp_basic_update
      raising
        /iwbep/cx_gateway.

endclass.



class zcl_e2e001_odata_v4_so_data implementation.

  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.

  method /iwbep/if_v4_dp_basic~delete_entity.

  endmethod.

  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-salesorder.
        read_entity_salesorder(
          exporting
            io_request  = io_request
            io_response = io_response ).

      when gcs_entity_set_names-internal-salesorderitem.
        read_entity_salesorderitem(
          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 ).


    " $orderby was called
    if ls_todo_list-process-orderby = abap_true.
      ls_done_list-orderby = abap_true.
      "** only supported as of 751 or 752
      "get Open SQL Order by Clause
      "io_request->get_osql_orderby_clause( IMPORTING ev_osql_orderby_clause = lv_orderby_string ).
*        CATCH /iwbep/cx_gateway.    "

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

        read_list_salesorder(
          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-salesorderitem.

        read_list_salesorderitem(
          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-salesorder.
        read_ref_key_list_salesorder(
           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 /iwbep/if_v4_dp_basic~update_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.
        update_salesorder(
          exporting
            io_request  = io_request
            io_response = io_response ).

      when others.

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

    endcase.

  endmethod.

  method read_entity_salesorder.

    "entity type specific data types
    data: ls_salesorder         type gty_cds_views-salesorder,
          ls_key_salesorder     type gty_cds_views-salesorder,
          lv_salesorder_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_salesorder ).
    ls_done_list-key_data = abap_true.

    select single * from ze2e001_c_salesorder
    into corresponding fields of @ls_salesorder
    where salesorder = @ls_key_salesorder-salesorder.

    if ls_salesorder is not initial.
      io_response->set_busi_data( is_busi_data = ls_salesorder ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_salesorder_key_edm = lv_helper_int = ls_key_salesorder-salesorder.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>entity_not_found
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-not_found
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder
          entity_key          = lv_salesorder_key_edm.

    endif.

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

  method read_entity_salesorderitem.
    "entity type specific data types
    data: ls_salesorderitem         type gty_cds_views-salesorderitem,
          ls_key_salesorderitem     type gty_cds_views-salesorderitem,
          lv_key_edm_salesorderitem 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_salesorderitem ).
    ls_done_list-key_data = abap_true.

    select single * from ze2e001_c_salesorderitem
    into corresponding fields of @ls_salesorderitem
    where salesorder = @ls_key_salesorderitem-salesorder
    and  salesorderitem = @ls_key_salesorderitem-salesorderitem.

    if ls_salesorderitem is not initial.
      io_response->set_busi_data( is_busi_data = ls_salesorderitem ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_key_edm_salesorderitem = lv_helper_int = ls_key_salesorderitem-salesorder.
      lv_key_edm_salesorderitem = lv_key_edm_salesorderitem && ','.
      lv_helper_int = ls_key_salesorderitem-salesorderitem.
      lv_key_edm_salesorderitem = lv_key_edm_salesorderitem && lv_helper_int.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>entity_not_found
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-not_found
          edm_entity_set_name = gcs_entity_set_names-edm-salesorderitem
          entity_key          = lv_key_edm_salesorderitem.

    endif.

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



  method read_list_salesorder.

    "entity type specific data types
    data : lt_key_range_salesorder type zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
           ls_key_range_salesorder type line of zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
           lt_salesorder           type standard table of gty_cds_views-salesorder,
           lt_key_salesorder       type standard table of gty_cds_views-salesorder.

    "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_salesorder ).
      loop at lt_key_salesorder into data(ls_key_entity).
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-salesorder ) to lt_key_range_salesorder.
      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.
      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>use_filter_top_or_nav
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-bad_request
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder.
    endif.

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

      "code for NW750 where OFFSET is not supported
      "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 ze2e001_c_salesorder
*      where (iv_where_clause)
*      and   salesorder in @lt_key_range_salesorder
*      order by (iv_orderby_string)
*      into corresponding fields of table @lt_salesorder
*      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_salesorder to iv_skip.
*      endif.

      "OFFSET is only supported as of NW751
      select (iv_select_string) from ze2e001_c_salesorder
      where (iv_where_clause)
      and   salesorder in @lt_key_range_salesorder
      order by (iv_orderby_string)
      into corresponding fields of table @lt_salesorder
      up to @iv_top rows
      offset @iv_skip.

      io_response->set_busi_data( it_busi_data = lt_salesorder ).

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

        select count( * ) from ze2e001_c_salesorder
            where (iv_where_clause) and
           salesorder in @lt_key_range_salesorder
            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_salesorderitem.

    "entity type specific data types
    data : lt_key_range_salesorder     type zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
           ls_key_range_salesorder     type line of zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
           lt_key_range_salesorderitem type zif_e2e001_odata_v4_so_types=>gt_key_range-itemposition,
           ls_key_range_salesorderitem type line of zif_e2e001_odata_v4_so_types=>gt_key_range-itemposition,
           lt_salesorderitem           type standard table of gty_cds_views-salesorderitem,
           lt_key_salesorderitem       type standard table of gty_cds_views-salesorderitem.

    "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_salesorderitem ).
      loop at lt_key_salesorderitem into data(ls_key_entity).
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-salesorder ) to lt_key_range_salesorder.
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-salesorderitem ) to lt_key_range_salesorderitem.
      endloop.

      "the first key field (salesoder) is always the same
      delete adjacent duplicates from lt_key_range_salesorder.
      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.
      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>use_filter_top_or_nav
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-bad_request
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder.
    endif.

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

      " read data from the CDS view

      "OFFSET is only supported as of NW751
      select (iv_select_string) from ze2e001_c_salesorderitem
      where (iv_where_clause)
      and   salesorder in @lt_key_range_salesorder
      and   salesorderitem in @lt_key_range_salesorderitem
      order by (iv_orderby_string)
      into corresponding fields of table @lt_salesorderitem
      UP TO @iv_top ROWS
      OFFSET @iv_skip.

*      "code for NW750 where offset is not supported
*      "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 ze2e001_c_salesorderitem
*      where (iv_where_clause)
*      and   salesorder in @lt_key_range_salesorder
*      and   salesorderitem in @lt_key_range_salesorderitem
*      order by (iv_orderby_string)
*      into corresponding fields of table @lt_salesorderitem
*      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_salesorderitem to iv_skip.
*      endif.

      io_response->set_busi_data( it_busi_data = lt_salesorderitem ).

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

        select count( * ) from ze2e001_c_salesorderitem
            where (iv_where_clause)
            and   salesorder in @lt_key_range_salesorder
            and   salesorderitem in @lt_key_range_salesorderitem
            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_ref_key_list_salesorder.

    "entity type specific data types
    data: ls_salesorder_key_data     type  gty_cds_views-salesorder,
          lt_salesorderitem_key_data type standard table of gty_cds_views-salesorderitem,
          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_salesorder_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-salesorder_to_items.

        select salesorder , salesorderitem from ze2e001_c_salesorderitem
        into corresponding fields of table @lt_salesorderitem_key_data
        where salesorder = @ls_salesorder_key_data-salesorder.

        io_response->set_target_key_data( lt_salesorderitem_key_data ).

      when others.

        raise exception type zcx_e2e001_odata_v4_so
          exporting
            http_status_code = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-sv_not_implemented.

    endcase.

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


  endmethod.


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


    " Create single entity using classic API.
    " Map request fields to function module parameters
    ls_salesorder_rfc-buyer_id = ls_salesorder-customer.
    "ls_salesorder_rfc-buyer_name = ls_salesorder-customer.
    ls_salesorder_rfc-currency_code  = ls_salesorder-transactioncurrency.


    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
    and lt_bapi_return[ 1 ]-type <> 'S'.
      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
      raise exception type zcx_e2e001_odata_v4_so
        exporting
          message_container = lo_message_container.
    endif.


    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.

    io_response->set_is_done( ls_done_list ).


  endmethod.


  method create_salesorderitem.

  endmethod.


  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.

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


    "check if key data matches key data provided in the payload
    "only needed if update instead of patch is used

    if ls_salesorder-salesorder <> ls_key_salesorder-salesorder and
    ls_todo_list-process-partial_busi_data = abap_false.

      "Move data first to an integer to remove leading zeros from the response
      lv_salesorder_key_edm = lv_helper_int = ls_key_salesorder-salesorder.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid           = zcx_e2e001_odata_v4_so=>entity_keys_do_not_match
          http_status_code = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-bad_request
          entity_key       = lv_salesorder_key_edm.
    endif.

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

    " In OData V4 an update request has to return data by default unless specified otherwise using
    " the http header ...
    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 ).



  endmethod.

endclass.

Service registration

What is left is to do the following.

  1. Register a service group ZE2E001 using transaction /iwbep/v4_admin
  2. Publish the service group using transaction /iwfnd/v4_admin
  3. Register the service in the service group ZE2E001 with the name ZE2E001_SALESORDER

For more details see the SAP Online Help.

Field Value
 Service ID  ZE2E001_SALESORDER
 Service Version  1
 Model Provider Class  ZCL_E2E001_ODATA_V4_SO_MODEL
 Data Provider Class  ZCL_E2E001_ODATA_V4_SO_DATA
Description OData V4 demo service
Package ZE2E001

 

Outlook

In the following blogs I will explain:

  • the basic interface methods for create, update and delete
  • the intermediate interface ($expand)
  • the advanced interface (navigation)

 

Core data services (CDS) is an infrastructure that can be used to define and consume semantically rich data models in SAP HANA.

The purpose is straight forward but the technical definition can provoke confusion sometimes, especially because we can find two different types of Core Data Services:

  • HANA CDS
  • ABAP CDS

While HANA CDS has to support only an SAP HANA database, ABAP CDS provides support to different database vendors, consequently there are some restrictions for ABAP CDS views compared to HANA CDS views. When it isn’t possible to solve your scenario with an ABAP CDS View there is an alternative solution creating an ABAP CDS Table Function powered by ABAP Managed Database Procedures (AMDP).

Let’s do a quick comparison between the development approaches with ABAP CDS View and ABAP CDS Table Function.

 

ABAP CDS View

In a common ABAP CDS View development we declare our field structure and annotations through a scripting editor in the ABAP layer (usually through HANA Studio with ADT installation) and after activation the system generates automatically all the SQL Views in the database layer.

ABAP CDS Views provide support to several SQL commands and functions, if you want to check in details all the available features I advise to have a look in the following post: ABAP CDS Feature Matrix.

 

ABAP CDS Table Function

In an ABAP CDS Table Function development we define an entity with the field structure, parameters (optional) and an association to a class/method. With AMDP we’re able to write database procedures directly in the ABAP layer and encapsulate inside the class/method we defined in the Table Function, the call works as the same like any other ABAP methods and we have the following advantages:

  • Detailed analysis of runtime errors through ST22;
  • Database procedures debug available through HANA Studio;
  • Transport identical as to ABAP classes.

Since AMDP works directly with database scripting some extra steps are necessary like the definition of database and the script language to be used (in SAP HANA the language is the SQL Script). We’ll talk about this configuration in details during the development of our demo.

 

With both development approaches explained we can finally start the development of our technical demo. After finishing this post you will be able to create your own ABAP CDS Table Function and provide a solution for a simple technical scenario that cannot be achieved directly through a default ABAP CDS View. For the purposes of this demo we will use the database view SFLIGHTS who provides details about flight connections.

 

Scenario

Each airline company provides flight connections to different cities around the world, the user wants to see all the cities supported by a specific airline in a single field separate by comma. Since the number of cities can be different for each one of the airlines we need to generate a logic to concatenate all the cities no matter how many entries are returned.

Through a common ABAP CDS View we could use a CONCAT function, but in this case we would need to define a fixed quantity of fields, since the CDS View can’t handle this logic dynamically how should we proceed?

This is actually a perfect scenario for an ABAP CDS Table Function because we can solve with a simple database function called STRING_AGG (String Aggregation). This function is available in the SQL Script but currently there is no support through ABAP CDS View.

 

Development

Open your HANA studio and create a new Core Data Services -> Data Definition.

Select your project, package and define the name and description as below.

Select your transport request and click Next. In the Templates section select the last option “Define Table Function with Parameters” and Finish.

Edit the generated entity including the following details:

  • Fields:
    • Client
    • Airline Code
    • Airline Name
    • Cities To
  • Class: ZCL_FLIGHTS_DEMO_CDS
  • Method: FLIGHTS_CONNECTIONS

This should be the final result:

define table function ZDEMO_FLIGHTS_TABLE_FUNCTION
returns
{
  client       : abap.clnt;
  airline_code : s_carr_id;
  airline_name : s_carrname;
  cities_to    : abap.string;
}
implemented by method
  ZCL_FLIGHTS_DEMO_CDS=>FLIGHTS_CONNECTIONS;

Let’s create a new ABAP class with the name ZCL_FLIGHTS_DEMO_CDS, select a transport and click in Finish.

Adapt your class including the interface IF_AMDP_MARKER_HDB. This step will transform the ABAP class into an AMDP class and provide the possibility to include database procedures inside its methods.

  PUBLIC SECTION.
    INTERFACES if_amdp_marker_hdb.

Declare a new public method and include the statement FOR TABLE FUNCTION referencing the table function we created in the first step.

  CLASS-METHODS:
    flights_connections FOR TABLE FUNCTION ZDEMO_FLIGHTS_TABLE_FUNCTION.

In the method implementation we need to include some configuration options:

  • BY DATABASE FUNCTION: This option will mark the method as a table function, another option is to generate a procedure changing the statement to BY DATABASE PROCEDURE.
  • FOR HDB: Defines the database type as HDB (HANA Database).
  • LANGUAGE SQLSCRIPT: Language used by HANA database procedures.
  • OPTIONS READ-ONLY: No changes allowed inside the database procedure.
  • USING: Definition of database tables, views or procedures that would be consumed inside our table function. In our case we need to access data only from SFLIGHTS view.
  METHOD flights_connections 
    BY DATABASE FUNCTION
    FOR HDB
    LANGUAGE SQLSCRIPT
    OPTIONS READ-ONLY
    USING sflights.

    <<YOUR_CODE_HERE>>

  ENDMETHOD.

Let’s prepare our selection splitting the logic in two SELECT statements.

The first SELECT should collect the fields Client, Airline Code, Airline Name and City To and execute a DISTINCT to remove possible duplicate entries, the reason behind this logic is because we can find different connection dates for the same Airline and City. Check the example below with a few entries from SFLIGHTS view:

One of the advantages of AMDP is that you can transfer results from a specific SELECT to an “internal table” and you can execute a new SELECT reading data from it. Let’s take advantage of this functionality and store the result of this first SELECT in a table called itab_cities.

    itab_cities =
      SELECT DISTINCT 
             sflights.mandt    as client,
             sflights.carrid   as airline_code,
             sflights.carrname as airline_name,
             sflights.cityto   as city_to
        FROM sflights;

In the second SELECT we should read the data from itab_cities and implement the database function STRING_AGG to aggregate multiple Cities per Airline. To achieve this functionality we need to GROUP BY the entries based on Client, Airline Code and Name. The expected result is the following:

    RETURN
      SELECT client,
             airline_code,
             airline_name,
             STRING_AGG(city_to, ', ' ORDER BY city_to) as cities_to
        FROM :itab_cities
       GROUP BY client,
                airline_code,
                airline_name;

Note: A table function will always expect a return parameter, so remember to place a RETURN statement before the last SELECT. Also, notice that we converted the field names to the expected name by the ABAP CDS Table Function declaration, if you don’t provide a proper name alias the compiler will complain during the activation.

 

This should be your final version of the class ZCL_FLIGHTS_DEMO_CDS:

CLASS zcl_flights_demo_cds DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES if_amdp_marker_hdb.

    CLASS-METHODS:
      flights_connections FOR TABLE FUNCTION zdemo_flights_table_function.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcl_flights_demo_cds IMPLEMENTATION.

  METHOD flights_connections
    BY DATABASE FUNCTION
    FOR HDB
    LANGUAGE SQLSCRIPT
    OPTIONS READ-ONLY
    USING sflights.

    itab_cities =
      SELECT DISTINCT 
             sflights.mandt    as client,
             sflights.carrid   as airline_code,
             sflights.carrname as airline_name,
             sflights.cityto   as city_to
        FROM sflights;

    RETURN
      SELECT client,
             airline_code,
             airline_name,
             STRING_AGG(city_to, ', ' ORDER BY city_to) as cities_to
        FROM :itab_cities
       GROUP BY client,
                airline_code,
                airline_name;

  ENDMETHOD.

ENDCLASS.

And this is the result of the Data Preview:

This guide explains the upgrade process for the upgrade from SAP S/4HANA, on-premise edition 1511 and SAP S/4HANA 1610 (all feature package stacks) to SAP S/4HANA 1709. As a prerequisite for SAP S/4HANA OP 1709, the HANA DB Version must be upgraded to HANA 2.0 SPSxx or higher. For information on SAP HANA revision strategy, and SAP HANA Maintenance, please refer to SAP Note 2021789 – SAP HANA Revision and Maintenance Strategy.

  • To download the HANA DB Revision, go to http://support.sap.com/swdc >> Support Packages and Patches >> Software Downloads >> Support Packages and Patches >> By Alphabetical Index (A-Z) >> H >> SAP HANA PLATFORM EDITION >> SAP HANA PLATFORM EDIT. 1.0. Then decide if you want to upgrade by Support Package Stack or Entry By Component and the revision you want to upgrade to. Please refer to SAP Note 2021789 – SAP HANA Revision and Maintenance Strategy for revision strategy. Download your files with download manager and copy to the SAP HANA DB server.

The high-level steps for the S/4 HANA 1709 upgrade include the following:

  • Full database backup both backend and frontend systems.
  • Upgrade HANA DB to version 2.0 SPSxx or higher.
  • Maintenance Planner for both SAP backend and frontend systems.
  • Download upgrade and SUM software.
  • SAP Simplification Pre-Checks.
  • SAP SUM upgrade for S/4HANA 1709 backend system.
  • SAP SUM upgrade for S/4HANA 1709 frontend system.
  • SPDD and SPAU adjustments.
  • Post Upgrade Activities.

The overview of the upgrade process shown in the figure below (including the tools, the phases, and the activities involved in the upgrade process) is intended to help you to plan and perform the upgrade from lower SAP S/4HANA releases to SAP S/4HANA 1709.

Pre-Requisites

The overview of the upgrade process is shown in the previous Introduction section. It is recommended to review and complete the Prepare Phases – System Requirement, Maintenance Planner, SI Checks, and Custom Code Migration, successfully before starting actual upgrade process in Realize Phase. This document will detail the Maintenance Planner process to generate a valid Stack.xml file for both Backend Server (BES) and Frontend Server (FES) systems. In addition, implementation of SI-Checks procedure is discussed in detail in this document but Custom Code Migration will not be included as it is very specific to the customer environment. But it’s advisable to review the custom code migration procedure to complete the upgrade successfully.

For S/4HANA 1709, HANA 2.0 database is a mandatory. This requirement may result in an upgrade from HANA 1.0 to HANA 2.0 and/or an Operating System upgrade prior to the S/4HANA 1709 application upgrade. Please review the Product Availability Matrix (PAM) for HANA 2.0 to plan the upgrades.

SAP Maintenance Planner

The first step in S/4HANA conversion is to analyze the source system using the Maintenance planner tool. With all S/4HANA implementation and upgrades, the Maintenance Planner become a mandatory solution to start any activity in a S/4HANA project to download the software or check the SAP ERP solution readiness to be converted S/4HANA and building the stack file for system conversion. It checks your components, add-ons, and business functions to ensure compatibility with SAP S/4HANA 1709 and also creates the stack file used during the upgrade process (done by the Software Update Manager tool).

Simplification Item-Checks

The Simplification Item-Checks identifies the steps you need to take to make sure that your system can technically be upgraded and that your business processes can start running directly after the upgrade process has been completed. The Simplification Item-Check (SI-Check) is run by the Software Update Manager. Before starting the Simplification Item Checks, you will need complete the following steps to install the software in your source SAP S/4HANA backend system. The SI-Check is delivered with SAP Notes 2399707 and 2502552. SAP Note 2399707 delivers the new check report; SAP Note 2502552 delivers the check classes via transport-based correction instructions (TCI) and prerequisite notes.

SAP S/4HANA 1709 SUM – Backend System

Software Update Manager is the technical tool used for upgrade to SAP S/4HANA 1709. It is recommended to download the latest SUM tool to perform the upgrade. Before starting the upgrade make sure you have a valid stack.xml created using Maintenance Planner, which will be required to start the upgrade. Also, check the upgrade relevant simplification items and a valid system backup exists, in case if the system needs to be restored to pre-upgrade state.

SAP S/4HANA 1709 SUM – Frontend System

Software Update Manager is the technical tool used for upgrade to SAP S/4HANA 1709. It is recommended to download the latest SUM tool to perform the upgrade. Before starting the upgrade make sure you have a valid stack.xml created using Maintenance Planner, which will be required to start the upgrade. Also, check a valid system backup exists, in case if the system needs to be restored to pre-upgrade state.

Please refer to the following document for step by step instructions on upgradig your S/4HANA 1511 or 1610 system to the latest S/4HANA 1709 system.

Link to document – https://www.sap.com/documents/2017/12/04b5120e-e57c-0010-82c7-eda71af511fa.html

 

Thanks,

SAP S/4HANA RIG

 

This document will provide you the required post system copy steps to get your SAP S/4HANA On Premise system up and running. The document will guide you through the steps required after you have successfully performed a SAP system copy of both SAP S/4HANA Backend and Frontend system. This document will not cover the SWPM steps for the target System of a standard System Copy. After completing the post system copy steps, you will have a functioning SAP Fiori Launchpad connecting to the SAP S/4HANA backend system.

High level post system steps include:

  • Generate SAP License
  • Adopt SAP Instance Profiles
  • Reconfigure SSL
  • Reconfigure Trusted RFC on both BES and FES
  • Reconfigure SAP Web Dispatcher Instance Profile
  • Reconfigure SAP Web Dispatcher SSL
  • Reconfigure RFC’s
  • Reconfigure STMS
  • Reconfigure SAML

Optional Steps, if SID rename is performed:

  • Update logical system names
  • Reconfigure Enterprise Search
  • Reconfigure Alias

As a prerequisite for SAP S/4HANA On Premise post system copy document, you will need to perform a homogenous or heterogenous system copy procedure. To perform a SAP system copy, you will need to follow the standard system copy procedures. Please refer to the following SAP System Copy procedure for details steps. Both SAP S/4HANA Backend and Frontend system must be successfully copied and started before use.

We will use the following acronyms in this guide:

BES = SAP S/4HANA Backend System

FES = SAP S/4HANA Frontend System

Please refer to the following document for step by step instructions on the necessary post system copy steps to get you S/4HANA Backend and Fiori Frontend system up and running.

Link to document – https://www.sap.com/documents/2017/12/584b4b05-e47c-0010-82c7-eda71af511fa.html

Thanks,

SAP S/4HANA RIG

I attended the 2017 Smart City Expo World Congress. As in previous years, this was a great learning experience. Of all the interesting things I heard, the shiny new toys and the fancy demos that I saw, five attributes clearly stood out as key success factors for Smart Cities.

The first and most important was that smart cities strategies must be people centric. The value is not in the smart traffic infrastructure, autonomous cars or machine learning algorithms supporting digital services. The value is in driving outcomes for citizens, businesses, visitors and city employees in domains like governance, transport, economy and the environment. At an even more granular level, it is important that cities have clear goals in mind not only for citizens, but also for specific segments, like the elderly, that can benefit from integrated health and social care services, or for the teenagers who crave opportunities to develop their talents at school and in the community. For example, I had the opportunity to speak with a Spanish city that is honing in on the needs of tourists. The city is well aware that tourists are a major source of economic development and is trying to develop even more personalized services, by integrating data and coordinating processes from the public transit authority, local museums, local hotels, restaurants and retail. I also heard from a local council from Australia mention the need to optimize transportation for students for after school activities.

High-level people centric strategic goals are not enough. They must be contextualized by district or neighborhood. Cities generate economic and social outcomes at the neighborhood level or by interacting activities across nearby neighborhoods. Each neighborhood has its texture of businesses, schools, cultures, community, health centers, and infrastructure that determine the success of smart programs and projects. For instance, I talked with a US city that is trying to differentiate economic development policies by neighborhood to best leverage local capabilities, rather than force a one-size fits all approach. Another US city mentioned the importance of equality of opportunities for different income brackets within neighborhoods and across neighborhoods.

Whether it is at the whole-of-city level, or at the neighborhood level, city administrations cannot do it on their own. They need to collaborate with the ecosystem. Reducing traffic congestion and vehicle CO2 emissions cannot be achieved by putting autonomous vehicle on the road. It must be a concerted effort with the transit authority, car makers, railway operators, taxi operators, electric vehicle charging station operators and many others to offer true mobility as a service solutions. Only by integrating knowledge and processes across those domains can the city innovate. For instance, the City of Nanjing garners  a detailed view of travel conditions and can provide planning recommendations through analysis of traffic movement patterns generated from sensors, and other data, such as travel behavior of individuals in conjunction with road conditions, area accessibility.

Cities and their ecosystem partners have the opportunity to create economic and social value for their citizens by leveraging data as a strategic asset. The data economy is not only about collecting data feeds from thousands of Internet of Things (IoT) devices, or applying machine learning, it is about business model innovation. Data can deliver the insights to policy decision makers or for service managers that must deliver personalized services. Data can become the currency for freemium services, whereby consumers get access to service for free, for instance wi-fi, but the service operator makes money through advertising or other means, or micro-transactions, whereby consumers pay a marginal fee for increased convenience of service, for instance to pay utility bills at a grocery store or collect social security payments at the local post office. One city I met during a panel at the conference, mentioned how they are piloting using water metering data, originally collected to optimize the operations of the water distribution network, to monitor the health conditions of elderly living alone, by detecting anomalies of excessive or scarce usage of water and then possibly triggering alerts for social workers. Cities that can scale these alternative business models, by investing in data science and business skills, and setting up the right commercial agreements with ecosystem partners, will deliver innovative services and increase sustainability of their finances.

To best leverage data across domains and ecosystems and to make sure that the insights from data can be injected into business processes to increase their efficiency and effectiveness, cities must take a platform approach to their technology architecture. It is only through platforms that can securely deliver data integration, data governance, data analysis, process orchestration, API services and collaborative tools that cities can realize the value of their investments. One of the cities that I met in Barcelona is leveraging its investment in a bike sharing fleet and the related technology to install sensors on the bikes, so that they can collect more granular, geo-located data on traffic, air, noise pollution, and road conditions. Cities that continue to conceive their technological roadmap in silos will not be able to scale pilots that they have launched in specific domains, such as smart bins, or bike sharing.

One may think that smart cities are about data and analytics, IoT and the digital twin, electric autonomous vehicles, blockchain and artificial intelligence. True, those five groups of technologies are opening up endless possibilities to innovate. But municipal leaders that want to make their cities smarter through innovation must focus first on: citizen centric outcomes, neighborhood context, ecosystem, alternative business models enabled by the data economy, and working with technology companies that can supply the right platforms.