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(
|
.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;
|
||||||
|
Reference in New Issue
Block a user