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_code", "=", "IN")]): self._ensure_india(company, categories) for company in self.Company.search([("country_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) if not base: base = self.Structure.create({ "name": "Base Payroll Structure", "code": "BASE", "company_id": company.id, "rule_ids": [(6, 0, [ self._rule(company, "Base Basic Salary", "BASE_BASIC", 10, categories["BASIC"], """ result = contract.wage """).id, self._rule(company, "Base Gross", "BASE_GROSS", 100, categories["GROSS"], """ result = categories.BASIC + categories.ALW """).id, self._rule(company, "Base Net Salary", "BASE_NET", 300, categories["NET"], """ result = categories.BASIC + categories.ALW + categories.DED """).id, ])], }) 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 - categories.BASIC - categories.ALW """), 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)