export interface OrderEmailProps { orderNumber: string status: 'pending' | 'pending_approval' | 'in_review' | 'approved' | 'active' | 'completed' | 'cancelled' | 'rejected' formattedDate: string customerCompanyName: string billingModel?: string // e.g. "Kauf" | "Miete (monatlich)" | "Kauf / Miete" rejectionReason?: string siteUrl?: string items?: any[] partnerCompanyName?: string partnerUserName?: string partnerUserEmail?: string taxRate?: number oneTimeNet?: number monthlyNet?: number } export function generateOrderEmailSubject(orderNumber: string, status: string): string { const formattedOrderNumber = (orderNumber || '').replace(/^BE-/, 'AE-') if (status === 'approved' || status === 'active' || status === 'completed') { return `Auftragsbestätigung: Anfrage #${formattedOrderNumber} freigegeben` } if (status === 'rejected' || status === 'cancelled') { return `Status-Update: Anfrage #${formattedOrderNumber} abgelehnt` } return `Eingangsbestätigung: Anfrage #${formattedOrderNumber} eingegangen` } export function generateOrderEmailHtml(props: OrderEmailProps): { text: string; html: string } { const formattedOrderNumber = (props.orderNumber || '').replace(/^BE-/, 'AE-') const siteUrl = props.siteUrl || process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de' const taxRate = props.taxRate ?? 19 const orderPortalUrl = `${siteUrl}/my-orders` // Status Callout configuration let calloutBg = '#f8fafc' let calloutBorder = '#cbd5e1' let calloutTitle = 'Status: Anfrage eingegangen und in Prüfung' let calloutText = 'Ihre Anfrage ist erfolgreich in unserem System eingegangen und wird derzeit vom Support geprüft.' let statusBadgeColor = '#2563eb' if (props.status === 'approved' || props.status === 'active' || props.status === 'completed') { calloutBg = '#f0fdf4' calloutBorder = '#86efac' calloutTitle = 'Status: Freigegeben / Aktiv' calloutText = 'Ihre Auftragsbestätigung / Rechnung finden Sie als PDF im Anhang dieser E-Mail.' statusBadgeColor = '#16a34a' } else if (props.status === 'rejected' || props.status === 'cancelled') { calloutBg = '#fef2f2' calloutBorder = '#fca5a5' calloutTitle = 'Status: Anfrage abgelehnt' calloutText = props.rejectionReason ? `Begründung: "${props.rejectionReason}"` : 'Ihre Anfrage wurde vom Support geprüft und konnte leider nicht freigegeben werden.' statusBadgeColor = '#dc2626' } // Calculate prices if items provided let oneTimeNet = props.oneTimeNet ?? 0 let monthlyNet = props.monthlyNet ?? 0 const items = props.items || [] if (items.length > 0 && props.oneTimeNet === undefined && props.monthlyNet === undefined) { items.forEach((item: any) => { if (item.billing_interval === 'monthly') { monthlyNet += item.base_price || 0 } else { oneTimeNet += item.base_price || 0 } item.selected_modules?.forEach((mod: any) => { const qty = mod.quantity || 1 const price = mod.total_price ?? (mod.price * qty) monthlyNet += price }) }) } const oneTimeTax = Math.round(oneTimeNet * (taxRate / 100) * 100) / 100 const oneTimeGross = Math.round((oneTimeNet + oneTimeTax) * 100) / 100 const monthlyTax = Math.round(monthlyNet * (taxRate / 100) * 100) / 100 const monthlyGross = Math.round((monthlyNet + monthlyTax) * 100) / 100 const formatEuro = (val: number) => new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(val) // Determine Billing Model Label let billingModelLabel = props.billingModel if (!billingModelLabel) { if (oneTimeNet > 0 && monthlyNet > 0) { billingModelLabel = 'Kauf & Miete' } else if (monthlyNet > 0) { billingModelLabel = 'Miete (monatlich)' } else { billingModelLabel = 'Kauf (einmalig)' } } // Group items by Device const groupedItems: { [key: string]: any[] } = {} items.forEach((item: any) => { const devName = item.device_name || 'Kasse 1' if (!groupedItems[devName]) { groupedItems[devName] = [] } groupedItems[devName].push(item) }) // Build Text Breakdown let itemsBreakdownText = '' if (items.length > 0) { itemsBreakdownText += '\nKassen-Aufstellung:\n' Object.entries(groupedItems).forEach(([devName, devItems]) => { itemsBreakdownText += `\n[ ${devName} ]\n` devItems.forEach((item: any) => { itemsBreakdownText += ` - ${item.product_name} (${item.category_name || 'Basis'}): ${formatEuro(item.base_price || 0)} ${item.billing_interval === 'one_time' ? 'einmalig' : 'mtl.'}\n` item.selected_modules?.forEach((mod: any) => { const qty = mod.quantity || 1 const price = mod.total_price ?? (mod.price * qty) itemsBreakdownText += ` + ${mod.module_name} ${qty > 1 ? `(x${qty})` : ''}: +${formatEuro(price)} mtl.\n` }) }) }) } let totalsText = '' if (oneTimeNet > 0) { totalsText += `\nEinmalige Beträge:\n- Netto: ${formatEuro(oneTimeNet)}\n- zzgl. ${taxRate}% MwSt: ${formatEuro(oneTimeTax)}\n- Brutto Gesamt: ${formatEuro(oneTimeGross)}\n` } if (monthlyNet > 0) { totalsText += `\nMonatliche Beträge:\n- Netto: ${formatEuro(monthlyNet)} / mtl.\n- zzgl. ${taxRate}% MwSt: ${formatEuro(monthlyTax)} / mtl.\n- Brutto Gesamt: ${formatEuro(monthlyGross)} / mtl.\n` } let partnerPlainText = '' if (props.partnerCompanyName || props.partnerUserName || props.partnerUserEmail) { partnerPlainText = `\nPartner / Betreuer:\n- Firma: ${props.partnerCompanyName || '-'}\n- Ansprechpartner: ${props.partnerUserName || '-'}\n- E-Mail: ${props.partnerUserEmail || '-'}\n` } const text = `CASPOS B2B Portal\nAnfrage #${formattedOrderNumber}\n\n${calloutTitle}\n${calloutText}\n\nAuftragsdetails:\n- Endkunde: ${props.customerCompanyName}\n- Datum: ${props.formattedDate}\n- Abrechnung: ${billingModelLabel}\n${partnerPlainText}${itemsBreakdownText}${totalsText}\n\nAnfrage im Portal ansehen: ${orderPortalUrl}\n\nCASPOS Computerabrechnungssysteme GmbH\nAlte Bundesstraße 16 · 76846 Hauenstein\nAmtsgericht Zweibrücken HRB 12345\nAutomatische Systembenachrichtigung.` // Build HTML Items Rows let itemsHtmlRows = '' if (items.length > 0) { Object.entries(groupedItems).forEach(([devName, devItems]) => { itemsHtmlRows += `
| Einmalige Beträge: | |
| Netto-Zwischensumme: | ${formatEuro(oneTimeNet)} |
| zzgl. ${taxRate}% MwSt: | ${formatEuro(oneTimeTax)} |
| Gesamt einmalig (brutto): | ${formatEuro(oneTimeGross)} |
| Monatlich wiederkehrend: | |
| Netto-Zwischensumme: | ${formatEuro(monthlyNet)} / mtl. |
| zzgl. ${taxRate}% MwSt: | ${formatEuro(monthlyTax)} / mtl. |
| Gesamt monatlich (brutto): | ${formatEuro(monthlyGross)} / mtl. |