* feat: add proration invoice and reminder email templates Add email templates for monthly proration billing notifications: - ProrationInvoiceEmail: Sent when invoice is created for additional seats - ProrationReminderEmail: Sent 7 days later if invoice remains unpaid Includes: - React email templates using V2BaseEmailHtml - BaseEmail classes for rendering - Billing email service functions - Translation keys for email content Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add tasker integration for proration email notifications Add task handlers for proration billing email flow: - sendProrationInvoiceEmail: Sends invoice email and schedules reminder - sendProrationReminderEmail: Sends reminder if invoice still unpaid - cancelProrationReminder: Cancels scheduled reminder on payment success The calling job should trigger these tasks after: 1. MonthlyProrationService.createProrationForTeam() succeeds with invoice 2. handleProrationPaymentSuccess() is called (to cancel reminder) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: i18n + feedback from cubic + morgan * fix: use default tasker import instead of non-existent getTasker Co-Authored-By: unknown <> * fix: add trigger tasks plus DI * use for await * fix query * fix: remove duplicate JOIN alias in findTeamMembersWithPermission query The raw SQL query had two INNER JOINs using the same alias 'u': - INNER JOIN "users" u ON m."userId" = u.id - INNER JOIN "User" u ON m."userId" = u.id This would cause a SQL error at runtime. Removed the duplicate JOIN with incorrect table name ("User" instead of "users"). Fixes issue identified by Cubic AI review. Co-Authored-By: unknown <> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
59 lines
2.9 KiB
TypeScript
59 lines
2.9 KiB
TypeScript
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
|
import type { TaskHandler, TaskTypes } from "../tasker";
|
|
|
|
/**
|
|
* This is a map of all the tasks that the Tasker can handle.
|
|
* The keys are the TaskTypes and the values are the task handlers.
|
|
* The task handlers are imported dynamically to avoid circular dependencies.
|
|
*/
|
|
const tasks: Record<TaskTypes, () => Promise<TaskHandler>> = {
|
|
sendWebhook: () => import("./sendWebook").then((module) => module.sendWebhook),
|
|
triggerHostNoShowWebhook: () =>
|
|
import("./triggerNoShow/triggerHostNoShow").then((module) => module.triggerHostNoShow),
|
|
triggerGuestNoShowWebhook: () =>
|
|
import("./triggerNoShow/triggerGuestNoShow").then((module) => module.triggerGuestNoShow),
|
|
triggerFormSubmittedNoEventWebhook: () =>
|
|
import("./triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook").then(
|
|
(module) => module.triggerFormSubmittedNoEventWebhook
|
|
),
|
|
triggerFormSubmittedNoEventWorkflow: () =>
|
|
import("./triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWorkflow").then(
|
|
(module) => module.triggerFormSubmittedNoEventWorkflow
|
|
),
|
|
sendSms: () => Promise.resolve(() => Promise.reject(new Error("Not implemented"))),
|
|
translateEventTypeData: () =>
|
|
import("./translateEventTypeData").then((module) => module.translateEventTypeData),
|
|
createCRMEvent: () => import("./crm/createCRMEvent").then((module) => module.createCRMEvent),
|
|
sendWorkflowEmails: () => import("./sendWorkflowEmails").then((module) => module.sendWorkflowEmails),
|
|
scanWorkflowBody: () => import("./scanWorkflowBody").then((module) => module.scanWorkflowBody),
|
|
scanWorkflowUrls: () => import("./scanWorkflowUrls").then((module) => module.scanWorkflowUrls),
|
|
sendAnalyticsEvent: () =>
|
|
import("./analytics/sendAnalyticsEvent").then((module) => module.sendAnalyticsEvent),
|
|
executeAIPhoneCall: () => import("./executeAIPhoneCall").then((module) => module.executeAIPhoneCall),
|
|
sendAwaitingPaymentEmail: () =>
|
|
import("./sendAwaitingPaymentEmail").then((module) => module.sendAwaitingPaymentEmail),
|
|
sendProrationInvoiceEmail: () =>
|
|
import("./sendProrationInvoiceEmail").then((module) => module.sendProrationInvoiceEmail),
|
|
sendProrationReminderEmail: () =>
|
|
import("./sendProrationReminderEmail").then((module) => module.sendProrationReminderEmail),
|
|
cancelProrationReminder: () =>
|
|
import("./cancelProrationReminder").then((module) => module.cancelProrationReminder),
|
|
bookingAudit: () => import("./bookingAudit").then((module) => module.bookingAudit),
|
|
webhookDelivery: () => import("./webhookDelivery").then((module) => module.webhookDelivery),
|
|
};
|
|
|
|
export const tasksConfig = {
|
|
createCRMEvent: {
|
|
minRetryIntervalMins: IS_PRODUCTION ? 10 : 1,
|
|
maxAttempts: 10,
|
|
},
|
|
executeAIPhoneCall: {
|
|
maxAttempts: 1,
|
|
},
|
|
webhookDelivery: {
|
|
minRetryIntervalMins: IS_PRODUCTION ? 5 : 1,
|
|
maxAttempts: 3,
|
|
},
|
|
};
|
|
export default tasks;
|