@linkup-ai/abap-ai 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +384 -0
- package/dist/adt-client.js +364 -0
- package/dist/cli/activate.js +113 -0
- package/dist/cli/init.js +333 -0
- package/dist/cli/remove.js +80 -0
- package/dist/cli/status.js +229 -0
- package/dist/cli/systems.js +68 -0
- package/dist/cli.js +81 -0
- package/dist/index.js +1318 -0
- package/dist/knowledge/abap/abap-dictionary.md +199 -0
- package/dist/knowledge/abap/abap-sql.md +296 -0
- package/dist/knowledge/abap/amdp.md +273 -0
- package/dist/knowledge/abap/clean-code.md +293 -0
- package/dist/knowledge/abap/cloud-background-processing.md +250 -0
- package/dist/knowledge/abap/cloud-communication.md +265 -0
- package/dist/knowledge/abap/cloud-development.md +176 -0
- package/dist/knowledge/abap/cloud-extensibility.md +252 -0
- package/dist/knowledge/abap/cloud-released-apis.md +261 -0
- package/dist/knowledge/abap/constructor-expressions.md +289 -0
- package/dist/knowledge/abap/enhancements.md +232 -0
- package/dist/knowledge/abap/exceptions.md +271 -0
- package/dist/knowledge/abap/internal-tables.md +205 -0
- package/dist/knowledge/abap/object-orientation.md +298 -0
- package/dist/knowledge/abap/performance.md +216 -0
- package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
- package/dist/knowledge/abap/rap-business-events.md +216 -0
- package/dist/knowledge/abap/rap-draft.md +191 -0
- package/dist/knowledge/abap/rap-eml.md +453 -0
- package/dist/knowledge/abap/rap-end-to-end.md +486 -0
- package/dist/knowledge/abap/rap-feature-control.md +185 -0
- package/dist/knowledge/abap/rap-numbering.md +280 -0
- package/dist/knowledge/abap/rap-service-exposure.md +163 -0
- package/dist/knowledge/abap/rap-unmanaged.md +468 -0
- package/dist/knowledge/abap/string-processing.md +180 -0
- package/dist/knowledge/abap/unit-testing.md +303 -0
- package/dist/knowledge/abap-cds/access-control.md +241 -0
- package/dist/knowledge/abap-cds/annotations.md +331 -0
- package/dist/knowledge/abap-cds/associations.md +254 -0
- package/dist/knowledge/abap-cds/expressions.md +230 -0
- package/dist/knowledge/abap-cds/functions.md +245 -0
- package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
- package/dist/knowledge/cap/authentication.md +278 -0
- package/dist/knowledge/cap/cdl-syntax.md +247 -0
- package/dist/knowledge/cap/cql-queries.md +266 -0
- package/dist/knowledge/cap/deployment.md +343 -0
- package/dist/knowledge/cap/event-handlers.md +287 -0
- package/dist/knowledge/cap/fiori-integration.md +303 -0
- package/dist/knowledge/cap/service-definitions.md +287 -0
- package/dist/knowledge/fiori/annotations.md +347 -0
- package/dist/knowledge/fiori/deployment.md +340 -0
- package/dist/knowledge/fiori/fiori-elements.md +332 -0
- package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
- package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
- package/dist/knowledge/fiori/ui5-controllers.md +358 -0
- package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
- package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
- package/dist/knowledge/fiori/ui5-manifest.md +411 -0
- package/dist/knowledge/fiori/ui5-routing.md +303 -0
- package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
- package/dist/logger.js +114 -0
- package/dist/system-profile.js +207 -0
- package/dist/tools/abap-doc.js +72 -0
- package/dist/tools/abapgit.js +161 -0
- package/dist/tools/activate.js +68 -0
- package/dist/tools/atc-check.js +117 -0
- package/dist/tools/auth-object.js +56 -0
- package/dist/tools/breakpoints.js +76 -0
- package/dist/tools/call-hierarchy.js +84 -0
- package/dist/tools/cds-annotations.js +98 -0
- package/dist/tools/cds-dependencies.js +65 -0
- package/dist/tools/check.js +47 -0
- package/dist/tools/code-completion.js +70 -0
- package/dist/tools/code-coverage.js +111 -0
- package/dist/tools/create-amdp.js +111 -0
- package/dist/tools/create-dcl.js +81 -0
- package/dist/tools/create-transport.js +38 -0
- package/dist/tools/create.js +285 -0
- package/dist/tools/data-preview.js +37 -0
- package/dist/tools/delete.js +45 -0
- package/dist/tools/deploy-bsp.js +298 -0
- package/dist/tools/discovery.js +59 -0
- package/dist/tools/element-info.js +93 -0
- package/dist/tools/enhancements.js +186 -0
- package/dist/tools/extract-method.js +44 -0
- package/dist/tools/function-group.js +59 -0
- package/dist/tools/knowledge.js +275 -0
- package/dist/tools/lock-object.js +75 -0
- package/dist/tools/message-class.js +67 -0
- package/dist/tools/navigate.js +80 -0
- package/dist/tools/number-range.js +57 -0
- package/dist/tools/object-documentation.js +43 -0
- package/dist/tools/object-structure.js +78 -0
- package/dist/tools/object-versions.js +57 -0
- package/dist/tools/package-contents.js +60 -0
- package/dist/tools/pretty-printer.js +35 -0
- package/dist/tools/publish-binding.js +49 -0
- package/dist/tools/quick-fix.js +69 -0
- package/dist/tools/read.js +167 -0
- package/dist/tools/refactor-rename.js +60 -0
- package/dist/tools/release-transport.js +24 -0
- package/dist/tools/released-apis.js +51 -0
- package/dist/tools/repository-tree.js +90 -0
- package/dist/tools/scaffold-rap.js +642 -0
- package/dist/tools/search.js +73 -0
- package/dist/tools/shared/data-format.js +101 -0
- package/dist/tools/sql-console.js +17 -0
- package/dist/tools/system-info.js +270 -0
- package/dist/tools/traces.js +66 -0
- package/dist/tools/transport-contents.js +83 -0
- package/dist/tools/transports.js +67 -0
- package/dist/tools/unit-test.js +135 -0
- package/dist/tools/where-used.js +59 -0
- package/dist/tools/write.js +101 -0
- package/package.json +49 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# CDS Annotations — view entity, element, header
|
|
2
|
+
|
|
3
|
+
## Syntax
|
|
4
|
+
|
|
5
|
+
```sql
|
|
6
|
+
-- Header level (before define)
|
|
7
|
+
@AnnotationName: value
|
|
8
|
+
|
|
9
|
+
-- Element level (before field)
|
|
10
|
+
@AnnotationName.property: value
|
|
11
|
+
|
|
12
|
+
-- Array
|
|
13
|
+
@AnnotationName: [{ property1: value1, property2: value2 }]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## AccessControl
|
|
17
|
+
|
|
18
|
+
```sql
|
|
19
|
+
@AccessControl.authorizationCheck: #CHECK
|
|
20
|
+
define view entity ZI_Order as select from ztab_order
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| Value | Effect |
|
|
24
|
+
|-------|--------|
|
|
25
|
+
| `#NOT_REQUIRED` | No DCL needed |
|
|
26
|
+
| `#CHECK` | Warning if DCL missing |
|
|
27
|
+
| `#MANDATORY` | Syntax error if DCL missing |
|
|
28
|
+
| `#NOT_ALLOWED` | DCL ignored |
|
|
29
|
+
|
|
30
|
+
## Metadata
|
|
31
|
+
|
|
32
|
+
```sql
|
|
33
|
+
@Metadata.allowExtensions: true
|
|
34
|
+
define view entity ZI_Order as select from ztab_order
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Allows Metadata Extensions (DDLX) to add UI annotations separately.
|
|
38
|
+
|
|
39
|
+
## EndUserText
|
|
40
|
+
|
|
41
|
+
```sql
|
|
42
|
+
@EndUserText.label: 'Sales Order'
|
|
43
|
+
define view entity ZI_SalesOrder as select from vbak
|
|
44
|
+
{
|
|
45
|
+
@EndUserText.label: 'Order Number'
|
|
46
|
+
@EndUserText.quickInfo: 'Unique sales document identifier'
|
|
47
|
+
vbeln as SalesOrder
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Semantics — Currency and Amount
|
|
52
|
+
|
|
53
|
+
```sql
|
|
54
|
+
{
|
|
55
|
+
@Semantics.currencyCode: true
|
|
56
|
+
currency_code as CurrencyCode,
|
|
57
|
+
|
|
58
|
+
@Semantics.amount.currencyCode: 'CurrencyCode'
|
|
59
|
+
total_price as TotalPrice
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Semantics — Quantity and Unit
|
|
64
|
+
|
|
65
|
+
```sql
|
|
66
|
+
{
|
|
67
|
+
@Semantics.unitOfMeasure: true
|
|
68
|
+
unit as Unit,
|
|
69
|
+
|
|
70
|
+
@Semantics.quantity.unitOfMeasure: 'Unit'
|
|
71
|
+
quantity as Quantity
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Semantics — Administrative Fields (RAP)
|
|
76
|
+
|
|
77
|
+
```sql
|
|
78
|
+
{
|
|
79
|
+
@Semantics.user.createdBy: true
|
|
80
|
+
created_by as CreatedBy,
|
|
81
|
+
|
|
82
|
+
@Semantics.systemDateTime.createdAt: true
|
|
83
|
+
created_at as CreatedAt,
|
|
84
|
+
|
|
85
|
+
@Semantics.user.localInstanceLastChangedBy: true
|
|
86
|
+
last_changed_by as LastChangedBy,
|
|
87
|
+
|
|
88
|
+
@Semantics.systemDateTime.localInstanceLastChangedAt: true
|
|
89
|
+
last_changed_at as LastChangedAt,
|
|
90
|
+
|
|
91
|
+
@Semantics.systemDateTime.lastChangedAt: true
|
|
92
|
+
local_last_changed_at as LocalLastChangedAt
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- `lastChangedAt` → etag master (per-record)
|
|
97
|
+
- `localInstanceLastChangedAt` → total etag (per-BO, includes child changes)
|
|
98
|
+
|
|
99
|
+
## Semantics — Boolean, Language, Text
|
|
100
|
+
|
|
101
|
+
```sql
|
|
102
|
+
@Semantics.booleanIndicator: true
|
|
103
|
+
is_active as IsActive,
|
|
104
|
+
|
|
105
|
+
@Semantics.language: true
|
|
106
|
+
language as Language,
|
|
107
|
+
|
|
108
|
+
@Semantics.text: true
|
|
109
|
+
description as Description
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## UI — List Report (lineItem + selectionField)
|
|
113
|
+
|
|
114
|
+
```sql
|
|
115
|
+
{
|
|
116
|
+
@UI.lineItem: [{ position: 10, importance: #HIGH }]
|
|
117
|
+
@UI.selectionField: [{ position: 10 }]
|
|
118
|
+
SalesOrder,
|
|
119
|
+
|
|
120
|
+
@UI.lineItem: [{ position: 20 }]
|
|
121
|
+
CreatedAt,
|
|
122
|
+
|
|
123
|
+
@UI.hidden: true
|
|
124
|
+
InternalID
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## UI — Object Page (headerInfo + facets + fieldGroup)
|
|
129
|
+
|
|
130
|
+
```sql
|
|
131
|
+
@UI.headerInfo: {
|
|
132
|
+
typeName: 'Sales Order',
|
|
133
|
+
typeNamePlural: 'Sales Orders',
|
|
134
|
+
title: { value: 'SalesOrder' },
|
|
135
|
+
description: { value: 'CustomerName' }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@UI.facet: [
|
|
139
|
+
{ id: 'General', type: #IDENTIFICATION_REFERENCE, label: 'General', position: 10 },
|
|
140
|
+
{ id: 'Items', type: #LINEITEM_REFERENCE, label: 'Items', position: 20,
|
|
141
|
+
targetElement: '_Item' },
|
|
142
|
+
{ id: 'Details', type: #FIELDGROUP_REFERENCE, label: 'Details', position: 30,
|
|
143
|
+
targetQualifier: 'DetailInfo' }
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
{
|
|
147
|
+
@UI.identification: [{ position: 10 }]
|
|
148
|
+
SalesOrder,
|
|
149
|
+
|
|
150
|
+
@UI.identification: [{ position: 20 }]
|
|
151
|
+
CustomerName,
|
|
152
|
+
|
|
153
|
+
@UI.fieldGroup: [{ qualifier: 'DetailInfo', position: 10 }]
|
|
154
|
+
PaymentTerms,
|
|
155
|
+
|
|
156
|
+
@UI.dataPoint: { qualifier: 'Status', title: 'Order Status' }
|
|
157
|
+
@UI.textArrangement: #TEXT_ONLY
|
|
158
|
+
OverallStatus
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`#TEXT_FIRST` | `#TEXT_LAST` | `#TEXT_ONLY` | `#TEXT_SEPARATE`
|
|
163
|
+
|
|
164
|
+
## Consumption — Value Help
|
|
165
|
+
|
|
166
|
+
```sql
|
|
167
|
+
@Consumption.valueHelpDefinition: [{
|
|
168
|
+
entity: { name: 'I_Currency', element: 'Currency' },
|
|
169
|
+
additionalBinding: [{
|
|
170
|
+
localElement: 'CompanyCode',
|
|
171
|
+
element: 'CompanyCode',
|
|
172
|
+
usage: #FILTER_AND_RESULT
|
|
173
|
+
}]
|
|
174
|
+
}]
|
|
175
|
+
CurrencyCode;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
→ see fiori-valuelist.md for complete reference
|
|
179
|
+
|
|
180
|
+
## Consumption — Filter
|
|
181
|
+
|
|
182
|
+
```sql
|
|
183
|
+
@Consumption.filter: {
|
|
184
|
+
selectionType: #RANGE,
|
|
185
|
+
multipleSelections: true,
|
|
186
|
+
mandatory: true
|
|
187
|
+
}
|
|
188
|
+
CompanyCode;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## ObjectModel
|
|
192
|
+
|
|
193
|
+
```sql
|
|
194
|
+
@ObjectModel.text.element: ['StatusText']
|
|
195
|
+
OverallStatus,
|
|
196
|
+
|
|
197
|
+
@ObjectModel.text.association: '_StatusText'
|
|
198
|
+
OverallStatus,
|
|
199
|
+
|
|
200
|
+
@ObjectModel.foreignKey.association: '_Customer'
|
|
201
|
+
CustomerID,
|
|
202
|
+
|
|
203
|
+
@ObjectModel.composition: true
|
|
204
|
+
_Item
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Search
|
|
208
|
+
|
|
209
|
+
```sql
|
|
210
|
+
@Search.searchable: true
|
|
211
|
+
define view entity ZC_Order as projection on ZI_Order
|
|
212
|
+
{
|
|
213
|
+
@Search.defaultSearchElement: true
|
|
214
|
+
@Search.fuzzinessThreshold: 0.8
|
|
215
|
+
@Search.ranking: #HIGH
|
|
216
|
+
SalesOrder,
|
|
217
|
+
|
|
218
|
+
@Search.defaultSearchElement: true
|
|
219
|
+
@Search.ranking: #MEDIUM
|
|
220
|
+
Description
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Analytics
|
|
225
|
+
|
|
226
|
+
```sql
|
|
227
|
+
@Analytics.dataCategory: #FACT
|
|
228
|
+
define view entity ZI_SalesFact as select from vbap
|
|
229
|
+
{
|
|
230
|
+
@DefaultAggregation: #SUM
|
|
231
|
+
total_price as TotalPrice,
|
|
232
|
+
|
|
233
|
+
@DefaultAggregation: #COUNT
|
|
234
|
+
item_number as ItemCount
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`#DIMENSION` | `#FACT` | `#CUBE`
|
|
239
|
+
`#SUM` | `#MIN` | `#MAX` | `#AVG` | `#COUNT`
|
|
240
|
+
|
|
241
|
+
## Environment — Parameter Defaults
|
|
242
|
+
|
|
243
|
+
```sql
|
|
244
|
+
define view entity ZI_WithDefaults
|
|
245
|
+
with parameters
|
|
246
|
+
@Environment.systemField: #SYSTEM_DATE
|
|
247
|
+
p_date : abap.dats,
|
|
248
|
+
@Environment.systemField: #SYSTEM_LANGUAGE
|
|
249
|
+
p_lang : spras,
|
|
250
|
+
@Environment.systemField: #USER
|
|
251
|
+
p_user : syuname
|
|
252
|
+
as select from ztab
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Full Annotated View Entity Example
|
|
256
|
+
|
|
257
|
+
```sql
|
|
258
|
+
@AccessControl.authorizationCheck: #CHECK
|
|
259
|
+
@EndUserText.label: 'Sales Order'
|
|
260
|
+
@Metadata.allowExtensions: true
|
|
261
|
+
@Search.searchable: true
|
|
262
|
+
define view entity ZI_SalesOrder
|
|
263
|
+
as select from vbak as h
|
|
264
|
+
composition [0..*] of ZI_SalesOrderItem as _Item
|
|
265
|
+
association [0..1] to kna1 as _Customer
|
|
266
|
+
on $projection.CustomerID = _Customer.kunnr
|
|
267
|
+
association [0..1] to I_Currency as _Currency
|
|
268
|
+
on $projection.CurrencyCode = _Currency.Currency
|
|
269
|
+
{
|
|
270
|
+
@UI.lineItem: [{ position: 10, importance: #HIGH }]
|
|
271
|
+
@UI.selectionField: [{ position: 10 }]
|
|
272
|
+
@Search.defaultSearchElement: true
|
|
273
|
+
key h.vbeln as SalesOrder,
|
|
274
|
+
|
|
275
|
+
@UI.lineItem: [{ position: 20 }]
|
|
276
|
+
@UI.selectionField: [{ position: 20 }]
|
|
277
|
+
@ObjectModel.foreignKey.association: '_Customer'
|
|
278
|
+
h.kunnr as CustomerID,
|
|
279
|
+
|
|
280
|
+
@UI.lineItem: [{ position: 30 }]
|
|
281
|
+
h.erdat as CreatedAt,
|
|
282
|
+
|
|
283
|
+
@Semantics.currencyCode: true
|
|
284
|
+
h.waers as CurrencyCode,
|
|
285
|
+
|
|
286
|
+
@UI.lineItem: [{ position: 40 }]
|
|
287
|
+
@Semantics.amount.currencyCode: 'CurrencyCode'
|
|
288
|
+
h.netwr as NetAmount,
|
|
289
|
+
|
|
290
|
+
@Semantics.user.createdBy: true
|
|
291
|
+
h.ernam as CreatedBy,
|
|
292
|
+
|
|
293
|
+
@ObjectModel.text.element: ['CustomerName']
|
|
294
|
+
_Customer.name1 as CustomerName,
|
|
295
|
+
|
|
296
|
+
_Item,
|
|
297
|
+
_Customer,
|
|
298
|
+
_Currency
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Annotation Placement
|
|
303
|
+
|
|
304
|
+
| Level | Position | Example |
|
|
305
|
+
|-------|----------|---------|
|
|
306
|
+
| Header | Before `define view entity` | `@AccessControl.authorizationCheck` |
|
|
307
|
+
| Element | Before field in projection | `@UI.lineItem` |
|
|
308
|
+
| Parameter | Before parameter definition | `@Environment.systemField` |
|
|
309
|
+
|
|
310
|
+
## Rules
|
|
311
|
+
|
|
312
|
+
- Always set `@AccessControl.authorizationCheck` on every view entity
|
|
313
|
+
- Always annotate CURR fields with `@Semantics.amount.currencyCode`
|
|
314
|
+
- Always annotate QUAN fields with `@Semantics.quantity.unitOfMeasure`
|
|
315
|
+
- Number `position` values by 10s for easy insertion
|
|
316
|
+
- Use `@ObjectModel.text.element` for code-text field pairs
|
|
317
|
+
- Use `@Metadata.allowExtensions: true` to enable Metadata Extensions (DDLX)
|
|
318
|
+
- Use `define view entity` (not legacy `define view`) for new development
|
|
319
|
+
- `@AbapCatalog.sqlViewName` is NOT needed for view entities (only legacy `define view`)
|
|
320
|
+
|
|
321
|
+
## Anti-Patterns
|
|
322
|
+
|
|
323
|
+
| Anti-Pattern | Correct |
|
|
324
|
+
|---|---|
|
|
325
|
+
| Missing `@Semantics.amount.currencyCode` on CURR field | Always pair amount with currency reference |
|
|
326
|
+
| `@AccessControl.authorizationCheck: #NOT_REQUIRED` on sensitive data | Use `#CHECK` or `#MANDATORY` |
|
|
327
|
+
| `@UI.lineItem` position: 1, 2, 3 | Use 10, 20, 30 for insertion gaps |
|
|
328
|
+
| Skipping `@Semantics.unitOfMeasure` on QUAN field | Always pair quantity with unit reference |
|
|
329
|
+
| Using `@UI.hidden` when field is never needed | Omit from projection entirely |
|
|
330
|
+
| Using `define view` with `@AbapCatalog.sqlViewName` for new development | Use `define view entity` (no sqlViewName needed) |
|
|
331
|
+
| UI annotations in interface CDS (ZI_*) | Use `@Metadata.allowExtensions` + Metadata Extension (DDLX) |
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# CDS Associations — Join-on-Demand Relationship Definitions
|
|
2
|
+
|
|
3
|
+
## Syntax
|
|
4
|
+
|
|
5
|
+
```sql
|
|
6
|
+
define view Z_ORDER as select from vbak as h
|
|
7
|
+
association [cardinality] to target as _Alias
|
|
8
|
+
on condition
|
|
9
|
+
{
|
|
10
|
+
key h.vbeln,
|
|
11
|
+
_Alias
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Cardinality
|
|
16
|
+
|
|
17
|
+
| Syntax | Meaning | Join |
|
|
18
|
+
|--------|---------|------|
|
|
19
|
+
| `[0..1]` | Zero or one | LEFT OUTER MANY TO ONE |
|
|
20
|
+
| `[1]` | Shorthand for [0..1] | LEFT OUTER MANY TO ONE |
|
|
21
|
+
| `[1..1]` | Exactly one | LEFT OUTER MANY TO ONE |
|
|
22
|
+
| `[0..*]` | Zero or more | LEFT OUTER MANY TO MANY |
|
|
23
|
+
| `[*]` | Shorthand for [0..*] | LEFT OUTER MANY TO MANY |
|
|
24
|
+
| `[1..*]` | One or more | LEFT OUTER MANY TO MANY |
|
|
25
|
+
| (omitted) | Default [0..1] | LEFT OUTER MANY TO ONE |
|
|
26
|
+
|
|
27
|
+
### New Syntax (S/4HANA 2302+ / NW 7.57+)
|
|
28
|
+
|
|
29
|
+
```sql
|
|
30
|
+
association to one _Target on ...
|
|
31
|
+
association to many _Targets on ...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Cardinality Examples
|
|
35
|
+
|
|
36
|
+
```sql
|
|
37
|
+
-- Optional single record
|
|
38
|
+
association [0..1] to kna1 as _Customer
|
|
39
|
+
on $projection.kunnr = _Customer.kunnr
|
|
40
|
+
|
|
41
|
+
-- Required single record
|
|
42
|
+
association [1..1] to t001 as _Company
|
|
43
|
+
on $projection.bukrs = _Company.bukrs
|
|
44
|
+
|
|
45
|
+
-- Multiple records
|
|
46
|
+
association [0..*] to vbap as _Items
|
|
47
|
+
on $projection.vbeln = _Items.vbeln
|
|
48
|
+
|
|
49
|
+
-- At least one
|
|
50
|
+
association [1..*] to spfli as _Flights
|
|
51
|
+
on $projection.carrid = _Flights.carrid
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Join Conditions
|
|
55
|
+
|
|
56
|
+
### Using $projection
|
|
57
|
+
|
|
58
|
+
```sql
|
|
59
|
+
association [0..1] to makt as _Text
|
|
60
|
+
on $projection.matnr = _Text.matnr
|
|
61
|
+
and $projection.spras = _Text.spras
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Using Source Alias
|
|
65
|
+
|
|
66
|
+
```sql
|
|
67
|
+
define view Z_MAT as select from mara as m
|
|
68
|
+
association [0..1] to makt as _Text
|
|
69
|
+
on m.matnr = _Text.matnr
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Complex Condition
|
|
73
|
+
|
|
74
|
+
```sql
|
|
75
|
+
association [0..*] to zcond as _Prices
|
|
76
|
+
on $projection.matnr = _Prices.matnr
|
|
77
|
+
and $projection.vkorg = _Prices.vkorg
|
|
78
|
+
and _Prices.valid_from <= $session.system_date
|
|
79
|
+
and _Prices.valid_to >= $session.system_date
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Exposing Associations
|
|
83
|
+
|
|
84
|
+
### Direct
|
|
85
|
+
|
|
86
|
+
```sql
|
|
87
|
+
{ key vbeln, _Customer, _Items }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Redirected
|
|
91
|
+
|
|
92
|
+
```sql
|
|
93
|
+
_Customer : redirected to Z_CUSTOMER_VIEW
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Filtered
|
|
97
|
+
|
|
98
|
+
```sql
|
|
99
|
+
_Items[Status = 'ACTIVE'] as _ActiveItems
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Using Associations — Field Access
|
|
103
|
+
|
|
104
|
+
```sql
|
|
105
|
+
{
|
|
106
|
+
key vbeln,
|
|
107
|
+
_Customer.name1 as CustomerName,
|
|
108
|
+
_Customer.ort01 as CustomerCity
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Path Expressions
|
|
113
|
+
|
|
114
|
+
```sql
|
|
115
|
+
-- Multi-level navigation
|
|
116
|
+
_Header._Customer._Country.name as CountryName
|
|
117
|
+
|
|
118
|
+
-- Path filter with cardinality indicator
|
|
119
|
+
_Items[1: ItemNumber = '000010'].Material as FirstItemMaterial
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Association vs Join
|
|
123
|
+
|
|
124
|
+
### Association (join-on-demand)
|
|
125
|
+
|
|
126
|
+
```sql
|
|
127
|
+
define view Z_WITH_ASSOC as select from vbak
|
|
128
|
+
association [0..1] to kna1 as _Customer
|
|
129
|
+
on $projection.kunnr = _Customer.kunnr
|
|
130
|
+
{
|
|
131
|
+
key vbeln,
|
|
132
|
+
kunnr,
|
|
133
|
+
_Customer -- join NOT executed until fields accessed
|
|
134
|
+
}
|
|
135
|
+
-- SELECT vbeln, kunnr FROM z_with_assoc => no join
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Join (always executed)
|
|
139
|
+
|
|
140
|
+
```sql
|
|
141
|
+
define view Z_WITH_JOIN as select from vbak as v
|
|
142
|
+
left outer join kna1 as c on v.kunnr = c.kunnr
|
|
143
|
+
{
|
|
144
|
+
key v.vbeln,
|
|
145
|
+
v.kunnr,
|
|
146
|
+
c.name1 -- join ALWAYS executed
|
|
147
|
+
}
|
|
148
|
+
-- SELECT vbeln, kunnr FROM z_with_join => join runs anyway
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Text Association Pattern
|
|
152
|
+
|
|
153
|
+
```sql
|
|
154
|
+
association [0..1] to makt as _Text
|
|
155
|
+
on $projection.matnr = _Text.matnr
|
|
156
|
+
and _Text.spras = $session.system_language
|
|
157
|
+
{
|
|
158
|
+
key matnr,
|
|
159
|
+
_Text.maktx as MaterialDescription
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Self-Reference / Hierarchy
|
|
164
|
+
|
|
165
|
+
```sql
|
|
166
|
+
define view Z_ORG as select from zorg_unit as o
|
|
167
|
+
association [0..1] to Z_ORG as _Parent
|
|
168
|
+
on $projection.parent_id = _Parent.org_id
|
|
169
|
+
association [0..*] to Z_ORG as _Children
|
|
170
|
+
on $projection.org_id = _Children.parent_id
|
|
171
|
+
{
|
|
172
|
+
key org_id,
|
|
173
|
+
parent_id,
|
|
174
|
+
name,
|
|
175
|
+
_Parent,
|
|
176
|
+
_Children
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Propagated Associations
|
|
181
|
+
|
|
182
|
+
```sql
|
|
183
|
+
-- Base view
|
|
184
|
+
define view Z_BASE as select from mara
|
|
185
|
+
association [0..1] to makt as _Text on ...
|
|
186
|
+
{ key matnr, _Text }
|
|
187
|
+
|
|
188
|
+
-- Consumer: _Text propagated automatically
|
|
189
|
+
define view Z_CONSUMER as select from Z_BASE
|
|
190
|
+
{ key matnr, _Text }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Block propagation:
|
|
194
|
+
|
|
195
|
+
```sql
|
|
196
|
+
@Metadata.ignorePropagatedAnnotations: true
|
|
197
|
+
define view Z_NO_PROP as select from Z_BASE
|
|
198
|
+
{ key matnr } -- _Text NOT available
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Composition (RAP)
|
|
202
|
+
|
|
203
|
+
```sql
|
|
204
|
+
define view entity Z_SO_HDR as select from vbak
|
|
205
|
+
composition [0..*] of Z_SO_ITEM as _Items
|
|
206
|
+
{
|
|
207
|
+
key vbeln,
|
|
208
|
+
erdat,
|
|
209
|
+
_Items
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
define view entity Z_SO_ITEM as select from vbap
|
|
213
|
+
association to parent Z_SO_HDR as _Header
|
|
214
|
+
on $projection.vbeln = _Header.vbeln
|
|
215
|
+
{
|
|
216
|
+
key vbeln,
|
|
217
|
+
key posnr,
|
|
218
|
+
matnr,
|
|
219
|
+
_Header
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## ABAP Consumption
|
|
224
|
+
|
|
225
|
+
```abap
|
|
226
|
+
SELECT
|
|
227
|
+
vbeln,
|
|
228
|
+
\_Customer-name1 AS customer_name,
|
|
229
|
+
\_Customer-ort01 AS city
|
|
230
|
+
FROM z_sales_order
|
|
231
|
+
INTO TABLE @DATA(lt_result).
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Rules
|
|
235
|
+
|
|
236
|
+
- Prefix association aliases with underscore: `_Customer`, `_Items`
|
|
237
|
+
- Use `[0..1]` for to-one, `[0..*]` for to-many
|
|
238
|
+
- Set correct cardinality to avoid duplicate rows and enable HANA optimization
|
|
239
|
+
- Use associations over joins when fields are optional or for OData/RAP
|
|
240
|
+
- Use joins when all fields are always needed or for aggregation
|
|
241
|
+
- Always expose associations in the projection list
|
|
242
|
+
- Use `$projection` in ON conditions for reusability
|
|
243
|
+
|
|
244
|
+
## Anti-Patterns
|
|
245
|
+
|
|
246
|
+
| Anti-Pattern | Correct |
|
|
247
|
+
|---|---|
|
|
248
|
+
| `association [0..*]` when target is always single record | Use `[0..1]` for HANA join pruning |
|
|
249
|
+
| Not exposing association in field list | Always add `_Alias` to projection |
|
|
250
|
+
| Deep path chains (5+ levels) | Flatten with intermediate views |
|
|
251
|
+
| Using join for optional lookup | Use association for join-on-demand |
|
|
252
|
+
| Missing `_Text.spras = $session.system_language` in text assoc | Always filter on language |
|
|
253
|
+
| Association alias without underscore prefix | Use `_Customer` not `Customer` |
|
|
254
|
+
| `association [1..1]` when data may not exist | Use `[0..1]` for nullable FKs |
|