feat: split pricing into one-time and monthly totals, fix invoice pdf rendering
Some checks failed
Staging Build / build (push) Has been cancelled
Some checks failed
Staging Build / build (push) Has been cancelled
This commit is contained in:
@@ -94,75 +94,103 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export const InvoicePDF = ({ order, product, modules, customer }: any) => (
|
||||
<Document>
|
||||
<Page size="A4" style={styles.page}>
|
||||
<View style={styles.header}>
|
||||
<View>
|
||||
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>CASPOS</Text>
|
||||
<Text>Software Solutions</Text>
|
||||
</View>
|
||||
<View style={styles.companyInfo}>
|
||||
<Text>CASPOS GmbH</Text>
|
||||
<Text>Musterstraße 1</Text>
|
||||
<Text>12345 Musterstadt</Text>
|
||||
<Text>Deutschland</Text>
|
||||
</View>
|
||||
</View>
|
||||
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;
|
||||
|
||||
<Text style={styles.title}>Bestellbestätigung / Rechnung</Text>
|
||||
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.label}>Rechnungsempfänger</Text>
|
||||
<Text>{customer.company_name}</Text>
|
||||
<Text>{customer.first_name} {customer.last_name}</Text>
|
||||
<Text>{customer.address}</Text>
|
||||
<Text>{customer.zip_code} {customer.city}</Text>
|
||||
<Text>USt-IdNr: {customer.vat_id}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.label}>Bestelldetails</Text>
|
||||
<Text>Bestellnummer: {order.id}</Text>
|
||||
<Text>Datum: {new Date().toLocaleDateString('de-DE')}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.table}>
|
||||
<View style={[styles.tableRow, { backgroundColor: '#f9f9f9' }]}>
|
||||
<View style={styles.tableCol}><Text style={{ fontWeight: 'bold' }}>Beschreibung</Text></View>
|
||||
<View style={styles.tableColPrice}><Text style={{ fontWeight: 'bold' }}>Preis (mtl.)</Text></View>
|
||||
</View>
|
||||
|
||||
<View style={styles.tableRow}>
|
||||
<View style={styles.tableCol}><Text>{product.name} (Basispaket)</Text></View>
|
||||
<View style={styles.tableColPrice}><Text>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)}</Text></View>
|
||||
</View>
|
||||
|
||||
{modules.map((mod: any) => (
|
||||
<View key={mod.id} style={styles.tableRow}>
|
||||
<View style={styles.tableCol}><Text>{mod.name}</Text></View>
|
||||
<View style={styles.tableColPrice}><Text>+{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(mod.additional_price)}</Text></View>
|
||||
return (
|
||||
<Document>
|
||||
<Page size="A4" style={styles.page}>
|
||||
<View style={styles.header}>
|
||||
<View>
|
||||
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>CASPOS</Text>
|
||||
<Text>Software Solutions</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<View style={styles.total}>
|
||||
<View style={{ width: '40%' }}>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<Text>Zwischensumme:</Text>
|
||||
<Text>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_amount)}</Text>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 5 }}>
|
||||
<Text style={styles.totalLabel}>Gesamtbetrag:</Text>
|
||||
<Text style={styles.totalLabel}>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_amount)}</Text>
|
||||
<View style={styles.companyInfo}>
|
||||
<Text>CASPOS GmbH</Text>
|
||||
<Text>Musterstraße 1</Text>
|
||||
<Text>12345 Musterstadt</Text>
|
||||
<Text>Deutschland</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text>Vielen Dank für Ihre Bestellung!</Text>
|
||||
<Text>Dies ist ein automatisch generiertes Dokument.</Text>
|
||||
</View>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
<Text style={styles.title}>Bestellbestätigung / Rechnung</Text>
|
||||
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.label}>Rechnungsempfänger</Text>
|
||||
<Text>{customer.company_name}</Text>
|
||||
<Text>{customer.first_name} {customer.last_name}</Text>
|
||||
<Text>{customer.address}</Text>
|
||||
<Text>{customer.zip_code} {customer.city}</Text>
|
||||
<Text>USt-IdNr: {customer.vat_id}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.label}>Bestelldetails</Text>
|
||||
<Text>Bestellnummer: {order.order_number || order.id}</Text>
|
||||
<Text>Datum: {new Date(order.created_at || Date.now()).toLocaleDateString('de-DE')}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.table}>
|
||||
<View style={[styles.tableRow, { backgroundColor: '#f9f9f9' }]}>
|
||||
<View style={styles.tableCol}><Text style={{ fontWeight: 'bold' }}>Beschreibung</Text></View>
|
||||
<View style={styles.tableColPrice}><Text style={{ fontWeight: 'bold' }}>Preis (netto)</Text></View>
|
||||
</View>
|
||||
|
||||
{items.map((item: any, idx: number) => (
|
||||
<View key={idx}>
|
||||
<View style={styles.tableRow}>
|
||||
<View style={styles.tableCol}>
|
||||
<Text style={{ fontWeight: 'bold' }}>{item.product_name} ({item.category_name})</Text>
|
||||
</View>
|
||||
<View style={styles.tableColPrice}>
|
||||
<Text>
|
||||
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(item.base_price)}
|
||||
{' '}{item.billing_interval === 'one_time' ? 'einmalig' : 'mtl.'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
{item.selected_modules?.map((mod: any, mIdx: number) => (
|
||||
<View key={mIdx} style={styles.tableRow}>
|
||||
<View style={[styles.tableCol, { paddingLeft: 20 }]}>
|
||||
<Text style={{ color: '#666' }}>+ {mod.module_name}</Text>
|
||||
</View>
|
||||
<View style={styles.tableColPrice}>
|
||||
<Text style={{ color: '#666' }}>
|
||||
+{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(mod.price)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<View style={styles.total}>
|
||||
<View style={{ width: '50%' }}>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 3 }}>
|
||||
<Text>Netto-Zwischensumme:</Text>
|
||||
<Text>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(subtotal)}</Text>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 3 }}>
|
||||
<Text>zzgl. {taxRate}% USt:</Text>
|
||||
<Text>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(taxAmount)}</Text>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 5, borderTopWidth: 1, borderTopColor: '#000', paddingTop: 5 }}>
|
||||
<Text style={styles.totalLabel}>Gesamtbetrag (brutto):</Text>
|
||||
<Text style={styles.totalLabel}>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(total)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text>Vielen Dank für Ihre Bestellung!</Text>
|
||||
<Text>Dies ist ein automatisch generiertes Dokument.</Text>
|
||||
</View>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user