208 lines
7.1 KiB
TypeScript
208 lines
7.1 KiB
TypeScript
import { getDb } from "./db";
|
|
import { sequences, inscriptions, apprenants, formations, datesFormation, attestations, logsNotifications } from "../drizzle/schema";
|
|
import { eq, and, lte, sql, isNull } from "drizzle-orm";
|
|
import { sendAttestationEmail } from "./emailService";
|
|
import { logNotification } from "./notificationLogsDb";
|
|
|
|
/**
|
|
* Vérifie si une attestation a déjà été envoyée pour une inscription
|
|
*/
|
|
async function attestationDejaEnvoyee(apprenantId: number, sequenceId: number): Promise<boolean> {
|
|
const db = await getDb();
|
|
if (!db) return false;
|
|
|
|
try {
|
|
const [existing] = await db
|
|
.select({ id: logsNotifications.id })
|
|
.from(logsNotifications)
|
|
.where(
|
|
and(
|
|
eq(logsNotifications.type, "attestation"),
|
|
eq(logsNotifications.apprenantId, apprenantId),
|
|
eq(logsNotifications.sequenceId, sequenceId),
|
|
eq(logsNotifications.statut, "success")
|
|
)
|
|
)
|
|
.limit(1);
|
|
|
|
return !!existing;
|
|
} catch (error) {
|
|
console.error("[Attestations Auto] Erreur lors de la vérification du log:", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Job automatique qui vérifie quotidiennement les séquences terminées
|
|
* et envoie les attestations aux apprenants pour les formations en mode envoi automatique
|
|
*/
|
|
export async function processAttestationsAutomatiques() {
|
|
console.log("[Attestations Auto] Démarrage du traitement des attestations automatiques...");
|
|
|
|
const db = await getDb();
|
|
if (!db) {
|
|
console.error("[Attestations Auto] 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)
|
|
// ET dont la formation a le mode d'envoi automatique activé
|
|
const sequencesTerminees = await db
|
|
.select({
|
|
sequence: sequences,
|
|
formation: formations,
|
|
derniereDate: sql<Date>`MAX(${datesFormation.dateFin})`.as('derniereDate'),
|
|
})
|
|
.from(sequences)
|
|
.innerJoin(formations, eq(sequences.formationId, formations.id))
|
|
.innerJoin(datesFormation, eq(sequences.id, datesFormation.sequenceId))
|
|
.where(
|
|
and(
|
|
eq(formations.modeEnvoi, 'auto'), // Seulement les formations en mode envoi automatique
|
|
eq(sequences.statut, 'ouverte') // Séquences encore ouvertes
|
|
)
|
|
)
|
|
.groupBy(sequences.id, formations.id)
|
|
.having(lte(sql`MAX(${datesFormation.dateFin})`, hier));
|
|
|
|
console.log(`[Attestations Auto] ${sequencesTerminees.length} séquence(s) terminée(s) avec envoi automatique trouvée(s)`);
|
|
|
|
for (const { sequence, formation, derniereDate } of sequencesTerminees) {
|
|
await processAttestationSequence(sequence, formation, derniereDate);
|
|
}
|
|
|
|
console.log("[Attestations Auto] Traitement terminé");
|
|
} catch (error) {
|
|
console.error("[Attestations Auto] Erreur lors du traitement des attestations:", error);
|
|
}
|
|
}
|
|
|
|
async function processAttestationSequence(
|
|
sequence: any,
|
|
formation: any,
|
|
derniereDate: Date
|
|
) {
|
|
console.log(`[Attestations Auto] 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 avec leurs attestations
|
|
const inscriptionsAvecAttestations = await db
|
|
.select({
|
|
inscription: inscriptions,
|
|
apprenant: apprenants,
|
|
attestation: attestations,
|
|
})
|
|
.from(inscriptions)
|
|
.innerJoin(apprenants, eq(inscriptions.apprenantId, apprenants.id))
|
|
.leftJoin(attestations, eq(inscriptions.id, attestations.inscriptionId))
|
|
.where(
|
|
and(
|
|
eq(inscriptions.sequenceId, sequence.id),
|
|
eq(inscriptions.statut, 'confirmee')
|
|
)
|
|
);
|
|
|
|
console.log(`[Attestations Auto] ${inscriptionsAvecAttestations.length} inscription(s) confirmée(s) pour ${sequence.nom}`);
|
|
|
|
let envoyesCount = 0;
|
|
let errorsCount = 0;
|
|
let sansAttestationCount = 0;
|
|
|
|
for (const { inscription, apprenant, attestation } of inscriptionsAvecAttestations) {
|
|
// Vérifier si l'attestation existe
|
|
if (!attestation || !attestation.documentUrl) {
|
|
sansAttestationCount++;
|
|
console.log(`[Attestations Auto] Pas d'attestation générée pour ${apprenant.email}`);
|
|
continue;
|
|
}
|
|
|
|
// Vérifier si l'attestation a déjà été envoyée
|
|
const dejaEnvoyee = await attestationDejaEnvoyee(apprenant.id, sequence.id);
|
|
if (dejaEnvoyee) {
|
|
console.log(`[Attestations Auto] Attestation déjà envoyée pour ${apprenant.email}`);
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
await sendAttestationEmail({
|
|
to: apprenant.email,
|
|
apprenantNom: apprenant.nom,
|
|
apprenantPrenom: apprenant.prenom,
|
|
formationNom: formation.nom,
|
|
pdfUrl: attestation.documentUrl,
|
|
});
|
|
|
|
// Logger la notification
|
|
await logNotification({
|
|
type: "attestation",
|
|
sequenceId: sequence.id,
|
|
apprenantId: apprenant.id,
|
|
emailDestinataire: apprenant.email,
|
|
sujet: `Votre attestation de formation - ${formation.nom}`,
|
|
statut: "success",
|
|
metadata: { attestationId: attestation.id },
|
|
});
|
|
|
|
envoyesCount++;
|
|
console.log(`[Attestations Auto] Email envoyé à ${apprenant.email}`);
|
|
} catch (error: any) {
|
|
// Logger l'échec
|
|
await logNotification({
|
|
type: "attestation",
|
|
sequenceId: sequence.id,
|
|
apprenantId: apprenant.id,
|
|
emailDestinataire: apprenant.email,
|
|
sujet: `Votre attestation de formation - ${formation.nom}`,
|
|
statut: "failed",
|
|
messageErreur: error.message,
|
|
});
|
|
|
|
errorsCount++;
|
|
console.error(`[Attestations Auto] Erreur envoi à ${apprenant.email}:`, error);
|
|
}
|
|
}
|
|
|
|
console.log(`[Attestations Auto] Séquence ${sequence.nom}: ${envoyesCount} envoyé(s), ${errorsCount} erreur(s), ${sansAttestationCount} sans attestation`);
|
|
|
|
} catch (error) {
|
|
console.error(`[Attestations Auto] Erreur pour la séquence ${sequence.nom}:`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialise le scheduler pour l'envoi automatique des attestations
|
|
* Exécute le job tous les jours à 10h00
|
|
*/
|
|
export function initAttestationScheduler() {
|
|
console.log("[Attestations Auto] Initialisation du scheduler d'envoi automatique");
|
|
|
|
// Exécuter immédiatement au démarrage
|
|
processAttestationsAutomatiques();
|
|
|
|
// Puis exécuter tous les jours à 10h00
|
|
const HEURE_EXECUTION = 10; // 10h00
|
|
const MINUTE_EXECUTION = 0;
|
|
|
|
setInterval(() => {
|
|
const now = new Date();
|
|
const heureActuelle = now.getHours();
|
|
const minuteActuelle = now.getMinutes();
|
|
|
|
// Exécuter seulement à l'heure prévue (avec une tolérance de 1 minute)
|
|
if (heureActuelle === HEURE_EXECUTION && minuteActuelle === MINUTE_EXECUTION) {
|
|
processAttestationsAutomatiques();
|
|
}
|
|
}, 60 * 1000); // Vérifier toutes les minutes
|
|
|
|
console.log(`[Attestations Auto] Scheduler initialisé - Exécution quotidienne à ${HEURE_EXECUTION}:${String(MINUTE_EXECUTION).padStart(2, '0')}`);
|
|
}
|