Update expense VAT specification source - #10312
Update expense VAT specification source#10312Alexander Yakunin (Alexander-Ya) wants to merge 11 commits into
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as outdated.
This comment was marked as outdated.
|
Suggested fix (apply manually — could not be anchored as a one-click suggestion): field(2; "Document Line No."; Integer)
{
Caption = 'Document Line No.';
TableRelation = if ("Document Line No." = filter(<> 0)) "Expense Report Line"."Line No." where("Document No." = field("Document No."));
ToolTip = 'Specifies the line number of the associated expense report line.';
}Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4 |
|
Suggested fix (apply manually — could not be anchored as a one-click suggestion): field(2; "Expense Report Line No."; Integer)
{
Caption = 'Expense Report Line No.';
TableRelation = if ("Expense Report Line No." = filter(<> 0)) "Posted Expense Report Line"."Line No." where("Document No." = field("Expense Report No."));
ToolTip = 'Specifies the line number of the posted expense report line this VAT specification line belongs to. A value of 0 indicates that this is a report-level aggregate line representing totals for the entire report.';
}Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4 |
| var | ||
| PendingVATSpecErrorInfo: ErrorInfo; | ||
| begin | ||
| if ExpenseReportLineVATSpec."Expense Subcategory" = '' then |
There was a problem hiding this comment.
This actionable Show-it error uses ErrorInfo and navigation, but it only sets Message. Add a Title and DetailedMessage so the user gets clear context and guidance before following the navigation action.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| ExpenseReportHeader: Record "Expense Report Header"; | ||
| ExpenseReportLineVATSpec: Record "Expense Report Line VAT Spec."; | ||
| begin | ||
| if not ExpenseReportLineVATSpec.FindSet(true) then |
There was a problem hiding this comment.
Both backfill loops call header Get() inside FindSet iteration, so the upgrade performs an inner lookup for every VAT-spec row even though many rows share the same document number. Cache the current header per document number, or process the rows grouped by document, to avoid this N+1 access pattern during upgrade.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| Rec."Reclaim Approved At" := CurrentDateTime(); | ||
| end; | ||
| } | ||
| /// <summary> |
There was a problem hiding this comment.
The XML summary above field "VAT Base Amount (RCY)" is a copy/paste mismatch: it says the value is a VAT amount converted to local currency, but the field stores the VAT base amount in reimbursement currency. Update the summary so generated documentation and reviewer context match the field's actual meaning.
Suggested fix (apply manually — could not be anchored as a one-click suggestion):
/// <summary>
/// VAT base amount converted to reimbursement currency for accounting and reporting purposes.
/// </summary>Agent judgement — not directly backed by a BCQuality knowledge article.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| tabledata "Posted Expense Report Header" = r, | ||
| tabledata "Posted Exp. Rep. Line VAT Spec" = rm; | ||
|
|
||
| trigger OnUpgradePerCompany() |
There was a problem hiding this comment.
The new upgrade codeunit backfills reimbursement amounts for both open and posted VAT-spec rows, but this PR adds no test that seeds pre-upgrade data, runs the upgrade path, and verifies the recalculated RCY fields and upgrade-tag idempotency. Current tests only cover newly created reports, so a regression in the migration logic could ship unnoticed.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| LCYAmountRoundingPrecision: Decimal; | ||
| ReimbursementAmountRoundingPrecision: Decimal; | ||
| begin | ||
| GLSetup.Get(); |
There was a problem hiding this comment.
Expense Report Line VAT Spec. is now part of the upgrade call path: OnUpgradePerCompany -> BackfillExpenseReportLineVATSpecs() -> UpdateReimbursementAmounts(...) -> UpdateReclaimAmounts(...). Inside that helper, GLSetup.Get() is unguarded. If a company is missing General Ledger Setup, this throws during upgrade and aborts the whole company upgrade instead of skipping the backfill safely.
Knowledge:
- microsoft/knowledge/upgrade/guard-database-reads.md
- microsoft/knowledge/upgrade/do-not-block-upgrade-on-data-errors.md
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| ExpenseReportHeader: Record "Expense Report Header"; | ||
| ExpenseReportLineVATSpec: Record "Expense Report Line VAT Spec."; | ||
| begin | ||
| if not ExpenseReportLineVATSpec.FindSet(true) then |
There was a problem hiding this comment.
Both backfill loops skip rows whose parent header cannot be read, but they do so silently and still let the upgrade tag be set afterward. That means orphaned VAT-spec rows stay unbackfilled with no telemetry telling support which data was skipped. The upgrade guidance is to log unexpected missing data with Session.LogMessage and continue.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
…//github.com/microsoft/BCApps into bugs/Update-Expense-VAT-Specification-Source
| // Amounts come directly from the spec row in reimbursement currency and LCY. | ||
| GenJournalLine."Gen. Posting Type" := GenJournalLine."Gen. Posting Type"::Purchase; | ||
| GenJournalLine."Currency Code" := ExpenseReportLine."Expense Currency Code"; | ||
| GenJournalLine."Currency Factor" := ExpenseReportLine."Expense Currency Factor"; |
There was a problem hiding this comment.
The new per-VAT-row "Expense Category" and "Expense Subcategory" references are now used when building posting descriptions and selecting posting accounts, but posting still only checks the parent Expense Report Line for inactive masters. A VAT specification row can therefore still point to an inactive category or subcategory and be posted. Validate each referenced VAT-spec master record at posting time before using it for description or account selection.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| ExpenseReportLineVATSpec.SetRange("Document No.", Rec."Document No."); | ||
| ExpenseReportLineVATSpec.SetRange("Document Line No.", Rec."Document Line No."); | ||
| ExpenseReportLineVATSpec.SetRange("Reclaim Status", ExpenseReportLineVATSpec."Reclaim Status"::Pending); | ||
| if ExpenseReportLineVATSpec.FindSet() then |
There was a problem hiding this comment.
Approve All Reclaims updates each iterated VAT-spec row, but it opens the set with read-only FindSet(). Use FindSet(true) so the read takes the UpdLock the subsequent Modify(true) calls need instead of upgrading locks row-by-row inside the loop.
Suggested fix (apply manually — could not be anchored as a one-click suggestion):
if ExpenseReportLineVATSpec.FindSet(true) thenKnowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| if Rec.GetFilter("Document No.") = '' then | ||
| exit; | ||
|
|
||
| if ExpenseReportHeader.Get(Rec.GetRangeMin("Document No.")) then |
There was a problem hiding this comment.
OnOpenPage fetches an Expense Report Header row only to read "Reimbursement Currency Code". Add SetLoadFields("Reimbursement Currency Code") before Get(...) so the factbox does not materialize the rest of the header row just to decide whether RCY fields should be shown.
Suggested fix (apply manually — could not be anchored as a one-click suggestion):
ExpenseReportHeader.SetLoadFields("Reimbursement Currency Code");
if ExpenseReportHeader.Get(Rec.GetRangeMin("Document No.")) then
ShowRCYFields := ExpenseReportHeader."Reimbursement Currency Code" <> '';Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| if Rec.GetFilter("Expense Report No.") = '' then | ||
| exit; | ||
|
|
||
| if PostedExpenseReportHeader.Get(Rec.GetRangeMin("Expense Report No.")) then |
There was a problem hiding this comment.
OnOpenPage fetches a Posted Expense Report Header row only to read "Reimbursement Currency Code". Add SetLoadFields("Reimbursement Currency Code") before Get(...) so the page does not load the rest of the posted-header row unnecessarily.
Suggested fix (apply manually — could not be anchored as a one-click suggestion):
PostedExpenseReportHeader.SetLoadFields("Reimbursement Currency Code");
if PostedExpenseReportHeader.Get(Rec.GetRangeMin("Expense Report No.")) then
ShowRCYFields := PostedExpenseReportHeader."Reimbursement Currency Code" <> '';Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| if ExpenseReportLineVATSpec."Expense Subcategory" = '' then | ||
| PendingVATSpecErrorInfo.Message := StrSubstNo(NotApprovedForVATReclaimCategoryErr, ExpenseReportLineVATSpec."Expense Category") | ||
| else | ||
| PendingVATSpecErrorInfo.Message := StrSubstNo(NotApprovedForVATReclaimErr, ExpenseReportLineVATSpec."Expense Category", ExpenseReportLineVATSpec."Expense Subcategory"); | ||
| PendingVATSpecErrorInfo.RecordId := ExpenseReportLineVATSpec.RecordId; | ||
| PendingVATSpecErrorInfo.FieldNo := ExpenseReportLineVATSpec.FieldNo("Reclaim Status"); | ||
| PendingVATSpecErrorInfo.PageNo := Page::"Expense Report Line VAT Spec."; | ||
| PendingVATSpecErrorInfo.AddNavigationAction(ShowItLbl); |
There was a problem hiding this comment.
The new ErrorInfo for pending VAT reclaim rows populates ErrorInfo.Message with dynamic category/subcategory values but does not set ErrorInfo.DataClassification or ErrorType. Because ErrorInfo.Message is emitted to telemetry, classify the message explicitly and choose the intended client-facing error type instead of relying on defaults.
| if ExpenseReportLineVATSpec."Expense Subcategory" = '' then | |
| PendingVATSpecErrorInfo.Message := StrSubstNo(NotApprovedForVATReclaimCategoryErr, ExpenseReportLineVATSpec."Expense Category") | |
| else | |
| PendingVATSpecErrorInfo.Message := StrSubstNo(NotApprovedForVATReclaimErr, ExpenseReportLineVATSpec."Expense Category", ExpenseReportLineVATSpec."Expense Subcategory"); | |
| PendingVATSpecErrorInfo.RecordId := ExpenseReportLineVATSpec.RecordId; | |
| PendingVATSpecErrorInfo.FieldNo := ExpenseReportLineVATSpec.FieldNo("Reclaim Status"); | |
| PendingVATSpecErrorInfo.PageNo := Page::"Expense Report Line VAT Spec."; | |
| PendingVATSpecErrorInfo.AddNavigationAction(ShowItLbl); | |
| if ExpenseReportLineVATSpec."Expense Subcategory" = '' then | |
| PendingVATSpecErrorInfo.Message := StrSubstNo(NotApprovedForVATReclaimCategoryErr, ExpenseReportLineVATSpec."Expense Category") | |
| else | |
| PendingVATSpecErrorInfo.Message := StrSubstNo(NotApprovedForVATReclaimErr, ExpenseReportLineVATSpec."Expense Category", ExpenseReportLineVATSpec."Expense Subcategory"); | |
| PendingVATSpecErrorInfo.DataClassification := DataClassification::SystemMetadata; | |
| PendingVATSpecErrorInfo.ErrorType := ErrorType::Client; | |
| PendingVATSpecErrorInfo.RecordId := ExpenseReportLineVATSpec.RecordId; | |
| PendingVATSpecErrorInfo.FieldNo := ExpenseReportLineVATSpec.FieldNo("Reclaim Status"); | |
| PendingVATSpecErrorInfo.PageNo := Page::"Expense Report Line VAT Spec."; | |
| PendingVATSpecErrorInfo.AddNavigationAction(ShowItLbl); |
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| ExpenseAgentAPIValidation.VerifyAgentAccess(); | ||
| end; | ||
|
|
||
| trigger OnNewRecord(BelowxRec: Boolean) |
There was a problem hiding this comment.
The PR adds a new agent-only insert path on Expense VAT Spec. API and stamps new rows as Source::Agent, but no test covers either the reject path for non-agent callers or the success path that verifies the Source value is set correctly. Add an API/page test for both cases so regressions in caller validation or source initialization are caught.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| [ModalPageHandler] | ||
| procedure ExpensesModalPageHandler(var Expenses: TestPage Expenses) | ||
| begin | ||
| Assert.AreNotEqual('', ExpectedExpenseNo, 'An unexpected Expenses modal page was shown.'); |
There was a problem hiding this comment.
The new Expenses/Confirm handlers hardcode expectations in globals and assert inline inside the handler bodies instead of driving the interaction through enqueue/dequeue expectations with an end-of-test emptiness check. That contradicts the UI-handler guidance and makes the dialog sequence less robust to diagnose if an extra or misordered interaction is introduced later.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| UpgradeTag.SetUpgradeTag(GetBackfillReimbursementAmountsUpgradeTag()); | ||
| end; | ||
|
|
||
| local procedure BackfillExpenseReportLineVATSpecs() |
There was a problem hiding this comment.
The new upgrade code backfills the four newly added RCY fields by iterating every existing VAT-spec row with FindSet(true) ... Modify(false) in both tables. That is the exact full-table loop/modify pattern the upgrade guidance warns against for new fields on existing tables. Use DataTransfer for the set-based portions of this migration and keep row-by-row logic only for the cases that genuinely require per-row calculation.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
| exit; | ||
|
|
||
| repeat | ||
| if ExpenseReportHeader.Get(ExpenseReportLineVATSpec."Document No.") then begin |
There was a problem hiding this comment.
Both backfill loops skip rows whose header record is missing and then still allow the upgrade tag to be set for the company. That leaves the new RCY fields at their datatype defaults for those rows with no telemetry telling the partner what was skipped and no later retry once the data is repaired. Per the upgrade guidance, missing related data should be logged before continuing so the tenant upgrades without silently stranding migrated rows.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
|
This API page now adds explicit POST logic for agent-authored VAT specification rows, but it still leaves PATCH and DELETE enabled by default. Because the table only blocks modify/delete when Source = Agent, an authenticated caller can still update or delete existing non-agent VAT specification rows through this endpoint. Make the surface explicitly create-only by setting ModifyAllowed = false and DeleteAllowed = false on the page. Suggested fix (apply manually — could not be anchored as a one-click suggestion): DelayedInsert = true;
EntityName = 'expenseVATSpecification';
EntitySetName = 'expenseVATSpecifications';
PageType = API;
ODataKeyFields = SystemId;
SourceTable = "Expense VAT Specification";
AboutText = 'Provides access to data from the Expense VAT Specification table';
ModifyAllowed = false;
DeleteAllowed = false;
AutoSplitKey = true;Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4 |
What & why
Linked work
Fixes AB#626087
How I validated this
What I tested and the outcome (required — be specific: scenarios, commands, screenshots for UI changes)
Risk & compatibility