1093 lines
56 KiB
C#
1093 lines
56 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Data.Entity;
|
|
using System.Linq;
|
|
using System.Net;
|
|
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 System.Web.Script.Serialization;
|
|
using System.Text;
|
|
using EnVisage.Code.Validation;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace EnVisage.Controllers
|
|
{
|
|
public class TeamController : BaseController
|
|
{
|
|
#region Actions
|
|
|
|
/// <summary>
|
|
/// GET: /Teams/
|
|
/// </summary>
|
|
/// <returns>Empty view</returns>
|
|
[HttpGet]
|
|
[AreaSecurity(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]
|
|
[AreaSecurity(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);
|
|
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, true);
|
|
|
|
return Json(new { Result = true, data = resultSet }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult GetCapacitySimpleMode(string teamId, bool planned)
|
|
{
|
|
Guid gTeamId;
|
|
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);
|
|
var categoriesInPlan = teamManager.GetPlannedCapacityCategoriesIds(gTeamId);
|
|
DateTime calendarMaxDate = DbContext.FiscalCalendars
|
|
.Where(c => c.Type == 0 && c.AdjustingPeriod == false && c.NonWorking == 0)
|
|
.OrderByDescending(c => c.StartDate)
|
|
.Select(c => 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);
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult GetResourcesForFillDecrease(Guid teamId, Guid categoryId, DateTime dateToFire)
|
|
{
|
|
// Get all resources in the team
|
|
PeopleResourcesManager mngr = new PeopleResourcesManager(this.DbContext);
|
|
var resources = mngr.LoadPeopleResourcesByTeamNonStrict(teamId, false, dateToFire.ToUniversalTime());
|
|
|
|
// Filter resources by EC
|
|
var resourcesOfEC = resources.ToList();
|
|
resourcesOfEC.RemoveAll(x => (x.StartDate >= dateToFire) || (x.EndDate.HasValue && (x.EndDate.Value <= dateToFire)));
|
|
resourcesOfEC.RemoveAll(x => !x.ExpenditureCategoryId.Equals(categoryId));
|
|
resourcesOfEC.RemoveAll(x => !x.IsActiveEmployee);
|
|
|
|
var result = resourcesOfEC.OrderBy(x => x.LastName).Select(x => new
|
|
{
|
|
Id = x.Id,
|
|
Name = x.FirstName + " " + x.LastName,
|
|
EndDate = !x.PermanentResource ? x.EndDate.Value : (DateTime?)null
|
|
});
|
|
|
|
return Json(new { Result = true, data = result }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult FillDecreaseSubmit(Guid? resourceId, DateTime endDate)
|
|
{
|
|
PeopleResourcesManager resManager = new PeopleResourcesManager(DbContext);
|
|
|
|
using (var transaction = DbContext.Database.BeginTransaction())
|
|
{
|
|
var transactionId = DbContext.GetClientConnectionId();
|
|
var userId = User.Identity.GetID();
|
|
|
|
try
|
|
{
|
|
if (resourceId != null)
|
|
{
|
|
var resourceId1 = (Guid)resourceId;
|
|
PeopleResourceModel model = resManager.LoadPeopleResource(resourceId1, DateTime.UtcNow.Date,
|
|
false, false, false);
|
|
model.PermanentResource = false;
|
|
model.EndDate = endDate;
|
|
model.ChangeCapacity = false;
|
|
|
|
resManager.Save(model);
|
|
|
|
DbContext.SaveChanges();
|
|
transaction.Commit();
|
|
Task.Run(() => AuditProxy.CommitHistoryChanges(transactionId, userId));
|
|
|
|
return Json(new { Result = true }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
throw new ArgumentException("Resource to fill capacity decrease can't be null.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
transaction.Rollback();
|
|
AuditProxy.ClearHistoryChanges(transactionId);
|
|
LogException(ex);
|
|
|
|
return Json(new { Result = false }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
}
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Read)]
|
|
public ActionResult Board()
|
|
{
|
|
// SA. ENV-804. Changed teams sorting
|
|
var model = new TeamboardModel();
|
|
var teamMngr = new TeamManager(DbContext);
|
|
|
|
// var teams = new TeamManager(DbContext).LoadTeamsWithResourcesByUser(Guid.Parse(User.Identity.GetID()));
|
|
var teams = teamMngr.GetTeamsByUser(Guid.Parse(User.Identity.GetID())).Select(t => t.TeamId);
|
|
|
|
if (!string.IsNullOrEmpty(Request.QueryString["teamId"]))
|
|
{
|
|
var tm = teams.FirstOrDefault(x => x.ToString() == Request.QueryString["teamId"].ToString());
|
|
|
|
if (tm.Equals(Guid.Empty))
|
|
return RedirectToAccessDenied();
|
|
|
|
var teamData = teamMngr.Load(tm, true);
|
|
model.Teams.Add((TeamWithResourcesModel)teamData);
|
|
}
|
|
else
|
|
{
|
|
var teamsData = teamMngr.LoadTeams(teams);
|
|
teamsData = teamsData.OrderBy(x => x.Name);
|
|
model.Teams = teamsData.ToList().Select(x => (TeamWithResourcesModel)x).ToList();
|
|
}
|
|
|
|
model.CalendarMaxDate = (from c in DbContext.FiscalCalendars where c.Type == (int)FiscalCalendarModel.FiscalYearType.Week && c.AdjustingPeriod == false && c.NonWorking == 0 orderby c.StartDate descending select c.EndDate).FirstOrDefault();
|
|
|
|
SetUserSelectedTeamId(model);
|
|
return View(model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Read)]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult GetTeamsByExpenditureCategory(Guid id)
|
|
{
|
|
if ((id == null) || (id.Equals(Guid.Empty)))
|
|
throw new ArgumentNullException(nameof(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);
|
|
}
|
|
|
|
[HttpGet]
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult Edit(Guid? id)
|
|
{
|
|
ViewBag.TeamId = id.ToString();
|
|
|
|
var model = new TeamModel();
|
|
try
|
|
{
|
|
if (id.HasValue)
|
|
{
|
|
var manager = new TeamManager(DbContext);
|
|
model = manager.LoadTeamModel(id.Value) ?? new TeamModel();
|
|
}
|
|
if (Guid.Empty.Equals(model.Id))
|
|
{
|
|
model.UserId = new List<Guid>();
|
|
model.WorkFlowContacts = new List<Guid>();
|
|
model.NotificationContacts = new List<Guid>();
|
|
model.NotificationWorkFlowStates = new List<string>();
|
|
}
|
|
else
|
|
{
|
|
model.UserId = DbContext.User2Team.Where(x => x.TeamId == model.Id).ToList().Select(x => Guid.Parse(x.UserId)).ToList();
|
|
model.WorkFlowContacts = (new WorkFlowManager(this.DbContext)).GetContactList(model.Id, WorkFlowContactNotificationType.None);
|
|
model.NotificationContacts = (new WorkFlowManager(this.DbContext)).GetContactList(model.Id, WorkFlowContactNotificationType.TeamScenarioAdd);
|
|
model.NotificationWorkFlowStates = (new WorkFlowManager(this.DbContext)).GetNotificationStates(model.Id, WorkFlowContactNotificationType.TeamScenarioAdd);
|
|
}
|
|
|
|
return PartialView("_editTeam", model);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAjax]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult Edit(TeamModel model)
|
|
{
|
|
if (model != null && model.Id != Guid.Empty && ContentLocker.IsLock("Team", model.Id.ToString(), User.Identity.GetUserName()))
|
|
{
|
|
ModelState.AddModelError(string.Empty, @"This team is currently being updated by another user. Please attempt your edit again later.");
|
|
return new FailedJsonResult(ModelState);
|
|
}
|
|
|
|
try
|
|
{
|
|
if (model == null)
|
|
throw new ArgumentNullException(nameof(model));
|
|
|
|
model.TrimStringProperties();
|
|
|
|
if (model.ReportToId.HasValue && model.ReportToId == Guid.Empty)
|
|
model.ReportToId = null;
|
|
|
|
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.GetUserName());
|
|
|
|
return new SuccessJsonResult();
|
|
}
|
|
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);
|
|
ModelState.AddModelError(string.Empty, @"Cannot save team. Try again later.");
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
ModelState.AddModelError(string.Empty, @"Cannot save team. Try again later.");
|
|
}
|
|
|
|
return new FailedJsonResult(ModelState);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public ActionResult Delete(Guid deleteTeamId)
|
|
{
|
|
try
|
|
{
|
|
if (deleteTeamId != Guid.Empty && ContentLocker.IsLock("Team", deleteTeamId.ToString(), User.Identity.GetUserName()))
|
|
{
|
|
ModelState.AddModelError(string.Empty, @"This team is currently being updated by another user. Please attempt your edit again later.");
|
|
return new FailedJsonResult(ModelState);
|
|
}
|
|
|
|
var manager = new TeamManager(DbContext);
|
|
var dbObj = manager.Load(deleteTeamId, false);
|
|
if (dbObj == null)
|
|
throw new InvalidOperationException(
|
|
$"System cannot delete team {deleteTeamId} because it does not exist");
|
|
|
|
if (!DbContext.VW_TeamResource.Any(t => t.TeamId == dbObj.Id))
|
|
{
|
|
manager.Delete(dbObj.Id);
|
|
ContentLocker.RemoveLock("Team", dbObj.Id.ToString(), User.Identity.GetUserName());
|
|
return new SuccessJsonResult();
|
|
}
|
|
ModelState.AddModelError(string.Empty, @"This team cannot be deleted, because it has some attached people resources.");
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
ModelState.AddModelError(string.Empty, blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
ModelState.AddModelError(string.Empty, @"Cannot delete team. Try again later.");
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
ModelState.AddModelError(string.Empty, @"Cannot delete team. Try again later.");
|
|
}
|
|
|
|
return new FailedJsonResult(ModelState);
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public JsonResult ChangeCapacity(DateTime? startDate, DateTime? endDate, Guid expenditureCategoryId, int capacityValue, Guid teamId, bool? permanent)
|
|
{
|
|
#if DEBUG
|
|
var watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
Logger.Debug("Start ChangeCapacity");
|
|
#endif
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
$"TeamController.ChangeCapacity method. teamId:{teamId}, startDate:{startDate}, endDate{endDate}, ExpCatId:{expenditureCategoryId}, permanent:{permanent}, capacityValue: {capacityValue}");
|
|
Logger.Debug(sb);
|
|
|
|
if (capacityValue == 0)
|
|
return null;
|
|
|
|
var rateManager = new RateManager(DbContext);
|
|
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);
|
|
capacityValue = (int)Math.Round(capacityValue / uomMultiplier);
|
|
var teamManager = new TeamManager(DbContext);
|
|
var team = teamManager.LoadTeamWithResourcesById(teamId);
|
|
Scenario plannedCapacityScenario = GetOrCreatePlannedCapacityScenario(team);
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ChangeCapacity Start Load has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ChangeCapacity Start Load {watch1.ElapsedMilliseconds} ms");
|
|
watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
#endif
|
|
|
|
List<DateTime> weekendings;
|
|
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();
|
|
}
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ChangeCapacity List<DateTime> weekendings; has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ChangeCapacity List<DateTime> weekendings; {watch1.ElapsedMilliseconds} ms");
|
|
watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
#endif
|
|
|
|
var rates = rateManager.GetRates(new List<Guid> {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();
|
|
var scenarioDetailToAddCollection = new List<ScenarioDetail>();
|
|
var scenarioDetailToDeleteCollection = new List<ScenarioDetail>();
|
|
foreach (var week in weekendings)
|
|
{
|
|
var sd = (from s in sds where s.WeekEndingDate == week select s).FirstOrDefault();
|
|
if (sd != null)
|
|
{
|
|
sd.Quantity += capacityValue;
|
|
Logger.Debug("/Team/ChangeCapacity method. TeamId:{0}, capacityValue: {1}, sd.Quantity: {2}", teamId, capacityValue, sd.Quantity);
|
|
if (sd.Quantity <= 0)
|
|
{
|
|
scenarioDetailToDeleteCollection.Add(sd);
|
|
}
|
|
else
|
|
sd.Cost = sd.Quantity * rateManager.GetRateValue(rates, expenditureCategoryId, week);
|
|
}
|
|
else
|
|
{
|
|
if (capacityValue <= 0)
|
|
continue;
|
|
Logger.Debug("/Team/ChangeCapacity method. TeamId:{0}, capacityValue: {1}", teamId, capacityValue);
|
|
|
|
var newSd = new ScenarioDetail { Id = Guid.NewGuid(), ParentID = plannedCapacityScenario.Id, Quantity = capacityValue };
|
|
newSd.Cost = newSd.Quantity * rateManager.GetRateValue(rates, expenditureCategoryId, week);
|
|
newSd.ExpenditureCategoryId = expenditureCategoryId;
|
|
newSd.WeekEndingDate = week;
|
|
newSd.WeekOrdinal = 0;
|
|
scenarioDetailToAddCollection.Add(newSd);
|
|
}
|
|
}
|
|
DbContext.ScenarioDetail.AddRange(scenarioDetailToAddCollection);
|
|
DbContext.ScenarioDetail.RemoveRange(scenarioDetailToDeleteCollection);
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ChangeCapacity foreach (var week in weekendings) has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ChangeCapacity foreach (var week in weekendings) {watch1.ElapsedMilliseconds} ms");
|
|
watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
#endif
|
|
|
|
DbContext.BulkSaveChanges();
|
|
|
|
// taken from ResetCapacity action method, original comment for reason of the second call is:
|
|
// 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
|
|
plannedCapacityScenario.EndDate = (from sd in DbContext.ScenarioDetail where sd.ParentID == plannedCapacityScenario.Id select sd.WeekEndingDate).Max();
|
|
if (plannedCapacityScenario.EndDate.HasValue)
|
|
plannedCapacityScenario.EndDate = plannedCapacityScenario.EndDate.Value.Date;
|
|
DbContext.SaveChanges();
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ChangeCapacity All has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ChangeCapacity All {watch1.ElapsedMilliseconds} ms");
|
|
#endif
|
|
|
|
return Json(new { Result = true, data = weekendings }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Write)]
|
|
public JsonResult ResetCapacity(DateTime? startDate, DateTime? endDate, Guid? expenditureCategoryId, Guid teamId, bool? permanent)
|
|
{
|
|
#if DEBUG
|
|
var watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
Logger.Debug("Start ResetCapacity");
|
|
#endif
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine($"TeamController.ResetCapacity method. teamId:{teamId}, startDate:{startDate}, endDate{endDate}, ExpCatId:{expenditureCategoryId}, permanent:{permanent}");
|
|
Logger.Debug(sb);
|
|
|
|
var rateManager = new RateManager(DbContext);
|
|
var teamManager = new TeamManager(DbContext);
|
|
var team = teamManager.LoadTeamWithResourcesById(teamId);
|
|
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 plannedCapacityScenario = GetOrCreatePlannedCapacityScenario(team);
|
|
|
|
var actualCapacityDetails = new Dictionary<Guid, Dictionary<DateTime?, List<ScenarioDetail>>>();
|
|
var existingPlannedDetails = new Dictionary<Guid, Dictionary<DateTime?, List<ScenarioDetail>>>();
|
|
|
|
List<DateTime> weekendings;
|
|
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();
|
|
|
|
if (expenditureCategoryId.HasValue)
|
|
{
|
|
actualCapacityDetails.Add(expenditureCategoryId.Value,
|
|
(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.Value,
|
|
(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()));
|
|
}
|
|
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()));
|
|
}
|
|
|
|
//get rates for actual capacity categories
|
|
var actualCategoriesIds = actualCapacityDetails.Keys.ToArray();
|
|
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());
|
|
|
|
var scenarioDetailToDeleteCollection = new List<ScenarioDetail>();
|
|
var sdToAdd = new List<ScenarioDetail>();
|
|
foreach (Guid categoryId in actualCapacityDetails.Keys)
|
|
{
|
|
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)
|
|
{
|
|
existingD.Quantity = capacityValue;
|
|
existingD.Cost = existingD.Quantity * rateManager.GetRateValue(categoriesRates, categoryId, week);
|
|
}
|
|
else
|
|
{
|
|
scenarioDetailToDeleteCollection.Add(existingD);
|
|
}
|
|
|
|
//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 = plannedCapacityScenario.Id,
|
|
Quantity = capacityValue,
|
|
Cost = capacityValue * rateManager.GetRateValue(categoriesRates, categoryId, week),
|
|
ExpenditureCategoryId = categoryId,
|
|
WeekEndingDate = week,
|
|
WeekOrdinal = 0
|
|
};
|
|
sdToAdd.Add(newSD);
|
|
}
|
|
}
|
|
}
|
|
|
|
var removedDetails = scenarioDetailToDeleteCollection;
|
|
foreach (var categoryId in existingPlannedDetails.Keys)
|
|
foreach (var weekEnding in existingPlannedDetails[categoryId].Keys)
|
|
removedDetails.AddRange(existingPlannedDetails[categoryId][weekEnding]);
|
|
|
|
DbContext.ScenarioDetail.RemoveRange(removedDetails);
|
|
DbContext.ScenarioDetail.AddRange(sdToAdd);
|
|
//We do not save scenario details history here - it is too time consuming on big calendars
|
|
DbContext.BulkSaveChanges();
|
|
|
|
//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
|
|
plannedCapacityScenario.EndDate = (from sd in DbContext.ScenarioDetail where sd.ParentID == plannedCapacityScenario.Id select sd.WeekEndingDate).Max();
|
|
if (plannedCapacityScenario.EndDate.HasValue)
|
|
plannedCapacityScenario.EndDate = plannedCapacityScenario.EndDate.Value.Date;
|
|
DbContext.SaveChanges();
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ResetCapacity All has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ResetCapacity All {watch1.ElapsedMilliseconds} ms");
|
|
#endif
|
|
|
|
return Json(new { Result = true, data = "" }, JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Teams, level = AccessLevel.Read)]
|
|
public ActionResult GetTeamResources(Guid teamId)
|
|
{
|
|
if (teamId.Equals(Guid.Empty))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
PeopleResourcesManager mngr = new PeopleResourcesManager(DbContext);
|
|
List<TeamResourceModel> resources =
|
|
mngr.LoadPeopleResourcesByTeamNonStrict(teamId, true, DateTime.UtcNow).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?.ToShortDateString() ?? "",
|
|
ExpenditureCategory = t.ExpenditureCategory.GetView().ExpCategoryWithCcName,
|
|
ResourceScenarioAllocationsInfo = t.ResourceScenarioAllocationsInfo,
|
|
ResourceActualsInfo = t.ResourceActualsInfo,
|
|
ResourceMixAllocationsInfo = t.ResourceMixAllocationsInfo
|
|
}).ToList();
|
|
|
|
return Json(resources);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult GetTeamsById(List<Guid> ids)
|
|
{
|
|
try
|
|
{
|
|
var teamManager = new TeamManager(DbContext);
|
|
var fiscalCalendar = (new FiscalCalendarManager(DbContext)).GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, null, null, true, null, false);
|
|
var teams = teamManager.GetTeamsByUserFiltered(User.Identity.GetID(), ids, null, null);
|
|
var teamModels = teamManager.GetTeamsInfo(teams, fiscalCalendar)
|
|
.ToDictionary(x => x.Id.ToString());
|
|
|
|
return BigJson(teamModels);
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
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, bool withoutSupperExpCat = false)
|
|
{
|
|
ExpenditureCategoryManager categoryManager = new ExpenditureCategoryManager(DbContext);
|
|
var allCategories = categoryManager.GetExpenditureDetails(!withoutSupperExpCat);
|
|
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 catId in allCategories.Keys)
|
|
{
|
|
var cat = allCategories[catId];
|
|
|
|
PlannedCapacitySimpleCategory resultItem = new PlannedCapacitySimpleCategory
|
|
{
|
|
Id = cat.ExpenditureCategoryId,
|
|
Name = cat.ExpenditureCategoryName,
|
|
InPlan = categoriesInPlan.Contains(cat.ExpenditureCategoryId)
|
|
};
|
|
|
|
var catDates = categoriesDates.ContainsKey(cat.ExpenditureCategoryId) ? categoriesDates[cat.ExpenditureCategoryId] : null;
|
|
if (catDates != null)
|
|
{
|
|
resultItem.Positions = new List<PlannedCapacityResourcePosition>();
|
|
Dictionary<DateTime?, List<ScenarioDetail>> categoryActuals = actualCapacityDetails.ContainsKey(cat.ExpenditureCategoryId) ?
|
|
actualCapacityDetails[cat.ExpenditureCategoryId] : new Dictionary<DateTime?, List<ScenarioDetail>>();
|
|
Dictionary<DateTime?, List<ScenarioDetail>> categoryPlans = plannedCapacityDetails.ContainsKey(cat.ExpenditureCategoryId) ?
|
|
plannedCapacityDetails[cat.ExpenditureCategoryId] : 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 { c.StartDate, 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.UOMValue;
|
|
}
|
|
}
|
|
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.UOMValue;
|
|
}
|
|
}
|
|
}
|
|
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.UOMValue;
|
|
}
|
|
}
|
|
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.UOMValue;
|
|
}
|
|
}
|
|
}
|
|
else //currentNeed == 0
|
|
{
|
|
if (newNeed > 0)
|
|
{
|
|
//open new position(s)
|
|
while (currentNeed < newNeed)
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = 1
|
|
});
|
|
currentNeed += cat.UOMValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//open new negative position(s)
|
|
while (currentNeed > newNeed)
|
|
{
|
|
positionsStack.Push(new PlannedCapacityResourcePosition()
|
|
{
|
|
StartDate = range.StartDate,
|
|
Need = -1
|
|
});
|
|
currentNeed -= cat.UOMValue;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
private IList<ListTeam> GetTeams(int startIndex,
|
|
int pageSize,
|
|
ReadOnlyCollection<SortedColumn> sortedColumns,
|
|
out int totalRecordCount,
|
|
out int searchRecordCount,
|
|
string searchString)
|
|
{
|
|
|
|
var query = DbContext.Teams.Select(team => new ListTeam
|
|
{
|
|
Id = team.Id,
|
|
Name = team.Name,
|
|
Company = team.Company.Name,
|
|
CostCenter = team.CreditDepartment.Name,
|
|
CostCenterNumber = team.CreditDepartment.CreditNumber,
|
|
ColumnCostCenter = team.CreditDepartment.Name + (string.IsNullOrEmpty(team.CreditDepartment.CreditNumber) ?
|
|
string.Empty : " (" + team.CreditDepartment.CreditNumber + ")"),
|
|
ReportTo = team.Contact.FirstName + " " + team.Contact.LastName,
|
|
IsResourcesAttached = DbContext.PeopleResource2Team.Any(s => s.TeamId == team.Id),
|
|
Users = DbContext.User2Team
|
|
.Where(s => s.TeamId == team.Id)
|
|
.Select(x => x.AspNetUser.FirstName + " " + x.AspNetUser.LastName)
|
|
.ToList()
|
|
});
|
|
|
|
//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":
|
|
query = sortedColumn.Direction == SortingDirection.Ascending ? query.OrderBy(c => c.Id) : query.OrderByDescending(c => c.Id);
|
|
break;
|
|
case "Company":
|
|
query = sortedColumn.Direction == SortingDirection.Ascending ? query.OrderBy(c => c.Company) : query.OrderByDescending(c => c.Company);
|
|
break;
|
|
case "ColumnCostCenter":
|
|
query = sortedColumn.Direction == SortingDirection.Ascending ? query.OrderBy(c => c.CostCenter).ThenBy(c => c.CostCenterNumber) : query.OrderByDescending(c => c.CostCenter).ThenByDescending(c => c.CostCenterNumber);
|
|
break;
|
|
case "View":
|
|
query = sortedColumn.Direction == SortingDirection.Ascending ? query.OrderBy(c => c.GLAccount) : query.OrderByDescending(c => c.GLAccount);
|
|
break;
|
|
case "ReportTo":
|
|
query = sortedColumn.Direction == SortingDirection.Ascending ? query.OrderBy(c => c.ReportTo) : query.OrderByDescending(c => c.ReportTo);
|
|
break;
|
|
default:
|
|
query = sortedColumn.Direction == SortingDirection.Ascending ? query.OrderBy(c => c.Name) : query.OrderByDescending(c => c.Name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
totalRecordCount = DbContext.Teams.Count();
|
|
searchRecordCount = query.Count();
|
|
|
|
return query.Skip(startIndex).Take(pageSize).ToList();
|
|
}
|
|
|
|
/// <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.Any())
|
|
{
|
|
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.Id = selectedTeamId;
|
|
model.SelectedTeamName = model.Teams.Where(x => x.Id.Equals(selectedTeamId)).Select(x => x.Name).First();
|
|
}
|
|
else
|
|
{
|
|
model.Id = teamIds.First();
|
|
model.SelectedTeamName = model.Teams.First().Name;
|
|
}
|
|
}
|
|
}
|
|
|
|
//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();
|
|
//}
|
|
|
|
private Scenario GetOrCreatePlannedCapacityScenario(TeamWithResourcesModel team)
|
|
{
|
|
var scenarioManager = new ScenarioManager(DbContext);
|
|
Scenario scen;
|
|
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, isReadOnly: false);
|
|
return scen;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Models
|
|
|
|
/// <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 List<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; }
|
|
public long StartDateMs => Utils.ConvertToUnixDate(StartDate);
|
|
public long EndDateMs => EndDate.HasValue ? Utils.ConvertToUnixDate(EndDate.Value) : 0;
|
|
}
|
|
|
|
class TeamResourceModel
|
|
{
|
|
public Guid Id;
|
|
public string Name;
|
|
public bool IsActive;
|
|
public string StartDate;
|
|
public string EndDate;
|
|
public string ExpenditureCategory;
|
|
public PeopleResourceAllocationsInfoModel ResourceScenarioAllocationsInfo;
|
|
public PeopleResourceAllocationsInfoModel ResourceActualsInfo;
|
|
public PeopleResourceAllocationsInfoModel ResourceMixAllocationsInfo;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|