import { eq, and, desc, sql } from "drizzle-orm"; import { getDb } from "./db"; import { formations, attestations, inscriptions, apprenants, sequences, datesFormation } from "../drizzle/schema"; /** * Récupérer la configuration d'une formation */ export async function getFormationConfig(formationId: number) { const db = await getDb(); if (!db) return null; const results = await db .select({ id: formations.id, nom: formations.nom, modeAttestation: formations.modeAttestation, modeEnvoi: formations.modeEnvoi, }) .from(formations) .where(eq(formations.id, formationId)) .limit(1); return results.length > 0 ? results[0] : null; } /** * Mettre à jour la configuration d'une formation */ export async function updateFormationConfig( formationId: number, modeAttestation: "auto" | "manuel", modeEnvoi: "auto" | "manuel" ) { const db = await getDb(); if (!db) throw new Error("Database not available"); await db .update(formations) .set({ modeAttestation, modeEnvoi, updatedAt: new Date(), }) .where(eq(formations.id, formationId)); return true; } /** * Récupérer toutes les attestations d'une formation avec les informations des apprenants */ export async function getAttestationsByFormation(formationId: number) { const db = await getDb(); if (!db) return []; const results = await db .select({ attestation: attestations, apprenant: apprenants, sequence: sequences, }) .from(attestations) .innerJoin(inscriptions, eq(attestations.inscriptionId, inscriptions.id)) .innerJoin(apprenants, eq(inscriptions.apprenantId, apprenants.id)) .innerJoin(sequences, eq(inscriptions.sequenceId, sequences.id)) .where(eq(sequences.formationId, formationId)) .orderBy(desc(attestations.createdAt)); return results; } /** * Uploader un document d'attestation pour un apprenant */ export async function uploadAttestationDocument( inscriptionId: number, documentUrl: string, documentS3Key: string, uploadedBy: number ) { const db = await getDb(); if (!db) throw new Error("Database not available"); // Vérifier si une attestation existe déjà pour cette inscription const existing = await db .select() .from(attestations) .where(eq(attestations.inscriptionId, inscriptionId)) .limit(1); if (existing.length > 0) { // Mettre à jour l'attestation existante await db .update(attestations) .set({ documentUrl, documentS3Key, uploadedBy, uploadedAt: new Date(), }) .where(eq(attestations.inscriptionId, inscriptionId)); return existing[0].id; } else { // Récupérer les informations de l'inscription const inscription = await db .select({ apprenantId: inscriptions.apprenantId, sequenceId: inscriptions.sequenceId, }) .from(inscriptions) .where(eq(inscriptions.id, inscriptionId)) .limit(1); if (inscription.length === 0) { throw new Error("Inscription not found"); } // Créer une nouvelle attestation const result = await db .insert(attestations) .values({ inscriptionId, apprenantId: inscription[0].apprenantId, sequenceId: inscription[0].sequenceId, documentUrl, documentS3Key, uploadedBy, uploadedAt: new Date(), }) .$returningId(); return result[0].id; } } /** * Envoyer une attestation par email */ export async function markAttestationAsSent(attestationId: number) { const db = await getDb(); if (!db) throw new Error("Database not available"); await db .update(attestations) .set({ emailEnvoye: true, dateEnvoiEmail: new Date(), }) .where(eq(attestations.id, attestationId)); return true; } /** * Récupérer les apprenants d'une formation avec leur statut d'attestation */ export async function getApprenantsWithAttestationStatus(formationId: number) { const db = await getDb(); if (!db) return []; // Récupérer les premières dates de chaque séquence const premieresDates = await db .select({ sequenceId: datesFormation.sequenceId, premiereDate: sql`MIN(${datesFormation.date})`, }) .from(datesFormation) .groupBy(datesFormation.sequenceId); // Créer un map pour accès rapide const datesMap = new Map(premieresDates.map(d => [d.sequenceId, d.premiereDate])); const results = await db .select({ // Apprenant apprenantId: apprenants.id, apprenantNom: apprenants.nom, apprenantPrenom: apprenants.prenom, apprenantEmail: apprenants.email, // Inscription inscriptionId: inscriptions.id, inscriptionSequenceId: inscriptions.sequenceId, // Sequence sequenceId: sequences.id, sequenceNom: sequences.nom, // Attestation attestationId: attestations.id, attestationDocumentUrl: attestations.documentUrl, attestationEmailEnvoye: attestations.emailEnvoye, attestationDateEnvoiEmail: attestations.dateEnvoiEmail, }) .from(inscriptions) .innerJoin(apprenants, eq(inscriptions.apprenantId, apprenants.id)) .innerJoin(sequences, eq(inscriptions.sequenceId, sequences.id)) .leftJoin(attestations, eq(attestations.inscriptionId, inscriptions.id)) .where( and( eq(sequences.formationId, formationId), eq(inscriptions.statut, "confirmee") ) ) .orderBy(apprenants.nom, apprenants.prenom); // Restructurer les résultats au format attendu return results.map(r => ({ apprenant: { id: r.apprenantId, nom: r.apprenantNom, prenom: r.apprenantPrenom, email: r.apprenantEmail, }, inscription: { id: r.inscriptionId, sequenceId: r.inscriptionSequenceId, }, sequence: { id: r.sequenceId, nom: r.sequenceNom, premiereDate: datesMap.get(r.sequenceId) || null, }, attestation: r.attestationId ? { id: r.attestationId, documentUrl: r.attestationDocumentUrl, emailEnvoye: r.attestationEmailEnvoye, dateEnvoiEmail: r.attestationDateEnvoiEmail, } : null, })); } /** * Supprimer un document d'attestation */ export async function deleteAttestationDocument(attestationId: number) { const db = await getDb(); if (!db) throw new Error("Database not available"); await db .update(attestations) .set({ documentUrl: null, documentS3Key: null, uploadedBy: null, uploadedAt: null, }) .where(eq(attestations.id, attestationId)); return true; }