976 lines
42 KiB
C#
976 lines
42 KiB
C#
using EnVisage.App_Start;
|
|
using EnVisage.Code.BLL;
|
|
using EnVisage.Models;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Web.Mvc;
|
|
using EnVisage.Code;
|
|
using System.Net;
|
|
using jQuery.DataTables.Mvc;
|
|
using System.Data.Entity.Infrastructure;
|
|
using System.Text;
|
|
using EnVisage.Code.Validation;
|
|
using System.Threading.Tasks;
|
|
using Resources;
|
|
|
|
namespace EnVisage.Controllers
|
|
{
|
|
public class PeopleResourceController : BaseController
|
|
{
|
|
private PeopleResourcesManager _manager = null;
|
|
public PeopleResourcesManager Manager
|
|
{
|
|
get { return _manager ?? (_manager = new PeopleResourcesManager(DbContext)); }
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.PeopleResourceDetails, level = AccessLevel.Read)]
|
|
public ActionResult Details(Guid? resourceId)
|
|
{
|
|
if (!resourceId.HasValue)
|
|
{
|
|
var mngr = new Code.BLL.AccountManager(new EnVisageEntities());
|
|
resourceId = mngr.GetCurrentResourceId(User.Identity.GetID());
|
|
if (resourceId.HasValue)
|
|
{
|
|
return RedirectToAction("Details", "PeopleResource", new { resourceId = resourceId.Value.ToString() });
|
|
}
|
|
return RedirectToAction("AccessDenied", "Home");
|
|
}
|
|
else
|
|
{
|
|
if (!SecurityManager.CheckSecurityObjectPermission(Areas.AccessToOtherResources, AccessLevel.Read))
|
|
{
|
|
var mngr = new Code.BLL.AccountManager(new EnVisageEntities());
|
|
var myResourceId = mngr.GetCurrentResourceId(User.Identity.GetID());
|
|
if ((!Guid.Empty.Equals(resourceId) && !Guid.Empty.Equals(myResourceId) && resourceId != myResourceId) // if this is no my resource
|
|
|| !myResourceId.HasValue // if I don't have any resource assigned
|
|
)
|
|
return RedirectToAction("AccessDenied", "Home"); // then deny access to the specified resource details page
|
|
}
|
|
}
|
|
|
|
var model = Manager.LoadPeopleResource(resourceId.Value, DateTime.Today, false, true, true);
|
|
if (SecurityManager.CheckSecurityObjectPermission(Areas.RD_ResourceNonProjectTime, AccessLevel.Read))
|
|
model.NonProjectTimes = (new NonProjectTimeManager(DbContext)).GetNonProjectTimeList(model.Id, false, false, false);
|
|
|
|
return View(model);
|
|
}
|
|
|
|
[HttpGet]
|
|
[AreaSecurity(area = Areas.RD_ResourceNonProjectTime, level = AccessLevel.Write)]
|
|
public ActionResult LoadNonProjectTime(Guid? Id, Guid? resourceId, Guid? teamId)
|
|
{
|
|
string userId = User.Identity.GetID();
|
|
var accountManager = new AccountManager(DbContext);
|
|
ViewBag.CurrentResourceId = accountManager.GetCurrentResourceId(userId); ;
|
|
|
|
if (Id.HasValue)
|
|
{
|
|
NonProjectTimeManager nptMngr = new NonProjectTimeManager(this.DbContext);
|
|
var npTime = nptMngr.GetNonProjectTime(Id.Value, userId);
|
|
if (npTime == null)
|
|
return HttpNotFound();
|
|
|
|
npTime.TeamId = teamId.HasValue ? teamId.Value : (Guid?)null;
|
|
return PartialView("_scheduleNonProjectTime", npTime);
|
|
}
|
|
else
|
|
{
|
|
Guid? _resId = null;
|
|
DateTime dt = DateTime.UtcNow;
|
|
TeamManager tmMngr = new TeamManager(this.DbContext);
|
|
bool openInTeamMode = !resourceId.HasValue && teamId.HasValue && !teamId.Equals(Guid.Empty);
|
|
|
|
if (openInTeamMode)
|
|
{
|
|
// If team only specified, we get first resource from the team
|
|
// to get it's UOM
|
|
var teamResources = Manager.LoadPeopleResourcesByTeamNonStrict(teamId.Value, false, dt);
|
|
_resId = teamResources.Select(x => x.Id).FirstOrDefault();
|
|
}
|
|
|
|
if (resourceId.HasValue && !resourceId.Equals(Guid.Empty) && !teamId.HasValue)
|
|
{
|
|
// If resource only specified, we get it's team on current Date point
|
|
var resourceTeams = Manager.GetResourceTeams(resourceId.Value, dt);
|
|
teamId = resourceTeams.FirstOrDefault();
|
|
}
|
|
|
|
#region Load Unit of Measure Value
|
|
|
|
decimal resourceUOM;
|
|
Guid? resourceToGetUom = (resourceId.HasValue) ? resourceId.Value : _resId.Value;
|
|
|
|
if (resourceToGetUom.HasValue && !resourceToGetUom.Value.Equals(Guid.Empty))
|
|
resourceUOM = Manager.GetResourceUOM(resourceToGetUom.Value);
|
|
else
|
|
resourceUOM = Manager.GetDafaultUOM();
|
|
|
|
if (resourceUOM == 0)
|
|
throw new UOMNotExistsException();
|
|
|
|
#endregion
|
|
|
|
var newItemModel = new NonProjectTimeModel()
|
|
{
|
|
NonProjectTimeStartDate = DateTime.UtcNow.Date,
|
|
NonProjectTimeEndDate = DateTime.UtcNow.Date.AddDays(14),
|
|
NonProjectTimeCost = 0,
|
|
TeamId = teamId.HasValue ? teamId.Value : (Guid?)null,
|
|
IsTeamAssignmentMode = openInTeamMode
|
|
};
|
|
|
|
newItemModel.Teams = new List<Guid>();
|
|
newItemModel.Resources = new List<Guid>();
|
|
|
|
if (teamId.HasValue)
|
|
newItemModel.Teams.Add(teamId.Value);
|
|
|
|
if (resourceId.HasValue)
|
|
newItemModel.Resources.Add(resourceId.Value);
|
|
|
|
return PartialView("_scheduleNonProjectTime", newItemModel);
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.RD_ResourceNonProjectTime, level = AccessLevel.Write)]
|
|
public ActionResult DeleteNonProjectTime(Guid deleteNPTimeId, Guid id)
|
|
{
|
|
if (ContentLocker.IsLock("NonProjectTime", deleteNPTimeId.ToString(), User.Identity.GetUserName()))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
Guid? resourceId;
|
|
var manager = new NonProjectTimeManager(DbContext);
|
|
if (!SecurityManager.CheckSecurityObjectPermission(Areas.AccessToOtherResources, AccessLevel.Write))
|
|
{
|
|
resourceId = id;
|
|
}
|
|
else
|
|
resourceId = null;
|
|
manager.DeleteNPT(deleteNPTimeId, resourceId);
|
|
this.DbContext.SaveChanges();
|
|
return new HttpStatusCodeResult(HttpStatusCode.OK);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogException(ex);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAjax]
|
|
[ValidateAntiForgeryToken]
|
|
[AreaSecurity(area = Areas.RD_ResourceNonProjectTime, level = AccessLevel.Write)]
|
|
public ActionResult ScheduleNonProjectTime(NonProjectTimeModel model)
|
|
{
|
|
#if DEBUG
|
|
var watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
Logger.Debug("Start ScheduleNonProjectTime");
|
|
#endif
|
|
if (model != null && model.Id != Guid.Empty && ContentLocker.IsLock("NonProjectTime", model.Id.ToString(), User.Identity.GetUserName()))
|
|
{
|
|
ModelState.AddModelError(string.Empty, Messages.NonProjectTime_Schedule_Another_User);
|
|
return new FailedJsonResult(ModelState);
|
|
}
|
|
|
|
try
|
|
{
|
|
if (model == null)
|
|
throw new ArgumentNullException(nameof(model));
|
|
|
|
model.TrimStringProperties();
|
|
|
|
var itemsForValidation = new List<Guid>();
|
|
var manager = new NonProjectTimeManager(DbContext);
|
|
|
|
if (!model.IsTeamAssignmentMode && (model.Resources != null) && (model.Resources.Count > 0))
|
|
{
|
|
// Resources to validate dates by NPT-dates
|
|
itemsForValidation.AddRange(model.Resources);
|
|
}
|
|
|
|
if (model.IsTeamAssignmentMode && (model.Teams != null) && (model.Teams.Count > 0))
|
|
{
|
|
// Teams to validate resources dates by NPT-dates
|
|
itemsForValidation.AddRange(model.Teams);
|
|
}
|
|
|
|
if (!SecurityManager.CheckSecurityObjectPermission(Areas.AccessToOtherResources, AccessLevel.Write))
|
|
{
|
|
var userId = User.Identity.GetID();
|
|
var accountManager = new AccountManager(DbContext);
|
|
var myResourceId = accountManager.GetCurrentResourceId(userId);
|
|
|
|
if (!myResourceId.HasValue)
|
|
{
|
|
//TODO: It's necessary to test this case. Handle this case correctly
|
|
throw new InvalidOperationException("There is no resource associated with current user");
|
|
}
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ScheduleNonProjectTime Validation has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ScheduleNonProjectTime Validation {watch1.ElapsedMilliseconds} ms");
|
|
watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
#endif
|
|
|
|
if ((model.Resources != null) && model.Resources.Any(x => x != myResourceId) && model.Resources.Any(x => x == myResourceId))
|
|
{
|
|
var originalNPT = manager.GetNonProjectTime(model.Id, userId);
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ScheduleNonProjectTime manager.GetNonProjectTime has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ScheduleNonProjectTime manager.GetNonProjectTime {watch1.ElapsedMilliseconds} ms");
|
|
watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
#endif
|
|
|
|
originalNPT.Resources.Remove(myResourceId.Value);
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ScheduleNonProjectTime manager.Save has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ScheduleNonProjectTime manager.Save {watch1.ElapsedMilliseconds} ms");
|
|
watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
#endif
|
|
|
|
manager.Save(originalNPT);
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ScheduleNonProjectTime DbContext.SaveChanges has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ScheduleNonProjectTime DbContext.SaveChanges {watch1.ElapsedMilliseconds} ms");
|
|
watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
#endif
|
|
|
|
DbContext.BulkSaveChanges();
|
|
|
|
model.Resources.RemoveAll(x => x != myResourceId); //remove other resources
|
|
model.Id = Guid.Empty; //to create a new instance
|
|
}
|
|
}
|
|
|
|
manager.Save(model);
|
|
DbContext.BulkSaveChanges();
|
|
|
|
ContentLocker.RemoveLock("NonProjectTime", model.Id.ToString(), User.Identity.GetUserName());
|
|
var result = new NonProjectTimeDatesCheckResultModel();
|
|
|
|
if (itemsForValidation.Count > 0)
|
|
{
|
|
if (!model.IsTeamAssignmentMode)
|
|
{
|
|
// Check resources to fit NPT dates
|
|
result = manager.CheckDateRangeViolationByResources(itemsForValidation, model.NonProjectTimeStartDate, model.NonProjectTimeEndDate);
|
|
}
|
|
else
|
|
{
|
|
// Check resources in teams to fit NPT dates
|
|
result = manager.CheckDateRangeViolationByTeams(itemsForValidation, model.NonProjectTimeStartDate, model.NonProjectTimeEndDate);
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"ScheduleNonProjectTime End has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"ScheduleNonProjectTime End {watch1.ElapsedMilliseconds} ms");
|
|
#endif
|
|
|
|
return new SuccessContentJsonResult(result);
|
|
}
|
|
catch (BLLException blEx) // handle any system specific error
|
|
{
|
|
// display error message if required
|
|
if (blEx.DisplayError)
|
|
ModelState.AddModelError(string.Empty, blEx.Message);
|
|
else // if display not requried then display modal form with general error message
|
|
{
|
|
LogException(blEx);
|
|
ModelState.AddModelError(string.Empty, Messages.NonProjectTime_Schedule_Cannot_Save);
|
|
}
|
|
}
|
|
catch (Exception exception) // handle any unexpected error
|
|
{
|
|
LogException(exception);
|
|
ModelState.AddModelError(string.Empty, Messages.NonProjectTime_Schedule_Cannot_Save);
|
|
}
|
|
|
|
return new FailedJsonResult(ModelState);
|
|
}
|
|
|
|
[HttpGet]
|
|
[AreaSecurity(area = Areas.RD_ResourceNonProjectTime, level = AccessLevel.Read)]
|
|
public ActionResult NonProjectTime(Guid? teamId)
|
|
{
|
|
if (!teamId.HasValue)
|
|
{
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
var model = new PeopleResourceModel();
|
|
var tm = new TeamManager(DbContext);
|
|
model.Team = tm.Load(teamId.Value);
|
|
return View(model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.RD_ResourceNonProjectTime, level = AccessLevel.Read)]
|
|
public JsonResult LoadNonProjectTimeList(JQueryDataTablesModel jQueryDataTablesModel, Guid? teamId, Guid? resourceId, bool permanent, bool ishistory)
|
|
{
|
|
int totalRecordCount;
|
|
int searchRecordCount;
|
|
|
|
var npTimes = GetNonProjectTimeList(teamId, resourceId, permanent, ishistory, startIndex: jQueryDataTablesModel.iDisplayStart,
|
|
pageSize: jQueryDataTablesModel.iDisplayLength, sortedColumns: jQueryDataTablesModel.GetSortedColumns(),
|
|
totalRecordCount: out totalRecordCount, searchRecordCount: out searchRecordCount, searchString: jQueryDataTablesModel.sSearch);
|
|
|
|
return this.DataTablesJson(items: npTimes,
|
|
totalRecords: totalRecordCount,
|
|
totalDisplayRecords: searchRecordCount,
|
|
sEcho: jQueryDataTablesModel.sEcho);
|
|
|
|
}
|
|
public IEnumerable<NonProjectTimeListItemModel> GetNonProjectTimeList(Guid? teamId, Guid? resourceId, bool permanent, bool ishistory, int startIndex,
|
|
int pageSize,
|
|
IEnumerable<SortedColumn> sortedColumns,
|
|
out int totalRecordCount,
|
|
out int searchRecordCount,
|
|
string searchString)
|
|
{
|
|
DateTime dt = DateTime.UtcNow.Date;
|
|
NonProjectTimeManager mngr = new NonProjectTimeManager(this.DbContext);
|
|
List<NonProjectTimeListItemModel> query = new List<NonProjectTimeListItemModel>();
|
|
|
|
if (teamId.HasValue)
|
|
query = mngr.GetNonProjectTimes4Team(teamId.Value, permanent, ishistory, dt);
|
|
|
|
if (resourceId.HasValue)
|
|
query = mngr.GetNonProjectTimeList(resourceId.Value, permanent, ishistory, (!permanent));
|
|
|
|
// Perform ordering
|
|
if (ishistory)
|
|
query = query.OrderByDescending(x => x.StartDate).ThenBy(x => x.EndDate).ThenBy(x => x.Name).ToList();
|
|
else
|
|
{
|
|
if (!permanent)
|
|
query = query.OrderBy(x => x.StartDate).ThenByDescending(x => x.EndDate).ThenBy(x => x.Name).ToList();
|
|
else
|
|
query = query.OrderBy(x => x.StartDate).ThenByDescending(x => x.Name).ToList();
|
|
}
|
|
|
|
totalRecordCount = query.Count();
|
|
searchRecordCount = query.Count();
|
|
return query.Skip(startIndex).Take(pageSize).ToList();
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Resources, level = AccessLevel.Write)]
|
|
public ActionResult Details(PeopleResourceModel model)
|
|
{
|
|
model.TrimStringProperties();
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
Manager.Save(model);
|
|
DbContext.SaveChanges();
|
|
return RedirectToAction("Index");
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
|
|
return View(model);
|
|
}
|
|
|
|
protected void WriteExpendituresListToViewBag(Guid? selectedExpCatId)
|
|
{
|
|
ViewBag.ExpendituresList =
|
|
Utils.GetResourceExpendituresList(selectedExpCatId.HasValue && !selectedExpCatId.Value.Equals(Guid.Empty)
|
|
? selectedExpCatId.Value : Guid.Empty, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fills ViewBag with lists, that used as select control data options on the edit resource form
|
|
/// </summary>
|
|
/// <param name="model"></param>
|
|
protected void InitViewBagLists(PeopleResourceModel model)
|
|
{
|
|
ViewBag.TeamsList = Utils.GetTeamsAvailableForUser(Guid.Parse(User.Identity.GetID()), selectedTeam: model.TeamId);
|
|
ViewBag.WorkWeeks = Utils.GetResourceWorkWeeksList(model.WorkWeekId);
|
|
WriteExpendituresListToViewBag(model.ExpenditureCategoryId);
|
|
|
|
ViewBag.MaxDate = Constants.FISCAL_CALENDAR_MAX_DATE.ToShortDateString();
|
|
|
|
List<DateTime> weekendings = FiscalCalendarManager.GetWeekendingsByRange(DateTime.Today.AddDays(-1),
|
|
Constants.FISCAL_CALENDAR_MAX_DATE, this.DbContext);
|
|
ViewBag.WeekendingsNextDays = weekendings.ToDictionary(k => Utils.ConvertToUnixDate(k), v => v.AddDays(1).ToShortDateString());
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.RD_ResourceInformation, level = AccessLevel.Write)]
|
|
public ActionResult Edit(Guid? resourceId, Guid? teamId, Guid? categoryId, bool? noAdjust, long? start, long? end)
|
|
{
|
|
var model = new PeopleResourceModel();
|
|
model.IsNew = !resourceId.HasValue || resourceId.Value.Equals(Guid.Empty);
|
|
|
|
if (resourceId.HasValue && (resourceId != Guid.Empty))
|
|
model = Manager.LoadPeopleResource(resourceId.Value, DateTime.Today, false, true, false);
|
|
else
|
|
{
|
|
if (teamId.HasValue && teamId.Value != Guid.Empty)
|
|
model.TeamId = teamId.Value;
|
|
|
|
if (categoryId.HasValue && categoryId.Value != Guid.Empty)
|
|
model.ExpenditureCategoryId = categoryId.Value;
|
|
|
|
if (noAdjust.HasValue && noAdjust.Value == true)
|
|
model.ChangeCapacity = false;
|
|
else
|
|
model.ChangeCapacity = true;
|
|
|
|
var workWeek = DbContext.WorkWeeks.FirstOrDefault(x => x.IsDefault);
|
|
if (workWeek != null)
|
|
model.WorkWeekId = workWeek.Id;
|
|
|
|
// If incoming Start Date set (parameter not null), we use parameters to set as resource initial dates.
|
|
// For creating resource with no incoming date parameters, we set End Date as Start Date + 1 year
|
|
DateTime utcToday = DateTime.UtcNow.Date;
|
|
model.StartDate = start.HasValue && start.Value > 0 ? Utils.ConvertFromUnixDate(start.Value).Date : utcToday;
|
|
model.EffectiveChangeDate = model.StartDate > utcToday ? model.StartDate : utcToday;
|
|
|
|
if (end.HasValue && end.Value > 0)
|
|
model.EndDate = model.StartDate < Utils.ConvertFromUnixDate(end.Value) ? Utils.ConvertFromUnixDate(end.Value) : model.StartDate.AddYears(1).Date;
|
|
else
|
|
model.EndDate = (start.HasValue && start.Value > 0) ? (DateTime?)null : model.StartDate.AddYears(1).Date;
|
|
|
|
model.PermanentResource = !model.EndDate.HasValue || model.EndDate.Value == Constants.FISCAL_CALENDAR_MAX_DATE;
|
|
}
|
|
|
|
InitViewBagLists(model);
|
|
|
|
return PartialView("_edit", model);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.RD_ResourceInformation, level = AccessLevel.Write)]
|
|
public ActionResult Edit(PeopleResourceModel model)
|
|
{
|
|
if (model == null || (!model.IsNew && ContentLocker.IsLock("PeopleResource", model.Id.ToString(), User.Identity.GetUserName())))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("PeopleResourceController.Edit method. model:");
|
|
model.DebugObjectProperties(sb);
|
|
Logger.Debug(sb);
|
|
|
|
if (!ModelState.IsValid)
|
|
{
|
|
InitViewBagLists(model);
|
|
return PartialView("_edit", model);
|
|
}
|
|
using (var transaction = DbContext.Database.BeginTransaction())
|
|
{
|
|
var transactionId = DbContext.GetClientConnectionId();
|
|
var userId = User.Identity.GetID();
|
|
|
|
try
|
|
{
|
|
PeopleResourceModel oldModel = null;
|
|
|
|
if (!model.IsNew)
|
|
{
|
|
oldModel = Manager.LoadPeopleResource(model.Id, DateTime.UtcNow.Date, false, false, true);
|
|
|
|
// When select2 for ExpenditureCastegoryId field was disabled for editing in the form,
|
|
// form submit sets to model.ExpenditureCategoryId Guid.Empty value, though the field
|
|
// ExpenditureCategoryId was not changed bu user. We need to restore the old value in this
|
|
// field to prevent the attempts to change ExpCat for PeopleResource in the code below
|
|
if (model.ExpenditureCategoryId.Equals(Guid.Empty))
|
|
model.ExpenditureCategoryId = oldModel.ExpenditureCategoryId;
|
|
|
|
// Perform resource reassignment
|
|
if (Manager.ReassignResource(model, oldModel))
|
|
DbContext.ExecuteBulkSaveChanges();
|
|
}
|
|
|
|
// Perform Resource properties saving
|
|
Manager.Save(model);
|
|
DbContext.ExecuteBulkSaveChanges();
|
|
transaction.Commit();
|
|
Task.Run(() => AuditProxy.CommitHistoryChanges(transactionId, userId));
|
|
|
|
if (!model.IsNew)
|
|
ContentLocker.RemoveLock("PeopleResource", model.Id.ToString(), User.Identity.GetUserName());
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
transaction.Rollback();
|
|
AuditProxy.ClearHistoryChanges(transactionId);
|
|
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
transaction.Rollback();
|
|
AuditProxy.ClearHistoryChanges(transactionId);
|
|
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
if (model.AddMore)
|
|
{
|
|
//TODO: return _edit partial view with a new model to create another one people ("Edit" action with empty resource id). And do not reload ajax form on client
|
|
}
|
|
//InitViewBagLists(model);
|
|
//return View(model);
|
|
return Json(model);
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.RD_ResourceInformation, level = AccessLevel.Write)]
|
|
public ActionResult ChangeResourceExpenditure(Guid resourceId)
|
|
{
|
|
if ((resourceId == null) || resourceId.Equals(Guid.Empty))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
var resourceModel = Manager.LoadPeopleResource(resourceId, DateTime.Today, false, true, true);
|
|
|
|
if (resourceModel != null)
|
|
{
|
|
resourceModel.ChangeCapacity = true;
|
|
var model = new PeopleResourceExpenditureChangeModel(resourceModel);
|
|
|
|
WriteExpendituresListToViewBag(resourceModel.ExpenditureCategoryId);
|
|
|
|
if (resourceModel.ExpenditureCategory != null)
|
|
model.CurrentExpenditureCategoryName = resourceModel.ExpenditureCategory.GetView().ExpCategoryWithCcName;
|
|
|
|
return PartialView("_changeResourceExpCat", model);
|
|
}
|
|
else
|
|
{
|
|
return new HttpStatusCodeResult(HttpStatusCode.NotFound);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogException(ex);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.RD_ResourceInformation, level = AccessLevel.Write)]
|
|
public ActionResult ChangeResourceExpenditure(PeopleResourceExpenditureChangeModel model)
|
|
{
|
|
if (model == null || (model.ResourceId == null) || model.ResourceId.Equals(Guid.Empty) ||
|
|
ContentLocker.IsLock("PeopleResource", model.ResourceId.ToString(), User.Identity.GetUserName()))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
model.TrimStringProperties();
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("PeopleResourceController.ChangeResourceExpenditure method. model:");
|
|
model.DebugObjectProperties(sb);
|
|
Logger.Debug(sb);
|
|
|
|
if (ModelState.IsValid && ((model.ExpenditureCategoryId == null) || model.ExpenditureCategoryId.Equals(Guid.Empty)))
|
|
{
|
|
ModelState.AddModelError("ExpenditureCategoryId", "New Expenditure Category value can't be empty");
|
|
}
|
|
|
|
if (!ModelState.IsValid)
|
|
{
|
|
// Save available to select EC list to ViewBag and current resource's EC name
|
|
WriteExpendituresListToViewBag(model.ExpenditureCategoryId);
|
|
ContentLocker.RemoveLock("PeopleResource", model.ResourceId.ToString(), User.Identity.GetUserName());
|
|
return PartialView("_changeResourceExpCat", model);
|
|
}
|
|
|
|
using (var transaction = DbContext.Database.BeginTransaction())
|
|
{
|
|
var transactionId = DbContext.GetClientConnectionId();
|
|
var userId = User.Identity.GetID();
|
|
|
|
try
|
|
{
|
|
// Perform Resource EC changes
|
|
Manager.ChangeResourceExpenditureCategory(model.ResourceId, model.ExpenditureCategoryId, model.ChangePlannedCapacity);
|
|
// it is more efficient way to save data in this place
|
|
DbContext.SaveChanges();
|
|
transaction.Commit();
|
|
Task.Run(() => AuditProxy.CommitHistoryChanges(transactionId, userId));
|
|
|
|
ContentLocker.RemoveLock("PeopleResource", model.ResourceId.ToString(), User.Identity.GetUserName());
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
transaction.Rollback();
|
|
AuditProxy.ClearHistoryChanges(transactionId);
|
|
|
|
LogException(blEx);
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
transaction.Rollback();
|
|
AuditProxy.ClearHistoryChanges(transactionId);
|
|
|
|
LogException(exception);
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.OK);
|
|
}
|
|
|
|
[HttpPost]
|
|
public JsonResult IsUnique(string emailAddress, Guid id)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(emailAddress))
|
|
return Json(false);
|
|
|
|
try
|
|
{
|
|
var isUnique = !PeopleResourcesManager.ResourceByEmailExists(emailAddress, id);
|
|
|
|
return Json(isUnique);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
return Json(false);
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.RD_ResourceInformation, level = AccessLevel.Write)]
|
|
public ActionResult DisplayResourceReassignForm(Guid id, long date, Guid? newTeamId)
|
|
{
|
|
// Transform incoming date to DateTime
|
|
var effectiveDateOfChange = Utils.ConvertFromUnixDate(date);
|
|
var currentUserId = Guid.Parse(User.Identity.GetID());
|
|
|
|
var model = new PeopleResourceReassignModel()
|
|
{
|
|
EffectiveChangeDate = FiscalCalendarManager.GetDateForTeamMembershipStart(effectiveDateOfChange, DbContext),
|
|
AllocationsReassignmentPlan = Manager.GetResourceSubstitutions(id, effectiveDateOfChange, currentUserId)
|
|
};
|
|
|
|
if ((model.AllocationsReassignmentPlan != null) && (model.AllocationsReassignmentPlan.Count > 0))
|
|
{
|
|
if (newTeamId.HasValue)
|
|
{
|
|
// Mark projects, which already have New Resource Team
|
|
model.AllocationsReassignmentPlan.ForEach(p =>
|
|
p.ProjectHasDestTeam =
|
|
(p.SubstitutionOptions != null) &&
|
|
(p.SubstitutionOptions.Where(t => t.TeamId.Equals(newTeamId.Value)).Count() > 0));
|
|
}
|
|
else
|
|
{
|
|
// Resource doesn't have any new team. Any project for reassignment doesn't has dest. team
|
|
model.AllocationsReassignmentPlan.ForEach(p => p.ProjectHasDestTeam = false);
|
|
}
|
|
|
|
return PartialView("_reassignResource", model);
|
|
}
|
|
|
|
// No project to perform reassignment on
|
|
return new EmptyResult();
|
|
}
|
|
|
|
[AreaSecurity(area = Areas.Resources, level = AccessLevel.Write)]
|
|
public ActionResult Delete(Guid? resourceId)
|
|
{
|
|
if (resourceId.HasValue)
|
|
{
|
|
var model = Manager.LoadPeopleResourceAsIs(resourceId.Value, DateTime.Today);
|
|
|
|
if (model != null)
|
|
{
|
|
model.ChangeCapacity = true;
|
|
return View(model);
|
|
}
|
|
else
|
|
return new HttpStatusCodeResult(HttpStatusCode.NotFound);
|
|
}
|
|
else
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.Resources, level = AccessLevel.Write)]
|
|
// TODO: create model class for current specific case using only required properties instead of PeopleResourceModel
|
|
public ActionResult Delete(PeopleResourceModel model, Guid? teamId)
|
|
{
|
|
if (ContentLocker.IsLock("PeopleResource", model.Id.ToString(), User.Identity.GetUserName()))
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("PeopleResourceController.Delete method. model:");
|
|
model.DebugObjectProperties(sb);
|
|
Logger.Debug(sb);
|
|
|
|
using (var transaction = DbContext.Database.BeginTransaction())
|
|
{
|
|
var transactionId = DbContext.GetClientConnectionId();
|
|
var userId = User.Identity.GetID();
|
|
|
|
try
|
|
{
|
|
var resource = Manager.LoadPeopleResourceAsIs(model.Id, DateTime.Today);
|
|
|
|
if (resource != null)
|
|
{
|
|
resource.ChangeCapacity = model.ChangeCapacity;
|
|
Manager.DeleteResource(resource);
|
|
DbContext.ExecuteBulkSaveChanges();
|
|
transaction.Commit();
|
|
Task.Run(() => AuditProxy.CommitHistoryChanges(transactionId, userId));
|
|
|
|
ContentLocker.RemoveLock("PeopleResource", resource.Id.ToString(), User.Identity.GetUserName());
|
|
return RedirectToAction("Board", "Team", new { @teamId = teamId.Value });
|
|
}
|
|
else
|
|
throw new Exception(String.Format("Resource (Id='{0}') not found", model.Id.ToString()));
|
|
}
|
|
catch (BLLException blEx)
|
|
{
|
|
transaction.Rollback();
|
|
AuditProxy.ClearHistoryChanges(transactionId);
|
|
|
|
if (blEx.DisplayError)
|
|
SetErrorScript(message: blEx.Message);
|
|
else
|
|
{
|
|
LogException(blEx);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
transaction.Rollback();
|
|
AuditProxy.ClearHistoryChanges(transactionId);
|
|
|
|
LogException(exception);
|
|
SetErrorScript();
|
|
}
|
|
}
|
|
|
|
return View(model);
|
|
}
|
|
|
|
[HttpGet]
|
|
public ActionResult PeopleResourceFNameSearch(string term)
|
|
{
|
|
using (var dbContext = new EnVisageEntities())
|
|
{
|
|
var fnames = (from pr in dbContext.PeopleResources.AsNoTracking()
|
|
join p2t in dbContext.PeopleResource2Team.AsNoTracking() on pr.Id equals p2t.PeopleResourceId into gr
|
|
from rel in gr.DefaultIfEmpty()
|
|
where pr.FirstName.StartsWith(term) && pr.IsActiveEmployee == true && rel == null
|
|
select pr.FirstName + "," + pr.LastName).Distinct().ToList();
|
|
return this.Json(fnames,
|
|
JsonRequestBehavior.AllowGet);
|
|
}
|
|
}
|
|
[HttpGet]
|
|
public JsonResult PeopleResourceFullRecord(string name)
|
|
{
|
|
char[] splprm = { ',' };
|
|
string[] parts = name.Split(splprm);
|
|
string firstname = parts[0];
|
|
string lastname = parts[1];
|
|
using (var dbContext = new EnVisageEntities())
|
|
{
|
|
var prRecord = dbContext.PeopleResources.AsNoTracking().Where(x => x.FirstName == firstname && x.LastName == lastname).OrderBy(p => p.FirstName).OrderBy(n => n.LastName);
|
|
PeopleResource pr = prRecord.Select(x => x).FirstOrDefault();
|
|
var pm = new
|
|
{
|
|
Id = pr.Id,
|
|
IsActiveEmployee = pr.IsActiveEmployee,
|
|
FirstName = pr.FirstName,
|
|
LastName = pr.LastName,
|
|
ExpenditureCategoryId = pr.ExpenditureCategoryId,
|
|
StartDate = String.Format("{0:M/d/yyyy}", pr.StartDate),
|
|
EndDate = String.Format("{0:M/d/yyyy}", pr.EndDate),
|
|
EmailAddress = pr.Email,
|
|
EmployeeID = pr.EmployeeID
|
|
};
|
|
JsonResult rs = this.Json(pm, JsonRequestBehavior.AllowGet);
|
|
return rs;
|
|
|
|
}
|
|
}
|
|
|
|
[HttpPost]
|
|
[AreaSecurity(area = Areas.RD_ResourceNonProjectTime, level = AccessLevel.Write)]
|
|
public ActionResult RecalculateNPTimeAllocations(NonProjectTimeRecalculationModel model)
|
|
{
|
|
if (model == null || !model.StartDate.HasValue)
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
|
|
try
|
|
{
|
|
var fcManager = (new FiscalCalendarManager(DbContext));
|
|
var nptManager = (new NonProjectTimeManager(DbContext));
|
|
|
|
var firstWeek = fcManager.GetFirstWeek();
|
|
if (firstWeek == null)
|
|
throw new InvalidOperationException("Cannot get the first week of fiscal calendar. Seems like it does not exist.");
|
|
|
|
var lastWeek = fcManager.GetLastWeek();
|
|
if (lastWeek == null)
|
|
throw new InvalidOperationException("Cannot get the last week of fiscal calendar. Seems like it does not exist.");
|
|
|
|
// if user has selected period after fiscal calendar ends
|
|
var rangeStartDate = Utils.ConvertFromUnixDate(model.StartDate.Value);
|
|
if (lastWeek.EndDate < rangeStartDate)
|
|
return null;
|
|
|
|
if (model.EndDate.HasValue)
|
|
{
|
|
// if user has selected period befor fiscal calendar starts
|
|
var rangeEndDate = Utils.ConvertFromUnixDate(model.EndDate.Value);
|
|
if (rangeEndDate < firstWeek.StartDate)
|
|
return null;
|
|
|
|
var week = fcManager.GetFirstWeek(rangeEndDate);
|
|
// if selected end date after fiscal calendar ends we should take the last calendar week
|
|
var weeks = fcManager.GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, rangeStartDate, (week ?? lastWeek).EndDate, true, null, false);
|
|
var allocations = nptManager.RecalculateNPTimeAllocations(rangeStartDate, rangeEndDate, weeks, model.ResourceIds, model.TeamIds);
|
|
|
|
return Json(allocations);
|
|
}
|
|
else
|
|
{
|
|
var weeks = fcManager.GetFiscalCalendar(FiscalCalendarModel.FiscalYearType.Week, rangeStartDate, rangeStartDate, true, null, true);
|
|
var allocations = nptManager.RecalculateNPTimeAllocations(rangeStartDate, rangeStartDate, weeks, model.ResourceIds, model.TeamIds);
|
|
var weekending = weeks.Count > 1 ? lastWeek.EndDate : weeks.First().EndDate;
|
|
var uomValue = 0;
|
|
|
|
if (allocations != null && allocations.Count == 1)
|
|
{
|
|
foreach (var item in allocations.Values)
|
|
{
|
|
uomValue = Convert.ToInt32(item.UOMValue);
|
|
break;
|
|
}
|
|
}
|
|
// we should show only one slider/input for permanent non-project time with distribution of 100%
|
|
var weekDistribution = new NonProjectTimeDistributionModel()
|
|
{
|
|
StartDate = model.StartDate.Value,
|
|
EndDate = Utils.ConvertToUnixDate(weekending), // Utils.ConvertToUnixDate(lastWeek.EndDate),
|
|
|
|
HoursOff = uomValue,
|
|
UOMValue = uomValue,
|
|
PercentOff = 100
|
|
};
|
|
allocations = new Dictionary<string, NonProjectTimeDistributionModel>()
|
|
{
|
|
{ weekDistribution.EndDate.ToString(), weekDistribution }
|
|
};
|
|
|
|
|
|
return Json(allocations);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogException(ex);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult LoadResourcesWithTeams4Resources(List<Guid> model)
|
|
{
|
|
if (model == null || !model.Any())
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
var dictionary = new Dictionary<string, PeopleResourceWithTeamsApiModel>();
|
|
var resources = (new PeopleResourcesManager(DbContext)).GetPeopleResourceWithTeams4Resources(model);
|
|
if (resources != null && resources.Any())
|
|
{
|
|
dictionary = resources.Select(x => new PeopleResourceWithTeamsApiModel(x))
|
|
.ToDictionary(x => x.Id.ToString());
|
|
}
|
|
|
|
return Json(dictionary);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateJsonAntiForgeryToken]
|
|
public ActionResult LoadResourcesWithTeams4Teams(List<Guid> model)
|
|
{
|
|
if (model == null || !model.Any())
|
|
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
|
|
|
|
try
|
|
{
|
|
var dictionary = new Dictionary<string, PeopleResourceWithTeamsApiModel>();
|
|
var resources = (new PeopleResourcesManager(DbContext)).GetPeopleResourceWithTeams4Teams(model);
|
|
if (resources != null && resources.Any())
|
|
{
|
|
dictionary = resources.Select(x => new PeopleResourceWithTeamsApiModel(x))
|
|
.ToDictionary(x => x.Id.ToString());
|
|
}
|
|
|
|
return Json(dictionary);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
LogException(exception);
|
|
}
|
|
|
|
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
} |