* fix(import): parse the SEB Transaktioner CSV layout (split Insättningar/Uttag)
The SEB profile only understood the Kontoutdrag export layout. The
Transaktioner page (the path most users find first) exports a different
header: Bokförd;Valutadatum;Text;Typ;Insättningar;Uttag;Bokfört saldo,
with dot decimals and the amount split across two columns. No profile
detected it, so auto-detection found nothing and an explicit SEB choice
failed on column detection.
Teach the SEB profile the layout: detect on the Insättningar/Uttag pair
(unique among supported formats), accept Bokförd as a booking-date
column, and combine the split amount (Uttag carries its own minus;
unsigned magnitudes are normalized to expenses). Fixture header and
first data row are verbatim from a user-provided export. The import
help text now lists both SEB export paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: decision log for SEB Transaktioner parser design
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(import): support Wise balance statements
* fix(import): fail closed on ambiguous Wise rows
* fix(import): guard Wise statement netted-fee assumption with running-balance continuity check
Swedish accounting review asked whether balance-statement Total fees is
netted into Amount. It is: Running Balance moves by exactly the signed
Amount per row, so a separate fee row would double-count the cost. Codify
the assumption with a pairwise continuity warning (order-agnostic, chain
resets across skipped rows) and document the decision.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(api): cap bank-import validation payload and harden issue assertion
CodeRabbit review: bound the VALIDATION_ERROR issues array to 20 entries
with issue_count carrying the full total, so a large malformed file cannot
balloon the response or log sink. Gate stays format-agnostic on purpose:
error severity means do-not-ingest for every parser, and no non-Wise parser
emits per-row errors alongside parsed transactions today.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(import): add Wise (TransferWise) CSV import format
Wise exports a single multi-currency transaction history (one row per balance
movement). Add it as a bank-file format plugin so it flows through the existing
upload -> preview -> confirm -> execute wizard.
- lib/import/bank-file/formats/wise.ts: quote-aware parse (dates contain a
space), Direction IN/OUT drives the sign, booked on the moved side (target
for IN, source for OUT). Native currency preserved; SEK conversion is left to
the downstream FX/booking pipeline (Riksbanken).
- Non-zero Wise fees become their own negative "Wise avgift" row (source and
target), so the fee books separately and the balance ties out.
- Only COMPLETED rows import. external_id keys on the stable Wise ID
(TRANSFER-/PLAN_ORDER-, -fee suffix for fee rows) via a new 'wise' branch in
generateExternalId, so re-imports dedup exactly.
- Register the format (types, parser list), add it to the manual-format picker
and the v1 /imports/bank format enum.
Tests cover detection, IN/OUT signing + currency, fee splitting, stable
external_id, and COMPLETED-only filtering.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexander Reinthal <email@reinthal.me>
* fix(import): harden Wise parser against malformed rows (CodeRabbit #1018)
- Strict amount parsing: reject "12abc"/"1,234" instead of parseFloat coercing
them to 12/1 and silently corrupting the imported amount.
- Require Status to be exactly COMPLETED: a blank/missing status no longer
slips through the completed-only filter.
- Fail hard on an unsupported Direction: a blank or non-IN/OUT value (e.g.
NEUTRAL for a balance conversion) throws instead of being guessed as income;
the parse route surfaces it as BANK_FILE_PARSE_FAILED. Proper conversion
support is tracked in #1019.
- Never invent currencies: a missing movement currency skips the row with a
warning (no SEK default), and a fee with no currency of its own is dropped
with a warning rather than inheriting the movement currency.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexander Reinthal <email@reinthal.me>
---------
Signed-off-by: Alexander Reinthal <email@reinthal.me>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jakob Wennberg <jakob.wennberg@gmail.com>
* fix(booking-templates): scope template list to the active company
GET /api/settings/booking-templates relied solely on the btl_select RLS
policy, which is membership-wide (user_company_ids) and returns templates
from every company the user belongs to. A user who owns multiple companies
saw all their templates merged regardless of which company was active.
Narrow the list in the API layer (mirroring counterparty-templates) to
system + the active company + the active company's team. RLS stays the
security backstop; this fixes the cross-company merge within a single
user's own view (it was never a cross-tenant data leak).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(import): show proper message for duplicate bank file upload
The bank file import page mis-parsed the structured error envelope
({ error: { code, message, details } }), so a BANK_FILE_DUPLICATE
(409) fell through to the generic "Kunde inte läsa filen" fallback.
The upload step also hardcoded that same string as the error heading,
so duplicates were doubly misreported as parse failures.
- Parse the structured envelope by error.code; surface error.message
for all codes instead of rendering the error object.
- Add a dedicated BANK_FILE_DUPLICATE message using the importedAt /
importedCount details the route already returns.
- Add an optional errorTitle prop to BankFileUploadStep (defaults to
the previous text) and pass "Filen är redan importerad" for dupes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(tests): add comprehensive tests for recordateEntry, inbox-linking, and external-id handling
- Implemented unit tests for recordateEntry in the bookkeeping module to validate various scenarios including date changes, non-posted entries, and fiscal period restrictions.
- Created tests for inbox-linking status in pending operations to ensure correct handling of invoice inbox items and supplier invoices, addressing historical bugs related to status updates.
- Added tests for external-id utilities to ensure consistent handling of monetary amounts and deduplication keys across different transaction sources.
- Introduced new functions in external-id.ts for stable external ID generation and normalization of imported descriptions, enhancing transaction deduplication reliability.
feat(migrations): add new database migrations for transaction handling
- Created migration to exclude storno and correction vouchers from unmatched GL lines, ensuring accurate reconciliation.
- Added a migration to preserve original bank transaction descriptions in a new immutable column, allowing for user edits while maintaining audit trails and deduplication integrity.
* feat(migrations): add function to exclude storno/correction vouchers from unmatched GL lines
* feat(transactions): enhance transaction handling with improved description normalization and preloaded original entries
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(api): implement caching and logging in health check endpoint
- Added in-memory caching for health check responses to reduce load on Postgres.
- Introduced logging for error handling in health check.
- Updated response structure to exclude error details from public responses.
feat(api): enhance OAuth consent UI and scope handling
- Improved consent UI to reflect exact requested scopes and added better user guidance.
- Updated scope handling logic to ensure least-privilege access.
- Enhanced styling for better user experience and accessibility.
chore(docker): improve security and resource management in Docker setup
- Updated Docker Compose configuration to enforce read-only file systems and resource limits.
- Added health checks and logging options for better observability.
- Introduced optional Caddy reverse proxy for TLS termination.
fix(migrations): resolve ambiguity in create_company_with_owner function
- Dropped orphaned 3-arg overload of create_company_with_owner function.
- Recreated canonical 4-arg version with cash account seeding logic.
- Ensured proper permissions for function execution in Postgres.
* feat: enhance security checks for team membership in company creation
* test: add CSP tests for OAuth authorization endpoint
* feat: enhance error handling and reporting in bank file import process
- 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>
Add LICENSE (AGPL-3.0-or-later), CONTRIBUTING.md, SECURITY.md, DCO, and NOTICE files.
Rewrite README for open-source audience with self-hosting instructions.
Redesign color palette to grayscale chrome theme across all components.
Add transaction uncategorize API route with tests.
Fix VAT account name mismatches in migration 052.
Improve import page with SIE file support and loading skeleton.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Replace PSD2 bank integration as the default with file-based bank
import (CSV/XML), which better suits Swedish sole traders and small
companies. Enable Banking is now an opt-in extension.
- Phase 1: Extract generic transaction ingestion service (ingest.ts)
with dedup, auto-categorization, and OCR-based invoice matching
- Phase 2: Bank file parser library supporting Nordea, SEB, Swedbank,
Handelsbanken CSV formats and ISO 20022 camt.053 XML
- Phase 3: Database migration adding import_source, reference columns
and bank_file_imports tracking table
- Phase 4: Import wizard UI (5-step flow) and API routes for parse/execute
- Phase 5: Move Enable Banking to extensions/enable-banking/ with
commented-out loader entry for opt-in activation
- Phase 6: 104 new tests (ingestion + all parser formats), fixing
Nordea detection overlap and camt.053 XML tag collision bugs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>