From ARC-1 — SAP ABAP for Claude
Generates CDS analytical star schema (cube, dimension, text views) from RAP business objects or DDIC tables for SAP analytics.
How this skill is triggered — by the user, by Claude, or both
Slash command
/arc-1:generate-analytics-star-schemaThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate a CDS **analytical model** — a star schema consisting of a cube view (`@Analytics.dataCategory: #CUBE`), one or more dimension views (`#DIMENSION`), and text views (`#TEXT`) — on top of a RAP business object or a plain DDIC table.
Generate a CDS analytical model — a star schema consisting of a cube view (@Analytics.dataCategory: #CUBE), one or more dimension views (#DIMENSION), and text views (#TEXT) — on top of a RAP business object or a plain DDIC table.
This skill replicates SAP Joule's "CDS Analytical Model Generation" capability (basic + extended scope) by combining ARC-1 (SAP system access) with mcp-sap-docs (documentation & best practices). Basic scope = cube + reuse existing dimensions. Extended scope = also generate new dimensions, and start from a DDIC table (not just a RAP BO).
SAPManage(action="probe") (rap.available). If RAP/CDS isn't available, stop.SAPWrite(action="batch_create", activateAtEnd: true) for exactly that.| Setting | Default | Rationale |
|---|---|---|
| Object type | DDLS | Cube/dimension/text are all CDS data definitions |
| Package | $TMP | Fast prototyping; ask before a transportable package |
| Activation | batch_create + activateAtEnd: true | Cross-references resolve in one terminal pass |
| Authorization | #NOT_REQUIRED (cube/dimension) | Standard for analytical interface views |
| Naming | ZI_<X>_Cube, ZI_<X>_Dim, ZI_<X>_Txt | Clear star-schema roles (match the casing the templates use) |
The user provides either:
ZBP_R_TRAVEL or ZI_Travel), orZTRAVEL), orOptionally: which numeric fields are measures, which fields are dimensions, target package, and whether to generate new dimension views or reuse existing ones.
If the user names a behavior pool class (ZBP_*), read the class metadata to find the root entity it's bound to:
SAPRead(type="CLAS", name="<ZBP_class>", format="structured")
Look for the bound root entity reference. Then read that CDS root entity (next step). If the user already named the CDS root entity / interface view, skip to 1c.
SAPRead(type="TABL", name="<table>")
Capture the field list with types. Identify candidate keys, dimension fields (foreign keys, characteristics), and numeric measure fields (amounts, quantities, counts).
SAPRead(type="DDLS", name="<view>")
SAPRead(type="DDLS", name="<view>", include="elements")
The element list classifies fields. Decide for each:
@Aggregation.default: #SUMFor each dimension field, check whether a reusable dimension view already exists before generating a new one:
SAPSearch(query="*<dimension_keyword>*", searchType="object", objectType="DDLS")
If a released #DIMENSION view exists (e.g. I_Country, I_CalendarDate), reuse it via association. Otherwise generate a new ZI_<X>_DIM (extended scope).
Ground the generation in current docs:
search("CDS analytical model cube Analytics.dataCategory CUBE dimension representativeKey")
search("ObjectModel.foreignKey.association text association Semantics.text")
Cite the doc IDs in your summary. Key facts (7.58):
@Analytics: { dataCategory: #CUBE, internalName: #LOCAL } + @ObjectModel: { supportedCapabilities: [ #ANALYTICAL_PROVIDER ], modelingPattern: #ANALYTICAL_CUBE }@Aggregation.default: #SUM (or #MIN/#MAX/#AVG); a numeric field WITHOUT @Aggregation.default is treated as a dimension[1..1]; @ObjectModel.foreignKey.association: '_Dim' goes on the dimension key field in the cube@Analytics.dataCategory: #DIMENSION + @ObjectModel: { representativeKey: '<key>', supportedCapabilities: [ #ANALYTICAL_DIMENSION ], modelingPattern: #ANALYTICAL_DIMENSION }@ObjectModel.dataCategory: #TEXT (the ObjectModel. namespace — NOT @Analytics.dataCategory) + @Semantics.text: true + @Semantics.language: true@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Cube'
@Analytics: {
dataCategory: #CUBE,
internalName: #LOCAL
}
@ObjectModel: {
supportedCapabilities: [ #ANALYTICAL_PROVIDER ],
modelingPattern: #ANALYTICAL_CUBE
}
define view entity ZI_Sales_Cube
as select from zsales_data
association [1..1] to ZI_Region_Dim as _Region
on _Region.RegionId = $projection.RegionId
{
@ObjectModel.foreignKey.association: '_Region'
key region_id as RegionId,
key calyear as CalendarYear,
@Semantics.amount.currencyCode: 'CurrencyCode'
@Aggregation.default: #SUM
amount as SalesAmount,
currency as CurrencyCode,
@Aggregation.default: #SUM
@EndUserText.label: 'Number of Records'
abap.int8'1' as RecordCount,
_Region
}
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Region Dimension'
@Analytics.dataCategory: #DIMENSION
@ObjectModel: {
representativeKey: 'RegionId',
supportedCapabilities: [ #ANALYTICAL_DIMENSION ],
modelingPattern: #ANALYTICAL_DIMENSION
}
define view entity ZI_Region_Dim
as select from zregion
association [0..1] to ZI_Region_Txt as _Text
on _Text.RegionId = $projection.RegionId
{
@ObjectModel.text.association: '_Text'
key region_id as RegionId,
_Text
}
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Region - Text'
@ObjectModel.dataCategory: #TEXT
@ObjectModel.representativeKey: 'RegionId'
define view entity ZI_Region_Txt
as select from zregion_t
{
key region_id as RegionId,
@Semantics.language: true
key spras as Language,
@Semantics.text: true
regiontext as RegionName
}
(The text view stands alone — it declares no association, so it carries no @ObjectModel.foreignKey.association. The link is one-directional: the dimension associates to this text view via _Text + @ObjectModel.text.association, not the other way round.)
Composition rules (enforce before writing):
@Aggregation.default).@Semantics.amount.currencyCode + the currency field is projected; each quantity measure has @Semantics.quantity.unitOfMeasure + the unit field is projected.[1..1] and the dimension key field carries @ObjectModel.foreignKey.association.representativeKey.@ObjectModel.dataCategory: #TEXT + @Semantics.text/language.Show the user the model tree before writing:
Cube: ZI_Sales_Cube (from zsales_data)
Measures: SalesAmount (SUM, currency CurrencyCode), RecordCount (SUM)
Dimensions:
ZI_Region_Dim (new) → ZI_Region_Txt (new)
On confirmation, create all views in one batch with deferred activation so cross-references resolve together:
SAPWrite(action="batch_create", activateAtEnd=true, objects=[
{ type: "DDLS", name: "ZI_Region_Txt", source: "<text_ddl>", package: "$TMP" },
{ type: "DDLS", name: "ZI_Region_Dim", source: "<dimension_ddl>", package: "$TMP" },
{ type: "DDLS", name: "ZI_Sales_Cube", source: "<cube_ddl>", package: "$TMP" }
])
Order the array dependencies-first (text → dimension → cube) so that even if activateAtEnd falls back to per-object activation on an older release, the chain still resolves. With activateAtEnd: true, ARC-1 writes all three as inactive drafts and fires one terminal activation over the whole graph.
If batch_create with activateAtEnd is not honored (older release), fall back to: create each object with SAPWrite(action="create", ...) then a single SAPActivate(objects=[...]) over all created objects.
SAPRead(type="DDLS", name="ZI_Sales_Cube", include="elements")
Confirm the cube is live and its dimension association + measures resolved.
The cube is the foundation; the query is what end users consume. Offer:
"Want me to generate an analytical query (the consumable KPI view) on top of this cube? →
generate-cds-analytical-query"
| Error | Cause | Fix |
|---|---|---|
| Cube has no measure | All numeric fields treated as dimensions | Add @Aggregation.default: #SUM to at least one numeric field |
| Association cardinality error | Cube→dimension not [1..1] | Change cardinality to [1..1] |
foreignKey.association placement | Annotation on the association instead of the key field | Move it to the dimension key field in the cube |
| Text view rejected | Used @Analytics.dataCategory instead of @ObjectModel.dataCategory for #TEXT | Text views use the ObjectModel namespace |
| Cross-reference unresolved | Created objects activated individually before siblings existed | Use batch_create with activateAtEnd: true |
| Representative key missing | Dimension has no representativeKey | Add @ObjectModel.representativeKey |
#DIMENSION views). Extended = also generate new ZI_*_DIM + ZI_*_TXT and accept a DDIC table as the fact source (Step 1b).generate-cds-analytical-query).generate-rap-service.I_CalendarDate, I_Country, I_Currency) instead of regenerating them — SAPSearch in Step 2.npx claudepluginhub arc-mcp/arc-1 --plugin arc-1Generates an analytical CDS query (transient projection view with PROVIDER CONTRACT ANALYTICAL_QUERY) on top of an existing SAP analytical cube for use in SAP Analytics Cloud or embedded analytics.
Guides creation of Analytic Models in SAP Datasphere for SAP Analytics Cloud, defining reporting dimensions, measures (calculated, restricted, count distinct), currency/unit conversions, exception aggregations for dashboards, KPIs, and self-service BI.
Build, validate, and manage semantic models using Sidemantic. Creates semantic layers mapping database tables to business dimensions/metrics, generates SQL, and imports from Cube/dbt/LookML.