using System.Collections.ObjectModel; namespace BudgetApp.Domain.Models; /// /// Ledger aggregate containing all ledger entries. /// Provides calculations and operations on entries. /// public class Ledger { private readonly List _entries; public ReadOnlyCollection Entries => _entries.AsReadOnly(); public Ledger() { _entries = new List(); } public Ledger(IEnumerable entries) { if (entries == null) throw new ArgumentNullException(nameof(entries)); _entries = new List(entries); } /// /// Adds a ledger entry to the ledger. /// public void AddEntry(LedgerEntry entry) { if (entry == null) throw new ArgumentNullException(nameof(entry)); entry.Validate(); _entries.Add(entry); } /// /// Removes a ledger entry from the ledger. /// public void RemoveEntry(Guid entryId) { var entry = _entries.FirstOrDefault(e => e.Id == entryId); if (entry != null) { _entries.Remove(entry); } } /// /// Gets all entries of a specific type. /// public IEnumerable GetEntriesByType(EntryType type) { return _entries.Where(e => e.Type == type); } /// /// Gets entries within a date range. /// public IEnumerable GetEntriesByDateRange(DateTime startDate, DateTime endDate) { return _entries.Where(e => e.Date >= startDate && e.Date <= endDate); } /// /// Calculates the total amount for entries of a specific type. /// public Money CalculateTotalByType(EntryType type, string currency) { var total = _entries .Where(e => e.Type == type && e.Amount.Currency == currency) .Sum(e => e.Amount.Amount); return new Money(total, currency); } /// /// Calculates the total income minus total expenses (net balance). /// public Money CalculateNetBalance(string currency) { var income = CalculateTotalByType(EntryType.Income, currency); var expenses = CalculateTotalByType(EntryType.Expense, currency); var netAmount = income.Amount - expenses.Amount; return new Money(Math.Max(0, netAmount), currency); } /// /// Gets all entries that allocate to a specific budget. /// public IEnumerable GetEntriesForBudget(Guid budgetId) { return _entries.Where(e => e.BudgetAllocations.ContainsKey(budgetId)); } /// /// Calculates the total amount allocated to a specific budget from all entries. /// public Money CalculateTotalAllocatedToBudget(Guid budgetId, string currency) { var total = _entries .Where(e => e.BudgetAllocations.ContainsKey(budgetId)) .Sum(e => { if (!e.BudgetAllocations.TryGetValue(budgetId, out var allocation)) return 0; if (allocation.Currency != currency) return 0; // Income adds to budget, expenses subtract return e.Type == EntryType.Income ? allocation.Amount : -allocation.Amount; }); return new Money(Math.Max(0, total), currency); } }