feat(woocommerce): store order/refund feed extension (#1442)
* feat(woocommerce): store order/refund feed extension Connect a WooCommerce store via the wc-auth key handshake (manual key fallback) with per-store consumer key/secret AES-256-GCM encrypted at rest, and import paid orders and refunds into the transactions inbox as a bank-style feed on the 1680 cash account. Feed-only: nothing auto-books, gateway fees/payouts are out of scope (core wc/v3 does not expose them). Sync is cursor-paginated on modified_after (offset pages only inside same-second date_modified ties), terminates on an empty page, holds the cursor below failed refund fetches / ingest errors / deadline-skipped work, checks the time budget between refund fetches, and drops rows dated on or before bookkeeping_locked_through on every run. Nightly cron gated on the extension registry + new paid capability woocommerce_sync (backfilled to existing bank_sync grant holders). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(migrations): move woocommerce migrations past main's 20260806090000 origin/main gained 20260806090000_recurring_schedule_interval_months while this branch was in flight; identical version timestamps abort the Supabase apply, so the two new migrations move to 20260806170000/20260806170100. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(woocommerce): resolve CodeRabbit review findings - callback 503s early when WOOCOMMERCE_CREDENTIALS_ENCRYPTION_KEY is unset: encryptCredential would otherwise throw after the probe and strand the pending row without error_message - disconnect and upstream-revoke clear the encrypted consumer key/secret: nothing reads them after revoke and keeping decryptable dead credentials is unnecessary retention - manual sync gets a 240s time budget and the panel reports a truncated run as 'partial, sync again' instead of a normal completion - listOrderRefunds terminates on an empty batch (hosts may cap per_page), dedupes by id against hosts that ignore page, and caps total pages - unparseable money strings count as errors and log instead of being silently identical to a zero total - pg test uses per-run unique store URLs so committed rows cannot hit the store_url partial unique index across pg-real runs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(woocommerce): resolve CodeRabbit cycle-2 findings - listOrderRefunds throws when the page cap is exhausted with data still flowing, instead of returning a silently partial list the sync cursor would advance past; the error routes into the existing held-cursor refund-retry path - partial sync results keep the row-error count, and the partial toast string surfaces it (ICU plural, hidden at zero) in both locales Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: retrigger CI after dropped push event Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cd344b6dbb
commit
707d597b2e
@@ -409,6 +409,59 @@
|
||||
"transaction_sync_toggle_failed": "Could not save the setting. Please try again.",
|
||||
"sync_done_transactions": "{imported} transaction(s) imported, {linked} linked to vouchers."
|
||||
},
|
||||
"woocommerce": {
|
||||
"title": "WooCommerce",
|
||||
"description": "Connect your WooCommerce store to fetch paid orders and refunds into the transaction inbox, as a bank-style feed for the store. You book the rows from the inbox as usual.",
|
||||
"not_configured": "The WooCommerce integration is not configured on this installation. Contact your administrator.",
|
||||
"load_failed": "Could not read the WooCommerce status. Check your connection and try again.",
|
||||
"action_timeout": "The action took too long. Reload the page to see whether it went through.",
|
||||
"action_network": "No contact with the server. Check your connection and try again.",
|
||||
"store_url_label": "Store address",
|
||||
"connect": "Connect store",
|
||||
"connecting": "Connecting…",
|
||||
"connect_hint": "You are sent to your store to approve the connection with read access. The keys are stored encrypted and you can revoke them at any time, here or in WooCommerce.",
|
||||
"manual_toggle": "Enter API keys manually",
|
||||
"manual_hint": "Create a read-only API key in WooCommerce under Settings, Advanced, REST API and paste the keys here.",
|
||||
"consumer_key_label": "Consumer key",
|
||||
"consumer_secret_label": "Consumer secret",
|
||||
"manual_connect": "Connect with keys",
|
||||
"disconnect": "Disconnect",
|
||||
"disconnect_confirm": "Yes, disconnect",
|
||||
"cancel": "Cancel",
|
||||
"status_active": "Connected",
|
||||
"status_pending": "Pending",
|
||||
"status_revoked": "Disconnected",
|
||||
"status_error": "Error",
|
||||
"connected_since": "Connected {date}",
|
||||
"unnamed_store": "WooCommerce store",
|
||||
"pending_note": "Waiting for the store to send the keys. Finish the approval in the store if you have not already.",
|
||||
"connected_toast_title": "WooCommerce connected",
|
||||
"connected_toast_description": "The store's paid orders and refunds are now fetched every night.",
|
||||
"disconnected_toast_title": "WooCommerce disconnected",
|
||||
"disconnected_toast_description": "Also remove the API key in the store's WooCommerce settings.",
|
||||
"connect_failed_title": "Connection failed",
|
||||
"disconnect_failed_title": "Disconnect failed",
|
||||
"error_generic": "Something went wrong. Please try again.",
|
||||
"error_denied": "The connection was denied in the store.",
|
||||
"sync_now": "Sync now",
|
||||
"syncing": "Syncing…",
|
||||
"sync_done_title": "Sync complete",
|
||||
"sync_done_feed": "{fetched} order(s) fetched: {imported} new rows in the inbox.",
|
||||
"sync_done_empty": "The store returned no orders for the period. Check that the right store is connected if you expected orders.",
|
||||
"sync_done_feed_errors": "{fetched} order(s) fetched: {imported} new rows in the inbox. {errors} row(s) could not be imported: sync again.",
|
||||
"sync_partial_title": "Sync paused",
|
||||
"sync_partial": "{fetched} order(s) fetched so far: {imported} new rows in the inbox.{errors, plural, =0 {} other { # row(s) could not be imported.}} Not all orders were fetched in time: sync again to continue where it stopped.",
|
||||
"sync_failed_title": "Sync failed",
|
||||
"sync_revoked": "The store rejected the API key, so no orders could be fetched. Connect the store again.",
|
||||
"transaction_sync_title": "Orders from WooCommerce",
|
||||
"transaction_sync_description": "Fetch the store's paid orders and refunds into the transaction inbox every night, as a bank-style feed for the store. You book the rows from the inbox as usual.",
|
||||
"transaction_sync_backfill_note": "The first sync fetches up to 90 days of history, but never before the bookkeeping lock.",
|
||||
"transaction_sync_last_synced": "Last synced {date}",
|
||||
"transaction_sync_never_synced": "Not synced yet",
|
||||
"transaction_sync_enabled_toast": "Order sync enabled. History is fetched on the next sync.",
|
||||
"transaction_sync_disabled_toast": "Order sync disabled.",
|
||||
"transaction_sync_toggle_failed": "Could not save the setting. Please try again."
|
||||
},
|
||||
"settings_modal": {
|
||||
"title": "Settings",
|
||||
"description": "Manage your company and account"
|
||||
@@ -6356,6 +6409,10 @@
|
||||
"stripe_description": "Connect your company's Stripe account to fetch payments, fees, and payouts continuously and book them against the Stripe balance.",
|
||||
"stripe_not_enabled_title": "The Stripe extension is not enabled",
|
||||
"stripe_not_enabled_description": "Enable the Stripe extension to connect your Stripe account and sync transactions automatically.",
|
||||
"woocommerce_title": "WooCommerce",
|
||||
"woocommerce_description": "Connect your WooCommerce store to fetch paid orders and refunds into the transaction inbox.",
|
||||
"woocommerce_not_enabled_title": "The WooCommerce extension is not enabled",
|
||||
"woocommerce_not_enabled_description": "Enable the WooCommerce extension to connect your store and fetch orders automatically.",
|
||||
"migration_title": "Import from another system",
|
||||
"migration_description": "Nothing changes in your existing system.",
|
||||
"bankfile_title": "Bank file",
|
||||
|
||||
@@ -409,6 +409,59 @@
|
||||
"transaction_sync_toggle_failed": "Kunde inte spara inställningen. Försök igen.",
|
||||
"sync_done_transactions": "{imported} transaktion(er) importerade, {linked} länkade till verifikat."
|
||||
},
|
||||
"woocommerce": {
|
||||
"title": "WooCommerce",
|
||||
"description": "Koppla din WooCommerce-butik så hämtas betalda ordrar och återbetalningar till transaktionsinkorgen, som ett bankflöde för butiken. Du bokför raderna som vanligt från inkorgen.",
|
||||
"not_configured": "WooCommerce-integrationen är inte konfigurerad på den här installationen. Kontakta administratören.",
|
||||
"load_failed": "Kunde inte läsa WooCommerce-statusen. Kontrollera din uppkoppling och försök igen.",
|
||||
"action_timeout": "Åtgärden tog för lång tid. Ladda om sidan för att se om den gick igenom.",
|
||||
"action_network": "Ingen kontakt med servern. Kontrollera din uppkoppling och försök igen.",
|
||||
"store_url_label": "Butikens adress",
|
||||
"connect": "Anslut butik",
|
||||
"connecting": "Ansluter…",
|
||||
"connect_hint": "Du skickas till din butik för att godkänna kopplingen med läsbehörighet. Nycklarna lagras krypterade och du kan när som helst återkalla dem här eller i WooCommerce.",
|
||||
"manual_toggle": "Ange API-nycklar manuellt",
|
||||
"manual_hint": "Skapa en API-nyckel med läsbehörighet i WooCommerce under Inställningar, Avancerat, REST-API och klistra in nycklarna här.",
|
||||
"consumer_key_label": "Konsumentnyckel (consumer key)",
|
||||
"consumer_secret_label": "Konsumenthemlighet (consumer secret)",
|
||||
"manual_connect": "Anslut med nycklar",
|
||||
"disconnect": "Koppla från",
|
||||
"disconnect_confirm": "Ja, koppla från",
|
||||
"cancel": "Avbryt",
|
||||
"status_active": "Ansluten",
|
||||
"status_pending": "Väntar",
|
||||
"status_revoked": "Frånkopplad",
|
||||
"status_error": "Fel",
|
||||
"connected_since": "Ansluten {date}",
|
||||
"unnamed_store": "WooCommerce-butik",
|
||||
"pending_note": "Väntar på att butiken ska skicka nycklarna. Slutför godkännandet i butiken om du inte redan gjort det.",
|
||||
"connected_toast_title": "WooCommerce anslutet",
|
||||
"connected_toast_description": "Butikens betalda ordrar och återbetalningar hämtas nu varje natt.",
|
||||
"disconnected_toast_title": "WooCommerce frånkopplat",
|
||||
"disconnected_toast_description": "Ta även bort API-nyckeln i butikens WooCommerce-inställningar.",
|
||||
"connect_failed_title": "Anslutningen misslyckades",
|
||||
"disconnect_failed_title": "Frånkopplingen misslyckades",
|
||||
"error_generic": "Något gick fel. Försök igen.",
|
||||
"error_denied": "Anslutningen nekades i butiken.",
|
||||
"sync_now": "Synka nu",
|
||||
"syncing": "Synkar…",
|
||||
"sync_done_title": "Synkronisering klar",
|
||||
"sync_done_feed": "{fetched} order/ordrar hämtade: {imported} nya rader i inkorgen.",
|
||||
"sync_done_empty": "Butiken returnerade inga ordrar för perioden. Kontrollera att rätt butik är ansluten om du väntade dig ordrar.",
|
||||
"sync_done_feed_errors": "{fetched} order/ordrar hämtade: {imported} nya rader i inkorgen. {errors} rad(er) kunde inte importeras: synka igen.",
|
||||
"sync_partial_title": "Synkroniseringen pausades",
|
||||
"sync_partial": "{fetched} order/ordrar hämtade hittills: {imported} nya rader i inkorgen.{errors, plural, =0 {} other { # rad(er) kunde inte importeras.}} Alla ordrar hann inte hämtas: synka igen för att fortsätta där det stannade.",
|
||||
"sync_failed_title": "Synkroniseringen misslyckades",
|
||||
"sync_revoked": "Butiken avvisade API-nyckeln, så inga ordrar kunde hämtas. Anslut butiken igen.",
|
||||
"transaction_sync_title": "Ordrar från WooCommerce",
|
||||
"transaction_sync_description": "Hämta butikens betalda ordrar och återbetalningar till transaktionsinkorgen varje natt, som ett bankflöde för butiken. Du bokför raderna som vanligt från inkorgen.",
|
||||
"transaction_sync_backfill_note": "Vid första synkningen hämtas upp till 90 dagars historik, dock inte före bokföringslåset.",
|
||||
"transaction_sync_last_synced": "Senast synkad {date}",
|
||||
"transaction_sync_never_synced": "Inte synkad ännu",
|
||||
"transaction_sync_enabled_toast": "Ordersynk aktiverad. Historiken hämtas vid nästa synkning.",
|
||||
"transaction_sync_disabled_toast": "Ordersynk avaktiverad.",
|
||||
"transaction_sync_toggle_failed": "Kunde inte spara inställningen. Försök igen."
|
||||
},
|
||||
"settings_modal": {
|
||||
"title": "Inställningar",
|
||||
"description": "Hantera ditt företag och konto"
|
||||
@@ -6356,6 +6409,10 @@
|
||||
"stripe_description": "Koppla företagets Stripe-konto så hämtas betalningar, avgifter och utbetalningar löpande och bokförs mot Stripe-saldot.",
|
||||
"stripe_not_enabled_title": "Stripe-tillägget är inte aktiverat",
|
||||
"stripe_not_enabled_description": "Aktivera tillägget Stripe för att koppla ditt Stripe-konto och synka transaktioner automatiskt.",
|
||||
"woocommerce_title": "WooCommerce",
|
||||
"woocommerce_description": "Koppla din WooCommerce-butik så hämtas betalda ordrar och återbetalningar till transaktionsinkorgen.",
|
||||
"woocommerce_not_enabled_title": "WooCommerce-tillägget är inte aktiverat",
|
||||
"woocommerce_not_enabled_description": "Aktivera tillägget WooCommerce för att koppla din butik och hämta ordrar automatiskt.",
|
||||
"migration_title": "Hämta från annat system",
|
||||
"migration_description": "Inget ändras i ditt befintliga system.",
|
||||
"bankfile_title": "Bankfil",
|
||||
|
||||
Reference in New Issue
Block a user