ABAP CDS Annotations: Metadata for Fiori and RAP

Category
ABAP-Statements
Published
Author
Johannes

CDS Annotations extend CDS Views with metadata for UI rendering, OData generation, search, and analytics. They are central to Fiori Elements and the RAP programming model.

Annotation Categories

CategoryPrefixDescription
UI@UIUser interface design
OData@ODataOData properties
Semantics@SemanticsSemantic meaning
Search@SearchSearch behavior
Analytics@AnalyticsAnalytical properties
ObjectModel@ObjectModelObject model metadata

UI Annotations

Basic UI Configuration

@EndUserText.label: 'Orders'
@Metadata.allowExtensions: true
define view entity ZC_Order
as projection on ZI_Order
{
@UI.facet: [{
id: 'OrderHeader',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Order Header',
position: 10
}, {
id: 'OrderItems',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Items',
position: 20,
targetElement: '_Items'
}]
@UI: {
lineItem: [{ position: 10, importance: #HIGH }],
identification: [{ position: 10 }],
selectionField: [{ position: 10 }]
}
key OrderId,
@UI: {
lineItem: [{ position: 20, importance: #HIGH }],
identification: [{ position: 20 }],
selectionField: [{ position: 20 }]
}
CustomerId,
@UI: {
lineItem: [{ position: 30, importance: #MEDIUM }],
identification: [{ position: 30 }]
}
OrderDate,
@UI: {
lineItem: [{ position: 40, importance: #HIGH, criticality: 'StatusCriticality' }],
identification: [{ position: 40 }],
selectionField: [{ position: 30 }]
}
Status,
@UI: {
lineItem: [{ position: 50, importance: #HIGH }],
identification: [{ position: 50 }]
}
@Semantics.amount.currencyCode: 'Currency'
TotalAmount,
Currency,
@UI.hidden: true
StatusCriticality,
_Items
}

Header Information

@UI.headerInfo: {
typeName: 'Order',
typeNamePlural: 'Orders',
title: { type: #STANDARD, value: 'OrderId' },
description: { type: #STANDARD, value: 'CustomerName' },
imageUrl: 'ImageUrl'
}
define view entity ZC_Order

Facets and Sections

@UI.facet: [
{
id: 'GeneralInfo',
purpose: #STANDARD,
type: #COLLECTION,
label: 'General Information',
position: 10
},
{
id: 'HeaderData',
parentId: 'GeneralInfo',
type: #FIELDGROUP_REFERENCE,
targetQualifier: 'HeaderData',
position: 10
},
{
id: 'AddressData',
parentId: 'GeneralInfo',
type: #FIELDGROUP_REFERENCE,
targetQualifier: 'Address',
position: 20
},
{
id: 'ItemsTable',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Items',
position: 20,
targetElement: '_Items'
},
{
id: 'Notes',
purpose: #STANDARD,
type: #FIELDGROUP_REFERENCE,
targetQualifier: 'Notes',
label: 'Notes',
position: 30
}
]

Field Groups

@UI.fieldGroup: [{ qualifier: 'HeaderData', position: 10, label: 'Order Number' }]
OrderId,
@UI.fieldGroup: [{ qualifier: 'HeaderData', position: 20, label: 'Order Date' }]
OrderDate,
@UI.fieldGroup: [{ qualifier: 'Address', position: 10, label: 'Street' }]
Street,
@UI.fieldGroup: [{ qualifier: 'Address', position: 20, label: 'City' }]
City,
@UI.fieldGroup: [{ qualifier: 'Notes', position: 10, label: 'Remarks' }]
@UI.multiLineText: true
Notes

Actions and Buttons

@UI: {
lineItem: [{
position: 10,
type: #FOR_ACTION,
dataAction: 'confirmOrder',
label: 'Confirm'
}, {
position: 20,
type: #FOR_ACTION,
dataAction: 'cancelOrder',
label: 'Cancel',
criticality: #NEGATIVE
}],
identification: [{
position: 100,
type: #FOR_ACTION,
dataAction: 'confirmOrder',
label: 'Confirm Order'
}]
}
OrderId,

DataField Types

" Standard field
@UI.lineItem: [{ position: 10, type: #STANDARD }]
OrderId,
" With navigation
@UI.lineItem: [{
position: 20,
type: #WITH_NAVIGATION_PATH,
targetElement: '_Customer'
}]
CustomerId,
" As URL
@UI.lineItem: [{
position: 30,
type: #WITH_URL,
url: 'WebLink'
}]
DocumentLink,
" Rating
@UI.lineItem: [{
position: 40,
type: #AS_DATAPOINT
}]
@UI.dataPoint: {
visualization: #RATING,
targetValue: 5
}
Rating,
" Progress
@UI.lineItem: [{
position: 50,
type: #AS_DATAPOINT
}]
@UI.dataPoint: {
visualization: #PROGRESS,
targetValue: 100,
criticality: 'ProgressCriticality'
}
CompletionPercent

Semantics Annotations

" Amounts and currencies
@Semantics.amount.currencyCode: 'Currency'
TotalAmount,
@Semantics.currencyCode: true
Currency,
" Quantities and units
@Semantics.quantity.unitOfMeasure: 'Unit'
Quantity,
@Semantics.unitOfMeasure: true
Unit,
" System fields
@Semantics.user.createdBy: true
CreatedBy,
@Semantics.user.lastChangedBy: true
ChangedBy,
@Semantics.systemDateTime.createdAt: true
CreatedAt,
@Semantics.systemDateTime.lastChangedAt: true
ChangedAt,
" Email and phone
@Semantics.eMail.address: true
Email,
@Semantics.telephone.type: [#WORK]
PhoneNumber,
" Name
@Semantics.name.fullName: true
CustomerName,
" Address
@Semantics.address.street: true
Street,
@Semantics.address.city: true
City,
@Semantics.address.country: true
Country

Search Annotations

@Search.searchable: true
define view entity ZC_Order
{
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
@Search.ranking: #HIGH
key OrderId,
@Search.defaultSearchElement: true
@Search.ranking: #MEDIUM
CustomerName,
@Search.defaultSearchElement: true
@Search.ranking: #LOW
Description
}

OData Annotations

@OData.publish: true
@OData.entityType.name: 'Order'
@OData.entitySet.name: 'Orders'
define view entity ZC_Order
" Navigations
@OData.navigable: true
_Customer,
" Actions
@OData.operation.name: 'confirmOrder'

Analytics Annotations

@Analytics.dataCategory: #CUBE
define view entity ZI_SalesAnalytics
{
@Analytics.dimension: true
@ObjectModel.text.element: ['CustomerName']
CustomerId,
CustomerName,
@Analytics.dimension: true
SalesOrg,
@Analytics.dimension: true
@Semantics.calendar.yearMonth: true
CalendarYearMonth,
@Analytics.measure: true
@Aggregation.default: #SUM
@Semantics.amount.currencyCode: 'Currency'
Revenue,
@Analytics.measure: true
@Aggregation.default: #SUM
Quantity,
@Analytics.measure: true
@Aggregation.default: #AVG
AveragePrice,
Currency
}

ObjectModel Annotations

@ObjectModel: {
modelCategory: #BUSINESS_OBJECT,
compositionRoot: true,
transactionalProcessingEnabled: true,
writeActivePersistence: 'ZORDERS',
semanticKey: ['OrderId'],
representativeKey: 'OrderId'
}
define view entity ZI_Order
" Text relationship
@ObjectModel.text.element: ['StatusText']
Status,
@ObjectModel.text.association: '_StatusText'
_StatusText
" Foreign key
@ObjectModel.foreignKey.association: '_Customer'
CustomerId,
" Transient (not persistent)
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_ORDER_VIRTUAL'
CalculatedField

Metadata Extensions

" Separate annotation file
@Metadata.layer: #CUSTOMER
annotate view ZC_Order with
{
@UI.lineItem: [{ position: 10, importance: #HIGH }]
@UI.identification: [{ position: 10 }]
OrderId;
@UI.lineItem: [{ position: 20 }]
@UI.hidden: true
InternalField;
@UI.facet: [{
id: 'CustomSection',
purpose: #STANDARD,
type: #FIELDGROUP_REFERENCE,
targetQualifier: 'Custom',
label: 'Customer-Specific',
position: 100
}]
_root;
}

Criticality (Traffic Light Colors)

" Status with criticality
@UI.lineItem: [{ criticality: 'StatusCriticality' }]
Status,
" Calculated criticality field
case Status
when 'O' then 2 -- Yellow (Open)
when 'C' then 3 -- Green (Confirmed)
when 'X' then 1 -- Red (Cancelled)
else 0 -- Neutral
end as StatusCriticality,
" Criticality values:
" 0 = Neutral (Gray)
" 1 = Negative (Red)
" 2 = Critical (Yellow/Orange)
" 3 = Positive (Green)
" 5 = New Item (Blue) - only for certain contexts

Value Helps

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_Customer',
element: 'CustomerId'
},
additionalBinding: [{
element: 'CustomerName',
localElement: 'CustomerName',
usage: #RESULT
}]
}]
CustomerId,
" With filter
@Consumption.valueHelpDefinition: [{
entity: { name: 'ZI_Status' },
qualifier: 'StatusVH',
useForValidation: true
}]
@Consumption.filter: {
selectionType: #SINGLE,
multipleSelections: false
}
Status

Access Control

@AccessControl.authorizationCheck: #CHECK
@AccessControl.privilegedAssociations: ['_Admin']
define view entity ZI_Order
" DCL (Data Control Language)
@EndUserText.label: 'Order Access Control'
@MappingRole: true
define role ZI_ORDER_DCL {
grant select on ZI_Order
where ( SalesOrg ) = aspect pfcg_auth( V_VBAK_VKO, VKORG, ACTVT = '03' )
and ( CustomerId ) = aspect pfcg_auth( Z_CUSTOMER, KUNNR, ACTVT = '03' );
}

Annotation Reference

AnnotationValuesDescription
@UI.importance#HIGH, #MEDIUM, #LOWColumn priority
@UI.hiddentrue/falseHide field
@Aggregation.default#SUM, #AVG, #MIN, #MAX, #COUNTDefault aggregation
@Analytics.dataCategory#DIMENSION, #CUBE, #FACTAnalytics category
@Search.ranking#HIGH, #MEDIUM, #LOWSearch priority

Best Practices

  1. Metadata Extensions: Keep UI annotations separate
  2. Use Semantics: For currencies, quantities, system fields
  3. Consistent Labels: Use EndUserText.label
  4. Criticality: For visual highlighting
  5. Search: Make important fields searchable
  6. Value Helps: User-friendly input assistance