Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,4 @@ dmypy.json

# Pyre type checker
.pyre/
.vscode
1 change: 1 addition & 0 deletions l10n_in_hr_payroll_extension/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
10 changes: 10 additions & 0 deletions l10n_in_hr_payroll_extension/__manifest__.py
Original file line number Diff line number Diff line change
@@ -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',
],
}
3 changes: 3 additions & 0 deletions l10n_in_hr_payroll_extension/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import res_company
from . import res_config_settings
from . import hr_contract
118 changes: 118 additions & 0 deletions l10n_in_hr_payroll_extension/models/hr_contract.py

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make code more readable so avoid using record instead of use contract

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I will take care of it from now.

Original file line number Diff line number Diff line change
@@ -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=4167, 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=1)
l10n_in_gratuity = fields.Monetary(string="Gratuity", currency_field="currency_id")
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")

l10n_in_basic_salary_percent = fields.Float(string="Basic Salary Percentage", help="basic salary percentage of wage", default=50)
l10n_in_house_rent_allowance_percent = fields.Float(string="House Rent Allowance Percentage", help="this is the percentage of basic salary", default=50)
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=20)
l10n_in_leave_travel_allowance_percent = fields.Float(string="Leave Travel Allowance Percentage", default=20)
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 record in self:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for record in self:
for contract in self:

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

record.l10n_in_basic_salary = record.wage * (record.l10n_in_basic_salary_percent / 100)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked percentage widget to avoid percentage logic on backend?

Also what if there is an hourly wage instead of a fixed wage on contract?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checked percentage widget how it works and also implemented it.
As of now if there is hourly wages then it set all fields to 0 as PO sir said that these fields are for fixed wages only. If we want, we can calculate the monthly wage from hourly wage by given working schedule hours.


def _inverse_l10n_in_basic_salary(self):
for record in self:
record.l10n_in_basic_salary_percent = (record.l10n_in_basic_salary * 100) / record.wage if record.wage else 0

@api.depends("l10n_in_basic_salary", "l10n_in_house_rent_allowance_percent")
def _compute_l10n_in_house_rent_allowance(self):
for record in self:
record.l10n_in_house_rent_allowance = record.l10n_in_basic_salary * (record.l10n_in_house_rent_allowance_percent / 100)

def _inverse_l10n_in_house_rent_allowance(self):
for record in self:
record.l10n_in_house_rent_allowance_percent = (record.l10n_in_house_rent_allowance * 100) / record.l10n_in_basic_salary if record.l10n_in_basic_salary else 0

@api.depends("l10n_in_standard_allowance", "wage")
def _compute_l10n_in_standard_allowance_percent(self):
for record in self:
record.l10n_in_standard_allowance_percent = (record.l10n_in_standard_allowance * 100) / record.wage if record.wage else 0

def _inverse_l10n_in_standard_allowance_percent(self):
for record in self:
record.l10n_in_standard_allowance = (record.l10n_in_standard_allowance_percent * record.wage) / 100

@api.depends("l10n_in_performance_bonus_percent", "l10n_in_basic_salary")
def _compute_l10n_in_performance_bonus(self):
for record in self:
record.l10n_in_performance_bonus = record.l10n_in_basic_salary * (record.l10n_in_performance_bonus_percent / 100)

def _inverse_l10n_in_performance_bonus(self):
for record in self:
record.l10n_in_performance_bonus_percent = (record.l10n_in_performance_bonus * 100) / record.l10n_in_basic_salary if record.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 record in self:
record.l10n_in_leave_travel_allowance = record.l10n_in_basic_salary * (record.l10n_in_leave_travel_allowance_percent / 100)

def _inverse_l10n_in_leave_travel_allowance(self):
for record in self:
record.l10n_in_leave_travel_allowance_percent = (record.l10n_in_leave_travel_allowance * 100) / record.l10n_in_basic_salary if record.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 record in self:
record.l10n_in_leave_allowance = (record.wage * (record.l10n_in_leave_allowance_per_day_percent / 100) * record.l10n_in_leave_days)

def _inverse_leave_allowance(self):
for record in self:
if record.l10n_in_basic_salary and record.l10n_in_leave_days:
record.l10n_in_leave_allowance_percent = record.l10n_in_leave_allowance * 100 / record.wage if record.wage else 0
record.l10n_in_leave_allowance_per_day_percent = (record.l10n_in_leave_allowance * 100 / (record.wage * record.l10n_in_leave_days)) if record.wage else 0

@api.depends("l10n_in_gratuity", "l10n_in_basic_salary")
def _compute_l10n_in_gratuity_percent(self):
for record in self:
record.l10n_in_gratuity_percent = (record.l10n_in_gratuity * 100) / record.l10n_in_basic_salary if record.l10n_in_basic_salary else 0

def _inverse_l10n_in_gratuity_percent(self):
for record in self:
record.l10n_in_gratuity = record.l10n_in_basic_salary * (record.l10n_in_gratuity_percent / 100)

@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 record in self:
total_allowance = sum([
record.l10n_in_basic_salary,
record.l10n_in_house_rent_allowance,
record.l10n_in_standard_allowance,
record.l10n_in_performance_bonus,
record.l10n_in_leave_travel_allowance,
record.l10n_in_leave_allowance,
record.l10n_in_gratuity
])
if record.wage:
record.l10n_in_supplementary_allowance = record.wage - total_allowance

def _inverse_l10n_in_supplementary_allowance(self):
for record in self:
record.l10n_in_supplementary_allowance_percent = (record.l10n_in_supplementary_allowance * 100) / record.wage if record.wage else 0
14 changes: 14 additions & 0 deletions l10n_in_hr_payroll_extension/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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_employer_identification = fields.Char(string="Employer Identification")
l10n_in_professional_tax_number = fields.Char(string="Professional Tax Number")
l10n_in_esic_ip = fields.Char(string="ESIC IP")
38 changes: 38 additions & 0 deletions l10n_in_hr_payroll_extension/models/res_config_settings.py

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you check those fields that are not related to multi-company environment each company has different value in it. so those fields should be company dependant.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they were not working with multiple company with different values.
Modified to work with multiple companies with different values.

Original file line number Diff line number Diff line change
@@ -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_employer_identification = fields.Char(related='company_id.l10n_in_employer_identification', readonly=False)
default_l10n_in_pf_employee_contribution = fields.Float(string="Employee Contribution", default_model="hr.contract", default=12)
default_l10n_in_pf_employer_contribution = fields.Float(string="Employer Contribution", default_model="hr.contract", default=12)

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(related='company_id.l10n_in_esic_ip', readonly=False)
default_l10n_in_esic_employee_contribution = fields.Float(string="Employee Contribution", default_model="hr.contract", default=0.75)
default_l10n_in_esic_employer_contribution = fields.Float(string="Employer Contribution", default_model="hr.contract", default=3.25)

l10n_in_is_lwf = fields.Boolean(string="Labour Welfare Fund", default=True)
default_l10n_in_lwf_employee_contribution = fields.Monetary(string="Employee Contribution", currency_field="currency_id", default_model="hr.contract", default=6)
default_l10n_in_lwf_employer_contribution = fields.Monetary(string="Employer Contribution", currency_field="currency_id", default_model="hr.contract", default=12)

default_l10n_in_basic_salary_percent = fields.Float(string="Basic Salary", help="You can define the % of the salary from company cost to compute the basic salary based on your wages (Including D4).", default_model="hr.contract", default=50)
default_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_model="hr.contract", default=50)
default_l10n_in_standard_allowance = fields.Float(string="Standard Allowance", default_model="hr.contract", default=4167)
default_l10n_in_performance_bonus_percent = fields.Float(string="Performance Bonus", default_model="hr.contract", default=20)
default_l10n_in_leave_travel_allowance_percent = fields.Float(string="Leave Travel Allowance", default_model="hr.contract", default=20)
default_l10n_in_leave_days = fields.Float(string="Leave Days", default_model="hr.contract", default=1)
default_l10n_in_gratuity = fields.Float(string="Gratuity", default_model="hr.contract")
default_l10n_in_supplementary_allowance = fields.Float(string="Supplementary Allowance", default_model="hr.contract")
1 change: 1 addition & 0 deletions l10n_in_hr_payroll_extension/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_hr_contract
29 changes: 29 additions & 0 deletions l10n_in_hr_payroll_extension/tests/test_hr_contract.py
Original file line number Diff line number Diff line change
@@ -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")
Loading