diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py
index f331885a..9b6f177c 100755
--- a/l10n_us_hr_payroll/__manifest__.py
+++ b/l10n_us_hr_payroll/__manifest__.py
@@ -29,8 +29,11 @@ USA Payroll Rules.
'data/federal/fed_941_fit_parameters.xml',
'data/federal/fed_941_fit_rules.xml',
'data/state/ak_alaska.xml',
+ 'data/state/al_alabama.xml',
'data/state/ar_arkansas.xml',
'data/state/az_arizona.xml',
+ 'data/state/ca_california.xml',
+ 'data/state/ct_connecticut.xml',
'data/state/fl_florida.xml',
'data/state/ga_georgia.xml',
'data/state/il_illinois.xml',
diff --git a/l10n_us_hr_payroll/data/final.xml b/l10n_us_hr_payroll/data/final.xml
index 4b1a0afc..5dd52d01 100644
--- a/l10n_us_hr_payroll/data/final.xml
+++ b/l10n_us_hr_payroll/data/final.xml
@@ -19,12 +19,23 @@
ref('hr_payroll_rule_er_us_ak_suta'),
ref('hr_payroll_rule_ee_us_ak_suta'),
+ ref('hr_payroll_rule_er_us_al_suta'),
+ ref('hr_payroll_rule_ee_us_al_sit'),
+
ref('hr_payroll_rule_er_us_ar_suta'),
ref('hr_payroll_rule_ee_us_ar_sit'),
ref('hr_payroll_rule_er_us_az_suta'),
ref('hr_payroll_rule_ee_us_az_sit'),
+ ref('hr_payroll_rule_er_us_ca_suta'),
+ ref('hr_payroll_rule_er_us_ca_suta_ett'),
+ ref('hr_payroll_rule_ee_us_ca_suta_sdi'),
+ ref('hr_payroll_rule_ee_us_ca_sit'),
+
+ ref('hr_payroll_rule_er_us_ct_suta'),
+ ref('hr_payroll_rule_ee_us_ct_sit'),
+
ref('hr_payroll_rule_er_us_fl_suta'),
ref('hr_payroll_rule_er_us_ga_suta'),
diff --git a/l10n_us_hr_payroll/data/state/al_alabama.xml b/l10n_us_hr_payroll/data/state/al_alabama.xml
new file mode 100644
index 00000000..51310ccc
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/al_alabama.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+ US AL Alabama SUTA Wage Base
+ us_al_suta_wage_base
+ 8000.0
+
+
+
+ US AL Alabama SUTA Wage Base
+ us_al_suta_wage_base
+ 8000.0
+
+
+
+
+
+
+
+ US AL Alabama SUTA Rate
+ us_al_suta_rate
+ 2.7
+
+
+
+ US AL Alabama SUTA Rate
+ us_al_suta_rate
+ 2.7
+
+
+
+
+
+
+ US AL Alabama SIT Tax Rate
+ us_al_sit_tax_rate
+ {
+ '0': [(500, 2),( 3000, 4),('inf', 5)],
+ 'M': [( 1000, 2),( 6000, 4),('inf', 5)],
+ }
+
+
+
+ US AL Alabama SIT Tax Rate
+ us_al_sit_tax_rate
+ {
+ '0' : [(500, 2),(2500, 4),('inf', 5)],
+ 'M': [(1000, 2),(5000, 4),('inf', 5)],
+ }
+
+
+
+
+
+
+ US AL Alabama Dependent Rate
+ us_al_sit_dependent_rate
+ [
+ ( 1000, 20000),
+ ( 500, 100000),
+ ( 300, 'inf'),
+ ]
+
+
+
+ US AL Alabama Dependent Rate
+ us_al_sit_dependent_rate
+ [
+ ( 1000, 20000),
+ ( 500, 100000),
+ ( 300, 'inf'),
+ ]
+
+
+
+
+
+
+ US AL Alabama Standard Deduction Rate
+ us_al_sit_standard_deduction_rate
+ {
+ '0': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'S': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'MS': ((10749.0, 3750.0), (15500.0, 3750.0, 88.0, 250.0), ('inf', 2000.0)),
+ 'M': ((23499.0, 7500.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ 'H': ((23499.0, 4700.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ }
+
+
+
+ US AL Alabama Standard Deduction Rate
+ us_al_sit_standard_deduction_rate
+ {
+ '0': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'S': ((23499.0, 2500.0), (33000.0, 2500.0, 25.0, 500.0), ('inf', 2000.0)),
+ 'MS': ((10749.0, 3750.0), (15500.0, 3750.0, 88.0, 250.0), ('inf', 2000.0)),
+ 'M': ((23499.0, 7500.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ 'H': ((23499.0, 4700.0), (33000.0, 7500.0, 175.0, 500.0), ('inf', 4000.0)),
+ }
+
+
+
+
+
+
+ US AL Alabama Personal Exemption Rate
+ us_al_sit_personal_exemption_rate
+ {
+ '0' : 0,
+ 'S' : 1500,
+ 'MS': 1500,
+ 'M' : 3000,
+ 'H' : 3000,
+ }
+
+
+
+ US AL Alabama Personal Exemption Rate
+ us_al_sit_personal_exemption_rate
+ {
+ '0' : 0,
+ 'S' : 1500,
+ 'MS': 1500,
+ 'M' : 3000,
+ 'H' : 3000,
+ }
+
+
+
+
+
+
+ US Alabama - Department of Economic Security (IDES) - Unemployment Tax
+
+
+ US Alabama - Department of Economic Security (IDES) - Unemployment Tax
+
+
+
+
+ US Alabama - Department of Revenue (IDOR) - Income Tax
+
+
+ US Alabama - Department of Revenue (IDOR) - Unemployment Tax
+
+
+
+
+
+
+
+
+
+ ER: US AL Alabama State Unemployment
+ ER_US_AL_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_al_suta_wage_base', rate='us_al_suta_rate', state_code='AL')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_al_suta_wage_base', rate='us_al_suta_rate', state_code='AL')
+
+
+
+
+
+
+
+ EE: US AL Alabama State Income Tax Withholding
+ EE_US_AL_SIT
+ python
+ result, _ = al_alabama_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = al_alabama_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ca_california.xml b/l10n_us_hr_payroll/data/state/ca_california.xml
new file mode 100644
index 00000000..4631b2c9
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ca_california.xml
@@ -0,0 +1,336 @@
+
+
+
+
+
+ US CA California SUTA Wage Base
+ us_ca_suta_wage_base
+ 7000.0
+
+
+
+ US CA California SUTA Wage Base
+ us_ca_suta_wage_base
+ 7000.0
+
+
+
+
+
+
+
+ US CA California SUTA Rate
+ us_ca_suta_rate
+ 3.5
+
+
+
+ US CA California SUTA Rate
+ us_ca_suta_rate
+ 3.4
+
+
+
+
+
+
+
+ US CA California SUTA ETT Rate
+ us_ca_suta_ett_rate
+ 0.1
+
+
+
+ US CA California SUTA ETT Rate
+ us_ca_suta_ett_rate
+ 0.1
+
+
+
+
+
+
+
+ US CA California SUTA SDI Rate
+ us_ca_suta_sdi_rate
+ 1.0
+
+
+
+ US CA California SUTA SDI Rate
+ us_ca_suta_sdi_rate
+ 1.0
+
+
+
+
+
+
+ US CA California SIT Tax Rate
+ us_ca_sit_tax_rate
+ {
+ 'head_household': {
+ 'weekly': ((316, 0.011, 0.0), (750, 0.022, 3.48), (967, 0.044, 13.03), (1196, 0.066, 22.58), (1413, 0.088, 37.69), (7212, 0.1023, 56.79), (8654, 0.1133, 650.03), (14423, 0.1243, 813.41), (19231, 0.1353, 1530.50), ('inf', 0.1463, 2181.02)),
+ 'bi-weekly': ((632, 0.011, 0.0), (1500, 0.022, 6.95), (1934, 0.044, 26.05), (2392, 0.066, 45.15), (2826, 0.088, 75.38), (14424, 0.1023, 113.57), (17308, 0.1133, 1300.05), (28846, 0.1243, 1626.81), (38462, 0.1353, 3060.98), ('inf', 0.1463, 4362.02)),
+ 'semi-monthly': ((686, 0.011, 0.0), (1625, 0.022, 7.55), (2094, 0.044, 28.21), (2592, 0.066, 48.85), (3062, 0.088, 81.72), (15625, 0.1023, 123.08), (18750, 0.1133, 1408.27), (31250, 0.1243, 1762.33), (41667, 0.1353, 3316.08), ('inf', 0.1463, 4725.50)),
+ 'monthly': ((1372, 0.011, 0.0), (3250, 0.022, 15.09), (4188, 0.044, 56.41), (5184, 0.066, 97.68), (6124, 0.088, 163.42), (31250, 0.1023, 246.148), (37500, 0.1133, 2816.53), (62500, 0.1243, 3524.66), (83334, 0.1353, 6632.16), ('inf', 0.1463, 9451.00)),
+ 'quarterly': ((4114, 0.011, 0.0), (9748, 0.022, 45.25), (12566, 0.044, 169.20), (15552, 0.066, 293.19), (18369, 0.088, 490.27), (93751, 0.1023, 738.17), (112501, 0.1133, 8449.75), (187501, 0.1243, 10574.13), (250000, 0.1353, 19896.63), ('inf', 0.1463, 28352.74)),
+ 'semi-annual': ((8228, 0.011, 0.0), (19496, 0.022, 90.51), (25132, 0.044, 338.41), (31104, 0.066, 586.39), (36738, 0.088, 980.54), (187502, 0.1023, 1476.33), (225002, 0.1133, 16899.49), (375002, 0.1243, 21148.24), (500000, 0.1353, 39793.24), ('inf', 0.1463, 56705.47)),
+ 'annually': ((16457, 0.011, 0.0), (38991, 0.022, 181.03), (50264, 0.044, 676.78), (62206, 0.066, 1172.79), (73477, 0.088, 1960.96), (375002, 0.1023, 2952.81), (450003, 0.1133, 33798.82), (750003, 0.1243, 42296.43), (1000000, 0.1353, 79586.43), ('inf', 0.1463, 113411.02)),
+ },
+ 'married': {
+ 'weekly': ((316, 0.011, 0.0),(750, 0.022, 3.48),(1184, 0.044, 13.03),(1642, 0.066, 32.13), (2076, 0.088, 62.36),(10606, 0.1023, 100.55),(12726, 0.1133, 973.17),(19231, 0.1243, 1213.37),(21210, 0.1353, 2021.94),('inf', 0.1463, 2289.70)),
+ 'bi-weekly': ((632, 0.011, 0.0), (1500, 0.022, 6.95), (2368, 0.044, 26.05), (3284, 0.066, 64.24), (4152, 0.088, 124.70), (21212, 0.1023, 201.08), (25452, 0.1133, 1946.32), (38462, 0.1243, 2426.71), (42420, 0.1353, 4043.85), ('inf', 0.1463, 4579.37)),
+ 'semi-monthly': ((686, 0.011, 0.0), (1624, 0.022, 7.55), (2564, 0.044, 28.19), (3560, 0.066, 69.55), (4498, 0.088, 135.29), (22978, 0.1023, 217.83), (27574, 0.1133, 2108.33), (41667, 0.1243, 2629.06), (45956, 0.1353, 4380.82), ('inf', 0.1463, 4961.12)),
+ 'monthly': ((1372, 0.011, 0.0), (3248, 0.022, 15.09), (5128, 0.044, 56.36), (7120, 0.066, 139.08), (8996, 0.088, 270.55), (45956, 0.1023, 435.64), (55148, 0.1133, 4216.65), (83334, 0.1243, 5258.10), (91912, 0.1353, 8761.62), ('inf', 0.1463, 9922.22)),
+ 'quarterly': ((4112, 0.011, 0.0), (9748, 0.022, 45.23), (15384, 0.044, 169.22), (21356, 0.066, 417.20), (26990, 0.088, 811.35), (137870, 0.1023, 1307.14), (165442, 0.1133, 12650.16), (250000, 0.1243, 15774.07), (275736, 0.1353, 26284.63), ('inf', 0.1463, 29766.71)),
+ 'semi-annual': ((8224, 0.011, 0.0), (19496, 0.022, 90.46), (30768, 0.044, 338.44), (42712, 0.066, 834.41), (53980, 0.088, 1622.71), (275740, 0.1023, 2614.29), (330884, 0.1133, 25300.34), (500000, 0.1243, 31548.16), (551472, 0.1353, 52569.28), ('inf', 0.1463, 59533.44)),
+ 'annually': ((16446, 0.011, 0.0), (38990, 0.022, 180.91), (61538, 0.044, 676.88), (85422, 0.066, 1668.99), (107960, 0.088, 3245.33), (551476, 0.1023, 5228.67), (661768, 0.1133, 50600.36), (1000000, 0.1243, 63096.44), (1102946, 0.1353, 105138.68), ('inf', 0.1463, 119067.26)),
+ },
+ 'single': {
+ 'weekly': ((158, 0.011, 0.0), (375, 0.022, 1.74), (592, 0.044, 6.51), (821, 0.066, 16.06), (1038, 0.088, 31.17), (5303, 0.1023, 50.27), (6363, 0.1133, 486.58), (10605, 0.1243, 606.68), (19231, 0.1353, 1133.96), ('inf', 0.1463, 2301.06)),
+ 'bi-weekly': ((316, 0.011, 0.0), (750, 0.022, 3.48), (1184, 0.044, 13.03), (1642, 0.066, 32.13), (2076, 0.088, 62.36), (10606, 0.1023, 100.55), (12726, 0.1133, 973.17), (21210, 0.1243, 1213.37), (38462, 0.1353, 2267.93), ('inf', 0.1463, 4602.13)),
+ 'semi-monthly': ((343, 0.011, 0.0), (812, 0.022, 3.77), (1282, 0.044, 14.09), (1780, 0.066, 34.77), (2249, 0.088, 67.64), (11489, 0.1023, 108.91), (13787, 0.1133, 1054.16), (22978, 0.1243, 1314.52), (41667, 0.1353, 2456.96),('inf', 0.1463, 4985.58)),
+ 'monthly': ((686, 0.011, 0.0), (1624, 0.022, 7.55), (2564, 0.044, 28.19), (3560, 0.066, 69.55), (4498, 0.088, 135.29), (22978, 0.1023, 217.83), (27574, 0.1133, 2108.33), (45956, 0.1243, 2629.06), (83334, 0.1353, 4913.94), ('inf', 0.1463, 9971.18)),
+ 'quarterly': ((2056, 0.011, 0.0), (4874, 0.022, 22.62), (7692, 0.044, 84.62), (10678, 0.066, 208.61), (13495, 0.088, 405.69), (68935, 0.1023, 653.59), (82721, 0.1133, 6325.10), (137868, 0.1243, 7887.05), (250000, 0.1353, 14741.82), ('inf', 0.1463, 29913.28)),
+ 'semi-annual': ((4112, 0.011, 0.0), (9748, 0.022, 45.23), (15384, 0.044, 169.22), (21356, 0.066, 417.20), (26990, 0.088, 811.35), (137870, 0.1023, 1307.14), (165442, 0.1133, 12650.16), (275736, 0.1243, 15774.07), (500000, 0.1353, 29483.61), ('inf', 0.1463, 59826.53)),
+ 'annually': ((8223, 0.011, 0.0), (19495, 0.022, 90.45), (30769, 0.044, 338.43), (42711, 0.066, 834.49), (53980, 0.088, 1622.66), (275738, 0.1023, 2614.33), (330884, 0.1133, 25300.17), (551473, 0.1243, 31548.21), (1000000, 0.1353, 58967.42), ('inf', 0.1463, 119653.12)),
+ },
+ }
+
+
+
+ US CA California SIT Tax Rate
+ us_ca_sit_tax_rate
+ {
+ 'head_household': {
+ 'weekly': ((339, 0.011, 0.0), (803, 0.022, 3.73), (1035, 0.044, 13.93), (1281, 0.066, 24.15), (1514, 0.088, 40.39), (7725, 0.1023, 60.89), (9270, 0.1133, 696.28), (15450, 0.1243, 871.33), (19231, 0.1353, 1639.50), ('inf', 0.1463, 2151.07)),
+ 'bi-weekly': ((678, 0.011, 0.0), (1606, 0.022, 7.46), (2070, 0.044, 27.88), (2562, 0.066, 48.30), (3028, 0.088, 80.77), (15450, 0.1023, 121.78), (18540, 0.1133, 1392.55), (30900, 0.1243, 1742.65), (38462, 0.1353, 3279.00), ('inf', 0.1463, 4302.14)),
+ 'semi-monthly': ((735, 0.011, 0.0), (1740, 0.022, 8.09), (2243, 0.044, 30.20), (2777, 0.066, 52.33), (3280, 0.088, 87.57), (16738, 0.1023, 131.83), (20085, 0.1133, 1508.58), (33475, 0.1243, 1887.80), (41667, 0.1353, 3552.18), ('inf', 0.1463, 4660.56)),
+ 'monthly': ((1470, 0.011, 0.0), (3480, 0.022, 16.17), (4486, 0.044, 60.39), (5554, 0.066, 104.65), (6560, 0.088, 175.14), (33476, 0.1023, 263.67), (40170, 0.1133, 3017.18), (66950, 0.1243, 3775.61), (83334, 0.1353, 7104.36), ('inf', 0.1463, 9321.12)),
+ 'quarterly': ((4407, 0.011, 0.0), (10442, 0.022, 48.48), (13461, 0.044, 181.25), (16659, 0.066, 314.09), (19678, 0.088, 525.16), (100426, 0.1023, 790.83), (120512, 0.1133, 9051.35), (200853, 0.1243, 11327.09), (250000, 0.1353, 21313.48), ('inf', 0.1463, 27963.07)),
+ 'semi-annual': ((8814, 0.011, 0.0), (20884, 0.022, 96.95), (26922, 0.044, 362.49), (33318, 0.066, 628.16), (39356, 0.088, 1050.30), (200852, 0.1023, 1581.64), (241024, 0.1133, 18102.68), (401706, 0.1243, 22654.17), (500000, 0.1353, 42626.94), ('inf', 0.1463, 55926.12)),
+ 'annually': ((17629, 0.011, 0.0), (41768, 0.022, 193.92), (53843, 0.044, 724.98), (66636, 0.066, 1256.28), (78710, 0.088, 2100.62), (401705, 0.1023, 3163.13), (482047, 0.1133, 36205.52), (803410, 0.1243, 45308.27), (1000000, 0.1353, 85253.69), ('inf', 0.1463, 111852.32)),
+ },
+ 'married': {
+ 'weekly': ((338, 0.011, 0.0),(804, 0.022, 3.72),(1268, 0.044, 13.97),(1760, 0.066, 34.39), (2224, 0.088, 66.86),(11360, 0.1023, 107.69),(13632, 0.1133, 1042.30),(19231, 0.1243, 1299.72),(22721, 0.1353, 1995.68),('inf', 0.1463, 2467.88)),
+ 'bi-weekly': ((676, 0.011, 0.0), (1608, 0.022, 7.44), (2536, 0.044, 27.94), (3520, 0.066, 68.77), (4448, 0.088, 124.70), (21212, 0.1023, 201.08), (25452, 0.1133, 1946.32), (38462, 0.1243, 2426.71), (42420, 0.1353, 4043.85), ('inf', 0.1463, 4579.37)),
+ 'semi-monthly': ((734, 0.011, 0.0), (1740, 0.022, 8.07), (2746, 0.044, 30.20), (3812, 0.066, 74.46), (4818, 0.088, 144.82), (24614, 0.1023, 233.35), (29538, 0.1133, 2258.48), (41667, 0.1243, 2816.37), (49229, 0.1353, 4324.00), ('inf', 0.1463, 5347.14)),
+ 'monthly': ((1468, 0.011, 0.0), (3480, 0.022, 16.15), (5492, 0.044, 60.41), (7624, 0.066, 148.94), (9636, 0.088, 2889.65), (49228, 0.1023, 466.71), (59076, 0.1133, 4516.97), (83334, 0.1243, 5632.75), (98458, 0.1353, 8648.02), ('inf', 0.1463, 10694.30)),
+ 'quarterly': ((4404, 0.011, 0.0), (10442, 0.022, 48.44), (16480, 0.044, 181.28), (22876, 0.066, 446.95), (28912, 0.088, 869.09), (147686, 0.1023, 1400.26), (177222, 0.1133, 13550.84), (250000, 0.1243, 16897.27), (295371, 0.1353, 25943.58), ('inf', 0.1463, 32082.28)),
+ 'semi-annual': ((8808, 0.011, 0.0), (20884, 0.022, 96.89), (32960, 0.044, 362.56), (45752, 0.066, 893.90), (57824, 0.088, 1738.17), (295372, 0.1023, 2800.51), (354444, 0.1133, 27101.67), (500000, 0.1243, 33794.53), (590742, 0.1353, 51887.14), ('inf', 0.1463, 64164.53)),
+ 'annually': ((17618, 0.011, 0.0), (41766, 0.022, 193.80), (65920, 0.044, 725.06), (91506, 0.066, 1787.84), (115648, 0.088, 3476.52), (590746, 0.1023, 5601.02), (708890, 0.1133, 54203.55), (1000000, 0.1243, 67589.27), (1181484, 0.1353, 103774.24), ('inf', 0.1463, 128329.03)),
+ },
+ 'single': {
+ 'weekly': ((169, 0.011, 0.0), (402, 0.022, 1.86), (634, 0.044, 6.99), (880, 0.066, 17.20), (1112, 0.088, 33.44), (5680, 0.1023, 53.86), (6816, 0.1133, 521.17), (11360, 0.1243, 649.88), (19231, 0.1353, 1214.70), ('inf', 0.1463, 2279.65)),
+ 'bi-weekly': ((338, 0.011, 0.0), (804, 0.022, 3.72), (1268, 0.044, 13.97), (1760, 0.066, 34.39), (2224, 0.088, 66.86), (11360, 0.1023, 107.69), (13632, 0.1133, 1042.30), (22720, 0.1243, 1299.72), (38462, 0.1353, 2429.36), ('inf', 0.1463, 4559.25)),
+ 'semi-monthly': ((367, 0.011, 0.0), (870, 0.022, 4.04), (1373, 0.044, 15.11), (1906, 0.066, 37.24), (2409, 0.088, 72.42), (12307, 0.1023, 116.68), (14769, 0.1133, 1129.25), (24614, 0.1243, 1408.19), (41667, 0.1353, 2631.92),('inf', 0.1463, 4939.19)),
+ 'monthly': ((734, 0.011, 0.0), (1740, 0.022, 8.07), (2746, 0.044, 30.20), (3812, 0.066, 74.46), (4818, 0.088, 144.82), (24614, 0.1023, 233.35), (29538, 0.1133, 2258.48), (49228, 0.1243, 2816.37), (83334, 0.1353, 5263.84), ('inf', 0.1463, 9878.38)),
+ 'quarterly': ((2202, 0.011, 0.0), (5221, 0.022, 24.22), (8240, 0.044, 90.64), (11438, 0.066, 223.48), (14456, 0.088, 434.55), (73843, 0.1023, 700.13), (88611, 0.1133, 6775.42), (147686, 0.1243, 8448.63), (250000, 0.1353, 15791.65), ('inf', 0.1463, 29634.73)),
+ 'semi-annual': ((4404, 0.011, 0.0), (10442, 0.022, 48.44), (16480, 0.044, 181.28), (22876, 0.066, 446.95), (28912, 0.088, 869.09), (147686, 0.1023, 1400.26), (177222, 0.1133, 13550.84), (295372, 0.1243, 16897.27), (500000, 0.1353, 31583.32), ('inf', 0.1463, 59269.49)),
+ 'annually': ((8809, 0.011, 0.0), (20883, 0.022, 96.90), (32960, 0.044, 362.53), (45753, 0.066, 893.92), (57824, 0.088, 1738.26), (295373, 0.1023, 2800.51), (354445, 0.1133, 27101.77), (590742, 0.1243, 33794.63), (1000000, 0.1353, 63166.35), ('inf', 0.1463, 118538.96)),
+ },
+ }
+
+
+
+
+
+
+ US CA California Low Income Exemption Rate
+ us_ca_sit_income_exemption_rate
+ {
+ 'weekly': ( 280, 280, 561, 561),
+ 'bi-weekly': ( 561, 561, 1121, 1121),
+ 'semi-monthly': ( 607, 607, 1214, 1214),
+ 'monthly': ( 1214, 1214, 2429, 2429),
+ 'quarterly': ( 3643, 3643, 7287, 7287),
+ 'semi-annual': ( 7287, 7287, 14573, 14573),
+ 'annually': (14573, 14573, 29146, 29146),
+ }
+
+
+
+ US CA California Low Income Exemption Rate
+ us_ca_sit_income_exemption_rate
+ {
+ 'weekly': ( 289, 289, 579, 579),
+ 'bi-weekly': ( 579, 579, 1157, 1157),
+ 'semi-monthly': ( 627, 627, 1253, 1253),
+ 'monthly': ( 1254, 1254, 2507, 2507),
+ 'quarterly': ( 3761, 3761, 7521, 7521),
+ 'semi-annual': ( 7521, 7521, 15042, 15042),
+ 'annually': (15042, 15042, 30083, 30083),
+ }
+
+
+
+
+
+
+ US CA California Estimated Deduction Rate
+ us_ca_sit_estimated_deduction_rate
+ {
+ 'weekly': ( 19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
+ 'bi-weekly': ( 38, 77, 115, 154, 192, 231, 269, 308, 346, 385),
+ 'semi-monthly': ( 42, 83, 125, 167, 208, 250, 292, 333, 375, 417),
+ 'monthly': ( 83, 167, 250, 333, 417, 500, 583, 667, 750, 833),
+ 'quarterly': ( 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500),
+ 'semi-annual': ( 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000),
+ 'annually': (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
+ }
+
+
+
+ US CA California Estimated Deduction Rate
+ us_ca_sit_estimated_deduction_rate
+ {
+ 'weekly': ( 19, 38, 58, 77, 96, 115, 135, 154, 173, 192),
+ 'bi-weekly': ( 38, 77, 115, 154, 192, 231, 269, 308, 346, 385),
+ 'semi-monthly': ( 42, 83, 125, 167, 208, 250, 292, 333, 375, 417),
+ 'monthly': ( 83, 167, 250, 333, 417, 500, 583, 667, 750, 833),
+ 'quarterly': ( 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500),
+ 'semi-annual': ( 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000),
+ 'annually': (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
+ }
+
+
+
+
+
+
+ US CA California Standard Deduction Rate
+ us_ca_sit_standard_deduction_rate
+ {
+ 'weekly': ( 85, 85, 169, 169),
+ 'bi-weekly': ( 169, 169, 339, 339),
+ 'semi-monthly': ( 183, 183, 367, 367),
+ 'monthly': ( 367, 367, 734, 734),
+ 'quarterly': (1100, 1100, 2201, 2201),
+ 'semi-annual': (2201, 2201, 4401, 4401),
+ 'annually': (4401, 4401, 8802, 8802),
+ }
+
+
+
+ US CA California Standard Deduction Rate
+ us_ca_sit_standard_deduction_rate
+ {
+ 'weekly': ( 87, 87, 175, 175),
+ 'bi-weekly': ( 175, 175, 349, 349),
+ 'semi-monthly': ( 189, 189, 378, 378),
+ 'monthly': ( 378, 378, 756, 756),
+ 'quarterly': (1134, 1134, 2269, 2269),
+ 'semi-annual': (2269, 2269, 4537, 4537),
+ 'annually': (4537, 4537, 9074, 9074),
+ }
+
+
+
+
+
+
+ US CA California Exemption Allowance Rate
+ us_ca_sit_exemption_allowance_rate
+ {
+ 'weekly': ( 2.41, 4.82, 7.23, 9.65, 12.06, 14.47, 16.88, 19.29, 21.70, 24.12),
+ 'bi-weekly': ( 4.82, 9.65, 14.47, 19.29, 24.12, 28.94, 33.76, 38.58, 43.41, 48.23),
+ 'semi-monthly': ( 5.23, 10.45, 15.68, 20.90, 26.13, 31.35, 36.58, 41.80, 47.03, 52.25),
+ 'monthly': ( 10.45, 20.90, 31.35, 41.80, 52.25, 62.70, 73.15, 83.60, 94.05, 104.50),
+ 'quarterly': ( 31.35, 62.70, 94.05, 125.40, 156.75, 188.10, 219.45, 250.80, 282.15, 313.50),
+ 'semi-annual': ( 62.70, 125.40, 188.10, 250.80, 313.50, 376.20, 438.90, 501.60, 564.30, 627.00),
+ 'annually': (125.40, 250.80, 376.20, 501.60, 627.00, 752.40, 877.80, 1003.20, 1128.60, 1254.00),
+ }
+
+
+
+ US CA California Exemption Allowance Rate
+ us_ca_sit_exemption_allowance_rate
+ {
+ 'weekly': ( 2.58, 5.16, 7.74, 10.32, 12.90, 15.48, 18.07, 20.65, 23.23, 25.81),
+ 'bi-weekly': ( 5.16, 10.32, 15.48, 20.65, 25.81, 30.97, 36.13, 41.29, 46.45, 51.62),
+ 'semi-monthly': ( 5.59, 11.18, 16.78, 22.37, 27.96, 33.55, 39.14, 44.73, 50.33, 55.92),
+ 'monthly': ( 11.18, 22.37, 33.55, 44.73, 55.92, 67.10, 78.28, 89.47, 100.65, 111.83),
+ 'quarterly': ( 33.55, 67.10, 100.65, 134.20, 167.75, 201.30, 234.85, 268.40, 301.95, 335.50),
+ 'semi-annual': ( 67.10, 134.20, 201.30, 268.40, 335.50, 402.60, 469.70, 536.80, 603.90, 671.00),
+ 'annually': (134.20, 268.40, 402.60, 536.80, 671.00, 805.20, 939.40, 1073.60, 1207.80, 1342.00),
+ }
+
+
+
+
+
+
+ US California - Department of Taxation (CA DE88) - Unemployment Tax
+ 1
+
+
+ US California - Department of Taxation (CA DE88) - Unemployment Tax
+
+
+
+
+ US California - Department of Taxation - Income Tax
+ 1
+
+
+ US California - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US CA California State Unemployment
+ ER_US_CA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_rate', state_code='CA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_rate', state_code='CA')
+
+
+
+
+
+
+
+ ER: US CA California State Employee Training Tax
+ ER_US_CA_SUTA_ETT
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_ett_rate', state_code='CA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_ett_rate', state_code='CA')
+
+
+
+
+
+
+
+ EE: US CA California State Disability Insurance
+ EE_US_CA_SUTA_SDI
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_sdi_rate', state_code='CA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ca_suta_wage_base', rate='us_ca_suta_sdi_rate', state_code='CA')
+
+
+
+
+
+
+
+ EE: US CA California State Income Tax Withholding
+ EE_US_CA_SIT
+ python
+ result, _ = ca_california_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ca_california_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ct_connecticut.xml b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
new file mode 100644
index 00000000..800ba3e3
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
@@ -0,0 +1,1212 @@
+
+
+
+
+
+ US CT Connecticut SUTA Wage Base
+ us_ct_suta_wage_base
+ 15000.0
+
+
+
+ US CT Connecticut SUTA Wage Base
+ us_ct_suta_wage_base
+ 15000.0
+
+
+
+
+
+
+
+ US CT Connecticut SUTA Rate
+ us_ct_suta_rate
+ 3.4
+
+
+
+ US CT Connecticut SUTA Rate
+ us_ct_suta_rate
+ 3.2
+
+
+
+
+
+
+ US CT Connecticut SIT Initial Tax Rate
+ us_ct_sit_initial_tax_rate
+ {
+ 'a': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'b': [
+ ( 16000, 0, 3.00),
+ ( 80000, 480, 5.00),
+ (160000, 3680, 5.50),
+ (320000, 8080, 6.00),
+ (400000, 17680, 6.50),
+ (800000, 22880, 6.90),
+ ( 'inf', 50480, 6.99),
+ ],
+ 'c': [
+ ( 20000, 0, 3.00),
+ ( 100000, 600, 5.00),
+ ( 200000, 4600, 5.50),
+ ( 400000, 10100, 6.00),
+ ( 500000, 22100, 6.50),
+ (1000000, 28600, 6.90),
+ ( 'inf', 63100, 6.99),
+ ],
+ 'd': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'f': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ }
+
+
+
+ US CT Connecticut SIT Initial Tax Rate
+ us_ct_sit_initial_tax_rate
+ {
+ 'a': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'b': [
+ ( 16000, 0, 3.00),
+ ( 80000, 480, 5.00),
+ (160000, 3680, 5.50),
+ (320000, 8080, 6.00),
+ (400000, 17680, 6.50),
+ (800000, 22880, 6.90),
+ ( 'inf', 50480, 6.99),
+ ],
+ 'c': [
+ ( 20000, 0, 3.00),
+ ( 100000, 600, 5.00),
+ ( 200000, 4600, 5.50),
+ ( 400000, 10100, 6.00),
+ ( 500000, 22100, 6.50),
+ (1000000, 28600, 6.90),
+ ( 'inf', 63100, 6.99),
+ ],
+ 'd': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ 'f': [
+ ( 10000, 0, 3.00),
+ ( 50000, 300, 5.00),
+ (100000, 2300, 5.50),
+ (200000, 5050, 6.00),
+ (250000, 11050, 6.50),
+ (500000, 14300, 6.90),
+ ( 'inf', 31550, 6.99),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Tax Rate
+ us_ct_sit_tax_rate
+ {
+ 'a': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'b': [
+ ( 78500, 0),
+ ( 82500, 32),
+ ( 86500, 64),
+ ( 90500, 96),
+ ( 94500, 128),
+ ( 98500, 160),
+ (102500, 192),
+ (106500, 224),
+ (110500, 256),
+ (114500, 288),
+ ( 'inf', 320),
+ ],
+ 'c': [
+ (100500, 0),
+ (105500, 40),
+ (110500, 80),
+ (115500, 120),
+ (120500, 160),
+ (125500, 200),
+ (130500, 240),
+ (135500, 280),
+ (140500, 320),
+ (145500, 360),
+ ( 'inf', 400),
+
+ ],
+ 'd': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'f': [
+ ( 56500, 0),
+ ( 61500, 20),
+ ( 66500, 40),
+ ( 71500, 60),
+ ( 76500, 80),
+ ( 81500, 100),
+ ( 86500, 120),
+ ( 91500, 140),
+ ( 96500, 160),
+ (101500, 180),
+ ( 'inf', 200),
+ ],
+ }
+
+
+
+ US CT Connecticut Tax Rate
+ us_ct_sit_tax_rate
+ {
+ 'a': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'b': [
+ ( 78500, 0),
+ ( 82500, 32),
+ ( 86500, 64),
+ ( 90500, 96),
+ ( 94500, 128),
+ ( 98500, 160),
+ (102500, 192),
+ (106500, 224),
+ (110500, 256),
+ (114500, 288),
+ ( 'inf', 320),
+ ],
+ 'c': [
+ (100500, 0),
+ (105500, 40),
+ (110500, 80),
+ (115500, 120),
+ (120500, 160),
+ (125500, 200),
+ (130500, 240),
+ (135500, 280),
+ (140500, 320),
+ (145500, 360),
+ ( 'inf', 400),
+
+ ],
+ 'd': [
+ (50250, 0),
+ (52750, 20),
+ (55250, 40),
+ (57750, 60),
+ (60250, 80),
+ (62750, 100),
+ (65250, 120),
+ (67750, 140),
+ (70250, 160),
+ (72750, 180),
+ ('inf', 200),
+ ],
+ 'f': [
+ ( 56500, 0),
+ ( 61500, 20),
+ ( 66500, 40),
+ ( 71500, 60),
+ ( 76500, 80),
+ ( 81500, 100),
+ ( 86500, 120),
+ ( 91500, 140),
+ ( 96500, 160),
+ (101500, 180),
+ ( 'inf', 200),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Decimal Rate
+ us_ct_sit_decimal_rate
+ {
+ 'a': [
+ (15000, 0.75),
+ (15500, 0.70),
+ (16000, 0.65),
+ (16500, 0.60),
+ (17000, 0.55),
+ (17500, 0.50),
+ (18000, 0.45),
+ (18500, 0.40),
+ (20000, 0.35),
+ (20500, 0.30),
+ (21000, 0.25),
+ (21500, 0.20),
+ (25000, 0.15),
+ (25500, 0.14),
+ (26000, 0.13),
+ (26500, 0.12),
+ (27000, 0.11),
+ (48000, 0.10),
+ (48500, 0.09),
+ (49000, 0.08),
+ (49500, 0.08),
+ (50000, 0.06),
+ (50500, 0.05),
+ (51000, 0.03),
+ (51500, 0.03),
+ (52000, 0.02),
+ (52500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'b': [
+ (24000, 0.75),
+ (24500, 0.70),
+ (25000, 0.65),
+ (25500, 0.60),
+ (26000, 0.55),
+ (26500, 0.50),
+ (27000, 0.45),
+ (27500, 0.40),
+ (34000, 0.35),
+ (34500, 0.30),
+ (35000, 0.25),
+ (35500, 0.20),
+ (44000, 0.15),
+ (44500, 0.14),
+ (45000, 0.13),
+ (45500, 0.12),
+ (46000, 0.11),
+ (74000, 0.10),
+ (74500, 0.09),
+ (75000, 0.08),
+ (75500, 0.08),
+ (76000, 0.06),
+ (76500, 0.05),
+ (77000, 0.03),
+ (77500, 0.03),
+ (78000, 0.02),
+ (78500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'c': [
+ (30000, 0.75),
+ (30500, 0.70),
+ (31000, 0.65),
+ (31500, 0.60),
+ (32000, 0.55),
+ (32500, 0.50),
+ (33000, 0.45),
+ (33500, 0.40),
+ (40000, 0.35),
+ (40500, 0.30),
+ (41000, 0.25),
+ (41500, 0.20),
+ (50000, 0.15),
+ (50500, 0.14),
+ (51000, 0.13),
+ (51500, 0.12),
+ (52000, 0.11),
+ (96000, 0.10),
+ (96500, 0.09),
+ (97000, 0.08),
+ (97500, 0.08),
+ (98000, 0.06),
+ (98500, 0.05),
+ (99000, 0.03),
+ (99500, 0.03),
+ (100000, 0.02),
+ (100500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'f': [
+ (18800, 0.75),
+ (19300, 0.70),
+ (19800, 0.65),
+ (20300, 0.60),
+ (20800, 0.55),
+ (21300, 0.50),
+ (21800, 0.45),
+ (22300, 0.40),
+ (25000, 0.35),
+ (25500, 0.30),
+ (26000, 0.25),
+ (26500, 0.20),
+ (31300, 0.15),
+ (31800, 0.14),
+ (32300, 0.13),
+ (32800, 0.12),
+ (33300, 0.11),
+ (60000, 0.10),
+ (60500, 0.09),
+ (61000, 0.08),
+ (61500, 0.08),
+ (62000, 0.06),
+ (62500, 0.05),
+ (63000, 0.03),
+ (63500, 0.03),
+ (64000, 0.02),
+ (64500, 0.01),
+ ('inf', 0.00),
+ ],
+ }
+
+
+
+ US CT Connecticut Decimal Rate
+ us_ct_sit_decimal_rate
+ {
+ 'a': [
+ (15000, 0.75),
+ (15500, 0.70),
+ (16000, 0.65),
+ (16500, 0.60),
+ (17000, 0.55),
+ (17500, 0.50),
+ (18000, 0.45),
+ (18500, 0.40),
+ (20000, 0.35),
+ (20500, 0.30),
+ (21000, 0.25),
+ (21500, 0.20),
+ (25000, 0.15),
+ (25500, 0.14),
+ (26000, 0.13),
+ (26500, 0.12),
+ (27000, 0.11),
+ (48000, 0.10),
+ (48500, 0.09),
+ (49000, 0.08),
+ (49500, 0.08),
+ (50000, 0.06),
+ (50500, 0.05),
+ (51000, 0.03),
+ (51500, 0.03),
+ (52000, 0.02),
+ (52500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'b': [
+ (24000, 0.75),
+ (24500, 0.70),
+ (25000, 0.65),
+ (25500, 0.60),
+ (26000, 0.55),
+ (26500, 0.50),
+ (27000, 0.45),
+ (27500, 0.40),
+ (34000, 0.35),
+ (34500, 0.30),
+ (35000, 0.25),
+ (35500, 0.20),
+ (44000, 0.15),
+ (44500, 0.14),
+ (45000, 0.13),
+ (45500, 0.12),
+ (46000, 0.11),
+ (74000, 0.10),
+ (74500, 0.09),
+ (75000, 0.08),
+ (75500, 0.08),
+ (76000, 0.06),
+ (76500, 0.05),
+ (77000, 0.03),
+ (77500, 0.03),
+ (78000, 0.02),
+ (78500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'c': [
+ (30000, 0.75),
+ (30500, 0.70),
+ (31000, 0.65),
+ (31500, 0.60),
+ (32000, 0.55),
+ (32500, 0.50),
+ (33000, 0.45),
+ (33500, 0.40),
+ (40000, 0.35),
+ (40500, 0.30),
+ (41000, 0.25),
+ (41500, 0.20),
+ (50000, 0.15),
+ (50500, 0.14),
+ (51000, 0.13),
+ (51500, 0.12),
+ (52000, 0.11),
+ (96000, 0.10),
+ (96500, 0.09),
+ (97000, 0.08),
+ (97500, 0.08),
+ (98000, 0.06),
+ (98500, 0.05),
+ (99000, 0.03),
+ (99500, 0.03),
+ (100000, 0.02),
+ (100500, 0.01),
+ ('inf', 0.00),
+ ],
+ 'f': [
+ (18800, 0.75),
+ (19300, 0.70),
+ (19800, 0.65),
+ (20300, 0.60),
+ (20800, 0.55),
+ (21300, 0.50),
+ (21800, 0.45),
+ (22300, 0.40),
+ (25000, 0.35),
+ (25500, 0.30),
+ (26000, 0.25),
+ (26500, 0.20),
+ (31300, 0.15),
+ (31800, 0.14),
+ (32300, 0.13),
+ (32800, 0.12),
+ (33300, 0.11),
+ (60000, 0.10),
+ (60500, 0.09),
+ (61000, 0.08),
+ (61500, 0.08),
+ (62000, 0.06),
+ (62500, 0.05),
+ (63000, 0.03),
+ (63500, 0.03),
+ (64000, 0.02),
+ (64500, 0.01),
+ ('inf', 0.00),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Recapture Rate
+ us_ct_sit_recapture_rate
+ {
+ 'a': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ 'b': [
+ (320000, 0),
+ (328000, 140),
+ (336000, 280),
+ (344000, 420),
+ (352000, 560),
+ (360000, 700),
+ (368000, 840),
+ (376000, 980),
+ (384000, 1120),
+ (392000, 1260),
+ (400000, 1400),
+ (408000, 1540),
+ (416000, 1680),
+ (424000, 1820),
+ (432000, 1960),
+ (440000, 2100),
+ (448000, 2240),
+ (456000, 2380),
+ (464000, 2520),
+ (472000, 2660),
+ (480000, 2800),
+ (488000, 2940),
+ (496000, 3080),
+ (504000, 3220),
+ (512000, 3360),
+ (520000, 3500),
+ (528000, 3640),
+ (536000, 3780),
+ (544000, 3920),
+ (552000, 4060),
+ (800000, 4200),
+ (808000, 4280),
+ (816000, 4360),
+ (824000, 4440),
+ (832000, 4520),
+ (840000, 4600),
+ (848000, 4680),
+ (856000, 4760),
+ (864000, 4840),
+ ( 'inf', 4920),
+ ],
+ 'c': [
+ ( 400000, 0),
+ ( 410000, 180),
+ ( 420000, 360),
+ ( 430000, 540),
+ ( 440000, 720),
+ ( 450000, 900),
+ ( 460000, 1080),
+ ( 470000, 1260),
+ ( 480000, 1440),
+ ( 490000, 1620),
+ ( 500000, 1800),
+ ( 510000, 1980),
+ ( 520000, 2160),
+ ( 530000, 2340),
+ ( 540000, 2520),
+ ( 550000, 2700),
+ ( 560000, 2880),
+ ( 570000, 3060),
+ ( 580000, 3240),
+ ( 590000, 3420),
+ ( 600000, 3600),
+ ( 610000, 3780),
+ ( 620000, 3960),
+ ( 630000, 4140),
+ ( 640000, 4320),
+ ( 650000, 4500),
+ ( 660000, 4680),
+ ( 670000, 4860),
+ ( 680000, 5040),
+ ( 690000, 5220),
+ (1000000, 5400),
+ (1010000, 5500),
+ (1020000, 5600),
+ (1030000, 5700),
+ (1040000, 5800),
+ (1050000, 5900),
+ (1060000, 6000),
+ (1070000, 6100),
+ (1080000, 6200),
+ ( 'inf', 6300),
+ ],
+ 'd': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ 'f': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ }
+
+
+
+ US CT Connecticut Recapture Rate
+ us_ct_sit_recapture_rate
+ {
+ 'a': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ 'b': [
+ (320000, 0),
+ (328000, 140),
+ (336000, 280),
+ (344000, 420),
+ (352000, 560),
+ (360000, 700),
+ (368000, 840),
+ (376000, 980),
+ (384000, 1120),
+ (392000, 1260),
+ (400000, 1400),
+ (408000, 1540),
+ (416000, 1680),
+ (424000, 1820),
+ (432000, 1960),
+ (440000, 2100),
+ (448000, 2240),
+ (456000, 2380),
+ (464000, 2520),
+ (472000, 2660),
+ (480000, 2800),
+ (488000, 2940),
+ (496000, 3080),
+ (504000, 3220),
+ (512000, 3360),
+ (520000, 3500),
+ (528000, 3640),
+ (536000, 3780),
+ (544000, 3920),
+ (552000, 4060),
+ (800000, 4200),
+ (808000, 4280),
+ (816000, 4360),
+ (824000, 4440),
+ (832000, 4520),
+ (840000, 4600),
+ (848000, 4680),
+ (856000, 4760),
+ (864000, 4840),
+ ( 'inf', 4920),
+ ],
+ 'c': [
+ ( 400000, 0),
+ ( 410000, 180),
+ ( 420000, 360),
+ ( 430000, 540),
+ ( 440000, 720),
+ ( 450000, 900),
+ ( 460000, 1080),
+ ( 470000, 1260),
+ ( 480000, 1440),
+ ( 490000, 1620),
+ ( 500000, 1800),
+ ( 510000, 1980),
+ ( 520000, 2160),
+ ( 530000, 2340),
+ ( 540000, 2520),
+ ( 550000, 2700),
+ ( 560000, 2880),
+ ( 570000, 3060),
+ ( 580000, 3240),
+ ( 590000, 3420),
+ ( 600000, 3600),
+ ( 610000, 3780),
+ ( 620000, 3960),
+ ( 630000, 4140),
+ ( 640000, 4320),
+ ( 650000, 4500),
+ ( 660000, 4680),
+ ( 670000, 4860),
+ ( 680000, 5040),
+ ( 690000, 5220),
+ (1000000, 5400),
+ (1010000, 5500),
+ (1020000, 5600),
+ (1030000, 5700),
+ (1040000, 5800),
+ (1050000, 5900),
+ (1060000, 6000),
+ (1070000, 6100),
+ (1080000, 6200),
+ ( 'inf', 6300),
+ ],
+ 'd': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ 'f': [
+ (200000, 0),
+ (205000, 90),
+ (210000, 180),
+ (215000, 270),
+ (220000, 360),
+ (225000, 450),
+ (230000, 540),
+ (235000, 630),
+ (240000, 720),
+ (245000, 810),
+ (250000, 900),
+ (255000, 990),
+ (260000, 1080),
+ (265000, 1170),
+ (270000, 1260),
+ (275000, 1350),
+ (280000, 1440),
+ (285000, 1530),
+ (290000, 1620),
+ (295000, 1710),
+ (300000, 1800),
+ (305000, 1890),
+ (310000, 1980),
+ (315000, 2070),
+ (320000, 2160),
+ (325000, 2250),
+ (330000, 2340),
+ (335000, 2430),
+ (340000, 2520),
+ (345000, 2610),
+ (500000, 2700),
+ (505000, 2750),
+ (510000, 2800),
+ (515000, 2850),
+ (520000, 2900),
+ (525000, 2950),
+ (530000, 3000),
+ (535000, 3050),
+ (540000, 3100),
+ ( 'inf', 200),
+ ],
+ }
+
+
+
+
+
+
+ US CT Connecticut Personal Exemption Rate
+ us_ct_sit_personal_exemption_rate
+ {
+ 'a' : [
+ (24000, 12000),
+ (25000, 11000),
+ (26000, 10000),
+ (27000, 9000),
+ (28000, 8000),
+ (29000, 7000),
+ (30000, 6000),
+ (31000, 5000),
+ (32000, 4000),
+ (33000, 3000),
+ (34000, 2000),
+ (35000, 1000),
+ ('inf', 0),
+ ],
+ 'b' : [
+ (38000, 19000),
+ (39000, 18000),
+ (40000, 17000),
+ (41000, 16000),
+ (42000, 15000),
+ (43000, 14000),
+ (44000, 13000),
+ (45000, 12000),
+ (46000, 11000),
+ (47000, 10000),
+ (48000, 9000),
+ (49000, 8000),
+ (50000, 7000),
+ (51000, 6000),
+ (52000, 5000),
+ (53000, 4000),
+ (54000, 3000),
+ (55000, 2000),
+ (56000, 1000),
+ ('inf', 0),
+ ],
+ 'c': [
+ (48000, 24000),
+ (49000, 23000),
+ (50000, 22000),
+ (51000, 21000),
+ (52000, 20000),
+ (53000, 19000),
+ (54000, 18000),
+ (55000, 17000),
+ (56000, 16000),
+ (57000, 15000),
+ (58000, 14000),
+ (59000, 13000),
+ (60000, 12000),
+ (61000, 11000),
+ (62000, 10000),
+ (63000, 9000),
+ (64000, 8000),
+ (65000, 7000),
+ (66000, 6000),
+ (67000, 5000),
+ (68000, 4000),
+ (69000, 3000),
+ (70000, 2000),
+ (71000, 1000),
+ ('inf', 0),
+ ],
+ 'f' : [
+ (30000, 15000),
+ (31000, 14000),
+ (22000, 13000),
+ (33000, 12000),
+ (34000, 11000),
+ (35000, 10000),
+ (36000, 9000),
+ (37000, 8000),
+ (38000, 7000),
+ (39000, 6000),
+ (40000, 5000),
+ (41000, 4000),
+ (42000, 3000),
+ (43000, 2000),
+ (44000, 1000),
+ ('inf', 0),
+ ],
+ }
+
+
+
+ US CT Connecticut Personal Exemption Rate
+ us_ct_sit_personal_exemption_rate
+ {
+ 'a' : [
+ (24000, 12000),
+ (25000, 11000),
+ (26000, 10000),
+ (27000, 9000),
+ (28000, 8000),
+ (29000, 7000),
+ (30000, 6000),
+ (31000, 5000),
+ (32000, 4000),
+ (33000, 3000),
+ (34000, 2000),
+ (35000, 1000),
+ ('inf', 0),
+ ],
+ 'b' : [
+ (38000, 19000),
+ (39000, 18000),
+ (40000, 17000),
+ (41000, 16000),
+ (42000, 15000),
+ (43000, 14000),
+ (44000, 13000),
+ (45000, 12000),
+ (46000, 11000),
+ (47000, 10000),
+ (48000, 9000),
+ (49000, 8000),
+ (50000, 7000),
+ (51000, 6000),
+ (52000, 5000),
+ (53000, 4000),
+ (54000, 3000),
+ (55000, 2000),
+ (56000, 1000),
+ ('inf', 0),
+ ],
+ 'c': [
+ (48000, 24000),
+ (49000, 23000),
+ (50000, 22000),
+ (51000, 21000),
+ (52000, 20000),
+ (53000, 19000),
+ (54000, 18000),
+ (55000, 17000),
+ (56000, 16000),
+ (57000, 15000),
+ (58000, 14000),
+ (59000, 13000),
+ (60000, 12000),
+ (61000, 11000),
+ (62000, 10000),
+ (63000, 9000),
+ (64000, 8000),
+ (65000, 7000),
+ (66000, 6000),
+ (67000, 5000),
+ (68000, 4000),
+ (69000, 3000),
+ (70000, 2000),
+ (71000, 1000),
+ ('inf', 0),
+ ],
+ 'f' : [
+ (30000, 15000),
+ (31000, 14000),
+ (22000, 13000),
+ (33000, 12000),
+ (34000, 11000),
+ (35000, 10000),
+ (36000, 9000),
+ (37000, 8000),
+ (38000, 7000),
+ (39000, 6000),
+ (40000, 5000),
+ (41000, 4000),
+ (42000, 3000),
+ (43000, 2000),
+ (44000, 1000),
+ ('inf', 0),
+ ],
+ }
+
+
+
+
+
+
+ US Connecticut - Department of Labor (CDOL) - Unemployment Tax
+
+
+ US Connecticut - Department of Labor (CDOL) - Unemployment Tax
+
+
+
+
+ US Connecticut - Department of Revenue Services (CDRS) - Income Tax
+
+
+ US Connecticut - Department of Revenue Services (CDRS) - Unemployment Tax
+
+
+
+
+
+
+
+
+
+ ER: US CT Connecticut State Unemployment
+ ER_US_CT_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ct_suta_wage_base', rate='us_ct_suta_rate', state_code='CT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ct_suta_wage_base', rate='us_ct_suta_rate', state_code='CT')
+
+
+
+
+
+
+
+ EE: US CT Connecticut State Income Tax Withholding
+ EE_US_CT_SIT
+ python
+ result, _ = ct_connecticut_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ct_connecticut_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/il_illinois.xml b/l10n_us_hr_payroll/data/state/il_illinois.xml
index 731da1fb..2dbcac77 100644
--- a/l10n_us_hr_payroll/data/state/il_illinois.xml
+++ b/l10n_us_hr_payroll/data/state/il_illinois.xml
@@ -65,6 +65,7 @@
US Illinois - Department of Economic Security (IDES) - Unemployment Tax
+ 1
US Illinois - Department of Economic Security (IDES) - Unemployment Tax
@@ -73,9 +74,10 @@
US Illinois - Department of Revenue (IDOR) - Income Tax
+ 1
- US Illinois - Department of Revenue (IDOR) - Unemployment Tax
+ US Illinois - Department of Revenue (IDOR) - Income Tax
diff --git a/l10n_us_hr_payroll/migrations/data.py b/l10n_us_hr_payroll/migrations/data.py
index ff3f606a..c971a89d 100644
--- a/l10n_us_hr_payroll/migrations/data.py
+++ b/l10n_us_hr_payroll/migrations/data.py
@@ -9,6 +9,11 @@ FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020 = {
'fica_exempt': 'fed_941_fica_exempt',
'futa_type': 'fed_940_type',
# State
+ 'al_a4_filing_status': 'al_a4_sit_exemptions',
+ 'al_a4_dependents': 'al_a4_sit_dependents',
+ 'al_a4_personal_exemption': 'state_income_tax_exempt',
+ 'al_a4_additional_wh': 'state_income_tax_additional_withholding',
+
'ar_w4_allowances': 'ar_ar4ec_sit_allowances',
'ar_w4_tax_exempt': 'state_income_tax_exempt',
'ar_w4_additional_wh': 'state_income_tax_additional_withholding',
@@ -16,6 +21,13 @@ FIELDS_CONTRACT_TO_US_PAYROLL_FORMS_2020 = {
'az_a4_withholding_percentage': 'az_a4_sit_withholding_percentage',
'az_a4_additional_withholding': 'state_income_tax_additional_withholding',
+ 'ca_de4_allowances': 'ca_de4_sit_allowances',
+ 'ca_additional_allowances': 'ca_sit_additional_allowances',
+ 'ca_de4_filing_status': 'ca_de4_sit_filing_status',
+
+ 'ct_w4na_code': 'ct_w4na_sit_code',
+ 'ct_w4na_wh_amount': 'state_income_tax_additional_withholding',
+
'ga_g4_filing_status': 'ga_g4_sit_filing_status',
'ga_g4_dependent_allowances': 'ga_g4_sit_dependent_allowances',
'ga_g4_additional_allowances': 'ga_g4_sit_additional_allowances',
@@ -84,6 +96,11 @@ XMLIDS_TO_REMOVE_2020 = [
'l10n_us_ak_hr_payroll.hr_payroll_ak_unemp_ee',
'l10n_us_ak_hr_payroll.hr_payroll_rules_ak_unemp_wages',
+ 'l10n_us_al_hr_payroll.hr_payroll_al_unemp_wages',
+ 'l10n_us_al_hr_payroll.hr_payroll_al_unemp',
+ 'l10n_us_al_hr_payroll.hr_payroll_al_income_withhold',
+ 'l10n_us_al_hr_payroll.hr_payroll_rules_al_unemp_wages',
+
'l10n_us_ar_hr_payroll.hr_payroll_ar_unemp_wages',
'l10n_us_ar_hr_payroll.hr_payroll_ar_unemp',
'l10n_us_ar_hr_payroll.hr_payroll_ar_income_withhold',
@@ -94,6 +111,24 @@ XMLIDS_TO_REMOVE_2020 = [
'l10n_us_az_hr_payroll.hr_payroll_az_income_withhold',
'l10n_us_az_hr_payroll.hr_payroll_rules_az_unemp_wages',
+ 'l10n_us_ca_hr_payroll.res_partner_cador_ett',
+ 'l10n_us_ca_hr_payroll.res_partner_cador_sdi',
+ 'l10n_us_ca_hr_payroll.hr_payroll_ca_uit_wages',
+ 'l10n_us_ca_hr_payroll.hr_payroll_ca_uit',
+ 'l10n_us_ca_hr_payroll.hr_payroll_ca_ett_wages',
+ 'l10n_us_ca_hr_payroll.hr_payroll_ca_ett',
+ 'l10n_us_ca_hr_payroll.hr_payroll_ca_sdi_wages',
+ 'l10n_us_ca_hr_payroll.hr_payroll_ca_sdi',
+ 'l10n_us_ca_hr_payroll.hr_payroll_ca_income_withhold',
+ 'l10n_us_ca_hr_payroll.hr_payroll_rules_ca_uit_wages_2018',
+ 'l10n_us_ca_hr_payroll.hr_payroll_rules_ca_ett_wages_2018',
+ 'l10n_us_ca_hr_payroll.hr_payroll_rules_ca_sdi_wages_2018',
+
+ 'l10n_us_ct_hr_payroll.hr_payroll_ct_unemp_wages',
+ 'l10n_us_ct_hr_payroll.hr_payroll_ct_unemp',
+ 'l10n_us_ct_hr_payroll.hr_payroll_ct_income_withhold',
+ 'l10n_us_ct_hr_payroll.hr_payroll_rules_ct_unemp_wages',
+
'l10n_us_fl_hr_payroll.hr_payroll_fl_unemp_wages',
'l10n_us_fl_hr_payroll.hr_payroll_fl_unemp',
'l10n_us_fl_hr_payroll.hr_payroll_rules_fl_unemp_wages_2018',
@@ -218,6 +253,13 @@ XMLIDS_TO_RENAME_2020 = {
'l10n_us_ak_hr_payroll.hr_payroll_rules_ak_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_ak_suta',
'l10n_us_ak_hr_payroll.hr_payroll_rules_ak_unemp_ee': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_ak_sit',
+ 'l10n_us_al_hr_payroll.res_partner_al_dol_unemp': 'l10n_us_hr_payroll.res_partner_us_al_dor',
+ 'l10n_us_al_hr_payroll.res_partner_al_dor_withhold': 'l10n_us_hr_payroll.res_partner_us_al_dor_sit',
+ 'l10n_us_al_hr_payroll.contrib_register_al_dol_unemp': 'l10n_us_hr_payroll.contrib_register_us_al_dor',
+ 'l10n_us_al_hr_payroll.contrib_register_al_dor_withhold': 'l10n_us_hr_payroll.contrib_register_us_al_dor_sit',
+ 'l10n_us_al_hr_payroll.hr_payroll_rules_al_unemp': 'l10n_us_hr_payroll.hr_payroll_rules_az_unemp',
+ 'l10n_us_al_hr_payroll.hr_payroll_rules_al_inc_withhold': 'l10n_us_hr_payroll.hr_payroll_rules_az_inc_withhold',
+
'l10n_us_ar_hr_payroll.res_partner_ar_dws_unemp': 'l10n_us_hr_payroll.res_partner_us_ar_dor',
'l10n_us_ar_hr_payroll.res_partner_ar_dfa_withhold': 'l10n_us_hr_payroll.res_partner_us_ar_dor_sit',
'l10n_us_ar_hr_payroll.contrib_register_ar_dws_unemp': 'l10n_us_hr_payroll.contrib_register_us_ar_dor',
@@ -232,6 +274,22 @@ XMLIDS_TO_RENAME_2020 = {
'l10n_us_az_hr_payroll.hr_payroll_az_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_az_suta',
'l10n_us_az_hr_payroll.hr_payroll_az_income_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_az_sit',
+ 'l10n_us_ca_hr_payroll.res_partner_cador_uit': 'l10n_us_hr_payroll.res_partner_us_ca_dor',
+ 'l10n_us_ca_hr_payroll.res_partner_cador_withhold': 'l10n_us_hr_payroll.res_partner_us_ca_dor_sit',
+ 'l10n_us_ca_hr_payroll.contrib_register_cador_uit': 'l10n_us_hr_payroll.contrib_register_us_ca_dor',
+ 'l10n_us_ca_hr_payroll.contrib_register_cador_withhold': 'l10n_us_hr_payroll.contrib_register_us_ca_dor_sit',
+ 'l10n_us_ca_hr_payroll.hr_payroll_rules_ca_uit_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_ca_suta',
+ 'l10n_us_ca_hr_payroll.hr_payroll_rules_ca_ett_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_ca_ett_suta',
+ 'l10n_us_ca_hr_payroll.hr_payroll_rules_ca_sdi_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_ca_sdi_sit',
+ 'l10n_us_ca_hr_payroll.hr_payroll_rules_ca_inc_withhold_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_ca_sit',
+
+ 'l10n_us_ct_hr_payroll.res_partner_ct_dol_unemp': 'l10n_us_hr_payroll.res_partner_us_ct_dor',
+ 'l10n_us_ct_hr_payroll.res_partner_ct_drs_withhold': 'l10n_us_hr_payroll.res_partner_us_ct_dor_sit',
+ 'l10n_us_ct_hr_payroll.contrib_register_ct_dol_unemp': 'l10n_us_hr_payroll.contrib_register_us_ct_dor',
+ 'l10n_us_ct_hr_payroll.contrib_register_ct_drs_withhold': 'l10n_us_hr_payroll.contrib_register_us_ct_dor_sit',
+ 'l10n_us_ct_hr_payroll.hr_payroll_rules_ct_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_ct_suta',
+ 'l10n_us_ct_hr_payroll.hr_payroll_rules_ct_inc_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_ct_sit',
+
'l10n_us_fl_hr_payroll.hr_payroll_rules_fl_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_fl_suta',
'l10n_us_fl_hr_payroll.res_partner_fldor': 'l10n_us_hr_payroll.res_partner_us_fl_dor',
'l10n_us_fl_hr_payroll.contrib_register_fldor': 'l10n_us_hr_payroll.contrib_register_us_fl_dor',
diff --git a/l10n_us_hr_payroll/models/hr_payslip.py b/l10n_us_hr_payroll/models/hr_payslip.py
index 449861d9..088bb83b 100644
--- a/l10n_us_hr_payroll/models/hr_payslip.py
+++ b/l10n_us_hr_payroll/models/hr_payslip.py
@@ -14,8 +14,11 @@ from .federal.fed_941 import ee_us_941_fica_ss, \
from .state.general import general_state_unemployment, \
general_state_income_withholding, \
is_us_state
+from .state.al_alabama import al_alabama_state_income_withholding
from .state.ar_arkansas import ar_arkansas_state_income_withholding
from .state.az_arizona import az_arizona_state_income_withholding
+from .state.ca_california import ca_california_state_income_withholding
+from .state.ct_connecticut import ct_connecticut_state_income_withholding
from .state.ga_georgia import ga_georgia_state_income_withholding
from .state.il_illinois import il_illinois_state_income_withholding
from .state.mi_michigan import mi_michigan_state_income_withholding
@@ -61,8 +64,11 @@ class HRPayslip(models.Model):
'general_state_unemployment': general_state_unemployment,
'general_state_income_withholding': general_state_income_withholding,
'is_us_state': is_us_state,
+ 'al_alabama_state_income_withholding': al_alabama_state_income_withholding,
'ar_arkansas_state_income_withholding': ar_arkansas_state_income_withholding,
'az_arizona_state_income_withholding': az_arizona_state_income_withholding,
+ 'ca_california_state_income_withholding': ca_california_state_income_withholding,
+ 'ct_connecticut_state_income_withholding': ct_connecticut_state_income_withholding,
'ga_georgia_state_income_withholding': ga_georgia_state_income_withholding,
'il_illinois_state_income_withholding': il_illinois_state_income_withholding,
'mi_michigan_state_income_withholding': mi_michigan_state_income_withholding,
diff --git a/l10n_us_hr_payroll/models/state/al_alabama.py b/l10n_us_hr_payroll/models/state/al_alabama.py
new file mode 100644
index 00000000..d34fa2ef
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/al_alabama.py
@@ -0,0 +1,77 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def al_alabama_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'AL'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ personal_exempt = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_exempt')
+ if personal_exempt:
+ return 0.0, 0.0
+
+ pay_periods = payslip.dict.get_pay_periods_in_year()
+ additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ tax_table = payslip.dict.rule_parameter('us_al_sit_tax_rate')
+ exemptions = payslip.dict.contract_id.us_payroll_config_value('al_a4_sit_exemptions')
+ dependent_rate = payslip.dict.rule_parameter('us_al_sit_dependent_rate')
+ standard_deduction = payslip.dict.rule_parameter('us_al_sit_standard_deduction_rate').get(exemptions, 0.0)
+ personal_exemption = payslip.dict.rule_parameter('us_al_sit_personal_exemption_rate').get(exemptions, 0.0)
+ dependent = payslip.dict.contract_id.us_payroll_config_value('al_a4_sit_dependents')
+ fed_withholding = categories.EE_US_941_FIT
+
+ annual_wage = wage * pay_periods
+ standard_deduction_amt = 0.0
+ personal_exemption_amt = 0.0
+ dependent_amt = 0.0
+ withholding = 0.0
+
+ if standard_deduction:
+ row = standard_deduction
+ last_amt = 0.0
+ for data in row:
+ if annual_wage < float(data[0]):
+ if len(data) > 3:
+ increment_count = (- (wage - last_amt) // data[3])
+ standard_deduction_amt = data[1] - (increment_count * data[2])
+ else:
+ standard_deduction_amt = data[1]
+ else:
+ last_amt = data[0]
+ after_deduction = annual_wage - standard_deduction_amt
+ after_fed_withholding = (fed_withholding * pay_periods) + after_deduction
+ if not personal_exempt:
+ personal_exemption_amt = personal_exemption
+ after_personal_exemption = after_fed_withholding - personal_exemption_amt
+ for row in dependent_rate:
+ if annual_wage < float(row[1]):
+ dependent_amt = row[0] * dependent
+ break
+
+ taxable_amount = after_personal_exemption - dependent_amt
+ last = 0.0
+ tax_table = tax_table['M'] if exemptions == 'M' else tax_table['0']
+ for row in tax_table:
+ if taxable_amount < float(row[0]):
+ withholding = withholding + ((taxable_amount - last) * (row[1] / 100))
+ break
+ withholding = withholding + ((row[0] - last) * (row[1] / 100))
+ last = row[0]
+
+ if withholding < 0.0:
+ withholding = 0.0
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ca_california.py b/l10n_us_hr_payroll/models/state/ca_california.py
new file mode 100644
index 00000000..e2bb5b23
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ca_california.py
@@ -0,0 +1,98 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+MAX_ALLOWANCES = 10
+
+
+def ca_california_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+
+ state_code = 'CA'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ filing_status = payslip.dict.contract_id.us_payroll_config_value('ca_de4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ schedule_pay = payslip.dict.contract_id.schedule_pay
+ additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ sit_allowances = payslip.dict.contract_id.us_payroll_config_value('ca_de4_sit_allowances')
+ additional_allowances = payslip.dict.contract_id.us_payroll_config_value('ca_de4_sit_additional_allowances')
+ low_income_exemption = payslip.dict.rule_parameter('us_ca_sit_income_exemption_rate')[schedule_pay]
+ estimated_deduction = payslip.dict.rule_parameter('us_ca_sit_estimated_deduction_rate')[schedule_pay]
+ tax_table = payslip.dict.rule_parameter('us_ca_sit_tax_rate')[filing_status].get(schedule_pay)
+ standard_deduction = payslip.dict.rule_parameter('us_ca_sit_standard_deduction_rate')[schedule_pay]
+ exemption_allowances = payslip.dict.rule_parameter('us_ca_sit_exemption_allowance_rate')[schedule_pay]
+
+ low_income = False
+ if filing_status == 'head_household':
+ _, _, _, income = low_income_exemption
+ if wage <= income:
+ low_income = True
+ elif filing_status == 'married':
+ if sit_allowances >= 2:
+ _, _, income, _ = low_income_exemption
+ if wage <= income:
+ low_income = True
+ else:
+ _, income, _, _ = low_income_exemption
+ if wage <= income:
+ low_income = True
+ else:
+ income, _, _, _ = low_income_exemption
+ if wage <= income:
+ low_income = True
+
+ withholding = 0.0
+ taxable_wage = wage
+ if not low_income:
+ allowance_index = max(additional_allowances - 1, 0)
+ if additional_allowances > MAX_ALLOWANCES:
+ deduction = (estimated_deduction[0] * additional_allowances)
+ taxable_wage -= deduction
+ elif additional_allowances > 0:
+ deduction = estimated_deduction[allowance_index]
+ taxable_wage -= deduction
+
+ if filing_status == 'head_household':
+ _, _, _, deduction = standard_deduction
+ taxable_wage -= deduction
+ elif filing_status == 'married':
+ if sit_allowances >= 2:
+ _, _, deduction, _ = standard_deduction
+ taxable_wage -= deduction
+ else:
+ _, deduction, _, _ = standard_deduction
+ taxable_wage -= deduction
+ else:
+ deduction, _, _, _ = standard_deduction
+ taxable_wage -= deduction
+
+ over = 0.0
+ for row in tax_table:
+ if taxable_wage <= row[0]:
+ withholding = ((taxable_wage - over) * row[1]) + row[2]
+ break
+ over = row[0]
+
+ allowance_index = sit_allowances - 1
+ if sit_allowances > MAX_ALLOWANCES:
+ deduction = exemption_allowances[0] * sit_allowances
+ withholding -= deduction
+ elif sit_allowances > 0:
+ deduction = exemption_allowances[allowance_index]
+ withholding -= deduction
+
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ct_connecticut.py b/l10n_us_hr_payroll/models/state/ct_connecticut.py
new file mode 100644
index 00000000..a3034fec
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ct_connecticut.py
@@ -0,0 +1,76 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ct_connecticut_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'CT'
+ if not _state_applies(payslip, state_code):
+ return 0.0, 0.0
+
+ # Determine Wage
+ wage = sit_wage(payslip, categories)
+ if not wage:
+ return 0.0, 0.0
+
+ pay_periods = payslip.dict.get_pay_periods_in_year()
+ additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ withholding_code = payslip.dict.contract_id.us_payroll_config_value('ct_w4na_sit_code')
+ exemption_table = payslip.dict.rule_parameter('us_ct_sit_personal_exemption_rate').get(withholding_code, [('inf', 0.0)])
+ initial_tax_tbl = payslip.dict.rule_parameter('us_ct_sit_initial_tax_rate').get(withholding_code, [('inf', 0.0, 0.0)])
+ tax_table = payslip.dict.rule_parameter('us_ct_sit_tax_rate').get(withholding_code, [('inf', 0.0)])
+ recapture_table = payslip.dict.rule_parameter('us_ct_sit_recapture_rate').get(withholding_code, [('inf', 0.0)])
+ decimal_table = payslip.dict.rule_parameter('us_ct_sit_decimal_rate').get(withholding_code, [('inf', 0.0)])
+
+ annual_wages = wage * pay_periods
+ personal_exemption = 0.0
+ for bracket in exemption_table:
+ if annual_wages <= float(bracket[0]):
+ personal_exemption = bracket[1]
+ break
+
+ withholding = 0.0
+ taxable_income = annual_wages - personal_exemption
+ if taxable_income < 0.0:
+ taxable_income = 0.0
+
+ if taxable_income:
+ initial_tax = 0.0
+ last = 0.0
+ for bracket in initial_tax_tbl:
+ if taxable_income <= float(bracket[0]):
+ initial_tax = bracket[1] + ((bracket[2] / 100.0) * (taxable_income - last))
+ break
+ last = bracket[0]
+
+ tax_add_back = 0.0
+ for bracket in tax_table:
+ if annual_wages <= float(bracket[0]):
+ tax_add_back = bracket[1]
+ break
+
+ recapture_amount = 0.0
+ for bracket in recapture_table:
+ if annual_wages <= float(bracket[0]):
+ recapture_amount = bracket[1]
+ break
+
+ withholding = initial_tax + tax_add_back + recapture_amount
+ decimal_amount = 1.0
+ for bracket in decimal_table:
+ if annual_wages <= float(bracket[0]):
+ decimal_amount= bracket[1]
+ break
+
+ withholding = withholding * (1.00 - decimal_amount)
+ if withholding < 0.0:
+ withholding = 0.0
+ withholding /= pay_periods
+
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/us_payroll_config.py b/l10n_us_hr_payroll/models/us_payroll_config.py
index 0284c170..4d1be994 100644
--- a/l10n_us_hr_payroll/models/us_payroll_config.py
+++ b/l10n_us_hr_payroll/models/us_payroll_config.py
@@ -51,12 +51,40 @@ class HRContractUSPayrollConfig(models.Model):
fed_941_fit_w4_additional_withholding = fields.Float(string='Federal W4 Additional Withholding [4(c)]',
help='Form W4 (2020+) 4(c)')
+ al_a4_sit_exemptions = fields.Selection([
+ ('0', '0'),
+ ('S', 'S'),
+ ('MS', 'MS'),
+ ('M', 'M'),
+ ('H', 'H'),
+ ], string='Alabama A4 Withholding Exemptions', help='A4 1. 2. 3.')
+ al_a4_sit_dependents = fields.Integer(string='Alabama A4 Dependents', help='A4 4.')
+
ar_ar4ec_sit_allowances = fields.Integer(string='Arkansas AR4EC allowances', help='AR4EC 3.')
az_a4_sit_withholding_percentage = fields.Float(
string='Arizona A-4 Withholding Percentage',
help='A-4 1. (0.8 or 1.3 or 1.8 or 2.7 or 3.6 or 4.2 or 5.1 or 0 for exempt.')
+ ca_de4_sit_allowances = fields.Integer(string='California W-4 Allowances',
+ help='CA W-4 3.')
+ ca_de4_sit_additional_allowances = fields.Integer(string='California W-4 Additional Allowances',
+ help='CA W-4 4(c).')
+ ca_de4_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single or Married filing separately'),
+ ('married', 'Married filing jointly'),
+ ('head_household', 'Head of Household')
+ ], string='California W-4 Filing Status', help='CA W-4 1(c).')
+
+ ct_w4na_sit_code = fields.Selection([
+ ('a', 'A'),
+ ('b', 'B'),
+ ('c', 'C'),
+ ('d', 'D'),
+ ('f', 'F'),
+ ], string='Connecticut CT-W4 Withholding Code', help='CT-W4 1.')
+
ga_g4_sit_filing_status = fields.Selection([
('exempt', 'Exempt'),
('single', 'Single'),
diff --git a/l10n_us_hr_payroll/tests/__init__.py b/l10n_us_hr_payroll/tests/__init__.py
index e5b8fa04..128988b3 100755
--- a/l10n_us_hr_payroll/tests/__init__.py
+++ b/l10n_us_hr_payroll/tests/__init__.py
@@ -7,12 +7,21 @@ from . import test_us_payslip_2020
from . import test_us_ak_alaska_payslip_2019
from . import test_us_ak_alaska_payslip_2020
+from . import test_us_al_alabama_payslip_2019
+from . import test_us_al_alabama_payslip_2020
+
from . import test_us_ar_arkansas_payslip_2019
from . import test_us_ar_arkansas_payslip_2020
from . import test_us_az_arizona_payslip_2019
from . import test_us_az_arizona_payslip_2020
+from . import test_us_ca_california_payslip_2019
+from . import test_us_ca_california_payslip_2020
+
+from . import test_us_ct_connecticut_payslip_2019
+from . import test_us_ct_connecticut_payslip_2020
+
from . import test_us_fl_florida_payslip_2019
from . import test_us_fl_florida_payslip_2020
diff --git a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
new file mode 100644
index 00000000..61290314
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2019.py
@@ -0,0 +1,264 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsALPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ AL_UNEMP_MAX_WAGE = 8000.00
+ AL_UNEMP = -2.70 / 100.0
+
+ def test_taxes_weekly(self):
+ salary = 10000.00
+ schedule_pay = 'weekly'
+ dependents = 1
+ filing_status = 'S'
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 10000.00 for wages per period , 52.0 for weekly -> 10000 * 52 -> 520000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 520000 - 2000 = 518000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -2999.66 * 52 = -155982.32
+ # -> 518000.0 - 155982.32 = 362017.68
+ # Step 2C -> Subtract the personal exemption -> 1500 for single filing_status
+ # -> 362017.68 - 1500 = 360517.68
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 300$. Subtract
+ # -> 360517.68 - 300 = 360217.68
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((360217.68 - 500 - 2500) * (5.00 / 100)) -> 17970.884000000002
+ # Convert back to pay period
+ # wh = round(17970.884000000002, 2) -> 17970.88 / 52.0 -> 345.59
+ wh = -345.59
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=filing_status,
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -2999.66) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ remaining_AL_UNEMP_wages = 0.00 # We already reached the maximum wage for unemployment insurance.
+
+ self._log('2019 Alabama tax second payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_AL_UNEMP_wages * self.AL_UNEMP) # 0
+
+ def test_taxes_married_jointly(self):
+ salary = 10000.00
+ schedule_pay = 'weekly'
+ dependents = 1
+ filing_status = 'M'
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 10000.00 for wages per period , 52.0 for weekly -> 10000 * 52 -> 520000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 4000. Subtract from yearly income
+ # 520000 - 4000 = 516000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -2999.66 * 52 = -155982.32
+ # -> 516000.0 - 155982.32 = 360017.68
+ # Step 2C -> Subtract the personal exemption -> 3000 for married filing jointly.
+ # -> 360017.68 - 3000 = 357017.68
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 300$. Subtract
+ # -> 357017.68 - 300 = 356717.68
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (1000 * (2.00 / 100)) + (5000 * (4.00 / 100)) + ((356717.68 - 1000 - 50000) * (5.00 / 100))
+ # -> 17755.884000000002
+ # Convert back to pay period
+ # wh = round(17755.884000000002, 2) -> 15505.88 / 52.0 -> 341.45923076923077
+ wh = -341.46
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=filing_status,
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -2999.66) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+
+ def test_taxes_semimonthly_filing_seperate(self):
+ salary = 20000.00
+ schedule_pay = 'monthly'
+ filing_status = 'MS'
+ dependents = 2
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 10000.00 for wages per period , 12.0 for monthly -> 20000 * 12 -> 240000.00
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 240000.00 - 2000 = 238000.00
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -4821.99 * 12 = -57863.88
+ # -> 238000.00 - 57863.88 = 180136.12
+ # Step 2C -> Subtract the personal exemption -> 1500 for married filing separately
+ # -> 180136.12 - 1500 = 178636.12
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 600. Subtract
+ # -> 178636.12 - 600 = 178036.12
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((178036.12 - 500 - 2500) * (5.00 / 100)) -> 8861.806
+ # Convert back to pay period
+ # wh = 8861.806 / 12.0 rounded -> 738.48
+ wh = -738.48
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=filing_status,
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip monthly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -4822.00) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.AL_UNEMP_MAX_WAGE * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ def test_tax_exempt(self):
+ salary = 5500.00
+ wh = 0
+ schedule_pay = 'weekly'
+ dependents = 2
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions='0',
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=True,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip exempt:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AL_UNEMP)
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), wh)
+
+ def test_additional_withholding(self):
+ salary = 5500.0
+ schedule_pay = 'weekly'
+ additional_wh = 40.0
+ dependents = 2
+ # filing status default is single
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 5500.00 for wages per period , 52.0 for monthly -> 5500 * 52.0 -> 286000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 286000.0 - 2000 = 284000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -1422.4 * 52.0 = -73964.8
+ # -> 284000.0 - 73964.8 = 210035.2
+ # Step 2C -> Subtract the personal exemption -> 1500 for single
+ # -> 210035.2 - 1500 = 208535.2
+ # Step 2D -> Since income is so high, only 300$ per dependent -> 600. Subtract
+ # -> 208535.2 - 600 = 207935.2
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((207935.2 - 500 - 2500) * (5.00 / 100)) -> 10356.76
+ # Convert back to pay period
+ # wh = 10356.76 / 52.0 rounded -> 199.17
+ wh = -199.17
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions='S',
+ state_income_tax_additional_withholding=40.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip additional withholding:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -1422.4) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh - additional_wh)
+
+ def test_personal_exemption(self):
+ salary = 5500.0
+ schedule_pay = 'weekly'
+ # filing status default is single
+
+ # see https://revenue.alabama.gov/wp-content/uploads/2019/01/whbooklet_0119.pdf for reference
+ # Hand Calculated Amount to Test
+ # Step 1 -> 5500.00 for wages per period , 52.0 for monthly -> 5500 * 52.0 -> 286000.0
+ # Step 2A -> standard deduction for highest wage bracket -> 2000. Subtract from yearly income
+ # 286000.0 - 2000 = 284000.0
+ # Step 2B -> Subtract Federal Income Tax in yearly form -> Our Fed withholding is -1422.4 * 52.0 = -73964.8
+ # -> 284000.0 - 73964.8 = 210035.2
+ # Step 2C -> Subtract the personal exemption -> 0 for personal exemptioon
+ # -> 210035.2 - 0 = 210035.2
+ # Step 2D -> Subtract per dependent. No dependents so 0
+ # -> 210035.2 - 0 = 210035.2
+ #
+ # Step 5 (after adding previous lines) -> Compute marginal taxes.
+ # (500 * (2.00 / 100)) + (2500 * (4.00 / 100)) + ((210035.2 - 500 - 2500) * (5.00 / 100)) -> 10461.76
+ # Convert back to pay period
+ # wh = 10461.76 / 52.0 rounded -> 201.19
+ wh = -199.74
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions='S',
+ state_income_tax_additional_withholding=0.0,
+ state_income_tax_exempt=False,
+ al_a4_sit_dependents=0.0,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Alabama tax first payslip additional withholding:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_941_FIT'], -1422.4) # Hand Calculated.
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.AL_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2020.py
new file mode 100644
index 00000000..055c95cb
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_al_alabama_payslip_2020.py
@@ -0,0 +1,36 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date, timedelta
+from .common import TestUsPayslip
+
+
+class TestUsALPayslip(TestUsPayslip):
+ # Taxes and Rates
+ AL_UNEMP_MAX_WAGE = 8000.00
+ AL_UNEMP = 2.70
+
+ def _test_sit(self, wage, exempt, exemptions, additional_withholding, dependent, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('AL'),
+ al_a4_sit_exemptions=exempt,
+ state_income_tax_exempt=exemptions,
+ state_income_tax_additional_withholding=additional_withholding,
+ al_a4_sit_dependents=dependent,
+ schedule_pay=schedule_pay)
+ payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self._log('Computed period tax: ' + str(expected_withholding))
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('AL', self.AL_UNEMP, date(2020, 1, 1), wage_base=self.AL_UNEMP_MAX_WAGE)
+ self._test_sit(10000.0, 'S', False, 0.0, 1.0, 'weekly', date(2020, 1, 1), 349.08)
+ self._test_sit(850.0, 'M', False, 0.0, 2.0, 'weekly', date(2020, 1, 1), 29.98)
+ self._test_sit(5000.0, 'H', False, 0.0, 2.0, 'bi-weekly', date(2020, 1, 1), 191.15)
+ self._test_sit(20000.0, 'MS', False, 2.0, 0, 'monthly', date(2020, 1, 1), 757.6)
+ self._test_sit(5500.0, '0', True, 2.0, 150, 'weekly', date(2020, 1, 1), 0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
new file mode 100644
index 00000000..b9331fe3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2019.py
@@ -0,0 +1,245 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsCAPayslip(TestUsPayslip):
+ ###
+ # Taxes and Rates
+ ###
+ CA_MAX_WAGE = 7000
+ CA_UIT = -3.5 / 100.0
+ CA_ETT = -0.1 / 100.0
+ CA_SDI = -1.0 / 100.0
+
+ # Examples from https://www.edd.ca.gov/pdf_pub_ctr/20methb.pdf
+ def test_example_a(self):
+ salary = 210
+ schedule_pay = 'weekly'
+ allowances = 1
+ additional_allowances = 0
+
+ wh = 0.00
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='single',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * (self.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ def test_example_b(self):
+ salary = 1250
+ schedule_pay = 'bi-weekly'
+ allowances = 2
+ additional_allowances = 1
+
+ # Example B
+ subject_to_withholding = salary - 38
+ taxable_income = subject_to_withholding - 339
+ computed_tax = (taxable_income - 632) * 0.022 + 6.95 # 6.95 Marginal Amount
+ wh = computed_tax - 9.65 # two exemption allowances
+ wh = -wh
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * (self.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+
+ def test_example_c(self):
+ salary = 4100
+ schedule_pay = 'monthly'
+ allowances = 5
+ additional_allowances = 0.0
+
+ wh = -9.3
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * (self.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 California tax second payslip:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
+
+ def test_example_d(self):
+ salary = 800
+ schedule_pay = 'weekly'
+ allowances = 3
+ additional_allowances = 0
+
+ wh = -3.18
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='head_household',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * (self.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 California tax second payslip:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
+
+ def test_example_e(self):
+ salary = 1800
+ schedule_pay = 'semi-monthly'
+ allowances = 4
+ additional_allowances = 0
+
+ wh = -3.08
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * (self.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], salary * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_ca_uit_wages = self.CA_MAX_WAGE - salary if (self.CA_MAX_WAGE - 2 * salary < salary) \
+ else salary
+
+ self._log('2019 California tax second payslip:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], round((remaining_ca_uit_wages * (self.CA_UIT + self.CA_ETT)), 2))
+
+ def test_example_f(self):
+ salary = 45000
+ schedule_pay = 'annually'
+ allowances = 4
+ additional_allowances = 0
+
+ wh = -113.85
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status='married',
+ state_income_tax_additional_withholding=0.0,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 California tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.CA_MAX_WAGE * (self.CA_UIT + self.CA_ETT))
+ self.assertPayrollEqual(cats['EE_US_SUTA'], self.CA_MAX_WAGE * self.CA_SDI)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2020.py
new file mode 100755
index 00000000..c6c58547
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ca_california_payslip_2020.py
@@ -0,0 +1,42 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date, timedelta
+from .common import TestUsPayslip
+
+
+class TestUsCAPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ CA_UNEMP_MAX_WAGE = 7000.0 # Note that this is used for SDI and FLI as well
+ CA_UIT = 3.4
+ CA_ETT = 0.1
+ CA_SDI = 1.0
+
+ def _test_sit(self, wage, filing_status, allowances, additional_allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('CA'),
+ ca_de4_sit_filing_status=filing_status,
+ ca_de4_sit_allowances=allowances,
+ ca_de4_sit_additional_allowances=additional_allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ schedule_pay=schedule_pay)
+ payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding if filing_status else 0.0)
+
+ def test_2020_taxes_example1(self):
+ combined_er_rate = self.CA_UIT + self.CA_ETT
+ self._test_er_suta('CA', combined_er_rate, date(2020, 1, 1), wage_base=self.CA_UNEMP_MAX_WAGE)
+ self._test_ee_suta('CA', self.CA_SDI, date(2020, 1, 1), wage_base=self.CA_UNEMP_MAX_WAGE, relaxed=True)
+ # these expected values come from https://www.edd.ca.gov/pdf_pub_ctr/20methb.pdf
+ self._test_sit(210.0, 'single', 1, 0, 0, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(1250.0, 'married', 2, 1, 0, 'bi-weekly', date(2020, 1, 1), 1.23)
+ self._test_sit(4100.0, 'married', 5, 0, 0, 'monthly', date(2020, 1, 1), 1.5)
+ self._test_sit(800.0, 'head_household', 3, 0, 0, 'weekly', date(2020, 1, 1), 2.28)
+ self._test_sit(1800.0, 'married', 4, 0, 0, 'semi-monthly', date(2020, 1, 1), 0.84)
+ self._test_sit(45000.0, 'married', 4, 0, 0, 'annually', date(2020, 1, 1), 59.78)
+ self._test_sit(45000.0, 'married', 4, 0, 20.0, 'annually', date(2020, 1, 1), 79.78)
diff --git a/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
new file mode 100644
index 00000000..ab423131
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2019.py
@@ -0,0 +1,121 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsCTPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ CT_UNEMP_MAX_WAGE = 15000.00
+ CT_UNEMP = -(3.40 / 100.0)
+
+ def test_taxes_weekly_with_additional_wh(self):
+
+ # Tax tables can be found here:
+ # https://portal.ct.gov/-/media/DRS/Publications/pubsip/2019/IP-2019(1).pdf?la=en
+ # Step 1 - Wages per period -> 10000.00
+ salary = 10000.00
+ # Step 2 and 3 - Annual wages -> 10000.00 * 52.0 -> 520000.0
+ schedule_pay = 'weekly'
+ # Step 4 Employee Withholding Code -> A
+ wh_code = 'a'
+ # Step 5 - Use annual wages and withholding code with table for exemption amount.
+ # exemption_amt = 0 since highest bracket.
+ # Step 6 - Subtract 5 from 3 for taxable income.
+ # taxable income = 520000.00 since we do not have an exemption.
+ # Step 7 - Determine initial amount from table
+ # initial = 31550 + ((6.99 / 100) * (520000.00 - 500000.00))
+ # 32948.0
+ # Step 8 - Determine the tax rate phase out add back from table.
+ # phase_out = 200
+ # Step 9 - Determine the recapture amount from table.
+ # Close to top, but not top. -> 2900
+ # Step 10 - Add Step 7, 8, 9
+ # 32948.0 + 200 + 2900.00 - > 36048.0
+ # Step 11 - Determine decimal amount from personal tax credits.
+ # We get no tax credit.
+ # Step 12 - Multiple Step 10 by 1.00 - Step 11
+ # 36048.0 * 1.00 = 36048.0
+ # Step 13 - Divide by the number of pay periods.
+ # 36048.0 / 52.0 = 693.23
+ # Step 14 & 15 & 16- Add / Subtract the additional or under withholding amount. Then Add this to the amount
+ # for withholding per period.
+ additional_wh = 12.50
+ # 693.23 + 12.50 ->
+ wh = -705.73
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CT'),
+ ct_w4na_sit_code=wh_code,
+ state_income_tax_additional_withholding=additional_wh,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Connecticut tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], salary * self.CT_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
+ process_payslip(payslip)
+
+ remaining_CT_UNEMP_wages = 5000.00 # We already reached the maximum wage for unemployment insurance.
+ self._log('2019 Connecticut tax second payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_CT_UNEMP_wages * self.CT_UNEMP)
+
+ def test_taxes_weekly_with_different_code(self):
+
+ # Tax tables can be found here:
+ # https://portal.ct.gov/-/media/DRS/Publications/pubsip/2019/IP-2019(1).pdf?la=en
+ # Step 1 - Wages per period -> 15000.00
+ salary = 15000.00
+ # Step 2 and 3 - Annual wages -> 15000.00 * 12.0 -> 180000.0
+ schedule_pay = 'monthly'
+ # Step 4 Employee Withholding Code -> B
+ wh_code = 'b'
+ # Step 5 - Use annual wages and withholding code with table for exemption amount.
+ # exemption_amt = 0 since highest bracket.
+ # Step 6 - Subtract 5 from 3 for taxable income.
+ # taxable income = 180000.0 since we do not have an exemption.
+ # Step 7 - Determine initial amount from table
+ # initial = 8080 + ((6.00 / 100) * (180000.0 - 160000))
+ # 9280.0
+ # Step 8 - Determine the tax rate phase out add back from table.
+ # phase_out = 320
+ # Step 9 - Determine the recapture amount from table.
+ # Bottom -> 0
+ # Step 10 - Add Step 7, 8, 9
+ # 9280.0 + 320 + 0 - > 9600.0
+ # Step 11 - Determine decimal amount from personal tax credits.
+ # We get no tax credit.
+ # Step 12 - Multiple Step 10 by 1.00 - Step 11
+ # 9600.0 * 1.00 = 9600.0
+ # Step 13 - Divide by the number of pay periods.
+ # 9600.0 / 12.0 = 800.0
+ # Step 14 & 15 & 16- Add / Subtract the additional or under withholding amount. Then Add this to the amount
+ # for withholding per period.
+ additional_wh = 15.00
+ # 800.0 + 15.00 ->
+ wh = -815.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('CT'),
+ ct_w4na_sit_code=wh_code,
+ state_income_tax_additional_withholding=additional_wh,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Connecticut tax first payslip monthly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.CT_UNEMP_MAX_WAGE * self.CT_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
diff --git a/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2020.py
new file mode 100644
index 00000000..a5db79a6
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ct_connecticut_payslip_2020.py
@@ -0,0 +1,34 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date, timedelta
+from .common import TestUsPayslip
+
+
+class TestUsCTPayslip(TestUsPayslip):
+ # Taxes and Rates
+ CT_UNEMP_MAX_WAGE = 15000.0
+ CT_UNEMP = 3.2
+
+ def _test_sit(self, wage, withholding_code, additional_withholding, schedule_pay, date_start, expected_withholding):
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('CT'),
+ ct_w4na_sit_code=withholding_code,
+ state_income_tax_additional_withholding=additional_withholding,
+ schedule_pay=schedule_pay)
+ payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self._log('Computed period tax: ' + str(expected_withholding))
+ self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('CT', self.CT_UNEMP, date(2020, 1, 1), wage_base=self.CT_UNEMP_MAX_WAGE)
+ self._test_sit(10000.0, 'a', 0.0, 'weekly', date(2020, 1, 1), 693.23)
+ self._test_sit(12000.0, 'b', 15.0, 'bi-weekly', date(2020, 1, 1), 688.85)
+ self._test_sit(5000.0, 'f', 15.0, 'monthly', date(2020, 1, 1), 230.25)
+ self._test_sit(15000.0, 'c', 0.0, 'monthly', date(2020, 1, 1), 783.33)
+ self._test_sit(18000.0, 'b', 0.0, 'weekly', date(2020, 1, 1), 1254.35)
diff --git a/l10n_us_hr_payroll/views/us_payroll_config_views.xml b/l10n_us_hr_payroll/views/us_payroll_config_views.xml
index 0bfa51a9..d89bccd4 100644
--- a/l10n_us_hr_payroll/views/us_payroll_config_views.xml
+++ b/l10n_us_hr_payroll/views/us_payroll_config_views.xml
@@ -44,6 +44,13 @@
+
+ Form A4 - State Income Tax
+
+
+
+
+
Form AR4EC - State Income Tax
@@ -55,6 +62,18 @@
+
+ Form W-4 - State Income Tax
+
+
+
+
+
+
+ Form CT-W4 - State Income Tax
+
+
+
No additional fields.