diff --git a/l10n_us_mo_hr_payroll/__init__.py b/l10n_us_mo_hr_payroll/__init__.py
index c90f169b..0650744f 100755
--- a/l10n_us_mo_hr_payroll/__init__.py
+++ b/l10n_us_mo_hr_payroll/__init__.py
@@ -1,3 +1 @@
-# -*- coding: utf-8 -*-
-
-from . import hr_payroll
\ No newline at end of file
+from . import models
diff --git a/l10n_us_mo_hr_payroll/__manifest__.py b/l10n_us_mo_hr_payroll/__manifest__.py
index 205fbdb6..964e1718 100755
--- a/l10n_us_mo_hr_payroll/__manifest__.py
+++ b/l10n_us_mo_hr_payroll/__manifest__.py
@@ -19,9 +19,10 @@ USA::Missouri Payroll Rules.
'auto_install': False,
'website': 'https://hibou.io/',
'data': [
- 'hr_payroll_view.xml',
+ 'views/hr_payroll_views.xml',
'data/base.xml',
- 'data/rules_2018.xml',
+ 'data/rates.xml',
+ 'data/rules.xml',
'data/final.xml',
],
'installable': True
diff --git a/l10n_us_mo_hr_payroll/data/base.xml b/l10n_us_mo_hr_payroll/data/base.xml
index d1952a63..cc4d3ff1 100755
--- a/l10n_us_mo_hr_payroll/data/base.xml
+++ b/l10n_us_mo_hr_payroll/data/base.xml
@@ -26,19 +26,19 @@
- Missouri Unemployment - Wages
- MO_UNEMP_WAGES
+ Wage: US-MO Unemployment
+ WAGE_US_MO_UNEMP
- Missouri Unemployment
- MO_UNEMP
+ ER: US-MO Unemployment
+ ER_US_MO_UNEMP
- Missouri Income Withholding
- MO_INC_WITHHOLD
+ EE: US-MO Income Withholding
+ EE_US_MO_INC_WITHHOLD
diff --git a/l10n_us_mo_hr_payroll/data/rates.xml b/l10n_us_mo_hr_payroll/data/rates.xml
new file mode 100644
index 00000000..5c139747
--- /dev/null
+++ b/l10n_us_mo_hr_payroll/data/rates.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ US Missouri Unemployment
+ US_MO_UNEMP
+ 2.511
+ 2018-01-01
+
+
+
+ US Missouri Unemployment
+ US_MO_UNEMP
+ 2.376
+ 2019-01-01
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_mo_hr_payroll/data/rules.xml b/l10n_us_mo_hr_payroll/data/rules.xml
new file mode 100755
index 00000000..9d0cfac4
--- /dev/null
+++ b/l10n_us_mo_hr_payroll/data/rules.xml
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+
+
+ Wage: US-MO Unemployment
+ WAGE_US_MO_UNEMP
+ python
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC)
+ code
+
+###
+year = int(payslip.dict.date_to[:4])
+rate = payslip.dict.get_rate('US_MO_UNEMP')
+ytd = payslip.sum('WAGE_US_MO_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01')
+ytd += contract.external_wages
+remaining = rate.wage_limit_year - ytd
+if remaining <= 0.0:
+ result = 0
+elif remaining < categories.BASIC:
+ result = remaining
+else:
+ result = categories.BASIC
+
+
+
+
+
+
+ ER: US-MO Unemployment
+ ER_US_MO_UNEMP
+ python
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC)
+ code
+
+rate = payslip.dict.get_rate('US_MO_UNEMP')
+result_rate = -rate.rate
+result = categories.WAGE_US_MO_UNEMP
+
+# result_rate of 0 implies 100% due to bug
+if result_rate == 0.0:
+ result = 0.0
+
+
+
+
+
+
+
+
+ EE: US-MO Income Withholding
+ EE_US_MO_INC_WITHHOLD
+ python
+ result = True
+ code
+
+year = int(payslip.dict.date_to[:4])
+wages = categories.GROSS
+exemptions = contract.mo_mow4_exemptions
+filing_status = contract.mo_mow4_filing_status
+additional = contract.mo_mow4_additional_withholding
+spouse_employed = contract.mo_mow4_spouse_employed
+schedule_pay = contract.schedule_pay
+result = 0.00
+
+if year == 2018:
+ TAX = [
+ (1028.0, 1.5),
+ (1028.0, 2.0),
+ (1028.0, 2.5),
+ (1028.0, 3.0),
+ (1028.0, 3.5),
+ (1028.0, 4.0),
+ (1028.0, 4.5),
+ (1028.0, 5.0),
+ (1028.0, 5.5),
+ (999999999.0, 5.9),
+ ]
+ if filing_status:
+ PP = 0
+ if 'weekly' == schedule_pay:
+ PP = 52
+ elif 'bi-weekly' == schedule_pay:
+ PP = 26
+ elif 'semi-monthly' == schedule_pay:
+ PP = 24
+ elif 'monthly' == schedule_pay:
+ PP = 12
+ elif 'quarterly' == schedule_pay:
+ PP = 4
+ elif 'semi-annually' == schedule_pay:
+ PP = 2
+ elif 'annually' == schedule_pay:
+ PP = 1
+ else:
+ raise Exception('Invalid schedule_pay="' + schedule_pay + '" for MO Income Withholding calculation')
+
+ gross_salary = PP * wages
+ deduction = -1
+ if filing_status == 'single' or (filing_status == 'married' and spouse_employed):
+ deduction = 6500.0
+ elif filing_status == 'married' and not spouse_employed:
+ deduction = 13000.0
+ else:
+ deduction = 9550.0
+
+ if deduction < 0:
+ raise UserError('Invalid deduction calculation')
+
+ allowances = 0
+ if filing_status == 'single' or (filing_status == 'married' and spouse_employed):
+ if exemptions == 1:
+ allowances = 2100.0
+ elif exemptions > 1:
+ allowances = 2100.0 + ((exemptions - 1) * 1200.0)
+ elif filing_status == 'married' and not spouse_employed:
+ if exemptions > 0 and exemptions < 3:
+ allowances = 2100.0 * exemptions
+ elif exemptions >= 3:
+ allowances = 2100.0 + 2100.0 + ((exemptions - 2) * 1200.0)
+ else:
+ if exemptions == 1:
+ allowances = 3500.0
+ elif exemptions > 1:
+ allowances = 3500.0 + ((exemptions - 1) * 1200.0)
+
+ federal = categories.EE_US_FED_INC_WITHHOLD * -1 * PP
+ if filing_status == 'married' and not spouse_employed:
+ federal = min(10000.0, federal)
+ else:
+ federal = min(5000.0, federal)
+
+ mo_taxable_income = gross_salary - deduction - allowances - federal
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in TAX:
+ rate = rate / 100.0
+ remaining_taxable_income = remaining_taxable_income - amt
+ if remaining_taxable_income > 0.0 or remaining_taxable_income == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * (remaining_taxable_income + amt)
+ break
+ tax = -tax
+ tax = tax / PP
+
+ # Small GROSS results in an underflow
+ if tax > 0.0:
+ tax = 0.0
+
+ if additional:
+ tax -= additional
+
+ result = round(tax)
+else:
+ if year == 2019:
+ # There are no longer allowances in the Missouri Withholding tables.
+ exemptions = 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),
+ ]
+ if filing_status:
+ PP = 0
+ if 'weekly' == schedule_pay:
+ PP = 52
+ elif 'bi-weekly' == schedule_pay:
+ PP = 26
+ elif 'semi-monthly' == schedule_pay:
+ PP = 24
+ elif 'monthly' == schedule_pay:
+ PP = 12
+ elif 'quarterly' == schedule_pay:
+ PP = 4
+ elif 'semi-annually' == schedule_pay:
+ PP = 2
+ elif 'annually' == schedule_pay:
+ PP = 1
+ else:
+ raise Exception('Invalid schedule_pay="' + schedule_pay + '" for MO Income Withholding calculation')
+
+ gross_salary = PP * wages
+ deduction = -1
+ if filing_status == 'single' or (filing_status == 'married' and spouse_employed):
+ deduction = 12200.0 / 2.0
+ elif filing_status == 'married' and not spouse_employed:
+ deduction = 24400.0 / 2.0
+ else:
+ deduction = 18350.0 / 2.0
+
+ if deduction < 0:
+ raise UserError('Invalid deduction calculation')
+
+ allowances = 0
+ if filing_status == 'single' or (filing_status == 'married' and spouse_employed):
+ if exemptions == 1:
+ allowances = 2100.0
+ elif exemptions > 1:
+ allowances = 2100.0 + ((exemptions - 1) * 1200.0)
+ elif filing_status == 'married' and not spouse_employed:
+ if exemptions > 0 and exemptions < 3:
+ allowances = 2100.0 * exemptions
+ elif exemptions >= 3:
+ allowances = 2100.0 + 2100.0 + ((exemptions - 2) * 1200.0)
+ else:
+ if exemptions == 1:
+ allowances = 3500.0
+ elif exemptions > 1:
+ allowances = 3500.0 + ((exemptions - 1) * 1200.0)
+
+ mo_taxable_income = gross_salary - deduction - allowances
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in TAX:
+ rate = rate / 100.0
+ remaining_taxable_income = remaining_taxable_income - amt
+ if remaining_taxable_income > 0.0 or remaining_taxable_income == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * (remaining_taxable_income + amt)
+ break
+ tax = -tax
+ tax = tax / PP
+
+ # Small GROSS results in an underflow
+ if tax > 0.0:
+ tax = 0.0
+
+ if additional:
+ tax -= additional
+
+ result = round(tax)
+
+
+
+
+
+
diff --git a/l10n_us_mo_hr_payroll/data/rules_2018.xml b/l10n_us_mo_hr_payroll/data/rules_2018.xml
deleted file mode 100755
index 5ad1ae1a..00000000
--- a/l10n_us_mo_hr_payroll/data/rules_2018.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-
-
-
-
-
-
- Missouri Unemployment - Wages (2018)
- MO_UNEMP_WAGES_2018
- python
- result = (payslip.date_to[:4] == '2018')
- code
-
-###
-ytd = payslip.sum('MO_UNEMP_WAGES_2018', '2018-01-01', '2019-01-01')
-ytd += contract.external_wages
-remaining = 12500.0 - ytd
-if remaining <= 0.0:
- result = 0
-elif remaining < categories.BASIC:
- result = remaining
-else:
- result = categories.BASIC
-
-
-
-
-
-
- Missouri Unemployment (2018)
- MO_UNEMP_2018
- python
- result = (payslip.date_to[:4] == '2018')
- code
-
-result_rate = -contract.mo_unemp_rate(2018)
-result = categories.MO_UNEMP_WAGES
-
-# result_rate of 0 implies 100% due to bug
-if result_rate == 0.0:
- result = 0.0
-
-
-
-
-
-
-
-
- Missouri Income Withholding (2018)
- MO_INC_WITHHOLD_2018
- python
- result = (payslip.date_to[:4] == '2018')
- code
-
-TAX = [
- (1028.0, 1.5),
- (1028.0, 2.0),
- (1028.0, 2.5),
- (1028.0, 3.0),
- (1028.0, 3.5),
- (1028.0, 4.0),
- (1028.0, 4.5),
- (1028.0, 5.0),
- (1028.0, 5.5),
- (999999999.0, 5.9),
-]
-wages = categories.GROSS
-exemptions = contract.mo_mow4_exemptions
-filing_status = contract.mo_mow4_filing_status
-additional = contract.mo_mow4_additional_withholding
-spouse_employed = contract.mo_mow4_spouse_employed
-schedule_pay = contract.schedule_pay
-result = 0.00
-
-if filing_status:
- PP = 0
- if 'weekly' == schedule_pay:
- PP = 52
- elif 'bi-weekly' == schedule_pay:
- PP = 26
- elif 'semi-monthly' == schedule_pay:
- PP = 24
- elif 'monthly' == schedule_pay:
- PP = 12
- elif 'quarterly' == schedule_pay:
- PP = 4
- elif 'semi-annually' == schedule_pay:
- PP = 2
- elif 'annually' == schedule_pay:
- PP = 1
- else:
- raise Exception('Invalid schedule_pay="' + schedule_pay + '" for MO Income Withholding calculation')
-
- gross_salary = PP * wages
- deduction = -1
- if filing_status == 'single' or (filing_status == 'married' and spouse_employed):
- deduction = 6500.0
- elif filing_status == 'married' and not spouse_employed:
- deduction = 13000.0
- else:
- deduction = 9550.0
-
- if deduction < 0:
- raise UserError('Invalid deduction calculation')
-
- allowances = 0
- if filing_status == 'single' or (filing_status == 'married' and spouse_employed):
- if exemptions == 1:
- allowances = 2100.0
- elif exemptions > 1:
- allowances = 2100.0 + ((exemptions - 1) * 1200.0)
- elif filing_status == 'married' and not spouse_employed:
- if exemptions > 0 and exemptions < 3:
- allowances = 2100.0 * exemptions
- elif exemptions >= 3:
- allowances = 2100.0 + 2100.0 + ((exemptions - 2) * 1200.0)
- else:
- if exemptions == 1:
- allowances = 3500.0
- elif exemptions > 1:
- allowances = 3500.0 + ((exemptions - 1) * 1200.0)
-
- federal = categories.FED_INC_WITHHOLD * -1 * PP
- if filing_status == 'married' and not spouse_employed:
- federal = min(10000.0, federal)
- else:
- federal = min(5000.0, federal)
-
- mo_taxable_income = gross_salary - deduction - allowances - federal
-
- remaining_taxable_income = mo_taxable_income
- tax = 0.0
- for amt, rate in TAX:
- rate = rate / 100.0
- remaining_taxable_income = remaining_taxable_income - amt
- if remaining_taxable_income > 0.0 or remaining_taxable_income == 0.0:
- tax += rate * amt
- else:
- tax += rate * (remaining_taxable_income + amt)
- break
- tax = -tax
- tax = tax / PP
-
- # Small GROSS results in an underflow
- if tax > 0.0:
- tax = 0.0
-
- if additional:
- tax -= additional
-
- result = round(tax)
-
-
-
-
-
-
diff --git a/l10n_us_mo_hr_payroll/hr_payroll.py b/l10n_us_mo_hr_payroll/hr_payroll.py
deleted file mode 100755
index 0f209a07..00000000
--- a/l10n_us_mo_hr_payroll/hr_payroll.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from odoo import models, fields, api
-
-
-class USMOHrContract(models.Model):
- _inherit = 'hr.contract'
-
- mo_mow4_filing_status = fields.Selection([
- ('', 'Exempt'),
- ('single', 'Single'),
- ('married', 'Married'),
- ('head_of_household', 'Head of Household'),
- ], string='Federal W4 Filing Status', default='single')
- mo_mow4_spouse_employed = fields.Boolean(string='Missouri W-4 Spouse Employed', default=False)
- mo_mow4_exemptions = fields.Integer(string='Missouri W-4 Exemptions', default=0)
- mo_mow4_additional_withholding = fields.Float(string="Missouri W-4 Additional Withholding", default=0.0)
-
-
- @api.multi
- def mo_unemp_rate(self, year):
- self.ensure_one()
- if self.futa_type == self.FUTA_TYPE_BASIC:
- return 0.0
-
- if hasattr(self.employee_id.company_id, 'mo_unemp_rate_' + str(year)):
- return self.employee_id.company_id['mo_unemp_rate_' + str(year)]
-
- raise NotImplemented('Year (' + str(year) + ') Not implemented for US Missouri.')
-
-
-class MOCompany(models.Model):
- _inherit = 'res.company'
-
- # Defaults from :: https://labor.mo.gov/DES/Employers/tax_rates#beginning
- mo_unemp_rate_2018 = fields.Float(string="Missouri Unemployment Rate 2018", default=2.511)
diff --git a/l10n_us_mo_hr_payroll/models/__init__.py b/l10n_us_mo_hr_payroll/models/__init__.py
new file mode 100644
index 00000000..e99aa24a
--- /dev/null
+++ b/l10n_us_mo_hr_payroll/models/__init__.py
@@ -0,0 +1 @@
+from . import hr_payroll
diff --git a/l10n_us_mo_hr_payroll/models/hr_payroll.py b/l10n_us_mo_hr_payroll/models/hr_payroll.py
new file mode 100755
index 00000000..626c9bc2
--- /dev/null
+++ b/l10n_us_mo_hr_payroll/models/hr_payroll.py
@@ -0,0 +1,17 @@
+from odoo import models, fields, api
+
+
+class USMOHrContract(models.Model):
+ _inherit = 'hr.contract'
+
+ mo_mow4_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_of_household', 'Head of Household'),
+ ], string='Federal W4 Filing Status', default='single')
+ mo_mow4_spouse_employed = fields.Boolean(string='Missouri W-4 Spouse Employed', default=False)
+ mo_mow4_exemptions = fields.Integer(string='Missouri W-4 Exemptions', default=0,
+ help="As of 2019, there are no longer allowances"
+ " in the Missouri Withholding tables.")
+ mo_mow4_additional_withholding = fields.Float(string="Missouri W-4 Additional Withholding", default=0.0)
diff --git a/l10n_us_mo_hr_payroll/tests/__init__.py b/l10n_us_mo_hr_payroll/tests/__init__.py
index c7d48e92..033cf841 100755
--- a/l10n_us_mo_hr_payroll/tests/__init__.py
+++ b/l10n_us_mo_hr_payroll/tests/__init__.py
@@ -1,3 +1,2 @@
-# -*- coding: utf-8 -*-
-
-from . import test_us_mo_payslip_2018
\ No newline at end of file
+from . import test_us_mo_payslip_2018
+from . import test_us_mo_payslip_2019
diff --git a/l10n_us_mo_hr_payroll/tests/test_us_mo_payslip_2018.py b/l10n_us_mo_hr_payroll/tests/test_us_mo_payslip_2018.py
index c4dcc763..1197d5aa 100755
--- a/l10n_us_mo_hr_payroll/tests/test_us_mo_payslip_2018.py
+++ b/l10n_us_mo_hr_payroll/tests/test_us_mo_payslip_2018.py
@@ -7,6 +7,7 @@ class TestUsMoPayslip(TestUsPayslip):
# Calculations from http://dor.mo.gov/forms/4282_2018.pdf
SALARY = 5000.0
MO_ALLOWANCES = 3 # Different calculated amounts for different filing statuses
+ MO_UNEMP = -2.511 / 100.0
TAX = [
(1028.0, 1.5),
@@ -33,17 +34,12 @@ class TestUsMoPayslip(TestUsPayslip):
mo_allowance_calculated = 2100.0 + 1200.0 + 1200.0
employee = self._createEmployee()
- employee.company_id.mo_unemp_rate_2018 = 2.511
-
contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_mo_hr_payroll.hr_payroll_salary_structure_us_mo_employee'))
contract.mo_mow4_spouse_employed = spouse_employed
contract.mo_mow4_filing_status = 'single'
contract.mo_mow4_exemptions = 3
contract.mo_mow4_additional_withholding = 0.0
- # tax rates
- mo_unemp = contract.mo_unemp_rate(2018) / -100.0
-
self._log('2018 Missouri tax single first payslip:')
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
@@ -51,10 +47,10 @@ class TestUsMoPayslip(TestUsPayslip):
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['MO_UNEMP_WAGES'], salary)
- self.assertPayrollEqual(cats['MO_UNEMP'], cats['MO_UNEMP_WAGES'] * mo_unemp)
+ self.assertPayrollEqual(cats['WAGE_US_MO_UNEMP'], salary)
+ self.assertPayrollEqual(cats['ER_US_MO_UNEMP'], cats['WAGE_US_MO_UNEMP'] * self.MO_UNEMP)
- US_WITHHOLDING = cats['FED_INC_WITHHOLD']
+ US_WITHHOLDING = cats['EE_US_FED_INC_WITHHOLD']
# -693.86
self._log(US_WITHHOLDING)
us_withholding = -US_WITHHOLDING * pp
@@ -85,7 +81,7 @@ class TestUsMoPayslip(TestUsPayslip):
tax = tax / pp
tax = round(tax)
self._log('Computed period tax: ' + str(tax))
- self.assertPayrollEqual(cats['MO_INC_WITHHOLD'], tax)
+ self.assertPayrollEqual(cats['EE_US_MO_INC_WITHHOLD'], tax)
def test_2018_spouse_not_employed(self):
# Payroll Period Semi-monthly
@@ -115,7 +111,7 @@ class TestUsMoPayslip(TestUsPayslip):
cats = self._getCategories(payslip)
- US_WITHHOLDING = cats['FED_INC_WITHHOLD']
+ US_WITHHOLDING = cats['EE_US_FED_INC_WITHHOLD']
# -693.86
self._log(US_WITHHOLDING)
us_withholding = -US_WITHHOLDING * pp
@@ -145,7 +141,7 @@ class TestUsMoPayslip(TestUsPayslip):
tax = tax / pp
tax = round(tax)
self._log('Computed period tax: ' + str(tax))
- self.assertPayrollEqual(cats['MO_INC_WITHHOLD'], tax)
+ self.assertPayrollEqual(cats['EE_US_MO_INC_WITHHOLD'], tax)
def test_2018_head_of_household(self):
# Payroll Period Weekly
@@ -178,7 +174,7 @@ class TestUsMoPayslip(TestUsPayslip):
cats = self._getCategories(payslip)
- US_WITHHOLDING = cats['FED_INC_WITHHOLD']
+ US_WITHHOLDING = cats['EE_US_FED_INC_WITHHOLD']
self._log(US_WITHHOLDING)
us_withholding = -US_WITHHOLDING * pp
@@ -206,7 +202,7 @@ class TestUsMoPayslip(TestUsPayslip):
tax = tax / pp
tax = round(tax)
self._log('Computed period tax: ' + str(tax))
- self.assertPayrollEqual(cats['MO_INC_WITHHOLD'], tax)
+ self.assertPayrollEqual(cats['EE_US_MO_INC_WITHHOLD'], tax)
def test_2018_underflow(self):
# Payroll Period Weekly
@@ -221,4 +217,4 @@ class TestUsMoPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['MO_INC_WITHHOLD'], 0.0)
+ self.assertPayrollEqual(cats['EE_US_MO_INC_WITHHOLD'], 0.0)
diff --git a/l10n_us_mo_hr_payroll/tests/test_us_mo_payslip_2019.py b/l10n_us_mo_hr_payroll/tests/test_us_mo_payslip_2019.py
new file mode 100755
index 00000000..12030a19
--- /dev/null
+++ b/l10n_us_mo_hr_payroll/tests/test_us_mo_payslip_2019.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+
+from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
+
+
+class TestUsMoPayslip(TestUsPayslip):
+ # Calculations from http://dor.mo.gov/forms/4282_2019.pdf
+ SALARY = 5000.0
+ MO_ALLOWANCES = 3 # Different calculated amounts for different filing statuses
+ 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 = 6100.0
+ mo_allowance_calculated = 0.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_mo_hr_payroll.hr_payroll_salary_structure_us_mo_employee'))
+ contract.mo_mow4_spouse_employed = spouse_employed
+ contract.mo_mow4_filing_status = 'single'
+ contract.mo_mow4_exemptions = 3
+ contract.mo_mow4_additional_withholding = 0.0
+
+ 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['WAGE_US_MO_UNEMP'], salary)
+ self.assertPayrollEqual(cats['ER_US_MO_UNEMP'], cats['WAGE_US_MO_UNEMP'] * self.MO_UNEMP)
+
+ mo_taxable_income = gross_salary - standard_deduction - mo_allowance_calculated
+ self._log('%s = %s - %s - %s' % (mo_taxable_income, gross_salary, standard_deduction, mo_allowance_calculated))
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in self.TAX:
+ rate = rate / 100.0
+ self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
+ remaining_taxable_income = remaining_taxable_income - amt
+ if remaining_taxable_income > 0.0 or remaining_taxable_income == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * (remaining_taxable_income + amt)
+ break
+ 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_MO_INC_WITHHOLD'], tax)
+
+ def test_2019_spouse_not_employed(self):
+ # Payroll Period Semi-monthly
+ salary = self.SALARY
+ pp = 24.0
+ gross_salary = salary * pp
+ spouse_employed = False
+
+ # Single
+ standard_deduction = 12200.0
+ mo_allowance_calculated = 0.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee, salary,
+ struct_id=self.ref('l10n_us_mo_hr_payroll.hr_payroll_salary_structure_us_mo_employee'),
+ schedule_pay='semi-monthly')
+ contract.mo_mow4_spouse_employed = spouse_employed
+ contract.mo_mow4_filing_status = 'married'
+ contract.mo_mow4_exemptions = 3
+ contract.mo_mow4_additional_withholding = 0.0
+
+ 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 - mo_allowance_calculated
+ self._log(mo_taxable_income)
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in self.TAX:
+ rate = rate / 100.0
+ self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
+ remaining_taxable_income = remaining_taxable_income - amt
+ if remaining_taxable_income > 0.0 or remaining_taxable_income == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * (remaining_taxable_income + amt)
+ break
+ 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_MO_INC_WITHHOLD'], 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
+ spouse_employed = False
+
+ # Single HoH
+ standard_deduction = 18350.0 / 2.0
+ mo_allowance_calculated = 0.0 # 2019 Allowances are no longer in the Missouri Withholding tables.
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee, salary,
+ struct_id=self.ref('l10n_us_mo_hr_payroll.hr_payroll_salary_structure_us_mo_employee'),
+ schedule_pay='weekly')
+ contract.mo_mow4_spouse_employed = spouse_employed
+ contract.mo_mow4_filing_status = 'head_of_household'
+ contract.mo_mow4_exemptions = 3
+ contract.mo_mow4_additional_withholding = 0.0
+
+ 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 - mo_allowance_calculated
+ self._log(mo_taxable_income)
+
+ remaining_taxable_income = mo_taxable_income
+ tax = 0.0
+ for amt, rate in self.TAX:
+ rate = rate / 100.0
+ self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
+ remaining_taxable_income = remaining_taxable_income - amt
+ if remaining_taxable_income > 0.0 or remaining_taxable_income == 0.0:
+ tax += rate * amt
+ else:
+ tax += rate * (remaining_taxable_income + amt)
+ break
+ 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_MO_INC_WITHHOLD'], tax)
+
+ def test_2019_underflow(self):
+ # Payroll Period Weekly
+ salary = 200.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee, salary,
+ struct_id=self.ref('l10n_us_mo_hr_payroll.hr_payroll_salary_structure_us_mo_employee'))
+
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_MO_INC_WITHHOLD'], 0.0)
diff --git a/l10n_us_mo_hr_payroll/hr_payroll_view.xml b/l10n_us_mo_hr_payroll/views/hr_payroll_views.xml
similarity index 65%
rename from l10n_us_mo_hr_payroll/hr_payroll_view.xml
rename to l10n_us_mo_hr_payroll/views/hr_payroll_views.xml
index 0212ebc2..8db3b723 100755
--- a/l10n_us_mo_hr_payroll/hr_payroll_view.xml
+++ b/l10n_us_mo_hr_payroll/views/hr_payroll_views.xml
@@ -1,19 +1,6 @@
-
- res.company.form
- res.company
- 64
-
-
-
-
-
-
-
-
-
hr.contract.form.inherit
hr.contract