changes
This commit is contained in:
@@ -2,133 +2,55 @@
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CANADA SALARY STRUCTURE -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<!-- Salary Structure Type (if needed) -->
|
||||
<!-- Structure Type -->
|
||||
<record id="structure_type_canada" model="hr.payroll.structure.type">
|
||||
<field name="name">Canada</field>
|
||||
<field name="name">Canadian Employee</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
</record>
|
||||
|
||||
<!-- Canada Salary Structure -->
|
||||
<!-- Salary Structure -->
|
||||
<record id="hr_payroll_structure_canada" model="hr.payroll.structure">
|
||||
<field name="name">Canada salary structure</field>
|
||||
<field name="name">Canadian Employee Salary</field>
|
||||
<field name="code">Canada</field>
|
||||
<field name="type_id" ref="structure_type_canada"/>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- SALARY RULE CATEGORY - CANADA (Deduction) -->
|
||||
<!-- This category links to CPP, EI, Federal and Provincial Tax -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_payroll_category_canada" model="hr.salary.rule.category">
|
||||
<field name="name">Deduction</field>
|
||||
<field name="code">CANADA</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
<!-- These links are set via the UI after tax rates are created -->
|
||||
<!-- cpp_deduction_id, ei_deduction_id, fed_tax_id, provincial_tax_id -->
|
||||
<!-- Salary Rule Categories -->
|
||||
<record id="hr_salary_rule_category_ca_cpp" model="hr.salary.rule.category">
|
||||
<field name="name">CPP</field>
|
||||
<field name="code">CPP</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- LINK SALARY RULES TO CANADA STRUCTURE -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<!-- Basic Salary -->
|
||||
<record id="hr_rule_basic" model="hr.salary.rule">
|
||||
<field name="name">Basic Salary</field>
|
||||
<field name="code">BASIC</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="category_id" ref="hr_payroll.BASIC"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = payslip.paid_amount</field>
|
||||
<record id="hr_salary_rule_category_ca_ei" model="hr.salary.rule.category">
|
||||
<field name="name">EI</field>
|
||||
<field name="code">EI</field>
|
||||
</record>
|
||||
|
||||
<!-- House Rent Allowance -->
|
||||
<record id="hr_rule_hra" model="hr.salary.rule">
|
||||
<field name="name">House Rent Allowance</field>
|
||||
<field name="code">HRA</field>
|
||||
<field name="sequence">5</field>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">fix</field>
|
||||
<field name="amount_fix">0.0</field>
|
||||
<record id="hr_salary_rule_category_ca_fed_tax" model="hr.salary.rule.category">
|
||||
<field name="name">Federal Tax</field>
|
||||
<field name="code">FED_TAX</field>
|
||||
</record>
|
||||
|
||||
<!-- Dearness Allowance -->
|
||||
<record id="hr_rule_da" model="hr.salary.rule">
|
||||
<field name="name">Dearness Allowance</field>
|
||||
<field name="code">DA</field>
|
||||
<field name="sequence">6</field>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">fix</field>
|
||||
<field name="amount_fix">0.0</field>
|
||||
<record id="hr_salary_rule_category_ca_prov_tax" model="hr.salary.rule.category">
|
||||
<field name="name">Provincial Tax</field>
|
||||
<field name="code">PROV_TAX</field>
|
||||
</record>
|
||||
|
||||
<!-- Travel Allowance -->
|
||||
<record id="hr_rule_travel" model="hr.salary.rule">
|
||||
<field name="name">Travel Allowance</field>
|
||||
<field name="code">Travel</field>
|
||||
<field name="sequence">7</field>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">fix</field>
|
||||
<field name="amount_fix">0.0</field>
|
||||
<record id="hr_salary_rule_category_ca_ohp" model="hr.salary.rule.category">
|
||||
<field name="name">Ontario Health Premium</field>
|
||||
<field name="code">OHP</field>
|
||||
</record>
|
||||
|
||||
<!-- Meal Allowance -->
|
||||
<record id="hr_rule_meal" model="hr.salary.rule">
|
||||
<field name="name">Meal Allowance</field>
|
||||
<field name="code">Meal</field>
|
||||
<field name="sequence">8</field>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">fix</field>
|
||||
<field name="amount_fix">0.0</field>
|
||||
<record id="hr_salary_rule_category_ca_employer" model="hr.salary.rule.category">
|
||||
<field name="name">Employer Contributions</field>
|
||||
<field name="code">EMPLOYER</field>
|
||||
</record>
|
||||
|
||||
<!-- Medical Allowance -->
|
||||
<record id="hr_rule_medical" model="hr.salary.rule">
|
||||
<field name="name">Medical Allowance</field>
|
||||
<field name="code">Medical</field>
|
||||
<field name="sequence">9</field>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">fix</field>
|
||||
<field name="amount_fix">0.0</field>
|
||||
</record>
|
||||
|
||||
<!-- Gross Salary -->
|
||||
<record id="hr_rule_gross" model="hr.salary.rule">
|
||||
<field name="name">Gross</field>
|
||||
<field name="code">GROSS</field>
|
||||
<field name="sequence">100</field>
|
||||
<field name="category_id" ref="hr_payroll.GROSS"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = categories.BASIC + categories.ALW</field>
|
||||
</record>
|
||||
|
||||
<!-- Net Salary -->
|
||||
<record id="hr_rule_net" model="hr.salary.rule">
|
||||
<field name="name">Net Salary</field>
|
||||
<field name="code">NET</field>
|
||||
<field name="sequence">200</field>
|
||||
<field name="category_id" ref="hr_payroll.NET"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result = categories.GROSS + categories.DED</field>
|
||||
<record id="hr_salary_rule_category_ca_earnings" model="hr.salary.rule.category">
|
||||
<field name="name">Earnings</field>
|
||||
<field name="code">EARN</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -2,37 +2,28 @@
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CANADIAN PAYSLIP INPUT TYPES -->
|
||||
<!-- These are used to pass additional pay inputs to salary rules -->
|
||||
<!-- ============================================================ -->
|
||||
<!-- Overtime (dollar amount) -->
|
||||
<record id="input_type_ot" model="hr.payslip.input.type">
|
||||
<field name="name">Overtime</field>
|
||||
<field name="code">OT</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Overtime Hours -->
|
||||
<record id="input_type_ot_hours" model="hr.payslip.input.type">
|
||||
<field name="name">Overtime Hours</field>
|
||||
<field name="code">OT_HOURS</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Stat Holiday Hours -->
|
||||
<record id="input_type_stat_hours" model="hr.payslip.input.type">
|
||||
<field name="name">Stat Holiday Hours</field>
|
||||
<field name="code">STAT_HOURS</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
</record>
|
||||
|
||||
<!-- Bonus Amount -->
|
||||
<!-- Bonus -->
|
||||
<record id="input_type_bonus" model="hr.payslip.input.type">
|
||||
<field name="name">Bonus</field>
|
||||
<field name="code">BONUS</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
</record>
|
||||
|
||||
<!-- Vacation Payout -->
|
||||
<record id="input_type_vacation_payout" model="hr.payslip.input.type">
|
||||
<field name="name">Vacation Payout</field>
|
||||
<field name="code">VACATION_PAYOUT</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Commission -->
|
||||
@@ -40,6 +31,39 @@
|
||||
<field name="name">Commission</field>
|
||||
<field name="code">COMMISSION</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- RRSP Deduction -->
|
||||
<record id="input_type_rrsp" model="hr.payslip.input.type">
|
||||
<field name="name">RRSP Deduction</field>
|
||||
<field name="code">RRSP</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Union Dues -->
|
||||
<record id="input_type_union" model="hr.payslip.input.type">
|
||||
<field name="name">Union Dues</field>
|
||||
<field name="code">UNION</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Vacation Pay -->
|
||||
<record id="input_type_vac_pay" model="hr.payslip.input.type">
|
||||
<field name="name">Vacation Pay</field>
|
||||
<field name="code">VAC_PAY</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Stat Holiday Hours -->
|
||||
<record id="input_type_stat_hours" model="hr.payslip.input.type">
|
||||
<field name="name">Stat Holiday Hours</field>
|
||||
<field name="code">STAT_HOURS</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Retroactive Pay -->
|
||||
@@ -47,6 +71,7 @@
|
||||
<field name="name">Retroactive Pay</field>
|
||||
<field name="code">RETRO_PAY</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Shift Premium -->
|
||||
@@ -54,6 +79,7 @@
|
||||
<field name="name">Shift Premium</field>
|
||||
<field name="code">SHIFT_PREMIUM</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="struct_ids" eval="[(4, ref('hr_payroll_structure_canada'))]"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CANADA PENSION PLAN (CPP) PARAMETERS - 2025 -->
|
||||
<!-- CANADA PENSION PLAN (CPP) PARAMETERS - 2026 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
|
||||
<record id="rule_parameter_ca_cpp_rate" model="hr.rule.parameter">
|
||||
<field name="name">Canada - CPP Rate</field>
|
||||
<field name="code">ca_cpp_rate</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">CPP employee/employer contribution rate</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_cpp_rate_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_cpp_rate_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_cpp_rate"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.0595</field>
|
||||
</record>
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">CPP basic exemption amount per year</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_cpp_exemption_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_cpp_exemption_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_cpp_exemption"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">3500.00</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">3500</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_cpp_max" model="hr.rule.parameter">
|
||||
@@ -36,14 +36,38 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Maximum CPP employee contribution per year</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_cpp_max_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_cpp_max_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_cpp_max"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">4034.10</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">4230.45</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_ympe" model="hr.rule.parameter">
|
||||
<field name="name">Canada - YMPE (CPP Ceiling 1)</field>
|
||||
<field name="code">ca_ympe</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Year's Maximum Pensionable Earnings - CPP first ceiling</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_ympe_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_ympe"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">74600</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_yampe" model="hr.rule.parameter">
|
||||
<field name="name">Canada - YAMPE (CPP Ceiling 2)</field>
|
||||
<field name="code">ca_yampe</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Year's Additional Maximum Pensionable Earnings - CPP second ceiling</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_yampe_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_yampe"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">85000</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- SECOND CANADA PENSION PLAN (CPP2) PARAMETERS - 2025 -->
|
||||
<!-- SECOND CANADA PENSION PLAN (CPP2) PARAMETERS - 2026 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_parameter_ca_cpp2_rate" model="hr.rule.parameter">
|
||||
@@ -52,9 +76,9 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">CPP2 contribution rate (on earnings above YMPE)</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_cpp2_rate_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_cpp2_rate_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_cpp2_rate"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.04</field>
|
||||
</record>
|
||||
|
||||
@@ -64,38 +88,14 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Maximum CPP2 employee contribution per year</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_cpp2_max_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_cpp2_max_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_cpp2_max"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">396.00</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_ympe" model="hr.rule.parameter">
|
||||
<field name="name">Canada - YMPE (CPP Ceiling 1)</field>
|
||||
<field name="code">ca_ympe</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Year's Maximum Pensionable Earnings - CPP first ceiling</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_ympe_2025" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_ympe"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">71300.00</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_yampe" model="hr.rule.parameter">
|
||||
<field name="name">Canada - YAMPE (CPP Ceiling 2)</field>
|
||||
<field name="code">ca_yampe</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Year's Additional Maximum Pensionable Earnings - CPP second ceiling</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_yampe_2025" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_yampe"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">81200.00</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">416.00</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- EMPLOYMENT INSURANCE (EI) PARAMETERS - 2025 -->
|
||||
<!-- EMPLOYMENT INSURANCE (EI) PARAMETERS - 2026 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_parameter_ca_ei_rate" model="hr.rule.parameter">
|
||||
@@ -104,10 +104,22 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">EI employee contribution rate</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_ei_rate_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_ei_rate_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_ei_rate"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">0.0164</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.0163</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_ei_max_insurable" model="hr.rule.parameter">
|
||||
<field name="name">Canada - EI Maximum Insurable Earnings</field>
|
||||
<field name="code">ca_ei_max_insurable</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Maximum annual insurable earnings for EI</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_ei_max_insurable_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_ei_max_insurable"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">68900</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_ei_max" model="hr.rule.parameter">
|
||||
@@ -116,10 +128,10 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Maximum EI employee premium per year</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_ei_max_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_ei_max_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_ei_max"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">1077.48</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">1123.07</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_ei_employer_mult" model="hr.rule.parameter">
|
||||
@@ -128,26 +140,150 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">EI employer contribution multiplier (1.4x employee)</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_ei_employer_mult_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_ei_employer_mult_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_ei_employer_mult"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">1.4</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- FEDERAL TAX PARAMETERS - 2025 -->
|
||||
<!-- FEDERAL TAX BRACKETS - 2026 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_parameter_ca_fed_bracket_1" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Bracket 1 Threshold</field>
|
||||
<field name="code">ca_fed_bracket_1</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Upper limit of the first federal tax bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_bracket_1_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_bracket_1"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">58523</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_rate_1" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Rate 1</field>
|
||||
<field name="code">ca_fed_rate_1</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal tax rate for the first bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_rate_1_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_rate_1"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.14</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_bracket_2" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Bracket 2 Threshold</field>
|
||||
<field name="code">ca_fed_bracket_2</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Upper limit of the second federal tax bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_bracket_2_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_bracket_2"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">117045</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_rate_2" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Rate 2</field>
|
||||
<field name="code">ca_fed_rate_2</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal tax rate for the second bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_rate_2_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_rate_2"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.205</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_bracket_3" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Bracket 3 Threshold</field>
|
||||
<field name="code">ca_fed_bracket_3</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Upper limit of the third federal tax bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_bracket_3_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_bracket_3"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">181440</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_rate_3" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Rate 3</field>
|
||||
<field name="code">ca_fed_rate_3</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal tax rate for the third bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_rate_3_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_rate_3"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.26</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_bracket_4" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Bracket 4 Threshold</field>
|
||||
<field name="code">ca_fed_bracket_4</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Upper limit of the fourth federal tax bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_bracket_4_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_bracket_4"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">258482</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_rate_4" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Rate 4</field>
|
||||
<field name="code">ca_fed_rate_4</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal tax rate for the fourth bracket</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_rate_4_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_rate_4"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.29</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_rate_5" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Rate 5</field>
|
||||
<field name="code">ca_fed_rate_5</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal tax rate for the fifth bracket (above bracket 4)</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_rate_5_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_rate_5"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.33</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- FEDERAL TAX CREDITS - 2026 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_parameter_ca_fed_bpa" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Basic Personal Amount</field>
|
||||
<field name="name">Canada - Federal Basic Personal Amount (Max)</field>
|
||||
<field name="code">ca_fed_bpa</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal basic personal amount (TD1 default)</field>
|
||||
<field name="description">Federal basic personal amount - maximum (TD1 default)</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_bpa_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_fed_bpa_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_bpa"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">16129</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">16452</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_bpa_min" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Basic Personal Amount (Min)</field>
|
||||
<field name="code">ca_fed_bpa_min</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal basic personal amount - minimum for high-income phase-out</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_bpa_min_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_bpa_min"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">14829</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_cea" model="hr.rule.parameter">
|
||||
@@ -156,54 +292,14 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Canada Employment Amount for federal tax credit</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_cea_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_fed_cea_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_cea"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">1433</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_fed_brackets" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Federal Tax Brackets</field>
|
||||
<field name="code">ca_fed_brackets</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Federal income tax brackets: [(threshold, rate), ...]</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_fed_brackets_2025" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_fed_brackets"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">[(57375, 0.15), (114750, 0.205), (177882, 0.26), (253414, 0.29), (float('inf'), 0.33)]</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- ONTARIO PROVINCIAL TAX PARAMETERS - 2025 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_parameter_ca_on_bpa" model="hr.rule.parameter">
|
||||
<field name="name">Canada Ontario - Basic Personal Amount</field>
|
||||
<field name="code">ca_on_bpa</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Ontario basic personal amount</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_on_bpa_2025" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_on_bpa"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">12399</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_on_brackets" model="hr.rule.parameter">
|
||||
<field name="name">Canada Ontario - Tax Brackets</field>
|
||||
<field name="code">ca_on_brackets</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Ontario income tax brackets: [(threshold, rate), ...]</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_on_brackets_2025" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_on_brackets"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">[(52886, 0.0505), (105775, 0.0915), (150000, 0.1116), (220000, 0.1216), (float('inf'), 0.1316)]</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- VACATION PAY RATE -->
|
||||
<!-- VACATION PAY - 2026 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_parameter_ca_vacation_rate" model="hr.rule.parameter">
|
||||
@@ -212,11 +308,39 @@
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Vacation pay percentage (Ontario minimum 4%)</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_vacation_rate_2025" model="hr.rule.parameter.value">
|
||||
<record id="rule_parameter_ca_vacation_rate_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_vacation_rate"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">0.04</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- OVERTIME PARAMETERS - 2026 -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_parameter_ca_overtime_multiplier" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Overtime Pay Multiplier</field>
|
||||
<field name="code">ca_overtime_multiplier</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Overtime pay multiplier (1.5x regular rate)</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_overtime_multiplier_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_overtime_multiplier"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">1.5</field>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ca_standard_hours_per_period" model="hr.rule.parameter">
|
||||
<field name="name">Canada - Standard Hours Per Pay Period</field>
|
||||
<field name="code">ca_standard_hours_per_period</field>
|
||||
<field name="country_id" ref="base.ca"/>
|
||||
<field name="description">Standard hours per bi-weekly pay period</field>
|
||||
</record>
|
||||
<record id="rule_parameter_ca_standard_hours_per_period_2026" model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ca_standard_hours_per_period"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">80</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,58 +1,101 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<data noupdate="0">
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- OVERTIME PAY - 1.5x Regular Rate -->
|
||||
<!-- BASIC PAY (seq 1) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_overtime_pay" model="hr.salary.rule">
|
||||
<field name="name">Overtime Pay</field>
|
||||
<field name="code">OT_PAY</field>
|
||||
<field name="sequence">101</field>
|
||||
<record id="hr_rule_basic" model="hr.salary.rule">
|
||||
<field name="name">Basic Pay</field>
|
||||
<field name="code">BASIC</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = 'OT_HOURS' in inputs</field>
|
||||
<field name="category_id" ref="hr_payroll.BASIC"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# Overtime Pay - 1.5x regular hourly rate
|
||||
OT_MULTIPLIER = 1.5
|
||||
ot_hours = inputs['OT_HOURS'].amount if 'OT_HOURS' in inputs else 0
|
||||
# Calculate hourly rate from paid amount (assuming semi-monthly ~86.67 hours)
|
||||
hourly_rate = payslip.paid_amount / 86.67 if payslip.paid_amount else 0
|
||||
result = ot_hours * hourly_rate * OT_MULTIPLIER
|
||||
result = payslip.paid_amount
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- STAT HOLIDAY PAY -->
|
||||
<!-- OVERTIME PAY (seq 3) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_overtime_pay" model="hr.salary.rule">
|
||||
<field name="name">Overtime Pay</field>
|
||||
<field name="code">OT_PAY</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = (inputs.get('OT_HOURS') and inputs['OT_HOURS'].amount > 0) or (inputs.get('OT') and inputs['OT'].amount > 0)</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
if inputs.get('OT_HOURS') and inputs['OT_HOURS'].amount > 0:
|
||||
basic = result_rules.get('BASIC', {}).get('total', 0)
|
||||
hours_per_period = payslip._rule_parameter('ca_standard_hours_per_period')
|
||||
ot_multiplier = payslip._rule_parameter('ca_overtime_multiplier')
|
||||
hourly_rate = basic / hours_per_period if hours_per_period else 0
|
||||
result = inputs['OT_HOURS'].amount * hourly_rate * ot_multiplier
|
||||
elif inputs.get('OT') and inputs['OT'].amount > 0:
|
||||
result = inputs['OT'].amount
|
||||
else:
|
||||
result = 0
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- STAT HOLIDAY PAY (seq 4) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_stat_holiday_pay" model="hr.salary.rule">
|
||||
<field name="name">Stat Holiday Pay</field>
|
||||
<field name="code">STAT_PAY</field>
|
||||
<field name="sequence">102</field>
|
||||
<field name="sequence">4</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = 'STAT_HOURS' in inputs</field>
|
||||
<field name="condition_python">result = inputs.get('STAT_HOURS') and inputs['STAT_HOURS'].amount > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# Stat Holiday Pay
|
||||
stat_hours = inputs['STAT_HOURS'].amount if 'STAT_HOURS' in inputs else 0
|
||||
hourly_rate = payslip.paid_amount / 86.67 if payslip.paid_amount else 0
|
||||
stat_hours = inputs['STAT_HOURS'].amount if inputs.get('STAT_HOURS') else 0
|
||||
basic = result_rules.get('BASIC', {}).get('total', 0)
|
||||
hours_per_period = payslip._rule_parameter('ca_standard_hours_per_period')
|
||||
hourly_rate = basic / hours_per_period if hours_per_period else 0
|
||||
result = stat_hours * hourly_rate
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- BONUS PAY -->
|
||||
<!-- VACATION PAY (seq 5) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_vacation_pay" model="hr.salary.rule">
|
||||
<field name="name">Vacation Pay</field>
|
||||
<field name="code">VAC_PAY</field>
|
||||
<field name="sequence">5</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_earnings"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.get('VAC_PAY') and inputs['VAC_PAY'].amount > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
if inputs.get('VAC_PAY') and inputs['VAC_PAY'].amount > 0:
|
||||
result = round(float(inputs['VAC_PAY'].amount), 2)
|
||||
else:
|
||||
result = 0
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- BONUS PAY (seq 6) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_bonus_pay" model="hr.salary.rule">
|
||||
<field name="name">Bonus</field>
|
||||
<field name="code">BONUS_PAY</field>
|
||||
<field name="sequence">103</field>
|
||||
<field name="sequence">6</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="condition_select">python</field>
|
||||
@@ -60,297 +103,468 @@ result = stat_hours * hourly_rate
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# Bonus Pay - direct amount from input
|
||||
result = inputs['BONUS'].amount if 'BONUS' in inputs else 0
|
||||
result = inputs['BONUS'].amount if inputs.get('BONUS') else 0
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CPP EMPLOYEE - Canada Pension Plan (Employee Portion) -->
|
||||
<!-- Uses rule parameters for rates and limits -->
|
||||
<!-- COMMISSION (seq 7) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_commission_pay" model="hr.salary.rule">
|
||||
<field name="name">Commission</field>
|
||||
<field name="code">COMMISSION</field>
|
||||
<field name="sequence">7</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.get('COMMISSION') and inputs['COMMISSION'].amount > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
result = inputs['COMMISSION'].amount if inputs.get('COMMISSION') else 0
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- RETROACTIVE PAY (seq 8) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_retro_pay" model="hr.salary.rule">
|
||||
<field name="name">Retroactive Pay</field>
|
||||
<field name="code">RETRO_PAY</field>
|
||||
<field name="sequence">8</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.get('RETRO_PAY') and inputs['RETRO_PAY'].amount > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
result = inputs['RETRO_PAY'].amount if inputs.get('RETRO_PAY') else 0
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- SHIFT PREMIUM (seq 9) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_shift_premium" model="hr.salary.rule">
|
||||
<field name="name">Shift Premium</field>
|
||||
<field name="code">SHIFT_PREMIUM</field>
|
||||
<field name="sequence">9</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.get('SHIFT_PREMIUM') and inputs['SHIFT_PREMIUM'].amount > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
result = inputs['SHIFT_PREMIUM'].amount if inputs.get('SHIFT_PREMIUM') else 0
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- GROSS (seq 10) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_rule_gross" model="hr.salary.rule">
|
||||
<field name="name">Gross</field>
|
||||
<field name="code">GROSS</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.GROSS"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
result = (
|
||||
result_rules.get('BASIC', {}).get('total', 0)
|
||||
+ result_rules.get('OT_PAY', {}).get('total', 0)
|
||||
+ result_rules.get('STAT_PAY', {}).get('total', 0)
|
||||
+ result_rules.get('VAC_PAY', {}).get('total', 0)
|
||||
+ result_rules.get('BONUS_PAY', {}).get('total', 0)
|
||||
+ result_rules.get('COMMISSION', {}).get('total', 0)
|
||||
+ result_rules.get('RETRO_PAY', {}).get('total', 0)
|
||||
+ result_rules.get('SHIFT_PREMIUM', {}).get('total', 0)
|
||||
)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- RRSP DEDUCTION (seq 15) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_rrsp_deduction" model="hr.salary.rule">
|
||||
<field name="name">RRSP Deduction</field>
|
||||
<field name="code">RRSP</field>
|
||||
<field name="sequence">15</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.get('RRSP') and inputs['RRSP'].amount > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
result = -(inputs['RRSP'].amount if inputs.get('RRSP') else 0)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- UNION DUES (seq 16) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_union_dues" model="hr.salary.rule">
|
||||
<field name="name">Union Dues</field>
|
||||
<field name="code">UNION_DUES</field>
|
||||
<field name="sequence">16</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = inputs.get('UNION') and inputs['UNION'].amount > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
result = -(inputs['UNION'].amount if inputs.get('UNION') else 0)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CPP EMPLOYEE (seq 20) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_cpp_employee" model="hr.salary.rule">
|
||||
<field name="name">CPP Employee</field>
|
||||
<field name="code">CPP_EE</field>
|
||||
<field name="sequence">150</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_cpp"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# CPP Employee Deduction - Using Rule Parameters
|
||||
CPP_RATE = payslip._rule_parameter('ca_cpp_rate')
|
||||
CPP_EXEMPTION = payslip._rule_parameter('ca_cpp_exemption')
|
||||
CPP_MAX = payslip._rule_parameter('ca_cpp_max')
|
||||
PAY_PERIODS = 24 # Semi-monthly
|
||||
|
||||
exemption_per_period = CPP_EXEMPTION / PAY_PERIODS
|
||||
gross = categories['GROSS']
|
||||
pensionable = max(0, gross - exemption_per_period)
|
||||
cpp = pensionable * CPP_RATE
|
||||
|
||||
# YTD check - get year start
|
||||
from datetime import date
|
||||
year_start = date(payslip.date_from.year, 1, 1)
|
||||
ytd = payslip._sum('CPP_EE', year_start, payslip.date_from) or 0
|
||||
remaining = CPP_MAX + ytd # ytd is negative
|
||||
|
||||
if remaining <= 0:
|
||||
gross_amount = result_rules.get('GROSS', {}).get('total', 0)
|
||||
if employee.exempt_cpp:
|
||||
result = 0
|
||||
elif cpp > remaining:
|
||||
result = -remaining
|
||||
else:
|
||||
result = -cpp
|
||||
cpp_rate = payslip._rule_parameter('ca_cpp_rate')
|
||||
cpp_exemption = payslip._rule_parameter('ca_cpp_exemption')
|
||||
cpp_max = payslip._rule_parameter('ca_cpp_max')
|
||||
ympe = payslip._rule_parameter('ca_ympe')
|
||||
period_exemption = cpp_exemption / 26
|
||||
period_max = cpp_max / 26
|
||||
period_ympe = ympe / 26
|
||||
pensionable = min(gross_amount, period_ympe)
|
||||
pensionable = max(pensionable - period_exemption, 0)
|
||||
result = -min(pensionable * cpp_rate, period_max)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CPP EMPLOYER - 1:1 Match -->
|
||||
<!-- CPP EMPLOYER (seq 21) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_cpp_employer" model="hr.salary.rule">
|
||||
<field name="name">CPP Employer</field>
|
||||
<field name="code">CPP_ER</field>
|
||||
<field name="sequence">151</field>
|
||||
<field name="sequence">21</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.COMP"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_employer"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# CPP Employer - 1:1 match with employee
|
||||
result = abs(CPP_EE) if CPP_EE else 0
|
||||
result = -result_rules.get('CPP_EE', {}).get('total', 0)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CPP2 EMPLOYEE - Second Canada Pension Plan -->
|
||||
<!-- Uses rule parameters for rates and limits -->
|
||||
<!-- CPP2 EMPLOYEE (seq 22) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_cpp2_employee" model="hr.salary.rule">
|
||||
<field name="name">CPP2 Employee</field>
|
||||
<field name="code">CPP2_EE</field>
|
||||
<field name="sequence">152</field>
|
||||
<field name="sequence">22</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_cpp"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# CPP2 (Second CPP) - Using Rule Parameters
|
||||
CPP2_RATE = payslip._rule_parameter('ca_cpp2_rate')
|
||||
YMPE = payslip._rule_parameter('ca_ympe')
|
||||
YAMPE = payslip._rule_parameter('ca_yampe')
|
||||
CPP2_MAX = payslip._rule_parameter('ca_cpp2_max')
|
||||
PAY_PERIODS = 24
|
||||
|
||||
gross = categories['GROSS']
|
||||
annual_equiv = gross * PAY_PERIODS
|
||||
|
||||
result = 0
|
||||
# CPP2 only on earnings between YMPE and YAMPE
|
||||
if annual_equiv > YMPE:
|
||||
cpp2_base = min(annual_equiv, YAMPE) - YMPE
|
||||
cpp2_per_period = (cpp2_base * CPP2_RATE) / PAY_PERIODS
|
||||
|
||||
# YTD check
|
||||
from datetime import date
|
||||
year_start = date(payslip.date_from.year, 1, 1)
|
||||
ytd = abs(payslip._sum('CPP2_EE', year_start, payslip.date_from) or 0)
|
||||
remaining = CPP2_MAX - ytd
|
||||
|
||||
if remaining > 0:
|
||||
result = -min(cpp2_per_period, remaining)
|
||||
gross_amount = result_rules.get('GROSS', {}).get('total', 0)
|
||||
if employee.exempt_cpp:
|
||||
result = 0
|
||||
else:
|
||||
ympe = payslip._rule_parameter('ca_ympe')
|
||||
cpp2_rate = payslip._rule_parameter('ca_cpp2_rate')
|
||||
yampe = payslip._rule_parameter('ca_yampe')
|
||||
cpp2_max = payslip._rule_parameter('ca_cpp2_max')
|
||||
period_ympe = ympe / 26
|
||||
period_ceiling = yampe / 26
|
||||
period_max = cpp2_max / 26
|
||||
if gross_amount > period_ympe:
|
||||
cpp2_pensionable = min(gross_amount, period_ceiling) - period_ympe
|
||||
result = -min(cpp2_pensionable * cpp2_rate, period_max)
|
||||
else:
|
||||
result = 0
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CPP2 EMPLOYER - 1:1 Match -->
|
||||
<!-- CPP2 EMPLOYER (seq 23) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_cpp2_employer" model="hr.salary.rule">
|
||||
<field name="name">CPP2 Employer</field>
|
||||
<field name="code">CPP2_ER</field>
|
||||
<field name="sequence">153</field>
|
||||
<field name="sequence">23</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.COMP"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_employer"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# CPP2 Employer - 1:1 match
|
||||
result = abs(CPP2_EE) if CPP2_EE else 0
|
||||
result = -result_rules.get('CPP2_EE', {}).get('total', 0)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- EI EMPLOYEE - Employment Insurance -->
|
||||
<!-- Uses rule parameters for rates and limits -->
|
||||
<!-- EI EMPLOYEE (seq 25) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_ei_employee" model="hr.salary.rule">
|
||||
<field name="name">EI Employee</field>
|
||||
<field name="code">EI_EE</field>
|
||||
<field name="sequence">154</field>
|
||||
<field name="sequence">25</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_ei"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# EI Employee - Using Rule Parameters
|
||||
EI_RATE = payslip._rule_parameter('ca_ei_rate')
|
||||
EI_MAX = payslip._rule_parameter('ca_ei_max')
|
||||
|
||||
gross = categories['GROSS']
|
||||
ei = gross * EI_RATE
|
||||
|
||||
# YTD check
|
||||
from datetime import date
|
||||
year_start = date(payslip.date_from.year, 1, 1)
|
||||
ytd = abs(payslip._sum('EI_EE', year_start, payslip.date_from) or 0)
|
||||
remaining = EI_MAX - ytd
|
||||
|
||||
if remaining <= 0:
|
||||
gross_amount = result_rules.get('GROSS', {}).get('total', 0)
|
||||
if employee.exempt_ei:
|
||||
result = 0
|
||||
elif ei > remaining:
|
||||
result = -remaining
|
||||
else:
|
||||
result = -ei
|
||||
ei_rate = payslip._rule_parameter('ca_ei_rate')
|
||||
ei_max_insurable = payslip._rule_parameter('ca_ei_max_insurable')
|
||||
ei_max = payslip._rule_parameter('ca_ei_max')
|
||||
period_max_insurable = ei_max_insurable / 26
|
||||
period_max_premium = ei_max / 26
|
||||
insurable = min(gross_amount, period_max_insurable)
|
||||
result = -min(insurable * ei_rate, period_max_premium)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- EI EMPLOYER - 1.4x Employee Premium -->
|
||||
<!-- EI EMPLOYER (seq 26) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_ei_employer" model="hr.salary.rule">
|
||||
<field name="name">EI Employer</field>
|
||||
<field name="code">EI_ER</field>
|
||||
<field name="sequence">155</field>
|
||||
<field name="sequence">26</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.COMP"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_employer"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# EI Employer - Using Rule Parameter for multiplier
|
||||
EI_ER_MULT = payslip._rule_parameter('ca_ei_employer_mult')
|
||||
result = abs(EI_EE) * EI_ER_MULT if EI_EE else 0
|
||||
ei_employer_mult = payslip._rule_parameter('ca_ei_employer_mult')
|
||||
result = -result_rules.get('EI_EE', {}).get('total', 0) * ei_employer_mult
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- FEDERAL INCOME TAX -->
|
||||
<!-- Uses rule parameters for brackets and credits -->
|
||||
<!-- FEDERAL INCOME TAX (seq 30) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_fed_tax" model="hr.salary.rule">
|
||||
<field name="name">Federal Income Tax</field>
|
||||
<field name="code">FED_TAX</field>
|
||||
<field name="sequence">160</field>
|
||||
<field name="sequence">30</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_fed_tax"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# Federal Income Tax - Using Rule Parameters
|
||||
PAY_PERIODS = 24
|
||||
brackets = payslip._rule_parameter('ca_fed_brackets')
|
||||
BPA = payslip._rule_parameter('ca_fed_bpa')
|
||||
CEA = payslip._rule_parameter('ca_fed_cea')
|
||||
CPP_MAX = payslip._rule_parameter('ca_cpp_max')
|
||||
EI_MAX = payslip._rule_parameter('ca_ei_max')
|
||||
if hasattr(employee, 'exempt_federal_tax') and employee.exempt_federal_tax:
|
||||
result = 0
|
||||
else:
|
||||
gross_amount = result_rules.get('GROSS', {}).get('total', 0)
|
||||
rrsp = abs(result_rules.get('RRSP', {}).get('total', 0))
|
||||
union = abs(result_rules.get('UNION_DUES', {}).get('total', 0))
|
||||
taxable_per_period = gross_amount - rrsp - union
|
||||
annual_income = taxable_per_period * 26
|
||||
|
||||
gross = categories['GROSS']
|
||||
annual = gross * PAY_PERIODS
|
||||
fed_brackets = [
|
||||
(payslip._rule_parameter('ca_fed_bracket_1'), payslip._rule_parameter('ca_fed_rate_1')),
|
||||
(payslip._rule_parameter('ca_fed_bracket_2'), payslip._rule_parameter('ca_fed_rate_2')),
|
||||
(payslip._rule_parameter('ca_fed_bracket_3'), payslip._rule_parameter('ca_fed_rate_3')),
|
||||
(payslip._rule_parameter('ca_fed_bracket_4'), payslip._rule_parameter('ca_fed_rate_4')),
|
||||
(float('inf'), payslip._rule_parameter('ca_fed_rate_5')),
|
||||
]
|
||||
|
||||
# Calculate tax using brackets
|
||||
tax = 0
|
||||
prev_threshold = 0
|
||||
for threshold, rate in brackets:
|
||||
if annual <= threshold:
|
||||
tax += (annual - prev_threshold) * rate
|
||||
break
|
||||
bpa_max = payslip._rule_parameter('ca_fed_bpa')
|
||||
bpa_min = payslip._rule_parameter('ca_fed_bpa_min')
|
||||
cea = payslip._rule_parameter('ca_fed_cea')
|
||||
phase_out_start = payslip._rule_parameter('ca_fed_bracket_3')
|
||||
phase_out_end = payslip._rule_parameter('ca_fed_bracket_4')
|
||||
|
||||
td1_override = employee.federal_td1_amount if hasattr(employee, 'federal_td1_amount') and employee.federal_td1_amount > 0 else 0
|
||||
if td1_override:
|
||||
fed_bpa = td1_override
|
||||
elif annual_income <= phase_out_start:
|
||||
fed_bpa = bpa_max
|
||||
elif annual_income >= phase_out_end:
|
||||
fed_bpa = bpa_min
|
||||
else:
|
||||
tax += (threshold - prev_threshold) * rate
|
||||
prev_threshold = threshold
|
||||
fed_bpa = bpa_max - (bpa_max - bpa_min) * (annual_income - phase_out_start) / (phase_out_end - phase_out_start)
|
||||
|
||||
# Basic personal amount credit
|
||||
tax_credit = BPA * brackets[0][1] # Lowest rate
|
||||
tax = 0
|
||||
prev_bracket = 0
|
||||
for bracket, rate in fed_brackets:
|
||||
taxable_in_bracket = min(annual_income, bracket) - prev_bracket
|
||||
if taxable_in_bracket > 0:
|
||||
tax += taxable_in_bracket * rate
|
||||
prev_bracket = bracket
|
||||
if annual_income <= bracket:
|
||||
break
|
||||
|
||||
# CPP/EI credits
|
||||
cpp_credit = min(abs(CPP_EE) * PAY_PERIODS if CPP_EE else 0, CPP_MAX) * brackets[0][1]
|
||||
ei_credit = min(abs(EI_EE) * PAY_PERIODS if EI_EE else 0, EI_MAX) * brackets[0][1]
|
||||
|
||||
# Canada Employment Amount credit
|
||||
cea_credit = CEA * brackets[0][1]
|
||||
|
||||
annual_tax = max(0, tax - tax_credit - cpp_credit - ei_credit - cea_credit)
|
||||
result = -(annual_tax / PAY_PERIODS)
|
||||
credit = fed_bpa * fed_brackets[0][1]
|
||||
cea_credit = cea * fed_brackets[0][1]
|
||||
annual_tax = max(tax - credit - cea_credit, 0)
|
||||
additional = employee.federal_additional_tax or 0
|
||||
result = -(annual_tax / 26 + additional)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- PROVINCIAL INCOME TAX (ONTARIO) -->
|
||||
<!-- Uses rule parameters for brackets and credits -->
|
||||
<!-- PROVINCIAL INCOME TAX (seq 35) -->
|
||||
<!-- All 12 provinces/territories with surtax support -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_prov_tax" model="hr.salary.rule">
|
||||
<field name="name">Provincial Income Tax</field>
|
||||
<field name="code">PROV_TAX</field>
|
||||
<field name="sequence">161</field>
|
||||
<field name="sequence">35</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.DED"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_prov_tax"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# Ontario Provincial Tax - Using Rule Parameters
|
||||
PAY_PERIODS = 24
|
||||
brackets = payslip._rule_parameter('ca_on_brackets')
|
||||
BPA_ON = payslip._rule_parameter('ca_on_bpa')
|
||||
CPP_MAX = payslip._rule_parameter('ca_cpp_max')
|
||||
EI_MAX = payslip._rule_parameter('ca_ei_max')
|
||||
gross_amount = result_rules.get('GROSS', {}).get('total', 0)
|
||||
rrsp = abs(result_rules.get('RRSP', {}).get('total', 0))
|
||||
union = abs(result_rules.get('UNION_DUES', {}).get('total', 0))
|
||||
taxable_per_period = gross_amount - rrsp - union
|
||||
annual_income = taxable_per_period * 26
|
||||
|
||||
gross = categories['GROSS']
|
||||
annual = gross * PAY_PERIODS
|
||||
province = employee.home_province or 'ON'
|
||||
|
||||
# Calculate tax using brackets
|
||||
tax = 0
|
||||
prev_threshold = 0
|
||||
for threshold, rate in brackets:
|
||||
if annual <= threshold:
|
||||
tax += (annual - prev_threshold) * rate
|
||||
break
|
||||
else:
|
||||
tax += (threshold - prev_threshold) * rate
|
||||
prev_threshold = threshold
|
||||
if province == 'QC':
|
||||
result = 0
|
||||
else:
|
||||
PROV = {
|
||||
'ON': {'b': [[53891, 0.0505], [107785, 0.0915], [150000, 0.1116], [220000, 0.1216], [0, 0.1316]], 'bpa': 12989, 'st': [[5818, 0.20], [7446, 0.36]]},
|
||||
'AB': {'b': [[61200, 0.08], [154259, 0.10], [185111, 0.12], [246813, 0.13], [370220, 0.14], [0, 0.15]], 'bpa': 21885, 'st': []},
|
||||
'BC': {'b': [[50363, 0.0506], [100728, 0.077], [115648, 0.105], [140430, 0.1229], [190405, 0.147], [265545, 0.168], [0, 0.205]], 'bpa': 12273, 'st': []},
|
||||
'SK': {'b': [[54532, 0.105], [155805, 0.125], [0, 0.145]], 'bpa': 18635, 'st': []},
|
||||
'MB': {'b': [[47000, 0.108], [100000, 0.1275], [0, 0.174]], 'bpa': 15780, 'st': []},
|
||||
'NB': {'b': [[52333, 0.094], [104666, 0.14], [193861, 0.16], [0, 0.195]], 'bpa': 12458, 'st': []},
|
||||
'NS': {'b': [[30995, 0.0879], [61991, 0.1495], [97417, 0.1667], [157124, 0.175], [0, 0.21]], 'bpa': 11481, 'st': []},
|
||||
'PE': {'b': [[33928, 0.095], [65820, 0.1347], [106890, 0.166], [142250, 0.1762], [0, 0.19]], 'bpa': 12750, 'st': []},
|
||||
'NL': {'b': [[44678, 0.087], [89354, 0.145], [159528, 0.158], [223340, 0.178], [285319, 0.198], [570638, 0.208], [1141275, 0.213], [0, 0.218]], 'bpa': 10382, 'st': []},
|
||||
'NT': {'b': [[53003, 0.059], [106009, 0.086], [172346, 0.122], [0, 0.1405]], 'bpa': 16442, 'st': []},
|
||||
'YT': {'b': [[58523, 0.064], [117045, 0.09], [181440, 0.109], [500000, 0.128], [0, 0.15]], 'bpa': 16729, 'st': []},
|
||||
'NU': {'b': [[55801, 0.04], [111602, 0.07], [181439, 0.09], [0, 0.115]], 'bpa': 17091, 'st': []},
|
||||
}
|
||||
|
||||
# Ontario Basic Personal Amount credit
|
||||
tax_credit = BPA_ON * brackets[0][1] # Lowest rate
|
||||
cfg = PROV.get(province, PROV['ON'])
|
||||
prov_brackets = []
|
||||
for br in cfg['b']:
|
||||
t = br[0] if br[0] != 0 else float('inf')
|
||||
prov_brackets.append((t, br[1]))
|
||||
|
||||
# CPP/EI credits at lowest rate
|
||||
cpp_credit = min(abs(CPP_EE) * PAY_PERIODS if CPP_EE else 0, CPP_MAX) * brackets[0][1]
|
||||
ei_credit = min(abs(EI_EE) * PAY_PERIODS if EI_EE else 0, EI_MAX) * brackets[0][1]
|
||||
tax = 0
|
||||
prev_bracket = 0
|
||||
for bracket, rate in prov_brackets:
|
||||
taxable_in_bracket = min(annual_income, bracket) - prev_bracket
|
||||
if taxable_in_bracket > 0:
|
||||
tax += taxable_in_bracket * rate
|
||||
prev_bracket = bracket
|
||||
if annual_income <= bracket:
|
||||
break
|
||||
|
||||
annual_tax = max(0, tax - tax_credit - cpp_credit - ei_credit)
|
||||
result = -(annual_tax / PAY_PERIODS)
|
||||
prov_bpa = cfg['bpa']
|
||||
if employee.provincial_claim_amount and employee.provincial_claim_amount > 0:
|
||||
prov_bpa = employee.provincial_claim_amount
|
||||
prov_credit = prov_bpa * prov_brackets[0][1]
|
||||
basic_provincial_tax = max(tax - prov_credit, 0)
|
||||
|
||||
surtax = 0
|
||||
for s in cfg['st']:
|
||||
if basic_provincial_tax > s[0]:
|
||||
surtax += (basic_provincial_tax - s[0]) * s[1]
|
||||
|
||||
total_provincial_tax = basic_provincial_tax + surtax
|
||||
result = -(total_provincial_tax / 26)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- VACATION PAY - 4% of Earnings -->
|
||||
<!-- ONTARIO HEALTH PREMIUM (seq 36) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_vacation_pay" model="hr.salary.rule">
|
||||
<field name="name">Vacation Pay</field>
|
||||
<field name="code">VAC_PAY</field>
|
||||
<field name="sequence">170</field>
|
||||
<record id="hr_ohp" model="hr.salary.rule">
|
||||
<field name="name">Ontario Health Premium</field>
|
||||
<field name="code">OHP</field>
|
||||
<field name="sequence">36</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="category_id" ref="hr_salary_rule_category_ca_ohp"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">
|
||||
province = employee.home_province or 'ON'
|
||||
result = (province == 'ON')
|
||||
</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
gross_amount = result_rules.get('GROSS', {}).get('total', 0)
|
||||
rrsp = abs(result_rules.get('RRSP', {}).get('total', 0))
|
||||
union = abs(result_rules.get('UNION_DUES', {}).get('total', 0))
|
||||
taxable_per_period = gross_amount - rrsp - union
|
||||
annual_income = taxable_per_period * 26
|
||||
|
||||
ohp = 0
|
||||
if annual_income <= 20000:
|
||||
ohp = 0
|
||||
elif annual_income <= 36000:
|
||||
ohp = min((annual_income - 20000) * 0.06, 300)
|
||||
elif annual_income <= 48000:
|
||||
ohp = 300 + min((annual_income - 36000) * 0.06, 150)
|
||||
elif annual_income <= 72000:
|
||||
ohp = 450 + min((annual_income - 48000) * 0.0025, 150)
|
||||
elif annual_income <= 200000:
|
||||
ohp = 600 + min((annual_income - 72000) * 0.0025, 300)
|
||||
else:
|
||||
ohp = 900
|
||||
|
||||
result = -(ohp / 26)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- NET PAY (seq 100) -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_rule_net" model="hr.salary.rule">
|
||||
<field name="name">Net Pay</field>
|
||||
<field name="code">NET</field>
|
||||
<field name="sequence">100</field>
|
||||
<field name="struct_id" ref="hr_payroll_structure_canada"/>
|
||||
<field name="category_id" ref="hr_payroll.NET"/>
|
||||
<field name="condition_select">none</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="appears_on_payslip">True</field>
|
||||
<field name="amount_python_compute">
|
||||
# Vacation Pay - Using Rule Parameter
|
||||
VAC_RATE = payslip._rule_parameter('ca_vacation_rate')
|
||||
result = categories['BASIC'] * VAC_RATE
|
||||
result = (
|
||||
result_rules.get('GROSS', {}).get('total', 0)
|
||||
+ result_rules.get('RRSP', {}).get('total', 0)
|
||||
+ result_rules.get('UNION_DUES', {}).get('total', 0)
|
||||
+ result_rules.get('CPP_EE', {}).get('total', 0)
|
||||
+ result_rules.get('CPP2_EE', {}).get('total', 0)
|
||||
+ result_rules.get('EI_EE', {}).get('total', 0)
|
||||
+ result_rules.get('FED_TAX', {}).get('total', 0)
|
||||
+ result_rules.get('PROV_TAX', {}).get('total', 0)
|
||||
+ result_rules.get('OHP', {}).get('total', 0)
|
||||
)
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
30
fusion_payroll/data/ir_sequence_data.xml
Normal file
30
fusion_payroll/data/ir_sequence_data.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="seq_payroll_cheque" model="ir.sequence">
|
||||
<field name="name">Payroll Cheque</field>
|
||||
<field name="code">payroll.cheque</field>
|
||||
<field name="prefix">CHQ</field>
|
||||
<field name="padding">6</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="seq_hr_roe" model="ir.sequence">
|
||||
<field name="name">Record of Employment</field>
|
||||
<field name="code">hr.roe</field>
|
||||
<field name="prefix">ROE</field>
|
||||
<field name="padding">6</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="seq_hr_tax_remittance" model="ir.sequence">
|
||||
<field name="name">Tax Remittance</field>
|
||||
<field name="code">hr.tax.remittance</field>
|
||||
<field name="prefix">REM</field>
|
||||
<field name="padding">6</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,134 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- FISCAL YEAR 2025 -->
|
||||
<!-- Note: This references account.fiscal.year - ensure it exists -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- FEDERAL TAX RATES 2025 -->
|
||||
<!-- Source: Canada Revenue Agency (CRA) 2025 Tax Brackets -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="federal_tax" model="tax.yearly.rates">
|
||||
<field name="tax_type">federal</field>
|
||||
<field name="canada_emp_amount">1433.00</field>
|
||||
<!-- fiscal_year field should reference your fiscal year record -->
|
||||
</record>
|
||||
|
||||
<!-- Federal Tax Bracket 1: 15% on first $55,867 -->
|
||||
<record id="federal_tax_line_1" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="federal_tax"/>
|
||||
<field name="tax_bracket">55867.00</field>
|
||||
<field name="tax_rate">15.00</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Federal Tax Bracket 2: 20.5% on $55,867 to $111,733 -->
|
||||
<record id="federal_tax_line_2" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="federal_tax"/>
|
||||
<field name="tax_bracket">111733.00</field>
|
||||
<field name="tax_rate">20.50</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Federal Tax Bracket 3: 26% on $111,733 to $173,205 -->
|
||||
<record id="federal_tax_line_3" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="federal_tax"/>
|
||||
<field name="tax_bracket">173205.00</field>
|
||||
<field name="tax_rate">26.00</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Federal Tax Bracket 4: 29% on $173,205 to $246,752 -->
|
||||
<record id="federal_tax_line_4" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="federal_tax"/>
|
||||
<field name="tax_bracket">246752.00</field>
|
||||
<field name="tax_rate">29.00</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Federal Tax Bracket 5: 33% on over $246,752 -->
|
||||
<record id="federal_tax_line_5" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="federal_tax"/>
|
||||
<field name="tax_bracket">246752.01</field>
|
||||
<field name="tax_rate">33.00</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- PROVINCIAL TAX RATES 2025 - ONTARIO -->
|
||||
<!-- Source: Ontario 2025 Tax Brackets -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="provincial_tax" model="tax.yearly.rates">
|
||||
<field name="tax_type">provincial</field>
|
||||
<!-- fiscal_year field should reference your fiscal year record -->
|
||||
</record>
|
||||
|
||||
<!-- Ontario Tax Bracket 1: 5.05% on first $52,886 -->
|
||||
<record id="provincial_tax_line_1" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="provincial_tax"/>
|
||||
<field name="tax_bracket">52886.00</field>
|
||||
<field name="tax_rate">5.05</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Ontario Tax Bracket 2: 9.15% on $52,886 to $105,775 -->
|
||||
<record id="provincial_tax_line_2" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="provincial_tax"/>
|
||||
<field name="tax_bracket">105775.00</field>
|
||||
<field name="tax_rate">9.15</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Ontario Tax Bracket 3: 11.16% on $105,775 to $150,000 -->
|
||||
<record id="provincial_tax_line_3" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="provincial_tax"/>
|
||||
<field name="tax_bracket">150000.00</field>
|
||||
<field name="tax_rate">11.16</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Ontario Tax Bracket 4: 12.16% on $150,000 to $220,000 -->
|
||||
<record id="provincial_tax_line_4" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="provincial_tax"/>
|
||||
<field name="tax_bracket">220000.00</field>
|
||||
<field name="tax_rate">12.16</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- Ontario Tax Bracket 5: 13.16% on over $220,000 -->
|
||||
<record id="provincial_tax_line_5" model="tax.yearly.rate.line">
|
||||
<field name="tax_id" ref="provincial_tax"/>
|
||||
<field name="tax_bracket">220000.01</field>
|
||||
<field name="tax_rate">13.16</field>
|
||||
<field name="tax_constant">0.00</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- CPP - CANADA PENSION PLAN 2025 -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="cpp_ded" model="tax.yearly.rates">
|
||||
<field name="ded_type">cpp</field>
|
||||
<field name="emp_contribution_rate">5.95</field>
|
||||
<field name="employer_contribution_rate">5.95</field>
|
||||
<field name="exemption">134.61</field>
|
||||
<field name="max_cpp">4034.10</field>
|
||||
<!-- fiscal_year field should reference your fiscal year record -->
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- EI - EMPLOYMENT INSURANCE 2025 -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="ei_ded" model="tax.yearly.rates">
|
||||
<field name="ded_type">ei</field>
|
||||
<field name="ei_rate">1.64</field>
|
||||
<field name="ei_earnings">65700.00</field>
|
||||
<field name="emp_ei_amount">1077.48</field>
|
||||
<field name="employer_ei_amount">1508.47</field>
|
||||
<!-- fiscal_year field should reference your fiscal year record -->
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user