Files
calendar/packages/features/tasker/tasks/index.ts
T
sean-brydonGitHubunknown <>Claude Opus 4.5Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
52f3ddaf74 feat: add tasker integration for proration email notifications (#27247)
* 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>
2026-02-03 08:29:36 +00:00

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;