upload current sources

This commit is contained in:
2026-09-16 01:47:28 -07:00
commit f8b45ea4a5
109 changed files with 26012 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
# RAP handover — Arena change-approval BO (stage 1, 2026-09-15)
Target: development tenant `my431353` (ADT destination `0M7DEJI`), package `ZARENA_PLM`
(sub-package of `ZLOCAL`, no transport). The ADT MCP Server can create objects and activate
them but cannot write source (PRD V-4), so every file in this folder is a **ready-to-paste
source** for the VS Code ADT editor. Each file header names the target object, its
`abap:` URI and says "replace entire content".
Every SAP behavioral claim in these files carries a `[source: … , verified 2026-09-15]`
tag; the sources are `abap-docs` (ABAP Cloud keyword docs + SAP samples), `sap-docs`
(SAP Help) and `sap-released-objects` (release states).
## What exists in the tenant right now
Generated by `x-ui-service` (all **active**, ATC 0 findings on 12 objects, 2026-09-15):
| Object | Type | Notes |
|---|---|---|
| `ZZCHNGPPROVAL000` | TABL | persistent table (generator naming) |
| `ZZR_CHANGEAPPROVAL000` | DDLS / BDEF / DCLS | root view entity, managed BDEF, access control |
| `ZZBP_R_CHANGEAPPROVAL000` | CLAS | behavior pool of the base BDEF |
| `ZZC_CHANGEAPPROVAL000` | DDLS / BDEF / DDLX / DCLS | UI projection view, projection BDEF, metadata extension, access control |
| `ZZBP_C_CHANGEAPPROVAL000` | CLAS | projection behavior pool — **delete it** (step 9) |
| `ZZUI_ARENA_CHANGE_APPRVL000_O4` | SRVD + SRVB | service definition and OData V4 **UI** binding (same name), **unpublished** |
| `ZZARENA_CHANGE_APPROVAL000` | RONT + NONT | SAP object type / node type |
Created empty via `create_object` (exist, **no source, inactive**):
| Object | Type | Purpose |
|---|---|---|
| `ZD_CHANGEAPPROVAL_REJECTP` | DDLS | abstract entity — `reject` parameter (`RejectReason`) |
| `ZD_CHANGEAPPROVAL_SYNCEDP` | DDLS | abstract entity — `markSynced` parameter (`SapChangeNumber`) |
| `ZD_CHANGEAPPROVAL_FAILEDP` | DDLS | abstract entity — `markFailed` parameter (`ErrorMessage`) |
| `ZC_CHANGEAPPROVAL_A2X` | DDLS + DCLS | A2X projection view (worker) and its access control |
| `ZAPI_ARENA_CHANGE_APPROVAL` | SRVD | Web API service definition (worker) |
Not yet creatable (the MCP validation refuses until the referenced objects are active):
| Object | Type | Blocker seen 2026-09-15 |
|---|---|---|
| `ZC_CHANGEAPPROVAL_A2X` | BDEF (projection) | "The referenced STOB object ZC_CHANGEAPPROVAL_A2X does not exist" |
| `ZAPI_ARENA_CHANGE_APPROVAL_A2X` | SRVB (OData V4 - Web API) | "An active version of the Service Definition ZAPI_ARENA_CHANGE_APPROVAL does not exist" |
## Paste-and-activate sequence
Open each object in the VS Code ADT editor (or ADT Eclipse), replace the whole source with
the file content, save, activate. Activation order matters — dependencies are top-down.
1. `zzchngpproval000.tabl.asddls` → table `ZZCHNGPPROVAL000`. (File name uses the
abap-file-formats extension by analogy with `.ddls.asddls`; the tenant object is the
same regardless.)
2. `zd_changeapproval_rejectp.ddls.asddls`, `zd_changeapproval_syncedp.ddls.asddls`,
`zd_changeapproval_failedp.ddls.asddls` → the three abstract entities.
3. `zzr_changeapproval000.ddls.asddls` → root view entity.
4. `zzr_changeapproval000.bdef.asbdef` → base BDEF.
Watch for a message about the unmapped elements `StatusText`, `StatusCriticality`,
`IsApprovable`, `IsRetriable` (CDS-calculated, deliberately not in the mapping). A
warning is acceptable; an **error** means the fallback in the file header applies.
5. `zzbp_r_changeapproval000.clas.abap` (global class) and
`zzbp_r_changeapproval000.clas.locals_imp.abap` (Local Types tab) → behavior pool.
6. `zzc_changeapproval000.ddls.asddls` and `zc_changeapproval_a2x.ddls.asddls` → both
projection views.
7. `zzc_changeapproval000.bdef.asbdef` → UI projection BDEF.
8. Create the A2X projection BDEF (now possible): ADT *New Behavior Definition* on
`ZC_CHANGEAPPROVAL_A2X`, implementation type *Projection* — or via MCP:
`abap_creation-run_validation` + `create_object` with objectType `BDEF/BDO` and
`{"behaviorDefinitionType":"definition","packageName":"ZARENA_PLM","rootEntity":"ZC_CHANGEAPPROVAL_A2X","name":"ZC_CHANGEAPPROVAL_A2X","description":"Arena change approval: A2X projection behavior","implementationType":"Projection"}`,
transport `""`. Then paste `zc_changeapproval_a2x.bdef.asbdef`, activate.
9. Delete the generated class `ZZBP_C_CHANGEAPPROVAL000` (no longer referenced by the
projection BDEF; a projection needs a behavior pool only for new actions/functions
[source: abap-docs /abap-docs-cloud/ABENBDL_BDEF_PROJECTION_HEADER, verified 2026-09-15]).
10. `zzr_changeapproval000.dcls.asdcls`, then `zzc_changeapproval000.dcls.asdcls` and
`zc_changeapproval_a2x.dcls.asdcls` → access controls.
11. `zzc_changeapproval000.ddlx.asddlx` → metadata extension.
12. `zzui_arena_change_apprvl000_o4.srvd.srvdsrv` and
`zapi_arena_change_approval.srvd.srvdsrv` → service definitions.
13. Create the Web API binding (now possible): ADT *New Service Binding* on
`ZAPI_ARENA_CHANGE_APPROVAL`, binding type *OData V4 - Web API*, name
`ZAPI_ARENA_CHANGE_APPROVAL_A2X` — or via MCP `create_object` with objectType
`SRVB/SVB` and
`{"packageName":"ZARENA_PLM","name":"ZAPI_ARENA_CHANGE_APPROVAL_A2X","description":"Arena change approval: OData V4 Web API binding (worker)","bindingType":"OData V4 - Web API","serviceDefinition":"ZAPI_ARENA_CHANGE_APPROVAL"}`.
Activate. **Do not publish** either binding (publishing is a later brief, together
with the IAM app / communication scenario).
## Activation URIs for `abap_activate_objects` (after pasting)
Pattern learned live 2026-09-15: `abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/<folder>/<NAME>/<name>.<ext>`
with these folders/extensions (all verified by a successful activation):
| Type | Folder | Extension |
|---|---|---|
| TABL | `Dictionary/Database%20Tables` | `.tabl.json` |
| DDLS | `Core%20Data%20Services/Data%20Definitions` | `.ddls.acds` |
| BDEF | `Core%20Data%20Services/Behavior%20Definitions` | `.bdef.json` (`.bdef.acds`, `.bdef.asbdef`, `.bdef.abap` all fail with "array element 0 is null") |
| DDLX | `Core%20Data%20Services/Metadata%20Extensions` | `.ddlx.acds` |
| DCLS | `Core%20Data%20Services/Access%20Controls` | `.dcls.acds` |
| CLAS | `Source%20Code%20Library/Classes` | `.clas.abap` |
| SRVD | `Business%20Services/Service%20Definitions` | `.srvd.acds` |
| SRVB | `Business%20Services/Service%20Bindings` | `.srvb.json` |
| RONT | `Business%20Services/SAP%20Object%20Types` | `.ront.json` |
| NONT | `Business%20Services/SAP%20Object%20Node%20Types` | `.nont.json` |
In sequence order (`B` = `abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM`):
```
B/Dictionary/Database%20Tables/ZZCHNGPPROVAL000/zzchngpproval000.tabl.json
B/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_REJECTP/zd_changeapproval_rejectp.ddls.acds
B/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_SYNCEDP/zd_changeapproval_syncedp.ddls.acds
B/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_FAILEDP/zd_changeapproval_failedp.ddls.acds
B/Core%20Data%20Services/Data%20Definitions/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.ddls.acds
B/Core%20Data%20Services/Behavior%20Definitions/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.bdef.json
B/Source%20Code%20Library/Classes/ZZBP_R_CHANGEAPPROVAL000/zzbp_r_changeapproval000.clas.abap
B/Core%20Data%20Services/Data%20Definitions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddls.acds
B/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.ddls.acds
B/Core%20Data%20Services/Behavior%20Definitions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.bdef.json
B/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.bdef.json
B/Core%20Data%20Services/Access%20Controls/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.dcls.acds
B/Core%20Data%20Services/Access%20Controls/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.dcls.acds
B/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.dcls.acds
B/Core%20Data%20Services/Metadata%20Extensions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddlx.acds
B/Business%20Services/Service%20Definitions/ZZUI_ARENA_CHANGE_APPRVL000_O4/zzui_arena_change_apprvl000_o4.srvd.acds
B/Business%20Services/Service%20Definitions/ZAPI_ARENA_CHANGE_APPROVAL/zapi_arena_change_approval.srvd.acds
B/Business%20Services/Service%20Bindings/ZZUI_ARENA_CHANGE_APPRVL000_O4/zzui_arena_change_apprvl000_o4.srvb.json
B/Business%20Services/Service%20Bindings/ZAPI_ARENA_CHANGE_APPROVAL_A2X/zapi_arena_change_approval_a2x.srvb.json
```
Then re-run ATC on the package objects (`abap_atc_run`; omit RONT/NONT — the ATC API
answers "No valid data model exists for object type RONT/ROT") and
`abap_business_services-fetch_services` on both bindings.
## Design decisions baked into the files (falsifiable, dated 2026-09-15)
- **Two projections, two service definitions.** The brief asked for both bindings on the
generated service definition *and* for the UI to expose only approve/reject/retrySync
while the worker gets create + mark*. A service definition exposes one projection, and a
projection BDEF exposes exactly what it lists (no automatic inheritance
[source: abap-docs /abap-docs-cloud/ABENBDL_USE_PROJECTION]). Both requirements cannot
hold on one definition, so the A2X path got its own projection view/BDEF/definition and
the Web API binding sits on `ZAPI_ARENA_CHANGE_APPROVAL`. The RAP-2 "read-only from the
UI" requirement wins over the structural wording.
- **`authorization master ( none )`** instead of the generator's `( global )`: ADR-002
makes the IAM app / business catalog the only access control and DCL governs reads;
`( none )` is the documented way to declare "no RAP authorization checks"
[source: abap-docs /abap-docs-cloud/ABENBDL_AUTHORIZATION]. Flip to `( global )` plus a
`FOR GLOBAL AUTHORIZATION` handler when a custom authorization object exists.
- **Derived fields are computed, not persisted.** `StatusText`, `StatusCriticality`,
`IsApprovable`, `IsRetriable` are CASE expressions in the root view; a transactional
query projection cannot hold expressions
[source: abap-docs /abap-docs-cloud/ABENCDS_DEFINE_VIEW_AS_PROJECTION]. They are
`field ( readonly )` and absent from the mapping — see step 4.
- **Action parameters are abstract entities** (`ZD_*`), one element each, because action
input must be an abstract entity or DDIC type
[source: abap-docs /abap-docs-cloud/ABENBDL_ACTION_INPUT_PARAM]. `markSynced` and
`markFailed` therefore take `SapChangeNumber` / `ErrorMessage` as parameters — the only
way an action can "set" them from the worker.
- **`internal update`** keeps `update` out of every OData surface while the actions and
the determination still update fields `IN LOCAL MODE`
[source: abap-docs /abap-docs-cloud/ABENBDL_INTERNAL].
- **RequestedBy** is the technical user of the creator (the worker's communication user in
the A2X flow). Change to `mandatory : create` if the Arena requester should be shown.
## Landmines learned live (KNOWLEDGE.md candidates)
- [2026-09-15] `x-ui-service` generator: every field needs `semanticType` (e.g. `custom`)
or validation fails with "Builtin type of field X is invalid"; semantic-key fields pass
without it. The generator prepends its own `Z` to the `prefix` (`Z` → `ZZ…`) and appends
`000` to every artifact name even with `suffix: ""`; service definition and UI binding
get the same name (`ZZUI_ARENA_CHANGE_APPRVL000_O4`). There is no delete tool in the ADT
MCP, so a naming mistake is permanent from the agent side.
- [2026-09-15] `create_object` for SRVD needs `sourceType` (`"S"` worked; omitted →
"serviceDefinitionInfo.sourceType is null"). BDEF/SRVB creation validates the referenced
DDLS/SRVD **active** version, so source-less objects block dependent creations.
- [2026-09-15] Activation URIs: see table above; BDEF is `.bdef.json`, table `.tabl.json`,
SAP object types live under `Business Services`.
- [2026-09-15] The MCP session id from `get_schema` `referenceContent` is single-use in
practice — fetch a fresh one before each `generate_objects` attempt.
@@ -0,0 +1,10 @@
/* TARGET OBJECT : service definition ZAPI_ARENA_CHANGE_APPROVAL (created empty via ADT MCP 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Definitions/ZAPI_ARENA_CHANGE_APPROVAL/zapi_arena_change_approval.srvd.acds
ACTION : replace entire content, then activate (after ZC_CHANGEAPPROVAL_A2X view + BDEF).
Sources: as ZZUI_ARENA_CHANGE_APPRVL000_O4; odata_v4_webapi is the Web API contract [ABENSRVD_PROVIDER_CONTRACT]. */
@EndUserText.label: 'Arena change approval - Web API (worker)'
define service ZAPI_ARENA_CHANGE_APPROVAL
provider contracts odata_v4_webapi
{
expose ZC_CHANGEAPPROVAL_A2X as ChangeApproval;
}
@@ -0,0 +1,19 @@
// TARGET OBJECT : behavior definition ZC_CHANGEAPPROVAL_A2X (A2X projection BDEF) - DOES NOT EXIST YET.
// The ADT MCP refused to create it while ZC_CHANGEAPPROVAL_A2X (DDLS) had no active source
// ("The referenced STOB object ZC_CHANGEAPPROVAL_A2X does not exist", 2026-09-15).
// Create it after the projection view is active: ADT > New Behavior Definition on ZC_CHANGEAPPROVAL_A2X,
// implementation type Projection (or re-run abap_creation-create_object BDEF/BDO, see README), paste, activate.
// Sources: as ZZC_CHANGEAPPROVAL000. use create lets the worker stage records (ARN-010: the staged payload is what
// gets executed); the worker never sees approve/reject/retrySync (PRD RAP-6).
projection;
strict ( 2 );
define behavior for ZC_CHANGEAPPROVAL_A2X alias ChangeApproval
{
use etag;
use create;
use action markExecuting;
use action markSynced;
use action markFailed;
}
@@ -0,0 +1,10 @@
/* TARGET OBJECT : access control ZC_CHANGEAPPROVAL_A2X (created empty via ADT MCP 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.dcls.acds
ACTION : replace entire content, then activate (after ZC_CHANGEAPPROVAL_A2X view and ZZR_CHANGEAPPROVAL000 DCL).
Sources: as ZZC_CHANGEAPPROVAL000 DCL. */
@EndUserText.label: 'Access control for ZC_CHANGEAPPROVAL_A2X'
@MappingRole: true
define role ZC_CHANGEAPPROVAL_A2X {
grant select on ZC_CHANGEAPPROVAL_A2X
where inheriting conditions from entity ZZR_CHANGEAPPROVAL000;
}
@@ -0,0 +1,38 @@
/* TARGET OBJECT : data definition ZC_CHANGEAPPROVAL_A2X (A2X projection for the worker; created empty via ADT MCP 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.ddls.acds
ACTION : replace entire content, then activate (after ZZR_CHANGEAPPROVAL000).
Notes: separate projection so the Web API exposes create + mark* while the UI projection stays read-only
(PRD RAP-2 / RAP-6); same syntax sources as ZZC_CHANGEAPPROVAL000. No metadata extension here -
a Web API binding carries no UI annotations [source: sap-docs sap-help loio 248b36b7ebce4f7e8c7670c9371adee1, verified 2026-09-15]. */
@EndUserText.label: 'Arena change approval - A2X projection (worker)'
@AccessControl.authorizationCheck: #CHECK
@ObjectModel.semanticKey: ['ItemNumber', 'ChangeType', 'ArenaEcoId']
define root view entity ZC_CHANGEAPPROVAL_A2X
provider contract transactional_query
as projection on ZZR_CHANGEAPPROVAL000
{
key ID,
ItemNumber,
ItemName,
ChangeType,
ArenaEcoId,
@ObjectModel.text.element: ['StatusText']
Status,
StatusText,
StatusCriticality,
RequestedBy,
RequestedOn,
DecidedBy,
DecidedOn,
SapChangeNumber,
ErrorMessage,
RejectReason,
SapPreview,
IsApprovable,
IsRetriable,
CreatedBy,
CreatedAt,
LastChangedBy,
LastChangedAt,
LocalLastChangedAt
}
@@ -0,0 +1,10 @@
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_FAILEDP (abstract entity = parameter of action markFailed; created empty via ADT MCP 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_FAILEDP/zd_changeapproval_failedp.ddls.acds
ACTION : replace entire content, then activate.
Sources: as ZD_CHANGEAPPROVAL_REJECTP. */
@EndUserText.label: 'Arena change approval: markFailed parameter'
define abstract entity ZD_CHANGEAPPROVAL_FAILEDP
{
@EndUserText.label: 'Error message'
ErrorMessage : abap.char(255);
}
@@ -0,0 +1,13 @@
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_REJECTP (abstract entity = parameter of action reject; created empty via ADT MCP 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_REJECTP/zd_changeapproval_rejectp.ddls.acds
ACTION : replace entire content, then activate.
Notes: action input parameters must be a CDS abstract entity or DDIC type; @EndUserText.label on the element
gives the UI label [source: abap-docs /abap-docs-cloud/ABENBDL_ACTION_INPUT_PARAM, verified 2026-09-15];
abstract-entity syntax [source: abap-docs /abap-docs-cloud/ABENBDL_ACTION2_ABEXA, verified 2026-09-15].
The OData parameter name is the element name -> RejectReason, as the Fiori app expects (PRD RAP-2). */
@EndUserText.label: 'Arena change approval: reject parameter'
define abstract entity ZD_CHANGEAPPROVAL_REJECTP
{
@EndUserText.label: 'Reject reason'
RejectReason : abap.char(255);
}
@@ -0,0 +1,10 @@
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_SYNCEDP (abstract entity = parameter of action markSynced; created empty via ADT MCP 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_SYNCEDP/zd_changeapproval_syncedp.ddls.acds
ACTION : replace entire content, then activate.
Sources: as ZD_CHANGEAPPROVAL_REJECTP. */
@EndUserText.label: 'Arena change approval: markSynced parameter'
define abstract entity ZD_CHANGEAPPROVAL_SYNCEDP
{
@EndUserText.label: 'SAP change number'
SapChangeNumber : abap.char(12);
}
@@ -0,0 +1,14 @@
" TARGET OBJECT : class ZZBP_R_CHANGEAPPROVAL000 - GLOBAL CLASS include (behavior pool; generated 2026-09-15)
" abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Source%20Code%20Library/Classes/ZZBP_R_CHANGEAPPROVAL000/zzbp_r_changeapproval000.clas.abap
" ACTION : replace entire content of the global class; the handlers go into "Local Types" (locals_imp file).
" Source: FOR BEHAVIOR OF defines the ABAP behavior pool; the global class implements nothing itself
" [source: abap-docs /abap-docs-cloud/ABAPCLASS_FOR_BEHAVIOR_OF, ABENABAP_BEHAVIOR_POOLS, verified 2026-09-15].
CLASS zzbp_r_changeapproval000 DEFINITION
PUBLIC
ABSTRACT
FINAL
FOR BEHAVIOR OF zzr_changeapproval000.
ENDCLASS.
CLASS zzbp_r_changeapproval000 IMPLEMENTATION.
ENDCLASS.
@@ -0,0 +1,426 @@
" TARGET OBJECT : class ZZBP_R_CHANGEAPPROVAL000 - LOCAL TYPES include (Local Types tab in ADT)
" abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Source%20Code%20Library/Classes/ZZBP_R_CHANGEAPPROVAL000/zzbp_r_changeapproval000.clas.locals_imp.abap
" ACTION : replace entire content, then activate the class (BDEF ZZR_CHANGEAPPROVAL000 must be active first).
"
" Sources (all verified 2026-09-15):
" handler class inherits from CL_ABAP_BEHAVIOR_HANDLER (released) [sap-released-objects public_cloud A]
" FOR INSTANCE FEATURES signature + result via %tky / %features-%action-<name> / if_abap_behv=>fc-o-enabled|disabled
" [abap-docs /abap-docs-cloud/ABAPHANDLER_METH_FEATURES, ABAPDERIVED_TYPES_FEATURES, ABENBDL_ACTIONS_FC]
" FOR MODIFY ... FOR ACTION with RESULT; %param carries the action parameter (keys) and the result ($self)
" [abap-docs /abap-docs-cloud/ABENBDL_ACTION2_ABEXA, ABAPDERIVED_TYPES_PARAM]
" READ ENTITIES ... IN LOCAL MODE ... FIELDS ( ) WITH CORRESPONDING #( keys ) RESULT ... FAILED failed
" [abap-docs /abap-docs-cloud/ABENBDL_ACTIONS_FC example]
" MODIFY ENTITIES ... IN LOCAL MODE (skips feature control and authorization) with UPDATE FIELDS
" [abap-docs /abap-docs-cloud/ABAPMODIFY_ENTITY_ENTITIES, ABAPMODIFY_ENTITY_ENTITIES_OP, ABENBDL_AUTHORIZATION]
" %msg = new_message_with_text( ... ) into reported [abap-docs /abap-docs-cloud/ABENBDL_ACTIONS_FC example]
" FOR DETERMINE ON MODIFY signature [abap-docs /abap-docs-cloud/ABAPHANDLER_METH_DET]
" utclong_current( ) [abap-docs /abap-docs-cloud/ABENUTCLONG_CURRENT]
" cl_abap_context_info=>get_user_technical_name( ) (released class) [sap-docs sap-help loio 7be24435b3e24540a9edad9a2535175a;
" sap-released-objects CL_ABAP_CONTEXT_INFO public_cloud A]
" Design notes:
" - Status transitions are enforced twice: get_instance_features disables the action (UI greys it out / EML fails
" with an entry in failed [ABENBDL_ACTIONS_FC]) AND each action re-checks the current status so an EML caller
" IN LOCAL MODE cannot skip the state machine.
" - The six actions repeat the same read/check/update/read pattern on purpose: each action has its own derived
" key type, so a generic helper would need additional derived-type plumbing that is not worth it for six methods.
" - RequestedBy is the technical user of the creator (for the A2X flow: the worker's communication user).
" If the Arena requester should be shown instead, make RequestedBy mandatory:create in the BDEF and drop it
" from the determination.
CLASS lhc_changeapproval DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
CONSTANTS:
BEGIN OF status,
pending TYPE zzchngpproval000-status VALUE 'PENDING',
approved TYPE zzchngpproval000-status VALUE 'APPROVED',
executing TYPE zzchngpproval000-status VALUE 'EXECUTING',
synced TYPE zzchngpproval000-status VALUE 'SYNCED',
rejected TYPE zzchngpproval000-status VALUE 'REJECTED',
failed TYPE zzchngpproval000-status VALUE 'FAILED',
END OF status.
METHODS get_instance_features FOR INSTANCE FEATURES
IMPORTING keys REQUEST requested_features FOR ChangeApproval RESULT result.
METHODS approve FOR MODIFY
IMPORTING keys FOR ACTION ChangeApproval~approve RESULT result.
METHODS reject FOR MODIFY
IMPORTING keys FOR ACTION ChangeApproval~reject RESULT result.
METHODS retrySync FOR MODIFY
IMPORTING keys FOR ACTION ChangeApproval~retrySync RESULT result.
METHODS markExecuting FOR MODIFY
IMPORTING keys FOR ACTION ChangeApproval~markExecuting RESULT result.
METHODS markSynced FOR MODIFY
IMPORTING keys FOR ACTION ChangeApproval~markSynced RESULT result.
METHODS markFailed FOR MODIFY
IMPORTING keys FOR ACTION ChangeApproval~markFailed RESULT result.
METHODS setInitialStatus FOR DETERMINE ON MODIFY
IMPORTING keys FOR ChangeApproval~setInitialStatus.
ENDCLASS.
CLASS lhc_changeapproval IMPLEMENTATION.
METHOD get_instance_features.
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals)
FAILED failed.
result = VALUE #( FOR approval IN approvals
( %tky = approval-%tky
%features-%action-approve = COND #( WHEN approval-Status = status-pending
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
%features-%action-reject = COND #( WHEN approval-Status = status-pending
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
%features-%action-retrySync = COND #( WHEN approval-Status = status-failed
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
%features-%action-markExecuting = COND #( WHEN approval-Status = status-approved
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
%features-%action-markSynced = COND #( WHEN approval-Status = status-executing
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
%features-%action-markFailed = COND #( WHEN approval-Status = status-executing
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled ) ) ).
ENDMETHOD.
METHOD approve.
" PENDING -> APPROVED; records the decision, never writes SapChangeNumber (PRD RAP-1)
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals)
FAILED failed.
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-pending.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Only PENDING records can be approved (status is { <approval>-Status })| ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
MODIFY ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status DecidedBy DecidedOn )
WITH VALUE #( ( %tky = <approval>-%tky
Status = status-approved
DecidedBy = cl_abap_context_info=>get_user_technical_name( )
DecidedOn = utclong_current( ) ) )
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
ENDLOOP.
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
result = VALUE #( FOR row IN updated ( %tky = row-%tky %param = row ) ).
ENDMETHOD.
METHOD reject.
" PENDING -> REJECTED with a mandatory reason (PRD RAP-2); REJECTED is terminal
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals)
FAILED failed.
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-pending.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Only PENDING records can be rejected (status is { <approval>-Status })| ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
IF key-%param-RejectReason IS INITIAL.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'A reject reason is required' ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
MODIFY ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status RejectReason DecidedBy DecidedOn )
WITH VALUE #( ( %tky = <approval>-%tky
Status = status-rejected
RejectReason = key-%param-RejectReason
DecidedBy = cl_abap_context_info=>get_user_technical_name( )
DecidedOn = utclong_current( ) ) )
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
ENDLOOP.
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
result = VALUE #( FOR row IN updated ( %tky = row-%tky %param = row ) ).
ENDMETHOD.
METHOD retrySync.
" FAILED -> EXECUTING; clears the previous error so the worker re-executes the staged payload (ARN-002 amendment)
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals)
FAILED failed.
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-failed.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Only FAILED records can be retried (status is { <approval>-Status })| ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
MODIFY ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status ErrorMessage )
WITH VALUE #( ( %tky = <approval>-%tky
Status = status-executing
ErrorMessage = '' ) )
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
ENDLOOP.
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
result = VALUE #( FOR row IN updated ( %tky = row-%tky %param = row ) ).
ENDMETHOD.
METHOD markExecuting.
" APPROVED -> EXECUTING (worker claims the record; the UI never offers Approve on an executing record, PRD RAP-6)
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals)
FAILED failed.
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-approved.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Only APPROVED records can start executing (status is { <approval>-Status })| ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
MODIFY ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status )
WITH VALUE #( ( %tky = <approval>-%tky
Status = status-executing ) )
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
ENDLOOP.
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
result = VALUE #( FOR row IN updated ( %tky = row-%tky %param = row ) ).
ENDMETHOD.
METHOD markSynced.
" EXECUTING -> SYNCED (terminal success); SAP is the ledger, the change number is the receipt (ARN-006)
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals)
FAILED failed.
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-executing.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Only EXECUTING records can be marked synced (status is { <approval>-Status })| ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
IF key-%param-SapChangeNumber IS INITIAL.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'The SAP change number is required to mark a record synced' ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
MODIFY ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status SapChangeNumber ErrorMessage )
WITH VALUE #( ( %tky = <approval>-%tky
Status = status-synced
SapChangeNumber = key-%param-SapChangeNumber
ErrorMessage = '' ) )
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
ENDLOOP.
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
result = VALUE #( FOR row IN updated ( %tky = row-%tky %param = row ) ).
ENDMETHOD.
METHOD markFailed.
" EXECUTING -> FAILED with the error text; distinct from REJECTED (predecessor defect, ARN-002)
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals)
FAILED failed.
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-executing.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Only EXECUTING records can be marked failed (status is { <approval>-Status })| ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
IF key-%param-ErrorMessage IS INITIAL.
APPEND VALUE #( %tky = <approval>-%tky ) TO failed-changeapproval.
APPEND VALUE #( %tky = <approval>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'An error message is required to mark a record failed' ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
MODIFY ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status ErrorMessage )
WITH VALUE #( ( %tky = <approval>-%tky
Status = status-failed
ErrorMessage = key-%param-ErrorMessage ) )
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
ENDLOOP.
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
result = VALUE #( FOR row IN updated ( %tky = row-%tky %param = row ) ).
ENDMETHOD.
METHOD setInitialStatus.
" Every created record starts PENDING with the requester audit fields (ARN-002)
READ ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
DELETE approvals WHERE Status IS NOT INITIAL.
IF approvals IS INITIAL.
RETURN.
ENDIF.
MODIFY ENTITIES OF zzr_changeapproval000 IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status RequestedBy RequestedOn )
WITH VALUE #( FOR approval IN approvals
( %tky = approval-%tky
Status = status-pending
RequestedBy = cl_abap_context_info=>get_user_technical_name( )
RequestedOn = utclong_current( ) ) )
REPORTED DATA(update_reported).
reported = CORRESPONDING #( DEEP update_reported ).
ENDMETHOD.
ENDCLASS.
@@ -0,0 +1,20 @@
// TARGET OBJECT : behavior definition ZZC_CHANGEAPPROVAL000 (UI projection BDEF; generated 2026-09-15)
// abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.bdef.json
// ACTION : replace entire content, then activate (after ZZR_CHANGEAPPROVAL000 BDEF + ZZC_CHANGEAPPROVAL000 view).
// Then DELETE the generated class ZZBP_C_CHANGEAPPROVAL000 in ADT: a projection BDEF needs no behavior
// pool unless it defines new actions/functions, and this one reuses everything
// [source: abap-docs /abap-docs-cloud/ABENBDL_BDEF_PROJECTION_HEADER, verified 2026-09-15].
// Sources (verified 2026-09-15): use action / use etag; no automatic inheritance - only what is listed is exposed,
// which is how create/update/delete and the worker actions are kept out of the UI service (PRD RAP-2)
// [abap-docs /abap-docs-cloud/ABENBDL_USE_PROJECTION]. Feature control of reused actions is passed on automatically (same source).
projection;
strict ( 2 );
define behavior for ZZC_CHANGEAPPROVAL000 alias ChangeApproval
{
use etag;
use action approve;
use action reject;
use action retrySync;
}
@@ -0,0 +1,12 @@
/* TARGET OBJECT : access control ZZC_CHANGEAPPROVAL000 (generated 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.dcls.acds
ACTION : replace entire content, then activate (after ZZR_CHANGEAPPROVAL000 DCL).
A transactional query needs its own DCL, inheriting the base view's conditions - there is no implicit inheritance
[source: abap-docs /abap-platform-rap-opensap/week2/unit7#step-5-create-cds-access-control-for-travel-bo-projection-view-and-check-the-result;
/abap-docs-cloud/ABENCDS_DEFINE_VIEW_AS_PROJECTION (access control must be symmetric), verified 2026-09-15]. */
@EndUserText.label: 'Access control for ZZC_CHANGEAPPROVAL000'
@MappingRole: true
define role ZZC_CHANGEAPPROVAL000 {
grant select on ZZC_CHANGEAPPROVAL000
where inheriting conditions from entity ZZR_CHANGEAPPROVAL000;
}
@@ -0,0 +1,35 @@
/* TARGET OBJECT : data definition ZZC_CHANGEAPPROVAL000 (UI projection, generated 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddls.acds
ACTION : replace entire content, then activate (after ZZR_CHANGEAPPROVAL000).
Notes: provider contract transactional_query [source: abap-docs /abap-docs-cloud/ABENCDS_DEFINE_VIEW_AS_PROJECTION, verified 2026-09-15];
@ObjectModel.semanticKey (PRD RAP-8) [source: abap-docs /abap-fiori-showcase/05_object_page_header#handling-semantic-key-fields, verified 2026-09-15];
@Metadata.allowExtensions is required for the metadata extension [source: abap-docs /abap-docs-cloud/ABENCDS_F1_ANNOTATE_VIEW, verified 2026-09-15]. */
@EndUserText.label: 'Arena change approval - UI projection'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@ObjectModel.semanticKey: ['ItemNumber', 'ChangeType', 'ArenaEcoId']
define root view entity ZZC_CHANGEAPPROVAL000
provider contract transactional_query
as projection on ZZR_CHANGEAPPROVAL000
{
key ID,
ItemNumber,
ItemName,
ChangeType,
ArenaEcoId,
@ObjectModel.text.element: ['StatusText']
Status,
StatusText,
StatusCriticality,
RequestedBy,
RequestedOn,
DecidedBy,
DecidedOn,
SapChangeNumber,
ErrorMessage,
RejectReason,
SapPreview,
IsApprovable,
IsRetriable,
LocalLastChangedAt
}
@@ -0,0 +1,142 @@
/* TARGET OBJECT : metadata extension ZZC_CHANGEAPPROVAL000 (generated 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Metadata%20Extensions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddlx.acds
ACTION : replace entire content, then activate (after ZZC_CHANGEAPPROVAL000 view).
Mirrors app/webapp/annotations/annotation.xml (line items, filter bar, header, facets, actions); the app's local
annotations still apply on top at runtime. Labels are literals here; the app carries the i18n texts.
Sources (all verified 2026-09-15, abap-docs /abap-fiori-showcase):
@Metadata.layer + ANNOTATE ENTITY [abap-docs /abap-docs-cloud/ABENCDS_F1_ANNOTATE_VIEW]
@UI.headerInfo typeName/typeNamePlural/typeImageUrl/title/description [05_object_page_header#title-and-subtitle]
@UI.presentationVariant sortOrder + visualizations [03_list_report_content (Defining the Default Sort Order)]
@UI.facet purpose/type/id/label/position/targetQualifier [06_object_page_content (Nested Tabs, RAPTreeview)]
@UI.dataPoint qualifier/title/criticality + #DATAPOINT_REFERENCE header facet [05_object_page_header (Key Value, Progress)]
@UI.identification #FOR_ACTION dataAction/label [05_object_page_header (Regular Actions)]
@UI.lineItem position/importance/criticality [03_list_report_content (Configuring Tables); 06 (Table)]
@UI.selectionField position [02_list_report_header#selection-fields]
@UI.fieldGroup qualifier/position [06_object_page_content (Multi Input Field)]
@UI.textArrangement #TEXT_ONLY [06_object_page_content#displaying-text-instead-of-id]
@UI.multiLineText [05_object_page_header (Plain Text)]; @UI.hidden [06_object_page_content (Hiding Features)]
Default filter Status = PENDING is NOT set here (string-literal escaping inside @UI.selectionVariant.filter is
unverified); the app's annotation.xml SelectionVariant#Default provides it. */
@Metadata.layer: #CUSTOMER
@UI: {
headerInfo: {
typeName: 'Change Approval',
typeNamePlural: 'Change Approvals',
typeImageUrl: 'sap-icon://approvals',
title: { type: #STANDARD, value: 'ItemNumber' },
description: { type: #STANDARD, value: 'ItemName' }
},
presentationVariant: [ {
sortOrder: [ { by: 'Status', direction: #ASC }, { by: 'RequestedOn', direction: #DESC } ],
visualizations: [ { type: #AS_LINEITEM } ]
} ]
}
annotate entity ZZC_CHANGEAPPROVAL000 with
{
@UI.facet: [
{ id: 'StatusPoint', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'StatusPoint' },
{ id: 'Keys', purpose: #HEADER, type: #FIELDGROUP_REFERENCE, targetQualifier: 'Keys', label: 'Reference numbers' },
{ id: 'Change', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, targetQualifier: 'Change', label: 'Change', position: 10 },
{ id: 'Payload', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, targetQualifier: 'Payload', label: 'Staged payload', position: 20 },
{ id: 'Approval', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, targetQualifier: 'Approval', label: 'Approval', position: 30 },
{ id: 'Execution', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, targetQualifier: 'Execution', label: 'Execution', position: 40 }
]
@UI.identification: [
{ position: 10, type: #FOR_ACTION, dataAction: 'approve', label: 'Approve' },
{ position: 20, type: #FOR_ACTION, dataAction: 'reject', label: 'Reject' },
{ position: 30, type: #FOR_ACTION, dataAction: 'retrySync', label: 'Retry sync' }
]
@UI.hidden: true
ID;
@UI: {
lineItem: [ { position: 10, importance: #HIGH } ],
selectionField: [ { position: 10 } ],
fieldGroup: [ { qualifier: 'Change', position: 10 } ]
}
@EndUserText.label: 'Item number'
ItemNumber;
@UI: {
lineItem: [ { position: 20, importance: #LOW } ],
fieldGroup: [ { qualifier: 'Change', position: 20 } ]
}
@EndUserText.label: 'Item name'
ItemName;
@UI: {
lineItem: [ { position: 30, importance: #HIGH } ],
selectionField: [ { position: 30 } ],
fieldGroup: [ { qualifier: 'Change', position: 30 } ]
}
@EndUserText.label: 'Change type'
ChangeType;
@UI: {
lineItem: [ { position: 60, importance: #MEDIUM } ],
selectionField: [ { position: 40 } ],
fieldGroup: [ { qualifier: 'Change', position: 40 }, { qualifier: 'Keys', position: 10 } ]
}
@EndUserText.label: 'Arena ECO'
ArenaEcoId;
@UI: {
lineItem: [ { position: 40, importance: #HIGH, criticality: 'StatusCriticality' } ],
selectionField: [ { position: 20 } ],
fieldGroup: [ { qualifier: 'Approval', position: 10, criticality: 'StatusCriticality' } ],
dataPoint: { qualifier: 'StatusPoint', title: 'Status', criticality: 'StatusCriticality' },
textArrangement: #TEXT_ONLY
}
@EndUserText.label: 'Status'
Status;
@UI.hidden: true
StatusText;
@UI.hidden: true
StatusCriticality;
@UI.fieldGroup: [ { qualifier: 'Approval', position: 20 } ]
@EndUserText.label: 'Requested by'
RequestedBy;
@UI: {
lineItem: [ { position: 50, importance: #MEDIUM } ],
fieldGroup: [ { qualifier: 'Approval', position: 30 } ]
}
@EndUserText.label: 'Requested on'
RequestedOn;
@UI.fieldGroup: [ { qualifier: 'Approval', position: 40 } ]
@EndUserText.label: 'Decided by'
DecidedBy;
@UI.fieldGroup: [ { qualifier: 'Approval', position: 50 } ]
@EndUserText.label: 'Decided on'
DecidedOn;
@UI.fieldGroup: [ { qualifier: 'Approval', position: 60 } ]
@EndUserText.label: 'Reject reason'
RejectReason;
@UI.fieldGroup: [ { qualifier: 'Execution', position: 10 }, { qualifier: 'Keys', position: 20 } ]
@EndUserText.label: 'SAP change number'
SapChangeNumber;
@UI.fieldGroup: [ { qualifier: 'Execution', position: 20 } ]
@EndUserText.label: 'Error message'
ErrorMessage;
@UI: {
fieldGroup: [ { qualifier: 'Payload', position: 10 } ],
multiLineText: true
}
@EndUserText.label: 'Staged payload'
SapPreview;
@UI.hidden: true
IsApprovable;
@UI.hidden: true
IsRetriable;
}
@@ -0,0 +1,35 @@
/* TARGET OBJECT : database table ZZCHNGPPROVAL000 (generated 2026-09-15 by x-ui-service)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Dictionary/Database%20Tables/ZZCHNGPPROVAL000/zzchngpproval000.tabl.json
ACTION : replace entire content, then activate.
Notes: sap_preview is abap.string(0) (RAP-7; char is capped at 1333 for table fields
[source: abap-docs /abap-docs-cloud/ABENDDIC_BUILTIN_TYPES, verified 2026-09-15];
abap.string(0) table-field syntax [source: abap-docs /abap-docs-cloud/ABENDDICDDL_DEFINE_TABLE_COMPS, verified 2026-09-15]).
Timestamps are abap.utclong (UTCLONG built-in type, same source). Admin fields use the released
ABP_* data elements and SYSUUID_X16 [source: sap-released-objects public_cloud level A, verified 2026-09-15]. */
@EndUserText.label : 'Arena change approval (staging + state machine)'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zzchngpproval000 {
key client : abap.clnt not null;
key id : sysuuid_x16 not null;
item_number : abap.char(18);
item_name : abap.char(40);
change_type : abap.char(15);
arena_eco_id : abap.char(20);
status : abap.char(10);
requested_by : abap.char(12);
requested_on : abap.utclong;
decided_by : abap.char(12);
decided_on : abap.utclong;
sap_change_number : abap.char(12);
error_message : abap.char(255);
reject_reason : abap.char(255);
sap_preview : abap.string(0);
created_by : abp_creation_user;
created_at : abp_creation_tstmpl;
last_changed_by : abp_lastchange_user;
last_changed_at : abp_lastchange_tstmpl;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
}
@@ -0,0 +1,73 @@
// TARGET OBJECT : behavior definition ZZR_CHANGEAPPROVAL000 (base BDEF, managed; generated 2026-09-15)
// abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.bdef.json
// ACTION : replace entire content, then activate (after the three ZD_* abstract entities and ZZR_CHANGEAPPROVAL000).
// Sources (all verified 2026-09-15):
// action syntax incl. ( features : instance ), parameter, result [1] $self
// [abap-docs /abap-docs-cloud/ABENBDL_ACTION, ABENBDL_ACTIONS_FC, ABENBDL_ACTION_INPUT_PARAM, ABENBDL_ACTION2_ABEXA]
// internal operations (usable only from the behavior pool) [abap-docs /abap-docs-cloud/ABENBDL_INTERNAL]
// field ( readonly, numbering : managed ) for a UUID key [abap-docs /abap-docs-cloud/ABENBDL_FIELD_NUMBERING; /abap-fiori-showcase/01_general_features#readonly]
// field ( readonly : update, mandatory : create ) combination [abap-docs /abap-fiori-showcase/06_object_page_content (StreamIsReadOnly)]
// determination on modify { create; } sets the initial status [abap-docs /abap-docs-cloud/ABENBDL_DETERMINATIONS (setStatustoNew)]
// readonly fields written by determinations/actions IN LOCAL MODE [same source: field ( readonly ) AmountSum + TotalPrice determination]
// etag master [abap-docs /abap-docs-cloud/ABENBDL_ETAG]; explicit mapping because CDS aliases differ from table fields [abap-docs /abap-docs-cloud/ABENBDL_TYPE_MAPPING]
// authorization master ( none ): "no authorization checks for the entire RAP BO" - access is governed exclusively by the
// IAM app / business catalog (ADR-002, ARN-007) and read access by DCL [abap-docs /abap-docs-cloud/ABENBDL_AUTHORIZATION]
// Open point (falsifiable at activation): StatusText/StatusCriticality/IsApprovable/IsRetriable are CDS-calculated and
// deliberately absent from the mapping. ABENBDL_TYPE_MAPPING only requires a mapping where table and entity field
// NAMES differ; it is silent on calculated elements. If activation reports an error (not a warning) for the unmapped
// elements, fall back to persisting them (add the four columns to the table + mapping) and maintain them in the actions.
managed implementation in class zzbp_r_changeapproval000 unique;
strict ( 2 );
define behavior for ZZR_CHANGEAPPROVAL000 alias ChangeApproval
persistent table zzchngpproval000
lock master
authorization master ( none )
etag master LocalLastChangedAt
{
create;
internal update;
field ( readonly, numbering : managed ) ID;
field ( readonly : update, mandatory : create ) ItemNumber, ChangeType, ArenaEcoId;
field ( mandatory : create ) SapPreview;
field ( readonly ) Status, StatusText, StatusCriticality, IsApprovable, IsRetriable,
RequestedBy, RequestedOn, DecidedBy, DecidedOn,
SapChangeNumber, ErrorMessage, RejectReason,
CreatedBy, CreatedAt, LastChangedBy, LastChangedAt, LocalLastChangedAt;
// Business-user actions (UI projection): PENDING -> APPROVED | REJECTED, FAILED -> EXECUTING (PRD RAP-1/RAP-2)
action ( features : instance ) approve result [1] $self;
action ( features : instance ) reject parameter ZD_CHANGEAPPROVAL_REJECTP result [1] $self;
action ( features : instance ) retrySync result [1] $self;
// Worker actions (A2X projection): APPROVED -> EXECUTING -> SYNCED | FAILED (PRD RAP-6)
action ( features : instance ) markExecuting result [1] $self;
action ( features : instance ) markSynced parameter ZD_CHANGEAPPROVAL_SYNCEDP result [1] $self;
action ( features : instance ) markFailed parameter ZD_CHANGEAPPROVAL_FAILEDP result [1] $self;
determination setInitialStatus on modify { create; }
mapping for zzchngpproval000
{
ID = id;
ItemNumber = item_number;
ItemName = item_name;
ChangeType = change_type;
ArenaEcoId = arena_eco_id;
Status = status;
RequestedBy = requested_by;
RequestedOn = requested_on;
DecidedBy = decided_by;
DecidedOn = decided_on;
SapChangeNumber = sap_change_number;
ErrorMessage = error_message;
RejectReason = reject_reason;
SapPreview = sap_preview;
CreatedBy = created_by;
CreatedAt = created_at;
LastChangedBy = last_changed_by;
LastChangedAt = last_changed_at;
LocalLastChangedAt = local_last_changed_at;
}
}
@@ -0,0 +1,12 @@
/* TARGET OBJECT : access control ZZR_CHANGEAPPROVAL000 (generated 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.dcls.acds
ACTION : replace entire content, then activate.
Full access rule: read access is governed by the IAM app / business catalog (ADR-002); SAP delivers no full-access
roles but customers may use them [source: abap-docs /abap-docs-cloud/ABENCDS_DCL_ROLE_GRANT_RULE, verified 2026-09-15].
@MappingRole: true assigns the role to every user [source: abap-docs /abap-platform-rap-opensap/week2/unit7, verified 2026-09-15].
Tighten later with a custom authorization object + pfcg_auth condition if per-user data restrictions are needed. */
@EndUserText.label: 'Access control for ZZR_CHANGEAPPROVAL000'
@MappingRole: true
define role ZZR_CHANGEAPPROVAL000 {
grant select on ZZR_CHANGEAPPROVAL000;
}
@@ -0,0 +1,76 @@
/* TARGET OBJECT : data definition ZZR_CHANGEAPPROVAL000 (root view entity, generated 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.ddls.acds
ACTION : replace entire content, then activate (table ZZCHNGPPROVAL000 must be active first).
Notes: StatusText / StatusCriticality / IsApprovable / IsRetriable are derived from Status with CASE
expressions here, because a transactional_query projection list may only contain elements of the
projected entity, path expressions, associations and virtual elements - no expressions
[source: abap-docs /abap-docs-cloud/ABENCDS_DEFINE_VIEW_AS_PROJECTION, verified 2026-09-15].
CASE syntax [source: abap-docs /abap-docs-cloud/ABENCDS_CASE_EXPRESSION_V2, verified 2026-09-15];
cast of a literal to a data element (abap_boolean, released) [source: abap-docs /abap-docs-cloud/ABENCDS_CAST_EXPRESSION_V2, verified 2026-09-15].
@ObjectModel.text.element [source: abap-docs /abap-fiori-showcase/06_object_page_content#displaying-text-instead-of-id, verified 2026-09-15].
@Semantics admin-field annotations [source: abap-docs /abap-platform-rap-opensap/week2/unit3#step-1-create-the-interface-cds-view-for-the-travel-entity, verified 2026-09-15].
Criticality values 2/2/5/3/1/1 per PRD RAP-1. */
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Arena change approval'
define root view entity ZZR_CHANGEAPPROVAL000
as select from zzchngpproval000
{
key id as ID,
item_number as ItemNumber,
item_name as ItemName,
change_type as ChangeType,
arena_eco_id as ArenaEcoId,
@ObjectModel.text.element: ['StatusText']
status as Status,
case status
when 'PENDING' then 'Pending approval'
when 'APPROVED' then 'Approved - awaiting execution'
when 'EXECUTING' then 'Executing in SAP'
when 'SYNCED' then 'Synced to SAP'
when 'REJECTED' then 'Rejected'
when 'FAILED' then 'Execution failed'
else status
end as StatusText,
case status
when 'PENDING' then 2
when 'APPROVED' then 2
when 'EXECUTING' then 5
when 'SYNCED' then 3
when 'REJECTED' then 1
when 'FAILED' then 1
else 0
end as StatusCriticality,
requested_by as RequestedBy,
requested_on as RequestedOn,
decided_by as DecidedBy,
decided_on as DecidedOn,
sap_change_number as SapChangeNumber,
error_message as ErrorMessage,
reject_reason as RejectReason,
sap_preview as SapPreview,
case status
when 'PENDING' then cast( 'X' as abap_boolean )
else cast( ' ' as abap_boolean )
end as IsApprovable,
case status
when 'FAILED' then cast( 'X' as abap_boolean )
else cast( ' ' as abap_boolean )
end as IsRetriable,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.lastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt
}
@@ -0,0 +1,13 @@
/* TARGET OBJECT : service definition ZZUI_ARENA_CHANGE_APPRVL000_O4 (generated 2026-09-15)
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Definitions/ZZUI_ARENA_CHANGE_APPRVL000_O4/zzui_arena_change_apprvl000_o4.srvd.acds
ACTION : replace entire content, then activate (after ZZC_CHANGEAPPROVAL000 view + BDEF).
expose ... as <alias>: the alias becomes the entity set (ChangeApproval) and the entity type (ChangeApproval_Type)
[source: abap-docs /abap-fiori-showcase/06_object_page_content (Service Definition), verified 2026-09-15].
provider contracts odata_v4_ui switches on the UI-binding syntax checks and must match the binding type
[source: abap-docs /abap-docs-cloud/ABENSRVD_PROVIDER_CONTRACT, verified 2026-09-15]. */
@EndUserText.label: 'Arena change approval - UI service'
define service ZZUI_ARENA_CHANGE_APPRVL000_O4
provider contracts odata_v4_ui
{
expose ZZC_CHANGEAPPROVAL000 as ChangeApproval;
}