* feat: add user locale preference to user_preferences table
- Introduced a new column 'locale' in the user_preferences table to store per-user UI language preferences.
- Added a CHECK constraint to ensure only supported locales ('sv', 'en') are allowed.
- Triggered a schema reload notification for the changes.
chore: declare CSS module support in TypeScript
- Added a declaration for CSS modules in globals.d.ts to enable TypeScript support for importing CSS files.
* feat: add Swish as an invoice payment method in company settings
* chore: post-bankid redirect, recapt feedback, TIC SPAR enrichment
- BankID login + register now redirect to /select-company so the picker
shows freshly enriched CompanyRoles from the current session.
- New lib/support/submit-feedback util prefers window.recapt feedback
widget when present, falls back to /api/support/contact. SupportLink
uses it and hides itself in sandbox companies via new isSandbox flag
on CompanyContext (+ useCompanyOptional hook).
- TIC enrichment re-requests SPAR alongside CompanyRoles now that both
types are enabled on the tenant; enrichment shape logged PII-free
(booleans/counts only). Tests cover the SPAR+CompanyRoles path.
- Skatteverket api-client: 15s AbortSignal timeout on outbound requests.
- Swedish compliance review CI: bump REVIEW_MODEL to claude-opus-4-7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: keep compliance review model on sonnet-4-6
Reverts the opus-4-7 bump from the previous commit per request.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tic): don't persist SPAR PII to extension_data
The previous commit started requesting SPAR alongside CompanyRoles and
wrote the full enrichment payload (incl. personnummer, full name, home
address, birth date, gender) verbatim to extension_data.value — a plain
JSON column. Personnummer is already hashed + encrypted in
bankid_identities, so the extension_data row was an unencrypted PII
duplicate exposed to anyone with read access to the table.
No consumer (middleware, /select-company, createCompanyFromTicRole)
reads any SPAR field today; they only read companyRoles. Persist a
sanitized blob of { companyRoles, enrichedAtUtc } instead. SPAR is
still requested from TIC (and its shape logged PII-free) so enrichment
completes; if address pre-fill ships later, those fields should be
encrypted before storage.
Also drop the dead `null` branch from SubmitFeedbackResult.channel —
every code path returns 'recapt' or 'email'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: implement salary module with personnummer encryption, salary entries, tax tables, and AGI tracking
- Added personnummer encryption and decryption functions for secure storage.
- Created salary entries handling for journal entries including gross salary, tax withholding, and employer contributions.
- Implemented tax table lookup functionality for calculating tax amounts based on monthly income.
- Developed SQL migration for salary module including tables for payroll configuration, tax rates, employees, salary runs, salary run employees, salary line items, and AGI declarations.
- Established row-level security policies for all new tables to ensure company-scoped access.
* feat: add salary calculation modules for 2026
- Implemented engångsskatt calculation for one-time payments with tax brackets.
- Added löneväxling functionality for salary sacrifice to pension, including employer savings and warnings.
- Created pain.001 generator for salary batch payments in compliance with Swedish banking standards.
- Developed PDF template for payslips, including detailed breakdowns and employer costs.
- Generated seed data for Swedish tax tables for 2026, including SQL insert statements.
- Implemented traktamente calculations for per diem and mileage allowances, adhering to Skatteverket regulations.
- Added seed script for populating tax tables in the database.
* feat: Update meal reduction percentages in traktamente calculation
fix: Remove obsolete seed script for 2026 tax tables
feat: Extend SalaryRunStatus type to include 'corrected' status
feat: Implement KU10 XML generation endpoint for annual employee income statements
feat: Add endpoint for creating corrections to booked salary runs
feat: Implement endpoint for sending payslip PDFs to employees
feat: Create KU10 XML generator for annual reporting
feat: Add salary transaction matcher for auto-linking bank transactions to salary entries
chore: Add database migration for salary correction support
* feat: replace select elements with custom Select component for employment and salary types
* feat: enhance salary calculations with pension entry and avgifter category support
* feat: event log, pending operations, and MCP staging
- Event log system: persist bus events to event_log table for external
automation platforms. Batch insert for transaction.synced. Daily
cleanup cron at 02:00 UTC.
- Pending operations: MCP write tools (categorize, create customer,
create invoice) now stage to pending_operations instead of executing
directly. Users review and commit/reject from /pending in the web UI.
- Granskning page: card-based review UI with expandable previews,
commit/reject dialogs. Only shown in nav when pending ops exist.
- Commit route re-executes using core lib functions (no extension
imports). Guards against stale state (double-commit, deleted entities).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: stage new MCP write tools after main merge
Add staging for 4 new write tools from #133:
- mark_invoice_paid, send_invoice, mark_invoice_sent,
match_transaction_invoice
- Expand pending_operations CHECK constraint
- Add commit executors with full execution logic
- Add UI labels and generic preview component
- Remove confirm parameter from categorize (single-call staging)
- Fix UUID in pending op title (fetch transaction description)
- Hide Granskning nav when no pending ops
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review feedback
- Fix TS build error: use `select('*, customer:customers(*)')` for
match_transaction_invoice to avoid array type inference
- Add status guard to commitSendInvoice (prevents duplicate sends)
- Replace auth.admin.getUserById with user email from session auth
- Restore optimistic lock check in commitMatchTransactionInvoice
- Fix tool description typo: expense_software → expense_office
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add support contact links and improve SIE import UX
Add a SupportLink component with a contact dialog throughout the app
(nav, help page, settings, MFA, error pages, empty states). Improve
SIE import flow with phased loading states, structured skip breakdowns,
and an elapsed-time counter. Fix MFA enroll stale factor cleanup and
URL encoding for settings return path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review — open redirect, XSS, test cleanup, fallback email
- Validate returnTo is a relative path in MFA enroll (prevents open redirect)
- Add afterEach import to event-log-handler tests (fixes handler leak)
- HTML-escape user-supplied subject and message in support email body
- Replace hardcoded personal email with support@gnubok.se fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>