mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
#31 Merge branch 'mig/12.0/l10n_us_hr_payroll' into mig/12.0/us_payroll
This commit is contained in:
77
l10n_us_hr_payroll/README.rst
Normal file
77
l10n_us_hr_payroll/README.rst
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
******************
|
||||||
|
Hibou - US Payroll
|
||||||
|
******************
|
||||||
|
|
||||||
|
Calculations and contribution registers for United States Payroll.
|
||||||
|
|
||||||
|
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||||
|
|
||||||
|
=============
|
||||||
|
Main Features
|
||||||
|
=============
|
||||||
|
|
||||||
|
* Contribution registers and partners for:
|
||||||
|
* The Electronic Federal Tax Payment System (EFTPS) - Form 941
|
||||||
|
* The Electronic Federal Tax Payment System (EFTPS) - Form 940
|
||||||
|
* The Electronic Federal Tax Payment System (EFTPS) - Form 941 (FICA + Federal Withholding)
|
||||||
|
* The Electronic Federal Tax Payment System (EFTPS) - Form 940 (FUTA)
|
||||||
|
|
||||||
|
* Contract level FICA Social Security
|
||||||
|
* Contract level FICA Employee Medicare
|
||||||
|
* Contract level FICA Employee Medicare Additional
|
||||||
|
* Contract level Federal Income Withholding
|
||||||
|
* Company level FICA Social Security
|
||||||
|
* Company level FICA Medicare
|
||||||
|
* Company level FUTA Federal Unemployment
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: https://user-images.githubusercontent.com/15882954/41485460-76a0060c-7095-11e8-851a-fec562013ce4.png
|
||||||
|
:alt: 'Employee Contract Detail'
|
||||||
|
:width: 988
|
||||||
|
:align: left
|
||||||
|
|
||||||
|
USA Employee added to Contract Salary Structure Menu
|
||||||
|
|
||||||
|
.. image:: https://user-images.githubusercontent.com/15882954/41485484-880f0816-7095-11e8-9ad0-874b3270c308.png
|
||||||
|
:alt: 'Computed Pay Slip Detail'
|
||||||
|
:width: 988
|
||||||
|
:align: left
|
||||||
|
|
||||||
|
Upgrading to 2019
|
||||||
|
==========================
|
||||||
|
|
||||||
|
If you were using this prior to January 2019, then you will need to run the following
|
||||||
|
migration script.
|
||||||
|
|
||||||
|
Odoo Shell code::
|
||||||
|
|
||||||
|
def migrate_rule_name(rule_id):
|
||||||
|
main = env.ref(rule_id)
|
||||||
|
old_2017 = env.ref(rule_id.replace('2018', '2017'))
|
||||||
|
old_2016 = env.ref(rule_id.replace('2018', '2016'))
|
||||||
|
lines = env['hr.payslip.line'].search([('salary_rule_id', 'in', [old_2017.id, old_2016.id,])])
|
||||||
|
lines.write({'salary_rule_id': main.id})
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
'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_fica_emp_ss_2018',
|
||||||
|
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_m_2018',
|
||||||
|
'l10n_us_hr_payroll.hr_payroll_rules_fica_emp_m_add_2018',
|
||||||
|
'l10n_us_hr_payroll.hr_payroll_rules_fed_inc_withhold_2018_single',
|
||||||
|
'l10n_us_hr_payroll.hr_payroll_rules_fed_inc_withhold_2018_married',
|
||||||
|
'l10n_us_hr_payroll.hr_payroll_rules_futa_wages_2018',
|
||||||
|
'l10n_us_hr_payroll.hr_payroll_rules_futa_2018',
|
||||||
|
]
|
||||||
|
for rule_id in rules:
|
||||||
|
migrate_rule_name(rule_id)
|
||||||
|
|
||||||
|
env.cr.commit()
|
||||||
|
|
||||||
|
|
||||||
|
=======
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||||
|
Copyright Hibou Corp. 2018
|
||||||
1
l10n_us_hr_payroll/__init__.py
Executable file
1
l10n_us_hr_payroll/__init__.py
Executable file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
30
l10n_us_hr_payroll/__manifest__.py
Executable file
30
l10n_us_hr_payroll/__manifest__.py
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
'name': 'USA - Payroll',
|
||||||
|
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'category': 'Localization',
|
||||||
|
'depends': ['hr_payroll', 'hr_payroll_rate'],
|
||||||
|
'version': '12.0.2019.1.0',
|
||||||
|
'description': """
|
||||||
|
USA Payroll Rules.
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Contract W4 Filing Status & Allowances
|
||||||
|
* FICA Social Security (with wages cap)
|
||||||
|
* FICA Medicare
|
||||||
|
* FICA Additioal Medicare Wages & Tax
|
||||||
|
* FUTA Federal Unemployment (with wages cap)
|
||||||
|
* Federal Income Tax Withholdings based on W4 values
|
||||||
|
""",
|
||||||
|
|
||||||
|
'auto_install': False,
|
||||||
|
'website': 'https://hibou.io/',
|
||||||
|
'data': [
|
||||||
|
'views/l10n_us_hr_payroll_view.xml',
|
||||||
|
'data/base.xml',
|
||||||
|
'data/rates.xml',
|
||||||
|
'data/rules.xml',
|
||||||
|
'data/final.xml',
|
||||||
|
],
|
||||||
|
'installable': True
|
||||||
|
}
|
||||||
83
l10n_us_hr_payroll/data/base.xml
Executable file
83
l10n_us_hr_payroll/data/base.xml
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
<?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>
|
||||||
|
<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>
|
||||||
|
<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"/>
|
||||||
|
</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"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
29
l10n_us_hr_payroll/data/final.xml
Executable file
29
l10n_us_hr_payroll/data/final.xml
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<!-- HR PAYROLL STRUCTURE -->
|
||||||
|
<record id="hr_payroll_salary_structure_us_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_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'),
|
||||||
|
])]" name="rule_ids"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="parent_id" ref="hr_payroll.structure_base"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
68
l10n_us_hr_payroll/data/rates.xml
Normal file
68
l10n_us_hr_payroll/data/rates.xml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?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>
|
||||||
1058
l10n_us_hr_payroll/data/rules.xml
Executable file
1058
l10n_us_hr_payroll/data/rules.xml
Executable file
File diff suppressed because it is too large
Load Diff
1
l10n_us_hr_payroll/models/__init__.py
Normal file
1
l10n_us_hr_payroll/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import l10n_us_hr_payroll
|
||||||
44
l10n_us_hr_payroll/models/l10n_us_hr_payroll.py
Executable file
44
l10n_us_hr_payroll/models/l10n_us_hr_payroll.py
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
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')
|
||||||
3
l10n_us_hr_payroll/tests/__init__.py
Executable file
3
l10n_us_hr_payroll/tests/__init__.py
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
from . import test_us_payslip
|
||||||
|
from . import test_us_payslip_2018
|
||||||
|
from . import test_us_payslip_2019
|
||||||
117
l10n_us_hr_payroll/tests/test_us_payslip.py
Executable file
117
l10n_us_hr_payroll/tests/test_us_payslip.py
Executable file
@@ -0,0 +1,117 @@
|
|||||||
|
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()
|
||||||
368
l10n_us_hr_payroll/tests/test_us_payslip_2018.py
Executable file
368
l10n_us_hr_payroll/tests/test_us_payslip_2018.py
Executable file
@@ -0,0 +1,368 @@
|
|||||||
|
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)
|
||||||
368
l10n_us_hr_payroll/tests/test_us_payslip_2019.py
Executable file
368
l10n_us_hr_payroll/tests/test_us_payslip_2019.py
Executable file
@@ -0,0 +1,368 @@
|
|||||||
|
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 TestUsPayslip2019(TestUsPayslip):
|
||||||
|
# FUTA Constants
|
||||||
|
FUTA_RATE_NORMAL = 0.6
|
||||||
|
FUTA_RATE_BASIC = 6.0
|
||||||
|
FUTA_RATE_EXEMPT = 0.0
|
||||||
|
|
||||||
|
# Wage caps
|
||||||
|
FICA_SS_MAX_WAGE = 132900.0
|
||||||
|
FICA_M_MAX_WAGE = float_info.max
|
||||||
|
FICA_M_ADD_START_WAGE = 200000.0
|
||||||
|
FUTA_MAX_WAGE = 7000.0
|
||||||
|
|
||||||
|
# Rates
|
||||||
|
FICA_SS = 6.2 / -100.0
|
||||||
|
FICA_M = 1.45 / -100.0
|
||||||
|
FUTA = FUTA_RATE_NORMAL / -100.0
|
||||||
|
FICA_M_ADD = 0.9 / -100.0
|
||||||
|
|
||||||
|
###
|
||||||
|
# 2019 Taxes and Rates
|
||||||
|
###
|
||||||
|
|
||||||
|
def test_2019_taxes(self):
|
||||||
|
# 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('2018 tax last slip')
|
||||||
|
payslip = self._createPayslip(employee, '2018-12-01', '2018-12-31')
|
||||||
|
payslip.compute_sheet()
|
||||||
|
process_payslip(payslip)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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('2019 tax second payslip:')
|
||||||
|
payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
|
||||||
|
|
||||||
|
payslip.compute_sheet()
|
||||||
|
|
||||||
|
cats = self._getCategories(payslip)
|
||||||
|
|
||||||
|
self.assertPayrollEqual(cats['WAGE_US_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('2019 tax third payslip:')
|
||||||
|
payslip = self._createPayslip(employee, '2019-03-01', '2019-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('2019 tax fourth payslip:')
|
||||||
|
payslip = self._createPayslip(employee, '2019-04-01', '2019-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_2019_fed_income_withholding_single(self):
|
||||||
|
salary = 6000.00
|
||||||
|
schedule_pay = 'monthly'
|
||||||
|
w4_allowances = 3
|
||||||
|
w4_allowance_amt = 350.00 * w4_allowances
|
||||||
|
adjusted_salary = salary - w4_allowance_amt # should be 4962.60, but would work over a wide value for the rate
|
||||||
|
###
|
||||||
|
# Single MONTHLY form Publication 15
|
||||||
|
expected_withholding = self.float_round(-(378.52 + ((adjusted_salary - 3606) * 0.22)), self.payroll_digits)
|
||||||
|
|
||||||
|
employee = self._createEmployee()
|
||||||
|
self._createContract(employee, salary, schedule_pay, w4_allowances, 'single')
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def test_2019_fed_income_withholding_married_as_single(self):
|
||||||
|
salary = 500.00
|
||||||
|
schedule_pay = 'weekly'
|
||||||
|
w4_allowances = 1
|
||||||
|
w4_allowance_amt = 80.80 * w4_allowances
|
||||||
|
adjusted_salary = salary - w4_allowance_amt # should be 420.50, but would work over a wide value for the rate
|
||||||
|
###
|
||||||
|
# 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')
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def test_2019_fed_income_withholding_married(self):
|
||||||
|
salary = 14000.00
|
||||||
|
schedule_pay = 'bi-weekly'
|
||||||
|
w4_allowances = 2
|
||||||
|
w4_allowance_amt = 161.50 * w4_allowances
|
||||||
|
adjusted_salary = salary - w4_allowance_amt # should be 13680.80, but would work over a wide value for the rate
|
||||||
|
###
|
||||||
|
# Single MONTHLY form Publication 15
|
||||||
|
expected_withholding = self.float_round(-(2519.06 + ((adjusted_salary - 12817) * 0.32)), self.payroll_digits)
|
||||||
|
|
||||||
|
employee = self._createEmployee()
|
||||||
|
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married')
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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._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)
|
||||||
|
|
||||||
|
def test_2019_taxes_with_full_futa(self):
|
||||||
|
futa_rate = self.FUTA_RATE_BASIC / -100.0
|
||||||
|
# social security salary
|
||||||
|
salary = self.FICA_M_ADD_START_WAGE
|
||||||
|
|
||||||
|
employee = self._createEmployee()
|
||||||
|
|
||||||
|
self._createContract(employee, salary, futa_type=USHrContract.FUTA_TYPE_BASIC)
|
||||||
|
|
||||||
|
self._log('2019 tax first payslip:')
|
||||||
|
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
|
||||||
|
|
||||||
|
payslip.compute_sheet()
|
||||||
|
|
||||||
|
cats = self._getCategories(payslip)
|
||||||
|
|
||||||
|
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_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._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)
|
||||||
|
|
||||||
|
def test_2019_fed_income_withholding_nonresident_alien(self):
|
||||||
|
salary = 3500.00
|
||||||
|
schedule_pay = 'quarterly'
|
||||||
|
w4_allowances = 1
|
||||||
|
w4_allowance_amt = 1050.0 * w4_allowances
|
||||||
|
nra_adjustment = 2000.0 # for quarterly
|
||||||
|
adjusted_salary = salary - w4_allowance_amt + nra_adjustment # 4425
|
||||||
|
|
||||||
|
###
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def test_2019_fed_income_additional_withholding(self):
|
||||||
|
salary = 50000.00
|
||||||
|
schedule_pay = 'annually'
|
||||||
|
w4_additional_withholding = 5000.0
|
||||||
|
w4_allowances = 2
|
||||||
|
w4_allowance_amt = 4200.0 * w4_allowances
|
||||||
|
adjusted_salary = salary - w4_allowance_amt # 41700
|
||||||
|
|
||||||
|
###
|
||||||
|
# Single ANNUAL form Publication 15
|
||||||
|
expected_withholding = \
|
||||||
|
self.float_round(-((1940.00 + ((adjusted_salary - 31200) * 0.12)) + w4_additional_withholding),
|
||||||
|
self.payroll_digits)
|
||||||
|
|
||||||
|
employee = self._createEmployee()
|
||||||
|
self._createContract(employee, salary, schedule_pay, w4_allowances, 'married',
|
||||||
|
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)
|
||||||
|
|
||||||
|
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, '')
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
self._log('2019 tax w4 exempt payslip:')
|
||||||
|
payslip = self._createPayslip(employee, '2019-01-01', '2019-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)
|
||||||
35
l10n_us_hr_payroll/views/l10n_us_hr_payroll_view.xml
Executable file
35
l10n_us_hr_payroll/views/l10n_us_hr_payroll_view.xml
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
<?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>
|
||||||
Reference in New Issue
Block a user