Community PR #2416 by @olofpinzke, adopted and finished by maintainers (rebased so every commit is signed).
Why the problem occurred: no Zettle integration; POS sales only reached the books as bank descriptors while Woo/Shopify already had order underlag via webshop_orders. The contributor's version also failed at the database (platform CHECKs listed only woocommerce/shopify), which the mocked unit tests never saw.
What was simplified: reused the Orders/book/invoice path instead of a new inbox; Finance API payouts/fees deferred. Sales the one-account, revenue-per-rate model cannot book (split tender, gift cards, tips) import unbookable with a "bokför manuellt" title instead of guessing accounts. Reset parity uses the rename-and-wrap pattern instead of re-issuing the reset body.
Why this solution: per-purchase rows give the radunderlag BFL verifikat need and the bulk-book path exists; daily kassarapport aggregation and Finance API fees/payouts are the follow-up (DECISIONS.md). Skeptic-refuted paths fixed before merge: concurrent refresh-token rotation (sync claim), cron offset paging (candidate snapshot), platform CHECKs, writer-role gate, migration-reset parity, white-label return origin re-validated at callback, VAT net from product rows.
Not live until ZETTLE_CLIENT_ID / ZETTLE_CLIENT_SECRET / ZETTLE_CREDENTIALS_ENCRYPTION_KEY are set on Vercel and a Zettle developer app is registered with the callback redirect URI.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WtYqzKPoTSRHskYYdf7MwB
* feat(webshop): generate orderunderlag PDF and attach it to the verifikat at booking
Booked webshop orders only carried the VAT split; the verifikat showed no
product lines, customer or payment method although the sync already stores
all of it in webshop_orders.line_items (#1881).
- lib/webshop-orders/order-underlag.tsx: pure model builder + react-pdf
template (order lines, customer, payment method, per-rate VAT summary,
SEK conversion facts) + archiveWebshopOrderUnderlag, which renders and
archives the PDF on the committed verifikat through uploadDocument
(upload_source system, extraction none), mirroring archiveIssuedInvoicePdf.
Never throws: the booking is immutable by then.
- book route: archive after commitEntry; response gains underlag_archived.
FX-retry now also syncs the in-memory row so the underlag shows the
resolved SEK facts.
- webshop_order added to NEEDS_DOC_SOURCE_TYPES and (new migration
20260825140000) to the verifikat_without_documents needs-doc list, so a
failed attach or a historical booking surfaces on the saknar-underlag
worklist. transactions_without_documents is deliberately unchanged.
- tests: underlag model/render/archive unit tests, book-route archive and
failure-isolation cases, pg test extended (per-source-type probe now
covers webshop_order; explicit flagged/silenced pair).
Fixes#1881
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(migrations): move webshop needs-doc migration after main's 20260825150000
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(webshop): add manually_booked fields to the underlag order fixture
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(webshop): skeptic findings on the orderunderlag (#1881)
Two refutations from the skeptic pass on PR #1899, both fixed:
1. Correctness: sv-SE Intl emits U+2212 MINUS SIGN for negatives, which
Helvetica/WinAnsi PDF fonts drop silently, so refund and discount
amounts on the archived underlag rendered as POSITIVE. formatAmount now
replaces U+2212 with an ASCII hyphen (same guard as formatPdfCurrency),
is exported, and is pinned by a regression test.
2. Regression: NEEDS_DOC_SOURCE_TYPES had two hardcoded copies that missed
webshop_order, so flagged rows rendered without the "Underlag saknas"
chip, waiver toggle, or batch-exempt selection, and the weekly
missing-underlag push cron disagreed with the badge. The constant now
lives in dependency-free lib/worklist/types.ts (client-safe), is
re-exported from categories.ts, and both JournalEntryList.tsx and
push-notifications/notification-scheduler.ts consume it instead of
their own copies.
Also: "Bokfört i SEK" reworded to "Motsvarande i SEK" (compliance skeptic
observation: the dialog's lines are user-editable, so the underlag must
state the order's conversion, not claim a booking fact).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>