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_add_days::add_days; ← from dates.add-days ^1.0.0 · built alongside by fune
use super::dates_add_months::add_months; ← from dates.add-months ^1.0.0 · built alongside by fune
use super::finance_tax_vat_flat_rate_data::{FLAT_RATES, FLAT_RATES_HISTORY}; ← this capability’s own data, compiled from data/flat-rates.json into the same file by fune build
use super::money_amount::{money, money_from_value, money_to_value, Money}; ← from money.amount ^1.0.0 · built alongside by fune
use super::money_apply_rate::apply_rate; ← from money.apply-rate ^1.0.0 · built alongside by fune
const LIMITED_COST: &str = "limited-cost-business";
fn rate_on(sector: &str, on_date: &str) -> i64 {
let rows: Vec<_> = FLAT_RATES.iter().filter(|r| r.sector == sector).collect();
for row in &rows {
if on_date >= row.valid_from && row.valid_to.map_or(true, |to| on_date <= to) {
return row.basis_points;
}
}
if FLAT_RATES_HISTORY != "full" {
if let Some(earliest) = rows.iter().map(|r| r.valid_from).min() {
if on_date < earliest {
panic!(
"no flat rate for {} on {}: this build was installed with history={}, so it only carries rates from {}. Reinstall with history=full for older periods.",
sector, on_date, FLAT_RATES_HISTORY, earliest
);
}
}
}
panic!("no flat rate for {} on {}", sector, on_date);
}
/// VAT due under the Flat Rate Scheme on a slice of VAT-inclusive turnover.
///
/// The rate is the one in force on the supply date; the first-year reduction
/// runs to the day before the first anniversary of registration.
///
/// # Panics
/// Panics on non-GBP turnover, an unknown sector, a date without a rate, or a
/// supply before the registration date.
pub fn flat_rate_vat(
turnover: &Money,
sector: &str,
limited_cost_business: bool,
registration_date: Option<&str>,
supply_date: &str,
) -> FlatRateVat {
if turnover.currency != "GBP" {
panic!("the flat rate scheme is for GBP turnover, received {}", turnover.currency);
}
add_days(supply_date, 0);
if sector == LIMITED_COST || !FLAT_RATES.iter().any(|r| r.sector == sector) {
panic!("unknown flat rate sector \"{}\"", sector);
}
let base = rate_on(if limited_cost_business { LIMITED_COST } else { sector }, supply_date);
let mut reduced = false;
if let Some(registered) = registration_date {
if supply_date < add_days(registered, 0).as_str() {
panic!(
"supply date {} is before the VAT registration date {}",
supply_date, registered
);
}
reduced = supply_date < add_months(registered, 12).as_str();
}
let basis_points = if reduced { (base - 100).max(0) } else { base };
FlatRateVat {
sector: sector.to_string(),
limited_cost_business,
first_year_reduction: reduced,
basis_points,
vat_due: money(apply_rate(turnover, basis_points, "half-up").minor, "GBP"),
}
}
pub fn flat_rate_vat_to_value(v: &FlatRateVat) -> Value {
Value::obj(vec![
("sector", Value::str(&v.sector)),
("limitedCostBusiness", Value::Bool(v.limited_cost_business)),
("firstYearReduction", Value::Bool(v.first_year_reduction)),
("basisPoints", Value::Int(v.basis_points)),
("vatDue", money_to_value(&v.vat_due)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
let registration = if args[3].is_null() { None } else { Some(args[3].as_str()) };
flat_rate_vat_to_value(&flat_rate_vat(
&money_from_value(&args[0]),
args[1].as_str(),
args[2].as_bool(),
registration,
args[4].as_str(),
))
}