Files
calendar/packages/features/tasker/tasks/sendProrationInvoiceEmail.ts
T
sean-brydonGitHubClaude 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

35 lines
1.1 KiB
TypeScript

import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { z } from "zod";
const log = logger.getSubLogger({ prefix: ["sendProrationInvoiceEmail"] });
export const sendProrationInvoiceEmailPayloadSchema = z.object({
prorationId: z.string(),
teamId: z.number(),
isAutoCharge: z.boolean(),
});
export async function sendProrationInvoiceEmail(payload: string): Promise<void> {
try {
const { prorationId, teamId, isAutoCharge } = sendProrationInvoiceEmailPayloadSchema.parse(
JSON.parse(payload)
);
log.debug(`Processing sendProrationInvoiceEmail task for prorationId ${prorationId}, teamId ${teamId}`);
const { ProrationEmailService } = await import(
"@calcom/features/ee/billing/service/proration/ProrationEmailService"
);
const emailService = new ProrationEmailService();
await emailService.sendInvoiceEmail({ prorationId, teamId, isAutoCharge });
} catch (error) {
log.error(
`Failed to send proration invoice email`,
safeStringify({ payload, error: error instanceof Error ? error.message : String(error) })
);
throw error;
}
}