Feature: add ability to unmap receipts from transactions
Add unmap functionality to allow users to disassociate receipts from transactions without deleting them: - ReceiptManager: Add UnmapReceiptAsync method - Receipts page: Add OnPostUnmapAsync handler - Receipts view: Add Unmap button for mapped receipts with confirmation dialog This provides a non-destructive alternative to deleting receipts when they need to be remapped to different transactions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -235,6 +235,14 @@
|
|||||||
Map
|
Map
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<form method="post" asp-page-handler="Unmap" asp-route-receiptId="@r.Id" style="display: inline;" onsubmit="return confirm('Are you sure you want to unmap this receipt from the transaction?');">
|
||||||
|
<button type="submit" class="btn btn-outline-warning btn-sm" title="Unmap from Transaction">
|
||||||
|
Unmap
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
<form method="post" asp-page-handler="Delete" asp-route-receiptId="@r.Id" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this receipt?');">
|
<form method="post" asp-page-handler="Delete" asp-route-receiptId="@r.Id" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this receipt?');">
|
||||||
<button type="submit" class="btn btn-outline-danger btn-sm" title="Delete">
|
<button type="submit" class="btn btn-outline-danger btn-sm" title="Delete">
|
||||||
Delete
|
Delete
|
||||||
|
|||||||
@@ -218,6 +218,24 @@ namespace MoneyMap.Pages
|
|||||||
return RedirectToPage();
|
return RedirectToPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnPostUnmapAsync(long receiptId)
|
||||||
|
{
|
||||||
|
var success = await _receiptManager.UnmapReceiptAsync(receiptId);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
Message = "Receipt unmapped successfully.";
|
||||||
|
IsSuccess = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message = "Failed to unmap receipt.";
|
||||||
|
IsSuccess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedirectToPage();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LoadReceiptsAsync()
|
private async Task LoadReceiptsAsync()
|
||||||
{
|
{
|
||||||
var receipts = await _db.Receipts
|
var receipts = await _db.Receipts
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace MoneyMap.Services
|
|||||||
Task<ReceiptUploadResult> UploadUnmappedReceiptAsync(IFormFile file);
|
Task<ReceiptUploadResult> UploadUnmappedReceiptAsync(IFormFile file);
|
||||||
Task<bool> DeleteReceiptAsync(long receiptId);
|
Task<bool> DeleteReceiptAsync(long receiptId);
|
||||||
Task<bool> MapReceiptToTransactionAsync(long receiptId, long transactionId);
|
Task<bool> MapReceiptToTransactionAsync(long receiptId, long transactionId);
|
||||||
|
Task<bool> UnmapReceiptAsync(long receiptId);
|
||||||
string GetReceiptPhysicalPath(Receipt receipt);
|
string GetReceiptPhysicalPath(Receipt receipt);
|
||||||
Task<Receipt?> GetReceiptAsync(long receiptId);
|
Task<Receipt?> GetReceiptAsync(long receiptId);
|
||||||
}
|
}
|
||||||
@@ -210,6 +211,19 @@ namespace MoneyMap.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UnmapReceiptAsync(long receiptId)
|
||||||
|
{
|
||||||
|
var receipt = await _db.Receipts.FindAsync(receiptId);
|
||||||
|
if (receipt == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Set TransactionId to null to unmap
|
||||||
|
receipt.TransactionId = null;
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static string SanitizeFileName(string fileName)
|
private static string SanitizeFileName(string fileName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(fileName))
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
@@ -219,7 +233,7 @@ namespace MoneyMap.Services
|
|||||||
var sanitized = new StringBuilder();
|
var sanitized = new StringBuilder();
|
||||||
foreach (var c in fileName)
|
foreach (var c in fileName)
|
||||||
{
|
{
|
||||||
if (c == '®' || c == '™' || c == '©')
|
if (c == '<EFBFBD>' || c == '<EFBFBD>' || c == '<EFBFBD>')
|
||||||
{
|
{
|
||||||
// Skip trademark/copyright symbols
|
// Skip trademark/copyright symbols
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user