Skip to Content
Technical Articles

How to use an S4/Hana OData service in Analytics Designer?

Analytics Designer complements SAC stories by allowing to respond to very specific scenarios.
One of these scenarios is to be able to explore and analyze data following an alert sent by the system (for example a sales revenue warning). Then be able to make simulations to correct this problem (review the material prices) and finally validate the option that solves the problem (update the material prices).
This workflow is called a close loop scenario. It basically gives the power to a business user to do a what-if analysis and to take a concrete action based on his simulation.

 
In this blog post I will show you how to implement an ODataService in S/4Hana and how to call it from an Analytic Application. I will use a well-known demo at SAP called BestRun Bike.

Demo context

The demo shows BestRun Bikes company tracks, analyzes eBike sales performance issue using SAP Analytics Cloud analytic application and takes the insight into action to improve the sales revenue.

The highlighted features include:

  • Embed Explorer, Smart Discovery, R visualization in analytic application
  • Call OData actions in the backend S/4HANA system via scripting

I will concentrate the rest of the blog post on the OData part. In this demo all the product prices are read from the S4HANA system using OData Function

In the simulation&planning page we, can see that the sales revenue based on current prices is showing red alert compared to budget.

During the analysis of the eBike sales performance, we can see that the MZ-FZ-E211 eBike model has a much higher price than competitive products.

To adjust the price to the competition, we can run a simulation on the eBike prices. In our simulation we reduce the price by 15%.

This has the effect of increasing the forecast revenue and the quantity sold in a positive way. We need to update the price based on our simulation. The good news is that to change the price you don’t need to go into the S/4Hana system. Thanks to Analytics Designer you can directly call an OData service that has been implemented in S/4Hana system. To do so we just need to click on the “Update” button

 

Create an OData Service in S/4HANA

In S/4Hana you can use 2 versions of OData : version 2 and version 4. In order to write back data you need to use the OData V4 version. In this blog I will explain what are the main steps to create an ABAP OData Service. If you want to have more details regarding the implementation of an OData V4 Service please have a look here

To be able to consume the S/4Hana OData service in this demo we had to:

  1. Create ABAP CDS view
  2. Create ABAP OData Service and implement ABAP classes
  3. Test the OData Service URL

Create ABAP CDS View
For this demo we created 2 ABAP CDS views that read the data from a Pricing table:

  • ZMATERIALCOND : Base CDS view to read Material Condition Records
@AbapCatalog.sqlViewName: 'ZMATCONDRECORD'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material Conditio Record'
define view ZMATERIALCOND as select from a304 join konp on a304.knumh = konp.knumh{
key a304.matnr as Material,
a304.knumh as ConditionRecord,

@Semantics.amount.currencyCode: 'Currency'
konp.kbetr as Amount,

@Semantics.currencyCode: true
konp.konwa as Currency,
konp.kpein as PriceUnit,
konp.kmein as UoM     
    
}
where a304.kappl = 'V' and a304.kschl = 'PPR0' and a304.vkorg = '1710' and a304.vtweg = '10' and konp.konwa != '';
  • ZMATERIALPRICE : Consumption CDS view to join Material Valuation & Condition Records
@AbapCatalog.sqlViewName: 'ZMATPRICE'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material price'
@OData.publish: true
define view ZMATERIALPRICE as select from mbew join ZMATERIALCOND on mbew.matnr = ZMATERIALCOND.Material {
 
 key mbew.matnr as materialno 
 ,
@Semantics.amount.currencyCode: 'Currency'
mbew.stprs as StandardPrice,

@Semantics.amount.currencyCode: 'Currency'
mbew.verpr as PeriodicUnitPrice,

@Semantics.amount.currencyCode: 'Currency'
mbew.vjstp as StdPricePreviousYear ,

@Semantics.amount.currencyCode: 'Currency'
ZMATERIALCOND.Amount as ConditionAmount,
@Semantics.currencyCode: true
ZMATERIALCOND.Currency as Currency 

// ZMATERIALCOND.PriceUnit as PriceUnit,

//ZMATERIALCOND.UoM as UoM
   
}

To expose these CDS view to external application, we created an ABAP OData service. To do so, you need to type the Transaction Code : SEGW in the S/4Hana System

Create ABAP OData Service
Then we created a new OData service. You need to select the Project Type= OData 4.0 Service.
In our example, we called the project zanalytics_material

Add EntitySet, Entity and OData actions
Once the service has been created we added an entityset called MaterialPriceDataSet and an entity MaterialPriceData

We also needed to implement 2 mandatory classes :

  • The model provider class  (ZCL_ZANALYTICS_MATERIA_MPC_EXT)
  • The data provider class ( ZCL_ZANALYTICS_MATERIA_DPC_EXT)

In the MPC EXT class we declared a new action called “MaterialPriceUpdate“ (line14). This is the action that is called by the analytic application

Here is an example of the implementation for this class

class ZCL_ZANALYTICS_MATERIA_MPC_EXT definition
  public
  inheriting from ZCL_ZANALYTICS_MATERIA_MPC
  create public .

public section.

  methods DEFINE_MATERIALDATA
    importing
      !IO_MODEL type ref to /IWBEP/IF_V4_MED_MODEL .

  methods /IWBEP/IF_V4_MP_BASIC~DEFINE
    redefinition .
protected section.
private section.
ENDCLASS.



CLASS ZCL_ZANALYTICS_MATERIA_MPC_EXT IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_ZANALYTICS_MATERIA_MPC_EXT->/IWBEP/IF_V4_MP_BASIC~DEFINE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_MODEL                       TYPE REF TO /IWBEP/IF_V4_MED_MODEL
* | [--->] IO_MODEL_INFO                  TYPE REF TO /IWBEP/IF_V4_MODEL_INFO
* | [!CX!] /IWBEP/CX_GATEWAY
* +--------------------------------------------------------------------------------------</SIGNATURE>
  method /IWBEP/IF_V4_MP_BASIC~DEFINE.
**TRY.
*CALL METHOD SUPER->/IWBEP/IF_V4_MP_BASIC~DEFINE
*  EXPORTING
*    IO_MODEL      =
*    IO_MODEL_INFO =
*    .
** CATCH /IWBEP/CX_GATEWAY .
super->/IWBEP/IF_V4_MP_BASIC~DEFINE(
  EXPORTING
    IO_MODEL      =  IO_MODEL                " OData V4 metadata model
    IO_MODEL_INFO =  IO_MODEL_INFO               " Information of / for the metadata model
).
*CATCH /IWBEP/CX_GATEWAY. " SAP Gateway Exception
*CATCH /IWBEP/CX_GATEWAY. " SAP Gateway Exception

**ENDTRY.
   define_materialdata( io_model ).


  endmethod.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_ZANALYTICS_MATERIA_MPC_EXT->DEFINE_MATERIALDATA
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_MODEL                       TYPE REF TO /IWBEP/IF_V4_MED_MODEL
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD define_materialdata.
    DATA:
      lo_action        TYPE REF TO /iwbep/if_v4_med_action,

      lo_action_import TYPE REF TO /iwbep/if_v4_med_action_imp,
      lo_parameter     TYPE REF TO /iwbep/if_v4_med_act_param,
      lo_return        TYPE REF TO /iwbep/if_v4_med_act_return.

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Update Material price
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

    lo_action = io_model->create_action( 'MATERIALPRICEUPDATE' ).
    lo_action->set_edm_name( 'MaterialPriceUpdate' ).

    lo_action_import = lo_action->create_action_import( iv_action_import_name = 'MATERIALPRICEUPDATE' ).
    lo_action_import->set_edm_name( 'MaterialPriceUpdate' ).
    lo_action_import->set_entity_set_name( 'MATERIALPRICEDATASET' ).

*    CATCH /iwbep/cx_v4_med. " Metadata exception
*    CATCH /iwbep/cx_v4_med. " OData V4 - Metadata exception

    io_model->create_primitive_type( iv_primitive_type_name = 'STRING' )->set_edm_type( /iwbep/if_v4_med_element=>gcs_edm_data_types-string ).
    lo_parameter = lo_action->create_parameter( 'MATERIAL' ).
    lo_parameter->set_edm_name( 'Material' ).
    lo_parameter->set_primitive_type( 'STRING' ).
*    lo_parameter->set_entity_type( 'MATERIALPRICEDATA' ).
*    lo_parameter->set_is_binding_parameter( ).

*    io_model->create_primitive_type( iv_primitive_type_name = 'STRING' )->set_edm_type( /iwbep/if_v4_med_element=>gcs_edm_data_types-string ).
*    FREE lo_parameter.
    lo_parameter = lo_action->create_parameter( 'PRICE' ).
    lo_parameter->set_edm_name( 'price' ).
    lo_parameter->set_primitive_type( 'STRING' ).

    lo_return = lo_action->create_return( ).
    lo_return->set_entity_type( 'MATERIALPRICEDATA' ).
    lo_return->set_is_collection( ).
    .
  ENDMETHOD.
ENDCLASS.

 

In the DPC_EXT Class, we implemented the Read and Update Price methods
The screenshot below is an example of the method “UPDATE_MATERIAL_PRICE”
You can see the places where Material Number and Material Price is being passed in the code.

Here is an example of implementation of this class

class ZCL_ZANALYTICS_MATERIA_DPC_EXT definition
  public
  inheriting from ZCL_ZANALYTICS_MATERIA_DPC
  create public .

public section.

  methods READ_MATERIAL_DATA
    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
      !LV_SKIP type I
      !LV_TOP type I
      !IS_DONE_LIST type /IWBEP/IF_V4_REQU_BASIC_LIST=>TY_S_TODO_PROCESS_LIST .

  methods /IWBEP/IF_V4_DP_ADVANCED~EXECUTE_ACTION
    redefinition .
  methods /IWBEP/IF_V4_DP_BASIC~READ_ENTITY_LIST
    redefinition .
protected section.
private section.

  methods UPDATE_MATERIAL_PRICE
    importing
      !MATERIAL_NO type STRING
      !PRICE type STRING .
ENDCLASS.



CLASS ZCL_ZANALYTICS_MATERIA_DPC_EXT IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_ZANALYTICS_MATERIA_DPC_EXT->/IWBEP/IF_V4_DP_ADVANCED~EXECUTE_ACTION
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_REQUEST                     TYPE REF TO /IWBEP/IF_V4_REQU_ADV_ACTION
* | [--->] IO_RESPONSE                    TYPE REF TO /IWBEP/IF_V4_RESP_ADV_ACTION
* | [!CX!] /IWBEP/CX_GATEWAY
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD /iwbep/if_v4_dp_advanced~execute_action.
**TRY.
*CALL METHOD SUPER->/IWBEP/IF_V4_DP_ADVANCED~EXECUTE_ACTION
*  EXPORTING
*    IO_REQUEST  =
*    IO_RESPONSE =
*    .
** CATCH /IWBEP/CX_GATEWAY .
**ENDTRY.


    TYPES: BEGIN OF s_matprice_action,
             material TYPE string,
             price    TYPE string,
           END OF s_matprice_action.

    DATA: ls_matprice_params TYPE s_matprice_action,
          lt_materialprice   TYPE STANDARD TABLE OF zif_odata_v4_mat_types=>gty_cds_views-materialprice.

    DATA:
      lv_type_kind          TYPE /iwbep/if_v4_med_element=>ty_e_med_element_kind,
      lv_action_name        TYPE /iwbep/if_v4_med_element=>ty_e_med_internal_name,
      lv_action_import_name TYPE /iwbep/if_v4_med_element=>ty_e_med_internal_name,
      ls_done               TYPE /iwbep/if_v4_requ_adv_action=>ty_s_todo_process_list,

      ls_materialdata       TYPE zmatprice,
      lt_materialdata       TYPE STANDARD TABLE OF zmatprice,

      ls_todo               TYPE /iwbep/if_v4_requ_adv_action=>ty_s_todo_list,
*      ls_done            TYPE /iwbep/if_v4_requ_adv_action=>ty_s_todo_process_list,
      ls_textid             LIKE if_t100_message=>t100key,


      lo_navigation_node    TYPE REF TO /iwbep/if_v4_navigation_node.
*    todo list
    io_request->get_todos( IMPORTING es_todo_list = ls_todo ).

    IF ls_todo-process-action_import = abap_true.


      io_request->get_action_import( IMPORTING ev_action_import_name =   lv_action_name  ).
      io_request->get_action( IMPORTING ev_action_name = DATA(lv_action) ).

      IF lv_action_name = 'MATERIALPRICEUPDATE'.

        io_request->get_parameter_data(
          IMPORTING
            es_parameter_data =  ls_matprice_params                " Structure with the action parameters

        ).

        ls_done-parameter_data = abap_true.

        update_material_price(
          EXPORTING
            material_no =   ls_matprice_params-material
            price       =   ls_matprice_params-price
        ).

        SELECT * FROM zmaterialprice INTO TABLE @lt_materialprice
                   WHERE materialno = @ls_matprice_params-material.

*        FIELD-SYMBOLS: <fs_material> TYPE zmaterialprice.
*
*        LOOP AT lt_materialprice ASSIGNING <fs_material>.
*          <fs_material>-conditionamount = ls_matprice_params-price.
**          <fs_material>-standardprice = ls_matprice_params-price.
*        ENDLOOP.

        io_response->set_busi_data( ia_busi_data = lt_materialprice ).

        ls_done-action_import = abap_true.

        io_response->set_is_done( ls_done ).

      ENDIF.

    ENDIF.

  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_ZANALYTICS_MATERIA_DPC_EXT->/IWBEP/IF_V4_DP_BASIC~READ_ENTITY_LIST
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_REQUEST                     TYPE REF TO /IWBEP/IF_V4_REQU_BASIC_LIST
* | [--->] IO_RESPONSE                    TYPE REF TO /IWBEP/IF_V4_RESP_BASIC_LIST
* | [!CX!] /IWBEP/CX_GATEWAY
* +--------------------------------------------------------------------------------------</SIGNATURE>
  method /IWBEP/IF_V4_DP_BASIC~READ_ENTITY_LIST.
**TRY.
*CALL METHOD SUPER->/IWBEP/IF_V4_DP_BASIC~READ_ENTITY_LIST
*  EXPORTING
*    IO_REQUEST  =
*    IO_RESPONSE =
*    .
** CATCH /IWBEP/CX_GATEWAY .
**ENDTRY.

     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,
           iv_orderby_string type string,
        iv_where_clause   type string,
        iv_select_string  type string,
          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 'MATERIALPRICEDATASET'.
CALL METHOD me->READ_MATERIAL_DATA
  EXPORTING
    IO_REQUEST        = io_request
    IO_RESPONSE       = io_response
    IV_ORDERBY_STRING = lv_select_string
    IV_WHERE_CLAUSE   = lV_WHERE_CLAUSE
    IV_SELECT_STRING  =  lV_SELECT_STRING
    LV_SKIP           = LV_SKIP
    LV_TOP            = LV_TOP
    IS_DONE_LIST      = ls_done_list
    .




      WHEN OTHERS.
        RAISE EXCEPTION TYPE /iwbep/cx_v4_sample_bas
          EXPORTING
            textid             = /iwbep/cx_v4_sample_bas=>resource_not_implemented
            exception_category = /iwbep/cx_v4_sample_bas=>gcs_excep_categories-provider
            http_status_code   = '501'
            is_for_user        = abap_true.
    ENDCASE.
  endmethod.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_ZANALYTICS_MATERIA_DPC_EXT->READ_MATERIAL_DATA
* +-------------------------------------------------------------------------------------------------+
* | [--->] 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
* | [--->] LV_SKIP                        TYPE        I
* | [--->] LV_TOP                         TYPE        I
* | [--->] IS_DONE_LIST                   TYPE        /IWBEP/IF_V4_REQU_BASIC_LIST=>TY_S_TODO_PROCESS_LIST
* +--------------------------------------------------------------------------------------</SIGNATURE>
  method READ_MATERIAL_DATA.
     "entity type specific data types
     DATA : lt_key_range_materialdata type ZIF_ODATA_V4_MAT_TYPES=>gt_key_range-materialprice,
ls_key_range_salesorder type line of ZIF_ODATA_V4_MAT_TYPES=>gt_key_range-materialprice,
lt_materialprice type standard table of ZIF_ODATA_V4_MAT_TYPES=>gty_cds_views-materialprice,
lt_key_materialprice  type standard table of ZIF_ODATA_V4_MAT_TYPES=>gty_cds_views-materialprice.
  "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 = ls_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_materialprice ).
      loop at lt_key_materialprice into data(ls_key_entity).
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-materialno ) to lt_key_range_materialdata.
      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 lv_top = 0.

    endif.

  if ls_todo_list-return-busi_data = abap_true.

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

      select (iv_select_string) from ZMATERIALPRICE
      where (iv_where_clause)
      and   materialno in @lt_key_range_materialdata
      order by (iv_orderby_string)
      into corresponding fields of table @lt_materialprice
      up to @lv_max_index rows.

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

*

      io_response->set_busi_data( it_busi_data = lt_materialprice ).

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

        select count( * ) from ZMATERIALPRICE
            where (iv_where_clause) and
           materialno in @lt_key_range_materialdata
            into @lv_count.

        io_response->set_count( lv_count ).
      endif.
    endif.
    ls_done_list = 'X'.
    io_response->set_is_done( ls_done_list ).
  endmethod.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_ZANALYTICS_MATERIA_DPC_EXT->UPDATE_MATERIAL_PRICE
* +-------------------------------------------------------------------------------------------------+
* | [--->] MATERIAL_NO                    TYPE        STRING
* | [--->] PRICE                          TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD update_material_price.


    DATA : ls_key_fields   TYPE komg,
           ls_copy_records TYPE komv,
           lt_copy_records TYPE TABLE OF komv,
           ls_konp         TYPE konp.

    CLEAR : ls_key_fields, lt_copy_records.
    REFRESH : lt_copy_records.

    SELECT SINGLE * FROM a304 INTO @DATA(ls_mat_cond) WHERE
                                    kappl = 'V' AND
                                    kschl = 'PPR0' AND
                                    vkorg = '1710' AND
                                    vtweg = '10' AND
                                    matnr = @material_no.

    CHECK sy-subrc = 0.

    SELECT SINGLE * FROM konp
    INTO ls_konp
    WHERE knumh = ls_mat_cond-knumh
    AND loevm_ko <> 'X'.

    ls_key_fields-bukrs = '1710'.
    ls_key_fields-vkorg = '1710'.
    ls_key_fields-vtweg = '10'.
    ls_key_fields-matnr = material_no.

*lt_copy_records-knumv = ls_mat_cond-knumh.
    ls_copy_records-kposn = ls_konp-kopos.
    ls_copy_records-kappl = 'V'.
    ls_copy_records-kschl = 'PPR0'.
    ls_copy_records-kbetr = price.

    ls_copy_records-waers = ls_konp-konwa.
    ls_copy_records-krech = ls_konp-krech.
    ls_copy_records-stfkz = ls_konp-stfkz.
    ls_copy_records-kpein = ls_konp-kpein.
    ls_copy_records-kmein = ls_konp-kmein.
    ls_copy_records-zaehk_ind = ls_konp-zaehk_ind.
    APPEND ls_copy_records TO lt_copy_records.

    CALL FUNCTION 'RV_CONDITION_COPY'
      EXPORTING
        application                 = 'V'
        condition_table             = '304'
        condition_type              = 'PPR0'
        enqueue                     = 'X'
        key_fields                  = ls_key_fields
        maintain_mode               = 'B'
        no_authority_check          = 'X'
      TABLES
        copy_records                = lt_copy_records
      EXCEPTIONS
        enqueue_on_record           = 1
        invalid_application         = 2
        invalid_condition_number    = 3
        invalid_condition_type      = 4
        no_authority_ekorg          = 5
        no_authority_kschl          = 6
        no_authority_vkorg          = 7
        no_selection                = 8
        table_not_valid             = 9
        no_material_for_settlement  = 10
        no_unit_for_period_cond     = 11
        no_unit_reference_magnitude = 12
        invalid_condition_table     = 13
        OTHERS                      = 14.
    IF sy-subrc = 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.ENDIF.
      CALL FUNCTION 'RV_CONDITION_SAVE'.
      COMMIT WORK AND WAIT.

*  CALL FUNCTION 'RV_CONDITION_RESET'.
*  COMMIT WORK.

*      SELECT SINGLE * FROM zmaterialprice INTO @DATA(ls_materialprice) WHERE materialno = @mat.
*      ls_materialprice-conditionamount = price.
*      ls_materialprice-standardprice = price.
*
*      UPDATE zmaterialprice FROM ls_materialprice .

    ENDIF.

  ENDMETHOD.
ENDCLASS.

 

Test the OData service URL
Once the OData service has been implemented you can call it from a URL. To retrieve this URL you can type the transaction code = /n/IWFND/V4_ADMIN

This transaction code is to open OData v4 services. Here you can see list of OData v4 services available in the system and also test them

Then you need to select  “ZANALYTICS_MATERIAL” entry and click on Service Test -> Gateway Client

 

Call an S/4Hana OData action from an Application

In Analytics Designer in SAP Analytics Cloud, you can define OData Services based on an existing live connection in your system which was created using CORS (Cross-origin resource sharing) connectivity

Before calling the OData action “MaterialPriceUpdate“ (that we have defined in our S/4Hana system) from an application the main steps are:

  • Define the CORS configuration to your system according to the help page.
  • Additionally: Configure support for “if-match” as allowed header in your system.
  • Define a direct connection to this system.
  • Open an application and add an OData service technical component

Regarding the CORS configuration for S/4Hana system you can consult the following blog

Define a direct connection to this system
In our SAC system we declared a S/4Hana connectivity where we specified different parameters:

  • The name of the connection = S4OPHE4
  • The connection type = Direct
  • Host: cihe4.internal.sde.cloud.sap
  • HTTPS Port: 50081
  • Client: 400

And we used the SAML Single Sign On authentication method.

 

Add and configure the OData Services technical component
To be able to call the OData service we needed to create an OData Service technical component in the application. This component has few properties:

Name: name of your technical component
Connection: connection to the S/4Hana system
End-Point URL: OData Service URL generated in S/4Hana

In an analytic application in the Layout Outline in the Scripting Section you can create a new OData Service by clicking on plus button.

In the side panel you can change the name, select the System from the list of available systems whose connections are already created in SAC under Connections, and specify the End-Point URL of the OData Service manually.

In the Metadata section you see all the Entity sets and Actions expose by the OData service. We find well the action “MaterialPriceUpdate“ that we defined previously in our S/4Hana system

 

Call the OData action
In the demo when we click on the “Update” button we use the following code to call the “MaterialPriceUpdate” action.

 The executeAction method accepts 2 arguments:

  • Name of an OData Action (in our case MaterialPriceUpdate)
  • Parameters (in our case price and Material)

When this method is called then the price is udpated on the S/4Hana system side.

We can check that the price has been correctly changed in the S/4Hana system. In our system we select the “Prices” application.

Then we execute PPR0 condition type

But before we need to change the condition display. This is a very important step. By default, this set material price app is in edit mode, which will lock the price changes. So you neet to set it to display mode first.

Finally we can see that the price has changed to 1104,99 USD.

 

In Conclusion

In this blog post we learned how to create an OData Service in S/4Hana system and how to call it from an analytic application. Because we used OData v4 we were able to udpate values in S/4Hana system direclty from an analytical application. This workfow is called a close loop scenario and it’s very powerful as a business user can explore, analyze and do simulation of its data and take concrete actions from a simple application.

If you want to know more about all the OData services capabilities in Analytics Designer look at the developer handbook.

Be the first to leave a comment
You must be Logged on to comment or reply to a post.