Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::dates_days_between::days_between; ← from dates.days-between ^1.0.0 · built alongside by fune
use super::lending_daily_interest::{daily_interest, dated_balance_from_value, DatedBalance}; ← from lending.daily-interest ^1.0.0 · built alongside by fune
use super::money_amount::{money, money_from_value, money_to_value, Money}; ← from money.amount ^1.0.0 · built alongside by fune
/// Overdraft interest over a statement period. Each day the account is
/// overdrawn, the part of the overdrawn amount above the interest-free buffer
/// accrues interest; credit balances earn nothing here. The accrual itself is
/// lending.daily-interest on those chargeable amounts, so it is summed exactly
/// and rounded once for the period.
///
/// # Panics
/// Panics on a negative rate or buffer, mixed currencies, or anything
/// lending.daily-interest refuses.
pub fn overdraft_interest(
balances: &[DatedBalance],
from_iso: &str,
to_iso: &str,
annual_rate_basis_points: i64,
interest_free_buffer: &Money,
convention: &str,
mode: &str,
) -> OverdraftInterest {
if annual_rate_basis_points < 0 {
panic!("annualRateBasisPoints must not be negative, received {}", annual_rate_basis_points);
}
if interest_free_buffer.minor < 0 {
panic!("interestFreeBuffer must not be negative, received {}", interest_free_buffer.minor);
}
for entry in balances {
if entry.balance.currency != interest_free_buffer.currency {
panic!("currency mismatch: {} and {}", entry.balance.currency, interest_free_buffer.currency);
}
}
let chargeable: Vec<DatedBalance> = balances
.iter()
.map(|entry| DatedBalance {
date: entry.date.clone(),
balance: money((-entry.balance.minor - interest_free_buffer.minor).max(0), &entry.balance.currency),
})
.collect();
// Validates the dates, their order and the period before anything is counted.
let accrued = daily_interest(&chargeable, from_iso, to_iso, annual_rate_basis_points, convention, mode);
let mut days_overdrawn = 0i64;
let mut days_charged = 0i64;
for i in 0..balances.len() {
let date = balances[i].date.as_str();
let start = if date > from_iso { date } else { from_iso };
let next = if i + 1 < balances.len() { balances[i + 1].date.as_str() } else { to_iso };
let end = if next < to_iso { next } else { to_iso };
if start >= end {
continue;
}
let days = days_between(start, end);
if balances[i].balance.minor < 0 {
days_overdrawn += days;
}
if chargeable[i].balance.minor > 0 {
days_charged += days;
}
}
OverdraftInterest {
interest: accrued.interest,
days_overdrawn,
days_charged,
}
}
pub fn overdraft_interest_to_value(result: &OverdraftInterest) -> Value {
Value::obj(vec![
("interest", money_to_value(&result.interest)),
("daysOverdrawn", Value::Int(result.days_overdrawn)),
("daysCharged", Value::Int(result.days_charged)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
let balances: Vec<DatedBalance> = args[0].as_arr().iter().map(dated_balance_from_value).collect();
overdraft_interest_to_value(&overdraft_interest(
&balances,
args[1].as_str(),
args[2].as_str(),
args[3].as_i64(),
&money_from_value(&args[4]),
args[5].as_str(),
args[6].as_str(),
))
}