fix: Seat Attendee Rescheduling Logic (#14784)
* When rescheduling to new time slot don't trigger reschedule logic * Add unique iCalUID when rescheduling to new slot * Generating iCalUID use v4 uuid * When moving an attendee to a new booking. Only move the attendee --------- Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
This commit is contained in:
co-authored by
Udit Takkar
parent
d666f2926c
commit
e1bdac077a
@@ -1,5 +1,5 @@
|
||||
import short from "short-uuid";
|
||||
import { v5 as uuidv5 } from "uuid";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
@@ -15,11 +15,13 @@ const getICalUID = ({
|
||||
uid,
|
||||
event,
|
||||
defaultToEventUid,
|
||||
attendeeId,
|
||||
}: {
|
||||
uid?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
event?: { iCalUID?: string | null; uid?: string | null; [key: string]: any };
|
||||
defaultToEventUid?: boolean;
|
||||
attendeeId?: number;
|
||||
}) => {
|
||||
if (event?.iCalUID) return event.iCalUID;
|
||||
|
||||
@@ -29,8 +31,8 @@ const getICalUID = ({
|
||||
|
||||
const translator = short();
|
||||
|
||||
uid = translator.fromUUID(uuidv5(APP_NAME, uuidv5.URL));
|
||||
return `${uid}@${APP_NAME}`;
|
||||
uid = translator.fromUUID(uuidv4());
|
||||
return `${uid}${attendeeId ? `${attendeeId}` : ""}@${APP_NAME}`;
|
||||
};
|
||||
|
||||
export default getICalUID;
|
||||
|
||||
@@ -1731,6 +1731,7 @@ async function handler(
|
||||
eventTrigger,
|
||||
responses,
|
||||
});
|
||||
|
||||
if (newBooking) {
|
||||
req.statusCode = 201;
|
||||
const bookingResponse = {
|
||||
@@ -1745,6 +1746,13 @@ async function handler(
|
||||
...bookingResponse,
|
||||
...luckyUserResponse,
|
||||
};
|
||||
} else {
|
||||
// Rescheduling logic for the original seated event was handled in handleSeats
|
||||
// We want to use new booking logic for the new time slot
|
||||
originalRescheduledBooking = null;
|
||||
evt.iCalUID = getICalUID({
|
||||
attendeeId: bookingSeat?.attendeeId,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isTeamEventType) {
|
||||
@@ -1754,7 +1762,6 @@ async function handler(
|
||||
id: eventType.team?.id ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (reqBody.recurringEventId && eventType.recurringEvent) {
|
||||
// Overriding the recurring event configuration count to be the actual number of events booked for
|
||||
// the recurring event (equal or less than recurring event configuration count)
|
||||
@@ -1876,7 +1883,7 @@ async function handler(
|
||||
let videoCallUrl;
|
||||
|
||||
//this is the actual rescheduling logic
|
||||
if (originalRescheduledBooking?.uid) {
|
||||
if (!eventType.seatsPerTimeSlot && originalRescheduledBooking?.uid) {
|
||||
log.silly("Rescheduling booking", originalRescheduledBooking.uid);
|
||||
try {
|
||||
// cancel workflow reminders from previous rescheduled booking
|
||||
|
||||
+30
-27
@@ -3,6 +3,7 @@ import { cloneDeep } from "lodash";
|
||||
|
||||
import type EventManager from "@calcom/core/EventManager";
|
||||
import { sendRescheduledSeatEmail } from "@calcom/emails";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { Person, CalendarEvent } from "@calcom/types/Calendar";
|
||||
|
||||
@@ -17,11 +18,28 @@ const attendeeRescheduleSeatedBooking = async (
|
||||
originalBookingEvt: CalendarEvent,
|
||||
eventManager: EventManager
|
||||
) => {
|
||||
const { tAttendees, bookingSeat, bookerEmail, rescheduleUid, evt } = rescheduleSeatedBookingObject;
|
||||
const { tAttendees, bookingSeat, bookerEmail, evt } = rescheduleSeatedBookingObject;
|
||||
let { originalRescheduledBooking } = rescheduleSeatedBookingObject;
|
||||
|
||||
seatAttendee["language"] = { translate: tAttendees, locale: bookingSeat?.attendee.locale ?? "en" };
|
||||
|
||||
// Update the original calendar event by removing the attendee that is rescheduling
|
||||
if (originalBookingEvt && originalRescheduledBooking) {
|
||||
// Event would probably be deleted so we first check than instead of updating references
|
||||
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
|
||||
return attendee.email !== bookerEmail;
|
||||
});
|
||||
const deletedReference = await lastAttendeeDeleteBooking(
|
||||
originalRescheduledBooking,
|
||||
filteredAttendees,
|
||||
originalBookingEvt
|
||||
);
|
||||
|
||||
if (!deletedReference) {
|
||||
await eventManager.updateCalendarAttendees(originalBookingEvt, originalRescheduledBooking);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no booking then remove the attendee from the old booking and create a new one
|
||||
if (!newTimeSlotBooking) {
|
||||
await prisma.attendee.delete({
|
||||
@@ -30,23 +48,6 @@ const attendeeRescheduleSeatedBooking = async (
|
||||
},
|
||||
});
|
||||
|
||||
// Update the original calendar event by removing the attendee that is rescheduling
|
||||
if (originalBookingEvt && originalRescheduledBooking) {
|
||||
// Event would probably be deleted so we first check than instead of updating references
|
||||
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
|
||||
return attendee.email !== bookerEmail;
|
||||
});
|
||||
const deletedReference = await lastAttendeeDeleteBooking(
|
||||
originalRescheduledBooking,
|
||||
filteredAttendees,
|
||||
originalBookingEvt
|
||||
);
|
||||
|
||||
if (!deletedReference) {
|
||||
await eventManager.updateCalendarAttendees(originalBookingEvt, originalRescheduledBooking);
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to trigger rescheduling logic of the original booking
|
||||
originalRescheduledBooking = null;
|
||||
|
||||
@@ -76,17 +77,19 @@ const attendeeRescheduleSeatedBooking = async (
|
||||
]);
|
||||
}
|
||||
|
||||
const copyEvent = cloneDeep(evt);
|
||||
// Add the new attendees to the new time slot booking attendees
|
||||
for (const attendee of newTimeSlotBooking.attendees) {
|
||||
const language = await getTranslation(attendee.locale ?? "en", "common");
|
||||
evt.attendees.push({
|
||||
email: attendee.email,
|
||||
name: attendee.name,
|
||||
language,
|
||||
});
|
||||
}
|
||||
|
||||
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newTimeSlotBooking.id);
|
||||
const copyEvent = cloneDeep({ ...evt, iCalUID: newTimeSlotBooking.iCalUID });
|
||||
|
||||
const results = updateManager.results;
|
||||
|
||||
const calendarResult = results.find((result) => result.type.includes("_calendar"));
|
||||
|
||||
evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
|
||||
? calendarResult?.updatedEvent[0]?.iCalUID
|
||||
: calendarResult?.updatedEvent?.iCalUID || undefined;
|
||||
await eventManager.updateCalendarAttendees(copyEvent, newTimeSlotBooking);
|
||||
|
||||
await sendRescheduledSeatEmail(copyEvent, seatAttendee as Person);
|
||||
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
|
||||
|
||||
@@ -39,11 +39,14 @@ const rescheduleSeatedBooking = async (
|
||||
select: {
|
||||
id: true,
|
||||
uid: true,
|
||||
iCalUID: true,
|
||||
userId: true,
|
||||
attendees: {
|
||||
include: {
|
||||
bookingSeat: true,
|
||||
},
|
||||
},
|
||||
references: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -82,6 +85,7 @@ const rescheduleSeatedBooking = async (
|
||||
startTime: dayjs(originalRescheduledBooking.startTime).utc().format(),
|
||||
endTime: dayjs(originalRescheduledBooking.endTime).utc().format(),
|
||||
attendees: updatedBookingAttendees,
|
||||
iCalUID: originalRescheduledBooking.iCalUID,
|
||||
// If the location is a video integration then include the videoCallData
|
||||
...(videoReference && {
|
||||
videoCallData: {
|
||||
|
||||
@@ -70,6 +70,9 @@ export type NewTimeSlotBooking = Prisma.BookingGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
uid: true;
|
||||
iCalUID: true;
|
||||
userId: true;
|
||||
references: true;
|
||||
attendees: {
|
||||
include: {
|
||||
bookingSeat: true;
|
||||
|
||||
Reference in New Issue
Block a user