From 41c1553b1ffa09c15c567155396dfe1e39b23351 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 26 May 2026 23:12:03 +0200 Subject: [PATCH] Phase7b: Manual Invoice --- src/lib/billing.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lib/billing.ts b/src/lib/billing.ts index 4525ae8..3a6e940 100644 --- a/src/lib/billing.ts +++ b/src/lib/billing.ts @@ -1319,15 +1319,29 @@ export async function computeCustomInvoiceTotals(params: { // (rappen precision). We carry the per-line amount on the row so // the PDF doesn't need to recompute and any rounding remains // identical between rendering passes. + // + // tenantName=null because custom invoices aren't bound to a + // specific tenant. unitLabel=null because admin-entered lines are + // free-form (the auto-cron lines use "day" / "request" / + // "message" — for custom lines the quantity is just a number). + // metadata.description preserves the admin's input so + // formatLineDescription can read it via the metadata channel + // (the row's description column also has it, redundantly, for + // safety). displayOrder reflects the order the admin added the + // rows so the PDF renders them top-to-bottom unchanged. const lines: Omit[] = payload.lines.map( - (ln) => { + (ln, idx) => { const amount = Math.round(ln.quantity * ln.unitPriceChf * 100) / 100; return { + tenantName: null, kind: "custom_line" as InvoiceLineKind, description: ln.description.trim(), quantity: ln.quantity, + unitLabel: null, unitPriceChf: ln.unitPriceChf, amountChf: amount, + metadata: { description: ln.description.trim() }, + displayOrder: idx, }; } );