fix(workflows): held booking invoices are 'scheduled', not 'pending_delivery' — so send_document can issue them

A quote with no explicit payment timing falls back to a single after_delivery
installment. spawnInstallmentInvoices marked those 'pending_delivery' even in
hold mode, so the booking flow's send_document -> sendInvoice threw 'Cannot send
invoice with status pending_delivery', the run failed, and no invoice email went
out (the symptom: approve the quote->invoice flow, receive nothing).

In hold mode the flow's review gate + explicit send_document IS the delivery
release, so a held invoice is always 'scheduled' (editable + sendable) regardless
of trigger; scheduled_send_at stays null so the scheduler never auto-sends it.
Non-hold after_delivery invoices keep 'pending_delivery' as before.

Adds a regression test (default after_delivery term -> draft -> scheduled+null).
This commit is contained in:
Luca
2026-06-27 12:36:35 +02:00
parent 7727b6714b
commit 882cfc0661
2 changed files with 32 additions and 5 deletions
+9 -5
View File
@@ -1077,11 +1077,15 @@ async function spawnInstallmentInvoices({ trx, eventId, quoteId, customer, curre
// status `scheduled`, so they sit idle until the admin clicks
// "Release for delivery" on the invoice detail page.
const isDeliveryTrigger = inst.trigger === 'after_delivery';
const rowStatus = isDeliveryTrigger ? 'pending_delivery' : 'scheduled';
// `hold` (workflow draft-seam): create the invoice but leave scheduled_send_at
// NULL so the scheduler never auto-sends it — a draft awaiting an explicit
// send_document after the admin's review gate. Status stays 'scheduled' so
// it's editable and sendInvoice can later issue it.
// `hold` (workflow draft-seam): the booking flow's review gate + explicit
// send_document IS the release, so a held invoice is always `scheduled`
// (editable + sendable via sendInvoice) regardless of trigger — never
// `pending_delivery`, which sendInvoice refuses. Without hold, an
// after_delivery invoice stays `pending_delivery` as before.
const rowStatus = (isDeliveryTrigger && !hold) ? 'pending_delivery' : 'scheduled';
// Held invoices carry no scheduled_send_at so the scheduler never auto-sends
// them — they wait for send_document. after_delivery rows are likewise null
// (the scheduler can't infer a delivery date).
const rowScheduledSendAt = (isDeliveryTrigger || hold) ? null : scheduledSendAt;
const invoiceNumber = await nextInvoiceNumber(trx);