public static List <PerformanceMeasuresValidationResult> ValidatePerformanceMeasures(this ProjectUpdateBatch projectUpdateBatch) { if (!projectUpdateBatch.AreProjectBasicsValid()) { return(new List <PerformanceMeasuresValidationResult> { new PerformanceMeasuresValidationResult(FirmaValidationMessages.UpdateSectionIsDependentUponBasicsSection) }); } List <PerformanceMeasuresValidationResult> results = new List <PerformanceMeasuresValidationResult>(); var performanceMeasureActualUpdates = projectUpdateBatch.PerformanceMeasureActualUpdates ?? new List <PerformanceMeasureActualUpdate>(); // What years are expected for this Project? var exemptYears = projectUpdateBatch.GetPerformanceMeasuresExemptReportingYears().Select(x => x.CalendarYear).ToList(); var yearsExpected = projectUpdateBatch.ProjectUpdate.GetProjectUpdateImplementationStartToCompletionYearRange().Where(x => !exemptYears.Contains(x)).ToList(); // validation 1: ensure that at least one PM has values for each year that isn't marked as 'No accomplishments to report' from ProjectUpdate Project Implementation start year to min(endyear, currentyear) // if the ProjectUpdate record has a stage of Planning/Design, we do not do this validation var missingYears = new HashSet <int>(); if (projectUpdateBatch.ProjectUpdate.ProjectStage.RequiresPerformanceMeasureActuals() || projectUpdateBatch.ProjectUpdate.ProjectStage == ProjectStage.Completed || projectUpdateBatch.ProjectUpdate.ProjectStage == ProjectStage.PostImplementation) { var yearsEntered = projectUpdateBatch.PerformanceMeasureActualUpdates.Select(x => x.PerformanceMeasureReportingPeriod.PerformanceMeasureReportingPeriodCalendarYear).Distinct(); missingYears = yearsExpected.GetMissingYears(yearsEntered); } if (missingYears.Any() && !performanceMeasureActualUpdates.Any()) { // There are missing years, but no PMs entered results.Add(new PerformanceMeasuresValidationResult(FirmaValidationMessages.PerformanceMeasureOrExemptYearsRequired)); } // What distinct PerformanceMeasures are being worked with? var pmausGrouped = performanceMeasureActualUpdates.GroupBy(pmas => pmas.PerformanceMeasureID); // Examine each PerformanceMeasure group as a unit to check for problems within the group foreach (var performanceMeasureActualUpdateGroup in pmausGrouped) { var currentPerformanceMeasureActualUpdate = performanceMeasureActualUpdateGroup.First(); var currentPerformanceMeasureID = currentPerformanceMeasureActualUpdate.PerformanceMeasureID; // validation 2: incomplete PM row (missing performanceMeasureSubcategory option id) var performanceMeasureActualsWithIncompleteWarnings = projectUpdateBatch.ValidateNoIncompletePerformanceMeasureActualUpdateRow(currentPerformanceMeasureID); // validation 3: duplicate PM row var performanceMeasureActualsWithDuplicateWarnings = projectUpdateBatch.ValidateNoDuplicatePerformanceMeasureActualUpdateRow(currentPerformanceMeasureID); // validation 4: data entered for exempt years var performanceMeasureActualsWithExemptYear = projectUpdateBatch.ValidateNoExemptYearsWithReportedPerformanceMeasureRow(currentPerformanceMeasureID); string currentPerformanceMeasureDisplayName = currentPerformanceMeasureActualUpdate.PerformanceMeasure.PerformanceMeasureDisplayName; var performanceMeasuresValidationResult = new PerformanceMeasuresValidationResult( currentPerformanceMeasureID, currentPerformanceMeasureDisplayName, missingYears, performanceMeasureActualsWithIncompleteWarnings, performanceMeasureActualsWithDuplicateWarnings, performanceMeasureActualsWithExemptYear); results.Add(performanceMeasuresValidationResult); } return(results); }