Functional Weave
Code in Rust

finance.depreciation-straight-line@1.0.0

impl/python.py

2,677 bytes · the Python implementation · view raw

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 List

from .finance_depreciation_straight_line_types import DepreciationPeriod
from .money_add import subtract_money  ← from money.add ^1.0.0 · built alongside by fune
from .money_allocate import allocate  ← from money.allocate ^1.0.0 · built alongside by fune
from .money_amount import Money  ← from money.amount ^1.0.0 · built alongside by fune


def straight_line_depreciation(cost: Money, residual_value: Money, years: int) -> List[DepreciationPeriod]:
    """A straight-line depreciation schedule, year by year.

    The depreciable amount is split with ``money.allocate`` instead of being
    divided by the number of years. Dividing leaves 1000.00 over three years as
    333.33 three times - a penny short - and an asset that never quite finishes
    writing down to its residual value. ``allocate`` hands out every minor unit,
    so the last closing value equals the residual value exactly, by construction
    rather than by a fudge at the end.

    The odd units go to the earliest years, following ``allocate``'s largest-
    remainder rule rather than a second rule invented here.
    """
    if isinstance(years, bool) or not isinstance(years, int) or years < 1:
        raise ValueError("years must be at least 1, received %r" % (years,))
    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 rejects a cost and a residual value in different
    # currencies, which would otherwise produce a plausible-looking schedule.
    depreciable = subtract_money(cost, residual_value)
    if depreciable.minor < 0:
        raise ValueError("residual value must not exceed cost")

    charges = allocate(depreciable, [1] * years)

    schedule: List[DepreciationPeriod] = []
    opening_value = cost
    for index in range(years):
        charge = charges[index]
        closing_value = subtract_money(opening_value, charge)
        schedule.append(
            DepreciationPeriod(
                year=index + 1,
                opening_value=opening_value,
                charge=charge,
                closing_value=closing_value,
            )
        )
        opening_value = closing_value
    return schedule


def written_down_value(cost: Money, residual_value: Money, years: int, elapsed_years: int) -> Money:
    """The written-down value after ``elapsed_years`` complete years."""
    schedule = straight_line_depreciation(cost, residual_value, years)
    if isinstance(elapsed_years, bool) or not isinstance(elapsed_years, int) or not 0 <= elapsed_years <= years:
        raise ValueError("elapsed_years must be between 0 and %d, received %r" % (years, elapsed_years))
    return cost if elapsed_years == 0 else schedule[elapsed_years - 1].closing_value