Appearance
Lesson 6 Practice — Semantic Booking Model
In Lesson 6 — CDS Semantics & Annotations, you added business meaning to CDS fields and relationships. In this practice, you will apply those concepts to booking data.
Your target model is:
text
Booking
├─ CustomerId → CustomerName
├─ CarrierId → CarrierName
└─ FlightPrice → CurrencyCodeYou will also use:
text
Active Annotationsand inspect SAP's language-dependent travel-status text model.
Objects
Create:
text
ZI_BOOKING_BASE_###
ZI_BOOKING_SEM_###If you already completed the Lesson 5 practice assignment and have:
text
ZI_BOOKING_ASSOC_###you may reuse it instead of creating ZI_BOOKING_BASE_###, provided it exposes the fields required below.
Reusing the Lesson 5 model
The standard Lesson 5 solution exposes CustomerLastName, not CustomerName. When creating ZI_BOOKING_SEM_###, project CustomerLastName as CustomerName, or keep the original name and update the text annotation reference consistently. The examples below use CustomerName.
Replace ### with your suffix.
Part 1 — Inspect the amount source
Open:
text
/DMO/BOOKINGFind:
text
flight_price
currency_codeOpen Data Preview.
Record one booking:
text
Flight price:
Currency code:Answer:
Why is
FlightPriceambiguous withoutCurrencyCode?
Part 2 — Inspect SAP's reference booking CDS
Open one of the SAP Flight Reference booking CDS entities available in your tenant, for example:
text
/DMO/I_BOOKING_Uor the corresponding current booking model in the demo package.
Find the price annotation.
Record the annotation name:
text
________________________________________Record the referenced element:
text
________________________________________Part 3 — Create the base association model
If you are not reusing your Lesson 5 booking entity, create:
text
ZI_BOOKING_BASE_###Use:
text
/DMO/BOOKINGas the source.
Add associations to:
text
/DMO/I_CUSTOMER
/DMO/I_CARRIERYour base model must expose:
text
TravelId
BookingId
CustomerId
CustomerName
CarrierId
CarrierName
ConnectionId
FlightDate
FlightPrice
CurrencyCode
_Customer
_CarrierUse:
text
_Customer.LastNamefor CustomerName.
Use:
text
_Carrier.Namefor CarrierName.
Activate and preview.
Part 4 — Create ZI_BOOKING_SEM_###
Create a new Data Definition:
text
Name: ZI_BOOKING_SEM_###
Description: Lesson 6 - Semantic BookingSource:
text
ZI_BOOKING_BASE_###or your compatible Lesson 5 booking entity.
In the select list, retain all ten scalar fields (value columns) and both associations listed in Part 3:
text
TravelId (key)
BookingId (key)
CustomerId
CustomerName
CarrierId
CarrierName
ConnectionId
FlightDate
FlightPrice
CurrencyCode
_Customer
_CarrierDeclare TravelId and BookingId with key in this entity too. The later SQL exercises select and order by these identifiers. Parts 5–7 add metadata to fields in this list; do not add duplicate projections of those fields.
If you reuse the Lesson 5 entity, project CustomerLastName as CustomerName here to match this list and the examples in Part 6. If you choose to keep CustomerLastName as allowed above, retain that field in place of CustomerName and use its name consistently in the text annotation and your checks.
Add:
abap
@Metadata.ignorePropagatedAnnotations: trueto the CDS header.
Part 5 — Add amount/currency semantics
Expose:
text
FlightPrice
CurrencyCodeAdd the semantic annotation to:
text
FlightPriceIt must reference:
text
CurrencyCodeDo not hard-code:
text
EUR
USD
GBPHint
The annotation starts with:
abap
@Semantics.amount.currencyCode:Part 6 — Add Customer text metadata
Expose:
text
CustomerId
CustomerNameAnnotate:
text
CustomerIdso that:
text
CustomerNameis defined as its descriptive text element.
Hint
Use:
abap
@ObjectModel.text.element: [...]Part 7 — Add Carrier text metadata
Expose:
text
CarrierId
CarrierNameDefine:
text
CarrierNameas the text element for:
text
CarrierIdPart 8 — Activate and preview
Run:
text
Ctrl + F2Activate:
text
Ctrl + F3Open Data Preview:
text
F8Confirm the data still contains normal scalar columns.
Answer:
Why does semantic metadata not necessarily create a visibly different row result in Data Preview?
Part 9 — Open Active Annotations
Right-click:
text
ZI_BOOKING_SEM_###Choose:
text
Open With
→ Active AnnotationsFind:
text
FlightPriceRecord:
text
Annotation:
Value:
Origin:Part 10 — Inspect CustomerId annotations
In Active Annotations, find:
text
CustomerIdRecord the text-related active annotation and its value.
Expected relationship:
text
CustomerId → CustomerNamePart 11 — Inspect CarrierId annotations
Repeat for:
text
CarrierIdExpected relationship:
text
CarrierId → CarrierNamePart 12 — Explain propagation suppression
Your semantic entity contains:
abap
@Metadata.ignorePropagatedAnnotations: trueAnswer:
What does this annotation do?
Why are the amount and text relationships defined explicitly in this entity?
What advantage can this give a higher consumer layer?
Part 13 — Propagation experiment
Use a temporary label as a known marker so that the result does not depend on metadata supplied by your tenant.
13.1 Add a marker to the lower entity
Open the entity used in the as select from clause of ZI_BOOKING_SEM_###: either ZI_BOOKING_BASE_### or your reused ZI_BOOKING_ASSOC_###.
Find the projection of ConnectionId. If it already has an element-level @EndUserText.label, copy that original annotation into your notes before replacing it temporarily. Otherwise, add the annotation immediately above the field:
abap
@EndUserText.label: 'Booking connection (Lesson 6)'
Booking.connection_id as ConnectionId,This is inside the select list, not in the CDS header. The snippet uses the standard lower model's source alias Booking; if yours uses another alias, retain your existing field projection and add only the label above it. Keep the upper entity's ConnectionId as a direct projection, without a local label or an expression, so the marker can propagate.
Activate the lower entity. Open its Active Annotations and confirm that ConnectionId has EndUserText.label with the value Booking connection (Lesson 6) and the lower entity as its origin.
13.2 Compare suppression on and off
Start with the header setting from Part 4 still present in ZI_BOOKING_SEM_###:
abap
@Metadata.ignorePropagatedAnnotations: trueActivate the upper entity, then open or reopen its Active Annotations. Check both ConnectionId and FlightPrice against the first two rows below.
Temporarily remove the suppression annotation from the upper header, activate the upper entity, and reopen its Active Annotations. Check the last two rows.
| Upper header setting | Element and annotation | Expected active value | Expected origin |
|---|---|---|---|
Suppression is true | ConnectionId → EndUserText.label | The marker Booking connection (Lesson 6) is absent | No propagated marker origin |
Suppression is true | FlightPrice → Semantics.amount.currencyCode | CurrencyCode | ZI_BOOKING_SEM_### |
| Suppression annotation removed | ConnectionId → EndUserText.label | Booking connection (Lesson 6) | The chosen lower entity: ZI_BOOKING_BASE_### or ZI_BOOKING_ASSOC_### |
| Suppression annotation removed | FlightPrice → Semantics.amount.currencyCode | CurrencyCode | ZI_BOOKING_SEM_### |
Record the values and origins you observed in both states. The lower label appears when propagation is allowed; the amount reference remains because you declared it locally in Part 5.
If the marker does not appear with suppression removed, check:
- The upper
as select fromclause names the lower entity you edited. - The lower label sits directly above
Booking.connection_id as ConnectionId, and the activated lower entity shows the marker in Active Annotations. - The upper field is a direct projection of
ConnectionId, with no local@EndUserText.labelthat overrides the marker. - The upper suppression annotation has been removed, the upper entity has been activated, and you reopened Active Annotations for the upper entity.
Resolve these checks and repeat the comparison; an unchanged display is not the expected result for this controlled experiment.
13.3 Restore the submitted model
Restore the upper header setting:
abap
@Metadata.ignorePropagatedAnnotations: trueIn the lower entity, restore the original element label you saved. If there was no element label before the experiment, remove only the temporary Booking connection (Lesson 6) annotation.
Activate the lower entity and then the upper entity. Reopen the upper Active Annotations: the temporary marker should be absent, while the local FlightPrice → CurrencyCode relationship should still have ZI_BOOKING_SEM_### as its origin.
The final submitted model again suppresses inherited annotations, preserves the lower entity's original labeling, and keeps its required semantics locally defined.
Part 14 — Alias-change challenge
Create a temporary higher layer or temporary copy.
Rename:
text
CurrencyCodeto:
text
TransactionCurrencyKeep:
text
FlightPriceQuestion:
What must the amount annotation on
FlightPricereference now?
Implement the correct annotation.
Activate.
Part 15 — Prove the reference with Active Annotations
Open Active Annotations for the alias-change model.
Verify:
text
FlightPrice
↓
TransactionCurrencynot:
text
FlightPrice
↓
CurrencyCodeExplain why the annotation must use the local CDS element name.
Part 16 — Inspect a text provider
Open:
text
/DMO/I_TRAVEL_STATUS_VHFind:
text
TravelStatus
_TextFind the annotation that tells the model that _Text supplies descriptive text.
Record:
text
Annotation:
Association:Part 17 — Inspect the text target
Open:
text
/DMO/I_TRAVEL_STATUS_VH_TEXTFind:
text
Language
TextRecord the annotation on the language field:
text
________________________________________Record the annotation on the text field:
text
________________________________________Part 18 — Draw the text model
Complete:
text
TravelStatus
↓
____________________
↓
TravelStatus Text
├─ ____________________
└─ ____________________Use:
text
association
language
textin your explanation.
Part 19 — Text element vs text association
Explain the difference between:
abap
@ObjectModel.text.element: ['CarrierName']
CarrierIdand:
abap
@ObjectModel.text.association: '_Text'
TravelStatusYour explanation must mention:
text
text in same entity
associated text providerPart 20 — SQL Console
Query:
text
ZI_BOOKING_SEM_###Return:
text
TravelId
BookingId
CustomerId
CustomerName
CarrierId
CarrierName
FlightPrice
CurrencyCodeOrder by:
text
TravelId
BookingIdQuestion:
Does ABAP SQL need special syntax to select
FlightPricebecause it carries currency semantics?
Part 21 — Reasoning questions
Answer in your own words.
1. What is the purpose of a CDS annotation?
2. What is the difference between an element annotation and an entity/header annotation?
3. What does @Semantics.amount.currencyCode describe?
4. Why does it reference an element instead of a literal currency?
5. What is the quantity/unit equivalent?
6. What does @ObjectModel.text.element describe?
7. Does a text annotation retrieve the text data by itself?
8. What does @Semantics.text identify?
9. What does @Semantics.language identify?
10. What does @ObjectModel.text.association describe?
11. What does @Metadata.ignorePropagatedAnnotations: true do?
12. Why can calculations interrupt annotation propagation?
13. Why can renaming CurrencyCode require re-annotating the amount?
14. What does Active Annotations tell you?
15. Why are UI annotations deliberately not the focus of this lesson?
Optional challenge 1 — Semantic Route
Create your own semantic layer on:
text
ZI_ROUTE_ASSOC_###Add:
text
Distance → DistanceUnit
CarrierId → CarrierName
OriginAirportId → OriginAirportName
DestinationAirportId → DestinationAirportNameUse:
text
@Semantics.quantity.unitOfMeasure
@ObjectModel.text.elementOptional challenge 2 — Annotation investigation
Open Active Annotations for:
text
/DMO/I_FLIGHT
/DMO/I_CONNECTION
/DMO/I_CARRIERFor each, record one active semantic annotation and its origin.
Submission checklist
Useful ADT shortcuts and actions
| Action | Shortcut / menu |
|---|---|
| Open ABAP Development Object | Ctrl + Shift + A |
| Code completion | Ctrl + Space |
| Syntax check | Ctrl + F2 |
| Activate current object | Ctrl + F3 |
| Activate inactive objects | Ctrl + Shift + F3 |
| CDS Data Preview | F8 |
| Active Annotations | Open With → Active Annotations |
| Format source | Shift + F1 |