diff --git a/.replit b/.replit index fff634c..1c5ab1f 100644 --- a/.replit +++ b/.replit @@ -19,6 +19,10 @@ externalPort = 80 localPort = 33873 externalPort = 3003 +[[ports]] +localPort = 38213 +externalPort = 5000 + [[ports]] localPort = 38343 externalPort = 3000 diff --git a/client/src/pages/BLReconciliation.tsx b/client/src/pages/BLReconciliation.tsx index ae36b7e..6e07047 100644 --- a/client/src/pages/BLReconciliation.tsx +++ b/client/src/pages/BLReconciliation.tsx @@ -52,7 +52,6 @@ export default function BLReconciliation() { const [searchTerm, setSearchTerm] = useState(""); const [selectedDelivery, setSelectedDelivery] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); - const [filterStatus, setFilterStatus] = useState<"all" | "validated" | "not_validated">("all"); // État pour le modal d'envoi de facture const [showInvoiceModal, setShowInvoiceModal] = useState(false); @@ -190,7 +189,7 @@ export default function BLReconciliation() { // Fonction pour vérifier toutes les factures avec un bouton const handleVerifyAllInvoices = () => { - const deliveriesToVerify = manualReconciliationDeliveries.filter(delivery => + const deliveriesToVerify = manualNotValidatedDeliveries.filter((delivery: any) => (delivery.invoiceReference?.trim() || delivery.blNumber?.trim()) && (delivery.group?.nocodbTableName || delivery.group?.nocodbConfigId || delivery.group?.webhookUrl) ); @@ -203,7 +202,7 @@ export default function BLReconciliation() { return; } - deliveriesToVerify.forEach((delivery, index) => { + deliveriesToVerify.forEach((delivery: any, index: number) => { // Délai échelonné pour éviter la surcharge setTimeout(() => { handleVerifyInvoice(delivery, true); // Force refresh pour toutes @@ -310,15 +309,16 @@ export default function BLReconciliation() { }); }, [deliveriesWithBL, suppliers, verificationResults, verifyingDeliveries]); - // Séparer les livraisons par mode de rapprochement - const manualReconciliationDeliveries = deliveriesWithBL.filter((delivery: any) => { + // Séparer les livraisons : non validées manuelles et toutes les validées + const manualNotValidatedDeliveries = deliveriesWithBL.filter((delivery: any) => { const supplier = suppliers.find(s => s.id === delivery.supplierId); - return supplier?.automaticReconciliation !== true; + const isManual = supplier?.automaticReconciliation !== true; + const isNotValidated = delivery.reconciled !== true && delivery.reconciled !== 1; + return isManual && isNotValidated; }); - const automaticReconciliationDeliveries = deliveriesWithBL.filter((delivery: any) => { - const supplier = suppliers.find(s => s.id === delivery.supplierId); - return supplier?.automaticReconciliation === true; + const allValidatedDeliveries = deliveriesWithBL.filter((delivery: any) => { + return delivery.reconciled === true || delivery.reconciled === 1; }); // Fonctions de gestion @@ -626,45 +626,23 @@ export default function BLReconciliation() { } }; - // Filtrage des livraisons par recherche et statut de validation + // Filtrage des livraisons par recherche uniquement const filterDeliveries = (deliveries: any[]) => { + if (!searchTerm) return deliveries; + + const searchLower = searchTerm.toLowerCase(); return deliveries.filter((delivery: any) => { - // Conversion sûre de reconciled - gestion de tous les cas possibles - const isReconciled = - delivery.reconciled === true || - delivery.reconciled === 1 || - delivery.reconciled === "t" || - delivery.reconciled === "true" || - (typeof delivery.reconciled === 'string' && delivery.reconciled.toLowerCase() === 'true'); - - // Vérifier aussi si une facture a été trouvée ET validée automatiquement - // (certaines livraisons ont une coche verte mais reconciled = false) - const hasVerifiedInvoice = verificationResults[delivery.id]?.exists === true; - const isValidated = isReconciled || (hasVerifiedInvoice && verificationResults[delivery.id]?.reconciled === true); - - // Filtre par statut validé - if (filterStatus === "validated") { - // Afficher seulement les livraisons vraiment validées (reconciled = true) - return isReconciled; - } - - if (filterStatus === "not_validated") { - // Afficher seulement les livraisons non validées (reconciled = false) - return !isReconciled; - } - - // Filtre par recherche - const searchLower = searchTerm.toLowerCase(); return ( delivery.supplier?.name?.toLowerCase().includes(searchLower) || delivery.blNumber?.toLowerCase().includes(searchLower) || - delivery.invoiceReference?.toLowerCase().includes(searchLower) + delivery.invoiceReference?.toLowerCase().includes(searchLower) || + delivery.group?.name?.toLowerCase().includes(searchLower) ); }); }; - const filteredManualDeliveries = filterDeliveries(manualReconciliationDeliveries); - const filteredAutomaticDeliveries = filterDeliveries(automaticReconciliationDeliveries); + const filteredManualDeliveries = filterDeliveries(manualNotValidatedDeliveries); + const filteredValidatedDeliveries = filterDeliveries(allValidatedDeliveries); // Pagination pour les rapprochements manuels const { @@ -677,16 +655,16 @@ export default function BLReconciliation() { totalItems: manualTotalItems } = usePagination(filteredManualDeliveries, 20); - // Pagination pour les rapprochements automatiques + // Pagination pour les livraisons validées const { - currentPage: autoCurrentPage, - setCurrentPage: setAutoCurrentPage, - itemsPerPage: autoItemsPerPage, - setItemsPerPage: setAutoItemsPerPage, - totalPages: autoTotalPages, - paginatedData: paginatedAutoDeliveries, - totalItems: autoTotalItems - } = usePagination(filteredAutomaticDeliveries, 20); + currentPage: validatedCurrentPage, + setCurrentPage: setValidatedCurrentPage, + itemsPerPage: validatedItemsPerPage, + setItemsPerPage: setValidatedItemsPerPage, + totalPages: validatedTotalPages, + paginatedData: paginatedValidatedDeliveries, + totalItems: validatedTotalItems + } = usePagination(filteredValidatedDeliveries, 20); const canModify = user?.role === 'directeur' || user?.role === 'admin'; @@ -710,10 +688,10 @@ export default function BLReconciliation() {
- {manualReconciliationDeliveries.length} manuels + {manualNotValidatedDeliveries.length} à traiter - - {automaticReconciliationDeliveries.length} automatiques + + {allValidatedDeliveries.length} validées
- {/* Filters */} + {/* Barre de recherche */}
-
-
-
- - setSearchTerm(e.target.value)} - className="pl-10 border border-gray-300 shadow-sm w-full" - /> -
-
-
- -
+
+ + setSearchTerm(e.target.value)} + className="pl-10 border border-gray-300 shadow-sm w-full" + />
@@ -1098,15 +1047,15 @@ export default function BLReconciliation() { )} - + {/* Message d'information */} -
+
- +
-

Mode rapprochement automatique

-

- Les livraisons de fournisseurs en mode automatique sont validées automatiquement lorsqu'elles ont le statut "delivered" et un numéro de BL. +

Livraisons validées

+

+ Cette section regroupe toutes les livraisons validées (manuellement ou automatiquement). {(permissions.canEdit('reconciliation') || permissions.canValidate('reconciliation')) ? ( " Vous pouvez dévalider ces rapprochements si nécessaire." ) : ( @@ -1117,14 +1066,14 @@ export default function BLReconciliation() {

- {filteredAutomaticDeliveries.length === 0 ? ( + {filteredValidatedDeliveries.length === 0 ? (
- +

- Aucun rapprochement automatique trouvé + Aucune livraison validée trouvée

- Les livraisons de fournisseurs en mode automatique apparaîtront ici. + Les livraisons validées apparaîtront ici.

) : ( @@ -1133,22 +1082,21 @@ export default function BLReconciliation() { {/* Pagination du haut */}
- +
+ + + - {paginatedAutoDeliveries.map((delivery: any) => ( - - - + + + + + + + + + - - - - - - ))} + + + + ); + })}
Fournisseur - AUTO N° BL @@ -1157,7 +1105,16 @@ export default function BLReconciliation() { Date Livr. - Date Valid. + Ref. Facture + + Montant BL + + Montant Fact. + + Écart Magasin @@ -1168,91 +1125,116 @@ export default function BLReconciliation() {
-
- {delivery.supplier?.name} -
-
-
- {delivery.blNumber || ( - { + const supplier = suppliers.find(s => s.id === delivery.supplierId); + const isAutomatic = supplier?.automaticReconciliation === true; + const ecart = delivery.blAmount && delivery.invoiceAmount ? + ((parseFloat(delivery.invoiceAmount) - parseFloat(delivery.blAmount)) / parseFloat(delivery.blAmount) * 100).toFixed(1) : + null; + + return ( +
+
+
+ {delivery.supplier?.name} +
+ {isAutomatic && ( + AUTO + )} +
+
+
+ {delivery.blNumber || ( + Non renseigné + )} +
+
+
+ {safeFormat(delivery.scheduledDate, 'dd/MM/yy')} +
+
+
+ {delivery.invoiceReference || ( + Non renseignée + )} +
+
+
+ {delivery.blAmount ? + `${parseFloat(delivery.blAmount).toFixed(2)}€` : + Non renseigné + } +
+
+
+ {delivery.invoiceAmount ? + `${parseFloat(delivery.invoiceAmount).toFixed(2)}€` : + Non renseigné + } +
+
+ {ecart !== null ? ( + 5 ? "destructive" : "secondary"} + className="text-xs" + > + {parseFloat(ecart) > 0 ? '+' : ''}{ecart}% + + ) : ( + - + )} + +
+ {delivery.group?.name} +
+
+
+ + {(permissions.canEdit('reconciliation') || permissions.canValidate('reconciliation')) && ( + + )} + {permissions.canDelete('reconciliation') && ( + + )} +
-
-
- {safeFormat(delivery.scheduledDate, 'dd/MM/yy')} -
-
-
- {delivery.validatedAt ? - safeFormat(delivery.validatedAt, 'dd/MM/yy HH:mm') : - Non validé - } -
-
-
- {delivery.group?.name} -
-
-
- {shouldShowInvoiceButton(delivery) && ( - - )} - - {(permissions.canEdit('reconciliation') || permissions.canValidate('reconciliation')) && ( - - )} - {permissions.canDelete('reconciliation') && ( - - )} - -
-
@@ -1260,12 +1242,12 @@ export default function BLReconciliation() { {/* Pagination du bas */}