/// <summary> /// Validates the schedule data needed (i.e. Breaks, spots etc) for /// starting run. /// </summary> /// <param name="run"></param> /// <param name="campaigns"></param> /// <param name="salesAreas"></param> /// <param name="demographicsCount"></param> private List <SystemMessage> ValidateScheduleDataForStartRun(Run run, IReadOnlyCollection <CampaignReducedModel> campaigns, IEnumerable <SalesArea> salesAreas, int demographicsCount) { List <SystemMessage> messages = new List <SystemMessage>(); var tenantSettings = _tenantSettingsRepository.Get(); var minDocumentRestriction = tenantSettings?.RunRestrictions?.MinDocRestriction; var minRunSizeDocumentRestriction = tenantSettings?.RunRestrictions?.MinRunSizeDocRestriction; if (minDocumentRestriction == null || minRunSizeDocumentRestriction == null) { messages.Add(FormatSystemMessage(SystemMessage.RunRestrictionsMissing, null)); return(messages); } var daysCount = (run.EndDate - run.StartDate).Days + 1; if (_featureManager.IsEnabled(nameof(ProductFeature.NineValidationMinSpot))) { var minSpotDocument = minRunSizeDocumentRestriction.Spots * daysCount * salesAreas.Count(); if (_spotRepository.Count(x => x.StartDateTime >= run.StartDate && x.StartDateTime <= run.EndDate) < minSpotDocument) { messages.Add(FormatSystemMessage(SystemMessage.SpotDataMissing, null)); } } var countBreaksAndProgrammes = _scheduleRepository.CountBreaksAndProgrammes(run.StartDate, run.EndDate); var minBreakDocument = minRunSizeDocumentRestriction.Breaks * daysCount * salesAreas.Count(); if (countBreaksAndProgrammes.breaksCount < minBreakDocument) { messages.Add(FormatSystemMessage(SystemMessage.BreakDataMissing, null)); } var minProgrammeDocuments = minRunSizeDocumentRestriction.Programmes * daysCount * salesAreas.Count(); if (countBreaksAndProgrammes.programmesCount < minProgrammeDocuments) { messages.Add(FormatSystemMessage(SystemMessage.ProgrammeDataMissing, null)); } if (campaigns.Count < minDocumentRestriction.Campaigns) { messages.Add(FormatSystemMessage(SystemMessage.CampaignDataMissing, null)); } else { // Check that campaign CustomId is set var campaignsWithInvalidCustomId = campaigns.Where(c => c.CustomId == 0); foreach (var campaignWithInvalidCustomId in campaignsWithInvalidCustomId) { messages.Add(FormatSystemMessage(SystemMessage.CampaignDataInvalid, new Dictionary <string, string>() { { _placeholderCampaignName, campaignWithInvalidCustomId.Name }, { _placeholderInvalidData, "Custom ID is not set" } })); } // Check that campaign CustomId is unique var campaignsWithCustomId = campaigns.Where(c => c.CustomId > 0).ToList(); var campaignCustomIdsGrouped = campaignsWithCustomId .GroupBy(c => c.CustomId) .ToDictionary(x => x.Key, x => x.Count()); foreach (var campaign in campaignsWithCustomId) { if (!campaignCustomIdsGrouped.ContainsKey(campaign.CustomId) || campaignCustomIdsGrouped[campaign.CustomId] > 1) { messages.Add(FormatSystemMessage(SystemMessage.CampaignDataInvalid, new Dictionary <string, string>() { { _placeholderCampaignName, campaign.Name }, { _placeholderInvalidData, "Custom ID is not unique" } })); } } } if (_clearanceRepository.CountAll < minDocumentRestriction.ClearanceCodes) { messages.Add(FormatSystemMessage(SystemMessage.ClearanceDataMissing, null)); } if (_clashRepository.CountAll < minDocumentRestriction.Clashes) { messages.Add(FormatSystemMessage(SystemMessage.ClashDataMissing, null)); } if (demographicsCount < minDocumentRestriction.Demographics) { messages.Add(FormatSystemMessage(SystemMessage.DemographicDataMissing, null)); } if (_productRepository.CountAll < minDocumentRestriction.Products) { messages.Add(FormatSystemMessage(SystemMessage.ProductDataMissing, null)); } var campaignProductRefs = campaigns .Where(c => !string.IsNullOrEmpty(c.Product)) .Select(c => c.Product) .Distinct() .ToList(); var existingProducts = _productRepository .FindByExternal(campaignProductRefs) .ToDictionary(x => x.Externalidentifier, x => x); var productClashCodes = existingProducts .Where(p => !string.IsNullOrEmpty(p.Value.ClashCode)) .Select(p => p.Value.ClashCode) .Distinct() .ToList(); var existingClashCodes = new HashSet <string>(_clashRepository .FindByExternal(productClashCodes) .Select(c => c.Externalref)); foreach (var campaign in campaigns) { if (string.IsNullOrEmpty(campaign.Product)) { messages.Add(FormatSystemMessage(SystemMessage.CampaignDataInvalid, new Dictionary <string, string> { { _placeholderCampaignName, campaign.ExternalId }, { _placeholderInvalidData, "Campaign is missing a Product" } })); continue; } var product = existingProducts.ContainsKey(campaign.Product) ? existingProducts[campaign.Product] : null; if (product is null) { messages.Add(FormatSystemMessage(SystemMessage.CampaignDataInvalid, new Dictionary <string, string> { { _placeholderCampaignName, campaign.ExternalId }, { _placeholderInvalidData, $"Product: {campaign.Product} is missing" } })); } else if (!string.IsNullOrEmpty(product.ClashCode) && !existingClashCodes.Contains(product.ClashCode)) { messages.Add(FormatSystemMessage(SystemMessage.CampaignDataInvalid, new Dictionary <string, string> { { _placeholderCampaignName, campaign.ExternalId }, { _placeholderInvalidData, $"Product: {product.Externalidentifier}, Clash Code: {product.ClashCode} is missing" } })); } } return(messages); }