2026-04-24 18:59:25 -04:00

202 lines
7.4 KiB
Python

from odoo import api, SUPERUSER_ID
def post_init_hook(env_or_cr, registry=None):
if registry is not None:
env = api.Environment(env_or_cr, SUPERUSER_ID, {})
else:
env = env_or_cr
PayrollSetup(env).run()
class PayrollSetup:
def __init__(self, env):
self.env = env
self.Category = env["hr.salary.rule.category"]
self.Register = env["hr.contribution.register"]
self.Rule = env["hr.salary.rule"]
self.RuleInput = env["hr.rule.input"]
self.Structure = env["hr.payroll.structure"]
self.Company = env["res.company"]
def run(self):
categories = self._ensure_categories()
for company in self.Company.search([("country_id.code", "=", "IN")]):
self._ensure_india(company, categories)
for company in self.Company.search([("country_id.code", "=", "CA")]):
self._ensure_canada(company, categories)
def _ensure_categories(self):
specs = [
("BASIC", "Basic"),
("ALW", "Allowance"),
("GROSS", "Gross"),
("DED", "Deduction"),
("NET", "Net"),
("COMP", "Company Contribution"),
]
result = {}
for code, name in specs:
category = self.Category.search([("code", "=", code)], limit=1)
if not category:
category = self.Category.create({"code": code, "name": name})
result[code] = category
return result
def _ensure_register(self, name):
register = self.Register.search([("name", "=", name)], limit=1)
if not register:
register = self.Register.create({"name": name})
return register
def _ensure_base_structure(self, company, categories):
base = self.Structure.search([("code", "=", "BASE"), ("company_id", "=", company.id)], limit=1)
values = {
"name": "Base Payroll Structure",
"code": "BASE",
"company_id": company.id,
"rule_ids": [(6, 0, [])],
}
if not base:
base = self.Structure.create(values)
else:
base.write(values)
return base
def _rule(self, company, name, code, sequence, category, python, register=False):
domain = [("code", "=", code)]
domain.append(("company_id", "=", company.id if company else False))
rule = self.Rule.search(domain, limit=1)
values = {
"name": name,
"code": code,
"sequence": sequence,
"category_id": category.id,
"company_id": company.id if company else False,
"condition_select": "none",
"amount_select": "code",
"amount_python_compute": python.strip(),
"appears_on_payslip": True,
}
if register:
values["register_id"] = register.id
if rule:
rule.write(values)
else:
rule = self.Rule.create(values)
return rule
def _input(self, rule, code, name):
item = self.RuleInput.search([("input_id", "=", rule.id), ("code", "=", code)], limit=1)
if not item:
item = self.RuleInput.create({"input_id": rule.id, "code": code, "name": name})
return item
def _structure(self, company, code, name, rules):
base = self._ensure_base_structure(company, self._ensure_categories())
structure = self.Structure.search([("code", "=", code), ("company_id", "=", company.id)], limit=1)
values = {
"name": name,
"code": code,
"company_id": company.id,
"parent_id": base.id,
"rule_ids": [(6, 0, [rule.id for rule in rules])],
}
if structure:
structure.write(values)
else:
structure = self.Structure.create(values)
return structure
def _ensure_india(self, company, categories):
pf = self._ensure_register("India Employees Provident Fund")
esi = self._ensure_register("India Employees State Insurance")
tax = self._ensure_register("India Payroll Tax Deductions")
rules = [
self._rule(company, "Basic Salary", "BASIC", 10, categories["BASIC"], """
result = contract.wage * 0.50
"""),
self._rule(company, "House Rent Allowance", "HRA", 20, categories["ALW"], """
result = contract.wage * 0.20
"""),
self._rule(company, "Special Allowance", "ALLOW", 30, categories["ALW"], """
result = contract.wage * 0.30
"""),
self._rule(company, "Gross", "GROSS", 100, categories["GROSS"], """
result = categories.BASIC + categories.ALW
"""),
self._rule(company, "Employee Provident Fund", "EPF", 150, categories["DED"], """
base = categories.BASIC
if base > 15000.0:
base = 15000.0
result = -(base * 0.12)
""", pf),
self._rule(company, "Employee State Insurance", "ESI", 160, categories["DED"], """
result = 0.0
if categories.GROSS <= 21000.0:
result = -(categories.GROSS * 0.0075)
""", esi),
self._rule(company, "Professional Tax", "IN_PT", 170, categories["DED"], """
result = -(inputs.IN_PT.amount) if inputs.IN_PT else 0.0
""", tax),
self._rule(company, "Income Tax TDS", "IN_TDS", 180, categories["DED"], """
result = -(inputs.IN_TDS.amount) if inputs.IN_TDS else 0.0
""", tax),
self._rule(company, "Net Salary", "NET", 300, categories["NET"], """
result = categories.BASIC + categories.ALW + categories.DED
"""),
]
self._input(rules[6], "IN_PT", "Professional Tax")
self._input(rules[7], "IN_TDS", "Income Tax TDS")
self._structure(company, "MCS_IN_PAYROLL", "India Monthly Payroll", rules)
def _ensure_canada(self, company, categories):
cpp = self._ensure_register("Canada Pension Plan")
ei = self._ensure_register("Employment Insurance")
tax = self._ensure_register("Canada Payroll Tax Deductions")
rules = [
self._rule(company, "Regular Salary", "BASIC", 10, categories["BASIC"], """
result = contract.wage
"""),
self._rule(company, "Gross", "GROSS", 100, categories["GROSS"], """
result = categories.BASIC + categories.ALW
"""),
self._rule(company, "CPP Employee Contribution", "CPP", 150, categories["DED"], """
monthly_exemption = 3500.0 / 12.0
monthly_max = 74600.0 / 12.0
base = categories.GROSS
if base > monthly_max:
base = monthly_max
base = base - monthly_exemption
if base < 0.0:
base = 0.0
result = -(base * 0.0595)
""", cpp),
self._rule(company, "CPP2 Employee Contribution", "CPP2", 155, categories["DED"], """
lower = 74600.0 / 12.0
upper = 85000.0 / 12.0
base = categories.GROSS
if base < lower:
base = lower
if base > upper:
base = upper
base = base - lower
result = -(base * 0.04)
""", cpp),
self._rule(company, "Employment Insurance", "EI", 160, categories["DED"], """
monthly_max = 68900.0 / 12.0
base = categories.GROSS
if base > monthly_max:
base = monthly_max
result = -(base * 0.0163)
""", ei),
self._rule(company, "Federal and Ontario Income Tax", "CA_TAX", 180, categories["DED"], """
result = -(inputs.CA_TAX.amount) if inputs.CA_TAX else 0.0
""", tax),
self._rule(company, "Net Salary", "NET", 300, categories["NET"], """
result = categories.BASIC + categories.ALW + categories.DED
"""),
]
self._input(rules[5], "CA_TAX", "Federal and Ontario Income Tax")
self._structure(company, "MCS_CA_PAYROLL", "Canada Monthly Payroll", rules)