from typing import List from .finance_depreciation_straight_line_types import DepreciationPeriod from .money_add import subtract_money from .money_amount import Money from .money_apply_rate import apply_rate def reducing_balance_depreciation( cost: Money, residual_value: Money, rate_basis_points: int, years: int ) -> List[DepreciationPeriod]: """A reducing-balance depreciation schedule, year by year. Each charge is the rate applied to the opening value, rounded once. The last year writes off down to the residual value, and no year may go below it. """ if isinstance(years, bool) or not isinstance(years, int) or years < 1: raise ValueError("years must be at least 1, received %r" % (years,)) if ( isinstance(rate_basis_points, bool) or not isinstance(rate_basis_points, int) or rate_basis_points < 0 or rate_basis_points > 10000 ): raise ValueError("rate must be 0 to 10000 basis points, received %r" % (rate_basis_points,)) if cost.minor < 0: raise ValueError("cost must not be negative") if residual_value.minor < 0: raise ValueError("residual value must not be negative") # subtract_money also refuses a cost and residual value in different currencies. if subtract_money(cost, residual_value).minor < 0: raise ValueError("residual value must not exceed cost") schedule: List[DepreciationPeriod] = [] opening_value = cost for year in range(1, years + 1): headroom = subtract_money(opening_value, residual_value) charge = headroom if year == years else apply_rate(opening_value, rate_basis_points, "half-up") if charge.minor > headroom.minor: charge = headroom closing_value = subtract_money(opening_value, charge) schedule.append( DepreciationPeriod(year=year, opening_value=opening_value, charge=charge, closing_value=closing_value) ) opening_value = closing_value return schedule