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" }); } } } } } } }