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.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 teamsIds, List viewsIds, List NewTeams) { // SA. ENV-1218 UOMManager uomMngr = new UOMManager(DbContext); Dictionary 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(); 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(); 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(); 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>> CastWeekEndingsToTree(List 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 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(); 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); } /// /// Get Scenario details for the Project in da mix /// [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(); 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 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(); 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(); 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); } } }