'use client'; import React, { useState, useEffect, useRef } from 'react'; import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; import { Button } from 'primereact/button'; import { InputText } from 'primereact/inputtext'; import { Card } from 'primereact/card'; import { Toast } from 'primereact/toast'; import { Toolbar } from 'primereact/toolbar'; import { Tag } from 'primereact/tag'; import { Dialog } from 'primereact/dialog'; import { Calendar } from 'primereact/calendar'; import { InputTextarea } from 'primereact/inputtextarea'; import { Dropdown } from 'primereact/dropdown'; import { Chip } from 'primereact/chip'; import { factureService } from '../../../../services/api'; import { formatDate, formatCurrency } from '../../../../utils/formatters'; import type { Facture } from '../../../../types/btp'; import { ActionButtonGroup, ViewButton, EditButton, DeleteButton, ActionButton } from '../../../../components/ui/ActionButton'; const FacturesImpayeesPage = () => { const [factures, setFactures] = useState([]); const [loading, setLoading] = useState(true); const [globalFilter, setGlobalFilter] = useState(''); const [selectedFactures, setSelectedFactures] = useState([]); const [actionDialog, setActionDialog] = useState(false); const [selectedFacture, setSelectedFacture] = useState(null); const [actionType, setActionType] = useState<'payment' | 'reminder' | 'schedule'>('payment'); const [paymentData, setPaymentData] = useState({ datePaiement: new Date(), montantPaye: 0, modePaiement: '', notes: '' }); const [reminderData, setReminderData] = useState({ type: 'EMAIL', message: '' }); const toast = useRef(null); const dt = useRef>(null); const paymentMethods = [ { label: 'Virement bancaire', value: 'VIREMENT' }, { label: 'Chèque', value: 'CHEQUE' }, { label: 'Espèces', value: 'ESPECES' }, { label: 'Carte bancaire', value: 'CB' }, { label: 'Prélèvement', value: 'PRELEVEMENT' } ]; const reminderTypes = [ { label: 'Email', value: 'EMAIL' }, { label: 'Courrier', value: 'COURRIER' }, { label: 'Téléphone', value: 'TELEPHONE' }, { label: 'SMS', value: 'SMS' } ]; useEffect(() => { loadFactures(); }, []); const loadFactures = async () => { try { setLoading(true); const data = await factureService.getAll(); // Filtrer les factures impayées (émises ou envoyées mais pas payées) const facturesImpayees = data.filter(facture => (facture.statut === 'EMISE' || facture.statut === 'ENVOYEE') && !facture.datePaiement ); setFactures(facturesImpayees); } catch (error) { console.error('Erreur lors du chargement des factures:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les factures impayées', life: 3000 }); } finally { setLoading(false); } }; const getDaysOverdue = (dateEcheance: string | Date) => { const today = new Date(); const dueDate = new Date(dateEcheance); const diffTime = today.getTime() - dueDate.getTime(); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return Math.max(0, diffDays); }; const isOverdue = (dateEcheance: string | Date) => { return getDaysOverdue(dateEcheance) > 0; }; const getUrgencyLevel = (dateEcheance: string | Date) => { const days = getDaysOverdue(dateEcheance); if (days === 0) return { level: 'À ÉCHÉANCE', color: 'orange', severity: 'warning' as const }; if (days <= 7) return { level: 'LÉGER RETARD', color: 'orange', severity: 'warning' as const }; if (days <= 30) return { level: 'RETARD', color: 'red', severity: 'danger' as const }; return { level: 'RETARD IMPORTANT', color: 'darkred', severity: 'danger' as const }; }; const recordPayment = (facture: Facture) => { setSelectedFacture(facture); setActionType('payment'); setPaymentData({ datePaiement: new Date(), montantPaye: facture.montantTTC || 0, modePaiement: '', notes: '' }); setActionDialog(true); }; const sendReminder = (facture: Facture) => { setSelectedFacture(facture); setActionType('reminder'); setReminderData({ type: 'EMAIL', message: `Madame, Monsieur,\n\nNous vous rappelons que la facture ${facture.numero} d'un montant de ${formatCurrency(facture.montantTTC || 0)} arrive à échéance le ${formatDate(facture.dateEcheance)}.\n\nMerci de bien vouloir procéder au règlement dans les plus brefs délais.\n\nCordialement` }); setActionDialog(true); }; const schedulePayment = (facture: Facture) => { setSelectedFacture(facture); setActionType('schedule'); setActionDialog(true); }; const handleAction = async () => { if (!selectedFacture) return; try { let message = ''; switch (actionType) { case 'payment': // TODO: Implémenter l'enregistrement de paiement console.log('Enregistrement de paiement:', { facture: selectedFacture, paymentData }); // Retirer de la liste des impayées setFactures(prev => prev.filter(f => f.id !== selectedFacture.id)); message = 'Paiement enregistré avec succès'; break; case 'reminder': // TODO: Implémenter l'envoi de relance console.log('Envoi de relance:', { facture: selectedFacture, reminderData }); message = 'Relance envoyée au client'; break; case 'schedule': // TODO: Implémenter la planification de paiement console.log('Planification de paiement:', selectedFacture); message = 'Échéancier de paiement programmé'; break; } setActionDialog(false); toast.current?.show({ severity: 'success', summary: 'Succès', detail: `${message} (simulation)`, life: 3000 }); } catch (error) { console.error('Erreur lors de l\'action:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible d\'effectuer l\'action', life: 3000 }); } }; const exportCSV = () => { dt.current?.exportCSV(); }; const bulkReminder = async () => { if (selectedFactures.length === 0) { toast.current?.show({ severity: 'warn', summary: 'Attention', detail: 'Veuillez sélectionner au moins une facture', life: 3000 }); return; } // Simulation d'envoi de relances en lot console.log('Envoi de relances en lot à', selectedFactures.length, 'factures'); setSelectedFactures([]); toast.current?.show({ severity: 'success', summary: 'Succès', detail: `${selectedFactures.length} relance(s) envoyée(s) (simulation)`, life: 3000 }); }; const generateOverdueReport = () => { const totalOverdue = factures.reduce((sum, f) => sum + (f.montantTTC || 0), 0); const severelyOverdue = factures.filter(f => getDaysOverdue(f.dateEcheance) > 30); const recentOverdue = factures.filter(f => getDaysOverdue(f.dateEcheance) <= 7); const report = ` === RAPPORT FACTURES IMPAYÉES === Date du rapport: ${new Date().toLocaleDateString('fr-FR')} STATISTIQUES GÉNÉRALES: - Nombre total de factures impayées: ${factures.length} - Montant total impayé: ${formatCurrency(totalOverdue)} - Montant moyen par facture: ${formatCurrency(totalOverdue / (factures.length || 1))} RÉPARTITION PAR GRAVITÉ: - Retard léger (≤7 jours): ${recentOverdue.length} factures, ${formatCurrency(recentOverdue.reduce((sum, f) => sum + (f.montantTTC || 0), 0))} - Retard important (>30 jours): ${severelyOverdue.length} factures, ${formatCurrency(severelyOverdue.reduce((sum, f) => sum + (f.montantTTC || 0), 0))} FACTURES EN RETARD CRITIQUE (>30 jours): ${severelyOverdue.map(f => ` - ${f.numero} - ${f.objet} Client: ${f.client ? `${f.client.prenom} ${f.client.nom}` : 'N/A'} Montant: ${formatCurrency(f.montantTTC || 0)} Retard: ${getDaysOverdue(f.dateEcheance)} jours Échéance: ${formatDate(f.dateEcheance)} `).join('')} ANALYSE PAR CLIENT: ${getClientOverdueAnalysis()} RECOMMANDATIONS: - Relancer immédiatement les factures en retard critique - Mettre en place des échéanciers pour les gros montants - Examiner les conditions de paiement accordées - Considérer un contentieux pour les retards >60 jours `; const blob = new Blob([report], { type: 'text/plain;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `rapport_factures_impayees_${new Date().toISOString().split('T')[0]}.txt`; link.click(); toast.current?.show({ severity: 'success', summary: 'Rapport généré', detail: 'Le rapport d\'impayés a été téléchargé', life: 3000 }); }; const getClientOverdueAnalysis = () => { const clientStats = {}; factures.forEach(f => { if (f.client) { const clientKey = `${f.client.prenom} ${f.client.nom}`; if (!clientStats[clientKey]) { clientStats[clientKey] = { count: 0, value: 0, maxDays: 0 }; } clientStats[clientKey].count++; clientStats[clientKey].value += f.montantTTC || 0; clientStats[clientKey].maxDays = Math.max(clientStats[clientKey].maxDays, getDaysOverdue(f.dateEcheance)); } }); return Object.entries(clientStats) .sort((a: [string, any], b: [string, any]) => b[1].value - a[1].value) .slice(0, 5) .map(([client, data]: [string, any]) => `- ${client}: ${data.count} factures, ${formatCurrency(data.value)}, retard max: ${data.maxDays} jours`) .join('\n'); }; const leftToolbarTemplate = () => { return (
Factures impayées ({factures.length})
sum + (f.montantTTC || 0), 0))}`} className="bg-red-100 text-red-800" />
); }; const rightToolbarTemplate = () => { return (