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 Sequence
from .fleet_vehicle_depreciation_types import VehicleValue
from .math_round_div import RoundingMode ← from math.round-div ^1.0.0 · built alongside by fune
from .math_round_div_big import round_div_big ← from math.round-div-big ^1.0.0 · built alongside by fune
from .money_amount import Money, money ← from money.amount ^1.0.0 · built alongside by fune
def _whole(name: str, value: int, maximum: int) -> None:
if isinstance(value, bool) or not isinstance(value, int) or value < 0 or value > maximum:
raise ValueError("%s must be a whole number from 0 to %d, received %r" % (name, maximum, value))
def vehicle_depreciation(
cost: Money,
age_months: int,
mileage: int,
retained_per_year_basis_points: Sequence[int],
retained_per10k_miles_basis_points: int,
mode: RoundingMode,
) -> VehicleValue:
"""A vehicle's value from its age and mileage, on the caller's retention curves.
Each whole year multiplies by that year's retention and a part year by a
straight-line share of it; each whole 10,000 miles multiplies by the
mileage retention and the remaining miles by a straight-line share. The
product is kept as one exact fraction and rounded once: rounding the value
year by year drifts by a penny or more over a long life.
"""
if cost.minor < 0:
raise ValueError("cost must not be negative, received %d" % (cost.minor,))
_whole("ageMonths", age_months, 600)
_whole("mileage", mileage, 2000000)
if len(retained_per_year_basis_points) == 0:
raise ValueError("retainedPerYearBasisPoints must have at least one year")
for bp in retained_per_year_basis_points:
_whole("a retention rate", bp, 10000)
_whole("a retention rate", retained_per10k_miles_basis_points, 10000)
last = len(retained_per_year_basis_points) - 1
def rate_for(year: int) -> int:
return retained_per_year_basis_points[min(year, last)]
num, den = 1, 1
years = age_months // 12
for year in range(years):
num *= rate_for(year)
den *= 10000
months = age_months % 12
if months > 0:
num *= 120000 - (10000 - rate_for(years)) * months
den *= 120000
mile_rate = retained_per10k_miles_basis_points
for _ in range(mileage // 10000):
num *= mile_rate
den *= 10000
miles = mileage % 10000
if miles > 0:
num *= 100000000 - (10000 - mile_rate) * miles
den *= 100000000
value = int(round_div_big(str(cost.minor * num), str(den), mode))
return VehicleValue(
value=money(value, cost.currency),
depreciation=money(cost.minor - value, cost.currency),
retained_basis_points=int(round_div_big(str(num * 10000), str(den), "half-up")),
)