1085 lines
55 KiB
C#
1085 lines
55 KiB
C#
using EnVisage.Code.BLL;
|
|
using EnVisage.Models;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Web;
|
|
|
|
namespace EnVisage.Code.Charts
|
|
{
|
|
public class DashboardCharthManager
|
|
{
|
|
private class Data
|
|
{
|
|
public decimal? cost { get; set; }
|
|
public decimal? quantity { get; set; }
|
|
public DateTime? weekend { get; set; }
|
|
public string projectStatus { get; set; }
|
|
public int? type { get; set; }
|
|
public string Name { get; set; }
|
|
public Guid? ExpCatId { get; set; }
|
|
public Guid ProjectId { get; set; }
|
|
public Guid ProjectType { get; set; }
|
|
public decimal? ScenarioBUCost { get; set; }
|
|
public int ScenarioDuration { get; set; }
|
|
}
|
|
|
|
public class PieData
|
|
{
|
|
public List<Guid> TypeId { get; set; }
|
|
public string Label { get; set; }
|
|
public decimal Value { get; set; }
|
|
public string PresetColor { get; set; }
|
|
}
|
|
|
|
EnVisageEntities db = new EnVisageEntities();
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="StartDate"></param>
|
|
/// <param name="EndDate"></param>
|
|
/// <param name="principals"></param>
|
|
/// <param name="type"></param>
|
|
/// <param name="status"></param>
|
|
/// <param name="classification"></param>
|
|
/// <param name="mode"></param>
|
|
/// <param name="teamId"></param>
|
|
/// <param name="viewId"></param>
|
|
/// <param name="isUOMHours">Indicates whether UOM (unit of measure) is hours or resources.</param>
|
|
/// <returns></returns>
|
|
public Dictionary<string, List<long[]>> GetData(DateTime StartDate, DateTime EndDate, Guid userId, string type, List<Guid> projectStatuses, List<Guid> projectTypes, ForecastDashboardMode mode, List<Guid> teams, List<Guid> views, bool? isUOMHours, bool isLaborMode, List<Guid> filterGroups, List<Guid> strategicGoals)
|
|
{
|
|
var result = new Dictionary<string, List<long[]>>();
|
|
var projects = GetFilteredProjectList(userId, teams, views);
|
|
|
|
if (strategicGoals != null && strategicGoals.Count > 0)
|
|
{
|
|
var sgs = db.StrategicGoal2Project.Where(x => strategicGoals.Contains(x.StrategicGoalId)).Select(x => x.ProjectId).ToList();
|
|
projects = projects.Where(x => sgs.Contains(x.Id));
|
|
}
|
|
|
|
int typeInt = 0;
|
|
if (!string.IsNullOrWhiteSpace(type) && type != "null")
|
|
typeInt = (int)Enum.Parse(typeof(ScenarioType), type);
|
|
|
|
Dictionary<Guid, ExpenditureCategory> expCats = new Dictionary<Guid, ExpenditureCategory>();
|
|
if (isLaborMode)
|
|
expCats = (from e in db.ExpenditureCategory where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor select e).ToDictionary(e => e.Id);
|
|
else
|
|
expCats = (from e in db.ExpenditureCategory where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor select e).ToDictionary(e => e.Id);
|
|
|
|
List<Guid> expCatGuids = expCats.Keys.ToList();
|
|
|
|
var uoms = db.UOMs.AsNoTracking().ToDictionary(u => u.Id);
|
|
|
|
IQueryable<Data> data = null;
|
|
|
|
long[] fiscalCalendarChartPoints = null;
|
|
List<DateTime> fiscalCalendarWeekendings = new List<DateTime>();
|
|
DateTime firstWeekEnding = DateTime.MinValue;
|
|
DateTime lastWeekEnding = DateTime.MinValue;
|
|
if (EndDate.Subtract(StartDate).TotalDays <= 400)
|
|
{
|
|
fiscalCalendarChartPoints = (from c in db.FiscalCalendars
|
|
where c.Type == (int)FiscalCalendarModel.FiscalYearType.Week &&
|
|
c.EndDate >= StartDate && c.EndDate <= EndDate && c.AdjustingPeriod == false && c.NonWorking == 0
|
|
orderby c.StartDate
|
|
select c.EndDate).ToArray().Select(t => (long)t.Subtract(Constants.UnixEpochDate).TotalMilliseconds).ToArray();
|
|
if (fiscalCalendarChartPoints.Length > 0)
|
|
{
|
|
firstWeekEnding = Constants.UnixEpochDate.AddMilliseconds(fiscalCalendarChartPoints[0]);
|
|
lastWeekEnding = Constants.UnixEpochDate.AddMilliseconds(fiscalCalendarChartPoints[fiscalCalendarChartPoints.Length - 1]);
|
|
}
|
|
fiscalCalendarWeekendings.AddRange(fiscalCalendarChartPoints.Select(i => Constants.UnixEpochDate.AddMilliseconds(i)));
|
|
data = GetWeeksData(firstWeekEnding, lastWeekEnding, projects, projectStatuses, projectTypes, typeInt, mode != ForecastDashboardMode.MainDashboard, isLaborMode, filterGroups);
|
|
}
|
|
else
|
|
{
|
|
//ENV-848. As our StartDate's month END will be the very first X point on the graph, it looks like graph is starting with next month, so let's gather date for month before the start date
|
|
StartDate = StartDate.AddMonths(-1);
|
|
|
|
fiscalCalendarChartPoints = (from f0 in db.FiscalCalendars
|
|
from f1 in db.FiscalCalendars
|
|
where f0.Type == (int)FiscalCalendarModel.FiscalYearType.Week && f1.Type == (int)FiscalCalendarModel.FiscalYearType.Month &&
|
|
f1.YearInt == f0.YearInt && f0.StartDate <= f1.EndDate && f0.EndDate >= f1.EndDate &&
|
|
f0.EndDate >= StartDate && f0.EndDate <= EndDate && f0.AdjustingPeriod == false && f0.NonWorking == 0
|
|
select f0.EndDate).ToArray().Select(t => (long)t.Subtract(Constants.UnixEpochDate).TotalMilliseconds).ToArray();
|
|
if (fiscalCalendarChartPoints.Length > 0)
|
|
{
|
|
firstWeekEnding = Constants.UnixEpochDate.AddMilliseconds(fiscalCalendarChartPoints[0]);
|
|
lastWeekEnding = Constants.UnixEpochDate.AddMilliseconds(fiscalCalendarChartPoints[fiscalCalendarChartPoints.Length - 1]);
|
|
}
|
|
fiscalCalendarWeekendings.AddRange(fiscalCalendarChartPoints.Select(i => Constants.UnixEpochDate.AddMilliseconds(i)));
|
|
data = GetMonthData(projects, projectStatuses, projectTypes, typeInt, mode != ForecastDashboardMode.MainDashboard, isLaborMode, filterGroups, fiscalCalendarWeekendings);
|
|
}
|
|
var projectIds = projects.Select(t => t.Id).ToArray();
|
|
var allVacations = (from vacWeek in db.PeopleResourceVacations
|
|
join vac in db.Vacations on vacWeek.VacationId equals vac.Id
|
|
join p in db.PeopleResources on vac.PeopleResourceId equals p.Id
|
|
join t in db.Team2Project on p.TeamId equals t.TeamId
|
|
where (teams.Count == 0 || (p.TeamId.HasValue && teams.Contains(p.TeamId.Value))) && projectIds.Contains(t.ProjectId) && expCatGuids.Contains(p.ExpenditureCategoryId)
|
|
select new
|
|
{
|
|
vacWeek.Id,
|
|
vacWeek.WeekEndingDate,
|
|
vacWeek.HoursOff,
|
|
Rate = db.Rates.FirstOrDefault(r => r.ExpenditureCategoryId == p.ExpenditureCategoryId && r.StartDate <= vacWeek.WeekEndingDate && r.EndDate >= vacWeek.WeekEndingDate)
|
|
}).Distinct().ToArray();
|
|
|
|
var allTrainings = (from traningWeek in db.PeopleResourceTrainings
|
|
join training in db.Trainings on traningWeek.TrainingId equals training.Id
|
|
join p in db.PeopleResources on traningWeek.PeopleResourceId equals p.Id
|
|
join t in db.Team2Project on p.TeamId equals t.TeamId
|
|
where (teams.Count == 0 || (p.TeamId.HasValue && teams.Contains(p.TeamId.Value))) && projectIds.Contains(t.ProjectId) && expCatGuids.Contains(p.ExpenditureCategoryId)
|
|
select new
|
|
{
|
|
traningWeek.Id,
|
|
traningWeek.WeekEndingDate,
|
|
traningWeek.HoursOff,
|
|
Rate = db.Rates.FirstOrDefault(r => r.ExpenditureCategoryId == p.ExpenditureCategoryId && r.StartDate <= traningWeek.WeekEndingDate && r.EndDate >= traningWeek.WeekEndingDate)
|
|
}).Distinct().ToArray();
|
|
|
|
//init result dictionary
|
|
if (mode == ForecastDashboardMode.MainDashboard)
|
|
{
|
|
|
|
result.Add("plannedCapacity", new List<long[]>());
|
|
result.Add("plannedCapacityQ", new List<long[]>());
|
|
}
|
|
result.Add("training", new List<long[]>());
|
|
result.Add("trainingQ", new List<long[]>());
|
|
result.Add("vacation", new List<long[]>());
|
|
result.Add("vacationQ", new List<long[]>());
|
|
result.Add("totalPlannedCapacity", new List<long[]>());
|
|
result.Add("totalPlannedCapacityQ", new List<long[]>());
|
|
result.Add("resourceCapacity", new List<long[]>());
|
|
result.Add("resourceCapacityQ", new List<long[]>());
|
|
//capacity and project status data remains uninitialized as we do not know what capacity scenarios and project statuses we have
|
|
|
|
List<Data> dataList = data.ToList();
|
|
long wkending = dataList.Count > 0 ? (long)dataList[0].weekend.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds : 0;
|
|
Dictionary<string, decimal> tmpProjectStatus = new Dictionary<string, decimal>();
|
|
Dictionary<string, decimal> tmpCapacity = new Dictionary<string, decimal>();
|
|
|
|
Dictionary<DateTime, PeopleResourceModel.CapacityValues> totalActualCapacity = new Dictionary<DateTime,PeopleResourceModel.CapacityValues>();
|
|
//if (mode == ForecastDashboardMode.MainDashboard)
|
|
// totalActualCapacity = GetWeeklyTeamCapacity(teams, expCats, uoms, isUOMHours, false, fiscalCalendarWeekendings);
|
|
//else if (mode == ForecastDashboardMode.TeamForecast)
|
|
// totalActualCapacity = GetWeeklyTeamCapacity(teams, expCats, uoms, isUOMHours, false, fiscalCalendarWeekendings);
|
|
//else if (mode == ForecastDashboardMode.ViewForecast)
|
|
// totalActualCapacity = GetWeeklyViewCapacity(views, expCats, uoms, isUOMHours, false, fiscalCalendarWeekendings);
|
|
|
|
Dictionary<DateTime, PeopleResourceModel.CapacityValues> totalPlannedCapacity = new Dictionary<DateTime, PeopleResourceModel.CapacityValues>();
|
|
//if (mode == ForecastDashboardMode.MainDashboard)
|
|
// totalPlannedCapacity = GetWeeklyTeamCapacity(teams, expCats, uoms, isUOMHours, true, fiscalCalendarWeekendings);
|
|
//else if (mode == ForecastDashboardMode.TeamForecast)
|
|
// totalPlannedCapacity = GetWeeklyTeamCapacity(teams, expCats, uoms, isUOMHours, true, fiscalCalendarWeekendings);
|
|
//else if (mode == ForecastDashboardMode.ViewForecast)
|
|
// totalPlannedCapacity = GetWeeklyViewCapacity(views, expCats, uoms, isUOMHours, true, fiscalCalendarWeekendings);
|
|
|
|
Guid[] plannedScenarios = new Guid[0];
|
|
Guid[] actualScenarios = new Guid[0];
|
|
if (mode == ForecastDashboardMode.MainDashboard || mode == ForecastDashboardMode.TeamForecast)
|
|
{
|
|
plannedScenarios = (from s in db.Teams where teams.Contains(s.Id) && s.PlannedCapacityScenarioId != null select s.PlannedCapacityScenarioId.Value).ToArray();
|
|
actualScenarios = (from s in db.Teams where teams.Contains(s.Id) && s.ActualCapacityScenarioId != null select s.ActualCapacityScenarioId.Value).ToArray();
|
|
}
|
|
else if (mode == ForecastDashboardMode.ViewForecast)
|
|
{
|
|
plannedScenarios = (from s in db.Teams
|
|
join tv in db.Team2View on s.Id equals tv.TeamId
|
|
where views.Contains(tv.ViewId) && s.PlannedCapacityScenarioId != null
|
|
select s.PlannedCapacityScenarioId.Value).ToArray();
|
|
actualScenarios = (from s in db.Teams
|
|
join tv in db.Team2View on s.Id equals tv.TeamId
|
|
where views.Contains(tv.ViewId) && s.ActualCapacityScenarioId != null
|
|
select s.ActualCapacityScenarioId.Value).ToArray();
|
|
}
|
|
|
|
Guid[] expCatIds = expCats.Keys.ToArray();
|
|
//var sdsP = (from sd in db.ScenarioDetail where plannedScenarios.Contains(sd.ParentID.Value) && fiscalCalendarWeekendings.Contains(sd.WeekEndingDate.Value) && expCatIds.Contains(sd.ExpenditureCategoryId.Value) select sd).ToDictionary( x=> x.WeekEndingDate, x => Sum(x.Cost));
|
|
//var sdsA = (from sd in db.ScenarioDetail where actualScenarios.Contains(sd.ParentID.Value) && fiscalCalendarWeekendings.Contains(sd.WeekEndingDate.Value) && expCatIds.Contains(sd.ExpenditureCategoryId.Value) select sd).ToDictionary(x => x.WeekEndingDate, x => x);
|
|
|
|
Dictionary<DateTime?, Dictionary<Guid?, List<ScenarioDetail>>> plannedCapacityDetails =
|
|
(from sdPlan in db.ScenarioDetail
|
|
where plannedScenarios.Contains(sdPlan.ParentID.Value) && fiscalCalendarWeekendings.Contains(sdPlan.WeekEndingDate.Value) && expCatIds.Contains(sdPlan.ExpenditureCategoryId.Value)
|
|
select sdPlan).GroupBy(sd => sd.WeekEndingDate).ToDictionary(k1 => k1.Key, g1 => g1.GroupBy(sd2 => sd2.ExpenditureCategoryId).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
Dictionary<DateTime?, Dictionary<Guid?, List<ScenarioDetail>>> actualCapacityDetails =
|
|
(from sdAct in db.ScenarioDetail
|
|
where actualScenarios.Contains(sdAct.ParentID.Value) && fiscalCalendarWeekendings.Contains(sdAct.WeekEndingDate.Value) && expCatIds.Contains(sdAct.ExpenditureCategoryId.Value)
|
|
select sdAct).GroupBy(sd => sd.WeekEndingDate).ToDictionary(k1 => k1.Key, g1 => g1.GroupBy(sd2 => sd2.ExpenditureCategoryId).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
|
|
for (int i = 0; i < dataList.Count; i++)
|
|
{
|
|
|
|
var week = dataList[i].weekend.Value;
|
|
if (!totalPlannedCapacity.ContainsKey(week))
|
|
{
|
|
var quantity = 0M;
|
|
var cost = 0M;
|
|
if (plannedCapacityDetails.ContainsKey(week))
|
|
{
|
|
var sdP = plannedCapacityDetails[week];// (from s in sdsP where s.WeekEndingDate == week select s).ToList();
|
|
foreach (var s in sdP)
|
|
{
|
|
quantity += s.Value.Sum(x => x.Quantity.Value) * Utils.GetUOMMultiplier(expCats, uoms, s.Key ?? Guid.Empty, isUOMHours);
|
|
cost += s.Value.Sum(x => x.Cost.Value);
|
|
}
|
|
}
|
|
totalPlannedCapacity.Add(week, new PeopleResourceModel.CapacityValues()
|
|
{
|
|
Quantity = quantity,
|
|
Cost = cost,
|
|
WeekEnding = week
|
|
});
|
|
}
|
|
if (!totalActualCapacity.ContainsKey(week))
|
|
{
|
|
var quantity = 0M;
|
|
var cost = 0M;
|
|
if (actualCapacityDetails.ContainsKey(week))
|
|
{
|
|
var sdA = actualCapacityDetails[week];//(from s in sdsA where s.WeekEndingDate == week select s).ToList();
|
|
foreach (var s in sdA)
|
|
{
|
|
quantity += s.Value.Sum(x => x.Quantity.Value) * Utils.GetUOMMultiplier(expCats, uoms, s.Key ?? Guid.Empty, isUOMHours);
|
|
cost += s.Value.Sum(x => x.Cost.Value);
|
|
}
|
|
}
|
|
totalActualCapacity.Add(week, new PeopleResourceModel.CapacityValues()
|
|
{
|
|
Quantity = quantity,
|
|
Cost = cost,
|
|
WeekEnding = week
|
|
});
|
|
}
|
|
|
|
Data sd = dataList[i];
|
|
|
|
//fill data into tmps
|
|
if (!sd.type.HasValue)
|
|
continue;
|
|
|
|
if ((int)ScenarioType.Portfolio == sd.type.Value || (int)ScenarioType.Scheduling == sd.type.Value)
|
|
{
|
|
//the dataset is already filtered by correct scenario type so we're just summarizing the numbers here
|
|
if (!tmpProjectStatus.ContainsKey(sd.projectStatus))
|
|
{
|
|
tmpProjectStatus.Add(sd.projectStatus, 0.0M);
|
|
tmpProjectStatus.Add(sd.projectStatus + "Q", 0.0M);
|
|
}
|
|
|
|
tmpProjectStatus[sd.projectStatus] += sd.cost ?? 0;
|
|
tmpProjectStatus[sd.projectStatus + "Q"] += (sd.quantity ?? 0) * Utils.GetUOMMultiplier(expCats, uoms, sd.ExpCatId ?? Guid.Empty, isUOMHours);
|
|
}
|
|
else if ((int)ScenarioType.Capacity == sd.type.Value)
|
|
{
|
|
if (!tmpCapacity.ContainsKey(sd.Name))
|
|
{
|
|
tmpCapacity.Add(sd.Name, 0.0M);
|
|
tmpCapacity.Add(sd.Name + "Q", 0.0M);
|
|
}
|
|
|
|
tmpCapacity[sd.Name] += sd.cost ?? 0;
|
|
tmpCapacity[sd.Name + "Q"] += (sd.quantity ?? 0) * Utils.GetUOMMultiplier(expCats, uoms, sd.ExpCatId ?? Guid.Empty, isUOMHours);
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException("Incorrect scenario type found");
|
|
}
|
|
|
|
if (i == dataList.Count - 1 || (i < dataList.Count - 1 && (long)dataList[i+1].weekend.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds != wkending))
|
|
{
|
|
//new week, push result data and reinit temps
|
|
if (0 != wkending)
|
|
{
|
|
if (mode == ForecastDashboardMode.MainDashboard)
|
|
{
|
|
foreach (string cn in tmpCapacity.Keys)
|
|
{
|
|
if (!result.ContainsKey(cn))
|
|
{
|
|
result.Add(cn, new List<long[]>());
|
|
}
|
|
result[cn].Add(new long[] { wkending, (long)(Math.Round(tmpCapacity[cn])) });
|
|
}
|
|
}
|
|
//TRAININGS
|
|
var ed = Constants.UnixEpochDate.AddMilliseconds(wkending);
|
|
var startd = ed.AddDays(-6);
|
|
decimal tmpTrainingQ = allTrainings.Where(t => t.WeekEndingDate <= ed && t.WeekEndingDate >= startd).Sum(t => t.HoursOff);
|
|
result["trainingQ"].Add(new long[] { wkending, (long)(tmpTrainingQ * Utils.GetUOMMultiplier(expCats, uoms, sd.ExpCatId ?? Guid.Empty, isUOMHours)) });
|
|
|
|
decimal tmpTraining = allTrainings.Where(t => t.WeekEndingDate <= ed && t.WeekEndingDate >= startd).Sum(t => t.HoursOff * (t.Rate != null ? t.Rate.Rate1 : 0));
|
|
result["training"].Add(new long[] { wkending, (long)tmpTraining });
|
|
|
|
//VACATIONS
|
|
ed = Constants.UnixEpochDate.AddMilliseconds(wkending);
|
|
startd = ed.AddDays(-6);
|
|
decimal tmpVacationQ = allVacations.Where(t => t.WeekEndingDate <= ed && t.WeekEndingDate >= startd).Sum(t => t.HoursOff);
|
|
result["vacationQ"].Add(new long[] { wkending, (long)(tmpVacationQ * Utils.GetUOMMultiplier(expCats, uoms, sd.ExpCatId ?? Guid.Empty, isUOMHours)) });
|
|
|
|
decimal tmpVacation = allVacations.Where(t => t.WeekEndingDate <= ed && t.WeekEndingDate >= startd).Sum(t => t.HoursOff * (t.Rate != null ? t.Rate.Rate1 : 0));
|
|
result["vacation"].Add(new long[] { wkending, (long)tmpVacation });
|
|
|
|
foreach (string sn in tmpProjectStatus.Keys)
|
|
{
|
|
if (!result.ContainsKey(sn))
|
|
{
|
|
result.Add(sn, new List<long[]>());
|
|
}
|
|
result[sn].Add(new long[] { wkending, (long)(Math.Round(tmpProjectStatus[sn])) });
|
|
}
|
|
}
|
|
if (i < dataList.Count - 1)
|
|
wkending = (long)dataList[i + 1].weekend.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
tmpProjectStatus = new Dictionary<string, decimal>();
|
|
tmpCapacity = new Dictionary<string, decimal>();
|
|
}
|
|
}
|
|
|
|
|
|
result["resourceCapacity"].AddRange(totalActualCapacity.Select(x => new long[]
|
|
{
|
|
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
(long)(Math.Round(x.Value.Cost))
|
|
}));
|
|
result["resourceCapacityQ"].AddRange(totalActualCapacity.Select(x => new long[]
|
|
{
|
|
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
(long)(Math.Round(x.Value.Quantity))
|
|
}));
|
|
|
|
|
|
result["totalPlannedCapacity"].AddRange(totalPlannedCapacity.Select(x => new long[]
|
|
{
|
|
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
(long)(Math.Round(x.Value.Cost))
|
|
}));
|
|
result["totalPlannedCapacityQ"].AddRange(totalPlannedCapacity.Select(x => new long[]
|
|
{
|
|
(long)x.Key.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
(long)(Math.Round(x.Value.Quantity))
|
|
}));
|
|
|
|
//Fill missing weekends for all items in dictionary
|
|
foreach (string key in result.Keys)
|
|
{
|
|
List<long[]> lst = FillMissingWeeends(result[key], fiscalCalendarChartPoints);
|
|
result[key].Clear();
|
|
result[key].AddRange(lst);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get projects, filtered by user access permissions and given filter view or team (depends on page mode)
|
|
/// </summary>
|
|
/// <param name="mode">Forecast page display mode</param>
|
|
/// <param name="userId">User id</param>
|
|
/// <param name="filterItemId">Id of view or team to filter projects (depends on page mode)</param>
|
|
/// <param name="teams">Returning list of teams, discovered during filtration (e.g. for the filtering by given view Id)</param>
|
|
/// <returns>Available for given user and team (view teams) projects</returns>
|
|
/// <remarks>SA. ENV-608</remarks>
|
|
private IQueryable<Project> GetFilteredProjectList(Guid userId, List<Guid> teams, List<Guid> views)
|
|
{
|
|
var filteredTeams = (new TeamManager(db)).GetTeamsByUser(userId.ToString(), teams, views);
|
|
teams.AddRange(filteredTeams.Where(x => !teams.Any(t => t == x.Id)).Select(x => x.Id));
|
|
|
|
// Get list of project, that are available to given user, filtered for given team or view
|
|
IQueryable<Project> projects =
|
|
(from c in db.Projects.AsNoTracking()
|
|
join o in db.VW_ProjectAccessByUser on c.Id equals o.Id
|
|
where o.UserId.Equals(userId) && c.Team2Project.Any(x => teams.Contains(x.TeamId))
|
|
select c);
|
|
|
|
return projects;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns current, next and previous fiscal periods of given type for the datepoint
|
|
/// </summary>
|
|
/// <param name="datePoint"></param>
|
|
/// <param name="periodType"></param>
|
|
/// <returns>Returns periods, if exist (-1 = prev, 0 = current, 1 = next)</returns>
|
|
/// <remarks>SA. ENV-608</remarks>
|
|
private Dictionary<int, Guid> GetClosedToDateFiscalPeriods(DateTime datePoint,
|
|
FiscalCalendarModel.FiscalYearType periodType)
|
|
{
|
|
Dictionary<int, Guid> result = new Dictionary<int, Guid>();
|
|
DateTime viewRangeStartDate = datePoint.AddYears(-2);
|
|
DateTime viewRangeEndDate = datePoint.AddYears(2);
|
|
|
|
List<FiscalCalendar> periods =
|
|
(from FiscalCalendar period in db.FiscalCalendars.AsNoTracking()
|
|
orderby period.StartDate
|
|
where (period.Type == (int)periodType) && (period.StartDate >= viewRangeStartDate) &&
|
|
(period.EndDate <= viewRangeEndDate)
|
|
select period).ToList();
|
|
|
|
int periodCount = periods.Count;
|
|
FiscalCalendar periodItem;
|
|
|
|
for (int index = 0; index < periodCount; index++)
|
|
{
|
|
periodItem = periods[index];
|
|
|
|
if ((periodItem.StartDate <= datePoint) && (periodItem.EndDate >= datePoint))
|
|
{
|
|
result.Add(0, periodItem.Id);
|
|
|
|
if (index > 0)
|
|
result.Add(-1, periods[index - 1].Id);
|
|
|
|
if ((index + 1) < periodCount)
|
|
result.Add(1, periods[index + 1].Id);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns top 5 projects performance data by cost
|
|
/// </summary>
|
|
/// <param name="mode">Page display mode (general dashboard, team or view dashboard)</param>
|
|
/// <param name="userId"></param>
|
|
/// <param name="datePoint"></param>
|
|
/// <param name="filterItemId">Team or View id to filter projects</param>
|
|
/// <returns></returns>
|
|
/// <remarks>SA. ENV-608</remarks>
|
|
public Dictionary<string, StackGraphDataBlock> GetPerformanceGraphDataByProjects(ForecastDashboardMode mode, Guid userId, DateTime datePoint, List<Guid> filterGroups, List<Guid> projectTypes, List<Guid> teams, List<Guid> views)
|
|
{
|
|
var result = new Dictionary<string, StackGraphDataBlock>();
|
|
|
|
StackGraphDataBlock singleGraphDataBlock;
|
|
StackGraphSerie forecastDataSerie;
|
|
StackGraphSerie variationsPosDataSerie;
|
|
StackGraphSerie variationsNegDataSerie;
|
|
StackGraphSerie actualsDataSerie;
|
|
StackGraphBarOptions barTitles;
|
|
|
|
// Get list of project, that are available to given user, filtered for given team or view
|
|
IQueryable<Project> projects = GetFilteredProjectList(userId, teams, views);
|
|
List<Guid> projectFilter = projects.Select(x => x.Id).ToList();
|
|
|
|
// Get current, prev and following fiscal periods
|
|
Dictionary<int, Guid> fiscalPeriods =
|
|
GetClosedToDateFiscalPeriods(datePoint, FiscalCalendarModel.FiscalYearType.Quarter);
|
|
|
|
Dictionary<int, string> resultKeys = new Dictionary<int, string>();
|
|
resultKeys.Add(-1, "PrevPeriod");
|
|
resultKeys.Add(0, "CurrentPeriod");
|
|
resultKeys.Add(1, "NextPeriod");
|
|
|
|
for (int index = -1; index <= 1; index++)
|
|
{
|
|
if (fiscalPeriods.ContainsKey(index))
|
|
{
|
|
Guid currentPeriodId = fiscalPeriods[index];
|
|
var query = db.VW_ProjectPerformance.AsNoTracking().OrderByDescending(row => row.ProjectWeight).Where(row => currentPeriodId.Equals(row.FiscalPeriodId) &&
|
|
projectFilter.Contains(row.ProjectId) && row.BUDirectCosts.HasValue);
|
|
if (filterGroups != null && filterGroups.Count > 0)
|
|
query = query.Where(row => row.ScenarioGroupId.HasValue && filterGroups.Contains(row.ScenarioGroupId.Value));
|
|
if (projectTypes != null && projectTypes.Count > 0)
|
|
query = query.Where(row => projectTypes.Contains(row.ProjectTypeId));
|
|
var projectRows =
|
|
(from VW_ProjectPerformance row in query
|
|
select row).Take(5);
|
|
|
|
|
|
int rowIndex = 1;
|
|
singleGraphDataBlock = new StackGraphDataBlock();
|
|
forecastDataSerie = new StackGraphSerie();
|
|
variationsPosDataSerie = new StackGraphSerie();
|
|
variationsNegDataSerie = new StackGraphSerie();
|
|
barTitles = new StackGraphBarOptions();
|
|
actualsDataSerie = new StackGraphSerie();
|
|
|
|
foreach (VW_ProjectPerformance row in projectRows)
|
|
{
|
|
long graphPoint = rowIndex;
|
|
|
|
if (row.ActualsCostVariation.HasValue)
|
|
{
|
|
if (row.ActualsCostVariation.Value > 0)
|
|
{
|
|
// Actual costs lager Forecast costs
|
|
forecastDataSerie.data.AddValue(graphPoint, row.BUDirectCosts.Value);
|
|
variationsPosDataSerie.data.AddValue(graphPoint, row.ActualsCostVariation.Value);
|
|
variationsNegDataSerie.data.AddValue(graphPoint, 0);
|
|
}
|
|
else
|
|
{
|
|
// Actual costs less Forecast costs
|
|
forecastDataSerie.data.AddValue(graphPoint, row.BUDirectCosts.Value + row.ActualsCostVariation.Value); // ActualsCostVariation is negative value
|
|
variationsPosDataSerie.data.AddValue(graphPoint, 0);
|
|
variationsNegDataSerie.data.AddValue(graphPoint, -row.ActualsCostVariation.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No actauls
|
|
forecastDataSerie.data.AddValue(graphPoint, row.BUDirectCosts.Value);
|
|
variationsPosDataSerie.data.AddValue(graphPoint, 0);
|
|
variationsNegDataSerie.data.AddValue(graphPoint, 0);
|
|
}
|
|
|
|
// Store actuals data to virtual serie
|
|
if (row.ActualsTotalCost.HasValue)
|
|
actualsDataSerie.data.AddValue(graphPoint, row.ActualsTotalCost.Value);
|
|
else
|
|
actualsDataSerie.data.AddValue(graphPoint, 0);
|
|
|
|
// Store project name
|
|
barTitles.AddValue(graphPoint, row.ProjectName);
|
|
rowIndex++;
|
|
}
|
|
|
|
singleGraphDataBlock.series.Add(forecastDataSerie);
|
|
singleGraphDataBlock.series.Add(variationsPosDataSerie);
|
|
singleGraphDataBlock.series.Add(variationsNegDataSerie);
|
|
singleGraphDataBlock.series.Add(actualsDataSerie);
|
|
singleGraphDataBlock.barOptions = barTitles;
|
|
|
|
string dataKey = resultKeys[index];
|
|
result.Add(dataKey, singleGraphDataBlock);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns top 5 projects performance data by cost
|
|
/// </summary>
|
|
/// <param name="mode">Page display mode (general dashboard, team or view dashboard)</param>
|
|
/// <param name="userId"></param>
|
|
/// <param name="datePoint"></param>
|
|
/// <param name="filterItemId">Team or View id to filter projects</param>
|
|
/// <returns></returns>
|
|
/// <remarks>SA. ENV-608</remarks>
|
|
public Dictionary<string, StackGraphDataBlock>
|
|
GetPerformanceGraphDataByProjectClass(ForecastDashboardMode mode, Guid userId, DateTime datePoint,
|
|
List<Guid> filterGroups, List<Guid> projectTypes, List<Guid> teams, List<Guid> views)
|
|
{
|
|
var result = new Dictionary<string, StackGraphDataBlock>();
|
|
|
|
StackGraphDataBlock singleGraphDataBlock;
|
|
StackGraphSerie forecastDataSerie;
|
|
StackGraphSerie variationsPosDataSerie;
|
|
StackGraphSerie variationsNegDataSerie;
|
|
StackGraphSerie actualsDataSerie;
|
|
StackGraphBarOptions barTitles;
|
|
|
|
// Get list of project, that are available to given user, filtered for given team or view
|
|
IQueryable<Project> projects = GetFilteredProjectList(userId, teams, views);
|
|
List<Guid> projectFilter = projects.Select(x => x.Id).ToList();
|
|
|
|
// Get current, prev and following fiscal periods
|
|
Dictionary<int, Guid> fiscalPeriods =
|
|
GetClosedToDateFiscalPeriods(datePoint, FiscalCalendarModel.FiscalYearType.Quarter);
|
|
|
|
Dictionary<int, string> resultKeys = new Dictionary<int, string>();
|
|
resultKeys.Add(-1, "PrevPeriod");
|
|
resultKeys.Add(0, "CurrentPeriod");
|
|
resultKeys.Add(1, "NextPeriod");
|
|
|
|
for (int index = -1; index <= 1; index++)
|
|
{
|
|
if (fiscalPeriods.ContainsKey(index))
|
|
{
|
|
Guid currentPeriodId = fiscalPeriods[index];
|
|
|
|
var query = db.VW_ProjectPerformance.AsNoTracking().Where(x => currentPeriodId.Equals(x.FiscalPeriodId) && projectFilter.Contains(x.ProjectId) && x.BUDirectCosts.HasValue);
|
|
if (filterGroups != null && filterGroups.Count > 0)
|
|
query = query.Where(x => x.ScenarioGroupId.HasValue && filterGroups.Contains(x.ScenarioGroupId.Value));
|
|
if (projectTypes != null && projectTypes.Count > 0)
|
|
query = query.Where(row => projectTypes.Contains(row.ProjectTypeId));
|
|
var projectClassRows =
|
|
(from VW_ProjectPerformance row in query
|
|
group row by new { row.ProjectTypeName }
|
|
into grp
|
|
select new
|
|
{
|
|
grp.Key.ProjectTypeName,
|
|
BUDirectCosts = grp.Sum(x => x.BUDirectCosts),
|
|
ActualsCostVariation = grp.Sum(x => x.ActualsCostVariation.HasValue ? x.ActualsCostVariation : 0),
|
|
ActualsTotalCosts = grp.Sum(x => x.ActualsTotalCost.HasValue ? x.ActualsTotalCost : 0),
|
|
ProjectsCount = grp.Count()
|
|
}).OrderByDescending(x => x.BUDirectCosts).Take(5).ToList();
|
|
|
|
int rowIndex = 1;
|
|
singleGraphDataBlock = new StackGraphDataBlock();
|
|
forecastDataSerie = new StackGraphSerie();
|
|
variationsPosDataSerie = new StackGraphSerie();
|
|
variationsNegDataSerie = new StackGraphSerie();
|
|
barTitles = new StackGraphBarOptions();
|
|
actualsDataSerie = new StackGraphSerie();
|
|
|
|
foreach (var row in projectClassRows)
|
|
{
|
|
long graphPoint = rowIndex;
|
|
|
|
if (row.ActualsCostVariation.HasValue)
|
|
{
|
|
if (row.ActualsCostVariation.Value > 0)
|
|
{
|
|
// Actual costs lager Forecast costs
|
|
forecastDataSerie.data.AddValue(graphPoint, row.BUDirectCosts.Value);
|
|
variationsPosDataSerie.data.AddValue(graphPoint, row.ActualsCostVariation.Value);
|
|
variationsNegDataSerie.data.AddValue(graphPoint, 0);
|
|
}
|
|
else
|
|
{
|
|
// Actual costs less Forecast costs
|
|
forecastDataSerie.data.AddValue(graphPoint, row.BUDirectCosts.Value + row.ActualsCostVariation.Value); // ActualsCostVariation is negative value
|
|
variationsPosDataSerie.data.AddValue(graphPoint, 0);
|
|
variationsNegDataSerie.data.AddValue(graphPoint, -row.ActualsCostVariation.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No actauls
|
|
forecastDataSerie.data.AddValue(graphPoint, row.BUDirectCosts.Value);
|
|
variationsPosDataSerie.data.AddValue(graphPoint, 0);
|
|
variationsNegDataSerie.data.AddValue(graphPoint, 0);
|
|
}
|
|
|
|
// Store actuals data to virtual serie
|
|
if (row.ActualsTotalCosts.HasValue)
|
|
actualsDataSerie.data.AddValue(graphPoint, row.ActualsTotalCosts.Value);
|
|
else
|
|
actualsDataSerie.data.AddValue(graphPoint, 0);
|
|
|
|
// Store project name
|
|
barTitles.AddValue(graphPoint, row.ProjectTypeName);
|
|
rowIndex++;
|
|
}
|
|
|
|
singleGraphDataBlock.series.Add(forecastDataSerie);
|
|
singleGraphDataBlock.series.Add(variationsPosDataSerie);
|
|
singleGraphDataBlock.series.Add(variationsNegDataSerie);
|
|
singleGraphDataBlock.series.Add(actualsDataSerie);
|
|
singleGraphDataBlock.barOptions = barTitles;
|
|
|
|
string dataKey = resultKeys[index];
|
|
result.Add(dataKey, singleGraphDataBlock);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill absent weekends and normalize arrays
|
|
/// </summary>
|
|
private List<long[]> FillMissingWeeends(List<long[]> list, IEnumerable<long> weekEnds)
|
|
{
|
|
if (list.Count < weekEnds.Count())
|
|
{
|
|
var result = weekEnds.Except(list.Select(x => x[0]));
|
|
list.AddRange(result.Select(x => new long[] { x, 0 }));
|
|
list.RemoveAll(x => !weekEnds.Contains(x[0]));
|
|
}
|
|
return list.OrderBy(x => x[0]).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="StartDate"></param>
|
|
/// <param name="EndDate"></param>
|
|
/// <param name="projects"></param>
|
|
/// <param name="status"></param>
|
|
/// <param name="classification"></param>
|
|
/// <param name="scenarioType"></param>
|
|
/// <param name="noOldCapacities"></param>
|
|
/// <returns></returns>
|
|
private IQueryable<Data> GetWeeksData(DateTime firstWeekEnding, DateTime lastWeekEnding, IQueryable<Project> projects, List<Guid> projectStatuses, List<Guid> projectTypes, int scenarioType, bool noOldCapacities, bool isLaborMode, List<Guid> filterGroups)
|
|
{
|
|
if (projectStatuses != null && projectStatuses.Count > 0)
|
|
projects = projects.Where(x => projectStatuses.Contains(x.StatusId));
|
|
if (projectTypes != null && projectTypes.Count > 0)
|
|
projects = projects.Where(x => projectTypes.Contains(x.TypeId));
|
|
|
|
Guid[] projectIds = (from p in projects select p.Id).ToArray();
|
|
Guid[] expCatIds = null;
|
|
if (isLaborMode) expCatIds = (from e in db.ExpenditureCategory where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor select e.Id).ToArray();
|
|
else expCatIds = (from e in db.ExpenditureCategory where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor select e.Id).ToArray();
|
|
List<int> scTypes = new List<int>();
|
|
if (!noOldCapacities)
|
|
scTypes.AddRange(new int[] { (int)ScenarioType.Capacity, (int)ScenarioType.Vacation, (int)ScenarioType.Training });
|
|
if (scenarioType != 0)
|
|
scTypes.Add(scenarioType);
|
|
else
|
|
scTypes.Add((int)ScenarioType.Portfolio);
|
|
|
|
IQueryable<Data> data = null;
|
|
if (filterGroups != null && filterGroups.Count > 0)
|
|
{
|
|
data = (from sd in db.ScenarioDetail.AsNoTracking()
|
|
join s in db.Scenarios on sd.ParentID equals s.Id
|
|
join p in db.Projects on s.ParentId equals p.Id
|
|
where sd.WeekEndingDate >= firstWeekEnding && sd.WeekEndingDate <= lastWeekEnding
|
|
&& projectIds.Contains(s.ParentId.Value) && s.Status == (int?)ScenarioStatus.Active
|
|
&& scTypes.Contains(s.Type) && expCatIds.Contains(sd.ExpenditureCategoryId.Value) && s.Scenario2Group.Any(g => filterGroups.Contains(g.GroupId))
|
|
orderby sd.WeekEndingDate
|
|
select new Data
|
|
{
|
|
cost = sd.Cost,
|
|
quantity = sd.Quantity,
|
|
weekend = sd.WeekEndingDate,
|
|
projectStatus = projects.FirstOrDefault(x => x.Id == s.ParentId).Status.Name,
|
|
type = s.Type,
|
|
Name = s.Name,
|
|
ScenarioBUCost = (s.UseLMMargin ?? 0) == 1 ? s.BUDirectCosts_LM : s.BUDirectCosts,
|
|
ScenarioDuration = s.Duration ?? 0,
|
|
ExpCatId = sd.ExpenditureCategoryId,
|
|
ProjectId = s.ParentId.Value,
|
|
ProjectType = p.TypeId
|
|
});
|
|
}
|
|
else
|
|
{
|
|
data = (from sd in db.ScenarioDetail.AsNoTracking()
|
|
join s in db.Scenarios on sd.ParentID equals s.Id
|
|
join p in db.Projects on s.ParentId equals p.Id
|
|
where sd.WeekEndingDate >= firstWeekEnding && sd.WeekEndingDate <= lastWeekEnding
|
|
&& projectIds.Contains(s.ParentId.Value) && s.Status == (int?)ScenarioStatus.Active
|
|
&& scTypes.Contains(s.Type) && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
|
|
orderby sd.WeekEndingDate
|
|
select new Data
|
|
{
|
|
cost = sd.Cost,
|
|
quantity = sd.Quantity,
|
|
weekend = sd.WeekEndingDate,
|
|
projectStatus = projects.FirstOrDefault(x => x.Id == s.ParentId).Status.Name,
|
|
type = s.Type,
|
|
Name = s.Name,
|
|
ScenarioBUCost = (s.UseLMMargin ?? 0) == 1 ? s.BUDirectCosts_LM : s.BUDirectCosts,
|
|
ScenarioDuration = s.Duration ?? 0,
|
|
ExpCatId = sd.ExpenditureCategoryId,
|
|
ProjectType = p.TypeId
|
|
});
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="StartDate"></param>
|
|
/// <param name="EndDate"></param>
|
|
/// <param name="projects"></param>
|
|
/// <param name="status"></param>
|
|
/// <param name="classification"></param>
|
|
/// <param name="scenarioType"></param>
|
|
/// <param name="noOldCapacities"></param>
|
|
/// <param name="uomMultiplier">A value of resource capacity multiplier. E.g. if user wants to see data in hours, then <paramref name="uomMultiplier"/>
|
|
/// equals to 1. If user wants to see data in resources then <paramref name="uomMultiplier"/> equals to 1/40 = 0.025.</param>
|
|
/// <returns></returns>
|
|
private IQueryable<Data> GetMonthData(IQueryable<Project> projects, List<Guid> projectStatuses, List<Guid> projectTypes, int scenarioType, bool noOldCapacities, bool isLaborMode, List<Guid> filterGroups, List<DateTime> fiscalCalendarWeekendings)
|
|
{
|
|
if (projectStatuses != null && projectStatuses.Count > 0)
|
|
projects = projects.Where(x => projectStatuses.Contains(x.StatusId));
|
|
if (projectTypes != null && projectTypes.Count > 0)
|
|
projects = projects.Where(x => projectTypes.Contains(x.TypeId));
|
|
|
|
Guid[] projectIds = (from p in projects select p.Id).ToArray();
|
|
Guid[] expCatIds = null;
|
|
if (isLaborMode) expCatIds = (from e in db.ExpenditureCategory where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor select e.Id).ToArray();
|
|
else expCatIds = (from e in db.ExpenditureCategory where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor select e.Id).ToArray();
|
|
List<int> scTypes = new List<int>();
|
|
if (!noOldCapacities)
|
|
scTypes.AddRange(new int[] { (int)ScenarioType.Capacity, (int)ScenarioType.Vacation, (int)ScenarioType.Training });
|
|
|
|
if (scenarioType != 0)
|
|
scTypes.Add(scenarioType);
|
|
else
|
|
scTypes.Add((int)ScenarioType.Portfolio);
|
|
|
|
IQueryable<Data> data = null;
|
|
if (filterGroups != null && filterGroups.Count > 0)
|
|
{
|
|
data = (from sd in db.ScenarioDetail.AsNoTracking()
|
|
join s in db.Scenarios on sd.ParentID equals s.Id
|
|
join p in db.Projects on s.ParentId equals p.Id
|
|
where fiscalCalendarWeekendings.Contains(sd.WeekEndingDate.Value)
|
|
&& projectIds.Contains(s.ParentId.Value) && s.Status == (int?)ScenarioStatus.Active
|
|
&& scTypes.Contains(s.Type) && expCatIds.Contains(sd.ExpenditureCategoryId.Value) && s.Scenario2Group.Any(g => filterGroups.Contains(g.GroupId))
|
|
orderby sd.WeekEndingDate
|
|
select new Data
|
|
{
|
|
cost = sd.Cost,
|
|
quantity = sd.Quantity,
|
|
weekend = sd.WeekEndingDate,
|
|
projectStatus = projects.FirstOrDefault(x => x.Id == s.ParentId).Status.Name,
|
|
type = s.Type,
|
|
Name = s.Name,
|
|
ScenarioBUCost = (s.UseLMMargin ?? 0) == 1 ? s.BUDirectCosts_LM : s.BUDirectCosts,
|
|
ScenarioDuration = s.Duration ?? 0,
|
|
ExpCatId = sd.ExpenditureCategoryId,
|
|
ProjectId = s.ParentId.Value,
|
|
ProjectType = p.TypeId
|
|
});
|
|
}
|
|
else
|
|
{
|
|
data = (from sd in db.ScenarioDetail.AsNoTracking()
|
|
join s in db.Scenarios on sd.ParentID equals s.Id
|
|
join p in db.Projects on s.ParentId equals p.Id
|
|
where fiscalCalendarWeekendings.Contains(sd.WeekEndingDate.Value)
|
|
&& projectIds.Contains(s.ParentId.Value) && s.Status == (int?)ScenarioStatus.Active
|
|
&& scTypes.Contains(s.Type) && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
|
|
orderby sd.WeekEndingDate
|
|
select new Data
|
|
{
|
|
cost = sd.Cost,
|
|
quantity = sd.Quantity,
|
|
weekend = sd.WeekEndingDate,
|
|
projectStatus = projects.FirstOrDefault(x => x.Id == s.ParentId).Status.Name,
|
|
type = s.Type,
|
|
Name = s.Name,
|
|
ScenarioBUCost = (s.UseLMMargin ?? 0) == 1 ? s.BUDirectCosts_LM : s.BUDirectCosts,
|
|
ScenarioDuration = s.Duration ?? 0,
|
|
ExpCatId = sd.ExpenditureCategoryId,
|
|
ProjectId = s.ParentId.Value,
|
|
ProjectType = p.TypeId
|
|
});
|
|
}
|
|
return data;
|
|
}
|
|
|
|
public List<PieData> GetPieData(DateTime startDate, DateTime endDate, Guid userId, ForecastDashboardMode mode, List<Guid> teams, List<Guid> views, bool isLaborMode, List<Guid> filterGroups, List<Guid> projectTypes, List<Guid> projectStatuses, List<Guid> strategicGoals, string presetColor, bool chartModeCost)
|
|
{
|
|
var query = db.Projects.AsNoTracking().Where(x => !x.HasChildren);
|
|
if (projectStatuses != null && projectStatuses.Count > 0)
|
|
query = query.Where(x => projectStatuses.Contains(x.StatusId));
|
|
if (projectTypes != null && projectTypes.Count > 0)
|
|
query = query.Where(x => projectTypes.Contains(x.TypeId));
|
|
if (strategicGoals != null && strategicGoals.Count > 0)
|
|
{
|
|
var sgs = db.StrategicGoal2Project.Where(x => strategicGoals.Contains(x.StrategicGoalId)).Select(x => x.ProjectId).ToList();
|
|
query = query.Where(x => sgs.Contains(x.Id));
|
|
}
|
|
var filteredTeams = (new TeamManager(db)).GetTeamsByUser(userId.ToString(), teams, views);
|
|
teams.AddRange(filteredTeams.Where(x => !teams.Any(t => t == x.Id)).Select(x=> x.Id));
|
|
|
|
IQueryable<Project> projects = (from c in query
|
|
join o in db.VW_ProjectAccessByUser on c.Id equals o.Id
|
|
where o.UserId.Equals(userId) && c.Team2Project.Any(x => teams.Contains(x.TeamId))
|
|
select c);
|
|
|
|
var data = GetPieChartData(startDate, endDate.AddDays(7), projects, isLaborMode, filterGroups, chartModeCost).OrderByDescending(x => x.Value);
|
|
var retData = new List<PieData>();
|
|
var other = new List<PieData>();
|
|
var totalTime = data.Sum(x=>x.Value);
|
|
var total = 1.0M;
|
|
if (data.Count() == 1)
|
|
data.First().PresetColor = presetColor;
|
|
if (projectTypes.Count == 0 && totalTime > 0)
|
|
{
|
|
foreach (var item in data)
|
|
{
|
|
var percentage = item.Value / totalTime;
|
|
if (total > .15M)
|
|
{
|
|
retData.Add(item);
|
|
total -= percentage;
|
|
}
|
|
else{
|
|
other.Add(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//var perPrecent = data.Max(x => x.Time) / 100;
|
|
//retData = data.Where(x => x.Time >= perPrecent * 80).Take(2).ToList<PieData>();
|
|
//var existedTypeIds = retData.SelectMany(ex => ex.TypeId).ToList();
|
|
//var other = data.Where(x => !existedTypeIds.Contains(x.TypeId.FirstOrDefault()));
|
|
if (other.Count() > 2)
|
|
{
|
|
retData.Add(new PieData()
|
|
{
|
|
Label = "Other",
|
|
TypeId = other.SelectMany(x => x.TypeId).ToList(),
|
|
Value = other.Sum(x => x.Value)
|
|
});
|
|
}else{
|
|
retData = data.ToList<PieData>();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
retData = data.ToList<PieData>();
|
|
}
|
|
|
|
return retData;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="startDate">Start of reporting period.</param>
|
|
/// <param name="endDate">End of reporting period.</param>
|
|
/// <param name="projects">A queryable collection of projects to filter by.</param>
|
|
/// <param name="isLaborMode">Indicates whether to load data for labor or for materials expenditure categories.</param>
|
|
/// <returns>A list of data for each classification.</returns>
|
|
private List<PieData> GetPieChartData(DateTime startDate, DateTime endDate, IQueryable<Project> projects, bool isLaborMode, List<Guid> filterGroups, bool chartModeCost)
|
|
{
|
|
var projectIds = (from p in projects select p.Id).ToArray();
|
|
var expCatIds = isLaborMode
|
|
? (from e in db.ExpenditureCategory
|
|
where e.Type == (int)ExpenditureCategoryModel.CategoryTypes.Labor
|
|
select e.Id).ToArray()
|
|
: (from e in db.ExpenditureCategory
|
|
where e.Type != (int)ExpenditureCategoryModel.CategoryTypes.Labor
|
|
select e.Id).ToArray();
|
|
var scTypes = new List<int> { (int)ScenarioType.Portfolio, (int)ScenarioType.Scheduling };
|
|
var types = db.Types.AsNoTracking().ToDictionary(key => key.Id, elem => elem.Name);
|
|
var query = db.Scenarios.AsNoTracking().Where(x => x.Status == (int?)ScenarioStatus.Active);
|
|
if (filterGroups != null && filterGroups.Count > 0)
|
|
query = query.Where(x => filterGroups.Contains(x.Scenario2Group.FirstOrDefault().GroupId));
|
|
var data = (from sd in db.ScenarioDetail
|
|
join s in query on sd.ParentID equals s.Id
|
|
join p in db.Projects on s.ParentId equals p.Id
|
|
//where !(s.StartDate <= StartDate || s.EndDate >= EndDate)
|
|
where sd.WeekEndingDate >= startDate && sd.WeekEndingDate <= endDate
|
|
&& projectIds.Contains(s.ParentId.Value)
|
|
&& scTypes.Contains(s.Type) && expCatIds.Contains(sd.ExpenditureCategoryId.Value)
|
|
select new
|
|
{
|
|
s.Id,
|
|
s.UseLMMargin,
|
|
s.BUDirectCosts_LM,
|
|
s.BUDirectCosts,
|
|
s.Duration,
|
|
p.TypeId,
|
|
}).Distinct().ToArray().GroupBy(group => group.TypeId).Select(grouping => new PieData
|
|
{
|
|
TypeId = new List<Guid>(){grouping.Key},
|
|
Value = (chartModeCost) ? grouping.Sum(x => (x.UseLMMargin ?? 0) == 1 ? x.BUDirectCosts_LM ?? 0 : x.BUDirectCosts ?? 0) : grouping.Sum(x => x.Duration ?? 0),
|
|
Label = types.ContainsKey(grouping.Key) ? types[grouping.Key] : string.Empty
|
|
}).ToList();
|
|
return data;
|
|
}
|
|
|
|
private Dictionary<DateTime, PeopleResourceModel.CapacityValues> GetWeeklyViewCapacity(List<Guid> views, Dictionary<Guid, ExpenditureCategory> expCats, Dictionary<Guid, UOM> uoms, bool? isUOMMode, bool isPlanned, List<DateTime> fiscalCalendarWeekendings)
|
|
{
|
|
if (views == null || views.Count <= 0)
|
|
return new Dictionary<DateTime, PeopleResourceModel.CapacityValues>();
|
|
|
|
var context = new EnVisageEntities();
|
|
Guid[] scenarios;
|
|
if (isPlanned) scenarios = (from s in context.Teams
|
|
join tv in context.Team2View on s.Id equals tv.TeamId
|
|
where views.Contains(tv.ViewId) && s.PlannedCapacityScenarioId != null
|
|
select s.PlannedCapacityScenarioId.Value).ToArray();
|
|
else scenarios = (from s in context.Teams
|
|
join tv in context.Team2View on s.Id equals tv.TeamId
|
|
where views.Contains(tv.ViewId) && s.ActualCapacityScenarioId != null
|
|
select s.ActualCapacityScenarioId.Value).ToArray();
|
|
return GetWeeklyCapacity(scenarios, expCats, uoms, isUOMMode, fiscalCalendarWeekendings);
|
|
}
|
|
|
|
private Dictionary<DateTime, PeopleResourceModel.CapacityValues> GetWeeklyTeamCapacity(List<Guid> TeamIds, Dictionary<Guid, ExpenditureCategory> expCats, Dictionary<Guid, UOM> uoms, bool? isUOMMode, bool isPlanned, List<DateTime> fiscalCalendarWeekendings)
|
|
{
|
|
if (TeamIds == null || TeamIds.Count <= 0)
|
|
return new Dictionary<DateTime, PeopleResourceModel.CapacityValues>();
|
|
|
|
var context = new EnVisageEntities();
|
|
Guid[] scenarios;
|
|
if (isPlanned) scenarios = (from s in context.Teams where TeamIds.Contains(s.Id) && s.PlannedCapacityScenarioId != null select s.PlannedCapacityScenarioId.Value).ToArray();
|
|
else scenarios = (from s in context.Teams where TeamIds.Contains(s.Id) && s.ActualCapacityScenarioId != null select s.ActualCapacityScenarioId.Value).ToArray();
|
|
return GetWeeklyCapacity(scenarios, expCats, uoms, isUOMMode, fiscalCalendarWeekendings);
|
|
}
|
|
|
|
private Dictionary<DateTime, PeopleResourceModel.CapacityValues> GetWeeklyCapacity(Guid[] SccenarioIds, Dictionary<Guid, ExpenditureCategory> expCats, Dictionary<Guid, UOM> uoms, bool? isUOMMode, List<DateTime> fiscalCalendarWeekendings)
|
|
{
|
|
var dict = new Dictionary<DateTime, PeopleResourceModel.CapacityValues>();
|
|
var context = new EnVisageEntities();
|
|
|
|
Guid[] expCatIds = expCats.Keys.ToArray();
|
|
|
|
var sds = (from sd in context.ScenarioDetail where SccenarioIds.Contains(sd.ParentID.Value) && fiscalCalendarWeekendings.Contains(sd.WeekEndingDate.Value) && expCatIds.Contains(sd.ExpenditureCategoryId.Value) select sd).ToList();
|
|
|
|
// fill dictionary with weekly capacity for each week
|
|
foreach (var week in fiscalCalendarWeekendings)
|
|
{
|
|
if (!dict.ContainsKey(week))
|
|
{
|
|
var quantity = 0M;
|
|
var cost = 0M;
|
|
var sd = (from s in sds where s.WeekEndingDate == week select s).ToList();
|
|
foreach (var s in sd)
|
|
{
|
|
quantity += s.Quantity.Value * Utils.GetUOMMultiplier(expCats, uoms, s.ExpenditureCategoryId ?? Guid.Empty, isUOMMode);
|
|
cost += s.Cost.Value;
|
|
}
|
|
dict.Add(week, new PeopleResourceModel.CapacityValues()
|
|
{
|
|
Quantity = quantity,
|
|
Cost = cost,
|
|
WeekEnding = week
|
|
});
|
|
}
|
|
}
|
|
return dict;
|
|
}
|
|
}
|
|
|
|
#region SA. ENV-608. Dashboard performance graph
|
|
|
|
public class StackGraphSerieData : List<List<object>>
|
|
{
|
|
public void AddValue(long tickValue, decimal dataValue)
|
|
{
|
|
List<object> point = new List<object>();
|
|
point.Add(tickValue);
|
|
point.Add(dataValue);
|
|
this.Add(point);
|
|
}
|
|
}
|
|
|
|
public class StackGraphSerie
|
|
{
|
|
public StackGraphSerieData data;
|
|
|
|
public StackGraphSerie()
|
|
{
|
|
data = new StackGraphSerieData();
|
|
}
|
|
}
|
|
|
|
public class StackGraphBarOptions : List<List<object>>
|
|
{
|
|
public void AddValue(long tickValue, string barTitle)
|
|
{
|
|
List<object> option = new List<object>();
|
|
option.Add(tickValue);
|
|
option.Add(barTitle);
|
|
this.Add(option);
|
|
}
|
|
}
|
|
|
|
public class StackGraphDataBlock
|
|
{
|
|
public List<StackGraphSerie> series;
|
|
public StackGraphBarOptions barOptions;
|
|
|
|
public StackGraphDataBlock()
|
|
{
|
|
series = new List<StackGraphSerie>();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|