public async Task <IEnumerable <AimAndDeliverableModel> > GetAimAndDeliverableModel(
            int ukPrn,
            CancellationToken cancellationToken)
        {
            var reportData = new List <AimAndDeliverableModel>();

            var providerConRefNumbers = (await _validLearnerDataService.GetLearnerConRefNumbers(ukPrn, cancellationToken)).ToList();

            var conRefNumbers = providerConRefNumbers.Where(IsConRefNumberRound2).ToList();

            var validLearners = (await _validLearnerDataService.GetLearnerDetails(ukPrn, FundingModel, conRefNumbers, cancellationToken))
                                .ToDictionary(vl => new ValidLearnerKey(vl.LearnRefNumber, vl.AimSeqNumber ?? 0), vl => vl);

            var fm70LearningDeliveries =
                (await _fm70DataService.GetLearningDeliveries(ukPrn, false, cancellationToken)).ToList();

            var learningDeliveryFams =
                (await _validLearnerDataService.GetLearningDeliveryFams(ukPrn, ReportingConstants.LearningDeliveryFamTypeRES, cancellationToken)).ToList();

            var learnerMonitorings = (await _validLearnerDataService.GetLearnerMonitorings(ukPrn, cancellationToken))
                                     .ToDictionary(
                lm => new LearnerMonitoringKey(lm.LearnRefNumber, lm.ProvSpecLearnMonOccur),
                lm => lm);

            var deliveryMonitorings = (await _validLearnerDataService.GetDeliveryMonitorings(ukPrn, cancellationToken))
                                      .ToDictionary(
                dm => new DeliveryMonitoringKey(dm.LearnRefNumber, dm.ProvSpecDelMonOccur, dm.AimSeqNumber),
                dm => dm);

            var fm70Outcomes = (await _fm70DataService.GetOutcomes(ukPrn, cancellationToken))
                               .ToDictionary(
                oc => new OutcomeKey(
                    oc.LearnRefNumber,
                    oc.OutType,
                    oc.OutCode,
                    oc.OutStartDate),
                oc => oc);

            var outcomes = (await _validLearnerDataService.GetDPOutcomes(ukPrn, cancellationToken))
                           .ToDictionary(
                oc => new OutcomeKey(
                    oc.LearnRefNumber,
                    oc.OutType,
                    oc.OutCode,
                    oc.OutStartDate),
                oc => oc);

            var deliverableCodes = fm70LearningDeliveries.Select(ld => ld.DeliverableCode).ToList();

            var fcsCodeMappings =
                _referenceDataService.GetContractDeliverableCodeMapping(deliverableCodes, cancellationToken).ToList();

            var learnAimRefs = validLearners.Select(ld => ld.Value.LearnAimRef).ToList();

            var larsDeliveries = (await _referenceDataService.GetLarsLearningDelivery(learnAimRefs, cancellationToken))
                                 .ToDictionary(t => t.LearnAimRef, t => t, StringComparer.OrdinalIgnoreCase);

            foreach (var fm70Delivery in fm70LearningDeliveries)
            {
                var periodReportedForDeliverable = false;

                var key = new ValidLearnerKey(fm70Delivery.LearnRefNumber, fm70Delivery.AimSeqNumber);
                validLearners.TryGetValue(key, out var learningDelivery);

                if (learningDelivery == null)
                {
                    continue;
                }

                DpOutcome     outcome     = null;
                Fm70DpOutcome fm70Outcome = null;
                if (!string.IsNullOrWhiteSpace(fm70Delivery.EligibleProgressionOutcomeType) &&
                    fm70Delivery.EligibleProgressionOutcomeCode != null &&
                    fm70Delivery.EligibleProgressionOutomeStartDate != null)
                {
                    var outcomeKey = new OutcomeKey(
                        fm70Delivery.LearnRefNumber,
                        fm70Delivery.EligibleProgressionOutcomeType,
                        fm70Delivery.EligibleProgressionOutcomeCode ?? 0,
                        fm70Delivery.EligibleProgressionOutomeStartDate ?? DateTime.MinValue);

                    outcomes.TryGetValue(outcomeKey, out outcome);

                    fm70Outcomes.TryGetValue(outcomeKey, out fm70Outcome);
                }

                larsDeliveries.TryGetValue(learningDelivery.LearnRefNumber, out var larsDelivery);

                var fam = learningDeliveryFams.FirstOrDefault(ldf =>
                                                              ldf.LearnRefNumber.CaseInsensitiveEquals(learningDelivery.LearnRefNumber) &&
                                                              ldf.AimSeqNumber == learningDelivery.AimSeqNumber);

                if (!fm70Delivery.Fm70LearningDeliveryDeliverables.Any())
                {
                    var model = GetAimAndDeliverableModel(learningDelivery, fm70Delivery, larsDelivery, outcome, fm70Outcome, fam, learnerMonitorings, deliveryMonitorings);

                    reportData.Add(model);
                    continue;
                }

                foreach (var deliverable in fm70Delivery.Fm70LearningDeliveryDeliverables)
                {
                    var deliverableCode = deliverable.DeliverableCode;

                    var fcsMapping = fcsCodeMappings?.SingleOrDefault(f =>
                                                                      f.ExternalDeliverableCode.CaseInsensitiveEquals(deliverableCode) &&
                                                                      f.FundingStreamPeriodCode.CaseInsensitiveEquals(FundingStreamPeriodCode));

                    var fm70Periods = fm70Delivery.Fm70LearningDeliveryDeliverablePeriods.Where(delivery =>
                                                                                                delivery.LearnRefNumber.CaseInsensitiveEquals(fm70Delivery.LearnRefNumber) &&
                                                                                                delivery.AimSeqNumber == fm70Delivery.AimSeqNumber &&
                                                                                                delivery.DeliverableCode.CaseInsensitiveEquals(deliverableCode));

                    foreach (var period in fm70Periods)
                    {
                        var total = (period.StartEarnings ?? 0) + (period.AchievementEarnings ?? 0)
                                    + (period.AdditionalProgCostEarnings ?? 0) + (period.ProgressionEarnings ?? 0);
                        if (period.ReportingVolume == 0 && period.DeliverableVolume == 0 && total == 0)
                        {
                            continue;
                        }

                        var model = GetAimAndDeliverableModel(
                            learningDelivery,
                            fm70Delivery,
                            larsDelivery,
                            outcome,
                            fm70Outcome,
                            fam,
                            learnerMonitorings,
                            deliveryMonitorings,
                            deliverable,
                            period,
                            fcsMapping,
                            total);

                        periodReportedForDeliverable = true;
                        reportData.Add(model);
                    }

                    if (!periodReportedForDeliverable)
                    {
                        var model = GetAimAndDeliverableModel(learningDelivery, fm70Delivery, larsDelivery, outcome, fm70Outcome, fam, learnerMonitorings, deliveryMonitorings, deliverable, null, fcsMapping, null);
                        reportData.Add(model);
                    }
                }
            }

            return(reportData);
        }
        private AimAndDeliverableModel GetAimAndDeliverableModel(
            LearnerDetails learnerDetails,
            Fm70LearningDelivery fm70Delivery,
            LarsLearningDeliveryModel larsDelivery,
            DpOutcome outcome,
            Fm70DpOutcome fm70Outcome,
            LearningDeliveryFam fam,
            IDictionary <LearnerMonitoringKey, ProviderSpecLearnerMonitoring> learnerMonitorings,
            IDictionary <DeliveryMonitoringKey, ProviderSpecDeliveryMonitoring> deliveryMonitorings,
            Fm70LearningDeliveryDeliverable deliverable  = null,
            Fm70LearningDeliveryDeliverablePeriod period = null,
            FcsDeliverableCodeMapping fcsMapping         = null,
            decimal?total = null)
        {
            var model = new AimAndDeliverableModel
            {
                LearnRefNumber                       = learnerDetails.LearnRefNumber,
                ULN                                  = learnerDetails.Uln,
                AimSeqNumber                         = learnerDetails.AimSeqNumber,
                ConRefNumber                         = learnerDetails.ConRefNumber,
                DeliverableCode                      = deliverable?.DeliverableCode,
                DeliverableName                      = fcsMapping?.DeliverableName,
                LearnAimRef                          = learnerDetails.LearnAimRef,
                DeliverableUnitCost                  = deliverable?.DeliverableUnitCost,
                ApplicWeightFundRate                 = fm70Delivery?.ApplicWeightFundRate,
                AimValue                             = fm70Delivery?.AimValue,
                LearnAimRefTitle                     = larsDelivery?.LearnAimRefTitle,
                PMUKPRN                              = learnerDetails.Pmukprn,
                CampId                               = learnerDetails.CampId,
                ProvSpecLearnMonA                    = GetLearnerMonitoring(learnerMonitorings, learnerDetails.LearnRefNumber, "A"),
                ProvSpecLearnMonB                    = GetLearnerMonitoring(learnerMonitorings, learnerDetails.LearnRefNumber, "B"),
                SWSupAimId                           = learnerDetails.SwsupAimId,
                NotionalNVQLevelv2                   = larsDelivery?.NotionalNVQLevelv2,
                SectorSubjectAreaTier2               = larsDelivery?.SectorSubjectAreaTier2,
                AdjustedAreaCostFactor               = fm70Delivery?.AdjustedAreaCostFactor,
                AdjustedPremiumFactor                = fm70Delivery?.AdjustedPremiumFactor,
                LearnStartDate                       = learnerDetails.LearnStartDate,
                LDESFEngagementStartDate             = fm70Delivery?.LdEsfEngagementStartDate,
                LearnPlanEndDate                     = learnerDetails.LearnPlanEndDate,
                CompStatus                           = learnerDetails.CompStatus,
                LearnActEndDate                      = learnerDetails.LearnActEndDate,
                Outcome                              = learnerDetails.Outcome,
                AddHours                             = learnerDetails.AddHours,
                LearnDelFAMCode                      = fam?.LearnDelFamCode,
                ProvSpecDelMonA                      = GetDeliveryMonitoring(deliveryMonitorings, learnerDetails.LearnRefNumber, learnerDetails.AimSeqNumber, "A"),
                ProvSpecDelMonB                      = GetDeliveryMonitoring(deliveryMonitorings, learnerDetails.LearnRefNumber, learnerDetails.AimSeqNumber, "B"),
                ProvSpecDelMonC                      = GetDeliveryMonitoring(deliveryMonitorings, learnerDetails.LearnRefNumber, learnerDetails.AimSeqNumber, "C"),
                ProvSpecDelMonD                      = GetDeliveryMonitoring(deliveryMonitorings, learnerDetails.LearnRefNumber, learnerDetails.AimSeqNumber, "D"),
                PartnerUKPRN                         = learnerDetails.PartnerUkprn,
                DelLocPostCode                       = learnerDetails.DelLocPostCode,
                LatestPossibleStartDate              = fm70Delivery?.LatestPossibleStartDate,
                EligibleProgressionOutomeStartDate   = fm70Delivery?.EligibleProgressionOutomeStartDate,
                EligibleOutcomeEndDate               = outcome?.OutEndDate,
                EligibleOutcomeCollectionDate        = outcome?.OutCollDate,
                EligibleOutcomeDateProgressionLength = fm70Outcome?.OutcomeDateForProgression,
                EligibleProgressionOutcomeType       = fm70Delivery?.EligibleProgressionOutcomeType,
                EligibleProgressionOutcomeCode       = fm70Delivery?.EligibleProgressionOutcomeCode,
                Period                               = _reportMonths[period?.Period - 1 ?? 0],
                DeliverableVolume                    = period?.DeliverableVolume,
                StartEarnings                        = period?.StartEarnings,
                AchievementEarnings                  = period?.AchievementEarnings,
                AdditionalProgCostEarnings           = period?.AdditionalProgCostEarnings,
                ProgressionEarnings                  = period?.ProgressionEarnings,
                TotalEarnings                        = total
            };

            return(model);
        }