Functional Weave
Code in Rust

finance.depreciation-straight-line@1.0.0

impl/typescript.ts

2,565 bytes · the TypeScript 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.

import { allocate } from "./money_allocate.ts";  ← from money.allocate ^1.0.0 · built alongside by fune
import { subtractMoney } from "./money_add.ts";  ← from money.add ^1.0.0 · built alongside by fune
import type { Money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { type DepreciationPeriod } from "./finance_depreciation_straight_line_types.ts";

/**
 * 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.
 */
export function straightLineDepreciation(
  cost: Money,
  residualValue: Money,
  years: number,
): readonly DepreciationPeriod[] {
  if (!Number.isInteger(years) || years < 1) {
    throw new RangeError(`years must be at least 1, received ${years}`);
  }
  if (cost.minor < 0) {
    throw new RangeError("cost must not be negative");
  }
  if (residualValue.minor < 0) {
    throw new RangeError("residual value must not be negative");
  }

  // subtractMoney also rejects a cost and a residual value in different
  // currencies, which would otherwise produce a plausible-looking schedule.
  const depreciable = subtractMoney(cost, residualValue);
  if (depreciable.minor < 0) {
    throw new RangeError("residual value must not exceed cost");
  }

  const charges = allocate(depreciable, new Array<number>(years).fill(1));

  const schedule: DepreciationPeriod[] = [];
  let openingValue = cost;
  for (let index = 0; index < years; index += 1) {
    const charge = charges[index];
    const closingValue = subtractMoney(openingValue, charge);
    schedule.push({ year: index + 1, openingValue, charge, closingValue });
    openingValue = closingValue;
  }
  return schedule;
}

/** The written-down value after `elapsedYears` complete years. */
export function writtenDownValue(cost: Money, residualValue: Money, years: number, elapsedYears: number): Money {
  const schedule = straightLineDepreciation(cost, residualValue, years);
  if (!Number.isInteger(elapsedYears) || elapsedYears < 0 || elapsedYears > years) {
    throw new RangeError(`elapsedYears must be between 0 and ${years}, received ${elapsedYears}`);
  }
  return elapsedYears === 0 ? cost : schedule[elapsedYears - 1].closingValue;
}