Skip to content

Service Line CalcVATAmountLines: count VAT Difference once and clear filters before OnAfterCalcVATAmountLines - #11026

Open
Franco111000 wants to merge 1 commit into
microsoft:mainfrom
Franco111000:fix-9102-service-calcvatamountlines
Open

Service Line CalcVATAmountLines: count VAT Difference once and clear filters before OnAfterCalcVATAmountLines#11026
Franco111000 wants to merge 1 commit into
microsoft:mainfrom
Franco111000:fix-9102-service-calcvatamountlines

Conversation

@Franco111000

@Franco111000 Franco111000 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What & why

CalcVATAmountLines on table 5902 "Service Line" diverges from the Sales Line and Purchase Line implementation in two places, and both show up on Service Statistics as soon as invoice rounding and a manual VAT difference meet:

  1. The rounding residual accumulated "Amount Including VAT" - Amount + "VAT Difference" per line. "Amount Including VAT" - Amount already contains the VAT difference, so it was counted twice and the excess was settled on the invoice rounding VAT line, which then showed a VAT amount equal to the VAT difference. Sales Line and Purchase Line accumulate "Amount Including VAT" - Amount only.
  2. When the rounding block ran, the filters set by FindVATAmountLine stayed on the var VATAmountLine when OnAfterCalcVATAmountLines fired and control returned to the caller. Page 6030 "Service Statistics" then ran TempVATAmountLine.ModifyAll(Modified, false) on one row and handed a filtered set to the VAT Specification subform, so only one VAT line was visible.

The fix drops the extra term, which makes the accumulation identical to Sales Line and Purchase Line, and adds VATAmountLine.Reset() after the rounding block, before the event, so the record is unfiltered on every path. For the record, Sales Line is not fully clean here either: GetVATAmountLineOfMaxAmt resets its filters only on its fall-through branch and leaves them set on the early exit. That narrower gap is out of scope for this change. Both layers that carry the procedure are changed: src/Layers/W1 and the full table copy in src/Layers/NA. The two edited regions are byte-identical between the layers before and after the change.

Linked work

Fixes #9102

Re-submission of microsoft/BusinessCentralApps#1913 (pilot issues #1562 and #1563), which received a code-owner approval and was closed unmerged when that repository was retired, with a note about internal test failures. Two things differ from that version and are the likely cause of those failures:

  • The NA layer copy of table 5902 was not patched there. It is patched here.
  • The pilot test helper validated "VAT Identifier" on a new VAT Posting Setup directly, which fails in the IT localization, where that field has a table relation to the VAT Identifier table. The tests here go through the codeunit's existing CreateVATPostingSetup helper, which the IT copy already overrides for exactly that reason.

How I validated this

  • I read the full diff and it contains only changes I intended.
  • I built the affected app(s) locally with no new analyzer warnings.
  • I ran the change in Business Central and confirmed it behaves as expected.
  • I added or updated tests for the new behavior, or explained below why none are needed.

What I tested and the outcome

Two tests were added to codeunit 134028 "ERM Change VAT On VAT Amt Line", and mirrored into its full-replacement copies in the APAC, ES and IT test layers (those copies differ from W1 only in their pre-existing localized lines, verified by diffing each copy against W1 before and after the change).

  • ServiceCalcVATAmountLinesCountsVATDifferenceOnce: a Service Invoice with an item line (10% VAT) and an invoice rounding G/L line (0% VAT, own VAT Identifier). The VAT amount of the item line is raised by a VAT difference the way the VAT Specification subform and Service Statistics do it (Validate("VAT Amount"), Modified := true, UpdateVATOnLines for General and Invoicing), then CalcVATAmountLines runs again. It asserts that the total VAT on the VAT Amount Lines equals the VAT on the Service Lines, and that the rounding VAT line stays at 0. Against the unpatched table the residual after UpdateLines is exactly the VAT difference, the rounding block adds it to the rounding VAT line, and both assertions fail.
  • ServiceCalcVATAmountLinesReturnsAllLinesAfterRoundingResidual: same document, no VAT difference. A rounding residual is injected through OnCalcVATAmountLinesOnBeforeUpdateVATAmountLine from a manually bound instance of the test codeunit, so the rounding block runs without depending on the rounding arithmetic of a particular localization. It asserts that both VAT Amount Lines are visible on the record handed back to the caller, and that the residual was settled on a VAT Amount Line. With the first fix alone the block still leaves the FindVATAmountLine filters in place and Count() returns 1, so this test isolates the Reset().

Neither test has executed yet: .alpackages is empty in this repository, so there is no local build, and the pull request build runs only after a maintainer approves the workflow. The pass and fail behaviour above comes from tracing the code paths by hand against upstream/main, not from a test run.

Risk & compatibility

  • Behaviour change for subscribers of OnCalcVATAmountLinesOnAfterCalcLineTotals on Service Line: TotalVATAmount no longer carries the duplicated VAT difference. That value was wrong; it now matches what the Sales and Purchase publishers pass.
  • Behaviour change for subscribers of OnAfterCalcVATAmountLines on Service Line: VATAmountLine now arrives unfiltered on every path. Nothing in this repository subscribes to that event.
  • No signature changes, no data or upgrade impact, no permissions, no feature flags.
  • Out of scope, kept as is: Service Line settles the rounding residual on the VAT line of the last document line, where Sales Line uses GetVATAmountLineOfMaxAmt.

…tern

CalcVATAmountLines on table 5902 Service Line added the per-line VAT
Difference to TotalVATAmount even though Amount Including VAT minus
Amount already contains it, so the duplicated amount was settled on the
invoice rounding VAT line. Sales Line and Purchase Line accumulate
Amount Including VAT minus Amount only; Service Line now does the same.

It also left the FindVATAmountLine filters on the var VATAmountLine when
OnAfterCalcVATAmountLines fired, which made the Service Statistics VAT
specification show a single line. The record is now reset after the
rounding block, before the event, on every path.

The NA layer copy of the table carries the same code and receives the
same changes. Two tests cover each defect in codeunit 134028 and are
mirrored into its APAC, ES and IT copies.

Fixes microsoft#9102. Re-submission of microsoft/BusinessCentralApps#1913.
@Franco111000
Franco111000 requested a review from a team September 3, 2026 18:21
@Franco111000
Franco111000 requested a review from a team as a code owner September 3, 2026 18:21
@github-actions github-actions Bot added From Fork Pull request is coming from a fork Team: SCM GitHub request for SCM area needs-approval Workflow runs require maintainer approval to start labels Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

From Fork Pull request is coming from a fork needs-approval Workflow runs require maintainer approval to start Team: SCM GitHub request for SCM area

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Service Line CalcVATAmountLines double-counts VAT Difference and leaks filters onto OnAfterCalcVATAmountLines

1 participant