fix: Stop sending Reminders when a 'Seat' cancels the Event (#10405)
Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
This commit is contained in:
co-authored by
CarinaWolli
Carina Wollendorfer
parent
a0fc045852
commit
17855251ff
@@ -17,6 +17,8 @@ import AttendeeLocationChangeEmail from "./templates/attendee-location-change-em
|
||||
import AttendeeRequestEmail from "./templates/attendee-request-email";
|
||||
import AttendeeRescheduledEmail from "./templates/attendee-rescheduled-email";
|
||||
import AttendeeScheduledEmail from "./templates/attendee-scheduled-email";
|
||||
import type { EmailVerifyCode } from "./templates/attendee-verify-email";
|
||||
import AttendeeVerifyEmail from "./templates/attendee-verify-email";
|
||||
import AttendeeWasRequestedToRescheduleEmail from "./templates/attendee-was-requested-to-reschedule-email";
|
||||
import BrokenIntegrationEmail from "./templates/broken-integration-email";
|
||||
import DisabledAppEmail from "./templates/disabled-app-email";
|
||||
@@ -42,7 +44,6 @@ import OrganizerScheduledEmail from "./templates/organizer-scheduled-email";
|
||||
import SlugReplacementEmail from "./templates/slug-replacement-email";
|
||||
import type { TeamInvite } from "./templates/team-invite-email";
|
||||
import TeamInviteEmail from "./templates/team-invite-email";
|
||||
import AttendeeVerifyEmail, { EmailVerifyCode } from "./templates/attendee-verify-email";
|
||||
|
||||
const sendEmail = (prepare: () => BaseEmail) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -24,7 +24,7 @@ export { BrokenIntegrationEmail } from "./BrokenIntegrationEmail";
|
||||
export { OrganizerAttendeeCancelledSeatEmail } from "./OrganizerAttendeeCancelledSeatEmail";
|
||||
export { NoShowFeeChargedEmail } from "./NoShowFeeChargedEmail";
|
||||
export { VerifyAccountEmail } from "./VerifyAccountEmail";
|
||||
export { VerifyEmailByCode } from "./VerifyEmailByCode"
|
||||
export { VerifyEmailByCode } from "./VerifyEmailByCode";
|
||||
export * from "@calcom/app-store/routing-forms/emails/components";
|
||||
export { DailyVideoDownloadRecordingEmail } from "./DailyVideoDownloadRecordingEmail";
|
||||
export { OrganisationAccountVerifyEmail } from "./OrganizationAccountVerifyEmail";
|
||||
|
||||
@@ -48,4 +48,4 @@ ${this.verifyAccountInput.language("happy_scheduling")} ${this.verifyAccountInpu
|
||||
)}
|
||||
`.replace(/(<([^>]+)>)/gi, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,6 +766,24 @@ async function handleSeatedEventCancellation(
|
||||
);
|
||||
await Promise.all(promises);
|
||||
|
||||
const workflowRemindersForAttendee = bookingToDelete?.workflowReminders.filter(
|
||||
(reminder) => reminder.seatReferenceId === seatReferenceUid
|
||||
);
|
||||
|
||||
if (workflowRemindersForAttendee && workflowRemindersForAttendee.length !== 0) {
|
||||
const deletionPromises = workflowRemindersForAttendee.map((reminder) => {
|
||||
if (reminder.method === WorkflowMethods.EMAIL) {
|
||||
return deleteScheduledEmailReminder(reminder.id, reminder.referenceId);
|
||||
} else if (reminder.method === WorkflowMethods.SMS) {
|
||||
return deleteScheduledSMSReminder(reminder.id, reminder.referenceId);
|
||||
} else if (reminder.method === WorkflowMethods.WHATSAPP) {
|
||||
return deleteScheduledWhatsappReminder(reminder.id, reminder.referenceId);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.allSettled(deletionPromises);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
|
||||
@@ -1664,6 +1664,7 @@ async function handler(
|
||||
isRescheduleEvent: !!rescheduleUid,
|
||||
isFirstRecurringEvent: true,
|
||||
emailAttendeeSendToOverride: bookerEmail,
|
||||
seatReferenceUid: evt.attendeeSeatId,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error("Error while scheduling workflow reminders", error);
|
||||
@@ -2306,6 +2307,7 @@ async function handler(
|
||||
isRescheduleEvent: !!rescheduleUid,
|
||||
isFirstRecurringEvent: true,
|
||||
hideBranding: !!eventType.owner?.hideBranding,
|
||||
seatReferenceUid: evt.attendeeSeatId,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error("Error while scheduling workflow reminders", error);
|
||||
|
||||
@@ -40,7 +40,8 @@ export const scheduleEmailReminder = async (
|
||||
workflowStepId: number,
|
||||
template: WorkflowTemplates,
|
||||
sender: string,
|
||||
hideBranding?: boolean
|
||||
hideBranding?: boolean,
|
||||
seatReferenceUid?: string
|
||||
) => {
|
||||
if (action === WorkflowActions.EMAIL_ADDRESS) return;
|
||||
const { startTime, endTime } = evt;
|
||||
@@ -221,6 +222,7 @@ export const scheduleEmailReminder = async (
|
||||
scheduledDate: scheduledDate.toDate(),
|
||||
scheduled: true,
|
||||
referenceId: batchIdResponse[1].batch_id,
|
||||
seatReferenceId: seatReferenceUid,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -235,6 +237,7 @@ export const scheduleEmailReminder = async (
|
||||
method: WorkflowMethods.EMAIL,
|
||||
scheduledDate: scheduledDate.toDate(),
|
||||
scheduled: false,
|
||||
seatReferenceId: seatReferenceUid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ type ProcessWorkflowStepParams = {
|
||||
calendarEvent: ExtendedCalendarEvent;
|
||||
emailAttendeeSendToOverride?: string;
|
||||
hideBranding?: boolean;
|
||||
seatReferenceUid?: string;
|
||||
};
|
||||
|
||||
export interface ScheduleWorkflowRemindersArgs extends ProcessWorkflowStepParams {
|
||||
@@ -41,6 +42,7 @@ const processWorkflowStep = async (
|
||||
calendarEvent: evt,
|
||||
emailAttendeeSendToOverride,
|
||||
hideBranding,
|
||||
seatReferenceUid,
|
||||
}: ProcessWorkflowStepParams
|
||||
) => {
|
||||
if (step.action === WorkflowActions.SMS_ATTENDEE || step.action === WorkflowActions.SMS_NUMBER) {
|
||||
@@ -60,7 +62,8 @@ const processWorkflowStep = async (
|
||||
step.sender || SENDER_ID,
|
||||
workflow.userId,
|
||||
workflow.teamId,
|
||||
step.numberVerificationPending
|
||||
step.numberVerificationPending,
|
||||
seatReferenceUid
|
||||
);
|
||||
} else if (step.action === WorkflowActions.EMAIL_ATTENDEE || step.action === WorkflowActions.EMAIL_HOST) {
|
||||
let sendTo: string[] = [];
|
||||
@@ -93,7 +96,8 @@ const processWorkflowStep = async (
|
||||
step.id,
|
||||
step.template,
|
||||
step.sender || SENDER_NAME,
|
||||
hideBranding
|
||||
hideBranding,
|
||||
seatReferenceUid
|
||||
);
|
||||
} else if (isWhatsappAction(step.action)) {
|
||||
const sendTo = step.action === WorkflowActions.WHATSAPP_ATTENDEE ? smsReminderNumber : step.sendTo;
|
||||
@@ -111,7 +115,8 @@ const processWorkflowStep = async (
|
||||
step.template,
|
||||
workflow.userId,
|
||||
workflow.teamId,
|
||||
step.numberVerificationPending
|
||||
step.numberVerificationPending,
|
||||
seatReferenceUid
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -126,7 +131,9 @@ export const scheduleWorkflowReminders = async (args: ScheduleWorkflowRemindersA
|
||||
isFirstRecurringEvent = false,
|
||||
emailAttendeeSendToOverride = "",
|
||||
hideBranding,
|
||||
seatReferenceUid,
|
||||
} = args;
|
||||
|
||||
if (requiresConfirmation || !workflows.length) return;
|
||||
|
||||
for (const workflowReference of workflows) {
|
||||
@@ -157,6 +164,7 @@ export const scheduleWorkflowReminders = async (args: ScheduleWorkflowRemindersA
|
||||
emailAttendeeSendToOverride,
|
||||
smsReminderNumber,
|
||||
hideBranding,
|
||||
seatReferenceUid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,8 @@ export const scheduleSMSReminder = async (
|
||||
sender: string,
|
||||
userId?: number | null,
|
||||
teamId?: number | null,
|
||||
isVerificationPending = false
|
||||
isVerificationPending = false,
|
||||
seatReferenceUid?: string
|
||||
) => {
|
||||
const { startTime, endTime } = evt;
|
||||
const uid = evt.uid as string;
|
||||
@@ -174,6 +175,7 @@ export const scheduleSMSReminder = async (
|
||||
scheduledDate: scheduledDate.toDate(),
|
||||
scheduled: true,
|
||||
referenceId: scheduledSMS.sid,
|
||||
seatReferenceId: seatReferenceUid,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -188,6 +190,7 @@ export const scheduleSMSReminder = async (
|
||||
method: WorkflowMethods.SMS,
|
||||
scheduledDate: scheduledDate.toDate(),
|
||||
scheduled: false,
|
||||
seatReferenceId: seatReferenceUid,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -200,6 +203,7 @@ export const deleteScheduledSMSReminder = async (reminderId: number, referenceId
|
||||
if (referenceId) {
|
||||
await twilio.cancelSMS(referenceId);
|
||||
}
|
||||
|
||||
await prisma.workflowReminder.delete({
|
||||
where: {
|
||||
id: reminderId,
|
||||
|
||||
@@ -36,7 +36,8 @@ export const scheduleWhatsappReminder = async (
|
||||
template: WorkflowTemplates,
|
||||
userId?: number | null,
|
||||
teamId?: number | null,
|
||||
isVerificationPending = false
|
||||
isVerificationPending = false,
|
||||
seatReferenceUid?: string
|
||||
) => {
|
||||
const { startTime, endTime } = evt;
|
||||
const uid = evt.uid as string;
|
||||
@@ -184,6 +185,7 @@ export const scheduleWhatsappReminder = async (
|
||||
scheduledDate: scheduledDate.toDate(),
|
||||
scheduled: true,
|
||||
referenceId: scheduledWHATSAPP.sid,
|
||||
seatReferenceId: seatReferenceUid,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -198,6 +200,7 @@ export const scheduleWhatsappReminder = async (
|
||||
method: WorkflowMethods.WHATSAPP,
|
||||
scheduledDate: scheduledDate.toDate(),
|
||||
scheduled: false,
|
||||
seatReferenceId: seatReferenceUid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "WorkflowReminder" ADD COLUMN "seatReferenceId" TEXT;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "WorkflowReminder_seatReferenceId_idx" ON "WorkflowReminder"("seatReferenceId");
|
||||
@@ -782,19 +782,21 @@ enum TimeUnit {
|
||||
}
|
||||
|
||||
model WorkflowReminder {
|
||||
id Int @id @default(autoincrement())
|
||||
bookingUid String?
|
||||
booking Booking? @relation(fields: [bookingUid], references: [uid])
|
||||
method WorkflowMethods
|
||||
scheduledDate DateTime
|
||||
referenceId String? @unique
|
||||
scheduled Boolean
|
||||
workflowStepId Int?
|
||||
workflowStep WorkflowStep? @relation(fields: [workflowStepId], references: [id])
|
||||
cancelled Boolean?
|
||||
id Int @id @default(autoincrement())
|
||||
bookingUid String?
|
||||
booking Booking? @relation(fields: [bookingUid], references: [uid])
|
||||
method WorkflowMethods
|
||||
scheduledDate DateTime
|
||||
referenceId String? @unique
|
||||
scheduled Boolean
|
||||
workflowStepId Int?
|
||||
workflowStep WorkflowStep? @relation(fields: [workflowStepId], references: [id])
|
||||
cancelled Boolean?
|
||||
seatReferenceId String?
|
||||
|
||||
@@index([bookingUid])
|
||||
@@index([workflowStepId])
|
||||
@@index([seatReferenceId])
|
||||
}
|
||||
|
||||
enum WorkflowTemplates {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { sendEmailVerificationByCode } from "@calcom/features/auth/lib/verifyEmail";
|
||||
import logger from "@calcom/lib/logger";
|
||||
|
||||
import { TSendVerifyEmailCodeSchema } from "./sendVerifyEmailCode.schema"
|
||||
import type { TSendVerifyEmailCodeSchema } from "./sendVerifyEmailCode.schema";
|
||||
|
||||
type SendVerifyEmailCode = {
|
||||
input: TSendVerifyEmailCodeSchema;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { z } from "zod";
|
||||
export const ZSendVerifyEmailCodeSchema = z.object({
|
||||
email: z.string().min(1),
|
||||
username: z.string().optional(),
|
||||
language: z.string().optional()
|
||||
language: z.string().optional(),
|
||||
});
|
||||
|
||||
export type TSendVerifyEmailCodeSchema = z.infer<typeof ZSendVerifyEmailCodeSchema>;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createHash } from "crypto";
|
||||
import { totp } from "otplib";
|
||||
|
||||
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
||||
import type { ZVerifyCodeInputSchema } from "@calcom/prisma/zod-utils";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user