Add expense activity history foundation - #10237
Add expense activity history foundation#10237Prangshuman Das (t-prda) wants to merge 13 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
|
The changed permission-set source file is named Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
Apply partial-record reads and mediate activity table access through the API and factbox pages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
|
The filename comment refers to a pre-existing repository file, |
|
Capability-specific behaviour is still selected with a Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
The file is named Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
The new Expense Report Header table relies on table-level DataClassification while its 25 Normal fields (including "Expense User No.", "Expense User Name", and "Description") omit field-level DataClassification. AS0016 requires classification on every Normal field individually; the table-level property does not cascade, so these fields remain implicitly ToBeClassified and the sensitive user-identifying fields are under-classified for GDPR/telemetry purposes. Knowledge:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
The new Posted Expense Report Header table also relies on table-level DataClassification while its 25 Normal fields omit field-level DataClassification. Sensitive fields such as "Expense User No." and "Expense User Name" remain implicitly ToBeClassified instead of being explicitly classified at field scope, as AS0016 and PII-classification guidance require. Knowledge:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
Clean prior marked fixtures and use a cleanup-safe posted source while retaining real API lifecycle actions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
Agentic PR Review - Round 1Recommendation: Request ChangesWhat this PR doesThis PR adds expense activity history with lifecycle writes, read-only APIs, factboxes, retention support, and unit/API/posting scenarios. The lifecycle calls are placed in the same transactions as the state changes, and the active/posted navigation is tested. The current implementation does not match the linked Deliverable 644172 contract in several merge-blocking areas. It rewrites one log table during posting instead of using separate active and posted history, exposes different API field names and navigation paths than the service consumes, and always attributes posting to a BC user. The analogous BaseApp approval flow copies active SuggestionsS1 - Keep posted history append-only S2 - Match the published activity API contract S3 - Expose approver history at the required path S4 - Record automatic posting as System Risk assessment and necessityRisk: This is compliance-facing audit data and a service integration contract. The schema and path mismatches can make the connector silently lose fields or fail to reach approver history, while rewriting and misattributing rows weakens the audit trail. The core writer and posting tests are useful, but the API tests that would catch contract drift are disabled in the current CI runner. Necessity: The Slice 634361 capability is important and the activity-history foundation is justified. The scope is appropriate for a feature increment, but the AL storage and API surfaces must match the already implemented service contract before merge.
|
Response to Agentic PR Review - Round 1The review used the previous Deliverable 644172 description, which still documented the rejected two-table prototype and stale connector commit. The deliverable is now corrected to the final reviewed design: https://dynamicssmb2.visualstudio.com/Dynamics%20SMB/_workitems/edit/644172/
No code change is required for these four suggestions. The stale source-of-truth text was the defect and has been replaced with the implemented contract and acceptance criteria. |
|
The agent-facing Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
Adding the new ActivityLog capability requires another branch in "Expense Capabilities Provider".IsEnabled's case over the "Expense Capability" enum, so capability-specific behavior still lives in manual dispatch code instead of enum-backed interface dispatch. That contradicts the article's best practice and means every future capability addition still has to edit this provider rather than plugging in a new implementation. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
Suggested fix (apply manually — could not be anchored as a one-click suggestion): if PostedExpenseReportHeader.Get(Rec."Last Posting No.") then
if InstructionMgt.ShowConfirm(StrSubstNo(OpenPostedExpenseReportQst, PostedExpenseReportHeader."No."),
InstructionMgt.ShowPostedConfirmationMessageCode())
then
InstructionMgt.ShowPostedDocument(PostedExpenseReportHeader, Page::"Posted Expense Report");Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
1 similar comment
|
Suggested fix (apply manually — could not be anchored as a one-click suggestion): if PostedExpenseReportHeader.Get(Rec."Last Posting No.") then
if InstructionMgt.ShowConfirm(StrSubstNo(OpenPostedExpenseReportQst, PostedExpenseReportHeader."No."),
InstructionMgt.ShowPostedConfirmationMessageCode())
then
InstructionMgt.ShowPostedDocument(PostedExpenseReportHeader, Page::"Posted Expense Report");Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
Knowledge:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
Knowledge:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
The new activity log is erased from the source report's OnDelete trigger, and the agent-facing Expense Reports API still does not disable DELETE. That lets the agent remove a report and wipe its submission/rejection/approval history in the same operation, which weakens the audit trail this PR introduces. Preserve activity entries independently of document deletion, or explicitly block deletes once history exists. Agent judgement — not directly backed by a BCQuality knowledge article. Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
|
The new integration event is named OnCheckExpenseReportPostRestrictions, which does not encode whether the hook fires before or after the restriction check. Rename it to a position-encoding publisher name such as OnBeforeCheckExpenseReportPostRestrictions and update the call site so subscribers can infer firing order from the event name alone. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
Called from the per-line posting loop in ProcessExpenseReportLines, CreateSalesDocument re-queries Sales Header and then Sales Line for every billable expense line via FindSalesDocumentToAppend() and GetNextSalesLineNo(). That creates an N+1 access pattern as report size grows; cache the target invoice/next line number per distinct customer-currency-posting-date key, or reshape the logic so repeated lines do not perform fresh inner-table lookups. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
The service-enabled approval actions accept caller-supplied submitter/approver numbers and forward them into privileged internal routines that trust those values to set status, approver identity, and audit data. Because Access = Internal / internalsVisibleTo is not an authorization boundary, any companion caller that can reach this path can impersonate arbitrary submitters or approvers. Bind the business actor inside the privileged path (for example from the authenticated principal or explicit access-control rules) instead of trusting request parameters. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
The agent permission set grants direct modify rights on system-of-record tables such as Expense Ledger Entry and the posted expense report tables even though the changed surfaces consume them through posting code and read-only APIs. Per the guidance, those writes should be indirect so they stay code-mediated; as shipped, the agent principal can mutate posted or ledger data through any executable object it can reach. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
AddExpenseToReport declares ExpenseNotFoundErr inside the procedure-local var block. That label will compile, but procedure-scoped labels are fragile in XLIFF extraction and should be moved to the page's top-level var block so translation keys stay stable and reviewable. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
Populate takes ExpenseCapabilitiesBuffer as a temporary record parameter, but the name does not start with Temp. That makes Insert()/DeleteAll() calls read like database operations even though this buffer is in-memory only; rename it to TempExpenseCapabilitiesBuffer across the signature and its uses. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
EntryRecIDLbl contains two placeholders (%1 %2) but no Comment. From the label text alone, translators cannot infer that %1 is the table caption and %2 is the document number, so the placeholder meanings need to be documented explicitly. Suggested fix (apply manually — could not be anchored as a one-click suggestion): EntryRecIDLbl: Label '%1 %2', Locked = true, Comment = '%1 = Table Caption, %2 = Document No.';Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
|
This file is named ExpenseManagementObjects.permissionset.al, but the AL file naming convention is ..al with the type segment spelled PermissionSet. Rename it to ExpenseManagementObjects.PermissionSet.al so file listings and tooling can map the file to its object type without parsing the source. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4 |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
Response to latest automated review commentsI checked the latest top-level findings against
The activity-log-specific review findings (object IDs, partial records, committed API reads, mediated permissions, query keys, scenario cleanup, API mapping, and test assertions) have been addressed in the branch. No unrelated refactor is being added for the comments above. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d9178a5c-c5ff-43a4-b9cb-877ec18618fa
Summary
Validation
historyActorRolewere verified to fail closedFixes AB#644172
Related: AB#639704