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:
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"name": "0M7DEJI(ABAP)",
|
|
||||||
"uri": "adt://0m7deji"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+699
@@ -2,6 +2,705 @@
|
|||||||
|
|
||||||
Append-only decision log for this package.
|
Append-only decision log for this package.
|
||||||
|
|
||||||
|
## 2026-09-18 — Reentrance-ticket login for `abap-adt-mcp`: built, tested, rejected
|
||||||
|
|
||||||
|
- What changed: I built a login helper at `tools/adt-reentrance-login/`
|
||||||
|
(Node, no dependencies): a one-shot loopback listener that captures an
|
||||||
|
ADT reentrance ticket from the `redirect-url=http://localhost:<port>/adt/redirect`
|
||||||
|
flow, writes it to a 0600 file, and probes how the tenant accepts it. It
|
||||||
|
never prints, logs or passes the ticket through argv or the environment,
|
||||||
|
and it parses the raw query itself, because `URLSearchParams` turns a
|
||||||
|
`+` in a ticket into a space and would corrupt it. Then I captured a real
|
||||||
|
ticket and tested it: `/sap/bc/adt/core/discovery` and an ABAP class
|
||||||
|
source URL answered 200 with the same 3094-byte SAML redirect page for
|
||||||
|
cookie transport, header transport and no credential, with and without
|
||||||
|
`sap-client=080`.
|
||||||
|
- Why: `abap-adt-mcp`'s browser SSO needs a window on the server's desktop,
|
||||||
|
which is unreachable over this remote session (OQ-9). ADT for VS Code
|
||||||
|
logs on with a reentrance ticket instead, so the question was whether
|
||||||
|
that ticket can feed the server's `sso2` (MYSAPSSO2 cookie) mode.
|
||||||
|
- Affected: new `tools/adt-reentrance-login/`, `~/.abap-adt-mcp/systems.json`
|
||||||
|
(switched to `sso2`, then restored to `sso`), KNOWLEDGE.md, PRD OQ-9.
|
||||||
|
- Decision: the approach is rejected on evidence. A reentrance ticket is
|
||||||
|
neither a logon-ticket cookie nor an assertion-ticket header on this
|
||||||
|
tenant, so `sso2` cannot consume it. I restored the browser-login
|
||||||
|
configuration, shredded the captured ticket, and kept the helper: it is
|
||||||
|
the only piece that would still be needed if the exchange mechanism is
|
||||||
|
ever identified. The next step, if this is revisited, is to capture what
|
||||||
|
ADT for VS Code actually sends, not to guess header names. Tenant work
|
||||||
|
continues on SAP's ADT MCP with editor paste, which is proven.
|
||||||
|
|
||||||
|
## 2026-09-16 — Worker leg active: approval BO complete on the development tenant
|
||||||
|
|
||||||
|
- What changed:
|
||||||
|
- I activated W3 (A2X projection BDEF) and W4 (service definition
|
||||||
|
`ZAPI_ARENA_CHANGE_APPROVAL`).
|
||||||
|
- I created and activated W5, the OData V4 Web API binding
|
||||||
|
`ZAPI_ARENA_CHANGE_APPR_A2X`, with binding type `OData V4 - Web API`
|
||||||
|
(`create_object` returned `.srvb.json`).
|
||||||
|
- `fetch_services`: service `ZAPI_ARENA_CHANGE_APPROVAL`, version `0001`,
|
||||||
|
V4, `isPublished: false`, `bindingCategory: "1"`.
|
||||||
|
- ATC over the five worker objects (worklist
|
||||||
|
`12ACF6BD93811FE1ACBA776536DD5BD6`): 0 errors and one priority-3
|
||||||
|
warning, W607 (+BDL 549) on BDEF `ZC_CHANGEAPPROVAL_A2X`: "CDS annotation
|
||||||
|
#CHECK … should be replaced with #MANDATORY".
|
||||||
|
|
||||||
|
[source: adt-mcp abap_activate_objects, abap_creation-create_object,
|
||||||
|
abap_atc_get_result, abap_business_services-fetch_services on 0M7DEJI,
|
||||||
|
verified 2026-09-16]
|
||||||
|
- Why: RAP-6. The worker needs create and the three execution-state
|
||||||
|
actions through a Web API, separate from the read-only UI.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM` (all 19 approval-BO objects now
|
||||||
|
active), PRD RAP BO status, KNOWLEDGE.md.
|
||||||
|
- Decision: W5 stays unpublished. It is exposed to the worker's technical
|
||||||
|
user through a custom communication scenario (V-2 A2X half), not through
|
||||||
|
business-user access. I do not switch
|
||||||
|
`@AccessControl.authorizationCheck` from `#CHECK` to `#MANDATORY` now:
|
||||||
|
the warning does not block activation, and the change belongs with the
|
||||||
|
OQ-8 authorization work, which revisits all three projection and root
|
||||||
|
views together (the same annotation is in `ZR_CHANGEAPPROVAL` and
|
||||||
|
`ZC_CHANGEAPPROVAL`).
|
||||||
|
|
||||||
|
## 2026-09-16 — Worker leg: A2X projection view and access control active; A2X BDEF created
|
||||||
|
|
||||||
|
- What changed: I activated W1 (`ZC_CHANGEAPPROVAL_A2X` projection view)
|
||||||
|
and W2 (its access control); both answered "Activation successful."
|
||||||
|
I then created W3, the projection BDEF `ZC_CHANGEAPPROVAL_A2X`, empty
|
||||||
|
(`.bdef.abdl`), after W1 was active [source: adt-mcp
|
||||||
|
abap_activate_objects, abap_creation-create_object on 0M7DEJI, verified
|
||||||
|
2026-09-16]. W4 (service definition) is saved but not active.
|
||||||
|
- Why: worker leg round 2 of the approval BO.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status.
|
||||||
|
- Decision: W4 activates only after W3 is active. Then W5, the Web API
|
||||||
|
binding, is created and verified unpublished.
|
||||||
|
|
||||||
|
## 2026-09-16 — Worker leg round 1 created; handover brought in line with live results
|
||||||
|
|
||||||
|
- What changed: after the expired VS Code ADT logon was restored, I
|
||||||
|
created W1 (`ZC_CHANGEAPPROVAL_A2X` view), W2 (its access control, with
|
||||||
|
`protectedEntity`) and W4 (service definition
|
||||||
|
`ZAPI_ARENA_CHANGE_APPROVAL`) empty in `ZARENA_PLM` [source: adt-mcp
|
||||||
|
create_object on 0M7DEJI, verified 2026-09-16]. I also corrected
|
||||||
|
`docs/handover/rap/`: both service definition sources are comment-free,
|
||||||
|
the README records the UI leg as active, ATC-clean and published, the
|
||||||
|
activation-URI table marks the proven extensions (`.tabl.ddic`,
|
||||||
|
`.bdef.abdl`, `.srvb.json`), and the worker-leg sequence is rewritten to
|
||||||
|
two paste rounds. All 31 existing citations were preserved.
|
||||||
|
- Why: worker (A2X) leg of the approval BO (RAP-6). The handover must not
|
||||||
|
repeat today's failed activations.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, `docs/handover/rap/`, KNOWLEDGE.md.
|
||||||
|
- Decision: pastes are opened manually from the ABAP workspace tree. I only
|
||||||
|
give the local source path and the `abap:/repotree-v1/…` target, because
|
||||||
|
editor tabs opened by URI cannot save. W5 uses binding type `OData V4 -
|
||||||
|
Web API`, the exact value the create metadata offers. W5 stays
|
||||||
|
unpublished until the custom communication scenario brief (V-2).
|
||||||
|
|
||||||
|
## 2026-09-16 — Fiori app bound to the live RAP service identity (V-2 UI half)
|
||||||
|
|
||||||
|
- What changed: I reconciled `app/` with the published service.
|
||||||
|
- Manifest dataSource and mock-server URL paths now use
|
||||||
|
`/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/`.
|
||||||
|
- The mock `metadata.xml` is the live `$metadata`, byte-identical (sha256
|
||||||
|
`c13e86e1…`, checked).
|
||||||
|
- Mock rows carry `StatusText`, `LocalLastChangedAt`,
|
||||||
|
`__OperationControl` and `SAP__Messages`.
|
||||||
|
- All annotation targets and action strings use namespace
|
||||||
|
`com.sap.gateway.srvd.zui_arena_change_approval.v0001` and entity type
|
||||||
|
`ChangeApprovalType`.
|
||||||
|
|
||||||
|
A grep for the old identity over `app/` (excluding `node_modules`/`dist`)
|
||||||
|
returns nothing. Lint, typecheck and `@ui5/linter` are clean. The mock
|
||||||
|
server answers `$metadata` and `ChangeApproval` with `$filter`, `$count`
|
||||||
|
and `__OperationControl` selects.
|
||||||
|
- Why: V-2 and ARN-007. The app must address the service that exists.
|
||||||
|
- Affected: `app/` (manifest, `ui5-mock.yaml`, `ui5-local.yaml`,
|
||||||
|
`annotation.xml`, mock metadata and data, README), PRD V-2, new OQ-10,
|
||||||
|
KNOWLEDGE.md.
|
||||||
|
- Decision:
|
||||||
|
- (1) Action enablement belongs to the backend. The mock's local
|
||||||
|
`OperationAvailable` overrides went away with the mock metadata, and
|
||||||
|
`annotation.xml` deliberately defines none, because a local one would
|
||||||
|
replace RAP's `_it/__OperationControl/<action>` [source:
|
||||||
|
fiori-mcp-server "Action Control for Context-Dependent Actions",
|
||||||
|
verified 2026-09-16].
|
||||||
|
- (2) No real backend is configured in the UI5 tooling yet. Any live
|
||||||
|
preview targets the UI host, never `-api`. Its authentication type and
|
||||||
|
client are verified before use, not assumed.
|
||||||
|
- (3) UI annotation ownership is opened as OQ-10 rather than moved now:
|
||||||
|
consolidating would drop i18n labels and action criticality and break
|
||||||
|
the OPA section IDs mid-POC.
|
||||||
|
- (4) `dist/` is stale and must be rebuilt before any deployment.
|
||||||
|
|
||||||
|
## 2026-09-16 — Live `$metadata` captured: backend contract matches the app; identity names differ
|
||||||
|
|
||||||
|
- What changed: the published UI service's `$metadata` is captured in
|
||||||
|
`docs/v2/zui_arena_change_appr_o4.metadata.xml`. It was copied from the
|
||||||
|
browser on the UI host `my431353.s4hana.cloud.sap`; browser-decoded `&`
|
||||||
|
characters were re-escaped. The `-api` host refused the same request to a
|
||||||
|
business user ("blocked by UCON").
|
||||||
|
- Matches the app: all 17 app properties with identical EDM types
|
||||||
|
(`Edm.Boolean` flags, `Edm.Byte` criticality, `Edm.DateTimeOffset`
|
||||||
|
Precision 7), key `ID`, entity set `ChangeApproval`, and the actions
|
||||||
|
`approve`, `reject(RejectReason)` (non-nullable) and `retrySync`.
|
||||||
|
- Differs from the mock: namespace
|
||||||
|
`com.sap.gateway.srvd.zui_arena_change_approval.v0001` (from the service
|
||||||
|
definition), entity type `ChangeApprovalType`, URL segment
|
||||||
|
`zui_arena_change_appr_o4`, extra `StatusText` / `LocalLastChangedAt` /
|
||||||
|
`__OperationControl` / `SAP__Messages`, and backend-driven
|
||||||
|
`Core.OperationAvailable`.
|
||||||
|
|
||||||
|
I dispatched the app reconciliation.
|
||||||
|
- Why: V-2. The app was built against a provisional mock identity, and only
|
||||||
|
the live service can settle it.
|
||||||
|
- Affected: PRD V-2, `app/` (in progress), KNOWLEDGE.md.
|
||||||
|
- Decision: the live `$metadata` replaces the mock metadata and becomes the
|
||||||
|
app's contract. Button enablement comes from the backend's
|
||||||
|
`__OperationControl` rather than local `IsApprovable`/`IsRetriable`
|
||||||
|
overrides, unless the Fiori docs show that breaks something. Any
|
||||||
|
non-mock backend configuration points at the UI host, never `-api`.
|
||||||
|
Earlier notes claiming the binding name sets the namespace are superseded
|
||||||
|
(KNOWLEDGE 2026-09-16).
|
||||||
|
|
||||||
|
## 2026-09-16 — Approval UI service published on the development tenant
|
||||||
|
|
||||||
|
- What changed: the OData V4 UI binding `ZUI_ARENA_CHANGE_APPR_O4` is
|
||||||
|
published. The ADT for VS Code Publish code lens gave no visible
|
||||||
|
confirmation, so I verified it live: `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].
|
||||||
|
- Why: RAP-5. The Fiori app cannot bind to an unpublished service.
|
||||||
|
- Affected: PRD RAP BO status, V-2, KNOWLEDGE.md.
|
||||||
|
- Decision: the URL path is exactly the designed one, so no binding change
|
||||||
|
follows. The app's V-2 reconciliation (dataSource first segment,
|
||||||
|
namespace, annotation targets, EDM types) is done against the live
|
||||||
|
`$metadata`, never inferred. The service reports client 080, so the
|
||||||
|
client value in any tool configuration comes from the live system.
|
||||||
|
|
||||||
|
## 2026-09-16 — UI leg active: approval BO and OData V4 UI service built on `my431353`
|
||||||
|
|
||||||
|
- What changed:
|
||||||
|
- U13, the service definition `ZUI_ARENA_CHANGE_APPROVAL`, activated after
|
||||||
|
I re-saved it without its header comment. The same definition failed
|
||||||
|
with the comment and succeeded without it, so the comment was the
|
||||||
|
blocker; the missing-draft message is not.
|
||||||
|
- I created U14, the OData V4 UI binding `ZUI_ARENA_CHANGE_APPR_O4`
|
||||||
|
(`create_object` returned `.srvb.json`), and activated it.
|
||||||
|
- ATC over all 14 UI-leg objects: 0 errors, 0 warnings, 43 infos
|
||||||
|
(worklist `12ACF6BD93811FE1ACB8096AC6763BD6`).
|
||||||
|
- `fetch_services`: service `ZUI_ARENA_CHANGE_APPROVAL`, version `0001`,
|
||||||
|
OData V4, unpublished.
|
||||||
|
|
||||||
|
[source: adt-mcp abap_activate_objects, abap_creation-create_object,
|
||||||
|
abap_atc_get_result, abap_business_services-fetch_services on 0M7DEJI,
|
||||||
|
verified 2026-09-16]
|
||||||
|
- Why: RAP-1..8 and the ARN-007 acceptance path. The service must exist
|
||||||
|
before the app can bind to it.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status, KNOWLEDGE.md,
|
||||||
|
handover README (both SRVD sources still carry header comments, and U9
|
||||||
|
still says `.bdef.json`).
|
||||||
|
- Decision: the service alias equals the definition name and the version
|
||||||
|
is `0001`. Publishing therefore yields
|
||||||
|
`/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/`,
|
||||||
|
as designed, so no rename is needed before publishing. The 43 ATC infos
|
||||||
|
are reviewed in the ADT ATC view (the MCP returns only totals); infos do
|
||||||
|
not gate publishing. I correct the handover README in one pass before
|
||||||
|
the worker leg: comment-free SRVD sources and BDEF `.bdef.abdl` URIs.
|
||||||
|
|
||||||
|
## 2026-09-16 — Wave D: projection BDEF, access control and metadata extension active; service definition refused
|
||||||
|
|
||||||
|
- What changed: I activated U9 (projection BDEF `ZC_CHANGEAPPROVAL`), U11
|
||||||
|
(its access control) and U12 (its metadata extension); each answered
|
||||||
|
"Activation successful." Activating U13, the service definition
|
||||||
|
`ZUI_ARENA_CHANGE_APPROVAL`, answered "Activation failed" with two
|
||||||
|
messages and no severity:
|
||||||
|
- "Comments are not supported and will be deleted on save" (Ln 1, the
|
||||||
|
handover header block comment)
|
||||||
|
- "oData V4 services with no draft capability will be primarily
|
||||||
|
READ-ONLY" (Ln 21). The BO has no draft, by design.
|
||||||
|
|
||||||
|
CDS SDL allows comments as a language rule [source: abap-docs
|
||||||
|
/abap-docs-cloud/ABENCDS_F1_SDL_SYNTAX, ABENCDS_GENERAL_SYNTAX_RULES,
|
||||||
|
verified 2026-09-16], so the refusal comes from the service definition
|
||||||
|
tooling, not the language. U14 (the binding) was not created.
|
||||||
|
- Why: wave D of the UI leg.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status, KNOWLEDGE.md.
|
||||||
|
- Decision: I re-save U13 with only its definition (annotation, `define
|
||||||
|
service`, `expose`) and no header comment, then activate again. If it
|
||||||
|
still fails with only the draft message, that message is blocking, and I
|
||||||
|
read its severity in the editor's Problems view before changing anything
|
||||||
|
about draft.
|
||||||
|
|
||||||
|
## 2026-09-16 — `abap-adt-mcp` parked: SSO cannot complete over remote access
|
||||||
|
|
||||||
|
- What changed: with `SAP_BROWSER_PATH` set, the SSO login launched
|
||||||
|
Chrome with a dedicated profile on the server's desktop display and
|
||||||
|
waited on the Microsoft Entra sign-in page. The login does not use a
|
||||||
|
localhost redirect: SAML returns to the tenant, and the server harvests
|
||||||
|
the session cookies from its own Chrome over the DevTools protocol
|
||||||
|
(`dist/lib/browserLogin.js`). I tried a localhost-only DevTools relay on
|
||||||
|
a fixed port for remote interaction. It is not workable through
|
||||||
|
multi-hop SSH proxies, so I stopped it and closed the login browser. No
|
||||||
|
login happened. The server remains registered, read-only.
|
||||||
|
- Why: the sign-in has to happen inside the server's browser, and that
|
||||||
|
browser is on a desktop I cannot reach from this remote session.
|
||||||
|
- Affected: PRD OQ-9, KNOWLEDGE.md.
|
||||||
|
- Decision: I park `abap-adt-mcp` rather than remove it. One sign-in at
|
||||||
|
the server's desktop, with "stay signed in", should let later logins run
|
||||||
|
unattended in the invisible browser; that is unverified. The UI leg
|
||||||
|
finishes on the proven path: SAP `adt-mcp` for create and activate,
|
||||||
|
source pasted in the VS Code editor. Four wave-D sources, one binding and
|
||||||
|
a publish remain.
|
||||||
|
|
||||||
|
## 2026-09-16 — `abap-adt-mcp` connected; SSO blocked on browser detection
|
||||||
|
|
||||||
|
- What changed: after the restart, `abap-adt-mcp` connected. `listSystems`
|
||||||
|
showed exactly one destination, `MY431353` (client 100, SSO), with policy
|
||||||
|
`readOnly: true` and `allowedPackages: ["ZARENA_PLM"]`. The first SAP call
|
||||||
|
(`classIncludes` on `ZBP_R_CHANGEAPPROVAL`) failed before any login: "No
|
||||||
|
Chrome/Edge/Brave found for SSO login." The server's browser detection
|
||||||
|
covers only macOS paths (KNOWLEDGE 2026-09-16).
|
||||||
|
- Why: SSO login is the gate to the read-only Local Types check.
|
||||||
|
- Affected: project MCP registration (env), KNOWLEDGE.md.
|
||||||
|
- Decision: the registration gains `SAP_BROWSER_PATH=/usr/bin/google-chrome`
|
||||||
|
and nothing else changes. This is a manual step, like the original
|
||||||
|
registration.
|
||||||
|
|
||||||
|
## 2026-09-16 — `abap-adt-mcp` registered read-only in project scope
|
||||||
|
|
||||||
|
- What changed: `abap-adt-mcp` is registered in the project `.mcp.json`
|
||||||
|
(git-ignored, mode 600). It runs over stdio as `npx -y
|
||||||
|
abap-adt-mcp@2.1.0` with `MCP_READ_ONLY=1`, an audit log at
|
||||||
|
`~/.abap-adt-mcp/audit.jsonl`, and the desktop display variables for the
|
||||||
|
SSO browser. Its systems file lists only the development tenant
|
||||||
|
`my431353`, client 100, SSO, with `policy.readOnly: true` and
|
||||||
|
`allowedPackages: ["ZARENA_PLM"]`. The six existing project servers are
|
||||||
|
unchanged, `adt-mcp` included.
|
||||||
|
- Why: OQ-9. Agent-side source read and write would replace manual pasting
|
||||||
|
for the remaining RAP objects.
|
||||||
|
- Affected: project MCP configuration, PRD OQ-9.
|
||||||
|
- Decision: the first use is a read-only `getObjectSource` of the behavior
|
||||||
|
pool's Local Types include. That check settles whether wave C's
|
||||||
|
activation proved the handler constructs. Write access is enabled only
|
||||||
|
after that read succeeds. The CLAUDE.md MCP routing table changes only
|
||||||
|
once the server has written and activated an object successfully.
|
||||||
|
|
||||||
|
## 2026-09-16 — `abap-adt-mcp` source review: all checks cleared; written report not produced
|
||||||
|
|
||||||
|
- What changed: the read-only source review of `abap-adt-mcp` 2.1.0 cleared
|
||||||
|
every check it ran (install scripts, network egress, credential and
|
||||||
|
session storage, policy enforcement, dangerous primitives), observed as it
|
||||||
|
ran. It then stalled while collecting line citations, consumed its budget,
|
||||||
|
and was stopped at ~03:28, so no written report with file:line evidence
|
||||||
|
exists. As of 03:30 the package is not registered, no `~/.abap-adt-mcp`
|
||||||
|
configuration exists, and no login has happened. The tenant state is
|
||||||
|
unchanged: waves A–C are active, and wave D (U9, U11, U12, U13) exists
|
||||||
|
empty.
|
||||||
|
- Why: OQ-9 was gated on this review.
|
||||||
|
- Affected: PRD OQ-9.
|
||||||
|
- Decision: I accept the review for the development tenant only, under the
|
||||||
|
read-only-first configuration (pinned 2.1.0, `readOnly: true`,
|
||||||
|
`allowedPackages: ["ZARENA_PLM"]`, `MCP_READ_ONLY=1`, audit file on).
|
||||||
|
Registration is run by hand. The missing citations mean this clearance
|
||||||
|
does not extend to the productive tenant. Any future review brief is
|
||||||
|
scoped to named files with a size budget, because this one ran unbounded.
|
||||||
|
|
||||||
|
## 2026-09-16 — `abap-adt-mcp` registration is a manual step; read-only first
|
||||||
|
|
||||||
|
- What changed: an attempt to write `~/.abap-adt-mcp/systems.json` and to
|
||||||
|
register `abap-adt-mcp` in the project MCP configuration was refused by
|
||||||
|
Claude Code's permission guard as self-modification. Nothing was written.
|
||||||
|
I confirmed the development tenant logs in on client 100. The package's
|
||||||
|
SSO login launches Chrome visibly (`headless: false`), so the MCP server
|
||||||
|
needs the desktop session's display variables.
|
||||||
|
- Why: I want agent-side source writes (OQ-9), and registration is the
|
||||||
|
gate.
|
||||||
|
- Affected: PRD OQ-9, project `.mcp.json` (only once registered by hand),
|
||||||
|
CLAUDE.md MCP routing (only once adopted).
|
||||||
|
- Decision: the registration is run by hand, not routed around the guard.
|
||||||
|
The first configuration is read-only (`policy.readOnly: true`,
|
||||||
|
`allowedPackages: ["ZARENA_PLM"]`, `MCP_READ_ONLY=1`), limited to the
|
||||||
|
development tenant, and pinned to 2.1.0. The first login waits for the
|
||||||
|
source review verdict. Writes are enabled only after a read-only
|
||||||
|
`getObjectSource` on the behavior pool's Local Types include succeeds.
|
||||||
|
|
||||||
|
## 2026-09-16 — Community `abap-adt-mcp` smoke-tested read-only; not adopted yet (OQ-9)
|
||||||
|
|
||||||
|
- What changed: I ran `abap-adt-mcp` 2.1.0 once through `npx` over stdio.
|
||||||
|
It ran with `MCP_READ_ONLY=1` and a scratchpad systems file listing only
|
||||||
|
the development tenant, restricted to `ZARENA_PLM`. `initialize` and
|
||||||
|
`tools/list` answered: 173 tools, including source read/write and
|
||||||
|
activation. `listSystems` showed the read-only, package-restricted policy
|
||||||
|
enforced. There was no login and no SAP call. Nothing was registered in
|
||||||
|
any Claude Code or project configuration (verified 03:12). The package
|
||||||
|
remains in the local npx cache. I opened OQ-9.
|
||||||
|
- Why: SAP's ADT MCP cannot write source (V-4), so every RAP source is
|
||||||
|
pasted by hand. A server that can read and write source would remove that
|
||||||
|
step and would also let me read the behavior pool's Local Types include
|
||||||
|
directly.
|
||||||
|
- Affected: PRD OQ-9, V-4 (possible alternative), CLAUDE.md MCP routing (a
|
||||||
|
change only on adoption).
|
||||||
|
- Decision: I do not adopt it mid-build. Only four pastes and one publish
|
||||||
|
remain on the UI leg. The package was published the same day, by one
|
||||||
|
maintainer, and would hold a browser SSO session into the tenant, so it
|
||||||
|
gets a source review before any login. I run it read-only first. The
|
||||||
|
productive tenant `my429480` is never listed in its systems file.
|
||||||
|
|
||||||
|
## 2026-09-16 — Wave C active: behavior pool and UI projection view live; wave D created
|
||||||
|
|
||||||
|
- What changed: I activated U6 (base BDEF) and U7 (behavior pool
|
||||||
|
`ZBP_R_CHANGEAPPROVAL`) together in one call, then U8 (UI projection
|
||||||
|
view `ZC_CHANGEAPPROVAL`). All answered "Activation successful." The
|
||||||
|
locally cached class metadata shows the class active with category
|
||||||
|
`behaviorPool`. I then created wave D empty: U9 projection BDEF
|
||||||
|
`ZC_CHANGEAPPROVAL` (`.bdef.abdl`), U11 access control
|
||||||
|
(`.dcls.acds`), U12 metadata extension (`.ddlx.acds`) and U13 service
|
||||||
|
definition `ZUI_ARENA_CHANGE_APPROVAL` (`.srvd.acds`). `SRVD/SRV` also
|
||||||
|
offers an optional `referencedObject` create field [source: adt-mcp
|
||||||
|
abap_activate_objects, abap_creation-create_object,
|
||||||
|
get_object_type_details on 0M7DEJI, verified 2026-09-16].
|
||||||
|
- Why: wave C of the UI leg. Wave D is the last paste group before the UI
|
||||||
|
binding.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status, handover README
|
||||||
|
(BDEF extension `.bdef.abdl` for U9 and W3; SRVD tag list), KNOWLEDGE.md.
|
||||||
|
- Decision: I do not record `OPTIONS PRIVILEGED ACCESS`,
|
||||||
|
`%op-%action-<name>` on MODIFY response rows or `@status-rejected` as
|
||||||
|
proven yet. Activation returns no warnings, and no source says whether a
|
||||||
|
behavior pool with an empty Local Types include fails activation. I
|
||||||
|
confirm the handler source is in the Local Types include in the editor
|
||||||
|
first. Failing that, the first action call at U15 proves it at runtime.
|
||||||
|
I correct the handover README's BDEF extension and SRVD tag list in one
|
||||||
|
pass after the UI leg, rather than interrupting the paste session.
|
||||||
|
|
||||||
|
## 2026-09-16 — Wave B active: base behavior definition and root access control live
|
||||||
|
|
||||||
|
- What changed: I activated U10, the access control `ZR_CHANGEAPPROVAL`,
|
||||||
|
and U6, the managed `strict ( 2 )` behavior definition
|
||||||
|
`ZR_CHANGEAPPROVAL`. Both answered "Activation successful." U6 activated
|
||||||
|
through its `.bdef.abdl` URI on the first attempt, before its behavior
|
||||||
|
pool class existed. That also proves the three action-parameter entities
|
||||||
|
it references are active. I then created wave C empty: U7, the behavior
|
||||||
|
pool `ZBP_R_CHANGEAPPROVAL` (`create_object` returned only the global
|
||||||
|
`.clas.abap` path, no local-types include), and U8, the UI projection view
|
||||||
|
`ZC_CHANGEAPPROVAL` (`.ddls.acds`) [source: adt-mcp abap_activate_objects,
|
||||||
|
abap_creation-create_object on 0M7DEJI, verified 2026-09-16].
|
||||||
|
- Why: wave B of the UI leg.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status, handover README
|
||||||
|
(the BDEF activation extension is `.bdef.abdl` for U6, U9 and W3, and the
|
||||||
|
U6 fallback was not needed), KNOWLEDGE.md.
|
||||||
|
- Decision: now that an empty `ZBP_R_CHANGEAPPROVAL` exists, I activate U6
|
||||||
|
and U7 together in one call, so the BDEF is never checked against a class
|
||||||
|
that is not yet a behavior pool. The activation tool returns no warnings,
|
||||||
|
so warnings about the four unmapped calculated fields are checked in the
|
||||||
|
editor's Problems view rather than assumed absent. I locate the
|
||||||
|
local-types include live, after the global class part is saved.
|
||||||
|
|
||||||
|
## 2026-09-16 — Wave A active: table, parameter entities and root view live; wave B created
|
||||||
|
|
||||||
|
- What changed:
|
||||||
|
- I activated U1 `ZCHANGEAPPROVAL`, U2–U4 (the three action-parameter
|
||||||
|
entities) and U5 `ZR_CHANGEAPPROVAL` on `my431353`. Each activation
|
||||||
|
answered "Activation successful.", and the tool returned no warnings.
|
||||||
|
- The table activates through its `.tabl.ddic` URI; `.tabl.json` holds
|
||||||
|
only header metadata.
|
||||||
|
- U5's `cast( case … as abap_boolean preserving type )` was accepted, so
|
||||||
|
the fallback is not needed [source: adt-mcp abap_activate_objects on
|
||||||
|
0M7DEJI, verified 2026-09-16].
|
||||||
|
- I then created wave B empty: U6, the managed BDEF `ZR_CHANGEAPPROVAL`
|
||||||
|
(`create_object` returned `…/zr_changeapproval.bdef.abdl`), and U10, the
|
||||||
|
access control `ZR_CHANGEAPPROVAL` (`…/zr_changeapproval.dcls.acds`,
|
||||||
|
with `protectedEntity`).
|
||||||
|
- Why: wave A of the UI leg. Creating the U6 BDEF requires the root view to
|
||||||
|
be active, and it validated, which confirms U5 is active.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status, the handover README
|
||||||
|
activation table (TABL now proven; BDEF extension open), KNOWLEDGE.md.
|
||||||
|
- Decision: BDEF source is pasted into `.bdef.abdl`, not `.bdef.json`. At
|
||||||
|
U6 activation I try the `.bdef.abdl` URI first and fall back to
|
||||||
|
`.bdef.json`, which activated a generator BDEF on 2026-09-15. The README
|
||||||
|
activation table is corrected once U6 proves the extension. Activation
|
||||||
|
gives no direct active/inactive readback, so proof is indirect: U6's
|
||||||
|
activation is the first test of U2–U4, because U6 names them as action
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
## 2026-09-16 — Review fixes applied to the handover; duplicate checks read with privileged access
|
||||||
|
|
||||||
|
- What changed: all four remaining review fixes are in `docs/handover/rap/`.
|
||||||
|
- `use etag` sits in the entity header of both projection BDEFs.
|
||||||
|
- `retrySync` writes APPROVED, so EXECUTING is written only by
|
||||||
|
`markExecuting`.
|
||||||
|
- All six action handlers set `%fail-cause` and `%op-%action-<name>`,
|
||||||
|
merge the internal UPDATE's failed and reported rows, and return
|
||||||
|
results only for keys that did not fail.
|
||||||
|
- The base BDEF declares `validation checkDuplicate on save { create; }`.
|
||||||
|
Its handler refuses a second non-REJECTED record with the same
|
||||||
|
`ItemNumber` + `ChangeType` + `ArenaEcoId`, within the request or
|
||||||
|
against persisted rows.
|
||||||
|
|
||||||
|
The base BDEF and README carry the OQ-8 note that `authorization master
|
||||||
|
( none )` is not final. The table's paste target is `.tabl.ddic`; its
|
||||||
|
activation extension is still pending live proof. Two of my working
|
||||||
|
sessions wrote these files concurrently between 02:36 and 02:43. I
|
||||||
|
confirmed the result is consistent and stable (content unchanged over 45
|
||||||
|
seconds at 02:45, no duplicate governance entries). This session is now
|
||||||
|
the only one driving the build.
|
||||||
|
- Why: review findings B1, M1, m1 and m4 (entry below). Every one must be
|
||||||
|
in the source before its object is pasted.
|
||||||
|
- Affected: handover U6, U7, U9, W3, U1 header, README; PRD RAP BO status.
|
||||||
|
- Decision: the duplicate check reads `ZR_CHANGEAPPROVAL` with privileged
|
||||||
|
access. The view is access-controlled, and a handler `SELECT` on it
|
||||||
|
without privileged access breaks the RAP implementation rules [source:
|
||||||
|
sap-docs sap-help abaprap_impl_rules; abap-docs
|
||||||
|
/abap-docs-cloud/ABAPSELECT_PRIVILEGED_ACCESS, verified 2026-09-16]. The
|
||||||
|
validation's failed rows carry only `cause-unspecific`, the only cause
|
||||||
|
validations may use (same rules page). Three constructs are syntax I
|
||||||
|
prove only at tenant activation, each with a documented fallback:
|
||||||
|
`OPTIONS PRIVILEGED ACCESS` (fallback `FROM … WITH PRIVILEGED ACCESS`),
|
||||||
|
`%op-%action-<name>` on MODIFY response rows, and `preserving type` on
|
||||||
|
the U5 casts.
|
||||||
|
|
||||||
|
## 2026-09-16 — Root view U5 final: derived booleans cast as a whole
|
||||||
|
|
||||||
|
- What changed: in `zr_changeapproval.ddls.asddls`, `IsApprovable` and
|
||||||
|
`IsRetriable` are now `cast( case status when … then abap.char'X' else
|
||||||
|
abap.char' ' end as abap_boolean preserving type )`. The file header
|
||||||
|
records why: typed `abap.char` literals make the CASE result CHAR(1)
|
||||||
|
[source: abap-docs /abap-docs-cloud/ABENCDS_TYPED_LITERAL_V2, verified
|
||||||
|
2026-09-16], and `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 `preserving type` matches exactly. U5 is cleared
|
||||||
|
for paste.
|
||||||
|
- Why: review finding (4). A cast inside a CASE branch drops the data
|
||||||
|
element semantics.
|
||||||
|
- Affected: handover U5, wave A paste.
|
||||||
|
- Decision: if activation still rejects `preserving type`, I drop only
|
||||||
|
that addition, not the outer cast. I finalized U5 ahead of the remaining
|
||||||
|
correction work so wave A is not held up. Two concurrent corrections
|
||||||
|
collided on this file; each later brief names file ownership explicitly.
|
||||||
|
|
||||||
|
## 2026-09-16 — App documentation aligned to retry → APPROVED
|
||||||
|
|
||||||
|
- What changed: I updated the four statements of the retry transition in
|
||||||
|
`app/` (README, both i18n `#XBUT` comments, the mock `metadata.xml`
|
||||||
|
comment) from FAILED → EXECUTING to FAILED → APPROVED. No labels,
|
||||||
|
annotations, manifest or mock data changed. `npm run lint` and
|
||||||
|
`npm run ts-typecheck` pass.
|
||||||
|
- Why: ARN-002 was amended today; the app's documentation must not
|
||||||
|
describe the old state machine.
|
||||||
|
- Affected: `app/README.md`, `app/webapp/i18n/i18n.properties`,
|
||||||
|
`app/webapp/i18n/i18n_en.properties`,
|
||||||
|
`app/webapp/localService/mainService/metadata.xml`, KNOWLEDGE.md.
|
||||||
|
- Decision: I do not add a mock `executeAction` handler to show the new
|
||||||
|
transition. The mock server ignores actions (KNOWLEDGE 2026-09-16), and
|
||||||
|
the live RAP service replaces the mock at V-2 within this build, so a
|
||||||
|
mock-only transition would be throwaway work. The remote
|
||||||
|
`ui5-mcp-server` cannot lint local files; I lint UI5 code locally until
|
||||||
|
that changes.
|
||||||
|
|
||||||
|
## 2026-09-16 — Pre-paste SAP review: one blocker, retry semantics corrected, authorization opened
|
||||||
|
|
||||||
|
- What changed: I reviewed all 18 handover sources read-only against ABAP
|
||||||
|
Cloud docs, clean-core release states, the PRD and the app contract. The
|
||||||
|
app contract holds (17 properties, 3 actions, entity set `ChangeApproval`,
|
||||||
|
no UI create/update/delete), and all 11 referenced SAP objects are clean
|
||||||
|
core level A [source: sap-released-objects sap_check_clean_core_compliance,
|
||||||
|
verified 2026-09-16]. The review found:
|
||||||
|
- One blocker: `use etag` sat in the body of both projection BDEFs, but
|
||||||
|
it belongs in the entity header [source: abap-docs
|
||||||
|
/abap-docs-cloud/ABENBDL_CHARACTER_PROJECTION, verified 2026-09-16].
|
||||||
|
- Two majors: retry semantics and missing per-operation authorization.
|
||||||
|
- Four minors.
|
||||||
|
I dispatched corrections before any affected source reached the tenant.
|
||||||
|
- Why: every defect caught here saves a paste-and-activate round trip
|
||||||
|
against the live tenant, and two of them were correctness defects, not
|
||||||
|
syntax.
|
||||||
|
- Affected: `docs/handover/rap/` (U5, U6, U7, U9, W3, README), PRD ARN-002,
|
||||||
|
RAP-2, new OQ-8, `app/` documentation comments, KNOWLEDGE.md.
|
||||||
|
- Decision:
|
||||||
|
- (1) `retrySync` goes FAILED → APPROVED, clears `ErrorMessage`, and the
|
||||||
|
worker re-claims the record through `markExecuting`. The earlier FAILED
|
||||||
|
→ EXECUTING could not be claimed, because `markExecuting` accepts only
|
||||||
|
APPROVED. Working around that would make a retried record
|
||||||
|
indistinguishable from one another run is executing, which is the
|
||||||
|
double-write risk ARN-006 exists for.
|
||||||
|
- (2) Duplicate staging is refused on create: a `checkDuplicate`
|
||||||
|
validation on the semantic key while a non-REJECTED record exists.
|
||||||
|
Webhook and delta poll can both deliver the same ECO, and I will not
|
||||||
|
leave idempotency to the executors alone.
|
||||||
|
- (3) Action handlers honor the RAP implementation contract (`%fail`,
|
||||||
|
`%op`, propagated UPDATE failures, results only for successful keys),
|
||||||
|
so the worker cannot read a failed `markSynced` as success.
|
||||||
|
- (4) The derived boolean flags cast the whole CASE to `abap_boolean`,
|
||||||
|
because a cast inside a CASE branch drops the data element semantics
|
||||||
|
[source: abap-docs /abap-docs-cloud/ABENCDS_CAST_EXPRESSION_V2, verified
|
||||||
|
2026-09-16].
|
||||||
|
- (5) Deferred: a metadata-level mandatory `RejectReason`, until after
|
||||||
|
the POC (server-side enforcement exists).
|
||||||
|
- (6) Opened: OQ-8. `authorization master ( none )` stays for the
|
||||||
|
dev-tenant POC only and must be replaced before business roles get the
|
||||||
|
app.
|
||||||
|
|
||||||
|
## 2026-09-16 — Wave A created: table, parameter entities, root view exist empty
|
||||||
|
|
||||||
|
- What changed: I created U1 `ZCHANGEAPPROVAL` (TABL) and U2–U5
|
||||||
|
`ZD_CHANGEAPPROVAL_REJECTP`, `ZD_CHANGEAPPROVAL_SYNCEDP`,
|
||||||
|
`ZD_CHANGEAPPROVAL_FAILEDP` and `ZR_CHANGEAPPROVAL` (DDLS) in
|
||||||
|
`ZARENA_PLM`. They are inactive and have no source. `abap_transport-get`
|
||||||
|
answered `isRecordingRequired: false`. Each object passed validation and
|
||||||
|
creation with the handover JSON unchanged. The table's returned URI ends
|
||||||
|
in `.tabl.ddic`, not the `.tabl.json` that activated a generator-created
|
||||||
|
table on 2026-09-15 [source: adt-mcp abap_creation-create_object on
|
||||||
|
0M7DEJI, verified 2026-09-16].
|
||||||
|
- Why: wave A of the UI leg. The sources are pasted next.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status, handover activation
|
||||||
|
URI table (TABL extension open).
|
||||||
|
- Decision: I activate U1 with the URI `create_object` returned
|
||||||
|
(`.tabl.ddic`) and fall back to `.tabl.json`. The handover URI table is
|
||||||
|
corrected once activation proves which one works.
|
||||||
|
|
||||||
|
## 2026-09-16 — Package recreated; UI-leg build started in waves
|
||||||
|
|
||||||
|
- What changed: package `ZARENA_PLM` exists again under `ZLOCAL`. ADT MCP
|
||||||
|
validation of `ZCHANGEAPPROVAL` in it succeeded (live, 02:27). I started
|
||||||
|
wave A of the UI leg: U1–U5 (table, the three action-parameter entities,
|
||||||
|
root view) are created empty so their sources can be pasted. I also read
|
||||||
|
the create fields for classes, access controls and metadata extensions
|
||||||
|
live. Those were the three types the handover had marked unconfirmed.
|
||||||
|
Metadata extension names allow 40 characters. Access controls and
|
||||||
|
metadata extensions take an optional protected/extended entity (KNOWLEDGE
|
||||||
|
2026-09-16).
|
||||||
|
- Why: the from-scratch build (OQ-7) is unblocked. The build moves in waves
|
||||||
|
bounded by activation dependencies: BDEF creation needs its root view
|
||||||
|
active, and the binding needs its service definition active. Each wave
|
||||||
|
asks for one paste session per group, not one per file.
|
||||||
|
- Affected: dev tenant `ZARENA_PLM`, PRD RAP BO status, KNOWLEDGE.md.
|
||||||
|
- Decision: waves are A = U1–U5 (create, paste, activate), B = U6 base
|
||||||
|
BDEF, C = U7 behavior pool, D = U8 projection view + U10 root access
|
||||||
|
control, E = U9 projection BDEF + U11 + U12 + U13, F = U14 binding with
|
||||||
|
the service-alias check. Nothing is activated before its source is
|
||||||
|
pasted.
|
||||||
|
|
||||||
|
## 2026-09-16 — Handover regenerated under clean names; binding names cut to SAP's 26-character limit
|
||||||
|
|
||||||
|
- What changed: I regenerated `docs/handover/rap/` for a build from an
|
||||||
|
empty package. Eleven source files were renamed to the clean object
|
||||||
|
names, every internal reference was updated, and the README was replaced
|
||||||
|
with a create → paste → activate sequence per object. The UI service
|
||||||
|
definition exposes only `ChangeApproval`. A scripted comparison against
|
||||||
|
the app's mock `metadata.xml` confirms that the UI projection carries all
|
||||||
|
17 app properties plus `approve`, `reject` and `retrySync`. The build
|
||||||
|
splits into a 14-source UI leg and a 4-source worker leg. The
|
||||||
|
`markSynced`/`markFailed` parameter entities move into the UI leg,
|
||||||
|
because the base BDEF references them and they must be active first. I
|
||||||
|
amended RAP-5 to two service definitions with one binding each, and
|
||||||
|
updated both PRD interface-contract rows. A read-only SAP review of the
|
||||||
|
18 sources is running before the paste session.
|
||||||
|
- Why: the tenant package is empty, so the handover's generator inventory
|
||||||
|
and `ZZ…000` targets no longer existed. SAP Help gives a maximum of 26
|
||||||
|
characters for service binding names, the same in ABAP Cloud, SAP Cloud
|
||||||
|
Platform and ABAP Platform 2021 [source: sap-docs sap-help loio
|
||||||
|
4bad0d7280b742a68dd2d42b1441f09a, f3e608ace2a64aada33bdd3e2419113a,
|
||||||
|
777e027f61c3490dba0433443d9143a6, verified 2026-09-16]. The ADT MCP's
|
||||||
|
create metadata allows 30, and generator bindings of 29–30 characters
|
||||||
|
activated on 2026-09-15, but none was ever published. No source says
|
||||||
|
whether publishing tolerates more than 26.
|
||||||
|
- Affected: PRD RAP-5, the two approval-BO interface-contract rows, RAP BO
|
||||||
|
status, `docs/handover/rap/`, V-2 (the app's dataSource URL, mock
|
||||||
|
namespace and annotation targets now carry a planned difference),
|
||||||
|
KNOWLEDGE.md.
|
||||||
|
- Decision: the bindings are `ZUI_ARENA_CHANGE_APPR_O4` (24) and
|
||||||
|
`ZAPI_ARENA_CHANGE_APPR_A2X` (26). The service definitions keep
|
||||||
|
`ZUI_ARENA_CHANGE_APPROVAL` and `ZAPI_ARENA_CHANGE_APPROVAL`. I chose the
|
||||||
|
documented limit over the tool's undocumented allowance, because a
|
||||||
|
refusal at publish would come after the whole UI leg and need a delete in
|
||||||
|
ADT to recover. The app edit this causes is one URL segment plus the
|
||||||
|
namespace, both already scheduled for V-2 reconciliation against live
|
||||||
|
`$metadata`, so I defer it to that brief instead of changing the app
|
||||||
|
twice. The derived-field EDM types (`StatusCriticality` as INT1,
|
||||||
|
`abap_boolean` flags, `utclong`, `sysuuid_x16`) stay unverified until
|
||||||
|
live `$metadata` exists; they are not assumed.
|
||||||
|
|
||||||
|
## 2026-09-16 — Deletion verified live: package `ZARENA_PLM` is gone; name limits confirmed
|
||||||
|
|
||||||
|
- What changed: with the ADT MCP back online I probed `my431353` read-only.
|
||||||
|
`abap_creation-run_validation` answers "Package 'ZARENA_PLM' does not
|
||||||
|
exist" for tables, data definitions and classes, the same as a
|
||||||
|
nonexistent control package. The deletion removed the package itself, not
|
||||||
|
just its contents. `abap_creation-get_object_type_details` gives the
|
||||||
|
maximum name lengths: database table 16; data definition, service
|
||||||
|
definition, service binding and behavior definition 30 [source: adt-mcp
|
||||||
|
abap_creation-get_object_type_details on 0M7DEJI, verified 2026-09-16].
|
||||||
|
- Why: the from-scratch build starts from the package, and every clean name
|
||||||
|
had to fit its limit before any object is created.
|
||||||
|
- Affected: PRD RAP BO status, `docs/handover/rap/README.md` (step 0 is now
|
||||||
|
"recreate the package"), KNOWLEDGE.md.
|
||||||
|
- Decision: I recreate `ZARENA_PLM` under `ZLOCAL` under the same name, so
|
||||||
|
every activation URI pattern verified on 2026-09-15 stays valid. All clean
|
||||||
|
names fit (`ZCHANGEAPPROVAL` 15, `ZUI_ARENA_CHANGE_APPROVAL_O4` 28).
|
||||||
|
`ZLOCAL` means no transport request on every create while I prototype. It
|
||||||
|
is not a dead end for production: a package can be reassigned from
|
||||||
|
`ZLOCAL` to `ZCUSTOM_DEVELOPMENT` (transport layer YDEV) later, though not
|
||||||
|
the reverse [source: sap-docs sap-help loio
|
||||||
|
a0d80f14fcb74cb1ac8fb6f3d9ad48cc (2608), verified 2026-09-15]. I make
|
||||||
|
that move once the approval app works end to end on the development
|
||||||
|
tenant.
|
||||||
|
|
||||||
|
## 2026-09-16 — Tenant package emptied; from-scratch build under names that match the Fiori app
|
||||||
|
|
||||||
|
- What changed: the `ZARENA_PLM` contents on `my431353` were deleted in ADT.
|
||||||
|
This includes both generator sets and the empty hand-created objects. I
|
||||||
|
have not yet verified the deletion live (the ADT MCP is offline), and the
|
||||||
|
package's own survival is unconfirmed. I closed OQ-7 and dispatched a
|
||||||
|
regeneration of `docs/handover/rap/` under the clean names.
|
||||||
|
- Why: the Fiori app binds to
|
||||||
|
`/sap/opu/odata4/sap/zui_arena_change_approval_o4/srvd/sap/zui_arena_change_approval/0001/`,
|
||||||
|
entity set `ChangeApproval`, and actions `approve`, `reject` and
|
||||||
|
`retrySync` (`app/webapp/manifest.json`, mock `metadata.xml`). A Fiori app
|
||||||
|
runs against a replacement service only if the entity set names,
|
||||||
|
properties and actions match [source: fiori-mcp-server search_docs
|
||||||
|
"Replacing OData Service in an Adaptation Project", verified 2026-09-16].
|
||||||
|
With the package empty, no leftover objects compete for these names.
|
||||||
|
- Affected: PRD OQ-7 (closed), RAP BO status, `docs/handover/rap/` (all 19
|
||||||
|
sources and the README), V-2 (the service identity is now designed to
|
||||||
|
match the app instead of reconciled after the fact).
|
||||||
|
- Decision: no generator this time, because it forces a `ZZ` prefix and a
|
||||||
|
`000` suffix (KNOWLEDGE, 2026-09-15). I create each object through the ADT
|
||||||
|
MCP under its final name: table `ZCHANGEAPPROVAL`, `ZR_CHANGEAPPROVAL`,
|
||||||
|
`ZBP_R_CHANGEAPPROVAL`, `ZC_CHANGEAPPROVAL`, SRVD
|
||||||
|
`ZUI_ARENA_CHANGE_APPROVAL`, SRVB `ZUI_ARENA_CHANGE_APPROVAL_O4`. The A2X
|
||||||
|
names are unchanged. The UI leg (about 13 sources) is built and verified
|
||||||
|
against the app before the worker leg starts.
|
||||||
|
|
||||||
|
## 2026-09-16 — Tenant state corrected: two generator sets, no designed logic deployed; naming decision opened
|
||||||
|
|
||||||
|
- What changed: a live ATC run over `ZARENA_PLM` returned 43 objects, not
|
||||||
|
the 12 my 2026-09-15 handover README lists. The package holds two default
|
||||||
|
`x-ui-service` generator sets side by side: the `ZZ…000` set
|
||||||
|
(`ZZCHNGPPROVAL000`, `ZZR_/ZZC_CHANGEAPPROVAL000`, binding
|
||||||
|
`ZZUI_ARENA_CHANGE_APPRVL000_O4`) and a set without the suffix
|
||||||
|
(`ZZCHANGEAPPROVAL`, `ZZR_/ZZC_CHANGEAPPROVAL`, binding
|
||||||
|
`ZZUI_ARENA_CHANGE_APPROVAL_O4`). Each set has its own behavior pools and
|
||||||
|
SAP object types. It also holds the five empty, inactive hand-created
|
||||||
|
objects and the probe class. None of the six actions, the status machine,
|
||||||
|
or the feature control exists in any tenant object. The designed source
|
||||||
|
lives only in `docs/handover/rap/`, which is now committed and pushed
|
||||||
|
(`f8b45ea`). My most likely explanation for the second set is an earlier
|
||||||
|
generator run that I never recorded; I cannot confirm it, because the ADT
|
||||||
|
MCP has no read tool.
|
||||||
|
- Why: my 2026-09-15 entry and the PRD status described a clean skeleton
|
||||||
|
that was ready for a paste session. Following them would put the sources
|
||||||
|
into objects whose names the Fiori app does not bind to.
|
||||||
|
- Affected: PRD status (RAP BO → blocked on OQ-7), new OQ-7,
|
||||||
|
`docs/handover/rap/README.md` (its object inventory is stale), the Fiori
|
||||||
|
app's V-2 service identity.
|
||||||
|
- Decision: none yet. I recommend building under clean names that match the
|
||||||
|
app, next to the unused sets, and deferring deletion. The two sets are
|
||||||
|
unpublished, their `ZZ` names do not collide with the clean set, and
|
||||||
|
deleting them needs Eclipse ADT, which is not set up. Of the 19 sources,
|
||||||
|
13 serve the UI milestone. The six A2X-leg files wait until the app runs
|
||||||
|
against the real service.
|
||||||
|
|
||||||
## 2026-09-15 — abapGit researched: not required for ADT for VS Code; viable as a bulk source-load path
|
## 2026-09-15 — abapGit researched: not required for ADT for VS Code; viable as a bulk source-load path
|
||||||
|
|
||||||
- What changed: I verified that abapGit is neither required nor used by SAP's
|
- What changed: I verified that abapGit is neither required nor used by SAP's
|
||||||
|
|||||||
+202
@@ -101,6 +101,208 @@ entries the same way — dated and falsifiable.
|
|||||||
SRVB creation validate the referenced object's ACTIVE version, so create
|
SRVB creation validate the referenced object's ACTIVE version, so create
|
||||||
and activate DDLS/SRVD sources first. ATC via MCP rejects RONT/NONT.
|
and activate DDLS/SRVD sources first. ATC via MCP rejects RONT/NONT.
|
||||||
|
|
||||||
|
- [2026-09-16] **An expired VS Code ADT logon shows up in the ADT MCP only as
|
||||||
|
"Your user was logged off."** `abap_list_destinations` still lists the
|
||||||
|
destination and `get_object_type_details` still returns field metadata,
|
||||||
|
so neither proves a live session. **ADT for VS Code logs on to S/4HANA
|
||||||
|
Cloud with a reentrance ticket and a localhost redirect:** it opens
|
||||||
|
`https://<tenant>/sap/bc/adt/core/http/reentranceticket?redirect-url=http://localhost:<port>/adt/redirect`,
|
||||||
|
and after SSO the tenant redirects to
|
||||||
|
`http://localhost:<port>/adt/redirect?…&reentrance-ticket=<ticket>`
|
||||||
|
(observed 2026-09-16). The ticket in that URL is a live logon credential:
|
||||||
|
never paste it into chats, logs or files. The same mechanism is the
|
||||||
|
plausible browser-free login path for `abap-adt-mcp` (OQ-9). **Tested
|
||||||
|
2026-09-18: it does not work.** With a freshly captured reentrance
|
||||||
|
ticket, `https://my431353.s4hana.cloud.sap/sap/bc/adt/core/discovery`
|
||||||
|
and an ABAP class source URL answered HTTP 200 with the same 3094-byte
|
||||||
|
SAML redirect page (`SAMLRequest`, `saml2`) for all of: `Cookie:
|
||||||
|
MYSAPSSO2=<ticket>`, header `MYSAPSSO2: <ticket>`, and no credential at
|
||||||
|
all, with and without `?sap-client=080`. A reentrance ticket is
|
||||||
|
therefore neither a logon-ticket cookie nor an assertion-ticket header
|
||||||
|
here; ADT exchanges it some other way. Do not spend more on
|
||||||
|
`abap-adt-mcp`'s `sso2` mode without first capturing what the extension
|
||||||
|
actually sends. `abap_creation-run_validation` is the
|
||||||
|
cheapest read-only liveness check (it answers "… validation failed: Your
|
||||||
|
user was logged off."). Fix it by logging on to the destination again in
|
||||||
|
VS Code; the MCP port and token stay the same unless VS Code reloads
|
||||||
|
(live 2026-09-16, ~1.5 h after the last successful call).
|
||||||
|
- [2026-09-16] **Ask the ADT MCP for name limits and package existence; do
|
||||||
|
not guess them.** `abap_creation-get_object_type_details` returns each
|
||||||
|
type's create fields and `maxLength`: `TABL/DT` 16, while `DDLS/DF`,
|
||||||
|
`SRVD/SRV`, `SRVB/SVB` and `BDEF/BDO` allow 30 (the generator's `APPRVL`
|
||||||
|
truncation came from that 30 limit). `abap_creation-run_validation` is a
|
||||||
|
read-only way to check that a package exists: a missing package answers
|
||||||
|
"Package '<name>' does not exist" [source: adt-mcp on 0M7DEJI, verified
|
||||||
|
2026-09-16]. The Claude Code session does not expose the adt-mcp tools
|
||||||
|
unless the server was up at session start. A JSON-RPC probe over
|
||||||
|
`http://127.0.0.1:2236/mcp` with the `.mcp.json` bearer token works for
|
||||||
|
read-only checks in the meantime.
|
||||||
|
|
||||||
|
- [2026-09-16] **In the VS Code ADT workspace, the `.json` file in an object
|
||||||
|
folder is header metadata, never the source.** A table folder holds
|
||||||
|
`zchangeapproval.tabl.json` (formatVersion, description, originalLanguage,
|
||||||
|
`abapLanguageVersion`) and `zchangeapproval.tabl.ddic` (the Dictionary DDL
|
||||||
|
source). Paste into `.ddic`, and activate through the `.tabl.ddic` URI
|
||||||
|
(live, "Activation successful."). For objects created with
|
||||||
|
`create_object`, the returned `filePath` names the source file: DDLS
|
||||||
|
`.ddls.acds`, DCLS `.dcls.acds`, BDEF **`.bdef.abdl`** [source: adt-mcp
|
||||||
|
abap_creation-create_object / abap_activate_objects on 0M7DEJI, verified
|
||||||
|
2026-09-16]. The `.tabl.json` / `.bdef.json` activation URIs recorded on
|
||||||
|
2026-09-15 were generator objects; trust the returned `filePath` over
|
||||||
|
that table. BDEF activation through `.bdef.abdl` is proven (U6, live).
|
||||||
|
- [2026-09-16] **A managed `strict ( 2 )` BDEF activates before its
|
||||||
|
behavior pool class exists** (`ZR_CHANGEAPPROVAL`, "Activation
|
||||||
|
successful.", with no class `ZBP_R_CHANGEAPPROVAL` yet) [source: adt-mcp
|
||||||
|
abap_activate_objects on 0M7DEJI, verified 2026-09-16]. Once the class
|
||||||
|
exists, activate BDEF and class together. `abap_activate_objects` returns
|
||||||
|
only "Activation successful." with no warnings on success. On failure it
|
||||||
|
lists messages with line and column but no severity (U13 SRVD,
|
||||||
|
2026-09-16), so read severity in the editor. Service definitions
|
||||||
|
report "Comments are not supported and will be deleted on save" for a
|
||||||
|
leading block comment, and **that message blocks activation**: the same SRVD failed with a
|
||||||
|
19-line `/* */` header and activated without it (live 2026-09-16; the
|
||||||
|
DCLS, DDLX and BDEF headers did not block). The language allows comments
|
||||||
|
[source: abap-docs /abap-docs-cloud/ABENCDS_GENERAL_SYNTAX_RULES,
|
||||||
|
verified 2026-09-16], so this is the tooling. Keep SRVD sources free of
|
||||||
|
comments. `abap_atc_get_result` returned only totals for a run with 43
|
||||||
|
infos (UI leg), but per-object `findings[]` (priority, check class,
|
||||||
|
message id and title, quick-fix flags) for a run with a priority-3
|
||||||
|
warning (worker leg). Expect details for warnings and errors at least
|
||||||
|
(both runs live 2026-09-16). A projection BDEF over a view with
|
||||||
|
`@AccessControl.authorizationCheck: #CHECK` raises SLIN W607 (+BDL 549)
|
||||||
|
"should be replaced with #MANDATORY" (priority 3; it does not block
|
||||||
|
activation). An OData V4 **Web API** binding reports
|
||||||
|
`bindingCategory: "1"` (UI: `"0"`) in `fetch_services`.
|
||||||
|
- [2026-09-16] **OData V4 identity on a RAP UI service: the binding name is
|
||||||
|
only the URL's first segment.** For definition `ZUI_ARENA_CHANGE_APPROVAL`
|
||||||
|
and binding `ZUI_ARENA_CHANGE_APPR_O4`, the live `$metadata` uses
|
||||||
|
namespace `com.sap.gateway.srvd.zui_arena_change_approval.v0001`, from the
|
||||||
|
**service definition**, and entity type `ChangeApprovalType`, with a
|
||||||
|
`Type` suffix and no underscore (live 2026-09-16,
|
||||||
|
`docs/v2/zui_arena_change_appr_o4.metadata.xml`). The earlier assumption
|
||||||
|
that the namespace carries the binding name and a `_Type` suffix was
|
||||||
|
wrong. RAP also emits `__OperationControl` with
|
||||||
|
`Core.OperationAvailable Path="_it/__OperationControl/<action>"` for
|
||||||
|
`features : instance` actions, `SAP__Messages`, `Nullable="false"` on
|
||||||
|
non-key elements, and `cast( case … as abap_boolean preserving type )`
|
||||||
|
surfaces as `Edm.Boolean` (INT1 literals as `Edm.Byte`, `utclong` as
|
||||||
|
`Edm.DateTimeOffset Precision="7"`). Copying `$metadata` from Chrome's
|
||||||
|
XML tree view decodes `&` to `&`; use `view-source:` or re-escape.
|
||||||
|
- [2026-09-16] **Live RAP `$metadata` works verbatim as Fiori mock metadata**
|
||||||
|
(`@sap-ux/fe-mockserver-core` 1.7.15 serves it and accepts `$select` on
|
||||||
|
`__OperationControl/approve`). Mock rows must then carry
|
||||||
|
`__OperationControl`, or the enablement paths resolve to nothing. The
|
||||||
|
app's ESLint config checks only TS/JS, so run `npx @ui5/linter` for UI5.
|
||||||
|
The fiori-mcp `list_functionality` for `app/` offers only
|
||||||
|
add/delete page and controller extension, so dataSource and annotation
|
||||||
|
edits are made directly. The cached `@ui5/manifest` 1.89.0 schema
|
||||||
|
rejects the generator's manifest `_version: "2.11.0"`; that is not a
|
||||||
|
defect in the app.
|
||||||
|
- [2026-09-16] **A published custom UI service answers on the UI host, not the
|
||||||
|
`-api` host, for a business user.** `fetch_service_information` reports
|
||||||
|
the service URL on `my431353-api.s4hana.cloud.sap` (with
|
||||||
|
`sap-client=080`), but a browser business-user call there is refused as
|
||||||
|
"Forbidden / blocked by UCON". The same path on
|
||||||
|
`my431353.s4hana.cloud.sap` returns `$metadata` for the developer user,
|
||||||
|
before any IAM app exists (live 2026-09-16). The `-api` host is the
|
||||||
|
communication-user entry point (communication arrangement), so the
|
||||||
|
worker's A2X service goes there through a custom communication scenario
|
||||||
|
(V-2), never the UI service. After publishing, ADT for VS Code reports
|
||||||
|
"<binding> published locally".
|
||||||
|
- [2026-09-16] **In ADT for VS Code, publishing a service binding is a code
|
||||||
|
lens, not a button or a palette command.** Open the binding's `.srvb.json`
|
||||||
|
source and click 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 (1.1.2) contributes
|
||||||
|
no publish command, and the SAP ADT MCP has no publish tool. `create_object` for a class returns only the global
|
||||||
|
`.clas.abap` path, no local-types (CCIMP) path.
|
||||||
|
- [2026-09-16] **`abap-adt-mcp` 2.1.0 auto-detects a browser for SSO only on
|
||||||
|
macOS.** `detectBrowser()` checks `SAP_BROWSER_PATH` and then three
|
||||||
|
`/Applications/…` paths; otherwise it throws "No Chrome/Edge/Brave found
|
||||||
|
for SSO login" (`dist/lib/browserLogin.js:38-50`, read 2026-09-16). On
|
||||||
|
this Linux host the server env must carry
|
||||||
|
`SAP_BROWSER_PATH=/usr/bin/google-chrome` along with the desktop display
|
||||||
|
variables. Login harvests the HttpOnly `MYSAPSSO2`/`SAP_SESSIONID` cookies
|
||||||
|
over the Chrome DevTools Protocol (`puppeteer-core`). `listSystems` needs
|
||||||
|
no SAP call and is the safe first probe of its policy. **Its SSO does not
|
||||||
|
work from a remote shell:** there is no localhost redirect (SAML returns
|
||||||
|
to the tenant), and the sign-in must be typed into the server-side Chrome
|
||||||
|
window (`headless: false`, profile
|
||||||
|
`~/.abap-adt-mcp/sso/<host>`, `--remote-debugging-port=0`, 300 s timeout).
|
||||||
|
Over multi-hop SSH neither the desktop nor a DevTools relay is reachable in
|
||||||
|
practice. Do the first sign-in at the server's desktop; with "stay signed
|
||||||
|
in" later logins should complete unattended (unverified, 2026-09-16).
|
||||||
|
- [2026-09-16] **Create fields for the remaining RAP types (`get_object_type_details`
|
||||||
|
on 0M7DEJI):** `CLAS/OC`: packageName, name (≤30), description, optional
|
||||||
|
superclass, interfaces. `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].
|
||||||
|
Pass `protectedEntity` / `extendedEntity` so the editor starts from the
|
||||||
|
right skeleton. DCLS creation does **not** require the protected entity to
|
||||||
|
be active. `ZC_CHANGEAPPROVAL_A2X` access control validated and was
|
||||||
|
created while its DDLS was still empty and never activated (unlike
|
||||||
|
BDEF/SRVB creation). `SRVB/SVB` `bindingType` values are exactly `OData
|
||||||
|
V2 - UI`, `OData V2 - Web API`, `OData V4 - UI`, `OData V4 - Web API`,
|
||||||
|
whatever label the release notes use [source: adt-mcp
|
||||||
|
abap_creation-run_validation / create_object /
|
||||||
|
get_object_type_details on 0M7DEJI, verified 2026-09-16].
|
||||||
|
- [2026-09-16] **Service binding names: SAP Help says 26 characters, the
|
||||||
|
ADT MCP allows 30.** The 26-character limit appears on the ABAP Cloud,
|
||||||
|
SAP Cloud Platform and ABAP Platform 2021 "Creating Service Binding"
|
||||||
|
pages [source: sap-docs sap-help loio 4bad0d7280b742a68dd2d42b1441f09a,
|
||||||
|
verified 2026-09-16]. `get_object_type_details` reports 30 for
|
||||||
|
`SRVB/SVB`, and the `x-ui-service` generator itself emits 29–30-character
|
||||||
|
bindings, which activated unpublished on `my431353` on 2026-09-15.
|
||||||
|
Whether publishing accepts more than 26 is unknown. Stay at 26 or fewer
|
||||||
|
rather than find out after a whole BO leg. The generator does not keep to
|
||||||
|
the documented limit, which is one more reason not to use it for names
|
||||||
|
that ship.
|
||||||
|
- [2026-09-16] **In an OData V4 binding URL
|
||||||
|
`/sap/opu/odata4/sap/<binding>/srvd/sap/<alias>/0001/`, the second
|
||||||
|
segment is the binding's service name (alias), not necessarily the
|
||||||
|
service definition name. 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]. Check it before publishing.
|
||||||
|
- [2026-09-16] **CDS CASE results take the longest branch type, and untyped
|
||||||
|
integer literals are typed INT1..INT8 by value** [source: abap-docs
|
||||||
|
/abap-docs-cloud/ABENCDS_CASE_EXPRESSION_V2, ABENCDS_UNTYPED_LITERAL_V2,
|
||||||
|
verified 2026-09-16]. A text CASE over status codes becomes CHAR(n) of
|
||||||
|
the longest literal, and a 0–5 criticality becomes INT1. Cast
|
||||||
|
explicitly when the OData type matters.
|
||||||
|
- [2026-09-16] **The `ui5-mcp-server` linter cannot see this repo.** The
|
||||||
|
server is reached over SSE on `mcp.alexzaw.dev`, and `run_ui5_linter` on
|
||||||
|
`app/` fails with `ENOENT: realpath '/home/alexz/…/arena-plm/app'`
|
||||||
|
(live 2026-09-16). Its doc/API tools work; its file-based tools
|
||||||
|
(linter, manifest validation, project info) do not. For real UI5 code
|
||||||
|
changes, lint locally (`npx @ui5/linter` inside `app/`) or run the
|
||||||
|
server where the files are.
|
||||||
|
- [2026-09-16] **The Fiori mock server does not simulate bound actions.**
|
||||||
|
Without a per-entity `webapp/localService/mainService/data/<Entity>.js`
|
||||||
|
exporting `executeAction`, `@sap-ux/fe-mockserver-core` 1.7.15 returns
|
||||||
|
the action data unchanged
|
||||||
|
(`node_modules/@sap-ux/fe-mockserver-core/dist/mockdata/fileBasedMockData.js:547-549`,
|
||||||
|
read locally 2026-09-16). Status transitions can be shown only against
|
||||||
|
the live RAP service, or with a hand-written mock handler.
|
||||||
|
- [2026-09-16] **Cast the whole CASE to a data element, never each
|
||||||
|
branch.** "If a data element is specified for `dtype`, the result …
|
||||||
|
inherits the semantic properties of the data element. An exception to
|
||||||
|
this is the use of the `CAST` expression within a case distinction."
|
||||||
|
Write `cast( case … end as abap_boolean preserving type )`.
|
||||||
|
`PRESERVING TYPE` requires an exact match of built-in type and length
|
||||||
|
[source: abap-docs /abap-docs-cloud/ABENCDS_CAST_EXPRESSION_V2, verified
|
||||||
|
2026-09-16]. To guarantee that length, use typed literals in the branches
|
||||||
|
(`abap.char'X'`, `abap.char' '` are CHAR(1)): an untyped character
|
||||||
|
literal's length is only "at least as large as the actual length of the
|
||||||
|
literal" [source: abap-docs /abap-docs-cloud/ABENCDS_TYPED_LITERAL_V2,
|
||||||
|
ABENCDS_UNTYPED_LITERAL_V2, verified 2026-09-16]. `ABAP_BOOLEAN` is CHAR 1
|
||||||
|
[source: abap-docs /abap-docs-standard/ABENDATAOBJECTS_TRUE_VALUE_GUIDL,
|
||||||
|
verified 2026-09-16]. The same caveat means a CASE over untyped status
|
||||||
|
literals is not provably CHAR(n) of the longest literal.
|
||||||
|
- [2026-09-16] **`sap-released-objects` has no `SRVB` object type for
|
||||||
|
public_cloud**, the same gap as `DDLS` (use `CDS_STOB` for views). Binding
|
||||||
|
release questions cannot be answered from that server.
|
||||||
|
|
||||||
- [2026-09-15] **Developer business catalogs: `SAP_A4C_BC_*` is the Public
|
- [2026-09-15] **Developer business catalogs: `SAP_A4C_BC_*` is the Public
|
||||||
Cloud / BTP ABAP family; `SAP_A4_BC_*` is on-premise / Private Cloud.** On
|
Cloud / BTP ABAP family; `SAP_A4_BC_*` is on-premise / Private Cloud.** On
|
||||||
`my431353` the developer role carries the A4C catalogs (e.g.
|
`my431353` the developer role carries the A4C catalogs (e.g.
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ tombstone remains valid.
|
|||||||
| BOM (read) | V2 | `/sap/opu/odata/sap/API_BILL_OF_MATERIAL_SRV` | v1 only; v2 rejects guid keys |
|
| BOM (read) | V2 | `/sap/opu/odata/sap/API_BILL_OF_MATERIAL_SRV` | v1 only; v2 rejects guid keys |
|
||||||
| Change Master | V2 | `/sap/opu/odata/sap/API_CHANGEMASTER;v=0002` | object-type assignments, material linking |
|
| Change Master | V2 | `/sap/opu/odata/sap/API_CHANGEMASTER;v=0002` | object-type assignments, material linking |
|
||||||
| Attachment | V2 | `/sap/opu/odata/sap/API_CV_ATTACHMENT_SRV` | function import for URL creation |
|
| Attachment | V2 | `/sap/opu/odata/sap/API_CV_ATTACHMENT_SRV` | function import for URL creation |
|
||||||
| Approval BO — UI binding (Fiori app) | V4 UI | `/sap/opu/odata4/sap/zzui_arena_change_apprvl000_o4/srvd/sap/zzui_arena_change_apprvl000_o4/0001/` (binding `ZZUI_ARENA_CHANGE_APPRVL000_O4`, generated 2026-09-15, unpublished) | expected namespace `com.sap.gateway.srvd.zzui_arena_change_apprvl000_o4.v0001`, alias `SAP__self`, entity `ChangeApproval_Type`/set `ChangeApproval`; confirm from live `$metadata` after publish (V-2); Fiori app still carries the pre-generation provisional name |
|
| Approval BO — UI binding (Fiori app) | V4 UI | `/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/` (binding `ZUI_ARENA_CHANGE_APPR_O4` on definition `ZUI_ARENA_CHANGE_APPROVAL`; designed 2026-09-16, not yet created) | entity set `ChangeApproval` / type `ChangeApproval_Type`, actions `approve`, `reject(RejectReason)`, `retrySync`; namespace, EDM types and the URL's service alias segment are confirmed from live `$metadata` after publish (V-2). The Fiori app still points at `zui_arena_change_approval_o4`, and V-2 reconciliation moves it |
|
||||||
| Approval BO — Web API (A2X) binding (worker) | V4 Web API | binding `ZAPI_ARENA_CHANGE_APPROVAL_A2X` on definition `ZAPI_ARENA_CHANGE_APPROVAL` (pending: created after the A2X projection is active) | custom communication scenario, technical user; namespace `com.sap.gateway.srvd_a2x.zapi_arena_change_approval_a2x.v0001` expected; exposes create + read + `markExecuting` / `markSynced(SapChangeNumber)` / `markFailed(ErrorMessage)` (RAP-6) |
|
| Approval BO — Web API (A2X) binding (worker) | V4 Web API | binding `ZAPI_ARENA_CHANGE_APPR_A2X` on definition `ZAPI_ARENA_CHANGE_APPROVAL` (designed 2026-09-16; created after the A2X projection is active) | custom communication scenario, technical user; namespace from live `$metadata`; exposes create + read + `markExecuting` / `markSynced(SapChangeNumber)` / `markFailed(ErrorMessage)` (RAP-6) |
|
||||||
| Worker inbound surface | REST (OpenAPI 3.1) | `docs/api/worker.openapi.json` | webhook intake (503 when buffer full), health (`ok`/`degraded`, session `none/active/expired/refreshing`), sync-run admin (strict request schema); contract v0.2.0 (2026-09-14 wave); auth scheme pending OQ-6 (ARN-009) |
|
| Worker inbound surface | REST (OpenAPI 3.1) | `docs/api/worker.openapi.json` | webhook intake (503 when buffer full), health (`ok`/`degraded`, session `none/active/expired/refreshing`), sync-run admin (strict request schema); contract v0.2.0 (2026-09-14 wave); auth scheme pending OQ-6 (ARN-009) |
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
@@ -76,7 +76,7 @@ Capability level — field mappings deferred to the POC per my POC-before-spec r
|
|||||||
| ID | Requirement | Status |
|
| ID | Requirement | Status |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| ARN-001 | Sync chain PRODUCT → CHANGE_MASTER → BOM → DOCUMENTS in config-driven order, executed by the worker on approval | Planned |
|
| ARN-001 | Sync chain PRODUCT → CHANGE_MASTER → BOM → DOCUMENTS in config-driven order, executed by the worker on approval | Planned |
|
||||||
| ARN-002 | Approval domain as an on-stack RAP BO: staging fields, state machine PENDING → APPROVED → EXECUTING → SYNCED \| FAILED, REJECTED terminal, FAILED → retrySync → EXECUTING (amended 2026-09-14: a terminal success state is required so an approved record is never executed twice); approve/reject/retrySync as RAP actions with instance feature control; audit fields (`RequestedBy`/`RequestedOn`/`DecidedBy`/`DecidedOn`); execution failure is a distinct state, never overloaded onto rejection (predecessor defect, `docs/predecessor-flow.md` §2) | Planned |
|
| ARN-002 | Approval domain as an on-stack RAP BO: staging fields, state machine PENDING → APPROVED → EXECUTING → SYNCED \| FAILED, REJECTED terminal, FAILED → retrySync → APPROVED (amended 2026-09-14: a terminal success state is required so an approved record is never executed twice; amended 2026-09-16: retry returns the record to APPROVED so the worker re-claims it through `markExecuting`, the only way into EXECUTING. A retry that jumped straight to EXECUTING was never picked up, or it risked a second concurrent execution); duplicate staging of the same `ItemNumber`+`ChangeType`+`ArenaEcoId` is refused on create while a non-REJECTED record exists (2026-09-16); approve/reject/retrySync as RAP actions with instance feature control; audit fields (`RequestedBy`/`RequestedOn`/`DecidedBy`/`DecidedOn`); execution failure is a distinct state, never overloaded onto rejection (predecessor defect, `docs/predecessor-flow.md` §2) | Planned |
|
||||||
| ARN-003 | Durable persistence & audit — satisfied structurally by in-tenant RAP persistence (the predecessor lost its queue on every restart) | Planned (by design) |
|
| ARN-003 | Durable persistence & audit — satisfied structurally by in-tenant RAP persistence (the predecessor lost its queue on every restart) | Planned (by design) |
|
||||||
| ARN-004 | Business mappings maintained in-tenant — evaluate Business Configuration maintenance objects (Custom Business Configurations app) before any file-based config | Planned |
|
| ARN-004 | Business mappings maintained in-tenant — evaluate Business Configuration maintenance objects (Custom Business Configurations app) before any file-based config | Planned |
|
||||||
| ARN-005 | Arena ingestion via webhook + delta poll in the connectivity worker | Planned |
|
| ARN-005 | Arena ingestion via webhook + delta poll in the connectivity worker | Planned |
|
||||||
@@ -94,10 +94,10 @@ once V-1 opens the development tenant.
|
|||||||
| ID | Requirement | Source |
|
| ID | Requirement | Source |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| RAP-1 | Behavior definition declares `approve`, `reject`, `retrySync` with `( features : instance )` and implements `get_instance_features` so the UI service emits `Core.OperationAvailable`; approve/reject only for PENDING, retrySync only for FAILED. `approve` writes no `SapChangeNumber` (execution does). The CDS emits numeric `StatusCriticality` 2/2/5/3/1/1 for PENDING/APPROVED/EXECUTING/SYNCED/REJECTED/FAILED (5 = Information is V4-only) | [source: abap-docs /abap-docs-standard/ABENBDL_ACTIONS_FC; sap-docs /abap-fiori-showcase/01_general_features#operations-and-actions, verified 2026-09-14] |
|
| RAP-1 | Behavior definition declares `approve`, `reject`, `retrySync` with `( features : instance )` and implements `get_instance_features` so the UI service emits `Core.OperationAvailable`; approve/reject only for PENDING, retrySync only for FAILED. `approve` writes no `SapChangeNumber` (execution does). The CDS emits numeric `StatusCriticality` 2/2/5/3/1/1 for PENDING/APPROVED/EXECUTING/SYNCED/REJECTED/FAILED (5 = Information is V4-only) | [source: abap-docs /abap-docs-standard/ABENBDL_ACTIONS_FC; sap-docs /abap-fiori-showcase/01_general_features#operations-and-actions, verified 2026-09-14] |
|
||||||
| RAP-2 | `reject` takes a mandatory `RejectReason` (char 255) parameter; `approve`/`retrySync` return `$self`; the entity is read-only from the UI (no create/update/delete) | UI contract (annotation.xml, mock metadata) |
|
| RAP-2 | `reject` takes a mandatory `RejectReason` (char 255) parameter; `approve`/`retrySync` return `$self`; the entity is read-only from the UI (no create/update/delete). **Partly met for the POC (2026-09-16):** the handler refuses an empty reason server-side. A metadata-level mandatory marker needs a root abstract entity, an abstract BDEF with `field ( mandatory : execute )` and a `deep parameter`, and is deferred until after the UI POC [source: abap-docs /abap-fiori-showcase/01_general_features #MandatoryParameter; /abap-docs-cloud/ABENBDL_ACTION_INPUT_PARAM, verified 2026-09-16 by my review] | UI contract (annotation.xml, mock metadata) |
|
||||||
| RAP-3 | `Status` carries a text element (`@ObjectModel.text.element`, `@UI.textArrangement: #TEXT_ONLY`) so the UI shows text, not codes | [source: fiori-mcp-server "Text and Text Arrangement", verified 2026-09-14] |
|
| RAP-3 | `Status` carries a text element (`@ObjectModel.text.element`, `@UI.textArrangement: #TEXT_ONLY`) so the UI shows text, not codes | [source: fiori-mcp-server "Text and Text Arrangement", verified 2026-09-14] |
|
||||||
| RAP-4 | Semantic key `ItemNumber` (`@ObjectModel.semanticKey`); `Status` and `RequestedOn` sortable/filterable | UI default variant |
|
| RAP-4 | Semantic key `ItemNumber` (`@ObjectModel.semanticKey`); `Status` and `RequestedOn` sortable/filterable | UI default variant |
|
||||||
| RAP-5 | Two service bindings on definition `ZUI_ARENA_CHANGE_APPROVAL`: OData V4 **UI** binding `ZUI_ARENA_CHANGE_APPROVAL_O4` for the app and an OData V4 **Web API** binding for the worker; the binding name sets the URL first segment and the namespace; hand back the live `$metadata` of the UI binding to reconcile (V-2) | [source: sap-docs sap-help loio 5717c0a1dafc4335add0207b81c39cef, 5f45f125dfe84c20beb18f5de1512f5a, verified 2026-09-14] |
|
| RAP-5 | (Amended 2026-09-16.) Two service definitions, one binding each. `ZUI_ARENA_CHANGE_APPROVAL` exposes the UI projection as `ChangeApproval` through the OData V4 **UI** binding `ZUI_ARENA_CHANGE_APPR_O4`. `ZAPI_ARENA_CHANGE_APPROVAL` exposes the A2X projection through the OData V4 **Web API** binding `ZAPI_ARENA_CHANGE_APPR_A2X`. Binding names are 26 characters or fewer. The binding name sets the URL's first segment; hand back the UI binding's live `$metadata` to reconcile (V-2). The single-definition wording of 2026-09-14 could not hold, because a projection BDEF exposes only what it lists | [source: sap-docs sap-help loio 5717c0a1dafc4335add0207b81c39cef, 5f45f125dfe84c20beb18f5de1512f5a, verified 2026-09-14; binding-name limit: sap-docs sap-help loio 4bad0d7280b742a68dd2d42b1441f09a, verified 2026-09-16; abap-docs /abap-docs-cloud/ABENBDL_USE_PROJECTION, verified 2026-09-15] |
|
||||||
| RAP-6 | Worker-callable execution-state actions on the A2X binding — `markExecuting`, `markSynced` (sets `SapChangeNumber`), `markFailed` (sets `ErrorMessage`) — with instance feature control so the UI never offers Approve on an executing record | [source: abap-docs /abap-docs-cloud/ABENBDL_ACTIONS_FC, verified 2026-09-14] |
|
| RAP-6 | Worker-callable execution-state actions on the A2X binding — `markExecuting`, `markSynced` (sets `SapChangeNumber`), `markFailed` (sets `ErrorMessage`) — with instance feature control so the UI never offers Approve on an executing record | [source: abap-docs /abap-docs-cloud/ABENBDL_ACTIONS_FC, verified 2026-09-14] |
|
||||||
| RAP-7 | Staged payload (`SapPreview`) is `abap.string` — CDS char elements are capped at 1333 characters and truncation would break ARN-010 | [source: abap-docs /abap-docs-cloud/ABENCDS_SELECT_LIST_ENTRY_V2, verified 2026-09-14] |
|
| RAP-7 | Staged payload (`SapPreview`) is `abap.string` — CDS char elements are capped at 1333 characters and truncation would break ARN-010 | [source: abap-docs /abap-docs-cloud/ABENCDS_SELECT_LIST_ENTRY_V2, verified 2026-09-14] |
|
||||||
| RAP-8 | Field `DecidedBy` (not `ApprovedBy`) holds approver or rejecter; semantic key `ItemNumber, ChangeType, ArenaEcoId` | UI contract |
|
| RAP-8 | Field `DecidedBy` (not `ApprovedBy`) holds approver or rejecter; semantic key `ItemNumber, ChangeType, ArenaEcoId` | UI contract |
|
||||||
@@ -112,11 +112,15 @@ once V-1 opens the development tenant.
|
|||||||
| ID | Item | Status |
|
| ID | Item | Status |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| V-1 | Developer-extensibility access on the development tenant | **Closed 2026-09-15** — `my431353.s4hana.cloud.sap`: business user AZAW holds `ZBR_DEVELOPER` (SAP_BR_DEVELOPER template), `ZBR_ADMINISTRATOR_SW_DEV`, `ZBR_DEVELOPMENT_MANAGER_DIN` and five more (live: `sap-api users getRoles`, verified 2026-09-15). V-1b done 2026-09-15 (destination `0M7DEJI`, MCP lists it). Role correct (A4C catalogs). V-1b **closed 2026-09-15**: package `ZARENA_PLM` under `ZLOCAL`; probe class created and activated via ADT MCP |
|
| V-1 | Developer-extensibility access on the development tenant | **Closed 2026-09-15** — `my431353.s4hana.cloud.sap`: business user AZAW holds `ZBR_DEVELOPER` (SAP_BR_DEVELOPER template), `ZBR_ADMINISTRATOR_SW_DEV`, `ZBR_DEVELOPMENT_MANAGER_DIN` and five more (live: `sap-api users getRoles`, verified 2026-09-15). V-1b done 2026-09-15 (destination `0M7DEJI`, MCP lists it). Role correct (A4C catalogs). V-1b **closed 2026-09-15**: package `ZARENA_PLM` under `ZLOCAL`; probe class created and activated via ADT MCP |
|
||||||
| V-2 | Custom communication scenario can expose the approval BO's A2X service to the worker's technical user; and the Fiori app's annotation namespace, `_Type` entity-type suffix and UI-binding URL segment are reconciled from the live `$metadata` of the UI binding (decided 2026-09-14; the mock cannot verify this) | Open |
|
| V-2 | Custom communication scenario can expose the approval BO's A2X service to the worker's technical user; and the Fiori app's annotation namespace, `_Type` entity-type suffix and UI-binding URL segment are reconciled from the live `$metadata` of the UI binding (decided 2026-09-14; the mock cannot verify this) | Open. **UI half in progress 2026-09-16:** live `$metadata` is captured in `docs/v2/zui_arena_change_appr_o4.metadata.xml`, and the UI host serves it to the developer user. The contract holds: all 17 property types, key, entity set, the three actions, and `RejectReason` non-nullable. Identity differs from the mock: namespace `com.sap.gateway.srvd.zui_arena_change_approval.v0001`, entity type `ChangeApprovalType`, URL segment `zui_arena_change_appr_o4`, and backend operation control via `__OperationControl`. **The app was reconciled 2026-09-16:** the dataSource targets `zui_arena_change_appr_o4`; the mock serves the live `$metadata` byte-for-byte; annotation targets use the live namespace and entity type; enablement is backend-owned; no old identity strings remain; lint, typecheck and `@ui5/linter` are clean; the mock server serves the service. Unverified until a live preview: the `DataFieldForAction` action-string form versus the backend's overload-specific `OperationAvailable` target. The A2X half (custom communication scenario, `-api` host) is still open. The service URL reported on `-api` carries `sap-client=080` |
|
||||||
| V-3 | On-stack Application Jobs vs worker-side polling for the Arena delta | Open — flow findings 2026-09-10: Arena's outbound-integration queue holds the cursor (`reconciled` flag) and is the clean delta mechanism; `/changes`-feed polling needs client-side date filtering (`docs/predecessor-flow.md` §1) |
|
| V-3 | On-stack Application Jobs vs worker-side polling for the Arena delta | Open — flow findings 2026-09-10: Arena's outbound-integration queue holds the cursor (`reconciled` flag) and is the clean delta mechanism; `/changes`-feed polling needs client-side date filtering (`docs/predecessor-flow.md` §1) |
|
||||||
| V-4 | Can a Claude Code agent outside the VS Code editor author ABAP source through the ADT MCP Server? | **Answered 2026-09-15: no.** `create_object` and the generators take structure, not source (live probe); SAP's editing MCP tool is planned for Public Edition 2702 [source: sap-docs loio 7f716223a88c40e0992777c8d9febccf, verified 2026-09-15]. Build plan: `x-ui-service` generator for the BO skeleton, source edits handed over as files for the VS Code editor until 2702 |
|
| V-4 | Can a Claude Code agent outside the VS Code editor author ABAP source through the ADT MCP Server? | **Answered 2026-09-15: no.** `create_object` and the generators take structure, not source (live probe); SAP's editing MCP tool is planned for Public Edition 2702 [source: sap-docs loio 7f716223a88c40e0992777c8d9febccf, verified 2026-09-15]. Build plan: `x-ui-service` generator for the BO skeleton, source edits handed over as files for the VS Code editor until 2702 |
|
||||||
| OQ-5 | Long-term: move the sync executor on-stack (released ABAP APIs) vs keep it in the worker — worker initially | Open |
|
| OQ-5 | Long-term: move the sync executor on-stack (released ABAP APIs) vs keep it in the worker — worker initially | Open |
|
||||||
| OQ-6 | Webhook receiver hosting and auth for the worker on CF | Open |
|
| OQ-6 | Webhook receiver hosting and auth for the worker on CF | Open |
|
||||||
|
| OQ-9 | Agent-side ABAP source read/write through the community `abap-adt-mcp` server (npm 2.1.0, a single maintainer, published 2026-09-16 01:04Z, SLSA provenance attested). It lists `getObjectSource`, `setObjectSource` and `activateObjects` next to 170 other tools, enforces `readOnly` / `allowedPackages` policy server-side, and signs in to Public Cloud through a one-time browser SSO. It would remove the manual paste step that SAP's ADT MCP forces (V-4). Checked 2026-09-16: a read-only smoke run with no login and no SAP call listed 173 tools and enforced a `ZARENA_PLM`-only read-only policy; nothing is registered. Adoption needs, in order: a source review of the package, because it would hold an SSO session into the tenant; a version pin; the development tenant only, never `my429480`; `allowedPackages: ["ZARENA_PLM"]`; read-only first; then a CLAUDE.md routing change | Open (2026-09-16), not blocking. I want it as the replacement for manual pasting; the read-only source review of the cached package (03:15) cleared every check it ran, observed as it ran. It stalled while collecting line citations for its report and was stopped at ~03:28, so no written report with file:line evidence exists. I accept it for the development tenant under the read-only-first configuration. Registered by hand in project scope at 03:37: stdio `npx -y abap-adt-mcp@2.1.0`, `MCP_READ_ONLY=1`, audit file on, `~/.abap-adt-mcp/systems.json` (development tenant `my431353`, client 100, SSO, `readOnly: true`, `allowedPackages: ["ZARENA_PLM"]`, mode 600). The other six project servers are intact. It connected after the restart, and its policy checked out live. **Parked 2026-09-16 03:49:** SSO needs an interactive sign-in inside the server's own Chrome, on the server's desktop (`:0`). That window is unreachable over my multi-hop SSH remote access; a DevTools relay was tried and dropped. The server stays registered but unused. It is revisited when a session at the server's desktop can complete one sign-in with "stay signed in", after which logins run unattended. The reentrance-ticket path was built and tested on 2026-09-18 (`tools/adt-reentrance-login/`) and **rejected on evidence**: the tenant ignores the ticket as both a `MYSAPSSO2` cookie and header, answering with its SAML redirect page (KNOWLEDGE 2026-09-18). Reviving OQ-9 requires capturing how ADT for VS Code exchanges the ticket. Until then, tenant work stays on SAP's ADT MCP plus editor paste. Until then, the UI leg continues with SAP's `adt-mcp` plus editor paste. The registration is run by hand, because Claude Code's permission guard refuses agent-side changes to its own MCP configuration (03:17, nothing written). The target is the development tenant, client 100, SSO login, first with `readOnly: true` + `allowedPackages: ["ZARENA_PLM"]` + `MCP_READ_ONLY=1`, then write enabled for `ZARENA_PLM` only |
|
||||||
|
| OQ-10 | Ownership of UI annotations: the backend metadata extension `ZC_CHANGEAPPROVAL` or the app's local `annotation.xml`. Today 35 of the 58 local annotations override a backend annotation with the same target, term and qualifier (layering rule [source: fiori-mcp-server "Visualizing and Managing Annotations with SAP Fiori Tools - Service Modeler", verified 2026-09-16]), so a DDLX change to those terms is invisible in the app. The local file adds i18n labels, action criticality, the `#Default` variants and the facet IDs the OPA journey relies on | Open (2026-09-16). Interim rule: a term defined locally is changed locally. I consolidate before launchpad deployment |
|
||||||
|
| OQ-8 | Per-operation authorization for the approval BO. Today it is `authorization master ( none )`: any holder of the UI app can approve, reject and retry, and any tenant ABAP code can create and mark* through EML. SAP Help calls `( none )` "not recommended in real-world scenarios". The target is `( global )` with a `FOR GLOBAL AUTHORIZATION` handler and `AUTHORITY-CHECK OBJECT` on a custom authorization object: decisions for business roles, create/mark* for the communication role. First verify that custom authorization objects can be created on `my431353` [source: abap-docs /abap-docs-cloud/ABENBDL_AUTHORIZATION, ABAPHANDLER_METH_GLOBAL_AUTH; sap-docs sap-help loio 673c05562e144702a080c574c3da2da3, verified 2026-09-16 by my review] | Open (2026-09-16): does not block the dev-tenant UI POC; must close before any business role other than the developer's gets the app (IAM brief) |
|
||||||
|
| OQ-7 | Tenant object naming and cleanup for the approval BO | **Closed 2026-09-16.** The package was deleted in ADT, contents and package itself (verified live through ADT MCP validation, 2026-09-16). I build from scratch, without the generator: table `ZCHANGEAPPROVAL`, `ZR_CHANGEAPPROVAL`, `ZBP_R_CHANGEAPPROVAL`, `ZC_CHANGEAPPROVAL`, SRVD `ZUI_ARENA_CHANGE_APPROVAL` with SRVB `ZUI_ARENA_CHANGE_APPR_O4`, and SRVD `ZAPI_ARENA_CHANGE_APPROVAL` with SRVB `ZAPI_ARENA_CHANGE_APPR_A2X`. Binding names follow SAP's 26-character limit (RAP-5). Entity set is `ChangeApproval`; the A2X projection and parameter entity names are unchanged |
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
@@ -128,6 +132,6 @@ once V-1 opens the development tenant.
|
|||||||
| Specialist agent roster (5 agents, docs/agent-roster.md) | Done (2026-09-04; all 5 files YAML-verified in .claude/agents/) |
|
| Specialist agent roster (5 agents, docs/agent-roster.md) | Done (2026-09-04; all 5 files YAML-verified in .claude/agents/) |
|
||||||
| V-1…V-3 verification POC | Planned — V-1 (ADT access on my429480) is the user-side gate for everything on-stack |
|
| V-1…V-3 verification POC | Planned — V-1 (ADT access on my429480) is the user-side gate for everything on-stack |
|
||||||
| Fiori Elements app (`app/`, mock-bound) | Done for the mock stage (2026-09-14) — gateway service shape (binding-derived identity), six-state domain, polished List Report/Object Page, launchpad inbound `ArenaChangeApproval-manage`; SAP review findings closed. Deploy gated on V-1/V-2 (live `$metadata` reconciliation) |
|
| Fiori Elements app (`app/`, mock-bound) | Done for the mock stage (2026-09-14) — gateway service shape (binding-derived identity), six-state domain, polished List Report/Object Page, launchpad inbound `ArenaChangeApproval-manage`; SAP review findings closed. Deploy gated on V-1/V-2 (live `$metadata` reconciliation) |
|
||||||
| RAP BO (approval domain) | In progress — skeleton generated and active in `ZARENA_PLM` (2026-09-15, ATC clean); **waiting on owner: paste `docs/handover/rap/` sources in VS Code per its README**, then I activate/ATC/create the A2X BDEF + binding; IAM app/catalog and publishing are the next brief |
|
| RAP BO (approval domain) | In progress (2026-09-16): from-scratch build in package `ZARENA_PLM` (under `ZLOCAL`, no transport recording) under the clean names (OQ-7 closed). The handover in `docs/handover/rap/` has all pre-paste review fixes applied and was stable as of 02:45. Wave A (U1–U5: table, three parameter entities, root view) was created empty at 02:30. Its sources were pasted at 02:47, and all five activated at 02:49 with no errors, `preserving type` included. Wave B (U6 base BDEF, U10 root access control) was created empty at 02:50, its sources were pasted at 02:53, and both activated at 02:55 (BDEF through `.bdef.abdl`). Wave C (U7 behavior pool, U8 UI projection view) was pasted at ~03:00 and activated at 03:03 (U6 and U7 together, then U8), with no errors. It is not yet confirmed that the handler source sits in the class's Local Types include; until it is, activation does not prove the three flagged constructs. Wave D (U9 projection BDEF, U11 access control, U12 metadata extension, U13 UI service definition) was created empty at 03:05 and pasted at ~04:12. U9, U11 and U12 activated at 04:15. U13 activation failed, listing "Comments are not supported and will be deleted on save" (Ln 1) and "oData V4 services with no draft capability will be primarily READ-ONLY" (Ln 21, expected by design). After a comment-free re-save, U13 activated at 04:18. U14 binding `ZUI_ARENA_CHANGE_APPR_O4` (OData V4 UI) was created and activated. **The UI leg is active: all 14 objects; ATC 0 errors, 0 warnings, 43 infos.** **Published 2026-09-16 04:26** through the ADT Publish code lens: `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`, entity set `ChangeApproval` (`fetch_service_information`). The table is empty until the worker leg creates records, because the UI exposes no create. V-2 app reconciliation done. **Worker (A2X) leg ACTIVE 2026-09-16 06:37:** W1 view `ZC_CHANGEAPPROVAL_A2X`, W2 access control, W3 projection BDEF (create, markExecuting, markSynced, markFailed), W4 service definition `ZAPI_ARENA_CHANGE_APPROVAL` and W5 OData V4 Web API binding `ZAPI_ARENA_CHANGE_APPR_A2X` (unpublished, service `0001`). ATC on the 5 worker objects: 0 errors, 1 priority-3 warning (W607 on `ZC_CHANGEAPPROVAL_A2X`: `authorizationCheck: #CHECK` should be `#MANDATORY`), deferred to OQ-8. **The approval BO is complete on the development tenant (19 objects).** Next: expose W5 to the worker's communication user through a custom communication scenario (V-2 A2X half); IAM app, business catalog and role for the UI app (ARN-007); OQ-8 authorization before other business users. The table source goes into `.tabl.ddic`; `.tabl.json` holds only header metadata. (3) UI leg, 14 sources ending with binding `ZUI_ARENA_CHANGE_APPR_O4`: I create each object through the MCP, its source is pasted in the VS Code editor, and I activate it. The `markSynced`/`markFailed` parameter entities belong to this leg, because the base BDEF references them. (4) Publish the UI binding, then run V-2 reconciliation of the app against its live `$metadata`. (5) Worker (A2X) leg, 4 sources. Syntax proven only by tenant activation, still open: `OPTIONS PRIVILEGED ACCESS` in `checkDuplicate` (fallback `FROM … WITH PRIVILEGED ACCESS`), `%op-%action` on MODIFY response rows, `preserving type` on the U5 boolean casts |
|
||||||
| Connectivity worker | Review wave Done (2026-09-14) — TTL wiring, cursor-after-success, buffer-full = tick failure, health conformance, strict JSON-only validation, bounded buffer, graceful drain, HTTP timeout, fixed-phrase errors; 137 tests green incl. the schema-driven contract-conformance suite. Next: Arena client POC (live), executor leg gated on V-2. **Do not deploy the scaffold**: the ingest buffer has no consumer yet, so a long-running instance would answer 503 after `WEBHOOK_QUEUE_MAX_SIZE` events. Next: Arena client POC (live), executor leg gated on V-2 |
|
| Connectivity worker | Review wave Done (2026-09-14) — TTL wiring, cursor-after-success, buffer-full = tick failure, health conformance, strict JSON-only validation, bounded buffer, graceful drain, HTTP timeout, fixed-phrase errors; 137 tests green incl. the schema-driven contract-conformance suite. Next: Arena client POC (live), executor leg gated on V-2. **Do not deploy the scaffold**: the ingest buffer has no consumer yet, so a long-running instance would answer 503 after `WEBHOOK_QUEUE_MAX_SIZE` events. Next: Arena client POC (live), executor leg gated on V-2 |
|
||||||
| Worker API contract (`docs/api/worker.openapi.json`) | v0.2.1 Done (2026-09-14) — health enums/`degraded`/`poller`, strict `SyncRunRequest`, webhook 503, 413/415 + `default` on every operation, reserved 502, `DOCUMENTS` token; `x-sap-stateInfo: beta`, provisional webhook schema pending Arena POC |
|
| Worker API contract (`docs/api/worker.openapi.json`) | v0.2.1 Done (2026-09-14) — health enums/`degraded`/`poller`, strict `SyncRunRequest`, webhook 503, 413/415 + `default` on every operation, reserved 502, `DOCUMENTS` token; `x-sap-stateInfo: beta`, provisional webhook schema pending Arena POC |
|
||||||
|
|||||||
+41
-24
@@ -17,42 +17,55 @@
|
|||||||
|**Add Eslint configuration**<br>True, see https://www.npmjs.com/package/@sap-ux/eslint-plugin-fiori-tools#rules for the eslint rules.|
|
|**Add Eslint configuration**<br>True, see https://www.npmjs.com/package/@sap-ux/eslint-plugin-fiori-tools#rules for the eslint rules.|
|
||||||
|**Main Entity**<br>ChangeApprovals|
|
|**Main Entity**<br>ChangeApprovals|
|
||||||
|
|
||||||
## Service binding (provisional)
|
## Service binding (live, reconciled 2026-09-16 - PRD V-2)
|
||||||
|
|
||||||
The app binds to the RAP **UI** service binding, not the A2X binding the worker uses.
|
The app binds to the RAP **UI** service binding, not the A2X binding the worker uses.
|
||||||
The single source of truth for the service identity is the header comment of
|
The single source of truth for the service identity is the live `$metadata` of the published
|
||||||
`webapp/localService/mainService/metadata.xml`:
|
binding, captured 2026-09-16 as `docs/v2/zui_arena_change_appr_o4.metadata.xml` (repo root).
|
||||||
|
`webapp/localService/mainService/metadata.xml` is a **byte-identical copy** of it, so the mock
|
||||||
|
server serves the real contract. Refresh both together; never hand-edit the copy.
|
||||||
|
|
||||||
| Item | Value |
|
| Item | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Service binding (OData V4 - UI) | `ZUI_ARENA_CHANGE_APPROVAL_O4` |
|
| Service binding (OData V4 - UI) | `ZUI_ARENA_CHANGE_APPR_O4` |
|
||||||
| Service definition | `ZUI_ARENA_CHANGE_APPROVAL` |
|
| Service definition | `ZUI_ARENA_CHANGE_APPROVAL` |
|
||||||
| Service URL | `/sap/opu/odata4/sap/zui_arena_change_approval_o4/srvd/sap/zui_arena_change_approval/0001/` |
|
| Service URL | `/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/` |
|
||||||
| Schema namespace / alias | `com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001` / `SAP__self` |
|
| Schema namespace / alias | `com.sap.gateway.srvd.zui_arena_change_approval.v0001` / `SAP__self` |
|
||||||
| Entity type / entity set | `ChangeApproval_Type` / `ChangeApproval` |
|
| Entity type / entity set | `ChangeApprovalType` / `ChangeApproval` |
|
||||||
|
| Bound actions | `approve`, `reject(RejectReason)`, `retrySync` |
|
||||||
|
|
||||||
The first URL segment and the schema namespace are derived from the service **binding** name,
|
Only the first URL segment comes from the service **binding** name. The schema namespace comes
|
||||||
the segment after `srvd/sap/` from the service **definition** name - strongly indicated by the
|
from the service **definition** name, and the entity type carries a `Type` suffix without an
|
||||||
tenant's `/sap/opu/odata4/sap/api_currencyexchangerate/srvd_a2x/sap/currencyexchangerate/0001`
|
underscore (live `$metadata`, 2026-09-16; see `KNOWLEDGE.md`). The pre-V-2 assumption of a
|
||||||
(namespace `com.sap.gateway.srvd_a2x.api_currencyexchangerate.v0001`) and SAP Help's
|
binding-derived namespace and a `_Type` suffix was wrong.
|
||||||
`/sap/opu/odata4/sap/api_product/srvd_a2x/sap/product/0002`
|
|
||||||
[source: sap-docs sap-help loio 5f45f125dfe84c20beb18f5de1512f5a, verified 2026-09-14].
|
|
||||||
|
|
||||||
`webapp/manifest.json` (`sap.app.dataSources.mainService.uri`, routing) and
|
`webapp/manifest.json` (`sap.app.dataSources.mainService.uri`), `ui5-mock.yaml` /
|
||||||
`webapp/annotations/annotation.xml` (`edmx:Reference`, every `Annotations Target`, every
|
`ui5-local.yaml` (`sap-fe-mockserver` `urlPath`) and `webapp/annotations/annotation.xml`
|
||||||
`DataFieldForAction.Action`) must stay in sync with it. JSON cannot carry comments, so the
|
(`edmx:Reference`, every `Annotations Target`, every `DataFieldForAction.Action`) must stay in
|
||||||
manifest references this note instead.
|
sync with it. JSON cannot carry comments, so the manifest references this note instead.
|
||||||
|
|
||||||
**Provisional - namespace = binding name is strongly indicated, not proven; reconcile namespace,
|
**Annotation layering.** The backend metadata extension already emits most UI annotations
|
||||||
entity type suffix and URL segments against the live `$metadata` of the RAP UI service binding
|
(LineItem, HeaderInfo, Facets, FieldGroups, Identification, SelectionFields, SemanticKey,
|
||||||
at PRD V-2 before deploy.**
|
`Status` text with `TextOnly`). An annotation in `annotation.xml` replaces the backend annotation
|
||||||
|
with the same term + qualifier on the same target, and deleting it there makes the backend one
|
||||||
|
effective again [source: fiori-mcp-server "Visualizing and Managing Annotations with SAP Fiori
|
||||||
|
Tools - Service Modeler", verified 2026-09-16].
|
||||||
|
|
||||||
|
**Backend for a non-mock run: not configured.** `ui5.yaml` and `ui5-local.yaml` carry
|
||||||
|
`fiori-tools-proxy` with `backend: []`, and `npm start` is a generator placeholder. When a live
|
||||||
|
preview is added, the backend is the **UI host** `https://my431353.s4hana.cloud.sap`, never the
|
||||||
|
`-api` host: a business-user call there is blocked by UCON (live 2026-09-16, `KNOWLEDGE.md`).
|
||||||
|
|
||||||
## Status model and actions
|
## Status model and actions
|
||||||
|
|
||||||
`Status`: `PENDING -> APPROVED -> EXECUTING -> SYNCED | FAILED`; `REJECTED` is terminal;
|
`Status`: `PENDING -> APPROVED -> EXECUTING -> SYNCED | FAILED`; `REJECTED` is terminal;
|
||||||
`FAILED -> retrySync -> EXECUTING`. `approve`/`reject` are enabled only for `PENDING`,
|
`FAILED -> retrySync -> APPROVED`. `approve`/`reject` are enabled only for `PENDING`,
|
||||||
`retrySync` only for `FAILED` (mocked via `IsApprovable` / `IsRetriable`, which stand in for the
|
`retrySync` only for `FAILED`. The backend owns enablement: RAP emits
|
||||||
`Core.OperationAvailable` RAP emits for `( features : instance )` actions - PRD RAP-1).
|
`Core.OperationAvailable Path="_it/__OperationControl/<action>"` for the `( features : instance )`
|
||||||
|
actions (PRD RAP-1), and the app keeps no local `OperationAvailable`, which would replace it
|
||||||
|
[source: fiori-mcp-server "Action Control for Context-Dependent Actions (SAP Fiori Elements)",
|
||||||
|
verified 2026-09-16]. `IsApprovable` / `IsRetriable` are still in the service but hidden, and
|
||||||
|
nothing in the app binds to them.
|
||||||
Actions are offered on the object page header only; the list report table is display-only
|
Actions are offered on the object page header only; the list report table is display-only
|
||||||
(`selectionMode: "None"`).
|
(`selectionMode: "None"`).
|
||||||
|
|
||||||
@@ -68,7 +81,11 @@ required for a custom app (only `archeType` is mandatory)
|
|||||||
|
|
||||||
Dev server: `npm run start-mock` / `npm run int-test` bind to port **8082** (8080 is taken on
|
Dev server: `npm run start-mock` / `npm run int-test` bind to port **8082** (8080 is taken on
|
||||||
the dev host). Mock data lives in `webapp/localService/mainService/data/ChangeApproval.json`
|
the dev host). Mock data lives in `webapp/localService/mainService/data/ChangeApproval.json`
|
||||||
(`generateMockData: false`), 8 rows covering every status.
|
(`generateMockData: false`), 8 rows covering every status. Each row carries every live property:
|
||||||
|
`StatusText` / `StatusCriticality` as `ZR_CHANGEAPPROVAL` computes them, `__OperationControl`
|
||||||
|
consistent with the status, `LocalLastChangedAt`, and `SAP__Messages: []`. Initial CHAR values are
|
||||||
|
`""`, not `null`, on `Nullable="false"` properties. The mock server does not execute bound
|
||||||
|
actions (see `KNOWLEDGE.md`), so status transitions show only against the live service.
|
||||||
|
|
||||||
## app
|
## app
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ server:
|
|||||||
configuration:
|
configuration:
|
||||||
mountPath: /
|
mountPath: /
|
||||||
services:
|
services:
|
||||||
- urlPath: /sap/opu/odata4/sap/zui_arena_change_approval_o4/srvd/sap/zui_arena_change_approval/0001/
|
- urlPath: /sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/
|
||||||
metadataPath: ./webapp/localService/mainService/metadata.xml
|
metadataPath: ./webapp/localService/mainService/metadata.xml
|
||||||
mockdataPath: ./webapp/localService/mainService/data
|
mockdataPath: ./webapp/localService/mainService/data
|
||||||
generateMockData: false
|
generateMockData: false
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ server:
|
|||||||
configuration:
|
configuration:
|
||||||
mountPath: /
|
mountPath: /
|
||||||
services:
|
services:
|
||||||
- urlPath: /sap/opu/odata4/sap/zui_arena_change_approval_o4/srvd/sap/zui_arena_change_approval/0001/
|
- urlPath: /sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/
|
||||||
metadataPath: ./webapp/localService/mainService/metadata.xml
|
metadataPath: ./webapp/localService/mainService/metadata.xml
|
||||||
mockdataPath: ./webapp/localService/mainService/data
|
mockdataPath: ./webapp/localService/mainService/data
|
||||||
generateMockData: false
|
generateMockData: false
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
Local UI annotations for the Arena PLM change-approval Fiori Elements app.
|
Local UI annotations for the Arena PLM change-approval Fiori Elements app.
|
||||||
Service identity (binding, service definition, version, namespace) is defined ONCE in
|
Service identity is the live $metadata of RAP UI binding ZUI_ARENA_CHANGE_APPR_O4 (PRD V-2, reconciled
|
||||||
webapp/localService/mainService/metadata.xml - keep this file's edmx:Reference, every
|
2026-09-16): webapp/localService/mainService/metadata.xml is a byte-identical copy of
|
||||||
Annotations Target and every DataFieldForAction.Action in sync with it.
|
docs/v2/zui_arena_change_appr_o4.metadata.xml. URL /sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/
|
||||||
Provisional - namespace = binding name is strongly indicated, not proven; reconcile namespace,
|
zui_arena_change_approval/0001/ (first segment = binding), schema namespace
|
||||||
entity type suffix and URL segments against the live $metadata of the RAP UI service binding
|
com.sap.gateway.srvd.zui_arena_change_approval.v0001 (from the service DEFINITION), entity type
|
||||||
at PRD V-2 before deploy.
|
ChangeApprovalType. Keep this file's edmx:Reference, every Annotations Target and every
|
||||||
|
DataFieldForAction.Action in sync with it.
|
||||||
|
Layering: the backend (metadata extension ZC_CHANGEAPPROVAL) already emits most UI annotations. An
|
||||||
|
annotation in this file replaces the backend annotation with the same term + qualifier on the same
|
||||||
|
target; deleting it here makes the backend one effective again
|
||||||
|
[source: fiori-mcp-server "Visualizing and Managing Annotations with SAP Fiori Tools - Service Modeler",
|
||||||
|
verified 2026-09-16].
|
||||||
-->
|
-->
|
||||||
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
|
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
|
||||||
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
|
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
|
||||||
@@ -15,8 +21,8 @@
|
|||||||
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
|
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
|
||||||
<edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="Common"/>
|
<edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="Common"/>
|
||||||
</edmx:Reference>
|
</edmx:Reference>
|
||||||
<edmx:Reference Uri="/sap/opu/odata4/sap/zui_arena_change_approval_o4/srvd/sap/zui_arena_change_approval/0001/$metadata">
|
<edmx:Reference Uri="/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/$metadata">
|
||||||
<edmx:Include Namespace="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001"/>
|
<edmx:Include Namespace="com.sap.gateway.srvd.zui_arena_change_approval.v0001"/>
|
||||||
</edmx:Reference>
|
</edmx:Reference>
|
||||||
<edmx:DataServices>
|
<edmx:DataServices>
|
||||||
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="local">
|
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="local">
|
||||||
@@ -29,92 +35,92 @@
|
|||||||
"Enabling Table Personalization in SAP Fiori Elements (OData V2 & V4)";
|
"Enabling Table Personalization in SAP Fiori Elements (OData V2 & V4)";
|
||||||
"Filter Fields - Add, Configure, Move, Delete...", verified 2026-09-14]
|
"Filter Fields - Add, Configure, Move, Delete...", verified 2026-09-14]
|
||||||
============================================================================ -->
|
============================================================================ -->
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/ID">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/ID">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_ID}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_ID}"/>
|
||||||
<Annotation Term="UI.Hidden" Bool="true"/>
|
<Annotation Term="UI.Hidden" Bool="true"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/ItemNumber">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/ItemNumber">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_ITEM}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_ITEM}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/ItemName">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/ItemName">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_NAME}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_NAME}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/ChangeType">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/ChangeType">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_CHANGE_TYPE}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_CHANGE_TYPE}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/Status">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/Status">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_STATUS}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_STATUS}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/StatusCriticality">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/StatusCriticality">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_STATUS_CRITICALITY}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_STATUS_CRITICALITY}"/>
|
||||||
<Annotation Term="UI.Hidden" Bool="true"/>
|
<Annotation Term="UI.Hidden" Bool="true"/>
|
||||||
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/RequestedBy">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/RequestedBy">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_REQUESTED_BY}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_REQUESTED_BY}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/RequestedOn">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/RequestedOn">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_REQUESTED_ON}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_REQUESTED_ON}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/DecidedBy">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/DecidedBy">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_DECIDED_BY}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_DECIDED_BY}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/DecidedOn">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/DecidedOn">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_DECIDED_ON}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_DECIDED_ON}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/ArenaEcoId">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/ArenaEcoId">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_ARENA_ECO}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_ARENA_ECO}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/SapChangeNumber">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/SapChangeNumber">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_SAP_CHANGE_NO}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_SAP_CHANGE_NO}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/ErrorMessage">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/ErrorMessage">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_ERROR_MSG}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_ERROR_MSG}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/RejectReason">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/RejectReason">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_REJECT_REASON}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_REJECT_REASON}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/SapPreview">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/SapPreview">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_STAGED_PAYLOAD}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_STAGED_PAYLOAD}"/>
|
||||||
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/ReadOnly"/>
|
||||||
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
||||||
<!-- long JSON payload: render as text area [source: fiori-mcp-server "Text and Text Arrangement" sample (@UI.MultiLineText), verified 2026-09-14] -->
|
<!-- long JSON payload: render as text area [source: fiori-mcp-server "Text and Text Arrangement" sample (@UI.MultiLineText), verified 2026-09-14] -->
|
||||||
<Annotation Term="UI.MultiLineText" Bool="true"/>
|
<Annotation Term="UI.MultiLineText" Bool="true"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/IsApprovable">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/IsApprovable">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_IS_APPROVABLE}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_IS_APPROVABLE}"/>
|
||||||
<Annotation Term="UI.Hidden" Bool="true"/>
|
<Annotation Term="UI.Hidden" Bool="true"/>
|
||||||
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type/IsRetriable">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType/IsRetriable">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_IS_RETRIABLE}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_IS_RETRIABLE}"/>
|
||||||
<Annotation Term="UI.Hidden" Bool="true"/>
|
<Annotation Term="UI.Hidden" Bool="true"/>
|
||||||
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
<Annotation Term="UI.HiddenFilter" Bool="true"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
|
|
||||||
<!-- Action parameter label for the reject dialog -->
|
<!-- Action parameter label for the reject dialog -->
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.reject(com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type)/RejectReason">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.reject(com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType)/RejectReason">
|
||||||
<Annotation Term="Common.Label" String="{@i18n>FIELD_REJECT_REASON}"/>
|
<Annotation Term="Common.Label" String="{@i18n>FIELD_REJECT_REASON}"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
|
|
||||||
<!-- ============================================================================
|
<!-- ============================================================================
|
||||||
Entity-type annotations
|
Entity-type annotations
|
||||||
============================================================================ -->
|
============================================================================ -->
|
||||||
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type">
|
<Annotations Target="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType">
|
||||||
|
|
||||||
<!-- Semantic key: bold ObjectIdentifier in responsive tables
|
<!-- Semantic key: bold ObjectIdentifier in responsive tables
|
||||||
[source: fiori-mcp-server "Handling Semantic Key Fields in SAP Fiori Elements", verified 2026-09-14] -->
|
[source: fiori-mcp-server "Handling Semantic Key Fields in SAP Fiori Elements", verified 2026-09-14] -->
|
||||||
@@ -255,28 +261,30 @@
|
|||||||
|
|
||||||
<!-- Object page header actions (the ONLY place approve/reject/retry are offered; the list
|
<!-- Object page header actions (the ONLY place approve/reject/retry are offered; the list
|
||||||
report table is display-only with selectionMode None in the manifest).
|
report table is display-only with selectionMode None in the manifest).
|
||||||
Enablement comes from Core.OperationAvailable in the service metadata
|
Enablement is owned by the backend: RAP emits Core.OperationAvailable
|
||||||
(approve/reject: IsApprovable, retrySync: IsRetriable).
|
Path="_it/__OperationControl/<action>" for the ( features : instance ) actions (live $metadata
|
||||||
[source: fiori-mcp-server "Action Control for Context-Dependent Actions";
|
2026-09-16). Deliberately NO OperationAvailable in this file - a local one would replace the
|
||||||
sap-docs /sapui5/06_SAP_Fiori_Elements/actions-cbf16c5#enabling-custom-actions-defined-using-annotations,
|
backend annotation on the same target.
|
||||||
verified 2026-09-14] -->
|
[source: fiori-mcp-server "Action Control for Context-Dependent Actions (SAP Fiori Elements)";
|
||||||
|
"Enable Header Actions on the Object Page (UI.Identification / DataFieldForAction)";
|
||||||
|
"Visualizing and Managing Annotations with SAP Fiori Tools - Service Modeler", verified 2026-09-16] -->
|
||||||
<Annotation Term="UI.Identification">
|
<Annotation Term="UI.Identification">
|
||||||
<Collection>
|
<Collection>
|
||||||
<Record Type="UI.DataFieldForAction">
|
<Record Type="UI.DataFieldForAction">
|
||||||
<PropertyValue Property="Label" String="{@i18n>ACTION_APPROVE}"/>
|
<PropertyValue Property="Label" String="{@i18n>ACTION_APPROVE}"/>
|
||||||
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.approve"/>
|
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval.v0001.approve"/>
|
||||||
<PropertyValue Property="Determining" Bool="false"/>
|
<PropertyValue Property="Determining" Bool="false"/>
|
||||||
<PropertyValue Property="Criticality" EnumMember="UI.CriticalityType/Positive"/>
|
<PropertyValue Property="Criticality" EnumMember="UI.CriticalityType/Positive"/>
|
||||||
</Record>
|
</Record>
|
||||||
<Record Type="UI.DataFieldForAction">
|
<Record Type="UI.DataFieldForAction">
|
||||||
<PropertyValue Property="Label" String="{@i18n>ACTION_REJECT}"/>
|
<PropertyValue Property="Label" String="{@i18n>ACTION_REJECT}"/>
|
||||||
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.reject"/>
|
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval.v0001.reject"/>
|
||||||
<PropertyValue Property="Determining" Bool="false"/>
|
<PropertyValue Property="Determining" Bool="false"/>
|
||||||
<PropertyValue Property="Criticality" EnumMember="UI.CriticalityType/Negative"/>
|
<PropertyValue Property="Criticality" EnumMember="UI.CriticalityType/Negative"/>
|
||||||
</Record>
|
</Record>
|
||||||
<Record Type="UI.DataFieldForAction">
|
<Record Type="UI.DataFieldForAction">
|
||||||
<PropertyValue Property="Label" String="{@i18n>ACTION_RETRY}"/>
|
<PropertyValue Property="Label" String="{@i18n>ACTION_RETRY}"/>
|
||||||
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.retrySync"/>
|
<PropertyValue Property="Action" String="com.sap.gateway.srvd.zui_arena_change_approval.v0001.retrySync"/>
|
||||||
<PropertyValue Property="Determining" Bool="false"/>
|
<PropertyValue Property="Determining" Bool="false"/>
|
||||||
<PropertyValue Property="Criticality" EnumMember="UI.CriticalityType/Critical"/>
|
<PropertyValue Property="Criticality" EnumMember="UI.CriticalityType/Critical"/>
|
||||||
</Record>
|
</Record>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ CHANGE_APPROVALS_TITLE=Change Approvals
|
|||||||
ACTION_APPROVE=Approve
|
ACTION_APPROVE=Approve
|
||||||
#XBUT: Rejects the staged change; asks for a reason
|
#XBUT: Rejects the staged change; asks for a reason
|
||||||
ACTION_REJECT=Reject
|
ACTION_REJECT=Reject
|
||||||
#XBUT: Re-runs a FAILED SAP sync (FAILED -> EXECUTING)
|
#XBUT: Re-runs a FAILED SAP sync (FAILED -> APPROVED)
|
||||||
ACTION_RETRY=Retry Sync
|
ACTION_RETRY=Retry Sync
|
||||||
|
|
||||||
#XFLD: Arena item number (e.g. MC-100-0042)
|
#XFLD: Arena item number (e.g. MC-100-0042)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ CHANGE_APPROVALS_TITLE=Change Approvals
|
|||||||
ACTION_APPROVE=Approve
|
ACTION_APPROVE=Approve
|
||||||
#XBUT: Rejects the staged change; asks for a reason
|
#XBUT: Rejects the staged change; asks for a reason
|
||||||
ACTION_REJECT=Reject
|
ACTION_REJECT=Reject
|
||||||
#XBUT: Re-runs a FAILED SAP sync (FAILED -> EXECUTING)
|
#XBUT: Re-runs a FAILED SAP sync (FAILED -> APPROVED)
|
||||||
ACTION_RETRY=Retry Sync
|
ACTION_RETRY=Retry Sync
|
||||||
|
|
||||||
#XFLD: Arena item number (e.g. MC-100-0042)
|
#XFLD: Arena item number (e.g. MC-100-0042)
|
||||||
|
|||||||
@@ -4,151 +4,215 @@
|
|||||||
"ItemNumber": "MC-100-0042",
|
"ItemNumber": "MC-100-0042",
|
||||||
"ItemName": "RF Amplifier Module, 2-18 GHz",
|
"ItemName": "RF Amplifier Module, 2-18 GHz",
|
||||||
"ChangeType": "PRODUCT",
|
"ChangeType": "PRODUCT",
|
||||||
"Status": "PENDING",
|
|
||||||
"StatusCriticality": 2,
|
|
||||||
"ArenaEcoId": "ECO-2026-0113",
|
"ArenaEcoId": "ECO-2026-0113",
|
||||||
|
"Status": "PENDING",
|
||||||
|
"StatusText": "Pending approval",
|
||||||
|
"StatusCriticality": 2,
|
||||||
"RequestedBy": "AZAW",
|
"RequestedBy": "AZAW",
|
||||||
"RequestedOn": "2026-09-12T14:05:00Z",
|
"RequestedOn": "2026-09-12T14:05:00Z",
|
||||||
"DecidedBy": null,
|
"DecidedBy": "",
|
||||||
"DecidedOn": null,
|
"DecidedOn": null,
|
||||||
"SapChangeNumber": null,
|
"SapChangeNumber": "",
|
||||||
"ErrorMessage": null,
|
"ErrorMessage": "",
|
||||||
"RejectReason": null,
|
"RejectReason": "",
|
||||||
"SapPreview": "{\"Product\":\"MC-100-0042\",\"ProductType\":\"FERT\",\"BaseUnit\":\"PC\",\"IndustrySector\":\"M\",\"GrossWeight\":\"0.412\",\"WeightUnit\":\"KG\",\"ProductGroup\":\"RFAMP\",\"CrossPlantStatus\":\"\",\"_ProductDescription\":[{\"Language\":\"EN\",\"ProductDescription\":\"RF Amplifier Module, 2-18 GHz\"}],\"_ProductPlant\":[{\"Plant\":\"1710\",\"AvailabilityCheckType\":\"02\",\"ProfitCenter\":\"YB700\",\"MRPType\":\"PD\",\"ProcurementType\":\"E\"},{\"Plant\":\"1720\",\"AvailabilityCheckType\":\"02\",\"ProfitCenter\":\"YB700\",\"MRPType\":\"PD\",\"ProcurementType\":\"F\"}],\"_ProductSalesDelivery\":[{\"ProductSalesOrg\":\"1710\",\"ProductDistributionChnl\":\"10\",\"DeliveryQuantityUnit\":\"PC\",\"ItemCategoryGroup\":\"NORM\"}],\"_ProductStorage\":[{\"Plant\":\"1710\",\"StorageLocation\":\"171A\"}],\"_ProductValuation\":[{\"ValuationArea\":\"1710\",\"ValuationClass\":\"7920\",\"PriceControl\":\"S\",\"StandardPrice\":\"245.00\",\"Currency\":\"USD\"}]}",
|
"SapPreview": "{\"Product\":\"MC-100-0042\",\"ProductType\":\"FERT\",\"BaseUnit\":\"PC\",\"IndustrySector\":\"M\",\"GrossWeight\":\"0.412\",\"WeightUnit\":\"KG\",\"ProductGroup\":\"RFAMP\",\"CrossPlantStatus\":\"\",\"_ProductDescription\":[{\"Language\":\"EN\",\"ProductDescription\":\"RF Amplifier Module, 2-18 GHz\"}],\"_ProductPlant\":[{\"Plant\":\"1710\",\"AvailabilityCheckType\":\"02\",\"ProfitCenter\":\"YB700\",\"MRPType\":\"PD\",\"ProcurementType\":\"E\"},{\"Plant\":\"1720\",\"AvailabilityCheckType\":\"02\",\"ProfitCenter\":\"YB700\",\"MRPType\":\"PD\",\"ProcurementType\":\"F\"}],\"_ProductSalesDelivery\":[{\"ProductSalesOrg\":\"1710\",\"ProductDistributionChnl\":\"10\",\"DeliveryQuantityUnit\":\"PC\",\"ItemCategoryGroup\":\"NORM\"}],\"_ProductStorage\":[{\"Plant\":\"1710\",\"StorageLocation\":\"171A\"}],\"_ProductValuation\":[{\"ValuationArea\":\"1710\",\"ValuationClass\":\"7920\",\"PriceControl\":\"S\",\"StandardPrice\":\"245.00\",\"Currency\":\"USD\"}]}",
|
||||||
"IsApprovable": true,
|
"IsApprovable": true,
|
||||||
"IsRetriable": false
|
"IsRetriable": false,
|
||||||
|
"LocalLastChangedAt": "2026-09-12T14:05:00Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": true,
|
||||||
|
"reject": true,
|
||||||
|
"retrySync": false
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "6f1c2a3e-0002-4a6b-9c1d-000000000002",
|
"ID": "6f1c2a3e-0002-4a6b-9c1d-000000000002",
|
||||||
"ItemNumber": "MC-100-0042",
|
"ItemNumber": "MC-100-0042",
|
||||||
"ItemName": "RF Amplifier Module, 2-18 GHz",
|
"ItemName": "RF Amplifier Module, 2-18 GHz",
|
||||||
"ChangeType": "CHANGE_MASTER",
|
"ChangeType": "CHANGE_MASTER",
|
||||||
"Status": "PENDING",
|
|
||||||
"StatusCriticality": 2,
|
|
||||||
"ArenaEcoId": "ECO-2026-0113",
|
"ArenaEcoId": "ECO-2026-0113",
|
||||||
|
"Status": "PENDING",
|
||||||
|
"StatusText": "Pending approval",
|
||||||
|
"StatusCriticality": 2,
|
||||||
"RequestedBy": "AZAW",
|
"RequestedBy": "AZAW",
|
||||||
"RequestedOn": "2026-09-12T14:05:04Z",
|
"RequestedOn": "2026-09-12T14:05:04Z",
|
||||||
"DecidedBy": null,
|
"DecidedBy": "",
|
||||||
"DecidedOn": null,
|
"DecidedOn": null,
|
||||||
"SapChangeNumber": null,
|
"SapChangeNumber": "",
|
||||||
"ErrorMessage": null,
|
"ErrorMessage": "",
|
||||||
"RejectReason": null,
|
"RejectReason": "",
|
||||||
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: Rev C amplifier bias network update\",\"ChangeNumberValidFromDate\":\"2026-09-12T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0113\",\"ChangeNumberStatus\":\"01\"}",
|
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: Rev C amplifier bias network update\",\"ChangeNumberValidFromDate\":\"2026-09-12T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0113\",\"ChangeNumberStatus\":\"01\"}",
|
||||||
"IsApprovable": true,
|
"IsApprovable": true,
|
||||||
"IsRetriable": false
|
"IsRetriable": false,
|
||||||
|
"LocalLastChangedAt": "2026-09-12T14:05:04Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": true,
|
||||||
|
"reject": true,
|
||||||
|
"retrySync": false
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "6f1c2a3e-0003-4a6b-9c1d-000000000003",
|
"ID": "6f1c2a3e-0003-4a6b-9c1d-000000000003",
|
||||||
"ItemNumber": "MC-200-0117",
|
"ItemNumber": "MC-200-0117",
|
||||||
"ItemName": "Bias Tee Assembly, SMA",
|
"ItemName": "Bias Tee Assembly, SMA",
|
||||||
"ChangeType": "BOM",
|
"ChangeType": "BOM",
|
||||||
"Status": "PENDING",
|
|
||||||
"StatusCriticality": 2,
|
|
||||||
"ArenaEcoId": "ECO-2026-0118",
|
"ArenaEcoId": "ECO-2026-0118",
|
||||||
|
"Status": "PENDING",
|
||||||
|
"StatusText": "Pending approval",
|
||||||
|
"StatusCriticality": 2,
|
||||||
"RequestedBy": "JDOE",
|
"RequestedBy": "JDOE",
|
||||||
"RequestedOn": "2026-09-13T09:41:00Z",
|
"RequestedOn": "2026-09-13T09:41:00Z",
|
||||||
"DecidedBy": null,
|
"DecidedBy": "",
|
||||||
"DecidedOn": null,
|
"DecidedOn": null,
|
||||||
"SapChangeNumber": null,
|
"SapChangeNumber": "",
|
||||||
"ErrorMessage": null,
|
"ErrorMessage": "",
|
||||||
"RejectReason": null,
|
"RejectReason": "",
|
||||||
"SapPreview": "{\"Material\":\"MC-200-0117\",\"Plant\":\"1710\",\"BillOfMaterialCategory\":\"M\",\"BillOfMaterialVariantUsage\":\"1\",\"HeaderValidityStartDate\":\"2026-09-13T00:00:00Z\",\"ToBOMItem\":[{\"BillOfMaterialItemNumber\":\"0010\",\"BillOfMaterialComponent\":\"MC-500-0031\",\"BillOfMaterialItemQuantity\":\"2\",\"BillOfMaterialItemCategory\":\"L\"},{\"BillOfMaterialItemNumber\":\"0020\",\"BillOfMaterialComponent\":\"MC-500-0088\",\"BillOfMaterialItemQuantity\":\"1\",\"BillOfMaterialItemCategory\":\"L\"}]}",
|
"SapPreview": "{\"Material\":\"MC-200-0117\",\"Plant\":\"1710\",\"BillOfMaterialCategory\":\"M\",\"BillOfMaterialVariantUsage\":\"1\",\"HeaderValidityStartDate\":\"2026-09-13T00:00:00Z\",\"ToBOMItem\":[{\"BillOfMaterialItemNumber\":\"0010\",\"BillOfMaterialComponent\":\"MC-500-0031\",\"BillOfMaterialItemQuantity\":\"2\",\"BillOfMaterialItemCategory\":\"L\"},{\"BillOfMaterialItemNumber\":\"0020\",\"BillOfMaterialComponent\":\"MC-500-0088\",\"BillOfMaterialItemQuantity\":\"1\",\"BillOfMaterialItemCategory\":\"L\"}]}",
|
||||||
"IsApprovable": true,
|
"IsApprovable": true,
|
||||||
"IsRetriable": false
|
"IsRetriable": false,
|
||||||
|
"LocalLastChangedAt": "2026-09-13T09:41:00Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": true,
|
||||||
|
"reject": true,
|
||||||
|
"retrySync": false
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "6f1c2a3e-0004-4a6b-9c1d-000000000004",
|
"ID": "6f1c2a3e-0004-4a6b-9c1d-000000000004",
|
||||||
"ItemNumber": "MC-300-0009",
|
"ItemNumber": "MC-300-0009",
|
||||||
"ItemName": "Low Noise Amplifier, 0.5-4 GHz",
|
"ItemName": "Low Noise Amplifier, 0.5-4 GHz",
|
||||||
"ChangeType": "PRODUCT",
|
"ChangeType": "PRODUCT",
|
||||||
"Status": "APPROVED",
|
|
||||||
"StatusCriticality": 2,
|
|
||||||
"ArenaEcoId": "ECO-2026-0097",
|
"ArenaEcoId": "ECO-2026-0097",
|
||||||
|
"Status": "APPROVED",
|
||||||
|
"StatusText": "Approved - awaiting execution",
|
||||||
|
"StatusCriticality": 2,
|
||||||
"RequestedBy": "AZAW",
|
"RequestedBy": "AZAW",
|
||||||
"RequestedOn": "2026-09-08T16:22:00Z",
|
"RequestedOn": "2026-09-08T16:22:00Z",
|
||||||
"DecidedBy": "MSMITH",
|
"DecidedBy": "MSMITH",
|
||||||
"DecidedOn": "2026-09-09T08:10:00Z",
|
"DecidedOn": "2026-09-09T08:10:00Z",
|
||||||
"SapChangeNumber": null,
|
"SapChangeNumber": "",
|
||||||
"ErrorMessage": null,
|
"ErrorMessage": "",
|
||||||
"RejectReason": null,
|
"RejectReason": "",
|
||||||
"SapPreview": "{\"Product\":\"MC-300-0009\",\"ProductType\":\"FERT\",\"BaseUnit\":\"PC\",\"ProductGroup\":\"LNA\",\"_ProductDescription\":[{\"Language\":\"EN\",\"ProductDescription\":\"Low Noise Amplifier, 0.5-4 GHz\"}]}",
|
"SapPreview": "{\"Product\":\"MC-300-0009\",\"ProductType\":\"FERT\",\"BaseUnit\":\"PC\",\"ProductGroup\":\"LNA\",\"_ProductDescription\":[{\"Language\":\"EN\",\"ProductDescription\":\"Low Noise Amplifier, 0.5-4 GHz\"}]}",
|
||||||
"IsApprovable": false,
|
"IsApprovable": false,
|
||||||
"IsRetriable": false
|
"IsRetriable": false,
|
||||||
|
"LocalLastChangedAt": "2026-09-09T08:10:00Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": false,
|
||||||
|
"reject": false,
|
||||||
|
"retrySync": false
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "6f1c2a3e-0007-4a6b-9c1d-000000000007",
|
"ID": "6f1c2a3e-0007-4a6b-9c1d-000000000007",
|
||||||
"ItemNumber": "MC-300-0009",
|
"ItemNumber": "MC-300-0009",
|
||||||
"ItemName": "Low Noise Amplifier, 0.5-4 GHz",
|
"ItemName": "Low Noise Amplifier, 0.5-4 GHz",
|
||||||
"ChangeType": "CHANGE_MASTER",
|
"ChangeType": "CHANGE_MASTER",
|
||||||
"Status": "EXECUTING",
|
|
||||||
"ArenaEcoId": "ECO-2026-0097",
|
"ArenaEcoId": "ECO-2026-0097",
|
||||||
|
"Status": "EXECUTING",
|
||||||
|
"StatusText": "Executing in SAP",
|
||||||
|
"StatusCriticality": 5,
|
||||||
"RequestedBy": "AZAW",
|
"RequestedBy": "AZAW",
|
||||||
"RequestedOn": "2026-09-08T16:22:03Z",
|
"RequestedOn": "2026-09-08T16:22:03Z",
|
||||||
"DecidedBy": "MSMITH",
|
"DecidedBy": "MSMITH",
|
||||||
"DecidedOn": "2026-09-09T08:10:00Z",
|
"DecidedOn": "2026-09-09T08:10:00Z",
|
||||||
"SapChangeNumber": null,
|
"SapChangeNumber": "",
|
||||||
"ErrorMessage": null,
|
"ErrorMessage": "",
|
||||||
"RejectReason": null,
|
"RejectReason": "",
|
||||||
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: LNA input match rework\",\"ChangeNumberValidFromDate\":\"2026-09-09T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0097\",\"ChangeNumberStatus\":\"01\"}",
|
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: LNA input match rework\",\"ChangeNumberValidFromDate\":\"2026-09-09T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0097\",\"ChangeNumberStatus\":\"01\"}",
|
||||||
"StatusCriticality": 5,
|
|
||||||
"IsApprovable": false,
|
"IsApprovable": false,
|
||||||
"IsRetriable": false
|
"IsRetriable": false,
|
||||||
|
"LocalLastChangedAt": "2026-09-09T08:10:00Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": false,
|
||||||
|
"reject": false,
|
||||||
|
"retrySync": false
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "6f1c2a3e-0008-4a6b-9c1d-000000000008",
|
"ID": "6f1c2a3e-0008-4a6b-9c1d-000000000008",
|
||||||
"ItemNumber": "MC-150-0301",
|
"ItemNumber": "MC-150-0301",
|
||||||
"ItemName": "Directional Coupler, 10 dB",
|
"ItemName": "Directional Coupler, 10 dB",
|
||||||
"ChangeType": "CHANGE_MASTER",
|
"ChangeType": "CHANGE_MASTER",
|
||||||
"Status": "SYNCED",
|
|
||||||
"ArenaEcoId": "ECO-2026-0088",
|
"ArenaEcoId": "ECO-2026-0088",
|
||||||
|
"Status": "SYNCED",
|
||||||
|
"StatusText": "Synced to SAP",
|
||||||
|
"StatusCriticality": 3,
|
||||||
"RequestedBy": "JDOE",
|
"RequestedBy": "JDOE",
|
||||||
"RequestedOn": "2026-09-05T10:00:00Z",
|
"RequestedOn": "2026-09-05T10:00:00Z",
|
||||||
"DecidedBy": "MSMITH",
|
"DecidedBy": "MSMITH",
|
||||||
"DecidedOn": "2026-09-05T11:30:00Z",
|
"DecidedOn": "2026-09-05T11:30:00Z",
|
||||||
"SapChangeNumber": "500000012301",
|
"SapChangeNumber": "500000012301",
|
||||||
"ErrorMessage": null,
|
"ErrorMessage": "",
|
||||||
"RejectReason": null,
|
"RejectReason": "",
|
||||||
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: Coupler housing plating change\",\"ChangeNumberValidFromDate\":\"2026-09-05T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0088\",\"ChangeNumberStatus\":\"01\"}",
|
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: Coupler housing plating change\",\"ChangeNumberValidFromDate\":\"2026-09-05T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0088\",\"ChangeNumberStatus\":\"01\"}",
|
||||||
"StatusCriticality": 3,
|
|
||||||
"IsApprovable": false,
|
"IsApprovable": false,
|
||||||
"IsRetriable": false
|
"IsRetriable": false,
|
||||||
|
"LocalLastChangedAt": "2026-09-05T11:30:00Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": false,
|
||||||
|
"reject": false,
|
||||||
|
"retrySync": false
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "6f1c2a3e-0005-4a6b-9c1d-000000000005",
|
"ID": "6f1c2a3e-0005-4a6b-9c1d-000000000005",
|
||||||
"ItemNumber": "MC-410-0250",
|
"ItemNumber": "MC-410-0250",
|
||||||
"ItemName": "Datasheet MC-410-0250 Rev B",
|
"ItemName": "Datasheet MC-410-0250 Rev B",
|
||||||
"ChangeType": "DOCUMENTS",
|
"ChangeType": "DOCUMENTS",
|
||||||
"Status": "REJECTED",
|
|
||||||
"StatusCriticality": 1,
|
|
||||||
"ArenaEcoId": "ECO-2026-0101",
|
"ArenaEcoId": "ECO-2026-0101",
|
||||||
|
"Status": "REJECTED",
|
||||||
|
"StatusText": "Rejected",
|
||||||
|
"StatusCriticality": 1,
|
||||||
"RequestedBy": "JDOE",
|
"RequestedBy": "JDOE",
|
||||||
"RequestedOn": "2026-09-10T11:15:00Z",
|
"RequestedOn": "2026-09-10T11:15:00Z",
|
||||||
"DecidedBy": "MSMITH",
|
"DecidedBy": "MSMITH",
|
||||||
"DecidedOn": "2026-09-10T13:02:00Z",
|
"DecidedOn": "2026-09-10T13:02:00Z",
|
||||||
"SapChangeNumber": null,
|
"SapChangeNumber": "",
|
||||||
"ErrorMessage": null,
|
"ErrorMessage": "",
|
||||||
"RejectReason": "Datasheet revision B superseded by Rev C in Arena; re-stage after ECO-2026-0121 is released.",
|
"RejectReason": "Datasheet revision B superseded by Rev C in Arena; re-stage after ECO-2026-0121 is released.",
|
||||||
"SapPreview": "{\"BusinessObjectTypeName\":\"BUS1001006\",\"LinkedSAPObjectKey\":\"MC-410-0250\",\"Url\":\"https://app.bom.com/files/MC-410-0250-B.pdf\",\"MIMEType\":\"text/url\",\"AttachmentDescription\":\"MC-410-0250 Datasheet Rev B\"}",
|
"SapPreview": "{\"BusinessObjectTypeName\":\"BUS1001006\",\"LinkedSAPObjectKey\":\"MC-410-0250\",\"Url\":\"https://app.bom.com/files/MC-410-0250-B.pdf\",\"MIMEType\":\"text/url\",\"AttachmentDescription\":\"MC-410-0250 Datasheet Rev B\"}",
|
||||||
"IsApprovable": false,
|
"IsApprovable": false,
|
||||||
"IsRetriable": false
|
"IsRetriable": false,
|
||||||
|
"LocalLastChangedAt": "2026-09-10T13:02:00Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": false,
|
||||||
|
"reject": false,
|
||||||
|
"retrySync": false
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "6f1c2a3e-0006-4a6b-9c1d-000000000006",
|
"ID": "6f1c2a3e-0006-4a6b-9c1d-000000000006",
|
||||||
"ItemNumber": "MC-200-0117",
|
"ItemNumber": "MC-200-0117",
|
||||||
"ItemName": "Bias Tee Assembly, SMA",
|
"ItemName": "Bias Tee Assembly, SMA",
|
||||||
"ChangeType": "CHANGE_MASTER",
|
"ChangeType": "CHANGE_MASTER",
|
||||||
"Status": "FAILED",
|
|
||||||
"StatusCriticality": 1,
|
|
||||||
"ArenaEcoId": "ECO-2026-0118",
|
"ArenaEcoId": "ECO-2026-0118",
|
||||||
|
"Status": "FAILED",
|
||||||
|
"StatusText": "Execution failed",
|
||||||
|
"StatusCriticality": 1,
|
||||||
"RequestedBy": "JDOE",
|
"RequestedBy": "JDOE",
|
||||||
"RequestedOn": "2026-09-13T09:40:00Z",
|
"RequestedOn": "2026-09-13T09:40:00Z",
|
||||||
"DecidedBy": "AZAW",
|
"DecidedBy": "AZAW",
|
||||||
"DecidedOn": "2026-09-13T10:05:00Z",
|
"DecidedOn": "2026-09-13T10:05:00Z",
|
||||||
"SapChangeNumber": null,
|
"SapChangeNumber": "",
|
||||||
"ErrorMessage": "API_CHANGEMASTER;v=0002 returned 400: Input parameters are initial (A_ChangeMasterObjTypeAssgmt requires IsActiveForChangeNumber).",
|
"ErrorMessage": "API_CHANGEMASTER;v=0002 returned 400: Input parameters are initial (A_ChangeMasterObjTypeAssgmt requires IsActiveForChangeNumber).",
|
||||||
"RejectReason": null,
|
"RejectReason": "",
|
||||||
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: Bias tee connector change to SMA-F\",\"ChangeNumberValidFromDate\":\"2026-09-13T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0118\",\"ChangeNumberStatus\":\"01\"}",
|
"SapPreview": "{\"ChangeNumberDescription\":\"ECO: Bias tee connector change to SMA-F\",\"ChangeNumberValidFromDate\":\"2026-09-13T00:00:00Z\",\"ReasonForChangeText\":\"ARENA ECO-2026-0118\",\"ChangeNumberStatus\":\"01\"}",
|
||||||
"IsApprovable": false,
|
"IsApprovable": false,
|
||||||
"IsRetriable": true
|
"IsRetriable": true,
|
||||||
|
"LocalLastChangedAt": "2026-09-13T10:05:00Z",
|
||||||
|
"__OperationControl": {
|
||||||
|
"approve": false,
|
||||||
|
"reject": false,
|
||||||
|
"retrySync": true
|
||||||
|
},
|
||||||
|
"SAP__Messages": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,132 +1,592 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<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">
|
||||||
MOCK OData V4 $metadata for the Arena PLM change-approval Fiori Elements app (ADR-002).
|
<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">
|
||||||
SERVICE IDENTITY - single source of truth for the app (referenced by manifest.json,
|
<edmx:Include Namespace="com.sap.vocabularies.Analytics.v1" Alias="SAP__analytics"/>
|
||||||
annotation.xml and ui5-mock.yaml / ui5-local.yaml):
|
</edmx:Reference>
|
||||||
RAP service binding (OData V4 - UI) : ZUI_ARENA_CHANGE_APPROVAL_O4
|
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_AUTHORIZATION',Version='0001',SAP__Origin='LOCAL')/$value">
|
||||||
RAP service definition : ZUI_ARENA_CHANGE_APPROVAL
|
<edmx:Include Namespace="Org.OData.Authorization.V1" Alias="SAP__authorization"/>
|
||||||
Version : 0001
|
</edmx:Reference>
|
||||||
Service URL : /sap/opu/odata4/sap/zui_arena_change_approval_o4/srvd/sap/zui_arena_change_approval/0001/
|
|
||||||
Schema namespace / alias : com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001 / SAP__self
|
|
||||||
Root entity type / entity set : ChangeApproval_Type / ChangeApproval
|
|
||||||
The first URL segment after /sap/opu/odata4/sap/ and the schema namespace are strongly
|
|
||||||
indicated to be derived from the service BINDING name (the segment after srvd|srvd_a2x/sap/
|
|
||||||
is the service DEFINITION name): the tenant serves
|
|
||||||
/sap/opu/odata4/sap/api_currencyexchangerate/srvd_a2x/sap/currencyexchangerate/0001 with
|
|
||||||
namespace com.sap.gateway.srvd_a2x.api_currencyexchangerate.v0001, and SAP Help documents
|
|
||||||
/sap/opu/odata4/sap/api_product/srvd_a2x/sap/product/0002.
|
|
||||||
[source: live tenant my429480 ~/projects/sap-apps/exchange-rates-cap/package.json lines 33,39 and
|
|
||||||
srv/external/API_CURRENCYEXCHANGERATE.edmx line 55; sap-docs sap-help loio 5f45f125dfe84c20beb18f5de1512f5a,
|
|
||||||
verified 2026-09-14]
|
|
||||||
Provisional - namespace = binding name is strongly indicated, not proven; reconcile namespace,
|
|
||||||
entity type suffix and URL segments against the live $metadata of the RAP UI service binding
|
|
||||||
at PRD V-2 before deploy.
|
|
||||||
=====================================================================================
|
|
||||||
|
|
||||||
Shape mirrors what an ABAP Cloud OData V4 service binding emits: schema namespace
|
|
||||||
com.sap.gateway.<srvd|srvd_a2x>.<binding>.v0001 with Alias="SAP__self", entity types
|
|
||||||
suffixed "_Type", un-suffixed entity sets, and vocabulary aliases SAP__common /
|
|
||||||
SAP__capabilities / SAP__core / SAP__UI.
|
|
||||||
[source: live tenant my429480 EDMX ~/projects/sap-apps/exchange-rates-cap/srv/external/API_CURRENCYEXCHANGERATE.edmx
|
|
||||||
lines 13-31 (vocabulary aliases), 55 (schema/alias), 57 (_Type), 95 (entity set), verified 2026-09-14]
|
|
||||||
A Fiori app consumes the UI service binding (srvd), not the A2X binding used by the worker.
|
|
||||||
[source: sap-docs sap-help loio 5717c0a1dafc4335add0207b81c39cef, verified 2026-09-14]
|
|
||||||
|
|
||||||
Action availability: RAP emits Core.OperationAvailable for actions declared with
|
|
||||||
"( features : instance )" in the behavior definition; the Fiori side needs no extra annotation.
|
|
||||||
[source: sap-docs /sapui5/06_SAP_Fiori_Elements/actions-cbf16c5#enabling-custom-actions-defined-using-annotations
|
|
||||||
("No ABAP CDS annotation is required, since the setting is made according to the modelling ... in RAP BDEF"),
|
|
||||||
abap-docs /abap-docs-standard/ABENBDL_ACTIONS_FC, verified 2026-09-14]
|
|
||||||
The mock models the emitted result with the boolean properties IsApprovable / IsRetriable.
|
|
||||||
-->
|
|
||||||
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
|
|
||||||
<edmx:Reference Uri="/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_CAPABILITIES',Version='0001',SAP__Origin='LOCAL')/$value">
|
<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:Include Namespace="Org.OData.Capabilities.V1" Alias="SAP__capabilities"/>
|
||||||
</edmx:Reference>
|
</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: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:Include Namespace="com.sap.vocabularies.Common.v1" Alias="SAP__common"/>
|
||||||
</edmx:Reference>
|
</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: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:Include Namespace="Org.OData.Core.V1" Alias="SAP__core"/>
|
||||||
</edmx:Reference>
|
</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: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:Include Namespace="com.sap.vocabularies.UI.v1" Alias="SAP__UI"/>
|
||||||
</edmx:Reference>
|
</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>
|
<edmx:DataServices>
|
||||||
<Schema Namespace="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001" Alias="SAP__self" xmlns="http://docs.oasis-open.org/odata/ns/edm">
|
<Schema Namespace="com.sap.gateway.srvd.zui_arena_change_approval.v0001" Alias="SAP__self">
|
||||||
<Annotation Term="SAP__core.SchemaVersion" String="1.0.0"/>
|
<Annotation Term="SAP__core.SchemaVersion" String="1.0.0"/>
|
||||||
|
<EntityType Name="ChangeApprovalType">
|
||||||
<EntityType Name="ChangeApproval_Type">
|
|
||||||
<Key>
|
<Key>
|
||||||
<PropertyRef Name="ID"/>
|
<PropertyRef Name="ID"/>
|
||||||
</Key>
|
</Key>
|
||||||
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
|
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
|
||||||
<Property Name="ItemNumber" Type="Edm.String" MaxLength="18"/>
|
<Property Name="ItemNumber" Type="Edm.String" Nullable="false" MaxLength="18"/>
|
||||||
<Property Name="ItemName" Type="Edm.String" MaxLength="40"/>
|
<Property Name="ItemName" Type="Edm.String" Nullable="false" MaxLength="40"/>
|
||||||
<Property Name="ChangeType" Type="Edm.String" MaxLength="15"/>
|
<Property Name="ChangeType" Type="Edm.String" Nullable="false" MaxLength="15"/>
|
||||||
<!-- Status domain: PENDING -> APPROVED -> EXECUTING -> SYNCED | FAILED; REJECTED terminal;
|
<Property Name="ArenaEcoId" Type="Edm.String" Nullable="false" MaxLength="20"/>
|
||||||
FAILED -retrySync-> EXECUTING. approve/reject only for PENDING, retrySync only for FAILED (PRD RAP-1). -->
|
<Property Name="Status" Type="Edm.String" Nullable="false" MaxLength="10"/>
|
||||||
<Property Name="Status" Type="Edm.String" MaxLength="10"/>
|
<Property Name="StatusText" Type="Edm.String" Nullable="false" MaxLength="29"/>
|
||||||
<Property Name="StatusCriticality" Type="Edm.Byte"/>
|
<Property Name="StatusCriticality" Type="Edm.Byte" Nullable="false"/>
|
||||||
<Property Name="ArenaEcoId" Type="Edm.String" MaxLength="20"/>
|
<Property Name="RequestedBy" Type="Edm.String" Nullable="false" MaxLength="12"/>
|
||||||
<Property Name="RequestedBy" Type="Edm.String" MaxLength="12"/>
|
|
||||||
<Property Name="RequestedOn" Type="Edm.DateTimeOffset" Precision="7"/>
|
<Property Name="RequestedOn" Type="Edm.DateTimeOffset" Precision="7"/>
|
||||||
<Property Name="DecidedBy" Type="Edm.String" MaxLength="12"/>
|
<Property Name="DecidedBy" Type="Edm.String" Nullable="false" MaxLength="12"/>
|
||||||
<Property Name="DecidedOn" Type="Edm.DateTimeOffset" Precision="7"/>
|
<Property Name="DecidedOn" Type="Edm.DateTimeOffset" Precision="7"/>
|
||||||
<Property Name="SapChangeNumber" Type="Edm.String" MaxLength="12"/>
|
<Property Name="SapChangeNumber" Type="Edm.String" Nullable="false" MaxLength="12"/>
|
||||||
<Property Name="ErrorMessage" Type="Edm.String" MaxLength="255"/>
|
<Property Name="ErrorMessage" Type="Edm.String" Nullable="false" MaxLength="255"/>
|
||||||
<Property Name="RejectReason" Type="Edm.String" MaxLength="255"/>
|
<Property Name="RejectReason" Type="Edm.String" Nullable="false" MaxLength="255"/>
|
||||||
<!-- abap.string on the RAP side: emitted as Edm.String without MaxLength -->
|
<Property Name="SapPreview" Type="Edm.String" Nullable="false"/>
|
||||||
<Property Name="SapPreview" Type="Edm.String"/>
|
<Property Name="IsApprovable" Type="Edm.Boolean" Nullable="false"/>
|
||||||
<!-- Instance feature-control results (RAP "features : instance"), consumed by Core.OperationAvailable below -->
|
<Property Name="IsRetriable" Type="Edm.Boolean" Nullable="false"/>
|
||||||
<Property Name="IsApprovable" Type="Edm.Boolean"/>
|
<Property Name="LocalLastChangedAt" Type="Edm.DateTimeOffset" Precision="7"/>
|
||||||
<Property Name="IsRetriable" Type="Edm.Boolean"/>
|
<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>
|
</EntityType>
|
||||||
|
<ComplexType Name="ChangeApprovalOperationControl">
|
||||||
<!-- Bound actions (binding parameter "_it" as emitted by RAP service bindings) -->
|
<Property Name="approve" Type="Edm.Boolean" Nullable="false"/>
|
||||||
<Action Name="approve" IsBound="true">
|
<Property Name="reject" Type="Edm.Boolean" Nullable="false"/>
|
||||||
<Parameter Name="_it" Type="SAP__self.ChangeApproval_Type" Nullable="false"/>
|
<Property Name="retrySync" Type="Edm.Boolean" Nullable="false"/>
|
||||||
<ReturnType Type="SAP__self.ChangeApproval_Type" 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>
|
||||||
<Action Name="reject" IsBound="true">
|
<Action Name="approve" EntitySetPath="_it" IsBound="true">
|
||||||
<Parameter Name="_it" Type="SAP__self.ChangeApproval_Type" Nullable="false"/>
|
<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"/>
|
<Parameter Name="RejectReason" Type="Edm.String" Nullable="false" MaxLength="255"/>
|
||||||
<ReturnType Type="SAP__self.ChangeApproval_Type" Nullable="false"/>
|
<ReturnType Type="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType" Nullable="false"/>
|
||||||
</Action>
|
</Action>
|
||||||
<Action Name="retrySync" IsBound="true">
|
|
||||||
<Parameter Name="_it" Type="SAP__self.ChangeApproval_Type" Nullable="false"/>
|
|
||||||
<ReturnType Type="SAP__self.ChangeApproval_Type" Nullable="false"/>
|
|
||||||
</Action>
|
|
||||||
|
|
||||||
<EntityContainer Name="Container">
|
<EntityContainer Name="Container">
|
||||||
<EntitySet Name="ChangeApproval" EntityType="com.sap.gateway.srvd.zui_arena_change_approval_o4.v0001.ChangeApproval_Type"/>
|
<EntitySet Name="ChangeApproval" EntityType="com.sap.gateway.srvd.zui_arena_change_approval.v0001.ChangeApprovalType"/>
|
||||||
</EntityContainer>
|
</EntityContainer>
|
||||||
|
<Annotations Target="SAP__self.ChangeApprovalType/ID">
|
||||||
<!-- Service-side (metadata) annotations, as a RAP service binding would emit them -->
|
<Annotation Term="SAP__core.Computed"/>
|
||||||
<Annotations Target="SAP__self.ChangeApproval_Type/ID">
|
<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"/>
|
<Annotation Term="SAP__core.Computed"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="SAP__self.approve">
|
<Annotations Target="SAP__self.ChangeApprovalType/StatusText">
|
||||||
<Annotation Term="SAP__core.OperationAvailable" Path="IsApprovable"/>
|
<Annotation Term="SAP__core.Computed"/>
|
||||||
|
<Annotation Term="SAP__UI.Hidden"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="SAP__self.reject">
|
<Annotations Target="SAP__self.ChangeApprovalType/StatusCriticality">
|
||||||
<Annotation Term="SAP__core.OperationAvailable" Path="IsApprovable"/>
|
<Annotation Term="SAP__core.Computed"/>
|
||||||
|
<Annotation Term="SAP__UI.Hidden"/>
|
||||||
</Annotations>
|
</Annotations>
|
||||||
<Annotations Target="SAP__self.retrySync">
|
<Annotations Target="SAP__self.ChangeApprovalType/RequestedBy">
|
||||||
<Annotation Term="SAP__core.OperationAvailable" Path="IsRetriable"/>
|
<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&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>
|
||||||
<Annotations Target="SAP__self.Container/ChangeApproval">
|
<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">
|
<Annotation Term="SAP__capabilities.InsertRestrictions">
|
||||||
<Record><PropertyValue Property="Insertable" Bool="false"/></Record>
|
<Record>
|
||||||
|
<PropertyValue Property="Insertable" Bool="false"/>
|
||||||
|
</Record>
|
||||||
</Annotation>
|
</Annotation>
|
||||||
<Annotation Term="SAP__capabilities.DeleteRestrictions">
|
<Annotation Term="SAP__capabilities.DeleteRestrictions">
|
||||||
<Record><PropertyValue Property="Deletable" Bool="false"/></Record>
|
<Record>
|
||||||
|
<PropertyValue Property="Deletable" Bool="false"/>
|
||||||
|
</Record>
|
||||||
</Annotation>
|
</Annotation>
|
||||||
<Annotation Term="SAP__capabilities.UpdateRestrictions">
|
<Annotation Term="SAP__capabilities.UpdateRestrictions">
|
||||||
<Record><PropertyValue Property="Updatable" Bool="false"/></Record>
|
<Record>
|
||||||
|
<PropertyValue Property="Updatable" Bool="false"/>
|
||||||
|
<PropertyValue Property="QueryOptions">
|
||||||
|
<Record>
|
||||||
|
<PropertyValue Property="SelectSupported" Bool="true"/>
|
||||||
|
</Record>
|
||||||
|
</PropertyValue>
|
||||||
|
</Record>
|
||||||
</Annotation>
|
</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>
|
</Annotations>
|
||||||
</Schema>
|
</Schema>
|
||||||
</edmx:DataServices>
|
</edmx:DataServices>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mainService": {
|
"mainService": {
|
||||||
"uri": "/sap/opu/odata4/sap/zui_arena_change_approval_o4/srvd/sap/zui_arena_change_approval/0001/",
|
"uri": "/sap/opu/odata4/sap/zui_arena_change_appr_o4/srvd/sap/zui_arena_change_approval/0001/",
|
||||||
"type": "OData",
|
"type": "OData",
|
||||||
"settings": {
|
"settings": {
|
||||||
"annotations": [
|
"annotations": [
|
||||||
|
|||||||
+607
-151
@@ -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`
|
Target: development tenant `my431353` (ADT destination `0M7DEJI`), package `ZARENA_PLM`, a
|
||||||
(sub-package of `ZLOCAL`, no transport). The ADT MCP Server can create objects and activate
|
sub-package of `ZLOCAL` with no transport. No generator is used. Every object is created empty
|
||||||
them but cannot write source (PRD V-4), so every file in this folder is a **ready-to-paste
|
through the ADT MCP `abap_creation-create_object`, filled by pasting a file from this folder into
|
||||||
source** for the VS Code ADT editor. Each file header names the target object, its
|
the VS Code ADT editor, saved, and activated through `abap_activate_objects`. The ADT MCP cannot
|
||||||
`abap:` URI and says "replace entire content".
|
write source (PRD V-4).
|
||||||
|
|
||||||
Every SAP behavioral claim in these files carries a `[source: … , verified 2026-09-15]`
|
Most source files start with a header comment that names the target object, its `abap:` URI and its
|
||||||
tag; the sources are `abap-docs` (ABAP Cloud keyword docs + SAP samples), `sap-docs`
|
README step. **The two service definition sources (U13 `zui_arena_change_approval.srvd.srvdsrv`, W4
|
||||||
(SAP Help) and `sap-released-objects` (release states).
|
`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 |
|
## Status (2026-09-16)
|
||||||
|---|---|---|
|
|
||||||
| `ZZCHNGPPROVAL000` | TABL | persistent table (generator naming) |
|
|
||||||
| `ZZR_CHANGEAPPROVAL000` | DDLS / BDEF / DCLS | root view entity, managed BDEF, access control |
|
|
||||||
| `ZZBP_R_CHANGEAPPROVAL000` | CLAS | behavior pool of the base BDEF |
|
|
||||||
| `ZZC_CHANGEAPPROVAL000` | DDLS / BDEF / DDLX / DCLS | UI projection view, projection BDEF, metadata extension, access control |
|
|
||||||
| `ZZBP_C_CHANGEAPPROVAL000` | CLAS | projection behavior pool — **delete it** (step 9) |
|
|
||||||
| `ZZUI_ARENA_CHANGE_APPRVL000_O4` | SRVD + SRVB | service definition and OData V4 **UI** binding (same name), **unpublished** |
|
|
||||||
| `ZZARENA_CHANGE_APPROVAL000` | RONT + NONT | SAP object type / node type |
|
|
||||||
|
|
||||||
Created empty via `create_object` (exist, **no source, inactive**):
|
- **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 |
|
## Object inventory and name limits
|
||||||
|---|---|---|
|
|
||||||
| `ZD_CHANGEAPPROVAL_REJECTP` | DDLS | abstract entity — `reject` parameter (`RejectReason`) |
|
|
||||||
| `ZD_CHANGEAPPROVAL_SYNCEDP` | DDLS | abstract entity — `markSynced` parameter (`SapChangeNumber`) |
|
|
||||||
| `ZD_CHANGEAPPROVAL_FAILEDP` | DDLS | abstract entity — `markFailed` parameter (`ErrorMessage`) |
|
|
||||||
| `ZC_CHANGEAPPROVAL_A2X` | DDLS + DCLS | A2X projection view (worker) and its access control |
|
|
||||||
| `ZAPI_ARENA_CHANGE_APPROVAL` | SRVD | Web API service definition (worker) |
|
|
||||||
|
|
||||||
Not yet creatable (the MCP validation refuses until the referenced objects are active):
|
| 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 |
|
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
|
||||||
| `ZC_CHANGEAPPROVAL_A2X` | BDEF (projection) | "The referenced STOB object ZC_CHANGEAPPROVAL_A2X does not exist" |
|
SAP Help. The ADT MCP accepts 30 for `SRVB/SVB`, but that is undocumented and not used here (D-1).
|
||||||
| `ZAPI_ARENA_CHANGE_APPROVAL_A2X` | SRVB (OData V4 - Web API) | "An active version of the Service Definition ZAPI_ARENA_CHANGE_APPROVAL does not exist" |
|
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
|
Split: the **UI leg** has 14 source files (13 objects with source + the U14 binding). The **worker
|
||||||
the file content, save, activate. Activation order matters — dependencies are top-down.
|
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
|
## Resolved decisions
|
||||||
abap-file-formats extension by analogy with `.ddls.asddls`; the tenant object is the
|
|
||||||
same regardless.)
|
|
||||||
2. `zd_changeapproval_rejectp.ddls.asddls`, `zd_changeapproval_syncedp.ddls.asddls`,
|
|
||||||
`zd_changeapproval_failedp.ddls.asddls` → the three abstract entities.
|
|
||||||
3. `zzr_changeapproval000.ddls.asddls` → root view entity.
|
|
||||||
4. `zzr_changeapproval000.bdef.asbdef` → base BDEF.
|
|
||||||
Watch for a message about the unmapped elements `StatusText`, `StatusCriticality`,
|
|
||||||
`IsApprovable`, `IsRetriable` (CDS-calculated, deliberately not in the mapping). A
|
|
||||||
warning is acceptable; an **error** means the fallback in the file header applies.
|
|
||||||
5. `zzbp_r_changeapproval000.clas.abap` (global class) and
|
|
||||||
`zzbp_r_changeapproval000.clas.locals_imp.abap` (Local Types tab) → behavior pool.
|
|
||||||
6. `zzc_changeapproval000.ddls.asddls` and `zc_changeapproval_a2x.ddls.asddls` → both
|
|
||||||
projection views.
|
|
||||||
7. `zzc_changeapproval000.bdef.asbdef` → UI projection BDEF.
|
|
||||||
8. Create the A2X projection BDEF (now possible): ADT *New Behavior Definition* on
|
|
||||||
`ZC_CHANGEAPPROVAL_A2X`, implementation type *Projection* — or via MCP:
|
|
||||||
`abap_creation-run_validation` + `create_object` with objectType `BDEF/BDO` and
|
|
||||||
`{"behaviorDefinitionType":"definition","packageName":"ZARENA_PLM","rootEntity":"ZC_CHANGEAPPROVAL_A2X","name":"ZC_CHANGEAPPROVAL_A2X","description":"Arena change approval: A2X projection behavior","implementationType":"Projection"}`,
|
|
||||||
transport `""`. Then paste `zc_changeapproval_a2x.bdef.asbdef`, activate.
|
|
||||||
9. Delete the generated class `ZZBP_C_CHANGEAPPROVAL000` (no longer referenced by the
|
|
||||||
projection BDEF; a projection needs a behavior pool only for new actions/functions
|
|
||||||
[source: abap-docs /abap-docs-cloud/ABENBDL_BDEF_PROJECTION_HEADER, verified 2026-09-15]).
|
|
||||||
10. `zzr_changeapproval000.dcls.asdcls`, then `zzc_changeapproval000.dcls.asdcls` and
|
|
||||||
`zc_changeapproval_a2x.dcls.asdcls` → access controls.
|
|
||||||
11. `zzc_changeapproval000.ddlx.asddlx` → metadata extension.
|
|
||||||
12. `zzui_arena_change_apprvl000_o4.srvd.srvdsrv` and
|
|
||||||
`zapi_arena_change_approval.srvd.srvdsrv` → service definitions.
|
|
||||||
13. Create the Web API binding (now possible): ADT *New Service Binding* on
|
|
||||||
`ZAPI_ARENA_CHANGE_APPROVAL`, binding type *OData V4 - Web API*, name
|
|
||||||
`ZAPI_ARENA_CHANGE_APPROVAL_A2X` — or via MCP `create_object` with objectType
|
|
||||||
`SRVB/SVB` and
|
|
||||||
`{"packageName":"ZARENA_PLM","name":"ZAPI_ARENA_CHANGE_APPROVAL_A2X","description":"Arena change approval: OData V4 Web API binding (worker)","bindingType":"OData V4 - Web API","serviceDefinition":"ZAPI_ARENA_CHANGE_APPROVAL"}`.
|
|
||||||
Activate. **Do not publish** either binding (publishing is a later brief, together
|
|
||||||
with the IAM app / communication scenario).
|
|
||||||
|
|
||||||
## Activation URIs for `abap_activate_objects` (after pasting)
|
- **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>`
|
The service definitions `ZUI_ARENA_CHANGE_APPROVAL` (25) and `ZAPI_ARENA_CHANGE_APPROVAL` (26)
|
||||||
with these folders/extensions (all verified by a successful activation):
|
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 |
|
## Preconditions
|
||||||
|---|---|---|
|
|
||||||
| TABL | `Dictionary/Database%20Tables` | `.tabl.json` |
|
|
||||||
| DDLS | `Core%20Data%20Services/Data%20Definitions` | `.ddls.acds` |
|
|
||||||
| BDEF | `Core%20Data%20Services/Behavior%20Definitions` | `.bdef.json` (`.bdef.acds`, `.bdef.asbdef`, `.bdef.abap` all fail with "array element 0 is null") |
|
|
||||||
| DDLX | `Core%20Data%20Services/Metadata%20Extensions` | `.ddlx.acds` |
|
|
||||||
| DCLS | `Core%20Data%20Services/Access%20Controls` | `.dcls.acds` |
|
|
||||||
| CLAS | `Source%20Code%20Library/Classes` | `.clas.abap` |
|
|
||||||
| SRVD | `Business%20Services/Service%20Definitions` | `.srvd.acds` |
|
|
||||||
| SRVB | `Business%20Services/Service%20Bindings` | `.srvb.json` |
|
|
||||||
| RONT | `Business%20Services/SAP%20Object%20Types` | `.ront.json` |
|
|
||||||
| NONT | `Business%20Services/SAP%20Object%20Node%20Types` | `.nont.json` |
|
|
||||||
|
|
||||||
In sequence order (`B` = `abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM`):
|
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
|
U1 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Dictionary/Database%20Tables/ZCHANGEAPPROVAL/zchangeapproval.tabl.ddic
|
||||||
B/Core%20Data%20Services/Data%20Definitions/ZD_CHANGEAPPROVAL_REJECTP/zd_changeapproval_rejectp.ddls.acds
|
U2 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/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
|
U3 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/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
|
U4 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/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
|
U5 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZR_CHANGEAPPROVAL/zr_changeapproval.ddls.acds
|
||||||
B/Core%20Data%20Services/Behavior%20Definitions/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.bdef.json
|
U6 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZR_CHANGEAPPROVAL/zr_changeapproval.bdef.abdl
|
||||||
B/Source%20Code%20Library/Classes/ZZBP_R_CHANGEAPPROVAL000/zzbp_r_changeapproval000.clas.abap
|
U7 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Source%20Code%20Library/Classes/ZBP_R_CHANGEAPPROVAL/zbp_r_changeapproval.clas.abap
|
||||||
B/Core%20Data%20Services/Data%20Definitions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddls.acds
|
U8 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddls.acds
|
||||||
B/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.ddls.acds
|
U9 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL/zc_changeapproval.bdef.abdl
|
||||||
B/Core%20Data%20Services/Behavior%20Definitions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.bdef.json
|
U10 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZR_CHANGEAPPROVAL/zr_changeapproval.dcls.acds
|
||||||
B/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.bdef.json
|
U11 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL/zc_changeapproval.dcls.acds
|
||||||
B/Core%20Data%20Services/Access%20Controls/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.dcls.acds
|
U12 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Metadata%20Extensions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddlx.acds
|
||||||
B/Core%20Data%20Services/Access%20Controls/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.dcls.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
|
||||||
B/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.dcls.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
|
||||||
B/Core%20Data%20Services/Metadata%20Extensions/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddlx.acds
|
W1 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.ddls.acds
|
||||||
B/Business%20Services/Service%20Definitions/ZZUI_ARENA_CHANGE_APPRVL000_O4/zzui_arena_change_apprvl000_o4.srvd.acds
|
W2 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.dcls.acds
|
||||||
B/Business%20Services/Service%20Definitions/ZAPI_ARENA_CHANGE_APPROVAL/zapi_arena_change_approval.srvd.acds
|
W3 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.bdef.abdl
|
||||||
B/Business%20Services/Service%20Bindings/ZZUI_ARENA_CHANGE_APPRVL000_O4/zzui_arena_change_apprvl000_o4.srvb.json
|
W4 abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Business%20Services/Service%20Definitions/ZAPI_ARENA_CHANGE_APPROVAL/zapi_arena_change_approval.srvd.acds
|
||||||
B/Business%20Services/Service%20Bindings/ZAPI_ARENA_CHANGE_APPROVAL_A2X/zapi_arena_change_approval_a2x.srvb.json
|
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
|
## App contract check (source vs `app/webapp/localService/mainService/metadata.xml`, 2026-09-16)
|
||||||
answers "No valid data model exists for object type RONT/ROT") and
|
|
||||||
`abap_business_services-fetch_services` on both bindings.
|
|
||||||
|
|
||||||
## Design decisions baked into the files (falsifiable, dated 2026-09-15)
|
**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
|
- Namespace `com.sap.gateway.srvd.zui_arena_change_approval.v0001`. Entity type
|
||||||
generated service definition *and* for the UI to expose only approve/reject/retrySync
|
`ChangeApprovalType`, not `ChangeApproval_Type`. Entity set `ChangeApproval`.
|
||||||
while the worker gets create + mark*. A service definition exposes one projection, and a
|
- `ID` `Edm.Guid`. `StatusText` `Edm.String` MaxLength 29. `StatusCriticality` `Edm.Byte`.
|
||||||
projection BDEF exposes exactly what it lists (no automatic inheritance
|
`IsApprovable`/`IsRetriable` `Edm.Boolean`. `RequestedOn`/`DecidedOn` `Edm.DateTimeOffset`
|
||||||
[source: abap-docs /abap-docs-cloud/ABENBDL_USE_PROJECTION]). Both requirements cannot
|
Precision 7. `SapPreview` `Edm.String` without MaxLength.
|
||||||
hold on one definition, so the A2X path got its own projection view/BDEF/definition and
|
- `reject` parameter `RejectReason` `Edm.String` `Nullable="false"` MaxLength 255, the same as the app.
|
||||||
the Web API binding sits on `ZAPI_ARENA_CHANGE_APPROVAL`. The RAP-2 "read-only from the
|
- Action enablement `Core.OperationAvailable Path="_it/__OperationControl/<action>"` for `approve`,
|
||||||
UI" requirement wins over the structural wording.
|
`reject` and `retrySync`.
|
||||||
- **`authorization master ( none )`** instead of the generator's `( global )`: ADR-002
|
- Insertable, Updatable and Deletable are all `false`.
|
||||||
makes the IAM app / business catalog the only access control and DCL governs reads;
|
|
||||||
`( none )` is the documented way to declare "no RAP authorization checks"
|
|
||||||
[source: abap-docs /abap-docs-cloud/ABENBDL_AUTHORIZATION]. Flip to `( global )` plus a
|
|
||||||
`FOR GLOBAL AUTHORIZATION` handler when a custom authorization object exists.
|
|
||||||
- **Derived fields are computed, not persisted.** `StatusText`, `StatusCriticality`,
|
|
||||||
`IsApprovable`, `IsRetriable` are CASE expressions in the root view; a transactional
|
|
||||||
query projection cannot hold expressions
|
|
||||||
[source: abap-docs /abap-docs-cloud/ABENCDS_DEFINE_VIEW_AS_PROJECTION]. They are
|
|
||||||
`field ( readonly )` and absent from the mapping — see step 4.
|
|
||||||
- **Action parameters are abstract entities** (`ZD_*`), one element each, because action
|
|
||||||
input must be an abstract entity or DDIC type
|
|
||||||
[source: abap-docs /abap-docs-cloud/ABENBDL_ACTION_INPUT_PARAM]. `markSynced` and
|
|
||||||
`markFailed` therefore take `SapChangeNumber` / `ErrorMessage` as parameters — the only
|
|
||||||
way an action can "set" them from the worker.
|
|
||||||
- **`internal update`** keeps `update` out of every OData surface while the actions and
|
|
||||||
the determination still update fields `IN LOCAL MODE`
|
|
||||||
[source: abap-docs /abap-docs-cloud/ABENBDL_INTERNAL].
|
|
||||||
- **RequestedBy** is the technical user of the creator (the worker's communication user in
|
|
||||||
the A2X flow). Change to `mandatory : create` if the Arena requester should be shown.
|
|
||||||
|
|
||||||
## Landmines learned live (KNOWLEDGE.md candidates)
|
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`)
|
All 17 app properties exist in `ZC_CHANGEAPPROVAL` with the same names and case. The three actions
|
||||||
or validation fails with "Builtin type of field X is invalid"; semantic-key fields pass
|
`approve`/`reject`/`retrySync` are in `ZC_CHANGEAPPROVAL` (projection BDEF), and
|
||||||
without it. The generator prepends its own `Z` to the `prefix` (`Z` → `ZZ…`) and appends
|
`ZUI_ARENA_CHANGE_APPROVAL` exposes exactly `ZC_CHANGEAPPROVAL as ChangeApproval`. `expose … as`
|
||||||
`000` to every artifact name even with `suffix: ""`; service definition and UI binding
|
sets the entity set name and `<alias>_Type` [source: abap-docs /abap-fiori-showcase/06_object_page_content,
|
||||||
get the same name (`ZZUI_ARENA_CHANGE_APPRVL000_O4`). There is no delete tool in the ADT
|
verified 2026-09-15] (live: `<alias>Type`, see above). The source is not changed for the open items below. Decisions sit with the
|
||||||
MCP, so a naming mistake is permanent from the agent side.
|
coordinator and are confirmed at U15 / V-2.
|
||||||
- [2026-09-15] `create_object` for SRVD needs `sourceType` (`"S"` worked; omitted →
|
|
||||||
|
| 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
|
"serviceDefinitionInfo.sourceType is null"). BDEF/SRVB creation validates the referenced
|
||||||
DDLS/SRVD **active** version, so source-less objects block dependent creations.
|
DDLS/SRVD's **active** version, so source-less objects block dependent creations.
|
||||||
- [2026-09-15] Activation URIs: see table above; BDEF is `.bdef.json`, table `.tabl.json`,
|
- [2026-09-15, **superseded 2026-09-16**] Activation URIs recorded on 2026-09-15 came from
|
||||||
SAP object types live under `Business Services`.
|
generator objects: BDEF `.bdef.json` and TABL `.tabl.json` (both generator-era). They are wrong for
|
||||||
- [2026-09-15] The MCP session id from `get_schema` `referenceContent` is single-use in
|
objects created with `create_object`; see the per-type entry below.
|
||||||
practice — fetch a fresh one before each `generate_objects` attempt.
|
- [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)'
|
@EndUserText.label: 'Arena change approval - Web API (worker)'
|
||||||
define service ZAPI_ARENA_CHANGE_APPROVAL
|
define service ZAPI_ARENA_CHANGE_APPROVAL
|
||||||
provider contracts odata_v4_webapi
|
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;
|
||||||
|
}
|
||||||
+9
-5
@@ -1,6 +1,10 @@
|
|||||||
/* TARGET OBJECT : data definition ZZC_CHANGEAPPROVAL000 (UI projection, generated 2026-09-15)
|
/* 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/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddls.acds
|
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Data%20Definitions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddls.acds
|
||||||
ACTION : replace entire content, then activate (after ZZR_CHANGEAPPROVAL000).
|
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];
|
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];
|
@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]. */
|
@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
|
@AccessControl.authorizationCheck: #CHECK
|
||||||
@Metadata.allowExtensions: true
|
@Metadata.allowExtensions: true
|
||||||
@ObjectModel.semanticKey: ['ItemNumber', 'ChangeType', 'ArenaEcoId']
|
@ObjectModel.semanticKey: ['ItemNumber', 'ChangeType', 'ArenaEcoId']
|
||||||
define root view entity ZZC_CHANGEAPPROVAL000
|
define root view entity ZC_CHANGEAPPROVAL
|
||||||
provider contract transactional_query
|
provider contract transactional_query
|
||||||
as projection on ZZR_CHANGEAPPROVAL000
|
as projection on ZR_CHANGEAPPROVAL
|
||||||
{
|
{
|
||||||
key ID,
|
key ID,
|
||||||
ItemNumber,
|
ItemNumber,
|
||||||
+5
-4
@@ -1,6 +1,7 @@
|
|||||||
/* TARGET OBJECT : metadata extension ZZC_CHANGEAPPROVAL000 (generated 2026-09-15)
|
/* 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/ZZC_CHANGEAPPROVAL000/zzc_changeapproval000.ddlx.acds
|
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Metadata%20Extensions/ZC_CHANGEAPPROVAL/zc_changeapproval.ddlx.acds
|
||||||
ACTION : replace entire content, then activate (after ZZC_CHANGEAPPROVAL000 view).
|
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
|
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.
|
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):
|
Sources (all verified 2026-09-15, abap-docs /abap-fiori-showcase):
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
visualizations: [ { type: #AS_LINEITEM } ]
|
visualizations: [ { type: #AS_LINEITEM } ]
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
annotate entity ZZC_CHANGEAPPROVAL000 with
|
annotate entity ZC_CHANGEAPPROVAL with
|
||||||
{
|
{
|
||||||
@UI.facet: [
|
@UI.facet: [
|
||||||
{ id: 'StatusPoint', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'StatusPoint' },
|
{ 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.
|
// TARGET OBJECT : behavior definition ZC_CHANGEAPPROVAL_A2X (A2X projection BDEF) - README step W3, worker leg
|
||||||
// The ADT MCP refused to create it while ZC_CHANGEAPPROVAL_A2X (DDLS) had no active source
|
// abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZC_CHANGEAPPROVAL_A2X/zc_changeapproval_a2x.bdef.abdl
|
||||||
// ("The referenced STOB object ZC_CHANGEAPPROVAL_A2X does not exist", 2026-09-15).
|
// ACTION : create via ADT MCP (objectType BDEF/BDO, implementationType Projection, README W3) only once the view
|
||||||
// Create it after the projection view is active: ADT > New Behavior Definition on ZC_CHANGEAPPROVAL_A2X,
|
// ZC_CHANGEAPPROVAL_A2X is active - the MCP validates the referenced entity's active version (live
|
||||||
// implementation type Projection (or re-run abap_creation-create_object BDEF/BDO, see README), paste, activate.
|
// 2026-09-15: "The referenced STOB object ZC_CHANGEAPPROVAL_A2X does not exist" while the view had no
|
||||||
// Sources: as ZZC_CHANGEAPPROVAL000. use create lets the worker stage records (ARN-010: the staged payload is what
|
// active source). Replace entire content, save, then activate.
|
||||||
// gets executed); the worker never sees approve/reject/retrySync (PRD RAP-6).
|
// 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;
|
projection;
|
||||||
strict ( 2 );
|
strict ( 2 );
|
||||||
|
|
||||||
define behavior for ZC_CHANGEAPPROVAL_A2X alias ChangeApproval
|
define behavior for ZC_CHANGEAPPROVAL_A2X alias ChangeApproval
|
||||||
|
use etag
|
||||||
{
|
{
|
||||||
use etag;
|
|
||||||
use create;
|
use create;
|
||||||
|
|
||||||
use action markExecuting;
|
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
|
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).
|
ACTION : create empty via ADT MCP (objectType DCLS/DL, README W2), replace entire content, save, then activate
|
||||||
Sources: as ZZC_CHANGEAPPROVAL000 DCL. */
|
(after ZC_CHANGEAPPROVAL_A2X view and ZR_CHANGEAPPROVAL DCL).
|
||||||
|
Sources: as ZC_CHANGEAPPROVAL DCL. */
|
||||||
@EndUserText.label: 'Access control for ZC_CHANGEAPPROVAL_A2X'
|
@EndUserText.label: 'Access control for ZC_CHANGEAPPROVAL_A2X'
|
||||||
@MappingRole: true
|
@MappingRole: true
|
||||||
define role ZC_CHANGEAPPROVAL_A2X {
|
define role ZC_CHANGEAPPROVAL_A2X {
|
||||||
grant select on 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
|
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
|
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]. */
|
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)'
|
@EndUserText.label: 'Arena change approval - A2X projection (worker)'
|
||||||
@AccessControl.authorizationCheck: #CHECK
|
@AccessControl.authorizationCheck: #CHECK
|
||||||
@ObjectModel.semanticKey: ['ItemNumber', 'ChangeType', 'ArenaEcoId']
|
@ObjectModel.semanticKey: ['ItemNumber', 'ChangeType', 'ArenaEcoId']
|
||||||
define root view entity ZC_CHANGEAPPROVAL_A2X
|
define root view entity ZC_CHANGEAPPROVAL_A2X
|
||||||
provider contract transactional_query
|
provider contract transactional_query
|
||||||
as projection on ZZR_CHANGEAPPROVAL000
|
as projection on ZR_CHANGEAPPROVAL
|
||||||
{
|
{
|
||||||
key ID,
|
key ID,
|
||||||
ItemNumber,
|
ItemNumber,
|
||||||
|
|||||||
+12
-5
@@ -1,17 +1,24 @@
|
|||||||
/* TARGET OBJECT : database table ZZCHNGPPROVAL000 (generated 2026-09-15 by x-ui-service)
|
/* 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/ZZCHNGPPROVAL000/zzchngpproval000.tabl.json
|
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Dictionary/Database%20Tables/ZCHANGEAPPROVAL/zchangeapproval.tabl.ddic
|
||||||
ACTION : replace entire content, then activate.
|
(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
|
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];
|
[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]).
|
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
|
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)'
|
@EndUserText.label : 'Arena change approval (staging + state machine)'
|
||||||
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
|
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
|
||||||
@AbapCatalog.tableCategory : #TRANSPARENT
|
@AbapCatalog.tableCategory : #TRANSPARENT
|
||||||
@AbapCatalog.deliveryClass : #A
|
@AbapCatalog.deliveryClass : #A
|
||||||
@AbapCatalog.dataMaintenance : #RESTRICTED
|
@AbapCatalog.dataMaintenance : #RESTRICTED
|
||||||
define table zzchngpproval000 {
|
define table zchangeapproval {
|
||||||
key client : abap.clnt not null;
|
key client : abap.clnt not null;
|
||||||
key id : sysuuid_x16 not null;
|
key id : sysuuid_x16 not null;
|
||||||
item_number : abap.char(18);
|
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
|
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. */
|
Sources: as ZD_CHANGEAPPROVAL_REJECTP. */
|
||||||
@EndUserText.label: 'Arena change approval: markFailed parameter'
|
@EndUserText.label: 'Arena change approval: markFailed parameter'
|
||||||
define abstract entity ZD_CHANGEAPPROVAL_FAILEDP
|
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
|
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
|
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];
|
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].
|
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
|
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. */
|
Sources: as ZD_CHANGEAPPROVAL_REJECTP. */
|
||||||
@EndUserText.label: 'Arena change approval: markSynced parameter'
|
@EndUserText.label: 'Arena change approval: markSynced parameter'
|
||||||
define abstract entity ZD_CHANGEAPPROVAL_SYNCEDP
|
define abstract entity ZD_CHANGEAPPROVAL_SYNCEDP
|
||||||
|
|||||||
+27
-9
@@ -1,6 +1,12 @@
|
|||||||
// TARGET OBJECT : behavior definition ZZR_CHANGEAPPROVAL000 (base BDEF, managed; generated 2026-09-15)
|
// 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/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.bdef.json
|
// abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Behavior%20Definitions/ZR_CHANGEAPPROVAL/zr_changeapproval.bdef.json
|
||||||
// ACTION : replace entire content, then activate (after the three ZD_* abstract entities and ZZR_CHANGEAPPROVAL000).
|
// 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):
|
// Sources (all verified 2026-09-15):
|
||||||
// action syntax incl. ( features : instance ), parameter, result [1] $self
|
// 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]
|
// [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]
|
// 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]
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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.
|
// 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 );
|
strict ( 2 );
|
||||||
|
|
||||||
define behavior for ZZR_CHANGEAPPROVAL000 alias ChangeApproval
|
define behavior for ZR_CHANGEAPPROVAL alias ChangeApproval
|
||||||
persistent table zzchngpproval000
|
persistent table zchangeapproval
|
||||||
lock master
|
lock master
|
||||||
authorization master ( none )
|
authorization master ( none )
|
||||||
etag master LocalLastChangedAt
|
etag master LocalLastChangedAt
|
||||||
@@ -36,7 +51,7 @@ etag master LocalLastChangedAt
|
|||||||
SapChangeNumber, ErrorMessage, RejectReason,
|
SapChangeNumber, ErrorMessage, RejectReason,
|
||||||
CreatedBy, CreatedAt, LastChangedBy, LastChangedAt, LocalLastChangedAt;
|
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 ) approve result [1] $self;
|
||||||
action ( features : instance ) reject parameter ZD_CHANGEAPPROVAL_REJECTP result [1] $self;
|
action ( features : instance ) reject parameter ZD_CHANGEAPPROVAL_REJECTP result [1] $self;
|
||||||
action ( features : instance ) retrySync result [1] $self;
|
action ( features : instance ) retrySync result [1] $self;
|
||||||
@@ -48,7 +63,10 @@ etag master LocalLastChangedAt
|
|||||||
|
|
||||||
determination setInitialStatus on modify { create; }
|
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;
|
ID = id;
|
||||||
ItemNumber = item_number;
|
ItemNumber = item_number;
|
||||||
+7
-6
@@ -1,12 +1,13 @@
|
|||||||
/* TARGET OBJECT : access control ZZR_CHANGEAPPROVAL000 (generated 2026-09-15)
|
/* 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/ZZR_CHANGEAPPROVAL000/zzr_changeapproval000.dcls.acds
|
abap: URI : abap:/repotree-v1/0M7DEJI/System%20Library/ZLOCAL/ZARENA_PLM/Core%20Data%20Services/Access%20Controls/ZR_CHANGEAPPROVAL/zr_changeapproval.dcls.acds
|
||||||
ACTION : replace entire content, then activate.
|
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
|
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].
|
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].
|
@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. */
|
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
|
@MappingRole: true
|
||||||
define role ZZR_CHANGEAPPROVAL000 {
|
define role ZR_CHANGEAPPROVAL {
|
||||||
grant select on ZZR_CHANGEAPPROVAL000;
|
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&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>
|
||||||
-653
@@ -1,653 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<!-- Google Tag Manager -->
|
|
||||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
||||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
||||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
||||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
||||||
})(window,document,'script','dataLayer','GTM-5WLCZXC');</script>
|
|
||||||
<!-- End Google Tag Manager --> <meta name="author" content="Denis Roy"/>
|
|
||||||
<meta name="keywords" content="eclipse,project,plug-ins,plugins,java,ide,swt,refactoring,free java ide,tools,platform,open source,development environment,development,ide"/>
|
|
||||||
<link rel="shortcut icon" href="/eclipse.org-common/themes/solstice/public/images/favicon.ico"/>
|
|
||||||
<title>Eclipse downloads - Select a mirror | The Eclipse Foundation</title>
|
|
||||||
<link rel="preconnect stylesheet" href="https://www.eclipse.org/public/css/neptune.css"/>
|
|
||||||
<meta name="description" content="The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks."/>
|
|
||||||
<meta property="og:description" content="The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks."/>
|
|
||||||
<meta property="og:image" content="https://www.eclipse.org/images/og-image.jpg"/>
|
|
||||||
<meta property="og:title" content="Eclipse downloads - Select a mirror | The Eclipse Foundation"/>
|
|
||||||
<meta property="og:image:width" content="1200"/>
|
|
||||||
<meta property="og:image:height" content="630"/>
|
|
||||||
<meta name="twitter:site" content="@EclipseFdn"/>
|
|
||||||
<meta name="twitter:card" content="summary"/>
|
|
||||||
<meta name="twitter:title" content="Eclipse downloads - Select a mirror | The Eclipse Foundation"/>
|
|
||||||
<meta name="twitter:url" content="https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip"/>
|
|
||||||
<meta name="twitter:description" content="The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks."/>
|
|
||||||
<meta name="twitter:image" content="https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-400x400.png"/>
|
|
||||||
<link href="/downloads/assets/public/stylesheets/mirror.min.css" media="screen" rel="stylesheet" type="text/css"/><link href="https://fonts.googleapis.com/css2?family=Libre+Franklin:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="preconnect stylesheet" type="text/css"/>
|
|
||||||
<link rel="stylesheet"
|
|
||||||
href="https://cdn.jsdelivr.net/npm/@eclipsefdn/solstice-components@0.9.0/themes/compatibility/ef-light.css"
|
|
||||||
integrity="sha384-OwhjLHnTIrR0xdp4DimHPw/OeD7m47zoau2qQEXbBR8yjwN1QI5zkxHnZUZ1CW7m"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script type="module"
|
|
||||||
src="https://cdn.jsdelivr.net/npm/@eclipsefdn/solstice-components@0.9.0/dist-cdn/solstice-components.loader.js"
|
|
||||||
integrity="sha384-UcWSQMj1c1b1VRiaHycuVGCSJ7ltwyF88VcYGthnJauU0cyXHJXhPWBuMA5vHM/a"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script> var eclipse_org_common = {"settings":{"cookies_class":{"name":"eclipse_settings","enabled":1}}}</script> </head>
|
|
||||||
<body class="hidden-cfa-button" id="body_solstice">
|
|
||||||
<!-- Google Tag Manager (noscript) -->
|
|
||||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5WLCZXC"
|
|
||||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
|
||||||
<!-- End Google Tag Manager (noscript) --> <a class="sr-only" href="#content">Skip to main content</a>
|
|
||||||
<div class="eclipsefdn-featured-story-banner" data-publish-target="eclipse_org"></div>
|
|
||||||
<header class="header-wrapper" id="header">
|
|
||||||
<div class="navigation-bar dark">
|
|
||||||
<div class="container">
|
|
||||||
<div class="navigation-bar-inner">
|
|
||||||
<div class="navigation-bar-left">
|
|
||||||
<div class="wrapper-logo-default"><a href="https://www.eclipse.org/"><img class="logo-eclipse-default img-responsive navigation-bar-brand" alt="Eclipse.org logo" width="120" src="//www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-white-orange.svg"/></a></div> </div>
|
|
||||||
<nav class="navigation-bar-nav hidden-xs hidden-sm">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<button class="btn btn-link" type="button" data-target="join-us-menu" aria-expanded="false">
|
|
||||||
Join us
|
|
||||||
</button>
|
|
||||||
</li> <li>
|
|
||||||
<button class="btn btn-link" type="button" data-target="what-we-do-menu" aria-expanded="false">
|
|
||||||
What we do
|
|
||||||
</button>
|
|
||||||
</li> <li>
|
|
||||||
<button class="btn btn-link" type="button" data-target="resources-menu" aria-expanded="false">
|
|
||||||
Resources
|
|
||||||
</button>
|
|
||||||
</li> <li>
|
|
||||||
<button class="btn btn-link" type="button" data-target="about-us-menu" aria-expanded="false">
|
|
||||||
About us
|
|
||||||
</button>
|
|
||||||
</li> </ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="navigation-bar-right">
|
|
||||||
<div class="navigation-bar-controls hidden-xs hidden-sm">
|
|
||||||
<ul>
|
|
||||||
<li class="dropdown">
|
|
||||||
<a class="dropdown-toggle link-unstyled focus-highlight" tabindex="0" id="search-dropdown-toggle" data-toggle="dropdown" href="#" aria-label="Search">
|
|
||||||
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<form class="search-bar dropdown-menu dropdown-menu-right" aria-labelledby="search-dropdown-toggle" action="https://www.eclipse.org/home/search/" method="get">
|
|
||||||
<input class="search-bar-input" name="q" placeholder="Search">
|
|
||||||
<button class="btn btn-link" type="submit">
|
|
||||||
<i class="fa-solid fa-arrow-right"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
<li class="dropdown">
|
|
||||||
<a class="dropdown-toggle focus-highlight" id="user-menu" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" tabindex="0">
|
|
||||||
<i class="fa-regular fa-user" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="user-menu">
|
|
||||||
<li>
|
|
||||||
<a href="https://accounts.eclipse.org/user">
|
|
||||||
<i class="fa-solid fa-user margin-right-10" aria-hidden="true"></i>
|
|
||||||
View my account
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://accounts.eclipse.org/user/edit">
|
|
||||||
<i class="fa-solid fa-edit margin-right-10" aria-hidden="true"></i>
|
|
||||||
Edit my account
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a role="link" class="toolbar-manage-cookies" href="#" tabindex="0">
|
|
||||||
<i class="fa-solid fa-wrench margin-right-10" aria-hidden=""></i>
|
|
||||||
Manage cookies
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="navigation-bar-ctas hidden-xs hidden-sm">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<a class="mobile-menu-toggle visible-xs visible-sm" aria-expanded="false"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mobile-menu hidden">
|
|
||||||
<nav class="mobile-menu-nav"><ul> <li>
|
|
||||||
<a role="button" href="#mobile-menu-join-us" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-join-us">
|
|
||||||
Join us
|
|
||||||
<i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<ul class="collapse" id="mobile-menu-join-us">
|
|
||||||
<li>
|
|
||||||
<a role="button" href="#mobile-menu-membership" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-membership">Membership <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-membership">
|
|
||||||
<li><a href="/membership/" target="">Become a member</a></li><li><a href="/membership/#tab-fees" target="">Review membership fees</a></li><li><a href="https://membership.eclipse.org/portal" target="">Access member portal</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-sponsorship" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-sponsorship">Sponsorship <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-sponsorship">
|
|
||||||
<li><a href="/sponsor/" target="">Become a sponsor</a></li><li><a href="/org/our-sponsors/" target="">Sponsor directory</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-contribute" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-contribute">Contribute <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-contribute">
|
|
||||||
<li><a href="/contribute/" target="">How to contribute</a></li><li><a href="/projects/bring-your-project/" target="">Bring us your project</a></li><li><a href="/projects/training/" target="">Committer training</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-collaborate-with-us" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-collaborate-with-us">Collaborate with us <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-collaborate-with-us">
|
|
||||||
<li><a href="/org/working-groups/explore/" target="">Join an existing industry collaboration</a></li><li><a href="/collaborations/#start-collaboration" target="">Start a new collaboration</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-what-we-do" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-what-we-do">
|
|
||||||
What we do
|
|
||||||
<i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<ul class="collapse" id="mobile-menu-what-we-do">
|
|
||||||
<li>
|
|
||||||
<a role="button" href="#mobile-menu-projects" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-projects">Projects <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-projects">
|
|
||||||
<li><a href="/projects/" target="">Explore projects</a></li><li><a href="/specifications/" target="">View specifications</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-industry-collaborations" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-industry-collaborations">Industry collaborations <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-industry-collaborations">
|
|
||||||
<li><a href="/org/working-groups/explore/" target="">Explore collaborations</a></li><li><a href="/org/working-groups/about/" target="">Join a working group</a></li><li><a href="/collaborations/interest-groups/" target="">Join an interest group</a></li><li><a href="/org/value/" target="">Read success stories</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-core-services" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-core-services">Core services <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-core-services">
|
|
||||||
<li><a href="/org/services/" target="">Explore core services</a></li><li><a href="/security/" target="">Ensure project security</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-key-initiatives" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-key-initiatives">Key initiatives <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-key-initiatives">
|
|
||||||
<li><a href="/topics/edge-and-iot/" target="">Embedded & IoT</a></li><li><a href="/topics/cloud-native/" target="">Enterprise Java</a></li><li><a href="https://dataspaces.eclipse.org/" target="">Data sovereignty</a></li><li><a href="/security/" target="">Security & privacy</a></li><li><a href="/topics/ide/" target="">Developer tools & IDEs</a></li><li><a href="https://aieclipse.org/" target="">AI & emerging technologies <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li><li><a href="/topics/automotive-and-mobility/" target="">Automotive & mobility</a></li><li><a href="https://orcwg.org/" target="">Policy & compliance <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-strategic-services" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-strategic-services">Strategic services <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-strategic-services">
|
|
||||||
<li><a href="/research/" target="">Transform research into open source</a></li><li><a href="/os4biz/services/" target="">Engage professional services</a></li><li><a href="/os4biz/ospo/" target="">Build your OSPO</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-resources" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-resources">
|
|
||||||
Resources
|
|
||||||
<i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<ul class="collapse" id="mobile-menu-resources">
|
|
||||||
<li>
|
|
||||||
<a role="button" href="#mobile-menu-what's-happening" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-what's-happening">What's happening <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-what's-happening">
|
|
||||||
<li><a href="https://blogs.eclipse.org/" target="">Read our blog</a></li><li><a href="https://accounts.eclipse.org/mailing-list" target="">Join mailing lists</a></li><li><a href="https://newsroom.eclipse.org/" target="">Get news updates</a></li><li><a href="/community/eclipse_newsletter" target="">Subscribe to newsletter</a></li><li><a href="https://opensourceglobalvoices.org" target="_blank">Explore OSS global voices <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-events" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-events">Events <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-events">
|
|
||||||
<li><a href="https://events.eclipse.org/" target="">Explore events</a></li><li><a href="/events/open-community-meetups/" target="">Join a community meetup</a></li><li><a href="/events/open-community-webinars/" target="">Attend a webinar</a></li><li><a href="https://www.ocxconf.org/" target="_blank">OCX <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-developer-resources" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-developer-resources">Developer resources <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-developer-resources">
|
|
||||||
<li><a href="/projects/" target="">Explore projects hub</a></li><li><a href="/projects/calendar/#office-hours" target="">Attend office hours</a></li><li><a href="/security/" target="">Report a vulnerability</a></li><li><a href="/security/known/" target="">View known vulnerabilities</a></li><li><a href="https://github.com/eclipse-csi/security-handbook/blob/main/docs/vulnerabilities/advisories.md" target="_blank">Use the security handbook</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-industry-insights" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-industry-insights">Industry insights <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-industry-insights">
|
|
||||||
<li><a href="/org/value/#success_stories" target="">View case studies</a></li><li><a href="/org/value/#whitepapers" target="">View whitepapers</a></li><li><a href="/org/value/#surveyReports" target="">View surveys & reports</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-marketplaces" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-marketplaces">Marketplaces <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-marketplaces">
|
|
||||||
<li><a href="https://adoptium.net/marketplace" target="_blank">Adoptium <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li><li><a href="https://marketplace.eclipse.org/" target="_blank">Eclipse IDE <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li><li><a href="https://open-vsx.org/" target="_blank">Open VSX <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-about-us" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-about-us">
|
|
||||||
About us
|
|
||||||
<i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<ul class="collapse" id="mobile-menu-about-us">
|
|
||||||
<li>
|
|
||||||
<a role="button" href="#mobile-menu-the-foundation" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-the-foundation">The Foundation <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-the-foundation">
|
|
||||||
<li><a href="/about/" target="">About</a></li><li><a href="/org/foundation/staff/" target="">Meet the team</a></li><li><a href="/org/governance/" target="">Board & governance</a></li><li><a href="/org/foundation/eclipseawards/" target="">Awards</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-our-members" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-our-members">Our members <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-our-members">
|
|
||||||
<li><a href="/membership/" target="">Become a member</a></li><li><a href="/membership/explore-membership/" target="">View member directory</a></li><li><a href="https://membership.eclipse.org/portal" target="_blank">Access member portal</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-sponsorship" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-sponsorship">Sponsorship <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-sponsorship">
|
|
||||||
<li><a href="/sponsor/" target="">Become a sponsor</a></li><li><a href="/org/our-sponsors/" target="">Sponsor directory</a></li>
|
|
||||||
</ul>
|
|
||||||
</li> <li>
|
|
||||||
<a role="button" href="#mobile-menu-more" data-toggle="collapse" aria-expanded="false" aria-controls="mobile-menu-more">More <i class="fa-solid fa-chevron-down" aria-hidden="true"></i></a>
|
|
||||||
<ul class="collapse" id="mobile-menu-more">
|
|
||||||
<li><a href="/careers/" target="">Join our team</a></li><li><a href="/org/artwork/" target="">Explore our brand</a></li><li><a href="/org/foundation/contact.php" target="">Contact us</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li></ul></nav>
|
|
||||||
</div> <div class="mega-menu"> <div class="menu hidden" id="join-us-menu" aria-expanded="false">
|
|
||||||
<div class="container">
|
|
||||||
<nav>
|
|
||||||
<div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-user-group" aria-hidden="true"></i></span><h2 class="menu-heading">Membership</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/membership/" target="">Become a member</a></li><li><a href="/membership/#tab-fees" target="">Review membership fees</a></li><li><a href="https://membership.eclipse.org/portal" target="">Access member portal</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-bookmark" aria-hidden="true"></i></span><h2 class="menu-heading">Sponsorship</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/sponsor/" target="">Become a sponsor</a></li><li><a href="/org/our-sponsors/" target="">Sponsor directory</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-handshake-simple" aria-hidden="true"></i></span><h2 class="menu-heading">Contribute</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/contribute/" target="">How to contribute</a></li><li><a href="/projects/bring-your-project/" target="">Bring us your project</a></li><li><a href="/projects/training/" target="">Committer training</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-users" aria-hidden="true"></i></span><h2 class="menu-heading">Collaborate with us</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/org/working-groups/explore/" target="">Join an existing industry collaboration</a></li><li><a href="/collaborations/#start-collaboration" target="">Start a new collaboration</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div> <div class="menu hidden" id="what-we-do-menu" aria-expanded="false">
|
|
||||||
<div class="container">
|
|
||||||
<nav>
|
|
||||||
<div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-puzzle-piece" aria-hidden="true"></i></span><h2 class="menu-heading">Projects</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/projects/" target="">Explore projects</a></li><li><a href="/specifications/" target="">View specifications</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-building" aria-hidden="true"></i></span><h2 class="menu-heading">Industry collaborations</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/org/working-groups/explore/" target="">Explore collaborations</a></li><li><a href="/org/working-groups/about/" target="">Join a working group</a></li><li><a href="/collaborations/interest-groups/" target="">Join an interest group</a></li><li><a href="/org/value/" target="">Read success stories</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-laptop-code" aria-hidden="true"></i></span><h2 class="menu-heading">Core services</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/org/services/" target="">Explore core services</a></li><li><a href="/security/" target="">Ensure project security</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-bullseye" aria-hidden="true"></i></span><h2 class="menu-heading">Key initiatives</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/topics/edge-and-iot/" target="">Embedded & IoT</a></li><li><a href="/topics/cloud-native/" target="">Enterprise Java</a></li><li><a href="https://dataspaces.eclipse.org/" target="">Data sovereignty</a></li><li><a href="/security/" target="">Security & privacy</a></li><li><a href="/topics/ide/" target="">Developer tools & IDEs</a></li><li><a href="https://aieclipse.org/" target="">AI & emerging technologies <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li><li><a href="/topics/automotive-and-mobility/" target="">Automotive & mobility</a></li><li><a href="https://orcwg.org/" target="">Policy & compliance <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="grid-column: -3; margin-top: -20rem;">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-flag" aria-hidden="true"></i></span><h2 class="menu-heading">Strategic services</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/research/" target="">Transform research into open source</a></li><li><a href="/os4biz/services/" target="">Engage professional services</a></li><li><a href="/os4biz/ospo/" target="">Build your OSPO</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div> <div class="menu hidden" id="resources-menu" aria-expanded="false">
|
|
||||||
<div class="container">
|
|
||||||
<nav>
|
|
||||||
<div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-bullhorn" aria-hidden="true"></i></span><h2 class="menu-heading">What's happening</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://blogs.eclipse.org/" target="">Read our blog</a></li><li><a href="https://accounts.eclipse.org/mailing-list" target="">Join mailing lists</a></li><li><a href="https://newsroom.eclipse.org/" target="">Get news updates</a></li><li><a href="/community/eclipse_newsletter" target="">Subscribe to newsletter</a></li><li><a href="https://opensourceglobalvoices.org" target="_blank">Explore OSS global voices <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-calendar-days" aria-hidden="true"></i></span><h2 class="menu-heading">Events</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://events.eclipse.org/" target="">Explore events</a></li><li><a href="/events/open-community-meetups/" target="">Join a community meetup</a></li><li><a href="/events/open-community-webinars/" target="">Attend a webinar</a></li><li><a href="https://www.ocxconf.org/" target="_blank">OCX <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-code" aria-hidden="true"></i></span><h2 class="menu-heading">Developer resources</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/projects/" target="">Explore projects hub</a></li><li><a href="/projects/calendar/#office-hours" target="">Attend office hours</a></li><li><a href="/security/" target="">Report a vulnerability</a></li><li><a href="/security/known/" target="">View known vulnerabilities</a></li><li><a href="https://github.com/eclipse-csi/security-handbook/blob/main/docs/vulnerabilities/advisories.md" target="_blank">Use the security handbook</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-gear" aria-hidden="true"></i></span><h2 class="menu-heading">Industry insights</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/org/value/#success_stories" target="">View case studies</a></li><li><a href="/org/value/#whitepapers" target="">View whitepapers</a></li><li><a href="/org/value/#surveyReports" target="">View surveys & reports</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="grid-column: -2; margin-top: -6rem;">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-store" aria-hidden="true"></i></span><h2 class="menu-heading">Marketplaces</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://adoptium.net/marketplace" target="_blank">Adoptium <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li><li><a href="https://marketplace.eclipse.org/" target="_blank">Eclipse IDE <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li><li><a href="https://open-vsx.org/" target="_blank">Open VSX <i class="fa-solid fa-arrow-up-right-from-square external-icon" aria-hidden="true"></i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div> <div class="menu hidden" id="about-us-menu" aria-expanded="false">
|
|
||||||
<div class="container">
|
|
||||||
<nav>
|
|
||||||
<div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-moon rotate-90" aria-hidden="true"></i></span><h2 class="menu-heading">The Foundation</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/about/" target="">About</a></li><li><a href="/org/foundation/staff/" target="">Meet the team</a></li><li><a href="/org/governance/" target="">Board & governance</a></li><li><a href="/org/foundation/eclipseawards/" target="">Awards</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-user-group" aria-hidden="true"></i></span><h2 class="menu-heading">Our members</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/membership/" target="">Become a member</a></li><li><a href="/membership/explore-membership/" target="">View member directory</a></li><li><a href="https://membership.eclipse.org/portal" target="_blank">Access member portal</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-bookmark" aria-hidden="true"></i></span><h2 class="menu-heading">Sponsorship</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/sponsor/" target="">Become a sponsor</a></li><li><a href="/org/our-sponsors/" target="">Sponsor directory</a></li>
|
|
||||||
</ul>
|
|
||||||
</div> <div class="submenu" style="">
|
|
||||||
<div class="menu-heading-group">
|
|
||||||
<span class="menu-pre"><i class="fa-solid fa-cube" aria-hidden="true"></i></span><h2 class="menu-heading">More</h2>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/careers/" target="">Join our team</a></li><li><a href="/org/artwork/" target="">Explore our brand</a></li><li><a href="/org/foundation/contact.php" target="">Contact us</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div></div>
|
|
||||||
</header> <main id="content">
|
|
||||||
|
|
||||||
<div class="novaContent container" id="novaContent">
|
|
||||||
<div class="row">
|
|
||||||
<section class="default-breadcrumbs hidden-print breadcrumbs-default-margin" id="breadcrumb">
|
|
||||||
<div class="container">
|
|
||||||
<h3 class="sr-only">Breadcrumbs</h3>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-24"><ol class="breadcrumb"><li><a href="/">Home</a></li><li><a href="https://www.eclipse.org/downloads">Downloads</a></li><li class="active">Eclipse downloads - Select a mirror</li></ol></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section> <!-- /#breadcrumb -->
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-md-24 main-col-content">
|
|
||||||
<div class=" main-col-content">
|
|
||||||
<div class="col-md-18">
|
|
||||||
<p>All downloads are provided under the terms and conditions of the <a href="/legal/epl/notice.php">Eclipse Foundation
|
|
||||||
Software User Agreement</a> unless otherwise specified.</p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-24">
|
|
||||||
<div class="mirror-well text-center">
|
|
||||||
<a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1135" class="btn btn-primary margin-bottom-15 btn-lg"><i class="fa fa-download"></i> Download</a>
|
|
||||||
|
|
||||||
<p><strong>Download from:</strong> Canada - Rafal Rzeczkowski (https)</p>
|
|
||||||
<p><strong>File:</strong> <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1135" class="orange">eclipse-java-2026-09-R-win32-x86_64.zip</a> <a class="btn btn-ajax-checksum btn-xs btn-default" data-checksum-file="/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip" data-checksum-type="sha512" data-toggle="tab" role="tab" aria-controls="settings" href="#btn-ajax-checksum-sha512">SHA-512</a></p>
|
|
||||||
<div class="tab-content">
|
|
||||||
<div id="btn-ajax-checksum-sha512" class="tab-pane fade" role="tabpanel">
|
|
||||||
<div class="well well-sha512">
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p><a role="button" data-toggle="collapse" href="#collapseExample" aria-expanded="false" aria-controls="collapseExample">>> Select Another Mirror</a></p>
|
|
||||||
<div class="collapse" id="collapseExample">
|
|
||||||
<div class="well choose-mirror-well">
|
|
||||||
<i class="fa fa-times-circle close-choose-mirror-well" aria-hidden="true"></i>
|
|
||||||
<h2>Choose a mirror close to you</h2>
|
|
||||||
<hr>
|
|
||||||
<div class="choose-mirror">
|
|
||||||
<div class="mirror-continent mirror-section-1">
|
|
||||||
<h3>North America</h3>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li class="margin-bottom-10">United States - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1281" target="">University of Maryland</a></li>
|
|
||||||
<li class="margin-bottom-10">United States - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1323" target="">Old Dominion University Computer Science Departmen</a></li>
|
|
||||||
<li class="margin-bottom-10">Canada - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=492" target="">University of Waterloo Computer Science Club</a></li>
|
|
||||||
<li class="margin-bottom-10">Canada - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1317" target="">CICKU</a></li>
|
|
||||||
<li class="margin-bottom-10">United States - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=272" target="">OSU Open Source Lab</a></li>
|
|
||||||
<li class="margin-bottom-10">United States - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=518" target="">XMission Internet</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mirror-continent mirror-section-2">
|
|
||||||
<h3>Europe</h3>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li class="margin-bottom-10">Netherlands - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1285" target="">LiteServer</a></li>
|
|
||||||
<li class="margin-bottom-10">Germany - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1324" target="">Hochschule Esslingen</a></li>
|
|
||||||
<li class="margin-bottom-10">Czech Republic - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1099" target="">UPC Ceska republika, a.s.</a></li>
|
|
||||||
<li class="margin-bottom-10">Russian Federation - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1029" target="">Tomsk State Pedagogical University</a></li>
|
|
||||||
<li class="margin-bottom-10">Germany - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1045" target="">RWTH Aachen University</a></li>
|
|
||||||
<li class="margin-bottom-10">Netherlands - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1186" target="">SNT, University of Twente</a></li>
|
|
||||||
<li class="margin-bottom-10">France - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1208" target="">CNRS IBCP</a></li>
|
|
||||||
<li class="margin-bottom-10">Serbia & Montenegro - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1325" target="">Serbian Open eXchange</a></li>
|
|
||||||
<li class="margin-bottom-10">Sweden - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1156" target="">Academic Computer Club in Umeå, Sweden</a></li>
|
|
||||||
<li class="margin-bottom-10">Denmark - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1260" target="">dotsrc.org</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mirror-continent mirror-section-3">
|
|
||||||
<h3>Asia</h3>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li class="margin-bottom-10">Japan - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=105" target="">Japan Advanced Institute of Science and Technology</a></li>
|
|
||||||
<li class="margin-bottom-10">Japan - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1287" target="">Yamagata University</a></li>
|
|
||||||
<li class="margin-bottom-10">China - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1290" target="">eScience Center, Nanjing University</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mirror-continent mirror-section-4">
|
|
||||||
<h3>Australia/Oceania</h3>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li class="margin-bottom-10">Australia - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1051" target="">Australian Academic Research Network</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mirror-continent mirror-section-5">
|
|
||||||
<h3>South America</h3>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li class="margin-bottom-10">Brazil - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=576" target="">C3SL - Federal University of Parana</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-sm btn-primary" id="show_all_mirrors" href="#show_all_mirrors">Show all</a>
|
|
||||||
</div>
|
|
||||||
<div class="choose-mirror">
|
|
||||||
<h3>Main Download Site</h3>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li>Canada - <a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&mirror_id=1">Main eclipse.org downloads area</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="download-faster col-md-24">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="rightcolumn" class="col-sm-6">
|
|
||||||
<div class="padding-top-10 padding-bottom-30">
|
|
||||||
<div class="eclipsefdn-promo-content text-center" data-ad-format="ads_square" data-ad-publish-to="eclipse_org_downloads"></div>
|
|
||||||
</div>
|
|
||||||
<div class="sideitem">
|
|
||||||
<h6>Other options for this file</h6>
|
|
||||||
<ul>
|
|
||||||
<li><a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip">All mirrors</a> (<a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&format=xml">xml</a>)</li>
|
|
||||||
<li><a href="download.php?file=/technology/epp/downloads/release/2026-09/R/eclipse-java-2026-09-R-win32-x86_64.zip&r=1">Direct link to file</a> (download starts immediately from best mirror)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="sideitem">
|
|
||||||
<h6>Related Links</h6>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/sponsor/">Sponsor</a></li>
|
|
||||||
<li><a href="mir_request.php">Becoming a mirror site</a></li>
|
|
||||||
<li><a href="https://help.eclipse.org/2020-06/index.jsp?topic=/org.eclipse.platform.doc.user/tasks/tasks-129.htm">Updating and installing Eclipse components</a></li>
|
|
||||||
<li><a href="/forums/">Eclipse forums</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<aside class="eclipsefdn-featured-footer featured-footer" id="featured-footer" data-publish-target="eclipse_org"><div class="container"><div class="row"><div class="col-sm-24 featured-container"></div></div></div></aside>
|
|
||||||
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="container">
|
|
||||||
<div class="footer-inner">
|
|
||||||
<section class="links-section">
|
|
||||||
<ul>
|
|
||||||
<li>Eclipse Foundation</li>
|
|
||||||
<ul><li><a href="https://www.eclipse.org/org/" class="">About</a></li><li><a href="https://projects.eclipse.org/" class="">Projects</a></li><li><a href="https://www.eclipse.org/collaborations/" class="">Collaborations</a></li><li><a href="https://www.eclipse.org/membership/" class="">Membership</a></li><li><a href="https://www.eclipse.org/sponsor/" class="">Sponsor</a></li></ul>
|
|
||||||
</ul> <ul>
|
|
||||||
<li>Legal</li>
|
|
||||||
<ul><li><a href="https://www.eclipse.org/legal/privacy.php" class="">Privacy Policy</a></li><li><a href="https://www.eclipse.org/legal/termsofuse.php" class="">Terms of Use</a></li><li><a href="https://www.eclipse.org/legal/compliance/" class="">Compliance</a></li><li><a href="https://www.eclipse.org/org/documents/Community_Code_of_Conduct.php" class="">Code of Conduct</a></li><li><a href="https://www.eclipse.org/legal/" class="">Legal Resources</a></li><li><a href="#" class="toolbar-manage-cookies">Manage Cookies</a></li></ul>
|
|
||||||
</ul> <ul>
|
|
||||||
<li>More</li>
|
|
||||||
<ul><li><a href="https://www.eclipse.org/security/" class="">Report a Vulnerability</a></li><li><a href="https://www.eclipsestatus.io/" class="">Service Status</a></li><li><a href="https://www.eclipse.org/org/foundation/contact.php" class="">Contact</a></li><li><a href="https://www.eclipse.org//projects/support/" class="">Support</a></li></ul>
|
|
||||||
</ul> </section>
|
|
||||||
<section class="social-media-section">
|
|
||||||
<h2 class="subheading">See what we're up to</h2>
|
|
||||||
<ul class="social-media-list">
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://x.com/EclipseFdn" aria-label="X">
|
|
||||||
<span class="fa-stack">
|
|
||||||
<i class="fa-solid fa-circle fa-stack-2x" aria-hidden="true"></i>
|
|
||||||
<i class="fa-brands fa-x-twitter fa-stack-1x fa-inverse" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://bsky.app/profile/eclipse.org" aria-label="Bluesky">
|
|
||||||
<span class="fa-stack">
|
|
||||||
<i class="fa-solid fa-circle fa-stack-2x" aria-hidden="true"></i>
|
|
||||||
<i class="fa-brands fa-bluesky fa-stack-1x fa-inverse" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://mastodon.social/@EclipseFdn" aria-label="Mastodon">
|
|
||||||
<span class="fa-stack">
|
|
||||||
<i class="fa-solid fa-circle fa-stack-2x" aria-hidden="true"></i>
|
|
||||||
<i class="fa-brands fa-mastodon fa-stack-1x fa-inverse" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://www.facebook.com/eclipse.org" aria-label="Facebook">
|
|
||||||
<span class="fa-stack">
|
|
||||||
<i class="fa-solid fa-circle fa-stack-2x" aria-hidden="true"></i>
|
|
||||||
<i class="fa-brands fa-facebook-f fa-stack-1x fa-inverse" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://www.instagram.com/eclipsefoundation/" aria-label="Instagram">
|
|
||||||
<span class="fa-stack">
|
|
||||||
<i class="fa-solid fa-circle fa-stack-2x" aria-hidden="true"></i>
|
|
||||||
<i class="fa-brands fa-instagram fa-stack-1x fa-inverse" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://www.youtube.com/user/EclipseFdn" aria-label="YouTube">
|
|
||||||
<span class="fa-stack">
|
|
||||||
<i class="fa-solid fa-circle fa-stack-2x" aria-hidden="true"></i>
|
|
||||||
<i class="fa-brands fa-youtube fa-stack-1x fa-inverse" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://www.linkedin.com/company/eclipse-foundation" aria-label="LinkedIn">
|
|
||||||
<span class="fa-stack">
|
|
||||||
<i class="fa-solid fa-circle fa-stack-2x" aria-hidden="true"></i>
|
|
||||||
<i class="fa-brands fa-linkedin-in fa-stack-1x fa-inverse" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section class="newsletter-section">
|
|
||||||
<p class="subheading">Stay up to date</p>
|
|
||||||
<h2>Subscribe to our newsletter</h2>
|
|
||||||
<form class="newsletter-form margin-top-40">
|
|
||||||
<div class="text-field-outlined">
|
|
||||||
<input type="email" placeholder="Your email">
|
|
||||||
<button class="newsletter-form-submit" type="submit" aria-label="Submit">
|
|
||||||
<i class="fa-solid fa-arrow-right" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
<section class="footer-bottom-section">
|
|
||||||
<img class="img-responsive" src="https://webdev.eclipse.org/docs/solstice/legacy/dist/images/logo/eclipse-foundation-white-orange.svg" alt="Eclipse Foundation">
|
|
||||||
<div class="legal-information">
|
|
||||||
<p id="copyright-text">Copyright © Eclipse Foundation AISBL. All Rights Reserved.</p>
|
|
||||||
<ul class="legal-links">
|
|
||||||
<li><a href="https://www.eclipse.org/legal/privacy/">Privacy policy</a></li>
|
|
||||||
<li><a href="https://www.eclipse.org/legal/terms-of-use/">Terms of use</a></li>
|
|
||||||
<li><a href="https://www.eclipse.org/legal/compliance/">Compliance</a></li>
|
|
||||||
<li><a href="https://www.eclipse.org/legal/">Legal</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
<!-- Placed at the end of the document so the pages load faster -->
|
|
||||||
<script src="https://www.eclipse.org/public/js/neptune/main.js?v=0.1.3"></script>
|
|
||||||
<script type="text/javascript" src="/downloads/assets/public/javascript/mirror.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
# adt-reentrance-login
|
||||||
|
|
||||||
|
Capture an SAP **ADT reentrance ticket** through a **localhost redirect**, so
|
||||||
|
`abap-adt-mcp` can log on to the S/4HANA Cloud development tenant from this
|
||||||
|
headless / SSH-only host — without a browser window on the server's desktop.
|
||||||
|
|
||||||
|
Node 24, standard library only, no dependencies, no install step.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why this exists
|
||||||
|
|
||||||
|
`abap-adt-mcp` 2.1.0 can do browser SSO, but only by launching a **server-side**
|
||||||
|
Chrome window (`headless: false`) and harvesting cookies over the DevTools
|
||||||
|
protocol; there is no localhost redirect in that path, so over multi-hop SSH
|
||||||
|
neither the desktop nor a DevTools relay is reachable in practice
|
||||||
|
[KNOWLEDGE.md, 2026-09-16].
|
||||||
|
|
||||||
|
ADT for VS Code does it differently. It opens
|
||||||
|
|
||||||
|
```
|
||||||
|
https://<tenant>/sap/bc/adt/core/http/reentranceticket?redirect-url=http://localhost:<port>/adt/redirect
|
||||||
|
```
|
||||||
|
|
||||||
|
and reads the ticket back out of the redirect
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:<port>/adt/redirect?...&reentrance-ticket=<ticket>
|
||||||
|
```
|
||||||
|
|
||||||
|
(observed 2026-09-16). That half is portable: **your** browser does the SSO, the
|
||||||
|
tenant redirects to `localhost`, and a forwarded TCP port puts "localhost" back
|
||||||
|
on this host. This script is that listener.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Run it
|
||||||
|
|
||||||
|
Three steps, three terminals-worth of context. Values below use the defaults
|
||||||
|
(tenant `my431353`, port `42010`, 300 s timeout).
|
||||||
|
|
||||||
|
### 1. Start the helper on this host
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node /home/alexz/projects/sap-integrations-mcl/arena-plm/tools/adt-reentrance-login/login.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
It prints (to stderr) the port to forward and the URL to open, then waits up to
|
||||||
|
300 seconds for exactly one redirect.
|
||||||
|
|
||||||
|
### 2. Forward the port to the machine running your browser
|
||||||
|
|
||||||
|
**VS Code:** open the **Ports** panel (`Ctrl+Shift+P` → *Ports: Focus on Ports
|
||||||
|
View*) → **Forward a Port** → enter `42010`. VS Code forwards it to your local
|
||||||
|
`localhost:42010`.
|
||||||
|
|
||||||
|
**Plain SSH**, from your laptop:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -L 42010:127.0.0.1:42010 <this-host>
|
||||||
|
```
|
||||||
|
|
||||||
|
The browser must reach **this host's** localhost. Without the forward the tenant
|
||||||
|
redirects your browser to its own machine and nothing is captured.
|
||||||
|
|
||||||
|
### 3. Open the URL in that browser and complete SSO
|
||||||
|
|
||||||
|
```
|
||||||
|
https://my431353.s4hana.cloud.sap/sap/bc/adt/core/http/reentranceticket?redirect-url=http://localhost:42010/adt/redirect
|
||||||
|
```
|
||||||
|
|
||||||
|
(Or capture it programmatically: `node login.mjs --print-url-only`.)
|
||||||
|
|
||||||
|
After SSO the tab lands on "Ticket captured", the helper writes
|
||||||
|
`~/.abap-adt-mcp/ticket.json` (mode 0600) and exits 0.
|
||||||
|
|
||||||
|
### 4. Make the MCP server pick it up
|
||||||
|
|
||||||
|
`~/.abap-adt-mcp/ticket-provider.sh` already `cat`s that file, and
|
||||||
|
`abap-adt-mcp` caches the login per destination — it only re-runs the provider
|
||||||
|
on an explicit `login` call or a `reauthenticate`
|
||||||
|
(`dist/index.js:268-292`, `615-625`, read 2026-09-18). So after capturing a
|
||||||
|
fresh ticket, call the MCP `login` tool for the destination.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Flag | Default | Meaning |
|
||||||
|
|---|---|---|
|
||||||
|
| `--tenant <url>` | `https://my431353.s4hana.cloud.sap` | Tenant base URL. The UI host, not the `-api` host. |
|
||||||
|
| `--port <n>` | `42010` | Loopback port. Bound to `127.0.0.1` **only**; there is no flag to change the bind address. |
|
||||||
|
| `--timeout <seconds>` | `300` | Give up and write nothing. |
|
||||||
|
| `--out <path>` | `~/.abap-adt-mcp/ticket.json` | Output file, mode 0600 (directory created 0700). |
|
||||||
|
| `--client <nnn>` | *not sent* | Adds `sap-client=<nnn>`. See "Which client?" below. |
|
||||||
|
| `--allow-host <name>` | — | Additionally accept this `Host` header (repeatable), for tunnelled forwards that are not `localhost`. |
|
||||||
|
| `--probe` | off | After capture, test cookie vs header transport (below). |
|
||||||
|
| `--print-url-only` | off | Print the logon URL to **stdout** and exit; start no listener. |
|
||||||
|
| `--help` | — | Usage. |
|
||||||
|
|
||||||
|
Exit codes: `0` captured · `2` usage · `3` timeout · `4` cannot bind · `5`
|
||||||
|
redirect arrived without a usable ticket.
|
||||||
|
|
||||||
|
### Which client?
|
||||||
|
|
||||||
|
Nothing is hard-coded. The client the browser session uses is unconfirmed; the
|
||||||
|
published service URL shows `sap-client=080`, but that is the API host's
|
||||||
|
service URL, not proof about the UI logon. Run without `--client` first; only
|
||||||
|
add `--client 080` if the tenant needs it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Probe mode (`--probe`)
|
||||||
|
|
||||||
|
Open question this settles: SAP documents a **logon ticket** in the `MYSAPSSO2`
|
||||||
|
**cookie** and an **assertion ticket** in a `mysapsso2` **header**
|
||||||
|
[source: sap-docs sap-help loio 3354605668 and 3361378221, verified
|
||||||
|
2026-09-18]. Which one a *reentrance* ticket behaves as is **unknown**, and it
|
||||||
|
decides whether `abap-adt-mcp`'s `sso2` mode can work at all — that mode sends
|
||||||
|
the value strictly as a **cookie** (`dist/lib/sso2TicketProvider.js`, read
|
||||||
|
2026-09-18).
|
||||||
|
|
||||||
|
With `--probe`, after capture the helper makes two GETs to
|
||||||
|
`<tenant>/sap/bc/adt/core/discovery` with `Accept: application/atomsvc+xml` and
|
||||||
|
**no other credentials**:
|
||||||
|
|
||||||
|
- **(a)** `Cookie: MYSAPSSO2=<ticket>`
|
||||||
|
- **(b)** `MYSAPSSO2: <ticket>`
|
||||||
|
|
||||||
|
and reports only the HTTP status of each, whether a `set-cookie` with
|
||||||
|
`SAP_SESSIONID` came back, and which succeeded. Bodies are discarded unread. A
|
||||||
|
redirect is reported by **host only** (a SAML `Location` can carry request
|
||||||
|
material).
|
||||||
|
|
||||||
|
Read the verdict as:
|
||||||
|
|
||||||
|
- **cookie worked** → `abap-adt-mcp`'s `sso2` mode works as-is.
|
||||||
|
- **only the header worked** → `sso2` mode **cannot** use this ticket; the MCP
|
||||||
|
server would need a header transport it does not have.
|
||||||
|
- **neither** → expired ticket, wrong client, or wrong mechanism entirely.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ticket lifetime
|
||||||
|
|
||||||
|
- **The lifetime of a reentrance ticket is not verified here.** Treat it as
|
||||||
|
short-lived and re-run the helper whenever logon fails. Do not assume the
|
||||||
|
8-hour default of a classic SAP logon ticket applies.
|
||||||
|
- Whether a reentrance ticket is **single-use** is also unverified. If a
|
||||||
|
captured ticket authenticates once and then stops, assume single-use and
|
||||||
|
re-capture.
|
||||||
|
- Expiry surfaces in the ADT MCP only as **"Your user was logged off."**
|
||||||
|
`abap_list_destinations` and `get_object_type_details` still answer, so
|
||||||
|
neither proves a live session; `abap_creation-run_validation` is the cheapest
|
||||||
|
read-only liveness check [KNOWLEDGE.md, 2026-09-16].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security rules
|
||||||
|
|
||||||
|
These are contracts, not preferences. The ticket is a **live logon credential**
|
||||||
|
for the tenant.
|
||||||
|
|
||||||
|
1. **The ticket is never printed, logged, or put in an error message, argv, or
|
||||||
|
an environment variable.** The only place it is written is `--out`, mode
|
||||||
|
0600. Validation failures report the *reason* and the parameter *names*
|
||||||
|
present — never values, never offending characters or positions.
|
||||||
|
2. **stdout stays empty.** Every human-readable message goes to stderr, so
|
||||||
|
nothing sensitive can be captured by a `$(...)` around the helper. The single
|
||||||
|
exception is `--print-url-only`, which prints the (secret-free) logon URL.
|
||||||
|
3. **The listener binds `127.0.0.1` only** and is one-shot. There is no flag to
|
||||||
|
bind a wider interface. A `Host` header that is not a loopback name is
|
||||||
|
refused (cheap DNS-rebinding guard); allow a specific tunnel name with
|
||||||
|
`--allow-host`.
|
||||||
|
4. **Never paste the redirect URL** into a chat, issue, log, or file — it
|
||||||
|
contains the ticket. Note that the URL also lands in **your browser's
|
||||||
|
history**; clear it on a shared machine.
|
||||||
|
5. The output file is written with permissions set **before** any ticket byte
|
||||||
|
reaches the disk (`open` + `fchmod` + `write`), so a pre-existing
|
||||||
|
world-readable file cannot briefly expose it.
|
||||||
|
6. Ticket values are validated against the RFC 6265 cookie-octet set copied
|
||||||
|
verbatim from `abap-adt-mcp` — a ticket the MCP server would reject is
|
||||||
|
rejected here instead of being written to a file that cannot work.
|
||||||
|
|
||||||
|
### Percent-decoding, and why not `URLSearchParams`
|
||||||
|
|
||||||
|
The ticket is URL-decoded **exactly once** with `decodeURIComponent` over the
|
||||||
|
raw query pair, and never re-encoded.
|
||||||
|
|
||||||
|
`URLSearchParams` is deliberately **not** used: it applies
|
||||||
|
`application/x-www-form-urlencoded` rules and turns a literal `+` into a space.
|
||||||
|
Measured on Node v24.15.0, 2026-09-18:
|
||||||
|
|
||||||
|
```
|
||||||
|
'?reentrance-ticket=AB%2BCD+EF%3d'
|
||||||
|
searchParams.get(...) -> 'AB+CD EF=' <-- space, corrupt
|
||||||
|
decodeURIComponent(raw pair) -> 'AB+CD+EF=' <-- correct
|
||||||
|
```
|
||||||
|
|
||||||
|
A space is not a legal cookie-octet, so a base64 ticket carrying `+` would be
|
||||||
|
silently corrupted and then rejected.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration (handled by the repo owner, documented here for reference)
|
||||||
|
|
||||||
|
This script does **not** touch `~/.abap-adt-mcp/systems.json` or `.mcp.json`.
|
||||||
|
For the record, `abap-adt-mcp` 2.1.0 requires for `authType: "sso2"`
|
||||||
|
(`dist/lib/systems.js:73-149`, read 2026-09-18):
|
||||||
|
|
||||||
|
- `sso2.command` must be an **absolute executable path** (`sso2.args` optional,
|
||||||
|
`sso2.timeoutMs` 1000–300000);
|
||||||
|
- the system `url` must be **HTTPS** ("so the ticket is never sent in
|
||||||
|
plaintext");
|
||||||
|
- `insecureTls` is refused in combination with `sso2`;
|
||||||
|
- `authType: "ticket"` is accepted as an alias for `sso2`;
|
||||||
|
- the server warns at startup that every remote caller of an `sso2` destination
|
||||||
|
shares the SAP identity of the user running it — one server instance per
|
||||||
|
person.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Cause |
|
||||||
|
|---|---|
|
||||||
|
| Browser cannot reach `localhost:42010` | Port not forwarded, or forwarded to the wrong host. Re-check step 2. |
|
||||||
|
| `port ... is already in use` (exit 4) | Another listener. Use `--port <n>` and forward **that** port. |
|
||||||
|
| `Timed out ... Nothing was written.` (exit 3) | SSO never completed, or the redirect went to the wrong machine. |
|
||||||
|
| `the redirect carried no reentrance-ticket parameter` (exit 5) | The tenant redirected without a ticket — usually a failed or cancelled SSO. The parameter names it *did* send are listed. |
|
||||||
|
| `ticket contains characters outside the RFC 6265 cookie-octet set` (exit 5) | The value is not usable as a `MYSAPSSO2` cookie; `abap-adt-mcp` would reject it too. |
|
||||||
|
| `Refused a redirect with Host: ...` | Forward is not a plain localhost forward. Re-run with `--allow-host <that-host>`. |
|
||||||
Executable
+441
@@ -0,0 +1,441 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* adt-reentrance-login — capture an SAP ADT reentrance ticket over a localhost
|
||||||
|
* redirect, so `abap-adt-mcp` can log on to an S/4HANA Cloud tenant from a
|
||||||
|
* headless / SSH-only host.
|
||||||
|
*
|
||||||
|
* ADT for VS Code logs on by opening
|
||||||
|
* https://<tenant>/sap/bc/adt/core/http/reentranceticket?redirect-url=http://localhost:<port>/adt/redirect
|
||||||
|
* and reading the ticket out of the redirect
|
||||||
|
* http://localhost:<port>/adt/redirect?...&reentrance-ticket=<ticket>
|
||||||
|
* (observed 2026-09-16). This script plays the "localhost" half of that dance:
|
||||||
|
* it listens on 127.0.0.1 only, captures exactly one ticket, and writes it as
|
||||||
|
* {"ticket":"..."} to a 0600 file for ~/.abap-adt-mcp/ticket-provider.sh.
|
||||||
|
*
|
||||||
|
* SECURITY CONTRACT (do not relax):
|
||||||
|
* - The ticket is a live logon credential. It is NEVER printed, logged, put
|
||||||
|
* in an error message, passed in argv, or exported to the environment.
|
||||||
|
* The only place it is written is the --out file, mode 0600.
|
||||||
|
* - The listener binds 127.0.0.1 exclusively. There is no flag to change it.
|
||||||
|
* - All human-facing output goes to stderr. stdout stays empty, except in
|
||||||
|
* --print-url-only mode, which prints the (secret-free) logon URL so it can
|
||||||
|
* be captured with $(...).
|
||||||
|
*
|
||||||
|
* Node 24, standard library only, no dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createServer } from 'node:http';
|
||||||
|
import { request as httpsRequest } from 'node:https';
|
||||||
|
import { parseArgs } from 'node:util';
|
||||||
|
import { chmodSync, closeSync, fchmodSync, mkdirSync, openSync, writeSync } from 'node:fs';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
import { homedir } from 'node:os';
|
||||||
|
|
||||||
|
const DEFAULT_TENANT = 'https://my431353.s4hana.cloud.sap';
|
||||||
|
const DEFAULT_PORT = 42010;
|
||||||
|
const DEFAULT_TIMEOUT_SECONDS = 300;
|
||||||
|
const DEFAULT_OUT = join(homedir(), '.abap-adt-mcp', 'ticket.json');
|
||||||
|
|
||||||
|
const BIND_ADDRESS = '127.0.0.1'; // loopback only, by design
|
||||||
|
const REDIRECT_PATH = '/adt/redirect';
|
||||||
|
const TICKET_PARAM = 'reentrance-ticket';
|
||||||
|
const TICKET_ENDPOINT = '/sap/bc/adt/core/http/reentranceticket';
|
||||||
|
const DISCOVERY_PATH = '/sap/bc/adt/core/discovery';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RFC 6265 cookie-octet. Copied verbatim from abap-adt-mcp 2.1.0
|
||||||
|
* dist/lib/sso2TicketProvider.js (read 2026-09-18): a ticket that fails this
|
||||||
|
* test is rejected by the MCP server itself, so fail here instead of writing a
|
||||||
|
* file that cannot work. Note \x3d (=) IS allowed, so base64 padding is fine;
|
||||||
|
* space, comma, semicolon, double quote and backslash are not.
|
||||||
|
*/
|
||||||
|
const COOKIE_OCTET = /^[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]+$/;
|
||||||
|
const MAX_TICKET_CHARS = 64 * 1024; // same ceiling as the MCP server
|
||||||
|
|
||||||
|
/** Hosts a browser may legitimately use to reach this listener. */
|
||||||
|
const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
|
||||||
|
|
||||||
|
const EXIT_OK = 0;
|
||||||
|
const EXIT_USAGE = 2;
|
||||||
|
const EXIT_TIMEOUT = 3;
|
||||||
|
const EXIT_BIND = 4;
|
||||||
|
const EXIT_BAD_TICKET = 5;
|
||||||
|
const EXIT_INTERRUPTED = 130;
|
||||||
|
|
||||||
|
/** Every human-readable message goes to stderr. Never pass ticket material. */
|
||||||
|
const note = (msg = '') => process.stderr.write(`${msg}\n`);
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
note(`adt-reentrance-login — capture an ADT reentrance ticket via a localhost redirect
|
||||||
|
|
||||||
|
node login.mjs [options]
|
||||||
|
|
||||||
|
--tenant <url> S/4HANA tenant base URL (default ${DEFAULT_TENANT})
|
||||||
|
--port <n> loopback port to listen on (default ${DEFAULT_PORT}, bound to ${BIND_ADDRESS} only)
|
||||||
|
--timeout <seconds> give up after this long (default ${DEFAULT_TIMEOUT_SECONDS})
|
||||||
|
--out <path> where to write {"ticket":"..."} (default ${DEFAULT_OUT}, mode 0600)
|
||||||
|
--client <nnn> add sap-client=<nnn> to the logon and probe URLs (default: not sent)
|
||||||
|
--allow-host <name> additionally accept this Host header (for tunnelled forwards)
|
||||||
|
--probe after capture, test the ticket as a cookie and as a header
|
||||||
|
--print-url-only print the logon URL to stdout and exit; start no listener
|
||||||
|
--help this text
|
||||||
|
|
||||||
|
Exit codes: 0 captured, 2 usage, 3 timeout, 4 cannot bind, 5 bad/absent ticket.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fail(code, msg) {
|
||||||
|
note(`ERROR: ${msg}`);
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Arguments
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
let values;
|
||||||
|
try {
|
||||||
|
({ values } = parseArgs({
|
||||||
|
options: {
|
||||||
|
tenant: { type: 'string', default: DEFAULT_TENANT },
|
||||||
|
port: { type: 'string', default: String(DEFAULT_PORT) },
|
||||||
|
timeout: { type: 'string', default: String(DEFAULT_TIMEOUT_SECONDS) },
|
||||||
|
out: { type: 'string', default: DEFAULT_OUT },
|
||||||
|
client: { type: 'string' },
|
||||||
|
'allow-host': { type: 'string', multiple: true, default: [] },
|
||||||
|
probe: { type: 'boolean', default: false },
|
||||||
|
'print-url-only': { type: 'boolean', default: false },
|
||||||
|
help: { type: 'boolean', default: false },
|
||||||
|
},
|
||||||
|
strict: true,
|
||||||
|
allowPositionals: false,
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
usage();
|
||||||
|
fail(EXIT_USAGE, err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.help) {
|
||||||
|
usage();
|
||||||
|
process.exit(EXIT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tenant;
|
||||||
|
try {
|
||||||
|
const parsed = new URL(values.tenant);
|
||||||
|
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
||||||
|
throw new Error('tenant must be an http(s) URL');
|
||||||
|
}
|
||||||
|
tenant = `${parsed.protocol}//${parsed.host}`;
|
||||||
|
} catch (err) {
|
||||||
|
fail(EXIT_USAGE, `--tenant is not a usable URL: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const port = Number(values.port);
|
||||||
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
||||||
|
fail(EXIT_USAGE, `--port must be an integer 1..65535 (got ${JSON.stringify(values.port)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeoutSeconds = Number(values.timeout);
|
||||||
|
if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
|
||||||
|
fail(EXIT_USAGE, `--timeout must be a positive number of seconds (got ${JSON.stringify(values.timeout)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = values.client;
|
||||||
|
if (client !== undefined && !/^[0-9]{3}$/.test(client)) {
|
||||||
|
fail(EXIT_USAGE, '--client must be a three-digit SAP client number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const outPath = values.out;
|
||||||
|
const allowedHosts = new Set([...LOOPBACK_HOSTS, ...values['allow-host']]);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Logon URL
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Built by string concatenation on purpose: ADT for VS Code sends the
|
||||||
|
* redirect-url value literally (observed 2026-09-16), and URLSearchParams would
|
||||||
|
* percent-encode the ':' and '/'. Colons and slashes are legal in a query
|
||||||
|
* string (RFC 3986), so the literal form is both valid and wire-identical to
|
||||||
|
* what the tenant already accepts from ADT.
|
||||||
|
*/
|
||||||
|
function buildLogonUrl() {
|
||||||
|
const redirect = `http://localhost:${port}${REDIRECT_PATH}`;
|
||||||
|
const clientParam = client ? `&sap-client=${encodeURIComponent(client)}` : '';
|
||||||
|
return `${tenant}${TICKET_ENDPOINT}?redirect-url=${redirect}${clientParam}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logonUrl = buildLogonUrl();
|
||||||
|
|
||||||
|
if (values['print-url-only']) {
|
||||||
|
// The only thing this script ever writes to stdout. Contains no secret.
|
||||||
|
process.stdout.write(`${logonUrl}\n`);
|
||||||
|
process.exit(EXIT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Ticket extraction
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull the ticket out of the raw request target.
|
||||||
|
*
|
||||||
|
* Deliberately NOT URLSearchParams: it applies
|
||||||
|
* application/x-www-form-urlencoded rules and turns a literal '+' into a space
|
||||||
|
* (measured on Node v24.15.0, 2026-09-18: '?t=AB%2BCD+EF%3d' -> 'AB+CD EF=').
|
||||||
|
* A space is not a legal cookie-octet, so a base64 ticket carrying '+' would be
|
||||||
|
* silently corrupted and then rejected. Splitting the raw query and decoding
|
||||||
|
* once with decodeURIComponent preserves '+' ('AB+CD+EF=').
|
||||||
|
*
|
||||||
|
* Returns { ok: true, ticket } or { ok: false, reason, names }.
|
||||||
|
* `reason` NEVER contains ticket material.
|
||||||
|
*/
|
||||||
|
function extractTicket(requestTarget) {
|
||||||
|
const q = requestTarget.indexOf('?');
|
||||||
|
if (q < 0) return { ok: false, reason: 'the redirect carried no query string', names: [] };
|
||||||
|
|
||||||
|
const names = [];
|
||||||
|
let raw = null;
|
||||||
|
for (const pair of requestTarget.slice(q + 1).split('&')) {
|
||||||
|
if (!pair) continue;
|
||||||
|
const eq = pair.indexOf('=');
|
||||||
|
const rawName = eq < 0 ? pair : pair.slice(0, eq);
|
||||||
|
let name = rawName;
|
||||||
|
try { name = decodeURIComponent(rawName); } catch { /* keep the raw name */ }
|
||||||
|
names.push(name);
|
||||||
|
if (name === TICKET_PARAM && raw === null) raw = eq < 0 ? '' : pair.slice(eq + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw === null) return { ok: false, reason: `the redirect carried no ${TICKET_PARAM} parameter`, names };
|
||||||
|
if (raw === '') return { ok: false, reason: `${TICKET_PARAM} was empty`, names };
|
||||||
|
|
||||||
|
let ticket;
|
||||||
|
try {
|
||||||
|
ticket = decodeURIComponent(raw); // exactly once; do not re-encode
|
||||||
|
} catch {
|
||||||
|
return { ok: false, reason: `${TICKET_PARAM} is not valid percent-encoding`, names };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticket.length === 0) return { ok: false, reason: `${TICKET_PARAM} decoded to an empty string`, names };
|
||||||
|
if (ticket.length > MAX_TICKET_CHARS) {
|
||||||
|
return { ok: false, reason: `ticket is longer than ${MAX_TICKET_CHARS} characters; abap-adt-mcp would reject it`, names };
|
||||||
|
}
|
||||||
|
if (!COOKIE_OCTET.test(ticket)) {
|
||||||
|
// No positions, no sample characters — that would leak ticket material.
|
||||||
|
return { ok: false, reason: 'ticket contains characters outside the RFC 6265 cookie-octet set', names };
|
||||||
|
}
|
||||||
|
return { ok: true, ticket };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write {"ticket":"..."} with 0600 before any ticket byte reaches the disk. */
|
||||||
|
function writeTicketFile(path, ticket) {
|
||||||
|
const dir = dirname(path);
|
||||||
|
const created = mkdirSync(dir, { recursive: true, mode: 0o700 });
|
||||||
|
if (created) chmodSync(created, 0o700); // only tighten what we created
|
||||||
|
|
||||||
|
// 'w' truncates on open; fchmod before write closes the window in which a
|
||||||
|
// pre-existing world-readable file would briefly hold the ticket.
|
||||||
|
const fd = openSync(path, 'w', 0o600);
|
||||||
|
try {
|
||||||
|
fchmodSync(fd, 0o600);
|
||||||
|
writeSync(fd, `${JSON.stringify({ ticket })}\n`);
|
||||||
|
} finally {
|
||||||
|
closeSync(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Probe (--probe)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One GET to the ADT discovery endpoint carrying the ticket either as a
|
||||||
|
* MYSAPSSO2 cookie or as a MYSAPSSO2 header, and nothing else. Resolves to
|
||||||
|
* status/redirect-host/session-cookie facts only — never headers, never a body.
|
||||||
|
*/
|
||||||
|
function probeOnce(mode, ticket) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const target = new URL(DISCOVERY_PATH, tenant);
|
||||||
|
if (client) target.searchParams.set('sap-client', client);
|
||||||
|
|
||||||
|
const headers = { Accept: 'application/atomsvc+xml' };
|
||||||
|
if (mode === 'cookie') headers.Cookie = `MYSAPSSO2=${ticket}`;
|
||||||
|
else headers.MYSAPSSO2 = ticket;
|
||||||
|
|
||||||
|
const req = httpsRequest(
|
||||||
|
{
|
||||||
|
hostname: target.hostname,
|
||||||
|
port: target.port || 443,
|
||||||
|
path: `${target.pathname}${target.search}`,
|
||||||
|
method: 'GET',
|
||||||
|
headers,
|
||||||
|
timeout: 30_000,
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
const setCookie = res.headers['set-cookie'] ?? [];
|
||||||
|
const sessionCookie = setCookie.some((c) => /^\s*SAP_SESSIONID/i.test(c));
|
||||||
|
let redirectHost = null;
|
||||||
|
if (res.headers.location) {
|
||||||
|
// Host only: a SAML redirect URL can carry request material.
|
||||||
|
try { redirectHost = new URL(res.headers.location, target).host; }
|
||||||
|
catch { redirectHost = '(unparseable)'; }
|
||||||
|
}
|
||||||
|
res.resume(); // discard the body; it is never read or logged
|
||||||
|
res.on('end', () => resolve({ status: res.statusCode, sessionCookie, redirectHost }));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
req.on('timeout', () => req.destroy(new Error('request timed out')));
|
||||||
|
req.on('error', (err) => resolve({ error: err.code ?? err.message }));
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeProbe(label, result) {
|
||||||
|
if (result.error) return ` ${label}: transport error (${result.error})`;
|
||||||
|
const bits = [`HTTP ${result.status}`];
|
||||||
|
bits.push(result.sessionCookie ? 'set-cookie SAP_SESSIONID: yes' : 'set-cookie SAP_SESSIONID: no');
|
||||||
|
if (result.redirectHost) bits.push(`redirect host: ${result.redirectHost}`);
|
||||||
|
return ` ${label}: ${bits.join(' ')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runProbe(ticket) {
|
||||||
|
note('');
|
||||||
|
note(`Probing ${tenant}${DISCOVERY_PATH} (Accept: application/atomsvc+xml, no other credentials)...`);
|
||||||
|
const asCookie = await probeOnce('cookie', ticket);
|
||||||
|
const asHeader = await probeOnce('header', ticket);
|
||||||
|
|
||||||
|
note(describeProbe('(a) MYSAPSSO2 cookie', asCookie));
|
||||||
|
note(describeProbe('(b) MYSAPSSO2 header', asHeader));
|
||||||
|
|
||||||
|
const cookieOk = asCookie.status === 200;
|
||||||
|
const headerOk = asHeader.status === 200;
|
||||||
|
if (cookieOk && headerOk) note("verdict: both worked — abap-adt-mcp's cookie-based sso2 mode can work.");
|
||||||
|
else if (cookieOk) note("verdict: the COOKIE worked — abap-adt-mcp's sso2 mode (MYSAPSSO2 cookie) can work.");
|
||||||
|
else if (headerOk) note('verdict: only the HEADER worked — abap-adt-mcp 2.1.0 sends a cookie, so its sso2 mode cannot use this ticket as-is.');
|
||||||
|
else note('verdict: neither returned HTTP 200 — this ticket is not accepted by either transport (expired, wrong client, or wrong mechanism).');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// One-shot loopback listener
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const PAGE_OK = `<!doctype html>
|
||||||
|
<html lang="en"><head><meta charset="utf-8">
|
||||||
|
<meta name="referrer" content="no-referrer">
|
||||||
|
<title>ADT logon captured</title></head>
|
||||||
|
<body style="font-family:system-ui,sans-serif;max-width:34rem;margin:4rem auto">
|
||||||
|
<h1>Ticket captured</h1>
|
||||||
|
<p>The reentrance ticket was written to the login helper's output file. You can close this tab.</p>
|
||||||
|
<p><small>This URL contains a live credential and stays in your browser history. Clear it if the machine is shared.</small></p>
|
||||||
|
</body></html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PAGE_FAIL = `<!doctype html>
|
||||||
|
<html lang="en"><head><meta charset="utf-8">
|
||||||
|
<meta name="referrer" content="no-referrer">
|
||||||
|
<title>ADT logon failed</title></head>
|
||||||
|
<body style="font-family:system-ui,sans-serif;max-width:34rem;margin:4rem auto">
|
||||||
|
<h1>No usable ticket</h1>
|
||||||
|
<p>The redirect did not carry a usable reentrance ticket. See the helper's terminal output.</p>
|
||||||
|
</body></html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
let finished = false;
|
||||||
|
|
||||||
|
const server = createServer((req, res) => {
|
||||||
|
// Keep the shared secret-free headers on every answer.
|
||||||
|
const send = (status, body, cb) => {
|
||||||
|
res.writeHead(status, {
|
||||||
|
'Content-Type': 'text/html; charset=utf-8',
|
||||||
|
'Cache-Control': 'no-store',
|
||||||
|
'Referrer-Policy': 'no-referrer',
|
||||||
|
Connection: 'close',
|
||||||
|
});
|
||||||
|
if (req.method === 'HEAD') res.end(cb);
|
||||||
|
else res.end(body, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (finished) { send(503, PAGE_FAIL); return; }
|
||||||
|
|
||||||
|
const target = req.url ?? '/';
|
||||||
|
const pathOnly = target.split('?')[0];
|
||||||
|
|
||||||
|
// Dropped rather than fatal: browsers ask for /favicon.ico unprompted.
|
||||||
|
if (pathOnly !== REDIRECT_PATH) { send(404, PAGE_FAIL); return; }
|
||||||
|
|
||||||
|
// Cheap DNS-rebinding guard. The socket is loopback-only anyway.
|
||||||
|
const hostHeader = (req.headers.host ?? '').replace(/:\d+$/, '');
|
||||||
|
if (hostHeader && !allowedHosts.has(hostHeader)) {
|
||||||
|
note(`Refused a redirect with Host: ${hostHeader} (expected localhost). Re-run with --allow-host ${hostHeader} if that forward is yours.`);
|
||||||
|
send(403, PAGE_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = extractTicket(target);
|
||||||
|
if (!result.ok) {
|
||||||
|
finished = true;
|
||||||
|
send(400, PAGE_FAIL, () => {
|
||||||
|
note(`Redirect received on ${REDIRECT_PATH}, but ${result.reason}.`);
|
||||||
|
if (result.names?.length) note(`Parameter names present (values withheld): ${result.names.join(', ')}`);
|
||||||
|
server.close();
|
||||||
|
process.exit(EXIT_BAD_TICKET);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
finished = true;
|
||||||
|
try {
|
||||||
|
writeTicketFile(outPath, result.ticket);
|
||||||
|
} catch (err) {
|
||||||
|
send(500, PAGE_FAIL, () => {
|
||||||
|
note(`ERROR: could not write ${outPath}: ${err.message}`);
|
||||||
|
server.close();
|
||||||
|
process.exit(EXIT_BAD_TICKET);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
send(200, PAGE_OK, async () => {
|
||||||
|
note(`Ticket captured and written to ${outPath} (mode 0600). Its value is not shown anywhere.`);
|
||||||
|
if (values.probe) {
|
||||||
|
try { await runProbe(result.ticket); }
|
||||||
|
catch (err) { note(`Probe failed to run: ${err.message}`); }
|
||||||
|
}
|
||||||
|
server.close();
|
||||||
|
process.exit(EXIT_OK);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', (err) => {
|
||||||
|
if (err.code === 'EADDRINUSE') {
|
||||||
|
fail(EXIT_BIND, `port ${port} is already in use on ${BIND_ADDRESS}. Pick another with --port (and forward that one).`);
|
||||||
|
}
|
||||||
|
fail(EXIT_BIND, `cannot listen on ${BIND_ADDRESS}:${port}: ${err.message}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
note(`Timed out after ${timeoutSeconds}s without a redirect. Nothing was written.`);
|
||||||
|
server.close();
|
||||||
|
process.exit(EXIT_TIMEOUT);
|
||||||
|
}, timeoutSeconds * 1000);
|
||||||
|
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
note('Interrupted. Nothing was written.');
|
||||||
|
server.close();
|
||||||
|
process.exit(EXIT_INTERRUPTED);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen({ host: BIND_ADDRESS, port, exclusive: true }, () => {
|
||||||
|
note(`Listening on http://${BIND_ADDRESS}:${port}${REDIRECT_PATH} (loopback only) for ${timeoutSeconds}s.`);
|
||||||
|
note('');
|
||||||
|
note(`1. Forward TCP port ${port} from this host to your machine.`);
|
||||||
|
note(` VS Code: Ports panel -> Forward a Port -> ${port} (or: ssh -L ${port}:127.0.0.1:${port} <this-host>)`);
|
||||||
|
note(' Your browser must reach THIS host\'s localhost, or the redirect lands nowhere.');
|
||||||
|
note('2. Open this URL in that browser and complete SSO:');
|
||||||
|
note('');
|
||||||
|
note(` ${logonUrl}`);
|
||||||
|
note('');
|
||||||
|
note(`3. The tenant redirects to http://localhost:${port}${REDIRECT_PATH}?... and the ticket is captured here.`);
|
||||||
|
note(` It is written to ${outPath} only. Never paste the redirect URL into a chat, log or file.`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user