mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
IMP l10n_us_hr_payroll Port l10n_us_ct_hr_payroll CT Connecticut including migration
This commit is contained in:
@@ -32,6 +32,7 @@ USA Payroll Rules.
|
||||
'data/state/al_alabama.xml',
|
||||
'data/state/ar_arkansas.xml',
|
||||
'data/state/az_arizona.xml',
|
||||
'data/state/ct_connecticut.xml',
|
||||
'data/state/fl_florida.xml',
|
||||
'data/state/ga_georgia.xml',
|
||||
'data/state/il_illinois.xml',
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
ref('hr_payroll_rule_er_us_az_suta'),
|
||||
ref('hr_payroll_rule_ee_us_az_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_ct_suta'),
|
||||
ref('hr_payroll_rule_ee_us_ct_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_fl_suta'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_ga_suta'),
|
||||
|
||||
1212
l10n_us_hr_payroll/data/state/ct_connecticut.xml
Normal file
1212
l10n_us_hr_payroll/data/state/ct_connecticut.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,9 @@ FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020 = {
|
||||
'az_a4_withholding_percentage': 'az_a4_sit_withholding_percentage',
|
||||
'az_a4_additional_withholding': 'state_income_tax_additional_withholding',
|
||||
|
||||
'ct_w4na_code': 'ct_w4na_sit_code',
|
||||
'ct_w4na_wh_amount': 'state_income_tax_additional_withholding',
|
||||
|
||||
'ga_g4_filing_status': 'ga_g4_sit_filing_status',
|
||||
'ga_g4_dependent_allowances': 'ga_g4_sit_dependent_allowances',
|
||||
'ga_g4_additional_allowances': 'ga_g4_sit_additional_allowances',
|
||||
@@ -104,6 +107,11 @@ XMLIDS_TO_REMOVE_2020 = [
|
||||
'l10n_us_az_hr_payroll.hr_payroll_az_income_withhold',
|
||||
'l10n_us_az_hr_payroll.hr_payroll_rules_az_unemp_wages',
|
||||
|
||||
'l10n_us_ct_hr_payroll.hr_payroll_ct_unemp_wages',
|
||||
'l10n_us_ct_hr_payroll.hr_payroll_ct_unemp',
|
||||
'l10n_us_ct_hr_payroll.hr_payroll_ct_income_withhold',
|
||||
'l10n_us_ct_hr_payroll.hr_payroll_rules_ct_unemp_wages',
|
||||
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_fl_unemp_wages',
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_fl_unemp',
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_rules_fl_unemp_wages_2018',
|
||||
@@ -249,6 +257,13 @@ XMLIDS_TO_RENAME_2020 = {
|
||||
'l10n_us_az_hr_payroll.hr_payroll_az_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_az_suta',
|
||||
'l10n_us_az_hr_payroll.hr_payroll_az_income_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_az_sit',
|
||||
|
||||
'l10n_us_ct_hr_payroll.res_partner_ct_dol_unemp': 'l10n_us_hr_payroll.res_partner_us_ct_dor',
|
||||
'l10n_us_ct_hr_payroll.res_partner_ct_drs_withhold': 'l10n_us_hr_payroll.res_partner_us_ct_dor_sit',
|
||||
'l10n_us_ct_hr_payroll.contrib_register_ct_dol_unemp': 'l10n_us_hr_payroll.contrib_register_us_ct_dor',
|
||||
'l10n_us_ct_hr_payroll.contrib_register_ct_drs_withhold': 'l10n_us_hr_payroll.contrib_register_us_ct_dor_sit',
|
||||
'l10n_us_ct_hr_payroll.hr_payroll_rules_ct_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_ct_suta',
|
||||
'l10n_us_ct_hr_payroll.hr_payroll_rules_ct_inc_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_ct_sit',
|
||||
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_rules_fl_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_fl_suta',
|
||||
'l10n_us_fl_hr_payroll.res_partner_fldor': 'l10n_us_hr_payroll.res_partner_us_fl_dor',
|
||||
'l10n_us_fl_hr_payroll.contrib_register_fldor': 'l10n_us_hr_payroll.contrib_register_us_fl_dor',
|
||||
|
||||
@@ -17,6 +17,7 @@ from .state.general import general_state_unemployment, \
|
||||
from .state.al_alabama import al_alabama_state_income_withholding
|
||||
from .state.ar_arkansas import ar_arkansas_state_income_withholding
|
||||
from .state.az_arizona import az_arizona_state_income_withholding
|
||||
from .state.ct_connecticut import ct_connecticut_state_income_withholding
|
||||
from .state.ga_georgia import ga_georgia_state_income_withholding
|
||||
from .state.il_illinois import il_illinois_state_income_withholding
|
||||
from .state.mi_michigan import mi_michigan_state_income_withholding
|
||||
@@ -65,6 +66,7 @@ class HRPayslip(models.Model):
|
||||
'al_alabama_state_income_withholding': al_alabama_state_income_withholding,
|
||||
'ar_arkansas_state_income_withholding': ar_arkansas_state_income_withholding,
|
||||
'az_arizona_state_income_withholding': az_arizona_state_income_withholding,
|
||||
'ct_connecticut_state_income_withholding': ct_connecticut_state_income_withholding,
|
||||
'ga_georgia_state_income_withholding': ga_georgia_state_income_withholding,
|
||||
'il_illinois_state_income_withholding': il_illinois_state_income_withholding,
|
||||
'mi_michigan_state_income_withholding': mi_michigan_state_income_withholding,
|
||||
|
||||
76
l10n_us_hr_payroll/models/state/ct_connecticut.py
Normal file
76
l10n_us_hr_payroll/models/state/ct_connecticut.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .general import _state_applies, sit_wage
|
||||
|
||||
|
||||
def ct_connecticut_state_income_withholding(payslip, categories, worked_days, inputs):
|
||||
"""
|
||||
Returns SIT eligible wage and rate.
|
||||
|
||||
:return: result, result_rate (wage, percent)
|
||||
"""
|
||||
state_code = 'CT'
|
||||
if not _state_applies(payslip, state_code):
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Wage
|
||||
wage = sit_wage(payslip, categories)
|
||||
if not wage:
|
||||
return 0.0, 0.0
|
||||
|
||||
pay_periods = payslip.dict.get_pay_periods_in_year()
|
||||
additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
|
||||
withholding_code = payslip.dict.contract_id.us_payroll_config_value('ct_w4na_sit_code')
|
||||
exemption_table = payslip.dict.rule_parameter('us_ct_sit_personal_exemption_rate').get(withholding_code, [('inf', 0.0)])
|
||||
initial_tax_tbl = payslip.dict.rule_parameter('us_ct_sit_initial_tax_rate').get(withholding_code, [('inf', 0.0, 0.0)])
|
||||
tax_table = payslip.dict.rule_parameter('us_ct_sit_tax_rate').get(withholding_code, [('inf', 0.0)])
|
||||
recapture_table = payslip.dict.rule_parameter('us_ct_sit_recapture_rate').get(withholding_code, [('inf', 0.0)])
|
||||
decimal_table = payslip.dict.rule_parameter('us_ct_sit_decimal_rate').get(withholding_code, [('inf', 0.0)])
|
||||
|
||||
annual_wages = wage * pay_periods
|
||||
personal_exemption = 0.0
|
||||
for bracket in exemption_table:
|
||||
if annual_wages <= float(bracket[0]):
|
||||
personal_exemption = bracket[1]
|
||||
break
|
||||
|
||||
withholding = 0.0
|
||||
taxable_income = annual_wages - personal_exemption
|
||||
if taxable_income < 0.0:
|
||||
taxable_income = 0.0
|
||||
|
||||
if taxable_income:
|
||||
initial_tax = 0.0
|
||||
last = 0.0
|
||||
for bracket in initial_tax_tbl:
|
||||
if taxable_income <= float(bracket[0]):
|
||||
initial_tax = bracket[1] + ((bracket[2] / 100.0) * (taxable_income - last))
|
||||
break
|
||||
last = bracket[0]
|
||||
|
||||
tax_add_back = 0.0
|
||||
for bracket in tax_table:
|
||||
if annual_wages <= float(bracket[0]):
|
||||
tax_add_back = bracket[1]
|
||||
break
|
||||
|
||||
recapture_amount = 0.0
|
||||
for bracket in recapture_table:
|
||||
if annual_wages <= float(bracket[0]):
|
||||
recapture_amount = bracket[1]
|
||||
break
|
||||
|
||||
withholding = initial_tax + tax_add_back + recapture_amount
|
||||
decimal_amount = 1.0
|
||||
for bracket in decimal_table:
|
||||
if annual_wages <= float(bracket[0]):
|
||||
decimal_amount= bracket[1]
|
||||
break
|
||||
|
||||
withholding = withholding * (1.00 - decimal_amount)
|
||||
if withholding < 0.0:
|
||||
withholding = 0.0
|
||||
withholding /= pay_periods
|
||||
|
||||
withholding += additional
|
||||
return wage, -((withholding / wage) * 100.0)
|
||||
@@ -66,6 +66,14 @@ class HRContractUSPayrollConfig(models.Model):
|
||||
string='Arizona A-4 Withholding Percentage',
|
||||
help='A-4 1. (0.8 or 1.3 or 1.8 or 2.7 or 3.6 or 4.2 or 5.1 or 0 for exempt.')
|
||||
|
||||
ct_w4na_sit_code = fields.Selection([
|
||||
('a', 'A'),
|
||||
('b', 'B'),
|
||||
('c', 'C'),
|
||||
('d', 'D'),
|
||||
('f', 'F'),
|
||||
], string='Connecticut CT-W4 Withholding Code', help='CT-W4 1.')
|
||||
|
||||
ga_g4_sit_filing_status = fields.Selection([
|
||||
('exempt', 'Exempt'),
|
||||
('single', 'Single'),
|
||||
|
||||
@@ -16,6 +16,9 @@ from . import test_us_ar_arkansas_payslip_2020
|
||||
from . import test_us_az_arizona_payslip_2019
|
||||
from . import test_us_az_arizona_payslip_2020
|
||||
|
||||
from . import test_us_ct_connecticut_payslip_2019
|
||||
from . import test_us_ct_connecticut_payslip_2020
|
||||
|
||||
from . import test_us_fl_florida_payslip_2019
|
||||
from . import test_us_fl_florida_payslip_2020
|
||||
|
||||
|
||||
121
l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
Normal file
121
l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .common import TestUsPayslip, process_payslip
|
||||
|
||||
|
||||
class TestUsCTPayslip(TestUsPayslip):
|
||||
# TAXES AND RATES
|
||||
CT_UNEMP_MAX_WAGE = 15000.00
|
||||
CT_UNEMP = -(3.40 / 100.0)
|
||||
|
||||
def test_taxes_weekly_with_additional_wh(self):
|
||||
|
||||
# Tax tables can be found here:
|
||||
# https://portal.ct.gov/-/media/DRS/Publications/pubsip/2019/IP-2019(1).pdf?la=en
|
||||
# Step 1 - Wages per period -> 10000.00
|
||||
salary = 10000.00
|
||||
# Step 2 and 3 - Annual wages -> 10000.00 * 52.0 -> 520000.0
|
||||
schedule_pay = 'weekly'
|
||||
# Step 4 Employee Withholding Code -> A
|
||||
wh_code = 'a'
|
||||
# Step 5 - Use annual wages and withholding code with table for exemption amount.
|
||||
# exemption_amt = 0 since highest bracket.
|
||||
# Step 6 - Subtract 5 from 3 for taxable income.
|
||||
# taxable income = 520000.00 since we do not have an exemption.
|
||||
# Step 7 - Determine initial amount from table
|
||||
# initial = 31550 + ((6.99 / 100) * (520000.00 - 500000.00))
|
||||
# 32948.0
|
||||
# Step 8 - Determine the tax rate phase out add back from table.
|
||||
# phase_out = 200
|
||||
# Step 9 - Determine the recapture amount from table.
|
||||
# Close to top, but not top. -> 2900
|
||||
# Step 10 - Add Step 7, 8, 9
|
||||
# 32948.0 + 200 + 2900.00 - > 36048.0
|
||||
# Step 11 - Determine decimal amount from personal tax credits.
|
||||
# We get no tax credit.
|
||||
# Step 12 - Multiple Step 10 by 1.00 - Step 11
|
||||
# 36048.0 * 1.00 = 36048.0
|
||||
# Step 13 - Divide by the number of pay periods.
|
||||
# 36048.0 / 52.0 = 693.23
|
||||
# Step 14 & 15 & 16- Add / Subtract the additional or under withholding amount. Then Add this to the amount
|
||||
# for withholding per period.
|
||||
additional_wh = 12.50
|
||||
# 693.23 + 12.50 ->
|
||||
wh = -705.73
|
||||
|
||||
employee = self._createEmployee()
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
state_id=self.get_us_state('CT'),
|
||||
ct_w4na_sit_code=wh_code,
|
||||
state_income_tax_additional_withholding=additional_wh,
|
||||
schedule_pay=schedule_pay)
|
||||
|
||||
self._log('2019 Connecticut tax first payslip weekly:')
|
||||
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
|
||||
payslip.compute_sheet()
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.CT_UNEMP)
|
||||
self.assertPayrollEqual(cats['EE_US_SIT'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
remaining_CT_UNEMP_wages = 5000.00 # We already reached the maximum wage for unemployment insurance.
|
||||
self._log('2019 Connecticut tax second payslip weekly:')
|
||||
payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
|
||||
payslip.compute_sheet()
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_CT_UNEMP_wages * self.CT_UNEMP)
|
||||
|
||||
def test_taxes_weekly_with_different_code(self):
|
||||
|
||||
# Tax tables can be found here:
|
||||
# https://portal.ct.gov/-/media/DRS/Publications/pubsip/2019/IP-2019(1).pdf?la=en
|
||||
# Step 1 - Wages per period -> 15000.00
|
||||
salary = 15000.00
|
||||
# Step 2 and 3 - Annual wages -> 15000.00 * 12.0 -> 180000.0
|
||||
schedule_pay = 'monthly'
|
||||
# Step 4 Employee Withholding Code -> B
|
||||
wh_code = 'b'
|
||||
# Step 5 - Use annual wages and withholding code with table for exemption amount.
|
||||
# exemption_amt = 0 since highest bracket.
|
||||
# Step 6 - Subtract 5 from 3 for taxable income.
|
||||
# taxable income = 180000.0 since we do not have an exemption.
|
||||
# Step 7 - Determine initial amount from table
|
||||
# initial = 8080 + ((6.00 / 100) * (180000.0 - 160000))
|
||||
# 9280.0
|
||||
# Step 8 - Determine the tax rate phase out add back from table.
|
||||
# phase_out = 320
|
||||
# Step 9 - Determine the recapture amount from table.
|
||||
# Bottom -> 0
|
||||
# Step 10 - Add Step 7, 8, 9
|
||||
# 9280.0 + 320 + 0 - > 9600.0
|
||||
# Step 11 - Determine decimal amount from personal tax credits.
|
||||
# We get no tax credit.
|
||||
# Step 12 - Multiple Step 10 by 1.00 - Step 11
|
||||
# 9600.0 * 1.00 = 9600.0
|
||||
# Step 13 - Divide by the number of pay periods.
|
||||
# 9600.0 / 12.0 = 800.0
|
||||
# Step 14 & 15 & 16- Add / Subtract the additional or under withholding amount. Then Add this to the amount
|
||||
# for withholding per period.
|
||||
additional_wh = 15.00
|
||||
# 800.0 + 15.00 ->
|
||||
wh = -815.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
state_id=self.get_us_state('CT'),
|
||||
ct_w4na_sit_code=wh_code,
|
||||
state_income_tax_additional_withholding=additional_wh,
|
||||
schedule_pay=schedule_pay)
|
||||
|
||||
self._log('2019 Connecticut tax first payslip monthly:')
|
||||
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
|
||||
payslip.compute_sheet()
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['ER_US_SUTA'], self.CT_UNEMP_MAX_WAGE * self.CT_UNEMP)
|
||||
self.assertPayrollEqual(cats['EE_US_SIT'], wh)
|
||||
@@ -0,0 +1,34 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, timedelta
|
||||
from .common import TestUsPayslip
|
||||
|
||||
|
||||
class TestUsCTPayslip(TestUsPayslip):
|
||||
# Taxes and Rates
|
||||
CT_UNEMP_MAX_WAGE = 15000.0
|
||||
CT_UNEMP = 3.2
|
||||
|
||||
def _test_sit(self, wage, withholding_code, additional_withholding, schedule_pay, date_start, expected_withholding):
|
||||
|
||||
employee = self._createEmployee()
|
||||
contract = self._createContract(employee,
|
||||
wage=wage,
|
||||
state_id=self.get_us_state('CT'),
|
||||
ct_w4na_sit_code=withholding_code,
|
||||
state_income_tax_additional_withholding=additional_withholding,
|
||||
schedule_pay=schedule_pay)
|
||||
payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
|
||||
payslip.compute_sheet()
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self._log('Computed period tax: ' + str(expected_withholding))
|
||||
self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
|
||||
|
||||
def test_2020_taxes_example(self):
|
||||
self._test_er_suta('CT', self.CT_UNEMP, date(2020, 1, 1), wage_base=self.CT_UNEMP_MAX_WAGE)
|
||||
self._test_sit(10000.0, 'a', 0.0, 'weekly', date(2020, 1, 1), 693.23)
|
||||
self._test_sit(12000.0, 'b', 15.0, 'bi-weekly', date(2020, 1, 1), 688.85)
|
||||
self._test_sit(5000.0, 'f', 15.0, 'monthly', date(2020, 1, 1), 230.25)
|
||||
self._test_sit(15000.0, 'c', 0.0, 'monthly', date(2020, 1, 1), 783.33)
|
||||
self._test_sit(18000.0, 'b', 0.0, 'weekly', date(2020, 1, 1), 1254.35)
|
||||
Reference in New Issue
Block a user