From 5c7c9e639775ff30fb531fee23feb5257059f2cf Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Sun, 5 Jan 2020 14:07:26 -0800
Subject: [PATCH 01/12] NEW `l10n_us_hr_payroll` Initiall commit for Odoo 13
(rewrite) and 2020 Federal Rates (including new W4)
---
l10n_us_hr_payroll/__init__.py | 3 +
l10n_us_hr_payroll/__manifest__.py | 35 ++
l10n_us_hr_payroll/data/base.xml | 20 +
.../data/federal/fed_940_futa_parameters.xml | 38 ++
.../data/federal/fed_940_futa_rules.xml | 34 ++
.../data/federal/fed_941_fica_parameters.xml | 88 +++
.../data/federal/fed_941_fica_rules.xml | 100 ++++
.../data/federal/fed_941_fit_parameters.xml | 504 ++++++++++++++++++
.../data/federal/fed_941_fit_rules.xml | 29 +
l10n_us_hr_payroll/data/integration_rules.xml | 29 +
l10n_us_hr_payroll/models/__init__.py | 5 +
l10n_us_hr_payroll/models/federal/__init__.py | 1 +
l10n_us_hr_payroll/models/federal/fed_940.py | 37 ++
l10n_us_hr_payroll/models/federal/fed_941.py | 239 +++++++++
l10n_us_hr_payroll/models/hr_contract.py | 28 +
l10n_us_hr_payroll/models/hr_payslip.py | 48 ++
.../models/us_payroll_config.py | 45 ++
.../security/ir.model.access.csv | 2 +
.../static/description/icon.png | Bin 0 -> 8776 bytes
l10n_us_hr_payroll/tests/__init__.py | 5 +
l10n_us_hr_payroll/tests/common.py | 150 ++++++
.../tests/test_us_payslip_2019.py | 338 ++++++++++++
.../tests/test_us_payslip_2020.py | 302 +++++++++++
.../views/hr_contract_views.xml | 19 +
.../views/us_payroll_config_views.xml | 75 +++
25 files changed, 2174 insertions(+)
create mode 100644 l10n_us_hr_payroll/__init__.py
create mode 100644 l10n_us_hr_payroll/__manifest__.py
create mode 100644 l10n_us_hr_payroll/data/base.xml
create mode 100644 l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
create mode 100644 l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
create mode 100644 l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
create mode 100644 l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
create mode 100644 l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
create mode 100644 l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
create mode 100644 l10n_us_hr_payroll/data/integration_rules.xml
create mode 100644 l10n_us_hr_payroll/models/__init__.py
create mode 100644 l10n_us_hr_payroll/models/federal/__init__.py
create mode 100644 l10n_us_hr_payroll/models/federal/fed_940.py
create mode 100644 l10n_us_hr_payroll/models/federal/fed_941.py
create mode 100644 l10n_us_hr_payroll/models/hr_contract.py
create mode 100644 l10n_us_hr_payroll/models/hr_payslip.py
create mode 100644 l10n_us_hr_payroll/models/us_payroll_config.py
create mode 100644 l10n_us_hr_payroll/security/ir.model.access.csv
create mode 100644 l10n_us_hr_payroll/static/description/icon.png
create mode 100755 l10n_us_hr_payroll/tests/__init__.py
create mode 100755 l10n_us_hr_payroll/tests/common.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/views/hr_contract_views.xml
create mode 100644 l10n_us_hr_payroll/views/us_payroll_config_views.xml
diff --git a/l10n_us_hr_payroll/__init__.py b/l10n_us_hr_payroll/__init__.py
new file mode 100644
index 00000000..09434554
--- /dev/null
+++ b/l10n_us_hr_payroll/__init__.py
@@ -0,0 +1,3 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from . import models
diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py
new file mode 100644
index 00000000..a748aae6
--- /dev/null
+++ b/l10n_us_hr_payroll/__manifest__.py
@@ -0,0 +1,35 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+{
+ 'name': 'United States of America - Payroll',
+ 'author': 'Hibou Corp. ',
+ 'version': '13.0.2020.0.0',
+ 'category': 'Payroll Localization',
+ 'depends': [
+ 'hr_payroll',
+ 'hr_contract_reports',
+ ],
+ 'description': """
+United States of America - Payroll Rules.
+=========================================
+
+ """,
+
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'data/base.xml',
+ 'data/integration_rules.xml',
+ 'data/federal/fed_940_futa_parameters.xml',
+ 'data/federal/fed_940_futa_rules.xml',
+ 'data/federal/fed_941_fica_parameters.xml',
+ 'data/federal/fed_941_fica_rules.xml',
+ 'data/federal/fed_941_fit_parameters.xml',
+ 'data/federal/fed_941_fit_rules.xml',
+ 'views/hr_contract_views.xml',
+ 'views/us_payroll_config_views.xml',
+ ],
+ 'demo': [
+ ],
+ 'auto_install': False,
+ 'license': 'OPL-1',
+}
diff --git a/l10n_us_hr_payroll/data/base.xml b/l10n_us_hr_payroll/data/base.xml
new file mode 100644
index 00000000..6838f283
--- /dev/null
+++ b/l10n_us_hr_payroll/data/base.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ USA Employee
+
+
+
+
+
+ USA Employee Standard
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml b/l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
new file mode 100644
index 00000000..9c34afb6
--- /dev/null
+++ b/l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
@@ -0,0 +1,38 @@
+
+
+
+
+ Federal 940 FUTA Wage Base
+ fed_940_futa_wage_base
+
+
+
+ 7000.00
+
+
+
+
+
+
+ Federal 940 FUTA Rate Basic
+ fed_940_futa_rate_basic
+
+
+
+ 6.0
+
+
+
+
+
+ Federal 940 FUTA Rate Normal
+ fed_940_futa_rate_normal
+
+
+
+ 0.6
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml b/l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
new file mode 100644
index 00000000..5b315100
--- /dev/null
+++ b/l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ US Federal 940 - EFTPS
+
+
+
+ ER: Federal 940 FUTA
+ ER_US_940_FUTA
+
+
+
+
+
+ WAGE: Federal 940 FUTA Exempt
+ WAGE_US_940_FUTA_EXEMPT
+
+
+
+
+
+
+ ER: US FUTA Federal Unemployment
+ ER_US_940_FUTA
+ python
+ result, _ = er_us_940_futa(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = er_us_940_futa(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml b/l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
new file mode 100644
index 00000000..1be404bb
--- /dev/null
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+ Federal 941 FICA Social Security Wage Base
+ fed_941_fica_ss_wage_base
+
+
+
+ 128400.0
+
+
+
+
+ 132900.0
+
+
+
+
+ 137700.0
+
+
+
+
+
+
+ Federal 941 FICA Rate
+ fed_941_fica_ss_rate
+
+
+
+ 6.2
+
+
+
+
+
+
+
+ Federal 941 FICA Medicare Wage Base
+ fed_941_fica_m_wage_base
+
+
+
+ "inf"
+
+
+
+
+
+
+ Federal 941 FICA Rate
+ fed_941_fica_m_rate
+
+
+
+ 1.45
+
+
+
+
+
+
+
+ Federal 941 FICA Medicare Additional Wage Start
+ fed_941_fica_m_add_wage_start
+
+
+
+ 200000.0
+
+
+
+
+
+
+ Federal 941 FICA Medicare Additional Rate
+ fed_941_fica_m_add_rate
+
+
+
+ 0.9
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml b/l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
new file mode 100644
index 00000000..324958a4
--- /dev/null
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
@@ -0,0 +1,100 @@
+
+
+
+
+ US Federal 941 - EFTPS
+
+
+
+ EE: Federal 941 FICA
+ EE_US_941_FICA
+
+
+
+
+ ER: Federal 941 FICA
+ ER_US_941_FICA
+
+
+
+
+
+ WAGE: Federal 941 FICA Exempt
+ WAGE_US_941_FICA_EXEMPT
+
+
+
+
+
+
+
+
+ EE: US FICA Social Security
+ EE_US_941_FICA_SS
+ python
+ result, _ = ee_us_941_fica_ss(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ee_us_941_fica_ss(payslip, categories, worked_days, inputs)
+
+
+
+
+
+
+
+
+ ER: US FICA Social Security
+ ER_US_941_FICA_SS
+ python
+ result, _ = er_us_941_fica_ss(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = er_us_941_fica_ss(payslip, categories, worked_days, inputs)
+
+
+
+
+
+
+
+
+
+ EE: US FICA Medicare
+ EE_US_941_FICA_M
+ python
+ result, _ = ee_us_941_fica_m(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ee_us_941_fica_m(payslip, categories, worked_days, inputs)
+
+
+
+
+
+
+
+
+ ER: US FICA Medicare
+ ER_US_941_FICA_M
+ python
+ result, _ = er_us_941_fica_m(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = er_us_941_fica_m(payslip, categories, worked_days, inputs)
+
+
+
+
+
+
+
+
+
+ EE: US FICA Medicare Additional
+ EE_US_941_FICA_M_ADD
+ python
+ result, _ = ee_us_941_fica_m_add(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ee_us_941_fica_m_add(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml b/l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
new file mode 100644
index 00000000..97753e5a
--- /dev/null
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
@@ -0,0 +1,504 @@
+
+
+
+
+
+ Federal 941 FIT Allowance
+ fed_941_fit_allowance
+
+
+
+
+ {
+ 'weekly': 80.80,
+ 'bi-weekly': 161.50,
+ 'semi-monthly': 175.00,
+ 'monthly': 350.00,
+ 'quarterly': 1050.00,
+ 'semi-annually': 2100.00,
+ 'annually': 4200.00,
+ }
+
+
+
+
+ {
+ 'weekly': 80.80,
+ 'bi-weekly': 161.50,
+ 'semi-monthly': 175.00,
+ 'monthly': 350.00,
+ 'quarterly': 1050.00,
+ 'semi-annually': 2100.00,
+ 'annually': 4200.00,
+ }
+
+
+
+
+
+ 4300.0
+
+
+
+
+
+ Federal 941 FIT NRA Additional
+ fed_941_fit_nra_additional
+
+
+
+
+ {
+ 'weekly': 153.80,
+ 'bi-weekly': 307.70,
+ 'semi-monthly': 333.30,
+ 'monthly': 666.70,
+ 'quarterly': 2000.00,
+ 'semi-annually': 4000.00,
+ 'annually': 8000.00,
+ }
+
+
+
+
+ {
+ 'weekly': 153.80,
+ 'bi-weekly': 307.70,
+ 'semi-monthly': 333.30,
+ 'monthly': 666.70,
+ 'quarterly': 2000.00,
+ 'semi-annually': 4000.00,
+ 'annually': 8000.00,
+ }
+
+
+
+
+ {
+ 'weekly': 238.50,
+ 'bi-weekly': 476.90,
+ 'semi-monthly': 516.70,
+ 'monthly': 1033.30,
+ 'quarterly': 3100.00,
+ 'semi-annually': 6200.00,
+ 'annually': 12400.00,
+ }
+
+
+
+
+
+
+ Federal 941 FIT Table Single
+ fed_941_fit_table_single
+
+
+
+
+
+ {
+ 'weekly': [
+ ( 73.00, 0.00, 0),
+ ( 260.00, 0.00, 10),
+ ( 832.00, 18.70, 12),
+ ( 1692.00, 87.34, 22),
+ ( 3164.00, 276.54, 24),
+ ( 3998.00, 629.82, 32),
+ ( 9887.00, 896.70, 35),
+ ( 'inf', 2957.85, 37),
+ ],
+ 'bi-weekly': [
+ ( 146.00, 0.00, 0),
+ ( 519.00, 0.00, 10),
+ ( 1664.00, 37.30, 12),
+ ( 3385.00, 174.70, 22),
+ ( 6328.00, 553.32, 24),
+ ( 7996.00, 1259.64, 32),
+ ( 19773.00, 1793.40, 35),
+ ( 'inf', 5915.35, 37),
+ ],
+ 'semi-monthly': [
+ ( 158.00, 0.00, 0),
+ ( 563.00, 0.00, 10),
+ ( 1803.00, 40.50, 12),
+ ( 3667.00, 189.30, 22),
+ ( 6855.00, 599.38, 24),
+ ( 8663.00, 1364.50, 32),
+ ( 21421.00, 1943.06, 35),
+ ( 'inf', 6408.36, 37),
+ ],
+ 'monthly': [
+ ( 317.00, 0.00, 0),
+ ( 1125.00, 0.00, 10),
+ ( 3606.00, 80.80, 12),
+ ( 7333.00, 378.52, 22),
+ ( 13710.00, 1198.46, 24),
+ ( 17325.00, 2728.94, 32),
+ ( 42842.00, 3885.74, 35),
+ ( 'inf', 12816.69, 37),
+ ],
+ 'quarterly': [
+ ( 950.00, 0.00, 0),
+ ( 3375.00, 0.00, 10),
+ ( 10819.00, 242.50, 12),
+ ( 22000.00, 1135.78, 22),
+ ( 41131.00, 3595.60, 24),
+ ( 51975.00, 8187.04, 32),
+ ( 128525.00, 11657.12, 35),
+ ( 'inf', 38449.62, 37),
+ ],
+ 'semi-annually': [
+ ( 1900.00, 0.00, 0),
+ ( 6750.00, 0.00, 10),
+ ( 21638.00, 485.00, 12),
+ ( 44000.00, 2271.56, 22),
+ ( 82263.00, 7191.20, 24),
+ ( 103950.00, 16374.32, 32),
+ ( 257050.00, 23314.16, 35),
+ ( 'inf', 76899.16, 37),
+ ],
+ 'annually': [
+ ( 3800.00, 0.00, 0),
+ ( 13500.00, 0.00, 10),
+ ( 43275.00, 970.00, 12),
+ ( 88000.00, 4543.00, 22),
+ ( 164525.00, 14382.50, 24),
+ ( 207900.00, 32748.50, 32),
+ ( 514100.00, 46628.50, 35),
+ ( 'inf', 153798.50, 37),
+ ],
+ }
+
+
+
+
+
+ {
+ 'weekly': [
+ ( 73.00, 0.00, 0),
+ ( 260.00, 0.00, 10),
+ ( 832.00, 18.70, 12),
+ ( 1692.00, 87.34, 22),
+ ( 3164.00, 276.54, 24),
+ ( 3998.00, 629.82, 32),
+ ( 9887.00, 896.70, 35),
+ ( 'inf', 2957.85, 37),
+ ],
+ 'bi-weekly': [
+ ( 146.00, 0.00, 0),
+ ( 519.00, 0.00, 10),
+ ( 1664.00, 37.30, 12),
+ ( 3385.00, 174.70, 22),
+ ( 6328.00, 553.32, 24),
+ ( 7996.00, 1259.64, 32),
+ ( 19773.00, 1793.40, 35),
+ ( 'inf', 5915.35, 37),
+ ],
+ 'semi-monthly': [
+ ( 158.00, 0.00, 0),
+ ( 563.00, 0.00, 10),
+ ( 1803.00, 40.50, 12),
+ ( 3667.00, 189.30, 22),
+ ( 6855.00, 599.38, 24),
+ ( 8663.00, 1364.50, 32),
+ ( 21421.00, 1943.06, 35),
+ ( 'inf', 6408.36, 37),
+ ],
+ 'monthly': [
+ ( 317.00, 0.00, 0),
+ ( 1125.00, 0.00, 10),
+ ( 3606.00, 80.80, 12),
+ ( 7333.00, 378.52, 22),
+ ( 13710.00, 1198.46, 24),
+ ( 17325.00, 2728.94, 32),
+ ( 42842.00, 3885.74, 35),
+ ( 'inf', 12816.69, 37),
+ ],
+ 'quarterly': [
+ ( 950.00, 0.00, 0),
+ ( 3375.00, 0.00, 10),
+ ( 10819.00, 242.50, 12),
+ ( 22000.00, 1135.78, 22),
+ ( 41131.00, 3595.60, 24),
+ ( 51975.00, 8187.04, 32),
+ ( 128525.00, 11657.12, 35),
+ ( 'inf', 38449.62, 37),
+ ],
+ 'semi-annually': [
+ ( 1900.00, 0.00, 0),
+ ( 6750.00, 0.00, 10),
+ ( 21638.00, 485.00, 12),
+ ( 44000.00, 2271.56, 22),
+ ( 82263.00, 7191.20, 24),
+ ( 103950.00, 16374.32, 32),
+ ( 257050.00, 23314.16, 35),
+ ( 'inf', 76899.16, 37),
+ ],
+ 'annually': [
+ ( 3800.00, 0.00, 0),
+ ( 13500.00, 0.00, 10),
+ ( 43275.00, 970.00, 12),
+ ( 88000.00, 4543.00, 22),
+ ( 164525.00, 14382.50, 24),
+ ( 207900.00, 32748.50, 32),
+ ( 514100.00, 46628.50, 35),
+ ( 'inf', 153798.50, 37),
+ ],
+ }
+
+
+
+
+
+
+ {
+ 'standard': [
+ ( 0.00, 0.00, 0.00),
+ ( 3800.00, 0.00, 0.10),
+ ( 13675.00, 987.50, 0.12),
+ ( 43925.00, 4617.50, 0.22),
+ ( 89325.00, 14605.50, 0.24),
+ ( 167100.00, 33271.50, 0.32),
+ ( 211150.00, 47367.50, 0.35),
+ ( 522200.00, 156235.00, 0.37),
+ ],
+ 'higher': [
+ ( 0.00, 0.00, 0.00),
+ ( 6200.00, 0.00, 0.10),
+ ( 11138.00, 493.75, 0.12),
+ ( 26263.00, 2308.75, 0.22),
+ ( 48963.00, 7302.75, 0.24),
+ ( 87850.00, 16635.75, 0.32),
+ ( 109875.00, 23683.75, 0.35),
+ ( 265400.00, 78117.50, 0.37),
+ ],
+ }
+
+
+
+
+
+
+ Federal 941 FIT Table Married
+ fed_941_fit_table_married
+
+
+
+
+
+ {
+ 'weekly': [
+ ( 227.00, 0.00, 0),
+ ( 600.00, 0.00, 10),
+ ( 1745.00, 37.30, 12),
+ ( 3465.00, 174.70, 22),
+ ( 6409.00, 553.10, 24),
+ ( 8077.00, 1259.66, 32),
+ ( 12003.00, 1793.42, 35),
+ ( 'inf', 3167.52, 37),
+ ],
+ 'bi-weekly': [
+ ( 454.00, 0.00, 0),
+ ( 1200.00, 0.00, 10),
+ ( 3490.00, 74.60, 12),
+ ( 6931.00, 349.40, 22),
+ ( 12817.00, 1106.42, 24),
+ ( 16154.00, 2519.06, 32),
+ ( 24006.00, 3586.90, 35),
+ ( 'inf', 6335.10, 37),
+ ],
+ 'semi-monthly': [
+ ( 492.00, 0.00, 0),
+ ( 1300.00, 0.00, 10),
+ ( 3781.00, 80.80, 12),
+ ( 7508.00, 378.52, 22),
+ ( 13885.00, 1198.46, 24),
+ ( 17500.00, 2728.94, 32),
+ ( 26006.00, 3885.74, 35),
+ ( 'inf', 6862.84, 37),
+ ],
+ 'monthly': [
+ ( 983.00, 0.00, 0),
+ ( 2600.00, 0.00, 10),
+ ( 7563.00, 161.70, 12),
+ ( 15017.00, 757.26, 22),
+ ( 27771.00, 2397.14, 24),
+ ( 35000.00, 5458.10, 32),
+ ( 52013.00, 7771.38, 35),
+ ( 'inf', 13725.93, 37),
+ ],
+ 'quarterly': [
+ ( 2950.00, 0.00, 0),
+ ( 7800.00, 0.00, 10),
+ ( 22688.00, 485.00, 12),
+ ( 45050.00, 2271.56, 22),
+ ( 83313.00, 7191.20, 24),
+ ( 105000.00, 16374.32, 32),
+ ( 156038.00, 23314.16, 35),
+ ( 'inf', 41177.46, 37),
+ ],
+ 'semi-annually': [
+ ( 5900.00, 0.00, 0),
+ ( 15600.00, 0.00, 10),
+ ( 45375.00, 970.00, 12),
+ ( 90100.00, 4543.00, 22),
+ ( 166625.00, 14382.50, 24),
+ ( 210000.00, 32748.50, 32),
+ ( 312075.00, 46628.50, 35),
+ ( 'inf', 82354.75, 37),
+ ],
+ 'annually': [
+ ( 11800.00, 0.00, 0),
+ ( 31200.00, 0.00, 10),
+ ( 90750.00, 1940.00, 12),
+ ( 180200.00, 9086.00, 22),
+ ( 333250.00, 28765.00, 24),
+ ( 420000.00, 65497.00, 32),
+ ( 624150.00, 93257.00, 35),
+ ( 'inf', 164709.50, 37),
+ ],
+ }
+
+
+
+
+
+ {
+ 'weekly': [
+ ( 227.00, 0.00, 0),
+ ( 600.00, 0.00, 10),
+ ( 1745.00, 37.30, 12),
+ ( 3465.00, 174.70, 22),
+ ( 6409.00, 553.10, 24),
+ ( 8077.00, 1259.66, 32),
+ ( 12003.00, 1793.42, 35),
+ ( 'inf', 3167.52, 37),
+ ],
+ 'bi-weekly': [
+ ( 454.00, 0.00, 0),
+ ( 1200.00, 0.00, 10),
+ ( 3490.00, 74.60, 12),
+ ( 6931.00, 349.40, 22),
+ ( 12817.00, 1106.42, 24),
+ ( 16154.00, 2519.06, 32),
+ ( 24006.00, 3586.90, 35),
+ ( 'inf', 6335.10, 37),
+ ],
+ 'semi-monthly': [
+ ( 492.00, 0.00, 0),
+ ( 1300.00, 0.00, 10),
+ ( 3781.00, 80.80, 12),
+ ( 7508.00, 378.52, 22),
+ ( 13885.00, 1198.46, 24),
+ ( 17500.00, 2728.94, 32),
+ ( 26006.00, 3885.74, 35),
+ ( 'inf', 6862.84, 37),
+ ],
+ 'monthly': [
+ ( 983.00, 0.00, 0),
+ ( 2600.00, 0.00, 10),
+ ( 7563.00, 161.70, 12),
+ ( 15017.00, 757.26, 22),
+ ( 27771.00, 2397.14, 24),
+ ( 35000.00, 5458.10, 32),
+ ( 52013.00, 7771.38, 35),
+ ( 'inf', 13725.93, 37),
+ ],
+ 'quarterly': [
+ ( 2950.00, 0.00, 0),
+ ( 7800.00, 0.00, 10),
+ ( 22688.00, 485.00, 12),
+ ( 45050.00, 2271.56, 22),
+ ( 83313.00, 7191.20, 24),
+ ( 105000.00, 16374.32, 32),
+ ( 156038.00, 23314.16, 35),
+ ( 'inf', 41177.46, 37),
+ ],
+ 'semi-annually': [
+ ( 5900.00, 0.00, 0),
+ ( 15600.00, 0.00, 10),
+ ( 45375.00, 970.00, 12),
+ ( 90100.00, 4543.00, 22),
+ ( 166625.00, 14382.50, 24),
+ ( 210000.00, 32748.50, 32),
+ ( 312075.00, 46628.50, 35),
+ ( 'inf', 82354.75, 37),
+ ],
+ 'annually': [
+ ( 11800.00, 0.00, 0),
+ ( 31200.00, 0.00, 10),
+ ( 90750.00, 1940.00, 12),
+ ( 180200.00, 9086.00, 22),
+ ( 333250.00, 28765.00, 24),
+ ( 420000.00, 65497.00, 32),
+ ( 624150.00, 93257.00, 35),
+ ( 'inf', 164709.50, 37),
+ ],
+ }
+
+
+
+
+
+
+ {
+ 'standard': [
+ ( 0.00, 0.00, 0.00),
+ ( 11900.00, 0.00, 0.10),
+ ( 31650.00, 1975.00, 0.12),
+ ( 92150.00, 9235.00, 0.22),
+ ( 182950.00, 29211.00, 0.24),
+ ( 338500.00, 66543.00, 0.32),
+ ( 426600.00, 94735.00, 0.35),
+ ( 633950.00, 167307.50, 0.37),
+ ],
+ 'higher': [
+ ( 0.00, 0.00, 0.00),
+ ( 12400.00, 0.00, 0.10),
+ ( 22275.00, 987.50, 0.12),
+ ( 52525.00, 4617.50, 0.22),
+ ( 97925.00, 14605.50, 0.24),
+ ( 175700.00, 33271.50, 0.32),
+ ( 219750.00, 47367.50, 0.35),
+ ( 323425.00, 83653.75, 0.37),
+ ],
+ }
+
+
+
+
+
+ Federal 941 FIT Table Head of Household
+ fed_941_fit_table_hh
+
+
+
+
+
+ {
+ 'standard': [
+ ( 0.00, 0.00, 0.00),
+ ( 10050.00, 0.00, 0.10),
+ ( 24150.00, 1410.00, 0.12),
+ ( 63750.00, 6162.00, 0.22),
+ ( 95550.00, 13158.00, 0.24),
+ ( 173350.00, 31830.00, 0.32),
+ ( 217400.00, 45926.00, 0.35),
+ ( 528450.00, 154793.50, 0.37),
+ ],
+ 'higher': [
+ ( 0.00, 0.00, 0.00),
+ ( 9325.00, 0.00, 0.10),
+ ( 16375.00, 705.00, 0.12),
+ ( 36175.00, 3081.00, 0.22),
+ ( 52075.00, 6579.00, 0.24),
+ ( 90975.00, 15915.00, 0.32),
+ ( 113000.00, 22963.00, 0.35),
+ ( 268525.00, 77396.75, 0.37),
+ ],
+ }
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml b/l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
new file mode 100644
index 00000000..a7751adf
--- /dev/null
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ WAGE: Federal 941 Income Tax Exempt
+ WAGE_US_941_FIT_EXEMPT
+
+
+
+ EE: Federal 941 Income Tax Withholding
+ EE_US_941_FIT
+
+
+
+
+
+
+
+ EE: US Federal Income Tax Withholding
+ EE_US_941_FIT
+ python
+ result, _ = ee_us_941_fit(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ee_us_941_fit(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/integration_rules.xml b/l10n_us_hr_payroll/data/integration_rules.xml
new file mode 100644
index 00000000..9991a3aa
--- /dev/null
+++ b/l10n_us_hr_payroll/data/integration_rules.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ python
+ result = inputs.COMMISSION.amount > 0.0 if inputs.COMMISSION else False
+ code
+ result = inputs.COMMISSION.amount if inputs.COMMISSION else 0
+ BASIC_COM
+
+ Commissions
+
+
+
+
+
+
+ python
+ result = inputs.BADGES.amount > 0.0 if inputs.BADGES else False
+ code
+ result = inputs.BADGES.amount if inputs.BADGES else 0
+ BASIC_BADGES
+
+ Badges
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/models/__init__.py b/l10n_us_hr_payroll/models/__init__.py
new file mode 100644
index 00000000..c6d607ff
--- /dev/null
+++ b/l10n_us_hr_payroll/models/__init__.py
@@ -0,0 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from . import hr_contract
+from . import hr_payslip
+from . import us_payroll_config
diff --git a/l10n_us_hr_payroll/models/federal/__init__.py b/l10n_us_hr_payroll/models/federal/__init__.py
new file mode 100644
index 00000000..0358305d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/federal/__init__.py
@@ -0,0 +1 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
diff --git a/l10n_us_hr_payroll/models/federal/fed_940.py b/l10n_us_hr_payroll/models/federal/fed_940.py
new file mode 100644
index 00000000..1cf042c7
--- /dev/null
+++ b/l10n_us_hr_payroll/models/federal/fed_940.py
@@ -0,0 +1,37 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+def er_us_940_futa(payslip, categories, worked_days, inputs):
+ """
+ Returns FUTA eligible wage and rate.
+ WAGE = GROSS - WAGE_US_940_FUTA_EXEMPT
+ :return: result, result_rate (wage, percent)
+ """
+
+ # Determine Rate.
+ if payslip.contract_id.futa_type == payslip.contract_id.FUTA_TYPE_EXEMPT:
+ # Exit early
+ return 0.0, 0.0
+ elif payslip.contract_id.futa_type == payslip.contract_id.FUTA_TYPE_BASIC:
+ result_rate = -payslip.rule_parameter('fed_940_futa_rate_basic')
+ else:
+ result_rate = -payslip.rule_parameter('fed_940_futa_rate_normal')
+
+ # Determine Wage
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage -= payslip.sum_category('WAGE_US_940_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage += payslip.contract_id.external_wages
+
+ wage_base = payslip.rule_parameter('fed_940_futa_wage_base')
+ remaining = wage_base - ytd_wage
+
+ wage = categories.GROSS - categories.WAGE_US_940_FUTA_EXEMPT
+
+ if remaining < 0.0:
+ result = 0.0
+ elif remaining < wage:
+ result = remaining
+ else:
+ result = wage
+
+ return result, result_rate
diff --git a/l10n_us_hr_payroll/models/federal/fed_941.py b/l10n_us_hr_payroll/models/federal/fed_941.py
new file mode 100644
index 00000000..e6288f88
--- /dev/null
+++ b/l10n_us_hr_payroll/models/federal/fed_941.py
@@ -0,0 +1,239 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+# import logging
+# _logger = logging.getLogger(__name__)
+
+
+def ee_us_941_fica_ss(payslip, categories, worked_days, inputs):
+ """
+ Returns FICA Social Security eligible wage and rate.
+ WAGE = GROSS - WAGE_US_941_FICA_EXEMPT
+ :return: result, result_rate (wage, percent)
+ """
+ exempt = payslip.contract_id.us_payroll_config_value('fed_941_fica_exempt')
+ if exempt:
+ return 0.0, 0.0
+
+ # Determine Rate.
+ result_rate = -payslip.rule_parameter('fed_941_fica_ss_rate')
+
+ # Determine Wage
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage -= payslip.sum_category('WAGE_US_941_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage += payslip.contract_id.external_wages
+
+ wage_base = payslip.rule_parameter('fed_941_fica_ss_wage_base')
+ remaining = wage_base - ytd_wage
+
+ wage = categories.GROSS - categories.WAGE_US_941_FICA_EXEMPT
+
+ if remaining < 0.0:
+ result = 0.0
+ elif remaining < wage:
+ result = remaining
+ else:
+ result = wage
+
+ return result, result_rate
+
+
+er_us_941_fica_ss = ee_us_941_fica_ss
+
+
+def ee_us_941_fica_m(payslip, categories, worked_days, inputs):
+ """
+ Returns FICA Medicare eligible wage and rate.
+ WAGE = GROSS - WAGE_US_941_FICA_EXEMPT
+ :return: result, result_rate (wage, percent)
+ """
+ exempt = payslip.contract_id.us_payroll_config_value('fed_941_fica_exempt')
+ if exempt:
+ return 0.0, 0.0
+
+ # Determine Rate.
+ result_rate = -payslip.rule_parameter('fed_941_fica_m_rate')
+
+ # Determine Wage
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage -= payslip.sum_category('WAGE_US_941_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage += payslip.contract_id.external_wages
+
+ wage_base = float(payslip.rule_parameter('fed_941_fica_m_wage_base')) # inf
+ remaining = wage_base - ytd_wage
+
+ wage = categories.GROSS - categories.WAGE_US_941_FICA_EXEMPT
+
+ if remaining < 0.0:
+ result = 0.0
+ elif remaining < wage:
+ result = remaining
+ else:
+ result = wage
+
+ return result, result_rate
+
+
+er_us_941_fica_m = ee_us_941_fica_m
+
+
+def ee_us_941_fica_m_add(payslip, categories, worked_days, inputs):
+ """
+ Returns FICA Medicare Additional eligible wage and rate.
+ Note that this wage is not capped like the above rules.
+ WAGE = GROSS - WAGE_FICA_EXEMPT
+ :return: result, result_rate (wage, percent)
+ """
+ exempt = payslip.contract_id.us_payroll_config_value('fed_941_fica_exempt')
+ if exempt:
+ return 0.0, 0.0
+
+ # Determine Rate.
+ result_rate = -payslip.rule_parameter('fed_941_fica_m_add_rate')
+
+ # Determine Wage
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage -= payslip.sum_category('WAGE_US_941_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+ ytd_wage += payslip.contract_id.external_wages
+
+ wage_start = payslip.rule_parameter('fed_941_fica_m_add_wage_start')
+ existing_wage = ytd_wage - wage_start
+
+ wage = categories.GROSS - categories.WAGE_US_941_FICA_EXEMPT
+
+ if existing_wage >= 0.0:
+ result = wage
+ elif wage + existing_wage > 0.0:
+ result = wage + existing_wage
+ else:
+ result = 0.0
+
+ return result, result_rate
+
+
+# Federal Income Tax
+def ee_us_941_fit(payslip, categories, worked_days, inputs):
+ """
+ Returns Wage and rate that is computed given the amount to withhold.
+ WAGE = GROSS - WAGE_US_941_FIT_EXEMPT
+ :return: result, result_rate (wage, percent)
+ """
+ filing_status = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ wage = categories.GROSS - categories.WAGE_US_941_FIT_EXEMPT
+ #_logger.warn('initial gross wage: ' + str(wage))
+ year = payslip.dict.get_year()
+ if year >= 2020:
+ # Large changes in Federal Income Tax in 2020 and the W4
+ # We will assume that your W4 is the 2020 version
+ # Steps are from IRS Publication 15-T
+ #
+ # Step 1
+ working_wage = wage
+ is_nra = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_is_nonresident_alien')
+ if is_nra:
+ nra_table = payslip.rule_parameter('fed_941_fit_nra_additional')
+ working_wage += nra_table.get(schedule_pay, 0.0)
+ #_logger.warn(' is_nrm after wage: ' + str(working_wage))
+
+ pay_periods = payslip.dict.get_pay_periods_in_year()
+ wage_annual = pay_periods * working_wage
+ #_logger.warn('annual wage: ' + str(wage_annual))
+ wage_annual += payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_other_income')
+ #_logger.warn(' after other income: ' + str(wage_annual))
+
+ deductions = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_deductions')
+ #_logger.warn('deductions from W4: ' + str(deductions))
+
+ higher_rate_type = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_multiple_jobs_higher')
+ if not higher_rate_type:
+ deductions += 12900.0 if filing_status == 'married' else 8600.0
+ #_logger.warn(' deductions after standard deduction: ' + str(deductions))
+
+ adjusted_wage_annual = wage_annual - deductions
+ if adjusted_wage_annual < 0.0:
+ adjusted_wage_annual = 0.0
+ #_logger.warn('adusted annual wage: ' + str(adjusted_wage_annual))
+
+ # Step 2
+ if filing_status == 'single':
+ tax_tables = payslip.rule_parameter('fed_941_fit_table_single')
+ elif filing_status == 'married':
+ tax_tables = payslip.rule_parameter('fed_941_fit_table_married')
+ else:
+ # married_as_single for historic reasons
+ tax_tables = payslip.rule_parameter('fed_941_fit_table_hh')
+
+ if higher_rate_type:
+ tax_table = tax_tables['higher']
+ else:
+ tax_table = tax_tables['standard']
+
+ selected_row = None
+ for row in tax_table:
+ if row[0] <= adjusted_wage_annual:
+ selected_row = row
+ else:
+ # First row where wage is higher than adjusted_wage_annual
+ break
+
+ wage_threshold, base_withholding_amount, marginal_rate = selected_row
+ #_logger.warn(' selected row: ' + str(selected_row))
+ working_wage = adjusted_wage_annual - wage_threshold
+ tentative_withholding_amount = (working_wage * marginal_rate) + base_withholding_amount
+ tentative_withholding_amount = tentative_withholding_amount / pay_periods
+ #_logger.warn('tenative withholding amount: ' + str(tentative_withholding_amount))
+
+ # Step 3
+ dependent_credit = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_dependent_credit')
+ dependent_credit = dependent_credit / pay_periods
+ #_logger.warn('dependent credit (per period): ' + str(dependent_credit))
+ tentative_withholding_amount -= dependent_credit
+ if tentative_withholding_amount < 0.0:
+ tentative_withholding_amount = 0.0
+
+ # Step 4
+ withholding_amount = tentative_withholding_amount + payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_additional_withholding')
+ #_logger.warn('final withholding amount: ' + str(withholding_amount))
+ # Ideally we would set the 'taxable wage' as the result and compute the percentage tax.
+ # This is off by 1 penny across our tests, but I feel like it is worth it for the added reporting.
+ # - Jared Kipe 2019 during Odoo 13.0 rewrite.
+ #
+ # return -withholding_amount, 100.0
+ return wage, -(withholding_amount / wage * 100.0)
+ else:
+ working_wage = wage
+ is_nra = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_is_nonresident_alien')
+ if is_nra:
+ nra_table = payslip.rule_parameter('fed_941_fit_nra_additional')
+ working_wage += nra_table[schedule_pay]
+
+ allowance_table = payslip.rule_parameter('fed_941_fit_allowance')
+ allowances = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_allowances')
+ working_wage -= allowance_table[schedule_pay] * allowances
+ tax = 0.0
+ last_limit = 0.0
+ if filing_status == 'married':
+ tax_table = payslip.rule_parameter('fed_941_fit_table_married')
+ else:
+ tax_table = payslip.rule_parameter('fed_941_fit_table_single')
+ for row in tax_table[schedule_pay]:
+ limit, base, percent = row
+ limit = float(limit) # 'inf'
+ if working_wage <= limit:
+ tax = base + ((working_wage - last_limit) * (percent / 100.0))
+ break
+ last_limit = limit
+
+ tax += payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_additional_withholding')
+ # Ideally we would set the 'taxable wage' as the result and compute the percentage tax.
+ # This is off by 1 penny across our tests, but I feel like it is worth it for the added reporting.
+ # - Jared Kipe 2019 during Odoo 13.0 rewrite.
+ #
+ # return -tax, 100.0
+ return wage, -(tax / wage * 100.0)
diff --git a/l10n_us_hr_payroll/models/hr_contract.py b/l10n_us_hr_payroll/models/hr_contract.py
new file mode 100644
index 00000000..46bc42b7
--- /dev/null
+++ b/l10n_us_hr_payroll/models/hr_contract.py
@@ -0,0 +1,28 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import api, fields, models
+from .us_payroll_config import FUTA_TYPE_NORMAL, \
+ FUTA_TYPE_BASIC, \
+ FUTA_TYPE_EXEMPT
+
+
+class HrPayrollStructure(models.Model):
+ _inherit = 'hr.payroll.structure'
+ schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
+
+
+class USHRContract(models.Model):
+ _inherit = 'hr.contract'
+
+ FUTA_TYPE_NORMAL = FUTA_TYPE_NORMAL
+ FUTA_TYPE_BASIC = FUTA_TYPE_BASIC
+ FUTA_TYPE_EXEMPT = FUTA_TYPE_EXEMPT
+
+ us_payroll_config_id = fields.Many2one('hr.contract.us_payroll_config', 'Payroll Forms')
+ external_wages = fields.Float(string='External Existing Wages')
+
+ # Simplified fields for easier rules, state code will exempt based on contract's futa_type
+ futa_type = fields.Selection(related='us_payroll_config_id.fed_940_type')
+
+ def us_payroll_config_value(self, name):
+ return self.us_payroll_config_id[name]
diff --git a/l10n_us_hr_payroll/models/hr_payslip.py b/l10n_us_hr_payroll/models/hr_payslip.py
new file mode 100644
index 00000000..92a1e0fd
--- /dev/null
+++ b/l10n_us_hr_payroll/models/hr_payslip.py
@@ -0,0 +1,48 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+from .federal.fed_940 import er_us_940_futa
+from .federal.fed_941 import ee_us_941_fica_ss, \
+ ee_us_941_fica_m, \
+ ee_us_941_fica_m_add,\
+ er_us_941_fica_ss, \
+ er_us_941_fica_m, \
+ ee_us_941_fit
+
+
+class HRPayslip(models.Model):
+ _inherit = 'hr.payslip'
+
+ # From IRS Publication 15-T or logically (annually, bi-monthly)
+ PAY_PERIODS_IN_YEAR = {
+ 'annually': 1,
+ 'semi-annually': 2,
+ 'quarterly': 4,
+ 'bi-monthly': 6,
+ 'monthly': 12,
+ 'semi-monthly': 24,
+ 'bi-weekly': 26,
+ 'weekly': 52,
+ 'daily': 260,
+ }
+
+ def _get_base_local_dict(self):
+ res = super()._get_base_local_dict()
+ res.update({
+ 'er_us_940_futa': er_us_940_futa,
+ 'ee_us_941_fica_ss': ee_us_941_fica_ss,
+ 'ee_us_941_fica_m': ee_us_941_fica_m,
+ 'ee_us_941_fica_m_add': ee_us_941_fica_m_add,
+ 'er_us_941_fica_ss': er_us_941_fica_ss,
+ 'er_us_941_fica_m': er_us_941_fica_m,
+ 'ee_us_941_fit': ee_us_941_fit,
+ })
+ return res
+
+ def get_year(self):
+ # Helper method to get the year (normalized between Odoo Versions)
+ return self.date_to.year
+
+ def get_pay_periods_in_year(self):
+ return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)
diff --git a/l10n_us_hr_payroll/models/us_payroll_config.py b/l10n_us_hr_payroll/models/us_payroll_config.py
new file mode 100644
index 00000000..ed0e6d73
--- /dev/null
+++ b/l10n_us_hr_payroll/models/us_payroll_config.py
@@ -0,0 +1,45 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+FUTA_TYPE_EXEMPT = 'exempt'
+FUTA_TYPE_BASIC = 'basic'
+FUTA_TYPE_NORMAL = 'normal'
+
+
+class HRContractUSPayrollConfig(models.Model):
+ _name = 'hr.contract.us_payroll_config'
+ _description = 'Contract US Payroll Forms'
+
+ name = fields.Char(string="Description")
+ employee_id = fields.Many2one('hr.employee', string="Employee", required=True)
+ state_id = fields.Many2one('res.country.state', string="Applied State")
+
+ fed_940_type = fields.Selection([
+ (FUTA_TYPE_EXEMPT, 'Exempt (0%)'),
+ (FUTA_TYPE_NORMAL, 'Normal Net Rate (0.6%)'),
+ (FUTA_TYPE_BASIC, 'Basic Rate (6%)'),
+ ], string="Federal Unemployment Tax Type (FUTA)", default='normal')
+
+ fed_941_fica_exempt = fields.Boolean(string='FICA Exempt', help="Exempt from Social Security and "
+ "Medicare e.g. F1 Student Visa")
+
+ fed_941_fit_w4_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single or Married filing separately'),
+ ('married', 'Married filing jointly'),
+ ('married_as_single', 'Head of Household'),
+ ], string='Federal W4 Filing Status [1(c)]', default='single')
+ fed_941_fit_w4_allowances = fields.Integer(string='Federal W4 Allowances (before 2020)')
+ fed_941_fit_w4_is_nonresident_alien = fields.Boolean(string='Federal W4 Is Nonresident Alien')
+ fed_941_fit_w4_multiple_jobs_higher = fields.Boolean(string='Federal W4 Multiple Jobs Higher [2(c)]',
+ help='Form W4 (2020+) 2(c) Checkbox. '
+ 'Uses Higher Withholding tables.')
+ fed_941_fit_w4_dependent_credit = fields.Float(string='Federal W4 Dependent Credit [3]',
+ help='Form W4 (2020+) Line 3')
+ fed_941_fit_w4_other_income = fields.Float(string='Federal W4 Other Income [4(a)]',
+ help='Form W4 (2020+) 4(a)')
+ fed_941_fit_w4_deductions = fields.Float(string='Federal W4 Deductions [4(b)]',
+ help='Form W4 (2020+) 4(b)')
+ fed_941_fit_w4_additional_withholding = fields.Float(string='Federal W4 Additional Withholding [4(c)]',
+ help='Form W4 (2020+) 4(c)')
diff --git a/l10n_us_hr_payroll/security/ir.model.access.csv b/l10n_us_hr_payroll/security/ir.model.access.csv
new file mode 100644
index 00000000..67a8fa2a
--- /dev/null
+++ b/l10n_us_hr_payroll/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_contract_us_payroll_config,hr.contract.us_payroll_config,model_hr_contract_us_payroll_config,hr_payroll.group_hr_payroll_manager,1,1,1,1
diff --git a/l10n_us_hr_payroll/static/description/icon.png b/l10n_us_hr_payroll/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a58a38132bd0b9a86324caa86d0cc6fc145fd4c
GIT binary patch
literal 8776
zcmai4XH-+$wx)Nb3DQeYst|e!z1K(=klqOd2qg3>M3AafrFRgJ04g9IK|p%%(gXyg
z_bR-2&bjy8JMMk&{n*J~Ykuq7-<)&JxyBx2N9jCPy+cGtgn@x^M_o-x5B;z6`y#+Y
z|HhLaR$yS@yn`4RBaFdXAX|4=K5IL78+$%qR~XukfgvU93$wO$u}84j*gHbpqyc-4
ztpFB?oixBm1k4YHDcUb6YyB9(l06^PW{vt;}9R6l^ga73|IuO9$7N7thKk#4S_P!7YW#fO!v4y~_`J7?E
z-_!qrwe~{0{g>{4WBw-^jrb4t|7Xd+ib2QtzoUJvVgI294F-e%Z@8=Lzo~&El)cf>
z`fncm;{k93KbSpG&mQjX}~IcVEaSi@8E&dU9BDMwd~y-5l+$oaej1ie+T|=
zlM%%3KP|t>pkBXIX6Ob%KVI5LYx7{p^3y`?uNTznRf-1)<3y4t`432z#_%egO$y0Rdiq5d#5n
z5Wg@;n4gy)eehcy9oSCAndK0u0urL4yn=iJ7#N%h
zUI{}yO1k&DW$_Z2)D`2pF|1hn8?wAGmzB
zYMh_-%9AejE`s%`T$=@=lEG#
zoW>{R(K#H!mO$7$d&NuT-E=xv)0=ZO_49sG1Qu0IyJB>4M!Ri0WqGl~Q7WuFF$zeO
zL*TRJDZfl_sx5vEh0iW)XPg)dlc3nG-5ZdU58v4#42><~wFgj~t!LGiqp$kG+Un5z
z?j0<>oUBc;LJDtKf=GH9zG#Osndq{&o^gaE+V>dRO@1x-pp;I+KA(?LJ1P8G4(r?CkME}LzMVl)(7*9^I8pyQyq29DMkz%{&-;YjrC;hes
z^Vc1xn1j_~W(V-1x{80ru=ljdvJt29;|iYMLF
zUG(p(;&?Sv-%ZTVKAr!0vU3&SQ*p5(Q`5jZL*WbA%nTeV9jn}{xSTKB`(-#$8GKUC
zWBjY_wkxS$k=!vjrzIHWLR@*vy+$RUW4_YYLw=db%|v`?rZ613NQC1Rky?|~e^0$I
zMomr^r!T-%_ul88QsMY5F@Xh!qLPBBf)&ED;yZGjcKqXuU5GL{Zqq~Da-zN~(|xI)
zo=xFw4stow0Llumb^PN838{fK{^o)@`wz@3;;s+&n!@WAtEgFV)YONEFTOU49UYu4
zs&P8?y9^E(ksfd`w}#tG^WKPXZSKV%)oU;GfHUxdO7$p+=mM{Qi5({RxU=WFusl>C
zaBB?~3AvW^86Kg?s6?)-?>A`*LI~8>Gy<>k?$hK9=0b?7osbHpz1lf4$UK)fsfwEL
zWQ~c{wgfJ2kr<^ixkuCr8fwKcfm@%m7$hWAzYY^bV%_5rVOj{r=<|k|y`1#Ley*1?
zkKe8C5M2njpG~(#G_AN@0xt7)m-ND{POyU~w&*;!ei%0G4aQxc)YS@kLh(Y^nk%lC
zyWLiL)>V9+u7V3WqLA2uKW|Ji&M#&6a?MbAZ;8&ny^o(mnGRUqaGr?ZH`#jwlI@lpyW;1iW<522u4|0WE-$c}@3V;=x>tXx~7MXi=Ra43H
z(7feOOaufpbl5=^3?NY4)+ZL6?Ddhw39WkPWq&4AvRZdRkwAOz!V_V`&dltUbH~=O
z9TVU0qvP_Qf8rMwa|!jru^=B4V~NyZKv(~3Ev7P4&z`s(bCaGHi_*G8!E
zSR}{8D;0JDl92bCJ5U>YOZuTb`i>K+!ehb~w_h0e@mfj!sr-l(o0-B7Prvtlh}^6x
ze|<``@rh~1j27u$9C*Gk5DPAn5EmqDi?-kSJ(N0Mm5GlX&@EMKUuN-ft0#G|9dvPT
z#*7eY)sZf<8#L#y()~kRAREJK=BLGO$dt(SBEFC3VJUVp=DlY&8#ws>1S?CtRM?f2
zVH2ck=K3ZhQySd*3Y<4@L}jw_N?DO)VTjr!NW@Pf$;lUJh+Q@uolGWY<}>Y8vQWr^M+clXt+oB-1B`GK#R2KXXU
z>e>O6IU!4Y+2@@Ei~jq5dOcFhPA?W4_VC)}vu-cNUcz8@6nWggGwiv@NPjUp71I7S
zfr~6`LUI0jbFU?dS*kXYS*G@Ny;63B@teiQ5aN`wPNSBCHlfaggi#lyQVXESBd9eK>JM;lO{@%Y#4qS}#N60}ER)Lk^T-C=5XjDWU~Kv+
zwaYV|FAex+ukPKfLAa?X^9UB|?HS!0z2CIngv_PA(ZEhLpMJCq921^t)qG3~SMwvIbKyv->5+tVh#sfYYL_IP!Owh0r)L*rKE;
zZ2zmUBx$CSMii>_o7a*3MtW~8BfbDHeyCT6v{}d+hHT*VLy9y2#?ArW0{kjv8w5*i
z2IgP|DqimLzP6R*dwG5R%3TO6V~ThEaM`WpcG@Bgu6`pW^0V;+^1U!g(_<$!f(^Q@
zh=8xFJktSYa`~J@*v%ElaLgOmt6=CHCLv>m03ZF&9|;hU%=DpASVWM
z!-P>W9aKvoZDjm_4(AjBE<$gHBD`OzulQAuA4|mCU&Ys06iDj8Eu({mrLa1%o1rp~y3CU2J%awa!
zg@gu3|3MPh=+sO`rs7(@SMsVT#hwbv=wW)aV+7BRZ1k382hFYm-
zwfq_VE8;X!Rc8Tq0jQNcJEC#b<6o?fbaKu@|L50KB?}9_Xzh@
zTQBY^$Y%j>e`#}3a}yXYFU(CIZS76B<^0-Vi)G5d#4LZ}eQ<%l`yG9RtbOJf!`7gjT|xJJxDmDdyCR=g%EbCOr?-C1$z7~4fB&H>BH8ZHo@urv
z$9ARX0%f0mSJIoA^{q>CdCLW>`OA@N38p|#=F$d2#d)@0OG#HW5&~O|4bMC$R!g%A
z#-WkvknrO^W}2*ghgtMVNykW9sn>~am?*Iyy#X!0#d`$edx=Yk?B{;CEDs4hZVtVC
z6^7T|TnAf+Jswi9$fx7%(th|Fy04ocypqRfh`WG6zy%x`xdJ?MD0#1G9;r!ZGvvyA
z<#Q*HkxZ7@>|hV4<*rk79?*-C5AtHu{bs3&ZUdL3Kc4!VRp=6wqC!XJrNJwOgMsw1
z$*AKFxi5YUA$q!18FW=oGe$J1uzE=HcVB#>@mMgK_Ld$^;oj7^(|z;;kK}2)ybUI{
zGJzSF0OS2WFApiwsU%;$LEyk+O<7kC9)(5$@W!
zyt0oGiXV~TocBxfex>6yWIr%3O?+fj-7IwF5K%TtHav0>63T+8JalPYaq@rj}g0Gvg>gX(@gU)(Ps*|D0=qm&P7%851t;Os}!FZCFC~?
zLc;o~QX9Bqa(mz;!y2ZUZK{j#kyrS+C_0wolAQc&=G)j92);(lV(>2>)LYmuywygx
zAjO6s^mwfNyA5e_+{8o8Upgt`9&AIcb-`L(v_ia@Mo)&1$ZEr3W|r7U*~j@VYCB%B
z;d+-2(JKI{A)f#)q781nvB9615oC{uX>j-wUzbC`caCn=xYXBTV!l!q$0hRQ4>}C9
zJB(16D0Si}FLeUM=PSg67(cm!eXIH3-v41r9v_p)lUI1OVLSE~ZlY8g_G(F4@l%$E
zrCR`{1-w5*JrvuH>iMqmYCgZK(%P3&zuMmD_T;=PKIth%@EZT!Y?dm=dpoO-@|oBO
zmT2g*f4K9+~&V_q8k?0;t
zkD^ezP?!8~vuZW0v5dlG6vyf6RXXh3>c=U~##5H@bECeZQQ1lvTDdz?vC=Y%BDwl;
z%2n8xO_@3^TEZ!8iClw8T;X^>iYE*fYspJmDWUj#>y5D1gCNdqN5f-wMIJ^FUTAQ4
z+(t*=udhcd`btdK5|;<17qpe*vtPE$WCVkDlAmV4PCCsED@~Nf{JwvnO%J)D=$%8g
zyF_RAuml}`yMH&y6Z)P$lB?zm%*$1tX&4;i#I3#6V*-lL1Q>9#Ia7ajJS%V@N?r^Q
zI+YXiG$C~mNB+8Q5z8o;LS9zOP2ESL$S=QjKX1q<70KeboaY#M37(igezoJ`xScN2
zkJ>GnrBN<(R3-OK*Wjhj8>i?fGx@@vRQ2A`f;a_AezZhbj0Nhk&oZEAbL=B`T)j7!
zAwb`*^j`&LGdG{>c3n%xE}
zg1)Xybs<-$)eQO{Yc%O9rnX39aU{pX3(w~Ud9f@0)AHfO&g
zS=ddj!{n(K1u?F!;j`qDwPXo#fj+ZIOAk&|cl0`G^>(Lj@}=F1cb)F_KI61v^H`gs
z7^3d>$aK(*v3Fq;iQ$H$kXLg0C>?T?EIk#^rrM+H6uTi{=pxt%jpg#4h4#WI`7qFhgqebk+{YKirM;Fp5&UE^c4f)REn#Q6sEf*M0jGD#`!31HWrV1_XX@u=a8x75W435G34ZJW7)Yo=%52Go5wiY}BmIX2u
zBg4@(I$3Go==&Ucfk!jCg~&w6(fEe&-qX5RaNkpOUeX4rgB*F5p4PUeP8iTBUt=Ui=zh$t{xuYcE=<|JLY;%`8mt~73>wnle8d*3!H7jq!=
z*%48>#fu98&GD=pg*HS>zv`{Wl?f)n!?C=lVu2#cX?1x;7(O>Is^lvAr1(>oNn6ht
z^y@n#sy4;%xlCe7w6GWjJnvy!gV2lMWp-F!Ov+&bLfP5mjhBuL&F)vo&nT`QEo(Q=rTnNnyB=y+N
z-UYLU16$YWP!;}P4uTuLBk;I5+NrGFDuC^^
zmzZrM(@GS{j2&~I~>HL!=h6jlgFsg4X5I@
zE-UW*97T%+7%xz-H&8K}Ne*M4m&=TKiwkBgZD?VAkSXrs`0?W5pi}PsJGP`JfVhYH
zO@2=8p4ZU?@~%fu6<+QsRrtJi0%@;raP=P0I6P3Rc7A^~jl)4no9gog5`WGyIo2si
z*~3t(Kse{|O6~*uxv|-LxzOE#hV~tStS77<&lKu=QEV$(4+M|vIw8^Ln)O81kZ=)FLK1*N-p3fcjS(;p00eRw@c=%IA?JmaM8C(h3(8JUOyjbJbkQ
zmonAD!p?QjFAbhfGL=ssnQ9bQA?Rqn1r$11wzPX~^|6<_7`V3*F+>gvU6H5IMQ@F^
zIwt?o&|4}euH=9<8k(6x-!t2zMm!qGcy9Y
zOEXtQRrB0d{YS_q8c~W}wm8|6FF+acZ`kidJ#xwZ7RN}W2dt^ecIk!`JL1|I5y;XIWv)0qq^Lf*gV?-wb(`62~f=D-CM;_5_}KI?*?_y
zLhpI|nC5S&NKghJz6FE8kpHwP&Yf;BePq|8l2&Y5qW;BwfIPRUs8z)_Yt^X@>
z?F&6r{tRDSJ%xc*Qv|=85%DREd6Fvp(|~{l51!&rx|#u(>eoIL(*ek@2V6FrSQ_<2
zsn}%^_esAiEBGGZZBt3V$#}9{E0$BEoa;dBq+i+u$|!9z!O0?}vh(wGI
z&nyZnOWpLb(V8qC9hPNW%^FYyK|#35!8JcxgPq!tpbXT8Dm%=SPxj0$J%6cIbh%bE
zD`?NF-6eKOM5Ssd$BSHPLWw>@K1*W|OOsl-07G3$g3;eUhi3ul+HRJowHtVVY{eQ^
zq7Fi|Qx{7Ut}gE5o3SJ>xSBFrwR(GY3EzF$n%5qa!aW9uP;4?3;kI;55nD@aa1;--
z|Db5|-K(u=eTUU*EiP>+6SMN9gH-xR6My$jDwy3UQ+BX{Y*UN8&--l}^>g_4;%t0}
z7N7$dFsjgVk6@cl-uRRT_Wd>YHQtKwGi^-2omJsy_f>(Ps#ZQd7?ogm)GK`d+;3_O
zB0Sl8nODZ0F#O$>cql0|T|Gfu{O;O9Pq(r@XjRCW!d}lYP)s7BxIbH)KR+)O9$FR(xyk$-{gU7~l>A>ir
z>4W1he6KvIrzaX+w-WJ4_&$2KVy>px#qU!#UUR-Xk~5^T86k^6k>AA?`1qljt)VzC
zYsH__ij0Vkra?tfSok(5Vk)ePQ9<$){pulQB^x|sqSrsw;4?Sb(YA>0XJJFkhmBwT
zy4V$`Hg=rtb1TE+YuORziW*UnnRSLKv#Abmv)c@k29*#W`ab_zlgY8_uR_0^hgCTU
zc6|x0?w$+4j%zfVE_~NqZui;>G>SPDwRcs(S&N`ygA^V9&?lrWJciC%L-w4>)WExD
zQ*0SPf!F<)-IkbO{ixrsj%2vy?C+I=KVe%xHC$3{Ydv+FYgpAyjUm{cuNA?*b~N*8
z3_eaKk@-Mcgjhx&w%%&?hxat794JgOaYs%K#{g*qNrO~9KQv=@v+5E%i4r2STq|+Nlv!RvvqFv_~7S`(a8j=nSoZ=qJm&4AVpd`pY!{p
zFsY$4@tmy%p1f5}WcD<8|3XnUSz(jM_b?A#7nJuGKGxw?&P4Y#%Cd!$MZX`6iIB0b
zF!i%s%igkekI=Vf%8m?%M(?ix`!~ATTY1ZV$bR?l4ybcA7tC37Sq>}s$gy`53&8fT
zB#B(G^jr7Z6RCU}lyd)0b1mpb@D;^F{dYJyvY&*`w{P}J>%X@y`;Ik}-VQy?fmjk(
zYUC#C!5oGRD>&}R63OvAgoEKkb(3aikB5@Bq_u08#4(XN!v<#`p(eMIw-Q8LpFIqb
zuweszf`pNK_C`AUYN`{#qN9rm*wDf4+N_%`Og#b*U2=cwXL=gAIh@$C&a4DD)DH@>
z5pqY-YjP+1J*d&rdasGiRbGn+G)ZFbLQHB!Oh!rH8vP_`z=C>s7AIsyPCAu=sJCCp
z(r-az$!0%-o&g>wIZP$hvrPQ#dAA5hl4&yM5Ai4fey-w%?uXs`Zum9HaO~~%c4tb;
z4$$quVe~a^Jf$umPbt?1EOPTh8pt^^tYE7RN{WKrlngC8y?lJ^|NPJ&Nb&Pgt(c|C
zfWI(e?2;=GOFPMFVhn;+eqw-Erl+xG!*XC|dPQ8L=eY~~tS0B*8U2GDey7W7@x>fj
zk9yx-+)4|0n~ca5m#gWsVF2@Mp^UpOS-ga{pKb$ERDBl6n|2QwEm|0a&zN^e=RXD?
zG7OoVH^orI5ZqJn)zhF1>95{jJcsNMd#YgHk<(AlFWP7pqe}MeFPEt<$2X_Uqo3l)
z+B7hRycqZG+i|zQy#(
X4vqDeFsl6h0f@TtV
+
+
+
+ hr.contract.form.inherit
+ hr.contract
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/views/us_payroll_config_views.xml b/l10n_us_hr_payroll/views/us_payroll_config_views.xml
new file mode 100644
index 00000000..dfe0e301
--- /dev/null
+++ b/l10n_us_hr_payroll/views/us_payroll_config_views.xml
@@ -0,0 +1,75 @@
+
+
+
+ hr.contract.us_payroll_config.tree
+ hr.contract.us_payroll_config
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.contract.us_payroll_config.form
+ hr.contract.us_payroll_config
+
+
+
+ Form 941 / W4 - Federal Income Tax
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.contract.us_payroll_config.search
+ hr.contract.us_payroll_config
+
+
+
+
+
+
+
+
+
+
+ Employee Payroll Forms
+ hr.contract.us_payroll_config
+ tree,form
+
+
+ No Forms
+
+
+
+
+
+
From be1fd5c8d2f79f342a5dbf21a3b41eae3a46dfd0 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 7 Jan 2020 07:52:41 -0800
Subject: [PATCH 02/12] [IMP] l10n_us_hr_payroll: Rules and improvements. (+104
squashed commits) Squashed commits: [9ca3d040] [FIX] l10n_us_hr_payroll:
payslip category sum over date range now includes child categories [7a92b96e]
[FIX] l10n_us_hr_payroll: remove overwrite to correct wage calculation above
[45d130ce] [IMP] l10n_us_hr_payroll: Add migration code to handle known
issues from Odoo S.A. migrations. [54bffced] [FIX] l10n_us_hr_payroll: add
missing `semi-monthly` as a default schedule pay [d7206395] [IMP]
l10n_us_hr_payroll: common test call paramaterize defaults for Structure Type
and Resource Calendar [a1174740] [FIX] l10n_us_hr_payroll : Fixed exempt test
case for 2019. [2d8ec31b] [IMP] l10n_us_hr_payroll: Improved Tax table and
improved Test case for NJ New Jersey 2020 [51f61ab5] [IMP]
l10n_us_hr_payroll: Added comment and improved Test case for MS Mississippi
2020 [5bfe38f3] [IMP] l10n_us_hr_payroll: Improved Test case for MI Michigan
2020 [c21aa7a7] [IMP] l10n_us_hr_payroll: Added comment for MN Minnesota 2020
[ed67319a] [IMP] l10n_us_hr_payroll: Added comment and improved Test case for
MO Missouri 2020 [cc68ea2e] [IMP] l10n_us_hr_payroll: Added Tax table and
improved Test case for MT Montana 2020 [9450418c] [IMP] l10n_us_hr_payroll:
Added Tax table and improved Test case for ID Idaho 2020 [c389748c] [IMP]
l10n_us_hr_payroll: Added Tax table and improved Test case for KY Kentucky
2020 [6d4171fc] [IMP] l10n_us_hr_payroll: Reformat tax table, improved
comments and test case for IA Iowa 2020 [77588bc6] [IMP] l10n_us_hr_payroll:
Improved Tax table and Test case for HI Hawaii 2020 [585f8cbf] [IMP]
l10n_us_hr_payroll: Added Tax table for 2020 and improved Test case for GA
Georgia 2020 [92a89e59] [IMP] l10n_us_hr_payroll: Reformat tax table,
improved comments and test case for CA California 2020 [785b33e3] [IMP]
l10n_us_hr_payroll: Improved comments and test case for CT Connecticut 2020
[13198a9e] [IMP] l10n_us_hr_payroll: Improved test case for CO Colorado 2020
[c65b62a7] [IMP] l10n_us_hr_payroll: Improved comments and test case for AR
Arkansas 2020 [e01eeb65] [IMP] l10n_us_hr_payroll: Improved test case for AZ
Arizona 2020 [5cf0b69e] [IMP] l10n_us_hr_payroll: Improved comments, Tax
table, filing status and test case for AL Alabama 2020 [64436b6e] [IMP]
l10n_us_hr_payroll: Improved comments and test case for NM New Mexico 2020
[c395c8a9] [IMP] l10n_us_hr_payroll: Added Comment, removed one filing status
which was not used in calculation and improve test case for exempt for NC
North Carolina 2020 [ff4adfe8] [IMP] l10n_us_hr_payroll: Comment add for
table for VA Virginia 2020 [9fc9b3b6] [IMP] l10n_us_hr_payroll: Reformat Tax
table and changed wage for VT Vermont 2020 [5c96026b] [IMP]
l10n_us_hr_payroll: Reformat Tax table and changed SUTA rate for RI Rhode
Island 2020 [2a2abb62] [IMP] l10n_us_hr_payroll: Reformat Tax table, changed
SUTA rate and improved test case for UT Utah 2020. [42edfc06] [IMP]
l10n_us_hr_payroll: Refactored Tax table, changed filing status string and
Improved test case for KS Kansas 2020. [733e721a] [IMP] l10n_us_hr_payroll:
Reformat Tax table, changed field string and improved test case for OK
Oklahoma 2020 [7c2d9a20] [IMP] l10n_us_hr_payroll: Reformat Tax table for WV
West Virginia 2020. [91630c86] [IMP] l10n_us_hr_payroll: Refactored Tax
table, changed tax rate and added additional withholding field. Improved test
case for ME Maine 2020. [9c62ebaf] [IMP] l10n_us_hr_payroll: Refactored Tax
table and Improved test case for NE Nebraska 2020. [88118297] [IMP]
l10n_us_hr_payroll: Refactored Tax table and Improved test case for ME Maine
2020. [641bb815] [IMP] l10n_us_hr_payroll: Refactored sit rate tax table and
added allowance field and apply on calculation. Also Improved test case for
for ND North Dakota 2020. [f6f81615] [IMP] l10n_us_hr_payroll: Changed Form
name and additional withholding field string for IN Indiana 2020. [e4c9774f]
[IMP] l10n_us_hr_payroll: Added additional withholding, changed suta rate for
2020 and Improved test for SC South Carolina 2020. [91887067] [IMP]
l10n_us_hr_payroll: Improved test and restructured table for WI Wisconsin
2020. [9110d174] [FIX] l10n_us_hr_payroll: Updated NY New York 2020 rates and
tests. [1a7c26d8] [FIX] l10n_us_hr_payroll: Added exempt on filing status
for NY. [8f447aaa] [IMP] l10n_us_hr_payroll: Changed wage base and suta rate
for NY New York 2019/2020. [e9a53918] [IMP] l10n_us_hr_payroll: Changed suta
rate for WY Wyoming 2020.
H2914
[eddc6431] [IMP] l10n_us_hr_payroll: Changed suta rate and added exempt.
H2816
[dff4a2ca] [IMP] l10n_us_hr_payroll: For Rhode Island 13.0
[baea9412] [IMP] l10n_us_hr_payroll: For West Virginia 13.0
[8fed8e7b] [IMP] l10n_us_hr_payroll: For Wisconsin 13.0
[57182a87] [IMP] l10n_us_hr_payroll: For South Dakota 13.0
[1011c62e] [IMP] l10n_us_hr_payroll: For Tennessee 13.0
[688a3cc1] [IMP] l10n_us_hr_payroll: For Utah 13.0
[ded656db] [IMP] l10n_us_hr_payroll: For Vermont 13.0
[a0da1841] [IMP] l10n_us_hr_payroll: Port `l10n_us_wy_hr_payroll` WY Wyoming including migration.
[977cc3af] [IMP] l10n_us_hr_payroll: For Oklahoma 13.0
[68a0697c] [FIX]l10n_us_hr_payroll: Spell mistake on Kansas state payroll.
[addd5f03] [IMP] l10n_us_hr_payroll: For Kentucky 13.0
[4fb48854] [IMP] l10n_us_hr_payroll: For Kansas 13.0
[60d40449] [IMP] l10n_us_hr_payroll: For Nevada 13.0
[2475250f] [IMP] l10n_us_hr_payroll: For Maine 13.0
[1234467d] [IMP] l10n_us_hr_payroll: For North Dakota 13.0
[d1642bbe] IMP `l10n_us_hr_payroll` Allow configurable changes to payslip summing behavior.
In stock Odoo, summing anything in payroll rules (but most importantly rule amounts and category amounts by code), the considered payslips are referenced from their `date_from` field. However in the USA, it is in fact the `date_to` that is more important (or accounting date). A Payslip made for 2019-12-20 to 2020-01-04 should in fact be considered a '2020' payslip, and thus the summation on other '2020' payslips must find it by considering payslips `date_to`.
[0af81085] IMP `l10n_us_hr_payroll` Port `l10n_us_ny_hr_payroll` NY New York including migration
[bc5c0b47] IMP `l10n_us_hr_payroll` for Nebraska 13.0
[6f3120f8] IMP `l10n_us_hr_payroll` Port `l10n_us_sc_hr_payroll` SC South Carolina including migration
[9bee1ce7] IMP `l10n_us_hr_payroll` Port `l10n_us_la_hr_payroll` LA Louisiana including migration
[368a7e59] IMP `l10n_us_hr_payroll` for Indiana 13.0
[c7647d08] IMP `l10n_us_hr_payroll` for New Hampshire 13.0
[a738a0af] IMP `l10n_us_hr_payroll` for New Mexico 13.0
[d2898035] IMP `l10n_us_hr_payroll` Port `l10n_us_ia_hr_payroll` IA Iowa including migration
[acdd3d43] IMP `l10n_us_hr_payroll` for Colorado 13.0
[e1eccfc2] IMP `l10n_us_hr_payroll` Port `l10n_us_de_hr_payroll` DE Delaware including migration
[7b4adef4] IMP `l10n_us_hr_payroll` Port `l10n_us_hi_hr_payroll` HI Hawaii including migration
[28eb5b9d] FIX `l10n_us_hr_payroll` Don't give error on Zero wage in FIT
[498137cb] FIX `l10n_us_hr_payroll` Port `l10n_us_id_hr_payroll` Remove supplier from the Partners..
[8895e59f] FIX `l10n_us_hr_payroll` Port `l10n_us_ca_hr_payroll` Added test case on file.
[0082fce8] IMP `l10n_us_hr_payroll` Port `l10n_us_id_hr_payroll` ID Idaho including migration
[92f6d30c] IMP `l10n_us_hr_payroll` Port `l10n_us_ca_hr_payroll` CA California including migration
[2059172b] IMP `l10n_us_hr_payroll` Port `l10n_us_ct_hr_payroll` CT Connecticut including migration
[dd8f7369] IMP `l10n_us_hr_payroll` Port `l10n_us_al_hr_payroll` AL Alabama including migration
[d5c3e427] IMP `l10n_us_hr_payroll` Port `l10n_us_ak_hr_payroll` AK Alaska including migration
[fbba5b2b] FIX `l10n_us_hr_payroll` Changed SUTA Rate for Illinois 2020.
[18421d01] IMP `l10n_us_hr_payroll` Port `l10n_us_az_hr_payroll` AZ Arizona including migration
[f960d135] IMP `l10n_us_hr_payroll` Port `l10n_us_il_hr_payroll` IL Illinois including migration
[b85e7483] IMP `l10n_us_hr_payroll` Port `l10n_us_ar_hr_payroll` AR Arkansas including migration
[61e9530f] IMP `l10n_us_hr_payroll` Create tax exempt categories for table coverage from IRS Pub. 15-B
[38decf71] IMP `l10n_us_hr_payroll` Port `l10n_us_mn_hr_payroll` MN Minnesota including migration
[2c9dca19] IMP `l10n_us_hr_payroll` Port `l10n_us_mi_hr_payroll` MI Michigan including migration
[e175ecbb] IMP `l10n_us_hr_payroll` Port `l10n_us_nc_hr_payroll` NC North Carolina including migration
[db689da4] IMP `l10n_us_hr_payroll` Port `l10n_us_nj_hr_payroll` NJ New Jersey including migration
[130ce65c] IMP `l10n_us_hr_payroll` Add MO Missouri (unemployment, income tax)
[4d4fcd45] IMP `l10n_us_hr_payroll` Use the raw ER rate for Washington LNI (instead of the combined rate and removing EE portion)
[45fb9682] FIX `l10n_us_hr_payroll` Missing Parent Category and Code not matching pattern.
[3ae7b859] IMP `l10n_us_hr_payroll` Refactor to simply tax exempt deductions.
[30eafd14] IMP `l10n_us_hr_payroll` Add MS Mississippi (unemployment, income tax)
[2f7e7b96] IMP `l10n_us_hr_payroll` Add GA Georgia (unemployment, income tax)
[3d79ed81] IMP `l10n_us_hr_payroll` Add form name in Virginia's state box.
[2e6c7050] IMP `l10n_us_hr_payroll` Add VA Virginia (unemployment, income tax)
[8ae58731] IMP `l10n_us_hr_payroll` Add TX Texas (unemployment, OA, ETIA)
[f83bf47c] IMP `l10n_us_hr_payroll` Add WA Washington (unemployment, lni, fml)
[1d661f8d] IMP `l10n_us_hr_payroll` Add OH Ohio (unemployment, income tax)
[edbc8c59] IMP `l10n_us_hr_payroll` Add MT Montana (unemployment (with AFT), income tax)
[dfe38521] IMP `l10n_us_hr_payroll` Implement generic state income tax exempt and additional fields. Include in PA Tests and State Form section.
[900bc138] IMP `l10n_us_hr_payroll` Add Generic SIT Category and method, add PA Pennsylvania (unemployment (ER, EE), income tax)
[dcafce90] IMP `l10n_us_hr_payroll` Refactor SUTA tests into generic test. (Reworked Florida 2020) (+1 squashed commit)
Squashed commits:
[667cc8c4] IMP `l10n_us_hr_payroll` Add Generic SUTA Category and method, add FL Florida (unemployment, no income tax)
---
l10n_us_hr_payroll/__init__.py | 9 +
l10n_us_hr_payroll/__manifest__.py | 50 +
l10n_us_hr_payroll/data/base.xml | 108 ++
.../data/federal/fed_940_futa_rules.xml | 6 -
.../data/federal/fed_941_fica_rules.xml | 6 -
.../data/federal/fed_941_fit_rules.xml | 5 -
l10n_us_hr_payroll/data/state/ak_alaska.xml | 95 ++
l10n_us_hr_payroll/data/state/al_alabama.xml | 219 +++
l10n_us_hr_payroll/data/state/ar_arkansas.xml | 145 ++
l10n_us_hr_payroll/data/state/az_arizona.xml | 81 ++
.../data/state/ca_california.xml | 829 +++++++++++
l10n_us_hr_payroll/data/state/co_colorado.xml | 97 ++
.../data/state/ct_connecticut.xml | 1237 +++++++++++++++++
l10n_us_hr_payroll/data/state/de_delaware.xml | 119 ++
l10n_us_hr_payroll/data/state/fl_florida.xml | 63 +
l10n_us_hr_payroll/data/state/ga_georgia.xml | 943 +++++++++++++
l10n_us_hr_payroll/data/state/hi_hawaii.xml | 181 +++
l10n_us_hr_payroll/data/state/ia_iowa.xml | 303 ++++
l10n_us_hr_payroll/data/state/id_idaho.xml | 448 ++++++
l10n_us_hr_payroll/data/state/il_illinois.xml | 117 ++
l10n_us_hr_payroll/data/state/in_indiana.xml | 122 ++
l10n_us_hr_payroll/data/state/ks_kansas.xml | 194 +++
l10n_us_hr_payroll/data/state/ky_kentucky.xml | 99 ++
.../data/state/la_louisiana.xml | 157 +++
l10n_us_hr_payroll/data/state/me_maine.xml | 131 ++
l10n_us_hr_payroll/data/state/mi_michigan.xml | 99 ++
.../data/state/mn_minnesota.xml | 147 ++
l10n_us_hr_payroll/data/state/mo_missouri.xml | 151 ++
.../data/state/ms_mississippi.xml | 139 ++
l10n_us_hr_payroll/data/state/mt_montana.xml | 227 +++
.../data/state/nc_northcarolina.xml | 113 ++
.../data/state/nd_north_dakota.xml | 280 ++++
l10n_us_hr_payroll/data/state/ne_nebraska.xml | 237 ++++
.../data/state/nh_new_hampshire.xml | 51 +
.../data/state/nj_newjersey.xml | 1048 ++++++++++++++
.../data/state/nm_new_mexico.xml | 303 ++++
l10n_us_hr_payroll/data/state/nv_nevada.xml | 53 +
l10n_us_hr_payroll/data/state/ny_new_york.xml | 429 ++++++
l10n_us_hr_payroll/data/state/oh_ohio.xml | 157 +++
l10n_us_hr_payroll/data/state/ok_oklahoma.xml | 301 ++++
.../data/state/pa_pennsylvania.xml | 131 ++
.../data/state/ri_rhode_island.xml | 141 ++
.../data/state/sc_south_carolina.xml | 151 ++
.../data/state/sd_south_dakota.xml | 51 +
.../data/state/tn_tennessee.xml | 51 +
l10n_us_hr_payroll/data/state/tx_texas.xml | 127 ++
l10n_us_hr_payroll/data/state/ut_utah.xml | 166 +++
l10n_us_hr_payroll/data/state/va_virginia.xml | 138 ++
l10n_us_hr_payroll/data/state/vt_vermont.xml | 193 +++
.../data/state/wa_washington.xml | 200 +++
.../data/state/wi_wisconsin.xml | 118 ++
.../data/state/wv_west_virginia.xml | 215 +++
l10n_us_hr_payroll/data/state/wy_wyoming.xml | 61 +
.../migrations/13.0.0.0.1/post-migration.py | 30 +
.../migrations/13.0.0.0.1/pre-migration.py | 26 +
l10n_us_hr_payroll/models/__init__.py | 3 +
l10n_us_hr_payroll/models/browsable_object.py | 148 ++
l10n_us_hr_payroll/models/federal/fed_940.py | 56 +-
l10n_us_hr_payroll/models/federal/fed_941.py | 129 +-
l10n_us_hr_payroll/models/hr_contract.py | 5 +
l10n_us_hr_payroll/models/hr_payslip.py | 84 ++
.../models/res_config_settings.py | 24 +
l10n_us_hr_payroll/models/state/__init__.py | 1 +
l10n_us_hr_payroll/models/state/al_alabama.py | 80 ++
.../models/state/ar_arkansas.py | 47 +
l10n_us_hr_payroll/models/state/az_arizona.py | 35 +
.../models/state/ca_california.py | 98 ++
.../models/state/co_colorado.py | 45 +
.../models/state/ct_connecticut.py | 76 +
.../models/state/de_delaware.py | 49 +
l10n_us_hr_payroll/models/state/ga_georgia.py | 51 +
l10n_us_hr_payroll/models/state/general.py | 131 ++
l10n_us_hr_payroll/models/state/hi_hawaii.py | 43 +
l10n_us_hr_payroll/models/state/ia_iowa.py | 48 +
l10n_us_hr_payroll/models/state/id_idaho.py | 41 +
.../models/state/il_illinois.py | 35 +
l10n_us_hr_payroll/models/state/in_indiana.py | 34 +
l10n_us_hr_payroll/models/state/ks_kansas.py | 44 +
.../models/state/ky_kentucky.py | 32 +
.../models/state/la_louisiana.py | 62 +
l10n_us_hr_payroll/models/state/me_maine.py | 62 +
.../models/state/mi_michigan.py | 35 +
.../models/state/mn_minnesota.py | 44 +
.../models/state/mo_missouri.py | 53 +
.../models/state/ms_mississippi.py | 46 +
l10n_us_hr_payroll/models/state/mt_montana.py | 45 +
.../models/state/nc_northcarolina.py | 38 +
.../models/state/nd_north_dakota.py | 45 +
.../models/state/ne_nebraska.py | 49 +
.../models/state/nj_newjersey.py | 52 +
.../models/state/nm_new_mexico.py | 40 +
.../models/state/ny_new_york.py | 54 +
l10n_us_hr_payroll/models/state/oh_ohio.py | 47 +
.../models/state/ok_oklahoma.py | 47 +
.../models/state/ri_rhode_island.py | 48 +
.../models/state/sc_south_carolina.py | 50 +
l10n_us_hr_payroll/models/state/ut_utah.py | 39 +
.../models/state/va_virginia.py | 44 +
l10n_us_hr_payroll/models/state/vt_vermont.py | 46 +
.../models/state/wa_washington.py | 27 +
.../models/state/wi_wisconsin.py | 47 +
.../models/state/wv_west_virginia.py | 44 +
l10n_us_hr_payroll/models/update.py | 26 +
.../models/us_payroll_config.py | 233 ++++
l10n_us_hr_payroll/tests/__init__.py | 125 ++
l10n_us_hr_payroll/tests/common.py | 104 +-
l10n_us_hr_payroll/tests/test_special.py | 119 ++
.../tests/test_us_ak_alaska_payslip_2019.py | 61 +
.../tests/test_us_ak_alaska_payslip_2020.py | 15 +
.../tests/test_us_al_alabama_payslip_2019.py | 264 ++++
.../tests/test_us_al_alabama_payslip_2020.py | 36 +
.../tests/test_us_ar_arkansas_payslip_2019.py | 72 +
.../tests/test_us_ar_arkansas_payslip_2020.py | 35 +
.../tests/test_us_az_arizona_payslip_2019.py | 72 +
.../tests/test_us_az_arizona_payslip_2020.py | 34 +
.../test_us_ca_california_payslip_2019.py | 245 ++++
.../test_us_ca_california_payslip_2020.py | 43 +
.../tests/test_us_co_colorado_payslip_2020.py | 37 +
.../test_us_ct_connecticut_payslip_2019.py | 121 ++
.../test_us_ct_connecticut_payslip_2020.py | 35 +
.../tests/test_us_de_delaware_payslip_2020.py | 36 +
.../tests/test_us_fl_florida_payslip_2019.py | 84 ++
.../tests/test_us_fl_florida_payslip_2020.py | 16 +
.../tests/test_us_ga_georgia_payslip_2019.py | 135 ++
.../tests/test_us_ga_georgia_payslip_2020.py | 39 +
.../tests/test_us_hi_hawaii_payslip_2019.py | 93 ++
.../tests/test_us_hi_hawaii_payslip_2020.py | 37 +
.../tests/test_us_ia_iowa_payslip_2019.py | 152 ++
.../tests/test_us_ia_iowa_payslip_2020.py | 36 +
.../tests/test_us_id_idaho_payslip_2019.py | 85 ++
.../tests/test_us_id_idaho_payslip_2020.py | 35 +
.../tests/test_us_il_illinois_payslip_2019.py | 71 +
.../tests/test_us_il_illinois_payslip_2020.py | 36 +
.../tests/test_us_in_indiana_payslip_2020.py | 36 +
.../tests/test_us_ks_kansas_payslip_2020.py | 36 +
.../tests/test_us_ky_kentucky_payslip_2020.py | 35 +
.../test_us_la_louisiana_payslip_2019.py | 91 ++
.../test_us_la_louisiana_payslip_2020.py | 36 +
.../tests/test_us_me_maine_payslip_2020.py | 39 +
.../tests/test_us_mi_michigan_payslip_2019.py | 194 +++
.../tests/test_us_mi_michigan_payslip_2020.py | 35 +
.../test_us_mn_minnesota_payslip_2019.py | 159 +++
.../test_us_mn_minnesota_payslip_2020.py | 36 +
.../tests/test_us_mo_missouri_payslip_2019.py | 188 +++
.../tests/test_us_mo_missouri_payslip_2020.py | 34 +
.../test_us_ms_mississippi_payslip_2019.py | 94 ++
.../test_us_ms_mississippi_payslip_2020.py | 35 +
.../tests/test_us_mt_montana_payslip_2019.py | 139 ++
.../tests/test_us_mt_montana_payslip_2020.py | 37 +
.../test_us_nc_northcarolina_payslip_2019.py | 270 ++++
.../test_us_nc_northcarolina_payslip_2020.py | 37 +
.../test_us_nd_north_dakota_payslip_2020.py | 37 +
.../tests/test_us_ne_nebraska_payslip_2020.py | 38 +
.../test_us_nh_new_hampshire_payslip_2020.py | 13 +
.../test_us_nj_newjersey_payslip_2019.py | 128 ++
.../test_us_nj_newjersey_payslip_2020.py | 51 +
.../test_us_nm_new_mexico_payslip_2020.py | 36 +
.../tests/test_us_nv_nevada_payslip_2020.py | 16 +
.../tests/test_us_ny_new_york_payslip_2019.py | 133 ++
.../tests/test_us_ny_new_york_payslip_2020.py | 39 +
.../tests/test_us_oh_ohio_payslip_2019.py | 96 ++
.../tests/test_us_oh_ohio_payslip_2020.py | 108 ++
.../tests/test_us_ok_oklahoma_payslip_2020.py | 38 +
.../test_us_pa_pennsylvania_payslip_2019.py | 33 +
.../test_us_pa_pennsylvania_payslip_2020.py | 43 +
.../test_us_ri_rhode_island_payslip_2020.py | 37 +
.../test_us_sc_south_carolina_payslip_2019.py | 97 ++
.../test_us_sc_south_carolina_payslip_2020.py | 36 +
.../test_us_sd_south_dakota_payslip_2020.py | 13 +
.../test_us_tn_tennessee_payslip_2020.py | 13 +
.../tests/test_us_tx_texas_payslip_2019.py | 100 ++
.../tests/test_us_tx_texas_payslip_2020.py | 17 +
.../tests/test_us_us_utah_payslip_2020.py | 36 +
.../tests/test_us_va_virginia_payslip_2019.py | 133 ++
.../tests/test_us_va_virginia_payslip_2020.py | 116 ++
.../tests/test_us_vt_vermont_payslip_2020.py | 37 +
.../test_us_wa_washington_payslip_2019.py | 92 ++
.../test_us_wa_washington_payslip_2020.py | 90 ++
.../test_us_wi_wisconsin_payslip_2020.py | 39 +
.../test_us_wv_west_virginia_payslip_2020.py | 36 +
.../tests/test_us_wy_wyoming_payslip_2019.py | 58 +
.../tests/test_us_wy_wyoming_payslip_2020.py | 13 +
.../views/res_config_settings_views.xml | 32 +
.../views/us_payroll_config_views.xml | 261 +++-
184 files changed, 19772 insertions(+), 57 deletions(-)
create mode 100644 l10n_us_hr_payroll/data/state/ak_alaska.xml
create mode 100644 l10n_us_hr_payroll/data/state/al_alabama.xml
create mode 100644 l10n_us_hr_payroll/data/state/ar_arkansas.xml
create mode 100644 l10n_us_hr_payroll/data/state/az_arizona.xml
create mode 100644 l10n_us_hr_payroll/data/state/ca_california.xml
create mode 100644 l10n_us_hr_payroll/data/state/co_colorado.xml
create mode 100644 l10n_us_hr_payroll/data/state/ct_connecticut.xml
create mode 100644 l10n_us_hr_payroll/data/state/de_delaware.xml
create mode 100644 l10n_us_hr_payroll/data/state/fl_florida.xml
create mode 100644 l10n_us_hr_payroll/data/state/ga_georgia.xml
create mode 100644 l10n_us_hr_payroll/data/state/hi_hawaii.xml
create mode 100644 l10n_us_hr_payroll/data/state/ia_iowa.xml
create mode 100644 l10n_us_hr_payroll/data/state/id_idaho.xml
create mode 100644 l10n_us_hr_payroll/data/state/il_illinois.xml
create mode 100644 l10n_us_hr_payroll/data/state/in_indiana.xml
create mode 100644 l10n_us_hr_payroll/data/state/ks_kansas.xml
create mode 100644 l10n_us_hr_payroll/data/state/ky_kentucky.xml
create mode 100644 l10n_us_hr_payroll/data/state/la_louisiana.xml
create mode 100644 l10n_us_hr_payroll/data/state/me_maine.xml
create mode 100644 l10n_us_hr_payroll/data/state/mi_michigan.xml
create mode 100644 l10n_us_hr_payroll/data/state/mn_minnesota.xml
create mode 100644 l10n_us_hr_payroll/data/state/mo_missouri.xml
create mode 100644 l10n_us_hr_payroll/data/state/ms_mississippi.xml
create mode 100644 l10n_us_hr_payroll/data/state/mt_montana.xml
create mode 100644 l10n_us_hr_payroll/data/state/nc_northcarolina.xml
create mode 100644 l10n_us_hr_payroll/data/state/nd_north_dakota.xml
create mode 100644 l10n_us_hr_payroll/data/state/ne_nebraska.xml
create mode 100644 l10n_us_hr_payroll/data/state/nh_new_hampshire.xml
create mode 100644 l10n_us_hr_payroll/data/state/nj_newjersey.xml
create mode 100644 l10n_us_hr_payroll/data/state/nm_new_mexico.xml
create mode 100644 l10n_us_hr_payroll/data/state/nv_nevada.xml
create mode 100644 l10n_us_hr_payroll/data/state/ny_new_york.xml
create mode 100644 l10n_us_hr_payroll/data/state/oh_ohio.xml
create mode 100644 l10n_us_hr_payroll/data/state/ok_oklahoma.xml
create mode 100644 l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
create mode 100644 l10n_us_hr_payroll/data/state/ri_rhode_island.xml
create mode 100644 l10n_us_hr_payroll/data/state/sc_south_carolina.xml
create mode 100644 l10n_us_hr_payroll/data/state/sd_south_dakota.xml
create mode 100644 l10n_us_hr_payroll/data/state/tn_tennessee.xml
create mode 100644 l10n_us_hr_payroll/data/state/tx_texas.xml
create mode 100644 l10n_us_hr_payroll/data/state/ut_utah.xml
create mode 100644 l10n_us_hr_payroll/data/state/va_virginia.xml
create mode 100644 l10n_us_hr_payroll/data/state/vt_vermont.xml
create mode 100644 l10n_us_hr_payroll/data/state/wa_washington.xml
create mode 100644 l10n_us_hr_payroll/data/state/wi_wisconsin.xml
create mode 100644 l10n_us_hr_payroll/data/state/wv_west_virginia.xml
create mode 100644 l10n_us_hr_payroll/data/state/wy_wyoming.xml
create mode 100644 l10n_us_hr_payroll/migrations/13.0.0.0.1/post-migration.py
create mode 100644 l10n_us_hr_payroll/migrations/13.0.0.0.1/pre-migration.py
create mode 100644 l10n_us_hr_payroll/models/browsable_object.py
create mode 100644 l10n_us_hr_payroll/models/res_config_settings.py
create mode 100644 l10n_us_hr_payroll/models/state/__init__.py
create mode 100644 l10n_us_hr_payroll/models/state/al_alabama.py
create mode 100644 l10n_us_hr_payroll/models/state/ar_arkansas.py
create mode 100644 l10n_us_hr_payroll/models/state/az_arizona.py
create mode 100644 l10n_us_hr_payroll/models/state/ca_california.py
create mode 100644 l10n_us_hr_payroll/models/state/co_colorado.py
create mode 100644 l10n_us_hr_payroll/models/state/ct_connecticut.py
create mode 100644 l10n_us_hr_payroll/models/state/de_delaware.py
create mode 100644 l10n_us_hr_payroll/models/state/ga_georgia.py
create mode 100644 l10n_us_hr_payroll/models/state/general.py
create mode 100644 l10n_us_hr_payroll/models/state/hi_hawaii.py
create mode 100644 l10n_us_hr_payroll/models/state/ia_iowa.py
create mode 100644 l10n_us_hr_payroll/models/state/id_idaho.py
create mode 100644 l10n_us_hr_payroll/models/state/il_illinois.py
create mode 100644 l10n_us_hr_payroll/models/state/in_indiana.py
create mode 100644 l10n_us_hr_payroll/models/state/ks_kansas.py
create mode 100644 l10n_us_hr_payroll/models/state/ky_kentucky.py
create mode 100644 l10n_us_hr_payroll/models/state/la_louisiana.py
create mode 100644 l10n_us_hr_payroll/models/state/me_maine.py
create mode 100644 l10n_us_hr_payroll/models/state/mi_michigan.py
create mode 100644 l10n_us_hr_payroll/models/state/mn_minnesota.py
create mode 100644 l10n_us_hr_payroll/models/state/mo_missouri.py
create mode 100644 l10n_us_hr_payroll/models/state/ms_mississippi.py
create mode 100644 l10n_us_hr_payroll/models/state/mt_montana.py
create mode 100644 l10n_us_hr_payroll/models/state/nc_northcarolina.py
create mode 100644 l10n_us_hr_payroll/models/state/nd_north_dakota.py
create mode 100644 l10n_us_hr_payroll/models/state/ne_nebraska.py
create mode 100644 l10n_us_hr_payroll/models/state/nj_newjersey.py
create mode 100644 l10n_us_hr_payroll/models/state/nm_new_mexico.py
create mode 100644 l10n_us_hr_payroll/models/state/ny_new_york.py
create mode 100644 l10n_us_hr_payroll/models/state/oh_ohio.py
create mode 100644 l10n_us_hr_payroll/models/state/ok_oklahoma.py
create mode 100644 l10n_us_hr_payroll/models/state/ri_rhode_island.py
create mode 100644 l10n_us_hr_payroll/models/state/sc_south_carolina.py
create mode 100644 l10n_us_hr_payroll/models/state/ut_utah.py
create mode 100644 l10n_us_hr_payroll/models/state/va_virginia.py
create mode 100644 l10n_us_hr_payroll/models/state/vt_vermont.py
create mode 100644 l10n_us_hr_payroll/models/state/wa_washington.py
create mode 100644 l10n_us_hr_payroll/models/state/wi_wisconsin.py
create mode 100644 l10n_us_hr_payroll/models/state/wv_west_virginia.py
create mode 100644 l10n_us_hr_payroll/models/update.py
create mode 100644 l10n_us_hr_payroll/tests/test_special.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.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
create mode 100755 l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/views/res_config_settings_views.xml
diff --git a/l10n_us_hr_payroll/__init__.py b/l10n_us_hr_payroll/__init__.py
index 09434554..013f4e73 100644
--- a/l10n_us_hr_payroll/__init__.py
+++ b/l10n_us_hr_payroll/__init__.py
@@ -1,3 +1,12 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models
+
+def _post_install_hook(cr, registry):
+ """
+ This method will set the default for the Payslip Sum Behavior
+ """
+ cr.execute("SELECT id FROM ir_config_parameter WHERE key = 'hr_payroll.payslip.sum_behavior';")
+ existing = cr.fetchall()
+ if not existing:
+ cr.execute("INSERT INTO ir_config_parameter (key, value) VALUES ('hr_payroll.payslip.sum_behavior', 'date');")
diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py
index a748aae6..32c55af1 100644
--- a/l10n_us_hr_payroll/__manifest__.py
+++ b/l10n_us_hr_payroll/__manifest__.py
@@ -8,6 +8,7 @@
'depends': [
'hr_payroll',
'hr_contract_reports',
+ 'hibou_professional',
],
'description': """
United States of America - Payroll Rules.
@@ -25,11 +26,60 @@ United States of America - Payroll Rules.
'data/federal/fed_941_fica_rules.xml',
'data/federal/fed_941_fit_parameters.xml',
'data/federal/fed_941_fit_rules.xml',
+ 'data/state/ak_alaska.xml',
+ 'data/state/al_alabama.xml',
+ 'data/state/ar_arkansas.xml',
+ 'data/state/az_arizona.xml',
+ 'data/state/ca_california.xml',
+ 'data/state/co_colorado.xml',
+ 'data/state/ct_connecticut.xml',
+ 'data/state/de_delaware.xml',
+ 'data/state/fl_florida.xml',
+ 'data/state/ga_georgia.xml',
+ 'data/state/hi_hawaii.xml',
+ 'data/state/ia_iowa.xml',
+ 'data/state/id_idaho.xml',
+ 'data/state/il_illinois.xml',
+ 'data/state/in_indiana.xml',
+ 'data/state/ks_kansas.xml',
+ 'data/state/ky_kentucky.xml',
+ 'data/state/la_louisiana.xml',
+ 'data/state/me_maine.xml',
+ 'data/state/mi_michigan.xml',
+ 'data/state/mn_minnesota.xml',
+ 'data/state/mo_missouri.xml',
+ 'data/state/ms_mississippi.xml',
+ 'data/state/mt_montana.xml',
+ 'data/state/nc_northcarolina.xml',
+ 'data/state/nd_north_dakota.xml',
+ 'data/state/ne_nebraska.xml',
+ 'data/state/nh_new_hampshire.xml',
+ 'data/state/nj_newjersey.xml',
+ 'data/state/nm_new_mexico.xml',
+ 'data/state/nv_nevada.xml',
+ 'data/state/ny_new_york.xml',
+ 'data/state/oh_ohio.xml',
+ 'data/state/ok_oklahoma.xml',
+ 'data/state/pa_pennsylvania.xml',
+ 'data/state/ri_rhode_island.xml',
+ 'data/state/sc_south_carolina.xml',
+ 'data/state/sd_south_dakota.xml',
+ 'data/state/tn_tennessee.xml',
+ 'data/state/tx_texas.xml',
+ 'data/state/ut_utah.xml',
+ 'data/state/vt_vermont.xml',
+ 'data/state/va_virginia.xml',
+ 'data/state/wa_washington.xml',
+ 'data/state/wi_wisconsin.xml',
+ 'data/state/wv_west_virginia.xml',
+ 'data/state/wy_wyoming.xml',
'views/hr_contract_views.xml',
+ 'views/res_config_settings_views.xml',
'views/us_payroll_config_views.xml',
],
'demo': [
],
'auto_install': False,
+ 'post_init_hook': '_post_install_hook',
'license': 'OPL-1',
}
diff --git a/l10n_us_hr_payroll/data/base.xml b/l10n_us_hr_payroll/data/base.xml
index 6838f283..2e29934e 100644
--- a/l10n_us_hr_payroll/data/base.xml
+++ b/l10n_us_hr_payroll/data/base.xml
@@ -17,4 +17,112 @@
]"/>
+
+
+ EE: State Unemployment SUTA
+ EE_US_SUTA
+
+
+
+ ER: State Unemployment SUTA
+ ER_US_SUTA
+
+
+
+
+
+ EE: State Income Tax Withholding
+ EE_US_SIT
+
+
+
+
+
+
+
+ Wage: US FIT Exempt
+ ALW_FIT_EXEMPT
+
+
+
+
+ Wage: US FIT & FICA Exempt
+ ALW_FIT_FICA_EXEMPT
+
+
+
+
+ Wage: US FIT & FUTA Exempt
+ ALW_FIT_FUTA_EXEMPT
+
+
+
+
+ Wage: US FIT & FICA & FUTA Exempt
+ ALW_FIT_FICA_FUTA_EXEMPT
+
+
+
+
+ Wage: US FICA Exempt
+ ALW_FICA_EXEMPT
+
+
+
+
+ Wage: US FICA & FUTA Exempt
+ ALW_FICA_FUTA_EXEMPT
+
+
+
+
+ Wage: US FUTA Exempt
+ ALW_FUTA_EXEMPT
+
+
+
+
+
+
+ Deduction: US FIT Exempt
+ DED_FIT_EXEMPT
+
+
+
+
+ Deduction: US FIT & FICA Exempt
+ DED_FIT_FICA_EXEMPT
+
+
+
+
+ Deduction: US FIT & FUTA Exempt
+ DED_FIT_FUTA_EXEMPT
+
+
+
+
+ Deduction: US FIT & FICA & FUTA Exempt
+ DED_FIT_FICA_FUTA_EXEMPT
+
+
+
+
+ Deduction: US FICA Exempt
+ DED_FICA_EXEMPT
+
+
+
+
+ Deduction: US FICA & FUTA Exempt
+ DED_FICA_FUTA_EXEMPT
+
+
+
+
+ Deduction: US FUTA Exempt
+ DED_FUTA_EXEMPT
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml b/l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
index 5b315100..6a153efb 100644
--- a/l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
+++ b/l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
@@ -11,12 +11,6 @@
-
-
- WAGE: Federal 940 FUTA Exempt
- WAGE_US_940_FUTA_EXEMPT
-
-
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml b/l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
index 324958a4..64c91607 100644
--- a/l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
@@ -17,12 +17,6 @@
-
-
- WAGE: Federal 941 FICA Exempt
- WAGE_US_941_FICA_EXEMPT
-
-
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml b/l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
index a7751adf..4e3cb28c 100644
--- a/l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
@@ -1,10 +1,5 @@
-
-
- WAGE: Federal 941 Income Tax Exempt
- WAGE_US_941_FIT_EXEMPT
-
EE: Federal 941 Income Tax Withholding
diff --git a/l10n_us_hr_payroll/data/state/ak_alaska.xml b/l10n_us_hr_payroll/data/state/ak_alaska.xml
new file mode 100644
index 00000000..2c995088
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ak_alaska.xml
@@ -0,0 +1,95 @@
+
+
+
+
+ US AK Alaska SUTA Wage Base
+ us_ak_suta_wage_base
+
+
+
+
+ 39900.00
+
+
+
+
+ 41500.00
+
+
+
+
+
+
+
+ US AK Alaska SUTA Rate
+ us_ak_suta_rate
+
+
+
+
+ 1.780
+
+
+
+
+ 1.590
+
+
+
+
+
+
+ US AK Alaska SUTA Rate EE
+ us_ak_suta_ee_rate
+
+
+
+
+ 0.500
+
+
+
+
+ 0.500
+
+
+
+
+
+
+
+ US Alaska - Department of Labor and Workforce Development (ADLWD) - Unemployment Tax
+
+
+
+
+
+
+
+
+
+ ER: US AK Alaska State Unemployment
+ ER_US_AK_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ak_suta_wage_base', rate='us_ak_suta_rate', state_code='AK')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ak_suta_wage_base', rate='us_ak_suta_rate', state_code='AK')
+
+
+
+
+
+
+
+
+ EE: US AK Alaska State Unemployment (UC-2)
+ EE_US_AK_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ak_suta_wage_base', rate='us_ak_suta_ee_rate', state_code='AK')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ak_suta_wage_base', rate='us_ak_suta_ee_rate', state_code='AK')
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/al_alabama.xml b/l10n_us_hr_payroll/data/state/al_alabama.xml
new file mode 100644
index 00000000..fc3b3af1
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/al_alabama.xml
@@ -0,0 +1,219 @@
+
+
+
+
+ US AL Alabama SUTA Wage Base
+ us_al_suta_wage_base
+
+
+
+
+ 8000.0
+
+
+
+
+ 8000.0
+
+
+
+
+
+
+
+ US AL Alabama SUTA Rate
+ us_al_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US AL Alabama SIT Tax Rate
+ us_al_sit_tax_rate
+
+
+
+
+ {
+ '0': [
+ ( 500, 2),
+ ( 3000, 4),
+ ('inf', 5),
+ ],
+ 'M': [
+ ( 1000, 2),
+ ( 6000, 4),
+ ('inf', 5),
+ ],
+ }
+
+
+
+
+ {
+ '0' : [
+ ( 500, 2),
+ ( 2500, 4),
+ ('inf', 5),
+ ],
+ 'M': [
+ ( 1000, 2),
+ ( 5000, 4),
+ ('inf', 5),
+ ],
+ }
+
+
+
+
+
+
+ US AL Alabama Dependent Rate
+ us_al_sit_dependent_rate
+
+
+
+
+
+
+ [
+ ( 1000, 20000),
+ ( 500, 100000),
+ ( 300, 'inf'),
+ ]
+
+
+
+
+
+
+ [
+ ( 1000, 20000),
+ ( 500, 100000),
+ ( 300, 'inf'),
+ ]
+
+
+
+
+
+
+ US AL Alabama Standard Deduction Rate
+ us_al_sit_standard_deduction_rate
+
+
+
+
+
+
+ {
+ '0': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'S': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'MS': ((10749.0, 3750.0), (15500.0, 3750.0, 88.0, 250.0), ('inf', 2000.0)),
+ 'M': ((23499.0, 7500.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ 'H': ((23499.0, 4700.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ }
+
+
+
+
+
+
+ {
+ '0': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'S': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'MS': ((10749.0, 3750.0), (15500.0, 3750.0, 88.0, 250.0), ('inf', 2000.0)),
+ 'M': ((23499.0, 7500.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ 'H': ((23499.0, 4700.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ }
+
+
+
+
+
+
+ US AL Alabama Personal Exemption Rate
+ us_al_sit_personal_exemption_rate
+
+
+
+
+
+
+ {
+ '0' : 0,
+ 'S' : 1500,
+ 'MS': 1500,
+ 'M' : 3000,
+ 'H' : 3000,
+ }
+
+
+
+
+
+
+ {
+ '0' : 0,
+ 'S' : 1500,
+ 'MS': 1500,
+ 'M' : 3000,
+ 'H' : 3000,
+ }
+
+
+
+
+
+
+
+ US Alabama - Department of Economic Security (IDES) - Unemployment Tax
+
+
+
+ US Alabama - Department of Revenue (IDOR) - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US AL Alabama State Unemployment
+ ER_US_AL_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_al_suta_wage_base', rate='us_al_suta_rate', state_code='AL')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_al_suta_wage_base', rate='us_al_suta_rate', state_code='AL')
+
+
+
+
+
+
+
+
+ EE: US AL Alabama State Income Tax Withholding
+ EE_US_AL_SIT
+ python
+ result, _ = al_alabama_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = al_alabama_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ar_arkansas.xml b/l10n_us_hr_payroll/data/state/ar_arkansas.xml
new file mode 100644
index 00000000..3e5b8211
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ar_arkansas.xml
@@ -0,0 +1,145 @@
+
+
+
+
+ US AR Arkansas SUTA Wage Base
+ us_ar_suta_wage_base
+
+
+
+
+ 10000.0
+
+
+
+
+ 8000.0
+
+
+
+
+
+
+
+ US AR Arkansas SUTA Rate
+ us_ar_suta_rate
+
+
+
+
+ 3.2
+
+
+
+
+ 2.9
+
+
+
+
+
+
+ US AR Arkansas SIT Tax Rate
+ us_ar_sit_tax_rate
+
+
+
+
+ [
+ ( 4599, 0.0, 0.00),
+ ( 9099, 2.0, 91.98),
+ ( 13699, 3.0, 182.97),
+ ( 22599, 3.4, 237.77),
+ ( 37899, 5.0, 421.46),
+ ( 80800, 5.9, 762.55),
+ ( 81800, 6.6, 1243.40),
+ ( 82800, 6.6, 1143.40),
+ ( 84100, 6.6, 1043.40),
+ ( 85200, 6.6, 943.40),
+ ( 86200, 6.6, 843.40),
+ ( 'inf', 6.6, 803.40),
+ ]
+
+
+
+
+
+
+ [
+ ( 4599, 0.0, 0.00),
+ ( 9099, 2.0, 91.98),
+ ( 13699, 3.0, 182.97),
+ ( 22599, 3.4, 237.77),
+ ( 37899, 5.0, 421.46),
+ ( 80800, 5.9, 762.55),
+ ( 81800, 6.6, 1243.40),
+ ( 82800, 6.6, 1143.40),
+ ( 84100, 6.6, 1043.40),
+ ( 85200, 6.6, 943.40),
+ ( 86200, 6.6, 843.40),
+ ( 'inf', 6.6, 803.40),
+ ]
+
+
+
+
+
+
+ US AR Arkansas Allowances Rate
+ us_ar_sit_standard_deduction_rate
+
+
+
+
+ 2200.0
+
+
+
+
+ 2200.0
+
+
+
+
+
+
+
+ US Arkansas - Department of Workforce Solutions - Unemployment Tax
+
+
+
+ US Arkansas - Department of Financial Administration - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US AR Arkansas State Unemployment
+ ER_US_AR_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ar_suta_wage_base', rate='us_ar_suta_rate', state_code='AR')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ar_suta_wage_base', rate='us_ar_suta_rate', state_code='AR')
+
+
+
+
+
+
+
+
+ EE: US AR Arkansas State Income Tax Withholding
+ EE_US_AR_SIT
+ python
+ result, _ = ar_arkansas_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ar_arkansas_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/az_arizona.xml b/l10n_us_hr_payroll/data/state/az_arizona.xml
new file mode 100644
index 00000000..80b800c1
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/az_arizona.xml
@@ -0,0 +1,81 @@
+
+
+
+
+ US AZ Arizona SUTA Wage Base
+ us_az_suta_wage_base
+
+
+
+
+ 7000.0
+
+
+
+
+ 7000.0
+
+
+
+
+
+
+
+ US AZ Arizona SUTA Rate
+ us_az_suta_rate
+
+
+
+
+ 2.0
+
+
+
+
+ 2.0
+
+
+
+
+
+
+
+ US Arizona - Department of Economic Security (ADES) - Unemployment Tax
+
+
+
+ US Arizona - Department of Revenue (ADOR) - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US AZ Arizona State Unemployment
+ ER_US_AZ_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_az_suta_wage_base', rate='us_az_suta_rate', state_code='AZ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_az_suta_wage_base', rate='us_az_suta_rate', state_code='AZ')
+
+
+
+
+
+
+
+
+ EE: US AZ Arizona State Income Tax Withholding
+ EE_US_AZ_SIT
+ python
+ result, _ = az_arizona_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = az_arizona_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ca_california.xml b/l10n_us_hr_payroll/data/state/ca_california.xml
new file mode 100644
index 00000000..2a907b3a
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ca_california.xml
@@ -0,0 +1,829 @@
+
+
+
+
+ US CA California SUTA Wage Base
+ us_ca_suta_wage_base
+
+
+
+
+ 7000.0
+
+
+
+
+ 7000.0
+
+
+
+
+
+
+
+ US CA California SUTA Rate
+ us_ca_suta_rate
+
+
+
+
+ 3.5
+
+
+
+
+ 3.4
+
+
+
+
+
+
+
+ US CA California SUTA ETT Rate
+ us_ca_suta_ett_rate
+
+
+
+
+ 0.1
+
+
+
+
+ 0.1
+
+
+
+
+
+
+
+ US CA California SUTA SDI Rate
+ us_ca_suta_sdi_rate
+
+
+
+
+ 1.0
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ US CA California SIT Tax Rate
+ us_ca_sit_tax_rate
+
+
+
+
+ {
+ 'head_household': {
+ 'weekly': (
+ ( 316, 0.0110, 0.00),
+ ( 750, 0.0220, 3.48),
+ ( 967, 0.0440, 13.03),
+ ( 1196, 0.0660, 22.58),
+ ( 1413, 0.0880, 37.69),
+ ( 7212, 0.1023, 56.79),
+ ( 8654, 0.1133, 650.03),
+ (14423, 0.1243, 813.41),
+ (19231, 0.1353, 1530.50),
+ ('inf', 0.1463, 2181.02),
+ ),
+ 'bi-weekly': (
+ ( 632, 0.0110, 0.00),
+ ( 1500, 0.0220, 6.95),
+ ( 1934, 0.0440, 26.05),
+ ( 2392, 0.0660, 45.15),
+ ( 2826, 0.0880, 75.38),
+ (14424, 0.1023, 113.57),
+ (17308, 0.1133, 1300.05),
+ (28846, 0.1243, 1626.81),
+ (38462, 0.1353, 3060.98),
+ ('inf', 0.1463, 4362.02),
+ ),
+ 'semi-monthly': (
+ ( 686, 0.0110, 0.00),
+ ( 1625, 0.0220, 7.55),
+ ( 2094, 0.0440, 28.21),
+ ( 2592, 0.0660, 48.85),
+ ( 3062, 0.0880, 81.72),
+ (15625, 0.1023, 123.08),
+ (18750, 0.1133, 1408.27),
+ (31250, 0.1243, 1762.33),
+ (41667, 0.1353, 3316.08),
+ ('inf', 0.1463, 4725.50),
+ ),
+ 'monthly': (
+ ( 1372, 0.0110, 0.00),
+ ( 3250, 0.0220, 15.09),
+ ( 4188, 0.0440, 56.41),
+ ( 5184, 0.0660, 97.68),
+ ( 6124, 0.0880, 163.42),
+ (31250, 0.1023, 246.14),
+ (37500, 0.1133, 2816.53),
+ (62500, 0.1243, 3524.66),
+ (83334, 0.1353, 6632.16),
+ ('inf', 0.1463, 9451.00),
+ ),
+ 'quarterly': (
+ ( 4114, 0.0110, 0.00),
+ ( 9748, 0.0220, 45.25),
+ ( 12566, 0.0440, 169.20),
+ ( 15552, 0.0660, 293.19),
+ ( 18369, 0.0880, 490.27),
+ ( 93751, 0.1023, 738.17),
+ (112501, 0.1133, 8449.75),
+ (187501, 0.1243, 10574.13),
+ (250000, 0.1353, 19896.63),
+ ( 'inf', 0.1463, 28352.74),
+ ),
+ 'semi-annual': (
+ ( 8228, 0.0110, 0.00),
+ ( 19496, 0.0220, 90.51),
+ ( 25132, 0.0440, 338.41),
+ ( 31104, 0.0660, 586.39),
+ ( 36738, 0.0880, 980.54),
+ (187502, 0.1023, 1476.33),
+ (225002, 0.1133, 16899.49),
+ (375002, 0.1243, 21148.24),
+ (500000, 0.1353, 39793.24),
+ ( 'inf', 0.1463, 56705.47),
+ ),
+ 'annually': (
+ ( 16457, 0.0110, 0.00),
+ ( 38991, 0.0220, 181.03),
+ ( 50264, 0.0440, 676.78),
+ ( 62206, 0.0660, 1172.79),
+ ( 73477, 0.0880, 1960.96),
+ ( 375002, 0.1023, 2952.81),
+ ( 450003, 0.1133, 33798.82),
+ ( 750003, 0.1243, 42296.43),
+ (1000000, 0.1353, 79586.43),
+ ( 'inf', 0.1463, 113411.02),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 316, 0.0110, 0.00),
+ ( 750, 0.0220, 3.48),
+ ( 1184, 0.0440, 13.03),
+ ( 1642, 0.0660, 32.13),
+ ( 2076, 0.0880, 62.36),
+ (10606, 0.1023, 100.55),
+ (12726, 0.1133, 973.17),
+ (19231, 0.1243, 1213.37),
+ (21210, 0.1353, 2021.94),
+ ('inf', 0.1463, 2289.70),
+ ),
+ 'bi-weekly': (
+ ( 632, 0.0110, 0.00),
+ ( 1500, 0.0220, 6.95),
+ ( 2368, 0.0440, 26.05),
+ ( 3284, 0.0660, 64.24),
+ ( 4152, 0.0880, 124.70),
+ (21212, 0.1023, 201.08),
+ (25452, 0.1133, 1946.32),
+ (38462, 0.1243, 2426.71),
+ (42420, 0.1353, 4043.85),
+ ('inf', 0.1463, 4579.37),
+ ),
+ 'semi-monthly': (
+ ( 686, 0.0110, 0.00),
+ ( 1624, 0.0220, 7.55),
+ ( 2564, 0.0440, 28.19),
+ ( 3560, 0.0660, 69.55),
+ ( 4498, 0.0880, 135.29),
+ (22978, 0.1023, 217.83),
+ (27574, 0.1133, 2108.33),
+ (41667, 0.1243, 2629.06),
+ (45956, 0.1353, 4380.82),
+ ('inf', 0.1463, 4961.12),
+ ),
+ 'monthly': (
+ ( 1372, 0.0110, 0.00),
+ ( 3248, 0.0220, 15.09),
+ ( 5128, 0.0440, 56.36),
+ ( 7120, 0.0660, 139.08),
+ ( 8996, 0.0880, 270.55),
+ (45956, 0.1023, 435.64),
+ (55148, 0.1133, 4216.65),
+ (83334, 0.1243, 5258.10),
+ (91912, 0.1353, 8761.62),
+ ('inf', 0.1463, 9922.22),
+ ),
+ 'quarterly': (
+ ( 4112, 0.0110, 0.00),
+ ( 9748, 0.0220, 45.23),
+ ( 15384, 0.0440, 169.22),
+ ( 21356, 0.0660, 417.20),
+ ( 26990, 0.0880, 811.35),
+ (137870, 0.1023, 1307.14),
+ (165442, 0.1133, 12650.16),
+ (250000, 0.1243, 15774.07),
+ (275736, 0.1353, 26284.63),
+ ( 'inf', 0.1463, 29766.71),
+ ),
+ 'semi-annual': (
+ ( 8224, 0.0110, 0.00),
+ ( 19496, 0.0220, 90.46),
+ ( 30768, 0.0440, 338.44),
+ ( 42712, 0.0660, 834.41),
+ ( 53980, 0.0880, 1622.71),
+ (275740, 0.1023, 2614.29),
+ (330884, 0.1133, 25300.34),
+ (500000, 0.1243, 31548.16),
+ (551472, 0.1353, 52569.28),
+ ( 'inf', 0.1463, 59533.44),
+ ),
+ 'annually': (
+ ( 16446, 0.0110, 0.00),
+ ( 38990, 0.0220, 180.91),
+ ( 61538, 0.0440, 676.88),
+ ( 85422, 0.0660, 1668.99),
+ ( 107960, 0.0880, 3245.33),
+ ( 551476, 0.1023, 5228.67),
+ ( 661768, 0.1133, 50600.36),
+ (1000000, 0.1243, 63096.44),
+ (1102946, 0.1353, 105138.68),
+ ( 'inf', 0.1463, 119067.26),
+ ),
+ },
+ 'single': {
+ 'weekly': (
+ ( 158, 0.0110, 0.00),
+ ( 375, 0.0220, 1.74),
+ ( 592, 0.0440, 6.51),
+ ( 821, 0.0660, 16.06),
+ ( 1038, 0.0880, 31.17),
+ ( 5303, 0.1023, 50.27),
+ ( 6363, 0.1133, 486.58),
+ (10605, 0.1243, 606.68),
+ (19231, 0.1353, 1133.96),
+ ('inf', 0.1463, 2301.06),
+ ),
+ 'bi-weekly': (
+ ( 316, 0.0110, 0.00),
+ ( 750, 0.0220, 3.48),
+ ( 1184, 0.0440, 13.03),
+ ( 1642, 0.066, 32.13),
+ ( 2076, 0.0880, 62.36),
+ (10606, 0.1023, 100.55),
+ (12726, 0.1133, 973.17),
+ (21210, 0.1243, 1213.37),
+ (38462, 0.1353, 2267.93),
+ ('inf', 0.1463, 4602.13),
+ ),
+ 'semi-monthly': (
+ ( 343, 0.0110, 0.00),
+ ( 812, 0.0220, 3.77),
+ ( 1282, 0.0440, 14.09),
+ ( 1780, 0.0660, 34.77),
+ ( 2249, 0.0880, 67.64),
+ (11489, 0.1023, 108.91),
+ (13787, 0.1133, 1054.16),
+ (22978, 0.1243, 1314.52),
+ (41667, 0.1353, 2456.96),
+ ('inf', 0.1463, 4985.58),
+ ),
+ 'monthly': (
+ ( 686, 0.0110, 0.00),
+ ( 1624, 0.0220, 7.55),
+ ( 2564, 0.0440, 28.19),
+ ( 3560, 0.0660, 69.55),
+ ( 4498, 0.0880, 135.29),
+ (22978, 0.1023, 217.83),
+ (27574, 0.1133, 2108.33),
+ (45956, 0.1243, 2629.06),
+ (83334, 0.1353, 4913.94),
+ ('inf', 0.1463, 9971.18),
+ ),
+ 'quarterly': (
+ ( 2056, 0.0110, 0.00),
+ ( 4874, 0.0220, 22.62),
+ ( 7692, 0.0440, 84.62),
+ ( 10678, 0.066, 208.61),
+ ( 13495, 0.0880, 405.69),
+ ( 68935, 0.1023, 653.59),
+ ( 82721, 0.1133, 6325.10),
+ (137868, 0.1243, 7887.05),
+ (250000, 0.1353, 14741.82),
+ ( 'inf', 0.1463, 29913.28),
+ ),
+ 'semi-annual': (
+ ( 4112, 0.0110, 0.00),
+ ( 9748, 0.0220, 45.23),
+ ( 15384, 0.0440, 169.22),
+ ( 21356, 0.0660, 417.20),
+ ( 26990, 0.0880, 811.35),
+ (137870, 0.1023, 1307.14),
+ (165442, 0.1133, 12650.16),
+ (275736, 0.1243, 15774.07),
+ (500000, 0.1353, 29483.61),
+ ('inf', 0.1463, 59826.53),
+ ),
+ 'annually': (
+ ( 8223, 0.0110, 0.00),
+ ( 19495, 0.0220, 90.45),
+ ( 30769, 0.0440, 338.43),
+ ( 42711, 0.0660, 834.49),
+ ( 53980, 0.0880, 1622.66),
+ ( 275738, 0.1023, 2614.33),
+ ( 330884, 0.1133, 25300.17),
+ ( 551473, 0.1243, 31548.21),
+ (1000000, 0.1353, 58967.42),
+ ( 'inf', 0.1463, 119653.12),
+ ),
+ },
+ }
+
+
+
+
+
+
+ {
+ 'head_household': {
+ 'weekly': (
+ ( 339, 0.0110, 0.00),
+ ( 803, 0.0220, 3.73),
+ ( 1035, 0.0440, 13.93),
+ ( 1281, 0.0660, 24.15),
+ ( 1514, 0.0880, 40.39),
+ ( 7725, 0.1023, 60.89),
+ ( 9270, 0.1133, 696.28),
+ (15450, 0.1243, 871.33),
+ (19231, 0.1353, 1639.50),
+ ('inf', 0.1463, 2151.07),
+ ),
+ 'bi-weekly': (
+ ( 678, 0.0110, 0.00),
+ ( 1606, 0.0220, 7.46),
+ ( 2070, 0.0440, 27.88),
+ ( 2562, 0.0660, 48.30),
+ ( 3028, 0.0880, 80.77),
+ (15450, 0.1023, 121.78),
+ (18540, 0.1133, 1392.55),
+ (30900, 0.1243, 1742.65),
+ (38462, 0.1353, 3279.00),
+ ('inf', 0.1463, 4302.14),
+ ),
+ 'semi-monthly': (
+ ( 735, 0.0110, 0.00),
+ ( 1740, 0.0220, 8.09),
+ ( 2243, 0.0440, 30.20),
+ ( 2777, 0.0660, 52.33),
+ ( 3280, 0.0880, 87.57),
+ (16738, 0.1023, 131.83),
+ (20085, 0.1133, 1508.58),
+ (33475, 0.1243, 1887.80),
+ (41667, 0.1353, 3552.18),
+ ('inf', 0.1463, 4660.56),
+ ),
+ 'monthly': (
+ ( 1470, 0.0110, 0.00),
+ ( 3480, 0.0220, 16.17),
+ ( 4486, 0.0440, 60.39),
+ ( 5554, 0.0660, 104.65),
+ ( 6560, 0.0880, 175.14),
+ (33476, 0.1023, 263.67),
+ (40170, 0.1133, 3017.18),
+ (66950, 0.1243, 3775.61),
+ (83334, 0.1353, 7104.36),
+ ('inf', 0.1463, 9321.12),
+ ),
+ 'quarterly': (
+ ( 4407, 0.0110, 0.00),
+ ( 10442, 0.0220, 48.48),
+ ( 13461, 0.0440, 181.25),
+ ( 16659, 0.0660, 314.09),
+ ( 19678, 0.0880, 525.16),
+ (100426, 0.1023, 790.83),
+ (120512, 0.1133, 9051.35),
+ (200853, 0.1243, 11327.09),
+ (250000, 0.1353, 21313.48),
+ ( 'inf', 0.1463, 27963.07),
+ ),
+ 'semi-annual': (
+ ( 8814, 0.0110, 0.00),
+ ( 20884, 0.0220, 96.95),
+ ( 26922, 0.0440, 362.49),
+ ( 33318, 0.0660, 628.16),
+ ( 39356, 0.0880, 1050.30),
+ (200852, 0.1023, 1581.64),
+ (241024, 0.1133, 18102.68),
+ (401706, 0.1243, 22654.17),
+ (500000, 0.1353, 42626.94),
+ ( 'inf', 0.1463, 55926.12),
+ ),
+ 'annually': (
+ ( 17629, 0.0110, 0.00),
+ ( 41768, 0.0220, 193.92),
+ ( 53843, 0.0440, 724.98),
+ ( 66636, 0.0660, 1256.28),
+ ( 78710, 0.0880, 2100.62),
+ ( 401705, 0.1023, 3163.13),
+ ( 482047, 0.1133, 36205.52),
+ ( 803410, 0.1243, 45308.27),
+ (1000000, 0.1353, 85253.69),
+ ( 'inf', 0.1463, 111852.32),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 338, 0.0110, 0.00),
+ ( 804, 0.0220, 3.72),
+ ( 1268, 0.0440, 13.97),
+ ( 1760, 0.0660, 34.39),
+ ( 2224, 0.0880, 66.86),
+ (11360, 0.1023, 107.69),
+ (13632, 0.1133, 1042.30),
+ (19231, 0.1243, 1299.72),
+ (22721, 0.1353, 1995.68),
+ ('inf', 0.1463, 2467.88),
+ ),
+ 'bi-weekly': (
+ ( 676, 0.0110, 0.00),
+ ( 1608, 0.0220, 7.44),
+ ( 2536, 0.0440, 27.94),
+ ( 3520, 0.0660, 68.77),
+ ( 4448, 0.0880, 124.70),
+ (21212, 0.1023, 201.08),
+ (25452, 0.1133, 1946.32),
+ (38462, 0.1243, 2426.71),
+ (42420, 0.1353, 4043.85),
+ ('inf', 0.1463, 4579.37),
+ ),
+ 'semi-monthly': (
+ ( 734, 0.0110, 0.00),
+ ( 1740, 0.0220, 8.07),
+ ( 2746, 0.0440, 30.20),
+ ( 3812, 0.0660, 74.46),
+ ( 4818, 0.0880, 144.82),
+ (24614, 0.1023, 233.35),
+ (29538, 0.1133, 2258.48),
+ (41667, 0.1243, 2816.37),
+ (49229, 0.1353, 4324.00),
+ ('inf', 0.1463, 5347.14),
+ ),
+ 'monthly': (
+ ( 1468, 0.0110, 0.00),
+ ( 3480, 0.0220, 16.15),
+ ( 5492, 0.0440, 60.41),
+ ( 7624, 0.0660, 148.94),
+ ( 9636, 0.0880, 289.65),
+ (49228, 0.1023, 466.71),
+ (59076, 0.1133, 4516.97),
+ (83334, 0.1243, 5632.75),
+ (98458, 0.1353, 8648.02),
+ ('inf', 0.1463, 10694.30),
+ ),
+ 'quarterly': (
+ ( 4404, 0.0110, 0.00),
+ ( 10442, 0.0220, 48.44),
+ ( 16480, 0.0440, 181.28),
+ ( 22876, 0.0660, 446.95),
+ ( 28912, 0.0880, 869.09),
+ (147686, 0.1023, 1400.26),
+ (177222, 0.1133, 13550.84),
+ (250000, 0.1243, 16897.27),
+ (295371, 0.1353, 25943.58),
+ ( 'inf', 0.1463, 32082.28),
+ ),
+ 'semi-annual': (
+ ( 8808, 0.0110, 0.00),
+ ( 20884, 0.0220, 96.89),
+ ( 32960, 0.0440, 362.56),
+ ( 45752, 0.0660, 893.90),
+ ( 57824, 0.0880, 1738.17),
+ (295372, 0.1023, 2800.51),
+ (354444, 0.1133, 27101.67),
+ (500000, 0.1243, 33794.53),
+ (590742, 0.1353, 51887.14),
+ ( 'inf', 0.1463, 64164.53),
+ ),
+ 'annually': (
+ ( 17618, 0.0110, 0.00),
+ ( 41766, 0.0220, 193.80),
+ ( 65920, 0.0440, 725.06),
+ ( 91506, 0.0660, 1787.84),
+ ( 115648, 0.0880, 3476.52),
+ ( 590746, 0.1023, 5601.02),
+ ( 708890, 0.1133, 54203.55),
+ (1000000, 0.1243, 67589.27),
+ (1181484, 0.1353, 103774.24),
+ ( 'inf', 0.1463, 128329.03),
+ ),
+ },
+ 'single': {
+ 'weekly': (
+ ( 169, 0.0110, 0.00),
+ ( 402, 0.0220, 1.86),
+ ( 634, 0.0440, 6.99),
+ ( 880, 0.0660, 17.20),
+ ( 1112, 0.0880, 33.44),
+ ( 5680, 0.1023, 53.86),
+ ( 6816, 0.1133, 521.17),
+ (11360, 0.1243, 649.88),
+ (19231, 0.1353, 1214.70),
+ ('inf', 0.1463, 2279.65),
+ ),
+ 'bi-weekly': (
+ ( 338, 0.0110, 0.00),
+ ( 804, 0.0220, 3.72),
+ ( 1268, 0.0440, 13.97),
+ ( 1760, 0.0660, 34.39),
+ ( 2224, 0.0880, 66.86),
+ (11360, 0.1023, 107.69),
+ (13632, 0.1133, 1042.30),
+ (22720, 0.1243, 1299.72),
+ (38462, 0.1353, 2429.36),
+ ('inf', 0.1463, 4559.25),
+ ),
+ 'semi-monthly': (
+ ( 367, 0.0110, 0.00),
+ ( 870, 0.0220, 4.04),
+ ( 1373, 0.0440, 15.11),
+ ( 1906, 0.0660, 37.24),
+ ( 2409, 0.0880, 72.42),
+ (12307, 0.1023, 116.68),
+ (14769, 0.1133, 1129.25),
+ (24614, 0.1243, 1408.19),
+ (41667, 0.1353, 2631.92),
+ ('inf', 0.1463, 4939.19),
+ ),
+ 'monthly': (
+ ( 734, 0.0110, 0.00),
+ ( 1740, 0.0220, 8.07),
+ ( 2746, 0.0440, 30.20),
+ ( 3812, 0.0660, 74.46),
+ ( 4818, 0.0880, 144.82),
+ (24614, 0.1023, 233.35),
+ (29538, 0.1133, 2258.48),
+ (49228, 0.1243, 2816.37),
+ (83334, 0.1353, 5263.84),
+ ('inf', 0.1463, 9878.38),
+ ),
+ 'quarterly': (
+ ( 2202, 0.0110, 0.00),
+ ( 5221, 0.0220, 24.22),
+ ( 8240, 0.0440, 90.64),
+ ( 11438, 0.0660, 223.48),
+ ( 14456, 0.0880, 434.55),
+ ( 73843, 0.1023, 700.13),
+ ( 88611, 0.1133, 6775.42),
+ (147686, 0.1243, 8448.63),
+ (250000, 0.1353, 15791.65),
+ ( 'inf', 0.1463, 29634.73),
+ ),
+ 'semi-annual': (
+ ( 4404, 0.0110, 0.00),
+ ( 10442, 0.0220, 48.44),
+ ( 16480, 0.0440, 181.28),
+ ( 22876, 0.0660, 446.95),
+ ( 28912, 0.0880, 869.09),
+ (147686, 0.1023, 1400.26),
+ (177222, 0.1133, 13550.84),
+ (295372, 0.1243, 16897.27),
+ (500000, 0.1353, 31583.32),
+ ( 'inf', 0.1463, 59269.49),
+ ),
+ 'annually': (
+ ( 8809, 0.0110, 0.00),
+ ( 20883, 0.0220, 96.90),
+ ( 32960, 0.0440, 362.53),
+ ( 45753, 0.0660, 893.92),
+ ( 57824, 0.0880, 1738.26),
+ ( 295373, 0.1023, 2800.51),
+ ( 354445, 0.1133, 27101.77),
+ ( 590742, 0.1243, 33794.63),
+ (1000000, 0.1353, 63166.35),
+ ( 'inf', 0.1463, 118538.96),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US CA California Low Income Exemption Rate
+ us_ca_sit_income_exemption_rate
+
+
+
+
+ {
+ 'weekly': ( 280, 280, 561, 561),
+ 'bi-weekly': ( 561, 561, 1121, 1121),
+ 'semi-monthly': ( 607, 607, 1214, 1214),
+ 'monthly': ( 1214, 1214, 2429, 2429),
+ 'quarterly': ( 3643, 3643, 7287, 7287),
+ 'semi-annual': ( 7287, 7287, 14573, 14573),
+ 'annually': (14573, 14573, 29146, 29146),
+ }
+
+
+
+
+
+
+ {
+ 'weekly': ( 289, 289, 579, 579),
+ 'bi-weekly': ( 579, 579, 1157, 1157),
+ 'semi-monthly': ( 627, 627, 1253, 1253),
+ 'monthly': ( 1254, 1254, 2507, 2507),
+ 'quarterly': ( 3761, 3761, 7521, 7521),
+ 'semi-annual': ( 7521, 7521, 15042, 15042),
+ 'annually': (15042, 15042, 30083, 30083),
+ }
+
+
+
+
+
+
+ US CA California Estimated Deduction Rate
+ us_ca_sit_estimated_deduction_rate
+
+
+
+
+ {
+ 'weekly': ( 19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
+ 'bi-weekly': ( 38, 77, 115, 154, 192, 231, 269, 308, 346, 385),
+ 'semi-monthly': ( 42, 83, 125, 167, 208, 250, 292, 333, 375, 417),
+ 'monthly': ( 83, 167, 250, 333, 417, 500, 583, 667, 750, 833),
+ 'quarterly': ( 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500),
+ 'semi-annual': ( 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000),
+ 'annually': (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
+ }
+
+
+
+
+
+
+ {
+ 'weekly': ( 19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
+ 'bi-weekly': ( 38, 77, 115, 154, 192, 231, 269, 308, 346, 385),
+ 'semi-monthly': ( 42, 83, 125, 167, 208, 250, 292, 333, 375, 417),
+ 'monthly': ( 83, 167, 250, 333, 417, 500, 583, 667, 750, 833),
+ 'quarterly': ( 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500),
+ 'semi-annual': ( 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000),
+ 'annually': (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
+ }
+
+
+
+
+
+
+ US CA California Standard Deduction Rate
+ us_ca_sit_standard_deduction_rate
+
+
+
+
+ {
+ 'weekly': ( 85, 85, 169, 169),
+ 'bi-weekly': ( 169, 169, 339, 339),
+ 'semi-monthly': ( 183, 183, 367, 367),
+ 'monthly': ( 367, 367, 734, 734),
+ 'quarterly': (1100, 1100, 2201, 2201),
+ 'semi-annual': (2201, 2201, 4401, 4401),
+ 'annually': (4401, 4401, 8802, 8802),
+ }
+
+
+
+
+
+
+ {
+ 'weekly': ( 87, 87, 175, 175),
+ 'bi-weekly': ( 175, 175, 349, 349),
+ 'semi-monthly': ( 189, 189, 378, 378),
+ 'monthly': ( 378, 378, 756, 756),
+ 'quarterly': (1134, 1134, 2269, 2269),
+ 'semi-annual': (2269, 2269, 4537, 4537),
+ 'annually': (4537, 4537, 9074, 9074),
+ }
+
+
+
+
+
+
+ US CA California Exemption Allowance Rate
+ us_ca_sit_exemption_allowance_rate
+
+
+
+
+ {
+ 'weekly': ( 2.41, 4.82, 7.23, 9.65, 12.06, 14.47, 16.88, 19.29, 21.70, 24.12),
+ 'bi-weekly': ( 4.82, 9.65, 14.47, 19.29, 24.12, 28.94, 33.76, 38.58, 43.41, 48.23),
+ 'semi-monthly': ( 5.23, 10.45, 15.68, 20.90, 26.13, 31.35, 36.58, 41.80, 47.03, 52.25),
+ 'monthly': ( 10.45, 20.90, 31.35, 41.80, 52.25, 62.70, 73.15, 83.60, 94.05, 104.50),
+ 'quarterly': ( 31.35, 62.70, 94.05, 125.40, 156.75, 188.10, 219.45, 250.80, 282.15, 313.50),
+ 'semi-annual': ( 62.70, 125.40, 188.10, 250.80, 313.50, 376.20, 438.90, 501.60, 564.30, 627.00),
+ 'annually': (125.40, 250.80, 376.20, 501.60, 627.00, 752.40, 877.80, 1003.20, 1128.60, 1254.00),
+ }
+
+
+
+
+
+
+ {
+ 'weekly': ( 2.58, 5.16, 7.74, 10.32, 12.90, 15.48, 18.07, 20.65, 23.23, 25.81),
+ 'bi-weekly': ( 5.16, 10.32, 15.48, 20.65, 25.81, 30.97, 36.13, 41.29, 46.45, 51.62),
+ 'semi-monthly': ( 5.59, 11.18, 16.78, 22.37, 27.96, 33.55, 39.14, 44.73, 50.33, 55.92),
+ 'monthly': ( 11.18, 22.37, 33.55, 44.73, 55.92, 67.10, 78.28, 89.47, 100.65, 111.83),
+ 'quarterly': ( 33.55, 67.10, 100.65, 134.20, 167.75, 201.30, 234.85, 268.40, 301.95, 335.50),
+ 'semi-annual': ( 67.10, 134.20, 201.30, 268.40, 335.50, 402.60, 469.70, 536.80, 603.90, 671.00),
+ 'annually': (134.20, 268.40, 402.60, 536.80, 671.00, 805.20, 939.40, 1073.60, 1207.80, 1342.00),
+ }
+
+
+
+
+
+
+
+ US California - Department of Taxation (CA DE88) - Unemployment Tax
+
+
+
+ US California - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US CA California State Unemployment
+ ER_US_CA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_rate', state_code='CA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_rate', state_code='CA')
+
+
+
+
+
+
+
+
+ ER: US CA California State Employee Training Tax
+ ER_US_CA_SUTA_ETT
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_ett_rate', state_code='CA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_ett_rate', state_code='CA')
+
+
+
+
+
+
+
+
+ EE: US CA California State Disability Insurance
+ EE_US_CA_SUTA_SDI
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_sdi_rate', state_code='CA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_sdi_rate', state_code='CA')
+
+
+
+
+
+
+
+
+ EE: US CA California State Income Tax Withholding
+ EE_US_CA_SIT
+ python
+ result, _ = ca_california_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ca_california_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/co_colorado.xml b/l10n_us_hr_payroll/data/state/co_colorado.xml
new file mode 100644
index 00000000..a37ee1b9
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/co_colorado.xml
@@ -0,0 +1,97 @@
+
+
+
+
+ US CO Colorado SUTA Wage Base
+ us_co_suta_wage_base
+
+
+
+
+ 13600.0
+
+
+
+
+
+
+
+ US CO Colorado SUTA Rate
+ us_co_suta_rate
+
+
+
+
+ 1.7
+
+
+
+
+
+
+ US CO Colorado SIT Tax Rate
+ us_co_sit_tax_rate
+
+
+
+
+ 4.63
+
+
+
+
+
+
+ US CO Colorado SIT Exemption Rate
+ us_co_sit_exemption_rate
+
+
+
+
+ 4000
+
+
+
+
+
+
+
+ US Colorado - Department of Labor and Employment - Unemployment Tax
+
+
+
+ US Colorado - Division of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US CO Colorado State Unemployment
+ ER_US_CO_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_co_suta_wage_base', rate='us_co_suta_rate', state_code='CO')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_co_suta_wage_base', rate='us_co_suta_rate', state_code='CO')
+
+
+
+
+
+
+
+
+ EE: US CO Colorado State Income Tax Withholding
+ EE_US_CO_SIT
+ python
+ result, _ = co_colorado_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = co_colorado_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ct_connecticut.xml b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
new file mode 100644
index 00000000..c4bf1dd1
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
@@ -0,0 +1,1237 @@
+
+
+
+
+ US CT Connecticut SUTA Wage Base
+ us_ct_suta_wage_base
+
+
+
+
+ 15000.0
+
+
+
+
+ 15000.0
+
+
+
+
+
+
+
+ US CT Connecticut SUTA Rate
+ us_ct_suta_rate
+
+
+
+
+ 3.4
+
+
+
+
+ 3.2
+
+
+
+
+
+
+ US CT Connecticut SIT Initial Tax Rate
+ us_ct_sit_initial_tax_rate
+
+
+
+
+ {
+ 'a': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'b': [
+ ( 16000, 0, 3.00),
+ ( 80000, 480, 5.00),
+ (160000, 3680, 5.50),
+ (320000, 8080, 6.00),
+ (400000, 17680, 6.50),
+ (800000, 22880, 6.90),
+ ( 'inf', 50480, 6.99),
+ ],
+ 'c': [
+ ( 20000, 0, 3.00),
+ ( 100000, 600, 5.00),
+ ( 200000, 4600, 5.50),
+ ( 400000, 10100, 6.00),
+ ( 500000, 22100, 6.50),
+ (1000000, 28600, 6.90),
+ ( 'inf', 63100, 6.99),
+ ],
+ 'd': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'f': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ }
+
+
+
+
+
+
+ {
+ 'a': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'b': [
+ ( 16000, 0, 3.00),
+ ( 80000, 480, 5.00),
+ (160000, 3680, 5.50),
+ (320000, 8080, 6.00),
+ (400000, 17680, 6.50),
+ (800000, 22880, 6.90),
+ ( 'inf', 50480, 6.99),
+ ],
+ 'c': [
+ ( 20000, 0, 3.00),
+ ( 100000, 600, 5.00),
+ ( 200000, 4600, 5.50),
+ ( 400000, 10100, 6.00),
+ ( 500000, 22100, 6.50),
+ (1000000, 28600, 6.90),
+ ( 'inf', 63100, 6.99),
+ ],
+ 'd': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'f': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Tax Rate
+ us_ct_sit_tax_rate
+
+
+
+
+ {
+ 'a': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'b': [
+ ( 78500, 0),
+ ( 82500, 32),
+ ( 86500, 64),
+ ( 90500, 96),
+ ( 94500, 128),
+ ( 98500, 160),
+ (102500, 192),
+ (106500, 224),
+ (110500, 256),
+ (114500, 288),
+ ( 'inf', 320),
+ ],
+ 'c': [
+ (100500, 0),
+ (105500, 40),
+ (110500, 80),
+ (115500, 120),
+ (120500, 160),
+ (125500, 200),
+ (130500, 240),
+ (135500, 280),
+ (140500, 320),
+ (145500, 360),
+ ( 'inf', 400),
+
+ ],
+ 'd': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'f': [
+ ( 56500, 0),
+ ( 61500, 20),
+ ( 66500, 40),
+ ( 71500, 60),
+ ( 76500, 80),
+ ( 81500, 100),
+ ( 86500, 120),
+ ( 91500, 140),
+ ( 96500, 160),
+ (101500, 180),
+ ( 'inf', 200),
+ ],
+ }
+
+
+
+
+
+
+ {
+ 'a': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'b': [
+ ( 78500, 0),
+ ( 82500, 32),
+ ( 86500, 64),
+ ( 90500, 96),
+ ( 94500, 128),
+ ( 98500, 160),
+ (102500, 192),
+ (106500, 224),
+ (110500, 256),
+ (114500, 288),
+ ( 'inf', 320),
+ ],
+ 'c': [
+ (100500, 0),
+ (105500, 40),
+ (110500, 80),
+ (115500, 120),
+ (120500, 160),
+ (125500, 200),
+ (130500, 240),
+ (135500, 280),
+ (140500, 320),
+ (145500, 360),
+ ( 'inf', 400),
+
+ ],
+ 'd': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'f': [
+ ( 56500, 0),
+ ( 61500, 20),
+ ( 66500, 40),
+ ( 71500, 60),
+ ( 76500, 80),
+ ( 81500, 100),
+ ( 86500, 120),
+ ( 91500, 140),
+ ( 96500, 160),
+ (101500, 180),
+ ( 'inf', 200),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Decimal Rate
+ us_ct_sit_decimal_rate
+
+
+
+
+ {
+ 'a': [
+ (15000, 0.75),
+ (15500, 0.70),
+ (16000, 0.65),
+ (16500, 0.60),
+ (17000, 0.55),
+ (17500, 0.50),
+ (18000, 0.45),
+ (18500, 0.40),
+ (20000, 0.35),
+ (20500, 0.30),
+ (21000, 0.25),
+ (21500, 0.20),
+ (25000, 0.15),
+ (25500, 0.14),
+ (26000, 0.13),
+ (26500, 0.12),
+ (27000, 0.11),
+ (48000, 0.10),
+ (48500, 0.09),
+ (49000, 0.08),
+ (49500, 0.08),
+ (50000, 0.06),
+ (50500, 0.05),
+ (51000, 0.03),
+ (51500, 0.03),
+ (52000, 0.02),
+ (52500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'b': [
+ (24000, 0.75),
+ (24500, 0.70),
+ (25000, 0.65),
+ (25500, 0.60),
+ (26000, 0.55),
+ (26500, 0.50),
+ (27000, 0.45),
+ (27500, 0.40),
+ (34000, 0.35),
+ (34500, 0.30),
+ (35000, 0.25),
+ (35500, 0.20),
+ (44000, 0.15),
+ (44500, 0.14),
+ (45000, 0.13),
+ (45500, 0.12),
+ (46000, 0.11),
+ (74000, 0.10),
+ (74500, 0.09),
+ (75000, 0.08),
+ (75500, 0.08),
+ (76000, 0.06),
+ (76500, 0.05),
+ (77000, 0.03),
+ (77500, 0.03),
+ (78000, 0.02),
+ (78500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'c': [
+ (30000, 0.75),
+ (30500, 0.70),
+ (31000, 0.65),
+ (31500, 0.60),
+ (32000, 0.55),
+ (32500, 0.50),
+ (33000, 0.45),
+ (33500, 0.40),
+ (40000, 0.35),
+ (40500, 0.30),
+ (41000, 0.25),
+ (41500, 0.20),
+ (50000, 0.15),
+ (50500, 0.14),
+ (51000, 0.13),
+ (51500, 0.12),
+ (52000, 0.11),
+ (96000, 0.10),
+ (96500, 0.09),
+ (97000, 0.08),
+ (97500, 0.08),
+ (98000, 0.06),
+ (98500, 0.05),
+ (99000, 0.03),
+ (99500, 0.03),
+ (100000, 0.02),
+ (100500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'f': [
+ (18800, 0.75),
+ (19300, 0.70),
+ (19800, 0.65),
+ (20300, 0.60),
+ (20800, 0.55),
+ (21300, 0.50),
+ (21800, 0.45),
+ (22300, 0.40),
+ (25000, 0.35),
+ (25500, 0.30),
+ (26000, 0.25),
+ (26500, 0.20),
+ (31300, 0.15),
+ (31800, 0.14),
+ (32300, 0.13),
+ (32800, 0.12),
+ (33300, 0.11),
+ (60000, 0.10),
+ (60500, 0.09),
+ (61000, 0.08),
+ (61500, 0.08),
+ (62000, 0.06),
+ (62500, 0.05),
+ (63000, 0.03),
+ (63500, 0.03),
+ (64000, 0.02),
+ (64500, 0.01),
+ ('inf', 0.00),
+ ],
+ }
+
+
+
+
+
+
+ {
+ 'a': [
+ (15000, 0.75),
+ (15500, 0.70),
+ (16000, 0.65),
+ (16500, 0.60),
+ (17000, 0.55),
+ (17500, 0.50),
+ (18000, 0.45),
+ (18500, 0.40),
+ (20000, 0.35),
+ (20500, 0.30),
+ (21000, 0.25),
+ (21500, 0.20),
+ (25000, 0.15),
+ (25500, 0.14),
+ (26000, 0.13),
+ (26500, 0.12),
+ (27000, 0.11),
+ (48000, 0.10),
+ (48500, 0.09),
+ (49000, 0.08),
+ (49500, 0.08),
+ (50000, 0.06),
+ (50500, 0.05),
+ (51000, 0.03),
+ (51500, 0.03),
+ (52000, 0.02),
+ (52500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'b': [
+ (24000, 0.75),
+ (24500, 0.70),
+ (25000, 0.65),
+ (25500, 0.60),
+ (26000, 0.55),
+ (26500, 0.50),
+ (27000, 0.45),
+ (27500, 0.40),
+ (34000, 0.35),
+ (34500, 0.30),
+ (35000, 0.25),
+ (35500, 0.20),
+ (44000, 0.15),
+ (44500, 0.14),
+ (45000, 0.13),
+ (45500, 0.12),
+ (46000, 0.11),
+ (74000, 0.10),
+ (74500, 0.09),
+ (75000, 0.08),
+ (75500, 0.08),
+ (76000, 0.06),
+ (76500, 0.05),
+ (77000, 0.03),
+ (77500, 0.03),
+ (78000, 0.02),
+ (78500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'c': [
+ (30000, 0.75),
+ (30500, 0.70),
+ (31000, 0.65),
+ (31500, 0.60),
+ (32000, 0.55),
+ (32500, 0.50),
+ (33000, 0.45),
+ (33500, 0.40),
+ (40000, 0.35),
+ (40500, 0.30),
+ (41000, 0.25),
+ (41500, 0.20),
+ (50000, 0.15),
+ (50500, 0.14),
+ (51000, 0.13),
+ (51500, 0.12),
+ (52000, 0.11),
+ (96000, 0.10),
+ (96500, 0.09),
+ (97000, 0.08),
+ (97500, 0.08),
+ (98000, 0.06),
+ (98500, 0.05),
+ (99000, 0.03),
+ (99500, 0.03),
+ (100000, 0.02),
+ (100500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'f': [
+ (18800, 0.75),
+ (19300, 0.70),
+ (19800, 0.65),
+ (20300, 0.60),
+ (20800, 0.55),
+ (21300, 0.50),
+ (21800, 0.45),
+ (22300, 0.40),
+ (25000, 0.35),
+ (25500, 0.30),
+ (26000, 0.25),
+ (26500, 0.20),
+ (31300, 0.15),
+ (31800, 0.14),
+ (32300, 0.13),
+ (32800, 0.12),
+ (33300, 0.11),
+ (60000, 0.10),
+ (60500, 0.09),
+ (61000, 0.08),
+ (61500, 0.08),
+ (62000, 0.06),
+ (62500, 0.05),
+ (63000, 0.03),
+ (63500, 0.03),
+ (64000, 0.02),
+ (64500, 0.01),
+ ('inf', 0.00),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Recapture Rate
+ us_ct_sit_recapture_rate
+
+
+
+
+ {
+ 'a': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ 'b': [
+ (320000, 0),
+ (328000, 140),
+ (336000, 280),
+ (344000, 420),
+ (352000, 560),
+ (360000, 700),
+ (368000, 840),
+ (376000, 980),
+ (384000, 1120),
+ (392000, 1260),
+ (400000, 1400),
+ (408000, 1540),
+ (416000, 1680),
+ (424000, 1820),
+ (432000, 1960),
+ (440000, 2100),
+ (448000, 2240),
+ (456000, 2380),
+ (464000, 2520),
+ (472000, 2660),
+ (480000, 2800),
+ (488000, 2940),
+ (496000, 3080),
+ (504000, 3220),
+ (512000, 3360),
+ (520000, 3500),
+ (528000, 3640),
+ (536000, 3780),
+ (544000, 3920),
+ (552000, 4060),
+ (800000, 4200),
+ (808000, 4280),
+ (816000, 4360),
+ (824000, 4440),
+ (832000, 4520),
+ (840000, 4600),
+ (848000, 4680),
+ (856000, 4760),
+ (864000, 4840),
+ ( 'inf', 4920),
+ ],
+ 'c': [
+ ( 400000, 0),
+ ( 410000, 180),
+ ( 420000, 360),
+ ( 430000, 540),
+ ( 440000, 720),
+ ( 450000, 900),
+ ( 460000, 1080),
+ ( 470000, 1260),
+ ( 480000, 1440),
+ ( 490000, 1620),
+ ( 500000, 1800),
+ ( 510000, 1980),
+ ( 520000, 2160),
+ ( 530000, 2340),
+ ( 540000, 2520),
+ ( 550000, 2700),
+ ( 560000, 2880),
+ ( 570000, 3060),
+ ( 580000, 3240),
+ ( 590000, 3420),
+ ( 600000, 3600),
+ ( 610000, 3780),
+ ( 620000, 3960),
+ ( 630000, 4140),
+ ( 640000, 4320),
+ ( 650000, 4500),
+ ( 660000, 4680),
+ ( 670000, 4860),
+ ( 680000, 5040),
+ ( 690000, 5220),
+ (1000000, 5400),
+ (1010000, 5500),
+ (1020000, 5600),
+ (1030000, 5700),
+ (1040000, 5800),
+ (1050000, 5900),
+ (1060000, 6000),
+ (1070000, 6100),
+ (1080000, 6200),
+ ( 'inf', 6300),
+ ],
+ 'd': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ 'f': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ }
+
+
+
+
+
+
+ {
+ 'a': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 3150),
+ ],
+ 'b': [
+ (320000, 0),
+ (328000, 140),
+ (336000, 280),
+ (344000, 420),
+ (352000, 560),
+ (360000, 700),
+ (368000, 840),
+ (376000, 980),
+ (384000, 1120),
+ (392000, 1260),
+ (400000, 1400),
+ (408000, 1540),
+ (416000, 1680),
+ (424000, 1820),
+ (432000, 1960),
+ (440000, 2100),
+ (448000, 2240),
+ (456000, 2380),
+ (464000, 2520),
+ (472000, 2660),
+ (480000, 2800),
+ (488000, 2940),
+ (496000, 3080),
+ (504000, 3220),
+ (512000, 3360),
+ (520000, 3500),
+ (528000, 3640),
+ (536000, 3780),
+ (544000, 3920),
+ (552000, 4060),
+ (800000, 4200),
+ (808000, 4280),
+ (816000, 4360),
+ (824000, 4440),
+ (832000, 4520),
+ (840000, 4600),
+ (848000, 4680),
+ (856000, 4760),
+ (864000, 4840),
+ ( 'inf', 4920),
+ ],
+ 'c': [
+ ( 400000, 0),
+ ( 410000, 180),
+ ( 420000, 360),
+ ( 430000, 540),
+ ( 440000, 720),
+ ( 450000, 900),
+ ( 460000, 1080),
+ ( 470000, 1260),
+ ( 480000, 1440),
+ ( 490000, 1620),
+ ( 500000, 1800),
+ ( 510000, 1980),
+ ( 520000, 2160),
+ ( 530000, 2340),
+ ( 540000, 2520),
+ ( 550000, 2700),
+ ( 560000, 2880),
+ ( 570000, 3060),
+ ( 580000, 3240),
+ ( 590000, 3420),
+ ( 600000, 3600),
+ ( 610000, 3780),
+ ( 620000, 3960),
+ ( 630000, 4140),
+ ( 640000, 4320),
+ ( 650000, 4500),
+ ( 660000, 4680),
+ ( 670000, 4860),
+ ( 680000, 5040),
+ ( 690000, 5220),
+ (1000000, 5400),
+ (1010000, 5500),
+ (1020000, 5600),
+ (1030000, 5700),
+ (1040000, 5800),
+ (1050000, 5900),
+ (1060000, 6000),
+ (1070000, 6100),
+ (1080000, 6200),
+ ( 'inf', 6300),
+ ],
+ 'd': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 3150),
+ ],
+ 'f': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 3150),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Personal Exemption Rate
+ us_ct_sit_personal_exemption_rate
+
+
+
+
+ {
+ 'a' : [
+ (24000, 12000),
+ (25000, 11000),
+ (26000, 10000),
+ (27000, 9000),
+ (28000, 8000),
+ (29000, 7000),
+ (30000, 6000),
+ (31000, 5000),
+ (32000, 4000),
+ (33000, 3000),
+ (34000, 2000),
+ (35000, 1000),
+ ('inf', 0),
+ ],
+ 'b' : [
+ (38000, 19000),
+ (39000, 18000),
+ (40000, 17000),
+ (41000, 16000),
+ (42000, 15000),
+ (43000, 14000),
+ (44000, 13000),
+ (45000, 12000),
+ (46000, 11000),
+ (47000, 10000),
+ (48000, 9000),
+ (49000, 8000),
+ (50000, 7000),
+ (51000, 6000),
+ (52000, 5000),
+ (53000, 4000),
+ (54000, 3000),
+ (55000, 2000),
+ (56000, 1000),
+ ('inf', 0),
+ ],
+ 'c': [
+ (48000, 24000),
+ (49000, 23000),
+ (50000, 22000),
+ (51000, 21000),
+ (52000, 20000),
+ (53000, 19000),
+ (54000, 18000),
+ (55000, 17000),
+ (56000, 16000),
+ (57000, 15000),
+ (58000, 14000),
+ (59000, 13000),
+ (60000, 12000),
+ (61000, 11000),
+ (62000, 10000),
+ (63000, 9000),
+ (64000, 8000),
+ (65000, 7000),
+ (66000, 6000),
+ (67000, 5000),
+ (68000, 4000),
+ (69000, 3000),
+ (70000, 2000),
+ (71000, 1000),
+ ('inf', 0),
+ ],
+ 'f' : [
+ (30000, 15000),
+ (31000, 14000),
+ (22000, 13000),
+ (33000, 12000),
+ (34000, 11000),
+ (35000, 10000),
+ (36000, 9000),
+ (37000, 8000),
+ (38000, 7000),
+ (39000, 6000),
+ (40000, 5000),
+ (41000, 4000),
+ (42000, 3000),
+ (43000, 2000),
+ (44000, 1000),
+ ('inf', 0),
+ ],
+ }
+
+
+
+
+
+
+ {
+ 'a' : [
+ (24000, 12000),
+ (25000, 11000),
+ (26000, 10000),
+ (27000, 9000),
+ (28000, 8000),
+ (29000, 7000),
+ (30000, 6000),
+ (31000, 5000),
+ (32000, 4000),
+ (33000, 3000),
+ (34000, 2000),
+ (35000, 1000),
+ ('inf', 0),
+ ],
+ 'b' : [
+ (38000, 19000),
+ (39000, 18000),
+ (40000, 17000),
+ (41000, 16000),
+ (42000, 15000),
+ (43000, 14000),
+ (44000, 13000),
+ (45000, 12000),
+ (46000, 11000),
+ (47000, 10000),
+ (48000, 9000),
+ (49000, 8000),
+ (50000, 7000),
+ (51000, 6000),
+ (52000, 5000),
+ (53000, 4000),
+ (54000, 3000),
+ (55000, 2000),
+ (56000, 1000),
+ ('inf', 0),
+ ],
+ 'c': [
+ (48000, 24000),
+ (49000, 23000),
+ (50000, 22000),
+ (51000, 21000),
+ (52000, 20000),
+ (53000, 19000),
+ (54000, 18000),
+ (55000, 17000),
+ (56000, 16000),
+ (57000, 15000),
+ (58000, 14000),
+ (59000, 13000),
+ (60000, 12000),
+ (61000, 11000),
+ (62000, 10000),
+ (63000, 9000),
+ (64000, 8000),
+ (65000, 7000),
+ (66000, 6000),
+ (67000, 5000),
+ (68000, 4000),
+ (69000, 3000),
+ (70000, 2000),
+ (71000, 1000),
+ ('inf', 0),
+ ],
+ 'f' : [
+ (30000, 15000),
+ (31000, 14000),
+ (22000, 13000),
+ (33000, 12000),
+ (34000, 11000),
+ (35000, 10000),
+ (36000, 9000),
+ (37000, 8000),
+ (38000, 7000),
+ (39000, 6000),
+ (40000, 5000),
+ (41000, 4000),
+ (42000, 3000),
+ (43000, 2000),
+ (44000, 1000),
+ ('inf', 0),
+ ],
+ }
+
+
+
+
+
+
+
+ US Connecticut - Department of Labor (CDOL) - Unemployment Tax
+
+
+
+ US Connecticut - Department of Revenue Services (CDRS) - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US CT Connecticut State Unemployment
+ ER_US_CT_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ct_suta_wage_base', rate='us_ct_suta_rate', state_code='CT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ct_suta_wage_base', rate='us_ct_suta_rate', state_code='CT')
+
+
+
+
+
+
+
+
+ EE: US CT Connecticut State Income Tax Withholding
+ EE_US_CT_SIT
+ python
+ result, _ = ct_connecticut_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ct_connecticut_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/de_delaware.xml b/l10n_us_hr_payroll/data/state/de_delaware.xml
new file mode 100644
index 00000000..fad2abf6
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/de_delaware.xml
@@ -0,0 +1,119 @@
+
+
+
+
+ US DE Delaware SUTA Wage Base
+ us_de_suta_wage_base
+
+
+
+
+ 16500.0
+
+
+
+
+
+
+
+ US DE Delaware SUTA Rate
+ us_de_suta_rate
+
+
+
+
+ 1.50
+
+
+
+
+
+
+ US DE Delaware SIT Tax Rate
+ us_de_sit_tax_rate
+
+
+
+
+ [
+ ( 2000, 0.0, 0.00),
+ ( 5000, 0.0, 2.20),
+ (10000, 66.0, 3.90),
+ (20000, 261.0, 4.80),
+ (25000, 741.0, 5.20),
+ (60000, 1001.0, 5.55),
+ ('inf', 2943.0, 6.60),
+
+ ]
+
+
+
+
+
+
+ US DE Delaware Standard Deduction Rate
+ us_de_sit_standard_deduction_rate
+
+
+
+
+ 3250
+
+
+
+
+
+
+ US DE Delaware Personal Exemption Rate
+ us_de_sit_personal_exemption_rate
+
+
+
+
+ 110
+
+
+
+
+
+
+
+ US Delaware - Division of Unemployment Insurance - Unemployment Tax
+
+
+
+ US Delaware - Division of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US DE Delaware State Unemployment
+ ER_US_DE_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_de_suta_wage_base', rate='us_de_suta_rate', state_code='DE')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_de_suta_wage_base', rate='us_de_suta_rate', state_code='DE')
+
+
+
+
+
+
+
+
+ EE: US DE Delaware State Income Tax Withholding
+ EE_US_DE_SIT
+ python
+ result, _ = de_delaware_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = de_delaware_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/fl_florida.xml b/l10n_us_hr_payroll/data/state/fl_florida.xml
new file mode 100644
index 00000000..8002a2ee
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/fl_florida.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ US FL Florida SUTA Wage Base
+ us_fl_suta_wage_base
+
+
+
+
+ 7000.00
+
+
+
+
+ 7000.00
+
+
+
+
+
+
+
+ US FL Florida SUTA Rate
+ us_fl_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+ 2.7
+
+
+
+
+
+
+
+ US Florida - Department of Revenue
+
+
+
+
+
+
+
+
+
+ ER: US FL Florida State Unemployment (RT-6)
+ ER_US_FL_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_fl_suta_wage_base', rate='us_fl_suta_rate', state_code='FL')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_fl_suta_wage_base', rate='us_fl_suta_rate', state_code='FL')
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ga_georgia.xml b/l10n_us_hr_payroll/data/state/ga_georgia.xml
new file mode 100644
index 00000000..659515db
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ga_georgia.xml
@@ -0,0 +1,943 @@
+
+
+
+
+ US GA Georgia SUTA Wage Base
+ us_ga_suta_wage_base
+
+
+
+
+ 9500.00
+
+
+
+
+ 9500.00
+
+
+
+
+
+
+
+ US GA Georgia SUTA Rate
+ us_ga_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US GA Georgia SIT Rate Table
+ us_ga_sit_rate
+
+
+
+
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
+ 'single': {
+ 'weekly': (
+ ( 14.50, 0.00, 1.00),
+ ( 43.50, 0.14, 2.00),
+ ( 72.00, 0.72, 3.00),
+ (101.00, 1.59, 4.00),
+ (135.00, 2.74, 5.00),
+ ( 'inf', 4.42, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 29.00, 0.00, 1.00),
+ ( 86.50, 0.29, 2.00),
+ (144.00, 1.44, 3.00),
+ (202.00, 3.17, 4.00),
+ (269.00, 5.48, 5.00),
+ ( 'inf', 8.85, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 31.00, 0.00, 1.00),
+ ( 93.50, 0.31, 2.00),
+ (156.00, 1.56, 3.00),
+ (219.00, 3.34, 4.00),
+ (292.00, 5.94, 5.00),
+ ( 'inf', 9.58, 5.75),
+ ),
+ 'monthly': (
+ ( 62.50, 0.00, 1.00),
+ (187.00, 0.62, 2.00),
+ (312.00, 3.12, 3.00),
+ (437.00, 6.87, 4.00),
+ (583.00, 11.87, 5.00),
+ ( 'inf', 19.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 187.50, 0.00, 1.00),
+ ( 562.50, 1.88, 2.00),
+ ( 937.50, 9.38, 3.00),
+ (1312.00, 20.63, 4.00),
+ (1750.00, 35.63, 5.00),
+ ( 'inf', 57.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 375.00, 0.00, 1.00),
+ (1125.00, 3.75, 2.00),
+ (1875.00, 18.75, 3.00),
+ (2625.00, 41.25, 4.00),
+ (3500.00, 71.25, 5.00),
+ ( 'inf', 115.00, 5.75),
+ ),
+ 'annual': (
+ ( 750.00, 0.00, 1.00),
+ (2250.00, 7.50, 2.00),
+ (3750.00, 37.50, 3.00),
+ (5250.00, 82.50, 4.00),
+ (7000.00, 142.50, 5.00),
+ ( 'inf', 230.00, 5.75),
+ ),
+ },
+ 'head of household': {
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
+ 'married filing separate': {
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },
+ }
+
+
+
+
+
+
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
+ 'single': {
+ 'weekly': (
+ ( 14.50, 0.00, 1.00),
+ ( 43.50, 0.14, 2.00),
+ ( 72.00, 0.72, 3.00),
+ (101.00, 1.59, 4.00),
+ (135.00, 2.74, 5.00),
+ ( 'inf', 4.42, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 29.00, 0.00, 1.00),
+ ( 86.50, 0.29, 2.00),
+ (144.00, 1.44, 3.00),
+ (202.00, 3.17, 4.00),
+ (269.00, 5.48, 5.00),
+ ( 'inf', 8.85, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 31.00, 0.00, 1.00),
+ ( 93.50, 0.31, 2.00),
+ (156.00, 1.56, 3.00),
+ (219.00, 3.34, 4.00),
+ (292.00, 5.94, 5.00),
+ ( 'inf', 9.58, 5.75),
+ ),
+ 'monthly': (
+ ( 62.50, 0.00, 1.00),
+ (187.00, 0.62, 2.00),
+ (312.00, 3.12, 3.00),
+ (437.00, 6.87, 4.00),
+ (583.00, 11.87, 5.00),
+ ( 'inf', 19.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 187.50, 0.00, 1.00),
+ ( 562.50, 1.88, 2.00),
+ ( 937.50, 9.38, 3.00),
+ (1312.00, 20.63, 4.00),
+ (1750.00, 35.63, 5.00),
+ ( 'inf', 57.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 375.00, 0.00, 1.00),
+ (1125.00, 3.75, 2.00),
+ (1875.00, 18.75, 3.00),
+ (2625.00, 41.25, 4.00),
+ (3500.00, 71.25, 5.00),
+ ( 'inf', 115.00, 5.75),
+ ),
+ 'annual': (
+ ( 750.00, 0.00, 1.00),
+ (2250.00, 7.50, 2.00),
+ (3750.00, 37.50, 3.00),
+ (5250.00, 82.50, 4.00),
+ (7000.00, 142.50, 5.00),
+ ( 'inf', 230.00, 5.75),
+ ),
+ },
+ 'head of household': {
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
+ 'married filing separate': {
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US GA Georgia SIT Personal Allowance
+ us_ga_sit_personal_allowance
+
+
+
+
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': 142.30,
+ 'bi-weekly': 284.62,
+ 'semi-monthly': 308.33,
+ 'monthly': 616.67,
+ 'quarterly': 1850.00,
+ 'semi-annual': 3700.00,
+ 'annual': 7400.00,
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': 142.30,
+ 'bi-weekly': 284.62,
+ 'semi-monthly': 308.33,
+ 'monthly': 616.67,
+ 'quarterly': 1850.00,
+ 'semi-annual': 3700.00,
+ 'annual': 7400.00,
+ },
+ 'single': {
+ 'weekly': 51.92,
+ 'bi-weekly': 103.85,
+ 'semi-monthly': 112.50,
+ 'monthly': 225.00,
+ 'quarterly': 675.00,
+ 'semi-annual': 1350.00,
+ 'annual': 2700.00,
+ },
+ 'head of household': {
+ 'weekly': 51.92,
+ 'bi-weekly': 103.85,
+ 'semi-monthly': 112.50,
+ 'monthly': 225.00,
+ 'quarterly': 675.00,
+ 'semi-annual': 1350.00,
+ 'annual': 2700.00,
+ },
+ 'married filing separate': {
+ 'weekly': 71.15,
+ 'bi-weekly': 142.30,
+ 'semi-monthly': 154.16,
+ 'monthly': 308.33,
+ 'quarterly': 925.00,
+ 'semi-annual': 1850.00,
+ 'annual': 3700.00,
+ },
+ }
+
+
+
+
+
+
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': 142.30,
+ 'bi-weekly': 284.62,
+ 'semi-monthly': 308.33,
+ 'monthly': 616.67,
+ 'quarterly': 1850.00,
+ 'semi-annual': 3700.00,
+ 'annual': 7400.00,
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': 142.30,
+ 'bi-weekly': 284.62,
+ 'semi-monthly': 308.33,
+ 'monthly': 616.67,
+ 'quarterly': 1850.00,
+ 'semi-annual': 3700.00,
+ 'annual': 7400.00,
+ },
+ 'single': {
+ 'weekly': 51.92,
+ 'bi-weekly': 103.85,
+ 'semi-monthly': 112.50,
+ 'monthly': 225.00,
+ 'quarterly': 675.00,
+ 'semi-annual': 1350.00,
+ 'annual': 2700.00,
+ },
+ 'head of household': {
+ 'weekly': 51.92,
+ 'bi-weekly': 103.85,
+ 'semi-monthly': 112.50,
+ 'monthly': 225.00,
+ 'quarterly': 675.00,
+ 'semi-annual': 1350.00,
+ 'annual': 2700.00,
+ },
+ 'married filing separate': {
+ 'weekly': 71.15,
+ 'bi-weekly': 142.30,
+ 'semi-monthly': 154.16,
+ 'monthly': 308.33,
+ 'quarterly': 925.00,
+ 'semi-annual': 1850.00,
+ 'annual': 3700.00,
+ },
+ }
+
+
+
+
+
+
+ US GA Georgia SIT Dependent Allowance Rate
+ us_ga_sit_dependent_allowance_rate
+
+
+
+
+ {
+ 'weekly': 57.50,
+ 'bi-weekly': 115.00,
+ 'semi-monthly': 125.00,
+ 'monthly': 250.00,
+ 'quarterly': 750.00,
+ 'semi-annual': 1500.00,
+ 'annual': 3000.00,
+ }
+
+
+
+
+
+
+ {
+ 'weekly': 57.50,
+ 'bi-weekly': 115.00,
+ 'semi-monthly': 125.00,
+ 'monthly': 250.00,
+ 'quarterly': 750.00,
+ 'semi-annual': 1500.00,
+ 'annual': 3000.00,
+ }
+
+
+
+
+
+
+ US GA Georgia SIT Deduction
+ us_ga_sit_deduction
+
+
+
+
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': 115.50,
+ 'bi-weekly': 230.75,
+ 'semi-monthly': 250.00,
+ 'monthly': 500.00,
+ 'quarterly': 1500.00,
+ 'semi-annual': 3000.00,
+ 'annual': 6000.00,
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': 115.50,
+ 'bi-weekly': 230.75,
+ 'semi-monthly': 250.00,
+ 'monthly': 500.00,
+ 'quarterly': 1500.00,
+ 'semi-annual': 3000.00,
+ 'annual': 6000.00,
+ },
+ 'single': {
+ 'weekly': 88.50,
+ 'bi-weekly': 177.00,
+ 'semi-monthly': 191.75,
+ 'monthly': 383.50,
+ 'quarterly': 1150.00,
+ 'semi-annual': 2300.00,
+ 'annual': 4600.00,
+ },
+ 'head of household': {
+ 'weekly': 88.50,
+ 'bi-weekly': 177.00,
+ 'semi-monthly': 191.75,
+ 'monthly': 383.50,
+ 'quarterly': 1150.00,
+ 'semi-annual': 2300.00,
+ 'annual': 4600.00,
+ },
+ 'married filing separate': {
+ 'weekly': 57.75,
+ 'bi-weekly': 115.50,
+ 'semi-monthly': 125.00,
+ 'monthly': 250.00,
+ 'quarterly': 750.00,
+ 'semi-annual': 1500.00,
+ 'annual': 3000.00,
+ },
+ }
+
+
+
+
+
+
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': 115.50,
+ 'bi-weekly': 230.75,
+ 'semi-monthly': 250.00,
+ 'monthly': 500.00,
+ 'quarterly': 1500.00,
+ 'semi-annual': 3000.00,
+ 'annual': 6000.00,
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': 115.50,
+ 'bi-weekly': 230.75,
+ 'semi-monthly': 250.00,
+ 'monthly': 500.00,
+ 'quarterly': 1500.00,
+ 'semi-annual': 3000.00,
+ 'annual': 6000.00,
+ },
+ 'single': {
+ 'weekly': 88.50,
+ 'bi-weekly': 177.00,
+ 'semi-monthly': 191.75,
+ 'monthly': 383.50,
+ 'quarterly': 1150.00,
+ 'semi-annual': 2300.00,
+ 'annual': 4600.00,
+ },
+ 'head of household': {
+ 'weekly': 88.50,
+ 'bi-weekly': 177.00,
+ 'semi-monthly': 191.75,
+ 'monthly': 383.50,
+ 'quarterly': 1150.00,
+ 'semi-annual': 2300.00,
+ 'annual': 4600.00,
+ },
+ 'married filing separate': {
+ 'weekly': 57.75,
+ 'bi-weekly': 115.50,
+ 'semi-monthly': 125.00,
+ 'monthly': 250.00,
+ 'quarterly': 750.00,
+ 'semi-annual': 1500.00,
+ 'annual': 3000.00,
+ },
+ }
+
+
+
+
+
+
+
+ US Georgia - Department of Taxation - Unemployment Tax
+
+
+
+ US Georgia - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US GA Georgia State Unemployment
+ ER_US_GA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ga_suta_wage_base', rate='us_ga_suta_rate', state_code='GA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ga_suta_wage_base', rate='us_ga_suta_rate', state_code='GA')
+
+
+
+
+
+
+
+
+ EE: US GA Georgia State Income Tax Withholding
+ EE_US_GA_SIT
+ python
+ result, _ = ga_georgia_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ga_georgia_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
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..798821f2
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/hi_hawaii.xml
@@ -0,0 +1,181 @@
+
+
+
+
+ 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/data/state/ia_iowa.xml b/l10n_us_hr_payroll/data/state/ia_iowa.xml
new file mode 100644
index 00000000..dcc90009
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ia_iowa.xml
@@ -0,0 +1,303 @@
+
+
+
+
+ US IA Iowa SUTA Wage Base
+ us_ia_suta_wage_base
+
+
+
+
+ 30600.0
+
+
+
+
+ 31600.0
+
+
+
+
+
+
+
+ US IA Iowa SUTA Rate
+ us_ia_suta_rate
+
+
+
+
+ 1.0
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ US IA Iowa SIT Tax Rate
+ us_ia_sit_tax_rate
+
+
+
+
+ {
+ 'daily': (
+ ( 5.13, 0.0033, 0.00),
+ ( 10.25, 0.0067, 0.02),
+ ( 20.50, 0.0225, 0.05),
+ ( 46.13, 0.0414, 0.28),
+ ( 76.89, 0.0563, 1.34),
+ (102.52, 0.0596, 3.07),
+ (153.78, 0.0625, 4.60),
+ (230.68, 0.0744, 7.80),
+ ( 'inf', 0.0853, 13.52),
+ ),
+ 'weekly': (
+ ( 25.63, 0.0033, 0.00),
+ ( 51.27, 0.0067, 0.08),
+ ( 102.52, 0.0225, 0.25),
+ ( 230.67, 0.0414, 1.40),
+ ( 384.46, 0.0563, 6.71),
+ ( 512.62, 0.0596, 15.37),
+ ( 768.92, 0.0625, 23.01),
+ (1153.38, 0.0744, 39.03),
+ ( 'inf', 0.0853, 67.63),
+ ),
+ 'bi-weekly': (
+ ( 51.27, 0.0033, 0.00),
+ ( 102.54, 0.0067, 0.17),
+ ( 205.04, 0.0225, 0.51),
+ ( 461.35, 0.0414, 2.82),
+ ( 768.92, 0.0563, 13.43),
+ (1025.23, 0.0596, 30.75),
+ (1537.85, 0.0625, 46.03),
+ (2306.77, 0.0744, 78.07),
+ ( 'inf', 0.0853, 135.28),
+ ),
+ 'semi-monthly': (
+ ( 55.54, 0.0033, 0.00),
+ ( 111.08, 0.0067, 0.18),
+ ( 222.13, 0.0225, 0.55),
+ ( 499.79, 0.0414, 3.05),
+ ( 833.00, 0.0563, 14.59),
+ (1110.67, 0.0596, 33.31),
+ (1666.00, 0.0625, 49.86),
+ (2499.00, 0.0744, 84.57),
+ ( 'inf', 0.0853, 146.55),
+ ),
+ 'monthly': (
+ ( 111.08, 0.0033, 0.00),
+ ( 222.17, 0.0067, 0.37),
+ ( 444.25, 0.0225, 1.11),
+ ( 999.58, 0.0414, 6.11),
+ (1666.00, 0.0563, 29.10),
+ (2221.33, 0.0596, 62.66),
+ (3332.00, 0.0625, 99.72),
+ (4998.00, 0.0744, 169.14),
+ ( 'inf', 0.0853, 293.09),
+ ),
+ 'annual': (
+ ( 1333.00, 0.0033, 0.00),
+ ( 2666.00, 0.0067, 4.40),
+ ( 5331.00, 0.0225, 13.33),
+ (11995.00, 0.0414, 73.29),
+ (19992.00, 0.0563, 349.19),
+ (26656.00, 0.0596, 799.41),
+ (39984.00, 0.0625, 1196.58),
+ (59976.00, 0.0744, 2029.58),
+ ( 'inf', 0.0853, 3516.98),
+ ),
+ }
+
+
+
+
+
+
+ {
+ 'daily': (
+ ( 5.69, 0.0033, 0.00),
+ ( 11.38, 0.0067, 0.02),
+ ( 22.76, 0.0225, 0.06),
+ ( 51.22, 0.0414, 0.32),
+ ( 85.36, 0.0563, 1.50),
+ (113.81, 0.0596, 3.42),
+ (170.71, 0.0625, 5.12),
+ (256.07, 0.0744, 8.68),
+ ( 'inf', 0.0853, 15.03),
+ ),
+ 'weekly': (
+ ( 28.46, 0.0033, 0.00),
+ ( 56.90, 0.0067, 0.09),
+ ( 113.81, 0.0225, 0.28),
+ ( 256.08, 0.0414, 1.56),
+ ( 426.79, 0.0563, 7.45),
+ ( 569.04, 0.0596, 17.06),
+ ( 853.56, 0.0625, 25.54),
+ (1280.35, 0.0744, 43.32),
+ ( 'inf', 0.0853, 75.07),
+ ),
+ 'bi-weekly': (
+ ( 56.92, 0.0033, 0.00),
+ ( 113.81, 0.0067, 0.19),
+ ( 227.62, 0.0225, 0.57),
+ ( 512.15, 0.0414, 3.13),
+ ( 853.58, 0.0563, 14.91),
+ (1138.08, 0.0596, 34.13),
+ (1707.12, 0.0625, 51.09),
+ (2560.69, 0.0744, 86.66),
+ ( 'inf', 0.0853, 150.17),
+ ),
+ 'semi-monthly': (
+ ( 61.67, 0.0033, 0.00),
+ ( 23.29, 0.0067, 0.20),
+ ( 246.58, 0.0225, 0.61),
+ ( 554.83, 0.0414, 3.38),
+ ( 924.71, 0.0563, 16.14),
+ (1232.92, 0.0596, 36.96),
+ (1849.38, 0.0625, 55.33),
+ (2774.08, 0.0744, 93.86),
+ ( 'inf', 0.0853, 162.66),
+ ),
+ 'monthly': (
+ ( 123.33, 0.0033, 0.00),
+ ( 246.58, 0.0067, 0.41),
+ ( 493.17, 0.0225, 1.24),
+ (1109.67, 0.0414, 6.79),
+ (1849.42, 0.0563, 32.31),
+ (2465.83, 0.0596, 73.96),
+ (3698.75, 0.0625, 110.70),
+ (5548.17, 0.0744, 187.76),
+ ( 'inf', 0.0853, 325.36),
+ ),
+ 'annual': (
+ ( 1480.00, 0.0033, 0.00),
+ ( 2959.00, 0.0067, 4.88),
+ ( 5918.00, 0.0225, 14.79),
+ (13316.00, 0.0414, 81.37),
+ (22193.00, 0.0563, 387.65),
+ (29590.00, 0.0596, 887.43),
+ (44385.00, 0.0625, 1328.29),
+ (66578.00, 0.0744, 2252.98),
+ ( 'inf', 0.0853, 3904.14),
+ ),
+ }
+
+
+
+
+
+
+ US IA Iowa Standard Deduction Rate
+ us_ia_sit_standard_deduction_rate
+
+
+
+
+ {
+ 'daily': ( 6.50, 16.00),
+ 'weekly': ( 32.50, 80.00),
+ 'bi-weekly': ( 65.00, 160.00),
+ 'semi-monthly': ( 70.42, 173.33),
+ 'monthly': ( 140.83, 346.67),
+ 'annually': (1690.00, 4160.00),
+ }
+
+
+
+
+
+
+ {
+ 'daily': ( 7.23, 17.81),
+ 'weekly': ( 36.15, 89.04),
+ 'bi-weekly': ( 72.31, 178.08),
+ 'semi-monthly': ( 78.33, 192.92),
+ 'monthly': ( 156.67, 385.83),
+ 'annually': (1880.00, 4630.00),
+ }
+
+
+
+
+
+
+ US IA Iowa Deduction Allowance Rate
+ us_ia_sit_deduction_allowance_rate
+
+
+
+
+ {
+ 'daily': 0.15,
+ 'weekly': 0.77,
+ 'bi-weekly': 1.54,
+ 'semi-monthly': 1.67,
+ 'monthly': 3.33,
+ 'annually': 40.00,
+ }
+
+
+
+
+
+
+ {
+ 'daily': 0.15,
+ 'weekly': 0.77,
+ 'bi-weekly': 1.54,
+ 'semi-monthly': 1.67,
+ 'monthly': 3.33,
+ 'annually': 40.00,
+ }
+
+
+
+
+
+
+
+ US Iowa - Department of Economic Security (IDES) - Unemployment Tax
+
+
+
+ US Iowa - Department of Revenue (IDOR) - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US IA Iowa State Unemployment
+ ER_US_IA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ia_suta_wage_base', rate='us_ia_suta_rate', state_code='IA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ia_suta_wage_base', rate='us_ia_suta_rate', state_code='IA')
+
+
+
+
+
+
+
+
+ EE: US IA Iowa State Income Tax Withholding
+ EE_US_IA_SIT
+ python
+ result, _ = ia_iowa_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ia_iowa_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/id_idaho.xml b/l10n_us_hr_payroll/data/state/id_idaho.xml
new file mode 100644
index 00000000..46dcce9d
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/id_idaho.xml
@@ -0,0 +1,448 @@
+
+
+
+
+ US ID Idaho SUTA Wage Base
+ us_id_suta_wage_base
+
+
+
+
+ 40000.0
+
+
+
+
+ 41600.0
+
+
+
+
+
+
+
+ US ID Idaho SUTA Rate
+ us_id_suta_rate
+
+
+
+
+ 1.0
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ US ID Idaho SIT Tax Rate
+ us_id_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 235, 0.00, 0.000),
+ ( 264, 0.00, 1.125),
+ ( 294, 0.00, 3.125),
+ ( 324, 1.00, 3.625),
+ ( 353, 2.00, 4.625),
+ ( 383, 4.00, 5.625),
+ ( 457, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 469, 0.00, 0.000),
+ ( 529, 0.00, 1.125),
+ ( 588, 1.00, 3.125),
+ ( 647, 3.00, 3.625),
+ ( 706, 5.00, 4.625),
+ ( 766, 7.00, 5.625),
+ ( 914, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 508, 0.00, 0.000),
+ ( 573, 0.00, 1.125),
+ ( 637, 1.00, 3.125),
+ ( 701, 3.00, 3.625),
+ ( 765, 5.00, 4.625),
+ ( 829, 8.00, 5.625),
+ ( 990, 12.00, 6.625),
+ ('inf', 22.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1017, 0.00, 0.000),
+ ( 1145, 0.00, 1.125),
+ ( 1273, 1.00, 3.125),
+ ( 1402, 5.00, 3.625),
+ ( 1530, 10.00, 4.625),
+ ( 1659, 16.00, 5.625),
+ ( 1980, 23.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12200, 0.00, 0.000),
+ (13741, 0.00, 1.125),
+ (15281, 17.00, 3.125),
+ (16822, 65.00, 3.625),
+ (18362, 121.00, 4.625),
+ (19903, 192.00, 5.625),
+ (23754, 279.00, 6.625),
+ ('inf', 534.00, 6.925),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 469, 0.00, 0.000),
+ ( 529, 0.00, 1.125),
+ ( 588, 0.00, 3.125),
+ ( 647, 1.00, 3.625),
+ ( 706, 2.00, 4.625),
+ ( 766, 4.00, 5.625),
+ ( 914, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 938, 0.00, 0.000),
+ ( 1057, 0.00, 1.125),
+ ( 1175, 1.00, 3.125),
+ ( 1294, 5.00, 3.625),
+ ( 1412, 9.00, 4.625),
+ ( 1531, 15.00, 5.625),
+ ( 1827, 21.00, 6.625),
+ ('inf', 41.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 1017, 0.00, 0.000),
+ ( 1145, 0.00, 1.125),
+ ( 1273, 1.00, 3.125),
+ ( 1402, 5.00, 3.625),
+ ( 1530, 10.00, 4.625),
+ ( 1659, 16.00, 5.625),
+ ( 1980, 23.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'monthly': (
+ ( 2033, 0.00, 0.000),
+ ( 2290, 0.00, 1.125),
+ ( 2547, 3.00, 3.125),
+ ( 2804, 11.00, 3.625),
+ ( 3060, 20.00, 4.625),
+ ( 3317, 32.00, 5.625),
+ ( 3959, 47.00, 6.625),
+ ('inf', 89.00, 6.925),
+ ),
+ 'annually': (
+ (24400, 0.00, 0.000),
+ (27482, 0.00, 1.125),
+ (30562, 35.00, 3.125),
+ (33644, 131.00, 3.625),
+ (36724, 243.00, 4.625),
+ (39806, 385.00, 5.625),
+ (47508, 558.00, 6.625),
+ ('inf', 1068.00, 6.925),
+ ),
+ },
+ 'head of household': {
+ 'weekly': (
+ ( 235, 0.00, 0.000),
+ ( 264, 0.00, 1.125),
+ ( 294, 0.00, 3.125),
+ ( 324, 1.00, 3.625),
+ ( 353, 2.00, 4.625),
+ ( 383, 4.00, 5.625),
+ ( 457, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 469, 0.00, 0.000),
+ ( 529, 0.00, 1.125),
+ ( 588, 1.00, 3.125),
+ ( 647, 3.00, 3.625),
+ ( 706, 5.00, 4.625),
+ ( 766, 7.00, 5.625),
+ ( 914, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 508, 0.00, 0.000),
+ ( 573, 0.00, 1.125),
+ ( 637, 1.00, 3.125),
+ ( 701, 3.00, 3.625),
+ ( 765, 5.00, 4.625),
+ ( 829, 8.00, 5.625),
+ ( 990, 12.00, 6.625),
+ ('inf', 22.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1017, 0.00, 0.000),
+ ( 1145, 0.00, 1.125),
+ ( 1273, 1.00, 3.125),
+ ( 1402, 5.00, 3.625),
+ ( 1530, 10.00, 4.625),
+ ( 1659, 16.00, 5.625),
+ ( 1980, 23.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12200, 0.00, 0.000),
+ (13741, 0.00, 1.125),
+ (15281, 17.00, 3.125),
+ (16822, 65.00, 3.625),
+ (18362, 121.00, 4.625),
+ (19903, 192.00, 5.625),
+ (23754, 279.00, 6.625),
+ ('inf', 534.00, 6.925),
+ ),
+ },
+ }
+
+
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 238, 0.00, 0.000),
+ ( 269, 0.00, 1.125),
+ ( 299, 0.00, 3.125),
+ ( 329, 1.00, 3.625),
+ ( 359, 2.00, 4.625),
+ ( 389, 4.00, 5.625),
+ ( 465, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 477, 0.00, 0.000),
+ ( 537, 0.00, 1.125),
+ ( 598, 1.00, 3.125),
+ ( 658, 3.00, 3.625),
+ ( 718, 5.00, 4.625),
+ ( 778, 8.00, 5.625),
+ ( 929, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 517, 0.00, 0.000),
+ ( 582, 0.00, 1.125),
+ ( 647, 1.00, 3.125),
+ ( 713, 3.00, 3.625),
+ ( 778, 5.00, 4.625),
+ ( 843, 8.00, 5.625),
+ ( 1007, 12.00, 6.625),
+ ('inf', 23.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1033, 0.00, 0.000),
+ ( 1164, 0.00, 1.125),
+ ( 1295, 1.00, 3.125),
+ ( 1425, 6.00, 3.625),
+ ( 1556, 10.00, 4.625),
+ ( 1687, 16.00, 5.625),
+ ( 2013, 24.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12400, 0.00, 0.000),
+ (13968, 0.00, 1.125),
+ (15536, 18.00, 3.125),
+ (17104, 67.00, 3.625),
+ (18672, 124.00, 4.625),
+ (20240, 197.00, 5.625),
+ (24160, 285.00, 6.625),
+ ('inf', 545.00, 6.925),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 477, 0.00, 0.000),
+ ( 537, 0.00, 1.125),
+ ( 598, 0.00, 3.125),
+ ( 658, 1.00, 3.625),
+ ( 718, 3.00, 4.625),
+ ( 778, 5.00, 5.625),
+ ( 929, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 954, 0.00, 0.000),
+ ( 1074, 0.00, 1.125),
+ ( 1195, 1.00, 3.125),
+ ( 1316, 5.00, 3.625),
+ ( 1436, 9.00, 4.625),
+ ( 1557, 15.00, 5.625),
+ ( 1858, 22.00, 6.625),
+ ('inf', 42.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 1033, 0.00, 0.000),
+ ( 1164, 0.00, 1.125),
+ ( 1295, 1.00, 3.125),
+ ( 1425, 6.00, 3.625),
+ ( 1556, 10.00, 4.625),
+ ( 1687, 16.00, 5.625),
+ ( 2013, 24.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'monthly': (
+ ( 2067, 0.00, 0.000),
+ ( 2328, 0.00, 1.125),
+ ( 2589, 3.00, 3.125),
+ ( 2851, 11.00, 3.625),
+ ( 3112, 21.00, 4.625),
+ ( 3373, 33.00, 5.625),
+ ( 4027, 47.00, 6.625),
+ ('inf', 91.00, 6.925),
+ ),
+ 'annually': (
+ (24400, 0.00, 0.000),
+ (27482, 0.00, 1.125),
+ (30562, 35.00, 3.125),
+ (33644, 131.00, 3.625),
+ (36724, 243.00, 4.625),
+ (39806, 385.00, 5.625),
+ (47508, 558.00, 6.625),
+ ('inf', 1068.00, 6.925),
+ ),
+ },
+ 'head of household': {
+ 'weekly': (
+ ( 238, 0.00, 0.000),
+ ( 269, 0.00, 1.125),
+ ( 299, 0.00, 3.125),
+ ( 329, 1.00, 3.625),
+ ( 359, 2.00, 4.625),
+ ( 389, 4.00, 5.625),
+ ( 465, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 477, 0.00, 0.000),
+ ( 537, 0.00, 1.125),
+ ( 598, 1.00, 3.125),
+ ( 658, 3.00, 3.625),
+ ( 718, 5.00, 4.625),
+ ( 778, 8.00, 5.625),
+ ( 929, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 517, 0.00, 0.000),
+ ( 582, 0.00, 1.125),
+ ( 647, 1.00, 3.125),
+ ( 713, 3.00, 3.625),
+ ( 778, 5.00, 4.625),
+ ( 843, 8.00, 5.625),
+ ( 1007, 12.00, 6.625),
+ ('inf', 23.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1033, 0.00, 0.000),
+ ( 1164, 0.00, 1.125),
+ ( 1295, 1.00, 3.125),
+ ( 1425, 6.00, 3.625),
+ ( 1556, 10.00, 4.625),
+ ( 1687, 16.00, 5.625),
+ ( 2013, 24.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12400, 0.00, 0.000),
+ (13968, 0.00, 1.125),
+ (15536, 18.00, 3.125),
+ (17104, 67.00, 3.625),
+ (18672, 124.00, 4.625),
+ (20240, 197.00, 5.625),
+ (24160, 285.00, 6.625),
+ ('inf', 545.00, 6.925),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US ID Idaho Child Tax Credit Allowance Rate
+ us_id_sit_ictcat_rate
+
+
+
+
+ {
+ 'weekly': 56.92,
+ 'bi-weekly': 113.85,
+ 'semi-monthly': 123.33,
+ 'monthly': 246.67,
+ 'annually': 2960.00,
+ }
+
+
+
+
+
+
+ {
+ 'weekly': 56.92,
+ 'bi-weekly': 113.85,
+ 'semi-monthly': 123.33,
+ 'monthly': 246.67,
+ 'annually': 2960.00,
+ }
+
+
+
+
+
+
+
+
+ US Idaho - Department of Labor (IDOL) - Unemployment Tax
+
+
+
+ US Idaho - State Tax Commission (ISTC) - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US ID Idaho State Unemployment
+ ER_US_ID_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_id_suta_wage_base', rate='us_id_suta_rate', state_code='ID')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_id_suta_wage_base', rate='us_id_suta_rate', state_code='ID')
+
+
+
+
+
+
+
+
+ EE: US ID Idaho State Income Tax Withholding
+ EE_US_ID_SIT
+ python
+ result, _ = id_idaho_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = id_idaho_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/il_illinois.xml b/l10n_us_hr_payroll/data/state/il_illinois.xml
new file mode 100644
index 00000000..840b2a9b
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/il_illinois.xml
@@ -0,0 +1,117 @@
+
+
+
+
+ US IL Illinois SUTA Wage Base
+ us_il_suta_wage_base
+
+
+
+
+ 12960.0
+
+
+
+
+ 12740.0
+
+
+
+
+
+
+
+ US IL Illinois SUTA Rate
+ us_il_suta_rate
+
+
+
+
+ 3.175
+
+
+
+
+ 3.125
+
+
+
+
+
+
+ US IL Illinois Basic Allowances Rate
+ us_il_sit_basic_allowances_rate
+
+
+
+
+ 2275.0
+
+
+
+
+ 2325.0
+
+
+
+
+
+
+ US IL Illinois Additional Allowances Rate
+ us_il_sit_additional_allowances_rate
+
+
+
+
+ 1000.0
+
+
+
+
+ 1000.0
+
+
+
+
+
+
+
+ US Illinois - Department of Economic Security (IDES) - Unemployment Tax
+
+
+
+ US Illinois - Department of Revenue (IDOR) - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US IL Illinois State Unemployment
+ ER_US_IL_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_il_suta_wage_base', rate='us_il_suta_rate', state_code='IL')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_il_suta_wage_base', rate='us_il_suta_rate', state_code='IL')
+
+
+
+
+
+
+
+
+ EE: US IL Illinois State Income Tax Withholding
+ EE_US_IL_SIT
+ python
+ result, _ = il_illinois_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = il_illinois_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/in_indiana.xml b/l10n_us_hr_payroll/data/state/in_indiana.xml
new file mode 100644
index 00000000..9bda9b1e
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/in_indiana.xml
@@ -0,0 +1,122 @@
+
+
+
+
+ US IN Indiana SUTA Wage Base
+ us_in_suta_wage_base
+
+
+
+
+ 9500.00
+
+
+
+
+
+
+
+ US IN Indiana SUTA Rate
+ us_in_suta_rate
+
+
+
+
+ 2.5
+
+
+
+
+
+
+ US IN Indiana SUTA Income Rate
+ us_in_suta_income_rate
+
+
+
+
+ 3.23
+
+
+
+
+
+
+ US IN Indiana SIT Personal Exemption Rate
+ us_in_sit_personal_exemption_rate
+
+
+
+
+ {
+ 'daily': ( 2.74, 5.48, 8.22, 10.96, 13.70, 16.44),
+ 'weekly': ( 19.23, 38.46, 57.69, 76.92, 96.15, 115.38),
+ 'bi-weekly': ( 38.46, 76.92, 115.38, 153.85, 192.31, 230.77),
+ 'semi-monthly': ( 41.67, 83.33, 125.00, 166.67, 208.33, 250.00),
+ 'monthly': ( 83.33, 166.67, 250.00, 333.33, 416.67, 500.00),
+ }
+
+
+
+
+
+
+ US IN Indiana SIT Dependent Exemption Rate
+ us_in_sit_dependent_exemption_rate
+
+
+
+
+ {
+ 'daily': ( 4.11, 8.22, 12.33, 16.44, 20.55),
+ 'weekly': ( 28.85, 57.69, 86.54, 115.38, 144.23),
+ 'bi-weekly': ( 57.69, 115.38, 173.08, 230.77, 288.46),
+ 'semi-monthly': ( 62.50, 125.00, 187.50, 250.00, 312.50),
+ 'monthly': (125.00, 250.00, 375.00, 500.00, 625.00),
+ }
+
+
+
+
+
+
+
+ US Indiana - Department of Workforce Development - Unemployment Tax
+
+
+
+ US Indiana - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US IN Indiana State Unemployment
+ ER_US_IN_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_in_suta_wage_base', rate='us_in_suta_rate', state_code='IN')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_in_suta_wage_base', rate='us_in_suta_rate', state_code='IN')
+
+
+
+
+
+
+
+
+ EE: US IN Indiana State Income Tax Withholding
+ EE_US_IN_SIT
+ python
+ result, _ = in_indiana_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = in_indiana_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/ks_kansas.xml b/l10n_us_hr_payroll/data/state/ks_kansas.xml
new file mode 100644
index 00000000..5b59d2b7
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ks_kansas.xml
@@ -0,0 +1,194 @@
+
+
+
+
+ US KS Kansas SUTA Wage Base
+ us_ks_suta_wage_base
+
+
+
+
+ 14000.0
+
+
+
+
+
+
+
+ US KS Kansas SUTA Rate
+ us_ks_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US KS Kansas Allowances Rate
+ us_ks_sit_allowances_rate
+
+
+
+
+ {
+ 'weekly' : 43.27,
+ 'bi-weekly' : 86.54,
+ 'semi-monthly': 93.75,
+ 'monthly' : 187.50,
+ 'quarterly' : 562.50,
+ 'semi-annual': 1125.00,
+ 'annually': 2250.00,
+ }
+
+
+
+
+
+
+ US KS Kansas SIT Tax Rate
+ us_ks_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 58, 0.00, 0.00),
+ ( 346, 3.10, 0.00),
+ ( 635, 5.25, 8.94),
+ ('inf', 5.70, 24.09),
+ ),
+ 'bi-weekly': (
+ ( 115, 0.00, 0.00),
+ ( 692, 3.10, 0.00),
+ ( 1269, 5.25, 17.88),
+ ('inf', 5.70, 48.17),
+ ),
+ 'semi-monthly': (
+ ( 125, 0.00, 0.00),
+ ( 750, 3.10, 0.00),
+ ( 1375, 5.25, 19.38),
+ ('inf', 5.70, 52.19),
+ ),
+ 'monthly': (
+ ( 250, 0.00, 0.00),
+ ( 1500, 3.10, 0.00),
+ ( 2750, 5.25, 38.75),
+ ('inf', 5.70, 104.38),
+ ),
+ 'quarterly': (
+ ( 750, 0.00, 0.00),
+ ( 4500, 3.10, 0.00),
+ (8250, 5.25, 116.25),
+ ('inf', 5.70, 313.13),
+ ),
+ 'semi-annual': (
+ ( 1500, 0.00, 0.00),
+ ( 9000, 3.10, 0.00),
+ (16500, 5.25, 232.50),
+ ('inf', 5.70, 626.25),
+ ),
+ 'annually': (
+ ( 3000, 0.00, 0.00),
+ (18000, 3.10, 0.00),
+ (33000, 5.25, 465.00),
+ ('inf', 5.70, 1252.50),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 144, 0.00, 0.00),
+ ( 721, 3.10, 0.00),
+ (1298, 5.25, 17.88),
+ ('inf', 5.70, 48.17),
+ ),
+ 'bi-weekly': (
+ ( 288, 0.00, 0.00),
+ ( 1442, 3.10, 0.00),
+ ( 2596, 5.25, 35.77),
+ ('inf', 5.70, 96.35),
+ ),
+ 'semi-monthly': (
+ ( 313, 0.00, 0.00),
+ ( 1563, 3.10, 0.00),
+ ( 2813, 5.25, 38.75),
+ ('inf', 5.70, 104.38),
+ ),
+ 'monthly': (
+ ( 625, 0.00, 0.00),
+ ( 3125, 3.10, 0.00),
+ ( 5625, 5.25, 77.50),
+ ('inf', 5.70, 208.75),
+ ),
+ 'quarterly': (
+ ( 1875, 0.00, 0.00),
+ ( 9375, 3.10, 0.00),
+ (16875, 5.25, 232.50),
+ ('inf', 5.70, 626.25),
+ ),
+ 'semi-annual': (
+ ( 3750, 0.00, 0.00),
+ (18750, 3.10, 0.00),
+ (33750, 5.25, 465.00),
+ ('inf', 5.70, 1252.50),
+ ),
+ 'annually': (
+ ( 7500, 0.00, 0.00),
+ (37500, 3.10, 0.00),
+ (67500, 5.25, 930.00),
+ ('inf', 5.70, 2505.00),
+ ),
+ },
+ }
+
+
+
+
+
+
+
+ US Kansas - Department of Labor - Unemployment Tax
+
+
+
+ US Kansas - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US KS Kansas State Unemployment
+ ER_US_KS_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ks_suta_wage_base', rate='us_ks_suta_rate', state_code='KS')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ks_suta_wage_base', rate='us_ks_suta_rate', state_code='KS')
+
+
+
+
+
+
+
+
+ EE: US KS Kansas State Income Tax Withholding
+ EE_US_KS_SIT
+ python
+ result, _ = ks_kansas_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ks_kansas_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ky_kentucky.xml b/l10n_us_hr_payroll/data/state/ky_kentucky.xml
new file mode 100644
index 00000000..bcdd2274
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ky_kentucky.xml
@@ -0,0 +1,99 @@
+
+
+
+
+ US KY Kentucky SUTA Wage Base
+ us_ky_suta_wage_base
+
+
+
+
+ 10800.0
+
+
+
+
+
+
+
+ US KY Kentucky SUTA Rate
+ us_ky_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US KY Kentucky Standard Deduction Rate
+ us_ky_sit_standard_deduction_rate
+
+
+
+
+
+ 2650
+
+
+
+
+
+
+ US KY Kentucky SIT Tax Rate
+ us_ky_sit_tax_rate
+
+
+
+
+
+ 5.0
+
+
+
+
+
+
+
+ US Kentucky - Office of Unemployment Insurance - Unemployment Tax
+
+
+
+ US Kentucky - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US KY Kentucky State Unemployment
+ ER_US_KY_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ky_suta_wage_base', rate='us_ky_suta_rate', state_code='KY')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ky_suta_wage_base', rate='us_ky_suta_rate', state_code='KY')
+
+
+
+
+
+
+
+
+ EE: US KY Kentucky State Income Tax Withholding
+ EE_US_KY_SIT
+ python
+ result, _ = ky_kentucky_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ky_kentucky_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/la_louisiana.xml b/l10n_us_hr_payroll/data/state/la_louisiana.xml
new file mode 100644
index 00000000..7e4f5fe2
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/la_louisiana.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ US LA Louisiana SUTA Wage Base
+ us_la_suta_wage_base
+
+
+
+
+ 7700.0
+
+
+
+
+ 7700.0
+
+
+
+
+
+
+
+ US LA Louisiana SUTA Rate
+ us_la_suta_rate
+
+
+
+
+ 1.14
+
+
+
+
+ 1.14
+
+
+
+
+
+
+ US LA Louisiana SIT Tax Rate
+ us_la_sit_tax_rate
+
+
+
+
+ {
+ 'single': (
+ (12500.00, 2.10),
+ (50000.00, 1.60),
+ ( 'inf', 1.35),
+ ),
+ 'married': (
+ ( 25000.00, 2.10),
+ (100000.00, 1.65),
+ ( 'inf', 1.35),
+ ),
+ }
+
+
+
+
+ {
+ 'single': (
+ (12500.00, 2.10),
+ (50000.00, 1.60),
+ ( 'inf', 1.35)
+ ),
+ 'married': (
+ ( 25000.00, 2.10),
+ (100000.00, 1.65),
+ ( 'inf', 1.35)
+ ),
+ }
+
+
+
+
+
+
+ US LA Louisiana Personal Exemption Rate
+ us_la_sit_personal_exemption_rate
+
+
+
+
+ 4500
+
+
+
+
+ 4500
+
+
+
+
+
+
+ US LA Louisiana Dependent Rate
+ us_la_sit_dependent_rate
+
+
+
+
+ 1000.0
+
+
+
+
+ 1000.0
+
+
+
+
+
+
+
+ US Louisiana - Workforce Commission (LWC) - Unemployment Tax
+
+
+
+ US Louisiana - Department of Revenue (LDOR) - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US LA Louisiana State Unemployment
+ ER_US_LA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_la_suta_wage_base', rate='us_la_suta_rate', state_code='LA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_la_suta_wage_base', rate='us_la_suta_rate', state_code='LA')
+
+
+
+
+
+
+
+
+ EE: US LA Louisiana State Income Tax Withholding
+ EE_US_LA_SIT
+ python
+ result, _ = la_louisiana_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = la_louisiana_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/me_maine.xml b/l10n_us_hr_payroll/data/state/me_maine.xml
new file mode 100644
index 00000000..2c1aaf71
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/me_maine.xml
@@ -0,0 +1,131 @@
+
+
+
+
+ US ME Maine SUTA Wage Base
+ us_me_suta_wage_base
+
+
+
+
+ 12000.0
+
+
+
+
+
+
+
+ US ME Maine SUTA Rate
+ us_me_suta_rate
+
+
+
+
+ 1.92
+
+
+
+
+
+
+ US ME Maine SIT Tax Rate
+ us_me_sit_tax_rate
+
+
+
+
+ {
+ 'single': (
+ ( 22200, 0, 5.80),
+ ( 52600, 1288, 6.75),
+ ( 'inf', 3340, 7.15),
+ ),
+ 'married': (
+ ( 44450, 0, 5.80),
+ ( 105200, 2578, 6.75),
+ ( 'inf', 6679, 7.15),
+ ),
+ }
+
+
+
+
+
+
+ US ME Maine Standard Deduction Rate
+ us_me_sit_standard_deduction_rate
+
+
+
+
+ {
+ 'single': {
+ ( 82900, 9550),
+ (157900, 75000),
+ },
+ 'married': {
+ (165800, 21950),
+ (315800, 150000),
+ },
+ }
+
+
+
+
+
+
+ US ME Maine Personal Exemption Rate
+ us_me_sit_personal_exemption_rate
+
+
+
+
+ 4300
+
+
+
+
+
+
+
+
+ US Maine - Department Of Labor | ReEmploy - Unemployment Tax
+
+
+
+ US Maine - Department Of Revenue Services - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US ME Maine State Unemployment
+ ER_US_ME_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_me_suta_wage_base', rate='us_me_suta_rate', state_code='ME')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_me_suta_wage_base', rate='us_me_suta_rate', state_code='ME')
+
+
+
+
+
+
+
+
+ EE: US ME Maine State Income Tax Withholding
+ EE_US_ME_SIT
+ python
+ result, _ = me_maine_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = me_maine_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/mi_michigan.xml b/l10n_us_hr_payroll/data/state/mi_michigan.xml
new file mode 100644
index 00000000..1ce32483
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/mi_michigan.xml
@@ -0,0 +1,99 @@
+
+
+
+
+ US MI Michigan SUTA Wage Base
+ us_mi_suta_wage_base
+
+
+
+
+ 9500.0
+
+
+
+
+ 9000.0
+
+
+
+
+
+
+
+ US MI Michigan SUTA Rate
+ us_mi_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US MI Michigan Exemption Rate
+ us_mi_sit_exemption_rate
+
+
+
+
+ 4400.0
+
+
+
+
+ 4750.0
+
+
+
+
+
+
+
+ US Michigan - Unemployment Insurance Agency - Unemployment Tax
+
+
+
+ US Michigan - Department of Treasury - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US MI Michigan State Unemployment
+ ER_US_MI_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mi_suta_wage_base', rate='us_mi_suta_rate', state_code='MI')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mi_suta_wage_base', rate='us_mi_suta_rate', state_code='MI')
+
+
+
+
+
+
+
+
+ EE: US MI Michigan State Income Tax Withholding
+ EE_US_MI_SIT
+ python
+ result, _ = mi_michigan_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = mi_michigan_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/mn_minnesota.xml b/l10n_us_hr_payroll/data/state/mn_minnesota.xml
new file mode 100644
index 00000000..d1044752
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/mn_minnesota.xml
@@ -0,0 +1,147 @@
+
+
+
+
+ US MN Minnesota SUTA Wage Base
+ us_mn_suta_wage_base
+
+
+
+
+ 34000.0
+
+
+
+
+ 35000.0
+
+
+
+
+
+
+
+ US MN Minnesota SUTA Rate
+ us_mn_suta_rate
+
+
+
+
+ 1.11
+
+
+
+
+ 1.11
+
+
+
+
+
+
+ US MN Minnesota SIT Tax Rate
+ us_mn_sit_tax_rate
+
+
+
+
+ {
+ 'single': (
+ ( 28920, 2400, 5.35, 0.00),
+ ( 89510, 28920, 7.05, 1418.82),
+ (166290, 89510, 7.85, 5690.42),
+ ( 'inf', 166290, 9.85, 11717.65),
+ ),
+ 'married': (
+ ( 47820, 9050, 5.35, 0.00),
+ ( 163070, 47820, 7.05, 2074.20),
+ ( 282200, 163070, 7.85, 10199.33),
+ ( 'inf', 282200, 9.85, 19551.04),
+ ),
+ }
+
+
+
+
+
+
+ {
+ 'single': (
+ ( 30760, 3800, 5.35, 0.00),
+ ( 92350, 30760, 6.80, 1442.36),
+ (168200, 92350, 7.85, 5630.48),
+ ( 'inf', 168200, 9.85, 11584.71),
+ ),
+ 'married': (
+ ( 51310, 11900, 5.35, 0.00),
+ ( 168470, 51310, 6.80, 2108.44),
+ ( 285370, 168470, 7.85, 10075.32),
+ ( 'inf', 285370, 9.85, 19251.97),
+ ),
+ }
+
+
+
+
+
+
+ US MN Minnesota Allowances Rate
+ us_mn_sit_allowances_rate
+
+
+
+
+ 4250.0
+
+
+
+
+
+
+ 4300.0
+
+
+
+
+
+
+
+ US Minnesota - Unemployment Insurance Agency - Unemployment Tax
+
+
+
+ US Minnesota - Department of Treasury - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US MN Minnesota State Unemployment
+ ER_US_MN_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mn_suta_wage_base', rate='us_mn_suta_rate', state_code='MN')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mn_suta_wage_base', rate='us_mn_suta_rate', state_code='MN')
+
+
+
+
+
+
+
+
+ EE: US MN Minnesota State Income Tax Withholding
+ EE_US_MN_SIT
+ python
+ result, _ = mn_minnesota_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = mn_minnesota_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/mo_missouri.xml b/l10n_us_hr_payroll/data/state/mo_missouri.xml
new file mode 100644
index 00000000..73ea5109
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/mo_missouri.xml
@@ -0,0 +1,151 @@
+
+
+
+
+ US MO Missouri SUTA Wage Base
+ us_mo_suta_wage_base
+
+
+
+
+ 12000.0
+
+
+
+
+ 11500.0
+
+
+
+
+
+
+
+ US MO Missouri SUTA Rate
+ us_mo_suta_rate
+
+
+
+
+ 2.376
+
+
+
+
+ 2.376
+
+
+
+
+
+
+ US MO Missouri SIT Rate Table
+ us_mo_sit_rate
+
+
+
+
+
+ [
+ (1053.0, 1.5),
+ (1053.0, 2.0),
+ (1053.0, 2.5),
+ (1053.0, 3.0),
+ (1053.0, 3.5),
+ (1053.0, 4.0),
+ (1053.0, 4.5),
+ (1053.0, 5.0),
+ ( 'inf', 5.4),
+ ]
+
+
+
+
+
+
+ [
+ (1073.0, 1.5),
+ (1073.0, 2.0),
+ (1073.0, 2.5),
+ (1073.0, 3.0),
+ (1073.0, 3.5),
+ (1073.0, 4.0),
+ (1073.0, 4.5),
+ (1073.0, 5.0),
+ ( 'inf', 5.4),
+ ]
+
+
+
+
+
+
+ US MO Missouri SIT Deduction
+ us_mo_sit_deduction
+
+
+
+
+
+ {
+ 'single': 12400.0,
+ 'married': 24800.0,
+ 'head_of_household': 18650.0,
+ }
+
+
+
+
+
+
+ {
+ 'single': 12400.0,
+ 'married': 24800.0,
+ 'head_of_household': 18650.0,
+ }
+
+
+
+
+
+
+
+ US Missouri - Department of Taxation - Unemployment Tax
+
+
+
+ US Missouri - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US MO Missouri State Unemployment
+ ER_US_MO_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mo_suta_wage_base', rate='us_mo_suta_rate', state_code='MO')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mo_suta_wage_base', rate='us_mo_suta_rate', state_code='MO')
+
+
+
+
+
+
+
+
+ EE: US MO Missouri State Income Tax Withholding
+ EE_US_MO_SIT
+ python
+ result, _ = mo_missouri_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = mo_missouri_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ms_mississippi.xml b/l10n_us_hr_payroll/data/state/ms_mississippi.xml
new file mode 100644
index 00000000..a3868e8d
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ms_mississippi.xml
@@ -0,0 +1,139 @@
+
+
+
+
+ US MS Mississippi SUTA Wage Base
+ us_ms_suta_wage_base
+
+
+
+
+ 14000.0
+
+
+
+
+ 14000.0
+
+
+
+
+
+
+
+ US MS Mississippi SUTA Rate
+ us_ms_suta_rate
+
+
+
+
+ 1.2
+
+
+
+
+ 1.2
+
+
+
+
+
+
+ US MS Mississippi SIT Rate Table
+ us_ms_sit_rate
+
+
+
+
+ [
+ ( 10000.00, 290.0, 0.05),
+ ( 5000.00, 90.0, 0.04),
+ ( 2000.00, 0.0, 0.03),
+ ]
+
+
+
+
+
+
+ [
+ ( 10000.00, 260.0, 0.05),
+ ( 5000.00, 60.0, 0.04),
+ ( 3000.00, 0.0, 0.03),
+ ]
+
+
+
+
+
+
+ US MS Mississippi SIT Deduction
+ us_ms_sit_deduction
+
+
+
+
+ {
+ 'single': 2300.0,
+ 'head_of_household': 3400.0,
+ 'married_dual': 2300.0,
+ 'married': 4600.0,
+ }
+
+
+
+
+
+
+ {
+ 'single': 2300.0,
+ 'head_of_household': 3400.0,
+ 'married_dual': 2300.0,
+ 'married': 4600.0,
+ }
+
+
+
+
+
+
+
+ US Mississippi - Department of Employment Security (Unemployment)
+
+
+
+ US Mississippi - Mississippi Department of Revenue (Income Tax)
+
+
+
+
+
+
+
+
+
+ ER: US MS Mississippi State Unemployment
+ ER_US_MS_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ms_suta_wage_base', rate='us_ms_suta_rate', state_code='MS')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ms_suta_wage_base', rate='us_ms_suta_rate', state_code='MS')
+
+
+
+
+
+
+
+
+ EE: US MS Mississippi State Income Tax Withholding
+ EE_US_MS_SIT
+ python
+ result, _ = ms_mississippi_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ms_mississippi_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/mt_montana.xml b/l10n_us_hr_payroll/data/state/mt_montana.xml
new file mode 100644
index 00000000..b777fb4e
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/mt_montana.xml
@@ -0,0 +1,227 @@
+
+
+
+
+ US MT Montana SUTA Wage Base
+ us_mt_suta_wage_base
+
+
+
+
+ 33000.00
+
+
+
+
+ 34100.00
+
+
+
+
+
+
+
+ US MT Montana SUTA Rate (UI)
+ us_mt_suta_rate
+
+
+
+
+ 1.18
+
+
+
+
+ 1.18
+
+
+
+
+
+
+ US MT Montana SUTA Administrative Fund Tax Rate
+ us_mt_suta_aft_rate
+
+
+
+
+ 0.13
+
+
+
+
+ 0.13
+
+
+
+
+
+
+ US MT Montana SIT Rate Table
+ us_mt_sit_rate
+
+
+
+
+ {
+ 'weekly': (
+ ( 135.00, 0.0, 1.80),
+ ( 288.00, 2.0, 4.40),
+ ( 2308.00, 9.0, 6.00),
+ ( 'inf', 130.0, 6.60),
+ ),
+ 'bi-weekly': (
+ ( 269.00, 0.0, 1.80),
+ ( 577.00, 5.0, 4.40),
+ ( 4615.00, 18.0, 6.00),
+ ( 'inf', 261.0, 6.60),
+ ),
+ 'semi-monthly': (
+ ( 292.00, 0.0, 1.80),
+ ( 625.00, 5.0, 4.40),
+ ( 5000.00, 20.0, 6.00),
+ ( 'inf', 282.0, 6.60),
+ ),
+ 'monthly': (
+ ( 583.00, 0.0, 1.80),
+ ( 1250.00, 11.0, 4.40),
+ ( 10000.00, 40.0, 6.00),
+ ( 'inf', 565.0, 6.60),
+ ),
+ 'annually': (
+ ( 7000.00, 0.0, 1.80),
+ ( 15000.00, 126.0, 4.40),
+ ( 120000.00, 478.0, 6.00),
+ ( 'inf', 6778.0, 6.60),
+ ),
+ }
+
+
+
+
+
+
+ {
+ 'weekly': (
+ ( 135.00, 0.0, 1.80),
+ ( 288.00, 2.0, 4.40),
+ ( 2308.00, 9.0, 6.00),
+ ( 'inf', 130.0, 6.60),
+ ),
+ 'bi-weekly': (
+ ( 269.00, 0.0, 1.80),
+ ( 577.00, 5.0, 4.40),
+ ( 4615.00, 18.0, 6.00),
+ ( 'inf', 261.0, 6.60),
+ ),
+ 'semi-monthly': (
+ ( 292.00, 0.0, 1.80),
+ ( 625.00, 5.0, 4.40),
+ ( 5000.00, 20.0, 6.00),
+ ( 'inf', 282.0, 6.60),
+ ),
+ 'monthly': (
+ ( 583.00, 0.0, 1.80),
+ ( 1250.00, 11.0, 4.40),
+ ( 10000.00, 40.0, 6.00),
+ ( 'inf', 565.0, 6.60),
+ ),
+ 'annually': (
+ ( 7000.00, 0.0, 1.80),
+ ( 15000.00, 126.0, 4.40),
+ ( 120000.00, 478.0, 6.00),
+ ( 'inf', 6778.0, 6.60),
+ ),
+ }
+
+
+
+
+
+
+ US MT Montana SIT Exemption Rate Table
+ us_mt_sit_exemption_rate
+
+
+
+
+ {
+ 'weekly': 37.0,
+ 'bi-weekly': 73.0,
+ 'semi-monthly': 79.0,
+ 'monthly': 158.0,
+ 'annually': 1900.0,
+ }
+
+
+
+
+
+
+ {
+ 'weekly': 37.0,
+ 'bi-weekly': 73.0,
+ 'semi-monthly': 79.0,
+ 'monthly': 158.0,
+ 'annually': 1900.0,
+ }
+
+
+
+
+
+
+
+ US Montana - Department of Labor & Industries
+
+
+
+ US Montana - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US MT Montana State Unemployment (UI-5)
+ ER_US_MT_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mt_suta_wage_base', rate='us_mt_suta_rate', state_code='MT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mt_suta_wage_base', rate='us_mt_suta_rate', state_code='MT')
+
+
+
+
+
+
+
+
+ ER: US MT Montana State Unemployment Administrative Fund Tax (AFT) (UI-5)
+ ER_US_MT_SUTA_AFT
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mt_suta_wage_base', rate='us_mt_suta_aft_rate', state_code='MT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_mt_suta_wage_base', rate='us_mt_suta_aft_rate', state_code='MT')
+
+
+
+
+
+
+
+
+ EE: US MT Montana State Income Tax Withholding (MW-3)
+ EE_US_MT_SIT
+ python
+ result, _ = mt_montana_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = mt_montana_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/nc_northcarolina.xml b/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
new file mode 100644
index 00000000..d09f8e3f
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
@@ -0,0 +1,113 @@
+
+
+
+
+ US NC North Carolina SUTA Wage Base
+ us_nc_suta_wage_base
+
+
+
+
+ 24300.0
+
+
+
+
+ 25200.0
+
+
+
+
+
+
+
+ US NC North Carolina SUTA Rate
+ us_nc_suta_rate
+
+
+
+
+ 1.0
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ US NC North Carolina Allowance Rate
+ us_nc_sit_allowance_rate
+
+
+
+
+
+
+ {
+ 'weekly': {'allowance': 48.08, 'standard_deduction': 192.31, 'standard_deduction_hh': 288.46},
+ 'bi-weekly': {'allowance': 96.15, 'standard_deduction': 384.62, 'standard_deduction_hh': 576.92},
+ 'semi-monthly': {'allowance': 104.17, 'standard_deduction': 416.67, 'standard_deduction_hh': 625.00},
+ 'monthly': {'allowance': 208.33, 'standard_deduction': 833.33, 'standard_deduction_hh': 1250.00},
+ }
+
+
+
+
+
+
+ {
+ 'weekly': {'allowance': 48.08, 'standard_deduction': 206.73, 'standard_deduction_hh': 310.10},
+ 'bi-weekly': {'allowance': 96.15, 'standard_deduction': 413.46, 'standard_deduction_hh': 620.19},
+ 'semi-monthly': {'allowance': 104.17, 'standard_deduction': 447.92, 'standard_deduction_hh': 671.88},
+ 'monthly': {'allowance': 208.33, 'standard_deduction': 895.83, 'standard_deduction_hh': 1343.75},
+ }
+
+
+
+
+
+
+
+ US North Carolina - Department of Taxation - Unemployment Tax
+
+
+
+ US North Carolina - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US NC North Carolina State Unemployment
+ ER_US_NC_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nc_suta_wage_base', rate='us_nc_suta_rate', state_code='NC')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nc_suta_wage_base', rate='us_nc_suta_rate', state_code='NC')
+
+
+
+
+
+
+
+
+ EE: US NC North Carolina State Income Tax Withholding
+ EE_US_NC_SIT
+ python
+ result, _ = nc_northcarolina_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = nc_northcarolina_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/nd_north_dakota.xml b/l10n_us_hr_payroll/data/state/nd_north_dakota.xml
new file mode 100644
index 00000000..d778c3aa
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nd_north_dakota.xml
@@ -0,0 +1,280 @@
+
+
+
+
+ US ND North Dakota SUTA Wage Base
+ us_nd_suta_wage_base
+
+
+
+
+ 37900.0
+
+
+
+
+
+
+
+ US ND North Dakota SUTA Rate
+ us_nd_suta_rate
+
+
+
+
+ 1.02
+
+
+
+
+
+
+ US ND North Dakota SIT Tax Rate
+ us_nd_sit_tax_rate
+
+
+
+
+ {
+ '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,
+ }
+
+
+
+
+
+
+
+ US North Dakota - Office of State Tax Commissioner - Unemployment Tax
+
+
+
+ US North Dakota - Taxpayer Access Point - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US ND North Dakota State Unemployment
+ ER_US_ND_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nd_suta_wage_base', rate='us_nd_suta_rate', state_code='ND')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nd_suta_wage_base', rate='us_nd_suta_rate', state_code='ND')
+
+
+
+
+
+
+
+
+ EE: US ND North Dakota State Income Tax Withholding
+ EE_US_ND_SIT
+ python
+ result, _ = nd_north_dakota_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = nd_north_dakota_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ne_nebraska.xml b/l10n_us_hr_payroll/data/state/ne_nebraska.xml
new file mode 100644
index 00000000..1951ff2f
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ne_nebraska.xml
@@ -0,0 +1,237 @@
+
+
+
+
+ US NE Nebraska SUTA Wage Base
+ us_ne_suta_wage_base
+
+
+
+
+ 9000.0
+
+
+
+
+
+
+
+ US NE Nebraska SUTA Rate
+ us_ne_suta_rate
+
+
+
+
+ 1.25
+
+
+
+
+
+
+ US NE Nebraska SIT Tax Rate
+ us_ne_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 57, 0.00, 0.00),
+ ( 105, 0.00, 2.26),
+ ( 342, 1.08, 3.22),
+ ( 496, 8.71, 4.91),
+ ( 629, 16.27, 6.20),
+ ( 1182, 24.52, 6.59),
+ ('inf', 60.96, 6.95),
+ ),
+ 'bi-weekly': (
+ ( 114, 0.00, 0.00),
+ ( 211, 0.00, 2.26),
+ ( 684, 2.19, 3.22),
+ ( 992, 17.42, 4.91),
+ ( 1259, 32.54, 6.20),
+ ( 2364, 49.09, 6.59),
+ ('inf', 121.91, 6.95),
+ ),
+ 'semi-monthly': (
+ ( 124, 0.00, 0.00),
+ ( 228, 0.00, 2.26),
+ ( 741, 2.35, 3.22),
+ ( 1074, 18.87, 4.91),
+ ( 1364, 35.22, 6.20),
+ ( 2561, 53.20, 6.59),
+ ('inf', 132.08, 6.95),
+ ),
+ 'monthly': (
+ ( 248, 0.00, 0.00),
+ ( 457, 0.00, 2.26),
+ ( 1483, 4.72, 3.22),
+ ( 2148, 37.76, 4.91),
+ ( 2728, 70.41, 6.20),
+ ( 5123, 106.37, 6.59),
+ ('inf', 264.20, 6.95),
+ ),
+ 'quarterly': (
+ ( 744, 0.00, 0.00),
+ ( 1370, 0.00, 2.26),
+ ( 4448, 14.15, 3.22),
+ ( 6445, 113.26, 4.91),
+ ( 8183, 211.31, 6.20),
+ ( 15368, 319.07, 6.59),
+ ( 'inf', 792.56, 6.95),
+ ),
+ 'semi-annual': (
+ ( 1488, 0.00, 0.00),
+ ( 2740, 0.00, 2.26),
+ ( 8895, 28.30, 3.22),
+ ( 12890, 226.49, 4.91),
+ ( 16365, 422.64, 6.20),
+ ( 30735, 638.09, 6.59),
+ ( 'inf', 1585.07, 6.95),
+ ),
+ 'annually': (
+ ( 2975, 0.00, 0.00),
+ ( 5480, 0.00, 2.26),
+ ( 17790, 56.61, 3.22),
+ ( 25780, 452.99, 4.91),
+ ( 32730, 845.30, 6.20),
+ ( 61470, 1276.20, 6.59),
+ ( 'inf', 3170.17, 6.95),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 137, 0.00, 0.00),
+ ( 204, 0.00, 2.26),
+ ( 508, 1.51, 3.22),
+ ( 790, 11.30, 4.91),
+ ( 981, 25.15, 6.20),
+ ( 1300, 36.99, 6.59),
+ ('inf', 58.01, 6.95),
+ ),
+ 'bi-weekly': (
+ ( 273, 0.00, 0.00),
+ ( 408, 0.00, 2.26),
+ ( 1016, 3.05, 3.22),
+ ( 1581, 22.63, 4.91),
+ ( 1961, 50.37, 6.20),
+ ( 2601, 73.93, 6.59),
+ ('inf', 116.11, 6.95),
+ ),
+ 'semi-monthly': (
+ ( 296, 0.00, 0.00),
+ ( 442, 0.00, 2.26),
+ ( 1101, 3.30, 3.22),
+ ( 1713, 24.52, 4.91),
+ ( 2125, 54.57, 6.20),
+ ( 2818, 80.11, 6.59),
+ ('inf', 125.78, 6.95),
+ ),
+ 'monthly': (
+ ( 592, 0.00, 0.00),
+ ( 884, 0.00, 2.26),
+ ( 2202, 6.60, 3.22),
+ ( 3425, 49.04, 4.91),
+ ( 4249, 109.09, 6.20),
+ ( 5635, 160.18, 6.59),
+ ('inf', 251.52, 6.95),
+ ),
+ 'quarterly': (
+ ( 1775, 0.00, 0.00),
+ ( 2653, 0.00, 2.26),
+ ( 6605, 19.84, 3.22),
+ ( 10275, 147.09, 4.91),
+ ( 12748, 327.29, 6.20),
+ ( 16905, 480.62, 6.59),
+ ( 'inf', 754.57, 6.95),
+ ),
+ 'semi-annual': (
+ ( 3550, 0.00, 0.00),
+ ( 5305, 0.00, 2.26),
+ ( 13210, 39.66, 3.22),
+ ( 20550, 294.20, 4.91),
+ ( 25495, 654.59, 6.20),
+ ( 33810, 961.18, 6.59),
+ ( 'inf', 1509.14, 6.95),
+ ),
+ 'annually': (
+ ( 7100, 0.00, 0.00),
+ ( 10610, 0.00, 2.26),
+ ( 26420, 79.33, 3.22),
+ ( 41100, 588.41, 4.91),
+ ( 50990, 1309.20, 6.20),
+ ( 67620, 1992.38, 6.59),
+ ( 'inf', 3018.30, 6.95),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US NE Nebraska Allowances Rate
+ us_ne_sit_allowances_rate
+
+
+
+
+ {
+ 'weekly' : 37.69,
+ 'bi-weekly' : 75.38,
+ 'semi-monthly': 81.67,
+ 'monthly' : 163.33,
+ 'quarterly' : 490.00,
+ 'semi-annual': 980.00,
+ 'annually': 1960.00,
+ }
+
+
+
+
+
+
+
+
+ US Nebraska - Nebraska Department of Labor - Unemployment Tax
+
+
+
+ US Nebraska - Nebraska Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US NE Nebraska State Unemployment
+ ER_US_NE_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ne_suta_wage_base', rate='us_ne_suta_rate', state_code='NE')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ne_suta_wage_base', rate='us_ne_suta_rate', state_code='NE')
+
+
+
+
+
+
+
+
+ EE: US NE Nebraska State Income Tax Withholding
+ EE_US_NE_SIT
+ python
+ result, _ = ne_nebraska_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ne_nebraska_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/nh_new_hampshire.xml b/l10n_us_hr_payroll/data/state/nh_new_hampshire.xml
new file mode 100644
index 00000000..374ff539
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nh_new_hampshire.xml
@@ -0,0 +1,51 @@
+
+
+
+
+ US NH New Hampshire SUTA Wage Base
+ us_nh_suta_wage_base
+
+
+
+
+ 14000.00
+
+
+
+
+
+
+
+ US NH New Hampshire SUTA Rate
+ us_nh_suta_rate
+
+
+
+
+ 1.2
+
+
+
+
+
+
+
+ US New Hampshire - Department of Employment Security - Unemployment Tax
+
+
+
+
+
+
+
+ ER: US NH New Hampshire State Unemployment
+ ER_US_NH_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nh_suta_wage_base', rate='us_nh_suta_rate', state_code='NH')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nh_suta_wage_base', rate='us_nh_suta_rate', state_code='NH')
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/nj_newjersey.xml b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
new file mode 100644
index 00000000..50c72dcd
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
@@ -0,0 +1,1048 @@
+
+
+
+
+ US NJ NewJersey SUTA Wage Base
+ us_nj_suta_wage_base
+
+
+
+
+ 34400.00
+
+
+
+
+ 35300.00
+
+
+
+
+
+
+
+
+ US NJ New Jersey Employer Unemployment SUTA Rate
+ us_nj_suta_rate
+
+
+
+
+ 2.6825
+
+
+
+
+ 2.6825
+
+
+
+
+
+
+ US NJ New Jersey Employee Unemployment SUTA Rate
+ us_nj_suta_ee_rate
+
+
+
+
+ 0.3825
+
+
+
+
+ 0.3825
+
+
+
+
+
+
+
+ US NJ New Jersey Employer State Disability Insurance Rate
+ us_nj_sdi_rate
+
+
+
+
+ 0.5
+
+
+
+
+ 0.5
+
+
+
+
+
+
+ US NJ New Jersey Employee State Disability Insurance Rate
+ us_nj_sdi_ee_rate
+
+
+
+
+ 0.17
+
+
+
+
+ 0.26
+
+
+
+
+
+
+
+ US NJ New Jersey Employer Workforce Development Rate
+ us_nj_wf_rate
+
+
+
+
+ 0.1175
+
+
+
+
+ 0.1175
+
+
+
+
+
+
+ US NJ New Jersey Employee Workforce Development Rate
+ us_nj_wf_ee_rate
+
+
+
+
+ 0.0425
+
+
+
+
+ 0.0425
+
+
+
+
+
+
+
+ US NJ New Jersey Employer Family Leave Insurance Rate
+ us_nj_fli_rate
+
+
+
+
+ 0.0
+
+
+
+
+ 0.0
+
+
+
+
+
+
+ US NJ New Jersey Employee Family Leave Insurance Rate
+ us_nj_fli_ee_rate
+
+
+
+
+ 0.08
+
+
+
+
+ 0.16
+
+
+
+
+
+
+
+ US NJ NewJersey SIT Rate Table
+ us_nj_sit_rate
+
+
+
+
+ {
+ 'A': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 769, 11.54, 3.90),
+ ( 1442, 15.29, 6.10),
+ ( 9615, 56.34, 7.00),
+ (96154, 628.46, 9.90),
+ ('inf', 9195.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 1538, 23.00, 3.90),
+ ( 2885, 31.00, 6.10),
+ ( 19231, 113.00, 7.00),
+ (192308, 1257.00, 9.90),
+ ( 'inf', 18392.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 1667, 25.00, 3.90),
+ ( 3125, 33.00, 6.10),
+ ( 20833, 122.00, 7.00),
+ (208333, 1362.00, 9.90),
+ ( 'inf', 19924.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2917, 25.00, 2.00),
+ ( 3333, 50.00, 3.90),
+ ( 6250, 66.00, 6.10),
+ ( 41667, 244.00, 7.00),
+ (416667, 2723.00, 9.90),
+ ( 'inf', 39848.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 10000, 150.00, 3.90),
+ ( 18750, 198.75, 6.10),
+ ( 125000, 732.50, 7.00),
+ (1250000, 8170.00, 9.90),
+ ( 'inf', 119545.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 20000, 300.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 250000, 1465.00, 7.00),
+ (2500000, 16340.00, 9.90),
+ ( 'inf', 239090.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 40000, 600.00, 3.90),
+ ( 75000, 795.00, 6.10),
+ ( 500000, 2930.00, 7.00),
+ (5000000, 32680.00, 9.90),
+ ( 'inf', 478180.00, 11.80),
+ ),
+ },
+ 'B': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 962, 5.77, 2.00),
+ ( 1346, 17.31, 2.70),
+ ( 1538, 27.69, 3.90),
+ ( 2885, 35.19, 6.10),
+ ( 9615, 117.31, 7.00),
+ (96154, 588.46, 9.90),
+ ('inf', 9155.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1923, 12.00, 2.00),
+ ( 2692, 35.00, 2.70),
+ ( 3076, 55.00, 3.90),
+ ( 5769, 70.00, 6.10),
+ ( 19231, 235.00, 7.00),
+ (192308, 1177.00, 9.90),
+ ( 'inf', 18312.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 2083, 12.50, 2.00),
+ ( 2917, 37.50, 2.70),
+ ( 3333, 59.99, 3.90),
+ ( 6250, 76.25, 6.10),
+ ( 20833, 254.19, 7.00),
+ (208333, 1275.00, 9.90),
+ ( 'inf', 19838.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 4167, 25.00, 2.00),
+ ( 5833, 75.00, 2.70),
+ ( 6667, 120.00, 3.90),
+ ( 12500, 153.00, 6.10),
+ ( 41667, 508.00, 7.00),
+ (416667, 2550.00, 9.90),
+ ( 'inf', 39675.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 12500, 75.00, 2.00),
+ ( 17500, 225.00, 2.70),
+ ( 20000, 360.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 125000, 1525.00, 7.00),
+ (1250000, 7650.00, 9.90),
+ ( 'inf', 119025.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 25000, 150.00, 2.00),
+ ( 35000, 450.00, 2.70),
+ ( 40000, 720.00, 3.90),
+ ( 75000, 915.00, 6.10),
+ ( 250000, 3050.00, 7.00),
+ (2500000, 15300.00, 9.90),
+ ( 'inf', 238050.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 50000, 300.00, 2.00),
+ ( 70000, 900.00, 2.70),
+ (80000, 1440.00, 3.90),
+ ( 150000, 1830.00, 6.10),
+ ( 500000, 6100.00, 7.00),
+ (5000000, 30600.00, 9.90),
+ ( 'inf', 476100.00, 11.80),
+ ),
+ },
+ 'C': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.30),
+ ( 962, 14.62, 2.80),
+ ( 1154, 20.00, 3.50),
+ ( 2885, 26.73, 5.60),
+ ( 9615, 123.65, 6.60),
+ (96154, 567.88, 9.90),
+ ('inf', 9135.19, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.30),
+ ( 1923, 29.23, 2.80),
+ ( 2308, 40.00, 3.50),
+ ( 5769, 53.46, 5.60),
+ ( 19231, 247.31, 6.60),
+ (192308, 1135.77, 9.90),
+ ( 'inf', 18270.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.30),
+ ( 2083, 31.67, 2.80),
+ ( 2500, 43.33, 3.50),
+ ( 6250, 57.92, 5.60),
+ ( 20833, 267.92, 6.60),
+ (208333, 1230.42, 9.90),
+ ( 'inf', 19792.92, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.30),
+ ( 4167, 63.33, 2.80),
+ ( 5000, 86.67, 3.50),
+ ( 12500, 115.83, 5.60),
+ ( 41667, 535.85, 6.60),
+ (416667, 2460.83, 9.90),
+ ( 'inf', 39585.83, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.30),
+ ( 12500, 190.00, 2.80),
+ ( 15000, 260.00, 3.50),
+ ( 37500, 347.50, 5.60),
+ ( 125000, 1607.50, 6.60),
+ (1250000, 7382.50, 9.90),
+ ( 'inf', 118757.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.30),
+ ( 25000, 380.00, 2.80),
+ ( 30000, 520.00, 3.50),
+ ( 75000, 695.00, 5.60),
+ ( 250000, 3215.00, 6.60),
+ (2500000, 14765.00, 9.90),
+ ( 'inf', 237515.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.30),
+ ( 50000, 760.00, 2.80),
+ ( 60000, 1040.00, 3.50),
+ ( 150000, 1390.00, 5.60),
+ ( 500000, 6430.00, 6.60),
+ (5000000, 29530.00, 9.90),
+ ( 'inf', 475030.00, 11.80),
+ ),
+ },
+ 'D': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.70),
+ ( 962, 16.15, 3.40),
+ ( 1154, 22.69, 4.30),
+ ( 2885, 30.96, 5.60),
+ ( 9615, 127.88, 6.50),
+ ( 96154, 565.38, 9.90),
+ ( 'inf', 9132.69, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.70),
+ ( 1923, 32.31, 3.40),
+ ( 2308, 45.38, 4.30),
+ ( 5769, 61.92, 5.60),
+ ( 19231, 255.77, 6.50),
+ (192308, 1130.77, 9.90),
+ ( 'inf', 18265.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.70),
+ ( 2083, 35.00, 3.40),
+ ( 2500, 49.17, 4.30),
+ ( 6250, 67.08, 5.60),
+ ( 20833, 277.08, 6.50),
+ (208333, 1225.00, 9.90),
+ ( 'inf', 19787.50, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.70),
+ ( 4167, 70.00, 3.40),
+ ( 5000, 98.33, 4.00),
+ ( 12500, 134.17, 5.60),
+ ( 41667, 554.17, 6.50),
+ (416667, 2450.00, 9.90),
+ ( 'inf', 39575.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.07),
+ ( 12500, 210.00, 3.40),
+ ( 15000, 295.00, 4.30),
+ ( 37500, 402.50, 5.60),
+ ( 125000, 1662.50, 6.50),
+ (1250000, 7350.00, 9.90),
+ ( 'inf', 118725.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.70),
+ ( 25000, 420.00, 3.40),
+ ( 30000, 590.00, 4.30),
+ ( 75000, 805.00, 5.60),
+ ( 250000, 3325.00, 6.50),
+ (2500000, 14700.00, 9.90),
+ ( 'inf', 237450.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.70),
+ ( 50000, 840.00, 3.40),
+ ( 60000, 1180.00, 4.30),
+ ( 150000, 1610.00, 5.60),
+ ( 250000, 6650.00, 6.50),
+ (2500000, 29400.00, 9.90),
+ ( 'inf', 474900.00, 11.80),
+ ),
+ },
+ 'E': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 1923, 11.54, 5.80),
+ ( 9615, 84.04, 6.50),
+ ( 96154, 584.04, 9.90),
+ ( 'inf', 9151.35, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 3846, 23.00, 5.80),
+ ( 19231, 168.00, 6.50),
+ (192308, 1168.00, 9.90),
+ ( 'inf', 18303.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 4167, 25.00, 5.80),
+ ( 20833, 182.00, 6.50),
+ (208333, 1265.00, 9.90),
+ ( 'inf', 19828.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2916, 25.00, 2.00),
+ ( 8333, 50.00, 5.80),
+ ( 41667, 364.00, 6.50),
+ (416667, 2531.00, 9.90),
+ ( 'inf', 39656.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 25000, 150.00, 5.80),
+ ( 125000, 1092.50, 6.50),
+ (1250000, 7592.50, 9.90),
+ ( 'inf', 118967.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 50000, 300.00, 5.80),
+ ( 250000, 2185.00, 6.50),
+ (2500000, 15185.00, 9.90),
+ ( 'inf', 237935.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 100000, 600.00, 5.80),
+ ( 500000, 4370.00, 6.50),
+ (5000000, 30370.00, 9.90),
+ ( 'inf', 475870.00, 11.80),
+ ),
+ },
+ }
+
+
+
+
+
+ {
+ 'A': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 769, 11.54, 3.90),
+ ( 1442, 15.29, 6.10),
+ ( 9615, 56.34, 7.00),
+ (96154, 628.46, 9.90),
+ ('inf', 9195.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 1538, 23.00, 3.90),
+ ( 2885, 31.00, 6.10),
+ ( 19231, 113.00, 7.00),
+ (192308, 1257.00, 9.90),
+ ( 'inf', 18392.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 1667, 25.00, 3.90),
+ ( 3125, 33.00, 6.10),
+ ( 20833, 122.00, 7.00),
+ (208333, 1362.00, 9.90),
+ ( 'inf', 19924.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2917, 25.00, 2.00),
+ ( 3333, 50.00, 3.90),
+ ( 6250, 66.00, 6.10),
+ ( 41667, 244.00, 7.00),
+ (416667, 2723.00, 9.90),
+ ( 'inf', 39848.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 10000, 150.00, 3.90),
+ ( 18750, 198.75, 6.10),
+ ( 125000, 732.50, 7.00),
+ (1250000, 8170.00, 9.90),
+ ( 'inf', 119545.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 20000, 300.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 250000, 1465.00, 7.00),
+ (2500000, 16340.00, 9.90),
+ ( 'inf', 239090.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 40000, 600.00, 3.90),
+ ( 75000, 795.00, 6.10),
+ ( 500000, 2930.00, 7.00),
+ (5000000, 32680.00, 9.90),
+ ( 'inf', 478180.00, 11.80),
+ ),
+ },
+ 'B': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 962, 5.77, 2.00),
+ ( 1346, 17.31, 2.70),
+ ( 1538, 27.69, 3.90),
+ ( 2885, 35.19, 6.10),
+ ( 9615, 117.31, 7.00),
+ (96154, 588.46, 9.90),
+ ('inf', 9155.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1923, 12.00, 2.00),
+ ( 2692, 35.00, 2.70),
+ ( 3076, 55.00, 3.90),
+ ( 5769, 70.00, 6.10),
+ ( 19231, 235.00, 7.00),
+ (192308, 1177.00, 9.90),
+ ( 'inf', 18312.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 2083, 12.50, 2.00),
+ ( 2917, 37.50, 2.70),
+ ( 3333, 59.99, 3.90),
+ ( 6250, 76.25, 6.10),
+ ( 20833, 254.19, 7.00),
+ (208333, 1275.00, 9.90),
+ ( 'inf', 19838.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 4167, 25.00, 2.00),
+ ( 5833, 75.00, 2.70),
+ ( 6667, 120.00, 3.90),
+ ( 12500, 153.00, 6.10),
+ ( 41667, 508.00, 7.00),
+ (416667, 2550.00, 9.90),
+ ( 'inf', 39675.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 12500, 75.00, 2.00),
+ ( 17500, 225.00, 2.70),
+ ( 20000, 360.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 125000, 1525.00, 7.00),
+ (1250000, 7650.00, 9.90),
+ ( 'inf', 119025.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 25000, 150.00, 2.00),
+ ( 35000, 450.00, 2.70),
+ ( 40000, 720.00, 3.90),
+ ( 75000, 915.00, 6.10),
+ ( 250000, 3050.00, 7.00),
+ (2500000, 15300.00, 9.90),
+ ( 'inf', 238050.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 50000, 300.00, 2.00),
+ ( 70000, 900.00, 2.70),
+ (80000, 1440.00, 3.90),
+ ( 150000, 1830.00, 6.10),
+ ( 500000, 6100.00, 7.00),
+ (5000000, 30600.00, 9.90),
+ ( 'inf', 476100.00, 11.80),
+ ),
+ },
+ 'C': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.30),
+ ( 962, 14.62, 2.80),
+ ( 1154, 20.00, 3.50),
+ ( 2885, 26.73, 5.60),
+ ( 9615, 123.65, 6.60),
+ (96154, 567.88, 9.90),
+ ('inf', 9135.19, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.30),
+ ( 1923, 29.23, 2.80),
+ ( 2308, 40.00, 3.50),
+ ( 5769, 53.46, 5.60),
+ ( 19231, 247.31, 6.60),
+ (192308, 1135.77, 9.90),
+ ( 'inf', 18270.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.30),
+ ( 2083, 31.67, 2.80),
+ ( 2500, 43.33, 3.50),
+ ( 6250, 57.92, 5.60),
+ ( 20833, 267.92, 6.60),
+ (208333, 1230.42, 9.90),
+ ( 'inf', 19792.92, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.30),
+ ( 4167, 63.33, 2.80),
+ ( 5000, 86.67, 3.50),
+ ( 12500, 115.83, 5.60),
+ ( 41667, 535.85, 6.60),
+ (416667, 2460.83, 9.90),
+ ( 'inf', 39585.83, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.30),
+ ( 12500, 190.00, 2.80),
+ ( 15000, 260.00, 3.50),
+ ( 37500, 347.50, 5.60),
+ ( 125000, 1607.50, 6.60),
+ (1250000, 7382.50, 9.90),
+ ( 'inf', 118757.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.30),
+ ( 25000, 380.00, 2.80),
+ ( 30000, 520.00, 3.50),
+ ( 75000, 695.00, 5.60),
+ ( 250000, 3215.00, 6.60),
+ (2500000, 14765.00, 9.90),
+ ( 'inf', 237515.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.30),
+ ( 50000, 760.00, 2.80),
+ ( 60000, 1040.00, 3.50),
+ ( 150000, 1390.00, 5.60),
+ ( 500000, 6430.00, 6.60),
+ (5000000, 29530.00, 9.90),
+ ( 'inf', 475030.00, 11.80),
+ ),
+ },
+ 'D': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.70),
+ ( 962, 16.15, 3.40),
+ ( 1154, 22.69, 4.30),
+ ( 2885, 30.96, 5.60),
+ ( 9615, 127.88, 6.50),
+ ( 96154, 565.38, 9.90),
+ ( 'inf', 9132.69, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.70),
+ ( 1923, 32.31, 3.40),
+ ( 2308, 45.38, 4.30),
+ ( 5769, 61.92, 5.60),
+ ( 19231, 255.77, 6.50),
+ (192308, 1130.77, 9.90),
+ ( 'inf', 18265.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.70),
+ ( 2083, 35.00, 3.40),
+ ( 2500, 49.17, 4.30),
+ ( 6250, 67.08, 5.60),
+ ( 20833, 277.08, 6.50),
+ (208333, 1225.00, 9.90),
+ ( 'inf', 19787.50, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.70),
+ ( 4167, 70.00, 3.40),
+ ( 5000, 98.33, 4.00),
+ ( 12500, 134.17, 5.60),
+ ( 41667, 554.17, 6.50),
+ (416667, 2450.00, 9.90),
+ ( 'inf', 39575.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.07),
+ ( 12500, 210.00, 3.40),
+ ( 15000, 295.00, 4.30),
+ ( 37500, 402.50, 5.60),
+ ( 125000, 1662.50, 6.50),
+ (1250000, 7350.00, 9.90),
+ ( 'inf', 118725.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.70),
+ ( 25000, 420.00, 3.40),
+ ( 30000, 590.00, 4.30),
+ ( 75000, 805.00, 5.60),
+ ( 250000, 3325.00, 6.50),
+ (2500000, 14700.00, 9.90),
+ ( 'inf', 237450.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.70),
+ ( 50000, 840.00, 3.40),
+ ( 60000, 1180.00, 4.30),
+ ( 150000, 1610.00, 5.60),
+ ( 250000, 6650.00, 6.50),
+ (2500000, 29400.00, 9.90),
+ ( 'inf', 474900.00, 11.80),
+ ),
+ },
+ 'E': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 1923, 11.54, 5.80),
+ ( 9615, 84.04, 6.50),
+ ( 96154, 584.04, 9.90),
+ ( 'inf', 9151.35, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 3846, 23.00, 5.80),
+ ( 19231, 168.00, 6.50),
+ (192308, 1168.00, 9.90),
+ ( 'inf', 18303.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 4167, 25.00, 5.80),
+ ( 20833, 182.00, 6.50),
+ (208333, 1265.00, 9.90),
+ ( 'inf', 19828.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2916, 25.00, 2.00),
+ ( 8333, 50.00, 5.80),
+ ( 41667, 364.00, 6.50),
+ (416667, 2531.00, 9.90),
+ ( 'inf', 39656.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 25000, 150.00, 5.80),
+ ( 125000, 1092.50, 6.50),
+ (1250000, 7592.50, 9.90),
+ ( 'inf', 118967.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 50000, 300.00, 5.80),
+ ( 250000, 2185.00, 6.50),
+ (2500000, 15185.00, 9.90),
+ ( 'inf', 237935.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 100000, 600.00, 5.80),
+ ( 500000, 4370.00, 6.50),
+ (5000000, 30370.00, 9.90),
+ ( 'inf', 475870.00, 11.80),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US NJ NewJersey SIT Allowance Rate
+ us_nj_sit_allowance_rate
+
+
+
+
+ {
+ 'weekly': 19.20,
+ 'bi-weekly': 38.40,
+ 'semi-monthly': 41.60,
+ 'monthly': 83.30,
+ 'quarterly': 250.00,
+ 'semi-annual': 500.00,
+ 'annual': 1000.00,
+ 'daily or miscellaneous': 2.70,
+ }
+
+
+
+
+
+ {
+ 'weekly': 19.20,
+ 'bi-weekly': 38.40,
+ 'semi-monthly': 41.60,
+ 'monthly': 83.30,
+ 'quarterly': 250.00,
+ 'semi-annual': 500.00,
+ 'annual': 1000.00,
+ 'daily or miscellaneous': 2.70,
+ }
+
+
+
+
+
+
+
+ US New Jersey - Division of Taxation - Unemployment Tax
+
+
+
+ US New Jersey - Division of Taxation - Income Tax
+
+
+
+
+
+
+
+
+ ER: US NJ New Jersey State Unemployment
+ ER_US_NJ_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_suta_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_suta_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+ EE: US NJ New Jersey State Unemployment
+ EE_US_NJ_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_suta_ee_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_suta_ee_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+
+ ER: US NJ New Jersey State Disability Insurance
+ ER_US_NJ_SDI
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_sdi_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_sdi_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+ EE: US NJ New Jersey State Disability Insurance
+ EE_US_NJ_SDI
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_sdi_ee_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_sdi_ee_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+
+ ER: US NJ New Jersey Workforce Development
+ ER_US_NJ_WF
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_wf_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_wf_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+ EE: US NJ New Jersey Workforce Development
+ EE_US_NJ_WF
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_wf_ee_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_wf_ee_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+
+ ER: US NJ New Jersey Family Leave Insurance
+ ER_US_NJ_FLI
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_fli_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_fli_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+ EE: US NJ New Jersey Family Leave Insurance
+ EE_US_NJ_FLI
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_fli_ee_rate', state_code='NJ')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nj_suta_wage_base', rate='us_nj_fli_ee_rate', state_code='NJ')
+
+
+
+
+
+
+
+
+ EE: US NJ New Jersey State Income Tax Withholding
+ EE_US_NJ_SIT
+ python
+ result, _ = nj_newjersey_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = nj_newjersey_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/nm_new_mexico.xml b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
new file mode 100644
index 00000000..2c4249a0
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
@@ -0,0 +1,303 @@
+
+
+
+
+ US NM New Mexico SUTA Wage Base
+ us_nm_suta_wage_base
+
+
+
+
+ 25800.0
+
+
+
+
+
+
+
+ US NM New Mexico SUTA Rate
+ us_nm_suta_rate
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ US NM New Mexico SIT Tax Rate
+ us_nm_sit_tax_rate
+
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 119, 0.00, 0.0),
+ ( 225, 0.00, 1.7),
+ ( 331, 1.80, 3.2),
+ ( 427, 5.18, 4.7),
+ ( 619, 9.70, 4.9),
+ ( 927, 19.13, 4.9),
+ ( 1369, 34.20, 4.9),
+ ('inf', 55.88, 4.9),
+ ),
+ 'bi-weekly': (
+ ( 238, 0.00, 0.0),
+ ( 450, 0.00, 1.7),
+ ( 662, 3.60, 3.2),
+ ( 854, 10.37, 4.7),
+ ( 1238, 19.40, 4.9),
+ ( 1854, 38.25, 4.9),
+ ( 2738, 68.40, 4.9),
+ ('inf', 111.75, 4.9),
+ ),
+ 'semi-monthly': (
+ ( 258, 0.00, 0.0),
+ ( 488, 0.00, 1.7),
+ ( 717, 3.90, 3.2),
+ ( 925, 11.23, 4.7),
+ ( 1342, 21.02, 4.9),
+ ( 2008, 41.44, 4.9),
+ ( 2967, 74.10, 4.9),
+ ('inf', 121.06, 4.9),
+ ),
+ 'monthly': (
+ ( 517, 0.00, 0.0),
+ ( 975, 0.00, 1.7),
+ ( 1433, 7.79, 3.2),
+ ( 1850, 22.46, 4.7),
+ ( 2683, 42.04, 4.9),
+ ( 4017, 82.88, 4.9),
+ ( 5933, 148.21, 4.9),
+ ('inf', 242.13, 4.9),
+ ),
+ 'quarterly': (
+ ( 1550, 0.00, 0.0),
+ ( 2925, 0.00, 1.7),
+ ( 4300, 23.38, 3.2),
+ ( 5550, 67.38, 4.7),
+ ( 8050, 126.13, 4.9),
+ ( 12050, 248.63, 4.9),
+ ( 17800, 444.63, 4.9),
+ ( 'inf', 726.38, 4.9),
+ ),
+ 'semi-annual': (
+ ( 3100, 0.00, 0.0),
+ ( 5850, 0.00, 1.7),
+ ( 8600, 46.75, 3.2),
+ (11100, 134.75, 4.7),
+ (16100, 252.25, 4.9),
+ (24100, 497.25, 4.9),
+ (35600, 889.25, 4.9),
+ ('inf', 1452.75, 4.9),
+ ),
+ 'annually': (
+ ( 6200, 0.00, 0.0),
+ (11700, 0.00, 1.7),
+ (17200, 93.50, 3.2),
+ (22200, 269.50, 4.7),
+ (32200, 504.50, 4.9),
+ (48200, 994.50, 4.9),
+ (71200, 1778.50, 4.9),
+ ('inf', 2905.50, 4.9),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 238, 0.00, 0.0),
+ ( 392, 0.00, 1.7),
+ ( 546, 2.62, 3.2),
+ ( 700, 7.54, 4.7),
+ ( 1008, 14.77, 4.9),
+ ( 1469, 29.85, 4.9),
+ ( 2162, 52.46, 4.9),
+ ('inf', 86.38, 4.9),
+ ),
+ 'bi-weekly': (
+ ( 477, 0.00, 0.0),
+ ( 785, 0.00, 1.7),
+ ( 1092, 5.23, 3.2),
+ ( 1400, 15.08, 4.7),
+ (2015, 29.54, 4.9),
+ ( 2938, 59.69, 4.9),
+ ( 4323, 104.92, 4.9),
+ ('inf', 172.77, 4.9),
+ ),
+ 'semi-monthly': (
+ ( 517, 0.00, 0.0),
+ ( 850, 0.00, 1.7),
+ ( 1183, 5.67, 3.2),
+ ( 1517, 16.33, 4.7),
+ ( 2183, 32.00, 4.9),
+ ( 3183, 64.67, 4.9),
+ ( 4683, 113.67, 4.9),
+ ('inf', 187.17, 4.9),
+ ),
+ 'monthly': (
+ ( 1033, 0.00, 0.0),
+ ( 1700, 0.00, 1.7),
+ ( 2367, 11.33, 3.2),
+ ( 3033, 32.67, 4.7),
+ ( 4367, 64.00, 4.9),
+ ( 6367, 129.33, 4.9),
+ ( 9367, 227.33, 4.9),
+ ('inf', 374.33, 4.9),
+ ),
+ 'quarterly': (
+ ( 3100, 0.00, 0.0),
+ ( 5100, 0.00, 1.7),
+ ( 7100, 34.00, 3.2),
+ ( 9100, 98.00, 4.7),
+ (13100, 192.00, 4.9),
+ (19100, 388.00, 4.9),
+ (28100, 682.00, 4.9),
+ ('inf', 1123.00, 4.9),
+ ),
+ 'semi-annual': (
+ ( 6200, 0.00, 0.0),
+ (10200, 0.00, 1.7),
+ (14200, 68.00, 3.2),
+ (18200, 196.00, 4.7),
+ (26200, 384.00, 4.9),
+ (38200, 776.00, 4.9),
+ (56200, 1364.00, 4.9),
+ ('inf', 2246.00, 4.9),
+ ),
+ 'annually': (
+ ( 12400, 0.00, 0.0),
+ ( 20400, 0.00, 1.7),
+ ( 28400, 136.00, 3.2),
+ ( 36400, 392.00, 4.7),
+ ( 52400, 768.00, 4.9),
+ ( 76400, 1552.00, 4.9),
+ (112400, 2728.00, 4.9),
+ ( 'inf', 4492.00, 4.9),
+ ),
+ },
+ 'married_as_single': {
+ 'weekly': (
+ ( 179, 0.00, 0.0),
+ ( 333, 0.00, 1.7),
+ ( 487, 2.62, 3.2),
+ ( 641, 7.54, 4.7),
+ ( 949, 14.77, 4.9),
+ ( 1410, 29.85, 4.9),
+ ( 2102, 52.46, 4.9),
+ ('inf', 86.38, 4.9),
+ ),
+ 'bi-weekly': (
+ ( 359, 0.00, 0.0),
+ ( 666, 0.00, 1.7),
+ ( 974, 5.23, 3.2),
+ ( 1282, 15.08, 4.7),
+ ( 1897, 29.54, 4.9),
+ ( 2820, 59.69, 4.9),
+ ( 4205, 104.92, 4.9),
+ ('inf', 172.77, 4.9),
+ ),
+ 'semi-monthly': (
+ ( 389, 0.00, 0.0),
+ ( 722, 0.00, 1.7),
+ ( 1055, 5.67, 3.2),
+ ( 1389, 16.33, 4.7),
+ ( 2055, 32.00, 4.9),
+ ( 3055, 64.67, 4.9),
+ ( 4555, 113.67, 4.9),
+ ('inf', 187.17, 4.9),
+ ),
+ 'monthly': (
+ ( 777, 0.00, 0.0),
+ ( 1444, 0.00, 1.7),
+ ( 2110, 11.33, 3.2),
+ ( 2777, 32.67, 4.7),
+ ( 4110, 64.00, 4.9),
+ ( 6110, 129.33, 4.9),
+ ( 9110, 227.33, 4.9),
+ ('inf', 374.33, 4.9),
+ ),
+ 'quarterly': (
+ ( 2331, 0.00, 0.0),
+ ( 4331, 0.00, 1.7),
+ ( 6331, 34.00, 3.2),
+ ( 8331, 98.00, 4.7),
+ ( 12331, 192.00, 4.9),
+ ( 18331, 388.00, 4.9),
+ ( 27331, 682.00, 4.9),
+ ( 'inf', 1123.00, 4.9),
+ ),
+ 'semi-annual': (
+ ( 4663, 0.00, 0.0),
+ ( 8663, 0.00, 1.7),
+ ( 12663, 68.00, 3.2),
+ ( 16663, 196.00, 4.7),
+ ( 24663, 384.00, 4.9),
+ ( 36663, 776.00, 4.9),
+ ( 54663, 1364.00, 4.9),
+ ( 'inf', 2246.00, 4.9),
+ ),
+ 'annually': (
+ ( 9325, 0.00, 0.0),
+ ( 17325, 0.00, 1.7),
+ ( 25325, 136.00, 3.2),
+ ( 33325, 392.00, 4.7),
+ ( 49325, 768.00, 4.9),
+ ( 73325, 1552.00, 4.9),
+ (109325, 2728.00, 4.9),
+ ( 'inf', 4492.00, 4.9),
+ ),
+ }
+ }
+
+
+
+
+
+
+
+
+ US New Mexico - Department of Workforce Solutions - Unemployment Tax
+
+
+
+ US New Mexico - Department of Taxation and Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US NM New Mexico State Unemployment
+ ER_US_NM_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nm_suta_wage_base', rate='us_nm_suta_rate', state_code='NM')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nm_suta_wage_base', rate='us_nm_suta_rate', state_code='NM')
+
+
+
+
+
+
+
+
+ EE: US NM New Mexico State Income Tax Withholding
+ EE_US_NM_SIT
+ python
+ result, _ = nm_new_mexico_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = nm_new_mexico_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/nv_nevada.xml b/l10n_us_hr_payroll/data/state/nv_nevada.xml
new file mode 100644
index 00000000..46e3c2d5
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nv_nevada.xml
@@ -0,0 +1,53 @@
+
+
+
+
+ US NV Nevada SUTA Wage Base
+ us_nv_suta_wage_base
+
+
+
+
+ 32500.00
+
+
+
+
+
+
+
+ US NV Nevada SUTA Rate
+ us_nv_suta_rate
+
+
+
+
+ 2.95
+
+
+
+
+
+
+
+ US Nevada - Department of Employment, Training, and Rehabilitation, Employment Security Division - Unemployment Tax
+
+
+
+
+
+
+
+
+
+ ER: US NV Nevada State Unemployment (RT-6)
+ ER_US_NV_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nv_suta_wage_base', rate='us_nv_suta_rate', state_code='NV')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nv_suta_wage_base', rate='us_nv_suta_rate', state_code='NV')
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ny_new_york.xml b/l10n_us_hr_payroll/data/state/ny_new_york.xml
new file mode 100644
index 00000000..a6f17a70
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ny_new_york.xml
@@ -0,0 +1,429 @@
+
+
+
+
+ US NY New York SUTA Wage Base
+ us_ny_suta_wage_base
+
+
+
+
+ 11400.0
+
+
+
+
+ 11600.0
+
+
+
+
+
+
+
+ US NY New York SUTA Rate
+ us_ny_suta_rate
+
+
+
+
+ 2.5
+
+
+
+
+ 2.5
+
+
+
+
+
+
+ US NY New York SUTA RSF Rate
+ us_ny_suta_rsf_rate
+
+
+
+
+ 0.075
+
+
+
+
+ 0.075
+
+
+
+
+
+
+ US NY New York SUTA MCTMT Rate
+ us_ny_suta_mctmt_rate
+
+
+
+
+ 0.0
+
+
+
+
+ 0.0
+
+
+
+
+
+
+ US NY New York SIT Tax Rate
+ us_ny_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': ((163, 0.0400, 0.0), (225, 0.0450, 6.54), (267, 0.0525, 9.31), (412, 0.0590, 11.54), (1551, 0.0633, 20.04), (1862, 0.0657, 92.17), (2070, 0.0758, 112.58), (3032, 0.0808, 128.38), (4142, 0.0707, 206.08), (5104, 0.0856, 284.60), (20722, 0.0735, 366.90), (21684, 0.5208, 1514.85), ('inf', 0.0962, 2015.62)),
+ 'bi-weekly': ((327, 0.0400, 0.0), (450, 0.0450, 13.08), (535, 0.0525, 18.62), (823, 0.0590, 23.08), (3102, 0.0633, 40.08), (3723, 0.0657, 184.35), (4140, 0.0758, 225.15), (6063, 0.0808, 256.77), (8285, 0.0707, 412.15), (10208, 0.0856, 569.19), (41444, 0.0735, 733.81), (43367, 0.5208, 3029.69), ('inf', 0.0962, 4021.23)),
+ 'semi-monthly': ((354, 0.0400, 0.0), (488, 0.0450, 14.17), (579, 0.0525, 20.17), (892, 0.0590, 25.00), (3360, 0.0633, 43.42), (4033, 0.0657, 199.71), (4485, 0.0758, 243.92), (6569, 0.0808, 278.17), (8975, 0.0707, 446.50), (11058, 0.0856, 616.63), (44898, 0.0735, 794.96), (46981, 0.5208, 3282.17), ('inf', 0.0962, 4367.17)),
+ 'monthly': ((708, 0.0400, 0.0), (975, 0.0450, 28.33), (1158, 0.0525, 40.33), (1783, 0.0590, 50.00), (6721, 0.0633, 86.83), (8067, 0.0657, 399.42), (8971, 0.0758, 487.83), (13138, 0.0808, 556.33), (17950, 0.0707, 893.00), (22117, 0.0856, 1233.25), (89796, 0.0735, 1589.92), (93963, 0.5208, 6564.33), ('inf', 0.0962, 8734.33)),
+ 'annually': ((8500, 0.0400, 0.0), (11700, 0.0450, 340.00), (13900, 0.0525, 484.00), (21400, 0.0590, 600.00), (80650, 0.0633, 1042.00), (96800, 0.0657, 4793.00), (107650, 0.0758, 5854.00), (157650, 0.0808, 6676.00), (215400, 0.0707, 10716.00), (265400, 0.0856, 14799.00), (1077550, 0.0735, 19079.00), (1127550, 0.5208, 78772.00), ('inf', 0.0962, 104812.00)),
+ },
+ 'married': {
+ 'weekly': ((163, 0.0400, 0.0), (225, 0.0450, 6.54), (267, 0.0525, 9.31), (412, 0.0590, 11.54), (1551, 0.0633, 20.04), (1862, 0.0657, 92.17), (2070, 0.0783, 112.58), (3032, 0.0833, 128.90), (4068, 0.0785, 209.00), (6215, 0.0707, 290.37), (7177, 0.0916, 442.17), (20722, 0.0735, 530.25), (41449, 0.0765, 1525.83), (42411, 0.9454, 3111.42), ('inf', 0.0962, 4020.46)),
+ 'bi-weekly': ((327, 0.0400, 0.0), (450, 0.0450, 13.08), (535, 0.0525, 18.62), (823, 0.0590, 23.08), (3102, 0.0633, 40.08), (3723, 0.0657, 184.35), (4140, 0.0783, 225.15), (6063, 0.0833, 257.81), (8137, 0.0785, 418.00), (12431, 0.0707, 580.73), (14354, 0.0916, 884.35), (41444, 0.0735, 1060.50), (82898, 0.0765, 3051.65), (84821, 0.9454, 6222.85), ('inf', 0.0962, 8040.92)),
+ 'semi-monthly': ((354, 0.0400, 0.0), (488, 0.0450, 14.17), (579, 0.0525, 20.17), (892, 0.0590, 25.00), (3360, 0.0633, 43.42), (4033, 0.0657, 199.71), (4485, 0.0783, 243.92), (6569, 0.0833, 279.29), (8815, 0.0785, 452.83), (13476, 0.0707, 629.13), (15550, 0.0916, 958.04), (44898, 0.0735, 1148.88), (89806, 0.0765, 3305.96), (91890, 0.9454, 6741.42), ('inf', 0.0962, 8711.00)),
+ 'monthly': ((708, 0.0400, 0.0), (975, 0.0450, 28.33), (1158, 0.0525, 40.33), (1783, 0.0590, 50.00), (6721, 0.0633, 86.83), (8067, 0.0657, 399.42), (8971, 0.0783, 487.83), (13138, 0.0833, 558.58), (17629, 0.0785, 905.67), (26933, 0.0707, 1258.25), (31100, 0.0916, 1916.08), (89796, 0.0735, 2297.75), (179613, 0.0765, 6611.92), (183779, 0.9454, 13482.83), ('inf', 0.0962, 17422.00)),
+ 'annually': ((8500, 0.0400, 0.0), (11700, 0.0450, 340.00), (13900, 0.0525, 484.00), (21400, 0.0590, 600.00), (80650, 0.0633, 1042.00), (96800, 0.0657, 4793.00), (107650, 0.0783, 5854.00), (157650, 0.0833, 6703.00), (211550, 0.0785, 10868.00), (323200, 0.0707, 15099.00), (373200, 0.0916, 22993.00), (1077550, 0.0735, 27573.00), (2155350, 0.0765, 79343.00), (2205350, 0.9454, 161794.00), ('inf', 0.0962, 209064.00)),
+ }
+ }
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 163, 0.0400, 0.00),
+ ( 225, 0.0450, 6.54),
+ ( 267, 0.0525, 9.31),
+ ( 412, 0.0590, 11.54),
+ ( 1551, 0.0609, 20.04),
+ ( 1862, 0.0641, 89.42),
+ ( 2070, 0.0745, 109.35),
+ ( 3032, 0.0795, 124.88),
+ ( 4142, 0.0691, 201.33),
+ ( 5104, 0.0925, 278.06),
+ (20722, 0.0735, 367.00),
+ (21684, 0.5208, 1514.94),
+ ('inf', 0.0962, 2015.71),
+ ),
+ 'bi-weekly': (
+ ( 327, 0.0400, 0.00),
+ ( 450, 0.0450, 13.08),
+ ( 535, 0.0525, 18.62),
+ ( 823, 0.0590, 23.08),
+ ( 3102, 0.0609, 40.08),
+ ( 3723, 0.0641, 178.85),
+ ( 4140, 0.0745, 218.69),
+ ( 6063, 0.0795, 249.77),
+ ( 8285, 0.0691, 402.65),
+ (10208, 0.0925, 556.12),
+ (41444, 0.0735, 734.00),
+ (43367, 0.5208, 3029.88),
+ ('inf', 0.0962, 4031.42),
+ ),
+ 'semi-monthly': (
+ ( 354, 0.0400, 0.00),
+ ( 488, 0.0450, 14.17),
+ ( 579, 0.0525, 20.17),
+ ( 892, 0.0590, 25.00),
+ ( 3360, 0.0609, 43.42),
+ ( 4033, 0.0641, 193.75),
+ ( 4485, 0.0745, 236.92),
+ ( 6569, 0.0795, 270.58),
+ ( 8975, 0.0691, 436.21),
+ (11058, 0.0925, 602.46),
+ (44898, 0.0735, 795.17),
+ (46981, 0.5208, 3282.38),
+ ('inf', 0.0962, 4367.38),
+ ),
+ 'monthly': (
+ ( 708, 0.0400, 0.00),
+ ( 975, 0.0450, 28.33),
+ ( 1158, 0.0525, 40.33),
+ ( 1783, 0.0590, 50.00),
+ ( 6721, 0.0609, 86.83),
+ ( 8067, 0.0641, 387.50),
+ ( 8971, 0.0745, 473.83),
+ (13138, 0.0795, 541.17),
+ (17950, 0.0691, 872.42),
+ (22117, 0.0925, 1204.92),
+ (89796, 0.0735, 1590.33),
+ (93963, 0.5208, 6564.75),
+ ('inf', 0.0962, 8734.75),
+ ),
+ 'annually': (
+ ( 8500, 0.0400, 0.00),
+ ( 11700, 0.0450, 340.00),
+ ( 13900, 0.0525, 484.00),
+ ( 21400, 0.0590, 600.00),
+ ( 80650, 0.0609, 1042.00),
+ ( 96800, 0.0641, 4650.00),
+ ( 107650, 0.0745, 5686.00),
+ ( 157650, 0.0795, 6494.00),
+ ( 215400, 0.0691, 10469.00),
+ ( 265400, 0.0925, 14459.00),
+ (1077550, 0.0735, 19084.00),
+ (1127550, 0.5208, 78777.00),
+ ( 'inf', 0.0962, 104817.00),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 163, 0.0400, 0.00),
+ ( 225, 0.0450, 6.54),
+ ( 267, 0.0525, 9.31),
+ ( 412, 0.0590, 11.54),
+ ( 1551, 0.0609, 20.04),
+ ( 1862, 0.0641, 89.42),
+ ( 2070, 0.0746, 109.35),
+ ( 3032, 0.0796, 124.90),
+ ( 4068, 0.0794, 201.44),
+ ( 6215, 0.0691, 283.75),
+ ( 7177, 0.1019, 432.12),
+ (20722, 0.0735, 530.10),
+ (41449, 0.0765, 1525.65),
+ (42411, 0.9454, 3111.27),
+ ('inf', 0.0962, 4020.31),
+ ),
+ 'bi-weekly': (
+ ( 327, 0.0400, 0.00),
+ ( 450, 0.0450, 13.08),
+ ( 535, 0.0525, 18.62),
+ ( 823, 0.0590, 23.08),
+ ( 3102, 0.0609, 40.08),
+ ( 3723, 0.0641, 178.85),
+ ( 4140, 0.0746, 218.69),
+ ( 6063, 0.0796, 249.81),
+ ( 8137, 0.0794, 402.88),
+ (12431, 0.0691, 567.50),
+ (14354, 0.1019, 864.23),
+ (41444, 0.0735, 1060.19),
+ (82898, 0.0765, 3051.31),
+ (84821, 0.9454, 6222.54),
+ ('inf', 0.0962, 8040.62),
+ ),
+ 'semi-monthly': (
+ ( 354, 0.0400, 0.00),
+ ( 488, 0.0450, 14.17),
+ ( 579, 0.0525, 20.17),
+ ( 892, 0.0590, 25.00),
+ ( 3360, 0.0609, 43.42),
+ ( 4033, 0.0641, 193.75),
+ ( 4485, 0.0746, 236.92),
+ ( 6569, 0.0796, 270.63),
+ ( 8815, 0.0794, 436.46),
+ (13467, 0.0691, 614.79),
+ (15550, 0.1019, 936.25),
+ (44898, 0.0735, 1148.54),
+ (89806, 0.0765, 3305.58),
+ (91890, 0.9454, 6741.08),
+ ('inf', 0.0962, 8710.67),
+ ),
+ 'monthly': (
+ ( 708, 0.0400, 0.00),
+ ( 975, 0.0450, 28.33),
+ ( 1158, 0.0525, 40.33),
+ ( 1783, 0.0590, 50.00),
+ ( 6721, 0.0609, 86.83),
+ ( 8067, 0.0641, 387.50),
+ ( 8971, 0.0746, 473.83),
+ ( 13138, 0.0796, 541.25),
+ ( 17629, 0.0794, 872.92),
+ ( 26933, 0.0691, 1229.58),
+ ( 31100, 0.1019, 1872.50),
+ ( 89796, 0.0735, 2297.08),
+ (179613, 0.0765, 6611.17),
+ (183779, 0.9454, 13482.17),
+ ( 'inf', 0.0962, 17421.33),
+ ),
+ 'annually': (
+ ( 8500, 0.0400, 0.00),
+ ( 11700, 0.0450, 340.00),
+ ( 13900, 0.0525, 484.00),
+ ( 21400, 0.0590, 600.00),
+ ( 80650, 0.0609, 1042.00),
+ ( 96800, 0.0641, 4650.00),
+ ( 107650, 0.0746, 5686.00),
+ ( 157650, 0.0796, 6495.00),
+ ( 211550, 0.0794, 10475.00),
+ ( 323200, 0.0691, 14755.00),
+ ( 373200, 0.1019, 22470.00),
+ (1077550, 0.0735, 27565.00),
+ (2155350, 0.0765, 79334.00),
+ (2205350, 0.9454, 161786.00),
+ ( 'inf', 0.0962, 209056.00),
+ ),
+ }
+ }
+
+
+
+
+
+
+ US NY New York Over 10 Exemption Rate
+ us_ny_sit_over_10_exemption_rate
+
+
+
+
+ {
+ 'weekly': (142.30, 152.90, 19.25),
+ 'bi-weekly': (284.60, 305.80, 38.50),
+ 'semi-monthly': (308.35, 331.25, 41.65),
+ 'monthly': (616.70, 662.50, 83.30),
+ 'annual': (7400, 7950, 1000),
+ }
+
+
+
+
+ {
+ 'weekly': (142.30, 152.90, 19.25),
+ 'bi-weekly': (284.60, 305.80, 38.50),
+ 'semi-monthly': (308.35, 331.25, 41.65),
+ 'monthly': (616.70, 662.50, 83.30),
+ 'annual': (7400, 7950, 1000),
+ }
+
+
+
+
+
+
+ US NY New York Deduction Exemption Rate
+ us_ny_sit_deduction_exemption_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (142.30, 161.55, 180.80, 200.05, 219.30, 238.55, 257.80, 277.05, 296.30, 315.55, 334.80),
+ 'bi-weekly': (284.60, 323.10, 361.60, 400.10, 438.60, 477.10, 515.60, 544.10, 592.60, 631.10, 669.60),
+ 'semi-monthly': (308.35, 350.0, 391.65, 433.30, 474.95, 516.60, 558.25, 599.90, 641.55, 683.20, 724.85),
+ 'monthly': (616.70, 700, 783.30, 866.60, 949.90, 1033.20, 1116.50, 1199.80, 1283.10, 1366.40, 1449.70),
+ 'annually': (7400, 8400, 9400, 10400, 11400, 12400, 13400, 14400, 15400, 16400, 17400),
+ },
+ 'married': {
+ 'weekly': (152.90, 172.15, 191.40, 210.65, 229.90, 249.15, 268.40, 287.65, 306.90, 326.15, 345.40),
+ 'bi-weekly': (305.80, 344.30, 382.80, 421.30, 459.80, 498.30, 536.80, 575.30, 613.80, 652.30, 690.80),
+ 'semi-monthly': (331.25, 372.90, 414.55, 456.20, 497.85, 539.50, 581.15, 622.80, 664.45, 706.10, 747.75),
+ 'monthly': (662.50, 745.80, 829.10, 912.40, 995.70, 1079.00, 1162.30, 1245.60, 1328.90, 1412.20, 1495.50),
+ 'annually': (7950, 8950, 9950, 10950, 11950, 12950, 13950, 14950, 15950, 16950, 17950),
+ },
+ }
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (142.30, 161.55, 180.80, 200.05, 219.30, 238.55, 257.80, 277.05, 296.30, 315.55, 334.80),
+ 'bi-weekly': (284.60, 323.10, 361.60, 400.10, 438.60, 477.10, 515.60, 544.10, 592.60, 631.10, 669.60),
+ 'semi-monthly': (308.35, 350.0, 391.65, 433.30, 474.95, 516.60, 558.25, 599.90, 641.55, 683.20, 724.85),
+ 'monthly': (616.70, 700, 783.30, 866.60, 949.90, 1033.20, 1116.50, 1199.80, 1283.10, 1366.40, 1449.70),
+ 'annually': (7400, 8400, 9400, 10400, 11400, 12400, 13400, 14400, 15400, 16400, 17400),
+ },
+ 'married': {
+ 'weekly': (152.90, 172.15, 191.40, 210.65, 229.90, 249.15, 268.40, 287.65, 306.90, 326.15, 345.40),
+ 'bi-weekly': (305.80, 344.30, 382.80, 421.30, 459.80, 498.30, 536.80, 575.30, 613.80, 652.30, 690.80),
+ 'semi-monthly': (331.25, 372.90, 414.55, 456.20, 497.85, 539.50, 581.15, 622.80, 664.45, 706.10, 747.75),
+ 'monthly': (662.50, 745.80, 829.10, 912.40, 995.70, 1079.00, 1162.30, 1245.60, 1328.90, 1412.20, 1495.50),
+ 'annually': (7950, 8950, 9950, 10950, 11950, 12950, 13950, 14950, 15950, 16950, 17950),
+ },
+ }
+
+
+
+
+
+
+
+ US New York - Department of Taxation and Finance - Unemployment Tax
+
+
+
+ US New York - Department of Taxation and Finance - Re-employment Service Fund
+
+
+
+ US New York - Department of Taxation and Finance - Metropolitan Commuter Transportation Mobility Tax
+
+
+
+ US New York - Department of Taxation and Finance - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US NY New York State Unemployment
+ ER_US_NY_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ny_suta_wage_base', rate='us_ny_suta_rate', state_code='NY')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ny_suta_wage_base', rate='us_ny_suta_rate', state_code='NY')
+
+
+
+
+
+
+
+
+ ER: US NY New York State Re-employment Service Fund
+ ER_US_NY_SUTA_RSF
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ny_suta_wage_base', rate='us_ny_suta_rsf_rate', state_code='NY')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ny_suta_wage_base', rate='us_ny_suta_rsf_rate', state_code='NY')
+
+
+
+
+
+
+
+
+ ER: US NY New York State Metropolitan Commuter Transportation Mobility Tax
+ ER_US_NY_SUTA_MCTMT
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ny_suta_wage_base', rate='us_ny_suta_mctmt_rate', state_code='NY')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ny_suta_wage_base', rate='us_ny_suta_mctmt_rate', state_code='NY')
+
+
+
+
+
+
+
+
+ EE: US NY New York State Income Tax Withholding
+ EE_US_NY_SIT
+ python
+ result, _ = ny_new_york_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ny_new_york_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/oh_ohio.xml b/l10n_us_hr_payroll/data/state/oh_ohio.xml
new file mode 100644
index 00000000..e6db8eb8
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/oh_ohio.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ US OH Ohio SUTA Wage Base
+ us_oh_suta_wage_base
+
+
+
+
+ 9500.00
+
+
+
+
+ 9000.00
+
+
+
+
+
+
+
+ US OH Ohio SUTA Rate
+ us_oh_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US OH Ohio SIT Rate Table
+ us_oh_sit_rate
+
+
+
+
+
+
+ [
+ ( 5000.00, 0.0, 0.005),
+ ( 10000.00, 25.0, 0.010),
+ ( 15000.00, 75.0, 0.020),
+ ( 20000.00, 175.0, 0.025),
+ ( 40000.00, 300.0, 0.030),
+ ( 80000.00, 900.0, 0.035),
+ ( 100000.00, 2300.0, 0.040),
+ ( 'inf', 3100.0, 0.050),
+ ]
+
+
+
+
+
+
+ [
+ ( 5000.00, 0.0, 0.005),
+ ( 10000.00, 25.0, 0.010),
+ ( 15000.00, 75.0, 0.020),
+ ( 20000.00, 175.0, 0.025),
+ ( 40000.00, 300.0, 0.030),
+ ( 80000.00, 900.0, 0.035),
+ ( 100000.00, 2300.0, 0.040),
+ ( 'inf', 3100.0, 0.050),
+ ]
+
+
+
+
+
+
+ US OH Ohio SIT Exemption Rate
+ us_oh_sit_exemption_rate
+
+
+
+
+ 650.0
+
+
+
+
+ 650.0
+
+
+
+
+
+
+ US OH Ohio SIT Multiplier Value
+ us_oh_sit_multiplier
+
+
+
+
+ 1.075
+
+
+
+
+ 1.032
+
+
+
+
+
+
+
+ US Ohio - OBG - Unemployment
+
+
+
+ US Ohio - OBG - Income Withholding
+
+
+
+
+
+
+
+
+
+ ER: US OH Ohio State Unemployment (JFS-20125)
+ ER_US_OH_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_oh_suta_wage_base', rate='us_oh_suta_rate', state_code='OH')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_oh_suta_wage_base', rate='us_oh_suta_rate', state_code='OH')
+
+
+
+
+
+
+
+
+ EE: US OH Ohio State Income Tax Withholding (IT 501)
+ EE_US_OH_SIT
+ python
+ result, _ = oh_ohio_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = oh_ohio_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ok_oklahoma.xml b/l10n_us_hr_payroll/data/state/ok_oklahoma.xml
new file mode 100644
index 00000000..04aa8380
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ok_oklahoma.xml
@@ -0,0 +1,301 @@
+
+
+
+
+ US OK Oklahoma SUTA Wage Base
+ us_ok_suta_wage_base
+
+
+
+
+ 18700.0
+
+
+
+
+
+
+
+ US OK Oklahoma SUTA Rate
+ us_ok_suta_rate
+
+
+
+
+ 1.5
+
+
+
+
+
+
+ US OK Oklahoma Allowances Rate
+ us_ok_sit_allowances_rate
+
+
+
+
+ {
+ 'weekly' : 19.23,
+ 'bi-weekly' : 38.46,
+ 'semi-monthly': 41.67,
+ 'monthly' : 83.33,
+ 'quarterly' : 250.00,
+ 'semi-annual': 500.00,
+ 'annually': 1000.00,
+ }
+
+
+
+
+
+
+ US OK Oklahoma SIT Tax Rate
+ us_ok_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 122, 0.00, 0.00),
+ ( 141, 0.50, 0.00),
+ ( 170, 1.00, 0.10),
+ ( 194, 2.00, 0.38),
+ ( 216, 3.00, 0.87),
+ ( 261, 4.00, 1.53),
+ ('inf', 5.00, 3.30),
+ ),
+ 'bi-weekly': (
+ ( 244, 0.00, 0.00),
+ ( 283, 0.50, 0.00),
+ ( 340, 1.00, 0.19),
+ ( 388, 2.00, 0.77),
+ ( 433, 3.00, 1.73),
+ ( 521, 4.00, 3.06),
+ ('inf', 5.00, 6.60),
+ ),
+ 'semi-monthly': (
+ ( 265, 0.00, 0.00),
+ ( 306, 0.50, 0.00),
+ ( 369, 1.00, 0.21),
+ ( 421, 2.00, 0.83),
+ ( 469, 3.00, 1.88),
+ ( 565, 4.00, 3.31),
+ ('inf', 5.00, 7.15),
+ ),
+ 'monthly': (
+ ( 529, 0.00, 0.00),
+ ( 613, 0.50, 0.00),
+ ( 738, 1.00, 0.42),
+ ( 842, 2.00, 1.67),
+ ( 938, 3.00, 3.75),
+ (1129, 4.00, 6.63),
+ ('inf', 5.00, 14.29),
+ ),
+ 'quarterly': (
+ ( 1588, 0.00, 0.00),
+ ( 1838, 0.50, 0.00),
+ ( 2213, 1.00, 1.25),
+ ( 2525, 2.00, 5.00),
+ ( 2813, 3.00, 11.25),
+ ( 3388, 4.00, 19.88),
+ ('inf', 5.00, 42.88),
+ ),
+ 'semi-annual': (
+ ( 3175, 0.00, 0.00),
+ ( 3675, 0.50, 0.00),
+ ( 4425, 1.00, 2.50),
+ ( 5050, 2.00, 10.00),
+ (5625, 3.00, 22.50),
+ ( 6775, 4.00, 39.75),
+ ('inf', 5.00, 85.75),
+ ),
+ 'annually': (
+ ( 6350, 0.00, 0.00),
+ ( 7350, 0.50, 0.00),
+ ( 8850, 1.00, 5.00),
+ (10100, 2.00, 20.00),
+ (11250, 3.00, 45.00),
+ (13550, 4.00, 79.50),
+ ('inf', 5.00, 171.50),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 244, 0.00, 0.00),
+ ( 283, 0.50, 0.00),
+ ( 340, 1.00, 0.19),
+ ( 388, 2.00, 0.77),
+ ( 433, 3.00, 1.73),
+ ( 479, 4.00, 3.06),
+ ('inf', 5.00, 4.90),
+ ),
+ 'bi-weekly': (
+ ( 488, 0.00, 0.00),
+ ( 565, 0.50, 0.00),
+ ( 681, 1.00, 0.38),
+ ( 777, 2.00, 1.54),
+ ( 865, 3.00, 3.46),
+ ( 958, 4.00, 6.12),
+ ('inf', 5.00, 9.81),
+ ),
+ 'semi-monthly': (
+ ( 529, 0.00, 0.00),
+ ( 613, 0.50, 0.00),
+ ( 738, 1.00, 0.42),
+ ( 842, 2.00, 1.67),
+ ( 938, 3.00, 3.75),
+ ( 1038, 4.00, 6.63),
+ ('inf', 5.00, 10.63),
+ ),
+ 'monthly': (
+ ( 1058, 0.00, 0.00),
+ ( 1225, 0.50, 0.00),
+ ( 1475, 1.00, 0.83),
+ ( 1683, 2.00, 3.33),
+ ( 1875, 3.00, 7.50),
+ ( 2075, 4.00, 13.25),
+ ('inf', 5.00, 21.25),
+ ),
+ 'quarterly': (
+ ( 3175, 0.00, 0.00),
+ ( 3675, 0.50, 0.00),
+ ( 4425, 1.00, 2.50),
+ ( 5050, 2.00, 10.00),
+ ( 5625, 3.00, 22.50),
+ ( 6225, 4.00, 39.75),
+ ('inf', 5.00, 63.75),
+ ),
+ 'semi-annual': (
+ ( 6350, 0.00, 0.00),
+ ( 7350, 0.50, 0.00),
+ ( 8850, 1.00, 5.00),
+ ( 10100, 2.00, 20.00),
+ ( 11250, 3.00, 45.00),
+ ( 12450, 4.00, 79.50),
+ ( 'inf', 5.00, 127.50),
+ ),
+ 'annually': (
+ ( 12700, 0.00, 0.00),
+ ( 14700, 0.50, 0.00),
+ ( 17700, 1.00, 10.00),
+ ( 20200, 2.00, 40.00),
+ ( 22500, 3.00, 90.00),
+ ( 24900, 4.00, 159.00),
+ ( 'inf', 5.00, 255.00),
+ ),
+ },
+ 'head_household': {
+ 'weekly': (
+ ( 122, 0.00, 0.00),
+ ( 141, 0.50, 0.00),
+ ( 170, 1.00, 0.10),
+ ( 194, 2.00, 0.38),
+ ( 216, 3.00, 0.87),
+ ( 261, 4.00, 1.53),
+ ('inf', 5.00, 3.30),
+ ),
+ 'bi-weekly': (
+ ( 244, 0.00, 0.00),
+ ( 283, 0.50, 0.00),
+ ( 340, 1.00, 0.19),
+ ( 388, 2.00, 0.77),
+ ( 433, 3.00, 1.73),
+ ( 521, 4.00, 3.06),
+ ('inf', 5.00, 6.60),
+ ),
+ 'semi-monthly': (
+ ( 265, 0.00, 0.00),
+ ( 306, 0.50, 0.00),
+ ( 369, 1.00, 0.21),
+ ( 421, 2.00, 0.83),
+ ( 469, 3.00, 1.88),
+ ( 565, 4.00, 3.31),
+ ('inf', 5.00, 7.15),
+ ),
+ 'monthly': (
+ ( 529, 0.00, 0.00),
+ ( 613, 0.50, 0.00),
+ ( 738, 1.00, 0.42),
+ ( 842, 2.00, 1.67),
+ ( 938, 3.00, 3.75),
+ ( 1129, 4.00, 6.63),
+ ('inf', 5.00, 14.29),
+ ),
+ 'quarterly': (
+ ( 1588, 0.00, 0.00),
+ ( 1838, 0.50, 0.00),
+ ( 2213, 1.00, 1.25),
+ ( 2525, 2.00, 5.00),
+ ( 2813, 3.00, 11.25),
+ ( 3388, 4.00, 19.88),
+ ('inf', 5.00, 42.88),
+ ),
+ 'semi-annual': (
+ ( 3175, 0.00, 0.00),
+ ( 3675, 0.50, 0.00),
+ ( 4425, 1.00, 2.50),
+ ( 5050, 2.00, 10.00),
+ ( 5625, 3.00, 22.50),
+ ( 6775, 4.00, 39.75),
+ ('inf', 5.00, 85.75),
+ ),
+ 'annually': (
+ ( 6350, 0.00, 0.00),
+ ( 7350, 0.50, 0.00),
+ ( 8850, 1.00, 5.00),
+ (10100, 2.00, 20.00),
+ (11250, 3.00, 45.00),
+ (13550, 4.00, 79.50),
+ ('inf', 5.00, 171.50),
+ ),
+ },
+ }
+
+
+
+
+
+
+
+ US Oklahoma - Employment Security Commission - Unemployment Tax
+
+
+
+ US Oklahoma - Tax Commission - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US OK Oklahoma State Unemployment
+ ER_US_OK_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ok_suta_wage_base', rate='us_ok_suta_rate', state_code='OK')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ok_suta_wage_base', rate='us_ok_suta_rate', state_code='OK')
+
+
+
+
+
+
+
+
+ EE: US OK Oklahoma State Income Tax Withholding
+ EE_US_OK_SIT
+ python
+ result, _ = ok_oklahoma_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ok_oklahoma_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/pa_pennsylvania.xml b/l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
new file mode 100644
index 00000000..f46a92b4
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
@@ -0,0 +1,131 @@
+
+
+
+
+ US PA Pennsylvania SUTA Wage Base (ER)
+ us_pa_suta_wage_base
+
+
+
+
+ 10000.00
+
+
+
+
+ 10000.00
+
+
+
+
+
+
+
+ US PA Pennsylvania SUTA Rate
+ us_pa_suta_rate
+
+
+
+
+ 3.6890
+
+
+
+
+ 3.6890
+
+
+
+
+
+
+ US PA Pennsylvania SUTA Employee Rate
+ us_pa_suta_ee_rate
+
+
+
+
+ 0.06
+
+
+
+
+ 0.06
+
+
+
+
+
+
+ US PA Pennsylvania SIT Rate
+ us_pa_sit_rate
+
+
+
+
+ 3.07
+
+
+
+
+ 3.07
+
+
+
+
+
+
+
+ US Pennsylvania - Department of Revenue - Unemployment Tax
+
+
+
+ US Pennsylvania - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US PA Pennsylvania State Unemployment (UC-2)
+ ER_US_PA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_pa_suta_wage_base', rate='us_pa_suta_rate', state_code='PA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_pa_suta_wage_base', rate='us_pa_suta_rate', state_code='PA')
+
+
+
+
+
+
+
+
+ EE: US PA Pennsylvania State Unemployment (UC-2)
+ EE_US_PA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, rate='us_pa_suta_ee_rate', state_code='PA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, rate='us_pa_suta_ee_rate', state_code='PA')
+
+
+
+
+
+
+
+
+ EE: US PA Pennsylvania State Income Tax Withholding (PA-501)
+ EE_US_PA_SIT
+ python
+ result, _ = general_state_income_withholding(payslip, categories, worked_days, inputs, rate='us_pa_sit_rate', state_code='PA')
+ code
+ result, result_rate = general_state_income_withholding(payslip, categories, worked_days, inputs, rate='us_pa_sit_rate', state_code='PA')
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ri_rhode_island.xml b/l10n_us_hr_payroll/data/state/ri_rhode_island.xml
new file mode 100644
index 00000000..155a2c86
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ri_rhode_island.xml
@@ -0,0 +1,141 @@
+
+
+
+
+ US RI Rhode Island SUTA Wage Base
+ us_ri_suta_wage_base
+
+
+
+
+ 24000.0
+
+
+
+
+
+
+
+ US RI Rhode Island SUTA Rate
+ us_ri_suta_rate
+
+
+
+
+ 1.06
+
+
+
+
+
+
+ US RI Rhode Island Exemption Rate
+ us_ri_sit_exemption_rate
+
+
+
+
+ {
+ 'weekly' : (( 0.00, 19.23), ( 4451.92, 0.00)),
+ 'bi-weekly' : (( 0.00, 38.46), ( 8903.85, 0.00)),
+ 'semi-monthly': (( 0.00, 41.67), ( 9645.83, 0.00)),
+ 'monthly' : (( 0.00, 83.33), ( 19291.67, 0.00)),
+ 'quarterly' : (( 0.00, 250.00), ( 57875.00, 0.00)),
+ 'semi-annually': (( 0.00, 500.00), ( 115750.00, 0.00)),
+ 'annually': (( 0.00, 1000.0), ( 231500.00, 0000)),
+ }
+
+
+
+
+
+
+ US RI Rhode Island SIT Tax Rate
+ us_ri_sit_tax_rate
+
+
+
+
+ {
+ 'weekly': (
+ ( 1255, 0.00, 3.75),
+ ( 2853, 47.06, 4.75),
+ ('inf', 122.97, 5.99),
+ ),
+ 'bi-weekly': (
+ ( 2510, 0.00, 3.75),
+ ( 5706, 94.13, 4.75),
+ ('inf', 245.94, 5.99),
+ ),
+ 'semi-monthly': (
+ ( 2719, 0.00, 3.75),
+ ( 6181, 101.96, 4.75),
+ ('inf', 266.41, 5.99),
+ ),
+ 'monthly': (
+ ( 5438, 0.00, 3.75),
+ (12363, 203.93, 4.75),
+ ('inf', 532.87, 5.99),
+ ),
+ 'quarterly': (
+ (16313, 0.00, 3.75),
+ (37088, 611.74, 4.75),
+ ('inf', 1598.55, 5.99),
+ ),
+ 'semi-annually': (
+ (32625, 0.00, 3.75),
+ (74175, 1223.44, 4.75),
+ ('inf', 3197.07, 5.99),
+ ),
+ 'annually': (
+ ( 65250, 0.00, 3.75),
+ (148350, 2446.88, 4.75),
+ ( 'inf', 6394.13, 5.99),
+ ),
+ }
+
+
+
+
+
+
+
+ US Rhode Island - Department of Labor and Training - Unemployment Tax
+
+
+
+ US Rhode Island - Division of Taxations - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US RI Rhode Island State Unemployment
+ ER_US_RI_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ri_suta_wage_base', rate='us_ri_suta_rate', state_code='RI')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ri_suta_wage_base', rate='us_ri_suta_rate', state_code='RI')
+
+
+
+
+
+
+
+
+ EE: US RI Rhode Island State Income Tax Withholding
+ EE_US_RI_SIT
+ python
+ result, _ = ri_rhode_island_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ri_rhode_island_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/sc_south_carolina.xml b/l10n_us_hr_payroll/data/state/sc_south_carolina.xml
new file mode 100644
index 00000000..b2a46192
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/sc_south_carolina.xml
@@ -0,0 +1,151 @@
+
+
+
+
+ US SC South Carolina SUTA Wage Base
+ us_sc_suta_wage_base
+
+
+
+
+ 14000.0
+
+
+
+
+ 14000.0
+
+
+
+
+
+
+
+ US SC South Carolina SUTA Rate
+ us_sc_suta_rate
+
+
+
+
+ 1.09
+
+
+
+
+
+ 0.55
+
+
+
+
+
+
+ US SC South Carolina SIT Tax Rate
+ us_sc_sit_tax_rate
+
+
+
+
+ [
+ ( 2450, 1.1, 0.0),
+ ( 4900, 3.0, 26.95),
+ ( 7350, 4.0, 100.45),
+ ( 9800, 5.0, 198.45),
+ (12250, 6.0, 320.95),
+ ('inf', 7.0, 467.95),
+ ]
+
+
+
+
+
+ [
+ ( 2620, 0.8, 0.0),
+ ( 5240, 3.0, 57.64),
+ ( 7860, 4.0, 110.04),
+ (10490, 5.0, 188.64),
+ (13110, 6.0, 293.54),
+ ('inf', 7.0, 424.64),
+ ]
+
+
+
+
+
+
+ US SC South Carolina Personal Exemption Rate
+ us_sc_sit_personal_exemption_rate
+
+
+
+
+ 2510
+
+
+
+
+ 2590
+
+
+
+
+
+
+ US SC South Carolina Standard Deduction Rate
+ us_sc_sit_standard_deduction_rate
+
+
+
+
+ 3470.0
+
+
+
+
+ 3820.0
+
+
+
+
+
+
+
+ US South Carolina - Department of Labor and Industrial Relations - Unemployment Tax
+
+
+
+ US South Carolina - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US SC South Carolina State Unemployment
+ ER_US_SC_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sc_suta_wage_base', rate='us_sc_suta_rate', state_code='SC')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sc_suta_wage_base', rate='us_sc_suta_rate', state_code='SC')
+
+
+
+
+
+
+
+
+ EE: US SC South Carolina State Income Tax Withholding
+ EE_US_SC_SIT
+ python
+ result, _ = sc_south_carolina_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = sc_south_carolina_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/sd_south_dakota.xml b/l10n_us_hr_payroll/data/state/sd_south_dakota.xml
new file mode 100644
index 00000000..95165ae5
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/sd_south_dakota.xml
@@ -0,0 +1,51 @@
+
+
+
+
+ US SD South Dakota SUTA Wage Base
+ us_sd_suta_wage_base
+
+
+
+
+ 15000.00
+
+
+
+
+
+
+
+ US SD South Dakota SUTA Rate
+ us_sd_suta_rate
+
+
+
+
+ 1.75
+
+
+
+
+
+
+
+ US South Dakota - Department of Labor - Unemployment Tax
+
+
+
+
+
+
+
+ ER: US SD South Dakota State Unemployment
+ ER_US_SD_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sd_suta_wage_base', rate='us_sd_suta_rate', state_code='SD')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sd_suta_wage_base', rate='us_sd_suta_rate', state_code='SD')
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/tn_tennessee.xml b/l10n_us_hr_payroll/data/state/tn_tennessee.xml
new file mode 100644
index 00000000..31465dee
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/tn_tennessee.xml
@@ -0,0 +1,51 @@
+
+
+
+
+ US TN Tennessee SUTA Wage Base
+ us_tn_suta_wage_base
+
+
+
+
+ 7000.00
+
+
+
+
+
+
+
+ US TN Tennessee SUTA Rate
+ us_tn_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+
+
+
+ US Tennessee - Department of Revenue - Unemployment Tax
+
+
+
+
+
+
+
+ ER: US TN Tennessee State Unemployment
+ ER_US_TN_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tn_suta_wage_base', rate='us_tn_suta_rate', state_code='TN')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tn_suta_wage_base', rate='us_tn_suta_rate', state_code='TN')
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/tx_texas.xml b/l10n_us_hr_payroll/data/state/tx_texas.xml
new file mode 100644
index 00000000..5d9c5772
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/tx_texas.xml
@@ -0,0 +1,127 @@
+
+
+
+
+ US TX Texas SUTA Wage Base
+ us_tx_suta_wage_base
+
+
+
+
+ 9000.0
+
+
+
+
+ 9000.0
+
+
+
+
+
+
+
+ US TX Texas SUTA Rate
+ us_tx_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US TX Texas Obligation Assessment Rate
+ us_tx_suta_oa_rate
+
+
+
+
+ 0.0
+
+
+
+
+ 0.0
+
+
+
+
+
+
+ US TX Texas Employment & Training Investment Assessment Rate
+ us_tx_suta_etia_rate
+
+
+
+
+ 0.1
+
+
+
+
+ 0.1
+
+
+
+
+
+
+
+ US Texas - Workforce Commission (Unemployment)
+
+
+
+
+
+
+
+
+
+ ER: US TX Texas State Unemployment (C-3)
+ ER_US_TX_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tx_suta_wage_base', rate='us_tx_suta_rate', state_code='TX')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tx_suta_wage_base', rate='us_tx_suta_rate', state_code='TX')
+
+
+
+
+
+
+
+
+ ER: US TX Texas Obligation Assessment (C-3)
+ ER_US_TX_SUTA_OA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tx_suta_wage_base', rate='us_tx_suta_oa_rate', state_code='TX')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tx_suta_wage_base', rate='us_tx_suta_oa_rate', state_code='TX')
+
+
+
+
+
+
+
+
+ ER: US TX Texas Employment & Training Investment Assessment (C-3)
+ ER_US_TX_SUTA_ETIA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tx_suta_wage_base', rate='us_tx_suta_etia_rate', state_code='TX')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tx_suta_wage_base', rate='us_tx_suta_etia_rate', state_code='TX')
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ut_utah.xml b/l10n_us_hr_payroll/data/state/ut_utah.xml
new file mode 100644
index 00000000..cb57813c
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ut_utah.xml
@@ -0,0 +1,166 @@
+
+
+
+
+ US UT Utah SUTA Wage Base
+ us_ut_suta_wage_base
+
+
+
+
+ 36600.0
+
+
+
+
+
+
+
+ US UT Utah SUTA Rate
+ us_ut_suta_rate
+
+
+
+
+ 0.1
+
+
+
+
+
+
+ US UT Utah TAX Rate
+ us_ut_tax_rate
+
+
+
+
+ 0.0495
+
+
+
+
+
+
+ US UT Utah Allowances Rate
+ us_ut_sit_allowances_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly' : 7,
+ 'bi-weekly' : 14,
+ 'semi-monthly': 15,
+ 'monthly' : 30,
+ 'quarterly' : 90,
+ 'semi-annual': 180,
+ 'annually': 360,
+ },
+ 'married': {
+ 'weekly' : 14,
+ 'bi-weekly' : 28,
+ 'semi-monthly': 30,
+ 'monthly' : 60,
+ 'quarterly' : 180,
+ 'semi-annual': 360,
+ 'annually': 720,
+ },
+ 'head_household': {
+ 'weekly' : 7,
+ 'bi-weekly' : 14,
+ 'semi-monthly': 15,
+ 'monthly' : 30,
+ 'quarterly' : 90,
+ 'semi-annual': 180,
+ 'annually': 360,
+ },
+ }
+
+
+
+
+
+
+ US UT Utah SIT Tax Rate
+ us_ut_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': ((137, 1.3)),
+ 'bi-weekly': ((274, 1.3)),
+ 'semi-monthly': ((297, 1.3)),
+ 'monthly': ((594, 1.3)),
+ 'quarterly': ((1782, 1.3)),
+ 'semi-annual': ((3564, 1.3)),
+ 'annually': ((7128, 1.3)),
+ },
+ 'married': {
+ 'weekly': ((274, 1.3)),
+ 'bi-weekly': (548, 1.3),
+ 'semi-monthly': ((594, 1.3)),
+ 'monthly': ((1188, 1.3)),
+ 'quarterly': ((3564, 1.3)),
+ 'semi-annual': ((7128, 1.3)),
+ 'annually': ((14256, 1.3)),
+ },
+ 'head_household': {
+ 'weekly': ((137, 1.3)),
+ 'bi-weekly': ((274, 1.3)),
+ 'semi-monthly': ((297, 1.3)),
+ 'monthly': ((594, 1.3)),
+ 'quarterly': ((1782, 1.3)),
+ 'semi-annual': ((3564, 1.3)),
+ 'annually': ((7128, 1.3)),
+ },
+ }
+
+
+
+
+
+
+
+ US Utah - Employment Security Commission - Unemployment Tax
+
+
+
+ US Utah - Tax Commission - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US UT Utah State Unemployment
+ ER_US_UT_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ut_suta_wage_base', rate='us_ut_suta_rate', state_code='UT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ut_suta_wage_base', rate='us_ut_suta_rate', state_code='UT')
+
+
+
+
+
+
+
+
+ EE: US UT Utah State Income Tax Withholding
+ EE_US_UT_SIT
+ python
+ result, _ = ut_utah_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ut_utah_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/va_virginia.xml b/l10n_us_hr_payroll/data/state/va_virginia.xml
new file mode 100644
index 00000000..5d19384a
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/va_virginia.xml
@@ -0,0 +1,138 @@
+
+
+
+
+ US VA Virginia SUTA Wage Base
+ us_va_suta_wage_base
+
+
+
+
+ 8000.0
+
+
+
+
+ 8000.0
+
+
+
+
+
+
+
+ US VA Virginia SUTA Rate
+ us_va_suta_rate
+
+
+
+
+ 2.51
+
+
+
+
+ 2.51
+
+
+
+
+
+
+ US VA Virginia SIT Rate Table
+ us_va_sit_rate
+
+
+
+
+ [
+ ( 0.00, 0.0, 2.00),
+ ( 3000.00, 60.0, 3.00),
+ ( 5000.00, 120.0, 5.00),
+ ( 17000.00, 720.0, 5.75),
+ ]
+
+
+
+
+
+
+ US VA Virginia SIT Exemption Rate Table
+ us_va_sit_exemption_rate
+
+
+
+
+ 930.0
+
+
+
+
+
+
+ US VA Virginia SIT Other Exemption Rate Table
+ us_va_sit_other_exemption_rate
+
+
+
+
+ 800.0
+
+
+
+
+
+
+ US VA Virginia SIT Deduction
+ us_va_sit_deduction
+
+
+
+
+ 4500.0
+
+
+
+
+
+
+
+ US Virginia - Department of Taxation - Unemployment Tax
+
+
+
+ US Virginia - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US VA Virginia State Unemployment
+ ER_US_VA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_va_suta_wage_base', rate='us_va_suta_rate', state_code='VA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_va_suta_wage_base', rate='us_va_suta_rate', state_code='VA')
+
+
+
+
+
+
+
+
+ EE: US VA Virginia State Income Tax Withholding
+ EE_US_VA_SIT
+ python
+ result, _ = va_virginia_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = va_virginia_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/vt_vermont.xml b/l10n_us_hr_payroll/data/state/vt_vermont.xml
new file mode 100644
index 00000000..74f1a491
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/vt_vermont.xml
@@ -0,0 +1,193 @@
+
+
+
+
+ US VT Vermont SUTA Wage Base
+ us_vt_suta_wage_base
+
+
+
+
+ 16100.0
+
+
+
+
+
+
+
+ US VT Vermont SUTA Rate
+ us_vt_suta_rate
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ US VT Vermont Allowances Rate
+ us_vt_sit_allowances_rate
+
+
+
+
+ {
+ 'weekly' : 83.65,
+ 'bi-weekly' : 167.31,
+ 'semi-monthly': 181.25,
+ 'monthly' : 362.50,
+ 'quarterly' : 1087.50,
+ 'annually': 4350.00,
+ }
+
+
+
+
+
+
+ US VT Vermont SIT Tax Rate
+ us_vt_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 60, 0.00, 0.00),
+ ( 836, 0.00, 3.35),
+ ( 1941, 26.00, 6.60),
+ ( 3983, 98.93, 7.60),
+ ('inf', 254.12, 8.75),
+ ),
+ 'bi-weekly': (
+ ( 120, 0.00, 0.00),
+ ( 1672, 0.00, 3.35),
+ ( 3882, 51.99, 6.60),
+ ( 7966, 197.85, 7.60),
+ ('inf', 508.24, 8.75),
+ ),
+ 'semi-monthly': (
+ ( 130, 0.00, 0.00),
+ ( 1811, 0.00, 3.35),
+ ( 4205, 56.31, 6.60),
+ ( 8630, 214.32, 7.60),
+ ('inf', 550.62, 8.75),
+ ),
+ 'monthly': (
+ ( 260, 0.00, 0.00),
+ ( 3623, 0.00, 3.35),
+ ( 8410, 112.66, 6.60),
+ (17260, 428.60, 7.60),
+ ('inf', 1101.20, 8.75),
+ ),
+ 'quarterly': (
+ ( 781, 0.00, 0.00),
+ (10869, 0.00, 3.35),
+ (25231, 337.95, 6.60),
+ (51781, 1285.84, 7.60),
+ ('inf', 3303.64, 8.75),
+ ),
+ 'annually': (
+ ( 3125, 0.00, 0.00),
+ ( 43475, 0.00, 3.35),
+ (100925, 1351.73, 6.60),
+ (207125, 5143.43, 7.60),
+ ( 'inf', 13214.63, 8.75),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 180, 0.00, 0.00),
+ ( 1477, 0.00, 3.35),
+ ( 3315, 43.45, 6.60),
+ ( 4956, 164.76, 7.60),
+ ('inf', 289.47, 8.75),
+ ),
+ 'bi-weekly': (
+ ( 361, 0.00, 0.00),
+ ( 2955, 0.00, 3.35),
+ ( 6630, 86.90, 6.60),
+ (9913, 329.45, 7.60),
+ ('inf', 578.96, 8.75),
+ ),
+ 'semi-monthly': (
+ ( 391, 0.00, 0.00),
+ ( 3201, 0.00, 3.35),
+ ( 7182, 94.14, 6.60),
+ (10739, 356.88, 7.60),
+ ('inf', 627.21, 8.75),
+ ),
+ 'monthly': (
+ ( 781, 0.00, 0.00),
+ ( 6402, 0.00, 3.35),
+ (14365, 188.30, 6.60),
+ (21477, 713.86, 7.60),
+ ('inf', 1254.37, 8.75),
+ ),
+ 'quarterly': (
+ ( 2344, 0.00, 0.00),
+ (19206, 0.00, 3.35),
+ (43094, 564.88, 6.60),
+ (64431, 2141.49, 7.60),
+ ('inf', 3763.10, 8.75),
+ ),
+ 'annually': (
+ ( 9375, 0.00, 0.00),
+ ( 76825, 0.00, 3.35),
+ (172375, 2259.58, 6.60),
+ (257725, 8565.88, 7.60),
+ ( 'inf', 15052.48, 8.75),
+ ),
+ },
+ }
+
+
+
+
+
+
+
+ US Vermont - Employment Security Commission - Unemployment Tax
+
+
+
+ US Vermont - Tax Commission - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US VT Vermont State Unemployment
+ ER_US_VT_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_vt_suta_wage_base', rate='us_vt_suta_rate', state_code='VT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_vt_suta_wage_base', rate='us_vt_suta_rate', state_code='VT')
+
+
+
+
+
+
+
+
+ EE: US VT Vermont State Income Tax Withholding
+ EE_US_VT_SIT
+ python
+ result, _ = vt_vermont_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = vt_vermont_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
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..3307689c
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wa_washington.xml
@@ -0,0 +1,200 @@
+
+
+
+
+ US WA Washington SUTA Wage Base
+ us_wa_suta_wage_base
+
+
+
+
+ 49800.0
+
+
+
+
+ 52700.00
+
+
+
+
+
+
+ US WA Washington FML Wage Base
+ us_wa_fml_wage_base
+
+
+
+
+ 132900.00
+
+
+
+
+ 137700.00
+
+
+
+
+
+
+
+ US WA Washington SUTA Rate
+ us_wa_suta_rate
+
+
+
+
+ 1.18
+
+
+
+
+ 1.0
+
+
+
+
+
+
+ US WA Washington FML Rate (Total)
+ us_wa_fml_rate
+
+
+
+
+ 0.4
+
+
+
+
+ 0.4
+
+
+
+
+
+
+ US WA Washington FML Rate (Employee)
+ us_wa_fml_rate_ee
+
+
+
+
+ 66.33
+
+
+
+
+ 66.33
+
+
+
+
+
+
+ US WA Washington FML Rate (Employer)
+ us_wa_fml_rate_er
+
+
+
+
+ 33.67
+
+
+
+
+ 33.67
+
+
+
+
+
+
+
+ US Washington - Employment Security Department (Unemployment)
+
+
+
+ US Washington - Department of Labor & Industries
+
+
+
+ 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_ee_code') and worked_days.WORK100 and worked_days.WORK100.number_of_hours and payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+ code
+ result, result_rate = worked_days.WORK100.number_of_hours, -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+
+
+
+
+
+
+
+
+ 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.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
+ code
+ result, result_rate = worked_days.WORK100.number_of_hours, -payslip.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/data/state/wi_wisconsin.xml b/l10n_us_hr_payroll/data/state/wi_wisconsin.xml
new file mode 100644
index 00000000..b62dc3f4
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wi_wisconsin.xml
@@ -0,0 +1,118 @@
+
+
+
+
+ US WI Wisconsin SUTA Wage Base
+ us_wi_suta_wage_base
+
+
+
+
+ 14000.00
+
+
+
+
+
+
+
+
+ US WI Wisconsin SUTA Rate
+ us_wi_suta_rate
+
+
+
+
+ 3.05
+
+
+
+
+
+
+ US WI Wisconsin Exemption Rate
+ us_wi_sit_exemption_rate
+
+
+
+
+ 22
+
+
+
+
+
+
+
+ US WI Wisconsin SIT Tax Rate
+ us_wi_sit_tax_rate
+
+
+
+
+ {
+ 'single': (
+ ( 5730, 0.0000, 0.00),
+ ( 15200, 4.0000, 0.00),
+ ( 16486, 4.4800, 378.80),
+ ( 26227, 6.5408, 436.41),
+ ( 62950, 7.0224, 1073.55),
+ (240190, 6.2700, 3652.39),
+ ( 'inf', 7.6500, 14765.34),
+ ),
+ 'married': (
+ ( 7870, 0.0000, 0.00),
+ ( 18780, 4.0000, 0.00),
+ ( 21400, 5.8400, 436.40),
+ ( 28308, 7.0080, 589.41),
+ ( 60750, 7.5240, 1073.52),
+ (240190, 6.2700, 3514.46),
+ ( 'inf', 7.6500, 14765.35),
+ ),
+ }
+
+
+
+
+
+
+
+ US Wisconsin - Department of Workforce Development - Unemployment Tax
+
+
+
+ US Wisconsin - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US WI Wisconsin State Unemployment
+ ER_US_WI_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wi_suta_wage_base', rate='us_wi_suta_rate', state_code='WI')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wi_suta_wage_base', rate='us_wi_suta_rate', state_code='WI')
+
+
+
+
+
+
+
+
+ EE: US WI Wisconsin State Income Tax Withholding
+ EE_US_WI_SIT
+ python
+ result, _ = wi_wisconsin_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = wi_wisconsin_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/wv_west_virginia.xml b/l10n_us_hr_payroll/data/state/wv_west_virginia.xml
new file mode 100644
index 00000000..ee542c4a
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wv_west_virginia.xml
@@ -0,0 +1,215 @@
+
+
+
+
+ US WV West Virginia SUTA Wage Base
+ us_wv_suta_wage_base
+
+
+
+
+ 12000.0
+
+
+
+
+
+
+
+ US WV West Virginia SUTA Rate
+ us_wv_suta_rate
+
+
+
+
+ 2.7
+
+
+
+
+
+
+ US WV West Virginia Exemption Rate
+ us_wv_sit_exemption_rate
+
+
+
+
+ {
+ 'weekly' : 38.46,
+ 'bi-weekly' : 76.92,
+ 'semi-monthly': 83.33,
+ 'monthly' : 166.67,
+ 'annually': 2000.00,
+ }
+
+
+
+
+
+
+ US WV West Virginia SIT Tax Rate
+ us_wv_sit_tax_rate
+
+
+
+
+ {
+ 'single': {
+ 'weekly':(
+ ( 192, 0.00, 3.0),
+ ( 481, 5.76, 4.0),
+ ( 769, 17.32, 4.5),
+ ( 1154, 30.28, 6.0),
+ ('inf', 53.38, 6.5),
+ ),
+ 'bi-weekly':(
+ ( 385, 0.00, 3.0),
+ ( 962, 11.55, 4.0),
+ ( 1538, 34.63, 4.5),
+ ( 2308, 60.55, 6.0),
+ ('inf', 106.75, 6.5),
+ ),
+ 'semi-monthly':(
+ ( 417, 0.00, 3.0),
+ ( 1042 , 12.51, 4.0),
+ ( 1667, 37.51, 4.5),
+ ( 2500, 65.64, 6.0),
+ ('inf', 115.62, 6.5),
+ ),
+ 'monthly':(
+ ( 833, 0.00, 3.0),
+ ( 2083, 24.99, 4.0),
+ ( 3333, 74.99, 4.5),
+ ( 5000, 131.24, 6.0),
+ ('inf', 231.26, 6.5),
+ ),
+ 'annually':(
+ ( 10000, 0.00, 3.0),
+ ( 25000, 300.00, 4.0),
+ ( 40000, 900.00, 4.5),
+ ( 60000, 1575.00, 6.0),
+ ( 'inf', 2775.00, 6.5),
+ ),
+ },
+ 'married': {
+ 'weekly':(
+ ( 115, 0.00, 3.0),
+ ( 288, 3.45, 4.0),
+ ( 462, 10.37, 4.5),
+ ( 692, 18.20, 6.0),
+ ('inf', 32.00, 6.5),
+ ),
+ 'bi-weekly':(
+ ( 231, 0.00, 3.0),
+ ( 577, 6.93, 4.0),
+ ( 923, 20.77, 4.5),
+ ( 1385, 36.34, 6.0),
+ ('inf', 64.06, 6.5),
+ ),
+ 'semi-monthly':(
+ ( 250, 0.00, 3.0),
+ ( 625, 7.50, 4.0),
+ ( 1000, 22.50, 4.5),
+ ( 1500, 39.38, 6.0),
+ ('inf', 69.38, 6.5),
+ ),
+ 'monthly':(
+ ( 500, 0.00, 3.0),
+ ( 1250, 15.00, 4.0),
+ ( 2000, 45.00, 4.5),
+ ( 3000, 78.75, 6.0),
+ ('inf', 138.75, 6.5),
+ ),
+ 'annually':(
+ ( 6000, 0.00, 3.0),
+ (15000, 180.00, 4.0),
+ (24000, 540.00, 4.5),
+ (36000, 945.00, 6.0),
+ ('inf', 1665.00, 6.5),
+ ),
+ },
+ 'head_household': {
+ 'weekly':(
+ ( 192, 0.00, 3.0),
+ ( 481, 5.76, 4.0),
+ ( 769, 17.32, 4.5),
+ ( 1154, 30.28, 6.0),
+ ('inf', 53.38, 6.5),
+ ),
+ 'bi-weekly':(
+ ( 385, 0.00, 3.0),
+ ( 962, 11.55, 4.0),
+ ( 1538, 34.63, 4.5),
+ ( 2308, 60.55, 6.0),
+ ('inf', 106.75, 6.5),
+ ),
+ 'semi-monthly':(
+ ( 417, 0.00, 3.0),
+ ( 1042, 12.51, 4.0),
+ ( 1667, 37.51, 4.5),
+ ( 2500, 65.64, 6.0),
+ ('inf', 115.62, 6.5),
+ ),
+ 'monthly':(
+ ( 833, 0.00, 3.0),
+ ( 2083, 24.99, 4.0),
+ ( 3333, 74.99, 4.5),
+ ( 5000, 131.24, 6.0),
+ ('inf', 231.26, 6.5),
+ ),
+ 'annually':(
+ ( 10000, 0.00, 3.0),
+ ( 25000, 300.00, 4.0),
+ ( 40000, 900.00, 4.5),
+ ( 60000, 1575.00, 6.0),
+ ( 'inf', 2775.00, 6.5),
+ ),
+ },
+ }
+
+
+
+
+
+
+
+ US West Virginia - WorkForce - Unemployment Tax
+
+
+
+ US West Virginia - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US WV West Virginia State Unemployment
+ ER_US_WV_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wv_suta_wage_base', rate='us_wv_suta_rate', state_code='WV')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wv_suta_wage_base', rate='us_wv_suta_rate', state_code='WV')
+
+
+
+
+
+
+
+
+ EE: US WV West Virginia State Income Tax Withholding
+ EE_US_WV_SIT
+ python
+ result, _ = wv_west_virginia_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = wv_west_virginia_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/wy_wyoming.xml b/l10n_us_hr_payroll/data/state/wy_wyoming.xml
new file mode 100644
index 00000000..247603c5
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wy_wyoming.xml
@@ -0,0 +1,61 @@
+
+
+
+
+ US WY Wyoming SUTA Wage Base
+ us_wy_suta_wage_base
+
+
+
+
+ 25400.00
+
+
+
+
+ 26400.00
+
+
+
+
+
+
+
+ US WY Wyoming SUTA Rate
+ us_wy_suta_rate
+
+
+
+
+ 2.10
+
+
+
+
+ 8.5
+
+
+
+
+
+
+
+ US Wyoming - Department of Workforce Services (WDWS) - Unemployment Tax
+
+
+
+
+
+
+
+ ER: US WY Wyoming State Unemployment
+ ER_US_WY_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wy_suta_wage_base', rate='us_wy_suta_rate', state_code='WY')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wy_suta_wage_base', rate='us_wy_suta_rate', state_code='WY')
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/migrations/13.0.0.0.1/post-migration.py b/l10n_us_hr_payroll/migrations/13.0.0.0.1/post-migration.py
new file mode 100644
index 00000000..406055d7
--- /dev/null
+++ b/l10n_us_hr_payroll/migrations/13.0.0.0.1/post-migration.py
@@ -0,0 +1,30 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+import odoo
+
+
+def migrate(cr, version):
+ """
+ Post-migration no contracts will have any structure types.
+ Unfortunately, we have no way of knowing if they used USA in the past
+ so we have to just assume they did (knowing of course that l10n_us_hr_payroll was installed)...
+ """
+ env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
+ structure_type = env.ref('l10n_us_hr_payroll.structure_type_employee')
+ cr.execute("UPDATE hr_contract "
+ "SET structure_type_id = %s "
+ "WHERE structure_type_id is null AND state in ('draft', 'open')", (structure_type.id, ))
+
+ """
+ Additionally, it is known that post-migration databases will have bad
+ work entry record states (and you will spend time trying to fix them
+ before you could run a payroll batch).
+ """
+ default_work_entry_type = env.ref('hr_work_entry.work_entry_type_attendance', raise_if_not_found=False)
+ if default_work_entry_type:
+ cr.execute("UPDATE hr_work_entry "
+ "SET work_entry_type_id = %s "
+ "WHERE work_entry_type_id is null", (default_work_entry_type.id, ))
+ cr.execute("UPDATE hr_work_entry "
+ "SET state = 'draft' "
+ "WHERE state = 'conflict'")
diff --git a/l10n_us_hr_payroll/migrations/13.0.0.0.1/pre-migration.py b/l10n_us_hr_payroll/migrations/13.0.0.0.1/pre-migration.py
new file mode 100644
index 00000000..e71317a4
--- /dev/null
+++ b/l10n_us_hr_payroll/migrations/13.0.0.0.1/pre-migration.py
@@ -0,0 +1,26 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+import odoo
+
+
+def migrate(cr, version):
+ """
+ Salary Rules can be archived by Odoo S.A. during migration.
+ This leaves them archived after the migration, and even un-archiving them
+ is not enough because they will then be pointed to a "migrated" structure.
+ """
+ env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
+ xml_refs = env['ir.model.data'].search([
+ ('module', '=', 'l10n_us_hr_payroll'),
+ ('model', '=', 'hr.salary.rule'),
+ ])
+ # I don't know why Odoo makes these non-updatable...
+ xml_refs.write({'noupdate': False})
+
+ rule_ids = xml_refs.mapped('res_id')
+ rules = env['hr.salary.rule'].browse(rule_ids)
+ rules.write({'active': True})
+
+ # Cannot add new selection type without fixing missing
+ cr.execute('UPDATE hr_payroll_structure SET schedule_pay = \'monthly\' WHERE schedule_pay is null;')
+ cr.execute('UPDATE hr_payroll_structure_type SET default_schedule_pay = \'monthly\' WHERE default_schedule_pay is null;')
diff --git a/l10n_us_hr_payroll/models/__init__.py b/l10n_us_hr_payroll/models/__init__.py
index c6d607ff..bb27e002 100644
--- a/l10n_us_hr_payroll/models/__init__.py
+++ b/l10n_us_hr_payroll/models/__init__.py
@@ -1,5 +1,8 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+from . import browsable_object
from . import hr_contract
from . import hr_payslip
+from . import res_config_settings
+from . import update
from . import us_payroll_config
diff --git a/l10n_us_hr_payroll/models/browsable_object.py b/l10n_us_hr_payroll/models/browsable_object.py
new file mode 100644
index 00000000..bed067fd
--- /dev/null
+++ b/l10n_us_hr_payroll/models/browsable_object.py
@@ -0,0 +1,148 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import fields
+from odoo.addons.hr_payroll.models import browsable_object
+
+
+class BrowsableObject(object):
+ def __init__(self, employee_id, dict, env):
+ self.employee_id = employee_id
+ self.dict = dict
+ self.env = env
+ # Customization to allow changing the behavior of the discrete browsable objects.
+ # you can think of this as 'compiling' the query based on the configuration.
+ sum_field = env['ir.config_parameter'].sudo().get_param('hr_payroll.payslip.sum_behavior', 'date_from')
+ if sum_field == 'date' and 'date' not in env['hr.payslip']:
+ # missing attribute, closest by definition
+ sum_field = 'date_to'
+ if not sum_field:
+ sum_field = 'date_from'
+ self._compile_browsable_query(sum_field)
+
+ def __getattr__(self, attr):
+ return attr in self.dict and self.dict.__getitem__(attr) or 0.0
+
+ def _compile_browsable_query(self, sum_field):
+ pass
+
+
+class InputLine(BrowsableObject):
+ """a class that will be used into the python code, mainly for usability purposes"""
+ def _compile_browsable_query(self, sum_field):
+ self.__browsable_query = """
+ SELECT sum(amount) as sum
+ FROM hr_payslip as hp, hr_payslip_input as pi
+ WHERE hp.employee_id = %s AND hp.state = 'done'
+ AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""".format(sum_field=sum_field)
+
+ def sum(self, code, from_date, to_date=None):
+ if to_date is None:
+ to_date = fields.Date.today()
+ self.env.cr.execute(self.__browsable_query, (self.employee_id, from_date, to_date, code))
+ return self.env.cr.fetchone()[0] or 0.0
+
+
+class WorkedDays(BrowsableObject):
+ """a class that will be used into the python code, mainly for usability purposes"""
+ def _compile_browsable_query(self, sum_field):
+ self.__browsable_query = """
+ SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours
+ FROM hr_payslip as hp, hr_payslip_worked_days as pi
+ WHERE hp.employee_id = %s AND hp.state = 'done'
+ AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""".format(sum_field=sum_field)
+
+ def _sum(self, code, from_date, to_date=None):
+ if to_date is None:
+ to_date = fields.Date.today()
+ self.env.cr.execute(self.__browsable_query, (self.employee_id, from_date, to_date, code))
+ return self.env.cr.fetchone()
+
+ def sum(self, code, from_date, to_date=None):
+ res = self._sum(code, from_date, to_date)
+ return res and res[0] or 0.0
+
+ def sum_hours(self, code, from_date, to_date=None):
+ res = self._sum(code, from_date, to_date)
+ return res and res[1] or 0.0
+
+
+class Payslips(BrowsableObject):
+ """a class that will be used into the python code, mainly for usability purposes"""
+ def _compile_browsable_query(self, sum_field):
+ # Note that the core odoo has this as `hp.credit_note = False` but what if it is NULL?
+ # reverse of the desired behavior.
+ self.__browsable_query_rule = """
+ SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
+ FROM hr_payslip as hp, hr_payslip_line as pl
+ WHERE hp.employee_id = %s AND hp.state = 'done'
+ AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""".format(sum_field=sum_field)
+ # Original (non-recursive)
+ # self.__browsable_query_category = """
+ # SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
+ # FROM hr_payslip as hp, hr_payslip_line as pl, hr_salary_rule_category as rc
+ # WHERE hp.employee_id = %s AND hp.state = 'done'
+ # AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id
+ # AND rc.id = pl.category_id AND rc.code = %s""".format(sum_field=sum_field)
+
+ # Hibou Recursive version
+ self.__browsable_query_category = """
+ WITH RECURSIVE
+ category_by_code as (
+ SELECT id
+ FROM hr_salary_rule_category
+ WHERE code = %s
+ ),
+ category_ids as (
+ SELECT COALESCE((SELECT id FROM category_by_code), -1) AS id
+ UNION ALL
+ SELECT rc.id
+ FROM hr_salary_rule_category AS rc
+ JOIN category_ids AS rcs ON rcs.id = rc.parent_id
+ )
+
+ SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
+ FROM hr_payslip as hp, hr_payslip_line as pl
+ WHERE hp.employee_id = %s AND hp.state = 'done'
+ AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id
+ AND pl.category_id in (SELECT id from category_ids)""".format(sum_field=sum_field)
+
+ def sum(self, code, from_date, to_date=None):
+ if to_date is None:
+ to_date = fields.Date.today()
+ self.env.cr.execute(self.__browsable_query_rule, (self.employee_id, from_date, to_date, code))
+ res = self.env.cr.fetchone()
+ return res and res[0] or 0.0
+
+ def rule_parameter(self, code):
+ return self.env['hr.rule.parameter']._get_parameter_from_code(code, self.dict.date_to)
+
+ def sum_category(self, code, from_date, to_date=None):
+ if to_date is None:
+ to_date = fields.Date.today()
+
+ self.env['hr.payslip'].flush(['credit_note', 'employee_id', 'state', 'date_from', 'date_to'])
+ self.env['hr.payslip.line'].flush(['total', 'slip_id', 'category_id'])
+ self.env['hr.salary.rule.category'].flush(['code'])
+
+ # standard version
+ # self.env.cr.execute(self.__browsable_query_category, (self.employee_id, from_date, to_date, code))
+ # recursive category version
+ self.env.cr.execute(self.__browsable_query_category, (code, self.employee_id, from_date, to_date))
+ res = self.env.cr.fetchone()
+ return res and res[0] or 0.0
+
+ @property
+ def paid_amount(self):
+ return self.dict._get_paid_amount()
+
+
+# Patch over Core
+browsable_object.BrowsableObject.__init__ = BrowsableObject.__init__
+browsable_object.BrowsableObject._compile_browsable_query = BrowsableObject._compile_browsable_query
+browsable_object.InputLine._compile_browsable_query = InputLine._compile_browsable_query
+browsable_object.InputLine.sum = InputLine.sum
+browsable_object.WorkedDays._compile_browsable_query = WorkedDays._compile_browsable_query
+browsable_object.WorkedDays.sum = WorkedDays.sum
+browsable_object.Payslips._compile_browsable_query = Payslips._compile_browsable_query
+browsable_object.Payslips.sum = Payslips.sum
+browsable_object.Payslips.sum_category = Payslips.sum_category
diff --git a/l10n_us_hr_payroll/models/federal/fed_940.py b/l10n_us_hr_payroll/models/federal/fed_940.py
index 1cf042c7..bbd4be17 100644
--- a/l10n_us_hr_payroll/models/federal/fed_940.py
+++ b/l10n_us_hr_payroll/models/federal/fed_940.py
@@ -1,9 +1,53 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+def futa_wage(payslip, categories):
+ """
+ Returns FUTA eligible wage for current Payslip (no wage_base, just by categories)
+ WAGE = GROSS - ALW_FUTA_EXEMPT + DED_FUTA_EXEMPT
+ :return: wage
+ """
+ wage = categories.GROSS
+
+ wage -= categories.ALW_FUTA_EXEMPT + \
+ categories.ALW_FIT_FUTA_EXEMPT + \
+ categories.ALW_FIT_FICA_FUTA_EXEMPT + \
+ categories.ALW_FICA_FUTA_EXEMPT
+
+ wage += categories.DED_FUTA_EXEMPT + \
+ categories.DED_FIT_FUTA_EXEMPT + \
+ categories.DED_FIT_FICA_FUTA_EXEMPT + \
+ categories.DED_FICA_FUTA_EXEMPT
+
+ return wage
+
+
+def futa_wage_ytd(payslip, categories):
+ """
+ Returns Year to Date FUTA eligible wages
+ WAGE = GROSS - ALW_FUTA_EXEMPT + DED_FUTA_EXEMPT
+ :return: wage
+ """
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ ytd_wage -= payslip.sum_category('ALW_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FIT_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ ytd_wage += payslip.sum_category('DED_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FIT_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ ytd_wage += payslip.contract_id.external_wages
+ return ytd_wage
+
+
def er_us_940_futa(payslip, categories, worked_days, inputs):
"""
Returns FUTA eligible wage and rate.
- WAGE = GROSS - WAGE_US_940_FUTA_EXEMPT
:return: result, result_rate (wage, percent)
"""
@@ -17,16 +61,14 @@ def er_us_940_futa(payslip, categories, worked_days, inputs):
result_rate = -payslip.rule_parameter('fed_940_futa_rate_normal')
# Determine Wage
- year = payslip.dict.get_year()
- ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage -= payslip.sum_category('WAGE_US_940_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage += payslip.contract_id.external_wages
+ wage = futa_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+ ytd_wage = futa_wage_ytd(payslip, categories)
wage_base = payslip.rule_parameter('fed_940_futa_wage_base')
remaining = wage_base - ytd_wage
- wage = categories.GROSS - categories.WAGE_US_940_FUTA_EXEMPT
-
if remaining < 0.0:
result = 0.0
elif remaining < wage:
diff --git a/l10n_us_hr_payroll/models/federal/fed_941.py b/l10n_us_hr_payroll/models/federal/fed_941.py
index e6288f88..009a93c6 100644
--- a/l10n_us_hr_payroll/models/federal/fed_941.py
+++ b/l10n_us_hr_payroll/models/federal/fed_941.py
@@ -4,10 +4,54 @@
# _logger = logging.getLogger(__name__)
+def fica_wage(payslip, categories):
+ """
+ Returns FICA eligible wage for current Payslip (no wage_base, just by categories)
+ WAGE = GROSS - ALW_FICA_EXEMPT + DED_FICA_EXEMPT
+ :return: wage
+ """
+ wage = categories.GROSS
+
+ less_exempt = categories.ALW_FICA_EXEMPT + \
+ categories.ALW_FIT_FICA_EXEMPT + \
+ categories.ALW_FIT_FICA_FUTA_EXEMPT + \
+ categories.ALW_FICA_FUTA_EXEMPT
+
+ plus_exempt = categories.DED_FICA_EXEMPT + \
+ categories.DED_FIT_FICA_EXEMPT + \
+ categories.DED_FIT_FICA_FUTA_EXEMPT + \
+ categories.DED_FICA_FUTA_EXEMPT
+ # _logger.info('fica wage GROSS: %0.2f less exempt ALW: %0.2f plus exempt DED: %0.2f' % (wage, less_exempt, plus_exempt))
+ return wage - less_exempt + plus_exempt
+
+
+def fica_wage_ytd(payslip, categories):
+ """
+ Returns Year to Date FICA eligible wages
+ WAGE = GROSS - ALW_FICA_EXEMPT + DED_FICA_EXEMPT
+ :return: wage
+ """
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ less_exempt = payslip.sum_category('ALW_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ plus_exempt = payslip.sum_category('DED_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ external_wages = payslip.dict.contract_id.external_wages
+ # _logger.info('fica ytd wage GROSS: %0.2f less exempt ALW: %0.2f plus exempt DED: %0.2f plus external: %0.2f' % (ytd_wage, less_exempt, plus_exempt, external_wages))
+ return ytd_wage - less_exempt + plus_exempt + external_wages
+
+
def ee_us_941_fica_ss(payslip, categories, worked_days, inputs):
"""
Returns FICA Social Security eligible wage and rate.
- WAGE = GROSS - WAGE_US_941_FICA_EXEMPT
:return: result, result_rate (wage, percent)
"""
exempt = payslip.contract_id.us_payroll_config_value('fed_941_fica_exempt')
@@ -18,16 +62,14 @@ def ee_us_941_fica_ss(payslip, categories, worked_days, inputs):
result_rate = -payslip.rule_parameter('fed_941_fica_ss_rate')
# Determine Wage
- year = payslip.dict.get_year()
- ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage -= payslip.sum_category('WAGE_US_941_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage += payslip.contract_id.external_wages
+ wage = fica_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+ ytd_wage = fica_wage_ytd(payslip, categories)
wage_base = payslip.rule_parameter('fed_941_fica_ss_wage_base')
remaining = wage_base - ytd_wage
- wage = categories.GROSS - categories.WAGE_US_941_FICA_EXEMPT
-
if remaining < 0.0:
result = 0.0
elif remaining < wage:
@@ -44,7 +86,6 @@ er_us_941_fica_ss = ee_us_941_fica_ss
def ee_us_941_fica_m(payslip, categories, worked_days, inputs):
"""
Returns FICA Medicare eligible wage and rate.
- WAGE = GROSS - WAGE_US_941_FICA_EXEMPT
:return: result, result_rate (wage, percent)
"""
exempt = payslip.contract_id.us_payroll_config_value('fed_941_fica_exempt')
@@ -55,16 +96,14 @@ def ee_us_941_fica_m(payslip, categories, worked_days, inputs):
result_rate = -payslip.rule_parameter('fed_941_fica_m_rate')
# Determine Wage
- year = payslip.dict.get_year()
- ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage -= payslip.sum_category('WAGE_US_941_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage += payslip.contract_id.external_wages
+ wage = fica_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+ ytd_wage = fica_wage_ytd(payslip, categories)
wage_base = float(payslip.rule_parameter('fed_941_fica_m_wage_base')) # inf
remaining = wage_base - ytd_wage
- wage = categories.GROSS - categories.WAGE_US_941_FICA_EXEMPT
-
if remaining < 0.0:
result = 0.0
elif remaining < wage:
@@ -81,8 +120,6 @@ er_us_941_fica_m = ee_us_941_fica_m
def ee_us_941_fica_m_add(payslip, categories, worked_days, inputs):
"""
Returns FICA Medicare Additional eligible wage and rate.
- Note that this wage is not capped like the above rules.
- WAGE = GROSS - WAGE_FICA_EXEMPT
:return: result, result_rate (wage, percent)
"""
exempt = payslip.contract_id.us_payroll_config_value('fed_941_fica_exempt')
@@ -93,16 +130,14 @@ def ee_us_941_fica_m_add(payslip, categories, worked_days, inputs):
result_rate = -payslip.rule_parameter('fed_941_fica_m_add_rate')
# Determine Wage
- year = payslip.dict.get_year()
- ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage -= payslip.sum_category('WAGE_US_941_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
- ytd_wage += payslip.contract_id.external_wages
+ wage = fica_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+ ytd_wage = fica_wage_ytd(payslip, categories)
wage_start = payslip.rule_parameter('fed_941_fica_m_add_wage_start')
existing_wage = ytd_wage - wage_start
- wage = categories.GROSS - categories.WAGE_US_941_FICA_EXEMPT
-
if existing_wage >= 0.0:
result = wage
elif wage + existing_wage > 0.0:
@@ -113,11 +148,54 @@ def ee_us_941_fica_m_add(payslip, categories, worked_days, inputs):
return result, result_rate
+def fit_wage(payslip, categories):
+ """
+ Returns FIT eligible wage for current Payslip (no wage_base, just by categories)
+ WAGE = GROSS - ALW_FIT_EXEMPT + DED_FIT_EXEMPT
+ :return: wage
+ """
+ wage = categories.GROSS
+
+ wage -= categories.ALW_FIT_EXEMPT + \
+ categories.ALW_FIT_FICA_EXEMPT + \
+ categories.ALW_FIT_FICA_FUTA_EXEMPT + \
+ categories.ALW_FIT_FUTA_EXEMPT
+
+ wage += categories.DED_FIT_EXEMPT + \
+ categories.DED_FIT_FICA_EXEMPT + \
+ categories.DED_FIT_FICA_FUTA_EXEMPT + \
+ categories.DED_FIT_FUTA_EXEMPT
+
+ return wage
+
+
+def fit_wage_ytd(payslip, categories):
+ """
+ Returns Year to Date FIT eligible wages
+ WAGE = GROSS - ALW_FIT_EXEMPT + DED_FIT_EXEMPT
+ :return: wage
+ """
+ year = payslip.dict.get_year()
+ ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ ytd_wage -= payslip.sum_category('ALW_FIT_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('ALW_FIT_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ ytd_wage += payslip.sum_category('DED_FIT_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \
+ payslip.sum_category('DED_FIT_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
+
+ ytd_wage += payslip.contract_id.external_wages
+ return ytd_wage
+
+
# Federal Income Tax
def ee_us_941_fit(payslip, categories, worked_days, inputs):
"""
Returns Wage and rate that is computed given the amount to withhold.
- WAGE = GROSS - WAGE_US_941_FIT_EXEMPT
:return: result, result_rate (wage, percent)
"""
filing_status = payslip.contract_id.us_payroll_config_value('fed_941_fit_w4_filing_status')
@@ -125,7 +203,10 @@ def ee_us_941_fit(payslip, categories, worked_days, inputs):
return 0.0, 0.0
schedule_pay = payslip.contract_id.schedule_pay
- wage = categories.GROSS - categories.WAGE_US_941_FIT_EXEMPT
+ wage = fit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
#_logger.warn('initial gross wage: ' + str(wage))
year = payslip.dict.get_year()
if year >= 2020:
diff --git a/l10n_us_hr_payroll/models/hr_contract.py b/l10n_us_hr_payroll/models/hr_contract.py
index 46bc42b7..ab5772c8 100644
--- a/l10n_us_hr_payroll/models/hr_contract.py
+++ b/l10n_us_hr_payroll/models/hr_contract.py
@@ -6,6 +6,11 @@ from .us_payroll_config import FUTA_TYPE_NORMAL, \
FUTA_TYPE_EXEMPT
+class HrPayrollStructureType(models.Model):
+ _inherit = 'hr.payroll.structure.type'
+ default_schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
+
+
class HrPayrollStructure(models.Model):
_inherit = 'hr.payroll.structure'
schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
diff --git a/l10n_us_hr_payroll/models/hr_payslip.py b/l10n_us_hr_payroll/models/hr_payslip.py
index 92a1e0fd..e5a728f2 100644
--- a/l10n_us_hr_payroll/models/hr_payslip.py
+++ b/l10n_us_hr_payroll/models/hr_payslip.py
@@ -9,6 +9,48 @@ from .federal.fed_941 import ee_us_941_fica_ss, \
er_us_941_fica_ss, \
er_us_941_fica_m, \
ee_us_941_fit
+from .state.general import general_state_unemployment, \
+ general_state_income_withholding, \
+ is_us_state
+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.ca_california import ca_california_state_income_withholding
+from .state.co_colorado import co_colorado_state_income_withholding
+from .state.ct_connecticut import ct_connecticut_state_income_withholding
+from .state.de_delaware import de_delaware_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.ia_iowa import ia_iowa_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.in_indiana import in_indiana_state_income_withholding
+from .state.ks_kansas import ks_kansas_state_income_withholding
+from .state.ky_kentucky import ky_kentucky_state_income_withholding
+from .state.la_louisiana import la_louisiana_state_income_withholding
+from .state.me_maine import me_maine_state_income_withholding
+from .state.mi_michigan import mi_michigan_state_income_withholding
+from .state.mn_minnesota import mn_minnesota_state_income_withholding
+from .state.mo_missouri import mo_missouri_state_income_withholding
+from .state.ms_mississippi import ms_mississippi_state_income_withholding
+from .state.mt_montana import mt_montana_state_income_withholding
+from .state.nc_northcarolina import nc_northcarolina_state_income_withholding
+from .state.nd_north_dakota import nd_north_dakota_state_income_withholding
+from .state.ne_nebraska import ne_nebraska_state_income_withholding
+from .state.nj_newjersey import nj_newjersey_state_income_withholding
+from .state.nm_new_mexico import nm_new_mexico_state_income_withholding
+from .state.ny_new_york import ny_new_york_state_income_withholding
+from .state.oh_ohio import oh_ohio_state_income_withholding
+from .state.ok_oklahoma import ok_oklahoma_state_income_withholding
+from .state.ri_rhode_island import ri_rhode_island_state_income_withholding
+from .state.sc_south_carolina import sc_south_carolina_state_income_withholding
+from .state.ut_utah import ut_utah_state_income_withholding
+from .state.vt_vermont import vt_vermont_state_income_withholding
+from .state.va_virginia import va_virginia_state_income_withholding
+from .state.wa_washington import wa_washington_fml_er, \
+ wa_washington_fml_ee
+from .state.wi_wisconsin import wi_wisconsin_state_income_withholding
+from .state.wv_west_virginia import wv_west_virginia_state_income_withholding
class HRPayslip(models.Model):
@@ -37,6 +79,48 @@ class HRPayslip(models.Model):
'er_us_941_fica_ss': er_us_941_fica_ss,
'er_us_941_fica_m': er_us_941_fica_m,
'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,
+ '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,
+ 'ca_california_state_income_withholding': ca_california_state_income_withholding,
+ 'co_colorado_state_income_withholding': co_colorado_state_income_withholding,
+ 'ct_connecticut_state_income_withholding': ct_connecticut_state_income_withholding,
+ 'de_delaware_state_income_withholding': de_delaware_state_income_withholding,
+ 'ga_georgia_state_income_withholding': ga_georgia_state_income_withholding,
+ 'hi_hawaii_state_income_withholding': hi_hawaii_state_income_withholding,
+ 'ia_iowa_state_income_withholding': ia_iowa_state_income_withholding,
+ 'id_idaho_state_income_withholding': id_idaho_state_income_withholding,
+ 'il_illinois_state_income_withholding': il_illinois_state_income_withholding,
+ 'in_indiana_state_income_withholding': in_indiana_state_income_withholding,
+ 'ks_kansas_state_income_withholding': ks_kansas_state_income_withholding,
+ 'ky_kentucky_state_income_withholding':ky_kentucky_state_income_withholding,
+ 'la_louisiana_state_income_withholding': la_louisiana_state_income_withholding,
+ 'me_maine_state_income_withholding': me_maine_state_income_withholding,
+ 'mi_michigan_state_income_withholding': mi_michigan_state_income_withholding,
+ 'mn_minnesota_state_income_withholding': mn_minnesota_state_income_withholding,
+ 'mo_missouri_state_income_withholding': mo_missouri_state_income_withholding,
+ 'ms_mississippi_state_income_withholding': ms_mississippi_state_income_withholding,
+ 'mt_montana_state_income_withholding': mt_montana_state_income_withholding,
+ 'nc_northcarolina_state_income_withholding': nc_northcarolina_state_income_withholding,
+ 'nd_north_dakota_state_income_withholding': nd_north_dakota_state_income_withholding,
+ 'ne_nebraska_state_income_withholding': ne_nebraska_state_income_withholding,
+ 'nj_newjersey_state_income_withholding': nj_newjersey_state_income_withholding,
+ 'nm_new_mexico_state_income_withholding': nm_new_mexico_state_income_withholding,
+ 'ny_new_york_state_income_withholding': ny_new_york_state_income_withholding,
+ 'oh_ohio_state_income_withholding': oh_ohio_state_income_withholding,
+ 'ok_oklahoma_state_income_withholding': ok_oklahoma_state_income_withholding,
+ 'ri_rhode_island_state_income_withholding': ri_rhode_island_state_income_withholding,
+ 'sc_south_carolina_state_income_withholding': sc_south_carolina_state_income_withholding,
+ 'ut_utah_state_income_withholding': ut_utah_state_income_withholding,
+ 'vt_vermont_state_income_withholding': vt_vermont_state_income_withholding,
+ 'va_virginia_state_income_withholding': va_virginia_state_income_withholding,
+ 'wa_washington_fml_er': wa_washington_fml_er,
+ 'wa_washington_fml_ee': wa_washington_fml_ee,
+ 'wi_wisconsin_state_income_withholding': wi_wisconsin_state_income_withholding,
+ 'wv_west_virginia_state_income_withholding': wv_west_virginia_state_income_withholding,
})
return res
diff --git a/l10n_us_hr_payroll/models/res_config_settings.py b/l10n_us_hr_payroll/models/res_config_settings.py
new file mode 100644
index 00000000..05af9430
--- /dev/null
+++ b/l10n_us_hr_payroll/models/res_config_settings.py
@@ -0,0 +1,24 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+ payslip_sum_type = fields.Selection([
+ ('date_from', 'Date From'),
+ ('date_to', 'Date To'),
+ ('date', 'Accounting Date'),
+ ], 'Payslip Sum Behavior', help="Behavior for what payslips are considered "
+ "during rule execution. Stock Odoo behavior "
+ "would not consider a payslip starting on 2019-12-30 "
+ "ending on 2020-01-07 when summing a 2020 payslip category.\n\n"
+ "Accounting Date requires Payroll Accounting and will "
+ "fall back to date_to as the 'closest behavior'.",
+ config_parameter='hr_payroll.payslip.sum_behavior')
+
+ def set_values(self):
+ super(ResConfigSettings, self).set_values()
+ self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior',
+ self.payslip_sum_type or 'date_from')
diff --git a/l10n_us_hr_payroll/models/state/__init__.py b/l10n_us_hr_payroll/models/state/__init__.py
new file mode 100644
index 00000000..0358305d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/__init__.py
@@ -0,0 +1 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
diff --git a/l10n_us_hr_payroll/models/state/al_alabama.py b/l10n_us_hr_payroll/models/state/al_alabama.py
new file mode 100644
index 00000000..11638b7f
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/al_alabama.py
@@ -0,0 +1,80 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def al_alabama_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'AL'
+ 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
+
+ exemptions = payslip.contract_id.us_payroll_config_value('al_a4_sit_exemptions')
+ if not exemptions:
+ return 0.0, 0.0
+
+ personal_exempt = payslip.contract_id.us_payroll_config_value('state_income_tax_exempt')
+ if personal_exempt:
+ 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')
+ tax_table = payslip.rule_parameter('us_al_sit_tax_rate')
+ dependent_rate = payslip.rule_parameter('us_al_sit_dependent_rate')
+ standard_deduction = payslip.rule_parameter('us_al_sit_standard_deduction_rate').get(exemptions, 0.0)
+ personal_exemption = payslip.rule_parameter('us_al_sit_personal_exemption_rate').get(exemptions, 0.0)
+ dependent = payslip.contract_id.us_payroll_config_value('al_a4_sit_dependents')
+ fed_withholding = categories.EE_US_941_FIT
+
+ annual_wage = wage * pay_periods
+ standard_deduction_amt = 0.0
+ personal_exemption_amt = 0.0
+ dependent_amt = 0.0
+ withholding = 0.0
+
+ if standard_deduction:
+ row = standard_deduction
+ last_amt = 0.0
+ for data in row:
+ if annual_wage < float(data[0]):
+ if len(data) > 3:
+ increment_count = (- (wage - last_amt) // data[3])
+ standard_deduction_amt = data[1] - (increment_count * data[2])
+ else:
+ standard_deduction_amt = data[1]
+ else:
+ last_amt = data[0]
+ after_deduction = annual_wage - standard_deduction_amt
+ after_fed_withholding = (fed_withholding * pay_periods) + after_deduction
+ if not personal_exempt:
+ personal_exemption_amt = personal_exemption
+ after_personal_exemption = after_fed_withholding - personal_exemption_amt
+ for row in dependent_rate:
+ if annual_wage < float(row[1]):
+ dependent_amt = row[0] * dependent
+ break
+
+ taxable_amount = after_personal_exemption - dependent_amt
+ last = 0.0
+ tax_table = tax_table['M'] if exemptions == 'M' else tax_table['0']
+ for row in tax_table:
+ if taxable_amount < float(row[0]):
+ withholding = withholding + ((taxable_amount - last) * (row[1] / 100))
+ break
+ withholding = withholding + ((row[0] - last) * (row[1] / 100))
+ last = row[0]
+
+ if withholding < 0.0:
+ withholding = 0.0
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ar_arkansas.py b/l10n_us_hr_payroll/models/state/ar_arkansas.py
new file mode 100644
index 00000000..c1306c94
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ar_arkansas.py
@@ -0,0 +1,47 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ar_arkansas_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'AR'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ sit_tax_rate = payslip.rule_parameter('us_ar_sit_tax_rate')
+ standard_deduction = payslip.rule_parameter('us_ar_sit_standard_deduction_rate')
+ allowances = payslip.contract_id.us_payroll_config_value('ar_ar4ec_sit_allowances')
+
+ allowances_amt = allowances * 26.0
+ taxable_income = (wage * pay_periods) - standard_deduction
+ if taxable_income < 87001.0:
+ taxable_income = (taxable_income // 50) * 50.0 + 50.0
+
+ withholding = 0.0
+ for row in sit_tax_rate:
+ cap, rate, adjust_amount = row
+ cap = float(cap)
+ if cap > taxable_income:
+ withholding = (((rate / 100.0) * taxable_income) - adjust_amount) - allowances_amt
+ break
+
+ # In case withholding or taxable_income is negative
+ withholding = max(withholding, 0.0)
+ withholding = round(withholding / pay_periods)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/az_arizona.py b/l10n_us_hr_payroll/models/state/az_arizona.py
new file mode 100644
index 00000000..90c44898
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/az_arizona.py
@@ -0,0 +1,35 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def az_arizona_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'AZ'
+ 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
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ withholding_percent = payslip.contract_id.us_payroll_config_value('az_a4_sit_withholding_percentage')
+
+ if withholding_percent <= 0.0:
+ return 0.0, 0.0
+
+ wh_percentage = withholding_percent / 100.0
+ withholding = wage * wh_percentage
+
+ if withholding < 0.0:
+ withholding = 0.0
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ca_california.py b/l10n_us_hr_payroll/models/state/ca_california.py
new file mode 100644
index 00000000..62382402
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ca_california.py
@@ -0,0 +1,98 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+MAX_ALLOWANCES = 10
+
+
+def ca_california_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+
+ state_code = 'CA'
+ 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('ca_de4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ sit_allowances = payslip.contract_id.us_payroll_config_value('ca_de4_sit_allowances')
+ additional_allowances = payslip.contract_id.us_payroll_config_value('ca_de4_sit_additional_allowances')
+ low_income_exemption = payslip.rule_parameter('us_ca_sit_income_exemption_rate')[schedule_pay]
+ estimated_deduction = payslip.rule_parameter('us_ca_sit_estimated_deduction_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_ca_sit_tax_rate')[filing_status].get(schedule_pay)
+ standard_deduction = payslip.rule_parameter('us_ca_sit_standard_deduction_rate')[schedule_pay]
+ exemption_allowances = payslip.rule_parameter('us_ca_sit_exemption_allowance_rate')[schedule_pay]
+
+ low_income = False
+ if filing_status == 'head_household':
+ _, _, _, income = low_income_exemption
+ if wage <= income:
+ low_income = True
+ elif filing_status == 'married':
+ if sit_allowances >= 2:
+ _, _, income, _ = low_income_exemption
+ if wage <= income:
+ low_income = True
+ else:
+ _, income, _, _ = low_income_exemption
+ if wage <= income:
+ low_income = True
+ else:
+ income, _, _, _ = low_income_exemption
+ if wage <= income:
+ low_income = True
+
+ withholding = 0.0
+ taxable_wage = wage
+ if not low_income:
+ allowance_index = max(additional_allowances - 1, 0)
+ if additional_allowances > MAX_ALLOWANCES:
+ deduction = (estimated_deduction[0] * additional_allowances)
+ taxable_wage -= deduction
+ elif additional_allowances > 0:
+ deduction = estimated_deduction[allowance_index]
+ taxable_wage -= deduction
+
+ if filing_status == 'head_household':
+ _, _, _, deduction = standard_deduction
+ taxable_wage -= deduction
+ elif filing_status == 'married':
+ if sit_allowances >= 2:
+ _, _, deduction, _ = standard_deduction
+ taxable_wage -= deduction
+ else:
+ _, deduction, _, _ = standard_deduction
+ taxable_wage -= deduction
+ else:
+ deduction, _, _, _ = standard_deduction
+ taxable_wage -= deduction
+
+ over = 0.0
+ for row in tax_table:
+ if taxable_wage <= row[0]:
+ withholding = ((taxable_wage - over) * row[1]) + row[2]
+ break
+ over = row[0]
+
+ allowance_index = sit_allowances - 1
+ if sit_allowances > MAX_ALLOWANCES:
+ deduction = exemption_allowances[0] * sit_allowances
+ withholding -= deduction
+ elif sit_allowances > 0:
+ deduction = exemption_allowances[allowance_index]
+ withholding -= deduction
+
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/co_colorado.py b/l10n_us_hr_payroll/models/state/co_colorado.py
new file mode 100644
index 00000000..f0c7b436
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/co_colorado.py
@@ -0,0 +1,45 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def co_colorado_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'CO'
+ 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('fed_941_fit_w4_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ state_exempt = payslip.contract_id.us_payroll_config_value('state_income_tax_exempt')
+ if state_exempt:
+ 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')
+ exemption_rate = payslip.rule_parameter('us_co_sit_exemption_rate')
+ tax_rate = payslip.rule_parameter('us_co_sit_tax_rate')
+
+ taxable_income = wage * pay_periods
+ if filing_status == 'married':
+ taxable_income -= exemption_rate * 2
+ else:
+ taxable_income -= exemption_rate
+
+ withholding = taxable_income * (tax_rate / 100)
+
+ 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/state/ct_connecticut.py b/l10n_us_hr_payroll/models/state/ct_connecticut.py
new file mode 100644
index 00000000..344dc9c8
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ct_connecticut.py
@@ -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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ withholding_code = payslip.contract_id.us_payroll_config_value('ct_w4na_sit_code')
+ exemption_table = payslip.rule_parameter('us_ct_sit_personal_exemption_rate').get(withholding_code, [('inf', 0.0)])
+ initial_tax_tbl = payslip.rule_parameter('us_ct_sit_initial_tax_rate').get(withholding_code, [('inf', 0.0, 0.0)])
+ tax_table = payslip.rule_parameter('us_ct_sit_tax_rate').get(withholding_code, [('inf', 0.0)])
+ recapture_table = payslip.rule_parameter('us_ct_sit_recapture_rate').get(withholding_code, [('inf', 0.0)])
+ decimal_table = payslip.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)
diff --git a/l10n_us_hr_payroll/models/state/de_delaware.py b/l10n_us_hr_payroll/models/state/de_delaware.py
new file mode 100644
index 00000000..b2588e5d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/de_delaware.py
@@ -0,0 +1,49 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def de_delaware_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'DE'
+ 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('de_w4_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')
+ tax_table = payslip.rule_parameter('us_de_sit_tax_rate')
+ personal_exemption = payslip.rule_parameter('us_de_sit_personal_exemption_rate')
+ allowances = payslip.contract_id.us_payroll_config_value('de_w4_sit_dependent')
+ standard_deduction = payslip.rule_parameter('us_de_sit_standard_deduction_rate')
+
+ taxable_income = wage * pay_periods
+ if filing_status == 'single':
+ taxable_income -= standard_deduction
+ else:
+ taxable_income -= standard_deduction * 2
+
+ 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)) - (allowances * personal_exemption))
+ 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/state/ga_georgia.py b/l10n_us_hr_payroll/models/state/ga_georgia.py
new file mode 100644
index 00000000..77fb0044
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ga_georgia.py
@@ -0,0 +1,51 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ga_georgia_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'GA'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+ ga_filing_status = payslip.contract_id.us_payroll_config_value('ga_g4_sit_filing_status')
+ if not ga_filing_status:
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ dependent_allowances = payslip.contract_id.us_payroll_config_value('ga_g4_sit_dependent_allowances')
+ additional_allowances = payslip.contract_id.us_payroll_config_value('ga_g4_sit_additional_allowances')
+ dependent_allowance_rate = payslip.rule_parameter('us_ga_sit_dependent_allowance_rate').get(schedule_pay)
+ personal_allowance = payslip.rule_parameter('us_ga_sit_personal_allowance').get(ga_filing_status, {}).get(schedule_pay)
+ deduction = payslip.rule_parameter('us_ga_sit_deduction').get(ga_filing_status, {}).get(schedule_pay)
+ withholding_rate = payslip.rule_parameter('us_ga_sit_rate').get(ga_filing_status, {}).get(schedule_pay)
+ if not all((dependent_allowance_rate, personal_allowance, deduction, withholding_rate)):
+ return 0.0, 0.0
+
+ after_standard_deduction = wage - deduction
+ allowances = dependent_allowances + additional_allowances
+ working_wages = after_standard_deduction - (personal_allowance + (allowances * dependent_allowance_rate))
+
+ withholding = 0.0
+ if working_wages > 0.0:
+ prior_row_base = 0.0
+ for row in withholding_rate:
+ wage_base, base, rate = row
+ wage_base = float(wage_base)
+ if working_wages < wage_base:
+ withholding = base + ((working_wages - prior_row_base) * rate / 100.0)
+ break
+ prior_row_base = wage_base
+
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/general.py b/l10n_us_hr_payroll/models/state/general.py
new file mode 100644
index 00000000..8979782f
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/general.py
@@ -0,0 +1,131 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+from odoo.exceptions import UserError
+from ..federal.fed_940 import futa_wage, futa_wage_ytd
+from ..federal.fed_941 import fit_wage, fit_wage_ytd
+
+# import logging
+# _logger = logging.getLogger(__name__)
+
+suta_wage = futa_wage
+suta_wage_ytd = futa_wage_ytd
+sit_wage = fit_wage
+sit_wage_ytd = fit_wage_ytd
+
+
+def _state_applies(payslip, state_code):
+ return state_code == payslip.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:
+ wage_base, wage_start, rate can either be strings (rule_parameters) or floats
+ :return: result, result_rate(wage, percent)
+ """
+
+ # Resolve parameters. On exception, return (probably missing a year, would rather not have exception)
+ if wage_base and isinstance(wage_base, str):
+ try:
+ wage_base = payslip.rule_parameter(wage_base)
+ except (KeyError, UserError):
+ return 0.0, 0.0
+
+ if wage_start and isinstance(wage_start, str):
+ try:
+ wage_start = payslip.rule_parameter(wage_start)
+ except (KeyError, UserError):
+ return 0.0, 0.0
+
+ if rate and isinstance(rate, str):
+ try:
+ rate = payslip.rule_parameter(rate)
+ except (KeyError, UserError):
+ return 0.0, 0.0
+
+ if not rate:
+ return 0.0, 0.0
+ else:
+ # Rate assumed positive percentage!
+ rate = -rate
+
+ if wage_base:
+ remaining = wage_base - ytd_wage
+ if remaining < 0.0:
+ result = 0.0
+ elif remaining < wage:
+ result = remaining
+ else:
+ result = wage
+
+ # _logger.warn(' wage_base method result: ' + str(result) + ' rate: ' + str(rate))
+ return result, rate
+ if wage_start:
+ if ytd_wage >= wage_start:
+ # _logger.warn(' wage_start 1 method result: ' + str(wage) + ' rate: ' + str(rate))
+ return wage, rate
+ if ytd_wage + wage <= wage_start:
+ # _logger.warn(' wage_start 2 method result: ' + str(0.0) + ' rate: ' + str(0.0))
+ return 0.0, 0.0
+ # _logger.warn(' wage_start 3 method result: ' + str((wage - (wage_start - ytd_wage))) + ' rate: ' + str(rate))
+ return (wage - (wage_start - ytd_wage)), rate
+
+ # If the wage doesn't have a start or a base
+ # _logger.warn(' basic result: ' + str(wage) + ' rate: ' + str(rate))
+ return wage, rate
+
+
+def general_state_unemployment(payslip, categories, worked_days, inputs, wage_base=None, wage_start=None, rate=None, state_code=None):
+ """
+ Returns SUTA eligible wage and rate.
+ WAGE = GROSS + DED_FUTA_EXEMPT
+
+ The contract's `futa_type` determines if SUTA should be collected.
+
+ :return: result, result_rate(wage, percent)
+ """
+
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ # Determine Eligible.
+ if payslip.contract_id.futa_type in (payslip.contract_id.FUTA_TYPE_EXEMPT, payslip.contract_id.FUTA_TYPE_BASIC):
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = suta_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ ytd_wage = suta_wage_ytd(payslip, categories)
+
+ return _general_rate(payslip, wage, ytd_wage, wage_base=wage_base, wage_start=wage_start, rate=rate)
+
+
+def general_state_income_withholding(payslip, categories, worked_days, inputs, wage_base=None, wage_start=None, rate=None, state_code=None):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ # Determine Wage
+ ytd_wage = sit_wage_ytd(payslip, categories)
+
+ wage = sit_wage(payslip, categories)
+ result, result_rate = _general_rate(payslip, wage, ytd_wage, wage_base=wage_base, wage_start=wage_start, rate=rate)
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ if additional:
+ tax = result * (result_rate / 100.0)
+ tax -= additional # assumed result_rate is negative and that the 'additional' should increase it.
+ return result, ((tax / result) * 100.0)
+ return result, result_rate
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/state/ia_iowa.py b/l10n_us_hr_payroll/models/state/ia_iowa.py
new file mode 100644
index 00000000..9bb9ac9d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ia_iowa.py
@@ -0,0 +1,48 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ia_iowa_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'IA'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ fed_withholding = categories.EE_US_941_FIT
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('ia_w4_sit_allowances')
+ standard_deduction = payslip.rule_parameter('us_ia_sit_standard_deduction_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_ia_sit_tax_rate')[schedule_pay]
+ deduction_per_allowance = payslip.rule_parameter('us_ia_sit_deduction_allowance_rate')[schedule_pay]
+
+ t1 = wage + fed_withholding
+ standard_deduction_amt = standard_deduction[0] if allowances < 2 else standard_deduction[1]
+ t2 = t1 - standard_deduction_amt
+ t3 = 0.0
+ last = 0.0
+ for row in tax_table:
+ cap, rate, flat_fee = row
+ if float(cap) > float(t2):
+ taxed_amount = t2 - last
+ t3 = flat_fee + (rate * taxed_amount)
+ break
+ last = cap
+ withholding = t3 - (deduction_per_allowance * allowances)
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/id_idaho.py b/l10n_us_hr_payroll/models/state/id_idaho.py
new file mode 100644
index 00000000..5bf503da
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/id_idaho.py
@@ -0,0 +1,41 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def id_idaho_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'ID'
+ 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('id_w4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ allowances = payslip.contract_id.us_payroll_config_value('id_w4_sit_allowances')
+ ictcat_table = payslip.rule_parameter('us_id_sit_ictcat_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_id_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (ictcat_table * 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 = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/il_illinois.py b/l10n_us_hr_payroll/models/state/il_illinois.py
new file mode 100644
index 00000000..6c8919c4
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/il_illinois.py
@@ -0,0 +1,35 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def il_illinois_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'IL'
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ basic_allowances_rate = payslip.rule_parameter('us_il_sit_basic_allowances_rate')
+ additional_allowances_rate = payslip.rule_parameter('us_il_sit_additional_allowances_rate')
+ basic_allowances = payslip.contract_id.us_payroll_config_value('il_w4_sit_basic_allowances')
+ additional_allowances = payslip.contract_id.us_payroll_config_value('il_w4_sit_additional_allowances')
+
+ rate = 4.95 / 100.0
+ withholding = rate * (wage - (((basic_allowances * basic_allowances_rate) + (additional_allowances *
+ additional_allowances_rate)) / pay_periods))
+ if withholding < 0.0:
+ withholding = 0.0
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/in_indiana.py b/l10n_us_hr_payroll/models/state/in_indiana.py
new file mode 100644
index 00000000..0b6bd03e
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/in_indiana.py
@@ -0,0 +1,34 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def in_indiana_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'IN'
+ 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
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ personal_exemption = payslip.contract_id.us_payroll_config_value('in_w4_sit_personal_exemption')
+ personal_exemption_rate = payslip.rule_parameter('us_in_sit_personal_exemption_rate')[schedule_pay][personal_exemption - 1]
+ dependent_exemption = payslip.contract_id.us_payroll_config_value('in_w4_sit_dependent_exemption')
+ dependent_exemption_rate = payslip.rule_parameter('us_in_sit_dependent_exemption_rate')[schedule_pay][dependent_exemption - 1]
+ income_tax_rate = payslip.rule_parameter('us_in_suta_income_rate')
+
+ taxable_income = wage - (personal_exemption_rate + dependent_exemption_rate)
+ withholding = taxable_income * (income_tax_rate / 100.0)
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ks_kansas.py b/l10n_us_hr_payroll/models/state/ks_kansas.py
new file mode 100644
index 00000000..1e7398d0
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ks_kansas.py
@@ -0,0 +1,44 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ks_kansas_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'KS'
+ 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('ks_k4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('ks_k4_sit_allowances')
+ allowances_amt = payslip.rule_parameter('us_ks_sit_allowances_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_ks_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (allowances * allowances_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, rate, flat_fee = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ky_kentucky.py b/l10n_us_hr_payroll/models/state/ky_kentucky.py
new file mode 100644
index 00000000..ab580880
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ky_kentucky.py
@@ -0,0 +1,32 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ky_kentucky_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'KY'
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ tax_rate = payslip.rule_parameter('us_ky_sit_tax_rate')
+ standard_deduction = payslip.rule_parameter('us_ky_sit_standard_deduction_rate')
+
+ taxable_income = (wage * pay_periods) - standard_deduction
+ withholding = taxable_income * (tax_rate / 100)
+
+ withholding = max(withholding, 0.0)
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/la_louisiana.py b/l10n_us_hr_payroll/models/state/la_louisiana.py
new file mode 100644
index 00000000..c580d5a2
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/la_louisiana.py
@@ -0,0 +1,62 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def la_louisiana_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'LA'
+ 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('la_l4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ pay_periods = payslip.dict.get_pay_periods_in_year()
+ personal_exemptions = payslip.contract_id.us_payroll_config_value('la_l4_sit_exemptions')
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ dependent_exemptions = payslip.contract_id.us_payroll_config_value('la_l4_sit_dependents')
+ tax_table = payslip.rule_parameter('us_la_sit_tax_rate')[filing_status]
+ exemption_rate = payslip.rule_parameter('us_la_sit_personal_exemption_rate')
+ dependent_rate = payslip.rule_parameter('us_la_sit_dependent_rate')
+
+ annual_wage = wage * pay_periods
+
+ effect_cap = 0.0
+ multiplier = 0.0
+ if filing_status == 'single':
+ effect_cap = 12500.00
+ multiplier = 1.60
+ elif filing_status == 'married':
+ effect_cap = 25000.00
+ multiplier = 1.65
+
+ after_credits_under = (2.100 / 100) * (((personal_exemptions * exemption_rate) +
+ (dependent_exemptions * dependent_rate)) / pay_periods)
+ after_credits_over = 0.00
+ if after_credits_under > effect_cap:
+ after_credits_under = effect_cap
+ after_credits_over_check = ((personal_exemptions * exemption_rate) + (dependent_exemptions * dependent_rate)) - effect_cap
+ after_credits_over = (multiplier / 100.00) * (after_credits_over_check / pay_periods) if after_credits_over_check > 0 else 0.00
+ withholding = 0.0
+ last = 0.0
+ for amt, rate in tax_table:
+ withholding = withholding + ((rate / 100.0) * (wage - (last / pay_periods)))
+ if annual_wage <= float(amt):
+ break
+ last = amt
+
+ withholding = withholding - (after_credits_under + after_credits_over)
+ withholding = round(withholding, 2)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/me_maine.py b/l10n_us_hr_payroll/models/state/me_maine.py
new file mode 100644
index 00000000..0accc6ff
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/me_maine.py
@@ -0,0 +1,62 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def me_maine_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'ME'
+ 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('me_w4me_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ exempt = payslip.contract_id.us_payroll_config_value('state_income_tax_exempt')
+ if exempt:
+ 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('me_w4me_sit_allowances')
+ tax_rate = payslip.rule_parameter('us_me_sit_tax_rate')[filing_status]
+ personal_exemption = payslip.rule_parameter('us_me_sit_personal_exemption_rate')
+ standard_deduction = payslip.rule_parameter('us_me_sit_standard_deduction_rate')[filing_status]
+
+ taxable_income = wage * pay_periods
+ exemption_amt = allowances * personal_exemption
+ last = 0.0
+ standard_deduction_amt = 0.0
+ for row in standard_deduction:
+ amt, flat_amt = row
+ if taxable_income < 82900:
+ standard_deduction_amt = flat_amt
+ break
+ elif taxable_income < amt:
+ standard_deduction_amt = last * (amt - taxable_income) / flat_amt
+ break
+ last = flat_amt
+ annual_income = taxable_income - (exemption_amt + standard_deduction_amt)
+ withholding = 0.0
+ for row in tax_rate:
+ amt, flat_fee, rate = row
+ if annual_income < float(amt):
+ withholding = ((annual_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding = round(withholding / pay_periods)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/mi_michigan.py b/l10n_us_hr_payroll/models/state/mi_michigan.py
new file mode 100644
index 00000000..f9656529
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/mi_michigan.py
@@ -0,0 +1,35 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def mi_michigan_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'MI'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ exemption_rate = payslip.rule_parameter('us_mi_sit_exemption_rate')
+ exemption = payslip.contract_id.us_payroll_config_value('mi_w4_sit_exemptions')
+
+ annual_exemption = (exemption * exemption_rate) / pay_periods
+ withholding = ((wage - annual_exemption) * 0.0425)
+ if withholding < 0.0:
+ withholding = 0.0
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/mn_minnesota.py b/l10n_us_hr_payroll/models/state/mn_minnesota.py
new file mode 100644
index 00000000..c626bc3b
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/mn_minnesota.py
@@ -0,0 +1,44 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def mn_minnesota_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'MN'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('mn_w4mn_sit_filing_status')
+ if not filing_status:
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ sit_tax_rate = payslip.rule_parameter('us_mn_sit_tax_rate')[filing_status]
+ allowances_rate = payslip.rule_parameter('us_mn_sit_allowances_rate')
+ allowances = payslip.contract_id.us_payroll_config_value('mn_w4mn_sit_allowances')
+
+ taxable_income = (wage * pay_periods) - (allowances * allowances_rate)
+ withholding = 0.0
+ for row in sit_tax_rate:
+ cap, subtract_amt, rate, flat_fee = row
+ cap = float(cap)
+ if cap > taxable_income:
+ withholding = ((rate / 100.00) * (taxable_income - subtract_amt)) + flat_fee
+ break
+ withholding = round(withholding / pay_periods)
+ if withholding < 0.0:
+ withholding = 0.0
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/mo_missouri.py b/l10n_us_hr_payroll/models/state/mo_missouri.py
new file mode 100644
index 00000000..47e56639
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/mo_missouri.py
@@ -0,0 +1,53 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def mo_missouri_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'MO'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('mo_mow4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ reduced_withholding = payslip.contract_id.us_payroll_config_value('mo_mow4_sit_withholding')
+ if reduced_withholding:
+ return wage, -((reduced_withholding / wage) * 100.0)
+
+ pay_periods = payslip.dict.get_pay_periods_in_year()
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ sit_table = payslip.rule_parameter('us_mo_sit_rate')
+ deduction = payslip.rule_parameter('us_mo_sit_deduction')[filing_status]
+
+ gross_taxable_income = wage * pay_periods
+ gross_taxable_income -= deduction
+
+ remaining_taxable_income = gross_taxable_income
+ withholding = 0.0
+ for amt, rate in sit_table:
+ amt = float(amt)
+ rate = rate / 100.0
+ if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
+ withholding += rate * amt
+ else:
+ withholding += rate * remaining_taxable_income
+ break
+ remaining_taxable_income = remaining_taxable_income - amt
+
+ withholding /= pay_periods
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ms_mississippi.py b/l10n_us_hr_payroll/models/state/ms_mississippi.py
new file mode 100644
index 00000000..10f30ee2
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ms_mississippi.py
@@ -0,0 +1,46 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ms_mississippi_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'MS'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('ms_89_350_sit_filing_status')
+ if not filing_status:
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ exemptions = payslip.contract_id.us_payroll_config_value('ms_89_350_sit_exemption_value')
+ standard_deduction = payslip.rule_parameter('us_ms_sit_deduction').get(filing_status)
+ withholding_rate = payslip.rule_parameter('us_ms_sit_rate')
+
+ wage_annual = wage * pay_periods
+ taxable_income = wage_annual - (exemptions + standard_deduction)
+ if taxable_income <= 0.01:
+ return wage, 0.0
+
+ withholding = 0.0
+ for row in withholding_rate:
+ wage_base, base, rate = row
+ if taxable_income >= wage_base:
+ withholding = base + ((taxable_income - wage_base) * rate)
+ break
+ withholding /= pay_periods
+ withholding = round(withholding)
+ withholding += round(additional)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/mt_montana.py b/l10n_us_hr_payroll/models/state/mt_montana.py
new file mode 100644
index 00000000..6e33261a
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/mt_montana.py
@@ -0,0 +1,45 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def mt_montana_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'MT'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ if payslip.contract_id.us_payroll_config_value('mt_mw4_sit_exempt'):
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ exemptions = payslip.contract_id.us_payroll_config_value('mt_mw4_sit_exemptions')
+ exemption_rate = payslip.rule_parameter('us_mt_sit_exemption_rate').get(schedule_pay)
+ withholding_rate = payslip.rule_parameter('us_mt_sit_rate').get(schedule_pay)
+ if not exemption_rate or not withholding_rate:
+ return 0.0, 0.0
+
+ adjusted_wage = wage - (exemption_rate * (exemptions or 0))
+ withholding = 0.0
+ if adjusted_wage > 0.0:
+ prior_wage_cap = 0.0
+ for row in withholding_rate:
+ wage_cap, base, rate = row
+ wage_cap = float(wage_cap) # e.g. 'inf'
+ if adjusted_wage < wage_cap:
+ withholding = round(base + ((rate / 100.0) * (adjusted_wage - prior_wage_cap)))
+ break
+ prior_wage_cap = wage_cap
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/nc_northcarolina.py b/l10n_us_hr_payroll/models/state/nc_northcarolina.py
new file mode 100644
index 00000000..056d1fe8
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/nc_northcarolina.py
@@ -0,0 +1,38 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def nc_northcarolina_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'NC'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('nc_nc4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('nc_nc4_sit_allowances')
+ allowances_rate = payslip.rule_parameter('us_nc_sit_allowance_rate').get(schedule_pay)['allowance']
+ deduction = payslip.rule_parameter('us_nc_sit_allowance_rate').get(schedule_pay)['standard_deduction'] if filing_status != 'head_household' else payslip.rule_parameter('us_nc_sit_allowance_rate').get(schedule_pay)['standard_deduction_hh']
+
+ taxable_wage = round((wage - (deduction + (allowances * allowances_rate))) * 0.0535)
+ withholding = 0.0
+ if taxable_wage < 0.0:
+ withholding -= taxable_wage
+ withholding = taxable_wage
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/nd_north_dakota.py b/l10n_us_hr_payroll/models/state/nd_north_dakota.py
new file mode 100644
index 00000000..1ef4ecbd
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/nd_north_dakota.py
@@ -0,0 +1,45 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def nd_north_dakota_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'ND'
+ 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('nd_w4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ 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 - (allowance * allowance_rate)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_rate:
+ amt, flat_fee, rate = row
+ if taxable_income < float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = round(withholding)
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ne_nebraska.py b/l10n_us_hr_payroll/models/state/ne_nebraska.py
new file mode 100644
index 00000000..9b360778
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ne_nebraska.py
@@ -0,0 +1,49 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ne_nebraska_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'NE'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ personal_exempt = payslip.contract_id.us_payroll_config_value('state_income_tax_exempt')
+ if personal_exempt:
+ 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('ne_w4n_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('ne_w4n_sit_allowances')
+ tax_rate = payslip.rule_parameter('us_ne_sit_tax_rate')[filing_status].get(schedule_pay)
+ sit_allowance = payslip.rule_parameter('us_ne_sit_allowances_rate')[schedule_pay]
+
+ allowance_amt = allowances * sit_allowance
+ taxable_income = wage - allowance_amt
+ withholding = 0.0
+ last = 0.0
+ for row in tax_rate:
+ amt, flat_fee, rate = row
+ if taxable_income < float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/nj_newjersey.py b/l10n_us_hr_payroll/models/state/nj_newjersey.py
new file mode 100644
index 00000000..f0a805b9
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/nj_newjersey.py
@@ -0,0 +1,52 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def nj_newjersey_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'NJ'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('nj_njw4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ allowances = payslip.contract_id.us_payroll_config_value('nj_njw4_sit_allowances')
+ sit_rate_table_key = payslip.contract_id.us_payroll_config_value('nj_njw4_sit_rate_table')
+ if not sit_rate_table_key and filing_status in ('single', 'married_joint'):
+ sit_rate_table_key = 'A'
+ elif not sit_rate_table_key:
+ sit_rate_table_key = 'B'
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ sit_table = payslip.rule_parameter('us_nj_sit_rate')[sit_rate_table_key].get(schedule_pay)
+ allowance_value = payslip.rule_parameter('us_nj_sit_allowance_rate')[schedule_pay]
+ if not allowances:
+ return 0.0, 0.0
+
+ gross_taxable_income = wage - (allowance_value * allowances)
+ withholding = 0.0
+ prior_wage_base = 0.0
+ for row in sit_table:
+ wage_base, base_amt, rate = row
+ wage_base = float(wage_base)
+ rate = rate / 100.0
+ if gross_taxable_income <= wage_base:
+ withholding = base_amt + ((gross_taxable_income - prior_wage_base) * rate)
+ break
+ prior_wage_base = wage_base
+
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/nm_new_mexico.py b/l10n_us_hr_payroll/models/state/nm_new_mexico.py
new file mode 100644
index 00000000..48bf1ae1
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/nm_new_mexico.py
@@ -0,0 +1,40 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def nm_new_mexico_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'NM'
+ 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('fed_941_fit_w4_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ tax_table = payslip.rule_parameter('us_nm_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage
+ 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 += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ny_new_york.py b/l10n_us_hr_payroll/models/state/ny_new_york.py
new file mode 100644
index 00000000..1a710b32
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ny_new_york.py
@@ -0,0 +1,54 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ny_new_york_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'NY'
+ 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('ny_it2104_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ tax_table = payslip.rule_parameter('us_ny_sit_tax_rate')[filing_status].get(schedule_pay)
+ allowances = payslip.contract_id.us_payroll_config_value('ny_it2104_sit_allowances')
+ over_10_deduction = payslip.rule_parameter('us_ny_sit_over_10_exemption_rate')[schedule_pay]
+ deduction_exemption = payslip.rule_parameter('us_ny_sit_deduction_exemption_rate')[filing_status].get(schedule_pay)
+
+ if allowances > 10:
+ if filing_status == 'single':
+ wage -= over_10_deduction[0] + over_10_deduction[2] * allowances
+ elif filing_status == 'married':
+ wage -= over_10_deduction[1] + over_10_deduction[2] * allowances
+
+ else:
+ if filing_status == 'single':
+ wage -= deduction_exemption[allowances]
+ elif filing_status == 'married':
+ wage -= deduction_exemption[allowances]
+ last = 0.0
+ withholding = 0.0
+ for row in tax_table:
+ amt, rate, flat_fee = row
+ if wage <= float(amt):
+ withholding = ((wage - last) * rate) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/oh_ohio.py b/l10n_us_hr_payroll/models/state/oh_ohio.py
new file mode 100644
index 00000000..5a7c3869
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/oh_ohio.py
@@ -0,0 +1,47 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def oh_ohio_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'OH'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ exemptions = payslip.contract_id.us_payroll_config_value('oh_it4_sit_exemptions')
+ exemption_rate = payslip.rule_parameter('us_oh_sit_exemption_rate')
+ withholding_rate = payslip.rule_parameter('us_oh_sit_rate')
+ multiplier_rate = payslip.rule_parameter('us_oh_sit_multiplier')
+
+ taxable_wage = (wage * pay_periods) - (exemption_rate * (exemptions or 0))
+ withholding = 0.0
+ if taxable_wage > 0.0:
+ prior_wage_cap = 0.0
+ for row in withholding_rate:
+ wage_cap, base, rate = row
+ wage_cap = float(wage_cap) # e.g. 'inf'
+ if taxable_wage < wage_cap:
+ withholding = base + (rate * (taxable_wage - prior_wage_cap))
+ break
+ prior_wage_cap = wage_cap
+ # Normalize to pay periods
+ withholding /= pay_periods
+ withholding *= multiplier_rate
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ok_oklahoma.py b/l10n_us_hr_payroll/models/state/ok_oklahoma.py
new file mode 100644
index 00000000..bc0ecc24
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ok_oklahoma.py
@@ -0,0 +1,47 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ok_oklahoma_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'OK'
+ 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
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('ok_w4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('ok_w4_sit_allowances')
+ allowances_amt = payslip.rule_parameter('us_ok_sit_allowances_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_ok_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (allowances * allowances_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, rate, flat_fee = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ri_rhode_island.py b/l10n_us_hr_payroll/models/state/ri_rhode_island.py
new file mode 100644
index 00000000..454bde47
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ri_rhode_island.py
@@ -0,0 +1,48 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ri_rhode_island_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'RI'
+ 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
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('ri_w4_sit_allowances')
+ exemption_rate = payslip.rule_parameter('us_ri_sit_exemption_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_ri_sit_tax_rate')[schedule_pay]
+
+ exemption_amt = 0.0
+ for row in exemption_rate:
+ amt, flat_fee = row
+ if wage > amt:
+ exemption_amt = flat_fee
+
+ taxable_income = wage - (allowances * exemption_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, flat_fee, rate = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/sc_south_carolina.py b/l10n_us_hr_payroll/models/state/sc_south_carolina.py
new file mode 100644
index 00000000..e877f8f9
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/sc_south_carolina.py
@@ -0,0 +1,50 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def sc_south_carolina_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'SC'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ personal_exempt = payslip.contract_id.us_payroll_config_value('state_income_tax_exempt')
+ if personal_exempt:
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('sc_w4_sit_allowances')
+ tax_rate = payslip.rule_parameter('us_sc_sit_tax_rate')
+ personal_exemption = payslip.rule_parameter('us_sc_sit_personal_exemption_rate')
+ deduction = payslip.rule_parameter('us_sc_sit_standard_deduction_rate')
+
+ annual_wage = wage * pay_periods
+ personal_exemption_amt = allowances * personal_exemption
+ standard_deduction = 0.0
+ if allowances > 0:
+ if (annual_wage * 0.1) > deduction:
+ standard_deduction = deduction
+ else:
+ standard_deduction = annual_wage * (10 / 100)
+ taxable_income = annual_wage - personal_exemption_amt - standard_deduction
+ withholding = 0.0
+ last = 0.0
+ for cap, rate, flat_amt in tax_rate:
+ if float(cap) > taxable_income:
+ withholding = (taxable_income * (rate / 100.0) - flat_amt)
+ break
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ut_utah.py b/l10n_us_hr_payroll/models/state/ut_utah.py
new file mode 100644
index 00000000..9e6b26f9
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ut_utah.py
@@ -0,0 +1,39 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ut_utah_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'UT'
+ 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('ut_w4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ 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_ut_tax_rate')
+ allowances = payslip.rule_parameter('us_ut_sit_allowances_rate')[filing_status].get(schedule_pay)
+ tax_table = payslip.rule_parameter('us_ut_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage * tax_rate
+ withholding = 0.0
+ amt, rate = tax_table
+ withholding = taxable_income - (allowances - ((wage - amt) * (rate / 100)))
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/va_virginia.py b/l10n_us_hr_payroll/models/state/va_virginia.py
new file mode 100644
index 00000000..a09f80a0
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/va_virginia.py
@@ -0,0 +1,44 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def va_virginia_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'VA'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ 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.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ personal_exemptions = payslip.contract_id.us_payroll_config_value('va_va4_sit_exemptions')
+ other_exemptions = payslip.contract_id.us_payroll_config_value('va_va4_sit_other_exemptions')
+ personal_exemption_rate = payslip.rule_parameter('us_va_sit_exemption_rate')
+ other_exemption_rate = payslip.rule_parameter('us_va_sit_other_exemption_rate')
+ deduction = payslip.rule_parameter('us_va_sit_deduction')
+ withholding_rate = payslip.rule_parameter('us_va_sit_rate')
+
+ taxable_wage = (wage * pay_periods) - (deduction + (personal_exemptions * personal_exemption_rate) + (other_exemptions * other_exemption_rate))
+ withholding = 0.0
+ if taxable_wage > 0.0:
+ for row in withholding_rate:
+ if taxable_wage > row[0]:
+ selected_row = row
+ wage_min, base, rate = selected_row
+ withholding = base + ((taxable_wage - wage_min) * rate / 100.0)
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/vt_vermont.py b/l10n_us_hr_payroll/models/state/vt_vermont.py
new file mode 100644
index 00000000..463e007c
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/vt_vermont.py
@@ -0,0 +1,46 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def vt_vermont_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'VT'
+ 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
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('vt_w4vt_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ allowances = payslip.contract_id.us_payroll_config_value('vt_w4vt_sit_allowances')
+ allowance_amt = payslip.rule_parameter('us_vt_sit_allowances_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_vt_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (allowances * allowance_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, flat_fee, rate = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
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..4294b5f5
--- /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.rule_parameter('us_wa_fml_rate')
+ rate *= payslip.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/state/wi_wisconsin.py b/l10n_us_hr_payroll/models/state/wi_wisconsin.py
new file mode 100644
index 00000000..c1d53bbb
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/wi_wisconsin.py
@@ -0,0 +1,47 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def wi_wisconsin_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'WI'
+ 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
+
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ filing_status = payslip.contract_id.us_payroll_config_value('wi_wt4_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')
+ exemptions = payslip.contract_id.us_payroll_config_value('wi_wt4_sit_exemptions')
+ exemption_amt = payslip.rule_parameter('us_wi_sit_exemption_rate')
+ tax_table = payslip.rule_parameter('us_wi_sit_tax_rate')[filing_status]
+
+ taxable_income = wage * pay_periods
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, rate, flat_fee = row
+ if taxable_income <= float(amt):
+ withholding = (((taxable_income - last) * (rate / 100)) + flat_fee) - (exemptions * exemption_amt)
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/wv_west_virginia.py b/l10n_us_hr_payroll/models/state/wv_west_virginia.py
new file mode 100644
index 00000000..34031eb0
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/wv_west_virginia.py
@@ -0,0 +1,44 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def wv_west_virginia_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'WV'
+ 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('wv_it104_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.contract_id.schedule_pay
+ additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ exemptions = payslip.contract_id.us_payroll_config_value('wv_it104_sit_exemptions')
+ exemption_amt = payslip.rule_parameter('us_wv_sit_exemption_rate')[schedule_pay]
+ tax_table = payslip.rule_parameter('us_wv_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (exemptions * exemption_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, flat_fee, rate = row
+ if taxable_income <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/update.py b/l10n_us_hr_payroll/models/update.py
new file mode 100644
index 00000000..2184abef
--- /dev/null
+++ b/l10n_us_hr_payroll/models/update.py
@@ -0,0 +1,26 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+import datetime
+from odoo import fields, models
+
+
+class PublisherWarrantyContract(models.AbstractModel):
+ _inherit = 'publisher_warranty.contract'
+
+ def _get_hibou_modules(self):
+ modules = super(PublisherWarrantyContract, self)._get_hibou_modules()
+ try:
+ today_date = fields.Date.today()
+ last_thirty_date = today_date - datetime.timedelta(days=30)
+ today = fields.Date.to_string(today_date + datetime.timedelta(days=1)) # Dates vs Datetimes, pad out a day
+ last_thirty = fields.Date.to_string(last_thirty_date)
+ self.env.cr.execute(
+ 'SELECT COUNT(DISTINCT(employee_id)) FROM hr_payslip WHERE create_date BETWEEN %s AND %s',
+ (last_thirty, today))
+ employee_count = self.env.cr.fetchone()[0] or 0
+ modules.update({
+ 'l10n_us_hr_payroll': employee_count,
+ })
+ except:
+ pass
+ return modules
diff --git a/l10n_us_hr_payroll/models/us_payroll_config.py b/l10n_us_hr_payroll/models/us_payroll_config.py
index ed0e6d73..6687cbeb 100644
--- a/l10n_us_hr_payroll/models/us_payroll_config.py
+++ b/l10n_us_hr_payroll/models/us_payroll_config.py
@@ -14,6 +14,13 @@ class HRContractUSPayrollConfig(models.Model):
name = fields.Char(string="Description")
employee_id = fields.Many2one('hr.employee', string="Employee", required=True)
state_id = fields.Many2one('res.country.state', string="Applied State")
+ 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 Rule Parameter, used by some states or your own rules.')
+ workers_comp_er_code = fields.Char(string='Workers\' Comp Code (Employer Withholding)',
+ help='Code for a Rule Parameter, used by some states or your own rules.')
fed_940_type = fields.Selection([
(FUTA_TYPE_EXEMPT, 'Exempt (0%)'),
@@ -43,3 +50,229 @@ class HRContractUSPayrollConfig(models.Model):
help='Form W4 (2020+) 4(b)')
fed_941_fit_w4_additional_withholding = fields.Float(string='Federal W4 Additional Withholding [4(c)]',
help='Form W4 (2020+) 4(c)')
+
+ al_a4_sit_exemptions = fields.Selection([
+ ('', '0'),
+ ('S', 'S'),
+ ('MS', 'MS'),
+ ('M', 'M'),
+ ('H', 'H'),
+ ], string='Alabama A4 Withholding Exemptions', help='A4 1. 2. 3.')
+ al_a4_sit_dependents = fields.Integer(string='Alabama A4 Dependents', help='A4 4.')
+
+ ar_ar4ec_sit_allowances = fields.Integer(string='Arkansas AR4EC allowances', help='AR4EC 3.')
+
+ az_a4_sit_withholding_percentage = fields.Float(
+ 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.')
+
+ ca_de4_sit_allowances = fields.Integer(string='California W-4 Allowances',
+ help='CA W-4 3.')
+ ca_de4_sit_additional_allowances = fields.Integer(string='California W-4 Additional Allowances',
+ help='CA W-4 4(c).')
+ ca_de4_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single or Married filing separately'),
+ ('married', 'Married filing jointly'),
+ ('head_household', 'Head of Household')
+ ], string='California W-4 Filing Status', help='CA W-4 1(c).')
+
+ 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.')
+
+ de_w4_sit_filing_status = fields.Selection([
+ ('single', 'Single or Married filing separately'),
+ ('married', 'Married filing jointly'),
+ ], string='Delaware W-4 Marital Status', help='DE W-4 3.')
+ de_w4_sit_dependent = fields.Integer(string='Delaware W-4 Dependents', help='DE W-4 4.')
+
+ ga_g4_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married filing joint, both spouses working', 'Married Filing Joint, both spouses working'),
+ ('married filing joint, one spouse working', 'Married Filing Joint, one spouse working'),
+ ('married filing separate', 'Married Filing Separate'),
+ ('head of household', 'Head of Household'),
+ ], string='Georgia G-4 Filing Status', help='G-4 3.')
+ ga_g4_sit_dependent_allowances = fields.Integer(string='Georgia G-4 Dependent Allowances',
+ help='G-4 4.')
+ 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.')
+
+ ia_w4_sit_allowances = fields.Integer(string='Iowa W-4 allowances', help='IA W-4 6.')
+
+ id_w4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head of household', 'Head of Household'),
+ ], string='Idaho ID W-4 Withholding Status', help='ID W-4 A.B.C.')
+ id_w4_sit_allowances = fields.Integer(string='Idaho ID W-4 Allowances', help='ID W-4 1.')
+
+ il_w4_sit_basic_allowances = fields.Integer(string='Illinois IL-W-4 Number of Basic Allowances', help='IL-W-4 Step 1.')
+ il_w4_sit_additional_allowances = fields.Integer(string='Illinois IL-W-4 Number of Additional Allowances', help='IL-W-4 Step 2.')
+
+ in_w4_sit_personal_exemption = fields.Integer(string='Indiana In-W-4 Number of Personal Exemption', help='IN-W-4 5.')
+ in_w4_sit_dependent_exemption = fields.Integer(string='Indiana In-W-4 Number of Dependent Exemption', help='IN-W-4 6.')
+
+ ks_k4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Joint'),
+ ], string='Kansas K-4 Filing Status', help='KS K-4 3.')
+ ks_k4_sit_allowances = fields.Integer(string='Kansas KS K-4 Number of Allowances', help='KS K-4 Step 4.')
+
+ la_l4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Louisiana LA L-4 Filing Status', help='LA L-4 3.')
+ la_l4_sit_exemptions = fields.Integer(string='Louisiana LA L-4 Number of Exemptions', help='LA L-4 6.')
+ la_l4_sit_dependents = fields.Integer(string='Louisiana LA L-4 Number of Dependents', help='LA L-4 7.')
+
+ me_w4me_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single or Head of Household'),
+ ('married', 'Married'),
+ ], string='Maine W-4ME Filing Status', help='ME W-4ME 3.')
+ me_w4me_sit_allowances = fields.Integer(string='Maine Allowances', help='W-4ME 4.')
+
+ mi_w4_sit_exemptions = fields.Integer(string='Michigan MI W-4 Exemptions', help='MI-W4 6.')
+
+ mn_w4mn_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Minnesota W-4MN Marital Status', help='W-4MN')
+ mn_w4mn_sit_allowances = fields.Integer(string='Minnesota Allowances', help='W-4MN 1.')
+
+ mo_mow4_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single or Married Spouse Works or Married Filing Separate'),
+ ('married', 'Married (Spouse does not work)'),
+ ('head_of_household', 'Head of Household'),
+ ], string='Missouri W-4 Filing Status', help='MO W-4 1.')
+ mo_mow4_sit_withholding = fields.Integer(string='Missouri MO W-4 Reduced Withholding', help='MO W-4 3.')
+
+ ms_89_350_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married', 'Married (spouse NOT employed)'),
+ ('married_dual', 'Married (spouse IS employed)'),
+ ('head_of_household', 'Head of Household'),
+ ], string='Mississippi 89-350 Filing Status', help='89-350 1. 2. 3. 8.')
+ ms_89_350_sit_exemption_value = fields.Float(string='Mississippi 89-350 Exemption Total',
+ help='89-350 Box 6 (including filing status amounts)')
+
+ mt_mw4_sit_exemptions = fields.Integer(string='Montana MW-4 Exemptions',
+ help='MW-4 Box G')
+ # Don't use the main state_income_tax_exempt because of special meaning and reporting
+ # Use additional withholding but name it on the form 'MW-4 Box H'
+ mt_mw4_sit_exempt = fields.Selection([
+ ('', 'Not Exempt'),
+ ('tribe', 'Registered Tribe'),
+ ('reserve', 'Reserve or National Guard'),
+ ('north_dakota', 'North Dakota'),
+ ('montana_for_marriage', 'Montana for Marriage'),
+ ], string='Montana MW-4 Exempt from Withholding', help='MW-4 Section 2')
+
+ nc_nc4_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Head of Household')
+ ], string='North Carolina NC-4 Filing Status', help='NC-4')
+ 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'),
+ ('married', 'Married'),
+ ], string='Nebraska NE W-4N Filing Status', help='NE W-4N')
+ ne_w4n_sit_allowances = fields.Integer(string='Nebraska NE W-4N Allowances', help='NE W-4N 1.')
+
+ nj_njw4_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married_separate', 'Married/Civil Union partner Separate'),
+ ('married_joint', 'Married/Civil Union Couple Joint'),
+ ('widower', 'Widower/Surviving Civil Union Partner'),
+ ('head_household', 'Head of Household')
+ ], string='New Jersey NJ-W4 Filing Status', help='NJ-W4 2.')
+ nj_njw4_sit_allowances = fields.Integer(string='New Jersey NJ-W4 Allowances', help='NJ-W4 4.')
+ nj_njw4_sit_rate_table = fields.Selection([
+ ('A', 'A'),
+ ('B', 'B'),
+ ('C', 'C'),
+ ('D', 'D'),
+ ('E', 'E')
+ ], string='New Jersey Wage Chart Letter', help='NJ-W4. 3.')
+
+ ny_it2104_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='New York NY IT-2104 Filing Status', help='NY IT-2104')
+ ny_it2104_sit_allowances = fields.Integer(string="New York IT-2104 Allowances", help="NY IT-2104 1. 2.")
+
+ # Ohio will use generic SIT exempt and additional fields
+ oh_it4_sit_exemptions = fields.Integer(string='Ohio IT-4 Exemptions',
+ help='Line 4')
+
+ ok_w4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Married, but withhold at higher Single rate')
+ ], string='Oklahoma OK-W-4 Filing Status', help='OK-W-4')
+ ok_w4_sit_allowances = fields.Integer(string='Oklahoma OK-W-4 Allowances', help='OK-W-4 5.')
+
+ ri_w4_sit_allowances = fields.Integer(string='Rhode Island RI W-4 Allowances', help='RI W-4 1.')
+
+ sc_w4_sit_allowances = fields.Integer(string='South Carolina SC W-4 Allowances', help='SC W-4 5.')
+
+ ut_w4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Head of Household')
+ ], string='Utah UT W-4 Filing Status', help='UT W-4 C.')
+
+ vt_w4vt_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Vermont VT W-4VT Filing Status', help='VT W-4VT')
+ vt_w4vt_sit_allowances = fields.Integer(string='Vermont VT W-4VT Allowances', help='VT W-4VT 5.')
+
+ va_va4_sit_exemptions = fields.Integer(string='Virginia VA-4(P) Personal Exemptions',
+ help='VA-4(P) 1(a)')
+ va_va4_sit_other_exemptions = fields.Integer(string='Virginia VA-4(P) Age & Blindness Exemptions',
+ help='VA-4(P) 1(b)')
+
+ wi_wt4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Wisconsin WT-4 Filing Status', help='WI WT-4')
+ wi_wt4_sit_exemptions = fields.Integer(string='Wisconsin Exemptions', help='WI WT-4 1.(d)')
+
+ wv_it104_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Head of Household')
+ ], string='West Virginia WV/IT-104 Filing Status', help='WV WV/IT-104')
+ wv_it104_sit_exemptions = fields.Integer(string='West Virginia Exemptions', help='WV WV/IT-104 4.')
diff --git a/l10n_us_hr_payroll/tests/__init__.py b/l10n_us_hr_payroll/tests/__init__.py
index 23702419..a7c95ebf 100755
--- a/l10n_us_hr_payroll/tests/__init__.py
+++ b/l10n_us_hr_payroll/tests/__init__.py
@@ -1,5 +1,130 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import common
+
+from . import test_special
+
from . import test_us_payslip_2019
from . import test_us_payslip_2020
+
+from . import test_us_ak_alaska_payslip_2019
+from . import test_us_ak_alaska_payslip_2020
+
+from . import test_us_al_alabama_payslip_2019
+from . import test_us_al_alabama_payslip_2020
+
+from . import test_us_ar_arkansas_payslip_2019
+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_ca_california_payslip_2019
+from . import test_us_ca_california_payslip_2020
+
+from . import test_us_co_colorado_payslip_2020
+
+from . import test_us_ct_connecticut_payslip_2019
+from . import test_us_ct_connecticut_payslip_2020
+
+from . import test_us_de_delaware_payslip_2020
+
+from . import test_us_fl_florida_payslip_2019
+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_ia_iowa_payslip_2019
+from . import test_us_ia_iowa_payslip_2020
+
+from . import test_us_id_idaho_payslip_2019
+from . import test_us_id_idaho_payslip_2020
+
+from . import test_us_il_illinois_payslip_2019
+from . import test_us_il_illinois_payslip_2020
+
+from . import test_us_in_indiana_payslip_2020
+
+from . import test_us_ky_kentucky_payslip_2020
+
+from . import test_us_ks_kansas_payslip_2020
+
+from . import test_us_la_louisiana_payslip_2019
+from . import test_us_la_louisiana_payslip_2020
+
+from . import test_us_me_maine_payslip_2020
+
+from . import test_us_mi_michigan_payslip_2019
+from . import test_us_mi_michigan_payslip_2020
+
+from . import test_us_mn_minnesota_payslip_2019
+from . import test_us_mn_minnesota_payslip_2020
+
+from . import test_us_mo_missouri_payslip_2019
+from . import test_us_mo_missouri_payslip_2020
+
+from . import test_us_ms_mississippi_payslip_2019
+from . import test_us_ms_mississippi_payslip_2020
+
+from . import test_us_mt_montana_payslip_2019
+from . import test_us_mt_montana_payslip_2020
+
+from . import test_us_nc_northcarolina_payslip_2019
+from . import test_us_nc_northcarolina_payslip_2020
+
+from . import test_us_nd_north_dakota_payslip_2020
+
+from . import test_us_ne_nebraska_payslip_2020
+
+from . import test_us_nh_new_hampshire_payslip_2020
+
+from . import test_us_nj_newjersey_payslip_2019
+from . import test_us_nj_newjersey_payslip_2020
+
+from . import test_us_nm_new_mexico_payslip_2020
+
+from . import test_us_nv_nevada_payslip_2020
+
+from . import test_us_ny_new_york_payslip_2019
+from . import test_us_ny_new_york_payslip_2020
+
+from . import test_us_oh_ohio_payslip_2019
+from . import test_us_oh_ohio_payslip_2020
+
+from . import test_us_ok_oklahoma_payslip_2020
+
+from . import test_us_pa_pennsylvania_payslip_2019
+from . import test_us_pa_pennsylvania_payslip_2020
+
+from . import test_us_ri_rhode_island_payslip_2020
+
+from . import test_us_sc_south_carolina_payslip_2019
+from . import test_us_sc_south_carolina_payslip_2020
+
+from . import test_us_sd_south_dakota_payslip_2020
+
+from . import test_us_tn_tennessee_payslip_2020
+
+from . import test_us_tx_texas_payslip_2019
+from . import test_us_tx_texas_payslip_2020
+
+from . import test_us_us_utah_payslip_2020
+
+from . import test_us_vt_vermont_payslip_2020
+
+from . import test_us_va_virginia_payslip_2019
+from . import test_us_va_virginia_payslip_2020
+
+from . import test_us_wa_washington_payslip_2019
+from . import test_us_wa_washington_payslip_2020
+
+from . import test_us_wv_west_virginia_payslip_2020
+
+from . import test_us_wi_wisconsin_payslip_2020
+
+from . import test_us_wy_wyoming_payslip_2019
+from . import test_us_wy_wyoming_payslip_2020
diff --git a/l10n_us_hr_payroll/tests/common.py b/l10n_us_hr_payroll/tests/common.py
index fc84b0fe..07615652 100755
--- a/l10n_us_hr_payroll/tests/common.py
+++ b/l10n_us_hr_payroll/tests/common.py
@@ -3,9 +3,11 @@
from logging import getLogger
from sys import float_info as sys_float_info
from collections import defaultdict
+from datetime import timedelta
from odoo.tests import common
from odoo.tools.float_utils import float_round as odoo_float_round
+from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
def process_payslip(payslip):
@@ -20,6 +22,12 @@ class TestUsPayslip(common.TransactionCase):
debug = False
_logger = getLogger(__name__)
+ def setUp(self):
+ super(TestUsPayslip, self).setUp()
+ self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior', 'date_to')
+ self.structure_type_id = self.ref('l10n_us_hr_payroll.structure_type_employee')
+ self.resource_calendar_id = self.ref('resource.resource_calendar_std')
+
float_info = sys_float_info
def float_round(self, value, digits):
@@ -61,6 +69,10 @@ class TestUsPayslip(common.TransactionCase):
'employee_id': employee.id,
}
+ # Backwards compatability with 'futa_type'
+ if 'futa_type' in kwargs:
+ kwargs['fed_940_type'] = kwargs['futa_type']
+
for key, val in kwargs.items():
# Assume any Odoo object is in a Many2one
if hasattr(val, 'id'):
@@ -83,13 +95,13 @@ class TestUsPayslip(common.TransactionCase):
if not contract_values.get('state'):
contract_values['state'] = 'open' # Running
if not contract_values.get('structure_type_id'):
- contract_values['structure_type_id'] = self.ref('l10n_us_hr_payroll.structure_type_employee')
+ contract_values['structure_type_id'] = self.structure_type_id
if not contract_values.get('date_start'):
contract_values['date_start'] = '2016-01-01'
if not contract_values.get('date_end'):
contract_values['date_end'] = '2030-12-31'
if not contract_values.get('resource_calendar_id'):
- contract_values['resource_calendar_id'] = self.ref('resource.resource_calendar_std')
+ contract_values['resource_calendar_id'] = self.resource_calendar_id
# Compatibility with earlier Odoo versions
if not contract_values.get('journal_id') and hasattr(contract_model, 'journal_id'):
@@ -140,11 +152,87 @@ class TestUsPayslip(common.TransactionCase):
def assertPayrollEqual(self, first, second):
self.assertAlmostEqual(first, second, self.payroll_digits)
- def test_semi_monthly(self):
- salary = 80000.0
- employee = self._createEmployee()
- # so the schedule_pay is now on the Structure...
- contract = self._createContract(employee, wage=salary, schedule_pay='semi-monthly')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-14')
+ def assertPayrollAlmostEqual(self, first, second):
+ self.assertAlmostEqual(first, second, self.payroll_digits-1)
+ def get_us_state(self, code, cache={}):
+ country_key = 'US_COUNTRY'
+ if code in cache:
+ return cache[code]
+ if country_key not in cache:
+ cache[country_key] = self.env.ref('base.us')
+ us_country = cache[country_key]
+ us_state = self.env['res.country.state'].search([
+ ('country_id', '=', us_country.id),
+ ('code', '=', code),
+ ], limit=1)
+ cache[code] = us_state
+ return us_state
+
+ def _test_suta(self, category, state_code, rate, date, wage_base=None, relaxed=False, **extra_contract):
+ if relaxed:
+ _assert = self.assertPayrollAlmostEqual
+ else:
+ _assert = self.assertPayrollEqual
+ if wage_base:
+ # Slightly larger than 1/2 the wage_base
+ wage = round(wage_base / 2.0) + 100.0
+ self.assertTrue((2 * wage) > wage_base, 'Granularity of wage_base too low.')
+ else:
+ wage = 1000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state(state_code),
+ **extra_contract)
+
+ rate = -rate / 100.0 # Assumed passed as percent positive
+
+ # Tests
+ payslip = self._createPayslip(employee, date, date + timedelta(days=30))
+
+ # Test exemptions
+ contract.us_payroll_config_id.fed_940_type = USHRContract.FUTA_TYPE_EXEMPT
payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ _assert(cats.get(category, 0.0), 0.0)
+
+ contract.us_payroll_config_id.fed_940_type = USHRContract.FUTA_TYPE_BASIC
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ _assert(cats.get(category, 0.0), 0.0)
+
+ # Test Normal
+ contract.us_payroll_config_id.fed_940_type = USHRContract.FUTA_TYPE_NORMAL
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ _assert(cats.get(category, 0.0), wage * rate)
+ process_payslip(payslip)
+
+ # Second Payslip
+ payslip = self._createPayslip(employee, date + timedelta(days=31), date + timedelta(days=60))
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ if wage_base:
+ remaining_unemp_wages = wage_base - wage
+ self.assertTrue((remaining_unemp_wages * rate) <= 0.01) # less than 0.01 because rate is negative
+ _assert(cats.get(category, 0.0), remaining_unemp_wages * rate)
+
+ # As if they were paid once already, so the first "two payslips" would remove all of the tax obligation
+ # 1 wage - Payslip (confirmed)
+ # 1 wage - external_wages
+ # 1 wage - current Payslip
+ contract.external_wages = wage
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ _assert(cats.get(category, 0.0), 0.0)
+ else:
+ _assert(cats.get(category, 0.0), wage * rate)
+
+ def _test_er_suta(self, state_code, rate, date, wage_base=None, relaxed=False, **extra_contract):
+ self._test_suta('ER_US_SUTA', state_code, rate, date, wage_base=wage_base, relaxed=relaxed, **extra_contract)
+
+ def _test_ee_suta(self, state_code, rate, date, wage_base=None, relaxed=False, **extra_contract):
+ self._test_suta('EE_US_SUTA', state_code, rate, date, wage_base=wage_base, relaxed=relaxed, **extra_contract)
diff --git a/l10n_us_hr_payroll/tests/test_special.py b/l10n_us_hr_payroll/tests/test_special.py
new file mode 100644
index 00000000..688761ec
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_special.py
@@ -0,0 +1,119 @@
+from .common import TestUsPayslip, process_payslip
+
+
+class TestSpecial(TestUsPayslip):
+ def test_semi_monthly(self):
+ salary = 80000.0
+ employee = self._createEmployee()
+ # so the schedule_pay is now on the Structure...
+ contract = self._createContract(employee, wage=salary, schedule_pay='semi-monthly')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-14')
+ payslip.compute_sheet()
+
+ def test_payslip_sum_behavior(self):
+ us_structure = self.env.ref('l10n_us_hr_payroll.hr_payroll_structure')
+ rule_category_comp = self.env.ref('hr_payroll.COMP')
+ test_rule_category = self.env['hr.salary.rule.category'].create({
+ 'name': 'Test Sum Behavior',
+ 'code': 'test_sum_behavior',
+ 'parent_id': rule_category_comp.id,
+ })
+ test_rule = self.env['hr.salary.rule'].create({
+ 'sequence': 450,
+ 'struct_id': us_structure.id,
+ 'category_id': test_rule_category.id,
+ 'name': 'Test Sum Behavior',
+ 'code': 'test_sum_behavior',
+ 'condition_select': 'python',
+ 'condition_python': 'result = 1',
+ 'amount_select': 'code',
+ 'amount_python_compute': '''
+ytd_category = payslip.sum_category('test_sum_behavior', '2020-01-01', '2021-01-01')
+ytd_rule = payslip.sum('test_sum_behavior', '2020-01-01', '2021-01-01')
+result = 0.0
+if ytd_category != ytd_rule:
+ # error
+ result = -1.0
+elif ytd_rule == 0.0:
+ # first payslip in period
+ result = 1.0
+'''
+ })
+ salary = 80000.0
+ employee = self._createEmployee()
+ contract = self._createContract(employee, wage=salary, schedule_pay='bi-weekly')
+ payslip = self._createPayslip(employee, '2019-12-30', '2020-01-12')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertEqual(cats['test_sum_behavior'], 1.0)
+ process_payslip(payslip)
+
+ # Basic date_from behavior.
+ self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior', 'date_from')
+ # The the date_from on the last payslip will not be found
+ payslip = self._createPayslip(employee, '2020-01-13', '2020-01-27')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertEqual(cats['test_sum_behavior'], 1.0)
+
+ # date_to behavior.
+ self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior', 'date_to')
+ # The date_to on the last payslip is found
+ payslip = self._createPayslip(employee, '2020-01-13', '2020-01-27')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertEqual(cats['test_sum_behavior'], 0.0)
+
+ def test_recursive_salary_rule_category(self):
+ # self.debug = True
+ us_structure = self.env.ref('l10n_us_hr_payroll.hr_payroll_structure')
+ # In this scenario, you are in rule code that will check for the category
+ # and a subcategory will also
+ alw_category = self.env.ref('hr_payroll.ALW')
+ ded_category = self.env.ref('hr_payroll.DED')
+ test_category = self.env['hr.salary.rule.category'].create({
+ 'name': 'Special ALW',
+ 'code': 'ALW_SPECIAL_RECURSIVE',
+ 'parent_id': alw_category.id,
+ })
+ test_special_alw = self.env['hr.salary.rule'].create({
+ 'name': 'Flat amount 200',
+ 'code': 'ALW_SPECIAL_RECURSIVE',
+ 'category_id': test_category.id,
+ 'condition_select': 'none',
+ 'amount_select': 'fix',
+ 'amount_fix': 200.0,
+ 'struct_id': us_structure.id,
+ })
+ test_recursion = self.env['hr.salary.rule'].create({
+ 'name': 'Actual Test Behavior',
+ 'code': 'RECURSION_TEST',
+ 'category_id': ded_category.id,
+ 'condition_select': 'none',
+ 'amount_select': 'code',
+ 'amount_python_compute': """
+# this rule will always be the total of the ALW category and YTD ALW category
+result = categories.ALW
+year = payslip.dict.get_year()
+result += payslip.sum_category('ALW', str(year) + '-01-01', str(year+1) + '-01-01')
+ """,
+ 'sequence': 101,
+ 'struct_id': us_structure.id,
+ })
+
+ salary = 80000.0
+ employee = self._createEmployee()
+ contract = self._createContract(employee, wage=salary, schedule_pay='bi-weekly')
+ payslip = self._createPayslip(employee, '2020-01-01', '2020-01-14')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+ self.assertEqual(rules['RECURSION_TEST'], 200.0)
+ process_payslip(payslip)
+
+ payslip = self._createPayslip(employee, '2020-01-15', '2020-01-27')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+ # two hundred is in the YTD ALW
+ self.assertEqual(rules['RECURSION_TEST'], 200.0 + 200.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py
new file mode 100644
index 00000000..3eb62184
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py
@@ -0,0 +1,61 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsAKPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ AK_UNEMP_MAX_WAGE = 39900.00
+ AK_UNEMP = -(1.780 / 100.0)
+ AK_UNEMP_EE = -(0.5 / 100.0)
+
+ def test_taxes_monthly_over_max(self):
+ salary = 50000.00
+ schedule_pay = 'monthly'
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AK'),
+ state_income_tax_additional_withholding=0.0,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alaska 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.AK_UNEMP_MAX_WAGE * self.AK_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SUTA'], self.AK_UNEMP_MAX_WAGE * self.AK_UNEMP_EE)
+
+ process_payslip(payslip)
+
+ remaining_ak_unemp_wages = 0.00 # We already reached the maximum wage for unemployment insurance.
+
+ self._log('2019 Alaska 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_ak_unemp_wages * self.AK_UNEMP) # 0
+
+ def test_taxes_weekly_under_max(self):
+ salary = 5000.00
+ schedule_pay = 'weekly'
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AK'),
+ state_income_tax_additional_withholding=0.0,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alaska 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.AK_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.AK_UNEMP_EE)
+
+ process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2020.py
new file mode 100644
index 00000000..868a8dff
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2020.py
@@ -0,0 +1,15 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsAKPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ AK_UNEMP_MAX_WAGE = 41500.00
+ AK_UNEMP = 1.590
+ AK_UNEMP_EE = 0.5
+
+ def test_2020_taxes(self):
+ self._test_er_suta('AK', self.AK_UNEMP, date(2020, 1, 1), wage_base=self.AK_UNEMP_MAX_WAGE)
+ self._test_ee_suta('AK', self.AK_UNEMP_EE, date(2020, 1, 1), wage_base=self.AK_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
new file mode 100644
index 00000000..33ddb2f9
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
@@ -0,0 +1,264 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsALPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ AL_UNEMP_MAX_WAGE = 8000.00
+ AL_UNEMP = -2.70 / 100.0
+
+ def test_taxes_weekly(self):
+ salary = 10000.00
+ schedule_pay = 'weekly'
+ dependents = 1
+ filing_status = 'S'
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 10000.00 for wages per period , 52.0 for weekly -> 10000 * 52 -> 520000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 520000 - 2000 = 518000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -2999.66 * 52 = -155982.32
+ # -> 518000.0 - 155982.32 = 362017.68
+ # Step 2C -> Subtract the personal exemption -> 1500 for single filing_status
+ # -> 362017.68 - 1500 = 360517.68
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 300$. Subtract
+ # -> 360517.68 - 300 = 360217.68
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((360217.68 - 500 - 2500) * (5.00 / 100)) -> 17970.884000000002
+ # Convert back to pay period
+ # wh = round(17970.884000000002, 2) -> 17970.88 / 52.0 -> 345.59
+ wh = -345.59
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=filing_status,
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -2999.66) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ remaining_AL_UNEMP_wages = 0.00 # We already reached the maximum wage for unemployment insurance.
+
+ self._log('2019 Alabama 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_AL_UNEMP_wages * self.AL_UNEMP) # 0
+
+ def test_taxes_married_jointly(self):
+ salary = 10000.00
+ schedule_pay = 'weekly'
+ dependents = 1
+ filing_status = 'M'
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 10000.00 for wages per period , 52.0 for weekly -> 10000 * 52 -> 520000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 4000. Subtract from yearly income
+ # 520000 - 4000 = 516000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -2999.66 * 52 = -155982.32
+ # -> 516000.0 - 155982.32 = 360017.68
+ # Step 2C -> Subtract the personal exemption -> 3000 for married filing jointly.
+ # -> 360017.68 - 3000 = 357017.68
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 300$. Subtract
+ # -> 357017.68 - 300 = 356717.68
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (1000 * (2.00 / 100)) + (5000 * (4.00 / 100)) + ((356717.68 - 1000 - 50000) * (5.00 / 100))
+ # -> 17755.884000000002
+ # Convert back to pay period
+ # wh = round(17755.884000000002, 2) -> 15505.88 / 52.0 -> 341.45923076923077
+ wh = -341.46
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=filing_status,
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -2999.66) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+
+ def test_taxes_semimonthly_filing_seperate(self):
+ salary = 20000.00
+ schedule_pay = 'monthly'
+ filing_status = 'MS'
+ dependents = 2
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 10000.00 for wages per period , 12.0 for monthly -> 20000 * 12 -> 240000.00
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 240000.00 - 2000 = 238000.00
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -4821.99 * 12 = -57863.88
+ # -> 238000.00 - 57863.88 = 180136.12
+ # Step 2C -> Subtract the personal exemption -> 1500 for married filing separately
+ # -> 180136.12 - 1500 = 178636.12
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 600. Subtract
+ # -> 178636.12 - 600 = 178036.12
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((178036.12 - 500 - 2500) * (5.00 / 100)) -> 8861.806
+ # Convert back to pay period
+ # wh = 8861.806 / 12.0 rounded -> 738.48
+ wh = -738.48
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=filing_status,
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip monthly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -4822.00) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ def test_tax_exempt(self):
+ salary = 5500.00
+ wh = 0
+ schedule_pay = 'weekly'
+ dependents = 2
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions='',
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=True,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip exempt:')
+ 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.AL_UNEMP)
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), wh)
+
+ def test_additional_withholding(self):
+ salary = 5500.0
+ schedule_pay = 'weekly'
+ additional_wh = 40.0
+ dependents = 2
+ # filing status default is single
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 5500.00 for wages per period , 52.0 for monthly -> 5500 * 52.0 -> 286000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 286000.0 - 2000 = 284000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -1422.4 * 52.0 = -73964.8
+ # -> 284000.0 - 73964.8 = 210035.2
+ # Step 2C -> Subtract the personal exemption -> 1500 for single
+ # -> 210035.2 - 1500 = 208535.2
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 600. Subtract
+ # -> 208535.2 - 600 = 207935.2
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((207935.2 - 500 - 2500) * (5.00 / 100)) -> 10356.76
+ # Convert back to pay period
+ # wh = 10356.76 / 52.0 rounded -> 199.17
+ wh = -199.17
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions='S',
+ state_income_tax_additional_withholding=40.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip additional withholding:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -1422.4) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh - additional_wh)
+
+ def test_personal_exemption(self):
+ salary = 5500.0
+ schedule_pay = 'weekly'
+ # filing status default is single
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 5500.00 for wages per period , 52.0 for monthly -> 5500 * 52.0 -> 286000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 286000.0 - 2000 = 284000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -1422.4 * 52.0 = -73964.8
+ # -> 284000.0 - 73964.8 = 210035.2
+ # Step 2C -> Subtract the personal exemption -> 0 for personal exemptioon
+ # -> 210035.2 - 0 = 210035.2
+ # Step 2D -> Subtract per dependent. No dependents so 0
+ # -> 210035.2 - 0 = 210035.2
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((210035.2 - 500 - 2500) * (5.00 / 100)) -> 10461.76
+ # Convert back to pay period
+ # wh = 10461.76 / 52.0 rounded -> 201.19
+ wh = -199.74
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions='S',
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=0.0,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip additional withholding:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -1422.4) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2020.py
new file mode 100644
index 00000000..23865fc7
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsALPayslip(TestUsPayslip):
+ # Taxes and Rates
+ AL_UNEMP_MAX_WAGE = 8000.00
+ AL_UNEMP = 2.70
+
+ def _test_sit(self, wage, exempt, exemptions, additional_withholding, dependent, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=exempt,
+ state_income_tax_exempt=exemptions,
+ state_income_tax_additional_withholding=additional_withholding,
+ al_a4_sit_dependents=dependent,
+ 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('AL', self.AL_UNEMP, date(2020, 1, 1), wage_base=self.AL_UNEMP_MAX_WAGE)
+ self._test_sit(10000.0, 'S', False, 0.0, 1.0, 'weekly', date(2020, 1, 1), 349.08)
+ self._test_sit(850.0, 'M', False, 0.0, 2.0, 'weekly', date(2020, 1, 1), 29.98)
+ self._test_sit(5000.0, 'H', False, 0.0, 2.0, 'bi-weekly', date(2020, 1, 1), 191.15)
+ self._test_sit(20000.0, 'MS', False, 2.0, 0, 'monthly', date(2020, 1, 1), 757.6)
+ self._test_sit(5500.0, '', True, 2.0, 150, 'weekly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py
new file mode 100644
index 00000000..73b0f59c
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py
@@ -0,0 +1,72 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsARPayslip(TestUsPayslip):
+ # https://www.dfa.arkansas.gov/images/uploads/incomeTaxOffice/whformula.pdf Calculation based on this file.
+ AR_UNEMP_MAX_WAGE = 10000.00
+ AR_UNEMP = -3.2 / 100.0
+ AR_INC_TAX = -0.0535
+
+ def test_taxes_monthly(self):
+ salary = 2127.0
+ schedule_pay = 'monthly'
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AR'),
+ state_income_tax_additional_withholding=0.0,
+ ar_ar4ec_sit_allowances=2.0,
+ state_income_tax_exempt=False,
+ schedule_pay='monthly')
+
+ self._log('2019 Arkansas tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ # Not exempt from rule 1 or rule 2 - unemployment wages., and actual unemployment.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AR_UNEMP)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+ remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - salary if (self.AR_UNEMP_MAX_WAGE - 2*salary < salary) else salary
+ # We reached the cap of 10000.0 in the first payslip.
+ self._log('2019 Arkansas 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_AR_UNEMP_wages * self.AR_UNEMP)
+
+ def test_additional_withholding(self):
+ salary = 5000.0
+ schedule_pay = 'monthly'
+ pay_periods = 12
+ allowances = 2
+ # TODO: comment on how it was calculated
+ test_ar_amt = 2598.60
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AR'),
+ state_income_tax_additional_withholding=100.0,
+ ar_ar4ec_sit_allowances=2.0,
+ state_income_tax_exempt=False,
+ schedule_pay='monthly')
+
+
+ self._log('2019 Arkansas 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.AR_UNEMP)
+ # TODO: change to hand the test_ar_amt already be divided by pay periods
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], -round(test_ar_amt / pay_periods) - 100)
+
+ process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2020.py
new file mode 100644
index 00000000..6afe3d4d
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_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 TestUsARPayslip(TestUsPayslip):
+ # Taxes and Rates
+ AR_UNEMP_MAX_WAGE = 8000.0
+ AR_UNEMP = 2.9
+
+ def _test_sit(self, wage, exemptions, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('AR'),
+ state_income_tax_exempt=exemptions,
+ state_income_tax_additional_withholding=additional_withholding,
+ ar_ar4ec_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('AR', self.AR_UNEMP, date(2020, 1, 1), wage_base=self.AR_UNEMP_MAX_WAGE)
+ self._test_sit(5000.0, True, 0.0, 0, 'monthly', date(2020, 1, 1), 0.0)
+ self._test_sit(5000.0, False, 0.0, 0, 'monthly', date(2020, 1, 1), 221.0)
+ self._test_sit(700.0, False, 0.0, 150, 'weekly', date(2020, 1, 1), 175.0)
+ self._test_sit(7000.0, False, 2.0, 0, 'semi-monthly', date(2020, 1, 1), 420.0)
+ self._test_sit(3000.0, False, 1.0, 0, 'bi-weekly', date(2020, 1, 1), 142.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py
new file mode 100644
index 00000000..b97063b6
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py
@@ -0,0 +1,72 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsAZPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ AZ_UNEMP_MAX_WAGE = 7000.00
+ AZ_UNEMP = -(2.00 / 100.0)
+
+ def test_taxes_with_additional_wh(self):
+ salary = 15000.00
+ schedule_pay = 'weekly'
+ withholding_percentage = 5.1
+ percent_wh = (5.10 / 100) # 5.1%
+ additional_wh = 12.50
+
+ wh_to_test = -((percent_wh * salary) + additional_wh)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AZ'),
+ state_income_tax_additional_withholding=12.50,
+ az_a4_sit_withholding_percentage=withholding_percentage,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Arizona 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'], self.AZ_UNEMP_MAX_WAGE * self.AZ_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
+
+ process_payslip(payslip)
+
+ remaining_AZ_UNEMP_wages = 0.0 # We already reached max unemployment wages.
+
+ self._log('2019 Arizona 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_AZ_UNEMP_wages * self.AZ_UNEMP)
+
+ def test_taxes_monthly(self):
+ salary = 1000.00
+ schedule_pay = 'monthly'
+ withholding_percentage = 2.7
+ percent_wh = (2.70 / 100) # 2.7%
+ additional_wh = 0.0
+ wh_to_test = -((percent_wh * salary) + additional_wh)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AZ'),
+ state_income_tax_additional_withholding=0.0,
+ az_a4_sit_withholding_percentage=withholding_percentage,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Arizona 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.AZ_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
+
+ process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py
new file mode 100644
index 00000000..248648bc
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py
@@ -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 TestUsAZPayslip(TestUsPayslip):
+ # Taxes and Rates
+ AZ_UNEMP_MAX_WAGE = 7000.0
+ AZ_UNEMP = 2.0
+
+ def _test_sit(self, wage, additional_withholding, withholding_percent, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('AZ'),
+ state_income_tax_additional_withholding=additional_withholding,
+ az_a4_sit_withholding_percentage=withholding_percent,
+ 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('AZ', self.AZ_UNEMP, date(2020, 1, 1), wage_base=self.AZ_UNEMP_MAX_WAGE)
+ self._test_sit(1000.0, 0.0, 2.70, 'monthly', date(2020, 1, 1), 27.0)
+ self._test_sit(1000.0, 10.0, 2.70, 'monthly', date(2020, 1, 1), 37.0)
+ self._test_sit(15000.0, 0.0, 3.60, 'weekly', date(2020, 1, 1), 540.0)
+ self._test_sit(8000.0, 0.0, 4.20, 'semi-monthly', date(2020, 1, 1), 336.0)
+ self._test_sit(8000.0, 0.0, 0.00, 'semi-monthly', date(2020, 1, 1), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
new file mode 100644
index 00000000..b9331fe3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
@@ -0,0 +1,245 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsCAPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ CA_MAX_WAGE = 7000
+ CA_UIT = -3.5 / 100.0
+ CA_ETT = -0.1 / 100.0
+ CA_SDI = -1.0 / 100.0
+
+ # Examples from https://www.edd.ca.gov/pdf_pub_ctr/20methb.pdf
+ def test_example_a(self):
+ salary = 210
+ schedule_pay = 'weekly'
+ allowances = 1
+ additional_allowances = 0
+
+ wh = 0.00
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='single',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ 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.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ def test_example_b(self):
+ salary = 1250
+ schedule_pay = 'bi-weekly'
+ allowances = 2
+ additional_allowances = 1
+
+ # Example B
+ subject_to_withholding = salary - 38
+ taxable_income = subject_to_withholding - 339
+ computed_tax = (taxable_income - 632) * 0.022 + 6.95 # 6.95 Marginal Amount
+ wh = computed_tax - 9.65 # two exemption allowances
+ wh = -wh
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ 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.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+
+ def test_example_c(self):
+ salary = 4100
+ schedule_pay = 'monthly'
+ allowances = 5
+ additional_allowances = 0.0
+
+ wh = -9.3
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ 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.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 California 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'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
+
+ def test_example_d(self):
+ salary = 800
+ schedule_pay = 'weekly'
+ allowances = 3
+ additional_allowances = 0
+
+ wh = -3.18
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='head_household',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ 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.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 California 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'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
+
+ def test_example_e(self):
+ salary = 1800
+ schedule_pay = 'semi-monthly'
+ allowances = 4
+ additional_allowances = 0
+
+ wh = -3.08
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ 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.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 California 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'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
+
+ def test_example_f(self):
+ salary = 45000
+ schedule_pay = 'annually'
+ allowances = 4
+ additional_allowances = 0
+
+ wh = -113.85
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.CA_MAX_WAGE * (self.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], self.CA_MAX_WAGE * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2020.py
new file mode 100755
index 00000000..264b115e
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2020.py
@@ -0,0 +1,43 @@
+# 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 TestUsCAPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ CA_UNEMP_MAX_WAGE = 7000.0 # Note that this is used for SDI and FLI as well
+ CA_UIT = 3.4
+ CA_ETT = 0.1
+ CA_SDI = 1.0
+
+ def _test_sit(self, wage, filing_status, allowances, additional_allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status=filing_status,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding if filing_status else 0.0)
+
+ def test_2020_taxes_example1(self):
+ combined_er_rate = self.CA_UIT + self.CA_ETT
+ self._test_er_suta('CA', combined_er_rate, date(2020, 1, 1), wage_base=self.CA_UNEMP_MAX_WAGE)
+ self._test_ee_suta('CA', self.CA_SDI, date(2020, 1, 1), wage_base=self.CA_UNEMP_MAX_WAGE, relaxed=True)
+ # these expected values come from https://www.edd.ca.gov/pdf_pub_ctr/20methb.pdf
+ self._test_sit(210.0, 'single', 1, 0, 0, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(1250.0, 'married', 2, 1, 0, 'bi-weekly', date(2020, 1, 1), 1.23)
+ self._test_sit(4100.0, 'married', 5, 0, 0, 'monthly', date(2020, 1, 1), 1.5)
+ self._test_sit(800.0, 'head_household', 3, 0, 0, 'weekly', date(2020, 1, 1), 2.28)
+ self._test_sit(1800.0, 'married', 4, 0, 0, 'semi-monthly', date(2020, 1, 1), 0.84)
+ self._test_sit(45000.0, 'married', 4, 0, 0, 'annually', date(2020, 1, 1), 59.78)
+ self._test_sit(45000.0, 'married', 4, 0, 20.0, 'annually', date(2020, 1, 1), 79.78)
+ self._test_sit(6000.0, '', 4, 0, 20.0, 'annually', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py
new file mode 100755
index 00000000..0fa45d9c
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsCOPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ CO_UNEMP_MAX_WAGE = 13600.0
+ CO_UNEMP = 1.7
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding, state_income_tax_exempt=False):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('CO'),
+ fed_941_fit_w4_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=state_income_tax_exempt,
+ 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('CO', self.CO_UNEMP, date(2020, 1, 1), wage_base=self.CO_UNEMP_MAX_WAGE)
+ self._test_sit(5000.0, 'married', 0.0, 'semi-monthly', date(2020, 1, 1), 216.07)
+ self._test_sit(800.0, 'single', 0.0, 'weekly', date(2020, 1, 1), 33.48)
+ self._test_sit(20000.0, 'married', 0.0, 'quarterly', date(2020, 1, 1), 833.4)
+ self._test_sit(20000.0, 'married', 10.0, 'quarterly', date(2020, 1, 1), 843.4)
+ self._test_sit(20000.0, 'married', 0.0, 'quarterly', date(2020, 1, 1), 0.0, True)
+ self._test_sit(800.0, '', 0.0, 'weekly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
new file mode 100644
index 00000000..ab423131
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
@@ -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)
diff --git a/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2020.py
new file mode 100644
index 00000000..8ce41d06
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_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 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)
+ self._test_sit(500.0, 'd', 0.0, 'weekly', date(2020, 1, 1), 21.15)
diff --git a/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py
new file mode 100755
index 00000000..ed285368
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsDEPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ DE_UNEMP_MAX_WAGE = 16500.0
+ DE_UNEMP = 1.50
+ # Calculation based on section 17. https://revenue.delaware.gov/employers-guide-withholding-regulations-employers-duties/
+
+ def _test_sit(self, wage, filing_status, additional_withholding, dependents, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('DE'),
+ de_w4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ de_w4_sit_dependent=dependents,
+ 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('DE', self.DE_UNEMP, date(2020, 1, 1), wage_base=self.DE_UNEMP_MAX_WAGE)
+ self._test_sit(480.77, 'single', 0.0, 1.0, 'weekly', date(2020, 1, 1), 13.88)
+ self._test_sit(5000.0, 'single', 0.0, 2.0, 'monthly', date(2020, 1, 1), 211.93)
+ self._test_sit(5000.0, 'single', 10.0, 1.0, 'monthly', date(2020, 1, 1), 231.1)
+ self._test_sit(20000.0, 'married', 0.0, 3.0, 'quarterly', date(2020, 1, 1), 876.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
new file mode 100755
index 00000000..419be377
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
@@ -0,0 +1,84 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
+
+
+class TestUsFlPayslip(TestUsPayslip):
+ ###
+ # 2019 Taxes and Rates
+ ###
+ FL_UNEMP_MAX_WAGE = 7000.0
+ FL_UNEMP = -2.7 / 100.0
+
+ def test_2019_taxes(self):
+ salary = 5000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('FL'))
+
+ self._log('2019 Florida tax first payslip:')
+ 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.FL_UNEMP)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_fl_unemp_wages = self.FL_UNEMP_MAX_WAGE - salary if (self.FL_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Florida 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_fl_unemp_wages * self.FL_UNEMP)
+
+ def test_2019_taxes_with_external(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ external_wages=external_wages,
+ state_id=self.get_us_state('FL'))
+
+ self._log('2019 Forida_external tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], (self.FL_UNEMP_MAX_WAGE - external_wages) * self.FL_UNEMP)
+
+ def test_2019_taxes_with_state_exempt(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ external_wages=external_wages,
+ futa_type=USHRContract.FUTA_TYPE_BASIC,
+ state_id=self.get_us_state('FL'))
+
+ self._log('2019 Forida_external tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats.get('ER_US_SUTA', 0.0), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2020.py
new file mode 100755
index 00000000..5952eb1f
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2020.py
@@ -0,0 +1,16 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsFlPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ FL_UNEMP_MAX_WAGE = 7000.0
+ FL_UNEMP = 2.7
+
+ def test_2020_taxes(self):
+ # Only has state unemployment
+ self._test_er_suta('FL', self.FL_UNEMP, date(2020, 1, 1), wage_base=self.FL_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
new file mode 100755
index 00000000..98206965
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
@@ -0,0 +1,135 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsGAPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ GA_UNEMP_MAX_WAGE = 9500.00
+ GA_UNEMP = -(2.70 / 100.0)
+
+ def test_taxes_weekly_single_with_additional_wh(self):
+ salary = 15000.00
+ schedule_pay = 'weekly'
+ allowances = 1
+ filing_status = 'single'
+ additional_wh = 12.50
+ # Hand Calculated Amount to Test
+ # Step 1 - Subtract standard deduction from wages. Std Deduct for single weekly is 88.50
+ # step1 = 15000.00 - 88.50 = 14911.5
+ # Step 2 - Subtract personal allowance from step1. Allowance for single weekly is 51.92
+ # step2 = step1 - 51.92 = 14859.58
+ # Step 3 - Subtract amount for dependents. Weekly dependent allowance is 57.50
+ # step3 = 14859.58 - 57.50 = 14802.08
+ # Step 4 -Determine wh amount from tables
+ # step4 = 4.42 + ((5.75 / 100.00) * (14802.08 - 135.00))
+ # Add additional_wh
+ # wh = 847.7771 + 12.50 = 860.2771
+ wh = -860.28
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('GA'),
+ ga_g4_sit_dependent_allowances=allowances,
+ ga_g4_sit_additional_allowances=0,
+ ga_g4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_wh,
+ schedule_pay=schedule_pay)
+
+ self.assertEqual(contract.schedule_pay, 'weekly')
+
+ self._log('2019 Georgia 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'], self.GA_UNEMP_MAX_WAGE * self.GA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ remaining_GA_UNEMP_wages = 0.0 # We already reached max unemployment wages.
+
+ self._log('2019 Georgia 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_GA_UNEMP_wages * self.GA_UNEMP)
+
+
+ def test_taxes_monthly_head_of_household(self):
+ salary = 25000.00
+ schedule_pay = 'monthly'
+ allowances = 2
+ filing_status = 'head of household'
+ additional_wh = 15.00
+ # Hand Calculated Amount to Test
+ # Step 1 - Subtract standard deduction from wages. Std Deduct for head of household monthly is 383.50
+ # step1 = 25000.00 - 383.50 = 24616.5
+ # Step 2 - Subtract personal allowance from step1. Allowance for head of household monthly is 225.00
+ # step2 = 24616.5 - 225.00 = 24391.5
+ # Step 3 - Subtract amount for dependents. Weekly dependent allowance is 250.00
+ # step3 = 24391.5 - (2 * 250.00) = 23891.5
+ # Step 4 - Determine wh amount from tables
+ # step4 = 28.33 + ((5.75 / 100.00) * (23891.5 - 833.00)) = 1354.19375
+ # Add additional_wh
+ # wh = 1354.19375 + 15.00 = 1369.19375
+ wh = -1369.19
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('GA'),
+ ga_g4_sit_dependent_allowances=allowances,
+ ga_g4_sit_additional_allowances=0,
+ ga_g4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_wh,
+ schedule_pay=schedule_pay)
+
+ self.assertEqual(contract.schedule_pay, 'monthly')
+
+ self._log('2019 Georgia 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.GA_UNEMP_MAX_WAGE * self.GA_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ remaining_GA_UNEMP_wages = 0.0 # We already reached max unemployment wages.
+
+ self._log('2019 Georgia 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_GA_UNEMP_wages * self.GA_UNEMP)
+
+ def test_taxes_exempt(self):
+ salary = 25000.00
+ schedule_pay = 'monthly'
+ allowances = 2
+ filing_status = ''
+ additional_wh = 15.00
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('GA'),
+ ga_g4_sit_dependent_allowances=allowances,
+ ga_g4_sit_additional_allowances=0,
+ ga_g4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_wh,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Georgia tax first payslip exempt:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0), 0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
new file mode 100755
index 00000000..21a0a810
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
@@ -0,0 +1,39 @@
+# 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 TestUsGAPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ GA_UNEMP_MAX_WAGE = 9500.00
+ GA_UNEMP = 2.70
+
+ # Example calculated based on https://dor.georgia.gov/employers-tax-guide 2020_employer tax gauide
+
+ def _test_sit(self, wage, filing_status, additional_withholding, dependent_allowances, additional_allowances,
+ schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('GA'),
+ ga_g4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ga_g4_sit_dependent_allowances=dependent_allowances,
+ ga_g4_sit_additional_allowances=additional_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_2020_taxes_example(self):
+ self._test_er_suta('GA', self.GA_UNEMP, date(2020, 1, 1), wage_base=self.GA_UNEMP_MAX_WAGE)
+ self._test_sit(15000.0, 'single', 12.50, 1, 0, 'weekly', date(2020, 1, 1), 860.28)
+ self._test_sit(25000.0, 'head of household', 15.00, 2, 0, 'monthly', date(2020, 1, 1), 1369.19)
+ self._test_sit(425.0, 'married filing separate', 0.0, 1, 0, 'weekly', date(2020, 1, 1), 11.45)
+ self._test_sit(3000.0, 'single', 0.00, 1, 1, 'quarterly', date(2020, 1, 1), 0.0)
+ self._test_sit(2500.0, '', 0.00, 1, 1, 'quarterly', date(2020, 1, 1), 0.0)
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..9a746057
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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)
+ self._test_sit(750.0, 'single', 10.0, 3.0, 'bi-weekly', date(2020, 1, 1), 40.59)
+ self._test_sit(3000.0, '', 0.0, 3.0, 'weekly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py
new file mode 100644
index 00000000..cb3bccfd
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py
@@ -0,0 +1,152 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsIAPayslip(TestUsPayslip):
+ IA_UNEMP_MAX_WAGE = 30600
+ IA_UNEMP = -1.0 / 100.0
+ IA_INC_TAX = -0.0535
+
+ def test_taxes_weekly(self):
+ wages = 30000.00
+ schedule_pay = 'weekly'
+ allowances = 1
+ additional_wh = 0.00
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wages,
+ state_id=self.get_us_state('IA'),
+ state_income_tax_additional_withholding=additional_wh,
+ ia_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Iowa tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
+ # withholding amount because it is calculated in the base US payroll module as a negative
+ # t1 = 30000 - (10399.66) = 19600.34
+ t1_to_test = wages + cats['EE_US_941_FIT']
+ self.assertPayrollAlmostEqual(t1_to_test, 19600.34)
+
+ # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
+ # In our case, we have a weekly period which on the table has a std deduct. of $32.50 for 0 or 1 allowances,
+ # and 80.00 of 2 or more allowances.
+ standard_deduction = 32.50 # The allowance tells us what standard_deduction amount to use.
+ # t2 = 19600.34 - 32.50 = 19567.84
+ t2_to_test = t1_to_test - standard_deduction
+ self.assertPayrollAlmostEqual(t2_to_test, 19567.84)
+ # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
+ # 1153.38 is the bracket floor. 8.53 is the rate, and 67.63 is the flat fee.
+ # t3 = 1638.38
+ t3_to_test = ((t2_to_test - 1153.38) * (8.53 / 100)) + 67.63
+ self.assertPayrollAlmostEqual(t3_to_test, 1638.38)
+ # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
+ # deduction amount per allowance is 0.77
+ # t4 = 1638.38 - 0.77 = 155.03
+ t4_to_test = t3_to_test - (0.77 * allowances)
+ self.assertPayrollAlmostEqual(t4_to_test, 1637.61)
+ # t5 is our T4 plus the additional withholding per period
+ # t5 = 1637.61 + 0.0
+ # Convert to negative as well.
+ t5_to_test = -t4_to_test - additional_wh
+ self.assertPayrollAlmostEqual(t5_to_test, -1637.61)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], wages * self.IA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], t5_to_test)
+
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_IA_UNEMP_wages = self.IA_UNEMP_MAX_WAGE - wages if (self.IA_UNEMP_MAX_WAGE - 2*wages < wages) \
+ else wages
+
+ self._log('2019 Iowa 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'], wages * self.IA_UNEMP)
+
+ def test_taxes_biweekly(self):
+ wages = 3000.00
+ schedule_pay = 'bi-weekly'
+ allowances = 1
+ additional_wh = 0.00
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wages,
+ state_id=self.get_us_state('IA'),
+ state_income_tax_additional_withholding=additional_wh,
+ ia_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Iowa tax first payslip bi-weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
+ # withholding amount because it is calculated in the base US payroll module as a negative
+ t1_to_test = wages + cats['EE_US_941_FIT']
+ # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
+ # In our case, we have a biweekly period which on the table has a std deduct. of $65.00 for 0 or 1 allowances,
+ # and $160.00 of 2 or more allowances.
+ standard_deduction = 65.00 # The allowance tells us what standard_deduction amount to use.
+ t2_to_test = t1_to_test - standard_deduction
+ # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
+ t3_to_test = ((t2_to_test - 2306.77) * (8.53 / 100)) + 135.28
+ # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
+ # deduction amount per allowance is 0.77
+ t4_to_test = t3_to_test - (1.54 * allowances)
+ # t5 is our T4 plus the additional withholding per period
+ t5_to_test = -t4_to_test - additional_wh
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], wages * self.IA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], t5_to_test - additional_wh)
+
+ process_payslip(payslip)
+
+ def test_taxes_with_external_weekly(self):
+ wages = 2500.00
+ schedule_pay = 'weekly'
+ allowances = 1
+ additional_wh = 0.00
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wages,
+ state_id=self.get_us_state('IA'),
+ state_income_tax_additional_withholding=additional_wh,
+ ia_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Iowa external tax first payslip external weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+
+ # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
+ # withholding amount because it is calculated in the base US payroll module as a negative
+ t1_to_test = wages + cats['EE_US_941_FIT']
+ # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
+ # In our case, we have a weekly period which on the table has a std deduct. of $32.50 for 0 or 1 allowances,
+ # and 80.00 of 2 or more allowances.
+ standard_deduction = 32.50 # The allowance tells us what standard_deduction amount to use.
+ t2_to_test = t1_to_test - standard_deduction
+ # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
+ t3_to_test = ((t2_to_test - 1153.38) * (8.53 / 100)) + 67.63
+ # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
+ # deduction amount per allowance is 0.77
+ t4_to_test = t3_to_test - (0.77 * allowances)
+ # t5 is our T4 plus the additional withholding per period
+ t5_to_test = -t4_to_test - additional_wh
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], wages * self.IA_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], t5_to_test)
+
+ process_payslip(payslip)
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py
new file mode 100755
index 00000000..eaca0e71
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsIAPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ IA_UNEMP_MAX_WAGE = 31600.00
+ IA_UNEMP = 1.0
+
+ def _test_sit(self, wage, exempt, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('IA'),
+ state_income_tax_exempt=exempt,
+ state_income_tax_additional_withholding=additional_withholding,
+ ia_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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('IA', self.IA_UNEMP, date(2020, 1, 1), wage_base=self.IA_UNEMP_MAX_WAGE)
+ self._test_sit(2100.0, False, 0.0, 3.0, 'bi-weekly', date(2020, 1, 1), 83.5)
+ self._test_sit(3000.0, True, 10.0, 1.0, 'bi-weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(300.0, False, 0.0, 1.0, 'weekly', date(2020, 1, 1), 6.77)
+ self._test_sit(5000.0, False, 0.0, 1.0, 'monthly', date(2020, 1, 1), 230.76)
+ self._test_sit(7500.0, False, 10.0, 2.0, 'semi-monthly', date(2020, 1, 1), 432.84)
diff --git a/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py
new file mode 100644
index 00000000..8e3576d6
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py
@@ -0,0 +1,85 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsIDPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ ID_UNEMP_MAX_WAGE = 40000.00
+ ID_UNEMP = -(1.00 / 100.0)
+
+ def test_taxes_single_biweekly(self):
+ salary = 1212.00
+ schedule_pay = 'bi-weekly'
+ filing_status = 'single'
+ allowances = 4
+ # SEE https://tax.idaho.gov/i-1026.cfm?seg=compute for example calculations
+ wh_to_check = -10.00
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('ID'),
+ id_w4_sit_filing_status=filing_status,
+ id_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Idaho 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.ID_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
+
+ process_payslip(payslip)
+
+ remaining_id_unemp_wages = self.ID_UNEMP_MAX_WAGE - salary if (self.ID_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Idaho 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.ID_UNEMP)
+
+ def test_taxes_married_monthly(self):
+ salary = 5000.00
+ schedule_pay = 'monthly'
+ filing_status = 'married'
+ allowances = 2
+
+ # ICTCAT says monthly allowances are 246.67
+ # we have 2 so 246.67 * 2 = 493.34
+ # 5000.00 - 493.34 = 4506.66
+ # Wh is 89$ plus 6.925% over 3959,00
+ # 126.92545499999999 - > 127.0
+ wh_to_check = -127.0
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('ID'),
+ id_w4_sit_filing_status=filing_status,
+ id_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Idaho 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.ID_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
+
+ process_payslip(payslip)
+
+ remaining_id_unemp_wages = self.ID_UNEMP_MAX_WAGE - salary if (self.ID_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Idaho 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.ID_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2020.py
new file mode 100755
index 00000000..eb0da9a7
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_id_idaho_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 TestUsIDPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ ID_UNEMP_MAX_WAGE = 41600.00
+ ID_UNEMP = 1.0
+
+ def _test_sit(self, wage, filing_status, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('ID'),
+ id_w4_sit_filing_status=filing_status,
+ id_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)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('ID', self.ID_UNEMP, date(2020, 1, 1), wage_base=self.ID_UNEMP_MAX_WAGE)
+ self._test_sit(1212.0, 'single', 4.0, 'bi-weekly', date(2020, 1, 1), 10.0)
+ self._test_sit(10000.0, 'married', 1.0, 'annually', date(2020, 1, 1), 0.0)
+ self._test_sit(52000.0, 'married', 4.0, 'monthly', date(2020, 1, 1), 3345.0)
+ self._test_sit(5000.0, 'head of household', 0.0, 'semi-monthly', date(2020, 1, 1), 300.0)
+ self._test_sit(5900.0, 'single', 5.0, 'weekly', date(2020, 1, 1), 367.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py
new file mode 100644
index 00000000..ba633607
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py
@@ -0,0 +1,71 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsILPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ IL_UNEMP_MAX_WAGE = 12960.00
+ IL_UNEMP = -(3.175 / 100.0)
+
+ def test_taxes_monthly(self):
+ salary = 15000.00
+ schedule_pay = 'monthly'
+ basic_allowances = 1
+ additional_allowances = 1
+ flat_rate = (4.95 / 100)
+ wh_to_test = -(flat_rate * (salary - ((basic_allowances * 2275 + additional_allowances * 1000) / 12.0)))
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('IL'),
+ state_income_tax_additional_withholding=0.0,
+ il_w4_sit_basic_allowances=1.0,
+ il_w4_sit_additional_allowances=1.0,
+ schedule_pay='monthly')
+
+ self._log('2019 Illinois 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.IL_UNEMP_MAX_WAGE * self.IL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
+
+ process_payslip(payslip)
+
+ remaining_IL_UNEMP_wages = 0.0 # We already reached max unemployment wages.
+
+ self._log('2019 Illinois 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_IL_UNEMP_wages * self.IL_UNEMP)
+
+ def test_taxes_with_additional_wh(self):
+ salary = 15000.00
+ schedule_pay = 'monthly'
+ basic_allowances = 1
+ additional_allowances = 1
+ additional_wh = 15.0
+ flat_rate = (4.95 / 100)
+ wh_to_test = -(flat_rate * (salary - ((basic_allowances * 2275 + additional_allowances * 1000) / 12.0)) + additional_wh)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('IL'),
+ state_income_tax_additional_withholding=15.0,
+ il_w4_sit_basic_allowances=1.0,
+ il_w4_sit_additional_allowances=1.0,
+ schedule_pay='monthly')
+
+ self._log('2019 Illinois 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.IL_UNEMP_MAX_WAGE * self.IL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
diff --git a/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2020.py
new file mode 100644
index 00000000..ead932e4
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsILPayslip(TestUsPayslip):
+ # Taxes and Rates
+ IL_UNEMP_MAX_WAGE = 12740.0
+ IL_UNEMP = 3.125
+
+ def _test_sit(self, wage, additional_withholding, basic_allowances, additional_allowances, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('IL'),
+ state_income_tax_additional_withholding=additional_withholding,
+ il_w4_sit_basic_allowances=basic_allowances,
+ il_w4_sit_additional_allowances=additional_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('IL', self.IL_UNEMP, date(2020, 1, 1), wage_base=self.IL_UNEMP_MAX_WAGE)
+ self._test_sit(800.0, 0.0, 2, 2, 'weekly', date(2020, 1, 1), 33.27)
+ self._test_sit(800.0, 10.0, 2, 2, 'weekly', date(2020, 1, 1), 43.27)
+ self._test_sit(2500.0, 0.0, 1, 1, 'monthly', date(2020, 1, 1), 110.04)
+ self._test_sit(2500.0, 0.0, 0, 0, 'monthly', date(2020, 1, 1), 123.75)
+ self._test_sit(3000.0, 15.0, 0, 0, 'quarterly', date(2020, 1, 1), 163.50)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2020.py
new file mode 100755
index 00000000..53b7ddf3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsINPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ IN_UNEMP_MAX_WAGE = 9500.0
+ IN_UNEMP = 2.5
+ # Calculation based on https://www.in.gov/dor/files/dn01.pdf
+
+ def _test_sit(self, wage, additional_withholding, personal_exemption, dependent_exemption, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('IN'),
+ state_income_tax_additional_withholding=additional_withholding,
+ in_w4_sit_personal_exemption=personal_exemption,
+ in_w4_sit_dependent_exemption=dependent_exemption,
+ 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('IN', self.IN_UNEMP, date(2020, 1, 1), wage_base=self.IN_UNEMP_MAX_WAGE)
+ self._test_sit(800.0, 0.0, 5.0, 3.0, 'weekly', date(2020, 1, 1), 19.94)
+ self._test_sit(800.0, 10.0, 5.0, 3.0, 'weekly', date(2020, 1, 1), 29.94)
+ self._test_sit(9000.0, 0.0, 4.0, 3.0, 'monthly', date(2020, 1, 1), 267.82)
+ self._test_sit(10000.0, 0.0, 2.0, 2.0, 'bi-weekly', date(2020, 1, 1), 316.79)
diff --git a/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2020.py
new file mode 100755
index 00000000..3ed586f8
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsKSPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ KS_UNEMP_MAX_WAGE = 14000.0
+ KS_UNEMP = 2.7
+ # Calculation based on example https://revenue.ky.gov/Forms/42A003(T)%20(12-2019)%202020%20Tax%20Tables.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('KS'),
+ ks_k4_sit_filing_status=filing_status,
+ ks_k4_sit_allowances=allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('KS', self.KS_UNEMP, date(2020, 1, 1), wage_base=self.KS_UNEMP_MAX_WAGE)
+ self._test_sit(6250, 'married', 2, 0, 'semi-monthly', date(2020, 1, 1), 290.00)
+ self._test_sit(5000, 'single', 1, 0, 'monthly', date(2020, 1, 1), 222.00)
+ self._test_sit(1500, 'married', 0, 0, 'bi-weekly', date(2020, 1, 1), 39.00)
+ self._test_sit(750, 'single', 2, 10, 'weekly', date(2020, 1, 1), 36.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2020.py
new file mode 100755
index 00000000..aa067848
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ky_kentucky_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 TestUsKYPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ KY_UNEMP_MAX_WAGE = 10800.0
+ KY_UNEMP = 2.7
+ # Calculation based on example https://revenue.ky.gov/Forms/42A003(T)%20(12-2019)%202020%20Tax%20Tables.pdf
+
+ def _test_sit(self, wage, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('KY'),
+ 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('KY', self.KY_UNEMP, date(2020, 1, 1), wage_base=self.KY_UNEMP_MAX_WAGE)
+ self._test_sit(3020, 0.0, 'monthly', date(2020, 1, 1), 139.96)
+ self._test_sit(1500, 0.0, 'bi-weekly', date(2020, 1, 1), 69.90)
+ self._test_sit(1500, 10.0, 'bi-weekly', date(2020, 1, 1), 79.90)
+ self._test_sit(750, 00.0, 'weekly', date(2020, 1, 1), 34.95)
+ self._test_sit(7000, 0.0, 'semi-monthly', date(2020, 1, 1), 344.48)
diff --git a/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
new file mode 100644
index 00000000..1d01c618
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
@@ -0,0 +1,91 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsLAPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ LA_UNEMP_MAX_WAGE = 7700.00
+ LA_UNEMP = -(1.14 / 100.0)
+
+ def test_taxes_single_weekly(self):
+ salary = 700.00
+ schedule_pay = 'weekly'
+ filing_status = 'single'
+ exemptions = 1
+ dependents = 2
+ additional_withholding = 0
+ # SEE http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf for example calculations
+ # wh_to test is 19.42
+ # Our algorithm correctly rounds whereas theirs does it prematurely.
+ wh_to_check = -19.43
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('LA'),
+ la_l4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ la_l4_sit_exemptions=exemptions,
+ la_l4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Louisiana 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.LA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
+
+ process_payslip(payslip)
+
+ remaining_la_unemp_wages = self.LA_UNEMP_MAX_WAGE - salary if (self.LA_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Louisiana 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_la_unemp_wages * self.LA_UNEMP)
+
+ def test_taxes_married_biweekly(self):
+ salary = 4600.00
+ schedule_pay = 'bi-weekly'
+ filing_status = 'married'
+ exemptions = 2
+ dependents = 3
+ additional_withholding = 0
+ # SEE http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf for example calculations
+ # wh_to test is 157.12
+ wh_to_check = -157.12
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('LA'),
+ la_l4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ la_l4_sit_exemptions=exemptions,
+ la_l4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Louisiana tax first payslip bi-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.LA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
+
+ process_payslip(payslip)
+
+ remaining_la_unemp_wages = self.LA_UNEMP_MAX_WAGE - salary if (self.LA_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Louisiana tax second payslip bi-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_la_unemp_wages * self.LA_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2020.py
new file mode 100755
index 00000000..d23c3ab3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsLAPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ LA_UNEMP_MAX_WAGE = 7700.0
+ LA_UNEMP = 1.14
+ # Calculation based on http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf
+
+ def _test_sit(self, wage, filing_status, additional_withholding, exemptions, dependents, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('LA'),
+ la_l4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ la_l4_sit_exemptions=exemptions,
+ la_l4_sit_dependents=dependents,
+ 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_2020_taxes_example(self):
+ self._test_er_suta('LA', self.LA_UNEMP, date(2020, 1, 1), wage_base=self.LA_UNEMP_MAX_WAGE)
+ self._test_sit(700.0, 'single', 0.0, 1.0, 2.0, 'weekly', date(2020, 1, 1), 19.43)
+ self._test_sit(4600.0, 'married', 0.0, 2.0, 3.0, 'bi-weekly', date(2020, 1, 1), 157.12)
+ self._test_sit(6000.0, 'single', 10.0, 2.0, 3.0, 'monthly', date(2020, 1, 1), 219.08)
diff --git a/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py
new file mode 100644
index 00000000..165455ce
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py
@@ -0,0 +1,39 @@
+# 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):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ ME_UNEMP_MAX_WAGE = 12000.0
+ ME_UNEMP = 1.92
+ # Calculation based on this file page.6 and 7 https://www.maine.gov/revenue/forms/with/2020/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_2020_taxes_example(self):
+ self._test_er_suta('ME', self.ME_UNEMP, date(2020, 1, 1), wage_base=self.ME_UNEMP_MAX_WAGE)
+ self._test_sit(300.0, 'single', 0.0, False, 2, 'weekly', date(2020, 1, 1), 0.0)
+ self._test_sit(800.0, 'single', 0.0, False, 2, 'bi-weekly', date(2020, 1, 1), 6.00)
+ self._test_sit(4500.0, 'married', 0.0, True, 0, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(4500.0, 'married', 0.0, False, 2, 'monthly', date(2020, 1, 1), 113.00)
+ self._test_sit(4500.0, 'married', 10.0, False, 2, 'weekly', date(2020, 1, 1), 287.00)
+ self._test_sit(7000.0, '', 10.0, False, 2, 'weekly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py
new file mode 100755
index 00000000..b12baed2
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py
@@ -0,0 +1,194 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsMIPayslip(TestUsPayslip):
+ # Taxes and Rates
+ MI_UNEMP_MAX_WAGE = 9500.0
+ MI_UNEMP = - 2.7 / 100.0
+ MI_INC_TAX = - 4.25 / 100.0
+ ANNUAL_EXEMPTION_AMOUNT = 4400.00
+ PAY_PERIOD_DIVISOR = {
+ 'weekly': 52.0,
+ 'bi-weekly': 26.0,
+ 'semi-monthly': 24.0,
+ 'monthly': 12.0
+ }
+
+ def test_2019_taxes_weekly(self):
+ salary = 5000.0
+ schedule_pay = 'weekly'
+ exemptions = 1
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MI'),
+ state_income_tax_additional_withholding=0.0,
+ mi_w4_sit_exemptions=1.0,
+ schedule_pay='weekly')
+
+ allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
+ wh = -((salary - (allowance_amount * exemptions)) * -self.MI_INC_TAX)
+
+ self._log('2019 Michigan 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.MI_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
+ #
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+ remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (self.MI_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Michigan 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_MI_UNEMP_wages * self.MI_UNEMP)
+
+ def test_2019_taxes_biweekly(self):
+ salary = 5000.0
+ schedule_pay = 'bi-weekly'
+ allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
+ exemption = 2
+
+ wh = -((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MI'),
+ state_income_tax_additional_withholding=0.0,
+ mi_w4_sit_exemptions=2.0,
+ schedule_pay='bi-weekly')
+
+ self._log('2019 Michigan tax first payslip bi-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.MI_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+ remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (self.MI_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Michigan tax second payslip bi-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_MI_UNEMP_wages * self.MI_UNEMP)
+
+ def test_2019_taxes_semimonthly(self):
+ salary = 5000.0
+ schedule_pay = 'semi-monthly'
+ allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
+ exemption = 1
+
+ wh = -((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MI'),
+ state_income_tax_additional_withholding=0.0,
+ mi_w4_sit_exemptions=1.0,
+ schedule_pay='semi-monthly')
+
+ self._log('2019 Michigan tax first payslip semi-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.MI_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+ remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (self.MI_UNEMP_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 Michigan tax second payslip semi-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_MI_UNEMP_wages * self.MI_UNEMP)
+
+ def test_2019_taxes_monthly(self):
+ salary = 5000.0
+ schedule_pay = 'monthly'
+ allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
+ exemption = 1
+
+ wh = -((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MI'),
+ state_income_tax_additional_withholding=0.0,
+ mi_w4_sit_exemptions=1.0,
+ schedule_pay='monthly')
+
+ self._log('2019 Michigan 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.MI_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+ remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (
+ self.MI_UNEMP_MAX_WAGE - (2 * salary) < salary) \
+ else salary
+
+ self._log('2019 Michigan 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_MI_UNEMP_wages * self.MI_UNEMP)
+
+ def test_additional_withholding(self):
+ salary = 5000.0
+ schedule_pay = 'weekly'
+ allowance_amount = 0.0
+ allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
+ additional_wh = 40.0
+ exemption = 1
+
+ wh = -(((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX) + additional_wh)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MI'),
+ state_income_tax_additional_withholding=40.0,
+ mi_w4_sit_exemptions=1.0,
+ schedule_pay='weekly')
+
+ self._log('2019 Michigan tax first payslip with additional withholding:')
+ 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.MI_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2020.py
new file mode 100755
index 00000000..6de7b664
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mi_michigan_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 TestUsMIPayslip(TestUsPayslip):
+ # Taxes and Rates
+ MI_UNEMP_MAX_WAGE = 9000.0
+ MI_UNEMP = 2.7
+
+ def _test_sit(self, wage, exemptions, additional_withholding, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MI'),
+ mi_w4_sit_exemptions=exemptions,
+ 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('MI', self.MI_UNEMP, date(2020, 1, 1), wage_base=self.MI_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 1, 100.0, 'weekly', date(2020, 1, 1), 127.99)
+ self._test_sit(1750.0, 1, 0.0, 'bi-weekly', date(2020, 1, 1), 66.61)
+ self._test_sit(5000.0, 1, 5.0, 'semi-monthly', date(2020, 1, 1), 209.09)
+ self._test_sit(8000.0, 1, 5.0, 'monthly', date(2020, 1, 1), 328.18)
+ self._test_sit(5000.0, 2, 0.0, 'monthly', date(2020, 1, 1), 178.86)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py
new file mode 100755
index 00000000..2a64b57d
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py
@@ -0,0 +1,159 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsMNPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ MN_UNEMP_MAX_WAGE = 34000.0
+ MN_UNEMP = -1.11 / 100.0
+
+ def test_taxes_weekly(self):
+ salary = 30000.0
+ # Hand Calculated Amount to Test
+ # Step 1 -> 30000.00 for wages per period Step 2 -> 52.0 for weekly -> 30000 * 52 -> 1560000
+ # Step 3 -> allowances * 4250.0 -> 4250.00 in this case.
+ # Step 4 -> Step 2 - Step 3 -> 1560000 - 4250.00 -> 1555750
+ # Step 5 -> using chart -> we have last row -> ((1555750 - 166290) * (9.85 / 100)) + 11717.65 -> 148579.46
+ # Step 6 -> Convert back to pay period amount and round - > 2857.297 - > 2857.0
+ # wh = 2857.0
+ wh = -2857.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MN'),
+ mn_w4mn_sit_filing_status='single',
+ state_income_tax_additional_withholding=0.0,
+ mn_w4mn_sit_allowances=1.0,
+ schedule_pay='weekly')
+
+ self._log('2019 Minnesota 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.MN_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh) # Test numbers are off by 1 penny
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+ remaining_MN_UNEMP_wages = self.MN_UNEMP_MAX_WAGE - salary if (self.MN_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Minnesota 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_MN_UNEMP_wages * self.MN_UNEMP)
+
+ def test_taxes_married(self):
+ salary = 5000.00
+
+ # Hand Calculated Amount to Test
+ # Step 1 -> 5000.0 for wages per period Step 2 -> 52.0 for weekly -> 5000 * 52 -> 260,000
+ # Step 3 -> allowances * 4250.0 -> 4250.00 in this case.
+ # Step 4 -> Step 2 - Step 3 -> 260,000 - 4250.00 -> 255750.0
+ # For step five we used the married section
+ # Step 5 -> using chart -> we have 2nd last row -> ((255750 - 163070) * (7.85 / 100)) + 10199.33 ->
+ # Step 6 -> Convert back to pay period amount and round
+ # wh = 336.0
+ wh = -336.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MN'),
+ mn_w4mn_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ mn_w4mn_sit_allowances=1.0,
+ schedule_pay='weekly')
+
+ self._log('2019 Minnesota tax first payslip married:')
+ 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.MN_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
+
+ def test_taxes_semimonthly(self):
+ salary = 6500.00
+ # Hand Calculated Amount to Test
+ # Step 1 -> 6500.00 for wages per period Step 2 -> 24 for semi-monthly -> 6500.00 * 24 -> 156000.00
+ # Step 3 -> allowances * 4250.0 -> 4250.00 in this case.
+ # Step 4 -> Step 2 - Step 3 -> 156000.00 - 4250.00 -> 151750.0
+ # Step 5 -> using chart -> we have 2nd last row -> ((151750.0- 89510) * (7.85 / 100)) + 5690.42 -> 10576.26
+ # Step 6 -> Convert back to pay period amount and round
+ # wh = -441
+ wh = -441.00
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MN'),
+ mn_w4mn_sit_filing_status='single',
+ state_income_tax_additional_withholding=0.0,
+ mn_w4mn_sit_allowances=1.0,
+ schedule_pay='semi-monthly')
+
+
+ self._log('2019 Minnesota tax first payslip semimonthly:')
+ 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.MN_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
+
+ def test_tax_exempt(self):
+ salary = 5500.00
+ wh = 0
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MN'),
+ mn_w4mn_sit_filing_status='',
+ state_income_tax_additional_withholding=0.0,
+ mn_w4mn_sit_allowances=2.0,
+ schedule_pay='weekly')
+
+ self._log('2019 Minnesota tax first payslip exempt:')
+ 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.MN_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
+
+ def test_additional_withholding(self):
+ salary = 5500.0
+ # Hand Calculated Amount to Test
+ # Step 1 -> 5500 for wages per period Step 2 -> 52 for weekly -> 5500 * 52 -> 286000.00
+ # Step 3 -> allowances * 4250.0 -> 8500 in this case.
+ # Step 4 -> Step 2 - Step 3 -> 286000.00 - 8500 -> 277500
+ # Step 5 -> using chart -> we have last row -> ((277500- 166290) * (9.85 / 100)) + 11717.65 -> 22671.835
+ # Step 6 -> Convert back to pay period amount and round
+ # wh = -436.0
+ # Add additional_withholding
+ # wh = -436.0 + 40.0
+ wh = -476.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MN'),
+ mn_w4mn_sit_filing_status='single',
+ state_income_tax_additional_withholding=40.0,
+ mn_w4mn_sit_allowances=2.0,
+ schedule_pay='weekly')
+
+ self._log('2019 Minnesota tax first payslip additional withholding:')
+ 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.MN_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2020.py
new file mode 100755
index 00000000..c91fa2a8
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsMNPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ MN_UNEMP_MAX_WAGE = 35000.0
+ MN_UNEMP = 1.11
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MN'),
+ mn_w4mn_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ mn_w4mn_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('MN', self.MN_UNEMP, date(2020, 1, 1), wage_base=self.MN_UNEMP_MAX_WAGE)
+ self._test_sit(5000.0, 'single', 1.0, 0.0, 'weekly', date(2020, 1, 1), 389.0)
+ self._test_sit(30000.0, 'single', 1.0, 0.0, 'weekly', date(2020, 1, 1), 2850.99)
+ self._test_sit(5000.0, 'married', 1.0, 0.0, 'weekly', date(2020, 1, 1), 325.0)
+ self._test_sit(6500.0, 'single', 1.0, 0.0, 'semi-monthly', date(2020, 1, 1), 429.0)
+ self._test_sit(5500.0, '', 2.0, 0.0, 'weekly', date(2020, 1, 1), 0.0)
+ self._test_sit(5500.0, 'single', 2.0, 40.0, 'weekly', date(2020, 1, 1), 470.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py
new file mode 100755
index 00000000..27a0ad93
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py
@@ -0,0 +1,188 @@
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsMoPayslip(TestUsPayslip):
+ # Calculations from http://dor.mo.gov/forms/4282_2019.pdf
+ SALARY = 12000.0
+ MO_UNEMP = -2.376 / 100.0
+
+ TAX = [
+ (1053.0, 1.5),
+ (1053.0, 2.0),
+ (1053.0, 2.5),
+ (1053.0, 3.0),
+ (1053.0, 3.5),
+ (1053.0, 4.0),
+ (1053.0, 4.5),
+ (1053.0, 5.0),
+ (999999999.0, 5.4),
+ ]
+
+ def test_2019_taxes_single(self):
+ # Payroll Period Monthly
+ salary = self.SALARY
+ pp = 12.0
+ gross_salary = salary * pp
+ spouse_employed = False
+
+ # Single
+ standard_deduction = 12400.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MO'),
+ mo_mow4_sit_filing_status='single',
+ state_income_tax_additional_withholding=0.0,
+ schedule_pay='monthly')
+
+ self._log('2019 Missouri tax single first payslip:')
+ 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.MO_UNEMP)
+
+ mo_taxable_income = gross_salary - standard_deduction
+ self._log('%s = %s - %s -' % (mo_taxable_income, gross_salary, standard_deduction))
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in self.TAX:
+ amt = float(amt)
+ rate = rate / 100.0
+ self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
+ if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * remaining_taxable_income
+ break
+ remaining_taxable_income = remaining_taxable_income - amt
+
+ tax = -tax
+ self._log('Computed annual tax: ' + str(tax))
+
+ tax = tax / pp
+ tax = round(tax)
+ self._log('Computed period tax: ' + str(tax))
+ self.assertPayrollEqual(cats['EE_US_SIT'], tax)
+
+ def test_2019_spouse_not_employed(self):
+ # Payroll Period Semi-monthly
+ salary = self.SALARY
+ pp = 24.0
+ gross_salary = salary * pp
+
+ # Married
+ standard_deduction = 24800.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MO'),
+ mo_mow4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ schedule_pay='semi-monthly')
+
+ self._log('2019 Missouri tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ mo_taxable_income = gross_salary - standard_deduction
+ self._log(mo_taxable_income)
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in self.TAX:
+ amt = float(amt)
+ rate = rate / 100.0
+ self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
+ if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * remaining_taxable_income
+ break
+ remaining_taxable_income = remaining_taxable_income - amt
+
+ tax = -tax
+ self._log('Computed annual tax: ' + str(tax))
+
+ tax = tax / pp
+ tax = round(tax)
+ self._log('Computed period tax: ' + str(tax))
+ self.assertPayrollEqual(cats['EE_US_SIT'], tax)
+
+ def test_2019_head_of_household(self):
+ # Payroll Period Weekly
+ salary = self.SALARY
+
+ # Payroll Period Weekly
+ salary = self.SALARY
+ pp = 52.0
+ gross_salary = salary * pp
+
+ # Single HoH
+ standard_deduction = 18650.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MO'),
+ mo_mow4_sit_filing_status='head_of_household',
+ state_income_tax_additional_withholding=0.0,
+ schedule_pay='weekly')
+
+ self._log('2019 Missouri tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ mo_taxable_income = gross_salary - standard_deduction
+ self._log(mo_taxable_income)
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in self.TAX:
+ amt = float(amt)
+ rate = rate / 100.0
+ self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
+ if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * remaining_taxable_income
+ break
+ remaining_taxable_income = remaining_taxable_income - amt
+ tax = -tax
+ self._log('Computed annual tax: ' + str(tax))
+
+ tax = tax / pp
+ tax = round(tax)
+ self._log('Computed period tax: ' + str(tax))
+ self.assertPayrollEqual(cats['EE_US_SIT'], tax)
+
+ def test_2019_underflow(self):
+ # Payroll Period Weekly
+ salary = 200.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MO'))
+
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SIT'], 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py
new file mode 100755
index 00000000..ff6a0ca1
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py
@@ -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 TestUsMoPayslip(TestUsPayslip):
+ # Calculations from http://dor.mo.gov/forms/4282_2020.pdf
+ MO_UNEMP_MAX_WAGE = 11500.0
+ MO_UNEMP = 2.376
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MO'),
+ mo_mow4_sit_filing_status=filing_status,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('MO', self.MO_UNEMP, date(2020, 1, 1), wage_base=self.MO_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 'single', 0.0, 'weekly', date(2020, 1, 1), 24.00)
+ self._test_sit(2500.0, 'single', 5.0, 'bi-weekly', date(2020, 1, 1), 107.00)
+ self._test_sit(7000.0, 'married', 0.0, 'monthly', date(2020, 1, 1), 251.00)
+ self._test_sit(5000.0, 'married', 10.0, 'semi-monthly', date(2020, 1, 1), 217.00)
+ self._test_sit(6000.0, '', 0.0, 'monthly', date(2020, 1, 1), 0.00)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
new file mode 100755
index 00000000..e7ce35d0
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
@@ -0,0 +1,94 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip
+
+
+class TestUsMsPayslip(TestUsPayslip):
+ # Calculations from https://www.dor.ms.gov/Documents/Computer%20Payroll%20Accounting%201-2-19.pdf
+ MS_UNEMP = -1.2 / 100.0
+
+ def test_2019_taxes_one(self):
+ salary = 1250.0
+ ms_89_350_exemption = 11000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MS'),
+ ms_89_350_sit_filing_status='head_of_household',
+ ms_89_350_sit_exemption_value=ms_89_350_exemption,
+ state_income_tax_additional_withholding=0.0,
+ schedule_pay='semi-monthly')
+
+ self._log('2019 Mississippi tax single first payslip:')
+ 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.MS_UNEMP)
+
+ STDED = 3400.0 # Head of Household
+ AGP = salary * 24 # Semi-Monthly
+ TI = AGP - (ms_89_350_exemption + STDED)
+ self.assertPayrollEqual(TI, 15600.0)
+ TAX = ((TI - 10000) * 0.05) + 290 # Over 10,000
+ self.assertPayrollEqual(TAX, 570.0)
+
+ ms_withhold = round(TAX / 24) # Semi-Monthly
+ self.assertPayrollEqual(cats['EE_US_SIT'], -ms_withhold)
+
+ def test_2019_taxes_one_exempt(self):
+ salary = 1250.0
+ ms_89_350_exemption = 11000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MS'),
+ ms_89_350_sit_filing_status='',
+ ms_89_350_sit_exemption_value=ms_89_350_exemption,
+ state_income_tax_additional_withholding=0.0,
+ schedule_pay='semi-monthly')
+
+ self._log('2019 Mississippi tax single first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
+
+ def test_2019_taxes_additional(self):
+ salary = 1250.0
+ ms_89_350_exemption = 11000.0
+ additional = 40.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MS'),
+ ms_89_350_sit_filing_status='head_of_household',
+ ms_89_350_sit_exemption_value=ms_89_350_exemption,
+ state_income_tax_additional_withholding=additional,
+ schedule_pay='semi-monthly')
+
+ self._log('2019 Mississippi tax single first payslip:')
+ 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.MS_UNEMP)
+
+ STDED = 3400.0 # Head of Household
+ AGP = salary * 24 # Semi-Monthly
+ TI = AGP - (ms_89_350_exemption + STDED)
+ self.assertPayrollEqual(TI, 15600.0)
+ TAX = ((TI - 10000) * 0.05) + 290 # Over 10,000
+ self.assertPayrollEqual(TAX, 570.0)
+
+ ms_withhold = round(TAX / 24) # Semi-Monthly
+ self.assertPayrollEqual(cats['EE_US_SIT'], -ms_withhold + -additional)
diff --git a/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py
new file mode 100755
index 00000000..ea0081ca
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_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 TestUsMsPayslip(TestUsPayslip):
+ # Calculations from https://www.dor.ms.gov/Documents/Computer%20Payroll%20Flowchart.pdf
+ MS_UNEMP = 1.2
+ MS_UNEMP_MAX_WAGE = 14000.0
+
+ def _test_sit(self, wage, filing_status, additional_withholding, exemption, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MS'),
+ ms_89_350_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ms_89_350_sit_exemption_value=exemption,
+ 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_2020_taxes_example(self):
+ self._test_er_suta('MS', self.MS_UNEMP, date(2020, 1, 1), wage_base=self.MS_UNEMP_MAX_WAGE)
+ self._test_sit(1250.0, 'head_of_household', 0.0, 11000, 'semi-monthly', date(2020, 1, 1), 22.00)
+ self._test_sit(500.0, '', 5.0, 0, 'bi-weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(12000.0, 'single', 0.0, 11000, 'monthly', date(2020, 1, 1), 525.00)
+ self._test_sit(2500.0, 'married', 5.0, 500, 'bi-weekly', date(2020, 1, 1), 111.00)
+
+
diff --git a/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
new file mode 100755
index 00000000..ff6e2daf
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
@@ -0,0 +1,139 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsMtPayslip(TestUsPayslip):
+ # Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705
+ MT_UNEMP = -1.18 / 100.0
+ MT_UNEMP_AFT = -0.13 / 100.0
+
+ def test_2019_taxes_one(self):
+ # Payroll Period Semi-Monthly example
+ salary = 550
+ mt_mw4_exemptions = 5
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MT'),
+ mt_mw4_sit_exemptions=mt_mw4_exemptions,
+ schedule_pay='semi-monthly')
+
+ self._log('2019 Montana tax single first payslip:')
+ 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.MT_UNEMP + self.MT_UNEMP_AFT)) # New non-combined...
+
+ mt_taxable_income = salary - (79.0 * mt_mw4_exemptions)
+ mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0)))
+ self.assertPayrollEqual(mt_taxable_income, 155.0)
+ self.assertPayrollEqual(mt_withhold, 3.0)
+ self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold)
+
+ def test_2019_taxes_two(self):
+ # Payroll Period Bi-Weekly example
+ salary = 2950
+ mt_mw4_exemptions = 2
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MT'),
+ mt_mw4_sit_exemptions=mt_mw4_exemptions,
+ schedule_pay='bi-weekly')
+
+ self._log('2019 Montana tax single first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], round(salary * (self.MT_UNEMP + self.MT_UNEMP_AFT), 2))
+
+ # Note!!
+ # The example calculation uses A = 16 but the actual table describes this as A = 18
+ mt_taxable_income = salary - (73.0 * mt_mw4_exemptions)
+ mt_withhold = round(18 + (0.06 * (mt_taxable_income - 577)))
+ self.assertPayrollEqual(mt_taxable_income, 2804.0)
+ self.assertPayrollEqual(mt_withhold, 152.0)
+ self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold)
+
+ def test_2019_taxes_three(self):
+ # Payroll Period Weekly example
+ salary = 135
+ mt_mw4_exemptions = 1
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MT'),
+ mt_mw4_sit_exemptions=mt_mw4_exemptions,
+ schedule_pay='weekly')
+
+ self._log('2019 Montana tax single first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], round(salary * (self.MT_UNEMP + self.MT_UNEMP_AFT), 2))
+
+ mt_taxable_income = salary - (37.0 * mt_mw4_exemptions)
+ mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0)))
+ self.assertPayrollEqual(mt_taxable_income, 98.0)
+ self.assertPayrollEqual(mt_withhold, 2.0)
+ self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold)
+
+ def test_2019_taxes_three_exempt(self):
+ # Payroll Period Weekly example
+ salary = 135
+ mt_mw4_exemptions = 1
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MT'),
+ mt_mw4_sit_exemptions=mt_mw4_exemptions,
+ mt_mw4_sit_exempt='reserve',
+ schedule_pay='weekly')
+
+ self._log('2019 Montana tax single first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
+
+ def test_2019_taxes_three_additional(self):
+ # Payroll Period Weekly example
+ salary = 135
+ mt_mw4_exemptions = 1
+ mt_mw4_additional_withholding = 20.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('MT'),
+ mt_mw4_sit_exemptions=mt_mw4_exemptions,
+ state_income_tax_additional_withholding=mt_mw4_additional_withholding,
+ schedule_pay='weekly')
+
+ self._log('2019 Montana tax single first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ mt_taxable_income = salary - (37.0 * mt_mw4_exemptions)
+ mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0)))
+ self.assertPayrollEqual(mt_taxable_income, 98.0)
+ self.assertPayrollEqual(mt_withhold, 2.0)
+ self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold + -mt_mw4_additional_withholding)
diff --git a/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
new file mode 100755
index 00000000..13a7e69f
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsMtPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ MT_UNEMP_WAGE_MAX = 34100.0
+ MT_UNEMP = 1.18
+ MT_UNEMP_AFT = 0.13
+
+ # Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705
+ def _test_sit(self, wage, additional_withholding, exemptions, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MT'),
+ state_income_tax_additional_withholding=additional_withholding,
+ mt_mw4_sit_exemptions=exemptions,
+ 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_one(self):
+ combined_rate = self.MT_UNEMP + self.MT_UNEMP_AFT # Combined for test as they both go to the same category and have the same cap
+ self._test_er_suta('MT', combined_rate, date(2020, 1, 1), wage_base=self.MT_UNEMP_WAGE_MAX)
+ self._test_sit(550.0, 0.0, 5.0, 'semi-monthly', date(2020, 1, 1), 3.0)
+ self._test_sit(2950.0, 10.0, 2.0, 'bi-weekly', date(2020, 1, 1), 162.0)
+ self._test_sit(5000.0, 0.0, 1.0, 'monthly', date(2020, 1, 1), 256.0)
+ self._test_sit(750.0, 0.0, 1.0, 'weekly', date(2020, 1, 1), 34.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py
new file mode 100755
index 00000000..14c1c5b2
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py
@@ -0,0 +1,270 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsNCPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ NC_UNEMP_MAX_WAGE = 24300.0
+ NC_UNEMP = -1.0 / 100.0
+ NC_INC_TAX = -0.0535
+
+
+ def test_2019_taxes_weekly(self):
+ salary = 20000.0
+ # allowance_multiplier and Portion of Standard Deduction for weekly
+ allowance_multiplier = 48.08
+ PST = 192.31
+ exemption = 1
+ # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
+ wh = -round((salary - (PST + (allowance_multiplier * exemption))) * -self.NC_INC_TAX)
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ nc_nc4_sit_allowances=1.0,
+ schedule_pay='weekly')
+
+ self._log('2019 North Carolina tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+ self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
+
+ def test_2019_taxes_with_external_weekly(self):
+ salary = 5000.0
+ schedule_pay = 'weekly'
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ nc_nc4_sit_allowances=1.0,
+ schedule_pay='weekly')
+
+ self._log('2019 NorthCarolina_external 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.NC_UNEMP)
+
+ def test_2019_taxes_biweekly(self):
+ salary = 5000.0
+ schedule_pay = 'bi-weekly'
+ # allowance_multiplier and Portion of Standard Deduction for weekly
+ allowance_multiplier = 96.15
+ PST = 384.62
+
+ allowances = 2
+ # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
+
+ wh = -round((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX)
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ nc_nc4_sit_allowances=2.0,
+ schedule_pay='bi-weekly')
+
+ self._log('2019 North Carolina tax first payslip bi-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.NC_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 North Carolina tax second payslip bi-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_NC_UNEMP_wages * self.NC_UNEMP)
+
+ def test_2019_taxes_semimonthly(self):
+ salary = 4000.0
+ # allowance_multiplier and Portion of Standard Deduction for weekly
+ allowance_multiplier = 104.17
+ PST = 625.00
+
+ allowances = 1
+ # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
+
+ wh = -round((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX)
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status='head_household',
+ state_income_tax_additional_withholding=0.0,
+ nc_nc4_sit_allowances=1.0,
+ schedule_pay='semi-monthly')
+
+ self._log('2019 North Carolina tax first payslip:')
+ 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.NC_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
+
+ def test_2019_taxes_monthly(self):
+ salary = 4000.0
+ schedule_pay = 'monthly'
+ # allowance_multiplier and Portion of Standard Deduction for weekly
+ allowance_multiplier = 208.33
+ PST = 833.33
+
+ allowances = 1
+ # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
+
+ wh = -round((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX)
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status='single',
+ state_income_tax_additional_withholding=0.0,
+ nc_nc4_sit_allowances=1.0,
+ schedule_pay='monthly')
+
+ self._log('2019 North Carolina tax first payslip:')
+ 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.NC_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (
+ self.NC_UNEMP_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
+
+ def test_additional_withholding(self):
+ salary = 4000.0
+ # allowance_multiplier and Portion of Standard Deduction for weekly
+ allowance_multiplier = 48.08
+ PST = 192.31
+ additional_wh = 40.0
+
+ #4000 - (168.27 + (48.08 * 1)
+
+ allowances = 1
+ # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
+
+ wh = -round(((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX) + additional_wh)
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status='married',
+ state_income_tax_additional_withholding=40.0,
+ nc_nc4_sit_allowances=1.0,
+ schedule_pay='weekly')
+
+ self._log('2019 North Carolina tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py
new file mode 100755
index 00000000..8e2d69c1
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsNCPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NC_UNEMP_MAX_WAGE = 25200.0
+ NC_UNEMP = 1.0
+ NC_INC_TAX = 0.0535
+ # Example based on https://files.nc.gov/ncdor/documents/files/NC-30_book_Web_1-16-19_v4_Final.pdf
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status=filing_status,
+ nc_nc4_sit_allowances=allowances,
+ 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 if filing_status else 0.0)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('NC', self.NC_UNEMP, date(2020, 1, 1), wage_base=self.NC_UNEMP_MAX_WAGE)
+ self._test_sit(20000.0, 'single', 1, 100.0, 'weekly', date(2020, 1, 1), 1156.0)
+ self._test_sit(5000.0, 'married', 1, 0.0, 'weekly', date(2020, 1, 1), 254.0)
+ self._test_sit(4000.0, 'head_household', 1, 5.0, 'semi-monthly', date(2020, 1, 1), 177.0)
+ self._test_sit(7000.0, '', 1, 5.0, 'monthly', date(2020, 1, 1), 0.0)
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
new file mode 100644
index 00000000..903cf816
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsNDPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ ND_UNEMP_MAX_WAGE = 37900.0
+ 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, 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.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, 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)
diff --git a/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py
new file mode 100644
index 00000000..785e4417
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py
@@ -0,0 +1,38 @@
+# 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 TestUsNEPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NE_UNEMP_MAX_WAGE = 9000.0
+ NE_UNEMP = 1.25
+
+ def _test_sit(self, wage, filing_status, exempt, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NE'),
+ ne_w4n_sit_filing_status=filing_status,
+ state_income_tax_exempt=exempt,
+ state_income_tax_additional_withholding=additional_withholding,
+ ne_w4n_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_2020_taxes_example(self):
+ self._test_er_suta('NE', self.NE_UNEMP, date(2020, 1, 1), wage_base=self.NE_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 'single', False, 0.0, 2, 'weekly', date(2020, 1, 1), 27.53)
+ self._test_sit(9500.0, 'single', False, 0.0, 1, 'bi-weekly', date(2020, 1, 1), 612.63)
+ self._test_sit(10500.0, 'married', False, 0.0, 1, 'bi-weekly', date(2020, 1, 1), 659.85)
+ self._test_sit(9500.0, 'single', True, 0.0, 1, 'bi-weekly', date(2020, 1, 1), 0.0)
+ self._test_sit(10500.0, 'single', False, 10.0, 2, 'monthly', date(2020, 1, 1), 625.2)
+ self._test_sit(4000.0, 'single', False, 0.0, 1, 'monthly', date(2020, 1, 1), 179.44)
diff --git a/l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2020.py
new file mode 100644
index 00000000..1d85e700
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2020.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsNHPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ NH_UNEMP_MAX_WAGE = 14000.00
+ NH_UNEMP = 1.2
+
+ def test_2020_taxes(self):
+ self._test_er_suta('NH', self.NH_UNEMP, date(2020, 1, 1), wage_base=self.NH_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py
new file mode 100755
index 00000000..c28849b5
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py
@@ -0,0 +1,128 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsNJPayslip(TestUsPayslip):
+ ###
+ # 2019 Taxes and Rates
+ ###
+ NJ_UNEMP_MAX_WAGE = 34400.0 # Note that this is used for SDI and FLI as well
+
+ ER_NJ_UNEMP = -2.6825 / 100.0
+ EE_NJ_UNEMP = -0.3825 / 100.0
+
+ ER_NJ_SDI = -0.5 / 100.0
+ EE_NJ_SDI = -0.17 / 100.0
+
+ ER_NJ_WF = -0.1175 / 100.0
+ EE_NJ_WF = -0.0425 / 100.0
+
+ ER_NJ_FLI = 0.0
+ EE_NJ_FLI = -0.08 / 100.0
+
+ # Examples found on page 24 of http://www.state.nj.us/treasury/taxation/pdf/current/njwt.pdf
+ def test_2019_taxes_example1(self):
+ salary = 300
+
+ # Tax Percentage Method for Single, taxable under $385
+ wh = -4.21
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NJ'),
+ nj_njw4_sit_filing_status='single',
+ nj_njw4_sit_allowances=1,
+ state_income_tax_additional_withholding=0.0,
+ nj_njw4_sit_rate_table='A',
+ schedule_pay='weekly')
+
+ self._log('2019 New Jersey tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * (self.EE_NJ_UNEMP + self.EE_NJ_SDI + self.EE_NJ_WF + self.EE_NJ_FLI))
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * (self.ER_NJ_UNEMP + self.ER_NJ_SDI + self.ER_NJ_WF + self.ER_NJ_FLI))
+ self.assertTrue(all((cats['EE_US_SUTA'], cats['ER_US_SUTA'])))
+ # self.assertPayrollEqual(cats['EE_US_NJ_SDI_SIT'], cats['EE_US_NJ_SDI_SIT'] * self.EE_NJ_SDI)
+ # self.assertPayrollEqual(cats['ER_US_NJ_SDI_SUTA'], cats['ER_US_NJ_SDI_SUTA'] * self.ER_NJ_SDI)
+ # self.assertPayrollEqual(cats['EE_US_NJ_FLI_SIT'], cats['EE_US_NJ_FLI_SIT'] * self.EE_NJ_FLI)
+ # self.assertPayrollEqual(cats['EE_US_NJ_WF_SIT'], cats['EE_US_NJ_WF_SIT'] * self.EE_NJ_WF)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # # Make a new payslip, this one will have maximums
+ #
+ remaining_nj_unemp_wages = self.NJ_UNEMP_MAX_WAGE - salary if (self.NJ_UNEMP_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 New Jersey tax second payslip:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ # self.assertPayrollEqual(cats['WAGE_US_NJ_UNEMP'], remaining_nj_unemp_wages)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_nj_unemp_wages * (self.ER_NJ_UNEMP + self.ER_NJ_SDI + self.ER_NJ_WF + self.ER_NJ_FLI))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], remaining_nj_unemp_wages * (self.EE_NJ_UNEMP + self.EE_NJ_SDI + self.EE_NJ_WF + self.EE_NJ_FLI))
+
+ def test_2019_taxes_example2(self):
+ salary = 1400.00
+
+ # Tax Percentage Method for Single, taxable pay over $962, under $1346
+ wh = -27.58
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NJ'),
+ nj_njw4_sit_filing_status='married_separate',
+ nj_njw4_sit_allowances=3,
+ state_income_tax_additional_withholding=0.0,
+ #nj_njw4_sit_rate_table='B',
+ schedule_pay='weekly')
+
+ self.assertEqual(contract.schedule_pay, 'weekly')
+
+ self._log('2019 New Jersey tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+
+ def test_2019_taxes_to_the_limits(self):
+ salary = 30000.00
+
+ # Tax Percentage Method for Single, taxable pay over $18750, under 125000
+ wh = -1467.51
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NJ'),
+ nj_njw4_sit_filing_status='married_joint',
+ nj_njw4_sit_allowances=3,
+ state_income_tax_additional_withholding=0.0,
+ # nj_njw4_sit_rate_table='B',
+ schedule_pay='quarterly')
+
+ self.assertEqual(contract.schedule_pay, 'quarterly')
+
+ self._log('2019 New Jersey tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-03-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py
new file mode 100755
index 00000000..1df4af6a
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py
@@ -0,0 +1,51 @@
+# 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 TestUsNJPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NJ_UNEMP_MAX_WAGE = 35300.0 # Note that this is used for SDI and FLI as well
+
+ ER_NJ_UNEMP = 2.6825
+ EE_NJ_UNEMP = 0.3825
+
+ ER_NJ_SDI = 0.5
+ EE_NJ_SDI = 0.26
+
+ ER_NJ_WF = 0.1175
+ EE_NJ_WF = 0.0425
+
+ ER_NJ_FLI = 0.0
+ EE_NJ_FLI = 0.16
+
+ def _test_sit(self, wage, filing_status, allowances, schedule_pay, date_start, expected_withholding, rate_table=False):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NJ'),
+ nj_njw4_sit_filing_status=filing_status,
+ nj_njw4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=0.0,
+ nj_njw4_sit_rate_table=rate_table,
+ schedule_pay=schedule_pay)
+ payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding if filing_status else 0.0)
+
+ def test_2020_taxes_example1(self):
+ combined_er_rate = self.ER_NJ_UNEMP + self.ER_NJ_FLI + self.ER_NJ_SDI + self.ER_NJ_WF
+ self._test_er_suta('NJ', combined_er_rate, date(2020, 1, 1), wage_base=self.NJ_UNEMP_MAX_WAGE)
+ combined_ee_rate = self.EE_NJ_UNEMP + self.EE_NJ_FLI + self.EE_NJ_SDI + self.EE_NJ_WF
+ self._test_ee_suta('NJ', combined_ee_rate, date(2020, 1, 1), wage_base=self.NJ_UNEMP_MAX_WAGE, relaxed=True)
+ # these expected values come from https://www.state.nj.us/treasury/taxation/pdf/current/njwt.pdf
+ self._test_sit(300.0, 'single', 1, 'weekly', date(2020, 1, 1), 4.21)
+ self._test_sit(375.0, 'married_separate', 3, 'weekly', date(2020, 1, 1), 4.76)
+ self._test_sit(1400.0, 'head_household', 3, 'weekly', date(2020, 1, 1), 27.60)
+ self._test_sit(1400.0, '', 3, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(2500.0, 'single', 3, 'bi-weekly', date(2020, 1, 1), 82.66)
+ self._test_sit(15000.0, 'married_joint', 2, 'monthly', date(2020, 1, 1), 844.85)
diff --git a/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py
new file mode 100755
index 00000000..24f8c5a4
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsNMPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NM_UNEMP_MAX_WAGE = 25800.0
+ NM_UNEMP = 1.0
+ # Calculation based on section 17. https://s3.amazonaws.com/realFile34821a95-73ca-43e7-b06d-fad20f5183fd/a9bf1098-533b-4a3d-806a-4bf6336af6e4?response-content-disposition=filename%3D%22FYI-104+-+New+Mexico+Withholding+Tax+-+Effective+January+1%2C+2020.pdf%22&response-content-type=application%2Fpdf&AWSAccessKeyId=AKIAJBI25DHBYGD7I7TA&Signature=feu%2F1oJvU6BciRfKcoR0iNxoVZE%3D&Expires=1585159702
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NM'),
+ fed_941_fit_w4_filing_status=filing_status,
+ 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('NM', self.NM_UNEMP, date(2020, 1, 1), wage_base=self.NM_UNEMP_MAX_WAGE)
+ self._test_sit(1000.0, 'married', 0.0, 'weekly', date(2020, 1, 1), 29.47)
+ self._test_sit(1000.0, 'married', 10.0, 'weekly', date(2020, 1, 1), 39.47)
+ self._test_sit(25000.0, 'single', 0.0, 'bi-weekly', date(2020, 1, 1), 1202.60)
+ self._test_sit(25000.0, 'married_as_single', 0.0, 'monthly', date(2020, 1, 1), 1152.95)
+ self._test_sit(4400.0, '', 0.0, 'monthly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py
new file mode 100755
index 00000000..52c2114b
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py
@@ -0,0 +1,16 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsNVPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NV_UNEMP_MAX_WAGE = 32500.0
+ NV_UNEMP = 2.95
+
+ def test_2020_taxes(self):
+ # Only has state unemployment
+ self._test_er_suta('NV', self.NV_UNEMP, date(2020, 1, 1), wage_base=self.NV_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
new file mode 100644
index 00000000..2c3b9306
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
@@ -0,0 +1,133 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsNYPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ NY_UNEMP_MAX_WAGE = 11400.0
+ NY_UNEMP = 2.5
+ NY_RSF = 0.075
+ NY_MCTMT = 0.0
+
+ def test_single_example1(self):
+ salary = 400
+ schedule_pay = 'weekly'
+ allowances = 3
+ additional_withholding = 0
+ filing_status = 'single'
+ additional = 0.0
+ wh = -8.20
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NY'),
+ ny_it2104_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional,
+ ny_it2104_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self.assertEqual(contract.schedule_pay, 'weekly')
+ self._log('2018 New York tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['NY_UNEMP'], cats['NY_UNEMP_WAGES'] * self.NY_UNEMP)
+ self.assertPayrollEqual(cats['NY_RSF'], cats['NY_UNEMP_WAGES'] * self.NY_RSF)
+ self.assertPayrollEqual(cats['NY_MCTMT'], cats['NY_UNEMP_WAGES'] * self.NY_MCTMT)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ def test_married_example2(self):
+ salary = 5000
+ schedule_pay = 'semi-monthly'
+ allowances = 3
+ additional = 0
+ filing_status = 'married'
+ wh = -284.19
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NY'),
+ ny_it2104_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional,
+ ny_it2104_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 New York tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['NY_UNEMP'], cats['NY_UNEMP_WAGES'] * self.NY_UNEMP)
+ self.assertPayrollEqual(cats['NY_RSF'], cats['NY_UNEMP_WAGES'] * self.NY_RSF)
+ self.assertPayrollEqual(cats['NY_MCTMT'], cats['NY_UNEMP_WAGES'] * self.NY_MCTMT)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ def test_single_example3(self):
+ salary = 50000
+ schedule_pay = 'monthly'
+ allowances = 3
+ additional = 0
+ filing_status = 'single'
+ wh = -3575.63
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NY'),
+ ny_it2104_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional,
+ ny_it2104_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 New York tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ def test_exempt_example3(self):
+ salary = 50000
+ schedule_pay = 'monthly'
+ allowances = 3
+ additional = 0
+ filing_status = ''
+ wh = 0.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NY'),
+ ny_it2104_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional,
+ ny_it2104_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 New York tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2020.py
new file mode 100644
index 00000000..05e50792
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2020.py
@@ -0,0 +1,39 @@
+# 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 TestUsNYPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NY_UNEMP_MAX_WAGE = 11600.0
+ NY_UNEMP = 2.5
+ NY_RSF = 0.075
+ NY_MCTMT = 0.0
+
+ 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('NY'),
+ ny_it2104_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ny_it2104_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_2020_taxes_example(self):
+ combined_er_rate = self.NY_UNEMP + self.NY_RSF + self.NY_MCTMT
+ self._test_er_suta('NY', combined_er_rate, date(2020, 1, 1), wage_base=self.NY_UNEMP_MAX_WAGE, relaxed=True)
+ self._test_sit(400.0, 'single', 0.0, 3, 'weekly', date(2020, 1, 1), 8.20)
+ self._test_sit(10000.0, 'single', 0.0, 3, 'monthly', date(2020, 1, 1), 554.09)
+ self._test_sit(8000.0, 'married', 0.0, 5, 'monthly', date(2020, 1, 1), 400.32)
+ self._test_sit(4500.0, 'married', 10.0, 3, 'semi-monthly', date(2020, 1, 1), 247.69)
+ self._test_sit(50000.0, '', 0.0, 0, 'monthly', date(2020, 1, 1), 0.00)
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
new file mode 100755
index 00000000..d1f65f05
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
@@ -0,0 +1,96 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
+
+
+class TestUsOhPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ OH_UNEMP_MAX_WAGE = 9500.0
+ OH_UNEMP = -2.7 / 100.0
+
+ def test_2019_taxes(self):
+ salary = 5000.0
+
+ # For formula here
+ # http://www.tax.ohio.gov/Portals/0/employer_withholding/August2015Rates/WTH_OptionalComputerFormula_073015.pdf
+ tw = salary * 12 # = 60000
+ wd = ((tw - 40000) * 0.035 + 900) / 12 * 1.075
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('OH'),
+ )
+
+ self._log('2019 Ohio tax first payslip:')
+ 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.OH_UNEMP)
+ 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)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_oh_unemp_wages = self.OH_UNEMP_MAX_WAGE - salary if (self.OH_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Ohio 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_oh_unemp_wages * self.OH_UNEMP)
+
+ def test_2019_taxes_with_external(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('OH'),
+ external_wages=external_wages,
+ )
+
+ self._log('2019 Ohio_external tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], (self.OH_UNEMP_MAX_WAGE - external_wages) * self.OH_UNEMP)
+
+ def test_2019_taxes_with_state_exempt(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('OH'),
+ external_wages=external_wages,
+ futa_type=USHRContract.FUTA_TYPE_BASIC)
+
+ self._log('2019 Ohio exempt tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ # FUTA_TYPE_BASIC
+ self.assertPayrollEqual(cats.get('ER_US_SUTA', 0.0), salary * 0.0)
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
new file mode 100755
index 00000000..9026da92
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py
@@ -0,0 +1,108 @@
+# 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 TestUsOhPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ OH_UNEMP_MAX_WAGE = 9000.0
+ OH_UNEMP = 2.7
+
+ def test_2020_taxes(self):
+ self._test_er_suta('OH', self.OH_UNEMP, date(2020, 1, 1), wage_base=self.OH_UNEMP_MAX_WAGE)
+
+ def _run_test_sit(self,
+ wage=0.0,
+ schedule_pay='monthly',
+ filing_status='single',
+ dependent_credit=0.0,
+ other_income=0.0,
+ deductions=0.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=False,
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=0,
+ expected=0.0,
+ ):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ schedule_pay=schedule_pay,
+ fed_941_fit_w4_is_nonresident_alien=is_nonresident_alien,
+ fed_941_fit_w4_filing_status=filing_status,
+ fed_941_fit_w4_multiple_jobs_higher=False,
+ fed_941_fit_w4_dependent_credit=dependent_credit,
+ fed_941_fit_w4_other_income=other_income,
+ fed_941_fit_w4_deductions=deductions,
+ fed_941_fit_w4_additional_withholding=additional_withholding,
+ state_income_tax_exempt=state_income_tax_exempt,
+ state_income_tax_additional_withholding=state_income_tax_additional_withholding,
+ oh_it4_sit_exemptions=oh_it4_sit_exemptions,
+ state_id=self.get_us_state('OH'),
+ )
+ payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ # Instead of PayrollEqual after initial first round of testing.
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected)
+ return payslip
+
+ def test_2020_sit_1(self):
+ wage = 400.0
+ exemptions = 1
+ additional = 10.0
+ pay_periods = 12.0
+ annual_adjusted_wage = (wage * pay_periods) - (650.0 * exemptions)
+ self.assertPayrollEqual(4150.0, annual_adjusted_wage)
+ WD = ((annual_adjusted_wage * 0.005) / pay_periods) * 1.032
+ self.assertPayrollEqual(WD, 1.7845)
+ expected = WD + additional
+ self._run_test_sit(wage=wage,
+ schedule_pay='monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=additional,
+ oh_it4_sit_exemptions=exemptions,
+ expected=expected,
+ )
+
+ # the above agrees with online calculator to the penny 0.01
+ # below expected coming from calculator to 0.10
+ #
+ # semi-monthly
+ self._run_test_sit(wage=1200,
+ schedule_pay='semi-monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=20.0,
+ oh_it4_sit_exemptions=2,
+ expected=42.58,
+ )
+
+ # bi-weekly
+ self._run_test_sit(wage=3000,
+ schedule_pay='bi-weekly',
+ state_income_tax_exempt=False,
+ #state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=0,
+ expected=88.51,
+ )
+ # weekly
+ self._run_test_sit(wage=355,
+ schedule_pay='weekly',
+ state_income_tax_exempt=False,
+ # state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=1,
+ expected=4.87,
+ )
+
+ # Exempt!
+ self._run_test_sit(wage=355,
+ schedule_pay='weekly',
+ state_income_tax_exempt=True,
+ # state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=1,
+ expected=0.0,
+ )
diff --git a/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py
new file mode 100755
index 00000000..cacdcc16
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py
@@ -0,0 +1,38 @@
+# 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 TestUsOKPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ OK_UNEMP_MAX_WAGE = 18700.0
+ OK_UNEMP = 1.5
+ # Calculation based on example https://www.ok.gov/tax/documents/2020WHTables.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('OK'),
+ ok_w4_sit_filing_status=filing_status,
+ ok_w4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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('OK', self.OK_UNEMP, date(2020, 1, 1), wage_base=self.OK_UNEMP_MAX_WAGE)
+ self._test_sit(1825, 'married', 2, 0, False, 'semi-monthly', date(2020, 1, 1), 46.00)
+ self._test_sit(1825, 'married', 2, 0, True, 'monthly', date(2020, 1, 1), 0.00)
+ self._test_sit(1000, 'single', 1, 0, False, 'weekly', date(2020, 1, 1), 39.00)
+ self._test_sit(1000, 'single', 1, 10, False, 'weekly', date(2020, 1, 1), 49.00)
+ self._test_sit(5000, 'head_household', 2, 10, False, 'monthly', date(2020, 1, 1), 210.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
new file mode 100755
index 00000000..ce7e4fb4
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
@@ -0,0 +1,33 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsPAPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ PA_UNEMP_MAX_WAGE = 10000.0
+ ER_PA_UNEMP = -3.6890 / 100.0
+ EE_PA_UNEMP = -0.06 / 100.0
+ PA_INC_WITHHOLD = 3.07
+
+ def test_2019_taxes(self):
+ salary = 4166.67
+ wh = -127.92
+
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('PA'))
+
+ self._log('2019 Pennsylvania tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SUTA'], cats['GROSS'] * self.EE_PA_UNEMP)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], cats['GROSS'] * self.ER_PA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2020.py
new file mode 100755
index 00000000..3dd3fd27
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2020.py
@@ -0,0 +1,43 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsPAPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ PA_UNEMP_MAX_WAGE = 10000.0
+ ER_PA_UNEMP = 3.6890
+ EE_PA_UNEMP = 0.06
+ PA_INC_WITHHOLD = 3.07
+
+ def test_2020_taxes(self):
+ self._test_er_suta('PA', self.ER_PA_UNEMP, date(2020, 1, 1), wage_base=self.PA_UNEMP_MAX_WAGE)
+ self._test_ee_suta('PA', self.EE_PA_UNEMP, date(2020, 1, 1))
+
+ salary = 4166.67
+ wh = -127.92
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('PA'))
+
+ self._log('2019 Pennsylvania tax first payslip:')
+ payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ # Test Additional
+ contract.us_payroll_config_id.state_income_tax_additional_withholding = 100.0
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh - 100.0)
+
+ # Test Exempt
+ contract.us_payroll_config_id.state_income_tax_exempt = True
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py
new file mode 100755
index 00000000..9d92fec1
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsRIPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ RI_UNEMP_MAX_WAGE = 24000.0
+ RI_UNEMP = 1.06
+ # Calculation based on example http://www.tax.ri.gov/forms/2020/Withholding/2020%20Withhholding%20Tax%20Booklet.pdf
+
+ def _test_sit(self, wage, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('RI'),
+ ri_w4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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('RI', self.RI_UNEMP, date(2020, 1, 1), wage_base=self.RI_UNEMP_MAX_WAGE)
+ self._test_sit(2195, 1, 0, False, 'weekly', date(2020, 1, 1), 90.80)
+ self._test_sit(1800, 2, 10, True, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(10000, 1, 0, False, 'bi-weekly', date(2020, 1, 1), 503.15)
+ self._test_sit(18000, 2, 0, False, 'monthly', date(2020, 1, 1), 860.54)
+ self._test_sit(18000, 2, 10, False, 'monthly', date(2020, 1, 1), 870.55)
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
new file mode 100644
index 00000000..793f84c4
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
@@ -0,0 +1,97 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsSCPayslip(TestUsPayslip):
+
+ # Taxes and Rates
+ SC_UNEMP_MAX_WAGE = 14000.0
+ US_SC_UNEMP = -1.09 / 100
+ US_SC_exemption_amount = 2510.00
+
+ def test_2019_taxes_weekly(self):
+ # We will hand calculate the amount to test for state withholding.
+ schedule_pay = 'weekly'
+ salary = 50000.00 # Employee is paid 50000 per week to be in top tax bracket
+ allowances = 2
+ # Calculate annual wages
+ annual = 50000 * 52.0
+ # From our annual we deduct personal exemption amounts.
+ # We deduct 2510.00 per exemption. Since we have two exemptions:
+ personal_exemption = self.US_SC_exemption_amount * allowances # 5020.0
+ # From annual, we will also deduct a standard_deduction of 3470.00 or .1 of salary, which ever
+ # is small -> if 1 or more exemptions, else 0
+ standard_deduction = 3470.00
+ taxable_income = annual - personal_exemption - standard_deduction # 2591510.0
+ # We then calculate the amounts off the SC tax pdf tables.
+ # 2591478.0 is in the highest bracket
+ test_amt = (taxable_income * (7.0 / 100.0)) - 467.95
+ test_amt = 180935.51
+ # Make it per period then negative
+ test_amt = (test_amt / 52.0) # Divided by 52 since it is weekly.
+ # test_amt = 3479.52
+ test_amt = -test_amt
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('SC'),
+ state_income_tax_exempt=False,
+ sc_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 South Carolina tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollAlmostEqual(cats['ER_US_SUTA'], self.SC_UNEMP_MAX_WAGE * self.US_SC_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], test_amt)
+
+ process_payslip(payslip)
+
+ remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - annual if (annual < self.SC_UNEMP_MAX_WAGE) \
+ else 0.00
+
+ self._log('2019 South Carolina tax second payslip:')
+
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertEqual(0.0, remaining_SC_UNEMP_wages)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_SC_UNEMP_wages * self.US_SC_UNEMP)
+
+ def test_2019_taxes_filing_status(self):
+ salary = 20000.00 # Wages per pay period
+ schedule_pay = 'monthly'
+ annual = salary * 12
+ allowances = 1
+ # Hand Calculations
+ personal_exemption = 2510.00
+ standard_deduction = min(3470.00, .1 * annual) # 3470.0 but min is shown for the process
+ taxable = annual - personal_exemption - standard_deduction
+ # taxable = 234020
+ test_amt = ((taxable) * (7.0 / 100.0)) - 467.95 # 15991.850000000002
+ test_amt = test_amt / 12.0 # Put it into monthly -> 1332.654166666667
+ # Make it negative
+ test_amt = -test_amt
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('SC'),
+ state_income_tax_exempt=False,
+ sc_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 South Carolina tax first payslip: ')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.SC_UNEMP_MAX_WAGE * self.US_SC_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], test_amt)
+
+ process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
new file mode 100644
index 00000000..170c3bf5
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsSCPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ SC_UNEMP_MAX_WAGE = 14000.0
+ SC_UNEMP = 0.55
+ # Calculation based on https://dor.sc.gov/forms-site/Forms/WH1603F_2020.pdf
+
+ def _test_sit(self, wage, 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('SC'),
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ sc_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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('SC', self.SC_UNEMP, date(2020, 1, 1), wage_base=self.SC_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 0.0, False, 3.0, 'weekly', date(2020, 1, 1), 28.73)
+ self._test_sit(800.0, 0.0, True, 0.0, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(9000.0, 0.0, False, 0.0, 'monthly', date(2020, 1, 1), 594.61)
+ self._test_sit(5000.0, 10.0, False, 2.0, 'semi-monthly', date(2020, 1, 1), 316.06)
diff --git a/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py
new file mode 100644
index 00000000..bdbb7858
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsSDPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ SD_UNEMP_MAX_WAGE = 15000.00
+ SD_UNEMP = 1.75
+
+ def test_2020_taxes(self):
+ self._test_er_suta('SD', self.SD_UNEMP, date(2020, 1, 1), wage_base=self.SD_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py
new file mode 100644
index 00000000..54acfa9a
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsTNPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ TN_UNEMP_MAX_WAGE = 7000.00
+ TN_UNEMP = 2.7
+
+ def test_2020_taxes(self):
+ self._test_er_suta('TN', self.TN_UNEMP, date(2020, 1, 1), wage_base=self.TN_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
new file mode 100755
index 00000000..15e657ae
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
@@ -0,0 +1,100 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
+
+class TestUsTXPayslip(TestUsPayslip):
+ ###
+ # 2019 Taxes and Rates
+ ###
+ TX_UNEMP_MAX_WAGE = 9000.0
+ TX_UNEMP = -2.7 / 100.0
+ TX_OA = 0.0
+ TX_ETIA = -0.1 / 100.0
+
+ def test_2019_taxes(self):
+ salary = 5000.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('TX'),
+ )
+
+ self._log('2019 Texas tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(rules['ER_US_TX_SUTA'], salary * self.TX_UNEMP)
+ self.assertPayrollEqual(rules['ER_US_TX_SUTA_OA'], salary * self.TX_OA)
+ self.assertPayrollEqual(rules['ER_US_TX_SUTA_ETIA'], salary * self.TX_ETIA)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_tx_unemp_wages = self.TX_UNEMP_MAX_WAGE - salary if (self.TX_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Texas tax second payslip:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(rules['ER_US_TX_SUTA'], remaining_tx_unemp_wages * self.TX_UNEMP)
+
+ def test_2019_taxes_with_external(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('TX'),
+ external_wages=external_wages,
+ )
+
+ self._log('2019 Texas_external tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ expected_wage = self.TX_UNEMP_MAX_WAGE - external_wages
+ self.assertPayrollEqual(rules['ER_US_TX_SUTA'], expected_wage * self.TX_UNEMP)
+ self.assertPayrollEqual(rules['ER_US_TX_SUTA_OA'], expected_wage * self.TX_OA)
+ self.assertPayrollEqual(rules['ER_US_TX_SUTA_ETIA'], expected_wage * self.TX_ETIA)
+
+ def test_2019_taxes_with_state_exempt(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('TX'),
+ external_wages=external_wages,
+ futa_type=USHRContract.FUTA_TYPE_BASIC)
+
+ self._log('2019 Texas_external tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(rules.get('ER_US_TX_SUTA', 0.0), 0.0)
+ self.assertPayrollEqual(rules.get('ER_US_TX_SUTA_OA', 0.0), 0.0)
+ self.assertPayrollEqual(rules.get('ER_US_TX_SUTA_ETIA', 0.0), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2020.py
new file mode 100755
index 00000000..8dba312c
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2020.py
@@ -0,0 +1,17 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+class TestUsTXPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ TX_UNEMP_MAX_WAGE = 9000.0
+ TX_UNEMP = 2.7
+ TX_OA = 0.0
+ TX_ETIA = 0.1
+
+ def test_2020_taxes(self):
+ combined_rate = self.TX_UNEMP + self.TX_OA + self.TX_ETIA
+ self._test_er_suta('TX', combined_rate, date(2020, 1, 1), wage_base=self.TX_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2020.py
new file mode 100755
index 00000000..a6881acb
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsUTPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ UT_UNEMP_MAX_WAGE = 36600.0
+ UT_UNEMP = 0.1
+ # Calculation based on example https://tax.utah.gov/forms/pubs/pub-14.pdf
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('UT'),
+ ut_w4_sit_filing_status=filing_status,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('UT', self.UT_UNEMP, date(2020, 1, 1), wage_base=self.UT_UNEMP_MAX_WAGE)
+ self._test_sit(400, 'single', 0, 'weekly', date(2020, 1, 1), 16.00)
+ self._test_sit(1000, 'single', 0, 'bi-weekly', date(2020, 1, 1), 45.00)
+ self._test_sit(855, 'married', 0, 'semi-monthly', date(2020, 1, 1), 16.00)
+ self._test_sit(2500, 'married', 0, 'monthly', date(2020, 1, 1), 81.00)
+ self._test_sit(8000, 'head_household', 10, 'quarterly', date(2020, 1, 1), 397.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
new file mode 100644
index 00000000..b8f14393
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
@@ -0,0 +1,133 @@
+# 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
+from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
+
+
+class TestUsVaPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ VA_UNEMP_MAX_WAGE = 8000.0
+ VA_UNEMP = 2.51
+ VA_SIT_DEDUCTION = 4500.0
+ VA_SIT_EXEMPTION = 930.0
+ VA_SIT_OTHER_EXEMPTION = 800.0
+
+ def test_2019_taxes(self):
+ salary = 5000.0
+
+ # For formula from https://www.tax.virginia.gov/withholding-calculator
+ """
+ Key
+ G = Gross Pay for Pay Period P = Pay periods per year
+ A = Annualized gross pay E1 = Personal and Dependent Exemptions
+ T = Annualized taxable income E2 = Age 65 and Over & Blind Exemptions
+ WH = Tax to be withheld for pay period W = Annualized tax to be withheld
+ G x P - [$3000+ (E1 x 930) + (E2 x 800)] = T
+ Calculate W as follows:
+ If T is: W is:
+ Not over $3,000 2% of T
+ Over But Not Over Then
+ $3,000 $5,000 $60 + (3% of excess over $3,000)
+ $5,000 $17,000 $120 + (5% of excess over $5,000)
+ $17,000 $720 + (5.75% of excess over $17,000)
+ W / P = WH
+ """
+ e1 = 2
+ e2 = 0
+ t = salary * 12 - (self.VA_SIT_DEDUCTION + (e1 * self.VA_SIT_EXEMPTION) + (e2 * self.VA_SIT_OTHER_EXEMPTION))
+
+ if t <= 3000:
+ w = 0.02 * t
+ elif t <= 5000:
+ w = 60 + (0.03 * (t - 3000))
+ elif t <= 17000:
+ w = 120 + (0.05 * (t - 5000))
+ else:
+ w = 720 + (0.0575 * (t - 17000))
+
+ wh = w / 12
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('VA'),
+ va_va4_sit_exemptions=e1,
+ va_va4_sit_other_exemptions=e2
+ )
+
+ # tax rates
+ va_unemp = self.VA_UNEMP / -100.0
+
+ self._log('2019 Virginia tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * va_unemp)
+ self.assertPayrollEqual(cats['EE_US_SIT'], -wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_va_unemp_wages = self.VA_UNEMP_MAX_WAGE - salary if (self.VA_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Virginia 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_va_unemp_wages * va_unemp)
+
+ def test_2019_taxes_with_external(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('VA'),
+ external_wages=external_wages,
+ )
+
+ # tax rates
+ va_unemp = self.VA_UNEMP / -100.0
+
+ self._log('2019 Virginia_external tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], (self.VA_UNEMP_MAX_WAGE - external_wages) * va_unemp)
+
+ def test_2019_taxes_with_state_exempt(self):
+ salary = 5000.0
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('VA'),
+ external_wages=external_wages,
+ futa_type=USHRContract.FUTA_TYPE_BASIC)
+
+ # tax rates
+ self._log('2019 Virginia exempt tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2020.py
new file mode 100644
index 00000000..012e4845
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2020.py
@@ -0,0 +1,116 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsVaPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ VA_UNEMP_MAX_WAGE = 8000.0
+ VA_UNEMP = 2.51
+ VA_SIT_DEDUCTION = 4500.0
+ VA_SIT_EXEMPTION = 930.0
+ VA_SIT_OTHER_EXEMPTION = 800.0
+
+ def _run_test_sit(self,
+ wage=0.0,
+ schedule_pay='monthly',
+ filing_status='single',
+ dependent_credit=0.0,
+ other_income=0.0,
+ deductions=0.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=False,
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=0,
+ va_va4_sit_other_exemptions=0,
+ expected=0.0,
+ ):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ schedule_pay=schedule_pay,
+ fed_941_fit_w4_is_nonresident_alien=is_nonresident_alien,
+ fed_941_fit_w4_filing_status=filing_status,
+ fed_941_fit_w4_multiple_jobs_higher=False,
+ fed_941_fit_w4_dependent_credit=dependent_credit,
+ fed_941_fit_w4_other_income=other_income,
+ fed_941_fit_w4_deductions=deductions,
+ fed_941_fit_w4_additional_withholding=additional_withholding,
+ state_income_tax_exempt=state_income_tax_exempt,
+ state_income_tax_additional_withholding=state_income_tax_additional_withholding,
+ va_va4_sit_exemptions=va_va4_sit_exemptions,
+ va_va4_sit_other_exemptions=va_va4_sit_other_exemptions,
+ state_id=self.get_us_state('VA'),
+ )
+ payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ # Instead of PayrollEqual after initial first round of testing.
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected)
+ return payslip
+
+ def test_2020_taxes(self):
+ self._test_er_suta('VA', self.VA_UNEMP, date(2020, 1, 1), wage_base=self.VA_UNEMP_MAX_WAGE)
+
+ salary = 5000.0
+
+ # For formula from https://www.tax.virginia.gov/withholding-calculator
+ e1 = 2
+ e2 = 0
+ t = salary * 12 - (self.VA_SIT_DEDUCTION + (e1 * self.VA_SIT_EXEMPTION) + (e2 * self.VA_SIT_OTHER_EXEMPTION))
+
+ if t <= 3000:
+ w = 0.02 * t
+ elif t <= 5000:
+ w = 60 + (0.03 * (t - 3000))
+ elif t <= 17000:
+ w = 120 + (0.05 * (t - 5000))
+ else:
+ w = 720 + (0.0575 * (t - 17000))
+
+ wh = w / 12
+
+ self._run_test_sit(wage=salary,
+ schedule_pay='monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=e1,
+ va_va4_sit_other_exemptions=e2,
+ expected=wh,)
+ self.assertPayrollEqual(wh, 235.57) # To test against calculator
+
+ # Below expected comes from the calculator linked above
+ self._run_test_sit(wage=450.0,
+ schedule_pay='weekly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=3,
+ va_va4_sit_other_exemptions=1,
+ expected=12.22,)
+ self._run_test_sit(wage=2500.0,
+ schedule_pay='bi-weekly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=1,
+ va_va4_sit_other_exemptions=0,
+ expected=121.84,)
+ self._run_test_sit(wage=10000.0,
+ schedule_pay='semi-monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=100.0,
+ va_va4_sit_exemptions=0,
+ va_va4_sit_other_exemptions=1,
+ expected=651.57,)
+
+ # Test exempt
+ self._run_test_sit(wage=2400.0,
+ schedule_pay='monthly',
+ state_income_tax_exempt=True,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=1,
+ va_va4_sit_other_exemptions=1,
+ expected=0.0,)
diff --git a/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.py
new file mode 100755
index 00000000..7807bed7
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsVTPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ VT_UNEMP_MAX_WAGE = 16100.0
+ VT_UNEMP = 1.0
+ # Calculation based on example https://tax.vermont.gov/sites/tax/files/documents/WithholdingInstructions.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('VT'),
+ vt_w4vt_sit_filing_status=filing_status,
+ vt_w4vt_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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('VT', self.VT_UNEMP, date(2020, 1, 1), wage_base=self.VT_UNEMP_MAX_WAGE)
+ self._test_sit(1800, 'married', 2, 0, False, 'weekly', date(2020, 1, 1), 53.73)
+ self._test_sit(1800, 'married', 2, 10, False, 'weekly', date(2020, 1, 1), 63.73)
+ self._test_sit(1000, 'single', 1, 0, True, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(8000, 'single', 1, 10, False, 'bi-weekly', date(2020, 1, 1), 506.58)
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..b67f69c6
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
@@ -0,0 +1,92 @@
+# 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.rule.parameter'].create({
+ 'name': 'Test LNI EE',
+ 'code': 'test_lni_ee',
+ 'parameter_version_ids': [(0, 0, {
+ 'date_from': date(2019, 1, 1),
+ 'parameter_value': str(self.test_ee_lni * 100),
+ })],
+ })
+ self.parameter_lni_er = self.env['hr.rule.parameter'].create({
+ 'name': 'Test LNI ER',
+ 'code': 'test_lni_er',
+ 'parameter_version_ids': [(0, 0, {
+ '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))
+ # 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..509e19b1
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2020.py
@@ -0,0 +1,90 @@
+# 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.rule.parameter'].create({
+ 'name': 'Test LNI EE',
+ 'code': 'test_lni_ee',
+ 'parameter_version_ids': [(0, 0, {
+ 'date_from': date(2020, 1, 1),
+ 'parameter_value': str(self.test_ee_lni * 100),
+ })],
+ })
+ self.parameter_lni_er = self.env['hr.rule.parameter'].create({
+ 'name': 'Test LNI ER',
+ 'code': 'test_lni_er',
+ 'parameter_version_ids': [(0, 0, {
+ 'date_from': date(2020, 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))
+ # 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/tests/test_us_wi_wisconsin_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2020.py
new file mode 100755
index 00000000..32bdfa30
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2020.py
@@ -0,0 +1,39 @@
+# 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 TestUsWIPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ WI_UNEMP_MAX_WAGE = 14000.0
+ WI_UNEMP = 3.05
+ # Calculation based on example https://www.revenue.wi.gov/DOR%20Publications/pb166.pdf
+
+ def _test_sit(self, wage, filing_status, exemption, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('WI'),
+ wi_wt4_sit_filing_status=filing_status,
+ wi_wt4_sit_exemptions=exemption,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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_2020_taxes_example(self):
+ self._test_er_suta('WI', self.WI_UNEMP, date(2020, 1, 1), wage_base=self.WI_UNEMP_MAX_WAGE)
+ self._test_sit(300, 'single', 1, 0, False, 'weekly', date(2020, 1, 1), 7.21)
+ self._test_sit(700, 'married', 3, 0, False, 'bi-weekly', date(2020, 1, 1), 13.35)
+ self._test_sit(7000, 'single', 1, 10, True, 'bi-weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(10000, 'married', 3, 10, False, 'bi-weekly', date(2020, 1, 1), 633.65)
+ # ((48000 - 26227) * (7.0224 /100) + 1073.55 - 44) / 12
+ self._test_sit(4000, 'single', 2, 0, False, 'monthly', date(2020, 1, 1), 213.21)
diff --git a/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2020.py
new file mode 100755
index 00000000..acef111e
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2020.py
@@ -0,0 +1,36 @@
+# 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 TestUsWVPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ WV_UNEMP_MAX_WAGE = 12000.0
+ WV_UNEMP = 2.7
+ # Calculation based on example https://tax.wv.gov/Documents/TaxForms/it100.1a.pdf
+
+ def _test_sit(self, wage, filing_status, exemption, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('WV'),
+ wv_it104_sit_filing_status=filing_status,
+ wv_it104_sit_exemptions=exemption,
+ 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('WV', self.WV_UNEMP, date(2020, 1, 1), wage_base=self.WV_UNEMP_MAX_WAGE)
+ self._test_sit(1250, 'married', 2, 0, 'semi-monthly', date(2020, 1, 1), 44.00)
+ self._test_sit(1300, 'single', 1, 0, 'bi-weekly', date(2020, 1, 1), 46.00)
+ self._test_sit(1300, 'single', 1, 10, 'bi-weekly', date(2020, 1, 1), 56.00)
+ self._test_sit(15000, 'single', 2, 0, 'monthly', date(2020, 1, 1), 860.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
new file mode 100644
index 00000000..a8fa3df8
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
@@ -0,0 +1,58 @@
+# 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 TestUsWYPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ WY_UNEMP_MAX_WAGE = 25400
+ WY_UNEMP = -2.10 / 100.0
+
+ def test_2019_taxes(self):
+ salary = 15000.00
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('WY'))
+
+ self._log('2019 Wyoming tax first payslip:')
+ 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.WY_UNEMP)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_wy_unemp_wages = self.WY_UNEMP_MAX_WAGE - salary if (self.WY_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Wyoming 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_wy_unemp_wages * self.WY_UNEMP)
+
+ def test_2019_taxes_with_external(self):
+ # Wage is the cap itself, 25400
+ # so salary is equal to self.WY_UNEMP
+ salary = 25400
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('WY'))
+
+ self._log('2019 Wyoming External tax first payslip:')
+ 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.WY_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py
new file mode 100644
index 00000000..b6ca4482
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsWYPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ WY_UNEMP_MAX_WAGE = 26400.00
+ WY_UNEMP = 8.5
+
+ def test_2020_taxes(self):
+ self._test_er_suta('WY', self.WY_UNEMP, date(2020, 1, 1), wage_base=self.WY_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/views/res_config_settings_views.xml b/l10n_us_hr_payroll/views/res_config_settings_views.xml
new file mode 100644
index 00000000..3c69b42f
--- /dev/null
+++ b/l10n_us_hr_payroll/views/res_config_settings_views.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ res.config.settings.view.form.inherit
+ res.config.settings
+
+
+
+
+
+
+
Payslip Sum Behavior
+
+ Customize the behavior of what payslips are eligible when summing over date ranges in rules.
+ Generally, "Date To" or "Accounting Date" would be preferred in the United States and anywhere
+ else where the ending date on the payslip is used to calculate wage bases.
+
+
+
+
+
+
+
+
+
+
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 dfe0e301..4b38115f 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,266 @@
+ State Information and Extra
+
+
+
+
+
+
+ Form A4 - State Income Tax
+
+
+
+
+
+
+ Form AR4EC - State Income Tax
+
+
+
+
+
+ Form A-4 - State Income Tax
+
+
+
+
+ Form W-4 - State Income Tax
+
+
+
+
+
+
+ Form W-4 - State Income Tax
+
+
+
+
+ Form DE W-4 - State Income Tax
+
+
+
+
+
+ Form CT-W4 - State Income Tax
+
+
+
+
+ No additional fields.
+
+
+ Form G-4 - State Income Tax
+
+
+
+
+
+
+
+ Form HI HW-4 - State Income Tax
+
+
+
+
+
+ Form IA W-4 - State Income Tax
+
+
+
+
+
+ Form ID W-4 - State Income Tax
+
+
+
+
+ Form IL-W-4 - State Income Tax
+
+
+
+
+
+ Form IN WH-4 - State Income Tax
+
+
+
+
+
+ Form KS K-4 - State Income Tax
+
+
+
+
+
+
+ No additional fields.
+
+
+
+
+ Form LA L-4 - State Income Tax
+
+
+
+
+
+
+ Form W-4ME - State Income Tax
+
+
+
+
+
+
+ Form MI-W4 - State Income Tax
+
+
+
+
+
+ Form W-4MN - State Income Tax
+
+
+
+
+
+ Form MO W-4 - State Income Tax
+
+
+
+
+
+ Form 89-350 - State Income Tax
+
+
+
+
+
+ Form MT-4 - State Income Tax
+
+
+
+
+
+ Form NC-4 - State Income Tax
+
+
+
+
+
+ Form ND W-4 - State Income Tax
+
+
+
+
+ Form NC-4 - State Income Tax
+
+
+
+
+
+
+ No additional fields.
+
+
+ Form NJ-W4 - State Income Tax
+
+
+
+
+
+
+ Form NM W-4 - State Income Tax
+
+
+
+ No additional fields.
+
+
+ Form NY IT-2104 - State Income Tax
+
+
+
+
+
+ Form IT-4 - State Income Tax
+
+
+
+
+
+ Form OK-W-4 - State Income Tax
+
+
+
+
+
+
+
+
+
+
+ Form RI W-4 - State Income Tax
+
+
+
+
+
+ Form SC W-4 - State Income Tax
+
+
+
+
+
+ No additional fields.
+
+
+ No additional fields.
+
+
+ No additional fields.
+
+
+ Form UT W-4 - State Income Tax
+
+
+
+
+ Form VT W-4VT - State Income Tax
+
+
+
+
+
+
+ Form VA-4/VA-4P - State Income Tax
+
+
+
+
+
+
+ No additional fields.
+ Ensure that your Employee and Employer workers' comp code fields are filled in for WA LNI withholding.
+
+
+ Form WV/IT-104 - State Income Tax
+
+
+
+
+
+ Form WT-4 - State Income Tax
+
+
+
+
+
+
+ No additional fields.
From fe423e0d0b32b41a48ffedd24e84cc6e9fa1313f Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Sat, 21 Nov 2020 16:57:52 -0800
Subject: [PATCH 03/12] [MIG] l10n_us_hr_payroll: to Odoo 14.0
Require `hr_payroll_hibou`, remove a lot of 'fixes' and stuff that is useful for 'all' payroll.
Failing tests:
Deleware 2020 is low by ~0.1% in SIT only.
Pennsylvania 2019, 2020 is low by ~0.01% in SIT only.
I did not try to correct, only verify that the data and calculation is the same between versions. (which it is)
Washington 2019, 2020 hours was off (183.99972222222223 != 184)
Fixed by turning it into assertAlmostEqual
---
l10n_us_hr_payroll/__manifest__.py | 7 +-
l10n_us_hr_payroll/data/base.xml | 11 +-
l10n_us_hr_payroll/models/__init__.py | 2 -
l10n_us_hr_payroll/models/browsable_object.py | 148 ------------------
l10n_us_hr_payroll/models/hr_contract.py | 10 --
l10n_us_hr_payroll/models/hr_payslip.py | 4 -
.../models/res_config_settings.py | 24 ---
l10n_us_hr_payroll/tests/__init__.py | 1 +
l10n_us_hr_payroll/tests/common.py | 117 ++------------
l10n_us_hr_payroll/tests/test_special.py | 116 +-------------
.../tests/test_us_payslip_2020.py | 12 +-
.../test_us_wa_washington_payslip_2019.py | 2 +-
.../test_us_wa_washington_payslip_2020.py | 2 +-
.../views/res_config_settings_views.xml | 32 ----
14 files changed, 29 insertions(+), 459 deletions(-)
delete mode 100644 l10n_us_hr_payroll/models/browsable_object.py
delete mode 100644 l10n_us_hr_payroll/models/res_config_settings.py
delete mode 100644 l10n_us_hr_payroll/views/res_config_settings_views.xml
diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py
index 32c55af1..b59efd56 100644
--- a/l10n_us_hr_payroll/__manifest__.py
+++ b/l10n_us_hr_payroll/__manifest__.py
@@ -3,12 +3,10 @@
{
'name': 'United States of America - Payroll',
'author': 'Hibou Corp. ',
- 'version': '13.0.2020.0.0',
+ 'version': '14.0.2020.0.0',
'category': 'Payroll Localization',
'depends': [
- 'hr_payroll',
- 'hr_contract_reports',
- 'hibou_professional',
+ 'hr_payroll_hibou',
],
'description': """
United States of America - Payroll Rules.
@@ -74,7 +72,6 @@ United States of America - Payroll Rules.
'data/state/wv_west_virginia.xml',
'data/state/wy_wyoming.xml',
'views/hr_contract_views.xml',
- 'views/res_config_settings_views.xml',
'views/us_payroll_config_views.xml',
],
'demo': [
diff --git a/l10n_us_hr_payroll/data/base.xml b/l10n_us_hr_payroll/data/base.xml
index 2e29934e..0ad1d5c5 100644
--- a/l10n_us_hr_payroll/data/base.xml
+++ b/l10n_us_hr_payroll/data/base.xml
@@ -7,14 +7,19 @@
+
+
USA Employee Standard
-
+ (4, ref('hr_work_entry_contract.work_entry_type_unpaid_leave')),
+ ]"/>
+
+
+
+
diff --git a/l10n_us_hr_payroll/models/__init__.py b/l10n_us_hr_payroll/models/__init__.py
index bb27e002..71d2ad33 100644
--- a/l10n_us_hr_payroll/models/__init__.py
+++ b/l10n_us_hr_payroll/models/__init__.py
@@ -1,8 +1,6 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-from . import browsable_object
from . import hr_contract
from . import hr_payslip
-from . import res_config_settings
from . import update
from . import us_payroll_config
diff --git a/l10n_us_hr_payroll/models/browsable_object.py b/l10n_us_hr_payroll/models/browsable_object.py
deleted file mode 100644
index bed067fd..00000000
--- a/l10n_us_hr_payroll/models/browsable_object.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from odoo import fields
-from odoo.addons.hr_payroll.models import browsable_object
-
-
-class BrowsableObject(object):
- def __init__(self, employee_id, dict, env):
- self.employee_id = employee_id
- self.dict = dict
- self.env = env
- # Customization to allow changing the behavior of the discrete browsable objects.
- # you can think of this as 'compiling' the query based on the configuration.
- sum_field = env['ir.config_parameter'].sudo().get_param('hr_payroll.payslip.sum_behavior', 'date_from')
- if sum_field == 'date' and 'date' not in env['hr.payslip']:
- # missing attribute, closest by definition
- sum_field = 'date_to'
- if not sum_field:
- sum_field = 'date_from'
- self._compile_browsable_query(sum_field)
-
- def __getattr__(self, attr):
- return attr in self.dict and self.dict.__getitem__(attr) or 0.0
-
- def _compile_browsable_query(self, sum_field):
- pass
-
-
-class InputLine(BrowsableObject):
- """a class that will be used into the python code, mainly for usability purposes"""
- def _compile_browsable_query(self, sum_field):
- self.__browsable_query = """
- SELECT sum(amount) as sum
- FROM hr_payslip as hp, hr_payslip_input as pi
- WHERE hp.employee_id = %s AND hp.state = 'done'
- AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""".format(sum_field=sum_field)
-
- def sum(self, code, from_date, to_date=None):
- if to_date is None:
- to_date = fields.Date.today()
- self.env.cr.execute(self.__browsable_query, (self.employee_id, from_date, to_date, code))
- return self.env.cr.fetchone()[0] or 0.0
-
-
-class WorkedDays(BrowsableObject):
- """a class that will be used into the python code, mainly for usability purposes"""
- def _compile_browsable_query(self, sum_field):
- self.__browsable_query = """
- SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours
- FROM hr_payslip as hp, hr_payslip_worked_days as pi
- WHERE hp.employee_id = %s AND hp.state = 'done'
- AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""".format(sum_field=sum_field)
-
- def _sum(self, code, from_date, to_date=None):
- if to_date is None:
- to_date = fields.Date.today()
- self.env.cr.execute(self.__browsable_query, (self.employee_id, from_date, to_date, code))
- return self.env.cr.fetchone()
-
- def sum(self, code, from_date, to_date=None):
- res = self._sum(code, from_date, to_date)
- return res and res[0] or 0.0
-
- def sum_hours(self, code, from_date, to_date=None):
- res = self._sum(code, from_date, to_date)
- return res and res[1] or 0.0
-
-
-class Payslips(BrowsableObject):
- """a class that will be used into the python code, mainly for usability purposes"""
- def _compile_browsable_query(self, sum_field):
- # Note that the core odoo has this as `hp.credit_note = False` but what if it is NULL?
- # reverse of the desired behavior.
- self.__browsable_query_rule = """
- SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
- FROM hr_payslip as hp, hr_payslip_line as pl
- WHERE hp.employee_id = %s AND hp.state = 'done'
- AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""".format(sum_field=sum_field)
- # Original (non-recursive)
- # self.__browsable_query_category = """
- # SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
- # FROM hr_payslip as hp, hr_payslip_line as pl, hr_salary_rule_category as rc
- # WHERE hp.employee_id = %s AND hp.state = 'done'
- # AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id
- # AND rc.id = pl.category_id AND rc.code = %s""".format(sum_field=sum_field)
-
- # Hibou Recursive version
- self.__browsable_query_category = """
- WITH RECURSIVE
- category_by_code as (
- SELECT id
- FROM hr_salary_rule_category
- WHERE code = %s
- ),
- category_ids as (
- SELECT COALESCE((SELECT id FROM category_by_code), -1) AS id
- UNION ALL
- SELECT rc.id
- FROM hr_salary_rule_category AS rc
- JOIN category_ids AS rcs ON rcs.id = rc.parent_id
- )
-
- SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
- FROM hr_payslip as hp, hr_payslip_line as pl
- WHERE hp.employee_id = %s AND hp.state = 'done'
- AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id
- AND pl.category_id in (SELECT id from category_ids)""".format(sum_field=sum_field)
-
- def sum(self, code, from_date, to_date=None):
- if to_date is None:
- to_date = fields.Date.today()
- self.env.cr.execute(self.__browsable_query_rule, (self.employee_id, from_date, to_date, code))
- res = self.env.cr.fetchone()
- return res and res[0] or 0.0
-
- def rule_parameter(self, code):
- return self.env['hr.rule.parameter']._get_parameter_from_code(code, self.dict.date_to)
-
- def sum_category(self, code, from_date, to_date=None):
- if to_date is None:
- to_date = fields.Date.today()
-
- self.env['hr.payslip'].flush(['credit_note', 'employee_id', 'state', 'date_from', 'date_to'])
- self.env['hr.payslip.line'].flush(['total', 'slip_id', 'category_id'])
- self.env['hr.salary.rule.category'].flush(['code'])
-
- # standard version
- # self.env.cr.execute(self.__browsable_query_category, (self.employee_id, from_date, to_date, code))
- # recursive category version
- self.env.cr.execute(self.__browsable_query_category, (code, self.employee_id, from_date, to_date))
- res = self.env.cr.fetchone()
- return res and res[0] or 0.0
-
- @property
- def paid_amount(self):
- return self.dict._get_paid_amount()
-
-
-# Patch over Core
-browsable_object.BrowsableObject.__init__ = BrowsableObject.__init__
-browsable_object.BrowsableObject._compile_browsable_query = BrowsableObject._compile_browsable_query
-browsable_object.InputLine._compile_browsable_query = InputLine._compile_browsable_query
-browsable_object.InputLine.sum = InputLine.sum
-browsable_object.WorkedDays._compile_browsable_query = WorkedDays._compile_browsable_query
-browsable_object.WorkedDays.sum = WorkedDays.sum
-browsable_object.Payslips._compile_browsable_query = Payslips._compile_browsable_query
-browsable_object.Payslips.sum = Payslips.sum
-browsable_object.Payslips.sum_category = Payslips.sum_category
diff --git a/l10n_us_hr_payroll/models/hr_contract.py b/l10n_us_hr_payroll/models/hr_contract.py
index ab5772c8..323d37f7 100644
--- a/l10n_us_hr_payroll/models/hr_contract.py
+++ b/l10n_us_hr_payroll/models/hr_contract.py
@@ -6,16 +6,6 @@ from .us_payroll_config import FUTA_TYPE_NORMAL, \
FUTA_TYPE_EXEMPT
-class HrPayrollStructureType(models.Model):
- _inherit = 'hr.payroll.structure.type'
- default_schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
-
-
-class HrPayrollStructure(models.Model):
- _inherit = 'hr.payroll.structure'
- schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
-
-
class USHRContract(models.Model):
_inherit = 'hr.contract'
diff --git a/l10n_us_hr_payroll/models/hr_payslip.py b/l10n_us_hr_payroll/models/hr_payslip.py
index e5a728f2..1779e03a 100644
--- a/l10n_us_hr_payroll/models/hr_payslip.py
+++ b/l10n_us_hr_payroll/models/hr_payslip.py
@@ -124,9 +124,5 @@ class HRPayslip(models.Model):
})
return res
- def get_year(self):
- # Helper method to get the year (normalized between Odoo Versions)
- return self.date_to.year
-
def get_pay_periods_in_year(self):
return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)
diff --git a/l10n_us_hr_payroll/models/res_config_settings.py b/l10n_us_hr_payroll/models/res_config_settings.py
deleted file mode 100644
index 05af9430..00000000
--- a/l10n_us_hr_payroll/models/res_config_settings.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from odoo import fields, models
-
-
-class ResConfigSettings(models.TransientModel):
- _inherit = 'res.config.settings'
-
- payslip_sum_type = fields.Selection([
- ('date_from', 'Date From'),
- ('date_to', 'Date To'),
- ('date', 'Accounting Date'),
- ], 'Payslip Sum Behavior', help="Behavior for what payslips are considered "
- "during rule execution. Stock Odoo behavior "
- "would not consider a payslip starting on 2019-12-30 "
- "ending on 2020-01-07 when summing a 2020 payslip category.\n\n"
- "Accounting Date requires Payroll Accounting and will "
- "fall back to date_to as the 'closest behavior'.",
- config_parameter='hr_payroll.payslip.sum_behavior')
-
- def set_values(self):
- super(ResConfigSettings, self).set_values()
- self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior',
- self.payslip_sum_type or 'date_from')
diff --git a/l10n_us_hr_payroll/tests/__init__.py b/l10n_us_hr_payroll/tests/__init__.py
index a7c95ebf..0dcf26fb 100755
--- a/l10n_us_hr_payroll/tests/__init__.py
+++ b/l10n_us_hr_payroll/tests/__init__.py
@@ -7,6 +7,7 @@ from . import test_special
from . import test_us_payslip_2019
from . import test_us_payslip_2020
+
from . import test_us_ak_alaska_payslip_2019
from . import test_us_ak_alaska_payslip_2020
diff --git a/l10n_us_hr_payroll/tests/common.py b/l10n_us_hr_payroll/tests/common.py
index 07615652..0fbb410a 100755
--- a/l10n_us_hr_payroll/tests/common.py
+++ b/l10n_us_hr_payroll/tests/common.py
@@ -1,60 +1,26 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-from logging import getLogger
-from sys import float_info as sys_float_info
-from collections import defaultdict
from datetime import timedelta
-from odoo.tests import common
-from odoo.tools.float_utils import float_round as odoo_float_round
from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
-
-def process_payslip(payslip):
- try:
- payslip.action_payslip_done()
- except AttributeError:
- # v9
- payslip.process_sheet()
+from odoo.addons.hr_payroll_hibou.tests import common
-class TestUsPayslip(common.TransactionCase):
- debug = False
- _logger = getLogger(__name__)
+process_payslip = common.process_payslip
+
+
+class TestUsPayslip(common.TestPayslip):
def setUp(self):
super(TestUsPayslip, self).setUp()
- self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior', 'date_to')
- self.structure_type_id = self.ref('l10n_us_hr_payroll.structure_type_employee')
- self.resource_calendar_id = self.ref('resource.resource_calendar_std')
-
- float_info = sys_float_info
-
- def float_round(self, value, digits):
- return odoo_float_round(value, digits)
-
- _payroll_digits = -1
-
- @property
- def payroll_digits(self):
- if self._payroll_digits == -1:
- self._payroll_digits = self.env['decimal.precision'].precision_get('Payroll')
- return self._payroll_digits
-
- def _log(self, message):
- if self.debug:
- self._logger.warn(message)
-
- def _createEmployee(self):
- return self.env['hr.employee'].create({
- 'birthday': '1985-03-14',
- 'country_id': self.ref('base.us'),
- 'department_id': self.ref('hr.dep_rd'),
- 'gender': 'male',
- 'name': 'Jared'
- })
+ self.structure_type = self.env.ref('l10n_us_hr_payroll.structure_type_employee')
+ self.structure = self.env.ref('l10n_us_hr_payroll.hr_payroll_structure')
+ # self.structure_type.default_struct_id = self.structure
+ self._log('US structue_type %s and structure %s' % (self.structure_type, self.structure))
def _createContract(self, employee, **kwargs):
+ # Override
if not 'schedule_pay' in kwargs:
kwargs['schedule_pay'] = 'monthly'
schedule_pay = kwargs['schedule_pay']
@@ -90,71 +56,14 @@ class TestUsPayslip(common.TransactionCase):
# US Payroll Config Defaults Should be set on the Model
config = config_model.create(config_values)
contract_values['us_payroll_config_id'] = config.id
-
- # Some Basic Defaults
- if not contract_values.get('state'):
- contract_values['state'] = 'open' # Running
- if not contract_values.get('structure_type_id'):
- contract_values['structure_type_id'] = self.structure_type_id
- if not contract_values.get('date_start'):
- contract_values['date_start'] = '2016-01-01'
- if not contract_values.get('date_end'):
- contract_values['date_end'] = '2030-12-31'
- if not contract_values.get('resource_calendar_id'):
- contract_values['resource_calendar_id'] = self.resource_calendar_id
-
- # Compatibility with earlier Odoo versions
- if not contract_values.get('journal_id') and hasattr(contract_model, 'journal_id'):
- try:
- contract_values['journal_id'] = self.env['account.journal'].search([('type', '=', 'general')], limit=1).id
- except KeyError:
- # Accounting not installed
- pass
-
+ self._get_contract_defaults(contract_values)
+ self._log('creating contract with finial values: %s' % (contract_values, ))
contract = contract_model.create(contract_values)
- # Compatibility with Odoo 13
+ # Compatibility with Odoo 13/14
contract.structure_type_id.default_struct_id.schedule_pay = schedule_pay
return contract
- def _createPayslip(self, employee, date_from, date_to):
- slip = self.env['hr.payslip'].create({
- 'name': 'Test %s From: %s To: %s' % (employee.name, date_from, date_to),
- 'employee_id': employee.id,
- 'date_from': date_from,
- 'date_to': date_to
- })
- slip._onchange_employee()
- return slip
-
- def _getCategories(self, payslip):
- categories = defaultdict(float)
- for line in payslip.line_ids:
- self._log(' line code: ' + str(line.code) +
- ' category code: ' + line.category_id.code +
- ' total: ' + str(line.total) +
- ' rate: ' + str(line.rate) +
- ' amount: ' + str(line.amount))
- category_id = line.category_id
- category_code = line.category_id.code
- while category_code:
- categories[category_code] += line.total
- category_id = category_id.parent_id
- category_code = category_id.code
- return categories
-
- def _getRules(self, payslip):
- rules = defaultdict(float)
- for line in payslip.line_ids:
- rules[line.code] += line.total
- return rules
-
- 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 get_us_state(self, code, cache={}):
country_key = 'US_COUNTRY'
if code in cache:
diff --git a/l10n_us_hr_payroll/tests/test_special.py b/l10n_us_hr_payroll/tests/test_special.py
index 688761ec..9fd9bdea 100644
--- a/l10n_us_hr_payroll/tests/test_special.py
+++ b/l10n_us_hr_payroll/tests/test_special.py
@@ -2,118 +2,4 @@ from .common import TestUsPayslip, process_payslip
class TestSpecial(TestUsPayslip):
- def test_semi_monthly(self):
- salary = 80000.0
- employee = self._createEmployee()
- # so the schedule_pay is now on the Structure...
- contract = self._createContract(employee, wage=salary, schedule_pay='semi-monthly')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-14')
- payslip.compute_sheet()
-
- def test_payslip_sum_behavior(self):
- us_structure = self.env.ref('l10n_us_hr_payroll.hr_payroll_structure')
- rule_category_comp = self.env.ref('hr_payroll.COMP')
- test_rule_category = self.env['hr.salary.rule.category'].create({
- 'name': 'Test Sum Behavior',
- 'code': 'test_sum_behavior',
- 'parent_id': rule_category_comp.id,
- })
- test_rule = self.env['hr.salary.rule'].create({
- 'sequence': 450,
- 'struct_id': us_structure.id,
- 'category_id': test_rule_category.id,
- 'name': 'Test Sum Behavior',
- 'code': 'test_sum_behavior',
- 'condition_select': 'python',
- 'condition_python': 'result = 1',
- 'amount_select': 'code',
- 'amount_python_compute': '''
-ytd_category = payslip.sum_category('test_sum_behavior', '2020-01-01', '2021-01-01')
-ytd_rule = payslip.sum('test_sum_behavior', '2020-01-01', '2021-01-01')
-result = 0.0
-if ytd_category != ytd_rule:
- # error
- result = -1.0
-elif ytd_rule == 0.0:
- # first payslip in period
- result = 1.0
-'''
- })
- salary = 80000.0
- employee = self._createEmployee()
- contract = self._createContract(employee, wage=salary, schedule_pay='bi-weekly')
- payslip = self._createPayslip(employee, '2019-12-30', '2020-01-12')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertEqual(cats['test_sum_behavior'], 1.0)
- process_payslip(payslip)
-
- # Basic date_from behavior.
- self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior', 'date_from')
- # The the date_from on the last payslip will not be found
- payslip = self._createPayslip(employee, '2020-01-13', '2020-01-27')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertEqual(cats['test_sum_behavior'], 1.0)
-
- # date_to behavior.
- self.env['ir.config_parameter'].set_param('hr_payroll.payslip.sum_behavior', 'date_to')
- # The date_to on the last payslip is found
- payslip = self._createPayslip(employee, '2020-01-13', '2020-01-27')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertEqual(cats['test_sum_behavior'], 0.0)
-
- def test_recursive_salary_rule_category(self):
- # self.debug = True
- us_structure = self.env.ref('l10n_us_hr_payroll.hr_payroll_structure')
- # In this scenario, you are in rule code that will check for the category
- # and a subcategory will also
- alw_category = self.env.ref('hr_payroll.ALW')
- ded_category = self.env.ref('hr_payroll.DED')
- test_category = self.env['hr.salary.rule.category'].create({
- 'name': 'Special ALW',
- 'code': 'ALW_SPECIAL_RECURSIVE',
- 'parent_id': alw_category.id,
- })
- test_special_alw = self.env['hr.salary.rule'].create({
- 'name': 'Flat amount 200',
- 'code': 'ALW_SPECIAL_RECURSIVE',
- 'category_id': test_category.id,
- 'condition_select': 'none',
- 'amount_select': 'fix',
- 'amount_fix': 200.0,
- 'struct_id': us_structure.id,
- })
- test_recursion = self.env['hr.salary.rule'].create({
- 'name': 'Actual Test Behavior',
- 'code': 'RECURSION_TEST',
- 'category_id': ded_category.id,
- 'condition_select': 'none',
- 'amount_select': 'code',
- 'amount_python_compute': """
-# this rule will always be the total of the ALW category and YTD ALW category
-result = categories.ALW
-year = payslip.dict.get_year()
-result += payslip.sum_category('ALW', str(year) + '-01-01', str(year+1) + '-01-01')
- """,
- 'sequence': 101,
- 'struct_id': us_structure.id,
- })
-
- salary = 80000.0
- employee = self._createEmployee()
- contract = self._createContract(employee, wage=salary, schedule_pay='bi-weekly')
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-14')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
- self.assertEqual(rules['RECURSION_TEST'], 200.0)
- process_payslip(payslip)
-
- payslip = self._createPayslip(employee, '2020-01-15', '2020-01-27')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
- # two hundred is in the YTD ALW
- self.assertEqual(rules['RECURSION_TEST'], 200.0 + 200.0)
+ pass
diff --git a/l10n_us_hr_payroll/tests/test_us_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_payslip_2020.py
index c10a230b..2bc61b65 100644
--- a/l10n_us_hr_payroll/tests/test_us_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_payslip_2020.py
@@ -30,7 +30,6 @@ class TestUsPayslip2020(TestUsPayslip):
###
def test_2020_taxes(self):
- self.debug = False
# salary is high so that second payslip runs over max
# social security salary
salary = 80000.0
@@ -42,17 +41,16 @@ class TestUsPayslip2020(TestUsPayslip):
self._log('2019 tax last slip')
payslip = self._createPayslip(employee, '2019-12-01', '2019-12-31')
- payslip.compute_sheet()
+ self.assertEqual(payslip.contract_id, contract)
self._log(payslip.read())
process_payslip(payslip)
# Ensure amounts are there, they shouldn't be added in the next year...
cats = self._getCategories(payslip)
- self.assertTrue(cats['ER_US_940_FUTA'], self.FUTA_MAX_WAGE * self.FUTA)
+ self.assertTrue(cats['ER_US_940_FUTA'], ' Value should be well above whatever was available that year!')
self._log('2020 tax first payslip:')
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
- payslip.compute_sheet()
cats = self._getCategories(payslip)
rules = self._getRules(payslip)
@@ -74,8 +72,6 @@ class TestUsPayslip2020(TestUsPayslip):
self._log('2020 tax second payslip:')
payslip = self._createPayslip(employee, '2020-02-01', '2020-02-28')
- payslip.compute_sheet()
-
cats = self._getCategories(payslip)
rules = self._getRules(payslip)
@@ -90,8 +86,6 @@ class TestUsPayslip2020(TestUsPayslip):
self._log('2020 tax third payslip:')
payslip = self._createPayslip(employee, '2020-03-01', '2020-03-31')
- payslip.compute_sheet()
-
cats = self._getCategories(payslip)
rules = self._getRules(payslip)
@@ -104,8 +98,6 @@ class TestUsPayslip2020(TestUsPayslip):
self._log('2020 tax fourth payslip:')
payslip = self._createPayslip(employee, '2020-04-01', '2020-04-30')
- payslip.compute_sheet()
-
cats = self._getCategories(payslip)
rules = self._getRules(payslip)
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
index b67f69c6..a0fda5c9 100755
--- 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
@@ -60,7 +60,7 @@ class TestUsWAPayslip(TestUsPayslip):
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
+ self.assertAlmostEqual(hours_in_period, 184) # only asserted to test algorithm
payslip.compute_sheet()
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
index 509e19b1..e02e55a0 100755
--- 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
@@ -61,7 +61,7 @@ class TestUsWAPayslip(TestUsPayslip):
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
+ self.assertAlmostEqual(hours_in_period, 184) # only asserted to test algorithm
payslip.compute_sheet()
rules = self._getRules(payslip)
diff --git a/l10n_us_hr_payroll/views/res_config_settings_views.xml b/l10n_us_hr_payroll/views/res_config_settings_views.xml
deleted file mode 100644
index 3c69b42f..00000000
--- a/l10n_us_hr_payroll/views/res_config_settings_views.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
- res.config.settings.view.form.inherit
- res.config.settings
-
-
-
-
-
-
-
Payslip Sum Behavior
-
- Customize the behavior of what payslips are eligible when summing over date ranges in rules.
- Generally, "Date To" or "Accounting Date" would be preferred in the United States and anywhere
- else where the ending date on the payslip is used to calculate wage bases.
-
-
-
-
-
-
-
-
-
-
From a87e15e19bd22e86d9de26933fb577d9593f35bd Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 31 Dec 2020 09:05:56 -0800
Subject: [PATCH 04/12] [IMP] l10n_us_hr_payroll: Migrating States and Federal
for 2021
TX Texas Amended (+50 squashed commits)
Squashed commits:
[7da4d616] ID Idaho
[a2970d9b] FED amended
[8ebf4c24] FED
[080f6f05] TN Tennessee
[bf2aa103] MI Michigan
[493a6361] SC South Carolina
[8e97119d] LA Louisiana
[ee97a733] DE Delaware
[5d9a7ced] WY Wyoming
[8f72bfdd] WI Wisconsin
[88073c7e] WV West Virginia
[192ea027] VA Virginia
[a32b4630] VT Vermont
[1874a69f] TX Texas
[39db5caa] SD South Dakota
[21ebee50] RI Rhode Island
[677e6f78] PA Pennsylvania
[91cb694d] OK Oklahoma
[5d93e5c4] ND North Dakota
[f3d535ba] NM New Mexico
[308fd185] NJ New Jersey
[3c36600c] NH New Hampshire
[d422bef6] NV Nevada
[dc0551f1] NE Nebraska
[eaae1e2b] MO Missouri
[1ecd2521] MS Mississippi
[bb8734ad] MN Minnesota
[da6d84bb] Revert "ME Maine"
This reverts commit e866f89b2b08b3a96bd9c8f3c29dcc6cea9ba958.
[e866f89b] ME Maine
[c0a7d06c] KY Kentucky
[84556347] IN Indiana
[ed241bf0] IL Illinois
[d4c33133] HI Hawaii
[982bf274] GA Georgia
[5d0ef117] CT Connecticut
[e4713512] CA California
[24b867f3] WA Washington - formatting changes
[e754b3aa] AR Arkansas
[33482ad3] UT Utah
[25595393] WA Washington
[ac222b58] MT Montana
[55babb7b] AZ Arizona todo: Income Tax not calculated
[8b184e6f] AL Alabama
[cc688c9d] AK Alaska
[e8b105ad] OH Ohio
[bff5ca12] nc north carolina
[118e9a76] ny new york
[575953a3] fl florida
[401a7ce3] IA Iowa
---
.../data/federal/fed_940_futa_parameters.xml | 3 +
.../data/federal/fed_941_fica_parameters.xml | 11 +
.../data/federal/fed_941_fit_parameters.xml | 448 +++-------
l10n_us_hr_payroll/data/state/ak_alaska.xml | 31 +-
l10n_us_hr_payroll/data/state/al_alabama.xml | 72 +-
l10n_us_hr_payroll/data/state/ar_arkansas.xml | 67 +-
l10n_us_hr_payroll/data/state/az_arizona.xml | 16 +-
.../data/state/ca_california.xml | 660 ++++++++-------
.../data/state/ct_connecticut.xml | 571 +------------
l10n_us_hr_payroll/data/state/de_delaware.xml | 5 +
l10n_us_hr_payroll/data/state/fl_florida.xml | 10 +-
l10n_us_hr_payroll/data/state/ga_georgia.xml | 427 +---------
l10n_us_hr_payroll/data/state/hi_hawaii.xml | 67 +-
l10n_us_hr_payroll/data/state/ia_iowa.xml | 200 +++--
l10n_us_hr_payroll/data/state/id_idaho.xml | 191 +----
l10n_us_hr_payroll/data/state/il_illinois.xml | 39 +-
l10n_us_hr_payroll/data/state/in_indiana.xml | 8 +-
l10n_us_hr_payroll/data/state/ks_kansas.xml | 4 +
l10n_us_hr_payroll/data/state/ky_kentucky.xml | 15 +
.../data/state/la_louisiana.xml | 63 +-
l10n_us_hr_payroll/data/state/mi_michigan.xml | 30 +-
.../data/state/mn_minnesota.xml | 60 +-
l10n_us_hr_payroll/data/state/mo_missouri.xml | 78 +-
.../data/state/ms_mississippi.xml | 49 +-
l10n_us_hr_payroll/data/state/mt_montana.xml | 16 +-
.../data/state/nc_northcarolina.xml | 48 +-
.../data/state/nd_north_dakota.xml | 193 +++++
l10n_us_hr_payroll/data/state/ne_nebraska.xml | 11 +
.../data/state/nh_new_hampshire.xml | 6 +
.../data/state/nj_newjersey.xml | 775 +++++++++---------
.../data/state/nm_new_mexico.xml | 275 ++++++-
l10n_us_hr_payroll/data/state/nv_nevada.xml | 6 +
l10n_us_hr_payroll/data/state/ny_new_york.xml | 266 ++++--
l10n_us_hr_payroll/data/state/oh_ohio.xml | 39 +-
l10n_us_hr_payroll/data/state/ok_oklahoma.xml | 18 +-
.../data/state/pa_pennsylvania.xml | 24 +-
.../data/state/ri_rhode_island.xml | 59 ++
.../data/state/sc_south_carolina.xml | 46 +-
.../data/state/sd_south_dakota.xml | 2 +
l10n_us_hr_payroll/data/state/tx_texas.xml | 24 +-
l10n_us_hr_payroll/data/state/ut_utah.xml | 19 +
l10n_us_hr_payroll/data/state/va_virginia.xml | 39 +-
l10n_us_hr_payroll/data/state/vt_vermont.xml | 114 +++
.../data/state/wa_washington.xml | 106 ++-
.../data/state/wi_wisconsin.xml | 7 +-
.../data/state/wv_west_virginia.xml | 5 +
l10n_us_hr_payroll/data/state/wy_wyoming.xml | 19 +-
l10n_us_hr_payroll/tests/__init__.py | 77 +-
.../tests/test_us_ak_alaska_payslip_2019.py | 61 --
.../tests/test_us_ak_alaska_payslip_2021.py | 15 +
.../tests/test_us_al_alabama_payslip_2019.py | 264 ------
.../tests/test_us_al_alabama_payslip_2021.py | 36 +
.../tests/test_us_ar_arkansas_payslip_2019.py | 72 --
.../tests/test_us_ar_arkansas_payslip_2021.py | 35 +
.../tests/test_us_az_arizona_payslip_2019.py | 72 --
.../tests/test_us_az_arizona_payslip_2021.py | 34 +
.../test_us_ca_california_payslip_2019.py | 245 ------
.../test_us_ca_california_payslip_2021.py | 43 +
.../tests/test_us_co_colorado_payslip_2021.py | 37 +
.../test_us_ct_connecticut_payslip_2019.py | 121 ---
.../test_us_ct_connecticut_payslip_2021.py | 35 +
.../tests/test_us_de_delaware_payslip_2020.py | 2 +-
.../tests/test_us_de_delaware_payslip_2021.py | 36 +
.../tests/test_us_fl_florida_payslip_2019.py | 84 --
.../tests/test_us_fl_florida_payslip_2021.py | 16 +
.../tests/test_us_ga_georgia_payslip_2019.py | 135 ---
.../tests/test_us_ga_georgia_payslip_2021.py | 39 +
.../tests/test_us_hi_hawaii_payslip_2019.py | 93 ---
.../tests/test_us_hi_hawaii_payslip_2021.py | 37 +
.../tests/test_us_ia_iowa_payslip_2019.py | 152 ----
.../tests/test_us_ia_iowa_payslip_2021.py | 36 +
.../tests/test_us_id_idaho_payslip_2019.py | 85 --
.../tests/test_us_id_idaho_payslip_2021.py | 35 +
.../tests/test_us_il_illinois_payslip_2019.py | 71 --
.../tests/test_us_il_illinois_payslip_2021.py | 36 +
.../tests/test_us_in_indiana_payslip_2021.py | 36 +
.../tests/test_us_ks_kansas_payslip_2021.py | 36 +
.../tests/test_us_ky_kentucky_payslip_2021.py | 36 +
.../test_us_la_louisiana_payslip_2019.py | 91 --
.../test_us_la_louisiana_payslip_2021.py | 36 +
.../tests/test_us_mi_michigan_payslip_2019.py | 194 -----
.../tests/test_us_mi_michigan_payslip_2021.py | 35 +
.../test_us_mn_minnesota_payslip_2019.py | 159 ----
.../test_us_mn_minnesota_payslip_2021.py | 36 +
.../tests/test_us_mo_missouri_payslip_2019.py | 188 -----
.../tests/test_us_mo_missouri_payslip_2021.py | 34 +
.../test_us_ms_mississippi_payslip_2019.py | 94 ---
.../test_us_ms_mississippi_payslip_2021.py | 35 +
.../tests/test_us_mt_montana_payslip_2019.py | 139 ----
.../tests/test_us_mt_montana_payslip_2021.py | 37 +
.../test_us_nc_northcarolina_payslip_2019.py | 270 ------
.../test_us_nc_northcarolina_payslip_2021.py | 39 +
.../test_us_nd_north_dakota_payslip_2021.py | 37 +
.../tests/test_us_ne_nebraska_payslip_2021.py | 38 +
.../test_us_nh_new_hampshire_payslip_2021.py | 13 +
.../test_us_nj_newjersey_payslip_2019.py | 128 ---
.../test_us_nj_newjersey_payslip_2021.py | 51 ++
.../test_us_nm_new_mexico_payslip_2021.py | 36 +
.../tests/test_us_nv_nevada_payslip_2021.py | 16 +
.../tests/test_us_ny_new_york_payslip_2019.py | 133 ---
.../tests/test_us_ny_new_york_payslip_2021.py | 39 +
.../tests/test_us_oh_ohio_payslip_2019.py | 96 ---
.../tests/test_us_oh_ohio_payslip_2021.py | 108 +++
.../tests/test_us_ok_oklahoma_payslip_2021.py | 38 +
.../test_us_pa_pennsylvania_payslip_2019.py | 33 -
.../test_us_pa_pennsylvania_payslip_2021.py | 43 +
.../tests/test_us_payslip_2019.py | 338 --------
.../tests/test_us_payslip_2021.py | 294 +++++++
.../test_us_ri_rhode_island_payslip_2021.py | 37 +
.../test_us_sc_south_carolina_payslip_2019.py | 97 ---
.../test_us_sc_south_carolina_payslip_2020.py | 16 +-
.../test_us_sc_south_carolina_payslip_2021.py | 36 +
.../test_us_sd_south_dakota_payslip_2021.py | 13 +
.../test_us_tn_tennessee_payslip_2021.py | 14 +
.../tests/test_us_tx_texas_payslip_2019.py | 100 ---
.../tests/test_us_tx_texas_payslip_2021.py | 17 +
.../tests/test_us_us_utah_payslip_2021.py | 36 +
.../tests/test_us_va_virginia_payslip_2019.py | 133 ---
.../tests/test_us_va_virginia_payslip_2021.py | 116 +++
.../tests/test_us_vt_vermont_payslip_2021.py | 37 +
.../test_us_wa_washington_payslip_2020.py | 1 +
... => test_us_wa_washington_payslip_2021.py} | 56 +-
.../test_us_wi_wisconsin_payslip_2021.py | 39 +
.../test_us_wv_west_virginia_payslip_2021.py | 36 +
.../tests/test_us_wy_wyoming_payslip_2019.py | 58 --
.../tests/test_us_wy_wyoming_payslip_2021.py | 13 +
126 files changed, 4297 insertions(+), 6740 deletions(-)
delete mode 100644 l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2021.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2021.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2021.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2021.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2021.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2021.py
delete mode 100755 l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2021.py
rename l10n_us_hr_payroll/tests/{test_us_wa_washington_payslip_2019.py => test_us_wa_washington_payslip_2021.py} (61%)
create mode 100755 l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2021.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2021.py
delete mode 100644 l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2021.py
diff --git a/l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml b/l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
index 9c34afb6..325f53b6 100644
--- a/l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
+++ b/l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
@@ -6,6 +6,7 @@
fed_940_futa_wage_base
+
7000.00
@@ -18,6 +19,7 @@
fed_940_futa_rate_basic
+
6.0
@@ -29,6 +31,7 @@
fed_940_futa_rate_normal
+
0.6
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml b/l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
index 1be404bb..6a4b2804 100644
--- a/l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
@@ -22,6 +22,12 @@
+
+
+ 142800.0
+
+
+
@@ -29,6 +35,7 @@
fed_941_fica_ss_rate
+
6.2
@@ -42,6 +49,7 @@
fed_941_fica_m_wage_base
+
"inf"
@@ -54,6 +62,7 @@
fed_941_fica_m_rate
+
1.45
@@ -67,6 +76,7 @@
fed_941_fica_m_add_wage_start
+
200000.0
@@ -79,6 +89,7 @@
fed_941_fica_m_add_rate
+
0.9
diff --git a/l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml b/l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
index 97753e5a..f83f9ebd 100644
--- a/l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
+++ b/l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
@@ -7,20 +7,6 @@
fed_941_fit_allowance
-
-
- {
- 'weekly': 80.80,
- 'bi-weekly': 161.50,
- 'semi-monthly': 175.00,
- 'monthly': 350.00,
- 'quarterly': 1050.00,
- 'semi-annually': 2100.00,
- 'annually': 4200.00,
- }
-
-
-
{
'weekly': 80.80,
@@ -36,6 +22,7 @@
+
4300.0
@@ -46,33 +33,6 @@
fed_941_fit_nra_additional
-
-
- {
- 'weekly': 153.80,
- 'bi-weekly': 307.70,
- 'semi-monthly': 333.30,
- 'monthly': 666.70,
- 'quarterly': 2000.00,
- 'semi-annually': 4000.00,
- 'annually': 8000.00,
- }
-
-
-
-
- {
- 'weekly': 153.80,
- 'bi-weekly': 307.70,
- 'semi-monthly': 333.30,
- 'monthly': 666.70,
- 'quarterly': 2000.00,
- 'semi-annually': 4000.00,
- 'annually': 8000.00,
- }
-
-
-
{
'weekly': 238.50,
@@ -86,6 +46,19 @@
+
+ {
+ 'weekly': 241.30,
+ 'bi-weekly': 482.70,
+ 'semi-monthly': 522.90,
+ 'monthly': 1045.80,
+ 'quarterly': 3137.50,
+ 'semi-annually': 6275.00,
+ 'annually': 12550.00,
+ }
+
+
+
@@ -93,161 +66,7 @@
fed_941_fit_table_single
-
-
-
- {
- 'weekly': [
- ( 73.00, 0.00, 0),
- ( 260.00, 0.00, 10),
- ( 832.00, 18.70, 12),
- ( 1692.00, 87.34, 22),
- ( 3164.00, 276.54, 24),
- ( 3998.00, 629.82, 32),
- ( 9887.00, 896.70, 35),
- ( 'inf', 2957.85, 37),
- ],
- 'bi-weekly': [
- ( 146.00, 0.00, 0),
- ( 519.00, 0.00, 10),
- ( 1664.00, 37.30, 12),
- ( 3385.00, 174.70, 22),
- ( 6328.00, 553.32, 24),
- ( 7996.00, 1259.64, 32),
- ( 19773.00, 1793.40, 35),
- ( 'inf', 5915.35, 37),
- ],
- 'semi-monthly': [
- ( 158.00, 0.00, 0),
- ( 563.00, 0.00, 10),
- ( 1803.00, 40.50, 12),
- ( 3667.00, 189.30, 22),
- ( 6855.00, 599.38, 24),
- ( 8663.00, 1364.50, 32),
- ( 21421.00, 1943.06, 35),
- ( 'inf', 6408.36, 37),
- ],
- 'monthly': [
- ( 317.00, 0.00, 0),
- ( 1125.00, 0.00, 10),
- ( 3606.00, 80.80, 12),
- ( 7333.00, 378.52, 22),
- ( 13710.00, 1198.46, 24),
- ( 17325.00, 2728.94, 32),
- ( 42842.00, 3885.74, 35),
- ( 'inf', 12816.69, 37),
- ],
- 'quarterly': [
- ( 950.00, 0.00, 0),
- ( 3375.00, 0.00, 10),
- ( 10819.00, 242.50, 12),
- ( 22000.00, 1135.78, 22),
- ( 41131.00, 3595.60, 24),
- ( 51975.00, 8187.04, 32),
- ( 128525.00, 11657.12, 35),
- ( 'inf', 38449.62, 37),
- ],
- 'semi-annually': [
- ( 1900.00, 0.00, 0),
- ( 6750.00, 0.00, 10),
- ( 21638.00, 485.00, 12),
- ( 44000.00, 2271.56, 22),
- ( 82263.00, 7191.20, 24),
- ( 103950.00, 16374.32, 32),
- ( 257050.00, 23314.16, 35),
- ( 'inf', 76899.16, 37),
- ],
- 'annually': [
- ( 3800.00, 0.00, 0),
- ( 13500.00, 0.00, 10),
- ( 43275.00, 970.00, 12),
- ( 88000.00, 4543.00, 22),
- ( 164525.00, 14382.50, 24),
- ( 207900.00, 32748.50, 32),
- ( 514100.00, 46628.50, 35),
- ( 'inf', 153798.50, 37),
- ],
- }
-
-
-
-
-
- {
- 'weekly': [
- ( 73.00, 0.00, 0),
- ( 260.00, 0.00, 10),
- ( 832.00, 18.70, 12),
- ( 1692.00, 87.34, 22),
- ( 3164.00, 276.54, 24),
- ( 3998.00, 629.82, 32),
- ( 9887.00, 896.70, 35),
- ( 'inf', 2957.85, 37),
- ],
- 'bi-weekly': [
- ( 146.00, 0.00, 0),
- ( 519.00, 0.00, 10),
- ( 1664.00, 37.30, 12),
- ( 3385.00, 174.70, 22),
- ( 6328.00, 553.32, 24),
- ( 7996.00, 1259.64, 32),
- ( 19773.00, 1793.40, 35),
- ( 'inf', 5915.35, 37),
- ],
- 'semi-monthly': [
- ( 158.00, 0.00, 0),
- ( 563.00, 0.00, 10),
- ( 1803.00, 40.50, 12),
- ( 3667.00, 189.30, 22),
- ( 6855.00, 599.38, 24),
- ( 8663.00, 1364.50, 32),
- ( 21421.00, 1943.06, 35),
- ( 'inf', 6408.36, 37),
- ],
- 'monthly': [
- ( 317.00, 0.00, 0),
- ( 1125.00, 0.00, 10),
- ( 3606.00, 80.80, 12),
- ( 7333.00, 378.52, 22),
- ( 13710.00, 1198.46, 24),
- ( 17325.00, 2728.94, 32),
- ( 42842.00, 3885.74, 35),
- ( 'inf', 12816.69, 37),
- ],
- 'quarterly': [
- ( 950.00, 0.00, 0),
- ( 3375.00, 0.00, 10),
- ( 10819.00, 242.50, 12),
- ( 22000.00, 1135.78, 22),
- ( 41131.00, 3595.60, 24),
- ( 51975.00, 8187.04, 32),
- ( 128525.00, 11657.12, 35),
- ( 'inf', 38449.62, 37),
- ],
- 'semi-annually': [
- ( 1900.00, 0.00, 0),
- ( 6750.00, 0.00, 10),
- ( 21638.00, 485.00, 12),
- ( 44000.00, 2271.56, 22),
- ( 82263.00, 7191.20, 24),
- ( 103950.00, 16374.32, 32),
- ( 257050.00, 23314.16, 35),
- ( 'inf', 76899.16, 37),
- ],
- 'annually': [
- ( 3800.00, 0.00, 0),
- ( 13500.00, 0.00, 10),
- ( 43275.00, 970.00, 12),
- ( 88000.00, 4543.00, 22),
- ( 164525.00, 14382.50, 24),
- ( 207900.00, 32748.50, 32),
- ( 514100.00, 46628.50, 35),
- ( 'inf', 153798.50, 37),
- ],
- }
-
-
-
+
@@ -276,6 +95,33 @@
+
+
+ {
+ 'standard': [
+ ( 0.00, 0.00, 0.00),
+ ( 3950.00, 0.00, 0.10),
+ ( 13900.00, 995.00, 0.12),
+ ( 44475.00, 4664.00, 0.22),
+ ( 90325.00, 14751.00, 0.24),
+ ( 168875.00, 33603.00, 0.32),
+ ( 213375.00, 47843.00, 0.35),
+ ( 527550.00, 157804.25, 0.37),
+ ],
+ 'higher': [
+ ( 0.00, 0.00, 0.00),
+ ( 6275.00, 0.00, 0.10),
+ ( 11250.00, 497.50, 0.12),
+ ( 26538.00, 2332.00, 0.22),
+ ( 49463.00, 7375.50, 0.24),
+ ( 88738.00, 16801.50, 0.32),
+ ( 110988.00, 23921.50, 0.35),
+ ( 268075.00, 78902.13, 0.37),
+ ],
+ }
+
+
+
@@ -283,161 +129,6 @@
fed_941_fit_table_married
-
-
-
- {
- 'weekly': [
- ( 227.00, 0.00, 0),
- ( 600.00, 0.00, 10),
- ( 1745.00, 37.30, 12),
- ( 3465.00, 174.70, 22),
- ( 6409.00, 553.10, 24),
- ( 8077.00, 1259.66, 32),
- ( 12003.00, 1793.42, 35),
- ( 'inf', 3167.52, 37),
- ],
- 'bi-weekly': [
- ( 454.00, 0.00, 0),
- ( 1200.00, 0.00, 10),
- ( 3490.00, 74.60, 12),
- ( 6931.00, 349.40, 22),
- ( 12817.00, 1106.42, 24),
- ( 16154.00, 2519.06, 32),
- ( 24006.00, 3586.90, 35),
- ( 'inf', 6335.10, 37),
- ],
- 'semi-monthly': [
- ( 492.00, 0.00, 0),
- ( 1300.00, 0.00, 10),
- ( 3781.00, 80.80, 12),
- ( 7508.00, 378.52, 22),
- ( 13885.00, 1198.46, 24),
- ( 17500.00, 2728.94, 32),
- ( 26006.00, 3885.74, 35),
- ( 'inf', 6862.84, 37),
- ],
- 'monthly': [
- ( 983.00, 0.00, 0),
- ( 2600.00, 0.00, 10),
- ( 7563.00, 161.70, 12),
- ( 15017.00, 757.26, 22),
- ( 27771.00, 2397.14, 24),
- ( 35000.00, 5458.10, 32),
- ( 52013.00, 7771.38, 35),
- ( 'inf', 13725.93, 37),
- ],
- 'quarterly': [
- ( 2950.00, 0.00, 0),
- ( 7800.00, 0.00, 10),
- ( 22688.00, 485.00, 12),
- ( 45050.00, 2271.56, 22),
- ( 83313.00, 7191.20, 24),
- ( 105000.00, 16374.32, 32),
- ( 156038.00, 23314.16, 35),
- ( 'inf', 41177.46, 37),
- ],
- 'semi-annually': [
- ( 5900.00, 0.00, 0),
- ( 15600.00, 0.00, 10),
- ( 45375.00, 970.00, 12),
- ( 90100.00, 4543.00, 22),
- ( 166625.00, 14382.50, 24),
- ( 210000.00, 32748.50, 32),
- ( 312075.00, 46628.50, 35),
- ( 'inf', 82354.75, 37),
- ],
- 'annually': [
- ( 11800.00, 0.00, 0),
- ( 31200.00, 0.00, 10),
- ( 90750.00, 1940.00, 12),
- ( 180200.00, 9086.00, 22),
- ( 333250.00, 28765.00, 24),
- ( 420000.00, 65497.00, 32),
- ( 624150.00, 93257.00, 35),
- ( 'inf', 164709.50, 37),
- ],
- }
-
-
-
-
-
- {
- 'weekly': [
- ( 227.00, 0.00, 0),
- ( 600.00, 0.00, 10),
- ( 1745.00, 37.30, 12),
- ( 3465.00, 174.70, 22),
- ( 6409.00, 553.10, 24),
- ( 8077.00, 1259.66, 32),
- ( 12003.00, 1793.42, 35),
- ( 'inf', 3167.52, 37),
- ],
- 'bi-weekly': [
- ( 454.00, 0.00, 0),
- ( 1200.00, 0.00, 10),
- ( 3490.00, 74.60, 12),
- ( 6931.00, 349.40, 22),
- ( 12817.00, 1106.42, 24),
- ( 16154.00, 2519.06, 32),
- ( 24006.00, 3586.90, 35),
- ( 'inf', 6335.10, 37),
- ],
- 'semi-monthly': [
- ( 492.00, 0.00, 0),
- ( 1300.00, 0.00, 10),
- ( 3781.00, 80.80, 12),
- ( 7508.00, 378.52, 22),
- ( 13885.00, 1198.46, 24),
- ( 17500.00, 2728.94, 32),
- ( 26006.00, 3885.74, 35),
- ( 'inf', 6862.84, 37),
- ],
- 'monthly': [
- ( 983.00, 0.00, 0),
- ( 2600.00, 0.00, 10),
- ( 7563.00, 161.70, 12),
- ( 15017.00, 757.26, 22),
- ( 27771.00, 2397.14, 24),
- ( 35000.00, 5458.10, 32),
- ( 52013.00, 7771.38, 35),
- ( 'inf', 13725.93, 37),
- ],
- 'quarterly': [
- ( 2950.00, 0.00, 0),
- ( 7800.00, 0.00, 10),
- ( 22688.00, 485.00, 12),
- ( 45050.00, 2271.56, 22),
- ( 83313.00, 7191.20, 24),
- ( 105000.00, 16374.32, 32),
- ( 156038.00, 23314.16, 35),
- ( 'inf', 41177.46, 37),
- ],
- 'semi-annually': [
- ( 5900.00, 0.00, 0),
- ( 15600.00, 0.00, 10),
- ( 45375.00, 970.00, 12),
- ( 90100.00, 4543.00, 22),
- ( 166625.00, 14382.50, 24),
- ( 210000.00, 32748.50, 32),
- ( 312075.00, 46628.50, 35),
- ( 'inf', 82354.75, 37),
- ],
- 'annually': [
- ( 11800.00, 0.00, 0),
- ( 31200.00, 0.00, 10),
- ( 90750.00, 1940.00, 12),
- ( 180200.00, 9086.00, 22),
- ( 333250.00, 28765.00, 24),
- ( 420000.00, 65497.00, 32),
- ( 624150.00, 93257.00, 35),
- ( 'inf', 164709.50, 37),
- ],
- }
-
-
-
@@ -466,6 +157,33 @@
+
+
+ {
+ 'standard': [
+ ( 0.00, 0.00, 0.00),
+ ( 12200.00, 0.00, 0.10),
+ ( 32100.00, 1990.00, 0.12),
+ ( 93250.00, 9328.00, 0.22),
+ ( 184950.00, 29502.00, 0.24),
+ ( 342050.00, 67206.00, 0.32),
+ ( 431050.00, 95686.00, 0.35),
+ ( 640500.00, 168993.50, 0.37),
+ ],
+ 'higher': [
+ ( 0.00, 0.00, 0.00),
+ ( 12550.00, 0.00, 0.10),
+ ( 22500.00, 995.00, 0.12),
+ ( 53075.00, 4664.00, 0.22),
+ ( 98925.00, 14751.00, 0.24),
+ ( 177475.00, 33603.00, 0.32),
+ ( 221975.00, 47843.00, 0.35),
+ ( 326700.00, 84496.75, 0.37),
+ ],
+ }
+
+
+
Federal 941 FIT Table Head of Household
@@ -500,5 +218,33 @@
+
+
+
+ {
+ 'standard': [
+ ( 0.00, 0.00, 0.00),
+ ( 10200.00, 0.00, 0.10),
+ ( 24400.00, 1420.00, 0.12),
+ ( 64400.00, 6220.00, 0.22),
+ ( 96550.00, 13293.00, 0.24),
+ ( 175100.00, 32145.00, 0.32),
+ ( 219600.00, 46385.00, 0.35),
+ ( 533800.00, 156355.50, 0.37),
+ ],
+ 'higher': [
+ ( 0.00, 0.00, 0.00),
+ ( 9400.00, 0.00, 0.10),
+ ( 16500.00, 710.00, 0.12),
+ ( 36500.00, 3110.00, 0.22),
+ ( 52575.00, 6646.50, 0.24),
+ ( 91950.00, 16072.50, 0.32),
+ ( 114100.00, 23192.50, 0.35),
+ ( 271200.00, 78177.50, 0.37),
+ ],
+ }
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ak_alaska.xml b/l10n_us_hr_payroll/data/state/ak_alaska.xml
index 2c995088..fec273d2 100644
--- a/l10n_us_hr_payroll/data/state/ak_alaska.xml
+++ b/l10n_us_hr_payroll/data/state/ak_alaska.xml
@@ -1,22 +1,23 @@
+
US AK Alaska SUTA Wage Base
us_ak_suta_wage_base
-
- 39900.00
-
-
-
41500.00
+
+ 43600.00
+
+
+
@@ -26,16 +27,16 @@
-
- 1.780
-
-
-
1.590
+
+ 2.570
+
+
+
@@ -44,16 +45,16 @@
-
- 0.500
-
-
-
0.500
+
+ 0.500
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/al_alabama.xml b/l10n_us_hr_payroll/data/state/al_alabama.xml
index fc3b3af1..f2b5ceeb 100644
--- a/l10n_us_hr_payroll/data/state/al_alabama.xml
+++ b/l10n_us_hr_payroll/data/state/al_alabama.xml
@@ -6,12 +6,9 @@
us_al_suta_wage_base
+
+
-
- 8000.0
-
-
-
8000.0
@@ -26,11 +23,8 @@
-
- 2.7
-
-
-
+
+
2.7
@@ -44,22 +38,8 @@
-
- {
- '0': [
- ( 500, 2),
- ( 3000, 4),
- ('inf', 5),
- ],
- 'M': [
- ( 1000, 2),
- ( 6000, 4),
- ('inf', 5),
- ],
- }
-
-
-
+
+
{
'0' : [
@@ -86,17 +66,7 @@
-
- [
- ( 1000, 20000),
- ( 500, 100000),
- ( 300, 'inf'),
- ]
-
-
-
-
-
+
[
( 1000, 20000),
@@ -116,19 +86,7 @@
-
- {
- '0': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
- 'S': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
- 'MS': ((10749.0, 3750.0), (15500.0, 3750.0, 88.0, 250.0), ('inf', 2000.0)),
- 'M': ((23499.0, 7500.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
- 'H': ((23499.0, 4700.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
- }
-
-
-
-
-
+
{
'0': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
@@ -150,19 +108,7 @@
-
- {
- '0' : 0,
- 'S' : 1500,
- 'MS': 1500,
- 'M' : 3000,
- 'H' : 3000,
- }
-
-
-
-
-
+
{
'0' : 0,
diff --git a/l10n_us_hr_payroll/data/state/ar_arkansas.xml b/l10n_us_hr_payroll/data/state/ar_arkansas.xml
index 3e5b8211..2c8f01a5 100644
--- a/l10n_us_hr_payroll/data/state/ar_arkansas.xml
+++ b/l10n_us_hr_payroll/data/state/ar_arkansas.xml
@@ -7,16 +7,17 @@
-
- 10000.0
-
-
-
+
8000.0
+
+ 10000.0
+
+
+
@@ -26,16 +27,17 @@
-
- 3.2
-
-
-
+
2.9
+
+ 3.2
+
+
+
@@ -44,24 +46,6 @@
-
- [
- ( 4599, 0.0, 0.00),
- ( 9099, 2.0, 91.98),
- ( 13699, 3.0, 182.97),
- ( 22599, 3.4, 237.77),
- ( 37899, 5.0, 421.46),
- ( 80800, 5.9, 762.55),
- ( 81800, 6.6, 1243.40),
- ( 82800, 6.6, 1143.40),
- ( 84100, 6.6, 1043.40),
- ( 85200, 6.6, 943.40),
- ( 86200, 6.6, 843.40),
- ( 'inf', 6.6, 803.40),
- ]
-
-
-
@@ -82,6 +66,26 @@
+
+
+
+ [
+ ( 4699, 0.0, 0.00),
+ ( 9199, 2.0, 93.98),
+ ( 13899, 3.0, 185.97),
+ ( 22899, 3.4, 241.57),
+ ( 38499, 5.0, 427.71),
+ ( 82000, 5.9, 774.20),
+ ( 83000, 5.9, 681.70),
+ ( 84000, 5.9, 581.70),
+ ( 85300, 5.9, 481.70),
+ ( 86400, 5.9, 381.70),
+ ( 87500, 5.9, 281.70),
+ ( 'inf', 5.9, 241.70),
+ ]
+
+
+
@@ -90,11 +94,8 @@
-
- 2200.0
-
-
-
+
+
2200.0
diff --git a/l10n_us_hr_payroll/data/state/az_arizona.xml b/l10n_us_hr_payroll/data/state/az_arizona.xml
index 80b800c1..87c4c2c5 100644
--- a/l10n_us_hr_payroll/data/state/az_arizona.xml
+++ b/l10n_us_hr_payroll/data/state/az_arizona.xml
@@ -7,11 +7,8 @@
-
- 7000.0
-
-
-
+
+
7000.0
@@ -26,11 +23,8 @@
-
- 2.0
-
-
-
+
+
2.0
@@ -38,6 +32,8 @@
+
+
US Arizona - Department of Economic Security (ADES) - Unemployment Tax
diff --git a/l10n_us_hr_payroll/data/state/ca_california.xml b/l10n_us_hr_payroll/data/state/ca_california.xml
index 2a907b3a..062cd764 100644
--- a/l10n_us_hr_payroll/data/state/ca_california.xml
+++ b/l10n_us_hr_payroll/data/state/ca_california.xml
@@ -7,11 +7,8 @@
-
- 7000.0
-
-
-
+
+
7000.0
@@ -26,11 +23,8 @@
-
- 3.5
-
-
-
+
+
3.4
@@ -45,11 +39,8 @@
-
- 0.1
-
-
-
+
+
0.1
@@ -64,16 +55,17 @@
-
- 1.0
-
-
-
+
1.0
+
+ 1.2
+
+
+
@@ -82,270 +74,6 @@
-
- {
- 'head_household': {
- 'weekly': (
- ( 316, 0.0110, 0.00),
- ( 750, 0.0220, 3.48),
- ( 967, 0.0440, 13.03),
- ( 1196, 0.0660, 22.58),
- ( 1413, 0.0880, 37.69),
- ( 7212, 0.1023, 56.79),
- ( 8654, 0.1133, 650.03),
- (14423, 0.1243, 813.41),
- (19231, 0.1353, 1530.50),
- ('inf', 0.1463, 2181.02),
- ),
- 'bi-weekly': (
- ( 632, 0.0110, 0.00),
- ( 1500, 0.0220, 6.95),
- ( 1934, 0.0440, 26.05),
- ( 2392, 0.0660, 45.15),
- ( 2826, 0.0880, 75.38),
- (14424, 0.1023, 113.57),
- (17308, 0.1133, 1300.05),
- (28846, 0.1243, 1626.81),
- (38462, 0.1353, 3060.98),
- ('inf', 0.1463, 4362.02),
- ),
- 'semi-monthly': (
- ( 686, 0.0110, 0.00),
- ( 1625, 0.0220, 7.55),
- ( 2094, 0.0440, 28.21),
- ( 2592, 0.0660, 48.85),
- ( 3062, 0.0880, 81.72),
- (15625, 0.1023, 123.08),
- (18750, 0.1133, 1408.27),
- (31250, 0.1243, 1762.33),
- (41667, 0.1353, 3316.08),
- ('inf', 0.1463, 4725.50),
- ),
- 'monthly': (
- ( 1372, 0.0110, 0.00),
- ( 3250, 0.0220, 15.09),
- ( 4188, 0.0440, 56.41),
- ( 5184, 0.0660, 97.68),
- ( 6124, 0.0880, 163.42),
- (31250, 0.1023, 246.14),
- (37500, 0.1133, 2816.53),
- (62500, 0.1243, 3524.66),
- (83334, 0.1353, 6632.16),
- ('inf', 0.1463, 9451.00),
- ),
- 'quarterly': (
- ( 4114, 0.0110, 0.00),
- ( 9748, 0.0220, 45.25),
- ( 12566, 0.0440, 169.20),
- ( 15552, 0.0660, 293.19),
- ( 18369, 0.0880, 490.27),
- ( 93751, 0.1023, 738.17),
- (112501, 0.1133, 8449.75),
- (187501, 0.1243, 10574.13),
- (250000, 0.1353, 19896.63),
- ( 'inf', 0.1463, 28352.74),
- ),
- 'semi-annual': (
- ( 8228, 0.0110, 0.00),
- ( 19496, 0.0220, 90.51),
- ( 25132, 0.0440, 338.41),
- ( 31104, 0.0660, 586.39),
- ( 36738, 0.0880, 980.54),
- (187502, 0.1023, 1476.33),
- (225002, 0.1133, 16899.49),
- (375002, 0.1243, 21148.24),
- (500000, 0.1353, 39793.24),
- ( 'inf', 0.1463, 56705.47),
- ),
- 'annually': (
- ( 16457, 0.0110, 0.00),
- ( 38991, 0.0220, 181.03),
- ( 50264, 0.0440, 676.78),
- ( 62206, 0.0660, 1172.79),
- ( 73477, 0.0880, 1960.96),
- ( 375002, 0.1023, 2952.81),
- ( 450003, 0.1133, 33798.82),
- ( 750003, 0.1243, 42296.43),
- (1000000, 0.1353, 79586.43),
- ( 'inf', 0.1463, 113411.02),
- ),
- },
- 'married': {
- 'weekly': (
- ( 316, 0.0110, 0.00),
- ( 750, 0.0220, 3.48),
- ( 1184, 0.0440, 13.03),
- ( 1642, 0.0660, 32.13),
- ( 2076, 0.0880, 62.36),
- (10606, 0.1023, 100.55),
- (12726, 0.1133, 973.17),
- (19231, 0.1243, 1213.37),
- (21210, 0.1353, 2021.94),
- ('inf', 0.1463, 2289.70),
- ),
- 'bi-weekly': (
- ( 632, 0.0110, 0.00),
- ( 1500, 0.0220, 6.95),
- ( 2368, 0.0440, 26.05),
- ( 3284, 0.0660, 64.24),
- ( 4152, 0.0880, 124.70),
- (21212, 0.1023, 201.08),
- (25452, 0.1133, 1946.32),
- (38462, 0.1243, 2426.71),
- (42420, 0.1353, 4043.85),
- ('inf', 0.1463, 4579.37),
- ),
- 'semi-monthly': (
- ( 686, 0.0110, 0.00),
- ( 1624, 0.0220, 7.55),
- ( 2564, 0.0440, 28.19),
- ( 3560, 0.0660, 69.55),
- ( 4498, 0.0880, 135.29),
- (22978, 0.1023, 217.83),
- (27574, 0.1133, 2108.33),
- (41667, 0.1243, 2629.06),
- (45956, 0.1353, 4380.82),
- ('inf', 0.1463, 4961.12),
- ),
- 'monthly': (
- ( 1372, 0.0110, 0.00),
- ( 3248, 0.0220, 15.09),
- ( 5128, 0.0440, 56.36),
- ( 7120, 0.0660, 139.08),
- ( 8996, 0.0880, 270.55),
- (45956, 0.1023, 435.64),
- (55148, 0.1133, 4216.65),
- (83334, 0.1243, 5258.10),
- (91912, 0.1353, 8761.62),
- ('inf', 0.1463, 9922.22),
- ),
- 'quarterly': (
- ( 4112, 0.0110, 0.00),
- ( 9748, 0.0220, 45.23),
- ( 15384, 0.0440, 169.22),
- ( 21356, 0.0660, 417.20),
- ( 26990, 0.0880, 811.35),
- (137870, 0.1023, 1307.14),
- (165442, 0.1133, 12650.16),
- (250000, 0.1243, 15774.07),
- (275736, 0.1353, 26284.63),
- ( 'inf', 0.1463, 29766.71),
- ),
- 'semi-annual': (
- ( 8224, 0.0110, 0.00),
- ( 19496, 0.0220, 90.46),
- ( 30768, 0.0440, 338.44),
- ( 42712, 0.0660, 834.41),
- ( 53980, 0.0880, 1622.71),
- (275740, 0.1023, 2614.29),
- (330884, 0.1133, 25300.34),
- (500000, 0.1243, 31548.16),
- (551472, 0.1353, 52569.28),
- ( 'inf', 0.1463, 59533.44),
- ),
- 'annually': (
- ( 16446, 0.0110, 0.00),
- ( 38990, 0.0220, 180.91),
- ( 61538, 0.0440, 676.88),
- ( 85422, 0.0660, 1668.99),
- ( 107960, 0.0880, 3245.33),
- ( 551476, 0.1023, 5228.67),
- ( 661768, 0.1133, 50600.36),
- (1000000, 0.1243, 63096.44),
- (1102946, 0.1353, 105138.68),
- ( 'inf', 0.1463, 119067.26),
- ),
- },
- 'single': {
- 'weekly': (
- ( 158, 0.0110, 0.00),
- ( 375, 0.0220, 1.74),
- ( 592, 0.0440, 6.51),
- ( 821, 0.0660, 16.06),
- ( 1038, 0.0880, 31.17),
- ( 5303, 0.1023, 50.27),
- ( 6363, 0.1133, 486.58),
- (10605, 0.1243, 606.68),
- (19231, 0.1353, 1133.96),
- ('inf', 0.1463, 2301.06),
- ),
- 'bi-weekly': (
- ( 316, 0.0110, 0.00),
- ( 750, 0.0220, 3.48),
- ( 1184, 0.0440, 13.03),
- ( 1642, 0.066, 32.13),
- ( 2076, 0.0880, 62.36),
- (10606, 0.1023, 100.55),
- (12726, 0.1133, 973.17),
- (21210, 0.1243, 1213.37),
- (38462, 0.1353, 2267.93),
- ('inf', 0.1463, 4602.13),
- ),
- 'semi-monthly': (
- ( 343, 0.0110, 0.00),
- ( 812, 0.0220, 3.77),
- ( 1282, 0.0440, 14.09),
- ( 1780, 0.0660, 34.77),
- ( 2249, 0.0880, 67.64),
- (11489, 0.1023, 108.91),
- (13787, 0.1133, 1054.16),
- (22978, 0.1243, 1314.52),
- (41667, 0.1353, 2456.96),
- ('inf', 0.1463, 4985.58),
- ),
- 'monthly': (
- ( 686, 0.0110, 0.00),
- ( 1624, 0.0220, 7.55),
- ( 2564, 0.0440, 28.19),
- ( 3560, 0.0660, 69.55),
- ( 4498, 0.0880, 135.29),
- (22978, 0.1023, 217.83),
- (27574, 0.1133, 2108.33),
- (45956, 0.1243, 2629.06),
- (83334, 0.1353, 4913.94),
- ('inf', 0.1463, 9971.18),
- ),
- 'quarterly': (
- ( 2056, 0.0110, 0.00),
- ( 4874, 0.0220, 22.62),
- ( 7692, 0.0440, 84.62),
- ( 10678, 0.066, 208.61),
- ( 13495, 0.0880, 405.69),
- ( 68935, 0.1023, 653.59),
- ( 82721, 0.1133, 6325.10),
- (137868, 0.1243, 7887.05),
- (250000, 0.1353, 14741.82),
- ( 'inf', 0.1463, 29913.28),
- ),
- 'semi-annual': (
- ( 4112, 0.0110, 0.00),
- ( 9748, 0.0220, 45.23),
- ( 15384, 0.0440, 169.22),
- ( 21356, 0.0660, 417.20),
- ( 26990, 0.0880, 811.35),
- (137870, 0.1023, 1307.14),
- (165442, 0.1133, 12650.16),
- (275736, 0.1243, 15774.07),
- (500000, 0.1353, 29483.61),
- ('inf', 0.1463, 59826.53),
- ),
- 'annually': (
- ( 8223, 0.0110, 0.00),
- ( 19495, 0.0220, 90.45),
- ( 30769, 0.0440, 338.43),
- ( 42711, 0.0660, 834.49),
- ( 53980, 0.0880, 1622.66),
- ( 275738, 0.1023, 2614.33),
- ( 330884, 0.1133, 25300.17),
- ( 551473, 0.1243, 31548.21),
- (1000000, 0.1353, 58967.42),
- ( 'inf', 0.1463, 119653.12),
- ),
- },
- }
-
-
-
@@ -612,6 +340,272 @@
+
+
+
+ {
+ 'head_household': {
+ 'weekly': (
+ ( 344, 0.0110, 0.00),
+ ( 814, 0.0220, 3.78),
+ ( 1050, 0.0440, 14.12),
+ ( 1299, 0.0660, 24.50),
+ ( 1535, 0.0880, 40.93),
+ ( 7833, 0.1023, 61.70),
+ ( 9400, 0.1133, 705.99),
+ (15667, 0.1243, 883.53),
+ (19231, 0.1353, 1662.52),
+ ('inf', 0.1463, 2144.73),
+ ),
+ 'bi-weekly': (
+ ( 688, 0.0110, 0.00),
+ ( 1628, 0.0220, 7.57),
+ ( 2100, 0.0440, 28.25),
+ ( 2598, 0.0660, 49.02),
+ ( 3070, 0.0880, 81.89),
+ (15666, 0.1023, 123.43),
+ (18800, 0.1133, 1412.00),
+ (31334, 0.1243, 1767.08),
+ (38462, 0.1353, 3325.06),
+ ('inf', 0.1463, 4289.48),
+ ),
+ 'semi-monthly': (
+ ( 745, 0.0110, 0.00),
+ ( 1765, 0.0220, 8.20),
+ ( 2275, 0.0440, 30.64),
+ ( 2815, 0.0660, 53.08),
+ ( 3326, 0.0880, 88.72),
+ (16972, 0.1023, 133.69),
+ (20367, 0.1133, 1529.68),
+ (33944, 0.1243, 1914.33),
+ (41667, 0.1353, 3601.95),
+ ('inf', 0.1463, 4646.87),
+ ),
+ 'monthly': (
+ ( 1490, 0.0110, 0.00),
+ ( 3530, 0.0220, 16.39),
+ ( 4550, 0.0440, 61.27),
+ ( 5630, 0.0660, 106.15),
+ ( 6652, 0.0880, 177.43),
+ (33944, 0.1023, 267.37),
+ (40734, 0.1133, 3059.34),
+ (67888, 0.1243, 3828.65),
+ (83334, 0.1353, 7203.89),
+ ('inf', 0.1463, 9293.73),
+ ),
+ 'quarterly': (
+ ( 4469, 0.0110, 0.00),
+ ( 10588, 0.0220, 49.13),
+ ( 13649, 0.0440, 183.78),
+ ( 16892, 0.0660, 318.46),
+ ( 19953, 0.0880, 532.50),
+ (101832, 0.1023, 801.87),
+ (122199, 0.1133, 9178.09),
+ (203665, 0.1243, 11485.67),
+ (250000, 0.1353, 21611.89),
+ ( 'inf', 0.1463, 27881.02),
+ ),
+ 'semi-annual': (
+ ( 8938, 0.0110, 0.00),
+ ( 21176, 0.0220, 98.32),
+ ( 27298, 0.0440, 367.56),
+ ( 33784, 0.0660, 636.93),
+ ( 39906, 0.0880, 1065.01),
+ (203664, 0.1023, 1603.75),
+ (244398, 0.1133, 18356.19),
+ (407330, 0.1243, 22971.35),
+ (500000, 0.1353, 43223.80),
+ ( 'inf', 0.1463, 55762.05),
+ ),
+ 'annually': (
+ ( 17876, 0.0110, 0.00),
+ ( 42353, 0.0220, 196.64),
+ ( 54597, 0.0440, 735.13),
+ ( 67569, 0.0660, 1273.87),
+ ( 79812, 0.0880, 2130.02),
+ ( 407329, 0.1023, 3207.40),
+ ( 488796, 0.1133, 36712.39),
+ ( 814658, 0.1243, 45942.60),
+ (1000000, 0.1353, 86447.25),
+ ( 'inf', 0.1463, 111524.02),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 344, 0.0110, 0.00),
+ ( 814, 0.0220, 3.78),
+ ( 1286, 0.0440, 14.12),
+ ( 1784, 0.0660, 34.89),
+ ( 2256, 0.0880, 67.76),
+ (11520, 0.1023, 109.30),
+ (13824, 0.1133, 1057.01),
+ (19231, 0.1243, 1318.05),
+ (23039, 0.1353, 1990.14),
+ ('inf', 0.1463, 2505.36),
+ ),
+ 'bi-weekly': (
+ ( 688, 0.0110, 0.00),
+ ( 1628, 0.0220, 7.57),
+ ( 2572, 0.0440, 28.25),
+ ( 3568, 0.0660, 69.79),
+ ( 4512, 0.0880, 135.53),
+ (23040, 0.1023, 218.60),
+ (27648, 0.1133, 2114.01),
+ (38462, 0.1243, 2636.10),
+ (46078, 0.1353, 2398.28),
+ ('inf', 0.1463, 5010.72),
+ ),
+ 'semi-monthly': (
+ ( 744, 0.0110, 0.00),
+ ( 1764, 0.0220, 8.18),
+ ( 2786, 0.0440, 30.62),
+ ( 3866, 0.0660, 75.59),
+ ( 4886, 0.0880, 146.87),
+ (24960, 0.1023, 236.63),
+ (29950, 0.1133, 2290.20),
+ (41667, 0.1243, 2855.57),
+ (49918, 0.1353, 4311.99),
+ ('inf', 0.1463, 5428.35),
+ ),
+ 'monthly': (
+ ( 1488, 0.0110, 0.00),
+ ( 3528, 0.0220, 16.37),
+ ( 5572, 0.0440, 61.25),
+ ( 7732, 0.0660, 151.19),
+ ( 9772, 0.0880, 293.75),
+ (49920, 0.1023, 473.27),
+ (59900, 0.1133, 4580.41),
+ (83334, 0.1243, 5711.14),
+ (99836, 0.1353, 8623.99),
+ ('inf', 0.1463, 10856.71),
+ ),
+ 'quarterly': (
+ ( 4466, 0.0110, 0.00),
+ ( 10588, 0.0220, 49.13),
+ ( 16710, 0.0440, 183.81),
+ ( 23198, 0.0660, 453.18),
+ ( 29318, 0.0880, 881.39),
+ (149754, 0.1023, 1419.95),
+ (179704, 0.1133, 13740.55),
+ (250000, 0.1243, 17133.89),
+ (299506, 0.1353, 25871.68),
+ ( 'inf', 0.1463, 32569.84),
+ ),
+ 'semi-annual': (
+ ( 8932, 0.0110, 0.00),
+ ( 21176, 0.0220, 98.25),
+ ( 33420, 0.0440, 367.62),
+ ( 46396, 0.0660, 906.36),
+ ( 58636, 0.0880, 1762.78),
+ (299508, 0.1023, 2839.90),
+ (359408, 0.1133, 27481.11),
+ (500000, 0.1243, 34267.78),
+ (599012, 0.1353, 51743.37),
+ ( 'inf', 0.1463, 65139.69),
+ ),
+ 'annually': (
+ ( 17864, 0.0110, 0.00),
+ ( 42350, 0.0220, 196.50),
+ ( 66842, 0.0440, 735.19),
+ ( 92788, 0.0660, 1812.84),
+ ( 117268, 0.0880, 3525.28),
+ ( 599016, 0.1023, 5679.52),
+ ( 718814, 0.1133, 54962.34),
+ (1000000, 0.1243, 68535.45),
+ (1198024, 0.1353, 103486.87),
+ ( 'inf', 0.1463, 130279.52),
+ ),
+ },
+ 'single': {
+ 'weekly': (
+ ( 172, 0.0110, 0.00),
+ ( 407, 0.0220, 1.89),
+ ( 643, 0.0440, 7.06),
+ ( 892, 0.0660, 17.44),
+ ( 1128, 0.0880, 33.87),
+ ( 5760, 0.1023, 54.64),
+ ( 6912, 0.1133, 528.49),
+ (11519, 0.1243, 659.01),
+ (19231, 0.1353, 1231.66),
+ ('inf', 0.1463, 2275.09),
+ ),
+ 'bi-weekly': (
+ ( 344, 0.0110, 0.00),
+ ( 814, 0.0220, 3.78),
+ ( 1286, 0.0440, 14.12),
+ ( 1784, 0.0660, 34.89),
+ ( 2256, 0.0880, 67.76),
+ (11520, 0.1023, 109.30),
+ (13824, 0.1133, 1057.01),
+ (23038, 0.1243, 1318.05),
+ (38462, 0.1353, 2463.35),
+ ('inf', 0.1463, 4550.22),
+ ),
+ 'semi-monthly': (
+ ( 372, 0.0110, 0.00),
+ ( 882, 0.0220, 4.09),
+ ( 1393, 0.0440, 15.31),
+ ( 1933, 0.0660, 37.79),
+ ( 2443, 0.0880, 73.43),
+ (12480, 0.1023, 118.31),
+ (14975, 0.1133, 1145.10),
+ (24959, 0.1243, 1427.78),
+ (41667, 0.1353, 2668.79),
+ ('inf', 0.1463, 4929.38),
+ ),
+ 'monthly': (
+ ( 744, 0.0110, 0.00),
+ ( 1764, 0.0220, 8.18),
+ ( 2786, 0.0440, 30.62),
+ ( 3866, 0.0660, 75.59),
+ ( 4886, 0.0880, 146.87),
+ (24960, 0.1023, 236.63),
+ (29950, 0.1133, 2290.20),
+ (49918, 0.1243, 2855.57),
+ (83334, 0.1353, 5337.59),
+ ('inf', 0.1463, 9858.77),
+ ),
+ 'quarterly': (
+ ( 2233, 0.0110, 0.00),
+ ( 5294, 0.0220, 24.56),
+ ( 8355, 0.0440, 91.90),
+ ( 11599, 0.0660, 226.58),
+ ( 14659, 0.0880, 440.68),
+ ( 74877, 0.1023, 709.96),
+ ( 89852, 0.1133, 6870.26),
+ (149753, 0.1243, 8566.93),
+ (250000, 0.1353, 16012.62),
+ ( 'inf', 0.1463, 29576.04),
+ ),
+ 'semi-annual': (
+ ( 4466, 0.0110, 0.00),
+ ( 10588, 0.0220, 49.13),
+ ( 16710, 0.0440, 183.81),
+ ( 23198, 0.0660, 453.18),
+ ( 29318, 0.0880, 881.39),
+ (149754, 0.1023, 1419.95),
+ (179704, 0.1133, 13740.55),
+ (299506, 0.1243, 17133.89),
+ (500000, 0.1353, 32025.28),
+ ( 'inf', 0.1463, 59152.12),
+ ),
+ 'annually': (
+ ( 8932, 0.0110, 0.00),
+ ( 21175, 0.0220, 98.25),
+ ( 33421, 0.0440, 367.60),
+ ( 46394, 0.0660, 906.42),
+ ( 58634, 0.0880, 1762.64),
+ ( 299508, 0.1023, 2839.76),
+ ( 359407, 0.1133, 27481.17),
+ ( 599012, 0.1243, 34267.73),
+ (1000000, 0.1353, 64050.63),
+ ( 'inf', 0.1463, 118304.31),
+ ),
+ },
+ }
+
+
+
@@ -620,19 +614,6 @@
-
- {
- 'weekly': ( 280, 280, 561, 561),
- 'bi-weekly': ( 561, 561, 1121, 1121),
- 'semi-monthly': ( 607, 607, 1214, 1214),
- 'monthly': ( 1214, 1214, 2429, 2429),
- 'quarterly': ( 3643, 3643, 7287, 7287),
- 'semi-annual': ( 7287, 7287, 14573, 14573),
- 'annually': (14573, 14573, 29146, 29146),
- }
-
-
-
@@ -648,6 +629,21 @@
+
+
+
+ {
+ 'weekly': ( 294, 294, 587, 587),
+ 'bi-weekly': ( 587, 587, 1174, 1174),
+ 'semi-monthly': ( 636, 636, 1272, 1272),
+ 'monthly': ( 1272, 1272, 2545, 2545),
+ 'quarterly': ( 3817, 3817, 7634, 7634),
+ 'semi-annual': ( 7634, 7634, 15267, 15267),
+ 'annually': (15267, 15267, 30534, 30534),
+ }
+
+
+
@@ -656,21 +652,9 @@
-
- {
- 'weekly': ( 19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
- 'bi-weekly': ( 38, 77, 115, 154, 192, 231, 269, 308, 346, 385),
- 'semi-monthly': ( 42, 83, 125, 167, 208, 250, 292, 333, 375, 417),
- 'monthly': ( 83, 167, 250, 333, 417, 500, 583, 667, 750, 833),
- 'quarterly': ( 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500),
- 'semi-annual': ( 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000),
- 'annually': (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
- }
-
-
-
+
{
'weekly': ( 19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
@@ -692,19 +676,6 @@
-
- {
- 'weekly': ( 85, 85, 169, 169),
- 'bi-weekly': ( 169, 169, 339, 339),
- 'semi-monthly': ( 183, 183, 367, 367),
- 'monthly': ( 367, 367, 734, 734),
- 'quarterly': (1100, 1100, 2201, 2201),
- 'semi-annual': (2201, 2201, 4401, 4401),
- 'annually': (4401, 4401, 8802, 8802),
- }
-
-
-
@@ -720,6 +691,21 @@
+
+
+
+ {
+ 'weekly': ( 88, 88, 177, 177),
+ 'bi-weekly': ( 177, 177, 354, 354),
+ 'semi-monthly': ( 192, 192, 383, 383),
+ 'monthly': ( 383, 383, 767, 767),
+ 'quarterly': (1150, 1150, 2301, 2301),
+ 'semi-annual': (2301, 2301, 4601, 4601),
+ 'annually': (4601, 4601, 9202, 9202),
+ }
+
+
+
@@ -728,19 +714,6 @@
-
- {
- 'weekly': ( 2.41, 4.82, 7.23, 9.65, 12.06, 14.47, 16.88, 19.29, 21.70, 24.12),
- 'bi-weekly': ( 4.82, 9.65, 14.47, 19.29, 24.12, 28.94, 33.76, 38.58, 43.41, 48.23),
- 'semi-monthly': ( 5.23, 10.45, 15.68, 20.90, 26.13, 31.35, 36.58, 41.80, 47.03, 52.25),
- 'monthly': ( 10.45, 20.90, 31.35, 41.80, 52.25, 62.70, 73.15, 83.60, 94.05, 104.50),
- 'quarterly': ( 31.35, 62.70, 94.05, 125.40, 156.75, 188.10, 219.45, 250.80, 282.15, 313.50),
- 'semi-annual': ( 62.70, 125.40, 188.10, 250.80, 313.50, 376.20, 438.90, 501.60, 564.30, 627.00),
- 'annually': (125.40, 250.80, 376.20, 501.60, 627.00, 752.40, 877.80, 1003.20, 1128.60, 1254.00),
- }
-
-
-
@@ -756,6 +729,21 @@
+
+
+
+ {
+ 'weekly': ( 2.62, 5.25, 7.87, 10.49, 13.12, 15.74, 18.36, 20.98, 23.61, 26.23),
+ 'bi-weekly': ( 5.25, 10.49, 15.74, 20.98, 26.23, 31.48, 36.72, 41.97, 47.22, 52.46),
+ 'semi-monthly': ( 5.68, 11.37, 17.05, 22.73, 28.42, 34.10, 39.78, 45.47, 51.15, 56.83),
+ 'monthly': ( 11.37, 22.73, 34.10, 45.47, 56.83, 68.20, 79.57, 90.93, 102.30, 113.67),
+ 'quarterly': ( 34.10, 68.20, 102.30, 136.40, 170.50, 204.60, 238.70, 272.80, 306.90, 341.00),
+ 'semi-annual': ( 68.20, 136.40, 204.60, 272.80, 341.00, 409.20, 477.40, 545.60, 613.80, 682.00),
+ 'annually': (136.40, 272.80, 409.20, 545.60, 682.00, 818.40, 954.80, 1091.20, 1227.60, 1364.00),
+ }
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/ct_connecticut.xml b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
index c4bf1dd1..ca211952 100644
--- a/l10n_us_hr_payroll/data/state/ct_connecticut.xml
+++ b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
@@ -7,11 +7,8 @@
-
- 15000.0
-
-
-
+
+
15000.0
@@ -26,11 +23,7 @@
-
- 3.4
-
-
-
+
3.2
@@ -44,59 +37,9 @@
-
- {
- 'a': [
- ( 10000, 0, 3.00),
- ( 50000, 300, 5.00),
- (100000, 2300, 5.50),
- (200000, 5050, 6.00),
- (250000, 11050, 6.50),
- (500000, 14300, 6.90),
- ( 'inf', 31550, 6.99),
- ],
- 'b': [
- ( 16000, 0, 3.00),
- ( 80000, 480, 5.00),
- (160000, 3680, 5.50),
- (320000, 8080, 6.00),
- (400000, 17680, 6.50),
- (800000, 22880, 6.90),
- ( 'inf', 50480, 6.99),
- ],
- 'c': [
- ( 20000, 0, 3.00),
- ( 100000, 600, 5.00),
- ( 200000, 4600, 5.50),
- ( 400000, 10100, 6.00),
- ( 500000, 22100, 6.50),
- (1000000, 28600, 6.90),
- ( 'inf', 63100, 6.99),
- ],
- 'd': [
- ( 10000, 0, 3.00),
- ( 50000, 300, 5.00),
- (100000, 2300, 5.50),
- (200000, 5050, 6.00),
- (250000, 11050, 6.50),
- (500000, 14300, 6.90),
- ( 'inf', 31550, 6.99),
- ],
- 'f': [
- ( 10000, 0, 3.00),
- ( 50000, 300, 5.00),
- (100000, 2300, 5.50),
- (200000, 5050, 6.00),
- (250000, 11050, 6.50),
- (500000, 14300, 6.90),
- ( 'inf', 31550, 6.99),
- ],
- }
-
-
-
+
{
'a': [
@@ -156,80 +99,9 @@
-
- {
- 'a': [
- (50250, 0),
- (52750, 20),
- (55250, 40),
- (57750, 60),
- (60250, 80),
- (62750, 100),
- (65250, 120),
- (67750, 140),
- (70250, 160),
- (72750, 180),
- ('inf', 200),
- ],
- 'b': [
- ( 78500, 0),
- ( 82500, 32),
- ( 86500, 64),
- ( 90500, 96),
- ( 94500, 128),
- ( 98500, 160),
- (102500, 192),
- (106500, 224),
- (110500, 256),
- (114500, 288),
- ( 'inf', 320),
- ],
- 'c': [
- (100500, 0),
- (105500, 40),
- (110500, 80),
- (115500, 120),
- (120500, 160),
- (125500, 200),
- (130500, 240),
- (135500, 280),
- (140500, 320),
- (145500, 360),
- ( 'inf', 400),
-
- ],
- 'd': [
- (50250, 0),
- (52750, 20),
- (55250, 40),
- (57750, 60),
- (60250, 80),
- (62750, 100),
- (65250, 120),
- (67750, 140),
- (70250, 160),
- (72750, 180),
- ('inf', 200),
- ],
- 'f': [
- ( 56500, 0),
- ( 61500, 20),
- ( 66500, 40),
- ( 71500, 60),
- ( 76500, 80),
- ( 81500, 100),
- ( 86500, 120),
- ( 91500, 140),
- ( 96500, 160),
- (101500, 180),
- ( 'inf', 200),
- ],
- }
-
-
-
+
{
'a': [
@@ -310,134 +182,9 @@
-
- {
- 'a': [
- (15000, 0.75),
- (15500, 0.70),
- (16000, 0.65),
- (16500, 0.60),
- (17000, 0.55),
- (17500, 0.50),
- (18000, 0.45),
- (18500, 0.40),
- (20000, 0.35),
- (20500, 0.30),
- (21000, 0.25),
- (21500, 0.20),
- (25000, 0.15),
- (25500, 0.14),
- (26000, 0.13),
- (26500, 0.12),
- (27000, 0.11),
- (48000, 0.10),
- (48500, 0.09),
- (49000, 0.08),
- (49500, 0.08),
- (50000, 0.06),
- (50500, 0.05),
- (51000, 0.03),
- (51500, 0.03),
- (52000, 0.02),
- (52500, 0.01),
- ('inf', 0.00),
- ],
- 'b': [
- (24000, 0.75),
- (24500, 0.70),
- (25000, 0.65),
- (25500, 0.60),
- (26000, 0.55),
- (26500, 0.50),
- (27000, 0.45),
- (27500, 0.40),
- (34000, 0.35),
- (34500, 0.30),
- (35000, 0.25),
- (35500, 0.20),
- (44000, 0.15),
- (44500, 0.14),
- (45000, 0.13),
- (45500, 0.12),
- (46000, 0.11),
- (74000, 0.10),
- (74500, 0.09),
- (75000, 0.08),
- (75500, 0.08),
- (76000, 0.06),
- (76500, 0.05),
- (77000, 0.03),
- (77500, 0.03),
- (78000, 0.02),
- (78500, 0.01),
- ('inf', 0.00),
- ],
- 'c': [
- (30000, 0.75),
- (30500, 0.70),
- (31000, 0.65),
- (31500, 0.60),
- (32000, 0.55),
- (32500, 0.50),
- (33000, 0.45),
- (33500, 0.40),
- (40000, 0.35),
- (40500, 0.30),
- (41000, 0.25),
- (41500, 0.20),
- (50000, 0.15),
- (50500, 0.14),
- (51000, 0.13),
- (51500, 0.12),
- (52000, 0.11),
- (96000, 0.10),
- (96500, 0.09),
- (97000, 0.08),
- (97500, 0.08),
- (98000, 0.06),
- (98500, 0.05),
- (99000, 0.03),
- (99500, 0.03),
- (100000, 0.02),
- (100500, 0.01),
- ('inf', 0.00),
- ],
- 'f': [
- (18800, 0.75),
- (19300, 0.70),
- (19800, 0.65),
- (20300, 0.60),
- (20800, 0.55),
- (21300, 0.50),
- (21800, 0.45),
- (22300, 0.40),
- (25000, 0.35),
- (25500, 0.30),
- (26000, 0.25),
- (26500, 0.20),
- (31300, 0.15),
- (31800, 0.14),
- (32300, 0.13),
- (32800, 0.12),
- (33300, 0.11),
- (60000, 0.10),
- (60500, 0.09),
- (61000, 0.08),
- (61500, 0.08),
- (62000, 0.06),
- (62500, 0.05),
- (63000, 0.03),
- (63500, 0.03),
- (64000, 0.02),
- (64500, 0.01),
- ('inf', 0.00),
- ],
- }
-
-
-
+
{
'a': [
@@ -572,224 +319,9 @@
-
- {
- 'a': [
- (200000, 0),
- (205000, 90),
- (210000, 180),
- (215000, 270),
- (220000, 360),
- (225000, 450),
- (230000, 540),
- (235000, 630),
- (240000, 720),
- (245000, 810),
- (250000, 900),
- (255000, 990),
- (260000, 1080),
- (265000, 1170),
- (270000, 1260),
- (275000, 1350),
- (280000, 1440),
- (285000, 1530),
- (290000, 1620),
- (295000, 1710),
- (300000, 1800),
- (305000, 1890),
- (310000, 1980),
- (315000, 2070),
- (320000, 2160),
- (325000, 2250),
- (330000, 2340),
- (335000, 2430),
- (340000, 2520),
- (345000, 2610),
- (500000, 2700),
- (505000, 2750),
- (510000, 2800),
- (515000, 2850),
- (520000, 2900),
- (525000, 2950),
- (530000, 3000),
- (535000, 3050),
- (540000, 3100),
- ( 'inf', 200),
- ],
- 'b': [
- (320000, 0),
- (328000, 140),
- (336000, 280),
- (344000, 420),
- (352000, 560),
- (360000, 700),
- (368000, 840),
- (376000, 980),
- (384000, 1120),
- (392000, 1260),
- (400000, 1400),
- (408000, 1540),
- (416000, 1680),
- (424000, 1820),
- (432000, 1960),
- (440000, 2100),
- (448000, 2240),
- (456000, 2380),
- (464000, 2520),
- (472000, 2660),
- (480000, 2800),
- (488000, 2940),
- (496000, 3080),
- (504000, 3220),
- (512000, 3360),
- (520000, 3500),
- (528000, 3640),
- (536000, 3780),
- (544000, 3920),
- (552000, 4060),
- (800000, 4200),
- (808000, 4280),
- (816000, 4360),
- (824000, 4440),
- (832000, 4520),
- (840000, 4600),
- (848000, 4680),
- (856000, 4760),
- (864000, 4840),
- ( 'inf', 4920),
- ],
- 'c': [
- ( 400000, 0),
- ( 410000, 180),
- ( 420000, 360),
- ( 430000, 540),
- ( 440000, 720),
- ( 450000, 900),
- ( 460000, 1080),
- ( 470000, 1260),
- ( 480000, 1440),
- ( 490000, 1620),
- ( 500000, 1800),
- ( 510000, 1980),
- ( 520000, 2160),
- ( 530000, 2340),
- ( 540000, 2520),
- ( 550000, 2700),
- ( 560000, 2880),
- ( 570000, 3060),
- ( 580000, 3240),
- ( 590000, 3420),
- ( 600000, 3600),
- ( 610000, 3780),
- ( 620000, 3960),
- ( 630000, 4140),
- ( 640000, 4320),
- ( 650000, 4500),
- ( 660000, 4680),
- ( 670000, 4860),
- ( 680000, 5040),
- ( 690000, 5220),
- (1000000, 5400),
- (1010000, 5500),
- (1020000, 5600),
- (1030000, 5700),
- (1040000, 5800),
- (1050000, 5900),
- (1060000, 6000),
- (1070000, 6100),
- (1080000, 6200),
- ( 'inf', 6300),
- ],
- 'd': [
- (200000, 0),
- (205000, 90),
- (210000, 180),
- (215000, 270),
- (220000, 360),
- (225000, 450),
- (230000, 540),
- (235000, 630),
- (240000, 720),
- (245000, 810),
- (250000, 900),
- (255000, 990),
- (260000, 1080),
- (265000, 1170),
- (270000, 1260),
- (275000, 1350),
- (280000, 1440),
- (285000, 1530),
- (290000, 1620),
- (295000, 1710),
- (300000, 1800),
- (305000, 1890),
- (310000, 1980),
- (315000, 2070),
- (320000, 2160),
- (325000, 2250),
- (330000, 2340),
- (335000, 2430),
- (340000, 2520),
- (345000, 2610),
- (500000, 2700),
- (505000, 2750),
- (510000, 2800),
- (515000, 2850),
- (520000, 2900),
- (525000, 2950),
- (530000, 3000),
- (535000, 3050),
- (540000, 3100),
- ( 'inf', 200),
- ],
- 'f': [
- (200000, 0),
- (205000, 90),
- (210000, 180),
- (215000, 270),
- (220000, 360),
- (225000, 450),
- (230000, 540),
- (235000, 630),
- (240000, 720),
- (245000, 810),
- (250000, 900),
- (255000, 990),
- (260000, 1080),
- (265000, 1170),
- (270000, 1260),
- (275000, 1350),
- (280000, 1440),
- (285000, 1530),
- (290000, 1620),
- (295000, 1710),
- (300000, 1800),
- (305000, 1890),
- (310000, 1980),
- (315000, 2070),
- (320000, 2160),
- (325000, 2250),
- (330000, 2340),
- (335000, 2430),
- (340000, 2520),
- (345000, 2610),
- (500000, 2700),
- (505000, 2750),
- (510000, 2800),
- (515000, 2850),
- (520000, 2900),
- (525000, 2950),
- (530000, 3000),
- (535000, 3050),
- (540000, 3100),
- ( 'inf', 200),
- ],
- }
-
-
-
+
{
'a': [
@@ -1014,96 +546,9 @@
-
- {
- 'a' : [
- (24000, 12000),
- (25000, 11000),
- (26000, 10000),
- (27000, 9000),
- (28000, 8000),
- (29000, 7000),
- (30000, 6000),
- (31000, 5000),
- (32000, 4000),
- (33000, 3000),
- (34000, 2000),
- (35000, 1000),
- ('inf', 0),
- ],
- 'b' : [
- (38000, 19000),
- (39000, 18000),
- (40000, 17000),
- (41000, 16000),
- (42000, 15000),
- (43000, 14000),
- (44000, 13000),
- (45000, 12000),
- (46000, 11000),
- (47000, 10000),
- (48000, 9000),
- (49000, 8000),
- (50000, 7000),
- (51000, 6000),
- (52000, 5000),
- (53000, 4000),
- (54000, 3000),
- (55000, 2000),
- (56000, 1000),
- ('inf', 0),
- ],
- 'c': [
- (48000, 24000),
- (49000, 23000),
- (50000, 22000),
- (51000, 21000),
- (52000, 20000),
- (53000, 19000),
- (54000, 18000),
- (55000, 17000),
- (56000, 16000),
- (57000, 15000),
- (58000, 14000),
- (59000, 13000),
- (60000, 12000),
- (61000, 11000),
- (62000, 10000),
- (63000, 9000),
- (64000, 8000),
- (65000, 7000),
- (66000, 6000),
- (67000, 5000),
- (68000, 4000),
- (69000, 3000),
- (70000, 2000),
- (71000, 1000),
- ('inf', 0),
- ],
- 'f' : [
- (30000, 15000),
- (31000, 14000),
- (22000, 13000),
- (33000, 12000),
- (34000, 11000),
- (35000, 10000),
- (36000, 9000),
- (37000, 8000),
- (38000, 7000),
- (39000, 6000),
- (40000, 5000),
- (41000, 4000),
- (42000, 3000),
- (43000, 2000),
- (44000, 1000),
- ('inf', 0),
- ],
- }
-
-
-
+
{
'a' : [
diff --git a/l10n_us_hr_payroll/data/state/de_delaware.xml b/l10n_us_hr_payroll/data/state/de_delaware.xml
index fad2abf6..70965b3e 100644
--- a/l10n_us_hr_payroll/data/state/de_delaware.xml
+++ b/l10n_us_hr_payroll/data/state/de_delaware.xml
@@ -7,6 +7,7 @@
+
16500.0
@@ -21,6 +22,7 @@
+
1.50
@@ -34,6 +36,7 @@
+
[
( 2000, 0.0, 0.00),
@@ -56,6 +59,7 @@
+
3250
@@ -69,6 +73,7 @@
+
110
diff --git a/l10n_us_hr_payroll/data/state/fl_florida.xml b/l10n_us_hr_payroll/data/state/fl_florida.xml
index 8002a2ee..995b3146 100644
--- a/l10n_us_hr_payroll/data/state/fl_florida.xml
+++ b/l10n_us_hr_payroll/data/state/fl_florida.xml
@@ -26,16 +26,16 @@
-
- 2.7
-
-
-
2.7
+
+ 2.9
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/ga_georgia.xml b/l10n_us_hr_payroll/data/state/ga_georgia.xml
index 659515db..3c900ce6 100644
--- a/l10n_us_hr_payroll/data/state/ga_georgia.xml
+++ b/l10n_us_hr_payroll/data/state/ga_georgia.xml
@@ -7,11 +7,7 @@
-
- 9500.00
-
-
-
+
9500.00
@@ -26,11 +22,7 @@
-
- 2.7
-
-
-
+
2.7
@@ -44,304 +36,9 @@
-
- {
- 'married filing joint, both spouses working': {
- 'weekly': (
- ( 9.50, 0.00, 1.00),
- (29.00, 0.10, 2.00),
- (48.00, 0.48, 3.00),
- (67.50, 1.06, 4.00),
- (96.00, 1.83, 5.00),
- ('inf', 3.27, 5.75),
- ),
- 'bi-weekly': (
- ( 19.00, 0.00, 1.00),
- ( 57.50, 0.19, 2.00),
- ( 96.00, 0.96, 3.00),
- (135.00, 2.12, 4.00),
- (192.00, 3.65, 5.00),
- ( 'inf', 6.54, 5.75),
- ),
- 'semi-monthly': (
- ( 21.00, 0.00, 1.00),
- ( 62.50, 0.21, 2.00),
- (104.00, 1.04, 3.00),
- (146.00, 2.29, 4.00),
- (208.00, 3.96, 5.00),
- ( 'inf', 7.08, 5.75),
- ),
- 'monthly': (
- ( 41.50, 0.00, 1.00),
- (125.50, 0.42, 2.00),
- (208.00, 2.08, 3.00),
- (292.00, 4.58, 4.00),
- (417.00, 7.92, 5.00),
- ( 'inf', 14.17, 5.75),
- ),
- 'quarterly': (
- ( 125.00, 0.00, 1.00),
- ( 375.00, 1.25, 2.00),
- ( 625.00, 6.25, 3.00),
- ( 875.00, 13.75, 4.00),
- (1250.00, 23.75, 5.00),
- ( 'inf', 42.50, 5.75),
- ),
- 'semi-annual': (
- ( 250.00, 0.00, 1.00),
- ( 750.00, 2.50, 2.00),
- (1250.00, 12.50, 3.00),
- (1750.00, 27.50, 4.00),
- (2500.00, 47.50, 5.00),
- ( 'inf', 85.00, 5.75),
- ),
- 'annual': (
- ( 500.00, 0.00, 1.00),
- (1500.00, 5.00, 2.00),
- (2500.00, 25.00, 3.00),
- (3500.00, 55.00, 4.00),
- (5000.00, 95.00, 5.00),
- ( 'inf', 170.00, 5.75),
- ),
- },
- 'married filing joint, one spouse working': {
- 'weekly': (
- ( 19.00, 0.00, 1.00),
- ( 57.50, 0.19, 2.00),
- ( 96.00, 0.96, 3.00),
- (135.00, 2.12, 4.00),
- (192.50, 3.65, 5.00),
- ( 'inf', 6.54, 5.75),
- ),
- 'bi-weekly': (
- ( 38.50, 0.00, 1.00),
- (115.00, 0.38, 2.00),
- (192.00, 1.92, 3.00),
- (269.00, 4.23, 4.00),
- (385.00, 7.31, 5.00),
- ( 'inf', 13.08, 5.75),
- ),
- 'semi-monthly': (
- ( 41.50, 0.00, 1.00),
- (125.00, 0.42, 2.00),
- (208.00, 2.08, 3.00),
- (292.00, 4.58, 4.00),
- (417.00, 7.92, 5.00),
- ( 'inf', 14.17, 5.75),
- ),
- 'monthly': (
- ( 83.00, 0.00, 1.00),
- (250.00, 0.83, 2.00),
- (417.00, 4.17, 3.00),
- (583.00, 9.17, 4.00),
- (833.00, 15.83, 5.00),
- ( 'inf', 28.33, 5.75),
- ),
- 'quarterly': (
- ( 250.00, 0.00, 1.00),
- ( 750.00, 2.50, 2.00),
- (1250.00, 12.50, 3.00),
- (1750.00, 27.50, 4.00),
- (2500.00, 47.50, 5.00),
- ( 'inf', 85.00, 5.75),
- ),
- 'semi-annual': (
- ( 500.00, 0.00, 1.00),
- (1500.00, 5.00, 2.00),
- (2500.00, 25.00, 3.00),
- (3500.00, 55.00, 4.00),
- (5000.00, 95.00, 5.00),
- ( 'inf', 170.00, 5.75),
- ),
- 'annual': (
- ( 1000.00, 0.00, 1.00),
- ( 3000.00, 10.00, 2.00),
- ( 5000.00, 50.00, 3.00),
- ( 7000.00, 110.00, 4.00),
- (10000.00, 190.00, 5.00),
- ( 'inf', 340.00, 5.75),
- ),
- },
- 'single': {
- 'weekly': (
- ( 14.50, 0.00, 1.00),
- ( 43.50, 0.14, 2.00),
- ( 72.00, 0.72, 3.00),
- (101.00, 1.59, 4.00),
- (135.00, 2.74, 5.00),
- ( 'inf', 4.42, 5.75),
- ),
- 'bi-weekly': (
- ( 29.00, 0.00, 1.00),
- ( 86.50, 0.29, 2.00),
- (144.00, 1.44, 3.00),
- (202.00, 3.17, 4.00),
- (269.00, 5.48, 5.00),
- ( 'inf', 8.85, 5.75),
- ),
- 'semi-monthly': (
- ( 31.00, 0.00, 1.00),
- ( 93.50, 0.31, 2.00),
- (156.00, 1.56, 3.00),
- (219.00, 3.34, 4.00),
- (292.00, 5.94, 5.00),
- ( 'inf', 9.58, 5.75),
- ),
- 'monthly': (
- ( 62.50, 0.00, 1.00),
- (187.00, 0.62, 2.00),
- (312.00, 3.12, 3.00),
- (437.00, 6.87, 4.00),
- (583.00, 11.87, 5.00),
- ( 'inf', 19.17, 5.75),
- ),
- 'quarterly': (
- ( 187.50, 0.00, 1.00),
- ( 562.50, 1.88, 2.00),
- ( 937.50, 9.38, 3.00),
- (1312.00, 20.63, 4.00),
- (1750.00, 35.63, 5.00),
- ( 'inf', 57.50, 5.75),
- ),
- 'semi-annual': (
- ( 375.00, 0.00, 1.00),
- (1125.00, 3.75, 2.00),
- (1875.00, 18.75, 3.00),
- (2625.00, 41.25, 4.00),
- (3500.00, 71.25, 5.00),
- ( 'inf', 115.00, 5.75),
- ),
- 'annual': (
- ( 750.00, 0.00, 1.00),
- (2250.00, 7.50, 2.00),
- (3750.00, 37.50, 3.00),
- (5250.00, 82.50, 4.00),
- (7000.00, 142.50, 5.00),
- ( 'inf', 230.00, 5.75),
- ),
- },
- 'head of household': {
- 'weekly': (
- ( 19.00, 0.00, 1.00),
- ( 57.50, 0.19, 2.00),
- ( 96.00, 0.96, 3.00),
- (135.00, 2.12, 4.00),
- (192.50, 3.65, 5.00),
- ( 'inf', 6.54, 5.75),
- ),
- 'bi-weekly': (
- ( 38.50, 0.00, 1.00),
- (115.00, 0.38, 2.00),
- (192.00, 1.92, 3.00),
- (269.00, 4.23, 4.00),
- (385.00, 7.31, 5.00),
- ( 'inf', 13.08, 5.75),
- ),
- 'semi-monthly': (
- ( 41.50, 0.00, 1.00),
- (125.00, 0.42, 2.00),
- (208.00, 2.08, 3.00),
- (292.00, 4.58, 4.00),
- (417.00, 7.92, 5.00),
- ( 'inf', 14.17, 5.75),
- ),
- 'monthly': (
- ( 83.00, 0.00, 1.00),
- (250.00, 0.83, 2.00),
- (417.00, 4.17, 3.00),
- (583.00, 9.17, 4.00),
- (833.00, 15.83, 5.00),
- ( 'inf', 28.33, 5.75),
- ),
- 'quarterly': (
- ( 250.00, 0.00, 1.00),
- ( 750.00, 2.50, 2.00),
- (1250.00, 12.50, 3.00),
- (1750.00, 27.50, 4.00),
- (2500.00, 47.50, 5.00),
- ( 'inf', 85.00, 5.75),
- ),
- 'semi-annual': (
- ( 500.00, 0.00, 1.00),
- (1500.00, 5.00, 2.00),
- (2500.00, 25.00, 3.00),
- (3500.00, 55.00, 4.00),
- (5000.00, 95.00, 5.00),
- ( 'inf', 170.00, 5.75),
- ),
- 'annual': (
- ( 1000.00, 0.00, 1.00),
- ( 3000.00, 10.00, 2.00),
- ( 5000.00, 50.00, 3.00),
- ( 7000.00, 110.00, 4.00),
- (10000.00, 190.00, 5.00),
- ( 'inf', 340.00, 5.75),
- ),
- },
- 'married filing separate': {
- 'weekly': (
- ( 9.50, 0.00, 1.00),
- (29.00, 0.10, 2.00),
- (48.00, 0.48, 3.00),
- (67.50, 1.06, 4.00),
- (96.00, 1.83, 5.00),
- ('inf', 3.27, 5.75),
- ),
- 'bi-weekly': (
- ( 19.00, 0.00, 1.00),
- ( 57.50, 0.19, 2.00),
- ( 96.00, 0.96, 3.00),
- (135.00, 2.12, 4.00),
- (192.00, 3.65, 5.00),
- ( 'inf', 6.54, 5.75),
- ),
- 'semi-monthly': (
- ( 21.00, 0.00, 1.00),
- ( 62.50, 0.21, 2.00),
- (104.00, 1.04, 3.00),
- (146.00, 2.29, 4.00),
- (208.00, 3.96, 5.00),
- ( 'inf', 7.08, 5.75),
- ),
- 'monthly': (
- ( 41.50, 0.00, 1.00),
- (125.50, 0.42, 2.00),
- (208.00, 2.08, 3.00),
- (292.00, 4.58, 4.00),
- (417.00, 7.92, 5.00),
- ( 'inf', 14.17, 5.75),
- ),
- 'quarterly': (
- ( 125.00, 0.00, 1.00),
- ( 375.00, 1.25, 2.00),
- ( 625.00, 6.25, 3.00),
- ( 875.00, 13.75, 4.00),
- (1250.00, 23.75, 5.00),
- ( 'inf', 42.50, 5.75),
- ),
- 'semi-annual': (
- ( 250.00, 0.00, 1.00),
- ( 750.00, 2.50, 2.00),
- (1250.00, 12.50, 3.00),
- (1750.00, 27.50, 4.00),
- (2500.00, 47.50, 5.00),
- ( 'inf', 85.00, 5.75),
- ),
- 'annual': (
- ( 500.00, 0.00, 1.00),
- (1500.00, 5.00, 2.00),
- (2500.00, 25.00, 3.00),
- (3500.00, 55.00, 4.00),
- (5000.00, 95.00, 5.00),
- ( 'inf', 170.00, 5.75),
- ),
- },
- }
-
-
-
+
{
'married filing joint, both spouses working': {
@@ -646,59 +343,9 @@
-
- {
- 'married filing joint, both spouses working': {
- 'weekly': 142.30,
- 'bi-weekly': 284.62,
- 'semi-monthly': 308.33,
- 'monthly': 616.67,
- 'quarterly': 1850.00,
- 'semi-annual': 3700.00,
- 'annual': 7400.00,
- },
- 'married filing joint, one spouse working': {
- 'weekly': 142.30,
- 'bi-weekly': 284.62,
- 'semi-monthly': 308.33,
- 'monthly': 616.67,
- 'quarterly': 1850.00,
- 'semi-annual': 3700.00,
- 'annual': 7400.00,
- },
- 'single': {
- 'weekly': 51.92,
- 'bi-weekly': 103.85,
- 'semi-monthly': 112.50,
- 'monthly': 225.00,
- 'quarterly': 675.00,
- 'semi-annual': 1350.00,
- 'annual': 2700.00,
- },
- 'head of household': {
- 'weekly': 51.92,
- 'bi-weekly': 103.85,
- 'semi-monthly': 112.50,
- 'monthly': 225.00,
- 'quarterly': 675.00,
- 'semi-annual': 1350.00,
- 'annual': 2700.00,
- },
- 'married filing separate': {
- 'weekly': 71.15,
- 'bi-weekly': 142.30,
- 'semi-monthly': 154.16,
- 'monthly': 308.33,
- 'quarterly': 925.00,
- 'semi-annual': 1850.00,
- 'annual': 3700.00,
- },
- }
-
-
-
+
{
'married filing joint, both spouses working': {
@@ -758,21 +405,9 @@
-
- {
- 'weekly': 57.50,
- 'bi-weekly': 115.00,
- 'semi-monthly': 125.00,
- 'monthly': 250.00,
- 'quarterly': 750.00,
- 'semi-annual': 1500.00,
- 'annual': 3000.00,
- }
-
-
-
+
{
'weekly': 57.50,
@@ -794,59 +429,9 @@
-
- {
- 'married filing joint, both spouses working': {
- 'weekly': 115.50,
- 'bi-weekly': 230.75,
- 'semi-monthly': 250.00,
- 'monthly': 500.00,
- 'quarterly': 1500.00,
- 'semi-annual': 3000.00,
- 'annual': 6000.00,
- },
- 'married filing joint, one spouse working': {
- 'weekly': 115.50,
- 'bi-weekly': 230.75,
- 'semi-monthly': 250.00,
- 'monthly': 500.00,
- 'quarterly': 1500.00,
- 'semi-annual': 3000.00,
- 'annual': 6000.00,
- },
- 'single': {
- 'weekly': 88.50,
- 'bi-weekly': 177.00,
- 'semi-monthly': 191.75,
- 'monthly': 383.50,
- 'quarterly': 1150.00,
- 'semi-annual': 2300.00,
- 'annual': 4600.00,
- },
- 'head of household': {
- 'weekly': 88.50,
- 'bi-weekly': 177.00,
- 'semi-monthly': 191.75,
- 'monthly': 383.50,
- 'quarterly': 1150.00,
- 'semi-annual': 2300.00,
- 'annual': 4600.00,
- },
- 'married filing separate': {
- 'weekly': 57.75,
- 'bi-weekly': 115.50,
- 'semi-monthly': 125.00,
- 'monthly': 250.00,
- 'quarterly': 750.00,
- 'semi-annual': 1500.00,
- 'annual': 3000.00,
- },
- }
-
-
-
+
{
'married filing joint, both spouses working': {
diff --git a/l10n_us_hr_payroll/data/state/hi_hawaii.xml b/l10n_us_hr_payroll/data/state/hi_hawaii.xml
index 798821f2..470c5239 100644
--- a/l10n_us_hr_payroll/data/state/hi_hawaii.xml
+++ b/l10n_us_hr_payroll/data/state/hi_hawaii.xml
@@ -7,16 +7,17 @@
-
- 46800.0
-
-
-
48100.0
+
+
+ 47400.0
+
+
+
@@ -26,16 +27,17 @@
-
- 2.40
-
-
-
2.40
+
+
+ 5.2
+
+
+
@@ -44,44 +46,9 @@
-
- {
- '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': (
@@ -126,11 +93,7 @@
-
- 1144
-
-
-
+
1144
diff --git a/l10n_us_hr_payroll/data/state/ia_iowa.xml b/l10n_us_hr_payroll/data/state/ia_iowa.xml
index dcc90009..67328363 100644
--- a/l10n_us_hr_payroll/data/state/ia_iowa.xml
+++ b/l10n_us_hr_payroll/data/state/ia_iowa.xml
@@ -7,16 +7,16 @@
-
- 30600.0
-
-
-
31600.0
+
+ 32400.0
+
+
+
@@ -26,11 +26,6 @@
-
- 1.0
-
-
-
1.0
@@ -44,78 +39,6 @@
-
- {
- 'daily': (
- ( 5.13, 0.0033, 0.00),
- ( 10.25, 0.0067, 0.02),
- ( 20.50, 0.0225, 0.05),
- ( 46.13, 0.0414, 0.28),
- ( 76.89, 0.0563, 1.34),
- (102.52, 0.0596, 3.07),
- (153.78, 0.0625, 4.60),
- (230.68, 0.0744, 7.80),
- ( 'inf', 0.0853, 13.52),
- ),
- 'weekly': (
- ( 25.63, 0.0033, 0.00),
- ( 51.27, 0.0067, 0.08),
- ( 102.52, 0.0225, 0.25),
- ( 230.67, 0.0414, 1.40),
- ( 384.46, 0.0563, 6.71),
- ( 512.62, 0.0596, 15.37),
- ( 768.92, 0.0625, 23.01),
- (1153.38, 0.0744, 39.03),
- ( 'inf', 0.0853, 67.63),
- ),
- 'bi-weekly': (
- ( 51.27, 0.0033, 0.00),
- ( 102.54, 0.0067, 0.17),
- ( 205.04, 0.0225, 0.51),
- ( 461.35, 0.0414, 2.82),
- ( 768.92, 0.0563, 13.43),
- (1025.23, 0.0596, 30.75),
- (1537.85, 0.0625, 46.03),
- (2306.77, 0.0744, 78.07),
- ( 'inf', 0.0853, 135.28),
- ),
- 'semi-monthly': (
- ( 55.54, 0.0033, 0.00),
- ( 111.08, 0.0067, 0.18),
- ( 222.13, 0.0225, 0.55),
- ( 499.79, 0.0414, 3.05),
- ( 833.00, 0.0563, 14.59),
- (1110.67, 0.0596, 33.31),
- (1666.00, 0.0625, 49.86),
- (2499.00, 0.0744, 84.57),
- ( 'inf', 0.0853, 146.55),
- ),
- 'monthly': (
- ( 111.08, 0.0033, 0.00),
- ( 222.17, 0.0067, 0.37),
- ( 444.25, 0.0225, 1.11),
- ( 999.58, 0.0414, 6.11),
- (1666.00, 0.0563, 29.10),
- (2221.33, 0.0596, 62.66),
- (3332.00, 0.0625, 99.72),
- (4998.00, 0.0744, 169.14),
- ( 'inf', 0.0853, 293.09),
- ),
- 'annual': (
- ( 1333.00, 0.0033, 0.00),
- ( 2666.00, 0.0067, 4.40),
- ( 5331.00, 0.0225, 13.33),
- (11995.00, 0.0414, 73.29),
- (19992.00, 0.0563, 349.19),
- (26656.00, 0.0596, 799.41),
- (39984.00, 0.0625, 1196.58),
- (59976.00, 0.0744, 2029.58),
- ( 'inf', 0.0853, 3516.98),
- ),
- }
-
-
-
@@ -190,6 +113,69 @@
+
+
+
+ {
+ 'weekly': (
+ ( 32.23, 0.0033, 0.00),
+ ( 64.46, 0.0067, 0.11),
+ ( 128.92, 0.0225, 0.33),
+ ( 290.08, 0.0414, 1.78),
+ ( 483.46, 0.0563, 8.45),
+ ( 644.62, 0.0596, 19.34),
+ ( 966.92, 0.0625, 28.95),
+ (1450.38, 0.0744, 49.09),
+ ( 'inf', 0.0853, 85.06),
+ ),
+ 'bi-weekly': (
+ ( 64.46, 0.0033, 0.00),
+ ( 128.92, 0.0067, 0.21),
+ ( 257.85, 0.0225, 0.64),
+ ( 580.15, 0.0414, 3.54),
+ ( 966.92, 0.0563, 16.88),
+ (1289.23, 0.0596, 38.66),
+ (1933.85, 0.0625, 57.87),
+ (2900.77, 0.0744, 98.16),
+ ( 'inf', 0.0853, 170.10),
+ ),
+ 'semi-monthly': (
+ ( 69.83, 0.0033, 0.00),
+ ( 139.67, 0.0067, 0.23),
+ ( 279.33, 0.0225, 0.70),
+ ( 628.50, 0.0414, 3.84),
+ (1047.50, 0.0563, 18.30),
+ (1396.67, 0.0596, 41.89),
+ (2095.00, 0.0625, 62.70),
+ (3142.50, 0.0744, 106.35),
+ ( 'inf', 0.0853, 184.28),
+ ),
+ 'monthly': (
+ ( 139.67, 0.0033, 0.00),
+ ( 279.33, 0.0067, 0.46),
+ ( 558.67, 0.0225, 1.40),
+ (1257.00, 0.0414, 7.69),
+ (2095.00, 0.0563, 36.60),
+ (2793.33, 0.0596, 83.78),
+ (4190.00, 0.0625, 125.40),
+ (6285.00, 0.0744, 212.69),
+ ( 'inf', 0.0853, 368.56),
+ ),
+ 'annual': (
+ ( 1676.00, 0.0033, 0.00),
+ ( 3352.00, 0.0067, 5.53),
+ ( 6704.00, 0.0225, 16.76),
+ (15084.00, 0.0414, 92.18),
+ (25140.00, 0.0563, 439.11),
+ (33520.00, 0.0596, 1005.26),
+ (50280.00, 0.0625, 1504.71),
+ (75420.00, 0.0744, 2552.21),
+ ( 'inf', 0.0853, 4422.63),
+ ),
+ }
+
+
+
@@ -198,18 +184,6 @@
-
- {
- 'daily': ( 6.50, 16.00),
- 'weekly': ( 32.50, 80.00),
- 'bi-weekly': ( 65.00, 160.00),
- 'semi-monthly': ( 70.42, 173.33),
- 'monthly': ( 140.83, 346.67),
- 'annually': (1690.00, 4160.00),
- }
-
-
-
@@ -224,6 +198,19 @@
+
+
+
+ {
+ 'weekly': ( 40.96, 100.77),
+ 'bi-weekly': ( 81.92, 201.54),
+ 'semi-monthly': ( 88.75, 218.33),
+ 'monthly': ( 177.50, 436.67),
+ 'annually': (2130.00, 5240.00),
+ }
+
+
+
@@ -232,18 +219,6 @@
-
- {
- 'daily': 0.15,
- 'weekly': 0.77,
- 'bi-weekly': 1.54,
- 'semi-monthly': 1.67,
- 'monthly': 3.33,
- 'annually': 40.00,
- }
-
-
-
@@ -258,6 +233,19 @@
+
+
+
+ {
+ 'weekly': 0.77,
+ 'bi-weekly': 1.54,
+ 'semi-monthly': 1.67,
+ 'monthly': 3.33,
+ 'annually': 40.00,
+ }
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/id_idaho.xml b/l10n_us_hr_payroll/data/state/id_idaho.xml
index 46dcce9d..80995fcc 100644
--- a/l10n_us_hr_payroll/data/state/id_idaho.xml
+++ b/l10n_us_hr_payroll/data/state/id_idaho.xml
@@ -7,16 +7,16 @@
-
- 40000.0
-
-
-
41600.0
+
+ 43000.0
+
+
+
@@ -26,11 +26,7 @@
-
- 1.0
-
-
-
+
1.0
@@ -44,170 +40,9 @@
-
- {
- 'single': {
- 'weekly': (
- ( 235, 0.00, 0.000),
- ( 264, 0.00, 1.125),
- ( 294, 0.00, 3.125),
- ( 324, 1.00, 3.625),
- ( 353, 2.00, 4.625),
- ( 383, 4.00, 5.625),
- ( 457, 5.00, 6.625),
- ('inf', 10.00, 6.925),
- ),
- 'bi-weekly': (
- ( 469, 0.00, 0.000),
- ( 529, 0.00, 1.125),
- ( 588, 1.00, 3.125),
- ( 647, 3.00, 3.625),
- ( 706, 5.00, 4.625),
- ( 766, 7.00, 5.625),
- ( 914, 11.00, 6.625),
- ('inf', 21.00, 6.925),
- ),
- 'semi-monthly': (
- ( 508, 0.00, 0.000),
- ( 573, 0.00, 1.125),
- ( 637, 1.00, 3.125),
- ( 701, 3.00, 3.625),
- ( 765, 5.00, 4.625),
- ( 829, 8.00, 5.625),
- ( 990, 12.00, 6.625),
- ('inf', 22.00, 6.925),
- ),
- 'monthly': (
- ( 1017, 0.00, 0.000),
- ( 1145, 0.00, 1.125),
- ( 1273, 1.00, 3.125),
- ( 1402, 5.00, 3.625),
- ( 1530, 10.00, 4.625),
- ( 1659, 16.00, 5.625),
- ( 1980, 23.00, 6.625),
- ('inf', 45.00, 6.925),
- ),
- 'annually': (
- (12200, 0.00, 0.000),
- (13741, 0.00, 1.125),
- (15281, 17.00, 3.125),
- (16822, 65.00, 3.625),
- (18362, 121.00, 4.625),
- (19903, 192.00, 5.625),
- (23754, 279.00, 6.625),
- ('inf', 534.00, 6.925),
- ),
- },
- 'married': {
- 'weekly': (
- ( 469, 0.00, 0.000),
- ( 529, 0.00, 1.125),
- ( 588, 0.00, 3.125),
- ( 647, 1.00, 3.625),
- ( 706, 2.00, 4.625),
- ( 766, 4.00, 5.625),
- ( 914, 5.00, 6.625),
- ('inf', 10.00, 6.925),
- ),
- 'bi-weekly': (
- ( 938, 0.00, 0.000),
- ( 1057, 0.00, 1.125),
- ( 1175, 1.00, 3.125),
- ( 1294, 5.00, 3.625),
- ( 1412, 9.00, 4.625),
- ( 1531, 15.00, 5.625),
- ( 1827, 21.00, 6.625),
- ('inf', 41.00, 6.925),
- ),
- 'semi-monthly': (
- ( 1017, 0.00, 0.000),
- ( 1145, 0.00, 1.125),
- ( 1273, 1.00, 3.125),
- ( 1402, 5.00, 3.625),
- ( 1530, 10.00, 4.625),
- ( 1659, 16.00, 5.625),
- ( 1980, 23.00, 6.625),
- ('inf', 45.00, 6.925),
- ),
- 'monthly': (
- ( 2033, 0.00, 0.000),
- ( 2290, 0.00, 1.125),
- ( 2547, 3.00, 3.125),
- ( 2804, 11.00, 3.625),
- ( 3060, 20.00, 4.625),
- ( 3317, 32.00, 5.625),
- ( 3959, 47.00, 6.625),
- ('inf', 89.00, 6.925),
- ),
- 'annually': (
- (24400, 0.00, 0.000),
- (27482, 0.00, 1.125),
- (30562, 35.00, 3.125),
- (33644, 131.00, 3.625),
- (36724, 243.00, 4.625),
- (39806, 385.00, 5.625),
- (47508, 558.00, 6.625),
- ('inf', 1068.00, 6.925),
- ),
- },
- 'head of household': {
- 'weekly': (
- ( 235, 0.00, 0.000),
- ( 264, 0.00, 1.125),
- ( 294, 0.00, 3.125),
- ( 324, 1.00, 3.625),
- ( 353, 2.00, 4.625),
- ( 383, 4.00, 5.625),
- ( 457, 5.00, 6.625),
- ('inf', 10.00, 6.925),
- ),
- 'bi-weekly': (
- ( 469, 0.00, 0.000),
- ( 529, 0.00, 1.125),
- ( 588, 1.00, 3.125),
- ( 647, 3.00, 3.625),
- ( 706, 5.00, 4.625),
- ( 766, 7.00, 5.625),
- ( 914, 11.00, 6.625),
- ('inf', 21.00, 6.925),
- ),
- 'semi-monthly': (
- ( 508, 0.00, 0.000),
- ( 573, 0.00, 1.125),
- ( 637, 1.00, 3.125),
- ( 701, 3.00, 3.625),
- ( 765, 5.00, 4.625),
- ( 829, 8.00, 5.625),
- ( 990, 12.00, 6.625),
- ('inf', 22.00, 6.925),
- ),
- 'monthly': (
- ( 1017, 0.00, 0.000),
- ( 1145, 0.00, 1.125),
- ( 1273, 1.00, 3.125),
- ( 1402, 5.00, 3.625),
- ( 1530, 10.00, 4.625),
- ( 1659, 16.00, 5.625),
- ( 1980, 23.00, 6.625),
- ('inf', 45.00, 6.925),
- ),
- 'annually': (
- (12200, 0.00, 0.000),
- (13741, 0.00, 1.125),
- (15281, 17.00, 3.125),
- (16822, 65.00, 3.625),
- (18362, 121.00, 4.625),
- (19903, 192.00, 5.625),
- (23754, 279.00, 6.625),
- ('inf', 534.00, 6.925),
- ),
- },
- }
-
-
-
+
{
'single': {
@@ -378,19 +213,9 @@
-
- {
- 'weekly': 56.92,
- 'bi-weekly': 113.85,
- 'semi-monthly': 123.33,
- 'monthly': 246.67,
- 'annually': 2960.00,
- }
-
-
-
+
{
'weekly': 56.92,
diff --git a/l10n_us_hr_payroll/data/state/il_illinois.xml b/l10n_us_hr_payroll/data/state/il_illinois.xml
index 840b2a9b..ccc6e314 100644
--- a/l10n_us_hr_payroll/data/state/il_illinois.xml
+++ b/l10n_us_hr_payroll/data/state/il_illinois.xml
@@ -7,16 +7,17 @@
-
- 12960.0
-
-
-
12740.0
+
+
+ 12960.0
+
+
+
@@ -26,16 +27,17 @@
-
- 3.175
-
-
-
3.125
+
+
+ 3.175
+
+
+
@@ -44,16 +46,17 @@
-
- 2275.0
-
-
-
2325.0
+
+
+ 2375.0
+
+
+
@@ -62,11 +65,7 @@
-
- 1000.0
-
-
-
+
1000.0
diff --git a/l10n_us_hr_payroll/data/state/in_indiana.xml b/l10n_us_hr_payroll/data/state/in_indiana.xml
index 9bda9b1e..56b7a979 100644
--- a/l10n_us_hr_payroll/data/state/in_indiana.xml
+++ b/l10n_us_hr_payroll/data/state/in_indiana.xml
@@ -7,6 +7,7 @@
+
9500.00
@@ -14,19 +15,21 @@
-
+
US IN Indiana SUTA Rate
us_in_suta_rate
+
2.5
+
US IN Indiana SUTA Income Rate
@@ -34,6 +37,7 @@
+
3.23
@@ -47,6 +51,7 @@
+
{
'daily': ( 2.74, 5.48, 8.22, 10.96, 13.70, 16.44),
@@ -66,6 +71,7 @@
+
{
'daily': ( 4.11, 8.22, 12.33, 16.44, 20.55),
diff --git a/l10n_us_hr_payroll/data/state/ks_kansas.xml b/l10n_us_hr_payroll/data/state/ks_kansas.xml
index 5b59d2b7..d69b6d37 100644
--- a/l10n_us_hr_payroll/data/state/ks_kansas.xml
+++ b/l10n_us_hr_payroll/data/state/ks_kansas.xml
@@ -7,6 +7,7 @@
+
14000.0
@@ -21,6 +22,7 @@
+
2.7
@@ -34,6 +36,7 @@
+
{
'weekly' : 43.27,
@@ -55,6 +58,7 @@
+
{
'single': {
diff --git a/l10n_us_hr_payroll/data/state/ky_kentucky.xml b/l10n_us_hr_payroll/data/state/ky_kentucky.xml
index bcdd2274..d9e9b346 100644
--- a/l10n_us_hr_payroll/data/state/ky_kentucky.xml
+++ b/l10n_us_hr_payroll/data/state/ky_kentucky.xml
@@ -12,6 +12,12 @@
+
+
+ 11100.0
+
+
+
@@ -21,6 +27,7 @@
+
2.7
@@ -40,6 +47,12 @@
+
+
+ 2690
+
+
+
@@ -49,6 +62,8 @@
+
+
5.0
diff --git a/l10n_us_hr_payroll/data/state/la_louisiana.xml b/l10n_us_hr_payroll/data/state/la_louisiana.xml
index 7e4f5fe2..9ac40aae 100644
--- a/l10n_us_hr_payroll/data/state/la_louisiana.xml
+++ b/l10n_us_hr_payroll/data/state/la_louisiana.xml
@@ -7,11 +7,7 @@
-
- 7700.0
-
-
-
+
7700.0
@@ -26,16 +22,18 @@
-
- 1.14
-
-
-
1.14
+
+
+
+ 1.0
+
+
+
@@ -44,22 +42,6 @@
-
- {
- 'single': (
- (12500.00, 2.10),
- (50000.00, 1.60),
- ( 'inf', 1.35),
- ),
- 'married': (
- ( 25000.00, 2.10),
- (100000.00, 1.65),
- ( 'inf', 1.35),
- ),
- }
-
-
-
{
'single': (
@@ -76,6 +58,23 @@
+
+
+ {
+ 'single': (
+ (12500.00, 2.00),
+ (50000.00, 4.00),
+ ( 'inf', 6.00)
+ ),
+ 'married': (
+ ( 25000.00, 2.00),
+ (100000.00, 4.00),
+ ( 'inf', 6.00)
+ ),
+ }
+
+
+
@@ -84,11 +83,7 @@
-
- 4500
-
-
-
+
4500
@@ -102,11 +97,7 @@
-
- 1000.0
-
-
-
+
1000.0
diff --git a/l10n_us_hr_payroll/data/state/mi_michigan.xml b/l10n_us_hr_payroll/data/state/mi_michigan.xml
index 1ce32483..df67c982 100644
--- a/l10n_us_hr_payroll/data/state/mi_michigan.xml
+++ b/l10n_us_hr_payroll/data/state/mi_michigan.xml
@@ -7,16 +7,17 @@
-
- 9500.0
-
-
-
9000.0
+
+
+ 9500.0
+
+
+
@@ -26,11 +27,7 @@
-
- 2.7
-
-
-
+
2.7
@@ -44,18 +41,21 @@
-
- 4400.0
-
-
-
4750.0
+
+
+ 4900.0
+
+
+
+
+
US Michigan - Unemployment Insurance Agency - Unemployment Tax
diff --git a/l10n_us_hr_payroll/data/state/mn_minnesota.xml b/l10n_us_hr_payroll/data/state/mn_minnesota.xml
index d1044752..997fcb46 100644
--- a/l10n_us_hr_payroll/data/state/mn_minnesota.xml
+++ b/l10n_us_hr_payroll/data/state/mn_minnesota.xml
@@ -7,16 +7,12 @@
-
- 34000.0
-
-
-
35000.0
+
@@ -26,16 +22,12 @@
-
- 1.11
-
-
-
1.11
+
@@ -44,24 +36,6 @@
-
- {
- 'single': (
- ( 28920, 2400, 5.35, 0.00),
- ( 89510, 28920, 7.05, 1418.82),
- (166290, 89510, 7.85, 5690.42),
- ( 'inf', 166290, 9.85, 11717.65),
- ),
- 'married': (
- ( 47820, 9050, 5.35, 0.00),
- ( 163070, 47820, 7.05, 2074.20),
- ( 282200, 163070, 7.85, 10199.33),
- ( 'inf', 282200, 9.85, 19551.04),
- ),
- }
-
-
-
@@ -82,6 +56,25 @@
+
+
+ {
+ 'single': (
+ ( 31055, 3825, 5.35, 0.00),
+ ( 93265, 31055, 6.80, 1418.82),
+ (169865, 93265, 7.85, 5687.09),
+ ( 'inf', 169865, 9.85, 11700.19),
+ ),
+ 'married': (
+ ( 51810, 12000, 5.35, 0.00),
+ ( 170140, 51810, 6.80, 2129.84),
+ ( 288200, 170140, 7.85, 10176.28),
+ ( 'inf', 288200, 9.85, 19443.99),
+ ),
+ }
+
+
+
@@ -90,11 +83,6 @@
-
- 4250.0
-
-
-
@@ -102,6 +90,12 @@
+
+
+ 4350.0
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/mo_missouri.xml b/l10n_us_hr_payroll/data/state/mo_missouri.xml
index 73ea5109..1e2cd863 100644
--- a/l10n_us_hr_payroll/data/state/mo_missouri.xml
+++ b/l10n_us_hr_payroll/data/state/mo_missouri.xml
@@ -7,16 +7,17 @@
-
- 12000.0
-
-
-
11500.0
+
+
+ 11000.0
+
+
+
@@ -26,16 +27,18 @@
-
- 2.376
-
-
-
2.376
+
+
+
+ 2.7
+
+
+
@@ -44,22 +47,6 @@
-
-
- [
- (1053.0, 1.5),
- (1053.0, 2.0),
- (1053.0, 2.5),
- (1053.0, 3.0),
- (1053.0, 3.5),
- (1053.0, 4.0),
- (1053.0, 4.5),
- (1053.0, 5.0),
- ( 'inf', 5.4),
- ]
-
-
-
@@ -77,6 +64,24 @@
+
+
+
+
+ [
+ (1088.0, 1.5),
+ (1088.0, 2.0),
+ (1088.0, 2.5),
+ (1088.0, 3.0),
+ (1088.0, 3.5),
+ (1088.0, 4.0),
+ (1088.0, 4.5),
+ (1088.0, 5.0),
+ ( 'inf', 5.4),
+ ]
+
+
+
@@ -85,16 +90,6 @@
-
-
- {
- 'single': 12400.0,
- 'married': 24800.0,
- 'head_of_household': 18650.0,
- }
-
-
-
@@ -106,6 +101,17 @@
+
+
+
+ {
+ 'single': 12550.0,
+ 'married': 25100.0,
+ 'head_of_household': 18800.0,
+ }
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/ms_mississippi.xml b/l10n_us_hr_payroll/data/state/ms_mississippi.xml
index a3868e8d..4c6566b7 100644
--- a/l10n_us_hr_payroll/data/state/ms_mississippi.xml
+++ b/l10n_us_hr_payroll/data/state/ms_mississippi.xml
@@ -7,11 +7,7 @@
-
- 14000.0
-
-
-
+
14000.0
@@ -26,16 +22,18 @@
-
- 1.2
-
-
-
1.2
+
+
+
+ 1.0
+
+
+
@@ -44,15 +42,6 @@
-
- [
- ( 10000.00, 290.0, 0.05),
- ( 5000.00, 90.0, 0.04),
- ( 2000.00, 0.0, 0.03),
- ]
-
-
-
@@ -64,6 +53,17 @@
+
+
+
+ [
+ ( 10000.00, 230.0, 0.05),
+ ( 5000.00, 30.0, 0.04),
+ ( 3000.00, 0.0, 0.03),
+ ]
+
+
+
@@ -72,18 +72,9 @@
-
- {
- 'single': 2300.0,
- 'head_of_household': 3400.0,
- 'married_dual': 2300.0,
- 'married': 4600.0,
- }
-
-
-
+
{
'single': 2300.0,
diff --git a/l10n_us_hr_payroll/data/state/mt_montana.xml b/l10n_us_hr_payroll/data/state/mt_montana.xml
index b777fb4e..7df6da1c 100644
--- a/l10n_us_hr_payroll/data/state/mt_montana.xml
+++ b/l10n_us_hr_payroll/data/state/mt_montana.xml
@@ -1,22 +1,23 @@
+
US MT Montana SUTA Wage Base
us_mt_suta_wage_base
-
- 33000.00
-
-
-
34100.00
+
+ 35300.00
+
+
+
@@ -26,11 +27,6 @@
-
- 1.18
-
-
-
1.18
diff --git a/l10n_us_hr_payroll/data/state/nc_northcarolina.xml b/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
index d09f8e3f..5134c4d0 100644
--- a/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
+++ b/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
@@ -1,41 +1,43 @@
+
US NC North Carolina SUTA Wage Base
us_nc_suta_wage_base
-
- 24300.0
-
-
-
25200.0
+
+ 26000.0
+
+
+
+
US NC North Carolina SUTA Rate
us_nc_suta_rate
-
- 1.0
-
-
-
1.0
+
+ 1.0
+
+
+
@@ -44,18 +46,6 @@
-
-
-
- {
- 'weekly': {'allowance': 48.08, 'standard_deduction': 192.31, 'standard_deduction_hh': 288.46},
- 'bi-weekly': {'allowance': 96.15, 'standard_deduction': 384.62, 'standard_deduction_hh': 576.92},
- 'semi-monthly': {'allowance': 104.17, 'standard_deduction': 416.67, 'standard_deduction_hh': 625.00},
- 'monthly': {'allowance': 208.33, 'standard_deduction': 833.33, 'standard_deduction_hh': 1250.00},
- }
-
-
-
@@ -68,6 +58,20 @@
+
+
+
+
+
+ {
+ 'weekly': {'allowance': 48.08, 'standard_deduction': 206.73, 'standard_deduction_hh': 310.10},
+ 'bi-weekly': {'allowance': 96.15, 'standard_deduction': 413.46, 'standard_deduction_hh': 620.19},
+ 'semi-monthly': {'allowance': 104.17, 'standard_deduction': 447.92, 'standard_deduction_hh': 671.88},
+ 'monthly': {'allowance': 208.33, 'standard_deduction': 895.83, 'standard_deduction_hh': 1343.75},
+ }
+
+
+
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 d778c3aa..9c7fdd69 100644
--- a/l10n_us_hr_payroll/data/state/nd_north_dakota.xml
+++ b/l10n_us_hr_payroll/data/state/nd_north_dakota.xml
@@ -12,6 +12,12 @@
+
+
+ 38500.0
+
+
+
@@ -21,12 +27,15 @@
+
+
1.02
+
US ND North Dakota SIT Tax Rate
@@ -214,6 +223,189 @@
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 121, 0.00, 0.00),
+ ( 900, 0.00, 1.10),
+ ( 2007, 8.57, 2.04),
+ ( 4057, 31.15, 2.27),
+ ( 8678, 77.69, 2.64),
+ ('inf', 199.68, 2.90),
+ ),
+ 'bi-weekly': (
+ ( 241, 0.00, 0.00),
+ ( 1800, 0.00, 1.10),
+ ( 4014, 17.15, 2.04),
+ ( 8113, 62.31, 2.27),
+ ( 17357, 155.36, 2.64),
+ ( 'inf', 399.40, 2.90),
+ ),
+ 'semi-monthly': (
+ ( 261, 0.00, 0.00),
+ ( 1950, 0.00, 1.10),
+ ( 4349, 18.58, 2.04),
+ ( 8790, 67.52, 2.27),
+ ( 18790, 168.33, 2.64),
+ ( 'inf', 432.67, 2.90),
+ ),
+ 'monthly': (
+ ( 523, 0.00, 0.00),
+ ( 3900, 0.00, 1.10),
+ ( 8698, 37.15, 2.04),
+ ( 17579, 135.03, 2.27),
+ ( 37606, 336.62, 2.64),
+ ( 'inf', 865.34, 2.90),
+ ),
+ 'quarterly': (
+ ( 1569, 0.00, 0.00),
+ ( 11700, 0.00, 1.10),
+ ( 26094, 111.44, 2.04),
+ ( 52738, 405.08, 2.27),
+ ( 112819, 1009.90, 2.64),
+ ( 'inf', 2596.04, 2.90),
+ ),
+ 'semi-annual': (
+ ( 3138, 0.00, 0.00),
+ ( 23400, 0.00, 1.10),
+ ( 52188, 222.88, 2.04),
+ ( 105475, 810.16, 2.27),
+ ( 225638, 2019.77, 2.64),
+ ( 'inf', 5192.08, 2.90),
+ ),
+ 'annual': (
+ ( 6275, 0.00, 0.00),
+ ( 46800, 0.00, 1.10),
+ ( 104375, 445.78, 2.04),
+ ( 210950, 1620.31, 2.27),
+ ( 451275, 4039.56, 2.64),
+ ( 'inf', 10384.14, 2.90),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 241, 0.00, 0.00),
+ ( 892, 0.00, 1.10),
+ ( 1814, 7.16, 2.04),
+ ( 2637, 25.97, 2.27),
+ ( 4520, 44.65, 2.64),
+ ('inf', 94.36, 2.90),
+ ),
+ 'bi-weekly': (
+ ( 483, 0.00, 0.00),
+ ( 1785, 0.00, 1.10),
+ ( 3628, 14.32, 2.04),
+ ( 5274, 51.92, 2.27),
+ ( 9040, 89.28, 2.64),
+ ( 'inf', 188.71, 2.90),
+ ),
+ 'semi-monthly': (
+ ( 523, 0.00, 0.00),
+ ( 1933, 0.00, 1.10),
+ ( 3930, 15.51, 2.04),
+ ( 5714, 56.25, 2.27),
+ ( 9794, 96.75, 2.64),
+ ( 'inf', 204.46, 2.90),
+ ),
+ 'monthly': (
+ ( 1046, 0.00, 0.00),
+ ( 3867, 0.00, 1.10),
+ ( 7860, 31.03, 2.04),
+ ( 11427, 112.49, 2.27),
+ ( 19588, 193.46, 2.64),
+ ( 'inf', 408.91, 2.90),
+ ),
+ 'quarterly': (
+ ( 3138, 0.00, 0.00),
+ ( 11600, 0.00, 1.10),
+ ( 23581, 93.08, 2.04),
+ ( 34281, 337.49, 2.27),
+ ( 58763, 580.38, 2.64),
+ ( 'inf', 1226.71, 2.90),
+ ),
+ 'semi-annual': (
+ ( 6275, 0.00, 0.00),
+ ( 23200, 0.00, 1.10),
+ ( 47163, 186.18, 2.04),
+ ( 68563, 675.02, 2.27),
+ ( 117525, 1160.80, 2.64),
+ ( 'inf', 2453.40, 2.90),
+ ),
+ 'annual': (
+ ( 12550, 0.00, 0.00),
+ ( 46400, 0.00, 1.10),
+ ( 94325, 372.35, 2.04),
+ ( 137125, 1350.02, 2.27),
+ ( 235050, 2321.58, 2.64),
+ ( 'inf', 4906.80, 2.90),
+ ),
+ },
+ 'head_household':{
+ 'weekly': (
+ ( 121, 0.00, 0.00),
+ ( 900, 0.00, 1.10),
+ ( 2007, 8.57, 2.04),
+ ( 4057, 31.15, 2.27),
+ ( 8678, 77.69, 2.64),
+ ('inf', 199.68, 2.90),
+ ),
+ 'bi-weekly': (
+ ( 241, 0.00, 0.00),
+ ( 1800, 0.00, 1.10),
+ ( 4014, 17.15, 2.04),
+ ( 8113, 62.31, 2.27),
+ ( 17357, 155.36, 2.64),
+ ( 'inf', 399.40, 2.90),
+ ),
+ 'semi-monthly': (
+ ( 261, 0.00, 0.00),
+ ( 1950, 0.00, 1.10),
+ ( 4349, 18.58, 2.04),
+ ( 8790, 67.52, 2.27),
+ ( 18790, 168.33, 2.64),
+ ( 'inf', 432.67, 2.90),
+ ),
+ 'monthly': (
+ ( 523, 0.00, 0.00),
+ ( 3900, 0.00, 1.10),
+ ( 8698, 37.15, 2.04),
+ ( 17579, 135.03, 2.27),
+ ( 37606, 336.62, 2.64),
+ ( 'inf', 865.34, 2.90),
+ ),
+ 'quarterly': (
+ ( 1569, 0.00, 0.00),
+ ( 11700, 0.00, 1.10),
+ ( 26094, 111.44, 2.04),
+ ( 52738, 405.08, 2.27),
+ ( 112819, 1009.90, 2.64),
+ ( 'inf', 2596.04, 2.90),
+ ),
+ 'semi-annual': (
+ ( 3138, 0.00, 0.00),
+ ( 23400, 0.00, 1.10),
+ ( 52188, 222.88, 2.04),
+ ( 105475, 810.16, 2.27),
+ ( 225638, 2019.77, 2.64),
+ ( 'inf', 5192.08, 2.90),
+ ),
+ 'annual': (
+ ( 6275, 0.00, 0.00),
+ ( 46800, 0.00, 1.10),
+ ( 104375, 445.78, 2.04),
+ ( 210950, 1620.31, 2.27),
+ ( 451275, 4039.56, 2.64),
+ ( 'inf', 10384.14, 2.90),
+ ),
+ },
+ }
+
+
+
@@ -222,6 +414,7 @@
+
{
'weekly' : 83.00,
diff --git a/l10n_us_hr_payroll/data/state/ne_nebraska.xml b/l10n_us_hr_payroll/data/state/ne_nebraska.xml
index 1951ff2f..ae668205 100644
--- a/l10n_us_hr_payroll/data/state/ne_nebraska.xml
+++ b/l10n_us_hr_payroll/data/state/ne_nebraska.xml
@@ -7,6 +7,7 @@
+
9000.0
@@ -26,7 +27,15 @@
+
+
+
+ 2.5
+
+
+
+
US NE Nebraska SIT Tax Rate
@@ -34,6 +43,8 @@
+
+
{
'single': {
diff --git a/l10n_us_hr_payroll/data/state/nh_new_hampshire.xml b/l10n_us_hr_payroll/data/state/nh_new_hampshire.xml
index 374ff539..ed53b7e2 100644
--- a/l10n_us_hr_payroll/data/state/nh_new_hampshire.xml
+++ b/l10n_us_hr_payroll/data/state/nh_new_hampshire.xml
@@ -7,6 +7,7 @@
+
14000.00
@@ -26,6 +27,11 @@
+
+ 2.7
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/nj_newjersey.xml b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
index 50c72dcd..c1b7e003 100644
--- a/l10n_us_hr_payroll/data/state/nj_newjersey.xml
+++ b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
@@ -7,16 +7,17 @@
-
- 34400.00
-
-
-
35300.00
+
+
+ 36200.00
+
+
+
@@ -27,11 +28,7 @@
-
- 2.6825
-
-
-
+
2.6825
@@ -45,11 +42,7 @@
-
- 0.3825
-
-
-
+
0.3825
@@ -64,11 +57,7 @@
-
- 0.5
-
-
-
+
0.5
@@ -82,16 +71,17 @@
-
- 0.17
-
-
-
0.26
+
+
+ 0.47
+
+
+
@@ -101,11 +91,7 @@
-
- 0.1175
-
-
-
+
0.1175
@@ -119,11 +105,7 @@
-
- 0.0425
-
-
-
+
0.0425
@@ -138,11 +120,7 @@
-
- 0.0
-
-
-
+
0.0
@@ -156,16 +134,17 @@
-
- 0.08
-
-
-
0.16
+
+
+ 0.28
+
+
+
@@ -175,351 +154,6 @@
-
- {
- 'A': {
- 'weekly': (
- ( 385, 0.00, 1.50),
- ( 673, 5.77, 2.00),
- ( 769, 11.54, 3.90),
- ( 1442, 15.29, 6.10),
- ( 9615, 56.34, 7.00),
- (96154, 628.46, 9.90),
- ('inf', 9195.77, 11.80),
- ),
- 'bi-weekly': (
- ( 769, 0.00, 1.50),
- ( 1346, 12.00, 2.00),
- ( 1538, 23.00, 3.90),
- ( 2885, 31.00, 6.10),
- ( 19231, 113.00, 7.00),
- (192308, 1257.00, 9.90),
- ( 'inf', 18392.00, 11.80),
- ),
- 'semi-monthly': (
- ( 833, 0.00, 1.50),
- ( 1458, 13.00, 2.00),
- ( 1667, 25.00, 3.90),
- ( 3125, 33.00, 6.10),
- ( 20833, 122.00, 7.00),
- (208333, 1362.00, 9.90),
- ( 'inf', 19924.00, 11.80),
- ),
- 'monthly': (
- ( 1667, 0.00, 1.50),
- ( 2917, 25.00, 2.00),
- ( 3333, 50.00, 3.90),
- ( 6250, 66.00, 6.10),
- ( 41667, 244.00, 7.00),
- (416667, 2723.00, 9.90),
- ( 'inf', 39848.00, 11.80),
- ),
- 'quarterly': (
- ( 5000, 0.00, 1.50),
- ( 8750, 75.00, 2.00),
- ( 10000, 150.00, 3.90),
- ( 18750, 198.75, 6.10),
- ( 125000, 732.50, 7.00),
- (1250000, 8170.00, 9.90),
- ( 'inf', 119545.00, 11.80),
- ),
- 'semi-annual': (
- ( 10000, 0.00, 1.50),
- ( 17500, 150.00, 2.00),
- ( 20000, 300.00, 3.90),
- ( 37500, 397.50, 6.10),
- ( 250000, 1465.00, 7.00),
- (2500000, 16340.00, 9.90),
- ( 'inf', 239090.00, 11.80),
- ),
- 'annual': (
- ( 20000, 0.00, 1.50),
- ( 35000, 300.00, 2.00),
- ( 40000, 600.00, 3.90),
- ( 75000, 795.00, 6.10),
- ( 500000, 2930.00, 7.00),
- (5000000, 32680.00, 9.90),
- ( 'inf', 478180.00, 11.80),
- ),
- },
- 'B': {
- 'weekly': (
- ( 385, 0.00, 1.50),
- ( 962, 5.77, 2.00),
- ( 1346, 17.31, 2.70),
- ( 1538, 27.69, 3.90),
- ( 2885, 35.19, 6.10),
- ( 9615, 117.31, 7.00),
- (96154, 588.46, 9.90),
- ('inf', 9155.77, 11.80),
- ),
- 'bi-weekly': (
- ( 769, 0.00, 1.50),
- ( 1923, 12.00, 2.00),
- ( 2692, 35.00, 2.70),
- ( 3076, 55.00, 3.90),
- ( 5769, 70.00, 6.10),
- ( 19231, 235.00, 7.00),
- (192308, 1177.00, 9.90),
- ( 'inf', 18312.00, 11.80),
- ),
- 'semi-monthly': (
- ( 833, 0.00, 1.50),
- ( 2083, 12.50, 2.00),
- ( 2917, 37.50, 2.70),
- ( 3333, 59.99, 3.90),
- ( 6250, 76.25, 6.10),
- ( 20833, 254.19, 7.00),
- (208333, 1275.00, 9.90),
- ( 'inf', 19838.00, 11.80),
- ),
- 'monthly': (
- ( 1667, 0.00, 1.50),
- ( 4167, 25.00, 2.00),
- ( 5833, 75.00, 2.70),
- ( 6667, 120.00, 3.90),
- ( 12500, 153.00, 6.10),
- ( 41667, 508.00, 7.00),
- (416667, 2550.00, 9.90),
- ( 'inf', 39675.00, 11.80),
- ),
- 'quarterly': (
- ( 5000, 0.00, 1.50),
- ( 12500, 75.00, 2.00),
- ( 17500, 225.00, 2.70),
- ( 20000, 360.00, 3.90),
- ( 37500, 397.50, 6.10),
- ( 125000, 1525.00, 7.00),
- (1250000, 7650.00, 9.90),
- ( 'inf', 119025.00, 11.80),
- ),
- 'semi-annual': (
- ( 10000, 0.00, 1.50),
- ( 25000, 150.00, 2.00),
- ( 35000, 450.00, 2.70),
- ( 40000, 720.00, 3.90),
- ( 75000, 915.00, 6.10),
- ( 250000, 3050.00, 7.00),
- (2500000, 15300.00, 9.90),
- ( 'inf', 238050.00, 11.80),
- ),
- 'annual': (
- ( 20000, 0.00, 1.50),
- ( 50000, 300.00, 2.00),
- ( 70000, 900.00, 2.70),
- (80000, 1440.00, 3.90),
- ( 150000, 1830.00, 6.10),
- ( 500000, 6100.00, 7.00),
- (5000000, 30600.00, 9.90),
- ( 'inf', 476100.00, 11.80),
- ),
- },
- 'C': {
- 'weekly': (
- ( 385, 0.00, 1.50),
- ( 769, 5.77, 2.30),
- ( 962, 14.62, 2.80),
- ( 1154, 20.00, 3.50),
- ( 2885, 26.73, 5.60),
- ( 9615, 123.65, 6.60),
- (96154, 567.88, 9.90),
- ('inf', 9135.19, 11.80),
- ),
- 'bi-weekly': (
- ( 769, 0.00, 1.50),
- ( 1538, 11.54, 2.30),
- ( 1923, 29.23, 2.80),
- ( 2308, 40.00, 3.50),
- ( 5769, 53.46, 5.60),
- ( 19231, 247.31, 6.60),
- (192308, 1135.77, 9.90),
- ( 'inf', 18270.38, 11.80),
- ),
- 'semi-monthly': (
- ( 833, 0.00, 1.50),
- ( 1667, 12.50, 2.30),
- ( 2083, 31.67, 2.80),
- ( 2500, 43.33, 3.50),
- ( 6250, 57.92, 5.60),
- ( 20833, 267.92, 6.60),
- (208333, 1230.42, 9.90),
- ( 'inf', 19792.92, 11.80),
- ),
- 'monthly': (
- ( 1667, 0.00, 1.50),
- ( 3333, 25.00, 2.30),
- ( 4167, 63.33, 2.80),
- ( 5000, 86.67, 3.50),
- ( 12500, 115.83, 5.60),
- ( 41667, 535.85, 6.60),
- (416667, 2460.83, 9.90),
- ( 'inf', 39585.83, 11.80),
- ),
- 'quarterly': (
- ( 5000, 0.00, 1.50),
- ( 10000, 75.00, 2.30),
- ( 12500, 190.00, 2.80),
- ( 15000, 260.00, 3.50),
- ( 37500, 347.50, 5.60),
- ( 125000, 1607.50, 6.60),
- (1250000, 7382.50, 9.90),
- ( 'inf', 118757.50, 11.80),
- ),
- 'semi-annual': (
- ( 10000, 0.00, 1.50),
- ( 20000, 150.00, 2.30),
- ( 25000, 380.00, 2.80),
- ( 30000, 520.00, 3.50),
- ( 75000, 695.00, 5.60),
- ( 250000, 3215.00, 6.60),
- (2500000, 14765.00, 9.90),
- ( 'inf', 237515.00, 11.80),
- ),
- 'annual': (
- ( 20000, 0.00, 1.50),
- ( 40000, 300.00, 2.30),
- ( 50000, 760.00, 2.80),
- ( 60000, 1040.00, 3.50),
- ( 150000, 1390.00, 5.60),
- ( 500000, 6430.00, 6.60),
- (5000000, 29530.00, 9.90),
- ( 'inf', 475030.00, 11.80),
- ),
- },
- 'D': {
- 'weekly': (
- ( 385, 0.00, 1.50),
- ( 769, 5.77, 2.70),
- ( 962, 16.15, 3.40),
- ( 1154, 22.69, 4.30),
- ( 2885, 30.96, 5.60),
- ( 9615, 127.88, 6.50),
- ( 96154, 565.38, 9.90),
- ( 'inf', 9132.69, 11.80),
- ),
- 'bi-weekly': (
- ( 769, 0.00, 1.50),
- ( 1538, 11.54, 2.70),
- ( 1923, 32.31, 3.40),
- ( 2308, 45.38, 4.30),
- ( 5769, 61.92, 5.60),
- ( 19231, 255.77, 6.50),
- (192308, 1130.77, 9.90),
- ( 'inf', 18265.38, 11.80),
- ),
- 'semi-monthly': (
- ( 833, 0.00, 1.50),
- ( 1667, 12.50, 2.70),
- ( 2083, 35.00, 3.40),
- ( 2500, 49.17, 4.30),
- ( 6250, 67.08, 5.60),
- ( 20833, 277.08, 6.50),
- (208333, 1225.00, 9.90),
- ( 'inf', 19787.50, 11.80),
- ),
- 'monthly': (
- ( 1667, 0.00, 1.50),
- ( 3333, 25.00, 2.70),
- ( 4167, 70.00, 3.40),
- ( 5000, 98.33, 4.00),
- ( 12500, 134.17, 5.60),
- ( 41667, 554.17, 6.50),
- (416667, 2450.00, 9.90),
- ( 'inf', 39575.00, 11.80),
- ),
- 'quarterly': (
- ( 5000, 0.00, 1.50),
- ( 10000, 75.00, 2.07),
- ( 12500, 210.00, 3.40),
- ( 15000, 295.00, 4.30),
- ( 37500, 402.50, 5.60),
- ( 125000, 1662.50, 6.50),
- (1250000, 7350.00, 9.90),
- ( 'inf', 118725.00, 11.80),
- ),
- 'semi-annual': (
- ( 10000, 0.00, 1.50),
- ( 20000, 150.00, 2.70),
- ( 25000, 420.00, 3.40),
- ( 30000, 590.00, 4.30),
- ( 75000, 805.00, 5.60),
- ( 250000, 3325.00, 6.50),
- (2500000, 14700.00, 9.90),
- ( 'inf', 237450.00, 11.80),
- ),
- 'annual': (
- ( 20000, 0.00, 1.50),
- ( 40000, 300.00, 2.70),
- ( 50000, 840.00, 3.40),
- ( 60000, 1180.00, 4.30),
- ( 150000, 1610.00, 5.60),
- ( 250000, 6650.00, 6.50),
- (2500000, 29400.00, 9.90),
- ( 'inf', 474900.00, 11.80),
- ),
- },
- 'E': {
- 'weekly': (
- ( 385, 0.00, 1.50),
- ( 673, 5.77, 2.00),
- ( 1923, 11.54, 5.80),
- ( 9615, 84.04, 6.50),
- ( 96154, 584.04, 9.90),
- ( 'inf', 9151.35, 11.80),
- ),
- 'bi-weekly': (
- ( 769, 0.00, 1.50),
- ( 1346, 12.00, 2.00),
- ( 3846, 23.00, 5.80),
- ( 19231, 168.00, 6.50),
- (192308, 1168.00, 9.90),
- ( 'inf', 18303.00, 11.80),
- ),
- 'semi-monthly': (
- ( 833, 0.00, 1.50),
- ( 1458, 13.00, 2.00),
- ( 4167, 25.00, 5.80),
- ( 20833, 182.00, 6.50),
- (208333, 1265.00, 9.90),
- ( 'inf', 19828.00, 11.80),
- ),
- 'monthly': (
- ( 1667, 0.00, 1.50),
- ( 2916, 25.00, 2.00),
- ( 8333, 50.00, 5.80),
- ( 41667, 364.00, 6.50),
- (416667, 2531.00, 9.90),
- ( 'inf', 39656.00, 11.80),
- ),
- 'quarterly': (
- ( 5000, 0.00, 1.50),
- ( 8750, 75.00, 2.00),
- ( 25000, 150.00, 5.80),
- ( 125000, 1092.50, 6.50),
- (1250000, 7592.50, 9.90),
- ( 'inf', 118967.50, 11.80),
- ),
- 'semi-annual': (
- ( 10000, 0.00, 1.50),
- ( 17500, 150.00, 2.00),
- ( 50000, 300.00, 5.80),
- ( 250000, 2185.00, 6.50),
- (2500000, 15185.00, 9.90),
- ( 'inf', 237935.00, 11.80),
- ),
- 'annual': (
- ( 20000, 0.00, 1.50),
- ( 35000, 300.00, 2.00),
- ( 100000, 600.00, 5.80),
- ( 500000, 4370.00, 6.50),
- (5000000, 30370.00, 9.90),
- ( 'inf', 475870.00, 11.80),
- ),
- },
- }
-
-
-
{
@@ -866,6 +500,352 @@
+
+
+ {
+ 'A': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 769, 11.54, 3.90),
+ ( 1442, 15.29, 6.10),
+ ( 9615, 56.34, 7.00),
+ (19231, 628.46, 9.90),
+ ('inf', 1580.38, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 1538, 23.00, 3.90),
+ ( 2885, 31.00, 6.10),
+ ( 19231, 113.00, 7.00),
+ ( 38462, 1257.00, 9.90),
+ ( 'inf', 3161.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 1667, 25.00, 3.90),
+ ( 3125, 33.00, 6.10),
+ ( 20833, 122.00, 7.00),
+ ( 41667, 1362.00, 9.90),
+ ( 'inf', 3424.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2917, 25.00, 2.00),
+ ( 3333, 50.00, 3.90),
+ ( 6250, 66.00, 6.10),
+ ( 41667, 244.00, 7.00),
+ ( 83333, 2723.00, 9.90),
+ ( 'inf', 6848.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 10000, 150.00, 3.90),
+ ( 18750, 198.75, 6.10),
+ ( 125000, 732.50, 7.00),
+ ( 250000, 8170.00, 9.90),
+ ( 'inf', 20545.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 20000, 300.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 250000, 1465.00, 7.00),
+ ( 500000, 16340.00, 9.90),
+ ( 'inf', 41060.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 40000, 600.00, 3.90),
+ ( 75000, 795.00, 6.10),
+ ( 500000, 2930.00, 7.00),
+ (1000000, 32680.00, 9.90),
+ ( 'inf', 82180.00, 11.80),
+ ),
+ },
+ 'B': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 962, 5.77, 2.00),
+ ( 1346, 17.31, 2.70),
+ ( 1538, 27.69, 3.90),
+ ( 2885, 35.19, 6.10),
+ ( 9615, 117.31, 7.00),
+ (19231, 588.46, 9.90),
+ ('inf', 1540.38, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1923, 12.00, 2.00),
+ ( 2692, 35.00, 2.70),
+ ( 3076, 55.00, 3.90),
+ ( 5769, 70.00, 6.10),
+ ( 19231, 235.00, 7.00),
+ ( 38462, 1177.00, 9.90),
+ ( 'inf', 3081.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 2083, 12.50, 2.00),
+ ( 2917, 37.50, 2.70),
+ ( 3333, 59.99, 3.90),
+ ( 6250, 76.25, 6.10),
+ ( 20833, 254.19, 7.00),
+ ( 41667, 1275.00, 9.90),
+ ( 'inf', 3338.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 4167, 25.00, 2.00),
+ ( 5833, 75.00, 2.70),
+ ( 6667, 120.00, 3.90),
+ ( 12500, 153.00, 6.10),
+ ( 41667, 508.00, 7.00),
+ ( 83333, 2550.00, 9.90),
+ ( 'inf', 6675.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 12500, 75.00, 2.00),
+ ( 17500, 225.00, 2.70),
+ ( 20000, 360.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 125000, 1525.00, 7.00),
+ ( 250000, 7650.00, 9.90),
+ ( 'inf', 20025.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 25000, 150.00, 2.00),
+ ( 35000, 450.00, 2.70),
+ ( 40000, 720.00, 3.90),
+ ( 75000, 915.00, 6.10),
+ ( 250000, 3050.00, 7.00),
+ ( 500000, 15300.00, 9.90),
+ ( 'inf', 40050.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 50000, 300.00, 2.00),
+ ( 70000, 900.00, 2.70),
+ (80000, 1440.00, 3.90),
+ ( 150000, 1830.00, 6.10),
+ ( 500000, 6100.00, 7.00),
+ (1000000, 30600.00, 9.90),
+ ( 'inf', 80100.00, 11.80),
+ ),
+ },
+ 'C': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.30),
+ ( 962, 14.62, 2.80),
+ ( 1154, 20.00, 3.50),
+ ( 2885, 26.73, 5.60),
+ ( 9615, 123.65, 6.60),
+ (19231, 567.88, 9.90),
+ ('inf', 1519.81, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.30),
+ ( 1923, 29.23, 2.80),
+ ( 2308, 40.00, 3.50),
+ ( 5769, 53.46, 5.60),
+ ( 19231, 247.31, 6.60),
+ ( 38462, 1135.77, 9.90),
+ ( 'inf', 3039.62, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.30),
+ ( 2083, 31.67, 2.80),
+ ( 2500, 43.33, 3.50),
+ ( 6250, 57.92, 5.60),
+ ( 20833, 267.92, 6.60),
+ ( 41667, 1230.42, 9.90),
+ ( 'inf', 3292.92, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.30),
+ ( 4167, 63.33, 2.80),
+ ( 5000, 86.67, 3.50),
+ ( 12500, 115.83, 5.60),
+ ( 41667, 535.85, 6.60),
+ ( 83333, 2460.83, 9.90),
+ ( 'inf', 6585.83, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.30),
+ ( 12500, 190.00, 2.80),
+ ( 15000, 260.00, 3.50),
+ ( 37500, 347.50, 5.60),
+ ( 125000, 1607.50, 6.60),
+ ( 250000, 7382.50, 9.90),
+ ( 'inf', 19757.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.30),
+ ( 25000, 380.00, 2.80),
+ ( 30000, 520.00, 3.50),
+ ( 75000, 695.00, 5.60),
+ ( 250000, 3215.00, 6.60),
+ ( 500000, 14765.00, 9.90),
+ ( 'inf', 39515.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.30),
+ ( 50000, 760.00, 2.80),
+ ( 60000, 1040.00, 3.50),
+ ( 150000, 1390.00, 5.60),
+ ( 500000, 6430.00, 6.60),
+ (1000000, 29530.00, 9.90),
+ ( 'inf', 79030.00, 11.80),
+ ),
+ },
+ 'D': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.70),
+ ( 962, 16.15, 3.40),
+ ( 1154, 22.69, 4.30),
+ ( 2885, 30.96, 5.60),
+ ( 9615, 127.88, 6.50),
+ ( 19231, 565.38, 9.90),
+ ( 'inf', 1517.31, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.70),
+ ( 1923, 32.31, 3.40),
+ ( 2308, 45.38, 4.30),
+ ( 5769, 61.92, 5.60),
+ ( 19231, 255.77, 6.50),
+ ( 38462, 1130.77, 9.90),
+ ( 'inf', 3034.62, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.70),
+ ( 2083, 35.00, 3.40),
+ ( 2500, 49.17, 4.30),
+ ( 6250, 67.08, 5.60),
+ ( 20833, 277.08, 6.50),
+ ( 41667, 1225.00, 9.90),
+ ( 'inf', 3287.50, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.70),
+ ( 4167, 70.00, 3.40),
+ ( 5000, 98.33, 4.00),
+ ( 12500, 134.17, 5.60),
+ ( 41667, 554.17, 6.50),
+ ( 83333, 2450.00, 9.90),
+ ( 'inf', 6575.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.07),
+ ( 12500, 210.00, 3.40),
+ ( 15000, 295.00, 4.30),
+ ( 37500, 402.50, 5.60),
+ ( 125000, 1662.50, 6.50),
+ ( 250000, 7350.00, 9.90),
+ ( 'inf', 19725.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.70),
+ ( 25000, 420.00, 3.40),
+ ( 30000, 590.00, 4.30),
+ ( 75000, 805.00, 5.60),
+ ( 250000, 3325.00, 6.50),
+ ( 500000, 14700.00, 9.90),
+ ( 'inf', 39450.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.70),
+ ( 50000, 840.00, 3.40),
+ ( 60000, 1180.00, 4.30),
+ ( 150000, 1610.00, 5.60),
+ ( 250000, 6650.00, 6.50),
+ (1000000, 29400.00, 9.90),
+ ( 'inf', 78900.00, 11.80),
+ ),
+ },
+ 'E': {
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 1923, 11.54, 5.80),
+ ( 9615, 84.04, 6.50),
+ ( 19231, 584.04, 9.90),
+ ( 'inf', 1535.96, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 3846, 23.00, 5.80),
+ ( 19231, 168.00, 6.50),
+ ( 38462, 1168.00, 9.90),
+ ( 'inf', 3072.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 4167, 25.00, 5.80),
+ ( 20833, 182.00, 6.50),
+ ( 41667, 1265.00, 9.90),
+ ( 'inf', 3328.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2916, 25.00, 2.00),
+ ( 8333, 50.00, 5.80),
+ ( 41667, 364.00, 6.50),
+ ( 83333, 2531.00, 9.90),
+ ( 'inf', 6656.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 25000, 150.00, 5.80),
+ ( 125000, 1092.50, 6.50),
+ ( 250000, 7592.50, 9.90),
+ ( 'inf', 19967.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 50000, 300.00, 5.80),
+ ( 250000, 2185.00, 6.50),
+ ( 500000, 15185.00, 9.90),
+ ( 'inf', 39935.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 100000, 600.00, 5.80),
+ ( 500000, 4370.00, 6.50),
+ (1000000, 30370.00, 9.90),
+ ( 'inf', 79870.00, 11.80),
+ ),
+ },
+ }
+
+
+
@@ -874,21 +854,8 @@
-
- {
- 'weekly': 19.20,
- 'bi-weekly': 38.40,
- 'semi-monthly': 41.60,
- 'monthly': 83.30,
- 'quarterly': 250.00,
- 'semi-annual': 500.00,
- 'annual': 1000.00,
- 'daily or miscellaneous': 2.70,
- }
-
-
-
+
{
'weekly': 19.20,
diff --git a/l10n_us_hr_payroll/data/state/nm_new_mexico.xml b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
index 2c4249a0..970b1f48 100644
--- a/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
+++ b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
@@ -12,6 +12,12 @@
+
+
+ 27000.0
+
+
+
@@ -21,6 +27,7 @@
+
1.0
@@ -34,7 +41,7 @@
-
+
{
'single': {
@@ -257,6 +264,272 @@
+
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 121, 0.00, 0.0),
+ ( 226, 0.00, 1.7),
+ ( 332, 1.80, 3.2),
+ ( 428, 5.18, 4.7),
+ ( 621, 9.70, 4.9),
+ ( 928, 19.13, 4.9),
+ ( 1371, 34.20, 4.9),
+ ( 2525, 55.88, 4.9),
+ ( 4159, 112.41, 4.9),
+ ('inf', 192.51, 5.9),
+ ),
+ 'bi-weekly': (
+ ( 241, 0.00, 0.0),
+ ( 453, 0.00, 1.7),
+ ( 664, 3.60, 3.2),
+ ( 857, 10.37, 4.7),
+ ( 1241, 19.40, 4.9),
+ ( 1857, 38.25, 4.9),
+ ( 2741, 68.40, 4.9),
+ ( 5049, 111.75, 4.9),
+ ( 8318, 224.83, 4.9),
+ ('inf', 385.02, 5.9),
+ ),
+ 'semi-monthly': (
+ ( 261, 0.00, 0.0),
+ ( 491, 0.00, 1.7),
+ ( 720, 3.90, 3.2),
+ ( 928, 11.23, 4.7),
+ ( 1345, 21.02, 4.9),
+ ( 2011, 41.44, 4.9),
+ ( 2970, 74.10, 4.9),
+ ( 2470, 121.06, 4.9),
+ ( 9011, 243.56, 4.9),
+ ('inf', 417.10, 5.9),
+ ),
+ 'monthly': (
+ ( 523, 0.00, 0.0),
+ ( 981, 0.00, 1.7),
+ ( 1440, 7.79, 3.2),
+ ( 1856, 22.46, 4.7),
+ ( 2690, 42.04, 4.9),
+ ( 4023, 82.88, 4.9),
+ ( 5940, 148.21, 4.9),
+ (10940, 242.13, 4.9),
+ (18023, 487.13, 4.9),
+ ('inf', 834.21, 5.9),
+ ),
+ 'quarterly': (
+ ( 1569, 0.00, 0.0),
+ ( 2944, 0.00, 1.7),
+ ( 4319, 23.38, 3.2),
+ ( 5569, 67.38, 4.7),
+ ( 8069, 126.13, 4.9),
+ ( 12069, 248.63, 4.9),
+ ( 17819, 444.63, 4.9),
+ ( 32819, 726.38, 4.9),
+ ( 54069, 1461.38, 4.9),
+ ( 'inf', 2502.63, 5.9),
+ ),
+ 'semi-annual': (
+ ( 3138, 0.00, 0.0),
+ ( 5888, 0.00, 1.7),
+ ( 8638, 46.75, 3.2),
+ ( 11138, 134.75, 4.7),
+ ( 16138, 252.25, 4.9),
+ ( 24138, 497.25, 4.9),
+ ( 35638, 889.25, 4.9),
+ ( 65638, 1452.75, 4.9),
+ (108138, 2922.75, 4.9),
+ ('inf' , 5005.25, 5.9),
+ ),
+ 'annually': (
+ ( 6275, 0.00, 0.0),
+ ( 11775, 0.00, 1.7),
+ ( 17275, 93.50, 3.2),
+ ( 22275, 269.50, 4.7),
+ ( 32275, 504.50, 4.9),
+ ( 48275, 994.50, 4.9),
+ ( 71275, 1778.50, 4.9),
+ (131275, 2905.50, 4.9),
+ (216275, 5845.50, 4.9),
+ ( 'inf', 10010.50, 5.9),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 241, 0.00, 0.0),
+ ( 395, 0.00, 1.7),
+ ( 549, 2.62, 3.2),
+ ( 703, 7.54, 4.7),
+ ( 1011, 14.77, 4.9),
+ ( 1472, 29.85, 4.9),
+ ( 2164, 52.46, 4.9),
+ ( 4088, 86.38, 4.9),
+ ( 6299, 180.62, 4.9),
+ ('inf', 288.98, 5.9),
+ ),
+ 'bi-weekly': (
+ ( 483, 0.00, 0.0),
+ ( 790, 0.00, 1.7),
+ ( 1089, 5.23, 3.2),
+ ( 1406, 15.08, 4.7),
+ ( 2021, 29.54, 4.9),
+ ( 2944, 59.69, 4.9),
+ ( 4329, 104.92, 4.9),
+ ( 8175, 172.77, 4.9),
+ (12598, 361.23, 4.9),
+ ('inf', 577.96, 5.9),
+ ),
+ 'semi-monthly': (
+ ( 523, 0.00, 0.0),
+ ( 856, 0.00, 1.7),
+ ( 1190, 5.67, 3.2),
+ ( 1523, 16.33, 4.7),
+ ( 2190, 32.00, 4.9),
+ ( 3190, 64.67, 4.9),
+ ( 4690, 113.67, 4.9),
+ ( 8856, 187.17, 4.9),
+ (13648, 391.33, 4.9),
+ ('inf', 626.13, 5.9),
+ ),
+ 'monthly': (
+ ( 1046, 0.00, 0.0),
+ ( 1713, 0.00, 1.7),
+ ( 2379, 11.33, 3.2),
+ ( 3046, 32.67, 4.7),
+ ( 4379, 64.00, 4.9),
+ ( 6379, 129.33, 4.9),
+ ( 9379, 227.33, 4.9),
+ (17713, 374.33, 4.9),
+ (27296, 782.67, 4.9),
+ ('inf', 1252.25, 5.9),
+ ),
+ 'quarterly': (
+ ( 3138, 0.00, 0.0),
+ ( 5138, 0.00, 1.7),
+ ( 7138, 34.00, 3.2),
+ ( 9138, 98.00, 4.7),
+ (13138, 192.00, 4.9),
+ (19138, 388.00, 4.9),
+ (28138, 682.00, 4.9),
+ (53138, 1123.00, 4.9),
+ (81888, 2348.00, 4.9),
+ ('inf', 3756.75, 5.9),
+ ),
+ 'semi-annual': (
+ ( 6275, 0.00, 0.0),
+ ( 10275, 0.00, 1.7),
+ ( 14275, 68.00, 3.2),
+ ( 18275, 196.00, 4.7),
+ ( 26275, 384.00, 4.9),
+ ( 38275, 776.00, 4.9),
+ ( 56275, 1364.00, 4.9),
+ (106275, 2246.00, 4.9),
+ (163775, 4696.00, 4.9),
+ ( 'inf', 7513.50, 5.9),
+ ),
+ 'annually': (
+ ( 12450, 0.00, 0.0),
+ ( 20450, 0.00, 1.7),
+ ( 28450, 136.00, 3.2),
+ ( 36450, 392.00, 4.7),
+ ( 52450, 768.00, 4.9),
+ ( 76450, 1552.00, 4.9),
+ (112450, 2728.00, 4.9),
+ (212550, 4492.00, 4.9),
+ (327550, 9392.00, 4.9),
+ ( 'inf', 15027.00, 5.9),
+ ),
+ },
+ 'married_as_single': {
+ 'weekly': (
+ ( 181, 0.00, 0.0),
+ ( 335, 0.00, 1.7),
+ ( 488, 2.62, 3.2),
+ ( 642, 7.54, 4.7),
+ ( 950, 14.77, 4.9),
+ ( 1412, 29.85, 4.9),
+ ( 2104, 52.46, 4.9),
+ ( 2027, 86.38, 4.9),
+ ( 6238, 180.62, 4.9),
+ ('inf', 288.98, 5.9),
+ ),
+ 'bi-weekly': (
+ ( 362, 0.00, 0.0),
+ ( 669, 0.00, 1.7),
+ ( 977, 5.23, 3.2),
+ ( 1285, 15.08, 4.7),
+ ( 1900, 29.54, 4.9),
+ ( 2823, 59.69, 4.9),
+ ( 4208, 104.92, 4.9),
+ ( 8054, 172.77, 4.9),
+ (12477, 361.23, 4.9),
+ ('inf', 577.96, 5.9),
+ ),
+ 'semi-monthly': (
+ ( 392, 0.00, 0.0),
+ ( 725, 0.00, 1.7),
+ ( 1058, 5.67, 3.2),
+ ( 1392, 16.33, 4.7),
+ ( 2058, 32.00, 4.9),
+ ( 3058, 64.67, 4.9),
+ ( 4558, 113.67, 4.9),
+ ( 8725, 187.17, 4.9),
+ (13517, 391.33, 4.9),
+ ('inf', 626.13, 5.9),
+ ),
+ 'monthly': (
+ ( 783, 0.00, 0.0),
+ ( 1450, 0.00, 1.7),
+ ( 2117, 11.33, 3.2),
+ ( 2783, 32.67, 4.7),
+ ( 4117, 64.00, 4.9),
+ ( 6117, 129.33, 4.9),
+ ( 9117, 227.33, 4.9),
+ (17450, 374.33, 4.9),
+ (27033, 782.67, 4.9),
+ ('inf', 1252.25, 5.9),
+ ),
+ 'quarterly': (
+ ( 2350, 0.00, 0.0),
+ ( 4350, 0.00, 1.7),
+ ( 6350, 34.00, 3.2),
+ ( 8350, 98.00, 4.7),
+ ( 12350, 192.00, 4.9),
+ ( 18350, 388.00, 4.9),
+ ( 27350, 682.00, 4.9),
+ ( 52350, 1123.00, 4.9),
+ ( 81100, 2348.00, 4.9),
+ ( 'inf', 3756.75, 5.9),
+ ),
+ 'semi-annual': (
+ ( 4700, 0.00, 0.0),
+ ( 8700, 0.00, 1.7),
+ ( 12700, 68.00, 3.2),
+ ( 16700, 196.00, 4.7),
+ ( 24700, 384.00, 4.9),
+ ( 36700, 776.00, 4.9),
+ ( 54700, 1364.00, 4.9),
+ (104700, 2246.00, 4.9),
+ (162200, 4696.00, 4.9),
+ ( 'inf', 7513.50, 5.9),
+ ),
+ 'annually': (
+ ( 9400, 0.00, 0.0),
+ ( 17400, 0.00, 1.7),
+ ( 25400, 136.00, 3.2),
+ ( 33400, 392.00, 4.7),
+ ( 49400, 768.00, 4.9),
+ ( 73400, 1552.00, 4.9),
+ (109400, 2728.00, 4.9),
+ (209400, 4492.00, 4.9),
+ (324400, 9392.00, 4.9),
+ ( 'inf', 15027.00, 5.9),
+ ),
+ }
+ }
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/nv_nevada.xml b/l10n_us_hr_payroll/data/state/nv_nevada.xml
index 46e3c2d5..b5b85f1e 100644
--- a/l10n_us_hr_payroll/data/state/nv_nevada.xml
+++ b/l10n_us_hr_payroll/data/state/nv_nevada.xml
@@ -12,6 +12,11 @@
+
+ 33400.00
+
+
+
@@ -21,6 +26,7 @@
+
2.95
diff --git a/l10n_us_hr_payroll/data/state/ny_new_york.xml b/l10n_us_hr_payroll/data/state/ny_new_york.xml
index a6f17a70..4bb0ac36 100644
--- a/l10n_us_hr_payroll/data/state/ny_new_york.xml
+++ b/l10n_us_hr_payroll/data/state/ny_new_york.xml
@@ -1,22 +1,23 @@
+
US NY New York SUTA Wage Base
us_ny_suta_wage_base
-
- 11400.0
-
-
-
11600.0
+
+ 11800.0
+
+
+
@@ -25,6 +26,8 @@
us_ny_suta_rate
+
+
2.5
@@ -80,26 +83,6 @@
-
- {
- 'single': {
- 'weekly': ((163, 0.0400, 0.0), (225, 0.0450, 6.54), (267, 0.0525, 9.31), (412, 0.0590, 11.54), (1551, 0.0633, 20.04), (1862, 0.0657, 92.17), (2070, 0.0758, 112.58), (3032, 0.0808, 128.38), (4142, 0.0707, 206.08), (5104, 0.0856, 284.60), (20722, 0.0735, 366.90), (21684, 0.5208, 1514.85), ('inf', 0.0962, 2015.62)),
- 'bi-weekly': ((327, 0.0400, 0.0), (450, 0.0450, 13.08), (535, 0.0525, 18.62), (823, 0.0590, 23.08), (3102, 0.0633, 40.08), (3723, 0.0657, 184.35), (4140, 0.0758, 225.15), (6063, 0.0808, 256.77), (8285, 0.0707, 412.15), (10208, 0.0856, 569.19), (41444, 0.0735, 733.81), (43367, 0.5208, 3029.69), ('inf', 0.0962, 4021.23)),
- 'semi-monthly': ((354, 0.0400, 0.0), (488, 0.0450, 14.17), (579, 0.0525, 20.17), (892, 0.0590, 25.00), (3360, 0.0633, 43.42), (4033, 0.0657, 199.71), (4485, 0.0758, 243.92), (6569, 0.0808, 278.17), (8975, 0.0707, 446.50), (11058, 0.0856, 616.63), (44898, 0.0735, 794.96), (46981, 0.5208, 3282.17), ('inf', 0.0962, 4367.17)),
- 'monthly': ((708, 0.0400, 0.0), (975, 0.0450, 28.33), (1158, 0.0525, 40.33), (1783, 0.0590, 50.00), (6721, 0.0633, 86.83), (8067, 0.0657, 399.42), (8971, 0.0758, 487.83), (13138, 0.0808, 556.33), (17950, 0.0707, 893.00), (22117, 0.0856, 1233.25), (89796, 0.0735, 1589.92), (93963, 0.5208, 6564.33), ('inf', 0.0962, 8734.33)),
- 'annually': ((8500, 0.0400, 0.0), (11700, 0.0450, 340.00), (13900, 0.0525, 484.00), (21400, 0.0590, 600.00), (80650, 0.0633, 1042.00), (96800, 0.0657, 4793.00), (107650, 0.0758, 5854.00), (157650, 0.0808, 6676.00), (215400, 0.0707, 10716.00), (265400, 0.0856, 14799.00), (1077550, 0.0735, 19079.00), (1127550, 0.5208, 78772.00), ('inf', 0.0962, 104812.00)),
- },
- 'married': {
- 'weekly': ((163, 0.0400, 0.0), (225, 0.0450, 6.54), (267, 0.0525, 9.31), (412, 0.0590, 11.54), (1551, 0.0633, 20.04), (1862, 0.0657, 92.17), (2070, 0.0783, 112.58), (3032, 0.0833, 128.90), (4068, 0.0785, 209.00), (6215, 0.0707, 290.37), (7177, 0.0916, 442.17), (20722, 0.0735, 530.25), (41449, 0.0765, 1525.83), (42411, 0.9454, 3111.42), ('inf', 0.0962, 4020.46)),
- 'bi-weekly': ((327, 0.0400, 0.0), (450, 0.0450, 13.08), (535, 0.0525, 18.62), (823, 0.0590, 23.08), (3102, 0.0633, 40.08), (3723, 0.0657, 184.35), (4140, 0.0783, 225.15), (6063, 0.0833, 257.81), (8137, 0.0785, 418.00), (12431, 0.0707, 580.73), (14354, 0.0916, 884.35), (41444, 0.0735, 1060.50), (82898, 0.0765, 3051.65), (84821, 0.9454, 6222.85), ('inf', 0.0962, 8040.92)),
- 'semi-monthly': ((354, 0.0400, 0.0), (488, 0.0450, 14.17), (579, 0.0525, 20.17), (892, 0.0590, 25.00), (3360, 0.0633, 43.42), (4033, 0.0657, 199.71), (4485, 0.0783, 243.92), (6569, 0.0833, 279.29), (8815, 0.0785, 452.83), (13476, 0.0707, 629.13), (15550, 0.0916, 958.04), (44898, 0.0735, 1148.88), (89806, 0.0765, 3305.96), (91890, 0.9454, 6741.42), ('inf', 0.0962, 8711.00)),
- 'monthly': ((708, 0.0400, 0.0), (975, 0.0450, 28.33), (1158, 0.0525, 40.33), (1783, 0.0590, 50.00), (6721, 0.0633, 86.83), (8067, 0.0657, 399.42), (8971, 0.0783, 487.83), (13138, 0.0833, 558.58), (17629, 0.0785, 905.67), (26933, 0.0707, 1258.25), (31100, 0.0916, 1916.08), (89796, 0.0735, 2297.75), (179613, 0.0765, 6611.92), (183779, 0.9454, 13482.83), ('inf', 0.0962, 17422.00)),
- 'annually': ((8500, 0.0400, 0.0), (11700, 0.0450, 340.00), (13900, 0.0525, 484.00), (21400, 0.0590, 600.00), (80650, 0.0633, 1042.00), (96800, 0.0657, 4793.00), (107650, 0.0783, 5854.00), (157650, 0.0833, 6703.00), (211550, 0.0785, 10868.00), (323200, 0.0707, 15099.00), (373200, 0.0916, 22993.00), (1077550, 0.0735, 27573.00), (2155350, 0.0765, 79343.00), (2205350, 0.9454, 161794.00), ('inf', 0.0962, 209064.00)),
- }
- }
-
-
-
{
'single': {
@@ -270,6 +253,177 @@
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 163, 0.0400, 0.00),
+ ( 225, 0.0450, 6.54),
+ ( 267, 0.0525, 9.31),
+ ( 412, 0.0590, 11.54),
+ ( 1551, 0.0597, 20.04),
+ ( 1862, 0.0633, 88.06),
+ ( 2070, 0.0738, 107.73),
+ ( 3032, 0.0788, 123.12),
+ ( 4142, 0.0683, 198.88),
+ ( 5104, 0.0959, 274.75),
+ (20722, 0.0735, 366.96),
+ (21684, 0.5208, 1514.90),
+ ('inf', 0.0962, 2015.67),
+ ),
+ 'bi-weekly': (
+ ( 327, 0.0400, 0.00),
+ ( 450, 0.0450, 13.08),
+ ( 535, 0.0525, 18.62),
+ ( 823, 0.0590, 23.08),
+ ( 3102, 0.0597, 40.08),
+ ( 3723, 0.0633, 176.12),
+ ( 4140, 0.0738, 215.46),
+ ( 6063, 0.0788, 246.23),
+ ( 8285, 0.0683, 397.77),
+ (10208, 0.0959, 549.50),
+ (41444, 0.0735, 733.92),
+ (43367, 0.5208, 3029.81),
+ ('inf', 0.0962, 4031.45),
+ ),
+ 'semi-monthly': (
+ ( 354, 0.0400, 0.00),
+ ( 488, 0.0450, 14.17),
+ ( 579, 0.0525, 20.17),
+ ( 892, 0.0590, 25.00),
+ ( 3360, 0.0597, 43.42),
+ ( 4033, 0.0633, 190.79),
+ ( 4485, 0.0738, 233.42),
+ ( 6569, 0.0788, 266.75),
+ ( 8975, 0.0683, 430.92),
+ (11058, 0.0959, 595.29),
+ (44898, 0.0735, 795.08),
+ (46981, 0.5208, 3282.29),
+ ('inf', 0.0962, 4367.29),
+ ),
+ 'monthly': (
+ ( 708, 0.0400, 0.00),
+ ( 975, 0.0450, 28.33),
+ ( 1158, 0.0525, 40.33),
+ ( 1783, 0.0590, 50.00),
+ ( 6721, 0.0597, 86.83),
+ ( 8067, 0.0633, 381.58),
+ ( 8971, 0.0738, 466.83),
+ (13138, 0.0788, 533.50),
+ (17950, 0.0683, 861.83),
+ (22117, 0.0959, 1190.58),
+ (89796, 0.0735, 1590.17),
+ (93963, 0.5208, 6564.58),
+ ('inf', 0.0962, 8734.58),
+ ),
+ 'annually': (
+ ( 8500, 0.0400, 0.00),
+ ( 11700, 0.0450, 340.00),
+ ( 13900, 0.0525, 484.00),
+ ( 21400, 0.0590, 600.00),
+ ( 80650, 0.0597, 1042.00),
+ ( 96800, 0.0633, 4579.00),
+ ( 107650, 0.0738, 5602.00),
+ ( 157650, 0.0788, 6402.00),
+ ( 215400, 0.0683, 10342.00),
+ ( 265400, 0.0959, 14287.00),
+ (1077550, 0.0735, 19082.00),
+ (1127550, 0.5208, 78775.00),
+ ( 'inf', 0.0962, 104815.00),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 163, 0.0400, 0.00),
+ ( 225, 0.0450, 6.54),
+ ( 267, 0.0525, 9.31),
+ ( 412, 0.0590, 11.54),
+ ( 1551, 0.0597, 20.04),
+ ( 1862, 0.0633, 88.06),
+ ( 2070, 0.0728, 107.73),
+ ( 3032, 0.0778, 122.90),
+ ( 4068, 0.0799, 197.71),
+ ( 6215, 0.0683, 280.54),
+ ( 7177, 0.1071, 427.19),
+ (20722, 0.0735, 530.17),
+ (41449, 0.0765, 1525.73),
+ (42411, 0.9454, 3111.35),
+ ('inf', 0.0962, 4020.38),
+ ),
+ 'bi-weekly': (
+ ( 327, 0.0400, 0.00),
+ ( 450, 0.0450, 13.08),
+ ( 535, 0.0525, 18.62),
+ ( 823, 0.0590, 23.08),
+ ( 3102, 0.0597, 40.08),
+ ( 3723, 0.0633, 176.12),
+ ( 4140, 0.0728, 215.46),
+ ( 6063, 0.0778, 245.81),
+ ( 8137, 0.0799, 395.42),
+ (12431, 0.0683, 561.08),
+ (14354, 0.1071, 854.38),
+ (41444, 0.0735, 1060.35),
+ (82898, 0.0765, 3051.46),
+ (84821, 0.9454, 6222.69),
+ ('inf', 0.0962, 8040.77),
+ ),
+ 'semi-monthly': (
+ ( 354, 0.0400, 0.00),
+ ( 488, 0.0450, 14.17),
+ ( 579, 0.0525, 20.17),
+ ( 892, 0.0590, 25.00),
+ ( 3360, 0.0597, 43.42),
+ ( 4033, 0.0633, 190.79),
+ ( 4485, 0.0728, 233.42),
+ ( 6569, 0.0778, 266.29),
+ ( 8815, 0.0799, 428.38),
+ (13467, 0.0683, 607.83),
+ (15550, 0.1071, 925.58),
+ (44898, 0.0735, 1148.71),
+ (89806, 0.0765, 3305.75),
+ (91890, 0.9454, 6741.25),
+ ('inf', 0.0962, 8710.83),
+ ),
+ 'monthly': (
+ ( 708, 0.0400, 0.00),
+ ( 975, 0.0450, 28.33),
+ ( 1158, 0.0525, 40.33),
+ ( 1783, 0.0590, 50.00),
+ ( 6721, 0.0597, 86.83),
+ ( 8067, 0.0633, 381.58),
+ ( 8971, 0.0728, 466.83),
+ ( 13138, 0.0778, 532.58),
+ ( 17629, 0.0799, 856.75),
+ ( 26933, 0.0683, 1215.67),
+ ( 31100, 0.1071, 1851.17),
+ ( 89796, 0.0735, 2297.42),
+ (179613, 0.0765, 6611.50),
+ (183779, 0.9454, 13482.50),
+ ( 'inf', 0.0962, 17421.67),
+ ),
+ 'annually': (
+ ( 8500, 0.0400, 0.00),
+ ( 11700, 0.0450, 340.00),
+ ( 13900, 0.0525, 484.00),
+ ( 21400, 0.0590, 600.00),
+ ( 80650, 0.0597, 1042.00),
+ ( 96800, 0.0633, 4579.00),
+ ( 107650, 0.0728, 5602.00),
+ ( 157650, 0.0778, 6391.00),
+ ( 211550, 0.0799, 10281.00),
+ ( 323200, 0.0683, 14588.00),
+ ( 373200, 0.1071, 22214.00),
+ (1077550, 0.0735, 27569.00),
+ (2155350, 0.0765, 79338.00),
+ (2205350, 0.9454, 161790.00),
+ ( 'inf', 0.0962, 209060.00),
+ ),
+ }
+ }
+
+
+
@@ -278,17 +432,6 @@
-
- {
- 'weekly': (142.30, 152.90, 19.25),
- 'bi-weekly': (284.60, 305.80, 38.50),
- 'semi-monthly': (308.35, 331.25, 41.65),
- 'monthly': (616.70, 662.50, 83.30),
- 'annual': (7400, 7950, 1000),
- }
-
-
-
{
'weekly': (142.30, 152.90, 19.25),
@@ -300,6 +443,17 @@
+
+ {
+ 'weekly': (142.30, 152.90, 19.25),
+ 'bi-weekly': (284.60, 305.80, 38.50),
+ 'semi-monthly': (308.35, 331.25, 41.65),
+ 'monthly': (616.70, 662.50, 83.30),
+ 'annual': (7400, 7950, 1000),
+ }
+
+
+
@@ -308,26 +462,6 @@
-
- {
- 'single': {
- 'weekly': (142.30, 161.55, 180.80, 200.05, 219.30, 238.55, 257.80, 277.05, 296.30, 315.55, 334.80),
- 'bi-weekly': (284.60, 323.10, 361.60, 400.10, 438.60, 477.10, 515.60, 544.10, 592.60, 631.10, 669.60),
- 'semi-monthly': (308.35, 350.0, 391.65, 433.30, 474.95, 516.60, 558.25, 599.90, 641.55, 683.20, 724.85),
- 'monthly': (616.70, 700, 783.30, 866.60, 949.90, 1033.20, 1116.50, 1199.80, 1283.10, 1366.40, 1449.70),
- 'annually': (7400, 8400, 9400, 10400, 11400, 12400, 13400, 14400, 15400, 16400, 17400),
- },
- 'married': {
- 'weekly': (152.90, 172.15, 191.40, 210.65, 229.90, 249.15, 268.40, 287.65, 306.90, 326.15, 345.40),
- 'bi-weekly': (305.80, 344.30, 382.80, 421.30, 459.80, 498.30, 536.80, 575.30, 613.80, 652.30, 690.80),
- 'semi-monthly': (331.25, 372.90, 414.55, 456.20, 497.85, 539.50, 581.15, 622.80, 664.45, 706.10, 747.75),
- 'monthly': (662.50, 745.80, 829.10, 912.40, 995.70, 1079.00, 1162.30, 1245.60, 1328.90, 1412.20, 1495.50),
- 'annually': (7950, 8950, 9950, 10950, 11950, 12950, 13950, 14950, 15950, 16950, 17950),
- },
- }
-
-
-
{
'single': {
@@ -348,6 +482,26 @@
+
+ {
+ 'single': {
+ 'weekly': (142.30, 161.55, 180.80, 200.05, 219.30, 238.55, 257.80, 277.05, 296.30, 315.55, 334.80),
+ 'bi-weekly': (284.60, 323.10, 361.60, 400.10, 438.60, 477.10, 515.60, 544.10, 592.60, 631.10, 669.60),
+ 'semi-monthly': (308.35, 350.0, 391.65, 433.30, 474.95, 516.60, 558.25, 599.90, 641.55, 683.20, 724.85),
+ 'monthly': (616.70, 700, 783.30, 866.60, 949.90, 1033.20, 1116.50, 1199.80, 1283.10, 1366.40, 1449.70),
+ 'annually': (7400, 8400, 9400, 10400, 11400, 12400, 13400, 14400, 15400, 16400, 17400),
+ },
+ 'married': {
+ 'weekly': (152.90, 172.15, 191.40, 210.65, 229.90, 249.15, 268.40, 287.65, 306.90, 326.15, 345.40),
+ 'bi-weekly': (305.80, 344.30, 382.80, 421.30, 459.80, 498.30, 536.80, 575.30, 613.80, 652.30, 690.80),
+ 'semi-monthly': (331.25, 372.90, 414.55, 456.20, 497.85, 539.50, 581.15, 622.80, 664.45, 706.10, 747.75),
+ 'monthly': (662.50, 745.80, 829.10, 912.40, 995.70, 1079.00, 1162.30, 1245.60, 1328.90, 1412.20, 1495.50),
+ 'annually': (7950, 8950, 9950, 10950, 11950, 12950, 13950, 14950, 15950, 16950, 17950),
+ },
+ }
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/oh_ohio.xml b/l10n_us_hr_payroll/data/state/oh_ohio.xml
index e6db8eb8..1d3344a0 100644
--- a/l10n_us_hr_payroll/data/state/oh_ohio.xml
+++ b/l10n_us_hr_payroll/data/state/oh_ohio.xml
@@ -7,11 +7,6 @@
-
- 9500.00
-
-
-
9000.00
@@ -20,17 +15,13 @@
+
US OH Ohio SUTA Rate
us_oh_suta_rate
-
- 2.7
-
-
-
2.7
@@ -38,28 +29,14 @@
+
+
US OH Ohio SIT Rate Table
us_oh_sit_rate
-
-
-
- [
- ( 5000.00, 0.0, 0.005),
- ( 10000.00, 25.0, 0.010),
- ( 15000.00, 75.0, 0.020),
- ( 20000.00, 175.0, 0.025),
- ( 40000.00, 300.0, 0.030),
- ( 80000.00, 900.0, 0.035),
- ( 100000.00, 2300.0, 0.040),
- ( 'inf', 3100.0, 0.050),
- ]
-
-
-
@@ -84,11 +61,6 @@
-
- 650.0
-
-
-
650.0
@@ -102,11 +74,6 @@
-
- 1.075
-
-
-
1.032
diff --git a/l10n_us_hr_payroll/data/state/ok_oklahoma.xml b/l10n_us_hr_payroll/data/state/ok_oklahoma.xml
index 04aa8380..8968ea39 100644
--- a/l10n_us_hr_payroll/data/state/ok_oklahoma.xml
+++ b/l10n_us_hr_payroll/data/state/ok_oklahoma.xml
@@ -12,6 +12,12 @@
+
+
+ 24000.0
+
+
+
@@ -26,7 +32,14 @@
+
+
+ 1.0
+
+
+
+
US OK Oklahoma Allowances Rate
@@ -34,6 +47,7 @@
+
{
'weekly' : 19.23,
@@ -48,13 +62,15 @@
-
+
US OK Oklahoma SIT Tax Rate
us_ok_sit_tax_rate
+
+
{
'single': {
diff --git a/l10n_us_hr_payroll/data/state/pa_pennsylvania.xml b/l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
index f46a92b4..c8002192 100644
--- a/l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
+++ b/l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
@@ -7,11 +7,7 @@
-
- 10000.00
-
-
-
+
10000.00
@@ -26,11 +22,7 @@
-
- 3.6890
-
-
-
+
3.6890
@@ -44,11 +36,7 @@
-
- 0.06
-
-
-
+
0.06
@@ -62,11 +50,7 @@
-
- 3.07
-
-
-
+
3.07
diff --git a/l10n_us_hr_payroll/data/state/ri_rhode_island.xml b/l10n_us_hr_payroll/data/state/ri_rhode_island.xml
index 155a2c86..5c9e05c8 100644
--- a/l10n_us_hr_payroll/data/state/ri_rhode_island.xml
+++ b/l10n_us_hr_payroll/data/state/ri_rhode_island.xml
@@ -7,6 +7,7 @@
+
24000.0
@@ -21,12 +22,14 @@
+
1.06
+
US RI Rhode Island Exemption Rate
@@ -47,6 +50,20 @@
+
+
+ {
+ 'weekly' : (( 0.00, 19.23), ( 4514.42, 0.00)),
+ 'bi-weekly' : (( 0.00, 38.46), ( 9028.85, 0.00)),
+ 'semi-monthly': (( 0.00, 41.67), ( 9781.25, 0.00)),
+ 'monthly' : (( 0.00, 83.33), ( 19562.50, 0.00)),
+ 'quarterly' : (( 0.00, 250.00), ( 58687.50, 0.00)),
+ 'semi-annually': (( 0.00, 500.00), ( 117375.00, 0.00)),
+ 'annually': (( 0.00, 1000.0), ( 234750.00, 0.00)),
+ }
+
+
+
@@ -96,6 +113,48 @@
+
+
+ {
+ 'weekly': (
+ ( 1273, 0.00, 3.75),
+ ( 2895, 47.74, 4.75),
+ ('inf', 124.79, 5.99),
+ ),
+ 'bi-weekly': (
+ ( 2546, 0.00, 3.75),
+ ( 5790, 95.48, 4.75),
+ ('inf', 249.57, 5.99),
+ ),
+ 'semi-monthly': (
+ ( 2758, 0.00, 3.75),
+ ( 6273, 103.43, 4.75),
+ ('inf', 270.39, 5.99),
+ ),
+ 'monthly': (
+ ( 5517, 0.00, 3.75),
+ (12546, 206.89, 4.75),
+ ('inf', 540.77, 5.99),
+ ),
+ 'quarterly': (
+ (16550, 0.00, 3.75),
+ (37638, 620.63, 4.75),
+ ('inf', 1622.31, 5.99),
+ ),
+ 'semi-annually': (
+ (33100, 0.00, 3.75),
+ (75275, 1241.25, 4.75),
+ ('inf', 3244.56, 5.99),
+ ),
+ 'annually': (
+ ( 66200, 0.00, 3.75),
+ (150550, 2482.50, 4.75),
+ ( 'inf', 6489.13, 5.99),
+ ),
+ }
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/sc_south_carolina.xml b/l10n_us_hr_payroll/data/state/sc_south_carolina.xml
index b2a46192..04a10d70 100644
--- a/l10n_us_hr_payroll/data/state/sc_south_carolina.xml
+++ b/l10n_us_hr_payroll/data/state/sc_south_carolina.xml
@@ -7,11 +7,7 @@
-
- 14000.0
-
-
-
+
14000.0
@@ -26,12 +22,8 @@
-
- 1.09
-
-
-
+
0.55
@@ -70,6 +62,19 @@
+
+
+ [
+ ( 2800, 0.5, 0.0),
+ ( 5610, 3.0, 70.00),
+ ( 8410, 4.0, 126.10),
+ (11220, 5.0, 210.20),
+ (14030, 6.0, 322.40),
+ ('inf', 7.0, 462.70),
+ ]
+
+
+
@@ -78,16 +83,17 @@
-
- 2510
-
-
-
2590
+
+
+ 2670
+
+
+
@@ -96,16 +102,16 @@
-
- 3470.0
-
-
-
3820.0
+
+ 4200
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/sd_south_dakota.xml b/l10n_us_hr_payroll/data/state/sd_south_dakota.xml
index 95165ae5..0b44d1c8 100644
--- a/l10n_us_hr_payroll/data/state/sd_south_dakota.xml
+++ b/l10n_us_hr_payroll/data/state/sd_south_dakota.xml
@@ -7,6 +7,7 @@
+
15000.00
@@ -21,6 +22,7 @@
+
1.75
diff --git a/l10n_us_hr_payroll/data/state/tx_texas.xml b/l10n_us_hr_payroll/data/state/tx_texas.xml
index 5d9c5772..d188e480 100644
--- a/l10n_us_hr_payroll/data/state/tx_texas.xml
+++ b/l10n_us_hr_payroll/data/state/tx_texas.xml
@@ -7,11 +7,7 @@
-
- 9000.0
-
-
-
+
9000.0
@@ -26,11 +22,7 @@
-
- 2.7
-
-
-
+
2.7
@@ -44,11 +36,7 @@
-
- 0.0
-
-
-
+
0.0
@@ -62,11 +50,7 @@
-
- 0.1
-
-
-
+
0.1
diff --git a/l10n_us_hr_payroll/data/state/ut_utah.xml b/l10n_us_hr_payroll/data/state/ut_utah.xml
index cb57813c..cb3b9710 100644
--- a/l10n_us_hr_payroll/data/state/ut_utah.xml
+++ b/l10n_us_hr_payroll/data/state/ut_utah.xml
@@ -12,6 +12,12 @@
+
+
+ 38900.0
+
+
+
@@ -26,6 +32,13 @@
+
+
+
+ 0.1052
+
+
+
@@ -34,6 +47,7 @@
+
0.0495
@@ -47,6 +61,9 @@
+
+
+
{
'single': {
@@ -88,6 +105,8 @@
+
+
{
'single': {
diff --git a/l10n_us_hr_payroll/data/state/va_virginia.xml b/l10n_us_hr_payroll/data/state/va_virginia.xml
index 5d19384a..d1c2db08 100644
--- a/l10n_us_hr_payroll/data/state/va_virginia.xml
+++ b/l10n_us_hr_payroll/data/state/va_virginia.xml
@@ -7,11 +7,7 @@
-
- 8000.0
-
-
-
+
8000.0
@@ -26,17 +22,20 @@
-
- 2.51
-
-
-
2.51
+
+
+ 2.5
+
+
+
+
+
US VA Virginia SIT Rate Table
@@ -44,7 +43,8 @@
-
+
+
[
( 0.00, 0.0, 2.00),
( 3000.00, 60.0, 3.00),
@@ -52,7 +52,7 @@
( 17000.00, 720.0, 5.75),
]
-
+
@@ -62,10 +62,11 @@
-
+
+
930.0
-
+
@@ -75,10 +76,11 @@
-
+
+
800.0
-
+
@@ -88,10 +90,11 @@
-
+
+
4500.0
-
+
diff --git a/l10n_us_hr_payroll/data/state/vt_vermont.xml b/l10n_us_hr_payroll/data/state/vt_vermont.xml
index 74f1a491..9ee8c497 100644
--- a/l10n_us_hr_payroll/data/state/vt_vermont.xml
+++ b/l10n_us_hr_payroll/data/state/vt_vermont.xml
@@ -12,6 +12,11 @@
+
+ 14100.0
+
+
+
@@ -21,6 +26,7 @@
+
1.0
@@ -46,6 +52,19 @@
+
+
+ {
+ 'weekly' : 84.62,
+ 'bi-weekly' : 169.23,
+ 'semi-monthly': 183.33,
+ 'monthly' : 366.67,
+ 'quarterly' : 1100.00,
+ 'annually': 4400.00,
+ }
+
+
+
@@ -148,6 +167,101 @@
+
+
+ {
+ 'single': {
+ 'weekly': (
+ ( 61, 0.00, 0.00),
+ ( 849, 0.00, 3.35),
+ ( 1969, 26.40, 6.60),
+ ( 4041, 100.32, 7.60),
+ ('inf', 257.79, 8.75),
+ ),
+ 'bi-weekly': (
+ ( 122, 0.00, 0.00),
+ ( 1697, 0.00, 3.35),
+ ( 3938, 52.76, 6.60),
+ ( 8082, 200.67, 7.60),
+ ('inf', 515.61, 8.75),
+ ),
+ 'semi-monthly': (
+ ( 132, 0.00, 0.00),
+ ( 1839, 0.00, 3.35),
+ ( 4266, 57.18, 6.60),
+ ( 8755, 217.37, 7.60),
+ ('inf', 558.53, 8.75),
+ ),
+ 'monthly': (
+ ( 265, 0.00, 0.00),
+ ( 3677, 0.00, 3.35),
+ ( 8531, 114.30, 6.60),
+ (17510, 434.67, 7.60),
+ ('inf', 1117.07, 8.75),
+ ),
+ 'quarterly': (
+ ( 794, 0.00, 0.00),
+ (11031, 0.00, 3.35),
+ (25594, 342.94, 6.60),
+ (52531, 1304.10, 7.60),
+ ('inf', 3351.31, 8.75),
+ ),
+ 'annually': (
+ ( 3175, 0.00, 0.00),
+ ( 44125, 0.00, 3.35),
+ (102375, 1371.83, 6.60),
+ (210125, 5216.33, 7.60),
+ ( 'inf', 13405.33, 8.75),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 183, 0.00, 0.00),
+ ( 1499, 0.00, 3.35),
+ ( 3363, 44.09, 6.60),
+ ( 5028, 167.11, 7.60),
+ ('inf', 293.65, 8.75),
+ ),
+ 'bi-weekly': (
+ ( 366, 0.00, 0.00),
+ ( 2997, 0.00, 3.35),
+ ( 6726, 88.14, 6.60),
+ (10057, 334.25, 7.60),
+ ('inf', 587.41, 8.75),
+ ),
+ 'semi-monthly': (
+ ( 397, 0.00, 0.00),
+ ( 3247, 0.00, 3.35),
+ ( 7286, 95.48, 6.60),
+ (10895, 362.05, 7.60),
+ ('inf', 636.33, 8.75),
+ ),
+ 'monthly': (
+ ( 794, 0.00, 0.00),
+ ( 6494, 0.00, 3.35),
+ (14573, 190.95, 6.60),
+ (21790, 724.16, 7.60),
+ ('inf', 1272.66, 8.75),
+ ),
+ 'quarterly': (
+ ( 2381, 0.00, 0.00),
+ (19481, 0.00, 3.35),
+ (43719, 572.85, 6.60),
+ (65719, 2172.56, 7.60),
+ ('inf', 3817.96, 8.75),
+ ),
+ 'annually': (
+ ( 9525, 0.00, 0.00),
+ ( 77925, 0.00, 3.35),
+ (174875, 2291.40, 6.60),
+ (261475, 8690.10, 7.60),
+ ( 'inf', 15271.70, 8.75),
+ ),
+ },
+ }
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/wa_washington.xml b/l10n_us_hr_payroll/data/state/wa_washington.xml
index 3307689c..53a4e491 100644
--- a/l10n_us_hr_payroll/data/state/wa_washington.xml
+++ b/l10n_us_hr_payroll/data/state/wa_washington.xml
@@ -1,22 +1,24 @@
+
+
US WA Washington SUTA Wage Base
us_wa_suta_wage_base
-
- 49800.0
-
-
-
52700.00
+
+ 56500.00
+
+
+
@@ -25,16 +27,17 @@
-
- 132900.00
-
-
-
+
137700.00
+
+ 142800.00
+
+
+
@@ -43,17 +46,23 @@
us_wa_suta_rate
+
-
- 1.18
-
-
-
1.0
+
+
+
+
+
+
+ 2.16
+
+
+
@@ -62,16 +71,17 @@
-
- 0.4
-
-
-
+
0.4
+
+ 0.4
+
+
+
@@ -80,16 +90,18 @@
-
- 66.33
-
-
-
66.33
+
+
+
+ 66.33
+
+
+
@@ -98,16 +110,18 @@
-
- 33.67
-
-
-
33.67
+
+
+
+ 33.67
+
+
+
@@ -133,9 +147,13 @@
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')
+ 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')
+ 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')
+
@@ -147,9 +165,12 @@
ER: US WA Washington State Family Medical Leave
ER_US_WA_FML
python
- result, _ = wa_washington_fml_er(payslip, categories, worked_days, inputs)
+ result, _ = wa_washington_fml_er(payslip, categories, worked_days, inputs)
+
code
- result, result_rate = wa_washington_fml_er(payslip, categories, worked_days, inputs)
+ result, result_rate = wa_washington_fml_er(payslip, categories, worked_days,
+ inputs)
+
@@ -161,9 +182,12 @@
EE: US WA Washington State Family Medical Leave
EE_US_WA_FML
python
- result, _ = wa_washington_fml_ee(payslip, categories, worked_days, inputs)
+ result, _ = wa_washington_fml_ee(payslip, categories, worked_days, inputs)
+
code
- result, result_rate = wa_washington_fml_ee(payslip, categories, worked_days, inputs)
+ result, result_rate = wa_washington_fml_ee(payslip, categories, worked_days,
+ inputs)
+
@@ -176,9 +200,11 @@
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_ee_code') and worked_days.WORK100 and worked_days.WORK100.number_of_hours and payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+ 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.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
code
- result, result_rate = worked_days.WORK100.number_of_hours, -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+ result, result_rate = worked_days.WORK100.number_of_hours,
+ -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+
@@ -192,7 +218,9 @@
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.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
code
- result, result_rate = worked_days.WORK100.number_of_hours, -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
+ result, result_rate = worked_days.WORK100.number_of_hours,
+ -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
+
diff --git a/l10n_us_hr_payroll/data/state/wi_wisconsin.xml b/l10n_us_hr_payroll/data/state/wi_wisconsin.xml
index b62dc3f4..491c7311 100644
--- a/l10n_us_hr_payroll/data/state/wi_wisconsin.xml
+++ b/l10n_us_hr_payroll/data/state/wi_wisconsin.xml
@@ -7,6 +7,7 @@
+
14000.00
@@ -15,13 +16,14 @@
-
US WI Wisconsin SUTA Rate
us_wi_suta_rate
+
+
3.05
@@ -42,13 +44,14 @@
-
US WI Wisconsin SIT Tax Rate
us_wi_sit_tax_rate
+
+
{
'single': (
diff --git a/l10n_us_hr_payroll/data/state/wv_west_virginia.xml b/l10n_us_hr_payroll/data/state/wv_west_virginia.xml
index ee542c4a..8ded67b7 100644
--- a/l10n_us_hr_payroll/data/state/wv_west_virginia.xml
+++ b/l10n_us_hr_payroll/data/state/wv_west_virginia.xml
@@ -7,6 +7,7 @@
+
12000.0
@@ -21,12 +22,14 @@
+
2.7
+
US WV West Virginia Exemption Rate
@@ -34,6 +37,7 @@
+
{
'weekly' : 38.46,
@@ -53,6 +57,7 @@
+
{
'single': {
diff --git a/l10n_us_hr_payroll/data/state/wy_wyoming.xml b/l10n_us_hr_payroll/data/state/wy_wyoming.xml
index 247603c5..2f5485e1 100644
--- a/l10n_us_hr_payroll/data/state/wy_wyoming.xml
+++ b/l10n_us_hr_payroll/data/state/wy_wyoming.xml
@@ -7,16 +7,17 @@
-
- 25400.00
-
-
-
26400.00
+
+
+ 27300.00
+
+
+
@@ -26,11 +27,9 @@
-
- 2.10
-
-
-
+
+
+
8.5
diff --git a/l10n_us_hr_payroll/tests/__init__.py b/l10n_us_hr_payroll/tests/__init__.py
index 0dcf26fb..9fd41db3 100755
--- a/l10n_us_hr_payroll/tests/__init__.py
+++ b/l10n_us_hr_payroll/tests/__init__.py
@@ -4,128 +4,147 @@ from . import common
from . import test_special
-from . import test_us_payslip_2019
from . import test_us_payslip_2020
+from . import test_us_payslip_2021
-from . import test_us_ak_alaska_payslip_2019
from . import test_us_ak_alaska_payslip_2020
+from . import test_us_ak_alaska_payslip_2021
-from . import test_us_al_alabama_payslip_2019
from . import test_us_al_alabama_payslip_2020
+from . import test_us_al_alabama_payslip_2021
-from . import test_us_ar_arkansas_payslip_2019
from . import test_us_ar_arkansas_payslip_2020
+from . import test_us_ar_arkansas_payslip_2021
-from . import test_us_az_arizona_payslip_2019
from . import test_us_az_arizona_payslip_2020
+from . import test_us_az_arizona_payslip_2021
-from . import test_us_ca_california_payslip_2019
from . import test_us_ca_california_payslip_2020
+from . import test_us_ca_california_payslip_2021
from . import test_us_co_colorado_payslip_2020
+from . import test_us_co_colorado_payslip_2021
-from . import test_us_ct_connecticut_payslip_2019
from . import test_us_ct_connecticut_payslip_2020
+from . import test_us_ct_connecticut_payslip_2021
from . import test_us_de_delaware_payslip_2020
+from . import test_us_de_delaware_payslip_2021
-from . import test_us_fl_florida_payslip_2019
from . import test_us_fl_florida_payslip_2020
+from . import test_us_fl_florida_payslip_2021
-from . import test_us_ga_georgia_payslip_2019
from . import test_us_ga_georgia_payslip_2020
+from . import test_us_ga_georgia_payslip_2021
-from . import test_us_hi_hawaii_payslip_2019
from . import test_us_hi_hawaii_payslip_2020
+from . import test_us_hi_hawaii_payslip_2021
-from . import test_us_ia_iowa_payslip_2019
from . import test_us_ia_iowa_payslip_2020
+from . import test_us_ia_iowa_payslip_2021
-from . import test_us_id_idaho_payslip_2019
from . import test_us_id_idaho_payslip_2020
+from . import test_us_id_idaho_payslip_2021
-from . import test_us_il_illinois_payslip_2019
from . import test_us_il_illinois_payslip_2020
+from . import test_us_il_illinois_payslip_2021
from . import test_us_in_indiana_payslip_2020
+from . import test_us_in_indiana_payslip_2021
from . import test_us_ky_kentucky_payslip_2020
+from . import test_us_ky_kentucky_payslip_2021
from . import test_us_ks_kansas_payslip_2020
+from . import test_us_ks_kansas_payslip_2021
-from . import test_us_la_louisiana_payslip_2019
from . import test_us_la_louisiana_payslip_2020
+from . import test_us_la_louisiana_payslip_2021
from . import test_us_me_maine_payslip_2020
+from . import test_us_me_maine_payslip_2021
-from . import test_us_mi_michigan_payslip_2019
from . import test_us_mi_michigan_payslip_2020
+from . import test_us_mi_michigan_payslip_2021
-from . import test_us_mn_minnesota_payslip_2019
from . import test_us_mn_minnesota_payslip_2020
+from . import test_us_mn_minnesota_payslip_2021
-from . import test_us_mo_missouri_payslip_2019
from . import test_us_mo_missouri_payslip_2020
+from . import test_us_mo_missouri_payslip_2021
-from . import test_us_ms_mississippi_payslip_2019
from . import test_us_ms_mississippi_payslip_2020
+from . import test_us_ms_mississippi_payslip_2021
-from . import test_us_mt_montana_payslip_2019
from . import test_us_mt_montana_payslip_2020
+from . import test_us_mt_montana_payslip_2021
-from . import test_us_nc_northcarolina_payslip_2019
from . import test_us_nc_northcarolina_payslip_2020
+from . import test_us_nc_northcarolina_payslip_2021
from . import test_us_nd_north_dakota_payslip_2020
+from . import test_us_nd_north_dakota_payslip_2021
from . import test_us_ne_nebraska_payslip_2020
+from . import test_us_ne_nebraska_payslip_2021
from . import test_us_nh_new_hampshire_payslip_2020
+from . import test_us_nh_new_hampshire_payslip_2021
-from . import test_us_nj_newjersey_payslip_2019
from . import test_us_nj_newjersey_payslip_2020
+from . import test_us_nj_newjersey_payslip_2021
from . import test_us_nm_new_mexico_payslip_2020
+from . import test_us_nm_new_mexico_payslip_2021
from . import test_us_nv_nevada_payslip_2020
+from . import test_us_nv_nevada_payslip_2021
-from . import test_us_ny_new_york_payslip_2019
from . import test_us_ny_new_york_payslip_2020
+from . import test_us_ny_new_york_payslip_2021
-from . import test_us_oh_ohio_payslip_2019
from . import test_us_oh_ohio_payslip_2020
+from . import test_us_oh_ohio_payslip_2021
from . import test_us_ok_oklahoma_payslip_2020
+from . import test_us_ok_oklahoma_payslip_2021
-from . import test_us_pa_pennsylvania_payslip_2019
from . import test_us_pa_pennsylvania_payslip_2020
+from . import test_us_pa_pennsylvania_payslip_2021
from . import test_us_ri_rhode_island_payslip_2020
+from . import test_us_ri_rhode_island_payslip_2021
-from . import test_us_sc_south_carolina_payslip_2019
from . import test_us_sc_south_carolina_payslip_2020
+from . import test_us_sc_south_carolina_payslip_2021
from . import test_us_sd_south_dakota_payslip_2020
+from . import test_us_sd_south_dakota_payslip_2021
from . import test_us_tn_tennessee_payslip_2020
+from . import test_us_tn_tennessee_payslip_2021
-from . import test_us_tx_texas_payslip_2019
from . import test_us_tx_texas_payslip_2020
+from . import test_us_tx_texas_payslip_2021
from . import test_us_us_utah_payslip_2020
+from . import test_us_us_utah_payslip_2021
from . import test_us_vt_vermont_payslip_2020
+from . import test_us_vt_vermont_payslip_2021
-from . import test_us_va_virginia_payslip_2019
from . import test_us_va_virginia_payslip_2020
+from . import test_us_va_virginia_payslip_2021
-from . import test_us_wa_washington_payslip_2019
from . import test_us_wa_washington_payslip_2020
+from . import test_us_wa_washington_payslip_2021
from . import test_us_wv_west_virginia_payslip_2020
+from . import test_us_wv_west_virginia_payslip_2021
from . import test_us_wi_wisconsin_payslip_2020
+from . import test_us_wi_wisconsin_payslip_2021
-from . import test_us_wy_wyoming_payslip_2019
from . import test_us_wy_wyoming_payslip_2020
+from . import test_us_wy_wyoming_payslip_2021
diff --git a/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py
deleted file mode 100644
index 3eb62184..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2019.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsAKPayslip(TestUsPayslip):
- # TAXES AND RATES
- AK_UNEMP_MAX_WAGE = 39900.00
- AK_UNEMP = -(1.780 / 100.0)
- AK_UNEMP_EE = -(0.5 / 100.0)
-
- def test_taxes_monthly_over_max(self):
- salary = 50000.00
- schedule_pay = 'monthly'
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AK'),
- state_income_tax_additional_withholding=0.0,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alaska 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.AK_UNEMP_MAX_WAGE * self.AK_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SUTA'], self.AK_UNEMP_MAX_WAGE * self.AK_UNEMP_EE)
-
- process_payslip(payslip)
-
- remaining_ak_unemp_wages = 0.00 # We already reached the maximum wage for unemployment insurance.
-
- self._log('2019 Alaska 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_ak_unemp_wages * self.AK_UNEMP) # 0
-
- def test_taxes_weekly_under_max(self):
- salary = 5000.00
- schedule_pay = 'weekly'
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AK'),
- state_income_tax_additional_withholding=0.0,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alaska 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.AK_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.AK_UNEMP_EE)
-
- process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2021.py
new file mode 100644
index 00000000..ebf84eda
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ak_alaska_payslip_2021.py
@@ -0,0 +1,15 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsAKPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ AK_UNEMP_MAX_WAGE = 43600.00
+ AK_UNEMP = 2.57
+ AK_UNEMP_EE = 0.5
+
+ def test_2021_taxes(self):
+ self._test_er_suta('AK', self.AK_UNEMP, date(2021, 1, 1), wage_base=self.AK_UNEMP_MAX_WAGE)
+ self._test_ee_suta('AK', self.AK_UNEMP_EE, date(2021, 1, 1), wage_base=self.AK_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
deleted file mode 100644
index 33ddb2f9..00000000
--- a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsALPayslip(TestUsPayslip):
- # TAXES AND RATES
- AL_UNEMP_MAX_WAGE = 8000.00
- AL_UNEMP = -2.70 / 100.0
-
- def test_taxes_weekly(self):
- salary = 10000.00
- schedule_pay = 'weekly'
- dependents = 1
- filing_status = 'S'
- # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
- # Hand Calculated Amount to Test
- # Step 1 -> 10000.00 for wages per period , 52.0 for weekly -> 10000 * 52 -> 520000.0
- # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
- # 520000 - 2000 = 518000.0
- # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -2999.66 * 52 = -155982.32
- # -> 518000.0 - 155982.32 = 362017.68
- # Step 2C -> Subtract the personal exemption -> 1500 for single filing_status
- # -> 362017.68 - 1500 = 360517.68
- # Step 2D -> Since income is so high, only 300$ per dependent -> 300$. Subtract
- # -> 360517.68 - 300 = 360217.68
- #
- # Step 5 (after adding previous lines) -> Compute marginal taxes.
- # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((360217.68 - 500 - 2500) * (5.00 / 100)) -> 17970.884000000002
- # Convert back to pay period
- # wh = round(17970.884000000002, 2) -> 17970.88 / 52.0 -> 345.59
- wh = -345.59
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AL'),
- al_a4_sit_exemptions=filing_status,
- state_income_tax_additional_withholding=0.0,
- state_income_tax_exempt=False,
- al_a4_sit_dependents=dependents,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alabama tax first payslip weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_941_FIT'], -2999.66) # Hand Calculated.
- self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- remaining_AL_UNEMP_wages = 0.00 # We already reached the maximum wage for unemployment insurance.
-
- self._log('2019 Alabama 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_AL_UNEMP_wages * self.AL_UNEMP) # 0
-
- def test_taxes_married_jointly(self):
- salary = 10000.00
- schedule_pay = 'weekly'
- dependents = 1
- filing_status = 'M'
-
- # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
- # Hand Calculated Amount to Test
- # Step 1 -> 10000.00 for wages per period , 52.0 for weekly -> 10000 * 52 -> 520000.0
- # Step 2A -> standard deduction for highest wage bracket -> 4000. Subtract from yearly income
- # 520000 - 4000 = 516000.0
- # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -2999.66 * 52 = -155982.32
- # -> 516000.0 - 155982.32 = 360017.68
- # Step 2C -> Subtract the personal exemption -> 3000 for married filing jointly.
- # -> 360017.68 - 3000 = 357017.68
- # Step 2D -> Since income is so high, only 300$ per dependent -> 300$. Subtract
- # -> 357017.68 - 300 = 356717.68
- #
- # Step 5 (after adding previous lines) -> Compute marginal taxes.
- # (1000 * (2.00 / 100)) + (5000 * (4.00 / 100)) + ((356717.68 - 1000 - 50000) * (5.00 / 100))
- # -> 17755.884000000002
- # Convert back to pay period
- # wh = round(17755.884000000002, 2) -> 15505.88 / 52.0 -> 341.45923076923077
- wh = -341.46
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AL'),
- al_a4_sit_exemptions=filing_status,
- state_income_tax_additional_withholding=0.0,
- state_income_tax_exempt=False,
- al_a4_sit_dependents=dependents,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alabama tax first payslip weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_941_FIT'], -2999.66) # Hand Calculated.
- self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
-
- def test_taxes_semimonthly_filing_seperate(self):
- salary = 20000.00
- schedule_pay = 'monthly'
- filing_status = 'MS'
- dependents = 2
-
- # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
- # Hand Calculated Amount to Test
- # Step 1 -> 10000.00 for wages per period , 12.0 for monthly -> 20000 * 12 -> 240000.00
- # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
- # 240000.00 - 2000 = 238000.00
- # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -4821.99 * 12 = -57863.88
- # -> 238000.00 - 57863.88 = 180136.12
- # Step 2C -> Subtract the personal exemption -> 1500 for married filing separately
- # -> 180136.12 - 1500 = 178636.12
- # Step 2D -> Since income is so high, only 300$ per dependent -> 600. Subtract
- # -> 178636.12 - 600 = 178036.12
- #
- # Step 5 (after adding previous lines) -> Compute marginal taxes.
- # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((178036.12 - 500 - 2500) * (5.00 / 100)) -> 8861.806
- # Convert back to pay period
- # wh = 8861.806 / 12.0 rounded -> 738.48
- wh = -738.48
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AL'),
- al_a4_sit_exemptions=filing_status,
- state_income_tax_additional_withholding=0.0,
- state_income_tax_exempt=False,
- al_a4_sit_dependents=dependents,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alabama tax first payslip monthly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_941_FIT'], -4822.00) # Hand Calculated.
- self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- def test_tax_exempt(self):
- salary = 5500.00
- wh = 0
- schedule_pay = 'weekly'
- dependents = 2
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AL'),
- al_a4_sit_exemptions='',
- state_income_tax_additional_withholding=0.0,
- state_income_tax_exempt=True,
- al_a4_sit_dependents=dependents,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alabama tax first payslip exempt:')
- 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.AL_UNEMP)
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), wh)
-
- def test_additional_withholding(self):
- salary = 5500.0
- schedule_pay = 'weekly'
- additional_wh = 40.0
- dependents = 2
- # filing status default is single
-
- # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
- # Hand Calculated Amount to Test
- # Step 1 -> 5500.00 for wages per period , 52.0 for monthly -> 5500 * 52.0 -> 286000.0
- # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
- # 286000.0 - 2000 = 284000.0
- # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -1422.4 * 52.0 = -73964.8
- # -> 284000.0 - 73964.8 = 210035.2
- # Step 2C -> Subtract the personal exemption -> 1500 for single
- # -> 210035.2 - 1500 = 208535.2
- # Step 2D -> Since income is so high, only 300$ per dependent -> 600. Subtract
- # -> 208535.2 - 600 = 207935.2
- #
- # Step 5 (after adding previous lines) -> Compute marginal taxes.
- # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((207935.2 - 500 - 2500) * (5.00 / 100)) -> 10356.76
- # Convert back to pay period
- # wh = 10356.76 / 52.0 rounded -> 199.17
- wh = -199.17
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AL'),
- al_a4_sit_exemptions='S',
- state_income_tax_additional_withholding=40.0,
- state_income_tax_exempt=False,
- al_a4_sit_dependents=dependents,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alabama tax first payslip additional withholding:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_941_FIT'], -1422.4) # Hand Calculated.
- self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AL_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh - additional_wh)
-
- def test_personal_exemption(self):
- salary = 5500.0
- schedule_pay = 'weekly'
- # filing status default is single
-
- # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
- # Hand Calculated Amount to Test
- # Step 1 -> 5500.00 for wages per period , 52.0 for monthly -> 5500 * 52.0 -> 286000.0
- # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
- # 286000.0 - 2000 = 284000.0
- # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -1422.4 * 52.0 = -73964.8
- # -> 284000.0 - 73964.8 = 210035.2
- # Step 2C -> Subtract the personal exemption -> 0 for personal exemptioon
- # -> 210035.2 - 0 = 210035.2
- # Step 2D -> Subtract per dependent. No dependents so 0
- # -> 210035.2 - 0 = 210035.2
- #
- # Step 5 (after adding previous lines) -> Compute marginal taxes.
- # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((210035.2 - 500 - 2500) * (5.00 / 100)) -> 10461.76
- # Convert back to pay period
- # wh = 10461.76 / 52.0 rounded -> 201.19
- wh = -199.74
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AL'),
- al_a4_sit_exemptions='S',
- state_income_tax_additional_withholding=0.0,
- state_income_tax_exempt=False,
- al_a4_sit_dependents=0.0,
- schedule_pay=schedule_pay)
-
- self._log('2019 Alabama tax first payslip additional withholding:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_941_FIT'], -1422.4) # Hand Calculated.
- self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AL_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py
new file mode 100644
index 00000000..382f679a
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsALPayslip(TestUsPayslip):
+ # Taxes and Rates
+ AL_UNEMP_MAX_WAGE = 8000.00
+ AL_UNEMP = 2.70
+
+ def _test_sit(self, wage, exempt, exemptions, additional_withholding, dependent, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=exempt,
+ state_income_tax_exempt=exemptions,
+ state_income_tax_additional_withholding=additional_withholding,
+ al_a4_sit_dependents=dependent,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('AL', self.AL_UNEMP, date(2021, 1, 1), wage_base=self.AL_UNEMP_MAX_WAGE)
+ self._test_sit(10000.0, 'S', False, 0.0, 1.0, 'weekly', date(2021, 1, 1), 349.08)
+ self._test_sit(850.0, 'M', False, 0.0, 2.0, 'weekly', date(2021, 1, 1), 29.98)
+ self._test_sit(5000.0, 'H', False, 0.0, 2.0, 'bi-weekly', date(2021, 1, 1), 191.15)
+ self._test_sit(20000.0, 'MS', False, 2.0, 0, 'monthly', date(2021, 1, 1), 757.6)
+ self._test_sit(5500.0, '', True, 2.0, 150, 'weekly', date(2021, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py
deleted file mode 100644
index 73b0f59c..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2019.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsARPayslip(TestUsPayslip):
- # https://www.dfa.arkansas.gov/images/uploads/incomeTaxOffice/whformula.pdf Calculation based on this file.
- AR_UNEMP_MAX_WAGE = 10000.00
- AR_UNEMP = -3.2 / 100.0
- AR_INC_TAX = -0.0535
-
- def test_taxes_monthly(self):
- salary = 2127.0
- schedule_pay = 'monthly'
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AR'),
- state_income_tax_additional_withholding=0.0,
- ar_ar4ec_sit_allowances=2.0,
- state_income_tax_exempt=False,
- schedule_pay='monthly')
-
- self._log('2019 Arkansas tax first payslip weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- # Not exempt from rule 1 or rule 2 - unemployment wages., and actual unemployment.
- self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AR_UNEMP)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
- remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - salary if (self.AR_UNEMP_MAX_WAGE - 2*salary < salary) else salary
- # We reached the cap of 10000.0 in the first payslip.
- self._log('2019 Arkansas 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_AR_UNEMP_wages * self.AR_UNEMP)
-
- def test_additional_withholding(self):
- salary = 5000.0
- schedule_pay = 'monthly'
- pay_periods = 12
- allowances = 2
- # TODO: comment on how it was calculated
- test_ar_amt = 2598.60
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AR'),
- state_income_tax_additional_withholding=100.0,
- ar_ar4ec_sit_allowances=2.0,
- state_income_tax_exempt=False,
- schedule_pay='monthly')
-
-
- self._log('2019 Arkansas 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.AR_UNEMP)
- # TODO: change to hand the test_ar_amt already be divided by pay periods
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], -round(test_ar_amt / pay_periods) - 100)
-
- process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2021.py
new file mode 100644
index 00000000..f7a2928e
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2021.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 TestUsARPayslip(TestUsPayslip):
+ # Taxes and Rates
+ AR_UNEMP_MAX_WAGE = 10000.0
+ AR_UNEMP = 3.2
+
+ def _test_sit(self, wage, exemptions, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('AR'),
+ state_income_tax_exempt=exemptions,
+ state_income_tax_additional_withholding=additional_withholding,
+ ar_ar4ec_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_2021_taxes_example(self):
+ self._test_er_suta('AR', self.AR_UNEMP, date(2021, 1, 1), wage_base=self.AR_UNEMP_MAX_WAGE)
+ self._test_sit(5000.0, True, 0.0, 0, 'monthly', date(2021, 1, 1), 0.0)
+ self._test_sit(5000.0, False, 0.0, 0, 'monthly', date(2021, 1, 1), 220.0)
+ self._test_sit(700.0, False, 0.0, 150, 'weekly', date(2021, 1, 1), 175.0)
+ self._test_sit(7000.0, False, 2.0, 0, 'semi-monthly', date(2021, 1, 1), 395.0)
+ self._test_sit(3000.0, False, 1.0, 0, 'bi-weekly', date(2021, 1, 1), 141.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py
deleted file mode 100644
index b97063b6..00000000
--- a/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2019.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsAZPayslip(TestUsPayslip):
-
- # TAXES AND RATES
- AZ_UNEMP_MAX_WAGE = 7000.00
- AZ_UNEMP = -(2.00 / 100.0)
-
- def test_taxes_with_additional_wh(self):
- salary = 15000.00
- schedule_pay = 'weekly'
- withholding_percentage = 5.1
- percent_wh = (5.10 / 100) # 5.1%
- additional_wh = 12.50
-
- wh_to_test = -((percent_wh * salary) + additional_wh)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AZ'),
- state_income_tax_additional_withholding=12.50,
- az_a4_sit_withholding_percentage=withholding_percentage,
- schedule_pay=schedule_pay)
-
- self._log('2019 Arizona 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'], self.AZ_UNEMP_MAX_WAGE * self.AZ_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
-
- process_payslip(payslip)
-
- remaining_AZ_UNEMP_wages = 0.0 # We already reached max unemployment wages.
-
- self._log('2019 Arizona 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_AZ_UNEMP_wages * self.AZ_UNEMP)
-
- def test_taxes_monthly(self):
- salary = 1000.00
- schedule_pay = 'monthly'
- withholding_percentage = 2.7
- percent_wh = (2.70 / 100) # 2.7%
- additional_wh = 0.0
- wh_to_test = -((percent_wh * salary) + additional_wh)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('AZ'),
- state_income_tax_additional_withholding=0.0,
- az_a4_sit_withholding_percentage=withholding_percentage,
- schedule_pay=schedule_pay)
-
- self._log('2019 Arizona 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.AZ_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
-
- process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2021.py
new file mode 100644
index 00000000..582fd58b
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2021.py
@@ -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 TestUsAZPayslip(TestUsPayslip):
+ # Taxes and Rates
+ AZ_UNEMP_MAX_WAGE = 7000.0
+ AZ_UNEMP = 2.0
+
+ def _test_sit(self, wage, additional_withholding, withholding_percent, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('AZ'),
+ state_income_tax_additional_withholding=additional_withholding,
+ az_a4_sit_withholding_percentage=withholding_percent,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('AZ', self.AZ_UNEMP, date(2021, 1, 1), wage_base=self.AZ_UNEMP_MAX_WAGE)
+ self._test_sit(1000.0, 0.0, 2.70, 'monthly', date(2021, 1, 1), 27.0)
+ self._test_sit(1000.0, 10.0, 2.70, 'monthly', date(2021, 1, 1), 37.0)
+ self._test_sit(15000.0, 0.0, 3.60, 'weekly', date(2021, 1, 1), 540.0)
+ self._test_sit(8000.0, 0.0, 4.20, 'semi-monthly', date(2021, 1, 1), 336.0)
+ self._test_sit(8000.0, 0.0, 0.00, 'semi-monthly', date(2021, 1, 1), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
deleted file mode 100644
index b9331fe3..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
+++ /dev/null
@@ -1,245 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsCAPayslip(TestUsPayslip):
- ###
- # Taxes and Rates
- ###
- CA_MAX_WAGE = 7000
- CA_UIT = -3.5 / 100.0
- CA_ETT = -0.1 / 100.0
- CA_SDI = -1.0 / 100.0
-
- # Examples from https://www.edd.ca.gov/pdf_pub_ctr/20methb.pdf
- def test_example_a(self):
- salary = 210
- schedule_pay = 'weekly'
- allowances = 1
- additional_allowances = 0
-
- wh = 0.00
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('CA'),
- ca_de4_sit_filing_status='single',
- state_income_tax_additional_withholding=0.0,
- ca_de4_sit_allowances=allowances,
- ca_de4_sit_additional_allowances=additional_allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 California tax first payslip:')
- 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.CA_UIT + self.CA_ETT))
- self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- def test_example_b(self):
- salary = 1250
- schedule_pay = 'bi-weekly'
- allowances = 2
- additional_allowances = 1
-
- # Example B
- subject_to_withholding = salary - 38
- taxable_income = subject_to_withholding - 339
- computed_tax = (taxable_income - 632) * 0.022 + 6.95 # 6.95 Marginal Amount
- wh = computed_tax - 9.65 # two exemption allowances
- wh = -wh
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('CA'),
- ca_de4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- ca_de4_sit_allowances=allowances,
- ca_de4_sit_additional_allowances=additional_allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 California tax first payslip:')
- 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.CA_UIT + self.CA_ETT))
- self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
-
- def test_example_c(self):
- salary = 4100
- schedule_pay = 'monthly'
- allowances = 5
- additional_allowances = 0.0
-
- wh = -9.3
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('CA'),
- ca_de4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- ca_de4_sit_allowances=allowances,
- ca_de4_sit_additional_allowances=additional_allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 California tax first payslip:')
- 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.CA_UIT + self.CA_ETT))
- self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 California 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'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
-
- def test_example_d(self):
- salary = 800
- schedule_pay = 'weekly'
- allowances = 3
- additional_allowances = 0
-
- wh = -3.18
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('CA'),
- ca_de4_sit_filing_status='head_household',
- state_income_tax_additional_withholding=0.0,
- ca_de4_sit_allowances=allowances,
- ca_de4_sit_additional_allowances=additional_allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 California tax first payslip:')
- 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.CA_UIT + self.CA_ETT))
- self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 California 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'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
-
- def test_example_e(self):
- salary = 1800
- schedule_pay = 'semi-monthly'
- allowances = 4
- additional_allowances = 0
-
- wh = -3.08
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('CA'),
- ca_de4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- ca_de4_sit_allowances=allowances,
- ca_de4_sit_additional_allowances=additional_allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 California tax first payslip:')
- 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.CA_UIT + self.CA_ETT))
- self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 California 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'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
-
- def test_example_f(self):
- salary = 45000
- schedule_pay = 'annually'
- allowances = 4
- additional_allowances = 0
-
- wh = -113.85
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('CA'),
- ca_de4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- ca_de4_sit_allowances=allowances,
- ca_de4_sit_additional_allowances=additional_allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 California tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['ER_US_SUTA'], self.CA_MAX_WAGE * (self.CA_UIT + self.CA_ETT))
- self.assertPayrollEqual(cats['EE_US_SUTA'], self.CA_MAX_WAGE * self.CA_SDI)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2021.py
new file mode 100755
index 00000000..5a0b8b7f
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2021.py
@@ -0,0 +1,43 @@
+# 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 TestUsCAPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ CA_UNEMP_MAX_WAGE = 7000.0 # Note that this is used for SDI and FLI as well
+ CA_UIT = 3.4
+ CA_ETT = 0.1
+ CA_SDI = 1.2
+
+ def _test_sit(self, wage, filing_status, allowances, additional_allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status=filing_status,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding if filing_status else 0.0)
+
+ def test_2021_taxes_example1(self):
+ combined_er_rate = self.CA_UIT + self.CA_ETT
+ self._test_er_suta('CA', combined_er_rate, date(2021, 1, 1), wage_base=self.CA_UNEMP_MAX_WAGE)
+ self._test_ee_suta('CA', self.CA_SDI, date(2021, 1, 1), wage_base=self.CA_UNEMP_MAX_WAGE, relaxed=True)
+ # these expected values come from examples in https://edd.ca.gov/pdf_pub_ctr/21methb.pdf
+ self._test_sit(210.0, 'single', 1, 0, 0, 'weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(1250.0, 'married', 2, 1, 0, 'bi-weekly', date(2021, 1, 1), 0.82)
+ self._test_sit(4100.0, 'married', 5, 0, 0, 'monthly', date(2021, 1, 1), 0.13)
+ self._test_sit(800.0, 'head_household', 3, 0, 0, 'weekly', date(2021, 1, 1), 2.05)
+ self._test_sit(1800.0, 'married', 4, 0, 0, 'semi-monthly', date(2021, 1, 1), 0.24)
+ self._test_sit(45000.0, 'married', 4, 0, 0, 'annually', date(2021, 1, 1), 45.45)
+ self._test_sit(45000.0, 'married', 4, 0, 20.0, 'annually', date(2021, 1, 1), 65.45)
+ self._test_sit(6000.0, '', 4, 0, 20.0, 'annually', date(2021, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2021.py
new file mode 100755
index 00000000..754e1623
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2021.py
@@ -0,0 +1,37 @@
+# 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 TestUsCOPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ CO_UNEMP_MAX_WAGE = 13600.0
+ CO_UNEMP = 1.7
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding, state_income_tax_exempt=False):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('CO'),
+ fed_941_fit_w4_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=state_income_tax_exempt,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('CO', self.CO_UNEMP, date(2021, 1, 1), wage_base=self.CO_UNEMP_MAX_WAGE)
+ self._test_sit(5000.0, 'married', 0.0, 'semi-monthly', date(2021, 1, 1), 216.07)
+ self._test_sit(800.0, 'single', 0.0, 'weekly', date(2021, 1, 1), 33.48)
+ self._test_sit(20000.0, 'married', 0.0, 'quarterly', date(2021, 1, 1), 833.4)
+ self._test_sit(20000.0, 'married', 10.0, 'quarterly', date(2021, 1, 1), 843.4)
+ self._test_sit(20000.0, 'married', 0.0, 'quarterly', date(2021, 1, 1), 0.0, True)
+ self._test_sit(800.0, '', 0.0, 'weekly', date(2021, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
deleted file mode 100644
index ab423131..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# 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)
diff --git a/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2021.py
new file mode 100644
index 00000000..661131ea
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2021.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 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_2021_taxes_example(self):
+ self._test_er_suta('CT', self.CT_UNEMP, date(2021, 1, 1), wage_base=self.CT_UNEMP_MAX_WAGE)
+ self._test_sit(10000.0, 'a', 0.0, 'weekly', date(2021, 1, 1), 693.23)
+ self._test_sit(12000.0, 'b', 15.0, 'bi-weekly', date(2021, 1, 1), 688.85)
+ self._test_sit(5000.0, 'f', 15.0, 'monthly', date(2021, 1, 1), 230.25)
+ self._test_sit(15000.0, 'c', 0.0, 'monthly', date(2021, 1, 1), 783.33)
+ self._test_sit(18000.0, 'b', 0.0, 'weekly', date(2021, 1, 1), 1254.35)
+ self._test_sit(500.0, 'd', 0.0, 'weekly', date(2021, 1, 1), 21.15)
diff --git a/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py
index ed285368..75c0e755 100755
--- a/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2020.py
@@ -30,7 +30,7 @@ class TestUsDEPayslip(TestUsPayslip):
def test_2020_taxes_example(self):
self._test_er_suta('DE', self.DE_UNEMP, date(2020, 1, 1), wage_base=self.DE_UNEMP_MAX_WAGE)
- self._test_sit(480.77, 'single', 0.0, 1.0, 'weekly', date(2020, 1, 1), 13.88)
+ self._test_sit(480.77, 'single', 0.0, 1.0, 'weekly', date(2020, 1, 1), 13.84)
self._test_sit(5000.0, 'single', 0.0, 2.0, 'monthly', date(2020, 1, 1), 211.93)
self._test_sit(5000.0, 'single', 10.0, 1.0, 'monthly', date(2020, 1, 1), 231.1)
self._test_sit(20000.0, 'married', 0.0, 3.0, 'quarterly', date(2020, 1, 1), 876.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2021.py
new file mode 100755
index 00000000..55ae8983
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_de_delaware_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsDEPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ DE_UNEMP_MAX_WAGE = 16500.0
+ DE_UNEMP = 1.50
+ # Calculation based on section 17. https://revenue.delaware.gov/employers-guide-withholding-regulations-employers-duties/
+
+ def _test_sit(self, wage, filing_status, additional_withholding, dependents, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('DE'),
+ de_w4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ de_w4_sit_dependent=dependents,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('DE', self.DE_UNEMP, date(2021, 1, 1), wage_base=self.DE_UNEMP_MAX_WAGE)
+ self._test_sit(480.77, 'single', 0.0, 1.0, 'weekly', date(2021, 1, 1), 13.84)
+ self._test_sit(5000.0, 'single', 0.0, 2.0, 'monthly', date(2021, 1, 1), 211.93)
+ self._test_sit(5000.0, 'single', 10.0, 1.0, 'monthly', date(2021, 1, 1), 231.1)
+ self._test_sit(20000.0, 'married', 0.0, 3.0, 'quarterly', date(2021, 1, 1), 876.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
deleted file mode 100755
index 419be377..00000000
--- a/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
-
-
-class TestUsFlPayslip(TestUsPayslip):
- ###
- # 2019 Taxes and Rates
- ###
- FL_UNEMP_MAX_WAGE = 7000.0
- FL_UNEMP = -2.7 / 100.0
-
- def test_2019_taxes(self):
- salary = 5000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('FL'))
-
- self._log('2019 Florida tax first payslip:')
- 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.FL_UNEMP)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_fl_unemp_wages = self.FL_UNEMP_MAX_WAGE - salary if (self.FL_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Florida 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_fl_unemp_wages * self.FL_UNEMP)
-
- def test_2019_taxes_with_external(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- external_wages=external_wages,
- state_id=self.get_us_state('FL'))
-
- self._log('2019 Forida_external tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], (self.FL_UNEMP_MAX_WAGE - external_wages) * self.FL_UNEMP)
-
- def test_2019_taxes_with_state_exempt(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- external_wages=external_wages,
- futa_type=USHRContract.FUTA_TYPE_BASIC,
- state_id=self.get_us_state('FL'))
-
- self._log('2019 Forida_external tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats.get('ER_US_SUTA', 0.0), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2021.py
new file mode 100755
index 00000000..f146bf4e
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2021.py
@@ -0,0 +1,16 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsFlPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ FL_UNEMP_MAX_WAGE = 7000.0
+ FL_UNEMP = 2.9
+
+ def test_2021_taxes(self):
+ # Only has state unemployment
+ self._test_er_suta('FL', self.FL_UNEMP, date(2021, 1, 1), wage_base=self.FL_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
deleted file mode 100755
index 98206965..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsGAPayslip(TestUsPayslip):
-
- # TAXES AND RATES
- GA_UNEMP_MAX_WAGE = 9500.00
- GA_UNEMP = -(2.70 / 100.0)
-
- def test_taxes_weekly_single_with_additional_wh(self):
- salary = 15000.00
- schedule_pay = 'weekly'
- allowances = 1
- filing_status = 'single'
- additional_wh = 12.50
- # Hand Calculated Amount to Test
- # Step 1 - Subtract standard deduction from wages. Std Deduct for single weekly is 88.50
- # step1 = 15000.00 - 88.50 = 14911.5
- # Step 2 - Subtract personal allowance from step1. Allowance for single weekly is 51.92
- # step2 = step1 - 51.92 = 14859.58
- # Step 3 - Subtract amount for dependents. Weekly dependent allowance is 57.50
- # step3 = 14859.58 - 57.50 = 14802.08
- # Step 4 -Determine wh amount from tables
- # step4 = 4.42 + ((5.75 / 100.00) * (14802.08 - 135.00))
- # Add additional_wh
- # wh = 847.7771 + 12.50 = 860.2771
- wh = -860.28
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('GA'),
- ga_g4_sit_dependent_allowances=allowances,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional_wh,
- schedule_pay=schedule_pay)
-
- self.assertEqual(contract.schedule_pay, 'weekly')
-
- self._log('2019 Georgia 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'], self.GA_UNEMP_MAX_WAGE * self.GA_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- remaining_GA_UNEMP_wages = 0.0 # We already reached max unemployment wages.
-
- self._log('2019 Georgia 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_GA_UNEMP_wages * self.GA_UNEMP)
-
-
- def test_taxes_monthly_head_of_household(self):
- salary = 25000.00
- schedule_pay = 'monthly'
- allowances = 2
- filing_status = 'head of household'
- additional_wh = 15.00
- # Hand Calculated Amount to Test
- # Step 1 - Subtract standard deduction from wages. Std Deduct for head of household monthly is 383.50
- # step1 = 25000.00 - 383.50 = 24616.5
- # Step 2 - Subtract personal allowance from step1. Allowance for head of household monthly is 225.00
- # step2 = 24616.5 - 225.00 = 24391.5
- # Step 3 - Subtract amount for dependents. Weekly dependent allowance is 250.00
- # step3 = 24391.5 - (2 * 250.00) = 23891.5
- # Step 4 - Determine wh amount from tables
- # step4 = 28.33 + ((5.75 / 100.00) * (23891.5 - 833.00)) = 1354.19375
- # Add additional_wh
- # wh = 1354.19375 + 15.00 = 1369.19375
- wh = -1369.19
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('GA'),
- ga_g4_sit_dependent_allowances=allowances,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional_wh,
- schedule_pay=schedule_pay)
-
- self.assertEqual(contract.schedule_pay, 'monthly')
-
- self._log('2019 Georgia 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.GA_UNEMP_MAX_WAGE * self.GA_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- remaining_GA_UNEMP_wages = 0.0 # We already reached max unemployment wages.
-
- self._log('2019 Georgia 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_GA_UNEMP_wages * self.GA_UNEMP)
-
- def test_taxes_exempt(self):
- salary = 25000.00
- schedule_pay = 'monthly'
- allowances = 2
- filing_status = ''
- additional_wh = 15.00
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('GA'),
- ga_g4_sit_dependent_allowances=allowances,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional_wh,
- schedule_pay=schedule_pay)
-
- self._log('2019 Georgia tax first payslip exempt:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0), 0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2021.py
new file mode 100755
index 00000000..56cb9abc
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2021.py
@@ -0,0 +1,39 @@
+# 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 TestUsGAPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ GA_UNEMP_MAX_WAGE = 9500.00
+ GA_UNEMP = 2.70
+
+ # Example calculated based on https://dor.georgia.gov/employers-tax-guide 2021_employer tax gauide
+
+ def _test_sit(self, wage, filing_status, additional_withholding, dependent_allowances, additional_allowances,
+ schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('GA'),
+ ga_g4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ga_g4_sit_dependent_allowances=dependent_allowances,
+ ga_g4_sit_additional_allowances=additional_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):
+ self._test_er_suta('GA', self.GA_UNEMP, date(2021, 1, 1), wage_base=self.GA_UNEMP_MAX_WAGE)
+ self._test_sit(15000.0, 'single', 12.50, 1, 0, 'weekly', date(2021, 1, 1), 860.28)
+ self._test_sit(25000.0, 'head of household', 15.00, 2, 0, 'monthly', date(2021, 1, 1), 1369.19)
+ self._test_sit(425.0, 'married filing separate', 0.0, 1, 0, 'weekly', date(2021, 1, 1), 11.45)
+ self._test_sit(3000.0, 'single', 0.00, 1, 1, 'quarterly', date(2021, 1, 1), 0.0)
+ self._test_sit(2500.0, '', 0.00, 1, 1, 'quarterly', date(2021, 1, 1), 0.0)
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
deleted file mode 100644
index 13f1f2b5..00000000
--- a/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2019.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# 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_2021.py b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2021.py
new file mode 100755
index 00000000..0463b227
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2021.py
@@ -0,0 +1,37 @@
+# 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):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ HI_UNEMP_MAX_WAGE = 47400.00
+ HI_UNEMP = 5.2
+
+ 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_2021_taxes_example(self):
+ self._test_er_suta('HI', self.HI_UNEMP, date(2021, 1, 1), wage_base=self.HI_UNEMP_MAX_WAGE)
+ self._test_sit(375.0, 'single', 0.0, 3.0, 'weekly', date(2021, 1, 1), 15.3)
+ self._test_sit(5000.0, 'married', 0.0, 2.0, 'monthly', date(2021, 1, 1), 287.1)
+ self._test_sit(5000.0, 'married', 10.0, 2.0, 'monthly', date(2021, 1, 1), 297.1)
+ self._test_sit(50000.0, 'head_of_household', 0.0, 3.0, 'weekly', date(2021, 1, 1), 3933.65)
+ self._test_sit(750.0, 'single', 10.0, 3.0, 'bi-weekly', date(2021, 1, 1), 40.59)
+ self._test_sit(3000.0, '', 0.0, 3.0, 'weekly', date(2021, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py
deleted file mode 100644
index cb3bccfd..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2019.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsIAPayslip(TestUsPayslip):
- IA_UNEMP_MAX_WAGE = 30600
- IA_UNEMP = -1.0 / 100.0
- IA_INC_TAX = -0.0535
-
- def test_taxes_weekly(self):
- wages = 30000.00
- schedule_pay = 'weekly'
- allowances = 1
- additional_wh = 0.00
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=wages,
- state_id=self.get_us_state('IA'),
- state_income_tax_additional_withholding=additional_wh,
- ia_w4_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 Iowa tax first payslip weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
- # withholding amount because it is calculated in the base US payroll module as a negative
- # t1 = 30000 - (10399.66) = 19600.34
- t1_to_test = wages + cats['EE_US_941_FIT']
- self.assertPayrollAlmostEqual(t1_to_test, 19600.34)
-
- # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
- # In our case, we have a weekly period which on the table has a std deduct. of $32.50 for 0 or 1 allowances,
- # and 80.00 of 2 or more allowances.
- standard_deduction = 32.50 # The allowance tells us what standard_deduction amount to use.
- # t2 = 19600.34 - 32.50 = 19567.84
- t2_to_test = t1_to_test - standard_deduction
- self.assertPayrollAlmostEqual(t2_to_test, 19567.84)
- # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
- # 1153.38 is the bracket floor. 8.53 is the rate, and 67.63 is the flat fee.
- # t3 = 1638.38
- t3_to_test = ((t2_to_test - 1153.38) * (8.53 / 100)) + 67.63
- self.assertPayrollAlmostEqual(t3_to_test, 1638.38)
- # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
- # deduction amount per allowance is 0.77
- # t4 = 1638.38 - 0.77 = 155.03
- t4_to_test = t3_to_test - (0.77 * allowances)
- self.assertPayrollAlmostEqual(t4_to_test, 1637.61)
- # t5 is our T4 plus the additional withholding per period
- # t5 = 1637.61 + 0.0
- # Convert to negative as well.
- t5_to_test = -t4_to_test - additional_wh
- self.assertPayrollAlmostEqual(t5_to_test, -1637.61)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], wages * self.IA_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], t5_to_test)
-
-
- # Make a new payslip, this one will have maximums
-
- remaining_IA_UNEMP_wages = self.IA_UNEMP_MAX_WAGE - wages if (self.IA_UNEMP_MAX_WAGE - 2*wages < wages) \
- else wages
-
- self._log('2019 Iowa 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'], wages * self.IA_UNEMP)
-
- def test_taxes_biweekly(self):
- wages = 3000.00
- schedule_pay = 'bi-weekly'
- allowances = 1
- additional_wh = 0.00
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=wages,
- state_id=self.get_us_state('IA'),
- state_income_tax_additional_withholding=additional_wh,
- ia_w4_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 Iowa tax first payslip bi-weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
- # withholding amount because it is calculated in the base US payroll module as a negative
- t1_to_test = wages + cats['EE_US_941_FIT']
- # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
- # In our case, we have a biweekly period which on the table has a std deduct. of $65.00 for 0 or 1 allowances,
- # and $160.00 of 2 or more allowances.
- standard_deduction = 65.00 # The allowance tells us what standard_deduction amount to use.
- t2_to_test = t1_to_test - standard_deduction
- # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
- t3_to_test = ((t2_to_test - 2306.77) * (8.53 / 100)) + 135.28
- # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
- # deduction amount per allowance is 0.77
- t4_to_test = t3_to_test - (1.54 * allowances)
- # t5 is our T4 plus the additional withholding per period
- t5_to_test = -t4_to_test - additional_wh
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], wages * self.IA_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], t5_to_test - additional_wh)
-
- process_payslip(payslip)
-
- def test_taxes_with_external_weekly(self):
- wages = 2500.00
- schedule_pay = 'weekly'
- allowances = 1
- additional_wh = 0.00
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=wages,
- state_id=self.get_us_state('IA'),
- state_income_tax_additional_withholding=additional_wh,
- ia_w4_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 Iowa external tax first payslip external weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
-
- # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
- # withholding amount because it is calculated in the base US payroll module as a negative
- t1_to_test = wages + cats['EE_US_941_FIT']
- # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
- # In our case, we have a weekly period which on the table has a std deduct. of $32.50 for 0 or 1 allowances,
- # and 80.00 of 2 or more allowances.
- standard_deduction = 32.50 # The allowance tells us what standard_deduction amount to use.
- t2_to_test = t1_to_test - standard_deduction
- # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
- t3_to_test = ((t2_to_test - 1153.38) * (8.53 / 100)) + 67.63
- # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
- # deduction amount per allowance is 0.77
- t4_to_test = t3_to_test - (0.77 * allowances)
- # t5 is our T4 plus the additional withholding per period
- t5_to_test = -t4_to_test - additional_wh
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], wages * self.IA_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], t5_to_test)
-
- process_payslip(payslip)
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py
new file mode 100755
index 00000000..df169186
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsIAPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ IA_UNEMP_MAX_WAGE = 32400.0
+ IA_UNEMP = 1.0
+
+ def _test_sit(self, wage, exempt, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('IA'),
+ state_income_tax_exempt=exempt,
+ state_income_tax_additional_withholding=additional_withholding,
+ ia_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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('IA', self.IA_UNEMP, date(2021, 1, 1), wage_base=self.IA_UNEMP_MAX_WAGE)
+ self._test_sit(2100.0, False, 0.0, 3.0, 'bi-weekly', date(2021, 1, 1), 79.13)
+ self._test_sit(3000.0, True, 10.0, 1.0, 'bi-weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(300.0, False, 0.0, 1.0, 'weekly', date(2021, 1, 1), 6.14)
+ self._test_sit(5000.0, False, 0.0, 1.0, 'monthly', date(2021, 1, 1), 217.60)
+ self._test_sit(7500.0, False, 10.0, 2.0, 'semi-monthly', date(2021, 1, 1), 420.87)
diff --git a/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py
deleted file mode 100644
index 8e3576d6..00000000
--- a/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2019.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsIDPayslip(TestUsPayslip):
-
- # TAXES AND RATES
- ID_UNEMP_MAX_WAGE = 40000.00
- ID_UNEMP = -(1.00 / 100.0)
-
- def test_taxes_single_biweekly(self):
- salary = 1212.00
- schedule_pay = 'bi-weekly'
- filing_status = 'single'
- allowances = 4
- # SEE https://tax.idaho.gov/i-1026.cfm?seg=compute for example calculations
- wh_to_check = -10.00
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('ID'),
- id_w4_sit_filing_status=filing_status,
- id_w4_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 Idaho 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.ID_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
-
- process_payslip(payslip)
-
- remaining_id_unemp_wages = self.ID_UNEMP_MAX_WAGE - salary if (self.ID_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Idaho 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.ID_UNEMP)
-
- def test_taxes_married_monthly(self):
- salary = 5000.00
- schedule_pay = 'monthly'
- filing_status = 'married'
- allowances = 2
-
- # ICTCAT says monthly allowances are 246.67
- # we have 2 so 246.67 * 2 = 493.34
- # 5000.00 - 493.34 = 4506.66
- # Wh is 89$ plus 6.925% over 3959,00
- # 126.92545499999999 - > 127.0
- wh_to_check = -127.0
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('ID'),
- id_w4_sit_filing_status=filing_status,
- id_w4_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 Idaho 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.ID_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
-
- process_payslip(payslip)
-
- remaining_id_unemp_wages = self.ID_UNEMP_MAX_WAGE - salary if (self.ID_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Idaho 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.ID_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2021.py
new file mode 100755
index 00000000..c6149719
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2021.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 TestUsIDPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ ID_UNEMP_MAX_WAGE = 43000.00
+ ID_UNEMP = 1.0
+
+ def _test_sit(self, wage, filing_status, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('ID'),
+ id_w4_sit_filing_status=filing_status,
+ id_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)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('ID', self.ID_UNEMP, date(2021, 1, 1), wage_base=self.ID_UNEMP_MAX_WAGE)
+ self._test_sit(1212.0, 'single', 4.0, 'bi-weekly', date(2021, 1, 1), 10.0)
+ self._test_sit(10000.0, 'married', 1.0, 'annually', date(2021, 1, 1), 0.0)
+ self._test_sit(52000.0, 'married', 4.0, 'monthly', date(2021, 1, 1), 3345.0)
+ self._test_sit(5000.0, 'head of household', 0.0, 'semi-monthly', date(2021, 1, 1), 300.0)
+ self._test_sit(5900.0, 'single', 5.0, 'weekly', date(2021, 1, 1), 367.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py
deleted file mode 100644
index ba633607..00000000
--- a/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2019.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsILPayslip(TestUsPayslip):
- # TAXES AND RATES
- IL_UNEMP_MAX_WAGE = 12960.00
- IL_UNEMP = -(3.175 / 100.0)
-
- def test_taxes_monthly(self):
- salary = 15000.00
- schedule_pay = 'monthly'
- basic_allowances = 1
- additional_allowances = 1
- flat_rate = (4.95 / 100)
- wh_to_test = -(flat_rate * (salary - ((basic_allowances * 2275 + additional_allowances * 1000) / 12.0)))
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('IL'),
- state_income_tax_additional_withholding=0.0,
- il_w4_sit_basic_allowances=1.0,
- il_w4_sit_additional_allowances=1.0,
- schedule_pay='monthly')
-
- self._log('2019 Illinois 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.IL_UNEMP_MAX_WAGE * self.IL_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
-
- process_payslip(payslip)
-
- remaining_IL_UNEMP_wages = 0.0 # We already reached max unemployment wages.
-
- self._log('2019 Illinois 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_IL_UNEMP_wages * self.IL_UNEMP)
-
- def test_taxes_with_additional_wh(self):
- salary = 15000.00
- schedule_pay = 'monthly'
- basic_allowances = 1
- additional_allowances = 1
- additional_wh = 15.0
- flat_rate = (4.95 / 100)
- wh_to_test = -(flat_rate * (salary - ((basic_allowances * 2275 + additional_allowances * 1000) / 12.0)) + additional_wh)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('IL'),
- state_income_tax_additional_withholding=15.0,
- il_w4_sit_basic_allowances=1.0,
- il_w4_sit_additional_allowances=1.0,
- schedule_pay='monthly')
-
- self._log('2019 Illinois 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.IL_UNEMP_MAX_WAGE * self.IL_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_test)
diff --git a/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2021.py
new file mode 100644
index 00000000..4f62cb5e
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_il_illinois_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsILPayslip(TestUsPayslip):
+ # Taxes and Rates
+ IL_UNEMP_MAX_WAGE = 12960.0
+ IL_UNEMP = 3.175
+
+ def _test_sit(self, wage, additional_withholding, basic_allowances, additional_allowances, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('IL'),
+ state_income_tax_additional_withholding=additional_withholding,
+ il_w4_sit_basic_allowances=basic_allowances,
+ il_w4_sit_additional_allowances=additional_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_2021_taxes_example(self):
+ self._test_er_suta('IL', self.IL_UNEMP, date(2021, 1, 1), wage_base=self.IL_UNEMP_MAX_WAGE, relaxed=True)
+ self._test_sit(800.0, 0.0, 2, 2, 'weekly', date(2021, 1, 1), 33.17)
+ self._test_sit(800.0, 10.0, 2, 2, 'weekly', date(2021, 1, 1), 43.17)
+ self._test_sit(2500.0, 0.0, 1, 1, 'monthly', date(2021, 1, 1), 109.83)
+ self._test_sit(2500.0, 0.0, 0, 0, 'monthly', date(2021, 1, 1), 123.75)
+ self._test_sit(3000.0, 15.0, 0, 0, 'quarterly', date(2021, 1, 1), 163.50)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2021.py
new file mode 100755
index 00000000..53b7ddf3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsINPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ IN_UNEMP_MAX_WAGE = 9500.0
+ IN_UNEMP = 2.5
+ # Calculation based on https://www.in.gov/dor/files/dn01.pdf
+
+ def _test_sit(self, wage, additional_withholding, personal_exemption, dependent_exemption, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('IN'),
+ state_income_tax_additional_withholding=additional_withholding,
+ in_w4_sit_personal_exemption=personal_exemption,
+ in_w4_sit_dependent_exemption=dependent_exemption,
+ 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('IN', self.IN_UNEMP, date(2020, 1, 1), wage_base=self.IN_UNEMP_MAX_WAGE)
+ self._test_sit(800.0, 0.0, 5.0, 3.0, 'weekly', date(2020, 1, 1), 19.94)
+ self._test_sit(800.0, 10.0, 5.0, 3.0, 'weekly', date(2020, 1, 1), 29.94)
+ self._test_sit(9000.0, 0.0, 4.0, 3.0, 'monthly', date(2020, 1, 1), 267.82)
+ self._test_sit(10000.0, 0.0, 2.0, 2.0, 'bi-weekly', date(2020, 1, 1), 316.79)
diff --git a/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2021.py
new file mode 100755
index 00000000..fba4c2f5
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsKSPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ KS_UNEMP_MAX_WAGE = 14000.0
+ KS_UNEMP = 2.7
+ # Calculation based on example https://revenue.ky.gov/Forms/42A003(T)%20(12-2019)%202120%20Tax%20Tables.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('KS'),
+ ks_k4_sit_filing_status=filing_status,
+ ks_k4_sit_allowances=allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('KS', self.KS_UNEMP, date(2021, 1, 1), wage_base=self.KS_UNEMP_MAX_WAGE)
+ self._test_sit(6250, 'married', 2, 0, 'semi-monthly', date(2021, 1, 1), 290.00)
+ self._test_sit(5000, 'single', 1, 0, 'monthly', date(2021, 1, 1), 222.00)
+ self._test_sit(1500, 'married', 0, 0, 'bi-weekly', date(2021, 1, 1), 39.00)
+ self._test_sit(750, 'single', 2, 10, 'weekly', date(2021, 1, 1), 36.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2021.py
new file mode 100755
index 00000000..d05add23
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsKYPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ KY_UNEMP_MAX_WAGE = 11100.0
+ KY_UNEMP = 2.7
+ # Calculation based on example https://revenue.ky.gov/Forms/42A003(T)%20(12-2019)%202120%20Tax%20Tables.pdf
+
+ def _test_sit(self, wage, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('KY'),
+ 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_2021_taxes_example(self):
+ self._test_er_suta('KY', self.KY_UNEMP, date(2021, 1, 1), wage_base=self.KY_UNEMP_MAX_WAGE)
+ # page 8 of https://revenue.ky.gov/Software-Developer/Software%20Development%20Documents/2021%20Withholding%20Tax%20Tables%20-%20Computer%20Formual%2042A003(T)(12-20)(10-15-20%20DRAFT).pdf
+ self._test_sit(3020, 0.0, 'monthly', date(2021, 1, 1), 139.79)
+ self._test_sit(1500, 0.0, 'bi-weekly', date(2021, 1, 1), 69.83)
+ self._test_sit(1500, 10.0, 'bi-weekly', date(2021, 1, 1), 79.83)
+ self._test_sit(750, 00.0, 'weekly', date(2021, 1, 1), 34.91)
+ self._test_sit(7000, 0.0, 'semi-monthly', date(2021, 1, 1), 344.39)
diff --git a/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
deleted file mode 100644
index 1d01c618..00000000
--- a/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsLAPayslip(TestUsPayslip):
-
- # TAXES AND RATES
- LA_UNEMP_MAX_WAGE = 7700.00
- LA_UNEMP = -(1.14 / 100.0)
-
- def test_taxes_single_weekly(self):
- salary = 700.00
- schedule_pay = 'weekly'
- filing_status = 'single'
- exemptions = 1
- dependents = 2
- additional_withholding = 0
- # SEE http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf for example calculations
- # wh_to test is 19.42
- # Our algorithm correctly rounds whereas theirs does it prematurely.
- wh_to_check = -19.43
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('LA'),
- la_l4_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional_withholding,
- la_l4_sit_exemptions=exemptions,
- la_l4_sit_dependents=dependents,
- schedule_pay=schedule_pay)
-
- self._log('2019 Louisiana 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.LA_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
-
- process_payslip(payslip)
-
- remaining_la_unemp_wages = self.LA_UNEMP_MAX_WAGE - salary if (self.LA_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Louisiana 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_la_unemp_wages * self.LA_UNEMP)
-
- def test_taxes_married_biweekly(self):
- salary = 4600.00
- schedule_pay = 'bi-weekly'
- filing_status = 'married'
- exemptions = 2
- dependents = 3
- additional_withholding = 0
- # SEE http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf for example calculations
- # wh_to test is 157.12
- wh_to_check = -157.12
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('LA'),
- la_l4_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional_withholding,
- la_l4_sit_exemptions=exemptions,
- la_l4_sit_dependents=dependents,
- schedule_pay=schedule_pay)
-
- self._log('2019 Louisiana tax first payslip bi-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.LA_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
-
- process_payslip(payslip)
-
- remaining_la_unemp_wages = self.LA_UNEMP_MAX_WAGE - salary if (self.LA_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Louisiana tax second payslip bi-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_la_unemp_wages * self.LA_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2021.py
new file mode 100755
index 00000000..d23c3ab3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsLAPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ LA_UNEMP_MAX_WAGE = 7700.0
+ LA_UNEMP = 1.14
+ # Calculation based on http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf
+
+ def _test_sit(self, wage, filing_status, additional_withholding, exemptions, dependents, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('LA'),
+ la_l4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ la_l4_sit_exemptions=exemptions,
+ la_l4_sit_dependents=dependents,
+ 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_2020_taxes_example(self):
+ self._test_er_suta('LA', self.LA_UNEMP, date(2020, 1, 1), wage_base=self.LA_UNEMP_MAX_WAGE)
+ self._test_sit(700.0, 'single', 0.0, 1.0, 2.0, 'weekly', date(2020, 1, 1), 19.43)
+ self._test_sit(4600.0, 'married', 0.0, 2.0, 3.0, 'bi-weekly', date(2020, 1, 1), 157.12)
+ self._test_sit(6000.0, 'single', 10.0, 2.0, 3.0, 'monthly', date(2020, 1, 1), 219.08)
diff --git a/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py
deleted file mode 100755
index b12baed2..00000000
--- a/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2019.py
+++ /dev/null
@@ -1,194 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsMIPayslip(TestUsPayslip):
- # Taxes and Rates
- MI_UNEMP_MAX_WAGE = 9500.0
- MI_UNEMP = - 2.7 / 100.0
- MI_INC_TAX = - 4.25 / 100.0
- ANNUAL_EXEMPTION_AMOUNT = 4400.00
- PAY_PERIOD_DIVISOR = {
- 'weekly': 52.0,
- 'bi-weekly': 26.0,
- 'semi-monthly': 24.0,
- 'monthly': 12.0
- }
-
- def test_2019_taxes_weekly(self):
- salary = 5000.0
- schedule_pay = 'weekly'
- exemptions = 1
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MI'),
- state_income_tax_additional_withholding=0.0,
- mi_w4_sit_exemptions=1.0,
- schedule_pay='weekly')
-
- allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
- wh = -((salary - (allowance_amount * exemptions)) * -self.MI_INC_TAX)
-
- self._log('2019 Michigan 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.MI_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
- #
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
- remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (self.MI_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Michigan 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_MI_UNEMP_wages * self.MI_UNEMP)
-
- def test_2019_taxes_biweekly(self):
- salary = 5000.0
- schedule_pay = 'bi-weekly'
- allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
- exemption = 2
-
- wh = -((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MI'),
- state_income_tax_additional_withholding=0.0,
- mi_w4_sit_exemptions=2.0,
- schedule_pay='bi-weekly')
-
- self._log('2019 Michigan tax first payslip bi-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.MI_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
- remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (self.MI_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Michigan tax second payslip bi-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_MI_UNEMP_wages * self.MI_UNEMP)
-
- def test_2019_taxes_semimonthly(self):
- salary = 5000.0
- schedule_pay = 'semi-monthly'
- allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
- exemption = 1
-
- wh = -((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MI'),
- state_income_tax_additional_withholding=0.0,
- mi_w4_sit_exemptions=1.0,
- schedule_pay='semi-monthly')
-
- self._log('2019 Michigan tax first payslip semi-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.MI_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
- remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (self.MI_UNEMP_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 Michigan tax second payslip semi-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_MI_UNEMP_wages * self.MI_UNEMP)
-
- def test_2019_taxes_monthly(self):
- salary = 5000.0
- schedule_pay = 'monthly'
- allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
- exemption = 1
-
- wh = -((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MI'),
- state_income_tax_additional_withholding=0.0,
- mi_w4_sit_exemptions=1.0,
- schedule_pay='monthly')
-
- self._log('2019 Michigan 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.MI_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
- remaining_MI_UNEMP_wages = self.MI_UNEMP_MAX_WAGE - salary if (
- self.MI_UNEMP_MAX_WAGE - (2 * salary) < salary) \
- else salary
-
- self._log('2019 Michigan 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_MI_UNEMP_wages * self.MI_UNEMP)
-
- def test_additional_withholding(self):
- salary = 5000.0
- schedule_pay = 'weekly'
- allowance_amount = 0.0
- allowance_amount = self.ANNUAL_EXEMPTION_AMOUNT / self.PAY_PERIOD_DIVISOR[schedule_pay]
- additional_wh = 40.0
- exemption = 1
-
- wh = -(((salary - (allowance_amount * exemption)) * -self.MI_INC_TAX) + additional_wh)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MI'),
- state_income_tax_additional_withholding=40.0,
- mi_w4_sit_exemptions=1.0,
- schedule_pay='weekly')
-
- self._log('2019 Michigan tax first payslip with additional withholding:')
- 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.MI_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2021.py
new file mode 100755
index 00000000..690749f8
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2021.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 TestUsMIPayslip(TestUsPayslip):
+ # Taxes and Rates
+ MI_UNEMP_MAX_WAGE = 9500.0
+ MI_UNEMP = 2.7
+
+ def _test_sit(self, wage, exemptions, additional_withholding, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MI'),
+ mi_w4_sit_exemptions=exemptions,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('MI', self.MI_UNEMP, date(2021, 1, 1), wage_base=self.MI_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 1, 100.0, 'weekly', date(2021, 1, 1), 127.87)
+ self._test_sit(1750.0, 1, 0.0, 'bi-weekly', date(2021, 1, 1), 66.37)
+ self._test_sit(5000.0, 1, 5.0, 'semi-monthly', date(2021, 1, 1), 208.83)
+ self._test_sit(8000.0, 1, 5.0, 'monthly', date(2021, 1, 1), 327.65)
+ self._test_sit(5000.0, 2, 0.0, 'monthly', date(2021, 1, 1), 177.79)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py
deleted file mode 100755
index 2a64b57d..00000000
--- a/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2019.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsMNPayslip(TestUsPayslip):
- # TAXES AND RATES
- MN_UNEMP_MAX_WAGE = 34000.0
- MN_UNEMP = -1.11 / 100.0
-
- def test_taxes_weekly(self):
- salary = 30000.0
- # Hand Calculated Amount to Test
- # Step 1 -> 30000.00 for wages per period Step 2 -> 52.0 for weekly -> 30000 * 52 -> 1560000
- # Step 3 -> allowances * 4250.0 -> 4250.00 in this case.
- # Step 4 -> Step 2 - Step 3 -> 1560000 - 4250.00 -> 1555750
- # Step 5 -> using chart -> we have last row -> ((1555750 - 166290) * (9.85 / 100)) + 11717.65 -> 148579.46
- # Step 6 -> Convert back to pay period amount and round - > 2857.297 - > 2857.0
- # wh = 2857.0
- wh = -2857.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MN'),
- mn_w4mn_sit_filing_status='single',
- state_income_tax_additional_withholding=0.0,
- mn_w4mn_sit_allowances=1.0,
- schedule_pay='weekly')
-
- self._log('2019 Minnesota 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.MN_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh) # Test numbers are off by 1 penny
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
- remaining_MN_UNEMP_wages = self.MN_UNEMP_MAX_WAGE - salary if (self.MN_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Minnesota 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_MN_UNEMP_wages * self.MN_UNEMP)
-
- def test_taxes_married(self):
- salary = 5000.00
-
- # Hand Calculated Amount to Test
- # Step 1 -> 5000.0 for wages per period Step 2 -> 52.0 for weekly -> 5000 * 52 -> 260,000
- # Step 3 -> allowances * 4250.0 -> 4250.00 in this case.
- # Step 4 -> Step 2 - Step 3 -> 260,000 - 4250.00 -> 255750.0
- # For step five we used the married section
- # Step 5 -> using chart -> we have 2nd last row -> ((255750 - 163070) * (7.85 / 100)) + 10199.33 ->
- # Step 6 -> Convert back to pay period amount and round
- # wh = 336.0
- wh = -336.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MN'),
- mn_w4mn_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- mn_w4mn_sit_allowances=1.0,
- schedule_pay='weekly')
-
- self._log('2019 Minnesota tax first payslip married:')
- 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.MN_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
-
- def test_taxes_semimonthly(self):
- salary = 6500.00
- # Hand Calculated Amount to Test
- # Step 1 -> 6500.00 for wages per period Step 2 -> 24 for semi-monthly -> 6500.00 * 24 -> 156000.00
- # Step 3 -> allowances * 4250.0 -> 4250.00 in this case.
- # Step 4 -> Step 2 - Step 3 -> 156000.00 - 4250.00 -> 151750.0
- # Step 5 -> using chart -> we have 2nd last row -> ((151750.0- 89510) * (7.85 / 100)) + 5690.42 -> 10576.26
- # Step 6 -> Convert back to pay period amount and round
- # wh = -441
- wh = -441.00
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MN'),
- mn_w4mn_sit_filing_status='single',
- state_income_tax_additional_withholding=0.0,
- mn_w4mn_sit_allowances=1.0,
- schedule_pay='semi-monthly')
-
-
- self._log('2019 Minnesota tax first payslip semimonthly:')
- 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.MN_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
-
- def test_tax_exempt(self):
- salary = 5500.00
- wh = 0
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MN'),
- mn_w4mn_sit_filing_status='',
- state_income_tax_additional_withholding=0.0,
- mn_w4mn_sit_allowances=2.0,
- schedule_pay='weekly')
-
- self._log('2019 Minnesota tax first payslip exempt:')
- 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.MN_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
-
- def test_additional_withholding(self):
- salary = 5500.0
- # Hand Calculated Amount to Test
- # Step 1 -> 5500 for wages per period Step 2 -> 52 for weekly -> 5500 * 52 -> 286000.00
- # Step 3 -> allowances * 4250.0 -> 8500 in this case.
- # Step 4 -> Step 2 - Step 3 -> 286000.00 - 8500 -> 277500
- # Step 5 -> using chart -> we have last row -> ((277500- 166290) * (9.85 / 100)) + 11717.65 -> 22671.835
- # Step 6 -> Convert back to pay period amount and round
- # wh = -436.0
- # Add additional_withholding
- # wh = -436.0 + 40.0
- wh = -476.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MN'),
- mn_w4mn_sit_filing_status='single',
- state_income_tax_additional_withholding=40.0,
- mn_w4mn_sit_allowances=2.0,
- schedule_pay='weekly')
-
- self._log('2019 Minnesota tax first payslip additional withholding:')
- 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.MN_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2021.py
new file mode 100755
index 00000000..dbae6ddd
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mn_minnesota_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsMNPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ MN_UNEMP_MAX_WAGE = 35000.0
+ MN_UNEMP = 1.11
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MN'),
+ mn_w4mn_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ mn_w4mn_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_2021_taxes_example(self):
+ self._test_er_suta('MN', self.MN_UNEMP, date(2021, 1, 1), wage_base=self.MN_UNEMP_MAX_WAGE)
+ self._test_sit(5000.0, 'single', 1.0, 0.0, 'weekly', date(2021, 1, 1), 388.0)
+ self._test_sit(30000.0, 'single', 1.0, 0.0, 'weekly', date(2021, 1, 1), 2850.0)
+ self._test_sit(5000.0, 'married', 1.0, 0.0, 'weekly', date(2021, 1, 1), 325.0)
+ self._test_sit(6500.0, 'single', 1.0, 0.0, 'semi-monthly', date(2021, 1, 1), 428.0)
+ self._test_sit(5500.0, '', 2.0, 0.0, 'weekly', date(2021, 1, 1), 0.0)
+ self._test_sit(5500.0, 'single', 2.0, 40.0, 'weekly', date(2021, 1, 1), 469.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py
deleted file mode 100755
index 27a0ad93..00000000
--- a/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2019.py
+++ /dev/null
@@ -1,188 +0,0 @@
-
-from datetime import date
-from .common import TestUsPayslip
-
-
-class TestUsMoPayslip(TestUsPayslip):
- # Calculations from http://dor.mo.gov/forms/4282_2019.pdf
- SALARY = 12000.0
- MO_UNEMP = -2.376 / 100.0
-
- TAX = [
- (1053.0, 1.5),
- (1053.0, 2.0),
- (1053.0, 2.5),
- (1053.0, 3.0),
- (1053.0, 3.5),
- (1053.0, 4.0),
- (1053.0, 4.5),
- (1053.0, 5.0),
- (999999999.0, 5.4),
- ]
-
- def test_2019_taxes_single(self):
- # Payroll Period Monthly
- salary = self.SALARY
- pp = 12.0
- gross_salary = salary * pp
- spouse_employed = False
-
- # Single
- standard_deduction = 12400.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MO'),
- mo_mow4_sit_filing_status='single',
- state_income_tax_additional_withholding=0.0,
- schedule_pay='monthly')
-
- self._log('2019 Missouri tax single first payslip:')
- 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.MO_UNEMP)
-
- mo_taxable_income = gross_salary - standard_deduction
- self._log('%s = %s - %s -' % (mo_taxable_income, gross_salary, standard_deduction))
-
- remaining_taxable_income = mo_taxable_income
- tax = 0.0
- for amt, rate in self.TAX:
- amt = float(amt)
- rate = rate / 100.0
- self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
- if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
- tax += rate * amt
- else:
- tax += rate * remaining_taxable_income
- break
- remaining_taxable_income = remaining_taxable_income - amt
-
- tax = -tax
- self._log('Computed annual tax: ' + str(tax))
-
- tax = tax / pp
- tax = round(tax)
- self._log('Computed period tax: ' + str(tax))
- self.assertPayrollEqual(cats['EE_US_SIT'], tax)
-
- def test_2019_spouse_not_employed(self):
- # Payroll Period Semi-monthly
- salary = self.SALARY
- pp = 24.0
- gross_salary = salary * pp
-
- # Married
- standard_deduction = 24800.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MO'),
- mo_mow4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- schedule_pay='semi-monthly')
-
- self._log('2019 Missouri tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- mo_taxable_income = gross_salary - standard_deduction
- self._log(mo_taxable_income)
-
- remaining_taxable_income = mo_taxable_income
- tax = 0.0
- for amt, rate in self.TAX:
- amt = float(amt)
- rate = rate / 100.0
- self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
- if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
- tax += rate * amt
- else:
- tax += rate * remaining_taxable_income
- break
- remaining_taxable_income = remaining_taxable_income - amt
-
- tax = -tax
- self._log('Computed annual tax: ' + str(tax))
-
- tax = tax / pp
- tax = round(tax)
- self._log('Computed period tax: ' + str(tax))
- self.assertPayrollEqual(cats['EE_US_SIT'], tax)
-
- def test_2019_head_of_household(self):
- # Payroll Period Weekly
- salary = self.SALARY
-
- # Payroll Period Weekly
- salary = self.SALARY
- pp = 52.0
- gross_salary = salary * pp
-
- # Single HoH
- standard_deduction = 18650.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MO'),
- mo_mow4_sit_filing_status='head_of_household',
- state_income_tax_additional_withholding=0.0,
- schedule_pay='weekly')
-
- self._log('2019 Missouri tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- mo_taxable_income = gross_salary - standard_deduction
- self._log(mo_taxable_income)
-
- remaining_taxable_income = mo_taxable_income
- tax = 0.0
- for amt, rate in self.TAX:
- amt = float(amt)
- rate = rate / 100.0
- self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
- if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
- tax += rate * amt
- else:
- tax += rate * remaining_taxable_income
- break
- remaining_taxable_income = remaining_taxable_income - amt
- tax = -tax
- self._log('Computed annual tax: ' + str(tax))
-
- tax = tax / pp
- tax = round(tax)
- self._log('Computed period tax: ' + str(tax))
- self.assertPayrollEqual(cats['EE_US_SIT'], tax)
-
- def test_2019_underflow(self):
- # Payroll Period Weekly
- salary = 200.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MO'))
-
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SIT'], 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2021.py
new file mode 100755
index 00000000..0824e2b9
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2021.py
@@ -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 TestUsMoPayslip(TestUsPayslip):
+ # Calculations from http://dor.mo.gov/forms/4282_2021.pdf
+ MO_UNEMP_MAX_WAGE = 11000.0
+ MO_UNEMP = 2.7
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MO'),
+ mo_mow4_sit_filing_status=filing_status,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('MO', self.MO_UNEMP, date(2021, 1, 1), wage_base=self.MO_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 'single', 0.0, 'weekly', date(2021, 1, 1), 24.00)
+ self._test_sit(2500.0, 'single', 5.0, 'bi-weekly', date(2021, 1, 1), 107.00)
+ self._test_sit(7000.0, 'married', 0.0, 'monthly', date(2021, 1, 1), 249.00)
+ self._test_sit(5000.0, 'married', 10.0, 'semi-monthly', date(2021, 1, 1), 216.00)
+ self._test_sit(6000.0, '', 0.0, 'monthly', date(2021, 1, 1), 0.00)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
deleted file mode 100755
index e7ce35d0..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip
-
-
-class TestUsMsPayslip(TestUsPayslip):
- # Calculations from https://www.dor.ms.gov/Documents/Computer%20Payroll%20Accounting%201-2-19.pdf
- MS_UNEMP = -1.2 / 100.0
-
- def test_2019_taxes_one(self):
- salary = 1250.0
- ms_89_350_exemption = 11000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MS'),
- ms_89_350_sit_filing_status='head_of_household',
- ms_89_350_sit_exemption_value=ms_89_350_exemption,
- state_income_tax_additional_withholding=0.0,
- schedule_pay='semi-monthly')
-
- self._log('2019 Mississippi tax single first payslip:')
- 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.MS_UNEMP)
-
- STDED = 3400.0 # Head of Household
- AGP = salary * 24 # Semi-Monthly
- TI = AGP - (ms_89_350_exemption + STDED)
- self.assertPayrollEqual(TI, 15600.0)
- TAX = ((TI - 10000) * 0.05) + 290 # Over 10,000
- self.assertPayrollEqual(TAX, 570.0)
-
- ms_withhold = round(TAX / 24) # Semi-Monthly
- self.assertPayrollEqual(cats['EE_US_SIT'], -ms_withhold)
-
- def test_2019_taxes_one_exempt(self):
- salary = 1250.0
- ms_89_350_exemption = 11000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MS'),
- ms_89_350_sit_filing_status='',
- ms_89_350_sit_exemption_value=ms_89_350_exemption,
- state_income_tax_additional_withholding=0.0,
- schedule_pay='semi-monthly')
-
- self._log('2019 Mississippi tax single first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
-
- def test_2019_taxes_additional(self):
- salary = 1250.0
- ms_89_350_exemption = 11000.0
- additional = 40.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MS'),
- ms_89_350_sit_filing_status='head_of_household',
- ms_89_350_sit_exemption_value=ms_89_350_exemption,
- state_income_tax_additional_withholding=additional,
- schedule_pay='semi-monthly')
-
- self._log('2019 Mississippi tax single first payslip:')
- 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.MS_UNEMP)
-
- STDED = 3400.0 # Head of Household
- AGP = salary * 24 # Semi-Monthly
- TI = AGP - (ms_89_350_exemption + STDED)
- self.assertPayrollEqual(TI, 15600.0)
- TAX = ((TI - 10000) * 0.05) + 290 # Over 10,000
- self.assertPayrollEqual(TAX, 570.0)
-
- ms_withhold = round(TAX / 24) # Semi-Monthly
- self.assertPayrollEqual(cats['EE_US_SIT'], -ms_withhold + -additional)
diff --git a/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2021.py
new file mode 100755
index 00000000..d59bded9
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2021.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 TestUsMsPayslip(TestUsPayslip):
+ # Calculations from https://www.dor.ms.gov/Documents/Computer%20Payroll%20Flowchart.pdf
+ MS_UNEMP = 1.0
+ MS_UNEMP_MAX_WAGE = 14000.0
+
+ def _test_sit(self, wage, filing_status, additional_withholding, exemption, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MS'),
+ ms_89_350_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ms_89_350_sit_exemption_value=exemption,
+ 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):
+ self._test_er_suta('MS', self.MS_UNEMP, date(2021, 1, 1), wage_base=self.MS_UNEMP_MAX_WAGE)
+ self._test_sit(1250.0, 'head_of_household', 0.0, 11000, 'semi-monthly', date(2021, 1, 1), 21.00)
+ self._test_sit(500.0, '', 5.0, 0, 'bi-weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(12000.0, 'single', 0.0, 11000, 'monthly', date(2021, 1, 1), 522.00)
+ self._test_sit(2500.0, 'married', 5.0, 500, 'bi-weekly', date(2021, 1, 1), 110.00)
+
+
diff --git a/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
deleted file mode 100755
index ff6e2daf..00000000
--- a/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsMtPayslip(TestUsPayslip):
- # Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705
- MT_UNEMP = -1.18 / 100.0
- MT_UNEMP_AFT = -0.13 / 100.0
-
- def test_2019_taxes_one(self):
- # Payroll Period Semi-Monthly example
- salary = 550
- mt_mw4_exemptions = 5
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MT'),
- mt_mw4_sit_exemptions=mt_mw4_exemptions,
- schedule_pay='semi-monthly')
-
- self._log('2019 Montana tax single first payslip:')
- 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.MT_UNEMP + self.MT_UNEMP_AFT)) # New non-combined...
-
- mt_taxable_income = salary - (79.0 * mt_mw4_exemptions)
- mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0)))
- self.assertPayrollEqual(mt_taxable_income, 155.0)
- self.assertPayrollEqual(mt_withhold, 3.0)
- self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold)
-
- def test_2019_taxes_two(self):
- # Payroll Period Bi-Weekly example
- salary = 2950
- mt_mw4_exemptions = 2
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MT'),
- mt_mw4_sit_exemptions=mt_mw4_exemptions,
- schedule_pay='bi-weekly')
-
- self._log('2019 Montana tax single first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], round(salary * (self.MT_UNEMP + self.MT_UNEMP_AFT), 2))
-
- # Note!!
- # The example calculation uses A = 16 but the actual table describes this as A = 18
- mt_taxable_income = salary - (73.0 * mt_mw4_exemptions)
- mt_withhold = round(18 + (0.06 * (mt_taxable_income - 577)))
- self.assertPayrollEqual(mt_taxable_income, 2804.0)
- self.assertPayrollEqual(mt_withhold, 152.0)
- self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold)
-
- def test_2019_taxes_three(self):
- # Payroll Period Weekly example
- salary = 135
- mt_mw4_exemptions = 1
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MT'),
- mt_mw4_sit_exemptions=mt_mw4_exemptions,
- schedule_pay='weekly')
-
- self._log('2019 Montana tax single first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], round(salary * (self.MT_UNEMP + self.MT_UNEMP_AFT), 2))
-
- mt_taxable_income = salary - (37.0 * mt_mw4_exemptions)
- mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0)))
- self.assertPayrollEqual(mt_taxable_income, 98.0)
- self.assertPayrollEqual(mt_withhold, 2.0)
- self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold)
-
- def test_2019_taxes_three_exempt(self):
- # Payroll Period Weekly example
- salary = 135
- mt_mw4_exemptions = 1
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MT'),
- mt_mw4_sit_exemptions=mt_mw4_exemptions,
- mt_mw4_sit_exempt='reserve',
- schedule_pay='weekly')
-
- self._log('2019 Montana tax single first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
-
- def test_2019_taxes_three_additional(self):
- # Payroll Period Weekly example
- salary = 135
- mt_mw4_exemptions = 1
- mt_mw4_additional_withholding = 20.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MT'),
- mt_mw4_sit_exemptions=mt_mw4_exemptions,
- state_income_tax_additional_withholding=mt_mw4_additional_withholding,
- schedule_pay='weekly')
-
- self._log('2019 Montana tax single first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- mt_taxable_income = salary - (37.0 * mt_mw4_exemptions)
- mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0)))
- self.assertPayrollEqual(mt_taxable_income, 98.0)
- self.assertPayrollEqual(mt_withhold, 2.0)
- self.assertPayrollEqual(cats['EE_US_SIT'], -mt_withhold + -mt_mw4_additional_withholding)
diff --git a/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2021.py
new file mode 100755
index 00000000..755f6208
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2021.py
@@ -0,0 +1,37 @@
+# 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 TestUsMtPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ MT_UNEMP_WAGE_MAX = 35300.0
+ MT_UNEMP = 1.18
+ MT_UNEMP_AFT = 0.13
+
+ # Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705
+ def _test_sit(self, wage, additional_withholding, exemptions, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MT'),
+ state_income_tax_additional_withholding=additional_withholding,
+ mt_mw4_sit_exemptions=exemptions,
+ 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_2021_taxes_one(self):
+ combined_rate = self.MT_UNEMP + self.MT_UNEMP_AFT # Combined for test as they both go to the same category and have the same cap
+ self._test_er_suta('MT', combined_rate + .0001, date(2021, 1, 1), wage_base=self.MT_UNEMP_WAGE_MAX, relaxed=True)
+ self._test_sit(550.0, 0.0, 5.0, 'semi-monthly', date(2021, 1, 1), 3.0)
+ self._test_sit(2950.0, 10.0, 2.0, 'bi-weekly', date(2021, 1, 1), 162.0)
+ self._test_sit(5000.0, 0.0, 1.0, 'monthly', date(2021, 1, 1), 256.0)
+ self._test_sit(750.0, 0.0, 1.0, 'weekly', date(2021, 1, 1), 34.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py
deleted file mode 100755
index 14c1c5b2..00000000
--- a/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2019.py
+++ /dev/null
@@ -1,270 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsNCPayslip(TestUsPayslip):
- ###
- # Taxes and Rates
- ###
- NC_UNEMP_MAX_WAGE = 24300.0
- NC_UNEMP = -1.0 / 100.0
- NC_INC_TAX = -0.0535
-
-
- def test_2019_taxes_weekly(self):
- salary = 20000.0
- # allowance_multiplier and Portion of Standard Deduction for weekly
- allowance_multiplier = 48.08
- PST = 192.31
- exemption = 1
- # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
- wh = -round((salary - (PST + (allowance_multiplier * exemption))) * -self.NC_INC_TAX)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NC'),
- nc_nc4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- nc_nc4_sit_allowances=1.0,
- schedule_pay='weekly')
-
- self._log('2019 North Carolina tax first payslip weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
- self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
-
- def test_2019_taxes_with_external_weekly(self):
- salary = 5000.0
- schedule_pay = 'weekly'
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NC'),
- nc_nc4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- nc_nc4_sit_allowances=1.0,
- schedule_pay='weekly')
-
- self._log('2019 NorthCarolina_external 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.NC_UNEMP)
-
- def test_2019_taxes_biweekly(self):
- salary = 5000.0
- schedule_pay = 'bi-weekly'
- # allowance_multiplier and Portion of Standard Deduction for weekly
- allowance_multiplier = 96.15
- PST = 384.62
-
- allowances = 2
- # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
-
- wh = -round((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX)
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NC'),
- nc_nc4_sit_filing_status='married',
- state_income_tax_additional_withholding=0.0,
- nc_nc4_sit_allowances=2.0,
- schedule_pay='bi-weekly')
-
- self._log('2019 North Carolina tax first payslip bi-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.NC_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 North Carolina tax second payslip bi-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_NC_UNEMP_wages * self.NC_UNEMP)
-
- def test_2019_taxes_semimonthly(self):
- salary = 4000.0
- # allowance_multiplier and Portion of Standard Deduction for weekly
- allowance_multiplier = 104.17
- PST = 625.00
-
- allowances = 1
- # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
-
- wh = -round((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX)
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NC'),
- nc_nc4_sit_filing_status='head_household',
- state_income_tax_additional_withholding=0.0,
- nc_nc4_sit_allowances=1.0,
- schedule_pay='semi-monthly')
-
- self._log('2019 North Carolina tax first payslip:')
- 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.NC_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
-
- def test_2019_taxes_monthly(self):
- salary = 4000.0
- schedule_pay = 'monthly'
- # allowance_multiplier and Portion of Standard Deduction for weekly
- allowance_multiplier = 208.33
- PST = 833.33
-
- allowances = 1
- # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
-
- wh = -round((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX)
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NC'),
- nc_nc4_sit_filing_status='single',
- state_income_tax_additional_withholding=0.0,
- nc_nc4_sit_allowances=1.0,
- schedule_pay='monthly')
-
- self._log('2019 North Carolina tax first payslip:')
- 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.NC_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (
- self.NC_UNEMP_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
-
- def test_additional_withholding(self):
- salary = 4000.0
- # allowance_multiplier and Portion of Standard Deduction for weekly
- allowance_multiplier = 48.08
- PST = 192.31
- additional_wh = 40.0
-
- #4000 - (168.27 + (48.08 * 1)
-
- allowances = 1
- # Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
-
- wh = -round(((salary - (PST + (allowance_multiplier * allowances))) * -self.NC_INC_TAX) + additional_wh)
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NC'),
- nc_nc4_sit_filing_status='married',
- state_income_tax_additional_withholding=40.0,
- nc_nc4_sit_allowances=1.0,
- schedule_pay='weekly')
-
- self._log('2019 North Carolina tax first payslip weekly:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 North Carolina 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_NC_UNEMP_wages * self.NC_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2021.py
new file mode 100755
index 00000000..b8119f21
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2021.py
@@ -0,0 +1,39 @@
+# 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 TestUsNCPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ # Example based on https://files.nc.gov/ncdor/documents/files/2021-NC-30-Final.pdf page 19
+ NC_UNEMP_MAX_WAGE = 26000.0
+ NC_UNEMP = 1.0
+ # get NC_INC_TAX from 'Annualized Tax" line 8 in the example on page 19
+ NC_INC_TAX = 0.0535
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NC'),
+ nc_nc4_sit_filing_status=filing_status,
+ nc_nc4_sit_allowances=allowances,
+ 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 if filing_status else 0.0)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('NC', self.NC_UNEMP, date(2021, 1, 1), wage_base=self.NC_UNEMP_MAX_WAGE)
+ self._test_sit(20000.0, 'single', 1, 100.0, 'weekly', date(2021, 1, 1), 1156.0)
+ self._test_sit(5000.0, 'married', 1, 0.0, 'weekly', date(2021, 1, 1), 254.0)
+ self._test_sit(4000.0, 'head_household', 1, 5.0, 'semi-monthly', date(2021, 1, 1), 177.0)
+ self._test_sit(7000.0, '', 1, 5.0, 'monthly', date(2021, 1, 1), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2021.py
new file mode 100644
index 00000000..c4dfaeb5
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2021.py
@@ -0,0 +1,37 @@
+# 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 TestUsNDPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ ND_UNEMP_MAX_WAGE = 38500.0
+ ND_UNEMP = 1.02
+ # Calculation based on this file page.47 https://www.nd.gov/tax/data/upfiles/media/rates-and-instructions.pdf?20210110115917
+
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('ND', self.ND_UNEMP, date(2021, 1, 1), wage_base=self.ND_UNEMP_MAX_WAGE)
+ self._test_sit(700.0, 'single', 0.0, 0.0, 'weekly', date(2021, 1, 1), 6.0)
+ self._test_sit(5000.0, 'married', 0.0, 2.0, 'bi-weekly', date(2021, 1, 1), 76.0)
+ self._test_sit(25000.0, 'head_household', 0.0, 0.0, 'monthly', date(2021, 1, 1), 533.0)
+ self._test_sit(25000.0, 'head_household', 10.0, 2.0, 'monthly', date(2021, 1, 1), 524.0)
+ self._test_sit(3000.0, '', 10.0, 2.0, 'monthly', date(2021, 1, 1), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2021.py
new file mode 100644
index 00000000..294856a7
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2021.py
@@ -0,0 +1,38 @@
+# 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 TestUsNEPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ NE_UNEMP_MAX_WAGE = 9000.0
+ NE_UNEMP = 2.5
+
+ def _test_sit(self, wage, filing_status, exempt, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NE'),
+ ne_w4n_sit_filing_status=filing_status,
+ state_income_tax_exempt=exempt,
+ state_income_tax_additional_withholding=additional_withholding,
+ ne_w4n_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):
+ self._test_er_suta('NE', self.NE_UNEMP, date(2021, 1, 1), wage_base=self.NE_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 'single', False, 0.0, 2, 'weekly', date(2021, 1, 1), 27.53)
+ self._test_sit(9500.0, 'single', False, 0.0, 1, 'bi-weekly', date(2021, 1, 1), 612.63)
+ self._test_sit(10500.0, 'married', False, 0.0, 1, 'bi-weekly', date(2021, 1, 1), 659.85)
+ self._test_sit(9500.0, 'single', True, 0.0, 1, 'bi-weekly', date(2021, 1, 1), 0.0)
+ self._test_sit(10500.0, 'single', False, 10.0, 2, 'monthly', date(2021, 1, 1), 625.2)
+ self._test_sit(4000.0, 'single', False, 0.0, 1, 'monthly', date(2021, 1, 1), 179.44)
diff --git a/l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2021.py
new file mode 100644
index 00000000..5c1b4a1f
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nh_new_hampshire_payslip_2021.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsNHPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ NH_UNEMP_MAX_WAGE = 14000.00
+ NH_UNEMP = 2.7
+
+ def test_2021_taxes(self):
+ self._test_er_suta('NH', self.NH_UNEMP, date(2021, 1, 1), wage_base=self.NH_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py
deleted file mode 100755
index c28849b5..00000000
--- a/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2019.py
+++ /dev/null
@@ -1,128 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsNJPayslip(TestUsPayslip):
- ###
- # 2019 Taxes and Rates
- ###
- NJ_UNEMP_MAX_WAGE = 34400.0 # Note that this is used for SDI and FLI as well
-
- ER_NJ_UNEMP = -2.6825 / 100.0
- EE_NJ_UNEMP = -0.3825 / 100.0
-
- ER_NJ_SDI = -0.5 / 100.0
- EE_NJ_SDI = -0.17 / 100.0
-
- ER_NJ_WF = -0.1175 / 100.0
- EE_NJ_WF = -0.0425 / 100.0
-
- ER_NJ_FLI = 0.0
- EE_NJ_FLI = -0.08 / 100.0
-
- # Examples found on page 24 of http://www.state.nj.us/treasury/taxation/pdf/current/njwt.pdf
- def test_2019_taxes_example1(self):
- salary = 300
-
- # Tax Percentage Method for Single, taxable under $385
- wh = -4.21
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NJ'),
- nj_njw4_sit_filing_status='single',
- nj_njw4_sit_allowances=1,
- state_income_tax_additional_withholding=0.0,
- nj_njw4_sit_rate_table='A',
- schedule_pay='weekly')
-
- self._log('2019 New Jersey tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SUTA'], salary * (self.EE_NJ_UNEMP + self.EE_NJ_SDI + self.EE_NJ_WF + self.EE_NJ_FLI))
- self.assertPayrollEqual(cats['ER_US_SUTA'], salary * (self.ER_NJ_UNEMP + self.ER_NJ_SDI + self.ER_NJ_WF + self.ER_NJ_FLI))
- self.assertTrue(all((cats['EE_US_SUTA'], cats['ER_US_SUTA'])))
- # self.assertPayrollEqual(cats['EE_US_NJ_SDI_SIT'], cats['EE_US_NJ_SDI_SIT'] * self.EE_NJ_SDI)
- # self.assertPayrollEqual(cats['ER_US_NJ_SDI_SUTA'], cats['ER_US_NJ_SDI_SUTA'] * self.ER_NJ_SDI)
- # self.assertPayrollEqual(cats['EE_US_NJ_FLI_SIT'], cats['EE_US_NJ_FLI_SIT'] * self.EE_NJ_FLI)
- # self.assertPayrollEqual(cats['EE_US_NJ_WF_SIT'], cats['EE_US_NJ_WF_SIT'] * self.EE_NJ_WF)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- # # Make a new payslip, this one will have maximums
- #
- remaining_nj_unemp_wages = self.NJ_UNEMP_MAX_WAGE - salary if (self.NJ_UNEMP_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 New Jersey tax second payslip:')
- payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- # self.assertPayrollEqual(cats['WAGE_US_NJ_UNEMP'], remaining_nj_unemp_wages)
- self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_nj_unemp_wages * (self.ER_NJ_UNEMP + self.ER_NJ_SDI + self.ER_NJ_WF + self.ER_NJ_FLI))
- self.assertPayrollEqual(cats['EE_US_SUTA'], remaining_nj_unemp_wages * (self.EE_NJ_UNEMP + self.EE_NJ_SDI + self.EE_NJ_WF + self.EE_NJ_FLI))
-
- def test_2019_taxes_example2(self):
- salary = 1400.00
-
- # Tax Percentage Method for Single, taxable pay over $962, under $1346
- wh = -27.58
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NJ'),
- nj_njw4_sit_filing_status='married_separate',
- nj_njw4_sit_allowances=3,
- state_income_tax_additional_withholding=0.0,
- #nj_njw4_sit_rate_table='B',
- schedule_pay='weekly')
-
- self.assertEqual(contract.schedule_pay, 'weekly')
-
- self._log('2019 New Jersey tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
-
- def test_2019_taxes_to_the_limits(self):
- salary = 30000.00
-
- # Tax Percentage Method for Single, taxable pay over $18750, under 125000
- wh = -1467.51
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NJ'),
- nj_njw4_sit_filing_status='married_joint',
- nj_njw4_sit_allowances=3,
- state_income_tax_additional_withholding=0.0,
- # nj_njw4_sit_rate_table='B',
- schedule_pay='quarterly')
-
- self.assertEqual(contract.schedule_pay, 'quarterly')
-
- self._log('2019 New Jersey tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-03-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2021.py
new file mode 100755
index 00000000..2b3fc627
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2021.py
@@ -0,0 +1,51 @@
+# 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 TestUsNJPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ NJ_UNEMP_MAX_WAGE = 36200.0 # Note that this is used for SDI and FLI as well
+
+ ER_NJ_UNEMP = 2.6825
+ EE_NJ_UNEMP = 0.3825
+
+ ER_NJ_SDI = 0.5
+ EE_NJ_SDI = 0.47
+
+ ER_NJ_WF = 0.1175
+ EE_NJ_WF = 0.0425
+
+ ER_NJ_FLI = 0.0
+ EE_NJ_FLI = 0.28
+
+ def _test_sit(self, wage, filing_status, allowances, schedule_pay, date_start, expected_withholding, rate_table=False):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NJ'),
+ nj_njw4_sit_filing_status=filing_status,
+ nj_njw4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=0.0,
+ nj_njw4_sit_rate_table=rate_table,
+ schedule_pay=schedule_pay)
+ payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding if filing_status else 0.0)
+
+ def test_2021_taxes_example1(self):
+ combined_er_rate = self.ER_NJ_UNEMP + self.ER_NJ_FLI + self.ER_NJ_SDI + self.ER_NJ_WF
+ self._test_er_suta('NJ', combined_er_rate, date(2021, 1, 1), wage_base=self.NJ_UNEMP_MAX_WAGE, relaxed=True)
+ combined_ee_rate = self.EE_NJ_UNEMP + self.EE_NJ_FLI + self.EE_NJ_SDI + self.EE_NJ_WF
+ self._test_ee_suta('NJ', combined_ee_rate, date(2021, 1, 1), wage_base=self.NJ_UNEMP_MAX_WAGE, relaxed=True)
+ # these expected values come from https://www.state.nj.us/treasury/taxation/pdf/current/njwt.pdf
+ self._test_sit(300.0, 'single', 1, 'weekly', date(2021, 1, 1), 4.21)
+ self._test_sit(375.0, 'married_separate', 3, 'weekly', date(2021, 1, 1), 4.76)
+ self._test_sit(1400.0, 'head_household', 3, 'weekly', date(2021, 1, 1), 27.60)
+ self._test_sit(1400.0, '', 3, 'weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(2500.0, 'single', 3, 'bi-weekly', date(2021, 1, 1), 82.66)
+ self._test_sit(15000.0, 'married_joint', 2, 'monthly', date(2021, 1, 1), 844.85)
diff --git a/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2021.py
new file mode 100755
index 00000000..317977ba
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsNMPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ NM_UNEMP_MAX_WAGE = 27000.0
+ NM_UNEMP = 1.0
+ # Calculation based on section 17. https://s3.amazonaws.com/realFile34821a95-73ca-43e7-b06d-fad20f5183fd/a9bf1098-533b-4a3d-806a-4bf6336af6e4?response-content-disposition=filename%3D%22FYI-104+-+New+Mexico+Withholding+Tax+-+Effective+January+1%2C+2021.pdf%22&response-content-type=application%2Fpdf&AWSAccessKeyId=AKIAJBI25DHBYGD7I7TA&Signature=feu%2F1oJvU6BciRfKcoR0iNxoVZE%3D&Expires=1585159702
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NM'),
+ fed_941_fit_w4_filing_status=filing_status,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('NM', self.NM_UNEMP, date(2021, 1, 1), wage_base=self.NM_UNEMP_MAX_WAGE)
+ self._test_sit(1000.0, 'married', 0.0, 'weekly', date(2021, 1, 1), 29.32)
+ self._test_sit(1000.0, 'married', 10.0, 'weekly', date(2021, 1, 1), 39.32)
+ self._test_sit(25000.0, 'single', 0.0, 'bi-weekly', date(2021, 1, 1), 1369.25)
+ self._test_sit(25000.0, 'married_as_single', 0.0, 'monthly', date(2021, 1, 1), 1152.63)
+ self._test_sit(4400.0, '', 0.0, 'monthly', date(2021, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2021.py
new file mode 100755
index 00000000..284990a0
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2021.py
@@ -0,0 +1,16 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsNVPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ NV_UNEMP_MAX_WAGE = 33400.0
+ NV_UNEMP = 2.95
+
+ def test_2021_taxes(self):
+ # Only has state unemployment
+ self._test_er_suta('NV', self.NV_UNEMP, date(2021, 1, 1), wage_base=self.NV_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
deleted file mode 100644
index 2c3b9306..00000000
--- a/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsNYPayslip(TestUsPayslip):
- ###
- # Taxes and Rates
- ###
- NY_UNEMP_MAX_WAGE = 11400.0
- NY_UNEMP = 2.5
- NY_RSF = 0.075
- NY_MCTMT = 0.0
-
- def test_single_example1(self):
- salary = 400
- schedule_pay = 'weekly'
- allowances = 3
- additional_withholding = 0
- filing_status = 'single'
- additional = 0.0
- wh = -8.20
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NY'),
- ny_it2104_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional,
- ny_it2104_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self.assertEqual(contract.schedule_pay, 'weekly')
- self._log('2018 New York tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['NY_UNEMP'], cats['NY_UNEMP_WAGES'] * self.NY_UNEMP)
- self.assertPayrollEqual(cats['NY_RSF'], cats['NY_UNEMP_WAGES'] * self.NY_RSF)
- self.assertPayrollEqual(cats['NY_MCTMT'], cats['NY_UNEMP_WAGES'] * self.NY_MCTMT)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- def test_married_example2(self):
- salary = 5000
- schedule_pay = 'semi-monthly'
- allowances = 3
- additional = 0
- filing_status = 'married'
- wh = -284.19
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NY'),
- ny_it2104_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional,
- ny_it2104_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 New York tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['NY_UNEMP'], cats['NY_UNEMP_WAGES'] * self.NY_UNEMP)
- self.assertPayrollEqual(cats['NY_RSF'], cats['NY_UNEMP_WAGES'] * self.NY_RSF)
- self.assertPayrollEqual(cats['NY_MCTMT'], cats['NY_UNEMP_WAGES'] * self.NY_MCTMT)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- process_payslip(payslip)
-
- def test_single_example3(self):
- salary = 50000
- schedule_pay = 'monthly'
- allowances = 3
- additional = 0
- filing_status = 'single'
- wh = -3575.63
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NY'),
- ny_it2104_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional,
- ny_it2104_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 New York tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
- def test_exempt_example3(self):
- salary = 50000
- schedule_pay = 'monthly'
- allowances = 3
- additional = 0
- filing_status = ''
- wh = 0.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('NY'),
- ny_it2104_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=additional,
- ny_it2104_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 New York tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
-
diff --git a/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2021.py
new file mode 100644
index 00000000..e8f0383d
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2021.py
@@ -0,0 +1,39 @@
+# 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 TestUsNYPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ NY_UNEMP_MAX_WAGE = 11800.0
+ NY_UNEMP = 2.5 #todo: update Feb 2021
+ NY_RSF = 0.075 #todo: update Feb 2021
+ NY_MCTMT = 0.0 #todo: update Feb 2021
+
+ 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('NY'),
+ ny_it2104_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ny_it2104_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_2020_taxes_example(self):
+ combined_er_rate = self.NY_UNEMP + self.NY_RSF + self.NY_MCTMT
+ self._test_er_suta('NY', combined_er_rate, date(2021, 1, 1), wage_base=self.NY_UNEMP_MAX_WAGE, relaxed=True)
+ self._test_sit(400.0, 'single', 0.0, 3, 'weekly', date(2021, 1, 1), 8.20)
+ self._test_sit(10000.0, 'single', 0.0, 3, 'monthly', date(2021, 1, 1), 546.30)
+ self._test_sit(8000.0, 'married', 0.0, 5, 'monthly', date(2021, 1, 1), 394.24)
+ self._test_sit(4500.0, 'married', 10.0, 3, 'semi-monthly', date(2021, 1, 1), 244.21)
+ self._test_sit(50000.0, '', 0.0, 0, 'monthly', date(2021, 1, 1), 0.00)
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
deleted file mode 100755
index d1f65f05..00000000
--- a/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
-
-
-class TestUsOhPayslip(TestUsPayslip):
- ###
- # Taxes and Rates
- ###
- OH_UNEMP_MAX_WAGE = 9500.0
- OH_UNEMP = -2.7 / 100.0
-
- def test_2019_taxes(self):
- salary = 5000.0
-
- # For formula here
- # http://www.tax.ohio.gov/Portals/0/employer_withholding/August2015Rates/WTH_OptionalComputerFormula_073015.pdf
- tw = salary * 12 # = 60000
- wd = ((tw - 40000) * 0.035 + 900) / 12 * 1.075
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('OH'),
- )
-
- self._log('2019 Ohio tax first payslip:')
- 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.OH_UNEMP)
- 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)
-
- # Make a new payslip, this one will have maximums
-
- remaining_oh_unemp_wages = self.OH_UNEMP_MAX_WAGE - salary if (self.OH_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Ohio 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_oh_unemp_wages * self.OH_UNEMP)
-
- def test_2019_taxes_with_external(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('OH'),
- external_wages=external_wages,
- )
-
- self._log('2019 Ohio_external tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], (self.OH_UNEMP_MAX_WAGE - external_wages) * self.OH_UNEMP)
-
- def test_2019_taxes_with_state_exempt(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('OH'),
- external_wages=external_wages,
- futa_type=USHRContract.FUTA_TYPE_BASIC)
-
- self._log('2019 Ohio exempt tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- # FUTA_TYPE_BASIC
- self.assertPayrollEqual(cats.get('ER_US_SUTA', 0.0), salary * 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2021.py
new file mode 100755
index 00000000..a05ff6ca
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2021.py
@@ -0,0 +1,108 @@
+# 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 TestUsOhPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ OH_UNEMP_MAX_WAGE = 9000.0
+ OH_UNEMP = 2.7
+
+ def test_2021_taxes(self):
+ self._test_er_suta('OH', self.OH_UNEMP, date(2021, 1, 1), wage_base=self.OH_UNEMP_MAX_WAGE)
+
+ def _run_test_sit(self,
+ wage=0.0,
+ schedule_pay='monthly',
+ filing_status='single',
+ dependent_credit=0.0,
+ other_income=0.0,
+ deductions=0.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=False,
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=0,
+ expected=0.0,
+ ):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ schedule_pay=schedule_pay,
+ fed_941_fit_w4_is_nonresident_alien=is_nonresident_alien,
+ fed_941_fit_w4_filing_status=filing_status,
+ fed_941_fit_w4_multiple_jobs_higher=False,
+ fed_941_fit_w4_dependent_credit=dependent_credit,
+ fed_941_fit_w4_other_income=other_income,
+ fed_941_fit_w4_deductions=deductions,
+ fed_941_fit_w4_additional_withholding=additional_withholding,
+ state_income_tax_exempt=state_income_tax_exempt,
+ state_income_tax_additional_withholding=state_income_tax_additional_withholding,
+ oh_it4_sit_exemptions=oh_it4_sit_exemptions,
+ state_id=self.get_us_state('OH'),
+ )
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ # Instead of PayrollEqual after initial first round of testing.
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected)
+ return payslip
+
+ def test_2021_sit_1(self):
+ wage = 400.0
+ exemptions = 1
+ additional = 10.0
+ pay_periods = 12.0
+ annual_adjusted_wage = (wage * pay_periods) - (650.0 * exemptions)
+ self.assertPayrollEqual(4150.0, annual_adjusted_wage)
+ WD = ((annual_adjusted_wage * 0.005) / pay_periods) * 1.032
+ self.assertPayrollEqual(WD, 1.7845)
+ expected = WD + additional
+ self._run_test_sit(wage=wage,
+ schedule_pay='monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=additional,
+ oh_it4_sit_exemptions=exemptions,
+ expected=expected,
+ )
+
+ # the above agrees with online calculator to the penny 0.01
+ # below expected coming from calculator to 0.10
+ #
+ # semi-monthly
+ self._run_test_sit(wage=1200,
+ schedule_pay='semi-monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=20.0,
+ oh_it4_sit_exemptions=2,
+ expected=42.58,
+ )
+
+ # bi-weekly
+ self._run_test_sit(wage=3000,
+ schedule_pay='bi-weekly',
+ state_income_tax_exempt=False,
+ #state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=0,
+ expected=88.51,
+ )
+ # weekly
+ self._run_test_sit(wage=355,
+ schedule_pay='weekly',
+ state_income_tax_exempt=False,
+ # state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=1,
+ expected=4.87,
+ )
+
+ # Exempt!
+ self._run_test_sit(wage=355,
+ schedule_pay='weekly',
+ state_income_tax_exempt=True,
+ # state_income_tax_additional_withholding=0.0,
+ oh_it4_sit_exemptions=1,
+ expected=0.0,
+ )
diff --git a/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2021.py
new file mode 100755
index 00000000..096f3dfd
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2021.py
@@ -0,0 +1,38 @@
+# 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 TestUsOKPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ OK_UNEMP_MAX_WAGE = 24000.0
+ OK_UNEMP = 1.0
+ # Calculation based on example https://www.ok.gov/tax/documents/2021WHTables.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('OK'),
+ ok_w4_sit_filing_status=filing_status,
+ ok_w4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('OK', self.OK_UNEMP, date(2021, 1, 1), wage_base=self.OK_UNEMP_MAX_WAGE)
+ self._test_sit(1825, 'married', 2, 0, False, 'semi-monthly', date(2021, 1, 1), 46.00)
+ self._test_sit(1825, 'married', 2, 0, True, 'monthly', date(2021, 1, 1), 0.00)
+ self._test_sit(1000, 'single', 1, 0, False, 'weekly', date(2021, 1, 1), 39.00)
+ self._test_sit(1000, 'single', 1, 10, False, 'weekly', date(2021, 1, 1), 49.00)
+ self._test_sit(5000, 'head_household', 2, 10, False, 'monthly', date(2021, 1, 1), 210.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
deleted file mode 100755
index ce7e4fb4..00000000
--- a/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsPAPayslip(TestUsPayslip):
- ###
- # Taxes and Rates
- ###
- PA_UNEMP_MAX_WAGE = 10000.0
- ER_PA_UNEMP = -3.6890 / 100.0
- EE_PA_UNEMP = -0.06 / 100.0
- PA_INC_WITHHOLD = 3.07
-
- def test_2019_taxes(self):
- salary = 4166.67
- wh = -127.92
-
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('PA'))
-
- self._log('2019 Pennsylvania tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SUTA'], cats['GROSS'] * self.EE_PA_UNEMP)
- self.assertPayrollEqual(cats['ER_US_SUTA'], cats['GROSS'] * self.ER_PA_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2021.py
new file mode 100755
index 00000000..800fe972
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2021.py
@@ -0,0 +1,43 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsPAPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ PA_UNEMP_MAX_WAGE = 10000.0
+ ER_PA_UNEMP = 3.6890
+ EE_PA_UNEMP = 0.06
+ PA_INC_WITHHOLD = 3.07
+
+ def test_2021_taxes(self):
+ self._test_er_suta('PA', self.ER_PA_UNEMP, date(2021, 1, 1), wage_base=self.PA_UNEMP_MAX_WAGE)
+ self._test_ee_suta('PA', self.EE_PA_UNEMP, date(2021, 1, 1))
+
+ salary = 4166.67
+ wh = -127.90
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('PA'))
+
+ self._log('2019 Pennsylvania tax first payslip:')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ # Test Additional
+ contract.us_payroll_config_id.state_income_tax_additional_withholding = 100.0
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh - 100.0)
+
+ # Test Exempt
+ contract.us_payroll_config_id.state_income_tax_exempt = True
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_payslip_2019.py
deleted file mode 100644
index db6004e9..00000000
--- a/l10n_us_hr_payroll/tests/test_us_payslip_2019.py
+++ /dev/null
@@ -1,338 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
-
-from sys import float_info
-
-
-class TestUsPayslip2019(TestUsPayslip):
- # FUTA Constants
- FUTA_RATE_NORMAL = 0.6
- FUTA_RATE_BASIC = 6.0
- FUTA_RATE_EXEMPT = 0.0
-
- # Wage caps
- FICA_SS_MAX_WAGE = 132900.0
- FICA_M_MAX_WAGE = float_info.max
- FICA_M_ADD_START_WAGE = 200000.0
- FUTA_MAX_WAGE = 7000.0
-
- # Rates
- FICA_SS = 6.2 / -100.0
- FICA_M = 1.45 / -100.0
- FUTA = FUTA_RATE_NORMAL / -100.0
- FICA_M_ADD = 0.9 / -100.0
-
- ###
- # 2019 Taxes and Rates
- ###
-
- def test_2019_taxes(self):
- self.debug = False
- # salary is high so that second payslip runs over max
- # social security salary
- salary = 80000.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee, wage=salary)
- self._log(contract.read())
-
- self._log('2018 tax last slip')
- payslip = self._createPayslip(employee, '2018-12-01', '2018-12-31')
- payslip.compute_sheet()
- self._log(payslip.read())
- process_payslip(payslip)
-
- # Ensure amounts are there, they shouldn't be added in the next year...
- cats = self._getCategories(payslip)
- self.assertTrue(cats['ER_US_940_FUTA'], self.FUTA_MAX_WAGE * self.FUTA)
-
- self._log('2019 tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
- # Employee
- self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], cats['BASIC'] * self.FICA_SS)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M'], cats['BASIC'] * self.FICA_M)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], 0.0)
- # Employer
- self.assertPayrollEqual(rules['ER_US_941_FICA_SS'], rules['EE_US_941_FICA_SS'])
- self.assertPayrollEqual(rules['ER_US_941_FICA_M'], rules['EE_US_941_FICA_M'])
- self.assertTrue(cats['ER_US_940_FUTA'], self.FUTA_MAX_WAGE * self.FUTA)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have reached Medicare Additional (employee only)
- remaining_ss_wages = self.FICA_SS_MAX_WAGE - salary if (self.FICA_SS_MAX_WAGE - 2 * salary < salary) else salary
- remaining_m_wages = self.FICA_M_MAX_WAGE - salary if (self.FICA_M_MAX_WAGE - 2 * salary < salary) else salary
-
- self._log('2019 tax second payslip:')
- payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
-
- self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], remaining_ss_wages * self.FICA_SS)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M'], remaining_m_wages * self.FICA_M)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], 0.0)
- self.assertPayrollEqual(cats['ER_US_940_FUTA'], 0.0)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have reached Medicare Additional (employee only)
- self._log('2019 tax third payslip:')
- payslip = self._createPayslip(employee, '2019-03-01', '2019-03-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
-
- self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], (self.FICA_M_ADD_START_WAGE - (salary * 2)) * self.FICA_M_ADD) # aka 40k
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have all salary as Medicare Additional
-
- self._log('2019 tax fourth payslip:')
- payslip = self._createPayslip(employee, '2019-04-01', '2019-04-30')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
-
- self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], salary * self.FICA_M_ADD)
-
- process_payslip(payslip)
-
- def test_2019_fed_income_withholding_single(self):
- self.debug = False
-
- salary = 6000.00
- schedule_pay = 'monthly'
- w4_allowances = 3
- w4_allowance_amt = 350.00 * w4_allowances
- adjusted_salary = salary - w4_allowance_amt # should be 4962.60, but would work over a wide value for the rate
- ###
- # Single MONTHLY form Publication 15
- expected_withholding = self.float_round(-(378.52 + ((adjusted_salary - 3606) * 0.22)), self.payroll_digits)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- schedule_pay=schedule_pay,
- fed_941_fit_w4_filing_status='single',
- fed_941_fit_w4_allowances=w4_allowances
- )
-
- self._log('2019 fed income single payslip: adjusted_salary: ' + str(adjusted_salary))
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['EE_US_941_FIT'], expected_withholding)
-
- def test_2019_fed_income_withholding_married_as_single(self):
- salary = 500.00
- schedule_pay = 'weekly'
- w4_allowances = 1
- w4_allowance_amt = 80.80 * w4_allowances
- adjusted_salary = salary - w4_allowance_amt # should be 420.50, but would work over a wide value for the rate
- ###
- expected_withholding = self.float_round(-(18.70 + ((adjusted_salary - 260) * 0.12)), self.payroll_digits)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- schedule_pay=schedule_pay,
- fed_941_fit_w4_filing_status='married_as_single',
- fed_941_fit_w4_allowances=w4_allowances,
- )
-
- self._log('2019 fed income married_as_single payslip: adjusted_salary: ' + str(adjusted_salary))
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['EE_US_941_FIT'], expected_withholding)
-
- def test_2019_fed_income_withholding_married(self):
- salary = 14000.00
- schedule_pay = 'bi-weekly'
- w4_allowances = 2
- w4_allowance_amt = 161.50 * w4_allowances
- adjusted_salary = salary - w4_allowance_amt # should be 13680.80, but would work over a wide value for the rate
- ###
- # Single MONTHLY form Publication 15
- expected_withholding = self.float_round(-(2519.06 + ((adjusted_salary - 12817) * 0.32)), self.payroll_digits)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- schedule_pay=schedule_pay,
- fed_941_fit_w4_filing_status='married',
- fed_941_fit_w4_allowances=w4_allowances
- )
-
- self._log('2019 fed income married payslip: adjusted_salary: ' + str(adjusted_salary))
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- # This is off by 1 penny given our new reporting of adjusted wage * computed percentage
- #self.assertPayrollEqual(cats['EE_US_941_FIT'], expected_withholding)
- self.assertTrue(abs(cats['EE_US_941_FIT'] - expected_withholding) < 0.011)
-
- def test_2019_taxes_with_external(self):
- # social security salary
- salary = self.FICA_M_ADD_START_WAGE
- external_wages = 6000.0
-
- employee = self._createEmployee()
-
- self._createContract(employee, wage=salary, external_wages=external_wages)
-
- self._log('2019 tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
- self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], (self.FICA_SS_MAX_WAGE - external_wages) * self.FICA_SS)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M'], salary * self.FICA_M)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], external_wages * self.FICA_M_ADD)
- self.assertPayrollEqual(rules['ER_US_941_FICA_SS'], rules['EE_US_941_FICA_SS'])
- self.assertPayrollEqual(rules['ER_US_941_FICA_M'], rules['EE_US_941_FICA_M'])
- self.assertPayrollEqual(cats['ER_US_940_FUTA'], (self.FUTA_MAX_WAGE - external_wages) * self.FUTA)
-
- def test_2019_taxes_with_full_futa(self):
- futa_rate = self.FUTA_RATE_BASIC / -100.0
- # social security salary
- salary = self.FICA_M_ADD_START_WAGE
-
- employee = self._createEmployee()
-
- self._createContract(employee, wage=salary, fed_940_type=USHRContract.FUTA_TYPE_BASIC)
-
- self._log('2019 tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
- self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], self.FICA_SS_MAX_WAGE * self.FICA_SS)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M'], salary * self.FICA_M)
- self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], 0.0 * self.FICA_M_ADD)
- self.assertPayrollEqual(rules['ER_US_941_FICA_SS'], rules['EE_US_941_FICA_SS'])
- self.assertPayrollEqual(rules['ER_US_941_FICA_M'], rules['EE_US_941_FICA_M'])
- self.assertPayrollEqual(cats['ER_US_940_FUTA'], self.FUTA_MAX_WAGE * futa_rate)
-
- def test_2019_taxes_with_futa_exempt(self):
- futa_rate = self.FUTA_RATE_EXEMPT / -100.0 # because of exemption
-
- # social security salary
- salary = self.FICA_M_ADD_START_WAGE
- employee = self._createEmployee()
- self._createContract(employee, wage=salary, fed_940_type=USHRContract.FUTA_TYPE_EXEMPT)
- self._log('2019 tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['ER_US_940_FUTA'], 0.0)
-
- def test_2019_fed_income_withholding_nonresident_alien(self):
- salary = 3500.00
- schedule_pay = 'quarterly'
- w4_allowances = 1
- w4_allowance_amt = 1050.0 * w4_allowances
- nra_adjustment = 2000.0 # for quarterly
- adjusted_salary = salary - w4_allowance_amt + nra_adjustment # 4450
-
- ###
- # Single QUARTERLY form Publication 15
- expected_withholding = self.float_round(-(242.50 + ((adjusted_salary - 3375) * 0.12)), self.payroll_digits)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- schedule_pay=schedule_pay,
- fed_941_fit_w4_allowances=w4_allowances,
- fed_941_fit_w4_is_nonresident_alien=True,
- )
-
- self._log('2019 fed income single payslip nonresident alien: adjusted_salary: ' + str(adjusted_salary))
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
- rules = self._getRules(payslip)
- self.assertPayrollEqual(rules['EE_US_941_FIT'], expected_withholding)
-
- def test_2019_fed_income_additional_withholding(self):
- salary = 50000.00
- schedule_pay = 'annually'
- w4_additional_withholding = 5000.0
- w4_allowances = 2
- w4_allowance_amt = 4200.0 * w4_allowances
- adjusted_salary = salary - w4_allowance_amt # 41700
-
- ###
- # Single ANNUAL form Publication 15
- expected_withholding = \
- self.float_round(-((1940.00 + ((adjusted_salary - 31200) * 0.12)) + w4_additional_withholding),
- self.payroll_digits)
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- schedule_pay=schedule_pay,
- fed_941_fit_w4_filing_status='married',
- fed_941_fit_w4_allowances=w4_allowances,
- fed_941_fit_w4_additional_withholding=w4_additional_withholding,
- )
-
- self._log('2019 fed income married payslip additional withholding: adjusted_salary: ' + str(adjusted_salary))
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
-
- rules = self._getRules(payslip)
- self.assertPayrollEqual(rules['EE_US_941_FIT'], expected_withholding)
-
- def test_2019_taxes_with_w4_exempt(self):
- salary = 6000.0
- schedule_pay = 'bi-weekly'
- w4_allowances = 0
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- schedule_pay=schedule_pay,
- fed_941_fit_w4_allowances=w4_allowances,
- fed_941_fit_w4_filing_status='',
- )
-
- self._log('2019 tax w4 exempt payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- rules = self._getRules(payslip)
- self.assertPayrollEqual(rules['EE_US_941_FIT'], 0.0)
-
- def test_2019_taxes_with_fica_exempt(self):
- salary = 6000.0
- schedule_pay = 'bi-weekly'
- employee = self._createEmployee()
- contract = self._createContract(employee, wage=salary, schedule_pay=schedule_pay)
- contract.us_payroll_config_id.fed_941_fica_exempt = True
-
- self._log('2019 tax w4 exempt payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['EE_US_941_FICA'], 0.0)
- self.assertPayrollEqual(cats['ER_US_941_FICA'], 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_payslip_2021.py
new file mode 100644
index 00000000..60822b05
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_payslip_2021.py
@@ -0,0 +1,294 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
+
+from sys import float_info
+
+
+class TestUsPayslip2021(TestUsPayslip):
+ # FUTA Constants
+ FUTA_RATE_NORMAL = 0.6
+ FUTA_RATE_BASIC = 6.0
+ FUTA_RATE_EXEMPT = 0.0
+
+ # Wage caps
+ FICA_SS_MAX_WAGE = 142800.0
+ FICA_M_MAX_WAGE = float_info.max
+ FICA_M_ADD_START_WAGE = 200000.0
+ FUTA_MAX_WAGE = 7000.0
+
+ # Rates
+ FICA_SS = 6.2 / -100.0
+ FICA_M = 1.45 / -100.0
+ FUTA = FUTA_RATE_NORMAL / -100.0
+ FICA_M_ADD = 0.9 / -100.0
+
+ ###
+ # 2021 Taxes and Rates
+ ###
+
+ def test_2021_taxes(self):
+ # salary is high so that second payslip runs over max
+ # social security salary
+ salary = 80000.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee, wage=salary)
+ self._log(contract.read())
+
+ self._log('2020 tax last slip')
+ payslip = self._createPayslip(employee, '2020-12-01', '2020-12-31')
+ self.assertEqual(payslip.contract_id, contract)
+ self._log(payslip.read())
+ process_payslip(payslip)
+
+ # Ensure amounts are there, they shouldn't be added in the next year...
+ cats = self._getCategories(payslip)
+ self.assertTrue(cats['ER_US_940_FUTA'], ' Value should be well above whatever was available that year!')
+
+ self._log('2021 tax first payslip:')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+ # Employee
+ self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], cats['BASIC'] * self.FICA_SS)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M'], cats['BASIC'] * self.FICA_M)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], 0.0)
+ # Employer
+ self.assertPayrollEqual(rules['ER_US_941_FICA_SS'], rules['EE_US_941_FICA_SS'])
+ self.assertPayrollEqual(rules['ER_US_941_FICA_M'], rules['EE_US_941_FICA_M'])
+ self.assertTrue(cats['ER_US_940_FUTA'], self.FUTA_MAX_WAGE * self.FUTA)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have reached Medicare Additional (employee only)
+ remaining_ss_wages = self.FICA_SS_MAX_WAGE - salary if (self.FICA_SS_MAX_WAGE - 2 * salary < salary) else salary
+ remaining_m_wages = self.FICA_M_MAX_WAGE - salary if (self.FICA_M_MAX_WAGE - 2 * salary < salary) else salary
+
+ self._log('2021 tax second payslip:')
+ payslip = self._createPayslip(employee, '2021-02-01', '2021-02-28')
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], remaining_ss_wages * self.FICA_SS)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M'], remaining_m_wages * self.FICA_M)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], 0.0)
+ self.assertPayrollEqual(cats['ER_US_940_FUTA'], 0.0)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have reached Medicare Additional (employee only)
+ self._log('2021 tax third payslip:')
+ payslip = self._createPayslip(employee, '2021-03-01', '2021-03-31')
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], (self.FICA_M_ADD_START_WAGE - (salary * 2)) * self.FICA_M_ADD) # aka 40k
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have all salary as Medicare Additional
+
+ self._log('2021 tax fourth payslip:')
+ payslip = self._createPayslip(employee, '2021-04-01', '2021-04-30')
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], salary * self.FICA_M_ADD)
+
+ process_payslip(payslip)
+
+ def test_2021_taxes_with_external(self):
+ # social security salary
+ salary = self.FICA_M_ADD_START_WAGE
+ external_wages = 6000.0
+
+ employee = self._createEmployee()
+
+ self._createContract(employee, wage=salary, external_wages=external_wages)
+
+ self._log('2021 tax first payslip:')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], (self.FICA_SS_MAX_WAGE - external_wages) * self.FICA_SS)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M'], salary * self.FICA_M)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], external_wages * self.FICA_M_ADD)
+ self.assertPayrollEqual(rules['ER_US_941_FICA_SS'], rules['EE_US_941_FICA_SS'])
+ self.assertPayrollEqual(rules['ER_US_941_FICA_M'], rules['EE_US_941_FICA_M'])
+ self.assertPayrollEqual(cats['ER_US_940_FUTA'], (self.FUTA_MAX_WAGE - external_wages) * self.FUTA)
+
+ def test_2021_taxes_with_full_futa(self):
+ futa_rate = self.FUTA_RATE_BASIC / -100.0
+ # social security salary
+ salary = self.FICA_M_ADD_START_WAGE
+
+ employee = self._createEmployee()
+
+ self._createContract(employee, wage=salary, fed_940_type=USHRContract.FUTA_TYPE_BASIC)
+
+ self._log('2021 tax first payslip:')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ rules = self._getRules(payslip)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_SS'], self.FICA_SS_MAX_WAGE * self.FICA_SS)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M'], salary * self.FICA_M)
+ self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], 0.0 * self.FICA_M_ADD)
+ self.assertPayrollEqual(rules['ER_US_941_FICA_SS'], rules['EE_US_941_FICA_SS'])
+ self.assertPayrollEqual(rules['ER_US_941_FICA_M'], rules['EE_US_941_FICA_M'])
+ self.assertPayrollEqual(cats['ER_US_940_FUTA'], self.FUTA_MAX_WAGE * futa_rate)
+
+ def test_2021_taxes_with_futa_exempt(self):
+ futa_rate = self.FUTA_RATE_EXEMPT / -100.0 # because of exemption
+
+ # social security salary
+ salary = self.FICA_M_ADD_START_WAGE
+ employee = self._createEmployee()
+ self._createContract(employee, wage=salary, fed_940_type=USHRContract.FUTA_TYPE_EXEMPT)
+ self._log('2021 tax first payslip:')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['ER_US_940_FUTA'], 0.0)
+
+ def test_2021_taxes_with_fica_exempt(self):
+ salary = 6000.0
+ schedule_pay = 'bi-weekly'
+ employee = self._createEmployee()
+ contract = self._createContract(employee, wage=salary, schedule_pay=schedule_pay)
+ contract.us_payroll_config_id.fed_941_fica_exempt = True
+
+ self._log('2021 tax w4 exempt payslip:')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_941_FICA'], 0.0)
+ self.assertPayrollEqual(cats['ER_US_941_FICA'], 0.0)
+
+ """
+ For Federal Income Tax Withholding, we are utilizing the calculations from the new IRS Excel calculator.
+ Given that you CAN round, we will round to compare even though we will calculate as close to the penny as possible
+ with the wage * computed_percent method.
+ """
+
+ def _run_test_fit(self,
+ wage=0.0,
+ schedule_pay='monthly',
+ filing_status='single',
+ dependent_credit=0.0,
+ other_income=0.0,
+ deductions=0.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=False,
+ expected_standard=0.0,
+ expected_higher=0.0,
+ ):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ schedule_pay=schedule_pay,
+ fed_941_fit_w4_is_nonresident_alien=is_nonresident_alien,
+ fed_941_fit_w4_filing_status=filing_status,
+ fed_941_fit_w4_multiple_jobs_higher=False,
+ fed_941_fit_w4_dependent_credit=dependent_credit,
+ fed_941_fit_w4_other_income=other_income,
+ fed_941_fit_w4_deductions=deductions,
+ fed_941_fit_w4_additional_withholding=additional_withholding,
+ )
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(round(cats.get('EE_US_941_FIT', 0.0)), -expected_standard)
+
+ contract.us_payroll_config_id.fed_941_fit_w4_multiple_jobs_higher = True
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(round(cats.get('EE_US_941_FIT', 0.0)), -expected_higher)
+ return payslip
+
+ def test_2021_fed_income_withholding_single(self):
+ _ = self._run_test_fit(
+ wage=6000.0,
+ schedule_pay='monthly',
+ filing_status='single',
+ dependent_credit=100.0,
+ other_income=200.0,
+ deductions=300.0,
+ additional_withholding=400.0,
+ is_nonresident_alien=False,
+ expected_standard=1125.0,
+ expected_higher=1455.0,
+ )
+
+ def test_2021_fed_income_withholding_married_as_single(self):
+ # This is "Head of Household" though the field name is the same for historical reasons.
+ _ = self._run_test_fit(
+ wage=500.0,
+ schedule_pay='weekly',
+ filing_status='married_as_single',
+ dependent_credit=20.0,
+ other_income=30.0,
+ deductions=40.0,
+ additional_withholding=10.0,
+ is_nonresident_alien=False,
+ expected_standard=23.0,
+ expected_higher=45.0,
+ )
+
+ def test_2021_fed_income_withholding_married(self):
+ _ = self._run_test_fit(
+ wage=14000.00,
+ schedule_pay='bi-weekly',
+ filing_status='married',
+ dependent_credit=2500.0,
+ other_income=1200.0,
+ deductions=1000.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=False,
+ expected_standard=2603.0,
+ expected_higher=3687.0,
+ )
+
+ def test_2021_fed_income_withholding_nonresident_alien(self):
+ # Monthly NRA additional wage is 1033.30
+ # Wage input on IRS Form entered as (3500+1033.30)=4533.30, not 3500.0
+ _ = self._run_test_fit(
+ wage=3500.00,
+ schedule_pay='monthly',
+ filing_status='married',
+ dependent_credit=340.0,
+ other_income=0.0,
+ deductions=0.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=True,
+ expected_standard=233.0,
+ expected_higher=387.0,
+ )
+
+ def test_2021_taxes_with_w4_exempt(self):
+ _ = self._run_test_fit(
+ wage=3500.00,
+ schedule_pay='monthly',
+ filing_status='', # Exempt
+ dependent_credit=340.0,
+ other_income=0.0,
+ deductions=0.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=True,
+ expected_standard=0.0,
+ expected_higher=0.0,
+ )
diff --git a/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2021.py
new file mode 100755
index 00000000..3fe9a189
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2021.py
@@ -0,0 +1,37 @@
+# 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 TestUsRIPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ RI_UNEMP_MAX_WAGE = 24000.0
+ RI_UNEMP = 1.06
+ # Calculation based on example http://www.tax.ri.gov/forms/2021/Withholding/2021%20Withhholding%20Tax%20Booklet.pdf
+
+ def _test_sit(self, wage, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('RI'),
+ ri_w4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('RI', self.RI_UNEMP, date(2021, 1, 1), wage_base=self.RI_UNEMP_MAX_WAGE)
+ self._test_sit(2195, 1, 0, False, 'weekly', date(2021, 1, 1), 90.62)
+ self._test_sit(1800, 2, 10, True, 'weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(10000, 1, 0, False, 'bi-weekly', date(2021, 1, 1), 501.75)
+ self._test_sit(18000, 2, 0, False, 'monthly', date(2021, 1, 1), 857.48)
+ self._test_sit(18000, 2, 10, False, 'monthly', date(2021, 1, 1), 867.47)
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
deleted file mode 100644
index 793f84c4..00000000
--- a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-
-
-class TestUsSCPayslip(TestUsPayslip):
-
- # Taxes and Rates
- SC_UNEMP_MAX_WAGE = 14000.0
- US_SC_UNEMP = -1.09 / 100
- US_SC_exemption_amount = 2510.00
-
- def test_2019_taxes_weekly(self):
- # We will hand calculate the amount to test for state withholding.
- schedule_pay = 'weekly'
- salary = 50000.00 # Employee is paid 50000 per week to be in top tax bracket
- allowances = 2
- # Calculate annual wages
- annual = 50000 * 52.0
- # From our annual we deduct personal exemption amounts.
- # We deduct 2510.00 per exemption. Since we have two exemptions:
- personal_exemption = self.US_SC_exemption_amount * allowances # 5020.0
- # From annual, we will also deduct a standard_deduction of 3470.00 or .1 of salary, which ever
- # is small -> if 1 or more exemptions, else 0
- standard_deduction = 3470.00
- taxable_income = annual - personal_exemption - standard_deduction # 2591510.0
- # We then calculate the amounts off the SC tax pdf tables.
- # 2591478.0 is in the highest bracket
- test_amt = (taxable_income * (7.0 / 100.0)) - 467.95
- test_amt = 180935.51
- # Make it per period then negative
- test_amt = (test_amt / 52.0) # Divided by 52 since it is weekly.
- # test_amt = 3479.52
- test_amt = -test_amt
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('SC'),
- state_income_tax_exempt=False,
- sc_w4_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 South Carolina tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollAlmostEqual(cats['ER_US_SUTA'], self.SC_UNEMP_MAX_WAGE * self.US_SC_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], test_amt)
-
- process_payslip(payslip)
-
- remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - annual if (annual < self.SC_UNEMP_MAX_WAGE) \
- else 0.00
-
- self._log('2019 South Carolina tax second payslip:')
-
- payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertEqual(0.0, remaining_SC_UNEMP_wages)
- self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_SC_UNEMP_wages * self.US_SC_UNEMP)
-
- def test_2019_taxes_filing_status(self):
- salary = 20000.00 # Wages per pay period
- schedule_pay = 'monthly'
- annual = salary * 12
- allowances = 1
- # Hand Calculations
- personal_exemption = 2510.00
- standard_deduction = min(3470.00, .1 * annual) # 3470.0 but min is shown for the process
- taxable = annual - personal_exemption - standard_deduction
- # taxable = 234020
- test_amt = ((taxable) * (7.0 / 100.0)) - 467.95 # 15991.850000000002
- test_amt = test_amt / 12.0 # Put it into monthly -> 1332.654166666667
- # Make it negative
- test_amt = -test_amt
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('SC'),
- state_income_tax_exempt=False,
- sc_w4_sit_allowances=allowances,
- schedule_pay=schedule_pay)
-
- self._log('2019 South Carolina tax first payslip: ')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], self.SC_UNEMP_MAX_WAGE * self.US_SC_UNEMP)
- self.assertPayrollAlmostEqual(cats['EE_US_SIT'], test_amt)
-
- process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
index 170c3bf5..086af309 100644
--- a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
@@ -6,11 +6,11 @@ from .common import TestUsPayslip
class TestUsSCPayslip(TestUsPayslip):
###
- # 2020 Taxes and Rates
+ # 2021 Taxes and Rates
###
SC_UNEMP_MAX_WAGE = 14000.0
SC_UNEMP = 0.55
- # Calculation based on https://dor.sc.gov/forms-site/Forms/WH1603F_2020.pdf
+ # Calculation based on https://dor.sc.gov/forms-site/Forms/WH1603F_2021.pdf
def _test_sit(self, wage, additional_withholding, exempt, allowances, schedule_pay, date_start, expected_withholding):
employee = self._createEmployee()
@@ -28,9 +28,9 @@ class TestUsSCPayslip(TestUsPayslip):
self._log('Computed period tax: ' + str(expected_withholding))
self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
- def test_2020_taxes_example(self):
- self._test_er_suta('SC', self.SC_UNEMP, date(2020, 1, 1), wage_base=self.SC_UNEMP_MAX_WAGE)
- self._test_sit(750.0, 0.0, False, 3.0, 'weekly', date(2020, 1, 1), 28.73)
- self._test_sit(800.0, 0.0, True, 0.0, 'weekly', date(2020, 1, 1), 0.00)
- self._test_sit(9000.0, 0.0, False, 0.0, 'monthly', date(2020, 1, 1), 594.61)
- self._test_sit(5000.0, 10.0, False, 2.0, 'semi-monthly', date(2020, 1, 1), 316.06)
+ def test_2021_taxes_example(self):
+ self._test_er_suta('SC', self.SC_UNEMP, date(2021, 1, 1), wage_base=self.SC_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 0.0, False, 3.0, 'weekly', date(2021, 1, 1), 28.73)
+ self._test_sit(800.0, 0.0, True, 0.0, 'weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(9000.0, 0.0, False, 0.0, 'monthly', date(2021, 1, 1), 594.61)
+ self._test_sit(5000.0, 10.0, False, 2.0, 'semi-monthly', date(2021, 1, 1), 316.06)
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2021.py
new file mode 100644
index 00000000..3fb11459
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsSCPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ SC_UNEMP_MAX_WAGE = 14000.0
+ SC_UNEMP = 0.55
+ # Calculation based on https://dor.sc.gov/forms-site/Forms/WH1603F_2021.pdf
+
+ def _test_sit(self, wage, 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('SC'),
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ sc_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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('SC', self.SC_UNEMP, date(2021, 1, 1), wage_base=self.SC_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 0.0, False, 3.0, 'weekly', date(2021, 1, 1), 27.57)
+ self._test_sit(800.0, 0.0, True, 0.0, 'weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(9000.0, 0.0, False, 0.0, 'monthly', date(2021, 1, 1), 591.44)
+ self._test_sit(5000.0, 10.0, False, 2.0, 'semi-monthly', date(2021, 1, 1), 312.90)
diff --git a/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2021.py
new file mode 100644
index 00000000..577a82ec
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2021.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsSDPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ SD_UNEMP_MAX_WAGE = 15000.00
+ SD_UNEMP = 1.75
+
+ def test_2021_taxes(self):
+ self._test_er_suta('SD', self.SD_UNEMP, date(2021, 1, 1), wage_base=self.SD_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2021.py
new file mode 100644
index 00000000..3a3080e6
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2021.py
@@ -0,0 +1,14 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsTNPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ # todo: 2021 rates not published yet
+ TN_UNEMP_MAX_WAGE = 7000.00
+ TN_UNEMP = 2.7
+
+ def test_2021_taxes(self):
+ self._test_er_suta('TN', self.TN_UNEMP, date(2021, 1, 1), wage_base=self.TN_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
deleted file mode 100755
index 15e657ae..00000000
--- a/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from .common import TestUsPayslip, process_payslip
-from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
-
-class TestUsTXPayslip(TestUsPayslip):
- ###
- # 2019 Taxes and Rates
- ###
- TX_UNEMP_MAX_WAGE = 9000.0
- TX_UNEMP = -2.7 / 100.0
- TX_OA = 0.0
- TX_ETIA = -0.1 / 100.0
-
- def test_2019_taxes(self):
- salary = 5000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('TX'),
- )
-
- self._log('2019 Texas tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
-
- self.assertPayrollEqual(rules['ER_US_TX_SUTA'], salary * self.TX_UNEMP)
- self.assertPayrollEqual(rules['ER_US_TX_SUTA_OA'], salary * self.TX_OA)
- self.assertPayrollEqual(rules['ER_US_TX_SUTA_ETIA'], salary * self.TX_ETIA)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_tx_unemp_wages = self.TX_UNEMP_MAX_WAGE - salary if (self.TX_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Texas tax second payslip:')
- payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
-
- self.assertPayrollEqual(rules['ER_US_TX_SUTA'], remaining_tx_unemp_wages * self.TX_UNEMP)
-
- def test_2019_taxes_with_external(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('TX'),
- external_wages=external_wages,
- )
-
- self._log('2019 Texas_external tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
-
- expected_wage = self.TX_UNEMP_MAX_WAGE - external_wages
- self.assertPayrollEqual(rules['ER_US_TX_SUTA'], expected_wage * self.TX_UNEMP)
- self.assertPayrollEqual(rules['ER_US_TX_SUTA_OA'], expected_wage * self.TX_OA)
- self.assertPayrollEqual(rules['ER_US_TX_SUTA_ETIA'], expected_wage * self.TX_ETIA)
-
- def test_2019_taxes_with_state_exempt(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('TX'),
- external_wages=external_wages,
- futa_type=USHRContract.FUTA_TYPE_BASIC)
-
- self._log('2019 Texas_external tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- rules = self._getRules(payslip)
-
- self.assertPayrollEqual(rules.get('ER_US_TX_SUTA', 0.0), 0.0)
- self.assertPayrollEqual(rules.get('ER_US_TX_SUTA_OA', 0.0), 0.0)
- self.assertPayrollEqual(rules.get('ER_US_TX_SUTA_ETIA', 0.0), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2021.py
new file mode 100755
index 00000000..14d8066f
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2021.py
@@ -0,0 +1,17 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+class TestUsTXPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ TX_UNEMP_MAX_WAGE = 9000.0
+ TX_UNEMP = 2.7
+ TX_OA = 0.0
+ TX_ETIA = 0.1
+
+ def test_2021_taxes(self):
+ combined_rate = self.TX_UNEMP + self.TX_OA + self.TX_ETIA
+ self._test_er_suta('TX', combined_rate, date(2021, 1, 1), wage_base=self.TX_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2021.py
new file mode 100755
index 00000000..0b23b462
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsUTPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ UT_UNEMP_MAX_WAGE = 38900.0
+ UT_UNEMP = 0.1052
+ # Calculation based on example https://tax.utah.gov/forms/pubs/pub-14.pdf
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('UT'),
+ ut_w4_sit_filing_status=filing_status,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2021_taxes_example(self):
+ self._test_er_suta('UT', self.UT_UNEMP, date(2021, 1, 1), wage_base=self.UT_UNEMP_MAX_WAGE)
+ self._test_sit(400, 'single', 0, 'weekly', date(2021, 1, 1), 16.00)
+ self._test_sit(1000, 'single', 0, 'bi-weekly', date(2021, 1, 1), 45.00)
+ self._test_sit(855, 'married', 0, 'semi-monthly', date(2021, 1, 1), 16.00)
+ self._test_sit(2500, 'married', 0, 'monthly', date(2021, 1, 1), 81.00)
+ self._test_sit(8000, 'head_household', 10, 'quarterly', date(2021, 1, 1), 397.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
deleted file mode 100644
index b8f14393..00000000
--- a/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# 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
-from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
-
-
-class TestUsVaPayslip(TestUsPayslip):
- ###
- # Taxes and Rates
- ###
- VA_UNEMP_MAX_WAGE = 8000.0
- VA_UNEMP = 2.51
- VA_SIT_DEDUCTION = 4500.0
- VA_SIT_EXEMPTION = 930.0
- VA_SIT_OTHER_EXEMPTION = 800.0
-
- def test_2019_taxes(self):
- salary = 5000.0
-
- # For formula from https://www.tax.virginia.gov/withholding-calculator
- """
- Key
- G = Gross Pay for Pay Period P = Pay periods per year
- A = Annualized gross pay E1 = Personal and Dependent Exemptions
- T = Annualized taxable income E2 = Age 65 and Over & Blind Exemptions
- WH = Tax to be withheld for pay period W = Annualized tax to be withheld
- G x P - [$3000+ (E1 x 930) + (E2 x 800)] = T
- Calculate W as follows:
- If T is: W is:
- Not over $3,000 2% of T
- Over But Not Over Then
- $3,000 $5,000 $60 + (3% of excess over $3,000)
- $5,000 $17,000 $120 + (5% of excess over $5,000)
- $17,000 $720 + (5.75% of excess over $17,000)
- W / P = WH
- """
- e1 = 2
- e2 = 0
- t = salary * 12 - (self.VA_SIT_DEDUCTION + (e1 * self.VA_SIT_EXEMPTION) + (e2 * self.VA_SIT_OTHER_EXEMPTION))
-
- if t <= 3000:
- w = 0.02 * t
- elif t <= 5000:
- w = 60 + (0.03 * (t - 3000))
- elif t <= 17000:
- w = 120 + (0.05 * (t - 5000))
- else:
- w = 720 + (0.0575 * (t - 17000))
-
- wh = w / 12
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('VA'),
- va_va4_sit_exemptions=e1,
- va_va4_sit_other_exemptions=e2
- )
-
- # tax rates
- va_unemp = self.VA_UNEMP / -100.0
-
- self._log('2019 Virginia tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], salary * va_unemp)
- self.assertPayrollEqual(cats['EE_US_SIT'], -wh)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_va_unemp_wages = self.VA_UNEMP_MAX_WAGE - salary if (self.VA_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Virginia 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_va_unemp_wages * va_unemp)
-
- def test_2019_taxes_with_external(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('VA'),
- external_wages=external_wages,
- )
-
- # tax rates
- va_unemp = self.VA_UNEMP / -100.0
-
- self._log('2019 Virginia_external tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['ER_US_SUTA'], (self.VA_UNEMP_MAX_WAGE - external_wages) * va_unemp)
-
- def test_2019_taxes_with_state_exempt(self):
- salary = 5000.0
- external_wages = 6000.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('VA'),
- external_wages=external_wages,
- futa_type=USHRContract.FUTA_TYPE_BASIC)
-
- # tax rates
- self._log('2019 Virginia exempt tax first payslip:')
- payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py
new file mode 100644
index 00000000..dc81a3ff
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py
@@ -0,0 +1,116 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsVaPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ VA_UNEMP_MAX_WAGE = 8000.0
+ VA_UNEMP = 2.5
+ VA_SIT_DEDUCTION = 4500.0
+ VA_SIT_EXEMPTION = 930.0
+ VA_SIT_OTHER_EXEMPTION = 800.0
+
+ def _run_test_sit(self,
+ wage=0.0,
+ schedule_pay='monthly',
+ filing_status='single',
+ dependent_credit=0.0,
+ other_income=0.0,
+ deductions=0.0,
+ additional_withholding=0.0,
+ is_nonresident_alien=False,
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=0,
+ va_va4_sit_other_exemptions=0,
+ expected=0.0,
+ ):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ schedule_pay=schedule_pay,
+ fed_941_fit_w4_is_nonresident_alien=is_nonresident_alien,
+ fed_941_fit_w4_filing_status=filing_status,
+ fed_941_fit_w4_multiple_jobs_higher=False,
+ fed_941_fit_w4_dependent_credit=dependent_credit,
+ fed_941_fit_w4_other_income=other_income,
+ fed_941_fit_w4_deductions=deductions,
+ fed_941_fit_w4_additional_withholding=additional_withholding,
+ state_income_tax_exempt=state_income_tax_exempt,
+ state_income_tax_additional_withholding=state_income_tax_additional_withholding,
+ va_va4_sit_exemptions=va_va4_sit_exemptions,
+ va_va4_sit_other_exemptions=va_va4_sit_other_exemptions,
+ state_id=self.get_us_state('VA'),
+ )
+ payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ # Instead of PayrollEqual after initial first round of testing.
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected)
+ return payslip
+
+ def test_2020_taxes(self):
+ self._test_er_suta('VA', self.VA_UNEMP, date(2020, 1, 1), wage_base=self.VA_UNEMP_MAX_WAGE)
+
+ salary = 5000.0
+
+ # For formula from https://www.tax.virginia.gov/withholding-calculator
+ e1 = 2
+ e2 = 0
+ t = salary * 12 - (self.VA_SIT_DEDUCTION + (e1 * self.VA_SIT_EXEMPTION) + (e2 * self.VA_SIT_OTHER_EXEMPTION))
+
+ if t <= 3000:
+ w = 0.02 * t
+ elif t <= 5000:
+ w = 60 + (0.03 * (t - 3000))
+ elif t <= 17000:
+ w = 120 + (0.05 * (t - 5000))
+ else:
+ w = 720 + (0.0575 * (t - 17000))
+
+ wh = w / 12
+
+ self._run_test_sit(wage=salary,
+ schedule_pay='monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=e1,
+ va_va4_sit_other_exemptions=e2,
+ expected=wh,)
+ self.assertPayrollEqual(wh, 235.57) # To test against calculator
+
+ # Below expected comes from the calculator linked above
+ self._run_test_sit(wage=450.0,
+ schedule_pay='weekly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=3,
+ va_va4_sit_other_exemptions=1,
+ expected=12.22,)
+ self._run_test_sit(wage=2500.0,
+ schedule_pay='bi-weekly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=1,
+ va_va4_sit_other_exemptions=0,
+ expected=121.84,)
+ self._run_test_sit(wage=10000.0,
+ schedule_pay='semi-monthly',
+ state_income_tax_exempt=False,
+ state_income_tax_additional_withholding=100.0,
+ va_va4_sit_exemptions=0,
+ va_va4_sit_other_exemptions=1,
+ expected=651.57,)
+
+ # Test exempt
+ self._run_test_sit(wage=2400.0,
+ schedule_pay='monthly',
+ state_income_tax_exempt=True,
+ state_income_tax_additional_withholding=0.0,
+ va_va4_sit_exemptions=1,
+ va_va4_sit_other_exemptions=1,
+ expected=0.0,)
diff --git a/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2021.py
new file mode 100755
index 00000000..67fb7a99
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2021.py
@@ -0,0 +1,37 @@
+# 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 TestUsVTPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ VT_UNEMP_MAX_WAGE = 14100.0
+ VT_UNEMP = 1.0
+ # Calculation based on example https://tax.vermont.gov/sites/tax/files/documents/WithholdingInstructions.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('VT'),
+ vt_w4vt_sit_filing_status=filing_status,
+ vt_w4vt_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('VT', self.VT_UNEMP, date(2021, 1, 1), wage_base=self.VT_UNEMP_MAX_WAGE)
+ self._test_sit(1800, 'married', 2, 0, False, 'weekly', date(2021, 1, 1), 52.79)
+ self._test_sit(1800, 'married', 2, 10, False, 'weekly', date(2021, 1, 1), 62.79)
+ self._test_sit(1000, 'single', 1, 0, True, 'weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(8000, 'single', 1, 10, False, 'bi-weekly', date(2021, 1, 1), 506.52)
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
index e02e55a0..2aeb940a 100755
--- 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
@@ -22,6 +22,7 @@ class TestUsWAPayslip(TestUsPayslip):
# 'rate': 0.1261,
# 'rate_emp_withhold': 0.05575,
# })
+ # todo: figure out how the two rates below corralate with ee and er rates above
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.rule.parameter'].create({
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_2021.py
similarity index 61%
rename from l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
rename to l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2021.py
index a0fda5c9..180494e0 100755
--- a/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
+++ b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2021.py
@@ -8,8 +8,9 @@ class TestUsWAPayslip(TestUsPayslip):
###
# Taxes and Rates
###
- WA_UNEMP_MAX_WAGE = 49800.0
- WA_UNEMP_RATE = 1.18
+ WA_UNEMP_MAX_WAGE = 56500.00
+ WA_UNEMP_RATE = 2.16
+ WA_FML_MAX_WAGE = 142800.00
WA_FML_RATE = 0.4
WA_FML_RATE_EE = 66.33
WA_FML_RATE_ER = 33.67
@@ -27,7 +28,7 @@ class TestUsWAPayslip(TestUsPayslip):
'name': 'Test LNI EE',
'code': 'test_lni_ee',
'parameter_version_ids': [(0, 0, {
- 'date_from': date(2019, 1, 1),
+ 'date_from': date(2021, 1, 1),
'parameter_value': str(self.test_ee_lni * 100),
})],
})
@@ -35,13 +36,15 @@ class TestUsWAPayslip(TestUsPayslip):
'name': 'Test LNI ER',
'code': 'test_lni_er',
'parameter_version_ids': [(0, 0, {
- 'date_from': date(2019, 1, 1),
+ 'date_from': date(2021, 1, 1),
'parameter_value': str(self.test_er_lni * 100),
})],
})
- def test_2019_taxes(self):
- salary = 25000.0
+ def test_2021_taxes(self):
+ self._test_er_suta('WA', self.WA_UNEMP_RATE, date(2021, 1, 1), wage_base=self.WA_UNEMP_MAX_WAGE)
+
+ salary = (self.WA_FML_MAX_WAGE / 2.0) + 1000.0
employee = self._createEmployee()
@@ -54,39 +57,34 @@ class TestUsWAPayslip(TestUsPayslip):
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')
+ # Non SUTA
+ self._log('2021 Washington tax first payslip:')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
hours_in_period = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100').number_of_hours
- self.assertAlmostEqual(hours_in_period, 184) # only asserted to test algorithm
+ self.assertPayrollAlmostEqual(hours_in_period, 169) # 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.assertPayrollAlmostEqual(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))
# 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')
+ # Second payslip
+ remaining_wage = self.WA_FML_MAX_WAGE - salary
+ payslip = self._createPayslip(employee, '2021-03-01', '2021-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)
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_wa_unemp_wages * wa_unemp)
+ # Third payslip
+ payslip = self._createPayslip(employee, '2021-04-01', '2021-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/tests/test_us_wi_wisconsin_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2021.py
new file mode 100755
index 00000000..59a9e814
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2021.py
@@ -0,0 +1,39 @@
+# 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 TestUsWIPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ WI_UNEMP_MAX_WAGE = 14000.0
+ WI_UNEMP = 3.05
+ # Calculation based on example https://www.revenue.wi.gov/DOR%20Publications/pb166.pdf
+
+ def _test_sit(self, wage, filing_status, exemption, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('WI'),
+ wi_wt4_sit_filing_status=filing_status,
+ wi_wt4_sit_exemptions=exemption,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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):
+ self._test_er_suta('WI', self.WI_UNEMP, date(2021, 1, 1), wage_base=self.WI_UNEMP_MAX_WAGE)
+ self._test_sit(300, 'single', 1, 0, False, 'weekly', date(2021, 1, 1), 7.21)
+ self._test_sit(700, 'married', 3, 0, False, 'bi-weekly', date(2021, 1, 1), 13.35)
+ self._test_sit(7000, 'single', 1, 10, True, 'bi-weekly', date(2021, 1, 1), 0.00)
+ self._test_sit(10000, 'married', 3, 10, False, 'bi-weekly', date(2021, 1, 1), 633.65)
+ # ((48000 - 26227) * (7.0224 /100) + 1073.55 - 44) / 12
+ self._test_sit(4000, 'single', 2, 0, False, 'monthly', date(2021, 1, 1), 213.21)
diff --git a/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2021.py
new file mode 100755
index 00000000..ec16d379
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2021.py
@@ -0,0 +1,36 @@
+# 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 TestUsWVPayslip(TestUsPayslip):
+ ###
+ # 2021 Taxes and Rates
+ ###
+ WV_UNEMP_MAX_WAGE = 12000.0
+ WV_UNEMP = 2.7
+ # Calculation based on example https://tax.wv.gov/Documents/TaxForms/it100.1a.pdf
+
+ def _test_sit(self, wage, filing_status, exemption, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('WV'),
+ wv_it104_sit_filing_status=filing_status,
+ wv_it104_sit_exemptions=exemption,
+ 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_2021_taxes_example(self):
+ self._test_er_suta('WV', self.WV_UNEMP, date(2021, 1, 1), wage_base=self.WV_UNEMP_MAX_WAGE)
+ self._test_sit(1250, 'married', 2, 0, 'semi-monthly', date(2021, 1, 1), 44.00)
+ self._test_sit(1300, 'single', 1, 0, 'bi-weekly', date(2021, 1, 1), 46.00)
+ self._test_sit(1300, 'single', 1, 10, 'bi-weekly', date(2021, 1, 1), 56.00)
+ self._test_sit(15000, 'single', 2, 0, 'monthly', date(2021, 1, 1), 860.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
deleted file mode 100644
index a8fa3df8..00000000
--- a/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# 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 TestUsWYPayslip(TestUsPayslip):
-
- # TAXES AND RATES
- WY_UNEMP_MAX_WAGE = 25400
- WY_UNEMP = -2.10 / 100.0
-
- def test_2019_taxes(self):
- salary = 15000.00
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('WY'))
-
- self._log('2019 Wyoming tax first payslip:')
- 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.WY_UNEMP)
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
-
- remaining_wy_unemp_wages = self.WY_UNEMP_MAX_WAGE - salary if (self.WY_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
-
- self._log('2019 Wyoming 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_wy_unemp_wages * self.WY_UNEMP)
-
- def test_2019_taxes_with_external(self):
- # Wage is the cap itself, 25400
- # so salary is equal to self.WY_UNEMP
- salary = 25400
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('WY'))
-
- self._log('2019 Wyoming External tax first payslip:')
- 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.WY_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2021.py
new file mode 100644
index 00000000..8d651a9a
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2021.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsWYPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ WY_UNEMP_MAX_WAGE = 27300.00
+ WY_UNEMP = 8.5
+
+ def test_2021_taxes(self):
+ self._test_er_suta('WY', self.WY_UNEMP, date(2021, 1, 1), wage_base=self.WY_UNEMP_MAX_WAGE)
From b876c775ecb3f31b2bf196db2a314fadc74c3dd8 Mon Sep 17 00:00:00 2001
From: Jared Self
Date: Thu, 21 Jan 2021 10:40:53 -0700
Subject: [PATCH 05/12] [FIX] l10n_us_hr_payroll: Missing Maine
---
l10n_us_hr_payroll/data/state/me_maine.xml | 38 ++++++++++++++++
l10n_us_hr_payroll/models/state/me_maine.py | 10 ++++-
.../tests/test_us_me_maine_payslip_2021.py | 45 +++++++++++++++++++
3 files changed, 92 insertions(+), 1 deletion(-)
create mode 100644 l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2021.py
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)
From 824ac3860d88c93288d33f5da60b98b8345b58db Mon Sep 17 00:00:00 2001
From: Jared Self
Date: Mon, 25 Jan 2021 09:26:10 -0700
Subject: [PATCH 06/12] [FIX] l10n_us_hr_payroll: Removed Debug From Maine Test
---
l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2021.py | 3 ---
1 file changed, 3 deletions(-)
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
index 5bd17991..2324b278 100644
--- 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
@@ -33,9 +33,6 @@ class TestUsMEPayslip(TestUsPayslip):
# 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)
From 63131f65cbda27ddcc7d6a07e8e0aecda35402c8 Mon Sep 17 00:00:00 2001
From: Jared Self
Date: Tue, 26 Jan 2021 09:22:48 -0700
Subject: [PATCH 07/12] [FIX] l10n_us_hr_payroll: Virginia 2021 test had 2020
dates
---
.../tests/test_us_va_virginia_payslip_2021.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py
index dc81a3ff..6a789c06 100644
--- a/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py
+++ b/l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2021.py
@@ -46,15 +46,15 @@ class TestUsVaPayslip(TestUsPayslip):
va_va4_sit_other_exemptions=va_va4_sit_other_exemptions,
state_id=self.get_us_state('VA'),
)
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
payslip.compute_sheet()
cats = self._getCategories(payslip)
# Instead of PayrollEqual after initial first round of testing.
self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected)
return payslip
- def test_2020_taxes(self):
- self._test_er_suta('VA', self.VA_UNEMP, date(2020, 1, 1), wage_base=self.VA_UNEMP_MAX_WAGE)
+ def test_2021_taxes(self):
+ self._test_er_suta('VA', self.VA_UNEMP, date(2021, 1, 1), wage_base=self.VA_UNEMP_MAX_WAGE)
salary = 5000.0
From 4bce6f866182750f3c57889a9cf07799b7f61d01 Mon Sep 17 00:00:00 2001
From: Jared Self
Date: Thu, 28 Jan 2021 16:41:55 -0700
Subject: [PATCH 08/12] [FIX] l10n_us_hr_payroll: Fix parameters dates, python
errors, and result values
Fixed parameter dates in Hawaii, Python errors in wa_washington.xml, some results in Alabama and Iowa, and removing 2019 info in test_us_payslip_2020.py
---
l10n_us_hr_payroll/data/state/hi_hawaii.xml | 4 ++--
l10n_us_hr_payroll/data/state/wa_washington.xml | 6 ++----
.../tests/test_us_al_alabama_payslip_2021.py | 8 ++++----
.../tests/test_us_ia_iowa_payslip_2021.py | 6 +++---
l10n_us_hr_payroll/tests/test_us_payslip_2020.py | 10 ----------
5 files changed, 11 insertions(+), 23 deletions(-)
diff --git a/l10n_us_hr_payroll/data/state/hi_hawaii.xml b/l10n_us_hr_payroll/data/state/hi_hawaii.xml
index 470c5239..e5011794 100644
--- a/l10n_us_hr_payroll/data/state/hi_hawaii.xml
+++ b/l10n_us_hr_payroll/data/state/hi_hawaii.xml
@@ -33,10 +33,10 @@
-
+
5.2
-
+
diff --git a/l10n_us_hr_payroll/data/state/wa_washington.xml b/l10n_us_hr_payroll/data/state/wa_washington.xml
index 53a4e491..618c0682 100644
--- a/l10n_us_hr_payroll/data/state/wa_washington.xml
+++ b/l10n_us_hr_payroll/data/state/wa_washington.xml
@@ -202,8 +202,7 @@
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.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
code
- result, result_rate = worked_days.WORK100.number_of_hours,
- -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
+ result, result_rate = worked_days.WORK100.number_of_hours, -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
@@ -218,8 +217,7 @@
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.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
code
- result, result_rate = worked_days.WORK100.number_of_hours,
- -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
+ result, result_rate = worked_days.WORK100.number_of_hours, -payslip.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))
diff --git a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py
index 382f679a..56376094 100644
--- a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py
+++ b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2021.py
@@ -29,8 +29,8 @@ class TestUsALPayslip(TestUsPayslip):
def test_2021_taxes_example(self):
self._test_er_suta('AL', self.AL_UNEMP, date(2021, 1, 1), wage_base=self.AL_UNEMP_MAX_WAGE)
- self._test_sit(10000.0, 'S', False, 0.0, 1.0, 'weekly', date(2021, 1, 1), 349.08)
- self._test_sit(850.0, 'M', False, 0.0, 2.0, 'weekly', date(2021, 1, 1), 29.98)
- self._test_sit(5000.0, 'H', False, 0.0, 2.0, 'bi-weekly', date(2021, 1, 1), 191.15)
- self._test_sit(20000.0, 'MS', False, 2.0, 0, 'monthly', date(2021, 1, 1), 757.6)
+ self._test_sit(10000.0, 'S', False, 0.0, 1.0, 'weekly', date(2021, 1, 1), 349.37)
+ self._test_sit(850.0, 'M', False, 0.0, 2.0, 'weekly', date(2021, 1, 1), 30.00)
+ self._test_sit(5000.0, 'H', False, 0.0, 2.0, 'bi-weekly', date(2021, 1, 1), 191.33)
+ self._test_sit(20000.0, 'MS', False, 2.0, 0, 'monthly', date(2021, 1, 1), 758.86)
self._test_sit(5500.0, '', True, 2.0, 150, 'weekly', date(2021, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py
index df169186..396acfe4 100755
--- a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py
+++ b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2021.py
@@ -29,8 +29,8 @@ class TestUsIAPayslip(TestUsPayslip):
def test_2021_taxes_example(self):
self._test_er_suta('IA', self.IA_UNEMP, date(2021, 1, 1), wage_base=self.IA_UNEMP_MAX_WAGE)
- self._test_sit(2100.0, False, 0.0, 3.0, 'bi-weekly', date(2021, 1, 1), 79.13)
+ self._test_sit(2100.0, False, 0.0, 3.0, 'bi-weekly', date(2021, 1, 1), 79.31)
self._test_sit(3000.0, True, 10.0, 1.0, 'bi-weekly', date(2021, 1, 1), 0.00)
self._test_sit(300.0, False, 0.0, 1.0, 'weekly', date(2021, 1, 1), 6.14)
- self._test_sit(5000.0, False, 0.0, 1.0, 'monthly', date(2021, 1, 1), 217.60)
- self._test_sit(7500.0, False, 10.0, 2.0, 'semi-monthly', date(2021, 1, 1), 420.87)
+ self._test_sit(5000.0, False, 0.0, 1.0, 'monthly', date(2021, 1, 1), 218.06)
+ self._test_sit(7500.0, False, 10.0, 2.0, 'semi-monthly', date(2021, 1, 1), 421.71)
diff --git a/l10n_us_hr_payroll/tests/test_us_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_payslip_2020.py
index 2bc61b65..fd6eafd7 100644
--- a/l10n_us_hr_payroll/tests/test_us_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_payslip_2020.py
@@ -39,16 +39,6 @@ class TestUsPayslip2020(TestUsPayslip):
contract = self._createContract(employee, wage=salary)
self._log(contract.read())
- self._log('2019 tax last slip')
- payslip = self._createPayslip(employee, '2019-12-01', '2019-12-31')
- self.assertEqual(payslip.contract_id, contract)
- self._log(payslip.read())
- process_payslip(payslip)
-
- # Ensure amounts are there, they shouldn't be added in the next year...
- cats = self._getCategories(payslip)
- self.assertTrue(cats['ER_US_940_FUTA'], ' Value should be well above whatever was available that year!')
-
self._log('2020 tax first payslip:')
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
From 3c7e67805cfa2946f3c226a31ae512e4af747c96 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Mon, 22 Feb 2021 14:28:42 -0800
Subject: [PATCH 09/12] [FIX] l10n_us_hr_payroll: 2021 fixes found during
porting
---
l10n_us_hr_payroll/data/state/mo_missouri.xml | 4 ++--
l10n_us_hr_payroll/data/state/nj_newjersey.xml | 8 ++++----
l10n_us_hr_payroll/data/state/wa_washington.xml | 4 ++--
l10n_us_hr_payroll/models/state/me_maine.py | 6 ------
.../test_us_sc_south_carolina_payslip_2020.py | 16 ++++++++--------
.../tests/test_us_wa_washington_payslip_2020.py | 2 +-
.../tests/test_us_wa_washington_payslip_2021.py | 4 ++--
7 files changed, 19 insertions(+), 25 deletions(-)
diff --git a/l10n_us_hr_payroll/data/state/mo_missouri.xml b/l10n_us_hr_payroll/data/state/mo_missouri.xml
index 1e2cd863..cea9bb6c 100644
--- a/l10n_us_hr_payroll/data/state/mo_missouri.xml
+++ b/l10n_us_hr_payroll/data/state/mo_missouri.xml
@@ -13,10 +13,10 @@
-
+
11000.0
-
+
diff --git a/l10n_us_hr_payroll/data/state/nj_newjersey.xml b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
index c1b7e003..1fdafd96 100644
--- a/l10n_us_hr_payroll/data/state/nj_newjersey.xml
+++ b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
@@ -77,10 +77,10 @@
-
+
0.47
-
+
@@ -140,10 +140,10 @@
-
+
0.28
-
+
diff --git a/l10n_us_hr_payroll/data/state/wa_washington.xml b/l10n_us_hr_payroll/data/state/wa_washington.xml
index 618c0682..5da8acef 100644
--- a/l10n_us_hr_payroll/data/state/wa_washington.xml
+++ b/l10n_us_hr_payroll/data/state/wa_washington.xml
@@ -98,7 +98,7 @@
- 66.33
+ 63.33
@@ -118,7 +118,7 @@
- 33.67
+ 36.67
diff --git a/l10n_us_hr_payroll/models/state/me_maine.py b/l10n_us_hr_payroll/models/state/me_maine.py
index ad6b9851..7ce80c42 100644
--- a/l10n_us_hr_payroll/models/state/me_maine.py
+++ b/l10n_us_hr_payroll/models/state/me_maine.py
@@ -1,8 +1,6 @@
# 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):
@@ -15,23 +13,19 @@ def me_maine_state_income_withholding(payslip, categories, worked_days, inputs):
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()
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
index 086af309..170c3bf5 100644
--- a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
@@ -6,11 +6,11 @@ from .common import TestUsPayslip
class TestUsSCPayslip(TestUsPayslip):
###
- # 2021 Taxes and Rates
+ # 2020 Taxes and Rates
###
SC_UNEMP_MAX_WAGE = 14000.0
SC_UNEMP = 0.55
- # Calculation based on https://dor.sc.gov/forms-site/Forms/WH1603F_2021.pdf
+ # Calculation based on https://dor.sc.gov/forms-site/Forms/WH1603F_2020.pdf
def _test_sit(self, wage, additional_withholding, exempt, allowances, schedule_pay, date_start, expected_withholding):
employee = self._createEmployee()
@@ -28,9 +28,9 @@ class TestUsSCPayslip(TestUsPayslip):
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):
- self._test_er_suta('SC', self.SC_UNEMP, date(2021, 1, 1), wage_base=self.SC_UNEMP_MAX_WAGE)
- self._test_sit(750.0, 0.0, False, 3.0, 'weekly', date(2021, 1, 1), 28.73)
- self._test_sit(800.0, 0.0, True, 0.0, 'weekly', date(2021, 1, 1), 0.00)
- self._test_sit(9000.0, 0.0, False, 0.0, 'monthly', date(2021, 1, 1), 594.61)
- self._test_sit(5000.0, 10.0, False, 2.0, 'semi-monthly', date(2021, 1, 1), 316.06)
+ def test_2020_taxes_example(self):
+ self._test_er_suta('SC', self.SC_UNEMP, date(2020, 1, 1), wage_base=self.SC_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 0.0, False, 3.0, 'weekly', date(2020, 1, 1), 28.73)
+ self._test_sit(800.0, 0.0, True, 0.0, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(9000.0, 0.0, False, 0.0, 'monthly', date(2020, 1, 1), 594.61)
+ self._test_sit(5000.0, 10.0, False, 2.0, 'semi-monthly', date(2020, 1, 1), 316.06)
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
index 2aeb940a..261512c3 100755
--- 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
@@ -62,7 +62,7 @@ class TestUsWAPayslip(TestUsPayslip):
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.assertAlmostEqual(hours_in_period, 184) # only asserted to test algorithm
+ self.assertPayrollAlmostEqual(hours_in_period, 184) # only asserted to test algorithm
payslip.compute_sheet()
rules = self._getRules(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2021.py b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2021.py
index 180494e0..6102e3dc 100755
--- a/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2021.py
+++ b/l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2021.py
@@ -12,8 +12,8 @@ class TestUsWAPayslip(TestUsPayslip):
WA_UNEMP_RATE = 2.16
WA_FML_MAX_WAGE = 142800.00
WA_FML_RATE = 0.4
- WA_FML_RATE_EE = 66.33
- WA_FML_RATE_ER = 33.67
+ WA_FML_RATE_EE = 63.33
+ WA_FML_RATE_ER = 36.67
def setUp(self):
super(TestUsWAPayslip, self).setUp()
From 761f66c932bc0ef61f20c153aad37a4d333c2058 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Fri, 24 Sep 2021 09:23:01 -0700
Subject: [PATCH 10/12] [IMP] l10n_us_hr_payroll: new September 2021 Ohio
multiplier
---
l10n_us_hr_payroll/data/state/oh_ohio.xml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/l10n_us_hr_payroll/data/state/oh_ohio.xml b/l10n_us_hr_payroll/data/state/oh_ohio.xml
index 1d3344a0..f3275f0e 100644
--- a/l10n_us_hr_payroll/data/state/oh_ohio.xml
+++ b/l10n_us_hr_payroll/data/state/oh_ohio.xml
@@ -79,6 +79,11 @@
+
+ 1.001
+
+
+
From 7310969e57c1436bf5c2fbb5feec2521a9f4393c Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 7 Oct 2021 07:35:41 -0700
Subject: [PATCH 11/12] [FIX] hr_payroll_hibou: test harness for downstream
needs home address
---
hr_payroll_hibou/tests/common.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/hr_payroll_hibou/tests/common.py b/hr_payroll_hibou/tests/common.py
index 00a3564b..f742eb75 100755
--- a/hr_payroll_hibou/tests/common.py
+++ b/hr_payroll_hibou/tests/common.py
@@ -60,13 +60,17 @@ class TestPayslip(common.TransactionCase):
self._logger.warning(message)
def _createEmployee(self):
- return self.env['hr.employee'].create({
+ employee = self.env['hr.employee'].create({
'birthday': '1985-03-14',
'country_id': self.ref('base.us'),
'department_id': self.ref('hr.dep_rd'),
'gender': 'male',
'name': 'Jared'
})
+ employee.address_home_id = self.env['res.partner'].create({
+ 'name': 'Jared (private)',
+ })
+ return employee
def _get_contract_defaults(self, contract_values):
if not contract_values.get('state'):
From ca1e573cb2b2161942af2cf16e6f8a0f6d68f504 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 7 Oct 2021 07:39:57 -0700
Subject: [PATCH 12/12] [MIG] l10n_us_hr_payroll: to Odoo 15.0
---
l10n_us_hr_payroll/__manifest__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py
index b59efd56..110f956f 100644
--- a/l10n_us_hr_payroll/__manifest__.py
+++ b/l10n_us_hr_payroll/__manifest__.py
@@ -3,7 +3,7 @@
{
'name': 'United States of America - Payroll',
'author': 'Hibou Corp. ',
- 'version': '14.0.2020.0.0',
+ 'version': '15.0.2021.0.0',
'category': 'Payroll Localization',
'depends': [
'hr_payroll_hibou',