private async Task <bool> HoldBackCompletionPayments(TEarningEvent earningEvent, Earning earning, int type, CancellationToken cancellationToken) { if (type != (int)OnProgrammeEarningType.Completion) { return(false); } if (earning.Amount <= 0m) { return(false); } var priceEpisode = earningEvent.PriceEpisodes.Single(p => p.Identifier == earning.PriceEpisodeIdentifier); var key = apprenticeshipKeyProvider.GetCurrentKey(); var employerPayments = await paymentHistoryRepository.GetEmployerCoInvestedPaymentHistoryTotal(key, cancellationToken).ConfigureAwait(false); return(completionPaymentService.ShouldHoldBackCompletionPayment(employerPayments, priceEpisode)); }
public async Task <ReadOnlyCollection <PeriodisedRequiredPaymentEvent> > HandleEarningEvent(TEarningEvent earningEvent, IDataCache <PaymentHistoryEntity[]> paymentHistoryCache, CancellationToken cancellationToken) { if (earningEvent == null) { throw new ArgumentNullException(nameof(earningEvent)); } try { var result = new List <PeriodisedRequiredPaymentEvent>(); if (await duplicateEarningEventService.IsDuplicate(earningEvent, cancellationToken) .ConfigureAwait(false)) { return(result.AsReadOnly()); } var cachedPayments = await paymentHistoryCache.TryGet(CacheKeys.PaymentHistoryKey, cancellationToken); var academicYearPayments = cachedPayments.HasValue ? cachedPayments.Value .Where(p => p.LearnAimReference.Equals(earningEvent.LearningAim.Reference, StringComparison.OrdinalIgnoreCase)) .Select(p => mapper.Map <PaymentHistoryEntity, Payment>(p)) .ToList() : new List <Payment>(); foreach (var(period, type) in GetPeriods(earningEvent)) { if (period.Period > earningEvent.CollectionPeriod.Period) // cut off future periods { continue; } if (period.Amount != 0 && !period.SfaContributionPercentage.HasValue) { throw new InvalidOperationException("Non-zero amount with no Sfa Contribution"); } var payments = academicYearPayments.Where(payment => payment.DeliveryPeriod == period.Period && payment.TransactionType == type) .ToList(); List <RequiredPayment> requiredPayments; var holdBackCompletionPayments = false; if (NegativeEarningWillResultInARefund(period, payments)) { requiredPayments = negativeEarningService .ProcessNegativeEarning(period.Amount, academicYearPayments, period.Period, period.PriceEpisodeIdentifier); } else { var earning = new Earning { Amount = period.Amount, SfaContributionPercentage = period.SfaContributionPercentage, EarningType = GetEarningType(type), PriceEpisodeIdentifier = period.PriceEpisodeIdentifier, AccountId = period.AccountId, TransferSenderAccountId = period.TransferSenderAccountId }; requiredPayments = requiredPaymentProcessor.GetRequiredPayments(earning, payments); if (requiredPayments.Count > 0) { holdBackCompletionPayments = await HoldBackCompletionPayments(earningEvent, earning, type, cancellationToken).ConfigureAwait(false); } } if (requiredPayments.GroupBy(x => x.SfaContributionPercentage) .All(x => x.Sum(y => y.Amount) == 0)) { continue; } foreach (var requiredPayment in requiredPayments) { var requiredPaymentEvent = CreateRequiredPaymentEvent(requiredPayment.EarningType, type, holdBackCompletionPayments); mapper.Map(period, requiredPaymentEvent); mapper.Map(earningEvent, requiredPaymentEvent); mapper.Map(requiredPayment, requiredPaymentEvent); AddRefundCommitmentDetails(requiredPayment, requiredPaymentEvent); var priceEpisodeIdentifier = requiredPaymentEvent.PriceEpisodeIdentifier; if (earningEvent.PriceEpisodes != null && earningEvent.PriceEpisodes.Any()) { var priceEpisode = earningEvent.PriceEpisodes.Count == 1 ? earningEvent.PriceEpisodes.FirstOrDefault() : earningEvent.PriceEpisodes?.SingleOrDefault(x => x.Identifier == priceEpisodeIdentifier); mapper.Map(priceEpisode, requiredPaymentEvent); if (requiredPaymentEvent.LearningAim != null) { mapper.Map(priceEpisode, requiredPaymentEvent.LearningAim); } } result.Add(requiredPaymentEvent); } } return(result.AsReadOnly()); } catch (Exception e) { paymentLogger.LogError($"Error while Handling EarningEvent for {earningEvent.Ukprn} ", e); throw; } }
private async Task <bool> HoldBackCompletionPayments(TEarningEvent earningEvent, Earning earning, int type, CancellationToken cancellationToken) { if (type != (int)OnProgrammeEarningType.Completion) { return(false); } if (earning.Amount <= 0m) { return(false); } var priceEpisode = earningEvent.PriceEpisodes.Single(p => p.Identifier == earning.PriceEpisodeIdentifier); var key = apprenticeshipKeyProvider.GetCurrentKey(); var employerPayments = await paymentHistoryRepository.GetEmployerCoInvestedPaymentHistoryTotal(key, cancellationToken).ConfigureAwait(false); var shouldHoldBackCompletionPayment = completionPaymentService.ShouldHoldBackCompletionPayment(employerPayments, priceEpisode); if (shouldHoldBackCompletionPayment) { var properties = new Dictionary <string, string> { { TelemetryKeys.JobId, earningEvent.JobId.ToString() }, { TelemetryKeys.Ukprn, earningEvent.Ukprn.ToString() }, { TelemetryKeys.LearnerRef, earningEvent.Learner.ReferenceNumber }, { "LearningAimReference", earningEvent.LearningAim.Reference }, { "EarningEventId", earningEvent.EventId.ToString() }, { TelemetryKeys.CollectionPeriod, earningEvent.CollectionPeriod.Period.ToString() }, { TelemetryKeys.AcademicYear, earningEvent.CollectionPeriod.AcademicYear.ToString() }, { "Completion HoldBack Exemption Code", employerPayments.ToString(CultureInfo.InvariantCulture) }, { "Expected Employer Contribution", priceEpisode.CompletionHoldBackExemptionCode.ToString() }, { "Reported Employer Contribution", priceEpisode.EmployerContribution.ToString() }, }; telemetry.TrackEvent("Holding Back Completion Payment", properties, new Dictionary <string, double>()); } return(shouldHoldBackCompletionPayment); }