Functional Weave
Code in TypeScript

logistics.duty-estimate@1.0.1

impl/rust.rs

2,874 bytes · the Rust 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.

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::vat_rate;  ← from finance.tax.vat-rate ^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_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

fn non_negative(name: &str, amount: &Money) {
    if amount.minor < 0 {
        panic!("{} must not be negative, received {}", name, amount.minor);
    }
}

/// Import duty on the CIF customs value, then import VAT on the customs value
/// plus duty plus the VAT-only additions, at the rate in force on import_date.
///
/// # Panics
/// Panics on a negative amount or duty rate, mixed currencies, an unknown
/// rounding mode, or no VAT rule for the jurisdiction, category and date.
#[allow(clippy::too_many_arguments)]
pub fn duty_estimate(
    goods: &Money,
    freight: &Money,
    insurance: &Money,
    duty_basis_points: i64,
    vat_additions: &Money,
    jurisdiction: &str,
    vat_category: &str,
    import_date: &str,
    rounding: &str,
) -> DutyEstimate {
    non_negative("goods", goods);
    non_negative("freight", freight);
    non_negative("insurance", insurance);
    non_negative("vatAdditions", vat_additions);
    if duty_basis_points < 0 {
        panic!("dutyBasisPoints must not be negative, received {}", duty_basis_points);
    }

    // CIF: duty is charged on what it cost to get the goods to the border.
    let customs_value = add_money(&add_money(goods, freight), insurance);
    let duty = apply_rate(&customs_value, duty_basis_points, rounding);
    // VAT is charged on the duty too, and on costs after the border.
    let vat_value = add_money(&add_money(&customs_value, &duty), vat_additions);
    let rate = vat_rate(jurisdiction, vat_category, import_date);
    let import_vat = apply_rate(&vat_value, rate.basis_points, rounding);
    DutyEstimate {
        total_payable: add_money(&duty, &import_vat),
        customs_value,
        duty_basis_points,
        duty,
        vat_value,
        vat_basis_points: rate.basis_points,
        import_vat,
    }
}

pub fn duty_estimate_to_value(e: &DutyEstimate) -> Value {
    Value::obj(vec![
        ("customsValue", money_to_value(&e.customs_value)),
        ("dutyBasisPoints", Value::Int(e.duty_basis_points)),
        ("duty", money_to_value(&e.duty)),
        ("vatValue", money_to_value(&e.vat_value)),
        ("vatBasisPoints", Value::Int(e.vat_basis_points)),
        ("importVat", money_to_value(&e.import_vat)),
        ("totalPayable", money_to_value(&e.total_payable)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    duty_estimate_to_value(&duty_estimate(
        &money_from_value(&args[0]),
        &money_from_value(&args[1]),
        &money_from_value(&args[2]),
        args[3].as_i64(),
        &money_from_value(&args[4]),
        args[5].as_str(),
        args[6].as_str(),
        args[7].as_str(),
        args[8].as_str(),
    ))
}