Skip to content

Add calculated value discrepancy checks - #932

Draft
alvarolivie wants to merge 4 commits into
mainfrom
correction-response
Draft

Add calculated value discrepancy checks#932
alvarolivie wants to merge 4 commits into
mainfrom
correction-response

Conversation

@alvarolivie

Copy link
Copy Markdown
Contributor

Summary

  • add a package-level gobl.Calculate(data, options...) operation
  • add gobl.WithDiscrepancies() to reject supplied calculated values that GOBL changes
  • return structured CalculationDiscrepancies through the error channel, including path, provided value, and calculated value
  • compare typed values semantically so equivalent amount precision is accepted
  • support standalone documents and envelopes

Behaviour

Calculated fields omitted by the caller continue to be populated normally. When discrepancy checking is enabled, only explicitly supplied fields marked with the existing calculated=true schema annotation are compared. Non-calculated normalization changes are ignored.

Callers can inspect discrepancy errors with errors.As.

Testing

  • go test ./...
  • go vet ./...
  • git diff --check

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.91304% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.86%. Comparing base (8652979) to head (a0e5784).

Files with missing lines Patch % Lines
discrepancies.go 73.91% 16 Missing and 14 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #932      +/-   ##
==========================================
- Coverage   94.99%   94.86%   -0.14%     
==========================================
  Files         327      328       +1     
  Lines       17732    17847     +115     
==========================================
+ Hits        16845    16930      +85     
- Misses        547      563      +16     
- Partials      340      354      +14     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

alvarolivie and others added 3 commits August 18, 2026 17:07
Replace the gobl.Calculate/WithDiscrepancies wrapper, which duplicated
the Parse+Calculate orchestration Envelope and Object already provide,
with FindCalculationDiscrepancies(data, calculated). Callers keep their
existing Parse/Calculate flow and diff the original bytes against the
result they already produced, instead of going through a second
calculation entry point.

Discrepancies are now returned as plain data rather than an error type,
avoiding the errname nolint the previous shape needed. The comparison
also walks a single reflect tree (the calculated result) instead of two,
using reflect.VisibleFields for embedded fields instead of hand-rolled
recursion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Parse data a second time (without calculating) to recover the
pre-calculation value, then diff it against the calculated result by
walking both with reflection, rather than hand-rolling a JSON tree
walk (byte-sniffing objects/arrays, re-unmarshalling fragments at
each leaf) alongside a single typed value.

Presence is now read off the parsed value itself (nil pointers, slice
length) instead of a separate raw-JSON presence check. The one gap
this leaves - a handful of non-pointer calculated fields (invoice
type, currency, issue date, line index, payment total) where an
explicit zero value is indistinguishable from an omitted one - is
accepted deliberately: GOBL treats both the same way regardless.

Also fixes a bug the new approach surfaced: num.Amount and similar
types are Go structs but marshal through MarshalText rather than
their (unexported) fields, so they must be compared as opaque leaves
via reflect.Type.Implements, not walked field by field.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous version couldn't tell an omitted non-pointer calculated
field (invoice type, currency, issue date, line index, payment total)
apart from one explicitly supplied as its Go zero value, so it treated
both as omitted. That's wrong whenever the zero value is a real,
deliberate value that happens to differ from what GOBL calculated.

Fix by decoding data a second time into a generic `any` tree solely to
answer "was this key present and non-null" at each path, threaded
alongside the two typed reflect trees. This generic tree is only ever
used for that yes/no check, never as a source of values, so it carries
none of the precision/formatting loss a naive read of it would.
An explicit "" for invoice type is now correctly reported when GOBL
replaces it with "standard", while a true omission still isn't.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant