* 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>
35 lines
1.1 KiB
TypeScript
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;
|
|
}
|
|
}
|