Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/cdms 147 #24

Merged
merged 5 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Btms.Analytics.Tests/Helpers/TestDataGeneratorHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ public static async Task<IHost> PushToConsumers(this IHost app, ILogger logger,
break;

case Decision d:
// This sleep is to allow the system to settle, and have made any links & decisions
// Before sending in a decision and causing a concurrency issue
// Ideally we want to switch to pushing to the bus, rather than directly to the consumer
// so we get the concurrency protection.

Thread.Sleep(1000);

var decisionConsumer = (DecisionsConsumer)scope
.ServiceProvider
Expand Down
4 changes: 2 additions & 2 deletions Btms.Analytics.Tests/MovementsByDecisionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public async Task WhenCalled_ReturnExpectedAggregation()

testOutputHelper.WriteLine("{0} aggregated items found", result.Count);

result.Count.Should().Be(3);
result.Count.Should().Be(4);
result.Select(r => r.Key).Order().Should()
.Equal("ALVS Linked : H01", "BTMS Linked : X00", "BTMS Not Linked : X00");
.Equal("ALVS Linked : H01", "BTMS Linked : C03", "BTMS Linked : X00", "BTMS Not Linked : X00");
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,64 @@
using Btms.Business.Services.Decisions;
using Btms.Business.Services.Decisions.Finders;
using Btms.Model.Ipaffs;
using FluentAssertions;
using Xunit;

namespace Btms.Business.Tests.Services.Decisions.Finders;

public class ChedADecisionFinderTests
{
[Fact]
public async Task Find_Should_DoThings()
[Theory]
[InlineData(true, DecisionDecisionEnum.AcceptableForTranshipment, null, DecisionCode.E03)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTransit, null, DecisionCode.E03)]
[InlineData(true, DecisionDecisionEnum.AcceptableForInternalMarket, null, DecisionCode.C03)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTemporaryImport, null, DecisionCode.C05)]
[InlineData(true, DecisionDecisionEnum.HorseReEntry, null, DecisionCode.C06)]

[InlineData(true, DecisionDecisionEnum.NonAcceptable, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableIfChanneled, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForSpecificWarehouse, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForPrivateImport, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTransfer, null, DecisionCode.X00)]

[InlineData(null, null, null, DecisionCode.X00)]

[InlineData(false,null, DecisionNotAcceptableActionEnum.Euthanasia, DecisionCode.N02)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Reexport, DecisionCode.N04)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Slaughter, DecisionCode.N02)]

[InlineData(false, null, DecisionNotAcceptableActionEnum.Redispatching, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Destruction, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Transformation, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Other, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.EntryRefusal, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.QuarantineImposed, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.SpecialTreatment, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.IndustrialProcessing, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.ReDispatch, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.UseForOtherPurposes, DecisionCode.X00)]

public void DecisionFinderTest(bool? consignmentAcceptable, DecisionDecisionEnum? decision, DecisionNotAcceptableActionEnum? notAcceptableAction, DecisionCode expectedCode)
{
// Arrange
var notification = new ImportNotification
{
PartTwo = new PartTwo
{
Decision = new Decision
{
ConsignmentAcceptable = consignmentAcceptable,
DecisionEnum = decision,
NotAcceptableAction = notAcceptableAction
}
}
};
var sut = new ChedADecisionFinder();

// Act
var result = sut.FindDecision(notification);

// Assert
await Task.CompletedTask;
result.DecisionCode.Should().Be(expectedCode);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,64 @@
using Btms.Business.Services.Decisions;
using Btms.Business.Services.Decisions.Finders;
using Btms.Model.Ipaffs;
using FluentAssertions;
using Xunit;

namespace Btms.Business.Tests.Services.Decisions.Finders;

public class ChedDDecisionFinderTests
{
[Fact]
public async Task Find_Should_DoThings()
[Theory]
[InlineData(true, DecisionDecisionEnum.AcceptableForInternalMarket, null, DecisionCode.C03)]

[InlineData(true, DecisionDecisionEnum.AcceptableForTranshipment, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTransit, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTemporaryImport, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.HorseReEntry, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.NonAcceptable, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableIfChanneled, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForSpecificWarehouse, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForPrivateImport, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTransfer, null, DecisionCode.X00)]

[InlineData(null, null, null, DecisionCode.X00)]

[InlineData(false, null, DecisionNotAcceptableActionEnum.Redispatching, DecisionCode.N04)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Destruction, DecisionCode.N02)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Transformation, DecisionCode.N03)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Other, DecisionCode.N07)]

[InlineData(false, null, DecisionNotAcceptableActionEnum.Euthanasia, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Reexport, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Slaughter, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.EntryRefusal, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.QuarantineImposed, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.SpecialTreatment, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.IndustrialProcessing, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.ReDispatch, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.UseForOtherPurposes, DecisionCode.X00)]

public void DecisionFinderTest(bool? consignmentAcceptable, DecisionDecisionEnum? decision, DecisionNotAcceptableActionEnum? notAcceptableAction, DecisionCode expectedCode)
{
// Arrange
var notification = new ImportNotification
{
PartTwo = new PartTwo
{
Decision = new Decision
{
ConsignmentAcceptable = consignmentAcceptable,
DecisionEnum = decision,
NotAcceptableAction = notAcceptableAction
}
}
};
var sut = new ChedDDecisionFinder();

// Act
var result = sut.FindDecision(notification);

// Assert
await Task.CompletedTask;
result.DecisionCode.Should().Be(expectedCode);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,63 @@
using Btms.Business.Services.Decisions;
using Btms.Business.Services.Decisions.Finders;
using Btms.Model.Ipaffs;
using FluentAssertions;
using Xunit;

namespace Btms.Business.Tests.Services.Decisions.Finders;

public class ChedPDecisionFinderTests
{
[Fact]
public async Task Find_Should_DoThings()
[Theory]
[InlineData(true, DecisionDecisionEnum.AcceptableForInternalMarket, null, DecisionCode.C03)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTransit, null, DecisionCode.E03)]
[InlineData(true, DecisionDecisionEnum.AcceptableIfChanneled, null, DecisionCode.C06)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTranshipment, null, DecisionCode.E03)]
[InlineData(true, DecisionDecisionEnum.AcceptableForSpecificWarehouse, null, DecisionCode.E03)]

[InlineData(true, DecisionDecisionEnum.AcceptableForTemporaryImport, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.HorseReEntry, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.NonAcceptable, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForPrivateImport, null, DecisionCode.X00)]
[InlineData(true, DecisionDecisionEnum.AcceptableForTransfer, null, DecisionCode.X00)]

[InlineData(null, null, null, DecisionCode.X00)]

[InlineData(false, null, DecisionNotAcceptableActionEnum.Reexport, DecisionCode.N04)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Destruction, DecisionCode.N02)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Transformation, DecisionCode.N03)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Other, DecisionCode.N07)]

[InlineData(false, null, DecisionNotAcceptableActionEnum.Euthanasia, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Slaughter, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.Redispatching, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.EntryRefusal, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.QuarantineImposed, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.SpecialTreatment, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.IndustrialProcessing, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.ReDispatch, DecisionCode.X00)]
[InlineData(false, null, DecisionNotAcceptableActionEnum.UseForOtherPurposes, DecisionCode.X00)]
public void DecisionFinderTest(bool? consignmentAcceptable, DecisionDecisionEnum? decision, DecisionNotAcceptableActionEnum? notAcceptableAction, DecisionCode expectedCode)
{
// Arrange
var notification = new ImportNotification
{
PartTwo = new PartTwo
{
Decision = new Decision
{
ConsignmentAcceptable = consignmentAcceptable,
DecisionEnum = decision,
NotAcceptableAction = notAcceptableAction
}
}
};
var sut = new ChedPDecisionFinder();

// Act
var result = sut.FindDecision(notification);

// Assert
await Task.CompletedTask;
result.DecisionCode.Should().Be(expectedCode);
}
}
41 changes: 36 additions & 5 deletions Btms.Business.Tests/Services/Decisions/NoMatchDecisionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,48 @@
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using SlimMessageBus;
using TestDataGenerator;
using TestDataGenerator.Scenarios;
using Xunit;
using Check = Btms.Model.Alvs.Check;
using Decision = Btms.Model.Ipaffs.Decision;

namespace Btms.Business.Tests.Services.Decisions;

public class NoMatchDecisionsTest
{
[Fact]
public async Task WhenClearanceRequest_HasNotMatch_AndNoChecks_ThenNoDecisionShouldBeGenerated()
{
// Arrange
var movements = GenerateMovements(false);
var publishBus = Substitute.For<IPublishBus>();

var sut = new DecisionService(NullLogger<DecisionService>.Instance, publishBus);

var matchingResult = new MatchingResult();
matchingResult.AddDocumentNoMatch(movements[0].Id!, movements[0].Items[0].ItemNumber!.Value, movements[0].Items[0].Documents?[0].DocumentReference!);

// Act
var decisionResult = await sut.Process(new DecisionContext(new List<ImportNotification>(), movements, matchingResult, true), CancellationToken.None);

// Assert
decisionResult.Should().NotBeNull();
decisionResult.Decisions.Count.Should().Be(0);
await publishBus.Received(0).Publish(Arg.Any<Decision>(), Arg.Any<string>(),
Arg.Any<IDictionary<string, object>>(), Arg.Any<CancellationToken>());
await Task.CompletedTask;
}

[Fact]
public async Task WhenClearanceRequest_HasNotMatch_ThenDecisionCodeShouldBeNoMatch()
{
// Arrange
var movements = GenerateMovements();
var movements = GenerateMovements(true);
movements[0].Items[0].Checks = [new Check() { CheckCode = "TEST" }];
var publishBus = Substitute.For<IPublishBus>();

var sut = new DecisionService(publishBus);
var sut = new DecisionService(NullLogger<DecisionService>.Instance, publishBus);

var matchingResult = new MatchingResult();
matchingResult.AddDocumentNoMatch(movements[0].Id!, movements[0].Items[0].ItemNumber!.Value, movements[0].Items[0].Documents?[0].DocumentReference!);
Expand All @@ -41,10 +68,14 @@ await publishBus.Received().Publish(Arg.Any<Types.Alvs.Decision>(), Arg.Any<stri
await Task.CompletedTask;
}

private static List<Movement> GenerateMovements()
// CrNoMatchNoChecksScenarioGenerator
// CrNoMatchScenarioGenerator
private static List<Movement> GenerateMovements(bool hasChecks)
{
CrNoMatchScenarioGenerator generator =
new CrNoMatchScenarioGenerator(NullLogger<CrNoMatchScenarioGenerator>.Instance);
ScenarioGenerator generator = hasChecks
? new CrNoMatchScenarioGenerator(NullLogger<CrNoMatchScenarioGenerator>.Instance)
: new CrNoMatchNoChecksScenarioGenerator(NullLogger<CrNoMatchNoChecksScenarioGenerator>.Instance);

var config = ScenarioFactory.CreateScenarioConfig(generator, 1, 1);

var generatorResult = generator.Generate(1, 1, DateTime.UtcNow, config);
Expand Down
2 changes: 2 additions & 0 deletions Btms.Business/Services/Decisions/DecisionCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ namespace Btms.Business.Services.Decisions;

public enum DecisionCode
{
H01,
H02,
E03,
C03,
C05,
Expand Down
40 changes: 24 additions & 16 deletions Btms.Business/Services/Decisions/DecisionService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using Btms.Business.Services.Decisions.Finders;
using Btms.Model.Ipaffs;
using Microsoft.Extensions.Logging;
using SlimMessageBus;

namespace Btms.Business.Services.Decisions;

public class DecisionService(IPublishBus bus) : IDecisionService
public class DecisionService(ILogger<DecisionService> logger, IPublishBus bus) : IDecisionService
{

public async Task<DecisionResult> Process(DecisionContext decisionContext, CancellationToken cancellationToken)
Expand All @@ -25,33 +26,37 @@ public async Task<DecisionResult> Process(DecisionContext decisionContext, Cance
return decisionResult;
}

private static Task<DecisionResult> DeriveDecision(DecisionContext decisionContext)
private Task<DecisionResult> DeriveDecision(DecisionContext decisionContext)
{
var decisionsResult = new DecisionResult();
if (decisionContext.GenerateNoMatch)
{
foreach (var noMatch in decisionContext.MatchingResult.NoMatches)
{
decisionsResult.AddDecision(noMatch.MovementId, noMatch.ItemNumber, noMatch.DocumentReference,
DecisionCode.X00);
if (decisionContext.HasChecks(noMatch.MovementId, noMatch.ItemNumber))
{
decisionsResult.AddDecision(noMatch.MovementId, noMatch.ItemNumber, noMatch.DocumentReference,
DecisionCode.X00);
}
}
}

////Not part of no matches, and the finders haven't been implemented yet, so leaving this commented out for the moment
////foreach (var match in decisionContext.MatchingResult.Matches)
////{
//// var n = decisionContext.Notifications.First(x => x.Id == match.NotificationId);
//// var decisionCode = GetDecision(n);
//// decisionsResult.AddDecision(match.MovementId, match.ItemNumber, match.DocumentReference, decisionCode.DecisionCode);
////}
foreach (var match in decisionContext.MatchingResult.Matches)
{
if (decisionContext.HasChecks(match.MovementId, match.ItemNumber))
{
var n = decisionContext.Notifications.First(x => x.Id == match.NotificationId);
var decisionCode = GetDecision(n);
decisionsResult.AddDecision(match.MovementId, match.ItemNumber, match.DocumentReference,
decisionCode.DecisionCode);
}
}

return Task.FromResult(decisionsResult);
}


#pragma warning disable S1144
private static DecisionFinderResult GetDecision(ImportNotification notification)
#pragma warning restore S1144

private DecisionFinderResult GetDecision(ImportNotification notification)
{
// get decision finder - fold IUU stuff into the decision finder for fish?
IDecisionFinder finder = notification.ImportNotificationType switch
Expand All @@ -63,7 +68,10 @@ private static DecisionFinderResult GetDecision(ImportNotification notification)
_ => throw new InvalidOperationException("Invalid ImportNotificationType")
};

return finder.FindDecision(notification);
var result = finder.FindDecision(notification);
logger.LogInformation("Decision finder result for Notification {Id} Decision {Decision} - ConsignmentAcceptable {ConsignmentAcceptable}: DecisionEnum {DecisionEnum}: NotAcceptableAction {NotAcceptableAction}",
notification.Id, result.DecisionCode.ToString(), notification.PartTwo?.Decision?.ConsignmentAcceptable, notification.PartTwo?.Decision?.DecisionEnum.ToString(), notification.PartTwo?.Decision?.NotAcceptableAction?.ToString());
return result;
}


Expand Down
Loading
Loading