diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py index 8f7b1131..f023e2df 100644 --- a/l10n_us_hr_payroll/__manifest__.py +++ b/l10n_us_hr_payroll/__manifest__.py @@ -33,6 +33,7 @@ United States of America - Payroll Rules. 'data/state/ct_connecticut.xml', 'data/state/fl_florida.xml', 'data/state/ga_georgia.xml', + 'data/state/hi_hawaii.xml', 'data/state/id_idaho.xml', 'data/state/il_illinois.xml', 'data/state/mi_michigan.xml', diff --git a/l10n_us_hr_payroll/data/state/hi_hawaii.xml b/l10n_us_hr_payroll/data/state/hi_hawaii.xml new file mode 100644 index 00000000..862ca102 --- /dev/null +++ b/l10n_us_hr_payroll/data/state/hi_hawaii.xml @@ -0,0 +1,125 @@ + + + + + US HI Hawaii SUTA Wage Base + us_hi_suta_wage_base + + + + + 46800.0 + + + + + 48100.0 + + + + + + + + US HI Hawaii SUTA Rate + us_hi_suta_rate + + + + + 2.40 + + + + + 2.40 + + + + + + + US HI Hawaii SIT Tax Rate + us_hi_sit_tax_rate + + + + + { + 'single': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)), + 'married': ((4800, 0.00, 1.40), (9600, 67.00, 3.20), (19200, 221.00, 5.50), (28800, 749.00, 6.40), (38400, 1363.00, 6.80), (48000, 2016.00, 7.20), (72000, 2707.00, 7.60), ('inf', 4531.00, 7.90)), + 'head_of_household': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)), + } + + + + + { + 'single': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)), + 'married': ((4800, 0.00, 1.40), (9600, 67.00, 3.20), (19200, 221.00, 5.50), (28800, 749.00, 6.40), (38400, 1363.00, 6.80), (48000, 2016.00, 7.20), (72000, 2707.00, 7.60), ('inf', 4531.00, 7.90)), + 'head_of_household': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)), + } + + + + + + + US HI Hawaii Personal Exemption Rate + us_hi_sit_personal_exemption_rate + + + + + 1144 + + + + + 1144 + + + + + + + + US Hawaii - Department of Labor and Industrial Relations - Unemployment Tax + + + + US Hawaii - Department of Taxation - Income Tax + + + + + + + + + + ER: US HI Hawaii State Unemployment + ER_US_HI_SUTA + python + result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_hi_suta_wage_base', rate='us_hi_suta_rate', state_code='HI') + code + result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_hi_suta_wage_base', rate='us_hi_suta_rate', state_code='HI') + + + + + + + + + EE: US HI Hawaii State Income Tax Withholding + EE_US_HI_SIT + python + result, _ = hi_hawaii_state_income_withholding(payslip, categories, worked_days, inputs) + code + result, result_rate = hi_hawaii_state_income_withholding(payslip, categories, worked_days, inputs) + + + + + \ No newline at end of file diff --git a/l10n_us_hr_payroll/models/hr_payslip.py b/l10n_us_hr_payroll/models/hr_payslip.py index 32f24ce7..3aa4b4b2 100644 --- a/l10n_us_hr_payroll/models/hr_payslip.py +++ b/l10n_us_hr_payroll/models/hr_payslip.py @@ -18,6 +18,7 @@ from .state.az_arizona import az_arizona_state_income_withholding from .state.ca_california import ca_california_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.hi_hawaii import hi_hawaii_state_income_withholding from .state.id_idaho import id_idaho_state_income_withholding from .state.il_illinois import il_illinois_state_income_withholding from .state.mi_michigan import mi_michigan_state_income_withholding @@ -68,6 +69,7 @@ class HRPayslip(models.Model): 'ca_california_state_income_withholding': ca_california_state_income_withholding, 'ct_connecticut_state_income_withholding': ct_connecticut_state_income_withholding, 'ga_georgia_state_income_withholding': ga_georgia_state_income_withholding, + 'hi_hawaii_state_income_withholding': hi_hawaii_state_income_withholding, 'id_idaho_state_income_withholding': id_idaho_state_income_withholding, 'il_illinois_state_income_withholding': il_illinois_state_income_withholding, 'mi_michigan_state_income_withholding': mi_michigan_state_income_withholding, diff --git a/l10n_us_hr_payroll/models/state/hi_hawaii.py b/l10n_us_hr_payroll/models/state/hi_hawaii.py new file mode 100644 index 00000000..42c51e3e --- /dev/null +++ b/l10n_us_hr_payroll/models/state/hi_hawaii.py @@ -0,0 +1,43 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from .general import _state_applies, sit_wage + + +def hi_hawaii_state_income_withholding(payslip, categories, worked_days, inputs): + """ + Returns SIT eligible wage and rate. + + :return: result, result_rate (wage, percent) + """ + state_code = 'HI' + 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 + + filing_status = payslip.contract_id.us_payroll_config_value('hi_hw4_sit_filing_status') + if not filing_status: + return 0.0, 0.0 + + pay_periods = payslip.dict.get_pay_periods_in_year() + additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding') + allowances = payslip.contract_id.us_payroll_config_value('hi_hw4_sit_allowances') + tax_table = payslip.rule_parameter('us_hi_sit_tax_rate')[filing_status] + personal_exemption = payslip.rule_parameter('us_hi_sit_personal_exemption_rate') + + taxable_income = (wage * pay_periods) - (personal_exemption * allowances) + withholding = 0.0 + last = 0.0 + for row in tax_table: + if taxable_income <= float(row[0]): + withholding = row[1] + ((row[2] / 100.0) * (taxable_income - last)) + break + last = row[0] + + withholding = max(withholding, 0.0) + withholding = withholding / pay_periods + 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 c61def5e..de8e3daf 100644 --- a/l10n_us_hr_payroll/models/us_payroll_config.py +++ b/l10n_us_hr_payroll/models/us_payroll_config.py @@ -98,6 +98,14 @@ class HRContractUSPayrollConfig(models.Model): ga_g4_sit_additional_allowances = fields.Integer(string='Georgia G-4 Additional Allowances', help='G-4 5.') + hi_hw4_sit_filing_status = fields.Selection([ + ('', 'Exempt'), + ('single', 'Single'), + ('married', 'Married'), + ('head_of_household', 'Head of Household'), + ], string='Hawaii HW-4 Marital Status', help='HI HW-4 3.') + hi_hw4_sit_allowances = fields.Integer(string='Hawaii HW-4 Allowances', help='HI HW-4 4.') + id_w4_sit_filing_status = fields.Selection([ ('single', 'Single'), ('married', 'Married'), diff --git a/l10n_us_hr_payroll/tests/__init__.py b/l10n_us_hr_payroll/tests/__init__.py index 6bdb478d..d6e43e9e 100755 --- a/l10n_us_hr_payroll/tests/__init__.py +++ b/l10n_us_hr_payroll/tests/__init__.py @@ -28,6 +28,9 @@ from . import test_us_fl_florida_payslip_2020 from . import test_us_ga_georgia_payslip_2019 from . import test_us_ga_georgia_payslip_2020 +from . import test_us_hi_hawaii_payslip_2019 +from . import test_us_hi_hawaii_payslip_2020 + from . import test_us_id_idaho_payslip_2019 from . import test_us_id_idaho_payslip_2020 diff --git a/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2019.py new file mode 100644 index 00000000..13f1f2b5 --- /dev/null +++ b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2019.py @@ -0,0 +1,93 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from .common import TestUsPayslip, process_payslip + + +class TestUsHIPayslip(TestUsPayslip): + + # TAXES AND RATES + HI_UNEMP_MAX_WAGE = 46800.00 + HI_UNEMP = -(2.40 / 100.0) + + def test_taxes_single_weekly(self): + salary = 375.00 + schedule_pay = 'weekly' + filing_status = 'single' + allowances = 3 + wh_to_check = -15.3 + # Taxable income = (wage * payperiod ) - (allownaces * personal_exemption) + # taxable_income = (375 * 52) - (3 * 1144) = 16068 + # Last = row[0] = 692 + # withholding = row[1] + ((row[2] / 100.0) * (taxable_income - last)) + # withholding = 682 + ((6.80 / 100.0 ) * (16068 - 14400)) = 795.42 + # wh_to_check = 795.42/52 = 15.3 + employee = self._createEmployee() + contract = self._createContract(employee, + wage=salary, + state_id=self.get_us_state('HI'), + hi_hw4_sit_filing_status=filing_status, + state_income_tax_additional_withholding=0.0, + hi_hw4_sit_allowances=allowances, + schedule_pay=schedule_pay) + + self._log('2019 Hawaii tax first payslip single:') + 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.HI_UNEMP) + self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check) + + process_payslip(payslip) + + remaining_id_unemp_wages = self.HI_UNEMP_MAX_WAGE - salary if (self.HI_UNEMP_MAX_WAGE - 2*salary < salary) \ + else salary + + self._log('2019 Hawaii tax second payslip single:') + payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28') + payslip.compute_sheet() + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_id_unemp_wages * self.HI_UNEMP) + + def test_taxes_married_monthly(self): + salary = 5000.00 + schedule_pay = 'monthly' + filing_status = 'married' + allowances = 2 + wh_to_check = -287.1 + # Taxable income = (wage * payperiod ) - (allownaces * personal_exemption) + # taxable_income = (5000 * 12) - (2 * 1144) = 57712 + # Last = row[0] = 48000 + # withholding = row[1] + ((row[2] / 100.0) * (taxable_income - last)) + # withholding = 2707 + ((7.70 / 100.0 ) * (57712 - 48000)) = 3445.112 + # wh_to_check = 3445.112/52 = 287.092 + employee = self._createEmployee() + contract = self._createContract(employee, + wage=salary, + state_id=self.get_us_state('HI'), + hi_hw4_sit_filing_status=filing_status, + state_income_tax_additional_withholding=0.0, + hi_hw4_sit_allowances=allowances, + schedule_pay=schedule_pay) + + self._log('2019 Hawaii 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'], salary * self.HI_UNEMP) + self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check) + + process_payslip(payslip) + + remaining_id_unemp_wages = self.HI_UNEMP_MAX_WAGE - salary if (self.HI_UNEMP_MAX_WAGE - 2*salary < salary) \ + else salary + + self._log('2019 Hawaii tax second payslip monthly:') + payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28') + payslip.compute_sheet() + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_id_unemp_wages * self.HI_UNEMP) + diff --git a/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py new file mode 100755 index 00000000..9684c52d --- /dev/null +++ b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py @@ -0,0 +1,35 @@ +# 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 TestUsHIPayslip(TestUsPayslip): + ### + # 2020 Taxes and Rates + ### + HI_UNEMP_MAX_WAGE = 48100.00 + HI_UNEMP = 2.4 + + 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('HI'), + hi_hw4_sit_filing_status=filing_status, + state_income_tax_additional_withholding=additional_withholding, + hi_hw4_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) + + def test_2020_taxes_example(self): + self._test_er_suta('HI', self.HI_UNEMP, date(2020, 1, 1), wage_base=self.HI_UNEMP_MAX_WAGE) + self._test_sit(375.0, 'single', 0.0, 3.0, 'weekly', date(2020, 1, 1), 15.3) + self._test_sit(5000.0, 'married', 0.0, 2.0, 'monthly', date(2020, 1, 1), 287.1) + self._test_sit(5000.0, 'married', 10.0, 2.0, 'monthly', date(2020, 1, 1), 297.1) + self._test_sit(50000.0, 'head_of_household', 0.0, 3.0, 'weekly', date(2020, 1, 1), 3933.65) diff --git a/l10n_us_hr_payroll/views/us_payroll_config_views.xml b/l10n_us_hr_payroll/views/us_payroll_config_views.xml index a3fda1fc..99889df7 100644 --- a/l10n_us_hr_payroll/views/us_payroll_config_views.xml +++ b/l10n_us_hr_payroll/views/us_payroll_config_views.xml @@ -85,6 +85,12 @@ + +

Form HI HW-4 - State Income Tax

+ + + +

Form ID W-4 - State Income Tax