WIP: share removed included taxes out between the rows - #910
Draft
cavalle wants to merge 1 commit into
Draft
Conversation
With the currency rounding rule the tax included in the prices is extracted from the sum of each rate's tax-inclusive line totals and shared back over the lines, so the rate's base is not the sum of the tax removed from every line on its own. RemoveIncludedTaxes did the latter, rounding every line down: a hotel invoice of 12 nights at a tax-inclusive 125.00 with 6% VAT came out with a base of 1415.04 instead of the 1415.09 it was issued with, and a totals.rounding of 0.06 that formats unable to express it silently drop. The net totals every row should end up with are now taken from the same distribution the tax totals apply, and the net line prices nudged until they add up to them, gaining only the accuracy needed for each line's sum to still be recalculable from the price presented. Documents using the precise rounding rule are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #910 +/- ##
==========================================
- Coverage 94.87% 94.69% -0.18%
==========================================
Files 320 321 +1
Lines 17431 17552 +121
==========================================
+ Hits 16537 16621 +84
- Misses 553 573 +20
- Partials 341 358 +17 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #895, which fixed the tax-inclusive totals under
rounding: currencybut not what happens when those prices are subsequently removed — which is mandatory for SAF-T/AT, as the format cannot express VAT-inclusive prices. Portuguese payment receipts were still being rejected as a result (Pylon #5267, customer Cloudbeds).The currency calculator extracts the included tax from the sum of each rate's tax-inclusive line totals and shares it back over the lines, so a rate's base is not the sum of the tax removed from each line on its own.
RemoveIncludedTaxesdid the latter, rounding every line down: 12 nights at a tax-inclusive 125.00 with 6% VAT came out with a base of 1415.04 instead of the 1415.09 the invoice was issued with, plus atotals.roundingof 0.06 that formats unable to express it silently drop.tax: new exportedTotalCalculator.ExtractIncludedTaxes, returning the total of each taxable line with the included tax taken out. This is the same per-line distributioncalculateCurrencyTotalsalready performed internally and threw away, so there is no second implementation to keep in step.bill:RemoveIncludedTaxesunderrounding: currencynow snapshots the net total every row should end up with before touching the prices, then nudges the net line prices (and fixed document discount/charge amounts) until they add up to it, recalculating in between so line discounts and charges settle. Each nudged price takes the least accuracy that still reproduces its line's sum, so the sums stay recalculable from the prices presented; large quantities pick up extra decimals as needed (117.92453for a quantity of 1000).rounding: preciseare untouched — every pre-existing removal test passes unchanged.Result for the invoice above: base 1415.09, tax 84.91,
total_with_tax1500.00, nototals.rounding, with the lines carrying the remainder as 7 × 117.9245 + 5 × 117.93. Checked end-to-end againstat-pt: SAF-T now rendersNetTotal1415.09 /TaxPayable84.91 /GrossTotal1500.00, and a receipt with base 1415.09 passes bothvalidatePaymentLineandAvailToPay.ApplyPayment— the two checks that previously demanded different roundings of the same economics and so could not both be satisfied.Multi-rate documents now get the correct base for every rate. A ±0.01 residual can still remain where a rate's tax cannot be recalculated from its own 2-decimal base at all (13% of 53.81 gives 7.00, but the tax-inclusive 60.80 implies 6.99); that is irreducible and still lands in
totals.rounding, which is exactly what the field is for.Pre-Review Checklist
RemoveIncludedTaxes, so no example can exercise this path. Theptandesprices-includeexamples added in Extract included taxes from rate group sums with currency rounding #895 already cover the calculation side.go generate .to ensure that the Schemas and Regime data are up to date.And if you are part of the org:
🤖 Generated with Claude Code