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::finance_markup_price::markup_price; ← from finance.markup-price ^1.0.0 · built alongside by fune
use super::finance_tax_add_vat::add_vat; ← from finance.tax.add-vat ^1.0.0 · built alongside by fune
use super::math_round_div::round_div; ← from math.round-div ^1.0.0 · built alongside by fune
use super::money_add::add_money; ← from money.add ^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
const MAX_SAFE: i128 = 9_007_199_254_740_991;
/// A quote from labour days, a day rate, marked-up materials and VAT.
///
/// The markup goes on the materials only: the day rate already is the price of
/// labour. Labour is rounded once, to the penny, from days x rate; VAT is one
/// rounding on the net total, as on the invoice that follows.
///
/// # Panics
/// Panics on negative days or rates, mixed currencies, an unknown VAT
/// category, or a product beyond 2^53 - 1.
pub fn day_rate_quote(
labour_days_hundredths: i64,
day_rate: &Money,
materials_cost: &Money,
materials_markup_basis_points: i64,
vat_category: Option<&str>,
on_date: &str,
) -> DayRateQuote {
if labour_days_hundredths < 0 {
panic!(
"labourDaysHundredths must be a whole number of hundredths, not negative, received {}",
labour_days_hundredths
);
}
if day_rate.minor < 0 {
panic!("dayRate must not be negative, received {}", day_rate.minor);
}
let product = day_rate.minor as i128 * labour_days_hundredths as i128;
// i64 could go further, but TypeScript cannot, and all three must agree.
if product > MAX_SAFE {
panic!("the labour calculation exceeds 2^53 - 1");
}
let labour = money(round_div(product as i64, 100, "half-up"), &day_rate.currency);
let materials = markup_price(materials_cost, materials_markup_basis_points, "markup", "half-up");
let net = add_money(&labour, &materials);
match vat_category {
None => DayRateQuote {
vat: money(0, &net.currency),
gross: net.clone(),
labour,
materials,
net,
vat_basis_points: 0,
},
Some(category) => {
let vat = add_vat(&net, "GB", category, on_date);
DayRateQuote {
labour,
materials,
net,
vat: vat.tax,
gross: vat.gross,
vat_basis_points: vat.basis_points,
}
}
}
}
pub fn day_rate_quote_to_value(q: &DayRateQuote) -> Value {
Value::obj(vec![
("labour", money_to_value(&q.labour)),
("materials", money_to_value(&q.materials)),
("net", money_to_value(&q.net)),
("vat", money_to_value(&q.vat)),
("gross", money_to_value(&q.gross)),
("vatBasisPoints", Value::Int(q.vat_basis_points)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
if let Value::Float(f) = &args[0] {
panic!(
"labourDaysHundredths must be a whole number of hundredths, not negative, received {}",
f
);
}
if let Value::Float(f) = &args[3] {
panic!("basisPoints must be an integer, received {}", f);
}
let category = if args[4].is_null() { None } else { Some(args[4].as_str()) };
day_rate_quote_to_value(&day_rate_quote(
args[0].as_i64(),
&money_from_value(&args[1]),
&money_from_value(&args[2]),
args[3].as_i64(),
category,
args[5].as_str(),
))
}