From 628c22ad6108dc415153192ac917913665348be5 Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0O5CDB\\DESK-555BD" Date: Thu, 21 Nov 2024 22:13:08 -0700 Subject: [PATCH] Added functionality to display multi year trends. --- Controllers/Vehicle/ReportController.cs | 49 ++++++ Helper/ReportHelper.cs | 159 +++++++++++++----- Helper/StaticHelper.cs | 21 +++ Models/Report/CostDistanceTableForVehicle.cs | 8 + Models/Report/CostForVehicleByMonth.cs | 2 + Views/Vehicle/_CollisionRecords.cshtml | 4 +- Views/Vehicle/_CostDistanceTableReport.cshtml | 97 +++++++++++ Views/Vehicle/_Gas.cshtml | 4 +- Views/Vehicle/_GasCostByMonthReport.cshtml | 3 + Views/Vehicle/_Notes.cshtml | 4 +- Views/Vehicle/_OdometerRecords.cshtml | 4 +- Views/Vehicle/_ReminderRecords.cshtml | 4 +- Views/Vehicle/_ServiceRecords.cshtml | 4 +- Views/Vehicle/_SupplyRecords.cshtml | 4 +- Views/Vehicle/_TaxRecords.cshtml | 4 +- Views/Vehicle/_UpgradeRecords.cshtml | 4 +- Views/Vehicle/_VehicleHistory.cshtml | 4 +- wwwroot/js/reports.js | 52 ++++++ 18 files changed, 378 insertions(+), 53 deletions(-) create mode 100644 Models/Report/CostDistanceTableForVehicle.cs create mode 100644 Views/Vehicle/_CostDistanceTableReport.cshtml diff --git a/Controllers/Vehicle/ReportController.cs b/Controllers/Vehicle/ReportController.cs index adc09d9..f7f5a7a 100644 --- a/Controllers/Vehicle/ReportController.cs +++ b/Controllers/Vehicle/ReportController.cs @@ -558,6 +558,55 @@ namespace CarCareTracker.Controllers }).ToList(); return PartialView("_GasCostByMonthReport", groupedRecord); } + [TypeFilter(typeof(CollaboratorFilter))] + [HttpPost] + public IActionResult GetCostByMonthAndYearByVehicle(int vehicleId, List selectedMetrics, int year = 0) + { + List allCosts = StaticHelper.GetBaseLineCosts(); + if (selectedMetrics.Contains(ImportMode.ServiceRecord)) + { + var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId); + allCosts.AddRange(_reportHelper.GetServiceRecordSum(serviceRecords, year, true)); + } + if (selectedMetrics.Contains(ImportMode.RepairRecord)) + { + var repairRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId); + allCosts.AddRange(_reportHelper.GetRepairRecordSum(repairRecords, year, true)); + } + if (selectedMetrics.Contains(ImportMode.UpgradeRecord)) + { + var upgradeRecords = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicleId); + allCosts.AddRange(_reportHelper.GetUpgradeRecordSum(upgradeRecords, year, true)); + } + if (selectedMetrics.Contains(ImportMode.GasRecord)) + { + var gasRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId); + allCosts.AddRange(_reportHelper.GetGasRecordSum(gasRecords, year, true)); + } + if (selectedMetrics.Contains(ImportMode.TaxRecord)) + { + var taxRecords = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId); + allCosts.AddRange(_reportHelper.GetTaxRecordSum(taxRecords, year, true)); + } + if (selectedMetrics.Contains(ImportMode.OdometerRecord)) + { + var odometerRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId); + allCosts.AddRange(_reportHelper.GetOdometerRecordSum(odometerRecords, year, true)); + } + var groupedRecord = allCosts.GroupBy(x => new { x.MonthName, x.MonthId, x.Year }).OrderByDescending(x=>x.Key.Year).Select(x => new CostForVehicleByMonth + { + Year = x.Key.Year, + MonthName = x.Key.MonthName, + Cost = x.Sum(y => y.Cost), + DistanceTraveled = x.Max(y => y.DistanceTraveled), + MonthId = x.Key.MonthId + }).ToList(); + var vehicleData = _dataAccess.GetVehicleById(vehicleId); + var userConfig = _config.GetUserConfig(User); + var viewModel = new CostDistanceTableForVehicle { CostData = groupedRecord }; + viewModel.DistanceUnit = vehicleData.UseHours ? "h" : userConfig.UseMPG ? "mi." : "km"; + return PartialView("_CostDistanceTableReport", viewModel); + } [HttpGet] public IActionResult GetAdditionalWidgets() { diff --git a/Helper/ReportHelper.cs b/Helper/ReportHelper.cs index ebc61b2..9317217 100644 --- a/Helper/ReportHelper.cs +++ b/Helper/ReportHelper.cs @@ -5,93 +5,166 @@ namespace CarCareTracker.Helper { public interface IReportHelper { - IEnumerable GetOdometerRecordSum(List odometerRecords, int year = 0); - IEnumerable GetServiceRecordSum(List serviceRecords, int year = 0); - IEnumerable GetRepairRecordSum(List repairRecords, int year = 0); - IEnumerable GetUpgradeRecordSum(List upgradeRecords, int year = 0); - IEnumerable GetGasRecordSum(List gasRecords, int year = 0); - IEnumerable GetTaxRecordSum(List taxRecords, int year = 0); + IEnumerable GetOdometerRecordSum(List odometerRecords, int year = 0, bool sortIntoYear = false); + IEnumerable GetServiceRecordSum(List serviceRecords, int year = 0, bool sortIntoYear = false); + IEnumerable GetRepairRecordSum(List repairRecords, int year = 0, bool sortIntoYear = false); + IEnumerable GetUpgradeRecordSum(List upgradeRecords, int year = 0, bool sortIntoYear = false); + IEnumerable GetGasRecordSum(List gasRecords, int year = 0, bool sortIntoYear = false); + IEnumerable GetTaxRecordSum(List taxRecords, int year = 0, bool sortIntoYear = false); } public class ReportHelper: IReportHelper { - public IEnumerable GetOdometerRecordSum(List odometerRecords, int year = 0) + public IEnumerable GetOdometerRecordSum(List odometerRecords, int year = 0, bool sortIntoYear = false) { if (year != default) { odometerRecords.RemoveAll(x => x.Date.Year != year); } - return odometerRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + if (sortIntoYear) { - MonthId = x.Key, - MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), - Cost = 0, - DistanceTraveled = x.Sum(y=>y.DistanceTraveled) - }); + return odometerRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key.Month, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month), + Year = x.Key.Year, + Cost = 0, + DistanceTraveled = x.Sum(y => y.DistanceTraveled) + }); + } else + { + return odometerRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), + Cost = 0, + DistanceTraveled = x.Sum(y => y.DistanceTraveled) + }); + } } - public IEnumerable GetServiceRecordSum(List serviceRecords, int year = 0) + public IEnumerable GetServiceRecordSum(List serviceRecords, int year = 0, bool sortIntoYear = false) { if (year != default) { serviceRecords.RemoveAll(x => x.Date.Year != year); } - return serviceRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + if (sortIntoYear) { - MonthId = x.Key, - MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), - Cost = x.Sum(y => y.Cost) - }); + return serviceRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key.Month, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month), + Year = x.Key.Year, + Cost = x.Sum(y => y.Cost) + }); + } else + { + return serviceRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), + Cost = x.Sum(y => y.Cost) + }); + } } - public IEnumerable GetRepairRecordSum(List repairRecords, int year = 0) + public IEnumerable GetRepairRecordSum(List repairRecords, int year = 0, bool sortIntoYear = false) { if (year != default) { repairRecords.RemoveAll(x => x.Date.Year != year); } - return repairRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + if (sortIntoYear) { - MonthId = x.Key, - MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), - Cost = x.Sum(y => y.Cost) - }); + return repairRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key.Month, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month), + Year = x.Key.Year, + Cost = x.Sum(y => y.Cost) + }); + } else + { + return repairRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), + Cost = x.Sum(y => y.Cost) + }); + } } - public IEnumerable GetUpgradeRecordSum(List upgradeRecords, int year = 0) + public IEnumerable GetUpgradeRecordSum(List upgradeRecords, int year = 0, bool sortIntoYear = false) { if (year != default) { upgradeRecords.RemoveAll(x => x.Date.Year != year); } - return upgradeRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + if (sortIntoYear) { - MonthId = x.Key, - MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), - Cost = x.Sum(y => y.Cost) - }); + return upgradeRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key.Month, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month), + Year = x.Key.Year, + Cost = x.Sum(y => y.Cost) + }); + } else + { + return upgradeRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), + Cost = x.Sum(y => y.Cost) + }); + } } - public IEnumerable GetGasRecordSum(List gasRecords, int year = 0) + public IEnumerable GetGasRecordSum(List gasRecords, int year = 0, bool sortIntoYear = false) { if (year != default) { gasRecords.RemoveAll(x => x.Date.Year != year); } - return gasRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + if (sortIntoYear) { - MonthId = x.Key, - MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), - Cost = x.Sum(y => y.Cost) - }); + return gasRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key.Month, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month), + Year = x.Key.Year, + Cost = x.Sum(y => y.Cost) + }); + } else + { + return gasRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), + Cost = x.Sum(y => y.Cost) + }); + } } - public IEnumerable GetTaxRecordSum(List taxRecords, int year = 0) + public IEnumerable GetTaxRecordSum(List taxRecords, int year = 0, bool sortIntoYear = false) { if (year != default) { taxRecords.RemoveAll(x => x.Date.Year != year); } - return taxRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + if (sortIntoYear) { - MonthId = x.Key, - MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), - Cost = x.Sum(y => y.Cost) - }); + return taxRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key.Month, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month), + Year = x.Key.Year, + Cost = x.Sum(y => y.Cost) + }); + } else + { + return taxRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth + { + MonthId = x.Key, + MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key), + Cost = x.Sum(y => y.Cost) + }); + } } } } diff --git a/Helper/StaticHelper.cs b/Helper/StaticHelper.cs index 257aa95..c728d82 100644 --- a/Helper/StaticHelper.cs +++ b/Helper/StaticHelper.cs @@ -601,6 +601,27 @@ namespace CarCareTracker.Helper _csv.NextRecord(); } } + public static string HideZeroCost(string input, bool hideZero, string decorations = "") + { + if (input == 0M.ToString("C2") && hideZero) + { + return "---"; + } else + { + return string.IsNullOrWhiteSpace(decorations) ? input : $"{input}{decorations}"; + } + } + public static string HideZeroCost(decimal input, bool hideZero, string decorations = "") + { + if (input == default && hideZero) + { + return "---"; + } + else + { + return string.IsNullOrWhiteSpace(decorations) ? input.ToString("C2") : $"{input.ToString("C2")}{decorations}"; + } + } public static void WriteGasRecordExportModel(CsvWriter _csv, IEnumerable genericRecords) { var extraHeaders = genericRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct(); diff --git a/Models/Report/CostDistanceTableForVehicle.cs b/Models/Report/CostDistanceTableForVehicle.cs new file mode 100644 index 0000000..4689d01 --- /dev/null +++ b/Models/Report/CostDistanceTableForVehicle.cs @@ -0,0 +1,8 @@ +namespace CarCareTracker.Models +{ + public class CostDistanceTableForVehicle + { + public string DistanceUnit { get; set; } = "mi."; + public List CostData { get; set; } = new List(); + } +} diff --git a/Models/Report/CostForVehicleByMonth.cs b/Models/Report/CostForVehicleByMonth.cs index d4185e5..8700a36 100644 --- a/Models/Report/CostForVehicleByMonth.cs +++ b/Models/Report/CostForVehicleByMonth.cs @@ -2,9 +2,11 @@ { public class CostForVehicleByMonth { + public int Year { get; set; } public int MonthId { get; set; } public string MonthName { get; set; } public decimal Cost { get; set; } public int DistanceTraveled { get; set; } + public decimal CostPerDistanceTraveled { get { if (DistanceTraveled > 0) { return Cost / DistanceTraveled; } else { return 0M; } } } } } diff --git a/Views/Vehicle/_CollisionRecords.cshtml b/Views/Vehicle/_CollisionRecords.cshtml index 9818fbc..8e2d1eb 100644 --- a/Views/Vehicle/_CollisionRecords.cshtml +++ b/Views/Vehicle/_CollisionRecords.cshtml @@ -145,12 +145,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_CostDistanceTableReport.cshtml b/Views/Vehicle/_CostDistanceTableReport.cshtml new file mode 100644 index 0000000..9084f8d --- /dev/null +++ b/Views/Vehicle/_CostDistanceTableReport.cshtml @@ -0,0 +1,97 @@ +@using CarCareTracker.Helper +@inject IConfigHelper config +@inject ITranslationHelper translator +@model CostDistanceTableForVehicle +@{ + var userConfig = config.GetUserConfig(User); + var userLanguage = userConfig.UserLanguage; + var hideZero = userConfig.HideZero; + var years = Model.CostData.Select(x => x.Year).Distinct(); + if (years.Count() > 5){ + years = years.Take(5); //if there is more than 5 years of data then we only take the last 5 years. + } + var months = Model.CostData.OrderBy(x => x.MonthId).Select(x => x.MonthName).Distinct(); +} +@if (Model.CostData.Any()) +{ +
+ + +
+} +else +{ +
+

@translator.Translate(userLanguage, "No data found or all records have zero sums, insert records with non-zero sums to see visualizations here.")

+
+} diff --git a/Views/Vehicle/_Gas.cshtml b/Views/Vehicle/_Gas.cshtml index 5fcebf7..bc7edd0 100644 --- a/Views/Vehicle/_Gas.cshtml +++ b/Views/Vehicle/_Gas.cshtml @@ -217,12 +217,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_GasCostByMonthReport.cshtml b/Views/Vehicle/_GasCostByMonthReport.cshtml index 3463fb5..a9bc880 100644 --- a/Views/Vehicle/_GasCostByMonthReport.cshtml +++ b/Views/Vehicle/_GasCostByMonthReport.cshtml @@ -52,6 +52,9 @@ ] }, options: { + onClick: (e) => { + showBarChartTable(); + }, plugins: { title: { display: true, diff --git a/Views/Vehicle/_Notes.cshtml b/Views/Vehicle/_Notes.cshtml index ca84ac4..b067142 100644 --- a/Views/Vehicle/_Notes.cshtml +++ b/Views/Vehicle/_Notes.cshtml @@ -55,12 +55,14 @@ @StaticHelper.TruncateStrings(note.NoteText, 100) } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_OdometerRecords.cshtml b/Views/Vehicle/_OdometerRecords.cshtml index 842b09b..4524f16 100644 --- a/Views/Vehicle/_OdometerRecords.cshtml +++ b/Views/Vehicle/_OdometerRecords.cshtml @@ -145,12 +145,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_ReminderRecords.cshtml b/Views/Vehicle/_ReminderRecords.cshtml index 0d23c88..1d1fd84 100644 --- a/Views/Vehicle/_ReminderRecords.cshtml +++ b/Views/Vehicle/_ReminderRecords.cshtml @@ -105,12 +105,14 @@ } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_ServiceRecords.cshtml b/Views/Vehicle/_ServiceRecords.cshtml index cb23ce8..17879d5 100644 --- a/Views/Vehicle/_ServiceRecords.cshtml +++ b/Views/Vehicle/_ServiceRecords.cshtml @@ -143,12 +143,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_SupplyRecords.cshtml b/Views/Vehicle/_SupplyRecords.cshtml index e4586eb..5248e14 100644 --- a/Views/Vehicle/_SupplyRecords.cshtml +++ b/Views/Vehicle/_SupplyRecords.cshtml @@ -161,12 +161,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_TaxRecords.cshtml b/Views/Vehicle/_TaxRecords.cshtml index 46b11b6..dbe8ab4 100644 --- a/Views/Vehicle/_TaxRecords.cshtml +++ b/Views/Vehicle/_TaxRecords.cshtml @@ -137,12 +137,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_UpgradeRecords.cshtml b/Views/Vehicle/_UpgradeRecords.cshtml index 20048a5..0617807 100644 --- a/Views/Vehicle/_UpgradeRecords.cshtml +++ b/Views/Vehicle/_UpgradeRecords.cshtml @@ -145,12 +145,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/Views/Vehicle/_VehicleHistory.cshtml b/Views/Vehicle/_VehicleHistory.cshtml index 50085dc..5ac6b2b 100644 --- a/Views/Vehicle/_VehicleHistory.cshtml +++ b/Views/Vehicle/_VehicleHistory.cshtml @@ -153,12 +153,14 @@ } } + + @StaticHelper.ReportNote - + diff --git a/wwwroot/js/reports.js b/wwwroot/js/reports.js index 653b000..3a8688b 100644 --- a/wwwroot/js/reports.js +++ b/wwwroot/js/reports.js @@ -176,6 +176,58 @@ function refreshBarChart() { }); setSelectedMetrics(); } +function showBarChartTable() { + var selectedMetrics = []; + var vehicleId = GetVehicleId().vehicleId; + var year = getYear(); + + if ($("#serviceExpenseCheck").is(":checked")) { + selectedMetrics.push('ServiceRecord'); + } + if ($("#repairExpenseCheck").is(":checked")) { + selectedMetrics.push('RepairRecord'); + } + if ($("#upgradeExpenseCheck").is(":checked")) { + selectedMetrics.push('UpgradeRecord'); + } + if ($("#gasExpenseCheck").is(":checked")) { + selectedMetrics.push('GasRecord'); + } + if ($("#taxExpenseCheck").is(":checked")) { + selectedMetrics.push('TaxRecord'); + } + if ($("#odometerExpenseCheck").is(":checked")) { + selectedMetrics.push('OdometerRecord'); + } + + $.post('/Vehicle/GetCostByMonthAndYearByVehicle', + { + vehicleId: vehicleId, + selectedMetrics: selectedMetrics, + year: year + }, function (data) { + $("#vehicleDataTableModalContent").html(data); + $("#vehicleDataTableModal").modal('show'); + }); +} +function toggleBarChartTableData() { + //find out which column data type is shown + if (!$('[report-data="cost"]').hasClass('d-none')) { + //currently cost is shown. + $('[report-data="cost"]').addClass('d-none'); + $('[report-data="distance"]').removeClass('d-none'); + } + else if (!$('[report-data="distance"]').hasClass('d-none')) { + //currently distance is shown. + $('[report-data="distance"]').addClass('d-none'); + $('[report-data="costperdistance"]').removeClass('d-none'); + } + else if (!$('[report-data="costperdistance"]').hasClass('d-none')) { + //currently cost per distance is shown. + $('[report-data="costperdistance"]').addClass('d-none'); + $('[report-data="cost"]').removeClass('d-none'); + } +} function updateReminderPie() { var vehicleId = GetVehicleId().vehicleId; var daysToAdd = $("#reminderOption").val();