public async Task Builds_an_add_event_with_status( PriceEpisodeStatusChangeBuilder sut, [Frozen] Mock <IApprenticeshipRepository> repository, PayableEarningEvent dataLock, List <EarningPeriod> periods, List <DataLockFailure> dataLockFailures, List <ApprenticeshipModel> apprenticeships) { CommonTestSetup(repository, dataLock, periods, apprenticeships, dataLockFailures); var priceEpisode = dataLock.PriceEpisodes[0]; var result = await sut.Build( new List <DataLockEvent> { dataLock }, new List <(string identifier, PriceEpisodeStatus status)> { (priceEpisode.Identifier, PriceEpisodeStatus.Updated) }, new List <PriceEpisodeStatusChange>(), dataLock.CollectionPeriod.AcademicYear); result.Should().ContainEquivalentOf(new { DataLock = new { PriceEpisodeIdentifier = priceEpisode.Identifier, Status = PriceEpisodeStatus.Updated, } }); }
public async Task Builds_an_add_event_with_no_errors( [Frozen] Mock <IApprenticeshipRepository> repository, PriceEpisodeStatusChangeBuilder sut, PayableEarningEvent dataLock, List <EarningPeriod> periods, List <ApprenticeshipModel> apprenticeships) { CommonTestSetup(repository, dataLock, periods, apprenticeships); var priceEpisode = dataLock.PriceEpisodes[0]; priceEpisode.TotalNegotiatedPrice3 = 1; var result = await sut.Build( new List <DataLockEvent> { dataLock }, new List <(string identifier, PriceEpisodeStatus status)> { (priceEpisode.Identifier, PriceEpisodeStatus.New) }, new List <PriceEpisodeStatusChange>(), dataLock.CollectionPeriod.AcademicYear); result.Should().ContainEquivalentOf(new { DataLock = new { PriceEpisodeIdentifier = priceEpisode.Identifier }, Errors = new LegacyDataLockEventError[0] }); }
public async Task OnprogPayableDataLockEventWithNoApprenticeshipIdShouldNotBeStored( IReceivedDataLockEventStore receivedDataLockEventStore, PayableEarningEvent payableEarningEvent, EarningPeriod period, ApprenticeshipModel apprenticeship, List <DataLockFailure> dataLockFailures, ManageReceivedDataLockEvent manageReceivedDataLockEvent) { period.ApprenticeshipId = default(long?); period.AccountId = default(long?); payableEarningEvent.OnProgrammeEarnings = new List <OnProgrammeEarning> { new OnProgrammeEarning { Periods = (new List <EarningPeriod> { period }).AsReadOnly(), Type = OnProgrammeEarningType.Learning, CensusDate = DateTime.UtcNow } }; await manageReceivedDataLockEvent.ProcessDataLockEvent(payableEarningEvent); var result = (await receivedDataLockEventStore .GetDataLocks(payableEarningEvent.JobId, payableEarningEvent.Ukprn)).ToList(); result.Should().BeEmpty(); }
public void ContractTypeIsCorrectForPayableEarningEvent(Type requiredPaymentEventType) { var requiredPaymentEvent = Activator.CreateInstance(requiredPaymentEventType) as PeriodisedRequiredPaymentEvent; var earningEvent = new PayableEarningEvent { PriceEpisodes = new List <PriceEpisode>(), LearningAim = new LearningAim() }; var actual = mapper.Map(earningEvent, requiredPaymentEvent); actual.ContractType.Should().Be(ContractType.Act1); }
public async Task Builds_an_add_event_with_data_from_datalock( [Frozen] Mock <IApprenticeshipRepository> repository, PriceEpisodeStatusChangeBuilder sut, PayableEarningEvent dataLock, List <EarningPeriod> periods, List <DataLockFailure> dataLockFailures, List <ApprenticeshipModel> apprenticeships) { CommonTestSetup(repository, dataLock, periods, apprenticeships, dataLockFailures); var priceEpisode = dataLock.PriceEpisodes[0]; dataLock.IlrFileName = "bob"; var result = await sut.Build( new List <DataLockEvent> { dataLock }, new List <(string identifier, PriceEpisodeStatus status)> { (priceEpisode.Identifier, PriceEpisodeStatus.New) }, new List <PriceEpisodeStatusChange>(), dataLock.CollectionPeriod.AcademicYear); result.Should().ContainEquivalentOf(new { DataLock = new { PriceEpisodeIdentifier = priceEpisode.Identifier, AcademicYear = dataLock.CollectionPeriod.AcademicYear.ToString(), UKPRN = dataLock.Ukprn, EventSource = 1, HasErrors = false, ULN = dataLock.Learner.Uln, LearnRefNumber = dataLock.Learner.ReferenceNumber, IlrFrameworkCode = dataLock.LearningAim.FrameworkCode, IlrPathwayCode = dataLock.LearningAim.PathwayCode, IlrProgrammeType = dataLock.LearningAim.ProgrammeType, IlrStandardCode = dataLock.LearningAim.StandardCode, SubmittedDateTime = dataLock.IlrSubmissionDateTime, AimSeqNumber = dataLock.LearningAim.SequenceNumber, IlrPriceEffectiveFromDate = priceEpisode.EffectiveTotalNegotiatedPriceStartDate, IlrPriceEffectiveToDate = priceEpisode.ActualEndDate.GetValueOrDefault(priceEpisode.PlannedEndDate), IlrFileName = "bob", IlrStartDate = priceEpisode.CourseStartDate, } }); }
public async Task StoreOnprogPayableDataLockEvents( IReceivedDataLockEventStore receivedDataLockEventStore, PayableEarningEvent payableEarningEvent, List <EarningPeriod> periods, ApprenticeshipModel apprenticeship, ManageReceivedDataLockEvent manageReceivedDataLockEvent) { CommonTestSetup(payableEarningEvent, periods, apprenticeship); await manageReceivedDataLockEvent.ProcessDataLockEvent(payableEarningEvent); var result = (await receivedDataLockEventStore .GetDataLocks(payableEarningEvent.JobId, payableEarningEvent.Ukprn)).ToList(); result.Should().NotBeNull(); result.Should().HaveCount(1); result[0].MessageType.Should().Be(typeof(PayableEarningEvent).AssemblyQualifiedName); result[0].Message.Should().Be(payableEarningEvent.ToJson()); }
public void TestDataLockEventToDataLockStatusChangedEventMap() { var dataLockEvent = new PayableEarningEvent { Learner = new Learner { Uln = 1, ReferenceNumber = "2" }, LearningAim = new LearningAim { FrameworkCode = 3 }, AgreementId = "4", CollectionPeriod = new CollectionPeriod { AcademicYear = 1, Period = 2 }, EventId = Guid.NewGuid() }; var changedToFailEvent = new DataLockStatusChangedToFailed { TransactionTypesAndPeriods = new Dictionary <TransactionType, List <EarningPeriod> > { { TransactionType.Balancing, new List <EarningPeriod> { new EarningPeriod { Period = 1 }, new EarningPeriod { Period = 2 } } } }, EventId = Guid.NewGuid() }; Mapper.Map(dataLockEvent, changedToFailEvent); changedToFailEvent.Learner.Should().NotBeNull(); changedToFailEvent.Learner.Uln.Should().Be(1); changedToFailEvent.LearningAim.Should().NotBeNull(); changedToFailEvent.LearningAim.FrameworkCode.Should().Be(3); changedToFailEvent.CollectionPeriod.Should().NotBeNull(); changedToFailEvent.CollectionPeriod.AcademicYear.Should().Be(1); changedToFailEvent.EventId.Should().Be(changedToFailEvent.EventId); changedToFailEvent.TransactionTypesAndPeriods.Should().NotBeNull(); changedToFailEvent.TransactionTypesAndPeriods.Should().NotBeEmpty(); }
public async Task TestNormalEvent() { // arrange var period = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2); var earningEvent = new PayableEarningEvent { Ukprn = 1, CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), CollectionYear = period.AcademicYear, Learner = EarningEventDataHelper.CreateLearner(), LearningAim = EarningEventDataHelper.CreateLearningAim(), OnProgrammeEarnings = new List <OnProgrammeEarning>() { new OnProgrammeEarning { Type = OnProgrammeEarningType.Learning, Periods = new ReadOnlyCollection <EarningPeriod>(new List <EarningPeriod>() { new EarningPeriod { Amount = 100, Period = period.Period, PriceEpisodeIdentifier = "2", SfaContributionPercentage = 0.9m, }, new EarningPeriod { Amount = 200, Period = (byte)(period.Period + 1), PriceEpisodeIdentifier = "2", SfaContributionPercentage = 0.9m, } }) } }, PriceEpisodes = new List <PriceEpisode> { new PriceEpisode { LearningAimSequenceNumber = 2, Identifier = "2" } } }; var requiredPayments = new List <RequiredPayment> { new RequiredPayment { Amount = 100, EarningType = EarningType.Levy, }, }; var paymentHistoryEntities = new[] { new PaymentHistoryEntity { CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), DeliveryPeriod = 2, LearnAimReference = earningEvent.LearningAim.Reference, TransactionType = (int)OnProgrammeEarningType.Learning } }; paymentHistoryCacheMock.Setup(c => c.TryGet(It.Is <string>(key => key == CacheKeys.PaymentHistoryKey), It.IsAny <CancellationToken>())) .ReturnsAsync(new ConditionalValue <PaymentHistoryEntity[]>(true, paymentHistoryEntities)) .Verifiable(); requiredPaymentsService.Setup(p => p.GetRequiredPayments(It.IsAny <Earning>(), It.IsAny <List <Payment> >())) .Returns(requiredPayments) .Verifiable(); // act var actualRequiredPayment = await processor.HandleEarningEvent(earningEvent, paymentHistoryCacheMock.Object, CancellationToken.None); // assert actualRequiredPayment.Should().HaveCount(1); actualRequiredPayment.First().LearningAimSequenceNumber.Should().Be(2); }
public async Task TestGeneratesValidRequiredEventFundingLineType() { // arrange var period = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2); var earningEvent = new PayableEarningEvent { Ukprn = 1, CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), CollectionYear = period.AcademicYear, Learner = EarningEventDataHelper.CreateLearner(), LearningAim = EarningEventDataHelper.CreateLearningAim(), OnProgrammeEarnings = new List <OnProgrammeEarning>() { new OnProgrammeEarning { Type = OnProgrammeEarningType.Learning, Periods = new ReadOnlyCollection <EarningPeriod>(new List <EarningPeriod>() { new EarningPeriod { Amount = 100, Period = period.Period, PriceEpisodeIdentifier = "1", SfaContributionPercentage = 0.9m, }, new EarningPeriod { Amount = 200, Period = (byte)(period.Period + 1), PriceEpisodeIdentifier = "2", SfaContributionPercentage = 0.9m, } }) } }, PriceEpisodes = new List <PriceEpisode> { new PriceEpisode { Identifier = "1", EffectiveTotalNegotiatedPriceStartDate = DateTime.UtcNow, PlannedEndDate = DateTime.UtcNow, ActualEndDate = DateTime.UtcNow, CompletionAmount = 100M, InstalmentAmount = 200M, NumberOfInstalments = 16, FundingLineType = "19+ Apprenticeship Non-Levy Contract (procured)" } } }; var requiredPayments = new List <RequiredPayment> { new RequiredPayment { Amount = 100, EarningType = EarningType.Levy, }, }; var paymentHistoryEntities = new[] { new PaymentHistoryEntity { CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), DeliveryPeriod = 2, LearnAimReference = earningEvent.LearningAim.Reference, TransactionType = (int)OnProgrammeEarningType.Learning } }; paymentHistoryCacheMock.Setup(c => c.TryGet(It.Is <string>(key => key == CacheKeys.PaymentHistoryKey), It.IsAny <CancellationToken>())) .ReturnsAsync(new ConditionalValue <PaymentHistoryEntity[]>(true, paymentHistoryEntities)) .Verifiable(); requiredPaymentsService.Setup(p => p.GetRequiredPayments(It.IsAny <Earning>(), It.IsAny <List <Payment> >())) .Returns(requiredPayments) .Verifiable(); // act var actualRequiredPayment = await processor.HandleEarningEvent(earningEvent, paymentHistoryCacheMock.Object, CancellationToken.None); // assert actualRequiredPayment.Should().HaveCount(1); actualRequiredPayment[0].LearningAim.Should().NotBeNull(); actualRequiredPayment[0].LearningAim.FundingLineType.Should().Be(earningEvent.PriceEpisodes[0].FundingLineType); }
public async Task When_HoldingBackCompletionPayment_Then_Log_a_Custom_Event() { // arrange var period = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2); var priceEpisodes = new List <PriceEpisode>(new [] { new PriceEpisode { Identifier = "1", EmployerContribution = 100, CompletionHoldBackExemptionCode = 0 }, new PriceEpisode { Identifier = "2", EmployerContribution = 1, CompletionHoldBackExemptionCode = 2 } }); var earningEvent = new PayableEarningEvent { Ukprn = 1, CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), CollectionYear = period.AcademicYear, Learner = EarningEventDataHelper.CreateLearner(), LearningAim = EarningEventDataHelper.CreateLearningAim(), PriceEpisodes = priceEpisodes, OnProgrammeEarnings = new List <OnProgrammeEarning> { new OnProgrammeEarning { Type = OnProgrammeEarningType.Completion, Periods = new ReadOnlyCollection <EarningPeriod>(new List <EarningPeriod> { new EarningPeriod { Amount = 200, Period = period.Period, PriceEpisodeIdentifier = "2", SfaContributionPercentage = 0.9m, } }) } } }; var requiredPayments = new List <RequiredPayment> { new RequiredPayment { Amount = 100, EarningType = EarningType.Levy, }, }; var paymentHistoryEntities = new[] { new PaymentHistoryEntity { CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), DeliveryPeriod = 2, LearnAimReference = "ZPROG001" } }; var key = new ApprenticeshipKey(); paymentHistoryCacheMock.Setup(c => c.TryGet(It.Is <string>(k => k == CacheKeys.PaymentHistoryKey), It.IsAny <CancellationToken>())) .ReturnsAsync(new ConditionalValue <PaymentHistoryEntity[]>(true, paymentHistoryEntities)) .Verifiable(); requiredPaymentsService.Setup(p => p.GetRequiredPayments(It.IsAny <Earning>(), It.IsAny <List <Payment> >())).Returns(requiredPayments).Verifiable(); apprenticeshipKeyProviderMock.Setup(a => a.GetCurrentKey()).Returns(key).Verifiable(); paymentHistoryRepositoryMock.Setup(repo => repo.GetEmployerCoInvestedPaymentHistoryTotal(key, It.IsAny <CancellationToken>())).ReturnsAsync(11).Verifiable(); holdingBackCompletionPaymentServiceMock.Setup(h => h.ShouldHoldBackCompletionPayment(11, priceEpisodes[1])).Returns(true).Verifiable(); // act var actualRequiredPayment = await processor.HandleEarningEvent(earningEvent, paymentHistoryCacheMock.Object, CancellationToken.None); // assert actualRequiredPayment.Should().HaveCount(1); mocker.Mock <ITelemetry>().Verify(t => t.TrackEvent("Holding Back Completion Payment", It.Is <Dictionary <string, string> >(d => d.Contains(new KeyValuePair <string, string>("Expected Employer Contribution", "2")) && d.Contains(new KeyValuePair <string, string>("Reported Employer Contribution", "1"))), It.IsAny <Dictionary <string, double> >())); }
public async Task Matching_Historic_Aim_To_Current_Aim_Should_Be_Case_InSensitive_On_Aim_Reference() { // arrange var period = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2); var earningEvent = new PayableEarningEvent { Ukprn = 1, CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), CollectionYear = period.AcademicYear, Learner = EarningEventDataHelper.CreateLearner(), LearningAim = EarningEventDataHelper.CreateLearningAim(), OnProgrammeEarnings = new List <OnProgrammeEarning>() { new OnProgrammeEarning { Type = OnProgrammeEarningType.Learning, Periods = new ReadOnlyCollection <EarningPeriod>(new List <EarningPeriod>() { new EarningPeriod { Amount = 100, Period = period.Period, PriceEpisodeIdentifier = "2", SfaContributionPercentage = 0.9m, } }) } } }; var requiredPayments = new List <RequiredPayment> { }; earningEvent.LearningAim.Reference = "ZPROG001"; var paymentHistoryEntities = new[] { new PaymentHistoryEntity { CollectionPeriod = CollectionPeriodFactory.CreateFromAcademicYearAndPeriod(1819, 2), DeliveryPeriod = period.Period, LearnAimReference = earningEvent.LearningAim.Reference.ToLower(), TransactionType = (int)OnProgrammeEarningType.Learning, Amount = 100, SfaContributionPercentage = 0.9m, PriceEpisodeIdentifier = "2" } }; paymentHistoryCacheMock.Setup(c => c.TryGet(It.Is <string>(key => key == CacheKeys.PaymentHistoryKey), It.IsAny <CancellationToken>())) .ReturnsAsync(new ConditionalValue <PaymentHistoryEntity[]>(true, paymentHistoryEntities)) .Verifiable(); requiredPaymentsService.Setup(p => p.GetRequiredPayments(It.IsAny <Earning>(), It.IsAny <List <Payment> >())) .Returns(requiredPayments) .Verifiable(); // act var actualRequiredPayment = await processor.HandleEarningEvent(earningEvent, paymentHistoryCacheMock.Object, CancellationToken.None); // assert actualRequiredPayment.Should().HaveCount(0); //Ideally this logic should be moved into a specific testable class requiredPaymentsService.Verify(x => x.GetRequiredPayments(It.IsAny <Earning>(), It.Is <List <Payment> >(lst => lst.Count == 1 && lst.Any(p => p.LearnAimReference == "zprog001")))); }
public async Task <ReadOnlyCollection <PeriodisedRequiredPaymentEvent> > HandlePayableEarningEvent(PayableEarningEvent earningEvent, CancellationToken cancellationToken) { paymentLogger.LogVerbose($"Handling PayableEarningEvent for jobId:{earningEvent.JobId} with apprenticeship key based on {logSafeApprenticeshipKeyString}"); try { using (var operation = telemetry.StartOperation("RequiredPaymentsService.HandlePayableEarningEvent", earningEvent.EventId.ToString())) { var stopwatch = Stopwatch.StartNew(); await ResetPaymentHistoryCacheIfDifferentCollectionPeriod(earningEvent.CollectionPeriod) .ConfigureAwait(false); await Initialise(earningEvent.CollectionPeriod.Period).ConfigureAwait(false); var requiredPaymentEvents = await payableEarningEventProcessor.HandleEarningEvent(earningEvent, paymentHistoryCache, cancellationToken).ConfigureAwait(false); Log(requiredPaymentEvents); telemetry.TrackDuration("RequiredPaymentsService.HandlePayableEarningEvent", stopwatch, earningEvent); telemetry.StopOperation(operation); return(requiredPaymentEvents); } } catch (Exception e) { paymentLogger.LogError($"Error handling payable earning. Error: {e.Message}"); throw; } }