1204 lines
67 KiB
C#
1204 lines
67 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Data;
|
|
using System.Data.Entity;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Net;
|
|
using System.Web;
|
|
using System.Web.Mvc;
|
|
using System.Web.Script.Serialization;
|
|
using EnVisage;
|
|
using EnVisage.Code;
|
|
using EnVisage.Code.BLL;
|
|
using EnVisage.Code.HtmlHelpers;
|
|
using EnVisage.Models;
|
|
using Microsoft.AspNet.Identity;
|
|
using jQuery.DataTables.Mvc;
|
|
using EnVisage.App_Start;
|
|
using EnVisage.Code.Cache;
|
|
|
|
namespace EnVisage.Controllers
|
|
{
|
|
[Authorize]
|
|
public class CapacityManagementController : BaseController
|
|
{
|
|
private class Pairs
|
|
{
|
|
public Guid Id { get; set; }
|
|
public decimal Quantity { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Direct GET to the page - returns main view
|
|
/// </summary>
|
|
public ActionResult Index(string menuId, string additionalFilters, PagePreferencesList.PagePreferencesSource src)
|
|
{
|
|
return PartialView("_capacityManagement",
|
|
new CapacityDetailsOptionsModel { MenuId = menuId, AdditionalFilterParams = additionalFilters, Source = src });
|
|
}
|
|
|
|
/// <summary>
|
|
/// GET: /Clients/
|
|
/// </summary>
|
|
/// <returns>Empty view</returns>
|
|
[HttpGet]
|
|
[AreaSecurityAttribute(area = Areas.Clients, level = AccessLevel.Read)]
|
|
public ActionResult Index()
|
|
{
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetUserId()));
|
|
if (user != null)
|
|
ViewBag.IsUOMHours = !user.PreferredResourceAllocation;
|
|
|
|
return View();
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult GetCompanies()
|
|
{
|
|
return Json(
|
|
DbContext.Companies.AsNoTracking().Select(c=>
|
|
new SelectListItem() {
|
|
Text = c.Name,
|
|
Value=c.Id.ToString()
|
|
}).ToList());
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult LoadJsonCalendar(CapacityDetailsModel model)
|
|
{
|
|
if (model == null || ((model.CompanyId == null || model.CompanyId == Guid.Empty)
|
|
&& (model.TeamId == null ||model.TeamId == Guid.Empty)
|
|
&& (model.ViewId == null || model.ViewId == Guid.Empty)))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
if (model.CompanyId != null && model.CompanyId != Guid.Empty)
|
|
{
|
|
var company = DbContext.Companies.AsNoTracking().FirstOrDefault(t => t.Id == model.CompanyId);
|
|
if (company == null)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
|
|
if (model.TeamId != null && model.TeamId != Guid.Empty)
|
|
{
|
|
var team = DbContext.Teams.AsNoTracking().FirstOrDefault(t => t.Id == model.TeamId);
|
|
if (team == null)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
|
|
if (model.ViewId != null && model.ViewId != Guid.Empty)
|
|
{
|
|
var team = DbContext.Views.AsNoTracking().FirstOrDefault(t => t.Id == model.ViewId);
|
|
if (team == null)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
|
|
model.StartDate = DateTime.Today;
|
|
model.EndDate = DateTime.Today.AddYears(1);
|
|
return Json(GetCalendar(model), JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a list of all principal GUIDs (user himself and his roles) to be used in direct requests to Security and ProjectAccess tables
|
|
/// </summary>
|
|
private Guid[] GetUserPrincipals()
|
|
{
|
|
var userId = User.Identity.GetUserId();
|
|
AspNetUser user = (from c in DbContext.AspNetUsers where c.Id == userId select c).FirstOrDefault();
|
|
var roleids = (from c in user.AspNetRoles select c.Id).ToList();
|
|
roleids.Add(userId);
|
|
var result = new Guid[roleids.Count() + 1];
|
|
for (int i = 0; i < roleids.Count(); i++)
|
|
result[i] = new Guid(roleids[i]);
|
|
result[roleids.Count()] = new Guid(userId);
|
|
return result;
|
|
}
|
|
|
|
private CapacityDetailsModel GetCalendar(CapacityDetailsModel model)
|
|
{
|
|
DateTime periodStartDate;
|
|
DateTime periodEndDate;
|
|
Guid actualScenarioId = Guid.Empty;
|
|
periodStartDate = model.StartDate;
|
|
periodEndDate = model.EndDate;
|
|
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
var calendarList = new List<CapacityDetailsModel.CalendarRow>();
|
|
var totalsList = new List<CapacityDetailsModel.CalendarRow>();
|
|
|
|
var principals = GetUserPrincipals();
|
|
var projectAccesses = new ProjectAccessCache().Value.Where(x => principals.Contains(x.PrincipalId)).Select(x => x.ProjectId);
|
|
|
|
List<Project> projects = null;
|
|
var teamIds = new List<Guid>();
|
|
var teams = new List<Team>();
|
|
var groupByTeamMode = model.GroupByTeam ?? false;
|
|
#region Collect common data
|
|
if (model.CompanyId != null && !model.CompanyId.Equals(Guid.Empty))
|
|
{
|
|
var companyIds = DbContext.Companies.Where(c => c.ParentCompanyId == model.CompanyId).Select(c=>c.Id).ToList();
|
|
companyIds.Add(model.CompanyId.Value);
|
|
projects = DbContext.Projects.Where(p => p.CompanyId.HasValue && companyIds.Contains(p.CompanyId.Value) && projectAccesses.Contains(p.Id) && !p.HasChildren).AsNoTracking().ToList();
|
|
projects.ForEach(p => p.Team2Project.Where(t2p=>!teams.Select(t => t.Id).Contains(t2p.TeamId)).ToList().ForEach(t => teams.Add(t.Team)));
|
|
teamIds.AddRange(teams.Select(t=>t.Id));
|
|
}
|
|
else if(model.TeamId != null && !model.TeamId.Equals(Guid.Empty)){
|
|
|
|
groupByTeamMode = false;
|
|
projects = DbContext.Team2Project.Where(p => p.TeamId == model.TeamId && projectAccesses.Contains(p.ProjectId) && !p.Project.HasChildren).Select(x => x.Project).AsNoTracking().ToList();
|
|
projects.ForEach(p => p.Team2Project.Where(t2p => model.TeamId == t2p.TeamId && !teams.Select(t => t.Id).Contains(t2p.TeamId)).ToList().ForEach(t => teams.Add(t.Team)));
|
|
teamIds.AddRange(teams.Select(t => t.Id));
|
|
}
|
|
else if (model.ViewId != null && !model.ViewId.Equals(Guid.Empty))
|
|
{
|
|
var viewTeamIds = (from tv in DbContext.Team2View
|
|
where tv.ViewId == model.ViewId
|
|
select tv.TeamId).ToList();
|
|
teamIds.AddRange(viewTeamIds);
|
|
projects = DbContext.Team2Project.Where(p => teamIds.Contains(p.TeamId) && projectAccesses.Contains(p.ProjectId) && !p.Project.HasChildren).
|
|
Select(x => x.Project).AsNoTracking().Distinct().ToList();
|
|
projects.ForEach(p => p.Team2Project.Where(t2p => teamIds.Contains(t2p.TeamId) && !teams.Select(t => t.Id).Contains(t2p.TeamId)).ToList().ForEach(t => teams.Add(t.Team)));
|
|
}
|
|
|
|
if (!model.GroupByTeam.HasValue || !model.GroupByTeam.Value || teams.Count() == 0)
|
|
{
|
|
teams = new List<Team>() { new Team(){Id = Guid.Empty} };
|
|
}
|
|
|
|
var projectIds = new List<Guid>();
|
|
projects.ForEach(p =>
|
|
projectIds.Add(p.Id));
|
|
|
|
var scenarioIds = new List<Guid>();
|
|
projects.ForEach(p=>
|
|
p.Scenarios.Where(s => s.Status.HasValue && s.Status.Value == (int)ScenarioStatus.Active
|
|
&& s.Type == (int)ScenarioType.Portfolio)
|
|
.ToList().ForEach(s => scenarioIds.Add(s.Id)));
|
|
|
|
var resourceAllocation = DbContext.PeopleResourceAllocations.Where(r => scenarioIds.Contains(r.ScenarioId)).ToList();
|
|
model.AllResources = new List<CapacityDetailsModel.ScenarioCalendarRowResource>();
|
|
|
|
var resourcesByTeams = DbContext.Teams.AsNoTracking().Where(x => teamIds.Contains(x.Id)).
|
|
Select(x => x.PeopleResources);
|
|
|
|
foreach (var teamResource in resourcesByTeams)
|
|
{
|
|
model.AllResources.AddRange(teamResource.Where(tr=>!model.AllResources.Select(ar=>ar.Id).Contains(tr.Id)).
|
|
Select(x => new CapacityDetailsModel.ScenarioCalendarRowResource()
|
|
{
|
|
Id = x.Id,
|
|
Name = string.Format("{0} {1}", x.FirstName, x.LastName),
|
|
ExpedentureCategoryId = x.ExpenditureCategoryId,
|
|
TeamId = x.TeamId.Value,
|
|
AssignedToTeam = teamIds.Contains(x.TeamId.Value),
|
|
ProjectIds = projects.SelectMany(p => p.Team2Project).Where(p => p.TeamId == x.TeamId.Value).Select(p=>p.ProjectId).ToArray(),
|
|
IsActiveEmployee = x.IsActiveEmployee
|
|
}));
|
|
}
|
|
var allResIds = model.AllResources.Select(t => t.Id).Distinct().ToArray();
|
|
var allResourceVacations = DbContext.PeopleResourceVacations.Where(t => allResIds.Contains(t.PeopleResourceId)).Select(t => new
|
|
{
|
|
t.PeopleResourceId,
|
|
t.HoursOff,
|
|
t.WeekEndingDate
|
|
}).ToArray();
|
|
|
|
var allResourceTrainings = DbContext.PeopleResourceTrainings.Where(t => allResIds.Contains(t.PeopleResourceId)).Select(t => new
|
|
{
|
|
t.PeopleResourceId,
|
|
t.HoursOff,
|
|
t.WeekEndingDate
|
|
}).ToArray();
|
|
|
|
var scenarioDetails = DbContext.VW_ScenarioAndProxyDetails.AsNoTracking()
|
|
.Where(t => t.ParentID.HasValue && scenarioIds.Contains(t.ParentID.Value) &&
|
|
t.WeekEndingDate >= periodStartDate &&
|
|
t.WeekEndingDate <= periodEndDate).OrderBy(t => t.ExpenditureCategoryName).ThenBy(t => t.WeekEndingDate);
|
|
|
|
|
|
Dictionary<Guid, List<Team2Scenario>> teams2scenarios =
|
|
DbContext.Team2Scenario.AsNoTracking().Where(t2s => scenarioIds.Contains(t2s.ScenarioId)).GroupBy(t2s => t2s.ScenarioId).ToDictionary(key => key.Key, grouping => grouping.ToList());
|
|
|
|
var scenarioDetailsInternal = scenarioDetails.Select(t => new
|
|
{
|
|
t.Id,
|
|
t.ExpenditureCategoryId,
|
|
t.WeekEndingDate,
|
|
t.GLId,
|
|
t.ExpenditureCategoryName,
|
|
t.Quantity,
|
|
t.Cost,
|
|
t.ParentID,
|
|
t.WeekOrdinal,
|
|
t.UseType,
|
|
t.UOMId
|
|
}).ToArray().GroupBy(
|
|
key => new
|
|
{
|
|
key.ExpenditureCategoryId,
|
|
key.ExpenditureCategoryName,
|
|
key.ParentID,
|
|
key.GLId,
|
|
key.UseType,
|
|
key.UOMId
|
|
}).ToDictionary(key => key.Key, grouping => grouping.ToList());
|
|
|
|
|
|
var itemsCount = 0;
|
|
CapacityDetailsModel.CalendarRow vacationRow = null;
|
|
CapacityDetailsModel.CalendarRow trainingRow = null;
|
|
CapacityDetailsModel.CalendarRow capacityRow = null;
|
|
CapacityDetailsModel.CalendarRow grandTotalRow = null;
|
|
|
|
var headerDates = (from c in DbContext.FiscalCalendars where c.Type == 0 && c.StartDate >= model.StartDate && c.EndDate <= model.EndDate orderby c.StartDate select c.EndDate);
|
|
BuildHeaders(model, headerDates.ToList());
|
|
|
|
itemsCount = model.Headers.Count;
|
|
|
|
#region Add Total (Active Scenarios), Vacation, Training, Loan-outs, Capacity
|
|
|
|
grandTotalRow = new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = "Total (Active Scenarios)",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Total
|
|
};
|
|
vacationRow = new CapacityDetailsModel.CalendarRow
|
|
{
|
|
Name = "Vacation",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Vacation
|
|
};
|
|
totalsList.Add(vacationRow);
|
|
trainingRow= new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = "Training",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Training
|
|
};
|
|
totalsList.Add(trainingRow);
|
|
//ENV-623 Hide loan-outs, as there is no way to input them or use the feature in the system, yet.
|
|
//totalsList.Add(new CapacityDetailsModel.CalendarRow()
|
|
//{
|
|
// Name = "Loan-outs",
|
|
// QuantityValues = new decimal[model.Headers.Count],
|
|
// IsParentCollapsed = false,
|
|
// IsTotals = true,
|
|
// RowType = CapacityDetailsModel.RowType.LoanOut
|
|
//});
|
|
totalsList.Add(grandTotalRow);
|
|
capacityRow = new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = "Capacity",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Capacity
|
|
};
|
|
totalsList.Add(capacityRow);
|
|
#endregion
|
|
|
|
if (!model.IsUOMHours.HasValue)
|
|
{
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetUserId()));
|
|
if (user != null)
|
|
model.IsUOMHours = !user.PreferredResourceAllocation;
|
|
}
|
|
|
|
var allExpCats = DbContext.ExpenditureCategory.AsNoTracking().ToList();
|
|
var allUoms = DbContext.UOMs.AsNoTracking().ToList();
|
|
model.AllResources.ForEach(res =>
|
|
{
|
|
res.VacationQuantityValues = new decimal[itemsCount];
|
|
res.TrainingQuantityValues = new decimal[itemsCount];
|
|
});
|
|
|
|
#endregion
|
|
|
|
foreach (var team in teams)
|
|
{
|
|
var groupByTeam = !Guid.Empty.Equals(team.Id);
|
|
if (groupByTeam && groupByTeamMode)
|
|
{
|
|
//Add Team row
|
|
calendarList.Add(new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = team.Name,
|
|
RowType = CapacityDetailsModel.RowType.Team,
|
|
});
|
|
}
|
|
|
|
foreach (var project in projects.Where(p => !groupByTeam || p.Team2Project.Select(t2p => t2p.TeamId).Contains(team.Id)).OrderBy(p => p.Name).ThenBy(p => p.Id))
|
|
{
|
|
var projectActiveScenarios = project.Scenarios.Where(s => s.Status.HasValue && s.Status.Value == (int)ScenarioStatus.Active
|
|
&& s.Type == (int)ScenarioType.Portfolio).OrderBy(p => p.Name).ThenBy(p => p.Id);
|
|
|
|
if (!projectActiveScenarios.Any())
|
|
continue;
|
|
|
|
#region Add Project row
|
|
|
|
calendarList.Add(new CapacityDetailsModel.CalendarRow() {
|
|
ProjectId = project.Id,
|
|
Name = (project.ParentProject != null ? project.ParentProject.Name + ": " : "") + project.Name,
|
|
Color = !string.IsNullOrEmpty(project.Color) ? project.Color.Contains('#') ? project.Color : "#" + project.Color :
|
|
(project.ParentProject != null ? project.ParentProject.Color.Contains('#') ? project.ParentProject.Color : "#" + project.ParentProject.Color : null ),
|
|
DetailIds = new Guid?[itemsCount],
|
|
QuantityValues = new decimal[itemsCount],
|
|
SpreadVal = new CapacityDetailsModel.Spread[itemsCount],
|
|
IsParentCollapsed = false,
|
|
ExpCatId = null,
|
|
ScenarioId = null,
|
|
RowType = CapacityDetailsModel.RowType.Project,
|
|
ReadOnly = new bool[itemsCount],
|
|
TeamId = team.Id
|
|
});
|
|
|
|
#endregion
|
|
|
|
foreach (var scenario in projectActiveScenarios)
|
|
{
|
|
var projectRow = calendarList.FirstOrDefault(x => x.ProjectId == project.Id && x.TeamId == team.Id && x.ExpCatId == null);
|
|
|
|
#region Add Scenario to Project row
|
|
|
|
if (projectRow != null && projectRow.ScenarioId == null)
|
|
{
|
|
projectRow.ScenarioId = scenario.Id;
|
|
projectRow.Name1 = scenario.Name;
|
|
var dates1 = scenarioDetailsInternal.Where(sd => sd.Key.ParentID == scenario.Id).SelectMany(x => x.Value).ToList();
|
|
var dates = dates1.Select(x => x.WeekEndingDate.Value).ToList();
|
|
if (dates.Count > 0)
|
|
projectRow.StartDate = (long)dates.Min().Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
|
|
if (dates.Count > 0)
|
|
projectRow.EndDate = (long)dates.Max().Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
|
|
}
|
|
|
|
#endregion
|
|
|
|
List<Guid?> project2ExpCats = null;
|
|
if (groupByTeam)
|
|
{
|
|
project2ExpCats = model.AllResources.Where(r => team.Id.Equals(r.TeamId)).Select(r => r.ExpedentureCategoryId).ToList();
|
|
}
|
|
else
|
|
{
|
|
project2ExpCats = model.AllResources.Where(r => teamIds.Contains(r.TeamId)).Select(r => r.ExpedentureCategoryId).ToList();
|
|
}
|
|
|
|
//Filter all ExpCats by available resorces
|
|
var expCats = scenarioDetailsInternal.Where(sd => sd.Key.ParentID == scenario.Id &&
|
|
project2ExpCats.Contains(sd.Key.ExpenditureCategoryId.Value));
|
|
|
|
decimal teamAssignmentMultiplier = 0;
|
|
if (groupByTeam)
|
|
{
|
|
teamAssignmentMultiplier = GetProjectNeedMultiplier(scenario.Id, new List<Guid>(){ team.Id}, teams2scenarios);
|
|
}
|
|
else
|
|
{
|
|
teamAssignmentMultiplier = GetProjectNeedMultiplier(scenario.Id, teamIds, teams2scenarios);
|
|
}
|
|
|
|
foreach (var expCat in expCats)
|
|
{
|
|
#region Add exp cat row for the top part of the calendar
|
|
|
|
calendarList.Add(new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
ProjectId = project.Id,
|
|
ScenarioId = scenario.Id,
|
|
TeamId = team.Id,
|
|
ExpCatId = expCat.Key.ExpenditureCategoryId,
|
|
Name = expCat.Key.ExpenditureCategoryName,
|
|
DetailIds = new Guid?[itemsCount],
|
|
QuantityValues = new decimal[itemsCount],
|
|
SpreadVal = new CapacityDetailsModel.Spread[itemsCount],
|
|
CollapsedClass = "fa-plus-square-2",
|
|
RestQuantity = new decimal[itemsCount],
|
|
ReadOnly = new bool[itemsCount],
|
|
EmptyScenario = false,
|
|
RowType = CapacityDetailsModel.RowType.ProjectExpenditureCategory,
|
|
Resources = model.AllResources.Where(x => resourceAllocation.Where(ec => ec.ExpenditureCategoryId == expCat.Key.ExpenditureCategoryId
|
|
&& ec.ScenarioId == scenario.Id && (!groupByTeam || (groupByTeam && team.Id == x.TeamId)))
|
|
.Select(r => r.PeopleResourceId).Contains(x.Id))
|
|
.Select(x => new CapacityDetailsModel.ScenarioCalendarRowResource()
|
|
{
|
|
Id = x.Id,
|
|
Name = x.Name,
|
|
QuantityValues = new decimal[itemsCount],
|
|
CapacityQuantityValues = new decimal[itemsCount],
|
|
VacationQuantityValues = new decimal[itemsCount],
|
|
TrainingQuantityValues = new decimal[itemsCount],
|
|
ReadOnly = !teamIds.Contains(x.TeamId),
|
|
IsActiveEmployee = x.IsActiveEmployee,
|
|
TeamId = x.TeamId
|
|
|
|
}).ToList(),
|
|
});
|
|
|
|
#endregion
|
|
|
|
var expCatRow = calendarList.Last();
|
|
|
|
#region Add exp cat row for the bottom part of the calendar
|
|
|
|
var totalsListExpCat = totalsList.FirstOrDefault(t=>t.ExpCatId == expCatRow.ExpCatId);
|
|
if (totalsListExpCat == null)
|
|
{
|
|
totalsListExpCat = new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
ExpCatId = expCat.Key.ExpenditureCategoryId,
|
|
Name = expCat.Key.ExpenditureCategoryName,
|
|
QuantityValues = new decimal[itemsCount],
|
|
SpreadVal = new CapacityDetailsModel.Spread[itemsCount],
|
|
CollapsedClass = "fa-plus-square",
|
|
IsParentCollapsed = false,
|
|
EmptyScenario = false,
|
|
QuantityTotalResValue = new decimal[itemsCount],
|
|
QuantityExpCatTotalValue = new decimal[itemsCount],
|
|
RowType = CapacityDetailsModel.RowType.BottomCategory,
|
|
Resources = new List<CapacityDetailsModel.ScenarioCalendarRowResource>(model.AllResources.
|
|
Where(x => x.ExpedentureCategoryId == expCat.Key.ExpenditureCategoryId && teamIds.Contains(x.TeamId)).Select(x =>
|
|
new CapacityDetailsModel.ScenarioCalendarRowResource()
|
|
{
|
|
Id = x.Id,
|
|
ExpedentureCategoryId = x.ExpedentureCategoryId,
|
|
Name = x.Name,
|
|
QuantityValues = new decimal[itemsCount],
|
|
SpreadVal = new CapacityDetailsModel.Spread[itemsCount],
|
|
QuantityTotalResValue = new decimal[itemsCount],
|
|
VacationQuantityValues = new decimal[itemsCount],
|
|
TrainingQuantityValues = new decimal[itemsCount],
|
|
GrandTotalCost = 0M,
|
|
GrandTotalQuantity = 0M,
|
|
IsActiveEmployee = x.IsActiveEmployee,
|
|
TeamId = x.TeamId
|
|
}))
|
|
};
|
|
totalsList.Add(totalsListExpCat);
|
|
}
|
|
|
|
#endregion
|
|
|
|
projectRow.EmptyScenario = false;
|
|
|
|
var uomMultiplier = Utils.GetUOMMultiplier(allExpCats, allUoms, expCat.Key.ExpenditureCategoryId ?? Guid.Empty, model.IsUOMHours);
|
|
var expCatUOM = allUoms.FirstOrDefault(t => t.Id == expCat.Key.UOMId);
|
|
var uomValue = expCatUOM == null ? 0 : expCatUOM.UOMValue*uomMultiplier;
|
|
|
|
expCatRow.Resources.ForEach(r => r.Title = (expCatRow.Resources.Exists(r1 => r1.ReadOnly) ?
|
|
"There are resources from other team assigned to the category so you cannot use Take all" : "Take all"));
|
|
|
|
var monthQuantity = 0.0M;
|
|
var monthVacation = 0.0M;
|
|
var monthTraining = 0.0M;
|
|
var monthResVal = 0.0M;
|
|
var monthResTotalVal = 0.0M;
|
|
var weeksCount = 0;
|
|
var resourceTotals = model.AllResources.Select(x => x.Id).ToDictionary(key => key, pairs => 0.0M);
|
|
var resourceTotalsExpCatTotals = model.AllResources.Select(x => new Pairs() { Id = x.Id, Quantity = 0.0M }).ToList();
|
|
var isMonthReadOnly = true;
|
|
|
|
// iterate through the weeks/months collection (1 month item following 4/5 week items)
|
|
for (int colIndex = 0; colIndex < itemsCount; colIndex++)
|
|
{
|
|
var isScenarioDate = (model.Headers[colIndex].Milliseconds >= projectRow.StartDate &&
|
|
model.Headers[colIndex].Milliseconds <= projectRow.EndDate);
|
|
|
|
expCatRow.ReadOnly[colIndex] = !isScenarioDate;
|
|
projectRow.ReadOnly[colIndex] = !isScenarioDate;
|
|
isMonthReadOnly &= !isScenarioDate;
|
|
|
|
// if item is a new week
|
|
if (!model.Headers[colIndex].IsMonth)
|
|
{
|
|
if (isScenarioDate)
|
|
{
|
|
var val = expCat.Value.FirstOrDefault(x => x.WeekEndingDate.Value.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds == model.Headers[colIndex].Milliseconds); //[colIndex1];
|
|
if (val != null)
|
|
{
|
|
expCatRow.DetailIds[colIndex] = val.Id; // column.Id;
|
|
expCatRow.QuantityValues[colIndex] = (val.Quantity ?? 0) * uomMultiplier * teamAssignmentMultiplier; // (column.Quantity ?? 0) * uomMultiplier;
|
|
monthQuantity += expCatRow.QuantityValues[colIndex];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
expCatRow.QuantityValues[colIndex] = 0;
|
|
expCatRow.DetailIds[colIndex] = null;
|
|
}
|
|
|
|
weeksCount++;
|
|
|
|
//Project/Scenario row update
|
|
projectRow.QuantityValues[colIndex] += expCatRow.QuantityValues[colIndex];
|
|
grandTotalRow.QuantityValues[colIndex] += expCatRow.QuantityValues[colIndex];
|
|
|
|
//Get resources quantity
|
|
var date = epoch.AddSeconds(model.Headers[colIndex].Milliseconds / 1000);
|
|
|
|
//Get allocation for the date
|
|
var currAllocation = resourceAllocation.Where(r => r.WeekEndingDate == date
|
|
&& r.ExpenditureCategoryId == expCatRow.ExpCatId && r.ScenarioId == expCatRow.ScenarioId).ToList();
|
|
|
|
expCatRow.Resources.ForEach(x =>
|
|
{
|
|
var peopleResourceAllocation = currAllocation.FirstOrDefault(ar => ar.PeopleResourceId == x.Id);
|
|
if (peopleResourceAllocation != null)
|
|
x.QuantityValues[colIndex] = (peopleResourceAllocation.Quantity ?? 0) * uomMultiplier;
|
|
else
|
|
x.QuantityValues[colIndex] = 0;
|
|
if (expCatUOM != null)
|
|
{
|
|
var vacationsSum = allResourceVacations.Where(t => t.PeopleResourceId == x.Id &&
|
|
t.WeekEndingDate <= date && t.WeekEndingDate >= date.AddDays(-6))
|
|
.Sum(s => s.HoursOff);
|
|
vacationRow.QuantityValues[colIndex] += vacationsSum * uomMultiplier;
|
|
monthVacation += vacationsSum * uomMultiplier;
|
|
grandTotalRow.QuantityValues[colIndex] += vacationsSum * uomMultiplier;
|
|
|
|
var trainingsSum = allResourceTrainings.Where(t => t.PeopleResourceId == x.Id &&
|
|
t.WeekEndingDate <= date && t.WeekEndingDate >= date.AddDays(-6))
|
|
.Sum(s => s.HoursOff);
|
|
trainingRow.QuantityValues[colIndex] += trainingsSum * uomMultiplier;
|
|
monthTraining += trainingsSum * uomMultiplier;
|
|
grandTotalRow.QuantityValues[colIndex] += trainingsSum * uomMultiplier;
|
|
|
|
x.CapacityQuantityValues[colIndex] = (expCatUOM.UOMValue - vacationsSum - trainingsSum) * uomMultiplier;
|
|
}
|
|
if (resourceTotals.ContainsKey(x.Id))
|
|
resourceTotals[x.Id] += x.QuantityValues[colIndex];
|
|
});
|
|
|
|
//Calculate not allocated quantity
|
|
expCatRow.RestQuantity[colIndex] = expCatRow.QuantityValues[colIndex] -
|
|
expCatRow.Resources.Select(x => x.QuantityValues[colIndex]).Sum() * uomMultiplier;
|
|
|
|
//Add resources to the capacity EC
|
|
foreach (var resCloneFrom in expCatRow.Resources)
|
|
{
|
|
var resCloneTo = totalsListExpCat.Resources.FirstOrDefault(r => resCloneFrom.Id == r.Id);
|
|
if (resCloneTo != null)
|
|
{
|
|
resCloneTo.QuantityValues[colIndex] += resCloneFrom.QuantityValues[colIndex];
|
|
}
|
|
}
|
|
totalsListExpCat.QuantityExpCatTotalValue[colIndex] += expCatRow.QuantityValues[colIndex];
|
|
|
|
#region Set validation class
|
|
|
|
var resTotal = expCatRow.Resources.Select(r => r.QuantityValues[colIndex]).Sum();
|
|
var compareRes = compareValues(resTotal, expCatRow.QuantityValues[colIndex]);
|
|
expCatRow.SpreadVal[colIndex] = compareRes == 0 ? CapacityDetailsModel.Spread.Equal :
|
|
compareRes > 0 ? CapacityDetailsModel.Spread.Over :
|
|
compareRes < 0 ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified;
|
|
|
|
if (projectRow.SpreadVal[colIndex] != CapacityDetailsModel.Spread.Over)
|
|
{
|
|
if (projectRow.SpreadVal[colIndex] != CapacityDetailsModel.Spread.Less)
|
|
{
|
|
if (expCatRow.SpreadVal[colIndex] != CapacityDetailsModel.Spread.Notspecified)
|
|
{
|
|
projectRow.SpreadVal[colIndex] = expCatRow.SpreadVal[colIndex];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (expCatRow.SpreadVal[colIndex] == CapacityDetailsModel.Spread.Over)
|
|
{
|
|
projectRow.SpreadVal[colIndex] = expCatRow.SpreadVal[colIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
//EC capacity
|
|
totalsListExpCat.QuantityResValue = uomValue;
|
|
totalsListExpCat.QuantityValues[colIndex] = totalsListExpCat.Resources.Select(x => x.QuantityValues[colIndex]).Sum();
|
|
expCatRow.Resources.ForEach(x =>
|
|
resourceTotalsExpCatTotals.FirstOrDefault(r => r.Id == x.Id).Quantity += x.QuantityValues[colIndex]);
|
|
totalsListExpCat.QuantityTotalResValue[colIndex] = totalsListExpCat.Resources.Where(r => r.IsActiveEmployee).Count() * uomValue;
|
|
totalsListExpCat.Resources.ForEach(r =>
|
|
r.QuantityTotalResValue[colIndex] = (r.IsActiveEmployee ? uomValue : 0));
|
|
|
|
monthResTotalVal += totalsListExpCat.QuantityTotalResValue[colIndex];
|
|
monthResVal += uomValue;
|
|
|
|
#region Set validation class
|
|
|
|
compareRes = compareValues(totalsListExpCat.QuantityValues[colIndex], totalsListExpCat.QuantityTotalResValue[colIndex]);
|
|
totalsListExpCat.SpreadVal[colIndex] = compareRes == 0 ? CapacityDetailsModel.Spread.Equal :
|
|
compareRes > 0 ? CapacityDetailsModel.Spread.Over :
|
|
compareRes < 0 ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified;
|
|
|
|
totalsListExpCat.Resources.ForEach(r => r.SpreadVal[colIndex] = r.QuantityValues[colIndex] == r.QuantityTotalResValue[colIndex] ? CapacityDetailsModel.Spread.Equal :
|
|
r.QuantityValues[colIndex] > r.QuantityTotalResValue[colIndex] ? CapacityDetailsModel.Spread.Over :
|
|
r.QuantityValues[colIndex] < r.QuantityTotalResValue[colIndex] ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified);
|
|
|
|
#endregion
|
|
}
|
|
else//if (model.Headers[colIndex].IsMonth) if item is a month then let's summarize data of it's weeks
|
|
{
|
|
expCatRow.DetailIds[colIndex] = Guid.Empty;
|
|
expCatRow.QuantityValues[colIndex] = monthQuantity;
|
|
|
|
expCatRow.ReadOnly[colIndex] = isMonthReadOnly;
|
|
projectRow.ReadOnly[colIndex] = isMonthReadOnly;
|
|
|
|
isMonthReadOnly = true;
|
|
|
|
expCatRow.Resources.ForEach(x =>
|
|
{
|
|
x.QuantityValues[colIndex] = resourceTotals.ContainsKey(x.Id)
|
|
? resourceTotals[x.Id]
|
|
: 0;
|
|
});
|
|
vacationRow.QuantityValues[colIndex] += monthVacation;
|
|
trainingRow.QuantityValues[colIndex] += monthTraining;
|
|
grandTotalRow.QuantityValues[colIndex] += monthVacation + monthTraining;
|
|
|
|
totalsListExpCat.QuantityExpCatTotalValue[colIndex] += expCatRow.QuantityValues[colIndex];
|
|
|
|
//Project/Scenario row update
|
|
projectRow.QuantityValues[colIndex] += expCatRow.QuantityValues[colIndex];
|
|
grandTotalRow.QuantityValues[colIndex] += expCatRow.QuantityValues[colIndex];
|
|
|
|
#region Set validation class
|
|
|
|
var resTotal = expCatRow.Resources.Select(r => r.QuantityValues[colIndex]).Sum();
|
|
var compareRes = compareValues(resTotal, expCatRow.QuantityValues[colIndex]);
|
|
expCatRow.SpreadVal[colIndex] = compareRes == 0 ? CapacityDetailsModel.Spread.Equal :
|
|
compareRes > 0 ? CapacityDetailsModel.Spread.Over :
|
|
compareRes < 0 ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified;
|
|
|
|
if (projectRow.SpreadVal[colIndex] != CapacityDetailsModel.Spread.Over)
|
|
{
|
|
if (projectRow.SpreadVal[colIndex] != CapacityDetailsModel.Spread.Less)
|
|
{
|
|
if (expCatRow.SpreadVal[colIndex] != CapacityDetailsModel.Spread.Notspecified)
|
|
{
|
|
projectRow.SpreadVal[colIndex] = expCatRow.SpreadVal[colIndex];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (expCatRow.SpreadVal[colIndex] == CapacityDetailsModel.Spread.Over)
|
|
{
|
|
projectRow.SpreadVal[colIndex] = expCatRow.SpreadVal[colIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
//expCatRow.Resources.ForEach(r => r.SpreadVal[colIndex] = expCatRow.SpreadVal[colIndex]);
|
|
|
|
#endregion
|
|
|
|
totalsListExpCat.QuantityTotalResValue[colIndex] = monthResTotalVal; // totalsListExpCat.Resources.Count* UoMVal;
|
|
totalsListExpCat.Resources.ForEach(x =>
|
|
{
|
|
x.QuantityTotalResValue[colIndex] = (x.IsActiveEmployee ? monthResVal : 0);
|
|
x.QuantityValues[colIndex] += resourceTotalsExpCatTotals.FirstOrDefault(r => r.Id == x.Id).Quantity;
|
|
});
|
|
totalsListExpCat.QuantityValues[colIndex] = totalsListExpCat.Resources.Select(x => x.QuantityValues[colIndex]).Sum();
|
|
totalsListExpCat.QuantityResValue = uomValue;
|
|
|
|
#region Set validation class to total exp cat row
|
|
compareRes = compareValues(totalsListExpCat.QuantityValues[colIndex], totalsListExpCat.QuantityTotalResValue[colIndex]);
|
|
totalsListExpCat.SpreadVal[colIndex] = compareRes == 0 ? CapacityDetailsModel.Spread.Equal :
|
|
compareRes > 0 ? CapacityDetailsModel.Spread.Over :
|
|
compareRes < 0 ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified;
|
|
|
|
compareRes = compareValues(totalsListExpCat.QuantityValues[colIndex], totalsListExpCat.QuantityTotalResValue[colIndex]);
|
|
totalsListExpCat.Resources.ForEach(r => r.SpreadVal[colIndex] = compareValues(r.QuantityValues[colIndex], r.QuantityTotalResValue[colIndex]) == 0 ? CapacityDetailsModel.Spread.Equal :
|
|
compareValues(r.QuantityValues[colIndex], r.QuantityTotalResValue[colIndex]) > 0 ? CapacityDetailsModel.Spread.Over :
|
|
compareValues(r.QuantityValues[colIndex], r.QuantityTotalResValue[colIndex]) > 0 ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified);
|
|
#endregion
|
|
|
|
monthQuantity = 0.0M;
|
|
monthVacation = 0.0M;
|
|
monthTraining = 0.0M;
|
|
monthResVal = 0.0M;
|
|
monthResTotalVal = 0.0M;
|
|
weeksCount = 0;
|
|
var keys = resourceTotals.Keys.ToArray();
|
|
foreach (var key in keys)
|
|
{
|
|
resourceTotals[key] = 0.0M;
|
|
}
|
|
resourceTotalsExpCatTotals.ForEach(x => x.Quantity = 0.0M);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
model.Calendar = calendarList;
|
|
|
|
|
|
if (totalsList.Count == 0)
|
|
{
|
|
var dates = (from c in DbContext.FiscalCalendars where c.Type == 0 && c.StartDate >= model.StartDate && c.EndDate <= model.EndDate orderby c.StartDate select c.EndDate);
|
|
BuildHeaders(model, dates.ToList());
|
|
|
|
#region Add Total (Active Scenarios), Vacation, Training, Loan-outs, Capacity
|
|
grandTotalRow = new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = "Total (Active Scenarios)",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Total
|
|
};
|
|
vacationRow = new CapacityDetailsModel.CalendarRow
|
|
{
|
|
Name = "Vacation",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Vacation
|
|
};
|
|
totalsList.Add(vacationRow);
|
|
|
|
trainingRow= new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = "Training",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Training
|
|
};
|
|
totalsList.Add(trainingRow);
|
|
|
|
totalsList.Add(new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = "Loan-outs",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.LoanOut
|
|
});
|
|
totalsList.Add(grandTotalRow);
|
|
|
|
totalsList.Add(new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
Name = "Capacity",
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
IsParentCollapsed = false,
|
|
IsTotals = true,
|
|
RowType = CapacityDetailsModel.RowType.Capacity
|
|
});
|
|
|
|
#endregion
|
|
}
|
|
|
|
totalsList.ForEach(ec =>
|
|
ec.EmptyScenario = (ec.Resources == null || !ec.Resources.Any()));
|
|
|
|
#region Add expected EC
|
|
|
|
var totalExpCats = DbContext.PeopleResources.Where(r => teamIds.Contains(r.TeamId.Value)).Select(r => r.ExpenditureCategory).Distinct().ToList();
|
|
var remainingExpCats = totalExpCats.Where(ec => !totalsList.Select(ec1 => ec1.ExpCatId).Contains(ec.Id)).ToList();
|
|
|
|
var d = remainingExpCats.Select(ec => new CapacityDetailsModel.CalendarRow()
|
|
{
|
|
ExpCatId = ec.Id,
|
|
Name = ec.Expenditure.Name,
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
SpreadVal = new CapacityDetailsModel.Spread[model.Headers.Count],
|
|
CollapsedClass = "fa-plus-square",
|
|
IsParentCollapsed = false,
|
|
EmptyScenario = false,
|
|
QuantityTotalResValue = new decimal[model.Headers.Count],
|
|
QuantityExpCatTotalValue = new decimal[model.Headers.Count],
|
|
RowType = CapacityDetailsModel.RowType.BottomCategory,
|
|
Resources = new List<CapacityDetailsModel.ScenarioCalendarRowResource>(model.AllResources.
|
|
Where(x => x.ExpedentureCategoryId == ec.Id && teamIds.Contains(x.TeamId)).Select(x =>
|
|
new CapacityDetailsModel.ScenarioCalendarRowResource()
|
|
{
|
|
Id = x.Id,
|
|
ExpedentureCategoryId = x.ExpedentureCategoryId,
|
|
Name = x.Name,
|
|
QuantityValues = new decimal[model.Headers.Count],
|
|
SpreadVal = new CapacityDetailsModel.Spread[model.Headers.Count],
|
|
QuantityTotalResValue = new decimal[model.Headers.Count],
|
|
GrandTotalCost = 0M,
|
|
GrandTotalQuantity = 0M,
|
|
IsActiveEmployee = x.IsActiveEmployee,
|
|
TeamId = x.TeamId
|
|
}))
|
|
}).ToList();
|
|
foreach (var ec in d)
|
|
{
|
|
var uomMultiplier = Utils.GetUOMMultiplier(remainingExpCats, allUoms, ec.ExpCatId.Value, model.IsUOMHours);
|
|
var UoMVal = remainingExpCats.First(x => x.Id == ec.ExpCatId).UOM.UOMValue * uomMultiplier;
|
|
ec.QuantityResValue = UoMVal;
|
|
var monthQuantity = 0.0M;
|
|
var monthQuantityRes = 0.0M;
|
|
for(var colIndex = 0; colIndex < model.Headers.Count; colIndex++)
|
|
{
|
|
if (model.Headers[colIndex].IsMonth){
|
|
ec.QuantityTotalResValue[colIndex] = monthQuantity;
|
|
ec.Resources.ForEach(r => r.QuantityTotalResValue[colIndex] = (r.IsActiveEmployee ? monthQuantityRes : 0));
|
|
|
|
|
|
|
|
monthQuantity = 0.0M;
|
|
monthQuantityRes = 0.0M;
|
|
}else{
|
|
monthQuantity += UoMVal * ec.Resources.Where(r=>r.IsActiveEmployee).Count();
|
|
monthQuantityRes += UoMVal;
|
|
ec.QuantityTotalResValue[colIndex] = UoMVal * ec.Resources.Where(r=>r.IsActiveEmployee).Count();
|
|
|
|
ec.Resources.ForEach(r => r.QuantityTotalResValue[colIndex] = (r.IsActiveEmployee ? UoMVal : 0));
|
|
}
|
|
|
|
//ec.SpreadVal[colIndex] = ec.QuantityValues[colIndex] == ec.QuantityTotalResValue[colIndex] ? CapacityDetailsModel.Spread.Equal :
|
|
// ec.QuantityValues[colIndex] > ec.QuantityTotalResValue[colIndex] ? CapacityDetailsModel.Spread.Over :
|
|
// ec.QuantityValues[colIndex] < r.QuantityTotalResValue[colIndex] ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified);
|
|
|
|
ec.Resources.ForEach(r => r.SpreadVal[colIndex] = compareValues(r.QuantityValues[colIndex] , r.QuantityTotalResValue[colIndex]) ==0 ? CapacityDetailsModel.Spread.Equal :
|
|
compareValues(r.QuantityValues[colIndex] , r.QuantityTotalResValue[colIndex]) > 0 ? CapacityDetailsModel.Spread.Over :
|
|
compareValues(r.QuantityValues[colIndex] , r.QuantityTotalResValue[colIndex]) < 0 ? CapacityDetailsModel.Spread.Less : CapacityDetailsModel.Spread.Notspecified);
|
|
}
|
|
}
|
|
totalsList.AddRange(d);
|
|
|
|
#endregion
|
|
|
|
#region set capacity row values as sum of each expenditure category capacities
|
|
foreach (var bottomExpCatRow in totalsList.Where(t => CapacityDetailsModel.RowType.BottomCategory.Equals(t.RowType)))
|
|
{
|
|
if (bottomExpCatRow.QuantityTotalResValue != null)
|
|
for (var weekIndex = 0; weekIndex < bottomExpCatRow.QuantityTotalResValue.Length; weekIndex++)
|
|
{
|
|
capacityRow.QuantityValues[weekIndex] += bottomExpCatRow.QuantityTotalResValue[weekIndex];
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
model.Calendar.AddRange(totalsList);
|
|
|
|
#region Fill Vacation, trainig and loan out weekly allocations for each resource
|
|
decimal monthResVacation = 0.0M;
|
|
decimal monthResTraining = 0.0M;
|
|
|
|
foreach (var res in model.AllResources)
|
|
{
|
|
var uomMultiplier = Utils.GetUOMMultiplier(allExpCats, allUoms,
|
|
res.ExpedentureCategoryId ?? Guid.Empty,
|
|
model.IsUOMHours);
|
|
for (int colIndex = 0; colIndex < itemsCount; colIndex++) //(var column1 in expCat.Value)
|
|
{
|
|
var date = epoch.AddSeconds(model.Headers[colIndex].Milliseconds/1000);
|
|
// if item is a new week
|
|
if (!model.Headers[colIndex].IsMonth)
|
|
{
|
|
var vacationQuantityValue = allResourceVacations.Where(t => t.PeopleResourceId == res.Id &&
|
|
t.WeekEndingDate <= date &&
|
|
t.WeekEndingDate >= date.AddDays(-6))
|
|
.Sum(s => s.HoursOff) * uomMultiplier;
|
|
res.VacationQuantityValues[colIndex] = vacationQuantityValue;
|
|
monthResVacation += vacationQuantityValue;
|
|
|
|
var trainingQuantityValue = allResourceTrainings.Where(t => t.PeopleResourceId == res.Id &&
|
|
t.WeekEndingDate <= date &&
|
|
t.WeekEndingDate >= date.AddDays(-6))
|
|
.Sum(s => s.HoursOff) * uomMultiplier;
|
|
res.TrainingQuantityValues[colIndex] = trainingQuantityValue;
|
|
monthResTraining += trainingQuantityValue;
|
|
}
|
|
else
|
|
{
|
|
res.VacationQuantityValues[colIndex] = monthResVacation;
|
|
monthResVacation = 0.0M;
|
|
res.TrainingQuantityValues[colIndex] = monthResTraining;
|
|
monthResTraining = 0.0M;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
return model;
|
|
//return null;
|
|
}
|
|
|
|
private int compareValues(decimal val1, decimal val2)
|
|
{
|
|
|
|
var val1_ = decimal.Round( val1, 3);
|
|
var val2_ = decimal.Round(val2, 3);
|
|
if ((-0.005m > (val1_ - val2_)))
|
|
return -1;
|
|
else
|
|
if ((0.005m < (val1_ - val2_)))
|
|
|
|
return 1;
|
|
else
|
|
return 0;
|
|
|
|
}
|
|
public void BuildHeaders(CapacityDetailsModel model, List<DateTime> gridHeaders)
|
|
{
|
|
model.Headers = new List<CapacityDetailsModel.Header>((int)(gridHeaders.Count * 1.25));
|
|
var prevMonth = string.Empty;
|
|
var prevYear = string.Empty;
|
|
var monthIndex = -1;
|
|
var yearIndex = -1;
|
|
CapacityDetailsModel.Header monthColumn = null;
|
|
CapacityDetailsModel.Header yearColumn = null;
|
|
foreach (var gridHeader in gridHeaders)
|
|
{
|
|
// get week start date as previous week end date + 1 day
|
|
//var weekStartDate = prevWeek.AddDays(1);
|
|
// if there is a gap between weeks (e.g. there is a non-working week that was not represented in scenario details records)
|
|
// then we should subtract 6 days from current week end date
|
|
//if (gridHeader.AddDays(-6) > weekStartDate)
|
|
// weekStartDate = gridHeader.AddDays(-6);
|
|
// get month name as month of the week start date
|
|
var gridHeaderTitle = gridHeader.ToString("MMMM"); //("MMMM yyyy");
|
|
var gridHeaderYearTitle = gridHeader.ToString("yyyy");
|
|
|
|
if (!prevMonth.Equals(gridHeaderTitle))
|
|
{
|
|
if (monthColumn != null)
|
|
{
|
|
model.Headers.Add(monthColumn);
|
|
yearColumn.SpanCount++; // model.Headers.Count - 1;
|
|
}
|
|
monthColumn = new CapacityDetailsModel.Header()
|
|
{
|
|
Show = true,
|
|
Milliseconds = (long)gridHeader.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds,
|
|
MonthIndex = ++monthIndex,
|
|
IsMonth = true,
|
|
Title = gridHeaderTitle,
|
|
Year = gridHeaderYearTitle,
|
|
Weeks = new List<int>()
|
|
};
|
|
}
|
|
if (!prevYear.Equals(gridHeaderYearTitle))
|
|
{
|
|
if (yearColumn != null)
|
|
{
|
|
model.YearHeaders.Add(yearColumn);
|
|
}
|
|
yearColumn = new CapacityDetailsModel.Header()
|
|
{
|
|
Show = true,
|
|
Milliseconds = (long)gridHeader.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds,
|
|
YearIndex = ++yearIndex,
|
|
IsMonth = false,
|
|
Title = gridHeaderYearTitle,
|
|
//Weeks = new List<int>()
|
|
};
|
|
}
|
|
|
|
var weekHeader = new CapacityDetailsModel.Header()
|
|
{
|
|
IsMonth = false,
|
|
Milliseconds = (long)gridHeader.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds,
|
|
MonthIndex = monthColumn.MonthIndex,
|
|
Title = gridHeader.ToString("dd") //ToShortDateString()
|
|
};
|
|
model.Headers.Add(weekHeader);
|
|
monthColumn.Weeks.Add(model.Headers.Count - 1);
|
|
monthColumn.Milliseconds = weekHeader.Milliseconds;
|
|
|
|
yearColumn.Milliseconds = weekHeader.Milliseconds;
|
|
|
|
prevMonth = gridHeaderTitle;
|
|
prevYear = gridHeaderYearTitle;
|
|
}
|
|
if (monthColumn != null)
|
|
{
|
|
model.Headers.Add(monthColumn);
|
|
yearColumn.SpanCount++;
|
|
}
|
|
if (yearColumn != null)
|
|
{
|
|
model.YearHeaders.Add(yearColumn);
|
|
}
|
|
}
|
|
|
|
private decimal GetProjectNeedMultiplier(Guid scenarioId, List<Guid> calendarTeamIds, Dictionary<Guid, List<Team2Scenario>> teams2scenarios)
|
|
{
|
|
if (!teams2scenarios.ContainsKey(scenarioId))
|
|
return 1;
|
|
|
|
decimal mul = 0.0M;
|
|
foreach (Team2Scenario t2s in teams2scenarios[scenarioId])
|
|
{
|
|
foreach (Guid teamId in calendarTeamIds)
|
|
{
|
|
if (t2s.TeamId == teamId)
|
|
mul += (decimal)t2s.Allocation;
|
|
}
|
|
}
|
|
return mul / 100;
|
|
}
|
|
|
|
//[HttpGet]
|
|
//public ActionResult GetScenarioAvailableExpCategories(Guid id)
|
|
//{
|
|
// var availableExpenditures =
|
|
// DbContext.VW_ExpCategoriesInScenario.AsNoTracking()
|
|
// //.Where(t => t.ScenarioID == id)
|
|
// .OrderBy(t => t.Name)
|
|
// .Select(t => new { t.Id, t.Name })
|
|
// .ToArray();
|
|
// return Json(availableExpenditures, JsonRequestBehavior.AllowGet);
|
|
//}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult SaveChanges(SaveCapacityDetailsChangesModel model)
|
|
{
|
|
//if (model.ScenarioId != Guid.Empty && ContentLocker.IsLock("Scenario", model.ScenarioId.ToString(), User.Identity.Name))
|
|
// return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
var context = new EnVisageEntities();
|
|
model.TrimStringProperties();
|
|
|
|
var allExpCats = DbContext.ExpenditureCategory.AsNoTracking().ToList();
|
|
var allUoms = DbContext.UOMs.AsNoTracking().ToList();
|
|
if (!model.ScenarioFilters.IsUOMHours.HasValue)
|
|
{
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetUserId()));
|
|
if (user != null)
|
|
model.ScenarioFilters.IsUOMHours = !user.PreferredResourceAllocation;
|
|
}
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
//var manager = new ScenarioManager(context);
|
|
//var scenario = manager.Load(model.ScenarioId, false);
|
|
//var scenarioStartDateMsSince1970 = scenario.StartDate.HasValue ?
|
|
// (long)scenario.StartDate.Value.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds :
|
|
// (long)new DateTime(1970, 1, 1).Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
|
|
|
|
foreach (var changedExpCat in model.ChangedExpCats)
|
|
{
|
|
var uomMultiplier = Utils.GetUOMMultiplier(allExpCats, allUoms, changedExpCat.Id, model.ScenarioFilters.IsUOMHours);
|
|
//foreach (var changedColumn in changedExpCat.Values)
|
|
//{
|
|
// if (changedColumn.Milliseconds >= scenarioStartDateMsSince1970)
|
|
// {
|
|
// var scenarioDetailsItem = (from c in context.ScenarioDetail where c.Id == changedColumn.Id select c).FirstOrDefault();//manager.LoadScenarioDetail(changedColumn.Id, false);
|
|
// if (scenarioDetailsItem == null) continue;
|
|
// if (scenarioDetailsItem.Id == Guid.Empty)
|
|
// {
|
|
// throw new NullReferenceException(string.Format("Scenario Details {0} mising", changedColumn.Id));
|
|
// }
|
|
// scenarioDetailsItem.Quantity = changedColumn.Quantity / uomMultiplier;
|
|
// scenarioDetailsItem.Cost = changedColumn.Cost;
|
|
// }
|
|
//}
|
|
if (changedExpCat.Resources != null)
|
|
{
|
|
var resourceIds = changedExpCat.Resources.Select(x => x.Id).ToList();
|
|
var resourceAllocations = context.PeopleResourceAllocations.Where(x => resourceIds.Contains(x.PeopleResourceId)
|
|
&& x.ScenarioId == changedExpCat.ScenarioId
|
|
&& x.ExpenditureCategoryId == changedExpCat.Id).ToList();
|
|
|
|
foreach (var resource in changedExpCat.Resources)
|
|
{
|
|
var resourceId = resource.Id;
|
|
|
|
if (resource.IsRemoved)
|
|
{
|
|
var recourcesToDelete = context.PeopleResourceAllocations.Where(x => x.PeopleResourceId == resource.Id && x.ScenarioId == changedExpCat.ScenarioId && x.ExpenditureCategoryId == changedExpCat.Id).ToList();
|
|
recourcesToDelete.ForEach(x => context.PeopleResourceAllocations.Remove(x));
|
|
recourcesToDelete.ForEach(x => context.Entry(x).State = System.Data.Entity.EntityState.Deleted);
|
|
}
|
|
else
|
|
{
|
|
var allocateResourceIdsUpdated = new List<Guid>();
|
|
|
|
foreach (var changedResource in resource.Values)
|
|
{
|
|
//if (changedResource.Id.HasValue)
|
|
{
|
|
var date = epoch.AddSeconds(changedResource.Milliseconds / 1000);
|
|
|
|
var allocatedResource = (from c in resourceAllocations
|
|
where c.WeekEndingDate == date && c.PeopleResourceId == resourceId
|
|
select c).FirstOrDefault();
|
|
if (changedResource.Quantity <= 0)
|
|
{
|
|
if (allocatedResource != null)
|
|
context.Entry(allocatedResource).State = System.Data.Entity.EntityState.Deleted;
|
|
continue;
|
|
}
|
|
if (allocatedResource == null)
|
|
{
|
|
allocatedResource = context.PeopleResourceAllocations.Create();
|
|
allocatedResource.Id = Guid.NewGuid();
|
|
allocatedResource.ExpenditureCategoryId = changedExpCat.Id;
|
|
allocatedResource.PeopleResourceId = resourceId;
|
|
allocatedResource.ScenarioId = changedExpCat.ScenarioId;
|
|
allocatedResource.WeekEndingDate = date;
|
|
context.Entry(allocatedResource).State = System.Data.Entity.EntityState.Added;
|
|
}
|
|
else
|
|
{
|
|
context.Entry(allocatedResource).State = System.Data.Entity.EntityState.Modified;
|
|
}
|
|
allocatedResource.Quantity = changedResource.Quantity / uomMultiplier;
|
|
}
|
|
|
|
//var recourcesToDelete = context.PeopleResourceAllocations.Where(x => allocateResourceIdsUpdated.Contains(x.Id)).ToList();
|
|
//recourcesToDelete.ForEach(x => context.PeopleResourceAllocations.Remove(x));
|
|
//recourcesToDelete.ForEach(x => context.Entry(x).State = System.Data.Entity.EntityState.Deleted);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
context.SaveChanges();
|
|
//manager.SetBottomUpCosts(scenario);
|
|
//context.SaveChanges();
|
|
|
|
//model.ScenarioFilters.StartDate = scenario.StartDate ?? DateTime.Today;
|
|
//model.ScenarioFilters.EndDate = scenario.EndDate ?? DateTime.Today.AddYears(1);
|
|
//model.ScenarioFilters.GrowthScenario = scenario.GrowthScenario;
|
|
//model.ScenarioFilters.ParentId = scenario.ParentId ?? Guid.Empty;
|
|
//model.ScenarioFilters.ScenarioType = (ScenarioType?)scenario.Type;
|
|
//var detailsGrid = GetScenarioCalendar(model.ScenarioFilters);
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.OK);
|
|
}
|
|
catch (BLLException blEx) // handle any system specific error
|
|
{
|
|
// display error message if required
|
|
if (blEx.DisplayError)
|
|
ModelState.AddModelError(string.Empty, blEx.Message);
|
|
else // if display not requried then display modal form with general error message
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
}
|
|
}
|