diff --git a/shop/components/invoice-pdf.tsx b/shop/components/invoice-pdf.tsx index abb8143..6e1b74e 100644 --- a/shop/components/invoice-pdf.tsx +++ b/shop/components/invoice-pdf.tsx @@ -94,75 +94,103 @@ const styles = StyleSheet.create({ }, }); -export const InvoicePDF = ({ order, product, modules, customer }: any) => ( - - - - - CASPOS - Software Solutions - - - CASPOS GmbH - Musterstraße 1 - 12345 Musterstadt - Deutschland - - +export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => { + const items = orderSnapshot?.items ?? []; + const subtotal = orderSnapshot?.subtotal ?? 0; + const taxAmount = orderSnapshot?.tax_amount ?? 0; + const taxRate = orderSnapshot?.tax_rate ?? 19; + const total = orderSnapshot?.total ?? 0; - Bestellbestätigung / Rechnung - - - Rechnungsempfänger - {customer.company_name} - {customer.first_name} {customer.last_name} - {customer.address} - {customer.zip_code} {customer.city} - USt-IdNr: {customer.vat_id} - - - - Bestelldetails - Bestellnummer: {order.id} - Datum: {new Date().toLocaleDateString('de-DE')} - - - - - Beschreibung - Preis (mtl.) - - - - {product.name} (Basispaket) - {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)} - - - {modules.map((mod: any) => ( - - {mod.name} - +{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(mod.additional_price)} + return ( + + + + + CASPOS + Software Solutions - ))} - - - - - - Zwischensumme: - {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_amount)} - - - Gesamtbetrag: - {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_amount)} + + CASPOS GmbH + Musterstraße 1 + 12345 Musterstadt + Deutschland - - - Vielen Dank für Ihre Bestellung! - Dies ist ein automatisch generiertes Dokument. - - - -); + Bestellbestätigung / Rechnung + + + Rechnungsempfänger + {customer.company_name} + {customer.first_name} {customer.last_name} + {customer.address} + {customer.zip_code} {customer.city} + USt-IdNr: {customer.vat_id} + + + + Bestelldetails + Bestellnummer: {order.order_number || order.id} + Datum: {new Date(order.created_at || Date.now()).toLocaleDateString('de-DE')} + + + + + Beschreibung + Preis (netto) + + + {items.map((item: any, idx: number) => ( + + + + {item.product_name} ({item.category_name}) + + + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(item.base_price)} + {' '}{item.billing_interval === 'one_time' ? 'einmalig' : 'mtl.'} + + + + {item.selected_modules?.map((mod: any, mIdx: number) => ( + + + + {mod.module_name} + + + + +{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(mod.price)} + + + + ))} + + ))} + + + + + + Netto-Zwischensumme: + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(subtotal)} + + + zzgl. {taxRate}% USt: + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(taxAmount)} + + + Gesamtbetrag (brutto): + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(total)} + + + + + + Vielen Dank für Ihre Bestellung! + Dies ist ein automatisch generiertes Dokument. + + + + ); +}; diff --git a/shop/components/order-wizard.tsx b/shop/components/order-wizard.tsx index 1f7210f..1b7180f 100644 --- a/shop/components/order-wizard.tsx +++ b/shop/components/order-wizard.tsx @@ -118,20 +118,29 @@ export function OrderWizard({ ) // Total price across all selections - const totalPrice = useMemo(() => { - let total = 0 + // Total price calculations (split by billing interval) + const { monthlyTotal, oneTimeTotal } = useMemo(() => { + let monthly = 0 + let oneTime = 0 for (const cat of categories) { const sel = selections[cat.id] if (!sel?.productId) continue const prod = products.find(p => p.id === sel.productId) if (!prod) continue - total += Number(prod.base_price) + const base = Number(prod.base_price) + let modulesSum = 0 sel.moduleIds.forEach(mId => { const mod = prod.modules?.find(m => m.id === mId) - if (mod) total += Number(mod.price) + if (mod) modulesSum += Number(mod.price) }) + const totalItem = base + modulesSum + if (prod.billing_interval === 'one_time') { + oneTime += totalItem + } else { + monthly += totalItem + } } - return total + return { monthlyTotal: monthly, oneTimeTotal: oneTime } }, [selections, products, categories]) // Helper: update product selection for a category (resets modules) @@ -425,12 +434,38 @@ export function OrderWizard({ ) })} -
- Gesamt: - - {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(totalPrice)} - -
+ {oneTimeTotal > 0 && monthlyTotal > 0 ? ( +
+
+ Einmalig gesamt: + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(oneTimeTotal)} + +
+
+ Monatlich gesamt: + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(monthlyTotal)} + {' '}/ Monat + +
+
+ ) : oneTimeTotal > 0 ? ( +
+ Gesamt (einmalig): + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(oneTimeTotal)} + +
+ ) : ( +
+ Gesamt: + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(monthlyTotal)} + {' '}/ Monat + +
+ )} {!allCategoriesFilled && (

@@ -669,12 +704,38 @@ export function OrderWizard({ )} -

- Gesamtbetrag: - - {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(totalPrice)} / Monat - -
+ {oneTimeTotal > 0 && monthlyTotal > 0 ? ( +
+
+ Einmaliger Gesamtbetrag: + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(oneTimeTotal)} + +
+
+ Monatlicher Gesamtbetrag: + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(monthlyTotal)} + {' '}/ Monat + +
+
+ ) : oneTimeTotal > 0 ? ( +
+ Gesamtbetrag: + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(oneTimeTotal)} + +
+ ) : ( +
+ Gesamtbetrag: + + {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(monthlyTotal)} + {' '}/ Monat + +
+ )}

Mit dem Klick auf „Kostenpflichtig bestellen" akzeptieren Sie unsere AGB und die Datenschutzerklärung.