Functional Weave
Code in TypeScript

payroll.redundancy-pay@1.0.1

impl/rust.rs

3,064 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::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::dates_age::age;  ← from dates.age ^1.0.0 · built alongside by fune
use super::hr_length_of_service::length_of_service;  ← from hr.length-of-service ^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_amount::{money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune
use super::payroll_redundancy_pay_data::{WEEK_PAY_CAPS, WEEK_PAY_CAPS_HISTORY};  ← this capability’s own data, compiled from data/week-pay-caps.json into the same file by fune build

fn week_pay_cap(relevant_date: &str) -> i64 {
    for row in WEEK_PAY_CAPS {
        if relevant_date >= row.valid_from && row.valid_to.map_or(true, |to| relevant_date <= to) {
            return row.cap_minor;
        }
    }
    if WEEK_PAY_CAPS_HISTORY != "full" {
        let earliest = WEEK_PAY_CAPS.iter().map(|r| r.valid_from).min();
        if let Some(earliest) = earliest {
            if relevant_date < earliest {
                panic!(
                    "no statutory week's pay cap for {}: this build was installed with history={}, so it only carries caps from {}. Reinstall with history=full for older dismissals.",
                    relevant_date, WEEK_PAY_CAPS_HISTORY, earliest
                );
            }
        }
    }
    panic!("no statutory week's pay cap for {}", relevant_date)
}

/// ERA 1996 s.162 redundancy pay. Years are reckoned backwards from the
/// relevant date and each is priced by the age on its first day, so the rate
/// steps up only for years spent wholly at 22 or 41 and over.
///
/// # Panics
/// Panics on malformed dates, negative pay, or a relevant date with no cap.
pub fn statutory_redundancy_pay(birth_date: &str, start_date: &str, relevant_date: &str, weekly_pay: &Money) -> RedundancyPay {
    if weekly_pay.minor < 0 {
        panic!("weeklyPay must not be negative, received {}", weekly_pay.minor);
    }
    let cap = week_pay_cap(relevant_date);
    let weekly_pay_used = money(weekly_pay.minor.min(cap), &weekly_pay.currency);
    let service_years = length_of_service(start_date, relevant_date).years;
    let eligible = service_years >= 2;
    let years_counted = if eligible { service_years.min(20) } else { 0 };
    let mut half_weeks = 0;
    for k in 1..=years_counted {
        let first_day = add_days(&add_months(relevant_date, -12 * k), 1);
        let age_then = age(birth_date, &first_day);
        half_weeks += if age_then >= 41 { 3 } else if age_then >= 22 { 2 } else { 1 };
    }
    let amount = money(round_div(half_weeks * weekly_pay_used.minor, 2, "half-up"), &weekly_pay.currency);
    RedundancyPay { eligible, service_years, years_counted, half_weeks, weekly_pay_used, amount }
}

pub fn fune_vector(args: &[Value]) -> Value {
    let r = statutory_redundancy_pay(args[0].as_str(), args[1].as_str(), args[2].as_str(), &money_from_value(&args[3]));
    Value::obj(vec![
        ("eligible", Value::Bool(r.eligible)),
        ("serviceYears", Value::Int(r.service_years)),
        ("yearsCounted", Value::Int(r.years_counted)),
        ("halfWeeks", Value::Int(r.half_weeks)),
        ("weeklyPayUsed", money_to_value(&r.weekly_pay_used)),
        ("amount", money_to_value(&r.amount)),
    ])
}