espace-paie-odentas/app/api/staff/virements-salaires/[id]/notify-client/route.ts

198 lines
6.4 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { sendUniversalEmailV2 } from "@/lib/emailTemplateService";
export async function POST(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const supabase = createRouteHandlerClient({ cookies });
// 1) Authentification
const {
data: { session },
error: sessionError,
} = await supabase.auth.getSession();
if (sessionError || !session) {
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
}
const user = session.user;
// 2) Vérifier que l'utilisateur est staff
const { data: staffData } = await supabase
.from("staff_users")
.select("is_staff")
.eq("user_id", user.id)
.maybeSingle();
const isStaff = staffData?.is_staff || false;
if (!isStaff) {
return NextResponse.json(
{ error: "Accès refusé : réservé au staff" },
{ status: 403 }
);
}
// 3) Récupérer le virement de salaire
const { data: salaryTransfer, error: stError } = await supabase
.from("salary_transfers")
.select("*")
.eq("id", params.id)
.single();
if (stError || !salaryTransfer) {
return NextResponse.json(
{ error: "Virement de salaire introuvable" },
{ status: 404 }
);
}
// 4) Récupérer l'organisation et ses détails
const { data: organization, error: orgError } = await supabase
.from("organizations")
.select("*")
.eq("id", salaryTransfer.org_id)
.single();
if (orgError || !organization) {
return NextResponse.json(
{ error: "Organisation introuvable" },
{ status: 404 }
);
}
const { data: orgDetails, error: orgDetailsError } = await supabase
.from("organization_details")
.select("*")
.eq("org_id", salaryTransfer.org_id)
.single();
if (orgDetailsError || !orgDetails) {
return NextResponse.json(
{ error: "Détails de l'organisation introuvables" },
{ status: 404 }
);
}
// 5) Vérifier les emails de notification
const cleanEmail = (email: string | null | undefined): string | undefined => {
if (!email) return undefined;
// Supprimer tous les espaces, retours à la ligne, tabulations, etc.
const cleaned = email.replace(/\s+/g, '').trim();
if (cleaned.length === 0) return undefined;
return cleaned;
};
const isValidEmail = (email: string | null | undefined): boolean => {
if (!email) return false;
const cleaned = cleanEmail(email);
if (!cleaned) return false;
// Vérifier le format basique d'un email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(cleaned);
};
const emailNotifs = cleanEmail(orgDetails.email_notifs);
const emailNotifsCC = cleanEmail(orgDetails.email_notifs_cc);
console.log("[notify-client] Email brut:", JSON.stringify(orgDetails.email_notifs));
console.log("[notify-client] Email nettoyé:", emailNotifs);
console.log("[notify-client] CC brut:", JSON.stringify(orgDetails.email_notifs_cc));
console.log("[notify-client] CC nettoyé:", emailNotifsCC);
if (!emailNotifs || !isValidEmail(emailNotifs)) {
return NextResponse.json(
{ error: "Email de notification non configuré ou invalide pour cette organisation" },
{ status: 400 }
);
}
// Valider l'email CC s'il existe (rejeter les chaînes vides ou invalides)
const validatedCcEmail = (emailNotifsCC && isValidEmail(emailNotifsCC)) ? emailNotifsCC : undefined;
// 6) Récupérer le prénom du contact depuis organization_details
const firstName = orgDetails.prenom_contact || "Cher client";
// 7) Formater les données pour l'email
const formatDate = (dateStr: string | null) => {
if (!dateStr) return "—";
const date = new Date(dateStr);
return date.toLocaleDateString("fr-FR", {
day: "2-digit",
month: "long",
year: "numeric"
});
};
const formatAmount = (amount: number | null) => {
if (amount === null || amount === undefined) return "0,00 €";
return amount.toLocaleString("fr-FR", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}) + " €";
};
// Construire la référence du virement
const codeEmployeur = orgDetails.code_employeur || "UNKNOWN";
const numAppel = salaryTransfer.num_appel || "00000";
const transferReference = `AV-${codeEmployeur}-${numAppel}`;
// 8) Préparer les données du template
const templateData = {
firstName,
organizationName: organization.name,
employerCode: codeEmployeur,
handlerName: "Renaud BREVIERE-ABRAHAM",
totalAmount: formatAmount(salaryTransfer.total_net),
periodLabel: salaryTransfer.period_label || formatDate(salaryTransfer.period_month),
deadline: formatDate(salaryTransfer.deadline),
transferReference,
showBankInfo: 'true', // Pour afficher la carte bancaire
};
// 9) Envoyer l'email via le système universel V2
console.log("[notify-client] Envoi de l'email à:", emailNotifs);
console.log("[notify-client] CC:", validatedCcEmail || "Aucun");
await sendUniversalEmailV2({
type: 'salary-transfer-notification',
toEmail: emailNotifs!,
ccEmail: validatedCcEmail,
data: templateData
});
// 10) Mettre à jour le virement pour indiquer que la notification a été envoyée
const { error: updateError } = await supabase
.from("salary_transfers")
.update({
notification_sent: true,
notification_ok: true,
updated_at: new Date().toISOString()
})
.eq("id", params.id);
if (updateError) {
console.error("[notify-client] Erreur lors de la mise à jour:", updateError);
// On ne retourne pas d'erreur car l'email a été envoyé
}
return NextResponse.json({
success: true,
message: "Notification envoyée avec succès",
emailSentTo: emailNotifs,
emailCc: validatedCcEmail || null
});
} catch (error: any) {
console.error("[notify-client] Erreur:", error);
return NextResponse.json(
{ error: "Erreur lors de l'envoi de la notification", details: error.message },
{ status: 500 }
);
}
}