Compare commits
3 Commits
2b92626bf9
...
a93fb975c0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a93fb975c0 | ||
|
|
21c4aa859e | ||
|
|
20f7d5a05a |
@@ -62,15 +62,13 @@ graph TD
|
|||||||
- **Snapshot-Exklusivität**: Die Rechnungsgenerierung erfolgt ausschließlich auf Basis der in `orders.order_data` und `orders.customer_data` eingefrorenen Snapshots.
|
- **Snapshot-Exklusivität**: Die Rechnungsgenerierung erfolgt ausschließlich auf Basis der in `orders.order_data` und `orders.customer_data` eingefrorenen Snapshots.
|
||||||
- **Layout-Unterscheidung**:
|
- **Layout-Unterscheidung**:
|
||||||
- **Typ `purchase` (Kauf)**:
|
- **Typ `purchase` (Kauf)**:
|
||||||
- Generiert eine klassische **B2B-Rechnung**.
|
- Generiert eine klassische **B2B-Angebot**.
|
||||||
- Weist die einmalige Gesamtsumme aus.
|
- Weist die einmalige Gesamtsumme aus.
|
||||||
- Beinhaltet ein explizites Zahlungsziel ("Zahlbar innerhalb von 14 Tagen ohne Abzug").
|
|
||||||
- **Typ `subscription` (Abonnement)**:
|
- **Typ `subscription` (Abonnement)**:
|
||||||
- Generiert eine **Vertragsbestätigung (Abonnement)**.
|
- Generiert eine **Angebotsbestätigung Abonnement**.
|
||||||
- Weist monatlich wiederkehrende Kosten aus.
|
- Weist monatlich wiederkehrende Kosten aus.
|
||||||
- Beinhaltet das SEPA-Lastschriftmandat inklusive Kontoinhaber und IBAN.
|
|
||||||
- **Archivierung & Benachrichtigung**:
|
- **Archivierung & Benachrichtigung**:
|
||||||
- Hochladen des PDFs in den Supabase Storage (`invoices` Bucket).
|
- Hochladen des PDFs in den Supabase Storage (`invoices` Bucket).
|
||||||
- E-Mail-Versand mit PDF-Anhang an den Besteller.
|
- E-Mail-Versand mit PDF-Anhang an den Partner.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -142,9 +142,9 @@ export async function POST(request: Request) {
|
|||||||
try {
|
try {
|
||||||
await sendMail({
|
await sendMail({
|
||||||
to: user.email,
|
to: user.email,
|
||||||
subject: `Bestellbestätigung ${order.order_number}`,
|
subject: `Bestätigung Ihrer CASPOS-Anfrage ${order.order_number}`,
|
||||||
text: `Vielen Dank für Ihre Bestellung ${order.order_number}. Typ: ${type}.`,
|
text: `Vielen Dank für Ihre Anfrage ${order.order_number}.`,
|
||||||
html: `<p>Vielen Dank für Ihre Bestellung <strong>${order.order_number}</strong>.</p><p>Typ: ${type}</p>`
|
html: `<p>Vielen Dank für Ihre Anfrage <strong>${order.order_number}</strong>.</p>`
|
||||||
});
|
});
|
||||||
} catch (mailErr) {
|
} catch (mailErr) {
|
||||||
console.error('Mail error for order ' + order.id, mailErr);
|
console.error('Mail error for order ' + order.id, mailErr);
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => {
|
|||||||
const totalTax = Math.round(totalNet * (taxRate / 100) * 100) / 100;
|
const totalTax = Math.round(totalNet * (taxRate / 100) * 100) / 100;
|
||||||
const totalGross = Math.round((totalNet + totalTax) * 100) / 100;
|
const totalGross = Math.round((totalNet + totalTax) * 100) / 100;
|
||||||
|
|
||||||
const formattedPrice = (val: number) =>
|
const formattedPrice = (val: number) =>
|
||||||
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(val);
|
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(val);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -123,7 +123,7 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={styles.title}>
|
<Text style={styles.title}>
|
||||||
{isSubscription ? 'Vertragsbestätigung (Abonnement)' : 'B2B-Rechnung'}
|
{isSubscription ? 'Anfragebestätigung: Software-Abonnement' : 'Anfragebestätigung: Einmalige Lizenzen'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
@@ -207,7 +207,7 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => {
|
|||||||
<View style={styles.sepaBox}>
|
<View style={styles.sepaBox}>
|
||||||
<Text style={{ fontWeight: 'bold', marginBottom: 4 }}>SEPA-Lastschriftmandat</Text>
|
<Text style={{ fontWeight: 'bold', marginBottom: 4 }}>SEPA-Lastschriftmandat</Text>
|
||||||
<Text style={{ fontSize: 8, color: '#555' }}>
|
<Text style={{ fontSize: 8, color: '#555' }}>
|
||||||
Ich ermächtige die CASPOS GmbH, Zahlungen von meinem Konto mittels Lastschrift einzuziehen. Zugleich weise ich mein Kreditinstitut an, die von der CASPOS GmbH auf mein Konto gezogenen Lastschriften einzulösen.
|
Ich ermächtige die CASPOS GmbH, Zahlungen von meinem Konto mittels Lastschrift einzuziehen. Zugleich weise ich mein Kreditinstitut an, die von der CASPOS GmbH auf mein Konto gezogenen Lastschriften einzulösen. Das Mandat wird erst mit der finalen Aktivierung der Lizenzen durch den CASPOS-Support wirksam.
|
||||||
</Text>
|
</Text>
|
||||||
{customer.bank_iban && (
|
{customer.bank_iban && (
|
||||||
<Text style={{ marginTop: 4, fontSize: 8, fontWeight: 'bold' }}>
|
<Text style={{ marginTop: 4, fontSize: 8, fontWeight: 'bold' }}>
|
||||||
@@ -217,9 +217,8 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => {
|
|||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View style={{ marginTop: 20 }}>
|
<View style={{ marginTop: 20 }}>
|
||||||
<Text style={{ fontWeight: 'bold' }}>Zahlungsziel</Text>
|
<Text style={{ fontSize: 9, color: '#555', fontStyle: 'italic' }}>
|
||||||
<Text style={{ color: '#555' }}>
|
Dies ist eine unverbindliche Bestätigung Ihrer Anfrage. Ein verbindliches Angebot wird Ihnen in Kürze von unserem Vertriebsteam zugesendet.
|
||||||
Zahlbar innerhalb von 14 Tagen nach Erhalt dieser Rechnung ohne Abzug.
|
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ export async function updateOrderStatus(
|
|||||||
|
|
||||||
await sendMail({
|
await sendMail({
|
||||||
to: user.email,
|
to: user.email,
|
||||||
subject: `Statusänderung Ihrer Bestellung ${orderNumber}`,
|
subject: `Statusänderung Ihrer Anfrage ${orderNumber}`,
|
||||||
text: statusEmail.text,
|
text: statusEmail.text,
|
||||||
html: statusEmail.html
|
html: statusEmail.html
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ export function buildOrderSnapshot(
|
|||||||
})
|
})
|
||||||
|
|
||||||
const moduleTotal = selectedModules.reduce((acc, m) => acc + (m.total_price || m.price), 0)
|
const moduleTotal = selectedModules.reduce((acc, m) => acc + (m.total_price || m.price), 0)
|
||||||
|
|
||||||
// If this product falls under the free limit, set its base price to 0
|
// If this product falls under the free limit, set its base price to 0
|
||||||
const actualBasePrice = (sortedIndex < freeLimit) ? 0 : prod.base_price
|
const actualBasePrice = (sortedIndex < freeLimit) ? 0 : prod.base_price
|
||||||
const itemTotal = actualBasePrice + moduleTotal
|
const itemTotal = actualBasePrice + moduleTotal
|
||||||
@@ -146,7 +146,7 @@ export function buildOrderSnapshot(
|
|||||||
category_id: cat.id,
|
category_id: cat.id,
|
||||||
category_name: cat.name,
|
category_name: cat.name,
|
||||||
product_id: prod.id,
|
product_id: prod.id,
|
||||||
product_name: prod.name + (actualBasePrice === 0 ? " (Inklusive/Frei)" : ""),
|
product_name: prod.name + (actualBasePrice === 0 ? "" : ""),
|
||||||
base_price: actualBasePrice,
|
base_price: actualBasePrice,
|
||||||
billing_interval: prod.billing_interval,
|
billing_interval: prod.billing_interval,
|
||||||
selected_modules: selectedModules,
|
selected_modules: selectedModules,
|
||||||
|
|||||||
Reference in New Issue
Block a user