1028 lines
54 KiB
C#
1028 lines
54 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Data.Entity;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Web;
|
|
using System.Web.Mvc;
|
|
using EnVisage.Code;
|
|
using EnVisage.Code.Cache;
|
|
using jQuery.DataTables.Mvc;
|
|
using EnVisage.Models;
|
|
using EnVisage.Code.BLL;
|
|
using EnVisage.App_Start;
|
|
using Microsoft.AspNet.Identity;
|
|
using Microsoft.AspNet.Identity.EntityFramework;
|
|
using System.Data.Entity.Infrastructure;
|
|
using System.Web.Script.Serialization;
|
|
|
|
namespace EnVisage.Controllers
|
|
{
|
|
public class TeamController : BaseController
|
|
{
|
|
/// <summary>
|
|
/// An UI representation of Team to be displayed as list items
|
|
/// </summary>
|
|
public class ListTeam
|
|
{
|
|
public Guid Id { get; set; }
|
|
public string Name { get; set; }
|
|
public string Users { get; set; }
|
|
public string Company { get; set; }
|
|
public string CostCenter { get; set; }
|
|
public string CostCenterNumber { get; set; }
|
|
public string ColumnCostCenter { get; set; }
|
|
public string GLAccount { get; set; }
|
|
public string ReportTo { get; set; }
|
|
public bool IsResourcesAttached { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// An UI representation of category and open positions for planned capacity simple mode
|
|
/// </summary>
|
|
public class PlannedCapacitySimpleCategory
|
|
{
|
|
public Guid Id { get; set; }
|
|
public string Name { get; set; }
|
|
public bool InPlan { get; set; }
|
|
public List<PlannedCapacityResourcePosition> Positions { get; set; }
|
|
}
|
|
|
|
public class PlannedCapacityResourcePosition
|
|
{
|
|
public int Need { get; set; }
|
|
public DateTime StartDate { get; set; }
|
|
public DateTime? EndDate { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// GET: /Teams/
|
|
/// </summary>
|
|
/// <returns>Empty view</returns>
|
|
[HttpGet]
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Read)]
|
|
public ActionResult Index()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns JSON teams list with filters and sort for jQuery DataTables
|
|
/// </summary>
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Read)]
|
|
public JsonResult Index(JQueryDataTablesModel jQueryDataTablesModel)
|
|
{
|
|
int totalRecordCount;
|
|
int searchRecordCount;
|
|
|
|
var teams = GetTeams(startIndex: jQueryDataTablesModel.iDisplayStart,
|
|
pageSize: jQueryDataTablesModel.iDisplayLength, sortedColumns: jQueryDataTablesModel.GetSortedColumns(),
|
|
totalRecordCount: out totalRecordCount, searchRecordCount: out searchRecordCount, searchString: jQueryDataTablesModel.sSearch);
|
|
|
|
return this.DataTablesJson(items: teams,
|
|
totalRecords: totalRecordCount,
|
|
totalDisplayRecords: searchRecordCount,
|
|
sEcho: jQueryDataTablesModel.sEcho);
|
|
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult GetPlanCapacitySimpleMode(string teamId)
|
|
{
|
|
Guid gTeamId = Guid.Parse(teamId);
|
|
|
|
TeamManager teamManager = new TeamManager(DbContext);
|
|
ExpenditureCategoryManager categoryManager = new ExpenditureCategoryManager(DbContext);
|
|
var categoriesInPlan = teamManager.GetPlannedCapacityCategoriesIds(Guid.Parse(teamId));
|
|
DateTime CalendarMaxDate = (from c in DbContext.FiscalCalendars where c.Type == 0 && c.AdjustingPeriod == false orderby c.StartDate descending select c.EndDate ).FirstOrDefault();
|
|
|
|
//first of all, we need to get categories + dates when category starts in either plan or reality -
|
|
//these ranges allow us to work with this date range vs. entire fiscal calendar
|
|
var subq = (from sdPlan in DbContext.ScenarioDetail join t in DbContext.Teams on sdPlan.ParentID equals t.PlannedCapacityScenarioId where t.Id == gTeamId select sdPlan).Union
|
|
(from sdActual in DbContext.ScenarioDetail join t in DbContext.Teams on sdActual.ParentID equals t.ActualCapacityScenarioId where t.Id == gTeamId select sdActual);
|
|
|
|
//In addition to that, we need a data set of details for both planned and actual capacities to check them for need increase or decrease while we iterating through FC' weeks
|
|
Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>> plannedCapacityDetails =
|
|
(from sdPlan in DbContext.ScenarioDetail
|
|
join t in DbContext.Teams on sdPlan.ParentID equals t.PlannedCapacityScenarioId
|
|
where t.Id == gTeamId
|
|
select sdPlan).GroupBy(sd => sd.ExpenditureCategoryId).ToDictionary(k1 => k1.Key, g1 => g1.GroupBy(sd2 => sd2.WeekEndingDate).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>> actualCapacityDetails =
|
|
(from sdPlan in DbContext.ScenarioDetail
|
|
join t in DbContext.Teams on sdPlan.ParentID equals t.ActualCapacityScenarioId
|
|
where t.Id == gTeamId
|
|
select sdPlan).GroupBy(sd => sd.ExpenditureCategoryId).ToDictionary(k1 => k1.Key, g1 => g1.GroupBy(sd2 => sd2.WeekEndingDate).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
|
|
|
|
var resultSet = GetPositionsSimpleMode(plannedCapacityDetails, actualCapacityDetails, categoriesInPlan, CalendarMaxDate, subq);
|
|
|
|
return Json(new { Result = true, data = resultSet }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult GetCapacitySimpleMode(string teamId, bool planned)
|
|
{
|
|
Guid gTeamId = Guid.Empty;
|
|
Guid.TryParse(teamId, out gTeamId);
|
|
|
|
Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>> plannedCapacityDetails =
|
|
new Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>>();
|
|
Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>> actualCapacityDetails =
|
|
new Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>>();
|
|
IQueryable<ScenarioDetail> subq = (new List<ScenarioDetail>()).AsQueryable();
|
|
|
|
TeamManager teamManager = new TeamManager(DbContext);
|
|
ExpenditureCategoryManager categoryManager = new ExpenditureCategoryManager(DbContext);
|
|
var categoriesInPlan = teamManager.GetPlannedCapacityCategoriesIds(gTeamId);
|
|
DateTime CalendarMaxDate = (from c in DbContext.FiscalCalendars where c.Type == 0 && c.AdjustingPeriod == false && c.NonWorking == 0 orderby c.StartDate descending select c.EndDate).FirstOrDefault();
|
|
|
|
if (!gTeamId.Equals(Guid.Empty))
|
|
{
|
|
//first of all, we need to get categories + dates when category starts in either plan or reality -
|
|
//these ranges allow us to work with this date range vs. entire fiscal calendar
|
|
if (planned)
|
|
subq = from sdPlan in DbContext.ScenarioDetail join t in DbContext.Teams on sdPlan.ParentID equals t.PlannedCapacityScenarioId where t.Id == gTeamId select sdPlan;
|
|
else
|
|
subq = from sdActual in DbContext.ScenarioDetail join t in DbContext.Teams on sdActual.ParentID equals t.ActualCapacityScenarioId where t.Id == gTeamId select sdActual;
|
|
|
|
//In addition to that, we need a data set of details for both planned and actual capacities to check them for need increase or decrease while we iterating through FC' weeks
|
|
if (planned)
|
|
plannedCapacityDetails = (from sdPlan in DbContext.ScenarioDetail
|
|
join t in DbContext.Teams on sdPlan.ParentID equals t.PlannedCapacityScenarioId
|
|
where t.Id == gTeamId
|
|
select sdPlan).GroupBy(sd => sd.ExpenditureCategoryId).ToDictionary(k1 => k1.Key, g1 => g1.GroupBy(sd2 => sd2.WeekEndingDate).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
else plannedCapacityDetails = (from sdPlan in DbContext.ScenarioDetail
|
|
join t in DbContext.Teams on sdPlan.ParentID equals t.ActualCapacityScenarioId
|
|
where t.Id == gTeamId
|
|
select sdPlan).GroupBy(sd => sd.ExpenditureCategoryId).ToDictionary(k1 => k1.Key, g1 => g1.GroupBy(sd2 => sd2.WeekEndingDate).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
}
|
|
|
|
var resultSet = GetPositionsSimpleMode(plannedCapacityDetails, actualCapacityDetails, categoriesInPlan, CalendarMaxDate, subq);
|
|
|
|
return Json(new { Result = true, data = resultSet }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
private List<PlannedCapacitySimpleCategory> GetPositionsSimpleMode(Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>> plannedCapacityDetails, Dictionary<Guid?, Dictionary<DateTime?, List<ScenarioDetail>>> actualCapacityDetails, Guid?[] categoriesInPlan, DateTime CalendarMaxDate, IQueryable<ScenarioDetail> subq)
|
|
{
|
|
ExpenditureCategoryManager categoryManager = new ExpenditureCategoryManager(DbContext);
|
|
List<Tuple<Guid, string, decimal>> allCategories = categoryManager.GetCategoriesWithUoMValues();
|
|
var categoriesDates = (from r in subq
|
|
group r by r.ExpenditureCategoryId into rg
|
|
select new
|
|
{
|
|
ExpenditureCategoryId = rg.Key,
|
|
StartDate = rg.Min(z => z.WeekEndingDate),
|
|
EndDate = rg.Max(z => z.WeekEndingDate)
|
|
}).ToDictionary(c => c.ExpenditureCategoryId);
|
|
|
|
List<PlannedCapacitySimpleCategory> resultSet = new List<PlannedCapacitySimpleCategory>();
|
|
foreach (var cat in allCategories)
|
|
{
|
|
PlannedCapacitySimpleCategory resultItem = new PlannedCapacitySimpleCategory();
|
|
resultItem.Id = cat.Item1;
|
|
resultItem.Name = cat.Item2;
|
|
resultItem.InPlan = categoriesInPlan.Contains(cat.Item1);
|
|
|
|
var catDates = categoriesDates.ContainsKey(cat.Item1) ? categoriesDates[cat.Item1] : null;
|
|
if (catDates != null)
|
|
{
|
|
resultItem.Positions = new List<PlannedCapacityResourcePosition>();
|
|
Dictionary<DateTime?, List<ScenarioDetail>> categoryActuals = actualCapacityDetails.ContainsKey(cat.Item1) ?
|
|
actualCapacityDetails[cat.Item1] : new Dictionary<DateTime?, List<ScenarioDetail>>();
|
|
Dictionary<DateTime?, List<ScenarioDetail>> categoryPlans = plannedCapacityDetails.ContainsKey(cat.Item1) ?
|
|
plannedCapacityDetails[cat.Item1] : new Dictionary<DateTime?, List<ScenarioDetail>>();
|
|
|
|
//AG: I did not generate single FiscalCalendar weeks set for all categories intentionally as once we change FiscalCalendar generation to on-the-fly calculation, it
|
|
//does not make sense to keep redundant data for all categories as we can simply re-generate FC weeks for each of them
|
|
var wkRanges = (from c in DbContext.FiscalCalendars
|
|
where c.Type == (int)FiscalCalendarModel.FiscalYearType.Week && c.EndDate >= catDates.StartDate && c.EndDate <= catDates.EndDate && c.AdjustingPeriod == false && c.NonWorking == 0
|
|
orderby c.StartDate
|
|
select new { StartDate = c.StartDate, EndDate = c.EndDate }).ToList();
|
|
|
|
decimal currentNeed = 0;
|
|
decimal newNeed = 0;
|
|
DateTime prevEndDate = DateTime.MinValue;
|
|
Stack<PlannedCapacityResourcePosition> positionsStack = new Stack<PlannedCapacityResourcePosition>();
|
|
foreach (var range in wkRanges)
|
|
{
|
|
newNeed = 0;
|
|
ScenarioDetail weeklyPlan = categoryPlans.ContainsKey(range.EndDate) ?
|
|
categoryPlans[range.EndDate].FirstOrDefault() : null;
|
|
ScenarioDetail weeklyActual = categoryActuals.ContainsKey(range.EndDate) ?
|
|
categoryActuals[range.EndDate].FirstOrDefault() : null;
|
|
|
|
if (weeklyPlan != null)
|
|
newNeed = weeklyPlan.Quantity.HasValue ? Math.Round(weeklyPlan.Quantity.Value) : 0;
|
|
|
|
if (weeklyActual != null)
|
|
newNeed -= weeklyActual.Quantity.HasValue ? Math.Round(weeklyActual.Quantity.Value) : 0;
|
|
|
|
if (currentNeed != newNeed)
|
|
{
|
|
#region Positions management
|
|
if (currentNeed > 0)
|
|
{
|
|
if (newNeed > currentNeed)
|
|
{
|
|
//open new position(s)
|
|
while (currentNeed < newNeed)
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = 1
|
|
});
|
|
currentNeed += cat.Item3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//close latest position(s) while we are above the zero, then open new negative positions
|
|
while (currentNeed > newNeed)
|
|
{
|
|
if (currentNeed > 0)
|
|
{
|
|
if (positionsStack.Count == 0)
|
|
throw new InvalidOperationException("There is a bug somewhere in the planned capacity positions builder algorithm - the stack should not be empty here - positive position close");
|
|
|
|
PlannedCapacityResourcePosition pos = positionsStack.Pop();
|
|
|
|
if (pos.Need < 0)
|
|
throw new InvalidOperationException("There is a bug somewhere in the planned capacity positions builder algorithm - previously opened position should be positive");
|
|
|
|
pos.EndDate = prevEndDate < CalendarMaxDate ? prevEndDate : (DateTime?)null;
|
|
resultItem.Positions.Add(pos);
|
|
}
|
|
else
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = -1
|
|
});
|
|
}
|
|
currentNeed -= cat.Item3;
|
|
}
|
|
}
|
|
}
|
|
else if (currentNeed < 0)
|
|
{
|
|
if (newNeed < currentNeed)
|
|
{
|
|
//open new negative position(s)
|
|
while (currentNeed > newNeed)
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = -1
|
|
});
|
|
currentNeed -= cat.Item3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//close latest negative position(s) while we less than 0; open new positive position when we passed 0
|
|
while (currentNeed < newNeed)
|
|
{
|
|
if (currentNeed < 0)
|
|
{
|
|
if (positionsStack.Count == 0)
|
|
throw new InvalidOperationException("There is a bug somewhere in the planned capacity positions builder algorithm - the stack should not be empty here - negative position close");
|
|
|
|
PlannedCapacityResourcePosition pos = positionsStack.Pop();
|
|
|
|
if (pos.Need > 0)
|
|
throw new InvalidOperationException("There is a bug somewhere in the planned capacity positions builder algorithm - previously opened position should be negative");
|
|
|
|
pos.EndDate = prevEndDate < CalendarMaxDate ? prevEndDate : (DateTime?)null;
|
|
resultItem.Positions.Add(pos);
|
|
}
|
|
else
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = 1
|
|
});
|
|
}
|
|
currentNeed += cat.Item3;
|
|
}
|
|
}
|
|
}
|
|
else //currentNeed == 0
|
|
{
|
|
if (newNeed > 0)
|
|
{
|
|
//open new position(s)
|
|
while (currentNeed < newNeed)
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = 1
|
|
});
|
|
currentNeed += cat.Item3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//open new negative position(s)
|
|
while (currentNeed > newNeed)
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = -1
|
|
});
|
|
currentNeed -= cat.Item3;
|
|
}
|
|
}
|
|
}
|
|
currentNeed = newNeed;
|
|
#endregion
|
|
}
|
|
prevEndDate = range.EndDate;
|
|
}
|
|
//we may have several positions still in stack as we retrieve only a significant portion of fiscal calendar.
|
|
//we just need to put all of these positions into the list
|
|
while (positionsStack.Count > 0)
|
|
{
|
|
PlannedCapacityResourcePosition pos = positionsStack.Pop();
|
|
pos.EndDate = wkRanges[wkRanges.Count - 1].EndDate < CalendarMaxDate ? wkRanges[wkRanges.Count - 1].EndDate : (DateTime?)null;
|
|
resultItem.Positions.Add(pos);
|
|
}
|
|
}
|
|
resultSet.Add(resultItem);
|
|
}
|
|
return resultSet;
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult GetResourcesForFillDecrease(Guid teamId, Guid categoryId, DateTime dateToFire)
|
|
{
|
|
DateTime CalendarMaxDate = (from c in DbContext.FiscalCalendars where c.Type == 0 && c.AdjustingPeriod == false && c.NonWorking == 0 orderby c.StartDate descending select c.EndDate).FirstOrDefault();
|
|
DateTime dateToFireResourceGap = dateToFire.AddDays(-7);
|
|
|
|
var resources = (from r in DbContext.PeopleResources
|
|
where r.TeamId == teamId && r.ExpenditureCategoryId == categoryId && r.IsActiveEmployee && r.EndDate >= dateToFire && r.StartDate <= dateToFireResourceGap
|
|
orderby r.LastName
|
|
select new
|
|
{
|
|
Id = r.Id,
|
|
Name = r.FirstName + " " + r.LastName,
|
|
EndDate = r.EndDate != CalendarMaxDate ? r.EndDate : (DateTime?)null
|
|
});
|
|
|
|
return Json(new { Result = true, data = resources }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult FillDecreaseSubmit(Guid resourceId, DateTime endDate)
|
|
{
|
|
PeopleResourcesManager resManager = new PeopleResourcesManager(DbContext);
|
|
PeopleResourceController resController = new PeopleResourceController();
|
|
PeopleResourceModel model = (PeopleResourceModel)resManager.Load(resourceId);
|
|
resController.RemoveCapacity(model, false);
|
|
model.EndDate = endDate;
|
|
resManager.Save(model);
|
|
DbContext.SaveChanges();
|
|
//we need to reset weekly capacities then add capacity through resource controller method
|
|
model = null;
|
|
model = (PeopleResourceModel)resManager.Load(resourceId);
|
|
resController.AddCapacity(model, false);
|
|
|
|
return Json(new { Result = true }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
|
|
private List<string> GetUserName(List<string> Ids)
|
|
{
|
|
var ac = new ApplicationDbContext();
|
|
var usermanager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(ac));
|
|
var users = new Dictionary<string, string>();
|
|
foreach(var Id in Ids) {
|
|
var user = usermanager.FindById(Id);
|
|
var lastName = user.LastName;
|
|
while (users.Keys.Contains(lastName))
|
|
lastName = lastName + "_";
|
|
|
|
users.Add(lastName, user.FirstName + " " + user.LastName);
|
|
}
|
|
|
|
return users.OrderBy(x=>x.Key).Select(x=>x.Value).ToList();
|
|
}
|
|
|
|
private IList<ListTeam> GetTeams(int startIndex,
|
|
int pageSize,
|
|
ReadOnlyCollection<SortedColumn> sortedColumns,
|
|
out int totalRecordCount,
|
|
out int searchRecordCount,
|
|
string searchString)
|
|
{
|
|
|
|
var query = from c in DbContext.Teams
|
|
select new ListTeam()
|
|
{
|
|
Id = c.Id,
|
|
Name = c.Name,
|
|
Company = c.Company.Name,
|
|
CostCenter = c.CreditDepartment.Name,
|
|
CostCenterNumber = c.CreditDepartment.CreditNumber,
|
|
ColumnCostCenter = c.CreditDepartment.Name + (string.IsNullOrEmpty(c.CreditDepartment.CreditNumber) ?
|
|
string.Empty : " (" + c.CreditDepartment.CreditNumber + ")"),
|
|
ReportTo = c.Contact.FirstName + " " + c.Contact.LastName,
|
|
IsResourcesAttached = c.PeopleResources.Any()
|
|
};
|
|
|
|
//filter
|
|
|
|
if (!string.IsNullOrWhiteSpace(searchString))
|
|
{
|
|
query = query.Where(c => c.Name.ToLower().Contains(searchString.ToLower()));
|
|
}
|
|
|
|
//sort
|
|
foreach (var sortedColumn in sortedColumns)
|
|
{
|
|
switch (sortedColumn.PropertyName)
|
|
{
|
|
case "Id":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.Id);
|
|
else
|
|
query = query.OrderByDescending(c => c.Id);
|
|
break;
|
|
case "Company":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.Company);
|
|
else
|
|
query = query.OrderByDescending(c => c.Company);
|
|
break;
|
|
case "ColumnCostCenter":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.CostCenter).ThenBy(c => c.CostCenterNumber);
|
|
else
|
|
query = query.OrderByDescending(c => c.CostCenter).ThenByDescending(c => c.CostCenterNumber);
|
|
break;
|
|
case "View":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.GLAccount);
|
|
else
|
|
query = query.OrderByDescending(c => c.GLAccount);
|
|
break;
|
|
case "ReportTo":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.ReportTo);
|
|
else
|
|
query = query.OrderByDescending(c => c.ReportTo);
|
|
break;
|
|
default:
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.Name);
|
|
else
|
|
query = query.OrderByDescending(c => c.Name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
totalRecordCount = DbContext.Teams.Count();
|
|
searchRecordCount = query.Count();
|
|
var list = query.Skip(startIndex).Take(pageSize).ToList();
|
|
var usersList = new List<string>();
|
|
foreach (var q in list)
|
|
{
|
|
usersList.Clear();
|
|
var users = DbContext.User2Team.Where(x => x.TeamId == q.Id).Select(x=>x.UserId).ToList();
|
|
usersList.AddRange(GetUserName(users.Select(user => user).ToList()));
|
|
q.Users = string.Join("; ", usersList.ToArray());// usersList.ToArray());
|
|
//q.Users = q.Users.TrimEnd(new char[]{' ',';'});//q.Users.TrimEnd(' ').TrimEnd(';');
|
|
}
|
|
return list;
|
|
}
|
|
|
|
|
|
// GET: /Team/Details/5
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Read)]
|
|
public ActionResult Board()
|
|
{
|
|
// SA. ENV-804. Changed teams sorting
|
|
var model = new TeamboardModel();
|
|
var teams = new TeamManager(DbContext).GetTeamsByUser(Guid.Parse(User.Identity.GetID()));
|
|
if (!string.IsNullOrEmpty(Request.QueryString["teamId"]))
|
|
model.Teams.Add(teams.Where(x => x.Id.ToString() == Request.QueryString["teamId"].ToString()).FirstOrDefault());
|
|
else model.Teams = teams.OrderBy(x => x.Name).ToList();
|
|
model.CalendarMaxDate = (from c in DbContext.FiscalCalendars where c.Type == 0 && c.AdjustingPeriod == false && c.NonWorking == 0 orderby c.StartDate descending select c.EndDate).FirstOrDefault();
|
|
|
|
// SA. ENV-815. Get User Preferences
|
|
SetUserSelectedTeamId(model);
|
|
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetID()));
|
|
if (user != null)
|
|
ViewBag.IsUOMHours = !user.PreferredResourceAllocation;
|
|
return View(model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set initially selected team according to user preferences
|
|
/// </summary>
|
|
/// <param name="model"></param>
|
|
/// <remarks>SA. ENV-815</remarks>
|
|
private void SetUserSelectedTeamId(TeamboardModel model)
|
|
{
|
|
Guid selectedTeamId = Guid.Empty;
|
|
|
|
string userIdAsText = User.Identity.GetID();
|
|
Guid userId = new Guid(userIdAsText);
|
|
|
|
string pageUrl = HttpContext.Request.Url.AbsolutePath;
|
|
|
|
var prefRecords = DbContext.UserPreferences.Where(x => x.UserId.Equals(userId) &&
|
|
x.Url.Equals(pageUrl, StringComparison.InvariantCultureIgnoreCase) &&
|
|
x.Section.Equals("teamsBlock", StringComparison.InvariantCultureIgnoreCase));
|
|
|
|
if (prefRecords.Count() > 0)
|
|
{
|
|
string prefData = prefRecords.First().Data;
|
|
JavaScriptSerializer ser = new JavaScriptSerializer();
|
|
var data = ser.Deserialize<List<PreferencesItem>>(prefData);
|
|
|
|
var selectedTeamPrefs = data.Where(x => x.Key.Equals("pageSelectedTeam", StringComparison.InvariantCultureIgnoreCase) &&
|
|
x.Value.Length > 0).ToList();
|
|
|
|
if (selectedTeamPrefs.Count > 0)
|
|
Guid.TryParse(selectedTeamPrefs.First().Value, out selectedTeamId);
|
|
|
|
}
|
|
|
|
|
|
if (model.Teams.Count > 0)
|
|
{
|
|
List<Guid> teamIds = model.Teams.Select(x => x.Id).ToList();
|
|
|
|
if (!selectedTeamId.Equals(Guid.Empty) && teamIds.Contains(selectedTeamId))
|
|
{
|
|
model.SelectedTeamId = selectedTeamId;
|
|
model.SelectedTeamName = model.Teams.Where(x => x.Id.Equals(selectedTeamId)).Select(x => x.Name).First();
|
|
}
|
|
else
|
|
{
|
|
model.SelectedTeamId = teamIds.First();
|
|
model.SelectedTeamName = model.Teams.First().Name;
|
|
}
|
|
}
|
|
}
|
|
|
|
// SA. ENV-1254
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Read)]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult GetTeamsByExpenditureCategory(Guid id)
|
|
{
|
|
if ((id == null) || (id.Equals(Guid.Empty)))
|
|
throw new ArgumentNullException("id");
|
|
|
|
Guid UserId = SecurityManager.GetUserPrincipal();
|
|
|
|
TeamManager mngr = new TeamManager(DbContext);
|
|
IQueryable<Team> teams = mngr.GetTeamsByExpenditureCategory(id, UserId);
|
|
|
|
Dictionary<string, string> result = teams.ToDictionary(k => k.Id.ToString(), v => v.Name);
|
|
return Json(result);
|
|
}
|
|
|
|
// GET: /Team/Edit/5
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult Edit(Guid? id)
|
|
{
|
|
ViewBag.TeamId = id.ToString();
|
|
|
|
var model = new TeamModel();
|
|
try
|
|
{
|
|
var manager = new TeamManager(DbContext);
|
|
model = (TeamModel)manager.Load(id) ?? new TeamModel();
|
|
if (Guid.Empty.Equals(model.Id))
|
|
{
|
|
model.UserId = new List<Guid>();
|
|
}
|
|
else
|
|
{
|
|
model.UserId = DbContext.User2Team.Where(x => x.TeamId == model.Id).ToList().Select(x => Guid.Parse(x.UserId)).ToList();
|
|
}
|
|
|
|
return PartialView("_editTeam", 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("_editTeam", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult Edit(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("_editTeam", 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("_editTeam", model);
|
|
}
|
|
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult Delete(Guid deleteTeamId)
|
|
{
|
|
try
|
|
{
|
|
if (ContentLocker.IsLock("Team", deleteTeamId.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var manager = new TeamManager(DbContext);
|
|
var dbObj = manager.Load(deleteTeamId, false);
|
|
if (dbObj == null)
|
|
return HttpNotFound();
|
|
if (!dbObj.PeopleResources.Any())
|
|
{
|
|
var capacityScenarioId = dbObj.PlannedCapacityScenarioId;
|
|
var actualCapacityScenarioId = dbObj.ActualCapacityScenarioId;
|
|
DbContext.User2Team.RemoveRange(DbContext.User2Team.Where(c2s => c2s.TeamId == dbObj.Id));
|
|
DbContext.Team2Scenario.RemoveRange(DbContext.Team2Scenario.Where(c2s => c2s.TeamId == dbObj.Id));
|
|
DbContext.Team2Project.RemoveRange(DbContext.Team2Project.Where(x => x.TeamId == dbObj.Id));
|
|
DbContext.Team2View.RemoveRange(DbContext.Team2View.Where(tv => tv.TeamId == dbObj.Id));
|
|
DbContext.TeamAllocations.RemoveRange(DbContext.TeamAllocations.Where(x => x.TeamId == dbObj.Id));
|
|
DbContext.Teams.Remove(dbObj);
|
|
DbContext.SaveChanges();
|
|
if (capacityScenarioId != null)
|
|
(DbContext as IObjectContextAdapter).ObjectContext.ExecuteStoreCommand(string.Format("exec sp_DeleteScenario '{0}'", capacityScenarioId));
|
|
if (actualCapacityScenarioId != null)
|
|
(DbContext as IObjectContextAdapter).ObjectContext.ExecuteStoreCommand(string.Format("exec sp_DeleteScenario '{0}'", actualCapacityScenarioId));
|
|
ContentLocker.RemoveLock("Team", dbObj.Id.ToString(), User.Identity.Name);
|
|
return RedirectToAction("Index");
|
|
}
|
|
//ModelState.AddModelError("error", "This team can't be deleted, because it's has some attached people resources.");
|
|
SetErrorScript(message: "This team cannot be deleted, because it has some attached people resources.");
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
return RedirectToAction("Index");
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="startDate"></param>
|
|
/// <param name="endDate"></param>
|
|
/// <param name="expCat"></param>
|
|
/// <param name="capacityValue"></param>
|
|
/// <param name="teamId"></param>
|
|
/// <param name="isUOMHours">A value indicating whether graph data is in hours or in resources. If <b>null</b> or <b>true</b> - Hours, false - Resources.</param>
|
|
/// <returns></returns>
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public JsonResult ChangeCapacity(DateTime? startDate, DateTime? endDate, string expCat, string capacityValue, Guid teamId, bool? permanent)
|
|
{
|
|
if (string.IsNullOrEmpty(capacityValue) || capacityValue == "undefined" || capacityValue == "0")
|
|
return null;
|
|
var rateManager = new RateManager(DbContext);
|
|
var expenditureCategoryId = new Guid(expCat);
|
|
var val = int.Parse(capacityValue);
|
|
var expCats = DbContext.ExpenditureCategory.Where(x => x.Id == expenditureCategoryId).ToDictionary(x => x.Id);
|
|
var uomIds = expCats.Select(t => t.Value.UOMId);
|
|
var uoms = DbContext.UOMs.Where(x => uomIds.Contains(x.Id)).ToDictionary(x => x.Id);
|
|
var uomMultiplier = Utils.GetUOMMultiplier(expCats, uoms, expenditureCategoryId, false);
|
|
val = (int)Math.Round((decimal)val / uomMultiplier);
|
|
var teamManager = new TeamManager(DbContext);
|
|
var team = teamManager.Load(teamId, false);
|
|
var scenarioManager = new ScenarioManager(DbContext);
|
|
Scenario scen = GetOrCreatePlannedCapacityScenario(team);
|
|
|
|
List<DateTime> weekendings = null;
|
|
if (permanent != null && (bool)permanent)
|
|
{
|
|
weekendings = (from c in DbContext.FiscalCalendars
|
|
where c.Type == 0 &&
|
|
((startDate >= c.StartDate && startDate <= c.EndDate) ||
|
|
(c.StartDate >= startDate))
|
|
orderby c.StartDate
|
|
select c.EndDate).ToList();
|
|
}
|
|
else
|
|
{
|
|
weekendings = (from c in DbContext.FiscalCalendars
|
|
where c.Type == 0 && c.AdjustingPeriod == false && c.NonWorking == 0 &&
|
|
((startDate >= c.StartDate && startDate <= c.EndDate) ||
|
|
(c.StartDate >= startDate && c.EndDate <= endDate) ||
|
|
(endDate >= c.StartDate && endDate <= c.EndDate))
|
|
orderby c.StartDate
|
|
select c.EndDate).ToList();
|
|
}
|
|
|
|
var rates = rateManager.GetRates(expenditureCategoryId, RateModel.RateType.Global);
|
|
var sds = (from sd in DbContext.ScenarioDetail where sd.ParentID == team.PlannedCapacityScenarioId && sd.ExpenditureCategoryId == expenditureCategoryId && weekendings.Contains(sd.WeekEndingDate.Value) select sd).ToList();
|
|
foreach (var week in weekendings)
|
|
{
|
|
var sd = (from s in sds where s.WeekEndingDate == week select s).FirstOrDefault();
|
|
if (sd != null)
|
|
{
|
|
sd.Quantity += val;
|
|
if (sd.Quantity < 0)
|
|
sd.Quantity = 0;
|
|
sd.Cost = sd.Quantity * rateManager.GetRateValue(rates, expenditureCategoryId, week);
|
|
|
|
}
|
|
else
|
|
{
|
|
if (val < 0) continue;
|
|
var newSD = new ScenarioDetail { Id = Guid.NewGuid(), ParentID = scen.Id, Quantity = val };
|
|
newSD.Cost = newSD.Quantity * rateManager.GetRateValue(rates, expenditureCategoryId, week);
|
|
newSD.ExpenditureCategoryId = expenditureCategoryId;
|
|
newSD.WeekEndingDate = week;
|
|
newSD.WeekOrdinal = 0;
|
|
DbContext.ScenarioDetail.Add(newSD);
|
|
}
|
|
}
|
|
DbContext.SaveChanges();
|
|
|
|
return Json(new { Result = true, data = weekendings }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public JsonResult ResetCapacity(DateTime? startDate, DateTime? endDate, string expCat, Guid teamId, bool? permanent)
|
|
{
|
|
RateManager rateManager = new RateManager(DbContext);
|
|
TeamManager teamManager = new TeamManager(DbContext);
|
|
Team team = teamManager.Load(teamId, false);
|
|
Scenario scen = GetOrCreatePlannedCapacityScenario(team);
|
|
if (team.ActualCapacityScenarioId == null || Guid.Empty == team.ActualCapacityScenarioId)
|
|
return Json(new { Result = false, data = "Cannot reset capacity - Team does not have Actual Capacity scenario" }, JsonRequestBehavior.AllowGet);
|
|
|
|
Scenario actualCapacityScenario = (new ScenarioManager(DbContext)).Load(team.ActualCapacityScenarioId, true);
|
|
Guid expenditureCategoryId = !string.IsNullOrEmpty(expCat) ? new Guid(expCat) : Guid.Empty;
|
|
Dictionary<Guid, List<PeopleResource>> teamResourcesByCategory = team.PeopleResources.Where(r => r.IsActiveEmployee).GroupBy(r => r.ExpenditureCategoryId).ToDictionary(key => key.Key, group => group.ToList());
|
|
Dictionary<Guid, Dictionary<DateTime?, List<ScenarioDetail>>> actualCapacityDetails = new Dictionary<Guid, Dictionary<DateTime?, List<ScenarioDetail>>>();
|
|
Dictionary<Guid, Dictionary<DateTime?, List<ScenarioDetail>>> existingPlannedDetails = new Dictionary<Guid, Dictionary<DateTime?, List<ScenarioDetail>>>();
|
|
|
|
List<DateTime> weekendings = null;
|
|
if (permanent != null && (bool)permanent)
|
|
weekendings = (from c in DbContext.FiscalCalendars.AsNoTracking() where c.Type == 0 && c.StartDate >= startDate && c.AdjustingPeriod == false && c.NonWorking == 0 orderby c.StartDate select c.EndDate).ToList();
|
|
else weekendings = (from c in DbContext.FiscalCalendars.AsNoTracking() where c.Type == 0 && c.StartDate >= startDate && c.EndDate <= endDate && c.AdjustingPeriod == false && c.NonWorking == 0 orderby c.StartDate select c.EndDate).ToList();
|
|
|
|
//List<ScenarioDetail> detailsToDelete;
|
|
|
|
if (expenditureCategoryId != Guid.Empty)
|
|
{
|
|
actualCapacityDetails.Add(expenditureCategoryId,
|
|
(from sd in DbContext.ScenarioDetail.AsNoTracking() where sd.ParentID == team.ActualCapacityScenarioId && sd.ExpenditureCategoryId == expenditureCategoryId && weekendings.Contains(sd.WeekEndingDate.Value) select sd)
|
|
.GroupBy(sd => sd.WeekEndingDate).ToDictionary(key => key.Key, group => group.ToList()));
|
|
existingPlannedDetails.Add(expenditureCategoryId,
|
|
(from sd in DbContext.ScenarioDetail where sd.ParentID == team.PlannedCapacityScenarioId && sd.ExpenditureCategoryId == expenditureCategoryId && weekendings.Contains(sd.WeekEndingDate.Value) select sd)
|
|
.GroupBy(sd => sd.WeekEndingDate).ToDictionary(key => key.Key, group => group.ToList()));
|
|
//detailsToDelete = (from sd in DbContext.ScenarioDetail where sd.ParentID == team.PlannedCapacityScenarioId && sd.ExpenditureCategoryId == expenditureCategoryId && weekendings.Contains(sd.WeekEndingDate.Value) select sd).ToList();
|
|
}
|
|
else
|
|
{
|
|
actualCapacityDetails = (from sd in DbContext.ScenarioDetail.AsNoTracking() where sd.ParentID == team.ActualCapacityScenarioId && weekendings.Contains(sd.WeekEndingDate.Value) select sd)
|
|
.GroupBy(sd => sd.ExpenditureCategoryId.Value).ToDictionary(key => key.Key,
|
|
group => group.GroupBy(x => x.WeekEndingDate).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
existingPlannedDetails = (from sd in DbContext.ScenarioDetail where sd.ParentID == team.PlannedCapacityScenarioId && weekendings.Contains(sd.WeekEndingDate.Value) select sd)
|
|
.GroupBy(sd => sd.ExpenditureCategoryId.Value).ToDictionary(key => key.Key,
|
|
group => group.GroupBy(x => x.WeekEndingDate).ToDictionary(k2 => k2.Key, g2 => g2.ToList()));
|
|
//detailsToDelete = (from sd in DbContext.ScenarioDetail where sd.ParentID == team.PlannedCapacityScenarioId && weekendings.Contains(sd.WeekEndingDate.Value) select sd).ToList();
|
|
}
|
|
|
|
//get rates for actual capacity categories
|
|
Guid[] actualCategoriesIds = actualCapacityDetails.Keys.ToArray();
|
|
//Dictionary<Guid, decimal> categoriesUoMValues = (from ec in DbContext.ExpenditureCategory where actualCategoriesIds.Contains(ec.Id) select new { Id = ec.Id, Value = ec.UOM != null ? ec.UOM.UOMValue : 0 }).ToDictionary(x => x.Id, y => y.Value);
|
|
Dictionary<Guid, List<Rate>> categoriesRates = DbContext.Rates.AsNoTracking().Where(r => actualCategoriesIds.Contains(r.ExpenditureCategoryId) && r.Type == (int)RateModel.RateType.Global)
|
|
.GroupBy(x => x.ExpenditureCategoryId).ToDictionary(key => key.Key, group => group.ToList());
|
|
|
|
//DbContext.ScenarioDetail.RemoveRange(detailsToDelete);
|
|
List<ScenarioDetail> sdToAdd = new List<ScenarioDetail>();
|
|
foreach (Guid categoryId in actualCapacityDetails.Keys)
|
|
{
|
|
List<Rate> rates = categoriesRates.ContainsKey(categoryId) ? categoriesRates[categoryId] : new List<Rate>();
|
|
Dictionary<DateTime?, List<ScenarioDetail>> actualCapacities = actualCapacityDetails.ContainsKey(categoryId) ?
|
|
actualCapacityDetails[categoryId] : new Dictionary<DateTime?, List<ScenarioDetail>>();
|
|
//We do not need UoM stuff here as we can just copy values from Actual Capacity week details into Planned Capacity week details
|
|
Dictionary<DateTime?, List<ScenarioDetail>> existingDetails = existingPlannedDetails.ContainsKey(categoryId) ?
|
|
existingPlannedDetails[categoryId] : new Dictionary<DateTime?, List<ScenarioDetail>>();
|
|
foreach (var week in weekendings)
|
|
{
|
|
ScenarioDetail existingD = existingDetails.ContainsKey(week) ? existingDetails[week].First() : null;
|
|
|
|
decimal capacityValue = actualCapacities.ContainsKey(week) ? actualCapacities[week].FirstOrDefault().Quantity ?? 0 : 0;
|
|
if (existingD != null)
|
|
{
|
|
if (capacityValue < 0)
|
|
capacityValue = 0;
|
|
var currrate = rates.FirstOrDefault(x => week >= x.StartDate && week <= x.EndDate);
|
|
existingD.Quantity = capacityValue;
|
|
existingD.Cost = existingD.Quantity * ((currrate != null) ? currrate.Rate1 : 0);
|
|
DbContext.Entry(existingD).State = EntityState.Modified;
|
|
|
|
//we also need to remove this existing detail record from the main dicionary to mark it as "processed".
|
|
//we should delete all remaining items because they do not get into our "new" list of categories for planned capacity -
|
|
//e.g. old planned capacity had more categories than actual capacity has
|
|
existingPlannedDetails[categoryId].Remove(week);
|
|
}
|
|
else
|
|
{
|
|
if (capacityValue <= 0) continue;
|
|
var newSD = new ScenarioDetail { Id = Guid.NewGuid(), ParentID = scen.Id, Quantity = capacityValue };
|
|
var currrate = rates.FirstOrDefault(x => week >= x.StartDate && week <= x.EndDate);
|
|
newSD.Cost = newSD.Quantity * ((currrate != null) ? currrate.Rate1 : 0);
|
|
newSD.ExpenditureCategoryId = categoryId;
|
|
newSD.WeekEndingDate = week;
|
|
newSD.WeekOrdinal = 0;
|
|
sdToAdd.Add(newSD);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (Guid catId in existingPlannedDetails.Keys)
|
|
{
|
|
foreach (DateTime? wk in existingPlannedDetails[catId].Keys)
|
|
{
|
|
DbContext.ScenarioDetail.RemoveRange(existingPlannedDetails[catId][wk]);
|
|
}
|
|
}
|
|
|
|
DbContext.ScenarioDetail.AddRange(sdToAdd);
|
|
//We do not save scenario details history here - it is too time consuming on big calendars
|
|
DbContext.SaveChangesWithoutHistory();
|
|
|
|
//AG: Second SaveChanges call made intentionally because it is much slower to search for max scenariodetails date in both DB and DbContext.Local collections than just a single simple query
|
|
//Plus, upper call does not save history and it is good to at least save the fact of scenario change
|
|
scen.EndDate = (from sd in DbContext.ScenarioDetail where sd.ParentID == scen.Id select sd.WeekEndingDate).Max();
|
|
DbContext.Entry(scen).State = EntityState.Modified;
|
|
DbContext.SaveChanges();
|
|
|
|
return Json(new { Result = true, data = "" }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
private Scenario GetOrCreatePlannedCapacityScenario(Team team)
|
|
{
|
|
var scenarioManager = new ScenarioManager(DbContext);
|
|
Scenario scen = null;
|
|
if (team.PlannedCapacityScenarioId == null || team.PlannedCapacityScenarioId == Guid.Empty)
|
|
{
|
|
scen = new Scenario
|
|
{
|
|
Name = team.Name.Trim() + " Planned Capacity",
|
|
Type = (int)ScenarioType.TeamPlannedCapacity,
|
|
Id = Guid.NewGuid()
|
|
};
|
|
team.PlannedCapacityScenarioId = scen.Id;
|
|
DbContext.Scenarios.Add(scen);
|
|
DbContext.SaveChanges();
|
|
}
|
|
else scen = scenarioManager.Load(team.PlannedCapacityScenarioId);
|
|
return scen;
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Read)]
|
|
public ActionResult GetTeamResourcesInfo(Guid? teamId)
|
|
{
|
|
var model = new TeamboardModel
|
|
{
|
|
Teams = new List<Team>(new Team[] { new TeamManager(DbContext).Load(teamId) }),
|
|
CalendarMaxDate = (from c in DbContext.FiscalCalendars where c.Type == 0 && c.AdjustingPeriod == false && c.NonWorking == 0 orderby c.StartDate descending select c.EndDate).FirstOrDefault()
|
|
};
|
|
return PartialView("~/Views/PeopleResource/_resourcesList.cshtml", model);
|
|
}
|
|
|
|
// SA. ENV-492
|
|
class TeamResourceModel
|
|
{
|
|
public Guid Id;
|
|
public string Name;
|
|
public bool IsActive;
|
|
public string StartDate;
|
|
public string EndDate;
|
|
public string ExpenditureCategory;
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Teams, level = AccessLevel.Read)]
|
|
public ActionResult GetTeamResources(Guid teamId)
|
|
{
|
|
if (teamId.Equals(Guid.Empty))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
TeamManager mngr = new TeamManager(DbContext);
|
|
Team team = mngr.Load(teamId);
|
|
|
|
var calendarMaxDate =
|
|
(from c in DbContext.FiscalCalendars
|
|
where c.Type == 0 && c.AdjustingPeriod == false && c.NonWorking == 0
|
|
orderby c.StartDate descending
|
|
select c.EndDate).FirstOrDefault();
|
|
|
|
List<TeamResourceModel> resources =
|
|
team.PeopleResources.OrderBy(x => x.LastName).Select(t => new TeamResourceModel()
|
|
{
|
|
Id = t.Id,
|
|
Name = t.FirstName + " " + t.LastName,
|
|
IsActive = t.IsActiveEmployee,
|
|
StartDate = t.StartDate.ToShortDateString(),
|
|
EndDate = t.EndDate != calendarMaxDate ? t.EndDate.ToShortDateString() : "",
|
|
ExpenditureCategory = t.ExpenditureCategory.GetView().ExpCategoryWithCcName
|
|
}).ToList();
|
|
|
|
return Json(resources);
|
|
}
|
|
|
|
private List<long[]> FillMissingWeeends(List<long[]> list, IEnumerable<long> weekEnds)
|
|
{
|
|
if (list.Count < weekEnds.Count())
|
|
{
|
|
var result = weekEnds.Except(list.Select(x => x[0]));
|
|
list.AddRange(result.Select(x => new long[] { x, 0 }));
|
|
}
|
|
|
|
return list.OrderBy(x => x[0]).ToList();
|
|
}
|
|
|
|
}
|
|
}
|