Functional Weave
Code in Rust

payroll.pro-rata-salary@1.0.1

impl/rust.rs

2,650 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, days_between};  ← from dates.add-days ^1.0.0 · built alongside by fune
use super::dates_business_days_between::business_days_between;  ← from dates.business-days-between ^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

fn count_days(from: &str, to: &str, method: &str, holidays: &[String]) -> i64 {
    match method {
        "calendar-days" => days_between(from, to) + 1,
        "working-days" => business_days_between(from, &add_days(to, 1), holidays),
        other => panic!("unknown pro-rata method \"{}\"", other),
    }
}

/// Pro-rate a period's salary by time employed and FTE, rounding once.
///
/// # Panics
/// Panics on an FTE outside 1-10000, reversed or out-of-period dates, an
/// unknown method, or a working-days period with no working days.
#[allow(clippy::too_many_arguments)]
pub fn pro_rata_salary(
    period_salary: &Money,
    fte_basis_points: i64,
    period_start: &str,
    period_end: &str,
    worked_from: &str,
    worked_to: &str,
    method: &str,
    holidays: &[String],
) -> ProRata {
    if !(1..=10000).contains(&fte_basis_points) {
        panic!("fteBasisPoints must be a whole number from 1 to 10000, received {}", fte_basis_points);
    }
    if days_between(period_start, period_end) < 0 {
        panic!("periodEnd is before periodStart");
    }
    if days_between(worked_from, worked_to) < 0 {
        panic!("workedTo is before workedFrom");
    }
    if worked_from < period_start || worked_to > period_end {
        panic!("the worked dates must fall inside the pay period");
    }
    let days_in_period = count_days(period_start, period_end, method, holidays);
    if days_in_period == 0 {
        panic!("the pay period has no working days to pro-rate over");
    }
    let days_counted = count_days(worked_from, worked_to, method, holidays);
    let minor = round_div(
        period_salary.minor * fte_basis_points * days_counted,
        10000 * days_in_period,
        "half-up",
    );
    ProRata { pay: money(minor, &period_salary.currency), days_counted, days_in_period }
}

pub fn fune_vector(args: &[Value]) -> Value {
    let holidays: Vec<String> = args[7].as_arr().iter().map(|v| v.as_str().to_string()).collect();
    let r = pro_rata_salary(
        &money_from_value(&args[0]),
        args[1].as_i64(),
        args[2].as_str(),
        args[3].as_str(),
        args[4].as_str(),
        args[5].as_str(),
        args[6].as_str(),
        &holidays,
    );
    Value::obj(vec![
        ("pay", money_to_value(&r.pay)),
        ("daysCounted", Value::Int(r.days_counted)),
        ("daysInPeriod", Value::Int(r.days_in_period)),
    ])
}