147 lines
5.6 KiB
TypeScript
147 lines
5.6 KiB
TypeScript
import db from '../../database/models/index.js';
|
|
import { Op } from 'sequelize';
|
|
import { sendEmail } from './email.service.js';
|
|
import { NotificationService } from '../../services/NotificationService.js';
|
|
import { REQUEST_TYPES } from '../config/constants.js';
|
|
|
|
const { RequestParticipant, User, Outlet, District } = db;
|
|
|
|
const frontendBase = () => process.env.FRONTEND_URL || 'http://localhost:5173';
|
|
|
|
/** Dealer acknowledgement + internal reviewers after resignation is created. */
|
|
export async function notifyResignationSubmittedEmails(resignation: any): Promise<void> {
|
|
const dealerUser = await User.findByPk(resignation.dealerId, {
|
|
attributes: ['id', 'email', 'fullName']
|
|
});
|
|
if (!dealerUser?.email) return;
|
|
|
|
const base = frontendBase();
|
|
const resignationCode = resignation.resignationId || resignation.id;
|
|
const lwd =
|
|
resignation.lastOperationalDateSales ||
|
|
resignation.lastOperationalDateServices ||
|
|
'As per application';
|
|
const dealerName = dealerUser.fullName || 'Dealer';
|
|
|
|
await sendEmail(
|
|
dealerUser.email,
|
|
`We received your resignation request — ${resignationCode}`,
|
|
'RESIGNATION_RECEIVED',
|
|
{
|
|
dealerName,
|
|
resignationId: resignationCode,
|
|
lwd: String(lwd),
|
|
link: `${base}/dealer-resignation/${resignation.id}`,
|
|
ctaLabel: 'View request'
|
|
}
|
|
).catch((err) => console.error('[notifyResignationSubmittedEmails] dealer ack:', err));
|
|
|
|
const participants = await RequestParticipant.findAll({
|
|
where: {
|
|
requestId: resignation.id,
|
|
requestType: REQUEST_TYPES.RESIGNATION,
|
|
userId: { [Op.ne]: resignation.dealerId }
|
|
},
|
|
include: [{ model: User, as: 'user', attributes: ['id', 'email', 'fullName'] }]
|
|
});
|
|
|
|
const internalLink = `${base}/resignation/${resignation.id}`;
|
|
for (const p of participants) {
|
|
const u = (p as any).user;
|
|
if (!u?.email) continue;
|
|
await NotificationService.notify(u.id, u.email, {
|
|
title: `New resignation request: ${resignationCode}`,
|
|
message: `Submitted by ${dealerName}.`,
|
|
channels: ['email', 'system'],
|
|
templateCode: 'RESIGNATION_SUBMITTED',
|
|
placeholders: {
|
|
dealerName,
|
|
resignationId: resignationCode,
|
|
lwd: String(lwd),
|
|
link: internalLink,
|
|
ctaLabel: 'Review resignation'
|
|
}
|
|
}).catch((err) => console.error('[notifyResignationSubmittedEmails] internal:', err));
|
|
}
|
|
}
|
|
|
|
/** Internal reviewers after constitutional request is created. */
|
|
export async function notifyConstitutionalSubmittedEmails(request: any, dealerDisplayName: string): Promise<void> {
|
|
const participants = await RequestParticipant.findAll({
|
|
where: {
|
|
requestId: request.id,
|
|
requestType: REQUEST_TYPES.CONSTITUTIONAL,
|
|
userId: { [Op.ne]: request.dealerId }
|
|
},
|
|
include: [{ model: User, as: 'user', attributes: ['id', 'email', 'fullName'] }]
|
|
});
|
|
|
|
const base = frontendBase();
|
|
const link = `${base}/constitutional-change/${request.id}`;
|
|
|
|
for (const p of participants) {
|
|
const u = (p as any).user;
|
|
if (!u?.email) continue;
|
|
await NotificationService.notify(u.id, u.email, {
|
|
title: `New constitutional change request: ${request.requestId}`,
|
|
message: `${dealerDisplayName} submitted a request.`,
|
|
channels: ['email', 'system'],
|
|
templateCode: 'CONSTITUTIONAL_CHANGE_SUBMITTED',
|
|
placeholders: {
|
|
dealerName: dealerDisplayName,
|
|
changeType: request.changeType || '',
|
|
requestId: request.requestId,
|
|
link,
|
|
ctaLabel: 'Review request'
|
|
}
|
|
}).catch((err) => console.error('[notifyConstitutionalSubmittedEmails]:', err));
|
|
}
|
|
}
|
|
|
|
/** Dealer + ASM when relocation request is submitted. */
|
|
export async function notifyRelocationSubmittedEmails(
|
|
request: any,
|
|
submitter: { email: string; fullName?: string | null }
|
|
): Promise<void> {
|
|
const base = frontendBase();
|
|
const code = request.requestId || request.id;
|
|
const dealerName = submitter.fullName?.trim() || 'Dealer';
|
|
|
|
if (submitter.email) {
|
|
await sendEmail(
|
|
submitter.email,
|
|
`Relocation request received — ${code}`,
|
|
'RELOCATION_RECEIVED',
|
|
{
|
|
dealerName,
|
|
requestId: code,
|
|
link: `${base}/relocation-requests/${request.id}`,
|
|
ctaLabel: 'View request'
|
|
}
|
|
).catch((err) => console.error('[notifyRelocationSubmittedEmails] dealer:', err));
|
|
}
|
|
|
|
const outlet = await Outlet.findByPk(request.outletId, {
|
|
include: [{ model: District, as: 'district', attributes: ['id', 'asmId'] }]
|
|
});
|
|
const asmId = (outlet as any)?.district?.asmId;
|
|
if (!asmId) return;
|
|
|
|
const asm = await User.findByPk(asmId, { attributes: ['id', 'email', 'fullName'] });
|
|
if (!asm?.email) return;
|
|
|
|
await NotificationService.notify(asm.id, asm.email, {
|
|
title: `New relocation request: ${code}`,
|
|
message: 'A dealer submitted an outlet relocation request.',
|
|
channels: ['email', 'system'],
|
|
templateCode: 'RELOCATION_SUBMITTED',
|
|
placeholders: {
|
|
dealerName,
|
|
requestId: code,
|
|
outletCode: outlet?.code || '',
|
|
link: `${base}/relocation-requests/${request.id}`,
|
|
ctaLabel: 'Review relocation'
|
|
}
|
|
}).catch((err) => console.error('[notifyRelocationSubmittedEmails] ASM:', err));
|
|
}
|