From e2c73da4022e7bb9b729dc5687f60fa90f0c74d7 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Wed, 8 Jan 2020 18:56:49 -0800
Subject: [PATCH] IMP `l10n_us_hr_payroll` Port `l10n_us_wa_hr_payroll` WA
Washington including migration + FML rules
---
l10n_us_hr_payroll/__manifest__.py | 1 +
l10n_us_hr_payroll/data/final.xml | 6 +
.../data/state/wa_washington.xml | 203 ++++++++++++++++++
l10n_us_hr_payroll/migrations/data.py | 21 ++
l10n_us_hr_payroll/models/hr_payslip.py | 8 +-
l10n_us_hr_payroll/models/state/general.py | 4 +
l10n_us_hr_payroll/models/state/mt_montana.py | 2 +
l10n_us_hr_payroll/models/state/oh_ohio.py | 2 +
.../models/state/wa_washington.py | 27 +++
.../models/us_payroll_config.py | 4 +
l10n_us_hr_payroll/tests/__init__.py | 3 +
l10n_us_hr_payroll/tests/common.py | 3 +
.../tests/test_us_oh_ohio_payslip_2019.py | 2 +-
.../tests/test_us_oh_ohio_payslip_2020.py | 2 +-
.../test_us_wa_washington_payslip_2019.py | 88 ++++++++
.../test_us_wa_washington_payslip_2020.py | 86 ++++++++
.../views/us_payroll_config_views.xml | 9 +-
17 files changed, 467 insertions(+), 4 deletions(-)
create mode 100644 l10n_us_hr_payroll/data/state/wa_washington.xml
create mode 100644 l10n_us_hr_payroll/models/state/wa_washington.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2020.py
diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py
index c10cc4e7..f2d7f78d 100755
--- a/l10n_us_hr_payroll/__manifest__.py
+++ b/l10n_us_hr_payroll/__manifest__.py
@@ -32,6 +32,7 @@ USA Payroll Rules.
'data/state/mt_montana.xml',
'data/state/oh_ohio.xml',
'data/state/pa_pennsylvania.xml',
+ 'data/state/wa_washington.xml',
'data/final.xml',
'views/hr_contract_views.xml',
'views/us_payroll_config_views.xml',
diff --git a/l10n_us_hr_payroll/data/final.xml b/l10n_us_hr_payroll/data/final.xml
index 7eb30e83..6e855bd4 100644
--- a/l10n_us_hr_payroll/data/final.xml
+++ b/l10n_us_hr_payroll/data/final.xml
@@ -29,6 +29,12 @@
ref('hr_payroll_rule_ee_us_pa_suta'),
ref('hr_payroll_rule_ee_us_pa_sit'),
+ ref('hr_payroll_rule_er_us_wa_suta'),
+ ref('hr_payroll_rule_er_us_wa_fml'),
+ ref('hr_payroll_rule_ee_us_wa_fml'),
+ ref('hr_payroll_rule_er_us_wa_lni'),
+ ref('hr_payroll_rule_ee_us_wa_lni'),
+
ref('hr_salary_rule_commission'),
ref('hr_salary_rule_gamification'),
])]" name="rule_ids"/>
diff --git a/l10n_us_hr_payroll/data/state/wa_washington.xml b/l10n_us_hr_payroll/data/state/wa_washington.xml
new file mode 100644
index 00000000..52e0b736
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wa_washington.xml
@@ -0,0 +1,203 @@
+
+
+
+
+
+ US WA Washington SUTA Wage Base
+ us_wa_suta_wage_base
+ 49800.0
+
+
+
+ US WA Washington SUTA Wage Base
+ us_wa_suta_wage_base
+ 52700.00
+
+
+
+
+
+
+ US WA Washington FML Wage Base
+ us_wa_fml_wage_base
+ 132900.00
+
+
+
+ US WA Washington FML Wage Base
+ us_wa_fml_wage_base
+ 137700.00
+
+
+
+
+
+
+
+ US WA Washington SUTA Rate
+ us_wa_suta_rate
+ 1.18
+
+
+
+ US WA Washington SUTA Rate
+ us_wa_suta_rate
+ 1.0
+
+
+
+
+
+
+ US WA Washington FML Rate (Total)
+ us_wa_fml_rate
+ 0.4
+
+
+
+ US WA Washington FML Rate (Total)
+ us_wa_fml_rate
+ 0.4
+
+
+
+
+
+
+ US WA Washington FML Rate (Employee)
+ us_wa_fml_rate_ee
+ 66.33
+
+
+
+ US WA Washington FML Rate (Employee)
+ us_wa_fml_rate_ee
+ 66.33
+
+
+
+
+
+
+ US WA Washington FML Rate (Employer)
+ us_wa_fml_rate_er
+ 33.67
+
+
+
+ US WA Washington FML Rate (Employer)
+ us_wa_fml_rate_er
+ 33.67
+
+
+
+
+
+
+ US Washington - Employment Security Department (Unemployment)
+
+
+
+ US Washington - Employment Security Department (Unemployment)
+
+
+
+
+ US Washington - Department of Labor & Industries
+
+
+
+ US Washington - Department of Labor & Industries
+
+
+
+
+ US Washington - Employment Security Department (PFML)
+
+
+
+ US Washington - Employment Security Department (PFML)
+
+
+
+
+
+
+
+
+
+ ER: US WA Washington State Unemployment (5208A/B)
+ ER_US_WA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wa_suta_wage_base', rate='us_wa_suta_rate', state_code='WA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wa_suta_wage_base', rate='us_wa_suta_rate', state_code='WA')
+
+
+
+
+
+
+
+ ER: US WA Washington State Family Medical Leave
+ ER_US_WA_FML
+ python
+ result, _ = wa_washington_fml_er(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = wa_washington_fml_er(payslip, categories, worked_days, inputs)
+
+
+
+
+
+
+
+ EE: US WA Washington State Family Medical Leave
+ EE_US_WA_FML
+ python
+ result, _ = wa_washington_fml_ee(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = wa_washington_fml_ee(payslip, categories, worked_days, inputs)
+
+
+
+
+
+
+
+
+ ER: US WA Washington State LNI
+ ER_US_WA_LNI
+ python
+ result = is_us_state(payslip, 'WA') and payslip.contract_id.us_payroll_config_value('workers_comp_er_code') and worked_days.WORK100 and worked_days.WORK100.number_of_hours and payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+ code
+
+hours = worked_days.WORK100.number_of_hours
+rate = payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+try:
+ # Redo employee withholding calculation
+ ee_withholding = worked_days.WORK100.number_of_hours * -payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code')) / 100.0
+except:
+ ee_withholding = 0.0
+er_withholding = -(hours * (rate / 100.0)) - ee_withholding
+result = hours
+result_rate = (er_withholding / hours) * 100.0
+
+
+
+
+
+
+
+
+ EE: US WA Washington State LNI
+ EE_US_WA_LNI
+ python
+ result = is_us_state(payslip, 'WA') and payslip.contract_id.us_payroll_config_value('workers_comp_ee_code') and worked_days.WORK100 and worked_days.WORK100.number_of_hours and payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
+ code
+ result, result_rate = worked_days.WORK100.number_of_hours, -payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/migrations/data.py b/l10n_us_hr_payroll/migrations/data.py
index b5d7d15e..c15c3749 100644
--- a/l10n_us_hr_payroll/migrations/data.py
+++ b/l10n_us_hr_payroll/migrations/data.py
@@ -58,6 +58,13 @@ XMLIDS_TO_REMOVE_2020 = [
'l10n_us_pa_hr_payroll.hr_payroll_pa_withhold',
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_unemp_wages_2018',
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_inc_withhold_add',
+
+ 'l10n_us_wa_hr_payroll.hr_payroll_wa_unemp_wages',
+ 'l10n_us_wa_hr_payroll.hr_payroll_wa_unemp',
+ 'l10n_us_wa_hr_payroll.hr_payroll_wa_lni',
+ 'l10n_us_wa_hr_payroll.hr_payroll_wa_lni_withhold',
+ 'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_unemp_wages_2018',
+
]
XMLIDS_TO_RENAME_2020 = {
@@ -100,10 +107,24 @@ XMLIDS_TO_RENAME_2020 = {
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_unemp_company_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_pa_suta',
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_inc_withhold_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_pa_sit',
+ 'l10n_us_wa_hr_payroll.res_partner_wador_unemp': 'l10n_us_hr_payroll.res_partner_us_wa_dor',
+ 'l10n_us_wa_hr_payroll.res_partner_wador_lni': 'l10n_us_hr_payroll.res_partner_us_wa_dor_lni',
+ 'l10n_us_wa_hr_payroll.contrib_register_wador_unemp': 'l10n_us_hr_payroll.contrib_register_us_wa_dor',
+ 'l10n_us_wa_hr_payroll.contrib_register_wador_lni': 'l10n_us_hr_payroll.contrib_register_us_wa_dor_lni',
+ 'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_suta',
+ 'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_lni_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_wa_lni',
+ 'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_lni': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_lni',
+
}
XMLIDS_COPY_ACCOUNTING_2020 = {
'l10n_us_hr_payroll.hr_payroll_rule_er_us_mt_suta': [
'l10n_us_hr_payroll.hr_payroll_rule_er_us_mt_suta_aft',
],
+ 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_lni': [
+ 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_fml',
+ ],
+ 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_wa_lni': [
+ 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_wa_fml',
+ ],
}
diff --git a/l10n_us_hr_payroll/models/hr_payslip.py b/l10n_us_hr_payroll/models/hr_payslip.py
index e2a885f7..5521b078 100644
--- a/l10n_us_hr_payroll/models/hr_payslip.py
+++ b/l10n_us_hr_payroll/models/hr_payslip.py
@@ -10,9 +10,12 @@ from .federal.fed_941 import ee_us_941_fica_ss, \
er_us_941_fica_m, \
ee_us_941_fit
from .state.general import general_state_unemployment, \
- general_state_income_withholding
+ general_state_income_withholding, \
+ is_us_state
from .state.mt_montana import mt_montana_state_income_withholding
from .state.oh_ohio import oh_ohio_state_income_withholding
+from .state.wa_washington import wa_washington_fml_er, \
+ wa_washington_fml_ee
class HRPayslip(models.Model):
@@ -44,8 +47,11 @@ class HRPayslip(models.Model):
'ee_us_941_fit': ee_us_941_fit,
'general_state_unemployment': general_state_unemployment,
'general_state_income_withholding': general_state_income_withholding,
+ 'is_us_state': is_us_state,
'mt_montana_state_income_withholding': mt_montana_state_income_withholding,
'oh_ohio_state_income_withholding': oh_ohio_state_income_withholding,
+ 'wa_washington_fml_er': wa_washington_fml_er,
+ 'wa_washington_fml_ee': wa_washington_fml_ee,
}
def get_year(self):
diff --git a/l10n_us_hr_payroll/models/state/general.py b/l10n_us_hr_payroll/models/state/general.py
index 0d11b054..af2e3931 100644
--- a/l10n_us_hr_payroll/models/state/general.py
+++ b/l10n_us_hr_payroll/models/state/general.py
@@ -9,6 +9,10 @@ def _state_applies(payslip, state_code):
return state_code == payslip.dict.contract_id.us_payroll_config_value('state_code')
+# Export for eval context
+is_us_state = _state_applies
+
+
def _general_rate(payslip, wage, ytd_wage, wage_base=None, wage_start=None, rate=None):
"""
Function parameters:
diff --git a/l10n_us_hr_payroll/models/state/mt_montana.py b/l10n_us_hr_payroll/models/state/mt_montana.py
index 3816b318..b9fa0986 100644
--- a/l10n_us_hr_payroll/models/state/mt_montana.py
+++ b/l10n_us_hr_payroll/models/state/mt_montana.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from .general import _state_applies
diff --git a/l10n_us_hr_payroll/models/state/oh_ohio.py b/l10n_us_hr_payroll/models/state/oh_ohio.py
index 793d4900..8ec52538 100644
--- a/l10n_us_hr_payroll/models/state/oh_ohio.py
+++ b/l10n_us_hr_payroll/models/state/oh_ohio.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from .general import _state_applies
diff --git a/l10n_us_hr_payroll/models/state/wa_washington.py b/l10n_us_hr_payroll/models/state/wa_washington.py
new file mode 100644
index 00000000..c608d2da
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/wa_washington.py
@@ -0,0 +1,27 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, _general_rate
+
+
+def _wa_washington_fml(payslip, categories, worked_days, inputs, inner_rate=None):
+ if not inner_rate:
+ return 0.0, 0.0
+
+ if not _state_applies(payslip, 'WA'):
+ return 0.0, 0.0
+
+ wage = categories.GROSS
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year + 1) + '-01-01')
+ ytd_wage += payslip.contract_id.external_wages
+ rate = payslip.dict.rule_parameter('us_wa_fml_rate')
+ rate *= payslip.dict.rule_parameter(inner_rate) / 100.0
+ return _general_rate(payslip, wage, ytd_wage, wage_base='us_wa_fml_wage_base', rate=rate)
+
+
+def wa_washington_fml_er(payslip, categories, worked_days, inputs):
+ return _wa_washington_fml(payslip, categories, worked_days, inputs, inner_rate='us_wa_fml_rate_er')
+
+
+def wa_washington_fml_ee(payslip, categories, worked_days, inputs):
+ return _wa_washington_fml(payslip, categories, worked_days, inputs, inner_rate='us_wa_fml_rate_ee')
diff --git a/l10n_us_hr_payroll/models/us_payroll_config.py b/l10n_us_hr_payroll/models/us_payroll_config.py
index e0d56a6c..be1fa946 100644
--- a/l10n_us_hr_payroll/models/us_payroll_config.py
+++ b/l10n_us_hr_payroll/models/us_payroll_config.py
@@ -17,6 +17,10 @@ class HRContractUSPayrollConfig(models.Model):
state_code = fields.Char(related='state_id.code')
state_income_tax_exempt = fields.Boolean(string='State Income Tax Exempt')
state_income_tax_additional_withholding = fields.Float(string='State Income Tax Additional Withholding')
+ workers_comp_ee_code = fields.Char(string='Workers\' Comp Code (Employee Withholding)',
+ help='Code for a Payroll Rate, used by some states or your own rules.')
+ workers_comp_er_code = fields.Char(string='Workers\' Comp Code (Employer Withholding)',
+ help='Code for a Payroll Rate, used by some states or your own rules.')
fed_940_type = fields.Selection([
(FUTA_TYPE_EXEMPT, 'Exempt (0%)'),
diff --git a/l10n_us_hr_payroll/tests/__init__.py b/l10n_us_hr_payroll/tests/__init__.py
index a2953520..7ffc630a 100755
--- a/l10n_us_hr_payroll/tests/__init__.py
+++ b/l10n_us_hr_payroll/tests/__init__.py
@@ -15,3 +15,6 @@ from . import test_us_oh_ohio_payslip_2020
from . import test_us_pa_pennsylvania_payslip_2019
from . import test_us_pa_pennsylvania_payslip_2020
+
+from . import test_us_wa_washington_payslip_2019
+from . import test_us_wa_washington_payslip_2020
diff --git a/l10n_us_hr_payroll/tests/common.py b/l10n_us_hr_payroll/tests/common.py
index 042302bb..338e1d44 100755
--- a/l10n_us_hr_payroll/tests/common.py
+++ b/l10n_us_hr_payroll/tests/common.py
@@ -151,6 +151,9 @@ class TestUsPayslip(common.TransactionCase):
def assertPayrollEqual(self, first, second):
self.assertAlmostEqual(first, second, self.payroll_digits)
+ def assertPayrollAlmostEqual(self, first, second):
+ self.assertAlmostEqual(first, second, self.payroll_digits-1)
+
def test_semi_monthly(self):
salary = 80000.0
employee = self._createEmployee()
diff --git a/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
index bf38f4d5..d1f65f05 100755
--- a/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
+++ b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
@@ -34,7 +34,7 @@ class TestUsOhPayslip(TestUsPayslip):
cats = self._getCategories(payslip)
self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.OH_UNEMP)
- self.assertAlmostEqual(cats['EE_US_SIT'], -wd, 1) # Off by 0.6 cents so it rounds off by a penny
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], -wd) # Off by 0.6 cents so it rounds off by a penny
#self.assertPayrollEqual(cats['EE_US_SIT'], -wd)
process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py
index 04256afa..9026da92 100755
--- a/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py
@@ -48,7 +48,7 @@ class TestUsOhPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
# Instead of PayrollEqual after initial first round of testing.
- self.assertAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected, 1)
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected)
return payslip
def test_2020_sit_1(self):
diff --git a/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
new file mode 100755
index 00000000..11cf6138
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
@@ -0,0 +1,88 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsWAPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ WA_UNEMP_MAX_WAGE = 49800.0
+ WA_UNEMP_RATE = 1.18
+ WA_FML_RATE = 0.4
+ WA_FML_RATE_EE = 66.33
+ WA_FML_RATE_ER = 33.67
+
+ def setUp(self):
+ super(TestUsWAPayslip, self).setUp()
+ # self.lni = self.env['hr.contract.lni.wa'].create({
+ # 'name': '5302 Computer Consulting',
+ # 'rate': 0.1261,
+ # 'rate_emp_withhold': 0.05575,
+ # })
+ self.test_ee_lni = 0.05575 # per 100 hours
+ self.test_er_lni = 0.1261 # per 100 hours
+ self.parameter_lni_ee = self.env['hr.payroll.rate'].create({
+ 'name': 'Test LNI EE',
+ 'code': 'test_lni_ee',
+ 'date_from': date(2019, 1, 1),
+ 'parameter_value': str(self.test_ee_lni * 100),
+ })
+ self.parameter_lni_er = self.env['hr.payroll.rate'].create({
+ 'name': 'Test LNI ER',
+ 'code': 'test_lni_er',
+ 'date_from': date(2019, 1, 1),
+ 'parameter_value': str(self.test_er_lni * 100),
+ })
+
+ def test_2019_taxes(self):
+ salary = 25000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('WA'),
+ workers_comp_ee_code=self.parameter_lni_ee.code,
+ workers_comp_er_code=self.parameter_lni_er.code,
+ )
+ self._log(str(contract.resource_calendar_id) + ' ' + contract.resource_calendar_id.name)
+
+
+ # tax rates
+ wa_unemp = self.WA_UNEMP_RATE / -100.0
+
+ self._log('2019 Washington tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ hours_in_period = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100').number_of_hours
+ self.assertEqual(hours_in_period, 184) # only asserted to test algorithm
+ payslip.compute_sheet()
+
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * wa_unemp)
+ self.assertPayrollEqual(rules['EE_US_WA_LNI'], -(self.test_ee_lni * hours_in_period))
+ self.assertPayrollEqual(rules['ER_US_WA_LNI'], -(self.test_er_lni * hours_in_period) - rules['EE_US_WA_LNI'])
+ # Both of these are known to be within 1 penny
+ self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_EE / 100.0)))
+ self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_ER / 100.0)))
+
+ # FML
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_wa_unemp_wages = self.WA_UNEMP_MAX_WAGE - salary if (self.WA_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Washington tax second payslip:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_wa_unemp_wages * wa_unemp)
diff --git a/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2020.py
new file mode 100755
index 00000000..9272eba0
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2020.py
@@ -0,0 +1,86 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsWAPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ WA_UNEMP_MAX_WAGE = 52700.00
+ WA_UNEMP_RATE = 1.0
+ WA_FML_MAX_WAGE = 137700.00
+ WA_FML_RATE = 0.4
+ WA_FML_RATE_EE = 66.33
+ WA_FML_RATE_ER = 33.67
+
+ def setUp(self):
+ super(TestUsWAPayslip, self).setUp()
+ # self.lni = self.env['hr.contract.lni.wa'].create({
+ # 'name': '5302 Computer Consulting',
+ # 'rate': 0.1261,
+ # 'rate_emp_withhold': 0.05575,
+ # })
+ self.test_ee_lni = 0.05575 # per 100 hours
+ self.test_er_lni = 0.1261 # per 100 hours
+ self.parameter_lni_ee = self.env['hr.payroll.rate'].create({
+ 'name': 'Test LNI EE',
+ 'code': 'test_lni_ee',
+ 'date_from': date(2019, 1, 1),
+ 'parameter_value': str(self.test_ee_lni * 100),
+ })
+ self.parameter_lni_er = self.env['hr.payroll.rate'].create({
+ 'name': 'Test LNI ER',
+ 'code': 'test_lni_er',
+ 'date_from': date(2019, 1, 1),
+ 'parameter_value': str(self.test_er_lni * 100),
+ })
+
+ def test_2020_taxes(self):
+ self._test_er_suta('WA', self.WA_UNEMP_RATE, date(2020, 1, 1), wage_base=self.WA_UNEMP_MAX_WAGE)
+
+ salary = (self.WA_FML_MAX_WAGE / 2.0) + 1000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('WA'),
+ workers_comp_ee_code=self.parameter_lni_ee.code,
+ workers_comp_er_code=self.parameter_lni_er.code,
+ )
+ self._log(str(contract.resource_calendar_id) + ' ' + contract.resource_calendar_id.name)
+
+
+ # Non SUTA
+ self._log('2020 Washington tax first payslip:')
+ payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ hours_in_period = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100').number_of_hours
+ self.assertEqual(hours_in_period, 184) # only asserted to test algorithm
+ payslip.compute_sheet()
+
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(rules['EE_US_WA_LNI'], -(self.test_ee_lni * hours_in_period))
+ self.assertPayrollEqual(rules['ER_US_WA_LNI'], -(self.test_er_lni * hours_in_period) - rules['EE_US_WA_LNI'])
+ # Both of these are known to be within 1 penny
+ self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_EE / 100.0)))
+ self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_ER / 100.0)))
+ process_payslip(payslip)
+
+ # Second payslip
+ remaining_wage = self.WA_FML_MAX_WAGE - salary
+ payslip = self._createPayslip(employee, '2020-03-01', '2020-03-31')
+ payslip.compute_sheet()
+ rules = self._getRules(payslip)
+ self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], -(remaining_wage * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_EE / 100.0)))
+ self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], -(remaining_wage * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_ER / 100.0)))
+ process_payslip(payslip)
+
+ # Third payslip
+ payslip = self._createPayslip(employee, '2020-04-01', '2020-04-30')
+ payslip.compute_sheet()
+ rules = self._getRules(payslip)
+ self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], 0.0)
+ self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], 0.0)
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 ee993be7..f47e18fe 100644
--- a/l10n_us_hr_payroll/views/us_payroll_config_views.xml
+++ b/l10n_us_hr_payroll/views/us_payroll_config_views.xml
@@ -26,7 +26,6 @@
-
Form 940 - Federal Unemployment
Form 941 / W4 - Federal Income Tax
@@ -39,6 +38,10 @@
+ State Information and Extra
+
+
+
@@ -60,6 +63,10 @@
+
+ No additional fields.
+ Ensure that your Employee and Employer workers' comp code fields are filled in for WA LNI withholding.
+