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(); /** * 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`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 }; }