Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Andre_Fischer
Product and Topic Expert
Product and Topic Expert

Introduction


Already a while ago I stumbled accross the problem to build an OData service that leverages CDS views as a data source and that also contains entitysets based on ABAP Code based implementation.

The question was

a) Is it possible to implement a navigation between those different implementation types?

b) If yes, how to implement the same?

To keep the story short here are the results:




























Scenario Source entity set based on Target entity set based on Result
1 RDS ABAP code based implementation Works
2 ABAP code based implementation RDS (Referenced data source) Does not work
3 ABAP code based implementation MDS (Mapped data source) Works


Though it is not possible to navigate from an entity set that is based on RDS there is a workaround by using the mapped data source (MDS) approach instead.

This approach has orignially been described in the following whitepaper (page 26, section 6.16 "Handling Odata navigation")

http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/00eaafa3-5f63-3110-72a7-f4d8044db...

In this document there is however one small important piece of Information missing.

The hint that alpha conversion for SALESORDER has to be used is missing. One has to pass the internal value (0500002030) instead of he external value (500002030) to io_query_options->add_condition_provider.

This can be achieved by calling io_tech_request_context->get_converted_source_keys( ) instead of get_source_keys( )in method SALESORDERITEMSE_GET_ENTITYSET.

Note that the method io_tech_request_context->get_converted_source_keys( ) doesn't return name-value pairs, but you need to properly type parameter ES_KEY_VALUES, like you do in method SALESORDERSET_GET_ENTITY.

Service structure


So I created an OData service Z_SADL_NON_SADL_SRV with three entity sets.
































Entity Set Based on CDS View DDIC table Navigation Property Service implementation
SEPM_I_BusinessPartner_E SEPM_I_BusinessPartner_E toSalesOrder RDS
SalesOrderSet ---- SEPM_ISOE toItem ABAP Code based implementation
SalesOrderItemSet SEPM_I_SalesOrderItem_E SEPM_ISOIE MDS


This service allows for the following navigation

Business Partner to Sales Order --> .../SEPM_I_BusinessPartner_E('100000000')/toSalesOrder

Sales Order to Sales Order Item --> SalesOrderSet('500002030')/toItem

Service implementation


The Service Builder Project is shown here



The mapping for the Sales Order Items is done via Mapped Data Source Approach



The mapping was simply generated.


RDS to ABAP code based implementation


Model provider extension class


Here we have to add a Navigation property via ABAP Code in the DEFINE method of the MPC_EXT class since it is not possible to add a navigation property using the Service Builder in this case.
class ZCL_Z_SADL_NON_SADL_MPC_EXT definition
public
inheriting from ZCL_Z_SADL_NON_SADL_MPC
create public .

public section.

methods DEFINE
redefinition .
protected section.
private section.
ENDCLASS.



CLASS ZCL_Z_SADL_NON_SADL_MPC_EXT IMPLEMENTATION.


method DEFINE.

super->define( ).

data:
lo_annotation type ref to /iwbep/if_mgw_odata_annotation, "#EC NEEDED
lo_entity_type type ref to /iwbep/if_mgw_odata_entity_typ, "#EC NEEDED
lo_association type ref to /iwbep/if_mgw_odata_assoc, "#EC NEEDED
lo_ref_constraint type ref to /iwbep/if_mgw_odata_ref_constr, "#EC NEEDED
lo_assoc_set type ref to /iwbep/if_mgw_odata_assoc_set, "#EC NEEDED
lo_nav_property type ref to /iwbep/if_mgw_odata_nav_prop. "#EC NEEDED

***********************************************************************************************************************************
* ASSOCIATIONS
***********************************************************************************************************************************

lo_association = model->create_association(
iv_association_name = 'AssocBuPa_SalesOrder' "#EC NOTEXT
iv_left_type = 'SEPM_I_BusinessPartner_EType' "#EC NOTEXT
iv_right_type = 'SalesOrder' "#EC NOTEXT
iv_right_card = 'M' "#EC NOTEXT
iv_left_card = '1' "#EC NOTEXT
iv_def_assoc_set = abap_false ). "#EC NOTEXT
* Referential constraint for association - AssocSalesOrder_Item
lo_ref_constraint = lo_association->create_ref_constraint( ).
lo_ref_constraint->add_property( iv_principal_property = 'BusinessPartner' iv_dependent_property = 'Customer' ). "#EC NOTEXT
lo_assoc_set = model->create_association_set( iv_association_set_name = 'AssocBuPa_SalesOrderSet' "#EC NOTEXT
iv_left_entity_set_name = 'SEPM_I_BusinessPartner_E' "#EC NOTEXT
iv_right_entity_set_name = 'SalesOrderSet' "#EC NOTEXT
iv_association_name = 'AssocBuPa_SalesOrder' ). "#EC NOTEXT


***********************************************************************************************************************************
* NAVIGATION PROPERTIES
***********************************************************************************************************************************

* Navigation Properties for entity - SalesOrder
lo_entity_type = model->get_entity_type( iv_entity_name = 'SEPM_I_BusinessPartner_EType' ). "#EC NOTEXT
lo_nav_property = lo_entity_type->create_navigation_property( iv_property_name = 'toSalesOrder' "#EC NOTEXT
iv_abap_fieldname = 'TOSALESORDER' "#EC NOTEXT
iv_association_name = 'AssocBuPa_SalesOrder' ). "#EC NOTEXT


endmethod.
ENDCLASS.

Data provider extension class


In the target GET_ENTITYSET method of the SalesOrders we have to implement code that handles the retrieval of the Business Partner ID if the GET_ENTITYSET method is called via Navigation from the Entity set SEPM_I_BusinessPartner_E.
data: lv_businesspartner type sepm_ibupae-businesspartner.

lt_nav_path = io_tech_request_context->get_navigation_path( ).

read table lt_nav_path into ls_nav_path with key nav_prop = 'TOSALESORDER'.

if sy-subrc = 0.

call method io_tech_request_context->get_converted_source_keys
importing
es_key_values = ls_headerdata.

lv_businesspartner = ls_headerdata-businesspartner.

select * from sepm_i_salesorder_e
into corresponding fields of table @et_entityset
up to @lv_max_index rows
where customer = @lv_businesspartner .

else.
...

ABAP code based implementation to MDS


Data provider extension class


In this case we only have to redefine several methods in the data provider extension class of our Service Builder project.

Here we have to create two member variable in the private Definition section of the DPC_EXT class.
  private section.
data mt_source_keys type /iwbep/t_mgw_tech_pairs .
data mt_navigation_info type /iwbep/t_mgw_tech_navi .

These are used to pass Information between the methods:
salesorderitemse_get_entityset and
if_sadl_gw_query_control~set_query_options

The trick is basically the the get_entityset method in the DPC_EXT class is redefined and manipulates the navigation Information that is passed to the SADL Framework before it is calling the SADL implementation in the super class.

 
method salesorderitemse_get_entityset.

data: lt_keys type /iwbep/t_mgw_tech_pairs,
ls_key type /iwbep/s_mgw_tech_pair,
ls_bp_id type bapi_epm_bp_id,
ls_headerdata type sepm_isoe.

call method io_tech_request_context->get_converted_source_keys
importing
es_key_values = ls_headerdata.

ls_key-name = 'SALESORDER'.
ls_key-value = ls_headerdata-salesorder.

append ls_key to mt_source_keys.

mt_navigation_info = io_tech_request_context->get_navigation_path( ).

try.
call method super->salesorderitemse_get_entityset

Result


The following calls are working now
/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderSet('500002030')/toItem?$format=json

 
{
"d" : {
"results" : [
{
"__metadata" : {
"id" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='10')",
"uri" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='10')",
"type" : "Z_SADL_NON_SADL_SRV.SalesOrderItem"
},
"Salesorder" : "500002030",
"Salesorderitem" : "10",
"Product" : "HT-1000",
"Transactioncurrency" : "EUR",
"Grossamountintransaccurrency" : "1137.64",
"Netamountintransactioncurrency" : "956.00",
"Taxamountintransactioncurrency" : "181.64",
"Productavailabilitystatus" : "",
"Opportunityitem" : ""
},
{
"__metadata" : {
"id" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='20')",
"uri" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='20')",
"type" : "Z_SADL_NON_SADL_SRV.SalesOrderItem"
},
"Salesorder" : "500002030",
"Salesorderitem" : "20",
"Product" : "HT-1001",
"Transactioncurrency" : "EUR",
"Grossamountintransaccurrency" : "2972.62",
"Netamountintransactioncurrency" : "2498.00",
"Taxamountintransactioncurrency" : "474.62",
"Productavailabilitystatus" : "",
"Opportunityitem" : ""
},

 

and
/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderSet('500002030')/toItem?$format=json

 
{
"d" : {
"results" : [
{
"__metadata" : {
"id" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='10')",
"uri" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='10')",
"type" : "Z_SADL_NON_SADL_SRV.SalesOrderItem"
},
"Salesorder" : "500002030",
"Salesorderitem" : "10",
"Product" : "HT-1000",
"Transactioncurrency" : "EUR",
"Grossamountintransaccurrency" : "1137.64",
"Netamountintransactioncurrency" : "956.00",
"Taxamountintransactioncurrency" : "181.64",
"Productavailabilitystatus" : "",
"Opportunityitem" : ""
},
{
"__metadata" : {
"id" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='20')",
"uri" : "https://ldai4er9.wdf.sap.corp:44300/sap/opu/odata/SAP/Z_SADL_NON_SADL_SRV/SalesOrderItemSet(Salesorder='500002030',Salesorderitem='20')",
"type" : "Z_SADL_NON_SADL_SRV.SalesOrderItem"
},
"Salesorder" : "500002030",
"Salesorderitem" : "20",
"Product" : "HT-1001",
"Transactioncurrency" : "EUR",
"Grossamountintransaccurrency" : "2972.62",
"Netamountintransactioncurrency" : "2498.00",
"Taxamountintransactioncurrency" : "474.62",
"Productavailabilitystatus" : "",
"Opportunityitem" : ""
},
{

 

Complete DPC_EXT code


class zcl_z_sadl_non_sadl_dpc_ext definition
public
inheriting from zcl_z_sadl_non_sadl_dpc
create public .

public section.

methods if_sadl_gw_query_control~set_query_options
redefinition .
protected section.

methods salesorderitemse_get_entityset
redefinition .
methods salesorderset_get_entityset
redefinition .
methods salesorderset_get_entity
redefinition .
private section.

data mt_source_keys type /iwbep/t_mgw_tech_pairs .
data mt_navigation_info type /iwbep/t_mgw_tech_navi .
endclass.



class zcl_z_sadl_non_sadl_dpc_ext implementation.


method if_sadl_gw_query_control~set_query_options.

case iv_entity_set.

when 'SalesOrderItemSet'.

if mt_navigation_info is not initial.
data(ls_nav_step) = mt_navigation_info[ 1 ].
" Is this the navigation we have to handle (SalesOrder -> SalesOrderItems)
if ls_nav_step-source_entity_type = 'SalesOrder'
and ls_nav_step-nav_prop = 'TOITEM'.
" Make SADL ignore navigation
io_query_options->remove_navigation_info( ).
" Define condition based on source entity key
data(lo_cond_factory) =
cl_sadl_cond_prov_factory_pub=>create_basic_condition_factory( ).
io_query_options->add_condition_provider(
lo_cond_factory->equals(
name = 'SALESORDER' " ABAP field name of GW property
value = mt_source_keys[ name = 'SALESORDER' ]-value ) ).
endif.
endif.

when others.

try.
call method super->if_sadl_gw_query_control~set_query_options
exporting
iv_entity_set = iv_entity_set
io_query_options = io_query_options.
catch /iwbep/cx_mgw_busi_exception .
catch /iwbep/cx_mgw_tech_exception .
endtry.

endcase.

endmethod.


method salesorderitemse_get_entityset.

data: lt_keys type /iwbep/t_mgw_tech_pairs,
ls_key type /iwbep/s_mgw_tech_pair,
ls_bp_id type bapi_epm_bp_id,
ls_headerdata type sepm_isoe.

call method io_tech_request_context->get_converted_source_keys
importing
es_key_values = ls_headerdata.

ls_key-name = 'SALESORDER'.
ls_key-value = ls_headerdata-salesorder.

append ls_key to mt_source_keys.

mt_navigation_info = io_tech_request_context->get_navigation_path( ).

try.
call method super->salesorderitemse_get_entityset
exporting
iv_entity_name = iv_entity_name
iv_entity_set_name = iv_entity_set_name
iv_source_name = iv_source_name
it_filter_select_options = it_filter_select_options
is_paging = is_paging
it_key_tab = it_key_tab
it_navigation_path = it_navigation_path
it_order = it_order
iv_filter_string = iv_filter_string
iv_search_string = iv_search_string
io_tech_request_context = io_tech_request_context
importing
et_entityset = et_entityset
es_response_context = es_response_context.
catch /iwbep/cx_mgw_busi_exception .
catch /iwbep/cx_mgw_tech_exception .
endtry.

endmethod.

method salesorderset_get_entity.

data: lt_keys type /iwbep/t_mgw_tech_pairs,
ls_key type /iwbep/s_mgw_tech_pair,
ls_headerdata type sepm_isoe.

call method io_tech_request_context->get_converted_keys
importing
es_key_values = ls_headerdata.

select single *
into corresponding fields of @er_entity
from sepm_i_salesorder_e
where salesorder = @ls_headerdata-salesorder.

endmethod.

method salesorderset_get_entityset.

data: lt_nav_path type /iwbep/t_mgw_tech_navi,
ls_nav_path type /iwbep/s_mgw_tech_navi,
ls_bp_id type bapi_epm_bp_id,
ls_headerdata type sepm_ibupae.

data: lv_osql_where_clause type string,
lv_top type i,
lv_skip type i,
lv_max_index type i,
n type i.

data: lv_businesspartner type sepm_ibupae-businesspartner.

lt_nav_path = io_tech_request_context->get_navigation_path( ).

read table lt_nav_path into ls_nav_path with key nav_prop = 'TOSALESORDER'.

if sy-subrc = 0.

call method io_tech_request_context->get_converted_source_keys
importing
es_key_values = ls_headerdata.

lv_businesspartner = ls_headerdata-businesspartner.

select * from sepm_i_salesorder_e
into corresponding fields of table @et_entityset
up to @lv_max_index rows
where customer = @lv_businesspartner .

else.

lv_osql_where_clause = io_tech_request_context->get_osql_where_clause( ).

select * from sepm_i_salesorder_e
into corresponding fields of table @et_entityset
up to @lv_max_index rows
where (lv_osql_where_clause).

endif.

endmethod.
endclass.
5 Comments