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) <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-04-21 08:34:59 +02:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 0076aa85f8
commit aec14f0ca7
2 changed files with 16 additions and 5 deletions
+7 -2
View File
@@ -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<BASAccount[]>([])
const [lines, setLines] = useState<FormLine[]>([])
@@ -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(() => {
+9 -3
View File
@@ -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 []