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
| Category | Prefix | Description |
|---|---|---|
| UI | @UI | User interface design |
| OData | @OData | OData properties |
| Semantics | @Semantics | Semantic meaning |
| Search | @Search | Search behavior |
| Analytics | @Analytics | Analytical properties |
| ObjectModel | @ObjectModel | Object model metadata |
UI Annotations
Basic UI Configuration
@EndUserText.label: 'Orders'@Metadata.allowExtensions: truedefine 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_OrderFacets 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: trueNotesActions 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'}CompletionPercentSemantics Annotations
" Amounts and currencies@Semantics.amount.currencyCode: 'Currency'TotalAmount,
@Semantics.currencyCode: trueCurrency,
" Quantities and units@Semantics.quantity.unitOfMeasure: 'Unit'Quantity,
@Semantics.unitOfMeasure: trueUnit,
" System fields@Semantics.user.createdBy: trueCreatedBy,
@Semantics.user.lastChangedBy: trueChangedBy,
@Semantics.systemDateTime.createdAt: trueCreatedAt,
@Semantics.systemDateTime.lastChangedAt: trueChangedAt,
" Email and phone@Semantics.eMail.address: trueEmail,
@Semantics.telephone.type: [#WORK]PhoneNumber,
" Name@Semantics.name.fullName: trueCustomerName,
" Address@Semantics.address.street: trueStreet,
@Semantics.address.city: trueCity,
@Semantics.address.country: trueCountrySearch Annotations
@Search.searchable: truedefine 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: #CUBEdefine 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'CalculatedFieldMetadata Extensions
" Separate annotation file@Metadata.layer: #CUSTOMERannotate 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 fieldcase Status when 'O' then 2 -- Yellow (Open) when 'C' then 3 -- Green (Confirmed) when 'X' then 1 -- Red (Cancelled) else 0 -- Neutralend as StatusCriticality,
" Criticality values:" 0 = Neutral (Gray)" 1 = Negative (Red)" 2 = Critical (Yellow/Orange)" 3 = Positive (Green)" 5 = New Item (Blue) - only for certain contextsValue 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}StatusAccess Control
@AccessControl.authorizationCheck: #CHECK@AccessControl.privilegedAssociations: ['_Admin']define view entity ZI_Order
" DCL (Data Control Language)@EndUserText.label: 'Order Access Control'@MappingRole: truedefine 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
| Annotation | Values | Description |
|---|---|---|
| @UI.importance | #HIGH, #MEDIUM, #LOW | Column priority |
| @UI.hidden | true/false | Hide field |
| @Aggregation.default | #SUM, #AVG, #MIN, #MAX, #COUNT | Default aggregation |
| @Analytics.dataCategory | #DIMENSION, #CUBE, #FACT | Analytics category |
| @Search.ranking | #HIGH, #MEDIUM, #LOW | Search priority |
Best Practices
- Metadata Extensions: Keep UI annotations separate
- Use Semantics: For currencies, quantities, system fields
- Consistent Labels: Use EndUserText.label
- Criticality: For visual highlighting
- Search: Make important fields searchable
- Value Helps: User-friendly input assistance
Related Topics
- CDS Views - Core Data Services fundamentals
- OData Services - RESTful Web Services
- AMDP - Database Procedures
- Authorization Checks - Access Control