@cspeach/cli 0.7.0 → 0.8.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 CHANGED
@@ -60,6 +60,32 @@ machine.
60
60
  Ten Forge Rules are non-negotiable. Full philosophy in the `CSPeach Principles`
61
61
  preamble shown to the model every turn.
62
62
 
63
+ ## Cost transparency (since 0.7.0)
64
+
65
+ Every turn shows what it cost. After each model call you see:
66
+
67
+ ```
68
+ session abc123 │ S4H │ /abap-explain │ 2 turns │ 487 tok │ $0.07 │ session: $0.18 │ 3.6s
69
+ ```
70
+
71
+ - `tok` = tokens billed this turn (input + output + cache)
72
+ - `$` = this turn's cost
73
+ - `session:` = cumulative across the open session
74
+ - `/cost` shows a per-skill / per-tool breakdown
75
+ - `/compact` summarises older turns when context gets large (cuts subsequent-turn cost ~80-90%)
76
+ - `auto_compact` in `~/.cspeach/config.toml` fires `/compact` automatically over a configurable threshold
77
+
78
+ On `/exit` you get a session summary + the `cspeach --resume <id>` command in plain text so you can pick up where you left off.
79
+
80
+ ## UI modes
81
+
82
+ Two REPL modes, both supported. **Default is `classic`.** Switch with `/ui ink` or `/ui classic`; saves to `~/.cspeach/config.toml` under `[ui]`.
83
+
84
+ - **`classic`** — chalk + readline. The supported, demo-tested mode. Use this unless you have a reason not to. Native terminal scroll, mouse wheel, search — everything your terminal does naturally just works.
85
+ - **`ink`** — React-based TUI. **PREVIEW.** Some rendering quirks remain (occasional phantom cursor, picker fossil in scrollback). Functional for daily use but not yet polished enough to recommend as default. Promoted to default in a later release.
86
+
87
+ If `ink` is misbehaving for you, run `/ui classic` once — change applies after relaunch.
88
+
63
89
  ## Configuration
64
90
 
65
91
  `~/.cspeach/config.toml` is created on first `cspeach config add-sap`:
@@ -0,0 +1,78 @@
1
+ # Model bench
2
+
3
+ Side-by-side outputs from Opus, Sonnet, and Haiku on the same ABAP prompt so
4
+ you can decide per-skill which model carries the load before changing any
5
+ default.
6
+
7
+ ## Run
8
+
9
+ ```bash
10
+ pnpm bench:models
11
+ pnpm bench:models --prompt abap-explain-bdef-handler
12
+ pnpm bench:models --models claude-opus-4-7,claude-sonnet-4-6
13
+ ```
14
+
15
+ Routes through whichever LLM mode `cspeach config` is in — managed (proxy),
16
+ BYOK, AI Hub, or local. In managed mode the proxy injects the **real skill
17
+ body from KV** as the system prompt, so each bench call is end-to-end
18
+ identical to what a real `/abap-explain` invocation would do — just on a
19
+ different model.
20
+
21
+ ## Prompts
22
+
23
+ Drop new prompts in `prompts/<name>.md`. Format:
24
+
25
+ ```markdown
26
+ ---
27
+ skill: abap-explain # required — must be a real CSPeach skill name
28
+ description: short label
29
+ ---
30
+
31
+ ## user
32
+ [the user-side prompt the model receives]
33
+ ```
34
+
35
+ Optionally include a `## system` section — it's **only** used in non-managed
36
+ modes (BYOK, AI Hub, local) where there's no proxy to inject a skill body.
37
+ In managed mode `## system` is ignored.
38
+
39
+ ## Results
40
+
41
+ `results/<prompt-name>/<model>.md` — full assistant text plus latency, tokens,
42
+ and a USD estimate (using the same pricing table the cost footer uses).
43
+
44
+ Read both files side by side. If Sonnet looks like a write-off on a given
45
+ skill, keep the skill on Opus. If it looks "as good or better," that skill
46
+ graduates down on the next config refactor.
47
+
48
+ ## Cost note
49
+
50
+ Each prompt × model call is one full skill turn through the proxy. The proxy
51
+ loads the full skill body as system prompt, so input is in the 5-15k token
52
+ range. A full 3-prompt × 3-model bench is roughly $0.30–$0.80 depending on
53
+ output verbosity. (Haiku columns will be much cheaper than Opus.)
54
+
55
+ ## Verdict log
56
+
57
+ Findings from each bench run, oldest first. Each entry should name the run
58
+ date, the prompts that were benched, and the model decision per skill — so
59
+ the config refactor (`resolveModelForSkill` + `[models.skills]`) can be
60
+ driven from evidence, not guesses.
61
+
62
+ ### 2026-05-18 — initial 3-prompt run
63
+
64
+ Prompts: `abap-explain-bdef-handler`, `abap-document-cds`, `abap-test-method`.
65
+
66
+ | Skill | Sonnet 4.6 | Haiku 4.5 | Decision |
67
+ |---|---|---|---|
68
+ | `abap-explain` | matches Opus, catches `IN LOCAL MODE` non-retrigger nuance | typo `cl_abap_behav_handler` (wrong class name) | **Sonnet OK; Haiku unsafe** |
69
+ | `abap-document` | matches Opus + adds client-handling + `@VDM.viewType` value | invented `DSOD` authority object; misread fuzzinessThreshold direction | **Sonnet OK; Haiku unsafe** |
70
+ | `abap-test` | comparable rigor; asserts exception **textid** (Opus only `assert_bound`) | clean code; coverage-estimate prose shaky | **Sonnet OK; Haiku borderline** |
71
+
72
+ Pattern: SAP/ABAP corpus is thinner than Python/JS — Haiku hallucinates SAP
73
+ terms on multi-paragraph synthesis. Sonnet does not. Do NOT route any
74
+ ABAP-content skill to Haiku based on this evidence.
75
+
76
+ Skills NOT yet benched (kept on Opus by default): `abap-generate`, `-rap`,
77
+ `-modernize`, `-design`, `-refactor`, `-review`, `-cca`, anything else
78
+ producing new code or architectural decisions. Bench before routing.
@@ -0,0 +1,44 @@
1
+ ---
2
+ skill: abap-document
3
+ description: Document a CDS interface view with associations + currency annotation
4
+ ---
5
+
6
+ ## user
7
+
8
+ Please document this CDS view:
9
+
10
+ ```cds
11
+ @AccessControl.authorizationCheck: #CHECK
12
+ @EndUserText.label: 'Sales Order — Interface View'
13
+ @Search.searchable: true
14
+ define view entity ZI_SalesOrder
15
+ as select from zsales_order as Header
16
+
17
+ association [0..1] to ZI_Customer as _Customer on $projection.CustomerID = _Customer.CustomerID
18
+ association [0..*] to ZI_SalesOrderItem as _SalesOrderItem on $projection.SalesOrderID = _SalesOrderItem.SalesOrderID
19
+ association [0..1] to I_Currency as _Currency on $projection.CurrencyCode = _Currency.Currency
20
+
21
+ {
22
+ key Header.sales_order_id as SalesOrderID,
23
+ Header.customer_id as CustomerID,
24
+ Header.order_date as OrderDate,
25
+
26
+ @Search.defaultSearchElement: true
27
+ @Search.fuzzinessThreshold: 0.8
28
+ @ObjectModel.text.element: [ '_Customer.CustomerName' ]
29
+ Header.customer_id as CustomerIDForSearch,
30
+
31
+ @Semantics.amount.currencyCode: 'CurrencyCode'
32
+ Header.net_amount as NetAmount,
33
+ Header.currency_code as CurrencyCode,
34
+
35
+ Header.created_at as CreatedAt,
36
+ Header.created_by as CreatedBy,
37
+ Header.last_changed_at as LastChangedAt,
38
+ Header.last_changed_by as LastChangedBy,
39
+
40
+ _Customer,
41
+ _SalesOrderItem,
42
+ _Currency
43
+ }
44
+ ```
@@ -0,0 +1,57 @@
1
+ ---
2
+ skill: abap-explain
3
+ description: Explain a managed RAP behavior pool with a validation + determination
4
+ ---
5
+
6
+ ## user
7
+
8
+ Please explain this behavior pool:
9
+
10
+ ```abap
11
+ CLASS lhc_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
12
+ PRIVATE SECTION.
13
+ METHODS validateorderdate FOR VALIDATE ON SAVE
14
+ IMPORTING keys FOR SalesOrder~validateorderdate.
15
+ METHODS determinenetamount FOR DETERMINE ON MODIFY
16
+ IMPORTING keys FOR SalesOrder~determinenetamount.
17
+ ENDCLASS.
18
+
19
+ CLASS lhc_salesorder IMPLEMENTATION.
20
+ METHOD validateorderdate.
21
+ READ ENTITIES OF zr_salesorder IN LOCAL MODE
22
+ ENTITY SalesOrder FIELDS ( OrderDate ) WITH CORRESPONDING #( keys )
23
+ RESULT DATA(lt_orders).
24
+
25
+ LOOP AT lt_orders INTO DATA(ls_order).
26
+ IF ls_order-OrderDate < cl_abap_context_info=>get_system_date( ).
27
+ APPEND VALUE #( %tky = ls_order-%tky ) TO failed-salesorder.
28
+ APPEND VALUE #(
29
+ %tky = ls_order-%tky
30
+ %msg = new_message_with_text(
31
+ severity = if_abap_behv_message=>severity-error
32
+ text = 'Order date must be today or in the future' )
33
+ ) TO reported-salesorder.
34
+ ENDIF.
35
+ ENDLOOP.
36
+ ENDMETHOD.
37
+
38
+ METHOD determinenetamount.
39
+ READ ENTITIES OF zr_salesorder IN LOCAL MODE
40
+ ENTITY SalesOrder FIELDS ( Quantity UnitPrice ) WITH CORRESPONDING #( keys )
41
+ RESULT DATA(lt_orders).
42
+
43
+ DATA lt_update TYPE TABLE FOR UPDATE zr_salesorder\\SalesOrder.
44
+ LOOP AT lt_orders INTO DATA(ls_order).
45
+ APPEND VALUE #(
46
+ %tky = ls_order-%tky
47
+ NetAmount = ls_order-Quantity * ls_order-UnitPrice
48
+ %control-NetAmount = if_abap_behv=>mk-on
49
+ ) TO lt_update.
50
+ ENDLOOP.
51
+
52
+ MODIFY ENTITIES OF zr_salesorder IN LOCAL MODE
53
+ ENTITY SalesOrder UPDATE FIELDS ( NetAmount )
54
+ WITH lt_update.
55
+ ENDMETHOD.
56
+ ENDCLASS.
57
+ ```
@@ -0,0 +1,42 @@
1
+ ---
2
+ skill: abap-test
3
+ description: Generate an ABAP Unit test class for a small business rule method
4
+ ---
5
+
6
+ ## user
7
+
8
+ Please write an ABAP Unit test class for this method:
9
+
10
+ ```abap
11
+ CLASS zcl_discount_calculator DEFINITION
12
+ PUBLIC FINAL CREATE PUBLIC.
13
+
14
+ PUBLIC SECTION.
15
+ "! Returns the discount percentage (0..30) for a given net amount.
16
+ "! < 1000 → 0%
17
+ "! < 5000 → 5%
18
+ "! < 10000 → 10%
19
+ "! >= 10000 → 15%
20
+ "! Raises zcx_discount_error on negative input.
21
+ METHODS calculate_discount
22
+ IMPORTING iv_net_amount TYPE p LENGTH 13 DECIMALS 2
23
+ RETURNING VALUE(rv_discount) TYPE p LENGTH 3 DECIMALS 2
24
+ RAISING zcx_discount_error.
25
+ ENDCLASS.
26
+
27
+ CLASS zcl_discount_calculator IMPLEMENTATION.
28
+ METHOD calculate_discount.
29
+ IF iv_net_amount < 0.
30
+ RAISE EXCEPTION TYPE zcx_discount_error
31
+ EXPORTING textid = zcx_discount_error=>negative_input.
32
+ ENDIF.
33
+
34
+ rv_discount = COND #(
35
+ WHEN iv_net_amount < 1000 THEN '0.00'
36
+ WHEN iv_net_amount < 5000 THEN '5.00'
37
+ WHEN iv_net_amount < 10000 THEN '10.00'
38
+ ELSE '15.00'
39
+ ).
40
+ ENDMETHOD.
41
+ ENDCLASS.
42
+ ```
@@ -0,0 +1,189 @@
1
+ # abap-document-cds — claude-haiku-4-5
2
+
3
+ - **skill:** abap-document
4
+ - **description:** Document a CDS interface view with associations + currency annotation
5
+ - **latency:** 26816 ms
6
+ - **input tokens:** 2,688
7
+ - **output tokens:** 2,623
8
+ - **cost:** $0.0158
9
+
10
+ ---
11
+
12
+ # abap-document — ZI_SalesOrder
13
+
14
+ ## TL;DR
15
+
16
+ **Headline:** Interface view for sales order header data with customer and item associations, read-only with authorization check.
17
+
18
+ **Top 3 (priority order):**
19
+ 1. **Authorization enforced:** `#CHECK` mode requires `DSOD` and sales order read authority at runtime — verify user roles before consuming downstream.
20
+ 2. **Fuzzy search enabled on CustomerID:** `fuzzinessThreshold: 0.8` will match partial/misspelled customer names; test with typical fat-finger errors in UAT.
21
+ 3. **No filtering logic:** View exposes all rows from `zsales_order` table — upstream consumers must apply their own `WHERE` clauses; no automatic date range or status filtering.
22
+
23
+ **Verdict:** Safe foundation view for consumption in analytics, APIs, or reporting. Confirm downstream apps implement business rule filtering (e.g., active orders only, date ranges).
24
+
25
+ ---
26
+
27
+ ## ZI_SalesOrder (CDS VIEW ENTITY)
28
+
29
+ ### Overview
30
+
31
+ **Package:** (not yet read — see below)
32
+ **Type:** CDS View Entity (Interface Layer)
33
+ **Annotations:** Authorization check enabled, search enabled, end-user label "Sales Order — Interface View"
34
+
35
+ **Summary:**
36
+ Exposes sales order header data from the custom table `zsales_order` with associations to customer master, line items, and SAP currency data. Designed as a read-only interface view for analytics, reporting, and API consumption.
37
+
38
+ ---
39
+
40
+ ### Purpose
41
+
42
+ This view serves as the **interface layer** for sales order headers in the custom data model. It:
43
+
44
+ 1. **Presents domain data cleanly** — maps technical table column names (`sales_order_id`, `customer_id`) to business-readable field names (`SalesOrderID`, `CustomerID`).
45
+ 2. **Enforces authorization** — restricts read access to users with appropriate sales order query authority.
46
+ 3. **Enables semantic search** — makes customer name searchable via fuzzy matching (0.8 threshold = allow 20% typos).
47
+ 4. **Links related business entities** — connects to customer master, line items, and currency metadata via associations (no data duplication, navigation-based joins).
48
+ 5. **Supports consumption layers** — designed for downstream consumption by reporting tools, OData APIs, or analytical views.
49
+
50
+ ---
51
+
52
+ ### Interface
53
+
54
+ **Key Field:**
55
+ - `SalesOrderID` (Header.sales_order_id) — unique identifier for the sales order
56
+
57
+ **Header-Level Fields:**
58
+
59
+ | Field Name | Source | Type | Purpose |
60
+ |------------|--------|------|---------|
61
+ | SalesOrderID | Header.sales_order_id | key | Sales order unique identifier |
62
+ | CustomerID | Header.customer_id | string | References customer master |
63
+ | OrderDate | Header.order_date | date | Date order was placed |
64
+ | CustomerIDForSearch | Header.customer_id | string | Fuzzy-searchable copy for customer lookup |
65
+ | NetAmount | Header.net_amount | decimal | Order value (currency-aware via annotation) |
66
+ | CurrencyCode | Header.currency_code | string | ISO currency code |
67
+ | CreatedAt | Header.created_at | timestamp | Audit: creation timestamp |
68
+ | CreatedBy | Header.created_by | user | Audit: creator user |
69
+ | LastChangedAt | Header.last_changed_at | timestamp | Audit: last change timestamp |
70
+ | LastChangedBy | Header.last_changed_by | user | Audit: last change user |
71
+
72
+ **Associations (Navigation):**
73
+
74
+ | Association | Target View | Cardinality | Join Condition | Purpose |
75
+ |-------------|-------------|-------------|-----------------|---------|
76
+ | `_Customer` | ZI_Customer | 0..1 | CustomerID | Navigate to customer master data |
77
+ | `_SalesOrderItem` | ZI_SalesOrderItem | 0..* | SalesOrderID | Navigate to line items for this order |
78
+ | `_Currency` | I_Currency (SAP standard) | 0..1 | CurrencyCode | Navigate to SAP currency details |
79
+
80
+ **Authorization & Search:**
81
+ - Authorization check: `#CHECK` — runtime check required (user must have query authority for sales order).
82
+ - Default search element: `CustomerIDForSearch` with fuzzy matching (80% match threshold).
83
+ - Searchable: all marked fields indexed for full-text search.
84
+
85
+ ---
86
+
87
+ ### Logic Flow
88
+
89
+ **Data Origin & Transformation:**
90
+
91
+ 1. **Source:** Read all rows from custom table `zsales_order` (Header alias).
92
+ 2. **Projection:** Map technical columns to semantic field names:
93
+ - `sales_order_id` → `SalesOrderID` (key)
94
+ - `customer_id` → `CustomerID`
95
+ - `order_date` → `OrderDate`
96
+ - `net_amount` → `NetAmount` (with currency semantic)
97
+ - `currency_code` → `CurrencyCode`
98
+ - Audit fields (created/changed by/at) mapped as-is.
99
+ 3. **Duplicate CustomerID:** `CustomerIDForSearch` is a copy of `CustomerID` explicitly marked for full-text search (allows UI to search by customer name via the association).
100
+ 4. **Associations:** Three navigation paths added (not joins — lazy-loaded):
101
+ - To customer master for customer details.
102
+ - To line items for order composition.
103
+ - To SAP currency master for exchange rates, decimals, etc.
104
+ 5. **Authorization Gate:** View enforces `#CHECK` at query time — SAP will validate user authority before returning rows.
105
+
106
+ **No filtering logic:** View is a "raw interface" — all rows from the source table are exposed. Upstream consumers must filter by date, status, or other business rules.
107
+
108
+ ---
109
+
110
+ ### Data Access
111
+
112
+ | Table/View | Operation | Purpose |
113
+ |------------|-----------|---------|
114
+ | `zsales_order` (custom) | **READ** | Source table for all header fields (sales_order_id, customer_id, order_date, net_amount, currency_code, audit fields) |
115
+ | `ZI_Customer` (custom view) | **READ** (via association) | Customer master lookup; enriches orders with customer details |
116
+ | `ZI_SalesOrderItem` (custom view) | **READ** (via association) | Line items for the order; enables per-item navigation |
117
+ | `I_Currency` (SAP standard) | **READ** (via association) | SAP currency master; provides conversion rates, decimal places |
118
+
119
+ **No writes:** This is a read-only interface view. No INSERT, UPDATE, or DELETE allowed.
120
+
121
+ ---
122
+
123
+ ### Dependencies
124
+
125
+ **Called by (Upstream Consumers):**
126
+ Unknown without further analysis — use `sap_usage_references` to identify (e.g., analytics views, OData services, reports using this view).
127
+
128
+ **Calls (Dependencies):**
129
+ - `zsales_order` — custom table (no option to change this view without understanding the source table schema).
130
+ - `ZI_Customer` — custom customer interface view (ensure it is available and stable).
131
+ - `ZI_SalesOrderItem` — custom sales order item interface view (ensure it exists).
132
+ - `I_Currency` (SAP standard) — standard SAP currency master (no custom risk; always available post-installation).
133
+
134
+ ---
135
+
136
+ ### Authorization & Security
137
+
138
+ **Authorization Check:** `#CHECK` mode
139
+ - When a user queries this view, SAP **enforces** a dynamic authorization check at runtime.
140
+ - The specific check depends on SAP's authority object mapping for CDS views (typically `DSOD` → Data Source Object Definition or equivalent).
141
+ - **Action required:** Verify that user roles define query access for this view; otherwise, queries will fail with "Authorization error."
142
+
143
+ **Search & Indexing:**
144
+ - `CustomerIDForSearch` with `fuzzinessThreshold: 0.8` allows typo-tolerant search (e.g., "APLE" matches "APPLE").
145
+ - Useful for user-facing search UIs; may return false positives in strict filter scenarios.
146
+
147
+ ---
148
+
149
+ ### Data Model & Semantics
150
+
151
+ **Semantic Annotations:**
152
+ - `@Semantics.amount.currencyCode: 'CurrencyCode'` on `NetAmount` — tells consuming systems that this field is a currency amount and which field holds the currency code. Tools like SAP Analytics Cloud will format it correctly.
153
+ - `@ObjectModel.text.element: [ '_Customer.CustomerName' ]` on `CustomerIDForSearch` — tells UI frameworks to display the customer **name** (not ID) when showing this field.
154
+
155
+ **Searchability:**
156
+ - View is marked `@Search.searchable: true` — all fields with `@Search.defaultSearchElement: true` are indexed.
157
+ - Only `CustomerIDForSearch` is explicitly indexed; other fields are not full-text searchable by default.
158
+
159
+ ---
160
+
161
+ ### Notes & Considerations
162
+
163
+ 1. **No status filtering:** View exposes all orders regardless of status (open, closed, cancelled). If you need only active orders, add a downstream view or consuming WHERE clause.
164
+
165
+ 2. **Audit trail:** `CreatedBy`, `LastChangedBy`, `CreatedAt`, `LastChangedAt` are available for compliance; confirm these are populated in the source table `zsales_order`.
166
+
167
+ 3. **Association multiplicity:**
168
+ - Customer is 0..1 (every order may not have a customer — nullable FK?). Verify business rule: must a sales order always have a customer?
169
+ - Line items are 0..* (orders with no items are allowed). Confirm with business if this is intentional.
170
+ - Currency is 0..1 (may not exist in SAP currency master for custom codes). Test with unusual currency codes.
171
+
172
+ 4. **Search on CustomerID not CustomerName:** The view exposes `CustomerID` for search but links to `_Customer.CustomerName`. If users expect to search by customer **name**, ensure the downstream OData/UI binding is configured to search on `CustomerIDForSearch` and display `_Customer.CustomerName`.
173
+
174
+ 5. **No performance optimizations:** View is a simple 1:1 projection. For large `zsales_order` tables (millions of rows), consider:
175
+ - Adding a semantic partitioning hint in the view definition.
176
+ - Creating secondary indexes on frequently filtered columns (e.g., `OrderDate`, `CustomerID`).
177
+ - Testing query response times with large datasets.
178
+
179
+ 6. **Missing fields?** Confirm with business that all required order header fields are in the projection. If fields exist in `zsales_order` but not exposed here, add them or create a second view variant.
180
+
181
+ ---
182
+
183
+ ## Next Steps
184
+
185
+ 1. **Trace consumers:** Run `sap_usage_references` to identify which views, APIs, and reports consume this view.
186
+ 2. **Verify source table:** Run `sap_get_source` on `zsales_order` to confirm the schema matches the projection.
187
+ 3. **Check related views:** Retrieve `ZI_Customer` and `ZI_SalesOrderItem` to ensure the associations are compatible (key cardinality, join conditions).
188
+ 4. **Authorization testing:** Create a test user with appropriate authority and verify they can query this view without "Authorization failed" errors.
189
+ 5. **Search testing:** Test fuzzy search with intentional typos (e.g., customer name "ACME Corp" vs. search "ACME Crp") to confirm the 0.8 threshold behavior.
@@ -0,0 +1,120 @@
1
+ # abap-document-cds — claude-opus-4-7
2
+
3
+ - **skill:** abap-document
4
+ - **description:** Document a CDS interface view with associations + currency annotation
5
+ - **latency:** 42479 ms
6
+ - **input tokens:** 6
7
+ - **output tokens:** 2,798
8
+ - **cost:** $0.2099
9
+
10
+ ---
11
+
12
+ ## abap-document — ZI_SalesOrder (CDS Interface View)
13
+
14
+ ## TL;DR
15
+
16
+ **Headline:** `ZI_SalesOrder` is a sales order header interface view exposing `zsales_order` with associations to customer, items, and currency, search-enabled on customer ID.
17
+
18
+ **Top 3 (priority order — most important first):**
19
+ 1. **Interface view in a VDM-style layering** — naming prefix `ZI_` and associations to `ZI_Customer` / `ZI_SalesOrderItem` indicate this is the I-layer; consumption should happen via a `ZC_` view, not directly.
20
+ 2. **Search + text association configured on `CustomerIDForSearch`** — fuzzy search (threshold 0.8) with text resolved via `_Customer.CustomerName`; this field is a duplicate exposure of `customer_id` purely for search semantics.
21
+ 3. **Amount/currency reference pair (`NetAmount` ↔ `CurrencyCode`)** is correctly annotated; `_Currency` association to `I_Currency` makes this view S/4-VDM-compliant for currency handling.
22
+
23
+ **Verdict:** Clean, well-annotated interface view. No code-level concerns — main thing to confirm with the team is whether a consumption view (`ZC_SalesOrder`) exists on top and whether `CustomerIDForSearch` duplication is intentional.
24
+
25
+ ---
26
+
27
+ > ⚠️ **Note:** This documentation is based solely on the source you pasted. I did not call `sap_get_source`, `sap_object_structure`, or `sap_usage_references`, so package, author, change history, and where-used data are not included. If you want the full document with dependencies and metadata, re-run and I'll pull it from the system.
28
+
29
+ ## ZI_SalesOrder (DDLS — CDS View Entity)
30
+
31
+ ### Overview
32
+ Package: *not retrieved* | Author: *not retrieved* | Last changed: *not retrieved*
33
+
34
+ `ZI_SalesOrder` is an **interface-layer CDS view entity** over the custom sales order header table `zsales_order`. It exposes header fields, exposes associations to customer, items, and currency, and enables fuzzy search on the customer ID with the customer name surfaced as text.
35
+
36
+ ### Purpose
37
+ Provide a reusable, semantically annotated interface for sales order header data following VDM (Virtual Data Model) conventions:
38
+
39
+ - Acts as the **single source of truth for the sales order header** in the I-layer.
40
+ - Wires up the **associations** that downstream consumption views, Fiori elements apps, and analytical views can navigate (customer, items, currency).
41
+ - Enables **enterprise search** over the order's customer with name resolution.
42
+ - Establishes **currency semantics** so amounts render correctly in UIs and analytics.
43
+
44
+ It does **not** implement business logic, filtering, or authorization beyond `#CHECK` (DCL-based row-level auth, if a DCL exists for this entity).
45
+
46
+ ### Interface
47
+
48
+ **Type:** `define view entity` (modern CDS, not legacy `define view`)
49
+
50
+ **Source:** `zsales_order` (aliased as `Header`)
51
+
52
+ **Key:**
53
+ | Field | Type source | Notes |
54
+ |---|---|---|
55
+ | `SalesOrderID` | `Header.sales_order_id` | Primary key |
56
+
57
+ **Exposed fields:**
58
+ | Element | Source | Semantics / Annotations |
59
+ |---|---|---|
60
+ | `SalesOrderID` (key) | `sales_order_id` | — |
61
+ | `CustomerID` | `customer_id` | — |
62
+ | `OrderDate` | `order_date` | — |
63
+ | `CustomerIDForSearch` | `customer_id` | `@Search.defaultSearchElement: true`, fuzziness 0.8, text from `_Customer.CustomerName` |
64
+ | `NetAmount` | `net_amount` | `@Semantics.amount.currencyCode: 'CurrencyCode'` |
65
+ | `CurrencyCode` | `currency_code` | Currency reference field |
66
+ | `CreatedAt` | `created_at` | Admin field |
67
+ | `CreatedBy` | `created_by` | Admin field |
68
+ | `LastChangedAt` | `last_changed_at` | Admin field |
69
+ | `LastChangedBy` | `last_changed_by` | Admin field |
70
+
71
+ **Associations (exposed):**
72
+ | Name | Cardinality | Target | Join condition |
73
+ |---|---|---|---|
74
+ | `_Customer` | `[0..1]` | `ZI_Customer` | `CustomerID = _Customer.CustomerID` |
75
+ | `_SalesOrderItem` | `[0..*]` | `ZI_SalesOrderItem` | `SalesOrderID = _SalesOrderItem.SalesOrderID` |
76
+ | `_Currency` | `[0..1]` | `I_Currency` (SAP standard) | `CurrencyCode = _Currency.Currency` |
77
+
78
+ **View-level annotations:**
79
+ - `@AccessControl.authorizationCheck: #CHECK` — DCL authorization is checked at runtime if a DCL is defined; otherwise warnings issued.
80
+ - `@EndUserText.label: 'Sales Order — Interface View'`
81
+ - `@Search.searchable: true` — view participates in enterprise search.
82
+
83
+ ### Logic Flow
84
+
85
+ There is no procedural logic — this is a projection view. At runtime:
86
+
87
+ 1. **Select** header rows from `zsales_order`.
88
+ 2. **Apply DCL** (`#CHECK`) if a corresponding DCL role exists on `ZI_SalesOrder`.
89
+ 3. **Expose** the renamed fields and forward the three associations to consumers.
90
+ 4. On association navigation, the database joins to `ZI_Customer`, `ZI_SalesOrderItem`, or `I_Currency` as needed.
91
+ 5. For search queries, `CustomerIDForSearch` is the entry point; the engine performs fuzzy matching (threshold 0.8) and shows `_Customer.CustomerName` as descriptive text.
92
+
93
+ ### Data Access
94
+ | Table/View | Operation | Purpose |
95
+ |---|---|---|
96
+ | `zsales_order` | READ | Source table — header data |
97
+ | `ZI_Customer` (via `_Customer`) | READ on navigation | Customer master for text and details |
98
+ | `ZI_SalesOrderItem` (via `_SalesOrderItem`) | READ on navigation | Child items |
99
+ | `I_Currency` (via `_Currency`) | READ on navigation | Standard currency master (TCURC) |
100
+
101
+ ### Dependencies
102
+
103
+ *Where-used not retrieved — re-run with system access to populate.*
104
+
105
+ **Calls (compile-time dependencies):**
106
+ - Source table: `zsales_order`
107
+ - Associated views: `ZI_Customer`, `ZI_SalesOrderItem`, `I_Currency`
108
+
109
+ **Called by:** *unknown — typically a `ZC_SalesOrder` consumption view, OData service bindings, or analytical views; confirm via `sap_usage_references`.*
110
+
111
+ ### Error Handling
112
+ None at view level. Authorization failures via DCL would result in rows being filtered out, not exceptions. If `zsales_order` or any associated view is inactive or missing, the view itself will not activate.
113
+
114
+ ### Notes / Observations
115
+ - **`CustomerIDForSearch` duplicates `CustomerID`** — this is a common pattern to keep `CustomerID` "clean" (no search annotations) while a separate element carries the search semantics. Confirm this is intentional and not legacy.
116
+ - **`@AccessControl.authorizationCheck: #CHECK`** requires a DCL role (`define role` on `ZI_SalesOrder`) to actually enforce anything. Verify a DCL exists; otherwise this is effectively `#NOT_REQUIRED` with an activation warning.
117
+ - **VDM naming** (`ZI_` prefix) suggests this is part of a layered model. Best practice: do not consume this view directly from UIs — build a `ZC_SalesOrder` on top.
118
+ - **`I_Currency`** is the SAP-standard currency interface view; the association is correctly modeled for S/4HANA-style currency reference.
119
+ - **No `@Metadata.allowExtensions` annotation** — if extension is expected (CDS view extensions for customer fields), add `@Metadata.allowExtensions: true`.
120
+ - **Purpose of `CustomerIDForSearch`** is clear from annotations, but the broader business process (order-to-cash? B2B sales?) **is not derivable from the source** — confirm with the business team.