From 1a7a765d7214871ff92a73a86f6c24b83f4ddffe Mon Sep 17 00:00:00 2001 From: DanielS Date: Fri, 10 Jul 2026 09:54:02 +0200 Subject: [PATCH] fix(checkout): use full email template with PDF attachment for checkout API route --- shop/app/api/orders/checkout/route.ts | 125 +++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-) diff --git a/shop/app/api/orders/checkout/route.ts b/shop/app/api/orders/checkout/route.ts index f8f277b..81edc86 100644 --- a/shop/app/api/orders/checkout/route.ts +++ b/shop/app/api/orders/checkout/route.ts @@ -7,6 +7,8 @@ import { createHash } from 'crypto'; import { renderToBuffer } from '@react-pdf/renderer'; import React from 'react'; import { InvoicePDF } from '@/components/invoice-pdf'; +import { buildEmailItemsSection } from '@/lib/actions/orders'; +import { getOrderEmailTemplate } from '@/lib/actions/email-templates'; function generateOrderNumber(prefix: 'BE' | 'AE' = 'AE'): string { const year = new Date().getFullYear(); @@ -144,11 +146,128 @@ export async function POST(request: Request) { // Trigger E-Mail notifications if (user.email) { try { + const formattedDate = new Date(order.created_at).toLocaleDateString('de-DE'); + const taxRate = orderSnapshot.tax_rate ?? 19; + const items = orderSnapshot.items ?? []; + const oneTimeItems = items.filter((item: any) => item.billing_interval === 'one_time'); + const monthlyItems = items.filter((item: any) => item.billing_interval === 'monthly'); + + const oneTimeNet = oneTimeItems.reduce((sum: number, item: any) => sum + (item.item_total || 0), 0); + const oneTimeTax = Math.round(oneTimeNet * (taxRate / 100) * 100) / 100; + const oneTimeGross = Math.round((oneTimeNet + oneTimeTax) * 100) / 100; + + const monthlyNet = monthlyItems.reduce((sum: number, item: any) => sum + (item.item_total || 0), 0); + const monthlyTax = Math.round(monthlyNet * (taxRate / 100) * 100) / 100; + const monthlyGross = Math.round((monthlyNet + monthlyTax) * 100) / 100; + + let totalDetailsText = ''; + if (oneTimeNet > 0 && monthlyNet > 0) { + totalDetailsText = `\nEinmaliger Betrag:\n- Netto: ${oneTimeNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })}\n- zzgl. ${taxRate}% MwSt: ${oneTimeTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })}\n- Brutto Gesamtbetrag: ${oneTimeGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })}\n\nMonatlicher Betrag:\n- Netto: ${monthlyNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat\n- zzgl. ${taxRate}% MwSt: ${monthlyTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat\n- Brutto Gesamtbetrag: ${monthlyGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat`; + } else if (oneTimeNet > 0) { + totalDetailsText = `\nGesamtsumme:\n- Netto: ${oneTimeNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })}\n- zzgl. ${taxRate}% MwSt: ${oneTimeTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })}\n- Brutto Gesamtbetrag: ${oneTimeGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })}`; + } else { + totalDetailsText = `\nGesamtsumme:\n- Netto: ${monthlyNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat\n- zzgl. ${taxRate}% MwSt: ${monthlyTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat\n- Brutto Gesamtbetrag: ${monthlyGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat`; + } + + let totalDetailsHtml = ''; + if (oneTimeNet > 0 && monthlyNet > 0) { + totalDetailsHtml = ` + + Einmaliger Betrag: + + + Netto: + ${oneTimeNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} + + + zzgl. ${taxRate}% MwSt: + ${oneTimeTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} + + + Brutto Gesamtbetrag: + ${oneTimeGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} + + + Monatlicher Betrag: + + + Netto: + ${monthlyNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat + + + zzgl. ${taxRate}% MwSt: + ${monthlyTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat + + + Brutto Gesamtbetrag: + ${monthlyGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat + + `; + } else if (oneTimeNet > 0) { + totalDetailsHtml = ` + + Netto-Gesamtbetrag: + ${oneTimeNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} + + + zzgl. ${taxRate}% MwSt: + ${oneTimeTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} + + + Gesamtbetrag (brutto): + ${oneTimeGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} + + `; + } else { + totalDetailsHtml = ` + + Netto-Gesamtbetrag: + ${monthlyNet.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat + + + zzgl. ${taxRate}% MwSt: + ${monthlyTax.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat + + + Gesamtbetrag (brutto): + ${monthlyGross.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} / Monat + + `; + } + + const itemsSection = buildEmailItemsSection(items); + + const emailTemplate = getOrderEmailTemplate({ + orderNumber: order.order_number, + formattedDate, + customerCompanyName: customerSnapshot.company_name, + totalDetailsText, + totalDetailsHtml, + itemsDetailsText: itemsSection.text, + itemsDetailsHtml: itemsSection.html + }, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, false); + + const { data: bufferData, error: downloadError } = await supabase + .storage + .from('invoices') + .download(fileName); + + const attachments: any[] = []; + if (!downloadError && bufferData) { + const buffer = Buffer.from(await bufferData.arrayBuffer()); + attachments.push({ + filename: `Anfragebestaetigung_${order.order_number}.pdf`, + content: buffer, + contentType: 'application/pdf', + }); + } + await sendMail({ to: user.email, - subject: `Bestätigung Ihrer CASPOS-Anfrage ${order.order_number}`, - text: `Vielen Dank für Ihre Anfrage ${order.order_number}.`, - html: `

Vielen Dank für Ihre Anfrage ${order.order_number}.

` + subject: `Anfragebestätigung ${order.order_number}`, + text: emailTemplate.text, + html: emailTemplate.html, + attachments }); } catch (mailErr) { console.error('Mail error for order ' + order.id, mailErr);