use super::funejson::Value; use super::math_round_div::round_div; use super::money_amount::{money, money_from_value, money_to_value, Money}; use super::money_sum::sum_money; /// Price the minutes worked band by band, rounding once per payslip line. /// /// # Panics /// Panics on negative minutes, an empty or unordered band list, or a band /// that does not start at 0. pub fn overtime_pay(minutes_worked: i64, hourly_rate: &Money, bands: &[OvertimeBand]) -> OvertimePay { if minutes_worked < 0 { panic!("minutesWorked must be a whole number of minutes, not negative, received {}", minutes_worked); } if bands.is_empty() { panic!("overtime needs at least one band"); } if bands[0].from_minutes != 0 { panic!("the first band must start at 0 minutes"); } for i in 1..bands.len() { if bands[i].from_minutes <= bands[i - 1].from_minutes { panic!("bands must be in strictly ascending order of fromMinutes"); } } let mut lines: Vec = Vec::new(); for i in 0..bands.len() { let start = bands[i].from_minutes; let end = if i + 1 < bands.len() { bands[i + 1].from_minutes.min(minutes_worked) } else { minutes_worked }; let minutes = end - start; if minutes <= 0 { break; } let multiplier = bands[i].multiplier_basis_points; if multiplier < 0 { panic!("multiplierBasisPoints must be a whole number, not negative, received {}", multiplier); } let pay = money(round_div(hourly_rate.minor * minutes * multiplier, 60 * 10000, "half-up"), &hourly_rate.currency); lines.push(OvertimeLine { minutes, multiplier_basis_points: multiplier, pay }); } let pays: Vec = lines.iter().map(|l| l.pay.clone()).collect(); let total = sum_money(&pays, &hourly_rate.currency); OvertimePay { lines, total } } pub fn fune_vector(args: &[Value]) -> Value { if let Value::Float(f) = &args[0] { panic!("minutesWorked must be a whole number of minutes, not negative, received {}", f); } let bands: Vec = args[2] .as_arr() .iter() .map(|b| OvertimeBand { from_minutes: b.get("fromMinutes").as_i64(), multiplier_basis_points: b.get("multiplierBasisPoints").as_i64(), }) .collect(); let result = overtime_pay(args[0].as_i64(), &money_from_value(&args[1]), &bands); Value::obj(vec![ ( "lines", Value::Arr( result .lines .iter() .map(|l| { Value::obj(vec![ ("minutes", Value::Int(l.minutes)), ("multiplierBasisPoints", Value::Int(l.multiplier_basis_points)), ("pay", money_to_value(&l.pay)), ]) }) .collect(), ), ), ("total", money_to_value(&result.total)), ]) }