diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/AddDraftApprenticeshipRequest.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/AddDraftApprenticeshipRequest.cs index 91374c5146..72a9ab0c68 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/AddDraftApprenticeshipRequest.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/AddDraftApprenticeshipRequest.cs @@ -25,5 +25,6 @@ public class AddDraftApprenticeshipRequest : SaveDataRequest public DateTime? EmploymentEndDate { get; set; } public bool IgnoreStartDateOverlap { get; set; } public bool? IsOnFlexiPaymentPilot { get; set; } + public LearnerVerificationResponse LearnerVerificationResponse { get; set; } } } \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/CreateCohortRequest.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/CreateCohortRequest.cs index 46f88f897d..85b8c6e4be 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/CreateCohortRequest.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/CreateCohortRequest.cs @@ -28,5 +28,6 @@ public class CreateCohortRequest : SaveDataRequest public DateTime? EmploymentEndDate { get; set; } public bool IgnoreStartDateOverlap { get; set; } public bool? IsOnFlexiPaymentPilot { get; set; } + public LearnerVerificationResponse LearnerVerificationResponse { get; set; } } } \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/UpdateDraftApprenticeshipRequest.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/UpdateDraftApprenticeshipRequest.cs index fbdc330e23..e2a1a6acb0 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/UpdateDraftApprenticeshipRequest.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.Types/Requests/UpdateDraftApprenticeshipRequest.cs @@ -24,5 +24,6 @@ public class UpdateDraftApprenticeshipRequest : SaveDataRequest public Guid? ReservationId { get; set; } public bool IgnoreStartDateOverlap { get; set; } public bool? IsOnFlexiPaymentPilot { get; set; } + public LearnerVerificationResponse LearnerVerificationResponse { get; set; } } -} +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.UnitTests/Controllers/CohortControllerTests/CreateTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.UnitTests/Controllers/CohortControllerTests/CreateTests.cs index d2778ea2e9..a9a1e00043 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.UnitTests/Controllers/CohortControllerTests/CreateTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api.UnitTests/Controllers/CohortControllerTests/CreateTests.cs @@ -68,7 +68,9 @@ public CreateTestsFixture() c.Uln == Request.Uln && c.TransferSenderId == Request.TransferSenderId && c.PledgeApplicationId == Request.PledgeApplicationId && - c.UserInfo == Request.UserInfo), CancellationToken.None)) + c.UserInfo == Request.UserInfo && + c.IsOnFlexiPaymentPilot == Request.IsOnFlexiPaymentPilot && + c.LearnerVerificationResponse == Request.LearnerVerificationResponse), CancellationToken.None)) .ReturnsAsync(Result); } diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api/Controllers/CohortController.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api/Controllers/CohortController.cs index 21bc60ce16..ac78102558 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api/Controllers/CohortController.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Api/Controllers/CohortController.cs @@ -66,7 +66,8 @@ public async Task Create([FromBody]CreateCohortRequest request) request.EmploymentEndDate, request.UserInfo, request.IgnoreStartDateOverlap, - request.IsOnFlexiPaymentPilot); + request.IsOnFlexiPaymentPilot, + request.LearnerVerificationResponse); var result = await _mediator.Send(command); diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Types/LearnerVerificationResponse.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Types/LearnerVerificationResponse.cs new file mode 100644 index 0000000000..7fffe726f0 --- /dev/null +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.Types/LearnerVerificationResponse.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace SFA.DAS.CommitmentsV2.Types +{ + public enum LearnerVerificationResponseType + { + SuccessfulMatch, + + SuccessfulLinkedMatch, + + SimilarMatch, + + SimilarLinkedMatch, + + LearnerDoesNotMatch, + + UlnNotFound + } + + public enum LearnerDetailMatchingError + { + GivenDoesntMatchGiven, + + GivenDoesntMatchFamily, + + GivenDoesntMatchPreviousFamily, + + FamilyDoesntMatchGiven, + + FamilyDoesntMatchFamily, + + FamilyDoesntMatchPreviousFamily, + + DateOfBirthDoesntMatchDateOfBirth, + + GenderDoesntMatchGender + } + + public class LearnerVerificationResponse + { + public LearnerVerificationResponseType ResponseType { get; set; } + + public IEnumerable MatchingErrors { get; set; } + } +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddCohortCommandHandlerTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddCohortCommandHandlerTests.cs index 6ac8e67ed1..5be004b283 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddCohortCommandHandlerTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddCohortCommandHandlerTests.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; @@ -44,6 +43,7 @@ public async Task ShouldCreateCohort() It.IsAny(), fixtures.UserInfo, fixtures.RequestingParty, + It.IsAny(), It.IsAny())); Assert.AreEqual(expectedHash, response.Reference); @@ -54,6 +54,10 @@ public class TestLogger : ILogger { private readonly List<(LogLevel logLevel, Exception exception, string message)> _logMessages = new List<(LogLevel logLevel, Exception exception, string message)>(); + public bool HasErrors => _logMessages.Any(l => l.logLevel == LogLevel.Error); + + public bool HasInfo => _logMessages.Any(l => l.logLevel == LogLevel.Information); + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { _logMessages.Add((logLevel, exception, formatter(state, exception))); @@ -68,17 +72,10 @@ public IDisposable BeginScope(TState state) { throw new NotImplementedException(); } - - public bool HasErrors => _logMessages.Any(l => l.logLevel == LogLevel.Error); - public bool HasInfo => _logMessages.Any(l => l.logLevel == LogLevel.Information); } public class AddCohortCommandHandlerTestFixture { - public ProviderCommitmentsDbContext Db { get; set; } - - public Mock Provider { get; set; } - public AddCohortCommandHandlerTestFixture() { Db = new ProviderCommitmentsDbContext(new DbContextOptionsBuilder() @@ -96,14 +93,18 @@ public AddCohortCommandHandlerTestFixture() commitment.Apprenticeships.Add(new DraftApprenticeship()); CohortDomainServiceMock = new Mock(); - CohortDomainServiceMock.Setup(x => x.CreateCohort(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + CohortDomainServiceMock + .Setup(x => x.CreateCohort(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(commitment); Logger = new TestLogger(); UserInfo = new UserInfo(); } + public ProviderCommitmentsDbContext Db { get; set; } + + public Mock Provider { get; set; } public Mock EncodingServiceMock { get; } public IEncodingService EncodingService => EncodingServiceMock.Object; @@ -113,6 +114,7 @@ public AddCohortCommandHandlerTestFixture() public TestLogger Logger { get; } public UserInfo UserInfo { get; } + public LearnerVerificationResponse LearnerVerificationResponse { get; } public Party RequestingParty => Party.Provider; public AddCohortCommandHandlerTestFixture WithGeneratedHash(string hash) @@ -127,13 +129,13 @@ public AddCohortCommandHandlerTestFixture WithGeneratedHash(string hash) public async Task Handle(long accountId, long accountLegalEntity, long providerId, long? transferSenderId, int? pledgeApplicationId, string courseCode) { Db.SaveChanges(); - + var command = new AddCohortCommand( RequestingParty, accountId, accountLegalEntity, providerId, - courseCode, + courseCode, null, null, null, @@ -152,7 +154,8 @@ public async Task Handle(long accountId, long accountLegalEntit null, UserInfo, false, - false); + false, + LearnerVerificationResponse); var handler = new AddCohortHandler(new Lazy(() => Db), EncodingService, diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddDraftApprenticeshipCommandHandlerTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddDraftApprenticeshipCommandHandlerTests.cs index 404cf5e297..766386785f 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddDraftApprenticeshipCommandHandlerTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Application/Commands/AddDraftApprenticeshipCommandHandlerTests.cs @@ -5,7 +5,6 @@ using FluentAssertions; using MediatR; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; @@ -31,7 +30,7 @@ public Task Handle_WhenCommandIsHandled_ThenShouldAddDraftApprenticeship() return TestAsync( f => f.AddDraftApprenticeship(), f => f.CohortDomainService.Verify(c => c.AddDraftApprenticeship(f.Command.ProviderId, - f.Command.CohortId, f.DraftApprenticeshipDetails, f.UserInfo, f.Command.RequestingParty, f.CancellationToken))); + f.Command.CohortId, f.DraftApprenticeshipDetails, f.UserInfo, f.Command.RequestingParty, f.LearnerVerificationResponse, f.CancellationToken))); } [Test] @@ -57,12 +56,13 @@ public class AddDraftApprenticeshipCommandHandlerTestsFixture public Mock CohortDomainService { get; set; } public IRequestHandler Handler { get; set; } public UserInfo UserInfo { get; } + public LearnerVerificationResponse LearnerVerificationResponse { get; } public AddDraftApprenticeshipCommandHandlerTestsFixture() { Fixture = new Fixture(); DraftApprenticeshipDetails = Fixture.Build() - .With(o => o.IgnoreStartDateOverlap,false) + .With(o => o.IgnoreStartDateOverlap, false) .Create(); DraftApprenticeship = new DraftApprenticeship().Set(a => a.Id, 123); CancellationToken = new CancellationToken(); @@ -74,8 +74,9 @@ public AddDraftApprenticeshipCommandHandlerTestsFixture() CohortDomainService = new Mock(); DraftApprenticeshipDetailsMapper = new Mock>(); UserInfo = Fixture.Create(); + LearnerVerificationResponse = Fixture.Create(); - Command = Fixture.Build().With(o => o.UserInfo, UserInfo).Without(x => x.IgnoreStartDateOverlap).Create(); + Command = Fixture.Build().With(o => o.UserInfo, UserInfo).With(o => o.LearnerVerificationResponse, LearnerVerificationResponse).Without(x => x.IgnoreStartDateOverlap).Create(); Handler = new AddDraftApprenticeshipCommandHandler( new Lazy(() => Db), @@ -84,7 +85,7 @@ public AddDraftApprenticeshipCommandHandlerTestsFixture() CohortDomainService.Object); CohortDomainService.Setup(s => s.AddDraftApprenticeship(Command.ProviderId, Command.CohortId, - DraftApprenticeshipDetails, Command.UserInfo, Command.RequestingParty, CancellationToken)).ReturnsAsync(DraftApprenticeship); + DraftApprenticeshipDetails, Command.UserInfo, Command.RequestingParty, Command.LearnerVerificationResponse, CancellationToken)).ReturnsAsync(DraftApprenticeship); DraftApprenticeshipDetailsMapper.Setup(m => m.Map(Command)).ReturnsAsync(DraftApprenticeshipDetails); } diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/AddCohortCommandToDraftApprenticeshipDetailsMapperTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/AddCohortCommandToDraftApprenticeshipDetailsMapperTests.cs index ab73a46579..4f85ea3eed 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/AddCohortCommandToDraftApprenticeshipDetailsMapperTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/AddCohortCommandToDraftApprenticeshipDetailsMapperTests.cs @@ -157,7 +157,7 @@ public AddCohortCommandToDraftApprenticeshipDetailsMapperTestsFixture() Command = new AddCohortCommand(command.RequestingParty, command.AccountId, command.AccountLegalEntityId, command.ProviderId, courseCode, command.DeliveryModel, command.Cost, command.StartDate, command.ActualStartDate, command.EndDate, command.OriginatorReference, command.ReservationId, command.FirstName, command.LastName, command.Email, command.DateOfBirth, - command.Uln, command.TransferSenderId, command.PledgeApplicationId, command.EmploymentPrice, command.EmploymentEndDate, command.UserInfo, true, true); + command.Uln, command.TransferSenderId, command.PledgeApplicationId, command.EmploymentPrice, command.EmploymentEndDate, command.UserInfo, true, true, command.LearnerVerificationResponse); AuthorizationService = new Mock(); TrainingProgrammeLookup = new Mock(); @@ -194,7 +194,7 @@ private AddCohortCommand AddCohortCommandNoDate() return new AddCohortCommand(Command.RequestingParty, Command.AccountId, Command.AccountLegalEntityId, Command.ProviderId, Command.CourseCode, Command.DeliveryModel, Command.Cost, null, null, null, Command.OriginatorReference, Command.ReservationId, Command.FirstName, Command.LastName, Command.Email, Command.DateOfBirth, Command.Uln, - Command.TransferSenderId, Command.PledgeApplicationId, Command.EmploymentPrice, Command.EmploymentEndDate, Command.UserInfo, false, false); + Command.TransferSenderId, Command.PledgeApplicationId, Command.EmploymentPrice, Command.EmploymentEndDate, Command.UserInfo, false, false, Command.LearnerVerificationResponse); } public Task MapWithFramework() @@ -217,7 +217,7 @@ private AddCohortCommand CommandWithFramework() return new AddCohortCommand(Command.RequestingParty, Command.AccountId, Command.AccountLegalEntityId, Command.ProviderId, frameworkId, Command.DeliveryModel, Command.Cost, Command.StartDate, Command.ActualStartDate, Command.EndDate, Command.OriginatorReference, Command.ReservationId, Command.FirstName, Command.LastName, Command.Email, Command.DateOfBirth, Command.Uln, - Command.TransferSenderId, Command.PledgeApplicationId, Command.EmploymentPrice, Command.EmploymentEndDate, Command.UserInfo, false, false); + Command.TransferSenderId, Command.PledgeApplicationId, Command.EmploymentPrice, Command.EmploymentEndDate, Command.UserInfo, false, false, Command.LearnerVerificationResponse); } } } \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapperTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapperTests.cs index 0d2c92cabd..62763c2ca8 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapperTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapperTests.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Moq; using NUnit.Framework; using SFA.DAS.CommitmentsV2.Api.Types.Requests; using SFA.DAS.CommitmentsV2.Application.Commands.UpdateDraftApprenticeship; @@ -9,9 +10,9 @@ namespace SFA.DAS.CommitmentsV2.UnitTests.Mapping.RequestToCommandMappers { [TestFixture] - public class UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapperTests : + public class UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapperTests : OldMapperTester { [Test] @@ -19,7 +20,7 @@ public Task Map_CourseCode_ShouldBeSet() { return AssertPropertySet(from => from.CourseCode, "001/AAA"); } - + [Test] public Task Map_CostWithoutValue_ShouldBeSet() { @@ -56,7 +57,6 @@ public Task Map_StartDateWithValue_ShouldBeSet() return AssertPropertySet(from => from.StartDate, (DateTime?)DateTime.Now); } - [Test] public Task Map_ActualStartDateWithoutValue_ShouldBeSet() { @@ -153,6 +153,18 @@ public Task Map_IsOnFlexiPaymentPilotWithValue_ShouldBeSet() return AssertPropertySet(from => from.IsOnFlexiPaymentPilot, (bool?)true); } + [Test] + public Task Map_LearnerVerificationResponseWithoutValue_ShouldBeSet() + { + return AssertPropertySet(from => from.LearnerVerificationResponse, (LearnerVerificationResponse)null); + } + + [Test] + public Task Map_LearnerVerificationResponseWithValue_ShouldBeSet() + { + return AssertPropertySet(from => from.LearnerVerificationResponse, It.IsAny()); + } + [TestCase(DeliveryModel.Regular)] [TestCase(DeliveryModel.PortableFlexiJob)] public Task Map_DeliveryModel_ShouldBeSet(DeliveryModel dm) @@ -166,4 +178,4 @@ public Task Map_RequestingParty_ShouldBeSet() return AssertPropertySet(from => from.RequestingParty, (Party?)Party.Employer); } } -} +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/UpdateDraftApprenticeshipToDraftApprenticeshipDetailsMapperTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/UpdateDraftApprenticeshipToDraftApprenticeshipDetailsMapperTests.cs index 0c1d8537cc..6105e43a53 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/UpdateDraftApprenticeshipToDraftApprenticeshipDetailsMapperTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Mapping/UpdateDraftApprenticeshipToDraftApprenticeshipDetailsMapperTests.cs @@ -49,7 +49,7 @@ public Task Map_WhenMapping_ThenShouldSetProperties() public Task Map_WhenMapping_WithNoDate_VersionPropertiesNotSet() { return TestAsync( - f => f.MapNoDateAndNoVersionFields(), + f => f.MapNoDateAndNoVersionFields(), (f, r) => { r.FirstName.Should().Be(f.Command.FirstName); @@ -73,7 +73,7 @@ public Task Map_WhenMapping_WithNoDate_VersionPropertiesNotSet() public Task Map_WhenMappingWithDateAndStandardId_Then_UsesCalculatedTrainingProgramme() { return TestAsync( - f => f.MapWithStandard(), + f => f.MapWithStandard(), (f, r) => { r.FirstName.Should().Be(f.Command.FirstName); @@ -121,7 +121,7 @@ public Task Map_WhenMappingWithFramework_Then_UsesGetTrainingProgramme() public Task Map_WhenMappingWithNoCourse_Then_TrainingCourseVersionConfirmedIsFalse() { return TestAsync( - f => f.MapNoCourse(), + f => f.MapNoCourse(), (f, r) => { r.FirstName.Should().Be(f.Command.FirstName); @@ -141,7 +141,7 @@ public Task Map_WhenMappingWithNoCourse_Then_TrainingCourseVersionConfirmedIsFal }); } } - + public class UpdateDraftApprenticeshipToDraftApprenticeshipDetailsMapperTestsFixture { public Fixture Fixture { get; set; } @@ -177,7 +177,7 @@ public Task MapNoDateAndNoVersionFields() Command.CourseCode = Fixture.Create().ToString(); return Mapper.Map(Command); } - + public Task MapWithDate() { Command.StartDate = DateTime.Now; @@ -197,4 +197,4 @@ public Task MapWithStandard() return Mapper.Map(Command); } } -} +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Services/CohortDomainServiceTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Services/CohortDomainServiceTests.cs index 855ee758ca..bd4ab9d393 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Services/CohortDomainServiceTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Services/CohortDomainServiceTests.cs @@ -207,8 +207,6 @@ await _fixture _fixture.VerifyException(); } - - [Test] public async Task CreateCohortWithOtherParty_ThrowsBadRequest_WhenPledgeApplicationNotFound() { @@ -219,7 +217,6 @@ await _fixture _fixture.VerifyException(); } - [Test] public async Task CreateCohort_ThrowsBadRequest_WhenAccountIdDoesNotMatchAccountIdOnLegalEntity() { @@ -407,6 +404,7 @@ public async Task EmailOverlapOnApprenticeship_Validation(Party party, bool isAp await _fixture.WithParty(party).WithEmailOverlapWithApprenticeship(isApproved).CreateCohort(); _fixture.VerifyEmailOverlapExceptionOnApprenticeship(isApproved); } + [TestCase(Party.Provider)] [TestCase(Party.Employer)] public async Task EmailOverlapOnApprenticeship_Validation_FindsNoOverlaps(Party party) @@ -447,7 +445,7 @@ public void AddDraftApprenticeship_WhenCohortIsApprovedByAllParties_ShouldThrowE _fixture.WithExistingCohortApprovedByAllParties(Party.Employer); Assert.ThrowsAsync(() => _fixture.AddDraftApprenticeship()); } - + [Test] public void UpdateDraftApprenticeship_WhenCohortIsApprovedByAllParties_ShouldThrowException() { @@ -549,6 +547,7 @@ public async Task ApproveCohort_WhenRPLIsRequiredAndRPLDataIsPresent_ShouldSucce Assert.AreEqual(0, _fixture.DomainErrors.Count); } + [Test] public async Task ApproveCohort_WhenExtendedRPLIsRequiredAndRPLDataIsPresent_ShouldSuceed() { @@ -602,7 +601,7 @@ public async Task UpdateDraftApprenticeship_WhenContinuation_StartDateMustBeAfte .WithContinuation(overlap); await _fixture.UpdateDraftApprenticeship(); - if(expectThrow) + if (expectThrow) { _fixture.VerifyException(); } @@ -743,54 +742,6 @@ public async Task UpdateDraftApprenticeship_IsSuccessfulAndRPLHasNotPreviouslyBe public class CohortDomainServiceTestFixture { - public DateTime Now { get; set; } - public CohortDomainService CohortDomainService { get; set; } - public ProviderCommitmentsDbContext Db { get; set; } - public long ProviderId { get; } - public long AccountId { get; } - public long TransferSenderId { get; } - public string TransferSenderName { get; } - public int? PledgeApplicationId { get; } - public long AccountLegalEntityId { get; } - public long CohortId { get; } - public Party RequestingParty { get; private set; } - public string AccountLegalEntityPublicHashedId { get; } - public long ChangeOfPartyRequestId { get; } - public DraftApprenticeshipDetails DraftApprenticeshipDetails { get; } - public DraftApprenticeship ExistingDraftApprenticeship { get; } - public Apprenticeship PreviousApprenticeship { get; } - public long DraftApprenticeshipId { get; } - public Mock ChangeOfPartyRequest { get; } - public Account EmployerAccount { get; set; } - public Account TransferSenderAccount { get; set; } - public Mock Provider { get; set; } - public Mock AccountLegalEntity { get; set; } - public Cohort Cohort { get; set; } - public Cohort NewCohort { get; set; } - public Mock AcademicYearDateProvider { get; } - public Mock UlnValidator { get; } - public Mock ReservationValidationService { get; } - public Mock EmployerAgreementService { get; } - public Mock EncodingService { get; } - private Mock OverlapCheckService { get; } - private Mock EmailOptionalService { get; } - public Party Party { get; set; } - public Mock AuthenticationService { get; } - public Mock CurrentDateTime { get; set; } - public Mock AccountApiClient { get; set; } - public Mock LevyTransferMatchingApiClient { get; set; } - public Mock> FeatureTogglesService { get; set; } - public PledgeApplication PledgeApplication { get; set; } - public List TransferConnections { get; } - - public Exception Exception { get; private set; } - public List DomainErrors { get; } - public string Message { get; private set; } - public UserInfo UserInfo { get; private set; } - public ApprenticeshipPriorLearning PriorLearning{ get; private set; } - - public long MaLegalEntityId { get; private set; } - public CohortDomainServiceTestFixture() { Now = DateTime.UtcNow; @@ -816,17 +767,17 @@ public CohortDomainServiceTestFixture() Message = fixture.Create(); - NewCohort = new Cohort {Apprenticeships = new List {new DraftApprenticeship()}}; - - Provider = new Mock(()=> new Provider(ProviderId, "Test Provider", DateTime.UtcNow, DateTime.UtcNow)); + NewCohort = new Cohort { Apprenticeships = new List { new DraftApprenticeship() } }; + + Provider = new Mock(() => new Provider(ProviderId, "Test Provider", DateTime.UtcNow, DateTime.UtcNow)); Provider.Setup(x => x.CreateCohort(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(NewCohort); Db.Providers.Add(Provider.Object); EmployerAccount = new Account(AccountId, "AAAA", "BBBB", "Account 1", DateTime.UtcNow); Db.Accounts.Add(EmployerAccount); - AccountLegalEntity = new Mock(()=> - new AccountLegalEntity(EmployerAccount,AccountLegalEntityId,MaLegalEntityId,"test","ABC","Test",OrganisationType.CompaniesHouse,"test",DateTime.UtcNow)); + AccountLegalEntity = new Mock(() => + new AccountLegalEntity(EmployerAccount, AccountLegalEntityId, MaLegalEntityId, "test", "ABC", "Test", OrganisationType.CompaniesHouse, "test", DateTime.UtcNow)); AccountLegalEntity.Setup(x => x.CreateCohort(ProviderId, It.IsAny(), null, null, It.IsAny(), It.IsAny())) .Returns(NewCohort); @@ -858,26 +809,30 @@ public CohortDomainServiceTestFixture() TransferConnections = new List {new TransferConnectionViewModel {FundingEmployerAccountId = TransferSenderId}}; - DraftApprenticeshipId = fixture.Create(); DraftApprenticeshipDetails = new DraftApprenticeshipDetails { - FirstName = "Test", LastName = "Test", DeliveryModel = DeliveryModel.Regular, IgnoreStartDateOverlap = false, IsOnFlexiPaymentPilot = false + FirstName = "Test", + LastName = "Test", + DeliveryModel = DeliveryModel.Regular, + IgnoreStartDateOverlap = false, + IsOnFlexiPaymentPilot = false }; - ExistingDraftApprenticeship = new DraftApprenticeship { - Id = DraftApprenticeshipId, - CommitmentId = CohortId, - FirstName = fixture.Create(), - LastName = fixture.Create(), - Uln = "4860364820", - StartDate = DateTime.UtcNow, - EndDate = DateTime.UtcNow.AddYears(1), - CourseCode = fixture.Create(), - Cost = fixture.Create(), - IsOnFlexiPaymentPilot = false + ExistingDraftApprenticeship = new DraftApprenticeship + { + Id = DraftApprenticeshipId, + CommitmentId = CohortId, + FirstName = fixture.Create(), + LastName = fixture.Create(), + Uln = "4860364820", + StartDate = DateTime.UtcNow, + EndDate = DateTime.UtcNow.AddYears(1), + CourseCode = fixture.Create(), + Cost = fixture.Create(), + IsOnFlexiPaymentPilot = false }; ExistingDraftApprenticeship.SetValue(x => x.DateOfBirth, ExistingDraftApprenticeship.StartDate.Value.AddYears(-16)); @@ -908,7 +863,7 @@ public CohortDomainServiceTestFixture() OverlapCheckService = new Mock(); OverlapCheckService.Setup(x => x.CheckForOverlaps(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new OverlapCheckResult(false,false)); + .ReturnsAsync(new OverlapCheckResult(false, false)); OverlapCheckService.Setup(x => x.CheckForEmailOverlaps(It.IsAny(), It.IsAny())) .ReturnsAsync(new List()); @@ -916,14 +871,14 @@ public CohortDomainServiceTestFixture() EncodingService = new Mock(); AuthenticationService = new Mock(); - + CurrentDateTime = new Mock(); CurrentDateTime.Setup(d => d.UtcNow).Returns(Now); AccountApiClient = new Mock(); AccountApiClient.Setup(x => x.GetTransferConnections(It.IsAny())) .ReturnsAsync(TransferConnections); - + EmailOptionalService = new Mock(); PriorLearning = fixture.Create(); @@ -951,9 +906,56 @@ public CohortDomainServiceTestFixture() Db.SaveChanges(); } + public DateTime Now { get; set; } + public CohortDomainService CohortDomainService { get; set; } + public ProviderCommitmentsDbContext Db { get; set; } + public long ProviderId { get; } + public long AccountId { get; } + public long TransferSenderId { get; } + public string TransferSenderName { get; } + public int? PledgeApplicationId { get; } + public long AccountLegalEntityId { get; } + public long CohortId { get; } + public Party RequestingParty { get; private set; } + public string AccountLegalEntityPublicHashedId { get; } + public long ChangeOfPartyRequestId { get; } + public DraftApprenticeshipDetails DraftApprenticeshipDetails { get; } + public DraftApprenticeship ExistingDraftApprenticeship { get; } + public Apprenticeship PreviousApprenticeship { get; } + public long DraftApprenticeshipId { get; } + public Mock ChangeOfPartyRequest { get; } + public Account EmployerAccount { get; set; } + public Account TransferSenderAccount { get; set; } + public Mock Provider { get; set; } + public Mock AccountLegalEntity { get; set; } + public Cohort Cohort { get; set; } + public Cohort NewCohort { get; set; } + public Mock AcademicYearDateProvider { get; } + public Mock UlnValidator { get; } + public Mock ReservationValidationService { get; } + public Mock EmployerAgreementService { get; } + public Mock EncodingService { get; } + public Party Party { get; set; } + public Mock AuthenticationService { get; } + public Mock CurrentDateTime { get; set; } + public Mock AccountApiClient { get; set; } + public Mock LevyTransferMatchingApiClient { get; set; } + public Mock> FeatureTogglesService { get; set; } + public PledgeApplication PledgeApplication { get; set; } + public List TransferConnections { get; } + public Exception Exception { get; private set; } + public List DomainErrors { get; } + public string Message { get; private set; } + public UserInfo UserInfo { get; private set; } + public LearnerVerificationResponse LearnerVerificationResponse { get; private set; } + public ApprenticeshipPriorLearning PriorLearning { get; private set; } + public long MaLegalEntityId { get; private set; } + private Mock OverlapCheckService { get; } + private Mock EmailOptionalService { get; } + public CohortDomainServiceTestFixture WithPriorLearning() { - ExistingDraftApprenticeship.SetValue(x=>x.RecognisePriorLearning, true); + ExistingDraftApprenticeship.SetValue(x => x.RecognisePriorLearning, true); ExistingDraftApprenticeship.SetPriorLearningDetails(10, 100); return this; } @@ -992,7 +994,7 @@ public CohortDomainServiceTestFixture WithNoMessage() return this; } - public CohortDomainServiceTestFixture WithTrainingProgramme(ProgrammeType programmeType = ProgrammeType.Standard) + public CohortDomainServiceTestFixture WithTrainingProgramme(ProgrammeType programmeType = ProgrammeType.Standard) { DraftApprenticeshipDetails.TrainingProgramme = new SFA.DAS.CommitmentsV2.Domain.Entities.TrainingProgramme("TEST", "TEST", @@ -1139,7 +1141,7 @@ public CohortDomainServiceTestFixture WithCohortMappedToProviderAndAccountLegalE Cohort = new Cohort { Id = CohortId, - WithParty = withParty, + WithParty = withParty, Originator = creatingParty.ToOriginator(), EditStatus = (withParty == Party.Employer || withParty == Party.Provider) ? withParty.ToEditStatus() : EditStatus.Both, Provider = Provider.Object, @@ -1150,8 +1152,8 @@ public CohortDomainServiceTestFixture WithCohortMappedToProviderAndAccountLegalE TransferSenderId = null, }; - var cohorts = new List {Cohort}; - + var cohorts = new List { Cohort }; + Provider.Setup(x => x.Cohorts).Returns(cohorts); AccountLegalEntity.Setup(x => x.Cohorts).Returns(cohorts); @@ -1217,6 +1219,7 @@ public CohortDomainServiceTestFixture WithChangeOfEmployerCohort(Party withParty return this; } + public CohortDomainServiceTestFixture WithExistingCohortApprovedByAllParties(Party creatingParty) { WithCohortMappedToProviderAndAccountLegalEntity(creatingParty, Party.None); @@ -1271,7 +1274,6 @@ public CohortDomainServiceTestFixture WithExistingDraftApprenticeshipWithPriorLe return this; } - public CohortDomainServiceTestFixture WithOverlappingEmails() { var f = new Fixture(); @@ -1280,7 +1282,7 @@ public CohortDomainServiceTestFixture WithOverlappingEmails() .ReturnsAsync(list); return this; } - + public void VerifyCheckForEmailOverlapsOnCohortIsCalledCorrectlyWhenApproving() { OverlapCheckService.Verify(x => x.CheckForEmailOverlaps(CohortId, It.IsAny())); @@ -1296,7 +1298,7 @@ public CohortDomainServiceTestFixture WithContinuation(bool overlap) DraftApprenticeshipDetails.DateOfBirth = ExistingDraftApprenticeship.DateOfBirth; DraftApprenticeshipDetails.Uln = ExistingDraftApprenticeship.Uln; DraftApprenticeshipDetails.StartDate = ExistingDraftApprenticeship.StartDate; - DraftApprenticeshipDetails.TrainingProgramme = new SFA.DAS.CommitmentsV2.Domain.Entities.TrainingProgramme(ExistingDraftApprenticeship.CourseCode, "", ProgrammeType.Framework, Now,Now); + DraftApprenticeshipDetails.TrainingProgramme = new SFA.DAS.CommitmentsV2.Domain.Entities.TrainingProgramme(ExistingDraftApprenticeship.CourseCode, "", ProgrammeType.Framework, Now, Now); if (overlap) { @@ -1339,7 +1341,7 @@ public CohortDomainServiceTestFixture WithDecodeOfPublicHashedAccountLegalEntity public CohortDomainServiceTestFixture WithAgreementSignedAs(bool signed) { - EmployerAgreementService.Setup(x => x.IsAgreementSigned(It.IsAny(), It.IsAny(), + EmployerAgreementService.Setup(x => x.IsAgreementSigned(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(signed); return this; } @@ -1357,19 +1359,19 @@ public async Task CreateCohort(long? accountId = null, long? accountLega DomainErrors.Clear(); accountId = accountId ?? AccountId; - accountLegalEntityId = accountLegalEntityId ?? AccountLegalEntityId; + accountLegalEntityId = accountLegalEntityId ?? AccountLegalEntityId; try { var result = await CohortDomainService.CreateCohort(ProviderId, accountId.Value, accountLegalEntityId.Value, transferSenderId, pledgeApplicationId, - DraftApprenticeshipDetails, UserInfo, RequestingParty, new CancellationToken()); + DraftApprenticeshipDetails, UserInfo, RequestingParty, LearnerVerificationResponse, new CancellationToken()); await Db.SaveChangesAsync(); return result; } catch (DomainException ex) { DomainErrors.AddRange(ex.DomainErrors); - if(Db.Cohorts.Contains(Cohort)) {Db.Cohorts.Remove(Cohort);} + if (Db.Cohorts.Contains(Cohort)) { Db.Cohorts.Remove(Cohort); } return null; } catch (Exception ex) @@ -1435,7 +1437,7 @@ public async Task AddDraftApprenticeship(bool ignoreStartDateOverlap = false) try { - await CohortDomainService.AddDraftApprenticeship(ProviderId, CohortId, DraftApprenticeshipDetails, UserInfo, RequestingParty, new CancellationToken()); + await CohortDomainService.AddDraftApprenticeship(ProviderId, CohortId, DraftApprenticeshipDetails, UserInfo, RequestingParty, LearnerVerificationResponse, new CancellationToken()); await Db.SaveChangesAsync(); } catch (DomainException ex) @@ -1498,7 +1500,7 @@ public async Task UpdateDraftApprenticeship(bool ignoreStartDateOverlap = false) try { - await CohortDomainService.UpdateDraftApprenticeship(CohortId, DraftApprenticeshipDetails, UserInfo, RequestingParty, new CancellationToken()); + await CohortDomainService.UpdateDraftApprenticeship(CohortId, DraftApprenticeshipDetails, UserInfo, RequestingParty, LearnerVerificationResponse, new CancellationToken()); await Db.SaveChangesAsync(); } catch (DomainException ex) @@ -1528,7 +1530,7 @@ public void VerifyCohortCreation(Party party) { if (party == Party.Provider) { - Provider.Verify(x => x.CreateCohort(ProviderId, It.Is(p=>p == AccountLegalEntity.Object), null, null, + Provider.Verify(x => x.CreateCohort(ProviderId, It.Is(p => p == AccountLegalEntity.Object), null, null, DraftApprenticeshipDetails, UserInfo)); } @@ -1608,7 +1610,7 @@ public void VerifyProviderDraftApprenticeshipAdded() public void VerifyDraftApprenticeshipUpdated() { - var updated = Cohort.DraftApprenticeships.SingleOrDefault(x=>x.Id == DraftApprenticeshipId); + var updated = Cohort.DraftApprenticeships.SingleOrDefault(x => x.Id == DraftApprenticeshipId); Assert.IsNotNull(updated, "No draft apprenticeship record found"); Assert.AreEqual(updated.FirstName, DraftApprenticeshipDetails.FirstName); @@ -1670,7 +1672,6 @@ public void VerifyPriorLearningIsSetToNewRPLValues() updated.PriorLearning?.WeightageReducedBy == DraftApprenticeshipDetails.WeightageReducedBy && updated.PriorLearning?.QualificationsForRplReduction == DraftApprenticeshipDetails.QualificationsForRplReduction && updated.PriorLearning?.ReasonForRplReduction == DraftApprenticeshipDetails.ReasonForRplReduction); - } public void VerifyLastUpdatedFieldsAreSet(Party withParty) @@ -1681,10 +1682,12 @@ public void VerifyLastUpdatedFieldsAreSet(Party withParty) Assert.AreEqual(Cohort.LastUpdatedByEmployerName, UserInfo.UserDisplayName); Assert.AreEqual(Cohort.LastUpdatedByEmployerEmail, UserInfo.UserEmail); break; + case Party.Provider: Assert.AreEqual(Cohort.LastUpdatedByProviderName, UserInfo.UserDisplayName); Assert.AreEqual(Cohort.LastUpdatedByProviderEmail, UserInfo.UserEmail); break; + default: Assert.Fail("Party must be provider or Employer"); break; @@ -1698,6 +1701,7 @@ public void VerifyLastUpdatedFieldsAreNotSet() Assert.IsNull(Cohort.LastUpdatedByProviderName); Assert.IsNull(Cohort.LastUpdatedByProviderEmail); } + public void VerifyStartDateException(bool passes) { if (passes) @@ -1819,7 +1823,7 @@ public void VerifyNoException() public void VerifyIsAgreementSignedIsCalledCorrectly() { - EmployerAgreementService.Verify(x => x.IsAgreementSigned(AccountId, MaLegalEntityId, + EmployerAgreementService.Verify(x => x.IsAgreementSigned(AccountId, MaLegalEntityId, It.IsAny())); } diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/AddDraftApprenticeshipRequestValidatorTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/AddDraftApprenticeshipRequestValidatorTests.cs index 5e5428bd2a..c66f1dd830 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/AddDraftApprenticeshipRequestValidatorTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/AddDraftApprenticeshipRequestValidatorTests.cs @@ -31,7 +31,6 @@ public void Validate_ProviderId_ShouldBeValidated(long value, bool expectedValid AssertValidationResult(request => request.ProviderId, value, expectedValid); } - [TestCase(false, false)] [TestCase(true, true)] public void Validate_ReservationId_ShouldBeValidated(bool hasValue, bool expectedValid) @@ -83,6 +82,12 @@ public void Validate_IsOnFlexiPaymentPilotIsNull_ShouldBeInvalid() AssertValidationResult(request => request.IsOnFlexiPaymentPilot, (bool?)null, false); } + [Test] + public void Validate_LearnerVerificationResponseIsNull_ShouldBeInvalid() + { + AssertValidationResult(request => request.LearnerVerificationResponse, null, false); + } + private void AssertValidationResult(Expression> property, T value, bool expectedValid) { // Arrange @@ -103,10 +108,10 @@ private void AssertValidationResult(Expression(); - + authorizationService.Setup(a => a.IsAuthorized(It.IsAny())) .Returns(o => feature(o.SingleOrDefault())); - + var validator = new AddDraftApprenticeshipRequestValidator(authorizationService.Object); // Act diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/CreateCohortRequestValidatorTests.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/CreateCohortRequestValidatorTests.cs index aaca5f9e9a..37bdadf464 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/CreateCohortRequestValidatorTests.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2.UnitTests/Validators/CreateCohortRequestValidatorTests.cs @@ -90,6 +90,12 @@ public void Validate_IsOnFlexiPaymentPilotIsNull_ShouldBeInvalid() AssertValidationResult(request => request.IsOnFlexiPaymentPilot, (bool?)null, false); } + [Test] + public void Validate_LearnerVerificationResponseIsNull_ShouldBeInvalid() + { + AssertValidationResult(request => request.LearnerVerificationResponse, null, false); + } + private void AssertValidationResult(Expression> property, T value, bool expectedValid) { // Arrange @@ -110,10 +116,10 @@ private void AssertValidationResult(Expression> { // Arrange var authorizationService = new Mock(); - + authorizationService.Setup(a => a.IsAuthorized(It.IsAny())) .Returns(o => feature(o.SingleOrDefault())); - + var validator = new CreateCohortRequestValidator(authorizationService.Object); // Act @@ -127,4 +133,4 @@ private void AssertValidationResult(Expression> } } } -} +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortCommand.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortCommand.cs index 79bc4a5a17..359033775e 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortCommand.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortCommand.cs @@ -30,6 +30,7 @@ public class AddCohortCommand : IRequest public UserInfo UserInfo { get; } public bool IgnoreStartDateOverlap { get; set; } public bool? IsOnFlexiPaymentPilot { get; set; } + public LearnerVerificationResponse LearnerVerificationResponse { get; set; } public AddCohortCommand(Party? requestingParty, long accountId, long accountLegalEntityId, long providerId, string courseCode, DeliveryModel? deliveryModel, int? cost, DateTime? startDate, DateTime? actualStartDate, DateTime? endDate, @@ -37,7 +38,7 @@ public AddCohortCommand(Party? requestingParty, long accountId, long accountLega string lastName, string email, DateTime? dateOfBirth, string uln, long? transferSenderId, int? pledgeApplicationId, int? employmentPrice, DateTime? employmentEndDate, UserInfo userInfo, bool ignoreStartDateOverlap, - bool? isOnFlexiPaymentPilot) + bool? isOnFlexiPaymentPilot, LearnerVerificationResponse learnerVerificationResponse) { RequestingParty = requestingParty; AccountId = accountId; @@ -61,6 +62,7 @@ public AddCohortCommand(Party? requestingParty, long accountId, long accountLega EmploymentPrice = employmentPrice; EmploymentEndDate = employmentEndDate; IsOnFlexiPaymentPilot = isOnFlexiPaymentPilot; + LearnerVerificationResponse = learnerVerificationResponse; UserInfo = userInfo ?? throw new ArgumentNullException(nameof(userInfo)); IgnoreStartDateOverlap = ignoreStartDateOverlap; diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortHandler.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortHandler.cs index a8c398dc87..0462662ef7 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortHandler.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddCohort/AddCohortHandler.cs @@ -7,7 +7,6 @@ using SFA.DAS.CommitmentsV2.Data; using SFA.DAS.CommitmentsV2.Domain.Entities; using SFA.DAS.CommitmentsV2.Domain.Interfaces; - using SFA.DAS.CommitmentsV2.Mapping; using SFA.DAS.Encoding; @@ -42,7 +41,7 @@ public async Task Handle(AddCohortCommand command, Cancellation var draftApprenticeshipDetails = await _draftApprenticeshipDetailsMapper.Map(command); - var cohort = await _cohortDomainService.CreateCohort(command.ProviderId, + var cohort = await _cohortDomainService.CreateCohort(command.ProviderId, command.AccountId, command.AccountLegalEntityId, command.TransferSenderId, @@ -50,6 +49,7 @@ public async Task Handle(AddCohortCommand command, Cancellation draftApprenticeshipDetails, command.UserInfo, command.RequestingParty, + command.LearnerVerificationResponse, cancellationToken); db.Cohorts.Add(cohort); @@ -70,4 +70,4 @@ public async Task Handle(AddCohortCommand command, Cancellation return response; } } -} +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddDraftApprenticeship/AddDraftApprenticeshipCommand.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddDraftApprenticeship/AddDraftApprenticeshipCommand.cs index b4fbc76601..a89c92a848 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddDraftApprenticeship/AddDraftApprenticeshipCommand.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/AddDraftApprenticeship/AddDraftApprenticeshipCommand.cs @@ -28,6 +28,6 @@ public class AddDraftApprenticeshipCommand : IRequest Handle(AddDraftApprenticeshipCom { var db = _dbContext.Value; var draftApprenticeshipDetails = await _draftApprenticeshipDetailsMapper.Map(request); - var draftApprenticeship = await _cohortDomainService.AddDraftApprenticeship(request.ProviderId, request.CohortId, draftApprenticeshipDetails, request.UserInfo, request.RequestingParty, cancellationToken); - + var draftApprenticeship = await _cohortDomainService.AddDraftApprenticeship(request.ProviderId, request.CohortId, draftApprenticeshipDetails, request.UserInfo, request.RequestingParty, request.LearnerVerificationResponse, cancellationToken); await db.SaveChangesAsync(cancellationToken); - + _logger.LogInformation($"Added draft apprenticeship. Reservation-Id:{request.ReservationId} Commitment-Id:{request.CohortId} Apprenticeship-Id:{draftApprenticeship.Id}"); - + var response = new AddDraftApprenticeshipResult { Id = draftApprenticeship.Id diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/UpdateDraftApprenticeship/UpdateDraftApprenticeshipCommand.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/UpdateDraftApprenticeship/UpdateDraftApprenticeshipCommand.cs index 2b4b1cd88c..2a6117a785 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/UpdateDraftApprenticeship/UpdateDraftApprenticeshipCommand.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Application/Commands/UpdateDraftApprenticeship/UpdateDraftApprenticeshipCommand.cs @@ -28,5 +28,6 @@ public class UpdateDraftApprenticeshipCommand : IRequest Handle(UpdateDraftApprentic { var draftApprenticeshipDetails = await _draftApprenticeshipDetailsMapper.Map(command); - await _cohortDomainService.UpdateDraftApprenticeship(command.CohortId, draftApprenticeshipDetails, command.UserInfo, command.RequestingParty, cancellationToken); + await _cohortDomainService.UpdateDraftApprenticeship(command.CohortId, draftApprenticeshipDetails, command.UserInfo, command.RequestingParty, command.LearnerVerificationResponse, cancellationToken); _logger.LogInformation($"Saved cohort. Reservation-Id:{command.ReservationId} Commitment-Id:{command.CohortId} Apprenticeship:{command.ApprenticeshipId}"); @@ -44,4 +41,4 @@ public async Task Handle(UpdateDraftApprentic return response; } } -} +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Domain/Interfaces/ICohortDomainService.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Domain/Interfaces/ICohortDomainService.cs index ee3cb879b6..b312fbceb1 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Domain/Interfaces/ICohortDomainService.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Domain/Interfaces/ICohortDomainService.cs @@ -10,15 +10,24 @@ namespace SFA.DAS.CommitmentsV2.Domain.Interfaces { public interface ICohortDomainService { - Task AddDraftApprenticeship(long providerId, long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken); + Task AddDraftApprenticeship(long providerId, long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, LearnerVerificationResponse learnerVerificationResponse, CancellationToken cancellationToken); + Task ApproveCohort(long cohortId, string message, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken); - Task CreateCohort(long providerId, long accountId, long accountLegalEntityId, long? transferSenderId, int? pledgeApplicationId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken); + + Task CreateCohort(long providerId, long accountId, long accountLegalEntityId, long? transferSenderId, int? pledgeApplicationId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, LearnerVerificationResponse learnerVerificationResponse, CancellationToken cancellationToken); + Task CreateCohortWithOtherParty(long providerId, long accountId, long accountLegalEntityId, long? transferSenderId, int? pledgeApplicationId, string message, UserInfo userInfo, CancellationToken cancellationToken); + Task CreateEmptyCohort(long providerId, long accountId, long accountLegalEntityId, UserInfo userInfo, CancellationToken cancellationToken); + Task SendCohortToOtherParty(long cohortId, string message, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken); - Task UpdateDraftApprenticeship(long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken); + + Task UpdateDraftApprenticeship(long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, LearnerVerificationResponse learnerVerificationResponse, CancellationToken cancellationToken); + Task DeleteDraftApprenticeship(long cohortId, long apprenticeshipId, UserInfo userInfo, CancellationToken cancellationToken); + Task> AddDraftApprenticeships(List draftApprenticeships, List csvBulkUploadApprenticehips, long providerId, UserInfo userInfo, CancellationToken cancellationToken); + Task ValidateDraftApprenticeshipForOverlappingTrainingDateRequest(long providerId, long? cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/AddDraftApprenticeshipRequestToAddDraftApprenticeshipCommandMapper.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/AddDraftApprenticeshipRequestToAddDraftApprenticeshipCommandMapper.cs index e5a07abe9e..554bf75b12 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/AddDraftApprenticeshipRequestToAddDraftApprenticeshipCommandMapper.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/AddDraftApprenticeshipRequestToAddDraftApprenticeshipCommandMapper.cs @@ -30,7 +30,8 @@ public Task Map(AddDraftApprenticeshipRequest sou EmploymentPrice = source.EmploymentPrice, UserInfo = source.UserInfo, IgnoreStartDateOverlap = source.IgnoreStartDateOverlap, - IsOnFlexiPaymentPilot = source.IsOnFlexiPaymentPilot + IsOnFlexiPaymentPilot = source.IsOnFlexiPaymentPilot, + LearnerVerificationResponse = source.LearnerVerificationResponse }); } } diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapper.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapper.cs index 9384f27a06..63db192b11 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapper.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Mapping/RequestToCommandMappers/UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapper.cs @@ -6,7 +6,7 @@ namespace SFA.DAS.CommitmentsV2.Mapping.RequestToCommandMappers { public class UpdateDraftApprenticeshipRequestToUpdateDraftApprenticeshipCommandMapper : IOldMapper { - public Task Map(UpdateDraftApprenticeshipRequest source) + public Task Map(UpdateDraftApprenticeshipRequest source) { return Task.FromResult(new UpdateDraftApprenticeshipCommand { @@ -30,6 +30,7 @@ public Task Map(UpdateDraftApprenticeshipRequ UserInfo = source.UserInfo, IgnoreStartDateOverlap = source.IgnoreStartDateOverlap, IsOnFlexiPaymentPilot = source.IsOnFlexiPaymentPilot, + LearnerVerificationResponse = source.LearnerVerificationResponse }); } } diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Services/CohortDomainService.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Services/CohortDomainService.cs index 72a1ce88c9..09563c6be0 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Services/CohortDomainService.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Services/CohortDomainService.cs @@ -4,7 +4,6 @@ using SFA.DAS.CommitmentsV2.Authentication; using SFA.DAS.CommitmentsV2.Data; using SFA.DAS.CommitmentsV2.Data.Extensions; -using SFA.DAS.CommitmentsV2.Domain; using SFA.DAS.CommitmentsV2.Domain.Entities; using SFA.DAS.CommitmentsV2.Domain.Entities.Reservations; using SFA.DAS.CommitmentsV2.Domain.Exceptions; @@ -22,8 +21,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using SFA.DAS.Authorization.Features.Models; -using SFA.DAS.Authorization.Features.Services; namespace SFA.DAS.CommitmentsV2.Services { @@ -39,9 +36,10 @@ public class CohortDomainService : ICohortDomainService private readonly ICurrentDateTime _currentDateTime; private readonly IEmployerAgreementService _employerAgreementService; private readonly IEncodingService _encodingService; - private readonly IAccountApiClient _accountApiClient; + private readonly IAccountApiClient _accountApiClient; private readonly IEmailOptionalService _emailService; private readonly ILevyTransferMatchingApiClient _levyTransferMatchingApiClient; + private const string UlnValidationPropertyName = "Uln"; public CohortDomainService(Lazy dbContext, ILogger logger, @@ -53,7 +51,7 @@ public CohortDomainService(Lazy dbContext, ICurrentDateTime currentDateTime, IEmployerAgreementService employerAgreementService, IEncodingService encodingService, - IAccountApiClient accountApiClient, + IAccountApiClient accountApiClient, IEmailOptionalService emailOptionalService, ILevyTransferMatchingApiClient levyTransferMatchingApiClient) { @@ -72,13 +70,13 @@ public CohortDomainService(Lazy dbContext, _levyTransferMatchingApiClient = levyTransferMatchingApiClient; } - public async Task AddDraftApprenticeship(long providerId, long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken) + public async Task AddDraftApprenticeship(long providerId, long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, LearnerVerificationResponse learnerVerificationResponse, CancellationToken cancellationToken) { var db = _dbContext.Value; var cohort = await db.GetCohortAggregate(cohortId, cancellationToken); var party = requestingParty ?? _authenticationService.GetUserParty(); var draftApprenticeship = cohort.AddDraftApprenticeship(draftApprenticeshipDetails, party, userInfo); - await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, cohort.Id, cancellationToken); + await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, learnerVerificationResponse, cohort.Id, cancellationToken); return draftApprenticeship; } @@ -86,7 +84,7 @@ public async Task ValidateDraftApprenticeshipForOverlappingTrainingDateRequest(l { Cohort cohort = null; draftApprenticeshipDetails.IgnoreStartDateOverlap = true; - await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, cohortId, cancellationToken); + await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, null, cohortId, cancellationToken); if (cohortId.HasValue && cohortId.Value > 0) { cohort = await _dbContext.Value.GetCohortAggregate(cohortId.Value, cancellationToken: cancellationToken); @@ -148,7 +146,7 @@ public async Task> AddDraftApprenticeships(List x.Value).Union(newCohorts.Select(x => x.Value)); @@ -173,11 +171,11 @@ public async Task ApproveCohort(long cohortId, string message, UserInfo userInfo private async Task ValidateUlnOverlap(Cohort cohort) { - foreach (var draftApprenticeship in cohort.DraftApprenticeships) + foreach (var draftApprenticeship in cohort.DraftApprenticeships) { if (!string.IsNullOrEmpty(draftApprenticeship.Uln) && draftApprenticeship.StartDate.HasValue && draftApprenticeship.EndDate.HasValue) { - var result = await _overlapCheckService.CheckForOverlaps(draftApprenticeship.Uln, draftApprenticeship.StartDate.Value.To(draftApprenticeship.EndDate.Value), draftApprenticeship.Id, CancellationToken.None); + var result = await _overlapCheckService.CheckForOverlaps(draftApprenticeship.Uln, draftApprenticeship.StartDate.Value.To(draftApprenticeship.EndDate.Value), draftApprenticeship.Id, CancellationToken.None); if (result.HasOverlaps) { throw new DomainException(draftApprenticeship.Uln, "The draft apprenticeship has overlap"); @@ -186,7 +184,7 @@ private async Task ValidateUlnOverlap(Cohort cohort) } } - public async Task CreateCohort(long providerId, long accountId, long accountLegalEntityId, long? transferSenderId, int? pledgeApplicationId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken) + public async Task CreateCohort(long providerId, long accountId, long accountLegalEntityId, long? transferSenderId, int? pledgeApplicationId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, LearnerVerificationResponse learnerVerificationResponse, CancellationToken cancellationToken) { var originatingParty = requestingParty ?? _authenticationService.GetUserParty(); var db = _dbContext.Value; @@ -195,7 +193,7 @@ public async Task CreateCohort(long providerId, long accountId, long acc var transferSender = transferSenderId.HasValue ? await GetTransferSender(accountId, transferSenderId.Value, pledgeApplicationId, db, cancellationToken) : null; var originator = GetCohortOriginator(originatingParty, provider, accountLegalEntity); - await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, null, cancellationToken); + await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, learnerVerificationResponse, null, cancellationToken); return originator.CreateCohort(providerId, accountLegalEntity, transferSender, pledgeApplicationId, draftApprenticeshipDetails, userInfo); } @@ -243,10 +241,10 @@ public async Task SendCohortToOtherParty(long cohortId, string message, UserInfo cohort.SendToOtherParty(party, message, userInfo, _currentDateTime.UtcNow); } - public async Task UpdateDraftApprenticeship(long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, CancellationToken cancellationToken) + public async Task UpdateDraftApprenticeship(long cohortId, DraftApprenticeshipDetails draftApprenticeshipDetails, UserInfo userInfo, Party? requestingParty, LearnerVerificationResponse learnerVerificationResponse, CancellationToken cancellationToken) { var cohort = await _dbContext.Value.GetCohortAggregate(cohortId, cancellationToken: cancellationToken); - + AssertHasProvider(cohortId, cohort.ProviderId); AssertHasApprenticeshipId(cohortId, draftApprenticeshipDetails.Id); @@ -257,7 +255,7 @@ public async Task UpdateDraftApprenticeship(long cohortId, DraftApprenti await ValidateStartDateForContinuation(cohort, draftApprenticeshipDetails); } - await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, cohortId, cancellationToken); + await ValidateDraftApprenticeshipDetails(draftApprenticeshipDetails, learnerVerificationResponse, cohortId, cancellationToken); return cohort; } @@ -275,6 +273,43 @@ public async Task DeleteDraftApprenticeship(long cohortId, long apprenti return cohort; } + private static void AssertHasApprenticeshipId(long cohortId, long draftApprenticeshipDetailId) + { + if (draftApprenticeshipDetailId < 1) + { + throw new InvalidOperationException($"Cannot update cohort {cohortId} because the supplied draft apprenticeship does not have an id"); + } + } + + private static async Task GetAccountLegalEntity(long accountId, long accountLegalEntityId, ProviderCommitmentsDbContext db, CancellationToken cancellationToken) + { + var accountLegalEntity = + await db.AccountLegalEntities.SingleOrDefaultAsync(x => x.Id == accountLegalEntityId, + cancellationToken); + if (accountLegalEntity == null) + throw new BadRequestException($"AccountLegalEntity {accountLegalEntityId} was not found"); + if (accountLegalEntity.AccountId != accountId) + throw new BadRequestException($"AccountLegalEntity {accountLegalEntityId} does not belong to the Account {accountId}"); + + return accountLegalEntity; + } + + private static async Task GetAccount(long accountId, ProviderCommitmentsDbContext db, CancellationToken cancellationToken) + { + var account = await db.Accounts.SingleOrDefaultAsync(x => x.Id == accountId, cancellationToken); + if (account == null) + throw new BadRequestException($"Account {accountId} was not found"); + + return account; + } + + private static async Task GetProvider(long providerId, ProviderCommitmentsDbContext db, CancellationToken cancellationToken) + { + var provider = await db.Providers.SingleOrDefaultAsync(p => p.UkPrn == providerId, cancellationToken); + if (provider == null) throw new BadRequestException($"Provider {providerId} was not found"); + return provider; + } + // Will remove once it goes through testing //public async Task DeleteApprenticeshipConfirmationStatus(long apprenticeshipId) //{ @@ -293,8 +328,10 @@ private ICohortOriginator GetCohortOriginator(Party originatingParty, Provider p { case Party.Employer: return accountLegalEntity; + case Party.Provider: return provider; + default: throw new ArgumentException($"Unable to get ICohortOriginator from Party of type {originatingParty}"); } @@ -304,41 +341,12 @@ private void AssertHasProvider(long cohortId, long? providerId) { if (providerId == null) { - // We need a provider id to validate the apprenticeship with reservations, so a provider id is mandatory. + // We need a provider id to validate the apprenticeship with reservations, so a + // provider id is mandatory. throw new InvalidOperationException($"Cannot update cohort {cohortId} because it is not linked to a provider"); } } - private static void AssertHasApprenticeshipId(long cohortId, long draftApprenticeshipDetailId) - { - if (draftApprenticeshipDetailId < 1) - { - throw new InvalidOperationException($"Cannot update cohort {cohortId} because the supplied draft apprenticeship does not have an id"); - } - } - - private static async Task GetAccountLegalEntity(long accountId, long accountLegalEntityId, ProviderCommitmentsDbContext db, CancellationToken cancellationToken) - { - var accountLegalEntity = - await db.AccountLegalEntities.SingleOrDefaultAsync(x => x.Id == accountLegalEntityId, - cancellationToken); - if (accountLegalEntity == null) - throw new BadRequestException($"AccountLegalEntity {accountLegalEntityId} was not found"); - if (accountLegalEntity.AccountId != accountId) - throw new BadRequestException($"AccountLegalEntity {accountLegalEntityId} does not belong to the Account {accountId}"); - - return accountLegalEntity; - } - - private static async Task GetAccount(long accountId, ProviderCommitmentsDbContext db, CancellationToken cancellationToken) - { - var account = await db.Accounts.SingleOrDefaultAsync(x => x.Id == accountId, cancellationToken); - if (account == null) - throw new BadRequestException($"Account {accountId} was not found"); - - return account; - } - private async Task GetTransferSender(long employerAccountId, long transferSenderId, int? pledgeApplicationId, ProviderCommitmentsDbContext db, CancellationToken cancellationToken) { if (pledgeApplicationId.HasValue) @@ -349,11 +357,10 @@ private async Task GetTransferSender(long employerAccountId, long trans { await ValidateTransferSenderIdIsAFundingConnection(employerAccountId, transferSenderId); } - + return await GetAccount(transferSenderId, db, cancellationToken); } - private async Task ValidatePledgeApplicationId(long accountId, long transferSenderId, int pledgeApplicationId) { var pledgeApplication = await _levyTransferMatchingApiClient.GetPledgeApplication(pledgeApplicationId); @@ -390,13 +397,6 @@ private async Task ValidateTransferSenderIdIsAFundingConnection(long accountId, throw new BadRequestException($"TransferSenderId {transferSenderId} is not a FundingEmployer for Account {accountId}"); } - private static async Task GetProvider(long providerId, ProviderCommitmentsDbContext db, CancellationToken cancellationToken) - { - var provider = await db.Providers.SingleOrDefaultAsync(p => p.UkPrn == providerId, cancellationToken); - if (provider == null) throw new BadRequestException($"Provider {providerId} was not found"); - return provider; - } - private async Task ValidateStartDateForContinuation(Cohort cohort, DraftApprenticeshipDetails draftApprenticeshipDetails) { if (!draftApprenticeshipDetails.StartDate.HasValue) return; @@ -418,15 +418,44 @@ private async Task ValidateStartDateForContinuation(Cohort cohort, DraftApprenti } } - private async Task ValidateDraftApprenticeshipDetails(DraftApprenticeshipDetails draftApprenticeshipDetails, long? cohortId, CancellationToken cancellationToken) + private async Task ValidateDraftApprenticeshipDetails(DraftApprenticeshipDetails draftApprenticeshipDetails, LearnerVerificationResponse learnerVerificationResponse, long? cohortId, CancellationToken cancellationToken) { ValidateApprenticeshipDate(draftApprenticeshipDetails); ValidateUln(draftApprenticeshipDetails); + if (learnerVerificationResponse != null) { HandleLearnerVerificationResponse(learnerVerificationResponse); } await ValidateOverlaps(draftApprenticeshipDetails, cancellationToken); await ValidateEmailOverlaps(draftApprenticeshipDetails, cohortId, cancellationToken); await ValidateReservation(draftApprenticeshipDetails, cancellationToken); } + private void HandleLearnerVerificationResponse(LearnerVerificationResponse learnerVerificationResponse) + { + switch (learnerVerificationResponse.ResponseType) + { + case LearnerVerificationResponseType.SuccessfulMatch: + case LearnerVerificationResponseType.SuccessfulLinkedMatch: + break; + + case LearnerVerificationResponseType.SimilarMatch: + case LearnerVerificationResponseType.SimilarLinkedMatch: + // Note that in these cases, some or all of the fields (excluding unique learner + // number) are found to be similar to a learner on the Learner Record Service, + // but not an exact match + break; + + case LearnerVerificationResponseType.LearnerDoesNotMatch: + // Note that in this case, some or all of the fields (excluding unique learner + // number) did not successfully match any learners on the Learner Record Service + // Further detials can be found in learnerValidationResponseCode.FailureFlags + break; + + case LearnerVerificationResponseType.UlnNotFound: + throw new DomainException(UlnValidationPropertyName, "Unique learner number does not match any learners on the Learner Record Service."); + default: + break; + } + } + private void ValidateUln(DraftApprenticeshipDetails draftApprenticeshipDetails) { if (string.IsNullOrWhiteSpace(draftApprenticeshipDetails.Uln)) return; @@ -477,7 +506,7 @@ private async Task ValidateReservation(DraftApprenticeshipDetails details, Cance var errors = validationResult.ValidationErrors.Select(error => new DomainError(error.PropertyName, error.Reason)).ToList(); errors.ThrowIfAny(); } - + private async Task ValidateOverlaps(DraftApprenticeshipDetails details, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(details.Uln) || !details.StartDate.HasValue || !details.EndDate.HasValue) return; @@ -546,6 +575,7 @@ async Task GetMaLegalEntityId() throw new DomainException(nameof(cohort.EmployerAccountId), $"Employer {cohort.EmployerAccountId} cannot approve any cohort because the agreement is not signed"); } } + private async Task ValidateNoEmailOverlapsExist(Cohort cohort, CancellationToken cancellationToken) { var emailOverlaps = await _overlapCheckService.CheckForEmailOverlaps(cohort.Id, cancellationToken); @@ -556,4 +586,4 @@ private async Task ValidateNoEmailOverlapsExist(Cohort cohort, CancellationToken } } } -} +} \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/AddDraftApprenticeshipRequestValidator.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/AddDraftApprenticeshipRequestValidator.cs index 3a495a4104..90343536c7 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/AddDraftApprenticeshipRequestValidator.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/AddDraftApprenticeshipRequestValidator.cs @@ -8,15 +8,16 @@ public class AddDraftApprenticeshipRequestValidator : AbstractValidator r.UserId).NotEmpty().WithMessage("The user id must be supplied"); + RuleFor(r => r.UserId).NotEmpty().WithMessage("The user id must be supplied"); RuleFor(r => r.ProviderId).Must(p => p > 0).WithMessage("The provider id must be positive"); RuleFor(r => r.FirstName).MaximumLength(100).WithMessage("You must enter a first name that's no longer than 100 characters"); - RuleFor(r => r.LastName).MaximumLength(100).WithMessage("You must enter a last name that's no longer than 100 characters"); + RuleFor(r => r.LastName).MaximumLength(100).WithMessage("You must enter a last name that's no longer than 100 characters"); RuleFor(r => r.OriginatorReference).MaximumLength(20).WithMessage("The Reference must be 20 characters or fewer"); RuleFor(r => r.ReservationId).NotEmpty().WithMessage("The reservation id must be supplied"); RuleFor(r => r.UserInfo).SetValidator(new UserInfoValidator()).When(r => r.UserInfo != null); RuleFor(model => (int)model.DeliveryModel).InclusiveBetween(0, 2).WithMessage("Delivery Model can only be between 0 and 2").When(model => model.DeliveryModel.HasValue); RuleFor(r => r.IsOnFlexiPaymentPilot).NotNull().WithMessage("Select whether this apprentice will be on the pilot programme."); + RuleFor(r => r.LearnerVerificationResponse).NotNull().WithMessage("The learner details were not successfully verified."); } } } \ No newline at end of file diff --git a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/CreateCohortRequestValidator.cs b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/CreateCohortRequestValidator.cs index bf00d3e109..27479c0914 100644 --- a/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/CreateCohortRequestValidator.cs +++ b/src/CommitmentsV2/SFA.DAS.CommitmentsV2/Validators/CreateCohortRequestValidator.cs @@ -9,15 +9,16 @@ public class CreateCohortRequestValidator : AbstractValidator r.AccountId).Must(accountId => accountId > 0).WithMessage("The Account Id valid"); - RuleFor(r => r.AccountLegalEntityId).Must(accountLegalEntityId => accountLegalEntityId > 0).WithMessage("The Account Legal Entity must be valid"); + RuleFor(r => r.AccountLegalEntityId).Must(accountLegalEntityId => accountLegalEntityId > 0).WithMessage("The Account Legal Entity must be valid"); RuleFor(r => r.ProviderId).Must(providerId => providerId > 0).WithMessage("The provider id must be positive"); RuleFor(r => r.FirstName).MaximumLength(100).WithMessage("You must enter a first name that's no longer than 100 characters"); - RuleFor(r => r.LastName).MaximumLength(100).WithMessage("You must enter a last name that's no longer than 100 characters"); + RuleFor(r => r.LastName).MaximumLength(100).WithMessage("You must enter a last name that's no longer than 100 characters"); RuleFor(r => r.OriginatorReference).MaximumLength(20).WithMessage("The Reference must be 20 characters or fewer"); RuleFor(r => r.ReservationId).NotEmpty().WithMessage("The reservation id must be supplied"); RuleFor(r => r.UserInfo).SetValidator(new UserInfoValidator()).When(r => r.UserInfo != null); RuleFor(model => (int)model.DeliveryModel).InclusiveBetween(0, 2).WithMessage("Delivery Model can only be between 0 and 2").When(model => model.DeliveryModel.HasValue); RuleFor(r => r.IsOnFlexiPaymentPilot).NotNull().WithMessage("Select whether this apprentice will be on the pilot programme."); + RuleFor(r => r.LearnerVerificationResponse).NotNull().WithMessage("The learner details were not successfully verified."); } } -} +} \ No newline at end of file