'use client' import { useState, useEffect, useMemo } from 'react' import { createClient } from '@/lib/supabase/client' import { Input } from '@/components/ui/input' import { formatCurrency, formatDate, cn } from '@/lib/utils' import { Search, FileText, Loader2 } from 'lucide-react' import { useCompany } from '@/contexts/CompanyContext' import type { SupplierInvoice, Supplier } from '@/types' import type { TransactionWithInvoice } from './transaction-types' type OpenSupplierInvoice = SupplierInvoice & { supplier?: Supplier } interface SupplierInvoicePickerProps { transaction: TransactionWithInvoice onSelect: (invoice: OpenSupplierInvoice) => void isProcessing: boolean } export default function SupplierInvoicePicker({ transaction, onSelect, isProcessing, }: SupplierInvoicePickerProps) { const { company } = useCompany() const supabase = useMemo(() => createClient(), []) const [invoices, setInvoices] = useState([]) const [isLoading, setIsLoading] = useState(true) const [search, setSearch] = useState('') useEffect(() => { if (!company) return const companyId = company.id let cancelled = false async function load() { setIsLoading(true) // Status filter mirrors match-supplier-invoice route expectations: only // approved/overdue/partially_paid invoices can take a payment. Registered // invoices haven't passed the approval gate yet; paid/credited/reversed // are terminal. const { data } = await supabase .from('supplier_invoices') .select('*, supplier:suppliers(*)') .eq('company_id', companyId) .in('status', ['approved', 'overdue', 'partially_paid']) .gt('remaining_amount', 0) .order('invoice_date', { ascending: false }) .limit(200) if (cancelled) return const all = ((data as OpenSupplierInvoice[]) || []) // Status-leak guard: if a supplier invoice still says 'approved'/'overdue' // but already has a payment voucher attached, hide it. Partially-paid // invoices intentionally pass through — they may take more payments. // Mirrors the customer-side guard in InvoicePicker. const fullIds = all .filter((inv) => inv.status === 'approved' || inv.status === 'overdue') .map((inv) => inv.id) let visible = all if (fullIds.length > 0) { const { data: paid } = await supabase .from('supplier_invoice_payments') .select('supplier_invoice_id') .eq('company_id', companyId) .in('supplier_invoice_id', fullIds) .not('journal_entry_id', 'is', null) if (cancelled) return const paidSet = new Set( ((paid as { supplier_invoice_id: string }[] | null) ?? []).map( (r) => r.supplier_invoice_id, ), ) visible = all.filter((inv) => !paidSet.has(inv.id)) } setInvoices(visible) setIsLoading(false) } load() return () => { cancelled = true } }, [company, supabase]) const sorted = useMemo(() => { const txAmount = Math.abs(transaction.amount) const filtered = !search ? invoices : invoices.filter((inv) => { const q = search.toLowerCase() return ( (inv.supplier_invoice_number ?? '').toLowerCase().includes(q) || (inv.supplier?.name ?? '').toLowerCase().includes(q) ) }) return [...filtered].sort((a, b) => { const remainA = a.remaining_amount ?? a.total const remainB = b.remaining_amount ?? b.total const diffA = Math.abs(remainA - txAmount) const diffB = Math.abs(remainB - txAmount) if (diffA !== diffB) return diffA - diffB return b.invoice_date.localeCompare(a.invoice_date) }) }, [invoices, search, transaction.amount]) if (isLoading) { return (
Laddar leverantörsfakturor...
) } if (invoices.length === 0) { return (

Inga öppna leverantörsfakturor att matcha mot.

) } return (
setSearch(e.target.value)} className="pl-9" autoFocus />
{sorted.map((invoice) => { const txAmount = Math.abs(transaction.amount) const remaining = invoice.remaining_amount ?? invoice.total const sameCurrency = transaction.currency === invoice.currency const exact = sameCurrency && Math.abs(remaining - txAmount) < 0.01 const close = sameCurrency && !exact && txAmount > 0 && Math.abs(remaining - txAmount) / txAmount < 0.01 return ( ) })} {sorted.length === 0 && (

Ingen faktura matchar "{search}"

)}
) }