Employee it declaration
This commit is contained in:
parent
4db7e5ade2
commit
5460f6c207
|
|
@ -34,7 +34,7 @@
|
||||||
'version': '0.1',
|
'version': '0.1',
|
||||||
|
|
||||||
# any module necessary for this one to work correctly
|
# any module necessary for this one to work correctly
|
||||||
'depends': ['base','hr','hr_payroll','hr_employee_extended'],
|
'depends': ['base','hr','hr_payroll','hr_employee_extended','l10n_in_hr_payroll'],
|
||||||
|
|
||||||
# always loaded
|
# always loaded
|
||||||
'data': [
|
'data': [
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
'report/report_action.xml',
|
'report/report_action.xml',
|
||||||
'report/it_tax_template.xml',
|
'report/it_tax_template.xml',
|
||||||
'views/it_tax_menu_and_wizard_view.xml',
|
'views/it_tax_menu_and_wizard_view.xml',
|
||||||
|
'wizards/hr_tds_calculation.xml',
|
||||||
'wizards/children_education_costing.xml',
|
'wizards/children_education_costing.xml',
|
||||||
'wizards/employee_life_insurance.xml',
|
'wizards/employee_life_insurance.xml',
|
||||||
'wizards/nsc_declaration.xml',
|
'wizards/nsc_declaration.xml',
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,26 @@ class ITTaxStatementWizard(models.TransientModel):
|
||||||
return '60_to_80'
|
return '60_to_80'
|
||||||
return 'above_80'
|
return 'above_80'
|
||||||
|
|
||||||
|
def _get_effective_period_start(self):
|
||||||
|
self.ensure_one()
|
||||||
|
period_start = self.period_id.from_date if self.period_id else False
|
||||||
|
if not period_start:
|
||||||
|
return False
|
||||||
|
if self.emp_doj and self.period_id.to_date and self.period_id.from_date <= self.emp_doj <= self.period_id.to_date:
|
||||||
|
return max(period_start, self.emp_doj.replace(day=1))
|
||||||
|
return period_start
|
||||||
|
|
||||||
|
def _get_effective_period_lines(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if not self.period_id:
|
||||||
|
return self.env['payroll.period.line']
|
||||||
|
|
||||||
|
period_lines = self.period_id.period_line_ids.sorted('from_date')
|
||||||
|
effective_start = self._get_effective_period_start()
|
||||||
|
if not effective_start:
|
||||||
|
return period_lines
|
||||||
|
return period_lines.filtered(lambda line: line.to_date and line.to_date >= effective_start)
|
||||||
|
|
||||||
def _find_applicable_slab(self, regime, period_id, age, residence_type):
|
def _find_applicable_slab(self, regime, period_id, age, residence_type):
|
||||||
"""Find the applicable tax slab without forcing both regimes to exist."""
|
"""Find the applicable tax slab without forcing both regimes to exist."""
|
||||||
age_category = self._get_age_category(age)
|
age_category = self._get_age_category(age)
|
||||||
|
|
@ -127,6 +147,20 @@ class ITTaxStatementWizard(models.TransientModel):
|
||||||
|
|
||||||
return slab_master
|
return slab_master
|
||||||
|
|
||||||
|
@api.onchange('employee_id', 'period_id')
|
||||||
|
def _onchange_employee_id_period_id(self):
|
||||||
|
domain_by_record = {}
|
||||||
|
for rec in self:
|
||||||
|
domain = [('period_id', '=', rec.period_id.id), ('to_date', '<', fields.Date.today())] if rec.period_id else []
|
||||||
|
if rec.emp_doj:
|
||||||
|
domain.append(('to_date', '>=', rec.emp_doj.replace(day=1)))
|
||||||
|
|
||||||
|
if rec.period_line and rec.period_line not in rec._get_effective_period_lines():
|
||||||
|
rec.period_line = False
|
||||||
|
domain_by_record[rec.id] = domain
|
||||||
|
if len(self) == 1:
|
||||||
|
return {'domain': {'period_line': domain_by_record.get(self.id, [])}}
|
||||||
|
|
||||||
def _get_standard_deduction(self, regime, slab_master=False):
|
def _get_standard_deduction(self, regime, slab_master=False):
|
||||||
if slab_master:
|
if slab_master:
|
||||||
return slab_master.standard_deduction
|
return slab_master.standard_deduction
|
||||||
|
|
@ -311,7 +345,7 @@ class ITTaxStatementWizard(models.TransientModel):
|
||||||
}
|
}
|
||||||
if not rec.employee_id or not rec.contract_id or not rec.period_id or not rec.period_line:
|
if not rec.employee_id or not rec.contract_id or not rec.period_id or not rec.period_line:
|
||||||
return data
|
return data
|
||||||
period_lines = rec.period_id.period_line_ids
|
period_lines = rec._get_effective_period_lines()
|
||||||
|
|
||||||
for line in period_lines:
|
for line in period_lines:
|
||||||
components = rec._get_salary_components_for_period_line(line)
|
components = rec._get_salary_components_for_period_line(line)
|
||||||
|
|
@ -387,7 +421,7 @@ class ITTaxStatementWizard(models.TransientModel):
|
||||||
if not rec.employee_id or not rec.contract_id or not rec.period_id or not rec.period_line:
|
if not rec.employee_id or not rec.contract_id or not rec.period_id or not rec.period_line:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
for line in rec.period_id.period_line_ids:
|
for line in rec._get_effective_period_lines():
|
||||||
rule_amounts = rec._get_rule_amounts_for_period_line(line, ['PT', 'PFE'])
|
rule_amounts = rec._get_rule_amounts_for_period_line(line, ['PT', 'PFE'])
|
||||||
bucket = 'actual' if line.from_date <= rec.period_line.from_date else 'projected'
|
bucket = 'actual' if line.from_date <= rec.period_line.from_date else 'projected'
|
||||||
data['professional_tax'][bucket].append(rule_amounts['PT'])
|
data['professional_tax'][bucket].append(rule_amounts['PT'])
|
||||||
|
|
@ -728,21 +762,15 @@ class ITTaxStatementWizard(models.TransientModel):
|
||||||
def _prepare_income_tax_data(self, include_comparison=False):
|
def _prepare_income_tax_data(self, include_comparison=False):
|
||||||
"""Prepare data for the tax statement report"""
|
"""Prepare data for the tax statement report"""
|
||||||
today = date.today()
|
today = date.today()
|
||||||
fy_start = self.period_id.from_date
|
display_fy_start = self.period_id.from_date
|
||||||
fy_end = self.period_id.to_date
|
fy_end = self.period_id.to_date
|
||||||
total_months = ((fy_end.year - fy_start.year) * 12 +
|
effective_fy_start = self._get_effective_period_start() or display_fy_start
|
||||||
(fy_end.month - fy_start.month) + 1)
|
total_months = ((fy_end.year - effective_fy_start.year) * 12 +
|
||||||
|
(fy_end.month - effective_fy_start.month) + 1)
|
||||||
|
|
||||||
line_start = self.period_line.from_date
|
line_start = self.period_line.from_date
|
||||||
current_month_index = ((line_start.year - fy_start.year) * 12 +
|
current_month_index = ((line_start.year - effective_fy_start.year) * 12 +
|
||||||
(line_start.month - fy_start.month) + 1)
|
(line_start.month - effective_fy_start.month) + 1)
|
||||||
if today.month >= 4:
|
|
||||||
fy_start = date(today.year, 4, 1)
|
|
||||||
fy_end = date(today.year + 1, 3, 31)
|
|
||||||
else:
|
|
||||||
fy_start = date(today.year - 1, 4, 1)
|
|
||||||
fy_end = date(today.year, 3, 31)
|
|
||||||
|
|
||||||
values = self._get_tax_base_values(include_comparison=include_comparison)
|
values = self._get_tax_base_values(include_comparison=include_comparison)
|
||||||
salary_components_data = values['salary_components_data']
|
salary_components_data = values['salary_components_data']
|
||||||
annual_gross_salary = values['annual_gross_salary']
|
annual_gross_salary = values['annual_gross_salary']
|
||||||
|
|
@ -778,15 +806,16 @@ class ITTaxStatementWizard(models.TransientModel):
|
||||||
|
|
||||||
# Prepare data structure matching screenshot format
|
# Prepare data structure matching screenshot format
|
||||||
# Financial year (period_id)
|
# Financial year (period_id)
|
||||||
fy_start = self.period_id.from_date
|
display_fy_start = self.period_id.from_date
|
||||||
fy_end = self.period_id.to_date
|
fy_end = self.period_id.to_date
|
||||||
total_months = ((fy_end.year - fy_start.year) * 12 +
|
effective_fy_start = self._get_effective_period_start() or display_fy_start
|
||||||
(fy_end.month - fy_start.month) + 1)
|
total_months = ((fy_end.year - effective_fy_start.year) * 12 +
|
||||||
|
(fy_end.month - effective_fy_start.month) + 1)
|
||||||
|
|
||||||
# Current month (period_line)
|
# Current month (period_line)
|
||||||
line_start = self.period_line.from_date
|
line_start = self.period_line.from_date
|
||||||
current_month_index = ((line_start.year - fy_start.year) * 12 +
|
current_month_index = ((line_start.year - effective_fy_start.year) * 12 +
|
||||||
(line_start.month - fy_start.month) + 1)
|
(line_start.month - effective_fy_start.month) + 1)
|
||||||
tax_result['roundoff_taxable_income'] = float(round(tax_result["taxable_income"] / 10) * 10)
|
tax_result['roundoff_taxable_income'] = float(round(tax_result["taxable_income"] / 10) * 10)
|
||||||
birthday = self.employee_id.birthday
|
birthday = self.employee_id.birthday
|
||||||
if birthday:
|
if birthday:
|
||||||
|
|
@ -806,7 +835,7 @@ class ITTaxStatementWizard(models.TransientModel):
|
||||||
'total': total,
|
'total': total,
|
||||||
})
|
})
|
||||||
data = {
|
data = {
|
||||||
'financial_year': f"{fy_start.year}-{fy_end.year}",
|
'financial_year': f"{display_fy_start.year}-{fy_end.year}",
|
||||||
'assessment_year': fy_end.year + 1,
|
'assessment_year': fy_end.year + 1,
|
||||||
'report_time': today.strftime('%d-%m-%Y %H:%M'),
|
'report_time': today.strftime('%d-%m-%Y %H:%M'),
|
||||||
'user': 'ESS',
|
'user': 'ESS',
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,17 @@ class IncomeTaxSlabMaster(models.Model):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
name = fields.Char(string="Slab Name", required=True)
|
name = fields.Char(string="Slab Name", required=True, copy=False, default="AY")
|
||||||
period_id = fields.Many2one('payroll.period')
|
period_id = fields.Many2one('payroll.period', copy=False)
|
||||||
regime = fields.Selection([
|
regime = fields.Selection([
|
||||||
('old', 'Old Tax Regime'),
|
('old', 'Old Tax Regime'),
|
||||||
('new', 'New Tax Regime')
|
('new', 'New Tax Regime')
|
||||||
], required=True)
|
], required=True, default='old')
|
||||||
age_category = fields.Selection([
|
age_category = fields.Selection([
|
||||||
('below_60', 'Below 60 Years'),
|
('below_60', 'Below 60 Years'),
|
||||||
('60_to_80', '60-80 Years'),
|
('60_to_80', '60-80 Years'),
|
||||||
('above_80', 'Above 80 Years')
|
('above_80', 'Above 80 Years')
|
||||||
], required=True)
|
], required=True, default='below_60')
|
||||||
residence_type = fields.Selection([
|
residence_type = fields.Selection([
|
||||||
('resident', 'Resident'),
|
('resident', 'Resident'),
|
||||||
('non_resident', 'Non Resident'),
|
('non_resident', 'Non Resident'),
|
||||||
|
|
@ -34,6 +34,37 @@ class IncomeTaxSlabMaster(models.Model):
|
||||||
rules = fields.One2many('it.slab.master.rules','slab_id', string="Slab Rules")
|
rules = fields.One2many('it.slab.master.rules','slab_id', string="Slab Rules")
|
||||||
surcharges = fields.One2many('it.sur.charge.rules','slab_id', string="Surcharges Rules")
|
surcharges = fields.One2many('it.sur.charge.rules','slab_id', string="Surcharges Rules")
|
||||||
|
|
||||||
|
def copy(self, default=None):
|
||||||
|
"""Override copy to duplicate slab rules and surcharge rules"""
|
||||||
|
if default is None:
|
||||||
|
default = {}
|
||||||
|
|
||||||
|
# Check if we should duplicate from context
|
||||||
|
duplicate_rules = self.env.context.get('duplicate_slab_rules', True)
|
||||||
|
|
||||||
|
default.update({
|
||||||
|
'name': _("%s (copy)") % (self.name or 'Slab'),
|
||||||
|
'rules': [],
|
||||||
|
'surcharges': [],
|
||||||
|
})
|
||||||
|
|
||||||
|
new_slab = super(IncomeTaxSlabMaster, self).copy(default)
|
||||||
|
|
||||||
|
# Only duplicate if flag is True
|
||||||
|
if duplicate_rules:
|
||||||
|
if self.rules:
|
||||||
|
for rule in self.rules:
|
||||||
|
rule.copy({
|
||||||
|
'slab_id': new_slab.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.surcharges:
|
||||||
|
for surcharge in self.surcharges:
|
||||||
|
surcharge.copy({
|
||||||
|
'slab_id': new_slab.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return new_slab
|
||||||
class IncomeTaxSlabMasterRules(models.Model):
|
class IncomeTaxSlabMasterRules(models.Model):
|
||||||
_name = 'it.slab.master.rules'
|
_name = 'it.slab.master.rules'
|
||||||
_description = 'Income Tax slab rules'
|
_description = 'Income Tax slab rules'
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
<field name="period_id" options="{'no_edit': True, 'no_create': True, 'no_open': True}"/>
|
<field name="period_id" options="{'no_edit': True, 'no_create': True, 'no_open': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="period_line" force_save="1" domain="[('period_id', '=', period_id),('to_date','<',datetime.datetime.now())]"/>
|
<field name="period_line" force_save="1" domain="[('period_id', '=', period_id),('to_date','<',(context_today() + datetime.timedelta(days=30)).strftime('%Y-%m-%d')),('to_date','>',emp_doj)]"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
<page string="Surcharge Rules">
|
<page string="Surcharge Rules">
|
||||||
<field name="rules">
|
<field name="surcharges">
|
||||||
<list editable="bottom">
|
<list editable="bottom">
|
||||||
<field name="min_income"/>
|
<field name="min_income"/>
|
||||||
<field name="max_income"/>
|
<field name="max_income"/>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from . import hr_tds_calculation
|
||||||
from . import children_education_costing
|
from . import children_education_costing
|
||||||
from . import employee_life_insurance
|
from . import employee_life_insurance
|
||||||
from . import nsc_declaration
|
from . import nsc_declaration
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,308 @@
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
class HrTdsCalculation(models.TransientModel):
|
||||||
|
_inherit = 'l10n.in.tds.computation.wizard'
|
||||||
|
|
||||||
|
def default_get(self, fields):
|
||||||
|
res = super().default_get(fields)
|
||||||
|
# Remove the standard_deduction if it was set by parent
|
||||||
|
if 'standard_deduction' in res:
|
||||||
|
del res['standard_deduction']
|
||||||
|
return res
|
||||||
|
|
||||||
|
tax_regime = fields.Selection([
|
||||||
|
('new', 'New Regime'),
|
||||||
|
('old', 'Old Regime')
|
||||||
|
], string="Tax Regime", required=True, default='new')
|
||||||
|
|
||||||
|
standard_deduction = fields.Float(
|
||||||
|
string="Standard Deduction",
|
||||||
|
compute="_compute_standard_deduction",
|
||||||
|
readonly=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _get_regular_pay_structure(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.payslip_id and self.payslip_id.struct_id:
|
||||||
|
return self.payslip_id.struct_id
|
||||||
|
if self.contract_id.structure_type_id.default_struct_id:
|
||||||
|
return self.contract_id.structure_type_id.default_struct_id
|
||||||
|
return self.env['hr.payroll.structure']
|
||||||
|
|
||||||
|
def _get_annualization_factor(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.contract_id:
|
||||||
|
return self.contract_id._get_salary_costs_factor()
|
||||||
|
return 12
|
||||||
|
|
||||||
|
def _get_monthly_gross_from_structure(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if not self.contract_id or not self.contract_id.employee_id:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
structure = self._get_regular_pay_structure()
|
||||||
|
payslip = self.payslip_id
|
||||||
|
dummy_payslip = self.env['hr.payslip']
|
||||||
|
|
||||||
|
if not payslip:
|
||||||
|
today = fields.Date.today()
|
||||||
|
period_start = today.replace(day=1)
|
||||||
|
period_end = fields.Date.end_of(period_start, 'month')
|
||||||
|
dummy_payslip = self.env['hr.payslip'].sudo().create({
|
||||||
|
'name': 'TDS Gross Preview',
|
||||||
|
'employee_id': self.contract_id.employee_id.id,
|
||||||
|
'contract_id': self.contract_id.id,
|
||||||
|
'struct_id': structure.id or self.contract_id.structure_type_id.default_struct_id.id,
|
||||||
|
'date_from': period_start,
|
||||||
|
'date_to': period_end,
|
||||||
|
})
|
||||||
|
dummy_payslip.sudo().compute_sheet()
|
||||||
|
payslip = dummy_payslip
|
||||||
|
|
||||||
|
try:
|
||||||
|
gross_lines = payslip.line_ids.filtered(lambda line: line.salary_rule_id.code == 'GROSS')
|
||||||
|
if gross_lines:
|
||||||
|
return sum(gross_lines.mapped('total'))
|
||||||
|
return self.contract_id.wage or 0.0
|
||||||
|
finally:
|
||||||
|
if dummy_payslip:
|
||||||
|
dummy_payslip.sudo().action_payslip_cancel()
|
||||||
|
dummy_payslip.sudo().unlink()
|
||||||
|
|
||||||
|
def _get_current_payroll_period(self):
|
||||||
|
today = fields.Date.today()
|
||||||
|
return self.env['payroll.period'].search([
|
||||||
|
('from_date', '<=', today),
|
||||||
|
('to_date', '>=', today),
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
|
def _get_age_category(self, age):
|
||||||
|
if age < 60:
|
||||||
|
return 'below_60'
|
||||||
|
if age < 80:
|
||||||
|
return '60_to_80'
|
||||||
|
return 'above_80'
|
||||||
|
|
||||||
|
def _get_employee_age(self):
|
||||||
|
self.ensure_one()
|
||||||
|
employee = self.contract_id.employee_id
|
||||||
|
if employee and employee.birthday:
|
||||||
|
return relativedelta(date.today(), employee.birthday).years
|
||||||
|
return 30
|
||||||
|
|
||||||
|
def _find_applicable_slab(self, regime):
|
||||||
|
self.ensure_one()
|
||||||
|
period = self._get_current_payroll_period()
|
||||||
|
if not period:
|
||||||
|
return self.env['it.slab.master']
|
||||||
|
|
||||||
|
age_category = self._get_age_category(self._get_employee_age())
|
||||||
|
return self.env['it.slab.master'].search([
|
||||||
|
('period_id', '=', period.id),
|
||||||
|
('regime', '=', regime),
|
||||||
|
('age_category', '=', age_category),
|
||||||
|
'|',
|
||||||
|
('residence_type', '=', 'resident'),
|
||||||
|
('residence_type', '=', 'both'),
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
|
def _get_standard_deduction_amount(self, regime, slab_master=False):
|
||||||
|
if slab_master and slab_master.standard_deduction:
|
||||||
|
return slab_master.standard_deduction
|
||||||
|
return 75000.0 if regime == 'new' else 50000.0
|
||||||
|
|
||||||
|
def _compute_tax_using_slab(self, taxable_income, slab_master):
|
||||||
|
rules = slab_master.rules.sorted(lambda r: (r.sequence, r.max_income or float('inf')))
|
||||||
|
applicable_rule = False
|
||||||
|
previous_rules = self.env['it.slab.master.rules']
|
||||||
|
|
||||||
|
for rule in rules:
|
||||||
|
min_income = rule.min_income or 0.0
|
||||||
|
max_income = rule.max_income or float('inf')
|
||||||
|
if min_income < taxable_income <= max_income:
|
||||||
|
applicable_rule = rule
|
||||||
|
previous_rules = rules.filtered(
|
||||||
|
lambda r: r.sequence < rule.sequence or
|
||||||
|
(r.sequence == rule.sequence and (r.max_income or float('inf')) < max_income)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not applicable_rule:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
previous_max_income = previous_rules[-1].max_income if previous_rules else 0.0
|
||||||
|
current_tax = (taxable_income - previous_max_income) * (applicable_rule.tax_rate / 100.0)
|
||||||
|
previous_fixed_amounts = sum(previous_rules.mapped('fixed_amount'))
|
||||||
|
return current_tax + previous_fixed_amounts
|
||||||
|
|
||||||
|
def _compute_rebate(self, regime, taxable_income, slab_tax):
|
||||||
|
if regime == 'old':
|
||||||
|
if taxable_income <= 500000.0:
|
||||||
|
return min(12500.0, slab_tax)
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
if taxable_income >= 1200000.0:
|
||||||
|
return max(0.0, slab_tax - (taxable_income - 1200000.0))
|
||||||
|
return min(60000.0, slab_tax)
|
||||||
|
|
||||||
|
def _compute_surcharge(self, slab_master, taxable_income, tax_after_rebate):
|
||||||
|
surcharge_rate = 0.0
|
||||||
|
for rule in slab_master.surcharges.sorted('min_income'):
|
||||||
|
max_income = rule.max_income or float('inf')
|
||||||
|
if rule.min_income < taxable_income <= max_income:
|
||||||
|
surcharge_rate = rule.surcharge_rate
|
||||||
|
return tax_after_rebate * (surcharge_rate / 100.0)
|
||||||
|
|
||||||
|
def _compute_cess(self, slab_master, taxable_income, tax_with_surcharge):
|
||||||
|
cess_rate = 4.0
|
||||||
|
for rule in slab_master.rules.sorted('min_income'):
|
||||||
|
max_income = rule.max_income or float('inf')
|
||||||
|
if rule.min_income < taxable_income <= max_income:
|
||||||
|
cess_rate = rule.cess_rate or 4.0
|
||||||
|
break
|
||||||
|
return tax_with_surcharge * (cess_rate / 100.0)
|
||||||
|
|
||||||
|
def _compute_tax_from_custom_slab(self, total_income, regime, slab_master):
|
||||||
|
taxable_income = max(total_income - self._get_standard_deduction_amount(regime, slab_master), 0.0)
|
||||||
|
slab_tax = self._compute_tax_using_slab(taxable_income, slab_master)
|
||||||
|
rebate = self._compute_rebate(regime, taxable_income, slab_tax)
|
||||||
|
total_tax_on_income = max(0.0, slab_tax - rebate)
|
||||||
|
surcharge = self._compute_surcharge(slab_master, taxable_income, total_tax_on_income)
|
||||||
|
tax_with_surcharge = total_tax_on_income + surcharge
|
||||||
|
cess = self._compute_cess(slab_master, taxable_income, tax_with_surcharge) if tax_with_surcharge else 0.0
|
||||||
|
return {
|
||||||
|
'taxable_income': taxable_income,
|
||||||
|
'tax_on_taxable_income': slab_tax,
|
||||||
|
'rebate': rebate,
|
||||||
|
'total_tax_on_income': total_tax_on_income,
|
||||||
|
'surcharge': surcharge,
|
||||||
|
'cess': cess,
|
||||||
|
'total_tax': tax_with_surcharge + cess,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _compute_tax_from_rule_parameters(self, total_income, standard_deduction):
|
||||||
|
rule_parameter = self.env['hr.rule.parameter']
|
||||||
|
tax_slabs = rule_parameter._get_parameter_from_code('l10n_in_tds_rate_chart')
|
||||||
|
tax_slabs_for_surcharge = rule_parameter._get_parameter_from_code('l10n_in_surcharge_rate')
|
||||||
|
min_income_for_surcharge = rule_parameter._get_parameter_from_code('l10n_in_min_income_surcharge')
|
||||||
|
min_income_for_rebate = rule_parameter._get_parameter_from_code('l10n_in_min_income_tax_rebate')
|
||||||
|
|
||||||
|
taxable_income = max(total_income - standard_deduction, 0.0)
|
||||||
|
tax = 0.0
|
||||||
|
for rate, (lower, upper) in tax_slabs:
|
||||||
|
if taxable_income <= lower:
|
||||||
|
break
|
||||||
|
taxable_amount = min(taxable_income, float(upper)) - lower
|
||||||
|
tax += round(taxable_amount * rate)
|
||||||
|
|
||||||
|
if taxable_income >= min_income_for_rebate:
|
||||||
|
marginal_income = taxable_income - min_income_for_rebate
|
||||||
|
rebate = max(tax - marginal_income, 0.0)
|
||||||
|
else:
|
||||||
|
rebate = tax
|
||||||
|
total_tax_on_income = tax - rebate
|
||||||
|
|
||||||
|
surcharge = 0.0
|
||||||
|
if taxable_income > min_income_for_surcharge:
|
||||||
|
for rate, amount in tax_slabs_for_surcharge:
|
||||||
|
if taxable_income <= float(amount[1]):
|
||||||
|
surcharge = total_tax_on_income * rate
|
||||||
|
break
|
||||||
|
|
||||||
|
max_tax_slabs = rule_parameter._get_parameter_from_code('l10n_in_max_surcharge_tax_rate')
|
||||||
|
max_taxable_income, max_tax, max_surcharge = 0.0, 0.0, 0.0
|
||||||
|
for income, tax_amount, surcharge_rate in max_tax_slabs:
|
||||||
|
if taxable_income <= income:
|
||||||
|
break
|
||||||
|
max_taxable_income, max_tax, max_surcharge = income, tax_amount, surcharge_rate
|
||||||
|
|
||||||
|
excess_income = taxable_income - max_taxable_income
|
||||||
|
max_tax_with_surcharge = max_tax + max_surcharge
|
||||||
|
total_tax_with_surcharge = total_tax_on_income + surcharge
|
||||||
|
excess_tax = total_tax_with_surcharge - max_tax_with_surcharge
|
||||||
|
if excess_tax - excess_income > 0:
|
||||||
|
surcharge = max_tax_with_surcharge + taxable_income - max_taxable_income - total_tax_on_income
|
||||||
|
|
||||||
|
cess = (total_tax_on_income + surcharge) * 0.04
|
||||||
|
return {
|
||||||
|
'taxable_income': taxable_income,
|
||||||
|
'tax_on_taxable_income': tax,
|
||||||
|
'rebate': rebate,
|
||||||
|
'total_tax_on_income': total_tax_on_income,
|
||||||
|
'surcharge': surcharge,
|
||||||
|
'cess': cess,
|
||||||
|
'total_tax': total_tax_on_income + surcharge + cess,
|
||||||
|
}
|
||||||
|
|
||||||
|
@api.depends('contract_id', 'payslip_id', 'contract_id.structure_type_id', 'contract_id.wage')
|
||||||
|
def _compute_total_income(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.contract_id:
|
||||||
|
record.total_income = 0.0
|
||||||
|
continue
|
||||||
|
monthly_gross = record._get_monthly_gross_from_structure()
|
||||||
|
|
||||||
|
rule_parameter = self.env['hr.rule.parameter']
|
||||||
|
pf = 0
|
||||||
|
if record.contract_id:
|
||||||
|
amounts = rule_parameter._get_parameter_from_code('l10n_in_professional_tax')
|
||||||
|
cost = record.contract_id.wage -1800
|
||||||
|
if cost >= 20000:
|
||||||
|
pf = amounts[0]
|
||||||
|
elif cost >= 15001 and cost < 20000:
|
||||||
|
pf = amounts[1]
|
||||||
|
else:
|
||||||
|
pf = 0
|
||||||
|
record.total_income = (monthly_gross * record._get_annualization_factor()) + (pf * 12)
|
||||||
|
|
||||||
|
@api.depends('contract_id', 'payslip_id', 'total_income')
|
||||||
|
def _compute_net_monthly(self):
|
||||||
|
for record in self:
|
||||||
|
factor = record._get_annualization_factor() if record.contract_id else 12
|
||||||
|
if record.payslip_id and record.payslip_id.net_wage:
|
||||||
|
record.net_monthly = record.payslip_id.net_wage
|
||||||
|
elif factor:
|
||||||
|
record.net_monthly = record.total_income / factor
|
||||||
|
else:
|
||||||
|
record.net_monthly = 0.0
|
||||||
|
|
||||||
|
@api.depends('tax_regime', 'contract_id')
|
||||||
|
def _compute_standard_deduction(self):
|
||||||
|
for record in self:
|
||||||
|
slab_master = record._find_applicable_slab(record.tax_regime) if record.tax_regime else False
|
||||||
|
record.standard_deduction = record._get_standard_deduction_amount(record.tax_regime or 'new', slab_master)
|
||||||
|
|
||||||
|
@api.depends('total_income', 'tax_regime', 'standard_deduction', 'contract_id')
|
||||||
|
def _compute_taxable_income(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.total_income:
|
||||||
|
record.taxable_income = 0.0
|
||||||
|
record.tax_on_taxable_income = 0.0
|
||||||
|
record.rebate = 0.0
|
||||||
|
record.total_tax_on_income = 0.0
|
||||||
|
record.surcharge = 0.0
|
||||||
|
record.cess = 0.0
|
||||||
|
record.total_tax = 0.0
|
||||||
|
continue
|
||||||
|
|
||||||
|
slab_master = record._find_applicable_slab(record.tax_regime) if record.tax_regime else False
|
||||||
|
if slab_master:
|
||||||
|
values = record._compute_tax_from_custom_slab(record.total_income, record.tax_regime, slab_master)
|
||||||
|
else:
|
||||||
|
values = record._compute_tax_from_rule_parameters(record.total_income, record.standard_deduction)
|
||||||
|
|
||||||
|
record.taxable_income = values['taxable_income']
|
||||||
|
record.tax_on_taxable_income = values['tax_on_taxable_income']
|
||||||
|
record.rebate = values['rebate']
|
||||||
|
record.total_tax_on_income = values['total_tax_on_income']
|
||||||
|
record.surcharge = values['surcharge']
|
||||||
|
record.cess = values['cess']
|
||||||
|
record.total_tax = values['total_tax']
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="l10n_in_tds_computation_wizard_view_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">l10n.in.tds.computation.wizard.view.form</field>
|
||||||
|
<field name="model">l10n.in.tds.computation.wizard</field>
|
||||||
|
<field name="inherit_id" ref="l10n_in_hr_payroll.l10n_in_tds_computation_wizard_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//form/group/field[@name='total_income']" position="after">
|
||||||
|
<field name="tax_regime" widget="radio" options="{'horizontal': true}"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//form/group/field[@name='standard_deduction']" position="attributes">
|
||||||
|
<attribute name="force_save">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in New Issue