* init logic for handling reservation for round robin event * remove unused comments * add function to get already reserved slots * fixup: add correct logic for round robin slots reservation * fixup: implement PR feedback * fixup: update logic to handle fixed and non fixed round robin hosts * update slots repository * fixup: implement PR feedback * fixup: implement PR feedback * add tests for round robin slot reservation with non fixed hosts * add tests for round robin slot reservation with fixed and non fixed hosts * fix: implement PR feedback * fix: e2e test * fix: move validateRoundRobinSlotAvailability to core libraries * fix: merge conflicts * fix: merge conflicts * update slots.ts for platform libraries * fix: import logic from platform libraries * cleanup * fix: import path * fix: missing import * fix: test code and handle error thrown --------- Co-authored-by: supalarry <laurisskraucis@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import { DateTime } from "luxon";
|
|
|
|
import { HttpError } from "@calcom/lib/http-error";
|
|
import { prisma } from "@calcom/prisma";
|
|
import type { Host } from "@calcom/prisma/client";
|
|
|
|
export async function validateRoundRobinSlotAvailability(
|
|
eventTypeId: number,
|
|
startDate: DateTime,
|
|
endDate: DateTime,
|
|
hosts: Host[]
|
|
) {
|
|
const fixedHosts = hosts.filter((host) => host.isFixed === true);
|
|
const nonFixedHosts = hosts.filter((host) => host.isFixed === false);
|
|
|
|
if (fixedHosts.length > 0) {
|
|
await validateFixedHostsAvailability(eventTypeId, startDate, endDate, fixedHosts);
|
|
} else {
|
|
await validateNonFixedHostsAvailability(eventTypeId, startDate, endDate, nonFixedHosts);
|
|
}
|
|
}
|
|
|
|
async function validateFixedHostsAvailability(
|
|
eventTypeId: number,
|
|
startDate: DateTime,
|
|
endDate: DateTime,
|
|
hosts: Host[]
|
|
) {
|
|
const existingBooking = await prisma.booking.findFirst({
|
|
where: {
|
|
eventTypeId,
|
|
startTime: startDate.toJSDate(),
|
|
endTime: endDate.toJSDate(),
|
|
},
|
|
select: { attendees: true, userId: true, status: true },
|
|
});
|
|
const existingSlotReservation = await prisma.selectedSlots.count({
|
|
where: {
|
|
eventTypeId,
|
|
slotUtcStartDate: startDate.toISO() ?? "",
|
|
slotUtcEndDate: endDate.toISO() ?? "",
|
|
// Only consider non-expired reservations
|
|
releaseAt: { gt: DateTime.utc().toJSDate() },
|
|
},
|
|
});
|
|
|
|
const hasHostAsAttendee = hosts.some(
|
|
(host) =>
|
|
existingBooking?.attendees.some((attendee) => attendee.id === host.userId) ||
|
|
existingBooking?.userId === host.userId
|
|
);
|
|
|
|
if (existingSlotReservation > 0) {
|
|
throw new HttpError({
|
|
statusCode: 422,
|
|
message: `Can't reserve the slot because the round robin event type has no available hosts left at this time slot.`,
|
|
});
|
|
}
|
|
|
|
if (hasHostAsAttendee) {
|
|
throw new HttpError({
|
|
statusCode: 422,
|
|
message: `Can't reserve a slot if the event is already booked.`,
|
|
});
|
|
}
|
|
}
|
|
|
|
async function validateNonFixedHostsAvailability(
|
|
eventTypeId: number,
|
|
startDate: DateTime,
|
|
endDate: DateTime,
|
|
hosts: Host[]
|
|
) {
|
|
const existingSlotReservations = await prisma.selectedSlots.count({
|
|
where: {
|
|
eventTypeId,
|
|
slotUtcStartDate: startDate.toISO() ?? "",
|
|
slotUtcEndDate: endDate.toISO() ?? "",
|
|
// Only consider non-expired reservations
|
|
releaseAt: { gt: DateTime.utc().toJSDate() },
|
|
},
|
|
});
|
|
|
|
if (existingSlotReservations === hosts.length) {
|
|
throw new HttpError({
|
|
statusCode: 422,
|
|
message: `Can't reserve the slot because the round robin event type has no available hosts left at this time slot.`,
|
|
});
|
|
}
|
|
}
|