Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public void ProcessPayment_Should_ThrowException_When_NoInoiceFoundForPaymentRef
{
var repo = new InvoiceRepository( );

Invoice invoice = null;
var paymentProcessor = new InvoiceService( repo );
Invoice invoice = null;
var paymentProcessor = new InvoiceService( repo );

var payment = new Payment( );
var failureMessage = "";
Expand All @@ -28,7 +28,7 @@ public void ProcessPayment_Should_ThrowException_When_NoInoiceFoundForPaymentRef
failureMessage = e.Message;
}

Assert.AreEqual( "There is no invoice matching this payment", failureMessage );
Assert.That( "There is no invoice matching this payment", Is.EqualTo(failureMessage) );
}

[Test]
Expand All @@ -51,7 +51,7 @@ public void ProcessPayment_Should_ReturnFailureMessage_When_NoPaymentNeeded( )

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "no payment needed", result );
Assert.That("no payment needed", Is.EqualTo(result));
}

[Test]
Expand Down Expand Up @@ -79,7 +79,7 @@ public void ProcessPayment_Should_ReturnFailureMessage_When_InvoiceAlreadyFullyP

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "invoice was already fully paid", result );
Assert.That("invoice was already fully paid", Is.EqualTo(result));
}

[Test]
Expand Down Expand Up @@ -109,7 +109,7 @@ public void ProcessPayment_Should_ReturnFailureMessage_When_PartialPaymentExists

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "the payment is greater than the partial amount remaining", result );
Assert.That("the payment is greater than the partial amount remaining", Is.EqualTo(result));
}

[Test]
Expand All @@ -133,7 +133,7 @@ public void ProcessPayment_Should_ReturnFailureMessage_When_NoPartialPaymentExis

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "the payment is greater than the invoice amount", result );
Assert.That("the payment is greater than the invoice amount", Is.EqualTo(result));
}

[Test]
Expand Down Expand Up @@ -163,7 +163,7 @@ public void ProcessPayment_Should_ReturnFullyPaidMessage_When_PartialPaymentExis

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "final partial payment received, invoice is now fully paid", result );
Assert.That( "final partial payment received, invoice is now fully paid", Is.EqualTo(result));
}

[Test]
Expand All @@ -187,7 +187,7 @@ public void ProcessPayment_Should_ReturnFullyPaidMessage_When_NoPartialPaymentEx

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "invoice was already fully paid", result );
Assert.That( "invoice was already fully paid", Is.EqualTo(result) );
}

[Test]
Expand Down Expand Up @@ -217,7 +217,7 @@ public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_PartialPayment

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "another partial payment received, still not fully paid", result );
Assert.That( "another partial payment received, still not fully paid", Is.EqualTo(result) );
}

[Test]
Expand All @@ -241,7 +241,7 @@ public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_NoPartialPayme

var result = paymentProcessor.ProcessPayment( payment );

Assert.AreEqual( "invoice is now partially paid", result );
Assert.That( "invoice is now partially paid", Is.EqualTo(result));
}
}
}
177 changes: 75 additions & 102 deletions RefactorThis.Domain/InvoiceService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,142 +8,115 @@ public class InvoiceService
{
private readonly InvoiceRepository _invoiceRepository;

public InvoiceService( InvoiceRepository invoiceRepository )
public InvoiceService(InvoiceRepository invoiceRepository)
{
_invoiceRepository = invoiceRepository;
}

public string ProcessPayment( Payment payment )
public string ProcessPayment(Payment payment)
{
var inv = _invoiceRepository.GetInvoice( payment.Reference );
Invoice inv = _invoiceRepository.GetInvoice(payment.Reference); // change var to Invoice to enable type checking
string responseMessage = string.Empty; // change var to string

var responseMessage = string.Empty;
if (inv == null) // return error message if no invoice found
{
throw new InvalidOperationException("There is no invoice matching this payment");
}

if ( inv == null )
// write the conditions as a boolean
bool checkAmountZero = inv.Amount == 0;
bool checkInvoiceHasNoPayment = inv.Payments == null || !inv.Payments.Any();
bool isInvoicePaid = checkAmountZero && checkInvoiceHasNoPayment;

if (isInvoicePaid)
{
throw new InvalidOperationException( "There is no invoice matching this payment" );
if (checkInvoiceHasNoPayment) // if invoice amount is 0 and has no payment, then no payment needed
{
responseMessage = "no payment needed";
}
else // throw an exception if ivalid invoice (0 amount but have payments, or non-zero amount but no payments)
{
throw new InvalidOperationException("The invoice is in an invalid state, it has an amount of 0 and it has payments.");
}
}
else
else // if not paid, proceed to payment process
{
if ( inv.Amount == 0 )
// write criteria and variables for payments.
bool checkInvoiceFullyPaid = inv.Amount == inv.Payments.Sum(x => x.Amount);
bool checkInvoiceHasPayment = inv.Payments.Sum(x => x.Amount) != 0;
decimal remainingInvoiceAmount = inv.Amount - inv.AmountPaid;
bool checkPaymentGreaterThanInvoiceAmount = payment.Amount > remainingInvoiceAmount;
bool checkPartialPaymentFull = (inv.Amount - inv.AmountPaid) == payment.Amount;
decimal paymentSubtractInvoiceAmount = payment.Amount - inv.Amount;

if (!checkInvoiceHasNoPayment) // if invoice already has payments
{
if ( inv.Payments == null || !inv.Payments.Any( ) )
if (checkInvoiceHasPayment && checkInvoiceFullyPaid)
{
responseMessage = "no payment needed";
responseMessage = "invoice was already fully paid";
}
else
else if (checkInvoiceHasPayment && checkPaymentGreaterThanInvoiceAmount)
{
throw new InvalidOperationException( "The invoice is in an invalid state, it has an amount of 0 and it has payments." );
responseMessage = "the payment is greater than the partial amount remaining";
}
}
else
{
if ( inv.Payments != null && inv.Payments.Any( ) )
else
{
if ( inv.Payments.Sum( x => x.Amount ) != 0 && inv.Amount == inv.Payments.Sum( x => x.Amount ) )
{
responseMessage = "invoice was already fully paid";
}
else if ( inv.Payments.Sum( x => x.Amount ) != 0 && payment.Amount > ( inv.Amount - inv.AmountPaid ) )
calculateAmountBasedOnInvType(inv, payment); // extract the switch case to a method to improve readability and easier future improvement

// at last, set the response message based on full or partial payment
if (checkPartialPaymentFull)
{
responseMessage = "the payment is greater than the partial amount remaining";
responseMessage = "final partial payment received, invoice is now fully paid";
}
else
{
if ( ( inv.Amount - inv.AmountPaid ) == payment.Amount )
{
switch ( inv.Type )
{
case InvoiceType.Standard:
inv.AmountPaid += payment.Amount;
inv.Payments.Add( payment );
responseMessage = "final partial payment received, invoice is now fully paid";
break;
case InvoiceType.Commercial:
inv.AmountPaid += payment.Amount;
inv.TaxAmount += payment.Amount * 0.14m;
inv.Payments.Add( payment );
responseMessage = "final partial payment received, invoice is now fully paid";
break;
default:
throw new ArgumentOutOfRangeException( );
}

}
else
{
switch ( inv.Type )
{
case InvoiceType.Standard:
inv.AmountPaid += payment.Amount;
inv.Payments.Add( payment );
responseMessage = "another partial payment received, still not fully paid";
break;
case InvoiceType.Commercial:
inv.AmountPaid += payment.Amount;
inv.TaxAmount += payment.Amount * 0.14m;
inv.Payments.Add( payment );
responseMessage = "another partial payment received, still not fully paid";
break;
default:
throw new ArgumentOutOfRangeException( );
}
}
responseMessage = "another partial payment received, still not fully paid";
}
}
}
else // otherwise, invoice has no existing payment
{
if (paymentSubtractInvoiceAmount > 0)
{
responseMessage = "the payment is greater than the invoice amount";
}
else
{
if ( payment.Amount > inv.Amount )
{
responseMessage = "the payment is greater than the invoice amount";
}
else if ( inv.Amount == payment.Amount )
calculateAmountBasedOnInvType(inv, payment);

// at last, set the response message based on full or partial payment
if (paymentSubtractInvoiceAmount == 0)
{
switch ( inv.Type )
{
case InvoiceType.Standard:
inv.AmountPaid = payment.Amount;
inv.TaxAmount = payment.Amount * 0.14m;
inv.Payments.Add( payment );
responseMessage = "invoice is now fully paid";
break;
case InvoiceType.Commercial:
inv.AmountPaid = payment.Amount;
inv.TaxAmount = payment.Amount * 0.14m;
inv.Payments.Add( payment );
responseMessage = "invoice is now fully paid";
break;
default:
throw new ArgumentOutOfRangeException( );
}
responseMessage = "invoice is now fully paid";
}
else
{
switch ( inv.Type )
{
case InvoiceType.Standard:
inv.AmountPaid = payment.Amount;
inv.TaxAmount = payment.Amount * 0.14m;
inv.Payments.Add( payment );
responseMessage = "invoice is now partially paid";
break;
case InvoiceType.Commercial:
inv.AmountPaid = payment.Amount;
inv.TaxAmount = payment.Amount * 0.14m;
inv.Payments.Add( payment );
responseMessage = "invoice is now partially paid";
break;
default:
throw new ArgumentOutOfRangeException( );
}
responseMessage = "invoice is now partially paid";
}
}
}
}

inv.Save();

inv.Save();
return responseMessage;
}


public void calculateAmountBasedOnInvType(Invoice inv, Payment payment)
{
switch (inv.Type) // do switch case first to remove further repitition
{
case InvoiceType.Standard:
break; // do nothing (I think here should not inclide tax amount? there should is a bug to include tax to standard invoice in the old code)
case InvoiceType.Commercial:
inv.TaxAmount += payment.Amount * 0.14m; // include tax for commercial invoice
break;
default:
throw new ArgumentOutOfRangeException();
}
// move below code out of switch statement to avoid repetition
inv.AmountPaid += payment.Amount;
inv.Payments.Add(payment);
}
}
}