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;
}