From 689969541b9b02872c588eb0d13e2ae0603287ad Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Tue, 21 Dec 2021 16:42:59 +0000 Subject: [PATCH] Handle the next statement date --- .../MerchantStatementDomainServiceTests.cs | 64 +------- .../SettlementDomainEventHandler.cs | 2 + .../TransactionDomainEventHandler.cs | 6 +- .../Services/MerchantDomainService.cs | 6 +- .../MerchantStatementDomainService.cs | 150 ++++++++++++++---- .../Responses/MerchantResponse.cs | 59 ++++--- .../AddressAddedEvent.cs | 50 ------ .../SettlementScheduleChangedEvent.cs | 105 ++++++++++++ .../MerchantAggregate.cs | 18 +++ .../MerchantStatementAggregateTests.cs | 28 ++-- .../MerchantStatementAggregate.cs | 33 +--- EstateManagement.Models/Merchant/Merchant.cs | 2 + EstateManagement/Factories/ModelFactory.cs | 3 +- EstateManagement/appsettings.json | 6 + 14 files changed, 324 insertions(+), 208 deletions(-) create mode 100644 EstateManagement.Merchant.DomainEvents/SettlementScheduleChangedEvent.cs diff --git a/EstateManagement.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs b/EstateManagement.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs index f542b498..e1f0485d 100644 --- a/EstateManagement.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs +++ b/EstateManagement.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs @@ -109,38 +109,7 @@ await merchantStatementDomainService.AddTransactionToStatement(TestData.EstateId var statementLines = merchantStatement.GetStatementLines(); statementLines.ShouldBeEmpty(); } - - [Fact] - public async Task MerchantStatementDomainService_AddTransactionToStatement_MerchantNotCreated_TransactionNotAddedToStatement() - { - Mock> merchantAggregateRepository = - new Mock>(); - merchantAggregateRepository.Setup(m => m.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(new MerchantAggregate()); - - MerchantStatementAggregate merchantStatementAggregate = TestData.CreatedMerchantStatementAggregate(); - - Mock> merchantStatementAggregateRepository = - new Mock>(); - merchantStatementAggregateRepository.Setup(m => m.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(merchantStatementAggregate); - MerchantStatementDomainService merchantStatementDomainService = - new MerchantStatementDomainService(merchantAggregateRepository.Object, merchantStatementAggregateRepository.Object); - - Should.NotThrow(async () => - { - await merchantStatementDomainService.AddTransactionToStatement(TestData.EstateId, - TestData.MerchantId, - TestData.TransactionDateTime1, - TestData.TransactionAmount1, - TestData.IsAuthorisedTrue, - TestData.TransactionId1, - CancellationToken.None); - }); - - var merchantStatement = merchantStatementAggregate.GetStatement(true); - var statementLines = merchantStatement.GetStatementLines(); - statementLines.ShouldBeEmpty(); - } - + [Fact] public async Task MerchantStatementDomainService_AddTransactionToStatement_StatementNotAlreadyCreated_TransactionAdded() { @@ -203,37 +172,6 @@ await merchantStatementDomainService.AddSettledFeeToStatement(TestData.EstateId, statementLines.Count.ShouldBe(2); } - [Fact] - public async Task MerchantStatementDomainService_AddSettledFeeToStatement_MerchantNotCreated_SettledFeeNotAddedToStatement() - { - Mock> merchantAggregateRepository = - new Mock>(); - merchantAggregateRepository.Setup(m => m.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(new MerchantAggregate()); - - MerchantStatementAggregate merchantStatementAggregate = TestData.MerchantStatementAggregateWithTransactionLineAdded(); - - Mock> merchantStatementAggregateRepository = - new Mock>(); - merchantStatementAggregateRepository.Setup(m => m.GetLatestVersion(It.IsAny(), It.IsAny())).ReturnsAsync(merchantStatementAggregate); - MerchantStatementDomainService merchantStatementDomainService = - new MerchantStatementDomainService(merchantAggregateRepository.Object, merchantStatementAggregateRepository.Object); - - Should.NotThrow(async () => - { - await merchantStatementDomainService.AddSettledFeeToStatement(TestData.EstateId, - TestData.MerchantId, - TestData.SettledFeeDateTime1, - TestData.SettledFeeAmount1, - TestData.TransactionId1, - TestData.SettledFeeId1, - CancellationToken.None); - }); - - var merchantStatement = merchantStatementAggregate.GetStatement(true); - var statementLines = merchantStatement.GetStatementLines(); - statementLines.ShouldNotBeEmpty(); - statementLines.Count.ShouldBe(1); - } [Fact] public async Task MerchantStatementDomainService_GenerateStatement_StatementGenerated() diff --git a/EstateManagement.BusinessLogic/EventHandling/SettlementDomainEventHandler.cs b/EstateManagement.BusinessLogic/EventHandling/SettlementDomainEventHandler.cs index c0da1ca5..c26dda01 100644 --- a/EstateManagement.BusinessLogic/EventHandling/SettlementDomainEventHandler.cs +++ b/EstateManagement.BusinessLogic/EventHandling/SettlementDomainEventHandler.cs @@ -1,5 +1,6 @@ namespace EstateManagement.BusinessLogic.EventHandling { + using System; using System.Threading; using System.Threading.Tasks; using MediatR; @@ -57,6 +58,7 @@ public async Task Handle(IDomainEvent domainEvent, private async Task HandleSpecificDomainEvent(MerchantFeeSettledEvent domainEvent, CancellationToken cancellationToken) { + //throw new Exception(); AddSettledFeeToMerchantStatementRequest addSettledFeeToMerchantStatementRequest = AddSettledFeeToMerchantStatementRequest.Create(domainEvent.EstateId, domainEvent.MerchantId, domainEvent.EventTimestamp.DateTime, diff --git a/EstateManagement.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs b/EstateManagement.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs index a40928c2..4068d695 100644 --- a/EstateManagement.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs +++ b/EstateManagement.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs @@ -1,11 +1,15 @@ namespace EstateManagement.BusinessLogic.EventHandling { + using System; using System.Threading; using System.Threading.Tasks; + using Common; using MediatR; using Requests; + using Services; using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.EventHandling; + using Shared.Logger; using TransactionProcessor.Transaction.DomainEvents; /// @@ -66,7 +70,7 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do await this.Mediator.Send(addTransactionToMerchantStatementRequest, cancellationToken); } - + #endregion } } \ No newline at end of file diff --git a/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs b/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs index 6bb26ed8..3c15d9f2 100644 --- a/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs +++ b/EstateManagement.BusinessLogic/Services/MerchantDomainService.cs @@ -120,8 +120,9 @@ public async Task CreateMerchant(Guid estateId, // Create the merchant if (merchantAggregate.IsCreated) { - merchantAggregate.Create(estateId, name, DateTime.Now); + merchantAggregate.Create(estateId, name, merchantAggregate.DateCreated); merchantAggregate.GenerateReference(); + merchantAggregate.SetStatementDate(new DateTime(2021,9,1)); } else { @@ -136,6 +137,9 @@ public async Task CreateMerchant(Guid estateId, // Set the settlement schedule merchantAggregate.SetSettlementSchedule(settlementSchedule); + + // Set the next statement date + merchantAggregate.SetStatementDate(DateTime.Now); } await this.MerchantAggregateRepository.SaveChanges(merchantAggregate, cancellationToken); diff --git a/EstateManagement.BusinessLogic/Services/MerchantStatementDomainService.cs b/EstateManagement.BusinessLogic/Services/MerchantStatementDomainService.cs index d9de3c6f..5e1b42bd 100644 --- a/EstateManagement.BusinessLogic/Services/MerchantStatementDomainService.cs +++ b/EstateManagement.BusinessLogic/Services/MerchantStatementDomainService.cs @@ -7,6 +7,7 @@ using MerchantAggregate; using MerchantStatementAggregate; using Models.MerchantStatement; + using NLog; using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.Aggregate; @@ -66,17 +67,19 @@ public async Task AddSettledFeeToStatement(Guid estateId, Guid settledFeeId, CancellationToken cancellationToken) { - // Merchant is rehydrated - MerchantAggregate merchant = await this.MerchantAggregateRepository.GetLatestVersion(merchantId, cancellationToken); - if (merchant.IsCreated == false) - return; - - // Work out the next statement date (how is this done), do we feed statement generated events back into the merchant to update the statement date? Statements will be monthly!! - // TODO: Statement date - DateTime nextStatementDate = Guid.Parse("b5963507-c561-08d9-0000-000000000000").ToDateTime(); + // Work out the next statement date + DateTime nextStatementDate = CalculateStatementDate(settledDateTime); + Guid statementId = GuidCalculator.Combine(merchantId, nextStatementDate.ToGuid()); + Guid settlementFeeId = GuidCalculator.Combine(transactionId, settledFeeId); MerchantStatementAggregate merchantStatementAggregate = - await this.MerchantStatementAggregateRepository.GetLatestVersion(nextStatementDate.ToGuid(), cancellationToken); + await this.MerchantStatementAggregateRepository.GetLatestVersion(statementId, cancellationToken); + + MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(); + if (merchantStatement.IsCreated == false) + { + merchantStatementAggregate.CreateStatement(estateId, merchantId, nextStatementDate); + } // Add settled fee to statement SettledFee settledFee = new SettledFee @@ -84,14 +87,21 @@ public async Task AddSettledFeeToStatement(Guid estateId, DateTime = settledDateTime, Amount = settledAmount, TransactionId = transactionId, - SettledFeeId = settledFeeId - }; + SettledFeeId = settlementFeeId + }; merchantStatementAggregate.AddSettledFeeToStatement(settledFee); await this.MerchantStatementAggregateRepository.SaveChanges(merchantStatementAggregate, cancellationToken); } + internal static DateTime CalculateStatementDate(DateTime eventDateTime) + { + var calculatedDateTime = eventDateTime.Date.AddMonths(1); + + return new DateTime(calculatedDateTime.Year, calculatedDateTime.Month, 1); + } + /// /// Generates the statement. /// @@ -114,7 +124,7 @@ public async Task GenerateStatement(Guid estateId, return merchantStatementAggregate.AggregateId; } - + /// /// Adds the transaction to statement. /// @@ -133,24 +143,19 @@ public async Task AddTransactionToStatement(Guid estateId, Guid transactionId, CancellationToken cancellationToken) { - // TODO: Move to domain service - // Transaction Completed arrives (if this is a logon transaction or failed then return) + // Transaction Completed arrives(if this is a logon transaction or failed then return) if (isAuthorised == false) return; if (transactionAmount.HasValue == false) return; - // Merchant is rehydrated - MerchantAggregate merchant = await this.MerchantAggregateRepository.GetLatestVersion(merchantId, cancellationToken); - if (merchant.IsCreated == false) - return; + // Work out the next statement date + DateTime nextStatementDate = CalculateStatementDate(transactionDateTime); - // Work out the next statement date (how is this done), do we feed statement generated events back into the merchant to update the statement date? Statements will be monthly!! - // TODO: Statement date - DateTime nextStatementDate = DateTime.Now.AddDays(7); + Guid statementId = GuidCalculator.Combine(merchantId, nextStatementDate.ToGuid()); MerchantStatementAggregate merchantStatementAggregate = - await this.MerchantStatementAggregateRepository.GetLatestVersion(nextStatementDate.ToGuid(), cancellationToken); + await this.MerchantStatementAggregateRepository.GetLatestVersion(statementId, cancellationToken); MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(); if (merchantStatement.IsCreated == false) @@ -160,11 +165,11 @@ public async Task AddTransactionToStatement(Guid estateId, // Add transaction to statement Transaction transaction = new Transaction - { - DateTime = transactionDateTime, - Amount = transactionAmount.Value, - TransactionId = transactionId - }; + { + DateTime = transactionDateTime, + Amount = transactionAmount.Value, + TransactionId = transactionId + }; merchantStatementAggregate.AddTransactionToStatement(transaction); @@ -173,4 +178,95 @@ public async Task AddTransactionToStatement(Guid estateId, #endregion } + + public static class GuidCalculator + { + #region Methods + + /// + /// Combines the specified GUIDs into a new GUID. + /// + /// The first unique identifier. + /// The second unique identifier. + /// The offset. + /// Guid. + public static Guid Combine(Guid firstGuid, + Guid secondGuid, + Byte offset) + { + Byte[] firstAsBytes = firstGuid.ToByteArray(); + Byte[] secondAsBytes = secondGuid.ToByteArray(); + + Byte[] newBytes = new Byte[16]; + + for (Int32 i = 0; i < 16; i++) + { + // Add and truncate any overflow + newBytes[i] = (Byte)(firstAsBytes[i] + secondAsBytes[i] + offset); + } + + return new Guid(newBytes); + } + + /// + /// Combines the specified GUIDs into a new GUID. + /// + /// The first unique identifier. + /// The second unique identifier. + /// Guid. + public static Guid Combine(Guid firstGuid, + Guid secondGuid) + { + return GuidCalculator.Combine(firstGuid, + secondGuid, + 0); + } + + /// + /// Combines the specified first unique identifier. + /// + /// The first unique identifier. + /// The second unique identifier. + /// The third unique identifier. + /// The offset. + /// Guid. + public static Guid Combine(Guid firstGuid, + Guid secondGuid, + Guid thirdGuid, + Byte offset) + { + Byte[] firstAsBytes = firstGuid.ToByteArray(); + Byte[] secondAsBytes = secondGuid.ToByteArray(); + Byte[] thirdAsBytes = thirdGuid.ToByteArray(); + + Byte[] newBytes = new Byte[16]; + + for (Int32 i = 0; i < 16; i++) + { + // Add and truncate any overflow + newBytes[i] = (Byte)(firstAsBytes[i] + secondAsBytes[i] + thirdAsBytes[i] + offset); + } + + return new Guid(newBytes); + } + + /// + /// Combines the specified first unique identifier. + /// + /// The first unique identifier. + /// The second unique identifier. + /// The third unique identifier. + /// Guid. + public static Guid Combine(Guid firstGuid, + Guid secondGuid, + Guid thirdGuid) + { + return GuidCalculator.Combine(firstGuid, + secondGuid, + thirdGuid, + 0); + } + + #endregion + } } \ No newline at end of file diff --git a/EstateManagement.DataTransferObjects/Responses/MerchantResponse.cs b/EstateManagement.DataTransferObjects/Responses/MerchantResponse.cs index ae259ae8..8bde2b91 100644 --- a/EstateManagement.DataTransferObjects/Responses/MerchantResponse.cs +++ b/EstateManagement.DataTransferObjects/Responses/MerchantResponse.cs @@ -3,14 +3,12 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; - using Requests; + /// + /// + /// public class MerchantResponse { - #region Constructors - - #endregion - #region Properties /// @@ -22,6 +20,24 @@ public class MerchantResponse [JsonProperty("addresses")] public List Addresses { get; set; } + /// + /// Gets or sets the available balance. + /// + /// + /// The available balance. + /// + [JsonProperty("available_balance")] + public Decimal AvailableBalance { get; set; } + + /// + /// Gets or sets the balance. + /// + /// + /// The balance. + /// + [JsonProperty("balance")] + public Decimal Balance { get; set; } + /// /// Gets or sets the contacts. /// @@ -68,38 +84,41 @@ public class MerchantResponse public String MerchantName { get; set; } /// - /// Gets or sets the operators. + /// Gets or sets the merchant reference. /// /// - /// The operators. + /// The merchant reference. /// - [JsonProperty("operators")] - public List Operators { get; set; } + [JsonProperty("merchant_reference")] + public String MerchantReference { get; set; } /// - /// Gets or sets the available balance. + /// Gets or sets the next statement date. /// /// - /// The available balance. + /// The next statement date. /// - [JsonProperty("available_balance")] - public Decimal AvailableBalance { get; set; } + [JsonProperty("next_statement_date")] + public DateTime NextStatementDate { get; set; } /// - /// Gets or sets the balance. + /// Gets or sets the operators. /// /// - /// The balance. + /// The operators. /// - [JsonProperty("balance")] - public Decimal Balance { get; set; } + [JsonProperty("operators")] + public List Operators { get; set; } + /// + /// Gets or sets the settlement schedule. + /// + /// + /// The settlement schedule. + /// [JsonProperty("settlement_schedule")] public SettlementSchedule SettlementSchedule { get; set; } - [JsonProperty("merchant_reference")] - public String MerchantReference { get; set; } - #endregion } } \ No newline at end of file diff --git a/EstateManagement.Merchant.DomainEvents/AddressAddedEvent.cs b/EstateManagement.Merchant.DomainEvents/AddressAddedEvent.cs index 50087fbf..135ca3a7 100644 --- a/EstateManagement.Merchant.DomainEvents/AddressAddedEvent.cs +++ b/EstateManagement.Merchant.DomainEvents/AddressAddedEvent.cs @@ -146,54 +146,4 @@ public AddressAddedEvent(Guid aggregateId, #endregion } - - public record SettlementScheduleChangedEvent : DomainEventRecord.DomainEvent - { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The aggregate identifier. - /// The estate identifier. - /// The settlement schedule - /// - public SettlementScheduleChangedEvent(Guid aggregateId, - Guid estateId, - Int32 settlementSchedule, - DateTime nextSettlementDate) : base(aggregateId, Guid.NewGuid()) - { - this.NextSettlementDate = nextSettlementDate; - this.MerchantId = aggregateId; - this.EstateId = estateId; - this.SettlementSchedule = settlementSchedule; - } - - #endregion - - #region Properties - - /// - /// Gets the estate identifier. - /// - /// - /// The estate identifier. - /// - public Guid EstateId { get; init; } - - public DateTime NextSettlementDate { get; init; } - - /// - /// Gets the merchant identifier. - /// - /// - /// The merchant identifier. - /// - public Guid MerchantId { get; init; } - - - public Int32 SettlementSchedule { get; init; } - - #endregion - } } \ No newline at end of file diff --git a/EstateManagement.Merchant.DomainEvents/SettlementScheduleChangedEvent.cs b/EstateManagement.Merchant.DomainEvents/SettlementScheduleChangedEvent.cs new file mode 100644 index 00000000..6e32db6b --- /dev/null +++ b/EstateManagement.Merchant.DomainEvents/SettlementScheduleChangedEvent.cs @@ -0,0 +1,105 @@ +namespace EstateManagement.Merchant.DomainEvents +{ + using System; + using Shared.DomainDrivenDesign.EventSourcing; + + public record SettlementScheduleChangedEvent : DomainEventRecord.DomainEvent + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The aggregate identifier. + /// The estate identifier. + /// The settlement schedule + /// + public SettlementScheduleChangedEvent(Guid aggregateId, + Guid estateId, + Int32 settlementSchedule, + DateTime nextSettlementDate) : base(aggregateId, Guid.NewGuid()) + { + this.NextSettlementDate = nextSettlementDate; + this.MerchantId = aggregateId; + this.EstateId = estateId; + this.SettlementSchedule = settlementSchedule; + } + + #endregion + + #region Properties + + /// + /// Gets the estate identifier. + /// + /// + /// The estate identifier. + /// + public Guid EstateId { get; init; } + + public DateTime NextSettlementDate { get; init; } + + /// + /// Gets the merchant identifier. + /// + /// + /// The merchant identifier. + /// + public Guid MerchantId { get; init; } + + + public Int32 SettlementSchedule { get; init; } + + #endregion + } + + public record NextStatementDateChangedEvent : DomainEventRecord.DomainEvent + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The aggregate identifier. + /// The estate identifier. + /// The next statement date. + public NextStatementDateChangedEvent(Guid aggregateId, + Guid estateId, + DateTime nextStatementDate) : base(aggregateId, Guid.NewGuid()) + { + this.NextStatementDate = nextStatementDate; + this.MerchantId = aggregateId; + this.EstateId = estateId; + } + + #endregion + + #region Properties + + /// + /// Gets the estate identifier. + /// + /// + /// The estate identifier. + /// + public Guid EstateId { get; init; } + + /// + /// Gets or sets the next statement date. + /// + /// + /// The next statement date. + /// + public DateTime NextStatementDate { get; init; } + + /// + /// Gets the merchant identifier. + /// + /// + /// The merchant identifier. + /// + public Guid MerchantId { get; init; } + + #endregion + } +} \ No newline at end of file diff --git a/EstateManagement.MerchantAggregate/MerchantAggregate.cs b/EstateManagement.MerchantAggregate/MerchantAggregate.cs index b881395b..5cfa76f5 100644 --- a/EstateManagement.MerchantAggregate/MerchantAggregate.cs +++ b/EstateManagement.MerchantAggregate/MerchantAggregate.cs @@ -102,6 +102,8 @@ private MerchantAggregate(Guid aggregateId) public DateTime NextSettlementDueDate { get; private set; } + public DateTime NextStatementDueDate { get; private set; } + /// /// Gets the estate identifier. /// @@ -331,6 +333,7 @@ public Merchant GetMerchant() merchantModel.Reference = this.MerchantReference; merchantModel.SettlementSchedule = this.SettlementSchedule; merchantModel.NextSettlementDueDate = this.NextSettlementDueDate; + merchantModel.NextStatementDate = this.NextStatementDueDate; if (this.Addresses.Any()) { @@ -462,6 +465,16 @@ public void MakeDeposit(MerchantDepositSource source, } } + /// + /// Sets the statement date. + /// + public void SetStatementDate(DateTime nextStatementDate) + { + NextStatementDateChangedEvent nextStatementDateChangedEvent = new NextStatementDateChangedEvent(this.AggregateId, this.EstateId, nextStatementDate); + + this.ApplyAndAppend(nextStatementDateChangedEvent); + } + public void SetSettlementSchedule(SettlementSchedule settlementSchedule) { // Check if there has actually been a change or not, if not ignore the request @@ -722,6 +735,11 @@ private void PlayEvent(SettlementScheduleChangedEvent domainEvent) this.NextSettlementDueDate = domainEvent.NextSettlementDate; } + private void PlayEvent(NextStatementDateChangedEvent domainEvent) + { + this.NextStatementDueDate = domainEvent.NextStatementDate; + } + private void PlayEvent(DeviceSwappedForMerchantEvent domainEvent) { var device = this.Devices.Where(d => d.Value == domainEvent.OriginalDeviceIdentifier).Single(); diff --git a/EstateManagement.MerchantStatementAggregate.Tests/MerchantStatementAggregateTests.cs b/EstateManagement.MerchantStatementAggregate.Tests/MerchantStatementAggregateTests.cs index a20badaf..0c09934b 100644 --- a/EstateManagement.MerchantStatementAggregate.Tests/MerchantStatementAggregateTests.cs +++ b/EstateManagement.MerchantStatementAggregate.Tests/MerchantStatementAggregateTests.cs @@ -84,10 +84,14 @@ public void MerchantStatementAggregate_AddTransactionToStatement_TransactionAlre merchantStatementAggregate.CreateStatement(TestData.EstateId, TestData.MerchantId, TestData.StatementCreateDate); merchantStatementAggregate.AddTransactionToStatement(TestData.Transaction1); - Should.Throw(() => + Should.NotThrow(() => { merchantStatementAggregate.AddTransactionToStatement(TestData.Transaction1); }); + + var statement = merchantStatementAggregate.GetStatement(true); + var statementLines = statement.GetStatementLines(); + statementLines.Count.ShouldBe(1); } [Fact] @@ -121,14 +125,13 @@ public void MerchantStatementAggregate_AddSettledFeeToStatement_FeeAddedToStatem { MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); merchantStatementAggregate.CreateStatement(TestData.EstateId, TestData.MerchantId, TestData.StatementCreateDate); - merchantStatementAggregate.AddTransactionToStatement(TestData.Transaction1); merchantStatementAggregate.AddSettledFeeToStatement(TestData.SettledFee1); MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(true); var statementLines = merchantStatement.GetStatementLines(); statementLines.ShouldNotBeNull(); statementLines.ShouldNotBeEmpty(); - statementLines.Count.ShouldBe(2); + statementLines.Count.ShouldBe(1); } [Fact] @@ -136,27 +139,18 @@ public void MerchantStatementAggregate_AddSettledFeeToStatement_SettledFeeAlread { MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); merchantStatementAggregate.CreateStatement(TestData.EstateId, TestData.MerchantId, TestData.StatementCreateDate); - merchantStatementAggregate.AddTransactionToStatement(TestData.Transaction1); merchantStatementAggregate.AddSettledFeeToStatement(TestData.SettledFee1); - Should.Throw(() => + Should.NotThrow(() => { merchantStatementAggregate.AddSettledFeeToStatement(TestData.SettledFee1); }); - } - [Fact] - public void MerchantStatementAggregate_AddSettledFeeToStatement_TransactionNotAlreadyAdded_ErrorThrown() - { - MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); - merchantStatementAggregate.CreateStatement(TestData.EstateId, TestData.MerchantId, TestData.StatementCreateDate); - - Should.Throw(() => - { - merchantStatementAggregate.AddSettledFeeToStatement(TestData.SettledFee1); - }); + var statement = merchantStatementAggregate.GetStatement(true); + var statementLines = statement.GetStatementLines(); + statementLines.Count.ShouldBe(1); } - + [Fact] public void MerchantStatementAggregate_AddSettledFeeToStatement_StatementNotCreated_ErrorThrown() { diff --git a/EstateManagement.MerchantStatementAggregate/MerchantStatementAggregate.cs b/EstateManagement.MerchantStatementAggregate/MerchantStatementAggregate.cs index f820b63a..15d4eb66 100644 --- a/EstateManagement.MerchantStatementAggregate/MerchantStatementAggregate.cs +++ b/EstateManagement.MerchantStatementAggregate/MerchantStatementAggregate.cs @@ -44,8 +44,9 @@ public void CreateStatement(Guid estateId, Guid merchantId, DateTime createdDate public void AddTransactionToStatement(Transaction transaction) { + if (this.Transactions.Any(t => t.TransactionId == transaction.TransactionId)) + return; this.EnsureStatementHasBeenCreated(); - this.EnsureTransactionHasNotAlreadyBeenAddedToStatement(transaction); this.EnsureStatementHasNotAlreadyBeenGenerated(); TransactionAddedToStatementEvent transactionAddedToStatementEvent = new TransactionAddedToStatementEvent(this.AggregateId, @@ -73,36 +74,12 @@ private void EnsureStatementHasNotAlreadyBeenGenerated() throw new InvalidOperationException("Statement has already been generated"); } } - - private void EnsureTransactionHasNotAlreadyBeenAddedToStatement(Transaction transaction) - { - if (this.Transactions.Any(t => t.TransactionId == transaction.TransactionId)) - { - throw new InvalidOperationException($"Transaction {transaction.TransactionId} already added to statement {this.AggregateId}"); - } - } - - private void EnsureTransactionHasBeenAddedToStatement(Guid transactionId) - { - if (this.Transactions.Any(t => t.TransactionId == transactionId) == false) - { - throw new InvalidOperationException($"Transaction {transactionId} has not been added to statement {this.AggregateId}"); - } - } - - private void EnsureSettledFeeHasNotAlreadyBeenAddedToStatement(SettledFee settledFee) - { - if (this.SettledFees.Any(t => t.SettledFeeId == settledFee.SettledFeeId)) - { - throw new InvalidOperationException($"Settled Fee {settledFee.SettledFeeId} already added to statement {this.AggregateId}"); - } - } - + public void AddSettledFeeToStatement(SettledFee settledFee) { + if (this.SettledFees.Any(t => t.SettledFeeId == settledFee.SettledFeeId)) + return; this.EnsureStatementHasBeenCreated(); - this.EnsureTransactionHasBeenAddedToStatement(settledFee.TransactionId); - this.EnsureSettledFeeHasNotAlreadyBeenAddedToStatement(settledFee); this.EnsureStatementHasNotAlreadyBeenGenerated(); SettledFeeAddedToStatementEvent settledFeeAddedToStatementEvent = diff --git a/EstateManagement.Models/Merchant/Merchant.cs b/EstateManagement.Models/Merchant/Merchant.cs index d1dca258..ce8377e2 100644 --- a/EstateManagement.Models/Merchant/Merchant.cs +++ b/EstateManagement.Models/Merchant/Merchant.cs @@ -87,6 +87,8 @@ public class Merchant public DateTime NextSettlementDueDate { get; set; } + public DateTime NextStatementDate { get; set; } + #endregion } } \ No newline at end of file diff --git a/EstateManagement/Factories/ModelFactory.cs b/EstateManagement/Factories/ModelFactory.cs index e2af7b26..87fa7ba9 100644 --- a/EstateManagement/Factories/ModelFactory.cs +++ b/EstateManagement/Factories/ModelFactory.cs @@ -151,7 +151,8 @@ public MerchantResponse ConvertFrom(Merchant merchant, MerchantId = merchant.MerchantId, MerchantName = merchant.MerchantName, SettlementSchedule = this.ConvertFrom(merchant.SettlementSchedule), - MerchantReference = merchant.Reference + MerchantReference = merchant.Reference, + NextStatementDate = merchant.NextStatementDate }; if (merchant.Addresses != null && merchant.Addresses.Any()) diff --git a/EstateManagement/appsettings.json b/EstateManagement/appsettings.json index 93642242..f4f1542f 100644 --- a/EstateManagement/appsettings.json +++ b/EstateManagement/appsettings.json @@ -10,6 +10,12 @@ ], "MerchantFeeSettledEvent": [ "EstateManagement.BusinessLogic.EventHandling.SettlementDomainEventHandler,EstateManagement.BusinessLogic" + ], + "MerchantBalanceChangedEvent": [ + "EstateManagement.BusinessLogic.EventHandling.TransactionDomainEventHandler,EstateManagement.BusinessLogic" + ], + "MerchantBalanceChangedEvent1": [ + "EstateManagement.BusinessLogic.EventHandling.TransactionDomainEventHandler,EstateManagement.BusinessLogic" ] } },