using Resources; namespace EnVisage.Code.BLL { using Models; using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; public class SupplyDemandReportManager : IDisposable { #region Private Variables private readonly EnVisageEntities _dbContext; private readonly bool _isContexLocal; #endregion #region Constructors public SupplyDemandReportManager(EnVisageEntities dbContext) { if (dbContext == null) { _dbContext = new EnVisageEntities(); _isContexLocal = true; } else { _dbContext = dbContext; } } #endregion #region Public Methods public ActivityCalendarModel GetReportModel(Guid userId, SupplyDemandReportRequestModel request) { if (userId == Guid.Empty) throw new ArgumentException(Messages.Commonl_ArgumentEmpty, "userId"); if (request == null) throw new ArgumentNullException("request"); if (request.Statuses == null || !request.Statuses.Any()) return null; var clientManager = new ClientManager(_dbContext); var projectManager = new ProjectManager(_dbContext); var scenarioManager = new ScenarioManager(_dbContext); var teamManager = new TeamManager(_dbContext); var resourcesManager = new PeopleResourcesManager(_dbContext); var fcManager = new FiscalCalendarManager(_dbContext); var acManager = new ActivityCalendarManager(_dbContext); if (request.Clients == null) request.Clients = new List(); if (!request.Clients.Any()) { // Get clients from projects, because they are not specified by user var extractedClients = clientManager.GetByProjectStatues(request.Statuses, userId); request.Clients.AddRange(extractedClients.Keys); } // we should retrieve only requested projects var projects = projectManager.GetProjectsWithChildrenByStatusAndClient(request.Statuses, request.Clients, request.Projects); var scenarios = projects.Select(x => x.Scenarios.Where(s => s.Type == ScenarioType.Portfolio) .OrderByDescending(s => s.DateCreated) .FirstOrDefault()) .Where(x => x != null); if (!scenarios.Any()) return null; // we should use intersection of what was requested (if any) with what we could retrieve by passed filter var projectIds = request.Projects != null && request.Projects.Any() ? projects.Select(x => x.Id).Intersect(request.Projects) : projects.Select(x => x.Id); var projectTeams = teamManager.GetTeamsByProjects(userId, projectIds, request.Teams); var visible2UserTeams = projectTeams.Select(x => x.TeamId).ToList(); var scenarioIds = scenarios.Select(x => x.Id); //var requestContainsTeams = request.Teams != null && request.Teams.Any(); //var scenariosRange = requestContainsTeams ? // GetScenariosRangeByTeamAllocations(scenarioIds, visible2UserTeams, request.StartDate, request.EndDate) : // GetScenariosRangeByScenarioDetails(scenarioIds, request.StartDate, request.EndDate); var scenariosRange = GetScenariosRangeByScenarioDetails(scenarioIds, request.StartDate, request.EndDate); // if user hasn't selected range we should get required dates from scenarios if (!request.StartDate.HasValue || !request.EndDate.HasValue) { if (!request.StartDate.HasValue) request.StartDate = scenariosRange.StartDate ?? scenarios.Min(x => x.StartDate); if (!request.EndDate.HasValue) request.EndDate = scenariosRange.EndDate ?? scenarios.Max(x => x.EndDate); } var fcWeeks = fcManager.GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, request.StartDate, request.EndDate, true, false, true); var weekEndings = fcWeeks.Select(x => x.EndDate).ToList(); if (weekEndings.Count <= 0) return null; var startDate = fcWeeks.Min(x => x.StartDate); var endDate = fcWeeks.Max(x => x.EndDate); var projectModel = acManager.ConvertProjectsToProjectModel(projects, fcWeeks, false).ToDictionary(x => x.ProjectId.ToString()); var projectScenarios = projectModel.Select(x => x.Value.ActiveScenario.Id); var resources = resourcesManager.GetResourcesWithAllocationsByTeams(visible2UserTeams, startDate, endDate); var resourcesECs = resources.Select(x => x.ExpenditureCategoryId).Distinct(); var superECsInScenarios = scenarioManager.GetExpendituresInScenarios(projectScenarios, true).Select(x => x.Id); var allECs = resourcesECs.Union(superECsInScenarios).ToList(); var needAllocations = scenarioManager.GetScenarioDetails(projectScenarios, null, weekEndings); var needAllocationsTree = acManager.ConvertNeedAllocationsToTree(needAllocations); var teamAllocations = teamManager.GetTeamsAllocation(visible2UserTeams, projectScenarios, allECs, startDate, endDate); var teamAllocationsTree = acManager.ConvertTeamAllocationsToTree(teamAllocations); foreach (var project in projects) { if (!projectModel.ContainsKey(project.Id.ToString())) continue; var pModel = projectModel[project.Id.ToString()]; var actualTeams = project.Teams != null ? project.Teams.Intersect(visible2UserTeams).ToList() : new List(); foreach (var teamId in actualTeams) pModel.Teams.Add(teamId.ToString(), new ActivityCalendarProjectTeamWithAssignedResourcesModel()); } var teams = teamManager.GetTeamsInfo(projectTeams, fcWeeks, true); var model = new ActivityCalendarModel { WeekEndings = fcManager.CastWeekEndingsToTree(weekEndings), Projects = projectModel, Teams = teams.ToDictionary(x => x.Id.ToString()), NeedAllocations = needAllocationsTree, TeamAllocations = teamAllocationsTree, }; return model; } public SupplyDemandReportFilterResponseModel GetFilterOptions(Guid userId, SupplyDemandReportFilterRequestModel request) { if (userId == Guid.Empty) throw new ArgumentException(Messages.Commonl_ArgumentEmpty, "userId"); if (request == null) throw new ArgumentNullException("request"); if ((request.Statuses == null) || !request.Statuses.Any()) return new SupplyDemandReportFilterResponseModel(); var clientManager = new ClientManager(_dbContext); var projectManager = new ProjectManager(_dbContext); var teamManager = new TeamManager(_dbContext); // Get clients by project statuses var clients = clientManager.GetByProjectStatues(request.Statuses, userId); if ((clients == null) || !clients.Any()) return new SupplyDemandReportFilterResponseModel(); // Get clients to extract project for. Apply user selection or take all clients, if clients not specified by user var clientsSelected = request.Clients != null && request.Clients.Any() ? request.Clients.Intersect(clients.Keys) : clients.Keys; // we should retrieve all projects by statuses and clients to fill list with available projects var projects = projectManager.GetProjectsWithChildrenByStatusAndClient(request.Statuses, clientsSelected, null); // We should throw out projects, that not suit selected clients list, as well as project without scenarios and teams projects.RemoveAll(p => !p.ClientId.HasValue || !clientsSelected.Contains(p.ClientId.Value) || p.Scenarios == null || p.Scenarios.All(t => t.Type != ScenarioType.Portfolio) || p.Teams == null || p.Teams.Count < 1); // we should use intersection of what was requested (if any) with what we could retrieve by passed filter var projectIds = (request.Projects != null && request.Projects.Any()) ? projects.Select(x => x.Id).Intersect(request.Projects) : projects.Select(x => x.Id); // we should get all available teams in the list var projectTeams = teamManager.GetTeamsByProjects(userId, projectIds, null); // Review clients list using extracted and filtered projects collection // ClientManager returns clients by project statuses using projects as links for that entities. // But ClientManager taskes all projects and avoid any checks on the existance of scenarios and teams for these projects. // As a result. The extracted list of clients may contain some not actual records. // We should throw out these (non-actual) client records, because selecting any of that client // in the report criteria makes Projects filter empty var clientsByExtractedProjects = projects.Where(x => x.ClientId.HasValue).Select(x => x.ClientId.Value); var model = new SupplyDemandReportFilterResponseModel { Clients = clients.Values.Where(c => clientsByExtractedProjects.Contains(c.Id)).Select(x => new SelectListItem() { Text = x.Name, Value = x.Id.ToString() }).ToList(), Projects = projects.Select(x => new SelectListItem { Text = x.ParentProject != null ? string.Format("{0} : {1}", x.Name, x.ParentProject.Name) : x.Name, Value = x.Id.ToString() }).ToList(), Teams = projectTeams.Select(x => new SelectListItem { Text = x.Name, Value = x.TeamId.ToString() }).ToList(), }; return model; } #endregion #region Private methods protected DateRangeNullable GetScenariosRangeByScenarioDetails(IEnumerable scenarios, DateTime? minDate, DateTime? maxDate) { if (scenarios == null) throw new ArgumentNullException("scenarios"); var result = new DateRangeNullable(); var qry = _dbContext.ScenarioDetail.Where(x => x.WeekEndingDate.HasValue && x.ParentID.HasValue && scenarios.Contains(x.ParentID.Value)); if (minDate.HasValue) qry.Where(x => x.WeekEndingDate.Value >= minDate.Value); if (maxDate.HasValue) { var lastWeek = FiscalCalendarManager.GetFiscalCalendarWeekForDate(maxDate.Value, _dbContext); qry.Where(x => x.WeekEndingDate.Value <= lastWeek.EndDate); } var datesQry = qry.Select(x => x.WeekEndingDate.Value); if (datesQry.Any()) { result.StartDate = datesQry.Min(); result.EndDate = datesQry.Max(); } return result; } protected DateRangeNullable GetScenariosRangeByTeamAllocations(IEnumerable scenarios, IEnumerable teams, DateTime? minDate, DateTime? maxDate) { if (scenarios == null) throw new ArgumentNullException("scenarios"); if (teams == null) throw new ArgumentNullException("teams"); var result = new DateRangeNullable(); var qry = _dbContext.TeamAllocations.Where(x => scenarios.Contains(x.ScenarioId) && teams.Contains(x.TeamId) && (x.Quantity > 0)); if (minDate.HasValue) qry.Where(x => x.WeekEndingDate >= minDate.Value); if (maxDate.HasValue) { var lastWeek = FiscalCalendarManager.GetFiscalCalendarWeekForDate(maxDate.Value, _dbContext); qry.Where(x => x.WeekEndingDate <= lastWeek.EndDate); } var datesQry = qry.Select(x => x.WeekEndingDate); if (datesQry.Any()) { result.StartDate = datesQry.Min(); result.EndDate = datesQry.Max(); } return result; } #endregion #region Overrides public void Dispose() { if (_isContexLocal) _dbContext.Dispose(); } #endregion } }