use std::ops::AddAssign;
use crate::params::TaxParams;
use crate::plan::{Dollars, FilingStatus};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Earnings {
pub covered: Dollars,
pub medicare: Dollars,
pub self_employed: Dollars,
}
impl Earnings {
pub fn deduct(&mut self, amount: Dollars) {
self.covered = (self.covered - amount).max(0);
self.medicare = (self.medicare - amount).max(0);
}
}
impl AddAssign for Earnings {
fn add_assign(&mut self, other: Self) {
self.covered += other.covered;
self.medicare += other.medicare;
self.self_employed += other.self_employed;
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Payroll {
pub total: Dollars,
pub deduction: Dollars,
}
#[must_use]
pub fn payroll(params: &TaxParams, status: FilingStatus, earnings: &[Earnings]) -> Payroll {
let rates = ¶ms.payroll;
let wage_base = params
.social_security
.benefit
.as_ref()
.map_or(Dollars::MAX, |benefit| benefit.wage_base(params.year));
let mut employee = 0.0;
let mut self_employment = 0.0;
let mut medicare_base = 0;
for person in earnings {
let covered = person.covered.min(wage_base);
let net = person.self_employed;
let net_covered = net.min(wage_base - covered);
employee += rates.oasdi * covered as f64 + rates.medicare * person.medicare as f64;
self_employment += 2.0 * (rates.oasdi * net_covered as f64 + rates.medicare * net as f64);
medicare_base += person.medicare + net;
}
let over = (medicare_base - rates.additional_medicare_over.get(status)).max(0);
let additional = rates.additional_medicare * over as f64;
Payroll {
total: (employee + self_employment + additional).round() as Dollars,
deduction: (self_employment / 2.0).round() as Dollars,
}
}