src/app/api/panier/convertir/route.ts

route·app·6.6 KB · 186 lignes· Voir l'itinéraire
Annotation non disponible

Lance npm run annotate (nécessite ANTHROPIC_API_KEY dans .env.local) pour générer une annotation française par Claude Haiku 4.5.

1 export

POST

Code source· typescript

import { NextRequest, NextResponse } from "next/server";
import { envoyerConfirmationCommande, envoyerNotificationAdmin } from "@/lib/email";
import { sendPushToTenantAdmin } from "@/lib/push";
import { prisma } from "@/lib/prisma/client";
import { getSession } from "@/lib/auth/session";
import { getTenant } from "@/lib/auth/get-tenant";

export async function POST(req: NextRequest) {
  try {
    const tenant = await getTenant();
    if (!tenant) return NextResponse.json({ error: "Tenant introuvable" }, { status: 404 });

    const session = await getSession();
    if (!session || session.role !== "CLIENT") {
      return NextResponse.json({ error: "Connexion requise pour commander" }, { status: 401 });
    }

    const panier = await prisma.panier.findFirst({
      where: { tenantId: tenant.id, clientId: session.userId, statut: "ACTIF" },
      include: { lignes: { include: { variante: true } } },
    });

    if (!panier || panier.lignes.length === 0) {
      return NextResponse.json({ error: "Panier vide" }, { status: 400 });
    }

    // Vérifier que le client existe dans la table clients (tenant-side)
    const client = await prisma.client.findUnique({
      where: { tenantId_email: { tenantId: tenant.id, email: "" } },
    });

    const total = panier.lignes.reduce(
      (acc, l) => acc + l.prixSnapshot * l.quantite,
      0
    );

    // Créer la commande + lignes en transaction
    const commande = await prisma.$transaction(async (tx) => {
      // Récupérer ou créer le client tenant
      const clientAccount = await tx.clientAccount.findUnique({
        where: { id: session.userId },
      });
      if (!clientAccount) throw new Error("Compte client introuvable");

      let clientTenant = await tx.client.findFirst({
        where: {
          tenantId: tenant.id,
          email: clientAccount.email ?? clientAccount.phone ?? "",
        },
      });

      if (!clientTenant) {
        clientTenant = await tx.client.create({
          data: {
            tenantId: tenant.id,
            email: clientAccount.email ?? clientAccount.phone ?? "",
            nom: clientAccount.name ?? "",
            telephone: clientAccount.phone ?? "",
          },
        });
      }

      const nouvelleCommande = await tx.commande.create({
        data: {
          tenantId: tenant.id,
          clientId: clientTenant.id,
          total,
          devise: panier.lignes[0]?.devise ?? "FCFA",
          statut: "EN_ATTENTE",
          lignes: {
            create: panier.lignes.map((l) => ({
              produitId: l.produitId,
              varianteId: l.varianteId ?? null,
              quantite: l.quantite,
              prixUnitaire: l.prixSnapshot,
            })),
          },
        },
      });

      // Decrementer le stock des produits commandes
      for (const ligne of panier.lignes) {
        await tx.produit.update({
          where: { id: ligne.produitId },
          data: { stock: { decrement: ligne.quantite } },
        })
        // Marquer indisponible si stock <= 0 apres decrement
        const produitMaj = await tx.produit.findUnique({
          where: { id: ligne.produitId },
          select: { stock: true },
        })
        if (produitMaj && produitMaj.stock <= 0) {
          await tx.produit.update({
            where: { id: ligne.produitId },
            data: { disponible: false, stock: 0 },
          })
        }
      }

      // Marquer le panier comme converti
      await tx.panier.update({
        where: { id: panier.id },
        data: { statut: "CONVERTI" },
      });

      return nouvelleCommande;
    });

    // Envoyer les emails de notification
    try {
      const [clientAccount, adminUser] = await Promise.all([
        prisma.clientAccount.findUnique({ where: { id: session.userId }, select: { name: true, prenom: true, email: true, phone: true } }),
        prisma.user.findFirst({ where: { tenantId: tenant.id, role: "TENANT_ADMIN" }, select: { email: true } }),
      ]);

      const lignesEmail = panier.lignes.map((l) => ({
        nom: l.produitId, quantite: l.quantite, prixUnitaire: l.prixSnapshot, devise: l.devise,
      }));

      // Récupérer les noms des produits
      const produits = await prisma.produit.findMany({
        where: { id: { in: panier.lignes.map((l) => l.produitId) } },
        select: { id: true, nom: true },
      });
      const lignesEmailAvecNoms = panier.lignes.map((l) => {
        const nomProduit = produits.find((p) => p.id === l.produitId)?.nom ?? "Produit";
        const variante = l.variante;
        const nomVariante = variante
          ? Object.entries(variante.attributs as Record<string, string>).map(([k, v]) => `${k}: ${v}`).join(" · ")
          : null;
        return {
          nom: nomVariante ? `${nomProduit} — ${nomVariante}` : nomProduit,
          quantite: l.quantite,
          prixUnitaire: l.prixSnapshot,
          devise: l.devise,
        };
      });

      const emailData = {
        tenantNom: tenant.nom,
        clientNom: (clientAccount?.prenom ?? "") + " " + (clientAccount?.name ?? ""),
        clientEmail: clientAccount?.email ?? clientAccount?.phone ?? "",
        commandeId: commande.id,
        lignes: lignesEmailAvecNoms,
        total: commande.total,
        devise: commande.devise,
      };

      const emailPromises = [];
      if (emailData.clientEmail && emailData.clientEmail.includes("@")) {
        emailPromises.push(envoyerConfirmationCommande(emailData));
      }
      if (adminUser?.email) {
        emailPromises.push(envoyerNotificationAdmin({ ...emailData, adminEmail: adminUser.email }));
      }
      await Promise.allSettled(emailPromises);
    } catch (emailError) {
      console.error("email notification error:", emailError);
    }

    // Push notification au gérant TENANT_ADMIN
    try {
      const totalFmt = `${commande.total.toLocaleString("fr-FR")} ${commande.devise}`;
      const c = await prisma.clientAccount.findUnique({
        where: { id: session.userId },
        select: { prenom: true, name: true, email: true, phone: true },
      });
      const clientNomShort =
        (c?.prenom && c?.name) ? `${c.prenom} ${c.name}` :
        c?.prenom ?? c?.name ?? c?.email ?? c?.phone ?? "Client";
      await sendPushToTenantAdmin(tenant.id, {
        title: "Nouvelle commande",
        body: `${clientNomShort} · ${totalFmt}`,
        data: { type: "commande", commandeId: commande.id, tenantId: tenant.id },
      });
    } catch (pushError) {
      console.error("push notification error:", pushError);
    }

    return NextResponse.json({ success: true, commandeId: commande.id });
  } catch (error) {
    console.error("panier convertir error:", error);
    return NextResponse.json({ error: "Erreur serveur" }, { status: 500 });
  }
}