Checkpoint: Refonte majeure du système de rappels : sélection granulaire au niveau des dates de formation individuelles (dateFormationId) au lieu des séquences. Permet de choisir précisément quelles dates d'une séquence recevront un rappel. Interface hiérarchique avec séquences dépliables et checkboxes par date. Scheduler adapté pour traiter date par date.
This commit is contained in:
@@ -39,91 +39,101 @@ export async function processRappelsAutomatiques() {
|
||||
}
|
||||
|
||||
async function processRappel(rappel: any) {
|
||||
console.log(`[Rappels] Traitement du rappel: ${rappel.nom} (J-${rappel.joursAvant})`);
|
||||
console.log(`[Rappels] Traitement du rappel: ${rappel.nom} (${rappel.timing === 'pre_formation' ? 'J-' : 'J+'}${rappel.joursAvant})`);
|
||||
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
|
||||
try {
|
||||
// Calculer la date cible (aujourd'hui + joursAvant)
|
||||
// Calculer la date cible selon le timing (pré ou post-formation)
|
||||
const dateCible = new Date();
|
||||
dateCible.setDate(dateCible.getDate() + rappel.joursAvant);
|
||||
if (rappel.timing === 'pre_formation') {
|
||||
dateCible.setDate(dateCible.getDate() + rappel.joursAvant);
|
||||
} else {
|
||||
dateCible.setDate(dateCible.getDate() - rappel.joursAvant);
|
||||
}
|
||||
dateCible.setHours(0, 0, 0, 0);
|
||||
|
||||
const dateCibleFin = new Date(dateCible);
|
||||
dateCibleFin.setHours(23, 59, 59, 999);
|
||||
|
||||
console.log(`[Rappels] Recherche des séquences débutant le ${dateCible.toLocaleDateString()}`);
|
||||
console.log(`[Rappels] Recherche des dates de formation le ${dateCible.toLocaleDateString()}`);
|
||||
|
||||
// Vérifier si ce rappel est associé à des séquences spécifiques
|
||||
const sequencesAssociees = rappel.sequenceIds || [];
|
||||
const filtreSequences = sequencesAssociees.length > 0;
|
||||
// Vérifier si ce rappel est associé à des dates spécifiques
|
||||
const datesAssociees = rappel.dateFormationIds || [];
|
||||
const filtreDates = datesAssociees.length > 0;
|
||||
|
||||
console.log(`[Rappels] ${filtreSequences ? `Filtré sur ${sequencesAssociees.length} séquence(s) spécifique(s)` : 'Toutes les séquences'}`);
|
||||
console.log(`[Rappels] ${filtreDates ? `Filtré sur ${datesAssociees.length} date(s) spécifique(s)` : 'Toutes les dates'}`);
|
||||
|
||||
// Récupérer toutes les séquences (ou uniquement celles associées)
|
||||
let toutesSequences;
|
||||
if (filtreSequences) {
|
||||
// Seulement les séquences associées à ce rappel
|
||||
toutesSequences = await db
|
||||
// Récupérer les dates de formation concernées
|
||||
let datesConcernees;
|
||||
if (filtreDates) {
|
||||
// Seulement les dates associées à ce rappel
|
||||
datesConcernees = await db
|
||||
.select({
|
||||
dateFormation: datesFormation,
|
||||
sequence: sequences,
|
||||
formation: formations,
|
||||
formateur: formateurs,
|
||||
})
|
||||
.from(sequences)
|
||||
.from(datesFormation)
|
||||
.leftJoin(sequences, eq(datesFormation.sequenceId, sequences.id))
|
||||
.leftJoin(formations, eq(sequences.formationId, formations.id))
|
||||
.leftJoin(formateurs, eq(sequences.formateurId, formateurs.id))
|
||||
.where(inArray(sequences.id, sequencesAssociees));
|
||||
.where(
|
||||
and(
|
||||
inArray(datesFormation.id, datesAssociees),
|
||||
gte(datesFormation.dateDebut, dateCible),
|
||||
lte(datesFormation.dateDebut, dateCibleFin)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Toutes les séquences
|
||||
toutesSequences = await db
|
||||
// Toutes les dates correspondant à la date cible
|
||||
datesConcernees = await db
|
||||
.select({
|
||||
dateFormation: datesFormation,
|
||||
sequence: sequences,
|
||||
formation: formations,
|
||||
formateur: formateurs,
|
||||
})
|
||||
.from(sequences)
|
||||
.leftJoin(formations, eq(sequences.formationId, formations.id))
|
||||
.leftJoin(formateurs, eq(sequences.formateurId, formateurs.id));
|
||||
}
|
||||
|
||||
// Filtrer les séquences dont la première date correspond à la date cible
|
||||
const sequencesConcernees = [];
|
||||
for (const { sequence, formation, formateur } of toutesSequences) {
|
||||
if (!sequence || !formation) continue;
|
||||
|
||||
// Récupérer la première date de cette séquence
|
||||
const premiereDates = await db
|
||||
.select()
|
||||
.from(datesFormation)
|
||||
.leftJoin(sequences, eq(datesFormation.sequenceId, sequences.id))
|
||||
.leftJoin(formations, eq(sequences.formationId, formations.id))
|
||||
.leftJoin(formateurs, eq(sequences.formateurId, formateurs.id))
|
||||
.where(
|
||||
and(
|
||||
eq(datesFormation.sequenceId, sequence.id),
|
||||
eq(datesFormation.ordre, 1)
|
||||
gte(datesFormation.dateDebut, dateCible),
|
||||
lte(datesFormation.dateDebut, dateCibleFin)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (premiereDates.length === 0) continue;
|
||||
|
||||
const premiereDate = premiereDates[0];
|
||||
const dateDebut = new Date(premiereDate.dateDebut);
|
||||
dateDebut.setHours(0, 0, 0, 0);
|
||||
|
||||
// Vérifier si cette première date correspond à la date cible
|
||||
if (dateDebut >= dateCible && dateDebut <= dateCibleFin) {
|
||||
sequencesConcernees.push({ sequence, formation, formateur });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`[Rappels] ${sequencesConcernees.length} séquence(s) trouvée(s)`);
|
||||
console.log(`[Rappels] ${datesConcernees.length} date(s) de formation trouvée(s)`);
|
||||
|
||||
for (const { sequence, formation, formateur } of sequencesConcernees) {
|
||||
// Grouper les dates par séquence pour envoyer un seul email par apprenant
|
||||
const sequencesMap = new Map<number, { sequence: any; formation: any; formateur: any; dates: any[] }>();
|
||||
|
||||
for (const { dateFormation, sequence, formation, formateur } of datesConcernees) {
|
||||
if (!dateFormation || !sequence || !formation) continue;
|
||||
|
||||
if (!sequencesMap.has(sequence.id)) {
|
||||
sequencesMap.set(sequence.id, {
|
||||
sequence,
|
||||
formation,
|
||||
formateur,
|
||||
dates: [],
|
||||
});
|
||||
}
|
||||
sequencesMap.get(sequence.id)!.dates.push(dateFormation);
|
||||
}
|
||||
|
||||
console.log(`[Rappels] ${sequencesMap.size} séquence(s) concernée(s)`);
|
||||
|
||||
for (const { sequence, formation, formateur, dates: datesRappel } of Array.from(sequencesMap.values())) {
|
||||
if (!sequence || !formation) continue;
|
||||
|
||||
// Récupérer toutes les dates de cette séquence
|
||||
const dates = await db
|
||||
// Récupérer toutes les dates de cette séquence pour l'email
|
||||
const toutesLesDates = await db
|
||||
.select()
|
||||
.from(datesFormation)
|
||||
.where(eq(datesFormation.sequenceId, sequence.id))
|
||||
@@ -174,7 +184,7 @@ async function processRappel(rappel: any) {
|
||||
apprenantFonction: apprenant.fonction,
|
||||
formationNom: formation.nom,
|
||||
sequenceNom: sequence.nom,
|
||||
dates: dates.map(d => ({
|
||||
dates: toutesLesDates.map(d => ({
|
||||
dateDebut: d.dateDebut,
|
||||
dateFin: d.dateFin,
|
||||
ordre: d.ordre
|
||||
@@ -195,7 +205,7 @@ async function processRappel(rappel: any) {
|
||||
apprenantFonction: apprenant.fonction,
|
||||
formationNom: formation.nom,
|
||||
sequenceNom: sequence.nom,
|
||||
dates: dates.map(d => ({
|
||||
dates: toutesLesDates.map(d => ({
|
||||
dateDebut: d.dateDebut,
|
||||
dateFin: d.dateFin,
|
||||
ordre: d.ordre
|
||||
@@ -217,7 +227,7 @@ async function processRappel(rappel: any) {
|
||||
emailDestinataire: apprenant.email,
|
||||
typeRappel: rappel.templateType,
|
||||
statut: "success",
|
||||
dateSequence: dates[0].dateDebut,
|
||||
dateSequence: toutesLesDates[0].dateDebut,
|
||||
});
|
||||
nbEnvoyes++;
|
||||
} catch (emailError: any) {
|
||||
@@ -233,7 +243,7 @@ async function processRappel(rappel: any) {
|
||||
typeRappel: rappel.templateType,
|
||||
statut: "failed",
|
||||
messageErreur: messageErreur,
|
||||
dateSequence: dates[0].dateDebut,
|
||||
dateSequence: toutesLesDates[0].dateDebut,
|
||||
});
|
||||
nbEchecs++;
|
||||
echecs.push({ email: apprenant.email, erreur: messageErreur });
|
||||
|
||||
Reference in New Issue
Block a user