diff --git a/.gitignore b/.gitignore
index b6e47617de1..2ab793ba540 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,3 +127,4 @@ dmypy.json
# Pyre type checker
.pyre/
+.vscode
diff --git a/l10n_in_hr_payroll_extension/__init__.py b/l10n_in_hr_payroll_extension/__init__.py
new file mode 100644
index 00000000000..0650744f6bc
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/l10n_in_hr_payroll_extension/__manifest__.py b/l10n_in_hr_payroll_extension/__manifest__.py
new file mode 100644
index 00000000000..1e4175e4ae9
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/__manifest__.py
@@ -0,0 +1,10 @@
+{
+ 'name': "Indian Payroll Extension",
+ 'depends': ['l10n_in_hr_payroll'],
+ 'installable': True,
+ 'license': 'LGPL-3',
+ 'data': [
+ 'views/res_config_settings_views.xml',
+ 'views/hr_contract_views.xml',
+ ],
+}
diff --git a/l10n_in_hr_payroll_extension/models/__init__.py b/l10n_in_hr_payroll_extension/models/__init__.py
new file mode 100644
index 00000000000..880e048d29b
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/models/__init__.py
@@ -0,0 +1,3 @@
+from . import res_company
+from . import res_config_settings
+from . import hr_contract
diff --git a/l10n_in_hr_payroll_extension/models/hr_contract.py b/l10n_in_hr_payroll_extension/models/hr_contract.py
new file mode 100644
index 00000000000..9eebf2c1419
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/models/hr_contract.py
@@ -0,0 +1,118 @@
+from odoo import api, fields, models
+
+
+class HrContract(models.Model):
+ _inherit = 'hr.contract'
+
+ l10n_in_basic_salary = fields.Monetary(string="Basic Salary", help="Basic salary calculated from the wage", compute="_compute_l10n_in_basic_salary", inverse="_inverse_l10n_in_basic_salary", currency_field="currency_id")
+ l10n_in_house_rent_allowance = fields.Monetary(string="House Rent Allowance", compute="_compute_l10n_in_house_rent_allowance", inverse="_inverse_l10n_in_house_rent_allowance", currency_field="currency_id")
+ l10n_in_standard_allowance = fields.Monetary(string="Standard Allowance", default=lambda self: self.env.company.l10n_in_standard_allowance, currency_field="currency_id")
+ l10n_in_performance_bonus = fields.Monetary(string="Performance Bonus", compute="_compute_l10n_in_performance_bonus", inverse="_inverse_l10n_in_performance_bonus", currency_field="currency_id")
+ l10n_in_leave_travel_allowance = fields.Monetary(string="Leave Travel Allowance", compute="_compute_l10n_in_leave_travel_allowance", inverse="_inverse_l10n_in_leave_travel_allowance", currency_field="currency_id")
+ l10n_in_leave_allowance = fields.Monetary(string="Leave Allowance", compute="_compute_leave_allowance", inverse="_inverse_leave_allowance", currency_field="currency_id")
+ l10n_in_leave_days = fields.Float(string="Leave Days", default=lambda self: self.env.company.l10n_in_leave_days)
+ l10n_in_gratuity = fields.Monetary(string="Gratuity", currency_field="currency_id", default=lambda self: self.env.company.l10n_in_gratuity)
+ l10n_in_supplementary_allowance = fields.Monetary(string="Supplementary Allowance", compute="_compute_l10n_in_supplementary_allowance", inverse="_inverse_l10n_in_supplementary_allowance", currency_field="currency_id", default=lambda self: self.env.company.l10n_in_supplementary_allowance)
+
+ l10n_in_basic_salary_percent = fields.Float(string="Basic Salary Percentage", help="basic salary percentage of wage", default=lambda self: self.env.company.l10n_in_basic_salary_percent)
+ l10n_in_house_rent_allowance_percent = fields.Float(string="House Rent Allowance Percentage", help="this is the percentage of basic salary", default=lambda self: self.env.company.l10n_in_house_rent_allowance_percent)
+ l10n_in_standard_allowance_percent = fields.Float(string="Standard Allowance Percentage", compute="_compute_l10n_in_standard_allowance_percent", inverse="_inverse_l10n_in_standard_allowance_percent")
+ l10n_in_performance_bonus_percent = fields.Float(string="Performance Bonus Percentage", default=lambda self: self.env.company.l10n_in_performance_bonus_percent)
+ l10n_in_leave_travel_allowance_percent = fields.Float(string="Leave Travel Allowance Percentage", default=lambda self: self.env.company.l10n_in_leave_travel_allowance_percent)
+ l10n_in_leave_allowance_per_day_percent = fields.Float(string="Leave allowance per day percentage")
+ l10n_in_leave_allowance_percent = fields.Float(string="Leave Allowance Percentage")
+ l10n_in_gratuity_percent = fields.Float(string="Gratuity Percentage", compute="_compute_l10n_in_gratuity_percent", inverse="_inverse_l10n_in_gratuity_percent")
+ l10n_in_supplementary_allowance_percent = fields.Float(string="Supplementary Allowance Percentage")
+
+ l10n_in_pf_employee_contribution = fields.Float(string="Employee Contribution", default=12)
+ l10n_in_pf_employer_contribution = fields.Float(string="Employer Contribution", default=12)
+ l10n_in_professional_tax = fields.Monetary(string="Professional Tax", default=200)
+ l10n_in_esic_employee_contribution = fields.Float(string="Employee Contribution", default=0.75)
+ l10n_in_esic_employer_contribution = fields.Float(string="Employer Contribution", default=3.25)
+ l10n_in_lwf_employee_contribution = fields.Monetary(string="Employee Contribution", currency_field="currency_id", default=6)
+ l10n_in_lwf_employer_contribution = fields.Monetary(string="Employer Contribution", currency_field="currency_id", default=12)
+ l10n_in_other_deduction = fields.Monetary(string="Other Deduction", currency_field="currency_id")
+
+ @api.depends("l10n_in_basic_salary_percent", "wage")
+ def _compute_l10n_in_basic_salary(self):
+ for contract in self:
+ contract.l10n_in_basic_salary = contract.wage * contract.l10n_in_basic_salary_percent
+
+ def _inverse_l10n_in_basic_salary(self):
+ for contract in self:
+ contract.l10n_in_basic_salary_percent = contract.l10n_in_basic_salary / contract.wage if contract.wage else 0
+
+ @api.depends("l10n_in_basic_salary", "l10n_in_house_rent_allowance_percent")
+ def _compute_l10n_in_house_rent_allowance(self):
+ for contract in self:
+ contract.l10n_in_house_rent_allowance = contract.l10n_in_basic_salary * contract.l10n_in_house_rent_allowance_percent
+
+ def _inverse_l10n_in_house_rent_allowance(self):
+ for contract in self:
+ contract.l10n_in_house_rent_allowance_percent = contract.l10n_in_house_rent_allowance / contract.l10n_in_basic_salary if contract.l10n_in_basic_salary else 0
+
+ @api.depends("l10n_in_standard_allowance", "wage")
+ def _compute_l10n_in_standard_allowance_percent(self):
+ for contract in self:
+ contract.l10n_in_standard_allowance_percent = contract.l10n_in_standard_allowance / contract.wage if contract.wage else 0
+
+ def _inverse_l10n_in_standard_allowance_percent(self):
+ for contract in self:
+ contract.l10n_in_standard_allowance = contract.l10n_in_standard_allowance_percent * contract.wage
+
+ @api.depends("l10n_in_performance_bonus_percent", "l10n_in_basic_salary")
+ def _compute_l10n_in_performance_bonus(self):
+ for contract in self:
+ contract.l10n_in_performance_bonus = contract.l10n_in_basic_salary * contract.l10n_in_performance_bonus_percent
+
+ def _inverse_l10n_in_performance_bonus(self):
+ for contract in self:
+ contract.l10n_in_performance_bonus_percent = contract.l10n_in_performance_bonus / contract.l10n_in_basic_salary if contract.l10n_in_basic_salary else 0
+
+ @api.depends("l10n_in_leave_travel_allowance_percent", "l10n_in_basic_salary")
+ def _compute_l10n_in_leave_travel_allowance(self):
+ for contract in self:
+ contract.l10n_in_leave_travel_allowance = contract.l10n_in_basic_salary * contract.l10n_in_leave_travel_allowance_percent
+
+ def _inverse_l10n_in_leave_travel_allowance(self):
+ for contract in self:
+ contract.l10n_in_leave_travel_allowance_percent = contract.l10n_in_leave_travel_allowance / contract.l10n_in_basic_salary if contract.l10n_in_basic_salary else 0
+
+ @api.depends('wage', 'l10n_in_leave_allowance_per_day_percent', 'l10n_in_leave_days')
+ def _compute_leave_allowance(self):
+ for contract in self:
+ contract.l10n_in_leave_allowance = contract.wage * contract.l10n_in_leave_allowance_per_day_percent * contract.l10n_in_leave_days
+
+ def _inverse_leave_allowance(self):
+ for contract in self:
+ if contract.l10n_in_basic_salary and contract.l10n_in_leave_days:
+ contract.l10n_in_leave_allowance_percent = contract.l10n_in_leave_allowance / contract.wage if contract.wage else 0
+ contract.l10n_in_leave_allowance_per_day_percent = contract.l10n_in_leave_allowance / (contract.wage * contract.l10n_in_leave_days) if contract.wage else 0
+
+ @api.depends("l10n_in_gratuity", "l10n_in_basic_salary")
+ def _compute_l10n_in_gratuity_percent(self):
+ for contract in self:
+ contract.l10n_in_gratuity_percent = contract.l10n_in_gratuity / contract.l10n_in_basic_salary if contract.l10n_in_basic_salary else 0
+
+ def _inverse_l10n_in_gratuity_percent(self):
+ for contract in self:
+ contract.l10n_in_gratuity = contract.l10n_in_basic_salary * contract.l10n_in_gratuity_percent
+
+ @api.depends("wage", "l10n_in_basic_salary", "l10n_in_house_rent_allowance", "l10n_in_standard_allowance", "l10n_in_performance_bonus", "l10n_in_leave_travel_allowance", "l10n_in_leave_allowance", "l10n_in_gratuity")
+ def _compute_l10n_in_supplementary_allowance(self):
+ for contract in self:
+ total_allowance = sum([
+ contract.l10n_in_basic_salary,
+ contract.l10n_in_house_rent_allowance,
+ contract.l10n_in_standard_allowance,
+ contract.l10n_in_performance_bonus,
+ contract.l10n_in_leave_travel_allowance,
+ contract.l10n_in_leave_allowance,
+ contract.l10n_in_gratuity
+ ])
+ if contract.wage:
+ contract.l10n_in_supplementary_allowance = contract.wage - total_allowance
+
+ def _inverse_l10n_in_supplementary_allowance(self):
+ for contract in self:
+ contract.l10n_in_supplementary_allowance_percent = contract.l10n_in_supplementary_allowance / contract.wage if contract.wage else 0
diff --git a/l10n_in_hr_payroll_extension/models/res_company.py b/l10n_in_hr_payroll_extension/models/res_company.py
new file mode 100644
index 00000000000..20a1185d9cc
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/models/res_company.py
@@ -0,0 +1,33 @@
+from odoo import fields, models
+
+
+class ResCompany(models.Model):
+ _inherit = 'res.company'
+
+ l10n_in_org_tax_details = fields.Boolean(string="Organisation Tax Details")
+ l10n_in_org_pan_number = fields.Char(string="PAN Number")
+ l10n_in_org_tan_number = fields.Char(string="TAN Number")
+ l10n_in_org_tds_circle = fields.Char(string="TDS Circle/AO Code")
+
+ l10n_in_pf_employer_identification = fields.Char(string="Employer Identification")
+ l10n_in_pf_employee_contribution = fields.Float(string="Employee Contribution", default=12)
+ l10n_in_pf_employer_contribution = fields.Float(string="Employer Contribution", default=12)
+
+ l10n_in_professional_tax_number = fields.Char(string="Professional Tax Number")
+
+ l10n_in_esic_ip = fields.Char(string="ESIC IP")
+ l10n_in_esic_employee_contribution = fields.Float(string="Employee Contribution", default=0.75)
+ l10n_in_esic_employer_contribution = fields.Float(string="Employer Contribution", default=3.25)
+
+ l10n_in_lwf_employee_contribution = fields.Monetary(string="Employee Contribution", currency_field="currency_id", default=6)
+ l10n_in_lwf_employer_contribution = fields.Monetary(string="Employer Contribution", currency_field="currency_id", default=12)
+
+ # Salary Structure
+ l10n_in_basic_salary_percent = fields.Float(string="Basic Salary Percentage", default=0.50)
+ l10n_in_house_rent_allowance_percent = fields.Float(string="House Rent Allowance", help="You can define 50% for metro city and 40% for non-metro city.", default=0.50)
+ l10n_in_standard_allowance = fields.Float(string="Standard Allowance", default=4167)
+ l10n_in_performance_bonus_percent = fields.Float(string="Performance Bonus", default=0.20)
+ l10n_in_leave_travel_allowance_percent = fields.Float(string="Leave Travel Allowance", default=0.20)
+ l10n_in_leave_days = fields.Float(string="Leave Days", default=1)
+ l10n_in_gratuity = fields.Float(string="Gratuity")
+ l10n_in_supplementary_allowance = fields.Float(string="Supplementary Allowance")
diff --git a/l10n_in_hr_payroll_extension/models/res_config_settings.py b/l10n_in_hr_payroll_extension/models/res_config_settings.py
new file mode 100644
index 00000000000..a90def5f03e
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/models/res_config_settings.py
@@ -0,0 +1,38 @@
+from odoo import fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+ currency_id = fields.Many2one("res.currency", default=lambda self: self.env.company.currency_id)
+
+ l10n_in_org_tax_details = fields.Boolean(related='company_id.l10n_in_org_tax_details', readonly=False)
+ l10n_in_org_pan_number = fields.Char(related='company_id.l10n_in_org_pan_number', readonly=False)
+ l10n_in_org_tan_number = fields.Char(related='company_id.l10n_in_org_tan_number', readonly=False)
+ l10n_in_org_tds_circle = fields.Char(related='company_id.l10n_in_org_tds_circle', readonly=False)
+
+ l10n_in_is_provident_fund = fields.Boolean(string="Employee's Provident Fund", default=True)
+ l10n_in_pf_employer_identification = fields.Char(related='company_id.l10n_in_pf_employer_identification', readonly=False)
+ l10n_in_pf_employee_contribution = fields.Float(related="company_id.l10n_in_pf_employee_contribution", readonly=False)
+ l10n_in_pf_employer_contribution = fields.Float(related="company_id.l10n_in_pf_employer_contribution", readonly=False)
+
+ l10n_in_is_professional_tax = fields.Boolean(string="Professional Tax", default=True)
+ l10n_in_professional_tax_number = fields.Char(related='company_id.l10n_in_professional_tax_number', readonly=False)
+
+ l10n_in_is_esic = fields.Boolean(string="Employee's State Insurance Corporation", default=True)
+ l10n_in_esic_ip = fields.Char(string="ESIC IP", related='company_id.l10n_in_esic_ip', readonly=False)
+ l10n_in_esic_employee_contribution = fields.Float(related="company_id.l10n_in_esic_employee_contribution", readonly=False)
+ l10n_in_esic_employer_contribution = fields.Float(related="company_id.l10n_in_esic_employer_contribution", readonly=False)
+
+ l10n_in_is_lwf = fields.Boolean(string="Labour Welfare Fund", default=True)
+ l10n_in_lwf_employee_contribution = fields.Monetary(related="company_id.l10n_in_lwf_employee_contribution", readonly=False)
+ l10n_in_lwf_employer_contribution = fields.Monetary(related="company_id.l10n_in_lwf_employer_contribution", readonly=False)
+
+ l10n_in_basic_salary_percent = fields.Float(related="company_id.l10n_in_basic_salary_percent", readonly=False)
+ l10n_in_house_rent_allowance_percent = fields.Float(related="company_id.l10n_in_house_rent_allowance_percent", readonly=False)
+ l10n_in_standard_allowance = fields.Float(related="company_id.l10n_in_standard_allowance", readonly=False)
+ l10n_in_performance_bonus_percent = fields.Float(related="company_id.l10n_in_performance_bonus_percent", readonly=False)
+ l10n_in_leave_travel_allowance_percent = fields.Float(related="company_id.l10n_in_leave_travel_allowance_percent", readonly=False)
+ l10n_in_leave_days = fields.Float(related="company_id.l10n_in_leave_days", readonly=False)
+ l10n_in_gratuity = fields.Float(related="company_id.l10n_in_gratuity", readonly=False)
+ l10n_in_supplementary_allowance = fields.Float(related="company_id.l10n_in_supplementary_allowance", readonly=False)
diff --git a/l10n_in_hr_payroll_extension/tests/__init__.py b/l10n_in_hr_payroll_extension/tests/__init__.py
new file mode 100644
index 00000000000..0894c057839
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_hr_contract
diff --git a/l10n_in_hr_payroll_extension/tests/test_hr_contract.py b/l10n_in_hr_payroll_extension/tests/test_hr_contract.py
new file mode 100644
index 00000000000..6c756030053
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/tests/test_hr_contract.py
@@ -0,0 +1,29 @@
+from odoo.tests.common import TransactionCase
+from odoo.tests import tagged
+
+
+@tagged('post_install', '-at_install')
+class TestHrContract(TransactionCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ cls.contract = cls.env['hr.contract'].create({
+ 'name': "Test Contract",
+ 'wage': 100000,
+ 'l10n_in_basic_salary_percent': 50,
+ 'l10n_in_house_rent_allowance_percent': 50,
+ 'l10n_in_standard_allowance': 4167,
+ 'l10n_in_performance_bonus_percent': 20,
+ 'l10n_in_leave_travel_allowance_percent': 20,
+ 'l10n_in_gratuity': 250,
+ })
+
+ def test_salary_component_amounts(self):
+ self.assertEqual(self.contract.l10n_in_basic_salary, 50000, "50% of 100000 should be 50000")
+ self.assertEqual(self.contract.l10n_in_house_rent_allowance, 25000, "50% of 50000(Basic Salary) should be 25000")
+ self.assertEqual(self.contract.l10n_in_standard_allowance_percent, 4.167, "4167 is a 4.17% of 100000(Wage)")
+ self.assertEqual(self.contract.l10n_in_performance_bonus, 10000, "20% of 50000(Basic Salary) should be 10000")
+ self.assertEqual(self.contract.l10n_in_leave_travel_allowance, 10000, "20% of 50000(Basic Salary) should be 10000")
+ self.assertEqual(self.contract.l10n_in_gratuity_percent, 0.50, "250 is a 0.50% of 50000(Basic Salary)")
+ self.assertEqual(self.contract.l10n_in_supplementary_allowance, 583, "supplementary should be remaining amount from wage")
diff --git a/l10n_in_hr_payroll_extension/views/hr_contract_views.xml b/l10n_in_hr_payroll_extension/views/hr_contract_views.xml
new file mode 100644
index 00000000000..32b877eb05e
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/views/hr_contract_views.xml
@@ -0,0 +1,138 @@
+
+
+ hr.contract.form.in.inherit
+ hr.contract
+
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/l10n_in_hr_payroll_extension/views/res_config_settings_views.xml b/l10n_in_hr_payroll_extension/views/res_config_settings_views.xml
new file mode 100644
index 00000000000..c36f6bbdb24
--- /dev/null
+++ b/l10n_in_hr_payroll_extension/views/res_config_settings_views.xml
@@ -0,0 +1,156 @@
+
+
+ res.config.settings.view.form.inherited.l10n_in_hr_payroll
+ res.config.settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+