[ADD] l10n_ca_hr_payroll: WIP Commit

H5939
This commit is contained in:
Jared Self
2021-05-25 08:54:13 -06:00
parent fdde2b41b8
commit 769693b261
33 changed files with 917 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
from . import models
def _post_install_hook(cr, registry):
"""
This method will set the default for the Payslip Sum Behavior
"""
cr.execute("SELECT id FROM ir_config_parameter WHERE key = 'hr_payroll.payslip.sum_behavior';")
existing = cr.fetchall()
if not existing:
cr.execute("INSERT INTO ir_config_parameter (key, value) VALUES ('hr_payroll.payslip.sum_behavior', 'date');")

View File

@@ -0,0 +1,29 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
{
'name': 'Canada - Payroll',
'author': 'Hibou Corp. <hello@hibou.io>',
'version': '14.0.2020.0.0',
'category': 'Payroll Localization',
'depends': [
'hr_payroll_hibou',
],
'description': """
Canada - Payroll Rules.
=========================================
""",
'data': [
'data/base.xml',
'data/federal.xml',
'security/ir.model.access.csv',
# 'views/hr_contract_views.xml',
# 'views/us_payroll_config_views.xml',
],
'demo': [
],
'auto_install': False,
'post_init_hook': '_post_install_hook',
'license': 'OPL-1',
}

View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="ca_structure_type_employee" model="hr.payroll.structure.type">
<field name="name">Canada Employee</field>
<field name="default_resource_calendar_id" ref="resource.resource_calendar_std"/>
<field name="country_id" ref="base.ca"/>
</record>
<record id="hr_ca_payroll_structure" model="hr.payroll.structure">
<field name="name">Canada Employee Standard</field>
<field name="country_id" ref="base.ca"/>
<field name="type_id" ref="l10n_ca_hr_payroll.ca_structure_type_employee"/>
<field name="unpaid_work_entry_type_ids" eval="[
(4, ref('hr_work_entry_contract.work_entry_type_unpaid_leave')),
]"/>
</record>
<record id="l10n_ca_hr_payroll.ca_structure_type_employee" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="hr_ca_payroll_structure"/>
</record>
<!-- Included in Gross Remuneration -->
<record id="hr_payroll_category_basic_ca_salary" model="hr.salary.rule.category">
<field name="name">Wages: Salary</field>
<field name="code">SALARY</field>
<field name="parent_id" ref="hr_payroll.BASIC"/>
</record>
<record id="hr_payroll_category_basic_ca_overtime" model="hr.salary.rule.category">
<field name="name">Wages: Overtime</field>
<field name="code">OVERTIME</field>
<field name="parent_id" ref="hr_payroll.BASIC"/>
</record>
<record id="hr_payroll_category_basic_ca_pension_income" model="hr.salary.rule.category">
<field name="name">Wages: Pension Income</field>
<field name="code">PENSION</field>
<field name="parent_id" ref="hr_payroll.BASIC"/>
</record>
<record id="hr_payroll_category_basic_ca_taxable_benefits" model="hr.salary.rule.category">
<field name="name">Wages: Taxable Benefits</field>
<field name="code">TAXED_BENEFITS</field>
<field name="parent_id" ref="hr_payroll.BASIC"/>
</record>
<!-- Excluded from Gross Remuneration-->
<record id="hr_payroll_category_alw_ca_bonus" model="hr.salary.rule.category">
<field name="name">Wages: Bonus</field>
<field name="code">BONUS</field>
<field name="parent_id" ref="hr_payroll.ALW"/>
</record>
<record id="hr_payroll_category_alw_ca_retro_pay_increase" model="hr.salary.rule.category">
<field name="name">Wages: Retroactive Pay Increase</field>
<field name="code">RETRO_PAY</field>
<field name="parent_id" ref="hr_payroll.ALW"/>
</record>
<record id="hr_payroll_category_alw_ca_nonperiodic_payment" model="hr.salary.rule.category">
<field name="name">Wages: Non-Periodic Payments</field>
<field name="code">NON-PERIOD</field>
<field name="parent_id" ref="hr_payroll.ALW"/>
</record>
<!-- Employee Contributed Deductions -->
<record id="hr_payroll_category_ded_ca_rpp" model="hr.salary.rule.category">
<field name="name">Deduction: Registerd Pension Plan</field>
<field name="code">DED_RPP</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_rrsp" model="hr.salary.rule.category">
<field name="name">Deduction: Registerd Retirement Savings Plan</field>
<field name="code">DED_RRSP</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_prpp" model="hr.salary.rule.category">
<field name="name">Deduction: Pooled Registered Pension Plan</field>
<field name="code">DED_RPP</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_rca" model="hr.salary.rule.category">
<field name="name">Deduction: Retirement Compensation Arrangement</field>
<field name="code">DED_RCA</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_alimony_pre_1997" model="hr.salary.rule.category">
<field name="name">Deduction: Alimony Before May 5th, 1997</field>
<field name="code">DED_ALIMONY_PRE_1997</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_maintenance_pre_1997" model="hr.salary.rule.category">
<field name="name">Deduction: Maintenance Before May 5th, 1997</field>
<field name="code">DED_ALIMONY_PRE_1997</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_union_dues" model="hr.salary.rule.category">
<field name="name">Deduction: Union Dues</field>
<field name="code">DED_UNION_DUES</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_prescribed_zone" model="hr.salary.rule.category">
<field name="name">Deduction: Living In Prescribed Zone</field>
<field name="code">DED_PRESCRIBED_ZONE</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_employee_requested" model="hr.salary.rule.category">
<field name="name">Deduction: Employee Requested</field>
<field name="code">DED_EMPLOYEE_REQUESTED</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_td1" model="hr.salary.rule.category">
<field name="name">Deduction: TD1 Deductions</field>
<field name="code">DED_TD1</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_ded_ca_td1" model="hr.salary.rule.category">
<field name="name">Deduction: TD1 Deductions</field>
<field name="code">DED_TD1</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<!-- rules for canada-->
<!-- BASIC-->
<!-- included in gross remuneration-->
<!-- salary-->
<!-- wages-->
<!-- overtime-->
<!-- pension_income-->
<!-- qualified_pension_income-->
<!-- taxable_benefits-->
<!-- not included in gross remuneration-->
<!-- bonuses-->
<!-- retroactive_pay_increase-->
<!-- nonperiodic_payments-->
<!-- DED-->
<!-- employee contributions-->
<!-- rpp registered pension plan-->
<!-- rrsp registered retirement savings plan-->
<!-- prpp pooled registered pension plan-->
<!-- rca retirement compensation arrangement-->
</odoo>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="rule_parameter_ca_fed_tax_rate" model="hr.rule.parameter">
<field name="name">CA Federal Tax Rate</field>
<field name="code">ca_fed_tax_rate</field>
<field name="country_id" ref="base.ca"/>
</record>
<data noupdate="1">
<record id="rule_parameter_ca_fed_tax_rate_2021_01" model="hr.rule.parameter.value">
<field name="parameter_value">{
'annually': (
( 0, 0.1500, 0.00),
( 49029, 0.2050, 2696.00),
( 98040, 0.2600, 8088.00),
( 151978, 0.2900, 12648.00),
( 216511, 0.3300, 21308.00),
( 'inf', 0.3300, 21308.00),
),
}</field>
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_tax_rate"/>
<field name="date_from" eval="datetime(2021, 1, 1).date()"/>
</record>
</data>
<record id="res_partner_ca_fed" model="res.partner">
<field name="name">CA Federal - Canada Revenue Agency - Federal Income Tax</field>
</record>
<!-- Rules -->
<record id="hr_payroll_rule_ee_ca_fit" model="hr.salary.rule">
<field name="sequence" eval="195"/>
<field name="struct_id" ref="hr_ca_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_basic_ca_salary"/>
<field name="name">EE: CA Federal Income Tax</field>
<field name="code">EE_CA_FIT</field>
<field name="condition_select">python</field>
<field name="condition_python">result, _ = ca_fit_federal_income_tax_withholding(payslip)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result, result_rate = ca_fit_federal_income_tax_withholding(payslip)</field>
<field name="partner_id" ref="res_partner_ca_fed"/>
<field name="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="rule_parameter_ca_cpp" model="hr.rule.parameter">
<field name="name">CA Canada Pension Plan</field>
<field name="code">ca_cpp</field>
<field name="country_id" ref="base.ca"/>
</record>
<data noupdate="1">
<record id="rule_parameter_ca_cpp_2021_01" model="hr.rule.parameter.value">
<field name="parameter_value">{
'annually': (
( 0, 0.1500, 0.00),
( 49029, 0.2050, 2696.00),
( 98040, 0.2600, 8088.00),
( 151978, 0.2900, 12648.00),
( 216511, 0.3300, 21308.00),
( 'inf', 0.3300, 21308.00),
),
}</field>
<field name="rule_parameter_id" ref="rule_parameter_ca_cpp"/>
<field name="date_from" eval="datetime(2021, 1, 1).date()"/>
</record>
</data>
<record id="res_partner_ca_cpp" model="res.partner">
<field name="name">CA Federal - Canada Revenue Agency - Canada Pension Plan</field>
</record>
<!-- Rules -->
<record id="hr_payroll_rule_ee_ca_cpp" model="hr.salary.rule">
<field name="sequence" eval="110"/>
<field name="struct_id" ref="hr_ca_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_ded_ca_rpp"/>
<field name="name">EE: CA Canada Pension Plan</field>
<field name="code">EE_CA_CPP</field>
<field name="condition_select">python</field>
<field name="condition_python">result = ca_cpp_canada_pension_plan_withholding(payslip)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = ca_cpp_canada_pension_plan_withholding(payslip)</field>
<field name="partner_id" ref="res_partner_ca_cpp"/>
<field name="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="rule_parameter_ca_fed_tax_rate" model="hr.rule.parameter">
<field name="name">CA Federal Tax Rate</field>
<field name="code">ca_fed_tax_rate</field>
<field name="country_id" ref="base.ca"/>
</record>
<data noupdate="1">
<record id="rule_parameter_ca_fed_tax_rate_2021_01" model="hr.rule.parameter.value">
<field name="parameter_value">{
'annually': (
( 0, 0.1500, 0.00),
( 49029, 0.2050, 2696.00),
( 98040, 0.2600, 8088.00),
( 151978, 0.2900, 12648.00),
( 216511, 0.3300, 21308.00),
( 'inf', 0.3300, 21308.00),
),
}</field>
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_tax_rate"/>
<field name="date_from" eval="datetime(2021, 1, 1).date()"/>
</record>
</data>
<record id="res_partner_ca_fed" model="res.partner">
<field name="name">CA Federal - Canada Revenue Agency - Federal Income Tax</field>
</record>
<!-- Rules -->
<record id="hr_payroll_rule_ee_ca_fit" model="hr.salary.rule">
<field name="sequence" eval="195"/>
<field name="struct_id" ref="hr_ca_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_basic_ca_salary"/>
<field name="name">EE: CA Federal Income Tax</field>
<field name="code">EE_CA_FIT</field>
<field name="condition_select">python</field>
<field name="condition_python">result, _ = ca_fit_federal_income_tax_withholding(payslip)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result, result_rate = ca_fit_federal_income_tax_withholding(payslip)</field>
<field name="partner_id" ref="res_partner_ca_fed"/>
<field name="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -0,0 +1,3 @@
from . import ca_payroll_config
from . import hr_ca_contract
from . import hr_payslip

View File

@@ -0,0 +1,66 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models
class HRContractCanadaPayrollConfig(models.Model):
_name = 'hr.contract.ca_payroll_config'
_description = 'Contract Canada Payroll Forms'
# Quebec https://www.revenuquebec.ca/en/online-services/tools/webras-and-winras-calculation-of-source-deductions-and-employer-contributions/
# https://www.canada.ca/en/revenue-agency/services/forms-publications/payroll/t4127-payroll-deductions-formulas/t4127-jan.html
name = fields.Char(string="Description")
employee_id = fields.Many2one('hr.employee', string="Employee", required=True)
state_id = fields.Many2one('res.country.state', string="Applied State")
state_code = fields.Char(related='state_id.code')
contributes_to_rpp = fields.Boolean(
string='Employee Contributes to a registered pension plan (RPP)?',
help='For tax deduction purposes, employers can deduct amounts contributed to an RPP, RRSP, PRPP, or RCA by or on behalf of an employee to determine the employee\'s taxable income',
)
rpp_withdrawal_per_check = fields.Float(
string='RPP to Withdrawal Per Paycheck',
help='Enter the dollar amount to be withdrawn per paycheck for a registered pension plan'
)
contributes_to_rrsp = fields.Boolean(
string='Contributes to a registered retirement savings plan (RRSP)',
help='For tax deduction purposes, employers can deduct amounts contributed to an RPP, RRSP, PRPP, or RCA by or on behalf of an employee to determine the employee\'s taxable income',
)
rrsp_withdrawal_per_check = fields.Float(
string='RRSP to Withdrawal Per Paycheck',
help='Enter the dollar amount to be withdrawn per paycheck for a registered retirement savings plan (RRSP)'
)
contributes_to_prpp = fields.Boolean(
string='Contributes to a pooled registered pension plan (PRPP)?',
help='For tax deduction purposes, employers can deduct amounts contributed to an RPP, RRSP, PRPP, or RCA by or on behalf of an employee to determine the employee\'s taxable income',
)
contributes_to_rca = fields.Boolean(
string='Contributes to a retirement compensation arrangement (RCA)?',
help='For tax deduction purposes, employers can deduct amounts contributed to an RPP, RRSP, PRPP, or RCA by or on behalf of an employee to determine the employee\'s taxable income',
)
alimony_or_maintenance_deduction_required = fields.Boolean(
string='Alimony or maintenance payments required?',
help='Annual deductions such as child care expenses and support payments, requested by an employee or pensioner and authorized by a tax services office or tax centre',
)
union_dues_deducted = fields.Boolean(
string='Dues deducted?',
help='Union dues for the pay period paid to a trade union, an association of public servants, or dues required under the law of a province to a parity or advisory committee or similar body',
)
lives_in_prescribed_zone = fields.Boolean(
string='Perscribed zone deduction?',
help='Annual deduction for living in a prescribed zone, as shown on Form TD1'
)
other_anual_deductions = fields.Boolean(
string='Other annual deductions?',
help='Annual deductions such as child care expenses and support payments, requested by an employee or pensioner and authorized by a tax services office or tax centre'
)
paid_commission = fields.Boolean(
string='Paid a commission?',
help='Does the employee receive any commissions?',
)
def ca_payroll_config_value(self, name):
return self.ca_payroll_config_id[name]

View File

@@ -0,0 +1,25 @@
def _compute_employee_contribution_deductions(payslip):
# todo: _compute_employee_contribution_deductions
return 0.0
def _compute_annual_taxable_income(payslip):
# A = Annual taxable income = [P × (I F F2 U1 )] HD F1
# # If the result is negative, T = L.
# annual_taxable_income = (
# annual_pay_periods_p
# *(
# gross_remuneration_i
# - employee_contribution_deductions_f
# - required_deductions_f2
# - union_dues_u1
# )
# - prescribed_zone_hd
# - employee_requested_deduction_f1
# )
pay_periods = payslip.dict.get_pay_periods_in_year()
annual_pay_periods_p = pay_periods[payslip.contract_id.schedule_pay]
gross_remuneration_i = annual_pay_periods_p * payslip.contract_id.wage
employee_contribution_deductions_f = _compute_employee_contribution_deductions(payslip)
required_deductions_f2 = _compute_employee_contribution_deductions(payslip)
pass

View File

@@ -0,0 +1,9 @@
from odoo import fields
from .common import TestCAPayslip
import logging
_logger = logging.getLogger("__name__")
def ca_cpp_canada_pension_plan_withholding(payslip):
_logger.warning('ca_cpp_canada_pension_plan_withholding************************')
pass

View File

@@ -0,0 +1,50 @@
import logging
_logger = logging.getLogger("__name__")
def ca_fit_federal_income_tax_withholding(payslip):
# annual_taxable_income = _compute_annual_taxable_income(payslip)
# _logger.warning('payslip.contract_id************************************')
# _logger.warning(str(payslip.contract_id.read()))
# _logger.warning('payslip.contract_id.structure_type_id.read()************************************')
# _logger.warning(str(payslip.contract_id.structure_type_id.read()))
# _logger.warning('payslip.contract_id.structure_type_id.struct_ids[0].read()************************************')
# _logger.warning(str(payslip.contract_id.structure_type_id.struct_ids[0].read()))
# _logger.warning('payslip.contract_id.structure_type_id.struct_ids[0].rule_ids[0].read()************************************')
# _logger.warning(str(payslip.contract_id.structure_type_id.struct_ids[0].rule_ids[0].read()))
_logger.warning('payslip.rule_parameter(rule_parameter_ca_fed_tax_rate)************************************')
_logger.warning(str(payslip.rule_parameter))
rates = payslip.rule_parameter('ca_fed_tax_rate')['annually'] #this is the hr.rule.parameter code
# _logger.warning(str(rates))
wage = payslip.contract_id.wage
_logger.warning(f'wage = {str(wage)}')
i = 0
_logger.warning(f'rates ================================== {str(rates)}')
for annual_taxable_income, rate, federal_constant in rates:
if isinstance(annual_taxable_income, str):
_logger.warning(f'annual_taxable_income is str {annual_taxable_income}')
_logger.warning(f'wage*rate = {str(wage*rate)}, and rate is {str(rate)}')
return wage, -rate
if annual_taxable_income/12 >= wage:
if i != 0:
_logger.warning(f'if i != 0')
rate = rates[i-1][1]*100
_logger.warning(f'rate = **************************************** {rate}')
_logger.warning(f' wage*rate = *************************************** {wage*rate}')
return wage, -rate
else:
_logger.warning(f'return 0.0, 0.0')
return 0.0, 0.0
else:
_logger.warning(f' annual_taxable_income/12 = {str(annual_taxable_income/12)} which is below wage = {str(str(wage))}*****************************')
i +=1
continue
return 0.0, 0.0

View File

@@ -0,0 +1,10 @@
from odoo import api, fields, models
class CAHRContract(models.Model):
_inherit = 'hr.contract'
ca_payroll_config_id = fields.Many2one('hr.contract.ca_payroll_config', 'Canada Payroll Forms')

View File

@@ -0,0 +1,32 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models
from .federal.ca_fit import ca_fit_federal_income_tax_withholding
class HRPayslip(models.Model):
_inherit = 'hr.payslip'
# From IRS Publication 15-T or logically (annually, bi-monthly)
PAY_PERIODS_IN_YEAR = {
'annually': 1,
'semi-annually': 2,
'quarterly': 4,
'bi-monthly': 6,
'monthly': 12,
'semi-monthly': 24,
'bi-weekly': 26,
'weekly': 52,
'daily': 260,
}
def _get_base_local_dict(self):
res = super()._get_base_local_dict()
res.update({
'ca_fit_federal_income_tax_withholding': ca_fit_federal_income_tax_withholding,
})
return res
def get_pay_periods_in_year(self):
return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_contract_ca_payroll_config,access_hr_contract_ca_payroll_config,model_hr_contract_ca_payroll_config,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_contract_ca_payroll_config access_hr_contract_ca_payroll_config model_hr_contract_ca_payroll_config base.group_user 1 1 1 1

View File

@@ -0,0 +1,2 @@
from . import common
from . import test_ca_federal_payslip

View File

@@ -0,0 +1,160 @@
from . import common
import logging
from odoo.addons.hr_payroll_hibou.tests import common
_logger = logging.getLogger("__name__")
#todo need to work in currency
class TestCAPayslip(common.TestPayslip):
def setUp(self):
super(TestCAPayslip, self).setUp()
self.structure_type = self.env.ref('l10n_ca_hr_payroll.ca_structure_type_employee')
self.structure = self.env.ref('l10n_ca_hr_payroll.hr_ca_payroll_structure')
self.structure_type.default_struct_id = self.structure
self._log('US structue_type %s and structure %s' % (self.structure_type, self.structure))
_logger.warning(str(self.structure_type))
def _createEmployee(self):
return self.env['hr.employee'].create({
'birthday': '1985-03-14',
'country_id': self.ref('base.ca'),
'department_id': self.ref('hr.dep_rd'),
'gender': 'male',
'name': 'Jared'
})
def _createCAContract(self, employee, wage=7000, pay_schedule='monthly'):
country_id = self.env['res.country'].search([('code', '=', 'CA')])
self.assertEqual(employee.country_id, country_id, 'The employee\'s country_id is not for Canada')
contract = self._createContract(employee,
wage=wage,
structure_type_id=self.env.ref(
'l10n_ca_hr_payroll.ca_structure_type_employee'),
pay_schedule=pay_schedule)
self.assertEqual(contract.wage, wage,
'The contract salary of "%s" does not equal the test salary of "%s".' % (
contract.wage, wage))
_logger.warning('Created Contract &&&&&&&&&&&&&&&&&&&&&&&')
return contract
def get_providence(self):
pass
def get_ca_cpp_canada_pension_plan_withholding(self):
_logger.warning(f'self.rpp_withdrawal_per_check = {str(self.rpp_withdrawal_per_check)} --------------------------------')
if self.rpp_withdrawal_per_check > 0:
return self.rpp_withdrawal_per_check
else:
return 0.0
# def get_ca_state(self, code, cache={}):
# country_key = 'CA_COUNTRY'
# if code in cache:
# return cache[code]
# if country_key not in cache:
# cache[country_key] = self.env.ref('base.ca')
# ca_country = cache[country_key]
# ca_state = self.env['res.country.state'].search([
# ('country_id', '=', ca_country.id),
# ('code', '=', code),
# ], limit=1)
# cache[code] = ca_state
# return ca_state
# _logger.warning(str(payslip.read()))
# start asserting
# # to work in shell
# employee = env['hr.employee'].create({
# 'birthday': '1985-03-14',
# 'country_id': env['res.country'].search([('code', '=', 'CA')]).id,
# 'department_id': env.ref('hr.dep_rd').id,
# 'gender': 'male',
# 'name': 'Jared'
# })
# schedule_pay = 'monthly'
# salary = 80000.0
# contract_values = {
# 'wage': salary,
# 'name': 'Test Contract',
# 'employee_id': employee.id,
# 'structure_type_id': env.ref("l10n_ca_hr_payroll.ca_structure_type_employee"),
# }
# contract = env['hr.contract'].create(contract_values)
# contract.structure_type_id.default_struct_id.schedule_pay = schedule_pay
# date_from = '2021-01-01'
# date_to = '2021-01-31'
# slip = env['hr.payslip'].create({
# 'name': 'Test %s From: %s To: %s' % (employee.name, date_from, date_to),
# 'employee_id': employee.id,
# 'date_from': date_from,
# 'date_to': date_to
# })
# def _createContract(self, employee, **kwargs):
# # Override
# if not 'schedule_pay' in kwargs:
# kwargs['schedule_pay'] = 'monthly'
# schedule_pay = kwargs['schedule_pay']
# config_model = self.env['hr.contract.us_payroll_config']
# contract_model = self.env['hr.contract']
# config_values = {
# 'name': 'Test Config Values',
# 'employee_id': employee.id,
# }
# contract_values = {
# 'name': 'Test Contract',
# 'employee_id': employee.id,
# }
#
# # Backwards compatability with 'futa_type'
# if 'futa_type' in kwargs:
# kwargs['fed_940_type'] = kwargs['futa_type']
#
# for key, val in kwargs.items():
# # Assume any Odoo object is in a Many2one
# if hasattr(val, 'id'):
# val = val.id
# found = False
# if hasattr(contract_model, key):
# contract_values[key] = val
# found = True
# if hasattr(config_model, key):
# config_values[key] = val
# found = True
# if not found:
# self._logger.warn('cannot locate attribute names "%s" on contract or payroll config' % (key, ))
#
# # US Payroll Config Defaults Should be set on the Model
# config = config_model.create(config_values)
# contract_values['us_payroll_config_id'] = config.id
# self._get_contract_defaults(contract_values)
# self._log('creating contract with finial values: %s' % (contract_values, ))
# contract = contract_model.create(contract_values)
#
# # Compatibility with Odoo 13/14
# contract.structure_type_id.default_struct_id.schedule_pay = schedule_pay
# return contract

View File

@@ -0,0 +1,119 @@
# from datetime import date
# from .common import TestCAPayslip
# import logging
#
# _logger = logging.getLogger("__name__")
#
#
# class TestPayslip(TestCAPayslip):
# # https://www.canada.ca/en/revenue-agency/services/forms-publications/payroll/t4127-payroll-deductions-formulas/t4127-jan/t4127-jan-payroll-deductions-formulas-computer-programs.html
#
# # P = The number of pay periods in the year:
# # Weekly P = 52 (or 53 where applicable)
# # Biweekly P = 26 (or 27 where applicable)
# # Semi-monthly P = 24
# # Monthly P = 12
# # Other P = 10, 13, 22, or any other number of pay periods for the year
#
# def test_2021_federal_payslip(self):
#
# salary = 80000.0
# annual_pay_periods_p = 12
#
# # i = Gross remuneration for the pay period.This includes overtime earned and paid in the same pay period,
# # pension income, qualified pension income, and taxable benefits,
# # but does not include bonuses, retroactive pay increases, or other non-periodic payments
#
# # included
# overtime = 200.0
# pension_income = 3000.0
# qualified_pension_income = 2000.0
# taxable_benefits = 400.0
#
# # not included
# bonuses = 50.0
# retroactive_pay_increase = 45.0
# nonperiodic_payments = 55.0
# non_gross_remuneration = bonuses + retroactive_pay_increase + nonperiodic_payments
#
# gross_remuneration_for_period_i = (salary/annual_pay_periods_p) + overtime + pension_income + qualified_pension_income + taxable_benefits
#
# # F = Payroll deductions for the pay period for employee contributions to a registered pension plan (RPP) for current and past services, a registered retirement savings plan (RRSP), to a pooled registered pension plan (PRPP), or a retirement compensation arrangement (RCA).For tax deduction purposes, employers can deduct amounts contributed to an RPP, RRSP, PRPP, or RCA by or on behalf of an employee to determine the employee's taxable income
# rpp = 100.0 #registered pension plan
# rrsp = 150.0 #registered retirement savings plan
# prpp = 200.0 #pooled registered pension plan
# rca = 250.0 #retirement compensation arrangement
# employee_contribution_deductions_f = rpp + rrsp + prpp + rca
#
# #F2 = Alimony or maintenance payments required by a legal document dated before May 1, 1997, to be payroll-deducted authorized by a tax services office or tax centre
# alimony_before_1997_05_01 = 100.0
# maintenance_payments_before_1997_05_01 = 150.0
# required_deductions_f2 = alimony_before_1997_05_01 + maintenance_payments_before_1997_05_01
#
# #U1 = Union dues for the pay period paid to a trade union, an association of public servants, or dues required under the law of a province to a parity or advisory committee or similar body
# union_dues_u1 = 50.0
#
# #HD = Annual deduction for living in a prescribed zone, as shown on Form TD1
# prescribed_zone_hd = 60.0
#
# #F1 = Annual deductions such as child care expenses and support payments, requested by an employee or pensioner and authorized by a tax services office or tax centre
# employee_requested_deduction_f1 = 70.0
#
# #L = Additional tax deductions for the pay period requested by the employee or pensioner as shown on Form TD1
# additional_ee_deductions_l = 1200.0
#
# #T = Estimated federal and provincial or territorial tax deductions for the pay period
# estimated_total_tax = None
#
# #A = Annual taxable income = [P × (I F F2 U1 )] HD F1
# # If the result is negative, T = L.
# annual_taxable_income = (
# annual_pay_periods_p
# *(
# gross_remuneration_i
# - employee_contribution_deductions_f
# - required_deductions_f2
# - union_dues_u1
# )
# - prescribed_zone_hd
# - employee_requested_deduction_f1
# )
#
# if annual_taxable_income < 0:
# estimated_total_tax = additional_ee_deductions_l
#
# employee = self._createEmployee()
# country_id = self.env['res.country'].search([('code', '=', 'CA')])
# self.assertEqual(employee.country_id, country_id, 'The employee\'s country_id is not for Canada')
#
# contract = self._createContract(employee, wage=salary)
# self.assertEqual(contract.wage, salary, 'The contract salary of "%s" does not equal the test salary of "%s".' % (contract.wage, salary))
#
# self._log('2021 tax first payslip:')
# payslip = self._createPayslip(employee, '2021-01-01', '2021-01-31')
# self.assertEqual(payslip.contract_id, contract)
#
# #Determine the taxable income for the pay period (pay minus allowable deductions) and multiply it by the number of pay periods in the year to get an estimated annual taxable income amount. This annual taxable income amount is factor A.
# #assert(gross_remuneration_for_period_i = gross payslip_pay - non_gross_remuneration)
#
# # Calculate the basic federal tax on the estimated annual taxable income, after allowable federal non-refundable tax credits. The basic federal tax is factor T3.
# # T3 = Annual basic federal tax
# # = (R × A) K K1 K2 K3 K4
#
# # (R federal rate for income based on table
# # X A Annual Income)
# # - K Federal constant. The constant is the tax overcharged when applying the 20.5%, 26%, 29%, and 33% rates to the annual taxable income A
# # - K1 Federal non-refundable personal tax credit (the lowest federal tax rate is used to calculate this credit)
# # - K2 Federal Canada Pension Plan contributions and employment insurance premiums tax credits for the year (the lowest federal tax rate is used to calculate this credit).
# # Note: If an employee has already contributed the maximum CPP and EI, for the year with the employer, use the maximum CPP and EI deduction to determine the credit for the rest of the year. If, during the pay period in which the employee reaches the maximum, the CPP and EI, when annualized, is less than the annual maximum, use the maximum annual deduction(s) in that pay period
# # - K3 Other federal non-refundable tax credits (such as medical expenses and charitable donations) authorized by a tax services office or tax centre
# # - K4 Factor calculated using the Canada employment amount credit (the lowest federal tax rate is used to calculate this credit)
# # If the result is negative, T3 = $0.
#
# # Calculate the annual federal tax payable. This is factor T1.
#
#
# # Calculate the basic provincial or territorial tax on the estimated annual taxable income, after allowable provincial or territorial personal tax credits. The annual basic provincial or territorial tax is factor T4.
# # Calculate the annual provincial or territorial tax deduction. This is factor T2.
# # To get the estimated federal and provincial or territorial tax deductions for a pay period, add the federal and provincial or territorial tax, and divide the result by the number of pay periods. This is factor T.
#

View File

@@ -0,0 +1,90 @@
from odoo import fields
from .common import TestCAPayslip
import logging
_logger = logging.getLogger("__name__")
class TestPayslip(TestCAPayslip):
def test_basic_federal_tax(self,
salary=7000.0,
date_from='2021-01-01',
date_to='2021-01-31',
state_code=None,
**extra_contract):
annual_pay_periods_p = 12
employee = self._createEmployee()
contract = self._createCAContract(employee=employee)
self._log('2021 tax first payslip:')
payslip = self._createPayslip(employee, date_from, date_to)
# self.assertEqual(payslip.struct_type_id, )
self.assertEqual(payslip.contract_id, contract, f'Payslip contract {str(payslip.contract_id)} is not correct')
self.assertEqual(payslip.struct_id.name, 'Canada Employee Standard',
f'payroll structure {payslip.struct_id.name} is not correct')
self.assertEqual(payslip.date_from, fields.Date.from_string(date_from),
f'payslip date_from {payslip.date_from} is not correct ')
self.assertEqual(payslip.date_to, fields.Date.from_string(date_to),
f'payslip date_to {payslip.date_to} is not correct ')
self.assertEqual(payslip.employee_id.name, 'Jared',
f'payslip employee {payslip.employee_id.name} is not correct')
_logger.warning(str(payslip.read()))
for line in payslip.line_ids:
_logger.warning(f'payslip line read {str(line)}************************************')
_logger.warning(line.read())
# if line.name == 'EE: CA Federal Income Tax':
# _logger.warning(f'payslip line read {str(line)}************************************')
# _logger.warning(line.read())
# _logger.warning('payslip.contract_id************************************')
# _logger.warning(str(payslip.contract_id.read()))
# _logger.warning('payslip.contract_id.structure_type_id.read()************************************')
# _logger.warning(str(payslip.contract_id.structure_type_id.read()))
# _logger.warning('payslip.contract_id.structure_type_id.struct_ids[0].read()************************************')
# _logger.warning(str(payslip.contract_id.structure_type_id.struct_ids[0].read()))
# _logger.warning('payslip.contract_id.structure_type_id.struct_ids[0].rule_ids[0].read()************************************')
# _logger.warning(str(payslip.contract_id.structure_type_id.struct_ids[0].rule_ids[0].read()))
# _logger.warning('payslip.rule_parameter(rule_parameter_ca_fed_tax_rate)************************************')
self.assertPayrollAlmostEqual(payslip.net_wage, 5565)
# self.assertEqual(payslip.net_wage, 5565, 'total tax is off')
# schedule_pay = payslip.contract_id.schedule_pay
# additional = payslip.contract_id.us_payroll_config_value('state_income_tax_additional_withholding')
# sit_allowances = payslip.contract_id.us_payroll_config_value('ca_de4_sit_allowances')
# additional_allowances = payslip.contract_id.us_payroll_config_value('ca_de4_sit_additional_allowances')
# low_income_exemption = payslip.rule_parameter('us_ca_sit_income_exemption_rate')[schedule_pay]
# estimated_deduction = payslip.rule_parameter('us_ca_sit_estimated_deduction_rate')[schedule_pay]
# tax_table = payslip.rule_parameter('us_ca_sit_tax_rate')[filing_status].get(schedule_pay)
# standard_deduction = payslip.rule_parameter('us_ca_sit_standard_deduction_rate')[schedule_pay]
# exemption_allowances = payslip.rule_parameter('us_ca_sit_exemption_allowance_rate')[schedule_pay]
#Determine the taxable income for the pay period (pay minus allowable deductions) and multiply it by the number of pay periods in the year to get an estimated annual taxable income amount. This annual taxable income amount is factor A.
#assert(gross_remuneration_for_period_i = gross payslip_pay - non_gross_remuneration)
# Calculate the basic federal tax on the estimated annual taxable income, after allowable federal non-refundable tax credits. The basic federal tax is factor T3.
# T3 = Annual basic federal tax
# = (R × A) K K1 K2 K3 K4
# (R federal rate for income based on table
# X A Annual Income)
# - K2 Federal Canada Pension Plan contributions and employment insurance premiums tax credits for the year (the lowest federal tax rate is used to calculate this credit).
# Note: If an employee has already contributed the maximum CPP and EI, for the year with the employer, use the maximum CPP and EI deduction to determine the credit for the rest of the year. If, during the pay period in which the employee reaches the maximum, the CPP and EI, when annualized, is less than the annual maximum, use the maximum annual deduction(s) in that pay period
# - K3 Other federal non-refundable tax credits (such as medical expenses and charitable donations) authorized by a tax services office or tax centre
# - K4 Factor calculated using the Canada employment amount credit (the lowest federal tax rate is used to calculate this credit)
# - K Federal constant. The constant is the tax overcharged when applying the 20.5%, 26%, 29%, and 33% rates to the annual taxable income A
# - K1 Federal non-refundable personal tax credit (the lowest federal tax rate is used to calculate this credit)
# If the result is negative, T3 = $0.
# Calculate the annual federal tax payable. This is factor T1.
# Calculate the basic provincial or territorial tax on the estimated annual taxable income, after allowable provincial or territorial personal tax credits. The annual basic provincial or territorial tax is factor T4.
# Calculate the annual provincial or territorial tax deduction. This is factor T2.
# To get the estimated federal and provincial or territorial tax deductions for a pay period, add the federal and provincial or territorial tax, and divide the result by the number of pay periods. This is factor T.
# test_federal_with