Fix: Korrigiere Konflikt-Erkennung für Multi-Treatment-Buchungen in recurring-availability.ts - Berechne Dauer korrekt für neue Behandlungen-Arrays - Filtere undefined Treatment-IDs aus Legacy-Cache - Erstelle Treatment-Cache nur bei Bedarf

This commit is contained in:
2025-10-08 18:26:09 +02:00
parent d153aad8b3
commit 9583148e02

View File

@@ -272,7 +272,12 @@ const getAvailableTimes = os
.input( .input(
z.object({ z.object({
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
treatmentId: z.string(), treatmentIds: z.array(z.string())
.min(1, "Mindestens eine Behandlung muss ausgewählt werden")
.max(3, "Maximal 3 Behandlungen können ausgewählt werden")
.refine(list => {
return list.length === new Set(list).size;
}, { message: "Doppelte Behandlungen sind nicht erlaubt" }),
}) })
) )
.handler(async ({ input }) => { .handler(async ({ input }) => {
@@ -287,13 +292,22 @@ const getAvailableTimes = os
return []; return [];
} }
// Get treatment duration // Get multiple treatments and calculate total duration
const treatment = await treatmentsKV.getItem(input.treatmentId); const treatments = await Promise.all(
if (!treatment) { input.treatmentIds.map(id => treatmentsKV.getItem(id))
throw new Error("Behandlung nicht gefunden."); );
// Validate that all treatments exist
const missingTreatments = treatments
.map((t, i) => t ? null : input.treatmentIds[i])
.filter(id => id !== null);
if (missingTreatments.length > 0) {
throw new Error(`Behandlung(en) nicht gefunden: ${missingTreatments.join(', ')}`);
} }
const treatmentDuration = treatment.duration; // Calculate total duration by summing all treatment durations
const treatmentDuration = treatments.reduce((sum, t) => sum + (t?.duration || 0), 0);
// Parse the date to get day of week // Parse the date to get day of week
const [year, month, day] = input.date.split('-').map(Number); const [year, month, day] = input.date.split('-').map(Number);
@@ -344,36 +358,38 @@ const getAvailableTimes = os
['pending', 'confirmed', 'completed'].includes(booking.status) ['pending', 'confirmed', 'completed'].includes(booking.status)
); );
// Optimize treatment duration lookup with Map caching // Build cache only for legacy treatmentId bookings
const uniqueTreatmentIds = [...new Set(dateBookings.map(booking => booking.treatmentId))]; const legacyTreatmentIds = [...new Set(dateBookings.filter(b => b.treatmentId).map(b => b.treatmentId as string))];
const treatmentDurationMap = new Map<string, number>(); const treatmentDurationMap = new Map<string, number>();
for (const treatmentId of uniqueTreatmentIds) { // Only build cache if there are legacy bookings
const treatment = await treatmentsKV.getItem(treatmentId); if (legacyTreatmentIds.length > 0) {
treatmentDurationMap.set(treatmentId, treatment?.duration || 60); for (const id of legacyTreatmentIds) {
const t = await treatmentsKV.getItem(id);
treatmentDurationMap.set(id, t?.duration || 60);
} }
// Get treatment durations for all bookings using the cached map
const bookingTreatments = new Map();
for (const booking of dateBookings) {
// Use bookedDurationMinutes if available, otherwise fallback to treatment duration
const duration = booking.bookedDurationMinutes || treatmentDurationMap.get(booking.treatmentId) || 60;
bookingTreatments.set(booking.id, duration);
} }
// Filter out booking conflicts // Filter out booking conflicts
const availableTimesFiltered = availableTimes.filter(slotTime => { const availableTimesFiltered = availableTimes.filter(slotTime => {
const slotStartMinutes = parseTime(slotTime); const slotStartMinutes = parseTime(slotTime);
const slotEndMinutes = slotStartMinutes + treatmentDuration; const slotEndMinutes = slotStartMinutes + treatmentDuration; // total from selected treatments
// Check if this slot overlaps with any existing booking
const hasConflict = dateBookings.some(booking => { const hasConflict = dateBookings.some(booking => {
const bookingStartMinutes = parseTime(booking.appointmentTime); let bookingDuration: number;
const bookingDuration = bookingTreatments.get(booking.id) || 60; if (booking.treatments && booking.treatments.length > 0) {
const bookingEndMinutes = bookingStartMinutes + bookingDuration; bookingDuration = booking.treatments.reduce((sum: number, t: { duration: number }) => sum + t.duration, 0);
} else if (booking.bookedDurationMinutes) {
bookingDuration = booking.bookedDurationMinutes;
} else if (booking.treatmentId) {
bookingDuration = treatmentDurationMap.get(booking.treatmentId) || 60;
} else {
bookingDuration = 60;
}
// Check overlap: slotStart < bookingEnd && slotEnd > bookingStart const bookingStart = parseTime(booking.appointmentTime);
return slotStartMinutes < bookingEndMinutes && slotEndMinutes > bookingStartMinutes; const bookingEnd = bookingStart + bookingDuration;
return slotStartMinutes < bookingEnd && slotEndMinutes > bookingStart;
}); });
return !hasConflict; return !hasConflict;