369 lines
15 KiB
C#
369 lines
15 KiB
C#
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<Scenario>
|
|
{
|
|
#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; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets an Id of the template.
|
|
/// </summary>
|
|
[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; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets an Id of the project.
|
|
/// </summary>
|
|
public Guid ParentId { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a color that will be used for scenario in reports.
|
|
/// </summary>
|
|
[MaxLength(8)]
|
|
public string Color { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a type of scenario.
|
|
/// </summary>
|
|
[Required]
|
|
[Display(Name = "Scenario Type")]
|
|
public ScenarioType? Type { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a scenario of scenario.
|
|
/// </summary>
|
|
[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<int> 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<ScenarioModel.ExpenditureItem> Expenditures { get; set; }
|
|
|
|
public IList<ScenarioModel.ProjectItem> Projects { get; set; }
|
|
|
|
public IList<ScenarioModel.ExpenditureItem> ScenarioExpenditures { get; set; }
|
|
|
|
public RatesModel RatesModel { get; set; }
|
|
|
|
public string BackUrl { get; set; }
|
|
public string BackName { get; set; }
|
|
|
|
public List<Team2Scenario> TeamAllocations { get; set; }
|
|
public string TeamAllocationsJson { get; set; }
|
|
|
|
public List<NoteModel> Notes { get {
|
|
List<NoteModel> result = new List<NoteModel>();
|
|
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<ScenarioModel.ExpenditureItem>();
|
|
RatesModel = new RatesModel();
|
|
}
|
|
#endregion
|
|
|
|
#region Methods
|
|
/// <summary>
|
|
/// Casts a <see cref="Scenario"/> obect to the object of type <see cref="ScenarioModel"/>.
|
|
/// </summary>
|
|
/// <param name="obj">A <see cref="Scenario"/> object.</param>
|
|
/// <returns>A <see cref="ScenarioModel"/> object filled with data from db.</returns>
|
|
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<ScenarioModel.ExpenditureItem>(),
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies data from model to DAL object.
|
|
/// </summary>
|
|
/// <param name="dbObj">A target DAL object.</param>
|
|
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;
|
|
}
|
|
}
|
|
} |