insurance.mid-term-adjustment@1.0.1
impl/typescript.ts
1,921 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 { daysBetween } from "./dates_days_between.ts"; ← from dates.days-between ^1.0.0 · built alongside by fune
import { type RoundingMode, roundDiv } from "./math_round_div.ts"; ← from math.round-div ^1.0.0 · built alongside by fune
import { type Money, assertSameCurrency, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { type MidTermAdjustment } from "./insurance_mid_term_adjustment_types.ts";
/**
* The additional or return premium for a change part-way through a policy term.
*
* The difference between the two full-term premiums is pro-rated over the
* days left, with one rounding. Pro-rating the old and new premiums separately
* and subtracting rounds twice and can be a penny out.
*/
export function midTermAdjustment(
oldPremium: Money,
newPremium: Money,
inceptionDate: string,
expiryDate: string,
changeDate: string,
adminFee: Money,
mode: RoundingMode
): MidTermAdjustment {
assertSameCurrency(oldPremium, newPremium);
assertSameCurrency(oldPremium, adminFee);
if (oldPremium.minor < 0 || newPremium.minor < 0) {
throw new RangeError("premiums must not be negative");
}
if (adminFee.minor < 0) {
throw new RangeError(`adminFee must not be negative, received ${adminFee.minor}`);
}
const totalDays = daysBetween(inceptionDate, expiryDate);
if (totalDays <= 0) {
throw new RangeError(`expiryDate must be after inceptionDate, received ${inceptionDate} to ${expiryDate}`);
}
const remainingDays = daysBetween(changeDate, expiryDate);
if (remainingDays < 0 || remainingDays > totalDays) {
throw new RangeError(`changeDate must be from inceptionDate to expiryDate, received ${changeDate}`);
}
const change = roundDiv((newPremium.minor - oldPremium.minor) * remainingDays, totalDays, mode);
const currency = oldPremium.currency;
return {
totalDays,
remainingDays,
premiumChange: money(change, currency),
adminFee,
amountDue: money(change + adminFee.minor, currency),
kind: change > 0 ? "additional" : change < 0 ? "return" : "none",
};
}