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:
@@ -272,7 +272,12 @@ const getAvailableTimes = os
|
||||
.input(
|
||||
z.object({
|
||||
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 }) => {
|
||||
@@ -287,13 +292,22 @@ const getAvailableTimes = os
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get treatment duration
|
||||
const treatment = await treatmentsKV.getItem(input.treatmentId);
|
||||
if (!treatment) {
|
||||
throw new Error("Behandlung nicht gefunden.");
|
||||
// Get multiple treatments and calculate total duration
|
||||
const treatments = await Promise.all(
|
||||
input.treatmentIds.map(id => treatmentsKV.getItem(id))
|
||||
);
|
||||
|
||||
// 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
|
||||
const [year, month, day] = input.date.split('-').map(Number);
|
||||
@@ -344,36 +358,38 @@ const getAvailableTimes = os
|
||||
['pending', 'confirmed', 'completed'].includes(booking.status)
|
||||
);
|
||||
|
||||
// Optimize treatment duration lookup with Map caching
|
||||
const uniqueTreatmentIds = [...new Set(dateBookings.map(booking => booking.treatmentId))];
|
||||
// Build cache only for legacy treatmentId bookings
|
||||
const legacyTreatmentIds = [...new Set(dateBookings.filter(b => b.treatmentId).map(b => b.treatmentId as string))];
|
||||
const treatmentDurationMap = new Map<string, number>();
|
||||
|
||||
for (const treatmentId of uniqueTreatmentIds) {
|
||||
const treatment = await treatmentsKV.getItem(treatmentId);
|
||||
treatmentDurationMap.set(treatmentId, treatment?.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);
|
||||
// Only build cache if there are legacy bookings
|
||||
if (legacyTreatmentIds.length > 0) {
|
||||
for (const id of legacyTreatmentIds) {
|
||||
const t = await treatmentsKV.getItem(id);
|
||||
treatmentDurationMap.set(id, t?.duration || 60);
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out booking conflicts
|
||||
const availableTimesFiltered = availableTimes.filter(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 bookingStartMinutes = parseTime(booking.appointmentTime);
|
||||
const bookingDuration = bookingTreatments.get(booking.id) || 60;
|
||||
const bookingEndMinutes = bookingStartMinutes + bookingDuration;
|
||||
let bookingDuration: number;
|
||||
if (booking.treatments && booking.treatments.length > 0) {
|
||||
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
|
||||
return slotStartMinutes < bookingEndMinutes && slotEndMinutes > bookingStartMinutes;
|
||||
const bookingStart = parseTime(booking.appointmentTime);
|
||||
const bookingEnd = bookingStart + bookingDuration;
|
||||
return slotStartMinutes < bookingEnd && slotEndMinutes > bookingStart;
|
||||
});
|
||||
|
||||
return !hasConflict;
|
||||
|
Reference in New Issue
Block a user