espace-paie-odentas/app/api/staff/naa/[id]/regenerate/route.ts

255 lines
8.2 KiB
TypeScript

import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
export const dynamic = "force-dynamic";
const s3Client = new S3Client({
region: process.env.AWS_REGION || "eu-west-3",
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
// Fonction de polling pour attendre la génération du PDF
async function pollDocumentStatus(
documentUrl: string,
apiKey: string,
maxAttempts = 15,
intervalMs = 2000
): Promise<{ status: string; download_url?: string }> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
console.log(`[NAA Regenerate] Poll attempt ${attempt}/${maxAttempts}`);
const res = await fetch(documentUrl, {
headers: { Authorization: `Bearer ${apiKey}` }
});
if (!res.ok) {
throw new Error(`Failed to poll document status: ${res.statusText}`);
}
const data = await res.json();
const status = data.document?.status;
console.log(`[NAA Regenerate] Poll attempt ${attempt}/${maxAttempts}, status: ${status}`);
if (status === "success" && data.document?.download_url) {
return { status, download_url: data.document.download_url };
}
if (status === "error" || status === "failure") {
throw new Error(`PDF generation failed with status: ${status}`);
}
if (attempt < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
}
return { status: "timeout" };
}
export async function POST(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const cookieStore = cookies();
const supabase = createRouteHandlerClient({ cookies: () => cookieStore });
// Vérifier l'authentification staff
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
}
const { data: staffUser } = await supabase
.from("staff_users")
.select("user_id")
.eq("user_id", user.id)
.single();
if (!staffUser) {
return NextResponse.json({ error: "Accès non autorisé" }, { status: 403 });
}
console.log(`[NAA Regenerate] Starting regeneration for NAA ID: ${params.id}`);
// Récupérer le document NAA avec l'apporteur
const { data: naaDoc, error: naaError } = await supabase
.from("naa_documents")
.select(`
*,
referrers (*)
`)
.eq("id", params.id)
.single();
if (naaError || !naaDoc) {
return NextResponse.json({ error: "NAA non trouvée" }, { status: 404 });
}
// Récupérer les prestations
const { data: prestations } = await supabase
.from("naa_prestations")
.select("*")
.eq("naa_id", params.id)
.order("created_at");
// Récupérer les line items (commissions)
const { data: lineItems } = await supabase
.from("naa_line_items")
.select("*")
.eq("naa_id", params.id)
.order("created_at");
const referrer = Array.isArray(naaDoc.referrers) ? naaDoc.referrers[0] : naaDoc.referrers;
// Préparer le payload pour PDFMonkey
const pdfMonkeyPayload = {
apporteur_address: referrer?.address || "",
apporteur_cp: referrer?.postal_code || "",
apporteur_city: referrer?.city || "",
apporteur_code: referrer?.code || naaDoc.referrer_code,
apporteur_name: referrer?.name || "",
apporteur_contact: referrer?.contact_name || "",
callsheet_date: new Date(naaDoc.callsheet_date).toLocaleDateString("fr-FR"),
limit_date: naaDoc.limit_date ? new Date(naaDoc.limit_date).toLocaleDateString("fr-FR") : "",
callsheet_number: naaDoc.naa_number,
periode: naaDoc.periode,
total_commission: naaDoc.total_commission || 0,
solde_compte_apporteur: naaDoc.solde_compte_apporteur || 0,
total_facture: naaDoc.total_facture || 0,
deposit: naaDoc.deposit || 0,
nbre_clients: naaDoc.nbre_clients || 0,
nbre_prestations: naaDoc.nbre_prestations || 0,
transfer_reference: naaDoc.transfer_reference || "",
logo_odentas: "",
lineItems: (lineItems || []).map(item => ({
id: item.client_code,
client: item.client_name,
code: item.client_code,
comactuelle: item.commission_rate,
caht: item.ca_ht,
commission: item.commission
})),
prestations: (prestations || []).map(p => ({
client: p.client_name,
code: p.client_code,
type_prestation: p.type_prestation,
quantite: p.quantite,
tarif: p.tarif,
total: p.total
}))
};
console.log("[NAA Regenerate] Calling PDFMonkey API...");
const pdfMonkeyApiKey = process.env.PDFMONKEY_API_KEY;
const pdfMonkeyUrl = "https://api.pdfmonkey.io/api/v1/documents";
const templateId = process.env.PDFMONKEY_NAA_TEMPLATE_ID || "422DA8A6-69E1-4798-B4A3-DF75D892BF2D";
const pdfMonkeyRes = await fetch(pdfMonkeyUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${pdfMonkeyApiKey}`,
},
body: JSON.stringify({
document: {
document_template_id: templateId,
status: "pending",
payload: pdfMonkeyPayload,
},
}),
});
if (!pdfMonkeyRes.ok) {
const errorText = await pdfMonkeyRes.text();
console.error("[NAA Regenerate] PDFMonkey API error:", errorText);
return NextResponse.json(
{ error: "Erreur lors de l'appel à PDFMonkey" },
{ status: 500 }
);
}
const pdfMonkeyData = await pdfMonkeyRes.json();
console.log("[NAA Regenerate] PDFMonkey response:", pdfMonkeyData);
const documentId = pdfMonkeyData.document?.id;
if (!documentId) {
return NextResponse.json({ error: "No document ID returned from PDFMonkey" }, { status: 500 });
}
const documentUrl = `${pdfMonkeyUrl}/${documentId}`;
console.log("[NAA Regenerate] Polling document status...");
const { status, download_url } = await pollDocumentStatus(documentUrl, pdfMonkeyApiKey!, 15, 2000);
if (status !== "success" || !download_url) {
return NextResponse.json({ error: "PDF generation failed or timed out" }, { status: 500 });
}
// Télécharger le PDF
console.log("[NAA Regenerate] Downloading PDF...");
const pdfRes = await fetch(download_url);
if (!pdfRes.ok) {
return NextResponse.json({ error: "Failed to download PDF" }, { status: 500 });
}
const pdfBuffer = Buffer.from(await pdfRes.arrayBuffer());
console.log("[NAA Regenerate] PDF downloaded, size:", pdfBuffer.length, "bytes");
// Uploader sur S3
console.log("[NAA Regenerate] Uploading to S3...");
const bucketName = process.env.AWS_S3_BUCKET_NAME || "odentas-docs";
const s3Key = `naa/${naaDoc.naa_number}.pdf`;
const uploadCommand = new PutObjectCommand({
Bucket: bucketName,
Key: s3Key,
Body: pdfBuffer,
ContentType: "application/pdf",
});
await s3Client.send(uploadCommand);
console.log("[NAA Regenerate] S3 upload successful");
const s3Url = `https://${bucketName}.s3.${process.env.AWS_REGION || "eu-west-3"}.amazonaws.com/${s3Key}`;
// Générer une URL présignée
const getObjectCommand = new GetObjectCommand({
Bucket: bucketName,
Key: s3Key,
});
const presignedUrl = await getSignedUrl(s3Client, getObjectCommand, { expiresIn: 3600 });
// Mettre à jour le document NAA
await supabase
.from("naa_documents")
.update({
pdf_url: s3Url,
s3_key: s3Key,
updated_at: new Date().toISOString()
})
.eq("id", params.id);
console.log("[NAA Regenerate] ✅ NAA regeneration completed successfully");
return NextResponse.json({
success: true,
pdf_url: s3Url,
presigned_url: presignedUrl
});
} catch (error: any) {
console.error("[NAA Regenerate] ❌ Error:", error);
return NextResponse.json(
{ error: error.message || "Erreur serveur" },
{ status: 500 }
);
}
}