EnVisageOnline/Beta/Source/EnVisage/Models/ScenarioModel.cs

924 lines
36 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using System.Linq;
using EnVisage.Code;
namespace EnVisage.Models
{
public class ScenarioModel : IBaseModel<Scenario>, IValidatableObject
{
#region Enums and subclasses
//public enum ScenarioStatus
//{
// InActive = 0,
// Active = 1
//}
public enum ScenarioProbability
{
Low = 1,
Expected = 2,
High = 3
}
public class ExpenditureItem
{
public Guid Id { get; set; }
public string Group { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
public class ProjectItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
public bool IsRevenueGenerating { get; set; }
}
#endregion
#region Properties
public Guid Id { get; set; }
[Required]
[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; }
/// <summary>
/// Gets or sets an Id of the project.
/// </summary>
public Guid ParentId { get; set; }
public string BackUrl { get; set; }
public string BackName { 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; }
[Display(Name = "Create as an active scenario")]
public bool IsActiveScenario { get; set; }
[Display(Name = "Allow prior week values to be adjusted?")]
public bool AllowAdjustment { 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; }
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Prior Week Cut-off")]
public DateTime? PriorWeekCutOff { get; set; }
[Required]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Scenario Start Date")]
public DateTime? StartDate { get; set; }
public DateTime? StartDateDisabled { get; set; }
[Required]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Scenario End Date")]
public DateTime? EndDate { get; set; }
[Required]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[DateRange(ErrorMessage = "Scenario End Date should be greater than or equal Scenario Start Date and Milestone Start Date should be greater than or equal to Scenario Start Date and less than or equal to Scenario End Date")]
[Display(Name = "Milestone Start Date")]
public DateTime? MilestoneStartDate { get; set; }
[Display(Name = "Growth Scenario?")]
public bool GrowthScenario { get; set; }
[Display(Name = "Projected Revenue")]
[DataType(DataType.Currency)]
[Range(0, 100000000000)]
public decimal? ProjectedRevenue { get; set; }
[Display(Name = "Projected Expense")]
[DataType(DataType.Currency)]
[Range(0, 100000000000)]
public decimal? TDDirectCosts { get; set; }
public decimal BUDirectCosts { get; set; }
public bool IsRevenueGenerating { get; set; }
[Display(Name = "Use L&M Margin?")]
public bool UseLMMargin { get; set; }
[Display(Name = "Gross Margin")]
[Range(0, Int32.MaxValue)]
public decimal? GrossMargin { get; set; }
[Display(Name = "L&M Margin")]
[Range(0, Int32.MaxValue)]
public decimal? LMMargin { get; set; }
[Display(Name = "Total Milestones")]
[Range(1, 2147483647)]
public int TotalMilestones { get; set; }
[Display(Name = "Labor Split Percentage")]
[Range(0, 100)]
[UIHint("Slider")]
public short LaborSplitPercentage { get; set; }
[Display(Name = "Freeze Resource")]
public bool FreezeResource { get; set; }
public IList<ExpenditureItem> Expenditures { get; set; }
public bool AsDraft { get; set; }
public ScenarioStatus? Status { get; set; }
#endregion
#region Constructors
public ScenarioModel()
{
Expenditures = new List<ExpenditureItem>();
IsActiveScenario = true;
UseActuals = true;
IsRevenueGenerating = true;
}
#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 ScenarioModel(Scenario obj)
{
if (obj == null)
return null;
var model = new ScenarioModel
{
Id = obj.Id,
Name = obj.Name,
Color = obj.Color,
TemplateId = ScenarioStatus.Draft.GetHashCode().Equals(obj.Status) ? null : obj.TemplateId,
ParentId = obj.ParentId ?? Guid.Empty,
Type = (ScenarioType)obj.Type,
ChildScenarios = obj.ChildScenarios.Count,
EndDate = obj.EndDate,
StartDate = obj.StartDate,
StartDateDisabled = obj.StartDate,
GrossMargin = obj.ExpectedGrossMargin.HasValue ? Convert.ToInt64(obj.ExpectedGrossMargin * 100) : (decimal?)null,
GrowthScenario = obj.GrowthScenario,
Status = (ScenarioStatus?)obj.Status,
IsActiveScenario = obj.Status == ScenarioStatus.Active.GetHashCode(),
LMMargin = obj.ExpectedGrossMargin_LM.HasValue ? Convert.ToInt64(obj.ExpectedGrossMargin_LM * 100) : (decimal?)null,
UseLMMargin = obj.UseLMMargin == 1,
LaborSplitPercentage = Convert.ToInt16(obj.CGSplit * 100),
MilestoneStartDate = obj.ShotStartDate,
ProjectedRevenue = obj.ProjectedRevenue,
TotalMilestones = obj.Shots ?? 0,
Expenditures = new List<ExpenditureItem>(),
FreezeResource = obj.FreezeRevenue,
IsRevenueGenerating = (obj.Project == null || obj.Project.Type == null || obj.Project.IsRevenueGenerating),
TDDirectCosts = obj.TDDirectCosts ?? 0,
BUDirectCosts = obj.BUDirectCosts ?? 0
};
model.TrimStringProperties();
return model;
}
public static explicit operator ScenarioModel(CreateScenarioModel obj)
{
if (obj == null)
return null;
var model = new ScenarioModel
{
Id = obj.ScenarioId,
Name = obj.ScenarioName,
Color = null,
TemplateId = obj.TemplateId,
ParentId = obj.PartId ?? obj.ProjectId,
Type = ScenarioType.Portfolio,
ChildScenarios = 0,
EndDate = obj.EndDate,
StartDate = obj.StartDate,
StartDateDisabled = obj.StartDate,
GrossMargin = obj.UseLMMargin ? (decimal?)null : obj.Margin,
GrowthScenario = obj.GrowthScenario,
Status = obj.CreateAsActive ? ScenarioStatus.Active : ScenarioStatus.Inactive,
IsActiveScenario = obj.CreateAsActive,
LMMargin = obj.UseLMMargin ? obj.Margin : (decimal?)null,
UseLMMargin = obj.UseLMMargin,
LaborSplitPercentage = 100,
MilestoneStartDate = obj.StartDate,
ProjectedRevenue = obj.IsRevenueGenerating ? obj.ProjectedRevenue_Expense : (decimal?)null,
TotalMilestones = 1,
Expenditures = obj.ScenarioExpenditures,
FreezeResource = false,
IsRevenueGenerating = obj.IsRevenueGenerating,
AsDraft = obj.SaveAsDraft,
TDDirectCosts = obj.IsRevenueGenerating ? (decimal?)null : obj.ProjectedRevenue_Expense,
BUDirectCosts = 0,
CostSavings = obj.CostSavings ?? 0,
CostSavingStartDate = obj.CostSavingStartDate,
CostSavingEndDate = obj.CostSavingEndDate,
CostSavingType = obj.CostSavingType
};
model.TrimStringProperties();
return model;
}
public bool CostSavingType { get; set; }
public DateTime? CostSavingEndDate { get; set; }
public DateTime? CostSavingStartDate { get; set; }
public decimal CostSavings { get; set; }
/// <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 ?? string.Empty).Replace("#", "");
dbObj.TemplateId = TemplateId;
dbObj.ParentId = ParentId;
dbObj.Type = Type.GetHashCode();
dbObj.EndDate = EndDate;
dbObj.StartDate = StartDate;
dbObj.GrowthScenario = GrowthScenario;
dbObj.Status = AsDraft ? (int)ScenarioStatus.Draft : IsActiveScenario ? (int)ScenarioStatus.Active : (int)ScenarioStatus.Inactive;
dbObj.ExpectedGrossMargin = GrossMargin.HasValue ? (decimal)(((double)GrossMargin) / 100.00) : (decimal?)null;
dbObj.ExpectedGrossMargin_LM = LMMargin.HasValue ? (decimal)(((double)LMMargin) / 100.00) : (decimal?)null;
dbObj.UseLMMargin = UseLMMargin ? 1 : 0;
dbObj.CGSplit = 1;
dbObj.EFXSplit = 0;
dbObj.ShotStartDate = MilestoneStartDate;
dbObj.ProjectedRevenue = ProjectedRevenue;
dbObj.Shots = TotalMilestones;
dbObj.FreezeRevenue = FreezeResource;
dbObj.TDDirectCosts = TDDirectCosts;
//dbObj.TDDirectCosts_LM = TDDirectCosts;
dbObj.BUDirectCosts = BUDirectCosts;
//dbObj.BUDirectCosts_LM = BUDirectCosts;
dbObj.CostSavings = CostSavings;
dbObj.CostSavingsStartDate = CostSavingStartDate;
dbObj.CostSavingsEndDate = CostSavingEndDate;
dbObj.CostSavingsType = Convert.ToInt16(CostSavingType ? 1 : 0);
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!AsDraft && Type != ScenarioType.Scheduling && ProjectedRevenue <= 0)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Projected Revenue"), new[] { "ProjectedRevenue" });
if (!AsDraft && IsRevenueGenerating && UseLMMargin && LMMargin == null)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "L&M Margin"), new[] { "LMMargin" });
if (!AsDraft && IsRevenueGenerating && !UseLMMargin && GrossMargin == null)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Gross Margin"), new[] { "GrossMargin" });
if (!Guid.Empty.Equals(Id) && !AsDraft && IsRevenueGenerating && ProjectedRevenue == null)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Projected Revenue"), new[] { "ProjectedRevenue" });
if (!AsDraft && (Guid.Empty.Equals(Id) || (!Guid.Empty.Equals(Id) && ScenarioStatus.Draft.Equals(Status))) && null == TemplateId)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Template"), new[] { "TemplateId" });
if (Guid.Empty.Equals(Id) && !AsDraft && IsRevenueGenerating && ProjectedRevenue == null && GrossMargin == 0)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED2, "Projected Revenue", "Gross Margin"), new[] { "ProjectedRevenue", "GrossMargin" });
}
#endregion
}
/// <summary>
/// An UI representation of scenario template to be displayed as list items
/// </summary>
public class ScenarioListItemModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int? Duration { get; set; }
public decimal? CGSplit { get; set; }
public decimal? EFXSplit { get; set; }
public int ScenariosCount { get; set; }
}
public class ScenarioTemplateCreationModel
{
public Guid Id { get; set; }
public string TemplateName { get; set; }
public IList<ScenarioModel.ExpenditureItem> Expenditures { get; set; }
public ScenarioTemplateCreationModel(ScenarioDetailModel model)
{
Id = model.Id;
TemplateName = model.Name + " Template";
Expenditures = model.Expenditures;
foreach (var exp in Expenditures)
exp.Checked = true;
}
public ScenarioTemplateCreationModel()
{
Id = Guid.NewGuid();
TemplateName = "Template";
Expenditures =new List<ScenarioModel.ExpenditureItem>();
}
}
public class ScenarioDetailsModel
{
public class ScenarioCalendarRowResource
{
public Guid Id { get; set; }
public Guid? ExpedentureCategoryId { get; set; }
public string Name { get; set; }
public decimal[] QuantityValues { get; set; }
public decimal[] CapacityQuantityValues { get; set; }
public decimal[] CostValues { get; set; }
public decimal GrandTotalCost { get; set; }
public decimal GrandTotalQuantity { get; set; }
public bool IsActiveEmployee { get; set; }
}
public class ScenarioCalendarRow
{
public Guid ExpCatId { get; set; }
public Guid? ResourceToAssignId { get; set; }
public string ExpCatName { get; set; }
public decimal GrandTotalCost { get; set; }
public decimal GrandTotalQuantity { get; set; }
public ExpenditureCategoryModel.UseTypes UseType { get; set; }
public bool Checked { get; set; }
public decimal[] QuantityValues { get; set; }
public decimal[] CostValues { get; set; }
public Guid?[] ScenarioDetailIds { get; set; }
public bool Collapsed { get; set; }
public string CollapsedClass { get; set; }
public decimal[] RestQuantity { get; set; }
public decimal[] RestCost { get; set; }
public List<ScenarioCalendarRowResource> Resources { get; set; }
public ScenarioCalendarRow()
{
QuantityValues = new decimal[0];
CostValues = new decimal[0];
ScenarioDetailIds = new Guid?[0];
CollapsedClass = "fa-plus-square";
RestQuantity = new decimal[0];
RestCost = new decimal[0];
Collapsed = true;
}
}
public class Header
{
/// <summary>
/// Gets or sets a number of milliseconds between month end date and 1/1/1970.
/// </summary>
public long Milliseconds { get; set; }
/// <summary>
/// Gets or sets an index of the related month cell.
/// </summary>
public int MonthIndex { get; set; }
/// <summary>
/// Gets or sets a values indicating whether the header cell is collapsed or not.
/// </summary>
public bool Collapsed { get; set; }
/// <summary>
/// Gets or sets a values indicating whether to show header cell.
/// </summary>
public bool Show { get; set; }
/// <summary>
/// Gets or sets a values indicating whether the header cell represents a month or a week.
/// </summary>
public bool IsMonth { get; set; }
/// <summary>
/// Gets or sets a CSS class used for the <i>plus/minus</i> icons in the month header.
/// </summary>
public string CollapsedClass { get; set; }
/// <summary>
/// Gets or sets a text of the header cell.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Gets or sets an array of indexes of children cells. I.e. week cells for month cell.
/// </summary>
public List<int> Weeks { get; set; }
public Header()
{
Collapsed = true;
Show = false;
CollapsedClass = "fa-plus-square";
}
}
[Display(Name = "Category Type")]
public ExpenditureCategoryModel.CategoryTypes? CategoryType { get; set; }
[Display(Name = "Labor / Materials")]
public ExpenditureCategoryModel.CgEfx? LaborMaterials { get; set; }
[Display(Name = "Income Type")]
public Guid? IncomeType { get; set; }
[Display(Name = "GL Name")]
public Guid? GLAccount { get; set; }
[Display(Name = "Cost Center")]
public Guid? CreditDepartment { get; set; }
[Display(Name = "Quantity Mode")]
public bool IsTableModeQuantity { get; set; }
/// <summary>
/// Indicates whether scenario details grid data is in Hours or in Resources.<br/>
/// null - data in Hours<br/>
/// true - data in Hours<br/>
/// false - data in Resources.
/// </summary>
[Display(Name = "Resources Mode")]
public bool? IsUOMHours { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public ScenarioType? ScenarioType { get; set; }
public Guid ScenarioId { get; set; }
public bool GrowthScenario { get; set; }
public Guid ParentId { get; set; }
public long ActualsEndDateMs { get; set; }
public List<SelectListItem> AvailableExpenditures { get; set; }
[Display(Name = "Expenditure Categories")]
public List<Guid> SelectedExpCats { get; set; }
public List<ScenarioCalendarRowResource> AllResources { get; set; }
/// <summary>
/// Gets or sets a list of scenario details data for each expenditure category.
/// </summary>
public List<ScenarioCalendarRow> ScenarioCalendar { get; set; }
public List<Header> Headers { get; set; }
public ScenarioDetailsModel()
{
AvailableExpenditures = new List<SelectListItem>();
SelectedExpCats = new List<Guid>();
ScenarioCalendar = new List<ScenarioCalendarRow>();
Headers = new List<Header>();
IsTableModeQuantity = true;
}
}
public class SaveScenarioDetailsChangesModel
{
public class ChangedRow
{
public Guid Id { get; set; }
public List<ChangedColumn> Values { get; set; }
public List<ResourcesRow> Resources { get; set; }
public ChangedRow()
{
Values = new List<ChangedColumn>();
}
}
public class ResourcesRow
{
public Guid Id { get; set; }
public List<ChangedColumn> Values { get; set; }
public bool IsAdded { get; set; }
public bool IsRemoved { get; set; }
}
public class ChangedColumn
{
public Guid? Id { get; set; }
/// <summary>
/// Number of milliseconds from 1/1/1970
/// </summary>
public long Milliseconds { get; set; }
public decimal Cost { get; set; }
public decimal Quantity { get; set; }
}
public Guid ScenarioId { get; set; }
public List<ChangedRow> ChangedExpCats { get; set; }
public ScenarioDetailsModel ScenarioFilters { get; set; }
public SaveScenarioDetailsChangesModel()
{
ChangedExpCats = new List<ChangedRow>();
}
}
public class UpdateScenarioModel
{
public Guid Id { get; set; }
[Display(Name = "Allow prior week values to be adjusted?")]
public bool AllowAdjustment { get; set; }
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Prior Week Cut-off")]
public DateTime? PriorWeekCutOff { get; set; }
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Scenario Start Date")]
public DateTime? StartDate { get; set; }
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Scenario End Date")]
public DateTime? EndDate { get; set; }
[Display(Name = "Projected Revenue")]
[DataType(DataType.Currency)]
[Range(1, 100000000000)]
public decimal? ProjectedRevenue { get; set; }
[Display(Name = "Projected Expense")]
[DataType(DataType.Currency)]
[Range(1, 100000000000)]
public decimal? TDDirectCosts { get; set; }
[Display(Name = "Use L&M Margin?")]
public bool UseLMMargin { get; set; }
[Display(Name = "Gross Margin")]
[Range(1, 100)]
public decimal? GrossMargin { get; set; }
[Display(Name = "L&M Margin")]
[Range(1, 100)]
public decimal? LMMargin { get; set; }
public bool IsRevenueGenerating { get; set; }
public decimal BUDirectCosts { get; set; }
public IList<ScenarioModel.ExpenditureItem> Expenditures { get; set; }
}
[Obsolete]
public class DateRangeAttribute : ValidationAttribute
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public DateRangeAttribute()
{
StartDate = DateTime.MaxValue;
EndDate = DateTime.MaxValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
DateTime date = DateTime.Parse(value.ToString()); // assuming it's in a parsable string format
var propertyStartDate = validationContext.ObjectType.GetProperty("StartDate");
var propertyStartDateDisabled = validationContext.ObjectType.GetProperty("StartDateDisabled");
var propertyEndDate = validationContext.ObjectType.GetProperty("EndDate");
if (propertyStartDate != null && propertyEndDate != null && propertyStartDateDisabled != null)
{
var startDateValue = propertyStartDate.GetValue(validationContext.ObjectInstance, null);
var startDateDisabledValue = propertyStartDateDisabled.GetValue(validationContext.ObjectInstance, null);
var endDateValue = propertyEndDate.GetValue(validationContext.ObjectInstance, null);
if ((startDateValue != null || startDateDisabledValue != null) && endDateValue != null)
{
StartDate = DateTime.Parse((startDateValue == null ? startDateDisabledValue : startDateValue).ToString());
EndDate = DateTime.Parse(endDateValue.ToString());
if (StartDate <= EndDate && date >= StartDate && date <= EndDate)
return ValidationResult.Success;
}
propertyStartDate.SetValue(validationContext.ObjectInstance, startDateDisabledValue);
}
return new ValidationResult(ErrorMessageString);
}
return ValidationResult.Success;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DateNotExceedPropValueAttribute : ValidationAttribute, IClientValidatable
{
string otherPropertyName;
public DateNotExceedPropValueAttribute(string otherPropertyName, string errorMessage) : base(errorMessage)
{
this.otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
try
{
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
if (otherPropertyInfo.PropertyType == new DateTime().GetType())
{
DateTime toValidate = value != null ? (DateTime)value : DateTime.MinValue;
DateTime referenceProperty = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (toValidate > referenceProperty)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
else if (otherPropertyInfo.PropertyType.Equals(typeof(DateTime?)))
{
DateTime toValidate = value != null ? (DateTime)value : DateTime.MinValue;
object referenceProperty = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (referenceProperty != null && toValidate > (DateTime)referenceProperty)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
else
{
validationResult = new ValidationResult("An error occurred while validating the property. OtherProperty is not of type DateTime");
}
}
catch (Exception ex)
{
throw ex;
}
return validationResult;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string errorMessage = ErrorMessageString;
ModelClientValidationRule dateNotExceedPropRule = new ModelClientValidationRule();
dateNotExceedPropRule.ErrorMessage = errorMessage;
dateNotExceedPropRule.ValidationType = "datenotexceedprop"; // This is the name the jQuery adapter will use
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
dateNotExceedPropRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);
yield return dateNotExceedPropRule;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DateGreaterThanOrEqualAttribute : ValidationAttribute, IClientValidatable
{
string otherPropertyName;
public DateGreaterThanOrEqualAttribute(string otherPropertyName, string errorMessage)
: base(errorMessage)
{
this.otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
try
{
// Using reflection we can get a reference to the other date property, in this example the project start date
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
// Let's check that otherProperty is of type DateTime as we expect it to be
if (otherPropertyInfo.PropertyType == new DateTime().GetType())
{
DateTime toValidate = value != null ? (DateTime)value : DateTime.MinValue;
DateTime referenceProperty = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (referenceProperty > toValidate)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
else if (otherPropertyInfo.PropertyType.Equals(typeof(DateTime?)))
{
DateTime toValidate = value != null ? (DateTime)value : DateTime.MinValue;
object referenceProperty = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
DateTime referenceDT = referenceProperty != null ? (DateTime)referenceProperty : DateTime.MinValue;
if (referenceDT > toValidate)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
else
{
validationResult = new ValidationResult("An error occurred while validating the property. OtherProperty is not of type DateTime");
}
}
catch (Exception ex)
{
throw ex;
}
return validationResult;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string errorMessage = ErrorMessageString;
ModelClientValidationRule dateGreaterThanOrEqualRule = new ModelClientValidationRule();
dateGreaterThanOrEqualRule.ErrorMessage = errorMessage;
dateGreaterThanOrEqualRule.ValidationType = "dategreaterthanorequal"; // This is the name the jQuery adapter will use
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
dateGreaterThanOrEqualRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);
yield return dateGreaterThanOrEqualRule;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class EitherOfRequiredAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherPropertyName;
private string _singleErorrMessage;
public EitherOfRequiredAttribute(string otherPropertyName, string errorMessage, string singleErrorMessage)
: base(errorMessage)
{
this._otherPropertyName = otherPropertyName;
this._singleErorrMessage = singleErrorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
try
{
// Using reflection we can get a reference to the other date property, in this example the project start date
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this._otherPropertyName);
// Let's check that otherProperty is of type DateTime as we expect it to be
if (otherPropertyInfo.PropertyType == (typeof(String)))
{
string referenceProperty = (string)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
string toValidate = value != null ? (string)value : string.Empty;
if (string.IsNullOrEmpty(referenceProperty) && string.IsNullOrEmpty(toValidate))
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
else
{
object referenceProperty = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (referenceProperty == null && value == null)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
}
catch (Exception ex)
{
throw ex;
}
return validationResult;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string errorMessage = ErrorMessageString;
ModelClientValidationRule eitherOfRequiredRule = new ModelClientValidationRule();
eitherOfRequiredRule.ErrorMessage = errorMessage;
eitherOfRequiredRule.ValidationType = "eitherofrequired"; // This is the name the jQuery adapter will use
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
eitherOfRequiredRule.ValidationParameters.Add("otherpropertyname", _otherPropertyName);
eitherOfRequiredRule.ValidationParameters.Add("singleerrormessage", _singleErorrMessage);
yield return eitherOfRequiredRule;
}
}
public class CreateScenarioModel : IValidatableObject
{
public Guid ScenarioId { get; set; }
//[UIHint("Project")]
[Display(Name = "Project Name")]
public Guid ProjectId { get; set; }
//[Display(Name = "Part Name")]
//public string PartName { get; set; }
[Display(Name = "Part Name")]
public Guid? PartId { get; set; }
[Required]
[Display(Name = "Scenario Name")]
public string ScenarioName { get; set; }
[Required]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Scenario Start Date")]
public DateTime? StartDate { get; set; }
[Required]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[DateNotExceedPropValue("ProjectDeadline", "Scenario End Date should not exceed Project Deadline date")]
[DateGreaterThanOrEqualAttribute("StartDate", "Scenario End Date should not be less than Start Date")]
[Display(Name = "Scenario End Date")]
public DateTime? EndDate { get; set; }
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Cost Saving Start Date")]
public DateTime? CostSavingStartDate { get; set; }
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:MM/dd/yy}", ApplyFormatInEditMode = true)]
[Display(Name = "Cost Saving End Date")]
public DateTime? CostSavingEndDate { get; set; }
[Required]
[Display(Name = "Template")]
public Guid? TemplateId { get; set; }
[Display(Name = "Create As Active")]
public bool CreateAsActive { get; set; }
[Display(Name = "Save as Draft")]
public bool SaveAsDraft { get; set; }
[Display(Name = "Cost Saving Type")]
public bool CostSavingType { get; set; }
[Display(Name = "Growth Scenario")]
public bool GrowthScenario { get; set; }
[Display(Name = "Project")]
public Project Project { get; set; }
public IList<ScenarioModel.ExpenditureItem> ScenarioExpenditures { get; set; }
[DataType(DataType.Currency)]
[Range(0, 100000000000)]
[Display(Name = "Projected Revenue/Expense")]
[EitherOfRequired("Margin", "Either Revenue/Expense or Margin should be entered", "Projected Revenue/Expense field is required")]
public decimal? ProjectedRevenue_Expense { get; set; }
[Display(Name = "Cost Savings")]
public decimal? CostSavings { get; set; }
[Display(Name = "Margin")]
[Range(0, Int32.MaxValue)]
public decimal? Margin { get; set; }
public bool UseLMMargin { get; set; }
public bool IsRevenueGenerating { get; set; }
public string TeamAllocations { get; set; }
public DateTime? ProjectDeadline { get; set; }
public string CostSavingItems { get; set; }
public bool CostSavingsPanelExpanded { get; set; }
#region Constructors
public CreateScenarioModel()
{
CreateAsActive = true;
}
#endregion
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (CostSavingsPanelExpanded)
{
if (!CostSavings.HasValue)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Cost Savings"), new[] { "CostSavings" });
if (!CostSavingStartDate.HasValue)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Cost Savings Start Date"), new[] { "CostSavingStartDate" });
if (!CostSavingEndDate.HasValue)
yield return new ValidationResult(string.Format(Constants.ERROR_TEMPLATE_REQUIRED, "Cost Savings End Date"), new[] { "CostSavingEndDate" });
}
}
}
public class ScenarioCostSavingModel
{
public int Year { get; set; }
public decimal?[] Costs { get; set; }
}
}