diff --git a/packages/features/bookings/lib/handleSeats/handleSeats.ts b/packages/features/bookings/lib/handleSeats/handleSeats.ts index ac64a72330..9473bf93fe 100644 --- a/packages/features/bookings/lib/handleSeats/handleSeats.ts +++ b/packages/features/bookings/lib/handleSeats/handleSeats.ts @@ -107,12 +107,14 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => { ...(typeof resultBooking.metadata === "object" && resultBooking.metadata), ...reqBodyMetadata, }; + // For seated events, use the phone number from the specific attendee being added + const attendeePhoneNumber = invitee[0]?.phoneNumber || smsReminderNumber || null; try { const creditService = new CreditService(); await WorkflowService.scheduleWorkflowsForNewBooking({ workflows: workflows, - smsReminderNumber: smsReminderNumber || null, + smsReminderNumber: attendeePhoneNumber, calendarEvent: { ...evt, uid: seatedBooking.uid, @@ -154,7 +156,7 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => { metadata, eventTypeId, status: "ACCEPTED", - smsReminderNumber: seatedBooking?.smsReminderNumber || undefined, + smsReminderNumber: attendeePhoneNumber || undefined, rescheduledBy, }; diff --git a/packages/features/bookings/repositories/BookingSeatRepository.ts b/packages/features/bookings/repositories/BookingSeatRepository.ts index ace67b38d8..479810e1ba 100644 --- a/packages/features/bookings/repositories/BookingSeatRepository.ts +++ b/packages/features/bookings/repositories/BookingSeatRepository.ts @@ -17,4 +17,26 @@ export class BookingSeatRepository { }, }); } + + getByReferenceUidWithAttendeeDetails(referenceUid: string) { + return this.prismaClient.bookingSeat.findUnique({ + where: { + referenceUid, + }, + select: { + attendee: { + select: { + name: true, + id: true, + bookingId: true, + noShow: true, + phoneNumber: true, + email: true, + locale: true, + timeZone: true, + }, + }, + }, + }); + } } diff --git a/packages/features/ee/workflows/api/scheduleSMSReminders.ts b/packages/features/ee/workflows/api/scheduleSMSReminders.ts index 6958a9c17a..f8ddb99c08 100644 --- a/packages/features/ee/workflows/api/scheduleSMSReminders.ts +++ b/packages/features/ee/workflows/api/scheduleSMSReminders.ts @@ -5,6 +5,7 @@ import { NextResponse } from "next/server"; import dayjs from "@calcom/dayjs"; import { bulkShortenLinks } from "@calcom/ee/workflows/lib/reminders/utils"; import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses"; +import { BookingSeatRepository } from "@calcom/features/bookings/repositories/BookingSeatRepository"; import { CreditService } from "@calcom/features/ee/billing/credit-service"; import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer"; import { isAttendeeAction } from "@calcom/features/ee/workflows/lib/actionHelperFunctions"; @@ -61,24 +62,34 @@ export async function handler(req: NextRequest) { const teamId = reminder.workflowStep.workflow.teamId; try { + // For seated events, get the correct attendee based on seatReferenceId + let targetAttendee = reminder.booking?.attendees[0]; + if (reminder.seatReferenceId) { + const bookingSeatRepository = new BookingSeatRepository(prisma); + const seatAttendeeData = await bookingSeatRepository.getByReferenceUidWithAttendeeDetails( + reminder.seatReferenceId + ); + if (seatAttendeeData?.attendee) { + targetAttendee = seatAttendeeData.attendee; + } + } + const sendTo = reminder.workflowStep.action === WorkflowActions.SMS_NUMBER ? reminder.workflowStep.sendTo - : reminder.booking?.smsReminderNumber; + : targetAttendee?.phoneNumber; const userName = - reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE - ? reminder.booking?.attendees[0].name - : ""; + reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE ? targetAttendee?.name || "" : ""; const attendeeName = reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE ? reminder.booking?.user?.name - : reminder.booking?.attendees[0].name; + : targetAttendee?.name; const timeZone = reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE - ? reminder.booking?.attendees[0].timeZone + ? targetAttendee?.timeZone : reminder.booking?.user?.timeZone; const senderID = getSenderId(sendTo, reminder.workflowStep.sender); @@ -86,7 +97,7 @@ export async function handler(req: NextRequest) { const locale = reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE || reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE - ? reminder.booking?.attendees[0].locale + ? targetAttendee?.locale : reminder.booking?.user?.locale; let message: string | null = reminder.workflowStep.reminderBody || null; @@ -111,7 +122,7 @@ export async function handler(req: NextRequest) { const recipientEmail = getWorkflowRecipientEmail({ action: reminder.workflowStep.action || WorkflowActions.SMS_NUMBER, - attendeeEmail: reminder.booking.attendees[0].email, + attendeeEmail: targetAttendee?.email, organizerEmail: reminder.booking.user?.email, }); @@ -131,8 +142,8 @@ export async function handler(req: NextRequest) { const variables: VariablesType = { eventName: reminder.booking?.eventType?.title, organizerName: reminder.booking?.user?.name || "", - attendeeName: reminder.booking?.attendees[0].name, - attendeeEmail: reminder.booking?.attendees[0].email, + attendeeName: targetAttendee?.name, + attendeeEmail: targetAttendee?.email, eventDate: dayjs(reminder.booking?.startTime).tz(timeZone), eventEndTime: dayjs(reminder.booking?.endTime).tz(timeZone), timeZone: timeZone, @@ -142,13 +153,9 @@ export async function handler(req: NextRequest) { meetingUrl, cancelLink, rescheduleLink, - attendeeTimezone: reminder.booking.attendees[0].timeZone, - eventTimeInAttendeeTimezone: dayjs(reminder.booking.startTime).tz( - reminder.booking.attendees[0].timeZone - ), - eventEndTimeInAttendeeTimezone: dayjs(reminder.booking?.endTime).tz( - reminder.booking.attendees[0].timeZone - ), + attendeeTimezone: targetAttendee?.timeZone, + eventTimeInAttendeeTimezone: dayjs(reminder.booking.startTime).tz(targetAttendee?.timeZone), + eventEndTimeInAttendeeTimezone: dayjs(reminder.booking?.endTime).tz(targetAttendee?.timeZone), }; const customMessage = customTemplate( reminder.workflowStep.reminderBody || "", @@ -190,7 +197,7 @@ export async function handler(req: NextRequest) { fallbackData: reminder.workflowStep.action && isAttendeeAction(reminder.workflowStep.action) ? { - email: reminder.booking.attendees[0].email, + email: targetAttendee?.email, t: await getTranslation(locale || "en", "common"), replyTo: reminder.booking?.user?.email ?? "", workflowStepId: reminder.workflowStep.id, diff --git a/packages/features/ee/workflows/api/scheduleWhatsappReminders.ts b/packages/features/ee/workflows/api/scheduleWhatsappReminders.ts index 6570a51811..25b59d3d83 100644 --- a/packages/features/ee/workflows/api/scheduleWhatsappReminders.ts +++ b/packages/features/ee/workflows/api/scheduleWhatsappReminders.ts @@ -3,6 +3,7 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import dayjs from "@calcom/dayjs"; +import { BookingSeatRepository } from "@calcom/features/bookings/repositories/BookingSeatRepository"; import { CreditService } from "@calcom/features/ee/billing/credit-service"; import { getTranslation } from "@calcom/lib/server/i18n"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; @@ -53,24 +54,34 @@ export async function handler(req: NextRequest) { const teamId = reminder.workflowStep.workflow.teamId; try { + // For seated events, get the correct attendee based on seatReferenceId + let targetAttendee = reminder.booking?.attendees[0]; + if (reminder.seatReferenceId) { + const bookingSeatRepository = new BookingSeatRepository(prisma); + const seatAttendeeData = await bookingSeatRepository.getByReferenceUidWithAttendeeDetails( + reminder.seatReferenceId + ); + if (seatAttendeeData?.attendee) { + targetAttendee = seatAttendeeData.attendee; + } + } + const sendTo = reminder.workflowStep.action === WorkflowActions.WHATSAPP_NUMBER ? reminder.workflowStep.sendTo - : reminder.booking?.smsReminderNumber; + : targetAttendee?.phoneNumber; const userName = - reminder.workflowStep.action === WorkflowActions.WHATSAPP_ATTENDEE - ? reminder.booking?.attendees[0].name - : ""; + reminder.workflowStep.action === WorkflowActions.WHATSAPP_ATTENDEE ? targetAttendee?.name || "" : ""; const attendeeName = reminder.workflowStep.action === WorkflowActions.WHATSAPP_ATTENDEE ? reminder.booking?.user?.name - : reminder.booking?.attendees[0].name; + : targetAttendee?.name; const timeZone = reminder.workflowStep.action === WorkflowActions.WHATSAPP_ATTENDEE - ? reminder.booking?.attendees[0].timeZone + ? targetAttendee?.timeZone : reminder.booking?.user?.timeZone; const startTime = reminder.booking?.startTime.toISOString(); @@ -121,8 +132,8 @@ export async function handler(req: NextRequest) { fallbackData: reminder.workflowStep.action && isAttendeeAction(reminder.workflowStep.action) ? { - email: reminder.booking.attendees[0].email, - t: await getTranslation(reminder.booking.attendees[0].locale || "en", "common"), + email: targetAttendee?.email, + t: await getTranslation(targetAttendee?.locale || "en", "common"), replyTo: reminder.booking?.user?.email ?? "", workflowStepId: reminder.workflowStep.id, } diff --git a/packages/features/ee/workflows/lib/getWorkflowReminders.ts b/packages/features/ee/workflows/lib/getWorkflowReminders.ts index f72d24ae2b..99ca56db09 100644 --- a/packages/features/ee/workflows/lib/getWorkflowReminders.ts +++ b/packages/features/ee/workflows/lib/getWorkflowReminders.ts @@ -49,7 +49,7 @@ type PartialBooking = export type PartialWorkflowReminder = Pick< WorkflowReminder, - "id" | "isMandatoryReminder" | "scheduledDate" | "uuid" + "id" | "isMandatoryReminder" | "scheduledDate" | "uuid" | "seatReferenceId" > & { booking: PartialBooking | null; } & { workflowStep: PartialWorkflowStep }; @@ -134,6 +134,7 @@ export const select = { scheduledDate: true, isMandatoryReminder: true, uuid: true, + seatReferenceId: true, workflowStep: { select: { action: true, diff --git a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts index e38c8624ce..eb6ed7c93e 100644 --- a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts +++ b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts @@ -63,6 +63,30 @@ export type ScheduleWorkflowRemindersArgs = ProcessWorkflowStepParams & { creditCheckFn: CreditCheckFn; }; +const getReminderPhoneNumber = async ( + action: WorkflowActions, + seatReferenceUid: string | undefined, + smsReminderNumber: string | null, + stepSendTo: string | null +) => { + const isAttendeeAction = + action === WorkflowActions.SMS_ATTENDEE || action === WorkflowActions.WHATSAPP_ATTENDEE; + + if (!isAttendeeAction) { + return stepSendTo; + } + + if (seatReferenceUid) { + const bookingSeatRepository = new BookingSeatRepository(prisma); + const seatAttendeeData = await bookingSeatRepository.getByReferenceUidWithAttendeeDetails( + seatReferenceUid + ); + return seatAttendeeData?.attendee?.phoneNumber || smsReminderNumber; + } + + return smsReminderNumber; +}; + const processWorkflowStep = async ( workflow: Workflow, step: WorkflowStep, @@ -101,7 +125,12 @@ const processWorkflowStep = async ( if (isSMSAction(step.action)) { const { scheduleSMSReminder } = await import("./smsReminderManager"); - const sendTo = step.action === WorkflowActions.SMS_ATTENDEE ? smsReminderNumber : step.sendTo; + const sendTo = await getReminderPhoneNumber( + step.action, + seatReferenceUid, + smsReminderNumber, + step.sendTo + ); await scheduleSMSReminder({ ...scheduleFunctionParams, @@ -139,7 +168,12 @@ const processWorkflowStep = async ( } const { scheduleWhatsappReminder } = await import("./whatsappReminderManager"); - const sendTo = step.action === WorkflowActions.WHATSAPP_ATTENDEE ? smsReminderNumber : step.sendTo; + const sendTo = await getReminderPhoneNumber( + step.action, + seatReferenceUid, + smsReminderNumber, + step.sendTo + ); await scheduleWhatsappReminder({ ...scheduleFunctionParams,