From f8bd12c6c53bac7e0ff10b99843049a00eea9b11 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Sat, 31 Jan 2026 21:34:20 +0000 Subject: [PATCH 1/2] Update contract/operator event handling and add unit test - Add unit test for removing and re-adding contracts to merchants - Refine contract addition logic to allow re-adding deleted contracts - Modify event handler to reactivate existing contracts on add - Refactor operator assignment event handler for consistency - Minor code cleanup and refactoring in event handling --- .../MerchantAggregateTests.cs | 27 ++++++++++++++++ .../MerchantAggregate.cs | 32 ++++++++++++------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/TransactionProcessor.Aggregates.Tests/MerchantAggregateTests.cs b/TransactionProcessor.Aggregates.Tests/MerchantAggregateTests.cs index f203951b..a5bfa0bb 100644 --- a/TransactionProcessor.Aggregates.Tests/MerchantAggregateTests.cs +++ b/TransactionProcessor.Aggregates.Tests/MerchantAggregateTests.cs @@ -818,5 +818,32 @@ public void MerchantAggregate_RemoveOperator_And_ReAdd_OperatorIsReAdded() operatorModel = merchantModel.Operators.Single(); operatorModel.IsDeleted.ShouldBeFalse(); } + + [Fact] + public void MerchantAggregate_RemoveContract_And_ReAdd_ContractIsReAdded() + { + MerchantAggregate aggregate = MerchantAggregate.Create(TestData.MerchantId); + aggregate.Create(TestData.EstateId, TestData.MerchantName, TestData.DateMerchantCreated, TestData.AddressModel, TestData.ContactModel, + TestData.SettlementScheduleModel); + aggregate.AddContract(TestData.Aggregates.CreatedContractAggregate()); + + Result result = aggregate.RemoveContract(TestData.ContractId); + result.IsSuccess.ShouldBeTrue(); + + Merchant merchantModel = aggregate.GetMerchant(); + merchantModel.Contracts.ShouldHaveSingleItem(); + Contract contractModel = merchantModel.Contracts.Single(); + contractModel.ContractId.ShouldBe(TestData.ContractId); + contractModel.IsDeleted.ShouldBeTrue(); + + result = aggregate.AddContract(TestData.Aggregates.CreatedContractAggregate()); + result.IsSuccess.ShouldBeTrue(); + + merchantModel = aggregate.GetMerchant(); + merchantModel.Contracts.ShouldHaveSingleItem(); + contractModel = merchantModel.Contracts.Single(); + contractModel.ContractId.ShouldBe(TestData.ContractId); + contractModel.IsDeleted.ShouldBeFalse(); + } } } diff --git a/TransactionProcessor.Aggregates/MerchantAggregate.cs b/TransactionProcessor.Aggregates/MerchantAggregate.cs index a4c0b48b..cf7f1e1d 100644 --- a/TransactionProcessor.Aggregates/MerchantAggregate.cs +++ b/TransactionProcessor.Aggregates/MerchantAggregate.cs @@ -621,7 +621,7 @@ private static Result EnsureOperatorHasBeenAssigned(this MerchantAggregate aggre private static Result EnsureContractHasNotAlreadyBeenAdded(this MerchantAggregate aggregate, Guid contractId) { - if (aggregate.Contracts.ContainsKey(contractId)) { + if (aggregate.Contracts.Any(o => o.Key == contractId && o.Value.IsDeleted == false)) { return Result.Invalid($"Contract {contractId} has already been assigned to merchant"); } @@ -785,19 +785,21 @@ public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEve aggregate.Contacts.Add(contactAddedEvent.ContactId, contact); } - public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEvents.OperatorAssignedToMerchantEvent operatorAssignedToMerchantEvent) { - var @operator = aggregate.Operators.SingleOrDefault(o => o.Key == operatorAssignedToMerchantEvent.OperatorId); - - if (@operator.Value != null) { - aggregate.Operators[operatorAssignedToMerchantEvent.OperatorId] = @operator.Value with { IsDeleted = false }; + public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEvents.OperatorAssignedToMerchantEvent domainEvent) { + if (aggregate.Operators.ContainsKey(domainEvent.OperatorId)) + { + KeyValuePair @operator = aggregate.Operators.Single(c => c.Key == domainEvent.OperatorId); + aggregate.Operators[domainEvent.OperatorId] = @operator.Value with + { + IsDeleted = true + }; return; } + Operator newOperator = new Operator(domainEvent.OperatorId, domainEvent.Name, + domainEvent.MerchantNumber, + domainEvent.TerminalNumber); - Operator newOperator = new Operator(operatorAssignedToMerchantEvent.OperatorId, operatorAssignedToMerchantEvent.Name, - operatorAssignedToMerchantEvent.MerchantNumber, - operatorAssignedToMerchantEvent.TerminalNumber); - - aggregate.Operators.Add(operatorAssignedToMerchantEvent.OperatorId, newOperator); + aggregate.Operators.Add(domainEvent.OperatorId, newOperator); } public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEvents.OperatorRemovedFromMerchantEvent operatorRemovedFromMerchantEvent){ @@ -822,6 +824,14 @@ public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEve } public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEvents.ContractAddedToMerchantEvent domainEvent){ + if (aggregate.Contracts.ContainsKey(domainEvent.ContractId)) { + KeyValuePair contract = aggregate.Contracts.Single(c => c.Key == domainEvent.ContractId); + aggregate.Contracts[domainEvent.ContractId] = contract.Value with + { + IsDeleted = false + }; + return; + } aggregate.Contracts.Add(domainEvent.ContractId, new Contract(domainEvent.ContractId)); } From 58028df36de2d9646801fd3b5de1fe9a3980bd4f Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Sun, 1 Feb 2026 06:57:16 +0000 Subject: [PATCH 2/2] fix broken test --- TransactionProcessor.Aggregates/MerchantAggregate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TransactionProcessor.Aggregates/MerchantAggregate.cs b/TransactionProcessor.Aggregates/MerchantAggregate.cs index cf7f1e1d..8369a08e 100644 --- a/TransactionProcessor.Aggregates/MerchantAggregate.cs +++ b/TransactionProcessor.Aggregates/MerchantAggregate.cs @@ -791,7 +791,7 @@ public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEve KeyValuePair @operator = aggregate.Operators.Single(c => c.Key == domainEvent.OperatorId); aggregate.Operators[domainEvent.OperatorId] = @operator.Value with { - IsDeleted = true + IsDeleted = false }; return; }