fix(invoice): suppress GiroCode + payment terms for refunded (and paid) orders
User reported that fully refunded orders still rendered a SEPA GiroCode
QR asking the customer to wire the original total. The existing gate
("!viewModel.paid") only excluded literally-PAID orders; REFUNDED,
PARTIALLY_REFUNDED, VOIDED, AUTHORIZED, etc. all sneaked through and
produced a confusing payment request for an order whose outstanding
balance is in fact 0.
Root cause: the view model exposed two related but ambiguous flags
("paid" and "paymentStatus") and the renderer mixed them
inconsistently. Both the GiroCode generation step in
generateInvoice.server.tsx AND the GiroCode/payment-terms render gates
in InvoiceDocument.tsx checked the wrong one.
Fix: introduce a single derived "requiresPayment" flag on the view
model (composeInvoice.ts) that is true only when:
- the document is a regular invoice (not a storno or an offer), AND
- paymentStatus is neither "paid" nor "refunded".
That single flag now drives:
- GiroCode QR generation (skip QR fetch for paid/refunded)
- GiroCode block render in the PDF
- payment-terms paragraph render in the PDF
The existing "Zahlstatus: Erstattet" / "Payment status: Refunded"
meta-row continues to communicate the refund visually — the change
just removes the contradictory call-to-pay.
Side benefits:
- Storno (cancellation invoice) PDFs no longer emit the German
"Bitte überweise …" payment-terms paragraph (kind=storno was
falling through the same not-paid branch).
- Same suppression for fully PAID orders (covered by a second smoke
fixture) so the QR doesn't suggest re-payment after the fact.
Verification: new smoke fixtures (REFUNDED + PAID) build a real
GiroCode data URL and verify the PDF text neither contains the
"GiroCode" caption nor the "Bitte überweise" German payment terms,
while still showing the corresponding "Erstattet" / "Bezahlt" status
row. tsc / smoke / tests / build all green.
This commit is contained in:
@@ -446,7 +446,7 @@ export function InvoiceDocument({ invoice }: DocProps) {
|
||||
<Text style={[styles.paragraph, { marginTop: 16 }]}>
|
||||
{invoice.dueDate ? t.offerValidUntil(formatDate(invoice.dueDate, invoice.language)) : null}
|
||||
</Text>
|
||||
) : !invoice.paid && (
|
||||
) : invoice.requiresPayment && (
|
||||
<Text style={[styles.paragraph, { marginTop: 16 }]}>
|
||||
{invoice.dueDate
|
||||
? t.paymentTerms(
|
||||
@@ -457,7 +457,7 @@ export function InvoiceDocument({ invoice }: DocProps) {
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{invoice.giroCodePngDataUrl && !invoice.paid && (
|
||||
{invoice.giroCodePngDataUrl && invoice.requiresPayment && (
|
||||
<View style={styles.giroBlock}>
|
||||
<Image src={invoice.giroCodePngDataUrl} style={styles.giroImage} />
|
||||
<View>
|
||||
|
||||
Reference in New Issue
Block a user