Files
formation-manager-itinova/server/remerciementScheduler.ts

210 lines
6.8 KiB
TypeScript

import { getDb } from "./db";
import { sequences, inscriptions, apprenants, formations, datesFormation, formateurs } from "../drizzle/schema";
import { eq, and, lte, sql } from "drizzle-orm";
import { sendRemerciementPostFormation } from "./emailService";
/**
* Table pour suivre les remerciements déjà envoyés
*/
const remerciementsEnvoyes = new Map<string, Date>();
/**
* Vérifie si un remerciement a déjà été envoyé pour une inscription
*/
function remerciementDejaEnvoye(inscriptionId: number, sequenceId: number): boolean {
const key = `${inscriptionId}-${sequenceId}`;
return remerciementsEnvoyes.has(key);
}
/**
* Marque un remerciement comme envoyé
*/
function marquerRemerciementEnvoye(inscriptionId: number, sequenceId: number): void {
const key = `${inscriptionId}-${sequenceId}`;
remerciementsEnvoyes.set(key, new Date());
}
/**
* Job automatique qui vérifie quotidiennement les séquences terminées
* et envoie les emails de remerciement aux apprenants
*/
export async function processRemerciementsAutomatiques() {
console.log("[Remerciements] Démarrage du traitement des remerciements post-formation...");
const db = await getDb();
if (!db) {
console.error("[Remerciements] Base de données non disponible");
return;
}
try {
const now = new Date();
const hier = new Date();
hier.setDate(hier.getDate() - 1);
hier.setHours(23, 59, 59, 999);
// Récupérer les séquences dont la dernière date est passée (terminées hier ou avant)
const sequencesTerminees = await db
.select({
sequence: sequences,
formation: formations,
formateur: formateurs,
derniereDate: sql<Date>`MAX(${datesFormation.dateFin})`.as('derniereDate'),
})
.from(sequences)
.innerJoin(formations, eq(sequences.formationId, formations.id))
.leftJoin(formateurs, eq(sequences.formateurId, formateurs.id))
.innerJoin(datesFormation, eq(sequences.id, datesFormation.sequenceId))
.where(eq(sequences.statut, 'ouverte')) // Séquences encore ouvertes (pas encore marquées terminées)
.groupBy(sequences.id, formations.id, formateurs.id)
.having(lte(sql`MAX(${datesFormation.dateFin})`, hier));
console.log(`[Remerciements] ${sequencesTerminees.length} séquence(s) terminée(s) trouvée(s)`);
for (const { sequence, formation, formateur, derniereDate } of sequencesTerminees) {
await processRemerciementSequence(sequence, formation, formateur, derniereDate);
}
console.log("[Remerciements] Traitement terminé");
} catch (error) {
console.error("[Remerciements] Erreur lors du traitement des remerciements:", error);
}
}
async function processRemerciementSequence(
sequence: any,
formation: any,
formateur: any | null,
derniereDate: Date
) {
console.log(`[Remerciements] Traitement de la séquence: ${sequence.nom} (terminée le ${derniereDate})`);
const db = await getDb();
if (!db) return;
try {
// Récupérer les inscriptions confirmées pour cette séquence
const inscriptionsConfirmees = await db
.select({
inscription: inscriptions,
apprenant: apprenants,
})
.from(inscriptions)
.innerJoin(apprenants, eq(inscriptions.apprenantId, apprenants.id))
.where(
and(
eq(inscriptions.sequenceId, sequence.id),
eq(inscriptions.statut, 'confirmee')
)
);
console.log(`[Remerciements] ${inscriptionsConfirmees.length} inscription(s) confirmée(s) pour ${sequence.nom}`);
let envoyesCount = 0;
let errorsCount = 0;
for (const { inscription, apprenant } of inscriptionsConfirmees) {
// Vérifier si le remerciement a déjà été envoyé
if (remerciementDejaEnvoye(inscription.id, sequence.id)) {
console.log(`[Remerciements] Remerciement déjà envoyé pour ${apprenant.email}`);
continue;
}
try {
await sendRemerciementPostFormation({
apprenantEmail: apprenant.email,
apprenantNom: apprenant.nom,
apprenantPrenom: apprenant.prenom,
apprenantFonction: apprenant.fonction,
formationNom: formation.nom,
sequenceNom: sequence.nom,
formateurNom: formateur?.nom,
});
marquerRemerciementEnvoye(inscription.id, sequence.id);
envoyesCount++;
console.log(`[Remerciements] Email envoyé à ${apprenant.email}`);
} catch (error) {
errorsCount++;
console.error(`[Remerciements] Erreur envoi à ${apprenant.email}:`, error);
}
}
console.log(`[Remerciements] Séquence ${sequence.nom}: ${envoyesCount} envoyé(s), ${errorsCount} erreur(s)`);
// Optionnel: Marquer la séquence comme terminée
// await db.update(sequences).set({ statut: 'terminee' }).where(eq(sequences.id, sequence.id));
} catch (error) {
console.error(`[Remerciements] Erreur pour la séquence ${sequence.nom}:`, error);
}
}
/**
* Envoie manuellement un email de remerciement pour une séquence spécifique
* Utilisé par l'interface admin pour envoyer les remerciements à la demande
*/
export async function envoyerRemerciementsSequence(sequenceId: number): Promise<{ sent: number; failed: number }> {
const db = await getDb();
if (!db) {
throw new Error("Base de données non disponible");
}
// Récupérer la séquence avec formation et formateur
const sequenceData = await db
.select({
sequence: sequences,
formation: formations,
formateur: formateurs,
})
.from(sequences)
.innerJoin(formations, eq(sequences.formationId, formations.id))
.leftJoin(formateurs, eq(sequences.formateurId, formateurs.id))
.where(eq(sequences.id, sequenceId))
.limit(1);
if (sequenceData.length === 0) {
throw new Error("Séquence introuvable");
}
const { sequence, formation, formateur } = sequenceData[0];
// Récupérer les inscriptions confirmées
const inscriptionsConfirmees = await db
.select({
inscription: inscriptions,
apprenant: apprenants,
})
.from(inscriptions)
.innerJoin(apprenants, eq(inscriptions.apprenantId, apprenants.id))
.where(
and(
eq(inscriptions.sequenceId, sequenceId),
eq(inscriptions.statut, 'confirmee')
)
);
let sent = 0;
let failed = 0;
for (const { inscription, apprenant } of inscriptionsConfirmees) {
try {
await sendRemerciementPostFormation({
apprenantEmail: apprenant.email,
apprenantNom: apprenant.nom,
apprenantPrenom: apprenant.prenom,
apprenantFonction: apprenant.fonction,
formationNom: formation.nom,
sequenceNom: sequence.nom,
formateurNom: formateur?.nom,
});
sent++;
} catch (error) {
console.error(`[Remerciements] Erreur envoi à ${apprenant.email}:`, error);
failed++;
}
}
return { sent, failed };
}