3009 lines
149 KiB
C#
3009 lines
149 KiB
C#
using System.Data.Entity;
|
|
using System.Data.Entity.Infrastructure;
|
|
using System.Net;
|
|
using EnVisage.App_Start;
|
|
using EnVisage.Code.BLL;
|
|
using EnVisage.Code.Cache;
|
|
using EnVisage.Code.HtmlHelpers;
|
|
using EnVisage.Models;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Web.Mvc;
|
|
using jQuery.DataTables.Mvc;
|
|
using EnVisage.Code;
|
|
using Microsoft.AspNet.Identity;
|
|
using Newtonsoft.Json;
|
|
using System.Linq.Expressions;
|
|
using System.IO;
|
|
|
|
namespace EnVisage.Controllers
|
|
{
|
|
[Authorize]
|
|
public class ScenariosController : BaseController
|
|
{
|
|
// GET: /Scenarios/Templates
|
|
[HttpGet]
|
|
[AreaSecurityAttribute(area = Areas.ScenarioTemplates, level = AccessLevel.Read)]
|
|
public ActionResult Templates()
|
|
{
|
|
if (!SecurityManager.CheckSecurityObjectPermission(Areas.ScenarioTemplates, AccessLevel.Read))
|
|
return Redirect("/");
|
|
return View();
|
|
}
|
|
|
|
[HttpGet]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult LoadScenario(ScenarioLoadModel loadModel)
|
|
{
|
|
if (loadModel == null)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
CreateScenarioModel model = new CreateScenarioModel()
|
|
{
|
|
SaveCallback = loadModel.SaveCallback,
|
|
CanSaveDraft = loadModel.CanSaveDraft
|
|
};
|
|
model.ScenarioId = Guid.Empty;
|
|
model.Step1.TemplateId = Guid.Empty;
|
|
|
|
model.Step1.Project = DbContext.Projects.AsNoTracking().FirstOrDefault(p => p.Id == loadModel.Id);
|
|
if (null == model.Step1.Project)
|
|
return new HttpNotFoundResult();
|
|
|
|
model.Step1.ProjectDeadline = model.Step1.Project.Deadline;
|
|
model.Step1.StartDate = loadModel.StartDate;
|
|
|
|
// SA. ENV-773. Begin
|
|
model.Step1.Teams = new SlidersGroupModel
|
|
{
|
|
GroupId = Guid.NewGuid(),
|
|
Options = Utils.GetTeams(false)
|
|
};
|
|
|
|
|
|
int projectTeamsCount = model.Step1.Project.Team2Project.Count();
|
|
int slidersTotalSumm = 100;
|
|
double remainingValue = slidersTotalSumm;
|
|
|
|
List<SliderModel> projectTeamModels =
|
|
(from Team2Project teamRec in model.Step1.Project.Team2Project
|
|
select new SliderModel()
|
|
{
|
|
Id = teamRec.Id,
|
|
EntityId = teamRec.TeamId,
|
|
Name = teamRec.Team.Name,
|
|
ParentId = model.Step1.Teams.GroupId,
|
|
AllocatePercentage = Math.Round((double)slidersTotalSumm / projectTeamsCount, 0)
|
|
}).ToList();
|
|
|
|
for (int index = 0; index < projectTeamModels.Count; index++)
|
|
{
|
|
if ((projectTeamModels.Count - index) != 1)
|
|
remainingValue -= Convert.ToInt32(projectTeamModels[index].AllocatePercentage);
|
|
else
|
|
// The last slider
|
|
projectTeamModels[index].AllocatePercentage = remainingValue;
|
|
}
|
|
|
|
if (loadModel.TeamId.HasValue && !projectTeamModels.Exists(x => x.EntityId == loadModel.TeamId.Value))
|
|
{
|
|
projectTeamModels.Add(new SliderModel()
|
|
{
|
|
Id = loadModel.TeamId.Value,
|
|
EntityId = loadModel.TeamId.Value,
|
|
Name = loadModel.TeamName,
|
|
ParentId = model.Step1.Teams.GroupId,
|
|
AllocatePercentage = 0
|
|
});
|
|
}
|
|
|
|
model.Step1.Teams.Sliders = projectTeamModels;
|
|
// SA. ENV-773. End
|
|
|
|
if (model.Step1.Project.ParentProjectId.HasValue)
|
|
{
|
|
model.Step1.ProjectId = model.Step1.Project.ParentProjectId.Value;
|
|
model.Step1.PartId = loadModel.Id;
|
|
}
|
|
else
|
|
{
|
|
model.Step1.ProjectId = loadModel.Id;
|
|
}
|
|
model.Step3.IsRevenueGenerating = model.Step1.Project.IsRevenueGenerating;
|
|
model.Step1.StatusIsEditable = loadModel.StatusIsEditable;
|
|
model.CurrentStep = "Step1";
|
|
return PartialView("_createScenario", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult SubmitCreateScenarioStep1(CreateScenarioModel.GeneralInfoModel model, string[] expCatGroups)
|
|
{
|
|
model.TrimStringProperties();
|
|
try
|
|
{
|
|
//var teamallocations =
|
|
// Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<Guid, short>>(model.TeamAllocations);
|
|
|
|
#region Populate fields not stored on client
|
|
|
|
var projman = new ProjectManager(DbContext);
|
|
var project = projman.Load(model.PartId ?? model.ProjectId);
|
|
model.Project = project;
|
|
model.ProjectDeadline = project.Deadline;
|
|
|
|
var ecmanager = new ScenarioManager(DbContext);
|
|
var ec = ecmanager.GetExpenditureCategories(model.TemplateId);
|
|
model.ScenarioExpenditures = new ScenarioModel.ExpenditureItem[ec.Count()];
|
|
List<string> selectedIDs;
|
|
if (null == expCatGroups)
|
|
selectedIDs = new List<string>();
|
|
else
|
|
selectedIDs = expCatGroups.ToList();
|
|
var checkedECs = ec.Where(expenditureItem => selectedIDs.Contains(expenditureItem.Id.ToString())).ToList();
|
|
var uncheckedECs = ec.Where(item => !selectedIDs.Contains(item.Id.ToString())).ToList();
|
|
int i = 0;
|
|
foreach (var expenditureItem in uncheckedECs)
|
|
{
|
|
model.ScenarioExpenditures[i] = new ScenarioModel.ExpenditureItem
|
|
{
|
|
Id = expenditureItem.Id,
|
|
Group = expenditureItem.Group,
|
|
Name = expenditureItem.Name,
|
|
Checked = false
|
|
};
|
|
i++;
|
|
}
|
|
foreach (var expenditureItem in checkedECs)
|
|
{
|
|
model.ScenarioExpenditures[i] = new ScenarioModel.ExpenditureItem
|
|
{
|
|
Id = expenditureItem.Id,
|
|
Group = expenditureItem.Group,
|
|
Name = expenditureItem.Name,
|
|
Checked = true
|
|
};
|
|
i++;
|
|
}
|
|
var projTeamIds = model.Project.Team2Project.Select(x => x.TeamId).ToList();
|
|
//var missingTeams = teamallocations.Keys.Where(x => !projTeamIds.Contains(x)).ToList();
|
|
var missingTeams = model.Teams.Sliders.Where(x => !projTeamIds.Contains(x.EntityId))
|
|
.Select(x => x.EntityId).ToList();
|
|
|
|
DbContext.Teams.AsNoTracking().Where(t => missingTeams.Contains(t.Id))
|
|
.ToList().ForEach(x => model.Project.Team2Project.Add(new Team2Project
|
|
{
|
|
Team = x
|
|
}));
|
|
|
|
var detailsModel = LoadScenarioDetailsModel(model);
|
|
model.LaborSplitPercentage = detailsModel.LaborSplitPercentage;
|
|
model.EFXSplit = detailsModel.EFXSplit;
|
|
model.Teams.Options = Utils.GetTeams(false);
|
|
#endregion
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
if (checkedECs.Count == 0)
|
|
{
|
|
ModelState.AddModelError("ScenarioExpenditures",
|
|
string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Expenditures"));
|
|
}
|
|
else
|
|
{
|
|
return PartialView("_generalStep", 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 scenario. Try again later.");
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
//SetErrorScript();
|
|
ModelState.AddModelError(string.Empty, "Cannot save scenario. Try again later.");
|
|
}
|
|
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
return PartialView("_generalStep", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult SubmitCreateScenarioStep3(CreateScenarioModel.FinInfoModel model)
|
|
{
|
|
model.TrimStringProperties();
|
|
try
|
|
{
|
|
//var costSavingItems = new List<ScenarioCostSavingModel>();
|
|
//if (model.CostSavings != null && !string.IsNullOrWhiteSpace(model.CostSavings.CostSavingItems))
|
|
// costSavingItems = JsonConvert.DeserializeObject<List<ScenarioCostSavingModel>>(model.CostSavings.CostSavingItems);
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
return PartialView("_finStep", 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 scenario. Try again later.");
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
//SetErrorScript();
|
|
ModelState.AddModelError(string.Empty, "Cannot save scenario. Try again later.");
|
|
}
|
|
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
return PartialView("_finStep", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult GetECsByTemplateId(Guid? id, List<Guid> selectedExpCats, List<Guid> teams, bool overrideChecked)
|
|
{
|
|
var manager = new ScenarioManager(DbContext);
|
|
var ec = manager.GetExpenditureCategories(id, teams);
|
|
var checkedItems = selectedExpCats != null && selectedExpCats.Count > 0 ? selectedExpCats : new List<Guid>();
|
|
foreach (var expenditureItem in ec)
|
|
{
|
|
if (overrideChecked)
|
|
{
|
|
expenditureItem.Checked = checkedItems.Contains(expenditureItem.Id);
|
|
}
|
|
else
|
|
{
|
|
expenditureItem.Checked |= checkedItems.Contains(expenditureItem.Id);
|
|
}
|
|
}
|
|
|
|
return PartialView("_createScenarioExpenditures", ec);
|
|
}
|
|
|
|
[HttpGet]
|
|
//[ValidateAntiForgeryToken]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult GetProjectPartsByProjectId(Guid? Id)
|
|
{
|
|
Guid userId = SecurityManager.GetUserPrincipal();
|
|
var pp = Utils.GetProjectParts(Id, userId);
|
|
return PartialView("_createScenarioProjectParts", pp);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult ProcessRates(RatesModel model)
|
|
{
|
|
try
|
|
{
|
|
model.TrimStringProperties();
|
|
switch (model.Mode)
|
|
{
|
|
case RatesModel.FormMode.ListRates:
|
|
|
|
#region List Rates
|
|
|
|
model.EditRateId = Guid.Empty;
|
|
model.DeleteRateId = Guid.Empty;
|
|
if (!Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var gRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Global
|
|
select c);
|
|
model.AddGlobalRange(gRates.ToList());
|
|
}
|
|
if (Guid.Empty != model.ScenarioId && !Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var lRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Derived &&
|
|
c.ParentId == model.ScenarioId
|
|
select c);
|
|
model.AddLocalRange(lRates.ToList());
|
|
}
|
|
|
|
#endregion
|
|
|
|
break;
|
|
case RatesModel.FormMode.EditRate:
|
|
|
|
#region Load Edit Rate Form
|
|
|
|
if (model.EditRateId.HasValue && !Guid.Empty.Equals(model.EditRateId))
|
|
{
|
|
var rate = DbContext.Rates.AsNoTracking().FirstOrDefault(t => t.Id == model.EditRateId);
|
|
if (rate != null)
|
|
{
|
|
model.EditRate = new RateModel()
|
|
{
|
|
Id = rate.Id,
|
|
DerivedObjectId = rate.DerivedId,
|
|
EndDate = rate.EndDate,
|
|
ExpenditureCategoryId = rate.ExpenditureCategoryId,
|
|
FreezeRate = rate.FreezeRate,
|
|
ParentId = rate.ParentId,
|
|
Rate1 = rate.Rate1,
|
|
StartDate = rate.StartDate,
|
|
Type = (RateModel.RateType)rate.Type
|
|
};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
model.EditRate = new RateModel()
|
|
{
|
|
DerivedObjectId = Guid.Empty,
|
|
EndDate = new DateTime(9999, 12, 31),
|
|
ExpenditureCategoryId = model.ExpenditureCategoryId,
|
|
FreezeRate = 0,
|
|
ParentId = model.ScenarioId,
|
|
Rate1 = 0,
|
|
StartDate = new DateTime(1753, 1, 1),
|
|
Type = RateModel.RateType.Derived
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
|
|
break;
|
|
case RatesModel.FormMode.ConfirmEditRate:
|
|
|
|
#region Save Rate
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
if (ContentLocker.IsLock("ExpenditureCategoryRate", model.EditRateId.ToString(),
|
|
User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var manager = new RateManager(DbContext);
|
|
manager.Save(model.EditRate);
|
|
DbContext.SaveChanges();
|
|
manager.Dispose();
|
|
ContentLocker.RemoveLock("ExpenditureCategoryRate", model.EditRateId.ToString(),
|
|
User.Identity.Name);
|
|
model.Mode = RatesModel.FormMode.ListRates; // reset action mode
|
|
model.EditRateId = Guid.Empty;
|
|
model.DeleteRateId = Guid.Empty;
|
|
if (!Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var gRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Global
|
|
select c);
|
|
model.AddGlobalRange(gRates.ToList());
|
|
}
|
|
if (Guid.Empty != model.ScenarioId && !Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var lRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Derived &&
|
|
c.ParentId == model.ScenarioId
|
|
select c);
|
|
model.AddLocalRange(lRates.ToList());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
model.Mode = RatesModel.FormMode.EditRate;
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
}
|
|
|
|
#endregion
|
|
|
|
break;
|
|
case RatesModel.FormMode.DeriveRate:
|
|
|
|
#region Derive rate from global
|
|
var sManager = new ScenarioManager(DbContext);
|
|
var scenario = sManager.Load(model.ScenarioId);
|
|
if (!Guid.Empty.Equals(model.ExpenditureCategoryId) && !Guid.Empty.Equals(model.ScenarioId))
|
|
{
|
|
var rates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Global &&
|
|
c.StartDate <= scenario.EndDate &&
|
|
c.EndDate >= scenario.StartDate
|
|
select c).ToList();
|
|
foreach (var rate in rates)
|
|
{
|
|
var derivedrates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.ParentId == model.ScenarioId && c.DerivedId == rate.Id
|
|
select c).ToList();
|
|
DbContext.Rates.RemoveRange(derivedrates);
|
|
var newrate = new Rate
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Rate1 = rate.Rate1,
|
|
StartDate = (rate.StartDate < scenario.StartDate) ? scenario.StartDate.Value : rate.StartDate,
|
|
EndDate = (rate.EndDate > scenario.EndDate) ? scenario.EndDate.Value : rate.EndDate,
|
|
ExpenditureCategoryId = rate.ExpenditureCategoryId,
|
|
FreezeRate = rate.FreezeRate,
|
|
ParentId = model.ScenarioId,
|
|
Type = (short)RateModel.RateType.Derived,
|
|
DerivedId = rate.Id
|
|
};
|
|
DbContext.Rates.Add(newrate);
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
model.Mode = RatesModel.FormMode.ListRates; // reset action mode
|
|
model.EditRateId = Guid.Empty;
|
|
model.DeleteRateId = Guid.Empty;
|
|
if (!Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var gRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Global
|
|
select c);
|
|
model.AddGlobalRange(gRates.ToList());
|
|
}
|
|
if (Guid.Empty != model.ScenarioId && !Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var lRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Derived &&
|
|
c.ParentId == model.ScenarioId
|
|
select c);
|
|
model.AddLocalRange(lRates.ToList());
|
|
}
|
|
|
|
#endregion
|
|
|
|
break;
|
|
case RatesModel.FormMode.ConfirmDeleteRate:
|
|
|
|
#region delete rate
|
|
|
|
if (!Guid.Empty.Equals(model.ExpenditureCategoryId) && !Guid.Empty.Equals(model.ScenarioId))
|
|
{
|
|
var deleterates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.ParentId == model.ScenarioId
|
|
select c);
|
|
if (model.DeleteRateId != null && !Guid.Empty.Equals(model.DeleteRateId))
|
|
deleterates = deleterates.Where(t => t.Id == model.DeleteRateId);
|
|
DbContext.Rates.RemoveRange(deleterates);
|
|
DbContext.SaveChanges();
|
|
}
|
|
model.DeleteRateId = Guid.Empty;
|
|
model.EditRateId = Guid.Empty;
|
|
model.Mode = RatesModel.FormMode.ListRates; // reset action mode
|
|
if (!Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var gRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Global
|
|
select c);
|
|
model.AddGlobalRange(gRates.ToList());
|
|
}
|
|
if (Guid.Empty != model.ScenarioId && !Guid.Empty.Equals(model.ExpenditureCategoryId))
|
|
{
|
|
var lRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == model.ExpenditureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Derived &&
|
|
c.ParentId == model.ScenarioId
|
|
select c);
|
|
model.AddLocalRange(lRates.ToList());
|
|
}
|
|
break;
|
|
|
|
#endregion
|
|
}
|
|
return PartialView("_rates", model);
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
model.Mode = RatesModel.FormMode.EditRate;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
model.Mode = RatesModel.FormMode.EditRate;
|
|
}
|
|
if (ModelState.IsValid && (RatesModel.FormMode.ConfirmEditRate == model.Mode || RatesModel.FormMode.ConfirmDeleteRate == model.Mode) && Guid.Empty != model.EditRateId)
|
|
ContentLocker.RemoveLock("ExpenditureCategoryRate", model.EditRateId.ToString(), User.Identity.Name);
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
return PartialView("_rates", model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns JSON UnitOfMeasure list with filters and sort for jQuery DataTables
|
|
/// </summary>
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.ScenarioTemplates, level = AccessLevel.Read)]
|
|
public JsonResult Templates(JQueryDataTablesModel jQueryDataTablesModel)
|
|
{
|
|
int totalRecordCount;
|
|
int searchRecordCount;
|
|
|
|
var units = GetTemplates(startIndex: jQueryDataTablesModel.iDisplayStart,
|
|
pageSize: jQueryDataTablesModel.iDisplayLength, sortedColumns: jQueryDataTablesModel.GetSortedColumns(),
|
|
totalRecordCount: out totalRecordCount, searchRecordCount: out searchRecordCount, searchString: jQueryDataTablesModel.sSearch);
|
|
|
|
return this.DataTablesJson(items: units,
|
|
totalRecords: totalRecordCount,
|
|
totalDisplayRecords: searchRecordCount,
|
|
sEcho: jQueryDataTablesModel.sEcho);
|
|
|
|
}
|
|
|
|
private IEnumerable<ScenarioListItemModel> GetTemplates(int startIndex,
|
|
int pageSize,
|
|
IEnumerable<SortedColumn> sortedColumns,
|
|
out int totalRecordCount,
|
|
out int searchRecordCount,
|
|
string searchString)
|
|
{
|
|
var query = from c in DbContext.Scenarios
|
|
where c.Type == (int)ScenarioType.Template
|
|
select new ScenarioListItemModel
|
|
{
|
|
Id = c.Id,
|
|
Name = c.Name,
|
|
StartDate = c.StartDate,
|
|
EndDate = c.EndDate,
|
|
Duration = c.Duration,
|
|
CGSplit = c.CGSplit,
|
|
EFXSplit = c.EFXSplit,
|
|
ScenariosCount = c.ChildScenarios.Count(),
|
|
Status = c.Status,
|
|
TemplateGroupNames = c.Template2TemplateGroup.Select(x => x.TemplateGroup.Name).OrderBy(x => x).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":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.Id);
|
|
else
|
|
query = query.OrderByDescending(c => c.Id);
|
|
break;
|
|
case "StartDate":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.StartDate);
|
|
else
|
|
query = query.OrderByDescending(c => c.StartDate);
|
|
break;
|
|
case "EndDate":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.EndDate);
|
|
else
|
|
query = query.OrderByDescending(c => c.EndDate);
|
|
break;
|
|
case "Duration":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.Duration);
|
|
else
|
|
query = query.OrderByDescending(c => c.Duration);
|
|
break;
|
|
case "CGSplit":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.CGSplit);
|
|
else
|
|
query = query.OrderByDescending(c => c.CGSplit);
|
|
break;
|
|
case "EFXSplit":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.EFXSplit);
|
|
else
|
|
query = query.OrderByDescending(c => c.EFXSplit);
|
|
break;
|
|
case "ScenariosCount":
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.ScenariosCount);
|
|
else
|
|
query = query.OrderByDescending(c => c.ScenariosCount);
|
|
break;
|
|
default:
|
|
if (sortedColumn.Direction == SortingDirection.Ascending)
|
|
query = query.OrderBy(c => c.Name);
|
|
else
|
|
query = query.OrderByDescending(c => c.Name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
totalRecordCount = DbContext.Scenarios.Count(s => s.Type == (int?)ScenarioType.Template);
|
|
searchRecordCount = query.Count();
|
|
return query.Skip(startIndex).Take(pageSize).ToList();
|
|
}
|
|
|
|
// GET: /Scenarios/
|
|
[HttpGet]
|
|
public ActionResult LoadExpenditures(Guid? id)
|
|
{
|
|
if (id == null || id == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
var manager = new ScenarioManager(DbContext);
|
|
var model = manager.GetExpenditureCategories(id.Value);
|
|
return PartialView("_expenditures", model);
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
return PartialView("_expenditures", new List<ScenarioModel.ExpenditureItem>());
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult LoadJsonScenarioCalendar(ScenarioDetailsModel model)
|
|
{
|
|
if (model == null || model.ScenarioId == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
var scenario = DbContext.Scenarios.AsNoTracking().FirstOrDefault(t => t.Id == model.ScenarioId);
|
|
if (scenario == null)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.StartDate = scenario.StartDate ?? DateTime.Today;
|
|
model.EndDate = scenario.EndDate ?? DateTime.Today.AddYears(1);
|
|
model.GrowthScenario = scenario.GrowthScenario;
|
|
model.ScenarioId = scenario.Id;
|
|
model.ParentId = scenario.ParentId ?? Guid.Empty;
|
|
model.ScenarioType = (ScenarioType?)scenario.Type;
|
|
if (scenario.Project != null)
|
|
{
|
|
model.YellowIndicator = scenario.Project.PerformanceYellowThreshold;
|
|
if (!model.YellowIndicator.HasValue)
|
|
model.YellowIndicator = scenario.Project.Type.PerformanceYellowThreshold;
|
|
model.RedIndicator = scenario.Project.PerformanceRedThreshold;
|
|
if (!model.RedIndicator.HasValue)
|
|
model.RedIndicator = scenario.Project.Type.PerformanceRedThreshold;
|
|
}
|
|
return Json(GetScenarioCalendar(model), JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
private class Pairs
|
|
{
|
|
public Guid Id { get; set; }
|
|
public decimal Quantity { get; set; }
|
|
}
|
|
|
|
[HttpPost]
|
|
public JsonResult GetMasterScenarioCalendar(MasterScenarioFindModel findModel)
|
|
{
|
|
if (findModel == null || findModel.ProjectId == Guid.Empty)
|
|
return null;
|
|
|
|
var actuals = new List<Guid>();
|
|
var actualsDetails = new List<ScenarioDetailWithProxyItemModel>();
|
|
long actualsStartDateMs = 0, actualsEndDateMs = 0;
|
|
|
|
var activeScenarios = DbContext.Scenarios.Where(x => x.Project.ParentProjectId == findModel.ProjectId &&
|
|
((x.Type == (int)ScenarioType.Portfolio && x.Status == (int)ScenarioStatus.Active) ||
|
|
(findModel.ShowActuals && x.Type == (int)ScenarioType.Actuals)))
|
|
.ToList();
|
|
|
|
var startDate = activeScenarios.Min(x => x.StartDate) ?? DateTime.UtcNow.Date;
|
|
var endDate = activeScenarios.Max(x => x.EndDate) ?? startDate.AddYears(1);
|
|
|
|
var scenarios = activeScenarios.Select(x => x.Id).ToList();
|
|
var forecast = activeScenarios.Where(x => x.Type == (int)ScenarioType.Portfolio).Select(x => x.Id).ToList();
|
|
var projects = activeScenarios.Select(x => x.ParentId ?? Guid.Empty).Distinct().ToList();
|
|
|
|
var scenarioDetails = GetScenarioDetailsProxy(null, startDate, endDate, scenarios);
|
|
var forecastDetails = scenarioDetails.Where(x => forecast.Contains(x.ScenarioId)).ToList();
|
|
|
|
if (findModel.ShowActuals)
|
|
{
|
|
actuals = activeScenarios.Where(x => x.Type == (int)ScenarioType.Actuals).Select(x => x.Id).ToList();
|
|
actualsDetails = scenarioDetails.Where(x => actuals.Contains(x.ScenarioId)).ToList();
|
|
actualsStartDateMs = (long)(activeScenarios.Where(x => x.Type == (int)ScenarioType.Actuals && x.StartDate.HasValue).Min(x => x.StartDate) ?? Constants.UnixEpochDate).Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
actualsEndDateMs = (long)(activeScenarios.Where(x => x.Type == (int)ScenarioType.Actuals && x.EndDate.HasValue).Max(x => x.EndDate) ?? Constants.UnixEpochDate).Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
}
|
|
|
|
var calendar = GetCalendar(forecastDetails, actualsDetails, scenarios, projects, findModel.IsUOMHours, actualsStartDateMs, actualsEndDateMs, findModel.ShowActuals);
|
|
|
|
var model = new MasterScenarioCalendarModel()
|
|
{
|
|
StartDate = (long)startDate.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
EndDate = (long)endDate.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
Headers = calendar.Headers,
|
|
ScenarioCalendar = calendar.Rows
|
|
};
|
|
return Json(model);
|
|
}
|
|
|
|
private ScenarioDetailsModel GetScenarioCalendar(ScenarioDetailsModel model)
|
|
{
|
|
DateTime periodStartDate;
|
|
DateTime periodEndDate;
|
|
long actualsStartDate = 0;
|
|
long actualsEndDate = 0;
|
|
Guid actualScenarioId = Guid.Empty;
|
|
var allExpCatsDict = DbContext.ExpenditureCategory.Include(x => x.Expenditure).AsNoTracking().ToDictionary(x => x.Id);
|
|
var allUomsDict = DbContext.UOMs.AsNoTracking().ToDictionary(x => x.Id);
|
|
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetID()));
|
|
if (!model.IsUOMHours.HasValue && user != null)
|
|
{
|
|
model.IsUOMHours = !user.PreferredResourceAllocation;
|
|
}
|
|
if (!model.PreferredTotalsDisplaying.HasValue && user != null)
|
|
{
|
|
model.PreferredTotalsDisplaying = user.PreferredTotalsDisplaying;
|
|
}
|
|
|
|
switch (model.ScenarioType)
|
|
{
|
|
case ScenarioType.LoanOut:
|
|
case ScenarioType.Training:
|
|
case ScenarioType.Vacation:
|
|
periodStartDate = new DateTime(DateTime.Today.Year, 1, 1);
|
|
periodEndDate = new DateTime(DateTime.Today.Year, 12, 31);
|
|
break;
|
|
default:
|
|
periodStartDate = model.StartDate;
|
|
periodEndDate = model.EndDate;
|
|
break;
|
|
}
|
|
if (!model.GrowthScenario && !ScenarioType.Actuals.Equals(model.ScenarioType))
|
|
{
|
|
var actualScenario = DbContext.Scenarios.FirstOrDefault(
|
|
t => t.ParentId == model.ParentId && t.Type == (int?)ScenarioType.Actuals);
|
|
if (actualScenario != null)
|
|
{
|
|
actualScenarioId = actualScenario.Id;
|
|
actualsStartDate = (long)(actualScenario.StartDate ?? Constants.UnixEpochDate).Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
actualsEndDate = (long)(actualScenario.EndDate ?? Constants.UnixEpochDate).Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
if (actualScenario.StartDate < model.StartDate)
|
|
periodStartDate = actualScenario.StartDate ?? Constants.UnixEpochDate;
|
|
if (actualScenario.EndDate > model.EndDate)
|
|
periodEndDate = actualScenario.EndDate ?? Constants.UnixEpochDate;
|
|
}
|
|
}
|
|
|
|
#region Load scenario Data
|
|
|
|
var filter = new ScenarioCalendarFilterModel()
|
|
{
|
|
CreditDepartment = model.CreditDepartment,
|
|
GLAccount = model.GLAccount,
|
|
LaborMaterials = model.LaborMaterials.HasValue ? model.LaborMaterials.Value.ToString() : null,
|
|
CategoryType = model.CategoryType.HasValue ? model.CategoryType.Value.ToString() : null,
|
|
IncomeType = model.IncomeType,
|
|
SelectedExpCats = model.SelectedExpCats
|
|
};
|
|
|
|
List<ScenarioDetailWithProxyItemModel> scenarioDetailsList = GetScenarioDetailsProxy(filter, periodStartDate, periodEndDate, new List<Guid> { model.ScenarioId });
|
|
List<ScenarioDetailWithProxyItemModel> actualsDetailsList = null;
|
|
if (model.ShowActuals && !Guid.Empty.Equals(actualScenarioId))
|
|
actualsDetailsList = GetScenarioDetailsProxy(filter, periodStartDate, periodEndDate, new List<Guid> { actualScenarioId });
|
|
|
|
#endregion
|
|
|
|
var calendar = GetCalendar(scenarioDetailsList, actualsDetailsList, new List<Guid> { model.ScenarioId }, new List<Guid>() { model.ParentId }, model.IsUOMHours ?? false, actualsStartDate, actualsEndDate, model.ShowActuals, model.PreferredTotalsDisplaying);
|
|
model.Headers = calendar.Headers;
|
|
model.ScenarioCalendar = calendar.Rows;
|
|
model.AllResources = calendar.AllResources;
|
|
model.ActualsStartDateMs = actualsStartDate;
|
|
model.ActualsEndDateMs = actualsEndDate;
|
|
|
|
return model;
|
|
}
|
|
|
|
private List<ScenarioDetailWithProxyItemModel> GetScenarioDetailsProxy(ScenarioCalendarFilterModel model, DateTime startDate, DateTime endDate, IEnumerable<Guid> scenarioList)
|
|
{
|
|
// get the last week even if scenario ends in the middle of the week. E.g. Scenario.EndDate=12/31 and week.EndDate=1/1
|
|
var details = (new ScenarioManager(DbContext)).GetScenarioProxyDetails(model, startDate, endDate.AddDays(6), scenarioList);
|
|
return details.Select(x => (ScenarioDetailWithProxyItemModel)x).ToList();
|
|
}
|
|
|
|
private CalendarModel GetCalendar(List<ScenarioDetailWithProxyItemModel> forecastDetailsList, List<ScenarioDetailWithProxyItemModel> actualsDetailsList, List<Guid> scenarios, List<Guid> projects, bool isUOMHours, long actualsStartDateMs = 0, long actualsEndDateMs = 0, bool showActuals = false, bool? showAvg = null)
|
|
{
|
|
var calendar = new CalendarModel();
|
|
if (forecastDetailsList == null)
|
|
return calendar;
|
|
|
|
var allExpCats = DbContext.ExpenditureCategory.Include(x => x.Expenditure).AsNoTracking().ToDictionary(x => x.Id);
|
|
var allUoms = DbContext.UOMs.AsNoTracking().ToDictionary(x => x.Id);
|
|
|
|
return GetCalendar(forecastDetailsList, actualsDetailsList, allExpCats, allUoms, scenarios, projects, isUOMHours, actualsStartDateMs, actualsEndDateMs, showActuals, showAvg);
|
|
}
|
|
|
|
private CalendarModel GetCalendar(List<ScenarioDetailWithProxyItemModel> forecastDetailsList,
|
|
List<ScenarioDetailWithProxyItemModel> actualsDetailsList,
|
|
Dictionary<Guid, ExpenditureCategory> expCategories,
|
|
Dictionary<Guid, UOM> uoms,
|
|
List<Guid> scenarios, List<Guid> projects, bool isUOMHours,
|
|
long actualsStartDateMs = 0, long actualsEndDateMs = 0,
|
|
bool showActuals = false, bool? showAvg = null)
|
|
{
|
|
var calendar = new CalendarModel();
|
|
if (expCategories == null || uoms == null)
|
|
return calendar;
|
|
|
|
if (forecastDetailsList == null)
|
|
forecastDetailsList = new List<ScenarioDetailWithProxyItemModel>();
|
|
|
|
if (actualsDetailsList == null)
|
|
actualsDetailsList = new List<ScenarioDetailWithProxyItemModel>();
|
|
|
|
if (!showAvg.HasValue)
|
|
{
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetID()));
|
|
if (user != null)
|
|
{
|
|
showAvg = user.PreferredTotalsDisplaying;
|
|
}
|
|
if (!showAvg.HasValue)
|
|
{
|
|
showAvg = true;
|
|
}
|
|
}
|
|
|
|
// SA. ENV-839
|
|
var expCatsEx = DbContext.VW_ExpenditureCategory.Where(x => expCategories.Keys.Contains(x.Id)).ToDictionary(y => y.Id);
|
|
|
|
var weekEndingDates = new List<DateTime>();
|
|
if (showActuals && actualsDetailsList.Count > 0)
|
|
weekEndingDates = forecastDetailsList.Union(actualsDetailsList).Select(t => Constants.UnixEpochDate.AddMilliseconds(t.WeekEndingDate)).Distinct().OrderBy(o => o).ToList();
|
|
else
|
|
weekEndingDates = forecastDetailsList.Select(t => Constants.UnixEpochDate.AddMilliseconds(t.WeekEndingDate)).Distinct().OrderBy(o => o).ToList();
|
|
|
|
calendar.Headers = BuildHeaders(weekEndingDates);
|
|
|
|
#region Forecast data
|
|
|
|
var scenarioDetails = forecastDetailsList.GroupBy(x => x.ExpenditureCategoryId).ToDictionary(key => key.Key, grouping => grouping.ToList());
|
|
if (scenarioDetails.Count > 0)
|
|
{
|
|
var totalRow = new ScenarioDetailsModel.ScenarioCalendarRow
|
|
{
|
|
ExpCatId = Guid.Empty,
|
|
ExpCatName = "Totals",
|
|
GrandTotalCost = 0,
|
|
GrandTotalQuantity = 0,
|
|
ScenarioDetailIds = new Guid[calendar.Headers.Count],
|
|
CostValues = new decimal[calendar.Headers.Count],
|
|
QuantityValues = new decimal[calendar.Headers.Count],
|
|
UseType = ExpenditureCategoryModel.UseTypes.Calculated
|
|
};
|
|
|
|
var resourcesByTeams = DbContext.Team2Project.Where(x => projects.Contains(x.ProjectId)).
|
|
SelectMany(x => x.Team.PeopleResources).OrderBy(r => r.LastName);
|
|
foreach (var teamResource in resourcesByTeams)
|
|
{
|
|
if (calendar.AllResources.Any(x => x.Id == teamResource.Id))
|
|
continue;
|
|
|
|
calendar.AllResources.Add(new ScenarioDetailsModel.ScenarioCalendarRowResource()
|
|
{
|
|
Id = teamResource.Id,
|
|
Name = teamResource.FirstName + " " + teamResource.LastName,
|
|
ExpedentureCategoryId = teamResource.ExpenditureCategoryId,
|
|
IsActiveEmployee = teamResource.IsActiveEmployee
|
|
});
|
|
}
|
|
var allResIds = calendar.AllResources.Select(t => t.Id).Distinct().ToArray();
|
|
var allResourceVacations = DbContext.PeopleResourceVacations.Where(t => allResIds.Contains(t.PeopleResourceId)).Select(t => new
|
|
{
|
|
t.PeopleResourceId,
|
|
t.HoursOff,
|
|
t.WeekEndingDate
|
|
}).ToArray();
|
|
|
|
var allResourceTrainings = DbContext.PeopleResourceTrainings.Where(t => allResIds.Contains(t.PeopleResourceId)).Select(t => new
|
|
{
|
|
t.PeopleResourceId,
|
|
t.HoursOff,
|
|
t.WeekEndingDate
|
|
}).ToArray();
|
|
|
|
var resourceAllocation = DbContext.PeopleResourceAllocations.Where(r => scenarios.Contains(r.ScenarioId)).ToList();
|
|
|
|
foreach (var row in scenarioDetails)
|
|
{
|
|
var expenditureCategory = expCatsEx[row.Key];
|
|
var expCatRow = new ScenarioDetailsModel.ScenarioCalendarRow
|
|
{
|
|
ExpCatId = row.Key,
|
|
ExpCatName = expenditureCategory.ExpCategoryWithCcName, // SA. ENV-756. ENV-839
|
|
GrandTotalCost = 0,
|
|
GrandTotalQuantity = 0,
|
|
ScenarioDetailIds = new Guid[calendar.Headers.Count],
|
|
CostValues = new decimal[calendar.Headers.Count],
|
|
QuantityValues = new decimal[calendar.Headers.Count],
|
|
UseType = (ExpenditureCategoryModel.UseTypes)expenditureCategory.UseType,
|
|
Resources = calendar.AllResources.Where(x => resourceAllocation.Where(ec => ec.ExpenditureCategoryId == row.Key).
|
|
Select(r => r.PeopleResourceId).Contains(x.Id)).
|
|
Select(x => new ScenarioDetailsModel.ScenarioCalendarRowResource()
|
|
{
|
|
Id = x.Id,
|
|
Name = x.Name,
|
|
QuantityValues = new decimal[calendar.Headers.Count],
|
|
CostValues = new decimal[calendar.Headers.Count],
|
|
CapacityQuantityValues = new decimal[calendar.Headers.Count],
|
|
}).ToList(),
|
|
RestQuantity = new decimal[calendar.Headers.Count],
|
|
RestCost = new decimal[calendar.Headers.Count],
|
|
ActualsCosts = new decimal[calendar.Headers.Count],
|
|
ActualsQuantities = new decimal[calendar.Headers.Count]
|
|
};
|
|
var uomMultiplier = Utils.GetUOMMultiplier(expCategories, uoms, row.Key, isUOMHours);
|
|
var uomValue = uoms.FirstOrDefault(t => t.Key == expenditureCategory.UOMId);
|
|
|
|
var monthCost = 0.0M;
|
|
var monthQuantity = 0.0M;
|
|
var resourceTotals = calendar.AllResources.Select(x => new Pairs() { Id = x.Id, Quantity = 0.0M }).ToList();
|
|
|
|
var rowValues = row.Value.GroupBy(x => Constants.UnixEpochDate.AddMilliseconds(x.WeekEndingDate)).ToDictionary(x => x.Key, g => g.ToList());
|
|
foreach (var weekEnding in weekEndingDates)
|
|
{
|
|
var values = rowValues.ContainsKey(weekEnding) ? rowValues[weekEnding] : new List<ScenarioDetailWithProxyItemModel>();
|
|
|
|
var colIndex = calendar.Headers.FindIndex(x => x.Milliseconds == weekEnding.Subtract(Constants.UnixEpochDate).TotalMilliseconds);
|
|
if (colIndex < 0)
|
|
continue;
|
|
|
|
// need to skip all forecast data befor actuals end date
|
|
if (showActuals && actualsStartDateMs > 0 && actualsEndDateMs > 0 &&
|
|
calendar.Headers[colIndex].Milliseconds <= actualsEndDateMs)
|
|
{
|
|
// skip actuals dates, because this cells will be populated later
|
|
expCatRow.CostValues[colIndex] = 0;
|
|
expCatRow.QuantityValues[colIndex] = 0;
|
|
expCatRow.ForecastQtyTotalInActualsRange += values.Sum(x => x.Quantity) * uomMultiplier;
|
|
}
|
|
else
|
|
{
|
|
// needs only for single scenario calendar
|
|
if (values.Count == 1)
|
|
expCatRow.ScenarioDetailIds[colIndex] = values.First().Id;
|
|
|
|
expCatRow.CostValues[colIndex] = values.Sum(x => x.Cost);
|
|
expCatRow.QuantityValues[colIndex] = values.Sum(x => x.Quantity) * uomMultiplier;
|
|
}
|
|
|
|
//Get resources cost\quantity
|
|
var currAllocation = resourceAllocation.Where(r => r.WeekEndingDate == weekEnding && r.ExpenditureCategoryId == row.Key).ToList();
|
|
expCatRow.Resources.ForEach(x =>
|
|
{
|
|
// set resource weekly allocation
|
|
x.QuantityValues[colIndex] = currAllocation.Where(ar => ar.PeopleResourceId == x.Id).Sum(ca => ca.Quantity ?? 0) * uomMultiplier;
|
|
|
|
// set resource weekly availability (UOM.Value - vacations - loan offs - trainings)
|
|
if (uomValue.Value != null)
|
|
{
|
|
var vacationsSum = allResourceVacations.Where(t =>
|
|
t.PeopleResourceId == x.Id &&
|
|
t.WeekEndingDate <= weekEnding && t.WeekEndingDate >= weekEnding.AddDays(-6))
|
|
.Sum(s => s.HoursOff);
|
|
var trainingsSum = allResourceTrainings.Where(t =>
|
|
t.PeopleResourceId == x.Id &&
|
|
t.WeekEndingDate <= weekEnding && t.WeekEndingDate >= weekEnding.AddDays(-6))
|
|
.Sum(s => s.HoursOff);
|
|
x.CapacityQuantityValues[colIndex] = (uomValue.Value.UOMValue - vacationsSum - trainingsSum) * uomMultiplier;
|
|
}
|
|
});
|
|
|
|
expCatRow.RestQuantity[colIndex] = expCatRow.QuantityValues[colIndex] - expCatRow.Resources.Select(x => x.QuantityValues[colIndex]).Sum();
|
|
|
|
totalRow.CostValues[colIndex] += expCatRow.CostValues[colIndex];
|
|
totalRow.QuantityValues[colIndex] += expCatRow.QuantityValues[colIndex];
|
|
|
|
expCatRow.Resources.ForEach(x => resourceTotals.FirstOrDefault(r => r.Id == x.Id).Quantity += x.QuantityValues[colIndex]);
|
|
|
|
monthQuantity += expCatRow.QuantityValues[colIndex];
|
|
monthCost += expCatRow.CostValues[colIndex];
|
|
|
|
if (colIndex < calendar.Headers.Count - 1)
|
|
colIndex++;
|
|
|
|
if (colIndex >= calendar.Headers.Count - 1 || calendar.Headers[colIndex].IsMonth)
|
|
{
|
|
expCatRow.ScenarioDetailIds[colIndex] = Guid.Empty;
|
|
expCatRow.CostValues[colIndex] = monthCost;
|
|
|
|
if (showAvg.Value && !isUOMHours)
|
|
{
|
|
expCatRow.QuantityValues[colIndex] = Math.Round(monthQuantity / calendar.Headers[colIndex].Weeks.Count(), 3);
|
|
}
|
|
else
|
|
{
|
|
expCatRow.QuantityValues[colIndex] = monthQuantity;
|
|
}
|
|
|
|
totalRow.ScenarioDetailIds[colIndex] = Guid.Empty;
|
|
|
|
if (showAvg.Value && !isUOMHours)
|
|
{
|
|
//var avgCost =Math.Round(monthCost/ calendar.Headers[colIndex].Weeks.Count(), 3);
|
|
var avgQuantity = Math.Round(monthQuantity / calendar.Headers[colIndex].Weeks.Count(), 3);
|
|
//expCatRow.GrandTotalCost += avgCost;
|
|
expCatRow.GrandTotalQuantity += avgQuantity;
|
|
expCatRow.Resources.ForEach(x => x.QuantityValues[colIndex] = Math.Round(resourceTotals.FirstOrDefault(r => r.Id == x.Id).Quantity / calendar.Headers[colIndex].Weeks.Count(), 3));
|
|
//totalRow.CostValues[colIndex] += avgCost;
|
|
totalRow.QuantityValues[colIndex] += avgQuantity;
|
|
|
|
}
|
|
else
|
|
{
|
|
expCatRow.GrandTotalQuantity += monthQuantity;
|
|
expCatRow.Resources.ForEach(x => x.QuantityValues[colIndex] = resourceTotals.FirstOrDefault(r => r.Id == x.Id).Quantity);
|
|
totalRow.QuantityValues[colIndex] += monthQuantity;
|
|
}
|
|
expCatRow.Resources.ForEach(x => x.GrandTotalQuantity += (x.QuantityValues[colIndex]));
|
|
|
|
expCatRow.GrandTotalCost += monthCost;
|
|
totalRow.CostValues[colIndex] += monthCost;
|
|
|
|
monthCost = 0.0M;
|
|
monthQuantity = 0.0M;
|
|
resourceTotals.ForEach(x => x.Quantity = 0.0M);
|
|
}
|
|
}
|
|
calendar.Rows.Add(expCatRow);
|
|
totalRow.GrandTotalCost += expCatRow.GrandTotalCost;
|
|
totalRow.GrandTotalQuantity += expCatRow.GrandTotalQuantity;
|
|
}
|
|
|
|
if (showAvg.Value && !isUOMHours)
|
|
{
|
|
var monthsCount = calendar.Headers.Count(x => x.IsMonth);
|
|
calendar.Rows.ForEach(
|
|
x => x.GrandTotalQuantity = Math.Round(x.GrandTotalQuantity / monthsCount, 3));
|
|
totalRow.GrandTotalQuantity = 0;
|
|
calendar.Rows.ForEach(x =>
|
|
totalRow.GrandTotalQuantity += x.GrandTotalQuantity);
|
|
|
|
calendar.Rows.ForEach(x =>
|
|
x.Resources.ForEach(r => r.GrandTotalQuantity = Math.Round(r.GrandTotalQuantity / monthsCount, 3)));
|
|
}
|
|
calendar.Rows = calendar.Rows.OrderBy(x => x.ExpCatName).ToList();
|
|
calendar.Rows.Insert(0, totalRow);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Load Actuals
|
|
|
|
if (showActuals && actualsDetailsList != null && actualsDetailsList.Count > 0)
|
|
{
|
|
var weekCells = calendar.Headers.Where(x => !x.IsMonth).Count();
|
|
var totalRow = calendar.Rows.FirstOrDefault(t => t.ExpCatId == Guid.Empty);
|
|
totalRow.ActualsCosts = new decimal[calendar.Headers.Count];
|
|
totalRow.ActualsQuantities = new decimal[calendar.Headers.Count];
|
|
var actualsDetails = actualsDetailsList.GroupBy(x => x.ExpenditureCategoryId).ToDictionary(x => x.Key);
|
|
foreach (var row in actualsDetails)
|
|
{
|
|
var expenditureCategory = expCatsEx[row.Key];
|
|
var expCatRow = calendar.Rows.FirstOrDefault(t => t.ExpCatId == row.Key);
|
|
if (expCatRow == null)
|
|
{
|
|
expCatRow = new ScenarioDetailsModel.ScenarioCalendarRow
|
|
{
|
|
ExpCatId = row.Key,
|
|
ExpCatName = expenditureCategory.ExpCategoryWithCcName, // SA. ENV-756. ENV-839
|
|
GrandTotalCost = 0,
|
|
GrandTotalQuantity = 0,
|
|
ScenarioDetailIds = new Guid[calendar.Headers.Count],
|
|
CostValues = new decimal[calendar.Headers.Count],
|
|
QuantityValues = new decimal[calendar.Headers.Count],
|
|
UseType = (ExpenditureCategoryModel.UseTypes)expenditureCategory.UseType,
|
|
Resources = new List<ScenarioDetailsModel.ScenarioCalendarRowResource>(),
|
|
RestQuantity = new decimal[calendar.Headers.Count],
|
|
RestCost = new decimal[calendar.Headers.Count],
|
|
ActualsCosts = new decimal[calendar.Headers.Count],
|
|
ActualsQuantities = new decimal[calendar.Headers.Count]
|
|
};
|
|
calendar.Rows.Add(expCatRow);
|
|
}
|
|
|
|
var uomMultiplier = Utils.GetUOMMultiplier(expCategories, uoms, row.Key, isUOMHours);
|
|
|
|
var rowValues = row.Value.GroupBy(x => Constants.UnixEpochDate.AddMilliseconds(x.WeekEndingDate)).ToDictionary(x => x.Key, g => g.ToList());
|
|
foreach (var weekEnding in weekEndingDates)
|
|
{
|
|
var values = rowValues.ContainsKey(weekEnding) ? rowValues[weekEnding] : new List<ScenarioDetailWithProxyItemModel>();
|
|
|
|
var colIndex = -1;
|
|
var monthIndex = -1;
|
|
var msEnd = weekEnding.Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
var msStart = msEnd - (6 * 24 * 60 * 60 * 1000);
|
|
ScenarioDetailsModel.Header header = null;
|
|
for (var i = 0; i < calendar.Headers.Count; i++)
|
|
{
|
|
if (!calendar.Headers[i].IsMonth && calendar.Headers[i].Milliseconds > msStart &&
|
|
calendar.Headers[i].Milliseconds <= msEnd && colIndex < 0)
|
|
{
|
|
header = calendar.Headers[i];
|
|
colIndex = i;
|
|
}
|
|
if (colIndex >= 0 && calendar.Headers[i].IsMonth && monthIndex < 0)
|
|
{
|
|
monthIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (header == null)
|
|
continue;
|
|
|
|
var oldCost = expCatRow.ActualsCosts[colIndex];
|
|
var oldQty = expCatRow.ActualsQuantities[colIndex];
|
|
|
|
expCatRow.ActualsCosts[colIndex] = values.Sum(x => x.Cost);
|
|
expCatRow.ActualsQuantities[colIndex] = (values.Sum(x => x.Quantity)) * uomMultiplier;
|
|
|
|
var monthQuantityDelta = (expCatRow.ActualsQuantities[colIndex] - oldQty);
|
|
var monthCostDelta = (expCatRow.ActualsCosts[colIndex] - oldCost);
|
|
|
|
totalRow.ActualsCosts[colIndex] += monthCostDelta;
|
|
totalRow.ActualsQuantities[colIndex] += monthQuantityDelta;
|
|
if (monthIndex > 0)
|
|
{
|
|
expCatRow.ActualsCosts[monthIndex] += monthCostDelta;
|
|
expCatRow.ActualsQuantities[monthIndex] += monthQuantityDelta;
|
|
|
|
totalRow.ActualsCosts[monthIndex] += monthCostDelta;
|
|
totalRow.ActualsQuantities[monthIndex] += monthQuantityDelta;
|
|
}
|
|
}
|
|
var catTotalActualsQty = 0.0M;
|
|
for (var i = 0; i < calendar.Headers.Count; i++)
|
|
{
|
|
if (!calendar.Headers[i].IsMonth && calendar.Headers[i].Milliseconds >= actualsStartDateMs &&
|
|
calendar.Headers[i].Milliseconds <= actualsEndDateMs)
|
|
{
|
|
catTotalActualsQty += expCatRow.ActualsQuantities[i];
|
|
}
|
|
}
|
|
expCatRow.ForecastCompletedPercent = expCatRow.ForecastQtyTotalInActualsRange == 0 ? 0 : Math.Abs(catTotalActualsQty / expCatRow.ForecastQtyTotalInActualsRange - 1.0M);
|
|
|
|
#region replace forecast data with actuals
|
|
|
|
var lastMonthRecalculated = false;
|
|
for (var i = 0; i < calendar.Headers.Count; i++)
|
|
{
|
|
if (!calendar.Headers[i].IsMonth)
|
|
{
|
|
// modify weekly data
|
|
// need to replace with actuals data all forecast befor actuals end date
|
|
if (showActuals && actualsStartDateMs > 0 && actualsEndDateMs > 0 &&
|
|
calendar.Headers[i].Milliseconds <= actualsEndDateMs)
|
|
{
|
|
// repalce forecast data with actuals if week is in actuals range
|
|
var costDelta = expCatRow.ActualsCosts[i] - expCatRow.CostValues[i];
|
|
var qtyDelta = expCatRow.ActualsQuantities[i] - expCatRow.QuantityValues[i];
|
|
expCatRow.QuantityValues[i] += qtyDelta;
|
|
expCatRow.CostValues[i] += costDelta;
|
|
|
|
totalRow.QuantityValues[i] += qtyDelta;
|
|
totalRow.CostValues[i] += costDelta;
|
|
}
|
|
else if (lastMonthRecalculated)
|
|
{
|
|
// break loop if all weeks within actuals date range have been updated
|
|
// and month on the right border of the range has been recalculated
|
|
break;
|
|
}
|
|
// indicate that new month has been started so we will need to recalculate it
|
|
lastMonthRecalculated = false;
|
|
}
|
|
else
|
|
{
|
|
var monthlyQuantity = 0.0M;
|
|
var monthlyCost = 0.0M;
|
|
// recalculate month
|
|
var qtyDelta = 0.0M;
|
|
foreach (var t in calendar.Headers[i].Weeks)
|
|
{
|
|
monthlyQuantity += expCatRow.QuantityValues[t];
|
|
monthlyCost += expCatRow.CostValues[t];
|
|
}
|
|
if (showAvg.Value && !isUOMHours)
|
|
qtyDelta = Math.Round(monthlyQuantity / calendar.Headers[i].Weeks.Count, 3) - expCatRow.QuantityValues[i];
|
|
else
|
|
qtyDelta = monthlyQuantity - expCatRow.QuantityValues[i];
|
|
var costDelta = monthlyCost - expCatRow.CostValues[i];
|
|
|
|
expCatRow.QuantityValues[i] += qtyDelta;
|
|
expCatRow.CostValues[i] += costDelta;
|
|
expCatRow.GrandTotalCost += costDelta;
|
|
expCatRow.GrandTotalQuantity += qtyDelta;
|
|
|
|
totalRow.CostValues[i] += costDelta;
|
|
totalRow.QuantityValues[i] += qtyDelta;
|
|
totalRow.GrandTotalCost += costDelta;
|
|
totalRow.GrandTotalQuantity += qtyDelta;
|
|
// indicate that month has been recalculated
|
|
lastMonthRecalculated = true;
|
|
}
|
|
}
|
|
if (showAvg.Value && !isUOMHours)
|
|
{
|
|
expCatRow.GrandTotalQuantity = 0;
|
|
for (int i = 0; i < calendar.Headers.Count; i++)
|
|
{
|
|
if (calendar.Headers[i].IsMonth)
|
|
continue;
|
|
expCatRow.GrandTotalQuantity += expCatRow.QuantityValues[i];
|
|
}
|
|
expCatRow.GrandTotalQuantity = Math.Round(expCatRow.GrandTotalQuantity / weekCells);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
if (showAvg.Value && !isUOMHours)
|
|
{
|
|
totalRow.GrandTotalQuantity = 0;
|
|
for (int i = 0; i < calendar.Headers.Count; i++)
|
|
{
|
|
if (calendar.Headers[i].IsMonth)
|
|
continue;
|
|
totalRow.GrandTotalQuantity += totalRow.QuantityValues[i];
|
|
}
|
|
totalRow.GrandTotalQuantity = Math.Round(totalRow.GrandTotalQuantity / weekCells);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
return calendar;
|
|
}
|
|
|
|
private List<ScenarioDetailsModel.Header> BuildHeaders(List<DateTime> gridHeaders)
|
|
{
|
|
var headers = new List<ScenarioDetailsModel.Header>((int)(gridHeaders.Count * 1.25));
|
|
var prevMonth = string.Empty;
|
|
var monthIndex = -1;
|
|
ScenarioDetailsModel.Header monthColumn = null;
|
|
foreach (var gridHeader in gridHeaders)
|
|
{
|
|
// get week start date as previous week end date + 1 day
|
|
//var weekStartDate = prevWeek.AddDays(1);
|
|
// if there is a gap between weeks (e.g. there is a non-working week that was not represented in scenario details records)
|
|
// then we should subtract 6 days from current week end date
|
|
//if (gridHeader.AddDays(-6) > weekStartDate)
|
|
// weekStartDate = gridHeader.AddDays(-6);
|
|
// get month name as month of the week start date
|
|
var gridHeaderTitle = gridHeader.ToString("MMMM yyyy");
|
|
if (!prevMonth.Equals(gridHeaderTitle))
|
|
{
|
|
if (monthColumn != null)
|
|
{
|
|
headers.Add(monthColumn);
|
|
}
|
|
monthColumn = new ScenarioDetailsModel.Header()
|
|
{
|
|
Show = true,
|
|
Milliseconds = (long)gridHeader.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
MonthIndex = ++monthIndex,
|
|
IsMonth = true,
|
|
Title = gridHeaderTitle,
|
|
Weeks = new List<int>()
|
|
};
|
|
}
|
|
|
|
var weekHeader = new ScenarioDetailsModel.Header()
|
|
{
|
|
IsMonth = false,
|
|
Milliseconds = (long)gridHeader.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
MonthIndex = monthColumn.MonthIndex,
|
|
Title = gridHeader.ToShortDateString()
|
|
};
|
|
headers.Add(weekHeader);
|
|
monthColumn.Weeks.Add(headers.Count - 1);
|
|
monthColumn.Milliseconds = weekHeader.Milliseconds;
|
|
|
|
prevMonth = gridHeaderTitle;
|
|
}
|
|
if (monthColumn != null)
|
|
{
|
|
headers.Add(monthColumn);
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
private Dictionary<string, List<ScenarioDetailsModel.HeaderBase>>
|
|
BuildHeadersEx(List<DateTime> gridHeaders)
|
|
{
|
|
var headers = new Dictionary<string, List<ScenarioDetailsModel.HeaderBase>>();
|
|
headers.Add(ScenarioDetailsModel.HeaderPeriod.Month, new List<ScenarioDetailsModel.HeaderBase>());
|
|
headers.Add(ScenarioDetailsModel.HeaderPeriod.Week, new List<ScenarioDetailsModel.HeaderBase>());
|
|
|
|
List<ScenarioDetailsModel.HeaderBase> monthHeaders = headers[ScenarioDetailsModel.HeaderPeriod.Month];
|
|
List<ScenarioDetailsModel.HeaderBase> weekHeaders = headers[ScenarioDetailsModel.HeaderPeriod.Week];
|
|
|
|
var prevMonth = string.Empty;
|
|
var monthIndex = -1;
|
|
ScenarioDetailsModel.MonthHeader lastMonthColumn = null;
|
|
ScenarioDetailsModel.WeekHeader totalsHeader = null;
|
|
|
|
foreach (var gridHeader in gridHeaders)
|
|
{
|
|
// get week start date as previous week end date + 1 day
|
|
//var weekStartDate = prevWeek.AddDays(1);
|
|
// if there is a gap between weeks (e.g. there is a non-working week that was not represented in scenario details records)
|
|
// then we should subtract 6 days from current week end date
|
|
//if (gridHeader.AddDays(-6) > weekStartDate)
|
|
// weekStartDate = gridHeader.AddDays(-6);
|
|
// get month name as month of the week start date
|
|
var gridHeaderTitle = gridHeader.ToString("MMMM yyyy");
|
|
|
|
if (!prevMonth.Equals(gridHeaderTitle))
|
|
{
|
|
if (totalsHeader != null)
|
|
{
|
|
weekHeaders.Add(totalsHeader);
|
|
lastMonthColumn.TotalsHeader = weekHeaders.Count - 1;
|
|
}
|
|
|
|
// Create month new column
|
|
lastMonthColumn = new ScenarioDetailsModel.MonthHeader()
|
|
{
|
|
Milliseconds = (long)gridHeader.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
Title = gridHeaderTitle,
|
|
IsCollapsed = true,
|
|
};
|
|
|
|
monthHeaders.Add(lastMonthColumn);
|
|
monthIndex = monthHeaders.Count - 1;
|
|
|
|
// Create month totals column for the prev month
|
|
totalsHeader = new ScenarioDetailsModel.WeekHeader()
|
|
{
|
|
DataType = ScenarioDetailsModel.WeekHeaderType.Totals,
|
|
Milliseconds = lastMonthColumn.Milliseconds,
|
|
MonthHeader = monthIndex,
|
|
};
|
|
}
|
|
|
|
var weekHeader = new ScenarioDetailsModel.WeekHeader()
|
|
{
|
|
Milliseconds = (long)gridHeader.Subtract(Constants.UnixEpochDate).TotalMilliseconds,
|
|
Title = gridHeader.ToShortDateString(),
|
|
MonthHeader = monthIndex,
|
|
};
|
|
|
|
weekHeaders.Add(weekHeader);
|
|
lastMonthColumn.WeekHeaders.Add(weekHeaders.Count - 1);
|
|
// lastMonthColumn.Milliseconds = weekHeader.Milliseconds;
|
|
|
|
prevMonth = gridHeaderTitle;
|
|
}
|
|
|
|
if (totalsHeader != null)
|
|
{
|
|
weekHeaders.Add(totalsHeader);
|
|
lastMonthColumn.TotalsHeader = weekHeaders.Count - 1;
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
private Dictionary<string, List<ScenarioDetailsModel.HeaderBase>>
|
|
BuildHeadersExForScenario(List<DateTime> weekEndings, long forecastDataStartDateMs, long forecastDataEndDateMs, long actualsViewDataStartDateMs, long actualsViewDataEndDateMs, long? actualsDataStartDateMs, long? actualsDataEndDateMs)
|
|
{
|
|
if (weekEndings == null || weekEndings.Count <= 0)
|
|
return new Dictionary<string, List<ScenarioDetailsModel.HeaderBase>>();
|
|
|
|
var headers = BuildHeadersEx(weekEndings);
|
|
|
|
// SA. ENV-667. Begin
|
|
// Get week headers, that must be visible in grid forecast mode
|
|
var weekHeadersForecastVisible =
|
|
(from ScenarioDetailsModel.HeaderBase item in headers[ScenarioDetailsModel.HeaderPeriod.Week]
|
|
where ((item as ScenarioDetailsModel.WeekHeader).DataType == ScenarioDetailsModel.WeekHeaderType.Ordinal) &&
|
|
((item as ScenarioDetailsModel.WeekHeader).Milliseconds >= forecastDataStartDateMs) &&
|
|
((item as ScenarioDetailsModel.WeekHeader).Milliseconds <= forecastDataEndDateMs)
|
|
select item as ScenarioDetailsModel.WeekHeader).ToList();
|
|
|
|
// Get week headers, that must be visible in grid actuals mode
|
|
var weekHeadersActualsVisible =
|
|
(from ScenarioDetailsModel.HeaderBase item in headers[ScenarioDetailsModel.HeaderPeriod.Week]
|
|
where ((item as ScenarioDetailsModel.WeekHeader).DataType == ScenarioDetailsModel.WeekHeaderType.Ordinal) &&
|
|
((item as ScenarioDetailsModel.WeekHeader).Milliseconds >= actualsViewDataStartDateMs) &&
|
|
((item as ScenarioDetailsModel.WeekHeader).Milliseconds <= actualsViewDataEndDateMs)
|
|
select item as ScenarioDetailsModel.WeekHeader).ToList();
|
|
|
|
// Get week headers, that must be editable in grid actuals mode (default = are editable)
|
|
var weekHeadersActualsEditable =
|
|
(from ScenarioDetailsModel.HeaderBase item in headers[ScenarioDetailsModel.HeaderPeriod.Week]
|
|
where ((item as ScenarioDetailsModel.WeekHeader).DataType == ScenarioDetailsModel.WeekHeaderType.Ordinal) &&
|
|
((item as ScenarioDetailsModel.WeekHeader).Milliseconds >= actualsDataStartDateMs) &&
|
|
((item as ScenarioDetailsModel.WeekHeader).Milliseconds <= actualsDataEndDateMs)
|
|
select item as ScenarioDetailsModel.WeekHeader).ToList();
|
|
|
|
// Set week headers visibility for forecast and actuals modes. Set non editable columns
|
|
weekHeadersForecastVisible.ForEach(x => x.Visible[ScenarioDetailsModel.CalendarDataViewModes.Forecast] = true);
|
|
weekHeadersActualsVisible.ForEach(x => x.Visible[ScenarioDetailsModel.CalendarDataViewModes.Actuals] = true);
|
|
weekHeadersActualsEditable.ForEach(x => x.Editable[ScenarioDetailsModel.CalendarDataViewModes.Actuals] = false);
|
|
|
|
// Set visibility for month and totals headers
|
|
for (int monthIndex = 0; monthIndex < headers[ScenarioDetailsModel.HeaderPeriod.Month].Count; monthIndex++)
|
|
{
|
|
ScenarioDetailsModel.MonthHeader monthItem =
|
|
headers[ScenarioDetailsModel.HeaderPeriod.Month][monthIndex] as ScenarioDetailsModel.MonthHeader;
|
|
|
|
int forcastVisibleWeeksCount =
|
|
headers[ScenarioDetailsModel.HeaderPeriod.Week]
|
|
.Where(x => (x as ScenarioDetailsModel.WeekHeader).MonthHeader == monthIndex &&
|
|
(x as ScenarioDetailsModel.WeekHeader).DataType == ScenarioDetailsModel.WeekHeaderType.Ordinal &&
|
|
x.Visible[ScenarioDetailsModel.CalendarDataViewModes.Forecast]).Count();
|
|
|
|
int actualsVisibleWeeksCount =
|
|
headers[ScenarioDetailsModel.HeaderPeriod.Week]
|
|
.Where(x => (x as ScenarioDetailsModel.WeekHeader).MonthHeader == monthIndex &&
|
|
(x as ScenarioDetailsModel.WeekHeader).DataType == ScenarioDetailsModel.WeekHeaderType.Ordinal &&
|
|
x.Visible[ScenarioDetailsModel.CalendarDataViewModes.Actuals]).Count();
|
|
|
|
int actualsNonEditableWeeksCount =
|
|
headers[ScenarioDetailsModel.HeaderPeriod.Week]
|
|
.Where(x => (x as ScenarioDetailsModel.WeekHeader).MonthHeader == monthIndex &&
|
|
(x as ScenarioDetailsModel.WeekHeader).DataType == ScenarioDetailsModel.WeekHeaderType.Ordinal &&
|
|
!x.Editable[ScenarioDetailsModel.CalendarDataViewModes.Actuals]).Count();
|
|
|
|
monthItem.ColspanValues[ScenarioDetailsModel.CalendarDataViewModes.Forecast] = forcastVisibleWeeksCount;
|
|
monthItem.ColspanValues[ScenarioDetailsModel.CalendarDataViewModes.Actuals] = actualsVisibleWeeksCount;
|
|
|
|
monthItem.Visible[ScenarioDetailsModel.CalendarDataViewModes.Forecast] = (forcastVisibleWeeksCount > 0);
|
|
monthItem.Visible[ScenarioDetailsModel.CalendarDataViewModes.Actuals] = (actualsVisibleWeeksCount > 0);
|
|
monthItem.Editable[ScenarioDetailsModel.CalendarDataViewModes.Actuals] = (actualsNonEditableWeeksCount < 1);
|
|
|
|
headers[ScenarioDetailsModel.HeaderPeriod.Week][monthItem.TotalsHeader]
|
|
.Visible[ScenarioDetailsModel.CalendarDataViewModes.Forecast] = (forcastVisibleWeeksCount > 0);
|
|
|
|
headers[ScenarioDetailsModel.HeaderPeriod.Week][monthItem.TotalsHeader]
|
|
.Visible[ScenarioDetailsModel.CalendarDataViewModes.Actuals] = (actualsVisibleWeeksCount > 0);
|
|
|
|
headers[ScenarioDetailsModel.HeaderPeriod.Week][monthItem.TotalsHeader]
|
|
.Editable[ScenarioDetailsModel.CalendarDataViewModes.Actuals] = (actualsNonEditableWeeksCount < 1);
|
|
} // for (int monthIndex = 0; monthIndex < headers[...
|
|
|
|
return headers;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill then ScenarioDetailModel model inner object collections, needed to render the view
|
|
/// </summary>
|
|
/// <param name="id">Scenario id</param>
|
|
/// <param name="manager">Manager</param>
|
|
/// <remarks>SA. ENV-707</remarks>
|
|
private ScenarioDetailModel LoadScenarioDetailsModel(Guid id, ScenarioManager manager)
|
|
{
|
|
var model = (ScenarioDetailModel)manager.Load(id) ?? new ScenarioDetailModel();
|
|
if (model.CostSaving != null) model.CostSaving.CostSavingDescription = model.CostSavingsDescription;
|
|
if (model.CostSaving != null) model.CostSaving.ScenarioStartDate = model.StartDate;
|
|
if (model.Id == Guid.Empty || !model.Status.HasValue)
|
|
return model;
|
|
|
|
var actualScenario = DbContext.Scenarios.FirstOrDefault(x => x.ParentId == model.ParentId && x.Type == (int)ScenarioType.Actuals);
|
|
if (actualScenario != null)
|
|
{
|
|
var actualSplitInfo = Utils.GetLaborMaterialsSplit(actualScenario.Id);
|
|
|
|
if (actualScenario.StartDate.HasValue)
|
|
model.ActualsStartDate = (long)actualScenario.StartDate.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
if (actualScenario.EndDate.HasValue)
|
|
model.ActualsEndDate = (long)actualScenario.EndDate.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
model.ActualLabor = actualSplitInfo.Labor;
|
|
model.ActualMaterials = actualSplitInfo.Materials;
|
|
model.ActualsTotal = actualSplitInfo.Total;
|
|
model.HasActuals = actualScenario.EndDate.HasValue;
|
|
model.ActualLaborMaterialsSplit = actualSplitInfo.LaborAndMaterialSplit;
|
|
}
|
|
|
|
var forecastSplitInfo = Utils.GetLaborMaterialsSplit(model.Id);
|
|
|
|
model.Expenditures = Utils.GetScenarioExpenditures(model.Id);
|
|
|
|
model.ScenarioExpenditures = manager.GetExpenditureCategories(model.Id);
|
|
model.LaborMaterialsSplit = forecastSplitInfo.LaborAndMaterialSplit;
|
|
|
|
#region Override Actuals fields if there are no actuals data
|
|
// SA. ENV-698. Show Forecast value as Actual Bottom-Up Direct Costs, if Project has no actuals. Begin
|
|
bool actualDataExists = false;
|
|
|
|
if (actualScenario != null)
|
|
// Actual data scenario exists
|
|
actualDataExists = actualScenario.EndDate.HasValue;
|
|
|
|
model.DateForStartOfChanges = model.StartDate.HasValue && model.StartDate.Value > DateTime.Today ? model.StartDate.Value.Date : DateTime.Today;
|
|
if (!actualDataExists)
|
|
{
|
|
// Show nulls and forecast values as actuals, if actuals not found
|
|
model.BUDirectCostsActuals = model.BUDirectCosts;
|
|
model.BUDirectCostsLMActuals = model.BUDirectCostsLM;
|
|
model.BUCostsShotsActuals = model.BUCostsShots;
|
|
model.BUCostsShotsLMActuals = model.BUCostsShotsLM;
|
|
model.CalculatedGrossMarginActuals = null;
|
|
model.CalculatedGrossMarginLMActuals = null;
|
|
model.ActualRevenueAfterCost = null;
|
|
model.ActualLabor = null;
|
|
model.ActualLaborMaterialsSplit = null;
|
|
model.ActualMaterials = null;
|
|
}
|
|
else
|
|
{
|
|
// date for start of changes should be bigger than actuals end date
|
|
if (actualScenario.EndDate >= model.DateForStartOfChanges)
|
|
model.DateForStartOfChanges = actualScenario.EndDate.Value.AddDays(1);
|
|
}
|
|
// SA. ENV-698. End
|
|
#endregion
|
|
|
|
return model;
|
|
}
|
|
private ScenarioDetailModel LoadScenarioDetailsModel(CreateScenarioModel.GeneralInfoModel sm)
|
|
{
|
|
var parentId = sm.PartId ?? sm.ProjectId;
|
|
if (null == parentId || Guid.Empty.Equals(parentId))
|
|
throw new BLLException("We should have a project or part to create a scenario");
|
|
|
|
ScenarioDetailModel model = null;
|
|
model = new ScenarioDetailModel
|
|
{
|
|
ParentId = parentId,
|
|
Name = sm.ScenarioName,
|
|
GrowthScenario = sm.GrowthScenario,
|
|
//UseLMMargin = sm.Step3.UseLMMargin,
|
|
//GrossMargin = sm.Step3.Margin != null ? (int) sm.Step3.Margin : 0,
|
|
TemplateId = sm.TemplateId ?? Guid.Empty,
|
|
StartDate = sm.StartDate,
|
|
EndDate = sm.EndDate,
|
|
//IsRevenueGenerating = sm.Project.IsRevenueGenerating,
|
|
Id = sm.ScenarioId,
|
|
ScenarioExpenditures = null != sm.ScenarioExpenditures ? sm.ScenarioExpenditures.Where(t => t.Checked).ToList() : null,
|
|
};
|
|
if (!Guid.Empty.Equals(model.ParentId))
|
|
{
|
|
var actualScenario = DbContext.Scenarios.FirstOrDefault(x => x.ParentId == model.ParentId && x.Type == (int)ScenarioType.Actuals);
|
|
if (actualScenario != null)
|
|
{
|
|
var actualSplitInfo = Utils.GetLaborMaterialsSplit(actualScenario.Id);
|
|
|
|
if (actualScenario.StartDate.HasValue)
|
|
model.ActualsStartDate = (long)actualScenario.StartDate.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
if (actualScenario.EndDate.HasValue)
|
|
model.ActualsEndDate = (long)actualScenario.EndDate.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
model.ActualLabor = actualSplitInfo.Labor;
|
|
model.ActualMaterials = actualSplitInfo.Materials;
|
|
model.ActualsTotal = actualSplitInfo.Total;
|
|
model.HasActuals = actualScenario.EndDate.HasValue;
|
|
model.ActualLaborMaterialsSplit = actualSplitInfo.LaborAndMaterialSplit;
|
|
}
|
|
var forecastSplitInfo = Utils.GetLaborMaterialsSplit(model.TemplateId);
|
|
|
|
model.LaborMaterialsSplit = forecastSplitInfo.LaborAndMaterialSplit;
|
|
|
|
#region Override Actuals fields if there are no actuals data
|
|
bool actualDataExists = false;
|
|
|
|
if (actualScenario != null)
|
|
// Actual data scenario exists
|
|
actualDataExists = actualScenario.EndDate.HasValue;
|
|
|
|
model.DateForStartOfChanges = model.StartDate.HasValue && model.StartDate.Value > DateTime.Today ? model.StartDate.Value.Date : DateTime.Today;
|
|
if (!actualDataExists)
|
|
{
|
|
// Show forecats values as actuals, if actuals not found
|
|
model.BUDirectCostsActuals = model.BUDirectCosts;
|
|
model.BUDirectCostsLMActuals = model.BUDirectCostsLM;
|
|
model.BUCostsShotsActuals = model.BUCostsShots;
|
|
model.BUCostsShotsLMActuals = model.BUCostsShotsLM;
|
|
model.CalculatedGrossMarginActuals = model.CalculatedGrossMargin;
|
|
model.CalculatedGrossMarginLMActuals = model.CalculatedGrossMarginLM;
|
|
}
|
|
else
|
|
{
|
|
// date for start of changes should be bigger than actuals end date
|
|
if (actualScenario.EndDate >= model.DateForStartOfChanges)
|
|
model.DateForStartOfChanges = actualScenario.EndDate.Value.AddDays(1);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
return model;
|
|
}
|
|
|
|
// GET: /Scenarios/Details/5
|
|
[HttpGet]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult Details(Guid? id, string tab, string backUrl, string backName)
|
|
{
|
|
if (id == null || id == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
ScenarioDetailModel model = null;
|
|
var manager = new ScenarioManager(DbContext);
|
|
try
|
|
{
|
|
model = LoadScenarioDetailsModel(id.Value, manager);
|
|
|
|
if (model.Id == Guid.Empty)
|
|
{
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
|
|
model.ActiveTab = tab;
|
|
|
|
// SA. Param names where restored. 'ref' changed to 'backUrl' and 'back' changed to 'backName' ENV-707. Begin
|
|
model.BackUrl = !string.IsNullOrEmpty(backUrl) ? backUrl : Url.Action("Index", "Project");
|
|
model.BackName = !string.IsNullOrEmpty(backName) ? backName : "list";
|
|
// SA. ENV-707. End
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
return View(model);
|
|
}
|
|
|
|
[HttpGet]
|
|
public ActionResult GetScenarioAvailableExpCategories(Guid id)
|
|
{
|
|
return Json(GetScenarioCategories(id), JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
private List<ExpenditureModel> GetScenarioCategories(Guid id)
|
|
{
|
|
return DbContext.VW_ExpCategoriesInScenario.AsNoTracking()
|
|
.Where(t => t.ScenarioID == id)
|
|
.OrderBy(t => t.ExpCategoryWithCcName)
|
|
.Select(t => new ExpenditureModel
|
|
{
|
|
Id = t.Id,
|
|
Name = t.ExpCategoryWithCcName // SA. ENV-839
|
|
}).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns ExpCategories list. Every category marked, if it is related to one of the given (scenario) teams
|
|
/// </summary>
|
|
/// <param name="teams">Scenario teams</param>
|
|
/// <returns></returns>
|
|
/// <remarks>SA. ENV-840</remarks>
|
|
private List<ScenarioExpenditureModel> GetAllCategories(List<Guid> teams)
|
|
{
|
|
// Get all categories
|
|
List<ScenarioExpenditureModel> categoties =
|
|
DbContext.VW_ExpenditureCategory.AsNoTracking()
|
|
.OrderBy(t => t.Name)
|
|
.Select(t => new ScenarioExpenditureModel
|
|
{
|
|
Id = t.Id,
|
|
Name = t.ExpCategoryWithCcName // SA. ENV-839
|
|
}).ToList();
|
|
|
|
if ((teams != null) && (teams.Count > 0))
|
|
{
|
|
// Get categories, related to given teams
|
|
List<Guid> scenarioCategories =
|
|
(from PeopleResource pr in DbContext.PeopleResources.AsNoTracking()
|
|
where pr.TeamId.HasValue && teams.Contains(pr.TeamId.Value)
|
|
select pr.ExpenditureCategoryId).ToList();
|
|
|
|
// Mark related to teams categories
|
|
categoties.Where(x => scenarioCategories.Contains(x.Id)).ToList().ForEach(x => x.IsRelatedToScenario = 1);
|
|
}
|
|
|
|
return categoties;
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult GetRates(Guid scenarioId, bool? uomMode)
|
|
{
|
|
// temporary solution, action method will be removed
|
|
var allExpCats = DbContext.ExpenditureCategory.AsNoTracking().ToDictionary(x => x.Id);
|
|
var allUoms = DbContext.UOMs.AsNoTracking().ToDictionary(x => x.Id);
|
|
|
|
if (!uomMode.HasValue)
|
|
{
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetID()));
|
|
if (user != null)
|
|
uomMode = !user.PreferredResourceAllocation;
|
|
}
|
|
var rates = (new ScenarioManager(DbContext)).GetRates(scenarioId);
|
|
foreach (var expCatData in rates)
|
|
{
|
|
foreach (var rate in expCatData.rateValues)
|
|
rate.rateValue = Utils.GetUOMMultiplier(allExpCats, allUoms, expCatData.expCatId, uomMode);
|
|
}
|
|
return Json(rates);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult CopyTo(ScenarioCopyModel model)
|
|
{
|
|
if (Guid.Empty.Equals(model.TargetProjectId) || Guid.Empty.Equals(model.ScenarioId))
|
|
ModelState.AddModelError(string.Empty, "Invalid parameters");
|
|
else
|
|
{
|
|
try
|
|
{
|
|
var newScenario = new ScenarioManager(DbContext).CopyTo(model.ScenarioId, model.TargetProjectId, model.TargetStatus, model.includeCostSavings);
|
|
return Json(newScenario);
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
HttpContext.Response.StatusCode = 500;
|
|
HttpContext.Response.Clear();
|
|
return PartialView("_copyToModal", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult SaveChanges(SaveScenarioDetailsChangesModel model)
|
|
{
|
|
if (model.ScenarioId != Guid.Empty && ContentLocker.IsLock("Scenario", model.ScenarioId.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
var context = new EnVisageEntities();
|
|
model.TrimStringProperties();
|
|
var allExpCats = DbContext.ExpenditureCategory.AsNoTracking().ToDictionary(x => x.Id);
|
|
var allUoms = DbContext.UOMs.AsNoTracking().ToDictionary(x => x.Id);
|
|
|
|
if (!model.ScenarioFilters.IsUOMHours.HasValue)
|
|
{
|
|
var user = new UsersCache().Value.FirstOrDefault(x => x.Id == new Guid(HttpContext.User.Identity.GetID()));
|
|
if (user != null)
|
|
model.ScenarioFilters.IsUOMHours = !user.PreferredResourceAllocation;
|
|
}
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var manager = new ScenarioManager(context);
|
|
var scenario = manager.Load(model.ScenarioId, false);
|
|
var scenarioStartDateMsSince1970 = scenario.StartDate.HasValue ?
|
|
(long)scenario.StartDate.Value.Subtract(Constants.UnixEpochDate).TotalMilliseconds : 0;
|
|
|
|
foreach (var changedExpCat in model.ChangedExpCats)
|
|
{
|
|
var uomMultiplier = Utils.GetUOMMultiplier(allExpCats, allUoms, changedExpCat.Id, model.ScenarioFilters.IsUOMHours);
|
|
foreach (var changedColumn in changedExpCat.Values)
|
|
{
|
|
if (changedColumn.Milliseconds >= scenarioStartDateMsSince1970)
|
|
{
|
|
var scenarioDetailsItem = (from c in context.ScenarioDetail where c.Id == changedColumn.Id select c).FirstOrDefault();//manager.LoadScenarioDetail(changedColumn.Id, false);
|
|
if (scenarioDetailsItem == null) continue;
|
|
if (scenarioDetailsItem.Id == Guid.Empty)
|
|
{
|
|
throw new NullReferenceException(string.Format("Scenario Details {0} mising", changedColumn.Id));
|
|
}
|
|
scenarioDetailsItem.Quantity = changedColumn.Quantity / uomMultiplier;
|
|
scenarioDetailsItem.Cost = changedColumn.Cost;
|
|
}
|
|
}
|
|
if (changedExpCat.Resources != null)
|
|
{
|
|
var resourceIds = changedExpCat.Resources.Select(x => x.Id).ToList();
|
|
var resourceAllocations = context.PeopleResourceAllocations.Where(x => resourceIds.Contains(x.PeopleResourceId) && x.ScenarioId == model.ScenarioId).ToList();
|
|
foreach (var resource in changedExpCat.Resources)
|
|
{
|
|
var resourceId = resource.Id;
|
|
|
|
if (resource.IsRemoved)
|
|
{
|
|
var recourcesToDelete = context.PeopleResourceAllocations.Where(x => x.PeopleResourceId == resourceId && x.ScenarioId == model.ScenarioId).ToList();
|
|
recourcesToDelete.ForEach(x => context.PeopleResourceAllocations.Remove(x));
|
|
recourcesToDelete.ForEach(x => context.Entry(x).State = EntityState.Deleted);
|
|
}
|
|
else
|
|
{
|
|
foreach (var changedResource in resource.Values)
|
|
{
|
|
var date = Constants.UnixEpochDate.AddSeconds(changedResource.Milliseconds / 1000);
|
|
|
|
var allocatedResource = (from c in resourceAllocations
|
|
where c.WeekEndingDate == date && c.ScenarioId == model.ScenarioId
|
|
&& c.PeopleResourceId == resourceId && c.ExpenditureCategoryId == changedExpCat.Id
|
|
select c).FirstOrDefault();
|
|
if (changedResource.Quantity <= 0)
|
|
{
|
|
if (allocatedResource != null)
|
|
context.Entry(allocatedResource).State = EntityState.Deleted;
|
|
continue;
|
|
}
|
|
if (allocatedResource == null)
|
|
{
|
|
allocatedResource = context.PeopleResourceAllocations.Create();
|
|
allocatedResource.Id = Guid.NewGuid();
|
|
allocatedResource.ExpenditureCategoryId = changedExpCat.Id;
|
|
allocatedResource.PeopleResourceId = resourceId;
|
|
allocatedResource.ScenarioId = model.ScenarioId;
|
|
allocatedResource.WeekEndingDate = date;
|
|
context.Entry(allocatedResource).State = EntityState.Added;
|
|
}
|
|
else
|
|
{
|
|
context.Entry(allocatedResource).State = EntityState.Modified;
|
|
}
|
|
allocatedResource.Quantity = changedResource.Quantity / uomMultiplier;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//TODO: review that we really need 2 transactions here. Looks like it was here since bottom-up costs recalculation was in stored procedure.
|
|
context.SaveChanges();
|
|
manager.SetBottomUpCosts(scenario);
|
|
context.SaveChanges();
|
|
|
|
model.ScenarioFilters.StartDate = scenario.StartDate ?? DateTime.Today;
|
|
model.ScenarioFilters.EndDate = scenario.EndDate ?? DateTime.Today.AddYears(1);
|
|
model.ScenarioFilters.GrowthScenario = scenario.GrowthScenario;
|
|
model.ScenarioFilters.ParentId = scenario.ParentId ?? Guid.Empty;
|
|
model.ScenarioFilters.ScenarioType = (ScenarioType?)scenario.Type;
|
|
//var detailsGrid = GetScenarioCalendar(model.ScenarioFilters);
|
|
|
|
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]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult AddNote(NoteModel model)
|
|
{
|
|
//if (model.ScenarioId != Guid.Empty && ContentLocker.IsLock("Scenario", model.ScenarioId.ToString(), User.Identity.Name))
|
|
// return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
model.Id = Guid.NewGuid();
|
|
var newnote = new Note();
|
|
model.CopyTo(newnote);
|
|
newnote.UserId = new Guid(User.Identity.GetID());
|
|
DbContext.Notes.Add(newnote);
|
|
DbContext.SaveChanges();
|
|
|
|
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);
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
}
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult EditNote(NoteModel model)
|
|
{
|
|
//if (model.ScenarioId != Guid.Empty && ContentLocker.IsLock("Scenario", model.ScenarioId.ToString(), User.Identity.Name))
|
|
// return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var note = (from c in DbContext.Notes where c.Id == model.Id select c).FirstOrDefault();
|
|
if (note == null) return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
note.Title = model.Title;
|
|
note.NoteDetail = model.Details;
|
|
DbContext.SaveChanges();
|
|
|
|
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);
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
}
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult DeleteNote(Guid id)
|
|
{
|
|
if (id == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var note = (from c in DbContext.Notes where c.Id == id select c).FirstOrDefault();
|
|
if (note == null)
|
|
{
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
DbContext.Notes.Remove(note);
|
|
DbContext.SaveChanges();
|
|
|
|
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);
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
// GET: /User/Edit/5
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult EditNote(Guid id)
|
|
{
|
|
if (id == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var note = DbContext.Notes.AsNoTracking().FirstOrDefault(x => x.Id == id);
|
|
if (note == null)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
else
|
|
return PartialView("_addNote", (NoteModel)note);
|
|
}
|
|
|
|
// GET: /User/Edit/5
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult AddNote(Guid id)
|
|
{
|
|
if (id == Guid.Empty)
|
|
{
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
|
|
return PartialView("_addNote", new NoteModel(id));
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.Scenarios, level = AccessLevel.Read)]
|
|
public ActionResult LoadNotes(Guid scenarioId)
|
|
{
|
|
try
|
|
{
|
|
var notesModel = new List<NoteModel>();
|
|
var notes = DbContext.Notes.Where(x => x.ParentId == scenarioId).OrderBy(x => x.DateAdded).ToList();
|
|
foreach (var note in notes)
|
|
notesModel.Add((NoteModel)note);
|
|
|
|
return PartialView("_notesList", notesModel);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult EditRate(Guid? id, Guid? parentId, Guid expentureCategoryId)
|
|
{
|
|
RateModel model = null;
|
|
if (id.HasValue && id != Guid.Empty)
|
|
{
|
|
var manager = new RateManager(DbContext);
|
|
model = (RateModel)manager.Load(id);
|
|
}
|
|
else
|
|
{
|
|
var sManager = new ScenarioManager(DbContext);
|
|
var scenario = sManager.Load(parentId);
|
|
model = new RateModel();
|
|
model.Type = RateModel.RateType.Derived;
|
|
var lRates = (from c in DbContext.Rates
|
|
where
|
|
c.ExpenditureCategoryId == expentureCategoryId &&
|
|
c.Type == (int)RateModel.RateType.Derived &&
|
|
c.ParentId == parentId
|
|
select c).ToList();
|
|
if ((from d in lRates select d.EndDate).Max() < DateTime.MaxValue.AddYears(-4))
|
|
{
|
|
model.StartDate = (lRates.Count == 0) ? scenario.StartDate.Value : ((from d in lRates select d.EndDate).Max()).AddDays(1);
|
|
model.EndDate = (scenario.EndDate.Value <= model.StartDate) ? model.StartDate.AddDays(1) : scenario.EndDate.Value;
|
|
}
|
|
else
|
|
{
|
|
model.StartDate = (from d in lRates select d.EndDate).Max();
|
|
model.EndDate = model.StartDate.AddDays(1);
|
|
}
|
|
}
|
|
|
|
if ((!model.ParentId.HasValue || model.ParentId == Guid.Empty) && parentId.HasValue)
|
|
model.ParentId = parentId;
|
|
if (model.ExpenditureCategoryId == Guid.Empty)
|
|
model.ExpenditureCategoryId = expentureCategoryId;
|
|
|
|
return PartialView("_editRate", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult EditRate(RateModel model)
|
|
{
|
|
if (ContentLocker.IsLock("Rate", model.Id.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var manager = new RateManager(DbContext);
|
|
manager.Save(model);
|
|
DbContext.SaveChanges();
|
|
manager.Dispose();
|
|
return JavaScript("window.location.search += '&tab=rateTable';");
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
|
|
ContentLocker.RemoveLock("Rate", model.Id.ToString(), User.Identity.Name);
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult DeleteRate(Guid? id)
|
|
{
|
|
RateModel model = null;
|
|
if (id.HasValue && id != Guid.Empty)
|
|
{
|
|
var manager = new RateManager(DbContext);
|
|
model = (RateModel)manager.Load(id);
|
|
}
|
|
else
|
|
model = new RateModel();
|
|
|
|
return PartialView("_deleteRate", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult DeleteRate(RateModel model)
|
|
{
|
|
// SA. ENV-1011. Removed model validation, because model has no enough data to be valid.
|
|
// For Reate deletion we need Id only
|
|
if ((model == null) || model.Id.Equals(Guid.Empty))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
if (ContentLocker.IsLock("Rate", model.Id.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var dbObj = DbContext.Rates.FirstOrDefault(x => x.Id == model.Id);
|
|
DbContext.Rates.Remove(dbObj);
|
|
DbContext.SaveChanges();
|
|
|
|
ContentLocker.RemoveLock("Rate", model.Id.ToString(), User.Identity.Name);
|
|
return ProcessRates(new RatesModel()
|
|
{
|
|
ExpenditureCategoryId = model.ExpenditureCategoryId,
|
|
ScenarioId = model.ParentId.Value,
|
|
Mode = RatesModel.FormMode.ListRates
|
|
});
|
|
}
|
|
|
|
[HttpGet]
|
|
public JsonResult GetTeamCapacityScenarioId(Guid teamId)
|
|
{
|
|
using (var context = new EnVisageEntities())
|
|
{
|
|
var data = (from sd in context.Teams
|
|
where sd.Id == teamId
|
|
select sd.PlannedCapacityScenarioId).FirstOrDefault();
|
|
if (data == null)
|
|
{
|
|
var teamManager = new TeamManager(DbContext);
|
|
var team = teamManager.Load(teamId, false);
|
|
var scen = new Scenario();
|
|
scen.Name = team.Name.Trim() + " Planned Capacity";
|
|
scen.Type = (int)ScenarioType.TeamPlannedCapacity;
|
|
scen.Id = Guid.NewGuid();
|
|
team.PlannedCapacityScenarioId = scen.Id;
|
|
DbContext.Scenarios.Add(scen);
|
|
DbContext.SaveChanges();
|
|
data = scen.Id;
|
|
}
|
|
return Json((data == null) ? string.Empty : data.ToString(), JsonRequestBehavior.AllowGet);
|
|
}
|
|
|
|
}
|
|
|
|
[HttpGet]
|
|
public void ToggleTemplateStatus()
|
|
{
|
|
string scenarioId = Request.QueryString["scenarioId"];
|
|
var guidId = new Guid(scenarioId);
|
|
var context = new EnVisageEntities();
|
|
var scenario = (from c in context.Scenarios where c.Id == guidId select c).FirstOrDefault();
|
|
if (scenario != null && scenario.Status != (int)ScenarioStatus.Active)
|
|
{
|
|
scenario.Status = (int?)ScenarioStatus.Active;
|
|
}
|
|
else if (scenario != null)
|
|
scenario.Status = (int)ScenarioStatus.Inactive;
|
|
context.SaveChanges();
|
|
|
|
}
|
|
|
|
// GET: /Scenario/Delete/5
|
|
[HttpGet]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult Delete(Guid? id, string backUrl = null)
|
|
{
|
|
if (id == null || id == Guid.Empty)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
// SA. ENV-755
|
|
if (!SecurityManager.CheckScenarioPermission(id.Value, AccessLevel.Write))
|
|
return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
|
|
|
|
var model = new ScenarioModel();
|
|
try
|
|
{
|
|
var manager = new ScenarioManager(DbContext);
|
|
model = (ScenarioModel)manager.Load(id);
|
|
if (model == null)
|
|
return HttpNotFound();
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
return View(model);
|
|
}
|
|
|
|
// POST: /Scenario/Delete/5
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult Delete(ScenarioModel model)
|
|
{
|
|
if (ContentLocker.IsLock("Scenario", model.Id.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var manager = new ScenarioManager(DbContext);
|
|
var dbObj = manager.Load(model.Id, false);
|
|
if (dbObj == null)
|
|
return HttpNotFound();
|
|
(DbContext as IObjectContextAdapter).ObjectContext.ExecuteStoreCommand(string.Format("exec sp_DeleteScenario '{0}'", dbObj.Id));
|
|
DbContext.SaveChanges();
|
|
ContentLocker.RemoveLock("Scenario", dbObj.Id.ToString(), User.Identity.Name);
|
|
|
|
if (Request["backUrl"] != null)
|
|
{
|
|
return Redirect(Request["backUrl"]);
|
|
}
|
|
else
|
|
{
|
|
return RedirectToAction("Index", "Project");
|
|
}
|
|
}
|
|
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult AdjustMargin(Guid? id)
|
|
{
|
|
var manager = new ScenarioManager(DbContext);
|
|
var scenario = (ScenarioModel)manager.Load(id, false) ?? new ScenarioModel();
|
|
var project = DbContext.Projects.FirstOrDefault(x => x.Id == scenario.ParentId);
|
|
var model = new UpdateScenarioModel()
|
|
{
|
|
Id = scenario.Id,
|
|
EndDate = scenario.EndDate,
|
|
Expenditures = scenario.Expenditures,
|
|
GrossMargin = scenario.GrossMargin,
|
|
LMMargin = scenario.LMMargin,
|
|
TDDirectCosts = scenario.TDDirectCosts,
|
|
ProjectedRevenue = scenario.ProjectedRevenue,
|
|
StartDate = scenario.StartDate,
|
|
UseLMMargin = scenario.UseLMMargin,
|
|
AllowAdjustment = scenario.AllowAdjustment,
|
|
PriorWeekCutOff = scenario.PriorWeekCutOff ?? scenario.StartDate,
|
|
BUDirectCosts = scenario.BUDirectCosts
|
|
};
|
|
|
|
if (project != null)
|
|
model.IsRevenueGenerating = project.IsRevenueGenerating;
|
|
else
|
|
model.IsRevenueGenerating = false;
|
|
|
|
|
|
model.Expenditures = manager.GetExpenditureCategories(model.Id);
|
|
|
|
return PartialView("_adjustMargin", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult AdjustMargin(UpdateScenarioModel model, Guid[] expCatId, string[] expCatName, string[] expCatGroup, bool[] expCatChecked)
|
|
{
|
|
if (ContentLocker.IsLock("Scenario", model.Id.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
if (expCatId != null && expCatName != null && expCatGroup != null && expCatChecked != null &&
|
|
expCatId.Length == expCatName.Length && expCatId.Length == expCatGroup.Length && expCatId.Length == expCatChecked.Length)
|
|
{
|
|
model.Expenditures = new ScenarioModel.ExpenditureItem[expCatId.Length];
|
|
for (var i = 0; i < expCatId.Length; i++)
|
|
{
|
|
model.Expenditures[i] = new ScenarioModel.ExpenditureItem
|
|
{
|
|
Id = expCatId[i],
|
|
Group = expCatGroup[i],
|
|
Name = expCatName[i],
|
|
Checked = expCatChecked[i]
|
|
};
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
// get scenario by id
|
|
var manager = new ScenarioManager(DbContext);
|
|
var scenario = (ScenarioModel)manager.Load(model.Id);
|
|
|
|
var project = DbContext.Projects.FirstOrDefault(x => x.Id == scenario.ParentId);
|
|
if (project != null)
|
|
model.IsRevenueGenerating = project.IsRevenueGenerating;
|
|
else
|
|
model.IsRevenueGenerating = false;
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
// update scenario values
|
|
scenario.StartDate = model.StartDate;
|
|
scenario.EndDate = model.EndDate;
|
|
// SA. ENV-698. GrossMargin and LMMargin adjustment was fixed. Begin
|
|
|
|
if (model.UseLMMargin)
|
|
// User selected the LM Margin adjustment only
|
|
scenario.LMMargin = model.LMMargin;
|
|
else
|
|
// User selected the Gross Margin adjustment only
|
|
scenario.GrossMargin = model.GrossMargin;
|
|
|
|
// The original code lines:
|
|
// scenario.GrossMargin = model.UseLMMargin ? null : model.GrossMargin;//!model.UseLMMargin ? model.GrossMargin / 100 : null;
|
|
// scenario.LMMargin = model.UseLMMargin ? model.LMMargin : null;
|
|
// SA. ENV-698. End
|
|
|
|
scenario.TDDirectCosts = model.TDDirectCosts;
|
|
scenario.ProjectedRevenue = model.ProjectedRevenue;
|
|
scenario.PriorWeekCutOff = model.PriorWeekCutOff;
|
|
scenario.AllowAdjustment = model.AllowAdjustment;
|
|
scenario.UseLMMargin = model.UseLMMargin;
|
|
scenario.Expenditures = model.Expenditures;
|
|
scenario.FreezeResource = model.AllowAdjustment;
|
|
|
|
manager.Save(scenario);
|
|
|
|
ContentLocker.RemoveLock("Scenario", model.Id.ToString(), User.Identity.Name);
|
|
return Json(new { Status = "Ok" });
|
|
}
|
|
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
ModelState.AddModelError("errors", "Error on adjust scenario.");
|
|
}
|
|
|
|
ContentLocker.RemoveLock("Scenario", model.Id.ToString(), User.Identity.Name);
|
|
return PartialView("_adjustMargin", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult CreateTemplate(ScenarioTemplateCreationModel model, Guid[] expCatId, string[] expCatName, string[] expCatGroup, bool[] templateExpCatChecked)
|
|
{
|
|
if (ContentLocker.IsLock("Scenario", model.Id.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
|
|
if (expCatId != null && expCatName != null && expCatGroup != null && templateExpCatChecked != null &&
|
|
expCatId.Length == expCatName.Length && expCatId.Length == expCatGroup.Length && expCatId.Length == templateExpCatChecked.Length)
|
|
{
|
|
model.Expenditures = new ScenarioModel.ExpenditureItem[expCatId.Length];
|
|
for (var i = 0; i < expCatId.Length; i++)
|
|
{
|
|
model.Expenditures[i] = new ScenarioModel.ExpenditureItem
|
|
{
|
|
Id = expCatId[i],
|
|
Group = expCatGroup[i],
|
|
Name = expCatName[i],
|
|
Checked = templateExpCatChecked[i]
|
|
};
|
|
}
|
|
}
|
|
var expCatIds = (from e in model.Expenditures where e.Checked == true select e.Id).ToArray();
|
|
|
|
try
|
|
{
|
|
// get scenario by id
|
|
var manager = new ScenarioManager(DbContext);
|
|
var scenario = manager.Load(model.Id);
|
|
|
|
if (ModelState.IsValid)
|
|
{
|
|
var newTemplate = DbContext.Scenarios.Create();
|
|
newTemplate.Id = Guid.NewGuid();
|
|
newTemplate.Name = model.TemplateName;
|
|
newTemplate.ParentId = null;
|
|
newTemplate.TemplateId = null;
|
|
newTemplate.Type = (int)ScenarioType.Template;
|
|
newTemplate.ProjectedRevenue = 0;
|
|
newTemplate.ExpectedGrossMargin = 0;
|
|
newTemplate.CalculatedGrossMargin = 0;
|
|
newTemplate.CGSplit = scenario.CGSplit;
|
|
newTemplate.EFXSplit = scenario.EFXSplit;
|
|
newTemplate.StartDate = null;
|
|
newTemplate.EndDate = null;
|
|
newTemplate.Duration = scenario.Duration;
|
|
newTemplate.TDDirectCosts = 0;
|
|
newTemplate.BUDirectCosts = 0;
|
|
newTemplate.Shots = scenario.Shots;
|
|
newTemplate.TDRevenueShot = 0;
|
|
newTemplate.BURevenueShot = 0;
|
|
newTemplate.FreezeRevenue = false;
|
|
newTemplate.Color = null;
|
|
newTemplate.Status = (int)ScenarioStatus.Active;
|
|
newTemplate.UseLMMargin = scenario.UseLMMargin;
|
|
newTemplate.ExpectedGrossMargin_LM = scenario.ExpectedGrossMargin_LM;
|
|
newTemplate.CalculatedGrossMargin_LM = scenario.CalculatedGrossMargin_LM;
|
|
newTemplate.TDDirectCosts_LM = 0;
|
|
newTemplate.BUDirectCosts_LM = 0;
|
|
newTemplate.BURevenueShot_LM = 0;
|
|
newTemplate.ShotStartDate = null;
|
|
newTemplate.GrowthScenario = scenario.GrowthScenario;
|
|
newTemplate.Actuals_BUDirectCosts = 0;
|
|
newTemplate.Actuals_BUDirectCosts_LM = 0;
|
|
newTemplate.SystemAttributeObjectID = scenario.SystemAttributeObjectID;
|
|
DbContext.Entry(newTemplate).State = EntityState.Added;
|
|
|
|
if (!string.IsNullOrEmpty(model.TemplateGroupNames))
|
|
{
|
|
var groups = model.TemplateGroupNames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
var savedGroups = DbContext.TemplateGroups.Where(x => groups.Contains(x.Name)).ToList();
|
|
var currentTemplategroups = new List<string>();
|
|
|
|
foreach (var item in groups)
|
|
{
|
|
currentTemplategroups.Add(item);
|
|
|
|
var group = savedGroups.FirstOrDefault(tg => tg.Name.Equals(item));
|
|
if (group == null)
|
|
{
|
|
group = new TemplateGroup()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Name = item
|
|
};
|
|
|
|
DbContext.TemplateGroups.Add(group);
|
|
}
|
|
|
|
DbContext.Template2TemplateGroup.Add(new Template2TemplateGroup()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TemplateId = newTemplate.Id,
|
|
TemplateGroupId = group.Id
|
|
});
|
|
}
|
|
}
|
|
|
|
var sds = DbContext.ScenarioDetail.Where(s => s.ParentID == scenario.Id && expCatIds.Contains(s.ExpenditureCategoryId.Value)).ToList();
|
|
foreach (var scenarioDetails in sds)
|
|
{
|
|
DbContext.ScenarioDetail.Add(new ScenarioDetail()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
ParentID = newTemplate.Id,
|
|
Cost = scenarioDetails.Cost,
|
|
ExpenditureCategoryId = scenarioDetails.ExpenditureCategoryId,
|
|
WeekEndingDate = null,
|
|
Quantity = scenarioDetails.Quantity,
|
|
WeekOrdinal = scenarioDetails.WeekOrdinal
|
|
});
|
|
}
|
|
DbContext.SaveChanges();
|
|
|
|
ContentLocker.RemoveLock("Scenario", model.Id.ToString(), User.Identity.Name);
|
|
return Json(new { Status = "Ok" });
|
|
}
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
ModelState.AddModelError("errors", "Error on adjust scenario.");
|
|
}
|
|
|
|
ContentLocker.RemoveLock("Scenario", model.Id.ToString(), User.Identity.Name);
|
|
return PartialView("_createTemplateModal", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult DeleteAllLocalRates(Guid? scenarioId, Guid? expenditureCategoryId)
|
|
{
|
|
if (!scenarioId.HasValue || scenarioId == Guid.Empty || !expenditureCategoryId.HasValue || expenditureCategoryId == Guid.Empty)
|
|
return Json(new { Status = "Error", Msg = "Parameter scenarioId or expenditureCategoryId can not be null or empty." });
|
|
|
|
if (ContentLocker.IsLock("Scenario", scenarioId.ToString(), User.Identity.Name))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
var ratesForDelete = DbContext.Rates
|
|
.Where(x => x.ParentId == scenarioId &&
|
|
x.ExpenditureCategoryId == expenditureCategoryId &&
|
|
x.Type == (short)RateModel.RateType.Derived);
|
|
DbContext.Rates.RemoveRange(ratesForDelete);
|
|
DbContext.SaveChanges();
|
|
|
|
ContentLocker.RemoveLock("Scenario", scenarioId.ToString(), User.Identity.Name);
|
|
|
|
return Json(new { Status = "Ok" });
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
return Json(new { Status = "Error", Msg = exception.Message });
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
public ActionResult RecalculateCalendar(ScenarioDetailSnapshotRecalculationModel recalculationModel)
|
|
{
|
|
if (recalculationModel == null)
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
var scenarioManager = (new ScenarioManager(DbContext));
|
|
var ratesManager = (new RateManager(DbContext));
|
|
|
|
var model = new ScenarioDetailSnapshotModel()
|
|
{
|
|
Scenario = recalculationModel.Scenario,
|
|
CalendarFilter = recalculationModel.CalendarFilter,
|
|
AvailableExpenditures = recalculationModel.AvailableExpenditures,
|
|
TeamsInScenario = recalculationModel.TeamsInScenario,
|
|
Rates = recalculationModel.Rates,
|
|
NeedToRebind = recalculationModel.NeedToRebind,
|
|
NeedToRecalculateScenarioDetails = recalculationModel.NeedToRecalculateScenarioDetails,
|
|
NeedToRefreshAllTeams = recalculationModel.NeedToRefreshAllTeams,
|
|
LocalRatesLoaded = recalculationModel.LocalRatesLoaded,
|
|
Calendar = new ScenarioCalendarModel()
|
|
{
|
|
Expenditures = recalculationModel.Calendar != null ? recalculationModel.Calendar.Expenditures : new Dictionary<string, ExpenditureDetail>()
|
|
}
|
|
};
|
|
|
|
if (model.NeedToRebind)
|
|
{
|
|
var actualScenarioId = Guid.Empty;
|
|
var periodStartDate = new DateTime(DateTime.Today.Year, 1, 1);
|
|
var periodEndDate = new DateTime(DateTime.Today.Year, 12, 31);
|
|
// if scenario was inited with forecast data
|
|
var forecast = model.Calendar.GetScenarioDetails(true);
|
|
var scenarioDetailsPreLoaded = !(forecast == null || forecast.Count <= 0);
|
|
var actuals = new List<VW_ScenarioAndProxyDetails>();
|
|
|
|
if (model.Scenario.ParentId.HasValue)
|
|
{
|
|
var project = DbContext.Projects.FirstOrDefault(x => x.Id == model.Scenario.ParentId);
|
|
if (project != null)
|
|
{
|
|
model.Scenario.ProjectDeadline = project.Deadline.HasValue ? Utils.ConvertToUnixDate(project.Deadline.Value) : (long?)null;
|
|
model.Scenario.YellowIndicator = project.PerformanceYellowThreshold;
|
|
if (!model.Scenario.YellowIndicator.HasValue)
|
|
model.Scenario.YellowIndicator = project.Type.PerformanceYellowThreshold;
|
|
|
|
model.Scenario.RedIndicator = project.PerformanceRedThreshold;
|
|
if (!model.Scenario.RedIndicator.HasValue)
|
|
model.Scenario.RedIndicator = project.Type.PerformanceRedThreshold;
|
|
}
|
|
if (!model.Scenario.GrowthScenario && !ScenarioType.Actuals.Equals(model.Scenario.Type))
|
|
{
|
|
var actualScenario = DbContext.Scenarios.FirstOrDefault(t => t.ParentId == model.Scenario.ParentId && t.Type == (int?)ScenarioType.Actuals);
|
|
if (actualScenario != null)
|
|
{
|
|
actualScenarioId = actualScenario.Id;
|
|
model.Scenario.ActualStartDate = (long)(actualScenario.StartDate ?? Constants.UnixEpochDate).Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
model.Scenario.ActualEndDate = (long)(actualScenario.EndDate ?? Constants.UnixEpochDate).Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
if (actualScenario.StartDate < Constants.UnixEpochDate.AddMilliseconds(model.Scenario.StartDate))
|
|
periodStartDate = actualScenario.StartDate ?? Constants.UnixEpochDate;
|
|
if (actualScenario.EndDate > Constants.UnixEpochDate.AddMilliseconds(model.Scenario.EndDate))
|
|
periodEndDate = actualScenario.EndDate ?? Constants.UnixEpochDate;
|
|
if (!Guid.Empty.Equals(actualScenarioId))
|
|
{
|
|
// get the last week even if scenario ends in the middle of the week. E.g. Scenario.EndDate=12/31 and week.EndDate=1/1
|
|
actuals = scenarioManager.GetScenarioProxyDetails(null, periodStartDate, periodEndDate.AddDays(6), new List<Guid> { actualScenarioId });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (model.Scenario.Type != ScenarioType.LoanOut &&
|
|
model.Scenario.Type != ScenarioType.Training &&
|
|
model.Scenario.Type != ScenarioType.Vacation)
|
|
{
|
|
periodStartDate = Utils.ConvertFromUnixDate(model.Scenario.StartDate);
|
|
periodEndDate = Utils.ConvertFromUnixDate(model.Scenario.EndDate);
|
|
}
|
|
|
|
if (model.Scenario.Id.HasValue && !Guid.Empty.Equals(model.Scenario.Id) && !scenarioDetailsPreLoaded)
|
|
{
|
|
// get the last week even if scenario ends in the middle of the week. E.g. Scenario.EndDate=12/31 and week.EndDate=1/1
|
|
forecast = scenarioManager.GetScenarioProxyDetails(null, periodStartDate, periodEndDate.AddDays(6),
|
|
new List<Guid> { model.Scenario.Id.Value });
|
|
model.AvailableExpenditures = GetScenarioCategories(model.Scenario.Id.Value);
|
|
}
|
|
|
|
if (!model.NeedToRecalculateScenarioDetails)
|
|
{
|
|
var oldExpenditureTeams = scenarioDetailsPreLoaded ? model.Calendar.GetTeams() : null;
|
|
|
|
// if scenario was inited with scenario details we do not need to refresh teams info on this step
|
|
model.Calendar = BuildScenarioDetailsCalendarData(forecast, actuals);
|
|
if (model.Calendar != null && model.Calendar.Expenditures != null)
|
|
{
|
|
// if there is no preloaded data we should recalculate team information for scenario
|
|
if (!scenarioDetailsPreLoaded)
|
|
{
|
|
model.TeamsInScenario = scenarioManager.FillTeamsDetails(model.Scenario.Id ?? model.Scenario.TemplateId, model.Calendar.Expenditures, HttpContext.User.Identity.GetID());
|
|
}
|
|
else
|
|
{
|
|
// otherwise we should fill expenditures with preloaded teams
|
|
if (oldExpenditureTeams != null && oldExpenditureTeams.Count > 0)
|
|
{
|
|
foreach (var expCatId in model.Calendar.Expenditures.Keys)
|
|
{
|
|
if (oldExpenditureTeams.ContainsKey(expCatId))
|
|
model.Calendar.Expenditures[expCatId].Teams = oldExpenditureTeams[expCatId];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// SA. ENV-840
|
|
List<Guid> scenarioTeamIds = model.TeamsInScenario.Select(x => x.TeamId).ToList();
|
|
model.CategoryTypes = Utils.CastEnumToSelectedList<ExpenditureCategoryModel.CategoryTypes>();
|
|
model.CGEFX = Utils.GetCGEFX().ToList();
|
|
model.IncomeTypes = Utils.GetIncomeTypes().ToList();
|
|
model.GLAccounts = Utils.GetGLAccounts().ToList();
|
|
model.CreditDepartments = Utils.GetCreditDepartments().ToList();
|
|
model.Teams = Utils.GetTeams(User.Identity.GetID()).ToList();
|
|
|
|
var id = model.Scenario.Id ?? model.Scenario.TemplateId;
|
|
if (!model.LocalRatesLoaded)
|
|
{
|
|
model.Rates = scenarioManager.GetRates(id);
|
|
}
|
|
else
|
|
{
|
|
var localRates = scenarioManager.GetRatesFromModel(model.Rates);
|
|
var globalRates = DbContext.Rates.Where(x => x.Type == (short)RateModel.RateType.Global).ToList();
|
|
var rates = ratesManager.MergeRates(globalRates, localRates);
|
|
|
|
model.Rates = scenarioManager.GetRatesModel(rates);
|
|
}
|
|
model.Expenditures2Add = GetAllCategories(scenarioTeamIds);
|
|
}
|
|
|
|
if (model.NeedToRecalculateScenarioDetails)
|
|
{
|
|
var rates = new Dictionary<Guid, Dictionary<DateTime, decimal>>();
|
|
foreach (var rateModel in model.Rates)
|
|
{
|
|
var rateValues = new Dictionary<DateTime, decimal>();
|
|
foreach (var rateValue in rateModel.rateValues)
|
|
rateValues.Add(Utils.ConvertFromUnixDate(rateValue.weekEndDate), rateValue.rateValue);
|
|
|
|
rates.Add(rateModel.expCatId, rateValues);
|
|
}
|
|
|
|
var forecast = model.Calendar.GetScenarioDetails(true);
|
|
var actuals = model.Calendar.GetScenarioDetails(false);
|
|
var oldExpenditureTeams = model.Calendar.GetTeams();
|
|
var oldExpenditures = model.Calendar.Expenditures;
|
|
var startDate = Utils.ConvertFromUnixDate(model.Scenario.StartDate);
|
|
var endDate = Utils.ConvertFromUnixDate(model.Scenario.EndDate);
|
|
var tempEndDate = endDate.AddDays(6);
|
|
var fiscalCalendars = (new FiscalCalendarManager(DbContext)).GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, startDate, tempEndDate, true, false);
|
|
var isUpdate = false;
|
|
var currentPeriods = 0;
|
|
var scenarioDetails = scenarioManager.PrepareScenarioDetails(model.Scenario, model.AvailableExpenditures, actuals, rates, forecast, fiscalCalendars, out isUpdate, out currentPeriods);
|
|
|
|
forecast = new List<VW_ScenarioAndProxyDetails>();
|
|
foreach (var ec in scenarioDetails)
|
|
{
|
|
foreach (var entity in ec.Value)
|
|
{
|
|
forecast.Add(new VW_ScenarioAndProxyDetails()
|
|
{
|
|
Id = entity.Id,
|
|
ExpenditureCategoryId = entity.ExpenditureCategoryId,
|
|
ExpenditureCategoryName = string.Empty,
|
|
ExpCategoryWithCcName = entity.ExpenditureCategoryName, // SA. ENV-839
|
|
WeekEndingDate = entity.WeekEndingDate,
|
|
LastUpdate = entity.LastUpdate,
|
|
WeekOrdinal = entity.WeekOrdinal,
|
|
Quantity = entity.Quantity,
|
|
Cost = entity.DetailCost,
|
|
Type = entity.CategoryType.GetHashCode(),
|
|
UseType = entity.UseType.GetHashCode(),
|
|
CGEFX = entity.CG_EFX.ToString(),
|
|
GLId = entity.GlId ?? Guid.Empty,
|
|
UOMId = entity.UOMId ?? Guid.Empty,
|
|
CreditId = entity.CreditId ?? Guid.Empty,
|
|
SystemAttributeOne = entity.SysField1,
|
|
SystemAttributeTwo = entity.SysField2,
|
|
SortOrder = entity.SortOrder
|
|
});
|
|
}
|
|
}
|
|
|
|
model.Calendar = BuildScenarioDetailsCalendarData(forecast, actuals);
|
|
model.AvailableExpenditures = forecast.Where(x => x.ExpenditureCategoryId.HasValue)
|
|
.Select(x => new ExpenditureModel()
|
|
{
|
|
Id = x.ExpenditureCategoryId.Value,
|
|
Name = x.ExpCategoryWithCcName // SA. ENV-839
|
|
}).Distinct().ToList();
|
|
|
|
if (model.Calendar != null && model.Calendar.Expenditures != null)
|
|
{
|
|
#region Filling information about all teams for existence/new ECs
|
|
|
|
var newExpCats = new Dictionary<string, ExpenditureDetail>();
|
|
foreach (var expCat in model.Calendar.Expenditures)
|
|
{
|
|
if (!oldExpenditures.ContainsKey(expCat.Key))
|
|
newExpCats.Add(expCat.Key, expCat.Value);
|
|
else
|
|
{
|
|
// all existence categories should be filled from snapshot data
|
|
if (oldExpenditureTeams.ContainsKey(expCat.Key))
|
|
expCat.Value.Teams = scenarioManager.PrepareScenarioTeams(Guid.Parse(expCat.Key), model.Scenario, fiscalCalendars, oldExpenditureTeams[expCat.Key], isUpdate, currentPeriods);
|
|
|
|
expCat.Value.Collapsed = oldExpenditures[expCat.Key].Collapsed;
|
|
expCat.Value.CollapsedClass = oldExpenditures[expCat.Key].CollapsedClass;
|
|
}
|
|
}
|
|
// need to recalculate team information only for new ECs
|
|
if (newExpCats != null && newExpCats.Count > 0)
|
|
{
|
|
var teams = model.TeamsInScenario == null ? null : model.TeamsInScenario.ToDictionary(x => x.TeamId, g => g.Allocation);
|
|
|
|
// SA. ENV-1001. Removed ProjectId param from the method invoke below. Original code:
|
|
//FillTeamsDetails(model.Scenario.Id ?? model.Scenario.TemplateId, model.Scenario.ParentId.Value, newExpCats, teams);
|
|
scenarioManager.FillTeamsDetails(model.Scenario.Id ?? model.Scenario.TemplateId, newExpCats, HttpContext.User.Identity.GetID(), teams);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Filling information about new only teams for all categories
|
|
|
|
var teams2Add = model.TeamsInScenario != null ? model.TeamsInScenario.Where(x => x.IsNew).ToDictionary(x => x.TeamId, g => g.Allocation) : null;
|
|
if (teams2Add != null && teams2Add.Count > 0)
|
|
{
|
|
model.TeamsInScenario.ForEach(x => x.IsNew = false);
|
|
|
|
// SA. ENV-1001. Removed ProjectId param from the method invoke below. Original code:
|
|
//FillTeamsDetails(model.Scenario.Id ?? model.Scenario.TemplateId, model.Scenario.ParentId.Value, model.Calendar.Expenditures, teams2Add);
|
|
scenarioManager.FillTeamsDetails(model.Scenario.Id ?? model.Scenario.TemplateId, model.Calendar.Expenditures, HttpContext.User.Identity.GetID(), teams2Add);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Recalculation all teams for all categories
|
|
|
|
if (model.NeedToRefreshAllTeams)
|
|
{
|
|
model.TeamsInScenario = scenarioManager.FillTeamsDetails(model.Scenario.Id ?? model.Scenario.TemplateId, model.Calendar.Expenditures, HttpContext.User.Identity.GetID());
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
// after recalculation we need to save all data
|
|
foreach (var expCat in model.Calendar.Expenditures)
|
|
foreach (var detail in expCat.Value.Details)
|
|
detail.Value.Changed = true;
|
|
|
|
model.NeedToRecalculateScenarioDetails = false;
|
|
}
|
|
|
|
return Json(model);
|
|
}
|
|
|
|
private ScenarioCalendarModel BuildScenarioDetailsCalendarData(List<VW_ScenarioAndProxyDetails> forecastDetails, List<VW_ScenarioAndProxyDetails> actualsDetails)
|
|
{
|
|
if (forecastDetails == null)
|
|
forecastDetails = new List<VW_ScenarioAndProxyDetails>();
|
|
|
|
if (actualsDetails == null)
|
|
actualsDetails = new List<VW_ScenarioAndProxyDetails>();
|
|
|
|
var forecastWeekEndings = forecastDetails.Where(x => x.WeekEndingDate.HasValue).Select(x => x.WeekEndingDate.Value).ToList();
|
|
var actualsWeekEndings = actualsDetails.Where(x => x.WeekEndingDate.HasValue).Select(x => x.WeekEndingDate.Value).ToList();
|
|
var weekEndings = forecastWeekEndings.Union(actualsWeekEndings).OrderBy(x => x).ToList();
|
|
|
|
// SA. ENV-667. Get the bound dates for forecast and actuals periods for display. Begin
|
|
var forecastDataExists = (forecastWeekEndings != null) && (forecastWeekEndings.Count > 0);
|
|
var actualsIsExists = (actualsWeekEndings != null && actualsWeekEndings.Count > 0);
|
|
|
|
long forecastDataStartDateMs = 0;
|
|
long forecastDataEndDateMs = 0;
|
|
long actualsViewDataStartDateMs = 0;
|
|
long actualsViewDataEndDateMs = 0;
|
|
long? actualsDataStartDateMs = null;
|
|
long? actualsDataEndDateMs = null;
|
|
|
|
if (forecastDataExists)
|
|
{
|
|
forecastDataStartDateMs = (long)forecastWeekEndings.Min().Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
forecastDataEndDateMs = (long)forecastWeekEndings.Max().Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
actualsViewDataStartDateMs = (long)weekEndings.Min().Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
actualsViewDataEndDateMs = (long)weekEndings.Max().Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
|
|
if (actualsIsExists)
|
|
{
|
|
actualsDataStartDateMs = actualsViewDataStartDateMs;
|
|
actualsDataEndDateMs = (long)actualsWeekEndings.Max().Subtract(Constants.UnixEpochDate).TotalMilliseconds;
|
|
}
|
|
//SA. ENV-667. End
|
|
} // if (forecastDataExists)
|
|
|
|
return new ScenarioCalendarModel()
|
|
{
|
|
Expenditures = (new ScenarioManager(DbContext)).BuildExpendituresForScenario(weekEndings, forecastDetails, actualsDetails),
|
|
Headers = BuildHeadersExForScenario(weekEndings, forecastDataStartDateMs, forecastDataEndDateMs, actualsViewDataStartDateMs, actualsViewDataEndDateMs, actualsDataStartDateMs, actualsDataEndDateMs)
|
|
};
|
|
// SA. ENV-667. End
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Write)]
|
|
public ActionResult SaveSnapshotChanges(ScenarioDetailsSnapshotSaveModel model)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
var scenarioManager = new ScenarioManager(DbContext);
|
|
var scenarioId = scenarioManager.Save(model);
|
|
DbContext.SaveChanges();
|
|
|
|
// need to refresh project access permissions after teams was changed
|
|
(new ProjectAccessCache()).Invalidate();
|
|
|
|
// need to return scenario id to redirect user on saved scenario page
|
|
return Json(new
|
|
{
|
|
ScenarioId = scenarioId
|
|
});
|
|
}
|
|
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]
|
|
[AreaSecurityAttribute(area = Areas.Scenarios, level = AccessLevel.Read)]
|
|
public JsonResult CalculateScenarioDuration(DateTime? startDate, DateTime? endDate)
|
|
{
|
|
if (!(startDate.HasValue && endDate.HasValue))
|
|
return Json(0);
|
|
|
|
var tempEndDate = endDate.Value.AddDays(6);
|
|
var duration = DbContext.FiscalCalendars.Where(t => t.Type == (int)FiscalCalendarModel.FiscalYearType.Week &&
|
|
t.EndDate >= startDate && t.EndDate <= tempEndDate && t.NonWorking == 0 && t.AdjustingPeriod == false)
|
|
.Count();
|
|
return Json(duration);
|
|
}
|
|
}
|
|
} |