diff --git a/src/lib/invoice-generation.ts b/src/lib/invoice-generation.ts index b7c9eac..6038a97 100644 --- a/src/lib/invoice-generation.ts +++ b/src/lib/invoice-generation.ts @@ -145,6 +145,47 @@ export async function generateInvoiceForClient(args: GenerateInvoiceArgs): Promi return { invoiceId: inv.id, invoiceNumber: inv.invoice_number }; } +// Find or create a per-client placeholder invoice used to mark items +// as "already invoiced" outside of normal invoice generation (e.g. manual +// flagging or imports). Items linked to this invoice are treated as billed +// and won't appear on unbilled lists. +export async function ensureMarkedInvoicedPlaceholder( + clientId: string, + caseId: string | null, + createdBy: string, +): Promise { + // Look for an existing placeholder for this client (any case). + const { data: existing } = await supabase + .from("invoices") + .select("id") + .eq("client_id", clientId) + .like("invoice_number", "MARKED-INVOICED-%") + .order("created_at", { ascending: false }) + .limit(1) + .maybeSingle(); + if (existing?.id) return existing.id; + + const invNumber = `MARKED-INVOICED-${Date.now()}-${Math.floor(Math.random() * 10000)}`; + const { data, error } = await supabase + .from("invoices") + .insert({ + client_id: clientId, + case_id: caseId, + invoice_number: invNumber, + status: "void", + issue_date: new Date().toISOString().slice(0, 10), + subtotal: 0, + tax: 0, + total: 0, + notes: "Placeholder invoice for items manually marked as already invoiced.", + created_by: createdBy, + } as any) + .select("id") + .single(); + if (error || !data) throw error ?? new Error("Could not create placeholder invoice"); + return data.id; +} + export async function recalcInvoiceTotals(invoiceId: string, taxRate?: number) { const { data: items } = await supabase .from("invoice_line_items")