diff --git a/l10n_us_hr_payroll/data/state/nd_north_dakota.xml b/l10n_us_hr_payroll/data/state/nd_north_dakota.xml index 6ab1c168..d778c3aa 100644 --- a/l10n_us_hr_payroll/data/state/nd_north_dakota.xml +++ b/l10n_us_hr_payroll/data/state/nd_north_dakota.xml @@ -27,7 +27,7 @@ - + US ND North Dakota SIT Tax Rate us_nd_sit_tax_rate @@ -36,36 +36,206 @@ { - 'single': ( - ( 6200, 0.00, 0.00), - ( 46325, 0.00, 1.10), - ( 103350, 441.38, 2.04), - ( 208850, 1604.69, 2.27), - ( 446800, 3999.54, 2.64), - ( 'inf',10281.42, 2.90), - ), - 'married': ( - ( 12400, 0.00, 0.00), - ( 45925, 0.00, 1.10), - ( 93375, 368.78, 2.04), - ( 135750, 1336.76, 2.27), - ( 232700, 2298.67, 2.64), - ( 'inf', 4858.15, 2.90), - ), - 'head_household': ( - ( 9325, 0.00, 0.00), - ( 63075, 0.00, 1.10), - ( 148125, 591.25, 2.04), - ( 234025, 2326.27, 2.27), - ( 449925, 4276.20, 2.64), - ( 'inf', 9975.96, 2.90), - ), + 'single': { + 'weekly': ( + ( 119, 0.00, 0.00), + ( 891, 0.00, 1.10), + ( 1988, 8.49, 2.04), + ( 4016, 30.87, 2.27), + ( 8592, 76.91, 2.64), + ('inf', 197.71, 2.90), + ), + 'bi-weekly': ( + ( 238, 0.00, 0.00), + ( 1782, 0.00, 1.10), + ( 3975, 16.98, 2.04), + ( 8033, 61.72, 2.27), + ( 17185, 153.84, 2.64), + ( 'inf', 395.45, 2.90), + ), + 'semi-monthly': ( + ( 258, 0.00, 0.00), + ( 1930, 0.00, 1.10), + ( 4306, 18.39, 2.04), + ( 8702, 66.86, 2.27), + ( 18617, 166.65, 2.64), + ( 'inf', 428.41, 2.90), + ), + 'monthly': ( + ( 517, 0.00, 0.00), + ( 3860, 0.00, 1.10), + ( 8613, 36.77, 2.04), + ( 17404, 133.73, 2.27), + ( 37233, 333.29, 2.64), + ( 'inf', 856.78, 2.90), + ), + 'quarterly': ( + ( 1550, 0.00, 0.00), + ( 11581, 0.00, 1.10), + ( 25838, 110.34, 2.04), + ( 52213, 401.18, 2.27), + ( 111700, 999.90, 2.64), + ( 'inf', 2570.35, 2.90), + ), + 'semi-annual': ( + ( 3100, 0.00, 0.00), + ( 23163, 0.00, 1.10), + ( 51675, 220.69, 2.04), + ( 104425, 802.34, 2.27), + ( 223400, 1999.76, 2.64), + ( 'inf', 5140.70, 2.90), + ), + 'annual': ( + ( 6200, 0.00, 0.00), + ( 46325, 0.00, 1.10), + ( 103350, 441.38, 2.04), + ( 208850, 1604.69, 2.27), + ( 446800, 3999.54, 2.64), + ( 'inf', 10281.42, 2.90), + ), + }, + 'married': { + 'weekly': ( + ( 238, 0.00, 0.00), + ( 883, 0.00, 1.10), + ( 1796, 7.10, 2.04), + ( 2611, 25.72, 2.27), + ( 4475, 44.22, 2.64), + ('inf', 93.43, 2.90), + ), + 'bi-weekly': ( + ( 477, 0.00, 0.00), + ( 1766, 0.00, 1.10), + ( 3591, 14.18, 2.04), + ( 5221, 51.41, 2.27), + ( 8950, 88.41, 2.64), + ( 'inf', 186.86, 2.90), + ), + 'semi-monthly': ( + ( 517, 0.00, 0.00), + ( 1914, 0.00, 1.10), + ( 3891, 15.37, 2.04), + ( 5656, 55.70, 2.27), + ( 9696, 95.76, 2.64), + ( 'inf', 202.42, 2.90), + ), + 'monthly': ( + ( 1033, 0.00, 0.00), + ( 3827, 0.00, 1.10), + ( 7781, 30.73, 2.04), + ( 11313, 111.40, 2.27), + ( 19392, 191.57, 2.64), + ( 'inf', 404.86, 2.90), + ), + 'quarterly': ( + ( 3100, 0.00, 0.00), + ( 11481, 0.00, 1.10), + ( 23344, 92.19, 2.04), + ( 33938, 334.20, 2.27), + ( 58175, 574.68, 2.64), + ( 'inf', 1214.54, 2.90), + ), + 'semi-annual': ( + ( 6200, 0.00, 0.00), + ( 22963, 0.00, 1.10), + ( 46688, 184.39, 2.04), + ( 67875, 668.38, 2.27), + ( 116350, 1149.33, 2.64), + ( 'inf', 2429.07, 2.90), + ), + 'annual': ( + ( 12400, 0.00, 0.00), + ( 45925, 0.00, 1.10), + ( 93375, 368.78, 2.04), + ( 135750, 1336.76, 2.27), + ( 232700, 2298.67, 2.64), + ( 'inf', 4858.15, 2.90), + ), + }, + 'head_household':{ + 'weekly': ( + ( 119, 0.00, 0.00), + ( 891, 0.00, 1.10), + ( 1988, 8.49, 2.04), + ( 4016, 30.87, 2.27), + ( 8592, 76.91, 2.64), + ('inf', 197.71, 2.90), + ), + 'bi-weekly': ( + ( 238, 0.00, 0.00), + ( 1782, 0.00, 1.10), + ( 3975, 16.98, 2.04), + ( 8033, 61.72, 2.27), + ( 17185, 153.84, 2.64), + ( 'inf', 395.45, 2.90), + ), + 'semi-monthly': ( + ( 258, 0.00, 0.00), + ( 1930, 0.00, 1.10), + ( 4306, 18.39, 2.04), + ( 8702, 66.86, 2.27), + ( 18617, 166.65, 2.64), + ( 'inf', 428.41, 2.90), + ), + 'monthly': ( + ( 517, 0.00, 0.00), + ( 3860, 0.00, 1.10), + ( 8613, 36.77, 2.04), + ( 17404, 133.73, 2.27), + ( 37233, 333.29, 2.64), + ( 'inf', 856.78, 2.90), + ), + 'quarterly': ( + ( 1550, 0.00, 0.00), + ( 11581, 0.00, 1.10), + ( 25838, 110.34, 2.04), + ( 52213, 401.18, 2.27), + ( 111700, 999.90, 2.64), + ( 'inf', 2570.35, 2.90), + ), + 'semi-annual': ( + ( 3100, 0.00, 0.00), + ( 23163, 0.00, 1.10), + ( 51675, 220.69, 2.04), + ( 104425, 802.34, 2.27), + ( 223400, 1999.76, 2.64), + ( 'inf', 5140.70, 2.90), + ), + 'annual': ( + ( 6200, 0.00, 0.00), + ( 46325, 0.00, 1.10), + ( 103350, 441.38, 2.04), + ( 208850, 1604.69, 2.27), + ( 446800, 3999.54, 2.64), + ( 'inf', 10281.42, 2.90), + ), + }, } + + US ND North Dakota Allowances Rate + us_nd_sit_allowances_rate + + + + + { + 'weekly' : 83.00, + 'bi-weekly' : 165.00, + 'semi-monthly': 179.00, + 'monthly' : 358.00, + 'quarterly' : 1075.00, + 'semi-annual': 2150.00, + 'annually': 4300.00, + } + + + + diff --git a/l10n_us_hr_payroll/models/state/nd_north_dakota.py b/l10n_us_hr_payroll/models/state/nd_north_dakota.py index 81136d6e..1ef4ecbd 100644 --- a/l10n_us_hr_payroll/models/state/nd_north_dakota.py +++ b/l10n_us_hr_payroll/models/state/nd_north_dakota.py @@ -23,11 +23,13 @@ def nd_north_dakota_state_income_withholding(payslip, categories, worked_days, i if not filing_status: return 0.0, 0.0 - pay_periods = payslip.dict.get_pay_periods_in_year() + schedule_pay = payslip.contract_id.schedule_pay additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding') - tax_rate = payslip.rule_parameter('us_nd_sit_tax_rate')[filing_status] + allowance = payslip.contract_id.us_payroll_config_value('nd_w4_sit_allowances') + allowance_rate = payslip.rule_parameter('us_nd_sit_allowances_rate')[schedule_pay] + tax_rate = payslip.rule_parameter('us_nd_sit_tax_rate')[filing_status].get(schedule_pay) - taxable_income = wage * pay_periods + taxable_income = wage - (allowance * allowance_rate) withholding = 0.0 last = 0.0 for row in tax_rate: @@ -37,7 +39,7 @@ def nd_north_dakota_state_income_withholding(payslip, categories, worked_days, i break last = amt - withholding = round(withholding / pay_periods) + withholding = round(withholding) withholding = max(withholding, 0.0) withholding += additional return wage, -((withholding / wage) * 100.0) diff --git a/l10n_us_hr_payroll/models/us_payroll_config.py b/l10n_us_hr_payroll/models/us_payroll_config.py index baceae6c..37fe2602 100644 --- a/l10n_us_hr_payroll/models/us_payroll_config.py +++ b/l10n_us_hr_payroll/models/us_payroll_config.py @@ -197,10 +197,12 @@ class HRContractUSPayrollConfig(models.Model): nc_nc4_sit_allowances = fields.Integer(string='North Carolina NC-4 Allowances', help='NC-4 1.') nd_w4_sit_filing_status = fields.Selection([ + ('', 'Exempt'), ('single', 'Single'), ('married', 'Married'), ('head_household', 'Head of Household') ], string='North Dakota ND W-4 Filing Status', help='ND W-4') + nd_w4_sit_allowances = fields.Integer(string='North Dakota ND W-4') ne_w4n_sit_filing_status = fields.Selection([ ('single', 'Single'), diff --git a/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py index 3bde5bee..903cf816 100644 --- a/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py +++ b/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py @@ -12,24 +12,26 @@ class TestUsNDPayslip(TestUsPayslip): ND_UNEMP = 1.02 # Calculation based on this file page.47 https://www.nd.gov/tax/data/upfiles/media/rates-and-instructions.pdf?20200110115917 - def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding): + def _test_sit(self, wage, filing_status, additional_withholding, allowances, schedule_pay, date_start, expected_withholding): employee = self._createEmployee() contract = self._createContract(employee, wage=wage, state_id=self.get_us_state('ND'), nd_w4_sit_filing_status=filing_status, state_income_tax_additional_withholding=additional_withholding, + nd_w4_sit_allowances=allowances, 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) + self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding) def test_2020_taxes_example(self): self._test_er_suta('ND', self.ND_UNEMP, date(2020, 1, 1), wage_base=self.ND_UNEMP_MAX_WAGE) - self._test_sit(700.0, 'single', 0.0, 'weekly', date(2020, 1, 1), 6.0) - self._test_sit(2500.0, 'married', 0.0, 'bi-weekly', date(2020, 1, 1), 29.0) - self._test_sit(25000.0, 'head_household', 0.0, 'monthly', date(2020, 1, 1), 501.0) - self._test_sit(25000.0, 'head_household', 10.0, 'monthly', date(2020, 1, 1), 511.0) + self._test_sit(700.0, 'single', 0.0, 0.0, 'weekly', date(2020, 1, 1), 6.0) + self._test_sit(5000.0, 'married', 0.0, 2.0, 'bi-weekly', date(2020, 1, 1), 76.0) + self._test_sit(25000.0, 'head_household', 0.0, 0.0, 'monthly', date(2020, 1, 1), 534.0) + self._test_sit(25000.0, 'head_household', 10.0, 2.0, 'monthly', date(2020, 1, 1), 525.0) + self._test_sit(3000.0, '', 10.0, 2.0, 'monthly', date(2020, 1, 1), 0.0)