feat: agregar soporte para plantillas de correo electrónico y contexto en el envío de notificaciones
This commit is contained in:
Generated
+26
@@ -20,6 +20,7 @@
|
|||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/mongoose": "^5.11.96",
|
"@types/mongoose": "^5.11.96",
|
||||||
"@types/node": "^24.3.3",
|
"@types/node": "^24.3.3",
|
||||||
|
"cross-env": "^10.1.0",
|
||||||
"jest": "^30.4.2",
|
"jest": "^30.4.2",
|
||||||
"ts-jest": "^29.4.11",
|
"ts-jest": "^29.4.11",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
@@ -600,6 +601,13 @@
|
|||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@epic-web/invariant": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
@@ -2322,6 +2330,24 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-env": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@epic-web/invariant": "^1.0.0",
|
||||||
|
"cross-spawn": "^7.0.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"cross-env": "dist/bin/cross-env.js",
|
||||||
|
"cross-env-shell": "dist/bin/cross-env-shell.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "NODE_NO_WARNINGS=1 DOTENV_DISABLE_TELEMETRY=1 node --loader ts-node/esm src/index.ts",
|
"dev": "cross-env NODE_NO_WARNINGS=1 DOTENV_DISABLE_TELEMETRY=1 node --loader ts-node/esm src/index.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"test": "jest --config jest.config.cjs"
|
"test": "jest --config jest.config.cjs"
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/mongoose": "^5.11.96",
|
"@types/mongoose": "^5.11.96",
|
||||||
"@types/node": "^24.3.3",
|
"@types/node": "^24.3.3",
|
||||||
|
"cross-env": "^10.1.0",
|
||||||
"jest": "^30.4.2",
|
"jest": "^30.4.2",
|
||||||
"ts-jest": "^29.4.11",
|
"ts-jest": "^29.4.11",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
|||||||
@@ -91,12 +91,17 @@ export async function dispatchEmail(params: {
|
|||||||
systemToken: string;
|
systemToken: string;
|
||||||
type: string;
|
type: string;
|
||||||
email: string;
|
email: string;
|
||||||
subject: string;
|
subject?: string;
|
||||||
message: string;
|
message?: string;
|
||||||
|
templateId?: string;
|
||||||
|
context?: Record<string, unknown>;
|
||||||
}): Promise<DispatchResult> {
|
}): Promise<DispatchResult> {
|
||||||
try {
|
try {
|
||||||
if (!params.email.trim() || !params.subject.trim() || !params.message.trim()) {
|
const hasContent = Boolean(params.subject?.trim() && params.message?.trim());
|
||||||
return { success: false, error: "Email dispatch requires email, subject, and message" };
|
const hasTemplate = Boolean(params.templateId?.trim() && params.context);
|
||||||
|
|
||||||
|
if (!params.email.trim() || (!hasContent && !hasTemplate)) {
|
||||||
|
return { success: false, error: "Email dispatch requires email and either subject/message or templateId/context" };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send worker-rendered content directly so cancellation jobs survive appointment deletion.
|
// Send worker-rendered content directly so cancellation jobs survive appointment deletion.
|
||||||
@@ -106,8 +111,10 @@ export async function dispatchEmail(params: {
|
|||||||
{
|
{
|
||||||
systemToken: params.systemToken,
|
systemToken: params.systemToken,
|
||||||
email: params.email,
|
email: params.email,
|
||||||
subject: params.subject,
|
...(params.subject ? { subject: params.subject } : {}),
|
||||||
message: params.message,
|
...(params.message ? { message: params.message } : {}),
|
||||||
|
...(params.templateId ? { templateId: params.templateId } : {}),
|
||||||
|
...(params.context ? { context: params.context } : {}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ export class JobProcessor {
|
|||||||
email: content.email || "",
|
email: content.email || "",
|
||||||
subject: content.subject,
|
subject: content.subject,
|
||||||
message: content.message,
|
message: content.message,
|
||||||
|
templateId: content.emailTemplateId,
|
||||||
|
context: content.emailContext,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "system":
|
case "system":
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ export interface ResolvedNotificationContent {
|
|||||||
companyOwnerId?: string;
|
companyOwnerId?: string;
|
||||||
subject: string;
|
subject: string;
|
||||||
message: string;
|
message: string;
|
||||||
|
emailTemplateId?: string;
|
||||||
|
emailContext?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const appointmentSchema = new Schema(
|
const appointmentSchema = new Schema(
|
||||||
@@ -169,6 +171,8 @@ export async function resolveNotificationContent(job: INotificationJobDocument):
|
|||||||
companyOwnerId: companyOwnerId ? String(companyOwnerId) : undefined,
|
companyOwnerId: companyOwnerId ? String(companyOwnerId) : undefined,
|
||||||
subject: fallbackSubject.trim(),
|
subject: fallbackSubject.trim(),
|
||||||
message: fallbackMessage.trim(),
|
message: fallbackMessage.trim(),
|
||||||
|
emailTemplateId: job.payload.emailTemplateId?.trim(),
|
||||||
|
emailContext: job.payload.emailContext,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export interface NotificationJobPayload {
|
|||||||
message?: string;
|
message?: string;
|
||||||
emailSubject?: string;
|
emailSubject?: string;
|
||||||
emailMessage?: string;
|
emailMessage?: string;
|
||||||
|
emailTemplateId?: string;
|
||||||
|
emailContext?: Record<string, unknown>;
|
||||||
wapMessage?: string;
|
wapMessage?: string;
|
||||||
systemSubject?: string;
|
systemSubject?: string;
|
||||||
systemMessage?: string;
|
systemMessage?: string;
|
||||||
|
|||||||
@@ -43,6 +43,30 @@ describe("ChannelDispatchers", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sends email template data when provided", async () => {
|
||||||
|
await dispatchEmail({
|
||||||
|
appointmentId: "appointment-1",
|
||||||
|
systemToken: "system-token",
|
||||||
|
type: "reminder",
|
||||||
|
email: "client@example.com",
|
||||||
|
templateId: "template-1",
|
||||||
|
context: { username: "Client Name" },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockedAxios.post).toHaveBeenCalledWith(
|
||||||
|
"https://api.example.com/notifications/send-email",
|
||||||
|
{
|
||||||
|
systemToken: "system-token",
|
||||||
|
email: "client@example.com",
|
||||||
|
templateId: "template-1",
|
||||||
|
context: { username: "Client Name" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("sends the job type in the WhatsApp system notification payload", async () => {
|
it("sends the job type in the WhatsApp system notification payload", async () => {
|
||||||
await dispatchWhatsApp({
|
await dispatchWhatsApp({
|
||||||
companyId: "company-1",
|
companyId: "company-1",
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ngrok config add-authtoken 3I94dMHXyh3wjRjGBu0oGQ8D8h4_2yBusck6ZWHJBx7WcbBFf
|
||||||
|
|
||||||
|
ngrok http 3000
|
||||||
+1
-1
@@ -45,7 +45,7 @@ MP_SUBSCRIPTION_FIND_URL = https://api.mercadopago.com/preapproval/
|
|||||||
MP_ACCESS_TOKEN = APP_USR-8753424836448507-121412-731599daea1ffa0b278b5dcc78aa1ec8-1611134648
|
MP_ACCESS_TOKEN = APP_USR-8753424836448507-121412-731599daea1ffa0b278b5dcc78aa1ec8-1611134648
|
||||||
MP_BACK_URL = https://turnosxpress.com.ar/landing/dashboard
|
MP_BACK_URL = https://turnosxpress.com.ar/landing/dashboard
|
||||||
MP_CHECK_PAYMENT_MINUTES = 1
|
MP_CHECK_PAYMENT_MINUTES = 1
|
||||||
MP_WEBHOOK_URL = https://9300-186-132-200-176.ngrok-free.app/mercadopago/webhook
|
MP_WEBHOOK_URL = https://30e1-186-132-147-13.ngrok-free.app/mercadopago/webhook
|
||||||
|
|
||||||
DEFAULT_BOT_ADMIN_PORT = 3005
|
DEFAULT_BOT_ADMIN_PORT = 3005
|
||||||
|
|
||||||
|
|||||||
@@ -204,6 +204,8 @@ export interface AppointmentNotificationIntent {
|
|||||||
message: string;
|
message: string;
|
||||||
checkClient: IClientDocument;
|
checkClient: IClientDocument;
|
||||||
companyCheck: ICompanyDocument;
|
companyCheck: ICompanyDocument;
|
||||||
|
emailTemplateId?: string;
|
||||||
|
emailContext?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAppointment {
|
export interface IAppointment {
|
||||||
|
|||||||
@@ -287,6 +287,23 @@ class AppointmentManager implements IAppointmentsManager {
|
|||||||
return parseInt(`${process.env.CANCELLATION_TIME}`) || 24;
|
return parseInt(`${process.env.CANCELLATION_TIME}`) || 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private formatAppointmentDuration(startHour: number, endHour: number): string {
|
||||||
|
const durationHours = endHour >= startHour ? endHour - startHour : endHour + 24 - startHour;
|
||||||
|
const durationMinutes = Math.round(durationHours * 60);
|
||||||
|
const hours = Math.floor(durationMinutes / 60);
|
||||||
|
const minutes = durationMinutes % 60;
|
||||||
|
|
||||||
|
if (hours > 0 && minutes > 0) {
|
||||||
|
return `${hours} ${hours === 1 ? "hora" : "horas"} y ${minutes} minutos`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
return `${hours} ${hours === 1 ? "hora" : "horas"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${minutes} minutos`;
|
||||||
|
}
|
||||||
|
|
||||||
private validateCancellationTime(aTime: number | undefined, start: string | Date): boolean {
|
private validateCancellationTime(aTime: number | undefined, start: string | Date): boolean {
|
||||||
let cancellationTime = this.getCancellationTime(aTime);
|
let cancellationTime = this.getCancellationTime(aTime);
|
||||||
|
|
||||||
@@ -2168,6 +2185,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
companyOwnerId: string;
|
companyOwnerId: string;
|
||||||
companyName: string;
|
companyName: string;
|
||||||
emailMessage: string;
|
emailMessage: string;
|
||||||
|
emailTemplateId?: string;
|
||||||
|
emailContext?: Record<string, unknown>;
|
||||||
wapMessage: string;
|
wapMessage: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const systemSubject = `Turno cancelado en ${data.companyName}`;
|
const systemSubject = `Turno cancelado en ${data.companyName}`;
|
||||||
@@ -2187,6 +2206,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
message: data.emailMessage,
|
message: data.emailMessage,
|
||||||
emailSubject: "TurnosXpress :: Turno cancelado",
|
emailSubject: "TurnosXpress :: Turno cancelado",
|
||||||
emailMessage: data.emailMessage,
|
emailMessage: data.emailMessage,
|
||||||
|
emailTemplateId: data.emailTemplateId,
|
||||||
|
emailContext: data.emailContext,
|
||||||
wapMessage: data.wapMessage,
|
wapMessage: data.wapMessage,
|
||||||
systemSubject,
|
systemSubject,
|
||||||
systemMessage: data.emailMessage,
|
systemMessage: data.emailMessage,
|
||||||
@@ -2206,8 +2227,12 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
appointmentStart: Date;
|
appointmentStart: Date;
|
||||||
emailMessage: string;
|
emailMessage: string;
|
||||||
wapMessage: string;
|
wapMessage: string;
|
||||||
|
emailTemplateId?: string;
|
||||||
|
emailContext?: Record<string, unknown>;
|
||||||
reminderEmailMessage: string;
|
reminderEmailMessage: string;
|
||||||
reminderWapMessage: string;
|
reminderWapMessage: string;
|
||||||
|
reminderEmailTemplateId?: string;
|
||||||
|
reminderEmailContext?: Record<string, unknown>;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const systemSubject = `Turno reservado en ${data.companyName}`;
|
const systemSubject = `Turno reservado en ${data.companyName}`;
|
||||||
|
|
||||||
@@ -2220,6 +2245,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
message: data.emailMessage,
|
message: data.emailMessage,
|
||||||
emailSubject: "TurnosXpress :: Alta de turno",
|
emailSubject: "TurnosXpress :: Alta de turno",
|
||||||
emailMessage: data.emailMessage,
|
emailMessage: data.emailMessage,
|
||||||
|
emailTemplateId: data.emailTemplateId,
|
||||||
|
emailContext: data.emailContext,
|
||||||
wapMessage: data.wapMessage,
|
wapMessage: data.wapMessage,
|
||||||
systemSubject,
|
systemSubject,
|
||||||
systemMessage: data.emailMessage,
|
systemMessage: data.emailMessage,
|
||||||
@@ -2277,6 +2304,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
companyName: data.companyName,
|
companyName: data.companyName,
|
||||||
appointmentStart: data.appointmentStart,
|
appointmentStart: data.appointmentStart,
|
||||||
reminderEmailMessage: reminderEmailContent.message,
|
reminderEmailMessage: reminderEmailContent.message,
|
||||||
|
reminderEmailTemplateId: reminderEmailContent.emailTemplateId,
|
||||||
|
reminderEmailContext: reminderEmailContent.emailContext,
|
||||||
reminderWapMessage: reminderWapContent.message,
|
reminderWapMessage: reminderWapContent.message,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2301,6 +2330,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
await this.createCreationNotificationJobs({
|
await this.createCreationNotificationJobs({
|
||||||
...notificationJobData,
|
...notificationJobData,
|
||||||
emailMessage: emailContent.message,
|
emailMessage: emailContent.message,
|
||||||
|
emailTemplateId: emailContent.emailTemplateId,
|
||||||
|
emailContext: emailContent.emailContext,
|
||||||
wapMessage: wapContent.message,
|
wapMessage: wapContent.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2317,6 +2348,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
appointmentStart: Date;
|
appointmentStart: Date;
|
||||||
reminderEmailMessage: string;
|
reminderEmailMessage: string;
|
||||||
reminderWapMessage: string;
|
reminderWapMessage: string;
|
||||||
|
reminderEmailTemplateId?: string;
|
||||||
|
reminderEmailContext?: Record<string, unknown>;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const reminderSystemSubject = `Recordatorio de turno en ${data.companyName}`;
|
const reminderSystemSubject = `Recordatorio de turno en ${data.companyName}`;
|
||||||
|
|
||||||
@@ -2335,6 +2368,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
message: data.reminderEmailMessage,
|
message: data.reminderEmailMessage,
|
||||||
emailSubject: "TurnosXpress :: Recordatorio",
|
emailSubject: "TurnosXpress :: Recordatorio",
|
||||||
emailMessage: data.reminderEmailMessage,
|
emailMessage: data.reminderEmailMessage,
|
||||||
|
emailTemplateId: data.reminderEmailTemplateId,
|
||||||
|
emailContext: data.reminderEmailContext,
|
||||||
wapMessage: data.reminderWapMessage,
|
wapMessage: data.reminderWapMessage,
|
||||||
systemSubject: reminderSystemSubject,
|
systemSubject: reminderSystemSubject,
|
||||||
systemMessage: data.reminderEmailMessage,
|
systemMessage: data.reminderEmailMessage,
|
||||||
@@ -2511,6 +2546,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
companyOwnerId: String(companyCheck.ownerId),
|
companyOwnerId: String(companyCheck.ownerId),
|
||||||
companyName: companyCheck.name,
|
companyName: companyCheck.name,
|
||||||
emailMessage: emailContent.message,
|
emailMessage: emailContent.message,
|
||||||
|
emailTemplateId: emailContent.emailTemplateId,
|
||||||
|
emailContext: emailContent.emailContext,
|
||||||
wapMessage: wapContent.message,
|
wapMessage: wapContent.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2705,6 +2742,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let wapMessage = "";
|
let wapMessage = "";
|
||||||
|
let emailTemplateId: string | undefined;
|
||||||
|
let emailContext: Record<string, unknown> | undefined;
|
||||||
|
|
||||||
if (data.type == APPOINTMENT_NOTIFICATION_TYPE.REMINDER) {
|
if (data.type == APPOINTMENT_NOTIFICATION_TYPE.REMINDER) {
|
||||||
if (companyCheck.templateWapNotifId) {
|
if (companyCheck.templateWapNotifId) {
|
||||||
@@ -2748,6 +2787,19 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
|
|
||||||
wapMessage = wapMessage.replaceAll("<---ORGANIZACION--->", companyCheck.name);
|
wapMessage = wapMessage.replaceAll("<---ORGANIZACION--->", companyCheck.name);
|
||||||
wapMessage = wapMessage.replaceAll("<---BR--->", "\r\n");
|
wapMessage = wapMessage.replaceAll("<---BR--->", "\r\n");
|
||||||
|
|
||||||
|
emailTemplateId = "6a86ef631898c6948d0f5308";
|
||||||
|
emailContext = {
|
||||||
|
username: ClientsManager.getClientFullName(checkClient),
|
||||||
|
nombre_organizacion: companyCheck.name,
|
||||||
|
fecha_turno: dayjs(checkAppointment.start).format("DD/MM/YYYY"),
|
||||||
|
hora_turno: dayjs(checkAppointment.start).format("HH:mm") + "hs.",
|
||||||
|
nombre_servicio: checkService.name,
|
||||||
|
nombre_profesional: UsersManager.getUserFullName(checkEmployeeUser),
|
||||||
|
duracion: this.formatAppointmentDuration(checkAppointment.startHour, checkAppointment.endHour),
|
||||||
|
precio: formatCurrency(checkAppointment.price),
|
||||||
|
direccion: CompaniesManager.getCompanyAddress(companyCheck),
|
||||||
|
};
|
||||||
} else if (data.type == APPOINTMENT_NOTIFICATION_TYPE.CREATION) {
|
} else if (data.type == APPOINTMENT_NOTIFICATION_TYPE.CREATION) {
|
||||||
const templateId = data.channel === "email"
|
const templateId = data.channel === "email"
|
||||||
? companyCheck.templateEmailAltaId || companyCheck.templateWapAltaId
|
? companyCheck.templateEmailAltaId || companyCheck.templateWapAltaId
|
||||||
@@ -2797,6 +2849,19 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
|
|
||||||
wapMessage = wapMessage.replaceAll("<---ORGANIZACION--->", companyCheck.name);
|
wapMessage = wapMessage.replaceAll("<---ORGANIZACION--->", companyCheck.name);
|
||||||
wapMessage = wapMessage.replaceAll("<---BR--->", "\r\n");
|
wapMessage = wapMessage.replaceAll("<---BR--->", "\r\n");
|
||||||
|
|
||||||
|
emailTemplateId = "6a86f55e3edd051e8a04c711";
|
||||||
|
emailContext = {
|
||||||
|
username: ClientsManager.getClientFullName(checkClient),
|
||||||
|
nombre_organizacion: companyCheck.name,
|
||||||
|
fecha_turno: dayjs(checkAppointment.start).format("DD/MM/YYYY"),
|
||||||
|
hora_turno: dayjs(checkAppointment.start).format("HH:mm") + "hs.",
|
||||||
|
nombre_servicio: checkService.name,
|
||||||
|
nombre_profesional: UsersManager.getUserFullName(checkEmployeeUser),
|
||||||
|
duracion: this.formatAppointmentDuration(checkAppointment.startHour, checkAppointment.endHour),
|
||||||
|
precio: formatCurrency(checkAppointment.price),
|
||||||
|
direccion: CompaniesManager.getCompanyAddress(companyCheck),
|
||||||
|
};
|
||||||
} else if (data.type == APPOINTMENT_NOTIFICATION_TYPE.CANCELLATION || data.type == APPOINTMENT_NOTIFICATION_TYPE.UPDATE) {
|
} else if (data.type == APPOINTMENT_NOTIFICATION_TYPE.CANCELLATION || data.type == APPOINTMENT_NOTIFICATION_TYPE.UPDATE) {
|
||||||
const isUpdate = data.type == APPOINTMENT_NOTIFICATION_TYPE.UPDATE;
|
const isUpdate = data.type == APPOINTMENT_NOTIFICATION_TYPE.UPDATE;
|
||||||
const templateId = data.channel === "email"
|
const templateId = data.channel === "email"
|
||||||
@@ -2852,12 +2917,29 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
wapMessage = wapMessage.replaceAll("<---PRECIO--->", formatCurrency(checkAppointment.price));
|
wapMessage = wapMessage.replaceAll("<---PRECIO--->", formatCurrency(checkAppointment.price));
|
||||||
wapMessage = wapMessage.replaceAll("<---ORGANIZACION--->", companyCheck.name);
|
wapMessage = wapMessage.replaceAll("<---ORGANIZACION--->", companyCheck.name);
|
||||||
wapMessage = wapMessage.replaceAll("<---BR--->", "\r\n");
|
wapMessage = wapMessage.replaceAll("<---BR--->", "\r\n");
|
||||||
|
|
||||||
|
emailTemplateId = isUpdate
|
||||||
|
? "6a86febf6713e0bdd104c7ca"
|
||||||
|
: "6a86f77d376a7371170b21c5";
|
||||||
|
emailContext = {
|
||||||
|
username: ClientsManager.getClientFullName(checkClient),
|
||||||
|
nombre_organizacion: companyCheck.name,
|
||||||
|
fecha_turno: dayjs(checkAppointment.start).format("DD/MM/YYYY"),
|
||||||
|
hora_turno: dayjs(checkAppointment.start).format("HH:mm") + "hs.",
|
||||||
|
nombre_servicio: checkService.name,
|
||||||
|
nombre_profesional: UsersManager.getUserFullName(checkEmployeeUser),
|
||||||
|
duracion: this.formatAppointmentDuration(checkAppointment.startHour, checkAppointment.endHour),
|
||||||
|
precio: formatCurrency(checkAppointment.price),
|
||||||
|
direccion: CompaniesManager.getCompanyAddress(companyCheck),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: wapMessage,
|
message: wapMessage,
|
||||||
checkClient: checkClient,
|
checkClient: checkClient,
|
||||||
companyCheck: companyCheck,
|
companyCheck: companyCheck,
|
||||||
|
emailTemplateId,
|
||||||
|
emailContext,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2876,7 +2958,7 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { message, checkClient, companyCheck } = await this.tryToSendNotification({
|
const { message, checkClient, companyCheck, emailTemplateId, emailContext } = await this.tryToSendNotification({
|
||||||
...data,
|
...data,
|
||||||
channel: "email",
|
channel: "email",
|
||||||
});
|
});
|
||||||
@@ -2913,6 +2995,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
email: clientEmail,
|
email: clientEmail,
|
||||||
subject: subjectEmail,
|
subject: subjectEmail,
|
||||||
message: message,
|
message: message,
|
||||||
|
templateId: emailTemplateId,
|
||||||
|
context: emailContext,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2964,6 +3048,8 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
|
|||||||
message: emailContent.message,
|
message: emailContent.message,
|
||||||
emailSubject: isCreation ? "TurnosXpress :: Alta de turno" : "TurnosXpress :: Recordatorio",
|
emailSubject: isCreation ? "TurnosXpress :: Alta de turno" : "TurnosXpress :: Recordatorio",
|
||||||
emailMessage: emailContent.message,
|
emailMessage: emailContent.message,
|
||||||
|
emailTemplateId: emailContent.emailTemplateId,
|
||||||
|
emailContext: emailContent.emailContext,
|
||||||
wapMessage: wapContent.message,
|
wapMessage: wapContent.message,
|
||||||
systemSubject,
|
systemSubject,
|
||||||
systemMessage: emailContent.message,
|
systemMessage: emailContent.message,
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ export interface NotificationJobPayload {
|
|||||||
message?: string;
|
message?: string;
|
||||||
emailSubject?: string;
|
emailSubject?: string;
|
||||||
emailMessage?: string;
|
emailMessage?: string;
|
||||||
|
emailTemplateId?: string;
|
||||||
|
emailContext?: Record<string, unknown>;
|
||||||
wapMessage?: string;
|
wapMessage?: string;
|
||||||
systemSubject?: string;
|
systemSubject?: string;
|
||||||
systemMessage?: string;
|
systemMessage?: string;
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import { ApiVoidResult } from "../../Models/Api.VoidResult.type";
|
|||||||
interface SendEmailByContentParams {
|
interface SendEmailByContentParams {
|
||||||
systemToken: string;
|
systemToken: string;
|
||||||
email: string;
|
email: string;
|
||||||
subject: string;
|
subject?: string;
|
||||||
message: string;
|
message?: string;
|
||||||
|
templateId?: string;
|
||||||
|
context?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SendWapByContentParams {
|
interface SendWapByContentParams {
|
||||||
@@ -29,6 +31,8 @@ export class SendEmailByContentController extends Controller {
|
|||||||
email: requestBody.email,
|
email: requestBody.email,
|
||||||
subject: requestBody.subject,
|
subject: requestBody.subject,
|
||||||
message: requestBody.message,
|
message: requestBody.message,
|
||||||
|
templateId: requestBody.templateId,
|
||||||
|
context: requestBody.context,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setStatus(200);
|
this.setStatus(200);
|
||||||
|
|||||||
Reference in New Issue
Block a user