* Enhance booking template functionality and add sandbox extraction checks
* Implement Recapt integration for feedback submission and user identification
* Add Recapt identification component and bank sync status chip; update crontab entries
* Update .gitignore to ignore the entire scripts directory
* Refactor Recapt integration: add loader component, update privacy policy, and enhance bank sync status messages
* Fix .gitignore to correctly ignore the scripts directory
* refactor: update VAT handling logic for non-registered sellers and improve related comments
* chore: gate automated email flows behind 503 responses
Disables user-facing access to invoice payment reminders and salary
payslip email sending. Underlying lib code (reminder-processor,
PDF templates, notification_settings) is preserved for easy re-enable.
- Invoice reminders cron route returns 503; settings UI section removed.
- Payslip send route returns 503; original implementation kept as
_sendPayslipsImpl for future re-enable.
- Push notifications were already extension-disabled, no change needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: remove Recapt feedback widget
Strips the third-party Recapt SDK and its floating feedback bubble from
the app. The in-app contact form keeps working via the existing email
channel (/api/support/contact). Drops the Recapt entries from the CSP
and the subprocessor list in the privacy policy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: reject meaningless rättelser in correctEntry
Guard against zero-economic-effect corrections in the storno engine:
- Reject when proposed lines net to zero on every account (e.g. 1930
debit 100 / 1930 credit 100), which would erase the original posting
without representing any affärshändelse (BFL 5 kap. 5 §).
- Reject when proposed lines are an exact multiset match of the original
entry — a rättelse must actually change something.
New MeaninglessCorrectionError wired through bookkeepingErrorResponse
(HTTP 400) and the Swedish error translator.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add date-range picker to resultat- and balansrapport
Adds optional from/to date filtering to the four operational financial
reports (resultatrapport, balansrapport, income-statement, balance-sheet)
so users can view a month, quarter, or custom range inside a fiscal year
without leaving the report. Defaults to YTD; "Hela året" preserves the
prior full-period behaviour (URL-identical, cache-stable).
- trial-balance engine accepts optional fromDate/toDate, rolling prior
in-period activity into IB and clamping period activity to the window
- 12 API routes accept and validate from_date/to_date query params
- ReportDateRange chip picker persists preset per company, only renders
on the four relevant tabs
- FiscalYearSelector now emits the period object so the range picker
has bounds without an extra fetch
- PDF/XLSX filenames reflect the chosen range
- Resultatrapport drops the prior-year column when narrowed (full-year
vs partial-year would mislead)
- 11 new tests (engine + parser); all existing report tests pass
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add support for marking journal entries as "no document required"
- Introduced a new sidecar table `journal_entry_no_doc_required` to track entries that do not require separate documentation (e.g., bank fees, interest).
- Implemented API routes for creating and deleting exemptions, including validation and authorization checks.
- Added a toggle component in the UI to allow users to mark entries as exempt, with an optional reason.
- Updated relevant tests to cover the new functionality, including RLS checks and cascading deletes.
- Enhanced existing schemas and types to accommodate the new `vat_amount` field for supplier invoice items.
* fix: address PR review findings on no-doc-required + VAT changes
- pg-real cascade test wraps DELETE in gnubok.allow_delete='true' txn so the
immutability trigger bypass fires (mirrors delete_last_voucher RPC).
- Clamp supplier-invoice item vat_amount to <= line_total * vat_rate via Zod
refinement (with 1-öre rounding tolerance) so the manual override can't
inflate the 2641 debit beyond the statutory ceiling.
- groupVatByRate falls back to line_total * rate when stored vat_amount is 0
with a positive rate, so legacy/import paths leaving the column at its
NOT NULL DEFAULT 0 don't silently understate ruta 48.
- ReportDateRange todayIso() and preset endpoints use local date components
instead of toISOString() (UTC) — fixes the midnight-to-02:00 off-by-one
that truncated a day from YTD / this-month / this-quarter for Swedish
users.
- NoDocRequiredToggle restores the previous reason on failed POST/DELETE so
the rolled-back toggle state stays consistent with the rendered reason.
- Document the company-scoped (not user-scoped) DELETE authorization policy
on the no-document-required route.
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 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 language preference for customers to support invoice localization
- Introduced language support for invoices, allowing customers to choose between Swedish and English.
- Updated invoice PDF generation to reflect the selected language for titles, labels, and messages.
- Enhanced email templates to generate content in the customer's preferred language.
- Added migration to include a language column in the customers table with a default value of Swedish.
- Updated tests to verify correct language usage in invoice emails and PDFs.
* fix: debounce API requests in InvoicePreviewCard and update F-skatt terminology in email templates
* 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
* fix(invoices): duplicate-payment guard on customer mark-paid + categorize
Two-pronged fix preventing duplicate verifikationer when a customer
invoice is marked paid OR a 19xx→1510 categorization is applied to an
inbound bank tx that already belongs to an open invoice.
Prong A (mark-paid): before booking, scan unlinked positive business
bank txs from the same customer within ±2% / ±60 days. If candidates
exist, return 409 INVOICE_PAID_LIKELY_DUPLICATE with per-candidate
match_reason (ocr_exact > name_amount_fuzzy > amount_only). Override
via `{ force: true }`. Applied to both legacy /api/invoices/[id]/
mark-paid and v1 /api/v1/.../invoices/[id]/mark-paid; v1 guard runs
before dry-run so previews can't mask the warning.
Prong B (categorize): when the user assigns 1930→1510 directly on a
positive business tx with a matching open customer invoice (by name
OR by OCR-normalized reference), return 409
TX_CATEGORIZE_SUGGEST_CI_MATCH routing them to /match-invoice.
Mirrors the supplier-side guard from #461. Shared helpers
(DUPLICATE_AMOUNT_TOLERANCE_PCT, escapeLikePattern) reused as-is.
New helper normalizeOcrReference() strips non-digits for Swedish OCR
equality. New shared candidate-finder
lib/invoices/duplicate-payment-candidates.ts keeps the legacy and v1
routes calling the same code.
Frontend:
- PaymentBookingDialog intercepts the 409, renders candidate list
with match_reason badges (Exakt OCR-träff / Sannolik träff /
Möjlig träff), offers "Länka transaktion" or "Bokför ändå"
(force-retry generates a fresh Idempotency-Key for v1 callers)
- transactions/page.tsx mirrors siMatchSuggestion handling as
ciMatchSuggestion with a parallel "Matcha mot kundfaktura?" dialog
v1 caveat documented in the route's pitfalls block:
INVOICE_PAID_LIKELY_DUPLICATE force-retry requires a fresh
Idempotency-Key because the original is body-hash bound; reusing it
returns 400 IDEMPOTENCY_KEY_REUSE.
Tests: 5 new mark-paid tests (legacy + v1) covering 409, force
bypass, partial-payment skip, ocr_exact match_reason, multi-candidate
ranking. 1 v1-only test verifying dry-run also surfaces the 409. 2
categorize Prong B tests (409 + confirm_no_match bypass).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(invoices): address compliance-swarm review on duplicate-payment guard
Three review-driven fixes:
1. **PostgREST .or() injection (OWASP V1.2.5).** `escapeLikePattern` neutralises
LIKE wildcards but NOT PostgREST filter-DSL chars (`,`, `.`, `(`, `)`). A
customer name like `Acme,fake.eq.true` could otherwise inject a synthetic
filter clause into the `.or('merchant_name.ilike.%X%,description.ilike.%X%')`
string. Replaced with two parameterised `.ilike()` queries dispatched in
parallel and merged by id in JS. Slight perf cost (two index hits per call),
eliminates the DSL-injection surface entirely.
2. **Date window anchored on invoice_date instead of due_date
(swedish-accounting-compliance bot).** The Prong B categorize intercept
filtered open customer invoices by `invoice_date ± 60d` relative to the
bank-tx date. For invoices with 60–90 day payment terms, the actual
payment lands well after `invoice_date`, so the legitimate match falls
outside the window and the guard silently misses it. Switched to
`due_date ± 60d` — the better proxy for "around when payment is expected."
No corresponding change for Prong A (mark-paid), which is correctly
anchored on `paymentDate` (the user-supplied or default-today date) and
scans bank-tx dates around that anchor.
3. **Force-bypass log enrichment (ISO A.8.15, OWASP V16).** Both
`duplicate-payment guard bypassed` warn entries now include `userId` and
`paymentAmount`. Attribution was previously incomplete — the bypass log
carried only `invoiceId`, which forced a join in log aggregation to
identify the acting principal.
Tests updated for the two-query pattern (legacy mark-paid suite enqueues
two transactions-table responses per guard invocation; v1 tests already
worked with the single-entry-per-table mock semantics).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(docs): redirect /docs/api and /llms-full.txt to docs.gnubok.se
Canonical docs host is now docs.gnubok.se. Every `docs_url` field on the
v1 error envelope still points at /docs/api/* on this app; the 308
permanent redirect forwards humans and agent crawlers to the docs
subdomain without us needing to mass-update structured-errors.ts.
/llms-full.txt also routes through the docs host where it's served from
the docs site's own build.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(csp): allow Supabase Storage URLs in frame-src for PDF previews
The invoice-inbox workspace renders attached PDFs in an <iframe> sourced
from a Supabase Storage signed URL. CSP frame-src was 'self' only, so
Firefox/Chrome blocked the embed with "Det här innehållet har blockerats"
even though extraction worked and the inbox row landed correctly.
Whitelist *.supabase.co alongside the existing self/Activepieces entries.
The signed URL is short-lived (Supabase default) so the exposure is
minimal — same trust boundary we already accept on connect-src.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(csp): scope frame-src to our Supabase project, not the wildcard
Per Greptile review on #411. supabaseUrl (NEXT_PUBLIC_SUPABASE_URL) is
already in scope and resolves to <project-ref>.supabase.co, which is
where our Storage signed URLs live. Using it instead of the *.supabase.co
wildcard prevents the app from being able to iframe content from any
other Supabase tenant.
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(invoice-inbox): switch from pdfjs-dist to unpdf for PDF text extraction
After three rounds of fighting pdfjs on Vercel (#407 stubbed DOM globals,
#408 tried to ship the worker file via outputFileTracingIncludes), text
extraction still failed in prod with "Setting up fake worker failed" —
Next's tracer can't reliably include pdfjs-dist's worker file when the
package is marked as a server external.
unpdf is a serverless-first wrapper around pdfjs (by unjs) that ships its
own bundled pdfjs build with no canvas/worker dependencies. Drop-in
replacement: extractText returns merged page text directly.
- Remove DOM stubs, serverExternalPackages, outputFileTracingIncludes
- Replace pdfjs-dist with unpdf (no transitive deps)
- Update test mock from getDocument → extractText
All 45 invoice-inbox unit tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(invoice-inbox): make unpdf import static, rename pdfjs test labels
Per Greptile review on #409:
1. CRITICAL: tryExtractPdfText was still using await import('unpdf'), a
dynamic import. CLAUDE.md forbids dynamic imports in extensions
precisely because Next.js bundling can't reliably trace them — which
is the same class of failure that caused the pdfjs prod bug. unpdf
bundles statically (no canvas/worker), so a top-level static import
is safe and correct.
2. NIT: two test descriptions still said "pdfjs" after the mock rename.
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(invoice-inbox): include pdfjs worker file in function bundle
After #407 landed and the DOMMatrix error was gone, the next pdfjs failure
surfaced in prod logs:
Setting up fake worker failed: "Cannot find module
'/var/task/node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs'..."
pdfjs loads its worker module via dynamic import at runtime, which Next's
tracer can't see. With pdfjs-dist marked as a server external package the
main pdf.mjs ships but the worker file gets pruned. Force-include it via
outputFileTracingIncludes for any /api/extensions/ext/** route, since
that's where invoice-inbox dispatches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: narrow pdfjs worker tracing to invoice-inbox sub-path
Per Greptile review on #408. Other extensions don't use pdfjs and
shouldn't pay the ~1 MB worker cost. In practice all extension routes
share one catch-all bundle, so this is more about clarity of intent than
bundle size today, but future-proofs against per-segment bundling.
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(invoice-inbox): stub DOM globals so pdfjs text extraction works on Vercel
pdfjs-dist's legacy build references DOMMatrix/ImageData/Path2D at module
load. On Vercel's Node runtime those globals don't exist, so the dynamic
import threw "DOMMatrix is not defined" before getDocument() ran — every
inbox item came back with empty extracted_data. We only call
getTextContent (no rendering), so empty-class stubs are sufficient.
Also mark pdfjs-dist as a server external package so Next.js doesn't try
to bundle it (the bundling step pulls in @napi-rs/canvas references that
aren't installed and fails silently with the same downstream error).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(invoice-inbox): hoist DOM stubs to module scope
Per Greptile review on #407: stubs run once at module load instead of
per-call. Same effect, clearer intent. Wrapped in a block to scope the
local variable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: remove Sentry, consolidate migrations, add test coverage
Remove @sentry/nextjs and all Sentry integration code — error tracking
now handled by Recapt. Consolidate 22 incremental migrations into a
single schema sync migration. Add 6 new test suites (auth, invoice
matching, VAT rules, opening balances) and extend report tests with
edge cases. Update Docker image name to gnubok, sync crontabs and
extension presets, fix CSP missing space, simplify journal entry
missing-document dialog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove viewer bank import migration never applied to production
20260413150000_viewer_bank_import_permissions.sql (PR #234) was merged
to main but never applied to the production database. It references
current_active_company_id() which does not exist in production either.
This breaks fresh installs and Supabase preview branches because the
migration runs before the consolidated schema sync.
Remove it so the migration chain matches production. The viewer bank
import RLS policies should be re-added in a future migration alongside
the helper functions they depend on.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: correct delete policies for tables without company_id column
Seven tables in the generic delete-policy loop don't have a direct
company_id column, causing fresh installs to fail with "column
company_id does not exist". Fix by moving them out of the loop:
- invoice_items, journal_entry_lines, receipt_line_items,
supplier_invoice_items → join through parent table
- extension_toggles, notification_settings, push_subscriptions →
user-scoped (auth.uid() = user_id)
All policies match their existing production definitions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: complete multi-tenant refactor for reconciliation, arcim, settings validation
- Migrate bank-reconciliation to company_id (all functions + tests)
- Migrate arcim-migration entity mappers and orchestrator to company_id
- Fix enable-banking reconciliation calls to use companyId
- Add Swedish law validation to settings schema:
- VAT number required when VAT-registered (ML 11 kap. 8§)
- Moms period required when VAT-registered (SFL 26 kap.)
- Aktiebolag must use accrual accounting (BFNAR 2006:1)
- Fix fiscal year period creation: always 12 months after first year (BFL 3 kap.)
- Add plusgiro, website, pays_salaries fields to CompanySettings
- Add plusgiro to invoice PDF template
- Add fiscal period CRUD and opening balances API routes
- Add frame-src CSP directive for future iframe embedding
- Fix unlinked_1930_lines RPC to use company_id parameter
- Update CLAUDE.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review findings (P1 + P2)
- Fix reconciliation events emitting companyId as userId — thread
actual userId through runReconciliation and manualLink
- Move VAT cross-field validation (vat_number, moms_period) from
schema refinements to route handler where effective stored state
is available, preventing false rejection on partial updates
- Add plusgiro format validation regex (N-N pattern)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 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>
* fix: trial balance silently truncated at 1000 entries, rename /nyckeltal to /kpi
Trial balance bug:
- The old implementation fetched journal entry IDs (capped at 1000 by
Supabase default limit), then queried lines via .in(entryIds) which
also hit URL length limits with large arrays of UUIDs.
- SIE imports create thousands of entries → KPIs showed zero.
- Replaced with a single joined query (journal_entry_lines → journal_entries)
using fetchAllRows() pagination. No row limit, no URL length issue.
- Removed the non-existent generate_trial_balance RPC call.
Page rename:
- /nyckeltal → /kpi (CLAUDE.md: all code in English)
- Nav label stays "Nyckeltal" (user-facing Swedish UI)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: select filtered columns in joined query, rename component, add redirect
- Select user_id/fiscal_period_id/status from journal_entries!inner()
so PostgREST applies embedded filters reliably (defense in depth)
- Rename NyckeltalPage → KpiPage per English code convention
- Add permanent /nyckeltal → /kpi redirect for existing bookmarks
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-chat from extensions.config.json and docker/extensions.hosted.json
(kept in self-hosted config). Remove ChatWidget imports from dashboard layout
and root page. Reposition chat widget FAB and panel to bottom-right corner.
Regenerate extension registry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 'unsafe-inline' to script-src so Next.js hydration scripts run
- Whitelist *.enablebanking.com in CSP (script, style, connect, img)
- Allow HTTPS images broadly for third-party bank logos
- Clear stale refresh tokens in middleware (skip on /auth callback)
- Fix login button disabled on browser autofill by reading email from form DOM
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vercel outage resolved — re-enable Sentry build-time integration for
source map uploads when SENTRY_AUTH_TOKEN is configured.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sentry runtime capture still works via instrumentation.ts. The build-time
wrapper can be re-added once SENTRY_ORG/PROJECT/AUTH_TOKEN are set on Vercel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>