Files
Atlas/lib/price-calculator.ts
Julia Wehden a2c95c70e7 feat: Equipment-System, Buchungsbearbeitung, Kundenadresse, LexOffice-Fix
- Vintage Modell hinzugefuegt
- Equipment Multi-Select (Neue Buchung + Bearbeitung)
- Kundenadresse in Formularen
- Bearbeiten-Seite fuer Buchungen
- Abbau-Zeiten in Formularen und Uebersicht
- Vertrag PDF nur bei Privatkunden
- LexOffice Kontakt-Erstellung Fix (BUSINESS)
- Zurueck-Pfeil auf Touren-Seite
2026-03-19 16:21:55 +01:00

109 lines
2.8 KiB
TypeScript

interface PriceConfig {
basePrice: number;
kmFlatRate: number;
kmFlatRateUpTo: number;
pricePerKm: number;
kmMultiplier: number;
}
interface PriceBreakdown {
basePrice: number;
kmFlatRate: number;
kmAdditionalNet: number;
kmAdditionalGross: number;
kmTotalGross: number;
totalPrice: number;
distance?: number;
}
export class PriceCalculator {
private static readonly VAT_RATE = 1.19;
static calculateKmCharge(
distance: number,
config: PriceConfig
): {
kmTotalGross: number;
kmFlatRate: number;
kmAdditionalNet: number;
kmAdditionalGross: number;
} {
const { kmFlatRate, kmFlatRateUpTo, pricePerKm, kmMultiplier } = config;
if (distance <= kmFlatRateUpTo) {
return {
kmTotalGross: kmFlatRate,
kmFlatRate: kmFlatRate,
kmAdditionalNet: 0,
kmAdditionalGross: 0,
};
}
const extraKm = distance - kmFlatRateUpTo;
const kmAdditionalNet = extraKm * kmMultiplier * pricePerKm;
const kmAdditionalGross = Math.round(kmAdditionalNet * this.VAT_RATE * 100) / 100;
const kmTotalGross = Math.round((kmFlatRate + kmAdditionalGross) * 100) / 100;
return {
kmTotalGross,
kmFlatRate,
kmAdditionalNet: Math.round(kmAdditionalNet * 100) / 100,
kmAdditionalGross,
};
}
static calculateTotalPrice(
basePrice: number,
distance: number | null | undefined,
config: PriceConfig
): PriceBreakdown {
if (!distance || distance <= 0) {
return {
basePrice,
kmFlatRate: 0,
kmAdditionalNet: 0,
kmAdditionalGross: 0,
kmTotalGross: 0,
totalPrice: basePrice,
};
}
const kmCharge = this.calculateKmCharge(distance, config);
const totalPrice = Math.round((basePrice + kmCharge.kmTotalGross) * 100) / 100;
return {
basePrice,
kmFlatRate: kmCharge.kmFlatRate,
kmAdditionalNet: kmCharge.kmAdditionalNet,
kmAdditionalGross: kmCharge.kmAdditionalGross,
kmTotalGross: kmCharge.kmTotalGross,
totalPrice,
distance,
};
}
static formatPriceBreakdown(breakdown: PriceBreakdown): string {
const lines = [
`Grundpreis Fotobox: ${breakdown.basePrice.toFixed(2)}`,
];
if (breakdown.distance && breakdown.distance > 0) {
lines.push(`\nKilometerpauschale (${breakdown.distance.toFixed(1)}km):`);
if (breakdown.kmFlatRate > 0) {
lines.push(` - Pauschale: ${breakdown.kmFlatRate.toFixed(2)}`);
}
if (breakdown.kmAdditionalGross > 0) {
lines.push(` - Zusätzlich: ${breakdown.kmAdditionalGross.toFixed(2)}`);
}
lines.push(` = Gesamt KM: ${breakdown.kmTotalGross.toFixed(2)}`);
}
lines.push(`\nGesamtpreis: ${breakdown.totalPrice.toFixed(2)}`);
return lines.join('\n');
}
}