- Ajout détails cachets/répétitions/heures au modal ContractDetails - Card verte avec validation quand tous les contrats ont une fiche de paie - Système complet de création de fiches de paie avec recherche et vérification - Modal liste des contrats sans paie avec création directe - Amélioration édition dates dans PayslipDetailsModal - Optimisation recherche contrats (ordre des filtres) - Augmentation limite pagination ContractsGrid à 200 - Ajout logs debug génération PDF logo - Script SQL vérification cohérence structure/organisation
143 lines
4.7 KiB
TypeScript
143 lines
4.7 KiB
TypeScript
// app/api/staff/payslips/missing-stats/route.ts
|
|
import { createSbServer } from "@/lib/supabaseServer";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const sb = createSbServer();
|
|
|
|
// Vérifier l'authentification
|
|
const { data: { user } } = await sb.auth.getUser();
|
|
if (!user) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
// Vérifier que c'est un staff
|
|
const { data: me } = await sb
|
|
.from("staff_users")
|
|
.select("is_staff")
|
|
.eq("user_id", user.id)
|
|
.maybeSingle();
|
|
|
|
if (!me?.is_staff) {
|
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
}
|
|
|
|
const searchParams = request.nextUrl.searchParams;
|
|
const structure = searchParams.get("structure");
|
|
const periodFrom = searchParams.get("period_from");
|
|
const periodTo = searchParams.get("period_to");
|
|
|
|
if (!structure || !periodFrom || !periodTo) {
|
|
return NextResponse.json({
|
|
missing_count: 0,
|
|
message: "Filtres incomplets"
|
|
});
|
|
}
|
|
|
|
console.log("[GET /api/staff/payslips/missing-stats] Params:", {
|
|
structure,
|
|
periodFrom,
|
|
periodTo
|
|
});
|
|
|
|
// Récupérer tous les contrats de cette structure dans cette période
|
|
const { data: contracts, error: contractsError } = await sb
|
|
.from("cddu_contracts")
|
|
.select(`
|
|
id,
|
|
start_date,
|
|
end_date,
|
|
type_de_contrat,
|
|
n_objet,
|
|
reference,
|
|
employee_name,
|
|
production_name,
|
|
profession
|
|
`)
|
|
.eq("structure", structure)
|
|
.lte("start_date", periodTo)
|
|
.gte("end_date", periodFrom);
|
|
|
|
if (contractsError) {
|
|
console.error("[GET /api/staff/payslips/missing-stats] Contracts error:", contractsError);
|
|
return NextResponse.json({ error: "Database error" }, { status: 500 });
|
|
}
|
|
|
|
if (!contracts || contracts.length === 0) {
|
|
return NextResponse.json({ missing_count: 0 });
|
|
}
|
|
|
|
console.log("[GET /api/staff/payslips/missing-stats] Found contracts:", contracts.length);
|
|
|
|
// Pour chaque contrat, vérifier s'il a des paies
|
|
const contractIds = contracts.map(c => c.id);
|
|
const { data: payslips, error: payslipsError } = await sb
|
|
.from("payslips")
|
|
.select("contract_id, period_month")
|
|
.in("contract_id", contractIds);
|
|
|
|
if (payslipsError) {
|
|
console.error("[GET /api/staff/payslips/missing-stats] Payslips error:", payslipsError);
|
|
return NextResponse.json({ error: "Database error" }, { status: 500 });
|
|
}
|
|
|
|
console.log("[GET /api/staff/payslips/missing-stats] Found payslips:", payslips?.length || 0);
|
|
|
|
// Créer un map des contrats avec paies
|
|
const contractsWithPayslips = new Set<string>();
|
|
if (payslips) {
|
|
payslips.forEach(p => contractsWithPayslips.add(p.contract_id));
|
|
}
|
|
|
|
// Collecter les contrats sans paie avec leurs informations
|
|
const missingContracts: Array<{
|
|
id: string;
|
|
start_date: string;
|
|
end_date: string;
|
|
type_de_contrat: string;
|
|
}> = [];
|
|
|
|
contracts.forEach(contract => {
|
|
const startDate = new Date(contract.start_date);
|
|
const endDate = new Date(contract.end_date);
|
|
const isMultiMonth = startDate.getMonth() !== endDate.getMonth() ||
|
|
startDate.getFullYear() !== endDate.getFullYear();
|
|
|
|
if (!isMultiMonth) {
|
|
// Mono-mois : pas de paie du tout
|
|
if (!contractsWithPayslips.has(contract.id)) {
|
|
missingContracts.push(contract);
|
|
}
|
|
} else {
|
|
// Multi-mois ou RG : vérifier s'il y a une paie dans la période demandée
|
|
const contractPayslips = payslips?.filter(p => p.contract_id === contract.id) || [];
|
|
|
|
// Vérifier si au moins une paie couvre la période
|
|
const hasPeriodPayslip = contractPayslips.some(p => {
|
|
const payslipMonth = new Date(p.period_month);
|
|
const requestedFrom = new Date(periodFrom);
|
|
const requestedTo = new Date(periodTo);
|
|
|
|
// Vérifier si le mois de paie est dans la période demandée
|
|
return payslipMonth >= requestedFrom && payslipMonth <= requestedTo;
|
|
});
|
|
|
|
if (!hasPeriodPayslip) {
|
|
missingContracts.push(contract);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log("[GET /api/staff/payslips/missing-stats] Missing count:", missingContracts.length);
|
|
|
|
return NextResponse.json({
|
|
missing_count: missingContracts.length,
|
|
total_contracts: contracts.length,
|
|
missing_contracts: missingContracts
|
|
});
|
|
} catch (error) {
|
|
console.error("[GET /api/staff/payslips/missing-stats] Error:", error);
|
|
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
|
}
|
|
}
|