Void & delete invoice options
X-Lovable-Edit-ID: edt-168536e7-0391-4e85-8d1d-7ac394fab988 Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
@@ -131,6 +131,26 @@ function InvoiceDetail() {
|
||||
const balance = Number(invoice.total) - Number(invoice.amount_paid);
|
||||
|
||||
const setStatus = async (status: string) => {
|
||||
if (status === "void") {
|
||||
if (!confirm("Void this invoice? Totals will be zeroed out and time/expenses will be unbilled.")) return;
|
||||
// Unlink time/expenses so they go back to unbilled
|
||||
await supabase.from("time_entries").update({ invoice_id: null }).eq("invoice_id", invoiceId);
|
||||
await supabase.from("expenses").update({ invoice_id: null }).eq("invoice_id", invoiceId);
|
||||
// Zero out line item amounts/rates so the invoice reads as $0
|
||||
await supabase
|
||||
.from("invoice_line_items")
|
||||
.update({ amount: 0, rate: 0, quantity: 0 })
|
||||
.eq("invoice_id", invoiceId);
|
||||
// Zero invoice totals + set status
|
||||
const { error } = await supabase
|
||||
.from("invoices")
|
||||
.update({ status: "void" as any, subtotal: 0, tax: 0, total: 0 })
|
||||
.eq("id", invoiceId);
|
||||
if (error) return toast.error(error.message);
|
||||
toast.success("Invoice voided and zeroed out");
|
||||
load();
|
||||
return;
|
||||
}
|
||||
const { error } = await supabase.from("invoices").update({ status: status as any }).eq("id", invoiceId);
|
||||
if (error) toast.error(error.message);
|
||||
else { toast.success(`Marked ${status}`); load(); }
|
||||
@@ -297,10 +317,16 @@ function InvoiceDetail() {
|
||||
};
|
||||
|
||||
const deleteInvoice = async () => {
|
||||
if (!confirm("Delete this draft invoice and unbill its time/expenses?")) return;
|
||||
const msg = isDraft
|
||||
? "Delete this draft invoice and unbill its time/expenses?"
|
||||
: `Permanently delete invoice ${invoice.invoice_number}? This will remove all payments and unbill its time/expenses. This cannot be undone.`;
|
||||
if (!confirm(msg)) return;
|
||||
// Unlink time/expenses
|
||||
await supabase.from("time_entries").update({ invoice_id: null }).eq("invoice_id", invoiceId);
|
||||
await supabase.from("expenses").update({ invoice_id: null }).eq("invoice_id", invoiceId);
|
||||
// Remove payments first (no cascade guarantee)
|
||||
await supabase.from("invoice_payments").delete().eq("invoice_id", invoiceId);
|
||||
await supabase.from("invoice_line_items").delete().eq("invoice_id", invoiceId);
|
||||
const { error } = await supabase.from("invoices").delete().eq("id", invoiceId);
|
||||
if (error) return toast.error(error.message);
|
||||
toast.success("Invoice deleted");
|
||||
@@ -547,9 +573,9 @@ function InvoiceDetail() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{canEdit && isDraft && (
|
||||
{canEdit && (
|
||||
<Button variant="outline" className="w-full text-destructive hover:text-destructive" onClick={deleteInvoice}>
|
||||
<Trash2 className="h-4 w-4 mr-2" /> Delete draft
|
||||
<Trash2 className="h-4 w-4 mr-2" /> {isDraft ? "Delete draft" : "Delete invoice"}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user