From b421ecae3eb6efe8bf25d92065ef80a51067ddb2 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Tue, 7 May 2019 16:26:15 -0700 Subject: [PATCH 1/5] FIX `l10n_us_nj_hr_payroll` Rates for 2019 and improved test coverage. --- l10n_us_nj_hr_payroll/data/base.xml | 6 + l10n_us_nj_hr_payroll/data/final.xml | 1 + l10n_us_nj_hr_payroll/data/rates.xml | 62 + l10n_us_nj_hr_payroll/data/rules.xml | 1446 ++++++++++++----- l10n_us_nj_hr_payroll/tests/__init__.py | 1 + .../tests/test_us_nj_payslip_2019.py | 179 ++ 6 files changed, 1246 insertions(+), 449 deletions(-) create mode 100755 l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py diff --git a/l10n_us_nj_hr_payroll/data/base.xml b/l10n_us_nj_hr_payroll/data/base.xml index 4d8da28b..d9c71493 100755 --- a/l10n_us_nj_hr_payroll/data/base.xml +++ b/l10n_us_nj_hr_payroll/data/base.xml @@ -145,6 +145,12 @@ + + ER: US-NJ Workforce Development + ER_US_NJ_WF + + + EE: US-NJ Income Withholding EE_US_NJ_INC_WITHHOLD diff --git a/l10n_us_nj_hr_payroll/data/final.xml b/l10n_us_nj_hr_payroll/data/final.xml index 568bd523..58716a41 100755 --- a/l10n_us_nj_hr_payroll/data/final.xml +++ b/l10n_us_nj_hr_payroll/data/final.xml @@ -18,6 +18,7 @@ ref('hr_payroll_rules_nj_fli_2018'), ref('hr_payroll_rules_nj_wf_wages_2018'), ref('hr_payroll_rules_nj_wf_2018'), + ref('hr_payroll_rules_nj_wf_er'), ])]" name="rule_ids"/> diff --git a/l10n_us_nj_hr_payroll/data/rates.xml b/l10n_us_nj_hr_payroll/data/rates.xml index c8173c3d..315bbe5a 100644 --- a/l10n_us_nj_hr_payroll/data/rates.xml +++ b/l10n_us_nj_hr_payroll/data/rates.xml @@ -52,5 +52,67 @@ 0.0425 2018-01-01 + + + + + + US NJ Unemployment (Company) + ER_US_NJ_UNEMP + 2.6825 + 2019-01-01 + + + + US NJ Unemployment (Employee) + EE_US_NJ_UNEMP + 0.3825 + 2019-01-01 + + + + + US NJ State Disability Insurance (Company) + ER_US_NJ_SDI + 0.5000 + 2019-01-01 + + + + US NJ State Disability Insurance (Employee) + EE_US_NJ_SDI + 0.17 + 2019-01-01 + + + + + US NJ Workforce Development (Company) + ER_US_NJ_WF + 0.1175 + 2019-01-01 + + + US NJ Workforce Development (Employee) + EE_US_NJ_WF + 0.0425 + 2019-01-01 + + + + US NJ Family Leave Insurance (Company) + ER_US_NJ_FLI + 0.00 + 2019-01-01 + + + + US NJ Family Leave Insurance (Employee) + EE_US_NJ_FLI + 0.08 + 2019-01-01 + + + \ No newline at end of file diff --git a/l10n_us_nj_hr_payroll/data/rules.xml b/l10n_us_nj_hr_payroll/data/rules.xml index 7a9e7c83..ed531d8b 100755 --- a/l10n_us_nj_hr_payroll/data/rules.xml +++ b/l10n_us_nj_hr_payroll/data/rules.xml @@ -5,7 +5,7 @@ - + Wage: US-NJ Unemployment Insurance WAGE_US_NJ_UNEMP @@ -18,11 +18,14 @@ year = payslip.dict.date_to.year rate = payslip.dict.get_rate('ER_US_NJ_UNEMP') ytd = payslip.sum('WAGE_US_NJ_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01') ytd += contract.external_wages -remaining = rate.wage_limit_year - ytd -if remaining <= 0.0: - result = 0 -elif remaining < categories.BASIC: - result = remaining +if rate.wage_limit_year: + remaining = rate.wage_limit_year - ytd + if remaining <= 0.0: + result = 0 + elif remaining < categories.BASIC: + result = remaining + else: + result = categories.BASIC else: result = categories.BASIC @@ -39,14 +42,14 @@ else: rate = payslip.dict.get_rate('EE_US_NJ_UNEMP') result_rate = -rate.rate -result = categories.BASIC +result = categories.WAGE_US_NJ_UNEMP # result_rate of 0 implies 100% due to bug if result_rate == 0.0: result = 0.0 - + @@ -72,7 +75,7 @@ if result_rate == 0.0: - + Wage: US-NJ State Disability Insurance WAGE_US_NJ_SDI @@ -85,11 +88,14 @@ year = payslip.dict.date_to.year rate = payslip.dict.get_rate('ER_US_NJ_SDI') ytd = payslip.sum('WAGE_US_NJ_SDI', str(year) + '-01-01', str(year+1) + '-01-01') ytd += contract.external_wages -remaining = rate.wage_limit_year - ytd -if remaining <= 0.0: - result = 0 -elif remaining < categories.BASIC: - result = remaining +if rate.wage_limit_year: + remaining = rate.wage_limit_year - ytd + if remaining <= 0.0: + result = 0 + elif remaining < categories.BASIC: + result = remaining + else: + result = categories.BASIC else: result = categories.BASIC @@ -106,14 +112,14 @@ else: rate = payslip.dict.get_rate('EE_US_NJ_SDI') result_rate = -rate.rate -result = categories.BASIC +result = categories.WAGE_US_NJ_SDI # result_rate of 0 implies 100% due to bug if result_rate == 0.0: result = 0.0 - + @@ -138,7 +144,7 @@ if result_rate == 0.0: - + Wage: US-NJ Family Leave Insurance WAGE_US_NJ_FLI @@ -151,11 +157,14 @@ year = payslip.dict.date_to.year rate = payslip.dict.get_rate('ER_US_NJ_FLI') ytd = payslip.sum('WAGE_US_NJ_FLI', str(year) + '-01-01', str(year+1) + '-01-01') ytd += contract.external_wages -remaining = rate.wage_limit_year - ytd -if remaining <= 0.0: - result = 0 -elif remaining < categories.BASIC: - result = remaining +if rate.wage_limit_year: + remaining = rate.wage_limit_year - ytd + if remaining <= 0.0: + result = 0 + elif remaining < categories.BASIC: + result = remaining + else: + result = categories.BASIC else: result = categories.BASIC @@ -172,15 +181,15 @@ else: rate = payslip.dict.get_rate('EE_US_NJ_FLI') result_rate = -rate.rate -result = categories.BASIC +result = categories.WAGE_US_NJ_FLI - + - + Wage: US-NJ Workforce Development Funds WAGE_US_NJ_WF @@ -193,11 +202,14 @@ year = payslip.dict.date_to.year rate = payslip.dict.get_rate('ER_US_NJ_WF') ytd = payslip.sum('WAGE_US_NJ_WF', str(year) + '-01-01', str(year+1) + '-01-01') ytd += contract.external_wages -remaining = rate.wage_limit_year - ytd -if remaining <= 0.0: - result = 0 -elif remaining < categories.BASIC: - result = remaining +if rate.wage_limit_year: + remaining = rate.wage_limit_year - ytd + if remaining <= 0.0: + result = 0 + elif remaining < categories.BASIC: + result = remaining + else: + result = categories.BASIC else: result = categories.BASIC @@ -214,7 +226,26 @@ else: rate = payslip.dict.get_rate('EE_US_NJ_WF') result_rate = -rate.rate -result = categories.BASIC +result = categories.WAGE_US_NJ_WF + +if result_rate == 0.0: + result = 0.0 + + + + + + + + ER: US-NJ Workforce Development Funds + ER_US_NJ_WF + python + result = (contract.futa_type != contract.FUTA_TYPE_BASIC) + code + +rate = payslip.dict.get_rate('ER_US_NJ_WF') +result_rate = -rate.rate +result = categories.WAGE_US_NJ_WF if result_rate == 0.0: result = 0.0 @@ -233,6 +264,7 @@ if result_rate == 0.0: result = True code +year = payslip.dict.date_to.year wages = categories.GROSS allowances = contract.nj_njw4_allowances additional_withholding = contract.nj_additional_withholding @@ -250,458 +282,974 @@ if not rate_table: # 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 year == 2018: + divisor = 1.0 + #### 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), + ] +else: + divisor = 100.0 + # 2019 Table Rates + # All Rate schedules share the same allowance. 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), + #### RATE 'A' #### + if rate_table == 'A': + base_rate = [ + 1.5, + 2.0, + 3.9, + 6.1, + 7.0, + 9.9, + 11.8, ] + ### WEEKLY ### + if schedule_pay == 'weekly': + tax_rate_table = [ + (385, 0.0), + (673, 5.77), + (769, 11.54), + (1442, 15.29), + (9615, 56.34), + (96154, 628.46), + (float('inf'), 9195.77), + ] - ### 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), + ### BI-WEEKLY ### + elif schedule_pay == 'bi-weekly': + tax_rate_table = [ + (769, 0.0), + (1346, 12.00), + (1538, 23.00), + (2885, 31.00), + (19231, 113.00), + (192308, 1257.00), + (float('inf'), 18392.00), + ] + + ### SEMI-MONTHLY ### + elif schedule_pay == 'semi-monthly': + tax_rate_table = [ + (833, 0.0), + (1458, 13.00), + (1667, 25.00), + (3125, 33.00), + (20833, 122.00), + (208333, 1362.00), + (float('inf'), 19924.00), + ] + + ### MONTHLY ### + elif schedule_pay == 'monthly': + tax_rate_table = [ + (1667, 0.0), + (2917, 25.00), + (3333, 50.00), + (6250, 66.00), + (41667, 244.00), + (416667, 2723.00), + (float('inf'), 39848.00), + ] + + ### QUARTERLY ### + elif schedule_pay == 'quarterly': + tax_rate_table = [ + (5000, 0.0), + (8750, 75.00), + (10000, 150.00), + (18750, 198.75), + (125000, 732.50), + (1250000, 8170.00), + (float('inf'), 119545.00), + ] + + ### SEMI-ANNUAL ### + elif schedule_pay == 'semi-annual': + tax_rate_table = [ + (10000, 0.0), + (17500, 150.00), + (20000, 300.00), + (37500, 397.50), + (250000, 1465.00), + (2500000, 16340.00), + (float('inf'), 239090.00), + ] + + ### ANNUAL ### + elif schedule_pay == 'annually': + tax_rate_table = [ + (20000, 0.0), + (35000, 300.00), + (40000, 600.00), + (75000, 795.00), + (500000, 2930.00), + (5000000, 32680.00), + (float('inf'), 478180.00), + ] + + #### RATE 'B' #### + elif rate_table == 'B': + base_rate = [ + 1.5, + 2.0, + 2.7, + 3.9, + 6.1, + 7.0, + 9.9, + 11.8, ] + ### WEEKLY ### + if schedule_pay == 'weekly': + tax_rate_table = [ + (385, 0.0), + (962, 5.77), + (1346, 17.31), + (1538, 27.69), + (2885, 35.19), + (9615, 117.31), + (96154, 588.46), + (float('inf'), 9155.77), + ] - ### 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), + ### BI-WEEKLY ### + elif schedule_pay == 'bi-weekly': + tax_rate_table = [ + (769, 0.0), + (1923, 12.00), + (2692, 35.00), + (3076, 55.00), + (5769, 70.00), + (19231, 235.00), + (192308, 1177.00), + (float('inf'), 18312.00), + ] + + ### SEMI-MONTHLY ### + elif schedule_pay == 'semi-monthly': + tax_rate_table = [ + (833, 0.0), + (2083, 12.50), + (2917, 37.50), + (3333, 59.99), + (6250, 76.25), + (20833, 254.19), + (208333, 1275.00), + (float('inf'), 19838.00), + ] + + ### MONTHLY ### + elif schedule_pay == 'monthly': + tax_rate_table = [ + (1667, 0.0), + (4167, 25.00), + (5833, 75.00), + (6667, 120.00), + (12500, 153.00), + (41667, 508.00), + (416667, 2550.00), + (float('inf'), 39675.00), + ] + + ### QUARTERLY ### + elif schedule_pay == 'quarterly': + tax_rate_table = [ + (5000, 0.0), + (12500, 75.00), + (17500, 225.00), + (20000, 360.00), + (37500, 457.50), + (125000, 1525.00), + (1250000, 7650.00), + (float('inf'), 119025.00), + ] + + ### SEMI-ANNUAL ### + elif schedule_pay == 'semi-annual': + tax_rate_table = [ + (10000, 0.0), + (25000, 150.00), + (35000, 450.00), + (40000, 720.00), + (75000, 915.00), + (250000, 3050.00), + (2500000, 15300.00), + (float('inf'), 238050.00), + ] + + ### ANNUAL ### + elif schedule_pay == 'annually': + tax_rate_table = [ + (20000, 0.0), + (50000, 300.00), + (70000, 900.00), + (80000, 1440.00), + (150000, 1830.00), + (500000, 6100.00), + (5000000, 30600.00), + (float('inf'), 476100.00), + ] + + #### RATE 'C' #### + elif rate_table == 'C': + base_rate = [ + 1.5, + 2.3, + 2.8, + 3.5, + 5.6, + 6.6, + 9.9, + 11.8, ] + ### WEEKLY ### + if schedule_pay == 'weekly': + tax_rate_table = [ + (385, 0.0), + (769, 5.77), + (962, 14.62), + (1154, 20.00), + (2885, 26.73), + (9615, 123.65), + (96154, 567.88), + (float('inf'), 9135.19), + ] - ### 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), + ### BI-WEEKLY ### + elif schedule_pay == 'bi-weekly': + tax_rate_table = [ + (769, 0.0), + (1538, 11.54), + (1923, 29.23), + (2308, 40.00), + (5769, 53.46), + (19231, 247.31), + (192308, 1135.77), + (float('inf'), 18270.38), + ] + + ### SEMI-MONTHLY ### + elif schedule_pay == 'semi-monthly': + tax_rate_table = [ + (833, 0.0), + (1667, 12.50), + (2083, 31.67), + (2500, 43.33), + (6250, 57.92), + (20833, 267.92), + (208333, 1230.42), + (float('inf'), 19792.92), + ] + + ### MONTHLY ### + elif schedule_pay == 'monthly': + tax_rate_table = [ + (1667, 0.0), + (3333, 25.00), + (4167, 63.33), + (5000, 86.67), + (12500, 115.83), + (41667, 535.85), + (416667, 2460.83), + (float('inf'), 39585.83), + ] + + ### QUARTERLY ### + elif schedule_pay == 'quarterly': + tax_rate_table = [ + (5000, 0.0), + (10000, 75.00), + (12500, 190.00), + (15000, 260.00), + (37500, 347.50), + (125000, 1607.50), + (1250000, 7382.50), + (float('inf'), 118757.50), + ] + + ### SEMI-ANNUAL ### + elif schedule_pay == 'semi-annual': + tax_rate_table = [ + (10000, 0.0), + (20000, 150.00), + (25000, 380.00), + (30000, 520.00), + (75000, 695.00), + (250000, 3215.00), + (2500000, 14765.00), + (float('inf'), 237515.00), + ] + + ### ANNUAL ### + elif schedule_pay == 'annually': + tax_rate_table = [ + (20000, 0.0), + (40000, 300.00), + (50000, 760.00), + (60000, 1040.00), + (150000, 1390.00), + (500000, 6430.00), + (5000000, 29530.00), + (float('inf'), 475030.00), + ] + + #### RATE 'D' #### + elif rate_table == 'D': + base_rate = [ + 1.5, + 2.7, + 3.4, + 4.3, + 5.6, + 6.5, + 9.9, + 11.8, ] + ### WEEKLY ### + if schedule_pay == 'weekly': + tax_rate_table = [ + (385, 0.0), + (769, 5.77), + (962, 16.15), + (1154, 22.69), + (2885, 30.96), + (9615, 127.88), + (96154, 565.38), + (float('inf'), 9132.69), + ] - ### 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), + ### BI-WEEKLY ### + elif schedule_pay == 'bi-weekly': + tax_rate_table = [ + (769, 0.0), + (1538, 11.54), + (1923, 32.31), + (2308, 45.38), + (5769, 61.92), + (19231, 255.77), + (192308, 1130.77), + (float('inf'), 18265.38), + ] + + ### SEMI-MONTHLY ### + elif schedule_pay == 'semi-monthly': + tax_rate_table = [ + (833, 0.0), + (1667, 12.50), + (2083, 35.00), + (2500, 49.17), + (6250, 67.08), + (20833, 277.08), + (208333, 1225.00), + (float('inf'), 19787.50), + ] + + ### MONTHLY ### + elif schedule_pay == 'monthly': + tax_rate_table = [ + (1667, 0.0), + (3333, 25.00), + (4167, 70.00), + (5000, 98.33), + (12500, 134.17), + (41667, 554.17), + (416667, 2450.00), + (float('inf'), 39575.00), + ] + + ### QUARTERLY ### + elif schedule_pay == 'quarterly': + tax_rate_table = [ + (5000, 0.0), + (10000, 75.00), + (12500, 210.00), + (15000, 295.00), + (37500, 402.50), + (125000, 1662.50), + (1250000, 7350.00), + (float('inf'), 118725.00), + ] + + ### SEMI-ANNUAL ### + elif schedule_pay == 'semi-annual': + tax_rate_table = [ + (10000, 0.0), + (20000, 150.00), + (25000, 420.00), + (30000, 590.00), + (75000, 805.00), + (250000, 3325.00), + (2500000, 14700.00), + (float('inf'), 237450.00), + ] + + ### ANNUAL ### + elif schedule_pay == 'annually': + tax_rate_table = [ + (20000, 0.0), + (40000, 300.00), + (50000, 840.00), + (60000, 1180.00), + (150000, 1610.00), + (250000, 6650.00), + (2500000, 29400.00), + (float('inf'), 474900.00), + ] + + #### RATE 'E' #### + elif rate_table == 'E': + base_rate = [ + 1.5, + 2.0, + 5.8, + 6.5, + 9.9, + 11.8, ] + ### WEEKLY ### + if schedule_pay == 'weekly': + tax_rate_table = [ + (385, 0.0), + (673, 5.77), + (1923, 11.54), + (9615, 84.04), + (96154, 584.04), + (float('inf'), 9151.35), + ] - ### 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), - ] + ### BI-WEEKLY ### + elif schedule_pay == 'bi-weekly': + tax_rate_table = [ + (769, 0.0), + (1346, 12.00), + (3846, 23.00), + (19231, 168.00), + (192308, 1168.00), + (float('inf'), 18303.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), - ] + ### SEMI-MONTHLY ### + elif schedule_pay == 'semi-monthly': + tax_rate_table = [ + (833, 0.0), + (1458, 13.00), + (4167, 25.00), + (20833, 182.00), + (208333, 1265.00), + (float('inf'), 19828.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), - ] + ### MONTHLY ### + elif schedule_pay == 'monthly': + tax_rate_table = [ + (1667, 0.0), + (2916, 25.00), + (8333, 50.00), + (41667, 364.00), + (416667, 2531.00), + (float('inf'), 39656.00), + ] - ### 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), - ] + ### QUARTERLY ### + elif schedule_pay == 'quarterly': + tax_rate_table = [ + (5000, 0.0), + (8750, 75.00), + (25000, 150.00), + (125000, 1092.50), + (1250000, 7592.50), + (float('inf'), 118967.50), + ] - ### 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), - ] + ### SEMI-ANNUAL ### + elif schedule_pay == 'semi-annual': + tax_rate_table = [ + (10000, 0.0), + (17500, 150.00), + (50000, 300.00), + (250000, 2185.00), + (2500000, 15185.00), + (float('inf'), 237935.00), + ] + ### ANNUAL ### + elif schedule_pay == 'annually': + tax_rate_table = [ + (20000, 0.0), + (35000, 300.00), + (100000, 600.00), + (500000, 4370.00), + (5000000, 30370.00), + (float('inf'), 475870.00), + ] over = 0.0 tax = 0.0 -for row in tax_rate_table: +for i, row in enumerate(tax_rate_table): if wages <= row[0]: - tax = ((wages - over) * row[1]) + row[2] - tax += additional_withholding - break + if len(row) == 2: + tax = ((wages - over) * (base_rate[i] / divisor)) + row[1] + tax += additional_withholding + break + else: + # 2018 rates include the base_rate in the row + tax = ((wages - over) * (row[1] / divisor)) + row[2] + tax += additional_withholding + break over = row[0] result = -tax diff --git a/l10n_us_nj_hr_payroll/tests/__init__.py b/l10n_us_nj_hr_payroll/tests/__init__.py index 02adeeae..812ad506 100755 --- a/l10n_us_nj_hr_payroll/tests/__init__.py +++ b/l10n_us_nj_hr_payroll/tests/__init__.py @@ -1 +1,2 @@ from . import test_us_nj_payslip_2018 +from . import test_us_nj_payslip_2019 diff --git a/l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py b/l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py new file mode 100755 index 00000000..eabf103c --- /dev/null +++ b/l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py @@ -0,0 +1,179 @@ +from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip + + +class TestUsNJPayslip(TestUsPayslip): + ### + # 2018 Taxes and Rates + ### + NJ_UNEMP_MAX_WAGE = 34400.0 # Note that this is used for SDI and FLI as well + + ER_NJ_UNEMP = -2.6825 / 100.0 + EE_NJ_UNEMP = -0.3825 / 100.0 + + ER_NJ_SDI = -0.5 / 100.0 + EE_NJ_SDI = -0.17 / 100.0 + + ER_NJ_WF = -0.1175 / 100.0 + EE_NJ_WF = -0.0425 / 100.0 + + ER_NJ_FLI = 0.0 + EE_NJ_FLI = -0.08 / 100.0 + + # Examples found on page 24 of http://www.state.nj.us/treasury/taxation/pdf/current/njwt.pdf + def test_2019_taxes_example1(self): + salary = 300 + schedule_pay = 'weekly' + allowances = 1 + additional_withholding = 0 + + # Tax Percentage Method for Single, taxable under $385 + wh = -4.21 + + employee = self._createEmployee() + 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' + + self.assertEqual(contract.schedule_pay, 'weekly') + + self._log('2019 New Jersey tax first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['WAGE_US_NJ_UNEMP'], salary) + self.assertPayrollEqual(cats['EE_US_NJ_UNEMP'], round(cats['BASIC'] * self.EE_NJ_UNEMP, 2)) + self.assertPayrollEqual(cats['ER_US_NJ_UNEMP'], round(cats['WAGE_US_NJ_UNEMP'] * self.ER_NJ_UNEMP, 2)) + self.assertPayrollEqual(cats['EE_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.EE_NJ_SDI) + self.assertPayrollEqual(cats['ER_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.ER_NJ_SDI) + self.assertPayrollEqual(cats['EE_US_NJ_FLI'], cats['WAGE_US_NJ_FLI'] * self.EE_NJ_FLI) + self.assertPayrollEqual(cats['EE_US_NJ_WF'], cats['WAGE_US_NJ_WF'] * self.EE_NJ_WF) + self.assertPayrollEqual(cats['EE_US_NJ_INC_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('2019 New Jersey tax second payslip:') + payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['WAGE_US_NJ_UNEMP'], remaining_nj_unemp_wages) + self.assertPayrollEqual(cats['ER_US_NJ_UNEMP'], round(remaining_nj_unemp_wages * self.ER_NJ_UNEMP, 2)) + self.assertPayrollEqual(cats['EE_US_NJ_UNEMP'], round(cats['BASIC'] * self.EE_NJ_UNEMP, 2)) + + def test_2019_taxes_example2(self): + salary = 1400.00 + schedule_pay = 'weekly' + allowances = 3 + additional_withholding = 0 + + # Tax Percentage Method for Single, taxable pay over $769, under $1442 + wh = -27.58 + + employee = self._createEmployee() + 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' + + self.assertEqual(contract.schedule_pay, 'weekly') + + self._log('2019 New Jersey tax first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['WAGE_US_NJ_UNEMP'], salary) + self.assertPayrollEqual(cats['EE_US_NJ_UNEMP'], cats['WAGE_US_NJ_UNEMP'] * self.EE_NJ_UNEMP) + #self.assertPayrollEqual(cats['ER_US_NJ_UNEMP'], round(cats['WAGE_US_NJ_UNEMP'] * self.ER_NJ_UNEMP, 2)) + # round(37.555, 2) => 37.55 but in reality this should be 37.56, floats are weird ;) + self.assertTrue(abs(cats['ER_US_NJ_UNEMP'] - round(cats['WAGE_US_NJ_UNEMP'] * self.ER_NJ_UNEMP, 2)) < 0.02) + self.assertPayrollEqual(cats['EE_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.EE_NJ_SDI) + self.assertPayrollEqual(cats['ER_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.ER_NJ_SDI) + self.assertPayrollEqual(cats['EE_US_NJ_FLI'], cats['WAGE_US_NJ_FLI'] * self.EE_NJ_FLI) + self.assertPayrollEqual(cats['EE_US_NJ_WF'], cats['WAGE_US_NJ_WF'] * self.EE_NJ_WF) + self.assertPayrollEqual(cats['EE_US_NJ_INC_WITHHOLD'], wh) + + process_payslip(payslip) + + def test_2019_taxes_to_the_limits(self): + salary = 30000.00 + schedule_pay = 'quarterly' + allowances = 3 + additional_withholding = 0 + + # Tax Percentage Method for Single, taxable pay over $18750, under 125000 + wh = -1021.75 + + employee = self._createEmployee() + 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' + + self.assertEqual(contract.schedule_pay, 'quarterly') + + self._log('2019 New Jersey tax first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-03-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['WAGE_US_NJ_UNEMP'], salary) + self.assertPayrollEqual(cats['EE_US_NJ_UNEMP'], cats['BASIC'] * self.EE_NJ_UNEMP) + #self.assertPayrollEqual(cats['ER_US_NJ_UNEMP'], round(cats['WAGE_US_NJ_UNEMP'] * self.ER_NJ_UNEMP, 2)) + # round(37.555, 2) => 37.55 but in reality this should be 37.56, floats are weird ;) + self.assertTrue(abs(cats['ER_US_NJ_UNEMP'] - round(cats['WAGE_US_NJ_UNEMP'] * self.ER_NJ_UNEMP, 2)) < 0.02) + self.assertPayrollEqual(cats['EE_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.EE_NJ_SDI) + self.assertPayrollEqual(cats['ER_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.ER_NJ_SDI) + self.assertPayrollEqual(cats['EE_US_NJ_FLI'], cats['WAGE_US_NJ_FLI'] * self.EE_NJ_FLI) + self.assertPayrollEqual(cats['EE_US_NJ_WF'], cats['WAGE_US_NJ_WF'] * self.EE_NJ_WF) + self.assertPayrollEqual(cats['EE_US_NJ_INC_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('2019 New Jersey tax second payslip:') + payslip = self._createPayslip(employee, '2019-04-01', '2019-07-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertFalse(abs(cats['BASIC'] - remaining_nj_unemp_wages) < 0.02) + self.assertTrue(abs(cats['WAGE_US_NJ_UNEMP'] - remaining_nj_unemp_wages) < 0.02) + self.assertPayrollEqual(cats['WAGE_US_NJ_UNEMP'], remaining_nj_unemp_wages) + self.assertPayrollEqual(cats['ER_US_NJ_UNEMP'], round(remaining_nj_unemp_wages * self.ER_NJ_UNEMP, 2)) + self.assertPayrollEqual(cats['EE_US_NJ_UNEMP'], round(remaining_nj_unemp_wages * self.EE_NJ_UNEMP, 2)) + self.assertPayrollEqual(cats['EE_US_NJ_SDI'], remaining_nj_unemp_wages * self.EE_NJ_SDI) + self.assertPayrollEqual(cats['ER_US_NJ_SDI'], remaining_nj_unemp_wages * self.ER_NJ_SDI) \ No newline at end of file From 0355d342f433f30d6f547db640e0fd6666601d4c Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Fri, 10 May 2019 08:16:54 -0700 Subject: [PATCH 2/5] FIX `l10n_us_nj_hr_payroll` Apply a wage limit to WF rates. --- l10n_us_nj_hr_payroll/data/rates.xml | 2 ++ l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/l10n_us_nj_hr_payroll/data/rates.xml b/l10n_us_nj_hr_payroll/data/rates.xml index 315bbe5a..4999f959 100644 --- a/l10n_us_nj_hr_payroll/data/rates.xml +++ b/l10n_us_nj_hr_payroll/data/rates.xml @@ -91,12 +91,14 @@ ER_US_NJ_WF 0.1175 2019-01-01 + US NJ Workforce Development (Employee) EE_US_NJ_WF 0.0425 2019-01-01 + diff --git a/l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py b/l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py index eabf103c..b5e8e6f6 100755 --- a/l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py +++ b/l10n_us_nj_hr_payroll/tests/test_us_nj_payslip_2019.py @@ -153,6 +153,7 @@ class TestUsNJPayslip(TestUsPayslip): self.assertPayrollEqual(cats['EE_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.EE_NJ_SDI) self.assertPayrollEqual(cats['ER_US_NJ_SDI'], cats['WAGE_US_NJ_SDI'] * self.ER_NJ_SDI) self.assertPayrollEqual(cats['EE_US_NJ_FLI'], cats['WAGE_US_NJ_FLI'] * self.EE_NJ_FLI) + self.assertPayrollEqual(cats['ER_US_NJ_WF'], cats['WAGE_US_NJ_WF'] * self.ER_NJ_WF) self.assertPayrollEqual(cats['EE_US_NJ_WF'], cats['WAGE_US_NJ_WF'] * self.EE_NJ_WF) self.assertPayrollEqual(cats['EE_US_NJ_INC_WITHHOLD'], wh) @@ -176,4 +177,6 @@ class TestUsNJPayslip(TestUsPayslip): self.assertPayrollEqual(cats['ER_US_NJ_UNEMP'], round(remaining_nj_unemp_wages * self.ER_NJ_UNEMP, 2)) self.assertPayrollEqual(cats['EE_US_NJ_UNEMP'], round(remaining_nj_unemp_wages * self.EE_NJ_UNEMP, 2)) self.assertPayrollEqual(cats['EE_US_NJ_SDI'], remaining_nj_unemp_wages * self.EE_NJ_SDI) - self.assertPayrollEqual(cats['ER_US_NJ_SDI'], remaining_nj_unemp_wages * self.ER_NJ_SDI) \ No newline at end of file + self.assertPayrollEqual(cats['ER_US_NJ_SDI'], remaining_nj_unemp_wages * self.ER_NJ_SDI) + self.assertPayrollEqual(cats['EE_US_NJ_WF'], remaining_nj_unemp_wages * self.EE_NJ_WF) + self.assertPayrollEqual(cats['ER_US_NJ_WF'], remaining_nj_unemp_wages * self.ER_NJ_WF) From 1f4b9e1627b8911044863c2b731e9b0a423ef2f3 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Wed, 13 Feb 2019 07:53:55 -0800 Subject: [PATCH 3/5] Initial commit of `l10n_us_mt_hr_payroll` US Montana Payroll for 11.0 --- l10n_us_mt_hr_payroll/README.rst | 24 ++++ l10n_us_mt_hr_payroll/__init__.py | 1 + l10n_us_mt_hr_payroll/__manifest__.py | 28 ++++ l10n_us_mt_hr_payroll/data/base.xml | 44 ++++++ l10n_us_mt_hr_payroll/data/final.xml | 17 +++ l10n_us_mt_hr_payroll/data/rates.xml | 12 ++ l10n_us_mt_hr_payroll/data/rules.xml | 125 +++++++++++++++++ l10n_us_mt_hr_payroll/models/__init__.py | 1 + l10n_us_mt_hr_payroll/models/hr_payroll.py | 17 +++ l10n_us_mt_hr_payroll/tests/__init__.py | 1 + .../tests/test_us_mt_payslip_2019.py | 129 ++++++++++++++++++ .../views/hr_payroll_views.xml | 22 +++ 12 files changed, 421 insertions(+) create mode 100644 l10n_us_mt_hr_payroll/README.rst create mode 100755 l10n_us_mt_hr_payroll/__init__.py create mode 100755 l10n_us_mt_hr_payroll/__manifest__.py create mode 100755 l10n_us_mt_hr_payroll/data/base.xml create mode 100755 l10n_us_mt_hr_payroll/data/final.xml create mode 100644 l10n_us_mt_hr_payroll/data/rates.xml create mode 100755 l10n_us_mt_hr_payroll/data/rules.xml create mode 100644 l10n_us_mt_hr_payroll/models/__init__.py create mode 100755 l10n_us_mt_hr_payroll/models/hr_payroll.py create mode 100755 l10n_us_mt_hr_payroll/tests/__init__.py create mode 100755 l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py create mode 100755 l10n_us_mt_hr_payroll/views/hr_payroll_views.xml diff --git a/l10n_us_mt_hr_payroll/README.rst b/l10n_us_mt_hr_payroll/README.rst new file mode 100644 index 00000000..d7719869 --- /dev/null +++ b/l10n_us_mt_hr_payroll/README.rst @@ -0,0 +1,24 @@ +********************************** +Hibou - US Payroll - Montana State +********************************** + +Calculations and contribution registers for Montana State Payroll. + +For more information and add-ons, visit `Hibou.io `_. + +============= +Main Features +============= + +* New Partners and Contribution Registers for: + * Montana Department of Revenue + * Montana Department of Labor & Industry +* Contract level Montana Exemptions and MW-4 fields +* Payroll Rate for Montana L&I Unemployment Rate + + +======= +License +======= +Please see `LICENSE `_. +Copyright Hibou Corp. 2019 diff --git a/l10n_us_mt_hr_payroll/__init__.py b/l10n_us_mt_hr_payroll/__init__.py new file mode 100755 index 00000000..0650744f --- /dev/null +++ b/l10n_us_mt_hr_payroll/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/l10n_us_mt_hr_payroll/__manifest__.py b/l10n_us_mt_hr_payroll/__manifest__.py new file mode 100755 index 00000000..f19ac5e2 --- /dev/null +++ b/l10n_us_mt_hr_payroll/__manifest__.py @@ -0,0 +1,28 @@ +{ + 'name': 'USA - Montana - Payroll', + 'author': 'Hibou Corp. ', + 'license': 'AGPL-3', + 'category': 'Localization', + 'depends': ['l10n_us_hr_payroll'], + 'version': '11.0.2019.0.0', + 'description': """ +USA::Montana Payroll Rules. +=========================== + +* New Partners and Contribution Registers for: + * Montana Department of Revenue + * Montana Department of Labor & Industry +* Contract level Montana Exemptions and MW-4 fields +* Payroll Rate for Montana L&I Unemployment Rate + """, + 'auto_install': False, + 'website': 'https://hibou.io/', + 'data': [ + 'views/hr_payroll_views.xml', + 'data/base.xml', + 'data/rates.xml', + 'data/rules.xml', + 'data/final.xml', + ], + 'installable': True +} diff --git a/l10n_us_mt_hr_payroll/data/base.xml b/l10n_us_mt_hr_payroll/data/base.xml new file mode 100755 index 00000000..95d98f01 --- /dev/null +++ b/l10n_us_mt_hr_payroll/data/base.xml @@ -0,0 +1,44 @@ + + + + + Montana Department of Labor & Industries + 1 + + + + Montana Department of Revenue + 1 + + + + Montana Unemployment + Montana Department of Labor & Industries + + + + Montana Income Tax Withholding + Montana Department of Revenue - Income Tax Withholding + + + + + + + Wage: US-MT Unemployment + WAGE_US_MT_UNEMP + + + + ER: US-MT Unemployment + ER_US_MT_UNEMP + + + + + EE: US-MT Income Withholding + EE_US_MT_INC_WITHHOLD + + + + diff --git a/l10n_us_mt_hr_payroll/data/final.xml b/l10n_us_mt_hr_payroll/data/final.xml new file mode 100755 index 00000000..043fb4d6 --- /dev/null +++ b/l10n_us_mt_hr_payroll/data/final.xml @@ -0,0 +1,17 @@ + + + + + + US_MT_EMP + USA Montana Employee + + + + + + diff --git a/l10n_us_mt_hr_payroll/data/rates.xml b/l10n_us_mt_hr_payroll/data/rates.xml new file mode 100644 index 00000000..d4819123 --- /dev/null +++ b/l10n_us_mt_hr_payroll/data/rates.xml @@ -0,0 +1,12 @@ + + + + + US Montana Unemployment + US_MT_UNEMP + 1.18 + 2019-01-01 + + + + \ No newline at end of file diff --git a/l10n_us_mt_hr_payroll/data/rules.xml b/l10n_us_mt_hr_payroll/data/rules.xml new file mode 100755 index 00000000..03607cef --- /dev/null +++ b/l10n_us_mt_hr_payroll/data/rules.xml @@ -0,0 +1,125 @@ + + + + + + + + Wage: US-MT Unemployment + WAGE_US_MT_UNEMP + python + result = (contract.futa_type != contract.FUTA_TYPE_BASIC) + code + +### +year = int(payslip.dict.date_to[:4]) +rate = payslip.dict.get_rate('US_MT_UNEMP') +ytd = payslip.sum('WAGE_US_MT_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01') +ytd += contract.external_wages +remaining = rate.wage_limit_year - ytd +if remaining <= 0.0: + result = 0 +elif remaining < categories.BASIC: + result = remaining +else: + result = categories.BASIC + + + + + + + ER: US-MT Unemployment + ER_US_MT_UNEMP + python + result = (contract.futa_type != contract.FUTA_TYPE_BASIC) + code + +rate = payslip.dict.get_rate('US_MT_UNEMP') +result_rate = -rate.rate +result = categories.WAGE_US_MT_UNEMP + +# result_rate of 0 implies 100% due to bug +if result_rate == 0.0: + result = 0.0 + + + + + + + + + EE: US-MT Income Withholding + EE_US_MT_INC_WITHHOLD + python + result = not contract.mt_mw4_exempt + code + +year = int(payslip.dict.date_to[:4]) +G = categories.GROSS +N = contract.mt_mw4_exemptions +additional = contract.mt_mw4_additional_withholding +schedule_pay = contract.schedule_pay +result = 0.00 +float_max = float('inf') + +if year == 2019 or True: + if schedule_pay == 'weekly': + bracket = [ + (135.0, 0.0, 1.80, 0.0), + (288.0, 2.0, 4.40, 135.0), + (2308.0, 9.0, 6.00, 288.0), + (float_max, 130.0, 6.60, 2308.0), + ] + exemption_rate = 37.0 + elif schedule_pay == 'bi-weekly': + bracket = [ + (269.0, 0.0, 1.80, 0.0), + (577.0, 5.0, 4.40, 269.0), + (4615.0, 18.0, 6.00, 577.0), + (float_max, 261.0, 6.60, 4615.0), + ] + exemption_rate = 73.0 + elif schedule_pay == 'semi-monthly': + bracket = [ + (292.0, 0.0, 1.80, 0.0), + (625.0, 5.0, 4.40, 292.0), + (5000.0, 20.0, 6.00, 625.0), + (float_max, 282.0, 6.60, 5000.0), + ] + exemption_rate = 79.0 + elif schedule_pay == 'monthly': + bracket = [ + (583.0, 0.0, 1.80, 0.0), + (1250.0, 11.0, 4.40, 583.0), + (10000.0, 40.0, 6.00, 1250.0), + (float_max, 565.0, 6.60, 10000.0), + ] + exemption_rate = 158.0 + elif schedule_pay == 'annually': + bracket = [ + (7000.0, 0.0, 1.80, 0.0), + (15000.0, 126.0, 4.40, 7000.0), + (120000.0, 478.0, 6.00, 15000.0), + (float_max, 6778.0, 6.60, 120000.0), + ] + exemption_rate = 1900.0 + else: + raise Exception('Invalid schedule_pay=' + schedule_pay + ' for MT Income Withholding') + + T = G - (exemption_rate * N) + if T <= 0.0: + result = 0.0 + else: + for data in bracket: + if T < data[0]: + result = round(data[1] + ((data[2] / 100.0) * (T - data[3]))) + break + +result += additional + + + + + diff --git a/l10n_us_mt_hr_payroll/models/__init__.py b/l10n_us_mt_hr_payroll/models/__init__.py new file mode 100644 index 00000000..e99aa24a --- /dev/null +++ b/l10n_us_mt_hr_payroll/models/__init__.py @@ -0,0 +1 @@ +from . import hr_payroll diff --git a/l10n_us_mt_hr_payroll/models/hr_payroll.py b/l10n_us_mt_hr_payroll/models/hr_payroll.py new file mode 100755 index 00000000..be1ce49e --- /dev/null +++ b/l10n_us_mt_hr_payroll/models/hr_payroll.py @@ -0,0 +1,17 @@ +from odoo import models, fields + + +class USMTHrContract(models.Model): + _inherit = 'hr.contract' + + mt_mw4_exemptions = fields.Integer(string='Montana MW-4 Exemptions', default=0, + help='Box G') + mt_mw4_additional_withholding = fields.Float(string="Montana MW-4 Additional Withholding", default=0.0, + help='Box H') + mt_mw4_exempt = fields.Selection([ + ('', 'Not Exempt'), + ('tribe', 'Registered Tribe'), + ('reserve', 'Reserve or National Guard'), + ('north_dakota', 'North Dakota'), + ('montana_for_marriage', 'Montana for Marriage'), + ], string='Exemption from Montana Withholding', default='', help='Section 2') diff --git a/l10n_us_mt_hr_payroll/tests/__init__.py b/l10n_us_mt_hr_payroll/tests/__init__.py new file mode 100755 index 00000000..52aa2a64 --- /dev/null +++ b/l10n_us_mt_hr_payroll/tests/__init__.py @@ -0,0 +1 @@ +from . import test_us_mt_payslip_2019 diff --git a/l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py b/l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py new file mode 100755 index 00000000..59f9a60f --- /dev/null +++ b/l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py @@ -0,0 +1,129 @@ +from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip + + +class TestUsMtPayslip(TestUsPayslip): + # Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705 + MT_UNEMP = -1.18 / 100.0 + + def test_2019_taxes_one(self): + # Payroll Period Semi-Monthly example + salary = 550 + mt_mw4_exemptions = 5 + + employee = self._createEmployee() + contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_mt_hr_payroll.hr_payroll_salary_structure_us_mt_employee'), + schedule_pay='semi-monthly') + contract.mt_mw4_exemptions = mt_mw4_exemptions + + self._log('2019 Montana tax single first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['WAGE_US_MT_UNEMP'], salary) + self.assertPayrollEqual(cats['ER_US_MT_UNEMP'], cats['WAGE_US_MT_UNEMP'] * self.MT_UNEMP) + + mt_taxable_income = salary - (79.0 * mt_mw4_exemptions) + mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0))) + self.assertPayrollEqual(mt_taxable_income, 155.0) + self.assertPayrollEqual(mt_withhold, 3.0) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold) + + def test_2019_taxes_two(self): + # Payroll Period Bi-Weekly example + salary = 2950 + mt_mw4_exemptions = 2 + + employee = self._createEmployee() + contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_mt_hr_payroll.hr_payroll_salary_structure_us_mt_employee'), + schedule_pay='bi-weekly') + contract.mt_mw4_exemptions = mt_mw4_exemptions + + self._log('2019 Montana tax single first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['WAGE_US_MT_UNEMP'], salary) + self.assertPayrollEqual(cats['ER_US_MT_UNEMP'], cats['WAGE_US_MT_UNEMP'] * self.MT_UNEMP) + + # Note!! + # The example calculation uses A = 16 but the actual table describes this as A = 18 + mt_taxable_income = salary - (73.0 * mt_mw4_exemptions) + mt_withhold = round(18 + (0.06 * (mt_taxable_income - 577))) + self.assertPayrollEqual(mt_taxable_income, 2804.0) + self.assertPayrollEqual(mt_withhold, 152.0) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold) + + def test_2019_taxes_three(self): + # Payroll Period Weekly example + salary = 135 + mt_mw4_exemptions = 1 + + employee = self._createEmployee() + contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_mt_hr_payroll.hr_payroll_salary_structure_us_mt_employee'), + schedule_pay='weekly') + contract.mt_mw4_exemptions = mt_mw4_exemptions + + self._log('2019 Montana tax single first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + + self.assertPayrollEqual(cats['WAGE_US_MT_UNEMP'], salary) + self.assertPayrollEqual(cats['ER_US_MT_UNEMP'], cats['WAGE_US_MT_UNEMP'] * self.MT_UNEMP) + + mt_taxable_income = salary - (37.0 * mt_mw4_exemptions) + mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0))) + self.assertPayrollEqual(mt_taxable_income, 98.0) + self.assertPayrollEqual(mt_withhold, 2.0) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold) + + def test_2019_taxes_three_exempt(self): + # Payroll Period Weekly example + salary = 135 + mt_mw4_exemptions = 1 + + employee = self._createEmployee() + contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_mt_hr_payroll.hr_payroll_salary_structure_us_mt_employee'), + schedule_pay='weekly') + contract.mt_mw4_exemptions = mt_mw4_exemptions + contract.mt_mw4_exempt = 'reserve' + + self._log('2019 Montana tax single first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31') + + payslip.compute_sheet() + + cats = self._getCategories(payslip) + self.assertPayrollEqual(cats.get('EE_US_MT_INC_WITHHOLD', 0.0), 0.0) + + def test_2019_taxes_three_additional(self): + # Payroll Period Weekly example + salary = 135 + mt_mw4_exemptions = 1 + mt_mw4_additional_withholding = 20.0 + + employee = self._createEmployee() + contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_mt_hr_payroll.hr_payroll_salary_structure_us_mt_employee'), + schedule_pay='weekly') + contract.mt_mw4_exemptions = mt_mw4_exemptions + contract.mt_mw4_additional_withholding = mt_mw4_additional_withholding + + self._log('2019 Montana tax single first payslip:') + payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31') + + payslip.compute_sheet() + cats = self._getCategories(payslip) + + mt_taxable_income = salary - (37.0 * mt_mw4_exemptions) + mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0))) + self.assertPayrollEqual(mt_taxable_income, 98.0) + self.assertPayrollEqual(mt_withhold, 2.0) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold + mt_mw4_additional_withholding) diff --git a/l10n_us_mt_hr_payroll/views/hr_payroll_views.xml b/l10n_us_mt_hr_payroll/views/hr_payroll_views.xml new file mode 100755 index 00000000..99f8dbac --- /dev/null +++ b/l10n_us_mt_hr_payroll/views/hr_payroll_views.xml @@ -0,0 +1,22 @@ + + + + + hr.contract.form.inherit + hr.contract + 139 + + + + + + + + + + + + + + + From 081e60ee2c399a2cf907553376390be0db923a26 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Wed, 13 Feb 2019 09:54:56 -0800 Subject: [PATCH 4/5] FIX Mississippi and Montana Withholding sign. --- l10n_us_mt_hr_payroll/data/rules.xml | 1 + l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/l10n_us_mt_hr_payroll/data/rules.xml b/l10n_us_mt_hr_payroll/data/rules.xml index 03607cef..a1292185 100755 --- a/l10n_us_mt_hr_payroll/data/rules.xml +++ b/l10n_us_mt_hr_payroll/data/rules.xml @@ -118,6 +118,7 @@ if year == 2019 or True: break result += additional +result = -result diff --git a/l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py b/l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py index 59f9a60f..c17b5364 100755 --- a/l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py +++ b/l10n_us_mt_hr_payroll/tests/test_us_mt_payslip_2019.py @@ -29,7 +29,7 @@ class TestUsMtPayslip(TestUsPayslip): mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0))) self.assertPayrollEqual(mt_taxable_income, 155.0) self.assertPayrollEqual(mt_withhold, 3.0) - self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], -mt_withhold) def test_2019_taxes_two(self): # Payroll Period Bi-Weekly example @@ -57,7 +57,7 @@ class TestUsMtPayslip(TestUsPayslip): mt_withhold = round(18 + (0.06 * (mt_taxable_income - 577))) self.assertPayrollEqual(mt_taxable_income, 2804.0) self.assertPayrollEqual(mt_withhold, 152.0) - self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], -mt_withhold) def test_2019_taxes_three(self): # Payroll Period Weekly example @@ -83,7 +83,7 @@ class TestUsMtPayslip(TestUsPayslip): mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0))) self.assertPayrollEqual(mt_taxable_income, 98.0) self.assertPayrollEqual(mt_withhold, 2.0) - self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], -mt_withhold) def test_2019_taxes_three_exempt(self): # Payroll Period Weekly example @@ -126,4 +126,4 @@ class TestUsMtPayslip(TestUsPayslip): mt_withhold = round(0 + (0.018 * (mt_taxable_income - 0))) self.assertPayrollEqual(mt_taxable_income, 98.0) self.assertPayrollEqual(mt_withhold, 2.0) - self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], mt_withhold + mt_mw4_additional_withholding) + self.assertPayrollEqual(cats['EE_US_MT_INC_WITHHOLD'], -mt_withhold + -mt_mw4_additional_withholding) From e864e0ab151c6091f23485504b110a3899acfc62 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Wed, 15 May 2019 09:44:13 -0700 Subject: [PATCH 5/5] MIG `l10n_us_mt_hr_payroll` to Odoo 12.0 --- l10n_us_mt_hr_payroll/__manifest__.py | 2 +- l10n_us_mt_hr_payroll/data/rules.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/l10n_us_mt_hr_payroll/__manifest__.py b/l10n_us_mt_hr_payroll/__manifest__.py index f19ac5e2..64883a44 100755 --- a/l10n_us_mt_hr_payroll/__manifest__.py +++ b/l10n_us_mt_hr_payroll/__manifest__.py @@ -4,7 +4,7 @@ 'license': 'AGPL-3', 'category': 'Localization', 'depends': ['l10n_us_hr_payroll'], - 'version': '11.0.2019.0.0', + 'version': '12.0.2019.0.0', 'description': """ USA::Montana Payroll Rules. =========================== diff --git a/l10n_us_mt_hr_payroll/data/rules.xml b/l10n_us_mt_hr_payroll/data/rules.xml index a1292185..37f39c47 100755 --- a/l10n_us_mt_hr_payroll/data/rules.xml +++ b/l10n_us_mt_hr_payroll/data/rules.xml @@ -12,7 +12,7 @@ code ### -year = int(payslip.dict.date_to[:4]) +year = payslip.dict.date_to.year rate = payslip.dict.get_rate('US_MT_UNEMP') ytd = payslip.sum('WAGE_US_MT_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01') ytd += contract.external_wages @@ -56,7 +56,7 @@ if result_rate == 0.0: result = not contract.mt_mw4_exempt code -year = int(payslip.dict.date_to[:4]) +year = payslip.dict.date_to.year G = categories.GROSS N = contract.mt_mw4_exemptions additional = contract.mt_mw4_additional_withholding