Compare commits
41 Commits
v1.0.7
...
Hargata/to
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bc47363fd | ||
|
|
b54809f399 | ||
|
|
f7f47c54ff | ||
|
|
92564ae527 | ||
|
|
52ada8574d | ||
|
|
013fb67943 | ||
|
|
d86298f502 | ||
|
|
5891b78be0 | ||
|
|
a9e3e44f2c | ||
|
|
be81f9727a | ||
|
|
04b7e7fd38 | ||
|
|
e6b50fafd2 | ||
|
|
d4896a7607 | ||
|
|
0b203709fa | ||
|
|
df5faba146 | ||
|
|
d8f8b63488 | ||
|
|
c100fc76ed | ||
|
|
c553d87600 | ||
|
|
b542bd54fb | ||
|
|
4ec11a47a1 | ||
|
|
175ce2be48 | ||
|
|
aad1655f2e | ||
|
|
85eb0b70e6 | ||
|
|
f54e12886a | ||
|
|
80ebe4c292 | ||
|
|
92b3bc3aea | ||
|
|
2cfb82c235 | ||
|
|
dd693323d7 | ||
|
|
5c8f03003e | ||
|
|
023fac2ea9 | ||
|
|
9086c26b5e | ||
|
|
0af8e99e61 | ||
|
|
4ca45dd32b | ||
|
|
127753ee86 | ||
|
|
30a9411cdd | ||
|
|
e801a4a77c | ||
|
|
d8c49995ce | ||
|
|
0c93663e51 | ||
|
|
605ac07594 | ||
|
|
9a7f2233a0 | ||
|
|
1339c427c4 |
@@ -22,10 +22,12 @@ namespace CarCareTracker.Controllers
|
||||
private readonly IReminderRecordDataAccess _reminderRecordDataAccess;
|
||||
private readonly IUpgradeRecordDataAccess _upgradeRecordDataAccess;
|
||||
private readonly IOdometerRecordDataAccess _odometerRecordDataAccess;
|
||||
private readonly ITorqueRecordDataAccess _torqueRecordDataAccess;
|
||||
private readonly IReminderHelper _reminderHelper;
|
||||
private readonly IGasHelper _gasHelper;
|
||||
private readonly IUserLogic _userLogic;
|
||||
private readonly IFileHelper _fileHelper;
|
||||
private readonly IConfigHelper _configHelper;
|
||||
public APIController(IVehicleDataAccess dataAccess,
|
||||
IGasHelper gasHelper,
|
||||
IReminderHelper reminderHelper,
|
||||
@@ -37,6 +39,8 @@ namespace CarCareTracker.Controllers
|
||||
IReminderRecordDataAccess reminderRecordDataAccess,
|
||||
IUpgradeRecordDataAccess upgradeRecordDataAccess,
|
||||
IOdometerRecordDataAccess odometerRecordDataAccess,
|
||||
ITorqueRecordDataAccess torqueRecordDataAccess,
|
||||
IConfigHelper configHelper,
|
||||
IFileHelper fileHelper,
|
||||
IUserLogic userLogic)
|
||||
{
|
||||
@@ -49,7 +53,9 @@ namespace CarCareTracker.Controllers
|
||||
_reminderRecordDataAccess = reminderRecordDataAccess;
|
||||
_upgradeRecordDataAccess = upgradeRecordDataAccess;
|
||||
_odometerRecordDataAccess = odometerRecordDataAccess;
|
||||
_torqueRecordDataAccess = torqueRecordDataAccess;
|
||||
_gasHelper = gasHelper;
|
||||
_configHelper = configHelper;
|
||||
_reminderHelper = reminderHelper;
|
||||
_userLogic = userLogic;
|
||||
_fileHelper = fileHelper;
|
||||
@@ -83,6 +89,53 @@ namespace CarCareTracker.Controllers
|
||||
return Json(result);
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpPost]
|
||||
[Route("/api/vehicle/servicerecords/add")]
|
||||
public IActionResult AddServiceRecord(int vehicleId, ServiceRecordExportModel input)
|
||||
{
|
||||
var response = new OperationResponse();
|
||||
if (vehicleId == default)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Must provide a valid vehicle id";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(input.Date) ||
|
||||
string.IsNullOrWhiteSpace(input.Description) ||
|
||||
string.IsNullOrWhiteSpace(input.Odometer) ||
|
||||
string.IsNullOrWhiteSpace(input.Cost))
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Input object invalid, Date, Description, Odometer, and Cost cannot be empty.";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
try
|
||||
{
|
||||
var serviceRecord = new ServiceRecord()
|
||||
{
|
||||
VehicleId = vehicleId,
|
||||
Date = DateTime.Parse(input.Date),
|
||||
Mileage = int.Parse(input.Odometer),
|
||||
Description = input.Description,
|
||||
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
|
||||
Cost = decimal.Parse(input.Cost)
|
||||
};
|
||||
_serviceRecordDataAccess.SaveServiceRecordToVehicle(serviceRecord);
|
||||
response.Success = true;
|
||||
response.Message = "Service Record Added";
|
||||
return Json(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = ex.Message;
|
||||
Response.StatusCode = 500;
|
||||
return Json(response);
|
||||
}
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpGet]
|
||||
[Route("/api/vehicle/repairrecords")]
|
||||
public IActionResult RepairRecords(int vehicleId)
|
||||
@@ -92,6 +145,53 @@ namespace CarCareTracker.Controllers
|
||||
return Json(result);
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpPost]
|
||||
[Route("/api/vehicle/repairrecords/add")]
|
||||
public IActionResult AddRepairRecord(int vehicleId, ServiceRecordExportModel input)
|
||||
{
|
||||
var response = new OperationResponse();
|
||||
if (vehicleId == default)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Must provide a valid vehicle id";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(input.Date) ||
|
||||
string.IsNullOrWhiteSpace(input.Description) ||
|
||||
string.IsNullOrWhiteSpace(input.Odometer) ||
|
||||
string.IsNullOrWhiteSpace(input.Cost))
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Input object invalid, Date, Description, Odometer, and Cost cannot be empty.";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
try
|
||||
{
|
||||
var repairRecord = new CollisionRecord()
|
||||
{
|
||||
VehicleId = vehicleId,
|
||||
Date = DateTime.Parse(input.Date),
|
||||
Mileage = int.Parse(input.Odometer),
|
||||
Description = input.Description,
|
||||
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
|
||||
Cost = decimal.Parse(input.Cost)
|
||||
};
|
||||
_collisionRecordDataAccess.SaveCollisionRecordToVehicle(repairRecord);
|
||||
response.Success = true;
|
||||
response.Message = "Repair Record Added";
|
||||
return Json(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = ex.Message;
|
||||
Response.StatusCode = 500;
|
||||
return Json(response);
|
||||
}
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpGet]
|
||||
[Route("/api/vehicle/upgraderecords")]
|
||||
public IActionResult UpgradeRecords(int vehicleId)
|
||||
@@ -101,6 +201,53 @@ namespace CarCareTracker.Controllers
|
||||
return Json(result);
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpPost]
|
||||
[Route("/api/vehicle/upgraderecords/add")]
|
||||
public IActionResult AddUpgradeRecord(int vehicleId, ServiceRecordExportModel input)
|
||||
{
|
||||
var response = new OperationResponse();
|
||||
if (vehicleId == default)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Must provide a valid vehicle id";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(input.Date) ||
|
||||
string.IsNullOrWhiteSpace(input.Description) ||
|
||||
string.IsNullOrWhiteSpace(input.Odometer) ||
|
||||
string.IsNullOrWhiteSpace(input.Cost))
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Input object invalid, Date, Description, Odometer, and Cost cannot be empty.";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
try
|
||||
{
|
||||
var upgradeRecord = new UpgradeRecord()
|
||||
{
|
||||
VehicleId = vehicleId,
|
||||
Date = DateTime.Parse(input.Date),
|
||||
Mileage = int.Parse(input.Odometer),
|
||||
Description = input.Description,
|
||||
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
|
||||
Cost = decimal.Parse(input.Cost)
|
||||
};
|
||||
_upgradeRecordDataAccess.SaveUpgradeRecordToVehicle(upgradeRecord);
|
||||
response.Success = true;
|
||||
response.Message = "Upgrade Record Added";
|
||||
return Json(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = ex.Message;
|
||||
Response.StatusCode = 500;
|
||||
return Json(response);
|
||||
}
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpGet]
|
||||
[Route("/api/vehicle/taxrecords")]
|
||||
public IActionResult TaxRecords(int vehicleId)
|
||||
@@ -109,6 +256,51 @@ namespace CarCareTracker.Controllers
|
||||
return Json(result);
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpPost]
|
||||
[Route("/api/vehicle/taxrecords/add")]
|
||||
public IActionResult AddTaxRecord(int vehicleId, TaxRecordExportModel input)
|
||||
{
|
||||
var response = new OperationResponse();
|
||||
if (vehicleId == default)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Must provide a valid vehicle id";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(input.Date) ||
|
||||
string.IsNullOrWhiteSpace(input.Description) ||
|
||||
string.IsNullOrWhiteSpace(input.Cost))
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Input object invalid, Date, Description, and Cost cannot be empty.";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
try
|
||||
{
|
||||
var taxRecord = new TaxRecord()
|
||||
{
|
||||
VehicleId = vehicleId,
|
||||
Date = DateTime.Parse(input.Date),
|
||||
Description = input.Description,
|
||||
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
|
||||
Cost = decimal.Parse(input.Cost)
|
||||
};
|
||||
_taxRecordDataAccess.SaveTaxRecordToVehicle(taxRecord);
|
||||
response.Success = true;
|
||||
response.Message = "Tax Record Added";
|
||||
return Json(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = ex.Message;
|
||||
Response.StatusCode = 500;
|
||||
return Json(response);
|
||||
}
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpGet]
|
||||
[Route("/api/vehicle/odometerrecords")]
|
||||
public IActionResult OdometerRecords(int vehicleId)
|
||||
@@ -127,6 +319,15 @@ namespace CarCareTracker.Controllers
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Must provide a valid vehicle id";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(input.Date) ||
|
||||
string.IsNullOrWhiteSpace(input.Odometer))
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Input object invalid, Date and Odometer cannot be empty.";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
try
|
||||
@@ -142,10 +343,12 @@ namespace CarCareTracker.Controllers
|
||||
response.Success = true;
|
||||
response.Message = "Odometer Record Added";
|
||||
return Json(response);
|
||||
} catch (Exception ex)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = StaticHelper.GenericErrorMessage;
|
||||
response.Message = ex.Message;
|
||||
Response.StatusCode = 500;
|
||||
return Json(response);
|
||||
}
|
||||
}
|
||||
@@ -156,7 +359,8 @@ namespace CarCareTracker.Controllers
|
||||
{
|
||||
var vehicleRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
||||
var result = _gasHelper.GetGasRecordViewModels(vehicleRecords, useMPG, useUKMPG)
|
||||
.Select(x => new GasRecordExportModel {
|
||||
.Select(x => new GasRecordExportModel
|
||||
{
|
||||
Date = x.Date,
|
||||
Odometer = x.Mileage.ToString(),
|
||||
Cost = x.Cost.ToString(),
|
||||
@@ -169,13 +373,65 @@ namespace CarCareTracker.Controllers
|
||||
return Json(result);
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpPost]
|
||||
[Route("/api/vehicle/gasrecords/add")]
|
||||
public IActionResult AddGasRecord(int vehicleId, GasRecordExportModel input)
|
||||
{
|
||||
var response = new OperationResponse();
|
||||
if (vehicleId == default)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Must provide a valid vehicle id";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(input.Date) ||
|
||||
string.IsNullOrWhiteSpace(input.Odometer) ||
|
||||
string.IsNullOrWhiteSpace(input.FuelConsumed) ||
|
||||
string.IsNullOrWhiteSpace(input.Cost) ||
|
||||
string.IsNullOrWhiteSpace(input.IsFillToFull) ||
|
||||
string.IsNullOrWhiteSpace(input.MissedFuelUp)
|
||||
)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Input object invalid, Date, Odometer, FuelConsumed, IsFillToFull, MissedFuelUp, and Cost cannot be empty.";
|
||||
Response.StatusCode = 400;
|
||||
return Json(response);
|
||||
}
|
||||
try
|
||||
{
|
||||
var gasRecord = new GasRecord()
|
||||
{
|
||||
VehicleId = vehicleId,
|
||||
Date = DateTime.Parse(input.Date),
|
||||
Mileage = int.Parse(input.Odometer),
|
||||
Gallons = decimal.Parse(input.FuelConsumed),
|
||||
IsFillToFull = bool.Parse(input.IsFillToFull),
|
||||
MissedFuelUp = bool.Parse(input.MissedFuelUp),
|
||||
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
|
||||
Cost = decimal.Parse(input.Cost)
|
||||
};
|
||||
_gasRecordDataAccess.SaveGasRecordToVehicle(gasRecord);
|
||||
response.Success = true;
|
||||
response.Message = "Gas Record Added";
|
||||
return Json(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = ex.Message;
|
||||
Response.StatusCode = 500;
|
||||
return Json(response);
|
||||
}
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpGet]
|
||||
[Route("/api/vehicle/reminders")]
|
||||
public IActionResult Reminders(int vehicleId)
|
||||
{
|
||||
var currentMileage = GetMaxMileage(vehicleId);
|
||||
var reminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicleId);
|
||||
var results = _reminderHelper.GetReminderRecordViewModels(reminders, currentMileage, DateTime.Now).Select(x=> new ReminderExportModel { Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes});
|
||||
var results = _reminderHelper.GetReminderRecordViewModels(reminders, currentMileage, DateTime.Now).Select(x => new ReminderExportModel { Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes });
|
||||
return Json(results);
|
||||
}
|
||||
[Authorize(Roles = nameof(UserData.IsRootUser))]
|
||||
@@ -186,6 +442,62 @@ namespace CarCareTracker.Controllers
|
||||
var result = _fileHelper.MakeBackup();
|
||||
return Json(result);
|
||||
}
|
||||
[Route("/api/obdii/vehicle/{vehicleId}")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult OBDII(int vehicleId, TorqueRecord record)
|
||||
{
|
||||
if (record.kff1005 != default && record.kff1006 != default && vehicleId != default)
|
||||
{
|
||||
//check if there is an existing session.
|
||||
try
|
||||
{
|
||||
var existingRecord = _torqueRecordDataAccess.GetTorqueRecordById(record.Session);
|
||||
if (existingRecord != null)
|
||||
{
|
||||
//calculate difference between last coordinates.
|
||||
var distance = GetDistance(existingRecord.LastLongitude, existingRecord.LastLatitude, record.kff1005, record.kff1006);
|
||||
var useMPG = _configHelper.GetUserConfig(User).UseMPG;
|
||||
if (useMPG)
|
||||
{
|
||||
distance /= 1609; //get miles.
|
||||
}
|
||||
else
|
||||
{
|
||||
distance /= 1000;
|
||||
}
|
||||
existingRecord.DistanceTraveled += distance;
|
||||
existingRecord.LastLongitude = record.kff1005;
|
||||
existingRecord.LastLatitude = record.kff1006;
|
||||
_torqueRecordDataAccess.SaveTorqueRecord(existingRecord);
|
||||
}
|
||||
else
|
||||
{
|
||||
//new record.
|
||||
record.InitialLongitude = record.kff1005;
|
||||
record.InitialLatitude = record.kff1006;
|
||||
record.LastLongitude = record.kff1005;
|
||||
record.LastLatitude = record.kff1006;
|
||||
_torqueRecordDataAccess.SaveTorqueRecord(record);
|
||||
}
|
||||
return Json(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(false);
|
||||
}
|
||||
}
|
||||
return Json(false);
|
||||
}
|
||||
private double GetDistance(double longitude, double latitude, double otherLongitude, double otherLatitude)
|
||||
{
|
||||
var d1 = latitude * (Math.PI / 180.0);
|
||||
var num1 = longitude * (Math.PI / 180.0);
|
||||
var d2 = otherLatitude * (Math.PI / 180.0);
|
||||
var num2 = otherLongitude * (Math.PI / 180.0) - num1;
|
||||
var d3 = Math.Pow(Math.Sin((d2 - d1) / 2.0), 2.0) + Math.Cos(d1) * Math.Cos(d2) * Math.Pow(Math.Sin(num2 / 2.0), 2.0);
|
||||
|
||||
return 6376500.0 * (2.0 * Math.Atan2(Math.Sqrt(d3), Math.Sqrt(1.0 - d3)));
|
||||
}
|
||||
private int GetMaxMileage(int vehicleId)
|
||||
{
|
||||
var numbersArray = new List<int>();
|
||||
|
||||
@@ -6,6 +6,11 @@ namespace CarCareTracker.Controllers
|
||||
{
|
||||
public IActionResult Unauthorized()
|
||||
{
|
||||
if (!User.IsInRole("CookieAuth"))
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return new EmptyResult();
|
||||
}
|
||||
return View("401");
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@ namespace CarCareTracker.Controllers
|
||||
public IActionResult Index(int vehicleId)
|
||||
{
|
||||
var data = _dataAccess.GetVehicleById(vehicleId);
|
||||
UpdateRecurringTaxes(vehicleId);
|
||||
return View(data);
|
||||
}
|
||||
[HttpGet]
|
||||
@@ -551,6 +552,16 @@ namespace CarCareTracker.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult SaveGasRecordToVehicleId(GasRecordInput gasRecord)
|
||||
{
|
||||
if (gasRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
|
||||
{
|
||||
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
|
||||
{
|
||||
Date = DateTime.Parse(gasRecord.Date),
|
||||
VehicleId = gasRecord.VehicleId,
|
||||
Mileage = gasRecord.Mileage,
|
||||
Notes = $"Auto Insert From Gas Record. {gasRecord.Notes}"
|
||||
});
|
||||
}
|
||||
gasRecord.Files = gasRecord.Files.Select(x => { return new UploadedFiles { Name = x.Name, Location = _fileHelper.MoveFileFromTemp(x.Location, "documents/") }; }).ToList();
|
||||
var result = _gasRecordDataAccess.SaveGasRecordToVehicle(gasRecord.ToGasRecord());
|
||||
return Json(result);
|
||||
@@ -612,9 +623,23 @@ namespace CarCareTracker.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult SaveServiceRecordToVehicleId(ServiceRecordInput serviceRecord)
|
||||
{
|
||||
if (serviceRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
|
||||
{
|
||||
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
|
||||
{
|
||||
Date = DateTime.Parse(serviceRecord.Date),
|
||||
VehicleId = serviceRecord.VehicleId,
|
||||
Mileage = serviceRecord.Mileage,
|
||||
Notes = $"Auto Insert From Service Record: {serviceRecord.Description}"
|
||||
});
|
||||
}
|
||||
//move files from temp.
|
||||
serviceRecord.Files = serviceRecord.Files.Select(x => { return new UploadedFiles { Name = x.Name, Location = _fileHelper.MoveFileFromTemp(x.Location, "documents/") }; }).ToList();
|
||||
var result = _serviceRecordDataAccess.SaveServiceRecordToVehicle(serviceRecord.ToServiceRecord());
|
||||
if (result && serviceRecord.Supplies.Any())
|
||||
{
|
||||
RequisitionSupplyRecordsByUsage(serviceRecord.Supplies);
|
||||
}
|
||||
return Json(result);
|
||||
}
|
||||
[HttpGet]
|
||||
@@ -667,9 +692,23 @@ namespace CarCareTracker.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult SaveCollisionRecordToVehicleId(CollisionRecordInput collisionRecord)
|
||||
{
|
||||
if (collisionRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
|
||||
{
|
||||
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
|
||||
{
|
||||
Date = DateTime.Parse(collisionRecord.Date),
|
||||
VehicleId = collisionRecord.VehicleId,
|
||||
Mileage = collisionRecord.Mileage,
|
||||
Notes = $"Auto Insert From Repair Record: {collisionRecord.Description}"
|
||||
});
|
||||
}
|
||||
//move files from temp.
|
||||
collisionRecord.Files = collisionRecord.Files.Select(x => { return new UploadedFiles { Name = x.Name, Location = _fileHelper.MoveFileFromTemp(x.Location, "documents/") }; }).ToList();
|
||||
var result = _collisionRecordDataAccess.SaveCollisionRecordToVehicle(collisionRecord.ToCollisionRecord());
|
||||
if (result && collisionRecord.Supplies.Any())
|
||||
{
|
||||
RequisitionSupplyRecordsByUsage(collisionRecord.Supplies);
|
||||
}
|
||||
return Json(result);
|
||||
}
|
||||
[HttpGet]
|
||||
@@ -719,6 +758,34 @@ namespace CarCareTracker.Controllers
|
||||
}
|
||||
return PartialView("_TaxRecords", result);
|
||||
}
|
||||
private void UpdateRecurringTaxes(int vehicleId)
|
||||
{
|
||||
var result = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId);
|
||||
var recurringFees = result.Where(x => x.IsRecurring);
|
||||
if (recurringFees.Any())
|
||||
{
|
||||
foreach(TaxRecord recurringFee in recurringFees)
|
||||
{
|
||||
var newDate = recurringFee.Date.AddMonths((int)recurringFee.RecurringInterval);
|
||||
if (DateTime.Now > newDate){
|
||||
recurringFee.IsRecurring = false;
|
||||
var newRecurringFee = new TaxRecord()
|
||||
{
|
||||
VehicleId = recurringFee.VehicleId,
|
||||
Date = newDate,
|
||||
Description = recurringFee.Description,
|
||||
Cost = recurringFee.Cost,
|
||||
IsRecurring = true,
|
||||
Notes = recurringFee.Notes,
|
||||
RecurringInterval = recurringFee.RecurringInterval,
|
||||
Files = recurringFee.Files
|
||||
};
|
||||
_taxRecordDataAccess.SaveTaxRecordToVehicle(recurringFee);
|
||||
_taxRecordDataAccess.SaveTaxRecordToVehicle(newRecurringFee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[HttpPost]
|
||||
public IActionResult SaveTaxRecordToVehicleId(TaxRecordInput taxRecord)
|
||||
{
|
||||
@@ -745,6 +812,8 @@ namespace CarCareTracker.Controllers
|
||||
Description = result.Description,
|
||||
Notes = result.Notes,
|
||||
VehicleId = result.VehicleId,
|
||||
IsRecurring = result.IsRecurring,
|
||||
RecurringInterval = result.RecurringInterval,
|
||||
Files = result.Files
|
||||
};
|
||||
return PartialView("_TaxRecordModal", convertedResult);
|
||||
@@ -778,7 +847,7 @@ namespace CarCareTracker.Controllers
|
||||
UpgradeRecordSum = upgradeRecords.Sum(x => x.Cost)
|
||||
};
|
||||
//get costbymonth
|
||||
List<CostForVehicleByMonth> allCosts = new List<CostForVehicleByMonth>();
|
||||
List<CostForVehicleByMonth> allCosts = StaticHelper.GetBaseLineCosts();
|
||||
allCosts.AddRange(_reportHelper.GetServiceRecordSum(serviceRecords, 0));
|
||||
allCosts.AddRange(_reportHelper.GetRepairRecordSum(collisionRecords, 0));
|
||||
allCosts.AddRange(_reportHelper.GetUpgradeRecordSum(upgradeRecords, 0));
|
||||
@@ -829,10 +898,17 @@ namespace CarCareTracker.Controllers
|
||||
var userConfig = _config.GetUserConfig(User);
|
||||
var mileageData = _gasHelper.GetGasRecordViewModels(gasRecords, userConfig.UseMPG, userConfig.UseUKMPG);
|
||||
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
||||
var monthlyMileageData = mileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
var monthlyMileageData = StaticHelper.GetBaseLineCostsNoMonthName();
|
||||
monthlyMileageData.AddRange(mileageData.GroupBy(x => x.MonthId).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
MonthId = x.Key,
|
||||
Cost = x.Average(y => y.MilesPerGallon)
|
||||
}));
|
||||
monthlyMileageData = monthlyMileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = x.Sum(y=>y.Cost)
|
||||
}).ToList();
|
||||
viewModel.FuelMileageForVehicleByMonth = monthlyMileageData;
|
||||
return PartialView("_Report", viewModel);
|
||||
@@ -914,11 +990,11 @@ namespace CarCareTracker.Controllers
|
||||
bool useUKMPG = _config.GetUserConfig(User).UseUKMPG;
|
||||
vehicleHistory.TotalGasCost = gasRecords.Sum(x => x.Cost);
|
||||
vehicleHistory.TotalCost = serviceRecords.Sum(x => x.Cost) + repairRecords.Sum(x => x.Cost) + upgradeRecords.Sum(x => x.Cost) + taxRecords.Sum(x => x.Cost);
|
||||
var averageMPG = 0.00M;
|
||||
var averageMPG = "0";
|
||||
var gasViewModels = _gasHelper.GetGasRecordViewModels(gasRecords, useMPG, useUKMPG);
|
||||
if (gasViewModels.Any())
|
||||
{
|
||||
averageMPG = gasViewModels.Average(x => x.MilesPerGallon);
|
||||
averageMPG = _gasHelper.GetAverageGasMileage(gasViewModels);
|
||||
}
|
||||
vehicleHistory.MPG = averageMPG;
|
||||
//insert servicerecords
|
||||
@@ -974,10 +1050,17 @@ namespace CarCareTracker.Controllers
|
||||
mileageData.RemoveAll(x => DateTime.Parse(x.Date).Year != year);
|
||||
}
|
||||
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
||||
var monthlyMileageData = mileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
var monthlyMileageData = StaticHelper.GetBaseLineCostsNoMonthName();
|
||||
monthlyMileageData.AddRange(mileageData.GroupBy(x => x.MonthId).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
MonthId = x.Key,
|
||||
Cost = x.Average(y => y.MilesPerGallon)
|
||||
}));
|
||||
monthlyMileageData = monthlyMileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
}).ToList();
|
||||
return PartialView("_MPGByMonthReport", monthlyMileageData);
|
||||
}
|
||||
@@ -985,7 +1068,7 @@ namespace CarCareTracker.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult GetCostByMonthByVehicle(int vehicleId, List<ImportMode> selectedMetrics, int year = 0)
|
||||
{
|
||||
List<CostForVehicleByMonth> allCosts = new List<CostForVehicleByMonth>();
|
||||
List<CostForVehicleByMonth> allCosts = StaticHelper.GetBaseLineCosts();
|
||||
if (selectedMetrics.Contains(ImportMode.ServiceRecord))
|
||||
{
|
||||
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId);
|
||||
@@ -1063,6 +1146,9 @@ namespace CarCareTracker.Controllers
|
||||
public IActionResult GetVehicleHaveUrgentOrPastDueReminders(int vehicleId)
|
||||
{
|
||||
var result = GetRemindersAndUrgency(vehicleId, DateTime.Now);
|
||||
//check if user wants auto-refresh past-due reminders
|
||||
if (_config.GetUserConfig(User).EnableAutoReminderRefresh)
|
||||
{
|
||||
//check for past due reminders that are eligible for recurring.
|
||||
var pastDueAndRecurring = result.Where(x => x.Urgency == ReminderUrgency.PastDue && x.IsRecurring);
|
||||
if (pastDueAndRecurring.Any())
|
||||
@@ -1072,25 +1158,14 @@ namespace CarCareTracker.Controllers
|
||||
//update based on recurring intervals.
|
||||
//pull reminderRecord based on ID
|
||||
var existingReminder = _reminderRecordDataAccess.GetReminderRecordById(reminderRecord.Id);
|
||||
if (existingReminder.Metric == ReminderMetric.Both)
|
||||
{
|
||||
existingReminder.Date = existingReminder.Date.AddMonths((int)existingReminder.ReminderMonthInterval);
|
||||
existingReminder.Mileage += (int)existingReminder.ReminderMileageInterval;
|
||||
}
|
||||
else if (existingReminder.Metric == ReminderMetric.Odometer)
|
||||
{
|
||||
existingReminder.Mileage += (int)existingReminder.ReminderMileageInterval;
|
||||
}
|
||||
else if (existingReminder.Metric == ReminderMetric.Date)
|
||||
{
|
||||
existingReminder.Date = existingReminder.Date.AddMonths((int)existingReminder.ReminderMonthInterval);
|
||||
}
|
||||
existingReminder = _reminderHelper.GetUpdatedRecurringReminderRecord(existingReminder);
|
||||
//save to db.
|
||||
_reminderRecordDataAccess.SaveReminderRecordToVehicle(existingReminder);
|
||||
//set urgency to not urgent so it gets excluded in count.
|
||||
reminderRecord.Urgency = ReminderUrgency.NotUrgent;
|
||||
}
|
||||
}
|
||||
}
|
||||
//check for very urgent or past due reminders that were not eligible for recurring.
|
||||
var pastDueAndUrgentReminders = result.Where(x => x.Urgency == ReminderUrgency.VeryUrgent || x.Urgency == ReminderUrgency.PastDue);
|
||||
if (pastDueAndUrgentReminders.Any())
|
||||
@@ -1108,6 +1183,15 @@ namespace CarCareTracker.Controllers
|
||||
return PartialView("_ReminderRecords", result);
|
||||
}
|
||||
[HttpPost]
|
||||
public IActionResult PushbackRecurringReminderRecord(int reminderRecordId)
|
||||
{
|
||||
var existingReminder = _reminderRecordDataAccess.GetReminderRecordById(reminderRecordId);
|
||||
existingReminder = _reminderHelper.GetUpdatedRecurringReminderRecord(existingReminder);
|
||||
//save to db.
|
||||
var result = _reminderRecordDataAccess.SaveReminderRecordToVehicle(existingReminder);
|
||||
return Json(result);
|
||||
}
|
||||
[HttpPost]
|
||||
public IActionResult SaveReminderRecordToVehicleId(ReminderRecordInput reminderRecord)
|
||||
{
|
||||
var result = _reminderRecordDataAccess.SaveReminderRecordToVehicle(reminderRecord.ToReminderRecord());
|
||||
@@ -1172,9 +1256,23 @@ namespace CarCareTracker.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult SaveUpgradeRecordToVehicleId(UpgradeRecordInput upgradeRecord)
|
||||
{
|
||||
if (upgradeRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
|
||||
{
|
||||
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
|
||||
{
|
||||
Date = DateTime.Parse(upgradeRecord.Date),
|
||||
VehicleId = upgradeRecord.VehicleId,
|
||||
Mileage = upgradeRecord.Mileage,
|
||||
Notes = $"Auto Insert From Upgrade Record: {upgradeRecord.Description}"
|
||||
});
|
||||
}
|
||||
//move files from temp.
|
||||
upgradeRecord.Files = upgradeRecord.Files.Select(x => { return new UploadedFiles { Name = x.Name, Location = _fileHelper.MoveFileFromTemp(x.Location, "documents/") }; }).ToList();
|
||||
var result = _upgradeRecordDataAccess.SaveUpgradeRecordToVehicle(upgradeRecord.ToUpgradeRecord());
|
||||
if (result && upgradeRecord.Supplies.Any())
|
||||
{
|
||||
RequisitionSupplyRecordsByUsage(upgradeRecord.Supplies);
|
||||
}
|
||||
return Json(result);
|
||||
}
|
||||
[HttpGet]
|
||||
@@ -1213,6 +1311,7 @@ namespace CarCareTracker.Controllers
|
||||
public IActionResult GetNotesByVehicleId(int vehicleId)
|
||||
{
|
||||
var result = _noteDataAccess.GetNotesByVehicleId(vehicleId);
|
||||
result = result.OrderByDescending(x => x.Pinned).ToList();
|
||||
return PartialView("_Notes", result);
|
||||
}
|
||||
[HttpPost]
|
||||
@@ -1240,6 +1339,21 @@ namespace CarCareTracker.Controllers
|
||||
}
|
||||
#endregion
|
||||
#region "Supply Records"
|
||||
private void RequisitionSupplyRecordsByUsage(List<SupplyUsage> supplyUsage)
|
||||
{
|
||||
foreach(SupplyUsage supply in supplyUsage)
|
||||
{
|
||||
//get supply record.
|
||||
var result = _supplyRecordDataAccess.GetSupplyRecordById(supply.SupplyId);
|
||||
var unitCost = (result.Quantity != 0 ) ? result.Cost / result.Quantity : 0;
|
||||
//deduct quantity used.
|
||||
result.Quantity -= supply.Quantity;
|
||||
//deduct cost.
|
||||
result.Cost -= (supply.Quantity * unitCost);
|
||||
//save
|
||||
_supplyRecordDataAccess.SaveSupplyRecordToVehicle(result);
|
||||
}
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpGet]
|
||||
public IActionResult GetSupplyRecordsByVehicleId(int vehicleId)
|
||||
@@ -1256,6 +1370,23 @@ namespace CarCareTracker.Controllers
|
||||
}
|
||||
return PartialView("_SupplyRecords", result);
|
||||
}
|
||||
[TypeFilter(typeof(CollaboratorFilter))]
|
||||
[HttpGet]
|
||||
public IActionResult GetSupplyRecordsForRecordsByVehicleId(int vehicleId)
|
||||
{
|
||||
var result = _supplyRecordDataAccess.GetSupplyRecordsByVehicleId(vehicleId);
|
||||
result.RemoveAll(x => x.Quantity <= 0);
|
||||
bool _useDescending = _config.GetUserConfig(User).UseDescending;
|
||||
if (_useDescending)
|
||||
{
|
||||
result = result.OrderByDescending(x => x.Date).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = result.OrderBy(x => x.Date).ToList();
|
||||
}
|
||||
return PartialView("_SupplyUsage", result);
|
||||
}
|
||||
[HttpPost]
|
||||
public IActionResult SaveSupplyRecordToVehicleId(SupplyRecordInput supplyRecord)
|
||||
{
|
||||
@@ -1316,6 +1447,10 @@ namespace CarCareTracker.Controllers
|
||||
//move files from temp.
|
||||
planRecord.Files = planRecord.Files.Select(x => { return new UploadedFiles { Name = x.Name, Location = _fileHelper.MoveFileFromTemp(x.Location, "documents/") }; }).ToList();
|
||||
var result = _planRecordDataAccess.SavePlanRecordToVehicle(planRecord.ToPlanRecord());
|
||||
if (result && planRecord.Supplies.Any())
|
||||
{
|
||||
RequisitionSupplyRecordsByUsage(planRecord.Supplies);
|
||||
}
|
||||
return Json(result);
|
||||
}
|
||||
[HttpGet]
|
||||
@@ -1332,6 +1467,16 @@ namespace CarCareTracker.Controllers
|
||||
var result = _planRecordDataAccess.SavePlanRecordToVehicle(existingRecord);
|
||||
if (planProgress == PlanProgress.Done)
|
||||
{
|
||||
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
|
||||
{
|
||||
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
|
||||
{
|
||||
Date = DateTime.Now,
|
||||
VehicleId = existingRecord.VehicleId,
|
||||
Mileage = odometer,
|
||||
Notes = $"Auto Insert From Plan Record: {existingRecord.Description}"
|
||||
});
|
||||
}
|
||||
//convert plan record to service/upgrade/repair record.
|
||||
if (existingRecord.ImportMode == ImportMode.ServiceRecord)
|
||||
{
|
||||
|
||||
@@ -2,12 +2,20 @@
|
||||
{
|
||||
public enum ReminderMileageInterval
|
||||
{
|
||||
FiftyMiles = 50,
|
||||
OneHundredMiles = 100,
|
||||
FiveHundredMiles = 500,
|
||||
OneThousandMiles = 1000,
|
||||
ThreeThousandMiles = 3000,
|
||||
FourThousandMiles = 4000,
|
||||
FiveThousandMiles = 5000,
|
||||
SevenThousandFiveHundredMiles = 7500,
|
||||
TenThousandMiles = 10000,
|
||||
FiftyThousandMiles = 50000
|
||||
FifteenThousandMiles = 15000,
|
||||
TwentyThousandMiles = 20000,
|
||||
ThirtyThousandMiles = 30000,
|
||||
FiftyThousandMiles = 50000,
|
||||
OneHundredThousandMiles = 100000,
|
||||
OneHundredFiftyThousandMiles = 150000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
{
|
||||
public enum ReminderMonthInterval
|
||||
{
|
||||
OneMonth = 1,
|
||||
ThreeMonths = 3,
|
||||
SixMonths = 6,
|
||||
OneYear = 12,
|
||||
TwoYears = 24,
|
||||
ThreeYears = 36,
|
||||
FiveYears = 60
|
||||
}
|
||||
}
|
||||
|
||||
39
External/Implementations/TorqueRecordDataAccess.cs
vendored
Normal file
39
External/Implementations/TorqueRecordDataAccess.cs
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
using CarCareTracker.External.Interfaces;
|
||||
using CarCareTracker.Helper;
|
||||
using CarCareTracker.Models;
|
||||
using LiteDB;
|
||||
|
||||
namespace CarCareTracker.External.Implementations
|
||||
{
|
||||
public class TorqueRecordDataAccess: ITorqueRecordDataAccess
|
||||
{
|
||||
private static string dbName = StaticHelper.DbName;
|
||||
private static string tableName = "torquerecords";
|
||||
public TorqueRecord GetTorqueRecordById(string torqueRecordId)
|
||||
{
|
||||
using (var db = new LiteDatabase(dbName))
|
||||
{
|
||||
var table = db.GetCollection<TorqueRecord>(tableName);
|
||||
return table.FindById(torqueRecordId);
|
||||
};
|
||||
}
|
||||
public bool DeleteTorqueRecordById(int torqueRecordId)
|
||||
{
|
||||
using (var db = new LiteDatabase(dbName))
|
||||
{
|
||||
var table = db.GetCollection<TorqueRecord>(tableName);
|
||||
table.Delete(torqueRecordId);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
public bool SaveTorqueRecord(TorqueRecord torqueRecord)
|
||||
{
|
||||
using (var db = new LiteDatabase(dbName))
|
||||
{
|
||||
var table = db.GetCollection<TorqueRecord>(tableName);
|
||||
table.Upsert(torqueRecord);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
11
External/Interfaces/ITorqueRecordDataAccess.cs
vendored
Normal file
11
External/Interfaces/ITorqueRecordDataAccess.cs
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
using CarCareTracker.Models;
|
||||
|
||||
namespace CarCareTracker.External.Interfaces
|
||||
{
|
||||
public interface ITorqueRecordDataAccess
|
||||
{
|
||||
public TorqueRecord GetTorqueRecordById(string torqueRecordId);
|
||||
public bool DeleteTorqueRecordById(int torqueRecordId);
|
||||
public bool SaveTorqueRecord(TorqueRecord torqueRecord);
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,8 @@ namespace CarCareTracker.Helper
|
||||
HideZero = bool.Parse(_config[nameof(UserConfig.HideZero)]),
|
||||
UseUKMPG = bool.Parse(_config[nameof(UserConfig.UseUKMPG)]),
|
||||
UseThreeDecimalGasCost = bool.Parse(_config[nameof(UserConfig.UseThreeDecimalGasCost)]),
|
||||
EnableAutoReminderRefresh = bool.Parse(_config[nameof(UserConfig.EnableAutoReminderRefresh)]),
|
||||
EnableAutoOdometerInsert = bool.Parse(_config[nameof(UserConfig.EnableAutoOdometerInsert)]),
|
||||
VisibleTabs = _config.GetSection("VisibleTabs").Get<List<ImportMode>>(),
|
||||
DefaultTab = (ImportMode)int.Parse(_config[nameof(UserConfig.DefaultTab)])
|
||||
};
|
||||
|
||||
@@ -5,9 +5,24 @@ namespace CarCareTracker.Helper
|
||||
public interface IGasHelper
|
||||
{
|
||||
List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG);
|
||||
string GetAverageGasMileage(List<GasRecordViewModel> results);
|
||||
}
|
||||
public class GasHelper : IGasHelper
|
||||
{
|
||||
public string GetAverageGasMileage(List<GasRecordViewModel> results)
|
||||
{
|
||||
var recordWithCalculatedMPG = results.Where(x => x.MilesPerGallon > 0);
|
||||
var minMileage = results.Min(x => x.Mileage);
|
||||
if (recordWithCalculatedMPG.Any())
|
||||
{
|
||||
var maxMileage = recordWithCalculatedMPG.Max(x => x.Mileage);
|
||||
var totalGallonsConsumed = recordWithCalculatedMPG.Sum(x => x.Gallons);
|
||||
var deltaMileage = maxMileage - minMileage;
|
||||
var averageGasMileage = (maxMileage - minMileage) / totalGallonsConsumed;
|
||||
return averageGasMileage.ToString("F");
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
public List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG)
|
||||
{
|
||||
var computedResults = new List<GasRecordViewModel>();
|
||||
|
||||
@@ -4,10 +4,28 @@ namespace CarCareTracker.Helper
|
||||
{
|
||||
public interface IReminderHelper
|
||||
{
|
||||
ReminderRecord GetUpdatedRecurringReminderRecord(ReminderRecord existingReminder);
|
||||
List<ReminderRecordViewModel> GetReminderRecordViewModels(List<ReminderRecord> reminders, int currentMileage, DateTime dateCompare);
|
||||
}
|
||||
public class ReminderHelper: IReminderHelper
|
||||
{
|
||||
public ReminderRecord GetUpdatedRecurringReminderRecord(ReminderRecord existingReminder)
|
||||
{
|
||||
if (existingReminder.Metric == ReminderMetric.Both)
|
||||
{
|
||||
existingReminder.Date = existingReminder.Date.AddMonths((int)existingReminder.ReminderMonthInterval);
|
||||
existingReminder.Mileage += (int)existingReminder.ReminderMileageInterval;
|
||||
}
|
||||
else if (existingReminder.Metric == ReminderMetric.Odometer)
|
||||
{
|
||||
existingReminder.Mileage += (int)existingReminder.ReminderMileageInterval;
|
||||
}
|
||||
else if (existingReminder.Metric == ReminderMetric.Date)
|
||||
{
|
||||
existingReminder.Date = existingReminder.Date.AddMonths((int)existingReminder.ReminderMonthInterval);
|
||||
}
|
||||
return existingReminder;
|
||||
}
|
||||
public List<ReminderRecordViewModel> GetReminderRecordViewModels(List<ReminderRecord> reminders, int currentMileage, DateTime dateCompare)
|
||||
{
|
||||
List<ReminderRecordViewModel> reminderViewModels = new List<ReminderRecordViewModel>();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CarCareTracker.Models;
|
||||
using System.Globalization;
|
||||
|
||||
namespace CarCareTracker.Helper
|
||||
{
|
||||
@@ -63,5 +64,41 @@ namespace CarCareTracker.Helper
|
||||
}
|
||||
return "";
|
||||
}
|
||||
public static List<CostForVehicleByMonth> GetBaseLineCosts()
|
||||
{
|
||||
return new List<CostForVehicleByMonth>()
|
||||
{
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(1), MonthId = 1, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(2), MonthId = 2, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(3), MonthId = 3, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(4), MonthId = 4, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(5), MonthId = 5, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(6), MonthId = 6, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(7), MonthId = 7, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(8), MonthId = 8, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(9), MonthId = 9, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(10), MonthId = 10, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(11), MonthId = 11, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(12), MonthId = 12, Cost = 0M}
|
||||
};
|
||||
}
|
||||
public static List<CostForVehicleByMonth> GetBaseLineCostsNoMonthName()
|
||||
{
|
||||
return new List<CostForVehicleByMonth>()
|
||||
{
|
||||
new CostForVehicleByMonth { MonthId = 1, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 2, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 3, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 4, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 5, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 6, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 7, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 8, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 9, Cost = 0M},
|
||||
new CostForVehicleByMonth { MonthId = 10, Cost = 0M},
|
||||
new CostForVehicleByMonth { MonthId = 11, Cost = 0M},
|
||||
new CostForVehicleByMonth { MonthId = 12, Cost = 0M}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
public decimal Cost { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||
public List<SupplyUsage> Supplies { get; set; } = new List<SupplyUsage>();
|
||||
public CollisionRecord ToCollisionRecord() { return new CollisionRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Mileage = Mileage, Description = Description, Notes = Notes, Files = Files }; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
public class TaxRecordExportModel
|
||||
{
|
||||
public string Date { get; set; }
|
||||
public string Odometer { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public string Cost { get; set; }
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
public int VehicleId { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string NoteText { get; set; }
|
||||
public bool Pinned { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
public string Description { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||
public List<SupplyUsage> Supplies { get; set; } = new List<SupplyUsage>();
|
||||
public ImportMode ImportMode { get; set; }
|
||||
public PlanPriority Priority { get; set; }
|
||||
public PlanProgress Progress { get; set; }
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
public Vehicle VehicleData { get; set; }
|
||||
public List<GenericReportModel> VehicleHistory { get; set; }
|
||||
public string Odometer { get; set; }
|
||||
public decimal MPG { get; set; }
|
||||
public string MPG { get; set; }
|
||||
public decimal TotalCost { get; set; }
|
||||
public decimal TotalGasCost { get; set; }
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
public decimal Cost { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||
public List<SupplyUsage> Supplies { get; set; } = new List<SupplyUsage>();
|
||||
public ServiceRecord ToServiceRecord() { return new ServiceRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Mileage = Mileage, Description = Description, Notes = Notes, Files = Files }; }
|
||||
}
|
||||
}
|
||||
|
||||
7
Models/Supply/SupplyUsage.cs
Normal file
7
Models/Supply/SupplyUsage.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace CarCareTracker.Models
|
||||
{
|
||||
public class SupplyUsage {
|
||||
public int SupplyId { get; set; }
|
||||
public decimal Quantity { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
public string Description { get; set; }
|
||||
public decimal Cost { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public bool IsRecurring { get; set; } = false;
|
||||
public ReminderMonthInterval RecurringInterval { get; set; } = ReminderMonthInterval.OneYear;
|
||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,18 @@
|
||||
public string Description { get; set; }
|
||||
public decimal Cost { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public bool IsRecurring { get; set; } = false;
|
||||
public ReminderMonthInterval RecurringInterval { get; set; } = ReminderMonthInterval.ThreeMonths;
|
||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||
public TaxRecord ToTaxRecord() { return new TaxRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Description = Description, Notes = Notes, Files = Files }; }
|
||||
public TaxRecord ToTaxRecord() { return new TaxRecord {
|
||||
Id = Id,
|
||||
VehicleId = VehicleId,
|
||||
Date = DateTime.Parse(Date),
|
||||
Cost = Cost,
|
||||
Description = Description,
|
||||
Notes = Notes,
|
||||
IsRecurring = IsRecurring,
|
||||
RecurringInterval = RecurringInterval,
|
||||
Files = Files }; }
|
||||
}
|
||||
}
|
||||
|
||||
38
Models/Torque/TorqueRecord.cs
Normal file
38
Models/Torque/TorqueRecord.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using LiteDB;
|
||||
|
||||
namespace CarCareTracker.Models
|
||||
{
|
||||
public class TorqueRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Session Id provided by Torque
|
||||
/// </summary>
|
||||
[BsonId]
|
||||
public string Session { get; set; }
|
||||
/// <summary>
|
||||
/// VehicleId
|
||||
/// </summary>
|
||||
public int VehicleId { get; set; }
|
||||
/// <summary>
|
||||
/// Email Address
|
||||
/// </summary>
|
||||
public string Eml { get; set; }
|
||||
/// <summary>
|
||||
/// longitude
|
||||
/// </summary>
|
||||
public double kff1005 { get; set; }
|
||||
/// <summary>
|
||||
/// latitude
|
||||
/// </summary>
|
||||
public double kff1006 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculated fields.
|
||||
/// </summary>
|
||||
public double InitialLongitude { get; set; }
|
||||
public double InitialLatitude { get; set; }
|
||||
public double LastLongitude { get; set; }
|
||||
public double LastLatitude { get; set; }
|
||||
public double DistanceTraveled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
public decimal Cost { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||
public List<SupplyUsage> Supplies { get; set; } = new List<SupplyUsage>();
|
||||
public UpgradeRecord ToUpgradeRecord() { return new UpgradeRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Mileage = Mileage, Description = Description, Notes = Notes, Files = Files }; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
public bool HideZero { get; set; }
|
||||
public bool UseUKMPG {get;set;}
|
||||
public bool UseThreeDecimalGasCost { get; set; }
|
||||
public bool EnableAutoReminderRefresh { get; set; }
|
||||
public bool EnableAutoOdometerInsert { get; set; }
|
||||
public string UserNameHash { get; set; }
|
||||
public string UserPasswordHash { get; set;}
|
||||
public List<ImportMode> VisibleTabs { get; set; } = new List<ImportMode>() {
|
||||
|
||||
@@ -25,6 +25,7 @@ builder.Services.AddSingleton<IUserConfigDataAccess, UserConfigDataAccess>();
|
||||
builder.Services.AddSingleton<ISupplyRecordDataAccess, SupplyRecordDataAccess>();
|
||||
builder.Services.AddSingleton<IPlanRecordDataAccess, PlanRecordDataAccess>();
|
||||
builder.Services.AddSingleton<IOdometerRecordDataAccess, OdometerRecordDataAccess>();
|
||||
builder.Services.AddSingleton<ITorqueRecordDataAccess, TorqueRecordDataAccess>();
|
||||
|
||||
//configure helpers
|
||||
builder.Services.AddSingleton<IFileHelper, FileHelper>();
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<div class="row">
|
||||
@{
|
||||
ViewData["Title"] = "LubeLogger API";
|
||||
}
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-6 mt-2">API</h6>
|
||||
</div>
|
||||
@@ -51,6 +54,28 @@
|
||||
vehicleId - Id of Vehicle
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
POST
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<code>/api/vehicle/servicerecords/add</code>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
Adds Service Record to the vehicle
|
||||
</div>
|
||||
<div class="col-3">
|
||||
vehicleId - Id of Vehicle
|
||||
<br />
|
||||
Body(form-data): {<br />
|
||||
date - Date to be entered<br />
|
||||
odometer - Odometer reading<br />
|
||||
description - Description<br/>
|
||||
cost - Cost<br />
|
||||
notes - notes(optional)<br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
GET
|
||||
@@ -65,6 +90,28 @@
|
||||
vehicleId - Id of Vehicle
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
POST
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<code>/api/vehicle/repairrecords/add</code>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
Adds Repair Record to the vehicle
|
||||
</div>
|
||||
<div class="col-3">
|
||||
vehicleId - Id of Vehicle
|
||||
<br />
|
||||
Body(form-data): {<br />
|
||||
date - Date to be entered<br />
|
||||
odometer - Odometer reading<br />
|
||||
description - Description<br />
|
||||
cost - Cost<br />
|
||||
notes - notes(optional)<br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
GET
|
||||
@@ -79,6 +126,28 @@
|
||||
vehicleId - Id of Vehicle
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
POST
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<code>/api/vehicle/upgraderecords/add</code>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
Adds Upgrade Record to the vehicle
|
||||
</div>
|
||||
<div class="col-3">
|
||||
vehicleId - Id of Vehicle
|
||||
<br />
|
||||
Body(form-data): {<br />
|
||||
date - Date to be entered<br />
|
||||
odometer - Odometer reading<br />
|
||||
description - Description<br />
|
||||
cost - Cost<br />
|
||||
notes - notes(optional)<br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
GET
|
||||
@@ -93,6 +162,27 @@
|
||||
vehicleId - Id of Vehicle
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
POST
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<code>/api/vehicle/taxrecords/add</code>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
Adds Tax Record to the vehicle
|
||||
</div>
|
||||
<div class="col-3">
|
||||
vehicleId - Id of Vehicle
|
||||
<br />
|
||||
Body(form-data): {<br />
|
||||
date - Date to be entered<br />
|
||||
description - Description<br />
|
||||
cost - Cost<br />
|
||||
notes - notes(optional)<br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
GET
|
||||
@@ -111,6 +201,30 @@
|
||||
useUKMPG(bool) - Use UK Imperial Calculation
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
POST
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<code>/api/vehicle/gasrecords/add</code>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
Adds Gas Record to the vehicle
|
||||
</div>
|
||||
<div class="col-3">
|
||||
vehicleId - Id of Vehicle
|
||||
<br />
|
||||
Body(form-data): {<br />
|
||||
date - Date to be entered<br />
|
||||
odometer - Odometer reading<br />
|
||||
fuelConsumed - Fuel Consumed<br />
|
||||
cost - Cost<br />
|
||||
isFillToFull(bool) - Filled To Full<br />
|
||||
missedFuelUp(bool) - Missed Fuel Up<br />
|
||||
notes - notes(optional)<br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1">
|
||||
GET
|
||||
@@ -164,7 +278,7 @@
|
||||
<code>/api/vehicle/odometerrecords/add</code>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
Returns a list of odometer records for the vehicle
|
||||
Adds Odometer Record to the vehicle
|
||||
</div>
|
||||
<div class="col-3">
|
||||
vehicleId - Id of Vehicle
|
||||
|
||||
@@ -35,6 +35,14 @@
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useThreeDecimal" checked="@Model.UseThreeDecimalGasCost">
|
||||
<label class="form-check-label" for="useThreeDecimal">Use Three Decimals For Fuel Cost</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoReminderRefresh" checked="@Model.EnableAutoReminderRefresh">
|
||||
<label class="form-check-label" for="enableAutoReminderRefresh">Auto Refresh Lapsed Recurring Reminders</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoOdometerInsert" checked="@Model.EnableAutoOdometerInsert">
|
||||
<label class="form-check-label" for="enableAutoOdometerInsert">Auto Insert Odometer Records<br /><small class="text-body-secondary">Only when Adding Service/Repair/Upgrade/Fuel Record or Completing a Plan</small></label>
|
||||
</div>
|
||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||
{
|
||||
<div class="form-check form-switch">
|
||||
@@ -146,7 +154,7 @@
|
||||
<img src="/defaults/lubelogger_logo.png" />
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<small class="text-body-secondary">Version 1.0.6</small>
|
||||
<small class="text-body-secondary">Version 1.0.7</small>
|
||||
</div>
|
||||
<p class="lead">
|
||||
Proudly developed in the rural town of Price, Utah by Hargata Softworks.
|
||||
@@ -203,6 +211,8 @@
|
||||
hideZero: $("#hideZero").is(":checked"),
|
||||
useUKMpg: $("#useUKMPG").is(":checked"),
|
||||
useThreeDecimalGasCost: $("#useThreeDecimal").is(":checked"),
|
||||
enableAutoReminderRefresh: $("#enableAutoReminderRefresh").is(":checked"),
|
||||
enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"),
|
||||
visibleTabs: visibleTabs,
|
||||
defaultTab: defaultTab
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
var useDarkMode = userConfig.UseDarkMode;
|
||||
var enableCsvImports = userConfig.EnableCsvImports;
|
||||
var shortDatePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
|
||||
var numberFormat = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
|
||||
shortDatePattern = shortDatePattern.ToLower();
|
||||
if (!shortDatePattern.Contains("dd"))
|
||||
{
|
||||
@@ -54,6 +55,21 @@
|
||||
pattern: "@shortDatePattern"
|
||||
}
|
||||
}
|
||||
function globalParseFloat(input){
|
||||
//remove thousands separator.
|
||||
var thousandSeparator = "@numberFormat.NumberGroupSeparator";
|
||||
var decimalSeparator = "@numberFormat.NumberDecimalSeparator";
|
||||
//strip thousands from input.
|
||||
input = input.replace(thousandSeparator, "");
|
||||
//convert to JS format where decimal is only separated by .
|
||||
input = input.replace(decimalSeparator, ".");
|
||||
return parseFloat(input);
|
||||
}
|
||||
function globalFloatToString(input) {
|
||||
var decimalSeparator = "@numberFormat.NumberDecimalSeparator";
|
||||
input = input.replace(".", decimalSeparator);
|
||||
return input;
|
||||
}
|
||||
</script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</head>
|
||||
|
||||
@@ -153,6 +153,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" data-bs-focus="false" id="inputSuppliesModal" tabindex="-1" role="dialog" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="inputSuppliesModalContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function GetVehicleId() {
|
||||
return { vehicleId: @Model.Id};
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
<input type="text" id="collisionRecordDescription" class="form-control" placeholder="Description of item(s) repaired(i.e. Alternator)" value="@Model.Description">
|
||||
<label for="collisionRecordCost">Cost</label>
|
||||
<input type="text" id="collisionRecordCost" class="form-control" placeholder="Cost of the repair" value="@(isNew ? "" : Model.Cost)">
|
||||
@if (isNew)
|
||||
{
|
||||
@await Html.PartialAsync("_SupplyStore", "RepairRecord")
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="collisionRecordNotes">Notes(optional)</label>
|
||||
@@ -71,6 +75,7 @@
|
||||
</div>
|
||||
<script>
|
||||
var uploadedFiles = [];
|
||||
var selectedSupplies = [];
|
||||
getUploadedFilesFromModel();
|
||||
function getUploadedFilesFromModel() {
|
||||
@foreach (UploadedFiles filesUploaded in Model.Files)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@using CarCareTracker.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject IGasHelper gasHelper
|
||||
@model GasRecordViewModelContainer
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
@@ -40,7 +41,7 @@
|
||||
<span class="ms-2 badge bg-success">@($"# of Gas Records: {Model.GasRecords.Count()}")</span>
|
||||
@if (Model.GasRecords.Where(x => x.MilesPerGallon > 0).Any())
|
||||
{
|
||||
<span class="ms-2 badge bg-primary">@($"Average Fuel Economy: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Average(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||
<span class="ms-2 badge bg-primary">@($"Average Fuel Economy: {gasHelper.GetAverageGasMileage(Model.GasRecords)}")</span>
|
||||
<span class="ms-2 badge bg-primary">@($"Min Fuel Economy: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Min(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||
<span class="ms-2 badge bg-primary">@($"Max Fuel Economy: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
@model List<CostForVehicleByMonth>
|
||||
@if (Model.Any())
|
||||
@{
|
||||
var barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
|
||||
var sortedByMPG = Model.OrderBy(x => x.Cost).ToList();
|
||||
}
|
||||
@if (Model.Where(x=>x.Cost > 0).Any())
|
||||
{
|
||||
<canvas id="bar-chart"></canvas>
|
||||
<script>
|
||||
@@ -7,11 +11,15 @@
|
||||
function renderChart() {
|
||||
var barGraphLabels = [];
|
||||
var barGraphData = [];
|
||||
//color gradient from high to low
|
||||
var barGraphColors = [];
|
||||
var useDarkMode = getGlobalConfig().useDarkMode;
|
||||
@foreach (CostForVehicleByMonth gasCost in Model)
|
||||
{
|
||||
@:barGraphLabels.push("@gasCost.MonthName");
|
||||
@:barGraphData.push(@gasCost.Cost);
|
||||
var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName);
|
||||
@:barGraphColors.push('@barGraphColors[index]');
|
||||
}
|
||||
new Chart($("#bar-chart"), {
|
||||
type: 'bar',
|
||||
@@ -20,14 +28,20 @@
|
||||
datasets: [
|
||||
{
|
||||
label: "Expenses by Month",
|
||||
backgroundColor: ["#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f"],
|
||||
backgroundColor: barGraphColors,
|
||||
data: barGraphData
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
color: useDarkMode ? "#fff" : "#000",
|
||||
text: 'Expenses by Month'
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
color: useDarkMode ? "#fff" : "#000"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
@model List<CostForVehicleByMonth>
|
||||
@if (Model.Any())
|
||||
@{
|
||||
var barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
|
||||
var sortedByMPG = Model.OrderByDescending(x => x.Cost).ToList();
|
||||
}
|
||||
@if (Model.Where(x=>x.Cost > 0).Any())
|
||||
{
|
||||
|
||||
<canvas id="bar-chart-mpg"></canvas>
|
||||
<script>
|
||||
renderChart();
|
||||
function renderChart() {
|
||||
var barGraphLabels = [];
|
||||
var barGraphData = [];
|
||||
//color gradient from high to low
|
||||
var barGraphColors = [];
|
||||
var useDarkMode = getGlobalConfig().useDarkMode;
|
||||
@foreach (CostForVehicleByMonth gasCost in Model)
|
||||
{
|
||||
@:barGraphLabels.push("@gasCost.MonthName");
|
||||
@:barGraphData.push(@gasCost.Cost);
|
||||
var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName);
|
||||
@:barGraphColors.push('@barGraphColors[index]');
|
||||
}
|
||||
new Chart($("#bar-chart-mpg"), {
|
||||
type: 'bar',
|
||||
@@ -20,14 +29,20 @@
|
||||
datasets: [
|
||||
{
|
||||
label: "Fuel Mileage by Month",
|
||||
backgroundColor: ["#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f"],
|
||||
backgroundColor: barGraphColors,
|
||||
data: barGraphData
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
color: useDarkMode ? "#fff" : "#000",
|
||||
text: 'Fuel Mileage by Month'
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
color: useDarkMode ? "#fff" : "#000"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="noteIsPinned" checked="@Model.Pinned">
|
||||
<label class="form-check-label" for="noteIsPinned">Pinned</label>
|
||||
</div>
|
||||
<label for="noteDescription">Description</label>
|
||||
<input type="text" id="noteDescription" class="form-control" placeholder="Description of the note" value="@(isNew ? "" : Model.Description)">
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,13 @@
|
||||
@foreach (Note note in Model)
|
||||
{
|
||||
<tr class="d-flex" style="cursor:pointer;" onclick="showEditNoteModal(@note.Id)">
|
||||
@if (note.Pinned)
|
||||
{
|
||||
<td class="col-3"><i class='bi bi-pin-fill me-2'></i>@note.Description"</td>
|
||||
} else
|
||||
{
|
||||
<td class="col-3">@note.Description</td>
|
||||
}
|
||||
<td class="col-9 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(note.NoteText, 100)</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<tr class="d-flex" style="cursor:pointer;" onclick="showEditOdometerRecordModal(@odometerRecord.Id)">
|
||||
<td class="col-2 col-xl-1">@odometerRecord.Date.ToShortDateString()</td>
|
||||
<td class="col-3">@odometerRecord.Mileage</td>
|
||||
<td class="col-7 col-xl-8 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes)</td>
|
||||
<td class="col-7 col-xl-8 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes, 75)</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@model PlanRecord
|
||||
<div class="taskCard @(Model.Progress == PlanProgress.Done ? "nodrag" : "") text-dark user-select-none mb-2" draggable="@(Model.Progress == PlanProgress.Done ? "false" : "true")" ondragstart="dragStart(event, @Model.Id)" onclick="@(Model.Progress == PlanProgress.Done ? $"deletePlanRecord({Model.Id})" : $"showEditPlanRecordModal({Model.Id})")">
|
||||
<div class="taskCard @(Model.Progress == PlanProgress.Done ? "nodrag" : "") text-dark user-select-none mt-2 mb-2" draggable="@(Model.Progress == PlanProgress.Done ? "false" : "true")" ondragstart="dragStart(event, @Model.Id)" onclick="@(Model.Progress == PlanProgress.Done ? $"deletePlanRecord({Model.Id})" : $"showEditPlanRecordModal({Model.Id})")">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-8 text-truncate">
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
<input type="text" id="planRecordDescription" class="form-control" placeholder="Describe the Plan" value="@Model.Description">
|
||||
<label for="planRecordCost">Cost</label>
|
||||
<input type="text" id="planRecordCost" class="form-control" placeholder="Cost of the Plan" value="@Model.Cost">
|
||||
@if (isNew)
|
||||
{
|
||||
@await Html.PartialAsync("_SupplyStore", "PlanRecord")
|
||||
}
|
||||
<label for="planRecordType">Type</label>
|
||||
<select class="form-select" id="planRecordType">
|
||||
<!option value="ServiceRecord" @(Model.ImportMode == ImportMode.ServiceRecord || isNew ? "selected" : "")>Service</!option>
|
||||
@@ -78,6 +82,7 @@
|
||||
</div>
|
||||
<script>
|
||||
var uploadedFiles = [];
|
||||
var selectedSupplies = [];
|
||||
getUploadedFilesFromModel();
|
||||
function getUploadedFilesFromModel() {
|
||||
@foreach (UploadedFiles filesUploaded in Model.Files)
|
||||
|
||||
@@ -47,19 +47,29 @@
|
||||
</div>
|
||||
<label for="reminderRecurringMileage">Odometer</label>
|
||||
<select class="form-select" id="reminderRecurringMileage" @(Model.IsRecurring ? "" : "disabled")>
|
||||
<!option value="FiveHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiveHundredMiles || isNew ? "selected" : "")>500 mi. / Km</!option>
|
||||
<!option value="FiftyMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiftyMiles ? "selected" : "")>50 mi. / Km</!option>
|
||||
<!option value="OneHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredMiles ? "selected" : "")>100 mi. / Km</!option>
|
||||
<!option value="FiveHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiveHundredMiles ? "selected" : "")>500 mi. / Km</!option>
|
||||
<!option value="OneThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneThousandMiles ? "selected" : "")>1000 mi. / Km</!option>
|
||||
<!option value="ThreeThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.ThreeThousandMiles ? "selected" : "")>3000 mi. / Km</!option>
|
||||
<!option value="FourThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FourThousandMiles ? "selected" : "")>4000 mi. / Km</!option>
|
||||
<!option value="FiveThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiveThousandMiles || isNew ? "selected" : "")>5000 mi. / Km</!option>
|
||||
<!option value="SevenThousandFiveHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.SevenThousandFiveHundredMiles ? "selected" : "")>7500 mi. / Km</!option>
|
||||
<!option value="TenThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.TenThousandMiles ? "selected" : "")>10000 mi. / Km</!option>
|
||||
<!option value="FifteenThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FifteenThousandMiles ? "selected" : "")>15000 mi. / Km</!option>
|
||||
<!option value="TwentyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.TwentyThousandMiles ? "selected" : "")>20000 mi. / Km</!option>
|
||||
<!option value="ThirtyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.ThirtyThousandMiles ? "selected" : "")>30000 mi. / Km</!option>
|
||||
<!option value="FiftyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiftyThousandMiles ? "selected" : "")>50000 mi. / Km</!option>
|
||||
<!option value="OneHundredThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredThousandMiles ? "selected" : "")>100000 mi. / Km</!option>
|
||||
<!option value="OneHundredFiftyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredFiftyThousandMiles ? "selected" : "")>150000 mi. / Km</!option>
|
||||
</select>
|
||||
<label for="reminderRecurringMonth">Month</label>
|
||||
<select class="form-select" id="reminderRecurringMonth" @(Model.IsRecurring ? "" : "disabled")>
|
||||
<!option value="ThreeMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>3 Months</!option>
|
||||
<!option value="SixMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>6 Months</!option>
|
||||
<!option value="OneYear" @(Model.ReminderMonthInterval == ReminderMonthInterval.OneYear ? "selected" : "")>1 Year</!option>
|
||||
<!option value="TwoYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>2 Years</!option>
|
||||
<!option value="ThreeYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>3 Years</!option>
|
||||
<!option value="FiveYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>5 Years</!option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@model List<ReminderRecordViewModel>
|
||||
@{
|
||||
var hasRefresh = Model.Where(x => (x.Urgency == ReminderUrgency.VeryUrgent || x.Urgency == ReminderUrgency.PastDue) && x.IsRecurring).Any();
|
||||
}
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="d-flex align-items-center flex-wrap">
|
||||
@@ -25,8 +28,12 @@
|
||||
<tr class="d-flex">
|
||||
<th scope="col" class="col-1">Urgency</th>
|
||||
<th scope="col" class="col-2">Metric</th>
|
||||
<th scope="col" class="col-5">Description</th>
|
||||
<th scope="col" class="@(hasRefresh ? "col-4" : "col-5")">Description</th>
|
||||
<th scope="col" class="col-3">Notes</th>
|
||||
@if (hasRefresh)
|
||||
{
|
||||
<th scope="col" class="col-1">Done</th>
|
||||
}
|
||||
<th scope="col" class="col-1">Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -62,8 +69,17 @@
|
||||
{
|
||||
<td class="col-2">@reminderRecord.Metric</td>
|
||||
}
|
||||
<td class="col-5">@reminderRecord.Description</td>
|
||||
<td class="@(hasRefresh ? "col-4" : "col-5")">@reminderRecord.Description</td>
|
||||
<td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(reminderRecord.Notes)</td>
|
||||
@if (hasRefresh)
|
||||
{
|
||||
<td class="col-1 text-truncate">
|
||||
@if((reminderRecord.Urgency == ReminderUrgency.VeryUrgent || reminderRecord.Urgency == ReminderUrgency.PastDue) && reminderRecord.IsRecurring)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" onclick="markDoneReminderRecord(@reminderRecord.Id, this)"><i class="bi bi-check-lg"></i></button>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
<td class="col-1 text-truncate">
|
||||
<button type="button" class="btn btn-danger" onclick="deleteReminderRecord(@reminderRecord.Id, this)"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
|
||||
@@ -24,23 +24,23 @@
|
||||
<div class="col-md-1 d-sm-none d-md-block"></div>
|
||||
<div class="col-12 col-md-10 reportsCheckBoxContainer">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" onChange="updateCheck(this)" type="checkbox" id="serviceExpenseCheck" value="1" checked>
|
||||
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="serviceExpenseCheck" value="1" checked>
|
||||
<label class="form-check-label" for="serviceExpenseCheck">Service</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" onChange="updateCheck(this)" type="checkbox" id="repairExpenseCheck" value="2" checked>
|
||||
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="repairExpenseCheck" value="2" checked>
|
||||
<label class="form-check-label" for="repairExpenseCheck">Repairs</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" onChange="updateCheck(this)" type="checkbox" id="upgradeExpenseCheck" value="3" checked>
|
||||
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="upgradeExpenseCheck" value="3" checked>
|
||||
<label class="form-check-label" for="upgradeExpenseCheck">Upgrades</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" onChange="updateCheck(this)" type="checkbox" id="gasExpenseCheck" value="4" checked>
|
||||
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="gasExpenseCheck" value="4" checked>
|
||||
<label class="form-check-label" for="gasExpenseCheck">Gas</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" onChange="updateCheck(this)" type="checkbox" id="taxExpenseCheck" value="5" checked>
|
||||
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="taxExpenseCheck" value="5" checked>
|
||||
<label class="form-check-label" for="taxExpenseCheck">Tax</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
<input type="text" id="serviceRecordDescription" class="form-control" placeholder="Description of item(s) serviced(i.e. Oil Change)" value="@Model.Description">
|
||||
<label for="serviceRecordCost">Cost</label>
|
||||
<input type="text" id="serviceRecordCost" class="form-control" placeholder="Cost of the service" value="@(isNew ? "" : Model.Cost)">
|
||||
@if (isNew)
|
||||
{
|
||||
@await Html.PartialAsync("_SupplyStore", "ServiceRecord")
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="serviceRecordNotes">Notes(optional)</label>
|
||||
@@ -71,6 +75,7 @@
|
||||
</div>
|
||||
<script>
|
||||
var uploadedFiles = [];
|
||||
var selectedSupplies = [];
|
||||
getUploadedFilesFromModel();
|
||||
function getUploadedFilesFromModel() {
|
||||
@foreach (UploadedFiles filesUploaded in Model.Files)
|
||||
|
||||
91
Views/Vehicle/_SupplyStore.cshtml
Normal file
91
Views/Vehicle/_SupplyStore.cshtml
Normal file
@@ -0,0 +1,91 @@
|
||||
@model string
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<a onclick="showSuppliesModal()" class="btn btn-link">Choose Supplies</a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
resetSuppliesModal();
|
||||
function GetCaller() {
|
||||
return { tab: '@Model' };
|
||||
}
|
||||
function resetSuppliesModal() {
|
||||
$("#inputSuppliesModalContent").html("");
|
||||
}
|
||||
function selectSupplies() {
|
||||
var selectedSupplyResult = getSuppliesAndQuantity();
|
||||
var caller = GetCaller().tab;
|
||||
switch (caller) {
|
||||
case "ServiceRecord":
|
||||
$('#serviceRecordCost').val(selectedSupplyResult.totalSum);
|
||||
break;
|
||||
case "RepairRecord":
|
||||
$('#collisionRecordCost').val(selectedSupplyResult.totalSum);
|
||||
break;
|
||||
case "UpgradeRecord":
|
||||
$('#upgradeRecordCost').val(selectedSupplyResult.totalSum);
|
||||
break;
|
||||
case "PlanRecord":
|
||||
$('#planRecordCost').val(selectedSupplyResult.totalSum);
|
||||
break;
|
||||
}
|
||||
selectedSupplies = getSuppliesAndQuantity().selectedSupplies;
|
||||
hideSuppliesModal();
|
||||
}
|
||||
function hideParentModal(){
|
||||
var caller = GetCaller().tab;
|
||||
switch (caller) {
|
||||
case "ServiceRecord":
|
||||
$('#serviceRecordModal').modal('hide');
|
||||
break;
|
||||
case "RepairRecord":
|
||||
$('#collisionRecordModal').modal('hide');
|
||||
break;
|
||||
case "UpgradeRecord":
|
||||
$('#upgradeRecordModal').modal('hide');
|
||||
break;
|
||||
case "PlanRecord":
|
||||
$('#planRecordModal').modal('hide');
|
||||
break;
|
||||
}
|
||||
}
|
||||
function showParentModal() {
|
||||
var caller = GetCaller().tab;
|
||||
switch (caller) {
|
||||
case "ServiceRecord":
|
||||
$('#serviceRecordModal').modal('show');
|
||||
break;
|
||||
case "RepairRecord":
|
||||
$('#collisionRecordModal').modal('show');
|
||||
break;
|
||||
case "UpgradeRecord":
|
||||
$('#upgradeRecordModal').modal('show');
|
||||
break;
|
||||
case "PlanRecord":
|
||||
$('#planRecordModal').modal('show');
|
||||
break;
|
||||
}
|
||||
}
|
||||
function showSuppliesModal() {
|
||||
if ($("#inputSuppliesModalContent").html() == "") {
|
||||
getSupplies();
|
||||
} else {
|
||||
hideParentModal();
|
||||
$('#inputSuppliesModal').modal('show');
|
||||
}
|
||||
}
|
||||
function getSupplies() {
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
$.get(`/Vehicle/GetSupplyRecordsForRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
||||
if (data) {
|
||||
hideParentModal();
|
||||
$("#inputSuppliesModalContent").html(data);
|
||||
$('#inputSuppliesModal').modal('show');
|
||||
}
|
||||
})
|
||||
}
|
||||
function hideSuppliesModal() {
|
||||
$('#inputSuppliesModal').modal('hide');
|
||||
showParentModal();
|
||||
}
|
||||
</script>
|
||||
127
Views/Vehicle/_SupplyUsage.cshtml
Normal file
127
Views/Vehicle/_SupplyUsage.cshtml
Normal file
@@ -0,0 +1,127 @@
|
||||
@model List<SupplyRecord>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Select Supplies</h5>
|
||||
<button type="button" class="btn-close" onclick="hideSuppliesModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if (Model.Any())
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12" style="max-height:50vh; overflow-y:auto;">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Supplies are requisitioned immediately after the record is created and cannot be modified.
|
||||
If you have incorrectly entered the amount you needed you will need to correct it in the Supplies tab.
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
<thead class="sticky-top">
|
||||
<tr class="d-flex">
|
||||
<th scope="col" class="col-1"></th>
|
||||
<th scope="col" class="col-2">Quantity.</th>
|
||||
<th scope="col" class="col-2">In Stock</th>
|
||||
<th scope="col" class="col-5">Description</th>
|
||||
<th scope="col" class="col-2">Unit Cost</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (SupplyRecord supplyRecord in Model)
|
||||
{
|
||||
<tr class="d-flex" id="supplyRows">
|
||||
<td class="col-1"><input class="form-check-input" type="checkbox" onchange="toggleQuantityFieldDisabled(this)" value="@supplyRecord.Id"></td>
|
||||
<td class="col-2"><input type="text" disabled onchange="recalculateTotal()" class="form-control"></td>
|
||||
<td class="col-2 supplyquantity">@supplyRecord.Quantity</td>
|
||||
<td class="col-5">@supplyRecord.Description</td>
|
||||
<td class="col-2 supplyprice">@((supplyRecord.Quantity > 0 ? supplyRecord.Cost / supplyRecord.Quantity : 0).ToString("F"))</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="text-center">
|
||||
<h4>No supplies with quantities greater than 0 is found.</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span id="supplySumLabel" style="margin-right:auto;">Total: 0.00</span>
|
||||
<button type="button" class="btn btn-secondary" onclick="hideSuppliesModal()">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" disabled id="selectSuppliesButton" onclick="selectSupplies()">Select</button>
|
||||
</div>
|
||||
<script>
|
||||
function recalculateTotal() {
|
||||
setDebounce(getSuppliesAndQuantity);
|
||||
}
|
||||
function toggleQuantityFieldDisabled(e) {
|
||||
var textField = getTextFieldFromCheckBox(e);
|
||||
var isChecked = $(e).is(":checked");
|
||||
textField.attr('disabled', !isChecked);
|
||||
if (!isChecked) {
|
||||
textField.removeClass("is-invalid");
|
||||
}
|
||||
recalculateTotal();
|
||||
}
|
||||
function getTextFieldFromCheckBox(elem) {
|
||||
var textField = $(elem.parentElement.parentElement).find('.col-2 > input[type=text]')[0];
|
||||
return $(textField);
|
||||
}
|
||||
function getInStockFieldFromCheckBox(elem) {
|
||||
var textField = $(elem.parentElement.parentElement).find('.col-2.supplyquantity')[0];
|
||||
return $(textField);
|
||||
}
|
||||
function getPriceFieldFromCheckBox(elem) {
|
||||
var textField = $(elem.parentElement.parentElement).find('.col-2.supplyprice')[0];
|
||||
return $(textField);
|
||||
}
|
||||
function getSuppliesAndQuantity() {
|
||||
var totalSum = 0;
|
||||
var hasError = false;
|
||||
var selectedSupplies = $("#supplyRows :checked").map(function () {
|
||||
var textField = getTextFieldFromCheckBox(this);
|
||||
var inStock = getInStockFieldFromCheckBox(this);
|
||||
var priceField = getPriceFieldFromCheckBox(this);
|
||||
var requestedQuantity = globalParseFloat(textField.val());
|
||||
var inStockQuantity = globalParseFloat(inStock.text());
|
||||
var unitPrice = globalParseFloat(priceField.text());
|
||||
//validation
|
||||
if (isNaN(requestedQuantity) || requestedQuantity > inStockQuantity) {
|
||||
textField.addClass("is-invalid");
|
||||
hasError = true;
|
||||
} else {
|
||||
textField.removeClass("is-invalid");
|
||||
}
|
||||
//calculate sum.
|
||||
var sum = requestedQuantity * unitPrice;
|
||||
totalSum += sum;
|
||||
return {
|
||||
supplyId: this.value,
|
||||
quantity: textField.val()
|
||||
};
|
||||
});
|
||||
if (isNaN(totalSum) || hasError) {
|
||||
$("#supplySumLabel").text(`Total: 0.00`);
|
||||
} else {
|
||||
totalSum = totalSum.toFixed(2);
|
||||
var parsedFloat = globalFloatToString(totalSum);
|
||||
$("#supplySumLabel").text(`Total: ${parsedFloat}`);
|
||||
}
|
||||
$("#selectSuppliesButton").attr('disabled', (hasError || totalSum == 0));
|
||||
if (!hasError) {
|
||||
return {
|
||||
totalSum: totalSum,
|
||||
selectedSupplies: selectedSupplies.toArray()
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
totalSum: 0,
|
||||
selectedSupplies: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -25,6 +25,20 @@
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="taxRecordNotes">Notes(optional)</label>
|
||||
<textarea id="taxRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" onChange="enableTaxRecurring()" role="switch" id="taxIsRecurring" checked="@Model.IsRecurring">
|
||||
<label class="form-check-label" for="taxIsRecurring">Is Recurring</label>
|
||||
</div>
|
||||
<label for="taxRecurringMonth">Month</label>
|
||||
<select class="form-select" id="taxRecurringMonth" @(Model.IsRecurring ? "" : "disabled")>
|
||||
<!option value="OneMonth" @(Model.RecurringInterval == ReminderMonthInterval.OneMonth ? "selected" : "")>1 Month</!option>
|
||||
<!option value="ThreeMonths" @(Model.RecurringInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>3 Months</!option>
|
||||
<!option value="SixMonths" @(Model.RecurringInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>6 Months</!option>
|
||||
<!option value="OneYear" @(Model.RecurringInterval == ReminderMonthInterval.OneYear ? "selected" : "")>1 Year</!option>
|
||||
<!option value="TwoYears" @(Model.RecurringInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>2 Years</!option>
|
||||
<!option value="ThreeYears" @(Model.RecurringInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>3 Years</!option>
|
||||
<!option value="FiveYears" @(Model.RecurringInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>5 Years</!option>
|
||||
</select>
|
||||
@if (Model.Files.Any())
|
||||
{
|
||||
<div>
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
<input type="text" id="upgradeRecordDescription" class="form-control" placeholder="Description of item(s) upgraded/modded" value="@Model.Description">
|
||||
<label for="upgradeRecordCost">Cost</label>
|
||||
<input type="text" id="upgradeRecordCost" class="form-control" placeholder="Cost of the upgrade/mods" value="@(isNew ? "" : Model.Cost)">
|
||||
@if (isNew)
|
||||
{
|
||||
@await Html.PartialAsync("_SupplyStore", "UpgradeRecord")
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="upgradeRecordNotes">Notes(optional)</label>
|
||||
@@ -71,6 +75,7 @@
|
||||
</div>
|
||||
<script>
|
||||
var uploadedFiles = [];
|
||||
var selectedSupplies = [];
|
||||
getUploadedFilesFromModel();
|
||||
function getUploadedFilesFromModel() {
|
||||
@foreach (UploadedFiles filesUploaded in Model.Files)
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<div class="col-6">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Last Reported Odometer Reading: @Model.Odometer</li>
|
||||
<li class="list-group-item">Average Fuel Economy: @($"{Model.MPG.ToString("F")} {fuelEconomyUnit}")</li>
|
||||
<li class="list-group-item">Average Fuel Economy: @($"{Model.MPG} {fuelEconomyUnit}")</li>
|
||||
<li class="list-group-item">Total Spent(excl. fuel): @Model.TotalCost.ToString("C")</li>
|
||||
<li class="list-group-item">Total Spent on Fuel: @Model.TotalGasCost.ToString("C")</li>
|
||||
</ul>
|
||||
@@ -68,8 +68,8 @@
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="d-flex">
|
||||
<th scope="col" class="col-2">Type</th>
|
||||
<th scope="col" class="col-1">Date</th>
|
||||
<th scope="col" class="col-2 servicehistorytype">Type</th>
|
||||
<th scope="col" class="col-1 servicehistorydate">Date</th>
|
||||
<th scope="col" class="col-1">Odometer</th>
|
||||
<th scope="col" class="col-3">Description</th>
|
||||
<th scope="col" class="col-1">Cost</th>
|
||||
@@ -80,7 +80,7 @@
|
||||
@foreach (GenericReportModel reportData in Model.VehicleHistory)
|
||||
{
|
||||
<tr class="d-flex">
|
||||
<td class="col-2">
|
||||
<td class="col-2 servicehistorytype">
|
||||
@if(reportData.DataType == ImportMode.ServiceRecord)
|
||||
{
|
||||
<span><i class="bi bi-card-checklist me-2"></i>Service</span>
|
||||
@@ -95,7 +95,7 @@
|
||||
<span><i class="bi bi-currency-dollar me-2"></i>Tax</span>
|
||||
}
|
||||
</td>
|
||||
<td class="col-1">@reportData.Date.ToShortDateString()</td>
|
||||
<td class="col-1 servicehistorydate">@reportData.Date.ToShortDateString()</td>
|
||||
<td class="col-1">@(reportData.Odometer == default ? "---" : reportData.Odometer.ToString("N0"))</td>
|
||||
<td class="col-3">@reportData.Description</td>
|
||||
<td class="col-1">@((hideZero && reportData.Cost == default) ? "---" : reportData.Cost.ToString("C"))</td>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
"UseDescending": false,
|
||||
"EnableAuth": false,
|
||||
"HideZero": false,
|
||||
"EnableAutoReminderRefresh": false,
|
||||
"EnableAutoOdometerInsert": false,
|
||||
"UseUKMPG": false,
|
||||
"UseThreeDecimalGasCost": true,
|
||||
"VisibleTabs": [ 0, 1, 4, 2, 3, 6, 5, 8 ],
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
## Garage
|
||||

|
||||
|
||||
## Planner(Kanban Board)
|
||||

|
||||
|
||||
## Dashboard
|
||||

|
||||
|
||||
@@ -24,7 +27,7 @@
|
||||

|
||||
|
||||
## Settings
|
||||

|
||||

|
||||
|
||||
## Admin Panel (Generate Tokens for new users to sign up)
|
||||

|
||||
|
||||
@@ -111,6 +111,13 @@ html {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.col-2.servicehistorytype {
|
||||
width: 13%;
|
||||
}
|
||||
.col-1.servicehistorydate{
|
||||
width: 13%;
|
||||
}
|
||||
|
||||
th.col-1 {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ function getAndValidateCollisionRecordValues() {
|
||||
cost: collisionCost,
|
||||
notes: collisionNotes,
|
||||
files: uploadedFiles,
|
||||
supplies: selectedSupplies,
|
||||
addReminderRecord: addReminderRecord
|
||||
}
|
||||
}
|
||||
@@ -91,20 +91,20 @@ function getAndValidateGasRecordValues() {
|
||||
} else {
|
||||
$("#gasRecordMileage").removeClass("is-invalid");
|
||||
}
|
||||
if (gasGallons.trim() == '' || parseFloat(gasGallons) <= 0) {
|
||||
if (gasGallons.trim() == '' || globalParseFloat(gasGallons) <= 0) {
|
||||
hasError = true;
|
||||
$("#gasRecordGallons").addClass("is-invalid");
|
||||
} else {
|
||||
$("#gasRecordGallons").removeClass("is-invalid");
|
||||
}
|
||||
if (gasCostType != undefined && gasCostType == 'unit') {
|
||||
var convertedGasCost = parseFloat(gasCost) * parseFloat(gasGallons);
|
||||
gasCost = convertedGasCost.toFixed(2).toString();
|
||||
if (isNaN(gasCost))
|
||||
var convertedGasCost = globalParseFloat(gasCost) * globalParseFloat(gasGallons);
|
||||
if (isNaN(convertedGasCost))
|
||||
{
|
||||
hasError = true;
|
||||
$("#gasRecordCost").addClass("is-invalid");
|
||||
} else {
|
||||
gasCost = globalFloatToString(convertedGasCost.toFixed(2).toString());
|
||||
$("#gasRecordCost").removeClass("is-invalid");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ function getAndValidateNoteValues() {
|
||||
var noteText = $("#noteTextArea").val();
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
var noteId = getNoteModelData().id;
|
||||
var noteIsPinned = $("#noteIsPinned").is(":checked");
|
||||
//validation
|
||||
var hasError = false;
|
||||
if (noteDescription.trim() == '') { //eliminates whitespace.
|
||||
@@ -86,6 +87,7 @@ function getAndValidateNoteValues() {
|
||||
hasError: hasError,
|
||||
vehicleId: vehicleId,
|
||||
description: noteDescription,
|
||||
noteText: noteText
|
||||
noteText: noteText,
|
||||
pinned: noteIsPinned
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,7 @@ function getAndValidatePlanRecordValues() {
|
||||
cost: planCost,
|
||||
notes: planNotes,
|
||||
files: uploadedFiles,
|
||||
supplies: selectedSupplies,
|
||||
priority: planPriority,
|
||||
progress: planProgress,
|
||||
importMode: planType
|
||||
|
||||
@@ -81,6 +81,19 @@ function enableRecurring() {
|
||||
}
|
||||
}
|
||||
|
||||
function markDoneReminderRecord(reminderRecordId, e) {
|
||||
event.stopPropagation();
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
$.post(`/Vehicle/PushbackRecurringReminderRecord?reminderRecordId=${reminderRecordId}`, function (data) {
|
||||
if (data) {
|
||||
successToast("Reminder Updated");
|
||||
getVehicleReminders(vehicleId);
|
||||
} else {
|
||||
errorToast("An error has occurred, please try again later.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getAndValidateReminderRecordValues() {
|
||||
var reminderDate = $("#reminderDate").val();
|
||||
var reminderMileage = $("#reminderMileage").val();
|
||||
|
||||
@@ -12,12 +12,8 @@ function generateVehicleHistoryReport() {
|
||||
}
|
||||
})
|
||||
}
|
||||
var debounce = null;
|
||||
function updateCheck(sender) {
|
||||
clearTimeout(debounce);
|
||||
debounce = setTimeout(function () {
|
||||
refreshBarChart();
|
||||
}, 1000);
|
||||
function updateCheck() {
|
||||
setDebounce(refreshBarChart);
|
||||
}
|
||||
function refreshMPGChart() {
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
@@ -26,7 +22,7 @@ function refreshMPGChart() {
|
||||
$("#monthFuelMileageReportContent").html(data);
|
||||
})
|
||||
}
|
||||
function refreshBarChart(callBack) {
|
||||
function refreshBarChart() {
|
||||
var selectedMetrics = [];
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
var year = getYear();
|
||||
|
||||
@@ -114,6 +114,7 @@ function getAndValidateServiceRecordValues() {
|
||||
cost: serviceCost,
|
||||
notes: serviceNotes,
|
||||
files: uploadedFiles,
|
||||
supplies: selectedSupplies,
|
||||
addReminderRecord: addReminderRecord
|
||||
}
|
||||
}
|
||||
@@ -148,3 +148,10 @@ function decodeHTMLEntities(text) {
|
||||
.html(text)
|
||||
.text();
|
||||
}
|
||||
var debounce = null;
|
||||
function setDebounce(callBack) {
|
||||
clearTimeout(debounce);
|
||||
debounce = setTimeout(function () {
|
||||
callBack();
|
||||
}, 1000);
|
||||
}
|
||||
@@ -93,7 +93,7 @@ function getAndValidateSupplyRecordValues() {
|
||||
} else {
|
||||
$("#supplyRecordDescription").removeClass("is-invalid");
|
||||
}
|
||||
if (supplyQuantity.trim() == '' || !isValidMoney(supplyQuantity) || parseFloat(supplyQuantity) < 0) {
|
||||
if (supplyQuantity.trim() == '' || !isValidMoney(supplyQuantity) || globalParseFloat(supplyQuantity) < 0) {
|
||||
hasError = true;
|
||||
$("#supplyRecordQuantity").addClass("is-invalid");
|
||||
} else {
|
||||
|
||||
@@ -18,6 +18,14 @@ function showEditTaxRecordModal(taxRecordId) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function enableTaxRecurring() {
|
||||
var taxIsRecurring = $("#taxIsRecurring").is(":checked");
|
||||
if (taxIsRecurring) {
|
||||
$("#taxRecurringMonth").attr('disabled', false);
|
||||
} else {
|
||||
$("#taxRecurringMonth").attr('disabled', true);
|
||||
}
|
||||
}
|
||||
function hideAddTaxRecordModal() {
|
||||
$('#taxRecordModal').modal('hide');
|
||||
}
|
||||
@@ -76,6 +84,8 @@ function getAndValidateTaxRecordValues() {
|
||||
var taxNotes = $("#taxRecordNotes").val();
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
var taxRecordId = getTaxRecordModelData().id;
|
||||
var taxIsRecurring = $("#taxIsRecurring").is(":checked");
|
||||
var taxRecurringMonth = $("#taxRecurringMonth").val();
|
||||
var addReminderRecord = $("#addReminderCheck").is(":checked");
|
||||
//validation
|
||||
var hasError = false;
|
||||
@@ -105,6 +115,8 @@ function getAndValidateTaxRecordValues() {
|
||||
description: taxDescription,
|
||||
cost: taxCost,
|
||||
notes: taxNotes,
|
||||
isRecurring: taxIsRecurring,
|
||||
recurringInterval: taxRecurringMonth,
|
||||
files: uploadedFiles,
|
||||
addReminderRecord: addReminderRecord
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ function getAndValidateUpgradeRecordValues() {
|
||||
cost: upgradeCost,
|
||||
notes: upgradeNotes,
|
||||
files: uploadedFiles,
|
||||
supplies: selectedSupplies,
|
||||
addReminderRecord: addReminderRecord
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user