diff --git a/TransactionProcessor.Aggregates.Tests/MerchantStatementAggregateTests.cs b/TransactionProcessor.Aggregates.Tests/MerchantStatementAggregateTests.cs index 1e3608c9..72e2fef8 100644 --- a/TransactionProcessor.Aggregates.Tests/MerchantStatementAggregateTests.cs +++ b/TransactionProcessor.Aggregates.Tests/MerchantStatementAggregateTests.cs @@ -4,97 +4,85 @@ namespace TransactionProcessor.Aggregates.Tests { - public class MerchantStatementAggregateTests + public class MerchantStatementForDateAggregateTests { [Fact] - public void MerchantStatementAggregate_CanBeCreated_IsCreated() + public void MerchantStatementForDateAggregate_CanBeCreated_IsCreated() { - MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); + MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1); - merchantStatementAggregate.ShouldNotBeNull(); - merchantStatementAggregate.AggregateId.ShouldBe(TestData.MerchantStatementId); + merchantStatementForDateAggregate.ShouldNotBeNull(); + merchantStatementForDateAggregate.AggregateId.ShouldBe(TestData.MerchantStatementForDateId1); } [Fact] - public void MerchantStatementAggregate_AddTransactionToStatement_TransactionAddedToStatement() + public void MerchantStatementForDateAggregate_AddTransactionToStatement_TransactionAddedToStatement() { - MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); - merchantStatementAggregate.AddTransactionToStatement(TestData.MerchantStatementId, - TestData.EventId1, - TestData.StatementCreateDate, - TestData.EstateId, - TestData.MerchantId, TestData.Transaction1); - - MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(true); - var statementLines = merchantStatement.GetStatementLines(); + MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1); + merchantStatementForDateAggregate.AddTransactionToStatement(TestData.MerchantStatementId, + TestData.StatementDate, + TestData.EventId1, + TestData.EstateId, + TestData.MerchantId, TestData.Transaction1); + + MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true); + List? statementLines = merchantStatementForDate.GetStatementLines(); statementLines.ShouldNotBeNull(); statementLines.ShouldNotBeEmpty(); statementLines.Count.ShouldBe(1); } - + [Fact] - public void MerchantStatementAggregate_AddTransactionToStatement_DuplicateTransaction_SilentlyHandled() + public void MerchantStatementForDateAggregate_AddTransactionToStatement_DuplicateTransaction_SilentlyHandled() { - MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); - merchantStatementAggregate.AddTransactionToStatement(TestData.MerchantStatementId, - TestData.EventId1, - TestData.StatementCreateDate, - TestData.EstateId, - TestData.MerchantId, TestData.Transaction1); - - merchantStatementAggregate.AddTransactionToStatement(TestData.MerchantStatementId, - TestData.EventId1, - TestData.StatementCreateDate, - TestData.EstateId, - TestData.MerchantId, TestData.Transaction1); - - MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(true); - var statementLines = merchantStatement.GetStatementLines(); + MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1); + merchantStatementForDateAggregate.AddTransactionToStatement(TestData.MerchantStatementId, + TestData.StatementDate, + TestData.EventId1, + TestData.EstateId, + TestData.MerchantId, TestData.Transaction1); + + merchantStatementForDateAggregate.AddTransactionToStatement(TestData.MerchantStatementId, + TestData.StatementDate, + TestData.EventId1, + TestData.EstateId, + TestData.MerchantId, TestData.Transaction1); + + MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true); + List? statementLines = merchantStatementForDate.GetStatementLines(); statementLines.ShouldNotBeNull(); statementLines.ShouldNotBeEmpty(); statementLines.Count.ShouldBe(1); } [Fact] - public void MerchantStatementAggregate_AddSettledFeeToStatement_FeeAddedToStatement() + public void MerchantStatementForDateAggregate_AddSettledFeeToStatement_FeeAddedToStatement() { - MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); - merchantStatementAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, - TestData.EventId1, - TestData.StatementCreateDate, - TestData.EstateId, - TestData.MerchantId, TestData.SettledFee1); + MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1); + merchantStatementForDateAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, TestData.StatementDate, TestData.EventId1, TestData.EstateId, TestData.MerchantId, TestData.SettledFee1); - MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(true); - var statementLines = merchantStatement.GetStatementLines(); + MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true); + List? statementLines = merchantStatementForDate.GetStatementLines(); statementLines.ShouldNotBeNull(); statementLines.ShouldNotBeEmpty(); statementLines.Count.ShouldBe(1); } - + [Fact] - public void MerchantStatementAggregate_AddSettledFeeToStatement_DuplicateFee_Silentlyhandled() + public void MerchantStatementForDateAggregate_AddSettledFeeToStatement_DuplicateFee_Silentlyhandled() { - MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); - merchantStatementAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, - TestData.EventId1, - TestData.StatementCreateDate, - TestData.EstateId, - TestData.MerchantId, TestData.SettledFee1); + MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1); + merchantStatementForDateAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, TestData.StatementDate, TestData.EventId1, TestData.EstateId, TestData.MerchantId, TestData.SettledFee1); + merchantStatementForDateAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, TestData.StatementDate, TestData.EventId1, TestData.EstateId, TestData.MerchantId, TestData.SettledFee1); - merchantStatementAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, - TestData.EventId1, - TestData.StatementCreateDate, - TestData.EstateId, - TestData.MerchantId, TestData.SettledFee1); - - MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(true); - var statementLines = merchantStatement.GetStatementLines(); + MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true); + List? statementLines = merchantStatementForDate.GetStatementLines(); statementLines.ShouldNotBeNull(); statementLines.ShouldNotBeEmpty(); statementLines.Count.ShouldBe(1); } + /* [Fact] public void MerchantStatementAggregate_GenerateStatement_StatementIsGenerated() { @@ -216,6 +204,52 @@ public void MerchantStatementAggregate_EmailStatement_NotGenerated_ErrorThrown() { merchantStatementAggregate.EmailStatement(TestData.StatementEmailedDate, TestData.MessageId); }); + }*/ + } + + public class MerchantStatementAggregateTests { + [Fact] + public void MerchantStatementAggregate_CanBeCreated_IsCreated() + { + MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); + + merchantStatementAggregate.ShouldNotBeNull(); + merchantStatementAggregate.AggregateId.ShouldBe(TestData.MerchantStatementId); + } + + [Fact] + public void MerchantStatementAggregate_RecordActivityDateOnStatement_ActivityDateIsRecorded() { + MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); + merchantStatementAggregate.RecordActivityDateOnStatement(TestData.MerchantStatementId, + TestData.StatementDate, TestData.EstateId, TestData.MerchantId, TestData.MerchantStatementForDateId1, + TestData.ActivityDate1); + + MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(); + merchantStatement.IsCreated.ShouldBe(merchantStatement.IsCreated); + List<(Guid merchantStatementForDateId, DateTime activityDate)>? activityDates = merchantStatement.GetActivityDates(); + activityDates.ShouldNotBeNull(); + activityDates.ShouldNotBeEmpty(); + activityDates.Count.ShouldBe(1); + } + + [Fact] + public void MerchantStatementAggregate_RecordActivityDateOnStatement_DuplicateActivityDate_Silentlyhandled() + { + MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId); + merchantStatementAggregate.RecordActivityDateOnStatement(TestData.MerchantStatementId, + TestData.StatementDate, TestData.EstateId, TestData.MerchantId, TestData.MerchantStatementForDateId1, + TestData.ActivityDate1); + + merchantStatementAggregate.RecordActivityDateOnStatement(TestData.MerchantStatementId, + TestData.StatementDate, TestData.EstateId, TestData.MerchantId, TestData.MerchantStatementForDateId1, + TestData.ActivityDate1); + + MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement(); + merchantStatement.IsCreated.ShouldBe(merchantStatement.IsCreated); + List<(Guid merchantStatementForDateId, DateTime activityDate)>? activityDates = merchantStatement.GetActivityDates(); + activityDates.ShouldNotBeNull(); + activityDates.ShouldNotBeEmpty(); + activityDates.Count.ShouldBe(1); } } } diff --git a/TransactionProcessor.Aggregates/MerchantStatementAggregate.cs b/TransactionProcessor.Aggregates/MerchantStatementAggregate.cs index f1bc76a5..cfc3baae 100644 --- a/TransactionProcessor.Aggregates/MerchantStatementAggregate.cs +++ b/TransactionProcessor.Aggregates/MerchantStatementAggregate.cs @@ -1,311 +1,201 @@ -using System.Diagnostics.CodeAnalysis; -using Shared.DomainDrivenDesign.EventSourcing; +using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.Aggregate; using Shared.General; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using TransactionProcessor.Aggregates.Models; using TransactionProcessor.DomainEvents; using TransactionProcessor.Models.Merchant; namespace TransactionProcessor.Aggregates { - public static class MerchantStatementAggregateExtenions{ - public static void AddSettledFeeToStatement(this MerchantStatementAggregate aggregate, - Guid statementId, - Guid eventId, - DateTime createdDate, - Guid estateId, - Guid merchantId, - SettledFee settledFee){ - // Create statement id required - aggregate.CreateStatement(statementId, createdDate, estateId, merchantId); - - MerchantStatementDomainEvents.SettledFeeAddedToStatementEvent settledFeeAddedToStatementEvent = - new MerchantStatementDomainEvents.SettledFeeAddedToStatementEvent(aggregate.AggregateId, - eventId, - aggregate.EstateId, - aggregate.MerchantId, - settledFee.SettledFeeId, - settledFee.TransactionId, - settledFee.DateTime, - settledFee.Amount); - - aggregate.ApplyAndAppend(settledFeeAddedToStatementEvent); - } - - public static void AddTransactionToStatement(this MerchantStatementAggregate aggregate, - Guid statementId, - Guid eventId, - DateTime createdDate, - Guid estateId, - Guid merchantId, - Transaction transaction){ - // Create statement id required - aggregate.CreateStatement(statementId, createdDate, estateId, merchantId); - - MerchantStatementDomainEvents.TransactionAddedToStatementEvent transactionAddedToStatementEvent = new MerchantStatementDomainEvents.TransactionAddedToStatementEvent(aggregate.AggregateId, - eventId, - aggregate.EstateId, - aggregate.MerchantId, - transaction.TransactionId, - transaction.DateTime, - transaction.Amount); - - aggregate.ApplyAndAppend(transactionAddedToStatementEvent); - } - - private static void CreateStatement(this MerchantStatementAggregate aggregate, - Guid statementId, - DateTime createdDate, - Guid estateId, - Guid merchantId) - { - if (aggregate.IsCreated == false) - { - Guard.ThrowIfInvalidGuid(statementId, nameof(statementId)); - Guard.ThrowIfInvalidGuid(estateId, nameof(estateId)); - Guard.ThrowIfInvalidGuid(merchantId, nameof(merchantId)); - - MerchantStatementDomainEvents.StatementCreatedEvent statementCreatedEvent = new MerchantStatementDomainEvents.StatementCreatedEvent(statementId, estateId, merchantId, createdDate); - - aggregate.ApplyAndAppend(statementCreatedEvent); - } - } - - public static void EmailStatement(this MerchantStatementAggregate aggregate, - DateTime emailedDateTime, - Guid messageId) - { - aggregate.EnsureStatementHasBeenCreated(); - aggregate.EnsureStatementHasBeenGenerated(); - - MerchantStatementDomainEvents.StatementEmailedEvent statementEmailedEvent = new MerchantStatementDomainEvents.StatementEmailedEvent(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, emailedDateTime, messageId); - - aggregate.ApplyAndAppend(statementEmailedEvent); - } - - public static void GenerateStatement(this MerchantStatementAggregate aggregate, - DateTime generatedDateTime) - { - aggregate.EnsureStatementHasNotAlreadyBeenGenerated(); - - if (aggregate.Transactions.Any() == false && aggregate.SettledFees.Any() == false) - { - throw new InvalidOperationException("Statement has no transactions or settled fees"); - } - - MerchantStatementDomainEvents.StatementGeneratedEvent statementGeneratedEvent = new MerchantStatementDomainEvents.StatementGeneratedEvent(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, generatedDateTime); - - aggregate.ApplyAndAppend(statementGeneratedEvent); - } - - public static MerchantStatement GetStatement(this MerchantStatementAggregate aggregate, Boolean includeStatementLines = false) - { - MerchantStatement merchantStatement = new MerchantStatement - { - EstateId = aggregate.EstateId, - MerchantId = aggregate.MerchantId, - MerchantStatementId = aggregate.AggregateId, - IsCreated = aggregate.IsCreated, - IsGenerated = aggregate.IsGenerated, - HasBeenEmailed = aggregate.HasBeenEmailed, - StatementCreatedDateTime = aggregate.CreatedDateTime, - StatementGeneratedDateTime = aggregate.GeneratedDateTime - }; - - if (includeStatementLines) - { - foreach (Transaction transaction in aggregate.Transactions) - { - merchantStatement.AddStatementLine(new MerchantStatementLine - { - Amount = transaction.Amount, - DateTime = transaction.DateTime, - Description = string.Empty, - LineType = 1 // Transaction - }); - } - - foreach (SettledFee settledFee in aggregate.SettledFees) - { - merchantStatement.AddStatementLine(new MerchantStatementLine - { - Amount = settledFee.Amount, - DateTime = settledFee.DateTime, - Description = string.Empty, - LineType = 2 // Settled Fee - }); - } - } - - return merchantStatement; - } - - private static void EnsureStatementHasBeenCreated(this MerchantStatementAggregate aggregate) - { - if (aggregate.IsCreated == false) - { - throw new InvalidOperationException("Statement has not been created"); - } - } - - private static void EnsureStatementHasBeenGenerated(this MerchantStatementAggregate aggregate) - { - if (aggregate.IsGenerated == false) - { - throw new InvalidOperationException("Statement has not been generated"); - } - } - - private static void EnsureStatementHasNotAlreadyBeenGenerated(this MerchantStatementAggregate aggregate) - { - if (aggregate.IsGenerated) - { - throw new InvalidOperationException("Statement has already been generated"); - } - } - - public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.StatementCreatedEvent domainEvent) - { - aggregate.IsCreated = true; - aggregate.EstateId = domainEvent.EstateId; - aggregate.MerchantId = domainEvent.MerchantId; - aggregate.CreatedDateTime = domainEvent.DateCreated; - } - - public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.TransactionAddedToStatementEvent domainEvent) - { - aggregate.EstateId = domainEvent.EstateId; - aggregate.MerchantId = domainEvent.MerchantId; - - aggregate.Transactions.Add(new Transaction(domainEvent.TransactionId, domainEvent.TransactionDateTime, domainEvent.TransactionValue)); - } - - public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.SettledFeeAddedToStatementEvent domainEvent) - { - aggregate.EstateId = domainEvent.EstateId; - aggregate.MerchantId = domainEvent.MerchantId; - - aggregate.SettledFees.Add(new SettledFee(domainEvent.SettledFeeId, domainEvent.TransactionId, domainEvent.SettledDateTime, domainEvent.SettledValue)); - } - - public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.StatementGeneratedEvent domainEvent) - { - aggregate.IsGenerated = true; - aggregate.GeneratedDateTime = domainEvent.DateGenerated; - } - - public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.StatementEmailedEvent domainEvent) - { - aggregate.HasBeenEmailed = true; - aggregate.EmailedDateTime = domainEvent.DateEmailed; - aggregate.EmailMessageId = domainEvent.MessageId; - } - } - public record MerchantStatementAggregate : Aggregate { #region Fields - /// - /// The created date time - /// - internal DateTime CreatedDateTime; + internal DateTime StatementDate; - /// - /// The emailed date time - /// internal DateTime EmailedDateTime; - /// - /// The email message identifier - /// internal Guid EmailMessageId; - /// - /// The estate identifier - /// internal Guid EstateId; - /// - /// The generated date time - /// internal DateTime GeneratedDateTime; - /// - /// The has been emailed - /// internal Boolean HasBeenEmailed; - /// - /// The is created - /// internal Boolean IsCreated; - /// - /// The is generated - /// internal Boolean IsGenerated; - /// - /// The merchant identifier - /// internal Guid MerchantId; - /// - /// The settled fees - /// - internal readonly List SettledFees; - - /// - /// The transactions - /// - internal readonly List Transactions; - + internal List<(Guid merchantStatementForDateId, DateTime activityDate)> ActivityDates; #endregion #region Constructors - /// - /// Initializes a new instance of the class. - /// [ExcludeFromCodeCoverage] public MerchantStatementAggregate() { // Nothing here - this.Transactions = new List(); - this.SettledFees = new List(); + this.ActivityDates = new(); } - /// - /// Initializes a new instance of the class. - /// - /// The aggregate identifier. private MerchantStatementAggregate(Guid aggregateId) { Guard.ThrowIfInvalidGuid(aggregateId, "Aggregate Id cannot be an Empty Guid"); this.AggregateId = aggregateId; - this.Transactions = new List(); - this.SettledFees = new List(); + this.ActivityDates = new(); } #endregion #region Methods - + public static MerchantStatementAggregate Create(Guid aggregateId) { return new MerchantStatementAggregate(aggregateId); } - public override void PlayEvent(IDomainEvent domainEvent) => MerchantStatementAggregateExtenions.PlayEvent(this, (dynamic)domainEvent); - + public override void PlayEvent(IDomainEvent domainEvent) => MerchantStatementAggregateExtensions.PlayEvent(this, (dynamic)domainEvent); + [ExcludeFromCodeCoverage] protected override Object GetMetadata() { return null; } - + #endregion } + + public static class MerchantStatementAggregateExtensions + { + public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.StatementCreatedEvent domainEvent) + { + aggregate.IsCreated = true; + aggregate.EstateId = domainEvent.EstateId; + aggregate.MerchantId = domainEvent.MerchantId; + aggregate.StatementDate = domainEvent.StatementDate; + } + + //public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.StatementGeneratedEvent domainEvent) + //{ + // aggregate.IsGenerated = true; + // aggregate.GeneratedDateTime = domainEvent.DateGenerated; + //} + + //public static void PlayEvent(this MerchantStatementAggregate aggregate, MerchantStatementDomainEvents.StatementEmailedEvent domainEvent) + //{ + // aggregate.HasBeenEmailed = true; + // aggregate.EmailedDateTime = domainEvent.DateEmailed; + // aggregate.EmailMessageId = domainEvent.MessageId; + //} + + public static void PlayEvent(this MerchantStatementAggregate aggregate, + MerchantStatementDomainEvents.ActivityDateAddedToStatementEvent domainEvent) { + aggregate.ActivityDates.Add((domainEvent.MerchantStatementForDateId, domainEvent.ActivityDate)); + } + + public static void RecordActivityDateOnStatement(this MerchantStatementAggregate aggregate, + Guid statementId, + DateTime statementDate, + Guid estateId, + Guid merchantId, + Guid merchantStatementForDateId, + DateTime activityDate) + { + if (aggregate.IsCreated == false) + { + Guard.ThrowIfInvalidGuid(statementId, nameof(statementId)); + Guard.ThrowIfInvalidGuid(estateId, nameof(estateId)); + Guard.ThrowIfInvalidGuid(merchantId, nameof(merchantId)); + + MerchantStatementDomainEvents.StatementCreatedEvent statementCreatedEvent = new(statementId, estateId, merchantId, statementDate); + + aggregate.ApplyAndAppend(statementCreatedEvent); + } + + if (aggregate.ActivityDates.SingleOrDefault(a => a.activityDate == activityDate) != default) + { + // Activity date already exists + return; + } + + MerchantStatementDomainEvents.ActivityDateAddedToStatementEvent activityDateAddedToStatementEvent = new(statementId, estateId, merchantId, merchantStatementForDateId, activityDate); + aggregate.ApplyAndAppend(activityDateAddedToStatementEvent); + } + + public static MerchantStatement GetStatement(this MerchantStatementAggregate aggregate) + { + MerchantStatement merchantStatement = new() + { + EstateId = aggregate.EstateId, + MerchantId = aggregate.MerchantId, + IsCreated = aggregate.IsCreated, + StatementDate = aggregate.StatementDate, + MerchantStatementId = aggregate.AggregateId, + }; + + foreach ((Guid merchantStatementForDateId, DateTime activityDate) aggregateActivityDate in aggregate.ActivityDates) { + merchantStatement.AddStatementActivityDate(aggregateActivityDate.merchantStatementForDateId, aggregateActivityDate.activityDate); + } + + return merchantStatement; + } + + //public static void EmailStatement(this MerchantStatementAggregate aggregate, + // DateTime emailedDateTime, + // Guid messageId) + //{ + // aggregate.EnsureStatementHasBeenCreated(); + // aggregate.EnsureStatementHasBeenGenerated(); + + // MerchantStatementDomainEvents.StatementEmailedEvent statementEmailedEvent = new(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, emailedDateTime, messageId); + + // aggregate.ApplyAndAppend(statementEmailedEvent); + //} + + //public static void GenerateStatement(this MerchantStatementAggregate aggregate, + // DateTime generatedDateTime) + //{ + // aggregate.EnsureStatementHasNotAlreadyBeenGenerated(); + + // // TODO: Validate days have been added + // //if (aggregate.Transactions.Any() == false && aggregate.SettledFees.Any() == false) + // //{ + // // throw new InvalidOperationException("Statement has no transactions or settled fees"); + // //} + + // MerchantStatementDomainEvents.StatementGeneratedEvent statementGeneratedEvent = new(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, generatedDateTime); + + // aggregate.ApplyAndAppend(statementGeneratedEvent); + //} + + //private static void EnsureStatementHasBeenCreated(this MerchantStatementAggregate aggregate) + //{ + // if (aggregate.IsCreated == false) + // { + // throw new InvalidOperationException("Statement header has not been created"); + // } + //} + + //private static void EnsureStatementHasBeenGenerated(this MerchantStatementAggregate aggregate) + //{ + // if (aggregate.IsGenerated == false) + // { + // throw new InvalidOperationException("Statement header has not been generated"); + // } + //} + + //private static void EnsureStatementHasNotAlreadyBeenGenerated(this MerchantStatementAggregate aggregate) + //{ + // if (aggregate.IsGenerated) + // { + // throw new InvalidOperationException("Statement header has already been generated"); + // } + //} + } } \ No newline at end of file diff --git a/TransactionProcessor.Aggregates/MerchantStatementForDateAggregate.cs b/TransactionProcessor.Aggregates/MerchantStatementForDateAggregate.cs new file mode 100644 index 00000000..e6c57af8 --- /dev/null +++ b/TransactionProcessor.Aggregates/MerchantStatementForDateAggregate.cs @@ -0,0 +1,182 @@ +using Shared.DomainDrivenDesign.EventSourcing; +using Shared.EventStore.Aggregate; +using Shared.General; +using System.Diagnostics.CodeAnalysis; +using TransactionProcessor.Aggregates.Models; +using TransactionProcessor.DomainEvents; +using TransactionProcessor.Models.Merchant; + +namespace TransactionProcessor.Aggregates; + +public static class MerchantStatementForDateAggregateExtensions +{ + public static void AddSettledFeeToStatement(this MerchantStatementForDateAggregate aggregate, + Guid merchantStatementId, + DateTime statementDate, + Guid eventId, + Guid estateId, + Guid merchantId, + SettledFee settledFee) { + // Create statement id required + aggregate.CreateStatementForDate(merchantStatementId, settledFee.DateTime.Date, statementDate, estateId, merchantId); + + MerchantStatementForDateDomainEvents.SettledFeeAddedToStatementForDateEvent settledFeeAddedToStatementEvent = new(aggregate.AggregateId, eventId, aggregate.EstateId, aggregate.MerchantId, settledFee.SettledFeeId, settledFee.TransactionId, settledFee.DateTime, settledFee.Amount); + + aggregate.ApplyAndAppend(settledFeeAddedToStatementEvent); + } + + public static void AddTransactionToStatement(this MerchantStatementForDateAggregate aggregate, + Guid merchantStatementId, + DateTime statementDate, + Guid eventId, + Guid estateId, + Guid merchantId, + Transaction transaction) + { + // Create statement if required + aggregate.CreateStatementForDate(merchantStatementId, transaction.DateTime.Date, statementDate, estateId, merchantId); + + MerchantStatementForDateDomainEvents.TransactionAddedToStatementForDateEvent transactionAddedToStatementEvent = new(aggregate.AggregateId, + eventId, + aggregate.EstateId, + aggregate.MerchantId, + transaction.TransactionId, + transaction.DateTime, + transaction.Amount); + + aggregate.ApplyAndAppend(transactionAddedToStatementEvent); + } + + private static void CreateStatementForDate(this MerchantStatementForDateAggregate aggregate, + Guid merchantStatementId, + DateTime activityDate, + DateTime statementDate, + Guid estateId, + Guid merchantId) + { + if (aggregate.IsCreated == false) + { + Guard.ThrowIfInvalidGuid(merchantStatementId, nameof(merchantStatementId)); + Guard.ThrowIfInvalidGuid(estateId, nameof(estateId)); + Guard.ThrowIfInvalidGuid(merchantId, nameof(merchantId)); + + MerchantStatementForDateDomainEvents.StatementCreatedForDateEvent statementCreatedForDateEvent = new(aggregate.AggregateId, + activityDate, statementDate, merchantStatementId, estateId, merchantId); + + aggregate.ApplyAndAppend(statementCreatedForDateEvent); + } + } + + public static void PlayEvent(this MerchantStatementForDateAggregate aggregate, MerchantStatementForDateDomainEvents.StatementCreatedForDateEvent domainEvent) + { + aggregate.IsCreated = true; + aggregate.EstateId = domainEvent.EstateId; + aggregate.MerchantId = domainEvent.MerchantId; + aggregate.ActivityDate = domainEvent.ActivityDate; + aggregate.MerchantStatementId = domainEvent.MerchantStatementId; + aggregate.StatementDate = domainEvent.MerchantStatementDate; + } + public static void PlayEvent(this MerchantStatementForDateAggregate aggregate, MerchantStatementForDateDomainEvents.TransactionAddedToStatementForDateEvent domainEvent) + { + aggregate.Transactions.Add(new Transaction(domainEvent.TransactionId, domainEvent.TransactionDateTime, domainEvent.TransactionValue)); + } + + public static void PlayEvent(this MerchantStatementForDateAggregate aggregate, MerchantStatementForDateDomainEvents.SettledFeeAddedToStatementForDateEvent domainEvent) + { + aggregate.SettledFees.Add(new SettledFee(domainEvent.SettledFeeId, domainEvent.TransactionId, domainEvent.SettledDateTime, domainEvent.SettledValue)); + } + + public static MerchantStatementForDate GetStatement(this MerchantStatementForDateAggregate aggregate, Boolean includeStatementLines = false) + { + MerchantStatementForDate merchantStatement = new MerchantStatementForDate + { + EstateId = aggregate.EstateId, + MerchantId = aggregate.MerchantId, + MerchantStatementId = aggregate.MerchantStatementId, + IsCreated = aggregate.IsCreated, + ActivityDate = aggregate.ActivityDate, + MerchantStatementForDateId = aggregate.AggregateId, + StatementDate = aggregate.StatementDate + }; + + if (includeStatementLines) + { + foreach (Transaction transaction in aggregate.Transactions) + { + merchantStatement.AddStatementLine(new MerchantStatementLine + { + Amount = transaction.Amount, + DateTime = transaction.DateTime, + Description = string.Empty, + LineType = 1 // Transaction + }); + } + + foreach (SettledFee settledFee in aggregate.SettledFees) + { + merchantStatement.AddStatementLine(new MerchantStatementLine + { + Amount = settledFee.Amount, + DateTime = settledFee.DateTime, + Description = string.Empty, + LineType = 2 // Settled Fee + }); + } + } + + return merchantStatement; + } +} + +public record MerchantStatementForDateAggregate : Aggregate +{ + #region Fields + + internal DateTime ActivityDate; + internal Guid EstateId; + internal Boolean IsCreated; + internal Guid MerchantId; + internal readonly List SettledFees; + internal readonly List Transactions; + internal Guid MerchantStatementId; + internal DateTime StatementDate; + #endregion + + #region Constructors + + [ExcludeFromCodeCoverage] + public MerchantStatementForDateAggregate() + { + // Nothing here + this.Transactions = new List(); + this.SettledFees = new List(); + } + + private MerchantStatementForDateAggregate(Guid aggregateId) + { + Guard.ThrowIfInvalidGuid(aggregateId, "Aggregate Id cannot be an Empty Guid"); + + this.AggregateId = aggregateId; + this.Transactions = new List(); + this.SettledFees = new List(); + } + + #endregion + + #region Methods + + public static MerchantStatementForDateAggregate Create(Guid aggregateId) + { + return new MerchantStatementForDateAggregate(aggregateId); + } + + public override void PlayEvent(IDomainEvent domainEvent) => MerchantStatementForDateAggregateExtensions.PlayEvent(this, (dynamic)domainEvent); + + [ExcludeFromCodeCoverage] + protected override Object GetMetadata() + { + return null; + } + + #endregion +} \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantSettlementDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantSettlementDomainEventHandlerTests.cs deleted file mode 100644 index 510894b1..00000000 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantSettlementDomainEventHandlerTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Client; -using Shouldly; -using TransactionProcessor.BusinessLogic.EventHandling; -using TransactionProcessor.Testing; -using Xunit; -using static TransactionProcessor.DomainEvents.SettlementDomainEvents; -using MediatR; -using Moq; -using Shared.Logger; -using SimpleResults; -using TransactionProcessor.BusinessLogic.Requests; -using Grpc.Core; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace TransactionProcessor.BusinessLogic.Tests.DomainEventHandlers -{ - public class MerchantSettlementDomainEventHandlerTests : DomainEventHandlerTests - { - private readonly MerchantSettlementDomainEventHandler EventHandler; - - public MerchantSettlementDomainEventHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - this.EventHandler = new MerchantSettlementDomainEventHandler(this.Mediator.Object); - } - - [Fact] - public async Task MerchantSettlementDomainEventHandler_Handle_MerchantFeeSettledEvent_EventIsHandled() - { - this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())) - .ReturnsAsync(Result.Success()); - - Result result = await this.EventHandler.Handle(TestData.DomainEvents.MerchantFeeSettledEvent, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - } - } -} diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs index 66c8ccf4..cbefca00 100644 --- a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/MerchantStatementDomainEventHandlerTests.cs @@ -44,4 +44,23 @@ public async Task MerchantStatementDomainEventHandler_Handle_TransactionHasBeenC result.IsSuccess.ShouldBeTrue(); } + [Fact] + public async Task MerchantStatementDomainEventHandler_Handle_MerchantFeeSettledEvent_EventIsHandled() + { + this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())) + .ReturnsAsync(Result.Success()); + + Result result = await this.EventHandler.Handle(TestData.DomainEvents.MerchantFeeSettledEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + } + + [Fact] + public async Task MerchantStatementDomainEventHandler_Handle_StatementCreatedForDateEvent_EventIsHandled() + { + this.Mediator.Setup(m => m.Send(It.IsAny>(), It.IsAny())) + .ReturnsAsync(Result.Success()); + + Result result = await this.EventHandler.Handle(TestData.DomainEvents.StatementCreatedForDateEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + } } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic.Tests/Mediator/DummyVoucherDomainService.cs b/TransactionProcessor.BusinessLogic.Tests/Mediator/DummyVoucherDomainService.cs index 1963e01b..5e1e0b14 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Mediator/DummyVoucherDomainService.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Mediator/DummyVoucherDomainService.cs @@ -1,4 +1,5 @@ using SimpleResults; +using TransactionProcessor.BusinessLogic.Manager; namespace TransactionProcessor.BusinessLogic.Tests.Mediator; @@ -30,3 +31,26 @@ public async Task> RedeemVoucher(Guid estateId, } } +public class DummyVoucherManagementManager : IVoucherManagementManager { + public async Task> GetVoucherByCode(Guid estateId, + String voucherCode, + CancellationToken cancellationToken) { + return Result.Success(new Voucher + { + ExpiryDate = DateTime.UtcNow.AddDays(30), + VoucherCode = voucherCode, + }); + } + + public async Task> GetVoucherByTransactionId(Guid estateId, + Guid transactionId, + CancellationToken cancellationToken) { + return Result.Success(new Voucher + { + ExpiryDate = DateTime.UtcNow.AddDays(30), + VoucherCode = "TEST-VOUCHER-CODE", + TransactionId = transactionId, + }); + } +} + diff --git a/TransactionProcessor.BusinessLogic.Tests/Mediator/MediatorTests.cs b/TransactionProcessor.BusinessLogic.Tests/Mediator/MediatorTests.cs index c2612054..00997807 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Mediator/MediatorTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Mediator/MediatorTests.cs @@ -92,15 +92,21 @@ public MediatorTests() this.Requests.Add(TestData.Queries.GetTransactionFeesForProductQuery); // Merchant Statement Commands - this.Requests.Add(TestData.Commands.GenerateMerchantStatementCommand); + //this.Requests.Add(TestData.Commands.GenerateMerchantStatementCommand); this.Requests.Add(TestData.Commands.AddTransactionToMerchantStatementCommand); - this.Requests.Add(TestData.Commands.EmailMerchantStatementCommand); + //this.Requests.Add(TestData.Commands.EmailMerchantStatementCommand); this.Requests.Add(TestData.Commands.AddSettledFeeToMerchantStatementCommand); + this.Requests.Add(TestData.Commands.RecordActivityDateOnMerchantStatementCommand); // Settlement Commands and Queries this.Requests.Add(TestData.Queries.GetSettlementQuery); this.Requests.Add(TestData.Queries.GetSettlementsQuery); this.Requests.Add(TestData.Queries.GetPendingSettlementQuery); + + // Voucher Queries + this.Requests.Add(TestData.Queries.GetVoucherByVoucherCodeQuery); + this.Requests.Add(TestData.Queries.GetVoucherByTransactionIdQuery); + } [Fact] @@ -171,6 +177,7 @@ private void AddTestRegistrations(ServiceRegistry services, s.AddSingleton(); s.AddSingleton(); s.AddSingleton(); + s.AddSingleton(); s.AddSingleton(); }); } diff --git a/TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs b/TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs index 836b17b9..b9f36322 100644 --- a/TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs +++ b/TransactionProcessor.BusinessLogic/Common/PolicyFactory.cs @@ -7,11 +7,13 @@ using Shared.Logger; using SimpleResults; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; namespace TransactionProcessor.BusinessLogic.Common; +[ExcludeFromCodeCoverage] public static class PolicyFactory{ public static IAsyncPolicy CreatePolicy(Int32 retryCount=5, TimeSpan? retryDelay = null, String policyTag="", Boolean withFallBack=false) { diff --git a/TransactionProcessor.BusinessLogic/EventHandling/MerchantSettlementDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/MerchantSettlementDomainEventHandler.cs deleted file mode 100644 index 906fa34a..00000000 --- a/TransactionProcessor.BusinessLogic/EventHandling/MerchantSettlementDomainEventHandler.cs +++ /dev/null @@ -1,54 +0,0 @@ -using MediatR; -using Polly; -using Shared.DomainDrivenDesign.EventSourcing; -using Shared.EventStore.EventHandling; -using SimpleResults; -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Prometheus; -using TransactionProcessor.BusinessLogic.Common; -using TransactionProcessor.BusinessLogic.Requests; -using TransactionProcessor.BusinessLogic.Services; -using TransactionProcessor.DomainEvents; - -namespace TransactionProcessor.BusinessLogic.EventHandling; - -public class MerchantSettlementDomainEventHandler : IDomainEventHandler { - private readonly IMediator Mediator; - - public MerchantSettlementDomainEventHandler(IMediator mediator) { - this.Mediator = mediator; - } - - public async Task Handle(IDomainEvent domainEvent, - CancellationToken cancellationToken) { - - Stopwatch sw = Stopwatch.StartNew(); - String g = domainEvent.GetType().Name; - String m = this.GetType().Name; - Counter counterCalls = AggregateService.GetCounterMetric($"{m}_{g}_events_processed"); - Histogram histogramMetric = AggregateService.GetHistogramMetric($"{m}_{g}"); - counterCalls.Inc(); - - Task t = domainEvent switch { - SettlementDomainEvents.MerchantFeeSettledEvent mfse => this.HandleSpecificDomainEvent(mfse, cancellationToken), - _ => null - }; - - Result result = Result.Success(); - if (t != null) - result = await t; - sw.Stop(); - histogramMetric.Observe(sw.Elapsed.TotalSeconds); - return result; - } - - private async Task HandleSpecificDomainEvent(SettlementDomainEvents.MerchantFeeSettledEvent domainEvent, - CancellationToken cancellationToken) { - MerchantStatementCommands.AddSettledFeeToMerchantStatementCommand command = new(domainEvent.EstateId, domainEvent.MerchantId, domainEvent.FeeCalculatedDateTime, domainEvent.CalculatedValue, domainEvent.TransactionId, domainEvent.FeeId); - - return await this.Mediator.Send(command, cancellationToken); - } -} \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs index 64a7dc08..98f948b3 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/MerchantStatementDomainEventHandler.cs @@ -54,8 +54,10 @@ public async Task Handle(IDomainEvent domainEvent, Task t = domainEvent switch { + SettlementDomainEvents.MerchantFeeSettledEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), MerchantStatementDomainEvents.StatementGeneratedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), TransactionDomainEvents.TransactionHasBeenCompletedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), + MerchantStatementForDateDomainEvents.StatementCreatedForDateEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), _ => null }; @@ -67,6 +69,13 @@ public async Task Handle(IDomainEvent domainEvent, return result; } + private async Task HandleSpecificDomainEvent(MerchantStatementForDateDomainEvents.StatementCreatedForDateEvent domainEvent, + CancellationToken cancellationToken) { + MerchantStatementCommands.RecordActivityDateOnMerchantStatementCommand command = new(domainEvent.EstateId, domainEvent.MerchantId, domainEvent.MerchantStatementId, domainEvent.MerchantStatementDate, + domainEvent.MerchantStatementForDateId, domainEvent.ActivityDate); + return await this.Mediator.Send(command, cancellationToken); + } + /// /// Handles the specific domain event. /// @@ -86,6 +95,14 @@ private async Task HandleSpecificDomainEvent(TransactionDomainEvents.Tra return await this.Mediator.Send(command, cancellationToken); } + private async Task HandleSpecificDomainEvent(SettlementDomainEvents.MerchantFeeSettledEvent domainEvent, + CancellationToken cancellationToken) + { + MerchantStatementCommands.AddSettledFeeToMerchantStatementCommand command = new(domainEvent.EstateId, domainEvent.MerchantId, domainEvent.FeeCalculatedDateTime, domainEvent.CalculatedValue, domainEvent.TransactionId, domainEvent.FeeId); + + return await this.Mediator.Send(command, cancellationToken); + } + #endregion } } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs index f16e06e3..ceaba3bd 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs @@ -91,8 +91,10 @@ public async Task Handle(IDomainEvent domainEvent, FileProcessor.File.DomainEvents.FileProcessingCompletedEvent de => this.EstateReportingRepository.UpdateFileAsComplete(de, cancellationToken), MerchantStatementDomainEvents.StatementCreatedEvent de => this.EstateReportingRepository.CreateStatement(de, cancellationToken), - MerchantStatementDomainEvents.TransactionAddedToStatementEvent de => this.EstateReportingRepository.AddTransactionToStatement(de, cancellationToken), - MerchantStatementDomainEvents.SettledFeeAddedToStatementEvent de => this.EstateReportingRepository.AddSettledFeeToStatement(de, cancellationToken), + // TODO@ Add this back in + //MerchantStatementDomainEvents.TransactionAddedToStatementEvent de => this.EstateReportingRepository.AddTransactionToStatement(de, cancellationToken), + // TODO@ Add this back in + //MerchantStatementDomainEvents.SettledFeeAddedToStatementEvent de => this.EstateReportingRepository.AddSettledFeeToStatement(de, cancellationToken), MerchantStatementDomainEvents.StatementGeneratedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), FloatCreatedForContractProductEvent de => this.EstateReportingRepository.CreateFloat(de, cancellationToken), diff --git a/TransactionProcessor.BusinessLogic/RequestHandlers/MerchantStatementRequestHandler.cs b/TransactionProcessor.BusinessLogic/RequestHandlers/MerchantStatementRequestHandler.cs index 31fd69b8..f8c1308d 100644 --- a/TransactionProcessor.BusinessLogic/RequestHandlers/MerchantStatementRequestHandler.cs +++ b/TransactionProcessor.BusinessLogic/RequestHandlers/MerchantStatementRequestHandler.cs @@ -9,9 +9,9 @@ namespace TransactionProcessor.BusinessLogic.RequestHandlers { public class MerchantStatementRequestHandler : IRequestHandler, IRequestHandler, - IRequestHandler, - IRequestHandler - { + //IRequestHandler, + //IRequestHandler, + IRequestHandler { #region Fields /// @@ -64,15 +64,21 @@ public async Task Handle(MerchantStatementCommands.AddSettledFeeToMercha #endregion - public async Task Handle(MerchantCommands.GenerateMerchantStatementCommand command, CancellationToken cancellationToken) - { - return await this.MerchantStatementDomainService.GenerateStatement(command, cancellationToken); - } + //public async Task Handle(MerchantCommands.GenerateMerchantStatementCommand command, CancellationToken cancellationToken) + //{ + // return await this.MerchantStatementDomainService.GenerateStatement(command, cancellationToken); + //} - public async Task Handle(MerchantStatementCommands.EmailMerchantStatementCommand command, - CancellationToken cancellationToken) - { - return await this.MerchantStatementDomainService.EmailStatement(command, cancellationToken); + //public async Task Handle(MerchantStatementCommands.EmailMerchantStatementCommand command, + // CancellationToken cancellationToken) + //{ + // return await this.MerchantStatementDomainService.EmailStatement(command, cancellationToken); + //} + + public async Task Handle(MerchantStatementCommands.RecordActivityDateOnMerchantStatementCommand request, + CancellationToken cancellationToken) { + return await this.MerchantStatementDomainService.RecordActivityDateOnMerchantStatement(request, + cancellationToken); } } } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/Requests/MerchantStatementCommands.cs b/TransactionProcessor.BusinessLogic/Requests/MerchantStatementCommands.cs index f3f7d421..a4810f47 100644 --- a/TransactionProcessor.BusinessLogic/Requests/MerchantStatementCommands.cs +++ b/TransactionProcessor.BusinessLogic/Requests/MerchantStatementCommands.cs @@ -24,4 +24,11 @@ public record AddSettledFeeToMerchantStatementCommand(Guid EstateId, Decimal SettledAmount, Guid TransactionId, Guid SettledFeeId) : IRequest; + + public record RecordActivityDateOnMerchantStatementCommand(Guid EstateId, + Guid MerchantId, + Guid MerchantStatementId, + DateTime StatementDate, + Guid MerchantStatementForDateId, + DateTime StatementActivityDate) : IRequest; } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs b/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs index 4fdbf2a7..43c8d261 100644 --- a/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs +++ b/TransactionProcessor.BusinessLogic/Services/IdGenerationService.cs @@ -52,5 +52,22 @@ public static Guid GenerateFloatActivityAggregateId(Guid estateId, Guid floatId, floatId, dateTime }); + + public static Guid GenerateMerchantStatementForDateAggregateId(Guid estateId, Guid merchantId, DateTime nextStatementDateTime, DateTime activityDateTime) => + IdGenerationService.GenerateUniqueId(new + { + estateId, + merchantId, + nextStatementDate = nextStatementDateTime.Date, + activityDate = activityDateTime.Date + }); + + public static Guid GenerateMerchantStatementAggregateId(Guid estateId, Guid merchantId, DateTime nextStatementDateTime) => + IdGenerationService.GenerateUniqueId(new + { + estateId, + merchantId, + nextStatementDate = nextStatementDateTime.Date + }); } } diff --git a/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs index 3af9416b..519126da 100644 --- a/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs @@ -1,10 +1,4 @@ -using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO.Abstractions; -using System.Threading; -using System.Threading.Tasks; -using MessagingService.Client; +using MessagingService.Client; using SecurityService.Client; using SecurityService.DataTransferObjects.Responses; using Shared.DomainDrivenDesign.EventSourcing; @@ -14,10 +8,17 @@ using Shared.Logger; using Shared.Results; using SimpleResults; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO.Abstractions; +using System.Threading; +using System.Threading.Tasks; using TransactionProcessor.Aggregates; using TransactionProcessor.Aggregates.Models; using TransactionProcessor.BusinessLogic.Common; using TransactionProcessor.BusinessLogic.Requests; +using TransactionProcessor.Models.Merchant; using TransactionProcessor.Repository; using Transaction = TransactionProcessor.Aggregates.Models.Transaction; @@ -34,6 +35,7 @@ public interface IMerchantStatementDomainService Task GenerateStatement(MerchantCommands.GenerateMerchantStatementCommand command, CancellationToken cancellationToken); Task EmailStatement(MerchantStatementCommands.EmailMerchantStatementCommand command, CancellationToken cancellationToken); + Task RecordActivityDateOnMerchantStatement(MerchantStatementCommands.RecordActivityDateOnMerchantStatementCommand command, CancellationToken cancellationToken); #endregion } @@ -53,7 +55,7 @@ public MerchantStatementDomainService(IAggregateService aggregateService) #region Methods - private async Task ApplyUpdates(Func> action, Guid estateId, Guid statementId, CancellationToken cancellationToken, Boolean isNotFoundError = true) + private async Task ApplyUpdates(Func> action, Guid statementId, CancellationToken cancellationToken, Boolean isNotFoundError = true) { try { @@ -82,17 +84,47 @@ private async Task ApplyUpdates(Func ApplyUpdates(Func> action, Guid statementId, CancellationToken cancellationToken, Boolean isNotFoundError = true) + { + try + { + Result getMerchantStatementResult = await this.AggregateService.GetLatest(statementId, cancellationToken); + + Result merchantStatementAggregateResult = + DomainServiceHelper.HandleGetAggregateResult(getMerchantStatementResult, statementId, isNotFoundError); + if (merchantStatementAggregateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantStatementAggregateResult); + + MerchantStatementForDateAggregate merchantStatementAggregate = merchantStatementAggregateResult.Data; + + Result result = await action(merchantStatementAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + + Result saveResult = await this.AggregateService.Save(merchantStatementAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); + + return Result.Success(); + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } + } + public async Task AddSettledFeeToStatement(MerchantStatementCommands.AddSettledFeeToMerchantStatementCommand command, CancellationToken cancellationToken) { // Work out the next statement date DateTime nextStatementDate = CalculateStatementDate(command.SettledDateTime); - Guid statementId = GuidCalculator.Combine(command.MerchantId, nextStatementDate.ToGuid()); + Guid merchantStatementId = IdGenerationService.GenerateMerchantStatementAggregateId(command.EstateId, command.MerchantId, nextStatementDate); + Guid merchantStatementForDateId = IdGenerationService.GenerateMerchantStatementForDateAggregateId(command.EstateId, command.MerchantId, nextStatementDate, command.SettledDateTime); Guid settlementFeeId = GuidCalculator.Combine(command.TransactionId, command.SettledFeeId); Result result = await ApplyUpdates( - async (MerchantStatementAggregate merchantStatementAggregate) => { + async (MerchantStatementForDateAggregate merchantStatementForDateAggregate) => { SettledFee settledFee = new SettledFee(settlementFeeId, command.TransactionId, command.SettledDateTime, command.SettledAmount); @@ -104,11 +136,10 @@ public async Task AddSettledFeeToStatement(MerchantStatementCommands.Add command.SettledDateTime, }); - merchantStatementAggregate.AddSettledFeeToStatement(statementId, eventId, nextStatementDate, command.EstateId, command.MerchantId, settledFee); + merchantStatementForDateAggregate.AddSettledFeeToStatement(merchantStatementId, nextStatementDate, eventId, command.EstateId, command.MerchantId, settledFee); return Result.Success(); - }, - command.EstateId, statementId, cancellationToken, false); + }, merchantStatementForDateId, cancellationToken, false); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -123,7 +154,7 @@ public async Task AddSettledFeeToStatement(MerchantStatementCommands.Add /// internal static DateTime CalculateStatementDate(DateTime eventDateTime) { - var calculatedDateTime = eventDateTime.Date.AddMonths(1); + DateTime calculatedDateTime = eventDateTime.Date.AddMonths(1); return new DateTime(calculatedDateTime.Year, calculatedDateTime.Month, 1); } @@ -138,19 +169,19 @@ internal static DateTime CalculateStatementDate(DateTime eventDateTime) /// public async Task GenerateStatement(MerchantCommands.GenerateMerchantStatementCommand command, CancellationToken cancellationToken) { - Guid statementId = GuidCalculator.Combine(command.MerchantId, command.RequestDto.MerchantStatementDate.ToGuid()); + //Guid statementId = GuidCalculator.Combine(command.MerchantId, command.RequestDto.MerchantStatementDate.ToGuid()); - Result result = await ApplyUpdates( - async (MerchantStatementAggregate merchantStatementAggregate) => { + //Result result = await ApplyUpdates( + // async (MerchantStatementAggregate merchantStatementAggregate) => { - merchantStatementAggregate.GenerateStatement(DateTime.Now); + // merchantStatementAggregate.GenerateStatement(DateTime.Now); - return Result.Success(); - }, - command.EstateId, statementId, cancellationToken, false); + // return Result.Success(); + // }, + // command.EstateId, statementId, cancellationToken, false); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + //if (result.IsFailed) + // return ResultHelpers.CreateFailure(result); return Result.Success(); } @@ -158,57 +189,75 @@ public async Task GenerateStatement(MerchantCommands.GenerateMerchantSta public async Task EmailStatement(MerchantStatementCommands.EmailMerchantStatementCommand command, CancellationToken cancellationToken) { + //Result result = await ApplyUpdates( + // async (MerchantStatementAggregate merchantStatementAggregate) => { + + // //StatementHeader statementHeader = await this.EstateManagementRepository.GetStatement(command.EstateId, command.MerchantStatementId, cancellationToken); + + // //String html = await this.StatementBuilder.GetStatementHtml(statementHeader, cancellationToken); + + // //String base64 = await this.PdfGenerator.CreatePDF(html, cancellationToken); + + // //SendEmailRequest sendEmailRequest = new SendEmailRequest + // //{ + // // Body = "Please find attached this months statement.", + // // ConnectionIdentifier = command.EstateId, + // // FromAddress = "golfhandicapping@btinternet.com", // TODO: lookup from config + // // IsHtml = true, + // // Subject = $"Merchant Statement for {statementHeader.StatementDate}", + // // // MessageId = command.MerchantStatementId, + // // ToAddresses = new List + // // { + // // statementHeader.MerchantEmail + // // }, + // // EmailAttachments = new List + // // { + // // new EmailAttachment + // // { + // // FileData = base64, + // // FileType = FileType.PDF, + // // Filename = $"merchantstatement{statementHeader.StatementDate}.pdf" + // // } + // // } + // //}; + + // //Guid messageId = IdGenerationService.GenerateEventId(new + // //{ + // // command.MerchantStatementId, + // // DateTime.Now + // //}); + + // //sendEmailRequest.MessageId = messageId; + + // //this.TokenResponse = await Helpers.GetToken(this.TokenResponse, this.SecurityServiceClient, cancellationToken); + + // //var sendEmailResponseResult = await this.MessagingServiceClient.SendEmail(this.TokenResponse.AccessToken, sendEmailRequest, cancellationToken); + // ////if (sendEmailResponseResult.IsFailed) { + // //// // TODO: record a failed event?? + // ////} + // //merchantStatementAggregate.EmailStatement(DateTime.Now, messageId); + + // return Result.Success(); + // }, + // command.EstateId, command.MerchantStatementId, cancellationToken); + + //if (result.IsFailed) + // return ResultHelpers.CreateFailure(result); + + return Result.Success(); + } + + public async Task RecordActivityDateOnMerchantStatement(MerchantStatementCommands.RecordActivityDateOnMerchantStatementCommand command, + CancellationToken cancellationToken) { Result result = await ApplyUpdates( async (MerchantStatementAggregate merchantStatementAggregate) => { - //StatementHeader statementHeader = await this.EstateManagementRepository.GetStatement(command.EstateId, command.MerchantStatementId, cancellationToken); - - //String html = await this.StatementBuilder.GetStatementHtml(statementHeader, cancellationToken); - - //String base64 = await this.PdfGenerator.CreatePDF(html, cancellationToken); - - //SendEmailRequest sendEmailRequest = new SendEmailRequest - //{ - // Body = "Please find attached this months statement.", - // ConnectionIdentifier = command.EstateId, - // FromAddress = "golfhandicapping@btinternet.com", // TODO: lookup from config - // IsHtml = true, - // Subject = $"Merchant Statement for {statementHeader.StatementDate}", - // // MessageId = command.MerchantStatementId, - // ToAddresses = new List - // { - // statementHeader.MerchantEmail - // }, - // EmailAttachments = new List - // { - // new EmailAttachment - // { - // FileData = base64, - // FileType = FileType.PDF, - // Filename = $"merchantstatement{statementHeader.StatementDate}.pdf" - // } - // } - //}; - - //Guid messageId = IdGenerationService.GenerateEventId(new - //{ - // command.MerchantStatementId, - // DateTime.Now - //}); - - //sendEmailRequest.MessageId = messageId; - - //this.TokenResponse = await Helpers.GetToken(this.TokenResponse, this.SecurityServiceClient, cancellationToken); - - //var sendEmailResponseResult = await this.MessagingServiceClient.SendEmail(this.TokenResponse.AccessToken, sendEmailRequest, cancellationToken); - ////if (sendEmailResponseResult.IsFailed) { - //// // TODO: record a failed event?? - ////} - //merchantStatementAggregate.EmailStatement(DateTime.Now, messageId); - + merchantStatementAggregate.RecordActivityDateOnStatement(command.MerchantStatementId, command.StatementDate, + command.EstateId, command.MerchantId, + command.MerchantStatementForDateId, command.StatementDate); + return Result.Success(); - }, - command.EstateId, command.MerchantStatementId, cancellationToken); + }, command.MerchantStatementId, cancellationToken, false); if (result.IsFailed) return ResultHelpers.CreateFailure(result); @@ -216,11 +265,6 @@ public async Task EmailStatement(MerchantStatementCommands.EmailMerchant return Result.Success(); } - /// - /// The token response - /// - private TokenResponse TokenResponse; - public async Task AddTransactionToStatement(MerchantStatementCommands.AddTransactionToMerchantStatementCommand command, CancellationToken cancellationToken) { @@ -233,10 +277,11 @@ public async Task AddTransactionToStatement(MerchantStatementCommands.Ad // Work out the next statement date DateTime nextStatementDate = CalculateStatementDate(command.TransactionDateTime); - Guid statementId = GuidCalculator.Combine(command.MerchantId, nextStatementDate.ToGuid()); + Guid merchantStatementId = IdGenerationService.GenerateMerchantStatementAggregateId(command.EstateId, command.MerchantId, nextStatementDate); + Guid merchantStatementForDateId = IdGenerationService.GenerateMerchantStatementForDateAggregateId(command.EstateId, command.MerchantId, nextStatementDate, command.TransactionDateTime); Result result = await ApplyUpdates( - async (MerchantStatementAggregate merchantStatementAggregate) => { + async (MerchantStatementForDateAggregate merchantStatementForDateAggregate) => { // Add transaction to statement Transaction transaction = new(command.TransactionId, command.TransactionDateTime, command.TransactionAmount.GetValueOrDefault(0)); @@ -248,11 +293,10 @@ public async Task AddTransactionToStatement(MerchantStatementCommands.Ad command.TransactionDateTime, }); - merchantStatementAggregate.AddTransactionToStatement(statementId, eventId, nextStatementDate, command.EstateId, command.MerchantId, transaction); + merchantStatementForDateAggregate.AddTransactionToStatement(merchantStatementId, nextStatementDate, eventId, command.EstateId, command.MerchantId, transaction); return Result.Success(); - }, - command.EstateId, statementId, cancellationToken, false); + }, merchantStatementForDateId, cancellationToken, false); if (result.IsFailed) return ResultHelpers.CreateFailure(result); diff --git a/TransactionProcessor.DomainEvents/ContractDomainEvents.cs b/TransactionProcessor.DomainEvents/ContractDomainEvents.cs index fd5ded7f..9eb6a183 100644 --- a/TransactionProcessor.DomainEvents/ContractDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/ContractDomainEvents.cs @@ -3,19 +3,11 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class ContractDomainEvents { - [ExcludeFromCodeCoverage] public record ContractCreatedEvent(Guid ContractId, Guid EstateId, Guid OperatorId, String Description) : DomainEvent(ContractId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record VariableValueProductAddedToContractEvent(Guid ContractId, Guid EstateId, Guid ProductId, String ProductName, String DisplayText, Int32 ProductType) : DomainEvent(ContractId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record FixedValueProductAddedToContractEvent(Guid ContractId, Guid EstateId, Guid ProductId, String ProductName, String DisplayText, Decimal Value, Int32 ProductType) : DomainEvent(ContractId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionFeeForProductAddedToContractEvent(Guid ContractId, Guid EstateId, Guid ProductId, Guid TransactionFeeId, String Description, Int32 CalculationType, Int32 FeeType, Decimal Value) : DomainEvent(ContractId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionFeeForProductDisabledEvent(Guid ContractId, Guid EstateId, Guid ProductId, Guid TransactionFeeId) : DomainEvent(ContractId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/EstateDomainEvents.cs b/TransactionProcessor.DomainEvents/EstateDomainEvents.cs index 1924beb4..20764374 100644 --- a/TransactionProcessor.DomainEvents/EstateDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/EstateDomainEvents.cs @@ -3,19 +3,11 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class EstateDomainEvents { - [ExcludeFromCodeCoverage] public record EstateCreatedEvent(Guid EstateId, String EstateName) : DomainEvent(EstateId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record EstateReferenceAllocatedEvent(Guid EstateId, String EstateReference) : DomainEvent(EstateId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record OperatorAddedToEstateEvent(Guid EstateId, Guid OperatorId) : DomainEvent(EstateId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record OperatorRemovedFromEstateEvent(Guid EstateId, Guid OperatorId) : DomainEvent(EstateId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record SecurityUserAddedToEstateEvent(Guid EstateId, Guid SecurityUserId, String EmailAddress) : DomainEvent(EstateId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/FloatActivityDomainEvents.cs b/TransactionProcessor.DomainEvents/FloatActivityDomainEvents.cs index d7fdc843..b1fe5c0a 100644 --- a/TransactionProcessor.DomainEvents/FloatActivityDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/FloatActivityDomainEvents.cs @@ -3,10 +3,8 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class FloatActivityDomainEvents { - [ExcludeFromCodeCoverage] public record FloatAggregateCreditedEvent(Guid FloatId, Guid EstateId, DateTime ActivityDateTime, Decimal Amount, Guid CreditId) : DomainEvent(FloatId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record FloatAggregateDebitedEvent(Guid FloatId, Guid EstateId, DateTime ActivityDateTime, Decimal Amount, Guid DebitId) : DomainEvent(FloatId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/FloatDomainEvents.cs b/TransactionProcessor.DomainEvents/FloatDomainEvents.cs index cb643e9e..617214c4 100644 --- a/TransactionProcessor.DomainEvents/FloatDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/FloatDomainEvents.cs @@ -3,14 +3,9 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class FloatDomainEvents { - - [ExcludeFromCodeCoverage] public record FloatCreatedForContractProductEvent(Guid FloatId, Guid EstateId, Guid ContractId, Guid ProductId, DateTime CreatedDateTime) : DomainEvent(FloatId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record FloatCreditPurchasedEvent(Guid FloatId, Guid EstateId, DateTime CreditPurchasedDateTime, Decimal Amount, Decimal CostPrice) : DomainEvent(FloatId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record FloatDecreasedByTransactionEvent(Guid FloatId, Guid EstateId, Guid TransactionId, Decimal Amount) : DomainEvent(FloatId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs b/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs index a651d834..95943591 100644 --- a/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs @@ -2,64 +2,37 @@ using Shared.DomainDrivenDesign.EventSourcing; namespace TransactionProcessor.DomainEvents { + + [ExcludeFromCodeCoverage] public class MerchantDomainEvents { - [ExcludeFromCodeCoverage] public record AddressAddedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String AddressLine1, String AddressLine2, String AddressLine3, String AddressLine4, String Town, String Region, String PostalCode, String Country) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record AutomaticDepositMadeEvent(Guid MerchantId, Guid EstateId, Guid DepositId, String Reference, DateTime DepositDateTime, Decimal Amount) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record ContactAddedEvent(Guid MerchantId, Guid EstateId, Guid ContactId, String ContactName, String ContactPhoneNumber, String ContactEmailAddress) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record DeviceAddedToMerchantEvent(Guid MerchantId, Guid EstateId, Guid DeviceId, String DeviceIdentifier) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record DeviceSwappedForMerchantEvent(Guid MerchantId, Guid EstateId, Guid DeviceId, String OriginalDeviceIdentifier, String NewDeviceIdentifier) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record ManualDepositMadeEvent(Guid MerchantId, Guid EstateId, Guid DepositId, String Reference, DateTime DepositDateTime, Decimal Amount) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantCreatedEvent(Guid MerchantId, Guid EstateId, String MerchantName, DateTime DateCreated) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantDepositListCreatedEvent(Guid MerchantId, Guid EstateId, DateTime DateCreated) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantReferenceAllocatedEvent(Guid MerchantId, Guid EstateId, String MerchantReference) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record OperatorAssignedToMerchantEvent(Guid MerchantId, Guid EstateId, Guid OperatorId, String Name, String MerchantNumber, String TerminalNumber) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record OperatorRemovedFromMerchantEvent(Guid MerchantId, Guid EstateId, Guid OperatorId) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record SecurityUserAddedToMerchantEvent(Guid MerchantId, Guid EstateId, Guid SecurityUserId, String EmailAddress) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record SettlementScheduleChangedEvent(Guid MerchantId, Guid EstateId, Int32 SettlementSchedule, DateTime NextSettlementDate) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record WithdrawalMadeEvent(Guid MerchantId, Guid EstateId, Guid WithdrawalId, DateTime WithdrawalDateTime, Decimal Amount) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record ContractAddedToMerchantEvent(Guid MerchantId, Guid EstateId, Guid ContractId) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record ContractRemovedFromMerchantEvent(Guid MerchantId, Guid EstateId, Guid ContractId) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record ContractProductAddedToMerchantEvent(Guid MerchantId, Guid EstateId, Guid ContractId, Guid ContractProductId) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantNameUpdatedEvent(Guid MerchantId, Guid EstateId, String MerchantName) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantAddressLine1UpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String AddressLine1) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantAddressLine2UpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String AddressLine2) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantAddressLine3UpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String AddressLine3) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantAddressLine4UpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String AddressLine4) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantCountyUpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String Country) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantRegionUpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String Region) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantTownUpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String Town) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantPostalCodeUpdatedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, String PostalCode) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantContactNameUpdatedEvent(Guid MerchantId, Guid EstateId, Guid ContactId, String ContactName) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantContactEmailAddressUpdatedEvent(Guid MerchantId, Guid EstateId, Guid ContactId, String ContactEmailAddress) : DomainEvent(MerchantId, Guid.NewGuid()); - [ExcludeFromCodeCoverage] public record MerchantContactPhoneNumberUpdatedEvent(Guid MerchantId, Guid EstateId, Guid ContactId, String ContactPhoneNumber) : DomainEvent(MerchantId, Guid.NewGuid()); } } diff --git a/TransactionProcessor.DomainEvents/MerchantStatementDomainEvents.cs b/TransactionProcessor.DomainEvents/MerchantStatementDomainEvents.cs index 3751f993..fa4befb2 100644 --- a/TransactionProcessor.DomainEvents/MerchantStatementDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/MerchantStatementDomainEvents.cs @@ -2,16 +2,25 @@ using Shared.DomainDrivenDesign.EventSourcing; namespace TransactionProcessor.DomainEvents { + [ExcludeFromCodeCoverage] public class MerchantStatementDomainEvents { - public record SettledFeeAddedToStatementEvent(Guid MerchantStatementId, Guid EventId, Guid EstateId, Guid MerchantId, Guid SettledFeeId, Guid TransactionId, DateTime SettledDateTime, Decimal SettledValue) : DomainEvent(MerchantStatementId, EventId); - - public record TransactionAddedToStatementEvent(Guid MerchantStatementId, Guid EventId, Guid EstateId, Guid MerchantId, Guid TransactionId, DateTime TransactionDateTime, Decimal TransactionValue) : DomainEvent(MerchantStatementId, EventId); - public record StatementGeneratedEvent(Guid MerchantStatementId, Guid EstateId, Guid MerchantId, DateTime DateGenerated) : DomainEvent(MerchantStatementId, Guid.NewGuid()); public record StatementEmailedEvent(Guid MerchantStatementId, Guid EstateId, Guid MerchantId, DateTime DateEmailed, Guid MessageId) : DomainEvent(MerchantStatementId, Guid.NewGuid()); - public record StatementCreatedEvent(Guid MerchantStatementId, Guid EstateId, Guid MerchantId, DateTime DateCreated) : DomainEvent(MerchantStatementId, Guid.NewGuid()); + public record StatementCreatedEvent(Guid MerchantStatementId, Guid EstateId, Guid MerchantId, DateTime StatementDate) : DomainEvent(MerchantStatementId, Guid.NewGuid()); + + public record ActivityDateAddedToStatementEvent(Guid MerchantStatementId, Guid EstateId, Guid MerchantId, Guid MerchantStatementForDateId, DateTime ActivityDate) : DomainEvent(MerchantStatementId, Guid.NewGuid()); + } + + [ExcludeFromCodeCoverage] + public class MerchantStatementForDateDomainEvents { + public record StatementCreatedForDateEvent(Guid MerchantStatementForDateId, DateTime ActivityDate, DateTime MerchantStatementDate, Guid MerchantStatementId, Guid EstateId, Guid MerchantId) : DomainEvent(MerchantStatementForDateId, Guid.NewGuid()); + + public record SettledFeeAddedToStatementForDateEvent(Guid MerchantStatementForDateId, Guid EventId, Guid EstateId, Guid MerchantId, Guid SettledFeeId, Guid TransactionId, DateTime SettledDateTime, Decimal SettledValue) : DomainEvent(MerchantStatementForDateId, EventId); + + public record TransactionAddedToStatementForDateEvent(Guid MerchantStatementForDateId, Guid EventId, Guid EstateId, Guid MerchantId, Guid TransactionId, DateTime TransactionDateTime, Decimal TransactionValue) : DomainEvent(MerchantStatementForDateId, EventId); + } } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/OperatorDomainEvents.cs b/TransactionProcessor.DomainEvents/OperatorDomainEvents.cs index 2d26238a..d475c3ba 100644 --- a/TransactionProcessor.DomainEvents/OperatorDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/OperatorDomainEvents.cs @@ -3,16 +3,10 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class OperatorDomainEvents { - [ExcludeFromCodeCoverage] public record OperatorCreatedEvent(Guid OperatorId, Guid EstateId, String Name, Boolean RequireCustomMerchantNumber, Boolean RequireCustomTerminalNumber) : DomainEvent(OperatorId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record OperatorNameUpdatedEvent(Guid OperatorId, Guid EstateId, String Name) : DomainEvent(OperatorId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record OperatorRequireCustomMerchantNumberChangedEvent(Guid OperatorId, Guid EstateId, Boolean RequireCustomMerchantNumber) : DomainEvent(OperatorId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record OperatorRequireCustomTerminalNumberChangedEvent(Guid OperatorId, Guid EstateId, Boolean RequireCustomTerminalNumber) : DomainEvent(OperatorId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/ReconciliationDomainEvents.cs b/TransactionProcessor.DomainEvents/ReconciliationDomainEvents.cs index e3e47b4c..2dc60f9e 100644 --- a/TransactionProcessor.DomainEvents/ReconciliationDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/ReconciliationDomainEvents.cs @@ -3,20 +3,11 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class ReconciliationDomainEvents { - - [ExcludeFromCodeCoverage] public record OverallTotalsRecordedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Int32 TransactionCount, Decimal TransactionValue, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record ReconciliationHasBeenLocallyAuthorisedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, String ResponseCode, String ResponseMessage, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record ReconciliationHasBeenLocallyDeclinedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, String ResponseCode, String ResponseMessage, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record ReconciliationHasCompletedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record ReconciliationHasStartedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/SettlementDomainEvents.cs b/TransactionProcessor.DomainEvents/SettlementDomainEvents.cs index 83fd9e1b..26926d53 100644 --- a/TransactionProcessor.DomainEvents/SettlementDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/SettlementDomainEvents.cs @@ -3,20 +3,11 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class SettlementDomainEvents { - - [ExcludeFromCodeCoverage] public record MerchantFeeAddedPendingSettlementEvent(Guid SettlementId, Guid EstateId, Guid MerchantId, Guid TransactionId, Decimal CalculatedValue, Int32 FeeCalculationType, Guid FeeId, Decimal FeeValue, DateTime FeeCalculatedDateTime) : DomainEvent(SettlementId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record MerchantFeeSettledEvent(Guid SettlementId, Guid EstateId, Guid MerchantId, Guid TransactionId, Decimal CalculatedValue, Int32 FeeCalculationType, Guid FeeId, Decimal FeeValue, DateTime FeeCalculatedDateTime, DateTime SettledDateTime) : DomainEvent(SettlementId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record SettlementCompletedEvent(Guid SettlementId, Guid EstateId, Guid MerchantId) : DomainEvent(SettlementId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record SettlementCreatedForDateEvent(Guid SettlementId, Guid EstateId, Guid MerchantId, DateTime SettlementDate) : DomainEvent(SettlementId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record SettlementProcessingStartedEvent(Guid SettlementId, Guid EstateId, Guid MerchantId, DateTime ProcessingStartedDateTime) : DomainEvent(SettlementId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs b/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs index a5c1cb90..420aadc2 100644 --- a/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs @@ -3,52 +3,22 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class TransactionDomainEvents { - [ExcludeFromCodeCoverage] public record CustomerEmailReceiptResendRequestedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record CustomerEmailReceiptRequestedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, String CustomerEmailAddress, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record AdditionalRequestDataRecordedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Guid OperatorId, Dictionary AdditionalTransactionRequestMetadata, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record AdditionalResponseDataRecordedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Guid OperatorId, Dictionary AdditionalTransactionResponseMetadata, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record SettledMerchantFeeAddedToTransactionEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Decimal CalculatedValue, Int32 FeeCalculationType, Guid FeeId, Decimal FeeValue, DateTime FeeCalculatedDateTime, DateTime SettledDateTime, Guid SettlementId, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record MerchantFeePendingSettlementAddedToTransactionEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Decimal CalculatedValue, Int32 FeeCalculationType, Guid FeeId, Decimal FeeValue, DateTime FeeCalculatedDateTime, DateTime SettlementDueDate, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record ProductDetailsAddedToTransactionEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Guid ContractId, Guid ProductId, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record ServiceProviderFeeAddedToTransactionEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Decimal CalculatedValue, Int32 FeeCalculationType, Guid FeeId, Decimal FeeValue, DateTime FeeCalculatedDateTime, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionAuthorisedByOperatorEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Guid OperatorId, String AuthorisationCode, String OperatorResponseCode, String OperatorResponseMessage, String OperatorTransactionId, String ResponseCode, String ResponseMessage, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionCostInformationRecordedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Decimal? UnitCostValue, Decimal? TotalCostValue, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionDeclinedByOperatorEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Guid OperatorId, String OperatorResponseCode, String OperatorResponseMessage, String ResponseCode, String ResponseMessage, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionHasBeenCompletedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, String ResponseCode, String ResponseMessage, Boolean IsAuthorised, DateTime CompletedDateTime, Decimal? TransactionAmount, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionHasBeenLocallyAuthorisedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, String AuthorisationCode, String ResponseCode, String ResponseMessage, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionHasBeenLocallyDeclinedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, String ResponseCode, String ResponseMessage, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionHasStartedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, DateTime TransactionDateTime, String TransactionNumber, String TransactionType, String TransactionReference, String DeviceIdentifier, Decimal? TransactionAmount) : DomainEvent(TransactionId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record TransactionSourceAddedToTransactionEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Int32 TransactionSource, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.DomainEvents/VoucherDomainEvents.cs b/TransactionProcessor.DomainEvents/VoucherDomainEvents.cs index 04e49fd8..dc602fa3 100644 --- a/TransactionProcessor.DomainEvents/VoucherDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/VoucherDomainEvents.cs @@ -3,16 +3,10 @@ namespace TransactionProcessor.DomainEvents; +[ExcludeFromCodeCoverage] public class VoucherDomainEvents { - [ExcludeFromCodeCoverage] public record BarcodeAddedEvent(Guid VoucherId, Guid EstateId, String Barcode) : DomainEvent(VoucherId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record VoucherFullyRedeemedEvent(Guid VoucherId, Guid EstateId, DateTime RedeemedDateTime) : DomainEvent(VoucherId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record VoucherGeneratedEvent(Guid VoucherId, Guid EstateId, Guid TransactionId, DateTime GeneratedDateTime, Guid OperatorId, Decimal Value, String VoucherCode, DateTime ExpiryDateTime, String Message) : DomainEvent(VoucherId, Guid.NewGuid()); - - [ExcludeFromCodeCoverage] public record VoucherIssuedEvent(Guid VoucherId, Guid EstateId, DateTime IssuedDateTime, String RecipientEmail, String RecipientMobile) : DomainEvent(VoucherId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.Models/Merchant/MerchantStatement.cs b/TransactionProcessor.Models/Merchant/MerchantStatement.cs deleted file mode 100644 index 78fc0dbb..00000000 --- a/TransactionProcessor.Models/Merchant/MerchantStatement.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -namespace TransactionProcessor.Models.Merchant; - -[ExcludeFromCodeCoverage] -public class MerchantStatement -{ - #region Fields - - /// - /// The statement lines - /// - private readonly List StatementLines; - - #endregion - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - public MerchantStatement() - { - this.StatementLines = new List(); - } - - #endregion - - #region Properties - - /// - /// Gets or sets the estate identifier. - /// - /// - /// The estate identifier. - /// - public Guid EstateId { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is created. - /// - /// - /// true if this instance is created; otherwise, false. - /// - public Boolean IsCreated { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is generated. - /// - /// - /// true if this instance is generated; otherwise, false. - /// - public Boolean IsGenerated { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is emailed. - /// - /// - /// true if this instance is emailed; otherwise, false. - /// - public Boolean HasBeenEmailed { get; set; } - - /// - /// Gets or sets the merchant identifier. - /// - /// - /// The merchant identifier. - /// - public Guid MerchantId { get; set; } - - /// - /// Gets or sets the merchant statement identifier. - /// - /// - /// The merchant statement identifier. - /// - public Guid MerchantStatementId { get; set; } - - /// - /// Gets or sets the statement created date time. - /// - /// - /// The statement created date time. - /// - public DateTime StatementCreatedDateTime { get; set; } - - /// - /// Gets or sets the statement generated date time. - /// - /// - /// The statement generated date time. - /// - public DateTime StatementGeneratedDateTime { get; set; } - - /// - /// Gets or sets the statement number. - /// - /// - /// The statement number. - /// - public Int32 StatementNumber { get; set; } // TODO: How is this allocated?? - - #endregion - - #region Methods - - /// - /// Adds the statement line. - /// - /// The statement line. - public void AddStatementLine(MerchantStatementLine statementLine) - { - this.StatementLines.Add(statementLine); - } - - /// - /// Gets the statement lines. - /// - /// - public List GetStatementLines() - { - //return this.StatementLines.OrderBy(s => s.DateTime).ToList(); - return this.StatementLines.OrderBy(s => s.DateTime).ToList(); - } - - #endregion -} \ No newline at end of file diff --git a/TransactionProcessor.Models/Merchant/MerchantStatementForDate.cs b/TransactionProcessor.Models/Merchant/MerchantStatementForDate.cs new file mode 100644 index 00000000..cc870fcf --- /dev/null +++ b/TransactionProcessor.Models/Merchant/MerchantStatementForDate.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace TransactionProcessor.Models.Merchant; + +[ExcludeFromCodeCoverage] +public class MerchantStatementForDate +{ + #region Fields + + private readonly List StatementLines; + + #endregion + + #region Constructors + + public MerchantStatementForDate() + { + this.StatementLines = new List(); + } + + #endregion + + #region Properties + + public Guid EstateId { get; set; } + public Boolean IsCreated { get; set; } + //public Boolean IsGenerated { get; set; } + //public Boolean HasBeenEmailed { get; set; } + public Guid MerchantId { get; set; } + public Guid MerchantStatementId { get; set; } + public Guid MerchantStatementForDateId { get; set; } + public DateTime StatementDate { get; set; } + public DateTime ActivityDate { get; set; } + + #endregion + + #region Methods + + /// + /// Adds the statement line. + /// + /// The statement line. + public void AddStatementLine(MerchantStatementLine statementLine) + { + this.StatementLines.Add(statementLine); + } + + /// + /// Gets the statement lines. + /// + /// + public List GetStatementLines() + { + return this.StatementLines.OrderBy(s => s.DateTime).ToList(); + } + + #endregion +} + +[ExcludeFromCodeCoverage] +public class MerchantStatement +{ + #region Fields + + private readonly List<(Guid merchantStatementForDateId, DateTime activityDate)> StatementActivityDates; + + #endregion + + #region Constructors + + public MerchantStatement() + { + this.StatementActivityDates = new(); + } + + #endregion + + #region Properties + + public Guid EstateId { get; set; } + public Boolean IsCreated { get; set; } + public Boolean IsGenerated { get; set; } + public Boolean HasBeenEmailed { get; set; } + public Guid MerchantId { get; set; } + public Guid MerchantStatementId { get; set; } + public DateTime StatementDate { get; set; } + #endregion + + #region Methods + + /// + /// Adds the statement line. + /// + /// The statement line. + public void AddStatementActivityDate(Guid merchantStatementForDateId, DateTime activityDate) + { + this.StatementActivityDates.Add((merchantStatementForDateId, activityDate)); + } + + public List<(Guid merchantStatementForDateId, DateTime activityDate)> GetActivityDates() + { + return this.StatementActivityDates.OrderBy(s => s.activityDate).ToList(); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionProcessor.ProjectionEngine.Tests/ProjectionHandlerTests.cs b/TransactionProcessor.ProjectionEngine.Tests/ProjectionHandlerTests.cs index 94558677..76c0b89f 100644 --- a/TransactionProcessor.ProjectionEngine.Tests/ProjectionHandlerTests.cs +++ b/TransactionProcessor.ProjectionEngine.Tests/ProjectionHandlerTests.cs @@ -1,4 +1,5 @@ -using SimpleResults; +using Shared.Logger; +using SimpleResults; using TransactionProcessor.DomainEvents; using TransactionProcessor.ProjectionEngine.Models; @@ -202,6 +203,7 @@ public class MerchantBalanceStateDispatcherTests public MerchantBalanceStateDispatcherTests() { this.Repository = new Mock(); this.Dispatcher = new MerchantBalanceStateDispatcher(this.Repository.Object); + Logger.Initialise(new NullLogger()); } [Theory] diff --git a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs index 4c6150ca..3b0c56a2 100644 --- a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs +++ b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs @@ -105,8 +105,9 @@ Task AddPendingMerchantFeeToSettlement(SettlementDomainEvents.MerchantFe Task AddProductDetailsToTransaction(TransactionDomainEvents.ProductDetailsAddedToTransactionEvent domainEvent, CancellationToken cancellationToken); - Task AddSettledFeeToStatement(SettledFeeAddedToStatementEvent domainEvent, - CancellationToken cancellationToken); + // TODO@ Add this back in + //Task AddSettledFeeToStatement(SettledFeeAddedToStatementEvent domainEvent, + // CancellationToken cancellationToken); Task AddSettledMerchantFeeToSettlement(TransactionDomainEvents.SettledMerchantFeeAddedToTransactionEvent domainEvent, CancellationToken cancellationToken); @@ -114,8 +115,9 @@ Task AddSettledMerchantFeeToSettlement(TransactionDomainEvents.SettledMe Task AddSourceDetailsToTransaction(TransactionDomainEvents.TransactionSourceAddedToTransactionEvent domainEvent, CancellationToken cancellationToken); - Task AddTransactionToStatement(TransactionAddedToStatementEvent domainEvent, - CancellationToken cancellationToken); + // TODO@ Add this back in + //Task AddTransactionToStatement(TransactionAddedToStatementEvent domainEvent, + // CancellationToken cancellationToken); Task CompleteReconciliation(ReconciliationDomainEvents.ReconciliationHasCompletedEvent domainEvent, CancellationToken cancellationToken); @@ -1708,37 +1710,38 @@ public async Task MarkStatementAsGenerated(StatementGeneratedEvent domai return await context.SaveChangesAsync(cancellationToken); } - public async Task AddSettledFeeToStatement(SettledFeeAddedToStatementEvent domainEvent, - CancellationToken cancellationToken) - { - EstateManagementGenericContext context = await this.GetContextFromDomainEvent(domainEvent, cancellationToken); + // TODO@ Add this back in + //public async Task AddSettledFeeToStatement(SettledFeeAddedToStatementEvent domainEvent, + // CancellationToken cancellationToken) + //{ + // EstateManagementGenericContext context = await this.GetContextFromDomainEvent(domainEvent, cancellationToken); - // Find the corresponding transaction - var getTransactionResult = await context.LoadTransaction(domainEvent, cancellationToken); - if (getTransactionResult.IsFailed) - return ResultHelpers.CreateFailure(getTransactionResult); - var transaction = getTransactionResult.Data; + // // Find the corresponding transaction + // var getTransactionResult = await context.LoadTransaction(domainEvent, cancellationToken); + // if (getTransactionResult.IsFailed) + // return ResultHelpers.CreateFailure(getTransactionResult); + // var transaction = getTransactionResult.Data; - Result operatorResult = await context.LoadOperator(transaction.OperatorId, cancellationToken); - if (operatorResult.IsFailed) - return ResultHelpers.CreateFailure(operatorResult); - var @operator = operatorResult.Data; + // Result operatorResult = await context.LoadOperator(transaction.OperatorId, cancellationToken); + // if (operatorResult.IsFailed) + // return ResultHelpers.CreateFailure(operatorResult); + // var @operator = operatorResult.Data; - StatementLine line = new StatementLine - { - StatementId = domainEvent.MerchantStatementId, - ActivityDateTime = domainEvent.SettledDateTime, - ActivityDate = domainEvent.SettledDateTime.Date, - ActivityDescription = $"{@operator.Name} Transaction Fee", - ActivityType = 2, // Transaction Fee - TransactionId = domainEvent.TransactionId, - InAmount = domainEvent.SettledValue - }; + // StatementLine line = new StatementLine + // { + // StatementId = domainEvent.MerchantStatementId, + // ActivityDateTime = domainEvent.SettledDateTime, + // ActivityDate = domainEvent.SettledDateTime.Date, + // ActivityDescription = $"{@operator.Name} Transaction Fee", + // ActivityType = 2, // Transaction Fee + // TransactionId = domainEvent.TransactionId, + // InAmount = domainEvent.SettledValue + // }; - await context.StatementLines.AddAsync(line, cancellationToken); + // await context.StatementLines.AddAsync(line, cancellationToken); - return await context.SaveChangesWithDuplicateHandling(cancellationToken); - } + // return await context.SaveChangesWithDuplicateHandling(cancellationToken); + //} public async Task CreateStatement(StatementCreatedEvent domainEvent, CancellationToken cancellationToken) @@ -1748,8 +1751,8 @@ public async Task CreateStatement(StatementCreatedEvent domainEvent, StatementHeader header = new StatementHeader { MerchantId = domainEvent.MerchantId, - StatementCreatedDateTime = domainEvent.DateCreated, - StatementCreatedDate = domainEvent.DateCreated.Date, + StatementCreatedDateTime = domainEvent.StatementDate, + StatementCreatedDate = domainEvent.StatementDate.Date, StatementId = domainEvent.MerchantStatementId }; @@ -1758,38 +1761,39 @@ public async Task CreateStatement(StatementCreatedEvent domainEvent, return await context.SaveChangesAsync(cancellationToken); } - public async Task AddTransactionToStatement(TransactionAddedToStatementEvent domainEvent, - CancellationToken cancellationToken) - { - EstateManagementGenericContext context = await this.GetContextFromDomainEvent(domainEvent, cancellationToken); + // TODO@ Add this back in + //public async Task AddTransactionToStatement(TransactionAddedToStatementEvent domainEvent, + // CancellationToken cancellationToken) + //{ + // EstateManagementGenericContext context = await this.GetContextFromDomainEvent(domainEvent, cancellationToken); - // Find the corresponding transaction - Result transactionResult = await context.LoadTransaction(domainEvent, cancellationToken); - if (transactionResult.IsFailed) - return ResultHelpers.CreateFailure(transactionResult); + // // Find the corresponding transaction + // Result transactionResult = await context.LoadTransaction(domainEvent, cancellationToken); + // if (transactionResult.IsFailed) + // return ResultHelpers.CreateFailure(transactionResult); - Transaction transaction = transactionResult.Data; + // Transaction transaction = transactionResult.Data; - Result operatorResult = await context.LoadOperator(transaction.OperatorId, cancellationToken); - if (operatorResult.IsFailed) - return ResultHelpers.CreateFailure(operatorResult); - Operator @operator = operatorResult.Data; + // Result operatorResult = await context.LoadOperator(transaction.OperatorId, cancellationToken); + // if (operatorResult.IsFailed) + // return ResultHelpers.CreateFailure(operatorResult); + // Operator @operator = operatorResult.Data; - StatementLine line = new StatementLine - { - StatementId = domainEvent.MerchantStatementId, - ActivityDateTime = domainEvent.TransactionDateTime, - ActivityDate = domainEvent.TransactionDateTime.Date, - ActivityDescription = $"{@operator.Name} Transaction", - ActivityType = 1, // Transaction - TransactionId = domainEvent.TransactionId, - OutAmount = domainEvent.TransactionValue - }; + // StatementLine line = new StatementLine + // { + // StatementId = domainEvent.MerchantStatementId, + // ActivityDateTime = domainEvent.TransactionDateTime, + // ActivityDate = domainEvent.TransactionDateTime.Date, + // ActivityDescription = $"{@operator.Name} Transaction", + // ActivityType = 1, // Transaction + // TransactionId = domainEvent.TransactionId, + // OutAmount = domainEvent.TransactionValue + // }; - await context.StatementLines.AddAsync(line, cancellationToken); + // await context.StatementLines.AddAsync(line, cancellationToken); - return await context.SaveChangesWithDuplicateHandling(cancellationToken); - } + // return await context.SaveChangesWithDuplicateHandling(cancellationToken); + //} public async Task AddFile(FileCreatedEvent domainEvent, CancellationToken cancellationToken) diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index 1173c251..46c849ce 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -69,12 +69,17 @@ public class TestData public static TransactionProcessor.Aggregates.Models.Transaction Transaction1 => new(TransactionId1, TransactionDateTime1, TransactionAmount1.Value); public static TransactionProcessor.Aggregates.Models.Transaction Transaction2 => new(TransactionId2, TransactionDateTime2, TransactionAmount2.Value); - public static DateTime StatementCreateDate = new DateTime(2021, 12, 10); + public static DateTime StatementDate = new DateTime(2021, 12, 10); public static DateTime StatementEmailedDate = new DateTime(2021, 12, 12); public static DateTime StatementGeneratedDate = new DateTime(2021, 12, 11); public static Guid MerchantStatementId = Guid.Parse("C8CC622C-07D9-48E9-B544-F53BD29DE1E6"); + public static Guid MerchantStatementForDateId1 = Guid.Parse("9AE8455F-72A7-4876-8403-D6FBEF6EBDBF"); + public static DateTime ActivityDate1 = new DateTime(2021, 1, 10); + public static Guid MerchantStatementForDateId2 = Guid.Parse("8014A0E9-9605-4496-995F-B022DDC63A53"); + public static DateTime ActivityDate2 = new DateTime(2021, 1, 11); + public static Guid EventId1 = Guid.Parse("C8CC622C-07D9-48E9-B544-F53BD29DE1E6"); public static List MerchantContractsEmptyList => new List(); @@ -2067,20 +2072,22 @@ public static DataTransferObjects.Requests.Contract.AddTransactionFeeForProductT public static Boolean IsAuthorisedTrue = true; - public static GenerateMerchantStatementRequest GenerateMerchantStatementRequest => - new GenerateMerchantStatementRequest - { - MerchantStatementDate = TestData.StatementCreateDate - }; + //public static GenerateMerchantStatementRequest GenerateMerchantStatementRequest => + // new GenerateMerchantStatementRequest + // { + // MerchantStatementDate = TestData.StatementCreateDate + // }; #endregion public static class Commands { - public static MerchantCommands.GenerateMerchantStatementCommand GenerateMerchantStatementCommand => new(TestData.EstateId, TestData.MerchantId, TestData.GenerateMerchantStatementRequest); + //public static MerchantCommands.GenerateMerchantStatementCommand GenerateMerchantStatementCommand => new(TestData.EstateId, TestData.MerchantId, TestData.GenerateMerchantStatementRequest); public static MerchantStatementCommands.AddTransactionToMerchantStatementCommand AddTransactionToMerchantStatementCommand => new(EstateId, MerchantId, TransactionDateTime, TransactionAmount, IsAuthorisedTrue, TransactionId); - public static MerchantStatementCommands.EmailMerchantStatementCommand EmailMerchantStatementCommand => new(EstateId, MerchantId, MerchantStatementId); + //public static MerchantStatementCommands.EmailMerchantStatementCommand EmailMerchantStatementCommand => new(EstateId, MerchantId, MerchantStatementId); public static MerchantStatementCommands.AddSettledFeeToMerchantStatementCommand AddSettledFeeToMerchantStatementCommand => new(EstateId, MerchantId, TransactionDateTime, SettledFeeAmount1, TransactionId, SettledFeeId1); + public static MerchantStatementCommands.RecordActivityDateOnMerchantStatementCommand RecordActivityDateOnMerchantStatementCommand => new(EstateId, MerchantId, MerchantStatementId,StatementDate,MerchantStatementForDateId1,ActivityDate1); + public static TransactionCommands.ResendTransactionReceiptCommand ResendTransactionReceiptCommand => new(TestData.TransactionId, TestData.EstateId); @@ -2574,9 +2581,11 @@ public static class DomainEvents { TestData.TransactionAmount, TestData.TransactionDateTime); + public static MerchantStatementForDateDomainEvents.StatementCreatedForDateEvent StatementCreatedForDateEvent => new(MerchantStatementForDateId1, ActivityDate1, StatementDate, MerchantStatementId, EstateId, MerchantId); + public static SettlementDomainEvents.MerchantFeeSettledEvent MerchantFeeSettledEvent => new(SettlementId, EstateId, MerchantId, TransactionId, CalculatedFeeValue, FeeCalculationType, SettledFeeId1, FeeValue, TransactionFeeCalculateDateTime, SettlementDate); - public static MerchantStatementDomainEvents.StatementCreatedEvent StatementCreatedEvent => new MerchantStatementDomainEvents.StatementCreatedEvent(TestData.MerchantStatementId, TestData.EstateId, TestData.MerchantId, TestData.StatementCreateDate); + public static MerchantStatementDomainEvents.StatementCreatedEvent StatementCreatedEvent => new(TestData.MerchantStatementId, TestData.EstateId, TestData.MerchantId, StatementDate); public static MerchantStatementDomainEvents.StatementGeneratedEvent StatementGeneratedEvent => new MerchantStatementDomainEvents.StatementGeneratedEvent(TestData.MerchantStatementId, TestData.EstateId, TestData.MerchantId, TestData.StatementGeneratedDate); public static CallbackReceivedEnrichedEvent CallbackReceivedEnrichedEventDeposit => diff --git a/TransactionProcessor/Bootstrapper/MediatorRegistry.cs b/TransactionProcessor/Bootstrapper/MediatorRegistry.cs index bb96347e..29f5935e 100644 --- a/TransactionProcessor/Bootstrapper/MediatorRegistry.cs +++ b/TransactionProcessor/Bootstrapper/MediatorRegistry.cs @@ -23,11 +23,6 @@ namespace TransactionProcessor.Bootstrapper [ExcludeFromCodeCoverage] public class MediatorRegistry : ServiceRegistry { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// public MediatorRegistry() { this.AddTransient(); @@ -47,12 +42,11 @@ public MediatorRegistry() private void RegisterMerchantStatementRequestHandler() { this.AddSingleton, MerchantStatementRequestHandler>(); this.AddSingleton, MerchantStatementRequestHandler>(); - this.AddSingleton, MerchantStatementRequestHandler>(); - this.AddSingleton, MerchantStatementRequestHandler>(); + //this.AddSingleton, MerchantStatementRequestHandler>(); + //this.AddSingleton, MerchantStatementRequestHandler>(); + this.AddSingleton, MerchantStatementRequestHandler>(); } - - #endregion - + private void RegisterMerchantRequestHandler() { this.AddSingleton, MerchantRequestHandler>(); this.AddSingleton, MerchantRequestHandler>(); diff --git a/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs b/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs index 27b58bc2..4df789f3 100644 --- a/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs +++ b/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs @@ -83,6 +83,7 @@ public RepositoryRegistry() this.AddSingleton, AggregateRepository>(); this.AddSingleton, AggregateRepository>(); this.AddSingleton, AggregateRepository>(); + this.AddSingleton, AggregateRepository>(); this.AddSingleton, MerchantBalanceStateRepository>(); this.AddSingleton, VoucherStateRepository>(); diff --git a/TransactionProcessor/Controllers/DomainEventController.cs b/TransactionProcessor/Controllers/DomainEventController.cs index 7efedcfc..f155268b 100644 --- a/TransactionProcessor/Controllers/DomainEventController.cs +++ b/TransactionProcessor/Controllers/DomainEventController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using SimpleResults; namespace TransactionProcessor.Controllers { @@ -74,16 +75,19 @@ public async Task PostEventAsync([FromBody] Object request, return this.Ok(); } - List tasks = new List(); + List> tasks = new(); foreach (IDomainEventHandler domainEventHandler in eventHandlers) { tasks.Add(domainEventHandler.Handle(domainEvent, cancellationToken)); } Task.WaitAll(tasks.ToArray()); + var anyFailed = tasks.Any(t => t.Result.IsFailed); + if (anyFailed) + return this.StatusCode(500); Logger.LogWarning($"Finished processing event - ID [{domainEvent.EventId}]"); - + return this.Ok(); } catch (Exception ex) diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json index 25a25c7e..cc72a6a5 100644 --- a/TransactionProcessor/appsettings.json +++ b/TransactionProcessor/appsettings.json @@ -99,9 +99,8 @@ ], "TransactionProcessor.BusinessLogic.EventHandling.MerchantStatementDomainEventHandler,TransactionProcessor.BusinessLogic": [ "TransactionHasBeenCompletedEvent", - "StatementGeneratedEvent" - ], - "TransactionProcessor.BusinessLogic.EventHandling.MerchantSettlementDomainEventHandler,TransactionProcessor.BusinessLogic": [ + "StatementCreatedForDateEvent", + "StatementGeneratedEvent", "MerchantFeeSettledEvent" ] },