CDS Annotations
RAP / CDS / ODataintermediate

CDS Annotations Part 2: Semantic Annotations for Currency, Units and Texts

Key takeaway

Make data self-describing with semantic annotations: amounts with currencies, quantities with units, text elements and text associations, languages, and admin fields that RAP maintains automatically.

Explanation

📌 KEY POINTS
✅ Every amount needs @Semantics.amount.currencyCode; every quantity needs @Semantics.quantity.unitOfMeasure.

✅ Show texts next to codes with @ObjectModel.text.element or a text association.

✅ Admin fields (@Semantics.user.* / @Semantics.systemDateTime.*) are filled automatically in managed RAP.

⚠️ Without semantics, Fiori shows raw numbers and analytics may sum different currencies.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Part 1 introduced the families. Semantic annotations come first because they describe what the data means — every consumer benefits from them.

━━━━━━━━━━━━━━━━━━━━
▶ 1. AMOUNTS AND CURRENCIES

@Semantics.amount.currencyCode: 'CurrencyCode'

net_amount as NetAmount,

currency_code as CurrencyCode,

Effects: Fiori formats the amount with the right decimals (JPY has none, others two or three), shows the currency, and analytics aggregates per currency. The currency element must be in the same view.

In CDS view entities, the currency-code field itself needs no separate annotation; the reference from the amount is enough.

━━━━━━━━━━━━━━━━━━━━
▶ 2. QUANTITIES AND UNITS

@Semantics.quantity.unitOfMeasure: 'OrderUnit'

order_qty as OrderQuantity,

order_unit as OrderUnit,

Same idea: correct display, conversion and aggregation per unit.

━━━━━━━━━━━━━━━━━━━━
▶ 3. TEXTS FOR CODES

Users want "Completed", not "C". Two ways:

  • Text element in the same view: @ObjectModel.text.element: ['StatusText'] on the code field, with StatusText calculated or read in the view.
  • Text association: @ObjectModel.text.association: '_StatusText' on the code, pointing to a text view annotated with @ObjectModel.dataCategory: #TEXT, where the language field has @Semantics.language: true and the description @Semantics.text: true.

Fiori then shows "Completed (C)" or only the text, controlled by @UI.textArrangement.
💡 TIP: text associations automatically use the logon language — no hard-coded language filter needed in the consumer.

━━━━━━━━━━━━━━━━━━━━
▶ 4. ADMIN FIELDS FOR RAP

@Semantics.user.createdBy: true, @Semantics.systemDateTime.createdAt: true, @Semantics.user.localInstanceLastChangedBy: true, @Semantics.systemDateTime.localInstanceLastChangedAt: true, @Semantics.systemDateTime.lastChangedAt: true.

In managed RAP these fields are filled automatically on create and update; localInstanceLastChangedAt is the typical etag master and lastChangedAt the total etag (Managed RAP Part 2).

⚠️ WATCH OUT: if the annotation is missing, the field stays empty and ETag checks do not work.

━━━━━━━━━━━━━━━━━━━━
▶ 5. OTHER USEFUL SEMANTICS

  • @Semantics.language: true — marks a language field.
  • @Semantics.businessDate.from / to — validity periods.
  • @Semantics.email.address, @Semantics.telephone.type — contact data.
  • @ObjectModel.representativeKey — the key element that represents the entity (e.g. in text and value-help views).

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Annotate amounts and quantities in ZI_SalesOrderItem.
  2. Create the text view ZI_OrderStatusText (code example) and a text association in ZI_SalesOrder.
  3. Annotate admin fields in ZR_PurchReq.
  4. Preview in Fiori: amounts formatted, status shown as text.

✅ CHECKPOINT: your data explains itself to every consumer.

Code example

ABAP Code
// ===== Step 1: amounts and quantities =====define view entity ZI_SalesOrderItem  as select from zso_item{  key sales_order      as SalesOrder,  key sales_order_item as SalesOrderItem,       @Semantics.quantity.unitOfMeasure: 'OrderUnit'      order_qty        as OrderQuantity,      order_unit       as OrderUnit,       @Semantics.amount.currencyCode: 'CurrencyCode'      net_amount       as NetAmount,      currency_code    as CurrencyCode} // ===== Step 2a: text view (one row per status and language) =====@ObjectModel.dataCategory: #TEXT@ObjectModel.representativeKey: 'Status'@EndUserText.label: 'Order Status Text'define view entity ZI_OrderStatusText  as select from zso_status_t{  key status      as Status,      @Semantics.language: true  key spras       as Language,      @Semantics.text: true      status_text as StatusText} // ===== Step 2b: text association on the code field =====define view entity ZI_SalesOrder  as select from zso_head  association [0..*] to ZI_OrderStatusText as _StatusText    on $projection.OverallStatus = _StatusText.Status{  key sales_order    as SalesOrder,      @ObjectModel.text.association: '_StatusText'     // Fiori shows the text      overall_status as OverallStatus,      _StatusText} // ===== Step 3: RAP admin fields (base BO view) =====//   @Semantics.user.createdBy: true                        created_by      as CreatedBy,//   @Semantics.systemDateTime.createdAt: true              created_at      as CreatedAt,//   @Semantics.user.localInstanceLastChangedBy: true       last_changed_by as LastChangedBy,//   @Semantics.systemDateTime.localInstanceLastChangedAt: true//                                                          local_last_changed_at as LocalLastChangedAt,//   @Semantics.systemDateTime.lastChangedAt: true          last_changed_at as LastChangedAt

Real project scenario

An order-value dashboard summed NetAmount across all orders and showed one total. Orders were in EUR, USD and JPY, so the total was meaningless — and nobody noticed for weeks because the number looked plausible.
Adding @Semantics.amount.currencyCode to the interface view made the analytics layer aggregate per currency automatically, and the dashboard showed three totals. In the same review, status codes were given a text association, and Fiori lists started showing "Completed" instead of "C" without any UI code.

Common mistakes

  • Amounts without a currency reference, quantities without a unit reference.
  • Referencing a currency field that is not in the same view.
  • Hard-coding the language in text joins instead of using text associations.
  • Missing admin field annotations, so managed RAP does not fill them and ETags fail.
  • Mixing text and code in one calculated field instead of using text semantics.

Best practices

  • Annotate every amount and quantity at the interface layer.
  • Model texts as text views with dataCategory #TEXT and text associations.
  • Annotate admin fields in every RAP base view.
  • Use @UI.textArrangement to control how text and code are shown.
  • Check the result in Fiori preview and $metadata.

Interview angle

Questions: "How do you connect an amount to its currency in CDS?", "How do you show texts for codes?", "Which annotations does managed RAP need for admin fields?"
Strong answer: @Semantics.amount.currencyCode and @Semantics.quantity.unitOfMeasure with the reference field in the same view; texts via @ObjectModel.text.element or text associations to a #TEXT view with @Semantics.language and @Semantics.text; admin fields via @Semantics.user.* and @Semantics.systemDateTime.*, with localInstanceLastChangedAt as ETag. Mention per-currency aggregation as a business reason.

Practice this lesson

Quick check

1. An order-value dashboard sums NetAmount across EUR, USD and JPY into one total. Which fix addresses the root cause?

2. A CDS view exposes an amount field together with a separate currency key field. Which annotation must be added to the amount field so that Fiori Elements and OData correctly format and display it as a currency amount?

3. A CDS view has a status code field along with an associated status description text field. Which annotation setting controls whether the UI displays the code, the text, both, or the text followed by the code in parentheses?

Interview questions

1. How do you connect amounts, quantities and texts to their semantics in CDS, and what goes wrong without it?

Answer it in your own words first, then compare.

2. Where should each kind of annotation live in a RAP stack (R_, C_, metadata extension), and why?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics