- 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
109 lines
2.8 KiB
TypeScript
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');
|
|
}
|
|
}
|