fix(webshop-orders): shared effective-rate helper and order-context refusal for the rate-0 slot (#1912) (#2008)

* fix(webshop): share rate classification and check rate-0 order context in bulk book (#1912)

The bulk revenue template's guard copied fetchDynamicVatAccounts'
effective-rate precedence (explicit momssats > treatment > class-3
number+name inference), so the two could drift. Both now call one
exported helper, resolveEffectiveVatRate, and a sibling
resolveRevenueVatBox resolves the momsdeklaration box for a revenue
account (treatment ruta first, then the static BAS map).

The rate-0 slot also ignored order context: a domestic 0% order could be
routed to an export account (ruta 36) and vice versa, misstating rutor
35-42 with no VAT amount to catch it. The sweep now refuses, per order,
a 0% bucket whose billing country contradicts the chosen account's box:
ruta 36 vs SE or an EU country, ruta 40 vs SE, ruta 35/38/39 vs SE or a
non-EU country. Unknown country (Shopify), domestic boxes (42/41/07) and
unclassified accounts are unchanged; the domestic-account + foreign-
country direction stays advisory in the dialog.

Item 1 of the issue (require a positive momsfri/export/EU classification
for the slot) is deferred: most such accounts are unconfigured today and
the strict rule needs a configure path first (DECISIONS.md).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nAd8XJ2RPCmG2eKoLBdna

* fix(webshop): address review findings (#1912)

- Finding 1: the rate-0 context guard keys on customer_country, which the
  WooCommerce sync stores from the billing address; the goods boxes 35/36/38
  follow the delivery destination, so a Swedish-billed order shipped outside
  the EU is a legitimate ruta 36 export the sweep refuses. Soften the
  WEBSHOP_ORDER_ZERO_RATE_CONTEXT_MISMATCH copy (sv/en) to say the check is
  based on the billing country and the account may still be right for the
  delivery address, and ask the user to confirm rather than change the
  account. Document the limitation in the route comment; storing shipping
  country in the sync is a follow-up. Test pins the new wording.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nAd8XJ2RPCmG2eKoLBdna

---------

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-28 17:22:38 +02:00
committed by GitHub
co-authored by Claude Fable 5 Jakob Wennberg
parent 22f0647d6c
commit 33a58bec51
8 changed files with 691 additions and 24 deletions
+7
View File
@@ -3967,6 +3967,13 @@ const WEBSHOP_ORDERS: Record<string, StructuredErrorEntry> = {
message_en:
'A chosen revenue account is not configured for the VAT rate it would receive, so the sale would drop out of ruta 05 in the VAT declaration. Set the account VAT rate in the chart of accounts (or pick an account for the right rate) and try again.',
},
WEBSHOP_ORDER_ZERO_RATE_CONTEXT_MISMATCH: {
httpStatus: 422,
message_sv:
'Ordern har en momsfri del men faktureringslandet stämmer inte med det valda 0 %-kontot (export- eller EU-konto). Kontrollen bygger på faktureringsadressen, inte leveransadressen: går varorna till ett annat land kan kontot ändå vara rätt. Bokför ordern enskilt och bekräfta kontot för ruta 35-42.',
message_en:
'The order has a 0 % part but the billing country does not match the chosen 0 % account (export or EU account). The check uses the billing address, not the delivery address: if the goods ship to another country the account may still be right. Book the order individually and confirm the account for the right box (ruta 35-42).',
},
WEBSHOP_ORDER_REVENUE_ACCOUNT_UNKNOWN: {
httpStatus: 422,
message_sv: