EnVisageOnline/Main/Source/EnVisage/Controllers/MixController.cs

1566 lines
68 KiB
C#

using EnVisage.App_Start;
using EnVisage.Code;
using EnVisage.Code.BLL;
using EnVisage.Code.Cache;
using EnVisage.Models;
using Iniphi.Calculations;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web.Mvc;
using Resources;
using StackExchange.Profiling;
namespace EnVisage.Controllers
{
[Authorize]
public class MixController : BaseController
{
#region Private Members
private ScenarioUIManager ScenarioUIManager { get; }
#endregion
#region Constructors
public MixController(ScenarioUIManager scenarioUIManager)
{
ScenarioUIManager = scenarioUIManager;
}
#endregion
#region Actions
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult Index(string id)
{
return View("Index", null, id);
}
[HttpPost]
[ValidateJsonAntiForgeryToken]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult GetPageFilter()
{
try
{
var userId = SecurityManager.GetUserPrincipal();
var model = new MixSaveModel
{
Users = new List<Guid> { userId }
};
LoadMixFilterValues(model.Filter.Variants, userId.ToString());
return Json(model);
}
catch (Exception exception)
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[ValidateJsonAntiForgeryToken]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult LoadMix(string model)
{
try
{
#if DEBUG
var watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
var serverModel = LoadMixSaveModel(model);
#if DEBUG
watch1.Stop();
#endif
// Fill client-side filter values
LoadClientSideFilterValues(serverModel);
UpdateFilterSelection(serverModel, null);
return BigJson(serverModel);
}
catch (Exception exception)
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
#endregion
[HttpPost]
[ValidateJsonAntiForgeryToken]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult DeleteMix(string model)
{
try
{
if (!string.IsNullOrWhiteSpace(model))
{
var mixManager = ResolveMixManager(DbProvider.MongoDb);
mixManager.Delete(model);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
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.GetUserName()))
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.Key.ToString()
});
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
}
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, User.Identity.GetUserName()))
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
try
{
var mixManager = ResolveMixManager(DbProvider.MongoDb);
var mix = mixManager.RetrieveWithChildren(model);
if (mix == null)
throw new InvalidOperationException($"Mix with Key [{model}] can not be activated because it does not exists.");
if (mix.Calendar != null)
{
#if DEBUG
var watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
var createdScenariosInfo = mixManager.ActivateMix(mix, model);
#if DEBUG
watch1.Stop();
#endif
// Get scenario versions (timestamps) in the mix model
var recentlyActivatedProjects = createdScenariosInfo.Select(x => x.ProjectId.ToString()).ToList();
var scenariosVersionInfo = GetScenariosTimestampsInternal(recentlyActivatedProjects);
MixModelsMergeManager.SetScenarioVersions(mix, scenariosVersionInfo);
// Save mix to Mongo
mixManager.SaveWithChildren(mix);
DbContext.SaveChanges();
}
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
//actionFinished = DateTime.UtcNow;
//totalActionTimeSeconds = Math.Round((actionFinished - actionStarted).TotalSeconds, 1).ToString();
//LogDebugMessage(String.Format("DEBUG: Mix Activation action completed with ERROR (mixId: {0}, Complete time (sec): {1})", model, totalActionTimeSeconds));
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[ValidateJsonAntiForgeryToken]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult HasResourceAssignments(MixCalendarModel model)
{
try
{
var result = Json(new
{
allowResourceRace = HasResourceAssignmentsBool(model),
CanDoTeamRace = HasSupperEcUnAssigned(model),
canDoRace = ValidateForRace(model)
}, JsonRequestBehavior.AllowGet);
return result;
}
catch (Exception exception)
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[ValidateJsonAntiForgeryToken]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult LoadCalendar(MixSaveModel model)
{
var userId = User.Identity.GetID();
MixSaveModel clientModel = model;
if (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, true);
// Construct need data from expenditure's data in model. Do it only for client model, because
// server model already got this data inside GetCalendar method
BuildNeedAllocations(clientModel.Calendar);
// Update filters
LoadMixFilterValues(serverModel.Filter.Variants, userId);
serverModel.Users = (clientModel.Users != null) && (clientModel.Users.Count > 0) ? clientModel.Users : new List<Guid>() { new Guid(userId) };
MixModelsMergeManager mergeMngr = new MixModelsMergeManager(DbContext, userId);
mergeMngr.MergeFilters(serverModel.Filter, clientModel.Filter);
mergeMngr.MergeCalendars(serverModel, clientModel, startDate, endDate);
// Fill client-side filter values
MixFilterSelectionModel clientSideFiltersSelection = model.Filter?.Selection;
LoadClientSideFilterValues(serverModel);
UpdateFilterSelection(serverModel, clientSideFiltersSelection);
var mix = new Code.DAL.Mongo.Mix();
serverModel.CopyTo(mix);
var mixManager = ResolveMixManager(DbProvider.MongoDb);
var fullMongoMix = serverModel;
if (!string.IsNullOrWhiteSpace(serverModel.Id))
fullMongoMix = mixManager.RetrieveWithChildren(serverModel.Id);
if (fullMongoMix == null)
fullMongoMix = serverModel;
serverModel.CanRunResourceRace = HasResourceAssignmentsBool(fullMongoMix.Calendar);
serverModel.CanRunTeamRace = HasSupperEcUnAssigned(fullMongoMix.Calendar);
serverModel.canDoRace = ValidateForRace(serverModel);
return BigJson(serverModel);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[ValidateJsonAntiForgeryToken]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult DoRace(MixSaveModel model)
{
MixSaveModel clientModel = model;
if (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 = FiscalCalendarManager.GetFiscalCalendarWeekForDate(endDate, DbContext).EndDate;
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();
List<DateTime> weekendings = FiscalCalendarManager.GetWeekendingsByRange(startDate, endDate, DbContext);
MixSaveModel serverModel = new MixSaveModel();
GetCalendar(serverModel.Calendar, startDate, endDateCorrected, teams, views, newTeams, false);
MixModelsMergeManager mergeMngr = new MixModelsMergeManager(DbContext, User.Identity.GetID());
mergeMngr.MergeFilters(serverModel.Filter, clientModel.Filter);
mergeMngr.MergeCalendars(serverModel, clientModel, startDate, endDate);
RaceMatrix rmx = new RaceMatrix(0, GetRaceModule(), clientModel.TeamLevelRace, serverModel, 1);
if (rmx.Projects.Length == 0)
{
throw new Exception("Race was not able to load one or more projects in the mix and can not be run due to errors");
}
int nbrProjs = rmx.Projects.Count(x => x.isPinned == false);
int cycles = weekendings.Count;
var raceIterations = GetPermutations(Enumerable.Range(1, cycles), nbrProjs).ToList();
int onIteration = 1;
foreach (var cycle in raceIterations)
{
rmx.BuildProjectIterationScores(onIteration, cycle.ToArray());
onIteration++;
}
var raceScores = GetRaceModule().GetBestMix();
var mixData = rmx.GetMixDataForIteration(raceIterations[raceScores.MixIteration - 1].ToArray());
var raceResults = new
{
mixData,
raceScore = Math.Round(raceScores.TotalScore),
raceIteration = raceScores.MixIteration,
raceScore1 = raceScores.Score1,
// raceScore2 = raceScores.Score2,
};
JsonResult rs = Json(raceResults, JsonRequestBehavior.AllowGet);
return rs;
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[ValidateJsonAntiForgeryToken]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
public ActionResult DoRace2(MixSaveModel model)
{
if (model?.Filter?.Selection == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
if (model.Filter.Selection.StartDate < 1 || model.Filter.Selection.EndDate < 1)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var mixManager = ResolveMixManager(DbProvider.MongoDb);
var fullMongoMix = model;
if (!string.IsNullOrWhiteSpace(model.Id))
fullMongoMix = mixManager.RetrieveWithChildren(model.Id);
if (fullMongoMix == null)
fullMongoMix = model;
try
{
RaceMatrix rmx = new RaceMatrix(0, GetRaceModule(), model.TeamLevelRace, fullMongoMix, 2);
if (rmx.Projects.Length == 0)
{
throw new Exception("Race was not able to load one or more projects in the mix and can not be run due to errors");
}
for (int pIdx = 0; pIdx < rmx.Projects.Length; pIdx++)
{
rmx.BuildProjectIterationRace2Scores(pIdx);
rmx.resetRaceModule();
}
var mixData = rmx.GetMixDataForRace2();
var raceResults = new
{
mixData
};
JsonResult rs = Json(raceResults, JsonRequestBehavior.AllowGet);
return rs;
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
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, bool retriveExpendituresNeedValues)
{
using (var eManager = new ExpenditureCategoryManager(DbContext))
using (var fiscalCalendarManager = new FiscalCalendarManager(DbContext))
using (var teamManager = new TeamManager(DbContext))
using (var scenarioManager = new ScenarioManager(DbContext))
{
var mixManager = ResolveMixManager(DbProvider.MongoDb);
var fiscalCalendar = fiscalCalendarManager.GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, null, null, true, null, false);
var weekEndings = fiscalCalendar.Where(x => x.EndDate >= startDate && x.EndDate <= endDate)
.Select(x => x.EndDate).OrderBy(x => x).ToList();
model.Teams = teamManager.GetTeamsInfoByUser(User.Identity.GetID(), teamsIds, viewsIds, fiscalCalendar)
.Select(x => new MixTeamModel(x)).ToList();
model.FiscalCalendarWeekEndings = fiscalCalendar.Select(x => Utils.ConvertToUnixDate(x.EndDate)).OrderBy(x => x).ToList();
model.WeekEndings = fiscalCalendarManager.CastWeekEndingsToTree(weekEndings);
var allTeams = model.Teams.Select(x => Guid.Parse(x.Id)).ToList();
var allTeamsProjects = teamManager.GetProjectsIds(allTeams);
#if DEBUG
var watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
model.Projects = mixManager.GetProjectModel(allTeamsProjects).ToDictionary(x => x.Id.ToString());
#if DEBUG
watch1.Stop();
#endif
#region New Teams Filling
if (newTeams != null)
{
#if DEBUG
watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
var teams2Add = newTeams.FindAll(x => model.Teams.All(t => t.Id.ToString() != x.Id));
var expCatsInTeams = teams2Add.Where(x => x.Data != null)
.SelectMany(x => x.Data)
.Where(x => x != null)
.Select(x => x.Id)
.ToList();
var expCats = DbContext.ExpenditureCategory.AsNoTracking()
.Include(x => x.UOM)
.Where(x => expCatsInTeams.Contains(x.Id))
.ToDictionary(x => x.Id, x => x);
var expCatsWithAdjustFactor = DbContext.VW_ExpendituresWithAdjustmentFactor.AsNoTracking()
.Where(x => x.ExpenditureCategoryId.HasValue && expCatsInTeams.Contains(x.ExpenditureCategoryId.Value))
.ToList()
.GroupBy(x => x.ExpenditureCategoryId.Value)
.ToDictionary(x => x.Key, x => x.GroupBy(s => s.WeekEndingDate)
.ToDictionary(s => s.Key, s => s.Any() ? (s.FirstOrDefault().AdjustmentFactor ?? 0) : 1));
#if DEBUG
watch1.Stop();
#endif
#if DEBUG
watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
foreach (var team in teams2Add)
{
var mixTeam = new MixTeamModel
{
Id = team.Id,
Name = team.TVName,
CompanyId = team.CompanyId,
UserId = team.UserId,
CostCenterId = team.CostCenterId,
Group = team.Group,
IsNew = true
};
if (null != team.Data)
{
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));
pos.EDate = !string.IsNullOrEmpty(pos.EndDate)
? Utils.ConvertFromUnixDate(long.Parse(pos.EndDate))
: (DateTime?)null;
}
}
var category = expCats.ContainsKey(expCat.Id) ? expCats[expCat.Id] : null;
var activityCategory = new ExpCategorySummaryInfoModel
{
Id = expCat.Id,
Name = category != null ? category.Name : string.Empty,
UomValue = category?.UOM.UOMValue ?? 0,
ECScenario = ExpCategorySummaryInfoModel.EC_Scenario.TEAM
};
var adjustmentFactors4Category = expCatsWithAdjustFactor.ContainsKey(expCat.Id) ? expCatsWithAdjustFactor[expCat.Id] : null;
foreach (var week in weekEndings)
{
var capacity = 0M;
if (expCat.Positions != null)
{
var factor = 1M;
if (adjustmentFactors4Category != null && adjustmentFactors4Category.ContainsKey(week))
factor = adjustmentFactors4Category[week];
var resourcesNumber = expCat.Positions.Count(x => x.SDate <= week && (!x.EDate.HasValue || x.EDate >= week));
capacity = resourcesNumber * activityCategory.UomValue * factor;
}
activityCategory.PlannedCapacityValues.Add(Utils.ConvertToUnixDate(week).ToString(), capacity);
}
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);
}
#if DEBUG
watch1.Stop();
#endif
}
#endregion
#if DEBUG
watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
model.Teams = model.Teams.OrderBy(x => x.Name).ToList();
model.SuperExpenditures = eManager.GetSuperExpenditureDetails();
if (retriveExpendituresNeedValues && model.Projects != null)
{
var scenarios = model.Projects.Values.Where(p => p.Scenario != null).Select(x => x.Scenario.Id).ToList();
var needAllocations = scenarioManager.GetScenarioDetails(scenarios, null, weekEndings);
model.NeedAllocations = ConvertNeedAllocationsToTree(needAllocations);
}
#if DEBUG
watch1.Stop();
#endif
}
}
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($"DbProvider with value = {dbProvider.GetHashCode()} not defined");
}
}
#endregion
[HttpGet]
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Write)]
public ActionResult EditTeam(Guid? id)
{
bool isNewTeam = !id.HasValue || (id.Value == Guid.Empty);
ViewBag.IsNewTeam = isNewTeam;
try
{
var model = new TeamModel();
if (!isNewTeam)
{
var manager = new TeamManager(DbContext);
model = manager.LoadTeamModel(id.Value) ?? new TeamModel();
}
else
{
model.Id = Guid.NewGuid();
}
model.UserId = isNewTeam ?
new List<Guid> { SecurityManager.GetUserPrincipal() } :
DbContext.User2Team.Where(x => x.TeamId == model.Id).ToList().Select(x => Guid.Parse(x.UserId)).ToList();
return PartialView("_mixTeams", model);
}
catch (Exception exception)
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
/// <summary>Get Scenario details for specified Projects in da mix</summary>
[HttpPost]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
[ValidateJsonAntiForgeryToken]
public ActionResult GetScenarioDetails(MixScenarioDetailsLoadModel model)
{
if (model?.Scenarios == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
try
{
var mixManager = ResolveMixManager(DbProvider.MongoDb);
var result = new Dictionary<string, Dictionary<string, ExpenditureDetail>>();
foreach (var scenarioId in model.Scenarios)
{
if (scenarioId != Guid.Empty)
{
// Model.MixId is actually the Mix.Key value
var allocations = mixManager.GetFullAllocationInfoByScenario(model.MixId, scenarioId);
result.Add(scenarioId.ToString(), allocations);
}
}
return BigJson(result);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Write)]
[ValidateJsonAntiForgeryToken]
public ActionResult EditScenarioDetails(MixScenarioDetailsFormLoadModel model)
{
if (model?.Scenario == null || model.Scenario.Id == Guid.Empty)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
try
{
Project project;
using (var projectManager = new ProjectManager(DbContext))
{
project = projectManager.Load(model.Scenario.ParentId);
}
DateTime? projectDeadline = null;
if (project?.Deadline != null)
projectDeadline = project.Deadline;
var userId = SecurityManager.GetUserPrincipal();
var detailsModel = ScenarioUIManager.CreateScenarioDetailsCalendarModel(userId, model.Scenario, model.TeamsInScenario, projectDeadline);
var depMngr = new ProjectDependencyManager(DbContext);
if (project != null)
{
var depInfo = depMngr.GetDependencies(project.Id, model.MixScenarioDependencyData);
detailsModel.ProjectHasDependencies = depInfo != null && depInfo.Count > 0;
if (detailsModel.ProjectHasDependencies)
{
var prevProjectsInfo = depInfo.FirstOrDefault(x => x.Type == ProjectDependencyDisplayType.StartsAfter);
var succProjectsInfo = depInfo.FirstOrDefault(x => x.Type == ProjectDependencyDisplayType.StartsBefore);
detailsModel.StartDateConstraint = prevProjectsInfo?.dtEndDate != null ? Utils.ConvertFromUnixDate(prevProjectsInfo.dtEndDate.Value) : (DateTime?)null;
detailsModel.EndDateConstraint = succProjectsInfo?.dtStartDate != null ? Utils.ConvertFromUnixDate(succProjectsInfo.dtStartDate.Value) : (DateTime?)null;
}
detailsModel.DependencyListModel = depInfo;
}
return PartialView("~/Views/Scenarios/_scenarioCalendar.cshtml", detailsModel);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Write)]
[ValidateJsonAntiForgeryToken]
public ActionResult RecalculateScenarioFinInfo(ScenarioCalendarMixModel model)
{
if (model == null ||
model.Id == Guid.Empty ||
!model.ParentId.HasValue ||
model.ParentId == Guid.Empty)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
try
{
var scenarioManager = (new ScenarioManager(DbContext));
var finInfo = scenarioManager.GetScenarioFinInfoModel(model);
if (finInfo == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var mixScenarioFinInfo = new ScenarioCalendarMixFinInfoModel()
{
IsRevenueGenerating = finInfo.IsRevenueGenerating,
ProjectedRevenue = finInfo.ProjectedRevenue ?? 0,
RevenueAfterCost = finInfo.RevenueAfterCost,
ActualRevenueAfterCost = finInfo.ActualRevenueAfterCost,
GrossMargin = finInfo.GrossMargin,
LMMargin = finInfo.LMMargin,
UseLMMargin = finInfo.UseLMMargin,
CalculatedGrossMargin = finInfo.CalculatedGrossMargin,
CalculatedGrossMarginLM = finInfo.CalculatedGrossMarginLM,
CalculatedGrossMarginActuals = finInfo.CalculatedGrossMarginActuals,
CalculatedGrossMarginLMActuals = finInfo.CalculatedGrossMarginLMActuals,
TDDirectCosts = finInfo.TDDirectCosts,
BUDirectCosts = finInfo.BUDirectCosts,
ActualLabor = finInfo.ActualLabor,
LaborMaterialsSplit = finInfo.LaborMaterialsSplit,
ActualLaborMaterialsSplit = finInfo.ActualLaborMaterialsSplit,
ActualMaterials = finInfo.ActualMaterials,
ActualsTotal = finInfo.ActualsTotal,
EFXSplit = finInfo.EFXSplit,
LaborSplitPercentage = finInfo.LaborSplitPercentage,
CostSaving = model.FinInfo?.CostSaving
};
return BigJson(mixScenarioFinInfo);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Write)]
[ValidateJsonAntiForgeryToken]
public ActionResult EditScenarioFinInfo(MixScenarioDetailsFormLoadModel model)
{
if (model?.Scenario == null ||
model.Scenario.Id == Guid.Empty ||
model.Scenario.ParentId == null ||
model.Scenario.ParentId == Guid.Empty ||
model.Scenario.FinInfo == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
try
{
var scenarioManager = (new ScenarioManager(DbContext));
var actualScenario = scenarioManager.GetActualScenario(model.Scenario.ParentId.Value);
var scenarioStartDate = Utils.ConvertFromUnixDate(model.Scenario.StartDate);
var scenarioEndDate = Utils.ConvertFromUnixDate(model.Scenario.EndDate);
var date4StartOfChanges = scenarioStartDate > DateTime.Today ? scenarioStartDate.Date : (DateTime.Today > scenarioEndDate ? scenarioEndDate.Date : DateTime.Today.Date);
if (actualScenario != null && actualScenario.EndDate > date4StartOfChanges)
date4StartOfChanges = actualScenario.EndDate.Value;
var finInfo = new ScenarioFinInfoModel
{
DateForStartOfChanges = date4StartOfChanges,
IsRevenueGenerating = model.Scenario.FinInfo.IsRevenueGenerating,
ProjectedRevenue = model.Scenario.FinInfo.ProjectedRevenue,
RevenueAfterCost = model.Scenario.FinInfo.RevenueAfterCost,
ActualRevenueAfterCost = model.Scenario.FinInfo.ActualRevenueAfterCost,
GrossMargin = model.Scenario.FinInfo.GrossMargin,
LMMargin = model.Scenario.FinInfo.LMMargin,
UseLMMargin = model.Scenario.FinInfo.UseLMMargin,
CalculatedGrossMargin = model.Scenario.FinInfo.CalculatedGrossMargin,
CalculatedGrossMarginLM = model.Scenario.FinInfo.CalculatedGrossMarginLM,
CalculatedGrossMarginActuals = model.Scenario.FinInfo.CalculatedGrossMarginActuals,
CalculatedGrossMarginLMActuals = model.Scenario.FinInfo.CalculatedGrossMarginLMActuals,
TDDirectCosts = model.Scenario.FinInfo.TDDirectCosts,
BUDirectCosts = model.Scenario.FinInfo.BUDirectCosts,
ActualLabor = model.Scenario.FinInfo.ActualLabor,
LaborMaterialsSplit = model.Scenario.FinInfo.LaborMaterialsSplit,
ActualLaborMaterialsSplit = model.Scenario.FinInfo.ActualLaborMaterialsSplit,
ActualMaterials = model.Scenario.FinInfo.ActualMaterials,
ActualsTotal = model.Scenario.FinInfo.ActualsTotal,
EFXSplit = model.Scenario.FinInfo.EFXSplit,
LaborSplitPercentage = model.Scenario.FinInfo.LaborSplitPercentage,
CostSaving = new CostSavingModel()
{
ScenarioId = model.Scenario.Id,
ScenarioStartDate = scenarioStartDate,
ScenarioEndDate = scenarioEndDate
}
};
if (model.Scenario.FinInfo.CostSaving != null)
{
var costSavings = scenarioManager.GetCostSavings(model.Scenario.FinInfo.CostSaving);
var costSavingItems = scenarioManager.GetScenarioCostSavingModelItems(costSavings);
finInfo.CostSaving.CostSavingsPanelExpanded = (model.Scenario.FinInfo.CostSaving.CostSavingStartDate.HasValue && model.Scenario.FinInfo.CostSaving.CostSavingEndDate.HasValue);
finInfo.CostSaving.CostSavings = model.Scenario.FinInfo.CostSaving.CostSavings;
finInfo.CostSaving.CostSavingStartDate = model.Scenario.FinInfo.CostSaving.CostSavingStartDate.HasValue ? Utils.ConvertFromUnixDate(model.Scenario.FinInfo.CostSaving.CostSavingStartDate.Value) : (DateTime?)null;
finInfo.CostSaving.CostSavingEndDate = model.Scenario.FinInfo.CostSaving.CostSavingEndDate.HasValue ? Utils.ConvertFromUnixDate(model.Scenario.FinInfo.CostSaving.CostSavingEndDate.Value) : (DateTime?)null;
finInfo.CostSaving.CostSavingType = model.Scenario.FinInfo.CostSaving.CostSavingType;
finInfo.CostSaving.CostSavingDescription = model.Scenario.FinInfo.CostSaving.CostSavingDescription;
finInfo.CostSaving.ROIDate = scenarioManager.GetROIDate(model.Scenario.FinInfo.CostSaving.CostSavings, model.Scenario.FinInfo.BUDirectCosts, costSavings);
finInfo.CostSaving.CostSavingItems = JsonConvert.SerializeObject(costSavingItems);
finInfo.CostSaving.IsEditable = true;
}
Project project;
using (var projectManager = new ProjectManager(DbContext))
{
project = projectManager.Load(model.Scenario.ParentId);
}
DateTime? deadLine = null;
if (project?.Deadline != null)
deadLine = project.Deadline;
var userId = SecurityManager.GetUserPrincipal();
var scenarioDetailsModel = ScenarioUIManager.CreateScenarioDetailsCalendarModel(userId, model.Scenario, model.TeamsInScenario, deadLine);
var editFinInfoModel = new MixEditScenarioFinInfoModel
{
FinInfo = finInfo,
Calendar = scenarioDetailsModel
};
return PartialView("_mixScenarioFinInfoEdit", editFinInfoModel);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
[HttpPost]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Write)]
[ValidateJsonAntiForgeryToken]
public ActionResult ActivateScenario(MixScenarioActivationModel model)
{
if (model?.Project == null ||
model.Project.Id == Guid.Empty ||
model.Project.Scenario == null ||
model.Project.Scenario.Id == Guid.Empty)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
try
{
if (model.Project.Teams == null)
model.Project.Teams = new List<Guid>();
var mixManager = ResolveMixManager(DbProvider.MongoDb);
var savedTeamsInProject = DbContext.Team2Project.AsNoTracking()
.Where(x => x.ProjectId == model.Project.Id)
.Select(x => x.TeamId).ToList();
var requiredTeams = savedTeamsInProject.Where(x => model.Project.Teams.All(s => s != x)).ToList();
if (requiredTeams.Count > 0)
model.Project.Teams.AddRange(requiredTeams);
mixManager.ActivateTeams(model.Teams);
mixManager.ActivateScenario(model.Project.Scenario, model.Project.Teams, model.MixId, model.IsActive);
DbContext.SaveChanges();
// we should return updated collection of inactive scenarios to refresh inactive scenarios list
var projectModel = mixManager.GetProjectModel(new List<Guid>() { model.Project.Id });
if (projectModel != null && projectModel.Count > 0)
{
var project = projectModel.FirstOrDefault(x => x.Id == model.Project.Id);
if (project != null)
return Json(project.InactiveScenarios);
}
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
/// <summary>
/// Get from SQL database current timestamps for specified scenario list and returns it
/// to client. The list can contain ID of projects, if their scenario IDs are unknown. In
/// this case it returns timestamp for current active scenarios for theese projects
/// </summary>
/// <param name="model">Project or scenario ID list</param>
/// <returns></returns>
[HttpPost]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
[ValidateJsonAntiForgeryToken]
public ActionResult GetScenariosTimestamps(Dictionary<string, bool> model)
{
if (model == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
try
{
// Get new timestamps and set marker to change active scenario Id for project in mix model
var result = GetScenariosTimestampsInternal(model.Keys.ToList());
result.ForEach(x =>
{
if (model.ContainsKey(x.ScenarioId.ToString()))
{
x.ChangeActiveScenarioId = model[x.ScenarioId.ToString()];
}
else
{
x.ChangeActiveScenarioId = model.ContainsKey(x.ProjectId.ToString()) && model[x.ProjectId.ToString()];
}
});
return Json(result);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
/// <summary>
/// Returns LIVE database data for specified project list
/// </summary>
/// <param name="model">Project or scenario ID list</param>
/// <returns></returns>
/// <remarks>SA. ENV-1246</remarks>
[HttpPost]
[AreaSecurity(area = Areas.Mixes, level = AccessLevel.Read)]
[ValidateJsonAntiForgeryToken]
public ActionResult GetProjectsDataFromLive(List<Guid> model)
{
var result = new List<MixProjectModel>();
if (model == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
if (model.Count < 1)
return Json(result);
try
{
var mixManager = ResolveMixManager(DbProvider.MongoDb);
result = mixManager.GetProjectModel(model);
string userId = User.Identity.GetID();
var scenarioManager = new ScenarioManager(DbContext);
foreach (MixProjectModel mixProject in result)
{
if (mixProject?.Scenario != null)
{
var scenarioId = mixProject.Scenario.Id;
var scenarioExpCats = scenarioManager.GetFullAllocationInfoByScenario(scenarioId, userId);
mixProject.Scenario.Expenditures = scenarioExpCats;
}
}
return BigJson(result);
}
catch (Exception exception) // handle any unexpected error
{
LogException(exception);
}
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
/// <summary>
/// Returns live DB timestamps for given scenarios
/// </summary>
/// <param name="scenarios">Scenario or project Ids</param>
/// <returns></returns>
private List<MixScenarioTimestampModel> GetScenariosTimestampsInternal(List<string> scenarios)
{
if (scenarios == null)
return null;
if (scenarios.Count < 1)
return new List<MixScenarioTimestampModel>();
var activeScenarios = new List<Guid>();
var foundScenarios = DbContext.BLL_Objects.Where(x =>
x.EntityName == "Scenario" && scenarios.Contains(x.Id.ToString()))
.Select(x => x.Id).ToList();
var projects = DbContext.BLL_Objects.Where(x =>
x.EntityName == "Project" && scenarios.Contains(x.Id.ToString()))
.Select(x => x.Id).ToList();
if (projects.Count > 0)
{
activeScenarios = DbContext.Scenarios.Where(x =>
x.ParentId.HasValue && projects.Contains(x.ParentId.Value) &&
(x.Type == (int)ScenarioType.Portfolio) && x.Status.HasValue &&
(x.Status.Value == (int)ScenarioStatus.Active))
.Select(x => x.Id).Except(foundScenarios).ToList();
}
foundScenarios.AddRange(activeScenarios);
var result = DbContext.Scenarios.Where(s =>
foundScenarios.Contains(s.Id) && s.ParentId.HasValue).ToList().Select(r => new MixScenarioTimestampModel
{
ScenarioId = r.Id,
ProjectId = r.ParentId.Value,
Timestamp = r.GetCurrentVersion(),
ChangeActiveScenarioId = false
}).ToList();
return result;
}
private List<SelectListItem> ConvertCostCentersToOptions(IEnumerable<CreditDepartmentModel> costCenters)
{
var result = costCenters?.Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Name
}).OrderBy(x => x.Text).ToList();
return result;
}
private List<MixFilterVariantsModel.ProjectRoleItem> ConvertExpendituresToOptions(IEnumerable<ExpenditureDetail> expCats)
{
var result = expCats?.Select(x => new MixFilterVariantsModel.ProjectRoleItem
{
Value = x.ExpenditureCategoryId.ToString(),
Text = x.ExpenditureCategoryName,
CostCenterId = x.CreditId.ToString()
}).OrderBy(x => x.Text).ToList();
return result;
}
private void UpdateFilterSelection(MixSaveModel model, MixFilterSelectionModel selection)
{
if (model?.Filter == null)
return;
if (model.Filter.Selection == null)
model.Filter.Selection = new MixFilterSelectionModel();
model.Filter.Selection.CostCenters = new List<Guid>();
model.Filter.Selection.ProjectRoles = new List<Guid>();
if (selection != null && model.Filter.Variants != null)
{
var availableCostCentersIds = model.Filter.Variants.AvailableCostCenters.Select(x => new Guid(x.Value)).ToList();
if (selection.CostCenters != null && model.Filter.Variants.AvailableCostCenters != null)
{
model.Filter.Selection.CostCenters = selection.CostCenters.Where(x => availableCostCentersIds.Contains(x)).ToList();
}
else
{
model.Filter.Selection.CostCenters = null;
}
if (selection.ProjectRoles != null && model.Filter.Variants.AvailableProjectRoles != null)
{
var availableProjectRolesIds = model.Filter.Variants.AvailableProjectRoles
.Where(x => !String.IsNullOrEmpty(x.CostCenterId) && availableCostCentersIds.Contains(new Guid(x.CostCenterId)))
.Select(x => new Guid(x.Value)).ToList();
model.Filter.Selection.ProjectRoles = selection.ProjectRoles.Where(x => availableProjectRolesIds.Contains(x)).ToList();
}
else
{
model.Filter.Selection.ProjectRoles = null;
}
}
// Transfer initially filter within result model, because client controller takes dates directily from
// model.StartDate & model.EndDate
if (selection != null)
{
model.StartDate = selection.StartDate;
model.EndDate = selection.EndDate;
}
}
public Dictionary<string, MixAllocationsByECModel> ConvertNeedAllocationsToTree(List<ScenarioDetail> allocations)
{
if (allocations == null || allocations.Count <= 0)
return new Dictionary<string, MixAllocationsByECModel>();
var tree = new Dictionary<string, MixAllocationsByECModel>();
foreach (var allocation in allocations)
{
if (!allocation.ParentID.HasValue || !allocation.WeekEndingDate.HasValue)
continue;
var scenarioKey = allocation.ParentID.Value.ToString();
var ecKey = allocation.ExpenditureCategoryId.ToString();
var weekEndingKey = Utils.ConvertToUnixDate(allocation.WeekEndingDate.Value).ToString();
if (!tree.ContainsKey(scenarioKey))
tree.Add(scenarioKey, new MixAllocationsByECModel());
if (!tree[scenarioKey].Expenditures.ContainsKey(ecKey))
tree[scenarioKey].Expenditures.Add(ecKey, new MixAllocationsModel());
tree[scenarioKey].Expenditures[ecKey].Allocations.Add(weekEndingKey, allocation.Quantity ?? 0);
}
return tree;
}
protected void BuildNeedAllocations(MixCalendarModel model)
{
if (model?.Projects != null)
{
// Build NeedAllocations from scenario's data of the model
model.NeedAllocations =
model.Projects.Values.Where(p =>
p?.Scenario?.Expenditures != null && p.Scenario.Expenditures.Count > 0)
.ToDictionary(k => k.Scenario.Id.ToString(), v => new MixAllocationsByECModel
{
Expenditures = v.Scenario.Expenditures.ToDictionary(e => e.Key, e => new MixAllocationsModel
{
Allocations = e.Value.Details.ToDictionary(sd => sd.Key, sd =>
sd.Value.ForecastQuantity ?? 0)
})
});
}
}
/// <summary>
/// Extracts expenditures from all the projects in model
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
protected List<Guid> GetExpendituresFromModel(MixCalendarModel model)
{
List<Guid> result = null;
if (model?.NeedAllocations != null)
{
result =
model.NeedAllocations.Values.Where(x => x.Expenditures != null).SelectMany(x => x.Expenditures.Keys)
.Select(x => new Guid(x)).Distinct().ToList();
}
return result;
}
private IEnumerable<CreditDepartmentModel> GetCostCenters(Dictionary<string, MixAllocationsByECModel> needAllocations,
IEnumerable<MixTeamModel> teamAllocations, IEnumerable<ExpenditureDetail> projectRolesInfo)
{
if (needAllocations == null)
return null;
// Gather all expenditures in the model and get cost centers
List<CreditDepartmentModel> result;
using (var costCenterMngr = new CreditDepartmentManager(DbContext))
{
var expCatsWithNeed = needAllocations.Where(x => x.Value.Expenditures != null).SelectMany(x =>
x.Value.Expenditures.Where(e => e.Value.Allocations != null && e.Value.Allocations.Values.Any(a => a > 0))
.Select(e => e.Key)).Distinct().ToList();
// Check team allocations existance only for ordinary ECs
var projectRolesInModel = projectRolesInfo?.Select(x => x.ExpenditureCategoryId).ToList() ?? new List<Guid>();
expCatsWithNeed = expCatsWithNeed.Where(e =>
projectRolesInModel.Contains(new Guid(e)) ||
teamAllocations != null && teamAllocations.Any(t => t?.ExpCategories != null &&
t.ExpCategories.ContainsKey(e) &&
t.ExpCategories[e].NeedCapacity != null &&
t.ExpCategories[e].NeedCapacity.Values.Any(a => a > 0))).ToList();
var expCatsWithNeedTyped = expCatsWithNeed.Select(x => new Guid(x));
result = costCenterMngr.GetCreditDepartmentsByExpCats(expCatsWithNeedTyped);
}
return result;
}
#region Private Methods
private IEnumerable<ExpenditureDetail> GetProjectRoles(IEnumerable<Guid> modelExpCats, Dictionary<string, MixAllocationsByECModel> needAllocations,
IEnumerable<MixTeamModel> teamAllocations, IEnumerable<CreditDepartmentModel> costCenters,
IEnumerable<ExpenditureDetail> projectRolesInfo)
{
if (modelExpCats == null || needAllocations == null || projectRolesInfo == null)
return null;
// Get all Project Roles in model with extended info
var allProjectRolesInModel = projectRolesInfo.Where(x => modelExpCats.Contains(x.ExpenditureCategoryId)).Select(x => x.ExpenditureCategoryId).ToList();
// Regroup Need data
var projectRolesNeedIndexed =
needAllocations.Where(n => n.Value.Expenditures != null).SelectMany(n => n.Value.Expenditures.Where(e =>
allProjectRolesInModel.Contains(new Guid(e.Key)) && e.Value.Allocations != null)
.SelectMany(e => e.Value.Allocations.Select(a => new
{
ScenarioId = new Guid(n.Key),
ExpCatId = new Guid(e.Key),
Weekending = a.Key,
a.Value
})))
.GroupBy(x => x.ExpCatId).ToDictionary(g1 => g1.Key, g1 =>
g1.GroupBy(x => x.Weekending).ToDictionary(k2 => k2.Key, v2 => v2.Sum(v => v.Value)));
// Get project roles with non-zero need and format team allocations for quick access - by scenarios
var rolesWithNonZeroRemainingNeed = GetRolesWithNonZeroRemainingNeed(teamAllocations, projectRolesNeedIndexed);
var costCentersIds = costCenters?.Select(x => x.Id).ToList() ?? new List<Guid>();
var result = projectRolesInfo.Where(x => rolesWithNonZeroRemainingNeed.Contains(x.ExpenditureCategoryId) &&
(costCentersIds.Count < 1 || costCentersIds.Contains(x.CreditId))).ToList();
return result;
}
private IEnumerable<Guid> GetRolesWithNonZeroRemainingNeed(IEnumerable<MixTeamModel> teamAllocations, Dictionary<Guid, Dictionary<string, decimal>> projectRolesNeedIndexed)
{
var projectRolesWithNonZeroNeed = projectRolesNeedIndexed.Where(x => x.Value.Values.Any(v => v > 0)).Select(z => z.Key).ToList();
var teamAllocationsForRolesIndexed = teamAllocations?.Where(t => t.ExpCategories != null)
.SelectMany(t => t.ExpCategories.Where(e =>
projectRolesWithNonZeroNeed.Contains(new Guid(e.Key)) && e.Value.NeedCapacity != null)
.SelectMany(e => e.Value.NeedCapacity.Select(a =>
new
{
ExpCatId = new Guid(e.Key),
Weekending = a.Key,
a.Value
})))
.GroupBy(x => x.ExpCatId).ToDictionary(g1 => g1.Key, g1 =>
g1.GroupBy(y => y.Weekending).ToDictionary(g2 => g2.Key, g2 =>
g2.Select(z => z.Value).Sum()));
// Get Project roles with non-zero remaining need (need != Sum(Team allocations)). Look for roles in every scenario
//var rolesWithNonZeroRemainingNeed = new List<Guid>();
foreach (var projectRoleId in projectRolesWithNonZeroNeed)
{
var currentRoleNeed = projectRolesNeedIndexed[projectRoleId];
if (teamAllocationsForRolesIndexed.ContainsKey(projectRoleId))
{
// Some teams are allocated to current project role
var allocationsToCurrentRole = teamAllocationsForRolesIndexed[projectRoleId];
var weekendings = currentRoleNeed.Keys;
foreach (var we in weekendings)
{
var currentWeekendingNeed = currentRoleNeed[we];
var currentWeekendingAllocations = allocationsToCurrentRole.ContainsKey(we) ? allocationsToCurrentRole[we] : 0;
if (currentWeekendingNeed > currentWeekendingAllocations)
{
yield return projectRoleId;
break;
}
}
}
else
{
// No teams allocated to project role. But project role has non-zero need
yield return projectRoleId;
}
}
}
private void LoadMixFilterValues(MixFilterVariantsModel model, string userId)
{
MongoMixManager mixManager = new MongoMixManager(null, userId);
if (model == null)
throw new ArgumentNullException(Messages.Common_ModelMustBeNotNull);
if (string.IsNullOrWhiteSpace(userId))
throw new ArgumentNullException(nameof(userId));
model.AvailableTeamsAndViews = Utils.GetViewsAndTeams(userId);
model.AvailableUsers = Utils.GetUsers().ToList();
model.AvailableMixes = mixManager.GetMixesAvailable4User();
using (var expCatManager = new ExpenditureManager(DbContext))
{
model.AvailableExpenditures = expCatManager.GetExpenditures();
}
}
private void LoadClientSideFilterValues(MixSaveModel model)
{
if (model == null)
throw new ArgumentNullException(Messages.Common_ModelMustBeNotNull);
if (model.Filter == null)
model.Filter = new MixFilterModel();
if (model.Filter.Variants == null)
model.Filter.Variants = new MixFilterVariantsModel();
// Get info abount all project roles
List<ExpenditureDetail> projectRolesInfoTyped;
using (var ecManager = new ExpenditureCategoryManager(DbContext))
{
var projectRolesInfo = ecManager.GetSuperExpenditureDetails();
projectRolesInfoTyped = projectRolesInfo.Values.ToList();
}
var needAllocationsTyped = model.Calendar?.NeedAllocations;
var teamAllocationsTyped = model.Calendar?.Teams;
var expCatsFromModel = GetExpendituresFromModel(model.Calendar);
// Get options for Cost Centers
var availableCostCenters = GetCostCenters(needAllocationsTyped, teamAllocationsTyped, projectRolesInfoTyped).ToList();
model.Filter.Variants.AvailableCostCenters = ConvertCostCentersToOptions(availableCostCenters);
// Get options for Project Roles
var availableProjectRoles = GetProjectRoles(expCatsFromModel, needAllocationsTyped, teamAllocationsTyped,
availableCostCenters, projectRolesInfoTyped);
model.Filter.Variants.AvailableProjectRoles = ConvertExpendituresToOptions(availableProjectRoles);
}
private MixSaveModel LoadMixSaveModel(string model)
{
string mixKey = model;
var serverModel = new MixSaveModel();
var mixManager = ResolveMixManager(DbProvider.MongoDb);
#if DEBUG
var watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
LoadMixFilterValues(serverModel.Filter.Variants, User.Identity.GetID());
#if DEBUG
watch1.Stop();
#endif
if (!string.IsNullOrWhiteSpace(mixKey))
{
#if DEBUG
watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
// Get mix data from Mongo
var mongoModel = mixManager.RetrieveWithChildren(mixKey);
#if DEBUG
watch1.Stop();
#endif
if (mongoModel != null)
{
var startDate = Utils.ConvertFromUnixDate(mongoModel.StartDate);
var endDate = Utils.ConvertFromUnixDate(mongoModel.EndDate);
var endDateCorrected = endDate.AddDays(6);
#if DEBUG
watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
// 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).ToList(), false);
#if DEBUG
watch1.Stop();
#endif
#if DEBUG
watch1 = new System.Diagnostics.Stopwatch();
watch1.Start();
#endif
// All expenditure's data must be in mongo mix model. Build EC need from this model
BuildNeedAllocations(mongoModel.Calendar);
MixModelsMergeManager mergeMngr = new MixModelsMergeManager(DbContext, User.Identity.GetID());
mergeMngr.MergeHeaders(serverModel, mongoModel);
mergeMngr.MergeFilters(serverModel.Filter, mongoModel.Filter);
mergeMngr.MergeCalendars(serverModel, mongoModel, startDate, endDate);
#if DEBUG
watch1.Stop();
#endif
}
}
var fullMongoMix = serverModel;
if (!string.IsNullOrWhiteSpace(mixKey))
fullMongoMix = mixManager.RetrieveWithChildren(serverModel.Id);
if (fullMongoMix == null)
fullMongoMix = serverModel;
serverModel.CanRunResourceRace = HasResourceAssignmentsBool(fullMongoMix.Calendar);
serverModel.CanRunTeamRace = HasSupperEcUnAssigned(fullMongoMix.Calendar);
serverModel.canDoRace = ValidateForRace(serverModel);
return serverModel;
}
private bool ValidateForRace(MixSaveModel mainModel)
{
var startDate = Utils.ConvertFromUnixDate(mainModel.Filter.Selection.StartDate);
if (startDate < DateTime.Now.AddDays(-1))
return false;
return ValidateForRace(mainModel.Calendar);
}
private bool ValidateForRace(MixCalendarModel model)
{
if (model?.ManagedProjects == null)
return false;
var dateNow = Utils.ConvertToUnixDate(DateTime.Now);
return model.Projects.Select(x => x.Value)
.Where(p => model.ManagedProjects.Contains(p.Id))
.Any(p => p.Scenario != null && p.Scenario.StartDate >= dateNow && !p.Pinned);
}
private bool HasSupperEcUnAssigned(MixCalendarModel model)
{
if (model?.Projects == null || model.ManagedProjects == null)
return false;
var managedScenarios = new List<Guid>();
var scenarioManager = new ScenarioManager(DbContext);
foreach (var projectId in model.ManagedProjects)
{
var projectKey = projectId.ToString();
if (!model.Projects.ContainsKey(projectKey))
continue;
var project = model.Projects[projectKey];
if (project?.Scenario?.Expenditures == null)
continue;
var hasSuperEc = project.Scenario.Expenditures.Any(x => x.Value != null && !x.Value.AllowResourceAssignment);
if (hasSuperEc)
return false;
managedScenarios.Add(project.Scenario.Id);
}
var superECs = scenarioManager.GetExpendituresInScenarios(managedScenarios, true);
return superECs == null || !superECs.Any();
}
private bool HasResourceAssignmentsBool(MixCalendarModel model)
{
if (model?.Projects == null || model.ManagedProjects == null)
return false;
var managedScenarios = new List<Guid>();
bool hasNonAllocatedEc;
using (var scenarioManager = new ScenarioManager(DbContext))
{
foreach (var projectId in model.ManagedProjects)
{
var projectKey = projectId.ToString();
if (!model.Projects.ContainsKey(projectKey))
continue;
var project = model.Projects[projectKey];
if (project?.Scenario?.Expenditures == null)
continue;
foreach (var category in project.Scenario.Expenditures.Values)
{
if (category.Teams != null && category.Teams.Any())
{
var emptyTeamExists = category.Teams.Values.Any(x => x.Resources == null || !x.Resources.Any());
if (emptyTeamExists)
return false;
}
}
managedScenarios.Add(project.Scenario.Id);
}
hasNonAllocatedEc = scenarioManager.CheckScenarioHasNonAllocatedEC(managedScenarios);
}
return !hasNonAllocatedEc;
}
Race _calcModule;
private Race GetRaceModule()
{
if (_calcModule == null)
{
var userId = User.Identity.GetID();
var userRec = DbContext.AspNetUsers.FirstOrDefault(x => x.Id == userId.ToString());
if (userRec != null)
_calcModule = new Race(userRec.OverUnderCoefficient);
}
return _calcModule;
}
static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => true), (t1, t2) => t1.Concat(new[] { t2 }));
}
//private bool ValidateProject(DateTime filterStartDate, DateTime filterEndDate, DateTime scenarioStartDate, DateTime scenarioEndDate, DateTime projectDeadLine)
//{
// if (CheckStartDateWithToday(filterStartDate) &&
// CheckEndDateScenarioStartDate(filterEndDate, scenarioStartDate) &&
// CheckScenarioEndDateWithProjectDeadline(scenarioEndDate, projectDeadLine))
// return true;
// return false;
//}
//private bool CheckScenarioEndDateWithProjectDeadline(DateTime scenarioEndDate, DateTime projectDeadLine)
//{
// return projectDeadLine == DateTime.MinValue || scenarioEndDate <= projectDeadLine;
//}
//private bool CheckEndDateScenarioStartDate(DateTime filterEndDate, DateTime scenarioStartDate)
//{
// return scenarioStartDate <= filterEndDate;
//}
//private bool CheckStartDateWithToday(DateTime filterStartDate)
//{
// return filterStartDate >= DateTime.Today;
//}
#endregion
}
}