Refactor: migrate PageModels to use new service layer
Refactor 7 PageModels to delegate business logic to services: - AccountDetails: Use AccountService for account details retrieval - Accounts: Use AccountService for listing and deletion - Cards: Use CardService for listing and deletion - EditTransaction: Use ReferenceDataService for dropdowns - Merchants: Use MerchantService for CRUD operations - Recategorize: Use ReferenceDataService and TransactionStatisticsService - Transactions: Use ReferenceDataService and TransactionStatisticsService Significantly simplifies PageModels (229 lines removed, 97 added) by extracting data access and business logic into testable services. Pages now focus solely on HTTP request/response handling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,16 +3,19 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MoneyMap.Data;
|
||||
using MoneyMap.Models;
|
||||
using MoneyMap.Services;
|
||||
|
||||
namespace MoneyMap.Pages
|
||||
{
|
||||
public class AccountDetailsModel : PageModel
|
||||
{
|
||||
private readonly MoneyMapContext _db;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ICardService _cardService;
|
||||
|
||||
public AccountDetailsModel(MoneyMapContext db)
|
||||
public AccountDetailsModel(IAccountService accountService, ICardService cardService)
|
||||
{
|
||||
_db = db;
|
||||
_accountService = accountService;
|
||||
_cardService = cardService;
|
||||
}
|
||||
|
||||
public Account Account { get; set; } = null!;
|
||||
@@ -27,67 +30,35 @@ namespace MoneyMap.Pages
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(int id)
|
||||
{
|
||||
var account = await _db.Accounts.FindAsync(id);
|
||||
if (account == null)
|
||||
var accountDetails = await _accountService.GetAccountDetailsAsync(id);
|
||||
if (accountDetails == null)
|
||||
return NotFound();
|
||||
|
||||
Account = account;
|
||||
|
||||
// Get cards linked to this account
|
||||
var cards = await _db.Cards
|
||||
.Where(c => c.AccountId == id)
|
||||
.OrderBy(c => c.Owner)
|
||||
.ThenBy(c => c.Last4)
|
||||
.ToListAsync();
|
||||
|
||||
var cardStats = new List<CardWithStats>();
|
||||
foreach (var card in cards)
|
||||
{
|
||||
var transactionCount = await _db.Transactions.CountAsync(t => t.CardId == card.Id);
|
||||
cardStats.Add(new CardWithStats
|
||||
{
|
||||
Card = card,
|
||||
TransactionCount = transactionCount
|
||||
});
|
||||
}
|
||||
|
||||
Cards = cardStats;
|
||||
|
||||
// Get transaction count for this account
|
||||
TransactionCount = await _db.Transactions.CountAsync(t => t.AccountId == id);
|
||||
Account = accountDetails.Account;
|
||||
Cards = accountDetails.Cards;
|
||||
TransactionCount = accountDetails.TransactionCount;
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostDeleteCardAsync(int cardId)
|
||||
{
|
||||
var card = await _db.Cards.FindAsync(cardId);
|
||||
if (card == null)
|
||||
// Get card to retrieve account ID before deletion
|
||||
var card = await _cardService.GetCardByIdAsync(cardId);
|
||||
var accountId = card?.AccountId;
|
||||
|
||||
var result = await _cardService.DeleteCardAsync(cardId);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
ErrorMessage = "Card not found.";
|
||||
return RedirectToPage(new { id = card?.AccountId });
|
||||
SuccessMessage = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage = result.Message;
|
||||
}
|
||||
|
||||
var accountId = card.AccountId;
|
||||
|
||||
var transactionCount = await _db.Transactions.CountAsync(t => t.CardId == card.Id);
|
||||
if (transactionCount > 0)
|
||||
{
|
||||
ErrorMessage = $"Cannot delete card. It has {transactionCount} transaction(s) associated with it.";
|
||||
return RedirectToPage(new { id = accountId });
|
||||
}
|
||||
|
||||
_db.Cards.Remove(card);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
SuccessMessage = "Card deleted successfully.";
|
||||
return RedirectToPage(new { id = accountId });
|
||||
}
|
||||
|
||||
public class CardWithStats
|
||||
{
|
||||
public Card Card { get; set; } = null!;
|
||||
public int TransactionCount { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,17 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MoneyMap.Data;
|
||||
using MoneyMap.Models;
|
||||
using MoneyMap.Services;
|
||||
|
||||
namespace MoneyMap.Pages
|
||||
{
|
||||
public class AccountsModel : PageModel
|
||||
{
|
||||
private readonly MoneyMapContext _db;
|
||||
private readonly IAccountService _accountService;
|
||||
|
||||
public AccountsModel(MoneyMapContext db)
|
||||
public AccountsModel(IAccountService accountService)
|
||||
{
|
||||
_db = db;
|
||||
_accountService = accountService;
|
||||
}
|
||||
|
||||
public List<AccountRow> Accounts { get; set; } = new();
|
||||
@@ -22,14 +23,9 @@ namespace MoneyMap.Pages
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
var accounts = await _db.Accounts
|
||||
.Include(a => a.Transactions)
|
||||
.OrderBy(a => a.Owner)
|
||||
.ThenBy(a => a.Institution)
|
||||
.ThenBy(a => a.Last4)
|
||||
.ToListAsync();
|
||||
var accountsWithStats = await _accountService.GetAllAccountsWithStatsAsync();
|
||||
|
||||
Accounts = accounts.Select(a => new AccountRow
|
||||
Accounts = accountsWithStats.Select(a => new AccountRow
|
||||
{
|
||||
Id = a.Id,
|
||||
Institution = a.Institution,
|
||||
@@ -37,30 +33,25 @@ namespace MoneyMap.Pages
|
||||
Last4 = a.Last4,
|
||||
Owner = a.Owner,
|
||||
Nickname = a.Nickname,
|
||||
TransactionCount = a.Transactions.Count
|
||||
TransactionCount = a.TransactionCount
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostDeleteAsync(int id)
|
||||
{
|
||||
var account = await _db.Accounts
|
||||
.Include(a => a.Transactions)
|
||||
.FirstOrDefaultAsync(a => a.Id == id);
|
||||
var result = await _accountService.DeleteAccountAsync(id);
|
||||
|
||||
if (account == null)
|
||||
return NotFound();
|
||||
|
||||
if (account.Transactions.Any())
|
||||
if (result.Success)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Cannot delete account with existing transactions.");
|
||||
SuccessMessage = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, result.Message);
|
||||
await OnGetAsync();
|
||||
return Page();
|
||||
}
|
||||
|
||||
_db.Accounts.Remove(account);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
SuccessMessage = $"Deleted account {account.Institution} {account.Last4}";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,17 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MoneyMap.Data;
|
||||
using MoneyMap.Models;
|
||||
using MoneyMap.Services;
|
||||
|
||||
namespace MoneyMap.Pages
|
||||
{
|
||||
public class CardsModel : PageModel
|
||||
{
|
||||
private readonly MoneyMapContext _db;
|
||||
private readonly ICardService _cardService;
|
||||
|
||||
public CardsModel(MoneyMapContext db)
|
||||
public CardsModel(ICardService cardService)
|
||||
{
|
||||
_db = db;
|
||||
_cardService = cardService;
|
||||
}
|
||||
|
||||
public List<CardWithStats> Cards { get; set; } = new();
|
||||
@@ -25,55 +26,23 @@ namespace MoneyMap.Pages
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
var cards = await _db.Cards
|
||||
.Include(c => c.Account)
|
||||
.OrderBy(c => c.Owner)
|
||||
.ThenBy(c => c.Last4)
|
||||
.ToListAsync();
|
||||
|
||||
var cardStats = new List<CardWithStats>();
|
||||
|
||||
foreach (var card in cards)
|
||||
{
|
||||
var transactionCount = await _db.Transactions.CountAsync(t => t.CardId == card.Id);
|
||||
|
||||
cardStats.Add(new CardWithStats
|
||||
{
|
||||
Card = card,
|
||||
TransactionCount = transactionCount
|
||||
});
|
||||
}
|
||||
|
||||
Cards = cardStats;
|
||||
Cards = await _cardService.GetAllCardsWithStatsAsync();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostDeleteAsync(int id)
|
||||
{
|
||||
var card = await _db.Cards.FindAsync(id);
|
||||
if (card == null)
|
||||
var result = await _cardService.DeleteCardAsync(id);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
ErrorMessage = "Card not found.";
|
||||
return RedirectToPage();
|
||||
SuccessMessage = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage = result.Message;
|
||||
}
|
||||
|
||||
var transactionCount = await _db.Transactions.CountAsync(t => t.CardId == card.Id);
|
||||
if (transactionCount > 0)
|
||||
{
|
||||
ErrorMessage = $"Cannot delete card. It has {transactionCount} transaction(s) associated with it.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
_db.Cards.Remove(card);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
SuccessMessage = "Card deleted successfully.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
public class CardWithStats
|
||||
{
|
||||
public Card Card { get; set; } = null!;
|
||||
public int TransactionCount { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,16 @@ namespace MoneyMap.Pages
|
||||
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)
|
||||
public EditTransactionModel(MoneyMapContext db, IReceiptManager receiptManager, IReceiptParser receiptParser, IReferenceDataService referenceDataService, IMerchantService merchantService)
|
||||
{
|
||||
_db = db;
|
||||
_receiptManager = receiptManager;
|
||||
_receiptParser = receiptParser;
|
||||
_referenceDataService = referenceDataService;
|
||||
_merchantService = merchantService;
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
@@ -124,22 +128,9 @@ namespace MoneyMap.Pages
|
||||
// Update merchant
|
||||
if (!string.IsNullOrWhiteSpace(Transaction.MerchantName))
|
||||
{
|
||||
// Create new merchant if custom name was entered
|
||||
var merchantName = Transaction.MerchantName.Trim();
|
||||
var existingMerchant = await _db.Merchants
|
||||
.FirstOrDefaultAsync(m => m.Name == merchantName);
|
||||
|
||||
if (existingMerchant != null)
|
||||
{
|
||||
transaction.MerchantId = existingMerchant.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newMerchant = new Merchant { Name = merchantName };
|
||||
_db.Merchants.Add(newMerchant);
|
||||
await _db.SaveChangesAsync();
|
||||
transaction.MerchantId = newMerchant.Id;
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
@@ -252,19 +243,12 @@ namespace MoneyMap.Pages
|
||||
|
||||
private async Task LoadAvailableCategoriesAsync()
|
||||
{
|
||||
AvailableCategories = await _db.Transactions
|
||||
.Select(t => t.Category ?? "")
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c))
|
||||
.Distinct()
|
||||
.OrderBy(c => c)
|
||||
.ToListAsync();
|
||||
AvailableCategories = await _referenceDataService.GetAvailableCategoriesAsync();
|
||||
}
|
||||
|
||||
private async Task LoadAvailableMerchantsAsync()
|
||||
{
|
||||
AvailableMerchants = await _db.Merchants
|
||||
.OrderBy(m => m.Name)
|
||||
.ToListAsync();
|
||||
AvailableMerchants = await _referenceDataService.GetAvailableMerchantsAsync();
|
||||
}
|
||||
|
||||
public class TransactionEditModel
|
||||
|
||||
@@ -3,17 +3,18 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MoneyMap.Data;
|
||||
using MoneyMap.Models;
|
||||
using MoneyMap.Services;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MoneyMap.Pages
|
||||
{
|
||||
public class MerchantsModel : PageModel
|
||||
{
|
||||
private readonly MoneyMapContext _db;
|
||||
private readonly IMerchantService _merchantService;
|
||||
|
||||
public MerchantsModel(MoneyMapContext db)
|
||||
public MerchantsModel(IMerchantService merchantService)
|
||||
{
|
||||
_db = db;
|
||||
_merchantService = merchantService;
|
||||
}
|
||||
|
||||
public List<MerchantRow> Merchants { get; set; } = new();
|
||||
@@ -40,9 +41,7 @@ namespace MoneyMap.Pages
|
||||
}
|
||||
|
||||
// Check if merchant already exists
|
||||
var existing = await _db.Merchants
|
||||
.FirstOrDefaultAsync(m => m.Name == model.Name.Trim());
|
||||
|
||||
var existing = await _merchantService.FindByNameAsync(model.Name);
|
||||
if (existing != null)
|
||||
{
|
||||
ErrorMessage = $"Merchant '{model.Name}' already exists.";
|
||||
@@ -50,13 +49,7 @@ namespace MoneyMap.Pages
|
||||
return Page();
|
||||
}
|
||||
|
||||
var merchant = new Merchant
|
||||
{
|
||||
Name = model.Name.Trim()
|
||||
};
|
||||
|
||||
_db.Merchants.Add(merchant);
|
||||
await _db.SaveChangesAsync();
|
||||
var merchant = await _merchantService.GetOrCreateAsync(model.Name);
|
||||
|
||||
SuccessMessage = $"Added merchant '{merchant.Name}'.";
|
||||
return RedirectToPage();
|
||||
@@ -71,68 +64,48 @@ namespace MoneyMap.Pages
|
||||
return Page();
|
||||
}
|
||||
|
||||
var merchant = await _db.Merchants.FindAsync(model.Id);
|
||||
if (merchant == null)
|
||||
var result = await _merchantService.UpdateMerchantAsync(model.Id, model.Name);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
ErrorMessage = "Merchant not found.";
|
||||
return RedirectToPage();
|
||||
SuccessMessage = result.Message;
|
||||
}
|
||||
|
||||
// Check if another merchant with the same name exists
|
||||
var existing = await _db.Merchants
|
||||
.FirstOrDefaultAsync(m => m.Name == model.Name.Trim() && m.Id != model.Id);
|
||||
|
||||
if (existing != null)
|
||||
else
|
||||
{
|
||||
ErrorMessage = $"Merchant '{model.Name}' already exists.";
|
||||
ErrorMessage = result.Message;
|
||||
await LoadDataAsync();
|
||||
return Page();
|
||||
}
|
||||
|
||||
merchant.Name = model.Name.Trim();
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
SuccessMessage = "Merchant updated successfully.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostDeleteMerchantAsync(int id)
|
||||
{
|
||||
var merchant = await _db.Merchants
|
||||
.Include(m => m.Transactions)
|
||||
.Include(m => m.CategoryMappings)
|
||||
.FirstOrDefaultAsync(m => m.Id == id);
|
||||
var result = await _merchantService.DeleteMerchantAsync(id);
|
||||
|
||||
if (merchant == null)
|
||||
if (result.Success)
|
||||
{
|
||||
ErrorMessage = "Merchant not found.";
|
||||
return RedirectToPage();
|
||||
SuccessMessage = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage = result.Message;
|
||||
}
|
||||
|
||||
var transactionCount = merchant.Transactions.Count;
|
||||
var mappingCount = merchant.CategoryMappings.Count;
|
||||
|
||||
_db.Merchants.Remove(merchant);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
SuccessMessage = $"Deleted merchant '{merchant.Name}'. {transactionCount} transactions and {mappingCount} category mappings are now unlinked.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
var merchants = await _db.Merchants
|
||||
.Include(m => m.Transactions)
|
||||
.Include(m => m.CategoryMappings)
|
||||
.OrderBy(m => m.Name)
|
||||
.ToListAsync();
|
||||
var merchantsWithStats = await _merchantService.GetAllMerchantsWithStatsAsync();
|
||||
|
||||
Merchants = merchants.Select(m => new MerchantRow
|
||||
Merchants = merchantsWithStats.Select(m => new MerchantRow
|
||||
{
|
||||
Id = m.Id,
|
||||
Name = m.Name,
|
||||
TransactionCount = m.Transactions.Count,
|
||||
MappingCount = m.CategoryMappings.Count
|
||||
TransactionCount = m.TransactionCount,
|
||||
MappingCount = m.MappingCount
|
||||
}).ToList();
|
||||
|
||||
TotalMerchants = Merchants.Count;
|
||||
|
||||
@@ -10,11 +10,13 @@ namespace MoneyMap.Pages
|
||||
{
|
||||
private readonly MoneyMapContext _db;
|
||||
private readonly ITransactionCategorizer _categorizer;
|
||||
private readonly ITransactionStatisticsService _statsService;
|
||||
|
||||
public RecategorizeModel(MoneyMapContext db, ITransactionCategorizer categorizer)
|
||||
public RecategorizeModel(MoneyMapContext db, ITransactionCategorizer categorizer, ITransactionStatisticsService statsService)
|
||||
{
|
||||
_db = db;
|
||||
_categorizer = categorizer;
|
||||
_statsService = statsService;
|
||||
}
|
||||
|
||||
public RecategorizeStats Stats { get; set; } = new();
|
||||
@@ -47,16 +49,13 @@ namespace MoneyMap.Pages
|
||||
|
||||
private async Task LoadStatsAsync()
|
||||
{
|
||||
var totalTransactions = await _db.Transactions.CountAsync();
|
||||
var uncategorized = await _db.Transactions
|
||||
.CountAsync(t => string.IsNullOrWhiteSpace(t.Category));
|
||||
var categorized = totalTransactions - uncategorized;
|
||||
var categorizationStats = await _statsService.GetCategorizationStatsAsync();
|
||||
|
||||
Stats = new RecategorizeStats
|
||||
{
|
||||
TotalTransactions = totalTransactions,
|
||||
Categorized = categorized,
|
||||
Uncategorized = uncategorized
|
||||
TotalTransactions = categorizationStats.TotalTransactions,
|
||||
Categorized = categorizationStats.Categorized,
|
||||
Uncategorized = categorizationStats.Uncategorized
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,21 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MoneyMap.Data;
|
||||
using MoneyMap.Models;
|
||||
using MoneyMap.Services;
|
||||
|
||||
namespace MoneyMap.Pages
|
||||
{
|
||||
public class TransactionsModel : PageModel
|
||||
{
|
||||
private readonly MoneyMapContext _db;
|
||||
private readonly ITransactionStatisticsService _statsService;
|
||||
private readonly IReferenceDataService _referenceDataService;
|
||||
|
||||
public TransactionsModel(MoneyMapContext db)
|
||||
public TransactionsModel(MoneyMapContext db, ITransactionStatisticsService statsService, IReferenceDataService referenceDataService)
|
||||
{
|
||||
_db = db;
|
||||
_statsService = statsService;
|
||||
_referenceDataService = referenceDataService;
|
||||
}
|
||||
|
||||
[BindProperty(SupportsGet = true)]
|
||||
@@ -147,33 +152,17 @@ namespace MoneyMap.Pages
|
||||
}).ToList();
|
||||
|
||||
// Calculate stats for filtered results (all pages, not just current)
|
||||
var allFilteredTransactions = await query.ToListAsync();
|
||||
Stats = new TransactionStats
|
||||
{
|
||||
Count = allFilteredTransactions.Count,
|
||||
TotalDebits = allFilteredTransactions.Where(t => t.Amount < 0).Sum(t => t.Amount),
|
||||
TotalCredits = allFilteredTransactions.Where(t => t.Amount > 0).Sum(t => t.Amount),
|
||||
NetAmount = allFilteredTransactions.Sum(t => t.Amount)
|
||||
};
|
||||
Stats = await _statsService.CalculateStatsAsync(query);
|
||||
|
||||
// Get available categories for filter dropdown
|
||||
AvailableCategories = await _db.Transactions
|
||||
.Select(t => t.Category ?? "")
|
||||
.Distinct()
|
||||
.OrderBy(c => c)
|
||||
.ToListAsync();
|
||||
AvailableCategories = await _referenceDataService.GetAvailableCategoriesAsync();
|
||||
|
||||
// Get available merchants for filter dropdown
|
||||
AvailableMerchants = await _db.Merchants
|
||||
.OrderBy(m => m.Name)
|
||||
.Select(m => m.Name)
|
||||
.ToListAsync();
|
||||
var merchants = await _referenceDataService.GetAvailableMerchantsAsync();
|
||||
AvailableMerchants = merchants.Select(m => m.Name).ToList();
|
||||
|
||||
// Get available cards for filter dropdown
|
||||
AvailableCards = await _db.Cards
|
||||
.OrderBy(c => c.Owner)
|
||||
.ThenBy(c => c.Last4)
|
||||
.ToListAsync();
|
||||
AvailableCards = await _referenceDataService.GetAvailableCardsAsync(includeAccount: false);
|
||||
}
|
||||
|
||||
public class TransactionRow
|
||||
@@ -189,13 +178,5 @@ namespace MoneyMap.Pages
|
||||
public string AccountLabel { get; set; } = "";
|
||||
public int ReceiptCount { get; set; }
|
||||
}
|
||||
|
||||
public class TransactionStats
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public decimal TotalDebits { get; set; }
|
||||
public decimal TotalCredits { get; set; }
|
||||
public decimal NetAmount { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user