feat(invoice): Stornorechnung und Rechnungskopie an Admin-Adresse

Rechnungskopie: unter Einstellungen > Firma > "Rechnungskopie an" laesst sich
eine Adresse hinterlegen; jede Rechnung und Stornorechnung geht zusaetzlich
als eigene Mail mit derselben E-Rechnung dorthin (lib/invoice-mail.ts).

Stornorechnung: eine Rechnung wird nie geloescht. Bei Stornierung im Admin,
erstatteter Retoure oder voller Erstattung bei Stripe entsteht genau eine
Stornorechnung ST-JJJJ-NNNN (eigener lueckenloser Nummernkreis) mit allen
Positionen und Bezug auf die Rechnung, als E-Rechnung (Gutschrift 381) an
die Kundschaft und als Kopie. Gleichzeitige Ausloeser (Admin-Storno und
Stripe-Webhook derselben Erstattung) enden bei einer Stornorechnung.
Bestellungen mit Stornorechnung lassen sich nicht wieder oeffnen.

ERP-Export: GET /api/erp/rechnungen?art=storno liefert Stornorechnungen mit
Bezug (cancels), Rechnungen tragen cancelledBy; PDFs auch fuer ST-Nummern.
Admin-Bestelldetail zeigt die Stornorechnung mit Download.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-12 06:19:42 +02:00
1 parent 508ff24aa7
commit 4d1bcbbdb7
22 files changed
+809 -16

No files matched your search

+14 -1
View File
@@ -29,7 +29,7 @@ export default async function AdminOrderDetailPage(props: PageProps<"/admin/best
payment: true,
shipment: true,
pickup: true,
invoice: true,
invoice: { include: { cancellation: { select: { number: true } } } },
customer: { select: { email: true, firstName: true, lastName: true } },
discountCode: { select: { code: true } },
},
@@ -279,6 +279,19 @@ export default async function AdminOrderDetailPage(props: PageProps<"/admin/best
>
{t.adminOrderDetailPage.downloadPdf}
</a>
{order.invoice.cancellation && (
<>
<span className="mt-3">
{t.invoicePage.cancellationTitle} {order.invoice.cancellation.number}
</span>
<a
href={`/rechnung/${order.invoice.cancellation.number}/pdf`}
className="link-draw text-sm text-heading"
>
{t.adminOrderDetailPage.downloadPdf}
</a>
</>
)}
</div>
) : (
<p className="text-ink/65">{t.adminOrderDetailPage.noInvoiceYet}</p>