Functional Weave
Code in Python

charity.payroll-giving@1.0.0

impl/rust.rs

2,674 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::money_amount::{money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune
use super::payroll_income_tax::income_tax;  ← from payroll.income-tax ^1.0.0 · built alongside by fune

/// Payroll Giving (ITEPA 2003 s713): the donation is deducted from pay before
/// PAYE is worked out, so the relief is whatever PAYE the lower pay saves, by
/// the same tax table routines the payroll uses. PAYE rounds taxable pay down
/// to whole pounds, so a 50p donation can save nothing.
///
/// # Panics
/// Panics on a non-GBP or negative donation, a donation above pay, and
/// anything payroll.income-tax refuses.
pub fn payroll_giving(
    tax_code: &str,
    frequency: &str,
    period: i64,
    pay: &Money,
    donation: &Money,
    previous_pay_to_date: &Money,
    previous_tax_to_date: &Money,
    pay_date: &str,
) -> PayrollGiving {
    if donation.currency != "GBP" {
        panic!("payroll amounts must be in GBP, received {} for donation", donation.currency);
    }
    if donation.minor < 0 {
        panic!("donation must not be negative, received {}", donation.minor);
    }
    if donation.minor > pay.minor {
        panic!("donation of {} is more than pay of {}", donation.minor, pay.minor);
    }
    let taxable_pay = money(pay.minor - donation.minor, "GBP");
    let without = income_tax(tax_code, frequency, period, pay, previous_pay_to_date, previous_tax_to_date, pay_date).tax.minor;
    let with_giving =
        income_tax(tax_code, frequency, period, &taxable_pay, previous_pay_to_date, previous_tax_to_date, pay_date).tax.minor;
    PayrollGiving {
        taxable_pay,
        tax_without_giving: money(without, "GBP"),
        tax_with_giving: money(with_giving, "GBP"),
        tax_saved: money(without - with_giving, "GBP"),
        net_cost: money(donation.minor - (without - with_giving), "GBP"),
    }
}

pub fn payroll_giving_to_value(g: &PayrollGiving) -> Value {
    Value::obj(vec![
        ("taxablePay", money_to_value(&g.taxable_pay)),
        ("taxWithoutGiving", money_to_value(&g.tax_without_giving)),
        ("taxWithGiving", money_to_value(&g.tax_with_giving)),
        ("taxSaved", money_to_value(&g.tax_saved)),
        ("netCost", money_to_value(&g.net_cost)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    if let Value::Float(_) = args[2] {
        panic!("period {} is not a tax period for {} pay", args[2].as_f64(), args[1].as_str());
    }
    payroll_giving_to_value(&payroll_giving(
        args[0].as_str(),
        args[1].as_str(),
        args[2].as_i64(),
        &money_from_value(&args[3]),
        &money_from_value(&args[4]),
        &money_from_value(&args[5]),
        &money_from_value(&args[6]),
        args[7].as_str(),
    ))
}