Checkpoint: Checkpoint saved: Correction complète de l'enregistrement des logs de rappels :

**Problèmes corrigés :**
1. Type de rappel erroné : utilisait typeRappel au lieu de type, affichait le mauvais template (J-1 au lieu de J+2)
2. Statut erroné : utilisait "success"/"failed" (anglais) au lieu de "succes"/"echec" (français)
3. Email erroné : utilisait emailDestinataire au lieu de email
4. Champs obsolètes : supprimé dateSequence qui n'existe plus dans le schéma

**Fichiers modifiés :**
- server/rappelDb.ts : Réécriture complète avec les bons noms de colonnes (email, type, statut, typeEnvoi)
- server/rappelScheduler.ts : Correction des appels à logRappelEnvoye avec les bons paramètres
- server/routers.ts : Correction de l'enum du statut dans la procédure historique (succes/echec)

**Résultat :**
- Les logs de rappels enregistrent maintenant correctement le type de rappel (J+2, J-7, etc.)
- Le statut est correctement enregistré en français (succes/echec)
- L'email du destinataire est correctement enregistré
- Les tests de rappel sont distingués des envois automatiques via typeEnvoi

Déployé sur le VPS de production (https://formations.itinova.org)
This commit is contained in:
Manus
2026-01-16 06:33:12 -05:00
parent b9fbfafc69
commit 8f21fdd276
4 changed files with 31 additions and 29 deletions

View File

@@ -9,11 +9,11 @@ export async function logRappelEnvoye(data: {
rappelId: number;
sequenceId: number;
apprenantId: number;
emailDestinataire: string;
typeRappel: string;
statut: "success" | "failed";
email: string;
type: string;
typeEnvoi?: 'automatique' | 'test';
statut: "succes" | "echec";
messageErreur?: string;
dateSequence: Date;
}): Promise<void> {
const db = await getDb();
if (!db) {
@@ -26,13 +26,13 @@ export async function logRappelEnvoye(data: {
rappelId: data.rappelId,
sequenceId: data.sequenceId,
apprenantId: data.apprenantId,
emailDestinataire: data.emailDestinataire,
typeRappel: data.typeRappel,
email: data.email,
type: data.type as any,
typeEnvoi: data.typeEnvoi || 'automatique',
dateEnvoi: new Date(),
statut: data.statut,
messageErreur: data.messageErreur || null,
dateSequence: data.dateSequence,
} as InsertLogRappel);
});
} catch (error) {
console.error("[LogsRappels] Erreur lors de l'enregistrement du log:", error);
}
@@ -61,7 +61,8 @@ export async function rappelDejaEnvoye(
eq(logsRappels.rappelId, rappelId),
eq(logsRappels.sequenceId, sequenceId),
eq(logsRappels.apprenantId, apprenantId),
eq(logsRappels.statut, "success")
eq(logsRappels.statut, "succes"),
eq(logsRappels.typeEnvoi, "automatique") // Ne pas compter les tests
)
)
.limit(1);
@@ -108,7 +109,7 @@ export async function getLogsRappelsEchoues(): Promise<LogRappel[]> {
.from(logsRappels)
.where(
and(
eq(logsRappels.statut, "failed"),
eq(logsRappels.statut, "echec"),
// Note: Drizzle ne supporte pas directement les comparaisons de dates
// On récupère tous les échecs et on filtre en JS
)
@@ -134,7 +135,7 @@ export async function countRappelsEnvoyesSequence(sequenceId: number): Promise<n
.where(
and(
eq(logsRappels.sequenceId, sequenceId),
eq(logsRappels.statut, "success")
eq(logsRappels.statut, "succes")
)
);
@@ -152,7 +153,7 @@ export async function getLogsRappelsWithFilters(filters: {
dateDebut?: Date;
dateFin?: Date;
sequenceId?: number;
statut?: "success" | "failed";
statut?: "succes" | "echec";
email?: string;
limit?: number;
offset?: number;
@@ -182,7 +183,7 @@ export async function getLogsRappelsWithFilters(filters: {
// Filtrer par email si nécessaire
if (filters.email) {
logs = logs.filter(log => log.emailDestinataire.toLowerCase().includes(filters.email!.toLowerCase()));
logs = logs.filter(log => log.email.toLowerCase().includes(filters.email!.toLowerCase()));
}
// Filtrer par date si nécessaire
@@ -225,15 +226,15 @@ export async function getStatsRappels(): Promise<{
const logs = await db.select().from(logsRappels);
const totalEnvoyes = logs.length;
const totalSucces = logs.filter(l => l.statut === "success").length;
const totalEchecs = logs.filter(l => l.statut === "failed").length;
const totalSucces = logs.filter(l => l.statut === "succes").length;
const totalEchecs = logs.filter(l => l.statut === "echec").length;
const tauxSucces = totalEnvoyes > 0 ? (totalSucces / totalEnvoyes) * 100 : 0;
// Compter les échecs par email
const echecsParEmail = new Map<string, number>();
logs.filter(l => l.statut === "failed").forEach(log => {
const count = echecsParEmail.get(log.emailDestinataire) || 0;
echecsParEmail.set(log.emailDestinataire, count + 1);
logs.filter(l => l.statut === "echec").forEach(log => {
const count = echecsParEmail.get(log.email) || 0;
echecsParEmail.set(log.email, count + 1);
});
const emailsProblematiques = Array.from(echecsParEmail.entries())
@@ -282,7 +283,7 @@ export async function getEvolutionEnvois(): Promise<Array<{
const dateStr = new Date(log.dateEnvoi).toISOString().split('T')[0];
const stats = parJour.get(dateStr) || { nbEnvoyes: 0, nbSucces: 0, nbEchecs: 0 };
stats.nbEnvoyes++;
if (log.statut === "success") stats.nbSucces++;
if (log.statut === "succes") stats.nbSucces++;
else stats.nbEchecs++;
parJour.set(dateStr, stats);
});
@@ -310,7 +311,7 @@ export async function getLogsAReessayer(): Promise<LogRappel[]> {
.from(logsRappels)
.where(
and(
eq(logsRappels.statut, "failed"),
eq(logsRappels.statut, "echec"),
// Limiter à 3 tentatives maximum
)
);

View File

@@ -224,10 +224,10 @@ async function processRappel(rappel: any) {
rappelId: rappel.id,
sequenceId: sequence.id,
apprenantId: apprenant.id,
emailDestinataire: apprenant.email,
typeRappel: rappel.templateType,
statut: "success",
dateSequence: toutesLesDates[0].dateDebut,
email: apprenant.email,
type: rappel.templateType,
typeEnvoi: 'automatique',
statut: "succes",
});
nbEnvoyes++;
} catch (emailError: any) {
@@ -239,11 +239,11 @@ async function processRappel(rappel: any) {
rappelId: rappel.id,
sequenceId: sequence.id,
apprenantId: apprenant.id,
emailDestinataire: apprenant.email,
typeRappel: rappel.templateType,
statut: "failed",
email: apprenant.email,
type: rappel.templateType,
typeEnvoi: 'automatique',
statut: "echec",
messageErreur: messageErreur,
dateSequence: toutesLesDates[0].dateDebut,
});
nbEchecs++;
echecs.push({ email: apprenant.email, erreur: messageErreur });

View File

@@ -1519,7 +1519,7 @@ export const appRouter = router({
dateDebut: z.string().optional(),
dateFin: z.string().optional(),
sequenceId: z.number().optional(),
statut: z.enum(['success', 'failed']).optional(),
statut: z.enum(['succes', 'echec']).optional(),
email: z.string().optional(),
limit: z.number().default(100),
offset: z.number().default(0),

View File

@@ -832,3 +832,4 @@
- [x] Modifier le DashboardLayout pour réduire tous les menus sauf "Gestion" à la connexion
- [x] Supprimer les boutons "Aperçu rappel J-7", "Envoyer rappel J-7", "Aperçu rappel J-1", "Envoyer rappel J-1" de la fenêtre de détails des séquences
- [x] Corriger le bouton "Tester le rappel" pour enregistrer les envois dans l'historique des rappels (table logsRappels)
- [x] Corriger l'enregistrement des logs de rappels : type erroné (J-1 au lieu de J+2), statut erroné (success/failed au lieu de succes/echec), email erroné (emailDestinataire au lieu de email)