fix: Buffers when combined with seated events (#20233)
This commit is contained in:
1 parent
9b952fd922
commit
c21094ab0a
4 files changed
+303
-245
No files matched your search
@@ -0,0 +1,128 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
||||
import { getBusyTimes } from "./getBusyTimes";
|
||||
|
||||
const startOfTomorrow = dayjs().add(1, "day").startOf("day");
|
||||
const tomorrowDate = startOfTomorrow.format("YYYY-MM-DD");
|
||||
|
||||
const mockBookings = ({
|
||||
beforeEventBuffer = 0,
|
||||
afterEventBuffer = 0,
|
||||
seatsPerTimeSlot,
|
||||
}: {
|
||||
beforeEventBuffer?: number;
|
||||
afterEventBuffer?: number;
|
||||
seatsPerTimeSlot?: number;
|
||||
}) => [
|
||||
{
|
||||
id: 1,
|
||||
startTime: startOfTomorrow.set("hour", 10).toDate(),
|
||||
endTime: startOfTomorrow.set("hour", 11).toDate(),
|
||||
title: "Booking Between X and Y",
|
||||
userId: 1,
|
||||
uid: "xxxx1",
|
||||
eventType: {
|
||||
id: 1,
|
||||
beforeEventBuffer,
|
||||
afterEventBuffer,
|
||||
seatsPerTimeSlot: seatsPerTimeSlot ?? null,
|
||||
},
|
||||
_count: {
|
||||
seatsReferences: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
startTime: startOfTomorrow.set("hour", 14).toDate(),
|
||||
endTime: startOfTomorrow.set("hour", 15).toDate(),
|
||||
title: "Booking Between X and Y",
|
||||
userId: 1,
|
||||
uid: "xxxx2",
|
||||
eventType: {
|
||||
id: 1,
|
||||
beforeEventBuffer,
|
||||
afterEventBuffer,
|
||||
seatsPerTimeSlot: seatsPerTimeSlot ?? null,
|
||||
},
|
||||
...(seatsPerTimeSlot
|
||||
? {
|
||||
_count: {
|
||||
seatsReferences: 1,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
];
|
||||
|
||||
describe("getBusyTimes", () => {
|
||||
it("blocks a regular time slot", async () => {
|
||||
const busyTimes = await getBusyTimes({
|
||||
credentials: [],
|
||||
userId: 1,
|
||||
userEmail: "exampleuser1@example.com",
|
||||
username: "exampleuser1",
|
||||
bypassBusyCalendarTimes: false,
|
||||
selectedCalendars: [],
|
||||
startTime: startOfTomorrow.format(),
|
||||
endTime: startOfTomorrow.endOf("day").format(),
|
||||
currentBookings: mockBookings({}),
|
||||
});
|
||||
expect(busyTimes).toEqual([
|
||||
expect.objectContaining({
|
||||
start: new Date(`${tomorrowDate}T10:00:00.000Z`),
|
||||
end: new Date(`${tomorrowDate}T11:00:00.000Z`),
|
||||
source: "eventType-1-booking-1",
|
||||
title: "Booking Between X and Y",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
start: new Date(`${tomorrowDate}T14:00:00.000Z`),
|
||||
end: new Date(`${tomorrowDate}T15:00:00.000Z`),
|
||||
source: "eventType-1-booking-2",
|
||||
title: "Booking Between X and Y",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
it("should block before and after buffer times", async () => {
|
||||
const busyTimes = await getBusyTimes({
|
||||
credentials: [],
|
||||
userId: 1,
|
||||
userEmail: "exampleuser1@example.com",
|
||||
username: "exampleuser1",
|
||||
bypassBusyCalendarTimes: false,
|
||||
selectedCalendars: [],
|
||||
startTime: startOfTomorrow.format(),
|
||||
endTime: startOfTomorrow.endOf("day").format(),
|
||||
currentBookings: [mockBookings({ beforeEventBuffer: 10 })[0]],
|
||||
});
|
||||
expect(busyTimes).toEqual([
|
||||
expect.objectContaining({
|
||||
start: new Date(`${tomorrowDate}T09:50:00.000Z`),
|
||||
end: new Date(`${tomorrowDate}T11:00:00.000Z`),
|
||||
source: "eventType-1-booking-1",
|
||||
title: "Booking Between X and Y",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
it("should have busy times only if seated with remaining seats when buffers exist", async () => {
|
||||
const busyTimes = await getBusyTimes({
|
||||
credentials: [],
|
||||
userId: 1,
|
||||
eventTypeId: 1,
|
||||
userEmail: "exampleuser1@example.com",
|
||||
username: "exampleuser1",
|
||||
bypassBusyCalendarTimes: false,
|
||||
selectedCalendars: [],
|
||||
startTime: startOfTomorrow.format(),
|
||||
endTime: startOfTomorrow.endOf("day").format(),
|
||||
currentBookings: [mockBookings({ beforeEventBuffer: 10, seatsPerTimeSlot: 10 })[0]],
|
||||
});
|
||||
expect(busyTimes).toEqual([
|
||||
expect.objectContaining({
|
||||
start: new Date(`${tomorrowDate}T09:50:00.000Z`),
|
||||
end: new Date(`${tomorrowDate}T10:00:00.000Z`),
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
+52
-116
@@ -1,6 +1,5 @@
|
||||
import type { Booking, EventType } from "@prisma/client";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import type { Prisma } from "@prisma/client";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { getBusyCalendarTimes } from "@calcom/lib/CalendarManager";
|
||||
@@ -18,6 +17,7 @@ import type { EventBusyDetails } from "@calcom/types/Calendar";
|
||||
import type { CredentialForCalendarService } from "@calcom/types/Credential";
|
||||
|
||||
import { getDefinedBufferTimes } from "../features/eventtypes/lib/getDefinedBufferTimes";
|
||||
import { BookingRepository as BookingRepo } from "./server/repository/booking";
|
||||
|
||||
export async function getBusyTimes(params: {
|
||||
credentials: CredentialForCalendarService[];
|
||||
@@ -100,15 +100,6 @@ export async function getBusyTimes(params: {
|
||||
const startTimeAdjustedWithMaxBuffer = dayjs(startTimeDate).subtract(maxBuffer, "minute").toDate();
|
||||
const endTimeAdjustedWithMaxBuffer = dayjs(endTimeDate).add(maxBuffer, "minute").toDate();
|
||||
|
||||
// startTime is less than endTimeDate and endTime grater than startTimeDate
|
||||
const sharedQuery: Prisma.BookingWhereInput = {
|
||||
startTime: { lte: endTimeAdjustedWithMaxBuffer },
|
||||
endTime: { gte: startTimeAdjustedWithMaxBuffer },
|
||||
status: {
|
||||
in: [BookingStatus.ACCEPTED],
|
||||
},
|
||||
};
|
||||
|
||||
// INFO: Refactored to allow this method to take in a list of current bookings for the user.
|
||||
// Will keep support for retrieving a user's bookings if the caller does not already supply them.
|
||||
// This function is called from multiple places but we aren't refactoring all of them at this moment
|
||||
@@ -116,119 +107,64 @@ export async function getBusyTimes(params: {
|
||||
let bookings = params.currentBookings;
|
||||
|
||||
if (!bookings) {
|
||||
const getBookingsForBusyTimesSpan = Sentry.startInactiveSpan({ name: "getBookingsForBusyTimes" });
|
||||
const bookingsSelect = Prisma.validator<Prisma.BookingSelect>()({
|
||||
id: true,
|
||||
uid: true,
|
||||
userId: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
title: true,
|
||||
eventType: {
|
||||
select: {
|
||||
id: true,
|
||||
afterEventBuffer: true,
|
||||
beforeEventBuffer: true,
|
||||
seatsPerTimeSlot: true,
|
||||
},
|
||||
},
|
||||
...(seatedEvent && {
|
||||
_count: {
|
||||
select: {
|
||||
seatsReferences: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
bookings = await BookingRepo.findAllExistingBookingsForEventTypeBetween({
|
||||
userIdAndEmailMap: new Map([[userId, userEmail]]),
|
||||
eventTypeId,
|
||||
startDate: startTimeAdjustedWithMaxBuffer,
|
||||
endDate: endTimeAdjustedWithMaxBuffer,
|
||||
seatedEvent,
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryOne = prisma.booking.findMany({
|
||||
where: {
|
||||
// User is primary host (individual events, or primary organizer)
|
||||
...sharedQuery,
|
||||
userId,
|
||||
},
|
||||
select: bookingsSelect,
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryTwo = prisma.booking.findMany({
|
||||
where: {
|
||||
...sharedQuery,
|
||||
attendees: {
|
||||
some: {
|
||||
email: userEmail,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
});
|
||||
|
||||
const unconfirmedBookingsBlockingSlots = eventTypeId
|
||||
? prisma.booking.findMany({
|
||||
where: {
|
||||
startTime: { lte: endTimeDate },
|
||||
endTime: { gte: startTimeDate },
|
||||
eventType: {
|
||||
id: eventTypeId,
|
||||
requiresConfirmation: true,
|
||||
requiresConfirmationWillBlockSlot: true,
|
||||
},
|
||||
status: {
|
||||
in: [BookingStatus.PENDING],
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
})
|
||||
: null;
|
||||
|
||||
const [resultOne, resultTwo, resultThree] = await Promise.all([
|
||||
currentBookingsAllUsersQueryOne,
|
||||
currentBookingsAllUsersQueryTwo,
|
||||
unconfirmedBookingsBlockingSlots,
|
||||
]);
|
||||
|
||||
bookings = [...resultOne, ...resultTwo, ...(resultThree ?? [])];
|
||||
|
||||
getBookingsForBusyTimesSpan.end();
|
||||
}
|
||||
|
||||
const bookingSeatCountMap: { [x: string]: number } = {};
|
||||
const busyTimes = bookings.reduce(
|
||||
(aggregate: EventBusyDetails[], { id, startTime, endTime, eventType, title, ...rest }) => {
|
||||
if (rest._count?.seatsReferences) {
|
||||
const bookedAt = `${dayjs(startTime).utc().format()}<>${dayjs(endTime).utc().format()}`;
|
||||
bookingSeatCountMap[bookedAt] = bookingSeatCountMap[bookedAt] || 0;
|
||||
bookingSeatCountMap[bookedAt]++;
|
||||
// Seat references on the current event are non-blocking until the event is fully booked.
|
||||
if (
|
||||
// there are still seats available.
|
||||
bookingSeatCountMap[bookedAt] < (eventType?.seatsPerTimeSlot || 1) &&
|
||||
// and this is the seated event, other event types should be blocked.
|
||||
eventTypeId === eventType?.id
|
||||
) {
|
||||
// then we do not add the booking to the busyTimes.
|
||||
return aggregate;
|
||||
const busyTimes = bookings.reduce((aggregate: EventBusyDetails[], booking) => {
|
||||
const { id, startTime, endTime, eventType, title, ...rest } = booking;
|
||||
|
||||
const minutesToBlockBeforeEvent = (eventType?.beforeEventBuffer || 0) + (afterEventBuffer || 0);
|
||||
const minutesToBlockAfterEvent = (eventType?.afterEventBuffer || 0) + (beforeEventBuffer || 0);
|
||||
|
||||
if (rest._count?.seatsReferences) {
|
||||
const bookedAt = `${dayjs(startTime).utc().format()}<>${dayjs(endTime).utc().format()}`;
|
||||
bookingSeatCountMap[bookedAt] = bookingSeatCountMap[bookedAt] || 0;
|
||||
bookingSeatCountMap[bookedAt]++;
|
||||
// Seat references on the current event are non-blocking until the event is fully booked.
|
||||
if (
|
||||
// there are still seats available.
|
||||
bookingSeatCountMap[bookedAt] < (eventType?.seatsPerTimeSlot || 1) &&
|
||||
// and this is the seated event, other event types should be blocked.
|
||||
eventTypeId === eventType?.id
|
||||
) {
|
||||
// then we ONLY add the before/after buffer times as busy times.
|
||||
if (minutesToBlockBeforeEvent) {
|
||||
aggregate.push({
|
||||
start: dayjs(startTime).subtract(minutesToBlockBeforeEvent, "minute").toDate(),
|
||||
end: dayjs(startTime).toDate(), // The event starts after the buffer
|
||||
});
|
||||
}
|
||||
if (minutesToBlockAfterEvent) {
|
||||
aggregate.push({
|
||||
start: dayjs(endTime).toDate(), // The event ends before the buffer
|
||||
end: dayjs(endTime).add(minutesToBlockAfterEvent, "minute").toDate(),
|
||||
});
|
||||
}
|
||||
// if it does get blocked at this point; we remove the bookingSeatCountMap entry
|
||||
// doing this allows using the map later to remove the ranges from calendar busy times.
|
||||
delete bookingSeatCountMap[bookedAt];
|
||||
}
|
||||
if (rest.uid === rescheduleUid) {
|
||||
return aggregate;
|
||||
}
|
||||
aggregate.push({
|
||||
start: dayjs(startTime)
|
||||
.subtract((eventType?.beforeEventBuffer || 0) + (afterEventBuffer || 0), "minute")
|
||||
.toDate(),
|
||||
end: dayjs(endTime)
|
||||
.add((eventType?.afterEventBuffer || 0) + (beforeEventBuffer || 0), "minute")
|
||||
.toDate(),
|
||||
title,
|
||||
source: `eventType-${eventType?.id}-booking-${id}`,
|
||||
});
|
||||
// if it does get blocked at this point; we remove the bookingSeatCountMap entry
|
||||
// doing this allows using the map later to remove the ranges from calendar busy times.
|
||||
delete bookingSeatCountMap[bookedAt];
|
||||
}
|
||||
// rescheduling the same booking to the same time should be possible. Why?
|
||||
if (rest.uid === rescheduleUid) {
|
||||
return aggregate;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
aggregate.push({
|
||||
start: dayjs(startTime).subtract(minutesToBlockBeforeEvent, "minute").toDate(),
|
||||
end: dayjs(endTime).add(minutesToBlockAfterEvent, "minute").toDate(),
|
||||
title,
|
||||
source: `eventType-${eventType?.id}-booking-${id}`,
|
||||
});
|
||||
return aggregate;
|
||||
}, []);
|
||||
|
||||
logger.debug(
|
||||
`Busy Time from Cal Bookings ${JSON.stringify({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
import type { FormResponse } from "@calcom/app-store/routing-forms/types/types";
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
@@ -161,6 +161,117 @@ export class BookingRepository {
|
||||
});
|
||||
}
|
||||
|
||||
static async findAllExistingBookingsForEventTypeBetween({
|
||||
eventTypeId,
|
||||
seatedEvent = false,
|
||||
startDate,
|
||||
endDate,
|
||||
userIdAndEmailMap,
|
||||
}: {
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
eventTypeId?: number | null;
|
||||
seatedEvent?: boolean;
|
||||
userIdAndEmailMap: Map<number, string>;
|
||||
}) {
|
||||
const sharedQuery = {
|
||||
startTime: { lte: endDate },
|
||||
endTime: { gte: startDate },
|
||||
status: {
|
||||
in: [BookingStatus.ACCEPTED],
|
||||
},
|
||||
};
|
||||
|
||||
const bookingsSelect = Prisma.validator<Prisma.BookingSelect>()({
|
||||
id: true,
|
||||
uid: true,
|
||||
userId: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
title: true,
|
||||
attendees: true,
|
||||
eventType: {
|
||||
select: {
|
||||
id: true,
|
||||
onlyShowFirstAvailableSlot: true,
|
||||
afterEventBuffer: true,
|
||||
beforeEventBuffer: true,
|
||||
seatsPerTimeSlot: true,
|
||||
requiresConfirmationWillBlockSlot: true,
|
||||
requiresConfirmation: true,
|
||||
allowReschedulingPastBookings: true,
|
||||
},
|
||||
},
|
||||
...(seatedEvent && {
|
||||
_count: {
|
||||
select: {
|
||||
seatsReferences: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryOne = prisma.booking.findMany({
|
||||
where: {
|
||||
...sharedQuery,
|
||||
userId: {
|
||||
in: Array.from(userIdAndEmailMap.keys()),
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryTwo = prisma.booking.findMany({
|
||||
where: {
|
||||
...sharedQuery,
|
||||
attendees: {
|
||||
some: {
|
||||
email: {
|
||||
in: Array.from(userIdAndEmailMap.values()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryThree = eventTypeId
|
||||
? prisma.booking.findMany({
|
||||
where: {
|
||||
startTime: { lte: endDate },
|
||||
endTime: { gte: startDate },
|
||||
eventType: {
|
||||
id: eventTypeId,
|
||||
requiresConfirmation: true,
|
||||
requiresConfirmationWillBlockSlot: true,
|
||||
},
|
||||
status: {
|
||||
in: [BookingStatus.PENDING],
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
})
|
||||
: [];
|
||||
|
||||
const [resultOne, resultTwo, resultThree] = await Promise.all([
|
||||
currentBookingsAllUsersQueryOne,
|
||||
currentBookingsAllUsersQueryTwo,
|
||||
currentBookingsAllUsersQueryThree,
|
||||
]);
|
||||
// Prevent duplicate booking records when the organizer books his own event type.
|
||||
//
|
||||
// *Three is about PENDING, so will never overlap
|
||||
// with the other two, which are about ACCEPTED. But ACCEPTED affects *One & *Two
|
||||
// and WILL result in a duplicate booking if the organizer books himself.
|
||||
const resultTwoWithOrganizersRemoved = resultTwo.filter((booking) => {
|
||||
if (!booking.userId) return true;
|
||||
const organizerEmail = userIdAndEmailMap.get(booking.userId);
|
||||
return !booking.attendees.some((attendee) => attendee.email === organizerEmail);
|
||||
});
|
||||
|
||||
return [...resultOne, ...resultTwoWithOrganizersRemoved, ...resultThree];
|
||||
}
|
||||
|
||||
static async getAllBookingsForRoundRobin({
|
||||
users,
|
||||
eventTypeId,
|
||||
|
||||
@@ -28,12 +28,13 @@ import {
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import monitorCallbackAsync, { monitorCallbackSync } from "@calcom/lib/sentryWrapper";
|
||||
import { BookingRepository as BookingRepo } from "@calcom/lib/server/repository/booking";
|
||||
import { EventTypeRepository } from "@calcom/lib/server/repository/eventType";
|
||||
import { UserRepository, withSelectedCalendars } from "@calcom/lib/server/repository/user";
|
||||
import getSlots from "@calcom/lib/slots";
|
||||
import prisma, { availabilityUserSelect } from "@calcom/prisma";
|
||||
import { PeriodType, Prisma } from "@calcom/prisma/client";
|
||||
import { BookingStatus, SchedulingType } from "@calcom/prisma/enums";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||
import type { EventBusyDate } from "@calcom/types/Calendar";
|
||||
import type { CredentialPayload, CredentialForCalendarService } from "@calcom/types/Credential";
|
||||
@@ -225,20 +226,6 @@ export async function getRegularOrDynamicEventType(
|
||||
: await getEventType(input, organizationDetails);
|
||||
}
|
||||
|
||||
const groupTimeSlotsByDay = (timeSlots: { time: Dayjs }[]) => {
|
||||
return timeSlots.reduce((acc: Record<string, string[]>, { time }) => {
|
||||
const day = time.format("YYYY-MM-DD"); // Group by date
|
||||
const formattedTime = time.format("HH:mm"); // Format as HH:mm
|
||||
|
||||
if (!acc[day]) {
|
||||
acc[day] = [];
|
||||
}
|
||||
acc[day].push(formattedTime);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
function applyOccupiedSeatsToCurrentSeats(currentSeats: CurrentSeats, occupiedSeats: SelectedSlots[]) {
|
||||
const occupiedSeatsCount = countBy(occupiedSeats, (item) => item.slotUtcStartDate.toISOString());
|
||||
Object.keys(occupiedSeatsCount).forEach((date) => {
|
||||
@@ -761,102 +748,6 @@ async function getTeamIdFromSlug(
|
||||
return team?.id;
|
||||
}
|
||||
|
||||
async function getExistingBookings(
|
||||
startTimeDate: Date,
|
||||
endTimeDate: Date,
|
||||
eventType: Awaited<ReturnType<typeof getEventType>>,
|
||||
sharedQuery: {
|
||||
startTime: {
|
||||
lte: Date;
|
||||
};
|
||||
endTime: {
|
||||
gte: Date;
|
||||
};
|
||||
status: {
|
||||
in: "ACCEPTED"[];
|
||||
};
|
||||
},
|
||||
usersWithCredentials: ReturnType<typeof getUsersWithCredentials>,
|
||||
allUserIds: number[]
|
||||
) {
|
||||
const bookingsSelect = Prisma.validator<Prisma.BookingSelect>()({
|
||||
id: true,
|
||||
uid: true,
|
||||
userId: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
title: true,
|
||||
attendees: true,
|
||||
eventType: {
|
||||
select: {
|
||||
id: true,
|
||||
onlyShowFirstAvailableSlot: true,
|
||||
afterEventBuffer: true,
|
||||
beforeEventBuffer: true,
|
||||
seatsPerTimeSlot: true,
|
||||
requiresConfirmationWillBlockSlot: true,
|
||||
requiresConfirmation: true,
|
||||
allowReschedulingPastBookings: true,
|
||||
},
|
||||
},
|
||||
...(!!eventType?.seatsPerTimeSlot && {
|
||||
_count: {
|
||||
select: {
|
||||
seatsReferences: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryOne = prisma.booking.findMany({
|
||||
where: {
|
||||
...sharedQuery,
|
||||
userId: {
|
||||
in: allUserIds,
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryTwo = prisma.booking.findMany({
|
||||
where: {
|
||||
...sharedQuery,
|
||||
attendees: {
|
||||
some: {
|
||||
email: {
|
||||
in: usersWithCredentials.map((user) => user.email),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
});
|
||||
|
||||
const currentBookingsAllUsersQueryThree = prisma.booking.findMany({
|
||||
where: {
|
||||
startTime: { lte: endTimeDate },
|
||||
endTime: { gte: startTimeDate },
|
||||
eventType: {
|
||||
id: eventType?.id,
|
||||
requiresConfirmation: true,
|
||||
requiresConfirmationWillBlockSlot: true,
|
||||
},
|
||||
status: {
|
||||
in: [BookingStatus.PENDING],
|
||||
},
|
||||
},
|
||||
select: bookingsSelect,
|
||||
});
|
||||
|
||||
const [resultOne, resultTwo, resultThree] = await Promise.all([
|
||||
currentBookingsAllUsersQueryOne,
|
||||
currentBookingsAllUsersQueryTwo,
|
||||
currentBookingsAllUsersQueryThree,
|
||||
]);
|
||||
|
||||
return [...resultOne, ...resultTwo, ...resultThree];
|
||||
}
|
||||
|
||||
async function getOOODates(startTimeDate: Date, endTimeDate: Date, allUserIds: number[]) {
|
||||
return await prisma.outOfOfficeEntry.findMany({
|
||||
where: {
|
||||
@@ -984,25 +875,17 @@ const calculateHostsAndAvailabilities = async ({
|
||||
const endTimeDate =
|
||||
input.rescheduleUid && durationToUse ? endTime.add(durationToUse, "minute").toDate() : endTime.toDate();
|
||||
|
||||
const sharedQuery = {
|
||||
startTime: { lte: endTimeDate },
|
||||
endTime: { gte: startTimeDate },
|
||||
status: {
|
||||
in: [BookingStatus.ACCEPTED],
|
||||
},
|
||||
};
|
||||
const userIdAndEmailMap = new Map(usersWithCredentials.map((user) => [user.id, user.email]));
|
||||
const allUserIds = Array.from(userIdAndEmailMap.keys());
|
||||
|
||||
const allUserIds = usersWithCredentials.map((user) => user.id);
|
||||
const [currentBookingsAllUsers, outOfOfficeDaysAllUsers] = await Promise.all([
|
||||
monitorCallbackAsync(
|
||||
getExistingBookings,
|
||||
startTimeDate,
|
||||
endTimeDate,
|
||||
eventType,
|
||||
sharedQuery,
|
||||
usersWithCredentials,
|
||||
allUserIds
|
||||
),
|
||||
monitorCallbackAsync(BookingRepo.findAllExistingBookingsForEventTypeBetween, {
|
||||
startDate: startTimeDate,
|
||||
endDate: endTimeDate,
|
||||
eventTypeId: eventType.id,
|
||||
seatedEvent: Boolean(eventType.seatsPerTimeSlot),
|
||||
userIdAndEmailMap,
|
||||
}),
|
||||
monitorCallbackAsync(getOOODates, startTimeDate, endTimeDate, allUserIds),
|
||||
]);
|
||||
|
||||
|
||||
Reference in new issue
Block a user