using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using MoneyMap.Data; using MoneyMap.Models; using MoneyMap.Services; namespace MoneyMap.Pages { public class EditTransactionModel : PageModel { private readonly MoneyMapContext _db; private readonly IReceiptManager _receiptManager; private readonly IReceiptParser _receiptParser; private readonly IReferenceDataService _referenceDataService; private readonly IMerchantService _merchantService; public EditTransactionModel(MoneyMapContext db, IReceiptManager receiptManager, IReceiptParser receiptParser, IReferenceDataService referenceDataService, IMerchantService merchantService) { _db = db; _receiptManager = receiptManager; _receiptParser = receiptParser; _referenceDataService = referenceDataService; _merchantService = merchantService; } [BindProperty] public TransactionEditModel Transaction { get; set; } = new(); [BindProperty] public IFormFile? ReceiptFile { get; set; } [BindProperty] public bool UseCustomCategory { get; set; } [BindProperty] public bool UseCustomMerchant { get; set; } public List AvailableCategories { get; set; } = new(); public List AvailableMerchants { get; set; } = new(); public List Receipts { get; set; } = new(); [TempData] public string? SuccessMessage { get; set; } [TempData] public string? ErrorMessage { get; set; } public async Task OnGetAsync(long id) { var transaction = await _db.Transactions .Include(t => t.Card) .ThenInclude(c => c!.Account) .Include(t => t.Account) .Include(t => t.TransferToAccount) .Include(t => t.Merchant) .Include(t => t.Receipts) .ThenInclude(r => r.LineItems) .FirstOrDefaultAsync(t => t.Id == id); if (transaction == null) return NotFound(); Transaction = new TransactionEditModel { Id = transaction.Id, Date = transaction.Date, Name = transaction.Name, Memo = transaction.Memo, Amount = transaction.Amount, Category = transaction.Category ?? "", MerchantId = transaction.MerchantId, Notes = transaction.Notes ?? "", CardLabel = transaction.PaymentMethodLabel, AccountLabel = transaction.Card?.Account?.DisplayLabel ?? transaction.Account?.DisplayLabel ?? "None" }; Receipts = transaction.Receipts?.Select(r => new ReceiptWithItems { Receipt = r, LineItems = r.LineItems?.OrderBy(li => li.LineNumber).ToList() ?? new List() }).ToList() ?? new List(); await LoadAvailableCategoriesAsync(); await LoadAvailableMerchantsAsync(); // Check if current category exists in list UseCustomCategory = !string.IsNullOrWhiteSpace(Transaction.Category) && !AvailableCategories.Contains(Transaction.Category); // Check if current merchant exists in list UseCustomMerchant = Transaction.MerchantId.HasValue && !AvailableMerchants.Any(m => m.Id == Transaction.MerchantId.Value); return Page(); } public async Task OnPostAsync() { // Remove Category from model state validation if it's empty (category is optional) if (string.IsNullOrWhiteSpace(Transaction.Category)) { ModelState.Remove("Transaction.Category"); } // Remove Notes from model state validation if it's empty if (string.IsNullOrWhiteSpace(Transaction.Notes)) { ModelState.Remove("Transaction.Notes"); } // Remove MerchantName from validation (it's only used when creating new merchant) ModelState.Remove("Transaction.MerchantName"); if (!ModelState.IsValid) { await LoadDataAsync(); return Page(); } var transaction = await _db.Transactions.FindAsync(Transaction.Id); if (transaction == null) return NotFound(); // Update category and notes transaction.Category = string.IsNullOrWhiteSpace(Transaction.Category) ? "" : Transaction.Category.Trim(); transaction.Notes = string.IsNullOrWhiteSpace(Transaction.Notes) ? "" : Transaction.Notes.Trim(); // Update merchant if (!string.IsNullOrWhiteSpace(Transaction.MerchantName)) { // Create or get merchant if custom name was entered var merchantId = await _merchantService.GetOrCreateIdAsync(Transaction.MerchantName); transaction.MerchantId = merchantId; } else if (Transaction.MerchantId.HasValue && Transaction.MerchantId.Value > 0) { // Existing merchant was selected transaction.MerchantId = Transaction.MerchantId.Value; } else { // No merchant selected transaction.MerchantId = null; } await _db.SaveChangesAsync(); SuccessMessage = "Transaction updated successfully!"; return RedirectToPage(new { id = Transaction.Id }); } public async Task OnPostUploadReceiptAsync() { if (ReceiptFile == null) { ErrorMessage = "Please select a file to upload."; await LoadDataAsync(); return Page(); } var result = await _receiptManager.UploadReceiptAsync(Transaction.Id, ReceiptFile); if (result.IsSuccess) { SuccessMessage = "Receipt uploaded successfully!"; } else { ErrorMessage = result.ErrorMessage; } return RedirectToPage(new { id = Transaction.Id }); } public async Task OnPostUnmapReceiptAsync(long receiptId) { var receipt = await _db.Receipts.FindAsync(receiptId); if (receipt != null) { receipt.TransactionId = null; await _db.SaveChangesAsync(); SuccessMessage = "Receipt unmapped successfully! You can now find it on the Receipts page."; } else { ErrorMessage = "Receipt not found."; } return RedirectToPage(new { id = Transaction.Id }); } public async Task OnPostDeleteReceiptAsync(long receiptId) { var success = await _receiptManager.DeleteReceiptAsync(receiptId); if (success) { SuccessMessage = "Receipt deleted permanently!"; } else { ErrorMessage = "Failed to delete receipt."; } return RedirectToPage(new { id = Transaction.Id }); } public async Task OnPostParseReceiptAsync(long receiptId) { var result = await _receiptParser.ParseReceiptAsync(receiptId); if (result.IsSuccess) { SuccessMessage = result.Message; } else { ErrorMessage = result.Message; } return RedirectToPage(new { id = Transaction.Id }); } private async Task LoadDataAsync() { await LoadAvailableCategoriesAsync(); await LoadAvailableMerchantsAsync(); var transaction = await _db.Transactions .Include(t => t.Receipts) .ThenInclude(r => r.LineItems) .FirstOrDefaultAsync(t => t.Id == Transaction.Id); if (transaction != null) { Receipts = transaction.Receipts?.Select(r => new ReceiptWithItems { Receipt = r, LineItems = r.LineItems?.OrderBy(li => li.LineNumber).ToList() ?? new List() }).ToList() ?? new List(); } } private async Task LoadAvailableCategoriesAsync() { AvailableCategories = await _referenceDataService.GetAvailableCategoriesAsync(); } private async Task LoadAvailableMerchantsAsync() { AvailableMerchants = await _referenceDataService.GetAvailableMerchantsAsync(); } public class TransactionEditModel { public long Id { get; set; } public DateTime Date { get; set; } public string Name { get; set; } = ""; public string Memo { get; set; } = ""; public decimal Amount { get; set; } public string Category { get; set; } = ""; public int? MerchantId { get; set; } public string? MerchantName { get; set; } = ""; public string? Notes { get; set; } = ""; public string CardLabel { get; set; } = ""; public string AccountLabel { get; set; } = ""; } public class ReceiptWithItems { public Receipt Receipt { get; set; } = null!; public List LineItems { get; set; } = new(); } } }