use super::funejson::Value; use super::dates_add_days::add_days; use super::dates_add_months::add_months; use super::dates_age::age; use super::hr_length_of_service::length_of_service; use super::math_round_div::round_div; use super::money_amount::{money, money_from_value, money_to_value, Money}; use super::payroll_redundancy_pay_data::{WEEK_PAY_CAPS, WEEK_PAY_CAPS_HISTORY}; 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)), ]) }