diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs index bfd03203..a1f408c3 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs @@ -4,9 +4,11 @@ using System.Threading; using System.Threading.Tasks; using BusinessLogic.Services; + using EstateManagement.Client; using Microsoft.Extensions.Configuration; using Models; using Moq; + using SecurityService.Client; using SettlementAggregates; using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.Aggregate; @@ -23,6 +25,10 @@ public class SettlementDomainServiceTests private Mock> settlementAggregateRepository; + private Mock securityServiceClient; + + private Mock estateClient; + private SettlementDomainService settlementDomainService; public SettlementDomainServiceTests() { @@ -30,9 +36,12 @@ public SettlementDomainServiceTests() { new Mock>(); this.settlementAggregateRepository = new Mock>(); + this.securityServiceClient = new Mock(); + this.estateClient = new Mock(); this.settlementDomainService = - new SettlementDomainService(this.transactionAggregateRepository.Object, settlementAggregateRepository.Object); + new SettlementDomainService(this.transactionAggregateRepository.Object, settlementAggregateRepository.Object, + this.securityServiceClient.Object, this.estateClient.Object); IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); ConfigurationReader.Initialise(configurationRoot); @@ -48,6 +57,11 @@ public async Task SettlementDomainService_ProcessSettlement_SettlementIsProcesse this.transactionAggregateRepository.Setup(s => s.GetLatestVersion(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetCompletedAuthorisedSaleTransactionAggregate); + this.securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + + this.estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); + ProcessSettlementResponse response = await settlementDomainService.ProcessSettlement(TestData.SettlementDate, TestData.EstateId, TestData.MerchantId, @@ -82,6 +96,11 @@ public async Task SettlementDomainService_ProcessSettlement_SettlementAggregateN settlementAggregateRepository.Setup(s => s.GetLatestVersion(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetCreatedSettlementAggregate); + this.securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + + this.estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); + ProcessSettlementResponse response = await settlementDomainService.ProcessSettlement(TestData.SettlementDate, TestData.EstateId, TestData.MerchantId, @@ -99,6 +118,11 @@ public async Task SettlementDomainService_ProcessSettlement_AddSettledFeeThrownE settlementAggregateRepository.Setup(s => s.GetLatestVersion(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.GetSettlementAggregateWithPendingMerchantFees(10)); + this.securityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(TestData.TokenResponse); + + this.estateClient.Setup(e => e.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.GetMerchantResponseWithOperator1); + ProcessSettlementResponse response = await settlementDomainService.ProcessSettlement(TestData.SettlementDate, TestData.EstateId, TestData.MerchantId, @@ -110,4 +134,4 @@ public async Task SettlementDomainService_ProcessSettlement_AddSettledFeeThrownE response.NumberOfFeesSuccessfullySettled.ShouldBe(0); } } -} \ No newline at end of file +} diff --git a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs index 35c33b67..d8fd3268 100644 --- a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs @@ -2,14 +2,21 @@ { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; using Common; + using EstateManagement.Client; + using EstateManagement.DataTransferObjects; + using EstateManagement.DataTransferObjects.Responses; using Models; + using SecurityService.Client; + using SecurityService.DataTransferObjects.Responses; using SettlementAggregates; using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.Aggregate; + using Shared.General; using Shared.Logger; using TransactionAggregate; @@ -18,6 +25,10 @@ public class SettlementDomainService : ISettlementDomainService private readonly IAggregateRepository TransactionAggregateRepository; private readonly IAggregateRepository SettlementAggregateRepository; + private readonly ISecurityServiceClient SecurityServiceClient; + + private readonly IEstateClient EstateClient; + public async Task ProcessSettlement(DateTime settlementDate, Guid estateId, Guid merchantId, @@ -36,6 +47,21 @@ public async Task ProcessSettlement(DateTime settleme return response; } + this.TokenResponse = await this.GetToken(cancellationToken); + + MerchantResponse merchant = await this.EstateClient.GetMerchant(this.TokenResponse.AccessToken, + estateId, + merchantId, + cancellationToken); + + if (merchant.SettlementSchedule == SettlementSchedule.Immediate){ + // Mark the settlement as completed + settlementAggregate.StartProcessing(DateTime.Now); + settlementAggregate.ManuallyComplete(); + await this.SettlementAggregateRepository.SaveChanges(settlementAggregate, cancellationToken); + return response; + } + List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled = settlementAggregate.GetFeesToBeSettled(); response.NumberOfFeesPendingSettlement = feesToBeSettled.Count; @@ -71,10 +97,43 @@ public async Task ProcessSettlement(DateTime settleme } public SettlementDomainService(IAggregateRepository transactionAggregateRepository, - IAggregateRepository settlementAggregateRepository) + IAggregateRepository settlementAggregateRepository, + ISecurityServiceClient securityServiceClient, + IEstateClient estateClient) { this.TransactionAggregateRepository = transactionAggregateRepository; this.SettlementAggregateRepository = settlementAggregateRepository; + this.SecurityServiceClient = securityServiceClient; + this.EstateClient = estateClient; + } + + private TokenResponse TokenResponse; + + [ExcludeFromCodeCoverage] + private async Task GetToken(CancellationToken cancellationToken) + { + // Get a token to talk to the estate service + String clientId = ConfigurationReader.GetValue("AppSettings", "ClientId"); + String clientSecret = ConfigurationReader.GetValue("AppSettings", "ClientSecret"); + Logger.LogInformation($"Client Id is {clientId}"); + Logger.LogInformation($"Client Secret is {clientSecret}"); + + if (this.TokenResponse == null) + { + TokenResponse token = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); + Logger.LogInformation($"Token is {token.AccessToken}"); + return token; + } + + if (this.TokenResponse.Expires.UtcDateTime.Subtract(DateTime.UtcNow) < TimeSpan.FromMinutes(2)) + { + Logger.LogInformation($"Token is about to expire at {this.TokenResponse.Expires.DateTime:O}"); + TokenResponse token = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); + Logger.LogInformation($"Token is {token.AccessToken}"); + return token; + } + + return this.TokenResponse; } } } \ No newline at end of file diff --git a/TransactionProcessor.SettlementAggregates.Tests/SettlementAggregateTests.cs b/TransactionProcessor.SettlementAggregates.Tests/SettlementAggregateTests.cs index 7afe7b44..b027bc63 100644 --- a/TransactionProcessor.SettlementAggregates.Tests/SettlementAggregateTests.cs +++ b/TransactionProcessor.SettlementAggregates.Tests/SettlementAggregateTests.cs @@ -209,7 +209,7 @@ public void SettlementAggregate_StartProcessing_CalledTwice_ProcessingStarted(){ aggregate.StartProcessing(TestData.SettlementProcessingStartedDateTimeSecondCall); aggregate.ProcessingStarted.ShouldBeTrue(); - aggregate.ProcessingStartedDateTime.ShouldBe(TestData.SettlementProcessingStartedDateTimeSecondCall); + aggregate.ProcessingStartedDateTime.ShouldBe(TestData.SettlementProcessingStartedDateTimeSecondCall); } [Fact] @@ -221,5 +221,38 @@ public void SettlementAggregate_StartProcessing_SettlementNotCreated_ErrorThron( aggregate.StartProcessing(TestData.SettlementProcessingStartedDateTime); }); } + + [Fact] + public void SettlementAggregate_ManuallyComplete_SettlementCompleted() + { + SettlementAggregate aggregate = SettlementAggregate.Create(TestData.SettlementAggregateId); + aggregate.Create(TestData.EstateId, TestData.MerchantId, TestData.SettlementDate); + aggregate.StartProcessing(TestData.SettlementProcessingStartedDateTime); + aggregate.ManuallyComplete(); + + aggregate.SettlementComplete.ShouldBeTrue(); + } + + [Fact] + public void SettlementAggregate_ManuallyComplete_CalledTwice_SettlementCompleted() + { + SettlementAggregate aggregate = SettlementAggregate.Create(TestData.SettlementAggregateId); + aggregate.Create(TestData.EstateId, TestData.MerchantId, TestData.SettlementDate); + aggregate.StartProcessing(TestData.SettlementProcessingStartedDateTime); + aggregate.ManuallyComplete(); + aggregate.ManuallyComplete(); + + aggregate.SettlementComplete.ShouldBeTrue(); + } + + [Fact] + public void SettlementAggregate_ManuallyComplete_SettlementNotCreated_ErrorThron() + { + SettlementAggregate aggregate = SettlementAggregate.Create(TestData.SettlementAggregateId); + + Should.Throw(() => { + aggregate.ManuallyComplete(); + }); + } } } \ No newline at end of file diff --git a/TransactionProcessor.SettlementAggregates/SettlementAggregate.cs b/TransactionProcessor.SettlementAggregates/SettlementAggregate.cs index 7985f135..ca40cf91 100644 --- a/TransactionProcessor.SettlementAggregates/SettlementAggregate.cs +++ b/TransactionProcessor.SettlementAggregates/SettlementAggregate.cs @@ -22,6 +22,17 @@ public static void StartProcessing(this SettlementAggregate aggregate, DateTime aggregate.ApplyAndAppend(startedEvent); } + public static void ManuallyComplete(this SettlementAggregate aggregate){ + + aggregate.CheckHasBeenCreated(); + + if (aggregate.SettlementComplete) + return; + + SettlementCompletedEvent pendingSettlementCompletedEvent = new SettlementCompletedEvent(aggregate.AggregateId, aggregate.EstateId); + aggregate.ApplyAndAppend(pendingSettlementCompletedEvent); + } + public static void MarkFeeAsSettled(this SettlementAggregate aggregate, Guid merchantId, Guid transactionId, Guid feeId) { (Guid transactionId, Guid merchantId, CalculatedFee calculatedFee) pendingFee = SettlementAggregateExtensions.GetPendingFee(aggregate, merchantId, transactionId, feeId); @@ -182,7 +193,7 @@ private static void CheckHasNotAlreadyBeenCreated(this SettlementAggregate aggre throw new InvalidOperationException($"Pending Settlement already created for this date {aggregate.SettlementDate}"); } } - + public static void PlayEvent(this SettlementAggregate aggregate, MerchantFeeSettledEvent domainEvent) { // Add to the settled fees list