diff --git a/.vs/ProjectEvaluation/refactorthis.metadata.v9.bin b/.vs/ProjectEvaluation/refactorthis.metadata.v9.bin new file mode 100644 index 0000000..c58f2cf Binary files /dev/null and b/.vs/ProjectEvaluation/refactorthis.metadata.v9.bin differ diff --git a/.vs/ProjectEvaluation/refactorthis.projects.v9.bin b/.vs/ProjectEvaluation/refactorthis.projects.v9.bin new file mode 100644 index 0000000..22ad44c Binary files /dev/null and b/.vs/ProjectEvaluation/refactorthis.projects.v9.bin differ diff --git a/.vs/ProjectEvaluation/refactorthis.strings.v9.bin b/.vs/ProjectEvaluation/refactorthis.strings.v9.bin new file mode 100644 index 0000000..d57d17c Binary files /dev/null and b/.vs/ProjectEvaluation/refactorthis.strings.v9.bin differ diff --git a/.vs/refactorthis/DesignTimeBuild/.dtbcache.v2 b/.vs/refactorthis/DesignTimeBuild/.dtbcache.v2 new file mode 100644 index 0000000..be8f48a Binary files /dev/null and b/.vs/refactorthis/DesignTimeBuild/.dtbcache.v2 differ diff --git a/.vs/refactorthis/FileContentIndex/4b8b06f5-2662-475c-a276-8a7a2a3dbf0e.vsidx b/.vs/refactorthis/FileContentIndex/4b8b06f5-2662-475c-a276-8a7a2a3dbf0e.vsidx new file mode 100644 index 0000000..ac3db34 Binary files /dev/null and b/.vs/refactorthis/FileContentIndex/4b8b06f5-2662-475c-a276-8a7a2a3dbf0e.vsidx differ diff --git a/.vs/refactorthis/FileContentIndex/68143bb3-650a-4056-b34d-927051f5ceeb.vsidx b/.vs/refactorthis/FileContentIndex/68143bb3-650a-4056-b34d-927051f5ceeb.vsidx new file mode 100644 index 0000000..4813be0 Binary files /dev/null and b/.vs/refactorthis/FileContentIndex/68143bb3-650a-4056-b34d-927051f5ceeb.vsidx differ diff --git a/.vs/refactorthis/FileContentIndex/a9af585b-0e9c-4271-a755-d9b7f1f6f182.vsidx b/.vs/refactorthis/FileContentIndex/a9af585b-0e9c-4271-a755-d9b7f1f6f182.vsidx new file mode 100644 index 0000000..444900f Binary files /dev/null and b/.vs/refactorthis/FileContentIndex/a9af585b-0e9c-4271-a755-d9b7f1f6f182.vsidx differ diff --git a/.vs/refactorthis/v17/.futdcache.v2 b/.vs/refactorthis/v17/.futdcache.v2 new file mode 100644 index 0000000..b714a4d Binary files /dev/null and b/.vs/refactorthis/v17/.futdcache.v2 differ diff --git a/.vs/refactorthis/v17/.suo b/.vs/refactorthis/v17/.suo new file mode 100644 index 0000000..94830fc Binary files /dev/null and b/.vs/refactorthis/v17/.suo differ diff --git a/.vs/refactorthis/v17/DocumentLayout.backup.json b/.vs/refactorthis/v17/DocumentLayout.backup.json new file mode 100644 index 0000000..ec8b263 --- /dev/null +++ b/.vs/refactorthis/v17/DocumentLayout.backup.json @@ -0,0 +1,102 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\mark2\\source\\repos\\refactorthis\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{7971BDEC-EAD1-4FB8-A4F5-B1F67E4F6355}|RefactorThis.Domain.Tests\\RefactorThis.Domain.Tests.csproj|C:\\Users\\mark2\\source\\repos\\refactorthis\\refactorthis.domain.tests\\invoicepaymentprocessortests.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{7971BDEC-EAD1-4FB8-A4F5-B1F67E4F6355}|RefactorThis.Domain.Tests\\RefactorThis.Domain.Tests.csproj|solutionrelative:refactorthis.domain.tests\\invoicepaymentprocessortests.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|c:\\users\\mark2\\source\\repos\\refactorthis\\refactorthis.persistence\\payment.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|solutionrelative:refactorthis.persistence\\payment.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|c:\\users\\mark2\\source\\repos\\refactorthis\\refactorthis.persistence\\invoicerepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|solutionrelative:refactorthis.persistence\\invoicerepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|c:\\users\\mark2\\source\\repos\\refactorthis\\refactorthis.persistence\\invoice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|solutionrelative:refactorthis.persistence\\invoice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{5310B2FE-E26D-414E-B656-1F74C5A70368}|RefactorThis.Domain\\RefactorThis.Domain.csproj|C:\\Users\\mark2\\source\\repos\\refactorthis\\refactorthis.domain\\invoiceservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{5310B2FE-E26D-414E-B656-1F74C5A70368}|RefactorThis.Domain\\RefactorThis.Domain.csproj|solutionrelative:refactorthis.domain\\invoiceservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 3, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "InvoiceRepository.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\InvoiceRepository.cs", + "RelativeDocumentMoniker": "RefactorThis.Persistence\\InvoiceRepository.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\InvoiceRepository.cs", + "RelativeToolTip": "RefactorThis.Persistence\\InvoiceRepository.cs", + "ViewState": "AgIAAJAAAAAAAAAAAADwv6oAAAABAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:58.436Z" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "Invoice.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Invoice.cs", + "RelativeDocumentMoniker": "RefactorThis.Persistence\\Invoice.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Invoice.cs", + "RelativeToolTip": "RefactorThis.Persistence\\Invoice.cs", + "ViewState": "AgIAAJYAAAAAAAAAAADwv6MAAAAFAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:57.031Z" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "Payment.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Payment.cs", + "RelativeDocumentMoniker": "RefactorThis.Persistence\\Payment.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Payment.cs", + "RelativeToolTip": "RefactorThis.Persistence\\Payment.cs", + "ViewState": "AgIAAAUAAAAAAAAAAAAAwBYAAAANAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:59.995Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "InvoicePaymentProcessorTests.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "RelativeDocumentMoniker": "RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "RelativeToolTip": "RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "ViewState": "AgIAAO8AAAAAAAAAAAAUwO8AAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:53.759Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 4, + "Title": "InvoiceService.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain\\InvoiceService.cs", + "RelativeDocumentMoniker": "RefactorThis.Domain\\InvoiceService.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain\\InvoiceService.cs", + "RelativeToolTip": "RefactorThis.Domain\\InvoiceService.cs", + "ViewState": "AgIAAC0AAAAAAAAAAAAUwDsAAAA3AAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:43.309Z" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/refactorthis/v17/DocumentLayout.json b/.vs/refactorthis/v17/DocumentLayout.json new file mode 100644 index 0000000..ec8b263 --- /dev/null +++ b/.vs/refactorthis/v17/DocumentLayout.json @@ -0,0 +1,102 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\mark2\\source\\repos\\refactorthis\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{7971BDEC-EAD1-4FB8-A4F5-B1F67E4F6355}|RefactorThis.Domain.Tests\\RefactorThis.Domain.Tests.csproj|C:\\Users\\mark2\\source\\repos\\refactorthis\\refactorthis.domain.tests\\invoicepaymentprocessortests.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{7971BDEC-EAD1-4FB8-A4F5-B1F67E4F6355}|RefactorThis.Domain.Tests\\RefactorThis.Domain.Tests.csproj|solutionrelative:refactorthis.domain.tests\\invoicepaymentprocessortests.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|c:\\users\\mark2\\source\\repos\\refactorthis\\refactorthis.persistence\\payment.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|solutionrelative:refactorthis.persistence\\payment.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|c:\\users\\mark2\\source\\repos\\refactorthis\\refactorthis.persistence\\invoicerepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|solutionrelative:refactorthis.persistence\\invoicerepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|c:\\users\\mark2\\source\\repos\\refactorthis\\refactorthis.persistence\\invoice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{50F3F93C-6688-4B4B-8B30-3BDF6FA4485F}|RefactorThis.Persistence\\RefactorThis.Persistence.csproj|solutionrelative:refactorthis.persistence\\invoice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{5310B2FE-E26D-414E-B656-1F74C5A70368}|RefactorThis.Domain\\RefactorThis.Domain.csproj|C:\\Users\\mark2\\source\\repos\\refactorthis\\refactorthis.domain\\invoiceservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{5310B2FE-E26D-414E-B656-1F74C5A70368}|RefactorThis.Domain\\RefactorThis.Domain.csproj|solutionrelative:refactorthis.domain\\invoiceservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 3, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "InvoiceRepository.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\InvoiceRepository.cs", + "RelativeDocumentMoniker": "RefactorThis.Persistence\\InvoiceRepository.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\InvoiceRepository.cs", + "RelativeToolTip": "RefactorThis.Persistence\\InvoiceRepository.cs", + "ViewState": "AgIAAJAAAAAAAAAAAADwv6oAAAABAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:58.436Z" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "Invoice.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Invoice.cs", + "RelativeDocumentMoniker": "RefactorThis.Persistence\\Invoice.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Invoice.cs", + "RelativeToolTip": "RefactorThis.Persistence\\Invoice.cs", + "ViewState": "AgIAAJYAAAAAAAAAAADwv6MAAAAFAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:57.031Z" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "Payment.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Payment.cs", + "RelativeDocumentMoniker": "RefactorThis.Persistence\\Payment.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Persistence\\Payment.cs", + "RelativeToolTip": "RefactorThis.Persistence\\Payment.cs", + "ViewState": "AgIAAAUAAAAAAAAAAAAAwBYAAAANAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:59.995Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "InvoicePaymentProcessorTests.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "RelativeDocumentMoniker": "RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "RelativeToolTip": "RefactorThis.Domain.Tests\\InvoicePaymentProcessorTests.cs", + "ViewState": "AgIAAO8AAAAAAAAAAAAUwO8AAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:53.759Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 4, + "Title": "InvoiceService.cs", + "DocumentMoniker": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain\\InvoiceService.cs", + "RelativeDocumentMoniker": "RefactorThis.Domain\\InvoiceService.cs", + "ToolTip": "C:\\Users\\mark2\\source\\repos\\refactorthis\\RefactorThis.Domain\\InvoiceService.cs", + "RelativeToolTip": "RefactorThis.Domain\\InvoiceService.cs", + "ViewState": "AgIAAC0AAAAAAAAAAAAUwDsAAAA3AAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-05-07T00:03:43.309Z" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/refactorthis/v17/TestStore/0/001.testlog b/.vs/refactorthis/v17/TestStore/0/001.testlog new file mode 100644 index 0000000..87a59c7 Binary files /dev/null and b/.vs/refactorthis/v17/TestStore/0/001.testlog differ diff --git a/.vs/refactorthis/v17/TestStore/0/testlog.manifest b/.vs/refactorthis/v17/TestStore/0/testlog.manifest new file mode 100644 index 0000000..620c4bf Binary files /dev/null and b/.vs/refactorthis/v17/TestStore/0/testlog.manifest differ diff --git a/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs b/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs index 3a607fd..19d7c16 100644 --- a/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs +++ b/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs @@ -2,246 +2,266 @@ using System.Collections.Generic; using NUnit.Framework; using RefactorThis.Persistence; +using RefactorThis.Domain; namespace RefactorThis.Domain.Tests { - [TestFixture] - public class InvoicePaymentProcessorTests - { - [Test] - public void ProcessPayment_Should_ThrowException_When_NoInoiceFoundForPaymentReference( ) - { - var repo = new InvoiceRepository( ); - - Invoice invoice = null; - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ); - var failureMessage = ""; - - try - { - var result = paymentProcessor.ProcessPayment( payment ); - } - catch ( InvalidOperationException e ) - { - failureMessage = e.Message; - } - - Assert.AreEqual( "There is no invoice matching this payment", failureMessage ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_NoPaymentNeeded( ) - { - var repo = new InvoiceRepository( ); - - var invoice = new Invoice( repo ) - { - Amount = 0, - AmountPaid = 0, - Payments = null - }; - - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ); - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "no payment needed", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_InvoiceAlreadyFullyPaid( ) - { - var repo = new InvoiceRepository( ); - - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 10, - Payments = new List - { - new Payment - { - Amount = 10 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ); - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "invoice was already fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_PartialPaymentExistsAndAmountPaidExceedsAmountDue( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 5, - Payments = new List - { - new Payment - { - Amount = 5 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 6 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "the payment is greater than the partial amount remaining", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_NoPartialPaymentExistsAndAmountPaidExceedsInvoiceAmount( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 5, - AmountPaid = 0, - Payments = new List( ) - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 6 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "the payment is greater than the invoice amount", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFullyPaidMessage_When_PartialPaymentExistsAndAmountPaidEqualsAmountDue( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 5, - Payments = new List - { - new Payment - { - Amount = 5 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 5 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "final partial payment received, invoice is now fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFullyPaidMessage_When_NoPartialPaymentExistsAndAmountPaidEqualsInvoiceAmount( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 0, - Payments = new List( ) { new Payment( ) { Amount = 10 } } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 10 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "invoice was already fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_PartialPaymentExistsAndAmountPaidIsLessThanAmountDue( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 5, - Payments = new List - { - new Payment - { - Amount = 5 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 1 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "another partial payment received, still not fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_NoPartialPaymentExistsAndAmountPaidIsLessThanInvoiceAmount( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 0, - Payments = new List( ) - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 1 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "invoice is now partially paid", result ); - } - } + [TestFixture] + public class InvoicePaymentProcessorTests + { + private IInvoiceRepository _repository; + private InvoiceService _paymentProcessor; + private const string TEST_REFERENCE = "TEST-123"; + + [SetUp] + public void Setup() + { + _repository = new InvoiceRepository(); + _paymentProcessor = new InvoiceService(_repository); + } + + [Test] + public void ProcessPayment_Should_ThrowException_When_NoInvoiceFoundForPaymentReference() + { + // Arrange + var payment = new Payment { Reference = TEST_REFERENCE }; + + // Act & Assert + var exception = Assert.Throws(() => + _paymentProcessor.ProcessPayment(payment)); + + Assert.AreEqual("There is no invoice matching this payment", exception.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnFailureMessage_When_NoPaymentNeeded() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 0 + }; + _repository.Add(invoice); + + var payment = new Payment { Reference = TEST_REFERENCE }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("no payment needed", result); + } + + [Test] + public void ProcessPayment_Should_ReturnFailureMessage_When_InvoiceAlreadyFullyPaid() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 10 + }; + + invoice.SetAmountPaid(10); + invoice.SetPayments(new List { new Payment { Amount = 10 } }); + + _repository.Add(invoice); + + var payment = new Payment { Reference = TEST_REFERENCE }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("invoice was already fully paid", result); + } + + [Test] + public void ProcessPayment_Should_ReturnFailureMessage_When_PartialPaymentExistsAndAmountPaidExceedsAmountDue() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 10 + }; + + invoice.SetAmountPaid(5); + invoice.SetPayments(new List { new Payment { Amount = 5 } }); + + _repository.Add(invoice); + + var payment = new Payment + { + Reference = TEST_REFERENCE, + Amount = 6 + }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("the payment is greater than the partial amount remaining", result); + } + + [Test] + public void ProcessPayment_Should_ReturnFailureMessage_When_NoPartialPaymentExistsAndAmountPaidExceedsInvoiceAmount() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 5 + }; + + _repository.Add(invoice); + + var payment = new Payment + { + Reference = TEST_REFERENCE, + Amount = 6 + }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("the payment is greater than the invoice amount", result); + } + + [Test] + public void ProcessPayment_Should_ReturnFullyPaidMessage_When_PartialPaymentExistsAndAmountPaidEqualsAmountDue() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 10 + }; + + invoice.SetAmountPaid(5); + invoice.SetPayments(new List { new Payment { Amount = 5 } }); + + _repository.Add(invoice); + + var payment = new Payment + { + Reference = TEST_REFERENCE, + Amount = 5 + }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("final partial payment received, invoice is now fully paid", result); + } + + [Test] + public void ProcessPayment_Should_ReturnFullyPaidMessage_When_NoPartialPaymentExistsAndAmountPaidEqualsInvoiceAmount() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 10 + }; + + _repository.Add(invoice); + + var payment = new Payment + { + Reference = TEST_REFERENCE, + Amount = 10 + }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("invoice is now fully paid", result); + } + + [Test] + public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_PartialPaymentExistsAndAmountPaidIsLessThanAmountDue() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 10 + }; + + invoice.SetAmountPaid(5); + invoice.SetPayments(new List { new Payment { Amount = 5 } }); + + _repository.Add(invoice); + + var payment = new Payment + { + Reference = TEST_REFERENCE, + Amount = 1 + }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("another partial payment received, still not fully paid", result); + } + + [Test] + public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_NoPartialPaymentExistsAndAmountPaidIsLessThanInvoiceAmount() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 10 + }; + + _repository.Add(invoice); + + var payment = new Payment + { + Reference = TEST_REFERENCE, + Amount = 1 + }; + + // Act + var result = _paymentProcessor.ProcessPayment(payment); + + // Assert + Assert.AreEqual("invoice is now partially paid", result); + } + + [Test] + public void ProcessPayment_Should_UpdateTaxAmount_When_CommercialInvoicePayment() + { + // Arrange + var invoice = new Invoice(_repository) + { + Reference = TEST_REFERENCE, + Amount = 100, + Type = InvoiceType.Commercial + }; + + _repository.Add(invoice); + + var payment = new Payment + { + Reference = TEST_REFERENCE, + Amount = 50 + }; + + // Act + _paymentProcessor.ProcessPayment(payment); + + // Assert + var updatedInvoice = _repository.GetInvoice(TEST_REFERENCE); + Assert.AreEqual(50, updatedInvoice.AmountPaid); + Assert.AreEqual(7, updatedInvoice.TaxAmount); // 50 * 0.14 + } + } } \ No newline at end of file diff --git a/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj b/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj index f118007..83908af 100644 --- a/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj +++ b/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj @@ -1,67 +1,143 @@  - - - Debug - AnyCPU - {7971BDEC-EAD1-4FB8-A4F5-B1F67E4F6355} - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - RefactorThis.Domain.Tests - RefactorThis.Domain.Tests - v4.7.2 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll - - - - - - - - - {5310b2fe-e26d-414e-b656-1f74c5a70368} - RefactorThis.Domain - - - {33cdc796-ff75-449c-9637-59c2efc46361} - RefactorThis.Persistence - - - - - - + \ No newline at end of file diff --git a/RefactorThis.Domain.Tests/app.config b/RefactorThis.Domain.Tests/app.config new file mode 100644 index 0000000..f10b4ac --- /dev/null +++ b/RefactorThis.Domain.Tests/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/RefactorThis.Domain.Tests/packages.config b/RefactorThis.Domain.Tests/packages.config index c108d44..9188629 100644 --- a/RefactorThis.Domain.Tests/packages.config +++ b/RefactorThis.Domain.Tests/packages.config @@ -1,4 +1,19 @@  + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RefactorThis.Domain/InvoiceService.cs b/RefactorThis.Domain/InvoiceService.cs index fbd674c..e126441 100644 --- a/RefactorThis.Domain/InvoiceService.cs +++ b/RefactorThis.Domain/InvoiceService.cs @@ -1,149 +1,74 @@ using System; -using System.Linq; using RefactorThis.Persistence; namespace RefactorThis.Domain { - public class InvoiceService - { - private readonly InvoiceRepository _invoiceRepository; - - 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 class InvoiceService + { + private readonly IInvoiceRepository _invoiceRepository; + + public InvoiceService(IInvoiceRepository invoiceRepository) + { + _invoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository)); + } + + public string ProcessPayment(Payment payment) + { + if (payment == null) + { + throw new ArgumentNullException(nameof(payment)); + } + + // Get invoice from repository + var invoice = _invoiceRepository.GetInvoice(payment.Reference); + + if (invoice == null) + { + throw new InvalidOperationException("There is no invoice matching this payment"); + } + + // Process payment on invoice domain object + var result = invoice.ProcessPayment(payment); + + // Save invoice + invoice.Save(); + + // Map domain result to response message + return GetResponseMessage(result); + } + + // Map payment result to user-friendly messages + private string GetResponseMessage(PaymentResult result) + { + switch (result) + { + case PaymentResult.NoPaymentNeeded: + return "no payment needed"; + + case PaymentResult.AlreadyPaid: + return "invoice was already fully paid"; + + case PaymentResult.ExceedsInvoiceAmount: + return "the payment is greater than the invoice amount"; + + case PaymentResult.ExceedsPartialRemaining: + return "the payment is greater than the partial amount remaining"; + + case PaymentResult.FullPayment: + return "invoice is now fully paid"; + + case PaymentResult.FirstPartialPayment: + return "invoice is now partially paid"; + + case PaymentResult.AdditionalPartialPayment: + return "another partial payment received, still not fully paid"; + + case PaymentResult.FinalPartialPayment: + return "final partial payment received, invoice is now fully paid"; + + default: + throw new ArgumentOutOfRangeException(nameof(result), result, "Unknown payment result"); + } + } + } } \ No newline at end of file diff --git a/RefactorThis.Persistence/Invoice.cs b/RefactorThis.Persistence/Invoice.cs index bd4370d..3303866 100644 --- a/RefactorThis.Persistence/Invoice.cs +++ b/RefactorThis.Persistence/Invoice.cs @@ -1,31 +1,177 @@ +using System; using System.Collections.Generic; +using System.Linq; namespace RefactorThis.Persistence { - public class Invoice - { - private readonly InvoiceRepository _repository; - public Invoice( InvoiceRepository repository ) - { - _repository = repository; - } - - public void Save( ) - { - _repository.SaveInvoice( this ); - } - - public decimal Amount { get; set; } - public decimal AmountPaid { get; set; } - public decimal TaxAmount { get; set; } - public List Payments { get; set; } - - public InvoiceType Type { get; set; } - } - - public enum InvoiceType - { - Standard, - Commercial - } + public class Invoice + { + private readonly List _payments = new List(); + private readonly IInvoiceRepository _repository; + private const decimal COMMERCIAL_TAX_RATE = 0.14m; + + // Constructor for new invoices + public Invoice(IInvoiceRepository repository) + { + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + } + + // Constructor for loading from repository or testing + public Invoice(IInvoiceRepository repository, string reference, decimal amount, InvoiceType type) + : this(repository) + { + Reference = reference ?? throw new ArgumentNullException(nameof(reference)); + Amount = amount; + Type = type; + } + + // Constructor for deserialization and testing + protected Invoice() { } + + // Required properties + public string Reference { get; set; } + public decimal Amount { get; set; } + public decimal AmountPaid { get; private set; } + public decimal TaxAmount { get; private set; } + public InvoiceType Type { get; set; } + + // Read-only collections + public IReadOnlyCollection Payments => _payments.AsReadOnly(); + + // Calculated properties + public decimal RemainingAmount => Amount - AmountPaid; + public bool IsFullyPaid => AmountPaid >= Amount && Amount > 0; + public bool RequiresPayment => Amount > 0; + + // Domain methods for payment processing + public PaymentResult ProcessPayment(Payment payment) + { + if (payment == null) + { + throw new ArgumentNullException(nameof(payment)); + } + + // Validation logic + if (!RequiresPayment) + { + return PaymentResult.NoPaymentNeeded; + } + + if (IsFullyPaid) + { + return PaymentResult.AlreadyPaid; + } + + if (payment.Amount > RemainingAmount) + { + return HasPartialPayments() + ? PaymentResult.ExceedsPartialRemaining + : PaymentResult.ExceedsInvoiceAmount; + } + + // Process the payment + bool isFullPayment = payment.Amount == RemainingAmount; + bool hasExistingPayments = HasPartialPayments(); + + ApplyPayment(payment); + + // Determine result type + if (isFullPayment) + { + return hasExistingPayments + ? PaymentResult.FinalPartialPayment + : PaymentResult.FullPayment; + } + else + { + return hasExistingPayments + ? PaymentResult.AdditionalPartialPayment + : PaymentResult.FirstPartialPayment; + } + } + + // Helper methods for payment processing + private bool HasPartialPayments() + { + return _payments != null && _payments.Any(); + } + + private void ApplyPayment(Payment payment) + { + AmountPaid += payment.Amount; + + if (Type == InvoiceType.Commercial) + { + TaxAmount += payment.Amount * COMMERCIAL_TAX_RATE; + } + + _payments.Add(payment); + } + + // Data access methods + public void Save() + { + if (_repository == null) + { + throw new InvalidOperationException("Cannot save invoice: no repository configured"); + } + + if (string.IsNullOrWhiteSpace(Reference)) + { + throw new InvalidOperationException("Cannot save invoice: Reference is required"); + } + + _repository.SaveInvoice(this); + } + + // Required for deserialization and testing + public void SetPayments(IEnumerable payments) + { + _payments.Clear(); + if (payments != null) + { + _payments.AddRange(payments); + } + } + + // Required for deserialization and testing + public void SetAmountPaid(decimal amountPaid) + { + if (amountPaid < 0) + { + throw new ArgumentException("Amount paid cannot be negative", nameof(amountPaid)); + } + + AmountPaid = amountPaid; + } + + // Required for deserialization and testing + public void SetTaxAmount(decimal taxAmount) + { + if (taxAmount < 0) + { + throw new ArgumentException("Tax amount cannot be negative", nameof(taxAmount)); + } + + TaxAmount = taxAmount; + } + } + + public enum InvoiceType + { + Standard, + Commercial + } + + public enum PaymentResult + { + NoPaymentNeeded, + AlreadyPaid, + ExceedsInvoiceAmount, + ExceedsPartialRemaining, + FullPayment, + FirstPartialPayment, + AdditionalPartialPayment, + FinalPartialPayment + } } \ No newline at end of file diff --git a/RefactorThis.Persistence/InvoiceRepository.cs b/RefactorThis.Persistence/InvoiceRepository.cs index 38548c7..20c1e15 100644 --- a/RefactorThis.Persistence/InvoiceRepository.cs +++ b/RefactorThis.Persistence/InvoiceRepository.cs @@ -1,21 +1,171 @@ -namespace RefactorThis.Persistence { - public class InvoiceRepository - { - private Invoice _invoice; - - public Invoice GetInvoice( string reference ) - { - return _invoice; - } - - public void SaveInvoice( Invoice invoice ) - { - //saves the invoice to the database - } - - public void Add( Invoice invoice ) - { - _invoice = invoice; - } - } +using System; +using System.Collections.Generic; +using System.Linq; + +namespace RefactorThis.Persistence +{ + // Interface for repository operations + public interface IInvoiceRepository + { + Invoice GetInvoice(string reference); + void SaveInvoice(Invoice invoice); + void Add(Invoice invoice); + bool Exists(string reference); + IEnumerable GetAll(); + } + + // Implementation that maintains backward compatibility with existing code + public class InvoiceRepository : IInvoiceRepository + { + // For backward compatibility, maintain the single invoice field + // but add a dictionary for proper multi-invoice support + private readonly Dictionary _invoices = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Invoice _lastInvoice; // For legacy code support + + public Invoice GetInvoice(string reference) + { + if (string.IsNullOrWhiteSpace(reference)) + { + return _lastInvoice; // Legacy behavior for tests + } + + return _invoices.TryGetValue(reference, out var invoice) ? invoice : null; + } + + public void SaveInvoice(Invoice invoice) + { + if (invoice == null) + { + throw new ArgumentNullException(nameof(invoice)); + } + + // For legacy code without reference + if (string.IsNullOrWhiteSpace(invoice.Reference)) + { + // Just update the last invoice for backward compatibility + _lastInvoice = invoice; + return; + } + + // Store in dictionary for modern code + _invoices[invoice.Reference] = invoice; + _lastInvoice = invoice; // Keep reference for legacy code + } + + public void Add(Invoice invoice) + { + if (invoice == null) + { + throw new ArgumentNullException(nameof(invoice)); + } + + // For legacy code without reference + if (string.IsNullOrWhiteSpace(invoice.Reference)) + { + _lastInvoice = invoice; + return; + } + + // Check for duplicates in modern implementation + if (_invoices.ContainsKey(invoice.Reference)) + { + throw new InvalidOperationException($"An invoice with reference '{invoice.Reference}' already exists"); + } + + _invoices[invoice.Reference] = invoice; + _lastInvoice = invoice; // Keep reference for legacy code + } + + public bool Exists(string reference) + { + if (string.IsNullOrWhiteSpace(reference)) + { + return _lastInvoice != null; // Legacy behavior + } + + return _invoices.ContainsKey(reference); + } + + public IEnumerable GetAll() + { + // Include the legacy invoice if it has no reference + var allInvoices = _invoices.Values.ToList(); + + if (_lastInvoice != null && string.IsNullOrWhiteSpace(_lastInvoice.Reference) && + !allInvoices.Contains(_lastInvoice)) + { + allInvoices.Add(_lastInvoice); + } + + return allInvoices; + } + } + + // A modern implementation for new code + public class ModernInvoiceRepository : IInvoiceRepository + { + private readonly Dictionary _invoices = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Database context would be injected here in real implementation + + public Invoice GetInvoice(string reference) + { + if (string.IsNullOrWhiteSpace(reference)) + { + throw new ArgumentException("Invoice reference cannot be null or empty", nameof(reference)); + } + + return _invoices.TryGetValue(reference, out var invoice) ? invoice : null; + } + + public void SaveInvoice(Invoice invoice) + { + if (invoice == null) + { + throw new ArgumentNullException(nameof(invoice)); + } + + if (string.IsNullOrWhiteSpace(invoice.Reference)) + { + throw new ArgumentException("Invoice must have a valid reference", nameof(invoice)); + } + + _invoices[invoice.Reference] = invoice; + } + + public void Add(Invoice invoice) + { + if (invoice == null) + { + throw new ArgumentNullException(nameof(invoice)); + } + + if (string.IsNullOrWhiteSpace(invoice.Reference)) + { + throw new ArgumentException("Invoice must have a valid reference", nameof(invoice)); + } + + if (_invoices.ContainsKey(invoice.Reference)) + { + throw new InvalidOperationException($"An invoice with reference '{invoice.Reference}' already exists"); + } + + _invoices[invoice.Reference] = invoice; + } + + public bool Exists(string reference) + { + if (string.IsNullOrWhiteSpace(reference)) + { + throw new ArgumentException("Invoice reference cannot be null or empty", nameof(reference)); + } + + return _invoices.ContainsKey(reference); + } + + public IEnumerable GetAll() + { + return _invoices.Values.ToList(); + } + } } \ No newline at end of file diff --git a/RefactorThis.Persistence/Payment.cs b/RefactorThis.Persistence/Payment.cs index db29372..e0e1fd6 100644 --- a/RefactorThis.Persistence/Payment.cs +++ b/RefactorThis.Persistence/Payment.cs @@ -1,8 +1,34 @@ +using System; + namespace RefactorThis.Persistence { - public class Payment - { - public decimal Amount { get; set; } - public string Reference { get; set; } - } + public class Payment + { + public Payment() + { + Id = Guid.NewGuid(); + Date = DateTime.UtcNow; + } + + public Payment(string reference, decimal amount) : this() + { + if (string.IsNullOrWhiteSpace(reference)) + { + throw new ArgumentException("Payment reference cannot be null or empty", nameof(reference)); + } + + if (amount <= 0) + { + throw new ArgumentException("Payment amount must be greater than zero", nameof(amount)); + } + + Reference = reference; + Amount = amount; + } + + public Guid Id { get; set; } + public string Reference { get; set; } + public decimal Amount { get; set; } + public DateTime Date { get; set; } + } } \ No newline at end of file