EnVisageOnline/Main/Source/EnVisage/Code/Audit/Trackers/ScenarioDetailsEventsTracke...

1490 lines
54 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using EnVisage.Code.BLL;
using EnVisage.Models;
using NLog;
using Prevu.Core.Audit.Model;
namespace EnVisage.Code.Audit
{
public enum ScenarioDetailsEventsTrackerUsageMode
{
None = 0,
ScenarioDetails = 1,
ActivityCalendar = 2
}
public class ScenarioDetailsEventsTracker : IDisposable
{
#region Constants and internal classes
protected const string C_EXPCAT_VALUE_TEXT_TEMPLATE = "{0}, {1} ({2})";
protected const string C_TEAM_VALUE_TEXT_TEMPLATE = "{0}, {1}";
protected const string C_TEAM_COMMENT_TEXT_TEMPLATE = "Category: '{0}'";
protected const string C_RESOURCE_VALUE_TEXT_TEMPLATE = "{0}, {1}";
protected const string C_RESOURCE_COMMENT_TEXT_TEMPLATE = "Category: '{0}', Team: '{1}'";
protected const string C_CURVING_VALUE_TEXT_TEMPLATE = "Curve from Category '{0}' was applied to '{1}'";
protected const string C_REALLOCATION_VALUE_TEXT_TEMPLATE = "'{0}' from {1} to {2} by {3}. Curve from '{4}'";
protected const string C_ROUNDING_VALUE_TEXT_TEMPLATE = "'{0}' from {1} to {2} with decimal places: '{3}'";
protected const string C_AUTOMATIC_UPDATE_COMMENT_TEXT_TEMPLATE = "Updated automatically";
protected const string C_EXPCAT_UNDEFINED_TEXT_TEMPLATE = "N/A Category {0}";
protected const string C_TEAM_UNDEFINED_TEXT_TEMPLATE = "N/A Team {0}";
protected const string C_RESOURCE_UNDEFINED_TEXT_TEMPLATE = "N/A Resource {0}";
protected Dictionary<ScenarioDetailsEventsTrackerUsageMode, Dictionary<TrackableEntity, Dictionary<ChangeAction, string>>> classificationKeys = null;
protected enum ChangeAction
{
[DisplayValue("None")]
None,
[DisplayValue("Added")]
Added,
[DisplayValue("Updated")]
Updated,
[DisplayValue("Deleted")]
Deleted
}
protected enum TrackableEntity
{
[DisplayValue("None")]
None,
[DisplayValue("Expenditure Category")]
ExpenditureCategory,
[DisplayValue("Expenditure Category Allocation")]
ExpenditureCategoryAllocation,
[DisplayValue("Team")]
Team,
[DisplayValue("Team Allocation")]
TeamAllocation,
[DisplayValue("Resource")]
Resource,
[DisplayValue("Resource Allocation")]
ResourceAllocation
}
protected class ValueChangeTraceBase
{
public Guid scenarioId = Guid.Empty;
public ChangeAction action = ChangeAction.None;
public decimal oldQuantityValue = 0;
public decimal newQuantityValue = 0;
public bool AutomaticUpdate = false;
}
protected class ScenarioDetailValueChangeTrace : ValueChangeTraceBase
{
public Guid ExpenditureCategoryId;
public decimal oldCostValue = 0;
public decimal newCostValue = 0;
}
protected class TeamAllocationValueChangeTrace : ScenarioDetailValueChangeTrace
{
public Guid TeamId;
}
protected class ResourceAllocationValueChangeTrace : TeamAllocationValueChangeTrace
{
public Guid ResourceId;
}
protected class ScenarioResource
{
public Guid ResourceId;
public Guid TeamId;
public Guid ExpenditureCategoryId;
}
#endregion
public ScenarioDetailsEventsTracker(ScenarioDetailsEventsTrackerUsageMode usage, EnVisageEntities dbContext, string userId)
{
this.usageMode = usage;
this.dbContext = dbContext;
this.userId = userId;
InitClassificationKeys();
}
protected static readonly Logger Logger = LogManager.GetCurrentClassLogger();
protected ScenarioDetailsEventsTrackerUsageMode usageMode = ScenarioDetailsEventsTrackerUsageMode.None;
protected EnVisageEntities dbContext = null;
protected Guid projectId = Guid.Empty;
protected Guid scenarioId = Guid.Empty;
protected string userId = String.Empty;
protected Dictionary<Guid, Guid> scenario2project = new Dictionary<Guid, Guid>();
protected Dictionary<Guid, string> expenditureCategories = null;
protected Dictionary<Guid, string> teams = null;
protected Dictionary<Guid, string> resources = null;
protected Dictionary<Guid, List<Guid>> expendituresAdded = new Dictionary<Guid, List<Guid>>();
protected Dictionary<Guid, List<Guid>> teamsAdded = new Dictionary<Guid, List<Guid>>();
protected Dictionary<Guid, List<ScenarioResource>> resourcesAdded = new Dictionary<Guid, List<ScenarioResource>>();
protected List<ScenarioDetailValueChangeTrace> scenarioDetailsChanges = new List<ScenarioDetailValueChangeTrace>();
protected List<TeamAllocationValueChangeTrace> teamAllocationChanges = new List<TeamAllocationValueChangeTrace>();
protected List<ResourceAllocationValueChangeTrace> resourceAllocationChanges = new List<ResourceAllocationValueChangeTrace>();
protected Dictionary<Guid, Dictionary<long, ExpenditureCategoryChangeCurveModel>> curveChanges = new Dictionary<Guid, Dictionary<long, ExpenditureCategoryChangeCurveModel>>();
protected Dictionary<Guid, Dictionary<long, ExpenditureCategoryReallocateResourceModel>> reallocations = new Dictionary<Guid, Dictionary<long, ExpenditureCategoryReallocateResourceModel>>();
protected Dictionary<Guid, Dictionary<long, ExpenditureCategoryRoundModel>> roundings = new Dictionary<Guid, Dictionary<long, ExpenditureCategoryRoundModel>>();
private bool NoScenarioIdSpecified
{
get
{
var result = false;
if (scenarioId.Equals(Guid.Empty))
{
result = true;
Logger?.Error("Scenario Details Audit: scenarioId if not specified. Tracking is unavailable. Use 'StartTracking' method to set scenarioId");
}
if (projectId.Equals(Guid.Empty))
{
result = true;
Logger?.Error("Scenario Details Audit: projectId if not specified. Tracking is unavailable. Use 'StartTracking' method to set projectId");
}
if (usageMode == ScenarioDetailsEventsTrackerUsageMode.None)
{
result = true;
Logger?.Error("Scenario Details Audit: Tracking usage mode not specified. Tracking is unavailable");
}
return result;
}
}
public void StartTracking(Guid scenarioId, Guid projectId)
{
this.scenarioId = scenarioId;
this.projectId = projectId;
// Store scenario to project link
if (!scenarioId.Equals(Guid.Empty) && !projectId.Equals(Guid.Empty) && !scenario2project.ContainsKey(scenarioId))
scenario2project.Add(scenarioId, projectId);
}
public void Dispose()
{
scenario2project?.Clear();
expenditureCategories?.Clear();
teams?.Clear();
resources?.Clear();
if (expendituresAdded != null)
{
expendituresAdded.Values.ToList().ForEach(x => x.Clear());
expendituresAdded.Clear();
}
if (teamsAdded != null)
{
teamsAdded.Values.ToList().ForEach(x => x.Clear());
teamsAdded.Clear();
}
if (resourcesAdded != null)
{
resourcesAdded.Values.ToList().ForEach(x => x.Clear());
resourcesAdded.Clear();
}
scenarioDetailsChanges?.Clear();
teamAllocationChanges?.Clear();
resourceAllocationChanges?.Clear();
if (curveChanges != null)
{
curveChanges.Values.ToList().ForEach(x => x.Clear());
curveChanges.Clear();
}
if (reallocations != null)
{
reallocations.Values.ToList().ForEach(x => x.Clear());
reallocations.Clear();
}
if (roundings != null)
{
roundings.Values.ToList().ForEach(x => x.Clear());
roundings.Clear();
}
scenario2project = null;
expenditureCategories = null;
teams = null;
resources = null;
expendituresAdded = null;
teamsAdded = null;
resourcesAdded = null;
scenarioDetailsChanges = null;
teamAllocationChanges = null;
resourceAllocationChanges = null;
curveChanges = null;
reallocations = null;
roundings = null;
}
protected void InitClassificationKeys()
{
classificationKeys = new Dictionary<ScenarioDetailsEventsTrackerUsageMode, Dictionary<TrackableEntity, Dictionary<ChangeAction, string>>>()
{
#region Classification keys for Scenario Details
{
ScenarioDetailsEventsTrackerUsageMode.ScenarioDetails, new Dictionary<TrackableEntity, Dictionary<ChangeAction, string>>()
{
{
TrackableEntity.ExpenditureCategory, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ScenarioDetailsECAdded" },
{ ChangeAction.Deleted, "ScenarioDetailsECDeleted" }
}
},
{
TrackableEntity.Team, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ScenarioDetailsTeamAdded" },
{ ChangeAction.Deleted, "ScenarioDetailsTeamDeleted" }
}
},
{
TrackableEntity.Resource, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ScenarioDetailsResourceAdded" },
{ ChangeAction.Deleted, "ScenarioDetailsResourceDeleted" }
}
},
{
TrackableEntity.ExpenditureCategoryAllocation, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ScenarioDetailsECAllocationsEntered" },
{ ChangeAction.Updated, "ScenarioDetialsECAllocationsEdited" }
}
},
{
TrackableEntity.TeamAllocation, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ScenarioDetailsTeamAllocationsEntered" },
{ ChangeAction.Updated, "ScenarioDetailsTeamAllocationsEdited" }
}
},
{
TrackableEntity.ResourceAllocation, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ScenarioDetailsResourceAllocationsEntered" },
{ ChangeAction.Updated, "ScenarioDetailsResourceAllocationsEdited" }
}
}
}
},
#endregion
#region Classification keys for Activity Calecndar
{
ScenarioDetailsEventsTrackerUsageMode.ActivityCalendar, new Dictionary<TrackableEntity, Dictionary<ChangeAction, string>>()
{
{
TrackableEntity.Team, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ActivityCalendarTeamAdded" }
}
},
{
TrackableEntity.Resource, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ActivityCalendarResourceAdded" },
{ ChangeAction.Deleted, "ActivityCalendarResourceDeleted" }
}
},
{
TrackableEntity.ExpenditureCategoryAllocation, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Updated, "ActivityCalendarECAllocationsEdited" }
}
},
{
TrackableEntity.TeamAllocation, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ActivityCalendarTeamAllocationsEntered" },
{ ChangeAction.Updated, "ActivityCalendarTeamAllocationsEdited" }
}
},
{
TrackableEntity.ResourceAllocation, new Dictionary<ChangeAction, string>()
{
{ ChangeAction.Added, "ActivityCalendarResourceAllocationsEntered" },
{ ChangeAction.Updated, "ActivityCalendarResourceAllocationsEdited" }
}
}
}
}
#endregion
};
}
#region Scenario Details tracking
public void ExpenditureCategoryAddedToScenario(Guid id)
{
if (!id.Equals(Guid.Empty))
{
if (this.NoScenarioIdSpecified)
return;
if (!expendituresAdded.ContainsKey(this.scenarioId))
expendituresAdded.Add(this.scenarioId, new List<Guid>());
var scenarioData = expendituresAdded[this.scenarioId];
if (!scenarioData.Contains(id))
scenarioData.Add(id);
}
}
public void ExpenditureCategoriesAddedToScenario(IEnumerable<Guid> expCats)
{
if (expCats != null)
{
if (this.NoScenarioIdSpecified)
return;
if (!expendituresAdded.ContainsKey(this.scenarioId))
expendituresAdded.Add(this.scenarioId, new List<Guid>());
var scenarioData = expendituresAdded[this.scenarioId];
var itemsToAdd = expCats.Except(expendituresAdded[this.scenarioId]);
scenarioData.AddRange(itemsToAdd);
}
}
public void ScenarioDetailsValueChanged(ScenarioDetail updatedItem, decimal? oldQuantityValue, decimal? oldCostValue, bool automaticUpdate = false)
{
if (updatedItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if ((oldQuantityValue == updatedItem.Quantity) && (oldCostValue == updatedItem.Cost))
// No changes found
return;
if (!updatedItem.ExpenditureCategoryId.HasValue || updatedItem.ExpenditureCategoryId.Value.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in ScenarioDetails item is empty");
ScenarioDetailValueChangeTrace trackingInfo = new ScenarioDetailValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Updated,
scenarioId = this.scenarioId,
ExpenditureCategoryId = updatedItem.ExpenditureCategoryId.Value,
oldQuantityValue = oldQuantityValue ?? 0,
oldCostValue = oldCostValue ?? 0,
newQuantityValue = updatedItem.Quantity ?? 0,
newCostValue = updatedItem.Cost ?? 0,
};
scenarioDetailsChanges.Add(trackingInfo);
}
public void ScenarioDetailsValueAdded(ScenarioDetail createdItem, bool automaticUpdate = false)
{
if (createdItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (!createdItem.ExpenditureCategoryId.HasValue || createdItem.ExpenditureCategoryId.Value.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in ScenarioDetails item is empty");
ScenarioDetailValueChangeTrace trackingInfo = new ScenarioDetailValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Added,
scenarioId = this.scenarioId,
ExpenditureCategoryId = createdItem.ExpenditureCategoryId.Value,
newQuantityValue = createdItem.Quantity ?? 0,
newCostValue = createdItem.Cost ?? 0,
};
scenarioDetailsChanges.Add(trackingInfo);
}
public void ScenarioDetailsValuesAdded(IEnumerable<ScenarioDetail> createdItems, bool automaticUpdate = false)
{
if (createdItems == null)
return;
if (this.NoScenarioIdSpecified)
return;
var invalidItems = createdItems.Where(x => !x.ExpenditureCategoryId.HasValue || x.ExpenditureCategoryId.Value.Equals(Guid.Empty));
if (invalidItems.Any())
Logger.Error("Scenario Details Audit: At least one item in the collection has empty ExpenditureCategoryId");
var trackingInfoItems = createdItems.Select(x => new ScenarioDetailValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Added,
scenarioId = this.scenarioId,
ExpenditureCategoryId = x.ExpenditureCategoryId.Value,
newQuantityValue = x.Quantity ?? 0,
newCostValue = x.Cost ?? 0,
}).ToList();
scenarioDetailsChanges.AddRange(trackingInfoItems);
}
public void ScenarioDetailsValueRemoved(ScenarioDetail queuedToRemoveItem, bool automaticUpdate = false)
{
if (queuedToRemoveItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (!queuedToRemoveItem.ExpenditureCategoryId.HasValue || queuedToRemoveItem.ExpenditureCategoryId.Value.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in ScenarioDetails item is empty");
ScenarioDetailValueChangeTrace trackingInfo = new ScenarioDetailValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Deleted,
scenarioId = this.scenarioId,
ExpenditureCategoryId = queuedToRemoveItem.ExpenditureCategoryId.Value,
oldQuantityValue = queuedToRemoveItem.Quantity ?? 0,
oldCostValue = queuedToRemoveItem.Cost ?? 0,
};
scenarioDetailsChanges.Add(trackingInfo);
}
public void ScenarioDetailsValuesRemoved(IEnumerable<ScenarioDetail> queuedToRemoveItems, bool automaticUpdate = false)
{
if (queuedToRemoveItems == null)
return;
if (this.NoScenarioIdSpecified)
return;
var invalidItems = queuedToRemoveItems.Where(x => !x.ExpenditureCategoryId.HasValue || x.ExpenditureCategoryId.Value.Equals(Guid.Empty));
if (invalidItems.Any())
Logger.Error("Scenario Details Audit: At least one item in the collection has empty ExpenditureCategoryId");
var trackingInfoItems = queuedToRemoveItems.Select(x => new ScenarioDetailValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Deleted,
scenarioId = this.scenarioId,
ExpenditureCategoryId = x.ExpenditureCategoryId.Value,
oldQuantityValue = x.Quantity ?? 0,
oldCostValue = x.Cost ?? 0,
}).ToList();
scenarioDetailsChanges.AddRange(trackingInfoItems);
}
#endregion
#region Team Allocations tracking
public void TeamAddedToScenario(Guid id)
{
if (!id.Equals(Guid.Empty))
{
if (this.NoScenarioIdSpecified)
return;
if (!teamsAdded.ContainsKey(this.scenarioId))
teamsAdded.Add(this.scenarioId, new List<Guid>());
var scenarioData = teamsAdded[this.scenarioId];
if (!scenarioData.Contains(id))
scenarioData.Add(id);
}
}
public void TeamsAddedToScenario(IEnumerable<Guid> teams)
{
if (teams != null)
{
if (this.NoScenarioIdSpecified)
return;
if (!teamsAdded.ContainsKey(this.scenarioId))
teamsAdded.Add(this.scenarioId, new List<Guid>());
var scenarioData = teamsAdded[this.scenarioId];
var itemsToAdd = teams.Except(scenarioData);
scenarioData.AddRange(itemsToAdd);
}
}
public void TeamAllocationValueChanged(TeamAllocation updatedItem, decimal oldQuantityValue, bool automaticUpdate = false)
{
if (updatedItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (oldQuantityValue == updatedItem.Quantity)
// No changes found
return;
if (updatedItem.ExpenditureCategoryId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in TeamAllocations item is empty");
if (updatedItem.TeamId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: TeamId in TeamAllocations item is empty");
var trackingInfo = new TeamAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Updated,
scenarioId = this.scenarioId,
ExpenditureCategoryId = updatedItem.ExpenditureCategoryId,
TeamId = updatedItem.TeamId,
oldQuantityValue = oldQuantityValue,
newQuantityValue = updatedItem.Quantity
};
teamAllocationChanges.Add(trackingInfo);
}
public void TeamAllocationValueAdded(TeamAllocation createdItem, bool automaticUpdate = false)
{
if (createdItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (createdItem.ExpenditureCategoryId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in TeamAllocations item is empty");
if (createdItem.TeamId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: TeamId in TeamAllocations item is empty");
var trackingInfo = new TeamAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Added,
scenarioId = this.scenarioId,
ExpenditureCategoryId = createdItem.ExpenditureCategoryId,
TeamId = createdItem.TeamId,
newQuantityValue = createdItem.Quantity
};
teamAllocationChanges.Add(trackingInfo);
}
public void TeamAllocationValuesAdded(IEnumerable<TeamAllocation> createdItems, bool automaticUpdate = false)
{
if (createdItems == null)
return;
if (this.NoScenarioIdSpecified)
return;
var invalidItems = createdItems.Where(x => x.ExpenditureCategoryId.Equals(Guid.Empty) || x.TeamId.Equals(Guid.Empty));
if (invalidItems.Any())
Logger.Error("Scenario Details Audit: At least one TeamAllocations item in collection has empty ExpenditureCategoryId or TeamId");
var trackingInfoItems = createdItems.Select(x => new TeamAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Added,
scenarioId = this.scenarioId,
ExpenditureCategoryId = x.ExpenditureCategoryId,
TeamId = x.TeamId,
newQuantityValue = x.Quantity
}).ToList();
teamAllocationChanges.AddRange(trackingInfoItems);
}
public void TeamAllocationValueRemoved(TeamAllocation queuedToRemoveItem, bool automaticUpdate = false)
{
if (queuedToRemoveItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (queuedToRemoveItem.ExpenditureCategoryId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in TeamAllocations item is empty");
if (queuedToRemoveItem.TeamId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: TeamId in TeamAllocations item is empty");
var trackingInfo = new TeamAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Deleted,
scenarioId = this.scenarioId,
ExpenditureCategoryId = queuedToRemoveItem.ExpenditureCategoryId,
TeamId = queuedToRemoveItem.TeamId,
oldQuantityValue = queuedToRemoveItem.Quantity
};
teamAllocationChanges.Add(trackingInfo);
}
public void TeamAllocationValuesRemoved(IEnumerable<TeamAllocation> queuedToRemoveItems, bool automaticUpdate = false)
{
if (queuedToRemoveItems == null)
return;
if (this.NoScenarioIdSpecified)
return;
var invalidItems = queuedToRemoveItems.Where(x => x.ExpenditureCategoryId.Equals(Guid.Empty) || x.TeamId.Equals(Guid.Empty));
if (invalidItems.Any())
Logger.Error("Scenario Details Audit: At least one TeamAllocations item in collection has empty ExpenditureCategoryId or TeamId");
var trackingInfoItems = queuedToRemoveItems.Select(x => new TeamAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Deleted,
scenarioId = this.scenarioId,
ExpenditureCategoryId = x.ExpenditureCategoryId,
TeamId = x.TeamId,
oldQuantityValue = x.Quantity
}).ToList();
teamAllocationChanges.AddRange(trackingInfoItems);
}
#endregion
#region Resource Allocations tracking
public void ResourceAddedToScenario(Guid resourceId, Guid teamId, Guid expCatId)
{
if (resourceId.Equals(Guid.Empty) || teamId.Equals(Guid.Empty) || expCatId.Equals(Guid.Empty))
return;
if (this.NoScenarioIdSpecified)
return;
if (!resourcesAdded.ContainsKey(this.scenarioId))
resourcesAdded.Add(this.scenarioId, new List<ScenarioResource>());
var scenarioData = resourcesAdded[this.scenarioId];
if (!scenarioData.Any(x =>
x.ExpenditureCategoryId.Equals(expCatId) &&
x.TeamId.Equals(teamId) &&
x.ResourceId.Equals(resourceId)))
{
var item = new ScenarioResource()
{
ExpenditureCategoryId = expCatId,
TeamId = teamId,
ResourceId = resourceId
};
scenarioData.Add(item);
}
}
public void ResourceAllocationValueChanged(PeopleResourceAllocation updatedItem, decimal oldQuantityValue, bool automaticUpdate = false)
{
if (updatedItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (oldQuantityValue == updatedItem.Quantity)
// No changes found
return;
if (updatedItem.ExpenditureCategoryId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in PeopleResourceAllocations item is empty");
if (updatedItem.TeamId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: TeamId in PeopleResourceAllocations item is empty");
if (updatedItem.PeopleResourceId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: PeopleResourceId in PeopleResourceAllocations item is empty");
var trackingInfo = new ResourceAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Updated,
scenarioId = this.scenarioId,
ExpenditureCategoryId = updatedItem.ExpenditureCategoryId,
TeamId = updatedItem.TeamId,
ResourceId = updatedItem.PeopleResourceId,
oldQuantityValue = oldQuantityValue,
newQuantityValue = updatedItem.Quantity
};
resourceAllocationChanges.Add(trackingInfo);
}
public void ResourceAllocationValueAdded(PeopleResourceAllocation createdItem, bool automaticUpdate = false)
{
if (createdItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (createdItem.ExpenditureCategoryId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in PeopleResourceAllocations item is empty");
if (createdItem.TeamId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: TeamId in PeopleResourceAllocations item is empty");
if (createdItem.PeopleResourceId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: PeopleResourceId in PeopleResourceAllocations item is empty");
var trackingInfo = new ResourceAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Added,
scenarioId = this.scenarioId,
ExpenditureCategoryId = createdItem.ExpenditureCategoryId,
TeamId = createdItem.TeamId,
ResourceId = createdItem.PeopleResourceId,
newQuantityValue = createdItem.Quantity
};
resourceAllocationChanges.Add(trackingInfo);
}
public void ResourceAllocationValuesAdded(IEnumerable<PeopleResourceAllocation> createdItems, bool automaticUpdate = false)
{
if (createdItems == null)
return;
if (this.NoScenarioIdSpecified)
return;
var invalidItems = createdItems.Where(x => x.ExpenditureCategoryId.Equals(Guid.Empty) || x.TeamId.Equals(Guid.Empty) || x.PeopleResourceId.Equals(Guid.Empty));
if (invalidItems.Any())
Logger.Error("Scenario Details Audit: At least one PeopleResourceAllocations item in collection has empty ExpenditureCategoryId, TeamId or PeopleResourceId");
var trackingInfoItems = createdItems.Select(x => new ResourceAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Added,
scenarioId = this.scenarioId,
ExpenditureCategoryId = x.ExpenditureCategoryId,
TeamId = x.TeamId,
ResourceId = x.PeopleResourceId,
newQuantityValue = x.Quantity
}).ToList();
resourceAllocationChanges.AddRange(trackingInfoItems);
}
public void ResourceAllocationValueRemoved(PeopleResourceAllocation queuedToRemoveItem, bool automaticUpdate = false)
{
if (queuedToRemoveItem == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (queuedToRemoveItem.ExpenditureCategoryId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: ExpenditureCategoryId in PeopleResourceAllocations item is empty");
if (queuedToRemoveItem.TeamId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: TeamId in PeopleResourceAllocations item is empty");
if (queuedToRemoveItem.PeopleResourceId.Equals(Guid.Empty))
Logger.Error("Scenario Details Audit: PeopleResourceId in PeopleResourceAllocations item is empty");
var trackingInfo = new ResourceAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Deleted,
scenarioId = this.scenarioId,
ExpenditureCategoryId = queuedToRemoveItem.ExpenditureCategoryId,
TeamId = queuedToRemoveItem.TeamId,
ResourceId = queuedToRemoveItem.PeopleResourceId,
oldQuantityValue = queuedToRemoveItem.Quantity
};
resourceAllocationChanges.Add(trackingInfo);
}
public void ResourceAllocationValuesRemoved(IEnumerable<PeopleResourceAllocation> queuedToRemoveItems, bool automaticUpdate = false)
{
if (queuedToRemoveItems == null)
return;
if (this.NoScenarioIdSpecified)
return;
var invalidItems = queuedToRemoveItems.Where(x => x.ExpenditureCategoryId.Equals(Guid.Empty) || x.TeamId.Equals(Guid.Empty) || x.PeopleResourceId.Equals(Guid.Empty));
if (invalidItems.Any())
Logger.Error("Scenario Details Audit: At least one PeopleResourceAllocations item in collection has empty ExpenditureCategoryId, TeamId or PeopleResourceId");
var trackingInfoItems = queuedToRemoveItems.Select(x => new ResourceAllocationValueChangeTrace()
{
AutomaticUpdate = automaticUpdate,
action = ChangeAction.Deleted,
scenarioId = this.scenarioId,
ExpenditureCategoryId = x.ExpenditureCategoryId,
TeamId = x.TeamId,
ResourceId = x.PeopleResourceId,
oldQuantityValue = x.Quantity
}).ToList();
resourceAllocationChanges.AddRange(trackingInfoItems);
}
#endregion
#region Client side events tracking
public void AddClientSideActions(ExpenditureCategoryChangesHistory clientSideActions)
{
if (clientSideActions == null)
return;
if (this.NoScenarioIdSpecified)
return;
if (clientSideActions.CurveChanges != null)
{
if (!curveChanges.ContainsKey(this.scenarioId))
curveChanges.Add(this.scenarioId, new Dictionary<long, ExpenditureCategoryChangeCurveModel>());
curveChanges[this.scenarioId] = clientSideActions.CurveChanges.Where(x => (x.Value != null)).ToDictionary(k => Convert.ToInt64(k.Key), v => v.Value);
}
if (clientSideActions.Reallocations != null)
{
if (!reallocations.ContainsKey(this.scenarioId))
reallocations.Add(this.scenarioId, new Dictionary<long, ExpenditureCategoryReallocateResourceModel>());
reallocations[this.scenarioId] = clientSideActions.Reallocations.Where(x => (x.Value != null)).ToDictionary(k => Convert.ToInt64(k.Key), v => v.Value);
}
if (clientSideActions.Roundings != null)
{
if (!roundings.ContainsKey(this.scenarioId))
roundings.Add(this.scenarioId, new Dictionary<long, ExpenditureCategoryRoundModel>());
roundings[this.scenarioId] = clientSideActions.Roundings.Where(x => (x.Value != null) && (x.Value.ExpCats != null) && (x.Value.ExpCats.Count > 0)).ToDictionary(k => Convert.ToInt64(k.Key), v => v.Value);
}
}
#endregion
#region Events for audit creation
public List<EventRequestBaseModel> GetEvents()
{
if (String.IsNullOrWhiteSpace(userId))
{
Logger.Error("ScenarioDetailsEventsTracker:User Id must be specified");
return null;
}
LoadDictionaries();
var clientSideEvents = GetClientSideEvents();
var sdEvents = GetScenarioDetailsEvents();
var taEvents = GetTeamAllocationsEvents();
var resourceEvents = GetResourceAllocationsEvents();
var result = clientSideEvents.Concat(sdEvents).Concat(taEvents).Concat(resourceEvents).ToList();
result.RemoveAll(x => String.IsNullOrWhiteSpace(x.ClassificationKey));
return result;
}
protected List<EventRequestBaseModel> GetScenarioDetailsEvents()
{
List<EventRequestBaseModel> result = new List<EventRequestBaseModel>();
if (scenarioDetailsChanges != null)
{
// Get records for the only scenarios, which have corresponding parent projectId in the cache
var changesFiltered = scenarioDetailsChanges.Where(x => scenario2project.ContainsKey(x.scenarioId)).ToList();
// Expenditures, added to scenario (with allocations)
var events = changesFiltered.Where(x =>
expendituresAdded.ContainsKey(x.scenarioId) &&
expendituresAdded[x.scenarioId].Contains(x.ExpenditureCategoryId))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId }).Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.ExpenditureCategory, ChangeAction.Added),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
NewValue = GetScenarioDetailsValueTextFormatted(x.Key.ExpenditureCategoryId, x.Select(q => q.newQuantityValue).Sum(), x.Select(c => c.newCostValue).Sum())
}).ToList();
result.AddRange(events);
// Added allocations to existing expenditures in scenario
events = changesFiltered.Where(x =>
(!expendituresAdded.ContainsKey(x.scenarioId) || !expendituresAdded[x.scenarioId].Contains(x.ExpenditureCategoryId)) &&
(x.action == ChangeAction.Added))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId }).Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.ExpenditureCategoryAllocation, ChangeAction.Added),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
NewValue = GetScenarioDetailsValueTextFormatted(x.Key.ExpenditureCategoryId, x.Select(q => q.newQuantityValue).Sum(), x.Select(c => c.newCostValue).Sum()),
Comment = x.ToList().TrueForAll(z => z.AutomaticUpdate) ? C_AUTOMATIC_UPDATE_COMMENT_TEXT_TEMPLATE : null
}).ToList();
result.AddRange(events);
events = changesFiltered.Where(x => (x.action == ChangeAction.Updated))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId }).Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.ExpenditureCategoryAllocation, ChangeAction.Updated),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
OldValue = GetScenarioDetailsValueTextFormatted(x.Key.ExpenditureCategoryId, x.Select(q => q.oldQuantityValue).Sum(), x.Select(c => c.oldCostValue).Sum()),
NewValue = GetScenarioDetailsValueTextFormatted(x.Key.ExpenditureCategoryId, x.Select(q => q.newQuantityValue).Sum(), x.Select(c => c.newCostValue).Sum()),
Comment = x.ToList().TrueForAll(z => z.AutomaticUpdate) ? C_AUTOMATIC_UPDATE_COMMENT_TEXT_TEMPLATE : null
}).ToList();
result.AddRange(events);
events = changesFiltered.Where(x => (x.action == ChangeAction.Deleted))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId }).Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.ExpenditureCategory, ChangeAction.Deleted),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
OldValue = GetScenarioDetailsValueTextFormatted(x.Key.ExpenditureCategoryId, x.Select(q => q.oldQuantityValue).Sum(), x.Select(c => c.oldCostValue).Sum())
}).ToList();
result.AddRange(events);
}
return result;
}
protected List<EventRequestBaseModel> GetTeamAllocationsEvents()
{
List<EventRequestBaseModel> result = new List<EventRequestBaseModel>();
if (teamAllocationChanges != null)
{
// Get records for the only scenarios, which have corresponding parent projectId in the cache
var changesFiltered = teamAllocationChanges.Where(x => scenario2project.ContainsKey(x.scenarioId)).ToList();
// Teams, added to scenario (with allocations)
var events = changesFiltered.Where(x =>
teamsAdded.ContainsKey(x.scenarioId) &&
teamsAdded[x.scenarioId].Contains(x.TeamId))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId }).Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.Team, ChangeAction.Added),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
NewValue = GetTeamAllocationsValueTextFormatted(x.Key.TeamId, x.Select(q => q.newQuantityValue).Sum()),
Comment = GetTeamAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId)
}).ToList();
result.AddRange(events);
// Added allocations to existing teams in scenario
events = changesFiltered.Where(x =>
(!teamsAdded.ContainsKey(x.scenarioId) || !teamsAdded[x.scenarioId].Contains(x.TeamId)) &&
(x.action == ChangeAction.Added))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId }).Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.TeamAllocation, ChangeAction.Added),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
NewValue = GetTeamAllocationsValueTextFormatted(x.Key.TeamId, x.Select(q => q.newQuantityValue).Sum()),
Comment =
$"{GetTeamAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId)}{(x.ToList().TrueForAll(z => z.AutomaticUpdate) ? ". " + C_AUTOMATIC_UPDATE_COMMENT_TEXT_TEMPLATE : String.Empty)}"
}).ToList();
result.AddRange(events);
events = changesFiltered.Where(x => (x.action == ChangeAction.Updated))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId }).Select(x => new EventRequestBaseModel
{
ClassificationKey = getEventClassificationId(TrackableEntity.TeamAllocation, ChangeAction.Updated),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
OldValue = GetTeamAllocationsValueTextFormatted(x.Key.TeamId, x.Select(q => q.oldQuantityValue).Sum()),
NewValue = GetTeamAllocationsValueTextFormatted(x.Key.TeamId, x.Select(q => q.newQuantityValue).Sum()),
Comment = String.Format("{0}{1}",
GetTeamAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId),
x.ToList().TrueForAll(z => z.AutomaticUpdate) ? (". " + C_AUTOMATIC_UPDATE_COMMENT_TEXT_TEMPLATE) : String.Empty)
}).ToList();
result.AddRange(events);
events = changesFiltered.Where(x => (x.action == ChangeAction.Deleted))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId }).Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.Team, ChangeAction.Deleted),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
OldValue = GetTeamAllocationsValueTextFormatted(x.Key.TeamId, x.Select(q => q.oldQuantityValue).Sum()),
Comment = GetTeamAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId)
}).ToList();
result.AddRange(events);
}
return result;
}
protected List<EventRequestBaseModel> GetResourceAllocationsEvents()
{
List<EventRequestBaseModel> result = new List<EventRequestBaseModel>();
if (resourceAllocationChanges != null)
{
// Get records for the only scenarios, which have corresponding parent projectId in the cache
var changesFiltered = resourceAllocationChanges.Where(x => scenario2project.ContainsKey(x.scenarioId)).ToList();
// Resources, Assigned to scenario (with allocations)
var changeRecordsForAddedResources = changesFiltered.Where(x =>
resourcesAdded.ContainsKey(x.scenarioId) && resourcesAdded[x.scenarioId].Any(z =>
z.ExpenditureCategoryId == x.ExpenditureCategoryId &&
z.TeamId == x.TeamId &&
z.ResourceId == x.ResourceId)).ToList();
var events = changeRecordsForAddedResources.Where(x => scenario2project.ContainsKey(x.scenarioId))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId, x.ResourceId })
.Select(x => new EventRequestBaseModel
{
ClassificationKey = getEventClassificationId(TrackableEntity.Resource, ChangeAction.Added),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
NewValue = GetResourceAllocationsValueTextFormatted(x.Key.ResourceId, x.Select(q => q.newQuantityValue).Sum()),
Comment = GetResourceAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId, x.Key.TeamId)
}).ToList();
result.AddRange(events);
// Added allocations to existing resources in scenario
events = changesFiltered.Except(changeRecordsForAddedResources).Where(x => (x.action == ChangeAction.Added))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId, x.ResourceId })
.Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.ResourceAllocation, ChangeAction.Added),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
NewValue = GetResourceAllocationsValueTextFormatted(x.Key.ResourceId, x.Select(q => q.newQuantityValue).Sum()),
Comment = String.Format("{0}{1}",
GetResourceAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId, x.Key.TeamId),
x.ToList().TrueForAll(z => z.AutomaticUpdate) ? (". " + C_AUTOMATIC_UPDATE_COMMENT_TEXT_TEMPLATE) : String.Empty)
}).ToList();
result.AddRange(events);
events = changesFiltered.Where(x => (x.action == ChangeAction.Updated))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId, x.ResourceId })
.Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.ResourceAllocation, ChangeAction.Updated),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
OldValue = GetResourceAllocationsValueTextFormatted(x.Key.ResourceId, x.Select(q => q.oldQuantityValue).Sum()),
NewValue = GetResourceAllocationsValueTextFormatted(x.Key.ResourceId, x.Select(q => q.newQuantityValue).Sum()),
Comment = String.Format("{0}{1}",
GetResourceAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId, x.Key.TeamId),
x.ToList().TrueForAll(z => z.AutomaticUpdate) ? (". " + C_AUTOMATIC_UPDATE_COMMENT_TEXT_TEMPLATE) : String.Empty)
}).ToList();
result.AddRange(events);
events = changesFiltered.Where(x => (x.action == ChangeAction.Deleted))
.GroupBy(x => new { x.scenarioId, x.ExpenditureCategoryId, x.TeamId, x.ResourceId })
.Select(x => new EventRequestBaseModel()
{
ClassificationKey = getEventClassificationId(TrackableEntity.Resource, ChangeAction.Deleted),
EntityId = x.Key.scenarioId,
ScenarioId = x.Key.scenarioId,
ProjectId = scenario2project[x.Key.scenarioId],
UserId = userId,
OldValue = GetResourceAllocationsValueTextFormatted(x.Key.ResourceId, x.Select(q => q.oldQuantityValue).Sum()),
Comment = GetResourceAllocationsCommentTextFormatted(x.Key.ExpenditureCategoryId, x.Key.TeamId)
}).ToList();
result.AddRange(events);
}
return result;
}
protected List<EventRequestBaseModel> GetClientSideEvents()
{
// Get single dictionary of all client-side events to save them in proper order
var result = new List<EventRequestBaseModel>();
if (this.usageMode != ScenarioDetailsEventsTrackerUsageMode.ScenarioDetails)
// Client-side events exist for Scenario Details only
return result;
var itemsMixed = new Dictionary<Guid, Dictionary<long, object>>();
if (roundings != null)
{
// Get records for the only scenarios, which have corresponding parent projectId in the cache
var scenariosFiltered = roundings.Keys.Where(x => scenario2project.ContainsKey(x)).ToList();
foreach (var scenarioId in scenariosFiltered)
{
var scenarioFeaturedItems = roundings[scenarioId];
if ((scenarioFeaturedItems != null) && (scenarioFeaturedItems.Count > 0))
{
if (!itemsMixed.ContainsKey(scenarioId))
itemsMixed.Add(scenarioId, new Dictionary<long, object>());
var itemsMixedForScenario = itemsMixed[scenarioId];
foreach (var key in scenarioFeaturedItems.Keys)
itemsMixedForScenario.Add(key, scenarioFeaturedItems[key]);
}
}
}
if (curveChanges != null)
{
// Get records for the only scenarios, which have corresponding parent projectId in the cache
var scenariosFiltered = curveChanges.Keys.Where(x => scenario2project.ContainsKey(x)).ToList();
foreach (var scenarioId in scenariosFiltered)
{
var scenarioFeaturedItems = curveChanges[scenarioId];
if ((scenarioFeaturedItems != null) && (scenarioFeaturedItems.Count > 0))
{
if (!itemsMixed.ContainsKey(scenarioId))
itemsMixed.Add(scenarioId, new Dictionary<long, object>());
var itemsMixedForScenario = itemsMixed[scenarioId];
foreach (var key in scenarioFeaturedItems.Keys)
itemsMixedForScenario.Add(key, scenarioFeaturedItems[key]);
}
}
}
if (reallocations != null)
{
// Get records for the only scenarios, which have corresponding parent projectId in the cache
var scenariosFiltered = reallocations.Keys.Where(x => scenario2project.ContainsKey(x)).ToList();
foreach (var scenarioId in scenariosFiltered)
{
var scenarioFeaturedItems = reallocations[scenarioId];
if ((scenarioFeaturedItems != null) && (scenarioFeaturedItems.Count > 0))
{
if (!itemsMixed.ContainsKey(scenarioId))
itemsMixed.Add(scenarioId, new Dictionary<long, object>());
var itemsMixedForScenario = itemsMixed[scenarioId];
foreach (var key in scenarioFeaturedItems.Keys)
itemsMixedForScenario.Add(key, scenarioFeaturedItems[key]);
}
}
}
foreach (var scenarioId in itemsMixed.Keys)
{
var scenarioItems = itemsMixed[scenarioId];
var keysSorted = scenarioItems.Keys.OrderBy(x => x).ToList();
foreach (var key in keysSorted)
{
var item = scenarioItems[key];
EventRequestBaseModel eventCreated = null;
if (item is ExpenditureCategoryRoundModel)
eventCreated = GetClientSideRoundingEvent(item as ExpenditureCategoryRoundModel, scenarioId);
if (item is ExpenditureCategoryChangeCurveModel)
eventCreated = GetClientSideCurvingEvent(item as ExpenditureCategoryChangeCurveModel, scenarioId);
if (item is ExpenditureCategoryReallocateResourceModel)
eventCreated = GetClientSideReallocationEvent(item as ExpenditureCategoryReallocateResourceModel, scenarioId);
if (eventCreated != null)
result.Add(eventCreated);
}
}
return result;
}
protected EventRequestBaseModel GetClientSideRoundingEvent(ExpenditureCategoryRoundModel item, Guid scenarioId)
{
if (item == null)
return null;
if (!scenario2project.ContainsKey(scenarioId))
return null;
var projectId = scenario2project[scenarioId];
var result = new EventRequestBaseModel()
{
ClassificationKey = "ScenarioDetailsRoundExpenditures",
EntityId = scenarioId,
ScenarioId = scenarioId,
ProjectId = projectId,
UserId = userId,
NewValue = GetRoundingValueTextFormatted(item)
};
return result;
}
protected EventRequestBaseModel GetClientSideCurvingEvent(ExpenditureCategoryChangeCurveModel item, Guid scenarioId)
{
if (item == null)
return null;
if (!scenario2project.ContainsKey(scenarioId))
return null;
var projectId = scenario2project[scenarioId];
var result = new EventRequestBaseModel()
{
ClassificationKey = "ScenarioDetailsChangeCurve",
EntityId = scenarioId,
ScenarioId = scenarioId,
ProjectId = projectId,
UserId = userId,
NewValue = String.Format(C_CURVING_VALUE_TEXT_TEMPLATE, GetExpenditureName(item.FromExpenditureId), GetExpenditureName(item.ToExpenditureId))
};
return result;
}
protected EventRequestBaseModel GetClientSideReallocationEvent(ExpenditureCategoryReallocateResourceModel item, Guid scenarioId)
{
if (item == null)
return null;
if (!scenario2project.ContainsKey(scenarioId))
return null;
var projectId = scenario2project[scenarioId];
var result = new EventRequestBaseModel()
{
ClassificationKey = "ScenarioDetailsReallocateResource",
EntityId = scenarioId,
ScenarioId = scenarioId,
ProjectId = projectId,
UserId = userId,
OldValue = GetExpenditureName(item.SourceExpCat),
NewValue = GetReallocationValueTextFormatted(item)
};
return result;
}
#endregion
#region Helper methods
protected void LoadDictionaries()
{
// Result names for entities
expenditureCategories = new Dictionary<Guid, string>();
teams = new Dictionary<Guid, string>();
resources = new Dictionary<Guid, string>();
var prMngr = new PeopleResourcesManager(dbContext);
var teamMngr = new TeamManager(dbContext);
var ecMngr = new ExpenditureCategoryManager(dbContext);
var resourcesInEvents = new List<Guid>();
var teamsInEvents = new List<Guid>();
var expCatsInEvents = new List<Guid>();
#region Entities from scenario details values manipulations
// Get Resources, Teams and ECs to load names for
if ((resourceAllocationChanges != null) && (resourceAllocationChanges.Count > 0))
{
resourcesInEvents.AddRange(resourceAllocationChanges.Select(x => x.ResourceId)
.Concat(resourcesAdded.Values.SelectMany(r => r.Select(k => k.ResourceId))));
teamsInEvents.AddRange(resourceAllocationChanges.Select(x => x.TeamId));
expCatsInEvents.AddRange(resourceAllocationChanges.Select(x => x.ExpenditureCategoryId));
}
if ((teamAllocationChanges != null) && (teamAllocationChanges.Count > 0))
{
teamsInEvents.AddRange(teamAllocationChanges.Select(x => x.TeamId).Concat(teamsAdded.Values.SelectMany(t => t)));
expCatsInEvents.AddRange(teamAllocationChanges.Select(x => x.ExpenditureCategoryId));
}
if (scenarioDetailsChanges != null && scenarioDetailsChanges.Count > 0)
{
expCatsInEvents.AddRange(scenarioDetailsChanges.Select(x => x.ExpenditureCategoryId).Concat(expendituresAdded.Values.SelectMany(e => e)));
}
#endregion
#region Entities from client-side actions
if ((roundings != null) && (roundings.Count > 0))
{
var expCatsFromRoundings = roundings.Values.SelectMany(x => x.Values.SelectMany(z => z.ExpCats.Where(e => !Guid.Empty.Equals(new Guid(e))))
.Select(e => new Guid(e)));
expCatsInEvents.AddRange(expCatsFromRoundings);
}
if ((curveChanges != null) && (curveChanges.Count > 0))
{
var expCatsFromCurving = curveChanges.Values.SelectMany(z => z.Values.Where(x => !x.FromExpenditureId.Equals(Guid.Empty)).Select(x => x.FromExpenditureId))
.Concat(curveChanges.Values.SelectMany(z => z.Values.Where(x => !x.ToExpenditureId.Equals(Guid.Empty)).Select(x => x.ToExpenditureId)));
expCatsInEvents.AddRange(expCatsFromCurving);
}
if ((reallocations != null) && (reallocations.Count > 0))
{
var expCatsFromReallocations = reallocations.Values.SelectMany(z => z.Values.Where(x => !x.SourceExpCat.Equals(Guid.Empty)).Select(x => x.SourceExpCat))
.Concat(
reallocations.Values.SelectMany(z => z.Values.Where(x => !x.TargetExpCat.Equals(Guid.Empty)).Select(x => x.TargetExpCat)))
.Concat(
reallocations.Values.SelectMany(z => z.Values.Where(x => !x.UseCurveFromExpenditureId.Equals(Guid.Empty)).Select(x => x.UseCurveFromExpenditureId)));
expCatsInEvents.AddRange(expCatsFromReallocations);
}
#endregion
resourcesInEvents = resourcesInEvents.Distinct().ToList();
teamsInEvents = teamsInEvents.Distinct().ToList();
expCatsInEvents = expCatsInEvents.Distinct().ToList();
// Perform loading
var expCatsInfo = ecMngr.GetExpenditureDetails(true, expCatsInEvents);
if (expCatsInfo != null)
expenditureCategories = expCatsInfo.Values.ToDictionary(k => k.ExpenditureCategoryId, v => v.ExpenditureCategoryName);
teams = teamMngr.GetTeamNames(teamsInEvents);
var resourcesInfo = prMngr.GetResourceNames(resourcesInEvents);
if (resourcesInfo != null)
resources = resourcesInfo.Values.ToDictionary(k => k.Id, v => v.DisplayName);
}
protected string GetResourceName(Guid resourceId)
{
return (resources != null) && !resourceId.Equals(Guid.Empty) && resources.ContainsKey(resourceId)
? resources[resourceId] : String.Format(C_RESOURCE_UNDEFINED_TEXT_TEMPLATE, resourceId);
}
protected string GetTeamName(Guid teamId)
{
return (teams != null) && !teamId.Equals(Guid.Empty) && teams.ContainsKey(teamId)
? teams[teamId] : String.Format(C_TEAM_UNDEFINED_TEXT_TEMPLATE, teamId);
}
protected string GetExpenditureName(Guid expCatId)
{
return (expenditureCategories != null) && !expCatId.Equals(Guid.Empty) && expenditureCategories.ContainsKey(expCatId)
? expenditureCategories[expCatId] : String.Format(C_EXPCAT_UNDEFINED_TEXT_TEMPLATE, expCatId);
}
protected string GetScenarioDetailsValueTextFormatted(Guid expCatId, decimal quantity, decimal cost)
{
string expCatName = GetExpenditureName(expCatId);
string quantityFormatted = String.Format("{0:F} hrs", quantity);
string costFormatted = String.Format("{0:C}", cost);
string result = String.Format(C_EXPCAT_VALUE_TEXT_TEMPLATE, expCatName, quantityFormatted, costFormatted);
return result;
}
protected string GetTeamAllocationsValueTextFormatted(Guid teamId, decimal quantity)
{
string teamName = GetTeamName(teamId);
string quantityFormatted = String.Format("{0:F} hrs", quantity);
string result = String.Format(C_TEAM_VALUE_TEXT_TEMPLATE, teamName, quantityFormatted);
return result;
}
protected string GetTeamAllocationsCommentTextFormatted(Guid expCatId)
{
string expCatName = GetExpenditureName(expCatId);
string result = String.Format(C_TEAM_COMMENT_TEXT_TEMPLATE, expCatName);
return result;
}
protected string GetResourceAllocationsValueTextFormatted(Guid resourceId, decimal quantity)
{
string resourceName = GetResourceName(resourceId);
string quantityFormatted = String.Format("{0:F} hrs", quantity);
string result = String.Format(C_RESOURCE_VALUE_TEXT_TEMPLATE, resourceName, quantityFormatted);
return result;
}
protected string GetResourceAllocationsCommentTextFormatted(Guid expCatId, Guid teamId)
{
string expCatName = GetExpenditureName(expCatId);
string teamName = GetTeamName(teamId);
string result = String.Format(C_RESOURCE_COMMENT_TEXT_TEMPLATE, expCatName, teamName);
return result;
}
protected string GetReallocationValueTextFormatted(ExpenditureCategoryReallocateResourceModel item)
{
var targetExpCatName = GetExpenditureName(item.TargetExpCat);
var startDate = Utils.ConvertFromUnixDate(item.SourceWeekEnding).ToShortDateString();
var endDate = Utils.ConvertFromUnixDate(item.TargetWeekEnding).ToShortDateString();
var curveFromExpCatName = GetExpenditureName(item.UseCurveFromExpenditureId);
var result = String.Format(C_REALLOCATION_VALUE_TEXT_TEMPLATE, targetExpCatName, startDate, endDate, item.ReallocateBy, curveFromExpCatName);
return result;
}
protected string GetRoundingValueTextFormatted(ExpenditureCategoryRoundModel item)
{
var expenditureNames = new List<string>();
foreach (var expCatId in item.ExpCats)
{
var expCatName = GetExpenditureName(new Guid(expCatId));
expenditureNames.Add(expCatName);
}
var expCatNamesJoint = String.Join(", ", expenditureNames.ToArray());
var startDate = Utils.ConvertFromUnixDate(item.StartDate).ToShortDateString();
var endDate = Utils.ConvertFromUnixDate(item.EndDate).ToShortDateString();
var result = String.Format(C_ROUNDING_VALUE_TEXT_TEMPLATE, expCatNamesJoint, startDate, endDate, item.DecimalPlaces);
return result;
}
protected string getEventClassificationId(TrackableEntity entity, ChangeAction action)
{
string result = String.Empty;
if (classificationKeys.ContainsKey(this.usageMode) &&
classificationKeys[this.usageMode].ContainsKey(entity) &&
classificationKeys[this.usageMode][entity].ContainsKey(action))
{
result = classificationKeys[this.usageMode][entity][action];
}
else
{
Logger.Error(
$"Scenario Details Audit: No Classification key found for UsageMode: '{this.usageMode.ToDisplayValue()}', EntityType: '{entity.ToDisplayValue()}', Action: '{action.ToDisplayValue()}'");
// FOR DEBUG!
throw new Exception("No Classification key found");
}
return result;
}
#endregion
}
}