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_tax_vat_rate_data::{VAT_RULES, VAT_RULES_HISTORY}; ← this capability’s own data, compiled from data/vat-rules.json into the same file by fune build
fn is_iso_date(value: &str) -> bool {
let bytes = value.as_bytes();
bytes.len() == 10
&& bytes[4] == b'-'
&& bytes[7] == b'-'
&& bytes
.iter()
.enumerate()
.all(|(i, b)| i == 4 || i == 7 || b.is_ascii_digit())
}
/// The VAT rate in force for a jurisdiction and category on a given date.
///
/// The date matters. A credit note against a 2009 invoice has to use the rate
/// that applied in 2009, not the one that applies today, and a system that
/// hardcodes 0.20 cannot do that at all.
///
/// ISO dates compare correctly as strings, so this needs no date crate.
///
/// # Panics
/// Panics if the date is malformed, or no rule covers the request.
pub fn vat_rate(jurisdiction: &str, category: &str, on_date: &str) -> VatRate {
if !is_iso_date(on_date) {
panic!(
"on_date must be an ISO date (YYYY-MM-DD), received \"{}\"",
on_date
);
}
let mut best: Option<VatRate> = None;
for rule in VAT_RULES {
if rule.jurisdiction != jurisdiction || rule.category != category {
continue;
}
if on_date < rule.valid_from {
continue;
}
if let Some(valid_to) = rule.valid_to {
if on_date > valid_to {
continue;
}
}
let better = match &best {
None => true,
Some(current) => rule.valid_from > current.valid_from.as_str(),
};
if better {
best = Some(VatRate {
jurisdiction: rule.jurisdiction.to_string(),
category: rule.category.to_string(),
basis_points: rule.basis_points,
valid_from: rule.valid_from.to_string(),
valid_to: rule.valid_to.map(str::to_string),
});
}
}
match best {
Some(rate) => rate,
None => {
// A build installed with history=current carries only the rules
// still in force. Answering an older date with today's rate would
// be the exact failure this capability exists to prevent, so say
// what happened instead. The horizon that matters is this
// jurisdiction and category's own earliest surviving rule, not the
// registry-wide one.
if VAT_RULES_HISTORY != "full" {
let mut earliest: Option<&'static str> = None;
for rule in VAT_RULES {
if rule.jurisdiction != jurisdiction || rule.category != category {
continue;
}
if earliest.is_none() || rule.valid_from < earliest.unwrap() {
earliest = Some(rule.valid_from);
}
}
if let Some(earliest) = earliest {
if on_date < earliest {
panic!(
"no VAT rule for {}/{} on {}: this build was installed with history={}, so it only carries rules from {}. Reinstall with history=full to reprint older invoices.",
jurisdiction, category, on_date, VAT_RULES_HISTORY, earliest
);
}
}
}
panic!(
"no VAT rule for {}/{} on {}",
jurisdiction, category, on_date
)
}
}
}
/// Every category this registry knows a rate for, for a jurisdiction and date.
pub fn vat_categories(jurisdiction: &str, on_date: &str) -> Vec<&'static str> {
let mut seen: Vec<&'static str> = Vec::new();
for rule in VAT_RULES {
if rule.jurisdiction != jurisdiction || on_date < rule.valid_from {
continue;
}
if let Some(valid_to) = rule.valid_to {
if on_date > valid_to {
continue;
}
}
if !seen.contains(&rule.category) {
seen.push(rule.category);
}
}
seen.sort_unstable();
seen
}
pub fn vat_rate_to_value(rate: &VatRate) -> Value {
Value::obj(vec![
("jurisdiction", Value::str(&rate.jurisdiction)),
("category", Value::str(&rate.category)),
("basisPoints", Value::Int(rate.basis_points)),
("validFrom", Value::str(&rate.valid_from)),
(
"validTo",
match &rate.valid_to {
Some(date) => Value::str(date),
None => Value::Null,
},
),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
vat_rate_to_value(&vat_rate(
args[0].as_str(),
args[1].as_str(),
args[2].as_str(),
))
}