Introduction :
Since all the new SAP Developments are moving towards RAP(Restful ABAP Programming) based model there could be multiple requirements to hide some of the Facets in the Fiori List Report based object page based using complex dynamic conditions.
Solution :
As there could be complex conditions for multiple tabs which will be displayed in the line item .We can achieve this kind of requirements using the Virtual Element and the UI annotations as shown below.
Lets take a below example scenario for a purchase order we have two different Child entities items PO items, PO Material Items which are required to be displayed based on the condition basis in the object page of list report .
Create the below three data base tables :
PO header
@EndUserText.label : 'Purchase Document'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zpurchasedoc {
key client : abap.clnt not null;
key purchasedocument : zpurchasedocumentdtel not null;
@EndUserText.label : 'Description'
description : abap.sstring(128);
@EndUserText.label : 'Approval Status'
status : abap.char(1);
@EndUserText.label : 'Priority'
priority : abap.char(1);
@EndUserText.label : 'Purchasing Organization'
purchasingorganization : abap.char(4);
@EndUserText.label : 'Purchase Document Image URL'
purchasedocumentimageurl : abap.sstring(255);
crea_date_time : timestampl;
crea_uname : uname;
lchg_date_time : timestampl;
lchg_uname : uname;
}
PO Item
@EndUserText.label : 'Purchase Document Item'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zpurchdocitem {
key client : abap.clnt not null;
@EndUserText.label : 'Purchase Document Item'
key purchasedocumentitem : abap.char(10) not null;
key purchasedocument : zpurchasedocumentdtel not null;
@EndUserText.label : 'Description'
description : abap.sstring(128);
@EndUserText.label : 'Price'
@Semantics.amount.currencyCode : 'zpurchdocitem.currency'
price : abap.curr(13,2);
@EndUserText.label : 'Currency'
currency : abap.cuky;
@EndUserText.label : 'Quantity'
@Semantics.quantity.unitOfMeasure : 'zpurchdocitem.quantityunit'
quantity : abap.quan(13,2);
@EndUserText.label : 'Unit'
quantityunit : abap.unit(3);
@EndUserText.label : 'Vendor'
vendor : abap.sstring(32);
@EndUserText.label : 'Vendor Type'
vendortype : abap.sstring(32);
@EndUserText.label : 'Purchase Document Item Image URL'
purchasedocumentitemimageurl : abap.sstring(255);
crea_date_time : timestampl;
crea_uname : uname;
lchg_date_time : timestampl;
lchg_uname : uname;
}
PO material Item
@EndUserText.label : 'Purchase Document Material Item'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zpurchdocmatitem {
key client : abap.clnt not null;
@EndUserText.label : 'Purchase Document Item'
key material : abap.char(10) not null;
key purchasedocument : zpurchasedocumentdtel not null;
@EndUserText.label : 'Description'
description : abap.sstring(128);
@EndUserText.label : 'Price'
@Semantics.amount.currencyCode : 'zpurchdocmatitem.currency'
price : abap.curr(13,2);
@EndUserText.label : 'Currency'
currency : abap.cuky;
@EndUserText.label : 'Quantity'
@Semantics.quantity.unitOfMeasure : 'zpurchdocmatitem.quantityunit'
quantity : abap.quan(13,2);
@EndUserText.label : 'Unit'
quantityunit : abap.unit(3);
@EndUserText.label : 'Vendor'
vendor : abap.sstring(32);
}
Now create the below Interface root Views entities for the
Purchase order header
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purschase Document Interface View'
define root view entity Z_I_PurchaseDocument as select from zpurchasedoc
composition [0..*] of Z_I_PurchItem as _poitem
composition [0..*] of Z_I_PURCHMATITEM as _pomatitem
{
key zpurchasedoc.purchasedocument as Purchasedocument,
zpurchasedoc.description as Description,
zpurchasedoc.status as Status,
zpurchasedoc.priority as Priority,
zpurchasedoc.purchasingorganization as Purchasingorganization,
zpurchasedoc.purchasedocumentimageurl as Purchasedocumentimageurl,
zpurchasedoc.crea_date_time as CreaDateTime,
zpurchasedoc.crea_uname as CreaUname,
zpurchasedoc.lchg_date_time as LchgDateTime,
zpurchasedoc.lchg_uname as LchgUname,
_poitem ,
_pomatitem
// Make association public
}
Purchase Order Item
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purschase Document Item Interface View'
define view entity Z_I_PurchItem as select from zpurchdocitem
association to parent Z_I_PurchaseDocument as _PoHeader on $projection.Purchasedocument = _PoHeader.Purchasedocument {
key zpurchdocitem.purchasedocumentitem as Purchasedocumentitem,
key zpurchdocitem.purchasedocument as Purchasedocument,
zpurchdocitem.description as Description,
zpurchdocitem.price as Price,
zpurchdocitem.currency as Currency,
zpurchdocitem.quantity as Quantity,
zpurchdocitem.quantityunit as Quantityunit,
zpurchdocitem.vendor as Vendor,
zpurchdocitem.vendortype as Vendortype,
zpurchdocitem.purchasedocumentitemimageurl as Purchasedocumentitemimageurl,
zpurchdocitem.crea_date_time as CreaDateTime,
zpurchdocitem.crea_uname as CreaUname,
zpurchdocitem.lchg_date_time as LchgDateTime,
zpurchdocitem.lchg_uname as LchgUname,
_PoHeader // Make association public
}
Purchase order Material Item
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purschase Document Item Interface View'
define view entity Z_I_PURCHMATITEM as select from zpurchdocmatitem
association to parent Z_I_PurchaseDocument as _PoHeader on $projection.Purchasedocument = _PoHeader.Purchasedocument {
key zpurchdocmatitem.material as material,
key zpurchdocmatitem.purchasedocument as Purchasedocument,
zpurchdocmatitem.description as Description,
zpurchdocmatitem.price as Price,
zpurchdocmatitem.currency as Currency,
zpurchdocmatitem.quantity as Quantity,
zpurchdocmatitem.quantityunit as Quantityunit,
zpurchdocmatitem.vendor as Vendor,
_PoHeader // Make association public
}
Now create the below Root View entities for Projection views :
@EndUserText.label: 'Purschase Order Consumption View'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
define root view entity Z_C_PurchDocument as projection on Z_I_PurchaseDocument {
key Purchasedocument,
Description,
Status,
Priority,
Purchasingorganization,
Purchasedocumentimageurl,
CreaDateTime,
CreaUname,
LchgDateTime,
LchgUname,
/* Associations */
_poitem : redirected to composition child Z_C_PURCHITEM ,
_pomatitem : redirected to composition child Z_C_PURCHMATITEM
}
PO item
@EndUserText.label: 'Purschase Order item Consumption view'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
define view entity Z_C_PURCHITEM as projection on Z_I_PurchItem {
key Purchasedocumentitem,
key Purchasedocument,
Description,
Price,
Currency,
Quantity,
Quantityunit,
Vendor,
Vendortype,
Purchasedocumentitemimageurl,
CreaDateTime,
CreaUname,
LchgDateTime,
LchgUname,
/* Associations */
_PoHeader : redirected to parent Z_C_PurchDocument
}
PO Material Item :
@EndUserText.label: 'Purschase Order Material item Consumption view'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
define view entity Z_C_PURCHMATITEM as projection on Z_I_PURCHMATITEM {
key material,
key Purchasedocument,
Description,
Price,
Currency,
Quantity,
Quantityunit,
Vendor,
/* Associations */
_PoHeader : redirected to parent Z_C_PurchDocument
}
Now create the below meta data extensions for all the 3 consumption views :
@Metadata.layer: #CORE
@UI: {
headerInfo: { typeName: 'Purchase Order Item',
typeNamePlural: 'Purchase Order Items',
title: { type: #STANDARD, label: 'Purchase Order Item'}
}
}
annotate view Z_C_PURCHITEM
with
{
@UI.facet: [ { id: 'Items',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Item Data',
position: 10 }]
@UI:{ lineItem: [{ position: 10, label: 'PO Item' }],
identification: [{ position: 10, label: 'PO Item'}]
}
Purchasedocumentitem;
@UI:{ lineItem: [{ position: 20, label: 'PO Document' }],
identification: [{ position: 20, label: 'PO Document'}] }
Purchasedocument;
@UI:{ lineItem: [{ position: 30, label: 'PO Desc' }],
identification: [{ position: 30, label: 'PO Desc'}] }
Description;
@UI:{ lineItem: [{ position: 40, label: 'Price' }],
identification: [{ position: 40, label: 'Price'}] }
Price;
@UI:{ lineItem: [{ position: 50, label: 'Currency' }],
identification: [{ position: 50, label: 'Currency'}] }
Currency;
@UI:{ lineItem: [{ position: 60, label: 'Quantity' }],
identification: [{ position: 60, label: 'Quantity'}] }
Quantity;
}
@Metadata.layer: #CORE
@UI: {
headerInfo: { typeName: 'Purchase Order',
typeNamePlural: 'Purchase Orders',
title: { type: #STANDARD, label: 'Purchase Order', value: 'Purchasedocument' } },
presentationVariant: [{ sortOrder: [{ by: 'Purchasedocument', direction: #DESC }] }] }
annotate view Z_C_PurchDocument
with
{
@UI.facet: [ { id: 'Header',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Header Data',
position: 10 },
{ id: 'Items',
purpose : #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Items',
position: 20,
targetElement: '_poitem'
},
{ id: 'MatItems',
purpose : #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'MateriallItems',
position: 20,
targetElement: '_pomatitem'
}]
@UI: { lineItem: [ { position: 10 } ],
identification: [ { position: 10 } ],
selectionField: [ { position: 10 } ] }
Purchasedocument;
@UI:{ lineItem: [{ position: 20, label: 'Desccriton'}],
selectionField: [{ position: 20 }],
identification: [{ position: 20, label: 'Desccriton'}] }
Description;
@UI:{ lineItem: [{ position: 30, label: 'Purchasing organization'}],
selectionField: [{ position: 30 }],
identification: [{ position: 30, label: 'Purchasing organization'}] }
Purchasingorganization;
}
@Metadata.layer: #CORE
@UI: {
headerInfo: { typeName: 'Purchase Order Material',
typeNamePlural: 'Purchase Order Material',
title: { type: #STANDARD, label: 'Purchase Order Material'}
}
}
annotate view Z_C_PURCHMATITEM
with
{
@UI.facet: [ { id: 'MatItems',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Item Data',
position: 10 }]
@UI:{ lineItem: [{ position: 10, label: 'Material' }],
identification: [{ position: 10, label: 'Material'}]
}
material;
@UI:{ lineItem: [{ position: 20, label: 'PO Document' }],
identification: [{ position: 20, label: 'PO Document'}] }
Purchasedocument;
@UI:{ lineItem: [{ position: 30, label: 'PO Desc' }],
identification: [{ position: 30, label: 'PO Desc'}] }
Description;
@UI:{ lineItem: [{ position: 40, label: 'Price' }],
identification: [{ position: 40, label: 'Price'}] }
Price;
@UI:{ lineItem: [{ position: 50, label: 'Currency' }],
identification: [{ position: 50, label: 'Currency'}] }
Currency;
@UI:{ lineItem: [{ position: 60, label: 'Quantity' }],
identification: [{ position: 60, label: 'Quantity'}] }
Quantity;
}
Now Create the beviour Definition and Implementation :
managed; // implementation in class zbp_i_purchasedocument unique;
define behavior for Z_I_PurchaseDocument alias Purchorder
persistent table ZPurchasedoc
lock master
//authorization master ( instance )
//etag master <field_name>
{
create;
update;
delete;
association _poitem { create; }
association _pomatitem { create; }
}
define behavior for Z_I_PurchItem alias PurchItem
persistent table ZPurchDOCitem
lock dependent by _PoHeader
//authorization dependent by <association>
//etag master <field_name>
{
update;
delete;
association _PoHeader { } field( mandatory) Purchasedocument;
}
define behavior for Z_I_PurchmatItem alias PurchmatItem
persistent table ZPurchDOCmatitem
lock dependent by _PoHeader
//authorization dependent by <association>
//etag master <field_name>
{
update;
delete;
association _PoHeader { } field( mandatory) Purchasedocument;
}
projection;
define behavior for Z_C_PurchDocument alias PurchDocumen
{
use create;
use update;
use delete;
use association _poitem { create; }
use association _pomatitem { create; }
}
define behavior for Z_C_PURCHITEM alias PURCHITEM
{
use update;
use delete;
use association _PoHeader;
}
define behavior for Z_C_PURCHMATITEM alias PurchmatItem
{
use update;
use delete;
use association _PoHeader;
}
Service Definition
@EndUserText.label: 'Purschase order server definition'
define service Z_PurschaseOrder_SD {
expose Z_C_PurchDocument;
expose Z_C_PURCHITEM;
expose Z_C_PURCHMATITEM;
expose Z_I_PurchaseDocument;
expose Z_I_PurchItem;
expose Z_I_PURCHMATITEM;
}
Service Binding as below
Service Binding
Now the Application Looks as below with List Reports and multiple tabs of object page :
Virtual Elements for UI annotations
Now the requirement is to hide the Material tab based on some conditions for example based on the User .
PS : We can also try to consume all the Header and Line item view entities contents to do multiple checks or conditions in the Class .
- Enhance the Interface PO header view entities with the below virtual elements
- Add the Virtual element fields in the PO header Projection view entity
- Enhance the Metadata extension using the hidden annotation in the Lineitemreference Facet as shown below
- Create the below class to handle the virtual elements
Create the class ZCL_COLL_PUR_ITEM_VE with the interface IF_SADL_EXIT_CALC_ELEMENT_READ
Implement the below methods IF_SADL_EXIT_CALC_ELEMENT_READ~CALCULATE
Since the annotation hidden will interpret as the boolean values pass either abap_true or abap_false as shown below
FIELD-SYMBOLS: <lv_data> TYPE any.
LOOP AT it_original_data ASSIGNING <lv_data>.
* Set index
DATA(lv_index) = sy-tabix.
ASSIGN COMPONENT to_upper( 'UICT_POITEM_IS' ) OF STRUCTURE ct_calculated_data[ lv_index ] TO FIELD-SYMBOL(<lv_purcitem>).
IF <lv_purcitem> IS ASSIGNED .
<lv_purcitem> = abap_false.
ENDIF.
ASSIGN COMPONENT to_upper( 'UICT_POITEMMAT_IS' ) OF STRUCTURE ct_calculated_data[ lv_index ] TO FIELD-SYMBOL(<lv_purcmatitem>).
IF <lv_purcitem> IS ASSIGNED .
if sy-uname = 'XXXXXX'.
<lv_purcmatitem> = abap_true.
endif.
ENDIF.
ENDLOOP.
method :
IF iv_entity EQ 'Z_I_PURCHASEDOCUMENT'.
INSERT |UICT_POITEM_IS| INTO TABLE et_requested_orig_elements.
INSERT |UICT_POITEMMAT_IS| INTO TABLE et_requested_orig_elements.
ENDIF.
- Make the fields read only in the Behavior definition
- Finally you can see that for your user the material item tab will be hidden
- Similarly you can hide both the views as well by passing both the virtual element values as true as shown below
Thus you can make use of virtual elements and control the UI annotations and the hide the different UI elements