122bcbbcc12f5017a48d71dccbcbb66f877d75dd
54 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0ca9c25aba |
Add/user feedback (#679)
* feat(bookkeeping): make blocked fiscal-year creation actionable When creating a new räkenskapsår is blocked because a prior period is still open, the "Skapa räkenskapsår" dialog no longer dead-ends on an English toast. The API now returns the canonical bilingual error envelope with the blocking periods (id/name/dates) under details, and the dialog renders a Swedish panel that locks them inline (reversible locked_at) via the existing /lock endpoint and retries creation. The guard rule is unchanged and remains BFL-compliant: BFL 6 kap allows löpande bokföring of the new year in parallel with the prior year's bokslut, so a lock (not a full close) is sufficient and reversible. - Add PERIOD_CREATE_BLOCKED_BY_OPEN_PERIODS structured error code - Return envelope + details.blockingPeriods from the 409 (was English string) - CreatePeriodDialog: inline "lås och skapa" panel + lock-and-retry - Update route tests for the new envelope shape Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ui): prevent mouse wheel from mutating number inputs A focused <input type="number"> would change its value on scroll, silently turning e.g. a 20000 salary into 19998. Blur number inputs on wheel so the page scrolls instead of editing the value. Applied at the Input primitive so all number fields are protected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(salary): auto-derive skattetabell and kolumn for employees Replace the opaque manual "Skattetabell (29-42)" and "Kolumn (1-6)" inputs on the employee form with a self-deriving flow: the user picks their folkbokföringskommun from a searchable dropdown and the tax table fills itself in, while the column derives from the personnummer we already collect. - Add a searchable municipality picker (MunicipalityCombobox) backed by a new cached GET /api/salary/tax-tables/kommuner endpoint. - Wrap the whole "Skatt" card in a self-contained EmployeeTaxCard used by both the create and edit pages, with InfoTooltips and named column options. - deriveTaxColumn(): auto-select column 1 for under-66 employees; leave the ambiguous 66+ case (pension vs working senior) to a clearly-named manual choice. - Fix fetchKommunTaxRates() to page through all ~1300 församling rows instead of a single 500-row page (which silently dropped ~200 kommuner, incl. Göteborg) and normalize the uppercase names to title case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(import): correct CSV amount-column guess and surface skipped rows Manual CSV column-mapping auto-guess walked each data row right-to-left and picked the first numeric cell as the amount, so on the common ...;Belopp;Saldo layout it grabbed the trailing running-balance column. Extract the guess into a pure, tested suggestColumnMapping(): match header labels first (belopp/amount -> amount, saldo/balance -> balance), auto-fill the balance field, and fall back to value heuristics that skip the balance column and prefer a column carrying negative values. Also surface stats.skipped_rows + parse warnings in BankFileConfirmStep - the manual-mapping path skips the preview step that was the only place they showed, so skipped rows were silently dropped from view. Add a unit test reproducing the Saldo-as-amount regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: add "Save as draft" functionality for invoices - Implemented a new feature to allow users to save invoices as unnumbered drafts without generating an invoice number until finalized. - Added a `save_as_draft` flag to the CreateInvoiceInput schema to handle draft saving logic. - Updated the invoice creation API to skip number allocation when saving as a draft. - Introduced a new endpoint for finalizing drafts, which allocates an invoice number and emits an `invoice.created` event. - Enhanced the UI to include a "Save as draft" button, with loading states and tooltips. - Updated tests to cover the new draft saving and finalization logic, including race conditions for concurrent modifications. - Added relevant error handling for draft finalization and deletion scenarios. * feat(employee): add employment start and end date fields to employee forms * feat: enhance invoice and salary run handling with improved validation and event logging --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
bc61862e76 |
feat(agent): telemetry + CI-gate quick wins from the "AI systems that ship" audit (#677)
* feat(agent): telemetry completeness + durability, CI gates, commit_method provenance Quick wins from the "Building AI systems that ship" audit: - mcp.tool_called gains errorMessage (message_sv, truncated 500 chars) on all failure exits; new mcp.skill_loaded event on every gnubok_load_skill (all tiers) so atom usage is finally measurable - event_log: (event_type, created_at) index; cleanup cron keeps mcp.*/agent.* telemetry 180 days (delivery events stay 30) - CI: lint ratchet (npm run check:lint — 60 legacy errors baselined, fails only on NEW errors) and a pg-real coverage gate (migrations touching trigger/RPC/RLS/DEFERRABLE require a *.pg.test.ts change; escape hatch: -- pg-test: covered-by/skip) - journal_entries.commit_method CHECK widened with 'api_key'/'agent'; the MCP approve path records 'api_key' truthfully instead of 'user_accept' (agent_first_vision §8 P0-1). 'agent' is reserved — ALL MCP traffic (incl. claude.ai OAuth, whose access_token is a minted API key) authenticates as api_key today Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(import): derive opening balances from prior-year #UB when SIE lacks #IB (#675) SIE files exported without #IB 0 rows (only #UB -1) previously imported with zero opening balances. getEffectiveOpeningBalances() now derives IB from prior-year UB for balance-sheet accounts when explicit #IB is absent, surfaces the derivation as an info issue in the import preview, and excludes share-capital vouchers from opening-balance detection. Detection regexes are shared between parser and importer so the two checks cannot drift. 507 lib/import tests pass. (Authored in a parallel session in this checkout; included per request.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(review): address PR #677 bot findings — RoPA entry, execFileSync, gate scope note Triage of the compliance-swarm + Greptile findings: Applied: - .compliance/ropa.yaml: new mcp.telemetry processing activity declaring the 180-day mcp.*/agent.* retention, lawful basis, data categories, and the no-args/no-results minimisation (ISO A.8.10, GDPR Art.5(1)(c) — the retention split is now formally documented, referenced from the cron) - check-pg-test-coverage.mjs: execFileSync with argv array — no shell, so a hostile base-ref can't inject (ASVS V13.2.1); verified an injection attempt exits 2 without executing - check-pg-test-coverage.mjs: documented the PR-level (not per-migration) scope of the gate so reviewers know to check coverage per migration when a PR carries several risky migrations (Greptile P2) Acknowledged, no change: - errorMessage PII risk: messages are domain-mapped strings; event_log already persists far richer delivery payloads under the same RLS; now declared in ropa.yaml - cron error envelope: errorResponse maps to the canonical safe envelope and the endpoint is CRON_SECRET-gated - two-pass delete "partial state": TTL deletes are idempotent — the next daily run sweeps whatever a failed pass left behind - skill_loaded actorLabel/sessionId: mirrors the pre-existing mcp.tool_called payload; sessionId is the join key the analytics exist for Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f7cd1b86e7 |
fix(import): preserve customized SIE #KONTO account names (#669)
* feat(import): add syncMappedAccounts helper for account create + rename Single home for the create-missing-accounts logic that exists in three near-identical copies (executeSIEImport, the SIE execute route, and the arcim-migration extension), plus a new rename pass that carries customized SIE #KONTO names into accounts that already exist (e.g. K1-seeded defaults). The file's name applies only to identity mappings (source === target); remapped targets keep their BAS/current name. With updateAccountNames=false the behavior matches the legacy code exactly. Not wired up yet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(import): preserve SIE #KONTO account names; add updateAccountNames option Customer report: account names customized in Fortnox did not follow into Accounted via SIE import. The import always used BAS default names for accounts in the BAS reference and never touched accounts that already existed (the K1-seeded chart), so the file's names were silently dropped. executeSIEImport now routes account creation through syncMappedAccounts, which prefers the file's #KONTO name for identity-mapped accounts and renames existing accounts whose name differs (surfaced as a warning). New option updateAccountNames (default true) restores the old behavior when disabled. The duplicated pre-create blocks in the execute route and the arcim-migration extension are removed — executeSIEImport owns account sync on every path now, including the Fortnox re-sync (idempotent renames). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(mcp): expose update_account_names on gnubok_import_sie Optional boolean on the tool schema, staged into the pending operation and threaded through commitImportSie to executeSIEImport. Defaults to true at both stage and commit time — the commit-side default also covers operations staged before the param existed (Boolean(undefined) would have silently flipped it off). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(api): v1 SIE import generated no account mappings The route passed [] as mappings to executeSIEImport, which the mapping-coverage guard (added in #613) rejects for any real file — and before that guard, every voucher was silently skipped as unmapped. The route has never produced a working import for files with vouchers. Generate mappings server-side from the file's #KONTO records plus stored per-company overrides (same as the dashboard execute route), reject unmappable files with a clean 400 before the operation row is created, and expose options.updateAccountNames (default true). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(import): "Använd kontonamn från filen" toggle in import review step New switch (default on) controlling whether the SIE file's #KONTO names are carried into the chart of accounts. Helper text shows how many identity-mapped accounts carry names that differ from the BAS defaults. The page already serializes the whole options object to the execute route, so no further wiring is needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(import): address PR #669 review — parallel renames, rename audit trail - Rename pass now runs UPDATEs concurrently in bounded batches of 25 (greptile P2): a re-sync with many custom names no longer serializes N round trips, and a pathological full-chart rename cannot stampede the API. Per-rename failures stay non-fatal via Promise.allSettled. - Persist the per-account rename detail (number, from, to) into sie_imports.migration_documentation as accountRenames — the behandlingshistorik record per BFNAR 2013:2 (swedish-compliance review); the result warnings only carry the count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c74b19df1b |
Accounted rebrand + swarm-skill cleanup + bank-reconciliation fixes (#643)
* feat(reconciliation): close the bank-feed loop on voucher links and re-tag mis-typed opening balances
Two related fixes to bank reconciliation correctness:
1. Auto-reconcile on voucher link. Linking an invoice or supplier invoice to
an existing voucher previously advanced only the invoice — the bank
transaction that paid it kept sitting in the Transactions inbox with a null
journal_entry_id. linkInvoiceToVoucher / linkSupplierInvoiceToVoucher now
call autoReconcileTransactionForLinkedVoucher (lib/reconciliation), which
links the bank transaction to the same verifikat when exactly one unbooked
line matches it. Best-effort and post-commit: a failure here never fails the
link. The result surfaces reconciledTransactionId; the inbox row leaves the
list and the UI shows link_success_tx_reconciled.
2. Re-tag mis-typed opening balances. getReconciliationStatus and the GL-line
matching RPCs identify a cash account's ingående balans solely by
journal_entries.source_type='opening_balance'. Companies migrated from other
systems often booked the bank IB as an ordinary voucher (source_type
'import' or 'manual'), so it was never excluded and surfaced as a phantom
reconciliation difference equal to the opening balance. Adds:
- migration mark_entry_as_opening_balance: a GUC-gated carve-out in the
immutability trigger plus a SECURITY DEFINER RPC that validates the entry
(balance-sheet lines only, dated on a fiscal-period boundary), flips the
source_type, and writes an audit row — no blanket data sweep.
- POST /api/reconciliation/bank/mark-opening-balance + MarkOpeningBalanceSchema.
- BankReconciliationView action to trigger it from the IB diff.
The gnubok_create_voucher executor now accepts a typed is_opening_balance flag
and derives source_type='opening_balance' only after validating class 1/2 lines
on the period start, so new IBs land correctly typed.
Covered by lib/reconciliation auto-reconcile tests, voucher-executors tests,
and a mark-entry-as-opening-balance pg-real test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: rebrand gnubok → Accounted and prune swarm agent skills
Product rebrand and skills housekeeping. No runtime behaviour change.
Rebrand: replace user-visible "gnubok" with "Accounted" across docs, READMEs,
in-code comments, doc-site content, MCP skill/resource prose, and the
gnubok-mcp package description. The MCP resource URI scheme is moved gnubok://
→ Accounted:// consistently across resource registrations, the event-type
comment, and the resource/skill tests. Deliberately preserved as stable
identifiers (NOT rebranded): the gnubok-company-id cookie, gnubok_sk_ / gnubok_inv_
token prefixes, the gnubok-mcp npm bridge name, and the AGI <gem:Programnamn>
value (kept 'gnubok' per its source comment — it is the software identifier sent
to Skatteverket and must not churn across visual rebrands).
Skills: remove the 27 swarm-* agent SKILL.md atoms (no longer used; already
absent from the agent_atom_registry in prod), refresh the remaining skill docs,
add the .claude/rules/ path-scoped rule set, and regenerate the
seed_agent_atom_bodies migration + .skill-body-manifest.json via
`npm run skills:generate` so the DB-backed skill bodies match the trimmed set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
c6c86cded4 |
Mcp/template data feedback (#617)
* 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> |
||
|
|
ea1bf01f1e |
Fix/m sprint fixes (#613)
* fix(dashboard): exclude ignored and already-triaged transactions from stale count The "Gamla transaktioner" widget counted transactions that had been ignored or already marked as is_business=true but not yet booked, so users saw a nag for a row they had already dealt with — and the /transactions inbox correctly hid it. Align the count with the inbox criterion (is_business IS NULL, is_ignored = false) so the widget clears when the row leaves the inbox. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(transactions): read entity_type from settings response wrapper The transactions page read entityRes.entity_type directly, but /api/settings returns { data: { entity_type, ... } }. The expression was always undefined, so setEntityType never fired and entityType stayed at its initial 'enskild_firma'. The template picker's entity_type filter then dropped every aktiebolag-tagged user template for AB customers — only entity_type='all' templates made it through. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * stale templates bank sync journal entry from transaction * fixed pr comments * fixed pr comment --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ccdfed5fea |
feat: voucher linking, recovery ops, and salary overrides (#591)
* feat: voucher linking, recovery ops, and salary overrides Adds reversible/correction-style write paths that customers and agents have been asking for, plus per-run salary employee overrides. Invoice → voucher linking - POST /api/invoices/[id]/link-to-voucher and GET /api/invoices/[id]/voucher-candidates - lib/invoices/voucher-matching.ts with full + pg test coverage - LinkVoucherPicker UI in PaymentBookingDialog - pending_operations.operation_type expanded with link_invoice_voucher (medium risk) and a (journal_entry_id, invoice_id) unique guard - MCP: gnubok_find_voucher_candidates_for_invoice and gnubok_link_invoice_to_voucher tools SIE undo - POST /api/import/sie/[id]/undo + undo_sie_import RPC - sie_imports.status gains 'undone' - ImportResultStep surfaces the action; structured error SIE_UNDO_FAILED Edit-recreate journal entries - POST /api/bookkeeping/journal-entries/[id]/edit-recreate - Bookkeeping detail page wires it into the existing edit flow Delete-last-voucher clears IB link - Trigger + pg test ensure deleting the last voucher of a period nulls the opening_balance_journal_entry_id link so a re-import lands cleanly Salary employee overrides - salary_run_employees gains per-run override fields + migration - lib/salary/effective-values.ts centralises resolved values; all payslip, payment, AGI, KU, and booking routes read through it - SalaryOverridePanel on the employee detail page Account classifier - lib/bookkeeping/account-classifier.ts + tests; AddAccountDialog uses it - backfill-import-accounts script updated Misc - toast: minor styling tweak - AGI generate-declaration: respect effective values - structured-errors: new LINK_INVOICE_VOUCHER namespace Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add link_invoice_voucher operation type to pending_operations * feat: refactor salary run calculations and update error handling for SIE imports * fix: PR review feedback on voucher linking and SIE recovery pg-real (blocking): - tests/pg/delete-last-voucher-ib: drop posted_at = now() from the seed UPDATE — journal_entries has no posted_at column. - lib/invoices/__tests__/voucher-matching.pg: seed the posted voucher before closing the fiscal period so enforce_period_lock doesn't block the INSERT during setup. voucher-matching error codes and rollback: - Add LINK_VOUCHER_DB_ERROR (HTTP 500) and return it on real invoice UPDATE / payment INSERT failures. Previously these returned LINK_VOUCHER_VOUCHER_NOT_FOUND (404) which the pending-op dispatcher auto-rejects on transient DB errors. - Log rollback failures explicitly so an invoice left in a half-linked state (advanced status, no payment row) surfaces for manual reconciliation instead of disappearing silently. resyncNextPeriodOpeningBalance ordering: - Create the new IB first, relink the period FK, then storno the old IB. Previously the storno ran first; if createJournalEntry failed the next period was left with a reversed IB and nothing to replace it, and executeSIEImport swallows the error as a non-fatal warning. replace_period_opening_balance_link: - Tighten role check to owner/admin (was owner/admin/member). Matches delete_last_voucher and undo_sie_import. Data minimisation: - /api/invoices/[id]/voucher-candidates and the matching MCP tools now project only the invoice and customer fields the matcher reads, instead of returning the full customer row. Schema bounds: - SalaryEmployeeOverrideSchema caps each numeric override at 10 MSEK to catch typos before they reach the ledger or AGI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(tests): supply user_id when seeding voucher_sequences voucher_sequences.user_id is NOT NULL (per the multi-tenant refactor in 20260330130000). The previous test seed only set company_id / fiscal_period_id / voucher_series, which made the seed fail with a constraint violation on the latest pg-real run. Pass the same userId used elsewhere in the seed helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(tests): scope delete-last-voucher RPC assertions inside the tx withUserContext always ROLLBACKs, so any DELETE the RPC performs is discarded when the callback returns. The previous test then queried journal_entries via a fresh getPool() connection that only saw the pre-RPC committed seed state — hence "expected '1' to be '0'". Move every post-RPC assertion (entry count, period FK clear, opening_balances_set flip, audit log entry, sie_imports clear) inside the same withUserContext callback so they observe the uncommitted state before ROLLBACK fires. Also fix the sie_imports INSERT: the column is `filename`, not `file_name`, and `sie_type` is NOT NULL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(tests): assert against the IB-marker audit row directly DELETE on journal_entries fires two audit_log writes: the generic write_audit_log() trigger row ("Deleted journal_entries record") and the delete_last_voucher RPC's explicit "(was period IB)" entry. Both land at the same statement_timestamp(), so ORDER BY created_at DESC LIMIT 1 returned the trigger row non-deterministically in CI. Switch to a presence check with a LIKE filter on the IB marker so the test verifies what it actually cares about — that the RPC's IB-aware audit row exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(db): set company_id on delete_last_voucher audit_log rows 20260528120000_delete_last_voucher_clears_ib_link.sql inserts directly into audit_log without setting company_id. audit_log's SELECT policy filters company_id IN user_company_ids(), so those rows landed with company_id=NULL and were invisible to every reader — only the generic write_audit_log() trigger row remained visible. That broke BFL audit- trail intent: the "(was period IB)" provenance row was never readable. Republish delete_last_voucher with p_company_id populated on both audit_log INSERTs (draft path and posted path). Behavior is otherwise unchanged; the pg-real test for the IB-clear flow now sees the RPC-written marker row as expected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Emil <emilmattsson14@gmail.com> |
||
|
|
32d9978f1b |
Fix/chrome pdf preview csp (#572)
* 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> |
||
|
|
39204cc0de |
UX polish bundle: Enable Banking lookback + sync progress, invoice inbox, matching previews (#548)
* fix(import): dedup opening-balance rows when account numbers differ only in whitespace The parser's merge map keyed on the post-strip account_number, but rows like "1930", " 1930 " and "1.930" could leak as separate entries when the upstream string contained non-breaking spaces or zero-width chars that the old .replace(/[^0-9]/g, '') ran on already-stripped output. Strip those explicitly in the raw string and use /\D/g for the digit extraction. Also adds defense-in-depth dedup inside OpeningBalanceEditStep so any duplicates that survive the parser collapse before the user sees them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(enable-banking): anchor lookback picker to fiscal year, not days Replaces the 90/180/365 days dropdown on the account-selection screen with three explicit modes: - "Senaste 90 dagar (snabbt)" — fastest path, matches PSD2 ceiling - "Sedan räkenskapsårets början" (default) — resolves via fiscal_year_start_month, surfaces the literal date inline - "Anpassat datum" — free date picker OR "Föregående räkenskapsårets start" When the resulting range exceeds 90 days, the picker now surfaces a quiet helper that points users at the SIE/bankfil import for older history, so they don't waste an account-selection round-trip discovering that banks usually cap at ~90 days. The PATCH /accounts handler accepts initial_lookback_from_date alongside initial_lookback_days; the new helper getCurrentFiscalYearStart() in lib/company/fiscal-year.ts is reused. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(enable-banking): dedicated sync progress modal replaces silent spinner After the user confirms account selection, transactions fetch in the background for 30–60 seconds. Previously this showed only the Spara-button spinner with no indication of duration or what was happening — users described being stuck on the page. The new BankSyncProgressDialog opens immediately on Save, lists the enabled accounts being synced, and disables manual close until the PATCH resolves. On completion it shows the imported count and the actual date range the bank returned, plus an amber escape hatch to SIE/bankfil import when the returned range was truncated by >7 days from what was requested. Failure path surfaces in the same modal rather than as a destructive toast that disappears. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(invoice-inbox): drop duplicate Skapa leverantör button The inbox detail panel had its own supplier-creation button that fired /api/suppliers + match-supplier. The same action is reachable from the supplier-invoice form's "Skapa & välj" card (showAISupplierHint), which also prefills more fields. The duplicate button is gone; a quiet inline hint replaces it so the user still knows why no supplier matched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ui(invoice-inbox): surface currency and totals above the long metadata tail Move Valuta / Totalt / Moms in FIELD_DEFS so they sit immediately under Leverantör / Org.nr / VAT-nr. These are the fields the user reads first when triaging an inbox item; burying them after nine metadata fields forces unnecessary scrolling on every single invoice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(invoice-inbox): accept .eml forwards and log rejected attachments Gmail's "Forward as attachment" packages the original email as message/rfc822, which our MIME allowlist silently dropped. Adds mailparser so we can unwrap the inner attachments and ingest them under the inner email's subject/from. Also persists every rejected attachment as an invoice_inbox_items row with status='error', so users can see what was dropped instead of guessing why nothing showed up in their inbox. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(supplier-invoices): redirect back to inbox after creating from invoice-inbox When the leverantörsfaktura form is opened from an invoice-inbox item, every successful create previously kicked the user out to /supplier-invoices or the just-created invoice's detail page — derailing the "process the next document" workflow. The Tillbaka button likewise routed to the supplier-invoice list rather than the inbox they came from. Adds an afterCreate helper that lands inbox-originated submissions at /e/general/invoice-inbox and preserves the original target everywhere else. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ui(pending): show transaction/document context for match-and-attach reviews The granskning page previously rendered attach_document_to_transaction and match_transaction_invoice operations through the generic key/value preview, so reviewers saw "document file name: Faktura.pdf / transaction amount: -216 USD" without any visual indication of which two things were being paired. The MCP tool already returns enriched preview data; we just needed dedicated layouts. Adds: - AttachDocumentPreview — two-card layout (Transaktion | Dokument) with a "Visa dokument" button that fetches a signed download URL on demand - MatchTransactionInvoicePreview — same layout (Transaktion | Faktura) - DocumentViewButton — reusable signed-URL opener Also tightens the matching tools' descriptions so AI clients are nudged to verify human-readable context before staging. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(review): address PR #548 review feedback - invoice-inbox: hoist mailparser to a static import. The extension system generates a static import tree via setup:extensions and disallows dynamic imports — await import('mailparser') worked in dev but could fail in production standalone builds. - enable-banking AccountPickerDialog: guard the Save path when "Anpassat datum" + "Specifikt datum" is selected with an empty date. Without this, lookback.body resolves to null and the PATCH silently falls back to the backend's 120-day default, ignoring the user's intent. - enable-banking BankSyncProgressDialog: drop the empty-body useEffect. Close-prevention is already handled inline via the onOpenChange guard + onPointerDownOutside + onEscapeKeyDown handlers. - lib/company/fiscal-year: pin both operands of daysBetween() to UTC when parsing ISO date strings. Mixing a UTC-parsed date with new Date() (local time) drifts by one day in any timezone east of UTC. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(review): address compliance swarm + Swedish review feedback Three actionable items from the post-fix compliance scan; the rest were false positives or out of scope. - enable-banking PATCH /accounts: reject future initial_lookback_from_date with 400 instead of silently falling through to the 120-day default. Compliance V2.2. - AttachDocumentPreview: promote the overwrite warning to a destructive banner with BFL 7 kap context when the existing document is marked as räkenskapsinformation. A muted footnote was too easy to skip past for a verifikationsunderlag replacement. - MatchTransactionInvoicePreview: surface transaction_date + invoice_date in the staged preview so reviewers can spot date drift before approving (BFL 5 kap 6§ — verifikation date must align with affärshändelse). Also shows a quiet hint when the two dates differ by > 31 days. Tool's SELECT + stage payload extended accordingly. Skipped (with rationale): - V5.3 inner.filename path traversal — lib/core/documents/document-service.ts already sanitizes filenames before constructing storage paths. - V5.2 magic-number MIME — pre-existing pattern for all email attachments; scope is codebase-wide. - V1.2 att.id composite ID — only used as a DB column value, never a path. - V13.1 / CM-8 SBOM/SCA — repository-wide policy, not this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(review): address second-round compliance + Swedish review feedback Compliance Swarm (defense-in-depth + valid finds): - invoice-inbox: sanitise .eml inner attachment filenames and content-types before they flow into uploadAndExtract or the raw_email_payload JSONB. document-service already strips bad chars before constructing storage paths, but the swarm flagged the upstream input as unsanitised — easier to add a thin sanitiseFilename/sanitiseMime layer than to argue about defense-in-depth. Caps lengths too. - DocumentViewButton: validate documentId as a UUID before interpolating into /api/documents/:id — staged preview_data is Record<string, unknown> on the wire, so refusing junk early gives a clearer error and keeps the internal API from seeing oddly-shaped path segments. (Compliance V1.2.) Swedish review: - MatchTransactionInvoicePreview: drop the BFL 5 kap 6§ citation from the date-drift hint — that section governs verifikationsinnehåll, not a 31-day tolerance. The hint stays (the practical concern is real) but no longer pretends to quote a legislated threshold. - fiscal-year: document the implicit assumption that entity_type reflects the company's current tax-year status, not a mid-conversion state. Skipped (with rationale): - V5.2 magic-number MIME — pre-existing pattern across all email attachments. - A.8.12 signed URL via window.open — pre-existing pattern shared with JournalEntryAttachments.tsx; refactor to server-side redirect is broader scope. - A.8.15 logRejection failure path — pre-existing console.error pattern. - CC9.2 mailparser vendor review / SBOM — out of PR scope. - CC6.1 IDOR — /api/documents/:id already enforces company_id; false positive. - Swedish #1 räkenskapsinformation flag origin — server-side already derives the flag from document_attachments.journal_entry_id in the staging tool; not caller-trusted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(review): fail-safe BFL warning + preserve merge validation errors Two findings from the third compliance pass; both worth addressing. - AttachDocumentPreview: treat an absent existing_document_is_rakenskapsinformation flag as räkenskapsinformation rather than as "safe to overwrite". The MCP staging tool sets the flag deterministically from document_attachments.journal_entry_id today, but a future code path that forgets it would silently downgrade the BFL 7 kap warning. Only an explicit `=== false` from the server keeps the muted note path. - Opening-balance merge: union validation_errors when collapsing duplicate account_number rows, both in the parser and the EditStep useState initializer. Previously a warning that fired on row 5 (e.g. BAS-class mismatch) was silently dropped if row 2 of the same account had no error, risking misclassified IB data downstream. Added a parser test covering the union behaviour for two rows of a class-3 (resultatkonto) account. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
566ed72984 |
Bug/mcp connection issue (#541)
* 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 |
||
|
|
f829c96d8b |
fix(sie-import): accept tab as field separator (Bollbok exports) (#513)
* fix(sie-import): accept tab as field separator (Bollbok exports) The SIE 4 spec allows either space or tab between fields, but splitSIELine() only treated space (0x20) as a separator. Bollbok exports tab-separated lines for every record except #RAR, which silently swallowed all #IB / #UB / #KONTO / #KTYP / #VER / #TRANS records — imports appeared empty even though the file was well-formed. Also adds a parser-side diagnostic that emits a warning when raw #IB or #VER lines are present in the input but parsing produced none. The previous silent failure is how this bug stayed hidden; the warning gives the import preview something visible to surface next time. Verified against two real reproducer files (Sean / Erik Hellqvist): erik h 2025.SE (UTF-8): 166 accounts, 66 IB, 4 UB, 11 RES, 95 vouchers, 198 TRANS. erik h 2026.SE (CP437): 166 accounts, 66 IB, 4 UB, 0 vouchers. Both now parse with zero warnings/errors. Tests: + 8 Bollbok-shape tab-separated fixtures (2025 + 2026 quoting variants). + 4 silent-failure diagnostic-warning tests. All 74 sie-parser tests pass; 155/155 in lib/import; 64/64 downstream callers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sie-import): address PR #513 review — strip #KTYP quotes, suppress redundant aggregate warning Two non-blocking P2 findings from Greptile review on PR #513: 1. #KTYP handler stored fields[2] directly, so Bollbok 2026 exports (#KTYP\t1510\t"T") stored '"T"' with literal quotes instead of 'T'. Latent defect — accountType is unused downstream today, but my tab- separator fix made the quoted-value path reachable. Now routes through parseStringField so both Bollbok 2025 (unquoted T) and 2026 (quoted "T") land as 'T'. 2. The aggregate "kontrollera fältavskiljare och teckenkodning" warning fired alongside per-record 'error'-severity issues for malformed #IB / #VER records, producing a misleading hint when the parser had already pinpointed the structural problem. Now suppressed when an error-severity issue with the same tag already exists. Test coverage: + accountType asserted to be 'T' (not '"T"') in both 2025 + 2026 shapes. + VER aggregate-warning test now uses #VER lines without { } blocks (silent loss, no per-record error) — the canonical case the diagnostic is designed for. + New suppression test: bare #VER produces per-record errors AND the aggregate warning is absent. 75/75 sie-parser tests pass; 156/156 in lib/import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
831920fede |
fix(arcim): allow Fortnox re-sync to replace prior SIE import per fiscal year (#512)
* fix(arcim): allow Fortnox re-sync to replace prior SIE import per fiscal year A user reported a sync failure when retrying Fortnox after adding more verifications: Import failed: Failed to create pending import record: duplicate key value violates unique constraint "sie_imports_company_id_file_hash_active_idx" Root cause: Fortnox embeds the export-time #GEN date in every SIE export, so the file hash always differs between syncs. The wizard's hash-based duplicate detection treated each sync as a brand new file, but the engine still rejected the insert because the per-period import slot was held by the prior 'completed' row. This change reframes Fortnox re-sync as a replace operation rather than a fresh import: - New executeSIEImport option `onExistingPeriod: 'block' | 'replace'`. Manual SIE upload at /api/import/sie keeps default 'block' (current behavior, no regression). The Fortnox /import-sie endpoint passes 'replace', which runs replaceSIEImport on any overlapping completed import before insert. Imported journal entries from the prior import are cancelled per BFL 5 kap 5§; user-created entries (manual, transaction, invoice) are untouched. - /sie-data switches from hash-based to period-based duplicate detection and returns previousImport metadata per fiscal year. - Wizard drops the alreadyImported skip filter, surfaces an amber callout in the confirm dialog listing fiscal years that will be replaced, and shows "ersatte N tidigare importerade verifikationer" per year. - createPendingImportRecord translates 23505 partial-index violations to a clear Swedish recovery message instead of leaking the raw constraint name. - cleanupStaleImportRecords drops the 1-hour age gate and also cleans status='mapped' orphans. SIE imports are single-flight per company so the gate just made legitimate retries fail. - After replace, the fiscal_periods row's opening_balances_set and opening_balance_entry_id are cleared (only when they pointed at the cancelled prior IB entry), so the new IB import isn't skipped. Schema-drift migration captures the partial unique index sie_imports_company_id_file_hash_active_idx that already exists in production (added out-of-band) and drops the now-superseded plain sie_imports_company_id_file_hash_key constraint. Both statements are idempotent — verified no-op against production. Tests: new pg-real test covers the partial index admit-replaced semantics, source_type='import'-only cancellation in replace_sie_import, and the post-replace insert path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(arcim): address PR review — restore 5-min cleanup gate, fix test source_type Greptile P2: `cleanupStaleImportRecords` was deleting `pending` rows unconditionally, which could wipe a concurrent in-flight import in another tab/session. Restored a 5-minute age gate (long enough for any normal interactive import, short enough that legitimate retries after a crash still succeed). Also dropped `mapped` from the cleanup — it is defined in SIEImportStatus but no code path writes it, so including it was both unnecessary and added the concurrent-session risk Greptile flagged. pg-real test: insertPostedEntry used `source_type='transaction'` which is not a valid value per the journal_entries_source_type_check constraint (migration 20260516060000). Switched to `'bank_transaction'` — the actual source_type emitted when a user categorizes a bank transaction in gnubok. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8f15f98687 |
Bug/employees creation (#440)
* Fix/employees page salary display logic and labels * feat: add salary_worked_days table and related functionality - Implemented the salary_worked_days table to track per-day worked hours for hourly employees. - Established row-level security (RLS) policies to ensure tenant isolation for salary_worked_days. - Created unique index on (employee_id, work_date) to enforce uniqueness. - Added trigger to enforce a 24-hour cap across worked and absence days for the same employee and date. - Developed tests to validate RLS, uniqueness, and 24-hour cap logic. * Fix: update hourly_salary calculation and refresh logic in salary run processing |
||
|
|
dec920682f |
Fix/UI changes (#439)
* feat(bookkeeping): add preview for next voucher number in JournalEntryForm * feat(encoding): implement U+FFFD recovery for Swedish text in encoding functions |
||
|
|
a53a119a2e |
Fix/vat parent accounts (#438)
* feat(enable-banking): add support for account selection and syncing - Updated StoredAccount interface to include an 'enabled' flag for account syncing preferences. - Enhanced ensureFiscalPeriod function to handle overlapping fiscal periods with posted entries and opening balances. - Added tests for fiscal period validation and account syncing logic. - Implemented AccountPickerDialog component for user account selection. - Created API routes for PATCH /accounts and POST /sync to manage account syncing. - Introduced 'pending_selection' status for bank connections to allow user account selection before syncing. - Updated database migration to support new connection status and backfill existing accounts with enabled=true. * feat(enable-banking): implement account selection and consent event logging --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
11e07b34c3 |
fix(sie-import): reject overlap-but-not-contain fiscal periods (#421)
* fix(sie-import): reject overlap-but-not-contain fiscal periods ensureFiscalPeriod previously fell back silently to any partially-overlapping fiscal_period when no period fully contained the SIE file's #RAR range. That stamped every imported voucher with a fiscal_period_id whose date window didn't cover the voucher's own entry_date — breaking the SIE invariant that #VER dates fall inside #RAR and BFL 5 kap. (verifikationsnummer i obruten serie per räkenskapsår). Reproduced in production: a customer with a broken fiscal year (Mar–Feb) had their previous-year SIE collapse into a calendar 2026 period, mixing 116 prior-year vouchers into the current period's voucher sequence. Now: fully-contained → reuse; partial overlap → reject with a clear Swedish message pointing at the period dates that would need to be fixed; no overlap → create a new period as before. Adds a defense-in-depth pre-check in executeImport that rejects with a Swedish error if any individual #VER date falls outside the resolved fiscal period — catches multi-year SIE files (which gnubok does not yet support) without producing corrupted journal entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sie-import): fail closed on period-fetch error in voucher-date guard Greptile correctly flagged that the voucher-date guard wrapped the period fetch in `if (resolvedPeriod)` without checking the .single() error. A transient network error or RLS failure returned null for data, the guard body skipped, and importVouchers ran with no date validation — the same data-corruption path the guard exists to close. Now: surface the fetch error to the user (Swedish) and abort the import. Also switches the date comparison from millisecond timestamps to YYYY-MM-DD string compare. SIE per spec is date-only and our parser normalizes to midnight, but string compare matches the underlying DATE columns exactly and removes a latent off-by-one risk on the period's last day if a future parser change ever attached a time component to v.date. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
81e9dd224e |
Add/csv import options (#420)
* feat(import): add customer and supplier parsing functionality - Implemented customer file parsing in `lib/import/customers/parser.ts` with support for Excel and CSV formats. - Created types for detected customer columns and parsed customer rows in `lib/import/customers/types.ts`. - Added tests for customer classification logic in `lib/import/shared/__tests__/classify.test.ts`. - Developed classification functions for customers and suppliers in `lib/import/shared/classify.ts`. - Introduced shared column utility functions in `lib/import/shared/column-utils.ts`. - Implemented supplier file parsing in `lib/import/suppliers/parser.ts` with validation for various fields. - Created types for detected supplier columns and parsed supplier rows in `lib/import/suppliers/types.ts`. - Added tests for supplier column detection and parsing in `lib/import/suppliers/__tests__/column-detector.test.ts` and `lib/import/suppliers/__tests__/parser.test.ts`. * fix(labels): update 'Svenskt företag' to 'Svenskt företag eller organisation' for clarity * feat(import): refactor encoding handling for Swedish files and add tests for character preservation * feat(recapt): implement clearRecaptIdentity function and integrate into logout flow * feat(bookkeeping): implement copy functionality and next voucher sequence retrieval * feat(import): enhance customer and supplier import functionality with normalization and event handling |
||
|
|
4131db2894 |
chore: MCP intent-tools, BankID enrichment table, multi-tenant fixes (#402)
* chore: MCP intent-tools, BankID enrichment table, multi-tenant fixes MCP server gains six intent-shaped tools that collapse multi-call agent flows into one: vat_close_check, query_journal, auto_match_period, create_supplier_invoice_from_inbox, audit_package, year_end_readiness. Tools wired into TOOL_SCOPE_MAP and OPERATION_RISK_TIERS as appropriate (create_supplier_invoice_from_inbox at medium tier — reversible until approve, but stages a leverantörsskuld). BankID enrichment now persists to a dedicated bankid_enrichment table keyed by user_id. extension_data has been company-scoped (NOT NULL company_id) since the multi-tenant refactor, so every BankID signup has silently been failing the enrichment upsert. Select-company picker reads from the new table. delete_last_voucher (BFNAR 2013:2) needs to clear document_attachments.journal_entry_id before deleting the entry, but the new document immutability trigger blocks that UPDATE. Added the same gnubok.allow_delete transaction-scoped bypass pattern used by the journal-entry/line/retention triggers. pg-real tests cover the happy path, the unauthorized direct UPDATE, and the swap-to-different-entry attempt under the bypass flag. fiscal_periods.no_overlapping_fiscal_periods exclusion was scoped to user_id from before multi-tenant — rebound to company_id so the same user can have overlapping fiscal years across companies they own/are member of. Also adds scripts/seed-demo-account.ts for end-to-end demo seeding (two companies, full FY2025, active FY2026 with mixed state). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(pr-402): address review feedback Migrations - Drop 20260506140000_document_journal_entry_immutability_delete_bypass.sql: redundant with 20260506140000_document_journal_entry_immutability_bypass.sql that landed on main while this branch was open. Both share the same gnubok.allow_delete pattern; main's version is what the DB actually has. - Rename 20260506150000_bankid_enrichment_table.sql → 20260506160000_bankid_enrichment_table.sql to clear the timestamp clash with 20260506150000_protect_document_journal_link.sql on main (Supabase branch preview was failing on schema_migrations PK collision). Tests - Drop the swap-under-flag test from delete-last-voucher.pg.test.ts: main's bypass returns NEW unconditionally when gnubok.allow_delete='true', so the swap is permitted. Drop the duplicate happy-path test (already covered by 'clears journal_entry_id on attached documents and deletes the voucher'). Keep the unauthorized-direct-UPDATE test. - Add bankid-enrichment.pg.test.ts covering the SELECT RLS policy: user reads own row, cannot read another user's row, INSERT denied for authenticated. gnubok_query_journal - amount_min/amount_max is applied post-fetch (PostgREST can't OR abs(debit) and abs(credit) cleanly), but PostgREST's count is computed pre-filter. Reporting that as total_lines mislead agents into paginating a tail that was already filtered out. When the amount filter is applied, anchor total_lines and truncated to the filtered set and surface db_matched_pre_amount_filter + amount_filter_applied_post_fetch separately. - Escape `_` in the free-text LIKE filter so a search for "2_441" doesn't match "2X441". VAT close check - Reverse-charge blocker no longer fires on ruta 30 (seller-side domestic omvänd skattskyldighet) — the seller books no VAT, the buyer does, so missing ruta 48 is expected. Now scoped to ruta 31/32 (EU acquisition) where the buyer must book both calculated output (2615) and matching ingående moms (2645). - High-value receipt threshold no longer reads journal_entries.total_amount (column doesn't exist; check silently never fired). Sums debits across the entry's lines, which equals the gross for ordinary purchase entries — comparing a gross figure against the BFL/ML 4 000 SEK threshold per ML 17 kap 26–28 §. seed-demo-account.ts - Require an explicit email argument; refuse to run with the previously hardcoded fallback that would silently target a real user. Ensure email is non-undefined for downstream typing. - Type the supabase fiscal_periods insert result locally so tsc no longer reports 'fp implicitly any' from the loose untyped client. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(test): adjust fiscal-period-start-day pg test for per-company overlap The pg-real failure on PR #402 was a latent bug surfaced by this branch's fiscal_periods exclusion constraint flip from user_id to company_id (migration 20260506140100). The test was inserting periods that overlapped seedCompany's default 2026-01-01..2026-12-31 period; the previous constraint slipped past it because the test's INSERT didn't set user_id (NULL escapes the WITH = match), so two same-company overlapping periods silently coexisted. Now that the constraint correctly fires per company, pick years that don't overlap with the seeded 2026 period. The trigger's behavior under test (allow mid-month start when no earlier period exists, allow back-dated SIE imports, reject mid-month start when an earlier period exists) is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(vat-close-check): correct reverse-charge/import blocker rutor Rutor 30/31/32 are the buyer's calculated utgående moms on reverse- charge purchases (domestic byggtjänster/electronics → 2614 → ruta 30; EU goods → 2624 → ruta 31; EU services → 2634 → ruta 32). The buyer must also book matching ingående moms (2647 inhemskt / 2645 utlandet → ruta 48). The previous fix removed ruta 30 on the basis that it was seller-side; that's incorrect — domestic-RC sellers book no VAT at all (they report only beskattningsunderlag on ruta 41), so 2614 only sees buyer-side entries. Restore ruta 30. Also extend the check to import rutor 60/61/62 (non-EU import VAT declared via momsdeklaration since 2015 — 2615/2625/2635). Same mechanic: importer books output VAT on these rutor and deducts the input side via ruta 48. SaaS-from-AWS / OpenAI / Vercel companies hit this path; without including 60/61/62 the blocker would silently miss their misbookings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(mcp): expose ruta 60/61/62 (import VAT) on the local VatReportResult The vat-close-check fix referenced vatReport.rutor.ruta60/61/62 but the MCP server's local VatReportResult type only carries ruta 05-49. Build broke on tsc. Extend the MCP server's slim VAT report to also project import VAT — 2615 → ruta 60 (25%), 2625 → ruta 61 (12%), 2635 → ruta 62 (6%) — and fold those into ruta 49 (att betala/återfå). Mirrors the BAS-to-Ruta mapping in lib/reports/vat-declaration.ts. Output schema and required list updated accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1a6b407a60 |
Supp/verifikationer inconsitency (#369)
* feat(bookkeeping): implement reset bookkeeping functionality with safeguards * feat(migrations): restore relaxed trigger for retroactive first fiscal year |
||
|
|
24107338fa |
Fix/balance inconsitency (#306)
* feat: implement fiscal period date fields component and validation logic * feat: update fiscal period validation and naming logic * feat: implement RPC for computing prior opening balances - Added `compute_prior_opening_balances` RPC to aggregate opening balances for balance-sheet accounts when no opening balance entry is set. - Updated tests across various reports to utilize the new RPC for fetching prior balances. - Refactored `getOpeningBalances` to call the RPC when necessary, improving performance and reliability. - Introduced a script to repair fiscal period chains for companies with broken periods, ensuring proper linking and continuity. - Enhanced error handling and validation in the repair script to ensure data integrity during the process. * feat: implement duplicate opening-balance repair for multi-year SIE imports * feat: enhance SIE entry listing and deduplication logic for opening balances * fix: refine companyHasPriorActivity logic to exclude storno entries and improve balance counting |
||
|
|
885dd8a2e4 |
feat: implement fiscal period date fields component and validation logic (#301)
* feat: implement fiscal period date fields component and validation logic * feat: update fiscal period validation and naming logic |
||
|
|
b5df2fb292 |
feat: invoice-inbox polish + SIE source voucher traceability (#299)
* fix: consolidate commit_journal_entry to single 4-arg signature Replaces the phantom-overload drop migration with an idempotent consolidation that leaves only the 4-arg-with-defaults signature, callable with either 2 or 4 named args. Fixes the "Could not choose the best candidate function" ambiguity caused when the commit-metadata migration CREATE OR REPLACE'd a 4-arg version alongside the existing 2-arg one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: preserve SIE source voucher identity on journal entries Adds source_voucher_series / source_voucher_number columns to journal_entries so per-verifikat traceability survives the importer's skip-empty-voucher logic. The SIE importer populates the original series/number even when skipped vouchers cause gnubok's target numbering to drift from the source file's sequence. Required for BFNAR 2013:2 kap 8 behandlingshistorik. - Migration adds columns + partial index + extends immutability trigger - importVouchers() records rawSeries/rawNumber per voucher - JournalEntry type + test fixtures gain the new fields - Bookkeeping detail page surfaces "Ursprungligt verifikat" when present Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: polish invoice-inbox workspace for production use - Bedrock image fit: shrink images > 5 MB via sharp before Bedrock upload so HEIC/high-res phone photos don't fail with the 5 MB cap - Swedish error mapping: toSwedishInboxError translates Bedrock / infrastructure errors to Swedish sentences stored in error_message - History timeline endpoint (GET /items/:id/history) returns the processing_history events correlated to the inbox item - Workspace UI: inline diagnostic timeline inside the convert dialog, same-email row grouping ("+N dokument" chip), inferred-VAT affordance with "needs review" signalling, Riksbanken exchange-rate prefill for foreign-currency invoices so the supplier-invoice create path populates *_sek audit columns Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: extend inbox-smart-match to supplier invoices Both receipts and supplier invoices expose structurally identical match anchors (date, amount, currency, counterparty name) so the matcher can reuse the same narrowing + LLM prompt. Adds getMatchAnchors() as a shared extractor across ReceiptExtractionResult / InvoiceExtractionResult, and updates the event handlers to process supplier_invoice items alongside receipts. LLM prompt re-phrased as "dokument" rather than "kvitto" and loosened the date-window heuristic since invoice payments can lag behind the invoice date by weeks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: drop unused category selector from TransactionForm The manual "Lägg till transaktion" dialog predates the current categorization flow (SwipeCategorizationView, BatchCategorySelector, AI suggestions). The category dropdown here never drove journal-entry creation — onSubmit fanned it out to CreateTransactionInput.category, which is optional. Removes the dropdown, the unused watch() hook, and the categories lookup table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(migrations): restore drop-phantom file and rebump timestamps Supabase branch DB failed with PK violation on schema_migrations because my two migrations collided with timestamps already on main: 20260421120000 → journal_entries_with_related_rpc (PR #298) 20260421130000 → drop_legacy_supplier_invoice_user_id_uniqueness (PR #296) Rebumped to 20260421140000 and 20260421150000 so each migration has a unique version (Supabase uses only the 14-digit prefix as the PK). Also restored the 20260420130000_drop_phantom_commit_journal_entry_overload migration I had deleted — CLAUDE.md rule #5 forbids modifying existing migrations. My consolidate migration is still compatible: drop_phantom drops the 4-arg overload (no-op where absent), then consolidate recreates it with defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(inbox-smart-match): anchor invoices on dueDate with wider window The original ±7d window around invoiceDate filtered out all real payments for invoices with standard 30–60 day terms — the matcher would see zero candidates before the LLM was called, making the supplier-invoice matcher effectively dead. New anchor selection: - Receipts: receipt date ±7 days (unchanged; paid on the spot) - Invoices with dueDate: dueDate ±14 days (covers early/late payments) - Invoices without dueDate: invoiceDate -7/+45 days (covers 30-day terms) MatchAnchors now carries windowDaysBefore/After so the window can vary per document shape. Added three getMatchAnchors tests asserting window sizes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
23664e79cb |
feat: multi-series SIE import, reusable FiscalYearSelector, library templates in picker (#278)
* feat: multi-series SIE import, reusable FiscalYearSelector, library templates in picker - SIE import preserves each voucher's source series (B/C/I/V/...), essential for Fortnox migrations where series carry semantic meaning (kundfakturor, inbetalningar, etc.). Target numbering still goes through next_voucher_number per series; source (series, number) is stored in the migration mapping for BFNAR 2013:2 audit trail. - Execute route reads company_settings.default_voucher_series as the fallback for vouchers arriving without a series (SIE4I). - Extract shared FiscalYearSelector component; adopt in /reports and /bookkeeping. - Transaction TemplatePicker now surfaces user-created library templates (company + team scope) alongside the static registry, with a helper to convert simple library templates into the BookingTemplate shape. - Exclude 8999 "Årets resultat" from income statement financial section and monthly breakdown so year-end closing entries don't cancel the net result. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: skip Bokio SIE regression when fixtures are absent /dev_docs is gitignored (contains anonymised customer exports), so the integration test can't find its input files in CI. Gate the suite on fixture presence so it still runs locally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address Greptile review feedback - convertLibraryToBookingTemplate: default entity_applicability to 'all' when the source template has no entity_type, so TemplatePicker doesn't silently hide it for companies with a set entity type. - FiscalYearSelector: fire onReady in the no-company early-return branch so consumers (e.g. ReportsPage) don't get stuck in a loading skeleton while the company context is still hydrating. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8ed943198f |
fix: allow retroactive first fiscal year via SIE import (#265)
The enforce_period_start_day trigger and its sie-import pre-validation rejected any non-first-of-month period_start whenever *any other* fiscal period existed for the company. That blocked a real user flow: after onboarding creates a default period (e.g. current year, day 1), importing an SIE for an older förlängt första räkenskapsår (e.g. 2017-07-28 – 2018-12-31) failed with "Non-first fiscal period must start on the 1st of a month". Per BFL 3 kap., the chronologically first fiscal year is the one that may be 6–18 months and start mid-month — which is a property of *when* the period starts relative to others, not of insert order. The trigger and pre-validation now allow mid-month start iff no existing period starts earlier. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c8a5f044c3 | feat: add ensureFiscalPeriod function and related tests for fiscal period validation (#264) | ||
|
|
a3fea6fb7c |
feat: add opening balance import functionality (#238)
- Implemented OpeningBalanceResultStep component to display results of the import process, including success messages and error handling. - Created OpeningBalanceUploadStep component for file upload with drag-and-drop support, including validation for accepted file types. - Developed column detection logic in column-detector.ts to identify account number, name, debit, credit, and balance columns based on headers and data. - Added parser functionality in parser.ts to handle parsing of opening balance files, including validation and BAS account matching. - Created tests for column detection and parsing logic to ensure accuracy and reliability. - Defined types for detected columns and parsed rows in types.ts to improve type safety and clarity in the codebase. |
||
|
|
9753f18533 |
fix: address user feedback — RC preview, bank sync lookback, CSV import robustness (#233)
Three confirmed issues from user feedback: 1. Reverse charge preview now uses per-item VAT rates and correct accounts (2645/2647, 2614/2624/2634) instead of hardcoded 25%/2614 2. Bank sync uses 90-day lookback on first sync (when last_synced_at is null) instead of hardcoded 7 days for all syncs 3. Bank file import improvements: - Shared date normalizer supporting DD.MM.YYYY, DD/MM/YYYY, YYYYMMDD - Silent row skips now reported with reason in issues[] - Decimal separator mismatch detection in generic CSV - Swedish error message with format diagnostics on detection failure - Date format selector in column mapping UI Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
258a64a849 |
feat: allow replacing completed SIE imports (#227)
* feat: allow replacing completed SIE imports Users who import a SIE file, make adjustments in the source system, and re-export can now replace the old import instead of being permanently blocked by the "overlapping fiscal year" guard. The old import's entries are cancelled (posted → cancelled) and the import is marked as 'replaced'. Nothing is deleted — full audit trail preserved per BFL 5 kap 5§ (rättelse) and BFNAR 2013:2 kap 8. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — atomic RPC, locked_at check - P1: Wrap entry cancellation + import status update in a single DB RPC (replace_sie_import) to prevent inconsistent state on partial failure - P2: Check locked_at in addition to is_closed for fiscal period guard - P2: Use RPC return value for accurate cancelled entry count Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
bca00cc5bd |
fix: BankID fallback, bank import fixes, account deletion (#216)
* fix: BankID graceful fallback, bank import fixes, and account deletion - BankID: surface service_unavailable state in login/register with password fallback messaging; structured error codes in tic extension; poll failure counter in BankIdAuth avoids infinite retry when TIC API is down. - Transactions: allow deleting unbooked bank-synced and imported transactions (only posted entries remain protected); detect reconnect duplicates by also checking unbooked bank-synced rows in content-based dedup. - Enable Banking: key external_id by account iban/uid instead of connection id so reconnects don't create duplicates. - Bank import: detect SEB privatbanken CSV variant (Bokföringsdatum / Valutadatum headers) via regex. - Banking settings: replace full-screen sync loader with toast notifications. - delete_user_account: raise statement_timeout, pre-clear NO ACTION FK references, and disable audit/immutability triggers during CASCADE. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — ingest dedup scoping and migration EXCEPTION handler - ingest: split buildExistingTransactionMap into two maps. Booked rows (any source) remain consumed by any incoming raw transaction, but unbooked enable_banking slots are only consumed when the incoming raw transaction is also enable_banking. This preserves reconnect dedup while preventing false positives where a pending bank-synced row silently blocks a legitimately separate CSV row with the same date/amount. - delete_user_account: add EXCEPTION WHEN OTHERS handler that re-enables every legally required enforcement trigger before re-raising. Postgres transactional DDL already rolls back on abort, but the explicit guard makes the intent unambiguous and covers sub-transaction edge cases so enforcement triggers are never left disabled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
6ccd4f429c |
fix: Swedish VAT compliance — representation, domestic RC, full 26xx mapping, SIE (#206)
* feat: add INK2 declaration improvements, invoice delivery date, and Swedish compliance skills Expand INK2 engine with full INK2S/INK2R support and improved SRU generation. Add delivery_date field to invoices and corresponding PDF/migration support. Add Claude skills for Swedish asset accounting, invoice compliance, SIE import/export, SRU filing, and tax planning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review — map BAS 4500–4899, strip CRLF in SRU, document P3 - Map BAS accounts 4500–4599 (legoarbeten), 4700–4899 (diverse varuinköpskostnader) to SRU 7512 so they are not silently dropped from INK2R declarations - Strip \r\n in sanitizeString to prevent CRLF injection in SRU fields - Document P3 period suffix limitation for brutet räkenskapsår Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: correct BAS 4500-4599, 4700-4899 mapping from 7512 to 7511 Per the official BAS-to-SRU mapping, these account ranges are cost of goods (legoarbeten, inkurans, svinn) and belong under 7511 (Råvaror och förnödenheter), not 7512 (Handelsvaror). 7512 remains 4600-4699. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Swedish VAT compliance — representation VAT, domestic RC, full BAS 26xx mapping, SIE encoding - Representation expenses now default to reduced_12 VAT (ML 13 kap 24-25 §§); income tax deduction was abolished 2017 but VAT deduction at 12% remains - Domestic reverse charge (byggtjänster etc.) uses 2647 instead of 2645, with distinct line descriptions for Swedish vs EU/non-EU RC - VAT declaration maps all BAS 26xx variant accounts (egna uttag 2612/2622/2632, uthyrning 2613/2623/2633, VMB 2616/2626/2636, import 2615/2625/2635, domestic RC 2647, frivillig skattskyldighet 2642) and revenue variants (3108/3105/3004/3100) to correct momsdeklaration rutor - SIE parser: remove unreliable #FORMAT PC8 encoding detection (most software exports UTF-8 with PC8 header), parse #FLAGGA for import-already-done warning, default SIE type to 1 when absent, fix RTRANS/BTRANS documentation - SIE export: add #RAR -1 (previous fiscal year), fix UB = IB + movements - Error messages: add pattern matching for locked period trigger errors Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — update ruta49 JSDoc, use null sentinel in error map Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
211033410c |
Fix/import data (#200)
* fix: enhance import data handling and consent management across components * feat: Enhance SIE import functionality with validation and error handling improvements - Added validation errors and warnings state management in SIEImportWizard. - Improved error handling for duplicate, validation, and parsing errors during SIE file import. - Enhanced user feedback with actionable guidance for common import errors. - Updated SIEUploadStep to display validation errors and warnings. - Improved error messages in API routes for better clarity and user experience. - Added file size and type validation in the SIE parse route. - Enhanced parsing logic to provide more detailed error messages for unbalanced vouchers and missing amounts. - Created a new storage bucket for SIE file archival in Supabase with appropriate policies for user access. - Updated tests to reflect changes in error messages and validation logic. * fix: Improve type assertion for response in getPage method * Update extensions/general/arcim-migration/lib/migration-orchestrator.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update supabase/migrations/20260408130000_sie_files_storage_bucket.sql Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix: Add company ID verification for consent handling in accept and disconnect endpoints --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> |
||
|
|
20818e3283 |
fix: Nordea Datum CSV variant + bank sync loading screen (#195)
* fix: support Nordea Datum CSV variant and add bank sync loading screen Add 4th Nordea Business CSV format variant that uses standalone "Datum" column header and YYYY/MM/DD date format. Also replace fire-and-forget bank sync with an awaited flow showing a loading screen after first bank connection, preventing users from navigating away before sync completes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback — cleanup timeout, deps, naming - Clear success setTimeout on unmount via useRef to prevent stale updates - Add isSyncing to useEffect dependency array for Strict Mode safety - Rename headers_detect to headersDetect (camelCase consistency) - Remove redundant toLowerCase() since firstLine is already lowercased Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
a25e75be25 |
fix: Nordea Business CSV variants, API keys UI polish, transaction categorization (#183)
* fix: MCP OAuth 303 redirect, send dialog auto-close, bank details null payload - OAuth authorize: use 303 See Other instead of default 307, which preserved POST method and caused Claude's callback to return 405 - SendInvoiceDialog: close dialog and show toast after email send instead of leaving a success message that requires manual close - BankDetailsSetupDialog: omit empty fields from payload instead of sending null, which fails Zod validation on non-nullable schema fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove dead sentMessage state and fix stale comment Remove sentMessage state, its success banner JSX, and the CheckCircle2 import — all unreachable after the dialog now auto-closes on email send. Fix stale "to null" comment in BankDetailsSetupDialog. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: support Nordea Business CSV variants, polish API keys UI, fix transaction categorization - Extend Nordea Business bank file parser to handle three CSV export formats (classic, Betalare/Mottagare variant, Bokföringsdatum variant) with proper detection guards against SEB/LF misidentification - Rework ApiKeysPanel: add CopyBlock component, destructive confirm on revoke, collapsible API-key-based connection methods, Claude.ai OAuth instructions as recommended path, simplified scope badges - Stop deriving is_business from category on manual transaction creation; set null so categorization flow handles it correctly - Show categorize button when journal_entry_id is missing regardless of is_business value Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review — await clipboard, fix zero-scope label, simplify condition - Await navigator.clipboard.writeText and catch failures - Change zero-scope label from "Enbart läs" to "Inga behörigheter" - Simplify redundant ternary condition in TransactionHistoryList Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: repair broken ternary in TransactionHistoryList JSX Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
8855ef1485 |
fix: use range overlap for duplicate SIE period check (BFL 4:1) (#163)
* fix: use range overlap for duplicate period check (BFL 4:1 compliance) The period duplicate check used exact-match on fiscal_year_start/end, missing overlapping periods (e.g., partial-year file vs full-year import). Now uses standard interval overlap test (start <= other_end AND end >= other_start). Updated Swedish error messages to reflect overlap semantics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align JSDoc citation to BFL 4:1 for period overlap check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
d23cb4c859 |
fix: SIE import duplicate check, performance, and UX improvements (#162)
* fix: catch duplicate SIE import early with clear error message - Add duplicate check in execute route before doing any work (defense in depth) - Handle duplicate error from execute route in frontend - Show "Filen har redan importerats" heading instead of generic "Kunde inte läsa filen" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: catch duplicate SIE import early and batch account creation for performance - Add duplicate check in execute route before doing any work (defense in depth) - Handle duplicate error from execute route in frontend with clear Swedish message - Replace sequential ensureAccountExists loop (50-100 DB round trips) with single batch SELECT + batch INSERT — reduces import time from 3+ min to seconds Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update mappings optimistically after creating missing accounts Previously re-parsed the SIE file after account creation, which could fail with a 409 duplicate error (leaving the "create accounts" card stuck). Now optimistically marks created accounts as self-mapped and updates the preview stats immediately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile P2 feedback — error handling and typed errorType prop - Check batch insert error in executeSIEImport account creation safety net - Replace brittle string-match error detection with typed errorType prop - Remove stale file dependency from handleCreateAccounts useCallback Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
9dceb6285c |
fix: SIE import voucher range bugs + remove describe transaction (#161)
* fix: fix SIE import voucher range bugs from multi-tenant migration and remove describe transaction feature - Fix reserve_voucher_range RPC parameter mismatch (p_user_id → p_company_id) causing duplicate voucher errors - Fix create-accounts onConflict from user_id to company_id - Add reserve-then-adjust pattern: pre-reserve voucher range before batch insert, release unused range on partial failure - Add release_voucher_range DB function for safe rollback - Remove DescribeTransactionDialog, describe/batch-describe API routes, and related schemas (feature superseded) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — voucher range race condition and orphaned entries - Add p_reserved_highest upper-bound guard to release_voucher_range to prevent rolling back past numbers claimed by concurrent operations - Move highestInsertedVoucher tracking to after both headers AND lines succeed, preventing orphaned journal entries with no lines from being counted as "used" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
d0b3f21bde |
feat: remove AI extensions, restructure settings, and add atomic voucher commits (#157)
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> |
||
|
|
0dd1f5ebc1 |
feat: multi-tenant company refactor (GNU-19) (#153)
* 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> |
||
|
|
d9c95a7b59 |
feat: counterparty templates with multi-line patterns, batch matching, and settings cleanup (#118)
* feat: separate AR/AP/accounting into distinct nav groups (#92) Split the flat "Finans" sidebar group into three visually distinct sections — Försäljning (AR), Inköp (AP), and Redovisning — so users coming from Fortnox immediately find customer invoicing and supplier invoices as top-level concepts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: journal entry detail view, correction chain, and account name display - Add journal entry detail page at /bookkeeping/[id] with full entry view - Add correction chain API and component showing storno relationships - Add JournalEntryStatusBadge component for entry status display - Show debit/credit account names in template picker and review dialogs - Expand client-side BAS account name mapping with additional accounts - Show account codes on transaction inbox suggestion buttons Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback — N+1 query, duplicate name, nav dedup - Batch reverse-lookup into single query per BFS iteration (was N+1) - Differentiate account 2393 from 2893 in display names - Extract shared loop for desktop/mobile nav group rendering Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: counterparty templates, Skatteverket extension, VAT form completeness, and UI cleanup - Add counterparty-based categorization templates (learned from user approvals and auto-ingestion) with fuzzy matching in the mapping engine - Add Skatteverket extension for direct VAT declaration submission via API - Complete VAT declaration form with all 30 SKV 4700 boxes (ruta 08, 35-42, 50, 60-62) - Fix ruta 49 formula to include import VAT (ruta 60+61+62) - Simplify dashboard UI: remove redundant icons from stat cards, customer cards, invoice list, supplier invoices; use Badge variants consistently - Add SkatteverketPanel component to reports page - Add categorization_templates and skatteverket_tokens migrations - Update tests and helpers for new types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback — VAT detection, migration timestamps, dedup - Fix detectVatTreatment to derive actual rate (12%/6%) from VAT line description instead of hardcoding standard_25 - Rename skatteverket_tokens migration to 20260324120001 to avoid duplicate timestamp with categorization_templates (fixes Supabase deployment failure) - Make refreshAccessToken accept previousRefreshCount param to enforce refresh limit contract at the type level - Fix rate limiter TOCTOU by claiming slot before await - Extract formatRedovisare/formatRedovisningsperiod to shared lib/skatteverket/format.ts — eliminates duplication between mappers.ts and SkatteverketPanel.tsx Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: multi-line counterparty templates, batch matching, settings restructure Counterparty template engine: - Multi-line booking patterns (line_pattern JSONB) for complex entries with split VAT, tax accounts, and ratio-based allocation - Batch matching (findCounterpartyTemplatesBatch) — 1 DB query for all transactions instead of up to 3 per transaction - SIE voucher population (populateTemplatesFromSieVouchers) — extracts patterns from historical vouchers on import with dominance filtering - Source priority system (user_approved > sie_import > auto_learned) - Centralized counterparty: prefix helpers to prevent string fragility - Fix: re-approval path now updates line_pattern Transaction categorization: - /describe route returns counterparty_match in parallel with templates/AI - /categorize route accepts counterparty_template_id for direct booking - /suggest-categories uses batch matching, injects counterparty suggestions - transaction-entries supports all_lines_complete for multi-line patterns UI: - TemplatePicker shows "Tidigare motparter" section (no AI extension needed) - DescribeTransactionDialog shows counterparty match card with detail - QuickReviewDialog supports counterparty line patterns - JournalEntryPreview renders multi-line patterns with VAT/ratio math - Inline LinePatternEntry types replaced with shared import from @/types Settings restructure: - 8 tabs → 5: merged Säkerhet + Utseende + Kalender into Konto - Renamed "Motparter" → "Mallar" - CounterpartyTemplatesPanel: click-to-expand detail view with account lines, VAT, confidence, aliases, and delete Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review — account_override guard, DELETE body parsing, stale test - Block account_override when counterparty_template_id is set (prevents corrupting stored template via override → upsert correction path) - Wrap DELETE request.json() in try-catch for malformed body (400 not 500) - Clean up stale 3-query mock enqueues in test for batch-based find Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0ba1db1c21 |
fix: skip BTRANS/RTRANS in SIE parser to prevent false balance errors (#63)
When Fortnox corrects a voucher, the SIE file contains #BTRANS (added) and #RTRANS (removed) lines alongside #TRANS (final state). The parser was summing all three, causing corrected vouchers to appear unbalanced. Only #TRANS represents the final voucher state — BTRANS/RTRANS are correction audit trail. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
93413a8fd0 |
fix: resolve SIE import 504 timeout and clean up migration preview (#33)
* feat: import system improvements, INK2 fix, and Swedish text corrections - 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> * feat: arcim migration wizard UX fixes, Sentry setup, and extension scaffolding Arcim migration wizard improvements: - Progress bar now excludes non-interactive steps (migrating/result) - Fix OAuth text to match target="_blank" behavior (new tab, not redirect) - Display month names instead of "Månad X" in preview - Fix Swedish typo "förifylla" in no-company-info message - Replace native checkboxes with shadcn Switch in options step - Add ConfirmationDialog before starting migration - Show progress percentage during migration - Add "Nästa steg" guidance and navigation links in result step - Add "Försök igen" button in error state (returns to options) - Add Bokio company ID help text (GUID from URL) - Add Fortnox integration add-on hint on connection failure Also includes: SIE import system improvements, INK2 fixes, Swedish text corrections, Sentry error tracking setup, and arcim-migration extension scaffolding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback - Fix OAuth error recovery blank page (restore provider from URL params) - Pass real userId to MigrationWizard instead of empty string - Remove ~50 debug console.log statements from sie-import.ts - Fix comment referencing account 3740 → 3741 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: comprehensive UI design audit and normalization Dashboard audit: - Fix muted-foreground contrast (4.31:1 → 5.08:1) for WCAG AA - Add prefers-reduced-motion media query for all animations - Replace border-l-2 accent anti-pattern with subtle full-border colors - Add aria-expanded to toggle buttons, role="status" to live counters - Fix touch targets on deadline buttons (28px → 36px) - Vary section spacing for rhythm (mb-12/mb-10/mb-8) - Remove unused imports and dead code Transactions audit + hardening: - Add pagination (200 per page) with "Ladda fler" button - Replace height animation with transform-only exit animation - Show batch progress in floating action bar during processing - Fix batch bar mobile overlap (bottom-20 on mobile) - Replace clickable badges with proper button elements - Add safe area padding to fullscreen swipe view - Add response.ok check to suggestion fetch - Add truncation to invoice number buttons Invoicing audit: - Remove border-l-4 accent pattern from invoice cards - Replace string concatenation with cn() utility Systemic sweep (34 files): - All page headings: font-bold → font-display font-medium (Fraunces) - All stat numbers: font-bold → font-display font-medium tabular-nums - All hard-coded blue/amber/emerald colors → design tokens - Remove all dark mode overrides (tokens handle automatically) - Tint pure white card background to 99% Design context added to CLAUDE.md with brand personality, aesthetic direction, and 5 design principles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: bookkeeping flow audit — design system, accessibility, UX - Replace raw <select> with shadcn Select component (JournalEntryForm) - Add confirmation dialog for account deletion (ChartOfAccountsManager) - Remove console.error from production code (JournalEntryList, JournalEntryForm) - Fix contradictory h-7/min-h-[44px] button sizing → h-10 (ChartOfAccountsManager) - Increase BAS catalog "Lägg till" touch target h-7 → h-9 - Improve loading state with spinner (JournalEntryList) - Improve empty state with icon, description, and guidance (JournalEntryList) - Add response.ok check on journal entry fetch - Add aria-expanded to entry expand buttons - Add tabular-nums to desktop debit/credit columns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: onboarding and empty state improvements Onboarding: - Replace font-serif with font-display (Fraunces) for brand consistency - Remove console.error calls from production code Empty states: - Fix broken /transactions/new link in EmptyTransactions (route doesn't exist) - Add actionHref fallback to EmptyCustomers when no onAction prop provided - Improve EmptyTransactions description copy Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: clarify Swedish UX copy — terminology, errors, descriptions Terminology consistency: - "Försenad" → "Förfallen" for overdue invoices (customers/[id]) - "bokföringsorder" → actionable description in bookkeeping page - "verifikation har bifogats" → "underlag har bifogats" in doc warning - "Fortsätt ändå" → "Bokför utan underlag" (specific action) Error messages — replace generic "Fel" + "Något gick fel" with specific: - "Något gick fel vid bokföring" → "Transaktionen kunde inte bokföras" - "Något gick fel vid matchning" → "Transaktionen kunde inte matchas" - "Kunde inte hämta X" → "Kunde inte ladda X" + recovery hint - Add "Försök igen" guidance to all error toasts Page descriptions — replace redundant with actionable: - Invoices: "Skapa och hantera" → "Skicka, följ betalningar, skapa kreditnotor" - Bookkeeping: list of features → actionable description Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: design critique — dashboard affordance, reports description Dashboard: - Add ChevronRight indicator to clickable summary cards (Att få betalt, Koppla bank) to distinguish from static cards - Add cursor-pointer to linked cards Reports: - Replace feature list description with actionable guidance "Huvudbok, grundbok..." → "Generera skattedeklarationer..." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace generic "Fel" error toasts with specific messages Deadlines: 5 generic "Fel" → specific per-action titles (create, toggle, edit, delete, load) Expenses detail: 5 generic "Fel" → specific per-action titles (load, approve, pay, credit, delete) Expenses new: 3 generic "Fel" → instructional validation messages (supplier name, supplier selection, invoice number) Customers: 1 generic "Fel" → specific load error with recovery hint All error toasts now follow pattern: title = what failed, description = how to recover Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace all remaining generic "Fel" error toasts (37 instances) Systematic sweep across 12 dashboard pages replacing generic title: 'Fel' with context-specific error titles: - Load errors: "Kunde inte ladda [resurs]" - Action errors: "[Åtgärd] misslyckades" - Validation: "[Fält] saknas" Every error toast now tells the user what failed without needing to read the description. Recovery hints added where missing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: import flow — normalize stat typography, remove console.warn - Replace font-bold with font-display font-medium on 13 stat numbers across SIEPreviewStep, BankFilePreviewStep, BankFileConfirmStep, ImportResultStep (missed by systemic sweep since these are in components/import/, not app/(dashboard)/) - Add tabular-nums to stat numbers displaying counts/currency - Remove console.warn in ArcimMigrationWorkspace Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: final cleanup — console statements, remaining font-bold stats Remove production console statements: - Step1EntityType: remove debug console.warn (dead code after onNext) - TransactionBookingDialog: remove console.error on doc link failure - JournalEntryAttachments: remove 3 console.error calls Normalize remaining font-bold stat displays: - SwipeCategorizationView: 3 instances (completion, amount displays) - NEDeclarationView: yearly result heading + value Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review feedback loadMoreTransactions: add inbox item enrichment matching fetchTransactions - Paginated transactions now fetch invoice_inbox_items in parallel - Fixes missing document indicator, template suggestions, and inbox match card for transactions loaded via "Ladda fler" fetchAllPages: add maxPages guard (default 500) to prevent infinite loop - If Arcim gateway returns hasMore:true indefinitely, the loop now exits after 500 pages instead of running forever Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: minimize CLAUDE.md — remove derivable content, fix stale data Remove ~230 lines (51% reduction) of content that duplicates what's already in the source code (directory tree, function tables, type definitions, migration lists). Update migration count (63→65), add missing test helpers, fix cron job list. Keep all high-value sections: accounting guard rails, BAS accounts, VAT rutor, design context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: enable banking hardening, arcim entity inference, SIE import fixes, and onboarding improvements - Enable Banking: OAuth CSRF state tokens, JWT caching, retry with timeouts, raw PSD2 response archival (BFL 7 kap), expired/error connection UI, consent expiry notifications, pagination safety limits - Arcim migration: Smarter entity type inference from org numbers, VAT prefixes, company name suffixes (GmbH, Ltd, etc.), and country codes - SIE import: Parser and import fixes with new migration - BAS accounts: Added vehicle accounts (1241, 1242, 1249, 1259) - Dashboard: New SIE import and stale uncategorized transaction queries - Onboarding: Enhanced NewUserChecklist - Period service: Improvements with updated tests - Transaction ingest: Updated logic and tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — credit note type, EU country codes, notification thresholds, migration timestamps - Fix dead ternary: credit notes now correctly stored as 'credit_note' instead of 'invoice' - Add 'GR' (Greece ISO 3166-1) to EU_COUNTRIES alongside 'EL' (VAT prefix) - Fix consent notification condition: fire at exactly 7 days or ≤3 days, not every day in 7-day window - Deduplicate migration timestamps: rename SIE migration to 20260316120100 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve SIE import 504 timeout and clean up migration preview Add maxDuration=300 to extension catch-all and SIE execute routes so large imports don't hit Vercel's default timeout. Add 120s AbortController to Arcim gateway client. Remove empty company info fields from migration preview step — only show SIE stats. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — retriedBatches overcounting, BankConnection type safety - retriedBatches now counts distinct batches that needed retries, not individual retry attempts across both header and line insert loops - Add error_message to BankConnection type, remove unsafe cast Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
98cd253bce |
feat: enable banking hardening, arcim inference, SIE fixes, onboarding (#32)
* feat: import system improvements, INK2 fix, and Swedish text corrections - 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> * feat: arcim migration wizard UX fixes, Sentry setup, and extension scaffolding Arcim migration wizard improvements: - Progress bar now excludes non-interactive steps (migrating/result) - Fix OAuth text to match target="_blank" behavior (new tab, not redirect) - Display month names instead of "Månad X" in preview - Fix Swedish typo "förifylla" in no-company-info message - Replace native checkboxes with shadcn Switch in options step - Add ConfirmationDialog before starting migration - Show progress percentage during migration - Add "Nästa steg" guidance and navigation links in result step - Add "Försök igen" button in error state (returns to options) - Add Bokio company ID help text (GUID from URL) - Add Fortnox integration add-on hint on connection failure Also includes: SIE import system improvements, INK2 fixes, Swedish text corrections, Sentry error tracking setup, and arcim-migration extension scaffolding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback - Fix OAuth error recovery blank page (restore provider from URL params) - Pass real userId to MigrationWizard instead of empty string - Remove ~50 debug console.log statements from sie-import.ts - Fix comment referencing account 3740 → 3741 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: comprehensive UI design audit and normalization Dashboard audit: - Fix muted-foreground contrast (4.31:1 → 5.08:1) for WCAG AA - Add prefers-reduced-motion media query for all animations - Replace border-l-2 accent anti-pattern with subtle full-border colors - Add aria-expanded to toggle buttons, role="status" to live counters - Fix touch targets on deadline buttons (28px → 36px) - Vary section spacing for rhythm (mb-12/mb-10/mb-8) - Remove unused imports and dead code Transactions audit + hardening: - Add pagination (200 per page) with "Ladda fler" button - Replace height animation with transform-only exit animation - Show batch progress in floating action bar during processing - Fix batch bar mobile overlap (bottom-20 on mobile) - Replace clickable badges with proper button elements - Add safe area padding to fullscreen swipe view - Add response.ok check to suggestion fetch - Add truncation to invoice number buttons Invoicing audit: - Remove border-l-4 accent pattern from invoice cards - Replace string concatenation with cn() utility Systemic sweep (34 files): - All page headings: font-bold → font-display font-medium (Fraunces) - All stat numbers: font-bold → font-display font-medium tabular-nums - All hard-coded blue/amber/emerald colors → design tokens - Remove all dark mode overrides (tokens handle automatically) - Tint pure white card background to 99% Design context added to CLAUDE.md with brand personality, aesthetic direction, and 5 design principles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: bookkeeping flow audit — design system, accessibility, UX - Replace raw <select> with shadcn Select component (JournalEntryForm) - Add confirmation dialog for account deletion (ChartOfAccountsManager) - Remove console.error from production code (JournalEntryList, JournalEntryForm) - Fix contradictory h-7/min-h-[44px] button sizing → h-10 (ChartOfAccountsManager) - Increase BAS catalog "Lägg till" touch target h-7 → h-9 - Improve loading state with spinner (JournalEntryList) - Improve empty state with icon, description, and guidance (JournalEntryList) - Add response.ok check on journal entry fetch - Add aria-expanded to entry expand buttons - Add tabular-nums to desktop debit/credit columns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: onboarding and empty state improvements Onboarding: - Replace font-serif with font-display (Fraunces) for brand consistency - Remove console.error calls from production code Empty states: - Fix broken /transactions/new link in EmptyTransactions (route doesn't exist) - Add actionHref fallback to EmptyCustomers when no onAction prop provided - Improve EmptyTransactions description copy Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: clarify Swedish UX copy — terminology, errors, descriptions Terminology consistency: - "Försenad" → "Förfallen" for overdue invoices (customers/[id]) - "bokföringsorder" → actionable description in bookkeeping page - "verifikation har bifogats" → "underlag har bifogats" in doc warning - "Fortsätt ändå" → "Bokför utan underlag" (specific action) Error messages — replace generic "Fel" + "Något gick fel" with specific: - "Något gick fel vid bokföring" → "Transaktionen kunde inte bokföras" - "Något gick fel vid matchning" → "Transaktionen kunde inte matchas" - "Kunde inte hämta X" → "Kunde inte ladda X" + recovery hint - Add "Försök igen" guidance to all error toasts Page descriptions — replace redundant with actionable: - Invoices: "Skapa och hantera" → "Skicka, följ betalningar, skapa kreditnotor" - Bookkeeping: list of features → actionable description Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: design critique — dashboard affordance, reports description Dashboard: - Add ChevronRight indicator to clickable summary cards (Att få betalt, Koppla bank) to distinguish from static cards - Add cursor-pointer to linked cards Reports: - Replace feature list description with actionable guidance "Huvudbok, grundbok..." → "Generera skattedeklarationer..." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace generic "Fel" error toasts with specific messages Deadlines: 5 generic "Fel" → specific per-action titles (create, toggle, edit, delete, load) Expenses detail: 5 generic "Fel" → specific per-action titles (load, approve, pay, credit, delete) Expenses new: 3 generic "Fel" → instructional validation messages (supplier name, supplier selection, invoice number) Customers: 1 generic "Fel" → specific load error with recovery hint All error toasts now follow pattern: title = what failed, description = how to recover Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace all remaining generic "Fel" error toasts (37 instances) Systematic sweep across 12 dashboard pages replacing generic title: 'Fel' with context-specific error titles: - Load errors: "Kunde inte ladda [resurs]" - Action errors: "[Åtgärd] misslyckades" - Validation: "[Fält] saknas" Every error toast now tells the user what failed without needing to read the description. Recovery hints added where missing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: import flow — normalize stat typography, remove console.warn - Replace font-bold with font-display font-medium on 13 stat numbers across SIEPreviewStep, BankFilePreviewStep, BankFileConfirmStep, ImportResultStep (missed by systemic sweep since these are in components/import/, not app/(dashboard)/) - Add tabular-nums to stat numbers displaying counts/currency - Remove console.warn in ArcimMigrationWorkspace Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: final cleanup — console statements, remaining font-bold stats Remove production console statements: - Step1EntityType: remove debug console.warn (dead code after onNext) - TransactionBookingDialog: remove console.error on doc link failure - JournalEntryAttachments: remove 3 console.error calls Normalize remaining font-bold stat displays: - SwipeCategorizationView: 3 instances (completion, amount displays) - NEDeclarationView: yearly result heading + value Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review feedback loadMoreTransactions: add inbox item enrichment matching fetchTransactions - Paginated transactions now fetch invoice_inbox_items in parallel - Fixes missing document indicator, template suggestions, and inbox match card for transactions loaded via "Ladda fler" fetchAllPages: add maxPages guard (default 500) to prevent infinite loop - If Arcim gateway returns hasMore:true indefinitely, the loop now exits after 500 pages instead of running forever Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: minimize CLAUDE.md — remove derivable content, fix stale data Remove ~230 lines (51% reduction) of content that duplicates what's already in the source code (directory tree, function tables, type definitions, migration lists). Update migration count (63→65), add missing test helpers, fix cron job list. Keep all high-value sections: accounting guard rails, BAS accounts, VAT rutor, design context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: enable banking hardening, arcim entity inference, SIE import fixes, and onboarding improvements - Enable Banking: OAuth CSRF state tokens, JWT caching, retry with timeouts, raw PSD2 response archival (BFL 7 kap), expired/error connection UI, consent expiry notifications, pagination safety limits - Arcim migration: Smarter entity type inference from org numbers, VAT prefixes, company name suffixes (GmbH, Ltd, etc.), and country codes - SIE import: Parser and import fixes with new migration - BAS accounts: Added vehicle accounts (1241, 1242, 1249, 1259) - Dashboard: New SIE import and stale uncategorized transaction queries - Onboarding: Enhanced NewUserChecklist - Period service: Improvements with updated tests - Transaction ingest: Updated logic and tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — credit note type, EU country codes, notification thresholds, migration timestamps - Fix dead ternary: credit notes now correctly stored as 'credit_note' instead of 'invoice' - Add 'GR' (Greece ISO 3166-1) to EU_COUNTRIES alongside 'EL' (VAT prefix) - Fix consent notification condition: fire at exactly 7 days or ≤3 days, not every day in 7-day window - Deduplicate migration timestamps: rename SIE migration to 20260316120100 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
2ad8731dc9 |
feat: arcim migration wizard UX, import fixes, Sentry setup (#22)
* feat: import system improvements, INK2 fix, and Swedish text corrections - 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> * feat: arcim migration wizard UX fixes, Sentry setup, and extension scaffolding Arcim migration wizard improvements: - Progress bar now excludes non-interactive steps (migrating/result) - Fix OAuth text to match target="_blank" behavior (new tab, not redirect) - Display month names instead of "Månad X" in preview - Fix Swedish typo "förifylla" in no-company-info message - Replace native checkboxes with shadcn Switch in options step - Add ConfirmationDialog before starting migration - Show progress percentage during migration - Add "Nästa steg" guidance and navigation links in result step - Add "Försök igen" button in error state (returns to options) - Add Bokio company ID help text (GUID from URL) - Add Fortnox integration add-on hint on connection failure Also includes: SIE import system improvements, INK2 fixes, Swedish text corrections, Sentry error tracking setup, and arcim-migration extension scaffolding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback - Fix OAuth error recovery blank page (restore provider from URL params) - Pass real userId to MigrationWizard instead of empty string - Remove ~50 debug console.log statements from sie-import.ts - Fix comment referencing account 3740 → 3741 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
e8fb84b4fd |
feat: import system improvements, INK2 fix, and Swedish text corrections (#10)
- 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> |
||
|
|
a1ca96453c |
feat: mobile UX improvements across dashboard (#9)
* feat: mobile UX improvements across dashboard pages - Responsive layouts with stacked mobile forms and grid desktop views - Safe area handling (viewportFit cover, bottom nav insets, main padding) - Touch-friendly dialogs, tabs, and page headers - Mobile nav drawer open/close animation - Dashboard layout parallel Supabase queries - SIE parser: handle quoted VER/TRANS fields - Skip account override for template-based transaction booking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback from Greptile - Fix nav drawer close timer race condition with useRef + clearTimeout - Show locked VAT rate as read-only label on mobile instead of hiding it - Remove dead bottom-16 class overridden by inline style Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
091d043c85 |
feat: UI polish, lint fixes, onboarding redesign, help page expansion, and test improvements
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> |
||
|
|
15fcb7e067 |
fix: correct account-mapper test to match bas_range fallback behavior
The test expected empty BAS reference to leave 1510 unmapped, but the bas_range fallback correctly self-maps valid 4-digit BAS accounts with confidence 0.9. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
03b569d708 |
refactor: consolidate extension system to general-only with manifest-driven architecture
- 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> |
||
|
|
39e407644d |
feat: unified document inbox, full BAS 2026, and document-transaction matching
- Expand BAS reference from ~180 to ~1,276 accounts (full BAS Kontoplan 2026) with K2 exclusion flags, per-class data files, and computed SRU codes - Evolve invoice inbox into unified document inbox handling invoices, receipts, and government letters with AI-powered classification (Claude Haiku Vision) - Add multi-pass document-to-transaction matching engine with greedy assignment for both supplier invoices (reference/amount/date/name) and receipts (weighted amount/merchant/date scoring) - Add supplier invoice matching in transaction ingest pipeline - Inject booking template suggestions into AI extraction prompts - Surface matched documents in swipe categorization UI with one-tap booking - Auto-activate missing BAS accounts during SIE import against full reference - Add K2 filter toggle in Chart of Accounts manager - Add receipt confirmation route with BFNAR representation fields - Add database migrations for K2 support and document matching columns - Remove obsolete extension migration scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
6956a757f3 |
feat: consolidate booking templates from 100 to 48 and improve codebase
Reduce booking template library to eliminate duplicate suggestions when users describe transactions. Templates with identical accounting treatment (same account + VAT) are merged, keywords consolidated, and the entire subscriptions group is eliminated. Also includes prior work on reports, extensions, and transaction improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |