@cspeach/cli 0.7.1 → 0.9.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/bench/README.md +78 -0
- package/bench/prompts/abap-document-cds.md +44 -0
- package/bench/prompts/abap-explain-bdef-handler.md +57 -0
- package/bench/prompts/abap-test-method.md +42 -0
- package/bench/results/abap-document-cds/claude-haiku-4-5.md +189 -0
- package/bench/results/abap-document-cds/claude-opus-4-7.md +120 -0
- package/bench/results/abap-document-cds/claude-sonnet-4-6.md +151 -0
- package/bench/results/abap-explain-bdef-handler/claude-haiku-4-5.md +112 -0
- package/bench/results/abap-explain-bdef-handler/claude-opus-4-7.md +101 -0
- package/bench/results/abap-explain-bdef-handler/claude-sonnet-4-6.md +101 -0
- package/bench/results/abap-test-method/claude-haiku-4-5.md +186 -0
- package/bench/results/abap-test-method/claude-opus-4-7.md +193 -0
- package/bench/results/abap-test-method/claude-sonnet-4-6.md +234 -0
- package/dist/agent/loop.js +144 -26
- package/dist/agent/steering-queue.js +27 -0
- package/dist/approvals/jwt.js +45 -5
- package/dist/approvals/render.js +38 -0
- package/dist/classifier/client.js +6 -2
- package/dist/commands/plan-resume.js +308 -0
- package/dist/config/loader.js +13 -6
- package/dist/cost/pricing.js +14 -5
- package/dist/one-shot.js +6 -0
- package/dist/projects/email-template.js +2 -0
- package/dist/projects/extract-plan.js +85 -0
- package/dist/projects/index.js +3 -0
- package/dist/projects/plan-run.js +120 -0
- package/dist/projects/plan-schema.js +150 -0
- package/dist/projects/promote-command.js +1 -0
- package/dist/projects/save-command.js +17 -1
- package/dist/projects/status.js +20 -0
- package/dist/projects/validate.js +2 -0
- package/dist/renderer/question-normalizer.js +16 -6
- package/dist/renderer/syntax.js +16 -1
- package/dist/renderer/thinking-heartbeat.js +13 -1
- package/dist/repl/at-picker.js +39 -20
- package/dist/repl/slash-picker.js +15 -4
- package/dist/repl.js +216 -17
- package/dist/router/classifier.js +74 -8
- package/dist/skill-catalog.js +8 -2
- package/dist/tools/subagent/agent_run.js +12 -0
- package/dist/ui/app.js +80 -11
- package/dist/ui/body.js +61 -2
- package/dist/ui/command-palette.js +46 -10
- package/dist/ui/file-palette.js +44 -0
- package/dist/ui/footer.js +8 -5
- package/dist/ui/line-resolution.js +81 -0
- package/dist/ui/turn-status-emitter.js +52 -0
- package/dist/ui/turn-status.js +59 -0
- package/dist/ui/widgets/ask-question-modal.js +26 -1
- package/package.json +6 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# abap-document-cds — claude-sonnet-4-6
|
|
2
|
+
|
|
3
|
+
- **skill:** abap-document
|
|
4
|
+
- **description:** Document a CDS interface view with associations + currency annotation
|
|
5
|
+
- **latency:** 45194 ms
|
|
6
|
+
- **input tokens:** 3
|
|
7
|
+
- **output tokens:** 2,460
|
|
8
|
+
- **cost:** $0.0369
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ZI_SalesOrder (CDS View Entity)
|
|
13
|
+
|
|
14
|
+
## TL;DR
|
|
15
|
+
|
|
16
|
+
**Headline:** `ZI_SalesOrder` is a search-enabled interface view entity exposing sales order header data with associations to customer, line items, and currency — the foundational read layer for sales order consumption.
|
|
17
|
+
|
|
18
|
+
**Top 3 (priority order — most important first):**
|
|
19
|
+
1. **Dual customer ID fields** — `CustomerID` and `CustomerIDForSearch` both map to `Header.customer_id`; the search field carries fuzzy-search and text-element annotations while the key field is clean — consumers must understand which to use for display vs. filtering.
|
|
20
|
+
2. **Access control is enforced (`#CHECK`)** — a DCL (`ZI_SalesOrder.dcls` or equivalent) must exist and be correct, or all data access will be silently blocked; verify the DCL is maintained before consuming in new apps.
|
|
21
|
+
3. **Interface ("I\_") naming convention** — the `ZI_` prefix marks this as a reuse/interface view intended to be the stable contract for consumption views (`ZC_`) and OData services; it should not be modified lightly, as changes ripple to all consumers.
|
|
22
|
+
|
|
23
|
+
**Verdict:** Well-structured interface view following SAP VDM conventions; safe to consume but verify the DCL and confirm the `zsales_order` base table schema matches expected field types before exposing via OData.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## ZI_SalesOrder (DDLS — CDS View Entity)
|
|
28
|
+
|
|
29
|
+
### Overview
|
|
30
|
+
|
|
31
|
+
| Attribute | Value |
|
|
32
|
+
|-----------|-------|
|
|
33
|
+
| **Object Type** | CDS View Entity (`define view entity`) |
|
|
34
|
+
| **Base Table** | `zsales_order` (aliased as `Header`) |
|
|
35
|
+
| **End-User Label** | Sales Order — Interface View |
|
|
36
|
+
| **VDM Role** | Interface View (`ZI_` prefix) |
|
|
37
|
+
| **Search-enabled** | Yes (`@Search.searchable: true`) |
|
|
38
|
+
| **Access Control** | Enforced (`@AccessControl.authorizationCheck: #CHECK`) |
|
|
39
|
+
|
|
40
|
+
**Summary:** A sales order header interface view that selects from the custom table `zsales_order` and exposes key business attributes — order identity, customer, amount, currency, and audit timestamps. It publishes three associations (customer master, order items, currency) for use by consumption layers.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### Purpose
|
|
45
|
+
|
|
46
|
+
`ZI_SalesOrder` sits at the **interface layer** of a Virtual Data Model (VDM) for sales order processing. Its responsibilities are:
|
|
47
|
+
|
|
48
|
+
1. **Stable contract** — provide a named, annotated projection of `zsales_order` that upstream consumption views (`ZC_SalesOrder`, OData services, Fiori apps) can depend on without coupling directly to the physical table.
|
|
49
|
+
2. **Search enablement** — expose fuzzy customer search over sales orders via `CustomerIDForSearch` with a fuzziness threshold of 0.8.
|
|
50
|
+
3. **Association hub** — publish navigation paths to related entities (customer, line items, currency) so consumers can traverse the model without re-joining.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### Interface (Exposed Fields)
|
|
55
|
+
|
|
56
|
+
| Field Name | Source Column | Key | Notes |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `SalesOrderID` | `Header.sales_order_id` | ✅ | Primary key |
|
|
59
|
+
| `CustomerID` | `Header.customer_id` | — | Use for filtering / key navigation |
|
|
60
|
+
| `OrderDate` | `Header.order_date` | — | |
|
|
61
|
+
| `CustomerIDForSearch` | `Header.customer_id` | — | Search field; carries fuzzy search & text element annotations — see note below |
|
|
62
|
+
| `NetAmount` | `Header.net_amount` | — | Annotated with `@Semantics.amount.currencyCode: 'CurrencyCode'` |
|
|
63
|
+
| `CurrencyCode` | `Header.currency_code` | — | Reference field for `NetAmount` |
|
|
64
|
+
| `CreatedAt` | `Header.created_at` | — | Audit: creation timestamp |
|
|
65
|
+
| `CreatedBy` | `Header.created_by` | — | Audit: creating user |
|
|
66
|
+
| `LastChangedAt` | `Header.last_changed_at` | — | Audit: last change timestamp |
|
|
67
|
+
| `LastChangedBy` | `Header.last_changed_by` | — | Audit: last changing user |
|
|
68
|
+
|
|
69
|
+
**Exposed Associations**
|
|
70
|
+
|
|
71
|
+
| Association | Target View | Cardinality | Join Condition |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| `_Customer` | `ZI_Customer` | `[0..1]` | `CustomerID = _Customer.CustomerID` |
|
|
74
|
+
| `_SalesOrderItem` | `ZI_SalesOrderItem` | `[0..*]` | `SalesOrderID = _SalesOrderItem.SalesOrderID` |
|
|
75
|
+
| `_Currency` | `I_Currency` (SAP standard) | `[0..1]` | `CurrencyCode = _Currency.Currency` |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### Logic Flow
|
|
80
|
+
|
|
81
|
+
This is a pure projection view — there is no filtering, aggregation, or conditional logic. The runtime behavior is:
|
|
82
|
+
|
|
83
|
+
1. **Full read** of `zsales_order` — no `WHERE` clause, so all header records are exposed (access is controlled by the DCL, not SQL predicates).
|
|
84
|
+
2. **Association resolution on demand** — `_Customer`, `_SalesOrderItem`, and `_Currency` are lazy; joins only occur when a consumer explicitly selects or navigates through them.
|
|
85
|
+
3. **Search index population** — the `@Search.searchable` and `@Search.defaultSearchElement` annotations instruct the search framework to index `CustomerIDForSearch` for Enterprise Search / Fiori search help.
|
|
86
|
+
4. **Amount/currency pairing** — the `@Semantics.amount` annotation links `NetAmount` to `CurrencyCode` so OData/Fiori clients render the currency symbol automatically.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Data Access
|
|
91
|
+
|
|
92
|
+
| Object | Type | Operation | Purpose |
|
|
93
|
+
|---|---|---|---|
|
|
94
|
+
| `zsales_order` | Database Table | READ | Source of all sales order header fields |
|
|
95
|
+
| `ZI_Customer` | CDS View Entity | READ (via assoc.) | Resolve customer name and attributes |
|
|
96
|
+
| `ZI_SalesOrderItem` | CDS View Entity | READ (via assoc.) | Resolve line items for a given order |
|
|
97
|
+
| `I_Currency` | SAP Standard CDS | READ (via assoc.) | Resolve currency description |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### Annotations Explained
|
|
102
|
+
|
|
103
|
+
| Annotation | Location | Effect |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `@AccessControl.authorizationCheck: #CHECK` | View level | A DCL for `ZI_SalesOrder` **must exist**; access is denied if no matching DCL condition passes |
|
|
106
|
+
| `@Search.searchable: true` | View level | Registers the view with the SAP Enterprise Search / SADL search framework |
|
|
107
|
+
| `@Search.defaultSearchElement: true` | `CustomerIDForSearch` | This field is indexed as the primary fuzzy-match target |
|
|
108
|
+
| `@Search.fuzzinessThreshold: 0.8` | `CustomerIDForSearch` | 80% match tolerance — typos and partial matches are accepted |
|
|
109
|
+
| `@ObjectModel.text.element: ['_Customer.CustomerName']` | `CustomerIDForSearch` | Instructs UI to display customer name as the human-readable label for this ID |
|
|
110
|
+
| `@Semantics.amount.currencyCode: 'CurrencyCode'` | `NetAmount` | Pairs the amount field with its currency — required for correct OData metadata and Fiori rendering |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### Dependencies
|
|
115
|
+
|
|
116
|
+
**Calls (depends on):**
|
|
117
|
+
|
|
118
|
+
| Object | Type | Relationship |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| `zsales_order` | Database Table | Data source |
|
|
121
|
+
| `ZI_Customer` | CDS View Entity | Association target |
|
|
122
|
+
| `ZI_SalesOrderItem` | CDS View Entity | Association target |
|
|
123
|
+
| `I_Currency` | SAP Standard CDS View | Association target |
|
|
124
|
+
|
|
125
|
+
**Called by (likely consumers — verify with where-used):**
|
|
126
|
+
|
|
127
|
+
Typical consumers of a `ZI_` interface view in this pattern would be:
|
|
128
|
+
- `ZC_SalesOrder` — a consumption view adding UI annotations for Fiori
|
|
129
|
+
- OData service definitions referencing this view
|
|
130
|
+
- Other CDS views joining via the published associations
|
|
131
|
+
|
|
132
|
+
> ⚠️ Where-used analysis was not run against the live system for this session. Run `sap_usage_references` on `ZI_SalesOrder` to get the confirmed caller list before making any changes to this view's field list or key structure.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### Error Handling & Authorization
|
|
137
|
+
|
|
138
|
+
- **No exception handling** — this is a declarative CDS view; runtime errors surface as SQL exceptions to the consumer.
|
|
139
|
+
- **Authorization:** `#CHECK` means the framework looks for a DCL object named `ZI_SalesOrder`. If the DCL is missing or its conditions are never satisfied, **no data will be returned** without an explicit error message — a common silent failure. Confirm the DCL is active and correctly maintained.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### Notes & Observations
|
|
144
|
+
|
|
145
|
+
| # | Observation |
|
|
146
|
+
|---|---|
|
|
147
|
+
| 1 | **Dual `customer_id` fields** (`CustomerID` + `CustomerIDForSearch`) is a deliberate SAP VDM pattern — the search field carries annotations that would be inappropriate on a key field. This is correct but must be documented for consumers so they don't treat `CustomerIDForSearch` as a second key. |
|
|
148
|
+
| 2 | **`I_Currency` is an SAP-delivered view** — coupling to standard content is intentional for currency semantics but means upgrades could theoretically affect the association if SAP changes `I_Currency`'s key field name. Low risk, worth noting. |
|
|
149
|
+
| 3 | **No `@VDM.viewType` annotation is present** — adding `@VDM.viewType: #BASIC` would make the VDM role explicit and improve tooling support (e.g. ABAP Development Tools VDM explorer). |
|
|
150
|
+
| 4 | **No client handling** — `zsales_order` is assumed to be client-dependent (standard for custom tables). Verify the table has a `client` field and that ABAP CDS implicit client handling is active, otherwise cross-client data leakage is possible in multi-client systems. |
|
|
151
|
+
| 5 | **`[0..1]` on `_Customer`** — assumes one customer per order. If `customer_id` can be null (guest orders, etc.), the association cardinality is correct but consumers should handle the null navigation case. |
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# abap-explain-bdef-handler — claude-haiku-4-5
|
|
2
|
+
|
|
3
|
+
- **skill:** abap-explain
|
|
4
|
+
- **description:** Explain a managed RAP behavior pool with a validation + determination
|
|
5
|
+
- **latency:** 29022 ms
|
|
6
|
+
- **input tokens:** 2
|
|
7
|
+
- **output tokens:** 2,535
|
|
8
|
+
- **cost:** $0.0127
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## abap-explain — lhc_salesorder behavior pool
|
|
13
|
+
|
|
14
|
+
## TL;DR
|
|
15
|
+
|
|
16
|
+
**Headline:** This is a behavior handler that enforces two automatic rules on sales orders: dates must not be in the past, and net amount must be calculated from quantity × unit price.
|
|
17
|
+
|
|
18
|
+
**Top 3 (priority order — most important first):**
|
|
19
|
+
1. **validateorderdate runs on SAVE and blocks invalid orders.** If order date is before today, the order fails validation and is rejected with an error message. The user cannot save.
|
|
20
|
+
2. **determinenetamount runs on MODIFY and auto-calculates a field.** Whenever quantity or unit price changes, net amount is automatically recalculated and written back — the user does not manually enter it.
|
|
21
|
+
3. **Both methods use LOCAL MODE, meaning they bypass authorization checks.** This is intentional for internal calculation but could be a security blind spot if sensitive logic depends on user permissions.
|
|
22
|
+
|
|
23
|
+
**Verdict:** This is a straightforward and safe behavior handler. The only risk is that `determinenetamount` silently overwrites any manual entry to NetAmount — document this user-facing behavior.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
### What This Code Does
|
|
28
|
+
|
|
29
|
+
This is a **behavior handler** — a behind-the-scenes enforcer for a sales order business object in SAP's modern application development framework (RAP, Restful ABAP Programming). It contains two rules:
|
|
30
|
+
|
|
31
|
+
1. **Order dates cannot be in the past.** If someone tries to save an order dated yesterday, the save fails and an error message appears.
|
|
32
|
+
2. **Net amount is automatically calculated.** Whenever quantity or unit price is entered or changed, the net amount (quantity × unit price) is computed and stored automatically. The user does not type it in.
|
|
33
|
+
|
|
34
|
+
These rules execute transparently whenever the sales order is created or modified, whether via UI, API, or batch job.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### Step by Step
|
|
39
|
+
|
|
40
|
+
1. **Class declaration and inheritance**
|
|
41
|
+
This class (`lhc_salesorder`) inherits from `cl_abap_behav_handler`, which is SAP's base class for behavior handlers. Inheriting from this class registers this code as the enforcer for the sales order business object (`zr_salesorder`). The two METHODS declarations define *what* will happen; the IMPLEMENTATION section defines *how*.
|
|
42
|
+
|
|
43
|
+
2. **validateorderdate: Entry point**
|
|
44
|
+
This method is triggered whenever a user tries to **SAVE** a sales order. The `FOR VALIDATE ON SAVE` phrase means "run this code during the save validation phase, before the database is actually written." The method receives a list of sales order keys (IDs) that are being saved.
|
|
45
|
+
|
|
46
|
+
3. **validateorderdate: Read the order dates**
|
|
47
|
+
The method queries the sales order entity (`SalesOrder`) in LOCAL MODE — a special read mode that does not check user permissions — and retrieves only the `OrderDate` field for each sales order being saved. The results go into a temporary list `lt_orders`.
|
|
48
|
+
|
|
49
|
+
4. **validateorderdate: Check each date**
|
|
50
|
+
For each order, the code compares its `OrderDate` to today's date [retrieved via `cl_abap_context_info=>get_system_date( )`]. If the order date is in the past (less than today), two things happen: (a) the order is marked as **failed** in the `failed-salesorder` list, and (b) an error message is added to the `reported-salesorder` list with the text "Order date must be today or in the future."
|
|
51
|
+
|
|
52
|
+
5. **validateorderdate: Result**
|
|
53
|
+
When the method finishes, SAP checks the `failed` and `reported` tables. If any order is in `failed`, the save is **blocked** — the database is not written, and the user sees the error message. The user must correct the date and retry.
|
|
54
|
+
|
|
55
|
+
6. **determinenetamount: Entry point**
|
|
56
|
+
This method is triggered whenever a user **MODIFIES** (creates or changes) a sales order. The `FOR DETERMINE ON MODIFY` phrase means "run this code after a change is made, to calculate or update derived fields." The method receives the list of sales order keys that were modified.
|
|
57
|
+
|
|
58
|
+
7. **determinenetamount: Read quantity and unit price**
|
|
59
|
+
The method reads the `Quantity` and `UnitPrice` fields for each modified order. Again, LOCAL MODE is used, so no permission checks happen.
|
|
60
|
+
|
|
61
|
+
8. **determinenetamount: Calculate net amount**
|
|
62
|
+
A temporary update list `lt_update` is created. For each order, the code multiplies `Quantity × UnitPrice` to get the net amount, and appends an update instruction to the list. The `%control-NetAmount = if_abap_behv=>mk-on` instruction tells SAP "include this field in the update." [INFERRED: `mk-on` stands for "mark on," meaning "process this field."]
|
|
63
|
+
|
|
64
|
+
9. **determinenetamount: Write back to the entity**
|
|
65
|
+
The `MODIFY ENTITIES` statement applies all the calculated net amounts back to the sales orders in memory. The `FIELDS ( NetAmount )` clause tells SAP "only update this one field." The changes are committed to the in-memory entity state, so when the user saves, the net amount is already populated.
|
|
66
|
+
|
|
67
|
+
10. **Return and chain**
|
|
68
|
+
Both methods return implicitly. If validation fails (step 5), the save stops. If validation passes and `determinenetamount` has recalculated net amounts, the save proceeds with the updated values.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
### SAP Concepts Explained
|
|
73
|
+
|
|
74
|
+
| Term | What It Means |
|
|
75
|
+
|------|--------------|
|
|
76
|
+
| Behavior Handler | A class in SAP's modern application framework (RAP) that enforces business rules on an entity (like a sales order). It runs automatically during create, modify, and save operations without the application code having to call it explicitly. |
|
|
77
|
+
| RAP (Restful ABAP Programming) | SAP's modern approach to building business applications. It separates the business logic (behavior handlers like this one) from the UI, so the same rules apply whether a user is using a web interface, a mobile app, or an API. |
|
|
78
|
+
| FOR VALIDATE ON SAVE | A behavior method trigger that says "run this code during the save validation phase." It runs before the database write, giving the code a chance to reject invalid data. |
|
|
79
|
+
| FOR DETERMINE ON MODIFY | A behavior method trigger that says "run this code whenever the entity is modified (created or changed)." Typically used to calculate derived fields or set defaults. |
|
|
80
|
+
| READ ENTITIES OF | A RAP statement that fetches data from a business entity (like a view or table). It is the modern replacement for SELECT in behavior code. |
|
|
81
|
+
| zr_salesorder | A data model object (CDS view) that defines the sales order entity structure. The "zr_" prefix indicates it is a custom object (not SAP standard). |
|
|
82
|
+
| LOCAL MODE | A read/write mode that bypasses SAP's authorization checks. Used in behavior code so that automatic calculations and validations run regardless of the user's permissions. |
|
|
83
|
+
| %tky | The technical key — a unique identifier (usually primary key fields) of an entity instance. Used to identify which specific sales order is being referenced. |
|
|
84
|
+
| failed-salesorder | A structure SAP populates to mark which entities failed validation. If any entity is in this list at the end of validation, the save is rejected. |
|
|
85
|
+
| reported-salesorder | A structure SAP populates with error messages to display to the user. Each entry includes the entity's key (%tky), the message text, and the severity (error, warning, info). |
|
|
86
|
+
| MODIFY ENTITIES OF | A RAP statement that updates one or more entity instances in memory. Unlike a database UPDATE, this changes the in-memory state before the save. |
|
|
87
|
+
| %control | A control structure that tells SAP which fields to include in an update. Setting `%control-NetAmount = if_abap_behv=>mk-on` means "include NetAmount in this update." |
|
|
88
|
+
| if_abap_behv_message=>severity-error | A constant that marks a message as an error. Used in the reported structure to tell SAP this is a blocker, not a warning. |
|
|
89
|
+
| cl_abap_context_info=>get_system_date( ) | A standard SAP class method that returns today's date in the system. Used here for date comparisons. |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### Key Things to Know
|
|
94
|
+
|
|
95
|
+
1. **Validation blocks the save; determination does not.** If `validateorderdate` marks an order as failed, the entire save is aborted — nothing is written to the database. If `determinenetamount` calculates a field, the value is always applied silently; there is no "reject" path. This is the intended behavior for business rules: validations are gates, determinations are auto-fills.
|
|
96
|
+
|
|
97
|
+
2. **Determination overwrites manual entry.** If a user manually enters a net amount and then changes the quantity, `determinenetamount` will recalculate and overwrite that entry. Document this in the UI — users may expect the field to be editable, but it is effectively read-only because it is always recalculated.
|
|
98
|
+
|
|
99
|
+
3. **LOCAL MODE bypasses all authorization.** Both methods read and write in LOCAL MODE, meaning they ignore AUTHORITY-CHECKs. This ensures the rules apply to all users equally. However, if sensitive calculations (e.g., discount logic, cost center assignment) are added to this handler, they will also bypass permission checks — use normal (non-LOCAL) mode for those and add explicit AUTHORITY-CHECKs.
|
|
100
|
+
|
|
101
|
+
4. **Order of execution matters.** `validateorderdate` runs during the SAVE phase (after all MODIFYs are done), so it validates the final state. `determinenetamount` runs during each MODIFY phase, so it recalculates net amount as soon as quantity or price changes. This is the correct order: calculate first, then validate the result.
|
|
102
|
+
|
|
103
|
+
5. **No error handling for date edge cases.** The code uses `<` (less than) to check if the date is in the past. A date exactly equal to today passes validation. Dates in the future always pass. If the system date is NULL or invalid, the comparison might not behave as expected — no explicit error handling is in place. [INFERRED: This is acceptable for most SAP systems, where the system date is always set correctly.]
|
|
104
|
+
|
|
105
|
+
6. **The messages are hardcoded, not translated.** The error message "Order date must be today or in the future" is English text baked into the code. For a production system, consider using message classes and message numbers for multi-language support.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### What Was Not Analyzed
|
|
110
|
+
- The CDS view definition for `zr_salesorder` was not provided. The exact fields (OrderDate, Quantity, UnitPrice, NetAmount) and their types (date, decimal, etc.) are assumed based on usage in the code, but their definitions and constraints were not examined.
|
|
111
|
+
- No parent behavior definitions or additional derived behavior handlers were provided. The sales order entity may have other rules defined elsewhere that interact with these two methods.
|
|
112
|
+
- The UI layer (Fiori app, Web Dynpro, OData consumer) is not visible. How the user experiences the validation error and the silent net amount recalculation depends on the UI implementation.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# abap-explain-bdef-handler — claude-opus-4-7
|
|
2
|
+
|
|
3
|
+
- **skill:** abap-explain
|
|
4
|
+
- **description:** Explain a managed RAP behavior pool with a validation + determination
|
|
5
|
+
- **latency:** 50316 ms
|
|
6
|
+
- **input tokens:** 6
|
|
7
|
+
- **output tokens:** 2,921
|
|
8
|
+
- **cost:** $0.2192
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Code Explanation — LHC_SALESORDER (Behavior Pool for SalesOrder)
|
|
13
|
+
|
|
14
|
+
## TL;DR
|
|
15
|
+
|
|
16
|
+
**Headline:** This is a RAP (RESTful Application Programming) behavior pool with two handlers attached to the `SalesOrder` business object: one validates that the order date is not in the past, and the other automatically calculates the net amount from quantity × unit price.
|
|
17
|
+
|
|
18
|
+
**Top 3 (priority order — most important first):**
|
|
19
|
+
1. **Validation runs on save and blocks bad data** — `validateorderdate` triggers automatically when the user saves; if `OrderDate` is in the past, the save is rejected with an error message.
|
|
20
|
+
2. **Net amount is auto-calculated on every change** — `determinenetamount` runs whenever quantity or unit price is modified and overwrites `NetAmount` with `Quantity × UnitPrice`. The user does not (and should not) enter this field manually.
|
|
21
|
+
3. **Both handlers are framework-driven, not callable directly** — they fire automatically based on user actions in the Fiori UI or OData API; no application code calls them by name.
|
|
22
|
+
|
|
23
|
+
**Verdict:** Standard, well-structured RAP behavior pool. Read it as two automatic "triggers" that the SAP framework invokes at well-defined moments in the transaction lifecycle.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
**Audience:** Non-ABAP Developer
|
|
28
|
+
**Object Type:** RAP Behavior Pool (Local Handler Class)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### What This Code Does
|
|
33
|
+
|
|
34
|
+
This code defines two automatic rules for sales orders in a modern SAP application. The first rule prevents users from saving a sales order with a date in the past. The second rule automatically computes the order's total amount (`NetAmount`) whenever the quantity or unit price changes, so the user never has to enter it.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### Step by Step
|
|
39
|
+
|
|
40
|
+
1. **Class declaration — registers two handlers with the RAP framework**
|
|
41
|
+
The class `lhc_salesorder` inherits from `cl_abap_behavior_handler`. The two `FOR VALIDATE ON SAVE` and `FOR DETERMINE ON MODIFY` declarations tell the SAP framework: "Call `validateorderdate` right before saving, and call `determinenetamount` immediately whenever the entity is modified." The application never calls these methods directly — the framework does, automatically.
|
|
42
|
+
|
|
43
|
+
2. **`validateorderdate` — read the order dates being saved**
|
|
44
|
+
When the user clicks Save, the framework passes in `keys` (the IDs of the orders being saved). The code reads the `OrderDate` field for each of those orders from the business object `zr_salesorder` into a local table `lt_orders`. `IN LOCAL MODE` means: skip authorization checks here, because we're inside the trusted transaction context.
|
|
45
|
+
|
|
46
|
+
3. **`validateorderdate` — check each order and reject the bad ones**
|
|
47
|
+
For each order, the code compares `OrderDate` to today's date. If the order date is earlier than today, two things happen:
|
|
48
|
+
- The order's key is added to `failed-salesorder` — this tells the framework "do not save this one."
|
|
49
|
+
- A user-facing error message ("Order date must be today or in the future") is added to `reported-salesorder` — this is what the Fiori UI will display to the user.
|
|
50
|
+
|
|
51
|
+
If all orders pass, neither table gets entries and the save proceeds.
|
|
52
|
+
|
|
53
|
+
4. **`determinenetamount` — read the inputs needed for the calculation**
|
|
54
|
+
This handler fires whenever `Quantity` or `UnitPrice` is changed (configured elsewhere in the behavior definition, not shown here). It reads both fields for the modified orders into `lt_orders`.
|
|
55
|
+
|
|
56
|
+
5. **`determinenetamount` — build an update table with the calculated value**
|
|
57
|
+
For each order, the code creates an update entry containing the order's key, the computed `NetAmount` (`Quantity * UnitPrice`), and a `%control` flag that explicitly says "yes, I really mean to update the NetAmount field." Without that control flag, RAP would ignore the field.
|
|
58
|
+
|
|
59
|
+
6. **`determinenetamount` — write the calculated NetAmount back to the business object**
|
|
60
|
+
The `MODIFY ENTITIES` statement pushes the updated `NetAmount` values back into the transactional buffer. The user sees the field update without typing anything.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### SAP Concepts Explained
|
|
65
|
+
|
|
66
|
+
| Term | What It Means |
|
|
67
|
+
|------|--------------|
|
|
68
|
+
| RAP (RESTful Application Programming) | SAP's modern framework for building business applications. Think of it like a backend framework (similar in spirit to Spring Boot or Django) where you declare a business object and the framework handles persistence, validation, OData exposure, and UI integration. |
|
|
69
|
+
| Behavior Pool | The ABAP class that holds the logic (handlers) for a business object. One business object can have one or more behavior pools. |
|
|
70
|
+
| Business Object (here: `zr_salesorder`) | The logical entity the framework manages — like a "model" in MVC. It has fields, keys, associations, and behaviors. The `zr_` prefix marks it as a custom (customer-defined) root entity. |
|
|
71
|
+
| `FOR VALIDATE ON SAVE` | A handler type. The framework calls this method right before committing changes to the database. Its job is to flag invalid records and prevent the save. |
|
|
72
|
+
| `FOR DETERMINE ON MODIFY` | A handler type. The framework calls this method immediately after any modification, *before* the user sees the result. Its job is to compute derived fields. |
|
|
73
|
+
| `keys` | A table of entity keys (IDs) that the framework passes in, telling the handler which records to act on. Handlers should only touch these records, not all records. |
|
|
74
|
+
| `READ ENTITIES` / `MODIFY ENTITIES` | RAP-specific statements that read from and write to the transactional buffer (the in-memory staging area for changes), not directly to the database. The database write happens later, during the save phase. |
|
|
75
|
+
| `IN LOCAL MODE` | A modifier that tells RAP "skip authorization and feature-control checks for this operation." Used inside trusted handler logic where the framework has already verified the user's right to perform the overall action. |
|
|
76
|
+
| `%tky` | The "transactional key" — a composite identifier RAP uses internally to track each entity instance during a transaction. Always use this when referencing a specific record in handler tables. |
|
|
77
|
+
| `%control` | A structure that explicitly marks which fields you intend to change. RAP requires you to opt-in field by field (`mk-on`) to prevent accidental overwrites of fields you didn't mean to touch. |
|
|
78
|
+
| `failed` / `reported` | Two output tables every handler can populate. `failed` lists records that should be rejected; `reported` carries user-facing messages (errors, warnings, info) back to the UI. |
|
|
79
|
+
| `cl_abap_context_info=>get_system_date( )` | A helper method that returns today's date according to the SAP application server. Preferred over the older `sy-datum` system variable in modern code because it's testable and time-zone-aware. |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### Key Things to Know
|
|
84
|
+
|
|
85
|
+
1. **Both handlers are invoked by the framework, never by application code.** You will not find any `CALL METHOD validateorderdate` anywhere — the linkage is declarative, defined in the behavior definition file (the `.bdef` artifact, not shown here).
|
|
86
|
+
|
|
87
|
+
2. **`determinenetamount` will overwrite any value the user types into `NetAmount`.** Because it runs on every modification of `Quantity` or `UnitPrice`, the user's manual edits to `NetAmount` will be silently replaced. The `NetAmount` field should be marked read-only in the UI to avoid confusing users.
|
|
88
|
+
|
|
89
|
+
3. **The validation message is hard-coded in English.** `'Order date must be today or in the future'` is a literal string, not a translatable message class. In a real production system this should be replaced with a message from a message class so it can be translated.
|
|
90
|
+
|
|
91
|
+
4. **`IN LOCAL MODE` skips authorization checks — by design.** This is correct inside RAP handlers because the framework already authorized the overall transaction. Do not copy this pattern into general-purpose code outside RAP handlers.
|
|
92
|
+
|
|
93
|
+
5. **No COMMIT WORK appears anywhere — and that's intentional.** In RAP, the application code never commits directly. The framework manages the save sequence (validations → determinations → adjust numbers → save → commit) and decides when to commit based on the outcome of the handlers.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### What Was Not Analyzed
|
|
98
|
+
|
|
99
|
+
- **The behavior definition (`zr_salesorder.bdef`)** — this file declares which handler runs on which trigger and on which fields. Without seeing it, the exact trigger conditions for `determinenetamount` (which field changes cause it to fire) are [INFERRED] from the field names read in the handler.
|
|
100
|
+
- **The CDS view `zr_salesorder`** — the field list, key definition, and associations of the business object were not provided. Field names like `OrderDate`, `Quantity`, `UnitPrice`, `NetAmount` are visible in use but their data types are not confirmed here.
|
|
101
|
+
- **The exception/message class for translatable texts** — none is referenced, which is itself a finding (see Key Things to Know #3).
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# abap-explain-bdef-handler — claude-sonnet-4-6
|
|
2
|
+
|
|
3
|
+
- **skill:** abap-explain
|
|
4
|
+
- **description:** Explain a managed RAP behavior pool with a validation + determination
|
|
5
|
+
- **latency:** 45091 ms
|
|
6
|
+
- **input tokens:** 3
|
|
7
|
+
- **output tokens:** 2,069
|
|
8
|
+
- **cost:** $0.0310
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Code Explanation — LHC_SALESORDER Behavior Pool
|
|
13
|
+
|
|
14
|
+
**Audience:** Non-ABAP Developer
|
|
15
|
+
**Object Type:** ABAP RAP Behavior Pool (Local Handler Class)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## TL;DR
|
|
20
|
+
|
|
21
|
+
**Headline:** A RAP behavior handler class that enforces a date validation rule and auto-calculates net amount on every sales order save/modify event.
|
|
22
|
+
|
|
23
|
+
**Top 3 (priority order — most important first):**
|
|
24
|
+
1. `validateorderdate` is a **hard gate on save** — any sales order with a past `OrderDate` is blocked from being saved, with an error message returned to the UI automatically.
|
|
25
|
+
2. `determinenetamount` **silently overwrites** `NetAmount` every time `Quantity` or `UnitPrice` changes — the field cannot be manually set by the user because the determination always recalculates it.
|
|
26
|
+
3. Both methods use `IN LOCAL MODE`, meaning they **bypass authorization and feature-control checks** — this is intentional for framework-triggered logic but worth knowing if you're auditing security boundaries.
|
|
27
|
+
|
|
28
|
+
**Verdict:** Straightforward, correct RAP boilerplate — understand the trigger timing (ON SAVE vs. ON MODIFY) and the `IN LOCAL MODE` implication before extending this code.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Code Explanation — LHC_SALESORDER Behavior Pool
|
|
33
|
+
|
|
34
|
+
**Audience:** Non-ABAP Developer
|
|
35
|
+
**Object Type:** ABAP RAP Behavior Pool — Local Handler Class (`lhc_`)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
### What This Code Does
|
|
40
|
+
|
|
41
|
+
This code enforces two automatic business rules on a sales order object in SAP's RAP framework. First, it prevents anyone from saving a sales order if the order date is in the past. Second, it automatically calculates the net amount of an order (quantity × unit price) whenever the order is modified — so the user never has to enter that field manually.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### Step by Step
|
|
46
|
+
|
|
47
|
+
1. **Class definition — declaring the two rules**
|
|
48
|
+
The class `lhc_salesorder` inherits from `cl_abap_behavior_handler`, which is SAP's base class for all RAP behavior handlers. It declares two methods: one that *validates* data before saving, and one that *determines* (auto-calculates) a field when data changes. These are not normal methods you call manually — the RAP framework calls them automatically at the right moment.
|
|
49
|
+
|
|
50
|
+
2. **`validateorderdate` — triggered just before saving**
|
|
51
|
+
When a user hits "Save" on a sales order, the RAP framework calls this method automatically (`FOR VALIDATE ON SAVE`). It reads the `OrderDate` field for every sales order that is about to be saved [`READ ENTITIES IN LOCAL MODE`].
|
|
52
|
+
|
|
53
|
+
3. **Date check — block past dates**
|
|
54
|
+
For each order, it compares `OrderDate` against today's date (retrieved via `cl_abap_context_info=>get_system_date()`). If the order date is in the past, the order is added to the `failed-salesorder` table — which tells the framework "this record failed validation, do not save it." An error message is also added to `reported-salesorder`, which the framework sends back to the UI so the user sees: *"Order date must be today or in the future."* No exception is thrown; the framework handles the rest.
|
|
55
|
+
|
|
56
|
+
4. **`determinenetamount` — triggered whenever the order is modified**
|
|
57
|
+
This method runs automatically whenever a sales order is created or changed (`FOR DETERMINE ON MODIFY`). It reads both `Quantity` and `UnitPrice` for the affected orders.
|
|
58
|
+
|
|
59
|
+
5. **Calculate and write back the net amount**
|
|
60
|
+
For every order read, it computes `NetAmount = Quantity × UnitPrice` and builds an update list [`lt_update`]. The `%control-NetAmount = if_abap_behv=>mk-on` line is a control flag that explicitly tells the framework "this field is intentionally being set" — without it, the framework might ignore the update. The method then writes the calculated value back to the business object using `MODIFY ENTITIES IN LOCAL MODE`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### SAP Concepts Explained
|
|
65
|
+
|
|
66
|
+
| Term | What It Means |
|
|
67
|
+
|------|--------------|
|
|
68
|
+
| RAP (ABAP RESTful Application Programming Model) | SAP's modern framework for building business objects (like sales orders) with standardised create/read/update/delete behaviour, validations, and UI integration. Think of it as SAP's version of a domain model + REST controller in one. |
|
|
69
|
+
| Behavior Pool | The ABAP class file that contains the actual code implementing a RAP business object's rules — validations, determinations, actions. The "pool" holds one or more local handler classes. |
|
|
70
|
+
| `FOR VALIDATE ON SAVE` | A RAP hook. The framework calls this method automatically just before committing data to the database. If validation fails, the save is blocked. |
|
|
71
|
+
| `FOR DETERMINE ON MODIFY` | A RAP hook. The framework calls this method automatically whenever the business object's data is changed (created or updated), so computed fields can be recalculated. |
|
|
72
|
+
| `READ ENTITIES` | RAP's way of reading data from a business object — goes through the object's own layer rather than hitting the database directly. Respects the object's in-memory state (changes not yet saved). |
|
|
73
|
+
| `MODIFY ENTITIES` | RAP's way of writing data back to a business object from within the framework itself. Used here to write the calculated `NetAmount` back. |
|
|
74
|
+
| `IN LOCAL MODE` | Skips re-checking authorizations and feature controls when reading or writing inside the framework. Used here because the framework itself is already trusted to call these methods at the right time. |
|
|
75
|
+
| `%tky` | Short for "transactional key" — the unique identifier of a business object instance inside a RAP transaction. It may include more fields than just the primary key, depending on the object's draft-handling setup. |
|
|
76
|
+
| `%control` | A parallel structure that accompanies every RAP entity instance. Each field in `%control` is a flag (`on`/`off`) saying whether the corresponding data field should be processed. Required when writing back computed values. |
|
|
77
|
+
| `failed-salesorder` | A framework-managed result parameter. Adding a key to it tells RAP: "this record has a hard error — do not save it." |
|
|
78
|
+
| `reported-salesorder` | A framework-managed result parameter. Adding an entry here attaches a user-visible message (info, warning, or error) to a specific record. |
|
|
79
|
+
| `new_message_with_text(...)` | A convenience function that creates a simple ad-hoc message object without needing a message class. The `severity-error` severity makes it a blocking error on the UI. |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### Key Things to Know
|
|
84
|
+
|
|
85
|
+
1. **Validation blocks the entire save for the affected record, not just that field.** If `OrderDate` is invalid, the whole sales order is rejected — not just the date field. Other records in the same save operation that pass validation are still saved normally.
|
|
86
|
+
|
|
87
|
+
2. **`NetAmount` is always overwritten — the user cannot override it.** Because `determinenetamount` fires on every modify, any manual entry a user makes in `NetAmount` (if the UI even exposes it as editable) will be immediately recalculated and replaced. This field is effectively read-only from the user's perspective.
|
|
88
|
+
|
|
89
|
+
3. **`IN LOCAL MODE` means these methods do not re-trigger each other.** Writes done inside `IN LOCAL MODE` do not fire other determinations or validations again. This prevents infinite loops (e.g., `determinenetamount` writing `NetAmount` would not re-trigger `determinenetamount`).
|
|
90
|
+
|
|
91
|
+
4. **The date validation uses server time, not the user's local time or timezone.** `cl_abap_context_info=>get_system_date()` returns the SAP application server's date. In global deployments with users in different timezones, a date that looks like "today" to the user might be "yesterday" on the server, causing surprising validation failures.
|
|
92
|
+
|
|
93
|
+
5. **There is no null/zero guard on the multiplication.** If `Quantity` or `UnitPrice` is initial (i.e., zero or blank — the ABAP default for unset numeric fields), `NetAmount` will be calculated as zero without any warning. Whether that is correct depends on whether those fields are mandatory elsewhere in the behavior definition.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### What Was Not Analyzed
|
|
98
|
+
|
|
99
|
+
- **`zr_salesorder` (the RAP Business Object / CDS view)** — referenced throughout but not provided. The full field list, key structure, draft table setup, and what other validations or determinations are defined in the behavior definition (`.bdef`) are not visible here.
|
|
100
|
+
- **`zcx_not_authorized` / authorization objects** — no authorization checks are present in this handler; they would be defined elsewhere in the behavior definition or a separate handler class.
|
|
101
|
+
- The **behavior definition file** (`zr_salesorder.bdef`) that registers `validateorderdate` and `determinenetamount` as the triggers for these methods was not provided. The timing and conditions under which these methods fire are configured there.
|