1490 lines
54 KiB
C#
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
|
|
}
|
|
} |