mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Improve invoice verification by adding caching and manual refresh functionality
Implement an invoice verification cache to avoid redundant checks, introduce a manual refresh button to trigger verification on demand, and modify the API to accept a forceRefresh parameter. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 7c468936-2a88-4686-ac0a-84921a45222d Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/7c468936-2a88-4686-ac0a-84921a45222d/Sh7jLhH
This commit is contained in:
1 parent
414c32f110
commit
7fb037bf53
4 files changed
+141
-40
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -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() {
|
||||
<Badge variant="outline" className="text-xs sm:text-sm border border-gray-300 bg-blue-50">
|
||||
{automaticReconciliationDeliveries.length} automatiques
|
||||
</Badge>
|
||||
<Button
|
||||
onClick={handleVerifyAllInvoices}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="text-xs sm:text-sm bg-green-50 border-green-300 text-green-700 hover:bg-green-100"
|
||||
>
|
||||
<Search className="w-4 h-4 mr-1" />
|
||||
Vérifier toutes les factures
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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<any | null> {
|
||||
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<void> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -860,7 +860,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
}
|
||||
|
||||
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<Server> {
|
||||
// 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);
|
||||
|
||||
Reference in new issue
Block a user