Commit Graph
22 Commits
Author SHA1 Message Date
Jakob WennbergandClaude Opus 4.7 cad83d180d fix(currency): transaction-date FX rate + mall bookings in SEK (#442) (#483)
* fix(currency): use transaction-date FX rate and post mall bookings in SEK (#442)

Three related defects in non-SEK transaction handling:

1. Ingest priced every transaction in a batch at today's rate because
   fetchMultipleRates() was called without a date. Switch to a per-
   (currency, date) fetch via fetchExchangeRate() + Promise.allSettled,
   so each row stores Riksbanken's rate as of its own transaction date.

2. buildMappingResultFromTemplate emitted VAT/reverse-charge/output-VAT
   lines from Math.abs(transaction.amount) — the source-currency amount.
   Mall bookings on USD/EUR transactions therefore posted the journal
   entry in the wrong currency. Resolve to SEK once via resolveSekAmount()
   and reuse for every line, matching what createTransactionJournalEntry
   already does for the settlement line.

3. The Granska bokföring modal and the journal-entry detail page lacked
   a clear audit trail. The verifikation preview now renders in SEK with
   a separate original-currency subtitle in the header; the detail page
   gains a dedicated "Valutaomräkning" card above Kontorader showing the
   rate and original total.

Also: replace truncate with break-all on the modal description so long
UUID-style strings (e.g. BALANCE_CASHBACK-…) wrap instead of forcing
horizontal overscroll.

Tests: 4 new booking-templates cases (USD 25%, EUR fallback via
exchange_rate, EU reverse-charge on USD, output VAT on USD income) and
2 new ingest cases (per-pair fetch dedup; no fetch for SEK rows).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(currency): wire SEK conversion into SwipeCategorizationView (#442 follow-up)

Address Greptile review:

- SwipeCategorizationView rendered JournalEntryPreview without amountSek,
  so the swipe flow had the same defect QuickReviewDialog had — a USD
  transaction showed the verifikation in source currency labeled SEK.
  Now resolves SEK once via resolveSekAmount, passes it to the preview,
  and shows SEK as the headline with the original-currency subtitle
  (parallel to QuickReviewDialog).
- Drop the now-dead `currency` prop from JournalEntryPreviewProps and
  its remaining call site in QuickReviewDialog — the component
  hardcodes 'SEK' on display, so the prop was misleading.
- Correct the misleading inline comment in ingest.ts: network failures
  resolve inside fetchExchangeRate to getFallbackRate() (non-null),
  so the key only stays unset on empty-observation responses or
  outright rejections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:00:12 +02:00
Jakob WennbergandClaude Opus 4.7 94f15b9c6c feat(invoice-inbox): pin documents to bank transactions + MCP tools (#397)
* feat(invoice-inbox): pin documents to bank transactions + MCP tools

Adds a first-class flow for attaching unmatched inbox documents to bank
transactions, separate from the existing supplier-invoice convert path:

- new transactions.document_id FK → document_attachments (ON DELETE SET NULL)
- POST/DELETE /api/transactions/[id]/attach-document
- categorize route propagates the link to journal_entry_id on commit
- three new MCP tools: gnubok_list_unmatched_documents,
  gnubok_get_document_content (5-min signed URL),
  gnubok_attach_document_to_transaction (staged via pending_operations)
- InvoiceInboxWorkspace gains a "Koppla till transaktion" picker dialog
  ranked by amount-match, plus a "Bilaga" badge in SwipeCategorizationView
- regex extraction unchanged; supplier-invoice convert flow unchanged

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(invoice-inbox): address PR #397 review findings

- categorize: destructure { error } from the document-link update so
  Supabase-level failures are logged instead of silently dropped (BFL 5 kap 6 §
  receipt-on-verifikation contract).
- list_unmatched_documents: emit next_cursor whenever the inbox query may have
  more rows, not only when the post-filter slice was full; switch to composite
  (created_at, id) cursor to avoid same-second collisions.
- DELETE /attach-document: return 404 when the tx isn't in the company; return
  409 when the linked document already has journal_entry_id set
  (räkenskapsinformation immutability).
- risk tier: attach_document_to_transaction medium (was low) — link becomes
  part of verifikation underlag once categorize propagates it.
- pg-real test: stop reusing $2 across uuid + text-concat contexts (Postgres
  couldn't deduce the parameter type).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(migrations): break duplicate version 20260505120000 (Supabase Preview)

Two migrations on main share filename version 20260505120000:
  - 20260505120000_api_keys_refresh_token.sql (PR #392)
  - 20260505120000_drop_agent_auto_commit.sql (PR #394)

The schema_migrations primary key is (version), so any fresh DB doing
`supabase db push` over both files conflicts on the second insert. This is
why every PR with a migration since #394 has had Supabase Preview either
fail or skip.

Renaming _drop_agent_auto_commit to 20260505190027 — that matches the
timestamp recorded in prod schema_migrations from when apply_migration was
called for it, so future `db push` against prod sees the file as already-
applied (no re-run). The migration body is fully idempotent (IF EXISTS on
every drop) so a re-run would be a no-op anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(invoice-inbox): address PR #397 round-2 compliance review

Two BFL gaps the compliance bot flagged on the round-1 fix commit:

1. commitAttachDocumentToTransaction silently broke verifikation→underlag if
   the transaction was categorized between staging and approval. Now reads
   transactions.journal_entry_id at commit time and, if non-null, also writes
   document_attachments.journal_entry_id in the same commit so BFL 5 kap 6 §
   is satisfied regardless of order.

2. Application-layer DELETE check was racy (SELECT then UPDATE) and the FK
   ON DELETE SET NULL path could null transactions.document_id even for a
   document that is räkenskapsinformation. Added a BEFORE UPDATE OF
   document_id trigger on transactions that raises check_violation when the
   previously-attached document has document_attachments.journal_entry_id
   set. The app-layer guard stays for friendly Swedish messaging; the
   trigger is the DB-level safety net.

pg-real test extended to cover both directions of the trigger (block detach
+ block swap) and the happy-path detach when there's no JE link yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(invoice-inbox): address PR #397 round-3 compliance review

Four findings from the round-2 update of the compliance bot. The first three
are genuine compliance gaps; the fourth (preview metadata distinguishing pre-
vs post-categorization overwrites) is a UX nicety left for follow-up.

1. transactions.document_id FK switched from ON DELETE SET NULL to RESTRICT
   (migration 20260506100000). Removes the "trigger ordering" concern: a doc
   that's pinned to any tx now cannot be deleted at all without explicit
   detach first. Belt-and-braces with block_document_deletion.

2. commitAttachDocumentToTransaction now does:
   - pre-check that mirrors the DELETE route's 409 when the existing pinned
     doc is räkenskapsinformation, so the same Swedish message is returned
     in both paths;
   - UPDATE…RETURNING journal_entry_id so the propagation decision uses the
     post-update state, closing the read-then-write race with concurrent
     categorize. Either ordering of attach-then-categorize or
     categorize-then-attach now lands at the same correct final state.

3. Both DELETE /attach-document and the MCP commit path catch the trigger's
   check_violation (SQLSTATE 23514) and translate to 409 with the Swedish
   underlag message. The trigger remains the DB-level safety net; the app
   layer is responsible only for friendly UX.

pg-real test rewritten for ON DELETE RESTRICT (blocks deletion of pinned doc;
detach-then-delete works). Unit coverage added for commitAttach: 404, two
distinct 409 paths (pre-check + trigger-translation), happy-path
uncategorized, and propagation when tx was categorized between staging and
commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(invoice-inbox): address PR #397 round-4 compliance review

Three of five round-3 findings actioned:

1. commitAttachDocumentToTransaction: surface propagation failure rather
   than logging-and-continuing. If document_attachments.journal_entry_id
   can't be set after the transaction has been categorized, the op fails
   (status 500) with a Swedish message instructing retry. Retry is
   idempotent — same document_id on the tx, same propagate target.

2. Replace check_violation (23514) matching with a stable
   "BFL_DOCUMENT_IMMUTABILITY:" message prefix. The trigger now uses default
   P0001 + tagged message; both the route handler and the executor match on
   the prefix instead of the generic SQLSTATE. Future unrelated CHECK
   constraints on transactions can no longer accidentally surface as the
   räkenskapsinformation message.

3. gnubok_list_unmatched_documents now returns invoice currency alongside
   amount so an agent can FX-normalise before comparing to
   transactions.amount. Description updated to make the requirement
   explicit. Mirrored in the UI: AttachToTransactionDialog ranks
   same-currency rows by amount distance and pushes cross-currency rows to
   the bottom of the list.

Skipped:
- Two-migration window for FK action change is acknowledged as resolved by
  the bot; deploy-atomicity is an ops concern, not code.
- Period-lock check in attach/detach: realistic compliance concern is
  already covered by the existing immutability trigger (post-categorize) and
  by the engine's period-lock enforcement (categorize itself). A dedicated
  period check on pre-categorize attach would only guard against pinning a
  doc to a tx in a closed period — defensible defense-in-depth, but no
  active BFL violation. Left for a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(invoice-inbox): address PR #397 round-5 compliance review

Three of four findings actioned. The fourth (block_document_deletion
verification) is already covered by 20240101000017_enforcement_triggers.sql
which raises when document_attachments.journal_entry_id IS NOT NULL on a
posted/reversed entry — confirmed via grep, no code change needed.

1. categorize/route.ts: propagation no longer fires-and-forgets. If
   document_attachments.journal_entry_id can't be set after the JE has been
   committed, the response now carries a document_link_warning field with a
   Swedish retry message. The JE is already committed so we can't roll back,
   but the client can no longer mistake a partial attach for a clean
   categorize.

2. Rättelse audit trail (BFL 5 kap 5 §): both the REST POST handler and the
   MCP commit executor now append a TransactionDocumentReplaced event to
   processing_history whenever a non-null document_id is overwritten, with
   previous_document_id and new_document_id in the payload. Best-effort —
   logging failure must not roll back the (compliant) attach. The previous
   doc id is also returned in the response so callers see what was displaced.

3. MCP staging preview now exposes the existing doc's identity
   (existing_document_id, existing_document_file_name) plus an explicit
   existing_document_is_rakenskapsinformation flag, so a human approver sees
   "replaces X.pdf with Y.pdf" rather than just a will_overwrite_existing
   boolean. Mirrors BFL 5 kap 5 § informed-rättelse intent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(invoice-inbox): close trigger SELECT race (PR #397 round-6)

The enforce_transactions_document_immutability trigger SELECTed
document_attachments.journal_entry_id without a row lock. A concurrent
UPDATE setting journal_entry_id on that row could commit between the
trigger's SELECT and its RAISE, letting a detach slip through against a
document that just became räkenskapsinformation.

Add FOR SHARE to the SELECT inside the trigger. A concurrent journal_entry_id
write blocks on our share lock until our transaction commits, so either we
observe the propagation and raise, or we run first and the propagation
observes our committed detach (which is fine because journal_entry_id was
still null at that point).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(invoice-inbox): bidirectional immutability + richer staging preview (PR #397 round-7)

Two of six round-6 findings actioned. The other four are recurring
architectural recommendations (atomic audit-log writes, background
reconciliation jobs, migration consolidation, anti-join via materialized
view) that are properly scoped as follow-up work.

1. document_attachments side of the immutability link (BFL 5 kap 6 § works
   in both directions). New trigger enforce_document_journal_entry_immutability
   blocks UPDATE OF journal_entry_id when going from non-null to NULL or to a
   different uuid. The original null→uuid path (initial propagation in the
   categorize / commitAttach flows) still works. Migration
   20260506130000.

2. gnubok_attach_document_to_transaction staging preview now joins on
   invoice_inbox_items.extracted_data and surfaces vendor/amount/currency/
   invoice_date alongside the existing doc filename/mime metadata. Gives the
   human approver the same hints the agent saw before choosing the
   attachment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 10:24:57 +02:00
Mattsson fa7d4075cf Supp/invoice bfl errors (#390)
* feat(accounting): update accounting method validation and messaging for aktiebolag and enskild firma

* Remove AI subsystem and related code

- Deleted AI proposals and requests persistence logic from `lib/ai/proposals/persist.ts`.
- Removed re-validation logic for proposals in `lib/ai/proposals/re-validate.ts`.
- Cleaned up schemas related to AI flows in `lib/api/schemas.ts`.
- Removed AI-related fields from bookkeeping engine in `lib/bookkeeping/engine.ts`.
- Eliminated AI event types from `lib/events/types.ts`.
- Updated tests to reflect the removal of AI-related functionality in `lib/extensions/__tests__/sectors.test.ts`.
- Adjusted initialization logic in `lib/init.ts` to exclude AI proposal handler registration.
- Cleaned up transaction ingestion logic in `lib/transactions/ingest.ts` to remove AI flow checks.
- Updated helper functions in `tests/helpers.ts` to remove AI-related settings.
- Removed AI-related types and interfaces from `types/index.ts`.
- Added migration script to drop AI-related tables and settings from the database.

* fix(migrations): ensure foreign key constraint is dropped before removing AI tables

* feat(invoice-inbox): implement deterministic invoice field extraction and inbox provisioning

- Added `extract-invoice-fields.ts` for extracting fields from PDF invoices using regex and pdfjs-dist, replacing the previous AI classifier.
- Introduced `inbox-provisioning.ts` to manage company inbox addresses and rotation of inboxes using Supabase RPCs.
- Created `resend-inbound.ts` for handling inbound email events and attachments via the Resend API.
- Defined the extension manifest for the invoice inbox, specifying required environment variables and descriptions.
- Migrated database schema to remove AI-related columns and tighten the status enum in `invoice_inbox_items`.

* feat(invoice-inbox): remove AI-specific columns and tighten status enum

* fix(skattekonto): remove manual entry creation reference from transaction input

* fix(schemas): remove accounting method validation for aktiebolag in UpdateSettingsSchema
2026-05-05 09:53:37 +02:00
Jakob WennbergandClaude Opus 4.7 23664e79cb feat: multi-series SIE import, reusable FiscalYearSelector, library templates in picker (#278)
* feat: multi-series SIE import, reusable FiscalYearSelector, library templates in picker

- SIE import preserves each voucher's source series (B/C/I/V/...), essential
  for Fortnox migrations where series carry semantic meaning (kundfakturor,
  inbetalningar, etc.). Target numbering still goes through next_voucher_number
  per series; source (series, number) is stored in the migration mapping for
  BFNAR 2013:2 audit trail.
- Execute route reads company_settings.default_voucher_series as the fallback
  for vouchers arriving without a series (SIE4I).
- Extract shared FiscalYearSelector component; adopt in /reports and
  /bookkeeping.
- Transaction TemplatePicker now surfaces user-created library templates
  (company + team scope) alongside the static registry, with a helper to
  convert simple library templates into the BookingTemplate shape.
- Exclude 8999 "Årets resultat" from income statement financial section and
  monthly breakdown so year-end closing entries don't cancel the net result.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test: skip Bokio SIE regression when fixtures are absent

/dev_docs is gitignored (contains anonymised customer exports), so the
integration test can't find its input files in CI. Gate the suite on
fixture presence so it still runs locally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: address Greptile review feedback

- convertLibraryToBookingTemplate: default entity_applicability to 'all'
  when the source template has no entity_type, so TemplatePicker doesn't
  silently hide it for companies with a set entity type.
- FiscalYearSelector: fire onReady in the no-company early-return branch
  so consumers (e.g. ReportsPage) don't get stuck in a loading skeleton
  while the company context is still hydrating.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 11:40:15 +02:00
Jakob WennbergandClaude Opus 4.6 9dceb6285c fix: SIE import voucher range bugs + remove describe transaction (#161)
* fix: fix SIE import voucher range bugs from multi-tenant migration and remove describe transaction feature

- Fix reserve_voucher_range RPC parameter mismatch (p_user_id → p_company_id) causing duplicate voucher errors
- Fix create-accounts onConflict from user_id to company_id
- Add reserve-then-adjust pattern: pre-reserve voucher range before batch insert, release unused range on partial failure
- Add release_voucher_range DB function for safe rollback
- Remove DescribeTransactionDialog, describe/batch-describe API routes, and related schemas (feature superseded)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — voucher range race condition and orphaned entries

- Add p_reserved_highest upper-bound guard to release_voucher_range to prevent
  rolling back past numbers claimed by concurrent operations
- Move highestInsertedVoucher tracking to after both headers AND lines succeed,
  preventing orphaned journal entries with no lines from being counted as "used"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 10:48:13 +02:00
Jakob WennbergandClaude Opus 4.6 d0b3f21bde feat: remove AI extensions, restructure settings, and add atomic voucher commits (#157)
Remove AI-dependent extensions (ai-chat, ai-categorization, receipt-ocr,
invoice-inbox) and their infrastructure (lib/ai/*, ai-consent, LangChain/
Anthropic/OpenAI deps) to simplify core and reduce bundle size.

Restructure monolithic settings page into dedicated sub-pages (company,
bookkeeping, invoicing, tax, banking, api, account, team, templates) with
shared layout and sidebar navigation.

Add atomic commit_journal_entry RPC so voucher number increment and status
update happen in a single transaction — prevents burned numbers on constraint
failures. Add continuity check report and voucher gap explanation tracking.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:08:00 +02:00
3050743013 Last feedback fixes (#116)
* Transaction updates. Email updates. Bookkeeping updates.

* Added email confirmation box

* Update components/invoices/SendInvoiceDialog.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update app/(dashboard)/invoices/[id]/page.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: restore deleted useEffect and remove stray JSX closing tag

The SendInvoiceDialog useEffect body was accidentally removed in e32c9b3,
leaving only the dependency array. Also removes a stray `/>` in the
invoice detail page that broke the Turbopack build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:31:34 +01:00
Jakob WennbergandClaude Opus 4.6 4d4a9a40c9 feat: AR/AP navigation separation and bookkeeping improvements (#109)
* feat: separate AR/AP/accounting into distinct nav groups (#92)

Split the flat "Finans" sidebar group into three visually distinct
sections — Försäljning (AR), Inköp (AP), and Redovisning — so users
coming from Fortnox immediately find customer invoicing and supplier
invoices as top-level concepts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: journal entry detail view, correction chain, and account name display

- Add journal entry detail page at /bookkeeping/[id] with full entry view
- Add correction chain API and component showing storno relationships
- Add JournalEntryStatusBadge component for entry status display
- Show debit/credit account names in template picker and review dialogs
- Expand client-side BAS account name mapping with additional accounts
- Show account codes on transaction inbox suggestion buttons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review feedback — N+1 query, duplicate name, nav dedup

- Batch reverse-lookup into single query per BFS iteration (was N+1)
- Differentiate account 2393 from 2893 in display names
- Extract shared loop for desktop/mobile nav group rendering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 20:01:07 +01:00
Jakob WennbergandClaude Opus 4.6 a7be1aab17 feat: comprehensive UI design audit and normalization (#28)
* 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>

* feat: comprehensive UI design audit and normalization

Dashboard audit:
- Fix muted-foreground contrast (4.31:1 → 5.08:1) for WCAG AA
- Add prefers-reduced-motion media query for all animations
- Replace border-l-2 accent anti-pattern with subtle full-border colors
- Add aria-expanded to toggle buttons, role="status" to live counters
- Fix touch targets on deadline buttons (28px → 36px)
- Vary section spacing for rhythm (mb-12/mb-10/mb-8)
- Remove unused imports and dead code

Transactions audit + hardening:
- Add pagination (200 per page) with "Ladda fler" button
- Replace height animation with transform-only exit animation
- Show batch progress in floating action bar during processing
- Fix batch bar mobile overlap (bottom-20 on mobile)
- Replace clickable badges with proper button elements
- Add safe area padding to fullscreen swipe view
- Add response.ok check to suggestion fetch
- Add truncation to invoice number buttons

Invoicing audit:
- Remove border-l-4 accent pattern from invoice cards
- Replace string concatenation with cn() utility

Systemic sweep (34 files):
- All page headings: font-bold → font-display font-medium (Fraunces)
- All stat numbers: font-bold → font-display font-medium tabular-nums
- All hard-coded blue/amber/emerald colors → design tokens
- Remove all dark mode overrides (tokens handle automatically)
- Tint pure white card background to 99%

Design context added to CLAUDE.md with brand personality,
aesthetic direction, and 5 design principles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: bookkeeping flow audit — design system, accessibility, UX

- Replace raw <select> with shadcn Select component (JournalEntryForm)
- Add confirmation dialog for account deletion (ChartOfAccountsManager)
- Remove console.error from production code (JournalEntryList, JournalEntryForm)
- Fix contradictory h-7/min-h-[44px] button sizing → h-10 (ChartOfAccountsManager)
- Increase BAS catalog "Lägg till" touch target h-7 → h-9
- Improve loading state with spinner (JournalEntryList)
- Improve empty state with icon, description, and guidance (JournalEntryList)
- Add response.ok check on journal entry fetch
- Add aria-expanded to entry expand buttons
- Add tabular-nums to desktop debit/credit columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: onboarding and empty state improvements

Onboarding:
- Replace font-serif with font-display (Fraunces) for brand consistency
- Remove console.error calls from production code

Empty states:
- Fix broken /transactions/new link in EmptyTransactions (route doesn't exist)
- Add actionHref fallback to EmptyCustomers when no onAction prop provided
- Improve EmptyTransactions description copy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clarify Swedish UX copy — terminology, errors, descriptions

Terminology consistency:
- "Försenad" → "Förfallen" for overdue invoices (customers/[id])
- "bokföringsorder" → actionable description in bookkeeping page
- "verifikation har bifogats" → "underlag har bifogats" in doc warning
- "Fortsätt ändå" → "Bokför utan underlag" (specific action)

Error messages — replace generic "Fel" + "Något gick fel" with specific:
- "Något gick fel vid bokföring" → "Transaktionen kunde inte bokföras"
- "Något gick fel vid matchning" → "Transaktionen kunde inte matchas"
- "Kunde inte hämta X" → "Kunde inte ladda X" + recovery hint
- Add "Försök igen" guidance to all error toasts

Page descriptions — replace redundant with actionable:
- Invoices: "Skapa och hantera" → "Skicka, följ betalningar, skapa kreditnotor"
- Bookkeeping: list of features → actionable description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: design critique — dashboard affordance, reports description

Dashboard:
- Add ChevronRight indicator to clickable summary cards
  (Att få betalt, Koppla bank) to distinguish from static cards
- Add cursor-pointer to linked cards

Reports:
- Replace feature list description with actionable guidance
  "Huvudbok, grundbok..." → "Generera skattedeklarationer..."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace generic "Fel" error toasts with specific messages

Deadlines: 5 generic "Fel" → specific per-action titles
  (create, toggle, edit, delete, load)
Expenses detail: 5 generic "Fel" → specific per-action titles
  (load, approve, pay, credit, delete)
Expenses new: 3 generic "Fel" → instructional validation messages
  (supplier name, supplier selection, invoice number)
Customers: 1 generic "Fel" → specific load error with recovery hint

All error toasts now follow pattern:
  title = what failed, description = how to recover

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace all remaining generic "Fel" error toasts (37 instances)

Systematic sweep across 12 dashboard pages replacing generic
title: 'Fel' with context-specific error titles:

- Load errors: "Kunde inte ladda [resurs]"
- Action errors: "[Åtgärd] misslyckades"
- Validation: "[Fält] saknas"

Every error toast now tells the user what failed without needing
to read the description. Recovery hints added where missing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: import flow — normalize stat typography, remove console.warn

- Replace font-bold with font-display font-medium on 13 stat numbers
  across SIEPreviewStep, BankFilePreviewStep, BankFileConfirmStep,
  ImportResultStep (missed by systemic sweep since these are in
  components/import/, not app/(dashboard)/)
- Add tabular-nums to stat numbers displaying counts/currency
- Remove console.warn in ArcimMigrationWorkspace

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: final cleanup — console statements, remaining font-bold stats

Remove production console statements:
- Step1EntityType: remove debug console.warn (dead code after onNext)
- TransactionBookingDialog: remove console.error on doc link failure
- JournalEntryAttachments: remove 3 console.error calls

Normalize remaining font-bold stat displays:
- SwipeCategorizationView: 3 instances (completion, amount displays)
- NEDeclarationView: yearly result heading + value

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review feedback

loadMoreTransactions: add inbox item enrichment matching fetchTransactions
- Paginated transactions now fetch invoice_inbox_items in parallel
- Fixes missing document indicator, template suggestions, and inbox
  match card for transactions loaded via "Ladda fler"

fetchAllPages: add maxPages guard (default 500) to prevent infinite loop
- If Arcim gateway returns hasMore:true indefinitely, the loop now
  exits after 500 pages instead of running forever

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 14:48:40 +01:00
Jakob WennbergandClaude Opus 4.6 66a4027f1e feat: BAS data overhaul, currency revaluation, expenses, UI polish, and cleanup
- Update BAS account catalog with comprehensive SRU codes and K2 flags
- Add currency revaluation service with tests and API route
- Add expenses page and account deletion API
- Enhance booking templates with new patterns and improved tests
- Improve transaction categorization with template picker and description matching
- Polish dashboard, onboarding, import, and transaction UIs
- Refactor year-end service for multi-step closing
- Move SRU generator to ne-bilaga, remove standalone SRU export
- Remove unused dev docs, mock data, and extension hooks
- Add invoice delivery note sequences migration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:19:56 +01:00
Jakob WennbergandClaude Opus 4.6 a9bf24ce0b feat: enhance supplier invoices, entity-aware categorization, and new tests
Expand supplier invoice module with overdue cron job, credit note journal
entries, and event emissions on approve/mark-paid/create flows. Add entity
type (EF/AB) awareness to transaction categorization UI and category
mapping logic. Add comprehensive tests for supplier-invoice-entries,
transaction-entries, and expanded API route coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 17:08:26 +01:00
Jakob WennbergandClaude Opus 4.6 03b569d708 refactor: consolidate extension system to general-only with manifest-driven architecture
- 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>
2026-02-26 14:32:56 +01:00
Jakob WennbergandClaude Opus 4.6 39e407644d feat: unified document inbox, full BAS 2026, and document-transaction matching
- Expand BAS reference from ~180 to ~1,276 accounts (full BAS Kontoplan 2026)
  with K2 exclusion flags, per-class data files, and computed SRU codes
- Evolve invoice inbox into unified document inbox handling invoices, receipts,
  and government letters with AI-powered classification (Claude Haiku Vision)
- Add multi-pass document-to-transaction matching engine with greedy assignment
  for both supplier invoices (reference/amount/date/name) and receipts
  (weighted amount/merchant/date scoring)
- Add supplier invoice matching in transaction ingest pipeline
- Inject booking template suggestions into AI extraction prompts
- Surface matched documents in swipe categorization UI with one-tap booking
- Auto-activate missing BAS accounts during SIE import against full reference
- Add K2 filter toggle in Chart of Accounts manager
- Add receipt confirmation route with BFNAR representation fields
- Add database migrations for K2 support and document matching columns
- Remove obsolete extension migration scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:59:02 +01:00
Jakob WennbergandClaude Opus 4.6 3e7fa45ed6 feat: transaction categorization UX improvements and description matching
Add journal entry preview, human-readable account names, auto-apply VAT,
fallback template suggestions, example prompts, invoice match comparison,
and batch result feedback. Also includes user-description-match extension,
describe/batch-describe API routes, improved AI categorization with multi-
suggestion support, and template embedding search.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 16:02:06 +01:00
Jakob WennbergandClaude Opus 4.6 bbb82866ee feat: add 4 new Swedish bank CSV parsers and improve transaction categorization
Add auto-detecting CSV parsers for Länsförsäkringar, ICA Banken, Skandia,
and Lunar. Refine SEB detection to avoid false matches. Update bank file
upload UI with new bank options and export instructions. Include booking
templates, improved AI categorization, and transaction review enhancements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:38:44 +01:00
Jakob WennbergandClaude Opus 4.6 acb85edf4a feat: wire Zod validation into API routes, improve types and components
- Add 8 new Zod schemas (UpdateCustomer, UpdateSupplier, UpdateSupplierInvoice,
  UpdateAccount, BankUnlink, RunReconciliation, CorrectJournalEntry,
  EvaluateMappingRules) and wire validateBody() into 24 JSON-body API routes
- Remove redundant manual validation checks replaced by Zod
- Add comprehensive schema tests (222 tests)
- Improve type definitions in types/index.ts with expanded interfaces
- Refactor extension types (push-notifications, receipt-ocr) for cleaner imports
- Update transaction components (BatchCategorySelector, SwipeCategorizationView,
  QuickReviewDialog, VatTreatmentSelect) and invoice inbox workspace
- Add invoice-inbox utilities and type decoupling tests
- Fix NE-bilaga, SRU export, and invoice PDF template type usage
- Update CLAUDE.md with expanded architecture documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 16:00:38 +01:00
EmilandClaude Opus 4.6 44231d0000 feat: add transaction booking dialog with manual journal entry
Replace CategoryExpandedDialog with TransactionBookingDialog that embeds the
JournalEntryForm for full manual booking. Add /api/transactions/[id]/book
endpoint. Add VAT treatment selector to CategoryExpandedDialog. Add booking
buttons to TransactionHistoryList and improve TransactionInboxCard actions.
Fix animation timing in transactions page state updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:59:47 +01:00
Jakob Wennberg 59d935f2cc Invoice fix and new inbound invoice extension etc 2026-02-21 17:41:14 +01:00
Jakob WennbergandClaude Opus 4.6 91e2c1705a feat: per-line VAT, invoice document types, ledger-based VAT declaration, bank reconciliation, and pagination
Per-line VAT rates:
- Add generatePerRateLines() to group invoice items by vat_rate with separate
  revenue + VAT lines per rate group (invoice-entries.ts)
- Add getAvailableVatRates() and getVatTreatmentForRate() (vat-rules.ts)
- PDF template shows per-line VAT column and per-rate totals for mixed-rate invoices
- Invoice create/review UI supports per-line rate selection
- Types: add vat_rate/vat_amount to InvoiceItem, vat_rate to CreateInvoiceItemInput

Invoice document types (proforma, delivery note):
- Add InvoiceDocumentType, document_type and converted_from_id to Invoice type
- PDF hides prices for delivery notes, adds proforma notice
- Email templates support all document types
- mark-paid skips journal entries for non-invoice document types
- Migration 031: invoice_document_type

Accounting method support:
- Add AccountingMethod type (accrual/cash)
- Migration 032: add_accounting_method column to company_settings

VAT declaration rewrite:
- Rewrite to read directly from general ledger (26xx/3xxx account lines)
  instead of aggregating invoices/transactions/receipts
- ACCOUNT_RUTA mapping drives momsdeklaration boxes from GL balances

Bank reconciliation:
- Transaction ingest now pre-fetches unlinked GL lines and attempts
  auto-reconciliation during import
- Add transaction.reconciled event type
- Add ReconciliationMethod type and reconciliation_method on Transaction
- Migration 030: bank_reconciliation
- New reconciliation engine, API routes, and BankReconciliationView component

Pagination (fetchAllRows):
- New lib/supabase/fetch-all.ts overcomes PostgREST 1000-row limit
- Adopted in all report generators, SIE/SRU export, account list APIs

Fiscal period validation:
- New validate-period-duration.ts enforces max 18 months per BFL 3 kap.
- Applied in period-service.ts and fiscal-periods API

Account mapper simplification:
- Remove Levenshtein/fuzzy matching, use exact account number match only

Swedbank parser improvements:
- Support abbreviated headers (Clnr, Bokfdag, Radnr)
- Use Referens column as counterparty

Chart of accounts management:
- Add DELETE endpoint with system account and usage protection
- PUT uses partial updates
- New AccountCombobox, AddAccountDialog, EditAccountDialog, ChartOfAccountsManager

Tax deadline corrections:
- Rewrite inkomstdeklaration_ab using Skatteverket lookup table
- Rewrite arsredovisning deadline to 7 months after FY end per ÅRL 8:3

Onboarding first fiscal year:
- Add first fiscal year toggle with date pickers and 18-month validation

UI terminology:
- Change "okategoriserad/kategorisera" to "obokförd/bokföra" throughout

Report column fix:
- Fix start_date/end_date to period_start/period_end in report queries

Supplier invoice input:
- CreateSupplierInvoiceItemInput uses amount field (legacy quantity/unit_price kept)

Misc:
- SIE import uses upsert for idempotent account creation
- account-descriptions.ts falls back to BAS reference data
- Add invoice_default_notes to CompanySettings
- Update CLAUDE.md to reflect current project state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:57:15 +01:00
Jakob Wennberg e8743e6e03 New base 2026-02-19 14:32:54 +01:00
Jakob WennbergandClaude Opus 4.6 0aecf5b41c Refactor to generic ERP base
Remove influencer-specific features (campaigns, TikTok, gifts, shadow ledger,
contracts, briefings) and consolidate into a clean ERP foundation with core
bookkeeping, invoicing, receipts, tax reporting, and calendar functionality.
Reorganize database migrations into a clean numbered sequence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:39:13 +01:00
Jakob Wennberg a25d10a528 Initial copy from influencer-biz 2026-02-13 14:00:46 +01:00