diff --git a/attached_assets/{F856D9DD-5EA8-4B46-B668-7E568CF278C4}_1755092650737.png b/attached_assets/{F856D9DD-5EA8-4B46-B668-7E568CF278C4}_1755092650737.png
new file mode 100644
index 0000000..9673b31
Binary files /dev/null and b/attached_assets/{F856D9DD-5EA8-4B46-B668-7E568CF278C4}_1755092650737.png differ
diff --git a/client/src/pages/BLReconciliation.tsx b/client/src/pages/BLReconciliation.tsx
index 55f5d08..135121b 100644
--- a/client/src/pages/BLReconciliation.tsx
+++ b/client/src/pages/BLReconciliation.tsx
@@ -69,11 +69,12 @@ export default function BLReconciliation() {
// Fonction de vérification de facture
const verifyInvoiceMutation = useMutation({
- mutationFn: async ({ deliveryId, invoiceReference, blNumber }: { deliveryId: number; invoiceReference?: string; blNumber?: string }) => {
+ mutationFn: async ({ deliveryId, invoiceReference, blNumber, forceRefresh }: { deliveryId: number; invoiceReference?: string; blNumber?: string; forceRefresh?: boolean }) => {
try {
const result = await apiRequest(`/api/deliveries/${deliveryId}/verify-invoice`, 'POST', {
invoiceReference,
- blNumber
+ blNumber,
+ forceRefresh: forceRefresh || false
});
return result;
} catch (error: any) {
@@ -129,17 +130,16 @@ export default function BLReconciliation() {
});
// Fonction pour déclencher la vérification
- const handleVerifyInvoice = (delivery: any) => {
- console.log('🔍 Debug vérification:', {
- delivery: delivery,
- group: delivery.group,
- webhookUrl: delivery.group?.webhookUrl,
- nocodbTableName: delivery.group?.nocodbTableName,
- invoiceColumnName: delivery.group?.invoiceColumnName,
- nocodbConfigId: delivery.group?.nocodbConfigId,
- supplier: delivery.supplier
- });
-
+ const handleVerifyInvoice = (delivery: any, forceRefresh: boolean = false) => {
+ if (!delivery.invoiceReference?.trim()) {
+ toast({
+ title: "Référence manquante",
+ description: "Veuillez saisir une référence de facture avant la vérification",
+ variant: "destructive",
+ });
+ return;
+ }
+
if (!delivery.group?.nocodbTableName && !delivery.group?.nocodbConfigId && !delivery.group?.webhookUrl) {
toast({
title: "Vérification non disponible",
@@ -154,7 +154,36 @@ export default function BLReconciliation() {
verifyInvoiceMutation.mutate({
deliveryId: delivery.id,
invoiceReference: delivery.invoiceReference,
- blNumber: delivery.blNumber
+ blNumber: delivery.blNumber,
+ forceRefresh
+ });
+ };
+
+ // Fonction pour vérifier toutes les factures avec un bouton
+ const handleVerifyAllInvoices = () => {
+ const deliveriesToVerify = manualReconciliationDeliveries.filter(delivery =>
+ delivery.invoiceReference?.trim() &&
+ (delivery.group?.nocodbTableName || delivery.group?.nocodbConfigId || delivery.group?.webhookUrl)
+ );
+
+ if (deliveriesToVerify.length === 0) {
+ toast({
+ title: "Aucune facture à vérifier",
+ description: "Aucune livraison avec référence de facture trouvée",
+ });
+ return;
+ }
+
+ deliveriesToVerify.forEach((delivery, index) => {
+ // Délai échelonné pour éviter la surcharge
+ setTimeout(() => {
+ handleVerifyInvoice(delivery, true); // Force refresh pour toutes
+ }, index * 200); // 200ms entre chaque vérification
+ });
+
+ toast({
+ title: "Vérification lancée",
+ description: `Vérification de ${deliveriesToVerify.length} facture(s) en cours...`,
});
};
@@ -188,24 +217,7 @@ export default function BLReconciliation() {
enabled: !!user
});
- // VÉRIFICATION AUTOMATIQUE AU CHARGEMENT
- useEffect(() => {
- if (!deliveriesWithBL.length || !suppliers.length) return;
-
- console.log('🔄 Déclenchement vérifications automatiques...');
-
- deliveriesWithBL.forEach((delivery: any) => {
- // Vérifier seulement si on a une référence de facture et pas déjà de résultat
- if (delivery.invoiceReference && !verificationResults[delivery.id] && !verifyingDeliveries.has(delivery.id)) {
- console.log(`🔍 Vérification auto pour livraison ${delivery.id}:`, delivery.invoiceReference);
-
- // Délai pour éviter de surcharger le serveur
- setTimeout(() => {
- handleVerifyInvoice(delivery);
- }, Math.random() * 2000); // Délai aléatoire entre 0 et 2 secondes
- }
- });
- }, [deliveriesWithBL, suppliers, verificationResults, verifyingDeliveries]);
+ // Plus de vérification automatique - seulement manuelle via bouton
// Séparer les livraisons par mode de rapprochement
const manualReconciliationDeliveries = deliveriesWithBL.filter((delivery: any) => {
@@ -509,6 +521,15 @@ export default function BLReconciliation() {
{automaticReconciliationDeliveries.length} automatiques
+
diff --git a/server/invoiceVerification.ts b/server/invoiceVerification.ts
index 20b8944..48d402a 100644
--- a/server/invoiceVerification.ts
+++ b/server/invoiceVerification.ts
@@ -6,19 +6,82 @@ import { storage } from "./storage.js";
*/
export class InvoiceVerificationService {
+ /**
+ * Générer une clé de cache unique
+ */
+ private generateCacheKey(invoiceReference: string, groupId: number): string {
+ return `${groupId}_${invoiceReference.trim().toLowerCase()}`;
+ }
+
+ /**
+ * Vérifier dans le cache d'abord
+ */
+ async checkCache(invoiceReference: string, groupId: number): Promise {
+ try {
+ const cacheKey = this.generateCacheKey(invoiceReference, groupId);
+ const cached = await storage.getInvoiceVerificationCache(cacheKey);
+
+ if (cached && new Date() < new Date(cached.expiresAt)) {
+ console.log('💾 Cache hit pour:', { invoiceReference, groupId });
+ return {
+ exists: cached.exists,
+ matchType: cached.matchType,
+ errorMessage: cached.errorMessage,
+ invoiceReference: cached.invoiceReference,
+ supplierName: cached.supplierName,
+ fromCache: true
+ };
+ }
+
+ return null;
+ } catch (error) {
+ console.error('❌ Erreur lecture cache:', error);
+ return null;
+ }
+ }
+
+ /**
+ * Sauvegarder dans le cache
+ */
+ async saveToCache(invoiceReference: string, groupId: number, result: any, supplierName?: string): Promise {
+ try {
+ const cacheKey = this.generateCacheKey(invoiceReference, groupId);
+ const expiresAt = new Date();
+ expiresAt.setHours(expiresAt.getHours() + 24); // Cache pendant 24h
+
+ await storage.saveInvoiceVerificationCache({
+ cacheKey,
+ groupId,
+ invoiceReference,
+ supplierName: supplierName || null,
+ exists: result.exists,
+ matchType: result.matchType,
+ errorMessage: result.errorMessage || null,
+ cacheHit: false,
+ apiCallTime: null,
+ expiresAt
+ });
+
+ console.log('💾 Résultat sauvé en cache:', { invoiceReference, groupId, exists: result.exists });
+ } catch (error) {
+ console.error('❌ Erreur sauvegarde cache:', error);
+ }
+ }
+
/**
* Vérifie une référence de facture pour un groupe donné
*/
- async verifyInvoice(invoiceReference: string, groupId: number): Promise<{
+ async verifyInvoice(invoiceReference: string, groupId: number, forceRefresh: boolean = false): Promise<{
exists: boolean;
matchType: 'invoice_reference' | 'bl_number' | 'none';
errorMessage?: string;
invoiceReference?: string;
invoiceAmount?: number;
supplierName?: string;
+ fromCache?: boolean;
}> {
try {
- console.log('🔍 Début vérification facture:', { invoiceReference, groupId });
+ console.log('🔍 Début vérification facture:', { invoiceReference, groupId, forceRefresh });
if (!invoiceReference || !invoiceReference.trim()) {
return {
@@ -28,6 +91,14 @@ export class InvoiceVerificationService {
};
}
+ // Vérifier le cache d'abord (sauf si refresh forcé)
+ if (!forceRefresh) {
+ const cachedResult = await this.checkCache(invoiceReference, groupId);
+ if (cachedResult) {
+ return cachedResult;
+ }
+ }
+
// Récupérer la configuration du groupe
const group = await storage.getGroup(groupId);
if (!group) {
@@ -126,13 +197,17 @@ export class InvoiceVerificationService {
);
if (matchResult.found) {
- return {
+ const result = {
exists: true,
- matchType: 'invoice_reference',
+ matchType: 'invoice_reference' as const,
invoiceReference: matchResult.data.invoice_reference || invoiceReference,
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
supplierName: matchResult.data[group.nocodbSupplierColumnName || 'supplier'] || 'Inconnu'
};
+
+ // Sauvegarder en cache si succès
+ await this.saveToCache(invoiceReference, groupId, result, result.supplierName);
+ return result;
}
// Si pas trouvé par référence de facture, chercher par numéro de BL
@@ -145,13 +220,17 @@ export class InvoiceVerificationService {
);
if (matchResult.found) {
- return {
+ const result = {
exists: true,
- matchType: 'bl_number',
+ matchType: 'bl_number' as const,
invoiceReference: matchResult.data.invoice_reference || `BL_${invoiceReference}`,
invoiceAmount: parseFloat(matchResult.data[group.nocodbAmountColumnName || 'amount'] || '0'),
supplierName: matchResult.data[group.nocodbSupplierColumnName || 'supplier'] || 'Inconnu'
};
+
+ // Sauvegarder en cache si succès
+ await this.saveToCache(invoiceReference, groupId, result, result.supplierName);
+ return result;
}
}
diff --git a/server/routes.ts b/server/routes.ts
index e57148a..e271212 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -860,7 +860,7 @@ export async function registerRoutes(app: Express): Promise {
}
}
- const { invoiceReference } = req.body;
+ const { invoiceReference, forceRefresh } = req.body;
if (!delivery.supplier || !delivery.group) {
console.log('❌ Livraison manque informations:', {
@@ -886,7 +886,8 @@ export async function registerRoutes(app: Express): Promise {
// Appeler le service de vérification
const result = await invoiceVerificationService.verifyInvoice(
invoiceReference,
- delivery.groupId
+ delivery.groupId,
+ forceRefresh || false
);
console.log('✅ Résultat vérification:', result);