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
@@ -134,6 +134,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query (no booked transactions)
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch (no unpaid invoices)
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup check returns null (no existing row)
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert returns the new transaction
|
||||
@@ -158,6 +160,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup check returns an existing record
|
||||
enqueue({ data: { id: 'existing-tx-1' }, error: null })
|
||||
|
||||
@@ -177,6 +181,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup check: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert fails
|
||||
@@ -203,6 +209,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert returns the new transaction
|
||||
@@ -243,6 +251,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -272,6 +282,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -308,6 +320,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -337,6 +351,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -365,6 +381,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Transaction 1: dedup (no match), insert OK
|
||||
enqueue({ data: null, error: null })
|
||||
enqueue({ data: inserted1, error: null })
|
||||
@@ -402,6 +420,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Transaction rawNew: dedup (no match), insert OK
|
||||
enqueue({ data: null, error: null })
|
||||
enqueue({ data: insertedNew, error: null })
|
||||
@@ -474,6 +494,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
enqueue({ data: null, error: null })
|
||||
enqueue({ data: inserted, error: null })
|
||||
|
||||
@@ -498,6 +520,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
enqueue({ data: null, error: null })
|
||||
enqueue({ data: inserted, error: null })
|
||||
|
||||
@@ -548,6 +572,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -590,6 +616,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -617,6 +645,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked transaction map query
|
||||
enqueue({ data: [], error: null })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// Dedup: no duplicate
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -647,6 +677,8 @@ describe('ingestTransactions', () => {
|
||||
data: [{ date: '2024-06-15', amount: -250 }],
|
||||
error: null,
|
||||
})
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// external_id dedup: no match (different source)
|
||||
enqueue({ data: null, error: null })
|
||||
|
||||
@@ -670,6 +702,8 @@ describe('ingestTransactions', () => {
|
||||
data: [{ date: '2024-06-15', amount: -250 }],
|
||||
error: null,
|
||||
})
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// external_id dedup: no match
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
@@ -702,6 +736,8 @@ describe('ingestTransactions', () => {
|
||||
],
|
||||
error: null,
|
||||
})
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
|
||||
// raw1: external_id dedup (no match) -> content dedup matches (bookedCount=2 -> 1)
|
||||
enqueue({ data: null, error: null })
|
||||
@@ -726,6 +762,8 @@ describe('ingestTransactions', () => {
|
||||
|
||||
// Booked map query throws (caught by try/catch in buildBookedTransactionMap)
|
||||
enqueue({ error: { message: 'Query failed' } })
|
||||
// Supplier invoices fetch
|
||||
enqueue({ data: [], error: null })
|
||||
// external_id dedup: no match
|
||||
enqueue({ data: null, error: null })
|
||||
// Insert
|
||||
|
||||
Reference in New Issue
Block a user