diff --git a/csharp/src/OrderDomain/Order.cs b/csharp/src/OrderDomain/Order.cs index 5205729..494e89d 100644 --- a/csharp/src/OrderDomain/Order.cs +++ b/csharp/src/OrderDomain/Order.cs @@ -2,12 +2,22 @@ namespace OrderDomain; public class Order { + private readonly List payments = new(); + public List Lines { get; } = new(); public decimal Total => Lines.Sum(l => l.Value); public bool IsCompleted { get; private set; } + public IReadOnlyList Payments => payments; + + public decimal AmountPaid => payments.Sum(); + + public decimal Outstanding => Total - AmountPaid; + + public bool IsFullyPaid => IsCompleted && Outstanding == 0m; + public void AddLine(decimal unitPrice, int quantity) => Lines.Add(new OrderLine(unitPrice, quantity)); @@ -16,6 +26,14 @@ public class Order public void Complete() => IsCompleted = true; - public bool IsFullyPaid(decimal expectedTotal) - => IsCompleted && Total == expectedTotal; + public void Pay(decimal amount) + { + if (!IsCompleted) + throw new InvalidOperationException("Cannot pay an open order."); + if (amount <= 0m) + throw new ArgumentOutOfRangeException(nameof(amount), amount, "Payment must be positive."); + if (amount > Outstanding) + throw new InvalidOperationException("Payment exceeds outstanding amount."); + payments.Add(amount); + } } diff --git a/csharp/tests/OrderTests/OrderCompletionTests.cs b/csharp/tests/OrderTests/OrderCompletionTests.cs index b7547a4..9401ba1 100644 --- a/csharp/tests/OrderTests/OrderCompletionTests.cs +++ b/csharp/tests/OrderTests/OrderCompletionTests.cs @@ -12,6 +12,61 @@ public class OrderCompletionTests order.AddLine(100m, 1); order.Complete(); - Assert.That(order.IsFullyPaid(100m), Is.True); + Assert.That(order.IsCompleted && order.Total == 100m, Is.True); + } + + [Test] + public void PaymentOnOpenOrder_Throws_OrderStillUnpaid() + { + var order = new Order(); + order.AddLine(100m, 1); + + Assert.Throws(() => order.Pay(100m)); + + Assert.That(order.IsCompleted, Is.False); + Assert.That(order.AmountPaid, Is.EqualTo(0m)); + } + + [Test] + public void CompletedOrder_PartialPayment_IsNotFullyPaid() + { + var order = new Order(); + order.AddLine(100m, 1); + order.Complete(); + + order.Pay(40m); + + Assert.That(order.AmountPaid, Is.EqualTo(40m)); + Assert.That(order.Outstanding, Is.EqualTo(60m)); + Assert.That(order.IsFullyPaid, Is.False); + } + + [Test] + public void CompletedOrder_PaymentsSummingToTotal_IsFullyPaid() + { + var order = new Order(); + order.AddLine(100m, 1); + order.Complete(); + + order.Pay(40m); + order.Pay(60m); + + Assert.That(order.Payments, Is.EqualTo(new[] { 40m, 60m })); + Assert.That(order.Outstanding, Is.EqualTo(0m)); + Assert.That(order.IsFullyPaid, Is.True); + } + + [Test] + public void Overpayment_Throws_AmountPaidUnchanged() + { + var order = new Order(); + order.AddLine(100m, 1); + order.Complete(); + order.Pay(100m); + + Assert.Throws(() => order.Pay(10m)); + + Assert.That(order.AmountPaid, Is.EqualTo(100m)); + Assert.That(order.IsFullyPaid, Is.True); } }