Add Travel Request API lifecycle to Expense Agent - #11007
Add Travel Request API lifecycle to Expense Agent#11007Claudiu Ciumedean (claudiuciumedean) wants to merge 7 commits into
Conversation
Expose scoped Travel Request navigation and lifecycle actions, enforce ownership and approval rules, and create the linked expense report after approval. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e03e077 to
6f3976f
Compare
Harden automatic approval and report creation, add indirect table permissions, and align the API fields with AL performance and tooltip guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a secondary key for Spend Request No. so idempotent travel request report lookup does not introduce AA0210 warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
The file name Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.36.6 |
Preserve create compatibility, add stable report navigation, optimize approval lookups, and extend lifecycle and approver-filter coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed the permission-set filename casing feedback in 595be6f by renaming it to |
Use the shared Library Expense helper when creating approval setup records while preserving updates to existing setup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Good Sense Reviewer - Round 1Recommendation: Request ChangesWhat this PR doesThe change adds travel request API lifecycle actions, owner and approver scoping, capability discovery, audit fields, and automatic expense report creation after approval. The release event is raised after the request is marked Released, so the automatic approval subscriber runs at a sensible point. However, the owner link and the remaining writable endpoint do not consistently use the same owner key, so the API contract is not complete yet. Problem-solution fitFit: Partial The requested travel request API flow is mostly present, including submit, approve, reject, approver filtering, and linked expense report creation. Two ownership paths are still wrong or incomplete, so owner-scoped navigation and the no-transfer rule are not fully enforced. SuggestionsS1 (🔴 High): Link travel requests by employee number S2 (🔴 High): Close the old requestedBy bypass Risk assessment and necessityRisk: The regression surface is the beta Expense Agent API over travel requests, expense users, approver views, and expense report creation. Wrong owner scoping can hide valid travel requests or bind them to the wrong user, and the old endpoint can still change ownership before submit. Necessity: The feature is needed so clients can manage travel requests and create linked expense reports through the API. The scope is reasonable, but it must use one consistent owner identity across all API paths.
|
Link Expense User navigation through the employee number and prevent the obsolete Spend Requests API from changing Travel Request ownership. Add API coverage for both paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
alexei-dobriansky Addressed both Round 1 findings in 1690791:
|
Good Sense Reviewer - Round 2Recommendation: Request ChangesWhat this PR doesThe changes pushed since round 1 replace the nested travel request link with the employee owner key, add the missing legacy owner-change guard, and add API tests for both paths. That fixes the two ownership gaps: the nested collection now uses the same key that travel requests store, and the legacy endpoint now rejects owner transfer for Travel Request records before the modify is accepted. The new tests cover different Expense User and Employee numbers plus the failed PATCH path, but one new test line does not pass the required analyzer rules yet. Status of previous suggestions
New observations (commits since round 1)S3 (🔴 High): Fix the new analyzer warning Risk assessment and necessityRisk: The round-2 runtime change is narrow and lowers ownership risk by using the stored employee owner key and by blocking owner transfer through the legacy endpoint. The remaining risk is validation risk: the test app currently fails the required build checks because of the new analyzer warning. Necessity: The ownership fixes are necessary for a safe travel request API lifecycle. The remaining change is mechanical, but it must be made so the PR can pass validation cleanly.
|
Use a locked JSON label, keep the approval helper internal, and scope the approved-request validation bypass to a local expense report record instance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
alexei-dobriansky Addressed S3 in 38e1de0 by moving the PATCH JSON template into the locked |
| ExpenseAgentAPIValidation.VerifyAgentAccess(); | ||
| end; | ||
|
|
||
| trigger OnModifyRecord(): Boolean |
There was a problem hiding this comment.
New lifecycle-validation logic (OnModifyRecord blocking a change to "Requested By" for travel requests) was added to the already-obsolete "Spend Requests API" page (ObsoleteState = Pending, ObsoleteReason references the Travel Requests API replacement, ObsoleteTag = '30.0'), inside its #if not CLEAN30 region. Building fresh behavior onto a deprecated surface instead of confining the new contract to the supported "Travel Requests API" replacement risks entrenching the obsolete API and complicates its eventual removal.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.38.6
| exit(true); | ||
| Capability::ApprovalConversation: | ||
| exit(true); | ||
| Capability::TravelRequest: |
There was a problem hiding this comment.
Adding TravelRequest required another case Capability of branch in IsEnabled. That continues the enum-plus-dispatcher pattern, so every new capability still edits the consumer instead of letting the enum select behaviour through an interface-backed implementation. Consider modelling capabilities with an interface and enum-backed implementations so new variants do not keep growing this case block.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.38.6
| exit(true); | ||
| end; | ||
|
|
||
| local procedure ProcessApproverFilter() |
There was a problem hiding this comment.
The Travel Requests API rebuilds the approver-derived Requested For filter inside ProcessApproverFilter, which is called from OnFindRecord. Each record fetch can therefore repeat Expense User and Expense Approval Setup reads and, for the default approver path, a full Expense User FindSet loop. Cache the derived filter once per page or request, or precompute it before iteration, so listing or expanding travel requests does not redo the same database work on every OnFindRecord call.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.38.6
| Status = filter("Pending Approval" | "Interim Approved"); | ||
| } | ||
|
|
||
| part(travelRequestsPendingApproval; "Travel Requests API") |
There was a problem hiding this comment.
The PR adds approver-facing pending-travel-request API wiring (Approver View API -> Travel Requests API via Approver Expense User Filter), but the tests only exercise TravelRequestApproval.ApplyApproverFilter directly. Add an integration test that queries the published API surface (for example the approver view expansion or the travelRequests collection with the approver filter) so broken SubPageLink/OnFindRecord plumbing cannot ship unnoticed.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.38.6
| exit(true); | ||
| end; | ||
|
|
||
| trigger OnModifyRecord(): Boolean |
There was a problem hiding this comment.
Travel Requests API now rejects direct changes to status and requestedBy in OnModifyRecord, but this PR adds no negative test for that page; the only owner-change API test still targets the legacy Spend Requests API. Add a PATCH-style test against Travel Requests API that proves both lifecycle guards fail end to end.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.38.6
|
Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.38.6 |
Good Sense Reviewer - Round 3Recommendation: Request ChangesWhat this PR doesThe changes pushed since round 2 move the PATCH JSON template into a locked label, keep the approval helper internal, and limit the approval-check bypass to a local expense report record. The previous ownership and literal-format issues are fixed in shape. However, the new label is still declared outside the #if not CLEAN30 test block that uses it, so the clean analyzer build still fails on the changed test code. Status of previous suggestions
New observations (commits since round 2)S4 (🔴 High): Guard the legacy-only test labels Risk assessment and necessityRisk: The runtime changes remain narrow and lower ownership risk for the beta travel request API. The remaining risk is validation risk: clean builds fail on legacy-only test variables, so the PR cannot validate cleanly yet. Necessity: The feature is needed so clients can manage trip-scoped travel requests and create linked expense reports through the API. The scope is reasonable, and the remaining fix is mechanical.
|
What & why
Extends the Expense Agent beta API so travel requests can be managed through the same scoped OData model as expenses and expense reports.
travelRequestcapability so clients can fail closed against older Business Central versions.travelRequestsnavigation underexpenseUser, relatingexpenseUser.numbertotravelRequest.requestedBy.approverView.No Travel Request UI approval or rejection actions are added.
Linked work
Fixes AB#626966
How I validated this
What I tested and the outcome
VATSpecificationaction warning..alsource and test files.Risk & compatibility
beta. Approve and reject now require the acting approver expense-user number; submit requires the acting submitter expense-user number.requestedByare intentionally read-only through ordinary API PATCH operations; callers must use the lifecycle actions.