This commit is contained in:
gsinghpal
2026-04-07 20:49:21 -04:00
parent 3cc93b8783
commit 4fde4c7bd1
25 changed files with 1253 additions and 900 deletions

View File

@@ -291,7 +291,8 @@ class PayrollEntry(models.TransientModel):
@api.depends('gross_pay', 'employee_id')
def _compute_taxes(self):
"""Calculate employee tax deductions."""
"""Calculate employee tax deductions (preview estimate for bi-weekly)."""
PAY_PERIODS = 26
for entry in self:
if entry.gross_pay <= 0:
entry.income_tax = 0
@@ -300,26 +301,81 @@ class PayrollEntry(models.TransientModel):
entry.cpp2 = 0
entry.total_employee_tax = 0
continue
# Get tax rates from parameters or use defaults
# These are simplified calculations - actual payroll uses full tax rules
gross = entry.gross_pay
# Simplified tax calculations (bi-weekly)
# Income tax: ~15-20% average for Canadian employees
entry.income_tax = round(gross * 0.128, 2) # Approximate federal + provincial
# EI: 1.64% of gross (2025 rate) up to maximum
entry.employment_insurance = round(min(gross * 0.0164, 1049.12 / 26), 2)
# CPP: 5.95% of pensionable earnings above basic exemption (2025)
cpp_exempt = 3500 / 26 # Annual exemption / 26 pay periods
pensionable = max(0, gross - cpp_exempt)
entry.cpp = round(min(pensionable * 0.0595, 4034.10 / 26), 2)
# CPP2: 4% on earnings above first ceiling (2025)
entry.cpp2 = 0 # Only applies if earnings exceed $71,300/year
annual = gross * PAY_PERIODS
emp = entry.employee_id
is_cpp_exempt = getattr(emp, 'exempt_cpp', False)
is_ei_exempt = getattr(emp, 'exempt_ei', False)
is_fed_exempt = getattr(emp, 'exempt_federal_tax', False)
# Federal tax estimate using 2026 brackets + BPA phase-out
fed_brackets = [
(58523, 0.14), (117045, 0.205), (181440, 0.26),
(258482, 0.29), (float('inf'), 0.33),
]
bpa_max, bpa_min = 16452, 14829
if annual <= 181440:
fed_bpa = bpa_max
elif annual >= 258482:
fed_bpa = bpa_min
else:
fed_bpa = bpa_max - (bpa_max - bpa_min) * (annual - 181440) / (258482 - 181440)
fed_tax = 0
prev = 0
for threshold, rate in fed_brackets:
taxable_in = min(annual, threshold) - prev
if taxable_in > 0:
fed_tax += taxable_in * rate
prev = threshold
if annual <= threshold:
break
fed_tax = max(fed_tax - fed_bpa * 0.14 - 1433 * 0.14, 0)
# Ontario provincial estimate (default province)
on_brackets = [
(53891, 0.0505), (107785, 0.0915), (150000, 0.1116),
(220000, 0.1216), (float('inf'), 0.1316),
]
prov_tax = 0
prev = 0
for threshold, rate in on_brackets:
taxable_in = min(annual, threshold) - prev
if taxable_in > 0:
prov_tax += taxable_in * rate
prev = threshold
if annual <= threshold:
break
prov_tax = max(prov_tax - 12989 * 0.0505, 0)
entry.income_tax = 0 if is_fed_exempt else round((fed_tax + prov_tax) / PAY_PERIODS, 2)
if is_ei_exempt:
entry.employment_insurance = 0
else:
period_max_insurable = 68900 / PAY_PERIODS
insurable = min(gross, period_max_insurable)
entry.employment_insurance = round(min(insurable * 0.0163, 1123.07 / PAY_PERIODS), 2)
if is_cpp_exempt:
entry.cpp = 0
entry.cpp2 = 0
else:
period_ympe = 74600 / PAY_PERIODS
cpp_exempt_amt = 3500 / PAY_PERIODS
pensionable = min(gross, period_ympe)
pensionable = max(0, pensionable - cpp_exempt_amt)
entry.cpp = round(min(pensionable * 0.0595, 4230.45 / PAY_PERIODS), 2)
if not is_cpp_exempt and gross > 74600 / PAY_PERIODS:
period_ceiling = 85000 / PAY_PERIODS
cpp2_base = min(gross, period_ceiling) - period_ympe
entry.cpp2 = round(min(cpp2_base * 0.04, 416.00 / PAY_PERIODS), 2)
else:
entry.cpp2 = 0
entry.total_employee_tax = entry.income_tax + entry.employment_insurance + entry.cpp + entry.cpp2
@api.depends('employment_insurance', 'cpp', 'cpp2')