on 2024 Aug 02 2:48 PM
Hey everyone,
I have followed this blog :
SAP ABAP RAP : Custom Entities with compositions relationship in a Fiori Elements App
And now I have a problem
In our system (S4 2022, 757 Basis), we have tried to reproduce this development.
We have two custom entities:
- One ZC_OMNIBUS_TILE
@EndUserText.label: 'Data def tile omnibus'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_OMNIBUS_TILE'
@UI.headerInfo: {
typeName: 'Omnibus',
typeNamePlural: 'Omnibus',
title: {
type: #STANDARD,
value: 'matnr'
},
description: {
type: #STANDARD,
value: 'msgtxt'
}
}
define root custom entity ZC_OMNIBUS_TILE
{
@ui.facet : [{
id : 'General',
purpose : #STANDARD,
parentId : '',
position : 10,
isPartOfPreview: true,
label : 'General',
type : #COLLECTION,
targetQualifier: 'General'
},
{
id : 'BasicInfo',
purpose : #STANDARD,
parentId : 'General',
position : 10,
isPartOfPreview: true,
label : 'Basic Info',
type : #FIELDGROUP_REFERENCE,
targetQualifier: 'QFBasicInfo'
},
{
id : 'ProductInfo',
purpose : #QUICK_VIEW,
parentId : 'General',
position : 20,
isPartOfPreview: true,
label : 'Product Info',
type : #FIELDGROUP_REFERENCE,
targetQualifier: 'QFProductInfo'
},
{
id : 'Items',
purpose : #STANDARD,
position : 30,
label : 'Items',
type : #LINEITEM_REFERENCE,
targetElement: '_OmnibusHeaderHisto'
}]
@ui.lineItem : [{ position: 10 }]
@ui .identification : [{position: 10}]
@ui.fieldGroup : [ { type: #STANDARD, position: 10 , qualifier: 'QFBasicInfo' } ]
key row_id : lineid;
@ui.lineItem : [{ position: 20 }]
@ui.selectionField : [{position: 20}]
@ui.identification : [{position: 20}]
@ui.fieldGroup : [ { type: #STANDARD, position: 20 , qualifier: 'QFProductInfo' } ]
key matnr : matnr;
@ui.lineItem : [{ position: 30 }]
@ui.selectionField : [{position: 30}]
@ui.identification : [{position: 30}]
@ui.fieldGroup : [ { type: #STANDARD, position: 30 , qualifier: 'QFProductInfo' } ]
werks : werks_d;
@ui.selectionField : [{position: 40}]
@Consumption.filter.selectionType: #SINGLE
@ui.fieldGroup : [ { type: #STANDARD, position: 40 , qualifier: 'QFProductInfo' } ]
sales_org : vkorg;
@ui.lineItem : [{ position: 50 }]
@ui.identification : [{position: 50}]
@Semantics.amount.currencyCode: 'currency'
@ui.fieldGroup : [ { type: #STANDARD, position: 50 , qualifier: 'QFProductInfo' } ]
kbetr_promo : ze_kbetr_promo;
@ui.lineItem : [{ position: 60 }]
@ui.selectionField : [{position: 60}]
@Consumption.filter.selectionType: #SINGLE
@ui.identification : [{position: 60}]
@ui.fieldGroup : [ { type: #STANDARD, position: 60 , qualifier: 'QFProductInfo' } ]
datab_promo : /sapapo/pm_startdate;
@ui.lineItem : [{ position: 70 }]
@ui.identification : [{position: 70}]
@ui.fieldGroup : [ { type: #STANDARD, position: 70 , qualifier: 'QFProductInfo' } ]
datbi_promo : /sapapo/pm_lastdate;
@ui.lineItem : [{ position: 80}]
@ui.identification : [{position: 80}]
@ui.fieldGroup : [ { type: #STANDARD, position: 80 , qualifier: 'QFProductInfo' } ]
@Semantics.amount.currencyCode: 'currency'
kbetr_omnibus : ze_kbetr_omnibus;
@ui.lineItem : [{ position: 90}]
@ui.identification : [{position: 90}]
@ui.fieldGroup : [ { type: #STANDARD, position: 90 , qualifier: 'QFProductInfo' } ]
msgtxt : msgtxt;
@Semantics.currencyCode
currency : waers;
_OmnibusHeaderHisto : composition [0..*] of ZC_OMNIBUS_TILE_HISTO;
}
- and the other for Object navigation which will have the details (several lines for this "header")
@EndUserText.label: 'Data def tile omnibus for histo part'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_OMNIBUS_TILE'
@UI.headerInfo: {
typeName: 'OmnibusHisto',
typeNamePlural: 'OmnibusHisto',
title: {
type: #STANDARD,
value: 'matnr'
},
description: {
type: #STANDARD,
value: 'tabname'
}
}
define custom entity ZC_OMNIBUS_TILE_HISTO
{
@ui.facet : [{
id : 'General',
purpose : #STANDARD,
position: 10,
label : 'Omnibus',
type : #IDENTIFICATION_REFERENCE
}]
@ui.hidden : true
key row_id : lineid;
@ui.lineItem : [{ position: 10 }]
@ui.identification : [{ position: 5 }]
key matnr : matnr;
@ui.lineItem : [{ position: 20 }]
@ui.identification : [{ position: 10 }]
num_ett : char4;
@ui.lineItem : [{ position: 30 }]
@ui.identification : [{ position: 20 }]
num_cond : char20;
@ui.lineItem : [{ position: 40 }]
@ui.identification : [{ position: 30 }]
val_prx : char15;
@ui.lineItem : [{ position: 50 }]
@ui.identification : [{ position: 40 }]
val_cond : char15;
@ui.lineItem : [{ position: 60 }]
@ui.identification : [{ position: 50 }]
cod_dev : char3;
@ui.lineItem : [{ position: 70 }]
@ui.identification : [{ position: 60 }]
mains : meins;
@ui.lineItem : [{ position: 80 }]
@ui.identification : [{ position: 70 }]
dat_debvalidmag : dats;
@ui.lineItem : [{ position: 90 }]
@ui.identification : [{ position: 80 }]
dat_finvalidmag : dats;
@ui.lineItem : [{ position: 100 }]
@ui.identification : [{ position: 90 }]
code_eca_pric : zecode_ecart;
@ui.lineItem : [{ position: 110 }]
@ui.identification : [{ position: 100 }]
tabname : tabname;
_OmnibusHeader : association to parent ZC_OMNIBUS_TILE on $projection.row_id = _OmnibusHeader.row_id
and $projection.matnr = _OmnibusHeader.matnr;
}
We have set on service definition
@EndUserText.label: 'Service definition for omnibus tile'
define service ZSD_OMNIBUS_TILE {
expose ZC_OMNIBUS_TILE as OmnibusHeader;
expose ZC_OMNIBUS_TILE_HISTO as OmnibusHeaderHisto;
}
and only one service binding in OData V4 - UI and no behavior definition
The first page display correctly the information but when we want to nagivate to the object page, in the custom entity class we do not retrieve the entity ZC_OMNIBUS_TILE_HISTO. The front end generate only 2 batch request for ZC_OMNIBUS_TILE.
CASE io_request->get_entity_id( ).
WHEN 'ZC_OMNIBUS_TILE'.
"do selection
WHEN 'ZC_OMNIBUS_TILE_HISTO'.
"do the history selection
ENDCASE.
We do not understand what is wrong ?
Hoping someone can help me
Thanks you in advance for your reply
Hi @DucourtyEtan ,
It seems to me that you can have some issue with filtering or paging.
I will share with you a simplified example of two custom entities with a parent-child relationship. There you also find the example of code that handles filtering, paging, and sorting functionality for custom entities.
ZMHL_C_CUSTOM_HEADER - parent custom entity.
@ObjectModel.query.implementedBy: 'ABAP:ZMHL_CL_CUSTOM_ENTITY'
@EndUserText.label: 'Header'
@UI: {
headerInfo: {
typeName: 'Header',
typeNamePlural: 'Headers',
title: { type: #STANDARD, label: 'Header', value: 'HeaderID' }
}}
define root custom entity ZMHL_C_Custom_Header
{
@UI.facet : [
{
id : 'Header',
purpose: #STANDARD,
type : #IDENTIFICATION_REFERENCE,
label : 'Header',
position: 10
},
{
id : 'Item',
purpose : #STANDARD,
type: #LINEITEM_REFERENCE,
label : 'Items',
position : 20,
targetElement: '_Item'
} ]
@UI.lineItem : [ { position: 10 } ]
@UI.identification : [ { position: 10 } ]
@EndUserText.label : 'Header ID'
key HeaderID : abap.numc( 3 );
@UI.lineItem : [ { position: 20 } ]
@UI.identification : [ { position: 20 } ]
@EndUserText.label : 'Description'
Description : abap.char(40);
_Item : composition [1..*] of ZMHL_C_Custom_Item;
}
ZMHL_C_CUSTOM_ITEM - child custom entity.
@ObjectModel.query.implementedBy: 'ABAP:ZMHL_CL_CUSTOM_ENTITY'
@EndUserText.label: 'Item'
@UI: {
headerInfo: {
typeName: 'Item',
typeNamePlural: 'Items',
title: { type: #STANDARD, label: 'Item', value: 'Item' }
}}
define custom entity ZMHL_C_Custom_Item
{
@UI.facet : [ { id : 'Item',
purpose: #STANDARD,
type : #IDENTIFICATION_REFERENCE,
label : 'Item',
position: 10 } ]
@UI.hidden : true
key HeaderID : abap.numc( 3 );
@UI.lineItem: [ { position: 10 } ]
@UI.identification : [ { position: 10 } ]
@EndUserText.label : 'Item'
key Item : abap.numc( 2 );
@UI.lineItem: [ { position: 20 } ]
@UI.identification : [ { position: 20 } ]
@EndUserText.label : 'Description'
Description : abap.char( 40 );
_Header : association to parent ZMHL_C_Custom_Header on _Header.HeaderID = $projection.HeaderID;
}
ZMHL_CL_CUSTOM_ENTITY - class with implementation for both custom entities Header and Item. Filtering and sorting functionality is implemented using the ability of SELECT statements with dynamic WHERE and ORDER BY clauses. Paging functionality is implemented by UP TO n ROWS and OFFSET clauses of SELECT statement.
CLASS zmhl_cl_custom_entity DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES: if_rap_query_provider.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zmhl_cl_custom_entity IMPLEMENTATION.
METHOD if_rap_query_provider~select.
DATA: lt_header TYPE STANDARD TABLE OF zmhl_c_custom_header,
lt_item TYPE STANDARD TABLE OF zmhl_c_custom_item,
lv_order_by TYPE string.
lt_header = VALUE #( ( headerid = '001' description = 'Header 1' )
( headerid = '002' description = 'Header 2' )
( headerid = '003' description = 'Header 3' ) ).
lt_item = VALUE #(
( headerid = '001' item = '01' description = 'Item 1.1' )
( headerid = '001' item = '02' description = 'Item 1.2' )
( headerid = '001' item = '03' description = 'Item 1.3' )
( headerid = '002' item = '01' description = 'Item 2.1' )
( headerid = '002' item = '02' description = 'Item 2.2' )
( headerid = '003' item = '01' description = 'Item 3.1' ) ).
"Get Filters
DATA(lv_filter) = io_request->get_filter( )->get_as_sql_string( ).
"Get paging properties
DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
"Get positive page size, to avoid -1
DATA(lv_page_size) = abs( io_request->get_paging( )->get_page_size( ) ).
"Get sorting
DATA(lt_sort_elements) = io_request->get_sort_elements( ).
LOOP AT lt_sort_elements ASSIGNING FIELD-SYMBOL(<fs_sort_element>).
lv_order_by = |{ lv_order_by }{ COND #(
WHEN <fs_sort_element>-descending = abap_false
THEN |, { <fs_sort_element>-element_name }|
ELSE |, { <fs_sort_element>-element_name } DESCENDING| ) }|.
ENDLOOP.
SHIFT lv_order_by BY 2 PLACES LEFT.
CASE io_request->get_entity_id( ).
WHEN 'ZMHL_C_CUSTOM_HEADER'.
"Apply filter, get table with all results
SELECT * FROM @lt_header AS header
WHERE (lv_filter)
INTO TABLE @DATA(lt_header_result).
"Apply paging and sorting, get requested part of results
IF lv_order_by IS INITIAL.
lv_order_by = |HEADERID|.
ENDIF.
SELECT * FROM @lt_header_result AS header
ORDER BY (lv_order_by)
INTO TABLE @DATA(lt_requested_header)
UP TO @lv_page_size ROWS OFFSET @lv_offset.
"set total count of results
IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lines( lt_header_result ) ).
ENDIF.
"set data
io_response->set_data( lt_requested_header ).
WHEN 'ZMHL_C_CUSTOM_ITEM'.
"Apply filter, get table with all results
SELECT * FROM @lt_item AS item
WHERE (lv_filter)
INTO TABLE @DATA(lt_item_result).
"Apply paging and sorting, get requested part of results
IF lv_order_by IS INITIAL.
lv_order_by = |ITEM|.
ENDIF.
SELECT * FROM @lt_item_result AS item
ORDER BY (lv_order_by)
INTO TABLE @DATA(lt_requested_item)
UP TO @lv_page_size ROWS OFFSET @lv_offset.
"set total count of results
IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lines( lt_item_result ) ).
ENDIF.
"set data
io_response->set_data( lt_requested_item ).
ENDCASE.
ENDMETHOD.
ENDCLASS.
Service binding in OData V4. Service definition:
@EndUserText.label: 'Custom Entity Service Definition'
define service ZMHL_Custom_Entity {
expose ZMHL_C_Custom_Header;
expose ZMHL_C_Custom_Item;
}
Application demo:
I hope it will help you to solve the issue.
Best Regards,
Maryia
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thank you very much for your answer that unblocked me.
User | Count |
---|---|
66 | |
10 | |
8 | |
7 | |
6 | |
6 | |
6 | |
6 | |
6 | |
5 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.