Files
car-tracker/app/models/maintenance_schedule_status.rb
bionickatana 87d2141ed0
CI / scan_ruby (push) Failing after 7s
CI / scan_js (push) Failing after 7s
CI / lint (push) Failing after 8s
Finished phase006
2026-09-16 16:37:03 -06:00

111 lines
2.4 KiB
Ruby

class MaintenanceScheduleStatus
UPCOMING_PERCENT = 0.05
attr_reader :schedule, :latest_entry, :status, :due_odometer, :due_date
def initialize(schedule, latest_entry, current_odometer:)
@schedule = schedule
@latest_entry = latest_entry
@current_odometer = current_odometer
@due_odometer = baseline_odometer + schedule.mileage_interval
@due_date = baseline_date + schedule.time_interval.days
@status = calculate_status
end
def overdue?
status == "overdue"
end
def upcoming?
status == "upcoming"
end
def no_history?
status == "no_history"
end
def current?
status == "current"
end
def status_label
case status
when "overdue" then "Overdue"
when "upcoming" then "Upcoming"
when "no_history" then "No history"
else "Current"
end
end
def reason
case status
when "overdue"
overdue_reasons.join(" and ")
when "upcoming"
upcoming_reasons.join(" and ")
when "no_history"
"No matching maintenance entry has been recorded."
else
"Maintenance is current."
end
end
private
attr_reader :current_odometer
def calculate_status
return "overdue" if mileage_overdue? || time_overdue?
return "upcoming" if mileage_upcoming? || time_upcoming?
return "no_history" if latest_entry.blank?
"current"
end
def baseline_odometer
latest_entry&.odometer || schedule.baseline_odometer
end
def baseline_date
latest_entry&.date || schedule.baseline_date
end
def mileage_overdue?
current_odometer >= due_odometer
end
def time_overdue?
Date.current >= due_date
end
def mileage_upcoming?
current_odometer >= due_odometer - mileage_upcoming_window
end
def time_upcoming?
Date.current >= due_date - time_upcoming_window.days
end
def mileage_upcoming_window
(schedule.mileage_interval * UPCOMING_PERCENT).ceil
end
def time_upcoming_window
(schedule.time_interval * UPCOMING_PERCENT).ceil
end
def overdue_reasons
reasons = []
reasons << "mileage interval reached" if mileage_overdue?
reasons << "time interval reached" if time_overdue?
reasons
end
def upcoming_reasons
reasons = []
reasons << "within #{mileage_upcoming_window} miles of due odometer" if mileage_upcoming?
reasons << "within #{time_upcoming_window} days of due date" if time_upcoming?
reasons
end
end