public EarningsAndPayments BuildEarningsAndPayments(
            IEnumerable <Payment> filteredPayments,
            IEnumerable <Payment> allPayments,
            LearningDelivery learningDelivery,
            ICollection <AECApprenticeshipPriceEpisodePeriodisedValues> aecPriceEpisodePeriodisedValues,
            int currentAcademicYear,
            DateTime academicYearStart,
            DateTime nextAcademicYearStart,
            int previousYearClosedReturnPeriod)
        {
            var filteredPaymentsList       = filteredPayments.ToList();
            var totalsByPeriodDictionary   = BuildCoInvestmentPaymentsPerPeriodDictionary(filteredPaymentsList, currentAcademicYear);
            var totalDueCurrentYear        = totalsByPeriodDictionary.Sum(d => d.Value);
            var totalDuePreviousYear       = TotalCoInvestmentDueFromEmployerInPreviousFundingYears(filteredPaymentsList, previousYearClosedReturnPeriod);
            var totalCollectedCurrentYear  = GetTotalPmrBetweenDates(learningDelivery, academicYearStart, nextAcademicYearStart);
            var totalCollectedPreviousYear = GetTotalPmrBetweenDates(learningDelivery, null, academicYearStart);

            var coInvestmentPaymentsFromEmployer = new CoInvestmentPaymentsDueFromEmployer()
            {
                August    = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 1),
                September = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 2),
                October   = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 3),
                November  = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 4),
                December  = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 5),
                January   = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 6),
                February  = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 7),
                March     = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 8),
                April     = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 9),
                May       = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 10),
                June      = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 11),
                July      = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 12),
                R13       = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 13),
                R14       = GetPeriodisedValueFromDictionaryForPeriod(totalsByPeriodDictionary, 14)
            };

            var earningsAndPayments = new EarningsAndPayments()
            {
                TotalPMRPreviousFundingYears = totalCollectedPreviousYear,
                TotalCoInvestmentDueFromEmployerInPreviousFundingYears = totalDuePreviousYear,
                TotalPMRThisFundingYear = totalCollectedCurrentYear,
                TotalCoInvestmentDueFromEmployerThisFundingYear = totalDueCurrentYear,
                EmployerCoInvestmentPercentage      = GetEmployerCoInvestmentPercentage(filteredPaymentsList),
                PercentageOfCoInvestmentCollected   = GetPercentageOfInvestmentCollected(totalDueCurrentYear, totalDuePreviousYear, totalCollectedCurrentYear, totalCollectedPreviousYear),
                CompletionEarningThisFundingYear    = CalculateCompletionEarningsThisFundingYear(learningDelivery, aecPriceEpisodePeriodisedValues),
                CompletionPaymentsThisFundingYear   = CalculateCompletionPaymentsInAcademicYear(allPayments, currentAcademicYear),
                CoInvestmentPaymentsDueFromEmployer = coInvestmentPaymentsFromEmployer
            };

            return(earningsAndPayments);
        }
        public EarningsAndPayments Build(
            IEnumerable <PaymentAndLearningDelivery> paymentAndLearningDeliveries,
            IEnumerable <ApprenticeshipPriceEpisodePeriodisedValues> periodisedValuesForLearner)
        {
            var result = new EarningsAndPayments();

            // All payments records for this reporting line get added together by period.
            var paymentsByPeriodLookup = paymentAndLearningDeliveries.GroupBy(p => p.Payment.CollectionPeriod).ToDictionary(pbp => pbp.Key);

            result.AugustR01Payments    = paymentsByPeriodLookup.GetValueOrDefault((byte)1)?.Sum(p => p.Payment.Amount) ?? 0;
            result.SeptemberR02Payments = paymentsByPeriodLookup.GetValueOrDefault((byte)2)?.Sum(p => p.Payment.Amount) ?? 0;
            result.OctoberR03Payments   = paymentsByPeriodLookup.GetValueOrDefault((byte)3)?.Sum(p => p.Payment.Amount) ?? 0;
            result.NovemberR04Payments  = paymentsByPeriodLookup.GetValueOrDefault((byte)4)?.Sum(p => p.Payment.Amount) ?? 0;
            result.DecemberR05Payments  = paymentsByPeriodLookup.GetValueOrDefault((byte)5)?.Sum(p => p.Payment.Amount) ?? 0;
            result.JanuaryR06Payments   = paymentsByPeriodLookup.GetValueOrDefault((byte)6)?.Sum(p => p.Payment.Amount) ?? 0;
            result.FebruaryR07Payments  = paymentsByPeriodLookup.GetValueOrDefault((byte)7)?.Sum(p => p.Payment.Amount) ?? 0;
            result.MarchR08Payments     = paymentsByPeriodLookup.GetValueOrDefault((byte)8)?.Sum(p => p.Payment.Amount) ?? 0;
            result.AprilR09Payments     = paymentsByPeriodLookup.GetValueOrDefault((byte)9)?.Sum(p => p.Payment.Amount) ?? 0;
            result.MayR10Payments       = paymentsByPeriodLookup.GetValueOrDefault((byte)10)?.Sum(p => p.Payment.Amount) ?? 0;
            result.JuneR11Payments      = paymentsByPeriodLookup.GetValueOrDefault((byte)11)?.Sum(p => p.Payment.Amount) ?? 0;
            result.JulyR12Payments      = paymentsByPeriodLookup.GetValueOrDefault((byte)12)?.Sum(p => p.Payment.Amount) ?? 0;
            result.R13Payments          = paymentsByPeriodLookup.GetValueOrDefault((byte)13)?.Sum(p => p.Payment.Amount) ?? 0;
            result.R14Payments          = paymentsByPeriodLookup.GetValueOrDefault((byte)14)?.Sum(p => p.Payment.Amount) ?? 0;

            result.TotalPaymentsYearToDate =
                result.AugustR01Payments + result.SeptemberR02Payments + result.OctoberR03Payments +
                result.NovemberR04Payments + result.DecemberR05Payments + result.JanuaryR06Payments +
                result.FebruaryR07Payments + result.MarchR08Payments + result.AprilR09Payments +
                result.MayR10Payments + result.JuneR11Payments + result.JulyR12Payments +
                result.R13Payments + result.R14Payments;

            //Need to compress the payment records to remove the duplicates when not looking at the collection period the payment belongs to.
            List <(int aimSeq, byte transType)> TransactionTypeAndAimSeqs = paymentAndLearningDeliveries.Select(pld =>
                                                                                                                (pld.LearningDelivery?.AimSequenceNumber ?? 0, pld.Payment.TransactionType))
                                                                            .Distinct().ToList();

            // For Earnings we need to get the relevant periodised values for the aim sequence from each transaction type, then get the relevant Attribute matches
            foreach (var TransactionTypeAndAimSeq in TransactionTypeAndAimSeqs)
            {
                var periodisedValuesForPayment =
                    periodisedValuesForLearner?.Where(pv => pv.AimSeqNumber == TransactionTypeAndAimSeq.aimSeq).ToList() ??
                    new List <ApprenticeshipPriceEpisodePeriodisedValues>();

                var attributeTypesToMatch = GetAttributesForTransactionType(TransactionTypeAndAimSeq.transType);

                result.AugustEarnings    += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_1);
                result.SeptemberEarnings += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_2);
                result.OctoberEarnings   += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_3);
                result.NovemberEarnings  += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_4);
                result.DecemberEarnings  += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_5);
                result.JanuaryEarnings   += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_6);
                result.FebruaryEarnings  += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_7);
                result.MarchEarnings     += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_8);
                result.AprilEarnings     += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_9);
                result.MayEarnings       += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_10);
                result.JuneEarnings      += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_11);
                result.JulyEarnings      += GetEarningsForPeriod(periodisedValuesForPayment, attributeTypesToMatch, pvp => pvp.Period_12);
            }

            result.TotalEarnings =
                result.AugustEarnings + result.SeptemberEarnings + result.OctoberEarnings +
                result.NovemberEarnings + result.DecemberEarnings + result.JanuaryEarnings +
                result.FebruaryEarnings + result.MarchEarnings + result.AprilEarnings +
                result.MayEarnings + result.JuneEarnings + result.JulyEarnings;

            return(result);
        }