ABAP CDS Annotations: Metadaten für Fiori und RAP

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

CDS Annotations erweitern CDS Views um Metadaten für UI-Rendering, OData-Generierung, Suche und Analytik. Sie sind zentral für Fiori Elements und das RAP-Programmiermodell.

Annotation-Kategorien

KategoriePräfixBeschreibung
UI@UIOberflächengestaltung
OData@ODataOData-Eigenschaften
Semantics@SemanticsSemantische Bedeutung
Search@SearchSuchverhalten
Analytics@AnalyticsAnalytische Eigenschaften
ObjectModel@ObjectModelObjektmodell-Metadaten

UI-Annotations

Grundlegende UI-Konfiguration

@EndUserText.label: 'Bestellungen'
@Metadata.allowExtensions: true
define view entity ZC_Order
as projection on ZI_Order
{
@UI.facet: [{
id: 'OrderHeader',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Bestellkopf',
position: 10
}, {
id: 'OrderItems',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Positionen',
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-Informationen

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

Facets und Sections

@UI.facet: [
{
id: 'GeneralInfo',
purpose: #STANDARD,
type: #COLLECTION,
label: 'Allgemeine Informationen',
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: 'Positionen',
position: 20,
targetElement: '_Items'
},
{
id: 'Notes',
purpose: #STANDARD,
type: #FIELDGROUP_REFERENCE,
targetQualifier: 'Notes',
label: 'Notizen',
position: 30
}
]

Field Groups

@UI.fieldGroup: [{ qualifier: 'HeaderData', position: 10, label: 'Bestellnummer' }]
OrderId,
@UI.fieldGroup: [{ qualifier: 'HeaderData', position: 20, label: 'Bestelldatum' }]
OrderDate,
@UI.fieldGroup: [{ qualifier: 'Address', position: 10, label: 'Straße' }]
Street,
@UI.fieldGroup: [{ qualifier: 'Address', position: 20, label: 'Stadt' }]
City,
@UI.fieldGroup: [{ qualifier: 'Notes', position: 10, label: 'Bemerkungen' }]
@UI.multiLineText: true
Notes

Aktionen und Buttons

@UI: {
lineItem: [{
position: 10,
type: #FOR_ACTION,
dataAction: 'confirmOrder',
label: 'Bestätigen'
}, {
position: 20,
type: #FOR_ACTION,
dataAction: 'cancelOrder',
label: 'Stornieren',
criticality: #NEGATIVE
}],
identification: [{
position: 100,
type: #FOR_ACTION,
dataAction: 'confirmOrder',
label: 'Bestellung bestätigen'
}]
}
OrderId,

DataField-Typen

" Standard-Feld
@UI.lineItem: [{ position: 10, type: #STANDARD }]
OrderId,
" Mit Navigation
@UI.lineItem: [{
position: 20,
type: #WITH_NAVIGATION_PATH,
targetElement: '_Customer'
}]
CustomerId,
" Als 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

" Beträge und Währungen
@Semantics.amount.currencyCode: 'Currency'
TotalAmount,
@Semantics.currencyCode: true
Currency,
" Mengen und Einheiten
@Semantics.quantity.unitOfMeasure: 'Unit'
Quantity,
@Semantics.unitOfMeasure: true
Unit,
" Systemfelder
@Semantics.user.createdBy: true
CreatedBy,
@Semantics.user.lastChangedBy: true
ChangedBy,
@Semantics.systemDateTime.createdAt: true
CreatedAt,
@Semantics.systemDateTime.lastChangedAt: true
ChangedAt,
" E-Mail und Telefon
@Semantics.eMail.address: true
Email,
@Semantics.telephone.type: [#WORK]
PhoneNumber,
" Name
@Semantics.name.fullName: true
CustomerName,
" Adresse
@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
" Navigationen
@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-Beziehung
@ObjectModel.text.element: ['StatusText']
Status,
@ObjectModel.text.association: '_StatusText'
_StatusText
" Fremdschlüssel
@ObjectModel.foreignKey.association: '_Customer'
CustomerId,
" Transient (nicht persistent)
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_ORDER_VIRTUAL'
CalculatedField

Metadata Extensions

" Separate Annotation-Datei
@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: 'Kundenspezifisch',
position: 100
}]
_root;
}

Criticality (Ampel-Farben)

" Status mit Criticality
@UI.lineItem: [{ criticality: 'StatusCriticality' }]
Status,
" Berechnetes Criticality-Feld
case Status
when 'O' then 2 -- Gelb (Open)
when 'C' then 3 -- Grün (Confirmed)
when 'X' then 1 -- Rot (Cancelled)
else 0 -- Neutral
end as StatusCriticality,
" Criticality-Werte:
" 0 = Neutral (Grau)
" 1 = Negative (Rot)
" 2 = Critical (Gelb/Orange)
" 3 = Positive (Grün)
" 5 = New Item (Blau) - nur für bestimmte Kontexte

Value Helps

@Consumption.valueHelpDefinition: [{
entity: {
name: 'ZI_Customer',
element: 'CustomerId'
},
additionalBinding: [{
element: 'CustomerName',
localElement: 'CustomerName',
usage: #RESULT
}]
}]
CustomerId,
" Mit 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-Referenz

AnnotationWerteBeschreibung
@UI.importance#HIGH, #MEDIUM, #LOWSpalten-Priorität
@UI.hiddentrue/falseFeld verstecken
@Aggregation.default#SUM, #AVG, #MIN, #MAX, #COUNTStandard-Aggregation
@Analytics.dataCategory#DIMENSION, #CUBE, #FACTAnalytik-Kategorie
@Search.ranking#HIGH, #MEDIUM, #LOWSuch-Priorität

Best Practices

  1. Metadata Extensions: UI-Annotations separat halten
  2. Semantics nutzen: Für Währungen, Mengen, Systemfelder
  3. Konsistente Labels: EndUserText.label verwenden
  4. Criticality: Für visuelle Hervorhebung
  5. Search: Wichtige Felder durchsuchbar machen
  6. Value Helps: Benutzerfreundliche Eingabehilfen

Verwandte Themen