EnVisageOnline/Main/Source/EnVisage/Code/BLL/ProjectManager.cs

4233 lines
209 KiB
C#

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using EnVisage.Models;
using EnVisage.Models.ProjectDependencies;
using System.Data.Entity.Infrastructure;
using jQuery.DataTables.Mvc;
using System.Data.SqlClient;
using System.Data.Entity.Core.Objects;
using System.Data;
using Prevu.Core.Audit.Model;
using System.Threading.Tasks;
using static EnVisage.Code.AuditProxy;
using Prevu.Core.Workflow;
using Prevu.DAL.Main.Accessors;
using Prevu.Core.Audit.Model.ResponseModels;
using Resources;
using System.Runtime.CompilerServices;
using EnVisage.Code.Cache;
namespace EnVisage.Code.BLL
{
public class ProjectManager : ManagerBase<Project, ProjectModel>
{
#region Properties
protected ScenarioManager ScenarioManager { get; set; }
protected IWorkflowManager WorkflowManager { get; set; }
protected UsersCache UsersCache { get; set; }
public const string EDIT_DEPENDENCY_KEY = "EDIT_DEPENDENCY_KEY";
#endregion
#region Local Enums
[Flags]
public enum LoadProjectItems
{
None = 0x0,
Scenarios = 0x1,
Teams = 0x2,
Goals = 0x4,
PortfolioLabels = 0x8,
Tags = 0x16,
All = Scenarios | Teams | Goals
}
#endregion
#region Constructors
public ProjectManager(EnVisageEntities dbContext)
: base(dbContext)
{
ScenarioManager = new ScenarioManager(dbContext);
}
public ProjectManager(EnVisageEntities dbContext, ScenarioManager scenarioManager, IWorkflowManager wfManager, UsersCache usersCache)
: base(dbContext)
{
ScenarioManager = scenarioManager;
WorkflowManager = wfManager;
UsersCache = usersCache;
}
#endregion
protected override Project InitInstance()
{
return new Project { Id = Guid.NewGuid() };
}
protected override Project RetrieveReadOnlyById(Guid key)
{
return DataTable.AsNoTracking().FirstOrDefault(t => t.Id == key);
}
public override DbSet<Project> DataTable => DbContext.Projects;
public Dictionary<Guid, ProjectTeamUsage> GetProjectTeamsUsageInfo(Guid projectId)
{
if (Guid.Empty.Equals(projectId))
return new Dictionary<Guid, ProjectTeamUsage>();
var projectScenarios = ScenarioManager.GetScenariosByParentProject(projectId, ScenarioType.Portfolio);
var projectActualScenario = ScenarioManager.GetActualScenario(projectId);
// return empty data if there are scenarios for the specified project (e.g. new project, not commited yet)
if (null == projectScenarios && null == projectActualScenario)
return new Dictionary<Guid, ProjectTeamUsage>();
var allProjectScenarios = new List<Guid>();
if (null != projectScenarios)
allProjectScenarios = projectScenarios.Clone();
if (null != projectActualScenario)
allProjectScenarios.Add(projectActualScenario.Id);
if (null == allProjectScenarios || !allProjectScenarios.Any())
return new Dictionary<Guid, ProjectTeamUsage>();
var teamsWithResourceAllocations = ScenarioManager.GetResourceAllocatedScenarioTeams(allProjectScenarios);
if (null == teamsWithResourceAllocations || !teamsWithResourceAllocations.Any())
return new Dictionary<Guid, ProjectTeamUsage>();
var allTeamsInAllProjectScenarios = teamsWithResourceAllocations.Values.SelectMany(x => x).Distinct().ToList();
var portfolioScenarioTeams = teamsWithResourceAllocations.Keys.Except(new List<Guid> { projectActualScenario.Id })
.SelectMany(x => teamsWithResourceAllocations[x]).Distinct().ToList();
var actualScenarioTeams = new List<Guid>();
if (teamsWithResourceAllocations.ContainsKey(projectActualScenario.Id))
{
actualScenarioTeams = teamsWithResourceAllocations[projectActualScenario.Id];
}
var result = allTeamsInAllProjectScenarios.ToDictionary(k => k, v =>
new ProjectTeamUsage
{
HasProjectionsResourceAllocations = portfolioScenarioTeams.Contains(v),
HasActualsResourceAllocations = actualScenarioTeams.Contains(v)
});
return result;
}
public List<Guid> getTeamsAssingedToProject(Guid projectId)
{
return projectId == Guid.Empty ? new List<Guid>() : DbContext.Team2Project.Where(x => x.ProjectId == projectId).Select(x => x.TeamId).ToList();
}
public override Project Save(ProjectModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
var currentProjectId = model.Id;
var scenarios4Copy = new Dictionary<Guid, List<Scenario>>();
var localRates4Copy = new Dictionary<Guid, List<Rate>>();
var scenarioDetails4Copy = new Dictionary<Guid, List<ScenarioDetail>>();
if (model.SaveAsCopy)
{
model.Id = Guid.Empty;
model.Name += " - Copy";
var projects = new List<Guid>() { currentProjectId };
if (model.HasChildren && model.Parts != null && model.Parts.Count > 0)
projects.AddRange(model.Parts.Select(x => x.Id));
var scenarios = DbContext.Scenarios.Where(x => x.ParentId.HasValue &&
x.Type == (int)ScenarioType.Portfolio && projects.Contains(x.ParentId.Value))
.Include(x => x.CostSavings1)
.Include(x => x.PeopleResourceAllocations)
.Include(x => x.Scenario2Group)
.Include(x => x.TeamAllocations)
.AsNoTracking()
.ToList();
var scenarioIds = scenarios.Select(x => x.Id).ToList();
if (scenarioIds.Count > 0)
{
scenarios4Copy = scenarios.GroupBy(x => x.ParentId.Value)
.ToDictionary(x => x.Key, g => g.ToList());
scenarioDetails4Copy = DbContext.ScenarioDetail
.AsNoTracking()
.Where(x => x.ParentID.HasValue && scenarioIds.Contains(x.ParentID.Value))
.ToList()
.GroupBy(x => x.ParentID.Value)
.ToDictionary(x => x.Key, g => g.ToList());
localRates4Copy = (new RateManager(DbContext)).Get4Parents(scenarioIds, RateModel.RateType.Derived);
}
}
var partManager = new ProjectPartManager(DbContext);
if (model.Parts != null && !model.HasChildren && model.Parts.Count == 1)
{
var part = model.Parts[0];
model.StatusId = part.StatusId;
model.TypeId = part.TypeId;
model.ClientId = part.ClientId;
model.Details = part.Details;
model.Priority = part.Priority;
model.IsRevenueGenerating = part.IsRevenueGenerating;
model.Deadline = part.Deadline;
model.Probability = part.Probability;
model.ParentProjectId = null;
model.ExternalContacts = part.ExternalContacts;
model.InternalContacts = part.InternalContacts;
model.AssignedTeams = part.AssignedTeams;
model.StrategicGoals = part.StrategicGoals;
model.PerformanceYellowThreshold = part.UseThreshold ? part.PerformanceYellowThreshold : null;
model.PerformanceRedThreshold = part.UseThreshold ? part.PerformanceRedThreshold : null;
model.WorkFlowContacts = part.WorkFlowContacts;
}
else if (model.Parts != null && model.HasChildren && model.Parts.Count > 0)
{
model.StatusId = model.Parts[0].StatusId;
model.TypeId = model.Parts[0].TypeId;
model.ClientId = model.Parts[0].ClientId;
}
var project = base.Save(model);
#region Save Project Parts if exist
if (model.Parts != null && model.HasChildren && model.Parts.Count > 0)
{
var currentParts = model.Id == Guid.Empty
? new List<Project>()
: DbContext.Projects.Where(t => t.ParentProjectId == model.Id).ToList();
var partNum = 1;
foreach (var currentPart in currentParts)
{
if (model.Parts.FirstOrDefault(x => x.Id == currentPart.Id) == null)
{
IList<Guid> holders = this.GetSubprojectsAndParts(currentPart);
FileManager fileMngr = new FileManager(DbContext);
fileMngr.QueuePermanentFilesToDelete(holders);
(DbContext as IObjectContextAdapter).ObjectContext.ExecuteStoreCommand(
$"exec sp_DeleteProject '{currentPart.Id}'");
fileMngr.DeleteQueuedFiles(true);
}
}
for (var i = 0; i < model.Parts.Count; i++)
{
var projectModel = model.Parts[i];
if (projectModel.DeletedPart && projectModel.Id != Guid.Empty)
{
var item2Delete = currentParts.FirstOrDefault(t => t.Id == projectModel.Id);
if (item2Delete != null)
{
Project deletableProject = DbContext.Projects.SingleOrDefault(x => x.Id.Equals(projectModel.Id));
if ((deletableProject != null) && !deletableProject.Id.Equals(Guid.Empty))
{
IList<Guid> holders = this.GetSubprojectsAndParts(deletableProject);
FileManager fileMngr = new FileManager(DbContext);
fileMngr.QueuePermanentFilesToDelete(holders);
(DbContext as IObjectContextAdapter).ObjectContext.ExecuteStoreCommand(string.Format("exec sp_DeleteProject '{0}'", projectModel.Id));
fileMngr.DeleteQueuedFiles(true);
}
}
}
else
{
projectModel.ParentProjectId = project.Id;
projectModel.PartNum = partNum++;
if (currentParts.Count > 0 && currentParts.All(t => t.Id != projectModel.Id) || model.SaveAsCopy)
{
var oldId = model.Parts[i].Id;
model.Parts[i].Id = Guid.Empty;
var savedPartModel = partManager.Save(projectModel);
var savedPartModelConverted = (ProjectPartModel)savedPartModel;
if (projectModel.ExternalContacts != null)
{
savedPartModelConverted.ExternalContacts = new List<Guid>();
savedPartModelConverted.ExternalContacts.AddRange(projectModel.ExternalContacts);
}
if (projectModel.InternalContacts != null)
{
savedPartModelConverted.InternalContacts = new List<Guid>();
savedPartModelConverted.InternalContacts.AddRange(projectModel.InternalContacts);
}
if (projectModel.WorkFlowContacts != null)
{
savedPartModelConverted.WorkFlowContacts = new List<Guid>();
savedPartModelConverted.WorkFlowContacts.AddRange(projectModel.WorkFlowContacts);
}
model.Parts[i] = savedPartModelConverted;
model.Parts[i].Attachments = projectModel.Attachments;
model.Parts[i].Links = projectModel.Links;
model.Parts[i].ProjectTags = projectModel.ProjectTags;
model.Parts[i].PortfolioTags = projectModel.PortfolioTags;
if (model.SaveAsCopy)
{
if (scenarios4Copy.ContainsKey(oldId))
{
CopyScenariosToProject(model.Parts[i].Id, scenarios4Copy[oldId], scenarioDetails4Copy, localRates4Copy, projectModel.AssignedTeams);
}
}
}
else
{
var savedPartModel = partManager.Save(projectModel);
var savedPartModelConverted = (ProjectPartModel)savedPartModel;
if (projectModel.ExternalContacts != null)
{
savedPartModelConverted.ExternalContacts = new List<Guid>();
savedPartModelConverted.ExternalContacts.AddRange(projectModel.ExternalContacts);
}
if (projectModel.InternalContacts != null)
{
savedPartModelConverted.InternalContacts = new List<Guid>();
savedPartModelConverted.InternalContacts.AddRange(projectModel.InternalContacts);
}
if (projectModel.WorkFlowContacts != null)
{
savedPartModelConverted.WorkFlowContacts = new List<Guid>();
savedPartModelConverted.WorkFlowContacts.AddRange(projectModel.WorkFlowContacts);
}
model.Parts[i] = savedPartModelConverted;
model.Parts[i].Attachments = projectModel.Attachments;
model.Parts[i].Links = projectModel.Links;
model.Parts[i].ProjectTags = projectModel.ProjectTags;
model.Parts[i].PortfolioTags = projectModel.PortfolioTags;
}
}
}
}
#endregion
if (model.Id == Guid.Empty)
{
#region Create Actuals scenario
// we need to create empty actual scenario for all new projects (just created or copied)
var scenario = new Scenario
{
Id = Guid.NewGuid(),
Name = "ACTUALS",
ParentId = project.Id,
Type = ScenarioType.Actuals.GetHashCode(),
StartDate = DateTime.UtcNow.Date,
Color = "",
ProjectedRevenue = 0
};
DbContext.Scenarios.Add(scenario);
#endregion
}
if (!model.HasChildren && model.Parts.Count == 1)
{
SaveContacts(project, model.InternalContacts, model.ExternalContacts);
model.AssignedTeams = model.Parts[0].AssignedTeams = UpdateProjectTeams(project, model.AssignedTeams, null, false, true);
SaveStrategicGoals(project, model.StrategicGoals);
(new WorkFlowManager(DbContext)).SaveContacts(model.WorkFlowContacts, null, project.Id, WorkFlowContactNotificationType.None);
if (model.SaveAsCopy)
{
if (scenarios4Copy.ContainsKey(currentProjectId))
{
CopyScenariosToProject(project.Id, scenarios4Copy[currentProjectId], scenarioDetails4Copy, localRates4Copy, model.AssignedTeams);
}
}
}
return project;
}
#region Events & History Logging
public void LogProjectCreateEvent(Project newProject, string userId, string transactionId = null)
{
transactionId = Utils.GetOrCreateTransactionId(DbContext, transactionId);
#region Project Created
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProject.Id,
ProjectId = newProject.Id,
ClassificationKey = "ProjectCreated",
NewValue = newProject.Name,
UserId = userId
});
#endregion
}
public void LogProjectDeleteEvent(ProjectModel oldProject, string userId, string transactionId = null)
{
transactionId = Utils.GetOrCreateTransactionId(DbContext, transactionId);
#region Project Delete
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = oldProject.Id,
ProjectId = oldProject.Id,
ClassificationKey = "ProjectDelete",
UserId = userId,
OldValue = oldProject.Number
});
#endregion
}
public void LogProjectEvents(string userId, ProjectModel newProjectModel, string transactionId = null, bool convertedToProjectWithParts = false)
{
transactionId = Utils.GetOrCreateTransactionId(DbContext, transactionId);
Project oldProject = null;
Guid newProjectModelId = Guid.Empty;
if (convertedToProjectWithParts)
{
//when we convert project to project with parts, first "virtual" part becomes real first part, old project id becomes first part's id
//so, to get old project we need to load by id taken from the first part because newProjectModel.Id is Guid.Empty until we save
if (newProjectModel.Parts.Count < 1 || newProjectModel.Parts.First() == null)
throw new ArgumentException("ProjectModel does not contain parts");
oldProject = DataTable.Find(newProjectModel.Parts.First().Id);
newProjectModelId = newProjectModel.Parts.First().Id;
}
else
{
oldProject = DataTable.Find(newProjectModel.Id);
newProjectModelId = newProjectModel.Id;
}
if (oldProject != null)
{
var oldProjectModel = ProjectModel.GetProjectModel(oldProject, DbContext);
#region Project Name
if (newProjectModel.Name != oldProjectModel.Name)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectNameChange",
UserId = userId,
NewValue = newProjectModel.Name,
OldValue = oldProjectModel.Name
});
}
#endregion
#region Project Number
if (!string.IsNullOrEmpty(newProjectModel.Number))
{
if (string.IsNullOrEmpty(oldProjectModel.Number))
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectNumberAdd",
UserId = userId,
NewValue = newProjectModel.Number,
OldValue = oldProjectModel.Number
});
}
else
{
if (oldProjectModel.Number != newProjectModel.Number)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectNumberChange",
UserId = userId,
NewValue = newProjectModel.Number,
OldValue = oldProjectModel.Number
});
}
}
}
else
{
if (!string.IsNullOrEmpty(oldProjectModel.Number))
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectNumberDelete",
UserId = userId,
NewValue = newProjectModel.Number,
OldValue = oldProjectModel.Number
});
}
}
#endregion
#region Project Color
if (!string.IsNullOrEmpty(newProjectModel.Color))
{
if (string.IsNullOrEmpty(oldProjectModel.Color))
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectColorAdd",
UserId = userId,
NewValue = newProjectModel.Color.Trim('#'),
OldValue = string.Empty
});
}
else
{
if (oldProjectModel.Color.Trim('#') != newProjectModel.Color.Trim('#'))
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectColorChange",
UserId = userId,
NewValue = newProjectModel.Color.Trim('#'),
OldValue = oldProjectModel.Color.Trim('#')
});
}
}
}
else
{
if (!string.IsNullOrEmpty(oldProjectModel.Color))
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectColorDelete",
UserId = userId,
NewValue = string.Empty,
OldValue = oldProjectModel.Color.Trim('#')
});
}
}
#endregion
#region Business Unit
if (newProjectModel.CompanyId != oldProjectModel.CompanyId)
{
var compayManager = new CompanyManager(DbContext);
var newCompanyName = string.Empty;
if (newProjectModel.CompanyId != null)
{
var newCompany = compayManager.GetCompanyById(newProjectModel.CompanyId.Value);
if (newCompany != null)
{
newCompanyName = newCompany.Name;
}
}
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectBusinessUnitChange",
UserId = userId,
NewValue = newCompanyName,
OldValue = oldProjectModel.CompanyName
});
}
#endregion
#region Project UserDefinedFields
if (newProjectModel.UserDefinedFields.Count > 0 && !string.IsNullOrEmpty(newProjectModel.UserDefinedFields.First().Values.Value))
{
if (oldProjectModel.UserDefinedFields.Count == 0 || string.IsNullOrEmpty(oldProjectModel.UserDefinedFields.First().Values.Value))
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectUserDefinedFieldsAdd",
UserId = userId,
NewValue = newProjectModel.UserDefinedFields.First().Values.Value,
OldValue = string.Empty
});
}
else
{
if (oldProjectModel.UserDefinedFields.Count > 0 && newProjectModel.UserDefinedFields.First().Values.Value != oldProjectModel.UserDefinedFields.First().Values.Value)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectUserDefinedFieldsChange",
UserId = userId,
NewValue = newProjectModel.UserDefinedFields.First().Values.Value,
OldValue = oldProjectModel.UserDefinedFields.First().Values.Value
});
}
}
}
else
{
if (oldProjectModel.UserDefinedFields.Count > 0 && !string.IsNullOrEmpty(oldProjectModel.UserDefinedFields.First().Values.Value))
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = newProjectModelId,
ProjectId = newProjectModelId,
ClassificationKey = "ProjectUserDefinedFieldsDelete",
UserId = userId,
NewValue = string.Empty,
OldValue = oldProjectModel.UserDefinedFields.First().Values.Value
});
}
}
#endregion
#region Parts
if (!newProjectModel.HasChildren || convertedToProjectWithParts)
{
LogProjectPartsEvents(false, oldProjectModel.Parts.First(), newProjectModel.Parts.First(), userId, transactionId, convertedToProjectWithParts);
}
#endregion
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private string FormatTagNameForEventsLog(Guid tagId, Dictionary<Guid, string> tagNames)
{
var tagName = (tagNames.ContainsKey(tagId)) ? tagNames[tagId] : String.Format(AuditStrings.Tag_UnknownNameTemplate, tagId.ToString());
return tagName;
}
public void LogProjectPartsEvents(bool isPart, ProjectPartModel oldProject, ProjectPartModel newProjectModel, string userId, string transactionId = null, bool convertedToProjectWithParts = false)
{
transactionId = Utils.GetOrCreateTransactionId(DbContext, transactionId);
string standardCommentForProjectPart = isPart ? String.Format(AuditStrings.ProjectPart_StandardEventComment, newProjectModel.Name) : (string)null;
Guid? projectId = null;
if (isPart || convertedToProjectWithParts)
{
projectId = newProjectModel.Id;
}
else
{
projectId = newProjectModel.ParentProjectId;
}
EventRequestBaseModel eventRecord;
// First load additional necessary data
var tagsToLoad = new List<Guid>();
Task<Dictionary<Guid, string>> tagsLoadingTask = null;
if (oldProject.PortfolioTags != null)
tagsToLoad.AddRange(oldProject.PortfolioTags);
if (newProjectModel.PortfolioTags != null)
tagsToLoad.AddRange(newProjectModel.PortfolioTags);
if (oldProject.ProjectTags != null)
tagsToLoad.AddRange(oldProject.ProjectTags);
if (newProjectModel.ProjectTags != null)
tagsToLoad.AddRange(newProjectModel.ProjectTags);
if (tagsToLoad.Count > 0)
{
var tagManager = new TagManager(DbContext);
tagsLoadingTask = tagManager.LoadTagNamesAsync(tagsToLoad.Distinct());
}
#region Project Type
if (newProjectModel.TypeId != oldProject.TypeId)
{
var newValue = DbContext.Types.FirstOrDefault(p => p.Id == newProjectModel.TypeId);
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectTypeChange",
UserId = userId,
NewValue = newValue?.Name ?? newProjectModel.TypeName,
OldValue = oldProject.TypeName,
Comment = standardCommentForProjectPart
});
}
#endregion
#region Client
if (newProjectModel.ClientId != oldProject.ClientId)
{
var clientManager = new ClientManager(DbContext);
var newClientName = string.Empty;
if (newProjectModel.ClientId != null)
{
var newClient = clientManager.LoadClientModel(newProjectModel.ClientId.Value);
if (newClient != null)
{
newClientName = newClient.Name;
}
}
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectClientChange",
UserId = userId,
NewValue = newClientName,
OldValue = oldProject.ClientName,
Comment = standardCommentForProjectPart
});
}
#endregion
#region Project Deadline
if (newProjectModel.Deadline != null)
{
if (oldProject.Deadline == null)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectDeadlineAdd",
UserId = userId,
NewValue = newProjectModel.Deadline.ToString(),
OldValue = oldProject.Deadline.ToString(),
Comment = standardCommentForProjectPart
});
}
else
{
if (oldProject.Deadline != newProjectModel.Deadline)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectDeadlineChange",
UserId = userId,
NewValue = newProjectModel.Deadline.ToString(),
OldValue = oldProject.Deadline.ToString(),
Comment = standardCommentForProjectPart
});
}
}
}
else
{
if (oldProject.Deadline != null)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectDeadlineDelete",
UserId = userId,
NewValue = newProjectModel.Deadline.ToString(),
OldValue = oldProject.Deadline.ToString(),
Comment = standardCommentForProjectPart
});
}
}
#endregion
#region Status
if (newProjectModel.StatusId != oldProject.StatusId)
{
var statusManager = new StatusManager(DbContext);
var newName = string.Empty;
var newItem = statusManager.GetTypeById(newProjectModel.StatusId);
if (newItem != null)
{
newName = newItem.Name;
}
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectStatusChange",
UserId = userId,
NewValue = newName,
OldValue = oldProject.StatusName,
Comment = standardCommentForProjectPart
});
}
#endregion
#region Project Probability
if (newProjectModel.Probability != 0)
{
if (oldProject.Probability == 0)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectProbabilityAdd",
UserId = userId,
NewValue = newProjectModel.Probability.ToString(),
OldValue = oldProject.Probability.ToString(),
Comment = standardCommentForProjectPart
});
}
else
{
if (oldProject.Probability != newProjectModel.Probability)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectProbabilityChange",
UserId = userId,
NewValue = newProjectModel.Probability.ToString(),
OldValue = oldProject.Probability.ToString(),
Comment = standardCommentForProjectPart
});
}
}
}
else
{
if (oldProject.Probability != 0)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectProbabilityDelete",
UserId = userId,
NewValue = newProjectModel.Probability.ToString(),
OldValue = oldProject.Probability.ToString(),
Comment = standardCommentForProjectPart
});
}
}
#endregion
#region Revenue Generating
if (newProjectModel.IsRevenueGenerating != oldProject.IsRevenueGenerating)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectRevenueGeneratingChange",
UserId = userId,
NewValue = newProjectModel.IsRevenueGenerating ? "Yes" : "No",
OldValue = oldProject.IsRevenueGenerating ? "Yes" : "No",
Comment = standardCommentForProjectPart
});
}
#endregion
#region Revenue Priority
if (newProjectModel.Priority != oldProject.Priority)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectPriorityChange",
UserId = userId,
NewValue = newProjectModel.Priority.ToString(),
OldValue = oldProject.Priority.ToString(),
Comment = standardCommentForProjectPart
});
}
#endregion
#region Revenue Details
if (newProjectModel.Details != oldProject.Details)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "ProjectDetailsEdit",
UserId = userId,
NewValue = newProjectModel.Details,
OldValue = oldProject.Details,
Comment = standardCommentForProjectPart
});
}
#endregion
#region Portfolio/Product Labels & Project Tags
if (tagsLoadingTask != null)
{
// tagsLoadingTask is NULL, if there is nothing to load. Means, no Porflolio and Project tags exist for current Project part
// Get added an removed Portfolio Tags
var oldPortfolioTags = (oldProject.PortfolioTags != null) ? oldProject.PortfolioTags : new List<Guid>();
var newPortfolioTags = (newProjectModel.PortfolioTags != null) ? newProjectModel.PortfolioTags : new List<Guid>();
var addedPortfolioTags = newPortfolioTags.Except(oldPortfolioTags).ToList();
var removedPortfolioTags = oldPortfolioTags.Except(newPortfolioTags).ToList();
// Get added an removed Project Tags
var oldProjectTags = (oldProject.ProjectTags != null) ? oldProject.ProjectTags : new List<Guid>();
var newProjectTags = (newProjectModel.ProjectTags != null) ? newProjectModel.ProjectTags : new List<Guid>();
var addedProjectTags = newProjectTags.Except(oldProjectTags).ToList();
var removedProjectTags = oldProjectTags.Except(newProjectTags).ToList();
var createdEventRecords = new List<EventRequestBaseModel>();
tagsLoadingTask.Wait();
// Events for removed Portfolio Tags / Labels
foreach (var tagId in removedPortfolioTags)
{
var tagName = FormatTagNameForEventsLog(tagId, tagsLoadingTask.Result);
eventRecord = new EventRequestBaseModel(projectId, userId, "ProjectPortfolioProductLabelsDelete",
oldValue: tagName, projectId: projectId, comment: standardCommentForProjectPart);
createdEventRecords.Add(eventRecord);
}
// Events for added Portfolio Tags / Labels
foreach (var tagId in addedPortfolioTags)
{
var tagName = FormatTagNameForEventsLog(tagId, tagsLoadingTask.Result);
eventRecord = new EventRequestBaseModel(projectId, userId, "ProjectPortfolioProductLabelsAdd",
newValue: tagName, projectId: projectId, comment: standardCommentForProjectPart);
createdEventRecords.Add(eventRecord);
}
// Events for removed Project Tags
foreach (var tagId in removedProjectTags)
{
var tagName = FormatTagNameForEventsLog(tagId, tagsLoadingTask.Result);
eventRecord = new EventRequestBaseModel(projectId, userId, "ProjectProjectTagsDelete",
oldValue: tagName, projectId: projectId, comment: standardCommentForProjectPart);
createdEventRecords.Add(eventRecord);
}
// Events for added Project Tags
foreach (var tagId in addedProjectTags)
{
var tagName = FormatTagNameForEventsLog(tagId, tagsLoadingTask.Result);
eventRecord = new EventRequestBaseModel(projectId, userId, "ProjectProjectTagsAdd",
newValue: tagName, projectId: projectId, comment: standardCommentForProjectPart);
createdEventRecords.Add(eventRecord);
}
if (createdEventRecords.Count > 0)
AuditProxy.LogEvent(transactionId, createdEventRecords);
}
#endregion
#region Project Dependency
if (oldProject.Dependencies != null && newProjectModel.Dependencies != null)
{
foreach (var oldDependencies in oldProject.Dependencies)
{
if (!newProjectModel.Dependencies.Contains(oldDependencies))
{
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectDependencyDelete",
oldValue: oldDependencies.DependencyTypeDisplayName, projectId: projectId, comment: standardCommentForProjectPart));
}
}
foreach (var newDependencies in newProjectModel.Dependencies)
{
if (!oldProject.Dependencies.Contains(newDependencies))
{
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectDependencyAdd",
newValue: newDependencies.DependencyTypeDisplayName, projectId: projectId, comment: standardCommentForProjectPart));
}
}
}
#endregion
#region Performance Indicators
if (newProjectModel.UseThreshold != oldProject.UseThreshold)
{
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = projectId,
ProjectId = projectId,
ClassificationKey = "PerformanceIndicatorsChange",
UserId = userId,
NewValue = newProjectModel.UseThreshold ? "Yes" : "No",
OldValue = oldProject.UseThreshold ? "Yes" : "No",
Comment = standardCommentForProjectPart
});
}
#endregion
#region Teams
if (oldProject.AssignedTeams != null && newProjectModel.AssignedTeams != null)
{
var tagManager = new TeamManager(DbContext);
foreach (var oldAssignedTeams in oldProject.AssignedTeams)
{
if (!newProjectModel.AssignedTeams.Contains(oldAssignedTeams))
{
var name = string.Empty;
var item = tagManager.LoadTeamModel(oldAssignedTeams);
if (item != null)
{
name = item.Name;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectTeamsDelete",
oldValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
foreach (var newAssignedTeams in newProjectModel.AssignedTeams)
{
if (!oldProject.AssignedTeams.Contains(newAssignedTeams))
{
var name = string.Empty;
var item = tagManager.LoadTeamModel(newAssignedTeams);
if (item != null)
{
name = item.Name;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectTeamsAdd",
newValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
}
#endregion
#region Strategic Goals
if (oldProject.StrategicGoals != null && newProjectModel.StrategicGoals != null)
{
var manager = new StrategicGoalManager(DbContext);
var deletedGoals = oldProject.StrategicGoals.Except(newProjectModel.StrategicGoals).ToList();
var newGoals = newProjectModel.StrategicGoals.Except(oldProject.StrategicGoals).ToList();
foreach (var deletedGoal in deletedGoals)
{
var name = string.Empty;
var item = manager.GetStrategicGoalById(deletedGoal);
if (item != null)
{
name = item.Name;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectStrategicGoalsDelete",
oldValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
foreach (var newGoal in newGoals)
{
var name = string.Empty;
var item = manager.GetStrategicGoalById(newGoal);
if (item != null)
{
name = item.Name;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectStrategicGoalsAdd",
newValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
//foreach (var oldStrategicGoals in oldProject.StrategicGoals)
//{
// if (!newProjectModel.StrategicGoals.Contains(oldStrategicGoals))
// {
// var name = string.Empty;
// var item = manager.GetStrategicGoalById(oldStrategicGoals);
// if (item != null)
// {
// name = item.Name;
// }
// LogEvent(transactionId,
// new EventRequestBaseModel(projectId, userId, "ProjectStrategicGoalsDelete",
// oldValue: name, projectId: projectId));
// }
//}
//foreach (var newStrategicGoals in newProjectModel.StrategicGoals)
//{
// if (!oldProject.StrategicGoals.Contains(newStrategicGoals))
// {
// var name = string.Empty;
// var item = manager.GetStrategicGoalById(newStrategicGoals);
// if (item != null)
// {
// name = item.Name;
// }
// LogEvent(transactionId,
// new EventRequestBaseModel(projectId, userId, "ProjectStrategicGoalsAdd",
// newValue: name, projectId: projectId));
// }
//}
}
#endregion
#region Internal Contacts
if (oldProject.InternalContacts != null && newProjectModel.InternalContacts != null)
{
var manager = new ContactManager(DbContext);
foreach (var oldItem in oldProject.InternalContacts)
{
if (!newProjectModel.InternalContacts.Contains(oldItem))
{
var name = string.Empty;
var item = manager.LoadContactModel(oldItem);
if (item != null)
{
name = item.FirstName + item.LastName;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectInternalContactsDelete",
oldValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
foreach (var newItem in newProjectModel.InternalContacts)
{
if (!oldProject.InternalContacts.Contains(newItem))
{
var name = string.Empty;
var item = manager.LoadContactModel(newItem);
if (item != null)
{
name = item.FirstName + item.LastName;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectInternalContactsAdd",
newValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
}
#endregion
#region External Contacts
if (oldProject.ExternalContacts != null && newProjectModel.ExternalContacts != null)
{
var manager = new ContactManager(DbContext);
foreach (var oldItem in oldProject.ExternalContacts)
{
if (!newProjectModel.ExternalContacts.Contains(oldItem))
{
var name = string.Empty;
var item = manager.LoadContactModel(oldItem);
if (item != null)
{
name = item.FirstName + item.LastName;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectExternalContactsDelete",
oldValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
foreach (var newItem in newProjectModel.ExternalContacts)
{
if (!oldProject.ExternalContacts.Contains(newItem))
{
var name = string.Empty;
var item = manager.LoadContactModel(newItem);
if (item != null)
{
name = item.FirstName + item.LastName;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectExternalContactsAdd",
newValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
}
#endregion
#region Workflow Contacts
if (oldProject.WorkFlowContacts != null && newProjectModel.WorkFlowContacts != null)
{
var manager = new ContactManager(DbContext);
foreach (var oldItem in oldProject.WorkFlowContacts)
{
if (!newProjectModel.WorkFlowContacts.Contains(oldItem))
{
var name = string.Empty;
var item = manager.LoadContactModel(oldItem);
if (item != null)
{
name = item.FirstName + item.LastName;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectWorkflowContactsDelete",
oldValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
foreach (var newItem in newProjectModel.WorkFlowContacts)
{
if (!oldProject.WorkFlowContacts.Contains(newItem))
{
var name = string.Empty;
var item = manager.LoadContactModel(newItem);
if (item != null)
{
name = item.FirstName + item.LastName;
}
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectWorkflowContactsAdd",
newValue: name, projectId: projectId, comment: standardCommentForProjectPart));
}
}
}
#endregion
#region Attachments
if (oldProject.Attachments != null && newProjectModel.Attachments != null)
{
foreach (var oldItem in oldProject.Attachments.ToDictionary(q => q.Id, q => q.Name))
{
if (!newProjectModel.Attachments.Select(q => q.Id).Contains(oldItem.Key))
{
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectAttachmentsDelete",
oldValue: oldItem.Value, projectId: projectId, comment: standardCommentForProjectPart));
}
}
foreach (var newItem in newProjectModel.Attachments.ToDictionary(q => q.Id, q => q.Name))
{
if (!oldProject.Attachments.Select(q => q.Id).Contains(newItem.Key))
{
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectAttachmentsAdd",
newValue: newItem.Value, projectId: projectId, comment: standardCommentForProjectPart));
}
}
}
#endregion
#region Links
if (oldProject.Links != null && newProjectModel.Links != null)
{
foreach (var oldItem in oldProject.Links.ToDictionary(q => q.Id, q => q.Name))
{
if (!newProjectModel.Links.Select(q => q.Id).Contains(oldItem.Key))
{
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectLinksDelete",
oldValue: oldItem.Value, projectId: projectId, comment: standardCommentForProjectPart));
}
}
foreach (var newItem in newProjectModel.Links.ToDictionary(q => q.Id, q => q.Name))
{
if (!oldProject.Links.Select(q => q.Id).Contains(newItem.Key))
{
LogEvent(transactionId,
new EventRequestBaseModel(projectId, userId, "ProjectLinksAdd",
newValue: newItem.Value, projectId: projectId, comment: standardCommentForProjectPart));
}
}
}
#endregion
}
public async Task<HistoryDisplayModelTotal> GetProjectEvents(Guid entityId, int take = 25, int skip = 0, string orderBy = "DateCreated", bool isOrderAsc = false)
{
// Get project parts, if exist
IList<Guid> projectAndPartsIds = new List<Guid>() { entityId };
var masterProject = this.Load(entityId, true);
if ((masterProject != null) && masterProject.HasChildren)
{
projectAndPartsIds = this.GetSubprojectsAndParts(masterProject);
}
// as we get history events and workflow events from different data source
// so we should get take + skip elements from both sources and then
// merge them, sort and only after that apply skip and take operators, otherwise paging wont work correctly
var auditEvents = await GetEvents(projectAndPartsIds, take + skip, 0, orderBy, isOrderAsc);
var wfEvents = await WorkflowManager.GetWorkflowHistory4Project(string.Empty, entityId, take + skip, 0, orderBy, isOrderAsc);
var events = MergeEventsWithWorkflowHistory(auditEvents, wfEvents, take, skip, orderBy, isOrderAsc);
if (events == null)
return new HistoryDisplayModelTotal();
var eventUserIds = events.Data?.Select(w => w.UserId).Distinct();
if (eventUserIds == null)
return new HistoryDisplayModelTotal();
var users = UsersCache.Value.Where(q => eventUserIds.Contains(q.Id.ToString().ToLower()))
.ToDictionary(q => q.Id.ToString().ToLower(), q => $"{q.FirstName} {q.LastName}");
var eventsModel = new List<HistoryDisplayModel>();
foreach (var eventItem in events.Data)
{
string userName = string.Empty;
if (!string.IsNullOrEmpty(eventItem.UserId?.ToLower()))
users.TryGetValue(eventItem.UserId?.ToLower(), out userName);
eventsModel.Add("Workflow".Equals(eventItem.ClassificationTitle)
? new HistoryDisplayModel(eventItem, userName?.Trim(), "Workflow")
: new HistoryDisplayModel(eventItem, userName?.Trim()));
}
return new HistoryDisplayModelTotal
{
Total = events.Total,
Data = eventsModel
};
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="projectId">A <see cref="Project"/> Id which Team2Project records we're going to update.</param>
/// <param name="teams">new project teams to save or just new teams to add if <paramref name="appendProjectTeamsToScenario"/> is true.</param>
/// <param name="excludeScenarioId">Scenario to exclude from processing</param>
/// <param name="appendProjectTeamsToScenario"><b>true</b> - append teams from <paramref name="teams"/> to current project teams.
/// <b>false</b> - replace current <see cref="Team2Project"/> records with teams specified in <paramref name="teams"/>.</param>
/// <param name="recalculateScenarioAllocations">Indicates whether to delete redundant team and resource allocations (and recalculate BU scenario details accordingly) or not.</param>
/// <returns>An updated list of proejct teams. It could contain some teams not exist in <paramref name="teams"/> which system cannot delete due to any restrictions.</returns>
public List<Guid> UpdateProjectTeams(Guid projectId, List<Guid> teamsToAdd, Guid? excludeScenarioId, bool appendProjectTeamsToScenario, bool recalculateScenarioAllocations = false)
{
var project = DbContext.Projects.FirstOrDefault(x => x.Id == projectId);
if (project == null)
return teamsToAdd;
return UpdateProjectTeams(project, teamsToAdd, excludeScenarioId, appendProjectTeamsToScenario, recalculateScenarioAllocations);
}
/// <summary>
/// Adds new teams to the project and all (except actuals) its scenarios, except the given one. Transaction usage REQUIRED if <paramref name="recalculateScenarioAllocations"/> is true.
/// </summary>
/// <param name="project">A <see cref="Project"/> record which Team2Project records we're going to update.</param>
/// <param name="teams">new project teams to save or just new teams to add if <paramref name="appendProjectTeamsToScenario"/> is true.</param>
/// <param name="excludeScenarioId">Scenario to exclude from processing</param>
/// <param name="appendProjectTeamsToScenario"><b>true</b> - append teams from <paramref name="teams"/> to current project teams.
/// <b>false</b> - replace current <see cref="Team2Project"/> records with teams specified in <paramref name="teams"/>.</param>
/// <param name="recalculateScenarioAllocations">Indicates whether to delete redundant team and resource allocations (and recalculate BU scenario details accordingly) or not.</param>
/// <returns>An updated list of proejct teams. It could contain some teams not exist in <paramref name="teams"/> which system cannot delete due to any restrictions.</returns>
public List<Guid> UpdateProjectTeams(Project project, List<Guid> teams, Guid? excludeScenarioId, bool appendProjectTeamsToScenario, bool recalculateScenarioAllocations = false, Dictionary<Guid, Dictionary<Guid, IEnumerable<RateModel>>> rates = null)
{
if (project == null)
return teams;
// The result team set for project
var resultProjectTeams = teams;
if (appendProjectTeamsToScenario)
{
var currentProjectTeams = DbContext.Team2Project.Where(x => x.ProjectId == project.Id).Select(y => y.TeamId).ToList();
var newTeams = teams.Except(currentProjectTeams);
resultProjectTeams = currentProjectTeams.Union(newTeams).ToList();
}
resultProjectTeams = SaveTeam2Projects(project, resultProjectTeams);
// Get scenarios to update
List<Guid> scenariosToUpdate = ScenarioManager.GetProjectNonActualsScenarios(project.Id);
if (excludeScenarioId.HasValue && !excludeScenarioId.Value.Equals(Guid.Empty))
scenariosToUpdate.Remove(excludeScenarioId.Value);
// Perform update of scenario allocations (SD, TA and RA)
if (recalculateScenarioAllocations)
{
if (rates == null)
{
var rateManager = new RateManager(DbContext);
rates = rateManager.GetRatesDict(scenariosToUpdate, rateManager.LoadRatesByScenarios(scenariosToUpdate));
}
ScenarioManager.RecalculateScenarioAllocationsForRemovedTeams(scenariosToUpdate, resultProjectTeams, rates);
}
return resultProjectTeams;
}
public void SaveContacts(Project project, List<Guid> internalContacts, List<Guid> externalContacts)
{
if (externalContacts == null)
externalContacts = new List<Guid>();
if (internalContacts == null)
internalContacts = new List<Guid>();
var allCurrentContacts = externalContacts.Concat(internalContacts).ToList();
var oldContacts = DbContext.Contact2Project.Where(c2S => c2S.ShowId == project.Id).ToList();
foreach (var c in oldContacts)
{
if (allCurrentContacts.Contains(c.ContactId))
allCurrentContacts.Remove(c.ContactId);
else
DbContext.Contact2Project.Remove(c);
}
foreach (var cId in allCurrentContacts)
{
DbContext.Contact2Project.Add(new Contact2Project()
{
Id = Guid.NewGuid(),
ContactId = cId,
ShowId = project.Id
});
}
}
/// <summary>
/// Deletes and adds tems to project according to incoming team list.
/// </summary>
/// <param name="project"></param>
/// <param name="allProjectTeamsToSave"></param>
/// <returns>Result teams list (some teams may be not deleted because of actuals existing)</returns>
/// <remarks>This method is just a subtask of updating project teams BLL task (see UpdateProjectTeams method). Don't use it separately to avoid data inconsistency.</remarks>
private List<Guid> SaveTeam2Projects(Project project, List<Guid> allProjectTeamsToSave)
{
if (project == null || project.Id == Guid.Empty)
return allProjectTeamsToSave;
if (allProjectTeamsToSave == null)
allProjectTeamsToSave = new List<Guid>();
var result = allProjectTeamsToSave.Clone();
var projectsIds = new List<Guid> { project.Id };
if (project.ParentProjectId.HasValue)
projectsIds.Add(project.ParentProjectId.Value);
var partsIds = DbContext.Projects.AsNoTracking()
.Where(x => x.ParentProjectId.HasValue && projectsIds.Contains(x.ParentProjectId.Value))
.Select(x => x.Id).ToList();
projectsIds.AddRange(partsIds.Where(x => !projectsIds.Contains(x)));
#region Saving permissions
var oldTeamsOfCurrentProject = DbContext.Team2Project.Where(x => x.ProjectId == project.Id).ToList();
var oldTeamIds = oldTeamsOfCurrentProject.Select(x => x.TeamId).ToList();
var oldTeamsUsers = DbContext.User2Team.Where(x => oldTeamIds.Contains(x.TeamId))
.Select(x => x.UserId.ToLower()).Distinct().ToList();
var newTeamsUsers = DbContext.User2Team.Where(x => allProjectTeamsToSave.Contains(x.TeamId))
.Select(x => x.UserId.ToLower()).Distinct().ToList();
// TODO: discuss, is it correct removing? in this case we can remove rights from all users for current project
#region Removing permissions from users which teams has no allocation in scenarios of current project or current project parts
var removedFromCurrentProjectUsers = oldTeamsUsers.Except(newTeamsUsers).ToList();
if (removedFromCurrentProjectUsers.Count > 0)
{
var contributorsInOtherParts =
(from ta in DbContext.TeamAllocations
join s in DbContext.Scenarios on ta.ScenarioId equals s.Id
join t in DbContext.Teams on ta.TeamId equals t.Id
join u2t in DbContext.User2Team on t.Id equals u2t.TeamId
where (ta.Quantity > 0) && s.ParentId.HasValue && projectsIds.Contains(s.ParentId.Value) &&
(s.ParentId != project.Id)
select u2t.UserId.ToLower()).Distinct().ToList();
var removedUsers = removedFromCurrentProjectUsers.Except(contributorsInOtherParts).ToList();
if (removedUsers.Count > 0)
DbContext.ProjectAccesses.RemoveRange(DbContext.ProjectAccesses.Where(x => projectsIds.Contains(x.ProjectId) && removedUsers.Contains(x.PrincipalId.ToString())));
}
#endregion
var currentPermissions = DbContext.ProjectAccesses.AsNoTracking().Where(x => projectsIds.Contains(x.ProjectId)).ToList();
//previous collection DOES NOT contain records that are not saved yet into the DB via context.SaveChanges, so we need to check the "Local" collection as well
var localPermissions = DbContext.ProjectAccesses.Local.Where(x => projectsIds.Contains(x.ProjectId)).ToList();
// need to grant access for all new users to project and all it parts
foreach (var projectId in projectsIds)
{
foreach (var user in newTeamsUsers)
{
var userId = new Guid(user);
if (currentPermissions.Any(x => x.ProjectId == projectId && x.PrincipalId == userId))
continue;
if (localPermissions.Any(x => x.ProjectId == projectId && x.PrincipalId == userId))
continue;
DbContext.ProjectAccesses.Add(new ProjectAccess()
{
PrincipalId = userId,
ProjectId = projectId,
Read = 1,
Write = 1
});
}
}
#endregion
#region Saving teams
// Get teams usage info
var teamsUsageInfo = this.GetProjectTeamsUsageInfo(project.Id);
foreach (var t in oldTeamsOfCurrentProject)
{
if (!allProjectTeamsToSave.Contains(t.TeamId))
{
if (!teamsUsageInfo.ContainsKey(t.TeamId) || !teamsUsageInfo[t.TeamId].HasActualsResourceAllocations)
{
// Team has no actuals assigned, and may be removed
DbContext.Team2Project.Remove(t);
}
else
{
// Team can not be deleted because of existing actuals for it. Add it back to result list
if (!result.Contains(t.TeamId))
result.Add(t.TeamId);
}
}
}
foreach (var tId in allProjectTeamsToSave.Except(oldTeamIds))
{
DbContext.Team2Project.Add(new Team2Project()
{
Id = Guid.NewGuid(),
ProjectId = project.Id,
TeamId = tId
});
var wfMan = new WorkFlowManager(this.DbContext);
//do notifications for new teams on project/scenario
foreach (var s in project.Scenarios)
{
var state = wfMan.GetCurrentState(s.Id);
}
}
#endregion
return result;
}
public void SaveStrategicGoals(Project project, List<Guid> strategicGoals)
{
if (project == null || project.Id == Guid.Empty)
return;
if (strategicGoals == null)
strategicGoals = new List<Guid>();
#region Saving
var oldGoalsOfCurrentProject = DbContext.StrategicGoal2Project.Where(x => x.ProjectId == project.Id).ToList();
var oldGoalsIds = oldGoalsOfCurrentProject.Select(x => x.StrategicGoalId).ToList();
foreach (var t in oldGoalsOfCurrentProject)
{
if (!strategicGoals.Contains(t.StrategicGoalId))
DbContext.StrategicGoal2Project.Remove(t);
}
foreach (var tId in strategicGoals.Except(oldGoalsIds))
{
DbContext.StrategicGoal2Project.Add(new StrategicGoal2Project()
{
Id = Guid.NewGuid(),
ProjectId = project.Id,
StrategicGoalId = tId
});
}
#endregion
}
/// <summary>
/// Returns list of IDs for all child projects and parts for given project via recursive search
/// (including given project ID)
/// </summary>
/// <param name="project">Root project for the search</param>
/// <returns></returns>
public IList<Guid> GetSubprojectsAndParts(Project project)
{
List<Guid> result = new List<Guid>();
if (project == null)
return result;
if (project.ChildProjects != null)
{
foreach (Project child in project.ChildProjects)
{
IList<Guid> subProjects = GetSubprojectsAndParts(child);
result.AddRange(subProjects);
}
}
result.Add(project.Id);
return result;
}
public List<Project> FindProjects(IEnumerable<Guid> projectIds)
{
if (projectIds == null || !projectIds.Any())
return new List<Project>();
return DbContext.Projects.Where(x => projectIds.Contains(x.Id)).ToList();
}
/// <summary>
/// Returns projects, that has scenarios with resource allocations for resources, linked
/// to specified userId by e-mail field
/// </summary>
/// <param name="userId">User Id to get projects for</param>
public List<Guid> GetAssignedProjectsByUserId(Guid userId)
{
List<Guid> result = null;
var projectsAndParts =
(from usr in DbContext.AspNetUsers
join pr in DbContext.PeopleResources on usr.Email equals pr.Email
join prAlloc in DbContext.PeopleResourceAllocations on pr.Id equals prAlloc.PeopleResourceId
join scen in DbContext.Scenarios on prAlloc.ScenarioId equals scen.Id
join prjPrt in DbContext.Projects on scen.ParentId equals prjPrt.Id
where scen.Status == (int)ScenarioStatus.Active
select new
{
Id = prjPrt.Id,
ParentProjectId = prjPrt.ParentProjectId
}).Distinct().AsNoTracking().ToList();
result =
projectsAndParts.Select(x => x.ParentProjectId ?? x.Id).Distinct().ToList();
return result;
}
public List<ProjectListModel> GetProjects4User(Guid userId, bool groupByTeam, SortedColumn sortedColumn, int startIndex, int pageSize, string searchString, out int totalRecordCount, out int searchRecordCount)
{
if (groupByTeam)
{
return GetGrouped(userId, sortedColumn, startIndex, pageSize, searchString, out totalRecordCount, out searchRecordCount);
}
else
{
return GetUnGrouped(userId, sortedColumn, startIndex, pageSize, searchString, out totalRecordCount, out searchRecordCount);
}
}
public List<ProjectWithChildrenView> GetProjectsWithChildren(Guid userId, LoadProjectItems includedItems = LoadProjectItems.None)
{
var baseQuery = GetProjectWithChildrenViewQuery(userId);
var projectsDict = baseQuery.Where(t => !t.HasChildren).GroupBy(x => x.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
var projects = projectsDict.Values.ToList();
FillProjectsWithChildren(projectsDict, includedItems);
return projects;
}
public List<ProjectWithChildrenView> GetProjectsWithChildrenByCompany(Guid companyId, LoadProjectItems includedItems = LoadProjectItems.All)
{
var result = this.GetProjectsWithChildrenByCompanies(new List<Guid>() { companyId }, includedItems);
return result;
}
public List<ProjectWithChildrenView> GetProjectsWithChildrenByCompanies(List<Guid> companies, LoadProjectItems includedItems = LoadProjectItems.All)
{
var userId = SecurityManager.GetUserPrincipal();
var userCompanies =
(new CompanyManager(DbContext)).GetCompaniesByUser(null)
.Where(x => companies.Contains(x.Id) || (x.ParentCompanyId.HasValue && companies.Contains(x.ParentCompanyId.Value)))
.ToList();
var companyIds = userCompanies.Select(x => x.Id);
var baseQuery = GetProjectWithChildrenViewQuery(userId);
var projectsDict = (from project in baseQuery
where !project.HasChildren &&
((project.CompanyId.HasValue && companyIds.Contains(project.CompanyId.Value)) ||
(project.ParentProject != null && project.ParentProject.CompanyId.HasValue && companyIds.Contains(project.ParentProject.CompanyId.Value)))
select project).GroupBy(x => x.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
var projects = projectsDict.Values.ToList();
FillProjectsWithChildren(projectsDict, includedItems);
return projects;
}
/// <summary>
/// Get projects, filtered by user access permissions and given teams
/// </summary>
/// <param name="teams">A collection of team Ids.</param>
/// <param name="tags">A collection of tag Ids.</param>
/// <returns>A list of <see cref="Project"/> objects.</returns>
public List<ProjectWithChildrenView> GetProjectsWithChildrenByTeams(IEnumerable<Guid> teams, IEnumerable<Guid> tags = null, LoadProjectItems includedItems = LoadProjectItems.All)
{
var userId = SecurityManager.GetUserPrincipal();
if (teams == null || !teams.Any())
return new List<ProjectWithChildrenView>();
var baseQuery = GetProjectWithChildrenViewQuery(userId);
var query = (from project in baseQuery
join t2p in DbContext.Team2Project.AsNoTracking() on project.Id equals t2p.ProjectId
where teams.Contains(t2p.TeamId) && !project.HasChildren
select project);
if (tags != null && tags.Any())
query = query.Where(x => DbContext.TagLinks.Any(tl => tl.ParentID == x.Id && tags.Contains(tl.TagID)));
var projectsDict = query.AsParallel().GroupBy(x => x.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
var projects = projectsDict.Values.ToList();
FillProjectsWithChildren(projectsDict, includedItems);
return projects;
}
public List<ProjectWithChildrenView> GetProjectsWithChildrenByView(Guid viewId, LoadProjectItems includedItems = LoadProjectItems.All)
{
if (viewId.Equals(Guid.Empty))
throw new ArgumentNullException("viewId");
var userId = SecurityManager.GetUserPrincipal();
var projects = new List<ProjectWithChildrenView>();
var companyMngr = new CompanyManager(DbContext);
var teamsMngr = new TeamManager(DbContext);
var viewsMngr = new ViewManager(DbContext);
// Get companies from view
var viewCompanies = new List<Guid>();
var viewData = viewsMngr.LoadWithChildCollections(viewId, true);
if (viewData == null)
return projects;
if (viewData.Companies != null)
{
var companies = companyMngr.GetCompaniesByUserFiltered(viewData.Companies.ToList()).ToList();
viewCompanies = companies.Select(x => x.Id).ToList();
}
// Get teams from view
var viewTeams = teamsMngr.GetTeamsByViewsByUser(new List<Guid>() { viewId }, userId).Select(t => t.TeamId);
var projectsViaCompanies = this.GetProjectsWithChildrenByCompanies(viewCompanies, includedItems: LoadProjectItems.None);
var projectsViaTeams = this.GetProjectsWithChildrenByTeams(viewTeams, includedItems: LoadProjectItems.None);
// Get projects from companies, which where not got from teams
var projectsViaTeamsIds = projectsViaTeams.Select(x => x.Id).ToList();
var newProjectsViaCompanies = projectsViaCompanies.Where(x => !projectsViaTeamsIds.Contains(x.Id)).ToList();
projects.AddRange(projectsViaTeams);
projects.AddRange(newProjectsViaCompanies);
var projectsDict = projects.GroupBy(t => t.Id).ToDictionary(k => k.Key, el => el.FirstOrDefault());
FillProjectsWithChildren(projectsDict, includedItems);
return projects;
}
public List<ProjectWithChildrenView> GetProjectsWithChildrenByStatusAndClient(IEnumerable<Guid> statuses, IEnumerable<Guid> clients, IEnumerable<Guid> projects, LoadProjectItems includedItems = LoadProjectItems.All)
{
var userId = SecurityManager.GetUserPrincipal();
var query = GetProjectWithChildrenViewQuery(userId).Where(x => !x.HasChildren);
if (statuses != null && statuses.Any())
query = query.Where(x => statuses.Contains(x.StatusId));
if (clients != null && clients.Any())
query = query.Where(x => x.ClientId.HasValue && clients.Contains(x.ClientId.Value));
if (projects != null && projects.Any())
query = query.Where(x => projects.Contains(x.Id));
var projectsDict = query.AsParallel().GroupBy(x => x.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
FillProjectsWithChildren(projectsDict, includedItems);
return projectsDict.Values.ToList();
}
public List<ProjectWithChildrenView> GetProjectsWithChildrenByResource(Guid resourceId, LoadProjectItems includedItems = LoadProjectItems.All)
{
if (resourceId == Guid.Empty)
return new List<ProjectWithChildrenView>();
var userId = SecurityManager.GetUserPrincipal();
var baseQuery = GetProjectWithChildrenViewQuery(userId);
var projectsDict = (from resAllocation in DbContext.PeopleResourceAllocations
join scen in DbContext.Scenarios on resAllocation.ScenarioId equals scen.Id
join proj in baseQuery on scen.ParentId equals proj.Id
where resAllocation.PeopleResourceId.Equals(resourceId) && !proj.HasChildren
select proj).GroupBy(x => x.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
var projects = projectsDict.Values.ToList();
FillProjectsWithChildren(projectsDict, includedItems);
return projects;
}
/// <summary>
/// Method used to load scenarios for already loaded projects. Intended to be used in GetProjectsWithChildrenBy... methods
/// for on-demand loading of only requried objects the same way in all methods.
/// </summary>
/// <param name="projectIds">A collection of project Ids.</param>
/// <returns>Dictionary where key=project.Id and value=collection of scenarios for the specified project.</returns>
/// <remarks>
/// Do not intended to be used as standalone public method. For general purpose of loading project scenarios use
/// ScenarioManager.GetScenarios4Projects method directly.
/// </remarks>
private Dictionary<Guid, IEnumerable<Scenario>> GetProjectScenarios(IEnumerable<Guid> projectIds)
{
var scenariomanager = new ScenarioManager(DbContext);
return scenariomanager.GetScenarios4Projects(projectIds.ToList(), null, null, noTracking: true)
.GroupBy(t => t.ParentId.Value).ToDictionary(t => t.Key, el => el.AsEnumerable());
}
/// <summary>
/// Method used to load teams for already loaded projects. Intended to be used in GetProjectsWithChildrenBy... methods
/// for on-demand loading of only requried objects the same way in all methods.
/// </summary>
/// <param name="projectIds">A collection of project Ids.</param>
/// <returns>Dictionary where key=project.Id and value=collection of team.Id for the specified project.</returns>
/// <remarks>
/// Do not intended to be used as standalone public method. For general purpose of loading project teams use
/// public methods.
/// </remarks>
private Dictionary<Guid, IEnumerable<Guid>> GetProjectTeams(IEnumerable<Guid> projectIds)
{
return DbContext.Team2Project.AsNoTracking().Where(t => projectIds.Contains(t.ProjectId))
.GroupBy(t => t.ProjectId).ToDictionary(t => t.Key, el => el.Select(t => t.TeamId));
}
/// <summary>
/// Method used to load strategic goals for already loaded projects. Intended to be used in GetProjectsWithChildrenBy... methods
/// for on-demand loading of only requried objects the same way in all methods.
/// </summary>
/// <param name="projectIds">A collection of project Ids.</param>
/// <returns>Dictionary where key=project.Id and value=collection of StrategicGoal.Id for the specified project.</returns>
/// <remarks>
/// Do not intended to be used as standalone public method. For general purpose of loading project strategic goals use
/// public methods.
/// </remarks>
private Dictionary<Guid, IEnumerable<Guid>> GetProjectGoals(IEnumerable<Guid> projectIds)
{
return DbContext.StrategicGoal2Project.AsNoTracking().Where(t => projectIds.Contains(t.ProjectId))
.GroupBy(t => t.ProjectId).ToDictionary(t => t.Key, el => el.Select(t => t.StrategicGoalId));
}
private void FillProjectsWithChildren(Dictionary<Guid, ProjectWithChildrenView> projectsDict, LoadProjectItems includedItems = LoadProjectItems.None)
{
if (includedItems == LoadProjectItems.None)
return;
Dictionary<Guid, IEnumerable<Scenario>> scenarios = new Dictionary<Guid, IEnumerable<Scenario>>();
Dictionary<Guid, IEnumerable<Guid>> projectTeamsDict = new Dictionary<Guid, IEnumerable<Guid>>();
Dictionary<Guid, IEnumerable<Guid>> projectGoalsDict = new Dictionary<Guid, IEnumerable<Guid>>();
// load scenarios for all retrieved projects
if (includedItems.HasFlag(LoadProjectItems.Scenarios))
{
scenarios = GetProjectScenarios(projectsDict.Keys.ToList());
}
// load Team2Project relations for all retrieved projects
if (includedItems.HasFlag(LoadProjectItems.Teams))
{
projectTeamsDict = GetProjectTeams(projectsDict.Keys.ToList());
}
// load StrategicGoals2Project relations for all retrieved projects
if (includedItems.HasFlag(LoadProjectItems.Goals))
{
projectGoalsDict = GetProjectGoals(projectsDict.Keys.ToList());
}
foreach (var project in projectsDict.Values)
{
if (scenarios.ContainsKey(project.Id))
{
project.Scenarios = scenarios[project.Id].Select(x => new ProjectWithChildrenView.ScenarioView()
{
Id = x.Id,
ParentId = x.ParentId,
Name = x.Name,
IsBottomUp = x.IsBottomUp,
Status = (ScenarioStatus?)x.Status,
Type = (ScenarioType?)x.Type,
StartDate = x.StartDate,
EndDate = x.EndDate,
DateCreated = x.DateCreated,
}).ToList();
}
if (projectTeamsDict.ContainsKey(project.Id))
project.Teams = projectTeamsDict[project.Id].ToList();
if (projectGoalsDict.ContainsKey(project.Id))
project.StrategicGoals = projectGoalsDict[project.Id].ToList();
}
}
/// <summary>
/// Return project teams. If filter specified, the result is filtered by existance in the filter
/// </summary>
/// <param name="projectId">Project Id to get teams of</param>
/// <param name="filter">Filter for result or NULL</param>
/// <returns></returns>
private List<ProjectListModel> GetGrouped(Guid userId, SortedColumn sortedColumn, int startIndex, int pageSize, string searchString, out int totalRecordCount, out int searchRecordCount)
{
totalRecordCount = searchRecordCount = 0;
if (userId == Guid.Empty)
return new List<ProjectListModel>();
if (sortedColumn == null)
sortedColumn = new SortedColumn("Name", "asc");
var columnName = "Name";
var direction = SortingDirection.Ascending;
if (sortedColumn.PropertyName == "Priority")
{
columnName = "Priority";
direction = sortedColumn.Direction;
}
if (sortedColumn.PropertyName == "Classification")
{
columnName = "TypeName";
direction = sortedColumn.Direction;
}
if (sortedColumn.PropertyName == "Status")
{
columnName = "StatusName";
direction = sortedColumn.Direction;
}
if (sortedColumn.PropertyName == "Client")
{
columnName = "ClientName";
direction = sortedColumn.Direction;
}
if (sortedColumn.PropertyName.Equals("ProjectName", StringComparison.InvariantCultureIgnoreCase))
{
columnName = "Name";
direction = sortedColumn.Direction;
}
if (sortedColumn.PropertyName.Equals("ProjectNumber", StringComparison.InvariantCultureIgnoreCase))
{
columnName = "ProjectNumber";
direction = sortedColumn.Direction;
}
if (sortedColumn.PropertyName.Equals("Deadline", StringComparison.InvariantCultureIgnoreCase))
{
columnName = "Deadline";
direction = sortedColumn.Direction;
}
if (sortedColumn.PropertyName.Equals("ActiveScenario", StringComparison.InvariantCultureIgnoreCase))
{
columnName = "ActiveScenarioName";
direction = sortedColumn.Direction;
}
var projectsWithParts = GetProjectsWithPartsThroughSortingGrouped(userId, columnName, direction == SortingDirection.Ascending ? "asc" : "desc", searchString, startIndex, pageSize, out totalRecordCount, out searchRecordCount);
var parentsIds = projectsWithParts.Select(x => x.ProjectId).Distinct().ToList();
var partsIds = projectsWithParts.Where(x => x.PartId.HasValue).Select(x => x.PartId.Value).ToList();
var projectsIds = parentsIds.Union(partsIds);
var projects = DbContext.VW_ProjectAccessByUserExtended.Where(x => x.UserId == userId && projectsIds.Contains(x.Id)).ToList();
var projectsList = new List<ProjectListModel>();
//switch (sortedColumn.PropertyName)
//{
// case "Teams":
// if (sortedColumn.Direction == SortingDirection.Ascending)
// projectsWithParts = projectsWithParts.OrderBy(x => x.TeamName).ToList();
// else
// projectsWithParts = projectsWithParts.OrderByDescending(x => x.TeamName).ToList();
// break;
//}
foreach (var item1 in projectsWithParts.GroupBy(x => x.TeamName))
{
//projectsList.Add(new ProjectListModel()
//{
// ProjectName = item1.Key
//});
foreach (var item in item1)
{
var project = projects.FirstOrDefault(x => x.Id == item.ProjectId);
if (project == null)
continue;
if (!projectsList.Any(x => x.Id == item.ProjectId && x.Rank == item.Rank && ((string.IsNullOrEmpty(x.Team) && string.IsNullOrEmpty(item.TeamName)) || x.Team.Equals(item.TeamName, StringComparison.InvariantCultureIgnoreCase))))
{
#region Add Project
projectsList.Add(new ProjectListModel()
{
Id = project.Id,
ProjectId = project.Id,
ProjectName = project.Name ?? string.Empty,
ProjectNumber = project.ProjectNumber ?? string.Empty,
Status = project.StatusName,
Classification = project.TypeName,
Client = project.ClientName,
Company = project.CompanyName,
Priority = project.Priority,
WritePermissionEnabledForCurrentUser = (project.Write > 0),
ActiveScenario = project.ActiveScenarioId.HasValue ? new ScenarioInProjectModel()
{
Id = project.ActiveScenarioId.Value,
Name = project.ActiveScenarioName
} : null,
HasChildren = project.HasChildren,
Deadline = project.Deadline,
//Teams = project.Teams,
Team = item.TeamName ?? string.Empty,
TeamId = item.TeamId ?? Guid.Empty,
Rank = item.Rank,
ProjectParts = new List<ProjectPartListModel>(),
ReadOnly = (!project.Write.HasValue || (project.Write.Value < 1))
});
#endregion
}
if (!item.PartId.HasValue)
continue;
var partProject = projects.FirstOrDefault(x => x.Id == item.PartId.Value);
if (partProject == null)
continue;
var parentProject = projectsList.FirstOrDefault(x => x.Id == item.ProjectId && x.Rank == item.Rank && ((string.IsNullOrEmpty(x.Team) && string.IsNullOrEmpty(item.TeamName)) || x.Team.Equals(item.TeamName, StringComparison.InvariantCultureIgnoreCase)));
parentProject?.ProjectParts.Add(new ProjectPartListModel
{
#region Add Project Part
Id = partProject.Id,
ProjectId = project.Id,
ProjectName = partProject.Name ?? string.Empty,
ProjectNumber = partProject.ProjectNumber ?? string.Empty,
Status = partProject.StatusName,
Classification = partProject.TypeName,
Client = partProject.ClientName,
Company = partProject.CompanyName,
Priority = partProject.Priority,
WritePermissionEnabledForCurrentUser = partProject.Write > 0,
ActiveScenario = partProject.ActiveScenarioId.HasValue
? new ScenarioInProjectModel
{
Id = partProject.ActiveScenarioId.Value,
Name = partProject.ActiveScenarioName
}
: null,
PartNum = partProject.PartNum,
Deadline = partProject.Deadline,
//Teams = partProject.Teams,
Team = item.TeamName ?? string.Empty,
TeamId = item.TeamId ?? Guid.Empty,
#endregion
});
}
}
// Fill project models with comma-separated team names
if (projectsList.Any())
{
var projectTeamsDict = GetProjectTeams(projectsIds);
var teamManager = new TeamManager(DbContext);
var teamsDict = teamManager.GetTeamsByUserFiltered(userId.ToString(), projectTeamsDict.SelectMany(t => t.Value).Distinct().ToList(), null, null)
.GroupBy(t => t.TeamId).ToDictionary(t => t.Key, el => el.FirstOrDefault());
foreach (var project in projectsList)
{
if (projectTeamsDict.ContainsKey(project.Id))
project.Teams = string.Join(", ",
teamsDict.Where(t => projectTeamsDict[project.Id].Any(x => t.Key == x)).Select(t => t.Value.Name)) ?? string.Empty;
if (project.ProjectParts != null && project.ProjectParts.Any())
foreach (var part in project.ProjectParts)
{
if (projectTeamsDict.ContainsKey(part.Id))
part.Teams = string.Join(", ",
teamsDict.Where(t => projectTeamsDict[part.Id].Any(x => t.Key == x)).Select(t => t.Value.Name)) ?? string.Empty;
}
}
}
return projectsList;
}
private List<ProjectListModel> GetUnGrouped(Guid userId, SortedColumn sortedColumn, int startIndex, int pageSize, string searchString, out int totalRecordCount, out int searchRecordCount)
{
totalRecordCount = searchRecordCount = 0;
if (userId == Guid.Empty)
return new List<ProjectListModel>();
if (sortedColumn == null)
sortedColumn = new SortedColumn("ProjectName", "asc");
if (sortedColumn.PropertyName == "Priority" ||
sortedColumn.PropertyName == "Classification" ||
sortedColumn.PropertyName == "Status" ||
sortedColumn.PropertyName == "ActiveScenario" ||
sortedColumn.PropertyName == "Client")
{
var columnName = string.Empty;
if (sortedColumn.PropertyName == "Priority")
{
columnName = "Priority";
}
if (sortedColumn.PropertyName == "Classification")
{
columnName = "TypeName";
}
if (sortedColumn.PropertyName == "Status")
{
columnName = "StatusName";
}
if (sortedColumn.PropertyName == "Client")
{
columnName = "ClientName";
}
if (sortedColumn.PropertyName == "ActiveScenario")
{
columnName = "ActiveScenarioName";
}
var projectsWithParts = GetProjectsWithPartsThroughSorting(userId, columnName, sortedColumn.Direction == SortingDirection.Ascending ? "asc" : "desc", searchString, startIndex, pageSize, out totalRecordCount, out searchRecordCount);
var parentsIds = projectsWithParts.Select(x => x.ProjectId).Distinct().ToList();
var partsIds = projectsWithParts.Where(x => x.PartId.HasValue).Select(x => x.PartId.Value).ToList();
var projectsIds = parentsIds.Union(partsIds);
var projects = DbContext.VW_ProjectAccessByUserExtended.Where(x => x.UserId == userId && projectsIds.Contains(x.Id)).ToList();
var projectsList = new List<ProjectListModel>();
foreach (var item in projectsWithParts.Where(x => x.ProjectId != null))
{
var project = projects.FirstOrDefault(x => x.Id == item.ProjectId);
if (project == null)
continue;
if (!projectsList.Any(x => x.Id == item.ProjectId && x.Rank == item.Rank))
{
projectsList.Add(new ProjectListModel()
{
Id = project.Id,
ProjectId = project.Id,
ProjectName = project.Name ?? string.Empty,
ProjectNumber = project.ProjectNumber ?? string.Empty,
Status = project.StatusName,
Classification = project.TypeName,
Client = project.ClientName,
Company = project.CompanyName,
Priority = project.Priority,
WritePermissionEnabledForCurrentUser = (project.Write > 0),
ActiveScenario = project.ActiveScenarioId.HasValue ? new ScenarioInProjectModel()
{
Id = project.ActiveScenarioId.Value,
Name = project.ActiveScenarioName
} : null,
HasChildren = project.HasChildren,
Deadline = project.Deadline,
//Teams = project.Teams,
Team = item.TeamName,
Rank = item.Rank,
ProjectParts = new List<ProjectPartListModel>(),
ReadOnly = (!project.Write.HasValue || (project.Write.Value < 1))
});
}
if (!item.PartId.HasValue)
continue;
var partProject = projects.FirstOrDefault(x => x.Id == item.PartId.Value);
if (partProject == null)
continue;
var parentProject = projectsList.FirstOrDefault(x => x.Id == item.ProjectId && x.Rank == item.Rank);
parentProject.ProjectParts.Add(new ProjectPartListModel()
{
Id = partProject.Id,
ProjectId = project.Id,
ProjectName = partProject.Name ?? string.Empty,
ProjectNumber = partProject.ProjectNumber ?? string.Empty,
Status = partProject.StatusName,
Classification = partProject.TypeName,
Client = partProject.ClientName,
Company = partProject.CompanyName,
Priority = partProject.Priority,
WritePermissionEnabledForCurrentUser = (partProject.Write > 0),
ActiveScenario = partProject.ActiveScenarioId.HasValue ? new ScenarioInProjectModel()
{
Id = partProject.ActiveScenarioId.Value,
Name = partProject.ActiveScenarioName
} : null,
PartNum = partProject.PartNum,
Deadline = partProject.Deadline,
//Teams = partProject.Teams,
Team = item.TeamName
});
}
// Fill project models with comma-separated team names
if (null != projectsList && projectsList.Any())
{
var projectIds = projectsList.Select(t => t.Id).Union(projectsList.Where(t => t.ProjectParts != null && t.ProjectParts.Any())
.SelectMany(t => t.ProjectParts).Select(t => t.Id));
var projectTeamsDict = GetProjectTeams(projectIds);
var teamManager = new TeamManager(DbContext);
var teamsDict = teamManager.GetTeamsByUserFiltered(userId.ToString(), projectTeamsDict.SelectMany(t => t.Value).Distinct().ToList(), null, null)
.GroupBy(t => t.TeamId).ToDictionary(t => t.Key, el => el.FirstOrDefault());
foreach (var project in projectsList)
{
if (projectTeamsDict.ContainsKey(project.Id))
project.Teams = string.Join(", ",
teamsDict.Where(t => projectTeamsDict[project.Id].Any(x => t.Key == x)).Select(t => t.Value.Name)) ?? string.Empty;
if (project.ProjectParts != null && project.ProjectParts.Any())
foreach (var part in project.ProjectParts)
{
if (projectTeamsDict.ContainsKey(part.Id))
part.Teams = string.Join(", ",
teamsDict.Where(t => projectTeamsDict[part.Id].Any(x => t.Key == x)).Select(t => t.Value.Name)) ?? string.Empty;
}
}
}
return projectsList;
}
else
{
//sort
var sortField = string.Empty;
switch (sortedColumn.PropertyName)
{
case "ProjectName":
sortField = "Name";
break;
//case "Teams":
// if (sortedColumn.Direction == SortingDirection.Ascending)
// query = query.OrderBy(x => x.Teams);
// else
// query = query.OrderByDescending(x => x.Teams);
// break;
default:
sortField = sortedColumn.PropertyName;
break;
}
var totalCount = new ObjectParameter("totalCount", typeof(int));
var mainProjects = DbContext.sp_ProjectSearch(userId, "%" + searchString + "%", startIndex, pageSize, sortField,
sortedColumn.Direction == SortingDirection.Ascending, totalCount).ToList();
var projectIds = mainProjects.Select(t => t.Id);
var projectParts = DbContext.VW_ProjectAccessByUserExtended.Where(t => t.UserId.Equals(userId) && t.ParentProjectId.HasValue && projectIds.Contains(t.ParentProjectId.Value))
.GroupBy(gr => gr.ParentProjectId.Value).ToDictionary(k => k.Key, el => el.ToList());
var projectsToDisplay = mainProjects.Select(p => new ProjectListModel()
{
Id = p.Id,
ProjectId = p.Id,
ProjectName = p.Name ?? string.Empty,
ProjectNumber = p.ProjectNumber ?? string.Empty,
Status = p.StatusName,
Classification = p.TypeName,
Client = p.ClientName,
Company = p.CompanyName,
Priority = p.Priority,
ReadOnly = (!p.Write.HasValue || (p.Write.Value < 1)),
WritePermissionEnabledForCurrentUser = (p.Write > 0),
ActiveScenario = p.ActiveScenarioId.HasValue ? new ScenarioInProjectModel()
{
Id = p.ActiveScenarioId.Value,
Name = p.ActiveScenarioName
} : null,
HasChildren = p.HasChildren,
Deadline = p.Deadline,
//Teams = p.Teams,
ProjectParts = projectParts.ContainsKey(p.Id) ? projectParts[p.Id].Where(pp => string.IsNullOrEmpty(searchString) ||
(p.Name ?? string.Empty).Contains(searchString) || (p.ProjectNumber ?? string.Empty).Contains(searchString) ||
(p.ActiveScenarioName ?? string.Empty).Contains(searchString) || (pp.Name ?? string.Empty).Contains(searchString) ||
(pp.ActiveScenarioName ?? string.Empty).Contains(searchString))
.Select(pp => new ProjectPartListModel
{
Id = pp.Id,
ProjectId = p.Id,
ProjectName = pp.Name ?? string.Empty,
ProjectNumber = pp.ProjectNumber ?? string.Empty,
Status = pp.StatusName,
Classification = pp.TypeName,
Client = pp.ClientName,
Company = pp.CompanyName,
Priority = pp.Priority,
Deadline = pp.Deadline,
WritePermissionEnabledForCurrentUser = (pp.Write > 0),
ActiveScenario = pp.ActiveScenarioId.HasValue ? new ScenarioInProjectModel()
{
Id = pp.ActiveScenarioId.Value,
Name = pp.ActiveScenarioName
} : null,
PartNum = pp.PartNum,
//Teams = pp.Teams
}).ToList() : new List<ProjectPartListModel>()
}).ToList();
totalRecordCount = DbContext.VW_ProjectAccessByUserExtended.Count(x => x.UserId.Equals(userId) && !x.ParentProjectId.HasValue);
searchRecordCount = Convert.ToInt32(totalCount.Value);
switch (sortedColumn.PropertyName)
{
case "ProjectNumber":
if (sortedColumn.Direction == SortingDirection.Ascending)
projectsToDisplay.ForEach(p => p.ProjectParts = p.ProjectParts.OrderBy(pp => pp.ProjectNumber).ToList());
else
projectsToDisplay.ForEach(p => p.ProjectParts = p.ProjectParts.OrderByDescending(pp => pp.ProjectNumber).ToList());
break;
//case "Teams":
// if (sortedColumn.Direction == SortingDirection.Ascending)
// projectsToDisplay.ForEach(p => p.ProjectParts = p.ProjectParts.OrderBy(pp => pp.Teams).ToList());
// else
// projectsToDisplay.ForEach(p => p.ProjectParts = p.ProjectParts.OrderByDescending(pp => pp.Teams).ToList());
// break;
default:
if (sortedColumn.Direction == SortingDirection.Ascending)
projectsToDisplay.ForEach(p => p.ProjectParts = p.ProjectParts.OrderBy(pp => pp.ProjectName).ToList());
else
projectsToDisplay.ForEach(p => p.ProjectParts = p.ProjectParts.OrderByDescending(pp => pp.ProjectName).ToList());
break;
}
// Fill project models with comma-separated team names
if (null != projectsToDisplay && projectsToDisplay.Any())
{
var allProjectIds = projectsToDisplay.Select(t => t.Id).Union(projectsToDisplay.Where(t => t.ProjectParts != null && t.ProjectParts.Any())
.SelectMany(t => t.ProjectParts).Select(t => t.Id));
var projectTeamsDict = GetProjectTeams(allProjectIds);
var teamManager = new TeamManager(DbContext);
var teamsDict = teamManager.GetTeamsByUserFiltered(userId.ToString(), projectTeamsDict.SelectMany(t => t.Value).Distinct().ToList(), null, null)
.GroupBy(t => t.TeamId).ToDictionary(t => t.Key, el => el.FirstOrDefault());
foreach (var project in projectsToDisplay)
{
if (projectTeamsDict.ContainsKey(project.Id))
project.Teams = string.Join(", ",
teamsDict.Where(t => projectTeamsDict[project.Id].Any(x => t.Key == x)).Select(t => t.Value.Name)) ?? string.Empty;
if (project.ProjectParts != null && project.ProjectParts.Any())
foreach (var part in project.ProjectParts)
{
if (projectTeamsDict.ContainsKey(part.Id))
part.Teams = string.Join(", ",
teamsDict.Where(t => projectTeamsDict[part.Id].Any(x => t.Key == x)).Select(t => t.Value.Name)) ?? string.Empty;
}
}
}
return projectsToDisplay;
}
}
/// <summary>
/// Loads a collection of simple project models for the specified project Ids, INCLUDING not accessible to current user (if userId is not set).
/// </summary>
/// <param name="projectIds">A lsit of project.Id values to load.</param>
/// <param name="userId">A user.Id of the current user.</param>
/// <returns>A collection of project models with the specified <paramref name="projectIds"/>. If <paramref name="userId"/> is set
/// then returned collection contains only project accessible by current user.</returns>
/// <remarks>
/// This method loads all projects for Ids specified in argument, INCLUDING not accessible to current user (if userId is not set).
/// </remarks>
public IEnumerable<ProjectListModel> LoadProjects(IEnumerable<Guid> projectIds)
{
return DbContext.Projects.AsNoTracking().Include(t => t.ParentProject).Where(t => projectIds.Contains(t.Id)).Select(t => new ProjectListModel
{
Id = t.Id,
Deadline = t.Deadline,
HasChildren = t.HasChildren,
PartNum = t.PartNum,
Priority = t.Priority,
Probability = (short)(t.Probability * 100),
ProjectId = t.ParentProjectId ?? t.Id,
ProjectName = t.ParentProject != null ? t.Name + ": " + t.ParentProject.Name : t.Name,
ProjectNumber = t.ProjectNumber
});
}
public List<Project> GetProjects(IEnumerable<Guid> projects, bool readOnly = false)
{
if (projects == null || !projects.Any())
return new List<Project>();
var projectsQuery = DbContext.Projects.Where(x => projects.Contains(x.Id));
if (readOnly)
projectsQuery = projectsQuery.AsNoTracking();
return projectsQuery.ToList();
}
private List<Project2PartModel> GetProjectsWithPartsThroughSorting(Guid userId, string columnName, string sort, string searchString, int startIndex, int pageSize, out int totalRecordCount, out int searchRecordCount)
{
var query = string.Format(
@";
DECLARE @T TABLE
(
ProjectId uniqueidentifier NOT NULL ,
PartId uniqueidentifier NULL ,
Part{0} {2} NOT NULL ,
[Row] int NOT NULL
)
DECLARE @Result TABLE
(
ProjectId uniqueidentifier NOT NULL ,
PartId uniqueidentifier NULL ,
[Rank] int NOT NULL
)
INSERT INTO @T (ProjectId, PartId, Part{0}, [Row])
SELECT
p.Id AS ProjectId ,
c.Id AS PartId ,
ISNULL(c.{0}, p.{0}) AS Part{0} ,
ROW_NUMBER() OVER
(
ORDER BY ISNULL(c.{0}, p.{0}) {1}, p.Id ASC
) AS [Row]
FROM dbo.VW_ProjectAccessByUserExtended p
LEFT
JOIN dbo.VW_ProjectAccessByUserExtended c
ON c.ParentProjectId = p.Id
AND c.UserId = @UserId
WHERE p.ParentProjectId IS NULL
AND p.UserId = @UserId
AND
(
@SearchString IS NULL OR
p.Name LIKE '%' + @SearchString + '%' OR
p.ProjectNumber LIKE '%' + @SearchString + '%' OR
p.ActiveScenarioName LIKE '%' + @SearchString + '%' OR
--p.Teams LIKE '%' + @SearchString + '%' OR
c.Name LIKE '%' + @SearchString + '%' OR
c.ActiveScenarioName LIKE '%' + @SearchString + '%' OR
p.ClientName LIKE '%' + @SearchString + '%'
--OR c.Teams LIKE '%' + @SearchString + '%'
)
INSERT INTO @Result (ProjectId, PartId, [Rank])
SELECT
Result.ProjectId ,
Result.PartId ,
Result.[Rank]
FROM
(
SELECT
Result.ProjectId ,
Result.PartId ,
DENSE_RANK() OVER
(
ORDER BY Result.[Rank]
) AS [Rank]
FROM
(
SELECT
T.ProjectId ,
T.PartId ,
T.Part{0} ,
(
SELECT TOP 1
ISNULL(InnerT.[Row], 0) + 1
FROM @T AS InnerT
WHERE InnerT.[Row] < T.[Row]
AND InnerT.[ProjectId] <> T.ProjectId
ORDER BY
InnerT.[Row] DESC
) AS [Rank]
FROM @T T
) AS Result
) AS Result
SELECT
r.ProjectId ,
r.PartId ,
r.[Rank]
FROM @Result r
WHERE r.[Rank] BETWEEN @StartIndex AND @EndIndex
SELECT @TotalRecordCount = ISNULL(MAX([Rank]), 0) from @Result
SELECT @SearchRecordCount = ISNULL(MAX([Rank]), 0) from @Result", columnName, sort, columnName == "Priority" ? "decimal(5,2)" : "nvarchar(1600)");
var totalRecordCountParam =
new SqlParameter("@TotalRecordCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
var searchRecordCountParam =
new SqlParameter("@SearchRecordCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
var projects = DbContext.Database.SqlQuery<Project2PartModel>(query, new SqlParameter("@UserId", userId),
new SqlParameter("@StartIndex", startIndex + 1),
new SqlParameter("@EndIndex", startIndex + pageSize),
new SqlParameter("@SearchString", searchString),
totalRecordCountParam, searchRecordCountParam)
.ToList();
totalRecordCount = (int)totalRecordCountParam.Value;
searchRecordCount = (int)searchRecordCountParam.Value;
return projects;
}
private List<Project2PartModel> GetProjectsWithPartsThroughSortingGrouped(Guid userId, string columnName, string sort, string searchString, int startIndex, int pageSize, out int totalRecordCount, out int searchRecordCount)
{
var query = string.Format(
@";
DECLARE @T TABLE
(
ProjectId uniqueidentifier NOT NULL ,
PartId uniqueidentifier NULL ,
Part{0} {2} NULL ,
TeamName nvarchar(255) NULL ,
TeamId uniqueidentifier NULL ,
[Row] int NOT NULL
)
DECLARE @Result TABLE
(
ProjectId uniqueidentifier NOT NULL ,
PartId uniqueidentifier NULL ,
TeamName nvarchar(255) NULL ,
TeamId uniqueidentifier NULL ,
[Rank] int NOT NULL
)
INSERT INTO @T (ProjectId, PartId, Part{0}, TeamName,TeamId, [Row])
SELECT
p.Id AS ProjectId ,
c.Id AS PartId ,
ISNULL(c.{0}, p.{0}) AS Part{0} ,
ISNULL(t_part.Name, t.Name) AS TeamName,
ISNULL(t_part.Id, t.Id) AS TeamId,
ROW_NUMBER() OVER
(
ORDER BY ISNULL(c.{0}, p.{0}) {1}, p.Id ASC
) AS [Row]
FROM dbo.VW_ProjectAccessByUserExtended p
LEFT
JOIN dbo.Team2Project t2p ON t2p.ProjectId = p.Id
LEFT
JOIN dbo.Team t ON t2p.TeamId = t.Id
LEFT
JOIN dbo.VW_ProjectAccessByUserExtended c
ON c.ParentProjectId = p.Id
AND c.UserId = @UserId
LEFT
JOIN dbo.Team2Project t2p_part ON t2p_part.ProjectId = c.Id
LEFT
JOIN dbo.Team t_part ON t2p_part.TeamId = t_part.Id
WHERE p.ParentProjectId IS NULL
AND p.UserId = @UserId
AND
(
@SearchString IS NULL OR
p.Name LIKE '%' + @SearchString + '%' OR
p.ProjectNumber LIKE '%' + @SearchString + '%' OR
p.ActiveScenarioName LIKE '%' + @SearchString + '%' OR
--p.Teams LIKE '%' + @SearchString + '%' OR
c.Name LIKE '%' + @SearchString + '%' OR
c.ActiveScenarioName LIKE '%' + @SearchString + '%' OR
t_part.Name LIKE '%' + @SearchString + '%' OR
t.Name LIKE '%' + @SearchString + '%'
)
group by ISNULL(t_part.Name, t.Name), p.Id, c.Id, isnull(c.{0}, p.{0}), ISNULL(t_part.Id, t.Id)
INSERT INTO @Result (ProjectId, PartId, TeamName, TeamId, [Rank])
SELECT
Result.ProjectId ,
Result.PartId ,
Result.TeamName ,
Result.TeamId ,
Result.[Rank]
FROM
(
SELECT
Result.ProjectId ,
Result.PartId ,
Result.TeamName ,
Result.TeamId ,
DENSE_RANK() OVER
(
ORDER BY Result.[Rank]
) AS [Rank]
FROM
(
SELECT
T.ProjectId ,
T.PartId ,
T.TeamName ,
T.TeamId ,
T.Part{0} ,
(
SELECT TOP 1
ISNULL(InnerT.[Row], 0) + 1
FROM @T AS InnerT
WHERE InnerT.[Row] < T.[Row]
AND InnerT.[ProjectId] <> T.ProjectId
ORDER BY
InnerT.[Row] DESC
) AS [Rank]
FROM @T T
) AS Result
) AS Result
SELECT
r.ProjectId ,
r.PartId ,
r.TeamName ,
r.TeamId ,
r.[Rank]
FROM @Result r
WHERE r.[Rank] BETWEEN @StartIndex AND @EndIndex
SELECT @TotalRecordCount = ISNULL(MAX([Rank]), 0) from @Result
SELECT @SearchRecordCount = ISNULL(MAX([Rank]), 0) from @Result", columnName, sort, columnName == "Priority" ? "decimal(5,2)" : "nvarchar(1600)");
var totalRecordCountParam = new SqlParameter("@TotalRecordCount", SqlDbType.Int);
totalRecordCountParam.Direction = System.Data.ParameterDirection.Output;
var searchRecordCountParam = new SqlParameter("@SearchRecordCount", SqlDbType.Int);
searchRecordCountParam.Direction = System.Data.ParameterDirection.Output;
var projects = DbContext.Database.SqlQuery<Project2PartModel>(query, new SqlParameter("@UserId", userId),
new SqlParameter("@StartIndex", startIndex + 1),
new SqlParameter("@EndIndex", startIndex + pageSize),
new SqlParameter("@SearchString", searchString),
totalRecordCountParam, searchRecordCountParam)
.ToList();
totalRecordCount = (int)totalRecordCountParam.Value;
searchRecordCount = (int)searchRecordCountParam.Value;
return projects;
}
private void CopyScenariosToProject(Guid targetProjectId, List<Scenario> scenarios, Dictionary<Guid, List<ScenarioDetail>> details, Dictionary<Guid, List<Rate>> rates, List<Guid> assignedTeams)
{
if (targetProjectId == Guid.Empty)
throw new InvalidOperationException("targetProjectId is empty");
if (scenarios == null || scenarios.Count <= 0)
throw new ArgumentNullException(nameof(scenarios));
foreach (var scenario in scenarios)
{
if (scenario == null)
continue;
var scenarioClone = new Scenario()
{
Id = Guid.NewGuid(),
ParentId = targetProjectId,
TemplateId = scenario.TemplateId,
Type = scenario.Type,
Name = scenario.Name,
ProjectedRevenue = scenario.ProjectedRevenue,
ExpectedGrossMargin = scenario.ExpectedGrossMargin,
CalculatedGrossMargin = scenario.CalculatedGrossMargin,
CGSplit = scenario.CGSplit,
EFXSplit = scenario.EFXSplit,
StartDate = scenario.StartDate,
EndDate = scenario.EndDate,
Duration = scenario.Duration,
TDDirectCosts = scenario.TDDirectCosts,
BUDirectCosts = scenario.BUDirectCosts,
Shots = scenario.Shots,
TDRevenueShot = scenario.TDRevenueShot,
BURevenueShot = scenario.BURevenueShot,
FreezeRevenue = scenario.FreezeRevenue,
LastUpdate = DateTime.UtcNow,
Color = scenario.Color,
Status = scenario.Status,
UseLMMargin = scenario.UseLMMargin,
ExpectedGrossMargin_LM = scenario.ExpectedGrossMargin_LM,
CalculatedGrossMargin_LM = scenario.CalculatedGrossMargin_LM,
TDDirectCosts_LM = scenario.TDDirectCosts_LM,
BUDirectCosts_LM = scenario.BUDirectCosts_LM,
BURevenueShot_LM = scenario.BURevenueShot_LM,
ShotStartDate = scenario.ShotStartDate,
GrowthScenario = scenario.GrowthScenario,
Actuals_BUDirectCosts = scenario.Actuals_BUDirectCosts,
Actuals_BUDirectCosts_LM = scenario.Actuals_BUDirectCosts_LM,
SystemAttributeObjectID = scenario.SystemAttributeObjectID,
ProjectedExpense = scenario.ProjectedExpense,
CostSavings = scenario.CostSavings,
CostSavingsStartDate = scenario.CostSavingsStartDate,
CostSavingsEndDate = scenario.CostSavingsEndDate,
CostSavingsType = scenario.CostSavingsType,
CostSavingsDescription = scenario.CostSavingsDescription,
ROIDate = scenario.ROIDate,
DateCreated = DateTime.UtcNow,
IsBottomUp = scenario.IsBottomUp
};
scenarioClone.CostSavings1 = scenario.CostSavings1.Select(x => new CostSaving()
{
Id = Guid.NewGuid(),
ScenarioId = scenarioClone.Id,
Year = x.Year,
Month = x.Month,
Cost = x.Cost
}).ToList();
if (assignedTeams != null && assignedTeams.Count > 0)
{
scenarioClone.PeopleResourceAllocations = scenario.PeopleResourceAllocations
.Where(x => assignedTeams.Contains(x.TeamId))
.Select(x =>
new PeopleResourceAllocation()
{
Id = Guid.NewGuid(),
ScenarioId = scenarioClone.Id,
TeamId = x.TeamId,
PeopleResourceId = x.PeopleResourceId,
ExpenditureCategoryId = x.ExpenditureCategoryId,
WeekEndingDate = x.WeekEndingDate,
Quantity = x.Quantity
}).ToList();
scenarioClone.TeamAllocations = scenario.TeamAllocations
.Where(x => assignedTeams.Contains(x.TeamId))
.Select(x =>
new TeamAllocation()
{
Id = Guid.NewGuid(),
ScenarioId = scenarioClone.Id,
TeamId = x.TeamId,
ExpenditureCategoryId = x.ExpenditureCategoryId,
WeekEndingDate = x.WeekEndingDate,
Quantity = x.Quantity
}).ToList();
}
scenarioClone.Scenario2Group = scenario.Scenario2Group.Select(x => new Scenario2Group()
{
Id = Guid.NewGuid(),
ScenarioId = scenarioClone.Id,
GroupId = x.GroupId
}).ToList();
// copy scenario local rates
if (rates != null && rates.ContainsKey(scenario.Id))
{
foreach (var rate in rates[scenario.Id])
{
DbContext.Rates.Add(new Rate()
{
Id = Guid.NewGuid(),
ParentId = scenarioClone.Id,
ExpenditureCategoryId = rate.ExpenditureCategoryId,
Rate1 = rate.Rate1,
StartDate = rate.StartDate,
EndDate = rate.EndDate,
FreezeRate = rate.FreezeRate,
Type = rate.Type,
DerivedId = rate.DerivedId
});
}
}
if (details != null && details.ContainsKey(scenario.Id))
{
foreach (var detail in details[scenario.Id])
{
DbContext.ScenarioDetail.Add(new ScenarioDetail()
{
Id = Guid.NewGuid(),
ParentID = scenarioClone.Id,
ExpenditureCategoryId = detail.ExpenditureCategoryId,
WeekEndingDate = detail.WeekEndingDate,
Quantity = detail.Quantity,
WeekOrdinal = detail.WeekOrdinal,
Cost = detail.Cost,
});
}
}
DbContext.Scenarios.Add(scenarioClone);
}
}
public VW_ProjectAccessByUserExtended GetProjectByUser(Guid userId, Guid projectId)
{
return DbContext.VW_ProjectAccessByUserExtended.FirstOrDefault(x => x.UserId == userId && x.Id == projectId);
}
private EventGetResponseModel MergeEventsWithWorkflowHistory(EventGetResponseModel events,
ResponseModel<EventGetResponseModel> wfEvents, int take, int skip, string orderBy, bool isOrderAsc)
{
var data = new List<EventGetResponse>();
if (events?.Data != null)
data.AddRange(events.Data);
if (wfEvents?.Result?.Data != null)
data.AddRange(wfEvents.Result.Data);
if (string.IsNullOrWhiteSpace(orderBy))
orderBy = string.Empty;
switch (orderBy.ToLower())
{
case "newvalue":
data = isOrderAsc ? data.OrderBy(t => t.NewValue).Skip(skip).Take(take).ToList() : data.OrderByDescending(t => t.NewValue).Skip(skip).Take(take).ToList();
break;
case "oldvalue":
data = isOrderAsc ? data.OrderBy(t => t.OldValue).Skip(skip).Take(take).ToList() : data.OrderByDescending(t => t.OldValue).Skip(skip).Take(take).ToList();
break;
default:
data = isOrderAsc ? data.OrderBy(t => t.DateCreated).Skip(skip).Take(take).ToList() : data.OrderByDescending(t => t.DateCreated).Skip(skip).Take(take).ToList();
break;
}
var total = events?.Total ?? 0;
if (wfEvents?.Result != null)
total += wfEvents.Result.Total;
return new EventGetResponseModel
{
Data = data,
Total = total
};
}
private class Project2PartModel
{
public Guid ProjectId { get; set; }
public Guid? PartId { get; set; }
public string TeamName { get; set; }
public Guid? TeamId { get; set; }
public int Rank { get; set; }
}
#region Queries
private IQueryable<ProjectWithChildrenView> GetProjectWithChildrenViewQuery(Guid userId)
{
var query = (from project in DbContext.VW_ProjectAccessByUserExtended
where project.UserId == userId
select new ProjectWithChildrenView()
{
Id = project.Id,
CompanyId = project.CompanyId,
CompanyName = project.CompanyName,
ClientId = project.ClientId,
ClientName = project.ClientName,
Name = project.Name,
TypeName = project.TypeName,
Deadline = project.Deadline,
Priority = project.Priority,
HasChildren = project.HasChildren,
Color = project.Color,
ReadOnly = (!project.Write.HasValue || project.Write.Value < 1),
StatusId = project.StatusId,
TypeId = project.TypeId,
Probability = project.Probability,
StatusName = project.StatusName,
PerformanceYellowThreshold = project.PerformanceYellowThreshold,
PerformanceRedThreshold = project.PerformanceRedThreshold,
ParentProjectId = project.ParentProjectId,
ParentProject = !project.ParentProjectId.HasValue ? null : new ProjectWithChildrenView.ParentProjectView()
{
Id = project.ParentProjectId.Value,
CompanyId = project.CompanyId,
Name = project.ParentProjectName,
Color = project.ParentProjectColor,
},
});
return query;
}
#endregion
}
public class ProjectPartManager : ManagerBase<Project, ProjectPartModel>
{
public ProjectPartManager(EnVisageEntities dbContext)
: base(dbContext)
{
}
protected override Project InitInstance()
{
return new Project { Id = Guid.NewGuid() };
}
protected override Project RetrieveReadOnlyById(Guid key)
{
return DataTable.AsNoTracking().FirstOrDefault(t => t.Id == key);
}
public override DbSet<Project> DataTable => DbContext.Projects;
public override Project Save(ProjectPartModel model)
{
var project = base.Save(model);
ProjectManager mngr = new ProjectManager(DbContext);
mngr.SaveContacts(project, model.InternalContacts, model.ExternalContacts);
model.AssignedTeams = mngr.UpdateProjectTeams(project, model.AssignedTeams, null, false, true);
mngr.SaveStrategicGoals(project, model.StrategicGoals);
(new WorkFlowManager(DbContext)).SaveContacts(model.WorkFlowContacts, null, project.Id, WorkFlowContactNotificationType.None);
// if it is new project part
if (model.Id == Guid.Empty)
{
#region Create Actuals scenario
// we need to create empty actual scenario for all new projects (just created or copied)
var scenario = new Scenario
{
Id = Guid.NewGuid(),
Name = "ACTUALS",
ParentId = project.Id,
Type = ScenarioType.Actuals.GetHashCode(),
StartDate = DateTime.UtcNow.Date,
Color = "",
ProjectedRevenue = 0
};
DbContext.Scenarios.Add(scenario);
#endregion
}
return project;
}
public Dictionary<Guid, ProjectPartModel> GetItemsAsModelWithChildren(Guid parentProjectId,
ProjectManager.LoadProjectItems includedItems = ProjectManager.LoadProjectItems.None)
{
if (parentProjectId.Equals(Guid.Empty))
throw new ArgumentNullException(nameof(parentProjectId));
var partsAsRaw = DbContext.Projects.AsNoTracking().Where(t => t.ParentProjectId.HasValue && t.ParentProjectId.Value == parentProjectId).ToList();
var partModels = partsAsRaw.ToDictionary(k => k.Id, v => (ProjectPartModel)v);
EnVisageEntities tagsContext = null;
try
{
var tasksToWait = new List<Task>();
if (includedItems.HasFlag(ProjectManager.LoadProjectItems.PortfolioLabels) ||
includedItems.HasFlag(ProjectManager.LoadProjectItems.Tags))
{
// Loading project tags and labels
tagsContext = new EnVisageEntities();
var tagsTask = tagsContext.TagLinks.AsNoTracking().Where(x => partModels.Keys.Contains(x.ParentID))
.GroupBy(x => x.ParentID).ToDictionaryAsync(k => k.Key, v => v.ToList());
tasksToWait.Add(tagsTask);
}
Task.WaitAll(tasksToWait.ToArray());
if (includedItems.HasFlag(ProjectManager.LoadProjectItems.PortfolioLabels) ||
includedItems.HasFlag(ProjectManager.LoadProjectItems.Tags))
{
// Find appropriate task in the awaitable list
var tagsLoadingTask = tasksToWait.FirstOrDefault(x =>
x is Task<Dictionary<Guid, List<TagLink>>>) as Task<Dictionary<Guid, List<TagLink>>>;
if (tagsLoadingTask != null)
{
var loadedLabelsAndTags = tagsLoadingTask.Result;
if (includedItems.HasFlag(ProjectManager.LoadProjectItems.PortfolioLabels))
{
// Get Project Portfolio Tags / Labels
foreach (var partId in partModels.Keys)
{
partModels[partId].PortfolioTags = loadedLabelsAndTags.ContainsKey(partId) ?
loadedLabelsAndTags[partId].Where(x => x.TagLinkType == (int)TagType.Portfolio).Select(x => x.TagID).ToList() :
new List<Guid>();
}
}
if (includedItems.HasFlag(ProjectManager.LoadProjectItems.Tags))
{
// Get Project Tags
foreach (var partId in partModels.Keys)
{
partModels[partId].ProjectTags = loadedLabelsAndTags.ContainsKey(partId) ?
loadedLabelsAndTags[partId].Where(x => x.TagLinkType == (int)TagType.Project).Select(x => x.TagID).ToList() :
new List<Guid>();
}
}
}
}
}
finally
{
if (tagsContext != null)
tagsContext.Dispose();
}
return partModels;
}
}
public class ProjectDependencyManager : ManagerBase<ProjectDependency, ProjectDependencyModel>
{
protected ScenarioManager ScenarioManager { get; set; }
protected ProjectManager ProjectManager { get; set; }
protected TeamManager TeamManager { get; set; }
public ProjectDependencyManager(EnVisageEntities dbContext) : base(dbContext)
{
ProjectManager = new ProjectManager(DbContext);
ScenarioManager = new ScenarioManager(DbContext);
TeamManager = new TeamManager(DbContext);
}
public ProjectDependencyManager(EnVisageEntities dbContext, ProjectManager projectManager, ScenarioManager scenarioManager, TeamManager teamManager) : base(dbContext)
{
ProjectManager = projectManager;
ScenarioManager = scenarioManager;
TeamManager = teamManager;
}
protected override ProjectDependency InitInstance()
{
return new ProjectDependency { Id = Guid.NewGuid() };
}
protected override ProjectDependency RetrieveReadOnlyById(Guid key)
{
return DataTable.AsNoTracking().FirstOrDefault(t => t.Id == key);
}
public override DbSet<ProjectDependency> DataTable => DbContext.ProjectDependencies;
public override ProjectDependency Save(ProjectDependencyModel model)
{
var oldObj = Load(model.Id);
var obj = base.Save(model);
// we need to update project timestamp of affected projects
var projectmanager = new ProjectManager(DbContext);
var sourceProjectModel = (ProjectModel)projectmanager.Load(obj.SourceProjectId);
if (sourceProjectModel != null)
projectmanager.Save(sourceProjectModel);
var targetProjectModel = (ProjectModel)projectmanager.Load(obj.TargetProjectId);
if (targetProjectModel != null)
projectmanager.Save(targetProjectModel);
#region Log Event
var isAdd = oldObj.Id == Guid.Empty;
var dependencyStrModelOld = new LogProjectDependencyStrModel();
var dependencyStrModelNew = new LogProjectDependencyStrModel();
if (!isAdd)
{
dependencyStrModelOld = GetLogProjectDependencyStr(oldObj, obj.Project1.Id, sourceProjectModel.Name, targetProjectModel.Name);
}
dependencyStrModelNew = GetLogProjectDependencyStr(obj, obj.Project.Id, sourceProjectModel.Name, targetProjectModel.Name);
var transactionId = Utils.GetOrCreateTransactionId(DbContext, null);
var userId = SecurityManager.GetUserPrincipal().ToString();
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = obj.Id,
ProjectId = obj.SourceProjectId,
ClassificationKey = isAdd ? "ProjectDependencyAdd" : "ProjectDependencyEdit",
OldValue = dependencyStrModelOld.ValueSourceStr,
NewValue = dependencyStrModelNew.ValueSourceStr,
UserId = userId
});
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = obj.Id,
ProjectId = obj.TargetProjectId,
ClassificationKey = isAdd ? "ProjectDependencyAdd" : "ProjectDependencyEdit",
OldValue = dependencyStrModelOld.ValueTargetStr,
NewValue = dependencyStrModelNew.ValueTargetStr,
UserId = userId
});
Task.Run(() => AuditProxy.CommitEventChanges(transactionId));
#endregion
if (IsContextLocal)
DbContext.SaveChanges();
return obj;
}
public void Delete(Guid id, string userId = null)
{
var obj = DataTable.FirstOrDefault(t => t.Id == id);
if (obj != null)
{
// we need to update project timestamp of affected projects
var projectmanager = new ProjectManager(DbContext);
var sourceProjectModel = (ProjectModel)projectmanager.Load(obj.SourceProjectId);
if (sourceProjectModel != null)
projectmanager.Save(sourceProjectModel);
var targetProjectModel = (ProjectModel)projectmanager.Load(obj.TargetProjectId);
if (targetProjectModel != null)
projectmanager.Save(targetProjectModel);
var projectId = obj.Project.Id;
DataTable.Remove(obj);
#region Log Event
var dependencyStrModel = GetLogProjectDependencyStr(obj, projectId, sourceProjectModel.Name, targetProjectModel.Name);
var transactionId = Utils.GetOrCreateTransactionId(DbContext, null);
if (string.IsNullOrEmpty(userId))
userId = SecurityManager.GetUserPrincipal().ToString();
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = obj.Id,
ProjectId = obj.SourceProjectId,
ClassificationKey = "ProjectDependencyDelete",
OldValue = dependencyStrModel.ValueSourceStr,
UserId = userId
});
LogEvent(transactionId, new EventRequestBaseModel
{
EntityId = obj.Id,
ProjectId = obj.TargetProjectId,
ClassificationKey = "ProjectDependencyDelete",
OldValue = dependencyStrModel.ValueTargetStr,
UserId = userId
});
Task.Run(() => AuditProxy.CommitEventChanges(transactionId));
#endregion
}
if (IsContextLocal)
DbContext.SaveChanges();
}
private LogProjectDependencyStrModel GetLogProjectDependencyStr(ProjectDependency obj, Guid projectId, string sourceProjectName, string targetProjectName)
{
var oldValueTargetStr = string.Empty;
var oldValueSourceStr = string.Empty;
if (obj.Type == (int)ProjectDependencyDisplayType.Link)
{
oldValueTargetStr = ProjectDependencyDisplayType.Link.ToDisplayValue();
oldValueSourceStr = ProjectDependencyDisplayType.Link.ToDisplayValue();
}
else
{
if (projectId == obj.SourceProjectId)
{
oldValueTargetStr = ProjectDependencyDisplayType.StartsAfter.ToDisplayValue();
oldValueSourceStr = ProjectDependencyDisplayType.StartsBefore.ToDisplayValue();
}
else
{
oldValueTargetStr = ProjectDependencyDisplayType.StartsBefore.ToDisplayValue();
oldValueSourceStr = ProjectDependencyDisplayType.StartsAfter.ToDisplayValue();
}
}
oldValueTargetStr = oldValueTargetStr + " " + sourceProjectName;
oldValueSourceStr = oldValueSourceStr + " " + targetProjectName;
return new LogProjectDependencyStrModel
{
ValueTargetStr = oldValueTargetStr,
ValueSourceStr = oldValueSourceStr
};
}
private class LogProjectDependencyStrModel
{
public string ValueTargetStr { get; set; }
public string ValueSourceStr { get; set; }
}
public IEnumerable<DependencyProjectItem> GetAvailableProjects(Guid projectId, Guid sessionKey, Guid userId)
{
var chain = LoadChain(new List<Guid> { projectId });
var excludeProjects = chain.Values.Select(t => t.SourceProjectId).Union(chain.Values.Select(x => x.TargetProjectId)).Union(new List<Guid> { projectId }).Distinct();
var projects = GetDependencyProjects(userId, excludeProjects);
return projects;
}
private static IEnumerable<DependencyProjectItem> GetDependencyProjects(Guid userId, IEnumerable<Guid> excludeProjects)
{
using (var dbContext = new EnVisageEntities())
{
var projectmanager = new ProjectManager(dbContext);
var projects = projectmanager.GetProjectsWithChildren(userId, ProjectManager.LoadProjectItems.Scenarios)
.Where(t => !excludeProjects.Contains(t.Id))
.Select(t => new DependencyProjectItem
{
ProjectId = t.Id,
ProjectName = t.ParentProject != null ? t.Name + ": " + t.ParentProject.Name : t.Name,
ScenarioStartDate = t.Scenarios.Any(sc => sc.Status == ScenarioStatus.Active && sc.Type == ScenarioType.Portfolio) ?
Utils.ConvertToUnixDate(t.Scenarios.FirstOrDefault(sc => sc.Status == ScenarioStatus.Active && sc.Type == ScenarioType.Portfolio).StartDate.Value)
: (long?)null,
ScenarioEndDate = t.Scenarios.Any(sc => sc.Status == ScenarioStatus.Active && sc.Type == ScenarioType.Portfolio) ?
Utils.ConvertToUnixDate(t.Scenarios.FirstOrDefault(sc => sc.Status == ScenarioStatus.Active && sc.Type == ScenarioType.Portfolio).EndDate.Value)
: (long?)null
});
return projects;
}
}
public DependencyChain<DependencyChainItem> LoadChain(IEnumerable<Guid> projectIds)
{
var result = new DependencyChain<DependencyChainItem>();
if (projectIds == null || !projectIds.Any())
return result;
result = LoadProjectDependencies(projectIds);
return result;
}
public DependencyChain<DependencyChainItem> LoadProjectDependencies(IEnumerable<Guid> projectIds)
{
var visitedProjects = new List<Guid>().Union(projectIds).ToList();
var ancestors = LoadDependenciesRecursively(projectIds, 0, true, visitedProjects);
var descendants = LoadDependenciesRecursively(projectIds, 0, false, visitedProjects);
var result = ancestors;
descendants.Keys.ToList().ForEach(k =>
{
if (!result.ContainsKey(k))
{
result.Add(k, descendants[k]);
}
});
return result;
}
public DependencyChain<DependencyChainItem> LoadDependenciesRecursively(IEnumerable<Guid> projectIds, int level, bool getAncestors, List<Guid> visited)
{
var query1 = DbContext.VW_ProjectDependencies.AsQueryable();
var query2 = DbContext.VW_ProjectDependencies.AsQueryable();
IEnumerable<DependencyChainItem> extractedDependencies;
if (getAncestors)
{
var commonQuery = query1.Where(t => projectIds.Contains(t.TargetProjectId) && !visited.Contains(t.SourceProjectId))
.Select(x => new
{
Item = x,
IsReversed = false
});
var reversedLinksQuery = query2.Where(t => (projectIds.Contains(t.SourceProjectId) &&
(t.Type == (short)ProjectDependencyDisplayType.Link) && !visited.Contains(t.TargetProjectId)))
.Select(x => new
{
Item = x,
IsReversed = true
});
extractedDependencies = commonQuery.Union(reversedLinksQuery).ToList().Select(x =>
DependencyChainItem.CreateFrom(x.Item, level, x.IsReversed));
}
else
{
var commonQuery = query1.Where(t => projectIds.Contains(t.SourceProjectId) && !visited.Contains(t.TargetProjectId))
.Select(x => new
{
Item = x,
IsReversed = false
});
var reversedLinksQuery = query2.Where(t => (projectIds.Contains(t.TargetProjectId) &&
(t.Type == (short)ProjectDependencyDisplayType.Link) && !visited.Contains(t.SourceProjectId)))
.Select(x => new
{
Item = x,
IsReversed = true
});
extractedDependencies = commonQuery.Union(reversedLinksQuery).ToList().Select(x =>
DependencyChainItem.CreateFrom(x.Item, level, x.IsReversed));
}
var result = extractedDependencies.GroupBy(t => t.Id).ToDictionary(key => key.Key, el => el.FirstOrDefault());
if (result.Any())
{
var itemsToGoFrom =
result.Values.Where(x => (getAncestors && !x.IsReversed) || (!getAncestors && x.IsReversed)).Select(x => x.SourceProjectId).Union(
result.Values.Where(x => (getAncestors && x.IsReversed) || (!getAncestors && !x.IsReversed)).Select(x => x.TargetProjectId));
visited.AddRange(itemsToGoFrom);
var nextLevel = getAncestors ? level - 1 : level + 1;
var nextLevelResult = LoadDependenciesRecursively(itemsToGoFrom, nextLevel, getAncestors, visited);
foreach (var item in nextLevelResult)
{
if (!result.ContainsKey(item.Key))
result.Add(item.Key, item.Value);
}
}
return DependencyChain<DependencyChainItem>.CreateFrom(result);
}
public List<ProjectDependency> LoadDependencies(IEnumerable<Guid> ids, bool trackChanges = false)
{
if (ids == null || !ids.Any())
return new List<ProjectDependency>();
var query = DbContext.ProjectDependencies.AsQueryable();
if (!trackChanges)
query = query.AsNoTracking();
return query.Where(t => ids.Contains(t.Id)).ToList();
}
/// <summary>
/// Loads dependencies for the specified project Id from DB and convert them into <see cref="ProjectDependencyListModel"/> object.
/// </summary>
/// <param name="id">Single project or project part Id.</param>
/// <returns>An instance of <see cref="ProjectDependencyListModel"/> class with info about all (explicit and implicit)
/// dependencies of the project with the specified Id.</returns>
public ProjectDependencyListModel GetDependencies(Guid id, Guid? userId = null)
{
return GetDependencies(id, null, userId);
//var dbRecords = LoadProjectDependencies(new List<Guid> { id });
//return BuildDependencyListModel(id, dbRecords.Values, userId);
}
public ProjectDependencyListModel GetDependencies(Guid id, List<MixScenarioDates> MongoScenarioData, Guid? userId = null)
{
//var dbRecords = LoadProjectDependencies(new List<Guid> { id });
var dbRecords = LoadProjectDependencies(new List<Guid> { id });
return BuildDependencyListModel(id, dbRecords.Values, MongoScenarioData, userId);
}
/// <summary>
/// Converts the specified collection of <see cref="DependencyChain.DependencyChainItem"/> items into <see cref="ProjectDependencyListModel"/> model
/// and loads requried project and scenario info from database.
/// </summary>
/// <param name="id">Single project or project part Id.</param>
/// <returns>An instance of <see cref="ProjectDependencyListModel"/> class with info about all (explicit and implicit)
/// dependencies of the project with the specified Id.</returns>
public ProjectDependencyListModel BuildDependencyListModel(Guid id, IEnumerable<DependencyChainItem> data, Guid? userId = null)
{
return BuildDependencyListModel(id, data, null, userId);
}
public ProjectDependencyListModel BuildDependencyListModel(Guid id, IEnumerable<DependencyChainItem> data, List<MixScenarioDates> MongoScenarioData, Guid? userId = null)
{
//Logger.Debug("BuildDependencyListModel started");
var dict = new Dictionary<ProjectDependencyDisplayType, ProjectDependencyDisplayModel>();
// gather all project ids i n chain in both directions
var allProjectIds = data.Select(t => t.SourceProjectId).Union(data.Select(t => t.TargetProjectId));
// load all referenced projects
var projects = ProjectManager.LoadProjects(allProjectIds).GroupBy(t => t.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
//Logger.Debug("BuildDependencyListModel projects loaded");
// load all active scenarios for previously loaded projects
var scenarios = ScenarioManager.GetScenarios4Projects(allProjectIds.ToList(), null, ScenarioStatus.Active, false, true).
GroupBy(t => t.ParentId).ToDictionary(t => t.Key, el => el.FirstOrDefault());
if (MongoScenarioData != null)
{
if (MongoScenarioData.Count > 0)
{
foreach (var s in MongoScenarioData)
{
if (scenarios.ContainsKey(s.ProjectId) && s.EndDate.HasValue && s.StartDate.HasValue)
{
scenarios[s.ProjectId].StartDate = Utils.ConvertFromUnixDate(s.StartDate.Value);
scenarios[s.ProjectId].EndDate = Utils.ConvertFromUnixDate(s.EndDate.Value);
}
}
}
}
//Logger.Debug("BuildDependencyListModel scenarios loaded");
var dataOrdered = data.OrderBy(x => Math.Abs(x.Level));
foreach (var item in dataOrdered)
{
ProjectDependencyDisplayType virtualType = (ProjectDependencyDisplayType)item.Type;
// calculate type to display
if (item.Level == 0) // if this is direct dependency of the project with Id equals to id argument
{
// if other task depends on current task then
// replace type with virtual because we need to take a look at dependency from opposite side (target project)
if (ProjectDependencyDisplayType.StartsBefore.Equals(virtualType) && item.TargetProjectId == id)
virtualType = ProjectDependencyDisplayType.StartsAfter;
}
else if (item.Level > 0 && ProjectDependencyDisplayType.StartsAfter.Equals(virtualType)) // if this is a descendant dependency item
{
virtualType = ProjectDependencyDisplayType.StartsBefore;
}
else if (item.Level < 0 && ProjectDependencyDisplayType.StartsBefore.Equals(virtualType))
{
virtualType = ProjectDependencyDisplayType.StartsAfter;
}
// create new item of the specified type or reuse one of already created
ProjectDependencyDisplayModel listItem = null;
if (dict.ContainsKey(virtualType))
listItem = dict[virtualType];
else
{
listItem = new ProjectDependencyDisplayModel()
{
Type = virtualType
};
dict.Add(virtualType, listItem);
}
// get referenced project
ProjectListModel itemProject = null;
Scenario itemScenario = null;
Guid displayedProjectId = Guid.Empty;
if (ProjectDependencyDisplayType.StartsBefore.Equals(virtualType)) // if current task depends on other task
{
itemProject = projects.ContainsKey(item.TargetProjectId) ? projects[item.TargetProjectId] : null;
displayedProjectId = item.TargetProjectId;
}
else if (ProjectDependencyDisplayType.StartsAfter.Equals(virtualType)) // if other task depends on current task
{
itemProject = projects.ContainsKey(item.SourceProjectId) ? projects[item.SourceProjectId] : null;
displayedProjectId = item.SourceProjectId;
}
else if (ProjectDependencyDisplayType.Link.Equals(virtualType))
{
if (item.Level == 0)
{
if (item.SourceProjectId == id)
{
if (projects.ContainsKey(item.TargetProjectId))
{
itemProject = projects[item.TargetProjectId];
displayedProjectId = item.TargetProjectId;
}
}
else
{
if (projects.ContainsKey(item.SourceProjectId))
{
itemProject = projects[item.SourceProjectId];
displayedProjectId = item.SourceProjectId;
}
}
}
else
{
if (((item.Level < 0) && !item.IsReversed) || ((item.Level > 0) && item.IsReversed))
{
itemProject = projects.ContainsKey(item.SourceProjectId) ? projects[item.SourceProjectId] : null;
displayedProjectId = item.SourceProjectId;
}
else
{
itemProject = projects.ContainsKey(item.TargetProjectId) ? projects[item.TargetProjectId] : null;
displayedProjectId = item.TargetProjectId;
}
}
}
// set start/end date properties for the specified project and push it to projects property
if (itemProject != null)
{
if (scenarios.ContainsKey(itemProject.Id))
{
itemScenario = scenarios[itemProject.Id];
if (!listItem.dtStartDate.HasValue || Utils.ConvertFromUnixDate(listItem.dtStartDate.Value) > itemScenario.StartDate)
listItem.dtStartDate = Utils.ConvertToUnixDate(itemScenario.StartDate.Value);
if (!listItem.dtEndDate.HasValue || Utils.ConvertFromUnixDate(listItem.dtEndDate.Value) < itemScenario.EndDate)
listItem.dtEndDate = Utils.ConvertToUnixDate(itemScenario.EndDate.Value);
}
}
// fill Items collection with raw data, required for saving
bool performItemAdding = true;
var itemModel = new ProjectDependencyModel
{
Id = item.Id,
SourceProjectId = item.SourceProjectId,
TargetProjectId = item.TargetProjectId,
Level = item.Level,
IsReversed = item.IsReversed,
DisplayedProjectId = displayedProjectId,
Type = (short)virtualType,
Name = itemProject != null ? itemProject.ProjectName : string.Empty,
StartDate = itemScenario?.StartDate != null ? Utils.ConvertToUnixDate(itemScenario.StartDate.Value) : (long?)null,
EndDate = itemScenario?.EndDate != null ? Utils.ConvertToUnixDate(itemScenario.EndDate.Value) : (long?)null
};
// Apply filtering of items, we going to add to result collection.
// Use no filtering for 0 level of related items.
// Do it to avoid displaying twice the same project name, if we reached it by different ways.
if (listItem.Items.Count > 0 && itemModel.Level != 0)
{
// Looking for duplacated records in current items set
bool relationToProjectExists = listItem.Items.Any(x => x.Type == itemModel.Type && x.DisplayedProjectId.Equals(itemModel.DisplayedProjectId));
performItemAdding = !relationToProjectExists;
}
if (performItemAdding)
{
listItem.Items.Add(itemModel);
}
}
//Logger.Debug("BuildDependencyListModel model built");
return new ProjectDependencyListModel(dict.Values);
}
public bool ValidateProjects(DependencyResolveModel model, out DependencyResolveModel validationResult)
{
validationResult = model.Clone();
TimeSpan startDateOffset = TimeSpan.Zero;
TimeSpan endDateOffset = TimeSpan.Zero;
// calculate offsets
var scenarios = ScenarioManager.GetScenarios4Projects(new List<Guid> { model.ProjectId }, ScenarioType.Portfolio, ScenarioStatus.Active, false, true);
if (scenarios != null && scenarios.Any())
{
var scenario = scenarios.FirstOrDefault();
if (scenario?.StartDate != null && scenario.EndDate.HasValue)
{
if (model.StartDate.HasValue)
startDateOffset = model.StartDate.Value - scenario.StartDate.Value;
if (model.EndDate.HasValue)
endDateOffset = model.EndDate.Value - scenario.EndDate.Value;
}
}
// validate dependencies
var referencedProjects = LoadAllReferencedProjects(model.ProjectId, model.ResolvePlan, model.StartDate, startDateOffset, model.EndDate, endDateOffset);
validationResult.ResolvePlan = referencedProjects.Values.ToList();
return false;
}
private Dictionary<Guid, DependencyResolveModel.ProjectItem> InjectChanges(IEnumerable<DependencyResolveModel.ProjectItem> items,
Dictionary<Guid, DependencyResolveModel.ProjectItem> changedItemsDict, Dictionary<Guid, Tuple<DateTime, DateTime>> changedProjects,
TimeSpan? startDateOffset = null, TimeSpan? endDateOffset = null)
{
startDateOffset = startDateOffset ?? TimeSpan.Zero;
endDateOffset = endDateOffset ?? TimeSpan.Zero;
var result = new Dictionary<Guid, DependencyResolveModel.ProjectItem>();
foreach (var dbItem in items)
{
#region replace dates for other project if it has been modified earlier
if (dbItem.ProjectId == dbItem.SourceProjectId)
{
if (changedProjects.ContainsKey(dbItem.TargetProjectId) && dbItem.Level != 0)
{
if (dbItem.TargetStartDate.HasValue)
startDateOffset = changedProjects[dbItem.TargetProjectId].Item1 - dbItem.TargetStartDate.Value;
if (dbItem.TargetEndDate.HasValue)
endDateOffset = changedProjects[dbItem.TargetProjectId].Item2 - dbItem.TargetEndDate.Value;
dbItem.TargetStartDate = changedProjects[dbItem.TargetProjectId].Item1;
dbItem.TargetEndDate = changedProjects[dbItem.TargetProjectId].Item2;
}
}
else if (dbItem.ProjectId == dbItem.TargetProjectId)
{
if (changedProjects.ContainsKey(dbItem.SourceProjectId) && dbItem.Level != 0)
{
if (dbItem.SourceStartDate.HasValue)
startDateOffset = changedProjects[dbItem.SourceProjectId].Item1 - dbItem.SourceStartDate.Value;
if (dbItem.SourceEndDate.HasValue)
endDateOffset = changedProjects[dbItem.SourceProjectId].Item2 - dbItem.SourceEndDate.Value;
dbItem.SourceStartDate = changedProjects[dbItem.SourceProjectId].Item1;
dbItem.SourceEndDate = changedProjects[dbItem.SourceProjectId].Item2;
}
}
#endregion
#region apply changes entered by user on the form
if (changedItemsDict.ContainsKey(dbItem.Id))
{
var changedItem = changedItemsDict[dbItem.Id];
dbItem.MoveStartDate = changedItem.MoveStartDate;
dbItem.Action = changedItem.Action;
if (dbItem.Action.HasValue && !dbItem.AvailableMoveTypes.Contains(dbItem.Action.Value))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
#endregion
#region remove DontMove option if necessary
if (dbItem.SourceEndDate.HasValue && dbItem.TargetStartDate.HasValue &&
dbItem.SourceEndDate.Value >= dbItem.TargetStartDate.Value && // validation rule 1
dbItem.Type == (short)ProjectDependencyModel.ProjectDependencyType.StartsBefore)
{
// we should remove don't move option as it will lead to overlap of projects
if (dbItem.AvailableMoveTypes.Contains(DependencyResolveModel.ActionType.DontMove))
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.DontMove);
if (DependencyResolveModel.ActionType.DontMove.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
}
#endregion
#region remove movewith option if necessary
if (dbItem.AvailableMoveTypes.Contains(DependencyResolveModel.ActionType.MoveWith))
{
if (dbItem.ProjectId == dbItem.SourceProjectId)
{
if (TimeSpan.Zero.Equals(startDateOffset) || dbItem.SourceHasActuals)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.MoveWith);
if (DependencyResolveModel.ActionType.MoveWith.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
}
else if (dbItem.ProjectId == dbItem.TargetProjectId)
{
if (TimeSpan.Zero.Equals(endDateOffset) || dbItem.TargetHasActuals)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.MoveWith);
if (DependencyResolveModel.ActionType.MoveWith.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
}
}
#endregion
#region remove link option if necessary
if (dbItem.AvailableMoveTypes.Contains(DependencyResolveModel.ActionType.ConvertToLink))
{
if (dbItem.IsNew)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.ConvertToLink);
if (DependencyResolveModel.ActionType.ConvertToLink.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
else if (dbItem.IsDeleted)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.ConvertToLink);
if (DependencyResolveModel.ActionType.ConvertToLink.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
}
#endregion
#region remove RemoveDependency option if necessary
if (dbItem.AvailableMoveTypes.Contains(DependencyResolveModel.ActionType.RemoveDependency))
{
if (dbItem.IsNew)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.RemoveDependency);
if (DependencyResolveModel.ActionType.RemoveDependency.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
else if (dbItem.IsDeleted)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.RemoveDependency);
if (DependencyResolveModel.ActionType.RemoveDependency.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
}
#endregion
#region Set MoveDate to current scenario start date if project is not going to be moved
if (DependencyResolveModel.ActionType.DontMove.Equals(dbItem.Action) ||
(!dbItem.Action.HasValue && dbItem.AvailableMoveTypes.FirstOrDefault() == DependencyResolveModel.ActionType.DontMove))
{
if (dbItem.ProjectId == dbItem.TargetProjectId)
dbItem.MoveStartDate = dbItem.TargetStartDate;
else
dbItem.MoveStartDate = dbItem.SourceStartDate;
}
else if (DependencyResolveModel.ActionType.ConvertToLink.Equals(dbItem.Action) ||
(!dbItem.Action.HasValue && dbItem.AvailableMoveTypes.FirstOrDefault() == DependencyResolveModel.ActionType.ConvertToLink))
{
if (dbItem.ProjectId == dbItem.TargetProjectId)
dbItem.MoveStartDate = dbItem.TargetStartDate;
else
dbItem.MoveStartDate = dbItem.SourceStartDate;
}
else if (DependencyResolveModel.ActionType.RemoveDependency.Equals(dbItem.Action) ||
(!dbItem.Action.HasValue && dbItem.AvailableMoveTypes.FirstOrDefault() == DependencyResolveModel.ActionType.RemoveDependency))
{
if (dbItem.ProjectId == dbItem.TargetProjectId)
dbItem.MoveStartDate = dbItem.TargetStartDate;
else
dbItem.MoveStartDate = dbItem.SourceStartDate;
}
#endregion
#region Apply MoveWith option and validate that new dates are acceptable
if (dbItem.Action == DependencyResolveModel.ActionType.MoveWith ||
(!dbItem.Action.HasValue && dbItem.AvailableMoveTypes.FirstOrDefault() == DependencyResolveModel.ActionType.MoveWith))
{
if (dbItem.ProjectId == dbItem.SourceProjectId)
{
var removeAction = false;
if (dbItem.SourceHasActuals)
removeAction = true;
else
{
// move source project to the same offset as changed project
if (dbItem.SourceStartDate.HasValue)
{
dbItem.MoveStartDate = dbItem.SourceStartDate.Value.Add(startDateOffset.Value);
// if source project ends after target starts even after move then
// we need to move it additionally so it ends before target starts
if (ProjectDependencyModel.ProjectDependencyType.StartsBefore.Equals(dbItem.Type) &&
dbItem.MoveEndDate.HasValue && dbItem.TargetStartDate.HasValue && dbItem.MoveEndDate >= dbItem.TargetStartDate)
{
var newOffset = dbItem.TargetStartDate.Value.AddDays(-1) - dbItem.MoveEndDate.Value;
dbItem.MoveStartDate = dbItem.MoveStartDate.Value.Add(newOffset);
}
}
// if projects intersect even after move then we should remove MoveWith option and reset move date
if (dbItem.MoveEndDate.HasValue && dbItem.TargetStartDate.HasValue && dbItem.MoveStartDate.HasValue &&
((ProjectDependencyModel.ProjectDependencyType.StartsBefore.Equals(dbItem.Type) && dbItem.MoveEndDate >= dbItem.TargetStartDate)
|| dbItem.MoveStartDate.Value < DateTime.UtcNow.Date))
removeAction = true;
}
// if projects intersect even after move then we should remove MoveWith option and reset move date
if (removeAction)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.MoveWith);
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
dbItem.MoveStartDate = dbItem.SourceStartDate;
}
}
else if (dbItem.ProjectId == dbItem.TargetProjectId)
{
var removeAction = false;
if (dbItem.TargetHasActuals)
removeAction = true;
else
{
if (dbItem.TargetStartDate.HasValue)
{
// move target project to the same offset as changed project
dbItem.MoveStartDate = dbItem.TargetStartDate.Value.Add(endDateOffset.Value);
// if target project starts before source ends even after move then
// we need to move it additionally so it starts after target ends
if (ProjectDependencyModel.ProjectDependencyType.StartsBefore.Equals(dbItem.Type) &&
dbItem.MoveStartDate.HasValue && dbItem.SourceEndDate.HasValue && dbItem.MoveStartDate <= dbItem.SourceEndDate)
{
var newOffset = dbItem.SourceEndDate.Value.AddDays(1) - dbItem.MoveStartDate.Value;
dbItem.MoveStartDate = dbItem.MoveStartDate.Value.Add(newOffset);
}
}
// if projects intersect even after move then we should remove MoveWith option and reset move date
if (dbItem.MoveStartDate.HasValue && dbItem.SourceEndDate.HasValue &&
((ProjectDependencyModel.ProjectDependencyType.StartsBefore.Equals(dbItem.Type) && dbItem.MoveStartDate <= dbItem.SourceEndDate)
|| dbItem.MoveStartDate.Value < DateTime.UtcNow.Date))
removeAction = true;
}
if (removeAction)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.MoveWith);
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
dbItem.MoveStartDate = dbItem.TargetStartDate;
}
}
}
#endregion
#region set move start date as original start date if other value was not set
if (dbItem.ProjectId == dbItem.SourceProjectId)
{
dbItem.MoveStartDate = dbItem.MoveStartDate ?? dbItem.SourceStartDate;
}
else if (dbItem.ProjectId == dbItem.TargetProjectId)
{
dbItem.MoveStartDate = dbItem.MoveStartDate ?? dbItem.TargetStartDate;
}
#endregion
#region Determine min and max available dates
dbItem.MinAvailableScenarioStartDate = Constants.FISCAL_CALENDAR_MIN_DATE > DateTime.UtcNow.Date ? Constants.FISCAL_CALENDAR_MIN_DATE : DateTime.UtcNow.Date;
dbItem.MaxAvailableScenarioStartDate = dbItem.MinAvailableScenarioStartDate;
if (dbItem.ProjectId == dbItem.SourceProjectId)
{
if (dbItem.SourceStartDate.HasValue && dbItem.SourceEndDate.HasValue)
{
var sourceDuration = dbItem.SourceEndDate.Value - dbItem.SourceStartDate.Value;
var sourceMaxEndDate = (dbItem.SourceDeadline.HasValue && dbItem.SourceDeadline.Value < Constants.FISCAL_CALENDAR_MAX_DATE) ? dbItem.SourceDeadline.Value : Constants.FISCAL_CALENDAR_MAX_DATE;
if (dbItem.TargetStartDate.HasValue &&
dbItem.TargetStartDate.Value.AddDays(-1) < sourceMaxEndDate)
{
sourceMaxEndDate = dbItem.TargetStartDate.Value.AddDays(-1);
}
dbItem.MaxAvailableScenarioStartDate = sourceMaxEndDate.Subtract(sourceDuration);
}
}
else if (dbItem.ProjectId == dbItem.TargetProjectId)
{
if (dbItem.TargetStartDate.HasValue && dbItem.TargetEndDate.HasValue)
{
var targetDuration = dbItem.TargetEndDate.Value - dbItem.TargetStartDate.Value;
var targetMaxEndDate = (dbItem.TargetDeadline.HasValue && dbItem.TargetDeadline.Value < Constants.FISCAL_CALENDAR_MAX_DATE) ? dbItem.TargetDeadline.Value : Constants.FISCAL_CALENDAR_MAX_DATE;
dbItem.MaxAvailableScenarioStartDate = targetMaxEndDate.Subtract(targetDuration);
if (dbItem.SourceEndDate.HasValue &&
dbItem.SourceEndDate.Value.AddDays(1) > dbItem.MinAvailableScenarioStartDate)
{
dbItem.MinAvailableScenarioStartDate = dbItem.SourceEndDate.Value.AddDays(1);
}
}
}
#endregion
#region remove Custom option if there are no available move dates
if (dbItem.AvailableMoveTypes.Contains(DependencyResolveModel.ActionType.CustomMove))
{
var availableRangeDuration = dbItem.MaxAvailableScenarioStartDate.Subtract(dbItem.MinAvailableScenarioStartDate);
var isRemove = availableRangeDuration.TotalDays <= 0;
if (!isRemove)
{
if (dbItem.ProjectId == dbItem.SourceProjectId && dbItem.SourceHasActuals)
isRemove = true;
else if (dbItem.ProjectId == dbItem.TargetProjectId && dbItem.TargetHasActuals)
isRemove = true;
}
if (isRemove)
{
dbItem.AvailableMoveTypes.Remove(DependencyResolveModel.ActionType.CustomMove);
if (DependencyResolveModel.ActionType.CustomMove.Equals(dbItem.Action))
dbItem.Action = dbItem.AvailableMoveTypes.Any() ? dbItem.AvailableMoveTypes.First() : (DependencyResolveModel.ActionType?)null;
}
else
{
if (dbItem.MoveStartDate.HasValue &&
(dbItem.MoveStartDate.Value < dbItem.MinAvailableScenarioStartDate ||
dbItem.MoveStartDate.Value > dbItem.MaxAvailableScenarioStartDate))
{
dbItem.MoveStartDate = dbItem.MinAvailableScenarioStartDate;
}
}
}
#endregion
// add project to disctionary if it's dates were changed
if (dbItem.MoveStartDate.HasValue && dbItem.MoveEndDate.HasValue
&& dbItem.MoveStartDate != (dbItem.ProjectId == dbItem.SourceProjectId ? dbItem.SourceStartDate : dbItem.TargetStartDate)
&& !changedProjects.ContainsKey(dbItem.ProjectId))
changedProjects.Add(dbItem.ProjectId, new Tuple<DateTime, DateTime>(dbItem.MoveStartDate.Value, dbItem.MoveEndDate.Value));
result.Add(dbItem.Id, dbItem);
}
return result;
}
public DependencyChain<DependencyResolveModel.ProjectItem> LoadAllReferencedProjects(Guid projectId,
IEnumerable<DependencyResolveModel.ProjectItem> changedItems, DateTime? newStartDate, TimeSpan startDateOffset,
DateTime? newEndDate, TimeSpan endDateOffset)
{
// gather direct relations
var dbItems = DbContext.VW_ProjectDependencies.Where(t => projectId == t.SourceProjectId || projectId == t.TargetProjectId)
.AsEnumerable().Select(t => (DependencyResolveModel.ProjectItem)t).GroupBy(t => t.Id).ToDictionary(key => key.Key, el => el.FirstOrDefault());
var changedProjects = new Dictionary<Guid, Tuple<DateTime, DateTime>>();
if (DateTime.MinValue.Equals(newStartDate))
{
newStartDate = null;
startDateOffset = TimeSpan.Zero;
}
if (DateTime.MinValue.Equals(newEndDate))
{
newEndDate = null;
endDateOffset = TimeSpan.Zero;
}
dbItems = RemoveDeletedItems(dbItems, changedItems.Where(t => t.IsDeleted));
dbItems = PrependPendingItems(dbItems, changedItems.Where(t => t.IsNew));
// inject changes from client model to direct relations
foreach (var item in dbItems.Values)
{
if (projectId == item.SourceProjectId)
{
// replace dates of changed scenario/project
item.SourceStartDate = item.SourceStartDate?.Add(startDateOffset) ?? newStartDate;
item.SourceEndDate = item.SourceEndDate?.Add(endDateOffset) ?? newEndDate;
if (!changedProjects.ContainsKey(projectId) && item.SourceStartDate.HasValue && item.SourceEndDate.HasValue)
changedProjects.Add(projectId, new Tuple<DateTime, DateTime>(item.SourceStartDate.Value, item.SourceEndDate.Value));
// fill source/target properties of the item
item.FillDescendant(endDateOffset);
}
if (projectId == item.TargetProjectId)
{
// replace dates of changed scenario/project
item.TargetStartDate = item.TargetStartDate?.Add(startDateOffset) ?? newStartDate;
item.TargetEndDate = item.TargetEndDate?.Add(endDateOffset) ?? newEndDate;
if (!changedProjects.ContainsKey(projectId) && item.TargetStartDate.HasValue && item.TargetEndDate.HasValue)
changedProjects.Add(projectId, new Tuple<DateTime, DateTime>(item.TargetStartDate.Value, item.TargetEndDate.Value));
// fill source/target properties of the item
item.FillAncestor(startDateOffset);
}
}
var changedItemsDict = changedItems.GroupBy(t => t.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
var result = InjectChanges(dbItems.Values, changedItemsDict, changedProjects, startDateOffset, endDateOffset);
if (result.Any())
{
var projectsToLookAncestorsFor =
result.Values.Where(t => t.Action != DependencyResolveModel.ActionType.RemoveDependency).Select(t => t.SourceProjectId).Distinct();
var projectsToLookDescendantsOf =
result.Values.Where(t => t.Action != DependencyResolveModel.ActionType.RemoveDependency).Select(t => t.TargetProjectId).Distinct();
var visitedProjects = projectsToLookAncestorsFor.Union(projectsToLookDescendantsOf)
.Union(new List<Guid>() { projectId }).Distinct().ToList();
var ancestors = LoadResolveDependenciesRecursively(projectsToLookAncestorsFor, -1, true, changedItemsDict, startDateOffset, endDateOffset, changedProjects, visitedProjects);
var descendants = LoadResolveDependenciesRecursively(projectsToLookDescendantsOf, 1, false, changedItemsDict, startDateOffset, endDateOffset, changedProjects, visitedProjects);
foreach (var item in ancestors)
if (!result.ContainsKey(item.Key))
{
result.Add(item.Key, item.Value);
}
foreach (var item in descendants)
if (!result.ContainsKey(item.Key))
{
result.Add(item.Key, item.Value);
}
}
return DependencyChain<DependencyResolveModel.ProjectItem>.CreateFrom(result);
}
private Dictionary<Guid, DependencyResolveModel.ProjectItem> RemoveDeletedItems(Dictionary<Guid, DependencyResolveModel.ProjectItem> dbItems, IEnumerable<DependencyResolveModel.ProjectItem> deletedItems)
{
var result = new Dictionary<Guid, DependencyResolveModel.ProjectItem>();
foreach (var item in dbItems)
{
if (deletedItems.Any(del => (del.SourceProjectId == item.Value.SourceProjectId && del.TargetProjectId == item.Value.TargetProjectId) || // exact match
(del.TargetProjectId == item.Value.SourceProjectId && del.SourceProjectId == item.Value.TargetProjectId))) // or backward match
continue;
if (!result.ContainsKey(item.Key))
result.Add(item.Key, item.Value);
}
return result;
}
/// <summary>
/// Loads information about projects and scenarios from <paramref name="newItems"/> collection and then append them to <paramref name="dbItems"/> collection.
/// </summary>
/// <param name="dbItems">A collection of direct dependencies.</param>
/// <param name="newItems">A collection of items system is going to create but not saved to DB yet.</param>
private Dictionary<Guid, DependencyResolveModel.ProjectItem> PrependPendingItems(Dictionary<Guid, DependencyResolveModel.ProjectItem> dbItems, IEnumerable<DependencyResolveModel.ProjectItem> newItems)
{
var projectIds = newItems.SelectMany(t => new Guid[] { t.SourceProjectId, t.TargetProjectId }).Distinct();
var projects = ProjectManager.LoadProjects(projectIds)
.GroupBy(t => t.Id).ToDictionary(t => t.Key, el => el.FirstOrDefault());
var scenarios = ScenarioManager.GetScenarios4Projects(projectIds.ToList(), ScenarioType.Portfolio, ScenarioStatus.Active, false, true)
.GroupBy(k => k.ParentId).ToDictionary(k => k.Key, el => el.FirstOrDefault());
var result = new Dictionary<Guid, DependencyResolveModel.ProjectItem>();
foreach (var item in newItems)
{
// if result collection already contains an item with the same project.Id tuple then skip this item
if (result.ContainsKey(item.Id) ||
result.Values.Any(t => t.SourceProjectId == item.SourceProjectId && t.TargetProjectId == item.TargetProjectId))
continue;
var dbItem = new DependencyResolveModel.ProjectItem
{
Id = item.Id,
IsNew = true,
Level = 0,
ProjectId = item.ProjectId,
SourceProjectId = item.SourceProjectId,
TargetProjectId = item.TargetProjectId,
SourceStartDate = item.SourceStartDate,
SourceEndDate = item.SourceEndDate,
TargetStartDate = item.TargetStartDate,
TargetEndDate = item.TargetEndDate,
Type = item.Type
};
if (projects.ContainsKey(dbItem.SourceProjectId))
{
dbItem.SourceProjectName = projects[dbItem.SourceProjectId].ProjectName;
if (scenarios.ContainsKey(projects[dbItem.SourceProjectId].Id))
{
var scenario = scenarios[projects[dbItem.SourceProjectId].Id];
dbItem.SourceStartDate = dbItem.SourceStartDate ?? scenario.StartDate;
dbItem.SourceEndDate = dbItem.SourceEndDate ?? scenario.EndDate;
}
}
if (projects.ContainsKey(dbItem.TargetProjectId))
{
dbItem.TargetProjectName = projects[dbItem.TargetProjectId].ProjectName;
if (scenarios.ContainsKey(projects[dbItem.TargetProjectId].Id))
{
var scenario = scenarios[projects[dbItem.TargetProjectId].Id];
dbItem.TargetStartDate = dbItem.TargetStartDate ?? scenario.StartDate;
dbItem.TargetEndDate = dbItem.TargetEndDate ?? scenario.EndDate;
}
}
result.Add(dbItem.Id, dbItem);
}
foreach (var item in dbItems)
{
if (!result.ContainsKey(item.Key))
result.Add(item.Key, item.Value);
}
return result;
}
public DependencyChain<DependencyResolveModel.ProjectItem> LoadResolveDependenciesRecursively(IEnumerable<Guid> projectIds, int level, bool getAncestors,
Dictionary<Guid, DependencyResolveModel.ProjectItem> changedItemsDict, TimeSpan startDateOffset, TimeSpan endDateOffset,
Dictionary<Guid, Tuple<DateTime, DateTime>> changedProjects, List<Guid> visited)
{
var query1 = DbContext.VW_ProjectDependencies.AsQueryable();
var query2 = DbContext.VW_ProjectDependencies.AsQueryable();
IEnumerable<DependencyResolveModel.ProjectItem> extractedDependencies;
if (getAncestors)
{
var commonQuery = query1.Where(t => projectIds.Contains(t.TargetProjectId) && !visited.Contains(t.SourceProjectId))
.Select(x => new
{
Item = x,
IsReversed = false
});
var reversedLinksQuery = query2.Where(t => (projectIds.Contains(t.SourceProjectId) &&
(t.Type == (short)ProjectDependencyDisplayType.Link) && !visited.Contains(t.TargetProjectId)))
.Select(x => new
{
Item = x,
IsReversed = true
});
extractedDependencies = commonQuery.Union(reversedLinksQuery).ToList().Select(x =>
DependencyResolveModel.ProjectItem.CreateFrom(x.Item, level, x.IsReversed));
}
else
{
var commonQuery = query1.Where(t => projectIds.Contains(t.SourceProjectId) && !visited.Contains(t.TargetProjectId))
.Select(x => new
{
Item = x,
IsReversed = false
});
var reversedLinksQuery = query2.Where(t => (projectIds.Contains(t.TargetProjectId) &&
(t.Type == (short)ProjectDependencyDisplayType.Link) && !visited.Contains(t.SourceProjectId)))
.Select(x => new
{
Item = x,
IsReversed = true
});
extractedDependencies = commonQuery.Union(reversedLinksQuery).ToList().Select(x =>
DependencyResolveModel.ProjectItem.CreateFrom(x.Item, level, x.IsReversed));
}
var dbItems = extractedDependencies.GroupBy(t => t.Id).ToDictionary(key => key.Key, el => el.FirstOrDefault());
foreach (var item in dbItems.Values)
{
// Source code:
//if (getAncestors)
// item.FillAncestor(startDateOffset);
//else
// item.FillDescendant(endDateOffset);
if ((getAncestors && (item.Type != (short)ProjectDependencyDisplayType.Link)) ||
(getAncestors && !item.IsReversed) || (!getAncestors && item.IsReversed))
item.FillAncestor(startDateOffset);
else
item.FillDescendant(endDateOffset);
}
// inject changes from client model to direct relations
var result = InjectChanges(dbItems.Values, changedItemsDict, changedProjects);
if (result.Any())
{
var itemsToGoFrom =
result.Values.Where(x => (x.Action != DependencyResolveModel.ActionType.RemoveDependency) &&
(getAncestors && !x.IsReversed) || (!getAncestors && x.IsReversed)).Select(x => x.SourceProjectId)
.Union(
result.Values.Where(x => (x.Action != DependencyResolveModel.ActionType.RemoveDependency) &&
(getAncestors && x.IsReversed) || (!getAncestors && !x.IsReversed)).Select(x => x.TargetProjectId)
);
visited.AddRange(itemsToGoFrom);
var nextLevel = getAncestors ? level - 1 : level + 1;
var nextLevelResult = LoadResolveDependenciesRecursively(itemsToGoFrom, nextLevel, getAncestors,
changedItemsDict, startDateOffset, endDateOffset, changedProjects, visited);
foreach (var item in nextLevelResult)
{
if (!result.ContainsKey(item.Key))
result.Add(item.Key, item.Value);
}
}
return DependencyChain<DependencyResolveModel.ProjectItem>.CreateFrom(result);
}
/// <summary>
/// Applies changes to projects/scenarios entered by user in resolve dependency conflicts modal form.
/// </summary>
/// <param name="model">A collection of resolve conflict items, one per project.</param>
public void ResolveConflicts(DependencyResolveModel model)
{
if (model == null || Guid.Empty.Equals(model.ProjectId) || model.ResolvePlan == null || !model.ResolvePlan.Any())
return;
var changedItems = model.ResolvePlan.GroupBy(t => t.Id).ToDictionary(gr => gr.Key, grel => grel.FirstOrDefault());
var dbItems = LoadDependencies(changedItems.Keys).GroupBy(t => t.Id).ToDictionary(gr => gr.Key, grel => grel.FirstOrDefault());
if (changedItems == null || !changedItems.Any())
return;
var updatedProjects = new Dictionary<Guid, double>();
foreach (var group in changedItems)
{
var item = group.Value;
var id = group.Key;
switch (item.Action)
{
case DependencyResolveModel.ActionType.ConvertToLink:
if (dbItems.ContainsKey(id))
{
// update dependency item itself
var dbItem = dbItems[id];
dbItem.Type = (short)ProjectDependencyModel.ProjectDependencyType.Link;
DbContext.Entry(dbItem).State = EntityState.Modified;
// gather projects which timestamps should be updated
if (!updatedProjects.ContainsKey(dbItem.SourceProjectId))
updatedProjects.Add(dbItem.SourceProjectId, 0);
if (!updatedProjects.ContainsKey(dbItem.TargetProjectId))
updatedProjects.Add(dbItem.TargetProjectId, 0);
}
break;
case DependencyResolveModel.ActionType.RemoveDependency:
if (dbItems.ContainsKey(id))
{
var dbItem = dbItems[id];
// gather projects which timestamps should be updated
if (!updatedProjects.ContainsKey(dbItem.SourceProjectId))
updatedProjects.Add(dbItem.SourceProjectId, 0);
if (!updatedProjects.ContainsKey(dbItem.TargetProjectId))
updatedProjects.Add(dbItem.TargetProjectId, 0);
// update dependency item itself
DbContext.Entry(dbItem).State = EntityState.Deleted;
}
break;
default:
if (DependencyResolveModel.ActionType.DontMove.Equals(item.Action) ||
!item.MoveStartDate.HasValue || !item.MoveEndDate.HasValue ||
(item.ProjectId == item.SourceProjectId && !item.SourceStartDate.HasValue) ||
(item.ProjectId == item.TargetProjectId && !item.TargetStartDate.HasValue))
break;
var timespan = 0D;
if (item.ProjectId == item.SourceProjectId)
timespan = item.MoveStartDate.Value.Subtract(item.SourceStartDate.Value).TotalMilliseconds;
else if (item.ProjectId == item.TargetProjectId)
timespan = item.MoveStartDate.Value.Subtract(item.TargetStartDate.Value).TotalMilliseconds;
// gather projects which active scenarios should be updated
if (!updatedProjects.ContainsKey(item.ProjectId))
updatedProjects.Add(item.ProjectId, timespan);
break;
}
}
if (!updatedProjects.Any())
return;
// update all changed project's timestamps
var projects = ProjectManager.FindProjects(updatedProjects.Keys);
var projectIds2Update = updatedProjects.Where(t => t.Value != 0).Select(t => t.Key).ToList();
var scenariosDict = ScenarioManager.GetScenarios4Projects(projectIds2Update, ScenarioType.Portfolio, ScenarioStatus.Active)
.GroupBy(t => t.ParentId.Value).ToDictionary(t => t.Key, x => x.FirstOrDefault());
var scenarioIds2Update = scenariosDict.Values.Select(t => t.Id).ToList();
var scenarioDetailsDict = ScenarioManager.GetScenarioDetails(scenarioIds2Update)
.GroupBy(t => t.ParentID).ToDictionary(t => t.Key, x => x.ToList()); ;
var teamAllocationsDict = TeamManager.GetTeamsAllocation(scenarioIds2Update)
.GroupBy(t => t.ScenarioId).ToDictionary(gr => gr.Key, grel => grel.ToList());
var resourceAllocationsDict = DbContext.PeopleResourceAllocations.Where(t => scenarioIds2Update.Contains(t.ScenarioId))
.GroupBy(t => t.ScenarioId).ToDictionary(gr => gr.Key, grel => grel.ToList());
var resourceIds = resourceAllocationsDict.Values.SelectMany(t => t.Select(x => x.PeopleResourceId)).Distinct();
var resources = (new PeopleResourcesManager(DbContext)).GetPeopleResourceWithTeams4Resources(resourceIds)
.GroupBy(t => t.Id).ToDictionary(gr => gr.Key, grel => grel.FirstOrDefault());
foreach (var item in projects)
{
// update project timestamp
ProjectManager.Save((ProjectModel)item);
// shift scenarios and all related allocations
var offsetMs = updatedProjects.ContainsKey(item.Id) ? updatedProjects[item.Id] : 0;
if (offsetMs != 0)
{
if (scenariosDict.ContainsKey(item.Id))
{
// shift scenario
var scenario = scenariosDict[item.Id];
var replWeeks = BuildReplacementWeeksDict(scenario.StartDate.Value, scenario.EndDate.Value, offsetMs);
//var offsetWeeks =
scenario.StartDate = scenario.StartDate.Value.AddMilliseconds(offsetMs);
scenario.EndDate = scenario.EndDate.Value.AddMilliseconds(offsetMs);
if (scenarioDetailsDict.ContainsKey(scenario.Id))
{
foreach (var sd in scenarioDetailsDict[scenario.Id])
{
var clone = new ScenarioDetail
{
Id = Guid.NewGuid(),
Cost = sd.Cost,
ExpenditureCategoryId = sd.ExpenditureCategoryId,
ParentID = sd.ParentID,
Quantity = sd.Quantity,
WeekOrdinal = sd.WeekOrdinal,
WeekEndingDate = replWeeks.ContainsKey(sd.WeekEndingDate.Value) ? replWeeks[sd.WeekEndingDate.Value] : sd.WeekEndingDate
};
DbContext.Entry(sd).State = EntityState.Deleted;
if (replWeeks.ContainsKey(sd.WeekEndingDate.Value))
DbContext.Entry(clone).State = EntityState.Added;
};
}
if (teamAllocationsDict.ContainsKey(scenario.Id))
{
foreach (var ta in teamAllocationsDict[scenario.Id])
{
var clone = new TeamAllocation
{
Id = Guid.NewGuid(),
ExpenditureCategoryId = ta.ExpenditureCategoryId,
Quantity = ta.Quantity,
ScenarioId = ta.ScenarioId,
TeamId = ta.TeamId,
WeekEndingDate = replWeeks.ContainsKey(ta.WeekEndingDate) ? replWeeks[ta.WeekEndingDate] : ta.WeekEndingDate
};
DbContext.Entry(ta).State = EntityState.Deleted;
if (replWeeks.ContainsKey(ta.WeekEndingDate))
DbContext.Entry(clone).State = EntityState.Added;
};
}
if (resourceAllocationsDict.ContainsKey(scenario.Id))
{
foreach (var ra in resourceAllocationsDict[scenario.Id])
{
var newWeek = ra.WeekEndingDate;
if (resources.ContainsKey(ra.PeopleResourceId))
{
var res = resources[ra.PeopleResourceId];
if (replWeeks.ContainsKey(ra.WeekEndingDate))
{
newWeek = replWeeks[ra.WeekEndingDate];
if (res.Teams.Any(team => (team.StartDate <= newWeek) && (!team.EndDate.HasValue || team.EndDate >= newWeek)))
{
var clone = new PeopleResourceAllocation
{
Id = Guid.NewGuid(),
ExpenditureCategoryId = ra.ExpenditureCategoryId,
PeopleResourceId = ra.PeopleResourceId,
Quantity = ra.Quantity,
ScenarioId = ra.ScenarioId,
TeamId = ra.TeamId,
WeekEndingDate = newWeek
};
DbContext.Entry(ra).State = EntityState.Deleted;
DbContext.Entry(clone).State = EntityState.Added;
}
else
{
DbContext.Entry(ra).State = EntityState.Deleted;
}
}
else
{
DbContext.Entry(ra).State = EntityState.Deleted;
}
}
}
}
}
}
}
}
private Dictionary<DateTime, DateTime> BuildReplacementWeeksDict(DateTime startDate, DateTime endDate, double offsetMs)
{
var result = new Dictionary<DateTime, DateTime>();
var scenarioWeeks = FiscalCalendarManager.GetWeekendingsByRange(startDate, endDate, DbContext);
var modifiedScenarioWeeks = new List<DateTime>();
modifiedScenarioWeeks = offsetMs > 0 ? FiscalCalendarManager.GetWeekendingsByRange(startDate, endDate.AddMilliseconds(offsetMs), DbContext) : FiscalCalendarManager.GetWeekendingsByRange(startDate.AddMilliseconds(offsetMs), endDate, DbContext);
var offsetWeeks = (modifiedScenarioWeeks.Count - scenarioWeeks.Count) * (offsetMs > 0 ? 1 : -1);
var indexOffset = offsetWeeks < 0 ? 0 : offsetWeeks;
for (var i = 0; i < scenarioWeeks.Count; i++)
{
result.Add(scenarioWeeks[i], modifiedScenarioWeeks[i + indexOffset]);
}
return result;
}
}
}