diff --git a/client/src/pages/BLReconciliation.tsx b/client/src/pages/BLReconciliation.tsx index c2243a9..5b1d3be 100644 --- a/client/src/pages/BLReconciliation.tsx +++ b/client/src/pages/BLReconciliation.tsx @@ -4,13 +4,15 @@ import { safeFormat } from "@/lib/dateUtils"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useToast } from "@/hooks/use-toast"; import { apiRequest } from "@/lib/queryClient"; import { useStore } from "@/components/Layout"; import { useAuthUnified } from "@/hooks/useAuthUnified"; import { usePermissions } from "@shared/permissions"; -import { Search, Edit, FileText, Settings, Eye, AlertTriangle, X, Check, Trash2, Ban } from "lucide-react"; +import { Pagination, usePagination } from "@/components/ui/pagination"; +import { Search, Edit, FileText, Settings, Eye, AlertTriangle, X, Check, Trash2, Ban, Filter } from "lucide-react"; import ReconciliationModal from "@/components/modals/ReconciliationModal"; export default function BLReconciliation() { @@ -73,50 +75,52 @@ export default function BLReconciliation() { return filtered.sort((a: any, b: any) => new Date(b.deliveredDate || b.updatedAt).getTime() - new Date(a.deliveredDate || a.updatedAt).getTime()); }, + enabled: !!user }); - // Séparer les livraisons selon le mode de rapprochement du fournisseur + // Séparer les livraisons par mode de rapprochement const manualReconciliationDeliveries = deliveriesWithBL.filter((delivery: any) => { - const supplier = suppliers.find((s: any) => s.id === delivery.supplierId); - return !supplier?.automaticReconciliation; + const supplier = suppliers.find(s => s.id === delivery.supplierId); + return !supplier?.isAutoReconciliation; }); const automaticReconciliationDeliveries = deliveriesWithBL.filter((delivery: any) => { - const supplier = suppliers.find((s: any) => s.id === delivery.supplierId); - return supplier?.automaticReconciliation; + const supplier = suppliers.find(s => s.id === delivery.supplierId); + return supplier?.isAutoReconciliation; }); - // Fonction pour ouvrir le modal d'édition + // Fonctions de gestion const handleOpenModal = (delivery: any) => { setSelectedDelivery(delivery); setIsModalOpen(true); }; - // Fonction pour valider un rapprochement rapidement (avec validation des données) + const handleCloseModal = () => { + setSelectedDelivery(null); + setIsModalOpen(false); + }; + + const handleSaveReconciliation = async (data: any) => { + try { + await apiRequest(`/api/deliveries/${selectedDelivery.id}`, "PUT", data); + + toast({ + title: "Succès", + description: "Données de rapprochement mises à jour", + }); + + queryClient.invalidateQueries({ queryKey: ['/api/deliveries/bl'] }); + handleCloseModal(); + } catch (error) { + toast({ + title: "Erreur", + description: "Impossible de mettre à jour les données", + variant: "destructive", + }); + } + }; + const handleQuickValidate = async (delivery: any) => { - if (!permissions.canValidate('reconciliation')) { - toast({ - title: "Accès refusé", - description: "Vous n'avez pas les permissions pour valider les rapprochements", - variant: "destructive", - }); - return; - } - - // Vérifier que les données minimales sont présentes - if (!delivery.blNumber || delivery.blNumber.trim() === '') { - toast({ - title: "Données manquantes", - description: "Le numéro de BL est requis. Cliquez sur 'Voir les détails' pour le renseigner.", - variant: "destructive", - }); - return; - } - - if (!window.confirm("Êtes-vous sûr de vouloir valider ce rapprochement ?")) { - return; - } - try { await apiRequest(`/api/deliveries/${delivery.id}`, "PUT", { reconciled: true, @@ -138,7 +142,6 @@ export default function BLReconciliation() { } }; - // Fonction pour dévalider un rapprochement (admins uniquement) const handleDevalidateReconciliation = async (deliveryId: number) => { if (user?.role !== 'admin') { toast({ @@ -174,7 +177,6 @@ export default function BLReconciliation() { } }; - // Fonction pour supprimer une livraison (admins uniquement) const handleDeleteDelivery = async (deliveryId: number) => { if (!permissions.canDelete('reconciliation')) { toast({ @@ -207,42 +209,6 @@ export default function BLReconciliation() { } }; - // Fonction pour dévalider un rapprochement automatique (admins uniquement) - const handleDevalidateAutoReconciliation = async (deliveryId: number) => { - if (user?.role !== 'admin') { - toast({ - title: "Accès refusé", - description: "Seuls les administrateurs peuvent dévalider les rapprochements automatiques", - variant: "destructive", - }); - return; - } - - if (!window.confirm("Êtes-vous sûr de vouloir dévalider ce rapprochement automatique ?")) { - return; - } - - try { - await apiRequest(`/api/deliveries/${deliveryId}`, "PUT", { - reconciled: false, - validatedAt: null - }); - - toast({ - title: "Succès", - description: "Rapprochement automatique dévalidé avec succès", - }); - - queryClient.invalidateQueries({ queryKey: ['/api/deliveries/bl'] }); - } catch (error) { - toast({ - title: "Erreur", - description: "Impossible de dévalider le rapprochement", - variant: "destructive", - }); - } - }; - // Filtrage des livraisons par recherche const filterDeliveries = (deliveries: any[]) => { return deliveries.filter((delivery: any) => { @@ -258,11 +224,37 @@ export default function BLReconciliation() { const filteredManualDeliveries = filterDeliveries(manualReconciliationDeliveries); const filteredAutomaticDeliveries = filterDeliveries(automaticReconciliationDeliveries); + // Pagination pour les rapprochements manuels + const { + currentPage: manualCurrentPage, + setCurrentPage: setManualCurrentPage, + itemsPerPage: manualItemsPerPage, + setItemsPerPage: setManualItemsPerPage, + totalPages: manualTotalPages, + paginatedData: paginatedManualDeliveries, + totalItems: manualTotalItems + } = usePagination(filteredManualDeliveries, 20); + + // Pagination pour les rapprochements automatiques + const { + currentPage: autoCurrentPage, + setCurrentPage: setAutoCurrentPage, + itemsPerPage: autoItemsPerPage, + setItemsPerPage: setAutoItemsPerPage, + totalPages: autoTotalPages, + paginatedData: paginatedAutoDeliveries, + totalItems: autoTotalItems + } = usePagination(filteredAutomaticDeliveries, 20); + const canModify = user?.role === 'directeur' || user?.role === 'admin'; + if (isLoading) { + return
| - Fournisseur - | -- N° BL - | -- Date Livr. - | -- Montant BL - | -- Ref. Facture - | -- Montant Fact. - | -- Écart - | -- Magasin - | -- Actions - | -||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
-
- {delivery.supplier?.name}
-
- |
-
-
- {delivery.blNumber || (
- Non renseigné
- )}
-
- |
-
-
- {safeFormat(delivery.scheduledDate, 'dd/MM/yy')}
-
- |
-
-
- {delivery.blAmount ?
- `${parseFloat(delivery.blAmount).toFixed(2)}€` :
- Non renseigné
- }
-
- |
-
-
- {delivery.invoiceReference || (
- Non renseigné
- )}
-
- |
-
-
- {delivery.invoiceAmount ?
- `${parseFloat(delivery.invoiceAmount).toFixed(2)}€` :
- Non renseigné
- }
-
- |
-
- {(() => {
- const blAmount = delivery.blAmount ? parseFloat(delivery.blAmount) : 0;
- const invoiceAmount = delivery.invoiceAmount ? parseFloat(delivery.invoiceAmount) : 0;
- if (blAmount && invoiceAmount) {
- const diff = blAmount - invoiceAmount;
- const diffAbs = Math.abs(diff);
- return (
- 10 ? 'text-red-600' : 'text-orange-600'
- }`}>
- {diff > 0 ? '+' : ''}{diff.toFixed(2)}€
-
- );
- }
- return -;
- })()}
- |
-
-
- {delivery.group?.name}
-
- |
-
-
- {/* Actions selon le statut de rapprochement et les permissions */}
- {!delivery.reconciled ? (
- // Livraison non rapprochée : Valider + Supprimer
- <>
- {permissions.canValidate('reconciliation') && (
+
+
+
+
+
-
|
+
|
- Fournisseur
- |
- - N° BL - | -- Date Livr. - | -- Date Valid. - | -- Montant BL - | -- Ref. Facture - | -- Montant Fact. - | -- Écart - | -- Magasin - | -- Actions - | -
|---|---|---|---|---|---|---|---|---|---|
|
-
-
-
- {delivery.supplier?.name}
-
- |
-
-
- {delivery.blNumber || '-'}
-
- |
-
-
- {safeFormat(delivery.scheduledDate, 'dd/MM/yy')}
-
- |
-
-
- {delivery.reconciled && delivery.updatedAt ?
- safeFormat(delivery.updatedAt, 'dd/MM/yy') :
- '-'
- }
-
- |
-
-
- {delivery.blAmount ?
- `${parseFloat(delivery.blAmount).toFixed(2)}€` :
- '-'
- }
-
- |
-
-
- {delivery.invoiceReference || '-'}
-
- |
-
-
- {delivery.invoiceAmount ?
- `${parseFloat(delivery.invoiceAmount).toFixed(2)}€` :
- '-'
- }
-
- |
-
- {(() => {
- const blAmount = delivery.blAmount ? parseFloat(delivery.blAmount) : 0;
- const invoiceAmount = delivery.invoiceAmount ? parseFloat(delivery.invoiceAmount) : 0;
- if (blAmount && invoiceAmount) {
- const diff = blAmount - invoiceAmount;
- const diffAbs = Math.abs(diff);
- return (
- 10 ? 'text-red-600' : 'text-orange-600'
- }`}>
- {diff > 0 ? '+' : ''}{diff.toFixed(2)}€
-
- );
- }
- return -;
- })()}
- |
-
-
- {delivery.group?.name}
-
- |
-
-
- {user?.role === 'admin' && delivery.reconciled && (
-
- )}
- {user?.role === 'admin' && (
-
- )}
- {/* Bouton Modifier : disponible seulement si pas encore validé */}
- {!delivery.reconciled && (
-
- )}
-
- |
-
|
+ Fournisseur
+ |
+ + N° BL + | ++ Date Livr. + | ++ Date Valid. + | ++ Magasin + | ++ Actions + | +
|---|---|---|---|---|---|
|
+
+ {delivery.supplier?.name}
+
+ |
+
+
+ {delivery.blNumber || (
+ Non renseigné
+ )}
+
+ |
+
+
+ {safeFormat(delivery.scheduledDate, 'dd/MM/yy')}
+
+ |
+
+
+ {delivery.validatedAt ?
+ safeFormat(delivery.validatedAt, 'dd/MM/yy HH:mm') :
+ Non validé
+ }
+
+ |
+
+
+ {delivery.group?.name}
+
+ |
+
+
+ {user?.role === 'admin' && (
+
+ )}
+ {permissions.canDelete('reconciliation') && (
+
+ )}
+
+
+ |
+