Fix merchant filtering in receipt mapping to be non-exclusive
Changed merchant filtering from exclusionary to relevance-based sorting. This fixes the issue where transactions don't appear if the merchant name doesn't exactly match. Changes: - Date range (±3 days) remains the primary hard filter - Merchant name now sorts results by relevance instead of excluding - Retrieves 100 candidates, sorts by match quality, returns top 50 - Case-insensitive matching using ToLower() Relevance Scoring: - Score 3: Exact match on merchant or transaction name - Score 2: Partial match on merchant name (Contains) - Score 1: Partial match on transaction name (Contains) - Score 0: No match (still included if within date range) Results sorted by: Match score → Date → ID Benefits: - McDonald's receipt will show all transactions in date range - Best matches appear at top - No transactions excluded due to name variations - More forgiving for typos, abbreviations, etc. - User can still see and select any transaction in date window Example: Receipt says "McDonald's" but transaction says "MCDONALD'S #1234" - Before: Excluded (no exact Contains match) - After: Included, sorted to top (partial match score 2) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -232,7 +232,7 @@ namespace MoneyMap.Pages
|
||||
.Where(t => !transactionsWithReceipts.Contains(t.Id))
|
||||
.AsQueryable();
|
||||
|
||||
// If receipt has a date, filter by +/- 3 days
|
||||
// If receipt has a date, filter by +/- 3 days (this is the primary filter)
|
||||
if (receipt.ReceiptDate.HasValue)
|
||||
{
|
||||
var minDate = receipt.ReceiptDate.Value.AddDays(-3);
|
||||
@@ -240,20 +240,50 @@ namespace MoneyMap.Pages
|
||||
query = query.Where(t => t.Date >= minDate && t.Date <= maxDate);
|
||||
}
|
||||
|
||||
// If receipt has merchant, filter by merchant name
|
||||
if (!string.IsNullOrWhiteSpace(receipt.Merchant))
|
||||
{
|
||||
query = query.Where(t =>
|
||||
(t.Merchant != null && t.Merchant.Name.Contains(receipt.Merchant)) ||
|
||||
t.Name.Contains(receipt.Merchant));
|
||||
}
|
||||
|
||||
// Get all candidates within date range (don't filter by merchant in query)
|
||||
var candidates = await query
|
||||
.OrderByDescending(t => t.Date)
|
||||
.ThenByDescending(t => t.Id)
|
||||
.Take(50) // Limit to 50 matches
|
||||
.Take(100) // Get more candidates for sorting
|
||||
.ToListAsync();
|
||||
|
||||
// If receipt has merchant, sort matches by relevance (but don't exclude)
|
||||
if (!string.IsNullOrWhiteSpace(receipt.Merchant))
|
||||
{
|
||||
var merchantLower = receipt.Merchant.ToLower();
|
||||
|
||||
// Sort: exact matches first, then partial matches, then others
|
||||
candidates = candidates
|
||||
.OrderByDescending(t =>
|
||||
{
|
||||
var merchantName = t.Merchant?.Name?.ToLower() ?? "";
|
||||
var transactionName = t.Name?.ToLower() ?? "";
|
||||
|
||||
// Exact match on merchant or transaction name
|
||||
if (merchantName == merchantLower || transactionName == merchantLower)
|
||||
return 3;
|
||||
|
||||
// Contains match on merchant
|
||||
if (merchantName.Contains(merchantLower))
|
||||
return 2;
|
||||
|
||||
// Contains match on transaction name
|
||||
if (transactionName.Contains(merchantLower))
|
||||
return 1;
|
||||
|
||||
// No match
|
||||
return 0;
|
||||
})
|
||||
.ThenByDescending(t => t.Date)
|
||||
.Take(50)
|
||||
.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No merchant filter, just take top 50 by date
|
||||
candidates = candidates.Take(50).ToList();
|
||||
}
|
||||
|
||||
// Calculate match scores and mark close amount matches
|
||||
var options = candidates.Select(t =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user