using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.SqlServer; using System.Linq; using System.Text.RegularExpressions; using MileageTraker.Web.Context; using MileageTraker.Web.Models; using MileageTraker.Web.Utility; using MileageTraker.Web.ViewModels; using MileageTraker.Web.ViewModels.FuelLog; using MileageTraker.Web.ViewModels.Log; using MileageTraker.Web.ViewModels.Vehicle; using MileageTraker.Web.ViewModels.VehicleService; namespace MileageTraker.Web.DAL { public class DataService : IDisposable { private readonly MileageTrakerContext _db = new MileageTrakerContext(); public void Dispose() { _db.Dispose(); } #region Log public void AddLog(Log log) { if (FindCityByName(log.CityName) == null) throw new CityNotFoundException("City Name '" + log.CityName + "' not found"); log.Created = DateTime.Now; _db.Logs.Add(log); // update previous log links var previousLog = SearchPreviousLog(log); log.VehiclePreviousLog = previousLog; var nextLog = SearchNextLog(log); if (nextLog != null) { nextLog.VehiclePreviousLog = log; nextLog.User = _db.Users.Find(nextLog.User.UserId); _db.Entry(nextLog).State = EntityState.Modified; } _db.SaveChanges(); UpdateCurrentOdometer(log.Vehicle); } public void UpdateLog(Log log) { if (FindCityByName(log.CityName) == null) throw new CityNotFoundException("City Name '" + log.CityName + "' not found"); // note: assumes log is already attached to current context // remove from the list var nextLog = (from l in _db.Logs where l.VehiclePreviousLogId == log.LogId select l).FirstOrDefault(); if (nextLog != null) { nextLog.VehiclePreviousLogId = log.VehiclePreviousLogId; nextLog.User = _db.Users.Find(nextLog.User.UserId); _db.Entry(nextLog).State = EntityState.Modified; } log.VehiclePreviousLog = null; log.VehiclePreviousLogId = null; // add it back in new position var previousLog = SearchPreviousLog(log); if (previousLog != null) log.VehiclePreviousLogId = previousLog.LogId; var newNextLog = SearchNextLog(log); if (newNextLog != null) { newNextLog.VehiclePreviousLogId = log.LogId; newNextLog.User = _db.Users.Find(newNextLog.User.UserId); _db.Entry(newNextLog).State = EntityState.Modified; } log.User = _db.Users.Find(log.User.UserId); _db.Entry(log).State = EntityState.Modified; _db.SaveChanges(); UpdateCurrentOdometer(log.Vehicle); } public void DeleteLog(int id) { var log = _db.Logs.Find(id); var nextLog = GetNextLog(log.LogId); if (nextLog != null) { nextLog.VehiclePreviousLog = log.VehiclePreviousLog; nextLog.User = _db.Users.Find(nextLog.User.UserId); _db.Entry(nextLog).State = EntityState.Modified; } // remove the reference to the log from any fuellogs foreach (var fuelLog in log.FuelLogs) { fuelLog.Log = null; } var vehicle = log.Vehicle; // save reference to vehicle _db.Logs.Remove(log); _db.SaveChanges(); UpdateCurrentOdometer(vehicle); } public IQueryable GetLogs() { return _db.Logs; } public Log GetLog(int id) { return _db.Logs.Find(id); } public Log GetNextLog(int id) { return (from l in _db.Logs where l.VehiclePreviousLogId == id select l).FirstOrDefault(); } public IList GetValidLogMonths() { var months = from l in GetLogs() group l by new {l.Date.Year, l.Date.Month} into g //let dt = new DateTime(g.Key.Year, g.Key.Month, 1) select g.Key; var ym = from m in months.ToList() select new DateTime(m.Year, m.Month, 1); return ym.OrderByDescending(dt => dt).ToList(); } public IEnumerable GetLogIndexViewModels(IQueryable logs) { var t = from log in logs select new { log, log.VehiclePreviousLog }; return from l in t.ToList() select new LogIndexViewModel(l.log, l.VehiclePreviousLog); } public IEnumerable GetMonthlyVehicleMileageItems(LogQueryViewModel query) { var items = from l in from log in FilterLogs(_db.Logs, query) let previousLog = log.VehiclePreviousLog select new {log, previousLog, miles = (previousLog != null ? log.EndOdometer - previousLog.EndOdometer : 0)} group l by l.log.Vehicle into g let miles = g.Sum(l => l.miles) let gasPurchased = g.Sum(l => l.log.GasPurchased) where miles > 0 || gasPurchased > 0 select new { g.Key.VehicleId, GasPurchased = gasPurchased, Miles = miles, g.Key.Prog, TripCount = g.Count() }; return items.ToList().Select(i => new VehicleMileageItem { VehicleId = i.VehicleId, GasPurchased = i.GasPurchased, Miles = i.Miles, Prog = i.Prog, TripCount = i.TripCount }); } public IEnumerable GetMonthlyDriverMileageItems(LogQueryViewModel query) { var items = from l in from log in FilterLogs(_db.Logs, query) let previousLog = log.VehiclePreviousLog select new { log, previousLog, miles = (previousLog != null ? log.EndOdometer - previousLog.EndOdometer : 0) } group l by l.log.User into g let vehicleLogs = from i in g group i by i.log.Vehicle into v let vehicle = v.Key let vehicleProg = vehicle.Prog let vehicleMiles = v.Sum(l => l.miles) let vehicleGasPurchased = v.Sum(l => l.log.GasPurchased) where vehicleMiles > 0 || vehicleGasPurchased > 0 select new { v.Key.VehicleId, Prog = vehicleProg, Miles = vehicleMiles, GasPurchased = vehicleGasPurchased, TripCount = v.Count() } select new { User = g.Key, VehicleLogs = vehicleLogs }; return items.ToList().Select(i => new DriverMileageItem { DriverName = i.User.FullName, VehicleMileageItems = i.VehicleLogs.ToList().Select(v => new VehicleMileageItem { VehicleId = v.VehicleId, Prog = v.Prog, Miles = v.Miles, GasPurchased = v.GasPurchased, TripCount = v.TripCount }).ToList() }); } public static IQueryable FilterLogs(IQueryable logs, LogQueryViewModel query) { // date filtering if (query.Year.HasValue & query.Month.HasValue) { var start = new DateTime(query.Year.Value, query.Month.Value, 1); var monthRange = query.MonthRange.HasValue ? query.MonthRange.Value : 1; var end = start.AddMonths(monthRange); logs = logs.Where(l => l.Date >= start && l.Date < end); } // log type if (query.LogType.HasValue) { // WTF The specified value is not an instance of type 'Edm.Int32' logs = logs.Where(query.LogType.Value.GetEqualsPredicate()); } // employee name if (!String.IsNullOrEmpty(query.EmployeeName)) { logs = logs.Where(l => l.User.FullName == query.EmployeeName || l.User.Username == query.EmployeeName); } // vehicle id if (!String.IsNullOrEmpty(query.VehicleId)) { logs = logs.Where(l => l.Vehicle.VehicleId == query.VehicleId); } return logs; } public IEnumerable GetDuplicateLogs(Log log) { return from dbLog in GetLogs().Where(log.LogType.Enum.GetEqualsPredicate()) where dbLog.Date == log.Date && dbLog.EndOdometer == log.EndOdometer && dbLog.Vehicle.VehicleId == log.Vehicle.VehicleId select dbLog; } public IEnumerable GetRecentLogsByUsername(string username) { return (from l in _db.Logs where l.User.Username == username orderby l.Created descending select l) .Take(3) .ToList(); } public Log SearchPreviousLog(Log current) { var vehicleId = current.Vehicle.VehicleId; var date = current.Date; var endOdometer = current.EndOdometer; return SearchPreviousLog(endOdometer, vehicleId, date, current.Created, current.LogId); } public Log SearchPreviousLog(int endOdometer, string vehicleId, DateTime date, DateTime created, int? excludeLogId = null) { return (from pl in _db.Logs where pl.Vehicle.VehicleId == vehicleId && ((pl.EndOdometer < endOdometer && pl.Date <= date) || (pl.EndOdometer == endOdometer && pl.Date < date) || (pl.EndOdometer == endOdometer && pl.Date == date && pl.Created < created)) orderby pl.EndOdometer descending, pl.Date descending, pl.Created descending select pl) .FirstOrDefault(l => !excludeLogId.HasValue || l.LogId != excludeLogId.Value); } public Log SearchNextLog(Log current) { var vehicleId = current.Vehicle.VehicleId; var date = current.Date; var endOdometer = current.EndOdometer; return SearchNextLog(endOdometer, vehicleId, date, current.Created, current.LogId); } public Log SearchNextLog(int endOdometer, string vehicleId, DateTime date, DateTime created, int? excludeLogId = null) { return (from nl in GetLogs() where nl.Vehicle.VehicleId == vehicleId && ((nl.EndOdometer > endOdometer && nl.Date >= date) || (nl.EndOdometer == endOdometer && nl.Date > date) || (nl.EndOdometer == endOdometer && nl.Date >= date && nl.Created > created)) orderby nl.EndOdometer ascending, nl.Date ascending, nl.Created ascending select nl) .FirstOrDefault(l => !excludeLogId.HasValue || l.LogId != excludeLogId.Value); } public IEnumerable GetPurposeTypes() { return (from pt in _db.PurposeTypes select pt).OrderBy(pt => pt.SortOrder).ToList(); } public void AddPurposeType(PurposeType purposeType) { _db.PurposeTypes.Add(purposeType); _db.SaveChanges(); } public void UpdatePurposeType(PurposeType purposeType) { _db.Entry(purposeType).State = EntityState.Modified; _db.SaveChanges(); } public PurposeType FindPurposeType(int id) { return GetPurposeTypes().FirstOrDefault(pt => pt.PurposeTypeId == id); } public PurposeType FindPurposeType(string name) { if (string.IsNullOrEmpty(name)) return null; var mpt = GetPurposeTypes() .Select(pt => Tuple.Create(pt.PurposeTypeId, Algorithms.Similarity( pt.Purpose.ToLowerInvariant(), name.ToLowerInvariant()))) .OrderByDescending(tp => tp.Item2) .FirstOrDefault(tp => tp.Item2 > .8); if (mpt != null) return FindPurposeType(mpt.Item1); return null; } #endregion #region Vehicle public void AddVehicle(Vehicle vehicle) { if (vehicle.Assigned == "Unassigned") vehicle.Assigned = null; _db.Vehicles.Add(vehicle); _db.SaveChanges(); } public void UpdateVehicle(Vehicle vehicle) { if (vehicle.Assigned == "Unassigned") vehicle.Assigned = null; _db.Entry(vehicle).State = EntityState.Modified; _db.SaveChanges(); } public IQueryable GetVehicles() { var vehicles = _db.Vehicles; return vehicles; } public Vehicle GetVehicle(string id) { return _db.Vehicles.Find(id); } public string GetVehicleIdByTag(string tagNumber) { var vehicle = GetVehicleByTag(tagNumber); if (vehicle != null) return vehicle.VehicleId; return null; } public Vehicle GetVehicleByTag(string tagNumber) { var vehicle = GetVehicles().FirstOrDefault(v => v.TagNumber == tagNumber); if (vehicle != null) return vehicle; // exact match, this is optimal for the DB query // no match, normalize the tags, have to get all the vehicles for regex var vehicles = GetVehicles().ToList(); Func b = s => Regex.Replace(s.ToLower(), @"-|\s", ""); vehicle = vehicles.FirstOrDefault(v => b(v.TagNumber) == b(tagNumber)); return vehicle; } public void UpdateCurrentOdometer(Vehicle vehicle) { var mostRecentOdometerQuery = from log in _db.Logs where log.Vehicle.VehicleId == vehicle.VehicleId orderby log.Date descending, log.EndOdometer descending select log; var mostRecentOdometer = mostRecentOdometerQuery.FirstOrDefault(); if (mostRecentOdometer != null && mostRecentOdometer.EndOdometer != vehicle.CurrentOdometer) { vehicle.CurrentOdometer = mostRecentOdometer.EndOdometer; UpdateVehicle(vehicle); } } public void ValidateOdometerChronology(string vehicleId, int odometer, DateTime date) { var conflictingLog = GetFirstConflictingOdometerChronologyLog(vehicleId, date, odometer); if (conflictingLog != null) { throw new ChronologicalOrderException( String.Format( "Odometer of {0} miles on {1:d} conflicts with other logs for vehicle {2}" + ", including an odometer log of {3} miles on {4:d} reported by {5}.", odometer, date, vehicleId, conflictingLog.EndOdometer, conflictingLog.Date, conflictingLog.User.FullName)); } } public Log GetFirstConflictingOdometerChronologyLog(string vehicleId, DateTime date, int odometer) { var conflictingLogsQuery = from log in _db.Logs let existingDate = log.Date let existingOdometer = log.EndOdometer where log.Vehicle.VehicleId == vehicleId && // this is also in Utility.Algorithms, but doesn't get emitted to DB as SQL if used in method call !(existingDate == date || existingOdometer == odometer || existingDate < date && existingOdometer < odometer || existingDate > date && existingOdometer > odometer) orderby existingOdometer descending select log; return conflictingLogsQuery.FirstOrDefault(); } #endregion #region City public string GetCitiesCorrected(string term) { var termLower = term.ToLower(); return (from city in GetCities().ToList() let cityToLower = city.Name.ToLower() let similarity = Algorithms.Similarity(cityToLower, termLower) where city.Name.StartsWith(term) || Algorithms.Similarity(cityToLower, termLower) > .8 select city.Name).FirstOrDefault(); } public IEnumerable GetCitiesAutocomplete(string term) { return from city in GetCities() where city.Name.StartsWith(term) select city.Name; } public IQueryable GetCities() { return _db.Cities; } public City FindCityByName(string name) { return _db.Cities.FirstOrDefault(u => name.Equals(u.Name, StringComparison.InvariantCultureIgnoreCase)); } public void AddCity(City city) { _db.Cities.Add(city); _db.SaveChanges(); } #endregion #region Membership public void AddUser(User user) { user.Created = DateTime.Now; _db.Users.Add(user); _db.SaveChanges(); } public void UpdateUser(User user) { _db.Entry(user).State = EntityState.Modified; _db.SaveChanges(); } /// /// Update only fields not needing extra membership provider support /// public void UpdateUserPersonalInfo(User user) { var original = GetUser(user.UserId); original.Comment = user.Comment; original.Email = user.Email; original.FullName = user.FullName; original.Username = user.Username; UpdateUser(original); } public void UpdateUserPassword(Guid userId, string password) { var user = GetUser(userId); user.Password = Crypto.HashPassword(password); user.LastPasswordChangedDate = DateTime.Now; user.IsLockedOut = false; user.PasswordFailuresSinceLastSuccess = 0; user.PasswordResetToken = null; UpdateUser(user); } public void UpdateUserLastActivity(string username) { var user = FindUserByUsername(username); if (user != null) { user.LastActivityDate = DateTime.Now; UpdateUser(user); } } public IQueryable GetUsers() { return _db.Users; } public User GetUser(Guid guid) { return _db.Users.Find(guid); } public User FindUserByUsername(string username) { return _db.Users.FirstOrDefault(u => username.Equals(u.Username, StringComparison.InvariantCultureIgnoreCase)); } public User FindUserByFullName(string fullName) { return _db.Users.FirstOrDefault(u => fullName.Equals(u.FullName, StringComparison.InvariantCultureIgnoreCase)); } public User FindUserByEmail(string email) { return _db.Users.FirstOrDefault(u => email.Equals(u.Email, StringComparison.InvariantCultureIgnoreCase)); } public IQueryable GetRoles() { return _db.Roles; } public IEnumerable GetUserFullNamesAutocomplete(string term) { var termLower = term.ToLower(); return from name in GetUserFullNames() let nameLower = name.ToLower() where nameLower.StartsWith(termLower) || Algorithms.Similarity(nameLower, termLower) > .8 select name; } public IEnumerable GetUserFullNames() { var logNames = (from l in _db.Users select l.FullName); return logNames; } #endregion #region FuelLog public void AddFuelLog(FuelLog fuelLog) { _db.FuelLogs.Add(fuelLog); _db.SaveChanges(); } public void AddFuelLogs(IEnumerable fuelLogs) { _db.FuelLogs.AddRange(fuelLogs); _db.SaveChanges(); } public void UpdateFuelLog(FuelLog fuelLog) { _db.Entry(fuelLog).State = EntityState.Modified; _db.SaveChanges(); } public IQueryable GetFuelLogs() { return _db.FuelLogs; } public FuelLog GetFuelLog(int id) { return _db.FuelLogs.Find(id); } public static IQueryable FilterFuelLogs(IQueryable fuelLogs, FuelLogQueryViewModel query) { // date filtering if (query.Year.HasValue & query.Month.HasValue) { var start = new DateTime(query.Year.Value, query.Month.Value, 1); var monthRange = query.MonthRange.HasValue ? query.MonthRange.Value : 1; var end = start.AddMonths(monthRange); fuelLogs = fuelLogs.Where(l => l.Date >= start && l.Date < end); } if (query.Unmatched) { fuelLogs = fuelLogs.Where(l => l.Log == null); } if (!string.IsNullOrEmpty(query.TagNumber)) { fuelLogs = fuelLogs.Where(l => l.TagNumber == query.TagNumber); } return fuelLogs; } public IEnumerable GetDuplicateFuelLogs(FuelLog log) { return from dbLog in GetFuelLogs() where dbLog.Date == log.Date && dbLog.GasPurchased == log.GasPurchased && dbLog.TagNumber == log.TagNumber select dbLog; } public Log GetMatchingLog(FuelLog fuelLog) { // add a little wiggle room for the gas - it is common to be off by a couple hundreths const double gasRange = .01; var gasMax = fuelLog.GasPurchased + gasRange; var gasMin = fuelLog.GasPurchased - gasRange; var dateGasVehicleQuery = GetLogs(); // matching by Date, Gas Purchased (gallons), and Vehicle var vehicle = GetVehicleByTag(fuelLog.TagNumber); if (vehicle != null) dateGasVehicleQuery = dateGasVehicleQuery.Where(log => log.Vehicle.VehicleId == vehicle.VehicleId); dateGasVehicleQuery = dateGasVehicleQuery .Where(log => log.Date.Year == fuelLog.Date.Year && log.Date.Month == fuelLog.Date.Month && log.Date.Day == fuelLog.Date.Day) .Where(log => log.GasPurchased <= gasMax && log.GasPurchased >= gasMin); var dateDriverGasQuery = GetLogs() .Where(log => log.Date.Year == fuelLog.Date.Year && log.Date.Month == fuelLog.Date.Month && log.Date.Day == fuelLog.Date.Day) .Where(log => log.User.FullName.Contains(fuelLog.DriverFullName)) .Where(log => log.GasPurchased <= gasMax && log.GasPurchased >= gasMin); return dateGasVehicleQuery.Union(dateDriverGasQuery).FirstOrDefault(); } public IEnumerable GetPossibleMatchingLogs(FuelLog fuelLog) { const int dateRange = 3; const double gasRange = 1; const double odometerRange = 100; var gasVehicleQuery = GetLogs(); // matching by Date, Gas Purchased (gallons), and Vehicle var vehicle = GetVehicleByTag(fuelLog.TagNumber); if (vehicle != null) gasVehicleQuery = gasVehicleQuery.Where(log => log.Vehicle.VehicleId == vehicle.VehicleId); var futureDate = fuelLog.Date.AddDays(dateRange); var pastDate = fuelLog.Date.AddDays(-dateRange); gasVehicleQuery = gasVehicleQuery.Where(log => log.Date.Year <= futureDate.Year && log.Date.Month <= futureDate.Month && log.Date.Day <= futureDate.Day && log.Date.Year >= pastDate.Year && log.Date.Month >= pastDate.Month && log.Date.Day >= pastDate.Day ); var gasMax = fuelLog.GasPurchased + gasRange; var gasMin = fuelLog.GasPurchased - gasRange; gasVehicleQuery = gasVehicleQuery.Where(log => log.GasPurchased <= gasMax && log.GasPurchased >= gasMin); var driverQuery = GetLogs() .Where(log => log.User.FullName.Contains(fuelLog.DriverFullName)) .Where(log => log.GasPurchased > 0) .Where(log => log.Date.Year <= futureDate.Year && log.Date.Month <= futureDate.Month && log.Date.Day <= futureDate.Day && log.Date.Year >= pastDate.Year && log.Date.Month >= pastDate.Month && log.Date.Day >= pastDate.Day ); var odoMax = fuelLog.Odometer + odometerRange; var odoMin = fuelLog.Odometer - odometerRange; var odometerQuery = GetLogs() .Where(log => log.EndOdometer > odoMin && log.EndOdometer < odoMax) .Where(log => log.GasPurchased > 0) .Where(log => log.Date.Year <= futureDate.Year && log.Date.Month <= futureDate.Month && log.Date.Day <= futureDate.Day && log.Date.Year >= pastDate.Year && log.Date.Month >= pastDate.Month && log.Date.Day >= pastDate.Day ); return gasVehicleQuery.Union(driverQuery).Union(odometerQuery); } public IList GetValidFuelLogMonths() { var months = from l in GetFuelLogs() group l by new { l.Date.Year, l.Date.Month } into g //let dt = new DateTime(g.Key.Year, g.Key.Month, 1) select g.Key; var ym = from m in months.ToList() select new DateTime(m.Year, m.Month, 1); return ym.OrderByDescending(dt => dt).ToList(); } #endregion #region Vehicle Service public void AddVehicleService(VehicleService vehicleService) { _db.VehicleServices.Add(vehicleService); _db.SaveChanges(); } public void UpdateVehicleService(VehicleService vehicleService) { _db.Entry(vehicleService).State = EntityState.Modified; _db.SaveChanges(); } public IQueryable GetVehicleServices() { var vehicleServices = _db.VehicleServices; return vehicleServices; } public VehicleService GetVehicleService(int id) { return _db.VehicleServices.Find(id); } public void DeleteVehicleService(int id) { var vehicleService = _db.VehicleServices.Find(id); _db.VehicleServices.Remove(vehicleService); foreach (var recall in _db.VehicleRecalls.Where(vr => vr.CompletedService.VehicleServiceId == id)) { recall.CompletedService = null; _db.Entry(recall).State = EntityState.Modified; } _db.SaveChanges(); } public IList GetValidVehicleServiceMonths() { var months = from l in GetVehicleServices() group l by new {l.InvoiceDate.Year, l.InvoiceDate.Month} into g select g.Key; var ym = from m in months.ToList() select new DateTime(m.Year, m.Month, 1); return ym.OrderByDescending(dt => dt).ToList(); } public static IQueryable FilterVehicleServices(IQueryable vehicleServices, VehicleServiceQueryViewModel query) { // date filtering if (query.Year.HasValue & query.Month.HasValue) { var start = new DateTime(query.Year.Value, query.Month.Value, 1); var monthRange = query.MonthRange.HasValue ? query.MonthRange.Value : 1; var end = start.AddMonths(monthRange); vehicleServices = vehicleServices.Where(vs => vs.InvoiceDate >= start && vs.InvoiceDate < end); } if (!string.IsNullOrEmpty(query.VehicleId)) vehicleServices = vehicleServices.Where(vs => vs.Vehicle.VehicleId == query.VehicleId); return vehicleServices; } public IQueryable GetServiceCenterNames() { return from l in GetVehicleServices() group l by new {l.ServiceCenterName} into g select g.Key.ServiceCenterName; } public IEnumerable GetServiceCenterNamesAutocomplete(string term) { return from name in GetServiceCenterNames() where name.StartsWith(term) select name; } #endregion #region Vehicle Cost Report private const int FiscalYearStartMonth = 7; public IQueryable GetFuelLogCost(int fiscalYear) { var fiscalYearStart = new DateTime(fiscalYear - 1, FiscalYearStartMonth, 1); var fiscalYearEnd = new DateTime(fiscalYear, FiscalYearStartMonth, 1); return from fuelLog in _db.FuelLogs where fuelLog.Date >= fiscalYearStart && fuelLog.Date < fiscalYearEnd group fuelLog by fuelLog.TagNumber into tagGroup let totalFuelPrice = tagGroup.Sum(i => i.TotalPrice) select new VehicleCostItem { CostType = "Fuel", TotalPrice = totalFuelPrice, TagNumber = tagGroup.Key}; } public IQueryable GetVehicleServiceCost(int fiscalYear) { var fiscalYearStart = new DateTime(fiscalYear - 1, FiscalYearStartMonth, 1); var fiscalYearEnd = new DateTime(fiscalYear, FiscalYearStartMonth, 1); return from vehicleService in _db.VehicleServices where vehicleService.InvoiceDate >= fiscalYearStart && vehicleService.InvoiceDate < fiscalYearEnd group vehicleService by vehicleService.Vehicle.TagNumber into tagGroup let totalServicePrice = tagGroup.Sum(i => i.Price) select new VehicleCostItem { CostType = "Service", TotalPrice = totalServicePrice, TagNumber = tagGroup.Key }; } public IList GetVehicleCostItems(int fiscalYear) { var costItems = GetFuelLogCost(fiscalYear).Union(GetVehicleServiceCost(fiscalYear)).ToList(); foreach (var costItem in costItems) { costItem.VehicleId = GetVehicleIdByTag(costItem.TagNumber); } return costItems; } public IList GetValidVehicleCostItemsYears() { var fuelYears = (from fuelLog in _db.FuelLogs let fiscalYear = SqlFunctions.DateAdd("month", FiscalYearStartMonth - 1, fuelLog.Date).Value.Year select fiscalYear).Distinct(); var serviceYears = (from vehicleService in _db.VehicleServices let fiscalYear = SqlFunctions.DateAdd("month", FiscalYearStartMonth - 1, vehicleService.InvoiceDate).Value.Year select fiscalYear).Distinct(); var years = fuelYears.Union(serviceYears).Distinct().OrderByDescending(y => y); return years.ToList(); } #endregion #region Service Reminder public void AddServiceReminder(ServiceReminder serviceReminder) { _db.ServiceReminders.Add(serviceReminder); _db.SaveChanges(); } public void UpdateServiceReminder(ServiceReminder serviceReminder) { _db.Entry(serviceReminder).State = EntityState.Modified; _db.SaveChanges(); } public IQueryable GetServiceReminders(string vehicleId) { var serviceReminders = _db.ServiceReminders.Where(sr => sr.Vehicle.VehicleId == vehicleId); return serviceReminders; } public ServiceReminder GetServiceReminder(int id) { return _db.ServiceReminders.Find(id); } public ServiceReminder FindDuplicateServiceReminder(string vehicleId, int targetOdometer, string description) { return (from sr in _db.ServiceReminders where sr.Vehicle.VehicleId == vehicleId && sr.TargetOdometer == targetOdometer && sr.Description == description select sr).FirstOrDefault(); } public void DeleteServiceReminder(int id) { var serviceReminder = _db.ServiceReminders.Find(id); _db.ServiceReminders.Remove(serviceReminder); _db.SaveChanges(); } public IQueryable GetUpcomingServiceReminders() { const int mileageThreshold = ServiceReminderUpcomingMileageThreshold; return from sr in _db.ServiceReminders where sr.Vehicle.CurrentOdometer.HasValue && sr.Vehicle.CurrentOdometer.Value > (sr.TargetOdometer - mileageThreshold) select sr; } public const int ServiceReminderDefaultMileageDelta = 5000; public const int ServiceReminderUpcomingMileageThreshold = 300; #endregion #region Vehicle Recall public void AddVehicleRecall(VehicleRecall vehicleRecall) { _db.VehicleRecalls.Add(vehicleRecall); _db.SaveChanges(); } public void UpdateVehicleRecall(VehicleRecall vehicleRecall) { _db.Entry(vehicleRecall).State = EntityState.Modified; _db.SaveChanges(); } public void UpdateVehicleRecallService(int vehicleRecallId, VehicleService vehicleService) { if (vehicleRecallId > 0) { var recall = GetVehicleRecall(vehicleRecallId); recall.CompletedService = vehicleService; UpdateVehicleRecall(recall); } else { var recall = GetVehicleRecalls().FirstOrDefault(vr => vr.CompletedService.VehicleServiceId == vehicleService.VehicleServiceId); if (recall != null) { recall.CompletedService = null; UpdateVehicleRecall(recall); } } } public IQueryable GetVehicleRecalls(string vehicleId) { return GetVehicleRecalls().Where(vr => vr.Vehicle.VehicleId == vehicleId); } public IQueryable GetOpenVehicleRecalls() { return GetVehicleRecalls().Where(vr => vr.CompletedService == null); } public IQueryable GetOpenVehicleRecalls(string vehicleId) { return GetOpenVehicleRecalls().Where(vr => vr.Vehicle.VehicleId == vehicleId); } public IQueryable GetVehicleRecallByServiceId(int vehicleServiceId) { return GetVehicleRecalls().Where(vr => vr.CompletedService.VehicleServiceId == vehicleServiceId); } public IQueryable GetVehicleRecalls() { return _db.VehicleRecalls; } public VehicleRecall GetVehicleRecall(int id) { return _db.VehicleRecalls.Find(id); } public void DeleteVehicleRecall(int id) { var vehicleRecall = _db.VehicleRecalls.Find(id); _db.VehicleRecalls.Remove(vehicleRecall); _db.SaveChanges(); } #endregion } }