* feat: add option to exclude year-end closing entries in SIE export and related reports
* delete docs
* fix: allow Chrome's PDF viewer in verifikat document preview
The /api/documents/:id/inline route shipped with
`object-src 'none'` in its CSP, which blocked Chrome's built-in PDF
viewer (it renders inline PDFs via an internal <embed>). Users on
Chrome saw "Det här innehållet har blockerats" when expanding a PDF
attachment in the bookkeeping view; Firefox (PDF.js) and Edge (own
viewer) were unaffected, and JPGs worked because <img> isn't subject
to object-src.
Drops the CSP for this route to the minimum needed for embeddability:
`frame-ancestors 'self'`. X-Content-Type-Options: nosniff plus the
fixed Content-Type from the handler already block MIME confusion;
X-Frame-Options: SAMEORIGIN + frame-ancestors still block clickjacking.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(auth): add webmail deep link to email confirmation screens
Mirrors Stripe's signup UX: after asking the user to verify their email,
detect their webmail provider from the domain and show a button that
opens the inbox in a new tab. Gmail gets a from:<sender> search
pre-populated; Outlook/Yahoo/iCloud/Proton open the inbox directly.
Unknown / custom domains fall back to the existing copy.
Sender address is configurable via NEXT_PUBLIC_BRANDING_AUTH_EMAIL_FROM
(default noreply@gnubok.se) so white-label installs can match their
Supabase Auth SMTP config.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(auth): unblock first-time password set for BankID users with MFA
Supabase rejects updateUser({password}) and mfa.unenroll with "AAL2 session
is required" whenever a TOTP factor is enrolled. BankID magic-link logins
produce AAL1, and middleware skips MFA enforcement for bankid_linked users,
so they had no path to AAL2 — leaving them unable to set a backup password
or disable MFA without going through the email-recovery escape hatch.
- /api/account/password: branch on app_metadata.has_password. First-time set
writes via service.auth.admin.updateUserById (no existing credential to
protect, AAL2 guard does not apply). Change-password keeps the user-session
updateUser so AAL2 still fires for credential rotation.
- /mfa/verify: accept a safeReturnTo query param and route there after
successful verify, so step-up flows can land back where they came from.
- SecuritySettings: detect the AAL2 error from both change-password and
mfa.unenroll and redirect through /mfa/verify?returnTo=/settings/account
instead of toasting a dead-end error.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add tests and rounding utility for öre precision in bokslut calculations
- Implemented `roundOre` function for rounding SEK amounts to two decimal places, ensuring consistent monetary calculations.
- Introduced `ORE_TOLERANCE` constant for comparing rounded amounts, facilitating invariant checks in financial entries.
- Created comprehensive tests for `roundOre`, covering typical cases, edge cases, and idempotency.
- Added year-end invariants tests to verify database-level guarantees for closing entries, ensuring they balance to the öre and reject discrepancies.
- Developed end-to-end tests for the dispositions chain, validating the correctness of calculations across various scenarios.
* fix: update PDF rendering to remove Swish QR code generation and set default to disable Swish visibility
* fix: enhance security by rejecting data URIs in safeReturnTo function tests
* fix: improve rounding logic in roundOre function and add customer_type migration
* fix: add customer_type column to customers and enforce CHECK constraint
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 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
* feat: invoicing & account-security polish bundle
Five independent improvements bundled to ship together:
- BankID/password lockout fix: BankID-only users could enroll MFA and
brick themselves (Supabase requires AAL2 to change password or unenroll
MFA, and AAL2 needs a password sign-in). New app_metadata.has_password
flag tracks this; middleware gates /mfa/enroll behind it, /account/set-
password is the unlock path, SecuritySettings shows a banner, and
/api/account/password is the single write path that flips the flag.
Backfill script for existing users.
- Swish invoice payment method: company_settings.swish + invoice_show_swish
columns, validation in lib/api/schemas.ts (accepts 123XXXXXXX företag or
07XXXXXXXX mobile, strips whitespace/hyphens), rendered on invoice PDFs.
- Send-reminders kill switch: per-company company_settings.send_invoice_
reminders toggle in PdfPrintSettings/Automatisering. Reminder processor
also tightened: positive status allowlist (sent + overdue) so terminal
statuses can never match; skip when customer already responded via
reminder link; race-window re-check before send.
- First-invoice logo prompt: one-shot dialog when creating the first
invoice without a logo (issue #520). Self-limits via head-only count.
- SIE export opening-balance fallback: route IB through getOpeningBalances
so the compute_prior_opening_balances RPC supplies #IB after multi-year
imports where opening_balance_entry_id is intentionally NULL. Previously
#IB silently went to zero and #UB collapsed to current-period movements.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(account-polish): address PR review feedback
- BankID-link path (extensions/general/tic/index.ts): read-merge-write
app_metadata instead of passing { bankid_linked: true } alone.
updateUserById REPLACES app_metadata wholesale, so the previous code
would have wiped has_password for any user who later linked BankID,
causing the set-password banner to (incorrectly) reappear and blocking
the standard MFA enrollment button. The comment is now corrected.
- Middleware (lib/supabase/middleware.ts): thread inner returnTo through
the /mfa/enroll → /account/set-password redirect so the user lands on
their original destination after the full chain completes, not on /.
- safeReturnTo helper (lib/auth/safe-return-to.ts): replace the
starts-with-/-but-not-// guard on mfa/enroll and set-password pages.
The previous guard let /\evil.com and /@evil.com through. The new
helper parses against a synthetic base origin and verifies it matches.
- set-password page (app/(auth)/account/set-password/page.tsx): remove
CLAUDE.md design system violations — bg-gradient-to-b on page bg,
inline shadow-md style on the card, space-y-5, font-medium on the h1,
rounded-xl on the card. Flat surface, hairline border, font-display
h1 per the design tokens.
- Swish dedup (lib/payments/swish.ts): extract normaliseSwish() and
isValidSwish() helpers and use them in lib/api/schemas.ts,
components/settings/BankDetailsForm.tsx, and the invoicing settings
page. Single source of truth for the regex.
- Password route (app/api/account/password/route.ts): emit a structured
success log so the audit pipeline can detect password-set events, not
just failures.
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: add BankID authentication via TIC Identity API
Integrate BankID as a login/signup method using the TIC Identity API.
Users can authenticate with BankID QR codes (desktop) or deep links (mobile),
link BankID to existing accounts, and skip TOTP MFA when BankID is linked.
Removes Step 0 (role choice) from onboarding for all users. Adds enrichment
data support for pre-filling company details from Bolagsverket during signup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review — server-side rate limit, unlink clears MFA bypass
- Add per-IP rate limit (5s cooldown) on /bankid/start to prevent
unbounded billable TIC sessions from unauthenticated callers
- Add /bankid/unlink endpoint that deletes bankid_identities AND clears
app_metadata.bankid_linked so MFA enforcement resumes after unlink
- Update BankIdSettings to call server-side unlink instead of client-side delete
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: move rate limiter to module scope, add BankID logo and year-end skill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
* 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>
- Replace magic-link-only login with email+password (primary) and magic link (toggle)
- Add registration page with strong password validation
- Add MFA enrollment (/mfa/enroll) with QR code and manual secret
- Add MFA verification (/mfa/verify) with 6-digit TOTP input
- Add password reset flow (/reset-password)
- Add middleware MFA enforcement gated by NEXT_PUBLIC_REQUIRE_MFA env var
- Self-hosted deployments (NEXT_PUBLIC_SELF_HOSTED=true) skip MFA entirely
- Add Security tab in Settings for password change and MFA management
- Add requireAuth() API route helper with MFA check
- Update CLAUDE.md with Authentication section and env var docs
- Update Dockerfile and docker-entrypoint.sh for new env var placeholders
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>