feat: Enhance error handling and logging in expense and interest duplicate detection, add retry logic for document deletion, and improve PDF generation with detailed receipt processing
This commit is contained in:
@@ -86,6 +86,9 @@ export default defineEventHandler(async (event) => {
|
||||
* Find duplicate interests based on multiple criteria
|
||||
*/
|
||||
function findDuplicateInterests(interests: any[], threshold: number = 0.8) {
|
||||
console.log('[INTERESTS] Starting duplicate detection with threshold:', threshold);
|
||||
console.log('[INTERESTS] Total interests to analyze:', interests.length);
|
||||
|
||||
const duplicateGroups: Array<{
|
||||
id: string;
|
||||
interests: any[];
|
||||
@@ -95,6 +98,7 @@ function findDuplicateInterests(interests: any[], threshold: number = 0.8) {
|
||||
}> = [];
|
||||
|
||||
const processedIds = new Set<number>();
|
||||
let comparisons = 0;
|
||||
|
||||
for (let i = 0; i < interests.length; i++) {
|
||||
const interest1 = interests[i];
|
||||
@@ -109,14 +113,21 @@ function findDuplicateInterests(interests: any[], threshold: number = 0.8) {
|
||||
if (processedIds.has(interest2.Id)) continue;
|
||||
|
||||
const similarity = calculateSimilarity(interest1, interest2);
|
||||
comparisons++;
|
||||
|
||||
console.log(`[INTERESTS] Comparing ${interest1.Id} vs ${interest2.Id}: score=${similarity.score.toFixed(3)}, threshold=${threshold}`);
|
||||
|
||||
if (similarity.score >= threshold) {
|
||||
console.log(`[INTERESTS] MATCH FOUND! ${interest1.Id} vs ${interest2.Id} (score: ${similarity.score.toFixed(3)})`);
|
||||
console.log('[INTERESTS] Match details:', similarity.details);
|
||||
matches.push(interest2);
|
||||
processedIds.add(interest2.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.length > 1) {
|
||||
console.log(`[INTERESTS] Creating duplicate group with ${matches.length} matches`);
|
||||
|
||||
// Mark all as processed
|
||||
matches.forEach(match => processedIds.add(match.Id));
|
||||
|
||||
@@ -138,6 +149,7 @@ function findDuplicateInterests(interests: any[], threshold: number = 0.8) {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[INTERESTS] Completed ${comparisons} comparisons, found ${duplicateGroups.length} duplicate groups`);
|
||||
return duplicateGroups;
|
||||
}
|
||||
|
||||
@@ -146,37 +158,68 @@ function findDuplicateInterests(interests: any[], threshold: number = 0.8) {
|
||||
*/
|
||||
function calculateSimilarity(interest1: any, interest2: any) {
|
||||
const scores: Array<{ type: string; score: number; weight: number }> = [];
|
||||
|
||||
console.log(`[INTERESTS] Calculating similarity between:`, {
|
||||
id1: interest1.Id,
|
||||
name1: interest1['Full Name'],
|
||||
email1: interest1['Email Address'],
|
||||
phone1: interest1['Phone Number'],
|
||||
id2: interest2.Id,
|
||||
name2: interest2['Full Name'],
|
||||
email2: interest2['Email Address'],
|
||||
phone2: interest2['Phone Number']
|
||||
});
|
||||
|
||||
// Email similarity (highest weight)
|
||||
// Email similarity (highest weight) - exact match required
|
||||
if (interest1['Email Address'] && interest2['Email Address']) {
|
||||
const emailScore = normalizeEmail(interest1['Email Address']) === normalizeEmail(interest2['Email Address']) ? 1.0 : 0.0;
|
||||
scores.push({ type: 'email', score: emailScore, weight: 0.4 });
|
||||
const email1 = normalizeEmail(interest1['Email Address']);
|
||||
const email2 = normalizeEmail(interest2['Email Address']);
|
||||
const emailScore = email1 === email2 ? 1.0 : 0.0;
|
||||
scores.push({ type: 'email', score: emailScore, weight: 0.5 });
|
||||
console.log(`[INTERESTS] Email comparison: "${email1}" vs "${email2}" = ${emailScore}`);
|
||||
}
|
||||
|
||||
// Phone similarity
|
||||
// Phone similarity - exact match on normalized numbers
|
||||
if (interest1['Phone Number'] && interest2['Phone Number']) {
|
||||
const phone1 = normalizePhone(interest1['Phone Number']);
|
||||
const phone2 = normalizePhone(interest2['Phone Number']);
|
||||
const phoneScore = phone1 === phone2 ? 1.0 : 0.0;
|
||||
scores.push({ type: 'phone', score: phoneScore, weight: 0.3 });
|
||||
const phoneScore = phone1 === phone2 && phone1.length >= 8 ? 1.0 : 0.0; // Require at least 8 digits
|
||||
scores.push({ type: 'phone', score: phoneScore, weight: 0.4 });
|
||||
console.log(`[INTERESTS] Phone comparison: "${phone1}" vs "${phone2}" = ${phoneScore}`);
|
||||
}
|
||||
|
||||
// Name similarity
|
||||
// Name similarity - fuzzy matching
|
||||
if (interest1['Full Name'] && interest2['Full Name']) {
|
||||
const nameScore = calculateNameSimilarity(interest1['Full Name'], interest2['Full Name']);
|
||||
scores.push({ type: 'name', score: nameScore, weight: 0.2 });
|
||||
scores.push({ type: 'name', score: nameScore, weight: 0.3 });
|
||||
console.log(`[INTERESTS] Name comparison: "${interest1['Full Name']}" vs "${interest2['Full Name']}" = ${nameScore.toFixed(3)}`);
|
||||
}
|
||||
|
||||
// Address similarity
|
||||
if (interest1.Address && interest2.Address) {
|
||||
const addressScore = calculateStringSimilarity(interest1.Address, interest2.Address);
|
||||
scores.push({ type: 'address', score: addressScore, weight: 0.1 });
|
||||
scores.push({ type: 'address', score: addressScore, weight: 0.2 });
|
||||
console.log(`[INTERESTS] Address comparison: ${addressScore.toFixed(3)}`);
|
||||
}
|
||||
|
||||
// Calculate weighted average
|
||||
// Special case: if we have exact email OR phone match, give high score regardless of other fields
|
||||
const hasExactEmailMatch = scores.find(s => s.type === 'email' && s.score === 1.0);
|
||||
const hasExactPhoneMatch = scores.find(s => s.type === 'phone' && s.score === 1.0);
|
||||
|
||||
if (hasExactEmailMatch || hasExactPhoneMatch) {
|
||||
console.log('[INTERESTS] Exact email or phone match found - high confidence');
|
||||
return {
|
||||
score: 0.95, // High confidence for exact email/phone match
|
||||
details: scores
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate weighted average for other cases
|
||||
const totalWeight = scores.reduce((sum, s) => sum + s.weight, 0);
|
||||
const weightedScore = scores.reduce((sum, s) => sum + (s.score * s.weight), 0) / (totalWeight || 1);
|
||||
|
||||
console.log(`[INTERESTS] Weighted score: ${weightedScore.toFixed(3)} (weights: ${totalWeight})`);
|
||||
|
||||
return {
|
||||
score: weightedScore,
|
||||
details: scores
|
||||
|
||||
Reference in New Issue
Block a user