public PerformanceMeasuresValidationResult ValidatePerformanceMeasures()
        {
            var performanceMeasureActualSimples   = PerformanceMeasureActuals ?? new List <PerformanceMeasureActualSimple>();
            var projectExemptReportingYearSimples = ProjectExemptReportingYears ?? new List <ProjectExemptReportingYearSimple>();

            var project = HttpRequestStorage.DatabaseEntities.Projects.Single(x => x.ProjectID == ProjectID);

            // validation 1: ensure that we have PM values from ProjectUpdate start year to min(endyear, currentyear)
            var exemptYears   = projectExemptReportingYearSimples.Where(x => x.IsExempt).Select(x => x.CalendarYear).ToList();
            var yearsExpected = project.GetProjectUpdateImplementationStartToCompletionDateRange()
                                .Where(x => !exemptYears.Contains(x)).ToList();
            var yearsEntered = performanceMeasureActualSimples.Select(x => x.CalendarYear.GetValueOrDefault()).Distinct();
            var missingYears = yearsExpected.GetMissingYears(yearsEntered);

            // validation 2: incomplete PM row (missing performanceMeasureSubcategory option id)
            var performanceMeasureActualsWithIncompleteWarnings = ValidateNoIncompletePerformanceMeasureActualRow();

            //validation 3: duplicate PM row
            var performanceMeasureActualsWithDuplicateWarnings = ValidateNoDuplicatePerformanceMeasureActualRow();

            //validation4: data entered for exempt years
            var performanceMeasureActualsWithExemptYear = ValidateNoExemptYearsWithReportedPerformanceMeasureRow();

            var performanceMeasuresValidationResult = new PerformanceMeasuresValidationResult(missingYears,
                                                                                              performanceMeasureActualsWithIncompleteWarnings, performanceMeasureActualsWithDuplicateWarnings,
                                                                                              performanceMeasureActualsWithExemptYear);

            return(performanceMeasuresValidationResult);
        }
        /// <summary>
        /// Validate Performance Measures by groups
        /// </summary>
        /// <returns>Null if there's nothing to validate, otherwise, a PerformanceMeasuresValidationResult</returns>
        public List <PerformanceMeasuresValidationResult> ValidatePerformanceMeasures()
        {
            List <PerformanceMeasuresValidationResult> results = new List <PerformanceMeasuresValidationResult>();

            var performanceMeasureActualSimples   = PerformanceMeasureActualSimples ?? new List <PerformanceMeasureActualSimple>();
            var projectExemptReportingYearSimples = ProjectExemptReportingYearSimples ?? new List <ProjectExemptReportingYearSimple>();

            // What project are we dealing with?
            var project = HttpRequestStorage.DatabaseEntities.Projects.Single(x => x.ProjectID == ProjectID);

            // What years are expected for this Project?
            var exemptYears   = projectExemptReportingYearSimples.Where(x => x.IsExempt).Select(x => x.CalendarYear).ToList();
            var yearsExpected = project.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 ProjectCreate Project Implementation start year to min(endyear, currentyear)
            // PMs are required when project stage is Implementation, Post-Implementation, or Completed
            var missingYears = new HashSet <int>();

            if (project.ProjectStage.RequiresPerformanceMeasureActuals() || project.ProjectStage == ProjectStage.Completed || project.ProjectStage == ProjectStage.PostImplementation)
            {
                var yearsEntered = performanceMeasureActualSimples.Select(x => x.CalendarYear).Distinct();
                missingYears = yearsExpected.GetMissingYears(yearsEntered);
            }
            if (missingYears.Any() && !performanceMeasureActualSimples.Any())
            {
                // There are missing years, but no PMs entered
                results.Add(new PerformanceMeasuresValidationResult(FirmaValidationMessages.PerformanceMeasureOrExemptYearsRequired));
            }

            // What distinct PerformanceMeasures are being worked with?
            var pmasGrouped = performanceMeasureActualSimples.GroupBy(pmas => pmas.PerformanceMeasureID.Value);

            // Examine each PerformanceMeasure group as a unit to check for problems within the group
            foreach (var performanceMeasureActualSimpleGroup in pmasGrouped)
            {
                int    currentPerformanceMeasureID          = performanceMeasureActualSimpleGroup.Key;
                var    currentPerformanceMeasure            = HttpRequestStorage.DatabaseEntities.PerformanceMeasures.Single(pm => pm.PerformanceMeasureID == currentPerformanceMeasureID);
                string currentPerformanceMeasureDisplayName = currentPerformanceMeasure.PerformanceMeasureDisplayName;

                // validation 2: incomplete PM row (missing performanceMeasureSubcategory option id)
                var performanceMeasureActualsWithIncompleteWarnings = ValidateNoIncompletePerformanceMeasureActualRow(currentPerformanceMeasureID);

                // validation 3: duplicate PM row
                var performanceMeasureActualsWithDuplicateWarnings = ValidateNoDuplicatePerformanceMeasureActualRow(currentPerformanceMeasureID);

                // validation 4: data entered for exempt years
                var performanceMeasureActualsWithExemptYear = ValidateNoExemptYearsWithReportedPerformanceMeasureRow(currentPerformanceMeasureID);

                var performanceMeasuresValidationResult = new PerformanceMeasuresValidationResult(
                    currentPerformanceMeasureID,
                    currentPerformanceMeasureDisplayName,
                    missingYears,
                    performanceMeasureActualsWithIncompleteWarnings,
                    performanceMeasureActualsWithDuplicateWarnings,
                    performanceMeasureActualsWithExemptYear);

                results.Add(performanceMeasuresValidationResult);
            }

            return(results);
        }