Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import Optional
from .dates_add_days import add_days ← from dates.add-days ^1.0.0 · built alongside by fune
from .dates_add_months import add_months ← from dates.add-months ^1.0.0 · built alongside by fune
from .dates_days_between import days_between ← from dates.days-between ^1.0.0 · built alongside by fune
from .fleet_service_due_types import ServiceDue, ServiceTrigger
from .math_round_div import round_div ← from math.round-div ^1.0.0 · built alongside by fune
def _whole_number(name: str, value: int) -> None:
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
raise ValueError("%s must be a whole number, 0 or more, received %r" % (name, value))
def service_due(
last_service_date: str,
last_service_mileage: int,
interval_months: int,
interval_miles: int,
on_date: str,
current_mileage: int,
) -> ServiceDue:
"""The next service, by time or mileage, whichever comes first.
"Whichever comes first" needs a date for the mileage limit too, so it is
projected from the average daily mileage since the last service, rounded up
to a whole day: a service booked for the projected day is never late on
that estimate.
"""
_whole_number("lastServiceMileage", last_service_mileage)
_whole_number("intervalMonths", interval_months)
_whole_number("intervalMiles", interval_miles)
_whole_number("currentMileage", current_mileage)
if interval_months == 0 and interval_miles == 0:
raise ValueError("at least one of intervalMonths and intervalMiles must be above zero")
days_since = days_between(last_service_date, on_date)
if days_since < 0:
raise ValueError("onDate %s is before the last service on %s" % (on_date, last_service_date))
miles_since = current_mileage - last_service_mileage
if miles_since < 0:
raise ValueError(
"currentMileage %d is below the mileage at the last service, %d" % (current_mileage, last_service_mileage)
)
due_date = add_months(last_service_date, interval_months) if interval_months > 0 else None
days_remaining = None if due_date is None else days_between(on_date, due_date)
due_mileage = last_service_mileage + interval_miles if interval_miles > 0 else None
miles_remaining = None if due_mileage is None else due_mileage - current_mileage
projected: Optional[str] = None
mileage_date: Optional[str] = None
if miles_remaining is not None:
if miles_remaining <= 0:
mileage_date = on_date
elif days_since > 0 and miles_since > 0:
projected = add_days(on_date, round_div(miles_remaining * days_since, miles_since, "up"))
mileage_date = projected
next_due: Optional[str] = due_date
due_by: Optional[ServiceTrigger] = None if due_date is None else "date"
if mileage_date is not None and (next_due is None or mileage_date < next_due):
next_due = mileage_date
due_by = "mileage"
return ServiceDue(
due_date=due_date,
due_mileage=due_mileage,
days_remaining=days_remaining,
miles_remaining=miles_remaining,
projected_mileage_date=projected,
next_due_date=next_due,
due_by=due_by,
overdue=(days_remaining is not None and days_remaining < 0)
or (miles_remaining is not None and miles_remaining < 0),
)