419 lines
17 KiB
C#
419 lines
17 KiB
C#
using System;
|
|
using System.Activities.Expressions;
|
|
using System.Collections.Generic;
|
|
using System.Data.Entity;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using EnVisage.Models;
|
|
using Prevu.Core.Audit.Model;
|
|
using Resources;
|
|
using static EnVisage.Code.AuditProxy;
|
|
|
|
namespace EnVisage.Code.BLL
|
|
{
|
|
public class RateManager : ManagerBase<Rate, RateModel>
|
|
{
|
|
public RateManager() : base(null)
|
|
{
|
|
|
|
}
|
|
|
|
public RateManager(EnVisageEntities dbContext) : base(dbContext)
|
|
{
|
|
|
|
}
|
|
|
|
protected override Rate InitInstance()
|
|
{
|
|
return new Rate { Id = Guid.NewGuid() };
|
|
}
|
|
|
|
protected override Rate RetrieveReadOnlyById(Guid key)
|
|
{
|
|
return DataTable.AsNoTracking().FirstOrDefault(t => t.Id == key);
|
|
}
|
|
|
|
public override DbSet<Rate> DataTable => DbContext.Rates;
|
|
|
|
#region Public Methods
|
|
|
|
public void Save(RateModel model, Guid? userid)
|
|
{
|
|
#if DEBUG
|
|
var watch1 = new System.Diagnostics.Stopwatch();
|
|
watch1.Start();
|
|
Logger.Debug("Start EditRate Save");
|
|
#endif
|
|
|
|
var isNew = false;
|
|
Rate oldRate = null;
|
|
if (model == null)
|
|
throw new ArgumentNullException(nameof(model));
|
|
|
|
#region Save Rate data
|
|
Rate dbObj = null;
|
|
if (model.Id != Guid.Empty)
|
|
{
|
|
dbObj = DataTable.Find(model.Id);
|
|
}
|
|
if (dbObj == null)
|
|
{
|
|
isNew = true;
|
|
dbObj = InitInstance();
|
|
}
|
|
|
|
if (isNew)
|
|
{
|
|
model.CopyTo(dbObj);
|
|
DbContext.Rates.Add(dbObj);
|
|
}
|
|
else
|
|
{
|
|
oldRate = Load(dbObj.Id);
|
|
model.CopyTo(dbObj);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#if DEBUG
|
|
watch1.Stop();
|
|
System.Diagnostics.Debug.WriteLine($"EditRate Save end save has taken {watch1.ElapsedMilliseconds} ms");
|
|
Logger.Debug($"EditRate Save end save All {watch1.ElapsedMilliseconds} ms");
|
|
#endif
|
|
|
|
#region Update Related Scenarios
|
|
//var scenarioManager = new ScenarioManager(_dbContext);
|
|
//scenarioManager.ApplyRateAndRecalculateScenarios(dbObj);
|
|
//if (model.Type == RateModel.RateType.Global)
|
|
// scenarioManager.RecalculateCapacityScenariosRates(dbObj);
|
|
|
|
ThreadedProcessing.BackgroundProcessManager bpm = new ThreadedProcessing.BackgroundProcessManager(userid.Value);
|
|
bpm.RateScenarioUpdatesAsync(model, dbObj.Id);
|
|
#endregion
|
|
|
|
if (IsContextLocal)
|
|
{
|
|
DbContext.SaveChanges();
|
|
LogRateEvents(dbObj, oldRate, userid.Value.ToString(), null);
|
|
}
|
|
|
|
}
|
|
|
|
public List<Rate> GetRates(Guid expenditureCategoryId,
|
|
RateModel.RateType? type,
|
|
Guid? parentId = null,
|
|
DateTime? scenarioStartDate = null,
|
|
DateTime? scenarioEndDate = null,
|
|
Guid? derivedId = null)
|
|
{
|
|
var ratesQuery = DbContext.Rates.Where(x => x.ExpenditureCategoryId == expenditureCategoryId);
|
|
if (type.HasValue)
|
|
ratesQuery = ratesQuery.Where(x => x.Type == (short)type);
|
|
if (parentId.HasValue)
|
|
ratesQuery = ratesQuery.Where(x => x.ParentId == parentId.Value);
|
|
if (scenarioEndDate.HasValue)
|
|
ratesQuery = ratesQuery.Where(x => x.StartDate <= scenarioEndDate);
|
|
if (scenarioStartDate.HasValue)
|
|
ratesQuery = ratesQuery.Where(x => x.EndDate >= scenarioStartDate);
|
|
if (derivedId.HasValue)
|
|
ratesQuery = ratesQuery.Where(x => x.DerivedId == derivedId.Value);
|
|
return ratesQuery.ToList();
|
|
}
|
|
|
|
public Dictionary<Guid, List<Rate>> GetRates(List<Guid> expCats, RateModel.RateType? type)
|
|
{
|
|
if (expCats == null || expCats.Count <= 0)
|
|
return new Dictionary<Guid, List<Rate>>();
|
|
|
|
var ratesQuery = DbContext.Rates.AsNoTracking().Where(x => expCats.Contains(x.ExpenditureCategoryId));
|
|
if (type.HasValue)
|
|
ratesQuery = ratesQuery.Where(x => x.Type == (short)type);
|
|
|
|
return ratesQuery.ToList().GroupBy(x => x.ExpenditureCategoryId)
|
|
.ToDictionary(x => x.Key, g => g.ToList());
|
|
}
|
|
|
|
public decimal GetRateValue(Dictionary<Guid, List<Rate>> rates, Guid expenditureCategoryId, DateTime currentDate)
|
|
{
|
|
if (expenditureCategoryId == Guid.Empty)
|
|
throw new ArgumentException(Messages.Rate_Parameter_expenditureCatetoryId_CantBeEmpty);
|
|
|
|
if (rates == null || rates.Count <= 0)
|
|
return 0;
|
|
|
|
if (!rates.ContainsKey(expenditureCategoryId))
|
|
return 0;
|
|
|
|
var rate = rates[expenditureCategoryId].FirstOrDefault(x => currentDate >= x.StartDate && currentDate <= x.EndDate);
|
|
|
|
return rate?.Rate1 ?? 0;
|
|
}
|
|
public static decimal GetRateValue(Dictionary<Guid, IEnumerable<RateModel>> rates, Guid expenditureCategoryId, DateTime currentDate)
|
|
{
|
|
if (expenditureCategoryId == Guid.Empty)
|
|
throw new ArgumentException(Messages.Rate_Parameter_expenditureCatetoryId_CantBeEmpty);
|
|
|
|
if (rates == null || rates.Count <= 0)
|
|
return 0;
|
|
|
|
if (!rates.ContainsKey(expenditureCategoryId))
|
|
return 0;
|
|
|
|
var rate = rates[expenditureCategoryId].FirstOrDefault(x => currentDate >= x.StartDate && currentDate <= x.EndDate);
|
|
|
|
return rate?.Rate1 ?? 0;
|
|
}
|
|
|
|
public Dictionary<Guid, List<Rate>> GetRates(Guid? scenarioId)
|
|
{
|
|
var localRates = DbContext.Rates.Where(x => x.Type == (short)RateModel.RateType.Derived && x.ParentId == scenarioId).ToList();
|
|
var globalRates = DbContext.Rates.Where(x => x.Type == (short)RateModel.RateType.Global).ToList();
|
|
|
|
return MergeRates(globalRates, localRates);
|
|
}
|
|
/// <summary>
|
|
/// Loads both global and local rates (for the specified scenarioIds) and returns local rates as first item in the tupple, global as second item in tuple.
|
|
/// </summary>
|
|
/// <param name="scenarioIds">A list of scenario.Id values.</param>
|
|
/// <returns>A <see cref="Tuple"/> of two items, where first item always represents loca rates and second item always represents global rates.</returns>
|
|
public Tuple<IEnumerable<RateModel>, IEnumerable<RateModel>> LoadRatesByScenarios(IEnumerable<Guid> scenarioIds)
|
|
{
|
|
var localRates = scenarioIds != null && scenarioIds.Any() ?
|
|
DbContext.Rates.Where(x => x.Type == (short)RateModel.RateType.Derived && x.ParentId.HasValue && scenarioIds.Contains(x.ParentId.Value)).ToList() :
|
|
new List<Rate>();
|
|
var globalRates = DbContext.Rates.Where(x => x.Type == (short)RateModel.RateType.Global).ToList();
|
|
|
|
return new Tuple<IEnumerable<RateModel>, IEnumerable<RateModel>>(localRates.Select(t => (RateModel)t), globalRates.Select(t => (RateModel)t));
|
|
}
|
|
/// <summary>
|
|
/// Groups rates specified in <paramref name="rates"/> argument by scenarioIds and then by ExpenditureCategoryId.
|
|
/// The lowest level contains a collection of <see cref="RateModel"/> instances describing both local rate and global rates.
|
|
/// If there are both rates ending at the same EndDate then only local rate is returned.
|
|
/// </summary>
|
|
/// <param name="scenarioIds">A list of scenario.Id values.</param>
|
|
/// <param name="rates">A <see cref="Tuple"/> containing both local (for the specified scenarios) and global rates.</param>
|
|
/// <returns></returns>
|
|
/// <remarks>
|
|
/// AK: I'm not sure why we use so strange filter by EndDate, I just refactored existing GetRates method.
|
|
/// This method supposed to be used together with GetRateValue method to find correct rate for any specified date.
|
|
/// </remarks>
|
|
public Dictionary<Guid, Dictionary<Guid, IEnumerable<RateModel>>> GetRatesDict(IEnumerable<Guid> scenarioIds, Tuple<IEnumerable<RateModel>, IEnumerable<RateModel>> rates)
|
|
{
|
|
var resultDict = new Dictionary<Guid, Dictionary<Guid, IEnumerable<RateModel>>>();
|
|
//var rates = LoadRatesByScenarios(scenarioIds);
|
|
if (scenarioIds.Any())
|
|
{
|
|
var localRatesDict = rates.Item1.GroupBy(t => t.ParentId.Value).ToDictionary(gr => gr.Key, elems => elems.AsEnumerable());
|
|
var globalRates = rates.Item2;
|
|
foreach (var scenarioId in scenarioIds)
|
|
{
|
|
var scenarioLocalRates = localRatesDict.ContainsKey(scenarioId) ? localRatesDict[scenarioId] : new List<RateModel>(0);
|
|
resultDict.Add(scenarioId, MergeRateModels(scenarioLocalRates, globalRates));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resultDict.Add(Guid.Empty, MergeRateModels(rates.Item1, rates.Item2));
|
|
}
|
|
return resultDict;
|
|
}
|
|
|
|
[Obsolete("Do not use DAL objects, use MergeRateModels method which deals with models")]
|
|
public Dictionary<Guid, List<Rate>> MergeRates(List<Rate> globalRates, List<Rate> localRates)
|
|
{
|
|
if (globalRates == null)
|
|
globalRates = new List<Rate>();
|
|
|
|
if (localRates == null)
|
|
localRates = new List<Rate>();
|
|
|
|
var result = new Dictionary<Guid, List<Rate>>();
|
|
var expCatsWithLocalRates = localRates.Select(x => x.ExpenditureCategoryId).ToList();
|
|
var expCatsWithGlobalRates = globalRates.Select(x => x.ExpenditureCategoryId).ToList();
|
|
|
|
foreach (var expCatId in expCatsWithLocalRates.Union(expCatsWithGlobalRates))
|
|
{
|
|
var rates = new List<Rate>();
|
|
var expCatLocal = localRates.FindAll(x => x.ExpenditureCategoryId == expCatId);
|
|
var expCatGlobal = globalRates.FindAll(x => x.ExpenditureCategoryId == expCatId);
|
|
foreach (var rateDate in expCatLocal.Select(x => x.EndDate).Union(expCatGlobal.Select(x => x.EndDate)).OrderBy(x => x))
|
|
{
|
|
var localRate = expCatLocal.FirstOrDefault(x => x.EndDate == rateDate);
|
|
var globalRate = expCatGlobal.FirstOrDefault(x => x.EndDate == rateDate);
|
|
rates.Add(localRate ?? globalRate);
|
|
}
|
|
result.Add(expCatId, rates);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
public Dictionary<Guid, IEnumerable<RateModel>> MergeRateModels(IEnumerable<RateModel> globalRates, IEnumerable<RateModel> localRates)
|
|
{
|
|
if (globalRates == null)
|
|
globalRates = new List<RateModel>();
|
|
|
|
if (localRates == null)
|
|
localRates = new List<RateModel>();
|
|
|
|
var result = new Dictionary<Guid, IEnumerable<RateModel>>();
|
|
var localRatesDict = localRates.GroupBy(t => t.ExpenditureCategoryId).ToDictionary(ecGr => ecGr.Key,
|
|
ecElems => ecElems.GroupBy(dtGr => dtGr.EndDate).ToDictionary(dtGr => dtGr.Key, dtElems => dtElems.FirstOrDefault()));
|
|
var globalRatesDict = globalRates.GroupBy(t => t.ExpenditureCategoryId).ToDictionary(ecGr => ecGr.Key,
|
|
ecElems => ecElems.GroupBy(dtGr => dtGr.EndDate).ToDictionary(dtGr => dtGr.Key, dtElems => dtElems.FirstOrDefault()));
|
|
|
|
foreach (var expCatId in localRatesDict.Keys.Union(globalRatesDict.Keys))
|
|
{
|
|
var rates = new List<RateModel>();
|
|
var expCatLocal = localRatesDict.ContainsKey(expCatId) ? localRatesDict[expCatId] : new Dictionary<DateTime, RateModel>();
|
|
var expCatGlobal = globalRatesDict.ContainsKey(expCatId) ? globalRatesDict[expCatId] : new Dictionary<DateTime, RateModel>();
|
|
foreach (var rateDate in expCatLocal.Keys.Union(expCatGlobal.Keys).OrderBy(x => x))
|
|
{
|
|
var localRate = expCatLocal.ContainsKey(rateDate) ? expCatLocal[rateDate] : null;
|
|
var globalRate = expCatGlobal.ContainsKey(rateDate) ? expCatGlobal[rateDate] : null;
|
|
rates.Add(localRate ?? globalRate);
|
|
}
|
|
result.Add(expCatId, rates);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public Dictionary<Guid, List<Rate>> Get4Parents(List<Guid> parents, RateModel.RateType? type)
|
|
{
|
|
if (parents == null || parents.Count <= 0)
|
|
return new Dictionary<Guid, List<Rate>>();
|
|
|
|
var query = DbContext.Rates.Where(x => x.ParentId.HasValue && parents.Contains(x.ParentId.Value));
|
|
if (type.HasValue)
|
|
query = query.Where(x => x.Type == (short)type.Value);
|
|
|
|
return query.ToList()
|
|
.GroupBy(x => x.ParentId.Value)
|
|
.ToDictionary(x => x.Key, g => g.ToList());
|
|
}
|
|
|
|
public void Delete(Rate rate, Guid? userid)
|
|
{
|
|
DbContext.Rates.Remove(rate);
|
|
DbContext.SaveChanges();
|
|
LogRateEvents(null, rate, null, null);
|
|
|
|
var bpm = new ThreadedProcessing.BackgroundProcessManager(userid.Value);
|
|
if (rate.Type == (short)RateModel.RateType.Derived)
|
|
{
|
|
var globalRates = DbContext.Rates.Where(q => q.ExpenditureCategoryId == rate.ExpenditureCategoryId && q.Type == (short)RateModel.RateType.Global
|
|
&& (q.StartDate < rate.EndDate || q.EndDate > rate.StartDate));
|
|
if (globalRates.Any())
|
|
{
|
|
foreach (var globalRate in globalRates)
|
|
{
|
|
bpm.RateScenarioUpdatesAsync((RateModel)globalRate, globalRate.Id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var globalRate = rate.Clone();
|
|
globalRate.Rate1 = 0;
|
|
bpm.RateScenarioUpdatesAsync((RateModel)globalRate, globalRate.Id);
|
|
}
|
|
|
|
}
|
|
|
|
public void DeleteRange(IEnumerable<Rate> rates)
|
|
{
|
|
DbContext.Rates.RemoveRange(rates);
|
|
DbContext.SaveChanges();
|
|
LogRatesDeleteEvent(rates, null, null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Log Methods
|
|
|
|
private readonly Func<Rate, string> _rateValue = r => $"{r.Rate1} - {r.StartDate} and {r.EndDate}";
|
|
|
|
public void LogRatesDeleteEvent(IEnumerable<Rate> rates, string userId, string transactionId)
|
|
{
|
|
transactionId = Utils.GetOrCreateTransactionId(DbContext, transactionId);
|
|
|
|
foreach (var rate in rates)
|
|
{
|
|
LogRateEvents(null, rate, userId, transactionId);
|
|
}
|
|
}
|
|
|
|
public void LogRateEvents(Rate rate, Rate oldrate, string userId, string transactionId)
|
|
{
|
|
Scenario scenario = null;
|
|
Guid? scenarioId = null;
|
|
Guid? projectId = null;
|
|
|
|
var parentid = rate?.ParentId ?? oldrate?.ParentId;
|
|
|
|
if (parentid != Guid.Empty)
|
|
{
|
|
using (var scenarioManager = new ScenarioManager(DbContext))
|
|
{
|
|
scenario = scenarioManager.Load(parentid);
|
|
}
|
|
}
|
|
if (scenario != null)
|
|
{
|
|
scenarioId = scenario.Id;
|
|
projectId = scenario.ParentId;
|
|
}
|
|
|
|
transactionId = Utils.GetOrCreateTransactionId(DbContext, transactionId);
|
|
|
|
if (string.IsNullOrEmpty(userId))
|
|
userId = SecurityManager.GetUserPrincipal().ToString();
|
|
|
|
if (rate != null && oldrate == null)
|
|
{
|
|
#region Rate Created
|
|
|
|
LogEvent(transactionId, new EventRequestBaseModel
|
|
{
|
|
EntityId = rate.Id,
|
|
ScenarioId = scenarioId,
|
|
ProjectId = projectId,
|
|
ClassificationKey = "RateAdd",
|
|
NewValue = _rateValue(rate),
|
|
UserId = userId
|
|
});
|
|
|
|
#endregion
|
|
}
|
|
if (oldrate != null)
|
|
{
|
|
|
|
if (rate != null)
|
|
LogEvent(transactionId, new EventRequestBaseModel
|
|
{
|
|
EntityId = rate.Id,
|
|
ScenarioId = scenarioId,
|
|
ProjectId = projectId,
|
|
ClassificationKey = "RateEditOrDerive",
|
|
UserId = userId,
|
|
NewValue = _rateValue(rate),
|
|
OldValue = _rateValue(oldrate)
|
|
});
|
|
else
|
|
LogEvent(transactionId, new EventRequestBaseModel
|
|
{
|
|
EntityId = oldrate.Id,
|
|
ScenarioId = scenarioId,
|
|
ProjectId = projectId,
|
|
ClassificationKey = "RateDelete",
|
|
UserId = userId,
|
|
OldValue = _rateValue(oldrate)
|
|
});
|
|
|
|
}
|
|
Task.Run(() => CommitEventChanges(transactionId));
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |