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))
|
.Where(t => !transactionsWithReceipts.Contains(t.Id))
|
||||||
.AsQueryable();
|
.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)
|
if (receipt.ReceiptDate.HasValue)
|
||||||
{
|
{
|
||||||
var minDate = receipt.ReceiptDate.Value.AddDays(-3);
|
var minDate = receipt.ReceiptDate.Value.AddDays(-3);
|
||||||
@@ -240,20 +240,50 @@ namespace MoneyMap.Pages
|
|||||||
query = query.Where(t => t.Date >= minDate && t.Date <= maxDate);
|
query = query.Where(t => t.Date >= minDate && t.Date <= maxDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If receipt has merchant, filter by merchant name
|
// Get all candidates within date range (don't filter by merchant in query)
|
||||||
if (!string.IsNullOrWhiteSpace(receipt.Merchant))
|
|
||||||
{
|
|
||||||
query = query.Where(t =>
|
|
||||||
(t.Merchant != null && t.Merchant.Name.Contains(receipt.Merchant)) ||
|
|
||||||
t.Name.Contains(receipt.Merchant));
|
|
||||||
}
|
|
||||||
|
|
||||||
var candidates = await query
|
var candidates = await query
|
||||||
.OrderByDescending(t => t.Date)
|
.OrderByDescending(t => t.Date)
|
||||||
.ThenByDescending(t => t.Id)
|
.ThenByDescending(t => t.Id)
|
||||||
.Take(50) // Limit to 50 matches
|
.Take(100) // Get more candidates for sorting
|
||||||
.ToListAsync();
|
.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
|
// Calculate match scores and mark close amount matches
|
||||||
var options = candidates.Select(t =>
|
var options = candidates.Select(t =>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user