271 lines
13 KiB
C#
271 lines
13 KiB
C#
using EnVisage.Code;
|
|
using EnVisage.Code.BLL;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Linq;
|
|
using System.Web;
|
|
|
|
namespace EnVisage.Models.ProjectDependencies
|
|
{
|
|
/// <summary>
|
|
/// Represents an information about all date conflicts with dependent projects with available options to resolve them.
|
|
/// </summary>
|
|
public class DependencyResolveModel : IValidatableObject
|
|
{
|
|
#region enums
|
|
public enum ActionType
|
|
{
|
|
[DisplayValue("Convert to Link")]
|
|
ConvertToLink = 0,
|
|
[DisplayValue("Remove Dependency")]
|
|
RemoveDependency = 1,
|
|
[DisplayValue("Move With")]
|
|
MoveWith = 2,
|
|
[DisplayValue("Don't Move")]
|
|
DontMove = 3,
|
|
[DisplayValue("Custom Move")]
|
|
CustomMove = 4
|
|
}
|
|
#endregion
|
|
|
|
#region sub-classes
|
|
public class ProjectItem : DependencyChainItem
|
|
{
|
|
public Guid ProjectId { get; set; }
|
|
public string ProjectName { get; set; }
|
|
[RequiredIf("IsActionRequired", true, ErrorMessage = "Select one of resolve options")]
|
|
public ActionType? Action { get; set; }
|
|
public bool IsActionRequired
|
|
{
|
|
get
|
|
{
|
|
return AvailableMoveTypes != null && AvailableMoveTypes.Any();
|
|
}
|
|
}
|
|
public List<ActionType> AvailableMoveTypes { get; set; }
|
|
public bool IsDateRequired
|
|
{
|
|
get
|
|
{
|
|
return ActionType.CustomMove.Equals(Action);
|
|
}
|
|
}
|
|
public DateTime? OldStartDate { get; set; }
|
|
public DateTime? OldEndDate { get; set; }
|
|
[RequiredIf("IsDateRequired", true, ErrorMessage = "Date field is required.")]
|
|
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
|
|
public DateTime? MoveStartDate { get; set; }
|
|
public DateTime? MoveEndDate
|
|
{
|
|
get
|
|
{
|
|
if (!MoveStartDate.HasValue)
|
|
return null;
|
|
if (!OldStartDate.HasValue || !OldEndDate.HasValue)
|
|
return null;
|
|
return OldEndDate.Value.Add((MoveStartDate.Value.Subtract(OldStartDate.Value)));
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether current item is new and not saved to databse yet or not.
|
|
/// </summary>
|
|
public bool IsNew { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether current item to be removed and not saved to databse yet or not.
|
|
/// </summary>
|
|
public bool IsDeleted { get; set; }
|
|
public DateTime MinAvailableScenarioStartDate { get; set; }
|
|
public DateTime MaxAvailableScenarioStartDate { get; set; }
|
|
|
|
public ProjectItem()
|
|
{
|
|
AvailableMoveTypes = new List<ActionType>();
|
|
}
|
|
public static explicit operator ProjectItem(VW_ProjectDependencies obj)
|
|
{
|
|
if (obj == null)
|
|
return null;
|
|
var newObject = new ProjectItem();
|
|
newObject.Id = obj.Id;
|
|
newObject.SourceProjectId = obj.SourceProjectId;
|
|
newObject.TargetProjectId = obj.TargetProjectId;
|
|
newObject.Type = obj.Type;
|
|
newObject.Level = 0;
|
|
newObject.SourceStartDate = obj.SourceStartDate;
|
|
newObject.SourceEndDate = obj.SourceEndDate;
|
|
newObject.SourceProjectName = obj.SourceProjectName;
|
|
newObject.TargetStartDate = obj.TargetStartDate;
|
|
newObject.TargetEndDate = obj.TargetEndDate;
|
|
newObject.TargetProjectName = obj.TargetProjectName;
|
|
newObject.SourceDeadline = obj.SourceDeadline;
|
|
newObject.TargetDeadline = obj.TargetDeadline;
|
|
newObject.SourceHasActuals = obj.SourceHasActuals;
|
|
newObject.TargetHasActuals = obj.TargetHasActuals;
|
|
return newObject;
|
|
}
|
|
public static new ProjectItem CreateFrom(VW_ProjectDependencies obj, int level, bool isReversed)
|
|
{
|
|
if (obj == null)
|
|
return null;
|
|
|
|
var newObject = (ProjectItem)obj;
|
|
newObject.Level = level;
|
|
newObject.IsReversed = isReversed;
|
|
return newObject;
|
|
}
|
|
|
|
public void FillAncestor(TimeSpan startDateOffset)
|
|
{
|
|
AvailableMoveTypes.Clear();
|
|
using (var db = new EnVisageEntities())
|
|
{
|
|
var utcNowWeekEnding = FiscalCalendarManager.GetFiscalCalendarWeekForDate(DateTime.UtcNow.Date, db);
|
|
if (utcNowWeekEnding == null)
|
|
return;
|
|
var weekStartDate = utcNowWeekEnding.StartDate;
|
|
var weekEndDate = utcNowWeekEnding.EndDate;
|
|
if (!SourceEndDate.HasValue || !TargetStartDate.HasValue ||
|
|
SourceEndDate.Value < TargetStartDate || // validation rule 1
|
|
Type == (short)ProjectDependencyModel.ProjectDependencyType.Link)
|
|
AvailableMoveTypes.Add(ActionType.DontMove);
|
|
if (SourceStartDate.HasValue && SourceStartDate > weekEndDate) // validation rule 2
|
|
if (SourceStartDate.Value.Add(startDateOffset) > weekEndDate) // validation rule 3
|
|
AvailableMoveTypes.Add(ActionType.MoveWith);
|
|
if (Type == (short)ProjectDependencyModel.ProjectDependencyType.Link || SourceEndDate.HasValue)
|
|
AvailableMoveTypes.Add(ActionType.CustomMove);
|
|
}
|
|
AvailableMoveTypes.Add(ActionType.ConvertToLink);
|
|
AvailableMoveTypes.Add(ActionType.RemoveDependency);
|
|
ProjectId = SourceProjectId;
|
|
ProjectName = SourceProjectName;
|
|
OldStartDate = SourceStartDate;
|
|
OldEndDate = SourceEndDate;
|
|
Action = AvailableMoveTypes.FirstOrDefault();
|
|
}
|
|
public void FillDescendant(TimeSpan endDateOffset)
|
|
{
|
|
AvailableMoveTypes.Clear();
|
|
using (var db = new EnVisageEntities())
|
|
{
|
|
var utcNowWeekEnding = FiscalCalendarManager.GetFiscalCalendarWeekForDate(DateTime.UtcNow.Date, db);
|
|
if (utcNowWeekEnding == null)
|
|
return;
|
|
var weekStartDate = utcNowWeekEnding.StartDate;
|
|
var weekEndDate = utcNowWeekEnding.EndDate;
|
|
// move this logic to ProjectDependencyManager.InjectChanges method
|
|
if (!SourceEndDate.HasValue || !TargetStartDate.HasValue ||
|
|
SourceEndDate.Value < TargetStartDate || // validation rule 1
|
|
Type == (short)ProjectDependencyModel.ProjectDependencyType.Link)
|
|
AvailableMoveTypes.Add(ActionType.DontMove);
|
|
if (TargetStartDate.HasValue && TargetStartDate > weekEndDate) // validation rule 2
|
|
if (TargetStartDate.Value.Add(endDateOffset) > weekEndDate || Type == (short)ProjectDependencyModel.ProjectDependencyType.Link) // validation rule 3
|
|
AvailableMoveTypes.Add(ActionType.MoveWith);
|
|
if (Type == (short)ProjectDependencyModel.ProjectDependencyType.Link || TargetStartDate.HasValue)
|
|
AvailableMoveTypes.Add(ActionType.CustomMove);
|
|
}
|
|
AvailableMoveTypes.Add(ActionType.ConvertToLink);
|
|
AvailableMoveTypes.Add(ActionType.RemoveDependency);
|
|
ProjectId = TargetProjectId;
|
|
ProjectName = TargetProjectName;
|
|
OldStartDate = TargetStartDate;
|
|
OldEndDate = TargetEndDate;
|
|
Action = AvailableMoveTypes.FirstOrDefault();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Properties
|
|
/// <summary>
|
|
/// Gets or sets a project.Id of changed project.
|
|
/// </summary>
|
|
public Guid ProjectId { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets new start date for the changed project.
|
|
/// </summary>
|
|
public DateTime? StartDate { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets new end date for the changed project.
|
|
/// </summary>
|
|
public DateTime? EndDate { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets a list of resolve conflict options for each dependent project.
|
|
/// </summary>
|
|
public List<ProjectItem> ResolvePlan { get; set; }
|
|
/// <summary>
|
|
/// Gets a value indicating whether new project dates intersects with dependent projects or not.
|
|
/// True - if at least one dependent project intersects with new changed project dates.
|
|
/// </summary>
|
|
public bool HasDateConflicts
|
|
{
|
|
get
|
|
{
|
|
if (ResolvePlan == null)
|
|
return false;
|
|
if (!ResolvePlan.Any())
|
|
return false;
|
|
foreach (var item in ResolvePlan)
|
|
{
|
|
// looking only at direct relations where dependencies could have been broken
|
|
if (item.Level > 0)
|
|
continue;
|
|
// looking only at StartsBefore dependencies, as Link dependencies could not be broken
|
|
if (item.Type == (short)ProjectDependencyModel.ProjectDependencyType.Link)
|
|
continue;
|
|
|
|
if (item.SourceEndDate.HasValue && item.TargetStartDate.HasValue && item.SourceEndDate >= item.TargetStartDate)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public DependencyResolveModel()
|
|
{
|
|
ResolvePlan = new List<ProjectItem>();
|
|
}
|
|
|
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
if (ResolvePlan != null && ResolvePlan.Any())
|
|
{
|
|
for (var i = 0; i < ResolvePlan.Count; i++)
|
|
{
|
|
var item = ResolvePlan[i];
|
|
if (item.Type == (short)ProjectDependencyModel.ProjectDependencyType.StartsBefore)
|
|
{
|
|
if (item.Action == ActionType.ConvertToLink ||
|
|
item.Action == ActionType.RemoveDependency)
|
|
continue;
|
|
if (item.ProjectId == item.SourceProjectId)
|
|
{
|
|
if (item.MoveEndDate >= item.TargetStartDate)
|
|
{
|
|
var newStartDate = item.TargetStartDate.Value.Subtract(item.OldEndDate.Value.Subtract(item.OldStartDate.Value));
|
|
yield return new ValidationResult(string.Format(Constants.ERROR_COMPARE_DATES, item.SourceProjectName +
|
|
" new start", "less than", newStartDate.ToShortDateString()), new[] { "ResolvePlan[" + i + "].Action" });
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (item.MoveStartDate <= item.SourceEndDate)
|
|
{
|
|
var newStartDate = item.SourceEndDate.Value;
|
|
yield return new ValidationResult(string.Format(Constants.ERROR_COMPARE_DATES, item.TargetProjectName +
|
|
" new start", "greater than", newStartDate.ToShortDateString()), new[] { "ResolvePlan[" + i + "].Action" });
|
|
}
|
|
}
|
|
if ((item.Action == ActionType.MoveWith || item.Action == ActionType.CustomMove)
|
|
&& item.MoveStartDate.HasValue && item.MoveStartDate < DateTime.UtcNow.Date)
|
|
{
|
|
yield return new ValidationResult(string.Format(Constants.ERROR_COMPARE_DATES,
|
|
(item.ProjectId == item.SourceProjectId ? item.SourceProjectName : item.TargetProjectName) +
|
|
" new start", "greater than", DateTime.UtcNow.Date.ToShortDateString()), new[] { "ResolvePlan[" + i + "].Action" });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |