From fa5e14de83817bbc545135d1ebaed7e04a507e18 Mon Sep 17 00:00:00 2001 From: Emil Date: Sat, 21 Feb 2026 17:14:08 +0100 Subject: [PATCH] Fixing bug --- app/(dashboard)/layout.tsx | 12 +- .../journal-entries/__tests__/route.test.ts | 4 + app/api/bookkeeping/journal-entries/route.ts | 3 + .../ai-categorization/settings/route.ts | 3 + .../ai-categorization/suggestions/route.ts | 3 + .../enable-banking/sync/cron/route.ts | 3 + .../extensions/receipt-ocr/upload/route.ts | 2 + .../[id]/categorize/__tests__/route.test.ts | 8 +- .../match-invoice/__tests__/route.test.ts | 29 ++-- .../general/push-notifications/index.ts | 47 +++++-- .../general/push-notifications/types.ts | 5 + extensions/general/receipt-ocr/index.ts | 2 + lib/bookkeeping/invoice-entries.ts | 2 +- lib/reports/__tests__/balance-sheet.test.ts | 9 +- .../20240101000033_ai_chat_schema.sql | 127 ++++++++++++++++++ ...40101000034_fix_extension_data_trigger.sql | 19 +++ .../20240101000035_fix_push_notifications.sql | 28 ++++ .../20240101000036_fix_enable_banking.sql | 5 + ...l => 20240101000037_extension_toggles.sql} | 0 ...000038_fix_match_documents_search_path.sql | 37 +++++ 20 files changed, 309 insertions(+), 39 deletions(-) create mode 100644 supabase/migrations/20240101000033_ai_chat_schema.sql create mode 100644 supabase/migrations/20240101000034_fix_extension_data_trigger.sql create mode 100644 supabase/migrations/20240101000035_fix_push_notifications.sql create mode 100644 supabase/migrations/20240101000036_fix_enable_banking.sql rename supabase/migrations/{20240101000029_extension_toggles.sql => 20240101000037_extension_toggles.sql} (100%) create mode 100644 supabase/migrations/20240101000038_fix_match_documents_search_path.sql diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx index bc6ec7fc..58119570 100644 --- a/app/(dashboard)/layout.tsx +++ b/app/(dashboard)/layout.tsx @@ -35,6 +35,16 @@ export default async function DashboardLayout({ .eq('user_id', user.id) .eq('enabled', true) + // Check if ai-chat is explicitly disabled (legacy default: enabled when no row exists) + const { data: aiChatToggle } = await supabase + .from('extension_toggles') + .select('enabled') + .eq('user_id', user.id) + .eq('sector_slug', 'general') + .eq('extension_slug', 'ai-chat') + .single() + const showAiChat = aiChatToggle ? aiChatToggle.enabled : true + return (
{/* Skip to content link for keyboard/screen reader users */} @@ -54,7 +64,7 @@ export default async function DashboardLayout({ {children}
- + {showAiChat && } ) } diff --git a/app/api/bookkeeping/journal-entries/__tests__/route.test.ts b/app/api/bookkeeping/journal-entries/__tests__/route.test.ts index 93e4dabb..318f7d77 100644 --- a/app/api/bookkeeping/journal-entries/__tests__/route.test.ts +++ b/app/api/bookkeeping/journal-entries/__tests__/route.test.ts @@ -12,6 +12,10 @@ vi.mock('@/lib/supabase/server', () => ({ createClient: () => Promise.resolve(mockSupabase), })) +vi.mock('@/lib/init', () => ({ + ensureInitialized: vi.fn(), +})) + const mockCreateJournalEntry = vi.fn() vi.mock('@/lib/bookkeeping/engine', () => ({ createJournalEntry: (...args: unknown[]) => mockCreateJournalEntry(...args), diff --git a/app/api/bookkeeping/journal-entries/route.ts b/app/api/bookkeeping/journal-entries/route.ts index 9c447af6..9366da98 100644 --- a/app/api/bookkeeping/journal-entries/route.ts +++ b/app/api/bookkeeping/journal-entries/route.ts @@ -1,8 +1,11 @@ import { createClient } from '@/lib/supabase/server' import { NextResponse } from 'next/server' import { createJournalEntry } from '@/lib/bookkeeping/engine' +import { ensureInitialized } from '@/lib/init' import type { CreateJournalEntryInput } from '@/types' +ensureInitialized() + export async function GET(request: Request) { const supabase = await createClient() const { data: { user } } = await supabase.auth.getUser() diff --git a/app/api/extensions/ai-categorization/settings/route.ts b/app/api/extensions/ai-categorization/settings/route.ts index 9a76b945..38ec4e63 100644 --- a/app/api/extensions/ai-categorization/settings/route.ts +++ b/app/api/extensions/ai-categorization/settings/route.ts @@ -1,6 +1,9 @@ import { createClient } from '@/lib/supabase/server' import { NextResponse } from 'next/server' import { getSettings, saveSettings } from '@/extensions/general/ai-categorization' +import { ensureInitialized } from '@/lib/init' + +ensureInitialized() /** * GET /api/extensions/ai-categorization/settings diff --git a/app/api/extensions/ai-categorization/suggestions/route.ts b/app/api/extensions/ai-categorization/suggestions/route.ts index c08a91c0..1b7a624f 100644 --- a/app/api/extensions/ai-categorization/suggestions/route.ts +++ b/app/api/extensions/ai-categorization/suggestions/route.ts @@ -1,8 +1,11 @@ import { createClient } from '@/lib/supabase/server' import { NextResponse } from 'next/server' import { categorizeTransactions } from '@/extensions/general/ai-categorization' +import { ensureInitialized } from '@/lib/init' import type { CategorizationSuggestion } from '@/extensions/general/ai-categorization/categorizer' +ensureInitialized() + /** * GET /api/extensions/ai-categorization/suggestions?transaction_ids=id1,id2,... * Fetch pre-computed AI suggestions for given transaction IDs diff --git a/app/api/extensions/enable-banking/sync/cron/route.ts b/app/api/extensions/enable-banking/sync/cron/route.ts index 025b436d..017beb38 100644 --- a/app/api/extensions/enable-banking/sync/cron/route.ts +++ b/app/api/extensions/enable-banking/sync/cron/route.ts @@ -2,8 +2,11 @@ import { createClient } from '@supabase/supabase-js' import { NextResponse } from 'next/server' import { syncAccountTransactions } from '@/extensions/general/enable-banking/lib/sync' import { isConsentExpiringSoon, getDaysUntilExpiry } from '@/extensions/general/enable-banking/lib/api-client' +import { ensureInitialized } from '@/lib/init' import type { StoredAccount } from '@/extensions/general/enable-banking/types' +ensureInitialized() + /** * GET /api/extensions/enable-banking/sync/cron * Automatic daily bank transaction sync diff --git a/app/api/extensions/receipt-ocr/upload/route.ts b/app/api/extensions/receipt-ocr/upload/route.ts index 7f70eaa9..215792dd 100644 --- a/app/api/extensions/receipt-ocr/upload/route.ts +++ b/app/api/extensions/receipt-ocr/upload/route.ts @@ -143,6 +143,8 @@ export async function POST(request: Request) { vat_amount: item.vatRate && item.lineTotal ? (item.lineTotal * item.vatRate) / (100 + item.vatRate) : null, extraction_confidence: item.confidence, suggested_category: item.suggestedCategory, + category: item.category, + bas_account: item.basAccount, sort_order: index, })) diff --git a/app/api/transactions/[id]/categorize/__tests__/route.test.ts b/app/api/transactions/[id]/categorize/__tests__/route.test.ts index 4246c696..35b2c1d7 100644 --- a/app/api/transactions/[id]/categorize/__tests__/route.test.ts +++ b/app/api/transactions/[id]/categorize/__tests__/route.test.ts @@ -128,7 +128,7 @@ describe('POST /api/transactions/[id]/categorize', () => { // Fetch company settings enqueue({ data: { entity_type: 'enskild_firma', fiscal_year_start_month: 1 }, error: null }) // ensureFiscalPeriod: check existing - enqueue({ data: { id: 'period-1' }, error: null }) + enqueue({ data: [{ id: 'period-1' }], error: null }) mockCreateTransactionJournalEntry.mockResolvedValue({ id: 'je-1' }) mockSaveUserMappingRule.mockResolvedValue(undefined) @@ -177,7 +177,7 @@ describe('POST /api/transactions/[id]/categorize', () => { enqueue({ data: tx, error: null }) enqueue({ data: { entity_type: 'enskild_firma', fiscal_year_start_month: 1 }, error: null }) - enqueue({ data: { id: 'period-1' }, error: null }) + enqueue({ data: [{ id: 'period-1' }], error: null }) mockCreateTransactionJournalEntry.mockRejectedValue(new Error('Period locked')) @@ -210,7 +210,7 @@ describe('POST /api/transactions/[id]/categorize', () => { enqueue({ data: tx, error: null }) enqueue({ data: { entity_type: 'enskild_firma', fiscal_year_start_month: 1 }, error: null }) - enqueue({ data: { id: 'period-1' }, error: null }) + enqueue({ data: [{ id: 'period-1' }], error: null }) mockCreateTransactionJournalEntry.mockResolvedValue({ id: 'je-1' }) @@ -237,7 +237,7 @@ describe('POST /api/transactions/[id]/categorize', () => { enqueue({ data: tx, error: null }) enqueue({ data: { entity_type: 'enskild_firma', fiscal_year_start_month: 1 }, error: null }) - enqueue({ data: { id: 'period-1' }, error: null }) + enqueue({ data: [{ id: 'period-1' }], error: null }) mockCreateTransactionJournalEntry.mockResolvedValue({ id: 'je-1' }) diff --git a/app/api/transactions/[id]/match-invoice/__tests__/route.test.ts b/app/api/transactions/[id]/match-invoice/__tests__/route.test.ts index 92cee214..99271abe 100644 --- a/app/api/transactions/[id]/match-invoice/__tests__/route.test.ts +++ b/app/api/transactions/[id]/match-invoice/__tests__/route.test.ts @@ -14,14 +14,11 @@ vi.mock('@/lib/supabase/server', () => ({ createClient: () => Promise.resolve(mockSupabase), })) -const mockCreateJournalEntry = vi.fn() -const mockFindFiscalPeriod = vi.fn() -vi.mock('@/lib/bookkeeping/engine', () => ({ - createJournalEntry: (...args: unknown[]) => mockCreateJournalEntry(...args), - findFiscalPeriod: (...args: unknown[]) => mockFindFiscalPeriod(...args), -})) - +const mockCreateInvoicePaymentJournalEntry = vi.fn() +const mockCreateInvoiceCashEntry = vi.fn() vi.mock('@/lib/bookkeeping/invoice-entries', () => ({ + createInvoicePaymentJournalEntry: (...args: unknown[]) => mockCreateInvoicePaymentJournalEntry(...args), + createInvoiceCashEntry: (...args: unknown[]) => mockCreateInvoiceCashEntry(...args), getRevenueAccount: vi.fn().mockReturnValue('3001'), getOutputVatAccount: vi.fn().mockReturnValue('2611'), })) @@ -35,7 +32,6 @@ describe('POST /api/transactions/[id]/match-invoice', () => { vi.clearAllMocks() reset() mockSupabase.auth.getUser.mockResolvedValue({ data: { user: mockUser } }) - mockFindFiscalPeriod.mockResolvedValue('period-1') }) it('returns 401 when not authenticated', async () => { @@ -161,7 +157,7 @@ describe('POST /api/transactions/[id]/match-invoice', () => { // Fetch company settings enqueue({ data: { accounting_method: 'accrual', entity_type: 'enskild_firma' }, error: null }) - mockCreateJournalEntry.mockResolvedValue({ id: 'je-1' }) + mockCreateInvoicePaymentJournalEntry.mockResolvedValue({ id: 'je-1' }) // Update invoice to paid enqueue({ data: null, error: null }) @@ -186,16 +182,11 @@ describe('POST /api/transactions/[id]/match-invoice', () => { expect(body.paid_amount).toBe(12500) expect(body.journal_entry_id).toBe('je-1') - // Verify accrual journal entry: debit 1930, credit 1510 - expect(mockCreateJournalEntry).toHaveBeenCalledWith( + // Verify accrual payment entry was called + expect(mockCreateInvoicePaymentJournalEntry).toHaveBeenCalledWith( 'user-1', - expect.objectContaining({ - source_type: 'invoice_paid', - lines: expect.arrayContaining([ - expect.objectContaining({ account_number: '1930', debit_amount: 12500 }), - expect.objectContaining({ account_number: '1510', credit_amount: 12500 }), - ]), - }) + expect.objectContaining({ id: 'inv-1' }), + '2024-06-15' ) }) @@ -207,7 +198,7 @@ describe('POST /api/transactions/[id]/match-invoice', () => { enqueue({ data: invoice, error: null }) enqueue({ data: { accounting_method: 'accrual', entity_type: 'enskild_firma' }, error: null }) - mockCreateJournalEntry.mockRejectedValue(new Error('Period locked')) + mockCreateInvoicePaymentJournalEntry.mockRejectedValue(new Error('Period locked')) // Update invoice enqueue({ data: null, error: null }) diff --git a/extensions/general/push-notifications/index.ts b/extensions/general/push-notifications/index.ts index 14e53bb4..3faab849 100644 --- a/extensions/general/push-notifications/index.ts +++ b/extensions/general/push-notifications/index.ts @@ -45,16 +45,22 @@ export async function getSettings(userId: string): Promise) } + return { + periodLockedEnabled: data.period_locked_enabled ?? DEFAULT_SETTINGS.periodLockedEnabled, + periodYearClosedEnabled: data.period_year_closed_enabled ?? DEFAULT_SETTINGS.periodYearClosedEnabled, + invoiceSentEnabled: data.invoice_sent_enabled ?? DEFAULT_SETTINGS.invoiceSentEnabled, + receiptExtractedEnabled: data.receipt_extracted_enabled ?? DEFAULT_SETTINGS.receiptExtractedEnabled, + receiptMatchedEnabled: data.receipt_matched_enabled ?? DEFAULT_SETTINGS.receiptMatchedEnabled, + } } export async function saveSettings( @@ -67,15 +73,17 @@ export async function saveSettings( const supabase = await createClient() await supabase - .from('extension_data') + .from('notification_settings') .upsert( { user_id: userId, - extension_id: 'push-notifications', - key: 'settings', - value: merged, + period_locked_enabled: merged.periodLockedEnabled, + period_year_closed_enabled: merged.periodYearClosedEnabled, + invoice_sent_enabled: merged.invoiceSentEnabled, + receipt_extracted_enabled: merged.receiptExtractedEnabled, + receipt_matched_enabled: merged.receiptMatchedEnabled, }, - { onConflict: 'user_id,extension_id,key' } + { onConflict: 'user_id' } ) return merged @@ -231,6 +239,21 @@ export const pushNotificationsExtension: Extension = { path: '/settings/extensions/push-notifications', }, async onInstall(ctx) { - await saveSettings(ctx.userId, DEFAULT_SETTINGS) + const supabase = await createClient() + + // Ensure a notification_settings row exists for this user + await supabase + .from('notification_settings') + .upsert( + { + user_id: ctx.userId, + period_locked_enabled: DEFAULT_SETTINGS.periodLockedEnabled, + period_year_closed_enabled: DEFAULT_SETTINGS.periodYearClosedEnabled, + invoice_sent_enabled: DEFAULT_SETTINGS.invoiceSentEnabled, + receipt_extracted_enabled: DEFAULT_SETTINGS.receiptExtractedEnabled, + receipt_matched_enabled: DEFAULT_SETTINGS.receiptMatchedEnabled, + }, + { onConflict: 'user_id' } + ) }, } diff --git a/extensions/general/push-notifications/types.ts b/extensions/general/push-notifications/types.ts index 40faeafb..5018f8bf 100644 --- a/extensions/general/push-notifications/types.ts +++ b/extensions/general/push-notifications/types.ts @@ -21,6 +21,11 @@ export interface NotificationSettings { quiet_end: string // time format "HH:MM" email_enabled: boolean push_enabled: boolean + period_locked_enabled: boolean + period_year_closed_enabled: boolean + invoice_sent_enabled: boolean + receipt_extracted_enabled: boolean + receipt_matched_enabled: boolean created_at: string updated_at: string } diff --git a/extensions/general/receipt-ocr/index.ts b/extensions/general/receipt-ocr/index.ts index 80234ec9..ba06d504 100644 --- a/extensions/general/receipt-ocr/index.ts +++ b/extensions/general/receipt-ocr/index.ts @@ -174,6 +174,8 @@ async function handleDocumentUploaded( : null, extraction_confidence: item.confidence, suggested_category: item.suggestedCategory, + category: item.category, + bas_account: item.basAccount, sort_order: index, })) diff --git a/lib/bookkeeping/invoice-entries.ts b/lib/bookkeeping/invoice-entries.ts index 440c8e55..a430149b 100644 --- a/lib/bookkeeping/invoice-entries.ts +++ b/lib/bookkeeping/invoice-entries.ts @@ -1,6 +1,6 @@ import { createJournalEntry, findFiscalPeriod } from './engine' import { generateSalesVatLines, generateReverseChargeLines } from './vat-entries' -import { getVatTreatmentForRate } from '@/lib/invoice/vat-rules' +import { getVatTreatmentForRate } from '@/lib/invoices/vat-rules' import type { CreateJournalEntryInput, CreateJournalEntryLineInput, diff --git a/lib/reports/__tests__/balance-sheet.test.ts b/lib/reports/__tests__/balance-sheet.test.ts index 31888595..69a8bcf0 100644 --- a/lib/reports/__tests__/balance-sheet.test.ts +++ b/lib/reports/__tests__/balance-sheet.test.ts @@ -159,9 +159,14 @@ describe('generateBalanceSheet', () => { const report = await generateBalanceSheet('user-1', 'period-1') expect(report.asset_sections).toHaveLength(1) // Only 1930 - expect(report.equity_liability_sections).toEqual([]) + // Class 3-8 accounts are not included as balance sheet rows, but their + // net result (credit - debit = 40000 + 500 - 8000 = 32500) appears as + // "Årets resultat" in equity so the balance sheet can balance. + expect(report.equity_liability_sections).toHaveLength(1) + expect(report.equity_liability_sections[0].title).toBe('Årets resultat') + expect(report.equity_liability_sections[0].subtotal).toBe(32500) expect(report.total_assets).toBe(50000) - expect(report.total_equity_liabilities).toBe(0) + expect(report.total_equity_liabilities).toBe(32500) }) it('uses Math.round for monetary precision on subtotals', async () => { diff --git a/supabase/migrations/20240101000033_ai_chat_schema.sql b/supabase/migrations/20240101000033_ai_chat_schema.sql new file mode 100644 index 00000000..c21dc34f --- /dev/null +++ b/supabase/migrations/20240101000033_ai_chat_schema.sql @@ -0,0 +1,127 @@ +-- Migration 033: AI Chat Schema +-- Creates tables for the AI chat assistant extension: +-- chat_sessions, chat_messages, knowledge_documents, and match_documents RPC + +-- Enable pgvector for embedding storage +create extension if not exists vector with schema extensions; + +-- ============================================================ +-- chat_sessions +-- ============================================================ + +create table public.chat_sessions ( + id uuid primary key default gen_random_uuid(), + user_id uuid references auth.users on delete cascade not null, + title text, + created_at timestamptz not null default now(), + updated_at timestamptz not null default now() +); + +alter table public.chat_sessions enable row level security; + +create policy "chat_sessions_select" on public.chat_sessions + for select using (auth.uid() = user_id); +create policy "chat_sessions_insert" on public.chat_sessions + for insert with check (auth.uid() = user_id); +create policy "chat_sessions_update" on public.chat_sessions + for update using (auth.uid() = user_id); +create policy "chat_sessions_delete" on public.chat_sessions + for delete using (auth.uid() = user_id); + +create index idx_chat_sessions_user_created on public.chat_sessions (user_id, created_at desc); + +create trigger chat_sessions_updated_at + before update on public.chat_sessions + for each row execute function public.update_updated_at_column(); + +-- ============================================================ +-- chat_messages +-- ============================================================ + +create table public.chat_messages ( + id uuid primary key default gen_random_uuid(), + session_id uuid references public.chat_sessions on delete cascade not null, + user_id uuid references auth.users on delete cascade not null, + role text not null check (role in ('user', 'assistant')), + content text not null, + sources jsonb, + created_at timestamptz not null default now() +); + +alter table public.chat_messages enable row level security; + +create policy "chat_messages_select" on public.chat_messages + for select using (auth.uid() = user_id); +create policy "chat_messages_insert" on public.chat_messages + for insert with check (auth.uid() = user_id); +create policy "chat_messages_update" on public.chat_messages + for update using (auth.uid() = user_id); +create policy "chat_messages_delete" on public.chat_messages + for delete using (auth.uid() = user_id); + +create index idx_chat_messages_session on public.chat_messages (session_id, created_at); + +-- ============================================================ +-- knowledge_documents +-- ============================================================ + +create table public.knowledge_documents ( + id uuid primary key default gen_random_uuid(), + source_file text not null, + title text not null, + section_title text, + content text not null, + content_hash text unique not null, + embedding extensions.vector(1536), + metadata jsonb default '{}', + created_at timestamptz not null default now() +); + +alter table public.knowledge_documents enable row level security; + +-- Knowledge documents are shared — any authenticated user can read +create policy "knowledge_documents_select" on public.knowledge_documents + for select using (true); + +create index idx_knowledge_documents_hash on public.knowledge_documents (content_hash); + +-- ============================================================ +-- match_documents RPC (vector similarity search) +-- ============================================================ + +create or replace function public.match_documents( + query_embedding extensions.vector, + match_count int default 5, + match_threshold float default 0.7 +) +returns table ( + id uuid, + source_file text, + title text, + section_title text, + content text, + metadata jsonb, + similarity float +) +language plpgsql +security definer +set search_path = public, extensions +as $$ +begin + return query + select + kd.id, + kd.source_file, + kd.title, + kd.section_title, + kd.content, + kd.metadata, + 1 - (kd.embedding <=> query_embedding)::float as similarity + from public.knowledge_documents kd + where 1 - (kd.embedding <=> query_embedding) >= match_threshold + order by kd.embedding <=> query_embedding + limit match_count; +end; +$$; + +grant execute on function public.match_documents(extensions.vector, int, float) to authenticated; diff --git a/supabase/migrations/20240101000034_fix_extension_data_trigger.sql b/supabase/migrations/20240101000034_fix_extension_data_trigger.sql new file mode 100644 index 00000000..6bfd0b85 --- /dev/null +++ b/supabase/migrations/20240101000034_fix_extension_data_trigger.sql @@ -0,0 +1,19 @@ +-- Migration 034: Fix extension_data updated_at trigger +-- The original trigger references update_updated_at() which does not exist. +-- The correct function is public.update_updated_at_column(). +-- Wrapped in DO block in case extension_data table does not yet exist. + +do $$ +begin + if exists ( + select 1 from information_schema.tables + where table_schema = 'public' and table_name = 'extension_data' + ) then + drop trigger if exists extension_data_updated_at on public.extension_data; + + create trigger extension_data_updated_at + before update on public.extension_data + for each row execute function public.update_updated_at_column(); + end if; +end; +$$; diff --git a/supabase/migrations/20240101000035_fix_push_notifications.sql b/supabase/migrations/20240101000035_fix_push_notifications.sql new file mode 100644 index 00000000..a12388db --- /dev/null +++ b/supabase/migrations/20240101000035_fix_push_notifications.sql @@ -0,0 +1,28 @@ +-- Migration 035: Fix push notifications +-- 1. Expand notification_log notification_type CHECK constraint to include new event types +-- 2. Add per-event enabled columns to notification_settings + +-- Drop and recreate the CHECK constraint with new types +alter table public.notification_log + drop constraint if exists notification_log_notification_type_check; + +alter table public.notification_log + add constraint notification_log_notification_type_check + check (notification_type in ( + 'tax_deadline', + 'invoice_due', + 'invoice_overdue', + 'period_locked', + 'period_year_closed', + 'invoice_sent', + 'receipt_extracted', + 'receipt_matched' + )); + +-- Add new per-event enabled columns to notification_settings +alter table public.notification_settings + add column if not exists period_locked_enabled boolean default true, + add column if not exists period_year_closed_enabled boolean default true, + add column if not exists invoice_sent_enabled boolean default false, + add column if not exists receipt_extracted_enabled boolean default true, + add column if not exists receipt_matched_enabled boolean default true; diff --git a/supabase/migrations/20240101000036_fix_enable_banking.sql b/supabase/migrations/20240101000036_fix_enable_banking.sql new file mode 100644 index 00000000..74d5269e --- /dev/null +++ b/supabase/migrations/20240101000036_fix_enable_banking.sql @@ -0,0 +1,5 @@ +-- Migration 036: Fix Enable Banking +-- Add authorization_id column to bank_connections for PSD2 authorization tracking + +alter table public.bank_connections + add column if not exists authorization_id text; diff --git a/supabase/migrations/20240101000029_extension_toggles.sql b/supabase/migrations/20240101000037_extension_toggles.sql similarity index 100% rename from supabase/migrations/20240101000029_extension_toggles.sql rename to supabase/migrations/20240101000037_extension_toggles.sql diff --git a/supabase/migrations/20240101000038_fix_match_documents_search_path.sql b/supabase/migrations/20240101000038_fix_match_documents_search_path.sql new file mode 100644 index 00000000..26cb1bf3 --- /dev/null +++ b/supabase/migrations/20240101000038_fix_match_documents_search_path.sql @@ -0,0 +1,37 @@ +-- Migration 038: Fix match_documents search_path +-- The function needs the extensions schema in search_path to use pgvector operators. + +create or replace function public.match_documents( + query_embedding extensions.vector, + match_count int default 5, + match_threshold float default 0.7 +) +returns table ( + id uuid, + source_file text, + title text, + section_title text, + content text, + metadata jsonb, + similarity float +) +language plpgsql +security definer +set search_path = public, extensions +as $$ +begin + return query + select + kd.id, + kd.source_file, + kd.title, + kd.section_title, + kd.content, + kd.metadata, + 1 - (kd.embedding <=> query_embedding)::float as similarity + from public.knowledge_documents kd + where 1 - (kd.embedding <=> query_embedding) >= match_threshold + order by kd.embedding <=> query_embedding + limit match_count; +end; +$$;