diff --git a/l10n_us_sc_hr_payroll/data/rules.xml b/l10n_us_sc_hr_payroll/data/rules.xml
index 95a63a90..eaa9f1f7 100644
--- a/l10n_us_sc_hr_payroll/data/rules.xml
+++ b/l10n_us_sc_hr_payroll/data/rules.xml
@@ -56,7 +56,53 @@ if result_rate == 0.0:
result = True
code
-result = categories.EE_US_FED_INC_WITHHOLD
+wages = categories.GROSS # Must make sure sequence is after the GROSS sequence.
+# Step 1 - Convert wages to annual amount
+pay_period = 0.0
+pay_periods = {
+ 'weekly': 52.0,
+ 'bi-weekly': 26.0,
+ 'semi-monthly': 24.0,
+ 'monthly': 12.0
+ }
+schedule_pay = contract.schedule_pay
+pay_period = pay_periods[schedule_pay]
+annual_wages = wages * pay_period
+
+# Step 2 - Deduct the personal exemption amount and standard deduction from the annual wages.
+sc_exemption_amount = 2510.00
+personal_exemption_amt = contract.w4_allowances * sc_exemption_amount
+standard_deduction = 0.00
+if contract.w4_allowances > 0:
+ if annual_wages > 3470.00:
+ standard_deduction = (10.0 / 100.0) * 3470.00
+ else:
+ standard_deduction = (10.0 / 100.0) * annual_wages
+taxable_income = annual_wages - personal_exemption_amt - standard_deduction
+
+# Step 3 - Use the balance (taxable_income) in the tables listed on the state pdf:
+# https://dor.sc.gov/forms-site/Forms/WH1603F_2019.pdf
+# to calculate the tax.
+
+tax_table = [
+ (2450, 1.1, 0.0),
+ (4900, 3.0, 26.95),
+ (7350, 4.0, 100.45),
+ (9800, 5.0, 198.45),
+ (12250, 6.0, 320.95),
+ (float('inf'), 7.0, 467.95)
+ ]
+last = 0.0
+for cap, rate, flat_amt in tax_table:
+ if cap > taxable_income:
+ result = ((taxable_income - last) * (rate / 100.0)) + flat_amt
+ break
+ last = cap
+
+# Step 4 - Divide the result by the number of pay periods to get withholding amount per pay period
+result = (result / pay_period)
+
+result = -result
diff --git a/l10n_us_sc_hr_payroll/tests/test_us_sc_payslip_2019.py b/l10n_us_sc_hr_payroll/tests/test_us_sc_payslip_2019.py
index 06874b93..a7457dd3 100644
--- a/l10n_us_sc_hr_payroll/tests/test_us_sc_payslip_2019.py
+++ b/l10n_us_sc_hr_payroll/tests/test_us_sc_payslip_2019.py
@@ -3,19 +3,39 @@ from odoo.addons.l10n_us_hr_payroll.models.l10n_us_hr_payroll import USHrContrac
class TestUsSCPayslip(TestUsPayslip):
- ###
+
# Taxes and Rates
- ###
SC_UNEMP_MAX_WAGE = 14000.0
US_SC_UNEMP = -1.09 / 100
+ US_SC_exemption_amount = 2510.00
- def test_2019_taxes(self):
- salary = 10000
+ def test_2019_taxes_weekly(self):
+ # We will hand calculate the amount to test for state withholding.
+ schedule_pay = 'weekly'
+ salary = 50000.00 # Employee is paid 50000 per week to be in top tax bracket
exemptions = 2
+ # Calculate annual wages
+ annual = 50000 * 52.0
+ # From our annual we deduct personal exemption amounts.
+ # We deduct 2510.00 per exemption. Since we have two exemptions:
+ personal_exemption = self.US_SC_exemption_amount * exemptions # 5020.0
+ # From annual, we will also deduct a standard_deduction of 10% up to 3470.00 if 1 or more exemptions
+ standard_deduction = (10.0 / 100.0) * 3470.00
+ taxable_income = annual - personal_exemption - standard_deduction # 2594633.0
+ # We then calculate the amounts off the SC tax pdf tables.
+ # 2594633.0 is in the highest bracket
+ test_amt = ((taxable_income - 12250) * (7.0 / 100.0)) + 467.95
+ # test_amt = 181234.76000000004
+ # Make it per period then negative
+ test_amt = (test_amt / 52.0) # Divided by 52 since it is weekly.
+ # test_amt = 3485.2838461538468
+ test_amt = -test_amt
employee = self._createEmployee()
- contract = self._createContract(employee, salary,
- struct_id=self.ref('l10n_us_sc_hr_payroll.hr_payroll_salary_structure_us_sc_employee'))
+ contract = self._createContract(employee,
+ salary,
+ struct_id=self.ref('l10n_us_sc_hr_payroll.hr_payroll_salary_structure_us_sc_employee'),
+ schedule_pay=schedule_pay)
contract.w4_allowances = exemptions
self._log('2019 South Carolina tax first payslip:')
@@ -23,34 +43,32 @@ class TestUsSCPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['WAGE_US_SC_UNEMP'], salary)
+ self.assertPayrollEqual(cats['WAGE_US_SC_UNEMP'], self.SC_UNEMP_MAX_WAGE)
self.assertPayrollEqual(cats['ER_US_SC_UNEMP'], cats['WAGE_US_SC_UNEMP'] * self.US_SC_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SC_INC_WITHHOLD'], cats['EE_US_FED_INC_WITHHOLD'])
+ self.assertPayrollEqual(cats['EE_US_SC_INC_WITHHOLD'], test_amt)
process_payslip(payslip)
- remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - salary if (self.SC_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
+ remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - annual if (annual < self.SC_UNEMP_MAX_WAGE) \
+ else 0.00
- # Testing Additional Income Tax Withholding
self._log('2019 South Carolina tax second payslip:')
- additional_wh = 40.0
- contract.w4_additional_withholding = additional_wh
- original_witholding = cats['EE_US_SC_INC_WITHHOLD']
+
payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['WAGE_US_SC_UNEMP'], remaining_SC_UNEMP_wages)
+ self.assertEqual(0.0, remaining_SC_UNEMP_wages)
self.assertPayrollEqual(cats['ER_US_SC_UNEMP'], remaining_SC_UNEMP_wages * self.US_SC_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SC_INC_WITHHOLD'], original_witholding - additional_wh)
def test_2019_taxes_with_external(self):
salary = 5000.0
external_wages = 30000.0
employee = self._createEmployee()
- contract = self._createContract(employee, salary, external_wages=external_wages,
+ contract = self._createContract(employee,
+ salary,
+ external_wages=external_wages,
struct_id=self.ref('l10n_us_sc_hr_payroll.hr_payroll_salary_structure_us_sc_employee'))
self._log('2019 South Carolina_external tax first payslip:')
@@ -62,14 +80,25 @@ class TestUsSCPayslip(TestUsPayslip):
self.assertPayrollEqual(cats['ER_US_SC_UNEMP'], cats['WAGE_US_SC_UNEMP'] * self.US_SC_UNEMP)
def test_2019_taxes_filing_status(self):
- salary = 4000.0
+ salary = 20000.00 # Wages per pay period
+ schedule_pay = 'monthly'
+ annual = salary * 12
w4_filing_status = 'married'
-
allowances = 1
+ # Hand Calculations
+ personal_exemption = 2510.00
+ standard_deduction = (10.0 / 100.0) * 3470.00
+ taxable = annual - personal_exemption - standard_deduction
+ # taxable = 237143.0
+ test_amt = ((taxable - 12250) * (7.0 / 100.0)) + 467.95 # 16210.460000000003
+ test_amt = test_amt / 12.0 # Put it into monthly -> 1350.871666666667
+ # Make it negative
+ test_amt = -test_amt
employee = self._createEmployee()
- contract = self._createContract(employee, salary, struct_id=self.ref(
- 'l10n_us_sc_hr_payroll.hr_payroll_salary_structure_us_sc_employee'))
+ contract = self._createContract(employee,
+ salary,
+ struct_id=self.ref('l10n_us_sc_hr_payroll.hr_payroll_salary_structure_us_sc_employee'))
contract.w4_allowances = allowances
contract.w4_filing_status = w4_filing_status
@@ -78,58 +107,21 @@ class TestUsSCPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['WAGE_US_SC_UNEMP'], salary)
+ self.assertPayrollEqual(cats['WAGE_US_SC_UNEMP'], self.SC_UNEMP_MAX_WAGE)
self.assertPayrollEqual(cats['ER_US_SC_UNEMP'], cats['WAGE_US_SC_UNEMP'] * self.US_SC_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SC_INC_WITHHOLD'], cats['EE_US_FED_INC_WITHHOLD'])
+ self.assertPayrollEqual(cats['EE_US_SC_INC_WITHHOLD'], test_amt)
process_payslip(payslip)
# Create a 2nd payslip
- remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - salary if (self.SC_UNEMP_MAX_WAGE - 2*salary < salary) \
- else salary
+ remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - salary if (salary < self.SC_UNEMP_MAX_WAGE) \
+ else 0.00
self._log('2019 South Carolina 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_SC_UNEMP'], remaining_SC_UNEMP_wages)
+ self.assertEqual(0.0, remaining_SC_UNEMP_wages)
self.assertPayrollEqual(cats['ER_US_SC_UNEMP'], remaining_SC_UNEMP_wages * self.US_SC_UNEMP)
- def test_additional_withholding(self):
- salary = 4000.0
- additional_wh = 40.0
- allowances = 1
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- salary,
- struct_id=self.ref('l10n_us_sc_hr_payroll.hr_payroll_salary_structure_us_sc_employee')
- )
- contract.w4_addition_withholding = additional_wh
- contract.w4_allowances = allowances
-
-
- self._log('2019 South Carolina 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_SC_UNEMP'], salary)
- self.assertPayrollEqual(cats['ER_US_SC_UNEMP'], cats['WAGE_US_SC_UNEMP'] * self.US_SC_UNEMP)
- self.assertPayrollEqual(cats['EE_US_SC_INC_WITHHOLD'], cats['EE_US_FED_INC_WITHHOLD'])
-
- process_payslip(payslip)
-
- # Make a new payslip, this one will have maximums
- remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - salary if (self.SC_UNEMP_MAX_WAGE - 2 * salary < salary) \
- else salary
-
- self._log('2019 South Carolina tax second payslip weekly:')
- payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
-
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['WAGE_US_SC_UNEMP'], remaining_SC_UNEMP_wages)
- self.assertPayrollEqual(cats['ER_US_SC_UNEMP'], remaining_SC_UNEMP_wages * self.US_SC_UNEMP)