From 55d15aa597c2a5727b88f5887bb332d0eb22d9f7 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Mon, 3 Jun 2019 11:34:51 -0700
Subject: [PATCH 01/43] IMP `connector_walmart` Add the ability to set the
Salesperson on imported Walmart orders.
---
connector_walmart/__manifest__.py | 2 +-
connector_walmart/models/sale_order/importer.py | 5 +++++
connector_walmart/models/walmart_backend/common.py | 2 ++
connector_walmart/views/walmart_backend_views.xml | 1 +
4 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/connector_walmart/__manifest__.py b/connector_walmart/__manifest__.py
index 33bca3fb..534a6e33 100644
--- a/connector_walmart/__manifest__.py
+++ b/connector_walmart/__manifest__.py
@@ -3,7 +3,7 @@
{
'name': 'Walmart Connector',
- 'version': '12.0.1.0.0',
+ 'version': '12.0.1.1.0',
'category': 'Connector',
'depends': [
'account',
diff --git a/connector_walmart/models/sale_order/importer.py b/connector_walmart/models/sale_order/importer.py
index adf4c099..f10518d3 100644
--- a/connector_walmart/models/sale_order/importer.py
+++ b/connector_walmart/models/sale_order/importer.py
@@ -123,6 +123,11 @@ class SaleOrderImportMapper(Component):
if self.backend_record.team_id:
return {'team_id': self.backend_record.team_id.id}
+ @mapping
+ def user_id(self, record):
+ if self.backend_record.user_id:
+ return {'user_id': self.backend_record.user_id.id}
+
@mapping
def payment_mode_id(self, record):
assert self.backend_record.payment_mode_id, ("Payment mode must be specified.")
diff --git a/connector_walmart/models/walmart_backend/common.py b/connector_walmart/models/walmart_backend/common.py
index 4f4d60cc..673857f1 100644
--- a/connector_walmart/models/walmart_backend/common.py
+++ b/connector_walmart/models/walmart_backend/common.py
@@ -59,6 +59,8 @@ class WalmartBackend(models.Model):
'field on the sale order created by the connector.'
)
team_id = fields.Many2one(comodel_name='crm.team', string='Sales Team')
+ user_id = fields.Many2one(comodel_name='res.users', string='Salesperson',
+ help="Default Salesperson for newly imported orders.")
sale_prefix = fields.Char(
string='Sale Prefix',
help="A prefix put before the name of imported sales orders.\n"
diff --git a/connector_walmart/views/walmart_backend_views.xml b/connector_walmart/views/walmart_backend_views.xml
index af4c84e6..00814d97 100644
--- a/connector_walmart/views/walmart_backend_views.xml
+++ b/connector_walmart/views/walmart_backend_views.xml
@@ -30,6 +30,7 @@
+
From b9835c3fb6a0d0662fc9537686f3f9795e60cd4c Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 18 Jun 2019 11:21:37 -0700
Subject: [PATCH 02/43] IMP `purchase_by_sale_history` Convert "to buy" QTY to
the PO Line UOM.
This means that if it is making a new line, it will be the "Purchase UOM", if it is re-using a line it will be the current line UOM (which may have been changed by the user).
---
.../wizard/purchase_by_sale_history.py | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/purchase_by_sale_history/wizard/purchase_by_sale_history.py b/purchase_by_sale_history/wizard/purchase_by_sale_history.py
index 2e9c91ae..eb19c316 100644
--- a/purchase_by_sale_history/wizard/purchase_by_sale_history.py
+++ b/purchase_by_sale_history/wizard/purchase_by_sale_history.py
@@ -79,6 +79,12 @@ class PurchaseBySaleHistory(models.TransientModel):
qty = ceil(history['sold_qty'] * self.procure_days / self.history_days)
history['buy_qty'] = max((0.0, qty - product.virtual_available))
+ def _convert_to_purchase_line_qty(self, line, qty):
+ # Skip calculation if they are the same UOM
+ if line.product_id.uom_id != line.product_uom:
+ return line.product_id.uom_id._compute_quantity(qty, line.product_uom)
+ return qty
+
def _apply_history(self, history, product_ids):
line_model = self.env['purchase.order.line']
updated_lines = line_model.browse()
@@ -98,13 +104,12 @@ class PurchaseBySaleHistory(models.TransientModel):
self._apply_history_product(p, history_dict[p.id])
for pid, history in history_dict.items():
- # TODO: Should convert from Sale UOM to Purchase UOM
qty = history.get('buy_qty', 0.0)
# Find line that already exists on PO
line = self.purchase_id.order_line.filtered(lambda l: l.product_id.id == pid)
if line:
- line.write({'product_qty': qty})
+ line.write({'product_qty': self._convert_to_purchase_line_qty(line, qty)})
line._onchange_quantity()
else:
# Create new PO line
@@ -115,7 +120,7 @@ class PurchaseBySaleHistory(models.TransientModel):
})
line.onchange_product_id()
line_vals = line._convert_to_write(line._cache)
- line_vals['product_qty'] = qty
+ line_vals['product_qty'] = self._convert_to_purchase_line_qty(line, qty)
line = line_model.create(line_vals)
updated_lines += line
From e59cdc0000d521db7273093c9d5e8027ad15888c Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Mon, 15 Jul 2019 08:17:42 -0700
Subject: [PATCH 03/43] FIX `sale_planner` FakeSaleOrder project_id =>
analytic_accout_id
---
sale_planner/wizard/order_planner.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sale_planner/wizard/order_planner.py b/sale_planner/wizard/order_planner.py
index c884fb58..75db1e3b 100644
--- a/sale_planner/wizard/order_planner.py
+++ b/sale_planner/wizard/order_planner.py
@@ -132,7 +132,7 @@ class FakeSaleOrder():
self.id = 0
self.name = 'Quote'
self.team_id = None
- self.project_id = None
+ self.analytic_account_id = None
self.amount_total = 0.0
self.date_order = fields.Date.today()
self.shipping_account_id = False # from delivery_hibou
From db7e47ac7d82878e3ca1e619b4c62e0e304188ec Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Mon, 15 Jul 2019 09:47:51 -0700
Subject: [PATCH 04/43] IMP `sale_planner` Fake objects now return False for
any attribute they don't implement.
---
sale_planner/wizard/order_planner.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/sale_planner/wizard/order_planner.py b/sale_planner/wizard/order_planner.py
index 75db1e3b..09b69169 100644
--- a/sale_planner/wizard/order_planner.py
+++ b/sale_planner/wizard/order_planner.py
@@ -78,6 +78,9 @@ class FakePartner():
return self.date_localization
+ def __getattr__(self, item):
+ return False
+
class FakeOrderLine():
def __init__(self, **kwargs):
@@ -108,6 +111,9 @@ class FakeOrderLine():
"""
return qty
+ def __getattr__(self, item):
+ return False
+
class FakeSaleOrder():
"""
@@ -149,6 +155,10 @@ class FakeSaleOrder():
def _compute_amount_total_without_delivery(self):
return self.amount_total
+ def __getattr__(self, item):
+ return False
+
+
def distance(lat_1, lon_1, lat_2, lon_2):
R = 6373.0
From 77e09d5de92fb3ca657f298526a7c8c248229a2a Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Mon, 15 Jul 2019 12:29:38 -0700
Subject: [PATCH 05/43] IMP `order_planner` allow subscription access to Fakes
---
sale_planner/wizard/order_planner.py | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/sale_planner/wizard/order_planner.py b/sale_planner/wizard/order_planner.py
index 09b69169..c23bf8f8 100644
--- a/sale_planner/wizard/order_planner.py
+++ b/sale_planner/wizard/order_planner.py
@@ -81,6 +81,11 @@ class FakePartner():
def __getattr__(self, item):
return False
+ def __getitem__(self, item):
+ if item == '__last_update':
+ return str(datetime.now())
+ return getattr(self, item)
+
class FakeOrderLine():
def __init__(self, **kwargs):
@@ -114,6 +119,11 @@ class FakeOrderLine():
def __getattr__(self, item):
return False
+ def __getitem__(self, item):
+ if item == '__last_update':
+ return str(datetime.now())
+ return getattr(self, item)
+
class FakeSaleOrder():
"""
@@ -158,6 +168,11 @@ class FakeSaleOrder():
def __getattr__(self, item):
return False
+ def __getitem__(self, item):
+ if item == '__last_update':
+ return str(datetime.now())
+ return getattr(self, item)
+
def distance(lat_1, lon_1, lat_2, lon_2):
R = 6373.0
From 6ed97d0a391c309e7f63ce230cbd0730d693ccba Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 16 Jul 2019 15:07:41 -0700
Subject: [PATCH 06/43] IMP `sale_planner` FakePartner now provides a
FakePartner parent_id
---
sale_planner/wizard/order_planner.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/sale_planner/wizard/order_planner.py b/sale_planner/wizard/order_planner.py
index c23bf8f8..6e27fcff 100644
--- a/sale_planner/wizard/order_planner.py
+++ b/sale_planner/wizard/order_planner.py
@@ -50,6 +50,9 @@ class FakePartner():
self.partner_latitude = 0.0
self.partner_longitude = 0.0
self.is_company = False
+ self._date_localization = kwargs.pop('date_localization', False)
+ if not kwargs.pop('PARENT', False):
+ self.parent_id = FakePartner(PARENT=True)
for attr, value in kwargs.items():
setattr(self, attr, value)
From 5e3655ffaaab6a1d824ed760f7ad0cf60654790d Mon Sep 17 00:00:00 2001
From: David Frick
Date: Thu, 11 Jul 2019 15:49:43 -0400
Subject: [PATCH 07/43] Port commit '6904f71fa4bc241dcd3d0f8e973256848e6922ab'
into 12.0-test
---
l10n_us_nc_hr_payroll/data/rules.xml | 2 +-
l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2018.py | 4 ++--
l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2019.py | 10 +++-------
3 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/l10n_us_nc_hr_payroll/data/rules.xml b/l10n_us_nc_hr_payroll/data/rules.xml
index 8d06ea80..6d404cd0 100755
--- a/l10n_us_nc_hr_payroll/data/rules.xml
+++ b/l10n_us_nc_hr_payroll/data/rules.xml
@@ -14,7 +14,7 @@
rate = payslip.dict.get_rate('US_NC_UNEMP')
year = payslip.dict.date_to.year
-ytd = payslip.sum('NC_UNEMP_WAGES_2018', str(year) + '-01-01', str(year+1) + '-01-01')
+ytd = payslip.sum('WAGE_US_NC_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01')
ytd += contract.external_wages
remaining = rate.wage_limit_year - ytd
if remaining <= 0.0:
diff --git a/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2018.py b/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2018.py
index eb5079fe..b5ae614b 100755
--- a/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2018.py
+++ b/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2018.py
@@ -248,13 +248,13 @@ class TestUsNCPayslip(TestUsPayslip):
salary = 4000.0
wh = 0
schedule_pay = 'weekly'
- excemptions = 1
+ exemptions = 1
employee = self._createEmployee()
contract = self._createContract(employee, salary, struct_id=self.ref(
'l10n_us_nc_hr_payroll.hr_payroll_salary_structure_us_nc_employee'), schedule_pay=schedule_pay)
- contract.nc_nc4_allowances = excemptions
+ contract.nc_nc4_allowances = exemptions
contract.nc_nc4_filing_status = 'exempt'
self.assertEqual(contract.schedule_pay, 'weekly')
diff --git a/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2019.py b/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2019.py
index 2b30e8c1..79599fb1 100755
--- a/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2019.py
+++ b/l10n_us_nc_hr_payroll/tests/test_us_nc_payslip_2019.py
@@ -10,9 +10,8 @@ class TestUsNCPayslip(TestUsPayslip):
NC_UNEMP = -1.0 / 100.0
NC_INC_TAX = -0.0535
-
def test_2019_taxes_weekly(self):
- salary = 5000.0
+ salary = 20000.0
schedule_pay = 'weekly'
# allowance_multiplier and Portion of Standard Deduction for weekly
allowance_multiplier = 48.08
@@ -20,11 +19,9 @@ class TestUsNCPayslip(TestUsPayslip):
exemption = 1
# Algorithm derived from percentage method in https://files.nc.gov/ncdor/documents/files/nc-30_book_web.pdf
-
wh = -round((salary - (PST + (allowance_multiplier * exemption))) * -self.NC_INC_TAX)
employee = self._createEmployee()
-
contract = self._createContract(employee, salary, struct_id=self.ref('l10n_us_nc_hr_payroll.hr_payroll_salary_structure_us_nc_employee'), schedule_pay=schedule_pay)
contract.nc_nc4_allowances = exemption
@@ -47,7 +44,6 @@ class TestUsNCPayslip(TestUsPayslip):
remaining_NC_UNEMP_wages = self.NC_UNEMP_MAX_WAGE - salary if (self.NC_UNEMP_MAX_WAGE - 2*salary < salary) \
else salary
-
self._log('2019 North Carolina tax second payslip weekly:')
payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
@@ -249,13 +245,13 @@ class TestUsNCPayslip(TestUsPayslip):
salary = 4000.0
wh = 0
schedule_pay = 'weekly'
- excemptions = 1
+ exemptions = 1
employee = self._createEmployee()
contract = self._createContract(employee, salary, struct_id=self.ref(
'l10n_us_nc_hr_payroll.hr_payroll_salary_structure_us_nc_employee'), schedule_pay=schedule_pay)
- contract.nc_nc4_allowances = excemptions
+ contract.nc_nc4_allowances = exemptions
contract.nc_nc4_filing_status = 'exempt'
self.assertEqual(contract.schedule_pay, 'weekly')
From 97c9c30d52d22abe1a510a62278be19ee7961c14 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 25 Jul 2019 15:14:33 -0700
Subject: [PATCH 08/43] IMP `delivery_partner_dhl` Allow 8-10 digit account
numbers.
---
delivery_partner_dhl/models/delivery.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/delivery_partner_dhl/models/delivery.py b/delivery_partner_dhl/models/delivery.py
index db975f51..6de6583a 100644
--- a/delivery_partner_dhl/models/delivery.py
+++ b/delivery_partner_dhl/models/delivery.py
@@ -10,8 +10,8 @@ class PartnerShippingAccount(models.Model):
delivery_type = fields.Selection(selection_add=[('dhl', 'DHL')])
def dhl_check_validity(self):
- m = re.search(r'^\d{10}$', self.name or '')
+ m = re.search(r'^(\d{8}|\d{9}|\d{10})$', self.name or '')
if not m:
- raise ValidationError('DHL Account numbers must be 10 decimal numbers.')
+ raise ValidationError('DHL Account numbers must be 8-10 decimal numbers.')
From 055032f7d4de6ca481a06e3d735ce40150d49432 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 6 Aug 2019 05:54:34 -0700
Subject: [PATCH 09/43] NEW Addition to gitlab-ci for creating merge reqeusts
---
.gitlab-ci-automerge.sh | 58 +++++++++++++++++++++++++++++++++++++++++
.gitlab-ci.yml | 10 +++++++
2 files changed, 68 insertions(+)
create mode 100755 .gitlab-ci-automerge.sh
diff --git a/.gitlab-ci-automerge.sh b/.gitlab-ci-automerge.sh
new file mode 100755
index 00000000..eda4db46
--- /dev/null
+++ b/.gitlab-ci-automerge.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+set -e
+set -x
+
+echo "Processing for merge requests."
+
+HOST="${HOST}api/v4/projects/"
+
+TARGET_BRANCH=$RELEASE
+
+BODY="{
+ \"id\": ${CI_PROJECT_ID},
+ \"source_branch\": \"${CI_COMMIT_REF_NAME}\",
+ \"target_branch\": \"${TARGET_BRANCH}\",
+ \"remove_source_branch\": true,
+ \"title\": \"WIP RELEASE: ${CI_COMMIT_REF_NAME}\",
+ \"assignee_id\":\"${GITLAB_USER_ID}\"
+}";
+
+LISTMR=`curl --silent "${HOST}${CI_PROJECT_ID}/merge_requests?state=opened" --header "PRIVATE-TOKEN:${PRIVATE_TOKEN}"`;
+MATCHES=`echo ${LISTMR} | jq ".[] | {target_branch: .target_branch | match(\"${TARGET_BRANCH}\"), source_branch: .source_branch | match(\"${CI_COMMIT_REF_NAME}\")}"`;
+
+# No MR found, let's create a new one
+if [ -z "${MATCHES}" ]; then
+ curl -X POST "${HOST}${CI_PROJECT_ID}/merge_requests" \
+ --header "PRIVATE-TOKEN:${PRIVATE_TOKEN}" \
+ --header "Content-Type: application/json" \
+ --data "${BODY}";
+
+ echo "Opened a new merge request: WIP RELEASE: ${CI_COMMIT_REF_NAME} and assigned to you";
+fi
+
+# Test
+TARGET_BRANCH="${RELEASE}-test"
+
+BODY="{
+ \"id\": ${CI_PROJECT_ID},
+ \"source_branch\": \"${CI_COMMIT_REF_NAME}\",
+ \"target_branch\": \"${TARGET_BRANCH}\",
+ \"remove_source_branch\": false,
+ \"title\": \"WIP TEST: ${CI_COMMIT_REF_NAME}\",
+ \"assignee_id\":\"${GITLAB_USER_ID}\"
+}";
+
+LISTMR=`curl --silent "${HOST}${CI_PROJECT_ID}/merge_requests?state=opened" --header "PRIVATE-TOKEN:${PRIVATE_TOKEN}"`;
+MATCHES=`echo ${LISTMR} | jq ".[] | {target_branch: .target_branch | match(\"${TARGET_BRANCH}\"), source_branch: .source_branch | match(\"${CI_COMMIT_REF_NAME}\")}"`;
+
+# No MR found, let's create a new one
+if [ -z "${MATCHES}" ]; then
+ curl -X POST "${HOST}${CI_PROJECT_ID}/merge_requests" \
+ --header "PRIVATE-TOKEN:${PRIVATE_TOKEN}" \
+ --header "Content-Type: application/json" \
+ --data "${BODY}";
+
+ echo "Opened a new merge request: WIP TEST: ${CI_COMMIT_REF_NAME} and assigned to you";
+fi
+
+
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 97144120..3b65e88e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,6 +21,16 @@ before_script:
- git submodule sync --recursive
- git submodule update --init --recursive
+merge_request:
+ before_script:
+ - apk add curl jq
+ stage: build
+ only:
+ - /(^new\/)|(^mig\/)|(^imp\/)|(^fix\/)/
+ script:
+ - ls -lah
+ - RELEASE=$(echo $CI_COMMIT_REF_NAME | sed "s{.*\/\(.*\)\/.*{\1{g") HOST=$(echo $CI_PROJECT_URL | sed 's{\(^https://[^/]*/\).*{\1{g') CI_PROJECT_ID=${CI_PROJECT_ID} CI_COMMIT_REF_NAME=${CI_COMMIT_REF_NAME} GITLAB_USER_ID=${GITLAB_USER_ID} PRIVATE_TOKEN=${PRIVATE_TOKEN} ./.gitlab-ci-automerge.sh
+
build:
stage: build
script:
From 08af152344ac8e406d255b1b8398c25fd0afd49d Mon Sep 17 00:00:00 2001
From: David Frick
Date: Wed, 10 Jul 2019 18:50:38 -0400
Subject: [PATCH 10/43] NEW `l10n_us_ar_hr_payroll` for 11.0
---
l10n_us_ar_hr_payroll/__init__.py | 1 +
l10n_us_ar_hr_payroll/__manifest__.py | 28 ++
l10n_us_ar_hr_payroll/data/base.xml | 47 +++
l10n_us_ar_hr_payroll/data/final.xml | 17 +
l10n_us_ar_hr_payroll/data/rates.xml | 14 +
l10n_us_ar_hr_payroll/data/rules.xml | 115 +++++++
l10n_us_ar_hr_payroll/models/__init__.py | 1 +
l10n_us_ar_hr_payroll/models/hr_payroll.py | 10 +
l10n_us_ar_hr_payroll/tests/__init__.py | 1 +
.../tests/test_us_ar_payslip_2019.py | 292 ++++++++++++++++++
.../views/hr_payroll_views.xml | 23 ++
11 files changed, 549 insertions(+)
create mode 100755 l10n_us_ar_hr_payroll/__init__.py
create mode 100755 l10n_us_ar_hr_payroll/__manifest__.py
create mode 100755 l10n_us_ar_hr_payroll/data/base.xml
create mode 100755 l10n_us_ar_hr_payroll/data/final.xml
create mode 100755 l10n_us_ar_hr_payroll/data/rates.xml
create mode 100755 l10n_us_ar_hr_payroll/data/rules.xml
create mode 100644 l10n_us_ar_hr_payroll/models/__init__.py
create mode 100755 l10n_us_ar_hr_payroll/models/hr_payroll.py
create mode 100755 l10n_us_ar_hr_payroll/tests/__init__.py
create mode 100755 l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
create mode 100755 l10n_us_ar_hr_payroll/views/hr_payroll_views.xml
diff --git a/l10n_us_ar_hr_payroll/__init__.py b/l10n_us_ar_hr_payroll/__init__.py
new file mode 100755
index 00000000..0650744f
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/l10n_us_ar_hr_payroll/__manifest__.py b/l10n_us_ar_hr_payroll/__manifest__.py
new file mode 100755
index 00000000..1bb42dad
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/__manifest__.py
@@ -0,0 +1,28 @@
+{
+ 'name': 'USA - Arkansas - Payroll',
+ 'author': 'Hibou Corp. ',
+ 'license': 'AGPL-3',
+ 'category': 'Localization',
+ 'depends': ['l10n_us_hr_payroll'],
+ 'version': '11.0.2019.0.0',
+ 'description': """
+USA::Arkansas Payroll Rules.
+==================================
+
+* Contribution register and partner for Arkansas Department of Financial Administration - Income Tax Withholding
+* Contribution register and partner for Arkansas Department of Workforce Solutions - Unemployment
+* Contract level Arkansas Exemptions
+* Company level Arkansas Unemployment Rate
+* Salary Structure for Arkansas
+ """,
+ 'auto_install': False,
+ 'website': 'https://hibou.io/',
+ 'data': [
+ 'views/hr_payroll_views.xml',
+ 'data/base.xml',
+ 'data/rates.xml',
+ 'data/rules.xml',
+ 'data/final.xml',
+ ],
+ 'installable': True
+}
diff --git a/l10n_us_ar_hr_payroll/data/base.xml b/l10n_us_ar_hr_payroll/data/base.xml
new file mode 100755
index 00000000..38534fdd
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/data/base.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+ Arkansas Department of Workforce Solutions - Unemployment Tax
+ 1
+
+
+
+ Arkansas Department of Financial Administration- Income Tax Withholding
+ 1
+
+
+
+
+
+ Arkansas Unemployment
+ Arkansas Department of Workforce Solutions - Unemployment
+
+
+
+ Arkansas Income Tax Withholding
+ Arkansas Department of Financial Administration - Income Tax Withholding
+
+
+
+
+
+
+ Wage: US-AR Unemployment
+ WAGE_US_AR_UNEMP
+
+
+
+ ER: US-AR Unemployment
+ ER_US_AR_UNEMP
+
+
+
+
+ EE: US-AR Income Tax Withholding
+ EE_US_AR_INC_WITHHOLD
+
+
+
+
diff --git a/l10n_us_ar_hr_payroll/data/final.xml b/l10n_us_ar_hr_payroll/data/final.xml
new file mode 100755
index 00000000..6bbf8f32
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/data/final.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ US_AR_EMP
+ USA Arkansas Employee
+
+
+
+
+
+
diff --git a/l10n_us_ar_hr_payroll/data/rates.xml b/l10n_us_ar_hr_payroll/data/rates.xml
new file mode 100755
index 00000000..00c3b202
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/data/rates.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ US AR Unemployment
+ US_AR_UNEMP
+ 3.2
+ 2019-01-01
+
+
+
+
+
diff --git a/l10n_us_ar_hr_payroll/data/rules.xml b/l10n_us_ar_hr_payroll/data/rules.xml
new file mode 100755
index 00000000..7082c131
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/data/rules.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+ Wage: US-AR Unemployment
+ WAGE_US_AR_UNEMP
+ python
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC and not contract.ar_w4_tax_exempt and not contract.ar_w4_texarkana_exemption)
+ code
+
+rate = payslip.dict.get_rate('US_AR_UNEMP')
+year = int(payslip.dict.date_to[:4])
+ytd = payslip.sum('WAGE_US_AR_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01')
+ytd += contract.external_wages
+remaining = rate.wage_limit_year - ytd
+if remaining <= 0.0:
+ result = 0
+elif remaining < categories.BASIC:
+ result = remaining
+else:
+ result = categories.BASIC
+
+
+
+
+
+
+
+ ER: US-AR Unemployment
+ ER_US_AR_UNEMP
+ python
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC and not contract.ar_w4_tax_exempt and not contract.ar_w4_texarkana_exemption)
+ code
+
+rate = payslip.dict.get_rate('US_AR_UNEMP')
+result_rate = -rate.rate
+result = categories.WAGE_US_AR_UNEMP
+
+# result_rate of 0 implies 100% due to bug
+if result_rate == 0.0:
+ result = 0.0
+
+
+
+
+
+
+
+
+ EE: US-AR Income Tax Withholding
+ EE_US_AR_INC_WITHHOLD
+ python
+ result = not (contract.ar_w4_texarkana_exemption or contract.ar_w4_tax_exempt)
+ code
+
+wages = categories.GROSS
+annual_gross_pay = 0.00
+allowance_amt = contract.ar_w4_allowances * 26.00
+schedule_pay = contract.schedule_pay
+standard_deduction = 2200
+pay_period = 0.0
+additional_withholding = contract.ar_w4_additional_wh
+
+if contract.w4_filing_status == 'married':
+ standard_deduction = standard_deduction * 2
+
+if schedule_pay == 'daily':
+ pay_period = 260.0
+elif schedule_pay == 'weekly':
+ pay_period = 52.0
+elif schedule_pay == 'bi-weekly':
+ pay_period = 26.0
+elif schedule_pay == 'semi-monthly':
+ pay_period = 24.0
+elif schedule_pay == 'monthly':
+ pay_period = 12.0
+else:
+ raise Exception('Invalid schedule_pay="' + schedule_pay + '" for AR Income Withholding calculation')
+
+annual_gross_pay = (wages * pay_period)
+net_taxable_income = annual_gross_pay - standard_deduction - allowance_amt
+if (net_taxable_income < 50000.00):
+ # This formula will round the number to the nearest 50 if under 50000
+ net_taxable_income = (net_taxable_income // 50) * 50.0 + 50.0
+
+tax_rate_table = [(4299, 0.90),
+ (8499, 2.50),
+ (12699, 3.50),
+ (21199, 4.50),
+ (35099, 6.0),
+ (float('inf'), 6.9)]
+
+result = 0.0
+last = 0.0
+
+for row in tax_rate_table:
+ cap, rate = row
+ if cap <= net_taxable_income:
+ taxed = cap - last
+ result = result + (taxed * (rate / 100.0))
+ last = cap
+ elif cap > net_taxable_income:
+ taxed = net_taxable_income - last
+ result = result + (taxed * (rate / 100.0))
+ break
+
+result = (result / pay_period) + additional_withholding
+result = -result
+
+
+
+
+
diff --git a/l10n_us_ar_hr_payroll/models/__init__.py b/l10n_us_ar_hr_payroll/models/__init__.py
new file mode 100644
index 00000000..e99aa24a
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/models/__init__.py
@@ -0,0 +1 @@
+from . import hr_payroll
diff --git a/l10n_us_ar_hr_payroll/models/hr_payroll.py b/l10n_us_ar_hr_payroll/models/hr_payroll.py
new file mode 100755
index 00000000..b6b51866
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/models/hr_payroll.py
@@ -0,0 +1,10 @@
+from odoo import models, fields, api
+
+
+class USARHrContract(models.Model):
+ _inherit = 'hr.contract'
+
+ ar_w4_allowances = fields.Integer(string='Arkansas W-4 allowances', default=0)
+ ar_w4_additional_wh = fields.Float(string="Arkansas Additional Withholding", default=0.0)
+ ar_w4_tax_exempt = fields.Boolean(string="Tax Exempt")
+ ar_w4_texarkana_exemption = fields.Boolean(string="Texarkana Exemption")
diff --git a/l10n_us_ar_hr_payroll/tests/__init__.py b/l10n_us_ar_hr_payroll/tests/__init__.py
new file mode 100755
index 00000000..7d2ca3e4
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_us_ar_payslip_2019
diff --git a/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py b/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
new file mode 100755
index 00000000..0468f48f
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
@@ -0,0 +1,292 @@
+from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
+from odoo.addons.l10n_us_hr_payroll.models.l10n_us_hr_payroll import USHrContract
+
+
+class TestUsARPayslip(TestUsPayslip):
+
+ AR_UNEMP_MAX_WAGE = 10000.00
+ AR_UNEMP = -3.2 / 100.0
+ AR_INC_TAX = -0.0535
+
+ def test_taxes_monthly(self):
+ salary = 10000.0
+ schedule_pay = 'monthly'
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee, salary,
+ struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
+ schedule_pay=schedule_pay)
+
+ self.assertEqual(contract.schedule_pay, 'monthly')
+
+ self._log('2019 Arkansas tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], salary)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+ remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - salary if (self.AR_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Arkansas tax second payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_AR_UNEMP_wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_AR_UNEMP_wages * self.AR_UNEMP)
+
+ def test_taxes_with_state_exempt(self):
+ salary = 50000.0
+ external_wages = 10000.0
+ tax_exempt = True # State withholding should be zero.
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ salary,
+ external_wages=external_wages,
+ struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
+ futa_type=USHrContract.FUTA_TYPE_BASIC)
+ contract.ar_w4_tax_exempt = tax_exempt
+ self._log('2019 Arkansas exempt tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats.get('WAGE_US_AR_UNEMP', 0.0), 0.0)
+ self.assertPayrollEqual(cats.get('ER_US_AR_UNEMP', 0.0), cats.get('WAGE_US_AR_UNEMP', 0.0) * self.AR_UNEMP)
+ self.assertPayrollEqual(cats.get('EE_US_AR_INC_WITHHOLD', 0.0), 0.0)
+
+ process_payslip(payslip)
+
+ def test_taxes_with_texarkana_exempt(self):
+ salary = 40000.00
+ texarkana_exemption = True # State withholding should be zero.
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ salary,
+ struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'))
+ contract.ar_w4_texarkana_exemption = texarkana_exemption
+
+
+ self._log('2019 Arkansas tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats.get('WAGE_US_AR_UNEMP', 0.0), 0.0)
+ self.assertPayrollEqual(cats.get('ER_US_AR_UNEMP', 0.0), cats.get('WAGE_US_AR_UNEMP', 0.0) * self.AR_UNEMP)
+
+ process_payslip(payslip)
+
+ def test_additional_withholding(self):
+ wages = 5000.0
+ schedule_pay = 'monthly'
+ pay_periods = 12
+ additional_wh = 150.0
+ exemptions = 2
+ # TODO: comment on how it was calculated
+ test_ar_amt = 3069.97
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wages,
+ struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
+ schedule_pay=schedule_pay)
+ contract.ar_w4_additional_wh = 0.0
+ contract.ar_w4_allowances = exemptions
+
+ self.assertEqual(contract.schedule_pay, 'monthly')
+
+ self._log('2019 Arkansas tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
+ # TODO: change to hand the test_ar_amt already be divided by pay periods
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
+
+ contract.ar_w4_additional_wh = additional_wh
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
+
+ process_payslip(payslip)
+
+ def test_under_fifty_thousand(self):
+ wages = 2500.00
+ schedule_pay = 'monthly'
+ pay_periods = 12
+ additional_wh = 150.0
+ exemptions = 2
+ # TODO: comment calc.
+ test_ar_amt = 1066.151
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wages,
+ struct_id=self.ref(
+ 'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
+ schedule_pay=schedule_pay)
+ contract.ar_w4_additional_wh = 0.0
+ contract.ar_w4_allowances = exemptions
+
+ self.assertEqual(contract.schedule_pay, 'monthly')
+
+ self._log('2019 Arkansas tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
+
+ contract.ar_w4_additional_wh = additional_wh
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - wages if (self.AR_UNEMP_MAX_WAGE - 2 * wages < wages) \
+ else wages
+
+ self._log('2019 Arkansas tax second payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_AR_UNEMP_wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_AR_UNEMP_wages * self.AR_UNEMP)
+
+ def test_over_fifty_thousand(self):
+ wages = 10000.00
+ schedule_pay = 'monthly'
+ pay_periods = 12
+ additional_wh = 150.0
+ exemptions = 2
+ # TODO: comment on how it was calculated
+ test_ar_amt = 7209.97
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wages,
+ struct_id=self.ref(
+ 'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
+ schedule_pay=schedule_pay)
+ contract.ar_w4_additional_wh = 0.0
+ contract.ar_w4_allowances = exemptions
+
+ self.assertEqual(contract.schedule_pay, 'monthly')
+
+ self._log('2019 Arkansas tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
+
+ contract.ar_w4_additional_wh = additional_wh
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - wages if (self.AR_UNEMP_MAX_WAGE - 2 * wages < wages) \
+ else wages
+
+ self._log('2019 Arkansas tax second payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_AR_UNEMP_wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_AR_UNEMP_wages * self.AR_UNEMP)
+
+ def test_married(self):
+ wages = 5500.00
+ schedule_pay = 'monthly'
+ pay_periods = 12
+ additional_wh = 150.0
+ exemptions = 2
+ w4_filing_status = 'married'
+ # TODO: explain calc.
+ # Yearly -> 3332.17. Monthly -> 427.681
+ test_ar_amt = 3332.17
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wages,
+ struct_id=self.ref(
+ 'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
+ schedule_pay=schedule_pay)
+ contract.ar_w4_additional_wh = additional_wh
+ contract.ar_w4_allowances = exemptions
+ contract.w4_filing_status = w4_filing_status
+
+ self.assertEqual(contract.w4_filing_status, 'married')
+
+ self._log('2019 Arkansas tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
+
+ def test_single(self):
+ wages = 5500.00
+ schedule_pay = 'monthly'
+ pay_periods = 12
+ additional_wh = 150.0
+ exemptions = 2
+ w4_filling_status = 'single'
+ # TODO: explain calc.
+ # Yearly -> 3483.972 Monthly -> 298.331
+ test_ar_amt = 3483.972
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wages,
+ struct_id=self.ref(
+ 'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
+ schedule_pay=schedule_pay)
+ contract.ar_w4_additional_wh = 0
+ contract.ar_w4_allowances = exemptions
+ contract.w4_filling_status = w4_filling_status
+
+ self.assertEqual(contract.w4_filling_status, 'single')
+
+ self._log('2019 Arkansas tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
+
+ contract.ar_w4_additional_wh = additional_wh
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
+
+ process_payslip(payslip)
diff --git a/l10n_us_ar_hr_payroll/views/hr_payroll_views.xml b/l10n_us_ar_hr_payroll/views/hr_payroll_views.xml
new file mode 100755
index 00000000..d1ed5d9c
--- /dev/null
+++ b/l10n_us_ar_hr_payroll/views/hr_payroll_views.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ hr.contract.form.inherit
+ hr.contract
+ 106
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 95720aa9c882ffad75431783ac4f144467b6e7c5 Mon Sep 17 00:00:00 2001
From: David Frick
Date: Thu, 11 Jul 2019 13:24:52 -0400
Subject: [PATCH 11/43] IMP `l10n_us_ar_hr_payroll` changed pay_period to be
dict and modified contribution and partner names to be more readable with
state name.
---
l10n_us_ar_hr_payroll/data/base.xml | 12 ++++++------
l10n_us_ar_hr_payroll/data/rules.xml | 24 +++++++++++-------------
2 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/l10n_us_ar_hr_payroll/data/base.xml b/l10n_us_ar_hr_payroll/data/base.xml
index 38534fdd..2e004924 100755
--- a/l10n_us_ar_hr_payroll/data/base.xml
+++ b/l10n_us_ar_hr_payroll/data/base.xml
@@ -2,27 +2,27 @@
-
+
Arkansas Department of Workforce Solutions - Unemployment Tax
1
-
+
Arkansas Department of Financial Administration- Income Tax Withholding
1
-
+
Arkansas Unemployment
Arkansas Department of Workforce Solutions - Unemployment
-
+
-
+
Arkansas Income Tax Withholding
Arkansas Department of Financial Administration - Income Tax Withholding
-
+
diff --git a/l10n_us_ar_hr_payroll/data/rules.xml b/l10n_us_ar_hr_payroll/data/rules.xml
index 7082c131..b9b73e35 100755
--- a/l10n_us_ar_hr_payroll/data/rules.xml
+++ b/l10n_us_ar_hr_payroll/data/rules.xml
@@ -42,7 +42,7 @@ result = categories.WAGE_US_AR_UNEMP
if result_rate == 0.0:
result = 0.0
-
+
@@ -60,22 +60,20 @@ annual_gross_pay = 0.00
allowance_amt = contract.ar_w4_allowances * 26.00
schedule_pay = contract.schedule_pay
standard_deduction = 2200
-pay_period = 0.0
additional_withholding = contract.ar_w4_additional_wh
if contract.w4_filing_status == 'married':
standard_deduction = standard_deduction * 2
-if schedule_pay == 'daily':
- pay_period = 260.0
-elif schedule_pay == 'weekly':
- pay_period = 52.0
-elif schedule_pay == 'bi-weekly':
- pay_period = 26.0
-elif schedule_pay == 'semi-monthly':
- pay_period = 24.0
-elif schedule_pay == 'monthly':
- pay_period = 12.0
+pay_period = 0.0
+pay_periods = {
+ 'weekly': 52.0,
+ 'bi-weekly': 26.0,
+ 'semi-monthly': 24.0,
+ 'monthly': 12.0
+ }
+if schedule_pay in pay_periods:
+ pay_period = pay_periods[schedule_pay]
else:
raise Exception('Invalid schedule_pay="' + schedule_pay + '" for AR Income Withholding calculation')
@@ -109,7 +107,7 @@ for row in tax_rate_table:
result = (result / pay_period) + additional_withholding
result = -result
-
+
From be5d75c18f64b858afe2f8051c367177b100e295 Mon Sep 17 00:00:00 2001
From: David Frick
Date: Wed, 17 Jul 2019 10:50:00 -0400
Subject: [PATCH 12/43] FIX `l10n_us_ar_hr_payroll` Arkansas Tax Exempt should
not exclude SUTA
---
l10n_us_ar_hr_payroll/data/rules.xml | 4 ++--
.../tests/test_us_ar_payslip_2019.py | 16 ++++++++--------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/l10n_us_ar_hr_payroll/data/rules.xml b/l10n_us_ar_hr_payroll/data/rules.xml
index b9b73e35..00fed6f5 100755
--- a/l10n_us_ar_hr_payroll/data/rules.xml
+++ b/l10n_us_ar_hr_payroll/data/rules.xml
@@ -7,7 +7,7 @@
Wage: US-AR Unemployment
WAGE_US_AR_UNEMP
python
- result = (contract.futa_type != contract.FUTA_TYPE_BASIC and not contract.ar_w4_tax_exempt and not contract.ar_w4_texarkana_exemption)
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC)
code
rate = payslip.dict.get_rate('US_AR_UNEMP')
@@ -31,7 +31,7 @@ else:
ER: US-AR Unemployment
ER_US_AR_UNEMP
python
- result = (contract.futa_type != contract.FUTA_TYPE_BASIC and not contract.ar_w4_tax_exempt and not contract.ar_w4_texarkana_exemption)
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC)
code
rate = payslip.dict.get_rate('US_AR_UNEMP')
diff --git a/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py b/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
index 0468f48f..44a4a058 100755
--- a/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
+++ b/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
@@ -24,6 +24,7 @@ class TestUsARPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
+ # Not exempt from rule 1 or rule 2 - unemployment wages., and actual unemployment.
self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], salary)
self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
@@ -32,7 +33,8 @@ class TestUsARPayslip(TestUsPayslip):
# Make a new payslip, this one will have maximums
remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - salary if (self.AR_UNEMP_MAX_WAGE - 2*salary < salary) \
else salary
-
+ # We reached the cap of 10000.0 in the first payslip.
+ self.assertEqual(0.0, remaining_AR_UNEMP_wages)
self._log('2019 Arkansas tax second payslip weekly:')
payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
payslip.compute_sheet()
@@ -43,22 +45,21 @@ class TestUsARPayslip(TestUsPayslip):
def test_taxes_with_state_exempt(self):
salary = 50000.0
- external_wages = 10000.0
tax_exempt = True # State withholding should be zero.
employee = self._createEmployee()
contract = self._createContract(employee,
salary,
- external_wages=external_wages,
struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
- futa_type=USHrContract.FUTA_TYPE_BASIC)
+ )
contract.ar_w4_tax_exempt = tax_exempt
+
self._log('2019 Arkansas exempt tax first payslip weekly:')
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('WAGE_US_AR_UNEMP', 0.0), 0.0)
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], self.AR_UNEMP_MAX_WAGE)
self.assertPayrollEqual(cats.get('ER_US_AR_UNEMP', 0.0), cats.get('WAGE_US_AR_UNEMP', 0.0) * self.AR_UNEMP)
self.assertPayrollEqual(cats.get('EE_US_AR_INC_WITHHOLD', 0.0), 0.0)
@@ -74,13 +75,12 @@ class TestUsARPayslip(TestUsPayslip):
struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'))
contract.ar_w4_texarkana_exemption = texarkana_exemption
-
self._log('2019 Arkansas tax first payslip:')
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('WAGE_US_AR_UNEMP', 0.0), 0.0)
+ self.assertPayrollEqual(cats.get('WAGE_US_AR_UNEMP', 0.0), self.AR_UNEMP_MAX_WAGE)
self.assertPayrollEqual(cats.get('ER_US_AR_UNEMP', 0.0), cats.get('WAGE_US_AR_UNEMP', 0.0) * self.AR_UNEMP)
process_payslip(payslip)
@@ -172,7 +172,7 @@ class TestUsARPayslip(TestUsPayslip):
self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_AR_UNEMP_wages * self.AR_UNEMP)
def test_over_fifty_thousand(self):
- wages = 10000.00
+ wages = 10000.00 # 10000.00 monthly is over 50,000 annually.
schedule_pay = 'monthly'
pay_periods = 12
additional_wh = 150.0
From 6d2d6f4f469f4542b3466b94d85fae9adcc8a327 Mon Sep 17 00:00:00 2001
From: David Frick
Date: Wed, 14 Aug 2019 16:19:53 -0400
Subject: [PATCH 13/43] MIG `l10n_us_ar_hr_payroll` for 12.0. Changed variables
names to match Arkansas Tax Form. Added abbreviations for partners in
manifest.
---
l10n_us_ar_hr_payroll/__manifest__.py | 8 +--
l10n_us_ar_hr_payroll/data/base.xml | 6 +--
l10n_us_ar_hr_payroll/data/rules.xml | 8 +--
l10n_us_ar_hr_payroll/models/hr_payroll.py | 8 +--
.../tests/test_us_ar_payslip_2019.py | 53 +++++++++----------
.../views/hr_payroll_views.xml | 8 +--
6 files changed, 45 insertions(+), 46 deletions(-)
diff --git a/l10n_us_ar_hr_payroll/__manifest__.py b/l10n_us_ar_hr_payroll/__manifest__.py
index 1bb42dad..45f86c6b 100755
--- a/l10n_us_ar_hr_payroll/__manifest__.py
+++ b/l10n_us_ar_hr_payroll/__manifest__.py
@@ -4,13 +4,13 @@
'license': 'AGPL-3',
'category': 'Localization',
'depends': ['l10n_us_hr_payroll'],
- 'version': '11.0.2019.0.0',
+ 'version': '12.0.2019.0.0',
'description': """
-USA::Arkansas Payroll Rules.
+USA - Arkansas Payroll Rules.
==================================
-* Contribution register and partner for Arkansas Department of Financial Administration - Income Tax Withholding
-* Contribution register and partner for Arkansas Department of Workforce Solutions - Unemployment
+* Contribution register and partner for Arkansas Department of Financial Administration (ADFA) - Income Tax Withholding
+* Contribution register and partner for Arkansas Department of Workforce Solutions (ADWS) - Unemployment
* Contract level Arkansas Exemptions
* Company level Arkansas Unemployment Rate
* Salary Structure for Arkansas
diff --git a/l10n_us_ar_hr_payroll/data/base.xml b/l10n_us_ar_hr_payroll/data/base.xml
index 2e004924..ce1b73cc 100755
--- a/l10n_us_ar_hr_payroll/data/base.xml
+++ b/l10n_us_ar_hr_payroll/data/base.xml
@@ -3,12 +3,12 @@
- Arkansas Department of Workforce Solutions - Unemployment Tax
+ Arkansas Department of Workforce Solutions - Unemployment Tax
1
- Arkansas Department of Financial Administration- Income Tax Withholding
+ Arkansas Department of Financial Administration - Income Tax Withholding
1
@@ -16,7 +16,7 @@
Arkansas Unemployment
- Arkansas Department of Workforce Solutions - Unemployment
+ Arkansas Department of Workforce Solutions - Unemployment
diff --git a/l10n_us_ar_hr_payroll/data/rules.xml b/l10n_us_ar_hr_payroll/data/rules.xml
index 00fed6f5..71b77a79 100755
--- a/l10n_us_ar_hr_payroll/data/rules.xml
+++ b/l10n_us_ar_hr_payroll/data/rules.xml
@@ -11,7 +11,7 @@
code
rate = payslip.dict.get_rate('US_AR_UNEMP')
-year = int(payslip.dict.date_to[:4])
+year = payslip.dict.date_to.year
ytd = payslip.sum('WAGE_US_AR_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01')
ytd += contract.external_wages
remaining = rate.wage_limit_year - ytd
@@ -52,15 +52,15 @@ if result_rate == 0.0:
EE: US-AR Income Tax Withholding
EE_US_AR_INC_WITHHOLD
python
- result = not (contract.ar_w4_texarkana_exemption or contract.ar_w4_tax_exempt)
+ result = not (contract.ar_ar4ec_texarkana_exemption or contract.ar_ar4ec_tax_exempt)
code
wages = categories.GROSS
annual_gross_pay = 0.00
-allowance_amt = contract.ar_w4_allowances * 26.00
+allowance_amt = contract.ar_ar4ec_allowances * 26.00
schedule_pay = contract.schedule_pay
standard_deduction = 2200
-additional_withholding = contract.ar_w4_additional_wh
+additional_withholding = contract.ar_ar4ec_additional_wh
if contract.w4_filing_status == 'married':
standard_deduction = standard_deduction * 2
diff --git a/l10n_us_ar_hr_payroll/models/hr_payroll.py b/l10n_us_ar_hr_payroll/models/hr_payroll.py
index b6b51866..e477e67e 100755
--- a/l10n_us_ar_hr_payroll/models/hr_payroll.py
+++ b/l10n_us_ar_hr_payroll/models/hr_payroll.py
@@ -4,7 +4,7 @@ from odoo import models, fields, api
class USARHrContract(models.Model):
_inherit = 'hr.contract'
- ar_w4_allowances = fields.Integer(string='Arkansas W-4 allowances', default=0)
- ar_w4_additional_wh = fields.Float(string="Arkansas Additional Withholding", default=0.0)
- ar_w4_tax_exempt = fields.Boolean(string="Tax Exempt")
- ar_w4_texarkana_exemption = fields.Boolean(string="Texarkana Exemption")
+ ar_ar4ec_allowances = fields.Integer(string='Arkansas AR-4EC Allowances', default=0)
+ ar_ar4ec_additional_wh = fields.Float(string="Arkansas AR-4EC Additional Withholding", default=0.0)
+ ar_ar4ec_tax_exempt = fields.Boolean(string="Arkansas AR-4EC Tax Exempt")
+ ar_ar4ec_texarkana_exemption = fields.Boolean(string="Arkansas AR-4EC Texarkana Exemption")
diff --git a/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py b/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
index 44a4a058..3f72292f 100755
--- a/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
+++ b/l10n_us_ar_hr_payroll/tests/test_us_ar_payslip_2019.py
@@ -1,5 +1,4 @@
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
-from odoo.addons.l10n_us_hr_payroll.models.l10n_us_hr_payroll import USHrContract
class TestUsARPayslip(TestUsPayslip):
@@ -31,17 +30,17 @@ class TestUsARPayslip(TestUsPayslip):
process_payslip(payslip)
# Make a new payslip, this one will have maximums
- remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - salary if (self.AR_UNEMP_MAX_WAGE - 2*salary < salary) \
+ remaining_ar_unemp_wages = self.AR_UNEMP_MAX_WAGE - salary if (self.AR_UNEMP_MAX_WAGE - 2*salary < salary) \
else salary
# We reached the cap of 10000.0 in the first payslip.
- self.assertEqual(0.0, remaining_AR_UNEMP_wages)
+ self.assertEqual(0.0, remaining_ar_unemp_wages)
self._log('2019 Arkansas tax second payslip weekly:')
payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_AR_UNEMP_wages)
- self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_AR_UNEMP_wages * self.AR_UNEMP)
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_ar_unemp_wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_ar_unemp_wages * self.AR_UNEMP)
def test_taxes_with_state_exempt(self):
salary = 50000.0
@@ -52,7 +51,7 @@ class TestUsARPayslip(TestUsPayslip):
salary,
struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
)
- contract.ar_w4_tax_exempt = tax_exempt
+ contract.ar_ar4ec_tax_exempt = tax_exempt
self._log('2019 Arkansas exempt tax first payslip weekly:')
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
@@ -73,7 +72,7 @@ class TestUsARPayslip(TestUsPayslip):
contract = self._createContract(employee,
salary,
struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'))
- contract.ar_w4_texarkana_exemption = texarkana_exemption
+ contract.ar_ar4ec_texarkana_exemption = texarkana_exemption
self._log('2019 Arkansas tax first payslip:')
payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
@@ -99,8 +98,8 @@ class TestUsARPayslip(TestUsPayslip):
wages,
struct_id=self.ref('l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
schedule_pay=schedule_pay)
- contract.ar_w4_additional_wh = 0.0
- contract.ar_w4_allowances = exemptions
+ contract.ar_ar4ec_additional_wh = 0.0
+ contract.ar_ar4ec_allowances = exemptions
self.assertEqual(contract.schedule_pay, 'monthly')
@@ -114,7 +113,7 @@ class TestUsARPayslip(TestUsPayslip):
# TODO: change to hand the test_ar_amt already be divided by pay periods
self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
- contract.ar_w4_additional_wh = additional_wh
+ contract.ar_ar4ec_additional_wh = additional_wh
payslip.compute_sheet()
cats = self._getCategories(payslip)
@@ -137,8 +136,8 @@ class TestUsARPayslip(TestUsPayslip):
struct_id=self.ref(
'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
schedule_pay=schedule_pay)
- contract.ar_w4_additional_wh = 0.0
- contract.ar_w4_allowances = exemptions
+ contract.ar_ar4ec_additional_wh = 0.0
+ contract.ar_ar4ec_allowances = exemptions
self.assertEqual(contract.schedule_pay, 'monthly')
@@ -151,7 +150,7 @@ class TestUsARPayslip(TestUsPayslip):
self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
- contract.ar_w4_additional_wh = additional_wh
+ contract.ar_ar4ec_additional_wh = additional_wh
payslip.compute_sheet()
cats = self._getCategories(payslip)
self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
@@ -160,7 +159,7 @@ class TestUsARPayslip(TestUsPayslip):
# Make a new payslip, this one will have maximums
- remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - wages if (self.AR_UNEMP_MAX_WAGE - 2 * wages < wages) \
+ remaining_ar_unemp_wages = self.AR_UNEMP_MAX_WAGE - wages if (self.AR_UNEMP_MAX_WAGE - 2 * wages < wages) \
else wages
self._log('2019 Arkansas tax second payslip weekly:')
@@ -168,8 +167,8 @@ class TestUsARPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_AR_UNEMP_wages)
- self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_AR_UNEMP_wages * self.AR_UNEMP)
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_ar_unemp_wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_ar_unemp_wages * self.AR_UNEMP)
def test_over_fifty_thousand(self):
wages = 10000.00 # 10000.00 monthly is over 50,000 annually.
@@ -186,8 +185,8 @@ class TestUsARPayslip(TestUsPayslip):
struct_id=self.ref(
'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
schedule_pay=schedule_pay)
- contract.ar_w4_additional_wh = 0.0
- contract.ar_w4_allowances = exemptions
+ contract.ar_ar4ec_additional_wh = 0.0
+ contract.ar_ar4ec_allowances = exemptions
self.assertEqual(contract.schedule_pay, 'monthly')
@@ -200,7 +199,7 @@ class TestUsARPayslip(TestUsPayslip):
self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
- contract.ar_w4_additional_wh = additional_wh
+ contract.ar_ar4ec_additional_wh = additional_wh
payslip.compute_sheet()
cats = self._getCategories(payslip)
self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
@@ -209,7 +208,7 @@ class TestUsARPayslip(TestUsPayslip):
# Make a new payslip, this one will have maximums
- remaining_AR_UNEMP_wages = self.AR_UNEMP_MAX_WAGE - wages if (self.AR_UNEMP_MAX_WAGE - 2 * wages < wages) \
+ remaining_ar_unemp_wages = self.AR_UNEMP_MAX_WAGE - wages if (self.AR_UNEMP_MAX_WAGE - 2 * wages < wages) \
else wages
self._log('2019 Arkansas tax second payslip weekly:')
@@ -217,8 +216,8 @@ class TestUsARPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_AR_UNEMP_wages)
- self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_AR_UNEMP_wages * self.AR_UNEMP)
+ self.assertPayrollEqual(cats['WAGE_US_AR_UNEMP'], remaining_ar_unemp_wages)
+ self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], remaining_ar_unemp_wages * self.AR_UNEMP)
def test_married(self):
wages = 5500.00
@@ -237,8 +236,8 @@ class TestUsARPayslip(TestUsPayslip):
struct_id=self.ref(
'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
schedule_pay=schedule_pay)
- contract.ar_w4_additional_wh = additional_wh
- contract.ar_w4_allowances = exemptions
+ contract.ar_ar4ec_additional_wh = additional_wh
+ contract.ar_ar4ec_allowances = exemptions
contract.w4_filing_status = w4_filing_status
self.assertEqual(contract.w4_filing_status, 'married')
@@ -269,8 +268,8 @@ class TestUsARPayslip(TestUsPayslip):
struct_id=self.ref(
'l10n_us_ar_hr_payroll.hr_payroll_salary_structure_us_ar_employee'),
schedule_pay=schedule_pay)
- contract.ar_w4_additional_wh = 0
- contract.ar_w4_allowances = exemptions
+ contract.ar_ar4ec_additional_wh = 0
+ contract.ar_ar4ec_allowances = exemptions
contract.w4_filling_status = w4_filling_status
self.assertEqual(contract.w4_filling_status, 'single')
@@ -284,7 +283,7 @@ class TestUsARPayslip(TestUsPayslip):
self.assertPayrollEqual(cats['ER_US_AR_UNEMP'], cats['WAGE_US_AR_UNEMP'] * self.AR_UNEMP)
self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods))
- contract.ar_w4_additional_wh = additional_wh
+ contract.ar_ar4ec_additional_wh = additional_wh
payslip.compute_sheet()
cats = self._getCategories(payslip)
self.assertPayrollEqual(cats['EE_US_AR_INC_WITHHOLD'], -(test_ar_amt / pay_periods) - additional_wh)
diff --git a/l10n_us_ar_hr_payroll/views/hr_payroll_views.xml b/l10n_us_ar_hr_payroll/views/hr_payroll_views.xml
index d1ed5d9c..e3fc69be 100755
--- a/l10n_us_ar_hr_payroll/views/hr_payroll_views.xml
+++ b/l10n_us_ar_hr_payroll/views/hr_payroll_views.xml
@@ -10,10 +10,10 @@
-
-
-
-
+
+
+
+
From 853e8f8b13750e880985bf2a2a800173ccf4a0fc Mon Sep 17 00:00:00 2001
From: David Frick
Date: Thu, 11 Jul 2019 16:46:42 -0400
Subject: [PATCH 14/43] NEW `l10n_us_ia_hr_payroll` for 11.0
---
l10n_us_ia_hr_payroll/__init__.py | 1 +
l10n_us_ia_hr_payroll/__manifest__.py | 29 +++
l10n_us_ia_hr_payroll/data/base.xml | 47 +++++
l10n_us_ia_hr_payroll/data/final.xml | 17 ++
l10n_us_ia_hr_payroll/data/rates.xml | 14 ++
l10n_us_ia_hr_payroll/data/rules.xml | 159 +++++++++++++++
l10n_us_ia_hr_payroll/models/__init__.py | 1 +
l10n_us_ia_hr_payroll/models/hr_payroll.py | 9 +
l10n_us_ia_hr_payroll/tests/__init__.py | 1 +
.../tests/test_us_ia_payslip.py | 192 ++++++++++++++++++
.../views/hr_payroll_views.xml | 22 ++
11 files changed, 492 insertions(+)
create mode 100755 l10n_us_ia_hr_payroll/__init__.py
create mode 100755 l10n_us_ia_hr_payroll/__manifest__.py
create mode 100755 l10n_us_ia_hr_payroll/data/base.xml
create mode 100755 l10n_us_ia_hr_payroll/data/final.xml
create mode 100755 l10n_us_ia_hr_payroll/data/rates.xml
create mode 100755 l10n_us_ia_hr_payroll/data/rules.xml
create mode 100644 l10n_us_ia_hr_payroll/models/__init__.py
create mode 100755 l10n_us_ia_hr_payroll/models/hr_payroll.py
create mode 100755 l10n_us_ia_hr_payroll/tests/__init__.py
create mode 100755 l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py
create mode 100755 l10n_us_ia_hr_payroll/views/hr_payroll_views.xml
diff --git a/l10n_us_ia_hr_payroll/__init__.py b/l10n_us_ia_hr_payroll/__init__.py
new file mode 100755
index 00000000..0650744f
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/l10n_us_ia_hr_payroll/__manifest__.py b/l10n_us_ia_hr_payroll/__manifest__.py
new file mode 100755
index 00000000..28dc125d
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/__manifest__.py
@@ -0,0 +1,29 @@
+{
+ 'name': 'USA - Iowa - Payroll',
+ 'author': 'Hibou Corp. ',
+ 'license': 'AGPL-3',
+ 'category': 'Localization',
+ 'depends': ['l10n_us_hr_payroll'],
+ 'version': '11.0.2019.0.0',
+ 'description': """
+USA::Iowa Payroll Rules.
+==================================
+
+* Contribution register and partner for Additional WithholdingTaxaction - Income Tax Withholding
+* Contribution register and partner for Iowa Workforce Development- Unemployment
+* Contract level Iowa Exemptions
+* Company level Iowa Unemployment Rate
+* Salary Structure for Iowa
+ """,
+
+ 'auto_install': False,
+ 'website': 'https://hibou.io/',
+ 'data': [
+ 'views/hr_payroll_views.xml',
+ 'data/base.xml',
+ 'data/rates.xml',
+ 'data/rules.xml',
+ 'data/final.xml',
+ ],
+ 'installable': True
+}
diff --git a/l10n_us_ia_hr_payroll/data/base.xml b/l10n_us_ia_hr_payroll/data/base.xml
new file mode 100755
index 00000000..6e5a2391
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/data/base.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+ Iowa Workforce Development- Unemployment Tax
+ 1
+
+
+
+ Iowa Department of Revenue - Income Tax Withholding
+ 1
+
+
+
+
+
+ Iowa Unemployment
+ Iowa Workforce Development - Unemployment
+
+
+
+ Iowa Income Tax Withholding
+ Iowa Department of Revenue - Income Tax Withholding
+
+
+
+
+
+
+ Wage: US-IA Unemployment
+ WAGE_US_IA_UNEMP
+
+
+
+ ER: US-IA Unemployment
+ ER_US_IA_UNEMP
+
+
+
+
+ EE: US-IA Income Tax Withholding
+ EE_US_IA_INC_WITHHOLD
+
+
+
+
diff --git a/l10n_us_ia_hr_payroll/data/final.xml b/l10n_us_ia_hr_payroll/data/final.xml
new file mode 100755
index 00000000..58b73b5a
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/data/final.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ US_IA_EMP
+ USA Iowa Employee
+
+
+
+
+
+
diff --git a/l10n_us_ia_hr_payroll/data/rates.xml b/l10n_us_ia_hr_payroll/data/rates.xml
new file mode 100755
index 00000000..6c910f0c
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/data/rates.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ US IA Unemployment
+ US_IA_UNEMP
+ 1.0
+ 2019-01-01
+
+
+
+
+
diff --git a/l10n_us_ia_hr_payroll/data/rules.xml b/l10n_us_ia_hr_payroll/data/rules.xml
new file mode 100755
index 00000000..eebebd47
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/data/rules.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+ Wage: US-IA Unemployment
+ WAGE_US_IA_UNEMP
+ python
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC)
+ code
+
+rate = payslip.dict.get_rate('US_IA_UNEMP')
+year = int(payslip.dict.date_to[:4])
+ytd = payslip.sum('WAGE_US_IA_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01')
+ytd += contract.external_wages
+remaining = rate.wage_limit_year - ytd
+if remaining <= 0.0:
+ result = 0
+elif remaining < categories.BASIC:
+ result = remaining
+else:
+ result = categories.BASIC
+
+
+
+
+
+
+
+ ER: US-IA Unemployment
+ ER_US_IA_UNEMP
+ python
+ result = (contract.futa_type != contract.FUTA_TYPE_BASIC)
+ code
+
+rate = payslip.dict.get_rate('US_IA_UNEMP')
+result_rate = -rate.rate
+result = categories.WAGE_US_IA_UNEMP
+
+# result_rate of 0 implies 100% due to bug
+if result_rate == 0.0:
+ result = 0.0
+
+
+
+
+
+
+
+
+ EE: US-IA Income Tax Withholding
+ EE_US_IA_INC_WITHHOLD
+ python
+ result = not contract.ia_w4_tax_exempt
+ code
+
+wages = categories.GROSS
+federal_withholding = categories.EE_US_FED_INC_WITHHOLD
+schedule_pay = contract.schedule_pay
+allowances = contract.ia_w4_allowances
+# It is + federal_withholding because federal_withholding is negative.
+t1 = wages + federal_withholding
+standard_deduction_table = {
+ 'daily': (6.50, 16.00),
+ 'weekly': (32.50, 80.00),
+ 'bi-weekly': (65.00, 160.00),
+ 'semi-monthly': (70.42, 173.33),
+ 'monthly': (140.83, 346.67),
+ 'annually': (1690.00, 4160.00)}
+t2 = t1 - standard_deduction_table[schedule_pay][0] if allowances < 2 else standard_deduction_table[schedule_pay][1]
+# IMPORTANT -- ALL RATES ARE ALREADY DIVIDED BY 100 -> 8.53% is in the table as 0.0853
+if schedule_pay == 'weekly':
+ tax_rate_table = [
+ (25.63, 0.0033, 0.0),
+ (51.27, 0.0067, 0.08),
+ (102.52, 0.0225, 0.025),
+ (230.67, 0.0414, 1.40),
+ (384.46, 0.0563, 6.71),
+ (512.62, 0.0596, 15.37),
+ (768.92, 0.0625, 23.01),
+ (1153.38, 0.0744, 39.03),
+ (float('inf'), 0.0853, 67.63),
+ ]
+elif schedule_pay == 'bi-weekly':
+ tax_rate_table = [
+ (51.27, 0.0033, 0.00),
+ (102.54, 0.0067, 0.17),
+ (205.04, 0.00225, 0.51),
+ (461.35, 0.0414, 2.82),
+ (768.92, 0.0563, 13.43),
+ (1025.23, 0.0596, 30.75),
+ (1537.85, 0.0625, 46.03),
+ (2306.77, 0.0744, 78.07),
+ (float('inf'), 0.0853, 135.28)
+ ]
+elif schedule_pay == 'semi-monthly':
+ tax_rate_table = [
+ (55.54, 0.0033, 0.00),
+ (111.08, 0.0067, 0.18),
+ (222.13, 0.0225, 0.55),
+ (499.79, 0.0414, 3.05),
+ (833.00, 0.0563, 14.59),
+ (1110.67, 0.0596, 33.31),
+ (1666.00, 0.0625, 49.86),
+ (2499.00, 0.0744, 84.57),
+ (float('inf'), 0.0853, 146.55)
+ ]
+elif schedule_pay == 'monthly':
+ tax_rate_table = [
+ (111.08, 0.0033, 0.00),
+ (222.17, 0.0067, 0.37),
+ (444.25, 0.0225, 1.11),
+ (999.58, 0.0414, 6.11),
+ (1666.00, 0.0563, 29.10),
+ (2221.33, 0.0596, 62.66),
+ (3332.00, 0.0625, 99.72),
+ (4998.00, 0.0744, 169.14),
+ (float('inf'), 0.0853, 293.09)
+ ]
+elif schedule_pay == 'annual':
+ tax_rate_table = [
+ (1333.00, 0.0033, 0.00),
+ (2666.00, 0.0067, 4.40),
+ (5331.00, 0.0225, 13.33),
+ (11995.00, 0.0414, 73.29),
+ (19992.00, 0.0563, 349.19),
+ (26656.00, 0.0596, 799.41),
+ (39984.00, 0.0625, 1196.58),
+ (59976.00, 0.0744, 2029.58),
+ (float('inf'), 0.0853, 3516.98)
+ ]
+
+t3 = 0.0
+last = 0.0
+for row in tax_rate_table:
+ cap, rate, flat_fee = row
+ if cap > t2:
+ taxed_amount = t2 - last
+ t3 = flat_fee + (rate * taxed_amount)
+ break
+ last = cap
+
+deduction_per_allowance = {
+ 'daily': 0.15,
+ 'weekly': 0.77,
+ 'bi-weekly': 1.54,
+ 'semi-monthly': 3.33,
+ 'annually': 40.00
+ }
+t4 = t3 - (deduction_per_allowance[schedule_pay] * allowances)
+t5 = t4 + contract.ia_w4_additional_wh
+result = -t5
+
+
+
+
+
diff --git a/l10n_us_ia_hr_payroll/models/__init__.py b/l10n_us_ia_hr_payroll/models/__init__.py
new file mode 100644
index 00000000..e99aa24a
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/models/__init__.py
@@ -0,0 +1 @@
+from . import hr_payroll
diff --git a/l10n_us_ia_hr_payroll/models/hr_payroll.py b/l10n_us_ia_hr_payroll/models/hr_payroll.py
new file mode 100755
index 00000000..c171154a
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/models/hr_payroll.py
@@ -0,0 +1,9 @@
+from odoo import models, fields, api
+
+
+class USIAHrContract(models.Model):
+ _inherit = 'hr.contract'
+
+ ia_w4_allowances = fields.Integer(string='Iowa W-4 allowances', default=0)
+ ia_w4_additional_wh = fields.Float(string="Iowa Additional Withholding", default=0.0)
+ ia_w4_tax_exempt = fields.Boolean(string="Tax Exempt")
diff --git a/l10n_us_ia_hr_payroll/tests/__init__.py b/l10n_us_ia_hr_payroll/tests/__init__.py
new file mode 100755
index 00000000..4f771ce0
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_us_ia_payslip
diff --git a/l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py b/l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py
new file mode 100755
index 00000000..53d1fd44
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py
@@ -0,0 +1,192 @@
+from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
+from odoo.addons.l10n_us_hr_payroll.models.l10n_us_hr_payroll import USHrContract
+
+
+class TestUsIAPayslip(TestUsPayslip):
+ IA_UNEMP_MAX_WAGE = 30600
+ IA_UNEMP = -1.0 / 100.0
+ IA_INC_TAX = -0.0535
+
+ def test_taxes_weekly(self):
+ wages = 30000.00
+ schedule_pay = 'weekly'
+ allowances = 1
+ additional_wh = 0.00
+ employee = self._createEmployee()
+ contract = self._createContract(employee, wages,
+ struct_id=self.ref('l10n_us_ia_hr_payroll.hr_payroll_salary_structure_us_ia_employee'),
+ schedule_pay=schedule_pay)
+ contract.ia_w4_allowances = allowances
+ contract.ia_w4_additional_wh = additional_wh
+
+ self.assertEqual(contract.schedule_pay, 'weekly')
+
+ self._log('2019 Iowa tax first payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
+ # withholding amount because it is calculated in the base US payroll module as a negative
+ # t1 = 30000 - (10399.66) = 19600.34
+ t1_to_test = wages + cats['EE_US_FED_INC_WITHHOLD']
+ self.assertPayrollEqual(t1_to_test, 19600.34)
+
+ # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
+ # In our case, we have a weekly period which on the table has a std deduct. of $32.50 for 0 or 1 allowances,
+ # and 80.00 of 2 or more allowances.
+ standard_deduction = 32.50 # The allowance tells us what standard_deduction amount to use.
+ # t2 = 19600.34 - 32.50 = 19567.84
+ t2_to_test = t1_to_test - standard_deduction
+ self.assertPayrollEqual(t2_to_test, 19567.84)
+ # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
+ # 1153.38 is the bracket floor. 8.53 is the rate, and 67.63 is the flat fee.
+ # t3 = 1638.38
+ t3_to_test = ((t2_to_test - 1153.38) * (8.53 / 100)) + 67.63
+ self.assertPayrollEqual(t3_to_test, 1638.38)
+ # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
+ # deduction amount per allowance is 0.77
+ # t4 = 1638.38 - 0.77 = 155.03
+ t4_to_test = t3_to_test - (0.77 * allowances)
+ self.assertPayrollEqual(t4_to_test, 1637.61)
+ # t5 is our T4 plus the additional withholding per period
+ # t5 = 1637.61 + 0.0
+ # Convert to negative as well.
+ t5_to_test = -t4_to_test - additional_wh
+ self.assertPayrollEqual(t5_to_test, -1637.61)
+
+ self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], cats['WAGE_US_IA_UNEMP'] * self.IA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_IA_INC_WITHHOLD'], t5_to_test)
+
+ # Test additional
+ additional_wh = 15.00
+ contract.ia_w4_additional_wh = additional_wh
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+ self.assertPayrollEqual(cats['EE_US_IA_INC_WITHHOLD'], t5_to_test - additional_wh)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_IA_UNEMP_wages = self.IA_UNEMP_MAX_WAGE - wages if (self.IA_UNEMP_MAX_WAGE - 2*wages < wages) \
+ else wages
+
+ self._log('2019 Iowa tax second payslip weekly:')
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], remaining_IA_UNEMP_wages)
+ self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], remaining_IA_UNEMP_wages * self.IA_UNEMP)
+
+ def test_taxes_biweekly(self):
+ wages = 3000.00
+ schedule_pay = 'bi-weekly'
+ allowances = 1
+ additional_wh = 0.00
+ employee = self._createEmployee()
+ contract = self._createContract(employee, wages,
+ struct_id=self.ref(
+ 'l10n_us_ia_hr_payroll.hr_payroll_salary_structure_us_ia_employee'),
+ schedule_pay=schedule_pay)
+ contract.ia_w4_allowances = allowances
+ contract.ia_w4_additional_wh = additional_wh
+
+ self.assertEqual(contract.schedule_pay, 'bi-weekly')
+
+ self._log('2019 Iowa tax first payslip bi-weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
+ # withholding amount because it is calculated in the base US payroll module as a negative
+ t1_to_test = wages + cats['EE_US_FED_INC_WITHHOLD']
+ # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
+ # In our case, we have a biweekly period which on the table has a std deduct. of $65.00 for 0 or 1 allowances,
+ # and $160.00 of 2 or more allowances.
+ standard_deduction = 65.00 # The allowance tells us what standard_deduction amount to use.
+ t2_to_test = t1_to_test - standard_deduction
+ # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
+ t3_to_test = ((t2_to_test - 2306.77) * (8.53 / 100)) + 135.28
+ # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
+ # deduction amount per allowance is 0.77
+ t4_to_test = t3_to_test - (1.54 * allowances)
+ # t5 is our T4 plus the additional withholding per period
+ t5_to_test = -t4_to_test - additional_wh
+
+ self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], cats['WAGE_US_IA_UNEMP'] * self.IA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_IA_INC_WITHHOLD'], t5_to_test - additional_wh)
+
+ process_payslip(payslip)
+
+ def test_taxes_with_external_weekly(self):
+ wages = 2500.00
+ schedule_pay = 'weekly'
+ allowances = 1
+ additional_wh = 0.00
+ external_wages = 500.0
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee, wages, external_wages=external_wages,
+ struct_id=self.ref('l10n_us_ia_hr_payroll.hr_payroll_salary_structure_us_ia_employee'),
+ schedule_pay=schedule_pay)
+ contract.ia_w4_additional_wh = additional_wh
+ contract.ia_w4_allowances = allowances
+
+ self._log('2019 Iowa external tax first payslip external weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+
+ # T1 is the gross taxable wages for the pay period minus the Federal withholding amount. We add the federal
+ # withholding amount because it is calculated in the base US payroll module as a negative
+ t1_to_test = wages + cats['EE_US_FED_INC_WITHHOLD']
+ # T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
+ # In our case, we have a weekly period which on the table has a std deduct. of $32.50 for 0 or 1 allowances,
+ # and 80.00 of 2 or more allowances.
+ standard_deduction = 32.50 # The allowance tells us what standard_deduction amount to use.
+ t2_to_test = t1_to_test - standard_deduction
+ # T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
+ t3_to_test = ((t2_to_test - 1153.38) * (8.53 / 100)) + 67.63
+ # T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
+ # deduction amount per allowance is 0.77
+ t4_to_test = t3_to_test - (0.77 * allowances)
+ # t5 is our T4 plus the additional withholding per period
+ t5_to_test = -t4_to_test - additional_wh
+
+ self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], wages)
+ self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], cats['WAGE_US_IA_UNEMP'] * self.IA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_IA_INC_WITHHOLD'], t5_to_test)
+
+ process_payslip(payslip)
+
+ def test_taxes_with_state_exempt_weekly(self):
+ salary = 5000.0
+ external_wages = 10000.0
+ schedule_pay = 'weekly'
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ salary,
+ external_wages=external_wages,
+ struct_id=self.ref('l10n_us_ia_hr_payroll.hr_payroll_salary_structure_us_ia_employee'),
+ futa_type=USHrContract.FUTA_TYPE_BASIC,
+ schedule_pay=schedule_pay)
+ contract.ia_w4_tax_exempt = True
+
+ self._log('2019 Iowa exempt tax first payslip exempt weekly:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats.get('WAGE_US_IA_UNEMP', 0.0), 0.0)
+ self.assertPayrollEqual(cats.get('ER_US_IA_UNEMP', 0.0), cats.get('WAGE_US_IA_UNEMP', 0.0) * self.IA_UNEMP)
+
+
+
+
diff --git a/l10n_us_ia_hr_payroll/views/hr_payroll_views.xml b/l10n_us_ia_hr_payroll/views/hr_payroll_views.xml
new file mode 100755
index 00000000..f061cceb
--- /dev/null
+++ b/l10n_us_ia_hr_payroll/views/hr_payroll_views.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ hr.contract.form.inherit
+ hr.contract
+ 119
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From a9d35b4c6b43ca5f864b1fbd7c146ddd42219f9b Mon Sep 17 00:00:00 2001
From: David Frick
Date: Mon, 22 Jul 2019 15:41:47 -0400
Subject: [PATCH 15/43] FIX 'l10n_us_ia_hr_payroll` missing mothnly allowance
per standard deduction
---
l10n_us_ia_hr_payroll/data/rules.xml | 9 +++++----
l10n_us_ia_hr_payroll/views/hr_payroll_views.xml | 2 ++
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/l10n_us_ia_hr_payroll/data/rules.xml b/l10n_us_ia_hr_payroll/data/rules.xml
index eebebd47..2281d900 100755
--- a/l10n_us_ia_hr_payroll/data/rules.xml
+++ b/l10n_us_ia_hr_payroll/data/rules.xml
@@ -69,7 +69,7 @@ standard_deduction_table = {
'semi-monthly': (70.42, 173.33),
'monthly': (140.83, 346.67),
'annually': (1690.00, 4160.00)}
-t2 = t1 - standard_deduction_table[schedule_pay][0] if allowances < 2 else standard_deduction_table[schedule_pay][1]
+t2 = t1 - standard_deduction_table[schedule_pay][0] if (allowances < 2) else standard_deduction_table[schedule_pay][1]
# IMPORTANT -- ALL RATES ARE ALREADY DIVIDED BY 100 -> 8.53% is in the table as 0.0853
if schedule_pay == 'weekly':
tax_rate_table = [
@@ -136,7 +136,7 @@ t3 = 0.0
last = 0.0
for row in tax_rate_table:
cap, rate, flat_fee = row
- if cap > t2:
+ if cap > t2:
taxed_amount = t2 - last
t3 = flat_fee + (rate * taxed_amount)
break
@@ -146,8 +146,9 @@ deduction_per_allowance = {
'daily': 0.15,
'weekly': 0.77,
'bi-weekly': 1.54,
- 'semi-monthly': 3.33,
- 'annually': 40.00
+ 'semi-monthly': 1.67,
+ 'monthly': 3.33,
+ 'annually': 40.00,
}
t4 = t3 - (deduction_per_allowance[schedule_pay] * allowances)
t5 = t4 + contract.ia_w4_additional_wh
diff --git a/l10n_us_ia_hr_payroll/views/hr_payroll_views.xml b/l10n_us_ia_hr_payroll/views/hr_payroll_views.xml
index f061cceb..4b822364 100755
--- a/l10n_us_ia_hr_payroll/views/hr_payroll_views.xml
+++ b/l10n_us_ia_hr_payroll/views/hr_payroll_views.xml
@@ -1,5 +1,6 @@
+
hr.contract.form.inherit
@@ -19,4 +20,5 @@
+
From aa5c1d76ccfd6bdfee7b3fe213801e6ae0828d7e Mon Sep 17 00:00:00 2001
From: David Frick
Date: Wed, 14 Aug 2019 18:28:19 -0400
Subject: [PATCH 16/43] MIG `l10n_us_ar_hr_payroll` added oldname to fields for
remapping
---
l10n_us_ar_hr_payroll/models/hr_payroll.py | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/l10n_us_ar_hr_payroll/models/hr_payroll.py b/l10n_us_ar_hr_payroll/models/hr_payroll.py
index e477e67e..2bb52b2c 100755
--- a/l10n_us_ar_hr_payroll/models/hr_payroll.py
+++ b/l10n_us_ar_hr_payroll/models/hr_payroll.py
@@ -4,7 +4,11 @@ from odoo import models, fields, api
class USARHrContract(models.Model):
_inherit = 'hr.contract'
- ar_ar4ec_allowances = fields.Integer(string='Arkansas AR-4EC Allowances', default=0)
- ar_ar4ec_additional_wh = fields.Float(string="Arkansas AR-4EC Additional Withholding", default=0.0)
- ar_ar4ec_tax_exempt = fields.Boolean(string="Arkansas AR-4EC Tax Exempt")
- ar_ar4ec_texarkana_exemption = fields.Boolean(string="Arkansas AR-4EC Texarkana Exemption")
+ ar_ar4ec_allowances = fields.Integer(string='Arkansas AR-4EC Allowances',
+ oldname='ar_w4_allowances')
+ ar_ar4ec_additional_wh = fields.Float(string="Arkansas AR-4EC Additional Withholding",
+ oldname='ar_w4_additional_wh')
+ ar_ar4ec_tax_exempt = fields.Boolean(string='Arkansas AR-4EC Tax Exempt',
+ oldname='ar_w4_tax_exempt')
+ ar_ar4ec_texarkana_exemption = fields.Boolean(string='Arkansas AR-4EC Texarkana Exemption',
+ oldname='ar_w4_texarkana_exemption')
From 71efc67e0567b580d6ae830fe83d16213b9c1794 Mon Sep 17 00:00:00 2001
From: David Frick
Date: Wed, 14 Aug 2019 18:44:22 -0400
Subject: [PATCH 17/43] MIG `l10n_us_ia_hr_payroll` for 12.0. removed default
values of fields, and removed superficial tests.
---
l10n_us_ia_hr_payroll/__manifest__.py | 8 ++---
l10n_us_ia_hr_payroll/data/rules.xml | 2 +-
l10n_us_ia_hr_payroll/models/hr_payroll.py | 6 ++--
.../tests/test_us_ia_payslip.py | 30 +++++++------------
4 files changed, 18 insertions(+), 28 deletions(-)
diff --git a/l10n_us_ia_hr_payroll/__manifest__.py b/l10n_us_ia_hr_payroll/__manifest__.py
index 28dc125d..7cc7b1d0 100755
--- a/l10n_us_ia_hr_payroll/__manifest__.py
+++ b/l10n_us_ia_hr_payroll/__manifest__.py
@@ -4,13 +4,13 @@
'license': 'AGPL-3',
'category': 'Localization',
'depends': ['l10n_us_hr_payroll'],
- 'version': '11.0.2019.0.0',
+ 'version': '12.0.2019.0.0',
'description': """
-USA::Iowa Payroll Rules.
+USA - Iowa Payroll Rules.
==================================
-* Contribution register and partner for Additional WithholdingTaxaction - Income Tax Withholding
-* Contribution register and partner for Iowa Workforce Development- Unemployment
+* Contribution register and partner for Iowa Workforce Development (IWD) - Unemployment
+* Contribution register and partner for Iowa Department of Revenue (IDOR) - Income Tax Withholding
* Contract level Iowa Exemptions
* Company level Iowa Unemployment Rate
* Salary Structure for Iowa
diff --git a/l10n_us_ia_hr_payroll/data/rules.xml b/l10n_us_ia_hr_payroll/data/rules.xml
index 2281d900..62ec8a59 100755
--- a/l10n_us_ia_hr_payroll/data/rules.xml
+++ b/l10n_us_ia_hr_payroll/data/rules.xml
@@ -12,7 +12,7 @@
code
rate = payslip.dict.get_rate('US_IA_UNEMP')
-year = int(payslip.dict.date_to[:4])
+year = payslip.dict.date_to.year
ytd = payslip.sum('WAGE_US_IA_UNEMP', str(year) + '-01-01', str(year+1) + '-01-01')
ytd += contract.external_wages
remaining = rate.wage_limit_year - ytd
diff --git a/l10n_us_ia_hr_payroll/models/hr_payroll.py b/l10n_us_ia_hr_payroll/models/hr_payroll.py
index c171154a..7eac3055 100755
--- a/l10n_us_ia_hr_payroll/models/hr_payroll.py
+++ b/l10n_us_ia_hr_payroll/models/hr_payroll.py
@@ -4,6 +4,6 @@ from odoo import models, fields, api
class USIAHrContract(models.Model):
_inherit = 'hr.contract'
- ia_w4_allowances = fields.Integer(string='Iowa W-4 allowances', default=0)
- ia_w4_additional_wh = fields.Float(string="Iowa Additional Withholding", default=0.0)
- ia_w4_tax_exempt = fields.Boolean(string="Tax Exempt")
+ ia_w4_allowances = fields.Integer(string='Iowa W-4 allowances')
+ ia_w4_additional_wh = fields.Float(string="Iowa W-4 Additional Withholding")
+ ia_w4_tax_exempt = fields.Boolean(string="Iowa W-4 Tax Exempt")
diff --git a/l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py b/l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py
index 53d1fd44..bde22b29 100755
--- a/l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py
+++ b/l10n_us_ia_hr_payroll/tests/test_us_ia_payslip.py
@@ -1,5 +1,4 @@
from odoo.addons.l10n_us_hr_payroll.tests.test_us_payslip import TestUsPayslip, process_payslip
-from odoo.addons.l10n_us_hr_payroll.models.l10n_us_hr_payroll import USHrContract
class TestUsIAPayslip(TestUsPayslip):
@@ -11,13 +10,11 @@ class TestUsIAPayslip(TestUsPayslip):
wages = 30000.00
schedule_pay = 'weekly'
allowances = 1
- additional_wh = 0.00
employee = self._createEmployee()
contract = self._createContract(employee, wages,
struct_id=self.ref('l10n_us_ia_hr_payroll.hr_payroll_salary_structure_us_ia_employee'),
schedule_pay=schedule_pay)
contract.ia_w4_allowances = allowances
- contract.ia_w4_additional_wh = additional_wh
self.assertEqual(contract.schedule_pay, 'weekly')
@@ -30,30 +27,24 @@ class TestUsIAPayslip(TestUsPayslip):
# withholding amount because it is calculated in the base US payroll module as a negative
# t1 = 30000 - (10399.66) = 19600.34
t1_to_test = wages + cats['EE_US_FED_INC_WITHHOLD']
- self.assertPayrollEqual(t1_to_test, 19600.34)
-
# T2 is T1 minus our standard deduction which is a table of flat rates dependent on the number of allowances.
# In our case, we have a weekly period which on the table has a std deduct. of $32.50 for 0 or 1 allowances,
# and 80.00 of 2 or more allowances.
standard_deduction = 32.50 # The allowance tells us what standard_deduction amount to use.
# t2 = 19600.34 - 32.50 = 19567.84
t2_to_test = t1_to_test - standard_deduction
- self.assertPayrollEqual(t2_to_test, 19567.84)
# T3 is T2 multiplied by the income rates in the large table plus a flat fee for that bracket.
# 1153.38 is the bracket floor. 8.53 is the rate, and 67.63 is the flat fee.
# t3 = 1638.38
t3_to_test = ((t2_to_test - 1153.38) * (8.53 / 100)) + 67.63
- self.assertPayrollEqual(t3_to_test, 1638.38)
# T4 is T3 minus a flat amount determined by pay period * the number of deductions. For 2019, our weekly
# deduction amount per allowance is 0.77
# t4 = 1638.38 - 0.77 = 155.03
t4_to_test = t3_to_test - (0.77 * allowances)
- self.assertPayrollEqual(t4_to_test, 1637.61)
# t5 is our T4 plus the additional withholding per period
# t5 = 1637.61 + 0.0
# Convert to negative as well.
- t5_to_test = -t4_to_test - additional_wh
- self.assertPayrollEqual(t5_to_test, -1637.61)
+ t5_to_test = -t4_to_test
self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], wages)
self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], cats['WAGE_US_IA_UNEMP'] * self.IA_UNEMP)
@@ -70,7 +61,7 @@ class TestUsIAPayslip(TestUsPayslip):
# Make a new payslip, this one will have maximums
- remaining_IA_UNEMP_wages = self.IA_UNEMP_MAX_WAGE - wages if (self.IA_UNEMP_MAX_WAGE - 2*wages < wages) \
+ remaining_ia_unemp_wages = self.IA_UNEMP_MAX_WAGE - wages if (self.IA_UNEMP_MAX_WAGE - 2*wages < wages) \
else wages
self._log('2019 Iowa tax second payslip weekly:')
@@ -78,21 +69,19 @@ class TestUsIAPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], remaining_IA_UNEMP_wages)
- self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], remaining_IA_UNEMP_wages * self.IA_UNEMP)
+ self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], remaining_ia_unemp_wages)
+ self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], remaining_ia_unemp_wages * self.IA_UNEMP)
def test_taxes_biweekly(self):
wages = 3000.00
schedule_pay = 'bi-weekly'
allowances = 1
- additional_wh = 0.00
employee = self._createEmployee()
contract = self._createContract(employee, wages,
struct_id=self.ref(
'l10n_us_ia_hr_payroll.hr_payroll_salary_structure_us_ia_employee'),
schedule_pay=schedule_pay)
contract.ia_w4_allowances = allowances
- contract.ia_w4_additional_wh = additional_wh
self.assertEqual(contract.schedule_pay, 'bi-weekly')
@@ -115,11 +104,11 @@ class TestUsIAPayslip(TestUsPayslip):
# deduction amount per allowance is 0.77
t4_to_test = t3_to_test - (1.54 * allowances)
# t5 is our T4 plus the additional withholding per period
- t5_to_test = -t4_to_test - additional_wh
+ t5_to_test = -t4_to_test
self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], wages)
self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], cats['WAGE_US_IA_UNEMP'] * self.IA_UNEMP)
- self.assertPayrollEqual(cats['EE_US_IA_INC_WITHHOLD'], t5_to_test - additional_wh)
+ self.assertPayrollEqual(cats['EE_US_IA_INC_WITHHOLD'], t5_to_test)
process_payslip(payslip)
@@ -175,7 +164,6 @@ class TestUsIAPayslip(TestUsPayslip):
salary,
external_wages=external_wages,
struct_id=self.ref('l10n_us_ia_hr_payroll.hr_payroll_salary_structure_us_ia_employee'),
- futa_type=USHrContract.FUTA_TYPE_BASIC,
schedule_pay=schedule_pay)
contract.ia_w4_tax_exempt = True
@@ -184,8 +172,10 @@ class TestUsIAPayslip(TestUsPayslip):
payslip.compute_sheet()
cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('WAGE_US_IA_UNEMP', 0.0), 0.0)
- self.assertPayrollEqual(cats.get('ER_US_IA_UNEMP', 0.0), cats.get('WAGE_US_IA_UNEMP', 0.0) * self.IA_UNEMP)
+ self.assertPayrollEqual(cats['WAGE_US_IA_UNEMP'], salary)
+ self.assertPayrollEqual(cats['ER_US_IA_UNEMP'], cats['WAGE_US_IA_UNEMP'] * self.IA_UNEMP)
+ self.assertPayrollEqual(cats.get('EE_US_IA_INC_WITHHOLD', 0.00), 0.00)
+
From 1fb03d8d7372e2d09ff90ca74bf783c2300457c8 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 5 Jun 2018 11:30:29 -0700
Subject: [PATCH 18/43] Initial commit of `product_catch_weight` for 11.0
---
product_catch_weight/__init__.py | 1 +
product_catch_weight/__manifest__.py | 19 +++
product_catch_weight/models/__init__.py | 3 +
.../models/account_invoice.py | 42 +++++
product_catch_weight/models/stock.py | 20 +++
product_catch_weight/models/stock_patch.py | 114 +++++++++++++
product_catch_weight/tests/__init__.py | 1 +
.../tests/test_catch_weight.py | 152 ++++++++++++++++++
product_catch_weight/views/stock_views.xml | 35 ++++
9 files changed, 387 insertions(+)
create mode 100644 product_catch_weight/__init__.py
create mode 100644 product_catch_weight/__manifest__.py
create mode 100644 product_catch_weight/models/__init__.py
create mode 100644 product_catch_weight/models/account_invoice.py
create mode 100644 product_catch_weight/models/stock.py
create mode 100644 product_catch_weight/models/stock_patch.py
create mode 100644 product_catch_weight/tests/__init__.py
create mode 100644 product_catch_weight/tests/test_catch_weight.py
create mode 100644 product_catch_weight/views/stock_views.xml
diff --git a/product_catch_weight/__init__.py b/product_catch_weight/__init__.py
new file mode 100644
index 00000000..0650744f
--- /dev/null
+++ b/product_catch_weight/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/product_catch_weight/__manifest__.py b/product_catch_weight/__manifest__.py
new file mode 100644
index 00000000..7c2ef68c
--- /dev/null
+++ b/product_catch_weight/__manifest__.py
@@ -0,0 +1,19 @@
+{
+ 'name': 'Product Catch Weight',
+ 'version': '11.0.1.0.0',
+ 'category': 'Warehouse',
+ 'depends': [
+ 'sale_stock',
+ 'purchase',
+ ],
+ 'description': """
+ """,
+ 'author': 'Hibou Corp.',
+ 'license': 'AGPL-3',
+ 'website': 'https://hibou.io/',
+ 'data': [
+ 'views/stock_views.xml',
+ ],
+ 'installable': True,
+ 'application': False,
+}
diff --git a/product_catch_weight/models/__init__.py b/product_catch_weight/models/__init__.py
new file mode 100644
index 00000000..57a87093
--- /dev/null
+++ b/product_catch_weight/models/__init__.py
@@ -0,0 +1,3 @@
+from . import account_invoice
+from . import stock_patch
+from . import stock
diff --git a/product_catch_weight/models/account_invoice.py b/product_catch_weight/models/account_invoice.py
new file mode 100644
index 00000000..ab72943c
--- /dev/null
+++ b/product_catch_weight/models/account_invoice.py
@@ -0,0 +1,42 @@
+from odoo import api, fields, models
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class AccountInvoiceLine(models.Model):
+ _inherit = 'account.invoice.line'
+
+ @api.one
+ @api.depends('price_unit', 'discount', 'invoice_line_tax_ids', 'quantity',
+ 'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id',
+ 'invoice_id.date_invoice', 'invoice_id.date')
+ def _compute_price(self):
+ currency = self.invoice_id and self.invoice_id.currency_id or None
+ price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
+
+ ratio = 1.0
+ qty_done_total = 0.0
+ if self.invoice_id.type in ('out_invoice', 'out_refund'):
+ move_lines = self.sale_line_ids.mapped('move_ids.move_line_ids')
+ else:
+ move_lines = self.purchase_line_id.mapped('move_ids.move_line_ids')
+ for move_line in move_lines:
+ qty_done = move_line.qty_done
+ r = move_line.lot_id.catch_weight_ratio
+ ratio = ((ratio * qty_done_total) + (qty_done * r)) / (qty_done + qty_done_total)
+ qty_done_total += qty_done
+ price = price * ratio
+
+ taxes = False
+ if self.invoice_line_tax_ids:
+ taxes = self.invoice_line_tax_ids.compute_all(price, currency, self.quantity, product=self.product_id,
+ partner=self.invoice_id.partner_id)
+ self.price_subtotal = price_subtotal_signed = taxes['total_excluded'] if taxes else self.quantity * price
+ self.price_total = taxes['total_included'] if taxes else self.price_subtotal
+ if self.invoice_id.currency_id and self.invoice_id.currency_id != self.invoice_id.company_id.currency_id:
+ price_subtotal_signed = self.invoice_id.currency_id.with_context(
+ date=self.invoice_id._get_currency_rate_date()).compute(price_subtotal_signed,
+ self.invoice_id.company_id.currency_id)
+ sign = self.invoice_id.type in ['in_refund', 'out_refund'] and -1 or 1
+ self.price_subtotal_signed = price_subtotal_signed * sign
\ No newline at end of file
diff --git a/product_catch_weight/models/stock.py b/product_catch_weight/models/stock.py
new file mode 100644
index 00000000..6acec3a4
--- /dev/null
+++ b/product_catch_weight/models/stock.py
@@ -0,0 +1,20 @@
+from odoo import api, fields, models
+
+
+class StockProductionLot(models.Model):
+ _inherit = 'stock.production.lot'
+
+ catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), default=1.0)
+
+
+class StockMoveLine(models.Model):
+ _inherit = 'stock.move.line'
+
+ lot_catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), default=1.0)
+ lot_catch_weight_ratio_related = fields.Float(related='lot_id.catch_weight_ratio')
+ #lot_catch_weight_ratio = fields.Float(related='lot_id.catch_weight_ratio')
+
+ # def _action_done(self):
+ # super(StockMoveLine, self)._action_done()
+ # for ml in self.filtered(lambda l: l.product_id.tracking == 'serial' and l.lot_id):
+ # ml.lot_id.catch_weight_ratio = ml.lot_catch_weight_ratio
diff --git a/product_catch_weight/models/stock_patch.py b/product_catch_weight/models/stock_patch.py
new file mode 100644
index 00000000..8787f4f6
--- /dev/null
+++ b/product_catch_weight/models/stock_patch.py
@@ -0,0 +1,114 @@
+from odoo import fields
+from odoo.exceptions import UserError
+from odoo.tools.float_utils import float_round, float_compare, float_is_zero
+from odoo.addons.stock.models.stock_move_line import StockMoveLine
+
+
+def _action_done(self):
+ """ This method is called during a move's `action_done`. It'll actually move a quant from
+ the source location to the destination location, and unreserve if needed in the source
+ location.
+
+ This method is intended to be called on all the move lines of a move. This method is not
+ intended to be called when editing a `done` move (that's what the override of `write` here
+ is done.
+ """
+
+ # First, we loop over all the move lines to do a preliminary check: `qty_done` should not
+ # be negative and, according to the presence of a picking type or a linked inventory
+ # adjustment, enforce some rules on the `lot_id` field. If `qty_done` is null, we unlink
+ # the line. It is mandatory in order to free the reservation and correctly apply
+ # `action_done` on the next move lines.
+ ml_to_delete = self.env['stock.move.line']
+ for ml in self:
+ # Check here if `ml.qty_done` respects the rounding of `ml.product_uom_id`.
+ uom_qty = float_round(ml.qty_done, precision_rounding=ml.product_uom_id.rounding, rounding_method='HALF-UP')
+ precision_digits = self.env['decimal.precision'].precision_get('Product Unit of Measure')
+ qty_done = float_round(ml.qty_done, precision_digits=precision_digits, rounding_method='HALF-UP')
+ if float_compare(uom_qty, qty_done, precision_digits=precision_digits) != 0:
+ raise UserError(_('The quantity done for the product "%s" doesn\'t respect the rounding precision \
+ defined on the unit of measure "%s". Please change the quantity done or the \
+ rounding precision of your unit of measure.') % (
+ ml.product_id.display_name, ml.product_uom_id.name))
+
+ qty_done_float_compared = float_compare(ml.qty_done, 0, precision_rounding=ml.product_uom_id.rounding)
+ if qty_done_float_compared > 0:
+ if ml.product_id.tracking != 'none':
+ picking_type_id = ml.move_id.picking_type_id
+ if picking_type_id:
+ if picking_type_id.use_create_lots:
+ # If a picking type is linked, we may have to create a production lot on
+ # the fly before assigning it to the move line if the user checked both
+ # `use_create_lots` and `use_existing_lots`.
+ if ml.lot_name and not ml.lot_id:
+ lot = self.env['stock.production.lot'].create(
+ {'name': ml.lot_name, 'product_id': ml.product_id.id, 'catch_weight_ratio': ml.lot_catch_weight_ratio}
+ )
+ ml.write({'lot_id': lot.id})
+ elif not picking_type_id.use_create_lots and not picking_type_id.use_existing_lots:
+ # If the user disabled both `use_create_lots` and `use_existing_lots`
+ # checkboxes on the picking type, he's allowed to enter tracked
+ # products without a `lot_id`.
+ continue
+ elif ml.move_id.inventory_id:
+ # If an inventory adjustment is linked, the user is allowed to enter
+ # tracked products without a `lot_id`.
+ continue
+
+ if not ml.lot_id:
+ raise UserError(_('You need to supply a lot/serial number for %s.') % ml.product_id.name)
+ elif qty_done_float_compared < 0:
+ raise UserError(_('No negative quantities allowed'))
+ else:
+ ml_to_delete |= ml
+ ml_to_delete.unlink()
+
+ # Now, we can actually move the quant.
+ done_ml = self.env['stock.move.line']
+ for ml in self - ml_to_delete:
+ if ml.product_id.type == 'product':
+ Quant = self.env['stock.quant']
+ rounding = ml.product_uom_id.rounding
+
+ # if this move line is force assigned, unreserve elsewhere if needed
+ if not ml.location_id.should_bypass_reservation() and float_compare(ml.qty_done, ml.product_qty,
+ precision_rounding=rounding) > 0:
+ extra_qty = ml.qty_done - ml.product_qty
+ ml._free_reservation(ml.product_id, ml.location_id, extra_qty, lot_id=ml.lot_id,
+ package_id=ml.package_id, owner_id=ml.owner_id, ml_to_ignore=done_ml)
+ # unreserve what's been reserved
+ if not ml.location_id.should_bypass_reservation() and ml.product_id.type == 'product' and ml.product_qty:
+ try:
+ Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id,
+ package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
+ except UserError:
+ Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=False,
+ package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
+
+ # move what's been actually done
+ quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id,
+ rounding_method='HALF-UP')
+ available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity,
+ lot_id=ml.lot_id, package_id=ml.package_id,
+ owner_id=ml.owner_id)
+ if available_qty < 0 and ml.lot_id:
+ # see if we can compensate the negative quants with some untracked quants
+ untracked_qty = Quant._get_available_quantity(ml.product_id, ml.location_id, lot_id=False,
+ package_id=ml.package_id, owner_id=ml.owner_id,
+ strict=True)
+ if untracked_qty:
+ taken_from_untracked_qty = min(untracked_qty, abs(quantity))
+ Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty,
+ lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id)
+ Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty,
+ lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
+ Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id,
+ package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date)
+ done_ml |= ml
+ # Reset the reserved quantity as we just moved it to the destination location.
+ (self - ml_to_delete).with_context(bypass_reservation_update=True).write({
+ 'product_uom_qty': 0.00,
+ 'date': fields.Datetime.now(),
+ })
+
+StockMoveLine._action_done = _action_done
diff --git a/product_catch_weight/tests/__init__.py b/product_catch_weight/tests/__init__.py
new file mode 100644
index 00000000..0edad729
--- /dev/null
+++ b/product_catch_weight/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_catch_weight
diff --git a/product_catch_weight/tests/test_catch_weight.py b/product_catch_weight/tests/test_catch_weight.py
new file mode 100644
index 00000000..a167e530
--- /dev/null
+++ b/product_catch_weight/tests/test_catch_weight.py
@@ -0,0 +1,152 @@
+import logging
+# from odoo.addons.stock.tests.test_move2 import TestPickShip
+from odoo import fields
+from odoo.tests.common import TransactionCase
+
+_logger = logging.getLogger(__name__)
+
+
+class TestPicking(TransactionCase):
+ def setUp(self):
+ super(TestPicking, self).setUp()
+ self.partner1 = self.env.ref('base.res_partner_2')
+ self.product1 = self.env['product.product'].create({
+ 'name': 'Product 1',
+ 'type': 'product',
+ 'tracking': 'serial',
+ 'list_price': 100.0,
+ 'standard_price': 50.0,
+ 'taxes_id': [(5, 0, 0)],
+ })
+ #self.product1 = self.env.ref('product.product_order_01')
+ self.product1.write({
+ 'type': 'product',
+ 'tracking': 'serial',
+ })
+ self.stock_location = self.env.ref('stock.stock_location_stock')
+
+
+ # def test_creation(self):
+ # self.productA.tracking = 'serial'
+ # lot = self.env['stock.production.lot'].create({
+ # 'product_id': self.productA.id,
+ # 'name': '123456789',
+ # })
+ #
+ # lot.catch_weight_ratio = 0.8
+ # _logger.warn(lot.xxxcatch_weight_ratio)
+
+
+
+ # def test_delivery(self):
+ # self.productA.tracking = 'serial'
+ # picking_pick, picking_pack, picking_ship = self.create_pick_pack_ship()
+ # stock_location = self.env['stock.location'].browse(self.stock_location)
+ # lot = self.env['stock.production.lot'].create({
+ # 'product_id': self.productA.id,
+ # 'name': '123456789',
+ # 'catch_weight_ratio': 0.8,
+ # })
+ # self.env['stock.quant']._update_available_quantity(self.productA, stock_location, 1.0, lot_id=lot)
+
+ def test_so_invoice(self):
+ ratio = 0.8
+ lot = self.env['stock.production.lot'].create({
+ 'product_id': self.product1.id,
+ 'name': '123456789',
+ 'catch_weight_ratio': ratio,
+ })
+ self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0, lot_id=lot)
+ so = self.env['sale.order'].create({
+ 'partner_id': self.partner1.id,
+ 'partner_invoice_id': self.partner1.id,
+ 'partner_shipping_id': self.partner1.id,
+ 'order_line': [(0, 0, {'product_id': self.product1.id})],
+ })
+ so.action_confirm()
+ self.assertTrue(so.state in ('sale', 'done'))
+ self.assertEqual(len(so.picking_ids), 1)
+ picking = so.picking_ids
+ self.assertEqual(picking.state, 'assigned')
+ self.assertEqual(picking.move_lines.move_line_ids.lot_id, lot)
+ picking.move_lines.move_line_ids.qty_done = 1.0
+ picking.button_validate()
+ self.assertEqual(picking.state, 'done')
+
+ inv_id = so.action_invoice_create()
+ inv = self.env['account.invoice'].browse(inv_id)
+ self.assertEqual(inv.amount_total, ratio * self.product1.list_price)
+
+ def test_so_invoice2(self):
+ ratio1 = 0.8
+ ratio2 = 1.1
+ lot1 = self.env['stock.production.lot'].create({
+ 'product_id': self.product1.id,
+ 'name': '1-low',
+ 'catch_weight_ratio': ratio1,
+ })
+ lot2 = self.env['stock.production.lot'].create({
+ 'product_id': self.product1.id,
+ 'name': '1-high',
+ 'catch_weight_ratio': ratio2,
+ })
+ self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0, lot_id=lot1)
+ self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0, lot_id=lot2)
+ so = self.env['sale.order'].create({
+ 'partner_id': self.partner1.id,
+ 'partner_invoice_id': self.partner1.id,
+ 'partner_shipping_id': self.partner1.id,
+ 'order_line': [(0, 0, {'product_id': self.product1.id, 'product_uom_qty': 2.0})],
+ })
+ so.action_confirm()
+ self.assertTrue(so.state in ('sale', 'done'))
+ self.assertEqual(len(so.picking_ids), 1)
+ picking = so.picking_ids
+ self.assertEqual(picking.state, 'assigned')
+ self.assertEqual(picking.move_lines.move_line_ids.mapped('lot_id'), lot1 + lot2)
+ for line in picking.move_lines.move_line_ids:
+ line.qty_done = 1.0
+ picking.button_validate()
+ self.assertEqual(picking.state, 'done')
+
+ inv_id = so.action_invoice_create()
+ inv = self.env['account.invoice'].browse(inv_id)
+ self.assertEqual(inv.amount_total, (ratio1 * self.product1.list_price) + (ratio2 * self.product1.list_price))
+
+ def test_po_invoice(self):
+ ratio1 = 0.8
+ ratio2 = 1.1
+ ratios = (ratio1, ratio2)
+ price = self.product1.standard_price
+ po = self.env['purchase.order'].create({
+ 'partner_id': self.partner1.id,
+ 'order_line': [(0, 0, {
+ 'product_id': self.product1.id,
+ 'product_qty': 2.0,
+ 'name': 'Test',
+ 'date_planned': fields.Datetime.now(),
+ 'product_uom': self.product1.uom_po_id.id,
+ 'price_unit': price,
+ })]
+ })
+ po.button_confirm()
+ self.assertEqual(po.state, 'purchase')
+ self.assertEqual(len(po.picking_ids), 1)
+
+ picking = po.picking_ids
+ for i, line in enumerate(picking.move_lines.move_line_ids):
+ line.write({'lot_name': str(i), 'qty_done': 1.0, 'lot_catch_weight_ratio': ratios[i]})
+ picking.button_validate()
+ self.assertEqual(picking.state, 'done')
+
+ inv = self.env['account.invoice'].create({
+ 'type': 'in_invoice',
+ 'partner_id': self.partner1.id,
+ 'purchase_id': po.id,
+ })
+ inv.purchase_order_change()
+ self.assertEqual(len(inv.invoice_line_ids), 1)
+ self.assertEqual(inv.invoice_line_ids.quantity, 2.0)
+ self.assertEqual(inv.amount_total, (ratio1 * price) + (ratio2 * price))
+
+
diff --git a/product_catch_weight/views/stock_views.xml b/product_catch_weight/views/stock_views.xml
new file mode 100644
index 00000000..cee50753
--- /dev/null
+++ b/product_catch_weight/views/stock_views.xml
@@ -0,0 +1,35 @@
+
+
+
+ stock.production.lot.form.inherit
+ stock.production.lot
+
+
+
+
+
+
+
+
+
+ stock.move.line.form.inherit
+ stock.move.line
+
+
+
+
+
+
+
+
+ stock.move.line.operations.tree.inherit
+ stock.move.line
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From b1c4aba75e082e486563b74ff334502907654833 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Sun, 10 Jun 2018 11:02:27 -0700
Subject: [PATCH 19/43] Improve UI, allowing user to measure the catch weight
in a specific unit of measure.
This UOM should be convertable (in the same category) as the normal stock UOM for the product.
Example: If you want to sell 'units' of 50lbs then you should make a "50lbs" UOM in the Weight category and use that as the sale and purchase UOM, then your "Catch Weight UOM" can be the stock "lb(s)" UOM.
---
product_catch_weight/__manifest__.py | 1 +
product_catch_weight/models/__init__.py | 1 +
.../models/account_invoice.py | 6 ++
product_catch_weight/models/product.py | 7 ++
product_catch_weight/models/stock.py | 44 +++++++++---
product_catch_weight/models/stock_patch.py | 3 +-
.../tests/test_catch_weight.py | 46 +++++++------
.../views/account_invoice_views.xml | 69 +++++++++++++++++++
product_catch_weight/views/stock_views.xml | 43 +++++++++---
9 files changed, 182 insertions(+), 38 deletions(-)
create mode 100644 product_catch_weight/models/product.py
create mode 100644 product_catch_weight/views/account_invoice_views.xml
diff --git a/product_catch_weight/__manifest__.py b/product_catch_weight/__manifest__.py
index 7c2ef68c..76d3cea2 100644
--- a/product_catch_weight/__manifest__.py
+++ b/product_catch_weight/__manifest__.py
@@ -12,6 +12,7 @@
'license': 'AGPL-3',
'website': 'https://hibou.io/',
'data': [
+ 'views/account_invoice_views.xml',
'views/stock_views.xml',
],
'installable': True,
diff --git a/product_catch_weight/models/__init__.py b/product_catch_weight/models/__init__.py
index 57a87093..5e099bc5 100644
--- a/product_catch_weight/models/__init__.py
+++ b/product_catch_weight/models/__init__.py
@@ -1,3 +1,4 @@
from . import account_invoice
+from . import product
from . import stock_patch
from . import stock
diff --git a/product_catch_weight/models/account_invoice.py b/product_catch_weight/models/account_invoice.py
index ab72943c..ee8c9f92 100644
--- a/product_catch_weight/models/account_invoice.py
+++ b/product_catch_weight/models/account_invoice.py
@@ -7,6 +7,9 @@ _logger = logging.getLogger(__name__)
class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
+ catch_weight = fields.Float(string='Catch Weight', digits=(10, 4), compute='_compute_price', store=True)
+ catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
+
@api.one
@api.depends('price_unit', 'discount', 'invoice_line_tax_ids', 'quantity',
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id',
@@ -17,6 +20,7 @@ class AccountInvoiceLine(models.Model):
ratio = 1.0
qty_done_total = 0.0
+ catch_weight = 0.0
if self.invoice_id.type in ('out_invoice', 'out_refund'):
move_lines = self.sale_line_ids.mapped('move_ids.move_line_ids')
else:
@@ -26,7 +30,9 @@ class AccountInvoiceLine(models.Model):
r = move_line.lot_id.catch_weight_ratio
ratio = ((ratio * qty_done_total) + (qty_done * r)) / (qty_done + qty_done_total)
qty_done_total += qty_done
+ catch_weight += move_line.lot_id.catch_weight
price = price * ratio
+ self.catch_weight = catch_weight
taxes = False
if self.invoice_line_tax_ids:
diff --git a/product_catch_weight/models/product.py b/product_catch_weight/models/product.py
new file mode 100644
index 00000000..16cb4b91
--- /dev/null
+++ b/product_catch_weight/models/product.py
@@ -0,0 +1,7 @@
+from odoo import api, fields, models
+
+
+class ProductProduct(models.Model):
+ _inherit = 'product.template'
+
+ catch_weight_uom_id = fields.Many2one('product.uom', string='Catch Weight UOM')
diff --git a/product_catch_weight/models/stock.py b/product_catch_weight/models/stock.py
index 6acec3a4..1ad5d3b6 100644
--- a/product_catch_weight/models/stock.py
+++ b/product_catch_weight/models/stock.py
@@ -4,17 +4,43 @@ from odoo import api, fields, models
class StockProductionLot(models.Model):
_inherit = 'stock.production.lot'
- catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), default=1.0)
+ catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), compute='_compute_catch_weight_ratio')
+ catch_weight = fields.Float(string='Catch Weight', digits=(10, 4))
+ catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
+
+
+ @api.depends('catch_weight')
+ def _compute_catch_weight_ratio(self):
+ for lot in self:
+ if not lot.catch_weight_uom_id:
+ lot.catch_weight_ratio = 1.0
+ else:
+ lot.catch_weight_ratio = lot.catch_weight_uom_id._compute_quantity(lot.catch_weight,
+ lot.product_id.uom_id,
+ rounding_method='DOWN')
+
+
+class StockMove(models.Model):
+ _inherit = 'stock.move'
+
+ product_catch_weight_uom_id = fields.Many2one('product.uom', related="product_id.catch_weight_uom_id")
+
+ def _prepare_move_line_vals(self, quantity=None, reserved_quant=None):
+ vals = super(StockMove, self)._prepare_move_line_vals(quantity=quantity, reserved_quant=reserved_quant)
+ vals['catch_weight_uom_id'] = self.product_catch_weight_uom_id.id if self.product_catch_weight_uom_id else False
+ return vals
+
+ def action_show_details(self):
+ action = super(StockMove, self).action_show_details()
+ action['context']['show_catch_weight'] = bool(self.product_id.catch_weight_uom_id)
+ return action
class StockMoveLine(models.Model):
_inherit = 'stock.move.line'
- lot_catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), default=1.0)
- lot_catch_weight_ratio_related = fields.Float(related='lot_id.catch_weight_ratio')
- #lot_catch_weight_ratio = fields.Float(related='lot_id.catch_weight_ratio')
-
- # def _action_done(self):
- # super(StockMoveLine, self)._action_done()
- # for ml in self.filtered(lambda l: l.product_id.tracking == 'serial' and l.lot_id):
- # ml.lot_id.catch_weight_ratio = ml.lot_catch_weight_ratio
+ catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), default=1.0)
+ catch_weight = fields.Float(string='Catch Weight', digits=(10,4))
+ catch_weight_uom_id = fields.Many2one('product.uom', string='Catch Weight UOM')
+ lot_catch_weight = fields.Float(related='lot_id.catch_weight')
+ lot_catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
diff --git a/product_catch_weight/models/stock_patch.py b/product_catch_weight/models/stock_patch.py
index 8787f4f6..fca04e4f 100644
--- a/product_catch_weight/models/stock_patch.py
+++ b/product_catch_weight/models/stock_patch.py
@@ -41,8 +41,9 @@ def _action_done(self):
# the fly before assigning it to the move line if the user checked both
# `use_create_lots` and `use_existing_lots`.
if ml.lot_name and not ml.lot_id:
+ lot_catch_weight = ml.catch_weight_uom_id._compute_quantity(ml.catch_weight, ml.product_id.catch_weight_uom_id, rounding_method='DOWN')
lot = self.env['stock.production.lot'].create(
- {'name': ml.lot_name, 'product_id': ml.product_id.id, 'catch_weight_ratio': ml.lot_catch_weight_ratio}
+ {'name': ml.lot_name, 'product_id': ml.product_id.id, 'catch_weight': lot_catch_weight}
)
ml.write({'lot_id': lot.id})
elif not picking_type_id.use_create_lots and not picking_type_id.use_existing_lots:
diff --git a/product_catch_weight/tests/test_catch_weight.py b/product_catch_weight/tests/test_catch_weight.py
index a167e530..1b13cb55 100644
--- a/product_catch_weight/tests/test_catch_weight.py
+++ b/product_catch_weight/tests/test_catch_weight.py
@@ -9,7 +9,16 @@ _logger = logging.getLogger(__name__)
class TestPicking(TransactionCase):
def setUp(self):
super(TestPicking, self).setUp()
+ self.nominal_weight = 50.0
self.partner1 = self.env.ref('base.res_partner_2')
+ self.stock_location = self.env.ref('stock.stock_location_stock')
+ self.ref_uom_id = self.env.ref('product.product_uom_kgm')
+ self.product_uom_id = self.env['product.uom'].create({
+ 'name': '50 ref',
+ 'category_id': self.ref_uom_id.category_id.id,
+ 'uom_type': 'bigger',
+ 'factor_inv': self.nominal_weight,
+ })
self.product1 = self.env['product.product'].create({
'name': 'Product 1',
'type': 'product',
@@ -17,13 +26,10 @@ class TestPicking(TransactionCase):
'list_price': 100.0,
'standard_price': 50.0,
'taxes_id': [(5, 0, 0)],
+ 'uom_id': self.product_uom_id.id,
+ 'uom_po_id': self.product_uom_id.id,
+ 'catch_weight_uom_id': self.ref_uom_id.id,
})
- #self.product1 = self.env.ref('product.product_order_01')
- self.product1.write({
- 'type': 'product',
- 'tracking': 'serial',
- })
- self.stock_location = self.env.ref('stock.stock_location_stock')
# def test_creation(self):
@@ -50,12 +56,13 @@ class TestPicking(TransactionCase):
# self.env['stock.quant']._update_available_quantity(self.productA, stock_location, 1.0, lot_id=lot)
def test_so_invoice(self):
- ratio = 0.8
+ ref_weight = 45.0
lot = self.env['stock.production.lot'].create({
'product_id': self.product1.id,
'name': '123456789',
- 'catch_weight_ratio': ratio,
+ 'catch_weight': ref_weight,
})
+ self.assertAlmostEqual(lot.catch_weight_ratio, ref_weight / self.nominal_weight)
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0, lot_id=lot)
so = self.env['sale.order'].create({
'partner_id': self.partner1.id,
@@ -75,20 +82,20 @@ class TestPicking(TransactionCase):
inv_id = so.action_invoice_create()
inv = self.env['account.invoice'].browse(inv_id)
- self.assertEqual(inv.amount_total, ratio * self.product1.list_price)
+ self.assertAlmostEqual(inv.amount_total, lot.catch_weight_ratio * self.product1.list_price)
def test_so_invoice2(self):
- ratio1 = 0.8
- ratio2 = 1.1
+ ref_weight1 = 45.0
+ ref_weight2 = 51.0
lot1 = self.env['stock.production.lot'].create({
'product_id': self.product1.id,
'name': '1-low',
- 'catch_weight_ratio': ratio1,
+ 'catch_weight': ref_weight1,
})
lot2 = self.env['stock.production.lot'].create({
'product_id': self.product1.id,
'name': '1-high',
- 'catch_weight_ratio': ratio2,
+ 'catch_weight': ref_weight2,
})
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0, lot_id=lot1)
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0, lot_id=lot2)
@@ -111,12 +118,12 @@ class TestPicking(TransactionCase):
inv_id = so.action_invoice_create()
inv = self.env['account.invoice'].browse(inv_id)
- self.assertEqual(inv.amount_total, (ratio1 * self.product1.list_price) + (ratio2 * self.product1.list_price))
+ self.assertAlmostEqual(inv.amount_total, self.product1.list_price * (lot1.catch_weight_ratio + lot2.catch_weight_ratio))
def test_po_invoice(self):
- ratio1 = 0.8
- ratio2 = 1.1
- ratios = (ratio1, ratio2)
+ ref_weight1 = 45.0
+ ref_weight2 = 51.0
+ weights = (ref_weight1, ref_weight2)
price = self.product1.standard_price
po = self.env['purchase.order'].create({
'partner_id': self.partner1.id,
@@ -135,7 +142,7 @@ class TestPicking(TransactionCase):
picking = po.picking_ids
for i, line in enumerate(picking.move_lines.move_line_ids):
- line.write({'lot_name': str(i), 'qty_done': 1.0, 'lot_catch_weight_ratio': ratios[i]})
+ line.write({'lot_name': str(i), 'qty_done': 1.0, 'catch_weight': weights[i]})
picking.button_validate()
self.assertEqual(picking.state, 'done')
@@ -147,6 +154,5 @@ class TestPicking(TransactionCase):
inv.purchase_order_change()
self.assertEqual(len(inv.invoice_line_ids), 1)
self.assertEqual(inv.invoice_line_ids.quantity, 2.0)
- self.assertEqual(inv.amount_total, (ratio1 * price) + (ratio2 * price))
-
+ self.assertAlmostEqual(inv.amount_total, price * sum(w / self.nominal_weight for w in weights))
diff --git a/product_catch_weight/views/account_invoice_views.xml b/product_catch_weight/views/account_invoice_views.xml
new file mode 100644
index 00000000..8eb57740
--- /dev/null
+++ b/product_catch_weight/views/account_invoice_views.xml
@@ -0,0 +1,69 @@
+
+
+
+ account.invoice.form.inherit
+ account.invoice
+
+
+
+
+
+
+
+
+
+ account.invoice.supplier.form.inherit
+ account.invoice
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Catch Weight |
+ CW Unit Price |
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ /
+
+ |
+
+
+ |
+ |
+
+
+
+
+
+
+ |
+ |
+
+
+
+
\ No newline at end of file
diff --git a/product_catch_weight/views/stock_views.xml b/product_catch_weight/views/stock_views.xml
index cee50753..40e2d9dd 100644
--- a/product_catch_weight/views/stock_views.xml
+++ b/product_catch_weight/views/stock_views.xml
@@ -7,17 +7,32 @@
+
+
-
- stock.move.line.form.inherit
- stock.move.line
-
+
+
+
+
+
+
+
+
+
+
+
+ stock.move.operations.form.inherit
+ stock.move
+
-
-
+
+
+
+
+ {'tree_view_ref': 'stock.view_stock_move_line_operation_tree', 'default_product_uom_id': product_uom, 'default_picking_id': picking_id, 'default_move_id': id, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_catch_weight_uom_id': product_catch_weight_uom_id}
@@ -27,8 +42,20 @@
-
-
+
+
+
+
+
+
+
+
+ product.template.common.form.inherit
+ product.template
+
+
+
+
From 8c059303bdf3cbd76251343e44c332be9f5c3d96 Mon Sep 17 00:00:00 2001
From: Kristen Marie Kulha
Date: Fri, 14 Sep 2018 11:05:35 -0700
Subject: [PATCH 20/43] Added computed field `has_catch_weight` to
`stock.picking` model in `product_catch_weight` module.
Additionally, added filter and group by for `has_catch_weight` to stock.picking search view.
---
.../models/account_invoice.py | 6 ++++-
product_catch_weight/models/stock.py | 12 ++++++++-
product_catch_weight/views/stock_views.xml | 26 ++++++++++++-------
3 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/product_catch_weight/models/account_invoice.py b/product_catch_weight/models/account_invoice.py
index ee8c9f92..c6b3db66 100644
--- a/product_catch_weight/models/account_invoice.py
+++ b/product_catch_weight/models/account_invoice.py
@@ -27,8 +27,12 @@ class AccountInvoiceLine(models.Model):
move_lines = self.purchase_line_id.mapped('move_ids.move_line_ids')
for move_line in move_lines:
qty_done = move_line.qty_done
+ current_qty_done = qty_done + qty_done_total
r = move_line.lot_id.catch_weight_ratio
- ratio = ((ratio * qty_done_total) + (qty_done * r)) / (qty_done + qty_done_total)
+ if current_qty_done == 0:
+ ratio = 0
+ else:
+ ratio = ((ratio * qty_done_total) + (qty_done * r)) / current_qty_done
qty_done_total += qty_done
catch_weight += move_line.lot_id.catch_weight
price = price * ratio
diff --git a/product_catch_weight/models/stock.py b/product_catch_weight/models/stock.py
index 1ad5d3b6..c5cc9363 100644
--- a/product_catch_weight/models/stock.py
+++ b/product_catch_weight/models/stock.py
@@ -8,7 +8,6 @@ class StockProductionLot(models.Model):
catch_weight = fields.Float(string='Catch Weight', digits=(10, 4))
catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
-
@api.depends('catch_weight')
def _compute_catch_weight_ratio(self):
for lot in self:
@@ -44,3 +43,14 @@ class StockMoveLine(models.Model):
catch_weight_uom_id = fields.Many2one('product.uom', string='Catch Weight UOM')
lot_catch_weight = fields.Float(related='lot_id.catch_weight')
lot_catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
+
+
+class StockPicking(models.Model):
+ _inherit = 'stock.picking'
+
+ has_catch_weight = fields.Boolean(string="Has Catch Weight", compute='_compute_has_catch_weight', store=True)
+
+ @api.depends('move_lines.product_catch_weight_uom_id')
+ def _compute_has_catch_weight(self):
+ for picking in self:
+ picking.has_catch_weight = any(picking.mapped('move_lines.product_catch_weight_uom_id'))
diff --git a/product_catch_weight/views/stock_views.xml b/product_catch_weight/views/stock_views.xml
index 40e2d9dd..3ce9015d 100644
--- a/product_catch_weight/views/stock_views.xml
+++ b/product_catch_weight/views/stock_views.xml
@@ -13,16 +13,6 @@
-
-
-
-
-
-
-
-
-
-
stock.move.operations.form.inherit
stock.move
@@ -36,6 +26,7 @@
+
stock.move.line.operations.tree.inherit
stock.move.line
@@ -49,6 +40,7 @@
+
product.template.common.form.inherit
product.template
@@ -59,4 +51,18 @@
+
+
+ stock.view.picking.internal.search.inherit
+ stock.picking
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 90add5bfdc70159822ed8f15303b5574aeda013a Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 16 Oct 2018 11:40:31 -0700
Subject: [PATCH 21/43] FIX `product_catch_weight` if line moved doesn't have a
lot, then the ratio is implicitly 0 despite having a default ratio.
---
product_catch_weight/models/account_invoice.py | 2 +-
product_catch_weight/tests/test_catch_weight.py | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/product_catch_weight/models/account_invoice.py b/product_catch_weight/models/account_invoice.py
index c6b3db66..11879f5b 100644
--- a/product_catch_weight/models/account_invoice.py
+++ b/product_catch_weight/models/account_invoice.py
@@ -25,7 +25,7 @@ class AccountInvoiceLine(models.Model):
move_lines = self.sale_line_ids.mapped('move_ids.move_line_ids')
else:
move_lines = self.purchase_line_id.mapped('move_ids.move_line_ids')
- for move_line in move_lines:
+ for move_line in move_lines.filtered(lambda l: l.lot_id):
qty_done = move_line.qty_done
current_qty_done = qty_done + qty_done_total
r = move_line.lot_id.catch_weight_ratio
diff --git a/product_catch_weight/tests/test_catch_weight.py b/product_catch_weight/tests/test_catch_weight.py
index 1b13cb55..48821d7a 100644
--- a/product_catch_weight/tests/test_catch_weight.py
+++ b/product_catch_weight/tests/test_catch_weight.py
@@ -30,6 +30,7 @@ class TestPicking(TransactionCase):
'uom_po_id': self.product_uom_id.id,
'catch_weight_uom_id': self.ref_uom_id.id,
})
+ self.pricelist = self.env.ref('product.list0')
# def test_creation(self):
@@ -69,6 +70,7 @@ class TestPicking(TransactionCase):
'partner_invoice_id': self.partner1.id,
'partner_shipping_id': self.partner1.id,
'order_line': [(0, 0, {'product_id': self.product1.id})],
+ 'pricelist_id': self.pricelist.id,
})
so.action_confirm()
self.assertTrue(so.state in ('sale', 'done'))
@@ -104,6 +106,7 @@ class TestPicking(TransactionCase):
'partner_invoice_id': self.partner1.id,
'partner_shipping_id': self.partner1.id,
'order_line': [(0, 0, {'product_id': self.product1.id, 'product_uom_qty': 2.0})],
+ 'pricelist_id': self.pricelist.id,
})
so.action_confirm()
self.assertTrue(so.state in ('sale', 'done'))
From 0fa7905590acfa229fc301a747b6aec0e969ff8b Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Fri, 23 Nov 2018 09:31:47 -0800
Subject: [PATCH 22/43] IMP `product_catch_weight` Add related fields to
`stock.quant` for tree view and POS.
---
product_catch_weight/models/stock.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/product_catch_weight/models/stock.py b/product_catch_weight/models/stock.py
index c5cc9363..0e553569 100644
--- a/product_catch_weight/models/stock.py
+++ b/product_catch_weight/models/stock.py
@@ -54,3 +54,11 @@ class StockPicking(models.Model):
def _compute_has_catch_weight(self):
for picking in self:
picking.has_catch_weight = any(picking.mapped('move_lines.product_catch_weight_uom_id'))
+
+
+class StockQuant(models.Model):
+ _inherit = 'stock.quant'
+
+ lot_catch_weight_ratio = fields.Float(related='lot_id.catch_weight_ratio')
+ lot_catch_weight = fields.Float(related='lot_id.catch_weight')
+ lot_catch_weight_uom_id = fields.Many2one('product.uom', related='lot_id.catch_weight_uom_id')
From c4c309246ad9fc4d486d5171e649f3b458b3a9e5 Mon Sep 17 00:00:00 2001
From: Bhoomi
Date: Fri, 20 Sep 2019 16:36:54 -0400
Subject: [PATCH 23/43] MIG `product_catch_weight` For Odoo 12.0
---
product_catch_weight/__manifest__.py | 2 +-
.../models/account_invoice.py | 2 +-
product_catch_weight/models/product.py | 2 +-
product_catch_weight/models/stock.py | 10 ++++----
.../tests/test_catch_weight.py | 4 +--
.../views/account_invoice_views.xml | 25 +++++++++----------
product_catch_weight/views/stock_views.xml | 7 +++---
7 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/product_catch_weight/__manifest__.py b/product_catch_weight/__manifest__.py
index 76d3cea2..535582f7 100644
--- a/product_catch_weight/__manifest__.py
+++ b/product_catch_weight/__manifest__.py
@@ -1,6 +1,6 @@
{
'name': 'Product Catch Weight',
- 'version': '11.0.1.0.0',
+ 'version': '12.0.1.0.0',
'category': 'Warehouse',
'depends': [
'sale_stock',
diff --git a/product_catch_weight/models/account_invoice.py b/product_catch_weight/models/account_invoice.py
index 11879f5b..c6db156c 100644
--- a/product_catch_weight/models/account_invoice.py
+++ b/product_catch_weight/models/account_invoice.py
@@ -8,7 +8,7 @@ class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
catch_weight = fields.Float(string='Catch Weight', digits=(10, 4), compute='_compute_price', store=True)
- catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
+ catch_weight_uom_id = fields.Many2one('uom.uom', related='product_id.catch_weight_uom_id')
@api.one
@api.depends('price_unit', 'discount', 'invoice_line_tax_ids', 'quantity',
diff --git a/product_catch_weight/models/product.py b/product_catch_weight/models/product.py
index 16cb4b91..0232c1c8 100644
--- a/product_catch_weight/models/product.py
+++ b/product_catch_weight/models/product.py
@@ -4,4 +4,4 @@ from odoo import api, fields, models
class ProductProduct(models.Model):
_inherit = 'product.template'
- catch_weight_uom_id = fields.Many2one('product.uom', string='Catch Weight UOM')
+ catch_weight_uom_id = fields.Many2one('uom.uom', string='Catch Weight UOM')
diff --git a/product_catch_weight/models/stock.py b/product_catch_weight/models/stock.py
index 0e553569..b96fa5bc 100644
--- a/product_catch_weight/models/stock.py
+++ b/product_catch_weight/models/stock.py
@@ -6,7 +6,7 @@ class StockProductionLot(models.Model):
catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), compute='_compute_catch_weight_ratio')
catch_weight = fields.Float(string='Catch Weight', digits=(10, 4))
- catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
+ catch_weight_uom_id = fields.Many2one('uom.uom', related='product_id.catch_weight_uom_id')
@api.depends('catch_weight')
def _compute_catch_weight_ratio(self):
@@ -22,7 +22,7 @@ class StockProductionLot(models.Model):
class StockMove(models.Model):
_inherit = 'stock.move'
- product_catch_weight_uom_id = fields.Many2one('product.uom', related="product_id.catch_weight_uom_id")
+ product_catch_weight_uom_id = fields.Many2one('uom.uom', related="product_id.catch_weight_uom_id")
def _prepare_move_line_vals(self, quantity=None, reserved_quant=None):
vals = super(StockMove, self)._prepare_move_line_vals(quantity=quantity, reserved_quant=reserved_quant)
@@ -40,9 +40,9 @@ class StockMoveLine(models.Model):
catch_weight_ratio = fields.Float(string='Catch Weight Ratio', digits=(10, 6), default=1.0)
catch_weight = fields.Float(string='Catch Weight', digits=(10,4))
- catch_weight_uom_id = fields.Many2one('product.uom', string='Catch Weight UOM')
+ catch_weight_uom_id = fields.Many2one('uom.uom', string='Catch Weight UOM')
lot_catch_weight = fields.Float(related='lot_id.catch_weight')
- lot_catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id')
+ lot_catch_weight_uom_id = fields.Many2one('uom.uom', related='product_id.catch_weight_uom_id')
class StockPicking(models.Model):
@@ -61,4 +61,4 @@ class StockQuant(models.Model):
lot_catch_weight_ratio = fields.Float(related='lot_id.catch_weight_ratio')
lot_catch_weight = fields.Float(related='lot_id.catch_weight')
- lot_catch_weight_uom_id = fields.Many2one('product.uom', related='lot_id.catch_weight_uom_id')
+ lot_catch_weight_uom_id = fields.Many2one('uom.uom', related='lot_id.catch_weight_uom_id')
diff --git a/product_catch_weight/tests/test_catch_weight.py b/product_catch_weight/tests/test_catch_weight.py
index 48821d7a..9a383ea3 100644
--- a/product_catch_weight/tests/test_catch_weight.py
+++ b/product_catch_weight/tests/test_catch_weight.py
@@ -12,8 +12,8 @@ class TestPicking(TransactionCase):
self.nominal_weight = 50.0
self.partner1 = self.env.ref('base.res_partner_2')
self.stock_location = self.env.ref('stock.stock_location_stock')
- self.ref_uom_id = self.env.ref('product.product_uom_kgm')
- self.product_uom_id = self.env['product.uom'].create({
+ self.ref_uom_id = self.env.ref('uom.product_uom_kgm')
+ self.product_uom_id = self.env['uom.uom'].create({
'name': '50 ref',
'category_id': self.ref_uom_id.category_id.id,
'uom_type': 'bigger',
diff --git a/product_catch_weight/views/account_invoice_views.xml b/product_catch_weight/views/account_invoice_views.xml
index 8eb57740..695a08e2 100644
--- a/product_catch_weight/views/account_invoice_views.xml
+++ b/product_catch_weight/views/account_invoice_views.xml
@@ -25,21 +25,21 @@
-
+
Catch Weight |
CW Unit Price |
-
-
-
+
+
+
-
-
+
+
-
-
+
+
|
-
+
/
-
+
|
@@ -57,10 +57,9 @@
|
-
-
-
+
+
|
|
diff --git a/product_catch_weight/views/stock_views.xml b/product_catch_weight/views/stock_views.xml
index 3ce9015d..1a580e0b 100644
--- a/product_catch_weight/views/stock_views.xml
+++ b/product_catch_weight/views/stock_views.xml
@@ -52,8 +52,8 @@
-
- stock.view.picking.internal.search.inherit
+
+ stock.picking.internal.search.inherit
stock.picking
@@ -61,8 +61,9 @@
-
+
+
\ No newline at end of file
From cbacd46a381c7fe75758e5b5bf3f77aaf68c5173 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Wed, 4 Dec 2019 10:26:24 -0800
Subject: [PATCH 24/43] IMP `connector_opencart` Add shipping price, and enable
scheduled action and exceptions.
Additionally, attempt to "unescape" the product name values.
---
connector_opencart/__manifest__.py | 1 +
.../data/connector_opencart_data.xml | 27 +++--------------
.../models/sale_order/importer.py | 30 +++++++++++--------
3 files changed, 23 insertions(+), 35 deletions(-)
diff --git a/connector_opencart/__manifest__.py b/connector_opencart/__manifest__.py
index ce265249..750f30c6 100644
--- a/connector_opencart/__manifest__.py
+++ b/connector_opencart/__manifest__.py
@@ -17,6 +17,7 @@
'license': 'AGPL-3',
'website': 'https://hibou.io',
'data': [
+ 'data/connector_opencart_data.xml',
'security/ir.model.access.csv',
'views/delivery_views.xml',
'views/opencart_backend_views.xml',
diff --git a/connector_opencart/data/connector_opencart_data.xml b/connector_opencart/data/connector_opencart_data.xml
index fef62b5b..b575ec5d 100644
--- a/connector_opencart/data/connector_opencart_data.xml
+++ b/connector_opencart/data/connector_opencart_data.xml
@@ -4,13 +4,13 @@
Opencart - Import Sales Orders
-
+
code
1
- days
+ hours
-1
-
+
model._scheduler_import_sale_orders()
@@ -27,26 +27,7 @@ Check your taxes and fiscal positions configuration and correct them if necessar
30
sale.order
sale
- if sale.opencart_bind_ids and abs(sale.amount_total - sale.opencart_bind_ids[0].total_amount) >= 0.01:
- failed = True
-
-
-
-
- Total Tax Amount differs from Opencart
- The tax amount computed in Odoo doesn't match with the tax amount in Opencart.
-
-Cause:
-The taxes are probably different between Odoo and Opencart. A fiscal position could have changed the final price.
-
-Resolution:
-Check your taxes and fiscal positions configuration and correct them if necessary.
- 30
- sale.order
- sale
- # By default, a cent of difference for the tax amount is allowed, feel free to customise it in your own module
-if sale.opencart_bind_ids and abs(sale.amount_tax - sale.opencart_bind_ids[0].total_amount_tax) > 0.01:
- failed = True
+ failed = sale.opencart_bind_ids and abs(sale.amount_total - sale.opencart_bind_ids[0].total_amount) >= 0.01
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index 42813627..0ff32f0d 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -2,6 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from copy import deepcopy, copy
+from html import unescape
from odoo import fields, _
from odoo.addons.component.core import Component
@@ -65,14 +66,14 @@ class SaleOrderImportMapper(Component):
record = map_record.source
line_builder = self.component(usage='order.line.builder.shipping')
- line_builder.price_unit = 0.0
+ line_builder.price_unit = record.get('shipping_exclude_tax', 0.0)
if values.get('carrier_id'):
carrier = self.env['delivery.carrier'].browse(values['carrier_id'])
line_builder.product = carrier.product_id
+ line = (0, 0, line_builder.get_line())
+ values['order_line'].append(line)
- line = (0, 0, line_builder.get_line())
- values['order_line'].append(line)
return values
def finalize(self, map_record, values):
@@ -120,9 +121,8 @@ class SaleOrderImportMapper(Component):
[('name', '=', record_method)],
limit=1,
)
- assert method, ("method %s should exist because the import fails "
- "in SaleOrderImporter._before_import when it is "
- " missing" % record_method)
+ if not method:
+ raise ValueError('Payment Mode named "%s", cannot be found.' % (record_method, ))
return {'payment_mode_id': method.id}
@mapping
@@ -138,8 +138,11 @@ class SaleOrderImportMapper(Component):
return {'warehouse_id': warehouse.id}
@mapping
- def shipping_method(self, record):
- method = record['shipping_method'] or ''
+ def shipping_code(self, record):
+ method = record.get('shipping_code') or record.get('shipping_method')
+ if not method:
+ return {'carrier_id': False}
+
carrier_domain = [('opencart_code', '=', method.strip())]
company = self.options.store.company_id or self.backend_record.company_id
if company:
@@ -148,8 +151,8 @@ class SaleOrderImportMapper(Component):
]
carrier = self.env['delivery.carrier'].search(carrier_domain, limit=1)
if not carrier:
- raise ValueError('Delivery Carrier for methodCode "%s", cannot be found.' % (method, ))
- return {'carrier_id': carrier.id, 'shipping_method_code': method}
+ raise ValueError('Delivery Carrier for method Code "%s", cannot be found.' % (method, ))
+ return {'carrier_id': carrier.id}
@mapping
def company_id(self, record):
@@ -328,7 +331,6 @@ class SaleOrderLineImportMapper(Component):
direct = [('quantity', 'product_uom_qty'),
('price', 'price_unit'),
- ('name', 'name'),
('order_product_id', 'external_id'),
]
@@ -340,13 +342,17 @@ class SaleOrderLineImportMapper(Component):
reference = record['model']
values = {
'default_code': reference,
- 'name': record.get('name', reference),
+ 'name': unescape(record.get('name', reference)), # unknown if other fields, but have observed " in product names
'type': 'product',
'list_price': record.get('price', 0.0),
'categ_id': self.backend_record.product_categ_id.id,
}
return self._finalize_product_values(record, values)
+ @mapping
+ def name(self, record):
+ return unescape(record['name'])
+
@mapping
def product_id(self, record):
reference = record['model']
From 9eb593b6ae319de6b542436a5fd863fdf1c37c5c Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 21 Jan 2020 12:37:36 -0800
Subject: [PATCH 25/43] FIX `connector_opencart` name mapping on order line
---
connector_opencart/models/sale_order/importer.py | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index 0ff32f0d..123ba001 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -53,15 +53,11 @@ class SaleOrderImportMapper(Component):
direct = [('order_id', 'external_id'),
('store_id', 'store_id'),
- # ('customerOrderId', 'customer_order_id'),
]
children = [('products', 'opencart_order_line_ids', 'opencart.sale.order.line'),
]
- # def _map_child(self, map_record, from_attr, to_attr, model_name):
- # return super(SaleOrderImportMapper, self)._map_child(map_record, from_attr, to_attr, model_name)
-
def _add_shipping_line(self, map_record, values):
record = map_record.source
@@ -351,7 +347,7 @@ class SaleOrderLineImportMapper(Component):
@mapping
def name(self, record):
- return unescape(record['name'])
+ return {'name': unescape(record['name'])}
@mapping
def product_id(self, record):
From ce8bb41c0353d3b3774625a48c8a47bf2d5731e8 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 20 Feb 2020 15:03:10 -0800
Subject: [PATCH 26/43] IMP `connector_opencart` Add bindings between Opencart
Product Option Values and Odoo's Product Template Attribute Values
---
connector_opencart/__manifest__.py | 1 +
connector_opencart/components/api/opencart.py | 15 ++++
connector_opencart/components/binder.py | 2 +
connector_opencart/models/__init__.py | 1 +
connector_opencart/models/opencart/backend.py | 9 ++-
connector_opencart/models/product/__init__.py | 2 +
connector_opencart/models/product/common.py | 77 ++++++++++++++++++
connector_opencart/models/product/importer.py | 78 +++++++++++++++++++
.../models/sale_order/importer.py | 35 +++------
.../security/ir.model.access.csv | 2 +
.../views/opencart_product_views.xml | 28 +++++++
11 files changed, 226 insertions(+), 24 deletions(-)
create mode 100644 connector_opencart/models/product/__init__.py
create mode 100644 connector_opencart/models/product/common.py
create mode 100644 connector_opencart/models/product/importer.py
create mode 100644 connector_opencart/views/opencart_product_views.xml
diff --git a/connector_opencart/__manifest__.py b/connector_opencart/__manifest__.py
index 750f30c6..c6ea2bb9 100644
--- a/connector_opencart/__manifest__.py
+++ b/connector_opencart/__manifest__.py
@@ -21,6 +21,7 @@
'security/ir.model.access.csv',
'views/delivery_views.xml',
'views/opencart_backend_views.xml',
+ 'views/opencart_product_views.xml',
],
'installable': True,
'application': False,
diff --git a/connector_opencart/components/api/opencart.py b/connector_opencart/components/api/opencart.py
index cd52828f..d1914f74 100644
--- a/connector_opencart/components/api/opencart.py
+++ b/connector_opencart/components/api/opencart.py
@@ -22,6 +22,10 @@ class Opencart:
def stores(self):
return Stores(connection=self)
+ @property
+ def products(self):
+ return Products(connection=self)
+
def get_headers(self, url, method):
headers = {}
if method in ('POST', 'PUT', ):
@@ -138,3 +142,14 @@ class Stores(Resource):
def get(self, id):
url = self.url + ('/%s' % id)
return self.connection.send_request(method='GET', url=url)
+
+
+class Products(Resource):
+ """
+ Retrieves Product details
+ """
+ path = 'products'
+
+ def get(self, id):
+ url = self.url + ('/%s' % id)
+ return self.connection.send_request(method='GET', url=url)
diff --git a/connector_opencart/components/binder.py b/connector_opencart/components/binder.py
index d8bbaebd..b2525fea 100644
--- a/connector_opencart/components/binder.py
+++ b/connector_opencart/components/binder.py
@@ -20,4 +20,6 @@ class OpencartModelBinder(Component):
'opencart.sale.order',
'opencart.sale.order.line',
'opencart.stock.picking',
+ 'opencart.product.template',
+ 'opencart.product.template.attribute.value',
]
diff --git a/connector_opencart/models/__init__.py b/connector_opencart/models/__init__.py
index 4ed0f156..a7f55577 100644
--- a/connector_opencart/models/__init__.py
+++ b/connector_opencart/models/__init__.py
@@ -1,4 +1,5 @@
from . import delivery
from . import opencart
+from . import product
from . import sale_order
from . import stock_picking
diff --git a/connector_opencart/models/opencart/backend.py b/connector_opencart/models/opencart/backend.py
index f3b344ce..cc362ac0 100644
--- a/connector_opencart/models/opencart/backend.py
+++ b/connector_opencart/models/opencart/backend.py
@@ -2,12 +2,12 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-from datetime import datetime, timedelta
from logging import getLogger
from contextlib import contextmanager
from odoo import api, fields, models, _
from odoo.exceptions import UserError
+from odoo.addons.connector.models.checkpoint import add_checkpoint
from ...components.api.opencart import Opencart
_logger = getLogger(__name__)
@@ -80,6 +80,13 @@ class OpencartBackend(models.Model):
with _super.work_on(model_name, opencart_api=opencart_api, **kwargs) as work:
yield work
+ @api.multi
+ def add_checkpoint(self, record):
+ self.ensure_one()
+ record.ensure_one()
+ return add_checkpoint(self.env, record._name, record.id,
+ self._name, self.id)
+
@api.multi
def synchronize_metadata(self):
try:
diff --git a/connector_opencart/models/product/__init__.py b/connector_opencart/models/product/__init__.py
new file mode 100644
index 00000000..79ab5dc6
--- /dev/null
+++ b/connector_opencart/models/product/__init__.py
@@ -0,0 +1,2 @@
+from . import common
+from . import importer
diff --git a/connector_opencart/models/product/common.py b/connector_opencart/models/product/common.py
new file mode 100644
index 00000000..c4afcaa7
--- /dev/null
+++ b/connector_opencart/models/product/common.py
@@ -0,0 +1,77 @@
+from odoo import api, fields, models
+from odoo.addons.queue_job.exception import NothingToDoJob, RetryableJobError
+from odoo.addons.component.core import Component
+
+
+class OpencartProductTemplate(models.Model):
+ _name = 'opencart.product.template'
+ _inherit = 'opencart.binding'
+ _inherits = {'product.template': 'odoo_id'}
+ _description = 'Opencart Product'
+
+ odoo_id = fields.Many2one('product.template',
+ string='Product',
+ required=True,
+ ondelete='restrict')
+ opencart_attribute_value_ids = fields.One2many('opencart.product.template.attribute.value',
+ 'opencart_product_tmpl_id',
+ string='Opencart Product Attribute Values')
+
+ def opencart_sale_get_combination(self, options, reentry=False):
+ selected_attribute_values = self.env['product.template.attribute.value']
+ for option in options:
+ product_option_value_id = str(option['product_option_value_id'])
+ opencart_attribute_value = self.opencart_attribute_value_ids.filtered(lambda v: v.external_id == product_option_value_id)
+ if not opencart_attribute_value:
+ if reentry:
+ # we have already triggered an import.
+ raise Exception('Order Product has option (%s) "%s" that does not exist on the product.' % (product_option_value_id, option.get('name', '')))
+ # need to re-import product.
+ try:
+ self.import_record(self.backend_id, self.external_id, force=True)
+ return self.opencart_sale_get_combination(options, reentry=True)
+ except NothingToDoJob:
+ if reentry:
+ raise RetryableJobError('Product imported, but selected option is not available.')
+ if not opencart_attribute_value.odoo_id:
+ raise RetryableJobError('Order Product has option (%s) "%s" that is not mapped to an Odoo Attribute Value.' % (opencart_attribute_value.external_id, opencart_attribute_value.opencart_name))
+ selected_attribute_values += opencart_attribute_value.odoo_id
+ # Now that we know what options are selected, we can load a variant with those options
+ return self.odoo_id._create_product_variant(selected_attribute_values)
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ opencart_bind_ids = fields.One2many('opencart.product.template', 'odoo_id', string='Opencart Bindings')
+
+
+class OpencartProductTemplateAdapter(Component):
+ _name = 'opencart.product.template.adapter'
+ _inherit = 'opencart.adapter'
+ _apply_on = 'opencart.product.template'
+
+ def read(self, id):
+ api_instance = self.api_instance
+ record = api_instance.products.get(id)
+ if 'data' in record and record['data']:
+ return record['data']
+ raise RetryableJobError('Product "' + str(id) + '" did not return an product response. ' + str(record))
+
+
+# Product Attribute Value, cannot "inherits" the odoo_id as then it cannot be empty
+class OpencartProductTemplateAttributeValue(models.Model):
+ _name = 'opencart.product.template.attribute.value'
+ _inherit = 'opencart.binding'
+ _description = 'Opencart Product Attribute Value'
+
+ odoo_id = fields.Many2one('product.template.attribute.value',
+ string='Product Attribute Value',
+ required=False,
+ ondelete='cascade')
+ opencart_name = fields.Char(string='Opencart Name', help='For matching purposes.')
+ opencart_product_tmpl_id = fields.Many2one('opencart.product.template',
+ string='Opencart Product',
+ required=True,
+ ondelete='cascade')
+ product_tmpl_id = fields.Many2one(related='opencart_product_tmpl_id.odoo_id')
diff --git a/connector_opencart/models/product/importer.py b/connector_opencart/models/product/importer.py
new file mode 100644
index 00000000..b45a6ce0
--- /dev/null
+++ b/connector_opencart/models/product/importer.py
@@ -0,0 +1,78 @@
+from html import unescape
+from odoo.addons.component.core import Component
+from odoo.addons.connector.components.mapper import mapping, only_create
+
+
+class ProductImportMapper(Component):
+ _name = 'opencart.product.template.import.mapper'
+ _inherit = 'opencart.import.mapper'
+ _apply_on = ['opencart.product.template']
+
+ @mapping
+ def backend_id(self, record):
+ return {'backend_id': self.backend_record.id}
+
+ @mapping
+ def name(self, record):
+ name = record.get('product_description', [{}])[0].get('name', record.get('id'))
+ return {'name': unescape(name)}
+
+ # TODO more fields like pricing....
+
+ @mapping
+ def product_type(self, record):
+ return {'type': 'product' if record.get('shipping') else 'service'}
+
+ @only_create
+ @mapping
+ def existing_product(self, record):
+ product_template = self.env['product.template']
+ template = product_template.browse()
+
+ if record.get('sku'):
+ template = product_template.search([('default_code', '=', record.get('sku'))], limit=1)
+ if not template and record.get('model'):
+ template = product_template.search([('default_code', '=', record.get('model'))], limit=1)
+ if not template and record.get('name'):
+ name = record.get('product_description', [{}])[0].get('name')
+ if name:
+ template = product_template.search([('name', '=', unescape(name))], limit=1)
+ return {'odoo_id': template.id}
+
+
+class ProductImporter(Component):
+ _name = 'opencart.product.template.importer'
+ _inherit = 'opencart.importer'
+ _apply_on = ['opencart.product.template']
+
+ def _create(self, data):
+ binding = super(ProductImporter, self)._create(data)
+ self.backend_record.add_checkpoint(binding)
+ return binding
+
+ def _after_import(self, binding):
+ self._sync_options(binding)
+
+ def _sync_options(self, binding):
+ existing_option_values = binding.opencart_attribute_value_ids
+ mapped_option_values = binding.opencart_attribute_value_ids.browse()
+ record = self.opencart_record
+ backend = self.backend_record
+ for option in record.get('options', []):
+ for record_option_value in option.get('option_value', []):
+ option_value = existing_option_values.filtered(lambda v: v.external_id == record_option_value['product_option_value_id'])
+ name = unescape(record_option_value.get('name', ''))
+ if not option_value:
+ option_value = existing_option_values.create({
+ 'backend_id': backend.id,
+ 'external_id': record_option_value['product_option_value_id'],
+ 'opencart_name': name,
+ 'opencart_product_tmpl_id': binding.id,
+ })
+ # Keep options consistent with Opencart by renaming them
+ if option_value.opencart_name != name:
+ option_value.opencart_name = name
+ mapped_option_values += option_value
+
+ to_unlink = existing_option_values - mapped_option_values
+ to_unlink.unlink()
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index 123ba001..eb47874d 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -316,7 +316,11 @@ class SaleOrderImporter(Component):
return binding
def _import_dependencies(self):
+ record = self.opencart_record
self._import_addresses()
+ for product in record.get('products', []):
+ if 'product_id' in product and product['product_id']:
+ self._import_dependency(product['product_id'], 'opencart.product.template')
class SaleOrderLineImportMapper(Component):
@@ -330,33 +334,18 @@ class SaleOrderLineImportMapper(Component):
('order_product_id', 'external_id'),
]
- def _finalize_product_values(self, record, values):
- # This would be a good place to create a vendor or add a route...
- return values
-
- def _product_values(self, record):
- reference = record['model']
- values = {
- 'default_code': reference,
- 'name': unescape(record.get('name', reference)), # unknown if other fields, but have observed " in product names
- 'type': 'product',
- 'list_price': record.get('price', 0.0),
- 'categ_id': self.backend_record.product_categ_id.id,
- }
- return self._finalize_product_values(record, values)
-
@mapping
def name(self, record):
return {'name': unescape(record['name'])}
@mapping
def product_id(self, record):
- reference = record['model']
- product = self.env['product.product'].search([
- ('default_code', '=', reference)
- ], limit=1)
-
- if not product:
- # we could use a record like (0, 0, values)
- product = self.env['product.product'].create(self._product_values(record))
+ product_id = record['product_id']
+ binder = self.binder_for('opencart.product.template')
+ # do not unwrap, because it would be a product.template, but I need a specific variant
+ opencart_product_template = binder.to_internal(product_id, unwrap=False)
+ if record.get('option'):
+ product = opencart_product_template.opencart_sale_get_combination(record.get('option'))
+ else:
+ product = opencart_product_template.odoo_id.product_variant_id
return {'product_id': product.id, 'product_uom': product.uom_id.id}
diff --git a/connector_opencart/security/ir.model.access.csv b/connector_opencart/security/ir.model.access.csv
index 44dcff29..2a8d2dfd 100644
--- a/connector_opencart/security/ir.model.access.csv
+++ b/connector_opencart/security/ir.model.access.csv
@@ -4,6 +4,8 @@
"access_opencart_binding","opencart_binding connector manager","model_opencart_binding","connector.group_connector_manager",1,1,1,1
"access_opencart_sale_order","opencart_sale_order connector manager","model_opencart_sale_order","connector.group_connector_manager",1,1,1,1
"access_opencart_sale_order_line","opencart_sale_order_line connector manager","model_opencart_sale_order_line","connector.group_connector_manager",1,1,1,1
+"access_opencart_product_template","opencart_product_template connector manager","model_opencart_product_template","connector.group_connector_manager",1,1,1,1
+"access_opencart_product_template_attribute_value","opencart_product_template_attribute_value connector manager","model_opencart_product_template_attribute_value","connector.group_connector_manager",1,1,1,1
"access_opencart_stock_picking","opencart_stock_picking connector manager","model_opencart_stock_picking","connector.group_connector_manager",1,1,1,1
"access_opencart_sale_order_sale_salesman","opencart_sale_order","model_opencart_sale_order","sales_team.group_sale_salesman",1,0,0,0
"access_opencart_sale_order_sale_manager","opencart_sale_order","model_opencart_sale_order","sales_team.group_sale_manager",1,1,1,1
diff --git a/connector_opencart/views/opencart_product_views.xml b/connector_opencart/views/opencart_product_views.xml
new file mode 100644
index 00000000..db349957
--- /dev/null
+++ b/connector_opencart/views/opencart_product_views.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ opencart.product.template.form
+ opencart.product.template
+
+
+
+
+
+
\ No newline at end of file
From cfaa06d2caf7352371350fc7ef103f1e3d89f1c0 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Fri, 6 Mar 2020 11:20:27 -0800
Subject: [PATCH 27/43] IMP `connector_opencart` Add field `opencart_sku` on
`product.template` to let you pre-make products and link them up.
Improved partner matching and added some logging for the testing phase.
Added menu item Connectors->Opencart-Products to find and fix mapping easily.
Allow deleting `product.template` even with bindings so that you could re-make the binding.
---
connector_opencart/__manifest__.py | 1 +
connector_opencart/models/product/common.py | 5 +-
connector_opencart/models/product/importer.py | 11 +++-
.../models/sale_order/importer.py | 53 ++++++++++++++-----
.../views/opencart_product_views.xml | 28 ++++++++++
connector_opencart/views/product_views.xml | 15 ++++++
6 files changed, 98 insertions(+), 15 deletions(-)
create mode 100644 connector_opencart/views/product_views.xml
diff --git a/connector_opencart/__manifest__.py b/connector_opencart/__manifest__.py
index c6ea2bb9..630a5319 100644
--- a/connector_opencart/__manifest__.py
+++ b/connector_opencart/__manifest__.py
@@ -22,6 +22,7 @@
'views/delivery_views.xml',
'views/opencart_backend_views.xml',
'views/opencart_product_views.xml',
+ 'views/product_views.xml',
],
'installable': True,
'application': False,
diff --git a/connector_opencart/models/product/common.py b/connector_opencart/models/product/common.py
index c4afcaa7..8e0a9c0c 100644
--- a/connector_opencart/models/product/common.py
+++ b/connector_opencart/models/product/common.py
@@ -12,7 +12,7 @@ class OpencartProductTemplate(models.Model):
odoo_id = fields.Many2one('product.template',
string='Product',
required=True,
- ondelete='restrict')
+ ondelete='cascade') # cascade so that you can delete an Odoo product that was created by connector
opencart_attribute_value_ids = fields.One2many('opencart.product.template.attribute.value',
'opencart_product_tmpl_id',
string='Opencart Product Attribute Values')
@@ -34,7 +34,7 @@ class OpencartProductTemplate(models.Model):
if reentry:
raise RetryableJobError('Product imported, but selected option is not available.')
if not opencart_attribute_value.odoo_id:
- raise RetryableJobError('Order Product has option (%s) "%s" that is not mapped to an Odoo Attribute Value.' % (opencart_attribute_value.external_id, opencart_attribute_value.opencart_name))
+ raise RetryableJobError('Order Product (%s) has option (%s) "%s" that is not mapped to an Odoo Attribute Value.' % (self, opencart_attribute_value.external_id, opencart_attribute_value.opencart_name))
selected_attribute_values += opencart_attribute_value.odoo_id
# Now that we know what options are selected, we can load a variant with those options
return self.odoo_id._create_product_variant(selected_attribute_values)
@@ -43,6 +43,7 @@ class OpencartProductTemplate(models.Model):
class ProductTemplate(models.Model):
_inherit = 'product.template'
+ opencart_sku = fields.Char('Opencart SKU')
opencart_bind_ids = fields.One2many('opencart.product.template', 'odoo_id', string='Opencart Bindings')
diff --git a/connector_opencart/models/product/importer.py b/connector_opencart/models/product/importer.py
index b45a6ce0..d0e68c45 100644
--- a/connector_opencart/models/product/importer.py
+++ b/connector_opencart/models/product/importer.py
@@ -23,6 +23,10 @@ class ProductImportMapper(Component):
def product_type(self, record):
return {'type': 'product' if record.get('shipping') else 'service'}
+ @mapping
+ def opencart_sku(self, record):
+ return {'opencart_sku': (record.get('sku') or '').strip()}
+
@only_create
@mapping
def existing_product(self, record):
@@ -30,7 +34,12 @@ class ProductImportMapper(Component):
template = product_template.browse()
if record.get('sku'):
- template = product_template.search([('default_code', '=', record.get('sku'))], limit=1)
+ sku = (record.get('sku') or '').strip()
+ # Try to match our own field
+ template = product_template.search([('opencart_sku', '=', sku)], limit=1)
+ if not template:
+ # Try to match the default_code
+ template = product_template.search([('default_code', '=', record.get('sku'))], limit=1)
if not template and record.get('model'):
template = product_template.search([('default_code', '=', record.get('model'))], limit=1)
if not template and record.get('name'):
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index eb47874d..f97691d9 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -9,6 +9,9 @@ from odoo.addons.component.core import Component
from odoo.addons.connector.components.mapper import mapping
from odoo.exceptions import ValidationError
+import logging
+_logger = logging.getLogger(__name__)
+
class SaleOrderBatchImporter(Component):
_name = 'opencart.sale.order.batch.importer'
@@ -184,14 +187,21 @@ class SaleOrderImporter(Component):
return self.env['res.partner'].create(values)
def _partner_matches(self, partner, values):
+ _logger.warn('_partner_matches partner: ' + str(partner) + ' values: ' + str(values))
for key, value in values.items():
+ if key in ('active', 'parent_id'):
+ continue
+
if key == 'state_id':
if value != partner.state_id.id:
+ _logger.warn(' return false for state_id value: ' + str(value) + ' partner.state_id.id: ' + str(partner.state_id.id))
return False
elif key == 'country_id':
if value != partner.country_id.id:
+ _logger.warn(' return false for country_id value: ' + str(value) + ' partner.country_id.id: ' + str(partner.country_id.id))
return False
elif bool(value) and value != getattr(partner, key):
+ _logger.warn(' return false for ' + str(key) + ' : ' + str(value) + ' partner value: ' + str(getattr(partner, key)))
return False
return True
@@ -232,13 +242,13 @@ class SaleOrderImporter(Component):
], limit=1)
return {
- 'email': email,
- 'name': name,
- 'phone': phone,
- 'street': street,
- 'street2': street2,
- 'zip': zip_,
- 'city': city,
+ 'email': email.strip(),
+ 'name': name.strip(),
+ 'phone': phone.strip(),
+ 'street': street.strip(),
+ 'street2': street2.strip(),
+ 'zip': zip_.strip(),
+ 'city': city.strip(),
'state_id': state.id,
'country_id': country.id,
}
@@ -247,9 +257,18 @@ class SaleOrderImporter(Component):
record = self.opencart_record
partner_values = self._get_partner_values()
- partner = self.env['res.partner'].search([
+ partners = self.env['res.partner'].search([
('email', '=', partner_values['email']),
- ], limit=1)
+ ])
+
+ partner = None
+ for possible in partners:
+ if self._partner_matches(possible, partner_values):
+ _logger.warn('matched partner: ' + str(possible))
+ partner = possible
+ break
+ if not partner and partners:
+ partner = partners[0]
if not partner:
# create partner.
@@ -260,18 +279,28 @@ class SaleOrderImporter(Component):
partner_values['active'] = False
shipping_partner = self._create_partner(copy(partner_values))
else:
+ _logger.warn('same shipping partner')
shipping_partner = partner
invoice_values = self._get_partner_values(info_string='payment_')
if (not self._partner_matches(partner, invoice_values)
and not self._partner_matches(shipping_partner, invoice_values)):
- partner_values['parent_id'] = partner.id
- partner_values['active'] = False
- invoice_partner = self._create_partner(copy(invoice_values))
+ # Try to find existing invoice address....
+ for possible in partners:
+ if self._partner_matches(possible, invoice_values):
+ _logger.warn('matched invoice partner: ' + str(possible))
+ invoice_partner = possible
+ break
+ else:
+ partner_values['parent_id'] = partner.id
+ partner_values['active'] = False
+ invoice_partner = self._create_partner(copy(invoice_values))
elif self._partner_matches(partner, invoice_values):
+ _logger.warn('same invoice partner')
invoice_partner = partner
elif self._partner_matches(shipping_partner, invoice_values):
+ _logger.warn('same invoice shipping partner')
invoice_partner = shipping_partner
self.partner = partner
diff --git a/connector_opencart/views/opencart_product_views.xml b/connector_opencart/views/opencart_product_views.xml
index db349957..fec97208 100644
--- a/connector_opencart/views/opencart_product_views.xml
+++ b/connector_opencart/views/opencart_product_views.xml
@@ -25,4 +25,32 @@
+
+ opencart.product.template.tree
+ opencart.product.template
+
+
+
+
+
+
+
+
+
+
+
+
+ Opencart Products
+ opencart.product.template
+ form
+ tree,form
+
+
+
+
+
\ No newline at end of file
diff --git a/connector_opencart/views/product_views.xml b/connector_opencart/views/product_views.xml
new file mode 100644
index 00000000..3ccc3955
--- /dev/null
+++ b/connector_opencart/views/product_views.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ product.template.product.form.inherit
+ product.template
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From b718c0f1601ed4a03249e59f546f6ef3acc13882 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Fri, 20 Mar 2020 12:44:25 -0700
Subject: [PATCH 28/43] IMP `connector_opencart` Support orders with 'coupons'
key to build up a discount product line.
---
connector_opencart/components/api/opencart.py | 17 +++++++++--
connector_opencart/models/opencart/backend.py | 2 ++
connector_opencart/models/opencart/store.py | 2 ++
.../models/sale_order/importer.py | 29 +++++++++++++++++++
.../views/opencart_backend_views.xml | 2 ++
5 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/connector_opencart/components/api/opencart.py b/connector_opencart/components/api/opencart.py
index d1914f74..f0dfe367 100644
--- a/connector_opencart/components/api/opencart.py
+++ b/connector_opencart/components/api/opencart.py
@@ -5,6 +5,9 @@ import requests
from urllib.parse import urlencode
from json import loads, dumps
+import logging
+_logger = logging.getLogger(__name__)
+
class Opencart:
@@ -37,11 +40,19 @@ class Opencart:
if params:
encoded_url += '?%s' % urlencode(params)
headers = self.get_headers(encoded_url, method)
-
+ _logger.debug('send_request method: %s url: %s headers: %s params: %s body: %s' % (
+ method,
+ url,
+ headers,
+ params,
+ body
+ ))
if method == 'GET':
- return loads(self.session.get(url, params=params, headers=headers).text)
+ result_text = self.session.get(url, params=params, headers=headers).text
elif method == 'PUT' or method == 'POST':
- return loads(self.session.put(url, data=body, headers=headers).text)
+ result_text = self.session.put(url, data=body, headers=headers).text
+ _logger.debug('raw_text: ' + str(result_text))
+ return loads(result_text)
class Resource:
diff --git a/connector_opencart/models/opencart/backend.py b/connector_opencart/models/opencart/backend.py
index cc362ac0..46620a87 100644
--- a/connector_opencart/models/opencart/backend.py
+++ b/connector_opencart/models/opencart/backend.py
@@ -62,6 +62,8 @@ class OpencartBackend(models.Model):
"in Odoo.",
)
# payment_mode_id = fields.Many2one(comodel_name='account.payment.mode', string="Payment Mode")
+ coupon_product_id = fields.Many2one(comodel_name='product.product', string='Coupon Product',
+ help='Product to represent coupon discounts.')
# New Product fields.
product_categ_id = fields.Many2one(comodel_name='product.category', string='Product Category',
diff --git a/connector_opencart/models/opencart/store.py b/connector_opencart/models/opencart/store.py
index 31e71e39..4444b16f 100644
--- a/connector_opencart/models/opencart/store.py
+++ b/connector_opencart/models/opencart/store.py
@@ -50,6 +50,8 @@ class OpencartStore(models.Model):
"order 36071 in Opencart, will be named 'OC-36071' "
"in Odoo. (overridden from backend)",
)
+ coupon_product_id = fields.Many2one(comodel_name='product.product', string='Coupon Product',
+ help='Product to represent coupon discounts.')
class OpencartStoreAdapter(Component):
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index f97691d9..dd8f1308 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -61,6 +61,34 @@ class SaleOrderImportMapper(Component):
children = [('products', 'opencart_order_line_ids', 'opencart.sale.order.line'),
]
+ def _add_coupon_lines(self, map_record, values):
+ # Data from API
+ # 'coupons': [{'amount': '7.68', 'code': '1111'}],
+ record = map_record.source
+
+ coupons = record.get('coupons')
+ if not coupons:
+ return values
+
+ coupon_product = self.options.store.coupon_product_id or self.backend_record.coupon_product_id
+ if not coupon_product:
+ coupon_product = self.env.ref('connector_ecommerce.product_product_discount', raise_if_not_found=False)
+
+ if not coupon_product:
+ raise ValueError('Coupon %s on order requires coupon product in configuration.' % (coupons, ))
+ for coupon in coupons:
+ line_builder = self.component(usage='order.line.builder')
+ line_builder.price_unit = -float(coupon.get('amount', 0.0))
+ line_builder.product = coupon_product
+ # `order.line.builder` does not allow naming.
+ values = line_builder.get_line()
+ code = coupon.get('code')
+ if code:
+ values['name'] = '%s Code: %s' % (coupon_product.name, code)
+ line = (0, 0, values)
+ values['order_line'].append(line)
+ return values
+
def _add_shipping_line(self, map_record, values):
record = map_record.source
@@ -77,6 +105,7 @@ class SaleOrderImportMapper(Component):
def finalize(self, map_record, values):
values.setdefault('order_line', [])
+ self._add_coupon_lines(map_record, values)
self._add_shipping_line(map_record, values)
values.update({
'partner_id': self.options.partner_id,
diff --git a/connector_opencart/views/opencart_backend_views.xml b/connector_opencart/views/opencart_backend_views.xml
index 360b9315..716dcc63 100644
--- a/connector_opencart/views/opencart_backend_views.xml
+++ b/connector_opencart/views/opencart_backend_views.xml
@@ -37,6 +37,7 @@
+
@@ -131,6 +132,7 @@
+
From 3aa31f8bcbf87c83458ac65189edbe6bd63c4290 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 24 Mar 2020 09:16:30 -0700
Subject: [PATCH 29/43] FIX `connector_opencart` refactor the coupon naming
that overwrote the local variable named values
---
connector_opencart/models/sale_order/importer.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index dd8f1308..ddd20665 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -81,12 +81,11 @@ class SaleOrderImportMapper(Component):
line_builder.price_unit = -float(coupon.get('amount', 0.0))
line_builder.product = coupon_product
# `order.line.builder` does not allow naming.
- values = line_builder.get_line()
+ line_values = line_builder.get_line()
code = coupon.get('code')
if code:
- values['name'] = '%s Code: %s' % (coupon_product.name, code)
- line = (0, 0, values)
- values['order_line'].append(line)
+ line_values['name'] = '%s Code: %s' % (coupon_product.name, code)
+ values['order_line'].append((0, 0, line_values))
return values
def _add_shipping_line(self, map_record, values):
From 5cc665df4cb6400a40309e01fa89aa12ac2b8294 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 28 Apr 2020 11:33:24 -0700
Subject: [PATCH 30/43] [IMP] connector_opencart: when finding partners, search
for inactive as well.
---
.../models/sale_order/importer.py | 17 ++---------------
1 file changed, 2 insertions(+), 15 deletions(-)
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index ddd20665..819e532a 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -9,9 +9,6 @@ from odoo.addons.component.core import Component
from odoo.addons.connector.components.mapper import mapping
from odoo.exceptions import ValidationError
-import logging
-_logger = logging.getLogger(__name__)
-
class SaleOrderBatchImporter(Component):
_name = 'opencart.sale.order.batch.importer'
@@ -215,21 +212,17 @@ class SaleOrderImporter(Component):
return self.env['res.partner'].create(values)
def _partner_matches(self, partner, values):
- _logger.warn('_partner_matches partner: ' + str(partner) + ' values: ' + str(values))
for key, value in values.items():
if key in ('active', 'parent_id'):
continue
if key == 'state_id':
if value != partner.state_id.id:
- _logger.warn(' return false for state_id value: ' + str(value) + ' partner.state_id.id: ' + str(partner.state_id.id))
return False
elif key == 'country_id':
if value != partner.country_id.id:
- _logger.warn(' return false for country_id value: ' + str(value) + ' partner.country_id.id: ' + str(partner.country_id.id))
return False
elif bool(value) and value != getattr(partner, key):
- _logger.warn(' return false for ' + str(key) + ' : ' + str(value) + ' partner value: ' + str(getattr(partner, key)))
return False
return True
@@ -282,17 +275,15 @@ class SaleOrderImporter(Component):
}
def _import_addresses(self):
- record = self.opencart_record
-
partner_values = self._get_partner_values()
partners = self.env['res.partner'].search([
('email', '=', partner_values['email']),
- ])
+ '|', ('active', '=', False), ('active', '=', True),
+ ], order='active DESC, id ASC')
partner = None
for possible in partners:
if self._partner_matches(possible, partner_values):
- _logger.warn('matched partner: ' + str(possible))
partner = possible
break
if not partner and partners:
@@ -307,7 +298,6 @@ class SaleOrderImporter(Component):
partner_values['active'] = False
shipping_partner = self._create_partner(copy(partner_values))
else:
- _logger.warn('same shipping partner')
shipping_partner = partner
invoice_values = self._get_partner_values(info_string='payment_')
@@ -317,7 +307,6 @@ class SaleOrderImporter(Component):
# Try to find existing invoice address....
for possible in partners:
if self._partner_matches(possible, invoice_values):
- _logger.warn('matched invoice partner: ' + str(possible))
invoice_partner = possible
break
else:
@@ -325,10 +314,8 @@ class SaleOrderImporter(Component):
partner_values['active'] = False
invoice_partner = self._create_partner(copy(invoice_values))
elif self._partner_matches(partner, invoice_values):
- _logger.warn('same invoice partner')
invoice_partner = partner
elif self._partner_matches(shipping_partner, invoice_values):
- _logger.warn('same invoice shipping partner')
invoice_partner = shipping_partner
self.partner = partner
From b68d1714a40389fff381968b2a4f1afb077a0a97 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Wed, 29 Apr 2020 14:40:20 -0700
Subject: [PATCH 31/43] [IMP] connector_opencart: try to find by model then sku
---
connector_opencart/models/product/importer.py | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/connector_opencart/models/product/importer.py b/connector_opencart/models/product/importer.py
index d0e68c45..cdfbfcf5 100644
--- a/connector_opencart/models/product/importer.py
+++ b/connector_opencart/models/product/importer.py
@@ -25,7 +25,8 @@ class ProductImportMapper(Component):
@mapping
def opencart_sku(self, record):
- return {'opencart_sku': (record.get('sku') or '').strip()}
+ sku = str(record.get('model') or record.get('sku') or '').strip()
+ return {'opencart_sku': sku}
@only_create
@mapping
@@ -33,15 +34,18 @@ class ProductImportMapper(Component):
product_template = self.env['product.template']
template = product_template.browse()
- if record.get('sku'):
- sku = (record.get('sku') or '').strip()
+ if record.get('model'):
+ model = str(record.get('model') or '').strip()
# Try to match our own field
- template = product_template.search([('opencart_sku', '=', sku)], limit=1)
+ template = product_template.search([('opencart_sku', '=', model)], limit=1)
if not template:
# Try to match the default_code
- template = product_template.search([('default_code', '=', record.get('sku'))], limit=1)
- if not template and record.get('model'):
- template = product_template.search([('default_code', '=', record.get('model'))], limit=1)
+ template = product_template.search([('default_code', '=', model)], limit=1)
+ if not template and record.get('sku'):
+ sku = str(record.get('sku') or '').strip()
+ template = product_template.search([('opencart_sku', '=', sku)], limit=1)
+ if not template:
+ template = product_template.search([('default_code', '=', sku)], limit=1)
if not template and record.get('name'):
name = record.get('product_description', [{}])[0].get('name')
if name:
From 93178894d475199a773c636326a0d07302044bbc Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Mon, 4 May 2020 14:06:09 -0700
Subject: [PATCH 32/43] [FIX] connector_opencart: Compare string to string
instead of integer.
---
connector_opencart/models/product/importer.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/connector_opencart/models/product/importer.py b/connector_opencart/models/product/importer.py
index cdfbfcf5..c7988323 100644
--- a/connector_opencart/models/product/importer.py
+++ b/connector_opencart/models/product/importer.py
@@ -73,7 +73,7 @@ class ProductImporter(Component):
backend = self.backend_record
for option in record.get('options', []):
for record_option_value in option.get('option_value', []):
- option_value = existing_option_values.filtered(lambda v: v.external_id == record_option_value['product_option_value_id'])
+ option_value = existing_option_values.filtered(lambda v: v.external_id == str(record_option_value['product_option_value_id']))
name = unescape(record_option_value.get('name', ''))
if not option_value:
option_value = existing_option_values.create({
From 544930f592c6d946caaa15e21935625119bcce5c Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 7 May 2020 08:50:58 -0700
Subject: [PATCH 33/43] [IMP] connector_opencart: Raise warning when product
cannot be created/found by attributes, enable logging in upstream.
---
connector_opencart/models/product/common.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/connector_opencart/models/product/common.py b/connector_opencart/models/product/common.py
index 8e0a9c0c..4d169803 100644
--- a/connector_opencart/models/product/common.py
+++ b/connector_opencart/models/product/common.py
@@ -37,7 +37,10 @@ class OpencartProductTemplate(models.Model):
raise RetryableJobError('Order Product (%s) has option (%s) "%s" that is not mapped to an Odoo Attribute Value.' % (self, opencart_attribute_value.external_id, opencart_attribute_value.opencart_name))
selected_attribute_values += opencart_attribute_value.odoo_id
# Now that we know what options are selected, we can load a variant with those options
- return self.odoo_id._create_product_variant(selected_attribute_values)
+ product = self.odoo_id._create_product_variant(selected_attribute_values, log_warning=True)
+ if not product:
+ raise Exception('No product can be created for selected attribute values, check logs. ' + str(selected_attribute_values))
+ return product
class ProductTemplate(models.Model):
From ac29c1ed077f9c6d1aa0b8a28e109a839ce895af Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Fri, 19 Jun 2020 11:08:08 -0700
Subject: [PATCH 34/43] [FIX] connector_opencart: Delivery and Invoice address
types set before creation.
---
connector_opencart/models/sale_order/importer.py | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index 819e532a..b33f4f5f 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -213,7 +213,7 @@ class SaleOrderImporter(Component):
def _partner_matches(self, partner, values):
for key, value in values.items():
- if key in ('active', 'parent_id'):
+ if key in ('active', 'parent_id', 'type'):
continue
if key == 'state_id':
@@ -227,7 +227,7 @@ class SaleOrderImporter(Component):
return True
def _make_partner_name(self, firstname, lastname):
- name = (str(firstname) + ' ' + str(lastname)).strip()
+ name = (str(firstname or '').strip() + ' ' + str(lastname or '').strip()).strip()
if not name:
return 'Undefined'
return name
@@ -295,12 +295,14 @@ class SaleOrderImporter(Component):
if not self._partner_matches(partner, partner_values):
partner_values['parent_id'] = partner.id
- partner_values['active'] = False
- shipping_partner = self._create_partner(copy(partner_values))
+ shipping_values = copy(partner_values)
+ shipping_values['type'] = 'delivery'
+ shipping_partner = self._create_partner(shipping_values)
else:
shipping_partner = partner
invoice_values = self._get_partner_values(info_string='payment_')
+ invoice_values['type'] = 'invoice'
if (not self._partner_matches(partner, invoice_values)
and not self._partner_matches(shipping_partner, invoice_values)):
@@ -310,8 +312,7 @@ class SaleOrderImporter(Component):
invoice_partner = possible
break
else:
- partner_values['parent_id'] = partner.id
- partner_values['active'] = False
+ invoice_values['parent_id'] = partner.id
invoice_partner = self._create_partner(copy(invoice_values))
elif self._partner_matches(partner, invoice_values):
invoice_partner = partner
From 3d2de4e80a8c917cbfda3abdbca2fb231cbbe11c Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Tue, 14 Jul 2020 14:01:23 -0700
Subject: [PATCH 35/43] [IMP] connector_opencart: Add option on backend to
require all SO products to not have checkpoints before importing order.
Additionally, allow users to re-bind product templates (understandably risky).
---
connector_opencart/components/importer.py | 10 +++++++++-
connector_opencart/models/opencart/backend.py | 17 +++++++++++++++++
.../models/sale_order/importer.py | 10 +++++++++-
.../views/opencart_backend_views.xml | 1 +
.../views/opencart_product_views.xml | 2 +-
5 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/connector_opencart/components/importer.py b/connector_opencart/components/importer.py
index a2c7f973..6a1ef5db 100644
--- a/connector_opencart/components/importer.py
+++ b/connector_opencart/components/importer.py
@@ -88,7 +88,8 @@ class OpencartImporter(AbstractComponent):
if not external_id:
return
binder = self.binder_for(binding_model)
- if always or not binder.to_internal(external_id):
+ record = binder.to_internal(external_id)
+ if always or not record:
if importer is None:
importer = self.component(usage='record.importer',
model_name=binding_model)
@@ -99,6 +100,13 @@ class OpencartImporter(AbstractComponent):
'Dependency import of %s(%s) has been ignored.',
binding_model._name, external_id
)
+ return True
+ if binding_model == 'opencart.product.template' and record.backend_id.so_require_product_setup:
+ # Though this is not the "right" place to do this,
+ # we need to return True if there is a checkpoint for a product.
+ if record.backend_id.find_checkpoint(record):
+ return True
+ return False
def _import_dependencies(self):
""" Import the dependencies for the record
diff --git a/connector_opencart/models/opencart/backend.py b/connector_opencart/models/opencart/backend.py
index 46620a87..42cd62ad 100644
--- a/connector_opencart/models/opencart/backend.py
+++ b/connector_opencart/models/opencart/backend.py
@@ -73,6 +73,9 @@ class OpencartBackend(models.Model):
string='Import sale orders after id',
)
+ so_require_product_setup = fields.Boolean(string='SO Require Product Setup',
+ help='Prevents SO from being confirmed (failed queue job), if one or more products has an open checkpoint.')
+
@contextmanager
@api.multi
def work_on(self, model_name, **kwargs):
@@ -89,6 +92,20 @@ class OpencartBackend(models.Model):
return add_checkpoint(self.env, record._name, record.id,
self._name, self.id)
+ @api.multi
+ def find_checkpoint(self, record):
+ self.ensure_one()
+ record.ensure_one()
+ checkpoint_model = self.env['connector.checkpoint']
+ model_model = self.env['ir.model']
+ model = model_model.search([('model', '=', record._name)], limit=1)
+ return checkpoint_model.search([
+ ('backend_id', '=', '%s,%s' % (self._name, self.id)),
+ ('model_id', '=', model.id),
+ ('record_id', '=', record.id),
+ ('state', '=', 'need_review'),
+ ], limit=1)
+
@api.multi
def synchronize_metadata(self):
try:
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index b33f4f5f..79e19595 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -8,6 +8,7 @@ from odoo import fields, _
from odoo.addons.component.core import Component
from odoo.addons.connector.components.mapper import mapping
from odoo.exceptions import ValidationError
+from odoo.addons.queue_job.exception import RetryableJobError
class SaleOrderBatchImporter(Component):
@@ -363,9 +364,16 @@ class SaleOrderImporter(Component):
def _import_dependencies(self):
record = self.opencart_record
self._import_addresses()
+ products_need_setup = []
for product in record.get('products', []):
if 'product_id' in product and product['product_id']:
- self._import_dependency(product['product_id'], 'opencart.product.template')
+ needs_product_setup = self._import_dependency(product['product_id'], 'opencart.product.template')
+ if needs_product_setup:
+ products_need_setup.append(product['product_id'])
+
+ if products_need_setup and self.backend_record.so_require_product_setup:
+ # There are products that were either just imported, or
+ raise RetryableJobError('Products need setup. OpenCart Product IDs:' + str(products_need_setup), seconds=3600)
class SaleOrderLineImportMapper(Component):
diff --git a/connector_opencart/views/opencart_backend_views.xml b/connector_opencart/views/opencart_backend_views.xml
index 716dcc63..d60fda7b 100644
--- a/connector_opencart/views/opencart_backend_views.xml
+++ b/connector_opencart/views/opencart_backend_views.xml
@@ -38,6 +38,7 @@
+
diff --git a/connector_opencart/views/opencart_product_views.xml b/connector_opencart/views/opencart_product_views.xml
index fec97208..4cec551f 100644
--- a/connector_opencart/views/opencart_product_views.xml
+++ b/connector_opencart/views/opencart_product_views.xml
@@ -9,7 +9,7 @@
-
+
From bc7ff935930076c52c6ecc66957a0321c31f2803 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 16 Jul 2020 12:45:37 -0700
Subject: [PATCH 36/43] Bump hibou-oca/social to current 12 for mass mailing
changes/fixes.
---
external/hibou-oca/social | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/external/hibou-oca/social b/external/hibou-oca/social
index eeedcbd4..6ecc9c52 160000
--- a/external/hibou-oca/social
+++ b/external/hibou-oca/social
@@ -1 +1 @@
-Subproject commit eeedcbd41dc960ffdf19e8f7924ecb5b7aa9d84d
+Subproject commit 6ecc9c52f37dc32c45f206c1152a67477f3a69e0
From 203e480f05539693fc3682878ab53370fc63921d Mon Sep 17 00:00:00 2001
From: Bhoomi Vaishnani
Date: Tue, 25 Aug 2020 16:28:15 -0400
Subject: [PATCH 37/43] [IMP] l10n_us_hr_payroll: Indiana, Kansas, Kentucky,
Louisiana, Maine, North Dakota, Nebraska, Nevada, Oklahoma, Rhode Island,
South Carolina, South Dakota, Tennessee, Utah, Vermont, Wisconsin, West
Virginia and Wyoming For Odoo 12.0
Improved xml table and test case for other states.
---
l10n_us_hr_payroll/__manifest__.py | 18 +
l10n_us_hr_payroll/data/final.xml | 50 ++
l10n_us_hr_payroll/data/state/ak_alaska.xml | 1 +
l10n_us_hr_payroll/data/state/al_alabama.xml | 58 +-
l10n_us_hr_payroll/data/state/ar_arkansas.xml | 4 +
l10n_us_hr_payroll/data/state/az_arizona.xml | 2 +
.../data/state/ca_california.xml | 565 +++++++++++--
.../data/state/ct_connecticut.xml | 24 +-
l10n_us_hr_payroll/data/state/fl_florida.xml | 1 +
l10n_us_hr_payroll/data/state/ga_georgia.xml | 750 +++++++++++++++++-
l10n_us_hr_payroll/data/state/hi_hawaii.xml | 68 +-
l10n_us_hr_payroll/data/state/ia_iowa.xml | 153 +++-
l10n_us_hr_payroll/data/state/id_idaho.xml | 346 +++++++-
l10n_us_hr_payroll/data/state/in_indiana.xml | 110 +++
l10n_us_hr_payroll/data/state/ks_kansas.xml | 186 +++++
l10n_us_hr_payroll/data/state/ky_kentucky.xml | 91 +++
.../data/state/la_louisiana.xml | 148 ++++
l10n_us_hr_payroll/data/state/me_maine.xml | 119 +++
.../data/state/mn_minnesota.xml | 20 +-
l10n_us_hr_payroll/data/state/mo_missouri.xml | 6 +
.../data/state/ms_mississippi.xml | 15 +
l10n_us_hr_payroll/data/state/mt_montana.xml | 73 +-
.../data/state/nc_northcarolina.xml | 4 +
.../data/state/nd_north_dakota.xml | 272 +++++++
l10n_us_hr_payroll/data/state/ne_nebraska.xml | 230 ++++++
.../data/state/nj_newjersey.xml | 750 ++++++++++++++++--
.../data/state/nm_new_mexico.xml | 238 +++++-
l10n_us_hr_payroll/data/state/nv_nevada.xml | 49 ++
l10n_us_hr_payroll/data/state/ny_new_york.xml | 2 +-
l10n_us_hr_payroll/data/state/ok_oklahoma.xml | 294 +++++++
.../data/state/ri_rhode_island.xml | 135 ++++
.../data/state/sc_south_carolina.xml | 146 ++++
.../data/state/sd_south_dakota.xml | 48 ++
.../data/state/tn_tennessee.xml | 48 ++
l10n_us_hr_payroll/data/state/ut_utah.xml | 153 ++++
l10n_us_hr_payroll/data/state/vt_vermont.xml | 185 +++++
.../data/state/wi_wisconsin.xml | 109 +++
.../data/state/wv_west_virginia.xml | 209 +++++
l10n_us_hr_payroll/data/state/wy_wyoming.xml | 60 ++
l10n_us_hr_payroll/migrations/data.py | 20 +
l10n_us_hr_payroll/models/hr_payslip.py | 28 +
l10n_us_hr_payroll/models/state/al_alabama.py | 5 +-
.../models/state/ar_arkansas.py | 2 +-
l10n_us_hr_payroll/models/state/ga_georgia.py | 2 +-
l10n_us_hr_payroll/models/state/ia_iowa.py | 6 +-
l10n_us_hr_payroll/models/state/in_indiana.py | 34 +
l10n_us_hr_payroll/models/state/ks_kansas.py | 44 +
.../models/state/ky_kentucky.py | 32 +
.../models/state/la_louisiana.py | 62 ++
l10n_us_hr_payroll/models/state/me_maine.py | 62 ++
.../models/state/nd_north_dakota.py | 45 ++
.../models/state/ne_nebraska.py | 49 ++
.../models/state/ok_oklahoma.py | 47 ++
.../models/state/ri_rhode_island.py | 48 ++
.../models/state/sc_south_carolina.py | 50 ++
l10n_us_hr_payroll/models/state/ut_utah.py | 39 +
l10n_us_hr_payroll/models/state/vt_vermont.py | 46 ++
.../models/state/wi_wisconsin.py | 47 ++
.../models/state/wv_west_virginia.py | 44 +
.../models/us_payroll_config.py | 78 +-
l10n_us_hr_payroll/tests/__init__.py | 39 +
.../tests/test_us_al_alabama_payslip_2020.py | 2 +-
.../tests/test_us_ar_arkansas_payslip_2020.py | 6 +-
.../tests/test_us_az_arizona_payslip_2020.py | 1 +
.../test_us_ca_california_payslip_2020.py | 1 +
.../tests/test_us_co_colorado_payslip_2020.py | 1 +
.../test_us_ct_connecticut_payslip_2020.py | 1 +
.../tests/test_us_ga_georgia_payslip_2020.py | 151 +---
.../tests/test_us_hi_hawaii_payslip_2020.py | 2 +
.../tests/test_us_ia_iowa_payslip_2020.py | 13 +-
.../tests/test_us_id_idaho_payslip_2020.py | 2 +-
.../tests/test_us_in_indiana_payslip_2020.py | 36 +
.../tests/test_us_ks_kansas_payslip_2020.py | 36 +
.../tests/test_us_ky_kentucky_payslip_2020.py | 35 +
.../test_us_la_louisiana_payslip_2019.py | 91 +++
.../test_us_la_louisiana_payslip_2020.py | 36 +
.../tests/test_us_me_maine_payslip_2020.py | 39 +
.../tests/test_us_mi_michigan_payslip_2020.py | 8 +-
.../tests/test_us_mo_missouri_payslip_2020.py | 97 +--
.../test_us_ms_mississippi_payslip_2020.py | 131 +--
.../tests/test_us_mt_montana_payslip_2020.py | 30 +-
.../test_us_nc_northcarolina_payslip_2020.py | 3 +-
.../test_us_nd_north_dakota_payslip_2020.py | 37 +
.../tests/test_us_ne_nebraska_payslip_2020.py | 38 +
.../test_us_nj_newjersey_payslip_2020.py | 3 +
.../test_us_nm_new_mexico_payslip_2020.py | 1 +
.../tests/test_us_nv_nevada_payslip_2020.py | 16 +
.../tests/test_us_ny_new_york_payslip_2019.py | 27 +
.../tests/test_us_ok_oklahoma_payslip_2020.py | 38 +
.../test_us_ri_rhode_island_payslip_2020.py | 37 +
.../test_us_sc_south_carolina_payslip_2019.py | 97 +++
.../test_us_sc_south_carolina_payslip_2020.py | 36 +
.../test_us_sd_south_dakota_payslip_2020.py | 13 +
.../test_us_tn_tennessee_payslip_2020.py | 13 +
.../tests/test_us_us_utah_payslip_2020.py | 36 +
.../tests/test_us_vt_vermont_payslip_2020.py | 37 +
.../test_us_wi_wisconsin_payslip_2020.py | 39 +
.../test_us_wv_west_virginia_payslip_2020.py | 36 +
.../tests/test_us_wy_wyoming_payslip_2019.py | 58 ++
.../tests/test_us_wy_wyoming_payslip_2020.py | 13 +
.../views/us_payroll_config_views.xml | 100 +++
101 files changed, 7332 insertions(+), 634 deletions(-)
create mode 100644 l10n_us_hr_payroll/data/state/in_indiana.xml
create mode 100644 l10n_us_hr_payroll/data/state/ks_kansas.xml
create mode 100644 l10n_us_hr_payroll/data/state/ky_kentucky.xml
create mode 100644 l10n_us_hr_payroll/data/state/la_louisiana.xml
create mode 100644 l10n_us_hr_payroll/data/state/me_maine.xml
create mode 100644 l10n_us_hr_payroll/data/state/nd_north_dakota.xml
create mode 100644 l10n_us_hr_payroll/data/state/ne_nebraska.xml
create mode 100644 l10n_us_hr_payroll/data/state/nv_nevada.xml
create mode 100644 l10n_us_hr_payroll/data/state/ok_oklahoma.xml
create mode 100644 l10n_us_hr_payroll/data/state/ri_rhode_island.xml
create mode 100644 l10n_us_hr_payroll/data/state/sc_south_carolina.xml
create mode 100644 l10n_us_hr_payroll/data/state/sd_south_dakota.xml
create mode 100644 l10n_us_hr_payroll/data/state/tn_tennessee.xml
create mode 100644 l10n_us_hr_payroll/data/state/ut_utah.xml
create mode 100644 l10n_us_hr_payroll/data/state/vt_vermont.xml
create mode 100644 l10n_us_hr_payroll/data/state/wi_wisconsin.xml
create mode 100644 l10n_us_hr_payroll/data/state/wv_west_virginia.xml
create mode 100644 l10n_us_hr_payroll/data/state/wy_wyoming.xml
create mode 100644 l10n_us_hr_payroll/models/state/in_indiana.py
create mode 100644 l10n_us_hr_payroll/models/state/ks_kansas.py
create mode 100644 l10n_us_hr_payroll/models/state/ky_kentucky.py
create mode 100644 l10n_us_hr_payroll/models/state/la_louisiana.py
create mode 100644 l10n_us_hr_payroll/models/state/me_maine.py
create mode 100644 l10n_us_hr_payroll/models/state/nd_north_dakota.py
create mode 100644 l10n_us_hr_payroll/models/state/ne_nebraska.py
create mode 100644 l10n_us_hr_payroll/models/state/ok_oklahoma.py
create mode 100644 l10n_us_hr_payroll/models/state/ri_rhode_island.py
create mode 100644 l10n_us_hr_payroll/models/state/sc_south_carolina.py
create mode 100644 l10n_us_hr_payroll/models/state/ut_utah.py
create mode 100644 l10n_us_hr_payroll/models/state/vt_vermont.py
create mode 100644 l10n_us_hr_payroll/models/state/wi_wisconsin.py
create mode 100644 l10n_us_hr_payroll/models/state/wv_west_virginia.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2020.py
create mode 100755 l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2020.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
create mode 100644 l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py
diff --git a/l10n_us_hr_payroll/__manifest__.py b/l10n_us_hr_payroll/__manifest__.py
index ec4a8428..d1f05209 100755
--- a/l10n_us_hr_payroll/__manifest__.py
+++ b/l10n_us_hr_payroll/__manifest__.py
@@ -42,21 +42,39 @@ USA Payroll Rules.
'data/state/ia_iowa.xml',
'data/state/id_idaho.xml',
'data/state/il_illinois.xml',
+ 'data/state/in_indiana.xml',
+ 'data/state/ks_kansas.xml',
+ 'data/state/ky_kentucky.xml',
+ 'data/state/la_louisiana.xml',
+ 'data/state/me_maine.xml',
'data/state/mi_michigan.xml',
'data/state/mn_minnesota.xml',
'data/state/mo_missouri.xml',
'data/state/ms_mississippi.xml',
'data/state/mt_montana.xml',
'data/state/nc_northcarolina.xml',
+ 'data/state/nd_north_dakota.xml',
+ 'data/state/ne_nebraska.xml',
'data/state/nh_new_hampshire.xml',
'data/state/nj_newjersey.xml',
'data/state/nm_new_mexico.xml',
+ 'data/state/nv_nevada.xml',
'data/state/ny_new_york.xml',
'data/state/oh_ohio.xml',
+ 'data/state/ok_oklahoma.xml',
'data/state/pa_pennsylvania.xml',
+ 'data/state/ri_rhode_island.xml',
+ 'data/state/sc_south_carolina.xml',
+ 'data/state/sd_south_dakota.xml',
+ 'data/state/tn_tennessee.xml',
'data/state/tx_texas.xml',
+ 'data/state/ut_utah.xml',
+ 'data/state/vt_vermont.xml',
'data/state/va_virginia.xml',
'data/state/wa_washington.xml',
+ 'data/state/wi_wisconsin.xml',
+ 'data/state/wv_west_virginia.xml',
+ 'data/state/wy_wyoming.xml',
'data/final.xml',
'views/hr_contract_views.xml',
'views/res_config_settings_views.xml',
diff --git a/l10n_us_hr_payroll/data/final.xml b/l10n_us_hr_payroll/data/final.xml
index b11f41da..0ba91196 100644
--- a/l10n_us_hr_payroll/data/final.xml
+++ b/l10n_us_hr_payroll/data/final.xml
@@ -59,6 +59,21 @@
ref('hr_payroll_rule_er_us_il_suta'),
ref('hr_payroll_rule_ee_us_il_sit'),
+ ref('hr_payroll_rule_er_us_in_suta'),
+ ref('hr_payroll_rule_ee_us_in_sit'),
+
+ ref('hr_payroll_rule_er_us_ks_suta'),
+ ref('hr_payroll_rule_ee_us_ks_sit'),
+
+ ref('hr_payroll_rule_er_us_ky_suta'),
+ ref('hr_payroll_rule_ee_us_ky_sit'),
+
+ ref('hr_payroll_rule_er_us_la_suta'),
+ ref('hr_payroll_rule_ee_us_la_sit'),
+
+ ref('hr_payroll_rule_er_us_me_suta'),
+ ref('hr_payroll_rule_ee_us_me_sit'),
+
ref('hr_payroll_rule_er_us_mn_suta'),
ref('hr_payroll_rule_ee_us_mn_sit'),
@@ -78,6 +93,12 @@
ref('hr_payroll_rule_er_us_nc_suta'),
ref('hr_payroll_rule_ee_us_nc_sit'),
+ ref('hr_payroll_rule_er_us_nd_suta'),
+ ref('hr_payroll_rule_ee_us_nd_sit'),
+
+ ref('hr_payroll_rule_er_us_ne_suta'),
+ ref('hr_payroll_rule_ee_us_ne_sit'),
+
ref('hr_payroll_rule_er_us_nh_suta'),
ref('hr_payroll_rule_er_us_nj_suta'),
@@ -93,6 +114,8 @@
ref('hr_payroll_rule_er_us_nm_suta'),
ref('hr_payroll_rule_ee_us_nm_sit'),
+ ref('hr_payroll_rule_er_us_nv_suta'),
+
ref('hr_payroll_rule_er_us_ny_suta'),
ref('hr_payroll_rule_er_us_ny_suta_rsf'),
ref('hr_payroll_rule_er_us_ny_suta_mctmt'),
@@ -101,23 +124,50 @@
ref('hr_payroll_rule_er_us_oh_suta'),
ref('hr_payroll_rule_ee_us_oh_sit'),
+ ref('hr_payroll_rule_er_us_ok_suta'),
+ ref('hr_payroll_rule_ee_us_ok_sit'),
+
ref('hr_payroll_rule_er_us_pa_suta'),
ref('hr_payroll_rule_ee_us_pa_suta'),
ref('hr_payroll_rule_ee_us_pa_sit'),
+ ref('hr_payroll_rule_er_us_ri_suta'),
+ ref('hr_payroll_rule_ee_us_ri_sit'),
+
+ ref('hr_payroll_rule_er_us_sc_suta'),
+ ref('hr_payroll_rule_ee_us_sc_sit'),
+
+ ref('hr_payroll_rule_er_us_sd_suta'),
+
+ ref('hr_payroll_rule_er_us_tn_suta'),
+
ref('hr_payroll_rule_er_us_tx_suta'),
ref('hr_payroll_rule_er_us_tx_suta_oa'),
ref('hr_payroll_rule_er_us_tx_suta_etia'),
+ ref('hr_payroll_rule_er_us_ut_suta'),
+ ref('hr_payroll_rule_ee_us_ut_sit'),
+
ref('hr_payroll_rule_er_us_va_suta'),
ref('hr_payroll_rule_ee_us_va_sit'),
+ ref('hr_payroll_rule_er_us_vt_suta'),
+ ref('hr_payroll_rule_ee_us_vt_sit'),
+
ref('hr_payroll_rule_er_us_wa_suta'),
ref('hr_payroll_rule_er_us_wa_fml'),
ref('hr_payroll_rule_ee_us_wa_fml'),
ref('hr_payroll_rule_er_us_wa_lni'),
ref('hr_payroll_rule_ee_us_wa_lni'),
+ ref('hr_payroll_rule_er_us_wi_suta'),
+ ref('hr_payroll_rule_ee_us_wi_sit'),
+
+ ref('hr_payroll_rule_er_us_wv_suta'),
+ ref('hr_payroll_rule_ee_us_wv_sit'),
+
+ ref('hr_payroll_rule_er_us_wy_suta'),
+
ref('hr_salary_rule_commission'),
ref('hr_salary_rule_gamification'),
])]" name="rule_ids"/>
diff --git a/l10n_us_hr_payroll/data/state/ak_alaska.xml b/l10n_us_hr_payroll/data/state/ak_alaska.xml
index cf0f747f..d63c387d 100644
--- a/l10n_us_hr_payroll/data/state/ak_alaska.xml
+++ b/l10n_us_hr_payroll/data/state/ak_alaska.xml
@@ -50,6 +50,7 @@
US Alaska - Department of Labor and Workforce Development (ADLWD) - Unemployment Tax
+ 1
US Alaska - Department of Labor and Workforce Development (ADLWD) - Unemployment Tax
diff --git a/l10n_us_hr_payroll/data/state/al_alabama.xml b/l10n_us_hr_payroll/data/state/al_alabama.xml
index 51310ccc..bf2d0a7f 100644
--- a/l10n_us_hr_payroll/data/state/al_alabama.xml
+++ b/l10n_us_hr_payroll/data/state/al_alabama.xml
@@ -37,8 +37,16 @@
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)],
+ '0': [
+ ( 500, 2),
+ ( 3000, 4),
+ ('inf', 5),
+ ],
+ 'M': [
+ ( 1000, 2),
+ ( 6000, 4),
+ ('inf', 5),
+ ],
}
@@ -46,14 +54,24 @@
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)],
+ '0' : [
+ ( 500, 2),
+ ( 2500, 4),
+ ('inf', 5),
+ ],
+ 'M': [
+ ( 1000, 2),
+ ( 5000, 4),
+ ('inf', 5),
+ ],
}
+
+
US AL Alabama Dependent Rate
us_al_sit_dependent_rate
@@ -64,6 +82,8 @@
]
+
+
US AL Alabama Dependent Rate
us_al_sit_dependent_rate
@@ -77,33 +97,39 @@
+
+
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)),
+ '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)),
+ '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
@@ -116,6 +142,8 @@
}
+
+
US AL Alabama Personal Exemption Rate
us_al_sit_personal_exemption_rate
@@ -133,6 +161,7 @@
US Alabama - Department of Economic Security (IDES) - Unemployment Tax
+ 1
US Alabama - Department of Economic Security (IDES) - Unemployment Tax
@@ -141,6 +170,7 @@
US Alabama - Department of Revenue (IDOR) - Income Tax
+ 1
US Alabama - Department of Revenue (IDOR) - Unemployment Tax
diff --git a/l10n_us_hr_payroll/data/state/ar_arkansas.xml b/l10n_us_hr_payroll/data/state/ar_arkansas.xml
index fcc162eb..1940ef85 100644
--- a/l10n_us_hr_payroll/data/state/ar_arkansas.xml
+++ b/l10n_us_hr_payroll/data/state/ar_arkansas.xml
@@ -52,6 +52,8 @@
]
+
+
US AR Arkansas SIT Tax Rate
us_ar_sit_tax_rate
@@ -91,6 +93,7 @@
US Arkansas - Department of Workforce Solutions - Unemployment Tax
+ 1
US Arkansas - Department of Workforce Solutions - Unemployment Tax
@@ -99,6 +102,7 @@
US Arkansas - Department of Financial Administration - Income Tax
+ 1
US Arkansas - Department of Financial Administration - Income Tax
diff --git a/l10n_us_hr_payroll/data/state/az_arizona.xml b/l10n_us_hr_payroll/data/state/az_arizona.xml
index 832fe816..92bb3ef1 100644
--- a/l10n_us_hr_payroll/data/state/az_arizona.xml
+++ b/l10n_us_hr_payroll/data/state/az_arizona.xml
@@ -35,6 +35,7 @@
US Arizona - Department of Economic Security (ADES) - Unemployment Tax
+ 1
US Arizona - Department of Economic Security (ADES) - Unemployment Tax
@@ -43,6 +44,7 @@
US Arizona - Department of Revenue (ADOR) - Income Tax
+ 1
US Arizona - Department of Revenue (ADOR) - Income Tax
diff --git a/l10n_us_hr_payroll/data/state/ca_california.xml b/l10n_us_hr_payroll/data/state/ca_california.xml
index 4631b2c9..0bd29bda 100644
--- a/l10n_us_hr_payroll/data/state/ca_california.xml
+++ b/l10n_us_hr_payroll/data/state/ca_california.xml
@@ -70,32 +70,263 @@
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)),
+ 'weekly': (
+ ( 316, 0.0110, 0.00),
+ ( 750, 0.0220, 3.48),
+ ( 967, 0.0440, 13.03),
+ ( 1196, 0.0660, 22.58),
+ ( 1413, 0.0880, 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.0110, 0.00),
+ ( 1500, 0.0220, 6.95),
+ ( 1934, 0.0440, 26.05),
+ ( 2392, 0.0660, 45.15),
+ ( 2826, 0.0880, 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.0110, 0.00),
+ ( 1625, 0.0220, 7.55),
+ ( 2094, 0.0440, 28.21),
+ ( 2592, 0.0660, 48.85),
+ ( 3062, 0.0880, 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.0110, 0.00),
+ ( 3250, 0.0220, 15.09),
+ ( 4188, 0.0440, 56.41),
+ ( 5184, 0.0660, 97.68),
+ ( 6124, 0.0880, 163.42),
+ (31250, 0.1023, 246.14),
+ (37500, 0.1133, 2816.53),
+ (62500, 0.1243, 3524.66),
+ (83334, 0.1353, 6632.16),
+ ('inf', 0.1463, 9451.00),
+ ),
+ 'quarterly': (
+ ( 4114, 0.0110, 0.00),
+ ( 9748, 0.0220, 45.25),
+ ( 12566, 0.0440, 169.20),
+ ( 15552, 0.0660, 293.19),
+ ( 18369, 0.0880, 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.0110, 0.00),
+ ( 19496, 0.0220, 90.51),
+ ( 25132, 0.0440, 338.41),
+ ( 31104, 0.0660, 586.39),
+ ( 36738, 0.0880, 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.0110, 0.00),
+ ( 38991, 0.0220, 181.03),
+ ( 50264, 0.0440, 676.78),
+ ( 62206, 0.0660, 1172.79),
+ ( 73477, 0.0880, 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)),
- },
+ 'weekly': (
+ ( 316, 0.0110, 0.00),
+ ( 750, 0.0220, 3.48),
+ ( 1184, 0.0440, 13.03),
+ ( 1642, 0.0660, 32.13),
+ ( 2076, 0.0880, 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.0110, 0.00),
+ ( 1500, 0.0220, 6.95),
+ ( 2368, 0.0440, 26.05),
+ ( 3284, 0.0660, 64.24),
+ ( 4152, 0.0880, 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.0110, 0.00),
+ ( 1624, 0.0220, 7.55),
+ ( 2564, 0.0440, 28.19),
+ ( 3560, 0.0660, 69.55),
+ ( 4498, 0.0880, 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.0110, 0.00),
+ ( 3248, 0.0220, 15.09),
+ ( 5128, 0.0440, 56.36),
+ ( 7120, 0.0660, 139.08),
+ ( 8996, 0.0880, 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.0110, 0.00),
+ ( 9748, 0.0220, 45.23),
+ ( 15384, 0.0440, 169.22),
+ ( 21356, 0.0660, 417.20),
+ ( 26990, 0.0880, 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.0110, 0.00),
+ ( 19496, 0.0220, 90.46),
+ ( 30768, 0.0440, 338.44),
+ ( 42712, 0.0660, 834.41),
+ ( 53980, 0.0880, 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.0110, 0.00),
+ ( 38990, 0.0220, 180.91),
+ ( 61538, 0.0440, 676.88),
+ ( 85422, 0.0660, 1668.99),
+ ( 107960, 0.0880, 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)),
- },
+ 'weekly': (
+ ( 158, 0.0110, 0.00),
+ ( 375, 0.0220, 1.74),
+ ( 592, 0.0440, 6.51),
+ ( 821, 0.0660, 16.06),
+ ( 1038, 0.0880, 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.0110, 0.00),
+ ( 750, 0.0220, 3.48),
+ ( 1184, 0.0440, 13.03),
+ ( 1642, 0.066, 32.13),
+ ( 2076, 0.0880, 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.0110, 0.00),
+ ( 812, 0.0220, 3.77),
+ ( 1282, 0.0440, 14.09),
+ ( 1780, 0.0660, 34.77),
+ ( 2249, 0.0880, 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.0110, 0.00),
+ ( 1624, 0.0220, 7.55),
+ ( 2564, 0.0440, 28.19),
+ ( 3560, 0.0660, 69.55),
+ ( 4498, 0.0880, 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.0110, 0.00),
+ ( 4874, 0.0220, 22.62),
+ ( 7692, 0.0440, 84.62),
+ ( 10678, 0.066, 208.61),
+ ( 13495, 0.0880, 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.0110, 0.00),
+ ( 9748, 0.0220, 45.23),
+ ( 15384, 0.0440, 169.22),
+ ( 21356, 0.0660, 417.20),
+ ( 26990, 0.0880, 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.0110, 0.00),
+ ( 19495, 0.0220, 90.45),
+ ( 30769, 0.0440, 338.43),
+ ( 42711, 0.0660, 834.49),
+ ( 53980, 0.0880, 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),
+ ),
+ },
}
@@ -103,33 +334,263 @@
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)),
- },
+ 'weekly': (
+ ( 339, 0.0110, 0.00),
+ ( 803, 0.0220, 3.73),
+ ( 1035, 0.0440, 13.93),
+ ( 1281, 0.0660, 24.15),
+ ( 1514, 0.0880, 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.0110, 0.00),
+ ( 1606, 0.0220, 7.46),
+ ( 2070, 0.0440, 27.88),
+ ( 2562, 0.0660, 48.30),
+ ( 3028, 0.0880, 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.0110, 0.00),
+ ( 1740, 0.0220, 8.09),
+ ( 2243, 0.0440, 30.20),
+ ( 2777, 0.0660, 52.33),
+ ( 3280, 0.0880, 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.0110, 0.00),
+ ( 3480, 0.0220, 16.17),
+ ( 4486, 0.0440, 60.39),
+ ( 5554, 0.0660, 104.65),
+ ( 6560, 0.0880, 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.0110, 0.00),
+ ( 10442, 0.0220, 48.48),
+ ( 13461, 0.0440, 181.25),
+ ( 16659, 0.0660, 314.09),
+ ( 19678, 0.0880, 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.0110, 0.00),
+ ( 20884, 0.0220, 96.95),
+ ( 26922, 0.0440, 362.49),
+ ( 33318, 0.0660, 628.16),
+ ( 39356, 0.0880, 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.0110, 0.00),
+ ( 41768, 0.0220, 193.92),
+ ( 53843, 0.0440, 724.98),
+ ( 66636, 0.0660, 1256.28),
+ ( 78710, 0.0880, 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)),
- },
+ 'weekly': (
+ ( 338, 0.0110, 0.00),
+ ( 804, 0.0220, 3.72),
+ ( 1268, 0.0440, 13.97),
+ ( 1760, 0.0660, 34.39),
+ ( 2224, 0.0880, 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.0110, 0.00),
+ ( 1608, 0.0220, 7.44),
+ ( 2536, 0.0440, 27.94),
+ ( 3520, 0.0660, 68.77),
+ ( 4448, 0.0880, 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.0110, 0.00),
+ ( 1740, 0.0220, 8.07),
+ ( 2746, 0.0440, 30.20),
+ ( 3812, 0.0660, 74.46),
+ ( 4818, 0.0880, 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.0110, 0.00),
+ ( 3480, 0.0220, 16.15),
+ ( 5492, 0.0440, 60.41),
+ ( 7624, 0.0660, 148.94),
+ ( 9636, 0.0880, 289.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.0110, 0.00),
+ ( 10442, 0.0220, 48.44),
+ ( 16480, 0.0440, 181.28),
+ ( 22876, 0.0660, 446.95),
+ ( 28912, 0.0880, 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.0110, 0.00),
+ ( 20884, 0.0220, 96.89),
+ ( 32960, 0.0440, 362.56),
+ ( 45752, 0.0660, 893.90),
+ ( 57824, 0.0880, 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.0110, 0.00),
+ ( 41766, 0.0220, 193.80),
+ ( 65920, 0.0440, 725.06),
+ ( 91506, 0.0660, 1787.84),
+ ( 115648, 0.0880, 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)),
- },
+ 'weekly': (
+ ( 169, 0.0110, 0.00),
+ ( 402, 0.0220, 1.86),
+ ( 634, 0.0440, 6.99),
+ ( 880, 0.0660, 17.20),
+ ( 1112, 0.0880, 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.0110, 0.00),
+ ( 804, 0.0220, 3.72),
+ ( 1268, 0.0440, 13.97),
+ ( 1760, 0.0660, 34.39),
+ ( 2224, 0.0880, 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.0110, 0.00),
+ ( 870, 0.0220, 4.04),
+ ( 1373, 0.0440, 15.11),
+ ( 1906, 0.0660, 37.24),
+ ( 2409, 0.0880, 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.0110, 0.00),
+ ( 1740, 0.0220, 8.07),
+ ( 2746, 0.0440, 30.20),
+ ( 3812, 0.0660, 74.46),
+ ( 4818, 0.0880, 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.0110, 0.00),
+ ( 5221, 0.0220, 24.22),
+ ( 8240, 0.0440, 90.64),
+ ( 11438, 0.0660, 223.48),
+ ( 14456, 0.0880, 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.0110, 0.00),
+ ( 10442, 0.0220, 48.44),
+ ( 16480, 0.0440, 181.28),
+ ( 22876, 0.0660, 446.95),
+ ( 28912, 0.0880, 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.0110, 0.00),
+ ( 20883, 0.0220, 96.90),
+ ( 32960, 0.0440, 362.53),
+ ( 45753, 0.0660, 893.92),
+ ( 57824, 0.0880, 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),
+ ),
+ },
}
@@ -150,6 +611,8 @@
}
+
+
US CA California Low Income Exemption Rate
us_ca_sit_income_exemption_rate
@@ -181,6 +644,8 @@
}
+
+
US CA California Estimated Deduction Rate
us_ca_sit_estimated_deduction_rate
@@ -212,6 +677,8 @@
}
+
+
US CA California Standard Deduction Rate
us_ca_sit_standard_deduction_rate
@@ -243,6 +710,8 @@
}
+
+
US CA California Exemption Allowance Rate
us_ca_sit_exemption_allowance_rate
diff --git a/l10n_us_hr_payroll/data/state/ct_connecticut.xml b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
index 800ba3e3..d74dc140 100644
--- a/l10n_us_hr_payroll/data/state/ct_connecticut.xml
+++ b/l10n_us_hr_payroll/data/state/ct_connecticut.xml
@@ -85,6 +85,8 @@
}
+
+
US CT Connecticut SIT Initial Tax Rate
us_ct_sit_initial_tax_rate
@@ -213,6 +215,8 @@
}
+
+
US CT Connecticut Tax Rate
us_ct_sit_tax_rate
@@ -416,6 +420,8 @@
}
+
+
US CT Connecticut Decimal Rate
us_ct_sit_decimal_rate
@@ -590,7 +596,7 @@
(530000, 3000),
(535000, 3050),
(540000, 3100),
- ( 'inf', 200),
+ ( 'inf', 3150),
],
'b': [
(320000, 0),
@@ -716,7 +722,7 @@
(530000, 3000),
(535000, 3050),
(540000, 3100),
- ( 'inf', 200),
+ ( 'inf', 3150),
],
'f': [
(200000, 0),
@@ -758,11 +764,13 @@
(530000, 3000),
(535000, 3050),
(540000, 3100),
- ( 'inf', 200),
+ ( 'inf', 3150),
],
}
+
+
US CT Connecticut Recapture Rate
us_ct_sit_recapture_rate
@@ -807,7 +815,7 @@
(530000, 3000),
(535000, 3050),
(540000, 3100),
- ( 'inf', 200),
+ ( 'inf', 3150),
],
'b': [
(320000, 0),
@@ -933,7 +941,7 @@
(530000, 3000),
(535000, 3050),
(540000, 3100),
- ( 'inf', 200),
+ ( 'inf', 3150),
],
'f': [
(200000, 0),
@@ -975,7 +983,7 @@
(530000, 3000),
(535000, 3050),
(540000, 3100),
- ( 'inf', 200),
+ ( 'inf', 3150),
],
}
@@ -1072,6 +1080,8 @@
}
+
+
US CT Connecticut Personal Exemption Rate
us_ct_sit_personal_exemption_rate
@@ -1166,6 +1176,7 @@
US Connecticut - Department of Labor (CDOL) - Unemployment Tax
+ 1
US Connecticut - Department of Labor (CDOL) - Unemployment Tax
@@ -1174,6 +1185,7 @@
US Connecticut - Department of Revenue Services (CDRS) - Income Tax
+ 1
US Connecticut - Department of Revenue Services (CDRS) - Unemployment Tax
diff --git a/l10n_us_hr_payroll/data/state/fl_florida.xml b/l10n_us_hr_payroll/data/state/fl_florida.xml
index de1cc49a..56265ef8 100644
--- a/l10n_us_hr_payroll/data/state/fl_florida.xml
+++ b/l10n_us_hr_payroll/data/state/fl_florida.xml
@@ -35,6 +35,7 @@
US Florida - Department of Revenue
+ 1
diff --git a/l10n_us_hr_payroll/data/state/ga_georgia.xml b/l10n_us_hr_payroll/data/state/ga_georgia.xml
index 2d07104a..d9365e35 100644
--- a/l10n_us_hr_payroll/data/state/ga_georgia.xml
+++ b/l10n_us_hr_payroll/data/state/ga_georgia.xml
@@ -38,53 +38,597 @@
us_ga_sit_rate
{
'married filing joint, both spouses working': {
- 'weekly': ((9.50, 0.00, 1.00), (29.00, .10, 2.00), (48.00, .48, 3.00), (67.50, 1.06, 4.00), (96.00, 1.83, 5.00), ('inf', 3.27, 5.75)),
- 'bi-weekly': ((19.00, 0.00, 1.00), (57.50, .19, 2.00), (96.00, .96, 3.00), (135.00, 2.12, 4.00), (192.00, 3.65, 5.00), ('inf', 6.54, 5.75)),
- 'semi-monthly': ((21.00, 0.00, 1.00), (62.50, .21, 2.00), (104.00, 1.04, 3.00), (146.00, 2.29, 4.00), (208.00, 3.96, 5.00), ('inf', 7.08, 5.75)),
- 'monthly': ((41.50, 0.00, 1.00), (125.50, .42, 2.00), (208.00, 2.08, 3.00), (292.00, 4.58, 4.00), (417.00, 7.92, 5.00), ('inf', 14.17, 5.75)),
- 'quarterly': ((125.00, 0.00, 1.00), (375.00, 1.25, 2.00), (625.00, 6.25, 3.00), (875.00, 13.75, 4.00), (1250.00, 23.75, 5.00), ('inf', 42.50, 5.75)),
- 'semi-annual': ((250.00, 0.00, 1.00), (750.00, 2.50, 2.00), (1250.00, 12.50, 3.00), (1750.00, 27.50, 4.00), (2500.00, 47.50, 5.00), ('inf', 85.00, 5.75)),
- 'annual': ((500.00, 0.00, 1.00), (1500.00, 5.00, 2.00), (2500.00, 25.00, 3.00), (3500.00, 55.00, 4.00), (5000.00, 95.00, 5.00), ('inf', 170.00, 5.75)),
- },
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },},
'married filing joint, one spouse working': {
- 'weekly': ((19.00, 0.00, 1.00), (57.50, .19, 2.00), (96.00, .96, 3.00), (135.00, 2.12, 4.00), (192.50, 3.65, 5.00), ('inf', 6.54, 5.75)),
- 'bi-weekly': ((38.50, 0.00, 1.00), (115.00, .38, 2.00), (192.00, 1.92, 3.00), (269.00, 4.23, 4.00), (385.00, 7.31, 5.00), ('inf', 13.08, 5.75)),
- 'semi-monthly': ((41.50, 0.00, 1.00), (125.00, .42, 2.00), (208.00, 2.08, 3.00), (292.00, 4.58, 4.00), (417.00, 7.92, 5.00), ('inf', 14.17, 5.75)),
- 'monthly': ((83.00, 0.00, 1.00), (250.00, .83, 2.00), (417.00, 4.17, 3.00), (583.00, 9.17, 4.00), (833.00, 15.83, 5.00), ('inf', 28.33, 5.75)),
- 'quarterly': ((250.00, 0.00, 1.00), (750.00, 2.50, 2.00), (1250.00, 12.50, 3.00), (1750.00, 27.50, 4.00), (2500.00, 47.50, 5.00), ('inf', 85.00, 5.75)),
- 'semi-annual': ((500.00, 0.00, 1.00), (1500.00, 5.00, 2.00), (2500.00, 25.00, 3.00), (3500.00, 55.00, 4.00), (5000.00, 95.00, 5.00), ('inf', 170.00, 5.75)),
- 'annual': ((1000.00, 0.00, 1.00), (3000.00, 10.00, 2.00), (5000.00, 50.00, 3.00), (7000.00, 110.00, 4.00), (10000.00, 190.00, 5.00), ('inf', 340.00, 5.75)),
- },
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
'single': {
- 'weekly': ((14.50, 0.00, 1.00), (43.50, .14, 2.00), (72.00, .72, 3.00), (101.00, 1.59, 4.00), (135.00, 2.74, 5.00), ('inf', 4.42, 5.75)),
- 'bi-weekly': ((29.00, 0.00, 1.00), (86.50, .29, 2.00), (144.00, 1.44, 3.00), (202.00, 3.17, 4.00), (269.00, 5.48, 5.00), ('inf', 8.85, 5.75)),
- 'semi-monthly': ((31.00, 0.00, 1.00), (93.50, .31, 2.00), (156.00, 1.56, 3.00), (219.00, 3.34, 4.00), (292.00, 5.94, 5.00), ('inf', 9.58, 5.75)),
- 'monthly': ((62.50, 0.00, 1.00), (187.00, .62, 2.00), (312.00, 3.12, 3.00), (437.00, 6.87, 4.00), (583.00, 11.87, 5.00), ('inf', 19.17, 5.75)),
- 'quarterly': ((187.50, 0.00, 1.00), (562.50, 1.88, 2.00), (937.50, 9.38, 3.00), (1312.00, 20.63, 4.00), (1750.00, 35.63, 5.00), ('inf', 57.50, 5.75)),
- 'semi-annual': ((375.00, 0.00, 1.00), (1125.00, 3.75, 2.00), (1875.00, 18.75, 3.00), (2625.00, 41.25, 4.00), (3500.00, 71.25, 5.00), ('inf', 115.00, 5.75)),
- 'annual': ((750.00, 0.00, 1.00), (2250.00, 7.50, 2.00), (3750.00, 37.50, 3.00), (5250.00, 82.50, 4.00), (7000.00, 142.50, 5.00), ('inf', 230.00, 5.75)),
- },
+ 'weekly': (
+ ( 14.50, 0.00, 1.00),
+ ( 43.50, 0.14, 2.00),
+ ( 72.00, 0.72, 3.00),
+ (101.00, 1.59, 4.00),
+ (135.00, 2.74, 5.00),
+ ( 'inf', 4.42, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 29.00, 0.00, 1.00),
+ ( 86.50, 0.29, 2.00),
+ (144.00, 1.44, 3.00),
+ (202.00, 3.17, 4.00),
+ (269.00, 5.48, 5.00),
+ ( 'inf', 8.85, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 31.00, 0.00, 1.00),
+ ( 93.50, 0.31, 2.00),
+ (156.00, 1.56, 3.00),
+ (219.00, 3.34, 4.00),
+ (292.00, 5.94, 5.00),
+ ( 'inf', 9.58, 5.75),
+ ),
+ 'monthly': (
+ ( 62.50, 0.00, 1.00),
+ (187.00, 0.62, 2.00),
+ (312.00, 3.12, 3.00),
+ (437.00, 6.87, 4.00),
+ (583.00, 11.87, 5.00),
+ ( 'inf', 19.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 187.50, 0.00, 1.00),
+ ( 562.50, 1.88, 2.00),
+ ( 937.50, 9.38, 3.00),
+ (1312.00, 20.63, 4.00),
+ (1750.00, 35.63, 5.00),
+ ( 'inf', 57.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 375.00, 0.00, 1.00),
+ (1125.00, 3.75, 2.00),
+ (1875.00, 18.75, 3.00),
+ (2625.00, 41.25, 4.00),
+ (3500.00, 71.25, 5.00),
+ ( 'inf', 115.00, 5.75),
+ ),
+ 'annual': (
+ ( 750.00, 0.00, 1.00),
+ (2250.00, 7.50, 2.00),
+ (3750.00, 37.50, 3.00),
+ (5250.00, 82.50, 4.00),
+ (7000.00, 142.50, 5.00),
+ ( 'inf', 230.00, 5.75),
+ ),
+ },
'head of household': {
- 'weekly': ((19.00, 0.00, 1.00), (57.50, .19, 2.00), (96.00, .96, 3.00), (135.00, 2.12, 4.00), (192.50, 3.65, 5.00), ('inf', 6.54, 5.75)),
- 'bi-weekly': ((38.50, 0.00, 1.00), (115.00, .38, 2.00), (192.00, 1.92, 3.00), (269.00, 4.23, 4.00), (385.00, 7.31, 5.00), ('inf', 13.08, 5.75)),
- 'semi-monthly': ((41.50, 0.00, 1.00), (125.00, .42, 2.00), (208.00, 2.08, 3.00), (292.00, 4.58, 4.00), (417.00, 7.92, 5.00), ('inf', 14.17, 5.75)),
- 'monthly': ((83.00, 0.00, 1.00), (250.00, .83, 2.00), (417.00, 4.17, 3.00), (583.00, 9.17, 4.00), (833.00, 15.83, 5.00), ('inf', 28.33, 5.75)),
- 'quarterly': ((250.00, 0.00, 1.00), (750.00, 2.50, 2.00), (1250.00, 12.50, 3.00), (1750.00, 27.50, 4.00), (2500.00, 47.50, 5.00), ('inf', 85.00, 5.75)),
- 'semi-annual': ((500.00, 0.00, 1.00), (1500.00, 5.00, 2.00), (2500.00, 25.00, 3.00), (3500.00, 55.00, 4.00), (5000.00, 95.00, 5.00), ('inf', 170.00, 5.75)),
- 'annual': ((1000.00, 0.00, 1.00), (3000.00, 10.00, 2.00), (5000.00, 50.00, 3.00), (7000.00, 110.00, 4.00), (10000.00, 190.00, 5.00), ('inf', 340.00, 5.75)),
- },
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
'married filing separate': {
- 'weekly': ((9.50, 0.00, 1.00), (29.00, .10, 2.00), (48.00, .48, 3.00), (67.50, 1.06, 4.00), (96.00, 1.83, 5.00), ('inf', 3.27, 5.75)),
- 'bi-weekly': ((19.00, 0.00, 1.00), (57.50, .19, 2.00), (96.00, .96, 3.00), (135.00, 2.12, 4.00), (192.00, 3.65, 5.00), ('inf', 6.54, 5.75)),
- 'semi-monthly': ((21.00, 0.00, 1.00), (62.50, .21, 2.00), (104.00, 1.04, 3.00), (146.00, 2.29, 4.00), (208.00, 3.96, 5.00), ('inf', 7.08, 5.75)),
- 'monthly': ((41.50, 0.00, 1.00), (125.50, .42, 2.00), (208.00, 2.08, 3.00), (292.00, 4.58, 4.00), (417.00, 7.92, 5.00), ('inf', 14.17, 5.75)),
- 'quarterly': ((125.00, 0.00, 1.00), (375.00, 1.25, 2.00), (625.00, 6.25, 3.00), (875.00, 13.75, 4.00), (1250.00, 23.75, 5.00), ('inf', 42.50, 5.75)),
- 'semi-annual': ((250.00, 0.00, 1.00), (750.00, 2.50, 2.00), (1250.00, 12.50, 3.00), (1750.00, 27.50, 4.00), (2500.00, 47.50, 5.00), ('inf', 85.00, 5.75)),
- 'annual': ((500.00, 0.00, 1.00), (1500.00, 5.00, 2.00), (2500.00, 25.00, 3.00), (3500.00, 55.00, 4.00), (5000.00, 95.00, 5.00), ('inf', 170.00, 5.75)),
- },
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },
}
+
+
+
+ US GA Georgia SIT Rate Table
+ us_ga_sit_rate
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
+ 'single': {
+ 'weekly': (
+ ( 14.50, 0.00, 1.00),
+ ( 43.50, 0.14, 2.00),
+ ( 72.00, 0.72, 3.00),
+ (101.00, 1.59, 4.00),
+ (135.00, 2.74, 5.00),
+ ( 'inf', 4.42, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 29.00, 0.00, 1.00),
+ ( 86.50, 0.29, 2.00),
+ (144.00, 1.44, 3.00),
+ (202.00, 3.17, 4.00),
+ (269.00, 5.48, 5.00),
+ ( 'inf', 8.85, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 31.00, 0.00, 1.00),
+ ( 93.50, 0.31, 2.00),
+ (156.00, 1.56, 3.00),
+ (219.00, 3.34, 4.00),
+ (292.00, 5.94, 5.00),
+ ( 'inf', 9.58, 5.75),
+ ),
+ 'monthly': (
+ ( 62.50, 0.00, 1.00),
+ (187.00, 0.62, 2.00),
+ (312.00, 3.12, 3.00),
+ (437.00, 6.87, 4.00),
+ (583.00, 11.87, 5.00),
+ ( 'inf', 19.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 187.50, 0.00, 1.00),
+ ( 562.50, 1.88, 2.00),
+ ( 937.50, 9.38, 3.00),
+ (1312.00, 20.63, 4.00),
+ (1750.00, 35.63, 5.00),
+ ( 'inf', 57.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 375.00, 0.00, 1.00),
+ (1125.00, 3.75, 2.00),
+ (1875.00, 18.75, 3.00),
+ (2625.00, 41.25, 4.00),
+ (3500.00, 71.25, 5.00),
+ ( 'inf', 115.00, 5.75),
+ ),
+ 'annual': (
+ ( 750.00, 0.00, 1.00),
+ (2250.00, 7.50, 2.00),
+ (3750.00, 37.50, 3.00),
+ (5250.00, 82.50, 4.00),
+ (7000.00, 142.50, 5.00),
+ ( 'inf', 230.00, 5.75),
+ ),
+ },
+ 'head of household': {
+ 'weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.50, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 38.50, 0.00, 1.00),
+ (115.00, 0.38, 2.00),
+ (192.00, 1.92, 3.00),
+ (269.00, 4.23, 4.00),
+ (385.00, 7.31, 5.00),
+ ( 'inf', 13.08, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.00, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'monthly': (
+ ( 83.00, 0.00, 1.00),
+ (250.00, 0.83, 2.00),
+ (417.00, 4.17, 3.00),
+ (583.00, 9.17, 4.00),
+ (833.00, 15.83, 5.00),
+ ( 'inf', 28.33, 5.75),
+ ),
+ 'quarterly': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'semi-annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ 'annual': (
+ ( 1000.00, 0.00, 1.00),
+ ( 3000.00, 10.00, 2.00),
+ ( 5000.00, 50.00, 3.00),
+ ( 7000.00, 110.00, 4.00),
+ (10000.00, 190.00, 5.00),
+ ( 'inf', 340.00, 5.75),
+ ),
+ },
+ 'married filing separate': {
+ 'weekly': (
+ ( 9.50, 0.00, 1.00),
+ (29.00, 0.10, 2.00),
+ (48.00, 0.48, 3.00),
+ (67.50, 1.06, 4.00),
+ (96.00, 1.83, 5.00),
+ ('inf', 3.27, 5.75),
+ ),
+ 'bi-weekly': (
+ ( 19.00, 0.00, 1.00),
+ ( 57.50, 0.19, 2.00),
+ ( 96.00, 0.96, 3.00),
+ (135.00, 2.12, 4.00),
+ (192.00, 3.65, 5.00),
+ ( 'inf', 6.54, 5.75),
+ ),
+ 'semi-monthly': (
+ ( 21.00, 0.00, 1.00),
+ ( 62.50, 0.21, 2.00),
+ (104.00, 1.04, 3.00),
+ (146.00, 2.29, 4.00),
+ (208.00, 3.96, 5.00),
+ ( 'inf', 7.08, 5.75),
+ ),
+ 'monthly': (
+ ( 41.50, 0.00, 1.00),
+ (125.50, 0.42, 2.00),
+ (208.00, 2.08, 3.00),
+ (292.00, 4.58, 4.00),
+ (417.00, 7.92, 5.00),
+ ( 'inf', 14.17, 5.75),
+ ),
+ 'quarterly': (
+ ( 125.00, 0.00, 1.00),
+ ( 375.00, 1.25, 2.00),
+ ( 625.00, 6.25, 3.00),
+ ( 875.00, 13.75, 4.00),
+ (1250.00, 23.75, 5.00),
+ ( 'inf', 42.50, 5.75),
+ ),
+ 'semi-annual': (
+ ( 250.00, 0.00, 1.00),
+ ( 750.00, 2.50, 2.00),
+ (1250.00, 12.50, 3.00),
+ (1750.00, 27.50, 4.00),
+ (2500.00, 47.50, 5.00),
+ ( 'inf', 85.00, 5.75),
+ ),
+ 'annual': (
+ ( 500.00, 0.00, 1.00),
+ (1500.00, 5.00, 2.00),
+ (2500.00, 25.00, 3.00),
+ (3500.00, 55.00, 4.00),
+ (5000.00, 95.00, 5.00),
+ ( 'inf', 170.00, 5.75),
+ ),
+ },
+ }
+
+
@@ -140,6 +684,60 @@
}
+
+
+
+ US GA Georgia SIT Personal Allowance
+ us_ga_sit_personal_allowance
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': 142.30,
+ 'bi-weekly': 284.62,
+ 'semi-monthly': 308.33,
+ 'monthly': 616.67,
+ 'quarterly': 1850.00,
+ 'semi-annual': 3700.00,
+ 'annual': 7400.00,
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': 142.30,
+ 'bi-weekly': 284.62,
+ 'semi-monthly': 308.33,
+ 'monthly': 616.67,
+ 'quarterly': 1850.00,
+ 'semi-annual': 3700.00,
+ 'annual': 7400.00,
+ },
+ 'single': {
+ 'weekly': 51.92,
+ 'bi-weekly': 103.85,
+ 'semi-monthly': 112.50,
+ 'monthly': 225.00,
+ 'quarterly': 675.00,
+ 'semi-annual': 1350.00,
+ 'annual': 2700.00,
+ },
+ 'head of household': {
+ 'weekly': 51.92,
+ 'bi-weekly': 103.85,
+ 'semi-monthly': 112.50,
+ 'monthly': 225.00,
+ 'quarterly': 675.00,
+ 'semi-annual': 1350.00,
+ 'annual': 2700.00,
+ },
+ 'married filing separate': {
+ 'weekly': 71.15,
+ 'bi-weekly': 142.30,
+ 'semi-monthly': 154.16,
+ 'monthly': 308.33,
+ 'quarterly': 925.00,
+ 'semi-annual': 1850.00,
+ 'annual': 3700.00,
+ },
+ }
+
+
@@ -157,6 +755,22 @@
}
+
+
+
+ US GA Georgia SIT Dependent Allowance Rate
+ us_ga_sit_dependent_allowance_rate
+ {
+ 'weekly': 57.50,
+ 'bi-weekly': 115.00,
+ 'semi-monthly': 125.00,
+ 'monthly': 250.00,
+ 'quarterly': 750.00,
+ 'semi-annual': 1500.00,
+ 'annual': 3000.00,
+ }
+
+
@@ -212,11 +826,66 @@
}
+
+
+
+ US GA Georgia SIT Deduction
+ us_ga_sit_deduction
+ {
+ 'married filing joint, both spouses working': {
+ 'weekly': 115.50,
+ 'bi-weekly': 230.75,
+ 'semi-monthly': 250.00,
+ 'monthly': 500.00,
+ 'quarterly': 1500.00,
+ 'semi-annual': 3000.00,
+ 'annual': 6000.00,
+ },
+ 'married filing joint, one spouse working': {
+ 'weekly': 115.50,
+ 'bi-weekly': 230.75,
+ 'semi-monthly': 250.00,
+ 'monthly': 500.00,
+ 'quarterly': 1500.00,
+ 'semi-annual': 3000.00,
+ 'annual': 6000.00,
+ },
+ 'single': {
+ 'weekly': 88.50,
+ 'bi-weekly': 177.00,
+ 'semi-monthly': 191.75,
+ 'monthly': 383.50,
+ 'quarterly': 1150.00,
+ 'semi-annual': 2300.00,
+ 'annual': 4600.00,
+ },
+ 'head of household': {
+ 'weekly': 88.50,
+ 'bi-weekly': 177.00,
+ 'semi-monthly': 191.75,
+ 'monthly': 383.50,
+ 'quarterly': 1150.00,
+ 'semi-annual': 2300.00,
+ 'annual': 4600.00,
+ },
+ 'married filing separate': {
+ 'weekly': 57.75,
+ 'bi-weekly': 115.50,
+ 'semi-monthly': 125.00,
+ 'monthly': 250.00,
+ 'quarterly': 750.00,
+ 'semi-annual': 1500.00,
+ 'annual': 3000.00,
+ },
+ }
+
+
US Georgia - Department of Taxation - Unemployment Tax
+ 1
@@ -226,6 +895,7 @@
US Georgia - Department of Taxation - Income Tax
+ 1
diff --git a/l10n_us_hr_payroll/data/state/hi_hawaii.xml b/l10n_us_hr_payroll/data/state/hi_hawaii.xml
index 1cbd4721..e4c07ab2 100644
--- a/l10n_us_hr_payroll/data/state/hi_hawaii.xml
+++ b/l10n_us_hr_payroll/data/state/hi_hawaii.xml
@@ -37,19 +37,75 @@
US HI Hawaii SIT Tax Rate
us_hi_sit_tax_rate
{
- 'single': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)),
- 'married': ((4800, 0.00, 1.40), (9600, 67.00, 3.20), (19200, 221.00, 5.50), (28800, 749.00, 6.40), (38400, 1363.00, 6.80), (48000, 2016.00, 7.20), (72000, 2707.00, 7.60), ('inf', 4531.00, 7.90)),
- 'head_of_household': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)),
+ 'single': (
+ ( 2400, 0.00, 1.40),
+ ( 4800, 34.00, 3.20),
+ ( 9600, 110.00, 5.50),
+ (14400, 374.00, 6.40),
+ (19200, 682.00, 6.80),
+ (24000, 1008.00, 7.20),
+ (36000, 1354.00, 7.60),
+ ('inf', 2266.00, 7.90),
+ ),
+ 'married': (
+ ( 4800, 0.00, 1.40),
+ ( 9600, 67.00, 3.20),
+ (19200, 221.00, 5.50),
+ (28800, 749.00, 6.40),
+ (38400, 1363.00, 6.80),
+ (48000, 2016.00, 7.20),
+ (72000, 2707.00, 7.60),
+ ('inf', 4531.00, 7.90),
+ ),
+ 'head_of_household': (
+ ( 2400, 0.00, 1.40),
+ ( 4800, 34.00, 3.20),
+ ( 9600, 110.00, 5.50),
+ (14400, 374.00, 6.40),
+ (19200, 682.00, 6.80),
+ (24000, 1008.00, 7.20),
+ (36000, 1354.00, 7.60),
+ ('inf', 2266.00, 7.90),
+ ),
}
+
+
US HI Hawaii SIT Tax Rate
us_hi_sit_tax_rate
{
- 'single': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)),
- 'married': ((4800, 0.00, 1.40), (9600, 67.00, 3.20), (19200, 221.00, 5.50), (28800, 749.00, 6.40), (38400, 1363.00, 6.80), (48000, 2016.00, 7.20), (72000, 2707.00, 7.60), ('inf', 4531.00, 7.90)),
- 'head_of_household': ((2400, 0.00, 1.40), (4800, 34.00, 3.20), (9600, 110.00, 5.50), (14400, 374.00, 6.40), (19200, 682.00, 6.80), (24000, 1008.00, 7.20), (36000, 1354.00, 7.60), ('inf', 2266.00, 7.90)),
+ 'single': (
+ ( 2400, 0.00, 1.40),
+ ( 4800, 34.00, 3.20),
+ ( 9600, 110.00, 5.50),
+ (14400, 374.00, 6.40),
+ (19200, 682.00, 6.80),
+ (24000, 1008.00, 7.20),
+ (36000, 1354.00, 7.60),
+ ('inf', 2266.00, 7.90),
+ ),
+ 'married': (
+ ( 4800, 0.00, 1.40),
+ ( 9600, 67.00, 3.20),
+ (19200, 221.00, 5.50),
+ (28800, 749.00, 6.40),
+ (38400, 1363.00, 6.80),
+ (48000, 2016.00, 7.20),
+ (72000, 2707.00, 7.60),
+ ('inf', 4531.00, 7.90),
+ ),
+ 'head_of_household': (
+ ( 2400, 0.00, 1.40),
+ ( 4800, 34.00, 3.20),
+ ( 9600, 110.00, 5.50),
+ (14400, 374.00, 6.40),
+ (19200, 682.00, 6.80),
+ (24000, 1008.00, 7.20),
+ (36000, 1354.00, 7.60),
+ ('inf', 2266.00, 7.90),
+ ),
}
diff --git a/l10n_us_hr_payroll/data/state/ia_iowa.xml b/l10n_us_hr_payroll/data/state/ia_iowa.xml
index 27e0bcd3..89e9e036 100644
--- a/l10n_us_hr_payroll/data/state/ia_iowa.xml
+++ b/l10n_us_hr_payroll/data/state/ia_iowa.xml
@@ -37,25 +37,148 @@
US IA Iowa SIT Tax Rate
us_ia_sit_tax_rate
{
- 'daily': [(5.13, 0.0033, 0.0), (10.25, 0.0067, 0.02), (20.50, 0.0225, 0.05), (46.13, 0.0414, 0.28), (76.89, 0.0563, 1.34), (102.52, 0.0596, 3.07), (153.78, 0.0625, 4.60), (230.68, 0.0744, 7.80), ('inf', 0.0853, 13.52)],
- 'weekly': [(25.63, 0.0033, 0.0), (51.27, 0.0067, 0.08), (102.52, 0.0225, 0.025), (230.67, 0.0414, 1.40), (384.46, 0.0563, 6.71), (512.62, 0.0596, 15.37), (768.92, 0.0625, 23.01), (1153.38, 0.0744, 39.03), ('inf', 0.0853, 67.63)],
- 'bi-weekly': [(51.27, 0.0033, 0.00), (102.54, 0.0067, 0.17), (205.04, 0.00225, 0.51), (461.35, 0.0414, 2.82), (768.92, 0.0563, 13.43), (1025.23, 0.0596, 30.75), (1537.85, 0.0625, 46.03), (2306.77, 0.0744, 78.07), ('inf', 0.0853, 135.28)],
- 'semi-monthly': [(55.54, 0.0033, 0.00), (111.08, 0.0067, 0.18), (222.13, 0.0225, 0.55), (499.79, 0.0414, 3.05), (833.00, 0.0563, 14.59), (1110.67, 0.0596, 33.31), (1666.00, 0.0625, 49.86), (2499.00, 0.0744, 84.57), ('inf', 0.0853, 146.55)],
- 'monthly': [(111.08, 0.0033, 0.00), (222.17, 0.0067, 0.37), (444.25, 0.0225, 1.11), (999.58, 0.0414, 6.11), (1666.00, 0.0563, 29.10), (2221.33, 0.0596, 62.66), (3332.00, 0.0625, 99.72), (4998.00, 0.0744, 169.14), ('inf', 0.0853, 293.09)],
- 'annual': [(1333.00, 0.0033, 0.00), (2666.00, 0.0067, 4.40), (5331.00, 0.0225, 13.33), (11995.00, 0.0414, 73.29), (19992.00, 0.0563, 349.19), (26656.00, 0.0596, 799.41), (39984.00, 0.0625, 1196.58), (59976.00, 0.0744, 2029.58), ('inf', 0.0853, 3516.98)],
+ 'daily': (
+ ( 5.13, 0.0033, 0.00),
+ ( 10.25, 0.0067, 0.02),
+ ( 20.50, 0.0225, 0.05),
+ ( 46.13, 0.0414, 0.28),
+ ( 76.89, 0.0563, 1.34),
+ (102.52, 0.0596, 3.07),
+ (153.78, 0.0625, 4.60),
+ (230.68, 0.0744, 7.80),
+ ( 'inf', 0.0853, 13.52),
+ ),
+ 'weekly': (
+ ( 25.63, 0.0033, 0.00),
+ ( 51.27, 0.0067, 0.08),
+ ( 102.52, 0.0225, 0.25),
+ ( 230.67, 0.0414, 1.40),
+ ( 384.46, 0.0563, 6.71),
+ ( 512.62, 0.0596, 15.37),
+ ( 768.92, 0.0625, 23.01),
+ (1153.38, 0.0744, 39.03),
+ ( 'inf', 0.0853, 67.63),
+ ),
+ 'bi-weekly': (
+ ( 51.27, 0.0033, 0.00),
+ ( 102.54, 0.0067, 0.17),
+ ( 205.04, 0.0225, 0.51),
+ ( 461.35, 0.0414, 2.82),
+ ( 768.92, 0.0563, 13.43),
+ (1025.23, 0.0596, 30.75),
+ (1537.85, 0.0625, 46.03),
+ (2306.77, 0.0744, 78.07),
+ ( 'inf', 0.0853, 135.28),
+ ),
+ 'semi-monthly': (
+ ( 55.54, 0.0033, 0.00),
+ ( 111.08, 0.0067, 0.18),
+ ( 222.13, 0.0225, 0.55),
+ ( 499.79, 0.0414, 3.05),
+ ( 833.00, 0.0563, 14.59),
+ (1110.67, 0.0596, 33.31),
+ (1666.00, 0.0625, 49.86),
+ (2499.00, 0.0744, 84.57),
+ ( 'inf', 0.0853, 146.55),
+ ),
+ 'monthly': (
+ ( 111.08, 0.0033, 0.00),
+ ( 222.17, 0.0067, 0.37),
+ ( 444.25, 0.0225, 1.11),
+ ( 999.58, 0.0414, 6.11),
+ (1666.00, 0.0563, 29.10),
+ (2221.33, 0.0596, 62.66),
+ (3332.00, 0.0625, 99.72),
+ (4998.00, 0.0744, 169.14),
+ ( 'inf', 0.0853, 293.09),
+ ),
+ 'annual': (
+ ( 1333.00, 0.0033, 0.00),
+ ( 2666.00, 0.0067, 4.40),
+ ( 5331.00, 0.0225, 13.33),
+ (11995.00, 0.0414, 73.29),
+ (19992.00, 0.0563, 349.19),
+ (26656.00, 0.0596, 799.41),
+ (39984.00, 0.0625, 1196.58),
+ (59976.00, 0.0744, 2029.58),
+ ( 'inf', 0.0853, 3516.98),
+ ),
}
+
+
+
US IA Iowa SIT Tax Rate
us_ia_sit_tax_rate
{
- 'daily': [(5.69, 0.0033, 0.0), (11.38, 0.0067, 0.02), (22.76, 0.0225, 0.06), (51.22, 0.0414, 0.32), (85.36, 0.0563, 1.50), (113.81, 0.0596, 3.42), (170.71, 0.0625, 5.12), (256.07, 0.0744, 8.68), ('inf', 0.0853, 15.03)],
- 'weekly': [(28.46, 0.0033, 0.0), (56.90, 0.0067, 0.09), (113.81, 0.0225, 0.028), (256.08, 0.0414, 1.56), (426.79, 0.0563, 7.45), (569.04, 0.0596, 17.06), (853.56, 0.0625, 25.54), (1280.35, 0.0744, 43.32), ('inf', 0.0853, 75.07)],
- 'bi-weekly': [(56.92, 0.0033, 0.00), (113.81, 0.0067, 0.19), (227.62, 0.00225, 0.57), (512.15, 0.0414, 3.13), (853.58, 0.0563, 14.91), (1138.08, 0.0596, 34.13), (1707.12, 0.0625, 51.09), (2560.69, 0.0744, 86.66), ('inf', 0.0853, 150.17)],
- 'semi-monthly': [(61.67, 0.0033, 0.00), (123.29, 0.0067, 0.20), (246.58, 0.0225, 0.61), (554.83, 0.0414, 3.38), (924.71, 0.0563, 16.14), (1232.92, 0.0596, 36.96), (1849.38, 0.0625, 55.33), (2774.08, 0.0744, 93.86), ('inf', 0.0853, 162.66)],
- 'monthly': [(123.33, 0.0033, 0.00), (246.58, 0.0067, 0.41), (493.17, 0.0225, 1.24), (1109.67, 0.0414, 6.79), (1849.42, 0.0563, 32.31), (2465.83, 0.0596, 73.96), (3698.75, 0.0625, 110.70), (5548.17, 0.0744, 187.76), ('inf', 0.0853, 325.36)],
- 'annual': [(1480.00, 0.0033, 0.00), (2959.00, 0.0067, 4.88), (5918.00, 0.0225, 14.79), (13316.00, 0.0414, 81.37), (22193.00, 0.0563, 387.65), (29590.00, 0.0596, 887.43), (44385.00, 0.0625, 1328.29), (66578.00, 0.0744, 2252.98), ('inf', 0.0853, 3904.14)],
+ 'daily': (
+ ( 5.69, 0.0033, 0.00),
+ ( 11.38, 0.0067, 0.02),
+ ( 22.76, 0.0225, 0.06),
+ ( 51.22, 0.0414, 0.32),
+ ( 85.36, 0.0563, 1.50),
+ (113.81, 0.0596, 3.42),
+ (170.71, 0.0625, 5.12),
+ (256.07, 0.0744, 8.68),
+ ( 'inf', 0.0853, 15.03),
+ ),
+ 'weekly': (
+ ( 28.46, 0.0033, 0.00),
+ ( 56.90, 0.0067, 0.09),
+ ( 113.81, 0.0225, 0.28),
+ ( 256.08, 0.0414, 1.56),
+ ( 426.79, 0.0563, 7.45),
+ ( 569.04, 0.0596, 17.06),
+ ( 853.56, 0.0625, 25.54),
+ (1280.35, 0.0744, 43.32),
+ ( 'inf', 0.0853, 75.07),
+ ),
+ 'bi-weekly': (
+ ( 56.92, 0.0033, 0.00),
+ ( 113.81, 0.0067, 0.19),
+ ( 227.62, 0.0225, 0.57),
+ ( 512.15, 0.0414, 3.13),
+ ( 853.58, 0.0563, 14.91),
+ (1138.08, 0.0596, 34.13),
+ (1707.12, 0.0625, 51.09),
+ (2560.69, 0.0744, 86.66),
+ ( 'inf', 0.0853, 150.17),
+ ),
+ 'semi-monthly': (
+ ( 61.67, 0.0033, 0.00),
+ ( 23.29, 0.0067, 0.20),
+ ( 246.58, 0.0225, 0.61),
+ ( 554.83, 0.0414, 3.38),
+ ( 924.71, 0.0563, 16.14),
+ (1232.92, 0.0596, 36.96),
+ (1849.38, 0.0625, 55.33),
+ (2774.08, 0.0744, 93.86),
+ ( 'inf', 0.0853, 162.66),
+ ),
+ 'monthly': (
+ ( 123.33, 0.0033, 0.00),
+ ( 246.58, 0.0067, 0.41),
+ ( 493.17, 0.0225, 1.24),
+ (1109.67, 0.0414, 6.79),
+ (1849.42, 0.0563, 32.31),
+ (2465.83, 0.0596, 73.96),
+ (3698.75, 0.0625, 110.70),
+ (5548.17, 0.0744, 187.76),
+ ( 'inf', 0.0853, 325.36),
+ ),
+ 'annual': (
+ ( 1480.00, 0.0033, 0.00),
+ ( 2959.00, 0.0067, 4.88),
+ ( 5918.00, 0.0225, 14.79),
+ (13316.00, 0.0414, 81.37),
+ (22193.00, 0.0563, 387.65),
+ (29590.00, 0.0596, 887.43),
+ (44385.00, 0.0625, 1328.29),
+ (66578.00, 0.0744, 2252.98),
+ ( 'inf', 0.0853, 3904.14),
+ ),
}
@@ -75,6 +198,8 @@
}
+
+
US IA Iowa Standard Deduction Rate
us_ia_sit_standard_deduction_rate
@@ -104,6 +229,8 @@
}
+
+
US IA Iowa Deduction Allowance Rate
us_ia_sit_deduction_allowance_rate
@@ -122,6 +249,7 @@
US Iowa - Workforce Development - Unemployment Tax
+ 1
US Iowa - Workforce Development - Unemployment Tax
@@ -130,6 +258,7 @@
US Iowa - Department of Revenue - Income Tax
+ 1
US Iowa - Department of Revenue - Income Tax
diff --git a/l10n_us_hr_payroll/data/state/id_idaho.xml b/l10n_us_hr_payroll/data/state/id_idaho.xml
index be1c71f8..3f572493 100644
--- a/l10n_us_hr_payroll/data/state/id_idaho.xml
+++ b/l10n_us_hr_payroll/data/state/id_idaho.xml
@@ -38,54 +38,326 @@
us_id_sit_tax_rate
{
'single': {
- 'weekly': ((235, 0.00, 0.00), (264, 0.00, 1.125), (294, 0.00, 3.125), (324, 1.00, 3.625), (353, 2.00, 4.625), (383, 4.00, 5.625), (457, 5.00, 6.625), ('inf', 10.00, 6.925)),
- 'bi-weekly': ((469, 0.00, 0.00), (529, 0.00, 1.125), (588, 1.00, 3.125), (647, 3.00, 3.625), (706, 5.00, 4.625), (766, 7.00, 5.625), (914, 11.00, 6.625), ('inf', 21.00, 6.925)),
- 'semi-monthly': ((508, 0.00, 0.00), (573, 0.00, 1.125), (637, 1.00, 3.125), (701, 3.00, 3.625), (765, 5.00, 4.625), (829, 8.00, 5.625), (990, 12.00, 6.625), ('inf', 22.00, 6.925)),
- 'monthly': ((1017, 0.00, 0.00), (1145, 0.00, 1.125), (1273, 1.00, 3.125), (1402, 5.00, 3.625), (1530, 10.00, 4.625), (1659, 16.00, 5.625), (1980, 23.00, 6.625), ('inf', 45.00, 6.925)),
- 'annually': ((12200, 0.00, 0.00), (13741, 0.00, 1.125), (15281, 17.00, 3.125), (16822, 65.00, 3.625), (18362, 121.00, 4.625), (19903, 192.00, 5.625), (23754, 279.00, 6.625), ('inf', 534.00, 6.925)),
- },
+ 'weekly': (
+ ( 235, 0.00, 0.000),
+ ( 264, 0.00, 1.125),
+ ( 294, 0.00, 3.125),
+ ( 324, 1.00, 3.625),
+ ( 353, 2.00, 4.625),
+ ( 383, 4.00, 5.625),
+ ( 457, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 469, 0.00, 0.000),
+ ( 529, 0.00, 1.125),
+ ( 588, 1.00, 3.125),
+ ( 647, 3.00, 3.625),
+ ( 706, 5.00, 4.625),
+ ( 766, 7.00, 5.625),
+ ( 914, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 508, 0.00, 0.000),
+ ( 573, 0.00, 1.125),
+ ( 637, 1.00, 3.125),
+ ( 701, 3.00, 3.625),
+ ( 765, 5.00, 4.625),
+ ( 829, 8.00, 5.625),
+ ( 990, 12.00, 6.625),
+ ('inf', 22.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1017, 0.00, 0.000),
+ ( 1145, 0.00, 1.125),
+ ( 1273, 1.00, 3.125),
+ ( 1402, 5.00, 3.625),
+ ( 1530, 10.00, 4.625),
+ ( 1659, 16.00, 5.625),
+ ( 1980, 23.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12200, 0.00, 0.000),
+ (13741, 0.00, 1.125),
+ (15281, 17.00, 3.125),
+ (16822, 65.00, 3.625),
+ (18362, 121.00, 4.625),
+ (19903, 192.00, 5.625),
+ (23754, 279.00, 6.625),
+ ('inf', 534.00, 6.925),
+ ),
+ },
'married': {
- 'weekly': ((469, 0.00, 0.00), (529, 0.00, 1.125), (588, 0.00, 3.125), (647, 1.00, 3.625), (706, 2.00, 4.625), (766, 4.00, 5.625), (914, 5.00, 6.625), ('inf', 10.00, 6.925)),
- 'bi-weekly': ((938, 0.00, 0.00), (1057, 0.00, 1.125), (1175, 1.00, 3.125), (1294, 5.00, 3.625), (1412, 9.00, 4.625), (1531, 15.00, 5.625), (1827, 21.00, 6.625), ('inf', 41.00, 6.925)),
- 'semi-monthly': ((1017, 0.00, 0.00), (1145, 0.00, 1.125), (1273, 1.00, 3.125), (1402, 5.00, 3.625), (1530, 10.00, 4.625), (1659, 16.00, 5.625), (1980, 23.00, 6.625), ('inf', 45.00, 6.925)),
- 'monthly': ((2033, 0.00, 0.00), (2290, 0.00, 1.125), (2547, 3.00, 3.125), (2804, 11.00, 3.625), (3060, 20.00, 4.625), (3317, 32.00, 5.625), (3959, 47.00, 6.625), ('inf', 89.00, 6.925)),
- 'annually': ((24400, 0.00, 0.00), (27482, 0.00, 1.125), (30562, 35.00, 3.125), (33644, 131.00, 3.625), (36724, 243.00, 4.625), (39806, 385.00, 5.625), (47508, 558.00, 6.625), ('inf', 1068.00, 6.925)),
- },
+ 'weekly': (
+ ( 469, 0.00, 0.000),
+ ( 529, 0.00, 1.125),
+ ( 588, 0.00, 3.125),
+ ( 647, 1.00, 3.625),
+ ( 706, 2.00, 4.625),
+ ( 766, 4.00, 5.625),
+ ( 914, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 938, 0.00, 0.000),
+ ( 1057, 0.00, 1.125),
+ ( 1175, 1.00, 3.125),
+ ( 1294, 5.00, 3.625),
+ ( 1412, 9.00, 4.625),
+ ( 1531, 15.00, 5.625),
+ ( 1827, 21.00, 6.625),
+ ('inf', 41.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 1017, 0.00, 0.000),
+ ( 1145, 0.00, 1.125),
+ ( 1273, 1.00, 3.125),
+ ( 1402, 5.00, 3.625),
+ ( 1530, 10.00, 4.625),
+ ( 1659, 16.00, 5.625),
+ ( 1980, 23.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'monthly': (
+ ( 2033, 0.00, 0.000),
+ ( 2290, 0.00, 1.125),
+ ( 2547, 3.00, 3.125),
+ ( 2804, 11.00, 3.625),
+ ( 3060, 20.00, 4.625),
+ ( 3317, 32.00, 5.625),
+ ( 3959, 47.00, 6.625),
+ ('inf', 89.00, 6.925),
+ ),
+ 'annually': (
+ (24400, 0.00, 0.000),
+ (27482, 0.00, 1.125),
+ (30562, 35.00, 3.125),
+ (33644, 131.00, 3.625),
+ (36724, 243.00, 4.625),
+ (39806, 385.00, 5.625),
+ (47508, 558.00, 6.625),
+ ('inf', 1068.00, 6.925),
+ ),
+ },
'head of household': {
- 'weekly': ((235, 0.00, 0.00), (264, 0.00, 1.125), (294, 0.00, 3.125), (324, 1.00, 3.625), (353, 2.00, 4.625), (383, 4.00, 5.625), (457, 5.00, 6.625), ('inf', 10.00, 6.925)),
- 'bi-weekly': ((469, 0.00, 0.00), (529, 0.00, 1.125), (588, 1.00, 3.125), (647, 3.00, 3.625), (706, 5.00, 4.625), (766, 7.00, 5.625), (914, 11.00, 6.625), ('inf', 21.00, 6.925)),
- 'semi-monthly': ((508, 0.00, 0.00), (573, 0.00, 1.125), (637, 1.00, 3.125), (701, 3.00, 3.625), (765, 5.00, 4.625), (829, 8.00, 5.625), (990, 12.00, 6.625), ('inf', 22.00, 6.925)),
- 'monthly': ((1017, 0.00, 0.00), (1145, 0.00, 1.125), (1273, 1.00, 3.125), (1402, 5.00, 3.625), (1530, 10.00, 4.625), (1659, 16.00, 5.625), (1980, 23.00, 6.625), ('inf', 45.00, 6.925)),
- 'annually': ((12200, 0.00, 0.00), (13741, 0.00, 1.125), (15281, 17.00, 3.125), (16822, 65.00, 3.625), (18362, 121.00, 4.625), (19903, 192.00, 5.625), (23754, 279.00, 6.625), ('inf', 534.00, 6.925)),
- },
+ 'weekly': (
+ ( 235, 0.00, 0.000),
+ ( 264, 0.00, 1.125),
+ ( 294, 0.00, 3.125),
+ ( 324, 1.00, 3.625),
+ ( 353, 2.00, 4.625),
+ ( 383, 4.00, 5.625),
+ ( 457, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 469, 0.00, 0.000),
+ ( 529, 0.00, 1.125),
+ ( 588, 1.00, 3.125),
+ ( 647, 3.00, 3.625),
+ ( 706, 5.00, 4.625),
+ ( 766, 7.00, 5.625),
+ ( 914, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 508, 0.00, 0.000),
+ ( 573, 0.00, 1.125),
+ ( 637, 1.00, 3.125),
+ ( 701, 3.00, 3.625),
+ ( 765, 5.00, 4.625),
+ ( 829, 8.00, 5.625),
+ ( 990, 12.00, 6.625),
+ ('inf', 22.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1017, 0.00, 0.000),
+ ( 1145, 0.00, 1.125),
+ ( 1273, 1.00, 3.125),
+ ( 1402, 5.00, 3.625),
+ ( 1530, 10.00, 4.625),
+ ( 1659, 16.00, 5.625),
+ ( 1980, 23.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12200, 0.00, 0.000),
+ (13741, 0.00, 1.125),
+ (15281, 17.00, 3.125),
+ (16822, 65.00, 3.625),
+ (18362, 121.00, 4.625),
+ (19903, 192.00, 5.625),
+ (23754, 279.00, 6.625),
+ ('inf', 534.00, 6.925),
+ ),
+ },
}
+
+
US ID Idaho SIT Tax Rate
us_id_sit_tax_rate
{
'single': {
- 'weekly': ((235, 0.00, 0.00), (264, 0.00, 1.125), (294, 0.00, 3.125), (324, 1.00, 3.625), (353, 2.00, 4.625), (383, 4.00, 5.625), (457, 5.00, 6.625), ('inf', 10.00, 6.925)),
- 'bi-weekly': ((469, 0.00, 0.00), (529, 0.00, 1.125), (588, 1.00, 3.125), (647, 3.00, 3.625), (706, 5.00, 4.625), (766, 7.00, 5.625), (914, 11.00, 6.625), ('inf', 21.00, 6.925)),
- 'semi-monthly': ((508, 0.00, 0.00), (573, 0.00, 1.125), (637, 1.00, 3.125), (701, 3.00, 3.625), (765, 5.00, 4.625), (829, 8.00, 5.625), (990, 12.00, 6.625), ('inf', 22.00, 6.925)),
- 'monthly': ((1017, 0.00, 0.00), (1145, 0.00, 1.125), (1273, 1.00, 3.125), (1402, 5.00, 3.625), (1530, 10.00, 4.625), (1659, 16.00, 5.625), (1980, 23.00, 6.625), ('inf', 45.00, 6.925)),
- 'annually': ((12200, 0.00, 0.00), (13741, 0.00, 1.125), (15281, 17.00, 3.125), (16822, 65.00, 3.625), (18362, 121.00, 4.625), (19903, 192.00, 5.625), (23754, 279.00, 6.625), ('inf', 534.00, 6.925)),
- },
+ 'weekly': (
+ ( 238, 0.00, 0.000),
+ ( 269, 0.00, 1.125),
+ ( 299, 0.00, 3.125),
+ ( 329, 1.00, 3.625),
+ ( 359, 2.00, 4.625),
+ ( 389, 4.00, 5.625),
+ ( 465, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 477, 0.00, 0.000),
+ ( 537, 0.00, 1.125),
+ ( 598, 1.00, 3.125),
+ ( 658, 3.00, 3.625),
+ ( 718, 5.00, 4.625),
+ ( 778, 8.00, 5.625),
+ ( 929, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 517, 0.00, 0.000),
+ ( 582, 0.00, 1.125),
+ ( 647, 1.00, 3.125),
+ ( 713, 3.00, 3.625),
+ ( 778, 5.00, 4.625),
+ ( 843, 8.00, 5.625),
+ ( 1007, 12.00, 6.625),
+ ('inf', 23.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1033, 0.00, 0.000),
+ ( 1164, 0.00, 1.125),
+ ( 1295, 1.00, 3.125),
+ ( 1425, 6.00, 3.625),
+ ( 1556, 10.00, 4.625),
+ ( 1687, 16.00, 5.625),
+ ( 2013, 24.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12400, 0.00, 0.000),
+ (13968, 0.00, 1.125),
+ (15536, 18.00, 3.125),
+ (17104, 67.00, 3.625),
+ (18672, 124.00, 4.625),
+ (20240, 197.00, 5.625),
+ (24160, 285.00, 6.625),
+ ('inf', 545.00, 6.925),
+ ),
+ },
'married': {
- 'weekly': ((469, 0.00, 0.00), (529, 0.00, 1.125), (588, 0.00, 3.125), (647, 1.00, 3.625), (706, 2.00, 4.625), (766, 4.00, 5.625), (914, 5.00, 6.625), ('inf', 10.00, 6.925)),
- 'bi-weekly': ((938, 0.00, 0.00), (1057, 0.00, 1.125), (1175, 1.00, 3.125), (1294, 5.00, 3.625), (1412, 9.00, 4.625), (1531, 15.00, 5.625), (1827, 21.00, 6.625), ('inf', 41.00, 6.925)),
- 'semi-monthly': ((1017, 0.00, 0.00), (1145, 0.00, 1.125), (1273, 1.00, 3.125), (1402, 5.00, 3.625), (1530, 10.00, 4.625), (1659, 16.00, 5.625), (1980, 23.00, 6.625), ('inf', 45.00, 6.925)),
- 'monthly': ((2033, 0.00, 0.00), (2290, 0.00, 1.125), (2547, 3.00, 3.125), (2804, 11.00, 3.625), (3060, 20.00, 4.625), (3317, 32.00, 5.625), (3959, 47.00, 6.625), ('inf', 89.00, 6.925)),
- 'annually': ((24400, 0.00, 0.00), (27482, 0.00, 1.125), (30562, 35.00, 3.125), (33644, 131.00, 3.625), (36724, 243.00, 4.625), (39806, 385.00, 5.625), (47508, 558.00, 6.625), ('inf', 1068.00, 6.925)),
- },
+ 'weekly': (
+ ( 477, 0.00, 0.000),
+ ( 537, 0.00, 1.125),
+ ( 598, 0.00, 3.125),
+ ( 658, 1.00, 3.625),
+ ( 718, 3.00, 4.625),
+ ( 778, 5.00, 5.625),
+ ( 929, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 954, 0.00, 0.000),
+ ( 1074, 0.00, 1.125),
+ ( 1195, 1.00, 3.125),
+ ( 1316, 5.00, 3.625),
+ ( 1436, 9.00, 4.625),
+ ( 1557, 15.00, 5.625),
+ ( 1858, 22.00, 6.625),
+ ('inf', 42.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 1033, 0.00, 0.000),
+ ( 1164, 0.00, 1.125),
+ ( 1295, 1.00, 3.125),
+ ( 1425, 6.00, 3.625),
+ ( 1556, 10.00, 4.625),
+ ( 1687, 16.00, 5.625),
+ ( 2013, 24.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'monthly': (
+ ( 2067, 0.00, 0.000),
+ ( 2328, 0.00, 1.125),
+ ( 2589, 3.00, 3.125),
+ ( 2851, 11.00, 3.625),
+ ( 3112, 21.00, 4.625),
+ ( 3373, 33.00, 5.625),
+ ( 4027, 47.00, 6.625),
+ ('inf', 91.00, 6.925),
+ ),
+ 'annually': (
+ (24400, 0.00, 0.000),
+ (27482, 0.00, 1.125),
+ (30562, 35.00, 3.125),
+ (33644, 131.00, 3.625),
+ (36724, 243.00, 4.625),
+ (39806, 385.00, 5.625),
+ (47508, 558.00, 6.625),
+ ('inf', 1068.00, 6.925),
+ ),
+ },},
'head of household': {
- 'weekly': ((235, 0.00, 0.00), (264, 0.00, 1.125), (294, 0.00, 3.125), (324, 1.00, 3.625), (353, 2.00, 4.625), (383, 4.00, 5.625), (457, 5.00, 6.625), ('inf', 10.00, 6.925)),
- 'bi-weekly': ((469, 0.00, 0.00), (529, 0.00, 1.125), (588, 1.00, 3.125), (647, 3.00, 3.625), (706, 5.00, 4.625), (766, 7.00, 5.625), (914, 11.00, 6.625), ('inf', 21.00, 6.925)),
- 'semi-monthly': ((508, 0.00, 0.00), (573, 0.00, 1.125), (637, 1.00, 3.125), (701, 3.00, 3.625), (765, 5.00, 4.625), (829, 8.00, 5.625), (990, 12.00, 6.625), ('inf', 22.00, 6.925)),
- 'monthly': ((1017, 0.00, 0.00), (1145, 0.00, 1.125), (1273, 1.00, 3.125), (1402, 5.00, 3.625), (1530, 10.00, 4.625), (1659, 16.00, 5.625), (1980, 23.00, 6.625), ('inf', 45.00, 6.925)),
- 'annually': ((12200, 0.00, 0.00), (13741, 0.00, 1.125), (15281, 17.00, 3.125), (16822, 65.00, 3.625), (18362, 121.00, 4.625), (19903, 192.00, 5.625), (23754, 279.00, 6.625), ('inf', 534.00, 6.925)),
- },
+ 'weekly': (
+ ( 238, 0.00, 0.000),
+ ( 269, 0.00, 1.125),
+ ( 299, 0.00, 3.125),
+ ( 329, 1.00, 3.625),
+ ( 359, 2.00, 4.625),
+ ( 389, 4.00, 5.625),
+ ( 465, 5.00, 6.625),
+ ('inf', 10.00, 6.925),
+ ),
+ 'bi-weekly': (
+ ( 477, 0.00, 0.000),
+ ( 537, 0.00, 1.125),
+ ( 598, 1.00, 3.125),
+ ( 658, 3.00, 3.625),
+ ( 718, 5.00, 4.625),
+ ( 778, 8.00, 5.625),
+ ( 929, 11.00, 6.625),
+ ('inf', 21.00, 6.925),
+ ),
+ 'semi-monthly': (
+ ( 517, 0.00, 0.000),
+ ( 582, 0.00, 1.125),
+ ( 647, 1.00, 3.125),
+ ( 713, 3.00, 3.625),
+ ( 778, 5.00, 4.625),
+ ( 843, 8.00, 5.625),
+ ( 1007, 12.00, 6.625),
+ ('inf', 23.00, 6.925),
+ ),
+ 'monthly': (
+ ( 1033, 0.00, 0.000),
+ ( 1164, 0.00, 1.125),
+ ( 1295, 1.00, 3.125),
+ ( 1425, 6.00, 3.625),
+ ( 1556, 10.00, 4.625),
+ ( 1687, 16.00, 5.625),
+ ( 2013, 24.00, 6.625),
+ ('inf', 45.00, 6.925),
+ ),
+ 'annually': (
+ (12400, 0.00, 0.000),
+ (13968, 0.00, 1.125),
+ (15536, 18.00, 3.125),
+ (17104, 67.00, 3.625),
+ (18672, 124.00, 4.625),
+ (20240, 197.00, 5.625),
+ (24160, 285.00, 6.625),
+ ('inf', 545.00, 6.925),
+ ),
+ },
}
@@ -104,6 +376,8 @@
}
+
+
US ID Idaho Child Tax Credit Allowance Rate
us_id_sit_ictcat_rate
diff --git a/l10n_us_hr_payroll/data/state/in_indiana.xml b/l10n_us_hr_payroll/data/state/in_indiana.xml
new file mode 100644
index 00000000..8a860e13
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/in_indiana.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+ US IN Indiana SUTA Wage Base
+ us_in_suta_wage_base
+ 9500.00
+
+
+
+
+
+
+
+ US IN Indiana SUTA Rate
+ us_in_suta_rate
+ 2.5
+
+
+
+
+
+
+ US IN Indiana SUTA Income Rate
+ us_in_suta_income_rate
+ 3.23
+
+
+
+
+
+
+ US IN Indiana SIT Personal Exemption Rate
+ us_in_sit_personal_exemption_rate
+ {
+ 'daily': ( 2.74, 5.48, 8.22, 10.96, 13.70, 16.44),
+ 'weekly': ( 19.23, 38.46, 57.69, 76.92, 96.15, 115.38),
+ 'bi-weekly': ( 38.46, 76.92, 115.38, 153.85, 192.31, 230.77),
+ 'semi-monthly': ( 41.67, 83.33, 125.00, 166.67, 208.33, 250.00),
+ 'monthly': ( 83.33, 166.67, 250.00, 333.33, 416.67, 500.00),
+ }
+
+
+
+
+
+
+ US IN Indiana SIT Dependent Exemption Rate
+ us_in_sit_dependent_exemption_rate
+ {
+ 'daily': ( 4.11, 8.22, 12.33, 16.44, 20.55),
+ 'weekly': ( 28.85, 57.69, 86.54, 115.38, 144.23),
+ 'bi-weekly': ( 57.69, 115.38, 173.08, 230.77, 288.46),
+ 'semi-monthly': ( 62.50, 125.00, 187.50, 250.00, 312.50),
+ 'monthly': (125.00, 250.00, 375.00, 500.00, 625.00),
+ }
+
+
+
+
+
+
+ US Indiana - Department of Workforce Development - Unemployment Tax
+ 1
+
+
+ US Indiana - Department of Workforce Development - Unemployment Tax
+
+
+
+
+ US Indiana - Department of Revenue - Income Tax
+ 1
+
+
+ US Indiana - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US IN Indiana State Unemployment
+ ER_US_IN_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_in_suta_wage_base', rate='us_in_suta_rate', state_code='IN')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_in_suta_wage_base', rate='us_in_suta_rate', state_code='IN')
+
+
+
+
+
+
+
+ EE: US IN Indiana State Income Tax Withholding
+ EE_US_IN_SIT
+ python
+ result, _ = in_indiana_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = in_indiana_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/ks_kansas.xml b/l10n_us_hr_payroll/data/state/ks_kansas.xml
new file mode 100644
index 00000000..5ad8de4c
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ks_kansas.xml
@@ -0,0 +1,186 @@
+
+
+
+
+
+ US KS Kansas SUTA Wage Base
+ us_ks_suta_wage_base
+ 14000.0
+
+
+
+
+
+
+
+ US KS Kansas SUTA Rate
+ us_ks_suta_rate
+ 2.7
+
+
+
+
+
+
+
+ US KS Kansas Allowances Rate
+ us_ks_sit_allowances_rate
+ {
+ 'weekly' : 43.27,
+ 'bi-weekly' : 86.54,
+ 'semi-monthly': 93.75,
+ 'monthly' : 187.50,
+ 'quarterly' : 562.50,
+ 'semi-annual': 1125.00,
+ 'annually': 2250.00,
+ }
+
+
+
+
+
+
+
+ US KS Kansas SIT Tax Rate
+ us_ks_sit_tax_rate
+ {
+ 'single': {
+ 'weekly': (
+ ( 58, 0.00, 0.00),
+ ( 346, 3.10, 0.00),
+ ( 635, 5.25, 8.94),
+ ('inf', 5.70, 24.09),
+ ),
+ 'bi-weekly': (
+ ( 115, 0.00, 0.00),
+ ( 692, 3.10, 0.00),
+ ( 1269, 5.25, 17.88),
+ ('inf', 5.70, 48.17),
+ ),
+ 'semi-monthly': (
+ ( 125, 0.00, 0.00),
+ ( 750, 3.10, 0.00),
+ ( 1375, 5.25, 19.38),
+ ('inf', 5.70, 52.19),
+ ),
+ 'monthly': (
+ ( 250, 0.00, 0.00),
+ ( 1500, 3.10, 0.00),
+ ( 2750, 5.25, 38.75),
+ ('inf', 5.70, 104.38),
+ ),
+ 'quarterly': (
+ ( 750, 0.00, 0.00),
+ ( 4500, 3.10, 0.00),
+ (8250, 5.25, 116.25),
+ ('inf', 5.70, 313.13),
+ ),
+ 'semi-annual': (
+ ( 1500, 0.00, 0.00),
+ ( 9000, 3.10, 0.00),
+ (16500, 5.25, 232.50),
+ ('inf', 5.70, 626.25),
+ ),
+ 'annually': (
+ ( 3000, 0.00, 0.00),
+ (18000, 3.10, 0.00),
+ (33000, 5.25, 465.00),
+ ('inf', 5.70, 1252.50),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 144, 0.00, 0.00),
+ ( 721, 3.10, 0.00),
+ (1298, 5.25, 17.88),
+ ('inf', 5.70, 48.17),
+ ),
+ 'bi-weekly': (
+ ( 288, 0.00, 0.00),
+ ( 1442, 3.10, 0.00),
+ ( 2596, 5.25, 35.77),
+ ('inf', 5.70, 96.35),
+ ),
+ 'semi-monthly': (
+ ( 313, 0.00, 0.00),
+ ( 1563, 3.10, 0.00),
+ ( 2813, 5.25, 38.75),
+ ('inf', 5.70, 104.38),
+ ),
+ 'monthly': (
+ ( 625, 0.00, 0.00),
+ ( 3125, 3.10, 0.00),
+ ( 5625, 5.25, 77.50),
+ ('inf', 5.70, 208.75),
+ ),
+ 'quarterly': (
+ ( 1875, 0.00, 0.00),
+ ( 9375, 3.10, 0.00),
+ (16875, 5.25, 232.50),
+ ('inf', 5.70, 626.25),
+ ),
+ 'semi-annual': (
+ ( 3750, 0.00, 0.00),
+ (18750, 3.10, 0.00),
+ (33750, 5.25, 465.00),
+ ('inf', 5.70, 1252.50),
+ ),
+ 'annually': (
+ ( 7500, 0.00, 0.00),
+ (37500, 3.10, 0.00),
+ (67500, 5.25, 930.00),
+ ('inf', 5.70, 2505.00),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US Kansas - Department of Labor - Unemployment Tax
+ 1
+
+
+ US Kansas - Department of Labor - Unemployment Tax
+
+
+
+
+ US Kansas - Department of Revenue - Income Tax
+ 1
+
+
+ US Kansas - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+ ER: US KS Kansas State Unemployment
+ ER_US_KS_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ks_suta_wage_base', rate='us_ks_suta_rate', state_code='KS')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ks_suta_wage_base', rate='us_ks_suta_rate', state_code='KS')
+
+
+
+
+
+
+
+ EE: US KS Kansas State Income Tax Withholding
+ EE_US_KS_SIT
+ python
+ result, _ = ks_kansas_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ks_kansas_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ky_kentucky.xml b/l10n_us_hr_payroll/data/state/ky_kentucky.xml
new file mode 100644
index 00000000..53eb3190
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ky_kentucky.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+ US KY Kentucky SUTA Wage Base
+ us_ky_suta_wage_base
+ 10800.0
+
+
+
+
+
+
+
+ US KY Kentucky SUTA Rate
+ us_ky_suta_rate
+ 2.7
+
+
+
+
+
+
+
+ US KY Kentucky Standard Deduction Rate
+ us_ky_sit_standard_deduction_rate
+ 2650
+
+
+
+
+
+
+
+ US KY Kentucky SIT Tax Rate
+ us_ky_sit_tax_rate
+ 5.0
+
+
+
+
+
+
+ US Kentucky - Office of Unemployment Insurance - Unemployment Tax
+ 1
+
+
+ US Kentucky - Office of Unemployment Insurance - Unemployment Tax
+
+
+
+
+ US Kentucky - Department of Revenue - Income Tax
+ 1
+
+
+ US Kentucky - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US KY Kentucky State Unemployment
+ ER_US_KY_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ky_suta_wage_base', rate='us_ky_suta_rate', state_code='KY')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ky_suta_wage_base', rate='us_ky_suta_rate', state_code='KY')
+
+
+
+
+
+
+
+ EE: US KY Kentucky State Income Tax Withholding
+ EE_US_KY_SIT
+ python
+ result, _ = ky_kentucky_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ky_kentucky_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/la_louisiana.xml b/l10n_us_hr_payroll/data/state/la_louisiana.xml
new file mode 100644
index 00000000..78cd6069
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/la_louisiana.xml
@@ -0,0 +1,148 @@
+
+
+
+
+
+ US LA Louisiana SUTA Wage Base
+ us_la_suta_wage_base
+ 7700.0
+
+
+
+ US LA Louisiana SUTA Wage Base
+ us_la_suta_wage_base
+ 7700.0
+
+
+
+
+
+
+
+ US LA Louisiana SUTA Rate
+ us_la_suta_rate
+ 1.14
+
+
+
+ US LA Louisiana SUTA Rate
+ us_la_suta_rate
+ 1.14
+
+
+
+
+
+
+ US LA Louisiana SIT Tax Rate
+ us_la_sit_tax_rate
+ {
+ 'single': (
+ (12500.00, 2.10),
+ (50000.00, 1.60),
+ ( 'inf', 1.35),
+ ),
+ 'married': (
+ ( 25000.00, 2.10),
+ (100000.00, 1.65),
+ ( 'inf', 1.35),
+ ),
+ }
+
+
+
+ US LA Louisiana SIT Tax Rate
+ us_la_sit_tax_rate
+ {
+ 'single': (
+ (12500.00, 2.10),
+ (50000.00, 1.60),
+ ( 'inf', 1.35)
+ ),
+ 'married': (
+ ( 25000.00, 2.10),
+ (100000.00, 1.65),
+ ( 'inf', 1.35)
+ ),
+ }
+
+
+
+
+
+
+ US LA Louisiana Personal Exemption Rate
+ us_la_sit_personal_exemption_rate
+ 4500
+
+
+
+ US LA Louisiana Personal Exemption Rate
+ us_la_sit_personal_exemption_rate
+ 4500
+
+
+
+
+
+
+ US LA Louisiana Dependent Rate
+ us_la_sit_dependent_rate
+ 1000.0
+
+
+
+ US LA Louisiana Dependent Rate
+ us_la_sit_dependent_rate
+ 1000.0
+
+
+
+
+
+
+ US Louisiana - Workforce Commission (LWC) - Unemployment Tax
+ 1
+
+
+ US Louisiana - Workforce Commission (LWC) - Unemployment Tax
+
+
+
+
+ US Louisiana - Department of Revenue (LDOR) - Income Tax
+ 1
+
+
+ US Louisiana - Department of Revenue (LDOR) - Income Tax
+
+
+
+
+
+
+
+ ER: US LA Louisiana State Unemployment
+ ER_US_LA_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_la_suta_wage_base', rate='us_la_suta_rate', state_code='LA')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_la_suta_wage_base', rate='us_la_suta_rate', state_code='LA')
+
+
+
+
+
+
+
+ EE: US LA Louisiana State Income Tax Withholding
+ EE_US_LA_SIT
+ python
+ result, _ = la_louisiana_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = la_louisiana_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/me_maine.xml b/l10n_us_hr_payroll/data/state/me_maine.xml
new file mode 100644
index 00000000..15fadbb9
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/me_maine.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+ US ME Maine SUTA Wage Base
+ us_me_suta_wage_base
+ 12000.0
+
+
+
+
+
+
+
+ US ME Maine SUTA Rate
+ us_me_suta_rate
+ 1.92
+
+
+
+
+
+
+ US ME Maine SIT Tax Rate
+ us_me_sit_tax_rate
+ {
+ 'single': (
+ ( 22200, 0, 5.80),
+ ( 52600, 1288, 6.75),
+ ( 'inf', 3340, 7.15),
+ ),
+ 'married': (
+ ( 44450, 0, 5.80),
+ ( 105200, 2578, 6.75),
+ ( 'inf', 6679, 7.15),
+ ),
+ }
+
+
+
+
+
+
+ US ME Maine Standard Deduction Rate
+ us_me_sit_standard_deduction_rate
+ {
+ 'single': {
+ ( 82900, 9550),
+ (157900, 75000),
+ },
+ 'married': {
+ (165800, 21950),
+ (315800, 150000),
+ },
+ }
+
+
+
+
+
+
+ US ME Maine Personal Exemption Rate
+ us_me_sit_personal_exemption_rate
+ 4300
+
+
+
+
+
+
+
+ US Maine - Department Of Labor | ReEmploy - Unemployment Tax
+ 1
+
+
+ US Maine - Department Of Labor | ReEmploy - Unemployment Tax
+
+
+
+
+ US Maine - Department Of Revenue Services - Income Tax
+ 1
+
+
+ US Maine - Department Of Revenue Services - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US ME Maine State Unemployment
+ ER_US_ME_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_me_suta_wage_base', rate='us_me_suta_rate', state_code='ME')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_me_suta_wage_base', rate='us_me_suta_rate', state_code='ME')
+
+
+
+
+
+
+
+ EE: US ME Maine State Income Tax Withholding
+ EE_US_ME_SIT
+ python
+ result, _ = me_maine_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = me_maine_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/mn_minnesota.xml b/l10n_us_hr_payroll/data/state/mn_minnesota.xml
index d21874cc..6408358c 100644
--- a/l10n_us_hr_payroll/data/state/mn_minnesota.xml
+++ b/l10n_us_hr_payroll/data/state/mn_minnesota.xml
@@ -37,37 +37,39 @@
US MN Minnesota SIT Tax Rate
us_mn_sit_tax_rate
{
- 'single': [
+ 'single': (
( 28920, 2400, 5.35, 0.00),
( 89510, 28920, 7.05, 1418.82),
(166290, 89510, 7.85, 5690.42),
( 'inf', 166290, 9.85, 11717.65),
- ],
- 'married': [
+ ),
+ 'married': (
( 47820, 9050, 5.35, 0.00),
( 163070, 47820, 7.05, 2074.20),
( 282200, 163070, 7.85, 10199.33),
( 'inf', 282200, 9.85, 19551.04),
- ],
+ ),
}
+
+
US MN Minnesota SIT Tax Rate
us_mn_sit_tax_rate
{
- 'single': [
+ 'single': (
( 30760, 3800, 5.35, 0.00),
( 92350, 30760, 6.80, 1442.36),
(168200, 92350, 7.85, 5630.48),
( 'inf', 168200, 9.85, 11584.71),
- ],
- 'married': [
+ ),
+ 'married': (
( 51310, 11900, 5.35, 0.00),
( 168470, 51310, 6.80, 2108.44),
( 285370, 168470, 7.85, 10075.32),
( 'inf', 285370, 9.85, 19251.97),
- ],
+ ),
}
@@ -80,6 +82,8 @@
4250.0
+
+
US MN Minnesota Allowances Rate
us_mn_sit_allowances_rate
diff --git a/l10n_us_hr_payroll/data/state/mo_missouri.xml b/l10n_us_hr_payroll/data/state/mo_missouri.xml
index cd6ccc28..500126b2 100644
--- a/l10n_us_hr_payroll/data/state/mo_missouri.xml
+++ b/l10n_us_hr_payroll/data/state/mo_missouri.xml
@@ -33,6 +33,7 @@
+
US MO Missouri SIT Rate Table
us_mo_sit_rate
@@ -49,6 +50,8 @@
]
+
+
US MO Missouri SIT Rate Table
us_mo_sit_rate
@@ -68,6 +71,7 @@
+
US MO Missouri SIT Deduction
us_mo_sit_deduction
@@ -78,6 +82,8 @@
}
+
+
US MO Missouri SIT Deduction
us_mo_sit_deduction
diff --git a/l10n_us_hr_payroll/data/state/ms_mississippi.xml b/l10n_us_hr_payroll/data/state/ms_mississippi.xml
index 7a94150d..fd9d670d 100644
--- a/l10n_us_hr_payroll/data/state/ms_mississippi.xml
+++ b/l10n_us_hr_payroll/data/state/ms_mississippi.xml
@@ -43,6 +43,8 @@
]
+
+
US MS Mississippi SIT Rate Table
us_ms_sit_rate
@@ -67,6 +69,19 @@
}
+
+
+
+ US MS Mississippi SIT Deduction
+ us_ms_sit_deduction
+ {
+ 'single': 2300.0,
+ 'head_of_household': 3400.0,
+ 'married_dual': 2300.0,
+ 'married': 4600.0,
+ }
+
+
diff --git a/l10n_us_hr_payroll/data/state/mt_montana.xml b/l10n_us_hr_payroll/data/state/mt_montana.xml
index 7cca142a..d73c74f3 100644
--- a/l10n_us_hr_payroll/data/state/mt_montana.xml
+++ b/l10n_us_hr_payroll/data/state/mt_montana.xml
@@ -52,39 +52,78 @@
US MT Montana SIT Rate Table
us_mt_sit_rate
{
- 'weekly': [
+ 'weekly': (
( 135.00, 0.0, 1.80),
( 288.00, 2.0, 4.40),
( 2308.00, 9.0, 6.00),
( 'inf', 130.0, 6.60),
- ],
- 'bi-weekly': [
+ ),
+ 'bi-weekly': (
( 269.00, 0.0, 1.80),
( 577.00, 5.0, 4.40),
( 4615.00, 18.0, 6.00),
( 'inf', 261.0, 6.60),
- ],
- 'semi-monthly': [
+ ),
+ 'semi-monthly': (
( 292.00, 0.0, 1.80),
( 625.00, 5.0, 4.40),
( 5000.00, 20.0, 6.00),
( 'inf', 282.0, 6.60),
- ],
- 'monthly': [
+ ),
+ 'monthly': (
( 583.00, 0.0, 1.80),
( 1250.00, 11.0, 4.40),
( 10000.00, 40.0, 6.00),
( 'inf', 565.0, 6.60),
- ],
- 'annually': [
+ ),
+ 'annually': (
( 7000.00, 0.0, 1.80),
( 15000.00, 126.0, 4.40),
( 120000.00, 478.0, 6.00),
( 'inf', 6778.0, 6.60),
- ],
+ ),
}
+
+
+
+ US MT Montana SIT Rate Table
+ us_mt_sit_rate
+ {
+ 'weekly': (
+ ( 135.00, 0.0, 1.80),
+ ( 288.00, 2.0, 4.40),
+ ( 2308.00, 9.0, 6.00),
+ ( 'inf', 130.0, 6.60),
+ ),
+ 'bi-weekly': (
+ ( 269.00, 0.0, 1.80),
+ ( 577.00, 5.0, 4.40),
+ ( 4615.00, 18.0, 6.00),
+ ( 'inf', 261.0, 6.60),
+ ),
+ 'semi-monthly': (
+ ( 292.00, 0.0, 1.80),
+ ( 625.00, 5.0, 4.40),
+ ( 5000.00, 20.0, 6.00),
+ ( 'inf', 282.0, 6.60),
+ ),
+ 'monthly': (
+ ( 583.00, 0.0, 1.80),
+ ( 1250.00, 11.0, 4.40),
+ ( 10000.00, 40.0, 6.00),
+ ( 'inf', 565.0, 6.60),
+ ),
+ 'annually': (
+ ( 7000.00, 0.0, 1.80),
+ ( 15000.00, 126.0, 4.40),
+ ( 120000.00, 478.0, 6.00),
+ ( 'inf', 6778.0, 6.60),
+ ),
+ }
+
+
@@ -100,6 +139,20 @@
}
+
+
+
+ US MT Montana SIT Exemption Rate Table
+ us_mt_sit_exemption_rate
+ {
+ 'weekly': 37.0,
+ 'bi-weekly': 73.0,
+ 'semi-monthly': 79.0,
+ 'monthly': 158.0,
+ 'annually': 1900.0,
+ }
+
+
diff --git a/l10n_us_hr_payroll/data/state/nc_northcarolina.xml b/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
index 85ce2c2d..4e032b89 100644
--- a/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
+++ b/l10n_us_hr_payroll/data/state/nc_northcarolina.xml
@@ -33,6 +33,8 @@
+
+
US NC North Carolina Allowance Rate
us_nc_sit_allowance_rate
@@ -44,6 +46,8 @@
}
+
+
US NC North Carolina Allowance Rate
us_nc_sit_allowance_rate
diff --git a/l10n_us_hr_payroll/data/state/nd_north_dakota.xml b/l10n_us_hr_payroll/data/state/nd_north_dakota.xml
new file mode 100644
index 00000000..37f129b9
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nd_north_dakota.xml
@@ -0,0 +1,272 @@
+
+
+
+
+
+ US ND North Dakota SUTA Wage Base
+ us_nd_suta_wage_base
+ 37900.0
+
+
+
+
+
+
+
+ US ND North Dakota SUTA Rate
+ us_nd_suta_rate
+ 1.02
+
+
+
+
+
+
+ US ND North Dakota SIT Tax Rate
+ us_nd_sit_tax_rate
+ {
+ 'single': {
+ 'weekly': (
+ ( 119, 0.00, 0.00),
+ ( 891, 0.00, 1.10),
+ ( 1988, 8.49, 2.04),
+ ( 4016, 30.87, 2.27),
+ ( 8592, 76.91, 2.64),
+ ('inf', 197.71, 2.90),
+ ),
+ 'bi-weekly': (
+ ( 238, 0.00, 0.00),
+ ( 1782, 0.00, 1.10),
+ ( 3975, 16.98, 2.04),
+ ( 8033, 61.72, 2.27),
+ ( 17185, 153.84, 2.64),
+ ( 'inf', 395.45, 2.90),
+ ),
+ 'semi-monthly': (
+ ( 258, 0.00, 0.00),
+ ( 1930, 0.00, 1.10),
+ ( 4306, 18.39, 2.04),
+ ( 8702, 66.86, 2.27),
+ ( 18617, 166.65, 2.64),
+ ( 'inf', 428.41, 2.90),
+ ),
+ 'monthly': (
+ ( 517, 0.00, 0.00),
+ ( 3860, 0.00, 1.10),
+ ( 8613, 36.77, 2.04),
+ ( 17404, 133.73, 2.27),
+ ( 37233, 333.29, 2.64),
+ ( 'inf', 856.78, 2.90),
+ ),
+ 'quarterly': (
+ ( 1550, 0.00, 0.00),
+ ( 11581, 0.00, 1.10),
+ ( 25838, 110.34, 2.04),
+ ( 52213, 401.18, 2.27),
+ ( 111700, 999.90, 2.64),
+ ( 'inf', 2570.35, 2.90),
+ ),
+ 'semi-annual': (
+ ( 3100, 0.00, 0.00),
+ ( 23163, 0.00, 1.10),
+ ( 51675, 220.69, 2.04),
+ ( 104425, 802.34, 2.27),
+ ( 223400, 1999.76, 2.64),
+ ( 'inf', 5140.70, 2.90),
+ ),
+ 'annual': (
+ ( 6200, 0.00, 0.00),
+ ( 46325, 0.00, 1.10),
+ ( 103350, 441.38, 2.04),
+ ( 208850, 1604.69, 2.27),
+ ( 446800, 3999.54, 2.64),
+ ( 'inf', 10281.42, 2.90),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 238, 0.00, 0.00),
+ ( 883, 0.00, 1.10),
+ ( 1796, 7.10, 2.04),
+ ( 2611, 25.72, 2.27),
+ ( 4475, 44.22, 2.64),
+ ('inf', 93.43, 2.90),
+ ),
+ 'bi-weekly': (
+ ( 477, 0.00, 0.00),
+ ( 1766, 0.00, 1.10),
+ ( 3591, 14.18, 2.04),
+ ( 5221, 51.41, 2.27),
+ ( 8950, 88.41, 2.64),
+ ( 'inf', 186.86, 2.90),
+ ),
+ 'semi-monthly': (
+ ( 517, 0.00, 0.00),
+ ( 1914, 0.00, 1.10),
+ ( 3891, 15.37, 2.04),
+ ( 5656, 55.70, 2.27),
+ ( 9696, 95.76, 2.64),
+ ( 'inf', 202.42, 2.90),
+ ),
+ 'monthly': (
+ ( 1033, 0.00, 0.00),
+ ( 3827, 0.00, 1.10),
+ ( 7781, 30.73, 2.04),
+ ( 11313, 111.40, 2.27),
+ ( 19392, 191.57, 2.64),
+ ( 'inf', 404.86, 2.90),
+ ),
+ 'quarterly': (
+ ( 3100, 0.00, 0.00),
+ ( 11481, 0.00, 1.10),
+ ( 23344, 92.19, 2.04),
+ ( 33938, 334.20, 2.27),
+ ( 58175, 574.68, 2.64),
+ ( 'inf', 1214.54, 2.90),
+ ),
+ 'semi-annual': (
+ ( 6200, 0.00, 0.00),
+ ( 22963, 0.00, 1.10),
+ ( 46688, 184.39, 2.04),
+ ( 67875, 668.38, 2.27),
+ ( 116350, 1149.33, 2.64),
+ ( 'inf', 2429.07, 2.90),
+ ),
+ 'annual': (
+ ( 12400, 0.00, 0.00),
+ ( 45925, 0.00, 1.10),
+ ( 93375, 368.78, 2.04),
+ ( 135750, 1336.76, 2.27),
+ ( 232700, 2298.67, 2.64),
+ ( 'inf', 4858.15, 2.90),
+ ),
+ },
+ 'head_household':{
+ 'weekly': (
+ ( 119, 0.00, 0.00),
+ ( 891, 0.00, 1.10),
+ ( 1988, 8.49, 2.04),
+ ( 4016, 30.87, 2.27),
+ ( 8592, 76.91, 2.64),
+ ('inf', 197.71, 2.90),
+ ),
+ 'bi-weekly': (
+ ( 238, 0.00, 0.00),
+ ( 1782, 0.00, 1.10),
+ ( 3975, 16.98, 2.04),
+ ( 8033, 61.72, 2.27),
+ ( 17185, 153.84, 2.64),
+ ( 'inf', 395.45, 2.90),
+ ),
+ 'semi-monthly': (
+ ( 258, 0.00, 0.00),
+ ( 1930, 0.00, 1.10),
+ ( 4306, 18.39, 2.04),
+ ( 8702, 66.86, 2.27),
+ ( 18617, 166.65, 2.64),
+ ( 'inf', 428.41, 2.90),
+ ),
+ 'monthly': (
+ ( 517, 0.00, 0.00),
+ ( 3860, 0.00, 1.10),
+ ( 8613, 36.77, 2.04),
+ ( 17404, 133.73, 2.27),
+ ( 37233, 333.29, 2.64),
+ ( 'inf', 856.78, 2.90),
+ ),
+ 'quarterly': (
+ ( 1550, 0.00, 0.00),
+ ( 11581, 0.00, 1.10),
+ ( 25838, 110.34, 2.04),
+ ( 52213, 401.18, 2.27),
+ ( 111700, 999.90, 2.64),
+ ( 'inf', 2570.35, 2.90),
+ ),
+ 'semi-annual': (
+ ( 3100, 0.00, 0.00),
+ ( 23163, 0.00, 1.10),
+ ( 51675, 220.69, 2.04),
+ ( 104425, 802.34, 2.27),
+ ( 223400, 1999.76, 2.64),
+ ( 'inf', 5140.70, 2.90),
+ ),
+ 'annual': (
+ ( 6200, 0.00, 0.00),
+ ( 46325, 0.00, 1.10),
+ ( 103350, 441.38, 2.04),
+ ( 208850, 1604.69, 2.27),
+ ( 446800, 3999.54, 2.64),
+ ( 'inf', 10281.42, 2.90),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US ND North Dakota Allowances Rate
+ us_nd_sit_allowances_rate
+ {
+ 'weekly' : 83.00,
+ 'bi-weekly' : 165.00,
+ 'semi-monthly': 179.00,
+ 'monthly' : 358.00,
+ 'quarterly' : 1075.00,
+ 'semi-annual': 2150.00,
+ 'annually': 4300.00,
+ }
+
+
+
+
+
+
+ US North Dakota - Office of State Tax Commissioner - Unemployment Tax
+ 1
+
+
+ US North Dakota - Office of State Tax Commissioner - Unemployment Tax
+
+
+
+
+ US North Dakota - Taxpayer Access Point - Income Tax
+ 1
+
+
+ US North Dakota - Taxpayer Access Point - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US ND North Dakota State Unemployment
+ ER_US_ND_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nd_suta_wage_base', rate='us_nd_suta_rate', state_code='ND')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nd_suta_wage_base', rate='us_nd_suta_rate', state_code='ND')
+
+
+
+
+
+
+
+ EE: US ND North Dakota State Income Tax Withholding
+ EE_US_ND_SIT
+ python
+ result, _ = nd_north_dakota_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = nd_north_dakota_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ne_nebraska.xml b/l10n_us_hr_payroll/data/state/ne_nebraska.xml
new file mode 100644
index 00000000..bd35fe1f
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ne_nebraska.xml
@@ -0,0 +1,230 @@
+
+
+
+
+
+ US NE Nebraska SUTA Wage Base
+ us_ne_suta_wage_base
+ 9000.0
+
+
+
+
+
+
+
+ US NE Nebraska SUTA Rate
+ us_ne_suta_rate
+ 1.25
+
+
+
+
+
+
+
+ US NE Nebraska SIT Tax Rate
+ us_ne_sit_tax_rate
+ {
+ 'single': {
+ 'weekly': (
+ ( 57, 0.00, 0.00),
+ ( 105, 0.00, 2.26),
+ ( 342, 1.08, 3.22),
+ ( 496, 8.71, 4.91),
+ ( 629, 16.27, 6.20),
+ ( 1182, 24.52, 6.59),
+ ('inf', 60.96, 6.95),
+ ),
+ 'bi-weekly': (
+ ( 114, 0.00, 0.00),
+ ( 211, 0.00, 2.26),
+ ( 684, 2.19, 3.22),
+ ( 992, 17.42, 4.91),
+ ( 1259, 32.54, 6.20),
+ ( 2364, 49.09, 6.59),
+ ('inf', 121.91, 6.95),
+ ),
+ 'semi-monthly': (
+ ( 124, 0.00, 0.00),
+ ( 228, 0.00, 2.26),
+ ( 741, 2.35, 3.22),
+ ( 1074, 18.87, 4.91),
+ ( 1364, 35.22, 6.20),
+ ( 2561, 53.20, 6.59),
+ ('inf', 132.08, 6.95),
+ ),
+ 'monthly': (
+ ( 248, 0.00, 0.00),
+ ( 457, 0.00, 2.26),
+ ( 1483, 4.72, 3.22),
+ ( 2148, 37.76, 4.91),
+ ( 2728, 70.41, 6.20),
+ ( 5123, 106.37, 6.59),
+ ('inf', 264.20, 6.95),
+ ),
+ 'quarterly': (
+ ( 744, 0.00, 0.00),
+ ( 1370, 0.00, 2.26),
+ ( 4448, 14.15, 3.22),
+ ( 6445, 113.26, 4.91),
+ ( 8183, 211.31, 6.20),
+ ( 15368, 319.07, 6.59),
+ ( 'inf', 792.56, 6.95),
+ ),
+ 'semi-annual': (
+ ( 1488, 0.00, 0.00),
+ ( 2740, 0.00, 2.26),
+ ( 8895, 28.30, 3.22),
+ ( 12890, 226.49, 4.91),
+ ( 16365, 422.64, 6.20),
+ ( 30735, 638.09, 6.59),
+ ( 'inf', 1585.07, 6.95),
+ ),
+ 'annually': (
+ ( 2975, 0.00, 0.00),
+ ( 5480, 0.00, 2.26),
+ ( 17790, 56.61, 3.22),
+ ( 25780, 452.99, 4.91),
+ ( 32730, 845.30, 6.20),
+ ( 61470, 1276.20, 6.59),
+ ( 'inf', 3170.17, 6.95),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 137, 0.00, 0.00),
+ ( 204, 0.00, 2.26),
+ ( 508, 1.51, 3.22),
+ ( 790, 11.30, 4.91),
+ ( 981, 25.15, 6.20),
+ ( 1300, 36.99, 6.59),
+ ('inf', 58.01, 6.95),
+ ),
+ 'bi-weekly': (
+ ( 273, 0.00, 0.00),
+ ( 408, 0.00, 2.26),
+ ( 1016, 3.05, 3.22),
+ ( 1581, 22.63, 4.91),
+ ( 1961, 50.37, 6.20),
+ ( 2601, 73.93, 6.59),
+ ('inf', 116.11, 6.95),
+ ),
+ 'semi-monthly': (
+ ( 296, 0.00, 0.00),
+ ( 442, 0.00, 2.26),
+ ( 1101, 3.30, 3.22),
+ ( 1713, 24.52, 4.91),
+ ( 2125, 54.57, 6.20),
+ ( 2818, 80.11, 6.59),
+ ('inf', 125.78, 6.95),
+ ),
+ 'monthly': (
+ ( 592, 0.00, 0.00),
+ ( 884, 0.00, 2.26),
+ ( 2202, 6.60, 3.22),
+ ( 3425, 49.04, 4.91),
+ ( 4249, 109.09, 6.20),
+ ( 5635, 160.18, 6.59),
+ ('inf', 251.52, 6.95),
+ ),
+ 'quarterly': (
+ ( 1775, 0.00, 0.00),
+ ( 2653, 0.00, 2.26),
+ ( 6605, 19.84, 3.22),
+ ( 10275, 147.09, 4.91),
+ ( 12748, 327.29, 6.20),
+ ( 16905, 480.62, 6.59),
+ ( 'inf', 754.57, 6.95),
+ ),
+ 'semi-annual': (
+ ( 3550, 0.00, 0.00),
+ ( 5305, 0.00, 2.26),
+ ( 13210, 39.66, 3.22),
+ ( 20550, 294.20, 4.91),
+ ( 25495, 654.59, 6.20),
+ ( 33810, 961.18, 6.59),
+ ( 'inf', 1509.14, 6.95),
+ ),
+ 'annually': (
+ ( 7100, 0.00, 0.00),
+ ( 10610, 0.00, 2.26),
+ ( 26420, 79.33, 3.22),
+ ( 41100, 588.41, 4.91),
+ ( 50990, 1309.20, 6.20),
+ ( 67620, 1992.38, 6.59),
+ ( 'inf', 3018.30, 6.95),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US NE Nebraska Allowances Rate
+ us_ne_sit_allowances_rate
+ {
+ 'weekly' : 37.69,
+ 'bi-weekly' : 75.38,
+ 'semi-monthly': 81.67,
+ 'monthly' : 163.33,
+ 'quarterly' : 490.00,
+ 'semi-annual': 980.00,
+ 'annually': 1960.00,
+ }
+
+
+
+
+
+
+
+ US Nebraska - Nebraska Department of Labor - Unemployment Tax
+ 1
+
+
+ US Nebraska - Nebraska Department of Labor - Unemployment Tax
+
+
+
+
+ US Nebraska - Nebraska Department of Revenue - Income Tax
+ 1
+
+
+ US Nebraska - Nebraska Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US NE Nebraska State Unemployment
+ ER_US_NE_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ne_suta_wage_base', rate='us_ne_suta_rate', state_code='NE')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ne_suta_wage_base', rate='us_ne_suta_rate', state_code='NE')
+
+
+
+
+
+
+
+ EE: US NE Nebraska State Income Tax Withholding
+ EE_US_NE_SIT
+ python
+ result, _ = ne_nebraska_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ne_nebraska_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/nj_newjersey.xml b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
index a01ad739..acd3729e 100644
--- a/l10n_us_hr_payroll/data/state/nj_newjersey.xml
+++ b/l10n_us_hr_payroll/data/state/nj_newjersey.xml
@@ -148,102 +148,691 @@
us_nj_sit_rate
{
'A': {
- 'weekly': ((385, 0.0, 1.50), (673, 5.77, 2.00), (769, 11.54, 3.90), (1442, 15.29, 6.10), (9615, 56.34, 7.00), (96154, 628.46, 9.90), ('inf', 9195.77, 11.80)),
- 'bi-weekly': ((769, 0.00, 1.50), (1346, 12.00, 2.00), (1538, 23.00, 3.90), (2885, 31.00, 6.10), (19231, 113.00, 7.00), (192308, 1257.00, 9.90), ('inf',18392.00, 11.80)),
- 'semi-monthly': ((833, 0.00, 1.50), (1458, 13.00, 2.00), (1667, 25.00, 3.90), (3125, 33.00, 6.10), (20833, 122.00, 7.00), (208333, 1362.00, 9.90), ('inf', 19924.00, 11.80)),
- 'monthly': ((1667, 0.00, 1.50), (2917, 25.00, 2.00), (3333, 50.00, 3.90), (6250, 66.00, 6.10), (41667, 244.00, 7.00), (416667, 2723.00, 9.90), ('inf', 39848.00, 11.80)),
- 'quarterly': ((5000, 0.00, 1.50), (8750, 75.00, 2.00), (10000, 150.00, 3.90), (18750, 198.75, 6.10), (125000, 732.50, 7.00), (1250000, 8170.00, 9.90), ('inf', 119545.00, 11.80)),
- 'semi-annual': ((10000, 0.00, 1.50), (17500, 150.00, 2.00), (20000, 300.00, 3.90), (37500, 397.50, 6.10), (250000, 1465.00, 7.00), (2500000, 16340.00, 9.90), ('inf', 239090.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (35000, 300.00, 2.00), (40000, 600.00, 3.90), (75000, 795.00, 6.10), (500000, 2930.00, 7.00), (5000000, 32680.00, 9.90), ('inf', 478180.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 769, 11.54, 3.90),
+ ( 1442, 15.29, 6.10),
+ ( 9615, 56.34, 7.00),
+ (96154, 628.46, 9.90),
+ ('inf', 9195.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 1538, 23.00, 3.90),
+ ( 2885, 31.00, 6.10),
+ ( 19231, 113.00, 7.00),
+ (192308, 1257.00, 9.90),
+ ( 'inf', 18392.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 1667, 25.00, 3.90),
+ ( 3125, 33.00, 6.10),
+ ( 20833, 122.00, 7.00),
+ (208333, 1362.00, 9.90),
+ ( 'inf', 19924.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2917, 25.00, 2.00),
+ ( 3333, 50.00, 3.90),
+ ( 6250, 66.00, 6.10),
+ ( 41667, 244.00, 7.00),
+ (416667, 2723.00, 9.90),
+ ( 'inf', 39848.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 10000, 150.00, 3.90),
+ ( 18750, 198.75, 6.10),
+ ( 125000, 732.50, 7.00),
+ (1250000, 8170.00, 9.90),
+ ( 'inf', 119545.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 20000, 300.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 250000, 1465.00, 7.00),
+ (2500000, 16340.00, 9.90),
+ ( 'inf', 239090.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 40000, 600.00, 3.90),
+ ( 75000, 795.00, 6.10),
+ ( 500000, 2930.00, 7.00),
+ (5000000, 32680.00, 9.90),
+ ( 'inf', 478180.00, 11.80),
+ ),
+ },
'B': {
- 'weekly': ((385, 0.0, 1.50), (962, 5.77, 2.00), (1346, 17.31, 2.70), (1538, 27.69, 3.9), (2885, 35.19, 6.10), (9615, 117.31, 7.00), (96154, 588.46, 9.90), ('inf', 9155.77, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1923, 12.00, 2.00), (2692, 35.00, 2.70), (3076, 55.00, 3.9), (5769, 70.00, 6.10), (19231, 235.00, 700), (192308, 1177.00, 9.90), ('inf', 18312.00, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (2083, 12.50, 2.00), (2917, 37.50, 2.70), (3333, 59.99, 3.9), (6250, 76.25, 6.10), (20833, 254.19, 7.00), (208333, 1275.00, 9.90), ('inf', 19838.00, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (4167, 25.00, 2.00), (5833, 75.00, 2.70), (6667, 120.00, 3.9), (12500, 153.00, 6.10), (41667, 508.00, 7.00), (416667, 2550.00, 9.90), ('inf', 39675.00, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (12500, 75.00, 2.00), (17500, 225.00, 2.70), (20000, 360.00, 3.9), (37500, 397.50, 6.10), (125000, 1525.00, 7.00), (1250000, 7650.00, 9.90), ('inf', 119025.00, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (25000, 150.00, 2.00), (35000, 450.00, 2.70), (40000, 720.00, 3.9), (75000, 915.00, 6.10), (250000, 3050.00, 7.00), (2500000, 15300.00, 9.90), ('inf', 238050.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (50000, 300.00, 2.00), (70000, 900.00, 2.70), (80000, 1440.00, 3.9), (150000, 1830.00, 6.10), (500000, 6100.00, 7.00), (5000000, 30600.00, 9.90), ('inf', 476100.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 962, 5.77, 2.00),
+ ( 1346, 17.31, 2.70),
+ ( 1538, 27.69, 3.90),
+ ( 2885, 35.19, 6.10),
+ ( 9615, 117.31, 7.00),
+ (96154, 588.46, 9.90),
+ ('inf', 9155.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1923, 12.00, 2.00),
+ ( 2692, 35.00, 2.70),
+ ( 3076, 55.00, 3.90),
+ ( 5769, 70.00, 6.10),
+ ( 19231, 235.00, 7.00),
+ (192308, 1177.00, 9.90),
+ ( 'inf', 18312.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 2083, 12.50, 2.00),
+ ( 2917, 37.50, 2.70),
+ ( 3333, 59.99, 3.90),
+ ( 6250, 76.25, 6.10),
+ ( 20833, 254.19, 7.00),
+ (208333, 1275.00, 9.90),
+ ( 'inf', 19838.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 4167, 25.00, 2.00),
+ ( 5833, 75.00, 2.70),
+ ( 6667, 120.00, 3.90),
+ ( 12500, 153.00, 6.10),
+ ( 41667, 508.00, 7.00),
+ (416667, 2550.00, 9.90),
+ ( 'inf', 39675.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 12500, 75.00, 2.00),
+ ( 17500, 225.00, 2.70),
+ ( 20000, 360.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 125000, 1525.00, 7.00),
+ (1250000, 7650.00, 9.90),
+ ( 'inf', 119025.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 25000, 150.00, 2.00),
+ ( 35000, 450.00, 2.70),
+ ( 40000, 720.00, 3.90),
+ ( 75000, 915.00, 6.10),
+ ( 250000, 3050.00, 7.00),
+ (2500000, 15300.00, 9.90),
+ ( 'inf', 238050.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 50000, 300.00, 2.00),
+ ( 70000, 900.00, 2.70),
+ (80000, 1440.00, 3.90),
+ ( 150000, 1830.00, 6.10),
+ ( 500000, 6100.00, 7.00),
+ (5000000, 30600.00, 9.90),
+ ( 'inf', 476100.00, 11.80),
+ ),
+ },
'C': {
- 'weekly': ((385, 0.0, 1.50), (769, 5.77, 2.30), (962, 14.62, 2.80), (1154, 20.00, 3.50), (2885, 26.73, 5.60), (9615, 123.65, 6.60), (96154, 567.88, 9.90), ('inf', 9135.19, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1538, 11.54, 2.30), (1923, 29.23, 2.80), (2308, 40.00, 3.50), (5769, 53.46, 5.60), (19231, 247.31, 6.60), (192308, 1135.77, 9.90), ('inf', 18270.38, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (1667, 12.50, 2.30), (2083, 31.67, 2.80), (2500, 43.33, 3.50), (6250, 57.92, 5.60), (20833, 267.92, 6.60), (208333, 1230.42, 9.90), ('inf', 19792.92, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (3333, 25.00, 2.30), (4167, 63.33, 2.80), (5000, 86.67, 3.50), (12500, 115.83, 5.60), (41667, 535.85, 6.60), (416667, 2460.83, 9.90), ('inf', 39585.83, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (10000, 75.00, 2.30), (12500, 190.00, 2.80), (15000, 260.00, 3.50), (37500, 347.50, 5.60), (125000, 1607.50, 6.60), (1250000, 7382.50, 9.90), ('inf', 118757.50, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (20000, 150.00, 2.30), (25000, 380.00, 2.80), (30000, 520.00, 3.50), (75000, 695.00, 5.60), (250000, 3215.00, 6.60), (2500000, 14765.00, 9.90), ('inf', 237515.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (40000, 300.00, 2.30), (50000, 760.00, 2.80), (60000, 1040.00, 3.50), (150000, 1390.00, 5.60), (500000, 6430.00, 6.60), (5000000, 29530.00, 9.90), ('inf', 475030.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.30),
+ ( 962, 14.62, 2.80),
+ ( 1154, 20.00, 3.50),
+ ( 2885, 26.73, 5.60),
+ ( 9615, 123.65, 6.60),
+ (96154, 567.88, 9.90),
+ ('inf', 9135.19, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.30),
+ ( 1923, 29.23, 2.80),
+ ( 2308, 40.00, 3.50),
+ ( 5769, 53.46, 5.60),
+ ( 19231, 247.31, 6.60),
+ (192308, 1135.77, 9.90),
+ ( 'inf', 18270.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.30),
+ ( 2083, 31.67, 2.80),
+ ( 2500, 43.33, 3.50),
+ ( 6250, 57.92, 5.60),
+ ( 20833, 267.92, 6.60),
+ (208333, 1230.42, 9.90),
+ ( 'inf', 19792.92, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.30),
+ ( 4167, 63.33, 2.80),
+ ( 5000, 86.67, 3.50),
+ ( 12500, 115.83, 5.60),
+ ( 41667, 535.85, 6.60),
+ (416667, 2460.83, 9.90),
+ ( 'inf', 39585.83, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.30),
+ ( 12500, 190.00, 2.80),
+ ( 15000, 260.00, 3.50),
+ ( 37500, 347.50, 5.60),
+ ( 125000, 1607.50, 6.60),
+ (1250000, 7382.50, 9.90),
+ ( 'inf', 118757.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.30),
+ ( 25000, 380.00, 2.80),
+ ( 30000, 520.00, 3.50),
+ ( 75000, 695.00, 5.60),
+ ( 250000, 3215.00, 6.60),
+ (2500000, 14765.00, 9.90),
+ ( 'inf', 237515.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.30),
+ ( 50000, 760.00, 2.80),
+ ( 60000, 1040.00, 3.50),
+ ( 150000, 1390.00, 5.60),
+ ( 500000, 6430.00, 6.60),
+ (5000000, 29530.00, 9.90),
+ ( 'inf', 475030.00, 11.80),
+ ),
+ },
'D': {
- 'weekly': ((385, 0.0, 1.50), (769, 5.77, 2.70), (962, 16.15, 3.40), (1154, 22.69, 4.30), (2885, 30.96, 5.60), (9615, 127.88, 6.50), (96154, 565.38, 9.90), ('inf', 9132.69, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1538, 11.54, 2.70), (1923, 32.31, 3.40), (2308, 45.38, 4.30), (5769, 61.92, 5.60), (19231, 255.77, 6.50), (192308, 1130.77, 9.90), ('inf', 18265.38, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (1667, 12.50, 2.70), (2083, 35.00, 3.40), (2500, 49.17, 4.30), (6250, 67.08, 5.60), (20833, 277.08, 6.50), (208333, 1225.00, 9.90), ('inf', 19787.50, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (3333, 25.00, 2.70), (4167, 70.00, 3.40), (5000, 98.33, 4.00), (12500, 134.17, 5.60), (41667, 554.17, 6.50), (416667, 2450.00, 9.90), ('inf', 39575.00, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (10000, 75.00, 2.07), (12500, 210.00, 3.40), (15000, 295.00, 4.30), (37500, 402.50, 5.60), (125000, 1662.50, 6.50), (1250000, 7350.00, 9.90), ('inf', 118725.00, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (20000, 150.00, 2.70), (25000, 420.00, 3.40), (30000, 590.00, 4.30), (75000, 805.00, 5.60), (250000, 3325.00, 6.50), (2500000, 14700.00, 9.90), ('inf', 237450.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (40000, 300.00, 2.70), (50000, 840.00, 3.40), (60000, 1180.00, 4.30), (150000, 1610.00, 5.60), (250000, 6650.00, 6.50), (2500000, 29400.00, 9.90), ('inf', 474900.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.70),
+ ( 962, 16.15, 3.40),
+ ( 1154, 22.69, 4.30),
+ ( 2885, 30.96, 5.60),
+ ( 9615, 127.88, 6.50),
+ ( 96154, 565.38, 9.90),
+ ( 'inf', 9132.69, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.70),
+ ( 1923, 32.31, 3.40),
+ ( 2308, 45.38, 4.30),
+ ( 5769, 61.92, 5.60),
+ ( 19231, 255.77, 6.50),
+ (192308, 1130.77, 9.90),
+ ( 'inf', 18265.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.70),
+ ( 2083, 35.00, 3.40),
+ ( 2500, 49.17, 4.30),
+ ( 6250, 67.08, 5.60),
+ ( 20833, 277.08, 6.50),
+ (208333, 1225.00, 9.90),
+ ( 'inf', 19787.50, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.70),
+ ( 4167, 70.00, 3.40),
+ ( 5000, 98.33, 4.00),
+ ( 12500, 134.17, 5.60),
+ ( 41667, 554.17, 6.50),
+ (416667, 2450.00, 9.90),
+ ( 'inf', 39575.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.07),
+ ( 12500, 210.00, 3.40),
+ ( 15000, 295.00, 4.30),
+ ( 37500, 402.50, 5.60),
+ ( 125000, 1662.50, 6.50),
+ (1250000, 7350.00, 9.90),
+ ( 'inf', 118725.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.70),
+ ( 25000, 420.00, 3.40),
+ ( 30000, 590.00, 4.30),
+ ( 75000, 805.00, 5.60),
+ ( 250000, 3325.00, 6.50),
+ (2500000, 14700.00, 9.90),
+ ( 'inf', 237450.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.70),
+ ( 50000, 840.00, 3.40),
+ ( 60000, 1180.00, 4.30),
+ ( 150000, 1610.00, 5.60),
+ ( 250000, 6650.00, 6.50),
+ (2500000, 29400.00, 9.90),
+ ( 'inf', 474900.00, 11.80),
+ ),
+ },
'E': {
- 'weekly': ((385, 0.0, 1.50), (673, 5.77, 2.00), (1923, 11.54, 5.80), (9615, 84.04, 6.50), (96154, 584.04, 9.90), ('inf', 9151.35, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1346, 12.00, 2.00), (3846, 23.00, 5.80), (19231, 168.00, 6.50), (192308, 1168.00, 9.90), ('inf', 18303.00, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (1458, 13.00, 2.00), (4167, 25.00, 5.80), (20833, 182.00, 6.50), (208333, 1265.00, 9.90), ('inf', 19828.00, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (2916, 25.00, 2.00), (8333, 50.00, 5.80), (41667, 364.00, 6.50), (416667, 2531.00, 9.90), ('inf', 39656, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (8750, 75.00, 2.00), (25000, 150.00, 5.80), (125000, 1092.50, 6.50), (1250000, 7592.50, 9.90), ('inf', 118967.50, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (17500, 150.00, 2.00), (50000, 300.00, 5.80), (250000, 2185.00, 6.50), (2500000, 15185.00, 9.90), ('inf', 237935.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (35000, 300.00, 2.00), (100000, 600.00, 5.80), (500000, 4370.00, 6.50), (5000000, 30370.00, 9.90), ('inf', 475870.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 1923, 11.54, 5.80),
+ ( 9615, 84.04, 6.50),
+ ( 96154, 584.04, 9.90),
+ ( 'inf', 9151.35, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 3846, 23.00, 5.80),
+ ( 19231, 168.00, 6.50),
+ (192308, 1168.00, 9.90),
+ ( 'inf', 18303.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 4167, 25.00, 5.80),
+ ( 20833, 182.00, 6.50),
+ (208333, 1265.00, 9.90),
+ ( 'inf', 19828.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2916, 25.00, 2.00),
+ ( 8333, 50.00, 5.80),
+ ( 41667, 364.00, 6.50),
+ (416667, 2531.00, 9.90),
+ ( 'inf', 39656.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 25000, 150.00, 5.80),
+ ( 125000, 1092.50, 6.50),
+ (1250000, 7592.50, 9.90),
+ ( 'inf', 118967.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 50000, 300.00, 5.80),
+ ( 250000, 2185.00, 6.50),
+ (2500000, 15185.00, 9.90),
+ ( 'inf', 237935.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 100000, 600.00, 5.80),
+ ( 500000, 4370.00, 6.50),
+ (5000000, 30370.00, 9.90),
+ ( 'inf', 475870.00, 11.80),
+ ),
+ },
}
+
US NJ NewJersey SIT Rate Table
us_nj_sit_rate
{
'A': {
- 'weekly': ((385, 0.0, 1.50), (673, 5.77, 2.00), (769, 11.54, 3.90), (1442, 15.29, 6.10), (9615, 56.34, 7.00), (96154, 628.46, 9.90), ('inf', 9195.77, 11.80)),
- 'bi-weekly': ((769, 0.00, 1.50), (1346, 12.00, 2.00), (1538, 23.00, 3.90), (2885, 31.00, 6.10), (19231, 113.00, 7.00), (192308, 1257.00, 9.90), ('inf',18392.00, 11.80)),
- 'semi-monthly': ((833, 0.00, 1.50), (1458, 13.00, 2.00), (1667, 25.00, 3.90), (3125, 33.00, 6.10), (20833, 122.00, 57.00), (208333, 1362.00, 9.90), ('inf', 19924.00, 11.80)),
- 'monthly': ((1667, 0.00, 1.50), (2917, 25.00, 2.00), (3333, 50.00, 3.90), (6250, 66.00, 6.10), (41667, 244.00, 57.00), (416667, 2723.00, 9.90), ('inf', 39848.00, 11.80)),
- 'quarterly': ((5000, 0.00, 1.50), (8750, 75.00, 2.00), (10000, 150.00, 3.90), (18750, 198.75, 6.10), (125000, 732.50, 57.00), (1250000, 8170.00, 9.90), ('inf', 119545.00, 11.80)),
- 'semi-annual': ((10000, 0.00, 1.50), (17500, 150.00, 2.00), (20000, 300.00, 3.90), (37500, 397.50, 6.10), (250000, 1465.00, 57.00), (2500000, 16340.00, 9.90), ('inf', 239090.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (35000, 300.00, 2.00), (40000, 600.00, 3.90), (75000, 795.00, 6.10), (500000, 2930.00, 57.00), (5000000, 32680.00, 9.90), ('inf', 478180.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 769, 11.54, 3.90),
+ ( 1442, 15.29, 6.10),
+ ( 9615, 56.34, 7.00),
+ (96154, 628.46, 9.90),
+ ('inf', 9195.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 1538, 23.00, 3.90),
+ ( 2885, 31.00, 6.10),
+ ( 19231, 113.00, 7.00),
+ (192308, 1257.00, 9.90),
+ ( 'inf', 18392.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 1667, 25.00, 3.90),
+ ( 3125, 33.00, 6.10),
+ ( 20833, 122.00, 7.00),
+ (208333, 1362.00, 9.90),
+ ( 'inf', 19924.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2917, 25.00, 2.00),
+ ( 3333, 50.00, 3.90),
+ ( 6250, 66.00, 6.10),
+ ( 41667, 244.00, 7.00),
+ (416667, 2723.00, 9.90),
+ ( 'inf', 39848.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 10000, 150.00, 3.90),
+ ( 18750, 198.75, 6.10),
+ ( 125000, 732.50, 7.00),
+ (1250000, 8170.00, 9.90),
+ ( 'inf', 119545.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 20000, 300.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 250000, 1465.00, 7.00),
+ (2500000, 16340.00, 9.90),
+ ( 'inf', 239090.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 40000, 600.00, 3.90),
+ ( 75000, 795.00, 6.10),
+ ( 500000, 2930.00, 7.00),
+ (5000000, 32680.00, 9.90),
+ ( 'inf', 478180.00, 11.80),
+ ),
+ },
'B': {
- 'weekly': ((385, 0.0, 1.50), (962, 5.77, 2.00), (1346, 17.31, 2.70), (1538, 27.69, 3.9), (2885, 35.19, 6.10), (9615, 117.31, 7.00), (96154, 588.46, 9.90), ('inf', 9155.77, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1923, 12.00, 2.00), (2692, 35.00, 2.70), (3076, 55.00, 3.9), (5769, 70.00, 6.10), (19231, 235.00, 700), (192308, 1177.00, 9.90), ('inf', 18312.00, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (2083, 12.50, 2.00), (2917, 37.50, 2.70), (3333, 59.99, 3.9), (6250, 76.25, 6.10), (20833, 254.19, 7.00), (208333, 1275.00, 9.90), ('inf', 19838.00, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (4167, 25.00, 2.00), (5833, 75.00, 2.70), (6667, 120.00, 3.9), (12500, 153.00, 6.10), (41667, 508.00, 7.00), (416667, 2550.00, 9.90), ('inf', 39675.00, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (12500, 75.00, 2.00), (17500, 225.00, 2.70), (20000, 360.00, 3.9), (37500, 397.50, 6.10), (125000, 1525.00, 7.00), (1250000, 7650.00, 9.90), ('inf', 119025.00, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (25000, 150.00, 2.00), (35000, 450.00, 2.70), (40000, 720.00, 3.9), (75000, 915.00, 6.10), (250000, 3050.00, 7.00), (2500000, 15300.00, 9.90), ('inf', 238050.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (50000, 300.00, 2.00), (70000, 900.00, 2.70), (80000, 1440.00, 3.9), (150000, 1830.00, 6.10), (500000, 6100.00, 7.00), (5000000, 30600.00, 9.90), ('inf', 476100.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 962, 5.77, 2.00),
+ ( 1346, 17.31, 2.70),
+ ( 1538, 27.69, 3.90),
+ ( 2885, 35.19, 6.10),
+ ( 9615, 117.31, 7.00),
+ (96154, 588.46, 9.90),
+ ('inf', 9155.77, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1923, 12.00, 2.00),
+ ( 2692, 35.00, 2.70),
+ ( 3076, 55.00, 3.90),
+ ( 5769, 70.00, 6.10),
+ ( 19231, 235.00, 7.00),
+ (192308, 1177.00, 9.90),
+ ( 'inf', 18312.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 2083, 12.50, 2.00),
+ ( 2917, 37.50, 2.70),
+ ( 3333, 59.99, 3.90),
+ ( 6250, 76.25, 6.10),
+ ( 20833, 254.19, 7.00),
+ (208333, 1275.00, 9.90),
+ ( 'inf', 19838.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 4167, 25.00, 2.00),
+ ( 5833, 75.00, 2.70),
+ ( 6667, 120.00, 3.90),
+ ( 12500, 153.00, 6.10),
+ ( 41667, 508.00, 7.00),
+ (416667, 2550.00, 9.90),
+ ( 'inf', 39675.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 12500, 75.00, 2.00),
+ ( 17500, 225.00, 2.70),
+ ( 20000, 360.00, 3.90),
+ ( 37500, 397.50, 6.10),
+ ( 125000, 1525.00, 7.00),
+ (1250000, 7650.00, 9.90),
+ ( 'inf', 119025.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 25000, 150.00, 2.00),
+ ( 35000, 450.00, 2.70),
+ ( 40000, 720.00, 3.90),
+ ( 75000, 915.00, 6.10),
+ ( 250000, 3050.00, 7.00),
+ (2500000, 15300.00, 9.90),
+ ( 'inf', 238050.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 50000, 300.00, 2.00),
+ ( 70000, 900.00, 2.70),
+ (80000, 1440.00, 3.90),
+ ( 150000, 1830.00, 6.10),
+ ( 500000, 6100.00, 7.00),
+ (5000000, 30600.00, 9.90),
+ ( 'inf', 476100.00, 11.80),
+ ),
+ },
'C': {
- 'weekly': ((385, 0.0, 1.50), (769, 5.77, 2.30), (962, 14.62, 2.80), (1154, 20.00, 3.50), (2885, 26.73, 5.60), (9615, 123.65, 6.60), (96154, 567.88, 9.90), ('inf', 9135.19, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1538, 11.54, 2.30), (1923, 29.23, 2.80), (2308, 40.00, 3.50), (5769, 53.46, 5.60), (19231, 247.31, 6.60), (192308, 1135.77, 9.90), ('inf', 18270.38, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (1667, 12.50, 2.30), (2083, 31.67, 2.80), (2500, 43.33, 3.50), (6250, 57.92, 5.60), (20833, 267.92, 6.60), (208333, 1230.42, 9.90), ('inf', 19792.92, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (3333, 25.00, 2.30), (4167, 63.33, 2.80), (5000, 86.67, 3.50), (12500, 115.83, 5.60), (41667, 535.85, 6.60), (416667, 2460.83, 9.90), ('inf', 39585.83, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (10000, 75.00, 2.30), (12500, 190.00, 2.80), (15000, 260.00, 3.50), (37500, 347.50, 5.60), (125000, 1607.50, 6.60), (1250000, 7382.50, 9.90), ('inf', 118757.50, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (20000, 150.00, 2.30), (25000, 380.00, 2.80), (30000, 520.00, 3.50), (75000, 695.00, 5.60), (250000, 3215.00, 6.60), (2500000, 14765.00, 9.90), ('inf', 237515.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (40000, 300.00, 2.30), (50000, 760.00, 2.80), (60000, 1040.00, 3.50), (150000, 1390.00, 5.60), (500000, 6430.00, 6.60), (5000000, 29530.00, 9.90), ('inf', 475030.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.30),
+ ( 962, 14.62, 2.80),
+ ( 1154, 20.00, 3.50),
+ ( 2885, 26.73, 5.60),
+ ( 9615, 123.65, 6.60),
+ (96154, 567.88, 9.90),
+ ('inf', 9135.19, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.30),
+ ( 1923, 29.23, 2.80),
+ ( 2308, 40.00, 3.50),
+ ( 5769, 53.46, 5.60),
+ ( 19231, 247.31, 6.60),
+ (192308, 1135.77, 9.90),
+ ( 'inf', 18270.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.30),
+ ( 2083, 31.67, 2.80),
+ ( 2500, 43.33, 3.50),
+ ( 6250, 57.92, 5.60),
+ ( 20833, 267.92, 6.60),
+ (208333, 1230.42, 9.90),
+ ( 'inf', 19792.92, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.30),
+ ( 4167, 63.33, 2.80),
+ ( 5000, 86.67, 3.50),
+ ( 12500, 115.83, 5.60),
+ ( 41667, 535.85, 6.60),
+ (416667, 2460.83, 9.90),
+ ( 'inf', 39585.83, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.30),
+ ( 12500, 190.00, 2.80),
+ ( 15000, 260.00, 3.50),
+ ( 37500, 347.50, 5.60),
+ ( 125000, 1607.50, 6.60),
+ (1250000, 7382.50, 9.90),
+ ( 'inf', 118757.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.30),
+ ( 25000, 380.00, 2.80),
+ ( 30000, 520.00, 3.50),
+ ( 75000, 695.00, 5.60),
+ ( 250000, 3215.00, 6.60),
+ (2500000, 14765.00, 9.90),
+ ( 'inf', 237515.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.30),
+ ( 50000, 760.00, 2.80),
+ ( 60000, 1040.00, 3.50),
+ ( 150000, 1390.00, 5.60),
+ ( 500000, 6430.00, 6.60),
+ (5000000, 29530.00, 9.90),
+ ( 'inf', 475030.00, 11.80),
+ ),
+ },
'D': {
- 'weekly': ((385, 0.0, 1.50), (769, 5.77, 2.70), (962, 16.15, 3.40), (1154, 22.69, 4.30), (2885, 30.96, 5.60), (9615, 127.88, 6.50), (96154, 565.38, 9.90), ('inf', 9132.69, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1538, 11.54, 2.70), (1923, 32.31, 3.40), (2308, 45.38, 4.30), (5769, 61.92, 5.60), (19231, 255.77, 6.50), (192308, 1130.77, 9.90), ('inf', 18265.38, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (1667, 12.50, 2.70), (2083, 35.00, 3.40), (2500, 49.17, 4.30), (6250, 67.08, 5.60), (20833, 277.08, 6.50), (208333, 1225.00, 9.90), ('inf', 19787.50, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (3333, 25.00, 2.70), (4167, 70.00, 3.40), (5000, 98.33, 4.00), (12500, 134.17, 5.60), (41667, 554.17, 6.50), (416667, 2450.00, 9.90), ('inf', 39575.00, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (10000, 75.00, 2.07), (12500, 210.00, 3.40), (15000, 295.00, 4.30), (37500, 402.50, 5.60), (125000, 1662.50, 6.50), (1250000, 7350.00, 9.90), ('inf', 118725.00, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (20000, 150.00, 2.70), (25000, 420.00, 3.40), (30000, 590.00, 4.30), (75000, 805.00, 5.60), (250000, 3325.00, 6.50), (2500000, 14700.00, 9.90), ('inf', 237450.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (40000, 300.00, 2.70), (50000, 840.00, 3.40), (60000, 1180.00, 4.30), (150000, 1610.00, 5.60), (250000, 6650.00, 6.50), (2500000, 29400.00, 9.90), ('inf', 474900.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 769, 5.77, 2.70),
+ ( 962, 16.15, 3.40),
+ ( 1154, 22.69, 4.30),
+ ( 2885, 30.96, 5.60),
+ ( 9615, 127.88, 6.50),
+ ( 96154, 565.38, 9.90),
+ ( 'inf', 9132.69, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1538, 11.54, 2.70),
+ ( 1923, 32.31, 3.40),
+ ( 2308, 45.38, 4.30),
+ ( 5769, 61.92, 5.60),
+ ( 19231, 255.77, 6.50),
+ (192308, 1130.77, 9.90),
+ ( 'inf', 18265.38, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1667, 12.50, 2.70),
+ ( 2083, 35.00, 3.40),
+ ( 2500, 49.17, 4.30),
+ ( 6250, 67.08, 5.60),
+ ( 20833, 277.08, 6.50),
+ (208333, 1225.00, 9.90),
+ ( 'inf', 19787.50, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 3333, 25.00, 2.70),
+ ( 4167, 70.00, 3.40),
+ ( 5000, 98.33, 4.00),
+ ( 12500, 134.17, 5.60),
+ ( 41667, 554.17, 6.50),
+ (416667, 2450.00, 9.90),
+ ( 'inf', 39575.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 10000, 75.00, 2.07),
+ ( 12500, 210.00, 3.40),
+ ( 15000, 295.00, 4.30),
+ ( 37500, 402.50, 5.60),
+ ( 125000, 1662.50, 6.50),
+ (1250000, 7350.00, 9.90),
+ ( 'inf', 118725.00, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 20000, 150.00, 2.70),
+ ( 25000, 420.00, 3.40),
+ ( 30000, 590.00, 4.30),
+ ( 75000, 805.00, 5.60),
+ ( 250000, 3325.00, 6.50),
+ (2500000, 14700.00, 9.90),
+ ( 'inf', 237450.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 40000, 300.00, 2.70),
+ ( 50000, 840.00, 3.40),
+ ( 60000, 1180.00, 4.30),
+ ( 150000, 1610.00, 5.60),
+ ( 250000, 6650.00, 6.50),
+ (2500000, 29400.00, 9.90),
+ ( 'inf', 474900.00, 11.80),
+ ),
+ },
'E': {
- 'weekly': ((385, 0.0, 1.50), (673, 5.77, 2.00), (1923, 11.54, 5.80), (9615, 84.04, 6.50), (96154, 584.04, 9.90), ('inf', 9151.35, 11.80)),
- 'bi-weekly': ((769, 0.0, 1.50), (1346, 12.00, 2.00), (3846, 23.00, 5.80), (19231, 168.00, 6.50), (192308, 1168.00, 9.90), ('inf', 18303.00, 11.80)),
- 'semi-monthly': ((833, 0.0, 1.50), (1458, 13.00, 2.00), (4167, 25.00, 5.80), (20833, 182.00, 6.50), (208333, 1265.00, 9.90), ('inf', 19828.00, 11.80)),
- 'monthly': ((1667, 0.0, 1.50), (2916, 25.00, 2.00), (8333, 50.00, 5.80), (41667, 364.00, 6.50), (416667, 2531.00, 9.90), ('inf', 39656, 11.80)),
- 'quarterly': ((5000, 0.0, 1.50), (8750, 75.00, 2.00), (25000, 150.00, 5.80), (125000, 1092.50, 6.50), (1250000, 7592.50, 9.90), ('inf', 118967.50, 11.80)),
- 'semi-annual': ((10000, 0.0, 1.50), (17500, 150.00, 2.00), (50000, 300.00, 5.80), (250000, 2185.00, 6.50), (2500000, 15185.00, 9.90), ('inf', 237935.00, 11.80)),
- 'annual': ((20000, 0.0, 1.50), (35000, 300.00, 2.00), (100000, 600.00, 5.80), (500000, 4370.00, 6.50), (5000000, 30370.00, 9.90), ('inf', 475870.00, 11.80)),
- },
+ 'weekly': (
+ ( 385, 0.00, 1.50),
+ ( 673, 5.77, 2.00),
+ ( 1923, 11.54, 5.80),
+ ( 9615, 84.04, 6.50),
+ ( 96154, 584.04, 9.90),
+ ( 'inf', 9151.35, 11.80),
+ ),
+ 'bi-weekly': (
+ ( 769, 0.00, 1.50),
+ ( 1346, 12.00, 2.00),
+ ( 3846, 23.00, 5.80),
+ ( 19231, 168.00, 6.50),
+ (192308, 1168.00, 9.90),
+ ( 'inf', 18303.00, 11.80),
+ ),
+ 'semi-monthly': (
+ ( 833, 0.00, 1.50),
+ ( 1458, 13.00, 2.00),
+ ( 4167, 25.00, 5.80),
+ ( 20833, 182.00, 6.50),
+ (208333, 1265.00, 9.90),
+ ( 'inf', 19828.00, 11.80),
+ ),
+ 'monthly': (
+ ( 1667, 0.00, 1.50),
+ ( 2916, 25.00, 2.00),
+ ( 8333, 50.00, 5.80),
+ ( 41667, 364.00, 6.50),
+ (416667, 2531.00, 9.90),
+ ( 'inf', 39656.00, 11.80),
+ ),
+ 'quarterly': (
+ ( 5000, 0.00, 1.50),
+ ( 8750, 75.00, 2.00),
+ ( 25000, 150.00, 5.80),
+ ( 125000, 1092.50, 6.50),
+ (1250000, 7592.50, 9.90),
+ ( 'inf', 118967.50, 11.80),
+ ),
+ 'semi-annual': (
+ ( 10000, 0.00, 1.50),
+ ( 17500, 150.00, 2.00),
+ ( 50000, 300.00, 5.80),
+ ( 250000, 2185.00, 6.50),
+ (2500000, 15185.00, 9.90),
+ ( 'inf', 237935.00, 11.80),
+ ),
+ 'annual': (
+ ( 20000, 0.00, 1.50),
+ ( 35000, 300.00, 2.00),
+ ( 100000, 600.00, 5.80),
+ ( 500000, 4370.00, 6.50),
+ (5000000, 30370.00, 9.90),
+ ( 'inf', 475870.00, 11.80),
+ ),
+ },
}
@@ -265,6 +854,7 @@
}
+
US NJ NewJersey SIT Allowance Rate
us_nj_sit_allowance_rate
diff --git a/l10n_us_hr_payroll/data/state/nm_new_mexico.xml b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
index 9eeadfa9..66c9cc36 100644
--- a/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
+++ b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
@@ -21,37 +21,227 @@
+
US NM New Mexico SIT Tax Rate
us_nm_sit_tax_rate
{
'single': {
- 'weekly': ((119, 0.00, 0.0), (225, 0.00, 1.7), (331, 1.80, 3.2), (427, 5.18, 4.7), (619, 9.70, 4.9), (927, 19.13, 4.9), (1369, 34.20, 4.9), ('inf', 55.88, 4.9)),
- 'bi-weekly': ((238, 0.00, 0.0), (450, 0.00, 1.7), (662, 3.60, 3.2), (854, 10.37, 4.7), (1238, 19.40, 4.9), (1854, 38.25, 4.9), (2738, 68.40, 4.9), ('inf', 111.75, 4.9)),
- 'semi-monthly': ((258, 0.00, 0.0), (488, 0.00, 1.7), (717, 3.90, 3.2), (925, 11.23, 4.7), (1342, 21.02, 4.9), (2008, 41.44, 4.9), (2967, 74.10, 4.9), ('inf', 121.06, 4.9)),
- 'monthly': ((517, 0.00, 0.0), (975, 0.00, 1.7), (1433, 7.79, 3.2), (1850, 22.46, 4.7), (2683, 42.04, 4.9), (4017, 82.88, 4.9), (5933, 148.21, 4.9), ('inf', 242.13, 4.9)),
- 'quarterly': ((1550, 0.00, 0.0), (2925, 0.00, 1.7), (4300, 23.38, 3.2), (5550, 67.38, 4.7), (8050, 126.13, 4.9), (12050, 248.63, 4.9), (17800, 444.63, 4.9), ('inf', 726.38, 4.9)),
- 'semi-annual': ((3100, 0.00, 0.0), (5850, 0.00, 1.7), (8600, 46.75, 3.2), (11100, 134.75, 4.7), (16100, 252.25, 4.9), (24100, 497.25, 4.9), (35600, 889.25, 4.9), ('inf', 1452.75, 4.9)),
- 'annually': ((6200, 0.00, 0.0), (11700, 0.00, 1.7), (17200, 93.50, 3.2), (22200, 269.50, 4.7), (32200, 504.50, 4.9), (48200, 994.50, 4.9), (71200, 1778.50, 4.9), ('inf', 2905.50, 4.9)),
- },
++ 'weekly': (
++ ( 119, 0.00, 0.0),
++ ( 225, 0.00, 1.7),
++ ( 331, 1.80, 3.2),
++ ( 427, 5.18, 4.7),
++ ( 619, 9.70, 4.9),
++ ( 927, 19.13, 4.9),
++ ( 1369, 34.20, 4.9),
++ ('inf', 55.88, 4.9),
++ ),
++ 'bi-weekly': (
++ ( 238, 0.00, 0.0),
++ ( 450, 0.00, 1.7),
++ ( 662, 3.60, 3.2),
++ ( 854, 10.37, 4.7),
++ ( 1238, 19.40, 4.9),
++ ( 1854, 38.25, 4.9),
++ ( 2738, 68.40, 4.9),
++ ('inf', 111.75, 4.9),
++ ),
++ 'semi-monthly': (
++ ( 258, 0.00, 0.0),
++ ( 488, 0.00, 1.7),
++ ( 717, 3.90, 3.2),
++ ( 925, 11.23, 4.7),
++ ( 1342, 21.02, 4.9),
++ ( 2008, 41.44, 4.9),
++ ( 2967, 74.10, 4.9),
++ ('inf', 121.06, 4.9),
++ ),
++ 'monthly': (
++ ( 517, 0.00, 0.0),
++ ( 975, 0.00, 1.7),
++ ( 1433, 7.79, 3.2),
++ ( 1850, 22.46, 4.7),
++ ( 2683, 42.04, 4.9),
++ ( 4017, 82.88, 4.9),
++ ( 5933, 148.21, 4.9),
++ ('inf', 242.13, 4.9),
++ ),
++ 'quarterly': (
++ ( 1550, 0.00, 0.0),
++ ( 2925, 0.00, 1.7),
++ ( 4300, 23.38, 3.2),
++ ( 5550, 67.38, 4.7),
++ ( 8050, 126.13, 4.9),
++ ( 12050, 248.63, 4.9),
++ ( 17800, 444.63, 4.9),
++ ( 'inf', 726.38, 4.9),
++ ),
++ 'semi-annual': (
++ ( 3100, 0.00, 0.0),
++ ( 5850, 0.00, 1.7),
++ ( 8600, 46.75, 3.2),
++ (11100, 134.75, 4.7),
++ (16100, 252.25, 4.9),
++ (24100, 497.25, 4.9),
++ (35600, 889.25, 4.9),
++ ('inf', 1452.75, 4.9),
++ ),
++ 'annually': (
++ ( 6200, 0.00, 0.0),
++ (11700, 0.00, 1.7),
++ (17200, 93.50, 3.2),
++ (22200, 269.50, 4.7),
++ (32200, 504.50, 4.9),
++ (48200, 994.50, 4.9),
++ (71200, 1778.50, 4.9),
++ ('inf', 2905.50, 4.9),
++ ),
+ },
'married': {
- 'weekly': ((238, 0.00, 0.0), (392, 0.00, 1.7), (546, 2.62, 3.2), (700, 7.54, 4.7), (1008, 14.77, 4.9), (1469, 29.85, 4.9), (2162, 52.46, 4.9), ('inf', 86.38, 4.9)),
- 'bi-weekly': ((477, 0.00, 0.0), (785, 0.00, 1.7), (1092, 5.23, 3.2), (1400, 15.08, 4.7), (2015, 29.54, 4.9), (2938, 59.69, 4.9), (4323, 104.92, 4.9), ('inf', 172.77, 4.9)),
- 'semi-monthly': ((517, 0.00, 0.0), (850, 0.00, 1.7), (1183, 5.67, 3.2), (1517, 16.33, 4.7), (2183, 32.00, 4.9), (3183, 64.67, 4.9), (4683, 113.67, 4.9), ('inf', 187.17, 4.9)),
- 'monthly': ((1033, 0.00, 0.0), (1700, 0.00, 1.7), (2367, 11.33, 3.2), (3033, 32.67, 4.7), (4367, 64.00, 4.9), (6367, 129.33, 4.9), (9367, 227.33, 4.9), ('inf', 374.33, 4.9)),
- 'quarterly': ((3100, 0.00, 0.0), (5100, 0.00, 1.7), (7100, 34.00, 3.2), (9100, 98.00, 4.7), (13100, 192.00, 4.9), (19100, 388.00, 4.9), (28100, 682.00, 4.9), ('inf', 1123.00, 4.9)),
- 'semi-annual': ((6200, 0.00, 0.0), (10200, 0.00, 1.7), (14200, 68.00, 3.2), (18200, 196.00, 4.7), (26200, 384.00, 4.9), (38200, 776.00, 4.9), (56200, 1364.00, 4.9), ('inf', 2246.00, 4.9)),
- 'annually': ((12400, 0.00, 0.0), (20400, 0.00, 1.7), (28400, 136.00, 3.2), (36400, 392.00, 4.7), (52400, 768.00, 4.9), (76400, 1552.00, 4.9), (112400, 2728.00, 4.9), ('inf', 4492.00, 4.9)),
- },
++ 'weekly': (
++ ( 238, 0.00, 0.0),
++ ( 392, 0.00, 1.7),
++ ( 546, 2.62, 3.2),
++ ( 700, 7.54, 4.7),
++ ( 1008, 14.77, 4.9),
++ ( 1469, 29.85, 4.9),
++ ( 2162, 52.46, 4.9),
++ ('inf', 86.38, 4.9),
++ ),
++ 'bi-weekly': (
++ ( 477, 0.00, 0.0),
++ ( 785, 0.00, 1.7),
++ ( 1092, 5.23, 3.2),
++ ( 1400, 15.08, 4.7),
++ (2015, 29.54, 4.9),
++ ( 2938, 59.69, 4.9),
++ ( 4323, 104.92, 4.9),
++ ('inf', 172.77, 4.9),
++ ),
++ 'semi-monthly': (
++ ( 517, 0.00, 0.0),
++ ( 850, 0.00, 1.7),
++ ( 1183, 5.67, 3.2),
++ ( 1517, 16.33, 4.7),
++ ( 2183, 32.00, 4.9),
++ ( 3183, 64.67, 4.9),
++ ( 4683, 113.67, 4.9),
++ ('inf', 187.17, 4.9),
++ ),
++ 'monthly': (
++ ( 1033, 0.00, 0.0),
++ ( 1700, 0.00, 1.7),
++ ( 2367, 11.33, 3.2),
++ ( 3033, 32.67, 4.7),
++ ( 4367, 64.00, 4.9),
++ ( 6367, 129.33, 4.9),
++ ( 9367, 227.33, 4.9),
++ ('inf', 374.33, 4.9),
++ ),
++ 'quarterly': (
++ ( 3100, 0.00, 0.0),
++ ( 5100, 0.00, 1.7),
++ ( 7100, 34.00, 3.2),
++ ( 9100, 98.00, 4.7),
++ (13100, 192.00, 4.9),
++ (19100, 388.00, 4.9),
++ (28100, 682.00, 4.9),
++ ('inf', 1123.00, 4.9),
++ ),
++ 'semi-annual': (
++ ( 6200, 0.00, 0.0),
++ (10200, 0.00, 1.7),
++ (14200, 68.00, 3.2),
++ (18200, 196.00, 4.7),
++ (26200, 384.00, 4.9),
++ (38200, 776.00, 4.9),
++ (56200, 1364.00, 4.9),
++ ('inf', 2246.00, 4.9),
++ ),
++ 'annually': (
++ ( 12400, 0.00, 0.0),
++ ( 20400, 0.00, 1.7),
++ ( 28400, 136.00, 3.2),
++ ( 36400, 392.00, 4.7),
++ ( 52400, 768.00, 4.9),
++ ( 76400, 1552.00, 4.9),
++ (112400, 2728.00, 4.9),
++ ( 'inf', 4492.00, 4.9),
++ ),
+ },
'married_as_single': {
- 'weekly': ((179, 0.00, 0.0), (333, 0.00, 1.7), (487, 2.62, 3.2), (641, 7.54, 4.7), (949, 14.77, 4.9), (1410, 29.85, 4.9), (2102, 52.46, 4.9), ('inf', 86.38, 4.9)),
- 'bi-weekly': ((359, 0.00, 0.0), (666, 0.00, 1.7), (974, 5.23, 3.2), (1282, 15.08, 4.7), (1897, 29.54, 4.9), (2820, 59.69, 4.9), (4205, 104.92, 4.9), ('inf', 172.77, 4.9)),
- 'semi-monthly': ((389, 0.00, 0.0), (722, 0.00, 1.7), (1055, 5.67, 3.2), (1389, 16.33, 4.7), (2055, 32.00, 4.9), (3055, 64.67, 4.9), (4555, 113.67, 4.9), ('inf', 187.17, 4.9)),
- 'monthly': ((777, 0.00, 0.0), (1444, 0.00, 1.7), (2110, 11.33, 3.2), (2777, 32.67, 4.7), (4110, 64.00, 4.9), (6110, 129.33, 4.9), (9110, 227.33, 4.9), ('inf', 374.33, 4.9)),
- 'quarterly': ((2331, 0.00, 0.0), (4331, 0.00, 1.7), (6331, 34.00, 3.2), (8331, 98.00, 4.7), (12331, 192.00, 4.9), (18331, 388.00, 4.9), (27331, 682.00, 4.9), ('inf', 1123.00, 4.9)),
- 'semi-annual': ((4663, 0.00, 0.0), (8663, 0.00, 1.7), (12663, 68.00, 3.2), (16663, 196.00, 4.7), (24663, 384.00, 4.9), (36663, 776.00, 4.9), (54663, 1364.00, 4.9), ('inf', 2246.00, 4.9)),
- 'annually': ((9325, 0.00, 0.0), (17325, 0.00, 1.7), (25325, 136.00, 3.2), (33325, 392.00, 4.7), (49325, 768.00, 4.9), (73325, 1552.00, 4.9), (109325, 2728.00, 4.9), ('inf', 4492.00, 4.9)),
- }
++ 'weekly': (
++ ( 179, 0.00, 0.0),
++ ( 333, 0.00, 1.7),
++ ( 487, 2.62, 3.2),
++ ( 641, 7.54, 4.7),
++ ( 949, 14.77, 4.9),
++ ( 1410, 29.85, 4.9),
++ ( 2102, 52.46, 4.9),
++ ('inf', 86.38, 4.9),
++ ),
++ 'bi-weekly': (
++ ( 359, 0.00, 0.0),
++ ( 666, 0.00, 1.7),
++ ( 974, 5.23, 3.2),
++ ( 1282, 15.08, 4.7),
++ ( 1897, 29.54, 4.9),
++ ( 2820, 59.69, 4.9),
++ ( 4205, 104.92, 4.9),
++ ('inf', 172.77, 4.9),
++ ),
++ 'semi-monthly': (
++ ( 389, 0.00, 0.0),
++ ( 722, 0.00, 1.7),
++ ( 1055, 5.67, 3.2),
++ ( 1389, 16.33, 4.7),
++ ( 2055, 32.00, 4.9),
++ ( 3055, 64.67, 4.9),
++ ( 4555, 113.67, 4.9),
++ ('inf', 187.17, 4.9),
++ ),
++ 'monthly': (
++ ( 777, 0.00, 0.0),
++ ( 1444, 0.00, 1.7),
++ ( 2110, 11.33, 3.2),
++ ( 2777, 32.67, 4.7),
++ ( 4110, 64.00, 4.9),
++ ( 6110, 129.33, 4.9),
++ ( 9110, 227.33, 4.9),
++ ('inf', 374.33, 4.9),
++ ),
++ 'quarterly': (
++ ( 2331, 0.00, 0.0),
++ ( 4331, 0.00, 1.7),
++ ( 6331, 34.00, 3.2),
++ ( 8331, 98.00, 4.7),
++ ( 12331, 192.00, 4.9),
++ ( 18331, 388.00, 4.9),
++ ( 27331, 682.00, 4.9),
++ ( 'inf', 1123.00, 4.9),
++ ),
++ 'semi-annual': (
++ ( 4663, 0.00, 0.0),
++ ( 8663, 0.00, 1.7),
++ ( 12663, 68.00, 3.2),
++ ( 16663, 196.00, 4.7),
++ ( 24663, 384.00, 4.9),
++ ( 36663, 776.00, 4.9),
++ ( 54663, 1364.00, 4.9),
++ ( 'inf', 2246.00, 4.9),
++ ),
++ 'annually': (
++ ( 9325, 0.00, 0.0),
++ ( 17325, 0.00, 1.7),
++ ( 25325, 136.00, 3.2),
++ ( 33325, 392.00, 4.7),
++ ( 49325, 768.00, 4.9),
++ ( 73325, 1552.00, 4.9),
++ (109325, 2728.00, 4.9),
++ ( 'inf', 4492.00, 4.9),
++ ),
+ },
}
diff --git a/l10n_us_hr_payroll/data/state/nv_nevada.xml b/l10n_us_hr_payroll/data/state/nv_nevada.xml
new file mode 100644
index 00000000..59022d7c
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/nv_nevada.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+ US NV Nevada SUTA Wage Base
+ us_nv_suta_wage_base
+ 32500.00
+
+
+
+
+
+
+
+ US NV Nevada SUTA Rate
+ us_nv_suta_rate
+ 2.95
+
+
+
+
+
+
+ US Nevada - Department of Employment, Training, and Rehabilitation, Employment Security Division - Unemployment Tax
+ 1
+
+
+ US Nevada - Department of Employment, Training, and Rehabilitation, Employment Security Division - Unemployment Tax
+
+
+
+
+
+
+
+
+
+ ER: US NV Nevada State Unemployment (RT-6)
+ ER_US_NV_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nv_suta_wage_base', rate='us_nv_suta_rate', state_code='NV')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_nv_suta_wage_base', rate='us_nv_suta_rate', state_code='NV')
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ny_new_york.xml b/l10n_us_hr_payroll/data/state/ny_new_york.xml
index f721ff5a..73010b8b 100644
--- a/l10n_us_hr_payroll/data/state/ny_new_york.xml
+++ b/l10n_us_hr_payroll/data/state/ny_new_york.xml
@@ -251,7 +251,7 @@
(2205350, 0.9454, 161786.00),
( 'inf', 0.0962, 209056.00),
),
- }
+ },
}
diff --git a/l10n_us_hr_payroll/data/state/ok_oklahoma.xml b/l10n_us_hr_payroll/data/state/ok_oklahoma.xml
new file mode 100644
index 00000000..86deb82d
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ok_oklahoma.xml
@@ -0,0 +1,294 @@
+
+
+
+
+
+ US OK Oklahoma SUTA Wage Base
+ us_ok_suta_wage_base
+ 18700.0
+
+
+
+
+
+
+
+ US OK Oklahoma SUTA Rate
+ us_ok_suta_rate
+ 1.5
+
+
+
+
+
+
+ US OK Oklahoma Allowances Rate
+ us_ok_sit_allowances_rate
+ {
+ 'weekly' : 19.23,
+ 'bi-weekly' : 38.46,
+ 'semi-monthly': 41.67,
+ 'monthly' : 83.33,
+ 'quarterly' : 250.00,
+ 'semi-annual': 500.00,
+ 'annually': 1000.00,
+ }
+
+
+
+
+
+
+
+ US OK Oklahoma SIT Tax Rate
+ us_ok_sit_tax_rate
+ {
+ 'single': {
+ 'weekly': (
+ ( 122, 0.00, 0.00),
+ ( 141, 0.50, 0.00),
+ ( 170, 1.00, 0.10),
+ ( 194, 2.00, 0.38),
+ ( 216, 3.00, 0.87),
+ ( 261, 4.00, 1.53),
+ ('inf', 5.00, 3.30),
+ ),
+ 'bi-weekly': (
+ ( 244, 0.00, 0.00),
+ ( 283, 0.50, 0.00),
+ ( 340, 1.00, 0.19),
+ ( 388, 2.00, 0.77),
+ ( 433, 3.00, 1.73),
+ ( 521, 4.00, 3.06),
+ ('inf', 5.00, 6.60),
+ ),
+ 'semi-monthly': (
+ ( 265, 0.00, 0.00),
+ ( 306, 0.50, 0.00),
+ ( 369, 1.00, 0.21),
+ ( 421, 2.00, 0.83),
+ ( 469, 3.00, 1.88),
+ ( 565, 4.00, 3.31),
+ ('inf', 5.00, 7.15),
+ ),
+ 'monthly': (
+ ( 529, 0.00, 0.00),
+ ( 613, 0.50, 0.00),
+ ( 738, 1.00, 0.42),
+ ( 842, 2.00, 1.67),
+ ( 938, 3.00, 3.75),
+ (1129, 4.00, 6.63),
+ ('inf', 5.00, 14.29),
+ ),
+ 'quarterly': (
+ ( 1588, 0.00, 0.00),
+ ( 1838, 0.50, 0.00),
+ ( 2213, 1.00, 1.25),
+ ( 2525, 2.00, 5.00),
+ ( 2813, 3.00, 11.25),
+ ( 3388, 4.00, 19.88),
+ ('inf', 5.00, 42.88),
+ ),
+ 'semi-annual': (
+ ( 3175, 0.00, 0.00),
+ ( 3675, 0.50, 0.00),
+ ( 4425, 1.00, 2.50),
+ ( 5050, 2.00, 10.00),
+ (5625, 3.00, 22.50),
+ ( 6775, 4.00, 39.75),
+ ('inf', 5.00, 85.75),
+ ),
+ 'annually': (
+ ( 6350, 0.00, 0.00),
+ ( 7350, 0.50, 0.00),
+ ( 8850, 1.00, 5.00),
+ (10100, 2.00, 20.00),
+ (11250, 3.00, 45.00),
+ (13550, 4.00, 79.50),
+ ('inf', 5.00, 171.50),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 244, 0.00, 0.00),
+ ( 283, 0.50, 0.00),
+ ( 340, 1.00, 0.19),
+ ( 388, 2.00, 0.77),
+ ( 433, 3.00, 1.73),
+ ( 479, 4.00, 3.06),
+ ('inf', 5.00, 4.90),
+ ),
+ 'bi-weekly': (
+ ( 488, 0.00, 0.00),
+ ( 565, 0.50, 0.00),
+ ( 681, 1.00, 0.38),
+ ( 777, 2.00, 1.54),
+ ( 865, 3.00, 3.46),
+ ( 958, 4.00, 6.12),
+ ('inf', 5.00, 9.81),
+ ),
+ 'semi-monthly': (
+ ( 529, 0.00, 0.00),
+ ( 613, 0.50, 0.00),
+ ( 738, 1.00, 0.42),
+ ( 842, 2.00, 1.67),
+ ( 938, 3.00, 3.75),
+ ( 1038, 4.00, 6.63),
+ ('inf', 5.00, 10.63),
+ ),
+ 'monthly': (
+ ( 1058, 0.00, 0.00),
+ ( 1225, 0.50, 0.00),
+ ( 1475, 1.00, 0.83),
+ ( 1683, 2.00, 3.33),
+ ( 1875, 3.00, 7.50),
+ ( 2075, 4.00, 13.25),
+ ('inf', 5.00, 21.25),
+ ),
+ 'quarterly': (
+ ( 3175, 0.00, 0.00),
+ ( 3675, 0.50, 0.00),
+ ( 4425, 1.00, 2.50),
+ ( 5050, 2.00, 10.00),
+ ( 5625, 3.00, 22.50),
+ ( 6225, 4.00, 39.75),
+ ('inf', 5.00, 63.75),
+ ),
+ 'semi-annual': (
+ ( 6350, 0.00, 0.00),
+ ( 7350, 0.50, 0.00),
+ ( 8850, 1.00, 5.00),
+ ( 10100, 2.00, 20.00),
+ ( 11250, 3.00, 45.00),
+ ( 12450, 4.00, 79.50),
+ ( 'inf', 5.00, 127.50),
+ ),
+ 'annually': (
+ ( 12700, 0.00, 0.00),
+ ( 14700, 0.50, 0.00),
+ ( 17700, 1.00, 10.00),
+ ( 20200, 2.00, 40.00),
+ ( 22500, 3.00, 90.00),
+ ( 24900, 4.00, 159.00),
+ ( 'inf', 5.00, 255.00),
+ ),
+ },
+ 'head_household': {
+ 'weekly': (
+ ( 122, 0.00, 0.00),
+ ( 141, 0.50, 0.00),
+ ( 170, 1.00, 0.10),
+ ( 194, 2.00, 0.38),
+ ( 216, 3.00, 0.87),
+ ( 261, 4.00, 1.53),
+ ('inf', 5.00, 3.30),
+ ),
+ 'bi-weekly': (
+ ( 244, 0.00, 0.00),
+ ( 283, 0.50, 0.00),
+ ( 340, 1.00, 0.19),
+ ( 388, 2.00, 0.77),
+ ( 433, 3.00, 1.73),
+ ( 521, 4.00, 3.06),
+ ('inf', 5.00, 6.60),
+ ),
+ 'semi-monthly': (
+ ( 265, 0.00, 0.00),
+ ( 306, 0.50, 0.00),
+ ( 369, 1.00, 0.21),
+ ( 421, 2.00, 0.83),
+ ( 469, 3.00, 1.88),
+ ( 565, 4.00, 3.31),
+ ('inf', 5.00, 7.15),
+ ),
+ 'monthly': (
+ ( 529, 0.00, 0.00),
+ ( 613, 0.50, 0.00),
+ ( 738, 1.00, 0.42),
+ ( 842, 2.00, 1.67),
+ ( 938, 3.00, 3.75),
+ ( 1129, 4.00, 6.63),
+ ('inf', 5.00, 14.29),
+ ),
+ 'quarterly': (
+ ( 1588, 0.00, 0.00),
+ ( 1838, 0.50, 0.00),
+ ( 2213, 1.00, 1.25),
+ ( 2525, 2.00, 5.00),
+ ( 2813, 3.00, 11.25),
+ ( 3388, 4.00, 19.88),
+ ('inf', 5.00, 42.88),
+ ),
+ 'semi-annual': (
+ ( 3175, 0.00, 0.00),
+ ( 3675, 0.50, 0.00),
+ ( 4425, 1.00, 2.50),
+ ( 5050, 2.00, 10.00),
+ ( 5625, 3.00, 22.50),
+ ( 6775, 4.00, 39.75),
+ ('inf', 5.00, 85.75),
+ ),
+ 'annually': (
+ ( 6350, 0.00, 0.00),
+ ( 7350, 0.50, 0.00),
+ ( 8850, 1.00, 5.00),
+ (10100, 2.00, 20.00),
+ (11250, 3.00, 45.00),
+ (13550, 4.00, 79.50),
+ ('inf', 5.00, 171.50),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US Oklahoma - Employment Security Commission - Unemployment Tax
+ 1
+
+
+ US Oklahoma - Employment Security Commission - Unemployment Tax
+
+
+
+
+ US Oklahoma - Tax Commission - Income Tax
+ 1
+
+
+ US Oklahoma - Tax Commission - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US OK Oklahoma State Unemployment
+ ER_US_OK_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ok_suta_wage_base', rate='us_ok_suta_rate', state_code='OK')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ok_suta_wage_base', rate='us_ok_suta_rate', state_code='OK')
+
+
+
+
+
+
+
+ EE: US OK Oklahoma State Income Tax Withholding
+ EE_US_OK_SIT
+ python
+ result, _ = ok_oklahoma_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ok_oklahoma_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/ri_rhode_island.xml b/l10n_us_hr_payroll/data/state/ri_rhode_island.xml
new file mode 100644
index 00000000..35b6041a
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ri_rhode_island.xml
@@ -0,0 +1,135 @@
+
+
+
+
+
+ US RI Rhode Island SUTA Wage Base
+ us_ri_suta_wage_base
+ 24000.0
+
+
+
+
+
+
+
+ US RI Rhode Island SUTA Rate
+ us_ri_suta_rate
+ 1.06
+
+
+
+
+
+
+
+ US RI Rhode Island Exemption Rate
+ us_ri_sit_exemption_rate
+ {
+ 'weekly' : (( 0.00, 19.23), ( 4451.92, 0.00)),
+ 'bi-weekly' : (( 0.00, 38.46), ( 8903.85, 0.00)),
+ 'semi-monthly': (( 0.00, 41.67), ( 9645.83, 0.00)),
+ 'monthly' : (( 0.00, 83.33), ( 19291.67, 0.00)),
+ 'quarterly' : (( 0.00, 250.00), ( 57875.00, 0.00)),
+ 'semi-annually': (( 0.00, 500.00), ( 115750.00, 0.00)),
+ 'annually': (( 0.00, 1000.0), ( 231500.00, 0000)),
+ }
+
+
+
+
+
+
+ US RI Rhode Island SIT Tax Rate
+ us_ri_sit_tax_rate
+ {
+ 'weekly': (
+ ( 1255, 0.00, 3.75),
+ ( 2853, 47.06, 4.75),
+ ('inf', 122.97, 5.99),
+ ),
+ 'bi-weekly': (
+ ( 2510, 0.00, 3.75),
+ ( 5706, 94.13, 4.75),
+ ('inf', 245.94, 5.99),
+ ),
+ 'semi-monthly': (
+ ( 2719, 0.00, 3.75),
+ ( 6181, 101.96, 4.75),
+ ('inf', 266.41, 5.99),
+ ),
+ 'monthly': (
+ ( 5438, 0.00, 3.75),
+ (12363, 203.93, 4.75),
+ ('inf', 532.87, 5.99),
+ ),
+ 'quarterly': (
+ (16313, 0.00, 3.75),
+ (37088, 611.74, 4.75),
+ ('inf', 1598.55, 5.99),
+ ),
+ 'semi-annually': (
+ (32625, 0.00, 3.75),
+ (74175, 1223.44, 4.75),
+ ('inf', 3197.07, 5.99),
+ ),
+ 'annually': (
+ ( 65250, 0.00, 3.75),
+ (148350, 2446.88, 4.75),
+ ( 'inf', 6394.13, 5.99),
+ ),
+ }
+
+
+
+
+
+
+ US Rhode Island - Department of Labor and Training - Unemployment Tax
+ 1
+
+
+ US Rhode Island - Department of Labor and Training - Unemployment Tax
+
+
+
+
+ US Rhode Island - Division of Taxations - Income Tax
+ 1
+
+
+ US Rhode Island - Division of Taxations - Income Tax
+
+
+
+
+
+
+
+
+
+
+ ER: US RI Rhode Island State Unemployment
+ ER_US_RI_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ri_suta_wage_base', rate='us_ri_suta_rate', state_code='RI')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ri_suta_wage_base', rate='us_ri_suta_rate', state_code='RI')
+
+
+
+
+
+
+
+ EE: US RI Rhode Island State Income Tax Withholding
+ EE_US_RI_SIT
+ python
+ result, _ = ri_rhode_island_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ri_rhode_island_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/sc_south_carolina.xml b/l10n_us_hr_payroll/data/state/sc_south_carolina.xml
new file mode 100644
index 00000000..add7ed8d
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/sc_south_carolina.xml
@@ -0,0 +1,146 @@
+
+
+
+
+
+ US SC South Carolina SUTA Wage Base
+ us_sc_suta_wage_base
+ 14000.0
+
+
+
+ US SC South Carolina SUTA Wage Base
+ us_sc_suta_wage_base
+ 14000.0
+
+
+
+
+
+
+
+ US SC South Carolina SUTA Rate
+ us_sc_suta_rate
+ 1.09
+
+
+
+
+ US SC South Carolina SUTA Rate
+ us_sc_suta_rate
+ 0.55
+
+
+
+
+
+
+ US SC South Carolina SIT Tax Rate
+ us_sc_sit_tax_rate
+ [
+ ( 2450, 1.1, 0.0),
+ ( 4900, 3.0, 26.95),
+ ( 7350, 4.0, 100.45),
+ ( 9800, 5.0, 198.45),
+ (12250, 6.0, 320.95),
+ ('inf', 7.0, 467.95),
+ ]
+
+
+
+
+ US SC South Carolina SIT Tax Rate
+ us_sc_sit_tax_rate
+ [
+ ( 2620, 0.8, 0.0),
+ ( 5240, 3.0, 57.64),
+ ( 7860, 4.0, 110.04),
+ (10490, 5.0, 188.64),
+ (13110, 6.0, 293.54),
+ ('inf', 7.0, 424.64),
+ ]
+
+
+
+
+
+
+ US SC South Carolina Personal Exemption Rate
+ us_sc_sit_personal_exemption_rate
+ 2510
+
+
+
+ US SC South Carolina Personal Exemption Rate
+ us_sc_sit_personal_exemption_rate
+ 2590
+
+
+
+
+
+
+ US SC South Carolina Standard Deduction Rate
+ us_sc_sit_standard_deduction_rate
+ 3470.0
+
+
+
+ US SC South Carolina Standard Deduction Rate
+ us_sc_sit_standard_deduction_rate
+ 3820.0
+
+
+
+
+
+
+ US South Carolina - Department of Labor and Industrial Relations - Unemployment Tax
+ 1
+
+
+ US South Carolina - Department of Labor and Industrial Relations - Unemployment Tax
+
+
+
+
+
+ US South Carolina - Department of Taxation - Income Tax
+ 1
+
+
+ US South Carolina - Department of Taxation - Income Tax
+
+
+
+
+
+
+
+
+
+
+ ER: US SC South Carolina State Unemployment
+ ER_US_SC_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sc_suta_wage_base', rate='us_sc_suta_rate', state_code='SC')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sc_suta_wage_base', rate='us_sc_suta_rate', state_code='SC')
+
+
+
+
+
+
+
+ EE: US SC South Carolina State Income Tax Withholding
+ EE_US_SC_SIT
+ python
+ result, _ = sc_south_carolina_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = sc_south_carolina_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/sd_south_dakota.xml b/l10n_us_hr_payroll/data/state/sd_south_dakota.xml
new file mode 100644
index 00000000..21505ae4
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/sd_south_dakota.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+ US SD South Dakota SUTA Wage Base
+ us_sd_suta_wage_base
+ 15000.00
+
+
+
+
+
+
+
+ US SD South Dakota SUTA Rate
+ us_sd_suta_rate
+ 1.75
+
+
+
+
+
+
+ US South Dakota - Department of Labor - Unemployment Tax
+ 1
+
+
+ US South Dakota - Department of Labor - Unemployment Tax
+
+
+
+
+
+
+
+
+ ER: US SD South Dakota State Unemployment
+ ER_US_SD_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sd_suta_wage_base', rate='us_sd_suta_rate', state_code='SD')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_sd_suta_wage_base', rate='us_sd_suta_rate', state_code='SD')
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/tn_tennessee.xml b/l10n_us_hr_payroll/data/state/tn_tennessee.xml
new file mode 100644
index 00000000..0083b391
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/tn_tennessee.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+ US TN Tennessee SUTA Wage Base
+ us_tn_suta_wage_base
+ 7000.00
+
+
+
+
+
+
+
+ US TN Tennessee SUTA Rate
+ us_tn_suta_rate
+ 2.7
+
+
+
+
+
+
+ US Tennessee - Department of Revenue - Unemployment Tax
+ 1
+
+
+ US Tennessee - Department of Revenue - Unemployment Tax
+
+
+
+
+
+
+
+
+ ER: US TN Tennessee State Unemployment
+ ER_US_TN_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tn_suta_wage_base', rate='us_tn_suta_rate', state_code='TN')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_tn_suta_wage_base', rate='us_tn_suta_rate', state_code='TN')
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/ut_utah.xml b/l10n_us_hr_payroll/data/state/ut_utah.xml
new file mode 100644
index 00000000..b1928223
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/ut_utah.xml
@@ -0,0 +1,153 @@
+
+
+
+
+
+ US UT Utah SUTA Wage Base
+ us_ut_suta_wage_base
+ 36600.0
+
+
+
+
+
+
+
+ US UT Utah SUTA Rate
+ us_ut_suta_rate
+ 0.1
+
+
+
+
+
+
+ US UT Utah TAX Rate
+ us_ut_tax_rate
+ 0.0495
+
+
+
+
+
+
+
+ US UT Utah Allowances Rate
+ us_ut_sit_allowances_rate
+ {
+ 'single': {
+ 'weekly' : 7,
+ 'bi-weekly' : 14,
+ 'semi-monthly': 15,
+ 'monthly' : 30,
+ 'quarterly' : 90,
+ 'semi-annual': 180,
+ 'annually': 360,
+ },
+ 'married': {
+ 'weekly' : 14,
+ 'bi-weekly' : 28,
+ 'semi-monthly': 30,
+ 'monthly' : 60,
+ 'quarterly' : 180,
+ 'semi-annual': 360,
+ 'annually': 720,
+ },
+ 'head_household': {
+ 'weekly' : 7,
+ 'bi-weekly' : 14,
+ 'semi-monthly': 15,
+ 'monthly' : 30,
+ 'quarterly' : 90,
+ 'semi-annual': 180,
+ 'annually': 360,
+ },
+ }
+
+
+
+
+
+
+ US UT Utah SIT Tax Rate
+ us_ut_sit_tax_rate
+ {
+ 'single': {
+ 'weekly': ((137, 1.3)),
+ 'bi-weekly': ((274, 1.3)),
+ 'semi-monthly': ((297, 1.3)),
+ 'monthly': ((594, 1.3)),
+ 'quarterly': ((1782, 1.3)),
+ 'semi-annual': ((3564, 1.3)),
+ 'annually': ((7128, 1.3)),
+ },
+ 'married': {
+ 'weekly': ((274, 1.3)),
+ 'bi-weekly': (548, 1.3),
+ 'semi-monthly': ((594, 1.3)),
+ 'monthly': ((1188, 1.3)),
+ 'quarterly': ((3564, 1.3)),
+ 'semi-annual': ((7128, 1.3)),
+ 'annually': ((14256, 1.3)),
+ },
+ 'head_household': {
+ 'weekly': ((137, 1.3)),
+ 'bi-weekly': ((274, 1.3)),
+ 'semi-monthly': ((297, 1.3)),
+ 'monthly': ((594, 1.3)),
+ 'quarterly': ((1782, 1.3)),
+ 'semi-annual': ((3564, 1.3)),
+ 'annually': ((7128, 1.3)),
+ },
+ }
+
+
+
+
+
+
+ US Utah - Employment Security Commission - Unemployment Tax
+ 1
+
+
+ US Utah - Employment Security Commission - Unemployment Tax
+
+
+
+
+ US Utah - Tax Commission - Income Tax
+ 1
+
+
+ US Utah - Tax Commission - Income Tax
+
+
+
+
+
+
+
+ ER: US UT Utah State Unemployment
+ ER_US_UT_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ut_suta_wage_base', rate='us_ut_suta_rate', state_code='UT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_ut_suta_wage_base', rate='us_ut_suta_rate', state_code='UT')
+
+
+
+
+
+
+
+ EE: US UT Utah State Income Tax Withholding
+ EE_US_UT_SIT
+ python
+ result, _ = ut_utah_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = ut_utah_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/vt_vermont.xml b/l10n_us_hr_payroll/data/state/vt_vermont.xml
new file mode 100644
index 00000000..a6df6085
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/vt_vermont.xml
@@ -0,0 +1,185 @@
+
+
+
+
+
+ US VT Vermont SUTA Wage Base
+ us_vt_suta_wage_base
+ 16100.0
+
+
+
+
+
+
+
+ US VT Vermont SUTA Rate
+ us_vt_suta_rate
+ 1.0
+
+
+
+
+
+
+ US VT Vermont Allowances Rate
+ us_vt_sit_allowances_rate
+ {
+ 'weekly' : 83.65,
+ 'bi-weekly' : 167.31,
+ 'semi-monthly': 181.25,
+ 'monthly' : 362.50,
+ 'quarterly' : 1087.50,
+ 'annually': 4350.00,
+ }
+
+
+
+
+
+
+ US VT Vermont SIT Tax Rate
+ us_vt_sit_tax_rate
+ {
+ 'single': {
+ 'weekly': (
+ ( 60, 0.00, 0.00),
+ ( 836, 0.00, 3.35),
+ ( 1941, 26.00, 6.60),
+ ( 3983, 98.93, 7.60),
+ ('inf', 254.12, 8.75),
+ ),
+ 'bi-weekly': (
+ ( 120, 0.00, 0.00),
+ ( 1672, 0.00, 3.35),
+ ( 3882, 51.99, 6.60),
+ ( 7966, 197.85, 7.60),
+ ('inf', 508.24, 8.75),
+ ),
+ 'semi-monthly': (
+ ( 130, 0.00, 0.00),
+ ( 1811, 0.00, 3.35),
+ ( 4205, 56.31, 6.60),
+ ( 8630, 214.32, 7.60),
+ ('inf', 550.62, 8.75),
+ ),
+ 'monthly': (
+ ( 260, 0.00, 0.00),
+ ( 3623, 0.00, 3.35),
+ ( 8410, 112.66, 6.60),
+ (17260, 428.60, 7.60),
+ ('inf', 1101.20, 8.75),
+ ),
+ 'quarterly': (
+ ( 781, 0.00, 0.00),
+ (10869, 0.00, 3.35),
+ (25231, 337.95, 6.60),
+ (51781, 1285.84, 7.60),
+ ('inf', 3303.64, 8.75),
+ ),
+ 'annually': (
+ ( 3125, 0.00, 0.00),
+ ( 43475, 0.00, 3.35),
+ (100925, 1351.73, 6.60),
+ (207125, 5143.43, 7.60),
+ ( 'inf', 13214.63, 8.75),
+ ),
+ },
+ 'married': {
+ 'weekly': (
+ ( 180, 0.00, 0.00),
+ ( 1477, 0.00, 3.35),
+ ( 3315, 43.45, 6.60),
+ ( 4956, 164.76, 7.60),
+ ('inf', 289.47, 8.75),
+ ),
+ 'bi-weekly': (
+ ( 361, 0.00, 0.00),
+ ( 2955, 0.00, 3.35),
+ ( 6630, 86.90, 6.60),
+ (9913, 329.45, 7.60),
+ ('inf', 578.96, 8.75),
+ ),
+ 'semi-monthly': (
+ ( 391, 0.00, 0.00),
+ ( 3201, 0.00, 3.35),
+ ( 7182, 94.14, 6.60),
+ (10739, 356.88, 7.60),
+ ('inf', 627.21, 8.75),
+ ),
+ 'monthly': (
+ ( 781, 0.00, 0.00),
+ ( 6402, 0.00, 3.35),
+ (14365, 188.30, 6.60),
+ (21477, 713.86, 7.60),
+ ('inf', 1254.37, 8.75),
+ ),
+ 'quarterly': (
+ ( 2344, 0.00, 0.00),
+ (19206, 0.00, 3.35),
+ (43094, 564.88, 6.60),
+ (64431, 2141.49, 7.60),
+ ('inf', 3763.10, 8.75),
+ ),
+ 'annually': (
+ ( 9375, 0.00, 0.00),
+ ( 76825, 0.00, 3.35),
+ (172375, 2259.58, 6.60),
+ (257725, 8565.88, 7.60),
+ ( 'inf', 15052.48, 8.75),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US Vermont - Employment Security Commission - Unemployment Tax
+ 1
+
+
+ US Vermont - Employment Security Commission - Unemployment Tax
+
+
+
+
+
+ US Vermont - Tax Commission - Income Tax
+ 1
+
+
+ US Vermont - Tax Commission - Income Tax
+
+
+
+
+
+
+
+
+ ER: US VT Vermont State Unemployment
+ ER_US_VT_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_vt_suta_wage_base', rate='us_vt_suta_rate', state_code='VT')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_vt_suta_wage_base', rate='us_vt_suta_rate', state_code='VT')
+
+
+
+
+
+
+
+ EE: US VT Vermont State Income Tax Withholding
+ EE_US_VT_SIT
+ python
+ result, _ = vt_vermont_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = vt_vermont_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/wi_wisconsin.xml b/l10n_us_hr_payroll/data/state/wi_wisconsin.xml
new file mode 100644
index 00000000..add50523
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wi_wisconsin.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+ US WI Wisconsin SUTA Wage Base
+ us_wi_suta_wage_base
+ 14000.00
+
+
+
+
+
+
+
+
+ US WI Wisconsin SUTA Rate
+ us_wi_suta_rate
+ 3.05
+
+
+
+
+
+
+ US WI Wisconsin Exemption Rate
+ us_wi_sit_exemption_rate
+ 22
+
+
+
+
+
+
+
+ US WI Wisconsin SIT Tax Rate
+ us_wi_sit_tax_rate
+ {
+ 'single': (
+ ( 5730, 0.0000, 0.00),
+ ( 15200, 4.0000, 0.00),
+ ( 16486, 4.4800, 378.80),
+ ( 26227, 6.5408, 436.41),
+ ( 62950, 7.0224, 1073.55),
+ (240190, 6.2700, 3652.39),
+ ( 'inf', 7.6500, 14765.34),
+ ),
+ 'married': (
+ ( 7870, 0.0000, 0.00),
+ ( 18780, 4.0000, 0.00),
+ ( 21400, 5.8400, 436.40),
+ ( 28308, 7.0080, 589.41),
+ ( 60750, 7.5240, 1073.52),
+ (240190, 6.2700, 3514.46),
+ ( 'inf', 7.6500, 14765.35),
+ ),
+ }
+
+
+
+
+
+
+ US Wisconsin - Department of Workforce Development - Unemployment Tax
+ 1
+
+
+ US Wisconsin - Department of Workforce Development - Unemployment Tax
+
+
+
+
+ US Wisconsin - Department of Revenue - Income Tax
+ 1
+
+
+ US Wisconsin - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+ ER: US WI Wisconsin State Unemployment
+ ER_US_WI_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wi_suta_wage_base', rate='us_wi_suta_rate', state_code='WI')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wi_suta_wage_base', rate='us_wi_suta_rate', state_code='WI')
+
+
+
+
+
+
+
+ EE: US WI Wisconsin State Income Tax Withholding
+ EE_US_WI_SIT
+ python
+ result, _ = wi_wisconsin_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = wi_wisconsin_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/data/state/wv_west_virginia.xml b/l10n_us_hr_payroll/data/state/wv_west_virginia.xml
new file mode 100644
index 00000000..4cf6eeb5
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wv_west_virginia.xml
@@ -0,0 +1,209 @@
+
+
+
+
+
+ US WV West Virginia SUTA Wage Base
+ us_wv_suta_wage_base
+ 12000.0
+
+
+
+
+
+
+
+ US WV West Virginia SUTA Rate
+ us_wv_suta_rate
+ 2.7
+
+
+
+
+
+
+
+ US WV West Virginia Exemption Rate
+ us_wv_sit_exemption_rate
+ {
+ 'weekly' : 38.46,
+ 'bi-weekly' : 76.92,
+ 'semi-monthly': 83.33,
+ 'monthly' : 166.67,
+ 'annually': 2000.00,
+ }
+
+
+
+
+
+
+
+ US WV West Virginia SIT Tax Rate
+ us_wv_sit_tax_rate
+ {
+ 'single': {
+ 'weekly':(
+ ( 192, 0.00, 3.0),
+ ( 481, 5.76, 4.0),
+ ( 769, 17.32, 4.5),
+ ( 1154, 30.28, 6.0),
+ ('inf', 53.38, 6.5),
+ ),
+ 'bi-weekly':(
+ ( 385, 0.00, 3.0),
+ ( 962, 11.55, 4.0),
+ ( 1538, 34.63, 4.5),
+ ( 2308, 60.55, 6.0),
+ ('inf', 106.75, 6.5),
+ ),
+ 'semi-monthly':(
+ ( 417, 0.00, 3.0),
+ ( 1042 , 12.51, 4.0),
+ ( 1667, 37.51, 4.5),
+ ( 2500, 65.64, 6.0),
+ ('inf', 115.62, 6.5),
+ ),
+ 'monthly':(
+ ( 833, 0.00, 3.0),
+ ( 2083, 24.99, 4.0),
+ ( 3333, 74.99, 4.5),
+ ( 5000, 131.24, 6.0),
+ ('inf', 231.26, 6.5),
+ ),
+ 'annually':(
+ ( 10000, 0.00, 3.0),
+ ( 25000, 300.00, 4.0),
+ ( 40000, 900.00, 4.5),
+ ( 60000, 1575.00, 6.0),
+ ( 'inf', 2775.00, 6.5),
+ ),
+ },
+ 'married': {
+ 'weekly':(
+ ( 115, 0.00, 3.0),
+ ( 288, 3.45, 4.0),
+ ( 462, 10.37, 4.5),
+ ( 692, 18.20, 6.0),
+ ('inf', 32.00, 6.5),
+ ),
+ 'bi-weekly':(
+ ( 231, 0.00, 3.0),
+ ( 577, 6.93, 4.0),
+ ( 923, 20.77, 4.5),
+ ( 1385, 36.34, 6.0),
+ ('inf', 64.06, 6.5),
+ ),
+ 'semi-monthly':(
+ ( 250, 0.00, 3.0),
+ ( 625, 7.50, 4.0),
+ ( 1000, 22.50, 4.5),
+ ( 1500, 39.38, 6.0),
+ ('inf', 69.38, 6.5),
+ ),
+ 'monthly':(
+ ( 500, 0.00, 3.0),
+ ( 1250, 15.00, 4.0),
+ ( 2000, 45.00, 4.5),
+ ( 3000, 78.75, 6.0),
+ ('inf', 138.75, 6.5),
+ ),
+ 'annually':(
+ ( 6000, 0.00, 3.0),
+ (15000, 180.00, 4.0),
+ (24000, 540.00, 4.5),
+ (36000, 945.00, 6.0),
+ ('inf', 1665.00, 6.5),
+ ),
+ },
+ 'head_household': {
+ 'weekly':(
+ ( 192, 0.00, 3.0),
+ ( 481, 5.76, 4.0),
+ ( 769, 17.32, 4.5),
+ ( 1154, 30.28, 6.0),
+ ('inf', 53.38, 6.5),
+ ),
+ 'bi-weekly':(
+ ( 385, 0.00, 3.0),
+ ( 962, 11.55, 4.0),
+ ( 1538, 34.63, 4.5),
+ ( 2308, 60.55, 6.0),
+ ('inf', 106.75, 6.5),
+ ),
+ 'semi-monthly':(
+ ( 417, 0.00, 3.0),
+ ( 1042, 12.51, 4.0),
+ ( 1667, 37.51, 4.5),
+ ( 2500, 65.64, 6.0),
+ ('inf', 115.62, 6.5),
+ ),
+ 'monthly':(
+ ( 833, 0.00, 3.0),
+ ( 2083, 24.99, 4.0),
+ ( 3333, 74.99, 4.5),
+ ( 5000, 131.24, 6.0),
+ ('inf', 231.26, 6.5),
+ ),
+ 'annually':(
+ ( 10000, 0.00, 3.0),
+ ( 25000, 300.00, 4.0),
+ ( 40000, 900.00, 4.5),
+ ( 60000, 1575.00, 6.0),
+ ( 'inf', 2775.00, 6.5),
+ ),
+ },
+ }
+
+
+
+
+
+
+ US West Virginia - WorkForce - Unemployment Tax
+ 1
+
+
+ US West Virginia - WorkForce - Unemployment Tax
+
+
+
+
+ US West Virginia - Department of Revenue - Income Tax
+ 1
+
+
+ US West Virginia - Department of Revenue - Income Tax
+
+
+
+
+
+
+
+
+
+ ER: US WV West Virginia State Unemployment
+ ER_US_WV_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wv_suta_wage_base', rate='us_wv_suta_rate', state_code='WV')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wv_suta_wage_base', rate='us_wv_suta_rate', state_code='WV')
+
+
+
+
+
+
+
+ EE: US WV West Virginia State Income Tax Withholding
+ EE_US_WV_SIT
+ python
+ result, _ = wv_west_virginia_state_income_withholding(payslip, categories, worked_days, inputs)
+ code
+ result, result_rate = wv_west_virginia_state_income_withholding(payslip, categories, worked_days, inputs)
+
+
+
+
+
\ No newline at end of file
diff --git a/l10n_us_hr_payroll/data/state/wy_wyoming.xml b/l10n_us_hr_payroll/data/state/wy_wyoming.xml
new file mode 100644
index 00000000..ac62e3f0
--- /dev/null
+++ b/l10n_us_hr_payroll/data/state/wy_wyoming.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+ US WY Wyoming SUTA Wage Base
+ us_wy_suta_wage_base
+ 25400.00
+
+
+
+ US WY Wyoming SUTA Wage Base
+ us_wy_suta_wage_base
+ 26400.00
+
+
+
+
+
+
+
+ US WY Wyoming SUTA Rate
+ us_wy_suta_rate
+ 2.10
+
+
+
+ US WY Wyoming SUTA Rate
+ us_wy_suta_rate
+ 8.5
+
+
+
+
+
+
+ US Wyoming - Department of Workforce Services (WDWS) - Unemployment Tax
+ 1
+
+
+ US Wyoming - Department of Workforce Services (WDWS) - Unemployment Tax
+
+
+
+
+
+
+
+
+ ER: US WY Wyoming State Unemployment
+ ER_US_WY_SUTA
+ python
+ result, _ = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wy_suta_wage_base', rate='us_wy_suta_rate', state_code='WY')
+ code
+ result, result_rate = general_state_unemployment(payslip, categories, worked_days, inputs, wage_base='us_wy_suta_wage_base', rate='us_wy_suta_rate', state_code='WY')
+
+
+
+
+
diff --git a/l10n_us_hr_payroll/migrations/data.py b/l10n_us_hr_payroll/migrations/data.py
index cb403412..674d2ebe 100644
--- a/l10n_us_hr_payroll/migrations/data.py
+++ b/l10n_us_hr_payroll/migrations/data.py
@@ -231,6 +231,11 @@ XMLIDS_TO_REMOVE_2020 = [
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_unemp_wages_2018',
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_inc_withhold_add',
+ 'l10n_us_sc_hr_payroll.hr_payroll_sc_unemp_wages',
+ 'l10n_us_sc_hr_payroll.hr_payroll_sc_unemp',
+ 'l10n_us_sc_hr_payroll.hr_payroll_sc_income_withhold',
+ 'l10n_us_sc_hr_payroll.hr_payroll_rules_sc_unemp_wages',
+
'l10n_us_tx_hr_payroll.contrib_register_txdor',
'l10n_us_tx_hr_payroll.hr_payroll_tx_unemp_wages',
'l10n_us_tx_hr_payroll.hr_payroll_tx_unemp',
@@ -249,6 +254,10 @@ XMLIDS_TO_REMOVE_2020 = [
'l10n_us_wa_hr_payroll.hr_payroll_wa_lni_withhold',
'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_unemp_wages_2018',
+ 'l10n_us_wy_hr_payroll.hr_payroll_wy_unemp_wages',
+ 'l10n_us_wy_hr_payroll.hr_payroll_wy_unemp',
+ 'l10n_us_wy_hr_payroll.hr_payroll_rules_wy_unemp_wages'
+
]
XMLIDS_TO_RENAME_2020 = {
@@ -409,6 +418,13 @@ XMLIDS_TO_RENAME_2020 = {
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_unemp_company_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_pa_suta',
'l10n_us_pa_hr_payroll.hr_payroll_rules_pa_inc_withhold_2018': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_pa_sit',
+ 'l10n_us_sc_hr_payroll.res_partner_sc_dew_unemp': 'l10n_us_hr.payroll.res_partner_us_sc_dor',
+ 'l10n_us_sc_hr_payroll.res_partner_sc_dor_withhold': 'l10n_us_hr.payroll.res_partner_us_sc_dor_sit',
+ 'l10n_us_sc_hr_payroll.contrib_register_sc_dew_unemp': 'l10n_us_hr.payroll.contrib_register_us_sc_dor',
+ 'l10n_us_sc_hr_payroll.contrib_register_sc_dor_withhold': 'l10n_us_hr.payroll.contrib_register_us_sc_dor_sit',
+ 'l10n_us_sc_hr_payroll.hr_payroll_rules_sc_unemp': 'l10n_us_hr.payroll.hr_payroll_rule_er_us_sc_suta',
+ 'l10n_us_sc_hr_payroll.hr_payroll_rules_sc_inc_withhold': 'l10n_us_hr.payroll.hr_payroll_rule_ee_us_sc_sit',
+
'l10n_us_tx_hr_payroll.res_partner_txdor': 'l10n_us_hr_payroll.res_partner_us_tx_dor',
'l10n_us_tx_hr_payroll.contrib_register_txdor': 'l10n_us_hr_payroll.contrib_register_us_tx_dor',
'l10n_us_tx_hr_payroll.hr_payroll_rules_tx_unemp_2018': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_tx_suta',
@@ -430,6 +446,10 @@ XMLIDS_TO_RENAME_2020 = {
'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_lni_withhold': 'l10n_us_hr_payroll.hr_payroll_rule_ee_us_wa_lni',
'l10n_us_wa_hr_payroll.hr_payroll_rules_wa_lni': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wa_lni',
+ 'l10n_us_wy_hr_payroll.res_partner_wy_dws_unemp': 'l10n_us_hr_payroll.res_partner_us_wy_dor',
+ 'l10n_us_wy_hr_payroll.contrib_register_wy_dws_unemp': 'l10n_us_hr_payroll.contrib_register_us_wy_dor',
+ 'l10n_us_wy_hr_payroll.hr_payroll_rules_wy_unemp': 'l10n_us_hr_payroll.hr_payroll_rule_er_us_wy_suta',
+
}
XMLIDS_COPY_ACCOUNTING_2020 = {
diff --git a/l10n_us_hr_payroll/models/hr_payslip.py b/l10n_us_hr_payroll/models/hr_payslip.py
index ea4d4113..adc58bbe 100644
--- a/l10n_us_hr_payroll/models/hr_payslip.py
+++ b/l10n_us_hr_payroll/models/hr_payslip.py
@@ -26,19 +26,33 @@ from .state.hi_hawaii import hi_hawaii_state_income_withholding
from .state.ia_iowa import ia_iowa_state_income_withholding
from .state.id_idaho import id_idaho_state_income_withholding
from .state.il_illinois import il_illinois_state_income_withholding
+from .state.in_indiana import in_indiana_state_income_withholding
+from .state.ks_kansas import ks_kansas_state_income_withholding
+from .state.ky_kentucky import ky_kentucky_state_income_withholding
+from .state.la_louisiana import la_louisiana_state_income_withholding
+from .state.me_maine import me_maine_state_income_withholding
from .state.mi_michigan import mi_michigan_state_income_withholding
from .state.mn_minnesota import mn_minnesota_state_income_withholding
from .state.mo_missouri import mo_missouri_state_income_withholding
from .state.ms_mississippi import ms_mississippi_state_income_withholding
from .state.mt_montana import mt_montana_state_income_withholding
from .state.nc_northcarolina import nc_northcarolina_state_income_withholding
+from .state.nd_north_dakota import nd_north_dakota_state_income_withholding
+from .state.ne_nebraska import ne_nebraska_state_income_withholding
from .state.nj_newjersey import nj_newjersey_state_income_withholding
from .state.nm_new_mexico import nm_new_mexico_state_income_withholding
from .state.ny_new_york import ny_new_york_state_income_withholding
from .state.oh_ohio import oh_ohio_state_income_withholding
+from .state.ok_oklahoma import ok_oklahoma_state_income_withholding
+from .state.ri_rhode_island import ri_rhode_island_state_income_withholding
+from .state.sc_south_carolina import sc_south_carolina_state_income_withholding
+from .state.ut_utah import ut_utah_state_income_withholding
+from .state.vt_vermont import vt_vermont_state_income_withholding
from .state.va_virginia import va_virginia_state_income_withholding
from .state.wa_washington import wa_washington_fml_er, \
wa_washington_fml_ee
+from .state.wi_wisconsin import wi_wisconsin_state_income_withholding
+from .state.wv_west_virginia import wv_west_virginia_state_income_withholding
class HRPayslip(models.Model):
@@ -83,19 +97,33 @@ class HRPayslip(models.Model):
'ia_iowa_state_income_withholding': ia_iowa_state_income_withholding,
'id_idaho_state_income_withholding': id_idaho_state_income_withholding,
'il_illinois_state_income_withholding': il_illinois_state_income_withholding,
+ 'in_indiana_state_income_withholding': in_indiana_state_income_withholding,
+ 'ks_kansas_state_income_withholding': ks_kansas_state_income_withholding,
+ 'ky_kentucky_state_income_withholding':ky_kentucky_state_income_withholding,
+ 'la_louisiana_state_income_withholding': la_louisiana_state_income_withholding,
+ 'me_maine_state_income_withholding': me_maine_state_income_withholding,
'mi_michigan_state_income_withholding': mi_michigan_state_income_withholding,
'mn_minnesota_state_income_withholding': mn_minnesota_state_income_withholding,
'mo_missouri_state_income_withholding': mo_missouri_state_income_withholding,
'ms_mississippi_state_income_withholding': ms_mississippi_state_income_withholding,
'mt_montana_state_income_withholding': mt_montana_state_income_withholding,
'nc_northcarolina_state_income_withholding': nc_northcarolina_state_income_withholding,
+ 'nd_north_dakota_state_income_withholding': nd_north_dakota_state_income_withholding,
+ 'ne_nebraska_state_income_withholding': ne_nebraska_state_income_withholding,
'nj_newjersey_state_income_withholding': nj_newjersey_state_income_withholding,
'nm_new_mexico_state_income_withholding': nm_new_mexico_state_income_withholding,
'ny_new_york_state_income_withholding': ny_new_york_state_income_withholding,
'oh_ohio_state_income_withholding': oh_ohio_state_income_withholding,
+ 'ok_oklahoma_state_income_withholding': ok_oklahoma_state_income_withholding,
+ 'ri_rhode_island_state_income_withholding': ri_rhode_island_state_income_withholding,
+ 'sc_south_carolina_state_income_withholding': sc_south_carolina_state_income_withholding,
+ 'ut_utah_state_income_withholding': ut_utah_state_income_withholding,
+ 'vt_vermont_state_income_withholding': vt_vermont_state_income_withholding,
'va_virginia_state_income_withholding': va_virginia_state_income_withholding,
'wa_washington_fml_er': wa_washington_fml_er,
'wa_washington_fml_ee': wa_washington_fml_ee,
+ 'wi_wisconsin_state_income_withholding': wi_wisconsin_state_income_withholding,
+ 'wv_west_virginia_state_income_withholding': wv_west_virginia_state_income_withholding,
}
def get_year(self):
diff --git a/l10n_us_hr_payroll/models/state/al_alabama.py b/l10n_us_hr_payroll/models/state/al_alabama.py
index d34fa2ef..31e084e2 100644
--- a/l10n_us_hr_payroll/models/state/al_alabama.py
+++ b/l10n_us_hr_payroll/models/state/al_alabama.py
@@ -18,6 +18,10 @@ def al_alabama_state_income_withholding(payslip, categories, worked_days, inputs
if not wage:
return 0.0, 0.0
+ exemptions = payslip.contract_id.us_payroll_config_value('al_a4_sit_exemptions')
+ if not exemptions:
+ 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
@@ -25,7 +29,6 @@ def al_alabama_state_income_withholding(payslip, categories, worked_days, inputs
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)
diff --git a/l10n_us_hr_payroll/models/state/ar_arkansas.py b/l10n_us_hr_payroll/models/state/ar_arkansas.py
index 87190e62..4e3a008e 100644
--- a/l10n_us_hr_payroll/models/state/ar_arkansas.py
+++ b/l10n_us_hr_payroll/models/state/ar_arkansas.py
@@ -18,7 +18,7 @@ def ar_arkansas_state_income_withholding(payslip, categories, worked_days, input
# Determine Wage
wage = sit_wage(payslip, categories)
- if wage == 0.0:
+ if not wage:
return 0.0, 0.0
pay_periods = payslip.dict.get_pay_periods_in_year()
diff --git a/l10n_us_hr_payroll/models/state/ga_georgia.py b/l10n_us_hr_payroll/models/state/ga_georgia.py
index f1bbbc31..188d3a34 100644
--- a/l10n_us_hr_payroll/models/state/ga_georgia.py
+++ b/l10n_us_hr_payroll/models/state/ga_georgia.py
@@ -13,7 +13,7 @@ def ga_georgia_state_income_withholding(payslip, categories, worked_days, inputs
if not _state_applies(payslip, state_code):
return 0.0, 0.0
ga_filing_status = payslip.dict.contract_id.us_payroll_config_value('ga_g4_sit_filing_status')
- if not ga_filing_status or ga_filing_status == 'exempt':
+ if not ga_filing_status:
return 0.0, 0.0
# Determine Wage
diff --git a/l10n_us_hr_payroll/models/state/ia_iowa.py b/l10n_us_hr_payroll/models/state/ia_iowa.py
index 9a799a47..ca339a20 100644
--- a/l10n_us_hr_payroll/models/state/ia_iowa.py
+++ b/l10n_us_hr_payroll/models/state/ia_iowa.py
@@ -13,6 +13,9 @@ def ia_iowa_state_income_withholding(payslip, categories, worked_days, inputs):
if not _state_applies(payslip, state_code):
return 0.0, 0.0
+ if payslip.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
# Determine Wage
wage = sit_wage(payslip, categories)
if not wage:
@@ -27,7 +30,8 @@ def ia_iowa_state_income_withholding(payslip, categories, worked_days, inputs):
deduction_per_allowance = payslip.dict.rule_parameter('us_ia_sit_deduction_allowance_rate')[schedule_pay]
t1 = wage + fed_withholding
- t2 = t1 - standard_deduction[0] if allowances < 2 else standard_deduction[1]
+ standard_deduction_amt = standard_deduction[0] if allowances < 2 else standard_deduction[1]
+ t2 = t1 - standard_deduction_amt
t3 = 0.0
last = 0.0
for row in tax_table:
diff --git a/l10n_us_hr_payroll/models/state/in_indiana.py b/l10n_us_hr_payroll/models/state/in_indiana.py
new file mode 100644
index 00000000..f210ae2d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/in_indiana.py
@@ -0,0 +1,34 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def in_indiana_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'IN'
+ 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
+
+ schedule_pay = payslip.dict.contract_id.schedule_pay
+ additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ personal_exemption = payslip.dict.contract_id.us_payroll_config_value('in_w4_sit_personal_exemption')
+ personal_exemption_rate = payslip.dict.rule_parameter('us_in_sit_personal_exemption_rate')[schedule_pay][personal_exemption - 1]
+ dependent_exemption = payslip.dict.contract_id.us_payroll_config_value('in_w4_sit_dependent_exemption')
+ dependent_exemption_rate = payslip.dict.rule_parameter('us_in_sit_dependent_exemption_rate')[schedule_pay][dependent_exemption - 1]
+ income_tax_rate = payslip.dict.rule_parameter('us_in_suta_income_rate')
+
+ taxable_income = wage - (personal_exemption_rate + dependent_exemption_rate)
+ withholding = taxable_income * (income_tax_rate / 100.0)
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ks_kansas.py b/l10n_us_hr_payroll/models/state/ks_kansas.py
new file mode 100644
index 00000000..cac3a05f
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ks_kansas.py
@@ -0,0 +1,44 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ks_kansas_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'KS'
+ 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('ks_k4_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')
+ allowances = payslip.dict.contract_id.us_payroll_config_value('ks_k4_sit_allowances')
+ allowances_amt = payslip.dict.rule_parameter('us_ks_sit_allowances_rate')[schedule_pay]
+ tax_table = payslip.dict.rule_parameter('us_ks_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (allowances * allowances_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, rate, flat_fee = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ky_kentucky.py b/l10n_us_hr_payroll/models/state/ky_kentucky.py
new file mode 100644
index 00000000..75f53778
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ky_kentucky.py
@@ -0,0 +1,32 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ky_kentucky_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'KY'
+ 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')
+ tax_rate = payslip.dict.rule_parameter('us_ky_sit_tax_rate')
+ standard_deduction = payslip.dict.rule_parameter('us_ky_sit_standard_deduction_rate')
+
+ taxable_income = (wage * pay_periods) - standard_deduction
+ withholding = taxable_income * (tax_rate / 100)
+
+ withholding = max(withholding, 0.0)
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/la_louisiana.py b/l10n_us_hr_payroll/models/state/la_louisiana.py
new file mode 100644
index 00000000..db9c3bcf
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/la_louisiana.py
@@ -0,0 +1,62 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def la_louisiana_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'LA'
+ 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('la_l4_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ pay_periods = payslip.dict.get_pay_periods_in_year()
+ personal_exemptions = payslip.dict.contract_id.us_payroll_config_value('la_l4_sit_exemptions')
+ additional = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
+ dependent_exemptions = payslip.dict.contract_id.us_payroll_config_value('la_l4_sit_dependents')
+ tax_table = payslip.dict.rule_parameter('us_la_sit_tax_rate')[filing_status]
+ exemption_rate = payslip.dict.rule_parameter('us_la_sit_personal_exemption_rate')
+ dependent_rate = payslip.dict.rule_parameter('us_la_sit_dependent_rate')
+
+ annual_wage = wage * pay_periods
+
+ effect_cap = 0.0
+ multiplier = 0.0
+ if filing_status == 'single':
+ effect_cap = 12500.00
+ multiplier = 1.60
+ elif filing_status == 'married':
+ effect_cap = 25000.00
+ multiplier = 1.65
+
+ after_credits_under = (2.100 / 100) * (((personal_exemptions * exemption_rate) +
+ (dependent_exemptions * dependent_rate)) / pay_periods)
+ after_credits_over = 0.00
+ if after_credits_under > effect_cap:
+ after_credits_under = effect_cap
+ after_credits_over_check = ((personal_exemptions * exemption_rate) + (dependent_exemptions * dependent_rate)) - effect_cap
+ after_credits_over = (multiplier / 100.00) * (after_credits_over_check / pay_periods) if after_credits_over_check > 0 else 0.00
+ withholding = 0.0
+ last = 0.0
+ for amt, rate in tax_table:
+ withholding = withholding + ((rate / 100.0) * (wage - (last / pay_periods)))
+ if annual_wage <= float(amt):
+ break
+ last = amt
+
+ withholding = withholding - (after_credits_under + after_credits_over)
+ withholding = round(withholding, 2)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/me_maine.py b/l10n_us_hr_payroll/models/state/me_maine.py
new file mode 100644
index 00000000..fa5bf874
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/me_maine.py
@@ -0,0 +1,62 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def me_maine_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'ME'
+ 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('me_w4me_sit_filing_status')
+ if not filing_status:
+ return 0.0, 0.0
+
+ exempt = payslip.dict.contract_id.us_payroll_config_value('state_income_tax_exempt')
+ if 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')
+ allowances = payslip.dict.contract_id.us_payroll_config_value('me_w4me_sit_allowances')
+ tax_rate = payslip.dict.rule_parameter('us_me_sit_tax_rate')[filing_status]
+ personal_exemption = payslip.dict.rule_parameter('us_me_sit_personal_exemption_rate')
+ standard_deduction = payslip.dict.rule_parameter('us_me_sit_standard_deduction_rate')[filing_status]
+
+ taxable_income = wage * pay_periods
+ exemption_amt = allowances * personal_exemption
+ last = 0.0
+ standard_deduction_amt = 0.0
+ for row in standard_deduction:
+ amt, flat_amt = row
+ if taxable_income < 82900:
+ standard_deduction_amt = flat_amt
+ break
+ elif taxable_income < amt:
+ standard_deduction_amt = last * (amt - taxable_income) / flat_amt
+ break
+ last = flat_amt
+ annual_income = taxable_income - (exemption_amt + standard_deduction_amt)
+ withholding = 0.0
+ for row in tax_rate:
+ amt, flat_fee, rate = row
+ if annual_income < float(amt):
+ withholding = ((annual_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding = round(withholding / pay_periods)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/nd_north_dakota.py b/l10n_us_hr_payroll/models/state/nd_north_dakota.py
new file mode 100644
index 00000000..f01fbb9d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/nd_north_dakota.py
@@ -0,0 +1,45 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def nd_north_dakota_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'ND'
+ 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('nd_w4_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')
+ allowance = payslip.dict.contract_id.us_payroll_config_value('nd_w4_sit_allowances')
+ allowance_rate = payslip.dict.rule_parameter('us_nd_sit_allowances_rate')[schedule_pay]
+ tax_rate = payslip.dict.rule_parameter('us_nd_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (allowance * allowance_rate)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_rate:
+ amt, flat_fee, rate = row
+ if taxable_income < float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = round(withholding)
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ne_nebraska.py b/l10n_us_hr_payroll/models/state/ne_nebraska.py
new file mode 100644
index 00000000..dfc67d6d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ne_nebraska.py
@@ -0,0 +1,49 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ne_nebraska_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'NE'
+ if not _state_applies(payslip, state_code):
+ 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
+
+ # 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('ne_w4n_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')
+ allowances = payslip.dict.contract_id.us_payroll_config_value('ne_w4n_sit_allowances')
+ tax_rate = payslip.dict.rule_parameter('us_ne_sit_tax_rate')[filing_status].get(schedule_pay)
+ sit_allowance = payslip.dict.rule_parameter('us_ne_sit_allowances_rate')[schedule_pay]
+
+ allowance_amt = allowances * sit_allowance
+ taxable_income = wage - allowance_amt
+ withholding = 0.0
+ last = 0.0
+ for row in tax_rate:
+ amt, flat_fee, rate = row
+ if taxable_income < float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ok_oklahoma.py b/l10n_us_hr_payroll/models/state/ok_oklahoma.py
new file mode 100644
index 00000000..608421a5
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ok_oklahoma.py
@@ -0,0 +1,47 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ok_oklahoma_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'OK'
+ 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
+
+ if payslip.dict.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ filing_status = payslip.dict.contract_id.us_payroll_config_value('ok_w4_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')
+ allowances = payslip.dict.contract_id.us_payroll_config_value('ok_w4_sit_allowances')
+ allowances_amt = payslip.dict.rule_parameter('us_ok_sit_allowances_rate')[schedule_pay]
+ tax_table = payslip.dict.rule_parameter('us_ok_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (allowances * allowances_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, rate, flat_fee = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ri_rhode_island.py b/l10n_us_hr_payroll/models/state/ri_rhode_island.py
new file mode 100644
index 00000000..20be0a0c
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ri_rhode_island.py
@@ -0,0 +1,48 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ri_rhode_island_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'RI'
+ 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
+
+ if payslip.dict.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ 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')
+ allowances = payslip.dict.contract_id.us_payroll_config_value('ri_w4_sit_allowances')
+ exemption_rate = payslip.dict.rule_parameter('us_ri_sit_exemption_rate')[schedule_pay]
+ tax_table = payslip.dict.rule_parameter('us_ri_sit_tax_rate')[schedule_pay]
+
+ exemption_amt = 0.0
+ for row in exemption_rate:
+ amt, flat_fee = row
+ if wage > amt:
+ exemption_amt = flat_fee
+
+ taxable_income = wage - (allowances * exemption_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, flat_fee, rate = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/sc_south_carolina.py b/l10n_us_hr_payroll/models/state/sc_south_carolina.py
new file mode 100644
index 00000000..e8d13934
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/sc_south_carolina.py
@@ -0,0 +1,50 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def sc_south_carolina_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+ WAGE = GROSS + DED_FIT_EXEMPT
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'SC'
+ if not _state_applies(payslip, state_code):
+ 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
+
+ # 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')
+ allowances = payslip.dict.contract_id.us_payroll_config_value('sc_w4_sit_allowances')
+ tax_rate = payslip.dict.rule_parameter('us_sc_sit_tax_rate')
+ personal_exemption = payslip.dict.rule_parameter('us_sc_sit_personal_exemption_rate')
+ deduction = payslip.dict.rule_parameter('us_sc_sit_standard_deduction_rate')
+
+ annual_wage = wage * pay_periods
+ personal_exemption_amt = allowances * personal_exemption
+ standard_deduction = 0.0
+ if allowances > 0:
+ if (annual_wage * 0.1) > deduction:
+ standard_deduction = deduction
+ else:
+ standard_deduction = annual_wage * (10 / 100)
+ taxable_income = annual_wage - personal_exemption_amt - standard_deduction
+ withholding = 0.0
+ last = 0.0
+ for cap, rate, flat_amt in tax_rate:
+ if float(cap) > taxable_income:
+ withholding = (taxable_income * (rate / 100.0) - flat_amt)
+ break
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/ut_utah.py b/l10n_us_hr_payroll/models/state/ut_utah.py
new file mode 100644
index 00000000..21bf2ba7
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/ut_utah.py
@@ -0,0 +1,39 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def ut_utah_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'UT'
+ 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('ut_w4_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')
+ tax_rate = payslip.dict.rule_parameter('us_ut_tax_rate')
+ allowances = payslip.dict.rule_parameter('us_ut_sit_allowances_rate')[filing_status].get(schedule_pay)
+ tax_table = payslip.dict.rule_parameter('us_ut_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage * tax_rate
+ withholding = 0.0
+ amt, rate = tax_table
+ withholding = taxable_income - (allowances - ((wage - amt) * (rate / 100)))
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/vt_vermont.py b/l10n_us_hr_payroll/models/state/vt_vermont.py
new file mode 100644
index 00000000..5a63d314
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/vt_vermont.py
@@ -0,0 +1,46 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def vt_vermont_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'VT'
+ 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
+
+ if payslip.dict.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ filing_status = payslip.dict.contract_id.us_payroll_config_value('vt_w4vt_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')
+ allowances = payslip.dict.contract_id.us_payroll_config_value('vt_w4vt_sit_allowances')
+ allowance_amt = payslip.dict.rule_parameter('us_vt_sit_allowances_rate')[schedule_pay]
+ tax_table = payslip.dict.rule_parameter('us_vt_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (allowances * allowance_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, flat_fee, rate = row
+ if wage <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/wi_wisconsin.py b/l10n_us_hr_payroll/models/state/wi_wisconsin.py
new file mode 100644
index 00000000..d522215d
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/wi_wisconsin.py
@@ -0,0 +1,47 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def wi_wisconsin_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'WI'
+ 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
+
+ if payslip.dict.contract_id.us_payroll_config_value('state_income_tax_exempt'):
+ return 0.0, 0.0
+
+ filing_status = payslip.dict.contract_id.us_payroll_config_value('wi_wt4_sit_filing_status')
+ if not filing_status:
+ 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')
+ exemptions = payslip.dict.contract_id.us_payroll_config_value('wi_wt4_sit_exemptions')
+ exemption_amt = payslip.dict.rule_parameter('us_wi_sit_exemption_rate')
+ tax_table = payslip.dict.rule_parameter('us_wi_sit_tax_rate')[filing_status]
+
+ taxable_income = wage * pay_periods
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, rate, flat_fee = row
+ if taxable_income <= float(amt):
+ withholding = (((taxable_income - last) * (rate / 100)) + flat_fee) - (exemptions * exemption_amt)
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding /= pay_periods
+ withholding += additional
+ return wage, -((withholding / wage) * 100.0)
diff --git a/l10n_us_hr_payroll/models/state/wv_west_virginia.py b/l10n_us_hr_payroll/models/state/wv_west_virginia.py
new file mode 100644
index 00000000..268d68ab
--- /dev/null
+++ b/l10n_us_hr_payroll/models/state/wv_west_virginia.py
@@ -0,0 +1,44 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .general import _state_applies, sit_wage
+
+
+def wv_west_virginia_state_income_withholding(payslip, categories, worked_days, inputs):
+ """
+ Returns SIT eligible wage and rate.
+
+ :return: result, result_rate (wage, percent)
+ """
+ state_code = 'WV'
+ 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('wv_it104_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')
+ exemptions = payslip.dict.contract_id.us_payroll_config_value('wv_it104_sit_exemptions')
+ exemption_amt = payslip.dict.rule_parameter('us_wv_sit_exemption_rate')[schedule_pay]
+ tax_table = payslip.dict.rule_parameter('us_wv_sit_tax_rate')[filing_status].get(schedule_pay)
+
+ taxable_income = wage - (exemptions * exemption_amt)
+ withholding = 0.0
+ last = 0.0
+ for row in tax_table:
+ amt, flat_fee, rate = row
+ if taxable_income <= float(amt):
+ withholding = ((taxable_income - last) * (rate / 100)) + flat_fee
+ break
+ last = amt
+
+ withholding = max(withholding, 0.0)
+ withholding += additional
+ withholding = round(withholding)
+ 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 4915b8d2..61481bae 100644
--- a/l10n_us_hr_payroll/models/us_payroll_config.py
+++ b/l10n_us_hr_payroll/models/us_payroll_config.py
@@ -52,7 +52,7 @@ class HRContractUSPayrollConfig(models.Model):
help='Form W4 (2020+) 4(c)')
al_a4_sit_exemptions = fields.Selection([
- ('0', '0'),
+ ('', '0'),
('S', 'S'),
('MS', 'MS'),
('M', 'M'),
@@ -92,7 +92,7 @@ class HRContractUSPayrollConfig(models.Model):
de_w4_sit_dependent = fields.Integer(string='Delaware W-4 Dependents', help='DE W-4 4.')
ga_g4_sit_filing_status = fields.Selection([
- ('exempt', 'Exempt'),
+ ('', 'Exempt'),
('single', 'Single'),
('married filing joint, both spouses working', 'Married Filing Joint, both spouses working'),
('married filing joint, one spouse working', 'Married Filing Joint, one spouse working'),
@@ -124,6 +124,29 @@ class HRContractUSPayrollConfig(models.Model):
il_w4_sit_basic_allowances = fields.Integer(string='Illinois IL-W-4 Number of Basic Allowances', help='IL-W-4 Step 1.')
il_w4_sit_additional_allowances = fields.Integer(string='Illinois IL-W-4 Number of Additional Allowances', help='IL-W-4 Step 2.')
+ in_w4_sit_personal_exemption = fields.Integer(string='Indiana In-W-4 Number of Personal Exemption', help='IN-W-4 5.')
+ in_w4_sit_dependent_exemption = fields.Integer(string='Indiana In-W-4 Number of Dependent Exemption', help='IN-W-4 6.')
+
+ ks_k4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Joint'),
+ ], string='Kansas K-4 Filing Status', help='KS K-4 3.')
+ ks_k4_sit_allowances = fields.Integer(string='Kansas KS K-4 Number of Allowances', help='KS K-4 Step 4.')
+
+ la_l4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Louisiana LA L-4 Filing Status', help='LA L-4 3.')
+ la_l4_sit_exemptions = fields.Integer(string='Louisiana LA L-4 Number of Exemptions', help='LA L-4 6.')
+ la_l4_sit_dependents = fields.Integer(string='Louisiana LA L-4 Number of Dependents', help='LA L-4 7.')
+
+ me_w4me_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single or Head of Household'),
+ ('married', 'Married'),
+ ], string='Maine W-4ME Filing Status', help='ME W-4ME 3.')
+ me_w4me_sit_allowances = fields.Integer(string='Maine Allowances', help='W-4ME 4.')
+
mi_w4_sit_exemptions = fields.Integer(string='Michigan MI W-4 Exemptions', help='MI-W4 6.')
mn_w4mn_sit_filing_status = fields.Selection([
@@ -167,11 +190,24 @@ class HRContractUSPayrollConfig(models.Model):
('', 'Exempt'),
('single', 'Single'),
('married', 'Married'),
- ('surviving_spouse', 'Surviving Spouse'),
('head_household', 'Head of Household')
], string='North Carolina NC-4 Filing Status', help='NC-4')
nc_nc4_sit_allowances = fields.Integer(string='North Carolina NC-4 Allowances', help='NC-4 1.')
+ nd_w4_sit_filing_status = fields.Selection([
+ ('', 'Exempt'),
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Head of Household')
+ ], string='North Dakota ND W-4 Filing Status', help='ND W-4')
+ nd_w4_sit_allowances = fields.Integer(string='North Dakota ND W-4')
+
+ ne_w4n_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Nebraska NE W-4N Filing Status', help='NE W-4N')
+ ne_w4n_sit_allowances = fields.Integer(string='Nebraska NE W-4N Allowances', help='NE W-4N 1.')
+
nj_njw4_sit_filing_status = fields.Selection([
('', 'Exempt'),
('single', 'Single'),
@@ -200,7 +236,43 @@ class HRContractUSPayrollConfig(models.Model):
oh_it4_sit_exemptions = fields.Integer(string='Ohio IT-4 Exemptions',
help='Line 4')
+ ok_w4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Married, but withhold at higher Single rate')
+ ], string='Oklahoma OK-W-4 Filing Status', help='OK-W-4')
+ ok_w4_sit_allowances = fields.Integer(string='Oklahoma OK-W-4 Allowances', help='OK-W-4 5.')
+
+ ri_w4_sit_allowances = fields.Integer(string='Rhode Island RI W-4 Allowances', help='RI W-4 1.')
+
+ sc_w4_sit_allowances = fields.Integer(string='South Carolina SC W-4 Allowances', help='SC W-4 5.')
+
+ ut_w4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Head of Household')
+ ], string='Utah UT W-4 Filing Status', help='UT W-4 C.')
+
+ vt_w4vt_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Vermont VT W-4VT Filing Status', help='VT W-4VT')
+ vt_w4vt_sit_allowances = fields.Integer(string='Vermont VT W-4VT Allowances', help='VT W-4VT 5.')
+
va_va4_sit_exemptions = fields.Integer(string='Virginia VA-4(P) Personal Exemptions',
help='VA-4(P) 1(a)')
va_va4_sit_other_exemptions = fields.Integer(string='Virginia VA-4(P) Age & Blindness Exemptions',
help='VA-4(P) 1(b)')
+
+ wi_wt4_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ], string='Wisconsin WT-4 Filing Status', help='WI WT-4')
+ wi_wt4_sit_exemptions = fields.Integer(string='Wisconsin Exemptions', help='WI WT-4 1.(d)')
+
+ wv_it104_sit_filing_status = fields.Selection([
+ ('single', 'Single'),
+ ('married', 'Married'),
+ ('head_household', 'Head of Household')
+ ], string='West Virginia WV/IT-104 Filing Status', help='WV WV/IT-104')
+ wv_it104_sit_exemptions = fields.Integer(string='West Virginia Exemptions', help='WV WV/IT-104 4.')
diff --git a/l10n_us_hr_payroll/tests/__init__.py b/l10n_us_hr_payroll/tests/__init__.py
index 6068dd3c..a7c95ebf 100755
--- a/l10n_us_hr_payroll/tests/__init__.py
+++ b/l10n_us_hr_payroll/tests/__init__.py
@@ -47,6 +47,17 @@ from . import test_us_id_idaho_payslip_2020
from . import test_us_il_illinois_payslip_2019
from . import test_us_il_illinois_payslip_2020
+from . import test_us_in_indiana_payslip_2020
+
+from . import test_us_ky_kentucky_payslip_2020
+
+from . import test_us_ks_kansas_payslip_2020
+
+from . import test_us_la_louisiana_payslip_2019
+from . import test_us_la_louisiana_payslip_2020
+
+from . import test_us_me_maine_payslip_2020
+
from . import test_us_mi_michigan_payslip_2019
from . import test_us_mi_michigan_payslip_2020
@@ -65,6 +76,10 @@ from . import test_us_mt_montana_payslip_2020
from . import test_us_nc_northcarolina_payslip_2019
from . import test_us_nc_northcarolina_payslip_2020
+from . import test_us_nd_north_dakota_payslip_2020
+
+from . import test_us_ne_nebraska_payslip_2020
+
from . import test_us_nh_new_hampshire_payslip_2020
from . import test_us_nj_newjersey_payslip_2019
@@ -72,20 +87,44 @@ from . import test_us_nj_newjersey_payslip_2020
from . import test_us_nm_new_mexico_payslip_2020
+from . import test_us_nv_nevada_payslip_2020
+
from . import test_us_ny_new_york_payslip_2019
from . import test_us_ny_new_york_payslip_2020
from . import test_us_oh_ohio_payslip_2019
from . import test_us_oh_ohio_payslip_2020
+from . import test_us_ok_oklahoma_payslip_2020
+
from . import test_us_pa_pennsylvania_payslip_2019
from . import test_us_pa_pennsylvania_payslip_2020
+from . import test_us_ri_rhode_island_payslip_2020
+
+from . import test_us_sc_south_carolina_payslip_2019
+from . import test_us_sc_south_carolina_payslip_2020
+
+from . import test_us_sd_south_dakota_payslip_2020
+
+from . import test_us_tn_tennessee_payslip_2020
+
from . import test_us_tx_texas_payslip_2019
from . import test_us_tx_texas_payslip_2020
+from . import test_us_us_utah_payslip_2020
+
+from . import test_us_vt_vermont_payslip_2020
+
from . import test_us_va_virginia_payslip_2019
from . import test_us_va_virginia_payslip_2020
from . import test_us_wa_washington_payslip_2019
from . import test_us_wa_washington_payslip_2020
+
+from . import test_us_wv_west_virginia_payslip_2020
+
+from . import test_us_wi_wisconsin_payslip_2020
+
+from . import test_us_wy_wyoming_payslip_2019
+from . import test_us_wy_wyoming_payslip_2020
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
index 055c95cb..23865fc7 100644
--- 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
@@ -33,4 +33,4 @@ class TestUsALPayslip(TestUsPayslip):
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)
+ self._test_sit(5500.0, '', True, 2.0, 150, 'weekly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2020.py
index bf630b6c..6afe3d4d 100644
--- a/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_ar_arkansas_payslip_2020.py
@@ -30,6 +30,6 @@ class TestUsARPayslip(TestUsPayslip):
self._test_er_suta('AR', self.AR_UNEMP, date(2020, 1, 1), wage_base=self.AR_UNEMP_MAX_WAGE)
self._test_sit(5000.0, True, 0.0, 0, 'monthly', date(2020, 1, 1), 0.0)
self._test_sit(5000.0, False, 0.0, 0, 'monthly', date(2020, 1, 1), 221.0)
- self._test_sit(5000.0, False, 0.0, 150, 'monthly', date(2020, 1, 1), 371.0)
- self._test_sit(5000.0, False, 2.0, 0, 'monthly', date(2020, 1, 1), 217)
- self._test_sit(5000.0, False, 2.0, 150, 'monthly', date(2020, 1, 1), 367)
+ self._test_sit(700.0, False, 0.0, 150, 'weekly', date(2020, 1, 1), 175.0)
+ self._test_sit(7000.0, False, 2.0, 0, 'semi-monthly', date(2020, 1, 1), 420.0)
+ self._test_sit(3000.0, False, 1.0, 0, 'bi-weekly', date(2020, 1, 1), 142.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py
index d1d14d80..248648bc 100644
--- a/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_az_arizona_payslip_2020.py
@@ -31,3 +31,4 @@ class TestUsAZPayslip(TestUsPayslip):
self._test_sit(1000.0, 10.0, 2.70, 'monthly', date(2020, 1, 1), 37.0)
self._test_sit(15000.0, 0.0, 3.60, 'weekly', date(2020, 1, 1), 540.0)
self._test_sit(8000.0, 0.0, 4.20, 'semi-monthly', date(2020, 1, 1), 336.0)
+ self._test_sit(8000.0, 0.0, 0.00, 'semi-monthly', date(2020, 1, 1), 0.0)
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
index c6c58547..264b115e 100755
--- 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
@@ -40,3 +40,4 @@ class TestUsCAPayslip(TestUsPayslip):
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)
+ self._test_sit(6000.0, '', 4, 0, 20.0, 'annually', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py
index 6e24cbb0..0fa45d9c 100755
--- a/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_co_colorado_payslip_2020.py
@@ -34,3 +34,4 @@ class TestUsCOPayslip(TestUsPayslip):
self._test_sit(20000.0, 'married', 0.0, 'quarterly', date(2020, 1, 1), 833.4)
self._test_sit(20000.0, 'married', 10.0, 'quarterly', date(2020, 1, 1), 843.4)
self._test_sit(20000.0, 'married', 0.0, 'quarterly', date(2020, 1, 1), 0.0, True)
+ self._test_sit(800.0, '', 0.0, 'weekly', date(2020, 1, 1), 0.00)
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
index a5db79a6..8ce41d06 100644
--- 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
@@ -32,3 +32,4 @@ class TestUsCTPayslip(TestUsPayslip):
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)
+ self._test_sit(500.0, 'd', 0.0, 'weekly', date(2020, 1, 1), 21.15)
diff --git a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
index 6debc2ca..21a0a810 100755
--- a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2020.py
@@ -1,7 +1,7 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-from datetime import date
-from .common import TestUsPayslip, process_payslip
+from datetime import date, timedelta
+from .common import TestUsPayslip
class TestUsGAPayslip(TestUsPayslip):
@@ -10,139 +10,30 @@ class TestUsGAPayslip(TestUsPayslip):
GA_UNEMP_MAX_WAGE = 9500.00
GA_UNEMP = 2.70
- def _run_test_sit(self,
- wage=0.0,
- schedule_pay='monthly',
- filing_status='single',
- dependent_credit=0.0,
- other_income=0.0,
- deductions=0.0,
- additional_withholding=0.0,
- is_nonresident_alien=False,
- state_income_tax_exempt=False,
- state_income_tax_additional_withholding=0.0,
- ga_g4_sit_dependent_allowances=0,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status=None,
- expected=0.0,
- ):
+ # Example calculated based on https://dor.georgia.gov/employers-tax-guide 2020_employer tax gauide
+
+ def _test_sit(self, wage, filing_status, additional_withholding, dependent_allowances, additional_allowances,
+ schedule_pay, date_start, expected_withholding):
employee = self._createEmployee()
contract = self._createContract(employee,
wage=wage,
- schedule_pay=schedule_pay,
- fed_941_fit_w4_is_nonresident_alien=is_nonresident_alien,
- fed_941_fit_w4_filing_status=filing_status,
- fed_941_fit_w4_multiple_jobs_higher=False,
- fed_941_fit_w4_dependent_credit=dependent_credit,
- fed_941_fit_w4_other_income=other_income,
- fed_941_fit_w4_deductions=deductions,
- fed_941_fit_w4_additional_withholding=additional_withholding,
- state_income_tax_exempt=state_income_tax_exempt,
- state_income_tax_additional_withholding=state_income_tax_additional_withholding,
- ga_g4_sit_dependent_allowances=ga_g4_sit_dependent_allowances,
- ga_g4_sit_additional_allowances=ga_g4_sit_additional_allowances,
- ga_g4_sit_filing_status=ga_g4_sit_filing_status,
state_id=self.get_us_state('GA'),
- )
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ ga_g4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ga_g4_sit_dependent_allowances=dependent_allowances,
+ ga_g4_sit_additional_allowances=additional_allowances,
+ schedule_pay=schedule_pay)
+ payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
payslip.compute_sheet()
cats = self._getCategories(payslip)
- # Instead of PayrollEqual after initial first round of testing.
- self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected)
- return payslip
- def test_taxes_weekly_single_with_additional_wh(self):
+ self._log('Computed period tax: ' + str(expected_withholding))
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
self._test_er_suta('GA', self.GA_UNEMP, date(2020, 1, 1), wage_base=self.GA_UNEMP_MAX_WAGE)
- salary = 15000.00
- schedule_pay = 'weekly'
- allowances = 1
- filing_status = 'single'
- additional_wh = 12.50
- # Hand Calculated Amount to Test
- # Step 1 - Subtract standard deduction from wages. Std Deduct for single weekly is 88.50
- # step1 = 15000.00 - 88.50 = 14911.5
- # Step 2 - Subtract personal allowance from step1. Allowance for single weekly is 51.92
- # step2 = step1 - 51.92 = 14859.58
- # Step 3 - Subtract amount for dependents. Weekly dependent allowance is 57.50
- # step3 = 14859.58 - 57.50 = 14802.08
- # Step 4 -Determine wh amount from tables
- # step4 = 4.42 + ((5.75 / 100.00) * (14802.08 - 135.00))
- # Add additional_wh
- # wh = 847.7771 + 12.50 = 860.2771
- wh = 860.28
-
- self._run_test_sit(wage=salary,
- schedule_pay=schedule_pay,
- state_income_tax_additional_withholding=additional_wh,
- ga_g4_sit_dependent_allowances=allowances,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status=filing_status,
- expected=wh,
- )
-
-
- def test_taxes_monthly_head_of_household(self):
- salary = 25000.00
- schedule_pay = 'monthly'
- allowances = 2
- filing_status = 'head of household'
- additional_wh = 15.00
- # Hand Calculated Amount to Test
- # Step 1 - Subtract standard deduction from wages. Std Deduct for head of household monthly is 383.50
- # step1 = 25000.00 - 383.50 = 24616.5
- # Step 2 - Subtract personal allowance from step1. Allowance for head of household monthly is 225.00
- # step2 = 24616.5 - 225.00 = 24391.5
- # Step 3 - Subtract amount for dependents. Weekly dependent allowance is 250.00
- # step3 = 24391.5 - (2 * 250.00) = 23891.5
- # Step 4 - Determine wh amount from tables
- # step4 = 28.33 + ((5.75 / 100.00) * (23891.5 - 833.00)) = 1354.19375
- # Add additional_wh
- # wh = 1354.19375 + 15.00 = 1369.19375
- wh = 1369.19
-
- self._run_test_sit(wage=salary,
- schedule_pay=schedule_pay,
- state_income_tax_additional_withholding=additional_wh,
- ga_g4_sit_dependent_allowances=allowances,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status=filing_status,
- expected=wh,
- )
-
- # additional from external calculator
- self._run_test_sit(wage=425.0,
- schedule_pay='weekly',
- state_income_tax_additional_withholding=0.0,
- ga_g4_sit_dependent_allowances=1,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status='married filing separate',
- expected=11.45,
- )
-
- self._run_test_sit(wage=3000.0,
- schedule_pay='quarterly',
- state_income_tax_additional_withholding=0.0,
- ga_g4_sit_dependent_allowances=1,
- ga_g4_sit_additional_allowances=1,
- ga_g4_sit_filing_status='single',
- expected=0.0,
- )
-
- # TODO 'married filing joint, both spouses working' returns lower than calculator
- # TODO 'married filing joint, one spouse working' returns lower than calculator
-
- def test_taxes_exempt(self):
- salary = 25000.00
- schedule_pay = 'monthly'
- allowances = 2
- filing_status = 'exempt'
- additional_wh = 15.00
-
- self._run_test_sit(wage=salary,
- schedule_pay=schedule_pay,
- state_income_tax_additional_withholding=additional_wh,
- ga_g4_sit_dependent_allowances=allowances,
- ga_g4_sit_additional_allowances=0,
- ga_g4_sit_filing_status=filing_status,
- expected=0.0,
- )
+ self._test_sit(15000.0, 'single', 12.50, 1, 0, 'weekly', date(2020, 1, 1), 860.28)
+ self._test_sit(25000.0, 'head of household', 15.00, 2, 0, 'monthly', date(2020, 1, 1), 1369.19)
+ self._test_sit(425.0, 'married filing separate', 0.0, 1, 0, 'weekly', date(2020, 1, 1), 11.45)
+ self._test_sit(3000.0, 'single', 0.00, 1, 1, 'quarterly', date(2020, 1, 1), 0.0)
+ self._test_sit(2500.0, '', 0.00, 1, 1, 'quarterly', date(2020, 1, 1), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py
index 9684c52d..9a746057 100755
--- a/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_hi_hawaii_payslip_2020.py
@@ -33,3 +33,5 @@ class TestUsHIPayslip(TestUsPayslip):
self._test_sit(5000.0, 'married', 0.0, 2.0, 'monthly', date(2020, 1, 1), 287.1)
self._test_sit(5000.0, 'married', 10.0, 2.0, 'monthly', date(2020, 1, 1), 297.1)
self._test_sit(50000.0, 'head_of_household', 0.0, 3.0, 'weekly', date(2020, 1, 1), 3933.65)
+ self._test_sit(750.0, 'single', 10.0, 3.0, 'bi-weekly', date(2020, 1, 1), 40.59)
+ self._test_sit(3000.0, '', 0.0, 3.0, 'weekly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py
index d5d66b16..eaca0e71 100755
--- a/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_ia_iowa_payslip_2020.py
@@ -11,11 +11,12 @@ class TestUsIAPayslip(TestUsPayslip):
IA_UNEMP_MAX_WAGE = 31600.00
IA_UNEMP = 1.0
- def _test_sit(self, wage, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
+ def _test_sit(self, wage, exempt, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
employee = self._createEmployee()
contract = self._createContract(employee,
wage=wage,
state_id=self.get_us_state('IA'),
+ state_income_tax_exempt=exempt,
state_income_tax_additional_withholding=additional_withholding,
ia_w4_sit_allowances=allowances,
schedule_pay=schedule_pay)
@@ -24,10 +25,12 @@ class TestUsIAPayslip(TestUsPayslip):
cats = self._getCategories(payslip)
self._log('Computed period tax: ' + str(expected_withholding))
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
def test_2020_taxes_example(self):
self._test_er_suta('IA', self.IA_UNEMP, date(2020, 1, 1), wage_base=self.IA_UNEMP_MAX_WAGE)
- self._test_sit(3000.0, 0.0, 1.0, 'bi-weekly', date(2020, 1, 1), 146.68)
- self._test_sit(3000.0, 10.0, 1.0, 'bi-weekly', date(2020, 1, 1), 156.68)
- self._test_sit(30000.0, 0.0, 1.0, 'weekly', date(2020, 1, 1), 1640.04)
+ self._test_sit(2100.0, False, 0.0, 3.0, 'bi-weekly', date(2020, 1, 1), 83.5)
+ self._test_sit(3000.0, True, 10.0, 1.0, 'bi-weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(300.0, False, 0.0, 1.0, 'weekly', date(2020, 1, 1), 6.77)
+ self._test_sit(5000.0, False, 0.0, 1.0, 'monthly', date(2020, 1, 1), 230.76)
+ self._test_sit(7500.0, False, 10.0, 2.0, 'semi-monthly', date(2020, 1, 1), 432.84)
diff --git a/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2020.py
index bf687080..eb0da9a7 100755
--- a/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_id_idaho_payslip_2020.py
@@ -30,6 +30,6 @@ class TestUsIDPayslip(TestUsPayslip):
self._test_er_suta('ID', self.ID_UNEMP, date(2020, 1, 1), wage_base=self.ID_UNEMP_MAX_WAGE)
self._test_sit(1212.0, 'single', 4.0, 'bi-weekly', date(2020, 1, 1), 10.0)
self._test_sit(10000.0, 'married', 1.0, 'annually', date(2020, 1, 1), 0.0)
- self._test_sit(52000.0, 'married', 4.0, 'monthly', date(2020, 1, 1), 3348.02)
+ self._test_sit(52000.0, 'married', 4.0, 'monthly', date(2020, 1, 1), 3345.0)
self._test_sit(5000.0, 'head of household', 0.0, 'semi-monthly', date(2020, 1, 1), 300.0)
self._test_sit(5900.0, 'single', 5.0, 'weekly', date(2020, 1, 1), 367.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_in_indiana_payslip_2020.py
new file mode 100755
index 00000000..53b7ddf3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_in_indiana_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 TestUsINPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ IN_UNEMP_MAX_WAGE = 9500.0
+ IN_UNEMP = 2.5
+ # Calculation based on https://www.in.gov/dor/files/dn01.pdf
+
+ def _test_sit(self, wage, additional_withholding, personal_exemption, dependent_exemption, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('IN'),
+ state_income_tax_additional_withholding=additional_withholding,
+ in_w4_sit_personal_exemption=personal_exemption,
+ in_w4_sit_dependent_exemption=dependent_exemption,
+ 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('IN', self.IN_UNEMP, date(2020, 1, 1), wage_base=self.IN_UNEMP_MAX_WAGE)
+ self._test_sit(800.0, 0.0, 5.0, 3.0, 'weekly', date(2020, 1, 1), 19.94)
+ self._test_sit(800.0, 10.0, 5.0, 3.0, 'weekly', date(2020, 1, 1), 29.94)
+ self._test_sit(9000.0, 0.0, 4.0, 3.0, 'monthly', date(2020, 1, 1), 267.82)
+ self._test_sit(10000.0, 0.0, 2.0, 2.0, 'bi-weekly', date(2020, 1, 1), 316.79)
diff --git a/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ks_kansas_payslip_2020.py
new file mode 100755
index 00000000..3ed586f8
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ks_kansas_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 TestUsKSPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ KS_UNEMP_MAX_WAGE = 14000.0
+ KS_UNEMP = 2.7
+ # Calculation based on example https://revenue.ky.gov/Forms/42A003(T)%20(12-2019)%202020%20Tax%20Tables.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('KS'),
+ ks_k4_sit_filing_status=filing_status,
+ ks_k4_sit_allowances=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._log('Computed period tax: ' + str(expected_withholding))
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('KS', self.KS_UNEMP, date(2020, 1, 1), wage_base=self.KS_UNEMP_MAX_WAGE)
+ self._test_sit(6250, 'married', 2, 0, 'semi-monthly', date(2020, 1, 1), 290.00)
+ self._test_sit(5000, 'single', 1, 0, 'monthly', date(2020, 1, 1), 222.00)
+ self._test_sit(1500, 'married', 0, 0, 'bi-weekly', date(2020, 1, 1), 39.00)
+ self._test_sit(750, 'single', 2, 10, 'weekly', date(2020, 1, 1), 36.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2020.py
new file mode 100755
index 00000000..aa067848
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ky_kentucky_payslip_2020.py
@@ -0,0 +1,35 @@
+# 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 TestUsKYPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ KY_UNEMP_MAX_WAGE = 10800.0
+ KY_UNEMP = 2.7
+ # Calculation based on example https://revenue.ky.gov/Forms/42A003(T)%20(12-2019)%202020%20Tax%20Tables.pdf
+
+ def _test_sit(self, wage, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('KY'),
+ 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('KY', self.KY_UNEMP, date(2020, 1, 1), wage_base=self.KY_UNEMP_MAX_WAGE)
+ self._test_sit(3020, 0.0, 'monthly', date(2020, 1, 1), 139.96)
+ self._test_sit(1500, 0.0, 'bi-weekly', date(2020, 1, 1), 69.90)
+ self._test_sit(1500, 10.0, 'bi-weekly', date(2020, 1, 1), 79.90)
+ self._test_sit(750, 00.0, 'weekly', date(2020, 1, 1), 34.95)
+ self._test_sit(7000, 0.0, 'semi-monthly', date(2020, 1, 1), 344.48)
diff --git a/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
new file mode 100644
index 00000000..1d01c618
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2019.py
@@ -0,0 +1,91 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsLAPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ LA_UNEMP_MAX_WAGE = 7700.00
+ LA_UNEMP = -(1.14 / 100.0)
+
+ def test_taxes_single_weekly(self):
+ salary = 700.00
+ schedule_pay = 'weekly'
+ filing_status = 'single'
+ exemptions = 1
+ dependents = 2
+ additional_withholding = 0
+ # SEE http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf for example calculations
+ # wh_to test is 19.42
+ # Our algorithm correctly rounds whereas theirs does it prematurely.
+ wh_to_check = -19.43
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('LA'),
+ la_l4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ la_l4_sit_exemptions=exemptions,
+ la_l4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Louisiana 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.LA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
+
+ process_payslip(payslip)
+
+ remaining_la_unemp_wages = self.LA_UNEMP_MAX_WAGE - salary if (self.LA_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Louisiana 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_la_unemp_wages * self.LA_UNEMP)
+
+ def test_taxes_married_biweekly(self):
+ salary = 4600.00
+ schedule_pay = 'bi-weekly'
+ filing_status = 'married'
+ exemptions = 2
+ dependents = 3
+ additional_withholding = 0
+ # SEE http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf for example calculations
+ # wh_to test is 157.12
+ wh_to_check = -157.12
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('LA'),
+ la_l4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ la_l4_sit_exemptions=exemptions,
+ la_l4_sit_dependents=dependents,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 Louisiana tax first payslip bi-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.LA_UNEMP)
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh_to_check)
+
+ process_payslip(payslip)
+
+ remaining_la_unemp_wages = self.LA_UNEMP_MAX_WAGE - salary if (self.LA_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Louisiana tax second payslip bi-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_la_unemp_wages * self.LA_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_la_louisiana_payslip_2020.py
new file mode 100755
index 00000000..d23c3ab3
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_la_louisiana_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 TestUsLAPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ LA_UNEMP_MAX_WAGE = 7700.0
+ LA_UNEMP = 1.14
+ # Calculation based on http://revenue.louisiana.gov/TaxForms/1306(1_12)TF.pdf
+
+ def _test_sit(self, wage, filing_status, additional_withholding, exemptions, dependents, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('LA'),
+ la_l4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ la_l4_sit_exemptions=exemptions,
+ la_l4_sit_dependents=dependents,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('LA', self.LA_UNEMP, date(2020, 1, 1), wage_base=self.LA_UNEMP_MAX_WAGE)
+ self._test_sit(700.0, 'single', 0.0, 1.0, 2.0, 'weekly', date(2020, 1, 1), 19.43)
+ self._test_sit(4600.0, 'married', 0.0, 2.0, 3.0, 'bi-weekly', date(2020, 1, 1), 157.12)
+ self._test_sit(6000.0, 'single', 10.0, 2.0, 3.0, 'monthly', date(2020, 1, 1), 219.08)
diff --git a/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py
new file mode 100644
index 00000000..165455ce
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_me_maine_payslip_2020.py
@@ -0,0 +1,39 @@
+# 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 TestUsMEPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ ME_UNEMP_MAX_WAGE = 12000.0
+ ME_UNEMP = 1.92
+ # Calculation based on this file page.6 and 7 https://www.maine.gov/revenue/forms/with/2020/20_WH_Tab&Instructions.pdf
+
+ def _test_sit(self, wage, filing_status, additional_withholding, exempt, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('ME'),
+ me_w4me_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ me_w4me_sit_allowances=allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('ME', self.ME_UNEMP, date(2020, 1, 1), wage_base=self.ME_UNEMP_MAX_WAGE)
+ self._test_sit(300.0, 'single', 0.0, False, 2, 'weekly', date(2020, 1, 1), 0.0)
+ self._test_sit(800.0, 'single', 0.0, False, 2, 'bi-weekly', date(2020, 1, 1), 6.00)
+ self._test_sit(4500.0, 'married', 0.0, True, 0, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(4500.0, 'married', 0.0, False, 2, 'monthly', date(2020, 1, 1), 113.00)
+ self._test_sit(4500.0, 'married', 10.0, False, 2, 'weekly', date(2020, 1, 1), 287.00)
+ self._test_sit(7000.0, '', 10.0, False, 2, 'weekly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2020.py
index 7d178d76..6de7b664 100755
--- a/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_mi_michigan_payslip_2020.py
@@ -27,9 +27,9 @@ class TestUsMIPayslip(TestUsPayslip):
def test_2020_taxes_example(self):
self._test_er_suta('MI', self.MI_UNEMP, date(2020, 1, 1), wage_base=self.MI_UNEMP_MAX_WAGE)
- self._test_sit(5000.0, 1, 100.0, 'weekly', date(2020, 1, 1), 308.62)
- self._test_sit(5000.0, 1, 0.0, 'weekly', date(2020, 1, 1), 208.62)
+ self._test_sit(750.0, 1, 100.0, 'weekly', date(2020, 1, 1), 127.99)
+ self._test_sit(1750.0, 1, 0.0, 'bi-weekly', date(2020, 1, 1), 66.61)
self._test_sit(5000.0, 1, 5.0, 'semi-monthly', date(2020, 1, 1), 209.09)
- self._test_sit(5000.0, 1, 5.0, 'monthly', date(2020, 1, 1), 200.68)
- self._test_sit(5000.0, 200, 0.0, 'monthly', date(2020, 1, 1), 0.0)
+ self._test_sit(8000.0, 1, 5.0, 'monthly', date(2020, 1, 1), 328.18)
+ self._test_sit(5000.0, 2, 0.0, 'monthly', date(2020, 1, 1), 178.86)
diff --git a/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py
index 164b0f0f..ff6a0ca1 100755
--- a/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_mo_missouri_payslip_2020.py
@@ -1,5 +1,6 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-from datetime import date
+from datetime import date, timedelta
from .common import TestUsPayslip
@@ -8,98 +9,26 @@ class TestUsMoPayslip(TestUsPayslip):
MO_UNEMP_MAX_WAGE = 11500.0
MO_UNEMP = 2.376
- TAX = [
- (1073.0, 1.5),
- (1073.0, 2.0),
- (1073.0, 2.5),
- (1073.0, 3.0),
- (1073.0, 3.5),
- (1073.0, 4.0),
- (1073.0, 4.5),
- (1073.0, 5.0),
- ( 'inf', 5.4),
- ]
- STD_DED = {
- '': 0.0, # Exempt
- 'single': 12400.0,
- 'married': 24800.0,
- 'head_of_household': 18650.0,
- }
-
- def _test_sit(self, filing_status, schedule_pay):
- wage = 5000.0
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
employee = self._createEmployee()
contract = self._createContract(employee,
wage=wage,
state_id=self.get_us_state('MO'),
mo_mow4_sit_filing_status=filing_status,
- state_income_tax_additional_withholding=0.0,
+ state_income_tax_additional_withholding=additional_withholding,
schedule_pay=schedule_pay)
-
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
+ payslip = self._createPayslip(employee, date_start, date_start + timedelta(days=7))
payslip.compute_sheet()
cats = self._getCategories(payslip)
- pp = payslip.get_pay_periods_in_year()
- gross_salary = wage * pp
- standard_deduction = self.STD_DED[filing_status]
+ self._log('Computed period tax: ' + str(expected_withholding))
+ self.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
- mo_taxable_income = gross_salary - standard_deduction
- self._log('%s = %s - %s -' % (mo_taxable_income, gross_salary, standard_deduction))
-
- remaining_taxable_income = mo_taxable_income
- tax = 0.0
- for amt, rate in self.TAX:
- amt = float(amt)
- rate = rate / 100.0
- self._log(str(amt) + ' : ' + str(rate) + ' : ' + str(remaining_taxable_income))
- if (remaining_taxable_income - amt) > 0.0 or (remaining_taxable_income - amt) == 0.0:
- tax += rate * amt
- else:
- tax += rate * remaining_taxable_income
- break
- remaining_taxable_income = remaining_taxable_income - amt
-
- tax = -tax
- self._log('Computed annual tax: ' + str(tax))
-
- tax = tax / pp
- tax = round(tax)
- self._log('Computed period tax: ' + str(tax))
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), tax if filing_status else 0.0)
-
- contract.us_payroll_config_id.state_income_tax_additional_withholding = 100.0
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), (tax - 100.0) if filing_status else 0.0)
-
- contract.us_payroll_config_id.mo_mow4_sit_withholding = 200.0
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), -200.0 if filing_status else 0.0)
-
- def test_2020_taxes_single(self):
+ def test_2020_taxes_example(self):
self._test_er_suta('MO', self.MO_UNEMP, date(2020, 1, 1), wage_base=self.MO_UNEMP_MAX_WAGE)
- self._test_sit('single', 'weekly')
+ self._test_sit(750.0, 'single', 0.0, 'weekly', date(2020, 1, 1), 24.00)
+ self._test_sit(2500.0, 'single', 5.0, 'bi-weekly', date(2020, 1, 1), 107.00)
+ self._test_sit(7000.0, 'married', 0.0, 'monthly', date(2020, 1, 1), 251.00)
+ self._test_sit(5000.0, 'married', 10.0, 'semi-monthly', date(2020, 1, 1), 217.00)
+ self._test_sit(6000.0, '', 0.0, 'monthly', date(2020, 1, 1), 0.00)
- def test_2020_spouse_not_employed(self):
- self._test_sit('married', 'semi-monthly')
-
- def test_2020_head_of_household(self):
- self._test_sit('head_of_household', 'monthly')
-
- def test_2020_underflow(self):
- # Payroll Period Weekly
- salary = 200.0
-
- employee = self._createEmployee()
-
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MO'))
-
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- self.assertPayrollEqual(cats['EE_US_SIT'], 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py
index 5942d7ad..ea0081ca 100755
--- a/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_ms_mississippi_payslip_2020.py
@@ -1,120 +1,35 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-from datetime import date
+from datetime import date, timedelta
from .common import TestUsPayslip
class TestUsMsPayslip(TestUsPayslip):
- # Calculations from https://www.dor.ms.gov/Documents/Computer%20Payroll%20Accounting%201-2-19.pdf
+ # Calculations from https://www.dor.ms.gov/Documents/Computer%20Payroll%20Flowchart.pdf
MS_UNEMP = 1.2
MS_UNEMP_MAX_WAGE = 14000.0
- def test_2020_taxes_one(self):
+ def _test_sit(self, wage, filing_status, additional_withholding, exemption, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MS'),
+ ms_89_350_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ ms_89_350_sit_exemption_value=exemption,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
self._test_er_suta('MS', self.MS_UNEMP, date(2020, 1, 1), wage_base=self.MS_UNEMP_MAX_WAGE)
+ self._test_sit(1250.0, 'head_of_household', 0.0, 11000, 'semi-monthly', date(2020, 1, 1), 22.00)
+ self._test_sit(500.0, '', 5.0, 0, 'bi-weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(12000.0, 'single', 0.0, 11000, 'monthly', date(2020, 1, 1), 525.00)
+ self._test_sit(2500.0, 'married', 5.0, 500, 'bi-weekly', date(2020, 1, 1), 111.00)
- salary = 1250.0
- ms_89_350_exemption = 11000.0
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MS'),
- ms_89_350_sit_filing_status='head_of_household',
- ms_89_350_sit_exemption_value=ms_89_350_exemption,
- state_income_tax_additional_withholding=0.0,
- schedule_pay='semi-monthly')
-
- self._log('2020 Mississippi tax single first payslip:')
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- STDED = 3400.0 # Head of Household
- AGP = salary * 24 # Semi-Monthly
- TI = AGP - (ms_89_350_exemption + STDED)
- self.assertPayrollEqual(TI, 15600.0)
- TAX = ((TI - 10000) * 0.05) + 260 # Over 10,000
- self.assertPayrollEqual(TAX, 540.0)
-
- ms_withhold = round(TAX / 24) # Semi-Monthly
- self.assertPayrollEqual(cats['EE_US_SIT'], -ms_withhold)
-
- def test_2020_taxes_one_exempt(self):
- salary = 1250.0
- ms_89_350_exemption = 11000.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MS'),
- ms_89_350_sit_filing_status='',
- ms_89_350_sit_exemption_value=ms_89_350_exemption,
- state_income_tax_additional_withholding=0.0,
- schedule_pay='semi-monthly')
-
- self._log('2020 Mississippi tax single first payslip:')
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
- self.assertPayrollEqual(cats.get('EE_US_SIT', 0.0), 0.0)
-
- def test_2020_taxes_additional(self):
- salary = 1250.0
- ms_89_350_exemption = 11000.0
- additional = 40.0
-
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MS'),
- ms_89_350_sit_filing_status='single',
- ms_89_350_sit_exemption_value=ms_89_350_exemption,
- state_income_tax_additional_withholding=additional,
- schedule_pay='monthly')
-
- self._log('2020 Mississippi tax single first payslip:')
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
-
- payslip.compute_sheet()
-
- cats = self._getCategories(payslip)
-
- STDED = 2300.0 # Single
- AGP = salary * 12 # Monthly
- TI = AGP - (ms_89_350_exemption + STDED)
- self.assertPayrollEqual(TI, 1700.0)
- TAX = ((TI - 3000) * 0.03)
- self.assertPayrollEqual(TAX, -39.0)
-
- ms_withhold = round(TAX / 12) # Monthly
- self.assertTrue(ms_withhold <= 0.0)
- self.assertPayrollEqual(cats['EE_US_SIT'], -40.0) # only additional
-
- # Test with higher wage
- salary = 1700.0
- employee = self._createEmployee()
- contract = self._createContract(employee,
- wage=salary,
- state_id=self.get_us_state('MS'),
- ms_89_350_sit_filing_status='single',
- ms_89_350_sit_exemption_value=ms_89_350_exemption,
- state_income_tax_additional_withholding=additional,
- schedule_pay='monthly')
- payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
- payslip.compute_sheet()
- cats = self._getCategories(payslip)
-
- STDED = 2300.0 # Single
- AGP = salary * 12 # Monthly
- TI = AGP - (ms_89_350_exemption + STDED)
- self.assertPayrollEqual(TI, 7100.0)
- TAX = ((TI - 5000) * 0.04) + 60.0
- self.assertPayrollEqual(TAX, 144.0)
-
- ms_withhold = round(TAX / 12) # Monthly
- self.assertPayrollEqual(ms_withhold, 12.0)
- self.assertPayrollEqual(cats['EE_US_SIT'], -(ms_withhold + additional))
diff --git a/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
index ec861a0d..13a7e69f 100755
--- a/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_mt_montana_payslip_2020.py
@@ -1,17 +1,37 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-from datetime import date
-from .common import TestUsPayslip, process_payslip
+from datetime import date, timedelta
+from .common import TestUsPayslip
class TestUsMtPayslip(TestUsPayslip):
- # Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705
+ ###
+ # 2020 Taxes and Rates
+ ###
MT_UNEMP_WAGE_MAX = 34100.0
MT_UNEMP = 1.18
MT_UNEMP_AFT = 0.13
+ # Calculations from https://app.mt.gov/myrevenue/Endpoint/DownloadPdf?yearId=705
+ def _test_sit(self, wage, additional_withholding, exemptions, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('MT'),
+ state_income_tax_additional_withholding=additional_withholding,
+ mt_mw4_sit_exemptions=exemptions,
+ 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_one(self):
combined_rate = self.MT_UNEMP + self.MT_UNEMP_AFT # Combined for test as they both go to the same category and have the same cap
self._test_er_suta('MT', combined_rate, date(2020, 1, 1), wage_base=self.MT_UNEMP_WAGE_MAX)
-
- # TODO Montana Incometax rates for 2020 when released
+ self._test_sit(550.0, 0.0, 5.0, 'semi-monthly', date(2020, 1, 1), 3.0)
+ self._test_sit(2950.0, 10.0, 2.0, 'bi-weekly', date(2020, 1, 1), 162.0)
+ self._test_sit(5000.0, 0.0, 1.0, 'monthly', date(2020, 1, 1), 256.0)
+ self._test_sit(750.0, 0.0, 1.0, 'weekly', date(2020, 1, 1), 34.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py
index 2c484ac4..8e2d69c1 100755
--- a/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_nc_northcarolina_payslip_2020.py
@@ -11,7 +11,7 @@ class TestUsNCPayslip(TestUsPayslip):
NC_UNEMP_MAX_WAGE = 25200.0
NC_UNEMP = 1.0
NC_INC_TAX = 0.0535
-
+ # Example based on https://files.nc.gov/ncdor/documents/files/NC-30_book_Web_1-16-19_v4_Final.pdf
def _test_sit(self, wage, filing_status, allowances, additional_withholding, schedule_pay, date_start, expected_withholding):
employee = self._createEmployee()
@@ -34,3 +34,4 @@ class TestUsNCPayslip(TestUsPayslip):
self._test_sit(20000.0, 'single', 1, 100.0, 'weekly', date(2020, 1, 1), 1156.0)
self._test_sit(5000.0, 'married', 1, 0.0, 'weekly', date(2020, 1, 1), 254.0)
self._test_sit(4000.0, 'head_household', 1, 5.0, 'semi-monthly', date(2020, 1, 1), 177.0)
+ self._test_sit(7000.0, '', 1, 5.0, 'monthly', date(2020, 1, 1), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py
new file mode 100644
index 00000000..903cf816
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nd_north_dakota_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsNDPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ ND_UNEMP_MAX_WAGE = 37900.0
+ ND_UNEMP = 1.02
+ # Calculation based on this file page.47 https://www.nd.gov/tax/data/upfiles/media/rates-and-instructions.pdf?20200110115917
+
+ def _test_sit(self, wage, filing_status, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('ND'),
+ nd_w4_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional_withholding,
+ nd_w4_sit_allowances=allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('ND', self.ND_UNEMP, date(2020, 1, 1), wage_base=self.ND_UNEMP_MAX_WAGE)
+ self._test_sit(700.0, 'single', 0.0, 0.0, 'weekly', date(2020, 1, 1), 6.0)
+ self._test_sit(5000.0, 'married', 0.0, 2.0, 'bi-weekly', date(2020, 1, 1), 76.0)
+ self._test_sit(25000.0, 'head_household', 0.0, 0.0, 'monthly', date(2020, 1, 1), 534.0)
+ self._test_sit(25000.0, 'head_household', 10.0, 2.0, 'monthly', date(2020, 1, 1), 525.0)
+ self._test_sit(3000.0, '', 10.0, 2.0, 'monthly', date(2020, 1, 1), 0.0)
diff --git a/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py
new file mode 100644
index 00000000..785e4417
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ne_nebraska_payslip_2020.py
@@ -0,0 +1,38 @@
+# 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 TestUsNEPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NE_UNEMP_MAX_WAGE = 9000.0
+ NE_UNEMP = 1.25
+
+ def _test_sit(self, wage, filing_status, exempt, additional_withholding, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('NE'),
+ ne_w4n_sit_filing_status=filing_status,
+ state_income_tax_exempt=exempt,
+ state_income_tax_additional_withholding=additional_withholding,
+ ne_w4n_sit_allowances=allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('NE', self.NE_UNEMP, date(2020, 1, 1), wage_base=self.NE_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 'single', False, 0.0, 2, 'weekly', date(2020, 1, 1), 27.53)
+ self._test_sit(9500.0, 'single', False, 0.0, 1, 'bi-weekly', date(2020, 1, 1), 612.63)
+ self._test_sit(10500.0, 'married', False, 0.0, 1, 'bi-weekly', date(2020, 1, 1), 659.85)
+ self._test_sit(9500.0, 'single', True, 0.0, 1, 'bi-weekly', date(2020, 1, 1), 0.0)
+ self._test_sit(10500.0, 'single', False, 10.0, 2, 'monthly', date(2020, 1, 1), 625.2)
+ self._test_sit(4000.0, 'single', False, 0.0, 1, 'monthly', date(2020, 1, 1), 179.44)
diff --git a/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py
index 79e0b861..1df4af6a 100755
--- a/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_nj_newjersey_payslip_2020.py
@@ -46,3 +46,6 @@ class TestUsNJPayslip(TestUsPayslip):
self._test_sit(300.0, 'single', 1, 'weekly', date(2020, 1, 1), 4.21)
self._test_sit(375.0, 'married_separate', 3, 'weekly', date(2020, 1, 1), 4.76)
self._test_sit(1400.0, 'head_household', 3, 'weekly', date(2020, 1, 1), 27.60)
+ self._test_sit(1400.0, '', 3, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(2500.0, 'single', 3, 'bi-weekly', date(2020, 1, 1), 82.66)
+ self._test_sit(15000.0, 'married_joint', 2, 'monthly', date(2020, 1, 1), 844.85)
diff --git a/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py
index 0ab6c321..24f8c5a4 100755
--- a/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py
+++ b/l10n_us_hr_payroll/tests/test_us_nm_new_mexico_payslip_2020.py
@@ -33,3 +33,4 @@ class TestUsNMPayslip(TestUsPayslip):
self._test_sit(1000.0, 'married', 10.0, 'weekly', date(2020, 1, 1), 39.47)
self._test_sit(25000.0, 'single', 0.0, 'bi-weekly', date(2020, 1, 1), 1202.60)
self._test_sit(25000.0, 'married_as_single', 0.0, 'monthly', date(2020, 1, 1), 1152.95)
+ self._test_sit(4400.0, '', 0.0, 'monthly', date(2020, 1, 1), 0.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py
new file mode 100755
index 00000000..52c2114b
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_nv_nevada_payslip_2020.py
@@ -0,0 +1,16 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsNVPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ NV_UNEMP_MAX_WAGE = 32500.0
+ NV_UNEMP = 2.95
+
+ def test_2020_taxes(self):
+ # Only has state unemployment
+ self._test_er_suta('NV', self.NV_UNEMP, date(2020, 1, 1), wage_base=self.NV_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
index 2c3b9306..eeb1f6dc 100644
--- a/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
+++ b/l10n_us_hr_payroll/tests/test_us_ny_new_york_payslip_2019.py
@@ -131,3 +131,30 @@ class TestUsNYPayslip(TestUsPayslip):
self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+ def test_exempt_example3(self):
+ salary = 50000
+ schedule_pay = 'monthly'
+ allowances = 3
+ additional = 0
+ filing_status = ''
+ wh = 0.0
+
+ employee = self._createEmployee()
+
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('NY'),
+ ny_it2104_sit_filing_status=filing_status,
+ state_income_tax_additional_withholding=additional,
+ ny_it2104_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 New York tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+
+ payslip.compute_sheet()
+
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['EE_US_SIT'], wh)
+
diff --git a/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py
new file mode 100755
index 00000000..cacdcc16
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ok_oklahoma_payslip_2020.py
@@ -0,0 +1,38 @@
+# 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 TestUsOKPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ OK_UNEMP_MAX_WAGE = 18700.0
+ OK_UNEMP = 1.5
+ # Calculation based on example https://www.ok.gov/tax/documents/2020WHTables.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('OK'),
+ ok_w4_sit_filing_status=filing_status,
+ ok_w4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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('OK', self.OK_UNEMP, date(2020, 1, 1), wage_base=self.OK_UNEMP_MAX_WAGE)
+ self._test_sit(1825, 'married', 2, 0, False, 'semi-monthly', date(2020, 1, 1), 46.00)
+ self._test_sit(1825, 'married', 2, 0, True, 'monthly', date(2020, 1, 1), 0.00)
+ self._test_sit(1000, 'single', 1, 0, False, 'weekly', date(2020, 1, 1), 39.00)
+ self._test_sit(1000, 'single', 1, 10, False, 'weekly', date(2020, 1, 1), 49.00)
+ self._test_sit(5000, 'head_household', 2, 10, False, 'monthly', date(2020, 1, 1), 210.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py
new file mode 100755
index 00000000..9d92fec1
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_ri_rhode_island_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsRIPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ RI_UNEMP_MAX_WAGE = 24000.0
+ RI_UNEMP = 1.06
+ # Calculation based on example http://www.tax.ri.gov/forms/2020/Withholding/2020%20Withhholding%20Tax%20Booklet.pdf
+
+ def _test_sit(self, wage, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('RI'),
+ ri_w4_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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('RI', self.RI_UNEMP, date(2020, 1, 1), wage_base=self.RI_UNEMP_MAX_WAGE)
+ self._test_sit(2195, 1, 0, False, 'weekly', date(2020, 1, 1), 90.80)
+ self._test_sit(1800, 2, 10, True, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(10000, 1, 0, False, 'bi-weekly', date(2020, 1, 1), 503.15)
+ self._test_sit(18000, 2, 0, False, 'monthly', date(2020, 1, 1), 860.54)
+ self._test_sit(18000, 2, 10, False, 'monthly', date(2020, 1, 1), 870.55)
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
new file mode 100644
index 00000000..793f84c4
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2019.py
@@ -0,0 +1,97 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsSCPayslip(TestUsPayslip):
+
+ # Taxes and Rates
+ SC_UNEMP_MAX_WAGE = 14000.0
+ US_SC_UNEMP = -1.09 / 100
+ US_SC_exemption_amount = 2510.00
+
+ def test_2019_taxes_weekly(self):
+ # We will hand calculate the amount to test for state withholding.
+ schedule_pay = 'weekly'
+ salary = 50000.00 # Employee is paid 50000 per week to be in top tax bracket
+ allowances = 2
+ # Calculate annual wages
+ annual = 50000 * 52.0
+ # From our annual we deduct personal exemption amounts.
+ # We deduct 2510.00 per exemption. Since we have two exemptions:
+ personal_exemption = self.US_SC_exemption_amount * allowances # 5020.0
+ # From annual, we will also deduct a standard_deduction of 3470.00 or .1 of salary, which ever
+ # is small -> if 1 or more exemptions, else 0
+ standard_deduction = 3470.00
+ taxable_income = annual - personal_exemption - standard_deduction # 2591510.0
+ # We then calculate the amounts off the SC tax pdf tables.
+ # 2591478.0 is in the highest bracket
+ test_amt = (taxable_income * (7.0 / 100.0)) - 467.95
+ test_amt = 180935.51
+ # Make it per period then negative
+ test_amt = (test_amt / 52.0) # Divided by 52 since it is weekly.
+ # test_amt = 3479.52
+ test_amt = -test_amt
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('SC'),
+ state_income_tax_exempt=False,
+ sc_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 South Carolina tax first payslip:')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollAlmostEqual(cats['ER_US_SUTA'], self.SC_UNEMP_MAX_WAGE * self.US_SC_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], test_amt)
+
+ process_payslip(payslip)
+
+ remaining_SC_UNEMP_wages = self.SC_UNEMP_MAX_WAGE - annual if (annual < self.SC_UNEMP_MAX_WAGE) \
+ else 0.00
+
+ self._log('2019 South Carolina tax second payslip:')
+
+ payslip = self._createPayslip(employee, '2019-02-01', '2019-02-28')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertEqual(0.0, remaining_SC_UNEMP_wages)
+ self.assertPayrollEqual(cats['ER_US_SUTA'], remaining_SC_UNEMP_wages * self.US_SC_UNEMP)
+
+ def test_2019_taxes_filing_status(self):
+ salary = 20000.00 # Wages per pay period
+ schedule_pay = 'monthly'
+ annual = salary * 12
+ allowances = 1
+ # Hand Calculations
+ personal_exemption = 2510.00
+ standard_deduction = min(3470.00, .1 * annual) # 3470.0 but min is shown for the process
+ taxable = annual - personal_exemption - standard_deduction
+ # taxable = 234020
+ test_amt = ((taxable) * (7.0 / 100.0)) - 467.95 # 15991.850000000002
+ test_amt = test_amt / 12.0 # Put it into monthly -> 1332.654166666667
+ # Make it negative
+ test_amt = -test_amt
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('SC'),
+ state_income_tax_exempt=False,
+ sc_w4_sit_allowances=allowances,
+ schedule_pay=schedule_pay)
+
+ self._log('2019 South Carolina tax first payslip: ')
+ payslip = self._createPayslip(employee, '2019-01-01', '2019-01-31')
+ payslip.compute_sheet()
+ cats = self._getCategories(payslip)
+
+ self.assertPayrollEqual(cats['ER_US_SUTA'], self.SC_UNEMP_MAX_WAGE * self.US_SC_UNEMP)
+ self.assertPayrollAlmostEqual(cats['EE_US_SIT'], test_amt)
+
+ process_payslip(payslip)
diff --git a/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_payslip_2020.py
new file mode 100644
index 00000000..170c3bf5
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sc_south_carolina_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 TestUsSCPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ SC_UNEMP_MAX_WAGE = 14000.0
+ SC_UNEMP = 0.55
+ # Calculation based on https://dor.sc.gov/forms-site/Forms/WH1603F_2020.pdf
+
+ def _test_sit(self, wage, additional_withholding, exempt, allowances, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('SC'),
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ sc_w4_sit_allowances=allowances,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('SC', self.SC_UNEMP, date(2020, 1, 1), wage_base=self.SC_UNEMP_MAX_WAGE)
+ self._test_sit(750.0, 0.0, False, 3.0, 'weekly', date(2020, 1, 1), 28.73)
+ self._test_sit(800.0, 0.0, True, 0.0, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(9000.0, 0.0, False, 0.0, 'monthly', date(2020, 1, 1), 594.61)
+ self._test_sit(5000.0, 10.0, False, 2.0, 'semi-monthly', date(2020, 1, 1), 316.06)
diff --git a/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py
new file mode 100644
index 00000000..bdbb7858
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_sd_south_dakota_payslip_2020.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsSDPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ SD_UNEMP_MAX_WAGE = 15000.00
+ SD_UNEMP = 1.75
+
+ def test_2020_taxes(self):
+ self._test_er_suta('SD', self.SD_UNEMP, date(2020, 1, 1), wage_base=self.SD_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py
new file mode 100644
index 00000000..54acfa9a
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_tn_tennessee_payslip_2020.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsTNPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ TN_UNEMP_MAX_WAGE = 7000.00
+ TN_UNEMP = 2.7
+
+ def test_2020_taxes(self):
+ self._test_er_suta('TN', self.TN_UNEMP, date(2020, 1, 1), wage_base=self.TN_UNEMP_MAX_WAGE)
diff --git a/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_us_utah_payslip_2020.py
new file mode 100755
index 00000000..a6881acb
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_us_utah_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 TestUsUTPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ UT_UNEMP_MAX_WAGE = 36600.0
+ UT_UNEMP = 0.1
+ # Calculation based on example https://tax.utah.gov/forms/pubs/pub-14.pdf
+
+ def _test_sit(self, wage, filing_status, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('UT'),
+ ut_w4_sit_filing_status=filing_status,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('UT', self.UT_UNEMP, date(2020, 1, 1), wage_base=self.UT_UNEMP_MAX_WAGE)
+ self._test_sit(400, 'single', 0, 'weekly', date(2020, 1, 1), 16.00)
+ self._test_sit(1000, 'single', 0, 'bi-weekly', date(2020, 1, 1), 45.00)
+ self._test_sit(855, 'married', 0, 'semi-monthly', date(2020, 1, 1), 16.00)
+ self._test_sit(2500, 'married', 0, 'monthly', date(2020, 1, 1), 81.00)
+ self._test_sit(8000, 'head_household', 10, 'quarterly', date(2020, 1, 1), 397.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.py
new file mode 100755
index 00000000..7807bed7
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_vt_vermont_payslip_2020.py
@@ -0,0 +1,37 @@
+# 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 TestUsVTPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ VT_UNEMP_MAX_WAGE = 16100.0
+ VT_UNEMP = 1.0
+ # Calculation based on example https://tax.vermont.gov/sites/tax/files/documents/WithholdingInstructions.pdf
+
+ def _test_sit(self, wage, filing_status, allowances, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('VT'),
+ vt_w4vt_sit_filing_status=filing_status,
+ vt_w4vt_sit_allowances=allowances,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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('VT', self.VT_UNEMP, date(2020, 1, 1), wage_base=self.VT_UNEMP_MAX_WAGE)
+ self._test_sit(1800, 'married', 2, 0, False, 'weekly', date(2020, 1, 1), 53.73)
+ self._test_sit(1800, 'married', 2, 10, False, 'weekly', date(2020, 1, 1), 63.73)
+ self._test_sit(1000, 'single', 1, 0, True, 'weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(8000, 'single', 1, 10, False, 'bi-weekly', date(2020, 1, 1), 506.58)
diff --git a/l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2020.py
new file mode 100755
index 00000000..32bdfa30
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wi_wisconsin_payslip_2020.py
@@ -0,0 +1,39 @@
+# 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 TestUsWIPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ WI_UNEMP_MAX_WAGE = 14000.0
+ WI_UNEMP = 3.05
+ # Calculation based on example https://www.revenue.wi.gov/DOR%20Publications/pb166.pdf
+
+ def _test_sit(self, wage, filing_status, exemption, additional_withholding, exempt, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('WI'),
+ wi_wt4_sit_filing_status=filing_status,
+ wi_wt4_sit_exemptions=exemption,
+ state_income_tax_additional_withholding=additional_withholding,
+ state_income_tax_exempt=exempt,
+ 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.assertPayrollAlmostEqual(cats.get('EE_US_SIT', 0.0), -expected_withholding)
+
+ def test_2020_taxes_example(self):
+ self._test_er_suta('WI', self.WI_UNEMP, date(2020, 1, 1), wage_base=self.WI_UNEMP_MAX_WAGE)
+ self._test_sit(300, 'single', 1, 0, False, 'weekly', date(2020, 1, 1), 7.21)
+ self._test_sit(700, 'married', 3, 0, False, 'bi-weekly', date(2020, 1, 1), 13.35)
+ self._test_sit(7000, 'single', 1, 10, True, 'bi-weekly', date(2020, 1, 1), 0.00)
+ self._test_sit(10000, 'married', 3, 10, False, 'bi-weekly', date(2020, 1, 1), 633.65)
+ # ((48000 - 26227) * (7.0224 /100) + 1073.55 - 44) / 12
+ self._test_sit(4000, 'single', 2, 0, False, 'monthly', date(2020, 1, 1), 213.21)
diff --git a/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_payslip_2020.py
new file mode 100755
index 00000000..acef111e
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wv_west_virginia_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 TestUsWVPayslip(TestUsPayslip):
+ ###
+ # 2020 Taxes and Rates
+ ###
+ WV_UNEMP_MAX_WAGE = 12000.0
+ WV_UNEMP = 2.7
+ # Calculation based on example https://tax.wv.gov/Documents/TaxForms/it100.1a.pdf
+
+ def _test_sit(self, wage, filing_status, exemption, additional_withholding, schedule_pay, date_start, expected_withholding):
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=wage,
+ state_id=self.get_us_state('WV'),
+ wv_it104_sit_filing_status=filing_status,
+ wv_it104_sit_exemptions=exemption,
+ 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('WV', self.WV_UNEMP, date(2020, 1, 1), wage_base=self.WV_UNEMP_MAX_WAGE)
+ self._test_sit(1250, 'married', 2, 0, 'semi-monthly', date(2020, 1, 1), 44.00)
+ self._test_sit(1300, 'single', 1, 0, 'bi-weekly', date(2020, 1, 1), 46.00)
+ self._test_sit(1300, 'single', 1, 10, 'bi-weekly', date(2020, 1, 1), 56.00)
+ self._test_sit(15000, 'single', 2, 0, 'monthly', date(2020, 1, 1), 860.00)
diff --git a/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
new file mode 100644
index 00000000..a8fa3df8
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2019.py
@@ -0,0 +1,58 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip, process_payslip
+
+
+class TestUsWYPayslip(TestUsPayslip):
+
+ # TAXES AND RATES
+ WY_UNEMP_MAX_WAGE = 25400
+ WY_UNEMP = -2.10 / 100.0
+
+ def test_2019_taxes(self):
+ salary = 15000.00
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('WY'))
+
+ self._log('2019 Wyoming 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.WY_UNEMP)
+
+ process_payslip(payslip)
+
+ # Make a new payslip, this one will have maximums
+
+ remaining_wy_unemp_wages = self.WY_UNEMP_MAX_WAGE - salary if (self.WY_UNEMP_MAX_WAGE - 2*salary < salary) \
+ else salary
+
+ self._log('2019 Wyoming 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'], remaining_wy_unemp_wages * self.WY_UNEMP)
+
+ def test_2019_taxes_with_external(self):
+ # Wage is the cap itself, 25400
+ # so salary is equal to self.WY_UNEMP
+ salary = 25400
+
+ employee = self._createEmployee()
+ contract = self._createContract(employee,
+ wage=salary,
+ state_id=self.get_us_state('WY'))
+
+ self._log('2019 Wyoming External 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.WY_UNEMP)
diff --git a/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py
new file mode 100644
index 00000000..b6ca4482
--- /dev/null
+++ b/l10n_us_hr_payroll/tests/test_us_wy_wyoming_payslip_2020.py
@@ -0,0 +1,13 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from datetime import date
+from .common import TestUsPayslip
+
+
+class TestUsWYPayslip(TestUsPayslip):
+ # TAXES AND RATES
+ WY_UNEMP_MAX_WAGE = 26400.00
+ WY_UNEMP = 8.5
+
+ def test_2020_taxes(self):
+ self._test_er_suta('WY', self.WY_UNEMP, date(2020, 1, 1), wage_base=self.WY_UNEMP_MAX_WAGE)
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 c464e524..94d316bf 100644
--- a/l10n_us_hr_payroll/views/us_payroll_config_views.xml
+++ b/l10n_us_hr_payroll/views/us_payroll_config_views.xml
@@ -119,6 +119,38 @@
+
+ Form IN WH-4 - State Income Tax
+
+
+
+
+
+ Form KS K-4 - State Income Tax
+
+
+
+
+
+
+ No additional fields.
+
+
+
+
+ Form LA L-4 - State Income Tax
+
+
+
+
+
+
+ Form W-4ME - State Income Tax
+
+
+
+
+
Form MI-W4 - State Income Tax
@@ -155,6 +187,18 @@
+
+ Form ND W-4 - State Income Tax
+
+
+
+
+ Form NC-4 - State Income Tax
+
+
+
+
+
No additional fields.
@@ -169,6 +213,9 @@
Form NM W-4 - State Income Tax
+
+ No additional fields.
+
Form NY IT-2104 - State Income Tax
@@ -181,13 +228,50 @@
+
+ Form OK-W-4 - State Income Tax
+
+
+
+
+
+
+ Form RI W-4 - State Income Tax
+
+
+
+
+
+ Form SC W-4 - State Income Tax
+
+
+
+
+
+ No additional fields.
+
+
+ No additional fields.
+
No additional fields.
+
+ Form UT W-4 - State Income Tax
+
+
+
+
+ Form VT W-4VT - State Income Tax
+
+
+
+
+
Form VA-4/VA-4P - State Income Tax
@@ -199,6 +283,22 @@
No additional fields.
Ensure that your Employee and Employer workers' comp code fields are filled in for WA LNI withholding.
+
+ Form WV/IT-104 - State Income Tax
+
+
+
+
+
+ Form WT-4 - State Income Tax
+
+
+
+
+
+
+ No additional fields.
+
From 7516682e0b4a6f198c636da8e85570712a39ed12 Mon Sep 17 00:00:00 2001
From: Bhoomi Vaishnani
Date: Wed, 26 Aug 2020 12:34:08 -0400
Subject: [PATCH 38/43] [FIX] l10n_us_hr_payroll: Fixed test case for exempt
in test case of 2019 and extra character on tax table for following states.
California, Georgia, Idaho, New Mexico and Alabama For Odoo12.0
---
.../data/state/ca_california.xml | 1 +
l10n_us_hr_payroll/data/state/ga_georgia.xml | 2 +-
l10n_us_hr_payroll/data/state/id_idaho.xml | 2 +-
.../data/state/nm_new_mexico.xml | 420 +++++++++---------
.../tests/test_us_al_alabama_payslip_2019.py | 2 +-
.../tests/test_us_ga_georgia_payslip_2019.py | 2 +-
6 files changed, 215 insertions(+), 214 deletions(-)
diff --git a/l10n_us_hr_payroll/data/state/ca_california.xml b/l10n_us_hr_payroll/data/state/ca_california.xml
index 0bd29bda..663699bb 100644
--- a/l10n_us_hr_payroll/data/state/ca_california.xml
+++ b/l10n_us_hr_payroll/data/state/ca_california.xml
@@ -334,6 +334,7 @@
US CA California SIT Tax Rate
us_ca_sit_tax_rate
{
+ 'head_household': {
'weekly': (
( 339, 0.0110, 0.00),
( 803, 0.0220, 3.73),
diff --git a/l10n_us_hr_payroll/data/state/ga_georgia.xml b/l10n_us_hr_payroll/data/state/ga_georgia.xml
index d9365e35..e53cf36d 100644
--- a/l10n_us_hr_payroll/data/state/ga_georgia.xml
+++ b/l10n_us_hr_payroll/data/state/ga_georgia.xml
@@ -94,7 +94,7 @@
(5000.00, 95.00, 5.00),
( 'inf', 170.00, 5.75),
),
- },},
+ },
'married filing joint, one spouse working': {
'weekly': (
( 19.00, 0.00, 1.00),
diff --git a/l10n_us_hr_payroll/data/state/id_idaho.xml b/l10n_us_hr_payroll/data/state/id_idaho.xml
index 3f572493..7331326e 100644
--- a/l10n_us_hr_payroll/data/state/id_idaho.xml
+++ b/l10n_us_hr_payroll/data/state/id_idaho.xml
@@ -305,7 +305,7 @@
(47508, 558.00, 6.625),
('inf', 1068.00, 6.925),
),
- },},
+ },
'head of household': {
'weekly': (
( 238, 0.00, 0.000),
diff --git a/l10n_us_hr_payroll/data/state/nm_new_mexico.xml b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
index 66c9cc36..9a8238f7 100644
--- a/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
+++ b/l10n_us_hr_payroll/data/state/nm_new_mexico.xml
@@ -27,220 +27,220 @@
us_nm_sit_tax_rate
{
'single': {
-+ 'weekly': (
-+ ( 119, 0.00, 0.0),
-+ ( 225, 0.00, 1.7),
-+ ( 331, 1.80, 3.2),
-+ ( 427, 5.18, 4.7),
-+ ( 619, 9.70, 4.9),
-+ ( 927, 19.13, 4.9),
-+ ( 1369, 34.20, 4.9),
-+ ('inf', 55.88, 4.9),
-+ ),
-+ 'bi-weekly': (
-+ ( 238, 0.00, 0.0),
-+ ( 450, 0.00, 1.7),
-+ ( 662, 3.60, 3.2),
-+ ( 854, 10.37, 4.7),
-+ ( 1238, 19.40, 4.9),
-+ ( 1854, 38.25, 4.9),
-+ ( 2738, 68.40, 4.9),
-+ ('inf', 111.75, 4.9),
-+ ),
-+ 'semi-monthly': (
-+ ( 258, 0.00, 0.0),
-+ ( 488, 0.00, 1.7),
-+ ( 717, 3.90, 3.2),
-+ ( 925, 11.23, 4.7),
-+ ( 1342, 21.02, 4.9),
-+ ( 2008, 41.44, 4.9),
-+ ( 2967, 74.10, 4.9),
-+ ('inf', 121.06, 4.9),
-+ ),
-+ 'monthly': (
-+ ( 517, 0.00, 0.0),
-+ ( 975, 0.00, 1.7),
-+ ( 1433, 7.79, 3.2),
-+ ( 1850, 22.46, 4.7),
-+ ( 2683, 42.04, 4.9),
-+ ( 4017, 82.88, 4.9),
-+ ( 5933, 148.21, 4.9),
-+ ('inf', 242.13, 4.9),
-+ ),
-+ 'quarterly': (
-+ ( 1550, 0.00, 0.0),
-+ ( 2925, 0.00, 1.7),
-+ ( 4300, 23.38, 3.2),
-+ ( 5550, 67.38, 4.7),
-+ ( 8050, 126.13, 4.9),
-+ ( 12050, 248.63, 4.9),
-+ ( 17800, 444.63, 4.9),
-+ ( 'inf', 726.38, 4.9),
-+ ),
-+ 'semi-annual': (
-+ ( 3100, 0.00, 0.0),
-+ ( 5850, 0.00, 1.7),
-+ ( 8600, 46.75, 3.2),
-+ (11100, 134.75, 4.7),
-+ (16100, 252.25, 4.9),
-+ (24100, 497.25, 4.9),
-+ (35600, 889.25, 4.9),
-+ ('inf', 1452.75, 4.9),
-+ ),
-+ 'annually': (
-+ ( 6200, 0.00, 0.0),
-+ (11700, 0.00, 1.7),
-+ (17200, 93.50, 3.2),
-+ (22200, 269.50, 4.7),
-+ (32200, 504.50, 4.9),
-+ (48200, 994.50, 4.9),
-+ (71200, 1778.50, 4.9),
-+ ('inf', 2905.50, 4.9),
-+ ),
+ 'weekly': (
+ ( 119, 0.00, 0.0),
+ ( 225, 0.00, 1.7),
+ ( 331, 1.80, 3.2),
+ ( 427, 5.18, 4.7),
+ ( 619, 9.70, 4.9),
+ ( 927, 19.13, 4.9),
+ ( 1369, 34.20, 4.9),
+ ('inf', 55.88, 4.9),
+ ),
+ 'bi-weekly': (
+ ( 238, 0.00, 0.0),
+ ( 450, 0.00, 1.7),
+ ( 662, 3.60, 3.2),
+ ( 854, 10.37, 4.7),
+ ( 1238, 19.40, 4.9),
+ ( 1854, 38.25, 4.9),
+ ( 2738, 68.40, 4.9),
+ ('inf', 111.75, 4.9),
+ ),
+ 'semi-monthly': (
+ ( 258, 0.00, 0.0),
+ ( 488, 0.00, 1.7),
+ ( 717, 3.90, 3.2),
+ ( 925, 11.23, 4.7),
+ ( 1342, 21.02, 4.9),
+ ( 2008, 41.44, 4.9),
+ ( 2967, 74.10, 4.9),
+ ('inf', 121.06, 4.9),
+ ),
+ 'monthly': (
+ ( 517, 0.00, 0.0),
+ ( 975, 0.00, 1.7),
+ ( 1433, 7.79, 3.2),
+ ( 1850, 22.46, 4.7),
+ ( 2683, 42.04, 4.9),
+ ( 4017, 82.88, 4.9),
+ ( 5933, 148.21, 4.9),
+ ('inf', 242.13, 4.9),
+ ),
+ 'quarterly': (
+ ( 1550, 0.00, 0.0),
+ ( 2925, 0.00, 1.7),
+ ( 4300, 23.38, 3.2),
+ ( 5550, 67.38, 4.7),
+ ( 8050, 126.13, 4.9),
+ ( 12050, 248.63, 4.9),
+ ( 17800, 444.63, 4.9),
+ ( 'inf', 726.38, 4.9),
+ ),
+ 'semi-annual': (
+ ( 3100, 0.00, 0.0),
+ ( 5850, 0.00, 1.7),
+ ( 8600, 46.75, 3.2),
+ (11100, 134.75, 4.7),
+ (16100, 252.25, 4.9),
+ (24100, 497.25, 4.9),
+ (35600, 889.25, 4.9),
+ ('inf', 1452.75, 4.9),
+ ),
+ 'annually': (
+ ( 6200, 0.00, 0.0),
+ (11700, 0.00, 1.7),
+ (17200, 93.50, 3.2),
+ (22200, 269.50, 4.7),
+ (32200, 504.50, 4.9),
+ (48200, 994.50, 4.9),
+ (71200, 1778.50, 4.9),
+ ('inf', 2905.50, 4.9),
+ ),
},
'married': {
-+ 'weekly': (
-+ ( 238, 0.00, 0.0),
-+ ( 392, 0.00, 1.7),
-+ ( 546, 2.62, 3.2),
-+ ( 700, 7.54, 4.7),
-+ ( 1008, 14.77, 4.9),
-+ ( 1469, 29.85, 4.9),
-+ ( 2162, 52.46, 4.9),
-+ ('inf', 86.38, 4.9),
-+ ),
-+ 'bi-weekly': (
-+ ( 477, 0.00, 0.0),
-+ ( 785, 0.00, 1.7),
-+ ( 1092, 5.23, 3.2),
-+ ( 1400, 15.08, 4.7),
-+ (2015, 29.54, 4.9),
-+ ( 2938, 59.69, 4.9),
-+ ( 4323, 104.92, 4.9),
-+ ('inf', 172.77, 4.9),
-+ ),
-+ 'semi-monthly': (
-+ ( 517, 0.00, 0.0),
-+ ( 850, 0.00, 1.7),
-+ ( 1183, 5.67, 3.2),
-+ ( 1517, 16.33, 4.7),
-+ ( 2183, 32.00, 4.9),
-+ ( 3183, 64.67, 4.9),
-+ ( 4683, 113.67, 4.9),
-+ ('inf', 187.17, 4.9),
-+ ),
-+ 'monthly': (
-+ ( 1033, 0.00, 0.0),
-+ ( 1700, 0.00, 1.7),
-+ ( 2367, 11.33, 3.2),
-+ ( 3033, 32.67, 4.7),
-+ ( 4367, 64.00, 4.9),
-+ ( 6367, 129.33, 4.9),
-+ ( 9367, 227.33, 4.9),
-+ ('inf', 374.33, 4.9),
-+ ),
-+ 'quarterly': (
-+ ( 3100, 0.00, 0.0),
-+ ( 5100, 0.00, 1.7),
-+ ( 7100, 34.00, 3.2),
-+ ( 9100, 98.00, 4.7),
-+ (13100, 192.00, 4.9),
-+ (19100, 388.00, 4.9),
-+ (28100, 682.00, 4.9),
-+ ('inf', 1123.00, 4.9),
-+ ),
-+ 'semi-annual': (
-+ ( 6200, 0.00, 0.0),
-+ (10200, 0.00, 1.7),
-+ (14200, 68.00, 3.2),
-+ (18200, 196.00, 4.7),
-+ (26200, 384.00, 4.9),
-+ (38200, 776.00, 4.9),
-+ (56200, 1364.00, 4.9),
-+ ('inf', 2246.00, 4.9),
-+ ),
-+ 'annually': (
-+ ( 12400, 0.00, 0.0),
-+ ( 20400, 0.00, 1.7),
-+ ( 28400, 136.00, 3.2),
-+ ( 36400, 392.00, 4.7),
-+ ( 52400, 768.00, 4.9),
-+ ( 76400, 1552.00, 4.9),
-+ (112400, 2728.00, 4.9),
-+ ( 'inf', 4492.00, 4.9),
-+ ),
+ 'weekly': (
+ ( 238, 0.00, 0.0),
+ ( 392, 0.00, 1.7),
+ ( 546, 2.62, 3.2),
+ ( 700, 7.54, 4.7),
+ ( 1008, 14.77, 4.9),
+ ( 1469, 29.85, 4.9),
+ ( 2162, 52.46, 4.9),
+ ('inf', 86.38, 4.9),
+ ),
+ 'bi-weekly': (
+ ( 477, 0.00, 0.0),
+ ( 785, 0.00, 1.7),
+ ( 1092, 5.23, 3.2),
+ ( 1400, 15.08, 4.7),
+ (2015, 29.54, 4.9),
+ ( 2938, 59.69, 4.9),
+ ( 4323, 104.92, 4.9),
+ ('inf', 172.77, 4.9),
+ ),
+ 'semi-monthly': (
+ ( 517, 0.00, 0.0),
+ ( 850, 0.00, 1.7),
+ ( 1183, 5.67, 3.2),
+ ( 1517, 16.33, 4.7),
+ ( 2183, 32.00, 4.9),
+ ( 3183, 64.67, 4.9),
+ ( 4683, 113.67, 4.9),
+ ('inf', 187.17, 4.9),
+ ),
+ 'monthly': (
+ ( 1033, 0.00, 0.0),
+ ( 1700, 0.00, 1.7),
+ ( 2367, 11.33, 3.2),
+ ( 3033, 32.67, 4.7),
+ ( 4367, 64.00, 4.9),
+ ( 6367, 129.33, 4.9),
+ ( 9367, 227.33, 4.9),
+ ('inf', 374.33, 4.9),
+ ),
+ 'quarterly': (
+ ( 3100, 0.00, 0.0),
+ ( 5100, 0.00, 1.7),
+ ( 7100, 34.00, 3.2),
+ ( 9100, 98.00, 4.7),
+ (13100, 192.00, 4.9),
+ (19100, 388.00, 4.9),
+ (28100, 682.00, 4.9),
+ ('inf', 1123.00, 4.9),
+ ),
+ 'semi-annual': (
+ ( 6200, 0.00, 0.0),
+ (10200, 0.00, 1.7),
+ (14200, 68.00, 3.2),
+ (18200, 196.00, 4.7),
+ (26200, 384.00, 4.9),
+ (38200, 776.00, 4.9),
+ (56200, 1364.00, 4.9),
+ ('inf', 2246.00, 4.9),
+ ),
+ 'annually': (
+ ( 12400, 0.00, 0.0),
+ ( 20400, 0.00, 1.7),
+ ( 28400, 136.00, 3.2),
+ ( 36400, 392.00, 4.7),
+ ( 52400, 768.00, 4.9),
+ ( 76400, 1552.00, 4.9),
+ (112400, 2728.00, 4.9),
+ ( 'inf', 4492.00, 4.9),
+ ),
},
'married_as_single': {
-+ 'weekly': (
-+ ( 179, 0.00, 0.0),
-+ ( 333, 0.00, 1.7),
-+ ( 487, 2.62, 3.2),
-+ ( 641, 7.54, 4.7),
-+ ( 949, 14.77, 4.9),
-+ ( 1410, 29.85, 4.9),
-+ ( 2102, 52.46, 4.9),
-+ ('inf', 86.38, 4.9),
-+ ),
-+ 'bi-weekly': (
-+ ( 359, 0.00, 0.0),
-+ ( 666, 0.00, 1.7),
-+ ( 974, 5.23, 3.2),
-+ ( 1282, 15.08, 4.7),
-+ ( 1897, 29.54, 4.9),
-+ ( 2820, 59.69, 4.9),
-+ ( 4205, 104.92, 4.9),
-+ ('inf', 172.77, 4.9),
-+ ),
-+ 'semi-monthly': (
-+ ( 389, 0.00, 0.0),
-+ ( 722, 0.00, 1.7),
-+ ( 1055, 5.67, 3.2),
-+ ( 1389, 16.33, 4.7),
-+ ( 2055, 32.00, 4.9),
-+ ( 3055, 64.67, 4.9),
-+ ( 4555, 113.67, 4.9),
-+ ('inf', 187.17, 4.9),
-+ ),
-+ 'monthly': (
-+ ( 777, 0.00, 0.0),
-+ ( 1444, 0.00, 1.7),
-+ ( 2110, 11.33, 3.2),
-+ ( 2777, 32.67, 4.7),
-+ ( 4110, 64.00, 4.9),
-+ ( 6110, 129.33, 4.9),
-+ ( 9110, 227.33, 4.9),
-+ ('inf', 374.33, 4.9),
-+ ),
-+ 'quarterly': (
-+ ( 2331, 0.00, 0.0),
-+ ( 4331, 0.00, 1.7),
-+ ( 6331, 34.00, 3.2),
-+ ( 8331, 98.00, 4.7),
-+ ( 12331, 192.00, 4.9),
-+ ( 18331, 388.00, 4.9),
-+ ( 27331, 682.00, 4.9),
-+ ( 'inf', 1123.00, 4.9),
-+ ),
-+ 'semi-annual': (
-+ ( 4663, 0.00, 0.0),
-+ ( 8663, 0.00, 1.7),
-+ ( 12663, 68.00, 3.2),
-+ ( 16663, 196.00, 4.7),
-+ ( 24663, 384.00, 4.9),
-+ ( 36663, 776.00, 4.9),
-+ ( 54663, 1364.00, 4.9),
-+ ( 'inf', 2246.00, 4.9),
-+ ),
-+ 'annually': (
-+ ( 9325, 0.00, 0.0),
-+ ( 17325, 0.00, 1.7),
-+ ( 25325, 136.00, 3.2),
-+ ( 33325, 392.00, 4.7),
-+ ( 49325, 768.00, 4.9),
-+ ( 73325, 1552.00, 4.9),
-+ (109325, 2728.00, 4.9),
-+ ( 'inf', 4492.00, 4.9),
-+ ),
+ 'weekly': (
+ ( 179, 0.00, 0.0),
+ ( 333, 0.00, 1.7),
+ ( 487, 2.62, 3.2),
+ ( 641, 7.54, 4.7),
+ ( 949, 14.77, 4.9),
+ ( 1410, 29.85, 4.9),
+ ( 2102, 52.46, 4.9),
+ ('inf', 86.38, 4.9),
+ ),
+ 'bi-weekly': (
+ ( 359, 0.00, 0.0),
+ ( 666, 0.00, 1.7),
+ ( 974, 5.23, 3.2),
+ ( 1282, 15.08, 4.7),
+ ( 1897, 29.54, 4.9),
+ ( 2820, 59.69, 4.9),
+ ( 4205, 104.92, 4.9),
+ ('inf', 172.77, 4.9),
+ ),
+ 'semi-monthly': (
+ ( 389, 0.00, 0.0),
+ ( 722, 0.00, 1.7),
+ ( 1055, 5.67, 3.2),
+ ( 1389, 16.33, 4.7),
+ ( 2055, 32.00, 4.9),
+ ( 3055, 64.67, 4.9),
+ ( 4555, 113.67, 4.9),
+ ('inf', 187.17, 4.9),
+ ),
+ 'monthly': (
+ ( 777, 0.00, 0.0),
+ ( 1444, 0.00, 1.7),
+ ( 2110, 11.33, 3.2),
+ ( 2777, 32.67, 4.7),
+ ( 4110, 64.00, 4.9),
+ ( 6110, 129.33, 4.9),
+ ( 9110, 227.33, 4.9),
+ ('inf', 374.33, 4.9),
+ ),
+ 'quarterly': (
+ ( 2331, 0.00, 0.0),
+ ( 4331, 0.00, 1.7),
+ ( 6331, 34.00, 3.2),
+ ( 8331, 98.00, 4.7),
+ ( 12331, 192.00, 4.9),
+ ( 18331, 388.00, 4.9),
+ ( 27331, 682.00, 4.9),
+ ( 'inf', 1123.00, 4.9),
+ ),
+ 'semi-annual': (
+ ( 4663, 0.00, 0.0),
+ ( 8663, 0.00, 1.7),
+ ( 12663, 68.00, 3.2),
+ ( 16663, 196.00, 4.7),
+ ( 24663, 384.00, 4.9),
+ ( 36663, 776.00, 4.9),
+ ( 54663, 1364.00, 4.9),
+ ( 'inf', 2246.00, 4.9),
+ ),
+ 'annually': (
+ ( 9325, 0.00, 0.0),
+ ( 17325, 0.00, 1.7),
+ ( 25325, 136.00, 3.2),
+ ( 33325, 392.00, 4.7),
+ ( 49325, 768.00, 4.9),
+ ( 73325, 1552.00, 4.9),
+ (109325, 2728.00, 4.9),
+ ( 'inf', 4492.00, 4.9),
+ ),
},
}
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
index 61290314..33ddb2f9 100644
--- 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
@@ -163,7 +163,7 @@ class TestUsALPayslip(TestUsPayslip):
contract = self._createContract(employee,
wage=salary,
state_id=self.get_us_state('AL'),
- al_a4_sit_exemptions='0',
+ al_a4_sit_exemptions='',
state_income_tax_additional_withholding=0.0,
state_income_tax_exempt=True,
al_a4_sit_dependents=dependents,
diff --git a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
index b407a079..98206965 100755
--- a/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
+++ b/l10n_us_hr_payroll/tests/test_us_ga_georgia_payslip_2019.py
@@ -114,7 +114,7 @@ class TestUsGAPayslip(TestUsPayslip):
salary = 25000.00
schedule_pay = 'monthly'
allowances = 2
- filing_status = 'exempt'
+ filing_status = ''
additional_wh = 15.00
employee = self._createEmployee()
From a74b76c7db2334e8dabea7eac1eb13ddc4ca15ec Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 29 Oct 2020 13:17:42 -0700
Subject: [PATCH 39/43] [MIG] auditlog, purchase_exception,
purchase_minimum_amount: from hibou-oca existing submodules
---
auditlog | 1 +
purchase_exception | 1 +
purchase_minimum_amount | 1 +
3 files changed, 3 insertions(+)
create mode 120000 auditlog
create mode 120000 purchase_exception
create mode 120000 purchase_minimum_amount
diff --git a/auditlog b/auditlog
new file mode 120000
index 00000000..d456b705
--- /dev/null
+++ b/auditlog
@@ -0,0 +1 @@
+./external/hibou-oca/server-tools/auditlog
\ No newline at end of file
diff --git a/purchase_exception b/purchase_exception
new file mode 120000
index 00000000..25c41368
--- /dev/null
+++ b/purchase_exception
@@ -0,0 +1 @@
+./external/hibou-oca/purchase-workflow/purchase_exception
\ No newline at end of file
diff --git a/purchase_minimum_amount b/purchase_minimum_amount
new file mode 120000
index 00000000..72a12fcf
--- /dev/null
+++ b/purchase_minimum_amount
@@ -0,0 +1 @@
+./external/hibou-oca/purchase-workflow/purchase_minimum_amount
\ No newline at end of file
From 9f2878bc1794360edcb42940ef608e986a82acd0 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Thu, 29 Oct 2020 16:02:38 -0700
Subject: [PATCH 40/43] [ADD] hibou-oca/purchase-workflow submodule
---
.gitmodules | 3 +++
external/hibou-oca/purchase-workflow | 1 +
2 files changed, 4 insertions(+)
create mode 160000 external/hibou-oca/purchase-workflow
diff --git a/.gitmodules b/.gitmodules
index 24cb44ff..d6fc7e5f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -39,3 +39,6 @@
[submodule "external/hibou-oca/connector-magento"]
path = external/hibou-oca/connector-magento
url = https://github.com/hibou-io/oca-connector-magento.git
+[submodule "external/hibou-oca/purchase-workflow"]
+ path = external/hibou-oca/purchase-workflow
+ url = https://github.com/hibou-io/oca-purchase-workflow.git
diff --git a/external/hibou-oca/purchase-workflow b/external/hibou-oca/purchase-workflow
new file mode 160000
index 00000000..cafb62ab
--- /dev/null
+++ b/external/hibou-oca/purchase-workflow
@@ -0,0 +1 @@
+Subproject commit cafb62abc97d487a51bd48053ea2c7108384db92
From 157e1219cf0e4c7b78fa62c60de450f89132918e Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Fri, 30 Oct 2020 09:19:24 -0700
Subject: [PATCH 41/43] [ADD] openupgradelib to Dockerfile
---
Dockerfile | 4 ++++
Dockerfile-GitLab | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/Dockerfile b/Dockerfile
index ab5d9837..b16b215c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,9 @@
FROM hibou/hibou-odoo:12.0
+USER 0
+RUN pip install git+git://github.com/OCA/openupgradelib.git
+
+USER 104
COPY --chown=104 . /opt/odoo/hibou-suite
RUN rm /etc/odoo/odoo.conf \
&& cp /opt/odoo/hibou-suite/debian/odoo.conf /etc/odoo/odoo.conf \
diff --git a/Dockerfile-GitLab b/Dockerfile-GitLab
index 17df09f1..8697e56a 100644
--- a/Dockerfile-GitLab
+++ b/Dockerfile-GitLab
@@ -1,5 +1,9 @@
FROM registry.gitlab.com/hibou-io/hibou-odoo/odoo:RELEASE
+USER 0
+RUN pip install git+git://github.com/OCA/openupgradelib.git
+
+USER 104
COPY --chown=104 . /opt/odoo/hibou-suite
RUN rm /etc/odoo/odoo.conf \
&& cp /opt/odoo/hibou-suite/debian/odoo.conf /etc/odoo/odoo.conf \
From f42328fc6f1e3969be0ef691a9008cbe472f694a Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Fri, 30 Oct 2020 10:09:39 -0700
Subject: [PATCH 42/43] [FIX] builds with openupgradelib
---
Dockerfile | 13 ++++++++++++-
Dockerfile-GitLab | 13 ++++++++++++-
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index b16b215c..ef1d55e0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,18 @@
FROM hibou/hibou-odoo:12.0
+# All this just for openupgradelib
USER 0
-RUN pip install git+git://github.com/OCA/openupgradelib.git
+RUN set -x; \
+ apt update && apt install -y git \
+ && pip install git+git://github.com/OCA/openupgradelib.git \
+ # Clean Up
+ && apt remove -y git \
+ && apt autoremove -y \
+ && rm -rf /root/.cache \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/* \
+ && rm -rf /tmp/* \
+ ;
USER 104
COPY --chown=104 . /opt/odoo/hibou-suite
diff --git a/Dockerfile-GitLab b/Dockerfile-GitLab
index 8697e56a..4e9a4984 100644
--- a/Dockerfile-GitLab
+++ b/Dockerfile-GitLab
@@ -1,7 +1,18 @@
FROM registry.gitlab.com/hibou-io/hibou-odoo/odoo:RELEASE
+# All this just for openupgradelib
USER 0
-RUN pip install git+git://github.com/OCA/openupgradelib.git
+RUN set -x; \
+ apt update && apt install -y git \
+ && pip install git+git://github.com/OCA/openupgradelib.git \
+ # Clean Up
+ && apt remove -y git \
+ && apt autoremove -y \
+ && rm -rf /root/.cache \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/* \
+ && rm -rf /tmp/* \
+ ;
USER 104
COPY --chown=104 . /opt/odoo/hibou-suite
From 1d2983484d8f221a19944b8198ca0b0d52b5faa1 Mon Sep 17 00:00:00 2001
From: Jared Kipe
Date: Wed, 4 Nov 2020 12:48:24 -0800
Subject: [PATCH 43/43] [MIG] connector_magento_product_by_sku: to Odoo 12.0
---
external/hibou-oca/connector-magento | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/external/hibou-oca/connector-magento b/external/hibou-oca/connector-magento
index 055dafb0..3433a239 160000
--- a/external/hibou-oca/connector-magento
+++ b/external/hibou-oca/connector-magento
@@ -1 +1 @@
-Subproject commit 055dafb0d7b572edc2331cbfdf54e76bf9c97f5a
+Subproject commit 3433a2395b23414c8e64ccc8930e6b3f0b404ef0