EnVisageOnline/Main/Source/EnVisage/Models/ScenarioDetailSnapshotModel.cs

691 lines
28 KiB
C#

using EnVisage.Code;
using System;
using System.Collections.Generic;
using System.Linq;
using EnVisage.Code.DAL.Mongo;
using EnVisage.Code.BLL;
using EnVisage.Models.ProjectDependencies;
namespace EnVisage.Models
{
public class ScenarioDetailSnapshotModelBase
{
public ScenarioInfoModel Scenario { get; set; }
public List<ExpenditureModel> AvailableExpenditures { get; set; }
public List<ScenarioCalendarRateModel> Rates { get; set; }
public List<TeamInScenarioModel> TeamsInScenario { get; set; }
public bool NeedToRebind { get; set; }
//8.24.16 start tab
public long filterStartDate { get; set; }
public long filterEndDate { get; set; }
//8.24.16 end
public bool NeedToRecalculateScenarioDetails { get; set; }
public bool LocalRatesLoaded { get; set; }
public List<WorkFlowCommand> WorkFlowActions { get; set; }
public List<WorkFlowState> WorkFlowStates { get; set; }
public string WorkFlowSchema { get; set; }
public ScenarioDetailSnapshotModelBase()
{
AvailableExpenditures = new List<ExpenditureModel>();
Rates = new List<ScenarioCalendarRateModel>();
TeamsInScenario = new List<TeamInScenarioModel>();
WorkFlowActions = new List<WorkFlowCommand>();
}
}
public class ScenarioDetailSnapshotModel : ScenarioDetailSnapshotModelBase
{
public List<ScenarioExpenditureModel> Expenditures2Add { get; set; }
public ScenarioCalendarModel Calendar { get; set; }
public List<NoteModel> Notes { get; set; }
public ScenarioDetailSnapshotModel()
: base()
{
}
}
public class ScenarioDetailSnapshotRecalculationModel : ScenarioDetailSnapshotModelBase
{
public ScenarioCalendarModelBase Calendar { get; set; }
}
public class ScenarioDetailsSnapshotSaveModel
{
public ScenarioInfoModel Scenario { get; set; }
public ScenarioCalendarModelBase Calendar { get; set; }
public List<ExpenditureModel> AvailableExpenditures { get; set; }
public List<TeamInScenarioModel> TeamsInScenario { get; set; }
public List<NoteModel> Notes { get; set; }
public List<NoteModel> NotesToDelete { get; set; }
public List<WorkFlowCommand> WorkFlowActions { get; set; }
public List<WorkFlowState> WorkFlowStates { get; set; }
public string WorkFlowSchema { get; set; }
public DependencyResolveModel Dependencies { get; set; }
public ExpenditureCategoryChangesHistory Audit { get; set; }
public ScenarioDetailsSnapshotSaveModel()
{
AvailableExpenditures = new List<ExpenditureModel>();
TeamsInScenario = new List<TeamInScenarioModel>();
WorkFlowActions = new List<WorkFlowCommand>();
Audit = new ExpenditureCategoryChangesHistory();
}
public ScenarioDetailsSnapshotSaveModel(ScenarioCalendarMixModel model)
: this()
{
if (model == null)
return;
Scenario = new ScenarioInfoModel(model);
Calendar = new ScenarioCalendarModelBase()
{
Expenditures = model.Expenditures
};
AvailableExpenditures = model.Expenditures.Select(x => new ExpenditureModel()
{
Id = x.Value.ExpenditureCategoryId,
Name = x.Value.ExpenditureCategoryName
}).Distinct().ToList();
//todo: copy dependencies here
}
}
#region Model Classes
public class ScenarioInfoModel
{
public Guid? Id { get; set; }
public string Name { get; set; }
public Guid? ParentId { get; set; }
public long? ProjectDeadline { get; set; }
public Guid? OldTemplateId { get; set; }
public Guid TemplateId { get; set; }
public long StartDate { get; set; }
public long EndDate { get; set; }
public decimal? ProjectedRevenue { get; set; }
public long ActualStartDate { get; set; }
public long ActualEndDate { get; set; }
public bool GrowthScenario { get; set; }
public ScenarioType Type { get; set; }
public bool UseLMMargin { get; set; }
public decimal? GrossMargin { get; set; }
public decimal? LMMargin { get; set; }
public decimal? GreenIndicator { get; set; }
public decimal? YellowIndicator { get; set; }
public decimal? RedIndicator { get; set; }
public int Duration { get; set; }
public decimal? TDDirectCosts { get; set; }
public decimal CGSplit { get; set; }
public decimal EFXSplit { get; set; }
public bool FreezeRevenue { get; set; }
public int Shots { get; set; }
public bool IsActiveScenario { get; set; }
public long? DateForStartOfChanges { get; set; }
public CostSavingSnapshotModel CostSavings { get; set; }
public SaveAsScenario? SaveAs { get; set; }
public bool SaveAsDraft { get; set; }
public bool NeedToAdjustMargin { get; set; }
public AdjustScenarioDetailsDistribution.DistributionType DistributionType { get; set; }
public bool IsBottomUp { get; set; }
public List<UDFValueCollection> UserDefinedFields { get; set; }
public bool? AdjustMarginRased { get; set; }
public long? DateForStartOfChangesOld { get; set; }
#region Constructors
public ScenarioInfoModel()
{
}
public ScenarioInfoModel(ScenarioCalendarMixModel model)
: this()
{
if (model == null)
return;
Id = model.Id;
ParentId = model.ParentId;
TemplateId = model.TemplateId;
Name = model.Name;
StartDate = model.StartDate;
EndDate = model.EndDate;
GrowthScenario = model.GrowthScenario;
Type = model.Type;
Duration = model.Duration;
IsActiveScenario = true;
SaveAs = SaveAsScenario.New;
SaveAsDraft = false;
NeedToAdjustMargin = false;
IsBottomUp = model.IsBottomUp;
if (model.FinInfo != null)
{
ProjectedRevenue = model.FinInfo.ProjectedRevenue;
UseLMMargin = model.FinInfo.UseLMMargin;
GrossMargin = model.FinInfo.GrossMargin;
LMMargin = model.FinInfo.LMMargin;
CGSplit = model.FinInfo.LaborSplitPercentage / (decimal)100.0;
EFXSplit = model.FinInfo.EFXSplit;
TDDirectCosts = model.FinInfo.TDDirectCosts;
CostSavings = model.FinInfo.CostSaving;
}
}
#endregion
}
public class ScenarioCalendarFilterModel
{
/// <summary>String representation for <see cref="ExpenditureCategoryModel.CategoryTypes"/></summary>
public string CategoryType { get; set; }
/// <summary>String representation for <see cref="ExpenditureCategoryModel.CgEfx"/></summary>
public string LaborMaterials { get; set; }
public Guid? IncomeType { get; set; }
public Guid? GLAccount { get; set; }
public Guid? CreditDepartment { get; set; }
public IEnumerable<Guid> SelectedExpCats { get; set; }
public bool IsTableModeQuantity { get; set; }
public bool IsViewModeMonth { get; set; }
public bool ShowActuals { get; set; }
public bool IsUOMHours { get; set; }
public bool ShowAvgTotals { get; set; }
}
public class ScenarioCalendarRateModel
{
public Guid expCatId { get; set; }
public List<RateValueModel> rateValues { get; set; }
}
public class RateValueModel
{
public long weekStartDate { get; set; }
public long weekEndDate { get; set; }
public decimal rateValue { get; set; }
}
public class CollapsableModelBase
{
public bool Collapsed { get; set; }
public string CollapsedClass { get; set; }
public CollapsableModelBase()
{
Collapsed = true;
CollapsedClass = "fa-plus-square";
}
}
public class ScenarioDetailsItem
{
public Guid? ForecastId { get; set; }
public Guid? ActualsId { get; set; }
public decimal? ForecastQuantity { get; set; }
public decimal? ActualsQuantity { get; set; }
public decimal? ForecastCost { get; set; }
public decimal? ActualsCost { get; set; }
public int WeekOrdinal { get; set; }
public bool Changed { get; set; }
}
public class ExpenditureDetail : CollapsableModelBase
{
public Guid ExpenditureCategoryId { get; set; }
public string ExpenditureCategoryName { get; set; }
public int Type { get; set; }
public int UseType { get; set; } //default is 1
public string CGEFX { get; set; }
public Guid GLId { get; set; }
public Guid UOMId { get; set; }
public Guid CreditId { get; set; }
public Guid? SystemAttributeOne { get; set; }
public Guid? SystemAttributeTwo { get; set; }
public int? SortOrder { get; set; }
public decimal UOMValue { get; set; }
public Dictionary<string, ScenarioDetailsItem> Details { get; set; }
public Dictionary<string, ExpenditureDetailsTeam> Teams { get; set; }
public bool AllowResourceAssignment { get; set; }
/// <summary>
/// Added for RMO to store EC unassigned to any team values
/// </summary>
public Dictionary<string, decimal> UnassignedAllocations { get; set; }
public ExpenditureDetail()
: base()
{
Details = new Dictionary<string, ScenarioDetailsItem>();
Teams = new Dictionary<string, ExpenditureDetailsTeam>();
UnassignedAllocations = new Dictionary<string, decimal>();
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (!(obj is ExpenditureDetail))
return false;
return ExpenditureCategoryId == (obj as ExpenditureDetail).ExpenditureCategoryId;
}
public override int GetHashCode()
{
return ExpenditureCategoryId.GetHashCode();
}
public static explicit operator ExpenditureDetail(MixExpenditureAllocation obj)
{
var model = new ExpenditureDetail
{
CGEFX = obj.CGEFX,
CreditId = obj.CreditId,
ExpenditureCategoryId = obj.ExpCatId,
ExpenditureCategoryName = obj.ExpenditureCategoryName,
GLId = obj.GLId,
SortOrder = obj.SortOrder,
SystemAttributeOne = obj.SysAttrOne,
SystemAttributeTwo = obj.SysAttrTwo,
Teams = new Dictionary<string, ExpenditureDetailsTeam>(),
Type = obj.Type,
UOMId = obj.UOMId,
UOMValue = obj.UOMValue,
UseType = obj.UseType,
AllowResourceAssignment = obj.AllowResourceAssignment,
Details = obj.Allocations.Where(x => x.Value != null).Select((pair, i) => new { pair.Key, pair.Value, index = i }).ToDictionary(key => key.Key, value => new ScenarioDetailsItem
{
ForecastId = Guid.NewGuid(),
ForecastQuantity = value.Value.Quantity,
ForecastCost = value.Value.Cost,
WeekOrdinal = value.index + 1
})
};
if ((obj.UnassignedAllocations != null) && (obj.UnassignedAllocations.Keys.Count > 0))
{
model.UnassignedAllocations = obj.UnassignedAllocations.Select(item =>
new KeyValuePair<string, decimal>(item.Key, item.Value))
.ToDictionary(i1 => i1.Key, i2 => i2.Value);
}
return model;
}
public void CopyTo(MixExpenditureAllocation expenditureAllocation)
{
expenditureAllocation.Allocations =
Details.Select(pair => new { pair.Key, pair.Value.ForecastQuantity, pair.Value.ForecastCost })
.ToDictionary(key => key.Key, value => new QuantityItem()
{
Quantity = value.ForecastQuantity ?? 0,
Cost = value.ForecastCost ?? 0
});
expenditureAllocation.CGEFX = CGEFX;
expenditureAllocation.CreditId = CreditId;
expenditureAllocation.ExpCatId = ExpenditureCategoryId;
expenditureAllocation.ExpenditureCategoryName = ExpenditureCategoryName;
expenditureAllocation.GLId = GLId;
expenditureAllocation.SortOrder = SortOrder;
expenditureAllocation.SysAttrOne = SystemAttributeOne;
expenditureAllocation.SysAttrTwo = SystemAttributeTwo;
expenditureAllocation.Type = Type;
expenditureAllocation.UOMId = UOMId;
expenditureAllocation.UOMValue = UOMValue;
expenditureAllocation.UseType = UseType;
expenditureAllocation.AllowResourceAssignment = AllowResourceAssignment;
if ((UnassignedAllocations != null) && (UnassignedAllocations.Keys.Count > 0))
{
expenditureAllocation.UnassignedAllocations = UnassignedAllocations.Select(item =>
new KeyValuePair<string, decimal>(item.Key, item.Value))
.ToDictionary(i1 => i1.Key, i2 => i2.Value);
}
}
}
public class ExpenditureDetailsTeam : CollapsableModelBase
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsAccessible { get; set; }
/// <summary>Current allocation for scenario in certain EC</summary>
public Dictionary<string, decimal> QuantityValues { get; set; }
/// <summary>Quantity available for allocation in certain EC</summary>
public Dictionary<string, decimal> RestQuantityValues { get; set; }
/// <summary>All team capacity in certain EC</summary>
public Dictionary<string, decimal> CapacityQuantityValues { get; set; }
/// <summary>All resources from current team allocated in certain EC</summary>
public Dictionary<string, TeamResource> Resources { get; set; }
/// <summary>All resources from current team available for allocation in certain EC</summary>
public Dictionary<string, TeamResource> AllResources { get; set; }
public bool Changed { get; set; }
public bool CanBeDeleted { get; set; }
public ExpenditureDetailsTeam()
: base()
{
AllResources = new Dictionary<string, TeamResource>();
Resources = new Dictionary<string, TeamResource>();
QuantityValues = new Dictionary<string, decimal>();
RestQuantityValues = new Dictionary<string, decimal>();
CapacityQuantityValues = new Dictionary<string, decimal>();
}
public static explicit operator ExpenditureDetailsTeam(MixTeamAllocation obj)
{
var model = new ExpenditureDetailsTeam
{
Id = Guid.Parse(obj.TeamId),
Name = obj.Name,
QuantityValues = obj.Allocations,
AllResources = (obj.AllResources ?? new Dictionary<string, MixResourceAllocation>()).Select(t => (TeamResource)t.Value).ToDictionary(key => key.Id.ToString())
};
return model;
}
public void CopyTo(MixTeamAllocation teamAllocation)
{
teamAllocation.Name = Name;
teamAllocation.TeamId = Id.ToString();
teamAllocation.Allocations = QuantityValues;
teamAllocation.AllResources = (AllResources ?? new Dictionary<string, TeamResource>()).Select(t =>
{
var alloc = new MixResourceAllocation();
t.Value.CopyTo(alloc);
return alloc;
}).ToDictionary(key => key.ResourceId);
}
}
/// <summary>
/// Represents info about the resource within one of the teams.
/// </summary>
public class TeamResource
{
public Guid Id { get; set; }
public Guid ExpenditureCategoryId { get; set; }
public string Name { get; set; }
public bool IsActiveEmployee { get; set; }
public Dictionary<string, decimal> CapacityQuantityValues { get; set; }
public Dictionary<string, decimal> QuantityValues { get; set; }
public Dictionary<string, decimal> RestQuantityValues { get; set; }
public Dictionary<string, bool> ReadOnly { get; set; }
public bool Changed { get; set; }
public bool Deleted { get; set; }
/// <summary>
/// Gets or sets a value indicating start of work (milliseconds from Unix date) of the specified resource in the specified team.
/// </summary>
public long StartDate { get; set; }
/// <summary>
/// Gets or sets a value indicating end of work (milliseconds from Unix date) of the specified resource in the specified team.
/// </summary>
public long? EndDate { get; set; }
/// <summary>
/// Gets or sets a value indicating percent of involvment of the resource within the specified team.
/// </summary>
/// <remarks>
/// Min value = 0. Max value = 1. Value=0.75 means 75%.
/// </remarks>
public decimal Allocation { get; set; }
public TeamResource()
{
CapacityQuantityValues = new Dictionary<string, decimal>();
QuantityValues = new Dictionary<string, decimal>();
RestQuantityValues = new Dictionary<string, decimal>();
ReadOnly = new Dictionary<string, bool>();
}
public static explicit operator TeamResource(MixResourceAllocation obj)
{
var model = new TeamResource
{
Id = Guid.Parse(obj.ResourceId),
ExpenditureCategoryId = obj.ExpCatId,
Name = obj.Name,
QuantityValues = obj.Allocations
};
return model;
}
public void CopyTo(MixResourceAllocation allocation)
{
allocation.Name = Name;
allocation.ResourceId = Id.ToString();
allocation.Allocations = QuantityValues;
}
}
public class TeamResourceSortable : TeamResource
{
public string LastName { get; set; }
public List<TeamMembershipModel> Teams { get; set; }
public TeamResourceSortable() : base()
{
Teams = new List<TeamMembershipModel>();
}
}
public class ScenarioCalendarModelBase
{
public Dictionary<string, ExpenditureDetail> Expenditures { get; set; }
public List<VW_ScenarioAndProxyDetails> GetScenarioDetails(bool needForecast)
{
var scenarioDetails = new List<VW_ScenarioAndProxyDetails>();
if (Expenditures != null && Expenditures.Count > 0)
{
// we have to create new items only for those dates where scenario has forecast data
// e.g.:
// actuals: 11.09.2015, 18.09.2015
// forecast: 25.09.2015, 02.10.2015, 09.10.2015
// so we have to create new forecast items for categories which exist only in actuals only for 3 dates which correspond to forecast data
var forecastDates = Expenditures.SelectMany(x => x.Value.Details)
.Where(x => x.Value?.ForecastId != null && x.Value.ForecastId != Guid.Empty)
.Select(x => x.Key).Distinct().ToList();
foreach (var ec in Expenditures)
{
foreach (var detail in ec.Value.Details)
{
if (needForecast && (!detail.Value.ForecastId.HasValue || Guid.Empty.Equals(detail.Value.ForecastId)))
{
// this condition must be here because if current week corresponds to left or right side of actuals (befor or after forecast)
// we do not need to create forecast items for these dates
if (forecastDates.Contains(detail.Key))
{
// SA. ENV-667. Create new dataitem
scenarioDetails.Add(new VW_ScenarioAndProxyDetails
{
Id = Guid.NewGuid(),
ExpenditureCategoryId = ec.Value.ExpenditureCategoryId,
ExpenditureCategoryName = String.Empty, // SA. ENV-839
ExpCategoryWithCcName = ec.Value.ExpenditureCategoryName, // SA. ENV-839
WeekEndingDate = Utils.ConvertFromUnixDate(long.Parse(detail.Key)),
WeekOrdinal = detail.Value.WeekOrdinal,
Quantity = detail.Value.ForecastQuantity ?? 0,
Cost = detail.Value.ForecastCost ?? 0,
Type = ec.Value.Type,
UseType = ec.Value.UseType,
CGEFX = ec.Value.CGEFX,
GLId = ec.Value.GLId,
UOMId = ec.Value.UOMId,
CreditId = ec.Value.CreditId,
SystemAttributeOne = ec.Value.SystemAttributeOne,
SystemAttributeTwo = ec.Value.SystemAttributeTwo,
SortOrder = ec.Value.SortOrder,
AllowResourceAssignment = ec.Value.AllowResourceAssignment
});
}
continue;
}
if (!needForecast && !detail.Value.ActualsId.HasValue)
continue;
scenarioDetails.Add(new VW_ScenarioAndProxyDetails
{
Id = needForecast ? detail.Value.ForecastId.Value : detail.Value.ActualsId.Value,
ExpenditureCategoryId = ec.Value.ExpenditureCategoryId,
ExpenditureCategoryName = String.Empty, // SA. ENV-839
ExpCategoryWithCcName = ec.Value.ExpenditureCategoryName, // SA. ENV-839
WeekEndingDate = Utils.ConvertFromUnixDate(long.Parse(detail.Key)),
WeekOrdinal = detail.Value.WeekOrdinal,
Quantity = needForecast ? detail.Value.ForecastQuantity : detail.Value.ActualsQuantity,
Cost = needForecast ? detail.Value.ForecastCost : detail.Value.ActualsCost,
Type = ec.Value.Type,
UseType = ec.Value.UseType,
CGEFX = ec.Value.CGEFX,
GLId = ec.Value.GLId,
UOMId = ec.Value.UOMId,
CreditId = ec.Value.CreditId,
SystemAttributeOne = ec.Value.SystemAttributeOne,
SystemAttributeTwo = ec.Value.SystemAttributeTwo,
SortOrder = ec.Value.SortOrder,
AllowResourceAssignment = ec.Value.AllowResourceAssignment
});
}
}
}
return scenarioDetails;
}
public Dictionary<string, Dictionary<string, ExpenditureDetailsTeam>> GetTeams()
{
if (Expenditures == null || Expenditures.Count <= 0)
return new Dictionary<string, Dictionary<string, ExpenditureDetailsTeam>>();
return Expenditures.Select(x => new
{
ExpenditureId = x.Key,
x.Value.Teams
}).ToDictionary(x => x.ExpenditureId, g => g.Teams);
}
public ScenarioCalendarModelBase()
{
Expenditures = new Dictionary<string, ExpenditureDetail>();
}
}
public class ScenarioCalendarModel : ScenarioCalendarModelBase
{
public Dictionary<string, List<ScenarioDetailsModel.HeaderBase>> Headers { get; set; }
public ScenarioCalendarModel()
: base()
{
Headers = new Dictionary<string, List<ScenarioDetailsModel.HeaderBase>>();
}
}
public class CostSavingSnapshotModel
{
public decimal? CostSavings { get; set; }
public long? CostSavingStartDate { get; set; }
public long? CostSavingEndDate { get; set; }
public bool CostSavingType { get; set; }
public string CostSavingDescription { get; set; }
public List<ScenarioCostSavingModel> CostSavingItems { get; set; }
public CostSavingSnapshotModel()
{
CostSavingItems = new List<ScenarioCostSavingModel>();
}
}
public class TeamInScenarioModel
{
public Guid TeamId { get; set; }
public short Allocation { get; set; }
public bool IsNew { get; set; }
public bool IsAdd { get; set; }
public bool IsAccessible { get; set; }
public bool CanBeDeleted { get; set; }
}
/// <summary>
/// Information about versions of ralated item
/// </summary>
public class ItemVersionInfo
{
public ItemVersionInfo()
{
RmoVersion = 1;
ChangedInMain = false;
ChangedInRmo = false;
}
public ulong? SourceVersion { get; set; }
public ulong RmoVersion { get; set; }
public bool ChangedInMain { get; set; }
public bool ChangedInRmo { get; set; }
}
public class ScenarioCalendarSelectItemTeamModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Guid> Expenditures { get; set; }
#region Constructors
public ScenarioCalendarSelectItemTeamModel()
{
Expenditures = new List<Guid>();
}
#endregion
}
public class ExpenditureCategoryChangeCurveModel
{
public Guid FromExpenditureId { get; set; }
public Guid ToExpenditureId { get; set; }
}
public class ExpenditureCategoryReallocateResourceModel
{
public Guid SourceExpCat { get; set; }
public Guid TargetExpCat { get; set; }
public long SourceWeekEnding { get; set; }
public long TargetWeekEnding { get; set; }
public short Percentage { get; set; }
public string ReallocateBy { get; set; }
public Guid UseCurveFromExpenditureId { get; set; }
}
public class ExpenditureCategoryRoundModel
{
public List<string> ExpCats { get; set; }
public long StartDate { get; set; }
public long EndDate { get; set; }
public string DecimalPlaces { get; set; }
}
public class ExpenditureCategoryChangesHistory
{
public Dictionary<string, ExpenditureCategoryChangeCurveModel> CurveChanges { get; set; }
public Dictionary<string, ExpenditureCategoryReallocateResourceModel> Reallocations { get; set; }
public Dictionary<string, ExpenditureCategoryRoundModel> Roundings { get; set; }
public ExpenditureCategoryChangesHistory()
{
CurveChanges = new Dictionary<string, ExpenditureCategoryChangeCurveModel>();
Reallocations = new Dictionary<string, ExpenditureCategoryReallocateResourceModel>();
Roundings = new Dictionary<string, ExpenditureCategoryRoundModel>();
}
}
#endregion
}