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
36 changes: 1 addition & 35 deletions RefactorThis.Domain.Tests/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1 @@
using System.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle( "RefactorThis.Domain.Tests" )]
[assembly: AssemblyDescription( "" )]
[assembly: AssemblyConfiguration( "" )]
[assembly: AssemblyCompany( "" )]
[assembly: AssemblyProduct( "RefactorThis.Domain.Tests" )]
[assembly: AssemblyCopyright( "Copyright © 2021" )]
[assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture( "" )]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible( false )]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid( "7971BDEC-EAD1-4FB8-A4F5-B1F67E4F6355" )]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion( "1.0.0.0" )]
[assembly: AssemblyFileVersion( "1.0.0.0" )]
// All assembly attributes have been removed to avoid duplication with auto-generated attributes.
83 changes: 17 additions & 66 deletions RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,67 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7971BDEC-EAD1-4FB8-A4F5-B1F67E4F6355}</ProjectGuid>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RefactorThis.Domain.Tests</RootNamespace>
<AssemblyName>RefactorThis.Domain.Tests</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb">
<HintPath>..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="InvoicePaymentProcessorTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RefactorThis.Domain\RefactorThis.Domain.csproj">
<Project>{5310b2fe-e26d-414e-b656-1f74c5a70368}</Project>
<Name>RefactorThis.Domain</Name>
</ProjectReference>
<ProjectReference Include="..\RefactorThis.Persistence\RefactorThis.Persistence.csproj">
<Project>{33cdc796-ff75-449c-9637-59c2efc46361}</Project>
<Name>RefactorThis.Persistence</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Project Sdk="Microsoft.NET.Sdk">

</Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\\RefactorThis.Domain\\RefactorThis.Domain.csproj" />
<ProjectReference Include="..\\RefactorThis.Persistence\\RefactorThis.Persistence.csproj" />
</ItemGroup>

</Project>
224 changes: 92 additions & 132 deletions RefactorThis.Domain/InvoiceService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,137 +13,97 @@ public InvoiceService( InvoiceRepository invoiceRepository )
_invoiceRepository = invoiceRepository;
}

public string ProcessPayment( Payment payment )
{
var inv = _invoiceRepository.GetInvoice( payment.Reference );

var responseMessage = string.Empty;

if ( inv == null )
{
throw new InvalidOperationException( "There is no invoice matching this payment" );
}
else
{
if ( inv.Amount == 0 )
{
if ( inv.Payments == null || !inv.Payments.Any( ) )
{
responseMessage = "no payment needed";
}
else
{
throw new InvalidOperationException( "The invoice is in an invalid state, it has an amount of 0 and it has payments." );
}
}
else
{
if ( inv.Payments != null && inv.Payments.Any( ) )
{
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 ) )
{
responseMessage = "the payment is greater than the partial amount remaining";
}
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( );
}
}
}
}
else
{
if ( payment.Amount > inv.Amount )
{
responseMessage = "the payment is greater than the invoice amount";
}
else if ( inv.Amount == payment.Amount )
{
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( );
}
}
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( );
}
}
}
}
}

inv.Save();

return responseMessage;
}
public string ProcessPayment(Payment payment)
{
var invoice = _invoiceRepository.GetInvoice(payment.Reference);
if (invoice == null)
throw new InvalidOperationException("There is no invoice matching this payment");

if (IsZeroAmountInvoice(invoice))
return HandleZeroAmountInvoice(invoice);

if (HasPayments(invoice))
return HandleInvoiceWithPayments(invoice, payment);

return HandleInvoiceWithoutPayments(invoice, payment);
}

private bool IsZeroAmountInvoice(Invoice invoice) => invoice.Amount == 0;

private bool HasPayments(Invoice invoice) => invoice.Payments != null && invoice.Payments.Any();

private string HandleZeroAmountInvoice(Invoice invoice)
{
if (!HasPayments(invoice))
return "no payment needed";
throw new InvalidOperationException("The invoice is in an invalid state, it has an amount of 0 and it has payments.");
}

private string HandleInvoiceWithPayments(Invoice invoice, Payment payment)
{
var totalPaid = invoice.Payments.Sum(x => x.Amount);
var remaining = invoice.Amount - invoice.AmountPaid;

if (totalPaid != 0 && invoice.Amount == totalPaid)
return "invoice was already fully paid";

if (totalPaid != 0 && payment.Amount > remaining)
return "the payment is greater than the partial amount remaining";

if (remaining == payment.Amount)
return ApplyFinalPartialPayment(invoice, payment);

return ApplyAnotherPartialPayment(invoice, payment);
}

private string HandleInvoiceWithoutPayments(Invoice invoice, Payment payment)
{
if (payment.Amount > invoice.Amount)
return "the payment is greater than the invoice amount";

if (invoice.Amount == payment.Amount)
return ApplyFullPayment(invoice, payment);

return ApplyPartialPayment(invoice, payment);
}

private string ApplyFullPayment(Invoice invoice, Payment payment)
{
ApplyPayment(invoice, payment, payment.Amount, isFull: true);
return "invoice is now fully paid";
}

private string ApplyPartialPayment(Invoice invoice, Payment payment)
{
ApplyPayment(invoice, payment, payment.Amount, isFull: false);
return "invoice is now partially paid";
}

private string ApplyFinalPartialPayment(Invoice invoice, Payment payment)
{
ApplyPayment(invoice, payment, payment.Amount, isFull: true);
return "final partial payment received, invoice is now fully paid";
}

private string ApplyAnotherPartialPayment(Invoice invoice, Payment payment)
{
ApplyPayment(invoice, payment, payment.Amount, isFull: false);
return "another partial payment received, still not fully paid";
}

private void ApplyPayment(Invoice invoice, Payment payment, decimal amount, bool isFull)
{
invoice.AmountPaid += amount;
if (invoice.Type == InvoiceType.Commercial || invoice.Type == InvoiceType.Standard)
invoice.TaxAmount += amount * 0.14m;
else
throw new ArgumentOutOfRangeException();

if (invoice.Payments == null)
invoice.Payments = new System.Collections.Generic.List<Payment>();
invoice.Payments.Add(payment);

invoice.Save();
}
}
}
Loading