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>> 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 scenario2project = new Dictionary(); protected Dictionary expenditureCategories = null; protected Dictionary teams = null; protected Dictionary resources = null; protected Dictionary> expendituresAdded = new Dictionary>(); protected Dictionary> teamsAdded = new Dictionary>(); protected Dictionary> resourcesAdded = new Dictionary>(); protected List scenarioDetailsChanges = new List(); protected List teamAllocationChanges = new List(); protected List resourceAllocationChanges = new List(); protected Dictionary> curveChanges = new Dictionary>(); protected Dictionary> reallocations = new Dictionary>(); protected Dictionary> roundings = new Dictionary>(); 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>>() { #region Classification keys for Scenario Details { ScenarioDetailsEventsTrackerUsageMode.ScenarioDetails, new Dictionary>() { { TrackableEntity.ExpenditureCategory, new Dictionary() { { ChangeAction.Added, "ScenarioDetailsECAdded" }, { ChangeAction.Deleted, "ScenarioDetailsECDeleted" } } }, { TrackableEntity.Team, new Dictionary() { { ChangeAction.Added, "ScenarioDetailsTeamAdded" }, { ChangeAction.Deleted, "ScenarioDetailsTeamDeleted" } } }, { TrackableEntity.Resource, new Dictionary() { { ChangeAction.Added, "ScenarioDetailsResourceAdded" }, { ChangeAction.Deleted, "ScenarioDetailsResourceDeleted" } } }, { TrackableEntity.ExpenditureCategoryAllocation, new Dictionary() { { ChangeAction.Added, "ScenarioDetailsECAllocationsEntered" }, { ChangeAction.Updated, "ScenarioDetialsECAllocationsEdited" } } }, { TrackableEntity.TeamAllocation, new Dictionary() { { ChangeAction.Added, "ScenarioDetailsTeamAllocationsEntered" }, { ChangeAction.Updated, "ScenarioDetailsTeamAllocationsEdited" } } }, { TrackableEntity.ResourceAllocation, new Dictionary() { { ChangeAction.Added, "ScenarioDetailsResourceAllocationsEntered" }, { ChangeAction.Updated, "ScenarioDetailsResourceAllocationsEdited" } } } } }, #endregion #region Classification keys for Activity Calecndar { ScenarioDetailsEventsTrackerUsageMode.ActivityCalendar, new Dictionary>() { { TrackableEntity.Team, new Dictionary() { { ChangeAction.Added, "ActivityCalendarTeamAdded" } } }, { TrackableEntity.Resource, new Dictionary() { { ChangeAction.Added, "ActivityCalendarResourceAdded" }, { ChangeAction.Deleted, "ActivityCalendarResourceDeleted" } } }, { TrackableEntity.ExpenditureCategoryAllocation, new Dictionary() { { ChangeAction.Updated, "ActivityCalendarECAllocationsEdited" } } }, { TrackableEntity.TeamAllocation, new Dictionary() { { ChangeAction.Added, "ActivityCalendarTeamAllocationsEntered" }, { ChangeAction.Updated, "ActivityCalendarTeamAllocationsEdited" } } }, { TrackableEntity.ResourceAllocation, new Dictionary() { { 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()); var scenarioData = expendituresAdded[this.scenarioId]; if (!scenarioData.Contains(id)) scenarioData.Add(id); } } public void ExpenditureCategoriesAddedToScenario(IEnumerable expCats) { if (expCats != null) { if (this.NoScenarioIdSpecified) return; if (!expendituresAdded.ContainsKey(this.scenarioId)) expendituresAdded.Add(this.scenarioId, new List()); 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 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 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()); var scenarioData = teamsAdded[this.scenarioId]; if (!scenarioData.Contains(id)) scenarioData.Add(id); } } public void TeamsAddedToScenario(IEnumerable teams) { if (teams != null) { if (this.NoScenarioIdSpecified) return; if (!teamsAdded.ContainsKey(this.scenarioId)) teamsAdded.Add(this.scenarioId, new List()); 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 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 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()); 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 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 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()); 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()); 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()); 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 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 GetScenarioDetailsEvents() { List result = new List(); 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 GetTeamAllocationsEvents() { List result = new List(); 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 GetResourceAllocationsEvents() { List result = new List(); 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 GetClientSideEvents() { // Get single dictionary of all client-side events to save them in proper order var result = new List(); if (this.usageMode != ScenarioDetailsEventsTrackerUsageMode.ScenarioDetails) // Client-side events exist for Scenario Details only return result; var itemsMixed = new Dictionary>(); 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()); 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()); 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()); 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(); teams = new Dictionary(); resources = new Dictionary(); var prMngr = new PeopleResourcesManager(dbContext); var teamMngr = new TeamManager(dbContext); var ecMngr = new ExpenditureCategoryManager(dbContext); var resourcesInEvents = new List(); var teamsInEvents = new List(); var expCatsInEvents = new List(); #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(); 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 } }