Miglioramenti Auto-Match AI - Movimenti Bancari
Problemi Risolti
1. Fornitore non inserito automaticamente
Problema: Il fornitore estratto dall’AI non veniva salvato se non c’era un match con le fatture.
Soluzione: Ora il supplierName viene salvato immediatamente dopo l’estrazione, indipendentemente dal match con le fatture. Questo permette di mantenere il fornitore anche per transazioni senza fatture corrispondenti.
2. Amazon (AMZN) non riconosciuto
Problema: Le transazioni con “AMZN” nella descrizione non venivano abbinate al fornitore Amazon.
Soluzione:
- Aggiunta una mappa di abbreviazioni comuni che espande automaticamente:
AMZN→AmazonAMZ→AmazonMETRO→Metro- etc.
- Il prompt AI ora riceve sia la descrizione originale che quella espansa
- Aggiunti esempi espliciti nel prompt AI per gestire AMZN/AMZ
3. Matching fornitori troppo rigido
Problema: Il matching tra transazioni e fatture falliva con piccole variazioni nei nomi (es. “Metro” vs “Metro Cash & Carry”).
Soluzione: Implementato matching più flessibile:
- Rimozione automatica di suffissi comuni (SRL, SPA, SAS, Ltd, Inc, LLC)
- Match bidirezionale (uno contiene l’altro)
- Match per parole chiave significative (>3 caratteri)
- Match case-insensitive
4. Abbinamento fatture prima del completamento estrazione fornitori
Problema: Il matching delle fatture iniziava prima che tutti i fornitori fossero stati estratti e salvati.
Soluzione:
- Salvataggio parallelo di tutti i fornitori tramite
Promise.all() - Log di conferma quando tutti i fornitori sono salvati
- Separazione chiara tra fase di estrazione e fase di matching
- Il matching inizia solo dopo che TUTTI i fornitori sono stati identificati e salvati
Modifiche Tecniche
File modificato
/src/app/api/bank-transactions/auto-match/route.ts
Cambiamenti principali
- Mappa abbreviazioni (linea ~420):
const supplierAbbreviations: Record<string, string> = {
AMZN: "Amazon",
AMZ: "Amazon",
AMAZON: "Amazon",
METRO: "Metro",
SYSCO: "Sysco",
};- Espansione descrizioni (linea ~428):
const expandAbbreviations = (description: string): string => {
let expanded = description;
for (const [abbr, fullName] of Object.entries(supplierAbbreviations)) {
const regex = new RegExp(`\\b${abbr}\\b`, "gi");
expanded = expanded.replace(regex, fullName);
}
return expanded;
};- Salvataggio parallelo e completo dei fornitori (linea ~500):
// Salva i fornitori estratti IMMEDIATAMENTE in parallelo
const savePromises = [];
for (const extraction of extractionResult.extractions || []) {
const transaction = supplierPayments.find(
(t: any) => t._id.toString() === extraction.id,
);
if (transaction && extraction.supplierName) {
transaction.supplierName = extraction.supplierName;
savePromises.push(
transaction.save().then(() => {
sendLog(`Fornitore estratto e salvato: ${extraction.supplierName}...`);
}),
);
}
}
// Aspetta che TUTTI i fornitori siano salvati prima di procedere
await Promise.all(savePromises);
sendLog(`Completato salvataggio di ${savePromises.length} fornitori estratti`);- Separazione chiara tra estrazione e matching (linea ~525):
// IMPORTANTE: A questo punto tutti i fornitori sono stati estratti e salvati
// Ora possiamo procedere con l'abbinamento delle fatture
sendLog("Inizio abbinamento fatture ai fornitori identificati...");
for (const transaction of supplierPayments) {
// ... logica di matching
}- Matching fornitori migliorato (linea ~570):
// Rimuovi suffissi comuni per il matching
const cleanSupplier = (name: string) =>
name
.replace(/\s+(s\.?r\.?l\.?|s\.?p\.?a\.?|s\.?a\.?s\.?|ltd|inc|llc)$/gi, "")
.trim();
const cleanTransaction = cleanSupplier(transactionSupplier);
const cleanInvoice = cleanSupplier(invoiceSupplier);
// Match se uno contiene l'altro o se sono molto simili
const supplierMatch =
invoiceSupplier.includes(transactionSupplier) ||
transactionSupplier.includes(invoiceSupplier) ||
cleanInvoice.includes(cleanTransaction) ||
cleanTransaction.includes(cleanInvoice) ||
// Match per parole chiave
transactionSupplier
.split(/\s+/)
.some(word => word.length > 3 && invoiceSupplier.includes(word)) ||
invoiceSupplier
.split(/\s+/)
.some(word => word.length > 3 && transactionSupplier.includes(word));Esempi di Miglioramenti
Prima delle modifiche:
- ❌ “AMZN EU SARL” → Nessun fornitore
- ❌ “PAGAMENTO METRO” → Nessun match con “Metro Cash & Carry”
- ❌ Fornitore estratto ma non salvato se manca fattura
Dopo le modifiche:
- ✅ “AMZN EU SARL” → Fornitore: “Amazon”
- ✅ “PAGAMENTO METRO” → Match con “Metro Cash & Carry”
- ✅ Fornitore sempre salvato, anche senza fattura corrispondente
Note per Estensioni Future
Per aggiungere nuove abbreviazioni comuni, modificare la mappa supplierAbbreviations:
const supplierAbbreviations: Record<string, string> = {
AMZN: "Amazon",
AMZ: "Amazon",
METRO: "Metro",
// Aggiungi qui nuove abbreviazioni
ABBREVIAZIONE: "Nome Completo",
};Testing
Testare con:
- Transazioni con “AMZN” nella descrizione
- Fornitori con nomi abbreviati vs nomi completi nelle fatture
- Verificare che il fornitore venga salvato anche senza match di fatture