From c4dc81e4bc1e1f3548fdd8cfc3462bc3652e55af Mon Sep 17 00:00:00 2001 From: "DESKTOP-GENO133\\IvanPlex" Date: Fri, 9 Feb 2024 10:34:01 -0700 Subject: [PATCH] fixed user access issue. --- .../Postgres/TokenRecordDataAccess.cs | 1 - .../Postgres/UserAccessDataAcces.cs | 212 ++++++++++++++++++ Logic/UserLogic.cs | 6 + Program.cs | 6 +- config/userConfig.json | 2 +- wwwroot/defaults/en_US.json | 2 +- 6 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 External/Implementations/Postgres/UserAccessDataAcces.cs diff --git a/External/Implementations/Postgres/TokenRecordDataAccess.cs b/External/Implementations/Postgres/TokenRecordDataAccess.cs index 8278d63..c785525 100644 --- a/External/Implementations/Postgres/TokenRecordDataAccess.cs +++ b/External/Implementations/Postgres/TokenRecordDataAccess.cs @@ -1,7 +1,6 @@ using CarCareTracker.External.Interfaces; using CarCareTracker.Models; using Npgsql; -using System.Net.Mail; namespace CarCareTracker.External.Implementations { diff --git a/External/Implementations/Postgres/UserAccessDataAcces.cs b/External/Implementations/Postgres/UserAccessDataAcces.cs new file mode 100644 index 0000000..3772b86 --- /dev/null +++ b/External/Implementations/Postgres/UserAccessDataAcces.cs @@ -0,0 +1,212 @@ +using CarCareTracker.External.Interfaces; +using CarCareTracker.Models; +using Npgsql; +using System.Net.Mail; + +namespace CarCareTracker.External.Implementations +{ + public class PGUserAccessDataAccess : IUserAccessDataAccess + { + private NpgsqlConnection pgDataSource; + private readonly ILogger _logger; + private static string tableName = "useraccessrecords"; + public PGUserAccessDataAccess(IConfiguration config, ILogger logger) + { + pgDataSource = new NpgsqlConnection(config["POSTGRES_CONNECTION"]); + _logger = logger; + try + { + pgDataSource.Open(); + //create table if not exist. + string initCMD = $"CREATE TABLE IF NOT EXISTS app.{tableName} (userId INT, vehicleId INT, PRIMARY KEY(userId, vehicleId))"; + using (var ctext = new NpgsqlCommand(initCMD, pgDataSource)) + { + ctext.ExecuteNonQuery(); + } + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + /// + /// Gets a list of vehicles user have access to. + /// + /// + /// + public List GetUserAccessByUserId(int userId) + { + try + { + string cmd = $"SELECT userId, vehicleId FROM app.{tableName} WHERE userId = @userId"; + var results = new List(); + using (var ctext = new NpgsqlCommand(cmd, pgDataSource)) + { + ctext.Parameters.AddWithValue("userId", userId); + using (NpgsqlDataReader reader = ctext.ExecuteReader()) + while (reader.Read()) + { + UserAccess result = new UserAccess() + { + Id = new UserVehicle + { + UserId = int.Parse(reader["userId"].ToString()), + VehicleId = int.Parse(reader["vehicleId"].ToString()) + } + }; + results.Add(result); + } + } + return results; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return new List(); + } + } + public UserAccess GetUserAccessByVehicleAndUserId(int userId, int vehicleId) + { + try + { + string cmd = $"SELECT userId, vehicleId FROM app.{tableName} WHERE userId = @userId AND vehicleId = @vehicleId"; + UserAccess result = null; + using (var ctext = new NpgsqlCommand(cmd, pgDataSource)) + { + ctext.Parameters.AddWithValue("userId", userId); + ctext.Parameters.AddWithValue("vehicleId", vehicleId); + using (NpgsqlDataReader reader = ctext.ExecuteReader()) + while (reader.Read()) + { + result = new UserAccess() + { + Id = new UserVehicle + { + UserId = int.Parse(reader["userId"].ToString()), + VehicleId = int.Parse(reader["vehicleId"].ToString()) + } + }; + return result; + } + } + return result; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return new UserAccess(); + } + } + public List GetUserAccessByVehicleId(int vehicleId) + { + try + { + string cmd = $"SELECT userId, vehicleId FROM app.{tableName} WHERE vehicleId = @vehicleId"; + var results = new List(); + using (var ctext = new NpgsqlCommand(cmd, pgDataSource)) + { + ctext.Parameters.AddWithValue("vehicleId", vehicleId); + using (NpgsqlDataReader reader = ctext.ExecuteReader()) + while (reader.Read()) + { + UserAccess result = new UserAccess() + { + Id = new UserVehicle + { + UserId = int.Parse(reader["userId"].ToString()), + VehicleId = int.Parse(reader["vehicleId"].ToString()) + } + }; + results.Add(result); + } + } + return results; + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return new List(); + } + } + public bool SaveUserAccess(UserAccess userAccess) + { + try + { + string cmd = $"INSERT INTO app.{tableName} (userId, vehicleId) VALUES(@userId, @vehicleId)"; + using (var ctext = new NpgsqlCommand(cmd, pgDataSource)) + { + ctext.Parameters.AddWithValue("userId", userAccess.Id.UserId); + ctext.Parameters.AddWithValue("vehicleId", userAccess.Id.VehicleId); + return ctext.ExecuteNonQuery() > 0; + } + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + public bool DeleteUserAccess(int userId, int vehicleId) + { + try + { + string cmd = $"DELETE FROM app.{tableName} WHERE userId = @userId AND vehicleId = @vehicleId"; + using (var ctext = new NpgsqlCommand(cmd, pgDataSource)) + { + ctext.Parameters.AddWithValue("userId", userId); + ctext.Parameters.AddWithValue("vehicleId", vehicleId); + return ctext.ExecuteNonQuery() > 0; + } + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + /// + /// Delete all access records when a vehicle is deleted. + /// + /// + /// + public bool DeleteAllAccessRecordsByVehicleId(int vehicleId) + { + try + { + string cmd = $"DELETE FROM app.{tableName} WHERE vehicleId = @vehicleId"; + using (var ctext = new NpgsqlCommand(cmd, pgDataSource)) + { + ctext.Parameters.AddWithValue("vehicleId", vehicleId); + return ctext.ExecuteNonQuery() > 0; + } + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + /// + /// Delee all access records when a user is deleted. + /// + /// + /// + public bool DeleteAllAccessRecordsByUserId(int userId) + { + try + { + string cmd = $"DELETE FROM app.{tableName} WHERE userId = @userId"; + using (var ctext = new NpgsqlCommand(cmd, pgDataSource)) + { + ctext.Parameters.AddWithValue("userId", userId); + return ctext.ExecuteNonQuery() > 0; + } + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + return false; + } + } + } +} \ No newline at end of file diff --git a/Logic/UserLogic.cs b/Logic/UserLogic.cs index bd80c08..2271752 100644 --- a/Logic/UserLogic.cs +++ b/Logic/UserLogic.cs @@ -48,6 +48,12 @@ namespace CarCareTracker.Logic if (existingUser.Id != default) { //user exists. + //check if user is already a collaborator + var userAccess = _userAccess.GetUserAccessByVehicleAndUserId(existingUser.Id, vehicleId); + if (userAccess != null) + { + return new OperationResponse { Success = false, Message = "User is already a collaborator" }; + } var result = AddUserAccessToVehicle(existingUser.Id, vehicleId); if (result) { diff --git a/Program.cs b/Program.cs index 09d9ab0..0fb5153 100644 --- a/Program.cs +++ b/Program.cs @@ -30,6 +30,7 @@ if (!string.IsNullOrWhiteSpace(builder.Configuration["POSTGRES_CONNECTION"])){ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); } else { @@ -48,12 +49,9 @@ else builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); } - - -builder.Services.AddSingleton(); - //configure helpers builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/config/userConfig.json b/config/userConfig.json index 681da11..f123e76 100644 --- a/config/userConfig.json +++ b/config/userConfig.json @@ -1 +1 @@ -{"UseDarkMode":true,"EnableCsvImports":false,"UseMPG":true,"UseDescending":false,"EnableAuth":false,"UserNameHash":"","UserPasswordHash":""} \ No newline at end of file +{"UseDarkMode":true,"EnableCsvImports":true,"UseMPG":false,"UseDescending":true,"EnableAuth":true,"HideZero":false,"UseUKMPG":false,"UseThreeDecimalGasCost":true,"UseMarkDownOnSavedNotes":false,"EnableAutoReminderRefresh":false,"EnableAutoOdometerInsert":false,"EnableShopSupplies":false,"PreferredGasUnit":"l","PreferredGasMileageUnit":"l/100km","UserNameHash":"8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918","UserPasswordHash":"8b3b357e205f36f3c3858994f0d15ce9451284e0e42493a817fc0d66a02bc3a2","UserLanguage":"en_US","VisibleTabs":[0,8,1,4,2,3,6,7],"DefaultTab":8} \ No newline at end of file diff --git a/wwwroot/defaults/en_US.json b/wwwroot/defaults/en_US.json index 0ada1db..f887181 100644 --- a/wwwroot/defaults/en_US.json +++ b/wwwroot/defaults/en_US.json @@ -1 +1 @@ -{"Garage":"Garage","Settings":"Settings","Admin_Panel":"Admin Panel","Logout":"Logout","Dark_Mode":"Dark Mode","Enable_CSV_Imports":"Enable CSV Imports","Use_Imperial_Calculation_for_Fuel_Economy_Calculations(MPG)":"Use Imperial Calculation for Fuel Economy Calculations(MPG)","This_Will_Also_Change_Units_to_Miles_and_Gallons":"This Will Also Change Units to Miles and Gallons","Use_UK_MPG_Calculation":"Use UK MPG Calculation","Input_Gas_Consumption_in_Liters,_it_will_be_converted_to_UK_Gals_for_MPG_Calculation":"Input Gas Consumption in Liters, it will be converted to UK Gals for MPG Calculation","Sort_lists_in_Descending_Order(Newest_to_Oldest)":"Sort lists in Descending Order(Newest to Oldest)","Replace_$0.00_Costs_with_---":"Replace $0.00 Costs with ---","Use_Three_Decimals_For_Fuel_Cost":"Use Three Decimals For Fuel Cost","Display_Saved_Notes_in_Markdown":"Display Saved Notes in Markdown","Auto_Refresh_Lapsed_Recurring_Reminders":"Auto Refresh Lapsed Recurring Reminders","Auto_Insert_Odometer_Records":"Auto Insert Odometer Records","Only_when_Adding_Service/Repair/Upgrade/Fuel_Record_or_Completing_a_Plan":"Only when Adding Service/Repair/Upgrade/Fuel Record or Completing a Plan","Enable_Authentication":"Enable Authentication","Visible_Tabs":"Visible Tabs","Service_Records":"Service Records","Dashboard":"Dashboard","Repairs":"Repairs","Upgrades":"Upgrades","Fuel":"Fuel","Odometer":"Odometer","Taxes":"Taxes","Notes":"Notes","Reminder":"Reminder","Supplies":"Supplies","Planner":"Planner","Default_Tab":"Default Tab","Service_Record":"Service Record","Tax":"Tax","Reminders":"Reminders","Backups":"Backups","Make":"Make","Restore":"Restore","About":"About","Add_New_Vehicle":"Add New Vehicle","Year":"Year","Year(must_be_after_1900)":"Year(must be after 1900)","Model":"Model","License_Plate":"License Plate","Electric_Vehicle":"Electric Vehicle","Use_Engine_Hours":"Use Engine Hours","Tags(optional)":"Tags(optional)","Upload_a_picture(optional)":"Upload a picture(optional)","Cancel":"Cancel","Edit_Vehicle":"Edit Vehicle","Delete_Vehicle":"Delete Vehicle","Manage_Vehicle":"Manage Vehicle","Expenses_by_Type":"Expenses by Type","Service":"Service","Expenses_by_Month":"Expenses by Month","As_of_Today":"As of Today","\u002B30_Days":"\u002B30 Days","\u002B60_Days":"\u002B60 Days","\u002B90_Days":"\u002B90 Days","Not_Urgent":"Not Urgent","Urgent":"Urgent","Very_Urgent":"Very Urgent","Past_Due":"Past Due","Reminders_by_Category":"Reminders by Category","Reminders_by_Urgency":"Reminders by Urgency","Collaborators":"Collaborators","Username":"Username","Delete":"Delete","Fuel_Mileage_by_Month":"Fuel Mileage by Month","Vehicle_Maintenance_Report":"Vehicle Maintenance Report","Export_Attachments":"Export Attachments","Gasoline":"Gasoline","Last_Reported_Odometer_Reading":"Last Reported Odometer Reading","Average_Fuel_Economy":"Average Fuel Economy","Total_Spent(excl._fuel)":"Total Spent(excl. fuel)","Total_Spent_on_Fuel":"Total Spent on Fuel","Type":"Type","Date":"Date","Description":"Description","Cost":"Cost","Repair":"Repair","Upgrade":"Upgrade","#_of_Odometer_Records":"# of Odometer Records","Add_Odometer_Record":"Add Odometer Record","Import_via_CSV":"Import via CSV","Export_to_CSV":"Export to CSV","Print":"Print","Add_New_Odometer_Record":"Add New Odometer Record","Date_recorded":"Date recorded","Odometer_reading":"Odometer reading","Notes(optional)":"Notes(optional)","Upload_documents(optional)":"Upload documents(optional)","Max_File_Size:_28.6MB":"Max File Size: 28.6MB","#_of_Service_Records":"# of Service Records","Total":"Total","Add_Service_Record":"Add Service Record","No_data_found,_create_reminders_to_see_visualizations_here.":"No data found, create reminders to see visualizations here.","No_data_found,_insert/select_some_data_to_see_visualizations_here.":"No data found, insert/select some data to see visualizations here.","Edit_Odometer_Record":"Edit Odometer Record","Import_Data_from_CSV":"Import Data from CSV","In_order_for_this_utility_to_function_properly,_your_CSV_file_MUST_be_formatted_exactly_like_the_provided_sample._Dates_must_be_supplied_in_a_string._Numbers_must_be_supplied_as_numbers_without_currency_formatting.":"In order for this utility to function properly, your CSV file MUST be formatted exactly like the provided sample. Dates must be supplied in a string. Numbers must be supplied as numbers without currency formatting.","Failure_to_format_the_data_correctly_can_cause_data_corruption._Please_make_sure_you_make_a_copy_of_the_local_database_before_proceeding.":"Failure to format the data correctly can cause data corruption. Please make sure you make a copy of the local database before proceeding.","Download_Sample":"Download Sample","Upload_CSV_File":"Upload CSV File","Import":"Import","Edit_Service_Record":"Edit Service Record","Date_service_was_performed":"Date service was performed","Odometer_reading_when_serviced":"Odometer reading when serviced","Description_of_item(s)_serviced(i.e._Oil_Change)":"Description of item(s) serviced(i.e. Oil Change)","Cost_of_the_service":"Cost of the service","Move_To":"Move To","#_of_Repair_Records":"# of Repair Records","Add_Repair_Record":"Add Repair Record","Add_New_Repair_Record":"Add New Repair Record","Date_repair_was_performed":"Date repair was performed","Odometer_reading_when_repaired":"Odometer reading when repaired","Description_of_item(s)_repaired(i.e._Alternator)":"Description of item(s) repaired(i.e. Alternator)","Cost_of_the_repair":"Cost of the repair","Choose_Supplies":"Choose Supplies","Add_Reminder":"Add Reminder","Select_Supplies":"Select Supplies","No_supplies_with_quantities_greater_than_0_is_found.":"No supplies with quantities greater than 0 is found.","Select":"Select","#_of_Upgrade_Records":"# of Upgrade Records","Add_Upgrade_Record":"Add Upgrade Record","Add_New_Upgrade_Record":"Add New Upgrade Record","Date_upgrade/mods_was_installed":"Date upgrade/mods was installed","Odometer_reading_when_upgraded/modded":"Odometer reading when upgraded/modded","Description_of_item(s)_upgraded/modded":"Description of item(s) upgraded/modded","Cost_of_the_upgrade/mods":"Cost of the upgrade/mods","#_of_Gas_Records":"# of Gas Records","Total_Fuel_Consumed":"Total Fuel Consumed","Total_Cost":"Total Cost","Add_Gas_Record":"Add Gas Record","Date_Refueled":"Date Refueled","Consumption":"Consumption","Fuel_Economy":"Fuel Economy","Unit_Cost":"Unit Cost","#_of_Supply_Records":"# of Supply Records","Add_Supply_Record":"Add Supply Record","Part_#":"Part #","Supplier":"Supplier","Quantity":"Quantity","Add_New_Supply_Record":"Add New Supply Record","Date_purchased":"Date purchased","Part_Number":"Part Number","Part_#/Model_#/SKU_#":"Part #/Model #/SKU #","Description_of_the_Part/Supplies":"Description of the Part/Supplies","Supplier/Vendor":"Supplier/Vendor","Part_Supplier":"Part Supplier","Edit_Supply_Record":"Edit Supply Record","Add_New_Service_Record":"Add New Service Record","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.":"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.","In_Stock":"In Stock","Edit_Repair_Record":"Edit Repair Record","Edit_Upgrade_Record":"Edit Upgrade Record","Save_Vehicle":"Save Vehicle","Add_New_Gas_Record":"Add New Gas Record","Date_refueled":"Date refueled","Odometer_Reading":"Odometer Reading","Odometer_reading_when_refueled":"Odometer reading when refueled","Fuel_Consumption":"Fuel Consumption","Amount_of_gas_refueled":"Amount of gas refueled","Is_Filled_To_Full":"Is Filled To Full","Missed_Fuel_Up(Skip_MPG_Calculation)":"Missed Fuel Up(Skip MPG Calculation)","Cost_of_gas_refueled":"Cost of gas refueled","Unit":"Unit","#_of_Tax_Records":"# of Tax Records","Add_Tax_Record":"Add Tax Record","Add_New_Tax_Record":"Add New Tax Record","Date_tax_was_paid":"Date tax was paid","Description_of_tax_paid(i.e._Registration)":"Description of tax paid(i.e. Registration)","Cost_of_tax_paid":"Cost of tax paid","Is_Recurring":"Is Recurring","Month":"Month","1_Month":"1 Month","3_Months":"3 Months","6_Months":"6 Months","1_Year":"1 Year","2_Years":"2 Years","3_Years":"3 Years","5_Years":"5 Years","Edit_Tax_Record":"Edit Tax Record","#_of_Notes":"# of Notes","Add_Note":"Add Note","Note":"Note","Add_New_Note":"Add New Note","Pinned":"Pinned","Description_of_the_note":"Description of the note","Min_Fuel_Economy":"Min Fuel Economy","Max_Fuel_Economy":"Max Fuel Economy","Edit_Gas_Record":"Edit Gas Record","#_of_Plan_Records":"# of Plan Records","Add_Plan_Record":"Add Plan Record","Planned":"Planned","Doing":"Doing","Testing":"Testing","Done":"Done","Add_New_Plan_Record":"Add New Plan Record","Describe_the_Plan":"Describe the Plan","Cost_of_the_Plan":"Cost of the Plan","Priority":"Priority","Critical":"Critical","Normal":"Normal","Low":"Low","Current_Stage":"Current Stage","#_of_Reminders":"# of Reminders","Urgency":"Urgency","Metric":"Metric","Add_New_Reminder":"Add New Reminder","Reminder_Description":"Reminder Description","Remind_me_on":"Remind me on","Future_Date":"Future Date","Future_Odometer_Reading":"Future Odometer Reading","Whichever_comes_first":"Whichever comes first","Other":"Other","Edit_Reminder":"Edit Reminder","Replace_picture(optional)":"Replace picture(optional)","Language":"Language","Manage_Languages":"Manage Languages","Upload":"Upload","Tokens":"Tokens","Generate_User_Token":"Generate User Token","Auto_Notify(via_Email)":"Auto Notify(via Email)","Token":"Token","Issued_To":"Issued To","Users":"Users","Email":"Email","Is_Admin":"Is Admin","An_error_has_occurred,_please_try_again_later":"An error has occurred, please try again later","Edit_Note":"Edit Note","Password":"Password","Remember_Me":"Remember Me","Login":"Login","Forgot_Password":"Forgot Password","Register":"Register","Request":"Request","I_Have_a_Token":"I Have a Token","Back_to_Login":"Back to Login","Email_Address":"Email Address","New_Password":"New Password","Reset_Password":"Reset Password","No_data_found_or_all_records_have_zero_sums,_insert_records_with_non-zero_sums_to_see_visualizations_here.":"No data found or all records have zero sums, insert records with non-zero sums to see visualizations here.","Save_as_Template":"Save as Template","View_Templates":"View Templates","Select_Template":"Select Template","No_templates_are_found.":"No templates are found.","Use":"Use","Edit_Plan_Record":"Edit Plan Record","Date_Created":"Date Created","Last_Modified":"Last Modified","Shop_Supplies":"Shop Supplies"} \ No newline at end of file +{"Garage":"Garage","Settings":"Settings","Admin_Panel":"Admin Panel","Logout":"Logout","Dark_Mode":"Dark Mode","Enable_CSV_Imports":"Enable CSV Imports","Use_Imperial_Calculation_for_Fuel_Economy_Calculations(MPG)":"Use Imperial Calculation for Fuel Economy Calculations(MPG)","This_Will_Also_Change_Units_to_Miles_and_Gallons":"This Will Also Change Units to Miles and Gallons","Use_UK_MPG_Calculation":"Use UK MPG Calculation","Input_Gas_Consumption_in_Liters,_it_will_be_converted_to_UK_Gals_for_MPG_Calculation":"Input Gas Consumption in Liters, it will be converted to UK Gals for MPG Calculation","Sort_lists_in_Descending_Order(Newest_to_Oldest)":"Sort lists in Descending Order(Newest to Oldest)","Replace_$0.00_Costs_with_---":"Replace $0.00 Costs with ---","Use_Three_Decimals_For_Fuel_Cost":"Use Three Decimals For Fuel Cost","Display_Saved_Notes_in_Markdown":"Display Saved Notes in Markdown","Auto_Refresh_Lapsed_Recurring_Reminders":"Auto Refresh Lapsed Recurring Reminders","Auto_Insert_Odometer_Records":"Auto Insert Odometer Records","Only_when_Adding_Service/Repair/Upgrade/Fuel_Record_or_Completing_a_Plan":"Only when Adding Service/Repair/Upgrade/Fuel Record or Completing a Plan","Enable_Authentication":"Enable Authentication","Visible_Tabs":"Visible Tabs","Service_Records":"Service Records","Dashboard":"Dashboard","Repairs":"Repairs","Upgrades":"Upgrades","Fuel":"Fuel","Odometer":"Odometer","Taxes":"Taxes","Notes":"Notes","Reminder":"Reminder","Supplies":"Supplies","Planner":"Planner","Default_Tab":"Default Tab","Service_Record":"Service Record","Tax":"Tax","Reminders":"Reminders","Backups":"Backups","Make":"Make","Restore":"Restore","About":"About","Add_New_Vehicle":"Add New Vehicle","Year":"Year","Year(must_be_after_1900)":"Year(must be after 1900)","Model":"Model","License_Plate":"License Plate","Electric_Vehicle":"Electric Vehicle","Use_Engine_Hours":"Use Engine Hours","Tags(optional)":"Tags(optional)","Upload_a_picture(optional)":"Upload a picture(optional)","Cancel":"Cancel","Edit_Vehicle":"Edit Vehicle","Delete_Vehicle":"Delete Vehicle","Manage_Vehicle":"Manage Vehicle","Expenses_by_Type":"Expenses by Type","Service":"Service","Expenses_by_Month":"Expenses by Month","As_of_Today":"As of Today","\u002B30_Days":"\u002B30 Days","\u002B60_Days":"\u002B60 Days","\u002B90_Days":"\u002B90 Days","Not_Urgent":"Not Urgent","Urgent":"Urgent","Very_Urgent":"Very Urgent","Past_Due":"Past Due","Reminders_by_Category":"Reminders by Category","Reminders_by_Urgency":"Reminders by Urgency","Collaborators":"Collaborators","Username":"Username","Delete":"Delete","Fuel_Mileage_by_Month":"Fuel Mileage by Month","Vehicle_Maintenance_Report":"Vehicle Maintenance Report","Export_Attachments":"Export Attachments","Gasoline":"Gasoline","Last_Reported_Odometer_Reading":"Last Reported Odometer Reading","Average_Fuel_Economy":"Average Fuel Economy","Total_Spent(excl._fuel)":"Total Spent(excl. fuel)","Total_Spent_on_Fuel":"Total Spent on Fuel","Type":"Type","Date":"Date","Description":"Description","Cost":"Cost","Repair":"Repair","Upgrade":"Upgrade","#_of_Odometer_Records":"# of Odometer Records","Add_Odometer_Record":"Add Odometer Record","Import_via_CSV":"Import via CSV","Export_to_CSV":"Export to CSV","Print":"Print","Add_New_Odometer_Record":"Add New Odometer Record","Date_recorded":"Date recorded","Odometer_reading":"Odometer reading","Notes(optional)":"Notes(optional)","Upload_documents(optional)":"Upload documents(optional)","Max_File_Size:_28.6MB":"Max File Size: 28.6MB","#_of_Service_Records":"# of Service Records","Total":"Total","Add_Service_Record":"Add Service Record","No_data_found,_create_reminders_to_see_visualizations_here.":"No data found, create reminders to see visualizations here.","No_data_found,_insert/select_some_data_to_see_visualizations_here.":"No data found, insert/select some data to see visualizations here.","Edit_Odometer_Record":"Edit Odometer Record","Import_Data_from_CSV":"Import Data from CSV","In_order_for_this_utility_to_function_properly,_your_CSV_file_MUST_be_formatted_exactly_like_the_provided_sample._Dates_must_be_supplied_in_a_string._Numbers_must_be_supplied_as_numbers_without_currency_formatting.":"In order for this utility to function properly, your CSV file MUST be formatted exactly like the provided sample. Dates must be supplied in a string. Numbers must be supplied as numbers without currency formatting.","Failure_to_format_the_data_correctly_can_cause_data_corruption._Please_make_sure_you_make_a_copy_of_the_local_database_before_proceeding.":"Failure to format the data correctly can cause data corruption. Please make sure you make a copy of the local database before proceeding.","Download_Sample":"Download Sample","Upload_CSV_File":"Upload CSV File","Import":"Import","Edit_Service_Record":"Edit Service Record","Date_service_was_performed":"Date service was performed","Odometer_reading_when_serviced":"Odometer reading when serviced","Description_of_item(s)_serviced(i.e._Oil_Change)":"Description of item(s) serviced(i.e. Oil Change)","Cost_of_the_service":"Cost of the service","Move_To":"Move To","#_of_Repair_Records":"# of Repair Records","Add_Repair_Record":"Add Repair Record","Add_New_Repair_Record":"Add New Repair Record","Date_repair_was_performed":"Date repair was performed","Odometer_reading_when_repaired":"Odometer reading when repaired","Description_of_item(s)_repaired(i.e._Alternator)":"Description of item(s) repaired(i.e. Alternator)","Cost_of_the_repair":"Cost of the repair","Choose_Supplies":"Choose Supplies","Add_Reminder":"Add Reminder","Select_Supplies":"Select Supplies","No_supplies_with_quantities_greater_than_0_is_found.":"No supplies with quantities greater than 0 is found.","Select":"Select","#_of_Upgrade_Records":"# of Upgrade Records","Add_Upgrade_Record":"Add Upgrade Record","Add_New_Upgrade_Record":"Add New Upgrade Record","Date_upgrade/mods_was_installed":"Date upgrade/mods was installed","Odometer_reading_when_upgraded/modded":"Odometer reading when upgraded/modded","Description_of_item(s)_upgraded/modded":"Description of item(s) upgraded/modded","Cost_of_the_upgrade/mods":"Cost of the upgrade/mods","#_of_Gas_Records":"# of Gas Records","Total_Fuel_Consumed":"Total Fuel Consumed","Total_Cost":"Total Cost","Add_Gas_Record":"Add Gas Record","Date_Refueled":"Date Refueled","Consumption":"Consumption","Fuel_Economy":"Fuel Economy","Unit_Cost":"Unit Cost","#_of_Supply_Records":"# of Supply Records","Add_Supply_Record":"Add Supply Record","Part_#":"Part #","Supplier":"Supplier","Quantity":"Quantity","Add_New_Supply_Record":"Add New Supply Record","Date_purchased":"Date purchased","Part_Number":"Part Number","Part_#/Model_#/SKU_#":"Part #/Model #/SKU #","Description_of_the_Part/Supplies":"Description of the Part/Supplies","Supplier/Vendor":"Supplier/Vendor","Part_Supplier":"Part Supplier","Edit_Supply_Record":"Edit Supply Record","Add_New_Service_Record":"Add New Service Record","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.":"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.","In_Stock":"In Stock","Edit_Repair_Record":"Edit Repair Record","Edit_Upgrade_Record":"Edit Upgrade Record","Save_Vehicle":"Save Vehicle","Add_New_Gas_Record":"Add New Gas Record","Date_refueled":"Date refueled","Odometer_Reading":"Odometer Reading","Odometer_reading_when_refueled":"Odometer reading when refueled","Fuel_Consumption":"Fuel Consumption","Amount_of_gas_refueled":"Amount of gas refueled","Is_Filled_To_Full":"Is Filled To Full","Missed_Fuel_Up(Skip_MPG_Calculation)":"Missed Fuel Up(Skip MPG Calculation)","Cost_of_gas_refueled":"Cost of gas refueled","Unit":"Unit","#_of_Tax_Records":"# of Tax Records","Add_Tax_Record":"Add Tax Record","Add_New_Tax_Record":"Add New Tax Record","Date_tax_was_paid":"Date tax was paid","Description_of_tax_paid(i.e._Registration)":"Description of tax paid(i.e. Registration)","Cost_of_tax_paid":"Cost of tax paid","Is_Recurring":"Is Recurring","Month":"Month","1_Month":"1 Month","3_Months":"3 Months","6_Months":"6 Months","1_Year":"1 Year","2_Years":"2 Years","3_Years":"3 Years","5_Years":"5 Years","Edit_Tax_Record":"Edit Tax Record","#_of_Notes":"# of Notes","Add_Note":"Add Note","Note":"Note","Add_New_Note":"Add New Note","Pinned":"Pinned","Description_of_the_note":"Description of the note","Min_Fuel_Economy":"Min Fuel Economy","Max_Fuel_Economy":"Max Fuel Economy","Edit_Gas_Record":"Edit Gas Record","#_of_Plan_Records":"# of Plan Records","Add_Plan_Record":"Add Plan Record","Planned":"Planned","Doing":"Doing","Testing":"Testing","Done":"Done","Add_New_Plan_Record":"Add New Plan Record","Describe_the_Plan":"Describe the Plan","Cost_of_the_Plan":"Cost of the Plan","Priority":"Priority","Critical":"Critical","Normal":"Normal","Low":"Low","Current_Stage":"Current Stage","#_of_Reminders":"# of Reminders","Urgency":"Urgency","Metric":"Metric","Add_New_Reminder":"Add New Reminder","Reminder_Description":"Reminder Description","Remind_me_on":"Remind me on","Future_Date":"Future Date","Future_Odometer_Reading":"Future Odometer Reading","Whichever_comes_first":"Whichever comes first","Other":"Other","Edit_Reminder":"Edit Reminder","Replace_picture(optional)":"Replace picture(optional)","Language":"Language","Manage_Languages":"Manage Languages","Upload":"Upload","Tokens":"Tokens","Generate_User_Token":"Generate User Token","Auto_Notify(via_Email)":"Auto Notify(via Email)","Token":"Token","Issued_To":"Issued To","Users":"Users","Email":"Email","Is_Admin":"Is Admin","An_error_has_occurred,_please_try_again_later":"An error has occurred, please try again later","Edit_Note":"Edit Note","Password":"Password","Remember_Me":"Remember Me","Login":"Login","Forgot_Password":"Forgot Password","Register":"Register","Request":"Request","I_Have_a_Token":"I Have a Token","Back_to_Login":"Back to Login","Email_Address":"Email Address","New_Password":"New Password","Reset_Password":"Reset Password","No_data_found_or_all_records_have_zero_sums,_insert_records_with_non-zero_sums_to_see_visualizations_here.":"No data found or all records have zero sums, insert records with non-zero sums to see visualizations here.","Save_as_Template":"Save as Template","View_Templates":"View Templates","Select_Template":"Select Template","No_templates_are_found.":"No templates are found.","Use":"Use","Edit_Plan_Record":"Edit Plan Record","Date_Created":"Date Created","Last_Modified":"Last Modified","Shop_Supplies":"Shop Supplies","Uploaded_Documents":"Uploaded Documents","Upload_more_documents":"Upload more documents"} \ No newline at end of file