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
{
///
/// Represents an information about all date conflicts with dependent projects with available options to resolve them.
///
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 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)));
}
}
///
/// Gets or sets a value indicating whether current item is new and not saved to databse yet or not.
///
public bool IsNew { get; set; }
///
/// Gets or sets a value indicating whether current item to be removed and not saved to databse yet or not.
///
public bool IsDeleted { get; set; }
public DateTime MinAvailableScenarioStartDate { get; set; }
public DateTime MaxAvailableScenarioStartDate { get; set; }
public ProjectItem()
{
AvailableMoveTypes = new List();
}
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
///
/// Gets or sets a project.Id of changed project.
///
public Guid ProjectId { get; set; }
///
/// Gets or sets new start date for the changed project.
///
public DateTime? StartDate { get; set; }
///
/// Gets or sets new end date for the changed project.
///
public DateTime? EndDate { get; set; }
///
/// Gets or sets a list of resolve conflict options for each dependent project.
///
public List ResolvePlan { get; set; }
///
/// 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.
///
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();
}
public IEnumerable 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" });
}
}
}
}
}
}
}