+
@if (!string.IsNullOrWhiteSpace(r.Merchant) || r.ReceiptDate.HasValue || r.DueDate.HasValue || r.Total.HasValue)
@@ -316,7 +318,7 @@
}
Rows highlighted in green have matching amounts (within ±2%). Only showing transactions within ±10% of receipt total.
-
+
@@ -327,6 +329,7 @@
| Name |
Merchant |
Payment |
+ Edit |
@@ -353,6 +356,7 @@
@txn.Name |
@(txn.MerchantName ?? "-") |
@txn.PaymentMethod |
+ Open |
}
@@ -398,7 +402,7 @@
-
@@ -411,3 +415,22 @@
}
+
+
+
+
+
diff --git a/MoneyMap/Pages/Receipts.cshtml.cs b/MoneyMap/Pages/Receipts.cshtml.cs
index d0f377f..548bf5d 100644
--- a/MoneyMap/Pages/Receipts.cshtml.cs
+++ b/MoneyMap/Pages/Receipts.cshtml.cs
@@ -140,6 +140,41 @@ namespace MoneyMap.Pages
public async Task OnPostMapToTransactionAsync(long receiptId, long transactionId)
{
+ if (transactionId <= 0)
+ {
+ Message = "Please select a transaction or enter a valid ID.";
+ IsSuccess = false;
+ return RedirectToPage();
+ }
+
+ var receipt = await _db.Receipts.FirstOrDefaultAsync(r => r.Id == receiptId);
+ if (receipt == null)
+ {
+ Message = $"Receipt not found (ID {receiptId}).";
+ IsSuccess = false;
+ return RedirectToPage();
+ }
+
+ var transactionExists = await _db.Transactions.AnyAsync(t => t.Id == transactionId);
+ if (!transactionExists)
+ {
+ Message = $"Transaction not found (ID {transactionId}).";
+ IsSuccess = false;
+ return RedirectToPage();
+ }
+
+ // Friendly duplicate check: same file already mapped to this transaction
+ if (!string.IsNullOrWhiteSpace(receipt.FileHashSha256))
+ {
+ var duplicateExists = await _db.Receipts.AnyAsync(r => r.Id != receiptId && r.TransactionId == transactionId && r.FileHashSha256 == receipt.FileHashSha256);
+ if (duplicateExists)
+ {
+ Message = "This transaction already has a receipt with the same file (duplicate prevented).";
+ IsSuccess = false;
+ return RedirectToPage();
+ }
+ }
+
var success = await _receiptManager.MapReceiptToTransactionAsync(receiptId, transactionId);
if (success)
@@ -393,3 +428,4 @@ namespace MoneyMap.Pages
}
}
}
+
diff --git a/MoneyMap/Services/ReceiptManager.cs b/MoneyMap/Services/ReceiptManager.cs
index a6d9c3e..129071a 100644
--- a/MoneyMap/Services/ReceiptManager.cs
+++ b/MoneyMap/Services/ReceiptManager.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using MoneyMap.Data;
@@ -206,12 +206,9 @@ namespace MoneyMap.Services
if (transaction == null)
return false;
- // Check if this receipt is already mapped to another transaction
- if (receipt.TransactionId.HasValue && receipt.TransactionId.Value != transactionId)
- {
- // Could return a more specific error, but for now just return false
- return false;
- }
+ // Allow remapping: simply update the TransactionId
+ if (receipt.TransactionId == transactionId)
+ return true;
receipt.TransactionId = transactionId;
await _db.SaveChangesAsync();
@@ -228,7 +225,7 @@ namespace MoneyMap.Services
var sanitized = new StringBuilder();
foreach (var c in fileName)
{
- if (c == '®' || c == '™' || c == '©')
+ if (c == '®' || c == '™' || c == '©')
{
// Skip trademark/copyright symbols
continue;
@@ -313,4 +310,4 @@ namespace MoneyMap.Services
public string? TransactionName { get; set; }
public string Reason { get; set; } = "";
}
-}
\ No newline at end of file
+}