Anotaciones CDS en ABAP: Metadatos para Fiori y RAP

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Las anotaciones CDS extienden los CDS Views con metadatos para renderizado de UI, generación de OData, búsqueda y analítica. Son centrales para Fiori Elements y el modelo de programación RAP.

Categorías de anotaciones

CategoríaPrefijoDescripción
UI@UIDiseño de interfaz
OData@ODataPropiedades OData
Semantics@SemanticsSignificado semántico
Search@SearchComportamiento de búsqueda
Analytics@AnalyticsPropiedades analíticas
ObjectModel@ObjectModelMetadatos del modelo de objetos

Anotaciones UI

Configuración UI básica

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

Información de cabecera

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

Facets y secciones

@UI.facet: [
{
id: 'GeneralInfo',
purpose: #STANDARD,
type: #COLLECTION,
label: 'Información general',
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: 'Posiciones',
position: 20,
targetElement: '_Items'
},
{
id: 'Notes',
purpose: #STANDARD,
type: #FIELDGROUP_REFERENCE,
targetQualifier: 'Notes',
label: 'Notas',
position: 30
}
]

Grupos de campos

@UI.fieldGroup: [{ qualifier: 'HeaderData', position: 10, label: 'Número de pedido' }]
OrderId,
@UI.fieldGroup: [{ qualifier: 'HeaderData', position: 20, label: 'Fecha de pedido' }]
OrderDate,
@UI.fieldGroup: [{ qualifier: 'Address', position: 10, label: 'Calle' }]
Street,
@UI.fieldGroup: [{ qualifier: 'Address', position: 20, label: 'Ciudad' }]
City,
@UI.fieldGroup: [{ qualifier: 'Notes', position: 10, label: 'Observaciones' }]
@UI.multiLineText: true
Notes

Acciones y botones

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

Tipos de DataField

" Campo estándar
@UI.lineItem: [{ position: 10, type: #STANDARD }]
OrderId,
" Con navegación
@UI.lineItem: [{
position: 20,
type: #WITH_NAVIGATION_PATH,
targetElement: '_Customer'
}]
CustomerId,
" Como 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,
" Progreso
@UI.lineItem: [{
position: 50,
type: #AS_DATAPOINT
}]
@UI.dataPoint: {
visualization: #PROGRESS,
targetValue: 100,
criticality: 'ProgressCriticality'
}
CompletionPercent

Anotaciones Semantics

" Importes y monedas
@Semantics.amount.currencyCode: 'Currency'
TotalAmount,
@Semantics.currencyCode: true
Currency,
" Cantidades y unidades
@Semantics.quantity.unitOfMeasure: 'Unit'
Quantity,
@Semantics.unitOfMeasure: true
Unit,
" Campos del sistema
@Semantics.user.createdBy: true
CreatedBy,
@Semantics.user.lastChangedBy: true
ChangedBy,
@Semantics.systemDateTime.createdAt: true
CreatedAt,
@Semantics.systemDateTime.lastChangedAt: true
ChangedAt,
" Email y teléfono
@Semantics.eMail.address: true
Email,
@Semantics.telephone.type: [#WORK]
PhoneNumber,
" Nombre
@Semantics.name.fullName: true
CustomerName,
" Dirección
@Semantics.address.street: true
Street,
@Semantics.address.city: true
City,
@Semantics.address.country: true
Country
@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
}

Anotaciones OData

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

Anotaciones Analytics

@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
}

Anotaciones ObjectModel

@ObjectModel: {
modelCategory: #BUSINESS_OBJECT,
compositionRoot: true,
transactionalProcessingEnabled: true,
writeActivePersistence: 'ZORDERS',
semanticKey: ['OrderId'],
representativeKey: 'OrderId'
}
define view entity ZI_Order
" Relación de texto
@ObjectModel.text.element: ['StatusText']
Status,
@ObjectModel.text.association: '_StatusText'
_StatusText
" Clave foránea
@ObjectModel.foreignKey.association: '_Customer'
CustomerId,
" Transitorio (no persistente)
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_ORDER_VIRTUAL'
CalculatedField

Metadata Extensions

" Archivo de anotaciones separado
@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: 'Específico del cliente',
position: 100
}]
_root;
}

Criticality (colores de semáforo)

" Estado con Criticality
@UI.lineItem: [{ criticality: 'StatusCriticality' }]
Status,
" Campo Criticality calculado
case Status
when 'O' then 2 -- Amarillo (Open)
when 'C' then 3 -- Verde (Confirmed)
when 'X' then 1 -- Rojo (Cancelled)
else 0 -- Neutral
end as StatusCriticality,
" Valores de Criticality:
" 0 = Neutral (Gris)
" 1 = Negative (Rojo)
" 2 = Critical (Amarillo/Naranja)
" 3 = Positive (Verde)
" 5 = New Item (Azul) - solo para ciertos contextos

Value Helps

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

Referencia de anotaciones

AnotaciónValoresDescripción
@UI.importance#HIGH, #MEDIUM, #LOWPrioridad de columna
@UI.hiddentrue/falseOcultar campo
@Aggregation.default#SUM, #AVG, #MIN, #MAX, #COUNTAgregación por defecto
@Analytics.dataCategory#DIMENSION, #CUBE, #FACTCategoría analítica
@Search.ranking#HIGH, #MEDIUM, #LOWPrioridad de búsqueda

Mejores prácticas

  1. Metadata Extensions: Mantener anotaciones UI separadas
  2. Usar Semantics: Para monedas, cantidades, campos del sistema
  3. Labels consistentes: Usar EndUserText.label
  4. Criticality: Para resaltado visual
  5. Search: Hacer campos importantes buscables
  6. Value Helps: Ayudas de entrada amigables

Temas relacionados