diff --git a/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureAuditEntryIsAddedForMovementUpdatesTests.cs b/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureAuditEntryIsAddedForMovementUpdatesTests.cs index f428095c..854bf00b 100644 --- a/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureAuditEntryIsAddedForMovementUpdatesTests.cs +++ b/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureAuditEntryIsAddedForMovementUpdatesTests.cs @@ -15,22 +15,23 @@ public class EnsureAuditEntryIsAddedForMovementUpdatesTests(ITestOutputHelper ou : ScenarioGeneratorBaseTest(output) { [Fact] - public void ShouldHaveUpdatedAuditEntry() + public void ShouldHaveCorrectDocumentReferenceFromUpdatedClearanceRequest() { // Assert var movement = Client.AsJsonApiClient() .Get("api/movements") .GetResourceObjects() .Single(); - - movement.AuditEntries - .Count(a => a is { CreatedBy: "Cds", Status: "Updated" }) + + movement.Items + .Where(x => x.Documents != null) + .SelectMany(x => x.Documents!) + .Count(x => x.DocumentReference == "GBCHD2024.001239999999") .Should().Be(1); } - [Fact(Skip = "The document ref isn't being updated.")] - // [Fact] - public void ShouldHaveCorrectDocumentReferenceFromUpdatedClearanceRequest() + [Fact] + public void ShouldHaveUpdatedAuditEntry() { // Assert var movement = Client.AsJsonApiClient() @@ -38,6 +39,8 @@ public void ShouldHaveCorrectDocumentReferenceFromUpdatedClearanceRequest() .GetResourceObjects() .Single(); - movement.Items.First().Documents!.First().DocumentReference.Should().Be("GBCHD2024.001239999999"); + movement.AuditEntries + .Count(a => a is { CreatedBy: "Cds", Status: "Updated" }) + .Should().Be(1); } } \ No newline at end of file diff --git a/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureDuplicateItemsAreNotCreatedTests.cs b/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureDuplicateItemsAreNotCreatedTests.cs index 0a9318f0..fe4f568e 100644 --- a/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureDuplicateItemsAreNotCreatedTests.cs +++ b/Btms.Backend.IntegrationTests/PreprocessingTests/EnsureDuplicateItemsAreNotCreatedTests.cs @@ -13,9 +13,7 @@ namespace Btms.Backend.IntegrationTests.PreprocessingTests; public class EnsureDuplicateItemsAreNotCreatedTests(ITestOutputHelper output) : ScenarioGeneratorBaseTest(output) { - - [Fact(Skip = "We're ending up with 2 items on the clearance request here.")] - // [Fact] + [Fact] public void ShouldNotCreateDuplicateItems() { // Arrange @@ -25,7 +23,6 @@ public void ShouldNotCreateDuplicateItems() d is { Message: AlvsClearanceRequest }) .Message; - // Act var jsonClientResponse = Client.AsJsonApiClient().GetById(movementMessage!.Header!.EntryReference!, "api/movements"); diff --git a/Btms.Business.Tests/PreProcessing/MovementPreProcessingTests.cs b/Btms.Business.Tests/PreProcessing/MovementPreProcessingTests.cs index a4140d79..3eddae43 100644 --- a/Btms.Business.Tests/PreProcessing/MovementPreProcessingTests.cs +++ b/Btms.Business.Tests/PreProcessing/MovementPreProcessingTests.cs @@ -18,7 +18,7 @@ public async Task WhenNotificationNotExists_ThenShouldBeCreated() // ARRANGE var clearanceRequest = CreateAlvsClearanceRequest(); var dbContext = new MemoryMongoDbContext(); - var preProcessor = new MovementPreProcessor(dbContext, NullLogger.Instance, new MovementBuilder(NullLogger.Instance)); + var preProcessor = new MovementPreProcessor(dbContext, NullLogger.Instance, new MovementBuilderFactory(NullLogger.Instance)); // ACT diff --git a/Btms.Business.Tests/Services/Decisions/NoMatchDecisionsTest.cs b/Btms.Business.Tests/Services/Decisions/NoMatchDecisionsTest.cs index 2ef160c1..c15df44b 100644 --- a/Btms.Business.Tests/Services/Decisions/NoMatchDecisionsTest.cs +++ b/Btms.Business.Tests/Services/Decisions/NoMatchDecisionsTest.cs @@ -79,13 +79,13 @@ private static List GenerateMovements(bool hasChecks) var config = ScenarioFactory.CreateScenarioConfig(generator, 1, 1); - var movementBuilder = new MovementBuilder(NullLogger.Instance); + var movementBuilderFactory = new MovementBuilderFactory(NullLogger.Instance); var generatorResult = generator .Generate(1, 1, DateTime.UtcNow, config) .First(x => x is AlvsClearanceRequest); var internalClearanceRequest = AlvsClearanceRequestMapper.Map((AlvsClearanceRequest)generatorResult); - var movement = movementBuilder + var movement = movementBuilderFactory .From(internalClearanceRequest) .Build(); diff --git a/Btms.Business.Tests/Services/Matching/MatchingServiceTests.cs b/Btms.Business.Tests/Services/Matching/MatchingServiceTests.cs index 0654b078..3aa755a6 100644 --- a/Btms.Business.Tests/Services/Matching/MatchingServiceTests.cs +++ b/Btms.Business.Tests/Services/Matching/MatchingServiceTests.cs @@ -50,7 +50,7 @@ private static List GenerateMovements() { CrNoMatchScenarioGenerator generator = new CrNoMatchScenarioGenerator(NullLogger.Instance); - var movementBuilder = new MovementBuilder(NullLogger.Instance); + var movementBuilderFactory = new MovementBuilderFactory(NullLogger.Instance); var config = ScenarioFactory.CreateScenarioConfig(generator, 1, 1); var generatorResult = generator @@ -58,7 +58,8 @@ private static List GenerateMovements() .First(x => x is AlvsClearanceRequest); var internalClearanceRequest = AlvsClearanceRequestMapper.Map((AlvsClearanceRequest)generatorResult); - var movement = movementBuilder + + var movement = movementBuilderFactory .From(internalClearanceRequest) .Build(); @@ -70,7 +71,7 @@ private static (List Notifications, List Movements ChedASimpleMatchScenarioGenerator generator = new ChedASimpleMatchScenarioGenerator(NullLogger.Instance); var config = ScenarioFactory.CreateScenarioConfig(generator, 1, 1); - var movementBuilder = new MovementBuilder(NullLogger.Instance); + var movementBuilderFactory = new MovementBuilderFactory(NullLogger.Instance); var generatorResult = generator.Generate(1, 1, DateTime.UtcNow, config); @@ -89,7 +90,7 @@ private static (List Notifications, List Movements case AlvsClearanceRequest cr: var internalClearanceRequest = AlvsClearanceRequestMapper.Map(cr); - memo.Movements.Add(movementBuilder.From(internalClearanceRequest).Build()); + memo.Movements.Add(movementBuilderFactory.From(internalClearanceRequest).Build()); break; default: throw new ArgumentException($"Unexpected type {x.GetType().Name}"); diff --git a/Btms.Business/Builders/MovementBuilder.cs b/Btms.Business/Builders/MovementBuilder.cs index 6ed228db..2646fb9a 100644 --- a/Btms.Business/Builders/MovementBuilder.cs +++ b/Btms.Business/Builders/MovementBuilder.cs @@ -10,62 +10,10 @@ namespace Btms.Business.Builders; -public class MovementBuilder(ILogger logger) +public class MovementBuilder(ILogger logger, Movement movement, bool hasChanges = false) { - private Movement? _movement; - public bool HasChanges = false; - - public MovementBuilder From(Model.Cds.CdsClearanceRequest request) - { - logger.LogInformation("Creating movement from clearance request {0}", request.Header!.EntryReference); - HasChanges = true; - _movement = new Movement() - { - Id = request.Header!.EntryReference, - UpdatedSource = request.ServiceHeader?.ServiceCalled, - CreatedSource = request.ServiceHeader?.ServiceCalled, - ArrivesAt = request.Header.ArrivesAt, - EntryReference = request.Header.EntryReference!, - EntryVersionNumber = request.Header.EntryVersionNumber.GetValueOrDefault(), - MasterUcr = request.Header.MasterUcr!, - DeclarationType = request.Header.DeclarationType!, - SubmitterTurn = request.Header.SubmitterTurn!, - DeclarantId = request.Header.DeclarantId!, - DeclarantName = request.Header.DeclarantName!, - DispatchCountryCode = request.Header.DispatchCountryCode!, - GoodsLocationCode = request.Header.GoodsLocationCode!, - ClearanceRequests = [request], - Items = request.Items?.ToList()!, - BtmsStatus = new MovementStatus() - { - ChedTypes = GetChedTypes(request.Items!.ToList()), - Linked = false, - LinkStatus = MovementStatus.NotLinkedStatus - } - }; - - return this; - } - - public MovementBuilder From(Movement movement) - { - HasChanges = true; - _movement = movement; - return this; - } - - private ImportNotificationTypeEnum[] GetChedTypes(List? items = null) - { - return items? - .SelectMany(i => i.Documents!) - .Select(d => - d.DocumentCode!.GetChedType() - ) - .Distinct() - .Where(ct => ct.HasValue()) - .Select(ct => ct!.Value) - .ToArray()!; - } + private Movement? _movement = movement; + public bool HasChanges = hasChanges; public string Id { @@ -100,7 +48,7 @@ public void ReplaceClearanceRequests(MovementBuilder builder) builder._movement.ClearanceRequests[0].Header?.EntryReference); _movement.ClearanceRequests.AddRange(builder._movement.ClearanceRequests); - _movement.Items.AddRange(builder._movement.Items); + _movement.Items = builder._movement.Items; } public ChangeSet GenerateChangeSet(MovementBuilder builder) diff --git a/Btms.Business/Builders/MovementBuilderFactory.cs b/Btms.Business/Builders/MovementBuilderFactory.cs new file mode 100644 index 00000000..aa690225 --- /dev/null +++ b/Btms.Business/Builders/MovementBuilderFactory.cs @@ -0,0 +1,60 @@ +using Btms.Common.Extensions; +using Btms.Model.Cds; +using Microsoft.Extensions.Logging; +using Btms.Business.Extensions; +using Btms.Model; +using Btms.Model.Ipaffs; + +namespace Btms.Business.Builders; + +public class MovementBuilderFactory(ILogger logger) +{ + public MovementBuilder From(CdsClearanceRequest request) + { + logger.LogInformation("Creating movement from clearance request {0}", request.Header!.EntryReference); + var movement = new Movement() + { + Id = request.Header!.EntryReference, + UpdatedSource = request.ServiceHeader?.ServiceCalled, + CreatedSource = request.ServiceHeader?.ServiceCalled, + ArrivesAt = request.Header.ArrivesAt, + EntryReference = request.Header.EntryReference!, + EntryVersionNumber = request.Header.EntryVersionNumber.GetValueOrDefault(), + MasterUcr = request.Header.MasterUcr!, + DeclarationType = request.Header.DeclarationType!, + SubmitterTurn = request.Header.SubmitterTurn!, + DeclarantId = request.Header.DeclarantId!, + DeclarantName = request.Header.DeclarantName!, + DispatchCountryCode = request.Header.DispatchCountryCode!, + GoodsLocationCode = request.Header.GoodsLocationCode!, + ClearanceRequests = [request], + Items = request.Items?.ToList()!, + BtmsStatus = new MovementStatus() + { + ChedTypes = GetChedTypes(request.Items!.ToList()), + Linked = false, + LinkStatus = MovementStatus.NotLinkedStatus + } + }; + + return new MovementBuilder(logger, movement, true); + } + + public MovementBuilder From(Movement movement) + { + return new MovementBuilder(logger, movement, true); + } + + private ImportNotificationTypeEnum[] GetChedTypes(List? items = null) + { + return items? + .SelectMany(i => i.Documents!) + .Select(d => + d.DocumentCode!.GetChedType() + ) + .Distinct() + .Where(ct => ct.HasValue()) + .Select(ct => ct!.Value) + .ToArray()!; + } +} \ No newline at end of file diff --git a/Btms.Business/Extensions/ServiceCollectionExtensions.cs b/Btms.Business/Extensions/ServiceCollectionExtensions.cs index c11c5119..d2316bd6 100644 --- a/Btms.Business/Extensions/ServiceCollectionExtensions.cs +++ b/Btms.Business/Extensions/ServiceCollectionExtensions.cs @@ -68,7 +68,7 @@ public static IServiceCollection AddBusinessServices(this IServiceCollection ser services.AddScoped(); services.AddScoped(); - services.AddTransient(); + services.AddTransient(); services.AddScoped, ImportNotificationPreProcessor>(); services.AddScoped, MovementPreProcessor>(); diff --git a/Btms.Business/Pipelines/PreProcessing/MovementPreProcessor.cs b/Btms.Business/Pipelines/PreProcessing/MovementPreProcessor.cs index 891e7ddb..3927fc53 100644 --- a/Btms.Business/Pipelines/PreProcessing/MovementPreProcessor.cs +++ b/Btms.Business/Pipelines/PreProcessing/MovementPreProcessor.cs @@ -9,13 +9,13 @@ namespace Btms.Business.Pipelines.PreProcessing; -public class MovementPreProcessor(IMongoDbContext dbContext, ILogger logger, MovementBuilder movementBuilder) : IPreProcessor +public class MovementPreProcessor(IMongoDbContext dbContext, ILogger logger, MovementBuilderFactory movementBuilderFactory) : IPreProcessor { public async Task> Process(PreProcessingContext preProcessingContext) { var internalClearanceRequest = AlvsClearanceRequestMapper.Map(preProcessingContext.Message); - var mb = movementBuilder.From(internalClearanceRequest); + var mb = movementBuilderFactory.From(internalClearanceRequest); var existingMovement = await dbContext.Movements.Find(mb.Id); if (existingMovement is null) @@ -36,9 +36,9 @@ public async Task> Process(PreProcessingContext existingMovement.ClearanceRequests[0].Header?.EntryVersionNumber) if (mb.IsEntryVersionNumberGreaterThan(existingMovement.ClearanceRequests[0].Header?.EntryVersionNumber)) { - var existingBuilder = movementBuilder.From(existingMovement); + var existingBuilder = movementBuilderFactory.From(existingMovement); // var changeSet = movement.ClearanceRequests[^1].GenerateChangeSet(existingMovement.ClearanceRequests[0]); - var changeSet = existingBuilder.GenerateChangeSet(mb); + var changeSet = mb.GenerateChangeSet(existingBuilder); var auditEntry = mb.UpdateAuditEntry( preProcessingContext.MessageId, @@ -46,7 +46,7 @@ public async Task> Process(PreProcessingContext.Instance); - var mb = - movementBuilder.From(AlvsClearanceRequestMapper.Map(clearanceRequest)); + var mbFactory = new MovementBuilderFactory(NullLogger.Instance); + var mb = mbFactory.From(AlvsClearanceRequestMapper.Map(clearanceRequest)); - movementBuilder.Update(AuditEntry.CreateLinked("Test", 1)); + mb.Update(AuditEntry.CreateLinked("Test", 1)); var movement = mb.Build(); @@ -67,11 +66,10 @@ public async Task WhenPreProcessingSucceeds_AndLastAuditEntryIsLinked_ThenLinkSh public async Task WhenPreProcessingSucceeds_AndLastAuditEntryIsCreated_ThenLinkShouldBeRun() { // ARRANGE - var movementBuilder = new MovementBuilder(NullLogger.Instance); + var mbFactory = new MovementBuilderFactory(NullLogger.Instance); var clearanceRequest = CreateAlvsClearanceRequest(); - var mb = - movementBuilder.From(AlvsClearanceRequestMapper.Map(clearanceRequest)); + var mb = mbFactory.From(AlvsClearanceRequestMapper.Map(clearanceRequest)); mb.Update(mb.CreateAuditEntry("Test", AuditEntry.CreatedByCds)); diff --git a/Btms.Consumers/DecisionsConsumer.cs b/Btms.Consumers/DecisionsConsumer.cs index 225b7e71..49749b9f 100644 --- a/Btms.Consumers/DecisionsConsumer.cs +++ b/Btms.Consumers/DecisionsConsumer.cs @@ -8,7 +8,7 @@ namespace Btms.Consumers; -public class DecisionsConsumer(IMongoDbContext dbContext, MovementBuilder existingMovementBuilder) +public class DecisionsConsumer(IMongoDbContext dbContext, MovementBuilderFactory movementBuilderFactory) : IConsumer, IConsumerWithContext { public async Task OnHandle(Decision message) @@ -21,7 +21,7 @@ public async Task OnHandle(Decision message) var auditId = Context.Headers["messageId"].ToString(); var notificationContext = Context.Headers.GetValueOrDefault("notifications", null) as List; - existingMovementBuilder = existingMovementBuilder + var existingMovementBuilder = movementBuilderFactory .From(existingMovement!) .MergeDecision(auditId!, internalClearanceRequest, notificationContext); // .Build(); diff --git a/Btms.Model/Auditing/AuditEntry.cs b/Btms.Model/Auditing/AuditEntry.cs index d399066a..8dbeb945 100644 --- a/Btms.Model/Auditing/AuditEntry.cs +++ b/Btms.Model/Auditing/AuditEntry.cs @@ -44,7 +44,7 @@ public bool IsCreated() public bool IsUpdated() { - return Status == "Created"; + return Status == "Updated"; }