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>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
6956a757f3
commit
39e407644d
@@ -15,7 +15,7 @@
|
||||
DO $$
|
||||
DECLARE
|
||||
-- >>> SET THE TARGET USER EMAIL HERE <<<
|
||||
target_email TEXT := 'user@example.com';
|
||||
target_email TEXT := 'jakob.wennberg@arcim.io';
|
||||
target_user_id UUID;
|
||||
BEGIN
|
||||
-- Resolve email to user ID
|
||||
@@ -45,6 +45,17 @@ BEGIN
|
||||
ALTER TABLE public.audit_log DISABLE TRIGGER USER;
|
||||
ALTER TABLE public.company_settings DISABLE TRIGGER USER;
|
||||
ALTER TABLE public.profiles DISABLE TRIGGER USER;
|
||||
ALTER TABLE public.chat_sessions DISABLE TRIGGER USER;
|
||||
ALTER TABLE public.invoice_inbox_items DISABLE TRIGGER USER;
|
||||
|
||||
-- Temporarily drop fiscal period constraints (migrations 042-043).
|
||||
-- The NOT VALID CHECK constraints are still enforced on UPDATE, so
|
||||
-- the circular-FK-breaking UPDATEs below will fail on rows with
|
||||
-- non-conforming dates. The EXCLUDE constraint can also interfere.
|
||||
-- We re-add them all after deletion.
|
||||
ALTER TABLE public.fiscal_periods DROP CONSTRAINT IF EXISTS no_overlapping_fiscal_periods;
|
||||
ALTER TABLE public.fiscal_periods DROP CONSTRAINT IF EXISTS fiscal_period_start_first_of_month;
|
||||
ALTER TABLE public.fiscal_periods DROP CONSTRAINT IF EXISTS fiscal_period_end_last_of_month;
|
||||
|
||||
-- Break circular / self-referencing FK constraints
|
||||
UPDATE public.fiscal_periods SET closing_entry_id = NULL, opening_balance_entry_id = NULL, previous_period_id = NULL WHERE user_id = target_user_id;
|
||||
@@ -61,6 +72,7 @@ BEGIN
|
||||
DELETE FROM public.invoice_reminders WHERE user_id = target_user_id;
|
||||
DELETE FROM public.supplier_invoice_items WHERE supplier_invoice_id IN (SELECT id FROM public.supplier_invoices WHERE user_id = target_user_id);
|
||||
DELETE FROM public.supplier_invoice_payments WHERE supplier_invoice_id IN (SELECT id FROM public.supplier_invoices WHERE user_id = target_user_id);
|
||||
DELETE FROM public.invoice_inbox_items WHERE user_id = target_user_id;
|
||||
DELETE FROM public.document_attachments WHERE user_id = target_user_id;
|
||||
DELETE FROM public.journal_entry_lines WHERE journal_entry_id IN (SELECT id FROM public.journal_entries WHERE user_id = target_user_id);
|
||||
DELETE FROM public.journal_entries WHERE user_id = target_user_id;
|
||||
@@ -85,6 +97,9 @@ BEGIN
|
||||
DELETE FROM public.cost_centers WHERE user_id = target_user_id;
|
||||
DELETE FROM public.projects WHERE user_id = target_user_id;
|
||||
DELETE FROM public.bank_file_imports WHERE user_id = target_user_id;
|
||||
DELETE FROM public.chat_messages WHERE user_id = target_user_id;
|
||||
DELETE FROM public.chat_sessions WHERE user_id = target_user_id;
|
||||
DELETE FROM public.extension_data WHERE user_id = target_user_id;
|
||||
DELETE FROM public.audit_log WHERE user_id = target_user_id;
|
||||
DELETE FROM public.extension_toggles WHERE user_id = target_user_id;
|
||||
DELETE FROM public.company_settings WHERE user_id = target_user_id;
|
||||
@@ -107,6 +122,26 @@ BEGIN
|
||||
ALTER TABLE public.audit_log ENABLE TRIGGER USER;
|
||||
ALTER TABLE public.company_settings ENABLE TRIGGER USER;
|
||||
ALTER TABLE public.profiles ENABLE TRIGGER USER;
|
||||
ALTER TABLE public.chat_sessions ENABLE TRIGGER USER;
|
||||
ALTER TABLE public.invoice_inbox_items ENABLE TRIGGER USER;
|
||||
|
||||
-- Re-add fiscal period constraints
|
||||
ALTER TABLE public.fiscal_periods
|
||||
ADD CONSTRAINT no_overlapping_fiscal_periods
|
||||
EXCLUDE USING gist (
|
||||
user_id WITH =,
|
||||
daterange(period_start, period_end, '[]') WITH &&
|
||||
);
|
||||
|
||||
ALTER TABLE public.fiscal_periods
|
||||
ADD CONSTRAINT fiscal_period_start_first_of_month
|
||||
CHECK (EXTRACT(DAY FROM period_start) = 1)
|
||||
NOT VALID;
|
||||
|
||||
ALTER TABLE public.fiscal_periods
|
||||
ADD CONSTRAINT fiscal_period_end_last_of_month
|
||||
CHECK (period_end = (date_trunc('month', period_end) + interval '1 month - 1 day')::date)
|
||||
NOT VALID;
|
||||
|
||||
-- Delete the auth user
|
||||
DELETE FROM auth.users WHERE id = target_user_id;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { cpSync, rmSync, existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
const root = process.cwd()
|
||||
const extensions = ['receipt-ocr', 'ai-categorization', 'ai-chat', 'push-notifications', 'enable-banking', 'example-logger']
|
||||
|
||||
for (const ext of extensions) {
|
||||
const src = join(root, 'extensions', ext)
|
||||
const dest = join(root, 'extensions', 'general', ext)
|
||||
|
||||
if (!existsSync(src)) {
|
||||
console.log(`SKIP: ${src} does not exist`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (existsSync(dest)) {
|
||||
console.log(`CLEAN: ${dest} already exists, removing`)
|
||||
rmSync(dest, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
console.log(`COPY: ${src} -> ${dest}`)
|
||||
cpSync(src, dest, { recursive: true })
|
||||
}
|
||||
|
||||
console.log('Done copying extensions to general/')
|
||||
|
||||
// Now remove the old directories
|
||||
for (const ext of extensions) {
|
||||
const src = join(root, 'extensions', ext)
|
||||
if (existsSync(src)) {
|
||||
console.log(`REMOVE: ${src}`)
|
||||
rmSync(src, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Done removing old extension directories')
|
||||
@@ -1,33 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const extsDir = path.join(root, 'extensions');
|
||||
const generalDir = path.join(extsDir, 'general');
|
||||
|
||||
const dirs = [
|
||||
'receipt-ocr',
|
||||
'ai-categorization',
|
||||
'ai-chat',
|
||||
'push-notifications',
|
||||
'enable-banking',
|
||||
'example-logger',
|
||||
];
|
||||
|
||||
if (!fs.existsSync(generalDir)) {
|
||||
fs.mkdirSync(generalDir, { recursive: true });
|
||||
}
|
||||
|
||||
for (const ext of dirs) {
|
||||
const src = path.join(extsDir, ext);
|
||||
const dest = path.join(generalDir, ext);
|
||||
if (fs.existsSync(src)) {
|
||||
fs.cpSync(src, dest, { recursive: true });
|
||||
fs.rmSync(src, { recursive: true, force: true });
|
||||
console.log('Done: ' + ext);
|
||||
} else {
|
||||
console.log('Skip: ' + ext);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('All done.');
|
||||
@@ -1 +0,0 @@
|
||||
const fs=require('fs'),p=require('path'),r=p.resolve(__dirname,'..'),b=p.join(r,'extensions'),t=p.join(b,'general'),d=['receipt-ocr','ai-categorization','ai-chat','push-notifications','enable-banking','example-logger'];fs.mkdirSync(t,{recursive:!0});d.forEach(n=>{const s=p.join(b,n),e=p.join(t,n);fs.existsSync(s)?(fs.cpSync(s,e,{recursive:!0}),fs.rmSync(s,{recursive:!0,force:!0}),console.log(n)):console.log('!'+n)});
|
||||
Reference in New Issue
Block a user