Sync current sources: clean RAP handover names, app rebind, tooling

Working-tree state as of 2026-09-29, uncommitted since the initial
"upload current sources" commit:

- docs/handover/rap/: renamed the 11 generator-suffixed sources
  (zz*000) to the clean object names built on the tenant, plus the
  A2X leg and parameter-entity edits.
- app/: manifest, annotations, i18n, mock data and local/mock ui5
  configs updated to the live service shape.
- PRD.md, CHANGELOG.md, KNOWLEDGE.md: build record through
  2026-09-19, including the rejected reentrance-ticket path.
- tools/adt-reentrance-login/: retained login helper (new).
- docs/v2/zui_arena_change_appr_o4.metadata.xml: captured live
  $metadata (new).
- Dropped a stray 0.3GB-class Eclipse installer that the initial
  commit had tracked under a URL-mangled filename, and the
  machine-specific 0M7DEJI.code-workspace.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 10:22:52 -07:00
co-authored by Claude Opus 5
parent f8b45ea4a5
commit 468729eb4e
42 changed files with 4597 additions and 1680 deletions
+607 -151
View File
@@ -1,176 +1,632 @@
# RAP handover — Arena change-approval BO (stage 1, 2026-09-15)
# RAP handover: Arena change-approval BO, built from an empty package (rev. 2026-09-16, corrected after the UI leg went live)
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".
Target: development tenant `my431353` (ADT destination `0M7DEJI`), package `ZARENA_PLM`, a
sub-package of `ZLOCAL` with no transport. No generator is used. Every object is created empty
through the ADT MCP `abap_creation-create_object`, filled by pasting a file from this folder into
the VS Code ADT editor, saved, and activated through `abap_activate_objects`. The ADT MCP cannot
write source (PRD V-4).
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).
Most source files start with a header comment that names the target object, its `abap:` URI and its
README step. **The two service definition sources (U13 `zui_arena_change_approval.srvd.srvdsrv`, W4
`zapi_arena_change_approval.srvd.srvdsrv`) carry no comment at all**, because a comment blocks SRVD
activation on this tenant (see Landmines). Their former header content is in the U13 and W4 notes
below. Where a header's `abap:` URI disagrees with this README, this README and the `filePath`
returned by `create_object` win. The U6 and U9 BDEF headers still name the generator-era `.bdef.json`;
they are left as they were, because those objects are already active. The W3 header, not yet pasted,
names `.bdef.abdl`.
## What exists in the tenant right now
The entity set, properties and actions match the Fiori app contract: entity set `ChangeApproval`,
key `ID`, bound actions `approve`, `reject(RejectReason)`, `retrySync`. The UI service is published
at `/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/` (the UI
binding was shortened to fit the 26-character limit, D-1). The app was reconciled to that live
service on 2026-09-16 (CHANGELOG): dataSource URL, namespace
`com.sap.gateway.srvd.zui_arena_change_approval.v0001` (taken from the **service definition**, not
the binding) and entity type `ChangeApprovalType` (no underscore), all from the live `$metadata` in
`docs/v2/zui_arena_change_appr_o4.metadata.xml` (live 2026-09-16; KNOWLEDGE 2026-09-16). Text in
this folder that says `ChangeApproval_Type`, or that the binding name sets the namespace, predates
that capture and is marked where it remains.
Generated by `x-ui-service` (all **active**, ATC 0 findings on 12 objects, 2026-09-15):
Citations use `[source: <server> <doc-id>, verified <date>]`. The servers are `abap-docs`,
`sap-docs`, `sap-released-objects`, and `adt-mcp` for live tool answers on `0M7DEJI`. Observations
made in the editor or browser, with no tool answer to cite, are marked `(live <date>)`.
| 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 |
## Status (2026-09-16)
Created empty via `create_object` (exist, **no source, inactive**):
- **UI leg U1–U14: ACTIVE and ATC-clean.** All 14 objects answered "Activation successful." ATC over
the 14 objects: 0 errors, 0 warnings, 43 infos (worklist `12ACF6BD93811FE1ACB8096AC6763BD6`)
[source: adt-mcp abap_activate_objects, abap_atc_get_result on 0M7DEJI, verified 2026-09-16]. The
MCP returns only totals by priority, so the 43 infos are read in the ADT ATC view. Infos do not
gate publishing.
- **UI binding `ZUI_ARENA_CHANGE_APPR_O4`: PUBLISHED 2026-09-16 04:26**, with the **Publish code
lens** in the binding's `.srvb.json` source editor (U15). The ADT MCP has no publish tool, and VS
Code shows no confirmation beyond "<binding> published locally". The publish was therefore verified
by tool: `fetch_services` reports `isPublished: true`, and `fetch_service_information` returns
`https://my431353-api.s4hana.cloud.sap/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/?sap-client=080`
with entity set `ChangeApproval` [source: adt-mcp abap_business_services-fetch_services,
fetch_service_information on 0M7DEJI, verified 2026-09-16].
- **Where the live service answers:** on the UI host `my431353.s4hana.cloud.sap`. The same path on
the `-api` host that `fetch_service_information` reports is refused to a business user as
"Forbidden / blocked by UCON" (live 2026-09-16; KNOWLEDGE 2026-09-16). The `-api` host is the
communication-user entry point.
- **Worker leg W1–W5:** started on the tenant 2026-09-16 06:14 (PRD status). Run it exactly as in
"Worker leg" below. This README records no worker-leg result.
- **Package:** `ZARENA_PLM` exists again under `ZLOCAL` (recreated 2026-09-16; validation in it
succeeded live at 02:27, CHANGELOG). Historical, before step 0: the package had been deleted
together with its contents, and `abap_creation-run_validation` answered "Package 'ZARENA_PLM' does
not exist." for TABL, DDLS and CLAS [source: adt-mcp on 0M7DEJI, verified 2026-09-16].
| 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) |
## Object inventory and name limits
Not yet creatable (the MCP validation refuses until the referenced objects are active):
| Step | Object | Type (create code) | Source file(s) | Length / limit |
|---|---|---|---|---|
| U1 | `ZCHANGEAPPROVAL` | TABL (`TABL/DT`) | `zchangeapproval.tabl.asddls` | 15 / 16 |
| U2 | `ZD_CHANGEAPPROVAL_REJECTP` | DDLS (`DDLS/DF`) | `zd_changeapproval_rejectp.ddls.asddls` | 25 / 30 |
| U3 | `ZD_CHANGEAPPROVAL_SYNCEDP` | DDLS (`DDLS/DF`) | `zd_changeapproval_syncedp.ddls.asddls` | 25 / 30 |
| U4 | `ZD_CHANGEAPPROVAL_FAILEDP` | DDLS (`DDLS/DF`) | `zd_changeapproval_failedp.ddls.asddls` | 25 / 30 |
| U5 | `ZR_CHANGEAPPROVAL` | DDLS (`DDLS/DF`) | `zr_changeapproval.ddls.asddls` | 17 / 30 |
| U6 | `ZR_CHANGEAPPROVAL` | BDEF (`BDEF/BDO`) | `zr_changeapproval.bdef.asbdef` | 17 / 30 |
| U7 | `ZBP_R_CHANGEAPPROVAL` | CLAS (`CLAS/OC`) | `zbp_r_changeapproval.clas.abap` + `.clas.locals_imp.abap` | 20 / 30 |
| U8 | `ZC_CHANGEAPPROVAL` | DDLS (`DDLS/DF`) | `zc_changeapproval.ddls.asddls` | 17 / 30 |
| U9 | `ZC_CHANGEAPPROVAL` | BDEF (`BDEF/BDO`) | `zc_changeapproval.bdef.asbdef` | 17 / 30 |
| U10 | `ZR_CHANGEAPPROVAL` | DCLS (`DCLS/DL`) | `zr_changeapproval.dcls.asdcls` | 17 / 30 |
| U11 | `ZC_CHANGEAPPROVAL` | DCLS (`DCLS/DL`) | `zc_changeapproval.dcls.asdcls` | 17 / 30 |
| U12 | `ZC_CHANGEAPPROVAL` | DDLX (`DDLX/EX`) | `zc_changeapproval.ddlx.asddlx` | 17 / 40 |
| U13 | `ZUI_ARENA_CHANGE_APPROVAL` | SRVD (`SRVD/SRV`) | `zui_arena_change_approval.srvd.srvdsrv` (no comments) | 25 / 30 |
| U14 | `ZUI_ARENA_CHANGE_APPR_O4` | SRVB (`SRVB/SVB`), OData V4 - UI | none in this folder; `create_object` returns `.srvb.json` | 24 / 26 (D-1) |
| W1 | `ZC_CHANGEAPPROVAL_A2X` | DDLS (`DDLS/DF`) | `zc_changeapproval_a2x.ddls.asddls` | 21 / 30 |
| W2 | `ZC_CHANGEAPPROVAL_A2X` | DCLS (`DCLS/DL`) | `zc_changeapproval_a2x.dcls.asdcls` | 21 / 30 |
| W3 | `ZC_CHANGEAPPROVAL_A2X` | BDEF (`BDEF/BDO`) | `zc_changeapproval_a2x.bdef.asbdef` | 21 / 30 |
| W4 | `ZAPI_ARENA_CHANGE_APPROVAL` | SRVD (`SRVD/SRV`) | `zapi_arena_change_approval.srvd.srvdsrv` (no comments) | 26 / 30 |
| W5 | `ZAPI_ARENA_CHANGE_APPR_A2X` | SRVB (`SRVB/SVB`), Web API (see W5 for the exact `bindingType` value) | none in this folder; `.srvb.json` expected, as U14 | 26 / 26 (D-1) |
| 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" |
Limits: `TABL/DT` 16; `DDLS/DF`, `BDEF/BDO` and `SRVD/SRV` 30 [source: adt-mcp
`abap_creation-get_object_type_details` on 0M7DEJI, verified 2026-09-16]. Service bindings: 26 per
SAP Help. The ADT MCP accepts 30 for `SRVB/SVB`, but that is undocumented and not used here (D-1).
The documentation agrees
for tables ("maximum of 16 characters") [source: abap-docs /abap-docs-cloud/ABENDDIC_DBTAB_NAMES,
verified 2026-09-16] and for CDS view entities ("maximum of 30 characters"; the DDL source and the
CDS entity must have the same name) [source: abap-docs /abap-docs-cloud/ABENCDS_DEFINE_VIEW_ENTITY,
verified 2026-09-16]. `CLAS/OC` and `DCLS/DL` allow 30 and `DDLX/EX` allows 40 [source: adt-mcp
abap_creation-get_object_type_details on 0M7DEJI, verified 2026-09-16] (KNOWLEDGE 2026-09-16; these
were "not recorded" in the pre-build revision).
## Paste-and-activate sequence
Not created: a projection behavior pool (see design decisions) and an SAP object type or node type.
No documentation found makes an SAP object type mandatory for a RAP BO. SAP Help lists it only as
one of the objects the "OData UI Service from Scratch" wizard generates [source: sap-docs sap-help
loio d3f5e90a306a44b6b37b8b05f04d2c10, verified 2026-09-16]. Business events are the case that
requires `@ObjectModel.sapObjectNodeType.name` [source: sap-docs
/btp-cloud-platform/30-development/log-custom-business-events-6ecc451, verified 2026-09-16]. Add
one if the BO later raises business events.
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.
Split: the **UI leg** has 14 source files (13 objects with source + the U14 binding). The **worker
leg** has 4 source files (+ the W5 binding). The `markSynced`/`markFailed` parameter entities
(U3, U4) belong to the UI leg because the base BDEF (U6) names them. They must be active before
U6 activates.
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).
## Resolved decisions
## Activation URIs for `abap_activate_objects` (after pasting)
- **D-1: service binding names stay within 26 characters (resolved 2026-09-16).** SAP Help gives
26 characters as the maximum length for a service binding name in every version checked: ABAP
Cloud [source: sap-docs sap-help loio 4bad0d7280b742a68dd2d42b1441f09a, verified 2026-09-16], SAP
Cloud Platform [source: sap-docs sap-help loio f3e608ace2a64aada33bdd3e2419113a and
6709cab6cb5b4c01b28463d760429a9a, verified 2026-09-16], and ABAP Platform 2021 [source: sap-docs
sap-help loio 777e027f61c3490dba0433443d9143a6, verified 2026-09-16]. The ADT MCP's 30 is
undocumented. The bindings are therefore:
- UI binding `ZUI_ARENA_CHANGE_APPR_O4` (24), replacing the 28-character long form.
- Web API binding `ZAPI_ARENA_CHANGE_APPR_A2X` (26), replacing the 30-character long form.
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):
The service definitions `ZUI_ARENA_CHANGE_APPROVAL` (25) and `ZAPI_ARENA_CHANGE_APPROVAL` (26)
and every other object name are unchanged. Consequence: the binding name is the first URL
segment, so the app's dataSource URL had to change. **Live correction (2026-09-16):** the
pre-build text here also said the binding name sets the schema namespace. It does not; the live
namespace `com.sap.gateway.srvd.zui_arena_change_approval.v0001` comes from the service definition
(live 2026-09-16, `docs/v2/zui_arena_change_appr_o4.metadata.xml`; KNOWLEDGE 2026-09-16). The app
was reconciled to the live service on 2026-09-16 (CHANGELOG). The PRD worker contract row now
names `ZAPI_ARENA_CHANGE_APPR_A2X` (PRD read 2026-09-16), so that governance item is closed. U14
published under its 24-character name, so a name within 26 is proven to publish. Whether
publishing accepts more than 26 is still unknown (KNOWLEDGE 2026-09-16).
| 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` |
## Preconditions
In sequence order (`B` = `abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM`):
1. VS Code with ADT (`sapse.adt-vscode`) is running, destination `0M7DEJI` is logged on, and the
ADT MCP answers on `http://127.0.0.1:2236/mcp`. After a VS Code reload, refresh the bearer
token in `.mcp.json` (CLAUDE.md). A Claude Code session exposes the `adt-mcp` tools only if the
server was up when the session started (KNOWLEDGE 2026-09-16).
2. `abap_list_destinations` lists `0M7DEJI`.
## Step 0: recreate the package (done 2026-09-16)
Done: `ZARENA_PLM` exists again under `ZLOCAL`, validation in it succeeded live at 02:27, and
`abap_transport-get` answered `isRecordingRequired: false` (CHANGELOG 2026-09-16). The steps below
are kept as the record.
In ADT (VS Code or Eclipse), create development package **`ZARENA_PLM` with super-package
`ZLOCAL`**, no transport. The ADT MCP has no package tool. Use a `ZLOCAL` sub-package because
`ZLOCAL` itself is a structure package, `$TMP` fails the language-version authorization, and
sub-packages of `ZLOCAL` created from release 2508 need no workbench request [source: sap-docs
sap-help loio a0d80f14fcb74cb1ac8fb6f3d9ad48cc (2608), verified 2026-09-15; refusals live
2026-09-15, KNOWLEDGE].
Check it read-only: `abap_creation-run_validation` for `TABL/DT` with the U1 JSON must no longer
answer "Package 'ZARENA_PLM' does not exist". Then call `abap_transport-get` once for U1
(`objectType` `TABL/DT`, `developmentPackage` `ZARENA_PLM`, `isCreation` true) and expect "recording
not required". Every `create_object` below uses `"transportRequestNumber": ""`.
## Per-object routine
For every step:
1. `abap_creation-get_object_type_details` (objectType, name, description). It lists the required
fields and limits. If it names a required field that the JSON below lacks, add it and note it.
2. `abap_creation-run_validation` with the JSON.
3. `abap_creation-create_object` with the same JSON, `destination` `0M7DEJI`, transport `""`.
**Record the `filePath` it returns.** That path is both the paste target and the activation URI.
4. Open that **source** file in the VS Code ADT editor, replace the whole content with the handover
file (its header comment included where it has one; the SRVD files have none and must stay
comment-free), and save. A DDIC database table is edited as Dictionary DDL source generated from its
metadata [source: abap-docs /abap-docs-cloud/ABENDDIC_DEFINE_TABLE, verified 2026-09-16].
5. `abap_activate_objects` with the returned `filePath` (URL-encoded, spaces as `%20`). The
activation table below is a cross-check, not a substitute. Fix errors before the next step.
**General rule (live 2026-09-16): in the VS Code ADT workspace, the `.json` file in an object folder
is header metadata and never receives pasted source. Always use the `filePath` returned by
`create_object`.** For example, `zchangeapproval.tabl.json` holds only formatVersion, description,
originalLanguage and `abapLanguageVersion`, and the table source lives in `zchangeapproval.tabl.ddic`
[source: adt-mcp abap_creation-create_object / abap_activate_objects on 0M7DEJI, verified
2026-09-16]. The returned source files are TABL `.tabl.ddic`, DDLS `.ddls.acds`, DCLS `.dcls.acds`,
DDLX `.ddlx.acds`, BDEF `.bdef.abdl`, CLAS `.clas.abap` and SRVD `.srvd.acds`. Among the types in
this build, the one returned `filePath` that is a `.json` is the service binding's `.srvb.json`.
Nothing is pasted into it, the binding is activated through it (U14), and its editor carries the
Publish code lens (U15).
Create-metadata tags per type [source: adt-mcp `abap_creation-get_object_type_details` on 0M7DEJI,
verified 2026-09-16]:
- `TABL/DT`: `packageName`, `name`, `description`.
- `DDLS/DF`: `packageName`, `name`, `description`, optional `referencedObject` (omitted here).
- `SRVD/SRV`: `packageName`, `name`, `description`, `sourceType` (`S` | `X` | none). Use `"S"`: it
worked live, and omitting it fails with "serviceDefinitionInfo.sourceType is null" (live 2026-09-15).
`SRVD/SRV` also offers an optional `referencedObject` (omitted here) [source: adt-mcp
get_object_type_details on 0M7DEJI, verified 2026-09-16].
- `SRVB/SVB`: `packageName`, `name`, `description`, `bindingType`, `serviceDefinition`. Proven
`bindingType` value: `OData V4 - UI` (U14, 2026-09-16). The Web API value is not proven; see W5.
- `BDEF/BDO`: `behaviorDefinitionType`, `packageName`, `rootEntity`, `name`, `description`,
`implementationType` (`Managed` | `Unmanaged` | `Projection` | `Abstract` | `Interface`).
- `CLAS/OC`: `packageName`, `name` (≤30), `description`, optional superclass and interfaces (none
needed here). `DCLS/DL`: `packageName`, `name` (≤30), `description`, optional `protectedEntity`.
`DDLX/EX`: `packageName`, `name` (≤40), `description`, optional `extendedEntity` [source: adt-mcp
abap_creation-get_object_type_details on 0M7DEJI, verified 2026-09-16]. The pre-build revision
marked these three "tags not recorded"; the JSON in the UI-leg table below still carries that
marker as the record.
Descriptions are kept at 40 characters or fewer. The per-type description limit was not recorded.
BDEF and SRVB creation validates the referenced DDLS/SRVD's **active** version (live 2026-09-15:
"The referenced STOB object … does not exist"; "An active version of the Service Definition … does
not exist"). The sequence below always activates the referenced object first.
## UI leg (first; done 2026-09-16)
**Done: U1–U14 are active, ATC-clean, and the U14 binding is published (see Status).** Do not re-run
this table. It is kept as the build record. The JSON column is the pre-build plan: the `(tags
unconfirmed)` markers were settled on 2026-09-16 (per-object routine), and U10 was created with
`protectedEntity` added (CHANGELOG 2026-09-16).
| Step | create_object JSON (objectType) | Paste | Depends on (active) |
|---|---|---|---|
| U1 | `TABL/DT` `{"packageName":"ZARENA_PLM","name":"ZCHANGEAPPROVAL","description":"Arena change approval"}` | `zchangeapproval.tabl.asddls` | none |
| U2 | `DDLS/DF` `{"packageName":"ZARENA_PLM","name":"ZD_CHANGEAPPROVAL_REJECTP","description":"Arena approval: reject parameter"}` | `zd_changeapproval_rejectp.ddls.asddls` | none |
| U3 | `DDLS/DF` `{"packageName":"ZARENA_PLM","name":"ZD_CHANGEAPPROVAL_SYNCEDP","description":"Arena approval: markSynced parameter"}` | `zd_changeapproval_syncedp.ddls.asddls` | none |
| U4 | `DDLS/DF` `{"packageName":"ZARENA_PLM","name":"ZD_CHANGEAPPROVAL_FAILEDP","description":"Arena approval: markFailed parameter"}` | `zd_changeapproval_failedp.ddls.asddls` | none |
| U5 | `DDLS/DF` `{"packageName":"ZARENA_PLM","name":"ZR_CHANGEAPPROVAL","description":"Arena change approval"}` | `zr_changeapproval.ddls.asddls` | U1 |
| U6 | `BDEF/BDO` `{"behaviorDefinitionType":"definition","packageName":"ZARENA_PLM","rootEntity":"ZR_CHANGEAPPROVAL","name":"ZR_CHANGEAPPROVAL","description":"Arena change approval behavior","implementationType":"Managed"}` | `zr_changeapproval.bdef.asbdef` | U2, U3, U4, U5 |
| U7 | `CLAS/OC` `{"packageName":"ZARENA_PLM","name":"ZBP_R_CHANGEAPPROVAL","description":"Arena change approval behavior pool"}` (tags unconfirmed) | `zbp_r_changeapproval.clas.abap` (global class) + `zbp_r_changeapproval.clas.locals_imp.abap` (Local Types) | U6 |
| U8 | `DDLS/DF` `{"packageName":"ZARENA_PLM","name":"ZC_CHANGEAPPROVAL","description":"Arena change approval UI projection"}` | `zc_changeapproval.ddls.asddls` | U5 (U6/U7 done) |
| U9 | `BDEF/BDO` `{"behaviorDefinitionType":"definition","packageName":"ZARENA_PLM","rootEntity":"ZC_CHANGEAPPROVAL","name":"ZC_CHANGEAPPROVAL","description":"Arena approval UI projection behavior","implementationType":"Projection"}` | `zc_changeapproval.bdef.asbdef` | U6, U7, U8 |
| U10 | `DCLS/DL` `{"packageName":"ZARENA_PLM","name":"ZR_CHANGEAPPROVAL","description":"Arena change approval access"}` (tags unconfirmed) | `zr_changeapproval.dcls.asdcls` | U5 |
| U11 | `DCLS/DL` `{"packageName":"ZARENA_PLM","name":"ZC_CHANGEAPPROVAL","description":"Arena approval UI projection access"}` (tags unconfirmed) | `zc_changeapproval.dcls.asdcls` | U8, U10 |
| U12 | `DDLX/EX` `{"packageName":"ZARENA_PLM","name":"ZC_CHANGEAPPROVAL","description":"Arena approval UI annotations"}` (tags unconfirmed) | `zc_changeapproval.ddlx.asddlx` | U8 |
| U13 | `SRVD/SRV` `{"packageName":"ZARENA_PLM","name":"ZUI_ARENA_CHANGE_APPROVAL","description":"Arena change approval UI service","sourceType":"S"}` | `zui_arena_change_approval.srvd.srvdsrv` | U8, U9 |
| U14 | `SRVB/SVB` `{"packageName":"ZARENA_PLM","name":"ZUI_ARENA_CHANGE_APPR_O4","description":"Arena change approval UI binding","bindingType":"OData V4 - UI","serviceDefinition":"ZUI_ARENA_CHANGE_APPROVAL"}` | none | U13 |
Notes per step:
- **U1:** paste `zchangeapproval.tabl.asddls` into **`zchangeapproval.tabl.ddic`**, never into
`zchangeapproval.tabl.json` (metadata only, see the per-object routine). Proven: U1 activated
through `.tabl.ddic` [source: adt-mcp abap_activate_objects on 0M7DEJI, verified 2026-09-16].
- **U6 then U7 (order matters).** **Resolved live 2026-09-16:** U6 activated through `.bdef.abdl` on
the first attempt, before class `ZBP_R_CHANGEAPPROVAL` existed, so the fallback below was not
needed. U6 and U7 were then activated together in one call [source: adt-mcp abap_activate_objects
on 0M7DEJI, verified 2026-09-16]. The pre-build instruction follows as the record.
Activate the base BDEF **before** the behavior pool class exists,
then create the class. SAP's RAP exercise does it that way [source: abap-docs
/abap-platform-rap-opensap/week4/unit3#step-3-create-and-implement-the-behavior-implementation-bil-class-for-the-travel-entity,
verified 2026-09-16]. That exercise is an unmanaged, non-strict BDEF. If U6 activation fails with
an **error** saying class `ZBP_R_CHANGEAPPROVAL` does not exist, run U7's create and paste, then
activate U6 and U7 together in one `abap_activate_objects` call. Also watch U6 for messages about
the unmapped CDS-calculated elements `StatusText`, `StatusCriticality`, `IsApprovable` and
`IsRetriable`. A warning is acceptable. An error triggers the fallback in the BDEF file header.
- **U7:** paste the global class, then switch to the Local Types include and paste the handlers.
Save both. Activate through the `.clas.abap` URI. The Local Types include also implements the
`checkDuplicate` validation. Its duplicate query uses `OPTIONS PRIVILEGED ACCESS`. If the syntax
check refuses that form, the documented fallback is in the include header. `create_object` for
the class returned only the global `.clas.abap` path, no Local Types path (KNOWLEDGE 2026-09-16).
- **U9:** no behavior pool is created for the projection (see design decisions). `use etag` is in
the entity header, not the body (design decisions). `create_object` returned `.bdef.abdl`, and U9
activated through it [source: adt-mcp abap_creation-create_object, abap_activate_objects on
0M7DEJI, verified 2026-09-16].
- **U13: service definition `ZUI_ARENA_CHANGE_APPROVAL` (UI service).** This note holds what the
source file's header comment used to say. The header is gone, because it blocked activation.
- `abap:` URI:
`abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Definitions/ZUI_ARENA_CHANGE_APPROVAL/zui_arena_change_approval.srvd.acds`
- Action: create empty through the ADT MCP (`SRVD/SRV`, `sourceType` `"S"`), replace the entire
content with `zui_arena_change_approval.srvd.srvdsrv`, save, and activate after the
`ZC_CHANGEAPPROVAL` view (U8) and BDEF (U9). Then create the OData V4 UI binding
`ZUI_ARENA_CHANGE_APPR_O4` on it (U14).
- **What happened live (2026-09-16):** the first activation failed with "Comments are not supported
and will be deleted on save" (Ln 1, the header block comment) and "oData V4 services with no
draft capability will be primarily READ-ONLY" (Ln 21). The same definition, re-saved without the
comment, activated. The comment was the blocker; the draft message was not (the BO has no draft,
by design) (live 2026-09-16; CHANGELOG 2026-09-16).
- Contract: exposes exactly one entity set, `ChangeApproval`. The service URL (live, published) is
`/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/`. The
first segment is the binding (service group `ZUI_ARENA_CHANGE_APPR_O4`, 24 characters, within
SAP Help's 26-character binding limit, D-1). The segment after `srvd/sap/` is the binding's
service alias (the service name in the binding editor), which can differ from this definition's
name and can be renamed only while the binding is unpublished [source: sap-docs community-14433266
"How to rename an OData V4 service?" (SAP, 2026-07-04), verified 2026-09-16]. The former header
called the app reconciliation pending. It was done on 2026-09-16 (CHANGELOG).
- `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]. **Live correction:** the entity set is `ChangeApproval`, but
the entity type is `ChangeApprovalType`, with no underscore (live 2026-09-16,
`docs/v2/zui_arena_change_appr_o4.metadata.xml`; KNOWLEDGE 2026-09-16).
- `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].
- **U14:** **Obsolete, removed:** the pre-build instruction to open the binding in the editor
*before activating* to check the alias. What was done and proven on 2026-09-16: `create_object`
returned `.srvb.json`, U14 was activated through that `filePath`, and the alias was verified
**after** activation with `fetch_services`: service `ZUI_ARENA_CHANGE_APPROVAL`, version `0001`,
OData V4, unpublished [source: adt-mcp abap_creation-create_object, abap_activate_objects,
abap_business_services-fetch_services on 0M7DEJI, verified 2026-09-16]. The alias check must
come before publishing, not before activating. The URL segment after `srvd/sap/` is that alias,
not necessarily the definition name, and it can be renamed only while the binding is unpublished
[source: sap-docs community-14433266 "How to rename an OData V4 service?", verified 2026-09-16].
The binding name `ZUI_ARENA_CHANGE_APPR_O4` (24) is within SAP Help's 26-character limit (D-1).
### U15: checks and publish of the UI binding (done 2026-09-16)
1. `abap_atc_run` on all 14 UI-leg objects: `ZCHANGEAPPROVAL` TABL; `ZD_CHANGEAPPROVAL_REJECTP`,
`ZD_CHANGEAPPROVAL_SYNCEDP`, `ZD_CHANGEAPPROVAL_FAILEDP`, `ZR_CHANGEAPPROVAL`, `ZC_CHANGEAPPROVAL`
DDLS; `ZR_CHANGEAPPROVAL`, `ZC_CHANGEAPPROVAL` BDEF; `ZBP_R_CHANGEAPPROVAL` CLAS;
`ZR_CHANGEAPPROVAL`, `ZC_CHANGEAPPROVAL` DCLS; `ZC_CHANGEAPPROVAL` DDLX;
`ZUI_ARENA_CHANGE_APPROVAL` SRVD; `ZUI_ARENA_CHANGE_APPR_O4` SRVB. Then
`abap_atc_get_result`. **Result:** 0 errors, 0 warnings, 43 infos (worklist
`12ACF6BD93811FE1ACB8096AC6763BD6`) [source: adt-mcp abap_atc_get_result on 0M7DEJI, verified
2026-09-16].
2. `abap_business_services-fetch_services` on `ZUI_ARENA_CHANGE_APPR_O4`. **Result:**
`isPublished: false`, service `ZUI_ARENA_CHANGE_APPROVAL`, version `0001`, OData V4.
3. **Publish with the Publish code lens, not a button.** Open the binding's `.srvb.json` source in
the VS Code ADT editor and choose the **Publish** code lens in the source. In ADT for VS Code a
service binding is a source-based editor, and publish, unpublish and preview are code lenses
[source: sap-docs sap-help loio dd6f5de63e7245a7abe7e39389841596 "ABAP RAP Tools", verified
2026-09-16]. (Superseded wording: "the Publish button in the ADT service binding editor" describes
the Eclipse form-based editor.) The binding must be active first [source: sap-docs sap-help loio
b58a3c27df4e406f9335d4b346f6be04, verified 2026-09-16]. The ADT MCP has no publish tool. VS Code
shows no confirmation beyond "<binding> published locally", so step 4 is the proof. **Done
2026-09-16 04:26.**
4. `fetch_services` again (expect `isPublished: true`), then
`abap_business_services-fetch_service_information`. The service URL path must equal
`/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/`
exactly, and the entity set must be `ChangeApproval`. **Result:** both hold, on host
`my431353-api.s4hana.cloud.sap` with `sap-client=080` [source: adt-mcp
abap_business_services-fetch_services, fetch_service_information on 0M7DEJI, verified
2026-09-16]. Business users and browser checks use the UI host `my431353.s4hana.cloud.sap`
instead; `-api` refuses them (Landmines).
5. Hand back the live `$metadata` for PRD V-2. **Done:** it is captured in
`docs/v2/zui_arena_change_appr_o4.metadata.xml`, and the app was reconciled to it on 2026-09-16
(CHANGELOG). The pre-build expectation `ChangeApproval_Type` was wrong; the live entity type is
`ChangeApprovalType`. See "App contract check" for the settled types.
## Worker leg (second; run exactly this sequence)
Prerequisites, all met on 2026-09-16: the U5 root view, U6 base BDEF, U7 behavior pool and U10 root
access control are active. Every step uses the per-object routine (details → validation → create →
paste into the returned `filePath` → activate through the same `filePath`).
| Step | create_object JSON (objectType) | Paste | Needs (active) |
|---|---|---|---|
| W1 | `DDLS/DF` `{"packageName":"ZARENA_PLM","name":"ZC_CHANGEAPPROVAL_A2X","description":"Arena approval A2X projection"}` | `zc_changeapproval_a2x.ddls.asddls` into `.ddls.acds` | activation: U5 (U6, U7 done) |
| W2 | `DCLS/DL` `{"packageName":"ZARENA_PLM","name":"ZC_CHANGEAPPROVAL_A2X","description":"Arena approval A2X projection access"}` (no `protectedEntity`, see round 1) | `zc_changeapproval_a2x.dcls.asdcls` into `.dcls.acds` | activation: W1, U10 |
| W3 | `BDEF/BDO` `{"behaviorDefinitionType":"definition","packageName":"ZARENA_PLM","rootEntity":"ZC_CHANGEAPPROVAL_A2X","name":"ZC_CHANGEAPPROVAL_A2X","description":"Arena approval A2X behavior","implementationType":"Projection"}` | `zc_changeapproval_a2x.bdef.asbdef` into `.bdef.abdl` | **creation: W1**; activation: W1, U6, U7 |
| W4 | `SRVD/SRV` `{"packageName":"ZARENA_PLM","name":"ZAPI_ARENA_CHANGE_APPROVAL","description":"Arena change approval Web API","sourceType":"S"}` | `zapi_arena_change_approval.srvd.srvdsrv` (no comments) into `.srvd.acds` | activation: W1, W3 |
| W5 | `SRVB/SVB` `{"packageName":"ZARENA_PLM","name":"ZAPI_ARENA_CHANGE_APPR_A2X","description":"Arena change approval Web API binding","bindingType":"OData V4 - Web API","serviceDefinition":"ZAPI_ARENA_CHANGE_APPROVAL"}` (check `bindingType`, step 7) | none | **creation: W4**; activation: W4 |
**Round 1: W1, W2 and W4 created empty and pasted; W1 and W2 activated.**
1. Create W1 (DDLS), W2 (DCLS) and W4 (SRVD) empty. Record each returned `filePath`.
- W2: leave out the optional `protectedEntity`. W1 has no active version yet. BDEF creation is
known to validate its referenced entity's active version; whether DCLS creation does the same
for `protectedEntity` is untested. The pasted source replaces the skeleton anyway.
- W4: `"sourceType":"S"` is required. Leave out the optional `referencedObject`.
2. Paste W1, W2 and W4 into their returned source files and save all three. Never paste into a
`.json`. W4 is pasted now but activated in round 2.
3. Activate W1, then W2, in that order.
**Round 2: W3 created and pasted; W3 then W4 activated.**
4. Create W3 (BDEF, `Projection`). This needs W1 **active**, because BDEF creation validates the
referenced entity's active version (live 2026-09-15). Expect a `filePath` ending in `.bdef.abdl`.
5. Paste W3 into that `.bdef.abdl` file and save.
6. Activate W3, then W4.
**Binding: W5 created and activated, not published.**
7. **Check the `bindingType` string before creating.** `OData V4 - UI` is proven (U14). The Web API
value has not been seen live. SAP Help names the type `OData V4 - Web API` [source: sap-docs
/btp-cloud-platform/30-development/service-bindings-c6dee8e, verified 2026-09-16], but the 2608
release note for ADT for VS Code calls it "OData V4 – API" [source: sap-docs sap-help loio
7f674e6190564228a5bb217ebf382611 (APPL-VSC-0011), verified 2026-09-16]. Take the exact value from
`get_object_type_details` for `SRVB/SVB` if it lists one; otherwise `run_validation` with
`OData V4 - Web API` settles it. If validation refuses the value and no allowed value is listed,
stop and report the message rather than trying label variants. The type must be the Web API one: the provider contract
`odata_v4_webapi` and the binding type must match, or an error occurs [source: abap-docs
/abap-docs-cloud/ABENSRVD_PROVIDER_CONTRACT, verified 2026-09-16].
8. Create W5 `ZAPI_ARENA_CHANGE_APPR_A2X` (needs W4 active). Activate it through its returned
`filePath` (`.srvb.json` expected, as U14).
9. Verify with `abap_business_services-fetch_services` on `ZAPI_ARENA_CHANGE_APPR_A2X`. Expect
service `ZAPI_ARENA_CHANGE_APPROVAL`, version `0001`, OData V4, and `isPublished: false`. That
unpublished state is the intended end state of this handover. Do not follow the tool's prompt to
have it published, and do not call `fetch_service_information` for W5.
**W5 is NOT published in this handover.** Web API exposure to the worker's communication user goes
through a custom communication scenario (PRD V-2), together with the IAM app. That is a separate
brief. The communication user will reach it on the `-api` host, the communication-user entry point
(KNOWLEDGE 2026-09-16). The W5 name `ZAPI_ARENA_CHANGE_APPR_A2X` is 26 characters, exactly SAP Help's
limit (D-1), and the PRD worker contract row names it.
Follow-up, not part of the sequence above: `abap_atc_run` on W1–W5 (DDLS, DCLS, BDEF, SRVD, SRVB),
as U15 step 1 did for the UI leg.
Notes per step:
- **W3:** the source header's `abap:` URI line was corrected from `.bdef.json` to `.bdef.abdl` on
2026-09-16 (comment only; no BDL change). Still use the returned `filePath`.
- **W4: service definition `ZAPI_ARENA_CHANGE_APPROVAL` (Web API service for the worker).** This note
holds what the source file's header comment used to say. The header is gone, for the U13 reason.
- `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: create empty through the ADT MCP (`SRVD/SRV`, `sourceType` `"S"`), replace the entire
content with `zapi_arena_change_approval.srvd.srvdsrv`, save, and activate after the
`ZC_CHANGEAPPROVAL_A2X` view (W1) and BDEF (W3). Then create the Web API binding
`ZAPI_ARENA_CHANGE_APPR_A2X` on it (W5), and do not publish it.
- **Keep the source comment-free.** A leading comment blocked U13's activation (U13 note).
On U13 the "no draft capability … READ-ONLY" message did not block. If W4 activation fails and
lists only that message, read its severity in the editor's Problems view before changing
anything; the BO has no draft by design (CHANGELOG 2026-09-16).
- Sources: as `ZUI_ARENA_CHANGE_APPROVAL` (U13 note); `odata_v4_webapi` is the Web API contract
[ABENSRVD_PROVIDER_CONTRACT], re-read as [source: abap-docs
/abap-docs-cloud/ABENSRVD_PROVIDER_CONTRACT, verified 2026-09-16].
## Activation URIs for `abap_activate_objects`
**Rule: activate through the `filePath` that `create_object` returned for the object** (per-object
routine). The table below is a cross-check. Pattern, learned live 2026-09-15 and unchanged for every
object built on 2026-09-16:
`abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/<folder>/<NAME>/<name>.<ext>`.
In the VS Code workspace the `.json` file in an object folder is header metadata and never receives
pasted source. Among the types in this build, SRVB is the one whose returned `filePath` is the
`.json` (nothing is pasted there).
Extensions as returned by `create_object` for this build and proven by activation on 2026-09-16
[source: adt-mcp abap_creation-create_object, abap_activate_objects on 0M7DEJI, verified
2026-09-16]:
| Type | Folder | Extension | Proven by |
|---|---|---|---|
| TABL | `Dictionary/Database%20Tables` | `.tabl.ddic` | U1. `.tabl.json` is header metadata only; it activated a **generator-era** table on 2026-09-15 and is not used |
| DDLS | `Core%20Data%20Services/Data%20Definitions` | `.ddls.acds` | U2–U5, U8 |
| BDEF | `Core%20Data%20Services/Behavior%20Definitions` | `.bdef.abdl` | U6, U9. **Generator-era record (2026-09-15, not for this build):** `.bdef.json` activated a generator BDEF, and `.bdef.acds`, `.bdef.asbdef` and `.bdef.abap` failed with "array element 0 is null" |
| DDLX | `Core%20Data%20Services/Metadata%20Extensions` | `.ddlx.acds` | U12 |
| DCLS | `Core%20Data%20Services/Access%20Controls` | `.dcls.acds` | U10, U11 |
| CLAS | `Source%20Code%20Library/Classes` | `.clas.abap` | U7 (no Local Types path is returned) |
| SRVD | `Business%20Services/Service%20Definitions` | `.srvd.acds` | U13 (after the comment-free re-save) |
| SRVB | `Business%20Services/Service%20Bindings` | `.srvb.json` | U14 (returned by `create_object`, activated through it) |
In step order. The U lines are the URIs of the active objects. The W lines follow the proven
pattern; confirm each against the returned `filePath`.
```
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
U1 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Dictionary/Database%20Tables/ZCHANGEAPPROVAL/zchangeapproval.tabl.ddic
U2 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_REJECTP/zd_changeapproval_rejectp.ddls.acds
U3 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_SYNCEDP/zd_changeapproval_syncedp.ddls.acds
U4 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_FAILEDP/zd_changeapproval_failedp.ddls.acds
U5 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZR_CHANGEAPPROVAL/zr_changeapproval.ddls.acds
U6 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZR_CHANGEAPPROVAL/zr_changeapproval.bdef.abdl
U7 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Source%20Code%20Library/Classes/ZBP_R_CHANGEAPPROVAL/zbp_r_changeapproval.clas.abap
U8 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddls.acds
U9 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL/zc_changeapproval.bdef.abdl
U10 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZR_CHANGEAPPROVAL/zr_changeapproval.dcls.acds
U11 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL/zc_changeapproval.dcls.acds
U12 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Metadata%20Extensions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddlx.acds
U13 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Definitions/ZUI_ARENA_CHANGE_APPROVAL/zui_arena_change_approval.srvd.acds
U14 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Bindings/ZUI_ARENA_CHANGE_APPR_O4/zui_arena_change_appr_o4.srvb.json
W1 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.ddls.acds
W2 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.dcls.acds
W3 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.bdef.abdl
W4 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Definitions/ZAPI_ARENA_CHANGE_APPROVAL/zapi_arena_change_approval.srvd.acds
W5 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Bindings/ZAPI_ARENA_CHANGE_APPR_A2X/zapi_arena_change_appr_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.
## App contract check (source vs `app/webapp/localService/mainService/metadata.xml`, 2026-09-16)
## Design decisions baked into the files (falsifiable, dated 2026-09-15)
**Settled by the live `$metadata` (2026-09-16).** The table below is the pre-activation comparison,
kept as the record. The live capture `docs/v2/zui_arena_change_appr_o4.metadata.xml` (live
2026-09-16; read for this revision) settles every open row:
- **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.
- Namespace `com.sap.gateway.srvd.zui_arena_change_approval.v0001`. Entity type
`ChangeApprovalType`, not `ChangeApproval_Type`. Entity set `ChangeApproval`.
- `ID` `Edm.Guid`. `StatusText` `Edm.String` MaxLength 29. `StatusCriticality` `Edm.Byte`.
`IsApprovable`/`IsRetriable` `Edm.Boolean`. `RequestedOn`/`DecidedOn` `Edm.DateTimeOffset`
Precision 7. `SapPreview` `Edm.String` without MaxLength.
- `reject` parameter `RejectReason` `Edm.String` `Nullable="false"` MaxLength 255, the same as the app.
- Action enablement `Core.OperationAvailable Path="_it/__OperationControl/<action>"` for `approve`,
`reject` and `retrySync`.
- Insertable, Updatable and Deletable are all `false`.
## Landmines learned live (KNOWLEDGE.md candidates)
The app was reconciled to this metadata on 2026-09-16, including backend-owned action enablement
(CHANGELOG). Rows below marked UNVERIFIED, "unknown until live" or "intended difference" are resolved
by this list.
- [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 →
All 17 app properties exist in `ZC_CHANGEAPPROVAL` with the same names and case. The three actions
`approve`/`reject`/`retrySync` are in `ZC_CHANGEAPPROVAL` (projection BDEF), and
`ZUI_ARENA_CHANGE_APPROVAL` exposes exactly `ZC_CHANGEAPPROVAL as ChangeApproval`. `expose … as`
sets the entity set name and `<alias>_Type` [source: abap-docs /abap-fiori-showcase/06_object_page_content,
verified 2026-09-15] (live: `<alias>Type`, see above). The source is not changed for the open items below. Decisions sit with the
coordinator and are confirmed at U15 / V-2.
| Item | App mock | Source | Status |
|---|---|---|---|
| Service URL / schema namespace | `manifest.json` dataSource `/sap/opu/odata4/sap/<UI binding, 28-character long form>/srvd/sap/zui_arena_change_approval/0001/`; namespace `com.sap.gateway.srvd.<same long form>.v0001` | binding `ZUI_ARENA_CHANGE_APPR_O4` (D-1): intended `/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/` | **intended difference**; the app's dataSource URL, mock namespace and annotation targets are reconciled at V-2 from the live `$metadata` (separate Fiori brief) |
| Entity set / entity type / key | `ChangeApproval` / `ChangeApproval_Type` / `ID` | `expose ZC_CHANGEAPPROVAL as ChangeApproval` | match |
| String lengths (ItemNumber 18, ItemName 40, ChangeType 15, Status 10, ArenaEcoId 20, RequestedBy/DecidedBy 12, SapChangeNumber 12, ErrorMessage/RejectReason 255) | as listed | same CHAR lengths in `ZCHANGEAPPROVAL` | match |
| `SapPreview` | Edm.String, no MaxLength | `abap.string(0)` (RAP-7) | match expected |
| `StatusText` | absent | CASE result, CHAR(29) by the max-type rule [source: abap-docs /abap-docs-cloud/ABENCDS_CASE_EXPRESSION_V2, verified 2026-09-16]; RAP-3 text element | extra; keep |
| `LocalLastChangedAt` | absent | etag field for `use etag` | extra; keep |
| `StatusCriticality` | Edm.Byte | integer literals typed INT1..INT8 by value [source: abap-docs /abap-docs-cloud/ABENCDS_UNTYPED_LITERAL_V2, verified 2026-09-16] | INT1 → Edm.Byte UNVERIFIED |
| `IsApprovable`, `IsRetriable` | Edm.Boolean | `cast(… as abap_boolean)` | ABAP_BOOLEAN → Edm.Boolean UNVERIFIED |
| `RequestedOn`, `DecidedOn` | Edm.DateTimeOffset Precision 7 | `abap.utclong` | UTCLONG mapping UNVERIFIED |
| `ID` | Edm.Guid, `Core.Computed` | `sysuuid_x16`, `readonly, numbering : managed` | expected, UNVERIFIED |
| `reject` parameter `RejectReason` | Nullable="false", MaxLength 255 | `ZD_CHANGEAPPROVAL_REJECTP` CHAR(255), not declared mandatory; the handler rejects an empty reason | differs in metadata, behavior meets RAP-2 |
| Action enablement | `Core.OperationAvailable Path="IsApprovable"/"IsRetriable"` (mock only) | RAP generates it from `( features : instance )` | path form unknown until live |
| Insert/Update/Delete restrictions | all false | UI projection BDEF exposes no create/update/delete | expected |
## Design decisions baked into the files (falsifiable, dated 2026-09-15 unless noted)
- **State machine** (PRD ARN-002; `retrySync` target decided 2026-09-16):
| Action | From | To | Caller |
|---|---|---|---|
| create (determination `setInitialStatus`) | none | PENDING | worker (A2X) |
| `approve` | PENDING | APPROVED | business user (UI) |
| `reject(RejectReason)` | PENDING | REJECTED (terminal) | business user (UI) |
| `markExecuting` | APPROVED | EXECUTING | worker (A2X) |
| `markSynced(SapChangeNumber)` | EXECUTING | SYNCED (terminal) | worker (A2X) |
| `markFailed(ErrorMessage)` | EXECUTING | FAILED | worker (A2X) |
| `retrySync` | FAILED | **APPROVED** (clears `ErrorMessage`, keeps `DecidedBy`/`DecidedOn`) | business user (UI) |
Only `markExecuting` writes EXECUTING. A retried record goes back through `markExecuting`, so the
worker re-claims it the same way as a first approval. Feature control is unchanged: `retrySync` is
enabled only for FAILED. Each action re-checks the status in the handler as well.
- **RAP action implementation contract** (2026-09-16). Every action handler returns failed rows
with `%fail-cause` (not_found or unspecific; `locked` is mapped to unspecific) and
`%op-%action-<name>`. Reported messages carry `%op-%action-<name>`. Failures and messages of the
internal UPDATE are merged into `failed`/`reported`, and `result` holds only keys that did not
fail [source: sap-docs sap-help loio de6569d4b92e40a0911c926170140beb (Action contract),
1040b81372d44ed38b07a409fa0e1769 (General contract), abaprap_impl_rules (allowed fail causes),
verified 2026-09-16]. Under `strict(2)`, contract violations are runtime errors [source: abap-docs
/abap-docs-cloud/ABENBDL_STRICT_2, verified 2026-09-16].
- **Semantic key enforced on create** (2026-09-16). `validation checkDuplicate on save { create; }`
fails a create when another record with the same `ItemNumber` + `ChangeType` + `ArenaEcoId`
exists and is not REJECTED, or when the same request contains that key twice. Validation syntax,
handler signature and effect (the whole transactional buffer is rejected) [source: abap-docs
/abap-docs-cloud/ABENBDL_VALIDATIONS, ABAPHANDLER_METH_VALIDATE, verified 2026-09-16].
- Fail cause: validations may only use `cause-unspecific`.
- Messages are state messages with `%state_area` and `%element`.
- Persisted rows are read from `ZR_CHANGEAPPROVAL` with `OPTIONS PRIVILEGED ACCESS`, because a
handler SELECT on an access-controlled view without it violates the RAP BO contract [source:
sap-docs sap-help abaprap_impl_rules; abap-docs /abap-docs-cloud/ABAPSELECT_PRIVILEGED_ACCESS,
ABAPDERIVED_TYPES_STATE_AREA, verified 2026-09-16].
- A validation is not something a projection lists [source: abap-docs
/abap-docs-cloud/ABENBDL_USE_PROJECTION, verified 2026-09-16], so both projections are unchanged.
- **`use etag` in the entity header** (2026-09-16). In both projection BDEFs `use etag` sits between
`define behavior for … alias ChangeApproval` and `{`, without a semicolon. It is an entity behavior
characteristic, not a body statement [source: abap-docs /abap-docs-cloud/ABENBDL_CHARACTER_PROJECTION,
ABENBDL_DEFINE_BEH_PROJECTION, verified 2026-09-16].
- **Two projections, two service definitions.** The UI projection `ZC_CHANGEAPPROVAL` exposes only
approve/reject/retrySync on `ZUI_ARENA_CHANGE_APPROVAL`. The worker's `ZC_CHANGEAPPROVAL_A2X`
exposes create + markExecuting/markSynced/markFailed on `ZAPI_ARENA_CHANGE_APPROVAL`. A projection
BDEF exposes exactly what it lists, with no automatic inheritance [source: abap-docs
/abap-docs-cloud/ABENBDL_USE_PROJECTION]. PRD RAP-5's wording (two bindings on one definition) is
superseded by this design and is a governance item. (Closed: PRD RAP-5 was amended to this
design on 2026-09-16; PRD read 2026-09-16.)
- **No projection behavior pool** (2026-09-16: never created). A projection BDEF needs one only for
new actions/functions [source: abap-docs /abap-docs-cloud/ABENBDL_BDEF_PROJECTION_HEADER].
- **`authorization master ( none )`**: ADR-002 makes the IAM app / business catalog the only access
control, and DCL governs reads [source: abap-docs /abap-docs-cloud/ABENBDL_AUTHORIZATION]. Switch
to `( global )` plus a `FOR GLOBAL AUTHORIZATION` handler when a custom authorization object
exists. **Not final:** `( none )` versus global authorization is open question PRD OQ-8, to be
decided before business roles are assigned.
- **Derived fields are computed, not persisted.** `StatusText`, `StatusCriticality`, `IsApprovable`
and `IsRetriable` are CASE expressions in `ZR_CHANGEAPPROVAL`, because 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 U6).
- **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].
- **`internal update`** keeps `update` off every OData surface. Actions and the determination still
update fields `IN LOCAL MODE` [source: abap-docs /abap-docs-cloud/ABENBDL_INTERNAL].
- **RequestedBy** holds the technical user of the creator (the worker's communication user in the
A2X flow). Change it to `mandatory : create` if the Arena requester should be shown.
- **No SAP object type** (2026-09-16), see the inventory section.
- **Released objects used** (all level A, public_cloud): `SYSUUID_X16`, `ABP_CREATION_USER`,
`ABP_CREATION_TSTMPL`, `ABP_LASTCHANGE_USER`, `ABP_LASTCHANGE_TSTMPL`,
`ABP_LOCINST_LASTCHANGE_TSTMPL`, `ABAP_BOOLEAN`, `CL_ABAP_BEHAVIOR_HANDLER`, `CL_ABAP_CONTEXT_INFO`,
`IF_ABAP_BEHV`, `IF_ABAP_BEHV_MESSAGE` [source: sap-released-objects
sap_check_clean_core_compliance, verified 2026-09-16].
## Landmines (live-learned, dated)
- [2026-09-15] `create_object` for SRVD needs `sourceType` (`"S"` worked; omitting it fails with
"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.
DDLS/SRVD's **active** version, so source-less objects block dependent creations.
- [2026-09-15, **superseded 2026-09-16**] Activation URIs recorded on 2026-09-15 came from
generator objects: BDEF `.bdef.json` and TABL `.tabl.json` (both generator-era). They are wrong for
objects created with `create_object`; see the per-type entry below.
- [2026-09-16] **Per-type file extensions: use the `filePath` returned by `create_object`.** Returned
and proven by activation: TABL `.tabl.ddic` (U1), DDLS `.ddls.acds`, DCLS `.dcls.acds`, DDLX
`.ddlx.acds`, BDEF **`.bdef.abdl`** (U6, U9), CLAS `.clas.abap` (global class only; no Local Types
path is returned), SRVD `.srvd.acds` (U13), SRVB **`.srvb.json`** (U14) [source: adt-mcp
abap_creation-create_object / abap_activate_objects on 0M7DEJI, verified 2026-09-16].
- [2026-09-16] A `.json` file in an object folder of the VS Code ADT workspace is metadata only
(`zchangeapproval.tabl.json`: formatVersion, description, originalLanguage,
`abapLanguageVersion: cloudDevelopment`). Never paste source into it. The table source goes into
`.tabl.ddic` (live observation 2026-09-16). The binding's `.srvb.json` is the exception by type:
it is the returned `filePath`, nothing is pasted into it, and activation and publish go through
it (U14, U15).
- [2026-09-16] **Service definition sources must carry no comments.** A leading `/* … */` header made
U13's activation fail with "Comments are not supported and will be deleted on save" (Ln 1), and the
same definition without it activated (live 2026-09-16). The DCLS, DDLX and BDEF headers did not
block. CDS SDL follows the general CDS syntax rules, which allow comments [source: abap-docs
/abap-docs-cloud/ABENCDS_F1_SDL_SYNTAX, ABENCDS_GENERAL_SYNTAX_RULES, verified 2026-09-16], so this
is the tooling, not the language. Both SRVD files in this folder are comment-free; keep them so.
- [2026-09-16] **`abap_activate_objects` output is thin.** Success returns only "Activation
successful.", with no warnings. Failure lists messages with line and column but no severity, so read
severity in the editor [source: adt-mcp abap_activate_objects on 0M7DEJI, verified 2026-09-16].
- [2026-09-16] **Publishing a binding in ADT for VS Code is a code lens.** Open the binding's
`.srvb.json` source and choose the **Publish** code lens; Unpublish and Preview… work the same way
[source: sap-docs sap-help loio dd6f5de63e7245a7abe7e39389841596 "ABAP RAP Tools", verified
2026-09-16]. The extension contributes no publish command, the SAP ADT MCP has no publish tool, and
the only visible feedback is "<binding> published locally". Verify with `fetch_services`
(`isPublished: true`) (KNOWLEDGE 2026-09-16; U14 published 2026-09-16 04:26).
- [2026-09-16] **UI host vs `-api` host.** `fetch_service_information` reports the service URL on
`my431353-api.s4hana.cloud.sap` with `sap-client=080` [source: adt-mcp
abap_business_services-fetch_service_information on 0M7DEJI, verified 2026-09-16]. A business user
calling that host from the browser is refused as "Forbidden / blocked by UCON". The same path on
`my431353.s4hana.cloud.sap` returns `$metadata` (live 2026-09-16; KNOWLEDGE 2026-09-16). The UI
service and the Fiori app use the UI host. The `-api` host is the communication-user entry point,
so the worker's Web API (W5) is reached there only through a custom communication scenario (PRD
V-2), never through the UI service.
- [2026-09-15] The ADT MCP has no delete tool and no package tool. A wrong name is permanent from
the agent side, and a missing package must be created in ADT.
- [2026-09-15] ATC via MCP rejects RONT/NONT ("No valid data model exists for object type
RONT/ROT"). Not relevant here because no SAP object type is created.
- [2026-09-16] A missing package shows up read-only as "Package '<name>' does not exist" from
`abap_creation-run_validation` [source: adt-mcp on 0M7DEJI, verified 2026-09-16].
- [2026-09-16] Service binding name limit: SAP Help says 26 in every version checked, while the
ADT MCP validation accepts 30 (undocumented). The 26 limit is used here (D-1, resolved). U14 (24
characters) published on 2026-09-16.
- [2026-09-16] The V4 URL's second segment is the binding's service alias, renamable only while
unpublished (U14). For U14 it was verified after activation with `fetch_services`, before
publishing; opening the binding before activation is not needed. U14 is now published, so its
alias is fixed.
@@ -1,7 +1,3 @@
/* 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
@@ -0,0 +1,16 @@
" TARGET OBJECT : class ZBP_R_CHANGEAPPROVAL - GLOBAL CLASS include (behavior pool) - README step U7, UI leg
" abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Source%20Code%20Library/Classes/ZBP_R_CHANGEAPPROVAL/zbp_r_changeapproval.clas.abap
" ACTION : after BDEF ZR_CHANGEAPPROVAL is active (README U6), create empty via ADT MCP (objectType CLAS/OC,
" README U7), replace entire content of the global class; the handlers go into "Local Types"
" (locals_imp file). Save both, then activate the class.
" 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 zbp_r_changeapproval DEFINITION
PUBLIC
ABSTRACT
FINAL
FOR BEHAVIOR OF zr_changeapproval.
ENDCLASS.
CLASS zbp_r_changeapproval IMPLEMENTATION.
ENDCLASS.
@@ -0,0 +1,765 @@
" TARGET OBJECT : class ZBP_R_CHANGEAPPROVAL - LOCAL TYPES include (Local Types tab in ADT) - README step U7, UI leg
" abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Source%20Code%20Library/Classes/ZBP_R_CHANGEAPPROVAL/zbp_r_changeapproval.clas.locals_imp.abap
" ACTION : replace entire content, save, then activate the class (README U7; BDEF ZR_CHANGEAPPROVAL must be
" active first). Activation goes through the class URI (.clas.abap), not this include.
"
" Sources (verified 2026-09-15 unless dated otherwise):
" handler class inherits from CL_ABAP_BEHAVIOR_HANDLER (released) [sap-released-objects public_cloud A, verified 2026-09-16]
" 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
" [abap-docs /abap-docs-cloud/ABENBDL_ACTIONS_FC example]
" MODIFY ENTITIES ... IN LOCAL MODE (skips feature control and authorization) with UPDATE FIELDS ( ) WITH <table>
" [abap-docs /abap-docs-cloud/ABAPMODIFY_ENTITY_ENTITIES, ABAPMODIFY_ENTITY_ENTITIES_OP, ABENBDL_AUTHORIZATION;
" sap-docs sap-help loio e75275489fe648f6b00977be76f0bc06 (deductDiscount: collect valid keys, update, read, result), verified 2026-09-16]
" 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, verified 2026-09-16]
" if_abap_behv / if_abap_behv_message are released [sap-released-objects INTF public_cloud A, verified 2026-09-16]
" RAP action implementation contract (verified 2026-09-16):
" - an unsuccessful action returns one row per failed input identifier in failed "with the correct fail cause and
" flags the %action component on the correct action"; for every failed identifier with a cause other than
" not_found there is one error message in reported [sap-docs sap-help loio de6569d4b92e40a0911c926170140beb]
" - for fail causes other than not_found/locked the failed operation in %op must be provided; reported always fills
" %op; result contains only input identifiers not in failed, without duplicates
" [sap-docs sap-help loio 1040b81372d44ed38b07a409fa0e1769]
" - %op groups the operation flags incl. %action [abap-docs /abap-docs-cloud/ABAPDERIVED_TYPES_OP]; %fail-cause values
" are defined in IF_ABAP_BEHV [abap-docs /abap-docs-cloud/ABAPDERIVED_TYPES_FAIL]; %op-%action-<name> = mk-on in
" reported [sap-docs loio e75275489fe648f6b00977be76f0bc06]
" - allowed fail causes: actions any cause except if_abap_behv=>cause-locked; validations on save only
" if_abap_behv=>cause-unspecific; with a non-initial %state_area only %element may be flagged in %op
" [sap-docs sap-help abaprap_impl_rules "RAP Implementation Rules"]
" - strict(2) turns RAP BO contract violations into runtime errors [abap-docs /abap-docs-cloud/ABENBDL_STRICT_2]
" - merging a MODIFY response: CORRESPONDING #( DEEP APPENDING BASE ( reported ) ... ). DEEP alone replaces a nested
" table such as reported-changeapproval and would drop the messages appended earlier in the handler; DEEP APPENDING
" BASE "ensures that the nested target tables are not deleted" and "appends the lines of the nested source tables"
" [abap-docs /abap-docs-cloud/ABENCORRESPONDING_CONSTR_ARG_TYPE, ABENNEWS-783-ASSIGNMENTS, verified 2026-09-16]
" Validation (verified 2026-09-16):
" - METHODS meth FOR VALIDATE ON SAVE IMPORTING keys FOR bdef~val; failed/reported implicit
" [abap-docs /abap-docs-cloud/ABAPHANDLER_METH_VALIDATE]; no EML MODIFY in validations; a failing validation
" rejects the whole transactional buffer [abap-docs /abap-docs-cloud/ABENBDL_VALIDATIONS]
" - state message pattern: invalidate %tky + %state_area first, then fail + message with %state_area and %element
" [abap-docs /abap-docs-cloud/ABAPDERIVED_TYPES_STATE_AREA, ABENBDL_VALIDATIONS example]
" - a SELECT on an access-controlled CDS view without OPTIONS PRIVILEGED ACCESS inside a RAP handler is a RAP BO
" contract violation [sap-docs sap-help abaprap_impl_rules "Non-privileged access"]; PRIVILEGED ACCESS disables CDS
" access control for the SELECT, OPTIONS goes at the end of the statement
" [abap-docs /abap-docs-cloud/ABAPSELECT_PRIVILEGED_ACCESS, ABAPSELECT_OPTIONS]
" Design notes:
" - State machine (PRD ARN-002; retrySync decision 2026-09-16): create -> PENDING; approve PENDING -> APPROVED;
" reject PENDING -> REJECTED (terminal); markExecuting APPROVED -> EXECUTING; markSynced EXECUTING -> SYNCED
" (terminal); markFailed EXECUTING -> FAILED; retrySync FAILED -> APPROVED. Only markExecuting writes EXECUTING, so a
" retried record is re-claimed by the worker through markExecuting and cannot be executed twice in parallel.
" - 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 flags its own
" %op-%action-<name> component, so a generic helper would need additional derived-type plumbing.
" - A failed UPDATE with cause locked is reported as unspecific, because locked is not an allowed cause for actions.
" - 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.
" - checkDuplicate: the SELECT only sees persisted rows (a validation runs in the save sequence before the new
" instances are written), so duplicates inside the same request are checked separately on the buffer data.
" UNVERIFIED 2026-09-16: the list of SELECT additions that cannot be combined with PRIVILEGED ACCESS was not
" retrievable; this uses SELECT SINGLE without FOR ALL ENTRIES. The syntax check at U7 activation is the proof.
" abaplint (abap-docs abap_lint, 2026-09-16) does not parse `OPTIONS PRIVILEGED ACCESS` but parses the FROM-clause
" form. If the tenant syntax check refuses the OPTIONS form, use the equally documented
" `SELECT SINGLE FROM zr_changeapproval WITH PRIVILEGED ACCESS FIELDS ID WHERE ... INTO @DATA(existing_id).`
" (affects only that entity; not combinable with OPTIONS PRIVILEGED ACCESS)
" [source: abap-docs /abap-docs-cloud/ABAPSELECT_DATA_SOURCE, verified 2026-09-16].
CLASS lhc_changeapproval DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
CONSTANTS:
BEGIN OF status,
pending TYPE zchangeapproval-status VALUE 'PENDING',
approved TYPE zchangeapproval-status VALUE 'APPROVED',
executing TYPE zchangeapproval-status VALUE 'EXECUTING',
synced TYPE zchangeapproval-status VALUE 'SYNCED',
rejected TYPE zchangeapproval-status VALUE 'REJECTED',
failed TYPE zchangeapproval-status VALUE 'FAILED',
END OF status.
CONSTANTS state_area_duplicate TYPE string VALUE `CHECK_DUPLICATE`.
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.
METHODS checkDuplicate FOR VALIDATE ON SAVE
IMPORTING keys FOR ChangeApproval~checkDuplicate.
ENDCLASS.
CLASS lhc_changeapproval IMPLEMENTATION.
METHOD get_instance_features.
READ ENTITIES OF zr_changeapproval 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)
DATA approvals_for_update TYPE TABLE FOR UPDATE zr_changeapproval.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-not_found
%op-%action-approve = if_abap_behv=>mk-on ) TO failed-changeapproval.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-pending.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-approve = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-approve = if_abap_behv=>mk-on
%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.
APPEND VALUE #( %tky = key-%tky
Status = status-approved
DecidedBy = cl_abap_context_info=>get_user_technical_name( )
DecidedOn = utclong_current( ) ) TO approvals_for_update.
ENDLOOP.
IF approvals_for_update IS NOT INITIAL.
MODIFY ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status DecidedBy DecidedOn )
WITH approvals_for_update
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
" Propagate the internal UPDATE response into the action response
LOOP AT update_reported-changeapproval ASSIGNING FIELD-SYMBOL(<update_message>).
<update_message>-%op-%action-approve = if_abap_behv=>mk-on.
ENDLOOP.
reported = CORRESPONDING #( DEEP APPENDING BASE ( reported ) update_reported ).
LOOP AT update_failed-changeapproval INTO DATA(update_failure).
APPEND VALUE #( %tky = update_failure-%tky
%fail-cause = COND #( WHEN update_failure-%fail-cause = if_abap_behv=>cause-locked
THEN if_abap_behv=>cause-unspecific
ELSE update_failure-%fail-cause )
%op-%action-approve = if_abap_behv=>mk-on ) TO failed-changeapproval.
IF update_failure-%fail-cause <> if_abap_behv=>cause-not_found
AND NOT line_exists( update_reported-changeapproval[ ID = update_failure-ID ] ).
APPEND VALUE #( %tky = update_failure-%tky
%op-%action-approve = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'The approval could not be recorded' ) )
TO reported-changeapproval.
ENDIF.
ENDLOOP.
ENDIF.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
LOOP AT updated INTO DATA(row).
IF NOT line_exists( failed-changeapproval[ ID = row-ID ] ).
APPEND VALUE #( %tky = row-%tky %param = row ) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD reject.
" PENDING -> REJECTED with a mandatory reason (PRD RAP-2); REJECTED is terminal
DATA approvals_for_update TYPE TABLE FOR UPDATE zr_changeapproval.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-not_found
%op-%action-reject = if_abap_behv=>mk-on ) TO failed-changeapproval.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-pending.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-reject = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-reject = if_abap_behv=>mk-on
%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 = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-reject = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-reject = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'A reject reason is required' ) )
TO reported-changeapproval.
CONTINUE.
ENDIF.
APPEND VALUE #( %tky = key-%tky
Status = status-rejected
RejectReason = key-%param-RejectReason
DecidedBy = cl_abap_context_info=>get_user_technical_name( )
DecidedOn = utclong_current( ) ) TO approvals_for_update.
ENDLOOP.
IF approvals_for_update IS NOT INITIAL.
MODIFY ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status RejectReason DecidedBy DecidedOn )
WITH approvals_for_update
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
LOOP AT update_reported-changeapproval ASSIGNING FIELD-SYMBOL(<update_message>).
<update_message>-%op-%action-reject = if_abap_behv=>mk-on.
ENDLOOP.
reported = CORRESPONDING #( DEEP APPENDING BASE ( reported ) update_reported ).
LOOP AT update_failed-changeapproval INTO DATA(update_failure).
APPEND VALUE #( %tky = update_failure-%tky
%fail-cause = COND #( WHEN update_failure-%fail-cause = if_abap_behv=>cause-locked
THEN if_abap_behv=>cause-unspecific
ELSE update_failure-%fail-cause )
%op-%action-reject = if_abap_behv=>mk-on ) TO failed-changeapproval.
IF update_failure-%fail-cause <> if_abap_behv=>cause-not_found
AND NOT line_exists( update_reported-changeapproval[ ID = update_failure-ID ] ).
APPEND VALUE #( %tky = update_failure-%tky
%op-%action-reject = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'The rejection could not be recorded' ) )
TO reported-changeapproval.
ENDIF.
ENDLOOP.
ENDIF.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
LOOP AT updated INTO DATA(row).
IF NOT line_exists( failed-changeapproval[ ID = row-ID ] ).
APPEND VALUE #( %tky = row-%tky %param = row ) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD retrySync.
" FAILED -> APPROVED (decision 2026-09-16): clears the previous error and keeps DecidedBy/DecidedOn of the original
" approval. The worker re-claims the record through markExecuting (APPROVED -> EXECUTING) and re-executes the staged
" payload (ARN-002 amendment, ARN-010). retrySync never writes EXECUTING itself.
DATA approvals_for_update TYPE TABLE FOR UPDATE zr_changeapproval.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-not_found
%op-%action-retrySync = if_abap_behv=>mk-on ) TO failed-changeapproval.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-failed.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-retrySync = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-retrySync = if_abap_behv=>mk-on
%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.
APPEND VALUE #( %tky = key-%tky
Status = status-approved
ErrorMessage = '' ) TO approvals_for_update.
ENDLOOP.
IF approvals_for_update IS NOT INITIAL.
MODIFY ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status ErrorMessage )
WITH approvals_for_update
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
LOOP AT update_reported-changeapproval ASSIGNING FIELD-SYMBOL(<update_message>).
<update_message>-%op-%action-retrySync = if_abap_behv=>mk-on.
ENDLOOP.
reported = CORRESPONDING #( DEEP APPENDING BASE ( reported ) update_reported ).
LOOP AT update_failed-changeapproval INTO DATA(update_failure).
APPEND VALUE #( %tky = update_failure-%tky
%fail-cause = COND #( WHEN update_failure-%fail-cause = if_abap_behv=>cause-locked
THEN if_abap_behv=>cause-unspecific
ELSE update_failure-%fail-cause )
%op-%action-retrySync = if_abap_behv=>mk-on ) TO failed-changeapproval.
IF update_failure-%fail-cause <> if_abap_behv=>cause-not_found
AND NOT line_exists( update_reported-changeapproval[ ID = update_failure-ID ] ).
APPEND VALUE #( %tky = update_failure-%tky
%op-%action-retrySync = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'The retry could not be recorded' ) )
TO reported-changeapproval.
ENDIF.
ENDLOOP.
ENDIF.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
LOOP AT updated INTO DATA(row).
IF NOT line_exists( failed-changeapproval[ ID = row-ID ] ).
APPEND VALUE #( %tky = row-%tky %param = row ) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD markExecuting.
" APPROVED -> EXECUTING (worker claims the record; the UI never offers Approve on an executing record, PRD RAP-6).
" The only place that writes EXECUTING.
DATA approvals_for_update TYPE TABLE FOR UPDATE zr_changeapproval.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-not_found
%op-%action-markExecuting = if_abap_behv=>mk-on ) TO failed-changeapproval.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-approved.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-markExecuting = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-markExecuting = if_abap_behv=>mk-on
%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.
APPEND VALUE #( %tky = key-%tky
Status = status-executing ) TO approvals_for_update.
ENDLOOP.
IF approvals_for_update IS NOT INITIAL.
MODIFY ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status )
WITH approvals_for_update
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
LOOP AT update_reported-changeapproval ASSIGNING FIELD-SYMBOL(<update_message>).
<update_message>-%op-%action-markExecuting = if_abap_behv=>mk-on.
ENDLOOP.
reported = CORRESPONDING #( DEEP APPENDING BASE ( reported ) update_reported ).
LOOP AT update_failed-changeapproval INTO DATA(update_failure).
APPEND VALUE #( %tky = update_failure-%tky
%fail-cause = COND #( WHEN update_failure-%fail-cause = if_abap_behv=>cause-locked
THEN if_abap_behv=>cause-unspecific
ELSE update_failure-%fail-cause )
%op-%action-markExecuting = if_abap_behv=>mk-on ) TO failed-changeapproval.
IF update_failure-%fail-cause <> if_abap_behv=>cause-not_found
AND NOT line_exists( update_reported-changeapproval[ ID = update_failure-ID ] ).
APPEND VALUE #( %tky = update_failure-%tky
%op-%action-markExecuting = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'The execution start could not be recorded' ) )
TO reported-changeapproval.
ENDIF.
ENDLOOP.
ENDIF.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
LOOP AT updated INTO DATA(row).
IF NOT line_exists( failed-changeapproval[ ID = row-ID ] ).
APPEND VALUE #( %tky = row-%tky %param = row ) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD markSynced.
" EXECUTING -> SYNCED (terminal success); SAP is the ledger, the change number is the receipt (ARN-006)
DATA approvals_for_update TYPE TABLE FOR UPDATE zr_changeapproval.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-not_found
%op-%action-markSynced = if_abap_behv=>mk-on ) TO failed-changeapproval.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-executing.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-markSynced = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-markSynced = if_abap_behv=>mk-on
%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 = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-markSynced = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-markSynced = if_abap_behv=>mk-on
%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.
APPEND VALUE #( %tky = key-%tky
Status = status-synced
SapChangeNumber = key-%param-SapChangeNumber
ErrorMessage = '' ) TO approvals_for_update.
ENDLOOP.
IF approvals_for_update IS NOT INITIAL.
MODIFY ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status SapChangeNumber ErrorMessage )
WITH approvals_for_update
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
LOOP AT update_reported-changeapproval ASSIGNING FIELD-SYMBOL(<update_message>).
<update_message>-%op-%action-markSynced = if_abap_behv=>mk-on.
ENDLOOP.
reported = CORRESPONDING #( DEEP APPENDING BASE ( reported ) update_reported ).
LOOP AT update_failed-changeapproval INTO DATA(update_failure).
APPEND VALUE #( %tky = update_failure-%tky
%fail-cause = COND #( WHEN update_failure-%fail-cause = if_abap_behv=>cause-locked
THEN if_abap_behv=>cause-unspecific
ELSE update_failure-%fail-cause )
%op-%action-markSynced = if_abap_behv=>mk-on ) TO failed-changeapproval.
IF update_failure-%fail-cause <> if_abap_behv=>cause-not_found
AND NOT line_exists( update_reported-changeapproval[ ID = update_failure-ID ] ).
APPEND VALUE #( %tky = update_failure-%tky
%op-%action-markSynced = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'The sync result could not be recorded' ) )
TO reported-changeapproval.
ENDIF.
ENDLOOP.
ENDIF.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
LOOP AT updated INTO DATA(row).
IF NOT line_exists( failed-changeapproval[ ID = row-ID ] ).
APPEND VALUE #( %tky = row-%tky %param = row ) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD markFailed.
" EXECUTING -> FAILED with the error text; distinct from REJECTED (predecessor defect, ARN-002)
DATA approvals_for_update TYPE TABLE FOR UPDATE zr_changeapproval.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
LOOP AT keys INTO DATA(key).
ASSIGN approvals[ ID = key-ID ] TO FIELD-SYMBOL(<approval>).
IF sy-subrc <> 0.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-not_found
%op-%action-markFailed = if_abap_behv=>mk-on ) TO failed-changeapproval.
CONTINUE.
ENDIF.
IF <approval>-Status <> status-executing.
APPEND VALUE #( %tky = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-markFailed = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-markFailed = if_abap_behv=>mk-on
%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 = key-%tky
%fail-cause = if_abap_behv=>cause-unspecific
%op-%action-markFailed = if_abap_behv=>mk-on ) TO failed-changeapproval.
APPEND VALUE #( %tky = key-%tky
%op-%action-markFailed = if_abap_behv=>mk-on
%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.
APPEND VALUE #( %tky = key-%tky
Status = status-failed
ErrorMessage = key-%param-ErrorMessage ) TO approvals_for_update.
ENDLOOP.
IF approvals_for_update IS NOT INITIAL.
MODIFY ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
UPDATE FIELDS ( Status ErrorMessage )
WITH approvals_for_update
FAILED DATA(update_failed)
REPORTED DATA(update_reported).
LOOP AT update_reported-changeapproval ASSIGNING FIELD-SYMBOL(<update_message>).
<update_message>-%op-%action-markFailed = if_abap_behv=>mk-on.
ENDLOOP.
reported = CORRESPONDING #( DEEP APPENDING BASE ( reported ) update_reported ).
LOOP AT update_failed-changeapproval INTO DATA(update_failure).
APPEND VALUE #( %tky = update_failure-%tky
%fail-cause = COND #( WHEN update_failure-%fail-cause = if_abap_behv=>cause-locked
THEN if_abap_behv=>cause-unspecific
ELSE update_failure-%fail-cause )
%op-%action-markFailed = if_abap_behv=>mk-on ) TO failed-changeapproval.
IF update_failure-%fail-cause <> if_abap_behv=>cause-not_found
AND NOT line_exists( update_reported-changeapproval[ ID = update_failure-ID ] ).
APPEND VALUE #( %tky = update_failure-%tky
%op-%action-markFailed = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'The failure could not be recorded' ) )
TO reported-changeapproval.
ENDIF.
ENDLOOP.
ENDIF.
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
LOOP AT updated INTO DATA(row).
IF NOT line_exists( failed-changeapproval[ ID = row-ID ] ).
APPEND VALUE #( %tky = row-%tky %param = row ) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD setInitialStatus.
" Every created record starts PENDING with the requester audit fields (ARN-002)
READ ENTITIES OF zr_changeapproval 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 zr_changeapproval 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.
METHOD checkDuplicate.
" Semantic key ItemNumber + ChangeType + ArenaEcoId (PRD RAP-8) must be unique among records that are not REJECTED.
" Checks (1) duplicates among the instances of this request and (2) persisted records, read through the CDS view
" with privileged access (released access only; the view is access-controlled, see header sources).
READ ENTITIES OF zr_changeapproval IN LOCAL MODE
ENTITY ChangeApproval
FIELDS ( ItemNumber ChangeType ArenaEcoId )
WITH CORRESPONDING #( keys )
RESULT DATA(approvals).
LOOP AT approvals INTO DATA(approval).
" Invalidate a state message left by an earlier run of this validation for the instance
APPEND VALUE #( %tky = approval-%tky
%state_area = state_area_duplicate ) TO reported-changeapproval.
DATA(same_key_in_request) = REDUCE i( INIT count = 0
FOR other IN approvals
WHERE ( ItemNumber = approval-ItemNumber
AND ChangeType = approval-ChangeType
AND ArenaEcoId = approval-ArenaEcoId )
NEXT count = count + 1 ).
DATA(is_duplicate) = xsdbool( same_key_in_request > 1 ).
IF is_duplicate = abap_false.
SELECT SINGLE FROM zr_changeapproval
FIELDS ID
WHERE ItemNumber = @approval-ItemNumber
AND ChangeType = @approval-ChangeType
AND ArenaEcoId = @approval-ArenaEcoId
AND Status <> @status-rejected
AND ID <> @approval-ID
INTO @DATA(existing_id)
OPTIONS PRIVILEGED ACCESS.
is_duplicate = xsdbool( sy-subrc = 0 ).
ENDIF.
IF is_duplicate = abap_true.
APPEND VALUE #( %tky = approval-%tky
%fail-cause = if_abap_behv=>cause-unspecific ) TO failed-changeapproval.
APPEND VALUE #( %tky = approval-%tky
%state_area = state_area_duplicate
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |A change approval for item { approval-ItemNumber }, | &&
|change type { approval-ChangeType } and ECO { approval-ArenaEcoId } | &&
|already exists and is not rejected| )
%element-ItemNumber = if_abap_behv=>mk-on
%element-ChangeType = if_abap_behv=>mk-on
%element-ArenaEcoId = if_abap_behv=>mk-on ) TO reported-changeapproval.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
@@ -0,0 +1,26 @@
// TARGET OBJECT : behavior definition ZC_CHANGEAPPROVAL (UI projection BDEF) - README step U9, UI leg
// abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL/zc_changeapproval.bdef.json
// ACTION : create via ADT MCP (objectType BDEF/BDO, implementationType Projection, README U9) once the view
// ZC_CHANGEAPPROVAL is active, replace entire content, save, then activate (BDEF ZR_CHANGEAPPROVAL and
// class ZBP_R_CHANGEAPPROVAL must be active).
// No behavior pool is created for this projection: a projection BDEF needs one only when it defines
// new actions/functions, and this one reuses everything
// [source: abap-docs /abap-docs-cloud/ABENBDL_BDEF_PROJECTION_HEADER, verified 2026-09-15].
// Contract : approve / reject / retrySync are the bound-action names the Fiori app calls (exact case).
// 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).
// `use etag` is an entity behavior characteristic: it sits between `define behavior for ... alias ...` and `{`,
// without a semicolon [source: abap-docs /abap-docs-cloud/ABENBDL_CHARACTER_PROJECTION and
// /abap-docs-cloud/ABENBDL_DEFINE_BEH_PROJECTION, verified 2026-09-16]; it must be stated per entity or optimistic
// concurrency control is lost [source: abap-docs /abap-docs-cloud/ABENBDL_USE_PROJECTION, verified 2026-09-16].
projection;
strict ( 2 );
define behavior for ZC_CHANGEAPPROVAL alias ChangeApproval
use etag
{
use action approve;
use action reject;
use action retrySync;
}
@@ -0,0 +1,13 @@
/* TARGET OBJECT : access control ZC_CHANGEAPPROVAL - README step U11, UI leg
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL/zc_changeapproval.dcls.acds
ACTION : create empty via ADT MCP (objectType DCLS/DL, README U11), replace entire content, save, then activate
(after view ZC_CHANGEAPPROVAL and access control ZR_CHANGEAPPROVAL).
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 ZC_CHANGEAPPROVAL'
@MappingRole: true
define role ZC_CHANGEAPPROVAL {
grant select on ZC_CHANGEAPPROVAL
where inheriting conditions from entity ZR_CHANGEAPPROVAL;
}
@@ -1,6 +1,10 @@
/* 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).
/* TARGET OBJECT : data definition ZC_CHANGEAPPROVAL (UI projection) - README step U8, UI leg
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddls.acds
ACTION : create empty via ADT MCP (objectType DDLS/DF, README U8), replace entire content, save, then activate
(after ZR_CHANGEAPPROVAL).
Contract : element names are the OData property names of entity type ChangeApproval_Type in the Fiori app
(app/webapp/localService/mainService/metadata.xml, 17 properties, exact case). StatusText (RAP-3 text
element) and LocalLastChangedAt (etag) are in addition to the app's mock - see README "App contract check".
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]. */
@@ -8,9 +12,9 @@
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@ObjectModel.semanticKey: ['ItemNumber', 'ChangeType', 'ArenaEcoId']
define root view entity ZZC_CHANGEAPPROVAL000
define root view entity ZC_CHANGEAPPROVAL
provider contract transactional_query
as projection on ZZR_CHANGEAPPROVAL000
as projection on ZR_CHANGEAPPROVAL
{
key ID,
ItemNumber,
@@ -1,6 +1,7 @@
/* 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).
/* TARGET OBJECT : metadata extension ZC_CHANGEAPPROVAL - README step U12, UI leg
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Metadata%20Extensions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddlx.acds
ACTION : create empty via ADT MCP (objectType DDLX/EX, README U12), replace entire content, save, then activate
(after ZC_CHANGEAPPROVAL 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):
@@ -31,7 +32,7 @@
visualizations: [ { type: #AS_LINEITEM } ]
} ]
}
annotate entity ZZC_CHANGEAPPROVAL000 with
annotate entity ZC_CHANGEAPPROVAL with
{
@UI.facet: [
{ id: 'StatusPoint', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'StatusPoint' },
@@ -1,16 +1,20 @@
// 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).
// TARGET OBJECT : behavior definition ZC_CHANGEAPPROVAL_A2X (A2X projection BDEF) - README step W3, worker leg
// abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.bdef.abdl
// ACTION : create via ADT MCP (objectType BDEF/BDO, implementationType Projection, README W3) only once the view
// ZC_CHANGEAPPROVAL_A2X is active - the MCP validates the referenced entity's active version (live
// 2026-09-15: "The referenced STOB object ZC_CHANGEAPPROVAL_A2X does not exist" while the view had no
// active source). Replace entire content, save, then activate.
// Sources: as ZC_CHANGEAPPROVAL (incl. `use etag` as an entity behavior characteristic in the header, not in the body).
// 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). The base BDEF's validation checkDuplicate runs on every create; it is
// not something a projection lists (validations are not in the `use` list [source: abap-docs
// /abap-docs-cloud/ABENBDL_USE_PROJECTION, verified 2026-09-16]).
projection;
strict ( 2 );
define behavior for ZC_CHANGEAPPROVAL_A2X alias ChangeApproval
use etag
{
use etag;
use create;
use action markExecuting;
@@ -1,10 +1,11 @@
/* TARGET OBJECT : access control ZC_CHANGEAPPROVAL_A2X (created empty via ADT MCP 2026-09-15)
/* TARGET OBJECT : access control ZC_CHANGEAPPROVAL_A2X - README step W2, worker leg
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. */
ACTION : create empty via ADT MCP (objectType DCLS/DL, README W2), replace entire content, save, then activate
(after ZC_CHANGEAPPROVAL_A2X view and ZR_CHANGEAPPROVAL DCL).
Sources: as ZC_CHANGEAPPROVAL 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;
where inheriting conditions from entity ZR_CHANGEAPPROVAL;
}
@@ -1,15 +1,16 @@
/* TARGET OBJECT : data definition ZC_CHANGEAPPROVAL_A2X (A2X projection for the worker; created empty via ADT MCP 2026-09-15)
/* TARGET OBJECT : data definition ZC_CHANGEAPPROVAL_A2X (A2X projection for the worker) - README step W1, worker leg
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).
ACTION : create empty via ADT MCP (objectType DDLS/DF, README W1), replace entire content, save, then activate
(after the UI leg; needs ZR_CHANGEAPPROVAL).
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 -
(PRD RAP-2 / RAP-6); same syntax sources as ZC_CHANGEAPPROVAL. 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
as projection on ZR_CHANGEAPPROVAL
{
key ID,
ItemNumber,
@@ -1,17 +1,24 @@
/* 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.
/* TARGET OBJECT : database table ZCHANGEAPPROVAL (persistence of the approval BO) - README step U1, UI leg
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Dictionary/Database%20Tables/ZCHANGEAPPROVAL/zchangeapproval.tabl.ddic
(returned by create_object 2026-09-16; activation extension pending live proof - README activation table)
ACTION : create empty via ADT MCP (objectType TABL/DT, README U1), then paste into the .tabl.ddic file, NEVER the
.tabl.json file: in the VS Code ADT workspace zchangeapproval.tabl.json holds only header metadata
(formatVersion, description, originalLanguage, abapLanguageVersion) - live observation 2026-09-16.
Replace entire content of the .tabl.ddic file, save, then activate.
A DDIC database table is edited as Dictionary DDL source code generated from, and saved back into, the
table metadata [source: abap-docs /abap-docs-cloud/ABENDDIC_DEFINE_TABLE, verified 2026-09-16].
Name : 15 characters; TABL/DT allows 16 [source: adt-mcp abap_creation-get_object_type_details on 0M7DEJI, verified 2026-09-16].
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]. */
ABP_* data elements and SYSUUID_X16 [source: sap-released-objects public_cloud level A, verified 2026-09-16]. */
@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 {
define table zchangeapproval {
key client : abap.clnt not null;
key id : sysuuid_x16 not null;
item_number : abap.char(18);
@@ -1,6 +1,8 @@
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_FAILEDP (abstract entity = parameter of action markFailed; created empty via ADT MCP 2026-09-15)
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_FAILEDP (abstract entity = parameter of action markFailed) - README step U4, UI leg
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.
ACTION : create empty via ADT MCP (objectType DDLS/DF, README U4), replace entire content, save, then activate.
Leg : UI leg although only the worker calls markFailed - the base BDEF ZR_CHANGEAPPROVAL (U6) names this
entity as the markFailed parameter, so it is ordered before that BDEF.
Sources: as ZD_CHANGEAPPROVAL_REJECTP. */
@EndUserText.label: 'Arena change approval: markFailed parameter'
define abstract entity ZD_CHANGEAPPROVAL_FAILEDP
@@ -1,6 +1,6 @@
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_REJECTP (abstract entity = parameter of action reject; created empty via ADT MCP 2026-09-15)
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_REJECTP (abstract entity = parameter of action reject) - README step U2, UI leg
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.
ACTION : create empty via ADT MCP (objectType DDLS/DF, README U2), replace entire content, save, 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].
@@ -1,6 +1,8 @@
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_SYNCEDP (abstract entity = parameter of action markSynced; created empty via ADT MCP 2026-09-15)
/* TARGET OBJECT : data definition ZD_CHANGEAPPROVAL_SYNCEDP (abstract entity = parameter of action markSynced) - README step U3, UI leg
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.
ACTION : create empty via ADT MCP (objectType DDLS/DF, README U3), replace entire content, save, then activate.
Leg : UI leg although only the worker calls markSynced - the base BDEF ZR_CHANGEAPPROVAL (U6) names this
entity as the markSynced parameter, so it is ordered before that BDEF.
Sources: as ZD_CHANGEAPPROVAL_REJECTP. */
@EndUserText.label: 'Arena change approval: markSynced parameter'
define abstract entity ZD_CHANGEAPPROVAL_SYNCEDP
@@ -1,6 +1,12 @@
// 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).
// TARGET OBJECT : behavior definition ZR_CHANGEAPPROVAL (base BDEF, managed) - README step U6, UI leg
// abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZR_CHANGEAPPROVAL/zr_changeapproval.bdef.json
// ACTION : create via ADT MCP (objectType BDEF/BDO, implementationType Managed, README U6) once the view
// ZR_CHANGEAPPROVAL is active, replace entire content, save, then activate BEFORE the behavior pool
// class exists. SAP's RAP exercise activates the BDEF first and only then creates the behavior
// implementation class [source: abap-docs /abap-platform-rap-opensap/week4/unit3#step-3-create-and-implement-the-behavior-implementation-bil-class-for-the-travel-entity,
// verified 2026-09-16]. The three ZD_* abstract entities (U2-U4) must be active. That exercise is an
// unmanaged, non-strict BDEF, so if activation refuses because class ZBP_R_CHANGEAPPROVAL does not exist
// (an error, not a warning), create the class (README U7), paste it, and activate BDEF + class in one call.
// 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]
@@ -11,16 +17,25 @@
// 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]
// IAM app / business catalog (ADR-002, ARN-007) and read access by DCL [abap-docs /abap-docs-cloud/ABENBDL_AUTHORIZATION].
// NOT FINAL: ( none ) vs global authorization is open question PRD OQ-8, decided before business roles are assigned.
// Sources (verified 2026-09-16):
// validation ValName on save { create; } - invoked in the save sequence; a failing validation rejects the complete
// transactional buffer of the RAP transaction; EML MODIFY is not allowed in a validation
// [abap-docs /abap-docs-cloud/ABENBDL_VALIDATIONS]
// State machine (PRD ARN-002, M1 decision 2026-09-16):
// create -> PENDING; approve: PENDING -> APPROVED; reject: PENDING -> REJECTED (terminal);
// markExecuting: APPROVED -> EXECUTING; markSynced: EXECUTING -> SYNCED (terminal); markFailed: EXECUTING -> FAILED;
// retrySync: FAILED -> APPROVED (the worker re-claims it through markExecuting). Only markExecuting writes EXECUTING.
// 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;
managed implementation in class zbp_r_changeapproval unique;
strict ( 2 );
define behavior for ZZR_CHANGEAPPROVAL000 alias ChangeApproval
persistent table zzchngpproval000
define behavior for ZR_CHANGEAPPROVAL alias ChangeApproval
persistent table zchangeapproval
lock master
authorization master ( none )
etag master LocalLastChangedAt
@@ -36,7 +51,7 @@ etag master LocalLastChangedAt
SapChangeNumber, ErrorMessage, RejectReason,
CreatedBy, CreatedAt, LastChangedBy, LastChangedAt, LocalLastChangedAt;
// Business-user actions (UI projection): PENDING -> APPROVED | REJECTED, FAILED -> EXECUTING (PRD RAP-1/RAP-2)
// Business-user actions (UI projection): PENDING -> APPROVED | REJECTED, FAILED -> APPROVED (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;
@@ -48,7 +63,10 @@ etag master LocalLastChangedAt
determination setInitialStatus on modify { create; }
mapping for zzchngpproval000
// Semantic key ItemNumber + ChangeType + ArenaEcoId (PRD RAP-8) is unique among non-REJECTED records
validation checkDuplicate on save { create; }
mapping for zchangeapproval
{
ID = id;
ItemNumber = item_number;
@@ -1,12 +1,13 @@
/* 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.
/* TARGET OBJECT : access control ZR_CHANGEAPPROVAL - README step U10, UI leg
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZR_CHANGEAPPROVAL/zr_changeapproval.dcls.acds
ACTION : create empty via ADT MCP (objectType DCLS/DL, README U10), replace entire content, save, then activate
(view ZR_CHANGEAPPROVAL must be active).
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'
@EndUserText.label: 'Access control for ZR_CHANGEAPPROVAL'
@MappingRole: true
define role ZZR_CHANGEAPPROVAL000 {
grant select on ZZR_CHANGEAPPROVAL000;
define role ZR_CHANGEAPPROVAL {
grant select on ZR_CHANGEAPPROVAL;
}
@@ -0,0 +1,100 @@
/* TARGET OBJECT : data definition ZR_CHANGEAPPROVAL (root view entity) - README step U5, UI leg
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZR_CHANGEAPPROVAL/zr_changeapproval.ddls.acds
ACTION : create empty via ADT MCP (objectType DDLS/DF, README U5), replace entire content, save, then activate
(table ZCHANGEAPPROVAL 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 case distinction to a data element (abap_boolean, released) [source: abap-docs /abap-docs-cloud/ABENCDS_CAST_EXPRESSION_V2, verified 2026-09-16].
@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.
ABAP_BOOLEAN is released [source: sap-released-objects DTEL ABAP_BOOLEAN public_cloud level A, verified 2026-09-16].
Result types of the CASE expressions:
- A CASE result gets the maximum technical data type of all THEN and ELSE branches, and keeps a data element
only if every branch with that type carries the same one [source: abap-docs /abap-docs-cloud/ABENCDS_CASE_EXPRESSION_V2,
verified 2026-09-16]. StatusText is therefore CHAR as long as its longest literal (29), not Status's CHAR(10).
- IsApprovable / IsRetriable cast the WHOLE case distinction to abap_boolean once. Casting inside the branches does
not work: a cast to a data element passes on its semantics, "An exception to this is the use of the CAST expression
within a case distinction" [source: abap-docs /abap-docs-cloud/ABENCDS_CAST_EXPRESSION_V2, verified 2026-09-16].
- The branches use typed literals abap.char'X' / abap.char' ': length is "derived from the literal value" and "the type
length includes the trimmed trailing blanks", so both are CHAR(1) and the CASE result is CHAR(1) [source: abap-docs
/abap-docs-cloud/ABENCDS_TYPED_LITERAL_V2, verified 2026-09-16]. (Untyped 'X' would not prove it: its DB length is only
"at least as large as" the literal [source: abap-docs /abap-docs-cloud/ABENCDS_UNTYPED_LITERAL_V2, verified 2026-09-16].)
- PRESERVING TYPE needs the operand's built-in type and length to match the data element exactly [source: abap-docs
/abap-docs-cloud/ABENCDS_CAST_EXPRESSION_V2, verified 2026-09-16]. ABAP_BOOLEAN is "of data type CHAR of length 1"
[source: abap-docs /abap-docs-standard/ABENDATAOBJECTS_TRUE_VALUE_GUIDL, verified 2026-09-16], so the types match.
If activation rejects the cast anyway, delete
"preserving type": a plain cast still inherits the data element's semantics, and a same-type warning "can be ignored"
[source: abap-docs /abap-docs-cloud/ABENCDS_CAST_EXPRESSION_V2, verified 2026-09-16].
- An untyped integer literal is INT1, INT2, INT4 or INT8 depending on its value [source: abap-docs
/abap-docs-cloud/ABENCDS_UNTYPED_LITERAL_V2, verified 2026-09-16], so StatusCriticality (0..5) is expected INT1.
Open (UNVERIFIED 2026-09-16 - no doc found): the OData EDM types the UI binding emits for INT1 (app expects
Edm.Byte), ABAP_BOOLEAN (app expects Edm.Boolean) and UTCLONG (app expects Edm.DateTimeOffset Precision 7).
Read them from the live $metadata after publishing (README step U15); do not change this source without a cited rule. */
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Arena change approval'
define root view entity ZR_CHANGEAPPROVAL
as select from zchangeapproval
{
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,
cast( case status
when 'PENDING' then abap.char'X'
else abap.char' '
end as abap_boolean preserving type ) as IsApprovable,
cast( case status
when 'FAILED' then abap.char'X'
else abap.char' '
end as abap_boolean preserving type ) 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,6 @@
@EndUserText.label: 'Arena change approval - UI service'
define service ZUI_ARENA_CHANGE_APPROVAL
provider contracts odata_v4_ui
{
expose ZC_CHANGEAPPROVAL as ChangeApproval;
}
@@ -1,14 +0,0 @@
" 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.
@@ -1,426 +0,0 @@
" 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.
@@ -1,20 +0,0 @@
// 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;
}
@@ -1,12 +0,0 @@
/* 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;
}
@@ -1,76 +0,0 @@
/* 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
}
@@ -1,13 +0,0 @@
/* 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;
}
@@ -0,0 +1,593 @@
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" xmlns="http://docs.oasis-open.org/odata/ns/edm" Version="4.0">
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_AGGREGATION',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="Org.OData.Aggregation.V1" Alias="SAP__aggregation"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_ANALYTICS',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.Analytics.v1" Alias="SAP__analytics"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_AUTHORIZATION',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="Org.OData.Authorization.V1" Alias="SAP__authorization"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_CAPABILITIES',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="SAP__capabilities"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_CODELIST',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.CodeList.v1" Alias="SAP__CodeList"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_COMMON',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="SAP__common"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_COMMUNICATION',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.Communication.v1" Alias="SAP__communication"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_CORE',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="Org.OData.Core.V1" Alias="SAP__core"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_HIERARCHY',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.Hierarchy.v1" Alias="SAP__hierarchy"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_HTML5',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.HTML5.v1" Alias="SAP__HTML5"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_MEASURES',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="Org.OData.Measures.V1" Alias="SAP__measures"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_ODM',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.ODM.v1" Alias="SAP__ODM"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_PDF',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.PDF.v1" Alias="SAP__PDF"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_PERSONALDATA',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.PersonalData.v1" Alias="SAP__personalData"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_SESSION',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.Session.v1" Alias="SAP__session"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_SUPPORT',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.Support.v1" Alias="SAP__support"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_UI',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="com.sap.vocabularies.UI.v1" Alias="SAP__UI"/>
</edmx:Reference>
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_VALIDATION',Version='0001',SAP__Origin='LOCAL')/$value">
<edmx:Include Namespace="Org.OData.Validation.V1" Alias="SAP__validation"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="com.sap.gateway.srvd.zui_arena_change_approval.v0001" Alias="SAP__self">
<Annotation Term="SAP__core.SchemaVersion" String="1.0.0"/>
<EntityType Name="ChangeApprovalType">
<Key>
<PropertyRef Name="ID"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<Property Name="ItemNumber" Type="Edm.String" Nullable="false" MaxLength="18"/>
<Property Name="ItemName" Type="Edm.String" Nullable="false" MaxLength="40"/>
<Property Name="ChangeType" Type="Edm.String" Nullable="false" MaxLength="15"/>
<Property Name="ArenaEcoId" Type="Edm.String" Nullable="false" MaxLength="20"/>
<Property Name="Status" Type="Edm.String" Nullable="false" MaxLength="10"/>
<Property Name="StatusText" Type="Edm.String" Nullable="false" MaxLength="29"/>
<Property Name="StatusCriticality" Type="Edm.Byte" Nullable="false"/>
<Property Name="RequestedBy" Type="Edm.String" Nullable="false" MaxLength="12"/>
<Property Name="RequestedOn" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="DecidedBy" Type="Edm.String" Nullable="false" MaxLength="12"/>
<Property Name="DecidedOn" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="SapChangeNumber" Type="Edm.String" Nullable="false" MaxLength="12"/>
<Property Name="ErrorMessage" Type="Edm.String" Nullable="false" MaxLength="255"/>
<Property Name="RejectReason" Type="Edm.String" Nullable="false" MaxLength="255"/>
<Property Name="SapPreview" Type="Edm.String" Nullable="false"/>
<Property Name="IsApprovable" Type="Edm.Boolean" Nullable="false"/>
<Property Name="IsRetriable" Type="Edm.Boolean" Nullable="false"/>
<Property Name="LocalLastChangedAt" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="__OperationControl" Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalOperationControl"/>
<Property Name="SAP__Messages" Type="Collection(com.sap.gateway.srvd.zui_arena_change_approval.v0001.SAP__Message)" Nullable="false"/>
</EntityType>
<ComplexType Name="ChangeApprovalOperationControl">
<Property Name="approve" Type="Edm.Boolean" Nullable="false"/>
<Property Name="reject" Type="Edm.Boolean" Nullable="false"/>
<Property Name="retrySync" Type="Edm.Boolean" Nullable="false"/>
</ComplexType>
<ComplexType Name="SAP__Message">
<Property Name="code" Type="Edm.String" Nullable="false"/>
<Property Name="message" Type="Edm.String" Nullable="false"/>
<Property Name="target" Type="Edm.String"/>
<Property Name="additionalTargets" Type="Collection(Edm.String)" Nullable="false"/>
<Property Name="transition" Type="Edm.Boolean" Nullable="false"/>
<Property Name="numericSeverity" Type="Edm.Byte" Nullable="false"/>
<Property Name="longtextUrl" Type="Edm.String"/>
</ComplexType>
<Action Name="retrySync" EntitySetPath="_it" IsBound="true">
<Parameter Name="_it" Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType" Nullable="false"/>
<ReturnType Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType" Nullable="false"/>
</Action>
<Action Name="approve" EntitySetPath="_it" IsBound="true">
<Parameter Name="_it" Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType" Nullable="false"/>
<ReturnType Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType" Nullable="false"/>
</Action>
<Action Name="reject" EntitySetPath="_it" IsBound="true">
<Parameter Name="_it" Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType" Nullable="false"/>
<Parameter Name="RejectReason" Type="Edm.String" Nullable="false" MaxLength="255"/>
<ReturnType Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType" Nullable="false"/>
</Action>
<EntityContainer Name="Container">
<EntitySet Name="ChangeApproval" EntityType="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType"/>
</EntityContainer>
<Annotations Target="SAP__self.ChangeApprovalType/ID">
<Annotation Term="SAP__core.Computed"/>
<Annotation Term="SAP__UI.Hidden"/>
<Annotation Term="SAP__common.Label" String="UUID"/>
<Annotation Term="SAP__common.QuickInfo" String="16 Byte UUID in 16 Bytes (Raw Format)"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/ItemNumber">
<Annotation Term="SAP__common.Label" String="Item number"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/ItemName">
<Annotation Term="SAP__common.Label" String="Item name"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/ChangeType">
<Annotation Term="SAP__common.Label" String="Change type"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/ArenaEcoId">
<Annotation Term="SAP__common.Label" String="Arena ECO"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/Status">
<Annotation Term="SAP__common.Label" String="Status"/>
<Annotation Term="SAP__common.Text" Path="StatusText">
<Annotation Term="SAP__UI.TextArrangement" EnumMember="SAP__UI.TextArrangementType/TextOnly"/>
</Annotation>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/StatusText">
<Annotation Term="SAP__core.Computed"/>
<Annotation Term="SAP__UI.Hidden"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/StatusCriticality">
<Annotation Term="SAP__core.Computed"/>
<Annotation Term="SAP__UI.Hidden"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/RequestedBy">
<Annotation Term="SAP__common.Label" String="Requested by"/>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/RequestedOn">
<Annotation Term="SAP__common.Label" String="Requested on"/>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/DecidedBy">
<Annotation Term="SAP__common.Label" String="Decided by"/>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/DecidedOn">
<Annotation Term="SAP__common.Label" String="Decided on"/>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/SapChangeNumber">
<Annotation Term="SAP__common.Label" String="SAP change number"/>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/ErrorMessage">
<Annotation Term="SAP__common.Label" String="Error message"/>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/RejectReason">
<Annotation Term="SAP__common.Label" String="Reject reason"/>
<Annotation Term="SAP__core.Computed"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/SapPreview">
<Annotation Term="SAP__common.Label" String="Staged payload"/>
<Annotation Term="SAP__UI.MultiLineText"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/IsApprovable">
<Annotation Term="SAP__core.Computed"/>
<Annotation Term="SAP__UI.Hidden"/>
<Annotation Term="SAP__common.Label" String="Truth Value"/>
<Annotation Term="SAP__common.QuickInfo" String="Truth Value: True/False"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/IsRetriable">
<Annotation Term="SAP__core.Computed"/>
<Annotation Term="SAP__UI.Hidden"/>
<Annotation Term="SAP__common.Label" String="Truth Value"/>
<Annotation Term="SAP__common.QuickInfo" String="Truth Value: True/False"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/LocalLastChangedAt">
<Annotation Term="SAP__core.Computed"/>
<Annotation Term="SAP__common.Label" String="Changed On"/>
<Annotation Term="SAP__common.QuickInfo" String="Local Instance Last Change Date Time"/>
<Annotation Term="SAP__common.DocumentationRef" String="urn:sap-com:documentation:key?=type=DE&amp;id=ABP_LOCINST_LASTCHANGE_TSTMPL"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType/__OperationControl">
<Annotation Term="SAP__core.Computed"/>
<Annotation Term="SAP__UI.HiddenFilter"/>
<Annotation Term="SAP__UI.Hidden"/>
</Annotations>
<Annotations Target="SAP__self.retrySync(SAP__self.ChangeApprovalType)">
<Annotation Term="SAP__core.OperationAvailable" Path="_it/__OperationControl/retrySync"/>
</Annotations>
<Annotations Target="SAP__self.approve(SAP__self.ChangeApprovalType)">
<Annotation Term="SAP__core.OperationAvailable" Path="_it/__OperationControl/approve"/>
</Annotations>
<Annotations Target="SAP__self.reject(SAP__self.ChangeApprovalType)/RejectReason">
<Annotation Term="SAP__common.Label" String="Reject reason"/>
</Annotations>
<Annotations Target="SAP__self.reject(SAP__self.ChangeApprovalType)">
<Annotation Term="SAP__core.OperationAvailable" Path="_it/__OperationControl/reject"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalType">
<Annotation Term="SAP__common.Label" String="Arena change approval - UI projection"/>
<Annotation Term="SAP__common.SemanticKey">
<Collection>
<PropertyPath>ArenaEcoId</PropertyPath>
<PropertyPath>ChangeType</PropertyPath>
<PropertyPath>ItemNumber</PropertyPath>
</Collection>
</Annotation>
<Annotation Term="SAP__UI.DataPoint" Qualifier="StatusPoint">
<Record>
<PropertyValue Property="Value" Path="Status"/>
<PropertyValue Property="Title" String="Status"/>
<PropertyValue Property="Criticality" Path="StatusCriticality"/>
</Record>
</Annotation>
<Annotation Term="SAP__UI.HeaderFacets">
<Collection>
<Record Type="SAP__UI.ReferenceFacet">
<PropertyValue Property="Label" String="Reference numbers"/>
<PropertyValue Property="ID" String="Keys"/>
<PropertyValue Property="Target" AnnotationPath="@SAP__UI.FieldGroup#Keys"/>
</Record>
<Record Type="SAP__UI.ReferenceFacet">
<PropertyValue Property="ID" String="StatusPoint"/>
<PropertyValue Property="Target" AnnotationPath="@SAP__UI.DataPoint#StatusPoint"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="SAP__UI.Facets">
<Collection>
<Record Type="SAP__UI.ReferenceFacet">
<PropertyValue Property="Label" String="Change"/>
<PropertyValue Property="ID" String="Change"/>
<PropertyValue Property="Target" AnnotationPath="@SAP__UI.FieldGroup#Change"/>
</Record>
<Record Type="SAP__UI.ReferenceFacet">
<PropertyValue Property="Label" String="Staged payload"/>
<PropertyValue Property="ID" String="Payload"/>
<PropertyValue Property="Target" AnnotationPath="@SAP__UI.FieldGroup#Payload"/>
</Record>
<Record Type="SAP__UI.ReferenceFacet">
<PropertyValue Property="Label" String="Approval"/>
<PropertyValue Property="ID" String="Approval"/>
<PropertyValue Property="Target" AnnotationPath="@SAP__UI.FieldGroup#Approval"/>
</Record>
<Record Type="SAP__UI.ReferenceFacet">
<PropertyValue Property="Label" String="Execution"/>
<PropertyValue Property="ID" String="Execution"/>
<PropertyValue Property="Target" AnnotationPath="@SAP__UI.FieldGroup#Execution"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="SAP__UI.FieldGroup" Qualifier="Approval">
<Record>
<PropertyValue Property="Data">
<Collection>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Criticality" Path="StatusCriticality"/>
<PropertyValue Property="Value" Path="Status"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="RequestedBy"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="RequestedOn"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="DecidedBy"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="DecidedOn"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="RejectReason"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__UI.FieldGroup" Qualifier="Change">
<Record>
<PropertyValue Property="Data">
<Collection>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ItemNumber"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ItemName"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ChangeType"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ArenaEcoId"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__UI.FieldGroup" Qualifier="Execution">
<Record>
<PropertyValue Property="Data">
<Collection>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="SapChangeNumber"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ErrorMessage"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__UI.FieldGroup" Qualifier="Keys">
<Record>
<PropertyValue Property="Data">
<Collection>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ArenaEcoId"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="SapChangeNumber"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__UI.FieldGroup" Qualifier="Payload">
<Record>
<PropertyValue Property="Data">
<Collection>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="SapPreview"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__UI.HeaderInfo">
<Record>
<PropertyValue Property="TypeName" String="Change Approval"/>
<PropertyValue Property="TypeNamePlural" String="Change Approvals"/>
<PropertyValue Property="TypeImageUrl" String="sap-icon://approvals"/>
<PropertyValue Property="Title">
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ItemNumber"/>
</Record>
</PropertyValue>
<PropertyValue Property="Description">
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ItemName"/>
</Record>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__UI.Identification">
<Collection>
<Record Type="SAP__UI.DataFieldForAction">
<PropertyValue Property="Label" String="Approve"/>
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval.v0001.approve(com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType)"/>
<PropertyValue Property="InvocationGrouping" EnumMember="SAP__UI.OperationGroupingType/Isolated"/>
</Record>
<Record Type="SAP__UI.DataFieldForAction">
<PropertyValue Property="Label" String="Reject"/>
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval.v0001.reject(com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType)"/>
<PropertyValue Property="InvocationGrouping" EnumMember="SAP__UI.OperationGroupingType/Isolated"/>
</Record>
<Record Type="SAP__UI.DataFieldForAction">
<PropertyValue Property="Label" String="Retry sync"/>
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval.v0001.retrySync(com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType)"/>
<PropertyValue Property="InvocationGrouping" EnumMember="SAP__UI.OperationGroupingType/Isolated"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="SAP__UI.LineItem">
<Collection>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ItemNumber"/>
<Annotation Term="SAP__UI.Importance" EnumMember="SAP__UI.ImportanceType/High"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ItemName"/>
<Annotation Term="SAP__UI.Importance" EnumMember="SAP__UI.ImportanceType/Low"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ChangeType"/>
<Annotation Term="SAP__UI.Importance" EnumMember="SAP__UI.ImportanceType/High"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Criticality" Path="StatusCriticality"/>
<PropertyValue Property="Value" Path="Status"/>
<Annotation Term="SAP__UI.Importance" EnumMember="SAP__UI.ImportanceType/High"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="RequestedOn"/>
<Annotation Term="SAP__UI.Importance" EnumMember="SAP__UI.ImportanceType/Medium"/>
</Record>
<Record Type="SAP__UI.DataField">
<PropertyValue Property="Value" Path="ArenaEcoId"/>
<Annotation Term="SAP__UI.Importance" EnumMember="SAP__UI.ImportanceType/Medium"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="SAP__UI.PresentationVariant">
<Record>
<PropertyValue Property="SortOrder">
<Collection>
<Record Type="SAP__common.SortOrderType">
<PropertyValue Property="Property" PropertyPath="Status"/>
<PropertyValue Property="Descending" Bool="false"/>
</Record>
<Record Type="SAP__common.SortOrderType">
<PropertyValue Property="Property" PropertyPath="RequestedOn"/>
<PropertyValue Property="Descending" Bool="true"/>
</Record>
</Collection>
</PropertyValue>
<PropertyValue Property="Visualizations">
<Collection>
<AnnotationPath>@SAP__UI.LineItem</AnnotationPath>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__UI.SelectionFields">
<Collection>
<PropertyPath>ItemNumber</PropertyPath>
<PropertyPath>Status</PropertyPath>
<PropertyPath>ChangeType</PropertyPath>
<PropertyPath>ArenaEcoId</PropertyPath>
</Collection>
</Annotation>
<Annotation Term="SAP__common.Messages" Path="SAP__Messages"/>
</Annotations>
<Annotations Target="SAP__self.Container/ChangeApproval">
<Annotation Term="SAP__capabilities.SearchRestrictions">
<Record>
<PropertyValue Property="Searchable" Bool="false"/>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.FilterRestrictions">
<Record>
<PropertyValue Property="Filterable" Bool="true"/>
<PropertyValue Property="FilterExpressionRestrictions">
<Collection>
<Record>
<PropertyValue Property="Property" PropertyPath="SapPreview"/>
<PropertyValue Property="AllowedExpressions" String="SearchExpression"/>
</Record>
</Collection>
</PropertyValue>
<PropertyValue Property="NonFilterableProperties">
<Collection>
<PropertyPath>__OperationControl</PropertyPath>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.SortRestrictions">
<Record>
<PropertyValue Property="NonSortableProperties">
<Collection>
<PropertyPath>SapPreview</PropertyPath>
<PropertyPath>__OperationControl</PropertyPath>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.InsertRestrictions">
<Record>
<PropertyValue Property="Insertable" Bool="false"/>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.DeleteRestrictions">
<Record>
<PropertyValue Property="Deletable" Bool="false"/>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.UpdateRestrictions">
<Record>
<PropertyValue Property="Updatable" Bool="false"/>
<PropertyValue Property="QueryOptions">
<Record>
<PropertyValue Property="SelectSupported" Bool="true"/>
</Record>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__core.OptimisticConcurrency">
<Collection>
<PropertyPath>LocalLastChangedAt</PropertyPath>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="SAP__self.Container">
<Annotation Term="SAP__common.ApplyMultiUnitBehaviorForSortingAndFiltering" Bool="true"/>
<Annotation Term="SAP__capabilities.FilterFunctions">
<Collection>
<String>eq</String>
<String>ne</String>
<String>gt</String>
<String>ge</String>
<String>lt</String>
<String>le</String>
<String>and</String>
<String>or</String>
<String>contains</String>
<String>startswith</String>
<String>endswith</String>
<String>any</String>
<String>all</String>
</Collection>
</Annotation>
<Annotation Term="SAP__support.TechnicalInfoLinks">
<Record>
<PropertyValue Property="Url" String="../../../../default/iwbep/common/0001/$metadata"/>
<PropertyValue Property="FunctionImport" String="GetTechnicalInfoLinks"/>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.SupportedFormats">
<Collection>
<String>application/json</String>
<String>application/pdf</String>
</Collection>
</Annotation>
<Annotation Term="SAP__PDF.Features">
<Record>
<PropertyValue Property="DocumentDescriptionReference" String="../../../../default/iwbep/common/0001/$metadata"/>
<PropertyValue Property="DocumentDescriptionCollection" String="MyDocumentDescriptions"/>
<PropertyValue Property="ArchiveFormat" Bool="true"/>
<PropertyValue Property="Border" Bool="true"/>
<PropertyValue Property="CoverPage" Bool="true"/>
<PropertyValue Property="FitToPage" Bool="true"/>
<PropertyValue Property="FontName" Bool="true"/>
<PropertyValue Property="FontSize" Bool="true"/>
<PropertyValue Property="HeaderFooter" Bool="true"/>
<PropertyValue Property="IANATimezoneFormat" Bool="true"/>
<PropertyValue Property="Margin" Bool="true"/>
<PropertyValue Property="Padding" Bool="true"/>
<PropertyValue Property="ResultSizeDefault" Int="20000"/>
<PropertyValue Property="ResultSizeMaximum" Int="20000"/>
<PropertyValue Property="Signature" Bool="true"/>
<PropertyValue Property="TextDirectionLayout" Bool="true"/>
<PropertyValue Property="Treeview" Bool="true"/>
<PropertyValue Property="UploadToFileShare" Bool="true"/>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.BatchSupport">
<Record>
<PropertyValue Property="SupportedFormats">
<Collection>
<String>application/json</String>
<String>multipart/mixed</String>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="SAP__capabilities.KeyAsSegmentSupported"/>
<Annotation Term="SAP__capabilities.AsynchronousRequestsSupported"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalOperationControl/approve">
<Annotation Term="SAP__common.Label" String="Dyn. Action Control"/>
<Annotation Term="SAP__common.Heading" String="Dynamic Action Control"/>
<Annotation Term="SAP__common.QuickInfo" String="Dynamic Action Property"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalOperationControl/reject">
<Annotation Term="SAP__common.Label" String="Dyn. Action Control"/>
<Annotation Term="SAP__common.Heading" String="Dynamic Action Control"/>
<Annotation Term="SAP__common.QuickInfo" String="Dynamic Action Property"/>
</Annotations>
<Annotations Target="SAP__self.ChangeApprovalOperationControl/retrySync">
<Annotation Term="SAP__common.Label" String="Dyn. Action Control"/>
<Annotation Term="SAP__common.Heading" String="Dynamic Action Control"/>
<Annotation Term="SAP__common.QuickInfo" String="Dynamic Action Property"/>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>