Generate invoice PDFs attached to order confirmation, and send emails on order status changes

Invoice PDFs (§14 UStG line items, tenant-configurable VAT rate) are now
generated at checkout and attached to the confirmation email, plus
available on demand from the order-detail page. Payload-side, orders now
also email the customer on shipped/cancelled/return_requested/returned,
with Stornorechnung/Gutschrift correction PDFs attached for the latter two
so the original invoice's immutable number stays honest.
This commit is contained in:
Marco
2026-07-22 09:49:56 +00:00
parent fa02d95dff
commit 5232b14cdf
15 changed files with 1077 additions and 24 deletions
+15 -3
View File
@@ -88,11 +88,12 @@ export async function POST(request: Request) {
// Re-price everything server-side — never trust client-submitted prices.
const productsBySlug = await fetchProductsBySlug();
const items: { productId: number; productName: string; quantity: number; unitPrice: number }[] = [];
const items: { productId: number; productName: string; quantity: number; unitPrice: number; imageUrl: string | null }[] = [];
for (const line of body.cart) {
const product = productsBySlug.get(line.id);
if (!product) return NextResponse.json({ ok: false, reason: "Ein Artikel im Warenkorb ist nicht mehr verfügbar." }, { status: 400 });
items.push({ productId: product.id, productName: product.name, quantity: line.qty, unitPrice: product.price });
const imageUrl = typeof product.image === "object" && product.image ? product.image.url : null;
items.push({ productId: product.id, productName: product.name, quantity: line.qty, unitPrice: product.price, imageUrl });
}
const subtotal = items.reduce((sum, i) => sum + i.quantity * i.unitPrice, 0);
@@ -164,7 +165,18 @@ export async function POST(request: Request) {
{
orderNumber: order.orderNumber,
createdAt: order.createdAt,
items: items.map((i) => ({ productName: i.productName, quantity: i.quantity, unitPrice: i.unitPrice })),
invoiceNumber: order.invoiceNumber,
invoiceIssuedAt: order.invoiceIssuedAt,
customerFirstName: body.firstName,
customerLastName: body.lastName,
deliveryMethod: body.deliveryMethod,
street: body.street,
packstationNumber: body.packstationNumber,
postNumber: body.postNumber,
zip: body.zip,
city: body.city,
country: body.country,
items: items.map((i) => ({ productName: i.productName, quantity: i.quantity, unitPrice: i.unitPrice, imageUrl: i.imageUrl })),
subtotal,
shippingCost,
discountAmount,