mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
Initial commit of l10n_us_ny_hr_payroll for 11.0.
This commit is contained in:
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)
|
||||
Reference in New Issue
Block a user