diff --git a/l10n_ca_hr_payroll/__manifest__.py b/l10n_ca_hr_payroll/__manifest__.py index ea7309f8..dbc83a1a 100644 --- a/l10n_ca_hr_payroll/__manifest__.py +++ b/l10n_ca_hr_payroll/__manifest__.py @@ -17,6 +17,7 @@ Canada - Payroll Rules. 'data': [ 'data/base.xml', 'data/federal.xml', + 'data/ca_cpp.xml', 'security/ir.model.access.csv', # 'views/hr_contract_views.xml', # 'views/us_payroll_config_views.xml', diff --git a/l10n_ca_hr_payroll/data/ca_cpp.xml b/l10n_ca_hr_payroll/data/ca_cpp.xml index b29ac57a..c61945d2 100644 --- a/l10n_ca_hr_payroll/data/ca_cpp.xml +++ b/l10n_ca_hr_payroll/data/ca_cpp.xml @@ -36,9 +36,9 @@ EE: CA Canada Pension Plan EE_CA_CPP python - result = ca_cpp_canada_pension_plan_withholding(payslip) + result, _ = ca_cpp_canada_pension_plan_withholding(payslip, categories) code - result = ca_cpp_canada_pension_plan_withholding(payslip) + result, result_rate = ca_cpp_canada_pension_plan_withholding(payslip, categories) diff --git a/l10n_ca_hr_payroll/models/__init__.py b/l10n_ca_hr_payroll/models/__init__.py index cddb74f4..4cc120cd 100644 --- a/l10n_ca_hr_payroll/models/__init__.py +++ b/l10n_ca_hr_payroll/models/__init__.py @@ -1,3 +1,4 @@ from . import ca_payroll_config from . import hr_ca_contract from . import hr_payslip +from .federal import ca_fit diff --git a/l10n_ca_hr_payroll/models/federal/ca_cpp.py b/l10n_ca_hr_payroll/models/federal/ca_cpp.py index d7ad6d1c..2205dedf 100644 --- a/l10n_ca_hr_payroll/models/federal/ca_cpp.py +++ b/l10n_ca_hr_payroll/models/federal/ca_cpp.py @@ -1,9 +1,114 @@ from odoo import fields -from .common import TestCAPayslip +from datetime import datetime, timedelta import logging + _logger = logging.getLogger("__name__") -def ca_cpp_canada_pension_plan_withholding(payslip): - _logger.warning('ca_cpp_canada_pension_plan_withholding************************') - pass \ No newline at end of file +def ca_cpp_canada_pension_plan_withholding(payslip, categories): + #K2 = [(0.15 × ((0.0545 × ((S1 × PI) + B1 – $3,500)*, maximum $3,166.45)))) + (0.15 × ((0.0158 × ((S1 × IE) + B1), maximum $889.54))] + + payperiods_s1 = _compute_payperiod_ratio_s1(payslip) + pensionable_income_pi = _compute_pensionable_income_pi(payslip, categories) + #todo: remove + import pydevd_pycharm + pydevd_pycharm.settrace('192.168.1.27', port=6900, stdoutToServer=True, stderrToServer=True) + + return 0.0, 0.0 + +def _compute_payperiod_ratio_s1(payslip): + wage_type = payslip.wage_type + pay_periods = payslip.dict.PAY_PERIODS_IN_YEAR[wage_type] + if wage_type == 'annually': + return 1 + elif wage_type == 'semi_annually': + if payslip.date_to.month < 7: + return 1/pay_periods + else: + return 2/pay_periods + elif wage_type == 'quarterly': + quarters = { + 1:1, + 2:1, + 3:1, + 4:2, + 5:2, + 6:2, + 7:3, + 8:3, + 9:3, + 10:4, + 11:4, + 12:4, + } + quarter = quarters[payslip.date_to.month] + return quarter/pay_periods + elif wage_type == 'bi-monthly': + bi_monthly_int = { + 1:1, + 2:1, + 3:2, + 4:2, + 5:3, + 6:3, + 7:4, + 8:4, + 9:5, + 10:5, + 11:6, + 12:6, + } + bi_monthly = bi_monthly_int[payslip.date_to.month] + return bi_monthly/pay_periods + elif wage_type == 'monthly': + return payslip.date_to.month/pay_periods + elif wage_type == 'semi-monthly': + pay_period = payslip.date_to.month * 2 + if payslip.date_to.day <= 15: + return pay_period/pay_periods + else: + pay_period += 1 + return pay_period/pay_periods + elif wage_type == 'bi-weekly': + week_num = payslip.date_to.isocalendar()[1] + if week_num == 53: + return 1 + else: + return week_num/pay_periods + elif wage_type == 'weekly': + return payslip.date_to.isocalendar()[1]/pay_periods + elif wage_type == 'daily': + day_of_year = payslip.date_to.timetuple().tm_yday + return day_of_year/pay_periods + else: + raise Exception(f'Payslip does not have a valid wage_type. The wagetype presented is "{wage_type}".') + +def _compute_pensionable_income_of_slip(slip): + pensionable_income = 0.0 + for line in slip.line_ids: + if line.category_id.code == 'BASIC': + pensionable_income += line.amount + return pensionable_income + +def _compute_pensionable_income_year_to_date_piytd(payslip, categories): + employee_payslips = payslip.dict.env['hr.payslip'].search([ + ('employee_id', '=', payslip.dict.employee_id.id), + ('id', '!=', payslip.dict.id), + ]) + piytd = 0.0 + for slip in employee_payslips: + piytd += _compute_pensionable_income_of_slip(slip) + return piytd + +def _compute_pensionable_income_pi(payslip, categories): + """ + PI = Pensionable income for the pay period, or the gross income plus any taxable benefits for the pay period, plus PIYTD + """ + pensionable_income_year_to_date_piytd = _compute_pensionable_income_year_to_date_piytd(payslip, categories) + pensionable_income_for_current_payslip = _compute_pensionable_income_of_slip(payslip) + return pensionable_income_year_to_date_piytd + pensionable_income_for_current_payslip + + + + + diff --git a/l10n_ca_hr_payroll/models/hr_payslip.py b/l10n_ca_hr_payroll/models/hr_payslip.py index a6db2ef4..80fa4922 100644 --- a/l10n_ca_hr_payroll/models/hr_payslip.py +++ b/l10n_ca_hr_payroll/models/hr_payslip.py @@ -3,6 +3,7 @@ from odoo import api, fields, models from .federal.ca_fit import ca_fit_federal_income_tax_withholding +from .federal.ca_cpp import ca_cpp_canada_pension_plan_withholding class HRPayslip(models.Model): @@ -25,6 +26,7 @@ class HRPayslip(models.Model): res = super()._get_base_local_dict() res.update({ 'ca_fit_federal_income_tax_withholding': ca_fit_federal_income_tax_withholding, + 'ca_cpp_canada_pension_plan_withholding': ca_cpp_canada_pension_plan_withholding, }) return res diff --git a/l10n_ca_hr_payroll/tests/common.py b/l10n_ca_hr_payroll/tests/common.py index c5021dc8..ecf49b9e 100644 --- a/l10n_ca_hr_payroll/tests/common.py +++ b/l10n_ca_hr_payroll/tests/common.py @@ -45,12 +45,7 @@ class TestCAPayslip(common.TestPayslip): 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' diff --git a/l10n_ca_hr_payroll/tests/test_ca_federal_payslip.py b/l10n_ca_hr_payroll/tests/test_ca_federal_payslip.py index d2b7e27f..f14fa0b8 100644 --- a/l10n_ca_hr_payroll/tests/test_ca_federal_payslip.py +++ b/l10n_ca_hr_payroll/tests/test_ca_federal_payslip.py @@ -21,7 +21,7 @@ class TestPayslip(TestCAPayslip): 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.contract_id, contract, f'Payslip contract {str(payslip.contract_id)} does not equal {str(contract)}') 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), @@ -31,10 +31,10 @@ class TestPayslip(TestCAPayslip): 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()) + # _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)}************************************') @@ -49,7 +49,15 @@ class TestPayslip(TestCAPayslip): # _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) + + + # import pydevd_pycharm + # pydevd_pycharm.settrace('192.168.1.27', port=6900, stdoutToServer=True, stderrToServer=True) + # self.assertPayrollAlmostEqual(payslip.net_wage, 5565) + + + + # self.assertEqual(payslip.net_wage, 5565, 'total tax is off') # schedule_pay = payslip.contract_id.schedule_pay