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


  •  Update October 4h, 2019 :  Replaced if_a4c_rap_query_provider which has been deprecated by if_rap_query_provider


  •  Changed code such that it also runs on the trial version where no backend systems can be called via RFC. By setting the boolean lv_abap_trial to abap_true mock data will be used.

  • Added information that RAP and custom entities are only available on premise as of SAP S/4HANA 1909


Introduction


In this blog I want to show how to build and implement a custom entity ZCE_Product_via_RFC whose query is being implemented via a class zcl_cq_product_via_rfc that reads the data from a remote system via RFC using the function modules

  • BAPI_EPM_PRODUCT_GET_LIST

  • BAPI_EPM_PRODUCT_GET_DETAIL


The class only others one method select that is called for both queries and GET requests for single products.

Please note that RAP and custom entities are only available on premise as of SAP S/4HANA 1909.

Blog series


This blog is part of a blog series about developing a side-by-side Extension for on-premise SAP Systems in SAP Cloud Platform ABAP Environment using RFC communication

BAPI_EPM_PRODUCT_GET_LIST


FUNCTION bapi_epm_product_get_list
IMPORTING
VALUE(max_rows) TYPE bapi_epm_max_rows OPTIONAL
TABLES
headerdata LIKE bapi_epm_product_header OPTIONAL
selparamproductid LIKE bapi_epm_product_id_range OPTIONAL
selparamsuppliernames LIKE bapi_epm_supplier_name_range OPTIONAL
selparamcategories LIKE bapi_epm_product_categ_range OPTIONAL
return LIKE bapiret2 OPTIONAL.

BAPI_EPM_PRODUCT_GET_DETAIL


FUNCTION bapi_epm_product_get_detail
IMPORTING
VALUE(product_id) TYPE bapi_epm_product_id
EXPORTING
VALUE(headerdata) TYPE bapi_epm_product_header
TABLES
conversion_factors LIKE bapi_epm_product_conv_factors OPTIONAL
return LIKE bapiret2 OPTIONAL.

Please note that both function modules need the structure BAPI_EPM_PRODUCT_HEADER.

Step 1: Create a class


We start our implementation by creating a class zcl_cq_product_via_rfc that implements the method select of the interface if_rap_query_provider.

We will continue with the implementation later on.
CLASS zcl_cq_product_via_rfc DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .

PUBLIC SECTION.
INTERFACES if_rap_query_provider.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS zcl_cq_product_via_rfc IMPLEMENTATION.
METHOD if_rap_query_provider~select.

ENDMETHOD.

ENDCLASS.

Step 2: Create custom entity


In ABAP in Eclipse we create a custom entity by creating a new Data Definition. In the wizard you can select the default template or start with the template for a custom entity with parameters.


In either case you have to change the coding such that it looks like follows. Please notice that the custom entity does not take any parameters and that we have added the root Statement.
@EndUserText.label: 'product demo data read via rfc from on prem'
@ObjectModel: {
query: {
implementedBy: 'ABAP:zcl_cq_product_via_rfc'
}
}

define root custom entity ZCE_Product_via_RFC
{
...

}

With the Annotation @ObjectModel.query.implementedBy we have to provide the name of the class that has been created in step 1 which implements the select method of the interface if_rap_query_provider.

The tricky part is now the creation of the DDL source code of our custom entity. Since there is no design time support available for this yet I have developed a class that takes the name of the structure BAPI_EPM_PRODUCT_HEADER that you can run via F9.

The source code of the report zcl_rfc_custom_entity_helper is available in my following blog How to generate the DDL source code for custom entities that are implemented by remote function call...

In this case where we want to create the custom entity in the SAP CP ABAP Environment System you have to run the class in the backend system where the RFC function module is being called since the structure BAPI_EPM_PRODUCT_HEADER is not available in the SAP CP ABAP Environment system.

In future versions of SAP S/4HANA it is planned to have the ABAP RESTful programming model available so that in this case the report can be run in the same system.

When running the class via F9 we get the following output in the console.



We can take the code and copy and paste it in the our custom entity.

In addition we will add some UI annotations.
@EndUserText.label: 'product demo data read via rfc from on prem'
@ObjectModel: {
query: {
implementedBy: 'ABAP:zcl_cq_product_via_rfc'
}
}

define root custom entity ZCE_Product_via_RFC
{
@UI.facet : [
{
id : 'Product',
purpose: #STANDARD,
type : #IDENTIFICATION_REFERENCE,
label : 'Product',
position : 10 }
]
// DDL source code for custom entity for BAPI_EPM_PRODUCT_HEADER
// generated on: 20190214 at:142338
@UI : {
lineItem : [{position: 10, importance: #HIGH}],
identification: [{position: 10}],
selectionField: [{position: 10}]
}
key ProductId : abap.char( 10 );
TypeCode : abap.char( 2 );
@UI : {
lineItem : [{position: 20, importance: #HIGH}],
identification: [{position: 20}],
selectionField: [{position: 20}]
}
Category : abap.char( 40 );
@UI : {
lineItem : [{position: 30, importance: #HIGH}],
identification: [{position: 30}]
}
Name : abap.char( 255 );
@UI : {
identification: [{position: 40}]
}
Description : abap.char( 255 );
SupplierId : abap.char( 10 );
SupplierName : abap.char( 80 );
TaxTarifCode : abap.int1;
@Semantics.unitOfMeasure: true
MeasureUnit : abap.unit( 3 );
@Semantics.quantity.unitOfMeasure: 'WeightUnit'
WeightMeasure : abap.quan( 13, 3 );
@Semantics.unitOfMeasure: true
WeightUnit : abap.unit( 3 );
@UI : {
lineItem : [{position: 50, importance: #HIGH}],
identification: [{position: 50}]
}
Price : abap.dec( 23, 4 );
@Semantics.currencyCode: true
CurrencyCode : abap.cuky( 5 );
@Semantics.quantity.unitOfMeasure: 'DimUnit'
Width : abap.quan( 13, 3 );
@Semantics.quantity.unitOfMeasure: 'DimUnit'
Depth : abap.quan( 13, 3 );
@Semantics.quantity.unitOfMeasure: 'DimUnit'
Height : abap.quan( 13, 3 );
@Semantics.unitOfMeasure: true
DimUnit : abap.unit( 3 );
ProductPicUrl : abap.char( 255 );
}

 

Step 3: Implement query


When implementing the query you have to know that the implementation class only offers one method which is called select.

The code first checks if data is being requested or not.
io_request->is_data_requested( ).

Since the same method is used for queries and single selects we have to find out whether a single select has been performed, that means whether a call like the following has been sent by the OData client:

/…/Products('HT-1000')

In this case the table lt_filter_cond will only contain one entry for the key field PRODUCTID. This is checked via the method is_key_filter( ).

Since the structure BAPIRET2 is not yet on the whitelist of released structures I have used the same report mentioned above to generate a type definition ty_bapiret2.

It is important to note that you must return the number of entries found, also if a single request is returned because otherwise no data will be shown in the object page. This is done by the statement:
io_response->set_total_number_of_records( lines( lt_product ) ).

This number must also not exceed the number of entries that have been requested by the client or the number that has been enforced by the fhe framework if no $top and $skip has been used.

The data both for the single read as well as for queries is returned as a internal table lt_return to the framework.
io_response->set_data( lt_product ).

 
CLASS zcl_cq_product_via_rfc DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .

PUBLIC SECTION.
INTERFACES if_rap_query_provider.
PROTECTED SECTION.
PRIVATE SECTION.

METHODS is_key_filter
IMPORTING it_filter_cond TYPE if_rap_query_filter=>tt_name_range_pairs
RETURNING VALUE(rv_is_key_filter) TYPE abap_bool.

METHODS get_orderby_clause
IMPORTING it_sort_elements TYPE if_rap_query_request=>tt_sort_elements
RETURNING VALUE(rv_orderby_string) TYPE string.

ENDCLASS.



CLASS zcl_cq_product_via_rfc IMPLEMENTATION.
METHOD if_rap_query_provider~select.



"variables needed to call BAPI's
DATA lt_product TYPE STANDARD TABLE OF zce_product_via_rfc.
DATA lt_result TYPE STANDARD TABLE OF zce_product_via_rfc.
DATA ls_product TYPE zce_product_via_rfc.

"key for BAPI_GET_DETAIL
TYPES : BEGIN OF product_rfc_key_type,
productid TYPE zce_product_via_rfc-productid,
END OF product_rfc_key_type.
DATA ls_product_rfc_key TYPE product_rfc_key_type.

"select options
DATA lt_filter_ranges_productid TYPE RANGE OF zce_product_via_rfc-productid.
DATA ls_filter_ranges_productid LIKE LINE OF lt_filter_ranges_productid.
DATA lt_filter_ranges_supplier TYPE RANGE OF zce_product_via_rfc-suppliername.
DATA ls_filter_ranges_supplier LIKE LINE OF lt_filter_ranges_supplier.
DATA lt_filter_ranges_category TYPE RANGE OF zce_product_via_rfc-category.
DATA ls_filter_ranges_category LIKE LINE OF lt_filter_ranges_category.

"######################### ABAP source code ################################
" ABAP source code for type definition for BAPIRET2
" generated on: 20190301 at: 165321 in: UIA
TYPES : BEGIN OF ty_bapiret2,
type TYPE c LENGTH 1,
id TYPE c LENGTH 20,
number TYPE n LENGTH 3,
message TYPE c LENGTH 220,
logno TYPE c LENGTH 20,
logmsgno TYPE n LENGTH 6,
messagev1 TYPE c LENGTH 50,
messagev2 TYPE c LENGTH 50,
messagev3 TYPE c LENGTH 50,
messagev4 TYPE c LENGTH 50,
parameter TYPE c LENGTH 32,
row TYPE i,
field TYPE c LENGTH 30,
system TYPE c LENGTH 10,
END OF ty_bapiret2.

"DATA lt_return TYPE STANDARD TABLE OF bapiret2.
DATA lt_return TYPE STANDARD TABLE OF ty_bapiret2.
"variables generic for implementation of custom entity
DATA lv_details_read TYPE abap_bool.
"DATA ls_sel_opt TYPE /iwbep/s_cod_select_option.

* ensure: in case of a single record is requested (e.g. data for a detail page),
* only one record is returned and SET_TOTAL_NUMBER_OF_RECORDS = 1
DATA lv_orderby_string TYPE string.
DATA lv_select_string TYPE string.
"In the trial version we cannot call RFC function module in backend systems
DATA(lv_abap_trial) = abap_true.

IF lv_abap_trial = abap_false.

TRY.
DATA(lo_rfc_dest) = cl_rfc_destination_provider=>create_by_cloud_destination(
i_name = |S4H_ON_PREM_RFC|
i_service_instance_name = |OutboundCommunication| ).
DATA(lv_rfc_dest_name) = lo_rfc_dest->get_destination_name( ).

CATCH cx_rfc_dest_provider_error INTO DATA(lx_dest).

ENDTRY.

ENDIF.

TRY.

IF io_request->is_data_requested( ).

TRY.
"get and add filter
DATA(lt_filter_cond) = io_request->get_filter( )->get_as_ranges( ). " get_filter_conditions( ).

CATCH cx_rap_query_filter_no_range INTO DATA(lx_no_sel_option).

"@todo :
" raise an exception that the filter that has been provided
" cannot be converted into select options
" here we just continue

ENDTRY.

DATA(lv_top) = io_request->get_paging( )->get_page_size( ).
DATA(lv_skip) = io_request->get_paging( )->get_offset( ).
DATA(lt_fields) = io_request->get_requested_elements( ).
DATA(lt_sort) = io_request->get_sort_elements( ).



" $orderby was called
IF lt_sort IS NOT INITIAL.
CLEAR lv_orderby_string.
LOOP AT lt_sort INTO DATA(ls_sort).
IF ls_sort-descending = abap_true.
CONCATENATE lv_orderby_string ls_sort-element_name 'DESCENDING' INTO lv_orderby_string SEPARATED BY space.
ELSE.
CONCATENATE lv_orderby_string ls_sort-element_name 'ASCENDING' INTO lv_orderby_string SEPARATED BY space.
ENDIF.
ENDLOOP.
ELSE.
" lv_orderby_string must not be empty.
lv_orderby_string = 'PRODUCTID'.
ENDIF.

" $select handling
IF lt_fields IS NOT INITIAL.
CONCATENATE LINES OF lt_fields 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 = '*'.
ENDIF.

"check if filter condition is for a single read
lv_details_read = is_key_filter( lt_filter_cond ).

"single read
IF lv_details_read = abap_true.

READ TABLE lt_filter_cond WITH KEY name = 'PRODUCTID' INTO DATA(ls_productid_filter_key).
IF sy-subrc = 0 AND lines( ls_productid_filter_key-range ) = 1.
READ TABLE ls_productid_filter_key-range INTO DATA(ls_id_option) INDEX 1.
IF sy-subrc = 0 AND ls_id_option-sign = 'I' AND ls_id_option-option = 'EQ' AND ls_id_option-low IS NOT INITIAL.
"read details for single record in list
ls_product_rfc_key-productid = ls_id_option-low.

IF lv_abap_trial = abap_true.

"fill structure with test data
ls_product = VALUE #( productid = ls_product_rfc_key-productid name = 'Notebook' ).

ELSE.

CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'
DESTINATION lv_rfc_dest_name
EXPORTING
product_id = ls_product_rfc_key
IMPORTING
headerdata = ls_product
TABLES
return = lt_return.


ENDIF.

APPEND ls_product TO lt_product.

ENDIF.
ENDIF.

"the request is a GET_LIST request and a filter has been provided
ELSE .

"-get filter for ProductID
READ TABLE lt_filter_cond WITH KEY name = 'PRODUCTID' INTO DATA(ls_productid_cond).
IF sy-subrc EQ 0.
LOOP AT ls_productid_cond-range INTO DATA(ls_sel_opt_productid).
MOVE-CORRESPONDING ls_sel_opt_productid TO ls_filter_ranges_productid.
INSERT ls_filter_ranges_productid INTO TABLE lt_filter_ranges_productid.
ENDLOOP.
ENDIF.

"-get filter for SUPPLIERNAME
READ TABLE lt_filter_cond WITH KEY name = 'SUPPLIERNAME' INTO DATA(ls_suppliername_cond).
IF sy-subrc EQ 0.
LOOP AT ls_suppliername_cond-range INTO DATA(ls_sel_opt_suppliername).
MOVE-CORRESPONDING ls_sel_opt_suppliername TO ls_filter_ranges_supplier.
INSERT ls_filter_ranges_supplier INTO TABLE lt_filter_ranges_supplier.
ENDLOOP.
ENDIF.

"-get filter for CATEGORY
READ TABLE lt_filter_cond WITH KEY name = 'CATEGORY' INTO DATA(ls_category_cond).
IF sy-subrc EQ 0.
LOOP AT ls_category_cond-range INTO DATA(ls_sel_opt_category).
MOVE-CORRESPONDING ls_sel_opt_category TO ls_filter_ranges_category.
INSERT ls_filter_ranges_category INTO TABLE lt_filter_ranges_category.
ENDLOOP.
ENDIF.

IF lv_abap_trial = abap_true.

"fill table with demo data
lt_product = VALUE #( ( productid = 'HT-1000' name = 'Notebook' )
( productid = 'HT-1001' name = 'Aotebook' )
( productid = 'HT-1002' name = 'Notebook' )
( productid = 'HT-1003' name = 'Notebook' )
( productid = 'HT-1004' name = 'Notebook' )
( productid = 'HT-1005' name = 'Notebook' )
).
ELSE.

CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_LIST'
DESTINATION lv_rfc_dest_name
* EXPORTING
* max_rows =
TABLES
headerdata = lt_product
selparamproductid = lt_filter_ranges_productid
selparamsuppliernames = lt_filter_ranges_supplier
selparamcategories = lt_filter_ranges_category
return = lt_return.

ENDIF.



ENDIF.

"Apply all query options to filter so that also filter options are supported that
"are not available as filter parameters for the RFC function modules being used
"Also ensure that not more elements are returned than have been
"requested by the framework

IF lv_details_read = abap_false.

DATA(dyn_clause) = io_request->get_filter( )->get_as_sql_string( ).

SELECT (lv_select_string) FROM @lt_product AS products
WHERE (dyn_clause)
ORDER BY (lv_orderby_string)
INTO CORRESPONDING FIELDS OF TABLE @lt_result
UP TO @lv_top ROWS
OFFSET @lv_skip .

IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lt_product ).
ENDIF.
io_response->set_data( lt_result ).

ELSE.

io_response->set_total_number_of_records( lines( lt_product ) ).
io_response->set_data( lt_product ).

ENDIF.




ELSE.
"no data has been requested
ENDIF.

"error handling
CATCH cx_rap_query_provider INTO DATA(lx_exc).


ENDTRY.

ENDMETHOD.

METHOD is_key_filter.

"check if the request is a single read
READ TABLE it_filter_cond WITH KEY name = 'PRODUCTID' INTO DATA(ls_productid_filter_key).
IF sy-subrc = 0 AND lines( ls_productid_filter_key-range ) = 1.
READ TABLE ls_productid_filter_key-range INTO DATA(ls_id_option) INDEX 1.
IF sy-subrc = 0 AND ls_id_option-sign = 'I' AND ls_id_option-option = 'EQ' AND ls_id_option-low IS NOT INITIAL.
"read details for single record in list
rv_is_key_filter = abap_true.
ENDIF.
ENDIF.

ENDMETHOD.

METHOD get_orderby_clause.

ENDMETHOD.

ENDCLASS.

 

Step 4: Create Service Definition and Service Binding


We can now create a Service definition
@EndUserText.label: 'Read product demo data via RFC'
define service ZSD_PRODUCT_VIA_RFC {
expose ZCE_Product_via_RFC;
}

 

and a service binding.

 



 

Result


Using the preview functionality



we can see that the app supports filtering as indicated by the @UI annotations.



 
57 Comments
yevgen_trukhin
Advisor
Advisor
0 Kudos
Worked for me with another RFC.

Thanks for the detailed blog.
joyal_laj
Participant
0 Kudos
I followed this blog, but the filter conditions is not working.
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
Then I suggest that you create a question in the Q&A area and share more details about the implementation you did.

https://answers.sap.com/questions/ask.html
Sapcoder
Explorer
andre.fischer - how are associations managed in this scenario?  I have a scenario where I need to read a BAPI to get header details and then based on that I need to call another BAPI to get the details.  It is a 1 to n relationship.

From looking at the documentation it does not look like associations are supported for custom entities.
julieplummer20
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Joyal,

I have written a test class, based on Andre’s, where I implement only filtering, not order by or Get Details (for single record). If you could create a question and link to it, I can post the code. (It is rather long to post here.) Or you can wait for my step-by-step tutorial.

Alternatively, I have added a new tutorial to the mission, calledhttps://developers.sap.com/tutorials/abap-environment-custom-entity-debug.html "Inspect your class in the ABAP Debugger." If you step through your class, you can perhaps see where it's going wrong.

HTH

Julie

former_member719109
Discoverer
0 Kudos
Hallo,

First let me explain the scenario : i want create a fiori element application in S/4HANA on promise system. The data should come from another SAP system ECC.

CDS and Odata service were created on the providing system ECC. I have trouble with consuming the data from S/4 HANA system.

I am unable to create a service consumption model after downloading the edmx file of the odata model.

Also i was not abale to create a remote client proxy class using the tutorial https://developers.sap.com/tutorials/abap-environment-a4c-create-proxy.html

For instance the class
cl_web_odata_client_factory

is not available on the system.

 

We havea a S/4 HANA on promise system with the following components:

 



 

Is there any workaround ? another possibly solution.I know i can use a remote function module to solve this problem, but then i have to program more in stead of using the standard existing functionality like
 DATA(lo_read_request) = lo_client_proxy->create_resource_for_entity_set( 'XXX' )->create_request_for_read( ).

Kind regards,

Andre_Fischer
Product and Topic Expert
Product and Topic Expert
former_member719109
Discoverer
0 Kudos
Thank you Andre for your reply.. I would like to use the RAP programming model using custom entity.

Is this possible ? The blog you mentioned is another approach
0 Kudos
hi,

We can specify $top for fetching more records than the default Llimit. But when a custom entity is used for providing data for SAC(Analytics) , the default limit (get_page_size()) is 1000. Is there any method to change the limit.

 
Jigang_Zhang张吉刚
Active Contributor
0 Kudos
Thanks for sharing this, I will use this as template : P
former_member716470
Discoverer
0 Kudos
Thanks Andre.

A number of the element was causing dump, now it works fine with your template. Thank you.
          "Also ensure that not more elements are returned than have been
"requested by the framework
0 Kudos
Hi Andre,

Thanks for this excellent blog.

I have a question: How can I extend a CDS custom entity?

Best regards,

Krishnan
0 Kudos
Hi andre.fischer

Is it possible to expose a custom root entity to SAC (Analytics)? Are there any additional annotations required? Also, is there any documentation to expose the custom root entity to SAC?

 

Regards

Geet
mahe2598
Member
0 Kudos
In io_response->set_data ( tbl ) ...

When I tried to create an fiori app with grid display, If the table tbl has more than 120 records , it is not working. Can anyone please help me on this!
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos
What do you mean by "it is not working" ?

Does it show no values, less values , ...

Please have in mind that your coding has to take care of $top and $skip.
 DATA(lv_top)     = io_request->get_paging( )->get_page_size( ).
DATA(lv_skip) = io_request->get_paging( )->get_offset( ).
anna74
Explorer
0 Kudos
Great blog. Can you tell me then using this custom entity how dynamic hide fields?
0 Kudos

Hi Andre,

thanks for your helpful blog post!

I would like to report a small bug. In case of paging is implemented, it should be...

io_response->set_total_number_of_records( lines( lt_product ) ).

instead of

io_response->set_total_number_of_records( lines( lt_result ) ).

...

to still give the info about the total quantity of possible rows.

 

Regards & Thanks

Kai

 

Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Kai,

Good catch.

I just fixed the code and added an IF ... ENDIF. clause because the total number of records might not always be requested.

This also shows the problem of implementing the query for a custom entity. The developer has to ensure that the correct data is returned rather than being able to rely on the SADL framework to do the heavy lifting.

Kind regards,

Andre

hendrikp
Explorer
0 Kudos
Hi Andre,

the sorting string part needs kommas between them otherwise it dumps.

This works:
      IF sort_order IS NOT INITIAL.
CLEAR lv_orderby_string.
DATA komma TYPE c LENGTH 1 VALUE ','.
LOOP AT sort_order ASSIGNING FIELD-SYMBOL(<sort>).
IF sy-tabix = lines( sort_order ).
komma = space.
ENDIF.
IF <sort>-descending = abap_true.
lv_orderby_string = |{ lv_orderby_string }{ <sort>-element_name } DESCENDING{ komma }|.
ELSE.
lv_orderby_string = |{ lv_orderby_string }{ <sort>-element_name } ASCENDING{ komma }|.
ENDIF.
ENDLOOP.
ELSE.
" lv_orderby_string must not be empty.
lv_orderby_string = |PRODUCTID|.
ENDIF.

 

 
0 Kudos
Hi Andre,

 

I have scenario like summary and Detail page report in RAP,

 

We have Created summary report with  Custom entity, we are facing issue with navigate from custom entity to CDS Detail report.

 

how can we Create this case.

 

With Regards,

Sridher
0 Kudos
Hello

 

Having the same issue I wonder how you have been able to resolve it ?

 

Kind Regards

Lamia
0 Kudos
Hi

We are having same issue ..I wonder how you have resolved it ?

Regards

Lamia
philippdoelker
Participant
0 Kudos
Hello andre.fischer,

thanks for that interesting blog post. Is there any way to provide a specific HTTP Status Code (e.g. 404 not found) to the caller in case my query logic cannot find the requested ressources?

Something similar to the following in an "old fashioned" SEGW service:


RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
http_status_code = /iwbep/cx_mgw_busi_exception=>gcs_http_status_codes-not_found.

When I raise an exception inheriting from the CX_RAP_QUERY_PROVIDER it seems I cannot influence the status code that will be provided to the caller.

Thanks a lot,

Philipp

 
former_member207873
Participant
0 Kudos

Hello andre.fischer,

How to provide value help on the selection screen of Custom entity ? @Consumption.valueHelpDefinition does not seems to work.

MatthiasH
Explorer
Hi,

with @Consumption.valueHelpDefinition

Example:
  @Consumption.valueHelpDefinition: [{ entity : {name: 'I_LogicalSystem', element: 'LogicalSystem'} }]
key LOGSYS : logsys; //abap.char( 10 );
former_member207873
Participant
0 Kudos
Hello matthias.htter ,

Has this worked for you ? For me the app does not even show up when the
@Consumption.valueHelpDefinition

is given .
MatthiasH
Explorer

Yes, it works definitly. Screenshots:


And app:


Maybe your release is not sufficient?? I´m running SAP S/4HANA 2021 onPremise.
former_member207873
Participant
0 Kudos

Hello matthias.htter ,

Thank you very much for your answer. I just tried your example , it works great for me as well. 🙂 I have a material field and I am giving

@Consumption.valueHelpDefinition: [{ entity : {name: 'I_PRODUCT', element: 'Product'} }]

and it does not even show up . But if i give logical system as you give, it works great.

udita10
Explorer
0 Kudos
Hi andre.fischer,

If one is using Search as one of the query options in odata service on a custom entity .
How can that be handled in the abap class?

 

Thanks,

Udita
WRoeckelein
Active Participant
0 Kudos

udita.saklani ,

I'm adding like clauses to the filter string for each field I want to be searchable:

    IF search_string IS NOT INITIAL.
DATA(like) = | LIKE '%{ cl_abap_dyn_prg=>escape_quotes( search_string ) }%' | ##NO_TEXT.
where_clause = where_clause && COND #( WHEN where_clause IS NOT INITIAL THEN ' AND' ) && | ( field1{ like } OR field2{ like } OR field3{ like } )| ##NO_TEXT.
ENDIF.
Micha_Reisner
Explorer
0 Kudos
Hi andre.fischer,

we implemented a custom entity following your approach. In our backend class we are using an existing function module (as in your example) which could return several hundreds of records.
We build the UI using Fiori elements. But with the app built via FE we didn't find a way to stop the paging/growing mechanism. The variable CL_RAP_QUERY_REQUEST->MO_PAGING  comes always with 'X' (and $top is filled in addition).

Do you know a way to influence this paging behavior in order to stop paging and instead be able to return the full result in one single call? This is also critical for the Excel export feature as this is calling the backend in page sizes. When using a function module this normally will do a complete data fetch and not support a "from/to" approach like in SQL .

Thanks for any comment on this.

Regards
Michaela
former_member847900
Discoverer
0 Kudos

Hi andre.fischer

 

I'm implementing the custom CDS entity in un-managed scenario for one of my application using action.

I'm reading the selection screen parameters and passing it to the the API to fetch the data in the select method. My API is having complex logic to process, before it gives the final data.

I see that there is an issue with the performance, for every user action my select method of the query provider class gets executed. Even if it is small action (Like selecting/de-selecting a few fields OR sorting with any field) my entire API code is executed again.

 

Is there any way to improve the performance. by having some buffer ?

 

Hello  pavankumarjagadeesh89 ramjee.korada parichaypatra  abhi.sharma05   julie.plummer   can u also pls share your thoughts ?

Best Regards.

Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Vijay,

writing data to the database in unmanaged queries is discuraged (if not even forbidden) in implementations of such custom entities at SAP.

Maybe it is possible to add buffering on table level for the data that you are requesting?

Kind regards,

Andre
former_member847900
Discoverer
0 Kudos
Dear andre.fischer

 

Thanks for the response.

In my scenario, before displaying the final data there are select queries on many standard table along with some complex logic. Since it is not a single table I can't use the buffering on table level.

Basically when the user fills the filter criteria and hit on GO button for first time my query provider class logic gets executed (with some complex logic wrapped inside the API) and result is shown to the user but when user makes a small action on UI screen like SORTING the data on a field or selecting/de-selecting a field then again the query provider class gets called and this complex logic executed again as if it is a new request because of this I'm facing the performance issues.

I want to understand if there is anyway to prevent the execution of this complex logic again and again, and use the already retrieved data to improve the performance.

 

Best Regards.

 
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos
There is a way to set a number of maxitems that should be displayed in one run in Fiori Elements.

But there is a hard limit set by the SAP Gateway Framework not to show more than 5000 Elements.


@ObjectModel.query.implementedBy: 'ABAP:ZCL_CE_PARENT'


@UI: {


headerInfo: {


typeName: 'parent',


typeNamePlural: 'parents',


title: {


type: #STANDARD,


label: 'parent',


value: 'SalesOrderID'


}


}


,


presentationVariant: [ {


sortOrder: [ {


by: 'SalesOrderID',


direction: #DESC


} ],


visualizations: [ {


type: #AS_LINEITEM


} ],


maxItems: 5010


} ]


}




But I am not sure whether it makes sense to display such huge lists in a UI anyway.

Which release are you working on?

Kind regards,

Andre

Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos
Which release are you working on?
Micha_Reisner
Explorer
Hi Andre
Thanks for your reply.

We tried the visualizations setting maxitems, but without any success.
Especially with a grid table this setting seems to be ignored. And furthermore :

In the end the killer was the excel download. It seems that the excel feature in FE always requests a number of 200 records without taking into account any table or variant settings.

I wonder how code-reuse should work if the UI /Odata-framework is so rigid.
Typically classical function modules, BAPIs etc. are not realized in the way to take top/paging requests into account. As a consequence the whole data read logic will called again and again for the complete amount of data. E.g. for an excel download of 1000 records in total it will produce 5 calls to the backend. And with an classical function module this will repeat the same data collection each time. So I don't see that reusing existing coding with custom entities makes much sense if the data amount gets bigger than 200 records. Unfortunately. Code reuse would be really nice.

Concerning your point whether it make sense - we do have the cases where the power users need a kind of reporting functionality and are forced to use a Fiori frontend due to SAP's proposed strategy.
They tend to export the data (e.g. in order to be able to use copy/paste functionality on their data) and they filter the data according to their needs. Nethertheless we  can not avoid that they request unfiltered data or that the result has more than 200 records.

We are on S/4 Hana 2020 SP01.

Regards
Michaela
0 Kudos
Hello,

Can you also use the same custom entity for POST request? i.e. I have a custom entity for Sales Order Header, which includes a field that contains the structure of Sales Order Items.. using this, it works fine when I use the READ operation of API_SALES_ORDER_SRV entity A_SALESORDER..

Now I want to create a sales order via the same API and entity.. can i use the same custom entity with it? Or the approach must be a creation of RAP BOs (root view entity, projection view, behavior definition & implementation, etc..)
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Olivia,

you must not perform commits within the read operation of a custom entity.

For update operations you would have to create an unmanaged behavior definition for the custom entity.

Projection views are not allowed to be created on top of custom entities.

Kind regards,

Andre
cap_ricardo
Explorer
0 Kudos
Hi Guys,

 

How to define search help for fields ekpo-KONNR, ekko-EBELN, EDOBRCTEINCOMING-ACCESSKEY, EDOBRCTEINCOMING-CTE_NUMBER and others?

this way does not work:

@Consumption.valueHelpDefinition: [{ entity: { name: 'EKPO', element: 'KONNR' } }]

 

Thanks.
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos
It doesn't work because you must use cds views,  not the table.
md_zohaib_akhter
Explorer
0 Kudos
Hi Andre,

 

How can we associate custom view entity to parent entity?

 

Thanks
former_member592528
Discoverer
0 Kudos
Great Blog!!

Is there a way to add custom error/success/warning message in the query implementation class?
cap_ricardo
Explorer
0 Kudos
Hi Guy, How i can make a Fiori App with Navigation between Custom Entity CDSs? Thanks!
axel_brke3
Explorer
0 Kudos
Having the same issue. Did you find a solution? It's always code 501
juanlee337
Explorer
0 Kudos
is there a way to save the output data  for the next call instead of querying the same list everytime?

This seems to retrieve same data every time user  scrolls for next list and send data to UI as a subset. I tried saving the data the static value  and singleton class but these get cleared on every call.

 
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
No, but the underlying database framework will probably cache data that is read several times.

If possible you should investigate if there are CDS views available that can be used instead to read your data rather than using RFC function modules.

When having used RFC function modules in SEGW you would have run into the same problem.
Frank1
Participant
0 Kudos
Hi Michaela,

That's an interesting scenario, how did you solve it? Thank you!
Frank1
Participant
0 Kudos

Hi Andre,

Yor are always posting very informative blogs. Thank you for sharing.

Regarding Vijay's above performance issue, how to solve it from your perspective? This is a very common scenario for every custom entity, they all face this problem.

Here you ask which release, and why the release is relevant to this issue? Do you mean with the right release, you recommend using below approach you posted below?

December 10, 2023 at 4:02 pm

No, but the underlying database framework will probably cache data that is read several times.

If possible you should investigate if there are CDS views available that can be used instead to read your data rather than using RFC function modules.

When having used RFC function modules in SEGW you would have run into the same problem.

Micha_Reisner
Explorer
0 Kudos
Hi Frank
We stopped trying to use the existing function module / custom entity. Instead we wrote a complete new RAP data model with "normal" CDS entities. The gain in performance was huge.
So from my point of view code reuse only makes sense to a small extent in specific cases.