mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
Merge branch '11.0-payroll-test' into 11.0-test
This commit is contained in:
49
l10n_us_ca_hr_payroll/README.rst
Normal file
49
l10n_us_ca_hr_payroll/README.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
*************************************
|
||||
Hibou - US Payroll - California State
|
||||
*************************************
|
||||
|
||||
Calculations and contribution registers for California State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* Contribution registers and partners for:
|
||||
* California Department of Taxation - Unemployment Insurance Tax
|
||||
* California Department of Taxation - Income Tax Withholding
|
||||
* California Department of Taxation - Employee Training Tax
|
||||
* California Department of Taxation - State Disability Insurance
|
||||
|
||||
* Contract level California Exemptions and California State Disability Insurance
|
||||
* Company level California Unemployment Insurance Tax and California Employee Training Tax
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41482877-d1311214-708b-11e8-9400-3bc5c134b836.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA California Employee Added to Contract Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41482910-ef25bbd0-708b-11e8-8720-d2065149f953.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* California Income Withholding
|
||||
* California State Disability Insurance - Wages
|
||||
* California State Disability Insurance
|
||||
* California Employee Training Tax - Wages
|
||||
* California Unemployment Insurance Tax - Wages
|
||||
* California Unemployment Insurance Tax
|
||||
* California Employee Training Tax
|
||||
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
1
l10n_us_ca_hr_payroll/__init__.py
Executable file
1
l10n_us_ca_hr_payroll/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import hr_payroll
|
||||
31
l10n_us_ca_hr_payroll/__manifest__.py
Executable file
31
l10n_us_ca_hr_payroll/__manifest__.py
Executable file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
'name': 'USA - California - Payroll',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Localization',
|
||||
'depends': ['l10n_us_hr_payroll'],
|
||||
'version': '11.0.2018.0.0',
|
||||
'description': """
|
||||
USA::California Payroll Rules.
|
||||
==============================
|
||||
|
||||
* Contribution register and partner for California Department of Taxation - Unemployment Insurance Tax
|
||||
* Contribution register and partner for California Department of Taxation - Income Tax Withholding
|
||||
* Contribution register and partner for Califronia Department of Taxation - Employee Training Tax
|
||||
* Contribution register and partner for Califronia Department of Taxation - State Disability Insurance
|
||||
* Contract level California Exemptions
|
||||
* Contract level California State Disability Insurance
|
||||
* Company level California Unemployment Insurance Tax
|
||||
* Company level California Employee Training Tax
|
||||
""",
|
||||
|
||||
'auto_install': False,
|
||||
'website': 'https://hibou.io/',
|
||||
'data': [
|
||||
'hr_payroll_view.xml',
|
||||
'data/base.xml',
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
}
|
||||
88
l10n_us_ca_hr_payroll/data/base.xml
Executable file
88
l10n_us_ca_hr_payroll/data/base.xml
Executable file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- CONTRIBUTION REGISTERS -->
|
||||
<record id="res_partner_cador_uit" model="res.partner">
|
||||
<field name="name">California Department of Taxation - Unemployment Insurance Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_cador_withhold" model="res.partner">
|
||||
<field name="name">California Department of Taxation - Income Tax Withholding</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_cador_ett" model="res.partner">
|
||||
<field name="name">California Department of Taxation - Employment Training Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_cador_sdi" model="res.partner">
|
||||
<field name="name">California Department of Taxation - State Disability Insurance</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_cador_uit" model="hr.contribution.register">
|
||||
<field name="name">California Unemployment Insurance Tax</field>
|
||||
<field name="note">California Department of Taxation - Unemployment Insurance Tax</field>
|
||||
<field name="partner_id" ref="res_partner_cador_uit"/>
|
||||
</record>
|
||||
<record id="contrib_register_cador_withhold" model="hr.contribution.register">
|
||||
<field name="name">California Income Tax Withholding</field>
|
||||
<field name="note">California Department of Taxation - Income Tax Withholding</field>
|
||||
<field name="partner_id" ref="res_partner_cador_withhold"/>
|
||||
</record>
|
||||
<record id="contrib_register_cador_ett" model="hr.contribution.register">
|
||||
<field name="name">Employment Training Tax</field>
|
||||
<field name="note">California Department of Taxation - Employment Training Tax</field>
|
||||
<field name="partner_id" ref="res_partner_cador_withhold"/>
|
||||
</record>
|
||||
<record id="contrib_register_cador_sdi" model="hr.contribution.register">
|
||||
<field name="name">State Disability Insurance</field>
|
||||
<field name="note">California Department of Taxation - State Disability Insurance</field>
|
||||
<field name="partner_id" ref="res_partner_cador_withhold"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- HR SALARY RULE CATEGORIES-->
|
||||
<record id="hr_payroll_ca_uit_wages" model="hr.salary.rule.category">
|
||||
<field name="name">California Unemployment Insurance Tax - Wages</field>
|
||||
<field name="code">CA_UIT_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ca_uit" model="hr.salary.rule.category">
|
||||
<field name="name">California Unemployment Insurance Tax</field>
|
||||
<field name="code">CA_UIT</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ca_ett_wages" model="hr.salary.rule.category">
|
||||
<field name="name">California Employee Training Tax - Wages</field>
|
||||
<field name="code">CA_ETT_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ca_ett" model="hr.salary.rule.category">
|
||||
<field name="name">California Employee Training Tax</field>
|
||||
<field name="code">CA_ETT</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ca_sdi_wages" model="hr.salary.rule.category">
|
||||
<field name="name">California State Disability Insurance - Wages</field>
|
||||
<field name="code">CA_SDI_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ca_sdi" model="hr.salary.rule.category">
|
||||
<field name="name">California State Disability Insurance</field>
|
||||
<field name="code">CA_SDI</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ca_income_withhold" model="hr.salary.rule.category">
|
||||
<field name="name">California Income Withholding</field>
|
||||
<field name="code">CA_WITHHOLD</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
23
l10n_us_ca_hr_payroll/data/final.xml
Executable file
23
l10n_us_ca_hr_payroll/data/final.xml
Executable file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR PAYROLL STRUCTURE -->
|
||||
<record id="hr_payroll_salary_structure_us_ca_employee" model="hr.payroll.structure">
|
||||
<field name="code">US_CA_EMP</field>
|
||||
<field name="name">USA California Employee</field>
|
||||
<field eval="[(6, 0, [
|
||||
ref('hr_payroll_rules_ca_uit_wages_2018'),
|
||||
ref('hr_payroll_rules_ca_uit_2018'),
|
||||
ref('hr_payroll_rules_ca_ett_wages_2018'),
|
||||
ref('hr_payroll_rules_ca_ett_2018'),
|
||||
ref('hr_payroll_rules_ca_sdi_wages_2018'),
|
||||
ref('hr_payroll_rules_ca_sdi_2018'),
|
||||
ref('hr_payroll_rules_ca_inc_withhold_2018'),
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="parent_id" ref="l10n_us_hr_payroll.hr_payroll_salary_structure_us_employee"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
576
l10n_us_ca_hr_payroll/data/rules_2018.xml
Executable file
576
l10n_us_ca_hr_payroll/data/rules_2018.xml
Executable file
@@ -0,0 +1,576 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR SALARY RULES-->
|
||||
<!-- UIT -->
|
||||
<record id="hr_payroll_rules_ca_uit_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_ca_uit_wages"/>
|
||||
<field name="name">California Unemployment Insurance Tax - Wages (2018)</field>
|
||||
<field name="code">CA_UIT_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('CA_UIT_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 7000.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rules_ca_uit_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_ca_uit"/>
|
||||
<field name="name">California Unemployment Insurance Tax(2018)</field>
|
||||
<field name="code">CA_UIT_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.ca_uit_rate(2018)
|
||||
result = categories.CA_UIT_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_cador_uit"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- ETT -->
|
||||
<record id="hr_payroll_rules_ca_ett_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_ca_ett_wages"/>
|
||||
<field name="name">California Employment Training Tax - Wages (2018)</field>
|
||||
<field name="code">CA_ETT_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('CA_ETT_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 7000.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rules_ca_ett_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_ca_ett"/>
|
||||
<field name="name">California Employee Training Tax(2018)</field>
|
||||
<field name="code">CA_ETT_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.ca_ett_rate(2018)
|
||||
result = categories.CA_ETT_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_cador_sdi"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- SDI -->
|
||||
<record id="hr_payroll_rules_ca_sdi_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="190"/>
|
||||
<field name="category_id" ref="hr_payroll_ca_sdi_wages"/>
|
||||
<field name="name">California State Disability Insurance Tax - Wages (2018)</field>
|
||||
<field name="code">CA_SDI_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('CA_SDI_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 114967.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rules_ca_sdi_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_ca_sdi"/>
|
||||
<field name="name">California State Disability Insurance(2018)</field>
|
||||
<field name="code">CA_SDI_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.ca_sdi_rate(2018)
|
||||
result = categories.CA_SDI_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_cador_sdi"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- STATE INCOME WITHHOLDING -->
|
||||
<record id="hr_payroll_rules_ca_inc_withhold_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="145"/>
|
||||
<field name="category_id" ref="hr_payroll_ca_income_withhold"/>
|
||||
<field name="name">California Income Withholding</field>
|
||||
<field name="code">CA_INC_WITHHOLD_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
wages = categories.GROSS
|
||||
allowances = contract.ca_de4_allowances
|
||||
additional_allowances = contract.ca_additional_allowances
|
||||
schedule_pay = contract.schedule_pay
|
||||
filing_status = contract.ca_de4_filing_status
|
||||
low_income = False
|
||||
|
||||
# Tables are found in http://www.edd.ca.gov/pdf_pub_ctr/18methb.pdf
|
||||
# First check low income exemption table (Step 1)
|
||||
low_income_exemption_table = {
|
||||
'weekly': (270, 270, 540, 540),
|
||||
'bi-weekly': (540, 540, 1081, 1081),
|
||||
'semi-monthly': (585, 585, 1171, 1171),
|
||||
'monthly': (1171, 1171, 2341, 2341),
|
||||
'quarterly': (3512, 3512, 7024, 7024),
|
||||
'semi-annual': (7024, 7024, 14048, 14048),
|
||||
'annually': (14048, 14048, 28095, 28095),
|
||||
}
|
||||
|
||||
if filing_status == 'head_household':
|
||||
_, _, _, income = low_income_exemption_table[schedule_pay]
|
||||
if wages <= income:
|
||||
result = 0
|
||||
low_income = True
|
||||
elif filing_status == 'married':
|
||||
if allowances >= 2:
|
||||
_, _, income, _ = low_income_exemption_table[schedule_pay]
|
||||
if wages <= income:
|
||||
result = 0
|
||||
low_income = True
|
||||
else:
|
||||
_, income, _, _ = low_income_exemption_table[schedule_pay]
|
||||
if wages <= income:
|
||||
result = 0
|
||||
low_income = True
|
||||
else:
|
||||
income, _, _, _ = low_income_exemption_table[schedule_pay]
|
||||
if wages <= income:
|
||||
result = 0
|
||||
low_income = True
|
||||
|
||||
if not low_income:
|
||||
# Estimated deduction table (Step 2)
|
||||
estimated_deduction_table = {
|
||||
'weekly': (19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
|
||||
'bi-weekly': (38, 77, 115, 154, 192, 231, 269, 308, 346, 385),
|
||||
'semi-monthly': (42, 83, 125, 167, 208, 250, 292, 333, 375, 417),
|
||||
'monthly': (83, 167, 250, 333, 417, 500, 583, 667, 750, 833),
|
||||
'quarterly': (250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500),
|
||||
'semi-annual': (500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000),
|
||||
'annually': (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
|
||||
}
|
||||
|
||||
allowance_index = additional_allowances - 1
|
||||
if additional_allowances > 10:
|
||||
deduction = (estimated_deduction_table[schedule_pay][0]) * additional_allowances
|
||||
wages -= deduction
|
||||
elif additional_allowances > 0:
|
||||
deduction = estimated_deduction_table[schedule_pay][allowance_index]
|
||||
wages -= deduction
|
||||
|
||||
# Standard deduction table (Step 3)
|
||||
standard_deduction_table = {
|
||||
'weekly': (81, 81, 163, 163),
|
||||
'bi-weekly': (163, 163, 326, 326),
|
||||
'semi-monthly': (177, 177, 353, 353),
|
||||
'monthly': (353, 353, 706, 706),
|
||||
'quarterly': (1059, 1059, 2118, 2118),
|
||||
'semi-annual': (2118, 2118, 4236, 4236),
|
||||
'annually': (4236, 4236, 8472, 8472),
|
||||
}
|
||||
|
||||
if filing_status == 'head_household':
|
||||
_, _, _, deduction = standard_deduction_table[schedule_pay]
|
||||
wages -= deduction
|
||||
elif filing_status == 'married':
|
||||
if allowances >= 2:
|
||||
_, _, deduction, _ = standard_deduction_table[schedule_pay]
|
||||
wages -= deduction
|
||||
else:
|
||||
_, deduction, _, _ = standard_deduction_table[schedule_pay]
|
||||
wages -= deduction
|
||||
else:
|
||||
deduction, _, _, _ = standard_deduction_table[schedule_pay]
|
||||
wages -= deduction
|
||||
|
||||
# Tax Rate Tables (Step 4)
|
||||
#### WEEKLY ####
|
||||
if schedule_pay == 'weekly':
|
||||
if filing_status == 'head_household' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(316, 0.011, 0.0),
|
||||
(750, 0.022, 3.48),
|
||||
(967, 0.044, 13.03),
|
||||
(1196, 0.066, 22.58),
|
||||
(1413, 0.088, 37.69),
|
||||
(7212, 0.1023, 56.79),
|
||||
(8654, 0.1133, 650.03),
|
||||
(14423, 0.1243, 813.41),
|
||||
(19231, 0.1353, 1530.50),
|
||||
(float('inf'), 0.1463, 2181.02),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(316, 0.011, 0.0),
|
||||
(750, 0.022, 3.48),
|
||||
(1184, 0.044, 13.03),
|
||||
(1642, 0.066, 32.13),
|
||||
(2076, 0.088, 62.36),
|
||||
(10606, 0.1023, 100.55),
|
||||
(12726, 0.1133, 973.17),
|
||||
(19231, 0.1243, 1213.37),
|
||||
(21210, 0.1353, 2021.94),
|
||||
(float('inf'), 0.1463, 2289.70),
|
||||
]
|
||||
|
||||
else:
|
||||
tax_rate_table = [
|
||||
(158, 0.011, 0.0),
|
||||
(375, 0.022, 1.74),
|
||||
(592, 0.044, 6.51),
|
||||
(821, 0.066, 16.06),
|
||||
(1038, 0.088, 31.17),
|
||||
(5303, 0.1023, 50.27),
|
||||
(6363, 0.1133, 486.58),
|
||||
(10605, 0.1243, 606.68),
|
||||
(19231, 0.1353, 1133.96),
|
||||
(float('inf'), 0.1463, 2301.06),
|
||||
]
|
||||
|
||||
#### BI-WEEKLY ####
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
if filing_status == 'head_household':
|
||||
tax_rate_table = [
|
||||
(632, 0.011, 0.0),
|
||||
(1500, 0.022, 6.95),
|
||||
(1934, 0.044, 26.05),
|
||||
(2392, 0.066, 45.15),
|
||||
(2826, 0.088, 75.38),
|
||||
(14424, 0.1023, 113.57),
|
||||
(17308, 0.1133, 1300.05),
|
||||
(28846, 0.1243, 1626.81),
|
||||
(38462, 0.1353, 3060.98),
|
||||
(float('inf'), 0.1463, 4362.02),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(632, 0.011, 0.0),
|
||||
(1500, 0.022, 6.95),
|
||||
(2368, 0.044, 26.05),
|
||||
(3284, 0.066, 64.24),
|
||||
(4152, 0.088, 124.70),
|
||||
(21212, 0.1023, 201.08),
|
||||
(25452, 0.1133, 1946.32),
|
||||
(38462, 0.1243, 2426.71),
|
||||
(42420, 0.1353, 4043.85),
|
||||
(float('inf'), 0.1463, 4579.37),
|
||||
]
|
||||
|
||||
else:
|
||||
tax_rate_table = [
|
||||
(316, 0.011, 0.0),
|
||||
(750, 0.022, 3.48),
|
||||
(1184, 0.044, 13.03),
|
||||
(1642, 0.066, 32.13),
|
||||
(2076, 0.088, 62.36),
|
||||
(10606, 0.1023, 100.55),
|
||||
(12726, 0.1133, 973.17),
|
||||
(21210, 0.1243, 1213.37),
|
||||
(38462, 0.1353, 2267.93),
|
||||
(float('inf'), 0.1463, 4602.13),
|
||||
]
|
||||
|
||||
#### SEMI-MONTHLY ####
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
if filing_status == 'head_household':
|
||||
tax_rate_table = [
|
||||
(686, 0.011, 0.0),
|
||||
(1625, 0.022, 7.55),
|
||||
(2094, 0.044, 28.21),
|
||||
(2592, 0.066, 48.85),
|
||||
(3062, 0.088, 81.72),
|
||||
(15625, 0.1023, 123.08),
|
||||
(18750, 0.1133, 1408.27),
|
||||
(31250, 0.1243, 1762.33),
|
||||
(41667, 0.1353, 3316.08),
|
||||
(float('inf'), 0.1463, 4725.50),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(686, 0.011, 0.0),
|
||||
(1624, 0.022, 7.55),
|
||||
(2564, 0.044, 28.19),
|
||||
(3560, 0.066, 69.55),
|
||||
(4498, 0.088, 135.29),
|
||||
(22978, 0.1023, 217.83),
|
||||
(27574, 0.1133, 2108.33),
|
||||
(41667, 0.1243, 2629.06),
|
||||
(45956, 0.1353, 4380.82),
|
||||
(float('inf'), 0.1463, 4961.12),
|
||||
]
|
||||
|
||||
else:
|
||||
tax_rate_table = [
|
||||
(343, 0.011, 0.0),
|
||||
(812, 0.022, 3.77),
|
||||
(1282, 0.044, 14.09),
|
||||
(1780, 0.066, 34.77),
|
||||
(2249, 0.088, 67.64),
|
||||
(11489, 0.1023, 108.91),
|
||||
(13787, 0.1133, 1054.16),
|
||||
(22978, 0.1243, 1314.52),
|
||||
(41667, 0.1353, 2456.96),
|
||||
(float('inf'), 0.1463, 4985.58),
|
||||
]
|
||||
|
||||
#### MONTHLY ####
|
||||
elif schedule_pay == 'monthly':
|
||||
if filing_status == 'head_household':
|
||||
tax_rate_table = [
|
||||
(1372, 0.011, 0.0),
|
||||
(3250, 0.022, 15.09),
|
||||
(4188, 0.044, 56.41),
|
||||
(5184, 0.066, 97.68),
|
||||
(6124, 0.088, 163.42),
|
||||
(31250, 0.1023, 246.148),
|
||||
(37500, 0.1133, 2816.53),
|
||||
(62500, 0.1243, 3524.66),
|
||||
(83334, 0.1353, 6632.16),
|
||||
(float('inf'), 0.1463, 9451.00),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(1372, 0.011, 0.0),
|
||||
(3248, 0.022, 15.09),
|
||||
(5128, 0.044, 56.36),
|
||||
(7120, 0.066, 139.08),
|
||||
(8996, 0.088, 270.55),
|
||||
(45956, 0.1023, 435.64),
|
||||
(55148, 0.1133, 4216.65),
|
||||
(83334, 0.1243, 5258.10),
|
||||
(91912, 0.1353, 8761.62),
|
||||
(float('inf'), 0.1463, 9922.22),
|
||||
]
|
||||
|
||||
else:
|
||||
tax_rate_table = [
|
||||
(686, 0.011, 0.0),
|
||||
(1624, 0.022, 7.55),
|
||||
(2564, 0.044, 28.19),
|
||||
(3560, 0.066, 69.55),
|
||||
(4498, 0.088, 135.29),
|
||||
(22978, 0.1023, 217.83),
|
||||
(27574, 0.1133, 2108.33),
|
||||
(45956, 0.1243, 2629.06),
|
||||
(83334, 0.1353, 4913.94),
|
||||
(float('inf'), 0.1463, 9971.18),
|
||||
]
|
||||
|
||||
#### QUARTERLY ####
|
||||
elif schedule_pay == 'quarterly':
|
||||
if filing_status == 'head_household':
|
||||
tax_rate_table = [
|
||||
(4114, 0.011, 0.0),
|
||||
(9748, 0.022, 45.25),
|
||||
(12566, 0.044, 169.20),
|
||||
(15552, 0.066, 293.19),
|
||||
(18369, 0.088, 490.27),
|
||||
(93751, 0.1023, 738.17),
|
||||
(112501, 0.1133, 8449.75),
|
||||
(187501, 0.1243, 10574.13),
|
||||
(250000, 0.1353, 19896.63),
|
||||
(float('inf'), 0.1463, 28352.74),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(4112, 0.011, 0.0),
|
||||
(9748, 0.022, 45.23),
|
||||
(15384, 0.044, 169.22),
|
||||
(21356, 0.066, 417.20),
|
||||
(26990, 0.088, 811.35),
|
||||
(137870, 0.1023, 1307.14),
|
||||
(165442, 0.1133, 12650.16),
|
||||
(250000, 0.1243, 15774.07),
|
||||
(275736, 0.1353, 26284.63),
|
||||
(float('inf'), 0.1463, 29766.71),
|
||||
]
|
||||
|
||||
else:
|
||||
tax_rate_table = [
|
||||
(2056, 0.011, 0.0),
|
||||
(4874, 0.022, 22.62),
|
||||
(7692, 0.044, 84.62),
|
||||
(10678, 0.066, 208.61),
|
||||
(13495, 0.088, 405.69),
|
||||
(68935, 0.1023, 653.59),
|
||||
(82721, 0.1133, 6325.10),
|
||||
(137868, 0.1243, 7887.05),
|
||||
(250000, 0.1353, 14741.82),
|
||||
(float('inf'), 0.1463, 29913.28),
|
||||
]
|
||||
|
||||
#### SEMI-ANNUAL ####
|
||||
elif schedule_pay == 'semi-annual':
|
||||
if filing_status == 'head_household':
|
||||
tax_rate_table = [
|
||||
(8228, 0.011, 0.0),
|
||||
(19496, 0.022, 90.51),
|
||||
(25132, 0.044, 338.41),
|
||||
(31104, 0.066, 586.39),
|
||||
(36738, 0.088, 980.54),
|
||||
(187502, 0.1023, 1476.33),
|
||||
(225002, 0.1133, 16899.49),
|
||||
(375002, 0.1243, 21148.24),
|
||||
(500000, 0.1353, 39793.24),
|
||||
(float('inf'), 0.1463, 56705.47),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(8224, 0.011, 0.0),
|
||||
(19496, 0.022, 90.46),
|
||||
(30768, 0.044, 338.44),
|
||||
(42712, 0.066, 834.41),
|
||||
(53980, 0.088, 1622.71),
|
||||
(275740, 0.1023, 2614.29),
|
||||
(330884, 0.1133, 25300.34),
|
||||
(500000, 0.1243, 31548.16),
|
||||
(551472, 0.1353, 52569.28),
|
||||
(float('inf'), 0.1463, 59533.44),
|
||||
]
|
||||
|
||||
else:
|
||||
tax_rate_table = [
|
||||
(4112, 0.011, 0.0),
|
||||
(9748, 0.022, 45.23),
|
||||
(15384, 0.044, 169.22),
|
||||
(21356, 0.066, 417.20),
|
||||
(26990, 0.088, 811.35),
|
||||
(137870, 0.1023, 1307.14),
|
||||
(165442, 0.1133, 12650.16),
|
||||
(275736, 0.1243, 15774.07),
|
||||
(500000, 0.1353, 29483.61),
|
||||
(float('inf'), 0.1463, 59826.53),
|
||||
]
|
||||
|
||||
#### ANNUAL ####
|
||||
elif schedule_pay == 'annually':
|
||||
if filing_status == 'head_household':
|
||||
tax_rate_table = [
|
||||
(16457, 0.011, 0.0),
|
||||
(38991, 0.022, 181.03),
|
||||
(50264, 0.044, 676.78),
|
||||
(62206, 0.066, 1172.79),
|
||||
(73477, 0.088, 1960.96),
|
||||
(375002, 0.1023, 2952.81),
|
||||
(450003, 0.1133, 33798.82),
|
||||
(750003, 0.1243, 42296.43),
|
||||
(1000000, 0.1353, 79586.43),
|
||||
(float('inf'), 0.1463, 113411.02),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(16446, 0.011, 0.0),
|
||||
(38990, 0.022, 180.91),
|
||||
(61538, 0.044, 676.88),
|
||||
(85422, 0.066, 1668.99),
|
||||
(107960, 0.088, 3245.33),
|
||||
(551476, 0.1023, 5228.67),
|
||||
(661768, 0.1133, 50600.36),
|
||||
(1000000, 0.1243, 63096.44),
|
||||
(1102946, 0.1353, 105138.68),
|
||||
(float('inf'), 0.1463, 119067.26),
|
||||
]
|
||||
|
||||
else:
|
||||
tax_rate_table = [
|
||||
(8223, 0.011, 0.0),
|
||||
(19495, 0.022, 90.45),
|
||||
(30769, 0.044, 338.43),
|
||||
(42711, 0.066, 834.49),
|
||||
(53980, 0.088, 1622.66),
|
||||
(275738, 0.1023, 2614.33),
|
||||
(330884, 0.1133, 25300.17),
|
||||
(551473, 0.1243, 31548.21),
|
||||
(1000000, 0.1353, 58967.42),
|
||||
(float('inf'), 0.1463, 119653.12),
|
||||
]
|
||||
|
||||
over = 0.0
|
||||
tax = 0.0
|
||||
for row in tax_rate_table:
|
||||
if wages <= row[0]:
|
||||
tax = ((wages - over) * row[1]) + row[2]
|
||||
break
|
||||
over = row[0]
|
||||
|
||||
# Exemption allowance table (Step 5)
|
||||
exemption_allowance_table = {
|
||||
'weekly': (2.41, 4.82, 7.23, 9.65, 12.06, 14.47, 16.88, 19.29, 21.70, 24.12),
|
||||
'bi-weekly': (4.82, 9.65, 14.47, 19.29, 24.12, 28.94, 33.76, 38.58, 43.41, 48.23),
|
||||
'semi-monthly': (5.23, 10.45, 15.68, 20.90, 26.13, 31.35, 36.58, 41.80, 47.03, 52.25),
|
||||
'monthly': (10.45, 20.90, 31.35, 41.80, 52.25, 62.70, 73.15, 83.60, 94.05, 104.50),
|
||||
'quarterly': (31.35, 62.70, 94.05, 125.40, 156.75, 188.10, 219.45, 250.80, 282.15, 313.50),
|
||||
'semi-annual': (62.70, 125.40, 188.10, 250.80, 313.50, 376.20, 438.90, 501.60, 564.30, 627.00),
|
||||
'annually': (125.40, 250.80, 376.20, 501.60, 627.00, 752.40, 877.80, 1003.20, 1128.60, 1254.00),
|
||||
}
|
||||
|
||||
allowance_index = allowances - 1
|
||||
if allowances > 10:
|
||||
deduction = (exemption_allowance_table[schedule_pay][0]) * allowances
|
||||
tax -= deduction
|
||||
elif allowances > 0:
|
||||
deduction = exemption_allowance_table[schedule_pay][allowance_index]
|
||||
tax -= deduction
|
||||
|
||||
result = -tax
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_cador_withhold"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
56
l10n_us_ca_hr_payroll/hr_payroll.py
Executable file
56
l10n_us_ca_hr_payroll/hr_payroll.py
Executable file
@@ -0,0 +1,56 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class USCAHrContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
ca_de4_allowances = fields.Integer(string="California CA-4 Allowances",
|
||||
default=0,
|
||||
help="Estimated Deductions claimed on DE-4")
|
||||
ca_additional_allowances = fields.Integer(string="Additional Allowances", default=0)
|
||||
ca_de4_filing_status = fields.Selection([
|
||||
('exempt', 'Exempt'),
|
||||
('single', 'Single'),
|
||||
('married', 'Married'),
|
||||
('head_household', 'Head of Household')
|
||||
], string='CA Filing Status', default='single')
|
||||
|
||||
@api.multi
|
||||
def ca_uit_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'ca_uit_rate_' + str(year)):
|
||||
return self.employee_id.company_id['ca_uit_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US California.')
|
||||
|
||||
def ca_ett_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'ca_ett_rate_' + str(year)):
|
||||
return self.employee_id.company_id['ca_ett_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US California.')
|
||||
|
||||
def ca_sdi_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'ca_sdi_rate_' + str(year)):
|
||||
return self.employee_id.company_id['ca_sdi_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US California.')
|
||||
|
||||
|
||||
class CACompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
# UIT can be calculated using http://www.edd.ca.gov/pdf_pub_ctr/de44.pdf ETT is default.
|
||||
ca_uit_rate_2018 = fields.Float(string="California Unemployment Insurance Tax Rate 2018", default=2.6)
|
||||
ca_ett_rate_2018 = fields.Float(string="California Employment Training Tax Rate 2018", default=0.1)
|
||||
ca_sdi_rate_2018 = fields.Float(string="California State Disability Insurance Rate 2018", default=1.0)
|
||||
35
l10n_us_ca_hr_payroll/hr_payroll_view.xml
Executable file
35
l10n_us_ca_hr_payroll/hr_payroll_view.xml
Executable file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="us_ca_view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority">64</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='currency_id']" position="after">
|
||||
<field name="ca_uit_rate_2018"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_contract_form_l10n_us_ca_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority">147</field>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//group[@name='state_filing']" position="inside">
|
||||
<group string="California" name="ca">
|
||||
<field name="ca_de4_filing_status"/>
|
||||
<field name="ca_de4_allowances" string="DE-4 Allowances"/>
|
||||
<field name="ca_additional_allowances" string="Additional Allowances"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
1
l10n_us_ca_hr_payroll/tests/__init__.py
Executable file
1
l10n_us_ca_hr_payroll/tests/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import test_us_ca_payslip_2018
|
||||
461
l10n_us_ca_hr_payroll/tests/test_us_ca_payslip_2018.py
Executable file
461
l10n_us_ca_hr_payroll/tests/test_us_ca_payslip_2018.py
Executable file
@@ -0,0 +1,461 @@
|
||||
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
|
||||
from odoo.addons.l10n_us_hr_payroll.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
|
||||
class TestUsCAPayslip(TestUsPayslip):
|
||||
###
|
||||
# Taxes and Rates
|
||||
###
|
||||
CA_UIT_MAX_WAGE = 7000
|
||||
CA_UIT_MAX_WAGE = 7000
|
||||
CA_SDI_MAX_WAGE = 114967
|
||||
|
||||
# Examples from http://www.edd.ca.gov/pdf_pub_ctr/18methb.pdf
|
||||
def test_example_a(self):
|
||||
salary = 210
|
||||
schedule_pay = 'weekly'
|
||||
allowances = 1
|
||||
additional_allowances = 0
|
||||
|
||||
wh = 0.00
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ca_hr_payroll.hr_payroll_salary_structure_us_ca_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ca_c4_exemptions = allowances
|
||||
contract.ca_additional_allowances = additional_allowances
|
||||
contract.ca_de4_filing_status = 'single'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'weekly')
|
||||
|
||||
# tax rates
|
||||
ca_uit = contract.ca_uit_rate(2018) / -100.0
|
||||
ca_ett = contract.ca_ett_rate(2018) / -100.0
|
||||
ca_sdi = contract.ca_sdi_rate(2018) / -100.0
|
||||
|
||||
self._log('2017 California tax last payslip:')
|
||||
payslip = self._createPayslip(employee, '2017-12-01', '2017-12-31')
|
||||
payslip.compute_sheet()
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2018 California tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], cats['CA_UIT_WAGES'] * ca_uit)
|
||||
self.assertPayrollEqual(cats['CA_ETT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_ETT'], cats['CA_ETT_WAGES'] * ca_ett)
|
||||
self.assertPayrollEqual(cats['CA_SDI_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_SDI'], cats['CA_SDI_WAGES'] * ca_sdi)
|
||||
self.assertPayrollEqual(cats['CA_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_ca_uit_wages = self.CA_UIT_MAX_WAGE - salary if (self.CA_UIT_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 California tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], remaining_ca_uit_wages)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], remaining_ca_uit_wages * ca_uit)
|
||||
|
||||
def test_example_b(self):
|
||||
salary = 1250
|
||||
schedule_pay = 'bi-weekly'
|
||||
allowances = 2
|
||||
additional_allowances = 1
|
||||
|
||||
wh = -2.89
|
||||
# tax rates
|
||||
ca_uit = 2.6
|
||||
ca_ett = 0.1
|
||||
ca_sdi = 1.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ca_uit_rate_2018 = ca_uit
|
||||
employee.company_id.ca_ett_rate_2018 = ca_ett
|
||||
employee.company_id.ca_sdi_rate_2018 = ca_sdi
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ca_hr_payroll.hr_payroll_salary_structure_us_ca_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ca_de4_allowances = allowances
|
||||
contract.ca_additional_allowances = additional_allowances
|
||||
contract.ca_de4_filing_status = 'married'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'bi-weekly')
|
||||
self.assertEqual(contract.ca_de4_filing_status, 'married')
|
||||
self.assertEqual(contract.ca_de4_allowances, 2)
|
||||
self.assertEqual(contract.ca_additional_allowances, 1)
|
||||
|
||||
self._log('2017 California tax last payslip:')
|
||||
payslip = self._createPayslip(employee, '2017-12-01', '2017-12-31')
|
||||
payslip.compute_sheet()
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2018 California tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((cats['CA_UIT_WAGES'] * ca_uit)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_ETT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_ETT'], round((cats['CA_ETT_WAGES'] * ca_ett)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_SDI_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_SDI'], round((cats['CA_SDI_WAGES'] * ca_sdi)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_ca_uit_wages = self.CA_UIT_MAX_WAGE - salary if (self.CA_UIT_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 California tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], remaining_ca_uit_wages)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((remaining_ca_uit_wages * ca_uit)/-100, 2))
|
||||
|
||||
def test_example_c(self):
|
||||
salary = 3800
|
||||
schedule_pay = 'monthly'
|
||||
allowances = 5
|
||||
additional_allowances = 0.72
|
||||
|
||||
wh = -0.72
|
||||
# tax rates
|
||||
ca_uit = 2.6
|
||||
ca_ett = 0.1
|
||||
ca_sdi = 1.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ca_uit_rate_2018 = ca_uit
|
||||
employee.company_id.ca_ett_rate_2018 = ca_ett
|
||||
employee.company_id.ca_sdi_rate_2018 = ca_sdi
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ca_hr_payroll.hr_payroll_salary_structure_us_ca_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ca_de4_allowances = allowances
|
||||
contract.ca_additional_allowances = additional_allowances
|
||||
contract.ca_de4_filing_status = 'married'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'monthly')
|
||||
self.assertEqual(contract.ca_de4_filing_status, 'married')
|
||||
self.assertEqual(contract.ca_de4_allowances, 5)
|
||||
self.assertEqual(contract.ca_additional_allowances, 0)
|
||||
|
||||
self._log('2017 California tax last payslip:')
|
||||
payslip = self._createPayslip(employee, '2017-12-01', '2017-12-31')
|
||||
payslip.compute_sheet()
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2018 California tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((cats['CA_UIT_WAGES'] * ca_uit)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_ETT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_ETT'], round((cats['CA_ETT_WAGES'] * ca_ett)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_SDI_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_SDI'], round((cats['CA_SDI_WAGES'] * ca_sdi)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_ca_uit_wages = self.CA_UIT_MAX_WAGE - salary if (self.CA_UIT_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 California tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], remaining_ca_uit_wages)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((remaining_ca_uit_wages * ca_uit)/-100, 2))
|
||||
|
||||
def test_example_d(self):
|
||||
salary = 800
|
||||
schedule_pay = 'weekly'
|
||||
allowances = 3
|
||||
additional_allowances = 0
|
||||
|
||||
wh = -3.31
|
||||
# tax rates
|
||||
ca_uit = 2.6
|
||||
ca_ett = 0.1
|
||||
ca_sdi = 1.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ca_uit_rate_2018 = ca_uit
|
||||
employee.company_id.ca_ett_rate_2018 = ca_ett
|
||||
employee.company_id.ca_sdi_rate_2018 = ca_sdi
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ca_hr_payroll.hr_payroll_salary_structure_us_ca_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ca_de4_allowances = allowances
|
||||
contract.ca_additional_allowances = additional_allowances
|
||||
contract.ca_de4_filing_status = 'head_household'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'weekly')
|
||||
self.assertEqual(contract.ca_de4_filing_status, 'head_household')
|
||||
self.assertEqual(contract.ca_de4_allowances, 3)
|
||||
self.assertEqual(contract.ca_additional_allowances, 0)
|
||||
|
||||
self._log('2017 California tax last payslip:')
|
||||
payslip = self._createPayslip(employee, '2017-12-01', '2017-12-31')
|
||||
payslip.compute_sheet()
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2018 California tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((cats['CA_UIT_WAGES'] * ca_uit)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_ETT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_ETT'], round((cats['CA_ETT_WAGES'] * ca_ett)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_SDI_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_SDI'], round((cats['CA_SDI_WAGES'] * ca_sdi)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_ca_uit_wages = self.CA_UIT_MAX_WAGE - salary if (self.CA_UIT_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 California tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], remaining_ca_uit_wages)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((remaining_ca_uit_wages * ca_uit)/-100, 2))
|
||||
|
||||
def test_example_e(self):
|
||||
salary = 1800
|
||||
schedule_pay = 'semi-monthly'
|
||||
allowances = 4
|
||||
additional_allowances = 0
|
||||
|
||||
wh = -3.39
|
||||
# tax rates
|
||||
ca_uit = 2.6
|
||||
ca_ett = 0.1
|
||||
ca_sdi = 1.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ca_uit_rate_2018 = ca_uit
|
||||
employee.company_id.ca_ett_rate_2018 = ca_ett
|
||||
employee.company_id.ca_sdi_rate_2018 = ca_sdi
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ca_hr_payroll.hr_payroll_salary_structure_us_ca_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ca_de4_allowances = allowances
|
||||
contract.ca_additional_allowances = additional_allowances
|
||||
contract.ca_de4_filing_status = 'married'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'semi-monthly')
|
||||
self.assertEqual(contract.ca_de4_filing_status, 'married')
|
||||
self.assertEqual(contract.ca_de4_allowances, 4)
|
||||
self.assertEqual(contract.ca_additional_allowances, 0)
|
||||
|
||||
self._log('2017 California tax last payslip:')
|
||||
payslip = self._createPayslip(employee, '2017-12-01', '2017-12-31')
|
||||
payslip.compute_sheet()
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2018 California tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((cats['CA_UIT_WAGES'] * ca_uit)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_ETT_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_ETT'], round((cats['CA_ETT_WAGES'] * ca_ett)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_SDI_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['CA_SDI'], round((cats['CA_SDI_WAGES'] * ca_sdi)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_ca_uit_wages = self.CA_UIT_MAX_WAGE - salary if (self.CA_UIT_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 California tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT_WAGES'], remaining_ca_uit_wages)
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((remaining_ca_uit_wages * ca_uit)/-100, 2))
|
||||
|
||||
def test_example_f(self):
|
||||
salary = 45000
|
||||
schedule_pay = 'annually'
|
||||
allowances = 4
|
||||
additional_allowances = 0
|
||||
|
||||
wh = -121.11
|
||||
# tax rates
|
||||
ca_uit = 2.6
|
||||
ca_ett = 0.1
|
||||
ca_sdi = 1.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ca_uit_rate_2018 = ca_uit
|
||||
employee.company_id.ca_ett_rate_2018 = ca_ett
|
||||
employee.company_id.ca_sdi_rate_2018 = ca_sdi
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ca_hr_payroll.hr_payroll_salary_structure_us_ca_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ca_de4_allowances = allowances
|
||||
contract.ca_additional_allowances = additional_allowances
|
||||
contract.ca_de4_filing_status = 'married'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'annually')
|
||||
self.assertEqual(contract.ca_de4_filing_status, 'married')
|
||||
self.assertEqual(contract.ca_de4_allowances, 4)
|
||||
self.assertEqual(contract.ca_additional_allowances, 0)
|
||||
|
||||
self._log('2017 California tax last payslip:')
|
||||
payslip = self._createPayslip(employee, '2017-12-01', '2017-12-31')
|
||||
payslip.compute_sheet()
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2018 California tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['CA_UIT'], round((cats['CA_UIT_WAGES'] * ca_uit)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_ETT'], round((cats['CA_ETT_WAGES'] * ca_ett)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_SDI'], round((cats['CA_SDI_WAGES'] * ca_sdi)/-100, 2))
|
||||
self.assertPayrollEqual(cats['CA_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
def test_estimated_deduction_table(self):
|
||||
salary = 600
|
||||
allowances = 5
|
||||
schedule_pay = 'bi-weekly'
|
||||
expected_deduction = 192
|
||||
deduction = 0
|
||||
taxable_pay = 0
|
||||
estimated_deduction_table = {
|
||||
'weekly': (19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
|
||||
'bi-weekly': (38, 77, 115, 154, 192, 231, 269, 308, 346, 385),
|
||||
'semi-monthly': (42, 83, 125, 167, 208, 250, 292, 333, 375, 417),
|
||||
'monthly': (83, 167, 250, 333, 417, 500, 583, 667, 750, 833),
|
||||
'quarterly': (250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500),
|
||||
'semi-annual': (500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000),
|
||||
'annual': (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
|
||||
}
|
||||
|
||||
allowance_index = allowances - 1
|
||||
if allowances > 10:
|
||||
deduction = (estimated_deduction_table[schedule_pay][0]) * allowances
|
||||
taxable_pay = salary - deduction
|
||||
elif allowances > 0:
|
||||
deduction = estimated_deduction_table[schedule_pay][allowance_index]
|
||||
taxable_pay = salary - deduction
|
||||
|
||||
self.assertEqual(expected_deduction, deduction)
|
||||
self.assertTrue(taxable_pay < salary)
|
||||
self.assertEqual(taxable_pay, salary - deduction)
|
||||
|
||||
def test_standard_deduction_table(self):
|
||||
salary = 3000
|
||||
schedule_pay = 'monthly'
|
||||
filing_status = 'head_household'
|
||||
expected_deduction = 706
|
||||
deduction = 0
|
||||
taxable_pay = 0
|
||||
standard_deduction_table = {
|
||||
'weekly': (81, 81, 163, 163),
|
||||
'bi-weekly': (163, 163, 326, 326),
|
||||
'semi-monthly': (177, 177, 353, 353),
|
||||
'monthly': (353, 352, 706, 706),
|
||||
'quarterly': (1059, 1059, 2188, 2188),
|
||||
'semi-annual': (2118, 2118, 4236, 4236),
|
||||
'annual': (4236, 4236, 8471, 8472),
|
||||
}
|
||||
|
||||
if filing_status == 'head_household':
|
||||
_, _, _, deduction = standard_deduction_table[schedule_pay]
|
||||
taxable_pay = salary - deduction
|
||||
elif filing_status == 'married':
|
||||
if allowances >= 2:
|
||||
_, _, deduction, _ = standard_deduction_table[schedule_pay]
|
||||
taxable_pay = salary - deduction
|
||||
else:
|
||||
_, deduction, _, _ = standard_deduction_table[schedule_pay]
|
||||
taxable_pay = salary - deduction
|
||||
else:
|
||||
deduction, _, _, _ = standard_deduction_table[schedule_pay]
|
||||
taxable_pay = salary - deduction
|
||||
|
||||
self.assertEqual(expected_deduction, deduction)
|
||||
self.assertTrue(taxable_pay < salary)
|
||||
self.assertEqual(taxable_pay, salary - deduction)
|
||||
37
l10n_us_fl_hr_payroll/README.rst
Normal file
37
l10n_us_fl_hr_payroll/README.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
**********************************
|
||||
Hibou - US Payroll - Florida State
|
||||
**********************************
|
||||
|
||||
Calculations and contribution registers for Florida State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Partner and Contribution Register for Florida Department of Revenue
|
||||
* Company level Florida Unemployment Rate
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41440232-a2ca8cb0-6fe2-11e8-9640-0bfd61ae6108.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA Florida Employee Added to Contract Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41440247-b7b42744-6fe2-11e8-8ffb-d259eb893646.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* Florida Unemployment
|
||||
* Florida Unemployment - Wages
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
44
l10n_us_hr_payroll/README.rst
Normal file
44
l10n_us_hr_payroll/README.rst
Normal file
@@ -0,0 +1,44 @@
|
||||
******************
|
||||
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
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
35
l10n_us_ks_hr_payroll/README.rst
Normal file
35
l10n_us_ks_hr_payroll/README.rst
Normal file
@@ -0,0 +1,35 @@
|
||||
*********************************
|
||||
Hibou - US Payroll - Kansas State
|
||||
*********************************
|
||||
Calculations and contribution registers for Kansas State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Partner and Contribution Register for Kansas Department of Revenue
|
||||
* Contract level Kansas Income Tax Rate
|
||||
* Company Level Kansas Unemployment Rate
|
||||
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41478253-d1ff4af4-707b-11e8-8ad3-463fb7ed40da.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA Kansas Employee Added to Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41478265-e07e4cb0-707b-11e8-84cc-1529baaee1a6.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for Kansas Income Withholding, Kansas Unemployment Insurance Tax, and Kansas Unemployment Insurance Tax - Wages
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
1
l10n_us_ks_hr_payroll/__init__.py
Executable file
1
l10n_us_ks_hr_payroll/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import us_ks_hr_payroll
|
||||
26
l10n_us_ks_hr_payroll/__manifest__.py
Executable file
26
l10n_us_ks_hr_payroll/__manifest__.py
Executable file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
'name': 'USA - Kansas - Payroll',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Localization',
|
||||
'depends': ['l10n_us_hr_payroll'],
|
||||
'version': '11.0.2018.0.0',
|
||||
'description': """
|
||||
USA::Kansas Payroll Rules.
|
||||
===========================
|
||||
|
||||
* Kansas Department of Revenue partner
|
||||
* Contribution register for Kansas DoR
|
||||
* Company level Kansas Unemployment Rate
|
||||
""",
|
||||
|
||||
'auto_install': False,
|
||||
'website': 'https://hibou.io/',
|
||||
'data':[
|
||||
'us_ks_hr_payroll_view.xml',
|
||||
'data/base.xml',
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
}
|
||||
48
l10n_us_ks_hr_payroll/data/base.xml
Executable file
48
l10n_us_ks_hr_payroll/data/base.xml
Executable file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- CONTRIBUTION REGISTERS -->
|
||||
<record id="res_partner_ksdor_unemp" model="res.partner">
|
||||
<field name="name">Kansas Department of Revenue - Unemployment Insurance Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_ksdor_withhold" model="res.partner">
|
||||
<field name="name">Kansas Department of Revenue - Income Tax Withholding</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_ksdor_unemp" model="hr.contribution.register">
|
||||
<field name="name">Kansas Unemployment Insurance Tax</field>
|
||||
<field name="note">Kansas Department of Revenue - Unemployment Insurance Tax</field>
|
||||
<field name="partner_id" ref="res_partner_ksdor_unemp"/>
|
||||
</record>
|
||||
<record id="contrib_register_ksdor_withhold" model="hr.contribution.register">
|
||||
<field name="name">Kansas Income Tax Withholding</field>
|
||||
<field name="note">Kansas Department of Revenue - Income Tax Withholding</field>
|
||||
<field name="partner_id" ref="res_partner_ksdor_withhold"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- HR SALARY RULE CATEGORIES-->
|
||||
<record id="hr_payroll_ks_unemp_wages" model="hr.salary.rule.category">
|
||||
<field name="name">Kansas Unemployment Insurance Tax - Wages</field>
|
||||
<field name="code">KS_UNEMP_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ks_unemp" model="hr.salary.rule.category">
|
||||
<field name="name">Kansas Unemployment Insurance Tax</field>
|
||||
<field name="code">KS_UNEMP</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ks_income_withhold" model="hr.salary.rule.category">
|
||||
<field name="name">Kansas Income Withholding</field>
|
||||
<field name="code">KS_WITHHOLD</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
19
l10n_us_ks_hr_payroll/data/final.xml
Executable file
19
l10n_us_ks_hr_payroll/data/final.xml
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR PAYROLL STRUCTURE -->
|
||||
<record id="hr_payroll_salary_structure_us_ks_employee" model="hr.payroll.structure">
|
||||
<field name="code">US_KS_EMP</field>
|
||||
<field name="name">USA Kansas Employee</field>
|
||||
<field eval="[(6, 0, [
|
||||
ref('hr_payroll_rules_ks_unemp_wages_2018'),
|
||||
ref('hr_payroll_rules_ks_unemp_2018'),
|
||||
ref('hr_payroll_rules_ks_inc_withhold_2018'),
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="parent_id" ref="l10n_us_hr_payroll.hr_payroll_salary_structure_us_employee"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
229
l10n_us_ks_hr_payroll/data/rules_2018.xml
Executable file
229
l10n_us_ks_hr_payroll/data/rules_2018.xml
Executable file
@@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR SALARY RULES-->
|
||||
<record id="hr_payroll_rules_ks_unemp_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_ks_unemp_wages"/>
|
||||
<field name="name">Kansas Unemployment Insurance Tax - Wages (2018)</field>
|
||||
<field name="code">KS_UNEMP_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('KS_UNEMP_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 14000.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_ks_unemp_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_ks_unemp"/>
|
||||
<field name="name">Kansas Unemployment (2018)</field>
|
||||
<field name="code">KS_UNEMP_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.ks_unemp_rate(2018)
|
||||
result = categories.KS_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_ksdor_unemp"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- STATE INCOME WITHHOLDING -->
|
||||
<record id="hr_payroll_rules_ks_inc_withhold_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="145"/>
|
||||
<field name="category_id" ref="hr_payroll_ks_income_withhold"/>
|
||||
<field name="name">Kansas Income Withholding</field>
|
||||
<field name="code">KS_INC_WITHHOLD_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
wages = categories.GROSS
|
||||
allowances = contract.ks_k4_allowances
|
||||
additional_withholding = contract.ks_additional_withholding
|
||||
schedule_pay = contract.schedule_pay
|
||||
filing_status = contract.ks_k4_filing_status
|
||||
|
||||
# Tables are found in https://www.ksrevenue.org/pdf/kw1002017.pdf
|
||||
# First check for exemption status (Step 1)
|
||||
if filing_status == 'exempt':
|
||||
result = 0
|
||||
|
||||
else:
|
||||
# Calculate Withholding Allowance Amounts using table (allowance multipliers are from table).
|
||||
if schedule_pay == 'weekly':
|
||||
wages -= (allowances * 43.27)
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
wages -= (allowances * 86.54)
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
wages -= (allowances * 93.75)
|
||||
elif schedule_pay == 'monthly':
|
||||
wages -= (allowances * 187.50)
|
||||
elif schedule_pay == 'quarterly':
|
||||
wages -= (allowances * 562.50)
|
||||
elif schedule_pay == 'semi-annual':
|
||||
wages -= (allowances * 1125.00)
|
||||
elif schedule_pay == 'annually':
|
||||
wages -= (allowances * 2250.00)
|
||||
|
||||
# Tax Rate Tables to calculate income withholding
|
||||
#### WEEKLY ####
|
||||
if schedule_pay == 'weekly':
|
||||
if filing_status == 'head_household' or 'single' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(58, 0.0, 0.0),
|
||||
(346, 0.031, 0.0),
|
||||
(635, 0.0525, 8.94),
|
||||
(float('inf'), 0.057, 24.09),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(144, 0.0, 0.0),
|
||||
(721, 0.031, 0.0),
|
||||
(1298, 0.0525, 17.88),
|
||||
(float('inf'), 0.057, 48.17),
|
||||
]
|
||||
|
||||
### BI-WEEKLY ###
|
||||
if schedule_pay == 'bi-weekly':
|
||||
if filing_status == 'head_household' or 'single' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(115, 0.0, 0.0),
|
||||
(692, 0.031, 0.0),
|
||||
(1269, 0.0525, 17.88),
|
||||
(float('inf'), 0.057, 48.17),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(288, 0.0, 0.0),
|
||||
(1442, 0.031, 0.0),
|
||||
(2596, 0.0525, 35.77),
|
||||
(float('inf'), 0.057, 96.35),
|
||||
]
|
||||
|
||||
### SEMI-MONTHLY ###
|
||||
if schedule_pay == 'semi-monthly':
|
||||
if filing_status == 'head_household' or 'single' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(125, 0.0, 0.0),
|
||||
(750, 0.031, 0.0),
|
||||
(1375, 0.0525, 19.38),
|
||||
(float('inf'), 0.057, 52.19),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(313, 0.0, 0.0),
|
||||
(1563, 0.031, 0.0),
|
||||
(2813, 0.0525, 38.75),
|
||||
(float('inf'), 0.057, 104.38),
|
||||
]
|
||||
|
||||
### MONTHLY ###
|
||||
if schedule_pay == 'monthly':
|
||||
if filing_status == 'head_household' or 'single' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(250, 0.0, 0.0),
|
||||
(1500, 0.031, 0.0),
|
||||
(2750, 0.0525, 38.75),
|
||||
(float('inf'), 0.057, 104.38),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(625, 0.0, 0.0),
|
||||
(3125, 0.031, 0.0),
|
||||
(5625, 0.0525, 77.50),
|
||||
(float('inf'), 0.057, 208.75),
|
||||
]
|
||||
|
||||
### QUARTERLY ###
|
||||
if schedule_pay == 'quarterly':
|
||||
if filing_status == 'head_household' or 'single' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(750, 0.0, 0.0),
|
||||
(4500, 0.031, 0.0),
|
||||
(8250, 0.0525, 116.25),
|
||||
(float('inf'), 0.057, 313.13),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(1875, 0.0, 0.0),
|
||||
(9375, 0.031, 0.0),
|
||||
(16875, 0.0525, 232.50),
|
||||
(float('inf'), 0.057, 626.25),
|
||||
]
|
||||
|
||||
### SEMI-ANNUAL ###
|
||||
if schedule_pay == 'semi-annual':
|
||||
if filing_status == 'head_household' or 'single' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(1500, 0.0, 0.0),
|
||||
(9000, 0.031, 0.0),
|
||||
(16500, 0.0525, 232.50),
|
||||
(float('inf'), 0.057, 626.25),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(3750, 0.0, 0.0),
|
||||
(18750, 0.031, 0.0),
|
||||
(33750, 0.0525, 465.00),
|
||||
(float('inf'), 0.057, 1252.50),
|
||||
]
|
||||
|
||||
### ANNUAL ###
|
||||
if schedule_pay == 'annually':
|
||||
if filing_status == 'head_household' or 'single' and wages > 0:
|
||||
tax_rate_table = [
|
||||
(3000, 0.0, 0.0),
|
||||
(18000, 0.031, 0.0),
|
||||
(33000, 0.0525, 465.00),
|
||||
(float('inf'), 0.057, 1252.50),
|
||||
]
|
||||
|
||||
elif filing_status == 'married':
|
||||
tax_rate_table = [
|
||||
(7500, 0.0, 0.0),
|
||||
(37500, 0.031, 0.0),
|
||||
(67500, 0.0525, 930.00),
|
||||
(float('inf'), 0.057, 2505.00),
|
||||
]
|
||||
|
||||
over = 0.0
|
||||
tax = 0.0
|
||||
for row in tax_rate_table:
|
||||
if wages <= row[0]:
|
||||
tax = ((wages - over) * row[1]) + row[2]
|
||||
tax += additional_withholding
|
||||
break
|
||||
over = row[0]
|
||||
|
||||
result = -tax
|
||||
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_ksdor_withhold"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
1
l10n_us_ks_hr_payroll/tests/__init__.py
Executable file
1
l10n_us_ks_hr_payroll/tests/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import test_us_ks_payslip_2018
|
||||
92
l10n_us_ks_hr_payroll/tests/test_us_ks_payslip_2018.py
Executable file
92
l10n_us_ks_hr_payroll/tests/test_us_ks_payslip_2018.py
Executable file
@@ -0,0 +1,92 @@
|
||||
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
|
||||
from odoo.addons.l10n_us_hr_payroll.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
|
||||
class TestUsKSPayslip(TestUsPayslip):
|
||||
###
|
||||
# 2018 Taxes and Rates
|
||||
###
|
||||
KS_UNEMP_MAX_WAGE = 14000.0
|
||||
|
||||
def test_2018_taxes(self):
|
||||
self.debug = True
|
||||
salary = 210
|
||||
schedule_pay = 'weekly'
|
||||
allowances = 2
|
||||
additional_withholding = 0
|
||||
|
||||
# Amount of each withholding allowance for weekly from Withholding Allowance Amounts Table
|
||||
# https://www.ksrevenue.org/pdf/kw1002017.pdf
|
||||
withholding_allowance = 43.27 * allowances
|
||||
taxable_pay = salary - withholding_allowance
|
||||
|
||||
# Tax Percentage Method for Single, taxable pay over $58, under $346
|
||||
wh = -round(((taxable_pay - 58) * 0.031) - additional_withholding, 2)
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ks_hr_payroll.hr_payroll_salary_structure_us_ks_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ks_k4_allowances = allowances
|
||||
contract.ks_additional_withholding = additional_withholding
|
||||
contract.ks_k4_filing_status = 'single'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'weekly')
|
||||
|
||||
# tax rates
|
||||
ks_unemp = contract.ks_unemp_rate(2018) / -100.0
|
||||
|
||||
self._log('2018 Kansas tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['KS_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['KS_UNEMP'], cats['KS_UNEMP_WAGES'] * ks_unemp)
|
||||
self.assertPayrollEqual(cats['KS_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_ks_unemp_wages = self.KS_UNEMP_MAX_WAGE - salary if (self.KS_UNEMP_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 Kansas tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['KS_UNEMP_WAGES'], remaining_ks_unemp_wages)
|
||||
self.assertPayrollEqual(cats['KS_UNEMP'], remaining_ks_unemp_wages * ks_unemp)
|
||||
|
||||
def test_2018_taxes_with_state_exempt(self):
|
||||
salary = 210
|
||||
schedule_pay = 'weekly'
|
||||
allowances = 2
|
||||
|
||||
# Tax Exempt
|
||||
wh = 0
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ks_hr_payroll.hr_payroll_salary_structure_us_ks_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ks_k4_allowances = allowances
|
||||
contract.ks_k4_filing_status = 'exempt'
|
||||
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertEqual(cats['KS_WITHHOLD'], wh)
|
||||
36
l10n_us_ks_hr_payroll/us_ks_hr_payroll.py
Executable file
36
l10n_us_ks_hr_payroll/us_ks_hr_payroll.py
Executable file
@@ -0,0 +1,36 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class USKSHrContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
ks_k4_allowances = fields.Integer(string="Kansas K-4 Allowances",
|
||||
default=0,
|
||||
help="Allowances claimed on K-4")
|
||||
ks_additional_withholding = fields.Float(string="Additional Withholding",
|
||||
default=0.0,
|
||||
help='Additional withholding from line 5 of form K-4')
|
||||
ks_k4_filing_status = fields.Selection([
|
||||
('exempt', 'Exempt'),
|
||||
('single', 'Single'),
|
||||
('married', 'Married'),
|
||||
('head_household', 'Head of Household')
|
||||
], string='KS Filing Status', default='single')
|
||||
|
||||
|
||||
@api.multi
|
||||
def ks_unemp_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'ks_unemp_rate_' + str(year)):
|
||||
return self.employee_id.company_id['ks_unemp_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US Kansas.')
|
||||
|
||||
|
||||
class KSCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
ks_unemp_rate_2018 = fields.Float(string="Kansas Unemployment Insurance Rate 2018", default=2.7)
|
||||
36
l10n_us_ks_hr_payroll/us_ks_hr_payroll_view.xml
Executable file
36
l10n_us_ks_hr_payroll/us_ks_hr_payroll_view.xml
Executable file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="us_ks_view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='currency_id']" position="after">
|
||||
<field name="ks_unemp_rate_2018"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_form_l10n_us_ks_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority">147</field>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//group[@name='state_filing']" position="inside">
|
||||
<group string="Kansas" name="ks">
|
||||
<field name="ks_k4_filing_status"/>
|
||||
<field name="ks_k4_allowances" string="K-4 Allowances"/>
|
||||
<field name="ks_additional_withholding" string="Additional Withholding"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
40
l10n_us_mo_hr_payroll/README.rst
Normal file
40
l10n_us_mo_hr_payroll/README.rst
Normal file
@@ -0,0 +1,40 @@
|
||||
***********************************
|
||||
Hibou - US Payroll - Missouri State
|
||||
***********************************
|
||||
|
||||
Calculations and contribution registers for Missouri State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Partners and Contribution Registers for:
|
||||
* Missouri Department of Revenue - Unemployment
|
||||
* Missouri Department of Revenue - Income Tax Withholding
|
||||
* Contract level Missouri Exemptions and MO W-4 fields
|
||||
* Company level Missouri Unemployment Rate
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41440915-40ac183e-6fe5-11e8-966a-7dd2d85bade1.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA Missouri Employee added to Contract Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41440928-4b3fc214-6fe5-11e8-84f4-d35b401963c4.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* Missouri Income Withholding
|
||||
* Missouri Unemployment and Missouri Unemployment - Wages
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
41
l10n_us_nc_hr_payroll/README.rst
Normal file
41
l10n_us_nc_hr_payroll/README.rst
Normal file
@@ -0,0 +1,41 @@
|
||||
*****************************************
|
||||
Hibou - US Payroll - North Carolina State
|
||||
*****************************************
|
||||
|
||||
Calculations and contribution registers for North Carolina State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* Contribution registers and partners for:
|
||||
* North Carolina Department of Revenue - Income Tax withholding
|
||||
* North Carolina Department of Taxation - Unemployment
|
||||
* Contract level North Carolina Exemptions
|
||||
* Company level North Carolina Unemployment Rate
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41436210-cb7f6a52-6fd5-11e8-9095-48799360f4e9.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA North Carolina Employee added to Contract Salary Structure menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41436282-fcfd3096-6fd5-11e8-8bc8-3c4985908b23.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* North Carolina Income Withholding
|
||||
* North Carolina Unemployment - Wages
|
||||
* North Carolina Unemployment.
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
50
l10n_us_nj_hr_payroll/README.rst
Normal file
50
l10n_us_nj_hr_payroll/README.rst
Normal file
@@ -0,0 +1,50 @@
|
||||
*************************************
|
||||
Hibou - US Payroll - New Jersey State
|
||||
*************************************
|
||||
|
||||
Calculations and contribution registers for New Jersey State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Partner and Contribution Register for New Jersey Department of Taxation
|
||||
* Company level New Jersey Unemployment Rate
|
||||
* Company level New Jersey State Disability Insurance Rate
|
||||
* Contract level New Jersey State Disability Insurance Rate
|
||||
* Contract level New Jersey Family Leave Insurance Rate
|
||||
* Contract level New Jersey Workforce Development/Supplemental Workforce Funds
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41478628-533a04e6-707d-11e8-8892-88e65aca231e.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA New Jersey Employee Added to Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41478648-62840ce4-707d-11e8-9623-9638b6136f31.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* New Jersey Income Withholding
|
||||
* New Jersey Family Leave Insurance Tax - Wages
|
||||
* New Jersey State Disability Insurance Tax - Wages
|
||||
* New Jersey Work Force Development - Wages
|
||||
* New Jersey Unemployment Insurance Tax - Wages
|
||||
* New Jersey State Workforce/Supplemental Tax
|
||||
* New Jersey Unemployment Insurance Tax - Employee
|
||||
* New Jersey Unemployment Insurance Tax - Employer
|
||||
* New Jersey State Disability Insurance - Employee
|
||||
* New Jersey State Disability Insurance - Employer
|
||||
* New Jersey State Family Leave Insurance Tax
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
1
l10n_us_nj_hr_payroll/__init__.py
Executable file
1
l10n_us_nj_hr_payroll/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import us_nj_hr_payroll
|
||||
31
l10n_us_nj_hr_payroll/__manifest__.py
Executable file
31
l10n_us_nj_hr_payroll/__manifest__.py
Executable file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
'name': 'USA - New Jersey - Payroll',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Localization',
|
||||
'depends': ['l10n_us_hr_payroll'],
|
||||
'version': '11.0.2018.0.0',
|
||||
'description': """
|
||||
USA::New Jersey Payroll Rules.
|
||||
==============================
|
||||
|
||||
* New Jersey Division of Taxation partner
|
||||
* Contribution register for New Jersey DoT
|
||||
* Company level New Jersey Unemployment Rate
|
||||
* Company level New Jersey State Disability Insurance Rate
|
||||
* Contract level New Jersey Unemployment Rate
|
||||
* Contract level New Jersey State Disability Insurance Rate
|
||||
* Contract level New Jersey Family Leave Insurance Rate
|
||||
* Contract level New Jersey Workforce Development/Supplemental Workforce Funds
|
||||
""",
|
||||
|
||||
'auto_install': False,
|
||||
'website': 'https://hibou.io/',
|
||||
'data':[
|
||||
'us_nj_hr_payroll_view.xml',
|
||||
'data/base.xml',
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
}
|
||||
155
l10n_us_nj_hr_payroll/data/base.xml
Executable file
155
l10n_us_nj_hr_payroll/data/base.xml
Executable file
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- CONTRIBUTION REGISTERS -->
|
||||
<record id="res_partner_njdor_unemp_employee" model="res.partner">
|
||||
<field name="name">New Jersey Division of Taxation - Unemployment Insurance Tax(Employee)</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_njdor_unemp_company" model="res.partner">
|
||||
<field name="name">New Jersey Division of Taxation - Unemployment Insurance Tax(Employer)</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_njdor_sdi_employee" model="res.partner">
|
||||
<field name="name">New Jersey Division of Taxation - State Disability Insurance Tax(Employee)</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_njdor_sdi_company" model="res.partner">
|
||||
<field name="name">New Jersey Division of Taxation - State Disability Insurance Tax(Employer)</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_njdor_fli" model="res.partner">
|
||||
<field name="name">New Jersey Division of Taxation - Family Leave Insurance Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_njdor_wf" model="res.partner">
|
||||
<field name="name">New Jersey Division of Taxation - Workforce Development/Supplemental Workforce Funds Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_njdor_withhold" model="res.partner">
|
||||
<field name="name">New Jersey Division of Taxation - Income Tax Withholding</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_njdor_unemp_employee" model="hr.contribution.register">
|
||||
<field name="name">New Jersey Unemployment Insurance Tax</field>
|
||||
<field name="note">New Jersey Division of Taxation - Unemployment Insurance Tax(Employee)</field>
|
||||
<field name="partner_id" ref="res_partner_njdor_unemp_employee"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_njdor_unemp_company" model="hr.contribution.register">
|
||||
<field name="name">New Jersey Unemployment Insurance Tax</field>
|
||||
<field name="note">New Jersey Division of Taxation - Unemployment Insurance Tax(Employer)</field>
|
||||
<field name="partner_id" ref="res_partner_njdor_unemp_company"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_njdor_sdi_employee" model="hr.contribution.register">
|
||||
<field name="name">New Jersey State Disability Insurance</field>
|
||||
<field name="note">New Jersey Division of Taxation - State Disability Insurance Tax(Employee)</field>
|
||||
<field name="partner_id" ref="res_partner_njdor_sdi_employee"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_njdor_sdi_company" model="hr.contribution.register">
|
||||
<field name="name">New Jersey State Disability Insurance</field>
|
||||
<field name="note">New Jersey Division of Taxation - State Disability Insurance Tax(Employer)</field>
|
||||
<field name="partner_id" ref="res_partner_njdor_sdi_company"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_njdor_fli" model="hr.contribution.register">
|
||||
<field name="name">New Jersey State Family Leave Insurance</field>
|
||||
<field name="note">New Jersey Division of Taxation - Family Leave Insurance Tax</field>
|
||||
<field name="partner_id" ref="res_partner_njdor_fli"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_njdor_wf" model="hr.contribution.register">
|
||||
<field name="name">New Jersey State Workforce Development/Supplemental Workforce Funds</field>
|
||||
<field name="note">New Jersey Division of Taxation - Workforce Development/Supplemental Workforce Funds Tax</field>
|
||||
<field name="partner_id" ref="res_partner_njdor_wf"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_njdor_withhold" model="hr.contribution.register">
|
||||
<field name="name">New Jersey Income Tax Withholding</field>
|
||||
<field name="note">New Jersey Division of Taxation - Income Tax Withholding</field>
|
||||
<field name="partner_id" ref="res_partner_njdor_withhold"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- HR SALARY RULE CATEGORIES-->
|
||||
<record id="hr_payroll_nj_unemp_wages" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey Unemployment Insurance Tax - Wages</field>
|
||||
<field name="code">NJ_UNEMP_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_sdi_wages" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey State Disability Insurance Tax - Wages</field>
|
||||
<field name="code">NJ_SDI_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_fli_wages" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey Family Leave Insurance Tax - Wages</field>
|
||||
<field name="code">NJ_FLI_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_wf_wages" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey Work Force Development - Wages</field>
|
||||
<field name="code">NJ_WF_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_unemp_employee" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey Unemployment Insurance Tax - Employee</field>
|
||||
<field name="code">NJ_UNEMP_EMPLOYEE</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_unemp_company" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey Unemployment Insurance Tax - Employer</field>
|
||||
<field name="code">NJ_UNEMP_COMPANY</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_sdi_company" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey State Disability Insurance - Employer</field>
|
||||
<field name="code">NJ_SDI_COMPANY</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_sdi_employee" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey State Disability Insurance - Employee</field>
|
||||
<field name="code">NJ_SDI_EMPLOYEE</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_fli" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey State Family Leave Insurance Tax</field>
|
||||
<field name="code">NJ_FLI</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_wf" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey State Workforce Development/Suppllemental Tax</field>
|
||||
<field name="code">NJ_WF</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_nj_income_withhold" model="hr.salary.rule.category">
|
||||
<field name="name">New Jersey Income Withholding</field>
|
||||
<field name="code">NJ_WITHHOLD</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
27
l10n_us_nj_hr_payroll/data/final.xml
Executable file
27
l10n_us_nj_hr_payroll/data/final.xml
Executable file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR PAYROLL STRUCTURE -->
|
||||
<record id="hr_payroll_salary_structure_us_nj_employee" model="hr.payroll.structure">
|
||||
<field name="code">US_NJ_EMP</field>
|
||||
<field name="name">USA New Jersey Employee</field>
|
||||
<field eval="[(6, 0, [
|
||||
ref('hr_payroll_rules_nj_unemp_wages_2018'),
|
||||
ref('hr_payroll_rules_nj_unemp_company_2018'),
|
||||
ref('hr_payroll_rules_nj_unemp_employee_2018'),
|
||||
ref('hr_payroll_rules_nj_inc_withhold_2018'),
|
||||
ref('hr_payroll_rules_nj_sdi_wages_2018'),
|
||||
ref('hr_payroll_rules_nj_sdi_company_2018'),
|
||||
ref('hr_payroll_rules_nj_sdi_employee_2018'),
|
||||
ref('hr_payroll_rules_nj_fli_wages_2018'),
|
||||
ref('hr_payroll_rules_nj_fli_2018'),
|
||||
ref('hr_payroll_rules_nj_wf_wages_2018'),
|
||||
ref('hr_payroll_rules_nj_wf_2018'),
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="parent_id" ref="l10n_us_hr_payroll.hr_payroll_salary_structure_us_employee"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
697
l10n_us_nj_hr_payroll/data/rules_2018.xml
Executable file
697
l10n_us_nj_hr_payroll/data/rules_2018.xml
Executable file
@@ -0,0 +1,697 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR SALARY RULES-->
|
||||
<!-- UNEMPLOYMENT -->
|
||||
<record id="hr_payroll_rules_nj_unemp_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_unemp_wages"/>
|
||||
<field name="name">New Jersey Unemployment Insurance Tax - Wages (2018)</field>
|
||||
<field name="code">NJ_UNEMP_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('NJ_UNEMP_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 33700.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_nj_unemp_employee_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_unemp_employee"/>
|
||||
<field name="name">New Jersey Unemployment - Employee(2018)</field>
|
||||
<field name="code">NJ_UNEMP_EMPLOYEE_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.nj_unemp_employee_rate(2018)
|
||||
result = categories.NJ_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_njdor_unemp_employee"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_nj_unemp_company_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_unemp_company"/>
|
||||
<field name="name">New Jersey Unemployment - Employer(2018)</field>
|
||||
<field name="code">NJ_UNEMP_COMPANY_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.nj_unemp_company_rate(2018)
|
||||
result = categories.NJ_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_njdor_unemp_employee"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- STATE DISABILITY INSURANCE -->
|
||||
|
||||
<record id="hr_payroll_rules_nj_sdi_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_sdi_wages"/>
|
||||
<field name="name">New Jersey State Disability Tax - Wages (2018)</field>
|
||||
<field name="code">NJ_SDI_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('NJ_SDI_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 33700.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_nj_sdi_employee_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_sdi_employee"/>
|
||||
<field name="name">New Jersey State Disability Insurance - Employee(2018)</field>
|
||||
<field name="code">NJ_SDI_EMPLOYEE_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.nj_sdi_employee_rate(2018)
|
||||
result = categories.NJ_SDI_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_njdor_sdi_employee"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_nj_sdi_company_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_sdi_company"/>
|
||||
<field name="name">New Jersey State Disability Insurance - Employer(2018)</field>
|
||||
<field name="code">NJ_SDI_COMPANY_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.nj_sdi_company_rate(2018)
|
||||
result = categories.NJ_SDI_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_njdor_unemp_company"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- FAMILY LEAVE INSURANCE -->
|
||||
<record id="hr_payroll_rules_nj_fli_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_fli_wages"/>
|
||||
<field name="name">New Jersey Family Leave Insurance Tax - Wages (2018)</field>
|
||||
<field name="code">NJ_FLI_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('NJ_FLI_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 33700.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_nj_fli_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_fli"/>
|
||||
<field name="name">New Jersey Family Leave Insurance(2018)</field>
|
||||
<field name="code">NJ_FLI_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.nj_fli_rate(2018)
|
||||
result = categories.NJ_FLI_WAGES
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_njdor_fli"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- WORKFORCE DEVELOPMENT/SUPPLEMENTAL WORKFORCE FUNDS -->
|
||||
<record id="hr_payroll_rules_nj_wf_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_wf_wages"/>
|
||||
<field name="name">New Jersey Workforce Development/Supplemental Workforce Funds Tax - Wages (2018)</field>
|
||||
<field name="code">NJ_WF_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('NJ_WF_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 33700.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_nj_wf_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_wf"/>
|
||||
<field name="name">New Jersey Workforce Development/Supplemental Workforce Funds(2018)</field>
|
||||
<field name="code">NJ_WF_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.nj_wf_rate(2018)
|
||||
result = categories.NJ_WF_WAGES
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_njdor_wf"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- STATE INCOME WITHHOLDING -->
|
||||
<record id="hr_payroll_rules_nj_inc_withhold_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="145"/>
|
||||
<field name="category_id" ref="hr_payroll_nj_income_withhold"/>
|
||||
<field name="name">New Jersey Income Withholding</field>
|
||||
<field name="code">NJ_INC_WITHHOLD_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
wages = categories.GROSS
|
||||
allowances = contract.nj_njw4_allowances
|
||||
additional_withholding = contract.nj_additional_withholding
|
||||
schedule_pay = contract.schedule_pay
|
||||
filing_status = contract.nj_njw4_filing_status
|
||||
rate_table = contract.nj_njw4_rate_table
|
||||
|
||||
# Determine which rate table to use
|
||||
if not rate_table:
|
||||
if filing_status == 'single' or filing_status == 'married_separate':
|
||||
rate_table = 'A'
|
||||
elif filing_status == 'married_joint' or filing_status == 'widower' or filing_status == 'head_household':
|
||||
rate_table = 'B'
|
||||
|
||||
# Tables are found in http://www.state.nj.us/treasury/taxation/pdf/current/njwt.pdf
|
||||
# Tax Rate Tables to calculate income withholding
|
||||
|
||||
#### RATE 'A' ####
|
||||
if rate_table == 'A':
|
||||
### WEEKLY ###
|
||||
if schedule_pay == 'weekly':
|
||||
wages -= 19.20 * allowances
|
||||
tax_rate_table = [
|
||||
(384, 0.015, 0.0),
|
||||
(673, 0.02, 5.76),
|
||||
(769, 0.039, 11.54),
|
||||
(1442, 0.061, 15.28),
|
||||
(9615, 0.07, 56.34),
|
||||
(float('inf'), 0.099, 628.45),
|
||||
]
|
||||
|
||||
### BI-WEEKLY ###
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
wages -= 38.40 * allowances
|
||||
tax_rate_table = [
|
||||
(769, 0.015, 0.0),
|
||||
(1346, 0.02, 11.54),
|
||||
(1538, 0.039, 23.08),
|
||||
(2884, 0.061, 30.56),
|
||||
(19231, 0.07, 112.67),
|
||||
(float('inf'), 0.099, 1256.96),
|
||||
]
|
||||
|
||||
### SEMI-MONTHLY ###
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
wages -= 41.60 * allowances
|
||||
tax_rate_table = [
|
||||
(833, 0.015, 0.0),
|
||||
(1458, 0.02, 12.50),
|
||||
(1666, 0.039, 25.00),
|
||||
(3125, 0.061, 33.11),
|
||||
(20833, 0.07, 112.11),
|
||||
(float('inf'), 0.099, 1361.67),
|
||||
]
|
||||
|
||||
### MONTHLY ###
|
||||
elif schedule_pay == 'monthly':
|
||||
wages -= 83.30 * allowances
|
||||
tax_rate_table = [
|
||||
(1666, 0.015, 0.0),
|
||||
(2916, 0.02, 24.99),
|
||||
(3333, 0.039, 49.99),
|
||||
(6250, 0.061, 66.25),
|
||||
(41667, 0.07, 244.19),
|
||||
(float('inf'), 0.099, 2723.38),
|
||||
]
|
||||
|
||||
### QUARTERLY ###
|
||||
elif schedule_pay == 'quarterly':
|
||||
wages -= 250.00 * allowances
|
||||
tax_rate_table = [
|
||||
(5000, 0.015, 0.0),
|
||||
(8750, 0.02, 75.00),
|
||||
(10000, 0.039, 150.00),
|
||||
(18750, 0.061, 198.75),
|
||||
(125000, 0.07, 732.50),
|
||||
(float('inf'), 0.099, 8170.00),
|
||||
]
|
||||
|
||||
### SEMI-ANNUAL ###
|
||||
elif schedule_pay == 'semi-annual':
|
||||
wages -= 500 * allowances
|
||||
tax_rate_table = [
|
||||
(10000, 0.015, 0.0),
|
||||
(17500, 0.02, 150.00),
|
||||
(20000, 0.039, 300.00),
|
||||
(37500, 0.061, 397.50),
|
||||
(250000, 0.07, 1465.00),
|
||||
(float('inf'), 0.099, 16340.00),
|
||||
]
|
||||
|
||||
### ANNUAL ###
|
||||
elif schedule_pay == 'annually':
|
||||
wages -= 1000.00 * allowances
|
||||
tax_rate_table = [
|
||||
(20000, 0.015, 0.0),
|
||||
(35000, 0.02, 300.00),
|
||||
(40000, 0.039, 600.00),
|
||||
(75000, 0.061, 795.00),
|
||||
(500000, 0.07, 2930.00),
|
||||
(float('inf'), 0.099, 32680.00),
|
||||
]
|
||||
|
||||
#### RATE 'B' ####
|
||||
elif rate_table == 'B':
|
||||
### WEEKLY ###
|
||||
if schedule_pay == 'weekly':
|
||||
wages -= 19.20 * allowances
|
||||
tax_rate_table = [
|
||||
(384, 0.015, 0.0),
|
||||
(961, 0.02, 5.76),
|
||||
(1346, 0.027, 17.30),
|
||||
(1538, 0.039, 27.70),
|
||||
(2884, 0.061, 35.18),
|
||||
(9615, 0.07, 117.29),
|
||||
(float('inf'), 0.099, 588.46),
|
||||
]
|
||||
|
||||
### BI-WEEKLY ###
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
wages -= 38.40 * allowances
|
||||
tax_rate_table = [
|
||||
(769, 0.015, 0.0),
|
||||
(1923, 0.02, 11.54),
|
||||
(2692, 0.027, 34.62),
|
||||
(3076, 0.039, 55.38),
|
||||
(5769, 0.061, 70.35),
|
||||
(19231, 0.07, 234.63),
|
||||
(float('inf'), 0.099, 1176.97),
|
||||
]
|
||||
|
||||
### SEMI-MONTHLY ###
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
wages -= 41.60 * allowances
|
||||
tax_rate_table = [
|
||||
(833, 0.015, 0.0),
|
||||
(2083, 0.02, 12.50),
|
||||
(2916, 0.027, 37.50),
|
||||
(3333, 0.039, 59.99),
|
||||
(6250, 0.061, 76.25),
|
||||
(20833, 0.07, 254.19),
|
||||
(float('inf'), 0.099, 1275),
|
||||
]
|
||||
|
||||
### MONTHLY ###
|
||||
elif schedule_pay == 'monthly':
|
||||
wages -= 83.30 * allowances
|
||||
tax_rate_table = [
|
||||
(1666, 0.015, 0.0),
|
||||
(4166, 0.02, 24.99),
|
||||
(5833, 0.027, 74.99),
|
||||
(6666, 0.039, 120.00),
|
||||
(12500, 0.061, 152.49),
|
||||
(41667, 0.07, 508.36),
|
||||
(float('inf'), 0.099, 2550.05),
|
||||
]
|
||||
|
||||
### QUARTERLY ###
|
||||
elif schedule_pay == 'quarterly':
|
||||
wages -= 250.00 * allowances
|
||||
tax_rate_table = [
|
||||
(5000, 0.015, 0.0),
|
||||
(12500, 0.02, 75.00),
|
||||
(17500, 0.027, 225.00),
|
||||
(20000, 0.039, 360.00),
|
||||
(37500, 0.061, 457.50),
|
||||
(125000, 0.07, 1525.00),
|
||||
(float('inf'), 0.099, 7650.00),
|
||||
]
|
||||
|
||||
### SEMI-ANNUAL ###
|
||||
elif schedule_pay == 'semi-annual':
|
||||
wages -= 500.00 * allowances
|
||||
tax_rate_table = [
|
||||
(10000, 0.015, 0.0),
|
||||
(25000, 0.02, 150.00),
|
||||
(35000, 0.027, 450.00),
|
||||
(40000, 0.039, 720.00),
|
||||
(75000, 0.061, 915.00),
|
||||
(250000, 0.07, 3050.00),
|
||||
(float('inf'), 0.099, 15300.00),
|
||||
]
|
||||
|
||||
### ANNUAL ###
|
||||
elif schedule_pay == 'annually':
|
||||
wages -= 1000.00 * allowances
|
||||
tax_rate_table = [
|
||||
(20000, 0.015, 0.0),
|
||||
(50000, 0.02, 300.00),
|
||||
(70000, 0.027, 900.00),
|
||||
(80000, 0.039, 1440.00),
|
||||
(150000, 0.061, 1830.00),
|
||||
(500000, 0.07, 6100.00),
|
||||
(float('inf'), 0.099, 30600.00),
|
||||
]
|
||||
|
||||
#### RATE 'C' ####
|
||||
elif rate_table == 'C':
|
||||
### WEEKLY ###
|
||||
if schedule_pay == 'weekly':
|
||||
wages -= 19.20 * allowances
|
||||
tax_rate_table = [
|
||||
(384, 0.015, 0.0),
|
||||
(769, 0.023, 5.76),
|
||||
(961, 0.028, 14.62),
|
||||
(1153, 0.035, 19.99),
|
||||
(2884, 0.056, 26.71),
|
||||
(9615, 0.066, 123.65),
|
||||
(float('inf'), 0.099, 567.90),
|
||||
]
|
||||
|
||||
### BI-WEEKLY ###
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
wages -= 38.40 * allowances
|
||||
tax_rate_table = [
|
||||
(769, 0.015, 0.0),
|
||||
(1538, 0.023, 11.54),
|
||||
(1923, 0.028, 29.22),
|
||||
(2307, 0.035, 40.00),
|
||||
(5769, 0.056, 53.44),
|
||||
(19231, 0.066, 247.31),
|
||||
(float('inf'), 0.099, 1135.80),
|
||||
]
|
||||
|
||||
### SEMI-MONTHLY ###
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
wages -= 41.60 * allowances
|
||||
tax_rate_table = [
|
||||
(833, 0.015, 0.0),
|
||||
(1666, 0.023, 12.50),
|
||||
(2083, 0.028, 31.65),
|
||||
(2500, 0.035, 43.33),
|
||||
(6250, 0.056, 57.93),
|
||||
(20833, 0.066, 26793),
|
||||
(float('inf'), 0.099, 1230.41),
|
||||
]
|
||||
|
||||
### MONTHLY ###
|
||||
elif schedule_pay == 'monthly':
|
||||
wages -= 83.30 * allowances
|
||||
tax_rate_table = [
|
||||
(1666, 0.015, 0.0),
|
||||
(3333, 0.023, 24.99),
|
||||
(4166, 0.028, 63.33),
|
||||
(5000, 0.035, 86.66),
|
||||
(12500, 0.056, 115.85),
|
||||
(41667, 0.066, 535.85),
|
||||
(float('inf'), 0.099, 2460.87),
|
||||
]
|
||||
|
||||
### QUARTERLY ###
|
||||
elif schedule_pay == 'quarterly':
|
||||
wages -= 250.00 * allowances
|
||||
tax_rate_table = [
|
||||
(5000, 0.015, 0.0),
|
||||
(10000, 0.023, 75.00),
|
||||
(12500, 0.028, 190.00),
|
||||
(15000, 0.035, 260.00),
|
||||
(37500, 0.056, 347.50),
|
||||
(125000, 0.066, 1607.50),
|
||||
(float('inf'), 0.099, 7382.50),
|
||||
]
|
||||
|
||||
### SEMI-ANNUAL ###
|
||||
elif schedule_pay == 'semi-annual':
|
||||
wages -= 500.00 * allowances
|
||||
tax_rate_table = [
|
||||
(10000, 0.015, 0.0),
|
||||
(20000, 0.023, 150.00),
|
||||
(25000, 0.028, 380.00),
|
||||
(30000, 0.035, 520.00),
|
||||
(75000, 0.056, 695.00),
|
||||
(250000, 0.066, 3215.00),
|
||||
(float('inf'), 0.099, 14765.00),
|
||||
]
|
||||
|
||||
### ANNUAL ###
|
||||
elif schedule_pay == 'annually':
|
||||
wages -= 1000.00 * allowances
|
||||
tax_rate_table = [
|
||||
(20000, 0.015, 0.0),
|
||||
(40000, 0.023, 300.00),
|
||||
(50000, 0.028, 760.00),
|
||||
(60000, 0.035, 1040.00),
|
||||
(150000, 0.056, 1390.00),
|
||||
(500000, 0.066, 6430.00),
|
||||
(float('inf'), 0.099, 29530.00),
|
||||
]
|
||||
|
||||
#### RATE 'D' ####
|
||||
elif rate_table == 'D':
|
||||
### WEEKLY ###
|
||||
if schedule_pay == 'weekly':
|
||||
wages -= 19.20 * allowances
|
||||
tax_rate_table = [
|
||||
(384, 0.015, 0.0),
|
||||
(769, 0.027, 5.76),
|
||||
(961, 0.034, 16.16),
|
||||
(1153, 0.043, 22.68),
|
||||
(2884, 0.056, 30.94),
|
||||
(9615, 0.065, 127.88),
|
||||
(float('inf'), 0.099, 565.40),
|
||||
]
|
||||
|
||||
### BI-WEEKLY ###
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
wages -= 38.40 * allowances
|
||||
tax_rate_table = [
|
||||
(769, 0.015, 0.0),
|
||||
(1538, 0.027, 11.54),
|
||||
(1923, 0.034, 32.30),
|
||||
(2307, 0.043, 45.39),
|
||||
(5769, 0.056, 61.90),
|
||||
(19231, 0.065, 255.77),
|
||||
(float('inf'), 0.099, 1130.80),
|
||||
]
|
||||
|
||||
### SEMI-MONTHLY ###
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
wages -= 41.60 * allowances
|
||||
tax_rate_table = [
|
||||
(833, 0.015, 0.0),
|
||||
(1666, 0.027, 12.50),
|
||||
(2083, 0.034, 34.99),
|
||||
(2500, 0.043, 49.16),
|
||||
(6250, 0.056, 67.10),
|
||||
(20833, 0.065, 277.10),
|
||||
(float('inf'), 0.099, 1225.00),
|
||||
]
|
||||
|
||||
### MONTHLY ###
|
||||
elif schedule_pay == 'monthly':
|
||||
wages -= 83.30 * allowances
|
||||
tax_rate_table = [
|
||||
(1666, 0.015, 0.0),
|
||||
(3333, 0.027, 24.99),
|
||||
(4166, 0.034, 70.00),
|
||||
(5000, 0.043, 98.32),
|
||||
(12500, 0.056, 134.18),
|
||||
(41667, 0.065, 554.18),
|
||||
(float('inf'), 0.099, 2450.04),
|
||||
]
|
||||
|
||||
### QUARTERLY ###
|
||||
elif schedule_pay == 'quarterly':
|
||||
wages -= 250.00 * allowances
|
||||
tax_rate_table = [
|
||||
(5000, 0.015, 0.0),
|
||||
(10000, 0.027, 75.00),
|
||||
(12500, 0.034, 210.00),
|
||||
(15000, 0.043, 295.00),
|
||||
(37500, 0.056, 402.50),
|
||||
(125000, 0.065, 1662.50),
|
||||
(float('inf'), 0.099, 7350.00),
|
||||
]
|
||||
|
||||
### SEMI-ANNUAL ###
|
||||
elif schedule_pay == 'semi-annual':
|
||||
wages -= 500.00 * allowances
|
||||
tax_rate_table = [
|
||||
(10000, 0.015, 0.0),
|
||||
(20000, 0.027, 150.00),
|
||||
(25000, 0.034, 420.00),
|
||||
(30000, 0.043, 590.00),
|
||||
(75000, 0.056, 805.00),
|
||||
(250000, 0.065, 3325.00),
|
||||
(float('inf'), 0.099, 14700.00),
|
||||
]
|
||||
|
||||
### ANNUAL ###
|
||||
elif schedule_pay == 'annually':
|
||||
wages -= 1000.00 * allowances
|
||||
tax_rate_table = [
|
||||
(20000, 0.015, 0.0),
|
||||
(40000, 0.027, 300.00),
|
||||
(50000, 0.034, 840.00),
|
||||
(60000, 0.043, 1180.00),
|
||||
(150000, 0.056, 1610.00),
|
||||
(250000, 0.065, 6650.00),
|
||||
(float('inf'), 0.099, 29400.00),
|
||||
]
|
||||
|
||||
#### RATE 'E' ####
|
||||
elif rate_table == 'E':
|
||||
### WEEKLY ###
|
||||
if schedule_pay == 'weekly':
|
||||
wages -= 19.20 * allowances
|
||||
tax_rate_table = [
|
||||
(384, 0.015, 0.0),
|
||||
(673, 0.02, 5.76),
|
||||
(1923, 0.058, 11.54),
|
||||
(9615, 0.065, 84.04),
|
||||
(float('inf'), 0.099, 584.02),
|
||||
]
|
||||
|
||||
### BI-WEEKLY ###
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
wages -= 38.40 * allowances
|
||||
tax_rate_table = [
|
||||
(769, 0.015, 0.0),
|
||||
(1346, 0.02, 11.54),
|
||||
(3846, 0.058, 23.08),
|
||||
(19231, 0.065, 168.08),
|
||||
(float('inf'), 0.099, 1168.11),
|
||||
]
|
||||
|
||||
### SEMI-MONTHLY ###
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
wages -= 41.60 * allowances
|
||||
tax_rate_table = [
|
||||
(833, 0.015, 0.0),
|
||||
(1458, 0.02, 12.50),
|
||||
(4166, 0.058, 25.00),
|
||||
(20833, 0.065, 182.06),
|
||||
(float('inf'), 0.099, 1265.42),
|
||||
]
|
||||
|
||||
### MONTHLY ###
|
||||
elif schedule_pay == 'monthly':
|
||||
wages -= 83.30 * allowances
|
||||
tax_rate_table = [
|
||||
(1666, 0.015, 0.0),
|
||||
(2916, 0.02, 24.99),
|
||||
(8333, 0.058, 49.99),
|
||||
(41667, 0.065, 364.18),
|
||||
(float('inf'), 0.099, 2530.89),
|
||||
]
|
||||
|
||||
### QUARTERLY ###
|
||||
elif schedule_pay == 'quarterly':
|
||||
wages -= 250.00 * allowances
|
||||
tax_rate_table = [
|
||||
(5000, 0.015, 0.0),
|
||||
(8750, 0.02, 75.00),
|
||||
(25000, 0.058, 150.00),
|
||||
(125000, 0.065, 1092.50),
|
||||
(float('inf'), 0.099, 7592.50),
|
||||
]
|
||||
|
||||
### SEMI-ANNUAL ###
|
||||
elif schedule_pay == 'semi-annual':
|
||||
wages -= 500.00 * allowances
|
||||
tax_rate_table = [
|
||||
(10000, 0.015, 0.0),
|
||||
(17500, 0.02, 150.00),
|
||||
(50000, 0.058, 300.00),
|
||||
(250000, 0.065, 2185.00),
|
||||
(float('inf'), 0.099, 15,185.00),
|
||||
]
|
||||
|
||||
### ANNUAL ###
|
||||
elif schedule_pay == 'annually':
|
||||
wages -= 1000.00 * allowances
|
||||
tax_rate_table = [
|
||||
(20000, 0.015, 0.0),
|
||||
(35000, 0.02, 300.00),
|
||||
(100000, 0.058, 600.00),
|
||||
(500000, 0.065, 4370.00),
|
||||
(float('inf'), 0.099, 30370.00),
|
||||
]
|
||||
|
||||
|
||||
over = 0.0
|
||||
tax = 0.0
|
||||
for row in tax_rate_table:
|
||||
if wages <= row[0]:
|
||||
tax = ((wages - over) * row[1]) + row[2]
|
||||
tax += additional_withholding
|
||||
break
|
||||
over = row[0]
|
||||
|
||||
result = -tax
|
||||
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_njdor_withhold"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
1
l10n_us_nj_hr_payroll/tests/__init__.py
Executable file
1
l10n_us_nj_hr_payroll/tests/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import test_us_nj_payslip_2018
|
||||
135
l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2018.py
Executable file
135
l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2018.py
Executable file
@@ -0,0 +1,135 @@
|
||||
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
|
||||
from odoo.addons.l10n_us_hr_payroll.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
|
||||
class TestUsNJPayslip(TestUsPayslip):
|
||||
###
|
||||
# 2018 Taxes and Rates
|
||||
###
|
||||
NJ_UNEMP_MAX_WAGE = 33700.0
|
||||
|
||||
# Examples found on page 24 of http://www.state.nj.us/treasury/taxation/pdf/current/njwt.pdf
|
||||
def test_2018_taxes_example1(self):
|
||||
salary = 300
|
||||
schedule_pay = 'weekly'
|
||||
allowances = 1
|
||||
additional_withholding = 0
|
||||
|
||||
# Tax Percentage Method for Single, taxable pay over $58, under $346
|
||||
wh = -4.21
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.nj_unemp_employee = 0.3825
|
||||
employee.company_id.nj_unemp_company = 3.4
|
||||
employee.company_id.nj_sdi_employee = 0.19
|
||||
employee.company_id.nj_sdi_company = 0.5
|
||||
employee.company_id.nj_fli = 0.09
|
||||
employee.company_id.nj_wf = 0.0
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_nj_hr_payroll.hr_payroll_salary_structure_us_nj_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.nj_njw4_allowances = allowances
|
||||
contract.nj_additional_withholding = additional_withholding
|
||||
contract.nj_njw4_filing_status = 'single'
|
||||
contract.nj_njw4_rate_table = 'A'
|
||||
|
||||
# tax rates
|
||||
nj_unemp_employee = contract.nj_unemp_employee_rate(2018) / -100.0
|
||||
nj_unemp_company = contract.nj_unemp_company_rate(2018) / -100.0
|
||||
nj_sdi_employee = contract.nj_sdi_employee_rate(2018) / -100.0
|
||||
nj_sdi_company = contract.nj_sdi_company_rate(2018) / -100.0
|
||||
nj_fli = contract.nj_fli_rate(2018) / -100.0
|
||||
nj_wf = contract.nj_wf_rate(2018) / -100.0
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'weekly')
|
||||
|
||||
self._log('2018 New Jersey tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_EMPLOYEE'], round(cats['NJ_UNEMP_WAGES'] * nj_unemp_employee, 2))
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_COMPANY'], cats['NJ_UNEMP_WAGES'] * nj_unemp_company)
|
||||
self.assertPayrollEqual(cats['NJ_SDI_EMPLOYEE'], cats['NJ_SDI_WAGES'] * nj_sdi_employee)
|
||||
self.assertPayrollEqual(cats['NJ_SDI_COMPANY'], cats['NJ_SDI_WAGES'] * nj_sdi_company)
|
||||
self.assertPayrollEqual(cats['NJ_FLI'], cats['NJ_FLI_WAGES'] * nj_fli)
|
||||
self.assertPayrollEqual(cats['NJ_WF'], cats['NJ_WF_WAGES'] * nj_wf)
|
||||
self.assertPayrollEqual(cats['NJ_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_nj_unemp_wages = self.NJ_UNEMP_MAX_WAGE - salary if (self.NJ_UNEMP_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 New Jersey tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_WAGES'], remaining_nj_unemp_wages)
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_COMPANY'], remaining_nj_unemp_wages * nj_unemp_company)
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_EMPLOYEE'], remaining_nj_unemp_wages * nj_unemp_employee)
|
||||
|
||||
def test_2018_taxes_example2(self):
|
||||
salary = 1400.00
|
||||
schedule_pay = 'weekly'
|
||||
allowances = 3
|
||||
additional_withholding = 0
|
||||
|
||||
# Tax Percentage Method for Single, taxable pay over $58, under $346
|
||||
wh = -27.60
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.nj_unemp_employee = 0.3825
|
||||
employee.company_id.nj_unemp_company = 3.4
|
||||
employee.company_id.nj_sdi_employee = 0.19
|
||||
employee.company_id.nj_sdi_company = 0.5
|
||||
employee.company_id.nj_fli = 0.09
|
||||
employee.company_id.nj_wf = 0.0
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_nj_hr_payroll.hr_payroll_salary_structure_us_nj_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.nj_njw4_allowances = allowances
|
||||
contract.nj_additional_withholding = additional_withholding
|
||||
contract.nj_njw4_filing_status = 'married_joint'
|
||||
|
||||
# tax rates
|
||||
nj_unemp_employee = contract.nj_unemp_employee_rate(2018) / -100.0
|
||||
nj_unemp_company = contract.nj_unemp_company_rate(2018) / -100.0
|
||||
nj_sdi_employee = contract.nj_sdi_employee_rate(2018) / -100.0
|
||||
nj_sdi_company = contract.nj_sdi_company_rate(2018) / -100.0
|
||||
nj_fli = contract.nj_fli_rate(2018) / -100.0
|
||||
nj_wf = contract.nj_wf_rate(2018) / -100.0
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'weekly')
|
||||
|
||||
self._log('2018 New Jersey tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_EMPLOYEE'], round((cats['NJ_UNEMP_WAGES'] * nj_unemp_employee), 2))
|
||||
self.assertPayrollEqual(cats['NJ_UNEMP_COMPANY'], cats['NJ_UNEMP_WAGES'] * nj_unemp_company)
|
||||
self.assertPayrollEqual(cats['NJ_SDI_EMPLOYEE'], cats['NJ_SDI_WAGES'] * nj_sdi_employee)
|
||||
self.assertPayrollEqual(cats['NJ_SDI_COMPANY'], cats['NJ_SDI_WAGES'] * nj_sdi_company)
|
||||
self.assertPayrollEqual(cats['NJ_FLI'], cats['NJ_FLI_WAGES'] * nj_fli)
|
||||
self.assertPayrollEqual(cats['NJ_WF'], cats['NJ_WF_WAGES'] * nj_wf)
|
||||
self.assertPayrollEqual(cats['NJ_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
92
l10n_us_nj_hr_payroll/us_nj_hr_payroll.py
Executable file
92
l10n_us_nj_hr_payroll/us_nj_hr_payroll.py
Executable file
@@ -0,0 +1,92 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class USNJHrContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
nj_njw4_allowances = fields.Integer(string="New Jersey NJ-W4 Allowances",
|
||||
default=0,
|
||||
help="Allowances claimed on NJ W-4")
|
||||
nj_additional_withholding = fields.Float(string="Additional Withholding",
|
||||
default=0.0,
|
||||
help='Additional withholding from line 5 of form NJ-W4')
|
||||
nj_njw4_filing_status = fields.Selection([
|
||||
('single', 'Single'),
|
||||
('married_separate', 'Married/Civil Union partner Separate'),
|
||||
('married_joint', 'Married/Civil Union Couple Joint'),
|
||||
('widower', 'Widower/Surviving Civil Union Partner'),
|
||||
('head_household', 'Head of Household')
|
||||
], string='NJ Filing Status', default='single')
|
||||
nj_njw4_rate_table = fields.Char(string='Wage Chart Letter',
|
||||
help='Wage Chart Letter from line 3 of form NJ-W4.')
|
||||
|
||||
def nj_unemp_employee_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'nj_unemp_employee_rate_' + str(year)):
|
||||
return self.employee_id.company_id['nj_unemp_employee_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New Jersey.')
|
||||
|
||||
def nj_unemp_company_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'nj_unemp_company_rate_' + str(year)):
|
||||
return self.employee_id.company_id['nj_unemp_company_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New Jersey.')
|
||||
|
||||
def nj_sdi_company_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'nj_sdi_company_rate_' + str(year)):
|
||||
return self.employee_id.company_id['nj_sdi_company_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New Jersey.')
|
||||
|
||||
def nj_sdi_employee_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'nj_sdi_employee_rate_' + str(year)):
|
||||
return self.employee_id.company_id['nj_sdi_employee_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New Jersey.')
|
||||
|
||||
def nj_fli_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'nj_fli_rate_' + str(year)):
|
||||
return self.employee_id.company_id['nj_fli_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New Jersey.')
|
||||
|
||||
def nj_wf_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'nj_wf_rate_' + str(year)):
|
||||
return self.employee_id.company_id['nj_wf_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New Jersey.')
|
||||
|
||||
|
||||
class NJCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
nj_unemp_company_rate_2018 = fields.Float(string="New Jersey Employer State Unemployment Insurance Rate 2018", default=3.4)
|
||||
nj_unemp_employee_rate_2018 = fields.Float(string="New Jersey Employee State Unemployment Insurance Rate 2018", default=0.3825)
|
||||
nj_sdi_company_rate_2018 = fields.Float(string="New Jersey Employer State Disability Insurance Rate 2018", default=0.5)
|
||||
nj_sdi_employee_rate_2018 = fields.Float(string="New Jersey Employee State Disability Insurance Rate 2018", default=0.19)
|
||||
nj_fli_rate_2018 = fields.Float(string="New Jersey Family Leave Insurance Rate 2018", default=0.09)
|
||||
nj_wf_rate_2018 = fields.Float(string="New Jersey Workforce Development Rate 2018", default=0.0)
|
||||
42
l10n_us_nj_hr_payroll/us_nj_hr_payroll_view.xml
Executable file
42
l10n_us_nj_hr_payroll/us_nj_hr_payroll_view.xml
Executable file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="us_nj_view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='currency_id']" position="after">
|
||||
<field name="nj_unemp_company_rate_2018"/>
|
||||
<field name="nj_unemp_employee_rate_2018"/>
|
||||
<field name="nj_sdi_company_rate_2018"/>
|
||||
<field name="nj_sdi_employee_rate_2018"/>
|
||||
<field name="nj_fli_rate_2018"/>
|
||||
<field name="nj_wf_rate_2018"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_form_l10n_us_nj_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority">147</field>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//group[@name='state_filing']" position="inside">
|
||||
<group string="New Jersey" name="nj">
|
||||
<field name="nj_njw4_filing_status"/>
|
||||
<field name="nj_njw4_rate_table"/>
|
||||
<field name="nj_njw4_allowances" string="NJ-W4 Allowances"/>
|
||||
<field name="nj_additional_withholding" string="Additional Withholding"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
49
l10n_us_ny_hr_payroll/README.rst
Normal file
49
l10n_us_ny_hr_payroll/README.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
*************************************
|
||||
Hibou - US Payroll - New York State
|
||||
*************************************
|
||||
|
||||
Calculations and contribution registers for New York State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Partner and Contribution Register for New York Department of Taxation and Finance
|
||||
* Company level New York Unemployment Rate
|
||||
* Company level New York Re-employment Service Fund
|
||||
* Company level New York Metropolitan Commuter Transportation Mobility Tax
|
||||
* Contract level New York State Income Tax
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41480034-ef3aa5b8-7081-11e8-990d-3231ebfc2c16.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA New York Employee Added to Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41480052-fbad372a-7081-11e8-9421-996d26f449ab.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* New York Income Withholding
|
||||
* New York Metropolitan Commuter Transportation Mobility Tax
|
||||
* New York Unemployment Insurance Tax - Wages
|
||||
* New York Unemployment Insurance Tax
|
||||
* New York Re-employment Service Fund
|
||||
|
||||
=======
|
||||
Notes
|
||||
=======
|
||||
|
||||
This module does not include tax rules for NYC and Yonkers.
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
1
l10n_us_ny_hr_payroll/__init__.py
Executable file
1
l10n_us_ny_hr_payroll/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import hr_payroll
|
||||
29
l10n_us_ny_hr_payroll/__manifest__.py
Executable file
29
l10n_us_ny_hr_payroll/__manifest__.py
Executable file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
'name': 'USA - New York - Payroll',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Localization',
|
||||
'depends': ['l10n_us_hr_payroll'],
|
||||
'version': '11.0.2018.0.0',
|
||||
'description': """
|
||||
USA::New York Payroll Rules.
|
||||
==============================
|
||||
|
||||
* New Jersey Department of Taxation and Finance partner
|
||||
* Contribution register and partner for New York State Department of Taxation and Finance
|
||||
* Company level New York Unemployment Rate
|
||||
* Company Level New York Re-employment Service Fund
|
||||
* Company level New York Metropolitan Commuter Transportation Mobility Tax
|
||||
* Contract level New York Income Tax
|
||||
""",
|
||||
|
||||
'auto_install': False,
|
||||
'website': 'https://hibou.io/',
|
||||
'data': [
|
||||
'hr_payroll_view.xml',
|
||||
'data/base.xml',
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
}
|
||||
78
l10n_us_ny_hr_payroll/data/base.xml
Executable file
78
l10n_us_ny_hr_payroll/data/base.xml
Executable file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- CONTRIBUTION REGISTERS -->
|
||||
<record id="res_partner_nydor_unemp" model="res.partner">
|
||||
<field name="name">New York State Department of Taxation and Finance - Unemployment Insurance Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_nydor_withhold" model="res.partner">
|
||||
<field name="name">New York State Department of Taxation and Finance - Income Tax Withholding</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_nydor_rsf" model="res.partner">
|
||||
<field name="name">New York State Department of Taxation and Finance - Re-employment Service Fund</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="res_partner_nydor_mctmt" model="res.partner">
|
||||
<field name="name">New York State Department of Taxation and Finance - Metropolitan Commuter Transportation Mobility Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_nydor_unemp" model="hr.contribution.register">
|
||||
<field name="name">New York Unemployment Insurance Tax</field>
|
||||
<field name="note">New York State Department of Taxation and Finance - Unemployment Insurance Tax</field>
|
||||
<field name="partner_id" ref="res_partner_nydor_unemp"/>
|
||||
</record>
|
||||
<record id="contrib_register_nydor_withhold" model="hr.contribution.register">
|
||||
<field name="name">New York Income Tax Withholding</field>
|
||||
<field name="note">New York State Department of Taxation and Finance - Income Tax Withholding</field>
|
||||
<field name="partner_id" ref="res_partner_nydor_withhold"/>
|
||||
</record>
|
||||
<record id="contrib_register_nydor_rsf" model="hr.contribution.register">
|
||||
<field name="name">Re-employment Service Fund</field>
|
||||
<field name="note">New York State Department of Taxation and Finance - Re-employment Service Fund</field>
|
||||
<field name="partner_id" ref="res_partner_nydor_rsf"/>
|
||||
</record>
|
||||
<record id="contrib_register_nydor_mctmt" model="hr.contribution.register">
|
||||
<field name="name">Metropolitan Commuter Transportation Mobility Tax</field>
|
||||
<field name="note">New York State Department of Taxation and Finance - Metropolitan Commuter Transportation Mobility Tax</field>
|
||||
<field name="partner_id" ref="res_partner_nydor_mctmt"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- HR SALARY RULE CATEGORIES-->
|
||||
<record id="hr_payroll_ny_unemp_wages" model="hr.salary.rule.category">
|
||||
<field name="name">New York Unemployment Insurance Tax - Wages</field>
|
||||
<field name="code">NY_UNEMP_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ny_unemp" model="hr.salary.rule.category">
|
||||
<field name="name">New York Unemployment Insurance Tax</field>
|
||||
<field name="code">NY_UNEMP</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ny_rsf" model="hr.salary.rule.category">
|
||||
<field name="name">New York Re-employment Service Fund</field>
|
||||
<field name="code">NY_RSF</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ny_mctmt" model="hr.salary.rule.category">
|
||||
<field name="name">New York Metropolitan Commuter Transportation Mobility Tax</field>
|
||||
<field name="code">NY_MCTMT</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_ny_income_withhold" model="hr.salary.rule.category">
|
||||
<field name="name">New York Income Withholding</field>
|
||||
<field name="code">NY_WITHHOLD</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
21
l10n_us_ny_hr_payroll/data/final.xml
Executable file
21
l10n_us_ny_hr_payroll/data/final.xml
Executable file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR PAYROLL STRUCTURE -->
|
||||
<record id="hr_payroll_salary_structure_us_ny_employee" model="hr.payroll.structure">
|
||||
<field name="code">US_NY_EMP</field>
|
||||
<field name="name">USA New York Employee</field>
|
||||
<field eval="[(6, 0, [
|
||||
ref('hr_payroll_rules_ny_unemp_wages_2018'),
|
||||
ref('hr_payroll_rules_ny_unemp_2018'),
|
||||
ref('hr_payroll_rules_ny_rsf_2018'),
|
||||
ref('hr_payroll_rules_ny_mctmt_2018'),
|
||||
ref('hr_payroll_rules_ny_inc_withhold_2018'),
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="parent_id" ref="l10n_us_hr_payroll.hr_payroll_salary_structure_us_employee"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
350
l10n_us_ny_hr_payroll/data/rules_2018.xml
Executable file
350
l10n_us_ny_hr_payroll/data/rules_2018.xml
Executable file
@@ -0,0 +1,350 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR SALARY RULES-->
|
||||
<!-- UNEMPLOYMENT -->
|
||||
<record id="hr_payroll_rules_ny_unemp_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_ny_unemp_wages"/>
|
||||
<field name="name">New York Unemployment Insurance Tax - Wages (2018)</field>
|
||||
<field name="code">NY_UNEMP_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('NY_UNEMP_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 11100.00 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rules_ny_unemp_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_ny_unemp"/>
|
||||
<field name="name">New York Unemployment Insurance Tax(2018)</field>
|
||||
<field name="code">NY_UNEMP_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.ny_unemp_rate(2018)
|
||||
result = categories.NY_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_nydor_unemp"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- RSF -->
|
||||
<record id="hr_payroll_rules_ny_rsf_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_ny_rsf"/>
|
||||
<field name="name">New York Re-employment Service Fund(2018)</field>
|
||||
<field name="code">NY_RSF_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.ny_rsf_rate(2018)
|
||||
result = categories.NY_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_nydor_rsf"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- MCTMT-->
|
||||
<record id="hr_payroll_rules_ny_mctmt_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="195"/>
|
||||
<field name="category_id" ref="hr_payroll_ny_mctmt"/>
|
||||
<field name="name">New York Metropolitan Commuter Transportation Mobility Tax(2018)</field>
|
||||
<field name="code">NY_MCTMT_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.ny_mctmt_rate(2018)
|
||||
result = categories.MCTMT_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_nydor_mctmt"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- STATE INCOME WITHHOLDING -->
|
||||
<record id="hr_payroll_rules_ny_inc_withhold_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="145"/>
|
||||
<field name="category_id" ref="hr_payroll_ny_income_withhold"/>
|
||||
<field name="name">New York Income Withholding</field>
|
||||
<field name="code">NY_INC_WITHHOLD_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
wages = categories.GROSS
|
||||
allowances = contract.ny_it2104_allowances
|
||||
additional_withholding = contract.ny_additional_withholding
|
||||
schedule_pay = contract.schedule_pay
|
||||
filing_status = contract.ny_it2104_filing_status
|
||||
|
||||
if filing_status == 'exempt':
|
||||
result = 0.0
|
||||
|
||||
# Tables are found in https://www.tax.ny.gov/pdf/publications/withholding/nys50_t_nys.pdf
|
||||
# Table A - Combined deduction and exemption allowance (Step 1)
|
||||
deduction_exemption_table_single = {
|
||||
'weekly': (142.30, 161.55, 180.80, 200.05, 219.30, 238.55, 257.80, 277.05, 296.30, 315.55, 334.80),
|
||||
'bi-weekly': (284.60, 323.10, 361.60, 400.10, 438.60, 477.10, 515.60, 544.10, 592.60, 631.10, 669.60),
|
||||
'semi-monthly': (308.35, 350.0, 391.65, 433.30, 474.95, 516.60, 558.25, 599.90, 641.55, 683.20, 724.85),
|
||||
'monthly': (616.70, 700, 783.30, 866.60, 949.90, 1033.20, 1116.50, 1199.80, 1283.10, 1366.40, 1449.70),
|
||||
'annually': (7400, 8400, 9400, 10400, 11400, 12400, 13400, 14400, 15400, 16400, 17400),
|
||||
}
|
||||
|
||||
deduction_exemption_table_married = {
|
||||
'weekly': (152.90, 172.15, 191.40, 210.65, 229.90, 249.15, 268.40, 287.65, 306.90, 326.15, 345.40),
|
||||
'bi-weekly': (305.80, 344.30, 382.80, 421.30, 459.80, 498.30, 536.80, 575.30, 613.80, 652.30, 690.80),
|
||||
'semi-monthly': (331.25, 372.90, 414.55, 456.20, 497.85, 539.50, 581.15, 622.80, 664.45, 706.10, 747.75),
|
||||
'monthly': (662.50, 745.80, 829.10, 912.40, 995.70, 1079.00, 1162.30, 1245.60, 1328.90, 1412.20, 1495.50),
|
||||
'annually': (7950, 8950, 9950, 10950, 11950, 12950, 13950, 14950, 15950, 16950, 17950),
|
||||
}
|
||||
|
||||
# For greater than 10 exemptions, from tables B and C
|
||||
over_10_deduction_table = {
|
||||
'weekly': (142.30, 152.90, 19.25),
|
||||
'bi-weekly': (284.60, 305.80, 38.50),
|
||||
'semi-monthly': (308.35, 331.25, 41.65),
|
||||
'monthly': (616.70, 662.50, 83.30),
|
||||
'annual': (7400, 7950, 1000),
|
||||
}
|
||||
|
||||
if allowances > 10:
|
||||
if filing_status == 'single':
|
||||
wages -= over_10_deduction_table[schedule_pay][0] + over_10_deduction_table[schedule_pay][2] * allowances
|
||||
elif filing_status == 'married':
|
||||
wages -= over_10_deduction_table[schedule_pay][1] + over_10_deduction_table[schedule_pay][2] * allowances
|
||||
|
||||
else:
|
||||
if filing_status == 'single':
|
||||
wages -= deduction_exemption_table_single[schedule_pay][allowances]
|
||||
elif filing_status == 'married':
|
||||
wages -= deduction_exemption_table_married[schedule_pay][allowances]
|
||||
|
||||
# Tax Rate Tables
|
||||
#### SINGLE ####
|
||||
if filing_status == 'single':
|
||||
if schedule_pay == 'weekly':
|
||||
tax_rate_table = [
|
||||
(163, 0.0400, 0.0),
|
||||
(225, 0.0450, 6.54),
|
||||
(267, 0.0525, 9.31),
|
||||
(412, 0.0590, 11.54),
|
||||
(1551, 0.0633, 20.04),
|
||||
(1862, 0.0657, 92.17),
|
||||
(2070, 0.0758, 112.58),
|
||||
(3032, 0.0808, 128.38),
|
||||
(4142, 0.0707, 206.08),
|
||||
(5104, 0.0856, 284.60),
|
||||
(20722, 0.0735, 366.90),
|
||||
(21684, 0.5208, 1514.85),
|
||||
(float('inf'), 0.0962, 2015.62),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
tax_rate_table = [
|
||||
(327, 0.0400, 0.0),
|
||||
(450, 0.0450, 13.08),
|
||||
(535, 0.0525, 18.62),
|
||||
(823, 0.0590, 23.08),
|
||||
(3102, 0.0633, 40.08),
|
||||
(3723, 0.0657, 184.35),
|
||||
(4140, 0.0758, 225.15),
|
||||
(6063, 0.0808, 256.77),
|
||||
(8285, 0.0707, 412.15),
|
||||
(10208, 0.0856, 569.19),
|
||||
(41444, 0.0735, 733.81),
|
||||
(43367, 0.5208, 3029.69),
|
||||
(float('inf'), 0.0962, 4021.23),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
tax_rate_table = [
|
||||
(354, 0.0400, 0.0),
|
||||
(488, 0.0450, 14.17),
|
||||
(579, 0.0525, 20.17),
|
||||
(892, 0.0590, 25.00),
|
||||
(3360, 0.0633, 43.42),
|
||||
(4033, 0.0657, 199.71),
|
||||
(4485, 0.0758, 243.92),
|
||||
(6569, 0.0808, 278.17),
|
||||
(8975, 0.0707, 446.50),
|
||||
(11058, 0.0856, 616.63),
|
||||
(44898, 0.0735, 794.96),
|
||||
(46981, 0.5208, 3282.17),
|
||||
(float('inf'), 0.0962, 4367.17),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'monthly':
|
||||
tax_rate_table = [
|
||||
(708, 0.0400, 0.0),
|
||||
(975, 0.0450, 28.33),
|
||||
(1158, 0.0525, 40.33),
|
||||
(1783, 0.0590, 50.00),
|
||||
(6721, 0.0633, 86.83),
|
||||
(8067, 0.0657, 399.42),
|
||||
(8971, 0.0758, 487.83),
|
||||
(13138, 0.0808, 556.33),
|
||||
(17950, 0.0707, 893.00),
|
||||
(22117, 0.0856, 1233.25),
|
||||
(89796, 0.0735, 1589.92),
|
||||
(93963, 0.5208, 6564.33),
|
||||
(float('inf'), 0.0962, 8734.33),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'annually':
|
||||
tax_rate_table = [
|
||||
(8500, 0.0400, 0.0),
|
||||
(11700, 0.0450, 340.00),
|
||||
(13900, 0.0525, 484.00),
|
||||
(21400, 0.0590, 600.00),
|
||||
(80650, 0.0633, 1042.00),
|
||||
(96800, 0.0657, 4793.00),
|
||||
(107650, 0.0758, 5854.00),
|
||||
(157650, 0.0808, 6676.00),
|
||||
(215400, 0.0707, 10716.00),
|
||||
(265400, 0.0856, 14799.00),
|
||||
(1077550, 0.0735, 19079.00),
|
||||
(1127550, 0.5208, 78772.00),
|
||||
(float('inf'), 0.0962, 104812.00),
|
||||
]
|
||||
|
||||
#### MARRIED ####
|
||||
elif filing_status == 'married':
|
||||
if schedule_pay == 'weekly':
|
||||
tax_rate_table = [
|
||||
(163, 0.0400, 0.0),
|
||||
(225, 0.0450, 6.54),
|
||||
(267, 0.0525, 9.31),
|
||||
(412, 0.0590, 11.54),
|
||||
(1551, 0.0633, 20.04),
|
||||
(1862, 0.0657, 92.17),
|
||||
(2070, 0.0783, 112.58),
|
||||
(3032, 0.0833, 128.90),
|
||||
(4068, 0.0785, 209.00),
|
||||
(6215, 0.0707, 290.37),
|
||||
(7177, 0.0916, 442.17),
|
||||
(20722, 0.0735, 530.25),
|
||||
(41449, 0.0765, 1525.83),
|
||||
(42411, 0.9454, 3111.42),
|
||||
(float('inf'), 0.0962, 4020.46),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'bi-weekly':
|
||||
tax_rate_table = [
|
||||
(327, 0.0400, 0.0),
|
||||
(450, 0.0450, 13.08),
|
||||
(535, 0.0525, 18.62),
|
||||
(823, 0.0590, 23.08),
|
||||
(3102, 0.0633, 40.08),
|
||||
(3723, 0.0657, 184.35),
|
||||
(4140, 0.0783, 225.15),
|
||||
(6063, 0.0833, 257.81),
|
||||
(8137, 0.0785, 418.00),
|
||||
(12431, 0.0707, 580.73),
|
||||
(14354, 0.0916, 884.35),
|
||||
(41444, 0.0735, 1060.50),
|
||||
(82898, 0.0765, 3051.65),
|
||||
(84821, 0.9454, 6222.85),
|
||||
(float('inf'), 0.0962, 8040.92),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'semi-monthly':
|
||||
tax_rate_table = [
|
||||
(354, 0.0400, 0.0),
|
||||
(488, 0.0450, 14.17),
|
||||
(579, 0.0525, 20.17),
|
||||
(892, 0.0590, 25.00),
|
||||
(3360, 0.0633, 43.42),
|
||||
(4033, 0.0657, 199.71),
|
||||
(4485, 0.0783, 243.92),
|
||||
(6569, 0.0833, 279.29),
|
||||
(8815, 0.0785, 452.83),
|
||||
(13476, 0.0707, 629.13),
|
||||
(15550, 0.0916, 958.04),
|
||||
(44898, 0.0735, 1148.88),
|
||||
(89806, 0.0765, 3305.96),
|
||||
(91890, 0.9454, 6741.42),
|
||||
(float('inf'), 0.0962, 8711.00),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'monthly':
|
||||
tax_rate_table = [
|
||||
(708, 0.0400, 0.0),
|
||||
(975, 0.0450, 28.33),
|
||||
(1158, 0.0525, 40.33),
|
||||
(1783, 0.0590, 50.00),
|
||||
(6721, 0.0633, 86.83),
|
||||
(8067, 0.0657, 399.42),
|
||||
(8971, 0.0783, 487.83),
|
||||
(13138, 0.0833, 558.58),
|
||||
(17629, 0.0785, 905.67),
|
||||
(26933, 0.0707, 1258.25),
|
||||
(31100, 0.0916, 1916.08),
|
||||
(89796, 0.0735, 2297.75),
|
||||
(179613, 0.0765, 6611.92),
|
||||
(183779, 0.9454, 13482.83),
|
||||
(float('inf'), 0.0962, 17422.00),
|
||||
]
|
||||
|
||||
elif schedule_pay == 'annually':
|
||||
tax_rate_table = [
|
||||
(8500, 0.0400, 0.0),
|
||||
(11700, 0.0450, 340.00),
|
||||
(13900, 0.0525, 484.00),
|
||||
(21400, 0.0590, 600.00),
|
||||
(80650, 0.0633, 1042.00),
|
||||
(96800, 0.0657, 4793.00),
|
||||
(107650, 0.0783, 5854.00),
|
||||
(157650, 0.0833, 6703.00),
|
||||
(211550, 0.0785, 10868.00),
|
||||
(323200, 0.0707, 15099.00),
|
||||
(373200, 0.0916, 22993.00),
|
||||
(1077550, 0.0735, 27573.00),
|
||||
(2155350, 0.0765, 79343.00),
|
||||
(2205350, 0.9454, 161794.00),
|
||||
(float('inf'), 0.0962, 209064.00),
|
||||
]
|
||||
|
||||
over = 0.0
|
||||
tax = 0.0
|
||||
for row in tax_rate_table:
|
||||
if wages <= row[0]:
|
||||
tax = ((wages - over) * row[1]) + row[2]
|
||||
break
|
||||
over = row[0]
|
||||
|
||||
tax += additional_withholding
|
||||
result = -tax
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_nydor_withhold"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
57
l10n_us_ny_hr_payroll/hr_payroll.py
Executable file
57
l10n_us_ny_hr_payroll/hr_payroll.py
Executable file
@@ -0,0 +1,57 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class USNYHrContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
ny_it2104_allowances = fields.Integer(string="New York IT-2104 Allowances",
|
||||
default=0,
|
||||
help="Allowances claimed on line 1 of IT-2104")
|
||||
ny_additional_withholding = fields.Integer(string="Additional Withholding",
|
||||
default=0,
|
||||
help="Line 3 of IT-2104")
|
||||
ny_it2104_filing_status = fields.Selection([
|
||||
('exempt', 'Exempt'),
|
||||
('single', 'Single'),
|
||||
('married', 'Married'),
|
||||
], string='NY Filing Status', default='single')
|
||||
|
||||
@api.multi
|
||||
def ny_unemp_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'ny_unemp_rate_' + str(year)):
|
||||
return self.employee_id.company_id['ny_unemp_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New York.')
|
||||
|
||||
def ny_rsf_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'ny_rsf_rate_' + str(year)):
|
||||
return self.employee_id.company_id['ny_rsf_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New York.')
|
||||
|
||||
def ny_mctmt_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'ny_mctmt_rate_' + str(year)):
|
||||
return self.employee_id.company_id['ny_mctmt_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US New York.')
|
||||
|
||||
|
||||
class NYCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
# Unemployment Rate is default for New Employer.
|
||||
ny_unemp_rate_2018 = fields.Float(string="New York Unemployment Insurance Tax Rate 2018", default=3.925)
|
||||
ny_rsf_rate_2018 = fields.Float(string="New York Re-employment Service Fund Rate 2018", default=0.075)
|
||||
ny_mctmt_rate_2018 = fields.Float(string="New York Metropolitan Commuter Transportation Mobility Tax Rate 2018", default=0.0)
|
||||
37
l10n_us_ny_hr_payroll/hr_payroll_view.xml
Executable file
37
l10n_us_ny_hr_payroll/hr_payroll_view.xml
Executable file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="us_ny_view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority">64</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='currency_id']" position="after">
|
||||
<field name="ny_unemp_rate_2018"/>
|
||||
<field name="ny_rsf_rate_2018"/>
|
||||
<field name="ny_mctmt_rate_2018"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_contract_form_l10n_us_ny_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority">147</field>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//group[@name='state_filing']" position="inside">
|
||||
<group string="New York" name="ny">
|
||||
<field name="ny_it2104_filing_status" string="NY IT-2104 Filing Status"/>
|
||||
<field name="ny_it2104_allowances" string="NY IT-2104 Allowances"/>
|
||||
<field name="ny_additional_withholding" string="Additional Withholding"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
1
l10n_us_ny_hr_payroll/tests/__init__.py
Executable file
1
l10n_us_ny_hr_payroll/tests/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import test_us_ny_payslip_2018
|
||||
167
l10n_us_ny_hr_payroll/tests/test_us_ny_payslip_2018.py
Executable file
167
l10n_us_ny_hr_payroll/tests/test_us_ny_payslip_2018.py
Executable file
@@ -0,0 +1,167 @@
|
||||
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
|
||||
from odoo.addons.l10n_us_hr_payroll.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
|
||||
class TestUsNYPayslip(TestUsPayslip):
|
||||
###
|
||||
# Taxes and Rates
|
||||
###
|
||||
NY_UNEMP_MAX_WAGE = 11100
|
||||
|
||||
# Examples from http://www.edd.ca.gov/pdf_pub_ctr/18methb.pdf
|
||||
def test_single_example1(self):
|
||||
salary = 400
|
||||
schedule_pay = 'weekly'
|
||||
allowances = 3
|
||||
additional_withholding = 0
|
||||
|
||||
wh = -8.20
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ny_unemp_rate_2018 = 0.825
|
||||
employee.company_id.ny_rsf_rate_2018 = 0.075
|
||||
employee.company_id.ny_mctmt_rate_2018 = 0.0
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ny_hr_payroll.hr_payroll_salary_structure_us_ny_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ny_it2104_allowances = allowances
|
||||
contract.ny_additional_withholding = additional_withholding
|
||||
contract.ny_it2104_filing_status = 'single'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'weekly')
|
||||
|
||||
# tax rates
|
||||
ny_unemp = contract.ny_unemp_rate(2018) / -100.0
|
||||
ny_rsf = contract.ny_rsf_rate(2018) / -100.0
|
||||
ny_mctmt = contract.ny_mctmt_rate(2018) / -100.0
|
||||
|
||||
self._log('2018 New York tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['NY_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['NY_UNEMP'], cats['NY_UNEMP_WAGES'] * ny_unemp)
|
||||
self.assertPayrollEqual(cats['NY_RSF'], cats['NY_UNEMP_WAGES'] * ny_rsf)
|
||||
self.assertPayrollEqual(cats['NY_MCTMT'], cats['NY_UNEMP_WAGES'] * ny_mctmt)
|
||||
self.assertPayrollEqual(cats['NY_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
# Make a new payslip, this one will have maximums
|
||||
|
||||
remaining_ny_unemp_wages = self.NY_UNEMP_MAX_WAGE - salary if (self.NY_UNEMP_MAX_WAGE - 2 * salary < salary) \
|
||||
else salary
|
||||
|
||||
self._log('2018 New York tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['NY_UNEMP_WAGES'], remaining_ny_unemp_wages)
|
||||
self.assertPayrollEqual(cats['NY_UNEMP'], remaining_ny_unemp_wages * ny_unemp)
|
||||
|
||||
def test_single_example2(self):
|
||||
salary = 5000
|
||||
schedule_pay = 'semi-monthly'
|
||||
allowances = 3
|
||||
additional_withholding = 0
|
||||
|
||||
wh = -284.19
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ny_unemp_rate_2018 = 0.825
|
||||
employee.company_id.ny_rsf_rate_2018 = 0.075
|
||||
employee.company_id.ny_mctmt_rate_2018 = 0.0
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ny_hr_payroll.hr_payroll_salary_structure_us_ny_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ny_it2104_allowances = allowances
|
||||
contract.ny_additional_withholding = additional_withholding
|
||||
contract.ny_it2104_filing_status = 'married'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'semi-monthly')
|
||||
|
||||
# tax rates
|
||||
ny_unemp = contract.ny_unemp_rate(2018) / -100.0
|
||||
ny_rsf = contract.ny_rsf_rate(2018) / -100.0
|
||||
ny_mctmt = contract.ny_mctmt_rate(2018) / -100.0
|
||||
|
||||
self._log('2018 New York tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['NY_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['NY_UNEMP'], cats['NY_UNEMP_WAGES'] * ny_unemp)
|
||||
self.assertPayrollEqual(cats['NY_RSF'], cats['NY_UNEMP_WAGES'] * ny_rsf)
|
||||
self.assertPayrollEqual(cats['NY_MCTMT'], cats['NY_UNEMP_WAGES'] * ny_mctmt)
|
||||
self.assertPayrollEqual(cats['NY_WITHHOLD'], wh)
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
def test_single_example3(self):
|
||||
salary = 50000
|
||||
schedule_pay = 'monthly'
|
||||
allowances = 3
|
||||
additional_withholding = 0
|
||||
|
||||
wh = -3575.63
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.ny_unemp_rate_2018 = 0.825
|
||||
employee.company_id.ny_rsf_rate_2018 = 0.075
|
||||
employee.company_id.ny_mctmt_rate_2018 = 0.0
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ny_hr_payroll.hr_payroll_salary_structure_us_ny_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ny_it2104_allowances = allowances
|
||||
contract.ny_additional_withholding = additional_withholding
|
||||
contract.ny_it2104_filing_status = 'single'
|
||||
|
||||
self.assertEqual(contract.schedule_pay, 'monthly')
|
||||
|
||||
# tax rates
|
||||
ny_unemp = contract.ny_unemp_rate(2018) / -100.0
|
||||
ny_rsf = contract.ny_rsf_rate(2018) / -100.0
|
||||
ny_mctmt = contract.ny_mctmt_rate(2018) / -100.0
|
||||
|
||||
self._log('2018 New York tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['NY_WITHHOLD'], wh)
|
||||
|
||||
def test_tax_exempt(self):
|
||||
schedule_pay = 'monthly'
|
||||
|
||||
wh = 0.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
salary,
|
||||
struct_id=self.ref(
|
||||
'l10n_us_ny_hr_payroll.hr_payroll_salary_structure_us_ny_employee'),
|
||||
schedule_pay=schedule_pay)
|
||||
contract.ny_it2104_filing_status = 'exempt'
|
||||
|
||||
self.assertPayrollEqual(cats['NY_WITHHOLD'], wh)
|
||||
42
l10n_us_oh_hr_payroll/README.rst
Normal file
42
l10n_us_oh_hr_payroll/README.rst
Normal file
@@ -0,0 +1,42 @@
|
||||
*******************************
|
||||
Hibou - US Payroll - Ohio State
|
||||
*******************************
|
||||
|
||||
Calculations and contribution registers for Ohio State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Ohio Department of Revenue partner
|
||||
* Contribution Registers for:
|
||||
* Ohio Department of Revenue - Unemployment
|
||||
* Ohio Department of Revenue - Income Tax withholding
|
||||
* Contract level Ohio Withholding Allowance
|
||||
* Company level Ohio Unemployment Rate
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41481725-e1cbd3c4-7087-11e8-8bf7-84843bb2f943.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA Ohio Employee added to Contract Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41481743-f1eceb4e-7087-11e8-8d09-dd45551a3fa4.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* Ohio Income Withholding
|
||||
* Ohio Unemployment - Wages
|
||||
* Ohio Unemployment
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
@@ -5,6 +5,7 @@ class USOHHrContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
oh_income_allowances = fields.Integer(string='Ohio Income Allowances', default=0)
|
||||
oh_additional_withholding = fields.Float(string="Additional Withholding", default=0.0)
|
||||
|
||||
@api.multi
|
||||
def oh_unemp_rate(self, year):
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<xpath expr="//group[@name='state_filing']" position="inside">
|
||||
<group string="Ohio" name="oh">
|
||||
<field name="oh_income_allowances" string="Allowances"/>
|
||||
<field name="oh_additional_withholding" string="Additional Withholding"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
|
||||
45
l10n_us_pa_hr_payroll/README.rst
Normal file
45
l10n_us_pa_hr_payroll/README.rst
Normal file
@@ -0,0 +1,45 @@
|
||||
***************************************
|
||||
Hibou - US Payroll - Pennsylvania State
|
||||
***************************************
|
||||
|
||||
Calculations and contribution registers for Pennsylvania State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Partner for Pennsylvania Department of Revenue
|
||||
* New Contribution Registers for:
|
||||
* Pennsylvania Department of Revenue - Unemployment Insurance
|
||||
* Pennsylvania Department of Revenue - State Income Tax
|
||||
* Contract level Pennsylvania Unemployment Rate
|
||||
* Contract level Pennsylvania State Income Tax Rate
|
||||
* Company level Pennsylvania Unemployment Rate
|
||||
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41480481-7e9c9b16-7083-11e8-83e0-25d8f37fb2c2.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA Pennsylvania Employee Added to Contract Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41480499-8a2d88e6-7083-11e8-88a0-2811356bca34.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* Pennsylvania Unemployment - Wages
|
||||
* Pennsylvania Income Withholding
|
||||
* Pennsylvania Unemployment - Employee
|
||||
* Pennsylvania Unemployment - Employer
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
1
l10n_us_pa_hr_payroll/__init__.py
Normal file
1
l10n_us_pa_hr_payroll/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import hr_payroll
|
||||
28
l10n_us_pa_hr_payroll/__manifest__.py
Executable file
28
l10n_us_pa_hr_payroll/__manifest__.py
Executable file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
'name': 'USA - Pennsylvania - Payroll',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Localization',
|
||||
'depends': ['l10n_us_hr_payroll'],
|
||||
'version': '11.0.2018.0.0',
|
||||
'description': """
|
||||
USA::Pennsylvania Payroll Rules.
|
||||
================================
|
||||
|
||||
* Partner for Pennsylvania Department of Revenue
|
||||
* Contribution register for Pennsylvania Department of Revenue - Unemployment Insurance
|
||||
* Contribution register Pennsylvania Department of Revenue - State Income Tax
|
||||
* Contract level Pennsylvania Unemployment Rate
|
||||
* Company level Pennsylvania Unemployement Rate
|
||||
""",
|
||||
|
||||
'auto_install': False,
|
||||
'website': 'https://hibou.io/',
|
||||
'data': [
|
||||
'hr_payroll_view.xml',
|
||||
'data/base.xml',
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
}
|
||||
66
l10n_us_pa_hr_payroll/data/base.xml
Executable file
66
l10n_us_pa_hr_payroll/data/base.xml
Executable file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- CONTRIBUTION REGISTERS -->
|
||||
<record id="res_partner_pador_unemp_employee" model="res.partner">
|
||||
<field name="name">Pennsylvania Department of Revenue - Unemployment Tax(Employee)</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_pador_unemp_company" model="res.partner">
|
||||
<field name="name">Pennsylvania Department of Revenue - Unemployment Tax(Employer)</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_pador_withhold" model="res.partner">
|
||||
<field name="name">Pennsylvania Department of Revenue - Income Tax</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_pador_unemp_employee" model="hr.contribution.register">
|
||||
<field name="name">Pennsylvania Unemployment(Employee)</field>
|
||||
<field name="note">Pennsylvania Department of Revenue - Unemployment(Employee)</field>
|
||||
<field name="partner_id" ref="res_partner_pador_unemp_employee"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_pador_unemp_company" model="hr.contribution.register">
|
||||
<field name="name">Pennsylvania Unemployment(Employer)</field>
|
||||
<field name="note">Pennsylvania Department of Revenue - Unemployment(Employee)</field>
|
||||
<field name="partner_id" ref="res_partner_pador_unemp_company"/>
|
||||
</record>
|
||||
|
||||
<record id="contrib_register_pador_withhold" model="hr.contribution.register">
|
||||
<field name="name">Pennsylvania Income Tax Withholding</field>
|
||||
<field name="note">Pennsylvania Department of Revenue - Income Tax</field>
|
||||
<field name="partner_id" ref="res_partner_pador_withhold"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- HR SALARY RULE CATEGORIES-->
|
||||
<record id="hr_payroll_pa_unemp_wages" model="hr.salary.rule.category">
|
||||
<field name="name">Pennsylvania Unemployment - Wages</field>
|
||||
<field name="code">PA_UNEMP_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_pa_unemp_employee" model="hr.salary.rule.category">
|
||||
<field name="name">Pennsylvania Unemployment(Employee)</field>
|
||||
<field name="code">PA_UNEMP_EMPLOYEE</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_pa_unemp_company" model="hr.salary.rule.category">
|
||||
<field name="name">Pennsylvania Unemployment(Employer)</field>
|
||||
<field name="code">PA_UNEMP_COMPANY</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_pa_withhold" model="hr.salary.rule.category">
|
||||
<field name="name">Pennsylvania Income Withholding</field>
|
||||
<field name="code">PA_WITHHOLD</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
20
l10n_us_pa_hr_payroll/data/final.xml
Executable file
20
l10n_us_pa_hr_payroll/data/final.xml
Executable file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR PAYROLL STRUCTURE -->
|
||||
<record id="hr_payroll_salary_structure_us_pa_employee" model="hr.payroll.structure">
|
||||
<field name="code">US_PA_EMP</field>
|
||||
<field name="name">USA Pennsylvania Employee</field>
|
||||
<field eval="[(6, 0, [
|
||||
ref('hr_payroll_rules_pa_unemp_wages_2018'),
|
||||
ref('hr_payroll_rules_pa_unemp_employee_2018'),
|
||||
ref('hr_payroll_rules_pa_unemp_company_2018'),
|
||||
ref('hr_payroll_rules_pa_inc_withhold_2018'),
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="parent_id" ref="l10n_us_hr_payroll.hr_payroll_salary_structure_us_employee"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
84
l10n_us_pa_hr_payroll/data/rules_2018.xml
Executable file
84
l10n_us_pa_hr_payroll/data/rules_2018.xml
Executable file
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR SALARY RULES-->
|
||||
<record id="hr_payroll_rules_pa_unemp_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_pa_unemp_wages"/>
|
||||
<field name="name">Pennsylvania Unemployment - Wages (2018)</field>
|
||||
<field name="code">PA_UNEMP_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('PA_UNEMP_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 10000.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_pa_unemp_employee_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_pa_unemp_employee"/>
|
||||
<field name="name">Pennsylvania Unemployment - Employee(2018)</field>
|
||||
<field name="code">PA_UNEMP_EMPLOYEE_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.pa_unemp_employee_rate(2018)
|
||||
result = categories.PA_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_pador_unemp_employee"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_pa_unemp_company_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_pa_unemp_company"/>
|
||||
<field name="name">Pennsylvania Unemployment - Company(2018)</field>
|
||||
<field name="code">PA_UNEMP_COMPANY_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.pa_unemp_company_rate(2018)
|
||||
result = categories.PA_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_pador_unemp_company"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rules_pa_inc_withhold_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="145"/>
|
||||
<field name="category_id" ref="hr_payroll_pa_withhold"/>
|
||||
<field name="name">Pennsylvania Income Withholding</field>
|
||||
<field name="code">PA_INC_WITHHOLD_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
wages = categories.GROSS
|
||||
income_tax_rate = contract.pa_withhold_rate(2018) / -100.0
|
||||
result = wages * income_tax_rate
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_pador_withhold"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
49
l10n_us_pa_hr_payroll/hr_payroll.py
Executable file
49
l10n_us_pa_hr_payroll/hr_payroll.py
Executable file
@@ -0,0 +1,49 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class USPAHrContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
pa_additional_withholding = fields.Integer(string="Additional Withholding",
|
||||
default=0)
|
||||
|
||||
@api.multi
|
||||
def pa_unemp_company_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'pa_unemp_company_rate_' + str(year)):
|
||||
return self.employee_id.company_id['pa_unemp_company_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US Pennsylvania')
|
||||
|
||||
def pa_unemp_employee_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'pa_unemp_employee_rate_' + str(year)):
|
||||
return self.employee_id.company_id['pa_unemp_employee_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US Pennsylvania')
|
||||
|
||||
def pa_withhold_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'pa_withhold_rate_' + str(year)):
|
||||
return self.employee_id.company_id['pa_withhold_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US Pennsylvania')
|
||||
|
||||
|
||||
class PACompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
# Company Unemployment rate is default rate for new employers.
|
||||
pa_unemp_company_rate_2018 = fields.Float(string="Pennsylvania Unemployment Rate 2018", default=3.6890)
|
||||
pa_unemp_employee_rate_2018 = fields.Float(string="Pennsylvania Unemployment Rate 2018", default=0.06)
|
||||
pa_withhold_rate_2018 = fields.Float(string="Pennsylvania Income Tax Rate 2018", default=3.07)
|
||||
|
||||
35
l10n_us_pa_hr_payroll/hr_payroll_view.xml
Executable file
35
l10n_us_pa_hr_payroll/hr_payroll_view.xml
Executable file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="us_pa_view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority">64</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='currency_id']" position="after">
|
||||
<field name="pa_unemp_company_rate_2018"/>
|
||||
<field name="pa_unemp_employee_rate_2018"/>
|
||||
<field name="pa_withhold_rate_2018"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_contract_form_l10n_us_pa_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority">148</field>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//group[@name='state_filing']" position="inside">
|
||||
<group string="Pennsylvania" name="pa">
|
||||
<field name="pa_additional_withholding"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
1
l10n_us_pa_hr_payroll/tests/__init__.py
Executable file
1
l10n_us_pa_hr_payroll/tests/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import test_us_pa_payslip_2018
|
||||
39
l10n_us_pa_hr_payroll/tests/test_us_pa_payslip_2018.py
Executable file
39
l10n_us_pa_hr_payroll/tests/test_us_pa_payslip_2018.py
Executable file
@@ -0,0 +1,39 @@
|
||||
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
|
||||
from odoo.addons.l10n_us_hr_payroll.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
|
||||
class TestUsPAPayslip(TestUsPayslip):
|
||||
###
|
||||
# Taxes and Rates
|
||||
###
|
||||
PA_UNEMP_MAX_WAGE = 10000.0
|
||||
|
||||
def test_2018_taxes(self):
|
||||
self.debug = True
|
||||
salary = 4166.67
|
||||
wh = -127.92
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.pa_unemp_employee_rate_2018 = 0.06~
|
||||
employee.company_id.pa_unemp_company_rate_2018 = 3.6785
|
||||
employee.company_id.pa_withhold_rate_2018 = 3.07
|
||||
|
||||
contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_pa_hr_payroll.hr_payroll_salary_structure_us_pa_employee'))
|
||||
|
||||
# tax rates
|
||||
pa_unemp_employee = contract.pa_unemp_employee_rate(2018) / -100.0
|
||||
pa_unemp_company = contract.pa_unemp_company_rate(2018) / -100.0
|
||||
|
||||
|
||||
self._log('2018 Pennsylvania tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
payslip.onchange_contract()
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['PA_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['PA_UNEMP_EMPLOYEE'], cats['PA_UNEMP_WAGES'] * pa_unemp_employee)
|
||||
self.assertPayrollEqual(cats['PA_UNEMP_COMPANY'], cats['PA_UNEMP_WAGES'] * pa_unemp_company)
|
||||
self.assertPayrollEqual(cats['PA_WITHHOLD'], wh)
|
||||
|
||||
43
l10n_us_tx_hr_payroll/README.rst
Normal file
43
l10n_us_tx_hr_payroll/README.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
***************************************
|
||||
Hibou - US Payroll - Texas State
|
||||
***************************************
|
||||
|
||||
Calculations and contribution registers for Texas State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* New Partner and Contribution Register for Texas Workforce Commission
|
||||
* Company level Texas Unemployment Rate
|
||||
* Company level Texas Obligation Assessment Rate
|
||||
* Company level Texas Employment & Training Investment Assessment
|
||||
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41480987-2a50aa14-7085-11e8-99d4-24773869d2e3.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA Texas Employee Added to Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41480994-330626f2-7085-11e8-8ca0-d41d621182ff.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Payslip Categories for:
|
||||
|
||||
* Texas Unemployment - Wages
|
||||
* Texas Unemployment
|
||||
* Texas Obligation Assessment
|
||||
* Texas Employment and Training Investment Assessment
|
||||
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
1
l10n_us_tx_hr_payroll/__init__.py
Executable file
1
l10n_us_tx_hr_payroll/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import us_tx_hr_payroll
|
||||
29
l10n_us_tx_hr_payroll/__manifest__.py
Executable file
29
l10n_us_tx_hr_payroll/__manifest__.py
Executable file
@@ -0,0 +1,29 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
{
|
||||
'name': 'USA - Texas - Payroll',
|
||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Localization',
|
||||
'depends': ['l10n_us_hr_payroll'],
|
||||
'version': '11.0.2018.0.0',
|
||||
'description': """
|
||||
USA::Texas Payroll Rules.
|
||||
=========================
|
||||
|
||||
* Texas Workforce Commission partner
|
||||
* Contribution register for Texas Workforce Commission
|
||||
* Company level Texas Umemployment Rate
|
||||
* Company level Texas Obligation Assessment Rate
|
||||
* Company level Texas Employment & Training Investment Assessment
|
||||
""",
|
||||
|
||||
'auto_install': False,
|
||||
'website': 'https://hibou.io/',
|
||||
'data':[
|
||||
'us_tx_hr_payroll_view.xml',
|
||||
'data/base.xml',
|
||||
'data/rules_2018.xml',
|
||||
'data/final.xml',
|
||||
],
|
||||
'installable': True
|
||||
}
|
||||
53
l10n_us_tx_hr_payroll/data/base.xml
Executable file
53
l10n_us_tx_hr_payroll/data/base.xml
Executable file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- CONTRIBUTION REGISTERS -->
|
||||
<record id="res_partner_txdor" model="res.partner">
|
||||
<field name="name">Texas Workforce Commission</field>
|
||||
<field name="supplier">1</field>
|
||||
<field eval="0" name="customer"/>
|
||||
</record>
|
||||
<record id="contrib_register_txdor" model="hr.contribution.register">
|
||||
<field name="name">Texas Unemployment</field>
|
||||
<field name="note">Texas Workforce Commission - Unemployment</field>
|
||||
<field name="partner_id" ref="res_partner_txdor"/>
|
||||
</record>
|
||||
<record id="contrib_register_txdor" model="hr.contribution.register">
|
||||
<field name="name">Texas Obligation Assessment</field>
|
||||
<field name="note">Texas Workforce Commission - Obligation Assessment</field>
|
||||
<field name="partner_id" ref="res_partner_txdor"/>
|
||||
</record>
|
||||
<record id="contrib_register_txdor" model="hr.contribution.register">
|
||||
<field name="name">Texas Employment and Training Investment Assessment</field>
|
||||
<field name="note">Texas Workforce Commission - Employment and Trainging Investment Assessment</field>
|
||||
<field name="partner_id" ref="res_partner_txdor"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- HR SALARY RULE CATEGORIES-->
|
||||
<record id="hr_payroll_tx_unemp_wages" model="hr.salary.rule.category">
|
||||
<field name="name">Texas Unemployment - Wages</field>
|
||||
<field name="code">TX_UNEMP_WAGES</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_tx_unemp" model="hr.salary.rule.category">
|
||||
<field name="name">Texas Unemployment</field>
|
||||
<field name="code">TX_UNEMP</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_tx_oa" model="hr.salary.rule.category">
|
||||
<field name="name">Texas Obligation Assessment</field>
|
||||
<field name="code">TX_OA</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_tx_etia" model="hr.salary.rule.category">
|
||||
<field name="name">Texas Employment and Training Investment Assessment</field>
|
||||
<field name="code">TX_ETIA</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
20
l10n_us_tx_hr_payroll/data/final.xml
Executable file
20
l10n_us_tx_hr_payroll/data/final.xml
Executable file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR PAYROLL STRUCTURE -->
|
||||
<record id="hr_payroll_salary_structure_us_tx_employee" model="hr.payroll.structure">
|
||||
<field name="code">US_TX_EMP</field>
|
||||
<field name="name">USA Texas Employee</field>
|
||||
<field eval="[(6, 0, [
|
||||
ref('hr_payroll_rules_tx_unemp_wages_2018'),
|
||||
ref('hr_payroll_rules_tx_unemp_2018'),
|
||||
ref('hr_payroll_rules_tx_oa_2018'),
|
||||
ref('hr_payroll_rules_tx_etia_2018'),
|
||||
])]" name="rule_ids"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="parent_id" ref="l10n_us_hr_payroll.hr_payroll_salary_structure_us_employee"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
87
l10n_us_tx_hr_payroll/data/rules_2018.xml
Executable file
87
l10n_us_tx_hr_payroll/data/rules_2018.xml
Executable file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- HR SALARY RULES-->
|
||||
<record id="hr_payroll_rules_tx_unemp_wages_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="423"/>
|
||||
<field name="category_id" ref="hr_payroll_tx_unemp_wages"/>
|
||||
<field name="name">Texas Unemployment - Wages (2018)</field>
|
||||
<field name="code">TX_UNEMP_WAGES_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
###
|
||||
ytd = payslip.sum('TX_UNEMP_WAGES_2018', '2018-01-01', '2019-01-01')
|
||||
ytd += contract.external_wages
|
||||
remaining = 9000.0 - ytd
|
||||
if remaining <= 0.0:
|
||||
result = 0
|
||||
elif remaining < categories.GROSS:
|
||||
result = remaining
|
||||
else:
|
||||
result = categories.GROSS
|
||||
</field>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_tx_unemp_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_tx_unemp"/>
|
||||
<field name="name">Texas Unemployment (2018)</field>
|
||||
<field name="code">TX_UNEMP_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.tx_unemp_rate(2018)
|
||||
result = categories.TX_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_txdor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_tx_oa_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_tx_oa"/>
|
||||
<field name="name">Texas Obligation Assessment (2018)</field>
|
||||
<field name="code">TX_OA_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.tx_oa_rate(2018)
|
||||
result = categories.TX_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_txdor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
<record id="hr_payroll_rules_tx_etia_2018" model="hr.salary.rule">
|
||||
<field name="sequence" eval="443"/>
|
||||
<field name="category_id" ref="hr_payroll_tx_etia"/>
|
||||
<field name="name">Texas Employment and Training Investment Assessment (2018)</field>
|
||||
<field name="code">TX_ETIA_2018</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (payslip.date_to[:4] == '2018')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
result_rate = -contract.tx_etia_rate(2018)
|
||||
result = categories.TX_UNEMP_WAGES
|
||||
|
||||
# result_rate of 0 implies 100% due to bug
|
||||
if result_rate == 0.0:
|
||||
result = 0.0
|
||||
</field>
|
||||
<field name="register_id" ref="contrib_register_txdor"/>
|
||||
<field name="appears_on_payslip" eval="False"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
1
l10n_us_tx_hr_payroll/tests/__init__.py
Executable file
1
l10n_us_tx_hr_payroll/tests/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import test_us_tx_payslip_2018
|
||||
161
l10n_us_tx_hr_payroll/tests/test_us_tx_payslip_2018.py
Executable file
161
l10n_us_tx_hr_payroll/tests/test_us_tx_payslip_2018.py
Executable file
@@ -0,0 +1,161 @@
|
||||
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
|
||||
from odoo.addons.l10n_us_hr_payroll.l10n_us_hr_payroll import USHrContract
|
||||
|
||||
|
||||
class TestUsTXPayslip(TestUsPayslip):
|
||||
###
|
||||
# 2018 Taxes and Rates
|
||||
###
|
||||
TX_UNEMP_MAX_WAGE = 9000.0
|
||||
|
||||
def test_2018_taxes(self):
|
||||
salary = 5000.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.tx_unemp_rate_2018 = 2.7
|
||||
employee.company_id.tx_oa_rate_2018 = 0.0
|
||||
employee.company_id.tx_etia_rate_2018 = 0.1
|
||||
|
||||
contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_tx_hr_payroll.hr_payroll_salary_structure_us_tx_employee'))
|
||||
|
||||
# tax rates
|
||||
tx_unemp = contract.tx_unemp_rate(2018) / -100.0
|
||||
tx_oa = contract.tx_oa_rate(2018) / -100.00
|
||||
tx_etia = contract.tx_etia_rate(2018) / -100.00
|
||||
|
||||
self._log('2018 Texas tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['TX_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['TX_UNEMP'], cats['TX_UNEMP_WAGES'] * tx_unemp)
|
||||
self.assertPayrollEqual(cats['TX_OA'], cats['TX_UNEMP_WAGES'] * tx_oa)
|
||||
self.assertPayrollEqual(cats['TX_ETIA'], cats['TX_UNEMP_WAGES'] * 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('2018 Texas tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['TX_UNEMP_WAGES'], remaining_tx_unemp_wages)
|
||||
self.assertPayrollEqual(cats['TX_UNEMP'], remaining_tx_unemp_wages * tx_unemp)
|
||||
|
||||
def test_2018_taxes_with_external(self):
|
||||
salary = 5000.0
|
||||
external_wages = 6000.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.tx_unemp_rate_2018 = 2.7
|
||||
employee.company_id.tx_oa_rate_2018 = 0.0
|
||||
employee.company_id.tx_etia_rate_2018 = 0.1
|
||||
|
||||
contract = self._createContract(employee, salary, external_wages=external_wages,
|
||||
struct_id=self.ref('l10n_us_tx_hr_payroll.hr_payroll_salary_structure_us_tx_employee'))
|
||||
|
||||
# tax rates
|
||||
tx_unemp = contract.tx_unemp_rate(2018) / -100.0
|
||||
tx_oa = contract.tx_oa_rate(2018) / -100.00
|
||||
tx_etia = contract.tx_etia_rate(2018) / -100.00
|
||||
|
||||
self._log('2018 Texas_external tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['TX_UNEMP_WAGES'], self.TX_UNEMP_MAX_WAGE - external_wages)
|
||||
self.assertPayrollEqual(cats['TX_UNEMP'], cats['TX_UNEMP_WAGES'] * tx_unemp)
|
||||
self.assertPayrollEqual(cats['TX_OA'], cats['TX_UNEMP_WAGES'] * tx_oa)
|
||||
self.assertPayrollEqual(cats['TX_ETIA'], cats['TX_UNEMP_WAGES'] * tx_etia)
|
||||
|
||||
def test_2018_taxes_with_state_exempt(self):
|
||||
salary = 5000.0
|
||||
external_wages = 6000.0
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.tx_unemp_rate_2018 = 2.7
|
||||
employee.company_id.tx_oa_rate_2018 = 0.0
|
||||
employee.company_id.tx_etia_rate_2018 = 0.1
|
||||
|
||||
contract = self._createContract(employee, salary, external_wages=external_wages, struct_id=self.ref(
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_salary_structure_us_tx_employee'), futa_type=USHrContract.FUTA_TYPE_BASIC)
|
||||
|
||||
# tax rates
|
||||
tx_unemp = contract.tx_unemp_rate(2018) / -100.0
|
||||
tx_oa = contract.tx_oa_rate(2018) / -100.00
|
||||
tx_etia = contract.tx_etia_rate(2018) / -100.00
|
||||
|
||||
self.assertPayrollEqual(tx_unemp, 0.0)
|
||||
|
||||
self._log('2018 Texas_external tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['TX_UNEMP_WAGES'], self.TX_UNEMP_MAX_WAGE - external_wages)
|
||||
self.assertPayrollEqual(cats['TX_UNEMP'], cats['TX_UNEMP_WAGES'] * tx_unemp)
|
||||
self.assertPayrollEqual(cats['TX_OA'], cats['TX_UNEMP_WAGES'] * tx_oa)
|
||||
self.assertPayrollEqual(cats['TX_ETIA'], cats['TX_UNEMP_WAGES'] * tx_etia)
|
||||
|
||||
def test_payslip_example(self):
|
||||
salary = 2916.67
|
||||
|
||||
employee = self._createEmployee()
|
||||
employee.company_id.tx_unemp_rate_2018 = 2.7
|
||||
employee.company_id.tx_oa_rate_2018 = 0.0
|
||||
employee.company_id.tx_etia_rate_2018 = 0.1
|
||||
|
||||
contract = self._createContract(employee, salary, struct_id=self.ref(
|
||||
'l10n_us_tx_hr_payroll.hr_payroll_salary_structure_us_tx_employee'))
|
||||
contract.w4_allowances = 2
|
||||
contract.w4_filing_status = 'single'
|
||||
|
||||
# tax rates
|
||||
tx_unemp = contract.tx_unemp_rate(2018) / -100.0
|
||||
tx_oa = contract.tx_oa_rate(2018) / -100.00
|
||||
tx_etia = contract.tx_etia_rate(2018) / -100.00
|
||||
|
||||
self._log('2018 Texas tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-01-01', '2018-01-31')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['TX_UNEMP_WAGES'], salary)
|
||||
self.assertPayrollEqual(cats['TX_UNEMP'], cats['TX_UNEMP_WAGES'] * tx_unemp)
|
||||
self.assertPayrollEqual(cats['TX_OA'], cats['TX_UNEMP_WAGES'] * tx_oa)
|
||||
self.assertPayrollEqual(cats['TX_ETIA'], cats['TX_UNEMP_WAGES'] * 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('2018 Texas tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2018-02-01', '2018-02-28')
|
||||
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
|
||||
self.assertPayrollEqual(cats['TX_UNEMP_WAGES'], remaining_tx_unemp_wages)
|
||||
self.assertPayrollEqual(cats['TX_UNEMP'], remaining_tx_unemp_wages * tx_unemp)
|
||||
|
||||
44
l10n_us_tx_hr_payroll/us_tx_hr_payroll.py
Executable file
44
l10n_us_tx_hr_payroll/us_tx_hr_payroll.py
Executable file
@@ -0,0 +1,44 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class USTXHrContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
@api.multi
|
||||
def tx_unemp_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'tx_unemp_rate_' + str(year)):
|
||||
return self.employee_id.company_id['tx_unemp_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US Texas.')
|
||||
|
||||
def tx_oa_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'tx_oa_rate_' + str(year)):
|
||||
return self.employee_id.company_id['tx_oa_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US Texas.')
|
||||
|
||||
def tx_etia_rate(self, year):
|
||||
self.ensure_one()
|
||||
if self.futa_type == self.FUTA_TYPE_BASIC:
|
||||
return 0.0
|
||||
|
||||
if hasattr(self.employee_id.company_id, 'tx_etia_rate_' + str(year)):
|
||||
return self.employee_id.company_id['tx_etia_rate_' + str(year)]
|
||||
|
||||
raise NotImplemented('Year (' + str(year) + ') Not implemented for US Texas.')
|
||||
|
||||
|
||||
class TXCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
tx_unemp_rate_2018 = fields.Float(string="Texas Unemployment Rate 2018", default=2.7)
|
||||
tx_oa_rate_2018 = fields.Float(strimg="Texas Obligation Assessment Rate 2018", default=0.0)
|
||||
tx_etia_rate_2018 = fields.Float(string="Texas Employment & Training Investment Assessment Rate", default=0.1)
|
||||
35
l10n_us_tx_hr_payroll/us_tx_hr_payroll_view.xml
Executable file
35
l10n_us_tx_hr_payroll/us_tx_hr_payroll_view.xml
Executable file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="us_tx_view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='currency_id']" position="after">
|
||||
<field name="tx_unemp_rate_2018"/>
|
||||
<field name="tx_oa_rate_2018"/>
|
||||
<field name="tx_etia_rate_2018"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_contract_form_l10n_us_tx_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority">110</field>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//group[@name='state_filing']" position="inside">
|
||||
<group string="Texas" name="tx">
|
||||
<p>No additional fields.</p>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
41
l10n_us_va_hr_payroll/README.rst
Normal file
41
l10n_us_va_hr_payroll/README.rst
Normal file
@@ -0,0 +1,41 @@
|
||||
***********************************
|
||||
Hibou - US Payroll - Virginia State
|
||||
***********************************
|
||||
|
||||
Calculations and contribution registers for Virginia State Payroll.
|
||||
|
||||
For more information and add-ons, visit `Hibou.io <https://hibou.io/>`_.
|
||||
|
||||
=============
|
||||
Main Features
|
||||
=============
|
||||
|
||||
* Contribution Registers and Partners for:
|
||||
* Virginia Department of Taxation - Unemployment
|
||||
* Virginia Department of Taxation Income Tax Withholding
|
||||
* Contract level Virginia Exemptions
|
||||
* Company level Virginia Unemployment Rate
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41482220-c4c6b6de-7089-11e8-8c32-50d9c32ba05b.png
|
||||
:alt: 'Employee Contract Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
USA Virginia Employee added to Contract Salary Structure Menu
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/15882954/41482325-19f4cc0e-708a-11e8-8c68-c9b56ac7302f.png
|
||||
:alt: 'Computed Pay Slip Detail'
|
||||
:width: 988
|
||||
:align: left
|
||||
|
||||
New Pay slip Categories for:
|
||||
|
||||
* Virginia Income Withholding
|
||||
* Virginia Unemployment - Wages
|
||||
* Virginia Unemployment
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
Please see `LICENSE <https://github.com/hibou-io/hibou-odoo-suite/blob/master/LICENSE>`_.
|
||||
Copyright Hibou Corp. 2018
|
||||
Reference in New Issue
Block a user