2026-01-03 10:29:03 -06:00

123 lines
3.5 KiB
C#

using System.Collections.ObjectModel;
namespace BudgetApp.Domain.Models;
/// <summary>
/// Ledger aggregate containing all ledger entries.
/// Provides calculations and operations on entries.
/// </summary>
public class Ledger
{
private readonly List<LedgerEntry> _entries;
public ReadOnlyCollection<LedgerEntry> Entries => _entries.AsReadOnly();
public Ledger()
{
_entries = new List<LedgerEntry>();
}
public Ledger(IEnumerable<LedgerEntry> entries)
{
if (entries == null)
throw new ArgumentNullException(nameof(entries));
_entries = new List<LedgerEntry>(entries);
}
/// <summary>
/// Adds a ledger entry to the ledger.
/// </summary>
public void AddEntry(LedgerEntry entry)
{
if (entry == null)
throw new ArgumentNullException(nameof(entry));
entry.Validate();
_entries.Add(entry);
}
/// <summary>
/// Removes a ledger entry from the ledger.
/// </summary>
public void RemoveEntry(Guid entryId)
{
var entry = _entries.FirstOrDefault(e => e.Id == entryId);
if (entry != null)
{
_entries.Remove(entry);
}
}
/// <summary>
/// Gets all entries of a specific type.
/// </summary>
public IEnumerable<LedgerEntry> GetEntriesByType(EntryType type)
{
return _entries.Where(e => e.Type == type);
}
/// <summary>
/// Gets entries within a date range.
/// </summary>
public IEnumerable<LedgerEntry> GetEntriesByDateRange(DateTime startDate, DateTime endDate)
{
return _entries.Where(e => e.Date >= startDate && e.Date <= endDate);
}
/// <summary>
/// Calculates the total amount for entries of a specific type.
/// </summary>
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);
}
/// <summary>
/// Calculates the total income minus total expenses (net balance).
/// </summary>
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);
}
/// <summary>
/// Gets all entries that allocate to a specific budget.
/// </summary>
public IEnumerable<LedgerEntry> GetEntriesForBudget(Guid budgetId)
{
return _entries.Where(e => e.BudgetAllocations.ContainsKey(budgetId));
}
/// <summary>
/// Calculates the total amount allocated to a specific budget from all entries.
/// </summary>
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);
}
}