Document VAT-ID-validity-vs-exemption decoupling and maxLength fixes

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-23 19:41:46 +00:00
parent 4d2e78dd2a
commit 80b82e0117
+34 -13
View File
@@ -744,18 +744,30 @@ retroactively owes the VAT itself).
cost from their normal VAT-inclusive catalog figures to net, since the
whole point of the exemption is that the buyer pays less, not that this
shop quietly keeps the VAT portion as extra margin.
- **Checkout UX**: `app/api/checkout/validate-vat/route.ts` gives instant
feedback on the USt-IdNr. field's blur (only once the destination
actually qualifies — no point calling VIES for a Deutschland/Schweiz
order) — "✓ Bestätigt" flips the sidebar total to the exempt (de-grossed)
figures live, as a preview. `app/api/checkout/route.ts` re-runs the exact
same VIES check server-side at submit time regardless, as the actual
source of truth — the client-side result is never trusted. **VIES being
unreachable fails closed**: normal VAT applies, never a guessed
exemption — the checkout-time check is the one place in this feature
that fails closed (contrast the Payload backend's own
`company-settings.vatId` VIES check, which fails open, since that one
only needs to catch an admin's data-entry typo, not decide a tax rate).
- **VAT-ID *validity* and the exemption *decision* are two separate
questions (fixed 2026-07-23)** — a first version only ever called VIES
when the destination already qualified for the exemption (Österreich),
so a garbage VAT ID on a domestic order (e.g. `"ED123456789"` — not even
a real country code) sailed through with just a format check, and a
genuinely valid German/Swiss VAT ID got no confirmation either. Now
`app/api/checkout/validate-vat/route.ts`/`app/api/checkout/route.ts`
check any format-valid VAT ID against VIES regardless of destination
(data quality — worth knowing whether it's real at all, same reasoning
as `company-settings.vatId`'s own check below) — the exemption itself
still only applies when the destination is *also* Österreich. A
validated German VAT ID never zero-rates a domestic sale, no matter how
real it is.
- **Checkout UX**: the USt-IdNr. field's blur gives instant feedback for
any country — "✓ USt-IdNr. bestätigt" (plus "— Lieferung wird steuerfrei
berechnet." only when the destination actually qualifies) flips the
sidebar total to the exempt (de-grossed) figures live, as a preview.
`app/api/checkout/route.ts` re-runs the exact same VIES check server-side
at submit time regardless, as the actual source of truth — the
client-side result is never trusted. **VIES being unreachable fails
closed on the exemption**: normal VAT applies, never a guessed exemption
(contrast the Payload backend's own `company-settings.vatId` VIES check,
which fails open, since that one only needs to catch an admin's
data-entry typo, not decide a tax rate).
- **Every other checkout field is now blur-validated too** (fixed
2026-07-23, alongside this feature) — inline red error text appears the
moment a field loses focus (required fields, email format, PLZ digit
@@ -765,9 +777,18 @@ retroactively owes the VAT itself).
never blurred (e.g. autofill).
- **Persistence**: `Orders.vatExempt`/`vatIdValidatedAt` (Payload backend)
record the outcome, decided once server-side, never editable in the
admin — see the Payload README's "B2B checkout & VAT exemption" section
admin. `vatIdValidatedAt` is set for *any* VIES-confirmed VAT ID
(data-quality audit trail), independently of whether `vatExempt` is also
true — see the Payload README's "B2B checkout & VAT exemption" section
for the full field/audit-trail reasoning and the invoice PDF/EN16931 XML
side of this feature.
- **Every fixed-length numeric field now hard-caps input length too**
(fixed 2026-07-23) — PLZ (`maxLength` = the selected country's own digit
count), USt-IdNr. (`14`), Packstationnummer (`3`)/Postnummer (`10`, on
the backend) all already had `pattern`/`validate` format checks, but
nothing stopped the browser from accepting more characters than could
ever pass. Same "as simple/state-of-the-art as possible" input-quality
pass as the blur-validation fix above.
- **`/bestellbestaetigung`** mirrors the same exempt-totals branch from the
persisted `OrderSnapshot.vatExempt` flag (it otherwise re-derives totals
live from the current catalog, which would show the wrong, VAT-inclusive