feat(order): add net/tax/gross breakdown for one-time and monthly totals
All checks were successful
Staging Build / build (push) Successful in 3m28s

This commit is contained in:
DanielS
2026-07-03 08:50:28 +02:00
parent 147dab5607
commit 7a61002ef5
5 changed files with 309 additions and 90 deletions

View File

@@ -36,6 +36,18 @@ export default async function OrderSuccessPage({
const o = order as Order
const items = o.order_data?.items ?? []
const taxRate = o.order_data?.tax_rate ?? 19
const oneTimeItems = items.filter((item) => item.billing_interval === 'one_time')
const monthlyItems = items.filter((item) => item.billing_interval === 'monthly')
const oneTimeNet = oneTimeItems.reduce((sum, item) => sum + (item.item_total || 0), 0)
const oneTimeTax = Math.round(oneTimeNet * (taxRate / 100) * 100) / 100
const oneTimeGross = Math.round((oneTimeNet + oneTimeTax) * 100) / 100
const monthlyNet = monthlyItems.reduce((sum, item) => sum + (item.item_total || 0), 0)
const monthlyTax = Math.round(monthlyNet * (taxRate / 100) * 100) / 100
const monthlyGross = Math.round((monthlyNet + monthlyTax) * 100) / 100
const statusLabel: Record<string, string> = {
pending: 'Eingegangen',
active: 'In Bearbeitung',
@@ -104,12 +116,49 @@ export default async function OrderSuccessPage({
<Separator className="bg-white/10" />
{/* Gesamtbetrag */}
<div className="flex justify-between text-lg font-bold">
<span className="text-slate-300">Gesamtbetrag</span>
<span className="text-gradient">
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(o.total_price)}
</span>
</div>
{oneTimeNet > 0 && (
<div className="space-y-1">
{monthlyNet > 0 && <p className="text-xs font-semibold text-slate-500 uppercase tracking-wider">Einmaliger Betrag</p>}
<div className="flex justify-between text-sm text-slate-400">
<span>Netto:</span>
<span>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(oneTimeNet)}</span>
</div>
<div className="flex justify-between text-sm text-slate-400">
<span>zzgl. {taxRate}% MwSt.:</span>
<span>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(oneTimeTax)}</span>
</div>
<div className="flex justify-between text-base font-bold text-white">
<span>Brutto Gesamtbetrag:</span>
<span className={monthlyNet > 0 ? "text-white" : "text-gradient"}>
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(oneTimeGross)}
</span>
</div>
</div>
)}
{oneTimeNet > 0 && monthlyNet > 0 && (
<Separator className="bg-white/5 my-2" />
)}
{monthlyNet > 0 && (
<div className="space-y-1">
{oneTimeNet > 0 && <p className="text-xs font-semibold text-slate-500 uppercase tracking-wider">Monatlicher Betrag</p>}
<div className="flex justify-between text-sm text-slate-400">
<span>Netto:</span>
<span>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(monthlyNet)} / Monat</span>
</div>
<div className="flex justify-between text-sm text-slate-400">
<span>zzgl. {taxRate}% MwSt.:</span>
<span>{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(monthlyTax)} / Monat</span>
</div>
<div className="flex justify-between text-base font-bold text-white">
<span>Brutto Gesamtbetrag:</span>
<span className="text-gradient">
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(monthlyGross)} / Monat
</span>
</div>
</div>
)}
{o.order_data?.price_multiplier !== null && o.order_data?.price_multiplier !== undefined && (
<div className="text-sm text-green-400 font-semibold bg-green-500/10 p-3 rounded-lg border border-green-500/20 space-y-1">