AgentStack
SKILL verified MIT Self-run

Cds View Entities

skill-likweitan-abap-skills-cds-view-entities · by likweitan

Help with CDS (Core Data Services) view entity development including data modeling, annotations, associations, compositions, access controls, aggregate expressions, built-in functions, and input parameters. Use when users ask about CDS views, CDS view entities, CDS annotations, CDS associations, CDS compositions, CDS access control, CDS metadata extensions, data modeling in ABAP, define view enti…

No reviews yet
0 installs
7 views
0.0% view→install

Install

$ agentstack add skill-likweitan-abap-skills-cds-view-entities

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Cds View Entities? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

CDS View Entities

Guide for building semantic data models using ABAP CDS (Core Data Services) view entities in ABAP Cloud.

Workflow

  1. Determine the user's goal:
  • Creating a new data model (standalone or for RAP)
  • Defining relationships between entities (associations, compositions)
  • Adding annotations for UI, semantics, or analytics
  • Implementing access controls
  • Using expressions, functions, or parameters in CDS
  1. Identify the context:
  • Standalone CDS view vs. RAP BO data model
  • Root entity vs. child entity vs. projection view
  • Transactional (RAP) vs. analytical vs. read-only consumption
  1. Apply best practices:
  • Use CDS view entities (v2 syntax define view entity) — not legacy CDS views (define view)
  • Follow naming conventions (e.g., ZR_* for interface/BO views, ZC_* for consumption/projection views, ZI_* for reuse views)
  • Add appropriate annotations for metadata consumers (UI, OData, analytics)

CDS View Entity Syntax

Basic View Entity

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order'
define view entity ZI_SalesOrder
  as select from zsalesorder
{
  key order_id       as OrderId,
      customer_id    as CustomerId,
      order_date     as OrderDate,
      net_amount     as NetAmount,
      currency_code  as CurrencyCode,
      status         as Status,
      created_by     as CreatedBy,
      created_at     as CreatedAt,
      last_changed_at as LastChangedAt
}

Root View Entity (for RAP)

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Root'
define root view entity ZR_SalesOrder
  as select from zsalesorder
  composition [0..*] of ZR_SalesOrderItem as _Item
{
  key order_uuid         as OrderUUID,
      order_id           as OrderId,
      customer_id        as CustomerId,
      order_date         as OrderDate,
      @Semantics.amount.currencyCode: 'CurrencyCode'
      net_amount         as NetAmount,
      currency_code      as CurrencyCode,
      status             as Status,

      @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
      last_changed_at    as LastChangedAt,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at    as LastChangedAt,

      _Item
}

Child View Entity

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Item'
define view entity ZR_SalesOrderItem
  as select from zsalesorder_item
  association to parent ZR_SalesOrder as _Order
    on $projection.OrderUUID = _Order.OrderUUID
{
  key item_uuid       as ItemUUID,
      order_uuid      as OrderUUID,
      product_id      as ProductId,
      quantity         as Quantity,
      @Semantics.amount.currencyCode: 'CurrencyCode'
      unit_price       as UnitPrice,
      currency_code    as CurrencyCode,

      @Semantics.user.createdBy: true
      created_by       as CreatedBy,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      last_changed_at  as LastChangedAt,

      _Order
}

Projection View (Consumption Layer)

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Projection'
@Metadata.allowExtensions: true
define view entity ZC_SalesOrder
  as projection on ZR_SalesOrder
{
  key OrderUUID,
      OrderId,
      CustomerId,
      OrderDate,
      NetAmount,
      CurrencyCode,
      Status,
      CreatedBy,
      CreatedAt,
      LastChangedBy,
      LastChangedAt,

      _Item : redirected to composition child ZC_SalesOrderItem
}

Associations & Compositions

Association Types

| Type | Syntax | Use Case | | ------------------------- | ------------------------------------------------------- | ----------------------------------------------------- | | Regular association | association [0..1] to ZI_Customer as _Customer on ... | Independent entities (e.g., master data lookup) | | Composition | composition [0..*] of ZR_Child as _Child | Parent-child with lifecycle dependency (RAP BO trees) | | To-parent association | association to parent ZR_Parent as _Parent on ... | Child → parent back-reference in compositions |

Association Syntax

define view entity ZI_SalesOrder
  as select from zsalesorder
  association [0..1] to ZI_Customer as _Customer
    on $projection.CustomerId = _Customer.CustomerId
  association [0..*] to ZI_SalesOrderItem as _Item
    on $projection.OrderId = _Item.OrderId
{
  key order_id    as OrderId,
      customer_id as CustomerId,

      // Expose associations — required for consumption via ABAP SQL or OData
      _Customer,
      _Item
}

Using Associations in ABAP SQL

" Path expression — triggers LEFT OUTER JOIN by default
SELECT FROM zi_salesorder
  FIELDS OrderId,
         \_Customer-CustomerName,
         \_Item-ProductId
  WHERE OrderId = @lv_order_id
  INTO TABLE @DATA(lt_result).

" Filtering associations
SELECT FROM zi_salesorder
  FIELDS OrderId, \_Item[ ProductId = 'PROD01' ]-Quantity
  WHERE OrderId = @lv_order_id
  INTO TABLE @DATA(lt_filtered).

Expressions & Built-in Functions

Cast Expressions

cast( amount as abap.dec(15,2) ) as ConvertedAmount,
cast( status as abap.char(10) )  as StatusText,

Case Expressions

// Simple CASE
case status
  when 'N' then 'New'
  when 'A' then 'Approved'
  when 'R' then 'Rejected'
  else 'Unknown'
end as StatusText,

// Searched CASE
case when net_amount > 10000 then 'High'
     when net_amount > 1000  then 'Medium'
     else 'Low'
end as PriorityCategory,

Arithmetic Expressions

quantity * unit_price as TotalPrice,
net_amount + tax_amount as GrossAmount,

String Functions

concat( first_name, concat( ' ', last_name ) ) as FullName,
substring( postal_code, 1, 2 )                 as Region,
length( description )                          as DescLength,
upper( country_code )                          as CountryUpper,

Date & Time Functions

dats_days_between( start_date, end_date ) as DurationDays,
dats_add_days( order_date, 30 )           as DueDate,
tstmp_current_utctimestamp()              as CurrentTimestamp,

Aggregate Expressions

define view entity ZI_OrderSummary
  as select from zsalesorder
{
  key customer_id as CustomerId,
      count(*)                      as OrderCount,
      sum( net_amount )             as TotalAmount,
      avg( net_amount as abap.dec(15,2) ) as AvgAmount,
      min( order_date )             as FirstOrderDate,
      max( order_date )             as LastOrderDate
}
group by customer_id

Input Parameters

define view entity ZI_SalesOrderByDate
  with parameters
    p_date : abap.dats
  as select from zsalesorder
{
  key order_id   as OrderId,
      order_date as OrderDate,
      net_amount as NetAmount
}
where order_date >= $parameters.p_date

Using in ABAP SQL:

SELECT FROM zi_salesorderbydate( p_date = @lv_date )
  FIELDS OrderId, OrderDate, NetAmount
  INTO TABLE @DATA(lt_orders).

Session Variables

$session.user           as CurrentUser,
$session.client         as CurrentClient,
$session.system_date    as SystemDate,
$session.system_language as SystemLanguage,

Joins

define view entity ZI_OrderWithCustomer
  as select from zsalesorder as so
  inner join zcustomer as cust
    on so.customer_id = cust.customer_id
{
  key so.order_id      as OrderId,
      cust.customer_name as CustomerName,
      so.net_amount     as NetAmount
}

| Join Type | Keyword | Behavior | | ----------- | ------------------ | ------------------------------ | | Inner | inner join | Only matching rows | | Left Outer | left outer join | All from left + matching right | | Right Outer | right outer join | All from right + matching left | | Cross | cross join | Cartesian product |

> Note: Prefer associations over joins when possible — associations are lazily resolved and support path expressions.

Key Annotations

Semantics Annotations (for managed fields in RAP)

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

Amount & Currency / Quantity & Unit

@Semantics.amount.currencyCode: 'CurrencyCode'
net_amount as NetAmount,

currency_code as CurrencyCode,

@Semantics.quantity.unitOfMeasure: 'QuantityUnit'
quantity as Quantity,

quantity_unit as QuantityUnit,

UI Annotations (in CDS or Metadata Extensions)

@UI.headerInfo: {
  typeName: 'Sales Order',
  typeNamePlural: 'Sales Orders',
  title: { type: #STANDARD, value: 'OrderId' }
}

@UI.lineItem: [{ position: 10 }]
@UI.identification: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
order_id as OrderId,

Metadata Extensions (recommended for UI annotations)

@Metadata.layer: #CUSTOMER
annotate view ZC_SalesOrder with
{
  @UI.facet: [{
    id: 'GeneralInfo',
    type: #IDENTIFICATION_REFERENCE,
    label: 'General Information',
    position: 10
  }]

  @UI.lineItem: [{ position: 10, importance: #HIGH }]
  @UI.identification: [{ position: 10 }]
  OrderId;

  @UI.lineItem: [{ position: 20 }]
  @UI.identification: [{ position: 20 }]
  @UI.selectionField: [{ position: 10 }]
  CustomerId;

  @UI.lineItem: [{ position: 30 }]
  @UI.identification: [{ position: 30 }]
  NetAmount;
}

CDS Access Control

@EndUserText.label: 'Access Control for Sales Order'
@MappingRole: true
define role ZR_SalesOrder
{
  grant select on ZR_SalesOrder
  where ( CustomerId ) = aspect pfcg_auth ( Z_SO_AUTH, CUSTOMER_ID, ACTVT = '03' );
}

Access Control Patterns

| Pattern | Description | | ---------------- | ------------------------------------------- | | pfcg_auth | Standard authorization object check | | inherit | Inherit restrictions from associated entity | | aspect user | Restrict by current user | | true / false | Unrestricted / no access |

// Inherit from parent entity
define role ZR_SalesOrderItem {
  grant select on ZR_SalesOrderItem
  where inheriting conditions from entity ZR_SalesOrder
    association _Order;
}

// User-based restriction
define role ZR_MyOrders {
  grant select on ZR_SalesOrder
  where CreatedBy = aspect user;
}

CDS Table Entities

define table entity ztab_salesorder {
  key client    : abap.clnt;
  key order_uuid: sysuuid_x16;
      order_id  : abap.numc(10);
      customer  : abap.char(10);
      amount    : abap.dec(15,2);
      currency  : abap.cuky(5);
}

> CDS table entities can serve as alternatives to classic DDIC database tables and can be used as persistent table in RAP BDEFs.

Data Model Patterns for RAP

Typical Composition Tree

ZR_Root (define root view entity)
├── composition [0..*] of ZR_Child1 as _Child1
└── composition [0..*] of ZR_Child2 as _Child2
    └── composition [0..*] of ZR_GrandChild as _GrandChild

ZR_Child1 (association to parent ZR_Root as _Root)
ZR_Child2 (association to parent ZR_Root as _Root)
  └── composition [0..*] of ZR_GrandChild as _GrandChild
ZR_GrandChild (association to parent ZR_Child2 as _Parent)

Admin Field Pattern (Managed RAP BO)

Every entity in a managed RAP BO should include admin fields:

| Field | Type | Annotation | Purpose | | -------------------- | --------- | ------------------------------------------------------------ | ------------------------------- | | CreatedBy | syuname | @Semantics.user.createdBy: true | Who created | | CreatedAt | utclong | @Semantics.systemDateTime.createdAt: true | When created | | LastChangedBy | syuname | @Semantics.user.localInstanceLastChangedBy: true | Who last changed | | LocalLastChangedAt | utclong | @Semantics.systemDateTime.localInstanceLastChangedAt: true | ETag for optimistic concurrency | | LastChangedAt | utclong | @Semantics.systemDateTime.lastChangedAt: true | Total ETag for draft |

Output Format

  • Provide complete CDS source code when creating new views
  • Include all relevant annotations
  • Follow naming conventions (ZR_* for interface, ZC_* for consumption, ZI_* for reuse)
  • Always expose associations used in consumption
  • Include access control definitions when security is relevant

References

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.