f3a3d07ed3e33079e4eb00a09f087dd88aaab617
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
dd920355b3 |
fix: use service-role client for processing_history writes (#289)
appendProcessingHistory was accepting a caller-provided SupabaseClient, but processing_history has no INSERT RLS policy (by design — audit trail writes should not be user-controllable). Extension handlers pass the authenticated user client (ctx.supabase), which caused silent RLS violations caught by try-catch — the audit trail was wired up but empty. Switch to creating a service-role client internally, matching the existing event_log pattern (event-log-handler.ts uses createServiceClientNoCookies). Remove the supabase parameter from both appendProcessingHistory and appendProcessingHistoryBatch. Update all 5 call sites in invoice-inbox and inbox-smart-match. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0076aa85f8 |
feat: arcim inbox (Resend Inbound) + smart-match extension + commit metadata (#286)
* 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> * feat: arcim inbox + smart-match extension + commit metadata Three threads, all gated off in extensions.config.json (invoice-inbox and inbox-smart-match are not in the enabled list for this PR). invoice-inbox: Gmail OAuth -> Resend Inbound (v2.0.0) - Remove gmail-scanner / gmail-helpers - Add resend-inbound.ts (webhook verify, attachment fetch) and inbox-provisioning.ts (per-company @arcim.io address with rotation) - Replace /gmail/* routes with /inbox/address and admin-only /inbox/rotate - Workspace UI: card layout + MatchBlock surfacing AI transaction matches - classify-document: tightened discount/total prompt; cap confidence at 50% when line items do not reconcile with amount_incl_vat - Manifest requires RESEND_API_KEY, RESEND_INBOUND_DOMAIN, RESEND_INBOUND_WEBHOOK_SECRET inbox-smart-match (new extension) - Event-driven AI matching of receipts to bank transactions - Listens on inbox_item.classified (match now) and transaction.synced (retro-match receipts waiting for a transaction) - Uses service-role client; processing_history append is scoped by company_id from the event payload commit metadata + audit plumbing - journal_entries gains commit_method and rubric_version columns - commit_journal_entry RPC accepts both (BFNAR 2013:2 behandlingshistorik) - processing-history PII detector strips UUID-shaped substrings before personnummer pattern matching (UUIDs were triggering false positives) - New generic inbox_item.classified event Migrations - arcim_inbox: company_inboxes table, resend_email_id, email_body_text, auto-provision trigger, drops obsolete email_connections - journal_entry_commit_metadata: new columns + updated RPC - inbox_attachment_composite: resend_attachment_id + composite unique index - inbox_smart_match: correlation_id, match_reasoning, expanded match_method CHECK, pending-match and correlation indexes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4fbfadb2b7 |
feat: invoice inbox extension — conversion, workspace UI, Gmail UX (#255)
* feat: invoice inbox extension — conversion, workspace UI, Gmail UX Complete the invoice-inbox extension with full end-to-end flow: - Add POST /items/:id/convert route to create supplier invoices from classified inbox items, with accrual journal entry and document linking - Add PATCH /items/:id/reject route to dismiss non-relevant items - Add workspace UI at /e/general/invoice-inbox with items table, status filtering, convert dialog, and match confirmation - Add Gmail connection banner (connect/disconnect/status) in workspace - Add one-click supplier creation from AI-extracted data - Add transaction auto-matching with fuzzy name + currency-aware amount - Add event emission (received, extracted, confirmed) on classification - Redirect OAuth callback to workspace instead of /settings/banking - Fix extension catch-all body clone for POST routes with path params - Fix duplicate Löner nav entry from salary module merge - Remove summary cards from expenses and supplier invoices pages - Fix supplier-invoices/new amount input (valueAsNumber → Controller) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — company_id filters, currency guard, skipAuth clone - Add company_id filter to reject route update (defense in depth) - Add company_id filter to document_attachments journal entry link - Guard sekMatch with tx.currency === 'SEK' to prevent false matches - Clone request in skipAuth branch for consistency with auth branch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
c4a6d16e94 |
feat: document inbox + fix MCP pending operations user_id (#172)
* fix: always use business PSU type for bank connections EF (sole trader) users connecting to Nordea got personal accounts because psu_type was set to 'personal' based on entity_type. Since gnubok is accounting software, all bank connections should use 'business' PSU type regardless of entity type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: document inbox extension with AI classification Add invoice-inbox extension for email-based document processing with AI-powered classification, supplier matching, and inbox management. Includes MCP tools for document upload/listing, migration, and supporting changes across document service, API keys, and banking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: disable invoice-inbox extension, use dynamic import in MCP server Keep invoice-inbox out of extensions.config.json until ready for production. MCP server now dynamically imports classifyDocument to avoid breaking when the extension is disabled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: correct file size error message in invoice-inbox upload 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> |
||
|
|
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> |
||
|
|
ef5a84a5d5 |
feat: make extension system packageable via enriched ExtensionContext
Enrich ExtensionContext with supabase, emit(), settings, storage, log, and services so extensions can receive everything through dependency injection instead of importing core modules directly. - Add context factory and inject context into event handlers via registry - Move supplier invoice journal entry creation to core event handler - Add services.ingestTransactions to ExtensionContext for enable-banking - Create catch-all API route for extension-declared apiRoutes - Migrate 5 extensions to accept context with dynamic import fallbacks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
59d935f2cc | Invoice fix and new inbound invoice extension etc |