From 992048501a3f0203aa5de21afda56e8f8c2b7c69 Mon Sep 17 00:00:00 2001 From: metatroncubeswdev Date: Sun, 30 Aug 2026 01:15:04 -0400 Subject: [PATCH] Add simple payroll structure --- addons/mcs_simple_payroll/__init__.py | 1 + addons/mcs_simple_payroll/__manifest__.py | 15 +++ addons/mcs_simple_payroll/hooks.py | 117 ++++++++++++++++++ .../views/hr_payslip_views.xml | 55 ++++++++ 4 files changed, 188 insertions(+) create mode 100644 addons/mcs_simple_payroll/__init__.py create mode 100644 addons/mcs_simple_payroll/__manifest__.py create mode 100644 addons/mcs_simple_payroll/hooks.py create mode 100644 addons/mcs_simple_payroll/views/hr_payslip_views.xml diff --git a/addons/mcs_simple_payroll/__init__.py b/addons/mcs_simple_payroll/__init__.py new file mode 100644 index 0000000..88be383 --- /dev/null +++ b/addons/mcs_simple_payroll/__init__.py @@ -0,0 +1 @@ +from . import hooks diff --git a/addons/mcs_simple_payroll/__manifest__.py b/addons/mcs_simple_payroll/__manifest__.py new file mode 100644 index 0000000..8e4d662 --- /dev/null +++ b/addons/mcs_simple_payroll/__manifest__.py @@ -0,0 +1,15 @@ +{ + "name": "Metatroncube Simple Payroll", + "version": "17.0.1.0.0", + "category": "Payroll", + "summary": "Simple monthly payroll without statutory tax, PF, or ESI deductions", + "author": "Metatroncube Software Solutions", + "license": "LGPL-3", + "depends": ["payroll"], + "data": [ + "views/hr_payslip_views.xml", + ], + "post_init_hook": "post_init_hook", + "installable": True, + "application": False, +} diff --git a/addons/mcs_simple_payroll/hooks.py b/addons/mcs_simple_payroll/hooks.py new file mode 100644 index 0000000..6b301a3 --- /dev/null +++ b/addons/mcs_simple_payroll/hooks.py @@ -0,0 +1,117 @@ +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 + SimplePayrollSetup(env).run() + + +class SimplePayrollSetup: + def __init__(self, env): + self.env = env + self.Category = env["hr.salary.rule.category"] + self.Rule = env["hr.salary.rule"] + self.Structure = env["hr.payroll.structure"] + self.Contract = env["hr.contract"] + self.Company = env["res.company"] + + def run(self): + categories = self._ensure_categories() + for company in self.Company.search([]): + structure = self._ensure_structure(company, categories) + self._assign_open_contracts(company, structure) + + def _ensure_categories(self): + specs = [ + ("BASIC", "Basic"), + ("GROSS", "Gross"), + ("NET", "Net"), + ] + 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_rule(self, company, code, name, sequence, category, python_code): + rule = self.Rule.search( + [ + ("code", "=", code), + ("company_id", "=", company.id), + ("name", "=", name), + ], + limit=1, + ) + values = { + "name": name, + "code": code, + "sequence": sequence, + "category_id": category.id, + "company_id": company.id, + "condition_select": "none", + "amount_select": "code", + "amount_python_compute": python_code.strip(), + "appears_on_payslip": True, + "active": True, + } + if rule: + rule.write(values) + else: + rule = self.Rule.create(values) + return rule + + def _ensure_structure(self, company, categories): + basic = self._ensure_rule( + company, + "MCS_BASIC", + "Basic Salary", + 10, + categories["BASIC"], + "result = contract.wage", + ) + gross = self._ensure_rule( + company, + "MCS_GROSS", + "Gross Salary", + 100, + categories["GROSS"], + "result = categories.BASIC", + ) + net = self._ensure_rule( + company, + "MCS_NET", + "Net Salary", + 200, + categories["NET"], + "result = categories.GROSS", + ) + structure = self.Structure.search( + [("code", "=", "MCS_SIMPLE_MONTHLY"), ("company_id", "=", company.id)], + limit=1, + ) + values = { + "name": "MCS Simple Monthly Payroll", + "code": "MCS_SIMPLE_MONTHLY", + "company_id": company.id, + "rule_ids": [(6, 0, [basic.id, gross.id, net.id])], + } + if structure: + structure.write(values) + else: + structure = self.Structure.create(values) + return structure + + def _assign_open_contracts(self, company, structure): + contracts = self.Contract.search( + [ + ("company_id", "=", company.id), + ("state", "=", "open"), + ("employee_id.active", "=", True), + ] + ) + contracts.write({"struct_id": structure.id, "schedule_pay": "monthly"}) diff --git a/addons/mcs_simple_payroll/views/hr_payslip_views.xml b/addons/mcs_simple_payroll/views/hr_payslip_views.xml new file mode 100644 index 0000000..d92bcc3 --- /dev/null +++ b/addons/mcs_simple_payroll/views/hr_payslip_views.xml @@ -0,0 +1,55 @@ + + +