Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.
Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.
Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.
Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* 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: 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>
* fix(reminder-processor): filter out credited invoices in overdue reminders
* feat: implement linking of transactions to journal entries
- Added POST endpoint for linking a bank transaction to an existing journal entry without creating new bookkeeping.
- Implemented validation for required fields and error handling for various scenarios (e.g., missing journal_entry_id, transaction already linked, journal entry not found).
- Created tests for the new endpoint to cover various cases including successful linking, error responses, and invoice handling.
- Introduced duplicate payment detection logic to prevent double-booking of bank receipts.
- Added a new component for correction affordance in the UI to facilitate user corrections on journal entries.
* feat(invoice-matching): enhance force matching with expected journal entry validation
* feat: add bike benefit handling and optional vacation accrual
- Introduced bike benefit (cykelförmån) with calculations for annual market value and monthly taxable value.
- Updated schemas to include new benefit types and validation rules.
- Implemented API routes for creating, updating, and deleting employee benefits.
- Enhanced salary calculation logic to accommodate new vacation rule options, including a 'none' option for no accrual.
- Added UI components for managing employee benefits, including input for bike benefit specifics.
- Created database migrations for employee benefits and updated salary line items to support new benefit types.
* chore: remove Langfuse env var checks
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: enhance OAuth callback URL handling and update default scopes for Visma integration
* feat: remove trade_name field and simplify company naming in invoices
* refactor: destructure canWrite from useCanWrite for consistency across components
* feat: enhance PATCH endpoint to validate existing benefits and handle bike benefit updates
* feat: add missing label for bike benefit in salary line item types
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add delete policies for provider consent tokens and provider OTC
* Add trade name support for companies in settings and documents
* Resolved currency selection issue
* Enhance invoice line display with foreign currency support and update delivery date schema to allow empty values
* Add currency display for journal entries and include currency metadata in transaction creation
* Add trade_name column to company_settings for external display
* feat: multi-tenant company refactor (GNU-19)
Introduce companies table, company_members, and user_preferences to
support multiple companies per user. All data scoping changes from
user_id to company_id across the entire codebase.
Key changes:
- Database migration: new tables, company_id on 40+ tables, backfill,
RLS rewrite from user_id to company-member-based, updated RPCs
- Types: Company, CompanyMember, CompanyRole, UserPreferences types;
company_id added to all entity interfaces; companyId on all events
- Engine: all 7 core functions take companyId; storno, period, year-end
services updated; 16 report generators updated
- Middleware: company context resolution (cookie → prefs → first company)
- API routes: ~120 routes updated with requireCompanyId()
- Frontend: CompanyProvider context, layout/dashboard/onboarding updated
- Extensions: context factory, 9 extensions, all lib files updated
- Tests: 1880 tests passing, all helpers updated with company_id defaults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add database migrations for multi-tenant company and team system (GNU-19)
Adds company_invitations, company creation RPC, team_members, account
deletion RPC, and teams table refactor migrations. Updates base
multi-tenant migration with cascading FKs and onboarding_step column.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add team types and update core infrastructure for multi-tenancy (GNU-19)
Adds TeamRole, MemberSource, and Team types. Refactors Supabase service
client to be stateless, updates middleware for team-aware routing, extends
CompanyContext with team/role fields, and updates extension service types
to accept companyId.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: thread company_id through business logic functions (GNU-19)
Replaces user_id scoping with company_id across all lib modules:
bookkeeping, documents, transactions, invoices, reconciliation, tax,
deadlines, and import. Updates corresponding tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: thread company_id through API routes and extensions (GNU-19)
Updates all existing API routes to extract and pass companyId. Updates
enable-banking and arcim-migration extensions for company-scoped
transaction ingestion and sync.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add company and team management API routes (GNU-19)
Adds CRUD endpoints for company members, company invitations, team
members, and team invitations. Includes invite token utilities, email
templates, and company switch server action.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add team/company UI components, pages, and dashboard updates (GNU-19)
Adds CompanySwitcher, ConsultantEmptyState, Step0RoleChoice, company
members and team management panels. Updates dashboard layout for
team-aware routing, onboarding for multi-step role choice, and auth
callback for team invite acceptance. Ignores supabase/.branches/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add null guards for company in import page (GNU-19)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: move appUrl declaration to outer scope in invite route (GNU-19)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add optional chaining for company.name in members section (GNU-19)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add optional chaining for second company.name in members section (GNU-19)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add null guards for company in extension components (GNU-19)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: pass companyId to executeSIEImport in arcim-migration extension (GNU-19)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update tests to use companyId instead of userId and improve type handling
---------
Co-authored-by: Claude Opus 4.6 (1M context) <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>
- Fix buildLineInserts() using wrong DB column names (cost_center_id → cost_center,
project_id → project) and add missing tax_code field
- Replace transaction-based dashboard income/expense with journal-entry-based
calculation using account classes (3xxx revenue, 4-7xxx expenses)
- Add email/phone fields to CompanySettings type, validate RESEND_FROM_EMAIL
in isResendConfigured(), remove unsafe type casts in invoice send/reminders
- Always auto-fill line description from account name in JournalEntryForm
- Reorder expense lines in TransactionBookingDialog so 1930 is always first row
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>