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
This commit is contained in:
108
lib/price-calculator.ts
Normal file
108
lib/price-calculator.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user