espace-paie-odentas/app/api/staff/payslips/search-contracts/route.ts
odentas dd570d4509 feat: Améliorations majeures des contrats et fiches de paie
- 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
2025-11-27 20:31:11 +01:00

211 lines
7.1 KiB
TypeScript

// app/api/staff/payslips/search-contracts/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 query = searchParams.get("q");
console.log("[GET /api/staff/payslips/search-contracts] Query:", query);
if (!query || query.trim().length < 2) {
console.log("[GET /api/staff/payslips/search-contracts] Query too short, returning empty");
return NextResponse.json({ contracts: [] });
}
const searchTerm = query.trim();
const searchPattern = `%${searchTerm}%`;
console.log("[GET /api/staff/payslips/search-contracts] Search pattern:", searchPattern);
// Rechercher dans les contrats avec jointures
// On utilise une recherche textuelle simple sur les champs directs du contrat
const { data: contracts, error } = await sb
.from("cddu_contracts")
.select(`
id,
contract_number,
employee_name,
employee_id,
structure,
type_de_contrat,
start_date,
end_date,
production_name,
n_objet,
objet_spectacle,
org_id,
salaries!employee_id(
salarie,
nom,
prenom
),
organizations!org_id(
name
)
`)
.or(`contract_number.ilike.${searchPattern},employee_name.ilike.${searchPattern},structure.ilike.${searchPattern},production_name.ilike.${searchPattern},n_objet.ilike.${searchPattern},objet_spectacle.ilike.${searchPattern}`)
.order("created_at", { ascending: false })
.limit(50);
if (error) {
console.error("[GET /api/staff/payslips/search-contracts] Database error:", error);
return NextResponse.json({ error: "Database error", details: error.message }, { status: 500 });
}
console.log("[GET /api/staff/payslips/search-contracts] Initial contracts found:", contracts?.length || 0);
// Filtrer aussi par nom/prénom de salarié côté serveur si pas de résultat
let finalContracts = contracts || [];
// Si on a peu de résultats, essayer une recherche sur les salariés
if (finalContracts.length < 10) {
console.log("[GET /api/staff/payslips/search-contracts] Searching in salaries table...");
const { data: contractsBySalaries, error: salariesError } = await sb
.from("salaries")
.select(`
id,
salarie,
nom,
prenom,
cddu_contracts!employee_id(
id,
contract_number,
employee_name,
employee_id,
structure,
type_de_contrat,
start_date,
end_date,
production_name,
n_objet,
objet_spectacle,
org_id,
organizations!org_id(
name
)
)
`)
.or(`salarie.ilike.${searchPattern},nom.ilike.${searchPattern},prenom.ilike.${searchPattern}`)
.limit(50);
if (salariesError) {
console.error("[GET /api/staff/payslips/search-contracts] Salaries search error:", salariesError);
}
if (!salariesError && contractsBySalaries) {
console.log("[GET /api/staff/payslips/search-contracts] Salaries found:", contractsBySalaries.length);
// Aplatir les résultats et ajouter les infos du salarié
const additionalContracts = contractsBySalaries.flatMap(salarie => {
if (!salarie.cddu_contracts || !Array.isArray(salarie.cddu_contracts)) return [];
return salarie.cddu_contracts.map((contract: any) => ({
...contract,
salaries: {
salarie: salarie.salarie,
nom: salarie.nom,
prenom: salarie.prenom
}
}));
});
// Fusionner et dédupliquer par ID
const existingIds = new Set(finalContracts.map(c => c.id));
additionalContracts.forEach(contract => {
if (!existingIds.has(contract.id)) {
finalContracts.push(contract);
existingIds.add(contract.id);
}
});
console.log("[GET /api/staff/payslips/search-contracts] After salaries merge:", finalContracts.length);
}
}
// Filtrer aussi par nom d'organisation si peu de résultats
if (finalContracts.length < 10) {
console.log("[GET /api/staff/payslips/search-contracts] Searching in organizations table...");
const { data: contractsByOrgs, error: orgsError } = await sb
.from("organizations")
.select(`
id,
name,
cddu_contracts!org_id(
id,
contract_number,
employee_name,
employee_id,
structure,
type_de_contrat,
start_date,
end_date,
production_name,
n_objet,
objet_spectacle,
org_id,
salaries!employee_id(
salarie,
nom,
prenom
)
)
`)
.ilike('name', searchPattern)
.limit(50);
if (orgsError) {
console.error("[GET /api/staff/payslips/search-contracts] Organizations search error:", orgsError);
}
if (!orgsError && contractsByOrgs) {
console.log("[GET /api/staff/payslips/search-contracts] Organizations found:", contractsByOrgs.length);
const additionalContracts = contractsByOrgs.flatMap(org => {
if (!org.cddu_contracts || !Array.isArray(org.cddu_contracts)) return [];
return org.cddu_contracts.map((contract: any) => ({
...contract,
organizations: {
name: org.name
}
}));
});
const existingIds = new Set(finalContracts.map(c => c.id));
additionalContracts.forEach(contract => {
if (!existingIds.has(contract.id)) {
finalContracts.push(contract);
existingIds.add(contract.id);
}
});
console.log("[GET /api/staff/payslips/search-contracts] After organizations merge:", finalContracts.length);
}
}
// Limiter à 50 résultats
finalContracts = finalContracts.slice(0, 50);
console.log("[GET /api/staff/payslips/search-contracts] Final contracts count:", finalContracts.length);
return NextResponse.json({ contracts: finalContracts });
} catch (error) {
console.error("[GET /api/staff/payslips/search-contracts] Error:", error);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}