* fix: prevent Chrome auto-translate from crashing React during onboarding
Chrome auto-translate modifies DOM text nodes when it detects a Swedish
page (lang="sv") in a browser set to English. React does not expect
external DOM mutations and throws, crashing the entire component tree
into global-error.tsx on every step transition.
Add translate="no" and <meta name="google" content="notranslate"> to
suppress browser translation. Also fix timezone-unsafe date parsing in
fiscal period validation (new Date("YYYY-MM-DD") + getDate() returns
local-timezone values, shifting dates by -1 day in Western timezones).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add notranslate meta tag to global-error.tsx for consistency
Per review feedback — global-error.tsx renders its own <html> document,
so it needs the same <meta name="google" content="notranslate"> tag as
layout.tsx to fully suppress Chrome translation on error pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add MCP server extension with OAuth, API keys, and KPI dashboard
Let users do bookkeeping through Claude Desktop, Claude Code, or any
MCP-compatible client. "Show my uncategorized transactions." "Book that
as office supplies." "Invoice Acme for 15,000 kr."
MCP server (extension):
- 10 tools: transactions, categorization, customers, invoices,
trial balance, VAT report, KPI report, income statement
- JSON-RPC 2.0 protocol (no SDK dependency, works in serverless)
- Tool annotations, pagination, input validation per MCP best practices
- Same engine as web UI (VAT rules, exchange rates, event emission)
API key infrastructure (core):
- api_keys table with RLS, rate limiting (100 RPM), scopes column
- Atomic rate limit via DB RPC (validate_and_increment_api_key)
- Key management API routes + settings UI panel
OAuth 2.1 for Claude Desktop connectors:
- .well-known/oauth-protected-resource + oauth-authorization-server
- Authorization endpoint with consent page
- Token endpoint with PKCE verification
- Stateless encrypted auth codes (AES-256-GCM, no DB storage)
- Dynamic client registration
KPI dashboard:
- /nyckeltal page with hero cards, operational grid, trend chart
- GET /api/reports/kpi endpoint
- Gross margin, cash position, expense ratio, avg payment days,
VAT liability, revenue/expense trend
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address OAuth security vulnerabilities from code review
Critical fixes:
- Auth code replay: Track used codes in oauth_used_codes table with
unique constraint. Codes are single-use per OAuth 2.1 §4.1.2.
- Open redirect: Validate redirect_uri against hardcoded allowlist
of known Claude callback URLs + localhost for dev.
P1 fixes:
- Move API key creation from /authorize to /token endpoint. Keys are
only created after PKCE verification, preventing orphaned keys on
abandoned OAuth flows.
- Add ensureInitialized() to MCP server so event handlers load and
transaction.categorized events reach extensions.
P2 fixes:
- Remove 'plain' from PKCE methods — only S256 is advertised and
accepted.
- Fix extension count in sectors test (10 → 11 for mcp-server).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove duplicate ensureInitialized() that caused circular import
The extension router (ext/[...path]/route.ts) already calls
ensureInitialized() before dispatching to handlers. The duplicate
call in server.ts created a circular import that Turbopack couldn't
resolve, breaking the Vercel build.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The runtime toggle system (extension_toggles table, API routes, hooks, UI components)
added unnecessary complexity. Extensions controlled via extensions.config.json at build
time are now always active for all users. This removes ~835 lines of toggle-related code
including API routes, DB queries, the ExtensionToggleButton component, useEnabledExtensions
and useExtensionToggle hooks, and the toggle-check module. AI consent gating remains
unchanged.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: include reversed entries in all reports (general ledger, trial balance, VAT, SIE, NE, INK2)
Reversed entries (storno) must appear alongside their original posted entries
in reports for a complete audit trail. Previously, filtering by status='posted'
excluded them, causing discrepancies when corrections had been made.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: semi-manual invoice payment booking with editable journal lines
When marking an invoice as paid, users now see a dialog where they can:
- Choose which bank/cash account the payment goes to (1910, 1920, 1930, etc.)
- Review and edit the proposed journal entry lines before committing
- The happy path remains fast — lines are pre-filled correctly
Implementation:
- Pure proposePaymentLines() function for line computation (accrual + cash)
- PaymentBookingDialog with AccountCombobox, balance validation, date picker
- API accepts optional custom lines, falls back to auto-generation without them
- 18 tests (8 unit + 10 API) all passing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address Greptile review — validation fallback, balance check, error handling
- P1: Return 400 on invalid body instead of silently falling back to
auto-generated lines (split JSON parse from schema validation)
- P1: Add server-side balance check for custom lines before committing
(debit must equal credit, totalDebit > 0)
- P2: Wrap PaymentBookingDialog init() in try/catch with toast on
failure and auto-close instead of silent empty state
- Add 2 new tests: unbalanced lines → 400, invalid schema → 400
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: OAuth callback redirect for local dev and timeout resilience
- Pass redirectUri dynamically from NEXT_PUBLIC_APP_URL so OAuth
callbacks work on localhost (not just production)
- Encode consentId/provider in OAuth state (base64url JSON) so the
callback doesn't depend on session storage
- Add skipAuth flag to extension API routes for OAuth callbacks
(external provider redirects have no user session cookie)
- Wrap AbortError in descriptive timeout messages in arcim-client
- Make preview endpoint resilient to partial failures (company info
and SIE fetch are individually non-blocking)
- Simplify login page (remove unused magic link auth mode)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: create journal entry before marking invoice as paid
Move journal entry creation before the invoice status update so that
if accounting fails, the invoice is not permanently marked paid without
a corresponding entry. Previously the error was silently swallowed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update mark-paid tests for journal-first ordering
Reorder mock queue to match new flow (settings before update), update
failure test to expect 500 instead of silent success, add try-catch
with proper error response in route handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add reverse charge VAT (ruta 20-32) and improve mobile UX across dashboard
Add full reverse charge (omvänd skattskyldighet) support to the VAT declaration:
- Map accounts 2614/2624/2634 to ruta 30/31/32 for self-assessed output VAT
- Calculate purchase bases (ruta 20-24) from supplier invoices by supplier type
- Include ruta 30-32 in ruta 49 formula and totalOutputVat summary
- Display reverse charge section in reports UI and composition chart
- Add comprehensive test coverage for all reverse charge scenarios
Improve mobile UX across the app:
- Convert nav drawer to bottom sheet with drag handle and safe area padding
- Add mobile card layout for PaymentBookingDialog journal lines
- Replace settings tab pills with dropdown selector on mobile
- Make wizard step indicators responsive (collapsed on mobile)
- Ensure all dialog footers stack buttons full-width on mobile
- Add 44px minimum touch targets throughout
- Make onboarding buttons full-width on mobile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address Greptile review — indentation, query efficiency, tab dedup
- Fix misleading try-block indentation in mark-paid route
- Filter reversed entries at DB level (.eq('status', 'posted')) instead
of fetching then discarding in memory
- Extract shared settingsTabs array so mobile Select and desktop
TabsList stay in sync automatically
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add resilience fallbacks, Arcim retry logic, and client tests
Add FallbackPrompt component and integrate it across banking and migration
error states so users always have a manual import escape hatch. Add retry
with exponential backoff to Arcim API client for transient failures (429,
502, 503, 504) and timeouts. Expand import page deep-linking with ?mode=
parameter. Add persistent error banner on settings page for bank connection
failures. Include 18 new tests for the Arcim client covering retry, backoff,
pagination, timeout, env validation, and singleton resource unwrapping.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address Greptile review — setActiveTab, test cleanup, redundant clearTimeout
- Add missing setActiveTab('banking') when handling bank_error query
param so the error banner is actually visible (P1)
- Guard env-var cleanup with try/finally in arcim-client tests to
prevent state leakage on assertion failure (P2)
- Only mock retry-range setTimeout delays in backoff test, letting
AbortController timers pass through real setTimeout (P2)
- Remove redundant clearTimeout in catch block — finally handles it (P2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add BFL-compliant counterparty names to journal descriptions and cancelled entry status
Journal descriptions now include customer/supplier names for traceability
(e.g. "Kundfaktura 1001, Foretag AB"). Failed draft entries are marked as
'cancelled' instead of deleted, respecting immutability constraints.
Includes DB migration for the new journal_entries status value.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address Greptile review — Swedish typos, missing source type, trigger and reversal cleanup
- Fix Swedish spelling: leverantor → leverantör in all supplier description prefixes
- Add supplier_credit_note to supplierSourceTypes in VAT declaration so credit
notes correctly reduce reverse-charge bases (ruta 20–24)
- Mark orphaned concurrent reversals as cancelled instead of attempting deletion
that the immutability trigger blocks
- Allow posted → cancelled transition in trigger for orphaned reversal cleanup
- Restrict cancelled entry line trigger to DELETE-only (block INSERT/UPDATE)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use main's Step3TaxRegistration (onboarding restructured in PR #54)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: retrigger Greptile review
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add TIC company lookup extension, extension nav items, and legacy toggle fallback
Introduces the TIC (Bolagsuppgifter) extension for automatic company data lookup
via org number during onboarding. Adds dynamic extension nav items in the sidebar,
legacy general extension fallback for toggle checks, and company lookup type
definitions in core.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address Greptile review — restore push-notifications, filter nav by toggles, fix timeout error name
- Restore push-notifications to LEGACY_GENERAL_EXTENSIONS (was silently
dropped when extracting the shared constant)
- Remove tic and arcim-migration from legacy defaults (new extensions
should not default to enabled for all users)
- Filter getExtensionNavItems() against user's enabled extensions so
disabled extensions don't appear in the sidebar
- Fix AbortSignal.timeout() error name check — Node.js throws
TimeoutError, not AbortError
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add all bundled extensions to legacy defaults (email, arcim-migration, tic)
Bundled extensions configured in extensions.config.json should default
to enabled. Adds email, arcim-migration, and tic alongside the
existing legacy defaults so they are accessible without explicit
toggle rows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: import system improvements, INK2 fix, and Swedish text corrections
- SIE parser: Windows-1252 and CP437 encoding detection and decoding
- Bank file parser: add Nordea Business (Företag) CSV format
- Bank file parser: improve format detection for SEB, Länsförsäkringar, generic CSV
- INK2 engine: calculate årets resultat (7222) from income statement for open fiscal years
- Dashboard: parallel Supabase queries, simplified dashboard page
- Fix Swedish characters (å, ä, ö) in BAS data descriptions, validation messages, AI consent disclosures
- Import wizard UI improvements across all steps
- Migration: add 'bas_range' match type to sie_account_mappings constraint
- Extensive new tests for SIE parser encoding and bank file parser
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: arcim migration wizard UX fixes, Sentry setup, and extension scaffolding
Arcim migration wizard improvements:
- Progress bar now excludes non-interactive steps (migrating/result)
- Fix OAuth text to match target="_blank" behavior (new tab, not redirect)
- Display month names instead of "Månad X" in preview
- Fix Swedish typo "förifylla" in no-company-info message
- Replace native checkboxes with shadcn Switch in options step
- Add ConfirmationDialog before starting migration
- Show progress percentage during migration
- Add "Nästa steg" guidance and navigation links in result step
- Add "Försök igen" button in error state (returns to options)
- Add Bokio company ID help text (GUID from URL)
- Add Fortnox integration add-on hint on connection failure
Also includes: SIE import system improvements, INK2 fixes, Swedish text
corrections, Sentry error tracking setup, and arcim-migration extension
scaffolding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback
- Fix OAuth error recovery blank page (restore provider from URL params)
- Pass real userId to MigrationWizard instead of empty string
- Remove ~50 debug console.log statements from sie-import.ts
- Fix comment referencing account 3740 → 3741
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- SIE parser: Windows-1252 and CP437 encoding detection and decoding
- Bank file parser: add Nordea Business (Företag) CSV format
- Bank file parser: improve format detection for SEB, Länsförsäkringar, generic CSV
- INK2 engine: calculate årets resultat (7222) from income statement for open fiscal years
- Dashboard: parallel Supabase queries, simplified dashboard page
- Fix Swedish characters (å, ä, ö) in BAS data descriptions, validation messages, AI consent disclosures
- Import wizard UI improvements across all steps
- Migration: add 'bas_range' match type to sie_account_mappings constraint
- Extensive new tests for SIE parser encoding and bank file parser
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Broad update across dashboard pages, components, extensions, and lib code. Includes ESLint config additions, onboarding flow redesign, settings page refactor, help page content expansion, dead code removal, and test mock fixes. Adds dev docs and public assets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix VAT declaration ruta mappings to match SKV 4700 form correctly
(ruta 05 = total taxable sales, ruta 10/11/12 = output VAT per rate)
- Add INK2 declaration report for aktiebolag with SRU export
- Add full archive ZIP export for 7-year retention compliance
- Add AI consent gate requiring user approval before AI extension API calls
- Add DPA and privacy policy public pages
- Add audit trail API routes
- Update VAT registration threshold from 80k to 120k kr in onboarding
- Update CLAUDE.md documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate the standalone user-description-match extension into ai-categorization,
adding an AI description analyzer that provides account/VAT suggestions alongside
template matching. The describe transaction dialog now shows AI suggestions with
confidence scores and supports both template-based and AI-based booking.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove all sector-specific extensions (construction, ecommerce, export,
hotel, restaurant, tech) — only general-purpose extensions remain
- Move NE-bilaga and SRU export from extensions to core reports (lib/reports/)
- Move moms-box-mapping from extensions/export/shared to lib/vat/
- Replace per-extension API routes with catch-all dispatcher
(app/api/extensions/ext/[...path]/route.ts)
- Add manifest.json for each extension with metadata, env vars, and deps
- Add api-routes.ts pattern for extension-defined API endpoints
- Add code generation scripts (generate-extension-registry, create-extension)
- Add extensions.config.json for opt-in extension loading
- Add extensions.schema.json for config validation
- Add email service interface with noop default (lib/email/service.ts)
- Add CI workflow (core-build.yml) to verify core builds with zero extensions
- Add migration 045: expand account_type CHECK for untaxed_reserves
- Update CLAUDE.md with comprehensive extension system documentation
- Update all report engines and bookkeeping services for new imports
- Clean up extensions.schema.json to only list existing extensions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce booking template library to eliminate duplicate suggestions when
users describe transactions. Templates with identical accounting treatment
(same account + VAT) are merged, keywords consolidated, and the entire
subscriptions group is eliminated. Also includes prior work on reports,
extensions, and transaction improvements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add export & utrikeshandel sector to the extension system with BAS
accounts (3105, 3108, 3109, 3521, 3522), sector metadata, icon
imports, workspace registry entries, and design document.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enrich ExtensionContext with supabase, emit(), settings, storage, log,
and services so extensions can receive everything through dependency
injection instead of importing core modules directly.
- Add context factory and inject context into event handlers via registry
- Move supplier invoice journal entry creation to core event handler
- Add services.ingestTransactions to ExtensionContext for enable-banking
- Create catch-all API route for extension-declared apiRoutes
- Migrate 5 extensions to accept context with dynamic import fallbacks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add shared components (ConfirmDeleteDialog, EditEntryDialog, validation utils),
enhance all 12 extension workspaces with edit/delete dialogs, input validation,
period comparisons, and new analytics features. Fix critical bugs in
ProjectBilling margin calculation and EarningsPerLiter revenue allocation.
Add pure calculation modules with 183 new tests across all extensions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>