From 129664533c706ce682175ef7d996b1f86d7085a0 Mon Sep 17 00:00:00 2001 From: Mattsson <111893710+mattssonn@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:25:13 +0200 Subject: [PATCH] Bug/fortnox import (#298) * feat: enhance journal entry handling with follow-up entries and related RPC * fix: improve validation for journal entry lines to ensure proper submission criteria * fix: enhance OAuth error handling and user feedback in Arcim migration process * fix: add OAuth error translation for user-friendly feedback in Fortnox integration --- .../general/ArcimMigrationWorkspace.tsx | 15 ++- extensions/general/arcim-migration/index.ts | 106 ++++++++++++++---- ...20000_journal_entries_with_related_rpc.sql | 7 +- 3 files changed, 102 insertions(+), 26 deletions(-) diff --git a/components/extensions/general/ArcimMigrationWorkspace.tsx b/components/extensions/general/ArcimMigrationWorkspace.tsx index 01076d34..fc5fabdb 100644 --- a/components/extensions/general/ArcimMigrationWorkspace.tsx +++ b/components/extensions/general/ArcimMigrationWorkspace.tsx @@ -1702,10 +1702,13 @@ export default function ArcimMigrationWorkspace(_props: WorkspaceComponentProps) await loadPreview(callbackConsentId) } else if (migrationStatus === 'error') { const callbackProvider = url.searchParams.get('provider') as ArcimProvider | null + const reason = url.searchParams.get('reason') || 'OAuth-anslutningen misslyckades. Försök igen.' url.searchParams.delete('migration') url.searchParams.delete('provider') + url.searchParams.delete('reason') window.history.replaceState({}, '', url.pathname) - setError('OAuth-anslutningen misslyckades. Försök igen.') + setError(reason) + toast({ title: 'Anslutning misslyckades', description: reason, variant: 'destructive' }) if (callbackProvider) { setSelectedProvider(callbackProvider) setStep('connect') @@ -1713,7 +1716,7 @@ export default function ArcimMigrationWorkspace(_props: WorkspaceComponentProps) setStep('provider') } } - }, [loadPreview]) + }, [loadPreview, toast]) // Check for OAuth callback on mount (fallback for non-popup flow) useEffect(() => { @@ -1728,12 +1731,16 @@ export default function ArcimMigrationWorkspace(_props: WorkspaceComponentProps) if (event.data?.type === 'arcim-oauth-success' && event.data.consentId) { loadPreview(event.data.consentId) } else if (event.data?.type === 'arcim-oauth-error') { - setError('OAuth-anslutningen misslyckades. Försök igen.') + const reason = typeof event.data.reason === 'string' && event.data.reason + ? event.data.reason + : 'OAuth-anslutningen misslyckades. Försök igen.' + setError(reason) + toast({ title: 'Anslutning misslyckades', description: reason, variant: 'destructive' }) } } window.addEventListener('message', handleMessage) return () => window.removeEventListener('message', handleMessage) - }, [loadPreview]) + }, [loadPreview, toast]) // Load SIE data when entering mapping step const loadSIEData = useCallback(async () => { diff --git a/extensions/general/arcim-migration/index.ts b/extensions/general/arcim-migration/index.ts index e6d34718..458445d6 100644 --- a/extensions/general/arcim-migration/index.ts +++ b/extensions/general/arcim-migration/index.ts @@ -30,6 +30,29 @@ const ALLOWED_FISCAL_YEARS = new Set([2024, 2025, 2026]) const fortnoxClient = new FortnoxClient() +/** + * Map known OAuth error codes from providers (Fortnox, Visma) to actionable + * Swedish guidance. Falls back to the raw provider message so we never hide + * unknown errors from the user. + */ +function translateOAuthError(error: string, description: string | null): string { + const haystack = `${error} ${description ?? ''}`.toLowerCase() + + if (haystack.includes('missing license') || haystack.includes('not have enough licenses')) { + return 'Du behöver aktivera tilläggstjänsten "Fortnox Integration" (~149 kr/mån) på ditt Fortnox-konto innan du kan ansluta. Aktivera den under Inställningar → Tilläggstjänster i Fortnox och försök igen.' + } + + if (error === 'access_denied') { + return 'Du avbröt anslutningen i leverantörens inloggning. Försök igen om du vill koppla kontot.' + } + + if (error === 'invalid_scope') { + return 'Tredjepartsappen har inte rätt behörigheter för ditt konto. Kontakta supporten.' + } + + return description ? `${error}: ${description}` : error +} + /** * Provider Migration extension * @@ -314,9 +337,59 @@ export const arcimMigrationExtension: Extension = { const url = new URL(request.url) const code = url.searchParams.get('code') const stateRaw = url.searchParams.get('state') + const oauthError = url.searchParams.get('error') + const oauthErrorDescription = url.searchParams.get('error_description') + const appUrl = process.env.NEXT_PUBLIC_APP_URL || '' + + // JSON-encode for safe embedding inside
Anslutningen misslyckades: ${escapedReason}