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.