espace-paie-odentas/app/api/staff/facturation/bulk-update-payment-date/route.ts
odentas 897af4b23a feat: Ajout fonctionnalités virements, facturation, signatures et emails
- Ajout sous-header total net à payer sur page virements-salaires
- Migration transfer_done_at pour tracking précis des virements
- Nouvelle page saisie tableau pour création factures en masse
- APIs bulk pour mise à jour dates signature et jours technicien
- API demande mandat SEPA avec email template
- Webhook DocuSeal pour signature contrats (mode TEST)
- Composants modaux détails et vérification PDF fiches de paie
- Upload/suppression/remplacement PDFs dans PayslipsGrid
- Amélioration affichage colonnes et filtres grilles contrats/paies
- Template email mandat SEPA avec sous-texte CTA
- APIs bulk facturation (création, update statut/date paiement)
- API clients sans facture pour période donnée
- Corrections calculs dates et montants avec auto-remplissage
2025-11-02 23:26:19 +01:00

105 lines
3.5 KiB
TypeScript

// app/api/staff/facturation/bulk-update-payment-date/route.ts
import { NextResponse } from "next/server";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
export const dynamic = 'force-dynamic';
export const revalidate = 0;
export const runtime = 'nodejs';
async function isStaffUser(supabase: any, userId: string): Promise<boolean> {
try {
const { data: staffRow } = await supabase
.from('staff_users')
.select('is_staff')
.eq('user_id', userId)
.maybeSingle();
return !!staffRow?.is_staff;
} catch {
return false;
}
}
// POST - Mise à jour en masse des dates de paiement
export async function POST(req: Request) {
try {
const body = await req.json();
const { invoiceIds, paymentDate } = body;
// Validation des données
if (!Array.isArray(invoiceIds) || invoiceIds.length === 0) {
return NextResponse.json({ error: 'invalid_invoice_ids' }, { status: 400 });
}
if (!paymentDate || typeof paymentDate !== 'string') {
return NextResponse.json({ error: 'invalid_payment_date' }, { status: 400 });
}
// Validation du format de date
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(paymentDate)) {
return NextResponse.json({ error: 'invalid_date_format' }, { status: 400 });
}
const supabase = createRouteHandlerClient({ cookies });
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
}
// Vérifier que l'utilisateur est staff
const isStaff = await isStaffUser(supabase, session.user.id);
if (!isStaff) {
return NextResponse.json({ error: 'forbidden', message: 'Staff access required' }, { status: 403 });
}
// Limiter le nombre d'IDs pour éviter les abus
if (invoiceIds.length > 100) {
return NextResponse.json({ error: 'too_many_invoices', message: 'Maximum 100 invoices per batch' }, { status: 400 });
}
// Vérifier que toutes les factures existent
const { data: existingInvoices, error: fetchError } = await supabase
.from('invoices')
.select('id')
.in('id', invoiceIds);
if (fetchError) {
console.error('Erreur lors de la récupération des factures:', fetchError);
return NextResponse.json({ error: 'database_error' }, { status: 500 });
}
if (!existingInvoices || existingInvoices.length === 0) {
return NextResponse.json({ error: 'no_invoices_found' }, { status: 404 });
}
// Mettre à jour les dates de paiement
const { data: updatedInvoices, error: updateError } = await supabase
.from('invoices')
.update({
payment_date: paymentDate,
updated_at: new Date().toISOString()
})
.in('id', invoiceIds)
.select();
if (updateError) {
console.error('Erreur lors de la mise à jour des dates de paiement:', updateError);
return NextResponse.json({ error: 'update_failed', details: updateError.message }, { status: 500 });
}
return NextResponse.json({
success: true,
updated: updatedInvoices?.length || 0,
message: `${updatedInvoices?.length || 0} facture(s) mise(s) à jour avec succès`
});
} catch (error: any) {
console.error('Erreur dans bulk-update-payment-date:', error);
return NextResponse.json(
{ error: 'internal_server_error', details: error.message },
{ status: 500 }
);
}
}