diff --git a/l10n_us_hr_payroll/data/state/me_maine.xml b/l10n_us_hr_payroll/data/state/me_maine.xml index 2c1aaf71..810154f6 100644 --- a/l10n_us_hr_payroll/data/state/me_maine.xml +++ b/l10n_us_hr_payroll/data/state/me_maine.xml @@ -26,6 +26,12 @@ + + + 2.31 + + + @@ -50,6 +56,22 @@ + + { + 'single': ( + ( 22450, 0, 5.80), + ( 53150, 1302, 6.75), + ( 'inf', 3374, 7.15), + ), + 'married': ( + ( 44950, 0, 5.80), + ( 106350, 2607, 6.75), + ( 'inf', 6752, 7.15), + ), + } + + + @@ -72,6 +94,22 @@ + + + + { + 'single': { + ( 83850, 9700), + (158850, 75000), + }, + 'married': { + (167700, 22250), + (317700, 150000), + }, + } + + + diff --git a/l10n_us_hr_payroll/models/state/me_maine.py b/l10n_us_hr_payroll/models/state/me_maine.py index 0accc6ff..ad6b9851 100644 --- a/l10n_us_hr_payroll/models/state/me_maine.py +++ b/l10n_us_hr_payroll/models/state/me_maine.py @@ -1,6 +1,8 @@ # Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. from .general import _state_applies, sit_wage +import logging +_logger = logging.getLogger(__name__) def me_maine_state_income_withholding(payslip, categories, worked_days, inputs): @@ -10,21 +12,26 @@ def me_maine_state_income_withholding(payslip, categories, worked_days, inputs): :return: result, result_rate (wage, percent) """ + state_code = 'ME' if not _state_applies(payslip, state_code): + _logger.warn('state doesnt apply') return 0.0, 0.0 # Determine Wage wage = sit_wage(payslip, categories) if not wage: + _logger.warn('no wage') return 0.0, 0.0 filing_status = payslip.contract_id.us_payroll_config_value('me_w4me_sit_filing_status') if not filing_status: + _logger.warn('exempt file status') return 0.0, 0.0 exempt = payslip.contract_id.us_payroll_config_value('state_income_tax_exempt') if exempt: + _logger.warn('generic exemption') return 0.0, 0.0 pay_periods = payslip.dict.get_pay_periods_in_year() @@ -38,7 +45,8 @@ def me_maine_state_income_withholding(payslip, categories, worked_days, inputs): exemption_amt = allowances * personal_exemption last = 0.0 standard_deduction_amt = 0.0 - for row in standard_deduction: + + for row in standard_deduction: #Standard_deduction is a set so looping through without giving it order isn't working amt, flat_amt = row if taxable_income < 82900: standard_deduction_amt = flat_amt diff --git a/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2021.py new file mode 100644 index 00000000..5bd17991 --- /dev/null +++ b/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2021.py @@ -0,0 +1,45 @@ +# 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 TestUsMEPayslip(TestUsPayslip): + ### + # 2021 Taxes and Rates + ### + ME_UNEMP_MAX_WAGE = 12000.0 + ME_UNEMP = 2.31 + # Calculation based on this file page.6 and 7 https://www.maine.gov/revenue/forms/with/2021/20_WH_Tab&Instructions.pdf + + def _test_sit(self, wage, filing_status, additional_withholding, exempt, allowances, schedule_pay, date_start, expected_withholding): + employee = self._createEmployee() + contract = self._createContract(employee, + wage=wage, + state_id=self.get_us_state('ME'), + me_w4me_sit_filing_status=filing_status, + state_income_tax_additional_withholding=additional_withholding, + state_income_tax_exempt=exempt, + me_w4me_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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding) + + def test_2021_taxes_example(self): + # todo: not calculating correctly according to https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/21_wh_tab_instr_1.pdf + # See examples on page 7 + + import pydevd_pycharm + pydevd_pycharm.settrace('192.168.1.134', port=6900, stdoutToServer=True, stderrToServer=True) + + self._test_er_suta('ME', self.ME_UNEMP, date(2021, 1, 1), wage_base=self.ME_UNEMP_MAX_WAGE) + self._test_sit(300.0, 'single', 0.0, False, 2, 'weekly', date(2021, 1, 1), 0.0) + self._test_sit(1800.0, 'single', 0.0, False, 2, 'bi-weekly', date(2021, 1, 1), 6.00) + self._test_sit(4500.0, 'married', 0.0, True, 0, 'weekly', date(2021, 1, 1), 0.00) + self._test_sit(4500.0, 'married', 0.0, False, 2, 'monthly', date(2021, 1, 1), 113.00) + self._test_sit(4500.0, 'married', 10.0, False, 2, 'weekly', date(2021, 1, 1), 287.00) + self._test_sit(7000.0, '', 10.0, False, 2, 'weekly', date(2021, 1, 1), 0.00)