EnVisageOnline/Main/Source/EnVisage/Code/BLL/SupplyDemandReportManager.cs

286 lines
12 KiB
C#

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<Guid>();
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<Guid>();
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<Guid> 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<Guid> scenarios, IEnumerable<Guid> 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
}
}