From aec14f0ca7f7a2c84e62d85953eeead5f22b679c Mon Sep 17 00:00:00 2001 From: Mattsson <111893710+mattssonn@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:34:59 +0200 Subject: [PATCH] fix: scope company_settings query to active company in invoice dialogs (#288) SendInvoiceDialog and PaymentBookingDialog called .single() on company_settings without a company_id filter, relying on RLS to narrow the result. RLS uses user_company_ids() which returns every company the user can access, so multi-company users (including team members) got multiple rows back and PostgREST returned 406. Filter explicitly by the active company and use maybeSingle() so a missing row falls back to defaults. Co-authored-by: Claude Opus 4.7 (1M context) --- components/invoices/PaymentBookingDialog.tsx | 9 +++++++-- components/invoices/SendInvoiceDialog.tsx | 12 +++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/components/invoices/PaymentBookingDialog.tsx b/components/invoices/PaymentBookingDialog.tsx index f4f8d06b..def7f0c7 100644 --- a/components/invoices/PaymentBookingDialog.tsx +++ b/components/invoices/PaymentBookingDialog.tsx @@ -18,6 +18,7 @@ import AccountCombobox from '@/components/bookkeeping/AccountCombobox' import { proposePaymentLines } from '@/lib/bookkeeping/propose-payment-lines' import { formatCurrency } from '@/lib/utils' import { createClient } from '@/lib/supabase/client' +import { useCompany } from '@/contexts/CompanyContext' import { Plus, Trash2, Loader2 } from 'lucide-react' import type { FormLine } from '@/components/bookkeeping/JournalEntryForm' import type { Invoice, InvoiceItem, Customer, BASAccount, EntityType } from '@/types' @@ -44,6 +45,7 @@ export default function PaymentBookingDialog({ }: PaymentBookingDialogProps) { const { toast } = useToast() const supabase = createClient() + const { company } = useCompany() const [accounts, setAccounts] = useState([]) const [lines, setLines] = useState([]) @@ -68,11 +70,14 @@ export default function PaymentBookingDialog({ const accountsData = await accountsRes.json() const fetchedAccounts: BASAccount[] = accountsData.data || [] + if (!company?.id) throw new Error('Inget aktivt företag') + // Fetch company settings const { data: settings, error: settingsError } = await supabase .from('company_settings') .select('accounting_method, entity_type') - .single() + .eq('company_id', company.id) + .maybeSingle() if (settingsError) throw new Error('Kunde inte ladda företagsinställningar') if (cancelled) return @@ -116,7 +121,7 @@ export default function PaymentBookingDialog({ init() return () => { cancelled = true } - }, [open, invoice.id]) + }, [open, invoice.id, company?.id]) // Balance computation const { totalDebit, totalCredit, isBalanced } = useMemo(() => { diff --git a/components/invoices/SendInvoiceDialog.tsx b/components/invoices/SendInvoiceDialog.tsx index 0ce73dec..6f30b49e 100644 --- a/components/invoices/SendInvoiceDialog.tsx +++ b/components/invoices/SendInvoiceDialog.tsx @@ -15,6 +15,7 @@ import { JournalEntryReviewContent } from '@/components/bookkeeping/JournalEntry import { proposeSendLines } from '@/lib/bookkeeping/propose-send-lines' import { formatCurrency } from '@/lib/utils' import { createClient } from '@/lib/supabase/client' +import { useCompany } from '@/contexts/CompanyContext' import { Loader2, Mail, Send } from 'lucide-react' import type { Invoice, InvoiceItem, Customer, EntityType } from '@/types' @@ -41,6 +42,7 @@ export default function SendInvoiceDialog({ }: SendInvoiceDialogProps) { const { toast } = useToast() const supabase = createClient() + const { company } = useCompany() const [isSubmitting, setIsSubmitting] = useState(false) const [accountingMethod, setAccountingMethod] = useState<'accrual' | 'cash'>('accrual') @@ -58,11 +60,14 @@ export default function SendInvoiceDialog({ async function init() { try { + if (!company?.id) throw new Error('Inget aktivt företag') + // Fetch company settings const { data: settings, error } = await supabase .from('company_settings') .select('accounting_method, entity_type') - .single() + .eq('company_id', company.id) + .maybeSingle() if (error) throw new Error('Kunde inte ladda företagsinställningar') if (cancelled) return @@ -71,9 +76,10 @@ export default function SendInvoiceDialog({ const { data: period } = await supabase .from('fiscal_periods') .select('name') + .eq('company_id', company.id) .lte('start_date', invoice.invoice_date) .gte('end_date', invoice.invoice_date) - .single() + .maybeSingle() if (cancelled) return @@ -94,7 +100,7 @@ export default function SendInvoiceDialog({ init() return () => { cancelled = true } - }, [open, invoice.id, invoice.invoice_date]) + }, [open, invoice.id, invoice.invoice_date, company?.id]) const proposedLines = useMemo(() => { if (!isInitialized || accountingMethod !== 'accrual') return []