using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using EnVisage.Code; using System.Data.Entity; using System.Linq; namespace EnVisage.Models { public class ScenarioDetailModel : IBaseModel { #region Enums and subclasses public enum ScenarioProbability { Low = 1, Expected = 2, High = 3 } public enum SavingsType { HardSavings, SoftSavings } #endregion #region Properties public Guid Id { get; set; } [Required] [AllowHtml] [MaxLength(100)] public string Name { get; set; } /// /// Gets or sets an Id of the template. /// [Display(Name = "Template")] public Guid TemplateId { get; set; } [Display(Name = "Template Name")] public string TemplateName{ get; set; } [Display(Name = "Project Name")] public string ProjectName { get; set; } /// /// Gets or sets an Id of the project. /// public Guid ParentId { get; set; } /// /// Gets or sets a color that will be used for scenario in reports. /// [MaxLength(8)] public string Color { get; set; } /// /// Gets or sets a type of scenario. /// [Required] [Display(Name = "Scenario Type")] public ScenarioType? Type { get; set; } /// /// Gets or sets a scenario of scenario. /// [Display(Name = "Scenario Status")] public ScenarioStatus? Status { get; set; } [Display(Name = "Active Scenario")] public bool IsActiveScenario { get; set; } [Display(Name = "Freeze Resources")] public bool FreezeResources { get; set; } [Display(Name = "Use this project's ACTUALS data for weekending dates that apply?")] public bool UseActuals { get; set; } [Display(Name = "Child Scenarios #")] public int ChildScenarios { get; set; } // not editable[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)] [Display(Name = "Scenario Start Date")] public DateTime? StartDate { get; set; } // not editable[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)] [Display(Name = "Scenario End Date")] public DateTime? EndDate { get; set; } [DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)] [Display(Name = "Milestone Start Date")] public DateTime? MilestoneStartDate { get; set; } [Display(Name = "Scenario End Date")] public string EndDateStr { get; set; } [Display(Name = "Scenario Start Date")] public string StartDateStr { get; set; } [Display(Name = "Growth Scenario?")] public bool GrowthScenario { get; set; } [Display(Name = "Revenue Generating")] public bool IsRevenueGenerating { get; set; } [Display(Name = "Projected Revenue")] public int ProjectedRevenue { get; set; } [Display(Name = "Use L&M Margin?")] public bool UseLMMargin { get; set; } [Display(Name = "Expected Gross Margin")] // not editable[Range(1, 100)] public int? GrossMargin { get; set; } [Display(Name = "L&M Margin")] // not editable //[Range(1, 100)] public int? LMMargin { get; set; } [Display(Name = "Calculated Gross Margin (Forecast)")] [Range(1, 100)] public int? CalculatedGrossMargin { get; set; } [Display(Name = "Calculated Gross Margin LM (Forecast)")] public int? CalculatedGrossMarginLM { get; set; } [Display(Name = "Calculated Gross Margin (Actuals)")] public int? CalculatedGrossMarginActuals { get; set; } [Display(Name = "Calculated Gross Margin LM (Actuals)")] [Range(1, 100)] public int? CalculatedGrossMarginLMActuals { get; set; } [Display(Name = "Top-Down Direct Costs")] public int TDDirectCosts { get; set; } [Display(Name = "Top-Down Direct Costs LM")] public int TDDirectCostsLM { get; set; } [Display(Name = "Top-Down Revenue Per Milestone")] public int TDRevenuePerShot { get; set; } [Display(Name = "Bottom-Up Direct Costs (Forecast)")] public int BUDirectCosts { get; set; } [Display(Name = "Bottom-Up Direct Costs LM (Forecast)")] public int BUDirectCostsLM { get; set; } [Display(Name = "Bottom-Up Direct Costs (Actuals)")] public int BUDirectCostsActuals { get; set; } [Display(Name = "Bottom-Up Direct Costs LM (Actuals)")] public int BUDirectCostsLMActuals { get; set; } [Display(Name = "Bottom-Up Costs/Milestones (Forecast)")] public int BUCostsShots { get; set; } [Display(Name = "Bottom-Up Costs/Milestones LM (Forecast)")] public int BUCostsShotsLM { get; set; } [Display(Name = "Bottom-Up Costs/Milestones (Actuals)")] public int BUCostsShotsActuals { get; set; } [Display(Name = "Bottom-Up Costs/Milestones LM (Actuals)")] public int BUCostsShotsLMActuals { get; set; } [Display(Name = "Total Milestones")] // not editable //[Range(1, 2147483647)] public int TotalMilestones { get; set; } [Display(Name = "Labor Split Percentage")] // not editable //[Range(0, 100)] [UIHint("Slider")] public short LaborSplitPercentage { get; set; } [Display(Name = "Labor/Materials Split")] public string LaborMaterialsSplit { get; set; } [Display(Name = "Duration")] public Nullable Duration { get; set; } [Display(Name = "Cost Savings")] public int? CostSavings { get; set; } [Display(Name = "Cost Savings Duration")] public int? CostSavingsDuration { get; set; } [Display(Name = "ROI Date")] public DateTime? ROIDate { get; set; } [Display(Name = "ROI Date")] public string ROIDateStr { get { return ROIDate.HasValue ? ROIDate.Value.ToShortDateString() : ""; } set { } } [Display(Name = "Hard/Soft Savings")] public string HardSoftSavings { get; set; } [Display(Name = "Savings After Cost")] public int? SavingsAfterCost { get; set; } [Display(Name = "Revenue After Cost")] public int? RevenueAfterCost { get; set; } public ScenarioStatus CopyStatus { get; set; } public int CopyProgectId { get; set; } public IList Expenditures { get; set; } public IList Projects { get; set; } public IList ScenarioExpenditures { get; set; } public RatesModel RatesModel { get; set; } public string BackUrl { get; set; } public string BackName { get; set; } public List TeamAllocations { get; set; } public string TeamAllocationsJson { get; set; } public List Notes { get { List result = new List(); EnVisageEntities DbContext = new EnVisageEntities(); var notes = (from c in DbContext.Notes where c.ParentId == Id select c).ToList(); foreach (var note in notes) result.Add((NoteModel)note); return result; } set{} } public string ActiveTab { get; set; } #endregion #region Constructors public ScenarioDetailModel() { Expenditures = new List(); RatesModel = new RatesModel(); } #endregion #region Methods /// /// Casts a obect to the object of type . /// /// A object. /// A object filled with data from db. public static explicit operator ScenarioDetailModel(Scenario obj) { if (obj == null) return null; var model = new ScenarioDetailModel { Id = obj.Id, Name = obj.Name, ProjectName = obj.Project.Name, TemplateName = obj.ParentScenario != null ? obj.ParentScenario.Name : string.Empty, Color = obj.Color, TemplateId = obj.TemplateId ?? Guid.Empty, ParentId = obj.ParentId ?? Guid.Empty, Type = obj.Type == null ? (ScenarioType?) null : (ScenarioType) obj.Type, ChildScenarios = obj.ChildScenarios.Count, EndDate = obj.EndDate, StartDate = obj.StartDate, EndDateStr = (obj.EndDate ?? DateTime.Today).ToString("d"), StartDateStr = (obj.StartDate ?? DateTime.Today).ToString("d"), Status = obj.Status == null ? (ScenarioStatus?)null : (ScenarioStatus)obj.Status, GrossMargin = obj.ExpectedGrossMargin.HasValue ? Convert.ToInt32(obj.ExpectedGrossMargin * 100) : (int?)null, GrowthScenario = obj.GrowthScenario, IsActiveScenario = obj.Status == Code.ScenarioStatus.Active.GetHashCode(), LMMargin = obj.ExpectedGrossMargin_LM.HasValue ? Convert.ToInt32(obj.ExpectedGrossMargin_LM * 100) : (int?)null, UseLMMargin = obj.UseLMMargin == 1, LaborSplitPercentage = Convert.ToInt16(obj.CGSplit * 100), MilestoneStartDate = obj.ShotStartDate, ProjectedRevenue = (int)Math.Round(obj.ProjectedRevenue ?? 0), TotalMilestones = obj.Shots ?? 0, Expenditures = new List(), Duration = obj.Duration, CalculatedGrossMargin = obj.CalculatedGrossMargin.HasValue ? Convert.ToInt32(obj.CalculatedGrossMargin * 100) : (int?)null, CalculatedGrossMarginLM = obj.CalculatedGrossMargin_LM.HasValue ? Convert.ToInt32(obj.CalculatedGrossMargin_LM * 100) : (int?)null, CalculatedGrossMarginActuals = obj.CalculatedGrossMargin.HasValue ? Convert.ToInt32(obj.CalculatedGrossMargin * 100) : (int?)null, CalculatedGrossMarginLMActuals = obj.CalculatedGrossMargin_LM.HasValue ? Convert.ToInt32(obj.CalculatedGrossMargin_LM * 100) : (int?)null, TDDirectCosts = (int)Math.Round(obj.TDDirectCosts ?? 0), TDDirectCostsLM = (int)Math.Round(obj.TDDirectCosts_LM ?? 0), TDRevenuePerShot = (int)Math.Round(obj.TDRevenueShot ?? 0), BUDirectCosts = (int)Math.Round(obj.BUDirectCosts ?? 0), BUDirectCostsLM = (int)Math.Round(obj.BUDirectCosts_LM ?? 0), BUDirectCostsActuals = (int)Math.Round(obj.Actuals_BUDirectCosts ?? 0), BUDirectCostsLMActuals = (int)Math.Round(obj.Actuals_BUDirectCosts_LM ?? 0), BUCostsShots = (int)Math.Round((((obj.Shots ?? 0) > 0) ? (obj.BUDirectCosts ?? 0) / (obj.Shots ?? 0) : (obj.BUDirectCosts ?? 0))), BUCostsShotsLM = (int)Math.Round((((obj.Shots ?? 0) > 0) ? (obj.BUDirectCosts_LM ?? 0) / (obj.Shots ?? 0) : (obj.BUDirectCosts_LM ?? 0))), BUCostsShotsActuals = (int)Math.Round((((obj.Shots ?? 0) > 0) ? (obj.Actuals_BUDirectCosts ?? 0) / (obj.Shots ?? 0) : (obj.Actuals_BUDirectCosts ?? 0))), BUCostsShotsLMActuals = (int)Math.Round((((obj.Shots ?? 0) > 0) ? (obj.Actuals_BUDirectCosts_LM ?? 0) / (obj.Shots ?? 0) : (obj.Actuals_BUDirectCosts_LM ?? 0))), FreezeResources = Convert.ToBoolean(obj.FreezeRevenue), IsRevenueGenerating = obj.Project.IsRevenueGenerating, CostSavings = (obj.CostSavings.HasValue) ? (int?)obj.CostSavings.Value : (int?)null, CostSavingsDuration = (obj.CostSavingsEndDate.HasValue && obj.CostSavingsStartDate.HasValue) ? (obj.CostSavingsEndDate.Value.Month - obj.CostSavingsStartDate.Value.Month) + 12 * (obj.CostSavingsEndDate.Value.Year - obj.CostSavingsStartDate.Value.Year) + 1 : (int?)null, //TODO: replace with loading of cost savings for all scenarios by single request. See ENV-590. ROIDate = GetROIDate(obj), HardSoftSavings = (obj.CostSavingsType.HasValue) ? (obj.CostSavingsType.Value == 1 ? "Hard" : "Soft") : string.Empty, SavingsAfterCost = (int?)null, RevenueAfterCost = (int?)null, TeamAllocations = obj.Team2Scenario.OrderBy(x => x.Team.Name).ToList(), TeamAllocationsJson = Newtonsoft.Json.JsonConvert.SerializeObject(obj.Team2Scenario.ToDictionary(c => c.TeamId, c => c.Allocation)) }; model.RatesModel = new RatesModel() { ScenarioId = model.Id }; if (obj.CostSavings.HasValue && obj.CostSavingsStartDate.HasValue && obj.CostSavingsEndDate.HasValue) { if (obj.BUDirectCosts.HasValue && obj.CostSavings.Value >= obj.BUDirectCosts.Value) { model.SavingsAfterCost = (int?)(obj.CostSavings.Value - obj.BUDirectCosts.Value); } if (model.IsRevenueGenerating) { model.RevenueAfterCost = model.ProjectedRevenue; if (obj.BUDirectCosts.HasValue && obj.CostSavings.Value < obj.BUDirectCosts.Value) { model.RevenueAfterCost += (int)(obj.CostSavings.Value - obj.BUDirectCosts.Value); } } } model.TrimStringProperties(); return model; } /// /// Copies data from model to DAL object. /// /// A target DAL object. public void CopyTo(Scenario dbObj) { if (dbObj == null) throw new ArgumentNullException(); dbObj.Name = Name; dbObj.Color = Color.Replace("#", ""); dbObj.Type = Type.GetHashCode(); dbObj.Status = IsActiveScenario ? Code.ScenarioStatus.Active.GetHashCode() : Code.ScenarioStatus.Inactive.GetHashCode(); dbObj.ShotStartDate = MilestoneStartDate; } #endregion private static DateTime? GetROIDate(Scenario scenario) { if (!scenario.CostSavings.HasValue) return null; if (scenario.BUDirectCosts > scenario.CostSavings) return null; if (scenario.CostSavings1 == null || scenario.CostSavings1.Count == 0) return null; var costSavingsSum = 0.0M; foreach (var costSaving in scenario.CostSavings1.OrderBy(t=>t.Year).ThenBy(t=>t.Month)) { costSavingsSum += costSaving.Cost; if (costSavingsSum >= scenario.BUDirectCosts) { return new DateTime(costSaving.Year, costSaving.Month, 1); } } return null; } } }