diff --git a/addons_extensions/employee_it_declaration/__manifest__.py b/addons_extensions/employee_it_declaration/__manifest__.py
index 5a6e3ee94..504b2607a 100644
--- a/addons_extensions/employee_it_declaration/__manifest__.py
+++ b/addons_extensions/employee_it_declaration/__manifest__.py
@@ -34,7 +34,7 @@
'version': '0.1',
# 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
'data': [
@@ -48,14 +48,15 @@
'report/report_action.xml',
'report/it_tax_template.xml',
'views/it_tax_menu_and_wizard_view.xml',
+ 'wizards/hr_tds_calculation.xml',
'wizards/children_education_costing.xml',
'wizards/employee_life_insurance.xml',
'wizards/nsc_declaration.xml',
- 'wizards/self_occupied_property.xml',
- 'wizards/letout_house_property.xml',
- 'wizards/nsc_income_loss.xml',
- 'data/default_investment_types.xml',
- # 'views/it_investment_type.xml',
- # 'views/it_investment_costing.xml'
- ],
-}
+ 'wizards/self_occupied_property.xml',
+ 'wizards/letout_house_property.xml',
+ 'wizards/nsc_income_loss.xml',
+ 'data/default_investment_types.xml',
+ # 'views/it_investment_type.xml',
+ # 'views/it_investment_costing.xml'
+ ],
+}
diff --git a/addons_extensions/employee_it_declaration/models/it_tax_statement_wiz.py b/addons_extensions/employee_it_declaration/models/it_tax_statement_wiz.py
index eecf2c1fd..574227e74 100644
--- a/addons_extensions/employee_it_declaration/models/it_tax_statement_wiz.py
+++ b/addons_extensions/employee_it_declaration/models/it_tax_statement_wiz.py
@@ -18,9 +18,9 @@ class ITTaxStatementWizard(models.TransientModel):
contract_id = fields.Many2one('hr.contract', related='employee_id.contract_id', required=True)
currency_id = fields.Many2one('res.currency', related='employee_id.company_id.currency_id')
- period_id = fields.Many2one('payroll.period', required=True)
- period_line = fields.Many2one('payroll.period.line',
- domain="[('period_id', '=', period_id), ('to_date', '<', fields.Date.today())]")
+ period_id = fields.Many2one('payroll.period', required=True)
+ period_line = fields.Many2one('payroll.period.line',
+ domain="[('period_id', '=', period_id), ('to_date', '<', fields.Date.today())]")
# Taxpayer profile
taxpayer_name = fields.Char(related='employee_id.name')
@@ -96,12 +96,32 @@ class ITTaxStatementWizard(models.TransientModel):
('new', 'New Regime')
], string="Beneficial Regime", readonly=True)
- def _get_age_category(self, age):
- if age < 60:
- return 'below_60'
- elif age < 80:
- return '60_to_80'
- return 'above_80'
+ def _get_age_category(self, age):
+ if age < 60:
+ return 'below_60'
+ elif age < 80:
+ return '60_to_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):
"""Find the applicable tax slab without forcing both regimes to exist."""
@@ -116,7 +136,7 @@ class ITTaxStatementWizard(models.TransientModel):
('residence_type', '=', 'both')
], limit=1)
- def _get_applicable_slab(self, regime, period_id, age, residence_type):
+ def _get_applicable_slab(self, regime, period_id, age, residence_type):
"""Get the applicable tax slab based on regime, age, and residence type"""
age_category = self._get_age_category(age)
slab_master = self._find_applicable_slab(regime, period_id, age, residence_type)
@@ -125,7 +145,21 @@ class ITTaxStatementWizard(models.TransientModel):
"No tax slab found for %s Regime with Age Category: %s and Residence Type: %s"
) % (regime.capitalize(), age_category.replace('_', ' ').title(), residence_type))
- 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):
if slab_master:
@@ -295,7 +329,7 @@ class ITTaxStatementWizard(models.TransientModel):
return list(grouped.values())
- def fetch_salary_components(self):
+ def fetch_salary_components(self):
"""fetch salary components from payroll data"""
for rec in self:
data = {
@@ -311,10 +345,10 @@ class ITTaxStatementWizard(models.TransientModel):
}
if not rec.employee_id or not rec.contract_id or not rec.period_id or not rec.period_line:
return data
- period_lines = rec.period_id.period_line_ids
-
- for line in period_lines:
- components = rec._get_salary_components_for_period_line(line)
+ period_lines = rec._get_effective_period_lines()
+
+ for line in period_lines:
+ components = rec._get_salary_components_for_period_line(line)
if line.from_date and rec.period_line.from_date and line.from_date <= rec.period_line.from_date:
data['basic_salary']['actual'].append(components['basic_salary'])
data['hra_salary']['actual'].append(components['hra_salary'])
@@ -378,7 +412,7 @@ class ITTaxStatementWizard(models.TransientModel):
)
rec.standard_deduction = rec._get_standard_deduction(rec.tax_regime, slab_master)
- def fetch_deduction_components(self):
+ def fetch_deduction_components(self):
for rec in self:
data = {
'professional_tax': {'actual': [], 'projected': []},
@@ -387,12 +421,12 @@ class ITTaxStatementWizard(models.TransientModel):
if not rec.employee_id or not rec.contract_id or not rec.period_id or not rec.period_line:
return data
- for line in rec.period_id.period_line_ids:
- 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'
- data['professional_tax'][bucket].append(rule_amounts['PT'])
- data['nps_employer_contribution'][bucket].append(rule_amounts['PFE'])
- return data
+ for line in rec._get_effective_period_lines():
+ 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'
+ data['professional_tax'][bucket].append(rule_amounts['PT'])
+ data['nps_employer_contribution'][bucket].append(rule_amounts['PFE'])
+ return data
@api.onchange('employee_id')
@@ -725,25 +759,19 @@ class ITTaxStatementWizard(models.TransientModel):
'target': 'current',
}
- def _prepare_income_tax_data(self, include_comparison=False):
- """Prepare data for the tax statement report"""
- today = date.today()
- fy_start = self.period_id.from_date
- fy_end = self.period_id.to_date
- total_months = ((fy_end.year - fy_start.year) * 12 +
- (fy_end.month - fy_start.month) + 1)
-
- line_start = self.period_line.from_date
- current_month_index = ((line_start.year - fy_start.year) * 12 +
- (line_start.month - 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)
+ def _prepare_income_tax_data(self, include_comparison=False):
+ """Prepare data for the tax statement report"""
+ today = date.today()
+ display_fy_start = self.period_id.from_date
+ fy_end = self.period_id.to_date
+ effective_fy_start = self._get_effective_period_start() or display_fy_start
+ 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
+ current_month_index = ((line_start.year - effective_fy_start.year) * 12 +
+ (line_start.month - effective_fy_start.month) + 1)
+ values = self._get_tax_base_values(include_comparison=include_comparison)
salary_components_data = values['salary_components_data']
annual_gross_salary = values['annual_gross_salary']
gross_salary_actual = values['gross_salary_actual']
@@ -778,15 +806,16 @@ class ITTaxStatementWizard(models.TransientModel):
# Prepare data structure matching screenshot format
# Financial year (period_id)
- fy_start = self.period_id.from_date
- fy_end = self.period_id.to_date
- total_months = ((fy_end.year - fy_start.year) * 12 +
- (fy_end.month - fy_start.month) + 1)
-
- # Current month (period_line)
- line_start = self.period_line.from_date
- current_month_index = ((line_start.year - fy_start.year) * 12 +
- (line_start.month - fy_start.month) + 1)
+ display_fy_start = self.period_id.from_date
+ fy_end = self.period_id.to_date
+ effective_fy_start = self._get_effective_period_start() or display_fy_start
+ total_months = ((fy_end.year - effective_fy_start.year) * 12 +
+ (fy_end.month - effective_fy_start.month) + 1)
+
+ # Current month (period_line)
+ line_start = self.period_line.from_date
+ current_month_index = ((line_start.year - effective_fy_start.year) * 12 +
+ (line_start.month - effective_fy_start.month) + 1)
tax_result['roundoff_taxable_income'] = float(round(tax_result["taxable_income"] / 10) * 10)
birthday = self.employee_id.birthday
if birthday:
@@ -806,7 +835,7 @@ class ITTaxStatementWizard(models.TransientModel):
'total': total,
})
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,
'report_time': today.strftime('%d-%m-%Y %H:%M'),
'user': 'ESS',
diff --git a/addons_extensions/employee_it_declaration/models/slab_master.py b/addons_extensions/employee_it_declaration/models/slab_master.py
index 79c426bb5..359fc2fe8 100644
--- a/addons_extensions/employee_it_declaration/models/slab_master.py
+++ b/addons_extensions/employee_it_declaration/models/slab_master.py
@@ -13,17 +13,17 @@ class IncomeTaxSlabMaster(models.Model):
)
]
- name = fields.Char(string="Slab Name", required=True)
- period_id = fields.Many2one('payroll.period')
+ name = fields.Char(string="Slab Name", required=True, copy=False, default="AY")
+ period_id = fields.Many2one('payroll.period', copy=False)
regime = fields.Selection([
('old', 'Old Tax Regime'),
('new', 'New Tax Regime')
- ], required=True)
+ ], required=True, default='old')
age_category = fields.Selection([
('below_60', 'Below 60 Years'),
('60_to_80', '60-80 Years'),
('above_80', 'Above 80 Years')
- ], required=True)
+ ], required=True, default='below_60')
residence_type = fields.Selection([
('resident', '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")
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):
_name = 'it.slab.master.rules'
_description = 'Income Tax slab rules'
diff --git a/addons_extensions/employee_it_declaration/views/it_tax_menu_and_wizard_view.xml b/addons_extensions/employee_it_declaration/views/it_tax_menu_and_wizard_view.xml
index 8fbeca178..597beb191 100644
--- a/addons_extensions/employee_it_declaration/views/it_tax_menu_and_wizard_view.xml
+++ b/addons_extensions/employee_it_declaration/views/it_tax_menu_and_wizard_view.xml
@@ -21,19 +21,19 @@
-
+
diff --git a/addons_extensions/employee_it_declaration/wizards/__init__.py b/addons_extensions/employee_it_declaration/wizards/__init__.py
index 704863e83..86a2ca6a6 100644
--- a/addons_extensions/employee_it_declaration/wizards/__init__.py
+++ b/addons_extensions/employee_it_declaration/wizards/__init__.py
@@ -1,3 +1,4 @@
+from . import hr_tds_calculation
from . import children_education_costing
from . import employee_life_insurance
from . import nsc_declaration
diff --git a/addons_extensions/employee_it_declaration/wizards/hr_tds_calculation.py b/addons_extensions/employee_it_declaration/wizards/hr_tds_calculation.py
new file mode 100644
index 000000000..6fd737bfc
--- /dev/null
+++ b/addons_extensions/employee_it_declaration/wizards/hr_tds_calculation.py
@@ -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']
diff --git a/addons_extensions/employee_it_declaration/wizards/hr_tds_calculation.xml b/addons_extensions/employee_it_declaration/wizards/hr_tds_calculation.xml
new file mode 100644
index 000000000..dcc5ecef3
--- /dev/null
+++ b/addons_extensions/employee_it_declaration/wizards/hr_tds_calculation.xml
@@ -0,0 +1,17 @@
+
+
+
+ l10n.in.tds.computation.wizard.view.form
+ l10n.in.tds.computation.wizard
+
+
+
+
+
+
+ 1
+
+
+
+
+
\ No newline at end of file