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());
        }
Ejemplo n.º 7
0
        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"))));
        }
Ejemplo n.º 12
0
        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;
            }
        }