use super::funejson::Value; use super::dates_add_days::{add_days, days_between}; use super::dates_business_days_between::business_days_between; use super::math_round_div::round_div; use super::money_amount::{money, money_from_value, money_to_value, Money}; 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 = 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)), ]) }