942 lines
37 KiB
C#
942 lines
37 KiB
C#
using EnVisage.App_Start;
|
|
using EnVisage.Code;
|
|
using EnVisage.Code.BLL;
|
|
using EnVisage.Models;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Web.Mvc;
|
|
using Microsoft.AspNet.Identity;
|
|
using System.Collections.Generic;
|
|
using MongoDB.Bson;
|
|
using EnVisage.Code.DAL.Mongo;
|
|
using MongoDB.Bson.Serialization;
|
|
using EnVisage.Code.Cache;
|
|
|
|
namespace EnVisage.Controllers
|
|
{
|
|
[Authorize]
|
|
public class MixController : BaseController
|
|
{
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
|
|
public ActionResult Index(string id)
|
|
{
|
|
return View("Index", null, id);
|
|
}
|
|
|
|
public ActionResult ExportToPDF(string id)
|
|
{
|
|
PDFExporter exporter = new PDFExporter();
|
|
exporter.BrowserWidth = 2560;
|
|
byte[] pdfBuffer = exporter.ExportPage(Url.Action("Index", "Mix", new { @id = id }, this.Request.Url.Scheme), 20, true, Request.Cookies);
|
|
|
|
// send the PDF file to browser
|
|
FileResult fileResult = new FileContentResult(pdfBuffer, "application/pdf");
|
|
fileResult.FileDownloadName = "Prevu-RMO-" + DateTime.Today.Month + "-" + DateTime.Today.Day + ".pdf";
|
|
|
|
return fileResult;
|
|
}
|
|
|
|
private void LoadMixFilterValues(MixFilterVariantsModel model, string userId)
|
|
{
|
|
if (model == null)
|
|
throw new ArgumentNullException("Model must be not null");
|
|
|
|
if (string.IsNullOrWhiteSpace(userId))
|
|
throw new ArgumentNullException("userId");
|
|
|
|
model.AvailableTeamsAndViews = Utils.GetViewsAndTeams(userId);
|
|
model.AvailableUsers = Utils.GetUsers().ToList();
|
|
model.AvailableMixes = Utils.GetMixesAvailable4User(userId);
|
|
model.AvailableExpenditures = Utils.GetExpenditures();
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
|
|
public JsonResult GetPageFilter()
|
|
{
|
|
var userId = User.Identity.GetID();
|
|
var model = new MixSaveModel()
|
|
{
|
|
Users = new List<Guid>() { Guid.Parse(userId) }
|
|
};
|
|
LoadMixFilterValues(model.Filter.Variants, userId);
|
|
|
|
return Json(model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
|
|
public JsonResult LoadMix(string model)
|
|
{
|
|
string mixId = model;
|
|
|
|
MixSaveModel serverModel = new MixSaveModel();
|
|
MixSaveModel mongoModel = new MixSaveModel();
|
|
|
|
LoadMixFilterValues(serverModel.Filter.Variants, User.Identity.GetID());
|
|
|
|
if (!string.IsNullOrWhiteSpace(mixId))
|
|
{
|
|
// Get mix data from Mongo
|
|
var mixManager = ResolveMixManager(DbProvider.MongoDb);
|
|
mongoModel = mixManager.RetrieveModel(mixId);
|
|
|
|
if (mongoModel != null)
|
|
{
|
|
var startDate = Utils.ConvertFromUnixDate(mongoModel.StartDate);
|
|
var endDate = Utils.ConvertFromUnixDate(mongoModel.EndDate);
|
|
var endDateCorrected = endDate.AddDays(6);
|
|
|
|
// Create server Mix model from SQL
|
|
GetCalendar(serverModel.Calendar, startDate, endDateCorrected,
|
|
mongoModel.Filter.Selection.TeamsViews.Where(t => t.Group.Name == "Teams").Select(x => new Guid(x.Id)).ToList(),
|
|
mongoModel.Filter.Selection.TeamsViews.Where(t => t.Group.Name == "Views").Select(x => new Guid(x.Id)).ToList(),
|
|
mongoModel.Filter.Selection.TeamsViews.Where(x => x.IsNew).Select(t => (MixTeamViewModel)t).ToList());
|
|
|
|
MixModelsMergeManager mergeMngr = new MixModelsMergeManager();
|
|
mergeMngr.MergeHeaders(serverModel, mongoModel);
|
|
mergeMngr.MergeFilters(serverModel.Filter, mongoModel.Filter);
|
|
mergeMngr.MergeCalendars(serverModel, mongoModel, startDate, endDate);
|
|
}
|
|
}
|
|
|
|
return Json(serverModel);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
|
|
public ActionResult DeleteMix(string model)
|
|
{
|
|
try
|
|
{
|
|
Guid userId = new Guid(User.Identity.GetID());
|
|
|
|
if (!string.IsNullOrWhiteSpace(model))
|
|
{
|
|
var mixManager = ResolveMixManager(DbProvider.MongoDb);
|
|
mixManager.Delete(model);
|
|
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);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Write)]
|
|
public ActionResult SaveMix(MixSaveModel model)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(model.Id) && ContentLocker.IsLock("Mix", model.Id, User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var mixManager = ResolveMixManager(DbProvider.MongoDb);
|
|
model.CreatedBy = User.Identity.GetID();
|
|
var mix = mixManager.SaveWithChildren(model);
|
|
DbContext.SaveChanges();
|
|
|
|
return Json(new
|
|
{
|
|
Id = mix.Id.ToString()
|
|
});
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
|
|
// SA. For model errors debug:
|
|
var errors = ModelState.Keys.Where(k => ModelState[k].Errors.Count > 0).ToList();
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Write)]
|
|
public ActionResult ActivateMix(string model)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(model) || ContentLocker.IsLock("Mix", model.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
var mixManager = ResolveMixManager(DbProvider.MongoDb);
|
|
mixManager.ActivateMix(model);
|
|
|
|
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);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
|
|
public ActionResult LoadCalendar(MixSaveModel model)
|
|
{
|
|
MixSaveModel clientModel = model;
|
|
|
|
if ((clientModel == null) || (clientModel.Filter == null) || (clientModel.Filter.Selection == null))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
if ((clientModel.Filter.Selection.StartDate < 1) || (clientModel.Filter.Selection.EndDate < 1))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
var startDate = Utils.ConvertFromUnixDate(clientModel.Filter.Selection.StartDate);
|
|
var endDate = Utils.ConvertFromUnixDate(clientModel.Filter.Selection.EndDate);
|
|
var endDateCorrected = endDate.AddDays(6);
|
|
|
|
var teams = clientModel.Filter.Selection.TeamsViews.Where(t => !t.IsNew && t.Group.Name == "Teams")
|
|
.Select(t => new Guid(t.Id)).Distinct().ToList();
|
|
var views = clientModel.Filter.Selection.TeamsViews.Where(t => t.Group.Name == "Views")
|
|
.Select(t => new Guid(t.Id)).Distinct().ToList();
|
|
var newTeams = clientModel.Filter.Selection.TeamsViews.Where(x => x.IsNew).ToList();
|
|
|
|
MixSaveModel serverModel = new MixSaveModel();
|
|
GetCalendar(serverModel.Calendar, startDate, endDateCorrected, teams, views, newTeams);
|
|
|
|
MixModelsMergeManager mergeMngr = new MixModelsMergeManager();
|
|
mergeMngr.MergeFilters(serverModel.Filter, clientModel.Filter);
|
|
mergeMngr.MergeCalendars(serverModel, clientModel, startDate, endDate);
|
|
|
|
return new JsonResult()
|
|
{
|
|
Data = serverModel,
|
|
MaxJsonLength = int.MaxValue
|
|
};
|
|
}
|
|
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);
|
|
}
|
|
|
|
#region Private Methods
|
|
|
|
private void GetCalendar(MixCalendarModel model, DateTime startDate, DateTime endDate, List<Guid> teamsIds,
|
|
List<Guid> viewsIds, List<MixTeamViewModel> NewTeams)
|
|
{
|
|
// SA. ENV-1218
|
|
UOMManager uomMngr = new UOMManager(DbContext);
|
|
Dictionary<Guid, UOM> uoms = uomMngr.GetAsDictionary();
|
|
|
|
var fiscalCalendarManager = (new FiscalCalendarManager(DbContext));
|
|
var teamManager = (new TeamManager(DbContext));
|
|
var mixManager = ResolveMixManager(DbProvider.MongoDb);
|
|
var scenarioManager = (new ScenarioManager(DbContext));
|
|
var prManager = (new PeopleResourcesManager(DbContext));
|
|
|
|
var fiscalCalendar = fiscalCalendarManager.GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, null, null, true, null);
|
|
var weekEndings = fiscalCalendar.FindAll(x => x.EndDate >= startDate && x.EndDate <= endDate).Select(x => x.EndDate).OrderBy(x => x).ToList();
|
|
var teams = teamManager.FindTeams(teamsIds, viewsIds, User.Identity.GetID());
|
|
var teams2Scenarios = teamManager.GetRelationsWithScenariosTree(teams.Select(x => x.Id).ToList(),
|
|
ScenarioType.Portfolio, ScenarioStatus.Active);
|
|
var activeScenarios = teams2Scenarios.SelectMany(x => x.Value.Select(s => s.ScenarioId)).Distinct().ToList();
|
|
var allTeamsProjects = teams.SelectMany(x => x.Team2Project.Select(p => p.ProjectId)).Distinct().ToList();
|
|
|
|
var plannedCapacityScenarios =
|
|
teams.Where(x => x.PlannedCapacityScenarioId.HasValue).Select(x => x.PlannedCapacityScenarioId.Value).ToList();
|
|
var actualCapacityScenarios =
|
|
teams.Where(x => x.ActualCapacityScenarioId.HasValue).Select(x => x.ActualCapacityScenarioId.Value).ToList();
|
|
var capacityScenarios = plannedCapacityScenarios.Union(actualCapacityScenarios).ToList();
|
|
|
|
var expCatsInTeams = scenarioManager.GetCategoriesInScenarios(capacityScenarios);
|
|
|
|
// for reducing number of retrieving scenario details rows we should to retrieve only by ECs which exists in teams
|
|
var scenarioDetails = scenarioManager.GetScenarioDetailsTree(capacityScenarios.Union(activeScenarios).ToList(),
|
|
expCatsInTeams.Keys.ToList());
|
|
|
|
// for reducing number of retrieving teams allocations rows we should to retrieve only by active scenarios and ECs which exists in teams
|
|
var teamsAllocations = teamManager.GetTeamsAllocationTree(teams.Select(x => x.Id).ToList(), activeScenarios,
|
|
expCatsInTeams.Keys.ToList());
|
|
|
|
var resources = prManager.FindResourcesTree(teams.Select(x => x.Id), true);
|
|
var resourcesIds = resources.SelectMany(x => x.Value.SelectMany(r => r.Value.Select(rv => rv.Id)));
|
|
|
|
model.FiscalCalendarWeekEndings = fiscalCalendar.Select(x => Utils.ConvertToUnixDate(x.EndDate)).OrderBy(x => x).ToList();
|
|
model.WeekEndings = CastWeekEndingsToTree(weekEndings);
|
|
model.Projects = mixManager.GetProjectModel(allTeamsProjects).ToDictionary(x => x.Id.ToString());
|
|
model.Vacations = prManager.GetTotalVacaitons(resourcesIds)
|
|
.ToDictionary(x => x.Key.ToString(), g => g.Value.ToDictionary(v => Utils.ConvertToUnixDate(v.Key).ToString(), gv => gv.Value));
|
|
model.Trainings = prManager.GetTotalTrainings(resourcesIds)
|
|
.ToDictionary(x => x.Key.ToString(), g => g.Value.ToDictionary(v => Utils.ConvertToUnixDate(v.Key).ToString(), gv => gv.Value));
|
|
|
|
#region Existence Teams Filling
|
|
|
|
foreach (var team in teams)
|
|
{
|
|
var teamAllocations = teamsAllocations.ContainsKey(team.Id) ? teamsAllocations[team.Id] : null;
|
|
var teamResources = resources.ContainsKey(team.Id) ? resources[team.Id] : null;
|
|
|
|
var mixTeam = new MixTeamModel()
|
|
{
|
|
Id = team.Id.ToString(),
|
|
Name = team.Name
|
|
};
|
|
|
|
var plannedCapacityScenario = (team.PlannedCapacityScenarioId.HasValue &&
|
|
scenarioDetails.ContainsKey(team.PlannedCapacityScenarioId.Value))
|
|
? scenarioDetails[team.PlannedCapacityScenarioId.Value]
|
|
: null;
|
|
|
|
var actualCapacityScenario = (team.ActualCapacityScenarioId.HasValue &&
|
|
scenarioDetails.ContainsKey(team.ActualCapacityScenarioId.Value))
|
|
? scenarioDetails[team.ActualCapacityScenarioId.Value]
|
|
: null;
|
|
|
|
var expCatsInTeam = new List<Guid>();
|
|
if (plannedCapacityScenario != null && plannedCapacityScenario.Keys.Count > 0)
|
|
expCatsInTeam.AddRange(plannedCapacityScenario.Keys);
|
|
|
|
if (actualCapacityScenario != null && actualCapacityScenario.Keys.Count > 0)
|
|
expCatsInTeam.AddRange(actualCapacityScenario.Keys);
|
|
|
|
foreach (var expCatId in expCatsInTeam.Distinct())
|
|
{
|
|
Guid uomId = expCatsInTeams[expCatId].UOMId; // SA. ENV-1218
|
|
var activityCategory = new ActivityExpCategoryFooterModel()
|
|
{
|
|
Id = expCatId,
|
|
Name = expCatsInTeams[expCatId].ExpCategoryWithCcName, // Name, SA. ENV-1218
|
|
UomValue = uoms[uomId].UOMValue // SA. ENV-1218
|
|
};
|
|
|
|
if (plannedCapacityScenario != null && plannedCapacityScenario.ContainsKey(expCatId))
|
|
{
|
|
activityCategory.PlannedCapacityValues = plannedCapacityScenario[expCatId]
|
|
.Where(x => x.WeekEndingDate.HasValue)
|
|
.OrderBy(x => x.WeekEndingDate)
|
|
.ToDictionary(x => Utils.ConvertToUnixDate(x.WeekEndingDate.Value).ToString(), g => g.Quantity ?? 0);
|
|
}
|
|
|
|
if (actualCapacityScenario != null && actualCapacityScenario.ContainsKey(expCatId))
|
|
{
|
|
activityCategory.ActualCapacityValues = actualCapacityScenario[expCatId]
|
|
.Where(x => x.WeekEndingDate.HasValue)
|
|
.OrderBy(x => x.WeekEndingDate)
|
|
.ToDictionary(x => Utils.ConvertToUnixDate(x.WeekEndingDate.Value).ToString(), g => g.Quantity ?? 0);
|
|
}
|
|
|
|
if (teams2Scenarios.ContainsKey(team.Id))
|
|
{
|
|
foreach (var team2scenario in teams2Scenarios[team.Id])
|
|
{
|
|
var teamAllocationsInScenarioAndEC = (teamAllocations != null &&
|
|
teamAllocations.ContainsKey(team2scenario.ScenarioId) &&
|
|
teamAllocations[team2scenario.ScenarioId].ContainsKey(expCatId))
|
|
? teamAllocations[team2scenario.ScenarioId][expCatId]
|
|
: new Dictionary<DateTime, TeamAllocation>();
|
|
|
|
if (!scenarioDetails.ContainsKey(team2scenario.ScenarioId))
|
|
continue;
|
|
|
|
if (!scenarioDetails[team2scenario.ScenarioId].ContainsKey(expCatId))
|
|
continue;
|
|
|
|
foreach (var detail in scenarioDetails[team2scenario.ScenarioId][expCatId])
|
|
{
|
|
decimal quantity = 0;
|
|
if (teamAllocationsInScenarioAndEC.ContainsKey(detail.WeekEndingDate.Value))
|
|
quantity = teamAllocationsInScenarioAndEC[detail.WeekEndingDate.Value].Quantity;
|
|
else
|
|
quantity = (team2scenario.Allocation * (detail.Quantity ?? 0)) / (decimal)100.0;
|
|
|
|
var sdKey = Utils.ConvertToUnixDate(detail.WeekEndingDate.Value).ToString();
|
|
if (!activityCategory.NeedCapacity.ContainsKey(sdKey))
|
|
activityCategory.NeedCapacity.Add(sdKey, 0);
|
|
|
|
activityCategory.NeedCapacity[sdKey] += quantity;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (teamResources != null && teamResources.ContainsKey(expCatId))
|
|
{
|
|
foreach (var resource in teamResources[expCatId])
|
|
{
|
|
var resourceModel = new ActivityResourceFooterModel()
|
|
{
|
|
Id = resource.Id,
|
|
Name = string.Format("{0} {1}", resource.FirstName, resource.LastName)
|
|
};
|
|
|
|
var fiscalCalendarWeeks4Resource = fiscalCalendar.Where(x => x.StartDate >= resource.StartDate && x.EndDate <= resource.EndDate).Select(x => x.EndDate).ToList();
|
|
|
|
// TODO: take into consideration trainings and vacations for current resource
|
|
foreach (var weekEnding in fiscalCalendarWeeks4Resource)
|
|
{
|
|
var rKey = Utils.ConvertToUnixDate(weekEnding).ToString();
|
|
if (!resourceModel.TotalCapacity.ContainsKey(rKey))
|
|
resourceModel.TotalCapacity.Add(rKey, activityCategory.UomValue);
|
|
}
|
|
|
|
foreach (var rAllocation in resource.PeopleResourceAllocations)
|
|
{
|
|
if (!rAllocation.WeekEndingDate.HasValue)
|
|
continue;
|
|
|
|
var rKey = Utils.ConvertToUnixDate(rAllocation.WeekEndingDate.Value).ToString();
|
|
if (!resourceModel.AllocatedCapacity.ContainsKey(rKey))
|
|
resourceModel.AllocatedCapacity.Add(rKey, 0);
|
|
|
|
if (!activityCategory.AllocatedCapacity.ContainsKey(rKey))
|
|
activityCategory.AllocatedCapacity.Add(rKey, 0);
|
|
|
|
resourceModel.AllocatedCapacity[rKey] += rAllocation.Quantity ?? 0;
|
|
activityCategory.AllocatedCapacity[rKey] += rAllocation.Quantity ?? 0;
|
|
}
|
|
|
|
activityCategory.Resources.Add(resourceModel.Id.ToString(), resourceModel);
|
|
}
|
|
}
|
|
|
|
mixTeam.ExpCategories.Add(activityCategory.Id.ToString(), activityCategory);
|
|
}
|
|
|
|
model.Teams.Add(mixTeam);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region New Teams Filling
|
|
|
|
if (NewTeams != null)
|
|
{
|
|
foreach (var team in NewTeams.Where(x => !teams.Any(t => t.Id.ToString() == x.Id)))
|
|
{
|
|
var mixTeam = new MixTeamModel()
|
|
{
|
|
Id = team.Id.ToString(),
|
|
Name = team.TVName,
|
|
CompanyId = team.CompanyId,
|
|
UserId = team.UserId,
|
|
CostCenterId = team.CostCenterId,
|
|
Group = team.Group,
|
|
IsNew = true
|
|
};
|
|
var expCatsInTeam = new List<Guid>();
|
|
if (null != team.Data)
|
|
{
|
|
if (team.Data != null)
|
|
expCatsInTeam = team.Data.Where(t => t != null).Select(x => x.Id).ToList();
|
|
var expCats = DbContext.ExpenditureCategory.Where(x => expCatsInTeam.Contains(x.Id))
|
|
.ToDictionary(x => x.Id, x => x);
|
|
foreach (var expCat in team.Data.Where(t => t != null))
|
|
{
|
|
if (expCat.Positions != null)
|
|
foreach (var pos in expCat.Positions)
|
|
{
|
|
pos.SDate = Utils.ConvertFromUnixDate(long.Parse(pos.StartDate.Substring(6, 13)));
|
|
pos.EDate = (!string.IsNullOrEmpty(pos.EndDate))
|
|
? Utils.ConvertFromUnixDate(long.Parse(pos.EndDate.Substring(6, 13)))
|
|
: (DateTime?)null;
|
|
}
|
|
|
|
var activityCategory = new ActivityExpCategoryFooterModel()
|
|
{
|
|
Id = expCat.Id,
|
|
Name = expCats.ContainsKey(expCat.Id) ? expCats[expCat.Id].Name : string.Empty,
|
|
UomValue = expCats.ContainsKey(expCat.Id)
|
|
? expCats[expCat.Id].UOM != null ? expCats[expCat.Id].UOM.UOMValue : 0
|
|
: 0
|
|
};
|
|
|
|
foreach (var week in weekEndings)
|
|
{
|
|
activityCategory.PlannedCapacityValues.Add(Utils.ConvertToUnixDate(week).ToString(),
|
|
expCat.Positions == null
|
|
? 0
|
|
: expCat.Positions.Count(
|
|
x => x.SDate <= week && (!x.EDate.HasValue || x.EDate >= week)) * activityCategory.UomValue);
|
|
}
|
|
mixTeam.ExpCategories.Add(activityCategory.Id.ToString(), activityCategory);
|
|
|
|
// SA. ENV-1025. We need to reset attributes, because the values cause model deserialization problems
|
|
// on saving Mix operation
|
|
if (expCat.Positions != null)
|
|
foreach (var pos in expCat.Positions)
|
|
{
|
|
pos.SDate = null;
|
|
pos.EDate = null;
|
|
}
|
|
}
|
|
}
|
|
model.Teams.Add(mixTeam);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
model.Teams = model.Teams.OrderBy(x => x.Name).ToList();
|
|
}
|
|
|
|
private Dictionary<string, Dictionary<string, List<int>>> CastWeekEndingsToTree(List<DateTime> weekEndings)
|
|
{
|
|
var weekEndingsHierarchy = weekEndings.Select(x => new
|
|
{
|
|
x.Year,
|
|
x.Month,
|
|
x.Day
|
|
})
|
|
.GroupBy(x => x.Year)
|
|
.OrderBy(x => x.Key)
|
|
.ToDictionary(year => year.Key.ToString(),
|
|
months => months.GroupBy(month => month.Month)
|
|
.OrderBy(month => month.Key)
|
|
.ToDictionary(month => month.Key.ToString(),
|
|
days => days.Select(day => day.Day).OrderBy(day => day).ToList()));
|
|
return weekEndingsHierarchy;
|
|
}
|
|
|
|
private IMixManager<Code.DAL.Mongo.Mix> ResolveMixManager(DbProvider dbProvider)
|
|
{
|
|
switch (dbProvider)
|
|
{
|
|
//case DbProvider.SqlServer:
|
|
// return new MixManager(DbContext);
|
|
case DbProvider.MongoDb:
|
|
return new MongoMixManager(DbContext, User.Identity.GetID());
|
|
default:
|
|
throw new InvalidOperationException(string.Format("DbProvider with value = {0} not defined", dbProvider.GetHashCode()));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
// GET: /Team/Edit/5
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult EditTeam(Guid? id)
|
|
{
|
|
bool isNewTeam = !id.HasValue || (id.Value == Guid.Empty);
|
|
ViewBag.IsNewTeam = isNewTeam;
|
|
|
|
var model = new TeamModel();
|
|
try
|
|
{
|
|
if (!isNewTeam)
|
|
{
|
|
var manager = new TeamManager(DbContext);
|
|
model = (TeamModel)manager.Load(id) ?? new TeamModel();
|
|
}
|
|
else
|
|
{
|
|
model.Id = Guid.NewGuid();
|
|
}
|
|
|
|
if (isNewTeam)
|
|
{
|
|
model.UserId = new List<Guid>();
|
|
model.UserId.Add(new Guid(User.Identity.GetUserId()));
|
|
}
|
|
else
|
|
{
|
|
model.UserId = DbContext.User2Team.Where(x => x.TeamId == model.Id).ToList().Select(x => Guid.Parse(x.UserId)).ToList();
|
|
}
|
|
|
|
return PartialView("_mixTeams", model);
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
{
|
|
//SetErrorScript(message: blEx.Message);
|
|
ModelState.AddModelError(string.Empty, "Cannot save view. Try again later.");
|
|
}
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
//SetErrorScript();
|
|
ModelState.AddModelError(string.Empty, "Cannot save view. Try again later.");
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
|
|
return PartialView("_mixTeams", model);
|
|
}
|
|
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult EditTeam(TeamModel model)
|
|
{
|
|
if (model == null || ContentLocker.IsLock("Team", model.Id.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
|
|
if (model.ReportToId.HasValue && model.ReportToId == Guid.Empty)
|
|
model.ReportToId = null;
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var context = new EnVisageEntities();
|
|
var manager = new TeamManager(context);
|
|
manager.Save(model);
|
|
context.SaveChanges();
|
|
//(new ProjectAccessCache()).Invalidate();
|
|
ContentLocker.RemoveLock("Team", model.Id.ToString(), User.Identity.Name);
|
|
return PartialView("_mixTeams", model);
|
|
}
|
|
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();
|
|
ModelState.AddModelError(string.Empty, "Cannot save team. Try again later.");
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
//SetErrorScript();
|
|
ModelState.AddModelError(string.Empty, "Cannot save team. Try again later.");
|
|
}
|
|
}
|
|
// return empty model with validation messages (if any)
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
|
|
return PartialView("_mixTeams", model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get Scenario details for the Project in da mix
|
|
/// </summary>
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult GetScenarioDetails(MixScenarioDetailsLoadModel model)
|
|
{
|
|
if (model == null || model.ScenarioId == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var errorMessage = string.Empty;
|
|
try
|
|
{
|
|
var mixManager = ResolveMixManager(DbProvider.MongoDb);
|
|
var allocations = mixManager.GetFullAllocationInfoByScenario(model.MixId, model.ScenarioId);
|
|
|
|
return Json(allocations);
|
|
}
|
|
catch (BLLException blEx) // handle any system specific error
|
|
{
|
|
// display error message if required
|
|
if (blEx.DisplayError)
|
|
errorMessage = blEx.Message;
|
|
else // if display not requried then display modal form with general error message
|
|
{
|
|
LogException(blEx);
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
}
|
|
// return empty model with validation messages (if any)
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
return Json(errorMessage);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Write)]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult EditScenarioDetails(ScenarioCalendarMixModel model)
|
|
{
|
|
if (model == null || model.Id == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var errorMessage = string.Empty;
|
|
try
|
|
{
|
|
var user = (new UsersCache()).Value.FirstOrDefault(x => x.Id == new Guid(User.Identity.GetID()));
|
|
var detailsModel = (new ScenarioDetailsCalendarModel(model, user));
|
|
return PartialView("~/Views/Scenarios/_scenarioCalendar.cshtml", detailsModel);
|
|
}
|
|
catch (BLLException blEx) // handle any system specific error
|
|
{
|
|
// display error message if required
|
|
if (blEx.DisplayError)
|
|
errorMessage = blEx.Message;
|
|
else // if display not requried then display modal form with general error message
|
|
{
|
|
LogException(blEx);
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
}
|
|
|
|
// return empty model with validation messages (if any)
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
return Json(errorMessage);
|
|
}
|
|
|
|
// SA. ENV-1254
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult GetTeamsById(Guid[] ids, long startDateMs, long endDateMs)
|
|
{
|
|
var teamModels = new List<MixTeamModel>();
|
|
DateTime startDate = Utils.ConvertFromUnixDate(startDateMs);
|
|
DateTime endDate = Utils.ConvertFromUnixDate(endDateMs);
|
|
|
|
var teams = DbContext.Teams.AsNoTracking().Where(x => ids.Contains(x.Id)).ToList();
|
|
|
|
// SA. ENV-1218
|
|
UOMManager uomMngr = new UOMManager(DbContext);
|
|
Dictionary<Guid, UOM> uoms = uomMngr.GetAsDictionary();
|
|
|
|
var fiscalCalendarManager = (new FiscalCalendarManager(DbContext));
|
|
var teamManager = (new TeamManager(DbContext));
|
|
var scenarioManager = (new ScenarioManager(DbContext));
|
|
var prManager = (new PeopleResourcesManager(DbContext));
|
|
|
|
var fiscalCalendar = fiscalCalendarManager.GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, null, null, true, null);
|
|
var weekEndings = fiscalCalendar.FindAll(x => x.EndDate >= startDate && x.EndDate <= endDate).Select(x => x.EndDate).OrderBy(x => x).ToList();
|
|
|
|
|
|
var teams2Scenarios = teamManager.GetRelationsWithScenariosTree(teams.Select(x => x.Id).ToList(),
|
|
ScenarioType.Portfolio, ScenarioStatus.Active);
|
|
var activeScenarios = teams2Scenarios.SelectMany(x => x.Value.Select(s => s.ScenarioId)).Distinct().ToList();
|
|
var allTeamsProjects = teams.SelectMany(x => x.Team2Project.Select(p => p.ProjectId)).Distinct().ToList();
|
|
|
|
var plannedCapacityScenarios =
|
|
teams.Where(x => x.PlannedCapacityScenarioId.HasValue).Select(x => x.PlannedCapacityScenarioId.Value).ToList();
|
|
var actualCapacityScenarios =
|
|
teams.Where(x => x.ActualCapacityScenarioId.HasValue).Select(x => x.ActualCapacityScenarioId.Value).ToList();
|
|
var capacityScenarios = plannedCapacityScenarios.Union(actualCapacityScenarios).ToList();
|
|
|
|
var expCatsInTeams = scenarioManager.GetCategoriesInScenarios(capacityScenarios);
|
|
|
|
// for reducing number of retrieving scenario details rows we should to retrieve only by ECs which exists in teams
|
|
var scenarioDetails = scenarioManager.GetScenarioDetailsTree(capacityScenarios.Union(activeScenarios).ToList(),
|
|
expCatsInTeams.Keys.ToList());
|
|
|
|
// for reducing number of retrieving teams allocations rows we should to retrieve only by active scenarios and ECs which exists in teams
|
|
var teamsAllocations = teamManager.GetTeamsAllocationTree(teams.Select(x => x.Id).ToList(), activeScenarios,
|
|
expCatsInTeams.Keys.ToList());
|
|
|
|
var resources = prManager.FindResourcesTree(teams.Select(x => x.Id), true);
|
|
var resourcesIds = resources.SelectMany(x => x.Value.SelectMany(r => r.Value.Select(rv => rv.Id)));
|
|
|
|
foreach(var team in teams)
|
|
{
|
|
MixTeamModel mixTeam = new MixTeamModel()
|
|
{
|
|
Id = team.Id.ToString(),
|
|
Name = team.Name
|
|
};
|
|
var teamAllocations = teamsAllocations.ContainsKey(team.Id) ? teamsAllocations[team.Id] : null;
|
|
var teamResources = resources.ContainsKey(team.Id) ? resources[team.Id] : null;
|
|
|
|
var plannedCapacityScenario = (team.PlannedCapacityScenarioId.HasValue &&
|
|
scenarioDetails.ContainsKey(team.PlannedCapacityScenarioId.Value))
|
|
? scenarioDetails[team.PlannedCapacityScenarioId.Value]
|
|
: null;
|
|
|
|
var actualCapacityScenario = (team.ActualCapacityScenarioId.HasValue &&
|
|
scenarioDetails.ContainsKey(team.ActualCapacityScenarioId.Value))
|
|
? scenarioDetails[team.ActualCapacityScenarioId.Value]
|
|
: null;
|
|
|
|
var expCatsInTeam = new List<Guid>();
|
|
if (plannedCapacityScenario != null && plannedCapacityScenario.Keys.Count > 0)
|
|
expCatsInTeam.AddRange(plannedCapacityScenario.Keys);
|
|
|
|
if (actualCapacityScenario != null && actualCapacityScenario.Keys.Count > 0)
|
|
expCatsInTeam.AddRange(actualCapacityScenario.Keys);
|
|
|
|
foreach (var expCatId in expCatsInTeam.Distinct())
|
|
{
|
|
Guid uomId = expCatsInTeams[expCatId].UOMId; // SA. ENV-1218
|
|
var activityCategory = new ActivityExpCategoryFooterModel()
|
|
{
|
|
Id = expCatId,
|
|
Name = expCatsInTeams[expCatId].ExpCategoryWithCcName, // Name, SA. ENV-1218
|
|
UomValue = uoms[uomId].UOMValue // SA. ENV-1218
|
|
};
|
|
|
|
if (plannedCapacityScenario != null && plannedCapacityScenario.ContainsKey(expCatId))
|
|
{
|
|
activityCategory.PlannedCapacityValues = plannedCapacityScenario[expCatId]
|
|
.Where(x => x.WeekEndingDate.HasValue)
|
|
.OrderBy(x => x.WeekEndingDate)
|
|
.ToDictionary(x => Utils.ConvertToUnixDate(x.WeekEndingDate.Value).ToString(), g => g.Quantity ?? 0);
|
|
}
|
|
|
|
if (actualCapacityScenario != null && actualCapacityScenario.ContainsKey(expCatId))
|
|
{
|
|
activityCategory.ActualCapacityValues = actualCapacityScenario[expCatId]
|
|
.Where(x => x.WeekEndingDate.HasValue)
|
|
.OrderBy(x => x.WeekEndingDate)
|
|
.ToDictionary(x => Utils.ConvertToUnixDate(x.WeekEndingDate.Value).ToString(), g => g.Quantity ?? 0);
|
|
}
|
|
|
|
if (teams2Scenarios.ContainsKey(team.Id))
|
|
{
|
|
foreach (var team2scenario in teams2Scenarios[team.Id])
|
|
{
|
|
var teamAllocationsInScenarioAndEC = (teamAllocations != null &&
|
|
teamAllocations.ContainsKey(team2scenario.ScenarioId) &&
|
|
teamAllocations[team2scenario.ScenarioId].ContainsKey(expCatId))
|
|
? teamAllocations[team2scenario.ScenarioId][expCatId]
|
|
: new Dictionary<DateTime, TeamAllocation>();
|
|
|
|
if (!scenarioDetails.ContainsKey(team2scenario.ScenarioId))
|
|
continue;
|
|
|
|
if (!scenarioDetails[team2scenario.ScenarioId].ContainsKey(expCatId))
|
|
continue;
|
|
|
|
foreach (var detail in scenarioDetails[team2scenario.ScenarioId][expCatId])
|
|
{
|
|
decimal quantity = 0;
|
|
if (teamAllocationsInScenarioAndEC.ContainsKey(detail.WeekEndingDate.Value))
|
|
quantity = teamAllocationsInScenarioAndEC[detail.WeekEndingDate.Value].Quantity;
|
|
else
|
|
quantity = (team2scenario.Allocation * (detail.Quantity ?? 0)) / (decimal)100.0;
|
|
|
|
var sdKey = Utils.ConvertToUnixDate(detail.WeekEndingDate.Value).ToString();
|
|
if (!activityCategory.NeedCapacity.ContainsKey(sdKey))
|
|
activityCategory.NeedCapacity.Add(sdKey, 0);
|
|
|
|
activityCategory.NeedCapacity[sdKey] += quantity;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (teamResources != null && teamResources.ContainsKey(expCatId))
|
|
{
|
|
foreach (var resource in teamResources[expCatId])
|
|
{
|
|
var resourceModel = new ActivityResourceFooterModel()
|
|
{
|
|
Id = resource.Id,
|
|
Name = string.Format("{0} {1}", resource.FirstName, resource.LastName)
|
|
};
|
|
|
|
var fiscalCalendarWeeks4Resource = fiscalCalendar.Where(x => x.StartDate >= resource.StartDate && x.EndDate <= resource.EndDate).Select(x => x.EndDate).ToList();
|
|
|
|
// TODO: take into consideration trainings and vacations for current resource
|
|
foreach (var weekEnding in fiscalCalendarWeeks4Resource)
|
|
{
|
|
var rKey = Utils.ConvertToUnixDate(weekEnding).ToString();
|
|
if (!resourceModel.TotalCapacity.ContainsKey(rKey))
|
|
resourceModel.TotalCapacity.Add(rKey, activityCategory.UomValue);
|
|
}
|
|
|
|
foreach (var rAllocation in resource.PeopleResourceAllocations)
|
|
{
|
|
if (!rAllocation.WeekEndingDate.HasValue)
|
|
continue;
|
|
|
|
var rKey = Utils.ConvertToUnixDate(rAllocation.WeekEndingDate.Value).ToString();
|
|
if (!resourceModel.AllocatedCapacity.ContainsKey(rKey))
|
|
resourceModel.AllocatedCapacity.Add(rKey, 0);
|
|
|
|
if (!activityCategory.AllocatedCapacity.ContainsKey(rKey))
|
|
activityCategory.AllocatedCapacity.Add(rKey, 0);
|
|
|
|
resourceModel.AllocatedCapacity[rKey] += rAllocation.Quantity ?? 0;
|
|
activityCategory.AllocatedCapacity[rKey] += rAllocation.Quantity ?? 0;
|
|
}
|
|
|
|
activityCategory.Resources.Add(resourceModel.Id.ToString(), resourceModel);
|
|
}
|
|
}
|
|
|
|
mixTeam.ExpCategories.Add(activityCategory.Id.ToString(), activityCategory);
|
|
}
|
|
teamModels.Add(mixTeam);
|
|
}
|
|
|
|
return Json(teamModels);
|
|
}
|
|
}
|
|
} |