mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
MIG l10n_us_hr_payroll Major refactor and Federal 2020 Rules (new W4 Form), Migration scripts and states deprecated
States: - FL Florida - PA Pennsylvania - MT Montana - OH Ohio - WA Washington - TX Texas - VA Virginia - GA Georgia - MS Mississippi
This commit is contained in:
29
LICENSE_PROFESSIONAL
Normal file
29
LICENSE_PROFESSIONAL
Normal file
@@ -0,0 +1,29 @@
|
||||
Odoo Proprietary License v1.0
|
||||
|
||||
This software and associated files (the "Software") may only be used
|
||||
(executed, modified, executed after modifications) if you have purchased
|
||||
a valid license from the authors, typically via Odoo Apps, or if you
|
||||
have received a written agreement from the authors of the Software
|
||||
(see the COPYRIGHT file).
|
||||
|
||||
You may develop Odoo modules that use the Software as a library
|
||||
(typically by depending on it, importing it and using its resources),
|
||||
but without copying any source code or material from the Software.
|
||||
You may distribute those modules under the license of your choice,
|
||||
provided that this license is compatible with the terms of the Odoo
|
||||
Proprietary License (For example: LGPL, MIT, or proprietary licenses
|
||||
similar to this one).
|
||||
|
||||
It is forbidden to publish, distribute, sublicense, or sell copies
|
||||
of the Software or modified copies of the Software.
|
||||
|
||||
The above copyright notice and this permission notice must be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,23 +1,41 @@
|
||||
import ast
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import ormcache
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class PayrollRate(models.Model):
|
||||
_name = 'hr.payroll.rate'
|
||||
_description = 'Payroll Rate'
|
||||
_order = 'date_from DESC, company_id ASC'
|
||||
|
||||
active = fields.Boolean(string='Active', default=True)
|
||||
name = fields.Char(string='Name')
|
||||
date_from = fields.Date(string='Date From', required=True)
|
||||
date_from = fields.Date(string='Date From', index=True, required=True)
|
||||
date_to = fields.Date(string='Date To')
|
||||
company_id = fields.Many2one('res.company', string='Company', copy=False,
|
||||
default=False)
|
||||
rate = fields.Float(string='Rate', digits=(12, 6), required=True)
|
||||
code = fields.Char(string='Code', required=True)
|
||||
rate = fields.Float(string='Rate', digits=(12, 6), default=0.0, required=True)
|
||||
code = fields.Char(string='Code', index=True, required=True)
|
||||
|
||||
limit_payslip = fields.Float(string='Payslip Limit')
|
||||
limit_year = fields.Float(string='Year Limit')
|
||||
wage_limit_payslip = fields.Float(string='Payslip Wage Limit')
|
||||
wage_limit_year = fields.Float(string='Year Wage Limit')
|
||||
parameter_value = fields.Text(help="Python data structure")
|
||||
|
||||
@api.model
|
||||
@ormcache('code', 'date', 'company_id', 'self.env.user.company_id.id')
|
||||
def _get_parameter_from_code(self, code, company_id, date=None):
|
||||
if not date:
|
||||
date = fields.Date.today()
|
||||
rate = self.search([
|
||||
('code', '=', code),
|
||||
('date_from', '<=', date),
|
||||
], limit=1)
|
||||
if not rate:
|
||||
raise UserError(_("No rule parameter with code '%s' was found for %s ") % (code, date))
|
||||
return ast.literal_eval(rate.parameter_value)
|
||||
|
||||
|
||||
class Payslip(models.Model):
|
||||
@@ -35,3 +53,6 @@ class Payslip(models.Model):
|
||||
self.ensure_one()
|
||||
return self.env['hr.payroll.rate'].search(
|
||||
self._get_rate_domain(code), limit=1, order='date_from DESC, company_id ASC')
|
||||
|
||||
def rule_parameter(self, code):
|
||||
return self.env['hr.payroll.rate']._get_parameter_from_code(code, self.company_id.id, self.date_to)
|
||||
|
||||
@@ -44,6 +44,15 @@ class TestPayrollRate(common.TransactionCase):
|
||||
rate = self.payslip.get_rate('TEST')
|
||||
self.assertEqual(rate, test_rate)
|
||||
|
||||
test_rate.parameter_value = """[
|
||||
(1, 2, 3),
|
||||
(4, 5, 6),
|
||||
]"""
|
||||
|
||||
value = self.payslip.rule_parameter('TEST')
|
||||
self.assertEqual(len(value), 2)
|
||||
self.assertEqual(value[0], (1, 2, 3))
|
||||
|
||||
def test_payroll_rate_multicompany(self):
|
||||
test_rate_other = self.env['hr.payroll.rate'].create({
|
||||
'name': 'Test Rate',
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="rate"/>
|
||||
<field name="parameter_value"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date_from"/>
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::Arkansas Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -28,5 +28,5 @@ USA::California Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::Florida Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
{
|
||||
'name': 'USA - Payroll',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Localization',
|
||||
'depends': ['hr_payroll', 'hr_payroll_rate'],
|
||||
'version': '11.0.2018.1.0',
|
||||
'version': '11.0.2020.1.0',
|
||||
'description': """
|
||||
USA Payroll Rules.
|
||||
==================
|
||||
@@ -21,11 +20,28 @@ USA Payroll Rules.
|
||||
'auto_install': False,
|
||||
'website': 'https://hibou.io/',
|
||||
'data': [
|
||||
'views/l10n_us_hr_payroll_view.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/base.xml',
|
||||
'data/rates.xml',
|
||||
'data/rules.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',
|
||||
'data/state/fl_florida.xml',
|
||||
'data/state/ga_georgia.xml',
|
||||
'data/state/ms_mississippi.xml',
|
||||
'data/state/mt_montana.xml',
|
||||
'data/state/oh_ohio.xml',
|
||||
'data/state/pa_pennsylvania.xml',
|
||||
'data/state/tx_texas.xml',
|
||||
'data/state/va_virginia.xml',
|
||||
'data/state/wa_washington.xml',
|
||||
'data/final.xml',
|
||||
'views/hr_contract_views.xml',
|
||||
'views/us_payroll_config_views.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': True,
|
||||
'license': 'OPL-1',
|
||||
}
|
||||
|
||||
115
l10n_us_hr_payroll/data/base.xml
Executable file → Normal file
115
l10n_us_hr_payroll/data/base.xml
Executable file → Normal file
@@ -1,83 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- CONTRIBUTION REGISTERS -->
|
||||
<record id="res_partner_eftps_941" model="res.partner">
|
||||
<field name="name">EFTPS - Form 941</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_eftps_940" model="res.partner">
|
||||
<field name="name">EFTPS - Form 940</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="contrib_register_eftps_941" model="hr.contribution.register">
|
||||
<field name="name">EFTPS - 941 (FICA + Federal Witholding)</field>
|
||||
<field name="note">Electronic Federal Tax Payment System - Form 941</field>
|
||||
<field name="partner_id" ref="res_partner_eftps_941"/>
|
||||
</record>
|
||||
<record id="contrib_register_eftps_940" model="hr.contribution.register">
|
||||
<field name="name">EFTPS - 940 (FUTA)</field>
|
||||
<field name="note">Electronic Federal Tax Payment System - Form 940</field>
|
||||
<field name="partner_id" ref="res_partner_eftps_940"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- HR SALARY RULE CATEGORIES-->
|
||||
<record id="hr_payroll_fica_emp_ss_wages" model="hr.salary.rule.category">
|
||||
<field name="name">Wage: US FICA Social Security</field>
|
||||
<field name="code">WAGE_US_FICA_SS</field>
|
||||
</record>
|
||||
<record id="hr_payroll_fica_emp_m_wages" model="hr.salary.rule.category">
|
||||
<field name="name">Wage: US FICA Medicare</field>
|
||||
<field name="code">WAGE_US_FICA_M</field>
|
||||
</record>
|
||||
<record id="hr_payroll_fica_emp_m_add_wages" model="hr.salary.rule.category">
|
||||
<field name="name">Wage: US FICA Medicare Additional</field>
|
||||
<field name="code">WAGE_US_FICA_M_ADD</field>
|
||||
</record>
|
||||
<record id="hr_payroll_futa_wages" model="hr.salary.rule.category">
|
||||
<field name="name">Wage: US FUTA Federal Unemployment</field>
|
||||
<field name="code">WAGE_US_FUTA</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_fica_emp_ss" model="hr.salary.rule.category">
|
||||
<field name="name">EE: US FICA Social Security</field>
|
||||
<field name="code">EE_US_FICA_SS</field>
|
||||
<!-- State Unemployment -->
|
||||
<record id="hr_payroll_category_ee_us_suta" model="hr.salary.rule.category">
|
||||
<field name="name">EE: State Unemployment SUTA</field>
|
||||
<field name="code">EE_US_SUTA</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
<record id="hr_payroll_fica_emp_m" model="hr.salary.rule.category">
|
||||
<field name="name">EE: US FICA Medicare</field>
|
||||
<field name="code">EE_US_FICA_M</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
<record id="hr_payroll_fica_emp_m_add" model="hr.salary.rule.category">
|
||||
<field name="name">EE: US FICA Medicare Additional</field>
|
||||
<field name="code">EE_US_FICA_M_ADD</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
<record id="hr_payroll_fed_income_withhold" model="hr.salary.rule.category">
|
||||
<field name="name">EE: US Federal Income Tax Withholding</field>
|
||||
<field name="code">EE_US_FED_INC_WITHHOLD</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_fica_comp_ss" model="hr.salary.rule.category">
|
||||
<field name="name">ER: US FICA Social Security</field>
|
||||
<field name="code">ER_US_FICA_SS</field>
|
||||
<record id="hr_payroll_category_er_us_suta" model="hr.salary.rule.category">
|
||||
<field name="name">ER: State Unemployment SUTA</field>
|
||||
<field name="code">ER_US_SUTA</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
<record id="hr_payroll_fica_comp_m" model="hr.salary.rule.category">
|
||||
<field name="name">ER: US FICA Medicare</field>
|
||||
<field name="code">ER_US_FICA_M</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
|
||||
<!-- State Income Tax -->
|
||||
<record id="hr_payroll_category_ee_us_sit" model="hr.salary.rule.category">
|
||||
<field name="name">EE: State Income Tax Withholding</field>
|
||||
<field name="code">EE_US_SIT</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
<record id="hr_payroll_futa" model="hr.salary.rule.category">
|
||||
<field name="name">ER: US FUTA Federal Unemployment</field>
|
||||
<field name="code">ER_US_FUTA</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
|
||||
|
||||
<!-- Tax Exempt Deductions -->
|
||||
|
||||
<!-- Deductions that reduce the wage for Federal Income Tax (e.g. 401k) -->
|
||||
<record id="hr_payroll_category_ded_fit_exempt" model="hr.salary.rule.category">
|
||||
<field name="name">Deduction: Federal Income Tax Exempt</field>
|
||||
<field name="code">DED_US_FIT_EXEMPT</field>
|
||||
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
<!-- Generally speaking, deductions to FICA and FUTA should probably reduce GROSS itself, there may be special or rare cases -->
|
||||
<!-- Deductions that reduce the wage for FICA -->
|
||||
<record id="hr_payroll_category_ded_fica_exempt" model="hr.salary.rule.category">
|
||||
<field name="name">Deduction: FICA Exempt</field>
|
||||
<field name="code">DED_FICA_EXEMPT</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<!-- Deductions that reduce the wage for Unemployment Insurance/Tax -->
|
||||
<record id="hr_payroll_category_ded_futa_exempt" model="hr.salary.rule.category">
|
||||
<field name="name">Deduction: FUTA Exempt</field>
|
||||
<field name="code">DED_FUTA_EXEMPT</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
26
l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
Normal file
26
l10n_us_hr_payroll/data/federal/fed_940_futa_parameters.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<record id="rule_parameter_940_futa_wage_base_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 940 FUTA Wage Base</field>
|
||||
<field name="code">fed_940_futa_wage_base</field>
|
||||
<field name="parameter_value">7000.00</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Rate -->
|
||||
<record id="rule_parameter_940_futa_rate_basic_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 940 FUTA Rate Basic</field>
|
||||
<field name="code">fed_940_futa_rate_basic</field>
|
||||
<field name="parameter_value">6.0</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_940_futa_rate_normal_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 940 FUTA Rate Normal</field>
|
||||
<field name="code">fed_940_futa_rate_normal</field>
|
||||
<field name="parameter_value">0.6</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
33
l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
Normal file
33
l10n_us_hr_payroll/data/federal/fed_940_futa_rules.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="res_partner_eftps_940" model="res.partner">
|
||||
<field name="name">US Federal 940 - EFTPS</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_eftps_940" model="hr.contribution.register">
|
||||
<field name="name">EFTPS - 940 (FUTA)</field>
|
||||
<field name="note">Electronic Federal Tax Payment System - Form 940</field>
|
||||
<field name="partner_id" ref="res_partner_eftps_940"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_category_er_fed_940" model="hr.salary.rule.category">
|
||||
<field name="name">ER: Federal 940 FUTA</field>
|
||||
<field name="code">ER_US_940_FUTA</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_er_fed_940" model="hr.salary.rule">
|
||||
<field name="sequence" eval="440"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_fed_940"/>
|
||||
<field name="name">ER: US FUTA Federal Unemployment</field>
|
||||
<field name="code">ER_US_940_FUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = er_us_940_futa(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = er_us_940_futa(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_eftps_940"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
66
l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
Normal file
66
l10n_us_hr_payroll/data/federal/fed_941_fica_parameters.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Social Security -->
|
||||
<!-- Wage Base -->
|
||||
<record id="rule_parameter_941_fica_ss_wage_base_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Social Security Wage Base</field>
|
||||
<field name="code">fed_941_fica_ss_wage_base</field>
|
||||
<field name="parameter_value">128400.0</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fica_ss_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Social Security Wage Base</field>
|
||||
<field name="code">fed_941_fica_ss_wage_base</field>
|
||||
<field name="parameter_value">132900.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fica_ss_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Social Security Wage Base</field>
|
||||
<field name="code">fed_941_fica_ss_wage_base</field>
|
||||
<field name="parameter_value">137700.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Rate -->
|
||||
<record id="rule_parameter_941_fica_ss_rate_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Rate</field>
|
||||
<field name="code">fed_941_fica_ss_rate</field>
|
||||
<field name="parameter_value">6.2</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Medicare -->
|
||||
<!-- Wage Base -->
|
||||
<record id="rule_parameter_941_fica_m_wage_base_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Medicare Wage Base</field>
|
||||
<field name="code">fed_941_fica_m_wage_base</field>
|
||||
<field name="parameter_value">"inf"</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Rate -->
|
||||
<record id="rule_parameter_941_fica_m_rate_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Rate</field>
|
||||
<field name="code">fed_941_fica_m_rate</field>
|
||||
<field name="parameter_value">1.45</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Medicare Additional -->
|
||||
<!-- Wage Base -->
|
||||
<record id="rule_parameter_941_fica_m_add_wage_start_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Medicare Additional Wage Start</field>
|
||||
<field name="code">fed_941_fica_m_add_wage_start</field>
|
||||
<field name="parameter_value">200000.0</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Rate -->
|
||||
<record id="rule_parameter_941_fica_m_add_rate_2016" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FICA Medicare Additional Rate</field>
|
||||
<field name="code">fed_941_fica_m_add_rate</field>
|
||||
<field name="parameter_value">0.9</field>
|
||||
<field name="date_from" eval="datetime(2016, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
95
l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
Normal file
95
l10n_us_hr_payroll/data/federal/fed_941_fica_rules.xml
Normal file
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="res_partner_eftps_941" model="res.partner">
|
||||
<field name="name">US Federal 941 - EFTPS</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_eftps_941" model="hr.contribution.register">
|
||||
<field name="name">EFTPS - 941 (FICA + Federal Witholding)</field>
|
||||
<field name="note">Electronic Federal Tax Payment System - Form 941</field>
|
||||
<field name="partner_id" ref="res_partner_eftps_941"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_category_ee_fed_941" model="hr.salary.rule.category">
|
||||
<field name="name">EE: Federal 941 FICA</field>
|
||||
<field name="code">EE_US_941_FICA</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_category_er_fed_941" model="hr.salary.rule.category">
|
||||
<field name="name">ER: Federal 941 FICA</field>
|
||||
<field name="code">ER_US_941_FICA</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Social Security -->
|
||||
<record id="hr_payroll_rule_ee_fed_941_ss" model="hr.salary.rule">
|
||||
<field name="sequence" eval="190"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_fed_941"/>
|
||||
<field name="name">EE: US FICA Social Security</field>
|
||||
<field name="code">EE_US_941_FICA_SS</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = ee_us_941_fica_ss(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = ee_us_941_fica_ss(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_eftps_941"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_er_fed_941_ss" model="hr.salary.rule">
|
||||
<field name="sequence" eval="440"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_fed_941"/>
|
||||
<field name="name">ER: US FICA Social Security</field>
|
||||
<field name="code">ER_US_941_FICA_SS</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = er_us_941_fica_ss(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = er_us_941_fica_ss(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_eftps_941"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- Medicare -->
|
||||
<record id="hr_payroll_rule_ee_fed_941_m" model="hr.salary.rule">
|
||||
<field name="sequence" eval="190"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_fed_941"/>
|
||||
<field name="name">EE: US FICA Medicare</field>
|
||||
<field name="code">EE_US_941_FICA_M</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = ee_us_941_fica_m(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = ee_us_941_fica_m(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_eftps_941"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_er_fed_941_m" model="hr.salary.rule">
|
||||
<field name="sequence" eval="440"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_fed_941"/>
|
||||
<field name="name">ER: US FICA Medicare</field>
|
||||
<field name="code">ER_US_941_FICA_M</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = er_us_941_fica_m(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = er_us_941_fica_m(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_eftps_941"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- Medicare Additional -->
|
||||
<record id="hr_payroll_rule_ee_fed_941_m_add" model="hr.salary.rule">
|
||||
<field name="sequence" eval="190"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_fed_941"/>
|
||||
<field name="name">EE: US FICA Medicare Additional</field>
|
||||
<field name="code">EE_US_941_FICA_M_ADD</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = ee_us_941_fica_m_add(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = ee_us_941_fica_m_add(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_eftps_941"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
492
l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
Normal file
492
l10n_us_hr_payroll/data/federal/fed_941_fit_parameters.xml
Normal file
@@ -0,0 +1,492 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<!-- Shared -->
|
||||
<record id="rule_parameter_941_fit_allowance_2018" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Allowance</field>
|
||||
<field name="code">fed_941_fit_allowance</field>
|
||||
<!-- Bogus 2018 -->
|
||||
<field name="parameter_value">{
|
||||
'weekly': 80.80,
|
||||
'bi-weekly': 161.50,
|
||||
'semi-monthly': 175.00,
|
||||
'monthly': 350.00,
|
||||
'quarterly': 1050.00,
|
||||
'semi-annually': 2100.00,
|
||||
'annually': 4200.00,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2018, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_allowance_2019" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Allowance</field>
|
||||
<field name="code">fed_941_fit_allowance</field>
|
||||
<field name="parameter_value">{
|
||||
'weekly': 80.80,
|
||||
'bi-weekly': 161.50,
|
||||
'semi-monthly': 175.00,
|
||||
'monthly': 350.00,
|
||||
'quarterly': 1050.00,
|
||||
'semi-annually': 2100.00,
|
||||
'annually': 4200.00,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_allowance_2020" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Allowance</field>
|
||||
<field name="code">fed_941_fit_allowance</field>
|
||||
<!-- Warning, major change to allowance in 2020 -->
|
||||
<field name="parameter_value">4300.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_941_fit_nra_additional_2018" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT NRA Additional</field>
|
||||
<field name="code">fed_941_fit_nra_additional</field>
|
||||
<!-- Bogus from 2018 -->
|
||||
<field name="parameter_value">{
|
||||
'weekly': 153.80,
|
||||
'bi-weekly': 307.70,
|
||||
'semi-monthly': 333.30,
|
||||
'monthly': 666.70,
|
||||
'quarterly': 2000.00,
|
||||
'semi-annually': 4000.00,
|
||||
'annually': 8000.00,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2018, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_nra_additional_2019" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT NRA Additional</field>
|
||||
<field name="code">fed_941_fit_nra_additional</field>
|
||||
<field name="parameter_value">{
|
||||
'weekly': 153.80,
|
||||
'bi-weekly': 307.70,
|
||||
'semi-monthly': 333.30,
|
||||
'monthly': 666.70,
|
||||
'quarterly': 2000.00,
|
||||
'semi-annually': 4000.00,
|
||||
'annually': 8000.00,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_nra_additional_2020" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT NRA Additional</field>
|
||||
<field name="code">fed_941_fit_nra_additional</field>
|
||||
<field name="parameter_value">{
|
||||
'weekly': 238.50,
|
||||
'bi-weekly': 476.90,
|
||||
'semi-monthly': 516.70,
|
||||
'monthly': 1033.30,
|
||||
'quarterly': 3100.00,
|
||||
'semi-annually': 6200.00,
|
||||
'annually': 12400.00,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Single and Married Single Rate -->
|
||||
<record id="rule_parameter_941_fit_table_single_2018" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Table Single</field>
|
||||
<field name="code">fed_941_fit_table_single</field>
|
||||
<!-- Bogus 2018 -->
|
||||
<!-- But-not-over, $, % -->
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2018, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_table_single_2019" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Table Single</field>
|
||||
<field name="code">fed_941_fit_table_single</field>
|
||||
<!-- But-not-over, $, % -->
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_table_single_2020" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Table Single</field>
|
||||
<field name="code">fed_941_fit_table_single</field>
|
||||
<!-- Major changes in 2020 -->
|
||||
<!-- Wage Threshold, Base Withholding Amount, Marginal Rate Over Threshold -->
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Married -->
|
||||
<record id="rule_parameter_941_fit_table_married_2018" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Table Married</field>
|
||||
<field name="code">fed_941_fit_table_married</field>
|
||||
<!-- Bogus 2018 -->
|
||||
<!-- But-not-over, $, % -->
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2018, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_table_married_2019" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Table Married</field>
|
||||
<field name="code">fed_941_fit_table_married</field>
|
||||
<!-- But-not-over, $, % -->
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_941_fit_table_married_2020" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Table Married</field>
|
||||
<field name="code">fed_941_fit_table_married</field>
|
||||
<!-- Major changes in 2020 -->
|
||||
<!-- Wage Threshold, Base Withholding Amount, Marginal Rate Over Threshold -->
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_941_fit_table_hh_2020" model="hr.payroll.rate">
|
||||
<field name="name">Federal 941 FIT Table Head of Household</field>
|
||||
<field name="code">fed_941_fit_table_hh</field>
|
||||
<!-- Major changes in 2020 -->
|
||||
<!-- Wage Threshold, Base Withholding Amount, Marginal Rate Over Threshold -->
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
23
l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
Normal file
23
l10n_us_hr_payroll/data/federal/fed_941_fit_rules.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="hr_payroll_category_ee_fed_941_fit" model="hr.salary.rule.category">
|
||||
<field name="name">EE: Federal 941 Income Tax Withholding</field>
|
||||
<field name="code">EE_US_941_FIT</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_fed_941_fit" model="hr.salary.rule">
|
||||
<field name="sequence" eval="190"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_fed_941_fit"/>
|
||||
<field name="name">EE: US Federal Income Tax Withholding</field>
|
||||
<field name="code">EE_US_941_FIT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = ee_us_941_fit(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = ee_us_941_fit(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_eftps_941"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
61
l10n_us_hr_payroll/data/final.xml
Executable file → Normal file
61
l10n_us_hr_payroll/data/final.xml
Executable file → Normal file
@@ -1,29 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR PAYROLL STRUCTURE -->
|
||||
<record id="hr_payroll_salary_structure_us_employee" model="hr.payroll.structure">
|
||||
<record id="structure_type_employee" model="hr.payroll.structure">
|
||||
<field name="code">US_EMP</field>
|
||||
<field name="name">USA Employee</field>
|
||||
<field eval="[(6, 0, [
|
||||
ref('hr_payroll_rules_fica_comp_ss'),
|
||||
ref('hr_payroll_rules_fica_comp_m'),
|
||||
ref('hr_payroll_rule_er_fed_940'),
|
||||
|
||||
ref('hr_payroll_rules_fica_emp_ss_wages_2018'),
|
||||
ref('hr_payroll_rules_fica_emp_m_wages_2018'),
|
||||
ref('hr_payroll_rules_fica_emp_m_add_wages_2018'),
|
||||
ref('hr_payroll_rules_fica_emp_ss_2018'),
|
||||
ref('hr_payroll_rules_fica_emp_m_2018'),
|
||||
ref('hr_payroll_rules_fica_emp_m_add_2018'),
|
||||
ref('hr_payroll_rules_futa_wages_2018'),
|
||||
ref('hr_payroll_rules_futa_2018'),
|
||||
ref('hr_payroll_rules_fed_inc_withhold_2018_single'),
|
||||
ref('hr_payroll_rules_fed_inc_withhold_2018_married'),
|
||||
ref('hr_payroll_rule_ee_fed_941_ss'),
|
||||
ref('hr_payroll_rule_er_fed_941_ss'),
|
||||
ref('hr_payroll_rule_ee_fed_941_m'),
|
||||
ref('hr_payroll_rule_er_fed_941_m'),
|
||||
ref('hr_payroll_rule_ee_fed_941_m_add'),
|
||||
|
||||
ref('hr_payroll_rule_ee_fed_941_fit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_fl_suta'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_ga_suta'),
|
||||
ref('hr_payroll_rule_ee_us_ga_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_ms_suta'),
|
||||
ref('hr_payroll_rule_ee_us_ms_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_mt_suta'),
|
||||
ref('hr_payroll_rule_er_us_mt_suta_aft'),
|
||||
ref('hr_payroll_rule_ee_us_mt_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_oh_suta'),
|
||||
ref('hr_payroll_rule_ee_us_oh_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_pa_suta'),
|
||||
ref('hr_payroll_rule_ee_us_pa_suta'),
|
||||
ref('hr_payroll_rule_ee_us_pa_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_tx_suta'),
|
||||
ref('hr_payroll_rule_er_us_tx_suta_oa'),
|
||||
ref('hr_payroll_rule_er_us_tx_suta_etia'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_va_suta'),
|
||||
ref('hr_payroll_rule_ee_us_va_sit'),
|
||||
|
||||
ref('hr_payroll_rule_er_us_wa_suta'),
|
||||
ref('hr_payroll_rule_er_us_wa_fml'),
|
||||
ref('hr_payroll_rule_ee_us_wa_fml'),
|
||||
ref('hr_payroll_rule_er_us_wa_lni'),
|
||||
ref('hr_payroll_rule_ee_us_wa_lni'),
|
||||
|
||||
ref('hr_salary_rule_commission'),
|
||||
ref('hr_salary_rule_gamification'),
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="parent_id" ref="hr_payroll.structure_base"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
27
l10n_us_hr_payroll/data/integration_rules.xml
Normal file
27
l10n_us_hr_payroll/data/integration_rules.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Commissions from hr_payroll_commission -->
|
||||
<record id="hr_salary_rule_commission" model="hr.salary.rule">
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.COMMISSION.amount > 0.0 if inputs.COMMISSION else False</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = inputs.COMMISSION.amount if inputs.COMMISSION else 0</field>
|
||||
<field name="code">BASIC_COM</field>
|
||||
<field name="category_id" ref="hr_payroll.BASIC"/>
|
||||
<field name="name">Commissions</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
</record>
|
||||
|
||||
<!-- Badges from hr_payroll_gamification -->
|
||||
<record id="hr_salary_rule_gamification" model="hr.salary.rule">
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.BADGES.amount > 0.0 if inputs.BADGES else False</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = inputs.BADGES.amount if inputs.BADGES else 0</field>
|
||||
<field name="code">BASIC_BADGES</field>
|
||||
<field name="category_id" ref="hr_payroll.BASIC"/>
|
||||
<field name="name">Badges</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- FUTA -->
|
||||
<record id="hr_payroll_rates_futa_exempt" model="hr.payroll.rate">
|
||||
<field name="name">US FUTA Exempt</field>
|
||||
<field name="code">US_FUTA_EXEMPT</field>
|
||||
<field name="rate">0.0</field>
|
||||
<field name="date_from">2016-01-01</field>
|
||||
<field name="wage_limit_year" eval="7000"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rates_futa_normal" model="hr.payroll.rate">
|
||||
<field name="name">US FUTA Normal</field>
|
||||
<field name="code">US_FUTA_NORMAL</field>
|
||||
<field name="rate">0.6</field>
|
||||
<field name="date_from">2016-01-01</field>
|
||||
<field name="wage_limit_year" eval="7000"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rates_futa_basic" model="hr.payroll.rate">
|
||||
<field name="name">US FUTA Basic</field>
|
||||
<field name="code">US_FUTA_BASIC</field>
|
||||
<field name="rate">6.0</field>
|
||||
<field name="date_from">2016-01-01</field>
|
||||
<field name="wage_limit_year" eval="7000"/>
|
||||
</record>
|
||||
|
||||
<!-- FICA -->
|
||||
<!-- Social Security -->
|
||||
<record id="hr_payroll_rates_fica_ss_old" model="hr.payroll.rate">
|
||||
<field name="name">US FICA Social Security</field>
|
||||
<field name="code">US_FICA_SS</field>
|
||||
<field name="rate">6.2</field>
|
||||
<field name="date_from">2016-01-01</field>
|
||||
<field name="date_to">2017-12-31</field>
|
||||
<field name="wage_limit_year" eval="128400.0"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rates_fica_ss_2018" model="hr.payroll.rate">
|
||||
<field name="name">US FICA Social Security</field>
|
||||
<field name="code">US_FICA_SS</field>
|
||||
<field name="rate">6.2</field>
|
||||
<field name="date_from">2018-01-01</field>
|
||||
<field name="date_to">2018-12-31</field>
|
||||
<field name="wage_limit_year" eval="128400.0"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rates_fica_ss_2019" model="hr.payroll.rate">
|
||||
<field name="name">US FICA Social Security</field>
|
||||
<field name="code">US_FICA_SS</field>
|
||||
<field name="rate">6.2</field>
|
||||
<field name="date_from">2019-01-01</field>
|
||||
<field name="date_to">2019-12-31</field>
|
||||
<field name="wage_limit_year" eval="132900.0"/>
|
||||
</record>
|
||||
<!-- Medicare -->
|
||||
<record id="hr_payroll_rates_fica_m" model="hr.payroll.rate">
|
||||
<field name="name">US FICA Medicare</field>
|
||||
<field name="code">US_FICA_M</field>
|
||||
<field name="rate">1.45</field>
|
||||
<field name="date_from">2016-01-01</field>
|
||||
</record>
|
||||
<!-- Medicare Additional -->
|
||||
<record id="hr_payroll_rates_fica_m_add" model="hr.payroll.rate">
|
||||
<field name="name">US FICA Medicare Additional</field>
|
||||
<field name="code">US_FICA_M_ADD</field>
|
||||
<field name="rate">0.9</field>
|
||||
<field name="date_from">2016-01-01</field>
|
||||
<field name="wage_limit_year">200000.0</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
61
l10n_us_hr_payroll/data/state/fl_florida.xml
Normal file
61
l10n_us_hr_payroll/data/state/fl_florida.xml
Normal file
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_fl_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US FL Florida SUTA Wage Base</field>
|
||||
<field name="code">us_fl_suta_wage_base</field>
|
||||
<field name="parameter_value">7000.00</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_fl_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US FL Florida SUTA Wage Base</field>
|
||||
<field name="code">us_fl_suta_wage_base</field>
|
||||
<field name="parameter_value">7000.00</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_fl_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US FL Florida SUTA Rate</field>
|
||||
<field name="code">us_fl_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_fl_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US FL Florida SUTA Rate</field>
|
||||
<field name="code">us_fl_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_fl_dor" model="res.partner">
|
||||
<field name="name">US Florida - Department of Revenue</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_fl_dor" model="hr.contribution.register">
|
||||
<field name="name">US Florida - Department of Revenue (RT-6)</field>
|
||||
<field name="partner_id" ref="res_partner_us_fl_dor"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_fl_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US FL Florida State Unemployment (RT-6)</field>
|
||||
<field name="code">ER_US_FL_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_fl_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
265
l10n_us_hr_payroll/data/state/ga_georgia.xml
Normal file
265
l10n_us_hr_payroll/data/state/ga_georgia.xml
Normal file
@@ -0,0 +1,265 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ga_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SUTA Wage Base</field>
|
||||
<field name="code">us_ga_suta_wage_base</field>
|
||||
<field name="parameter_value">9500.00</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_ga_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SUTA Wage Base</field>
|
||||
<field name="code">us_ga_suta_wage_base</field>
|
||||
<field name="parameter_value">9500.00</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ga_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SUTA Rate</field>
|
||||
<field name="code">us_ga_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_ga_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SUTA Rate</field>
|
||||
<field name="code">us_ga_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ga_sit_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SIT Rate Table</field>
|
||||
<field name="code">us_ga_sit_rate</field>
|
||||
<field name="parameter_value">{
|
||||
'married filing joint, both spouses working': {
|
||||
'weekly': ((9.50, 0.00, 1.00), (29.00, .10, 2.00), (48.00, .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, .19, 2.00), (96.00, .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, .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, .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, .19, 2.00), (96.00, .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, .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, .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, .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, .14, 2.00), (72.00, .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, .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, .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, .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, .19, 2.00), (96.00, .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, .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, .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, .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, .10, 2.00), (48.00, .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, .19, 2.00), (96.00, .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, .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, .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)),
|
||||
},
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ga_sit_personal_allowance_2019" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SIT Personal Allowance</field>
|
||||
<field name="code">us_ga_sit_personal_allowance</field>
|
||||
<field name="parameter_value">{
|
||||
'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,
|
||||
},
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ga_sit_dependent_allowance_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SIT Dependent Allowance Rate</field>
|
||||
<field name="code">us_ga_sit_dependent_allowance_rate</field>
|
||||
<field name="parameter_value">{
|
||||
'weekly': 57.50,
|
||||
'bi-weekly': 115.00,
|
||||
'semi-monthly': 125.00,
|
||||
'monthly': 250.00,
|
||||
'quarterly': 750.00,
|
||||
'semi-annual': 1500.00,
|
||||
'annual': 3000.00,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ga_sit_deduction_2019" model="hr.payroll.rate">
|
||||
<field name="name">US GA Georgia SIT Deduction</field>
|
||||
<field name="code">us_ga_sit_deduction</field>
|
||||
<field name="parameter_value">{
|
||||
'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,
|
||||
},
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_ga_dor" model="res.partner">
|
||||
<field name="name">US Georgia - Department of Taxation - Unemployment Tax</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_ga_dor" model="hr.contribution.register">
|
||||
<field name="name">US Pennsylvania - Department of Revenue - Unemployment Tax</field>
|
||||
<field name="partner_id" ref="res_partner_us_ga_dor"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_ga_dor_sit" model="res.partner">
|
||||
<field name="name">US Georgia - Department of Taxation - Income Tax</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_ga_dor_sit" model="hr.contribution.register">
|
||||
<field name="name">US Pennsylvania - Department of Revenue - Unemployment Tax</field>
|
||||
<field name="partner_id" ref="res_partner_us_ga_dor_sit"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_ga_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US GA Georgia State Unemployment</field>
|
||||
<field name="code">ER_US_GA_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_ga_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_ga_sit" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_us_sit"/>
|
||||
<field name="name">EE: US GA Georgia State Income Tax Withholding</field>
|
||||
<field name="code">EE_US_GA_SIT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = ga_georgia_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = ga_georgia_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_us_ga_dor_sit"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
120
l10n_us_hr_payroll/data/state/ms_mississippi.xml
Normal file
120
l10n_us_hr_payroll/data/state/ms_mississippi.xml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ms_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MS Mississippi SUTA Wage Base</field>
|
||||
<field name="code">us_ms_suta_wage_base</field>
|
||||
<field name="parameter_value">14000.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_ms_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US MS Mississippi SUTA Wage Base</field>
|
||||
<field name="code">us_ms_suta_wage_base</field>
|
||||
<field name="parameter_value">14000.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ms_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MS Mississippi SUTA Rate</field>
|
||||
<field name="code">us_ms_suta_rate</field>
|
||||
<field name="parameter_value">1.2</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_ms_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US MS Mississippi SUTA Rate</field>
|
||||
<field name="code">us_ms_suta_rate</field>
|
||||
<field name="parameter_value">1.2</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ms_sit_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MS Mississippi SIT Rate Table</field>
|
||||
<field name="code">us_ms_sit_rate</field>
|
||||
<field name="parameter_value">[
|
||||
( 10000.00, 290.0, 0.05),
|
||||
( 5000.00, 90.0, 0.04),
|
||||
( 2000.00, 0.0, 0.03),
|
||||
]</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_ms_sit_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US MS Mississippi SIT Rate Table</field>
|
||||
<field name="code">us_ms_sit_rate</field>
|
||||
<field name="parameter_value">[
|
||||
( 10000.00, 260.0, 0.05),
|
||||
( 5000.00, 60.0, 0.04),
|
||||
( 3000.00, 0.0, 0.03),
|
||||
]</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_ms_sit_deduction_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MS Mississippi SIT Deduction</field>
|
||||
<field name="code">us_ms_sit_deduction</field>
|
||||
<field name="parameter_value">{
|
||||
'single': 2300.0,
|
||||
'head_of_household': 3400.0,
|
||||
'married_dual': 2300.0,
|
||||
'married': 4600.0,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_ms_dor" model="res.partner">
|
||||
<field name="name">US Mississippi - Department of Employment Security (Unemployment)</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_ms_dor" model="hr.contribution.register">
|
||||
<field name="name">US Mississippi - Department of Employment Security (Unemployment)</field>
|
||||
<field name="partner_id" ref="res_partner_us_ms_dor"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_ms_dor_sit" model="res.partner">
|
||||
<field name="name">US Mississippi - Mississippi Department of Revenue (Income Tax)</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_ms_dor_sit" model="hr.contribution.register">
|
||||
<field name="name">US Mississippi - Mississippi Department of Revenue (Income Tax)</field>
|
||||
<field name="partner_id" ref="res_partner_us_ms_dor_sit"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_ms_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US MS Mississippi State Unemployment</field>
|
||||
<field name="code">ER_US_MS_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_ms_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_ms_sit" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_us_sit"/>
|
||||
<field name="name">EE: US MS Mississippi State Income Tax Withholding</field>
|
||||
<field name="code">EE_US_MS_SIT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = ms_mississippi_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = ms_mississippi_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_us_ms_dor_sit"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
166
l10n_us_hr_payroll/data/state/mt_montana.xml
Normal file
166
l10n_us_hr_payroll/data/state/mt_montana.xml
Normal file
@@ -0,0 +1,166 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_mt_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SUTA Wage Base</field>
|
||||
<field name="code">us_mt_suta_wage_base</field>
|
||||
<field name="parameter_value">33000.00</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_mt_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SUTA Wage Base</field>
|
||||
<field name="code">us_mt_suta_wage_base</field>
|
||||
<field name="parameter_value">34100.00</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_mt_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SUTA Rate (UI)</field>
|
||||
<field name="code">us_mt_suta_rate</field>
|
||||
<field name="parameter_value">1.18</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_mt_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SUTA Rate (UI)</field>
|
||||
<field name="code">us_mt_suta_rate</field>
|
||||
<field name="parameter_value">1.18</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_mt_suta_aft_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SUTA Administrative Fund Tax Rate</field>
|
||||
<field name="code">us_mt_suta_aft_rate</field>
|
||||
<field name="parameter_value">0.13</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_mt_suta_aft_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SUTA Administrative Fund Tax Rate</field>
|
||||
<field name="code">us_mt_suta_aft_rate</field>
|
||||
<field name="parameter_value">0.13</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_mt_sit_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SIT Rate Table</field>
|
||||
<field name="code">us_mt_sit_rate</field>
|
||||
<field name="parameter_value">{
|
||||
'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),
|
||||
],
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_mt_sit_exemption_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US MT Montana SIT Exemption Rate Table</field>
|
||||
<field name="code">us_mt_sit_exemption_rate</field>
|
||||
<field name="parameter_value">{
|
||||
'weekly': 37.0,
|
||||
'bi-weekly': 73.0,
|
||||
'semi-monthly': 79.0,
|
||||
'monthly': 158.0,
|
||||
'annually': 1900.0,
|
||||
}</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_mt_dor" model="res.partner">
|
||||
<field name="name">US Montana - Department of Labor & Industries</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_mt_dor" model="hr.contribution.register">
|
||||
<field name="name">US Montana - Department of Labor & Industries</field>
|
||||
<field name="partner_id" ref="res_partner_us_mt_dor"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_mt_dor_sit" model="res.partner">
|
||||
<field name="name">US Montana - Department of Revenue - Income Tax</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_mt_dor_sit" model="hr.contribution.register">
|
||||
<field name="name">US Montana - Department of Revenue - Income Tax</field>
|
||||
<field name="partner_id" ref="res_partner_us_mt_dor_sit"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_mt_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US MT Montana State Unemployment (UI-5)</field>
|
||||
<field name="code">ER_US_MT_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_mt_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_er_us_mt_suta_aft" model="hr.salary.rule">
|
||||
<field name="sequence" eval="451"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US MT Montana State Unemployment Administrative Fund Tax (AFT) (UI-5)</field>
|
||||
<field name="code">ER_US_MT_SUTA_AFT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_mt_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_mt_sit" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_us_sit"/>
|
||||
<field name="name">EE: US MT Montana State Income Tax Withholding (MW-3)</field>
|
||||
<field name="code">EE_US_MT_SIT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = mt_montana_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = mt_montana_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_us_mt_dor_sit"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
150
l10n_us_hr_payroll/data/state/oh_ohio.xml
Normal file
150
l10n_us_hr_payroll/data/state/oh_ohio.xml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_oh_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SUTA Wage Base</field>
|
||||
<field name="code">us_oh_suta_wage_base</field>
|
||||
<field name="parameter_value">9500.00</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_oh_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SUTA Wage Base</field>
|
||||
<field name="code">us_oh_suta_wage_base</field>
|
||||
<field name="parameter_value">9000.00</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_oh_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SUTA Rate</field>
|
||||
<field name="code">us_oh_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_oh_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SUTA Rate</field>
|
||||
<field name="code">us_oh_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_oh_sit_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SIT Rate Table</field>
|
||||
<field name="code">us_oh_sit_rate</field>
|
||||
<!-- https://www.tax.ohio.gov/Portals/0/employer_withholding/2019%20tables/WTH_OptionalComputerFormula_2019.pdf -->
|
||||
<!-- wage_less_than, base_amount, rate_over -->
|
||||
<field name="parameter_value">[
|
||||
( 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),
|
||||
]</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_oh_sit_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SIT Rate Table</field>
|
||||
<field name="code">us_oh_sit_rate</field>
|
||||
<!-- https://www.tax.ohio.gov/Portals/0/employer_withholding/2020%20tables/WTH_OptionalComputerFormula_2020.pdf -->
|
||||
<!-- wage_less_than, base_amount, rate_over -->
|
||||
<field name="parameter_value">[
|
||||
( 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),
|
||||
]</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_oh_sit_exemption_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SIT Exemption Rate</field>
|
||||
<field name="code">us_oh_sit_exemption_rate</field>
|
||||
<field name="parameter_value">650.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_oh_sit_exemption_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SIT Exemption Rate</field>
|
||||
<field name="code">us_oh_sit_exemption_rate</field>
|
||||
<field name="parameter_value">650.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_oh_sit_multiplier_2019" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SIT Multiplier Value</field>
|
||||
<field name="code">us_oh_sit_multiplier</field>
|
||||
<field name="parameter_value">1.075</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_oh_sit_multiplier_2020" model="hr.payroll.rate">
|
||||
<field name="name">US OH Ohio SIT Multiplier Value</field>
|
||||
<field name="code">us_oh_sit_multiplier</field>
|
||||
<field name="parameter_value">1.032</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_oh_dor" model="res.partner">
|
||||
<field name="name">US Ohio - OBG - Unemployment</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_oh_dor" model="hr.contribution.register">
|
||||
<field name="name">US Ohio - OBG - Unemployment</field>
|
||||
<field name="partner_id" ref="res_partner_us_oh_dor"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_oh_dor_sit" model="res.partner">
|
||||
<field name="name">US Ohio - OBG - Income Withholding</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_oh_dor_sit" model="hr.contribution.register">
|
||||
<field name="name">US Ohio - OBG - Income Withholding</field>
|
||||
<field name="partner_id" ref="res_partner_us_oh_dor_sit"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_oh_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US OH Ohio State Unemployment (JFS-20125)</field>
|
||||
<field name="code">ER_US_OH_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_oh_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_oh_sit" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_us_sit"/>
|
||||
<field name="name">EE: US OH Ohio State Income Tax Withholding (IT 501)</field>
|
||||
<field name="code">EE_US_OH_SIT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = oh_ohio_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = oh_ohio_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_us_oh_dor_sit"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
126
l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
Normal file
126
l10n_us_hr_payroll/data/state/pa_pennsylvania.xml
Normal file
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_pa_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SUTA Wage Base (ER)</field>
|
||||
<field name="code">us_pa_suta_wage_base</field>
|
||||
<field name="parameter_value">10000.00</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_pa_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SUTA Wage Base (ER)</field>
|
||||
<field name="code">us_pa_suta_wage_base</field>
|
||||
<field name="parameter_value">10000.00</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_pa_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SUTA Rate</field>
|
||||
<field name="code">us_pa_suta_rate</field>
|
||||
<field name="parameter_value">3.6890</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_pa_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SUTA Rate</field>
|
||||
<field name="code">us_pa_suta_rate</field>
|
||||
<field name="parameter_value">3.6890</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_pa_suta_ee_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SUTA Employee Rate</field>
|
||||
<field name="code">us_pa_suta_ee_rate</field>
|
||||
<field name="parameter_value">0.06</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_pa_suta_ee_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SUTA Employee Rate</field>
|
||||
<field name="code">us_pa_suta_ee_rate</field>
|
||||
<field name="parameter_value">0.06</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_pa_sit_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SIT Rate</field>
|
||||
<field name="code">us_pa_sit_rate</field>
|
||||
<field name="parameter_value">3.07</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_pa_sit_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US PA Pennsylvania SIT Rate</field>
|
||||
<field name="code">us_pa_sit_rate</field>
|
||||
<field name="parameter_value">3.07</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_pa_dor" model="res.partner">
|
||||
<field name="name">US Pennsylvania - Department of Revenue - Unemployment Tax</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_pa_dor" model="hr.contribution.register">
|
||||
<field name="name">US Pennsylvania - Department of Revenue - Unemployment Tax</field>
|
||||
<field name="partner_id" ref="res_partner_us_pa_dor"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_pa_dor_sit" model="res.partner">
|
||||
<field name="name">US Pennsylvania - Department of Revenue - Income Tax</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_pa_dor_sit" model="hr.contribution.register">
|
||||
<field name="name">US Pennsylvania - Department of Revenue - Income Tax</field>
|
||||
<field name="partner_id" ref="res_partner_us_pa_dor_sit"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_pa_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US PA Pennsylvania State Unemployment (UC-2)</field>
|
||||
<field name="code">ER_US_PA_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_pa_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_pa_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_us_suta"/>
|
||||
<field name="name">EE: US PA Pennsylvania State Unemployment (UC-2)</field>
|
||||
<field name="code">EE_US_PA_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, rate='us_pa_suta_ee_rate', state_code='PA')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, rate='us_pa_suta_ee_rate', state_code='PA')</field>
|
||||
<field name="register_id" ref="contrib_register_us_pa_dor"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_pa_sit" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_us_sit"/>
|
||||
<field name="name">EE: US PA Pennsylvania State Income Tax Withholding (PA-501)</field>
|
||||
<field name="code">EE_US_PA_SIT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = general_state_income_withholding(payslip, categories, worked_days, inputs, rate='us_pa_sit_rate', state_code='PA')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = general_state_income_withholding(payslip, categories, worked_days, inputs, rate='us_pa_sit_rate', state_code='PA')</field>
|
||||
<field name="register_id" ref="contrib_register_us_pa_dor_sit"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
117
l10n_us_hr_payroll/data/state/tx_texas.xml
Normal file
117
l10n_us_hr_payroll/data/state/tx_texas.xml
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_tx_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas SUTA Wage Base</field>
|
||||
<field name="code">us_tx_suta_wage_base</field>
|
||||
<field name="parameter_value">9000.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_tx_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas SUTA Wage Base</field>
|
||||
<field name="code">us_tx_suta_wage_base</field>
|
||||
<field name="parameter_value">9000.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_tx_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas SUTA Rate</field>
|
||||
<field name="code">us_tx_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_tx_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas SUTA Rate</field>
|
||||
<field name="code">us_tx_suta_rate</field>
|
||||
<field name="parameter_value">2.7</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_tx_suta_oa_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas Obligation Assessment Rate</field>
|
||||
<field name="code">us_tx_suta_oa_rate</field>
|
||||
<field name="parameter_value">0.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_tx_suta_oa_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas Obligation Assessment Rate</field>
|
||||
<field name="code">us_tx_suta_oa_rate</field>
|
||||
<field name="parameter_value">0.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_tx_suta_etia_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas Employment & Training Investment Assessment Rate</field>
|
||||
<field name="code">us_tx_suta_etia_rate</field>
|
||||
<field name="parameter_value">0.1</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_tx_suta_etia_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US TX Texas Employment & Training Investment Assessment Rate</field>
|
||||
<field name="code">us_tx_suta_etia_rate</field>
|
||||
<field name="parameter_value">0.1</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_tx_dor" model="res.partner">
|
||||
<field name="name">US Texas - Workforce Commission (Unemployment)</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_tx_dor" model="hr.contribution.register">
|
||||
<field name="name">US Texas - Workforce Commission (Unemployment)</field>
|
||||
<field name="partner_id" ref="res_partner_us_tx_dor"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_tx_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US TX Texas State Unemployment (C-3)</field>
|
||||
<field name="code">ER_US_TX_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_tx_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_er_us_tx_suta_oa" model="hr.salary.rule">
|
||||
<field name="sequence" eval="451"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US TX Texas Obligation Assessment (C-3)</field>
|
||||
<field name="code">ER_US_TX_SUTA_OA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_tx_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_er_us_tx_suta_etia" model="hr.salary.rule">
|
||||
<field name="sequence" eval="451"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US TX Texas Employment & Training Investment Assessment (C-3)</field>
|
||||
<field name="code">ER_US_TX_SUTA_ETIA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_tx_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
124
l10n_us_hr_payroll/data/state/va_virginia.xml
Normal file
124
l10n_us_hr_payroll/data/state/va_virginia.xml
Normal file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_va_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SUTA Wage Base</field>
|
||||
<field name="code">us_va_suta_wage_base</field>
|
||||
<field name="parameter_value">8000.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_va_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SUTA Wage Base</field>
|
||||
<field name="code">us_va_suta_wage_base</field>
|
||||
<field name="parameter_value">8000.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_va_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SUTA Rate</field>
|
||||
<field name="code">us_va_suta_rate</field>
|
||||
<field name="parameter_value">2.51</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_va_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SUTA Rate</field>
|
||||
<field name="code">us_va_suta_rate</field>
|
||||
<field name="parameter_value">2.51</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_va_sit_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SIT Rate Table</field>
|
||||
<field name="code">us_va_sit_rate</field>
|
||||
<field name="parameter_value">[
|
||||
( 0.00, 0.0, 2.00),
|
||||
( 3000.00, 60.0, 3.00),
|
||||
( 5000.00, 120.0, 5.00),
|
||||
( 17000.00, 720.0, 5.75),
|
||||
]</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_va_sit_exemption_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SIT Exemption Rate Table</field>
|
||||
<field name="code">us_va_sit_exemption_rate</field>
|
||||
<field name="parameter_value">930.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_va_sit_other_exemption_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SIT Other Exemption Rate Table</field>
|
||||
<field name="code">us_va_sit_other_exemption_rate</field>
|
||||
<field name="parameter_value">800.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_va_sit_deduction_2019" model="hr.payroll.rate">
|
||||
<field name="name">US VA Virginia SIT Deduction</field>
|
||||
<field name="code">us_va_sit_deduction</field>
|
||||
<field name="parameter_value">4500.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_va_dor" model="res.partner">
|
||||
<field name="name">US Virginia - Department of Taxation - Unemployment Tax</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_va_dor" model="hr.contribution.register">
|
||||
<field name="name">US Virginia - Department of Taxation - Unemployment Tax</field>
|
||||
<field name="partner_id" ref="res_partner_us_va_dor"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_va_dor_sit" model="res.partner">
|
||||
<field name="name">US Virginia - Department of Taxation - Income Tax</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_va_dor_sit" model="hr.contribution.register">
|
||||
<field name="name">US Virginia - Department of Taxation - Income Tax</field>
|
||||
<field name="partner_id" ref="res_partner_us_va_dor_sit"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_va_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US VA Virginia State Unemployment</field>
|
||||
<field name="code">ER_US_VA_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_va_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_va_sit" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_us_sit"/>
|
||||
<field name="name">EE: US VA Virginia State Income Tax Withholding</field>
|
||||
<field name="code">EE_US_VA_SIT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = va_virginia_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = va_virginia_state_income_withholding(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_us_va_dor_sit"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
203
l10n_us_hr_payroll/data/state/wa_washington.xml
Normal file
203
l10n_us_hr_payroll/data/state/wa_washington.xml
Normal file
@@ -0,0 +1,203 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Wage Base -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_wa_suta_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington SUTA Wage Base</field>
|
||||
<field name="code">us_wa_suta_wage_base</field>
|
||||
<field name="parameter_value">49800.0</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_wa_suta_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington SUTA Wage Base</field>
|
||||
<field name="code">us_wa_suta_wage_base</field>
|
||||
<field name="parameter_value">52700.00</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_wa_fml_wage_base_2019" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Wage Base</field>
|
||||
<field name="code">us_wa_fml_wage_base</field>
|
||||
<field name="parameter_value">132900.00</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_wa_fml_wage_base_2020" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Wage Base</field>
|
||||
<field name="code">us_wa_fml_wage_base</field>
|
||||
<field name="parameter_value">137700.00</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Rate -->
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_wa_suta_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington SUTA Rate</field>
|
||||
<field name="code">us_wa_suta_rate</field>
|
||||
<field name="parameter_value">1.18</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_wa_suta_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington SUTA Rate</field>
|
||||
<field name="code">us_wa_suta_rate</field>
|
||||
<field name="parameter_value">1.0</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_wa_fml_rate_2019" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Rate (Total)</field>
|
||||
<field name="code">us_wa_fml_rate</field>
|
||||
<field name="parameter_value">0.4</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_wa_fml_rate_2020" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Rate (Total)</field>
|
||||
<field name="code">us_wa_fml_rate</field>
|
||||
<field name="parameter_value">0.4</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_wa_fml_rate_ee_2019" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Rate (Employee)</field>
|
||||
<field name="code">us_wa_fml_rate_ee</field>
|
||||
<field name="parameter_value">66.33</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_wa_fml_rate_ee_2020" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Rate (Employee)</field>
|
||||
<field name="code">us_wa_fml_rate_ee</field>
|
||||
<field name="parameter_value">66.33</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record id="rule_parameter_us_wa_fml_rate_er_2019" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Rate (Employer)</field>
|
||||
<field name="code">us_wa_fml_rate_er</field>
|
||||
<field name="parameter_value">33.67</field>
|
||||
<field name="date_from" eval="datetime(2019, 1, 1).date()"/>
|
||||
</record>
|
||||
<record id="rule_parameter_us_wa_fml_rate_er_2020" model="hr.payroll.rate">
|
||||
<field name="name">US WA Washington FML Rate (Employer)</field>
|
||||
<field name="code">us_wa_fml_rate_er</field>
|
||||
<field name="parameter_value">33.67</field>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Partners and Contribution Registers -->
|
||||
<record id="res_partner_us_wa_dor" model="res.partner">
|
||||
<field name="name">US Washington - Employment Security Department (Unemployment)</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_wa_dor" model="hr.contribution.register">
|
||||
<field name="name">US Washington - Employment Security Department (Unemployment)</field>
|
||||
<field name="partner_id" ref="res_partner_us_wa_dor"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_wa_dor_lni" model="res.partner">
|
||||
<field name="name">US Washington - Department of Labor & Industries</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_wa_dor_lni" model="hr.contribution.register">
|
||||
<field name="name">US Washington - Department of Labor & Industries</field>
|
||||
<field name="partner_id" ref="res_partner_us_wa_dor_lni"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_us_wa_dor_fml" model="res.partner">
|
||||
<field name="name">US Washington - Employment Security Department (PFML)</field>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_us_wa_dor_fml" model="hr.contribution.register">
|
||||
<field name="name">US Washington - Employment Security Department (PFML)</field>
|
||||
<field name="partner_id" ref="res_partner_us_wa_dor_fml"/>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
<!-- Rules -->
|
||||
<record id="hr_payroll_rule_er_us_wa_suta" model="hr.salary.rule">
|
||||
<field name="sequence" eval="450"/>
|
||||
<field name="category_id" ref="hr_payroll_category_er_us_suta"/>
|
||||
<field name="name">ER: US WA Washington State Unemployment (5208A/B)</field>
|
||||
<field name="code">ER_US_WA_SUTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_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')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">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')</field>
|
||||
<field name="register_id" ref="contrib_register_us_wa_dor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_er_us_wa_fml" model="hr.salary.rule">
|
||||
<field name="sequence" eval="451"/>
|
||||
<field name="category_id" ref="hr_payroll.COMP"/>
|
||||
<field name="name">ER: US WA Washington State Family Medical Leave</field>
|
||||
<field name="code">ER_US_WA_FML</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = wa_washington_fml_er(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = wa_washington_fml_er(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_us_wa_dor_fml"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_wa_fml" model="hr.salary.rule">
|
||||
<field name="sequence" eval="196"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="name">EE: US WA Washington State Family Medical Leave</field>
|
||||
<field name="code">EE_US_WA_FML</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result, _ = wa_washington_fml_ee(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = wa_washington_fml_ee(payslip, categories, worked_days, inputs)</field>
|
||||
<field name="register_id" ref="contrib_register_us_wa_dor_fml"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- LNI May need to be updated depending on hours worked (or drywall laid) -->
|
||||
<record id="hr_payroll_rule_er_us_wa_lni" model="hr.salary.rule">
|
||||
<field name="sequence" eval="451"/>
|
||||
<field name="category_id" ref="hr_payroll.COMP"/>
|
||||
<field name="name">ER: US WA Washington State LNI</field>
|
||||
<field name="code">ER_US_WA_LNI</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = is_us_state(payslip, 'WA') and payslip.contract_id.us_payroll_config_value('workers_comp_er_code') and worked_days.WORK100 and worked_days.WORK100.number_of_hours and payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
hours = worked_days.WORK100.number_of_hours
|
||||
rate = payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_er_code'))
|
||||
try:
|
||||
# Redo employee withholding calculation
|
||||
ee_withholding = worked_days.WORK100.number_of_hours * -payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code')) / 100.0
|
||||
except:
|
||||
ee_withholding = 0.0
|
||||
er_withholding = -(hours * (rate / 100.0)) - ee_withholding
|
||||
result = hours
|
||||
result_rate = (er_withholding / hours) * 100.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_us_wa_dor_lni"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_us_wa_lni" model="hr.salary.rule">
|
||||
<field name="sequence" eval="196"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="name">EE: US WA Washington State LNI</field>
|
||||
<field name="code">EE_US_WA_LNI</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = is_us_state(payslip, 'WA') and payslip.contract_id.us_payroll_config_value('workers_comp_ee_code') and worked_days.WORK100 and worked_days.WORK100.number_of_hours and payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = worked_days.WORK100.number_of_hours, -payslip.dict.rule_parameter(payslip.contract_id.us_payroll_config_value('workers_comp_ee_code'))</field>
|
||||
<field name="register_id" ref="contrib_register_us_wa_dor_lni"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,93 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.l10n_us_hr_payroll.migrations.data import FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020, \
|
||||
XMLIDS_COPY_ACCOUNTING_2020
|
||||
from odoo.addons.l10n_us_hr_payroll.migrations.helper import field_exists, \
|
||||
temp_field_exists, \
|
||||
remove_temp_field, \
|
||||
temp_field_values
|
||||
|
||||
|
||||
from odoo import SUPERUSER_ID
|
||||
from odoo.api import Environment
|
||||
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def migrate(cr, installed_version):
|
||||
fields_to_move = [f for f in FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020 if temp_field_exists(cr, 'hr_contract', f)]
|
||||
if not fields_to_move:
|
||||
_logger.warn(' migration aborted because no temporary fields exist...')
|
||||
return
|
||||
|
||||
env = Environment(cr, SUPERUSER_ID, {})
|
||||
new_structure = env.ref('l10n_us_hr_payroll.structure_type_employee')
|
||||
|
||||
def get_state(code, cache={}):
|
||||
country_key = 'US_COUNTRY'
|
||||
if code in cache:
|
||||
return cache[code]
|
||||
if country_key not in cache:
|
||||
cache[country_key] = env.ref('base.us')
|
||||
us_country = cache[country_key]
|
||||
us_state = env['res.country.state'].search([
|
||||
('country_id', '=', us_country.id),
|
||||
('code', '=', code),
|
||||
], limit=1)
|
||||
cache[code] = us_state
|
||||
return us_state
|
||||
|
||||
# We will assume all contracts without a struct (because we deleted it), or with one like US_xx_EMP, need config
|
||||
contracts = env['hr.contract'].search([
|
||||
('employee_id', '!=', False),
|
||||
'|',
|
||||
('struct_id', '=', False),
|
||||
('struct_id.code', '=like', 'US_%'),
|
||||
])
|
||||
_logger.warn('Migrating Contracts: ' + str(contracts))
|
||||
for contract in contracts:
|
||||
_logger.warn('Migrating contract: ' + str(contract) + ' for employee: ' + str(contract.employee_id))
|
||||
if not contract.employee_id.id:
|
||||
_logger.warn(' unable to migrate for missing employee id')
|
||||
continue
|
||||
# Could we somehow detect the state off of the current/orphaned salary structure?
|
||||
state_code = False
|
||||
old_struct_code = contract.struct_id.code
|
||||
if old_struct_code:
|
||||
state_code = old_struct_code.split('_')[1]
|
||||
temp_values = temp_field_values(cr, 'hr_contract', contract.id, fields_to_move)
|
||||
# Resolve mapping to the new field names.
|
||||
values = {FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020[k]: v for k, v in temp_values.items()}
|
||||
values.update({
|
||||
'name': 'MIG: ' + str(contract.name),
|
||||
'employee_id': contract.employee_id.id,
|
||||
'state_id': get_state(state_code).id,
|
||||
})
|
||||
us_payroll_config = env['hr.contract.us_payroll_config'].create(values)
|
||||
contract.write({
|
||||
'struct_id': new_structure.id,
|
||||
'us_payroll_config_id': us_payroll_config.id,
|
||||
})
|
||||
|
||||
for field in fields_to_move:
|
||||
remove_temp_field(cr, 'hr_contract', field)
|
||||
|
||||
# Some added rules should have the same accounting side effects of other migrated rules
|
||||
# To ease the transition, we will copy the accounting fields from one to the other.
|
||||
rule_model = env['hr.salary.rule']
|
||||
if hasattr(rule_model, 'account_debit'):
|
||||
for source, destinations in XMLIDS_COPY_ACCOUNTING_2020.items():
|
||||
source_rule = env.ref(source, raise_if_not_found=False)
|
||||
if source_rule:
|
||||
for destination in destinations:
|
||||
destination_rule = env.ref(destination, raise_if_not_found=False)
|
||||
if destination_rule:
|
||||
_logger.warn('Mirgrating accounting from rule: ' + source + ' to rule: ' + destination)
|
||||
destination_rule.write({
|
||||
'account_debit': source_rule.account_debit.id,
|
||||
'account_credit': source_rule.account_credit.id,
|
||||
'account_tax_id': source_rule.account_tax_id.id,
|
||||
'analytic_account_id': source_rule.analytic_account_id.id,
|
||||
})
|
||||
29
l10n_us_hr_payroll/migrations/11.0.2020.1.0/pre-migration.py
Normal file
29
l10n_us_hr_payroll/migrations/11.0.2020.1.0/pre-migration.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.l10n_us_hr_payroll.migrations.data import FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020, \
|
||||
XMLIDS_TO_REMOVE_2020, \
|
||||
XMLIDS_TO_RENAME_2020
|
||||
from odoo.addons.l10n_us_hr_payroll.migrations.helper import field_exists, \
|
||||
temp_field_exists, \
|
||||
make_temp_field, \
|
||||
remove_xmlid, \
|
||||
rename_xmlid
|
||||
|
||||
|
||||
def migrate(cr, installed_version):
|
||||
# Add temporary columns for all hr_contract fields that move to hr_contract_us_payroll_config
|
||||
fields_to_move = [f for f in FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020 if field_exists(cr, 'hr_contract', f)]
|
||||
# Prevent error if repeatedly running and already copied.
|
||||
fields_to_move = [f for f in fields_to_move if not temp_field_exists(cr, 'hr_contract', f)]
|
||||
for field in fields_to_move:
|
||||
make_temp_field(cr, 'hr_contract', field)
|
||||
|
||||
# Need to migrate XMLIDs..
|
||||
for xmlid in XMLIDS_TO_REMOVE_2020:
|
||||
remove_xmlid(cr, xmlid)
|
||||
|
||||
for from_xmlid, to_xmlid in XMLIDS_TO_RENAME_2020.items():
|
||||
rename_xmlid(cr, from_xmlid, to_xmlid)
|
||||
|
||||
# Need to remove views as they don't work anymore.
|
||||
cr.execute("DELETE FROM ir_ui_view as v WHERE v.id in (SELECT t.res_id FROM ir_model_data as t WHERE t.model = 'ir.ui.view' and (t.module = 'l10n_us_hr_payroll' or t.module like 'l10n_us_%_hr_payroll'))")
|
||||
1
l10n_us_hr_payroll/migrations/__init__.py
Normal file
1
l10n_us_hr_payroll/migrations/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
191
l10n_us_hr_payroll/migrations/data.py
Normal file
191
l10n_us_hr_payroll/migrations/data.py
Normal file
@@ -0,0 +1,191 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020 = {
|
||||
# Federal
|
||||
'w4_allowances': 'fed_941_fit_w4_allowances',
|
||||
'w4_filing_status': 'fed_941_fit_w4_filing_status',
|
||||
'w4_is_nonresident_alien': 'fed_941_fit_w4_is_nonresident_alien',
|
||||
'w4_additional_withholding': 'fed_941_fit_w4_additional_withholding',
|
||||
'fica_exempt': 'fed_941_fica_exempt',
|
||||
'futa_type': 'fed_940_type',
|
||||
# State
|
||||
'ga_g4_filing_status': 'ga_g4_sit_filing_status',
|
||||
'ga_g4_dependent_allowances': 'ga_g4_sit_dependent_allowances',
|
||||
'ga_g4_additional_allowances': 'ga_g4_sit_additional_allowances',
|
||||
'ga_g4_additional_wh': 'state_income_tax_additional_withholding',
|
||||
|
||||
'ms_89_350_filing_status': 'ms_89_350_sit_filing_status',
|
||||
'ms_89_350_exemption': 'ms_89_350_sit_exemption_value',
|
||||
'ms_89_350_additional_withholding': 'state_income_tax_additional_withholding',
|
||||
|
||||
'mt_mw4_additional_withholding': 'state_income_tax_additional_withholding',
|
||||
'mt_mw4_exemptions': 'mt_mw4_sit_exemptions',
|
||||
'mt_mw4_exempt': 'mt_mw4_sit_exempt',
|
||||
|
||||
'oh_additional_withholding': 'state_income_tax_additional_withholding',
|
||||
'oh_income_allowances': 'oh_it4_sit_exemptions',
|
||||
|
||||
'pa_additional_withholding': 'state_income_tax_additional_withholding',
|
||||
|
||||
'va_va4_exemptions': 'va_va4_sit_exemptions',
|
||||
|
||||
}
|
||||
|
||||
XMLIDS_TO_REMOVE_2020 = [
|
||||
# Federal
|
||||
# Categories -- These are now all up in the EE FICA or ER FICA
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_emp_m',
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_emp_m_add',
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_emp_m_add_wages',
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_comp_m',
|
||||
'l10n_us_hr_payroll.hr_payroll_futa_wages',
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_emp_m_wages',
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_emp_ss_wages',
|
||||
# Rules -- These are mainly Wage rules or were simplified to a single rule
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_ss_wages_2018',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_m_wages_2018',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_m_add_wages_2018',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_futa_wages_2018',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fed_inc_withhold_2018_married',
|
||||
# State
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_fl_unemp_wages',
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_fl_unemp',
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_rules_fl_unemp_wages_2018',
|
||||
|
||||
'l10n_us_ga_hr_payroll.hr_payroll_ga_unemp_wages',
|
||||
'l10n_us_ga_hr_payroll.hr_payroll_ga_unemp',
|
||||
'l10n_us_ga_hr_payroll.hr_payroll_ga_income_withhold',
|
||||
'l10n_us_ga_hr_payroll.hr_payroll_rules_ga_unemp_wages',
|
||||
|
||||
'l10n_us_ms_hr_payroll.hr_payroll_ms_unemp_wages',
|
||||
'l10n_us_ms_hr_payroll.hr_payroll_ms_unemp',
|
||||
'l10n_us_ms_hr_payroll.hr_payroll_ms_income_withhold',
|
||||
'l10n_us_ms_hr_payroll.hr_payroll_rules_ms_unemp_wages',
|
||||
|
||||
'l10n_us_mt_hr_payroll.hr_payroll_mt_unemp_wages',
|
||||
'l10n_us_mt_hr_payroll.hr_payroll_mt_unemp',
|
||||
'l10n_us_mt_hr_payroll.hr_payroll_mt_income_withhold',
|
||||
'l10n_us_mt_hr_payroll.hr_payroll_rules_mt_unemp_wages',
|
||||
|
||||
'l10n_us_oh_hr_payroll.hr_payroll_oh_unemp_wages',
|
||||
'l10n_us_oh_hr_payroll.hr_payroll_oh_unemp',
|
||||
'l10n_us_oh_hr_payroll.hr_payroll_oh_income_withhold',
|
||||
'l10n_us_oh_hr_payroll.hr_payroll_rules_oh_unemp_wages_2018',
|
||||
|
||||
'l10n_us_pa_hr_payroll.res_partner_pador_unemp_employee',
|
||||
'l10n_us_pa_hr_payroll.contrib_register_pador_unemp_employee',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_pa_unemp_wages',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_pa_unemp_employee',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_pa_unemp_company',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_pa_withhold',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_unemp_wages_2018',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_inc_withhold_add',
|
||||
|
||||
'l10n_us_tx_hr_payroll.contrib_register_txdor',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_tx_unemp_wages',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_tx_unemp',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_tx_oa',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_tx_etia',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_rules_tx_unemp_wages_2018',
|
||||
|
||||
'l10n_us_va_hr_payroll.hr_payroll_va_unemp_wages',
|
||||
'l10n_us_va_hr_payroll.hr_payroll_va_unemp',
|
||||
'l10n_us_va_hr_payroll.hr_payroll_va_income_withhold',
|
||||
'l10n_us_va_hr_payroll.hr_payroll_rules_va_unemp_wages_2018',
|
||||
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_wa_unemp_wages',
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_wa_unemp',
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_wa_lni',
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_wa_lni_withhold',
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_unemp_wages_2018',
|
||||
|
||||
]
|
||||
|
||||
XMLIDS_TO_RENAME_2020 = {
|
||||
# Federal
|
||||
'l10n_us_hr_payroll.hr_payroll_futa': 'l10n_us_hr_payroll.hr_payroll_category_er_fed_940',
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_emp_ss': 'l10n_us_hr_payroll.hr_payroll_category_ee_fed_941',
|
||||
'l10n_us_hr_payroll.hr_payroll_fed_income_withhold': 'l10n_us_hr_payroll.hr_payroll_category_ee_fed_941_fit',
|
||||
'l10n_us_hr_payroll.hr_payroll_fica_comp_ss': 'l10n_us_hr_payroll.hr_payroll_category_er_fed_941',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_ss_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_fed_941_ss',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_m_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_fed_941_m',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_m_add_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_fed_941_m_add',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_futa_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_fed_940',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_comp_ss': 'l10n_us_hr_payroll.hr_payroll_rule_er_fed_941_ss',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fica_comp_m': 'l10n_us_hr_payroll.hr_payroll_rule_er_fed_941_m',
|
||||
'l10n_us_hr_payroll.hr_payroll_rules_fed_inc_withhold_2018_single': 'l10n_us_hr_payroll.hr_payroll_rule_ee_fed_941_fit',
|
||||
# State
|
||||
'l10n_us_fl_hr_payroll.hr_payroll_rules_fl_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_fl_suta',
|
||||
'l10n_us_fl_hr_payroll.res_partner_fldor': 'l10n_us_hr_payroll.res_partner_us_fl_dor',
|
||||
'l10n_us_fl_hr_payroll.contrib_register_fldor': 'l10n_us_hr_payroll.contrib_register_us_fl_dor',
|
||||
|
||||
'l10n_us_ga_hr_payroll.res_partner_ga_dol_unemp': 'l10n_us_hr_payroll.res_partner_us_ga_dor',
|
||||
'l10n_us_ga_hr_payroll.res_partner_ga_dor_withhold': 'l10n_us_hr_payroll.res_partner_us_ga_dor_sit',
|
||||
'l10n_us_ga_hr_payroll.contrib_register_ga_dol_unemp': 'l10n_us_hr_payroll.contrib_register_us_ga_dor',
|
||||
'l10n_us_ga_hr_payroll.contrib_register_ga_dor_withhold': 'l10n_us_hr_payroll.contrib_register_us_ga_dor_sit',
|
||||
'l10n_us_ga_hr_payroll.hr_payroll_rules_ga_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_ga_suta',
|
||||
'l10n_us_ga_hr_payroll.hr_payroll_rules_ga_inc_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_ga_sit',
|
||||
|
||||
'l10n_us_ms_hr_payroll.res_partner_msdor_unemp': 'l10n_us_hr_payroll.res_partner_us_ms_dor',
|
||||
'l10n_us_ms_hr_payroll.res_partner_msdor_withhold': 'l10n_us_hr_payroll.res_partner_us_ms_dor_sit',
|
||||
'l10n_us_ms_hr_payroll.contrib_register_msdor_unemp': 'l10n_us_hr_payroll.contrib_register_us_ms_dor',
|
||||
'l10n_us_ms_hr_payroll.contrib_register_msdor_withhold': 'l10n_us_hr_payroll.contrib_register_us_ms_dor_sit',
|
||||
'l10n_us_ms_hr_payroll.hr_payroll_rules_ms_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_ms_suta',
|
||||
'l10n_us_ms_hr_payroll.hr_payroll_rules_ms_inc_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_ms_sit',
|
||||
|
||||
'l10n_us_mt_hr_payroll.res_partner_mtdor_unemp': 'l10n_us_hr_payroll.res_partner_us_mt_dor',
|
||||
'l10n_us_mt_hr_payroll.res_partner_mtdor_withhold': 'l10n_us_hr_payroll.res_partner_us_mt_dor_sit',
|
||||
'l10n_us_mt_hr_payroll.contrib_register_mtdor_unemp': 'l10n_us_hr_payroll.contrib_register_us_mt_dor',
|
||||
'l10n_us_mt_hr_payroll.contrib_register_mtdor_withhold': 'l10n_us_hr_payroll.contrib_register_us_mt_dor_sit',
|
||||
'l10n_us_mt_hr_payroll.hr_payroll_rules_mt_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_mt_suta',
|
||||
'l10n_us_mt_hr_payroll.hr_payroll_rules_mt_inc_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_mt_sit',
|
||||
|
||||
'l10n_us_oh_hr_payroll.res_partner_ohdor_unemp': 'l10n_us_hr_payroll.res_partner_us_oh_dor',
|
||||
'l10n_us_oh_hr_payroll.res_partner_ohdor_withhold': 'l10n_us_hr_payroll.res_partner_us_oh_dor_sit',
|
||||
'l10n_us_oh_hr_payroll.res_partner_ohdor_unemp': 'l10n_us_hr_payroll.res_partner_us_oh_dor',
|
||||
'l10n_us_oh_hr_payroll.res_partner_ohdor_withhold': 'l10n_us_hr_payroll.res_partner_us_oh_dor_sit',
|
||||
'l10n_us_oh_hr_payroll.hr_payroll_rules_oh_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_oh_suta',
|
||||
'l10n_us_oh_hr_payroll.hr_payroll_rules_oh_inc_withhold_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_oh_sit',
|
||||
|
||||
'l10n_us_pa_hr_payroll.res_partner_pador_unemp_company': 'l10n_us_hr_payroll.res_partner_us_pa_dor',
|
||||
'l10n_us_pa_hr_payroll.res_partner_pador_withhold': 'l10n_us_hr_payroll.res_partner_us_pa_dor_sit',
|
||||
'l10n_us_pa_hr_payroll.contrib_register_pador_unemp_company': 'l10n_us_hr_payroll.contrib_register_us_pa_dor',
|
||||
'l10n_us_pa_hr_payroll.contrib_register_pador_withhold': 'l10n_us_hr_payroll.contrib_register_us_pa_dor_sit',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_unemp_employee_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_pa_suta',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_unemp_company_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_pa_suta',
|
||||
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_inc_withhold_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_pa_sit',
|
||||
|
||||
'l10n_us_tx_hr_payroll.res_partner_txdor': 'l10n_us_hr_payroll.res_partner_us_tx_dor',
|
||||
'l10n_us_tx_hr_payroll.contrib_register_txdor': 'l10n_us_hr_payroll.contrib_register_us_tx_dor',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_rules_tx_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_tx_suta',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_rules_tx_oa_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_tx_suta_oa',
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_rules_tx_etia_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_tx_suta_etia',
|
||||
|
||||
'l10n_us_va_hr_payroll.res_partner_vador_unemp': 'l10n_us_hr_payroll.res_partner_us_va_dor',
|
||||
'l10n_us_va_hr_payroll.res_partner_vador_withhold': 'l10n_us_hr_payroll.res_partner_us_va_dor_sit',
|
||||
'l10n_us_va_hr_payroll.contrib_register_vador_unemp': 'l10n_us_hr_payroll.contrib_register_us_va_dor',
|
||||
'l10n_us_va_hr_payroll.contrib_register_vador_withhold': 'l10n_us_hr_payroll.contrib_register_us_va_dor_sit',
|
||||
'l10n_us_va_hr_payroll.hr_payroll_rules_va_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_va_suta',
|
||||
'l10n_us_va_hr_payroll.hr_payroll_rules_va_inc_withhold_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_va_sit',
|
||||
|
||||
'l10n_us_wa_hr_payroll.res_partner_wador_unemp': 'l10n_us_hr_payroll.res_partner_us_wa_dor',
|
||||
'l10n_us_wa_hr_payroll.res_partner_wador_lni': 'l10n_us_hr_payroll.res_partner_us_wa_dor_lni',
|
||||
'l10n_us_wa_hr_payroll.contrib_register_wador_unemp': 'l10n_us_hr_payroll.contrib_register_us_wa_dor',
|
||||
'l10n_us_wa_hr_payroll.contrib_register_wador_lni': 'l10n_us_hr_payroll.contrib_register_us_wa_dor_lni',
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_suta',
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_lni_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_wa_lni',
|
||||
'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_lni': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_lni',
|
||||
|
||||
}
|
||||
|
||||
XMLIDS_COPY_ACCOUNTING_2020 = {
|
||||
'l10n_us_hr_payroll.hr_payroll_rule_er_us_mt_suta': [
|
||||
'l10n_us_hr_payroll.hr_payroll_rule_er_us_mt_suta_aft',
|
||||
],
|
||||
'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_lni': [
|
||||
'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_fml',
|
||||
],
|
||||
'l10n_us_hr_payroll.hr_payroll_rule_ee_us_wa_lni': [
|
||||
'l10n_us_hr_payroll.hr_payroll_rule_ee_us_wa_fml',
|
||||
],
|
||||
}
|
||||
63
l10n_us_hr_payroll/migrations/helper.py
Normal file
63
l10n_us_hr_payroll/migrations/helper.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
TMP_PREFIX = 'tmp_'
|
||||
|
||||
"""
|
||||
Fields
|
||||
"""
|
||||
|
||||
|
||||
def field_exists(cr, table_name, field_name):
|
||||
cr.execute('SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name=%s and column_name=%s);', (table_name, field_name))
|
||||
return cr.fetchone()[0]
|
||||
|
||||
|
||||
def temp_field_exists(cr, table_name, field_name):
|
||||
tmp_field_name = TMP_PREFIX + field_name
|
||||
cr.execute('SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name=%s and column_name=%s);', (table_name, tmp_field_name))
|
||||
return cr.fetchone()[0]
|
||||
|
||||
|
||||
def make_temp_field(cr, table_name, field_name):
|
||||
tmp_field_name = TMP_PREFIX + field_name
|
||||
cr.execute('SELECT data_type FROM information_schema.columns WHERE table_name=%s and column_name=%s;', (table_name, field_name))
|
||||
tmp_field_type = cr.fetchone()[0]
|
||||
cr.execute('ALTER TABLE ' + table_name + ' ADD ' + tmp_field_name + ' ' + tmp_field_type)
|
||||
cr.execute('UPDATE ' + table_name + ' SET ' + tmp_field_name + '=' + field_name)
|
||||
|
||||
|
||||
def remove_temp_field(cr, table_name, field_name):
|
||||
tmp_field_name = TMP_PREFIX + field_name
|
||||
cr.execute('ALTER TABLE ' + table_name + ' DROP COLUMN ' + tmp_field_name)
|
||||
|
||||
|
||||
def temp_field_values(cr, table_name, id, field_names):
|
||||
tmp_field_names = [TMP_PREFIX + f for f in field_names]
|
||||
if not tmp_field_names:
|
||||
return {}
|
||||
cr.execute('SELECT ' + ', '.join(tmp_field_names) + ' FROM ' + table_name + ' WHERE id=' + str(id))
|
||||
values = cr.dictfetchone()
|
||||
if not values:
|
||||
return {}
|
||||
|
||||
def _remove_tmp_prefix(key):
|
||||
if key.startswith(TMP_PREFIX):
|
||||
return key[len(TMP_PREFIX):]
|
||||
return key
|
||||
return {_remove_tmp_prefix(k): v for k, v in values.items()}
|
||||
|
||||
|
||||
"""
|
||||
XMLIDs
|
||||
"""
|
||||
|
||||
|
||||
def remove_xmlid(cr, xmlid):
|
||||
module, name = xmlid.split('.')
|
||||
cr.execute('DELETE FROM ir_model_data WHERE module=%s and name=%s;', (module, name))
|
||||
|
||||
|
||||
def rename_xmlid(cr, from_xmlid, to_xmlid):
|
||||
from_module, from_name = from_xmlid.split('.')
|
||||
to_module, to_name = to_xmlid.split('.')
|
||||
cr.execute('UPDATE ir_model_data SET module=%s, name=%s WHERE module=%s and name=%s;', (to_module, to_name, from_module, from_name))
|
||||
@@ -1 +1,6 @@
|
||||
from . import l10n_us_hr_payroll
|
||||
# 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 hr_salary_rule
|
||||
from . import us_payroll_config
|
||||
|
||||
1
l10n_us_hr_payroll/models/federal/__init__.py
Normal file
1
l10n_us_hr_payroll/models/federal/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
37
l10n_us_hr_payroll/models/federal/fed_940.py
Normal file
37
l10n_us_hr_payroll/models/federal/fed_940.py
Normal file
@@ -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 + DED_FUTA_EXEMPT
|
||||
:return: result, result_rate (wage, percent)
|
||||
"""
|
||||
|
||||
# Determine Rate.
|
||||
if payslip.dict.contract_id.futa_type == payslip.dict.contract_id.FUTA_TYPE_EXEMPT:
|
||||
# Exit early
|
||||
return 0.0, 0.0
|
||||
elif payslip.dict.contract_id.futa_type == payslip.dict.contract_id.FUTA_TYPE_BASIC:
|
||||
result_rate = -payslip.dict.rule_parameter('fed_940_futa_rate_basic')
|
||||
else:
|
||||
result_rate = -payslip.dict.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('DED_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
|
||||
ytd_wage += payslip.dict.contract_id.external_wages
|
||||
|
||||
wage_base = payslip.dict.rule_parameter('fed_940_futa_wage_base')
|
||||
remaining = wage_base - ytd_wage
|
||||
|
||||
wage = categories.GROSS + categories.DED_FUTA_EXEMPT
|
||||
|
||||
if remaining < 0.0:
|
||||
result = 0.0
|
||||
elif remaining < wage:
|
||||
result = remaining
|
||||
else:
|
||||
result = wage
|
||||
|
||||
return result, result_rate
|
||||
239
l10n_us_hr_payroll/models/federal/fed_941.py
Normal file
239
l10n_us_hr_payroll/models/federal/fed_941.py
Normal file
@@ -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 + DED_FICA_EXEMPT
|
||||
:return: result, result_rate (wage, percent)
|
||||
"""
|
||||
exempt = payslip.dict.contract_id.us_payroll_config_value('fed_941_fica_exempt')
|
||||
if exempt:
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Rate.
|
||||
result_rate = -payslip.dict.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('DED_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
|
||||
ytd_wage += payslip.dict.contract_id.external_wages
|
||||
|
||||
wage_base = payslip.dict.rule_parameter('fed_941_fica_ss_wage_base')
|
||||
remaining = wage_base - ytd_wage
|
||||
|
||||
wage = categories.GROSS + categories.DED_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 + DED_FICA_EXEMPT
|
||||
:return: result, result_rate (wage, percent)
|
||||
"""
|
||||
exempt = payslip.dict.contract_id.us_payroll_config_value('fed_941_fica_exempt')
|
||||
if exempt:
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Rate.
|
||||
result_rate = -payslip.dict.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('DED_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
|
||||
ytd_wage += payslip.dict.contract_id.external_wages
|
||||
|
||||
wage_base = float(payslip.dict.rule_parameter('fed_941_fica_m_wage_base')) # inf
|
||||
remaining = wage_base - ytd_wage
|
||||
|
||||
wage = categories.GROSS + categories.DED_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.dict.contract_id.us_payroll_config_value('fed_941_fica_exempt')
|
||||
if exempt:
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Rate.
|
||||
result_rate = -payslip.dict.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('DED_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01')
|
||||
ytd_wage += payslip.dict.contract_id.external_wages
|
||||
|
||||
wage_start = payslip.dict.rule_parameter('fed_941_fica_m_add_wage_start')
|
||||
existing_wage = ytd_wage - wage_start
|
||||
|
||||
wage = categories.GROSS + categories.DED_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 + DED_FIT_EXEMPT
|
||||
:return: result, result_rate (wage, percent)
|
||||
"""
|
||||
filing_status = payslip.dict.contract_id.us_payroll_config_value('fed_941_fit_w4_filing_status')
|
||||
if not filing_status:
|
||||
return 0.0, 0.0
|
||||
|
||||
schedule_pay = payslip.dict.contract_id.schedule_pay
|
||||
wage = categories.GROSS + categories.DED_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.dict.contract_id.us_payroll_config_value('fed_941_fit_w4_is_nonresident_alien')
|
||||
if is_nra:
|
||||
nra_table = payslip.dict.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.dict.contract_id.us_payroll_config_value('fed_941_fit_w4_other_income')
|
||||
#_logger.warn(' after other income: ' + str(wage_annual))
|
||||
|
||||
deductions = payslip.dict.contract_id.us_payroll_config_value('fed_941_fit_w4_deductions')
|
||||
#_logger.warn('deductions from W4: ' + str(deductions))
|
||||
|
||||
higher_rate_type = payslip.dict.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.dict.rule_parameter('fed_941_fit_table_single')
|
||||
elif filing_status == 'married':
|
||||
tax_tables = payslip.dict.rule_parameter('fed_941_fit_table_married')
|
||||
else:
|
||||
# married_as_single for historic reasons
|
||||
tax_tables = payslip.dict.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.dict.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.dict.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.dict.contract_id.us_payroll_config_value('fed_941_fit_w4_is_nonresident_alien')
|
||||
if is_nra:
|
||||
nra_table = payslip.dict.rule_parameter('fed_941_fit_nra_additional')
|
||||
working_wage += nra_table[schedule_pay]
|
||||
|
||||
allowance_table = payslip.dict.rule_parameter('fed_941_fit_allowance')
|
||||
allowances = payslip.dict.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.dict.rule_parameter('fed_941_fit_table_married')
|
||||
else:
|
||||
tax_table = payslip.dict.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.dict.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)
|
||||
24
l10n_us_hr_payroll/models/hr_contract.py
Normal file
24
l10n_us_hr_payroll/models/hr_contract.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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 USHRContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
FUTA_TYPE_NORMAL = FUTA_TYPE_NORMAL
|
||||
FUTA_TYPE_BASIC = FUTA_TYPE_BASIC
|
||||
FUTA_TYPE_EXEMPT = FUTA_TYPE_EXEMPT
|
||||
|
||||
schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
|
||||
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]
|
||||
235
l10n_us_hr_payroll/models/hr_payslip.py
Normal file
235
l10n_us_hr_payroll/models/hr_payslip.py
Normal file
@@ -0,0 +1,235 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
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
|
||||
from .state.general import general_state_unemployment, \
|
||||
general_state_income_withholding, \
|
||||
is_us_state
|
||||
from .state.ga_georgia import ga_georgia_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.oh_ohio import oh_ohio_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
|
||||
|
||||
|
||||
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):
|
||||
# back port for US Payroll
|
||||
#res = super()._get_base_local_dict()
|
||||
return {
|
||||
'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,
|
||||
'general_state_unemployment': general_state_unemployment,
|
||||
'general_state_income_withholding': general_state_income_withholding,
|
||||
'is_us_state': is_us_state,
|
||||
'ga_georgia_state_income_withholding': ga_georgia_state_income_withholding,
|
||||
'ms_mississippi_state_income_withholding': ms_mississippi_state_income_withholding,
|
||||
'mt_montana_state_income_withholding': mt_montana_state_income_withholding,
|
||||
'oh_ohio_state_income_withholding': oh_ohio_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,
|
||||
}
|
||||
|
||||
def get_year(self):
|
||||
# Helper method to get the year (normalized between Odoo Versions)
|
||||
return int(self.date_to[:4])
|
||||
|
||||
def get_pay_periods_in_year(self):
|
||||
return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)
|
||||
|
||||
@api.model
|
||||
def _get_payslip_lines(self, contract_ids, payslip_id):
|
||||
def _sum_salary_rule_category(localdict, category, amount):
|
||||
if category.parent_id:
|
||||
localdict = _sum_salary_rule_category(localdict, category.parent_id, amount)
|
||||
localdict['categories'].dict[category.code] = category.code in localdict['categories'].dict and localdict['categories'].dict[category.code] + amount or amount
|
||||
return localdict
|
||||
|
||||
class BrowsableObject(object):
|
||||
def __init__(self, employee_id, dict, env):
|
||||
self.employee_id = employee_id
|
||||
self.dict = dict
|
||||
self.env = env
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return attr in self.dict and self.dict.__getitem__(attr) or 0.0
|
||||
|
||||
class InputLine(BrowsableObject):
|
||||
"""a class that will be used into the python code, mainly for usability purposes"""
|
||||
def sum(self, code, from_date, to_date=None):
|
||||
if to_date is None:
|
||||
to_date = fields.Date.today()
|
||||
self.env.cr.execute("""
|
||||
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.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""",
|
||||
(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 _sum(self, code, from_date, to_date=None):
|
||||
if to_date is None:
|
||||
to_date = fields.Date.today()
|
||||
self.env.cr.execute("""
|
||||
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.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""",
|
||||
(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 sum(self, code, from_date, to_date=None):
|
||||
if to_date is None:
|
||||
to_date = fields.Date.today()
|
||||
self.env.cr.execute("""SELECT sum(case when hp.credit_note = False 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.date_from >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""",
|
||||
(self.employee_id, from_date, to_date, code))
|
||||
res = self.env.cr.fetchone()
|
||||
return res and res[0] or 0.0
|
||||
|
||||
def sum_category(self, code, from_date, to_date=None):
|
||||
# Hibou Backport
|
||||
if to_date is None:
|
||||
to_date = fields.Date.today()
|
||||
|
||||
self.env.cr.execute("""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.date_from >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id
|
||||
AND rc.id = pl.category_id AND rc.code = %s""",
|
||||
(self.employee_id, from_date, to_date, code))
|
||||
res = self.env.cr.fetchone()
|
||||
return res and res[0] or 0.0
|
||||
|
||||
#we keep a dict with the result because a value can be overwritten by another rule with the same code
|
||||
result_dict = {}
|
||||
rules_dict = {}
|
||||
worked_days_dict = {}
|
||||
inputs_dict = {}
|
||||
blacklist = []
|
||||
payslip = self.env['hr.payslip'].browse(payslip_id)
|
||||
for worked_days_line in payslip.worked_days_line_ids:
|
||||
worked_days_dict[worked_days_line.code] = worked_days_line
|
||||
for input_line in payslip.input_line_ids:
|
||||
inputs_dict[input_line.code] = input_line
|
||||
|
||||
categories = BrowsableObject(payslip.employee_id.id, {}, self.env)
|
||||
inputs = InputLine(payslip.employee_id.id, inputs_dict, self.env)
|
||||
worked_days = WorkedDays(payslip.employee_id.id, worked_days_dict, self.env)
|
||||
payslips = Payslips(payslip.employee_id.id, payslip, self.env)
|
||||
rules = BrowsableObject(payslip.employee_id.id, rules_dict, self.env)
|
||||
|
||||
baselocaldict = {'categories': categories, 'rules': rules, 'payslip': payslips, 'worked_days': worked_days, 'inputs': inputs}
|
||||
|
||||
# Hibou Backport
|
||||
baselocaldict.update(self._get_base_local_dict())
|
||||
|
||||
#get the ids of the structures on the contracts and their parent id as well
|
||||
contracts = self.env['hr.contract'].browse(contract_ids)
|
||||
if len(contracts) == 1 and payslip.struct_id:
|
||||
structure_ids = list(set(payslip.struct_id._get_parent_structure().ids))
|
||||
else:
|
||||
structure_ids = contracts.get_all_structures()
|
||||
#get the rules of the structure and thier children
|
||||
rule_ids = self.env['hr.payroll.structure'].browse(structure_ids).get_all_rules()
|
||||
#run the rules by sequence
|
||||
sorted_rule_ids = [id for id, sequence in sorted(rule_ids, key=lambda x:x[1])]
|
||||
sorted_rules = self.env['hr.salary.rule'].browse(sorted_rule_ids)
|
||||
|
||||
for contract in contracts:
|
||||
employee = contract.employee_id
|
||||
localdict = dict(baselocaldict, employee=employee, contract=contract)
|
||||
for rule in sorted_rules:
|
||||
key = rule.code + '-' + str(contract.id)
|
||||
localdict['result'] = None
|
||||
localdict['result_qty'] = 1.0
|
||||
localdict['result_rate'] = 100
|
||||
#check if the rule can be applied
|
||||
if rule._satisfy_condition(localdict) and rule.id not in blacklist:
|
||||
#compute the amount of the rule
|
||||
amount, qty, rate = rule._compute_rule(localdict)
|
||||
#check if there is already a rule computed with that code
|
||||
previous_amount = rule.code in localdict and localdict[rule.code] or 0.0
|
||||
#set/overwrite the amount computed for this rule in the localdict
|
||||
tot_rule = amount * qty * rate / 100.0
|
||||
localdict[rule.code] = tot_rule
|
||||
rules_dict[rule.code] = rule
|
||||
#sum the amount for its salary category
|
||||
localdict = _sum_salary_rule_category(localdict, rule.category_id, tot_rule - previous_amount)
|
||||
#create/overwrite the rule in the temporary results
|
||||
result_dict[key] = {
|
||||
'salary_rule_id': rule.id,
|
||||
'contract_id': contract.id,
|
||||
'name': rule.name,
|
||||
'code': rule.code,
|
||||
'category_id': rule.category_id.id,
|
||||
'sequence': rule.sequence,
|
||||
'appears_on_payslip': rule.appears_on_payslip,
|
||||
'condition_select': rule.condition_select,
|
||||
'condition_python': rule.condition_python,
|
||||
'condition_range': rule.condition_range,
|
||||
'condition_range_min': rule.condition_range_min,
|
||||
'condition_range_max': rule.condition_range_max,
|
||||
'amount_select': rule.amount_select,
|
||||
'amount_fix': rule.amount_fix,
|
||||
'amount_python_compute': rule.amount_python_compute,
|
||||
'amount_percentage': rule.amount_percentage,
|
||||
'amount_percentage_base': rule.amount_percentage_base,
|
||||
'register_id': rule.register_id.id,
|
||||
'amount': amount,
|
||||
'employee_id': contract.employee_id.id,
|
||||
'quantity': qty,
|
||||
'rate': rate,
|
||||
}
|
||||
else:
|
||||
#blacklist this rule and its children
|
||||
blacklist += [id for id, seq in rule._recursive_search_of_rules()]
|
||||
|
||||
return list(result_dict.values())
|
||||
35
l10n_us_hr_payroll/models/hr_salary_rule.py
Normal file
35
l10n_us_hr_payroll/models/hr_salary_rule.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class HRSalaryRule(models.Model):
|
||||
_inherit = 'hr.salary.rule'
|
||||
|
||||
@api.multi
|
||||
def _compute_rule(self, localdict):
|
||||
"""
|
||||
:param localdict: dictionary containing the environement in which to compute the rule
|
||||
:return: returns a tuple build as the base/amount computed, the quantity and the rate
|
||||
:rtype: (float, float, float)
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.amount_select == 'fix':
|
||||
try:
|
||||
return self.amount_fix, float(safe_eval(self.quantity, localdict)), 100.0
|
||||
except:
|
||||
raise UserError(_('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code))
|
||||
elif self.amount_select == 'percentage':
|
||||
try:
|
||||
return (float(safe_eval(self.amount_percentage_base, localdict)),
|
||||
float(safe_eval(self.quantity, localdict)),
|
||||
self.amount_percentage)
|
||||
except:
|
||||
raise UserError(_('Wrong percentage base or quantity defined for salary rule %s (%s).') % (self.name, self.code))
|
||||
else:
|
||||
try:
|
||||
safe_eval(self.amount_python_compute, localdict, mode='exec', nocopy=True)
|
||||
# Hibou Fix for setting 0.0 for result rate or result qty
|
||||
return float(localdict['result']), localdict.get('result_qty', 1.0), localdict.get('result_rate', 100.0)
|
||||
except:
|
||||
raise UserError(_('Wrong python code defined for salary rule %s (%s).') % (self.name, self.code))
|
||||
@@ -1,44 +0,0 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class Payslip(models.Model):
|
||||
_inherit = 'hr.payslip'
|
||||
|
||||
def get_futa_rate(self, contract):
|
||||
self.ensure_one()
|
||||
if contract.futa_type == USHrContract.FUTA_TYPE_EXEMPT:
|
||||
rate = self.get_rate('US_FUTA_EXEMPT')
|
||||
elif contract.futa_type == USHrContract.FUTA_TYPE_NORMAL:
|
||||
rate = self.get_rate('US_FUTA_NORMAL')
|
||||
else:
|
||||
rate = self.get_rate('US_FUTA_BASIC')
|
||||
return rate
|
||||
|
||||
|
||||
class USHrContract(models.Model):
|
||||
FUTA_TYPE_EXEMPT = 'exempt'
|
||||
FUTA_TYPE_BASIC = 'basic'
|
||||
FUTA_TYPE_NORMAL = 'normal'
|
||||
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
|
||||
w4_allowances = fields.Integer(string='Federal W4 Allowances', default=0)
|
||||
w4_filing_status = fields.Selection([
|
||||
('', 'Exempt'),
|
||||
('single', 'Single'),
|
||||
('married', 'Married'),
|
||||
('married_as_single', 'Married but at Single Rate'),
|
||||
], string='Federal W4 Filing Status', default='single')
|
||||
w4_is_nonresident_alien = fields.Boolean(string="Federal W4 Is Nonresident Alien", default=False)
|
||||
w4_additional_withholding = fields.Float(string="Federal W4 Additional Withholding", default=0.0)
|
||||
|
||||
external_wages = fields.Float(string='External Existing Wages', default=0.0)
|
||||
|
||||
fica_exempt = fields.Boolean(string='FICA Exempt', help="Exempt from Social Security and "
|
||||
"Medicare e.g. F1 Student Visa")
|
||||
futa_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')
|
||||
1
l10n_us_hr_payroll/models/state/__init__.py
Normal file
1
l10n_us_hr_payroll/models/state/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
52
l10n_us_hr_payroll/models/state/ga_georgia.py
Normal file
52
l10n_us_hr_payroll/models/state/ga_georgia.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .general import _state_applies
|
||||
|
||||
|
||||
def ga_georgia_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 = 'GA'
|
||||
if not _state_applies(payslip, state_code):
|
||||
return 0.0, 0.0
|
||||
ga_filing_status = payslip.dict.contract_id.us_payroll_config_value('ga_g4_sit_filing_status')
|
||||
if not ga_filing_status or ga_filing_status == 'exempt':
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Wage
|
||||
wage = categories.GROSS + categories.DED_FIT_EXEMPT
|
||||
schedule_pay = payslip.dict.contract_id.schedule_pay
|
||||
additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
|
||||
dependent_allowances = payslip.dict.contract_id.us_payroll_config_value('ga_g4_sit_dependent_allowances')
|
||||
additional_allowances = payslip.dict.contract_id.us_payroll_config_value('ga_g4_sit_additional_allowances')
|
||||
dependent_allowance_rate = payslip.dict.rule_parameter('us_ga_sit_dependent_allowance_rate').get(schedule_pay)
|
||||
personal_allowance = payslip.dict.rule_parameter('us_ga_sit_personal_allowance').get(ga_filing_status, {}).get(schedule_pay)
|
||||
deduction = payslip.dict.rule_parameter('us_ga_sit_deduction').get(ga_filing_status, {}).get(schedule_pay)
|
||||
withholding_rate = payslip.dict.rule_parameter('us_ga_sit_rate').get(ga_filing_status, {}).get(schedule_pay)
|
||||
if not all((dependent_allowance_rate, personal_allowance, deduction, withholding_rate)) or wage == 0.0:
|
||||
return 0.0, 0.0
|
||||
|
||||
if wage == 0.0:
|
||||
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)
|
||||
127
l10n_us_hr_payroll/models/state/general.py
Normal file
127
l10n_us_hr_payroll/models/state/general.py
Normal file
@@ -0,0 +1,127 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
# import logging
|
||||
# _logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _state_applies(payslip, state_code):
|
||||
return state_code == payslip.dict.contract_id.us_payroll_config_value('state_code')
|
||||
|
||||
|
||||
# Export for eval context
|
||||
is_us_state = _state_applies
|
||||
|
||||
|
||||
def _general_rate(payslip, wage, ytd_wage, wage_base=None, wage_start=None, rate=None):
|
||||
"""
|
||||
Function parameters:
|
||||
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.dict.rule_parameter(wage_base)
|
||||
except (KeyError, UserError):
|
||||
return 0.0, 0.0
|
||||
|
||||
if wage_start and isinstance(wage_start, str):
|
||||
try:
|
||||
wage_start = payslip.dict.rule_parameter(wage_start)
|
||||
except (KeyError, UserError):
|
||||
return 0.0, 0.0
|
||||
|
||||
if rate and isinstance(rate, str):
|
||||
try:
|
||||
rate = payslip.dict.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.dict.contract_id.futa_type in (payslip.dict.contract_id.FUTA_TYPE_EXEMPT, payslip.dict.contract_id.FUTA_TYPE_BASIC):
|
||||
return 0.0, 0.0
|
||||
|
||||
# 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('DED_FUTA_EXEMPT', str(year) + '-01-01', str(year + 1) + '-01-01')
|
||||
ytd_wage += payslip.dict.contract_id.external_wages
|
||||
|
||||
wage = categories.GROSS + categories.DED_FUTA_EXEMPT
|
||||
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.dict.contract_id.us_payroll_config_value('state_income_tax_exempt'):
|
||||
return 0.0, 0.0
|
||||
|
||||
# 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('DED_FIT_EXEMPT', str(year) + '-01-01', str(year + 1) + '-01-01')
|
||||
ytd_wage += payslip.dict.contract_id.external_wages
|
||||
|
||||
wage = categories.GROSS + categories.DED_FIT_EXEMPT
|
||||
result, result_rate = _general_rate(payslip, wage, ytd_wage, wage_base=wage_base, wage_start=wage_start, rate=rate)
|
||||
additional = payslip.dict.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
|
||||
46
l10n_us_hr_payroll/models/state/ms_mississippi.py
Normal file
46
l10n_us_hr_payroll/models/state/ms_mississippi.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .general import _state_applies
|
||||
|
||||
|
||||
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.dict.contract_id.us_payroll_config_value('ms_89_350_sit_filing_status')
|
||||
if not filing_status:
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Wage
|
||||
wage = categories.GROSS + categories.DED_FIT_EXEMPT
|
||||
if wage == 0.0:
|
||||
return 0.0, 0.0
|
||||
|
||||
pay_periods = payslip.dict.get_pay_periods_in_year()
|
||||
additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
|
||||
exemptions = payslip.dict.contract_id.us_payroll_config_value('ms_89_350_sit_exemption_value')
|
||||
standard_deduction = payslip.dict.rule_parameter('us_ms_sit_deduction').get(filing_status)
|
||||
withholding_rate = payslip.dict.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)
|
||||
42
l10n_us_hr_payroll/models/state/mt_montana.py
Normal file
42
l10n_us_hr_payroll/models/state/mt_montana.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .general import _state_applies
|
||||
|
||||
|
||||
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.dict.contract_id.us_payroll_config_value('mt_mw4_sit_exempt'):
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Wage
|
||||
wage = categories.GROSS + categories.DED_FIT_EXEMPT
|
||||
schedule_pay = payslip.dict.contract_id.schedule_pay
|
||||
additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
|
||||
exemptions = payslip.dict.contract_id.us_payroll_config_value('mt_mw4_sit_exemptions')
|
||||
exemption_rate = payslip.dict.rule_parameter('us_mt_sit_exemption_rate').get(schedule_pay)
|
||||
withholding_rate = payslip.dict.rule_parameter('us_mt_sit_rate').get(schedule_pay)
|
||||
if not exemption_rate or not withholding_rate or wage == 0.0:
|
||||
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)
|
||||
46
l10n_us_hr_payroll/models/state/oh_ohio.py
Normal file
46
l10n_us_hr_payroll/models/state/oh_ohio.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .general import _state_applies
|
||||
|
||||
|
||||
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.dict.contract_id.us_payroll_config_value('state_income_tax_exempt'):
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Wage
|
||||
wage = categories.GROSS + categories.DED_FIT_EXEMPT
|
||||
pay_periods = payslip.dict.get_pay_periods_in_year()
|
||||
additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
|
||||
exemptions = payslip.dict.contract_id.us_payroll_config_value('oh_it4_sit_exemptions')
|
||||
exemption_rate = payslip.dict.rule_parameter('us_oh_sit_exemption_rate')
|
||||
withholding_rate = payslip.dict.rule_parameter('us_oh_sit_rate')
|
||||
multiplier_rate = payslip.dict.rule_parameter('us_oh_sit_multiplier')
|
||||
if wage == 0.0:
|
||||
return 0.0, 0.0
|
||||
|
||||
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)
|
||||
43
l10n_us_hr_payroll/models/state/va_virginia.py
Normal file
43
l10n_us_hr_payroll/models/state/va_virginia.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .general import _state_applies
|
||||
|
||||
|
||||
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.dict.contract_id.us_payroll_config_value('state_income_tax_exempt'):
|
||||
return 0.0, 0.0
|
||||
|
||||
# Determine Wage
|
||||
wage = categories.GROSS + categories.DED_FIT_EXEMPT
|
||||
pay_periods = payslip.dict.get_pay_periods_in_year()
|
||||
additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
|
||||
personal_exemptions = payslip.dict.contract_id.us_payroll_config_value('va_va4_sit_exemptions')
|
||||
other_exemptions = payslip.dict.contract_id.us_payroll_config_value('va_va4_sit_other_exemptions')
|
||||
personal_exemption_rate = payslip.dict.rule_parameter('us_va_sit_exemption_rate')
|
||||
other_exemption_rate = payslip.dict.rule_parameter('us_va_sit_other_exemption_rate')
|
||||
deduction = payslip.dict.rule_parameter('us_va_sit_deduction')
|
||||
withholding_rate = payslip.dict.rule_parameter('us_va_sit_rate')
|
||||
if wage == 0.0:
|
||||
return 0.0, 0.0
|
||||
|
||||
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)
|
||||
27
l10n_us_hr_payroll/models/state/wa_washington.py
Normal file
27
l10n_us_hr_payroll/models/state/wa_washington.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .general import _state_applies, _general_rate
|
||||
|
||||
|
||||
def _wa_washington_fml(payslip, categories, worked_days, inputs, inner_rate=None):
|
||||
if not inner_rate:
|
||||
return 0.0, 0.0
|
||||
|
||||
if not _state_applies(payslip, 'WA'):
|
||||
return 0.0, 0.0
|
||||
|
||||
wage = categories.GROSS
|
||||
year = payslip.dict.get_year()
|
||||
ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year + 1) + '-01-01')
|
||||
ytd_wage += payslip.contract_id.external_wages
|
||||
rate = payslip.dict.rule_parameter('us_wa_fml_rate')
|
||||
rate *= payslip.dict.rule_parameter(inner_rate) / 100.0
|
||||
return _general_rate(payslip, wage, ytd_wage, wage_base='us_wa_fml_wage_base', rate=rate)
|
||||
|
||||
|
||||
def wa_washington_fml_er(payslip, categories, worked_days, inputs):
|
||||
return _wa_washington_fml(payslip, categories, worked_days, inputs, inner_rate='us_wa_fml_rate_er')
|
||||
|
||||
|
||||
def wa_washington_fml_ee(payslip, categories, worked_days, inputs):
|
||||
return _wa_washington_fml(payslip, categories, worked_days, inputs, inner_rate='us_wa_fml_rate_ee')
|
||||
96
l10n_us_hr_payroll/models/us_payroll_config.py
Normal file
96
l10n_us_hr_payroll/models/us_payroll_config.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# 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")
|
||||
state_code = fields.Char(related='state_id.code')
|
||||
state_income_tax_exempt = fields.Boolean(string='State Income Tax Exempt')
|
||||
state_income_tax_additional_withholding = fields.Float(string='State Income Tax Additional Withholding')
|
||||
workers_comp_ee_code = fields.Char(string='Workers\' Comp Code (Employee Withholding)',
|
||||
help='Code for a Payroll Rate, used by some states or your own rules.')
|
||||
workers_comp_er_code = fields.Char(string='Workers\' Comp Code (Employer Withholding)',
|
||||
help='Code for a Payroll Rate, used by some states or your own rules.')
|
||||
|
||||
fed_940_type = fields.Selection([
|
||||
(FUTA_TYPE_EXEMPT, 'Exempt (0%)'),
|
||||
(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)')
|
||||
|
||||
ga_g4_sit_filing_status = fields.Selection([
|
||||
('exempt', '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.')
|
||||
|
||||
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')
|
||||
|
||||
# Ohio will use generic SIT exempt and additional fields
|
||||
oh_it4_sit_exemptions = fields.Integer(string='Ohio IT-4 Exemptions',
|
||||
help='Line 4')
|
||||
|
||||
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)')
|
||||
2
l10n_us_hr_payroll/security/ir.model.access.csv
Normal file
2
l10n_us_hr_payroll/security/ir.model.access.csv
Normal file
@@ -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
|
||||
|
BIN
l10n_us_hr_payroll/static/description/icon.png
Normal file
BIN
l10n_us_hr_payroll/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -1,5 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from . import test_us_payslip
|
||||
from . import test_us_payslip_2018
|
||||
from . import common
|
||||
from . import test_us_payslip_2019
|
||||
from . import test_us_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_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_oh_ohio_payslip_2019
|
||||
from . import test_us_oh_ohio_payslip_2020
|
||||
|
||||
from . import test_us_pa_pennsylvania_payslip_2019
|
||||
from . import test_us_pa_pennsylvania_payslip_2020
|
||||
|
||||
from . import test_us_tx_texas_payslip_2019
|
||||
from . import test_us_tx_texas_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
|
||||
|
||||
242
l10n_us_hr_payroll/tests/common.py
Executable file
242
l10n_us_hr_payroll/tests/common.py
Executable file
@@ -0,0 +1,242 @@
|
||||
# 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()
|
||||
|
||||
|
||||
class TestUsPayslip(common.TransactionCase):
|
||||
debug = False
|
||||
_logger = getLogger(__name__)
|
||||
|
||||
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'
|
||||
})
|
||||
|
||||
def _createContract(self, employee, **kwargs):
|
||||
if not 'schedule_pay' in kwargs:
|
||||
kwargs['schedule_pay'] = 'monthly'
|
||||
schedule_pay = kwargs['schedule_pay']
|
||||
config_model = self.env['hr.contract.us_payroll_config']
|
||||
contract_model = self.env['hr.contract']
|
||||
config_values = {
|
||||
'name': 'Test Config Values',
|
||||
'employee_id': employee.id,
|
||||
}
|
||||
contract_values = {
|
||||
'name': 'Test Contract',
|
||||
'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'):
|
||||
val = val.id
|
||||
found = False
|
||||
if hasattr(contract_model, key):
|
||||
contract_values[key] = val
|
||||
found = True
|
||||
if hasattr(config_model, key):
|
||||
config_values[key] = val
|
||||
found = True
|
||||
if not found:
|
||||
self._logger.warn('cannot locate attribute names "%s" on contract or payroll config' % (key, ))
|
||||
|
||||
# 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('struct_id'):
|
||||
contract_values['struct_id'] = self.ref('l10n_us_hr_payroll.structure_type_employee')
|
||||
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')
|
||||
|
||||
# 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
|
||||
|
||||
contract = contract_model.create(contract_values)
|
||||
|
||||
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
|
||||
})
|
||||
if hasattr(slip, '_onchange_employee'):
|
||||
slip._onchange_employee()
|
||||
if hasattr(slip, 'onchange_employee'):
|
||||
# Odoo 12
|
||||
slip.onchange_employee()
|
||||
if self.debug:
|
||||
self._logger.warn(slip.read())
|
||||
self._logger.warn(slip.contract_id.read())
|
||||
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 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 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, **extra_contract):
|
||||
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)
|
||||
self.assertPayrollEqual(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)
|
||||
self.assertPayrollEqual(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)
|
||||
self.assertPayrollEqual(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
|
||||
self.assertPayrollEqual(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)
|
||||
self.assertPayrollEqual(cats.get(category, 0.0), 0.0)
|
||||
else:
|
||||
self.assertPayrollEqual(cats.get(category, 0.0), wage * rate)
|
||||
|
||||
def _test_er_suta(self, state_code, rate, date, wage_base=None, **extra_contract):
|
||||
self._test_suta('ER_US_SUTA', state_code, rate, date, wage_base=wage_base, **extra_contract)
|
||||
|
||||
def _test_ee_suta(self, state_code, rate, date, wage_base=None, **extra_contract):
|
||||
self._test_suta('EE_US_SUTA', state_code, rate, date, wage_base=wage_base, **extra_contract)
|
||||
84
l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
Executable file
84
l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2019.py
Executable file
@@ -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)
|
||||
16
l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2020.py
Executable file
16
l10n_us_hr_payroll/tests/test_us_fl_florida_payslip_2020.py
Executable file
@@ -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)
|
||||
135
l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
Executable file
135
l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
Executable file
@@ -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 = 'exempt'
|
||||
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)
|
||||
148
l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
Executable file
148
l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
Executable file
@@ -0,0 +1,148 @@
|
||||
# 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 TestUsGAPayslip(TestUsPayslip):
|
||||
|
||||
# TAXES AND RATES
|
||||
GA_UNEMP_MAX_WAGE = 9500.00
|
||||
GA_UNEMP = 2.70
|
||||
|
||||
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,
|
||||
ga_g4_sit_dependent_allowances=0,
|
||||
ga_g4_sit_additional_allowances=0,
|
||||
ga_g4_sit_filing_status=None,
|
||||
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,
|
||||
ga_g4_sit_dependent_allowances=ga_g4_sit_dependent_allowances,
|
||||
ga_g4_sit_additional_allowances=ga_g4_sit_additional_allowances,
|
||||
ga_g4_sit_filing_status=ga_g4_sit_filing_status,
|
||||
state_id=self.get_us_state('GA'),
|
||||
)
|
||||
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_taxes_weekly_single_with_additional_wh(self):
|
||||
self._test_er_suta('GA', self.GA_UNEMP, date(2020, 1, 1), wage_base=self.GA_UNEMP_MAX_WAGE)
|
||||
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
|
||||
|
||||
self._run_test_sit(wage=salary,
|
||||
schedule_pay=schedule_pay,
|
||||
state_income_tax_additional_withholding=additional_wh,
|
||||
ga_g4_sit_dependent_allowances=allowances,
|
||||
ga_g4_sit_additional_allowances=0,
|
||||
ga_g4_sit_filing_status=filing_status,
|
||||
expected=wh,
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
|
||||
self._run_test_sit(wage=salary,
|
||||
schedule_pay=schedule_pay,
|
||||
state_income_tax_additional_withholding=additional_wh,
|
||||
ga_g4_sit_dependent_allowances=allowances,
|
||||
ga_g4_sit_additional_allowances=0,
|
||||
ga_g4_sit_filing_status=filing_status,
|
||||
expected=wh,
|
||||
)
|
||||
|
||||
# additional from external calculator
|
||||
self._run_test_sit(wage=425.0,
|
||||
schedule_pay='weekly',
|
||||
state_income_tax_additional_withholding=0.0,
|
||||
ga_g4_sit_dependent_allowances=1,
|
||||
ga_g4_sit_additional_allowances=0,
|
||||
ga_g4_sit_filing_status='married filing separate',
|
||||
expected=11.45,
|
||||
)
|
||||
|
||||
self._run_test_sit(wage=3000.0,
|
||||
schedule_pay='quarterly',
|
||||
state_income_tax_additional_withholding=0.0,
|
||||
ga_g4_sit_dependent_allowances=1,
|
||||
ga_g4_sit_additional_allowances=1,
|
||||
ga_g4_sit_filing_status='single',
|
||||
expected=0.0,
|
||||
)
|
||||
|
||||
# TODO 'married filing joint, both spouses working' returns lower than calculator
|
||||
# TODO 'married filing joint, one spouse working' returns lower than calculator
|
||||
|
||||
def test_taxes_exempt(self):
|
||||
salary = 25000.00
|
||||
schedule_pay = 'monthly'
|
||||
allowances = 2
|
||||
filing_status = 'exempt'
|
||||
additional_wh = 15.00
|
||||
|
||||
self._run_test_sit(wage=salary,
|
||||
schedule_pay=schedule_pay,
|
||||
state_income_tax_additional_withholding=additional_wh,
|
||||
ga_g4_sit_dependent_allowances=allowances,
|
||||
ga_g4_sit_additional_allowances=0,
|
||||
ga_g4_sit_filing_status=filing_status,
|
||||
expected=0.0,
|
||||
)
|
||||
94
l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
Executable file
94
l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2019.py
Executable file
@@ -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)
|
||||
120
l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py
Executable file
120
l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py
Executable file
@@ -0,0 +1,120 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from datetime import date
|
||||
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
|
||||
MS_UNEMP_MAX_WAGE = 14000.0
|
||||
|
||||
def test_2020_taxes_one(self):
|
||||
self._test_er_suta('MS', self.MS_UNEMP, date(2020, 1, 1), wage_base=self.MS_UNEMP_MAX_WAGE)
|
||||
|
||||
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('2020 Mississippi tax single first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
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) + 260 # Over 10,000
|
||||
self.assertPayrollEqual(TAX, 540.0)
|
||||
|
||||
ms_withhold = round(TAX / 24) # Semi-Monthly
|
||||
self.assertPayrollEqual(cats['EE_US_SIT'], -ms_withhold)
|
||||
|
||||
def test_2020_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('2020 Mississippi tax single first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
|
||||
|
||||
def test_2020_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='single',
|
||||
ms_89_350_sit_exemption_value=ms_89_350_exemption,
|
||||
state_income_tax_additional_withholding=additional,
|
||||
schedule_pay='monthly')
|
||||
|
||||
self._log('2020 Mississippi tax single first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
STDED = 2300.0 # Single
|
||||
AGP = salary * 12 # Monthly
|
||||
TI = AGP - (ms_89_350_exemption + STDED)
|
||||
self.assertPayrollEqual(TI, 1700.0)
|
||||
TAX = ((TI - 3000) * 0.03)
|
||||
self.assertPayrollEqual(TAX, -39.0)
|
||||
|
||||
ms_withhold = round(TAX / 12) # Monthly
|
||||
self.assertTrue(ms_withhold <= 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_SIT'], -40.0) # only additional
|
||||
|
||||
# Test with higher wage
|
||||
salary = 1700.0
|
||||
employee = self._createEmployee()
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
state_id=self.get_us_state('MS'),
|
||||
ms_89_350_sit_filing_status='single',
|
||||
ms_89_350_sit_exemption_value=ms_89_350_exemption,
|
||||
state_income_tax_additional_withholding=additional,
|
||||
schedule_pay='monthly')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
payslip.compute_sheet()
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
STDED = 2300.0 # Single
|
||||
AGP = salary * 12 # Monthly
|
||||
TI = AGP - (ms_89_350_exemption + STDED)
|
||||
self.assertPayrollEqual(TI, 7100.0)
|
||||
TAX = ((TI - 5000) * 0.04) + 60.0
|
||||
self.assertPayrollEqual(TAX, 144.0)
|
||||
|
||||
ms_withhold = round(TAX / 12) # Monthly
|
||||
self.assertPayrollEqual(ms_withhold, 12.0)
|
||||
self.assertPayrollEqual(cats['EE_US_SIT'], -(ms_withhold + additional))
|
||||
139
l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
Executable file
139
l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2019.py
Executable file
@@ -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)
|
||||
17
l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
Executable file
17
l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
Executable file
@@ -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, process_payslip
|
||||
|
||||
|
||||
class TestUsMtPayslip(TestUsPayslip):
|
||||
# Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705
|
||||
MT_UNEMP_WAGE_MAX = 34100.0
|
||||
MT_UNEMP = 1.18
|
||||
MT_UNEMP_AFT = 0.13
|
||||
|
||||
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)
|
||||
|
||||
# TODO Montana Incometax rates for 2020 when released
|
||||
96
l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
Executable file
96
l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2019.py
Executable file
@@ -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)
|
||||
108
l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py
Executable file
108
l10n_us_hr_payroll/tests/test_us_oh_ohio_payslip_2020.py
Executable file
@@ -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,
|
||||
)
|
||||
33
l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
Executable file
33
l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2019.py
Executable file
@@ -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)
|
||||
43
l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2020.py
Executable file
43
l10n_us_hr_payroll/tests/test_us_pa_pennsylvania_payslip_2020.py
Executable file
@@ -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)
|
||||
@@ -1,119 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from logging import getLogger
|
||||
from sys import float_info as sys_float_info
|
||||
|
||||
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.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
|
||||
def process_payslip(payslip):
|
||||
try:
|
||||
#v9
|
||||
payslip.process_sheet()
|
||||
except AttributeError:
|
||||
payslip.action_payslip_done()
|
||||
|
||||
|
||||
class TestUsPayslip(common.TransactionCase):
|
||||
debug = False
|
||||
_logger = getLogger(__name__)
|
||||
|
||||
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'
|
||||
})
|
||||
|
||||
def _createContract(self, employee, salary,
|
||||
schedule_pay='monthly',
|
||||
w4_allowances=0,
|
||||
w4_filing_status='single',
|
||||
w4_is_nonresident_alien=False,
|
||||
w4_additional_withholding=0.0,
|
||||
external_wages=0.0,
|
||||
struct_id=False,
|
||||
futa_type=USHrContract.FUTA_TYPE_NORMAL,
|
||||
):
|
||||
if not struct_id:
|
||||
struct_id = self.ref('l10n_us_hr_payroll.hr_payroll_salary_structure_us_employee')
|
||||
|
||||
values = {
|
||||
'date_start': '2016-01-01',
|
||||
'date_end': '2030-12-31',
|
||||
'name': 'Contract for Jared 2016',
|
||||
'wage': salary,
|
||||
'type_id': self.ref('hr_contract.hr_contract_type_emp'),
|
||||
'employee_id': employee.id,
|
||||
'struct_id': struct_id,
|
||||
'resource_calendar_id': self.ref('resource.resource_calendar_std'),
|
||||
'schedule_pay': schedule_pay,
|
||||
'w4_allowances': w4_allowances,
|
||||
'w4_filing_status': w4_filing_status,
|
||||
'w4_is_nonresident_alien': w4_is_nonresident_alien,
|
||||
'w4_additional_withholding': w4_additional_withholding,
|
||||
'external_wages': external_wages,
|
||||
'futa_type': futa_type,
|
||||
'state': 'open', # if not "Running" then no automatic selection when Payslip is created
|
||||
}
|
||||
try:
|
||||
values['journal_id'] = self.env['account.journal'].search([('type', '=', 'general')], limit=1).id
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return self.env['hr.contract'].create(values)
|
||||
|
||||
def _createPayslip(self, employee, date_from, date_to):
|
||||
return self.env['hr.payslip'].create({
|
||||
'employee_id': employee.id,
|
||||
'date_from': date_from,
|
||||
'date_to': date_to
|
||||
})
|
||||
|
||||
def _getCategories(self, payslip):
|
||||
detail_lines = payslip.details_by_salary_rule_category
|
||||
categories = {}
|
||||
for line in detail_lines:
|
||||
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))
|
||||
if line.category_id.code not in categories:
|
||||
categories[line.category_id.code] = line.total
|
||||
else:
|
||||
categories[line.category_id.code] += line.total
|
||||
|
||||
return categories
|
||||
|
||||
def assertPayrollEqual(self, first, second):
|
||||
self.assertAlmostEqual(first, second, self.payroll_digits)
|
||||
|
||||
def test_semi_monthly(self):
|
||||
salary = 80000.0
|
||||
employee = self._createEmployee()
|
||||
contract = self._createContract(employee, salary, schedule_pay='semi-monthly')
|
||||
payslip = self._createPayslip(employee, '2016-01-01', '2016-01-14')
|
||||
|
||||
payslip.compute_sheet()
|
||||
@@ -1,368 +0,0 @@
|
||||
from .test_us_payslip import TestUsPayslip, process_payslip
|
||||
|
||||
from odoo.addons.l10n_us_hr_payroll.models.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
from sys import float_info
|
||||
|
||||
|
||||
class TestUsPayslip2018(TestUsPayslip):
|
||||
# FUTA Constants
|
||||
FUTA_RATE_NORMAL = 0.6
|
||||
FUTA_RATE_BASIC = 6.0
|
||||
FUTA_RATE_EXEMPT = 0.0
|
||||
|
||||
# Wage caps
|
||||
FICA_SS_MAX_WAGE = 128400.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
|
||||
|
||||
###
|
||||
# 2018 Taxes and Rates
|
||||
###
|
||||
|
||||
def test_2018_taxes(self):
|
||||
# salary is high so that second payslip runs over max
|
||||
# social security salary
|
||||
salary = 80000.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
self._createContract(employee, salary)
|
||||
|
||||
self._log('2017 tax last slip')
|
||||
payslip = self._createPayslip(employee, '2017-12-01', '2017-12-31')
|
||||
payslip.compute_sheet()
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2018 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], self.FUTA_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], cats['WAGE_US_FUTA'] * self.FUTA)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums for FICA Social Security Wages
|
||||
|
||||
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('2018 tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], remaining_ss_wages)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], remaining_m_wages)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], 0)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], 0)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have reached Medicare Additional (employee only)
|
||||
|
||||
self._log('2018 tax third payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-03-01', '2018-03-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], self.FICA_M_ADD_START_WAGE - (salary * 2)) # aka 40k
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have all salary as Medicare Additional
|
||||
|
||||
self._log('2018 tax fourth payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-04-01', '2018-04-30')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
def test_2018_fed_income_withholding_single(self):
|
||||
salary = 6000.00
|
||||
schedule_pay = 'monthly'
|
||||
w4_allowances = 3
|
||||
w4_allowance_amt = 345.80 * 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(-(371.12 + ((adjusted_salary - 3533) * 0.22)), self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'single')
|
||||
|
||||
self._log('2018 fed income single payslip: adjusted_salary: ' + str(adjusted_salary))
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
|
||||
def test_2018_fed_income_withholding_married_as_single(self):
|
||||
salary = 500.00
|
||||
schedule_pay = 'weekly'
|
||||
w4_allowances = 1
|
||||
w4_allowance_amt = 79.80 * w4_allowances
|
||||
adjusted_salary = salary - w4_allowance_amt # should be 420.50, but would work over a wide value for the rate
|
||||
###
|
||||
# Single MONTHLY form Publication 15
|
||||
expected_withholding = self.float_round(-(18.30 + ((adjusted_salary - 254) * 0.12)), self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married_as_single')
|
||||
|
||||
self._log('2018 fed income married_as_single payslip: adjusted_salary: ' + str(adjusted_salary))
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
|
||||
def test_2018_fed_income_withholding_married(self):
|
||||
salary = 14000.00
|
||||
schedule_pay = 'bi-weekly'
|
||||
w4_allowances = 2
|
||||
w4_allowance_amt = 159.60 * 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(-(2468.56 + ((adjusted_salary - 12560) * 0.32)), self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married')
|
||||
|
||||
self._log('2018 fed income married payslip: adjusted_salary: ' + str(adjusted_salary))
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
|
||||
def test_2018_taxes_with_external(self):
|
||||
|
||||
# social security salary
|
||||
salary = self.FICA_M_ADD_START_WAGE
|
||||
external_wages = 6000.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
self._createContract(employee, salary, external_wages=external_wages)
|
||||
|
||||
self._log('2018 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], self.FICA_SS_MAX_WAGE - external_wages)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], self.FUTA_MAX_WAGE - external_wages)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], cats['WAGE_US_FUTA'] * self.FUTA)
|
||||
|
||||
def test_2018_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, salary, futa_type=USHrContract.FUTA_TYPE_BASIC)
|
||||
|
||||
self._log('2018 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], self.FICA_SS_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], self.FUTA_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], cats['WAGE_US_FUTA'] * futa_rate)
|
||||
|
||||
def test_2018_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, salary, futa_type=USHrContract.FUTA_TYPE_EXEMPT)
|
||||
|
||||
self._log('2018 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], self.FICA_SS_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
|
||||
futa_wages = 0.0
|
||||
if 'WAGE_US_FUTA' in cats:
|
||||
futa_wages = cats['WAGE_US_FUTA']
|
||||
futa = 0.0
|
||||
if 'ER_US_FUTA' in cats:
|
||||
futa = cats['ER_US_FUTA']
|
||||
self.assertPayrollEqual(futa_wages, 0.0)
|
||||
self.assertPayrollEqual(futa, futa_wages * futa_rate)
|
||||
|
||||
def test_2018_fed_income_withholding_nonresident_alien(self):
|
||||
salary = 3500.00
|
||||
schedule_pay = 'quarterly'
|
||||
w4_allowances = 1
|
||||
w4_allowance_amt = 1037.50 * w4_allowances
|
||||
nra_adjustment = 1962.50 # for quarterly
|
||||
adjusted_salary = salary - w4_allowance_amt + nra_adjustment # 4425
|
||||
|
||||
###
|
||||
# Single QUARTERLY form Publication 15
|
||||
expected_withholding = self.float_round(-(238.10 + ((adjusted_salary - 3306) * 0.12)), self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'single',
|
||||
w4_is_nonresident_alien=True)
|
||||
|
||||
self._log('2018 fed income single payslip nonresident alien: adjusted_salary: ' + str(adjusted_salary))
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
|
||||
def test_2018_fed_income_additional_withholding(self):
|
||||
salary = 50000.00
|
||||
schedule_pay = 'annually'
|
||||
w4_additional_withholding = 5000.0
|
||||
w4_allowances = 2
|
||||
w4_allowance_amt = 4150.00 * w4_allowances
|
||||
adjusted_salary = salary - w4_allowance_amt # 41700
|
||||
|
||||
###
|
||||
# Single ANNUAL form Publication 15
|
||||
expected_withholding = \
|
||||
self.float_round(-((1905.00 + ((adjusted_salary - 30600) * 0.12)) + w4_additional_withholding),
|
||||
self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married',
|
||||
w4_additional_withholding=w4_additional_withholding)
|
||||
|
||||
self._log('2018 fed income married payslip additional withholding: adjusted_salary: ' + str(adjusted_salary))
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
|
||||
def test_2018_taxes_with_w4_exempt(self):
|
||||
salary = 6000.0
|
||||
schedule_pay = 'bi-weekly'
|
||||
w4_allowances = 0
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, '')
|
||||
|
||||
self._log('2018 tax w4 exempt payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
fed_inc_withhold = 0.0
|
||||
if 'EE_US_FED_INC_WITHHOLD' in cats:
|
||||
fed_inc_withhold = cats['EE_US_FED_INC_WITHHOLD']
|
||||
self.assertPayrollEqual(fed_inc_withhold, 0.0)
|
||||
|
||||
def test_2018_taxes_with_fica_exempt(self):
|
||||
salary = 6000.0
|
||||
schedule_pay = 'bi-weekly'
|
||||
w4_allowances = 2
|
||||
employee = self._createEmployee()
|
||||
contract = self._createContract(employee, salary, schedule_pay, w4_allowances)
|
||||
contract.fica_exempt = True
|
||||
|
||||
self._log('2018 tax w4 exempt payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
ss_wages = cats.get('WAGE_US_FICA_SS', 0.0)
|
||||
med_wages = cats.get('WAGE_US_FICA_M', 0.0)
|
||||
ss = cats.get('EE_US_FICA_SS', 0.0)
|
||||
med = cats.get('EE_US_FICA_M', 0.0)
|
||||
self.assertPayrollEqual(ss_wages, 0.0)
|
||||
self.assertPayrollEqual(med_wages, 0.0)
|
||||
self.assertPayrollEqual(ss, 0.0)
|
||||
self.assertPayrollEqual(med, 0.0)
|
||||
239
l10n_us_hr_payroll/tests/test_us_payslip_2019.py
Executable file → Normal file
239
l10n_us_hr_payroll/tests/test_us_payslip_2019.py
Executable file → Normal file
@@ -1,6 +1,8 @@
|
||||
from .test_us_payslip import TestUsPayslip, process_payslip
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.l10n_us_hr_payroll.models.l10n_us_hr_payroll import USHrContract
|
||||
from .common import TestUsPayslip, process_payslip
|
||||
|
||||
from odoo.addons.l10n_us_hr_payroll.models.hr_contract import USHRContract
|
||||
|
||||
from sys import float_info
|
||||
|
||||
@@ -28,41 +30,44 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
###
|
||||
|
||||
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()
|
||||
|
||||
self._createContract(employee, salary)
|
||||
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)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], self.FUTA_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], cats['WAGE_US_FUTA'] * self.FUTA)
|
||||
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 maximums for FICA Social Security Wages
|
||||
|
||||
# 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
|
||||
|
||||
@@ -72,32 +77,25 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], remaining_ss_wages)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], remaining_m_wages)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], 0)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], 0)
|
||||
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(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], self.FICA_M_ADD_START_WAGE - (salary * 2)) # aka 40k
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
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)
|
||||
|
||||
@@ -109,14 +107,15 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
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
|
||||
@@ -127,16 +126,18 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
expected_withholding = self.float_round(-(378.52 + ((adjusted_salary - 3606) * 0.22)), self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'single')
|
||||
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_FED_INC_WITHHOLD'], expected_withholding)
|
||||
self.assertPayrollEqual(cats['EE_US_941_FIT'], expected_withholding)
|
||||
|
||||
def test_2019_fed_income_withholding_married_as_single(self):
|
||||
salary = 500.00
|
||||
@@ -145,20 +146,21 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
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
|
||||
###
|
||||
# Single MONTHLY form Publication 15
|
||||
expected_withholding = self.float_round(-(18.70 + ((adjusted_salary - 260) * 0.12)), self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married_as_single')
|
||||
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_FED_INC_WITHHOLD'], expected_withholding)
|
||||
self.assertPayrollEqual(cats['EE_US_941_FIT'], expected_withholding)
|
||||
|
||||
def test_2019_fed_income_withholding_married(self):
|
||||
salary = 14000.00
|
||||
@@ -171,53 +173,52 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
expected_withholding = self.float_round(-(2519.06 + ((adjusted_salary - 12817) * 0.32)), self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married')
|
||||
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)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
# 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, salary, external_wages=external_wages)
|
||||
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)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], self.FICA_SS_MAX_WAGE - external_wages)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], self.FUTA_MAX_WAGE - external_wages)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], cats['WAGE_US_FUTA'] * self.FUTA)
|
||||
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):
|
||||
self.debug = False
|
||||
futa_rate = self.FUTA_RATE_BASIC / -100.0
|
||||
# social security salary
|
||||
salary = self.FICA_M_ADD_START_WAGE
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
self._createContract(employee, salary, futa_type=USHrContract.FUTA_TYPE_BASIC)
|
||||
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')
|
||||
@@ -225,52 +226,26 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_SS'], self.FICA_SS_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
self.assertPayrollEqual(cats['WAGE_US_FUTA'], self.FUTA_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['ER_US_FUTA'], cats['WAGE_US_FUTA'] * futa_rate)
|
||||
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, salary, futa_type=USHrContract.FUTA_TYPE_EXEMPT)
|
||||
|
||||
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['WAGE_US_FICA_SS'], self.FICA_SS_MAX_WAGE)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_SS'], cats['WAGE_US_FICA_SS'] * self.FICA_SS)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M'], salary)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M'], cats['WAGE_US_FICA_M'] * self.FICA_M)
|
||||
self.assertPayrollEqual(cats['WAGE_US_FICA_M_ADD'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_FICA_M_ADD'], cats['WAGE_US_FICA_M_ADD'] * self.FICA_M_ADD)
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_SS'], cats['EE_US_FICA_SS'])
|
||||
self.assertPayrollEqual(cats['ER_US_FICA_M'], cats['EE_US_FICA_M'])
|
||||
|
||||
futa_wages = 0.0
|
||||
if 'WAGE_US_FUTA' in cats:
|
||||
futa_wages = cats['WAGE_US_FUTA']
|
||||
futa = 0.0
|
||||
if 'ER_US_FUTA' in cats:
|
||||
futa = cats['ER_US_FUTA']
|
||||
self.assertPayrollEqual(futa_wages, 0.0)
|
||||
self.assertPayrollEqual(futa, futa_wages * futa_rate)
|
||||
self.assertPayrollEqual(cats['ER_US_940_FUTA'], 0.0)
|
||||
|
||||
def test_2019_fed_income_withholding_nonresident_alien(self):
|
||||
salary = 3500.00
|
||||
@@ -278,24 +253,26 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
w4_allowances = 1
|
||||
w4_allowance_amt = 1050.0 * w4_allowances
|
||||
nra_adjustment = 2000.0 # for quarterly
|
||||
adjusted_salary = salary - w4_allowance_amt + nra_adjustment # 4425
|
||||
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()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'single',
|
||||
w4_is_nonresident_alien=True)
|
||||
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()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
rules = self._getRules(payslip)
|
||||
self.assertPayrollEqual(rules['EE_US_941_FIT'], expected_withholding)
|
||||
|
||||
def test_2019_fed_income_additional_withholding(self):
|
||||
salary = 50000.00
|
||||
@@ -312,44 +289,45 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
self.payroll_digits)
|
||||
|
||||
employee = self._createEmployee()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married',
|
||||
w4_additional_withholding=w4_additional_withholding)
|
||||
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()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['EE_US_FED_INC_WITHHOLD'], expected_withholding)
|
||||
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()
|
||||
self._createContract(employee, salary, schedule_pay, w4_allowances, '')
|
||||
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()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
fed_inc_withhold = 0.0
|
||||
if 'EE_US_FED_INC_WITHHOLD' in cats:
|
||||
fed_inc_withhold = cats['EE_US_FED_INC_WITHHOLD']
|
||||
self.assertPayrollEqual(fed_inc_withhold, 0.0)
|
||||
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'
|
||||
w4_allowances = 2
|
||||
employee = self._createEmployee()
|
||||
contract = self._createContract(employee, salary, schedule_pay, w4_allowances)
|
||||
contract.fica_exempt = True
|
||||
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')
|
||||
@@ -357,12 +335,5 @@ class TestUsPayslip2019(TestUsPayslip):
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
ss_wages = cats.get('WAGE_US_FICA_SS', 0.0)
|
||||
med_wages = cats.get('WAGE_US_FICA_M', 0.0)
|
||||
ss = cats.get('EE_US_FICA_SS', 0.0)
|
||||
med = cats.get('EE_US_FICA_M', 0.0)
|
||||
self.assertPayrollEqual(ss_wages, 0.0)
|
||||
self.assertPayrollEqual(med_wages, 0.0)
|
||||
self.assertPayrollEqual(ss, 0.0)
|
||||
self.assertPayrollEqual(med, 0.0)
|
||||
self.assertPayrollEqual(cats['EE_US_941_FICA'], 0.0)
|
||||
self.assertPayrollEqual(cats['ER_US_941_FICA'], 0.0)
|
||||
|
||||
302
l10n_us_hr_payroll/tests/test_us_payslip_2020.py
Normal file
302
l10n_us_hr_payroll/tests/test_us_payslip_2020.py
Normal file
@@ -0,0 +1,302 @@
|
||||
# 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 TestUsPayslip2020(TestUsPayslip):
|
||||
# FUTA Constants
|
||||
FUTA_RATE_NORMAL = 0.6
|
||||
FUTA_RATE_BASIC = 6.0
|
||||
FUTA_RATE_EXEMPT = 0.0
|
||||
|
||||
# Wage caps
|
||||
FICA_SS_MAX_WAGE = 137700.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
|
||||
|
||||
###
|
||||
# 2020 Taxes and Rates
|
||||
###
|
||||
|
||||
def test_2020_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('2019 tax last slip')
|
||||
payslip = self._createPayslip(employee, '2019-12-01', '2019-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('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)
|
||||
# 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('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)
|
||||
|
||||
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('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)
|
||||
|
||||
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('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)
|
||||
|
||||
self.assertPayrollEqual(rules['EE_US_941_FICA_M_ADD'], salary * self.FICA_M_ADD)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
def test_2020_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('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)
|
||||
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_2020_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('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)
|
||||
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_2020_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('2020 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
payslip.compute_sheet()
|
||||
cats = self._getCategories(payslip)
|
||||
self.assertPayrollEqual(cats['ER_US_940_FUTA'], 0.0)
|
||||
|
||||
def test_2020_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('2020 tax w4 exempt payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-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, '2020-01-01', '2020-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_2020_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=1132.0,
|
||||
expected_higher=1459.0,
|
||||
)
|
||||
|
||||
def test_2020_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=24.0,
|
||||
expected_higher=45.0,
|
||||
)
|
||||
|
||||
def test_2020_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=2621.0,
|
||||
expected_higher=3702.0,
|
||||
)
|
||||
|
||||
def test_2020_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=235.0,
|
||||
expected_higher=391.0,
|
||||
)
|
||||
|
||||
def test_2020_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,
|
||||
)
|
||||
100
l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
Executable file
100
l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2019.py
Executable file
@@ -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)
|
||||
17
l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2020.py
Executable file
17
l10n_us_hr_payroll/tests/test_us_tx_texas_payslip_2020.py
Executable file
@@ -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)
|
||||
133
l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
Normal file
133
l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2019.py
Normal file
@@ -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)
|
||||
116
l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2020.py
Normal file
116
l10n_us_hr_payroll/tests/test_us_va_virginia_payslip_2020.py
Normal file
@@ -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,)
|
||||
88
l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
Executable file
88
l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2019.py
Executable file
@@ -0,0 +1,88 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from datetime import date
|
||||
from .common import TestUsPayslip, process_payslip
|
||||
|
||||
|
||||
class TestUsWAPayslip(TestUsPayslip):
|
||||
###
|
||||
# Taxes and Rates
|
||||
###
|
||||
WA_UNEMP_MAX_WAGE = 49800.0
|
||||
WA_UNEMP_RATE = 1.18
|
||||
WA_FML_RATE = 0.4
|
||||
WA_FML_RATE_EE = 66.33
|
||||
WA_FML_RATE_ER = 33.67
|
||||
|
||||
def setUp(self):
|
||||
super(TestUsWAPayslip, self).setUp()
|
||||
# self.lni = self.env['hr.contract.lni.wa'].create({
|
||||
# 'name': '5302 Computer Consulting',
|
||||
# 'rate': 0.1261,
|
||||
# 'rate_emp_withhold': 0.05575,
|
||||
# })
|
||||
self.test_ee_lni = 0.05575 # per 100 hours
|
||||
self.test_er_lni = 0.1261 # per 100 hours
|
||||
self.parameter_lni_ee = self.env['hr.payroll.rate'].create({
|
||||
'name': 'Test LNI EE',
|
||||
'code': 'test_lni_ee',
|
||||
'date_from': date(2019, 1, 1),
|
||||
'parameter_value': str(self.test_ee_lni * 100),
|
||||
})
|
||||
self.parameter_lni_er = self.env['hr.payroll.rate'].create({
|
||||
'name': 'Test LNI ER',
|
||||
'code': 'test_lni_er',
|
||||
'date_from': date(2019, 1, 1),
|
||||
'parameter_value': str(self.test_er_lni * 100),
|
||||
})
|
||||
|
||||
def test_2019_taxes(self):
|
||||
salary = 25000.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
state_id=self.get_us_state('WA'),
|
||||
workers_comp_ee_code=self.parameter_lni_ee.code,
|
||||
workers_comp_er_code=self.parameter_lni_er.code,
|
||||
)
|
||||
self._log(str(contract.resource_calendar_id) + ' ' + contract.resource_calendar_id.name)
|
||||
|
||||
|
||||
# tax rates
|
||||
wa_unemp = self.WA_UNEMP_RATE / -100.0
|
||||
|
||||
self._log('2019 Washington tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
|
||||
hours_in_period = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100').number_of_hours
|
||||
self.assertEqual(hours_in_period, 184) # only asserted to test algorithm
|
||||
payslip.compute_sheet()
|
||||
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['ER_US_SUTA'], salary * wa_unemp)
|
||||
self.assertPayrollEqual(rules['EE_US_WA_LNI'], -(self.test_ee_lni * hours_in_period))
|
||||
self.assertPayrollEqual(rules['ER_US_WA_LNI'], -(self.test_er_lni * hours_in_period) - rules['EE_US_WA_LNI'])
|
||||
# Both of these are known to be within 1 penny
|
||||
self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_EE / 100.0)))
|
||||
self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_ER / 100.0)))
|
||||
|
||||
# FML
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_wa_unemp_wages = self.WA_UNEMP_MAX_WAGE - salary if (self.WA_UNEMP_MAX_WAGE - 2*salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2019 Washington tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_wa_unemp_wages * wa_unemp)
|
||||
86
l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2020.py
Executable file
86
l10n_us_hr_payroll/tests/test_us_wa_washington_payslip_2020.py
Executable file
@@ -0,0 +1,86 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from datetime import date
|
||||
from .common import TestUsPayslip, process_payslip
|
||||
|
||||
|
||||
class TestUsWAPayslip(TestUsPayslip):
|
||||
###
|
||||
# Taxes and Rates
|
||||
###
|
||||
WA_UNEMP_MAX_WAGE = 52700.00
|
||||
WA_UNEMP_RATE = 1.0
|
||||
WA_FML_MAX_WAGE = 137700.00
|
||||
WA_FML_RATE = 0.4
|
||||
WA_FML_RATE_EE = 66.33
|
||||
WA_FML_RATE_ER = 33.67
|
||||
|
||||
def setUp(self):
|
||||
super(TestUsWAPayslip, self).setUp()
|
||||
# self.lni = self.env['hr.contract.lni.wa'].create({
|
||||
# 'name': '5302 Computer Consulting',
|
||||
# 'rate': 0.1261,
|
||||
# 'rate_emp_withhold': 0.05575,
|
||||
# })
|
||||
self.test_ee_lni = 0.05575 # per 100 hours
|
||||
self.test_er_lni = 0.1261 # per 100 hours
|
||||
self.parameter_lni_ee = self.env['hr.payroll.rate'].create({
|
||||
'name': 'Test LNI EE',
|
||||
'code': 'test_lni_ee',
|
||||
'date_from': date(2019, 1, 1),
|
||||
'parameter_value': str(self.test_ee_lni * 100),
|
||||
})
|
||||
self.parameter_lni_er = self.env['hr.payroll.rate'].create({
|
||||
'name': 'Test LNI ER',
|
||||
'code': 'test_lni_er',
|
||||
'date_from': date(2019, 1, 1),
|
||||
'parameter_value': str(self.test_er_lni * 100),
|
||||
})
|
||||
|
||||
def test_2020_taxes(self):
|
||||
self._test_er_suta('WA', self.WA_UNEMP_RATE, date(2020, 1, 1), wage_base=self.WA_UNEMP_MAX_WAGE)
|
||||
|
||||
salary = (self.WA_FML_MAX_WAGE / 2.0) + 1000.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
state_id=self.get_us_state('WA'),
|
||||
workers_comp_ee_code=self.parameter_lni_ee.code,
|
||||
workers_comp_er_code=self.parameter_lni_er.code,
|
||||
)
|
||||
self._log(str(contract.resource_calendar_id) + ' ' + contract.resource_calendar_id.name)
|
||||
|
||||
|
||||
# Non SUTA
|
||||
self._log('2020 Washington tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
hours_in_period = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100').number_of_hours
|
||||
self.assertEqual(hours_in_period, 184) # only asserted to test algorithm
|
||||
payslip.compute_sheet()
|
||||
|
||||
rules = self._getRules(payslip)
|
||||
|
||||
self.assertPayrollEqual(rules['EE_US_WA_LNI'], -(self.test_ee_lni * hours_in_period))
|
||||
self.assertPayrollEqual(rules['ER_US_WA_LNI'], -(self.test_er_lni * hours_in_period) - rules['EE_US_WA_LNI'])
|
||||
# Both of these are known to be within 1 penny
|
||||
self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_EE / 100.0)))
|
||||
self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], -(salary * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_ER / 100.0)))
|
||||
process_payslip(payslip)
|
||||
|
||||
# Second payslip
|
||||
remaining_wage = self.WA_FML_MAX_WAGE - salary
|
||||
payslip = self._createPayslip(employee, '2020-03-01', '2020-03-31')
|
||||
payslip.compute_sheet()
|
||||
rules = self._getRules(payslip)
|
||||
self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], -(remaining_wage * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_EE / 100.0)))
|
||||
self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], -(remaining_wage * (self.WA_FML_RATE / 100.0) * (self.WA_FML_RATE_ER / 100.0)))
|
||||
process_payslip(payslip)
|
||||
|
||||
# Third payslip
|
||||
payslip = self._createPayslip(employee, '2020-04-01', '2020-04-30')
|
||||
payslip.compute_sheet()
|
||||
rules = self._getRules(payslip)
|
||||
self.assertPayrollAlmostEqual(rules['EE_US_WA_FML'], 0.0)
|
||||
self.assertPayrollAlmostEqual(rules['ER_US_WA_FML'], 0.0)
|
||||
19
l10n_us_hr_payroll/views/hr_contract_views.xml
Normal file
19
l10n_us_hr_payroll/views/hr_contract_views.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="hr_contract_view_form_inherit_l10n_us" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="inherit_id" ref="hr_payroll.hr_contract_form_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='struct_id']" position="after">
|
||||
<field name="us_payroll_config_id"
|
||||
domain="[('employee_id', '=', employee_id)]"
|
||||
attrs="{'invisible': [('struct_id', '!=', %(l10n_us_hr_payroll.structure_type_employee)s)],
|
||||
'required': [('struct_id', '=', %(l10n_us_hr_payroll.structure_type_employee)s)]}"
|
||||
context="{'default_employee_id': employee_id}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="hr_contract_form_l10n_us_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//page[@name='other']" position="before">
|
||||
<page string="US Federal Filing">
|
||||
<group>
|
||||
<group string="W4" name="w4">
|
||||
<field name="w4_filing_status" string="Filing Status"/>
|
||||
<field name="w4_allowances" string="Allowances"/>
|
||||
<field name="w4_additional_withholding" string="Additional Withholding"/>
|
||||
<field name="w4_is_nonresident_alien" string="Non-resident Alien"/>
|
||||
</group>
|
||||
<group string="Other" name="other">
|
||||
<field name="external_wages" string="External YTD Wages"/>
|
||||
<field name="futa_type" string="Unemployment Tax Type (FUTA)"/>
|
||||
<field name="fica_exempt"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="US State Filing">
|
||||
<group name="state_filing"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
126
l10n_us_hr_payroll/views/us_payroll_config_views.xml
Normal file
126
l10n_us_hr_payroll/views/us_payroll_config_views.xml
Normal file
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="us_payroll_config_tree" model="ir.ui.view">
|
||||
<field name="name">hr.contract.us_payroll_config.tree</field>
|
||||
<field name="model">hr.contract.us_payroll_config</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Employee Payroll Forms">
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
<field name="state_id"/>
|
||||
<field name="create_date"/>
|
||||
<field name="write_date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="us_payroll_config_form" model="ir.ui.view">
|
||||
<field name="name">hr.contract.us_payroll_config.form</field>
|
||||
<field name="model">hr.contract.us_payroll_config</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Employee Payroll Forms">
|
||||
<sheet>
|
||||
<group name="General">
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
</group>
|
||||
<group>
|
||||
<group name="federal" string="Federal">
|
||||
<p colspan="2"><h3>Form 940 - Federal Unemployment</h3></p>
|
||||
<field name="fed_940_type" string="Federal Unemployment Rate"/>
|
||||
<p colspan="2"><h3>Form 941 / W4 - Federal Income Tax</h3></p>
|
||||
<field name="fed_941_fica_exempt" string="FICA Exempt"/>
|
||||
<field name="fed_941_fit_w4_filing_status" string="Filing Status"/>
|
||||
<field name="fed_941_fit_w4_allowances" string="Allowances (Old W4)"/>
|
||||
<field name="fed_941_fit_w4_is_nonresident_alien" string="Is Nonresident Alien"/>
|
||||
<field name="fed_941_fit_w4_multiple_jobs_higher" string="Multiple Jobs Checked"/>
|
||||
<field name="fed_941_fit_w4_dependent_credit" string="Dependent Credit"/>
|
||||
<field name="fed_941_fit_w4_other_income" string="Other Income"/>
|
||||
<field name="fed_941_fit_w4_deductions" string="Deductions"/>
|
||||
<field name="fed_941_fit_w4_additional_withholding" string="Additional Withholding"/>
|
||||
<p colspan="2"><h3>State Information and Extra</h3></p>
|
||||
<field name="state_id" domain="[('country_id', '=', %(base.us)s)]" options="{'no_create': True}"/>
|
||||
<field name="workers_comp_ee_code"/>
|
||||
<field name="workers_comp_er_code"/>
|
||||
</group>
|
||||
|
||||
<group name="state_fl_florida" string="FL Florida" attrs="{'invisible':[('state_id', '!=', %(base.state_us_10)s)]}">
|
||||
<p colspan="2"><h3>No additional fields.</h3></p>
|
||||
</group>
|
||||
<group name="state_ga_georgia" string="GA Georgia" attrs="{'invisible':[('state_id', '!=', %(base.state_us_11)s)]}">
|
||||
<p colspan="2"><h3>Form G-4 - State Income Tax</h3></p>
|
||||
<field name="ga_g4_sit_filing_status" string="Marital Status 3."/>
|
||||
<field name="ga_g4_sit_dependent_allowances" string="Dependent Allowances 4."/>
|
||||
<field name="ga_g4_sit_additional_allowances" string="Additional Allowances 5."/>
|
||||
<field name="state_income_tax_additional_withholding" string="Additional Withholding 6."/>
|
||||
<field name="state_income_tax_exempt" string="Exempt 8."/>
|
||||
</group>
|
||||
<group name="state_ms_mississippi" string="MS Mississippi" attrs="{'invisible':[('state_id', '!=', %(base.state_us_37)s)]}">
|
||||
<p colspan="2"><h3>Form 89-350 - State Income Tax</h3></p>
|
||||
<field name="ms_89_350_sit_filing_status" string="Marital Status 1. 2. 3. 8."/>
|
||||
<field name="ms_89_350_sit_exemption_value" string="Exemptions (Total) 6."/>
|
||||
<field name="state_income_tax_additional_withholding" string="Additional Withholding 7."/>
|
||||
</group>
|
||||
<group name="state_mt_montana" string="MT Montana" attrs="{'invisible':[('state_id', '!=', %(base.state_us_21)s)]}">
|
||||
<p colspan="2"><h3>Form MT-4 - State Income Tax</h3></p>
|
||||
<field name="mt_mw4_sit_exempt" string="Exempt"/>
|
||||
<field name="mt_mw4_sit_exemptions" string="Exemptions"/>
|
||||
<field name="state_income_tax_additional_withholding" string="Additional Withholding (Box H)"/>
|
||||
</group>
|
||||
<group name="state_oh_ohio" string="OH Ohio" attrs="{'invisible':[('state_id', '!=', %(base.state_us_30)s)]}">
|
||||
<p colspan="2"><h3>Form IT-4 - State Income Tax</h3></p>
|
||||
<field name="state_income_tax_exempt"/>
|
||||
<field name="oh_it4_sit_exemptions" string="Exemptions"/>
|
||||
<field name="state_income_tax_additional_withholding" string="Additional Withholding (Line 5)"/>
|
||||
</group>
|
||||
<group name="state_pa_pennsylvania" string="PA Pennsylvania" attrs="{'invisible':[('state_id', '!=', %(base.state_us_39)s)]}">
|
||||
<field name="state_income_tax_exempt"/>
|
||||
<field name="state_income_tax_additional_withholding"/>
|
||||
</group>
|
||||
<group name="state_tx_texas" string="TX Texas" attrs="{'invisible':[('state_id', '!=', %(base.state_us_44)s)]}">
|
||||
<p colspan="2"><h3>No additional fields.</h3></p>
|
||||
</group>
|
||||
<group name="state_va_virginia" string="VA Virginia" attrs="{'invisible':[('state_id', '!=', %(base.state_us_47)s)]}">
|
||||
<p colspan="2"><h3>Form VA-4/VA-4P - State Income Tax</h3></p>
|
||||
<field name="va_va4_sit_exemptions" string="Personal Exemptions (Line 1(a))"/>
|
||||
<field name="va_va4_sit_other_exemptions" string="Age & Blindness Exemptions (Line 1(b))"/>
|
||||
<field name="state_income_tax_additional_withholding" string="Additional Withholding (Line 2)"/>
|
||||
<field name="state_income_tax_exempt" string="Exempt (Line 3 or 4)"/>
|
||||
</group>
|
||||
<group name="state_wa_washington" string="WA Washington" attrs="{'invisible':[('state_id', '!=', %(base.state_us_48)s)]}">
|
||||
<p colspan="2"><h3>No additional fields.</h3></p>
|
||||
<p colspan="2">Ensure that your Employee and Employer workers' comp code fields are filled in for WA LNI withholding.</p>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="us_payroll_config_search" model="ir.ui.view">
|
||||
<field name="name">hr.contract.us_payroll_config.search</field>
|
||||
<field name="model">hr.contract.us_payroll_config</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Employee Payroll Forms Search">
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
<field name="state_id"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="us_payroll_config_action_main" model="ir.actions.act_window">
|
||||
<field name="name">Employee Payroll Forms</field>
|
||||
<field name="res_model">hr.contract.us_payroll_config</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
No Forms
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="us_payroll_config_menu_main" name="Payroll Forms"
|
||||
action="us_payroll_config_action_main"
|
||||
sequence="10" parent="hr.menu_hr_root"/>
|
||||
</odoo>
|
||||
@@ -25,5 +25,5 @@ USA::Iowa Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -22,5 +22,5 @@ USA::Kansas Payroll Rules.
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::Michigan Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA - Minnesota Payroll Rules
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ USA::Missouri Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::Mississippi Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::Montana Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::North Carolina Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -28,5 +28,5 @@ USA::New Jersey Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ USA::New York Payroll Rules.
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -26,5 +26,5 @@ USA::Ohio Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ USA::Pennsylvania Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@ USA::South Carolina Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -26,5 +26,5 @@ USA::Texas Payroll Rules.
|
||||
'data/rules.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::Virginia Payroll Rules.
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ USA::Washington Payroll Rules.
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
'installable': False
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user