* feat(shopify): port the order sync from the transactions feed to webshop_orders
Shopify orders now land as rich rows on the Orders page (platform
'shopify'), the same surface WooCommerce uses, instead of opaque
bank-feed rows on the 1584 cash account:
- order-sync.ts writes through the shared upsertWebshopOrders service;
the 1584/ensureManualCashAccount wiring is gone (prod has zero Shopify
feed rows). Cursor/overlap/dedup, revoked classification and the
frozen external_id formats are unchanged.
- vat_breakdown is reconstructed from the order-level taxLines
(net = tax/rate, remainder as a 0%-bucket, refuse on unusable data);
refund VAT is prorated from the parent order's mix. The line-item
snapshot is stored only when it reconstructs the charged total to the
ore, else the invoice conversion falls back to one aggregate line.
- GraphQL query gains createdAt, taxesIncluded, taxLines, lineItems and
shippingLines (all non-PII; page size 100 -> 25 for query cost).
- Nav gate counts active shopify_connections; the Orders empty-state CTA
goes to the platform-neutral /import hub; panel/manifest copy now
points at the Orders page (sv + en).
- Paid-only qualification and the 90-day backfill stay; the
bookkeeping-lock row filter is dropped (lock is enforced at booking,
parity with WooCommerce).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(shopify): carry the prorated parent tax on refunds when per-rate bucketing is refused
A refund whose parent vat_breakdown was refused (unreported rates) stored
total_tax 0 and prefilled a 0%-refund with no moms reversal. The parent's
total tax is now prorated into the refund row, so the booking dialog's
ratio-inference fallback presents an editable bucket with the reversal
instead (CodeRabbit + Swedish review + skeptic finding). Adds the
mixed-rate line and truncated shipping-page tests CodeRabbit asked for.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(shopify): Shopify order/refund feed into the transactions inbox
New extensions/general/shopify feed extension, modeled on the WooCommerce
feed: connect a Shopify store with Dev Dashboard custom-app client
credentials (client credentials grant, ~24h tokens, never stored), then a
nightly cron + manual sync imports paid orders and refunds via the GraphQL
Admin API (pinned 2026-07) into the transactions inbox on clearing account
1584. Feed-only: nothing auto-books. Zero PII fields are queried, keeping
the app outside Shopify's protected customer data program.
- shopify_connections migration (RLS, revoke-never-delete, encrypted
client id/secret) + shopify_sync capability and bank_sync-mirrored
backfill
- frozen external_id scheme shopify_{shop_domain}_order|refund_{id},
scoped on the shop domain so reconnects never re-import
- cursor sync on updated_at windows with 24h overlap, lock-date drop at
map time, ingest-failure cursor floor, deadline stop-and-resume,
revoked-credential flip
- /import card + settings panel, sv/en i18n, cron 03:15 in vercel.json +
regenerated Docker crontabs, logo, events, panel registry
- 65 unit tests + pg-real RLS test; extensions.schema.json enum also
gains the missing stripe entry (pre-existing drift)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(shopify): review findings from PR 1474
- token exchange: a 429 that survives every retry is throttling, not a
credential failure; stop remapping retryable 4xx to 401 so sustained
throttling can no longer flip the connection to revoked and delete the
stored credentials (CodeRabbit critical)
- order sync: advance a scanned-through watermark (run start, capped by
the failure floor) after a fully-listed window, so empty first runs and
quiet stores rotate to the back of the cron's oldest-first selection
instead of permanently occupying the 50-connection batch (CodeRabbit
major, starvation)
- add handler-level tests for the orders cron route (auth 401, disabled
503, unconfigured no-op, query failure, capability skip, happy path,
per-connection failure isolation, revoked marking)
- add 401 tests for /sync, /transaction-sync and /disconnect; pin the
cursor floor rule with a two-order page; stub the encryption key via
vi.stubEnv
- note in the panel description (sv/en) that orders can mix VAT rates and
must be split at booking (Swedish review advisory)
- DECISIONS.md: wrap underscore identifiers in backticks (MD037)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>