114 lines
3.4 KiB
C#
114 lines
3.4 KiB
C#
namespace BudgetApp.Domain.Models;
|
|
|
|
/// <summary>
|
|
/// Represents a ledger entry for tracking money in/out of budgets.
|
|
/// </summary>
|
|
public class LedgerEntry
|
|
{
|
|
public Guid Id { get; protected set; }
|
|
public DateTime Date { get; protected set; }
|
|
public string Description { get; protected set; } = string.Empty;
|
|
public Money Amount { get; protected set; } = null!;
|
|
public EntryType Type { get; protected set; }
|
|
public Dictionary<Guid, Money> BudgetAllocations { get; protected set; }
|
|
|
|
protected LedgerEntry()
|
|
{
|
|
BudgetAllocations = new Dictionary<Guid, Money>();
|
|
}
|
|
|
|
public LedgerEntry(
|
|
Guid id,
|
|
DateTime date,
|
|
string description,
|
|
Money amount,
|
|
EntryType type,
|
|
Dictionary<Guid, Money> budgetAllocations)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(description))
|
|
throw new ArgumentException("Description cannot be null or empty.", nameof(description));
|
|
|
|
if (amount == null)
|
|
throw new ArgumentNullException(nameof(amount));
|
|
|
|
if (budgetAllocations == null)
|
|
throw new ArgumentNullException(nameof(budgetAllocations));
|
|
|
|
Id = id;
|
|
Date = date;
|
|
Description = description;
|
|
Amount = amount;
|
|
Type = type;
|
|
BudgetAllocations = new Dictionary<Guid, Money>(budgetAllocations);
|
|
|
|
Validate();
|
|
}
|
|
|
|
public LedgerEntry(
|
|
DateTime date,
|
|
string description,
|
|
Money amount,
|
|
EntryType type,
|
|
Dictionary<Guid, Money> budgetAllocations)
|
|
: this(Guid.NewGuid(), date, description, amount, type, budgetAllocations)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates that budget allocations sum to the entry amount.
|
|
/// </summary>
|
|
public void Validate()
|
|
{
|
|
if (Type == EntryType.Expense)
|
|
{
|
|
ValidateExpenseBalance();
|
|
}
|
|
else if (Type == EntryType.Income)
|
|
{
|
|
ValidateIncomeBalance();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates that expense budget allocations sum to the expense amount.
|
|
/// </summary>
|
|
private void ValidateExpenseBalance()
|
|
{
|
|
if (BudgetAllocations.Count == 0)
|
|
throw new InvalidOperationException("Expense must have at least one budget allocation.");
|
|
|
|
var totalAllocated = BudgetAllocations.Values
|
|
.Aggregate(new Money(0, Amount.Currency), (sum, money) => sum.Add(money));
|
|
|
|
if (totalAllocated.Amount != Amount.Amount)
|
|
throw new InvalidOperationException(
|
|
$"Expense amount ({Amount.Amount}) does not match sum of budget allocations ({totalAllocated.Amount}).");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates that income budget allocations sum to the income amount.
|
|
/// </summary>
|
|
private void ValidateIncomeBalance()
|
|
{
|
|
if (BudgetAllocations.Count == 0)
|
|
throw new InvalidOperationException("Income must have at least one budget allocation.");
|
|
|
|
var totalAllocated = BudgetAllocations.Values
|
|
.Aggregate(new Money(0, Amount.Currency), (sum, money) => sum.Add(money));
|
|
|
|
if (totalAllocated.Amount != Amount.Amount)
|
|
throw new InvalidOperationException(
|
|
$"Income amount ({Amount.Amount}) does not match sum of budget allocations ({totalAllocated.Amount}).");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type of ledger entry.
|
|
/// </summary>
|
|
public enum EntryType
|
|
{
|
|
Income,
|
|
Expense
|
|
}
|
|
|