Files
enterprise-addons/l10n_tr_hr_payroll/models/hr_payslip.py
T

102 lines
4.5 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.tools import float_round
from odoo.tools.float_utils import float_compare
class HrPayslip(models.Model):
_inherit = 'hr.payslip'
l10n_tr_ytd_gross = fields.Monetary(string='Year to Date Gross', compute='_compute_l10n_tr_ytd_amounts', currency_field='currency_id')
l10n_tr_ytd_tax = fields.Monetary(string='Year to Date Tax', compute='_compute_l10n_tr_ytd_amounts', currency_field='currency_id')
l10n_tr_current_month_gross = fields.Monetary(compute='_compute_l10n_tr_current_month_gross', currency_field='currency_id')
@api.depends('employee_id', 'date_from')
def _compute_l10n_tr_ytd_amounts(self):
self.l10n_tr_ytd_gross = 0
self.l10n_tr_ytd_tax = 0
tr_slips = self.filtered(lambda p: p.country_code == 'TR')
if not tr_slips:
return
reference_dates = tr_slips.mapped('date_from')
min_date = min(reference_dates).replace(month=1, day=1)
max_date = max(reference_dates)
current_year_payslips_raw = self.env['hr.payslip']._read_group(
domain=[('employee_id', 'in', self.employee_id.ids),
('date_from', '>=', min_date),
('date_to', '<', max_date),
('state', 'in', ('validated', 'paid'))],
groupby=['employee_id', 'date_from:year'],
aggregates=['id:recordset']
)
current_year_payslips = {}
for employee, date, payslips in current_year_payslips_raw:
current_year_payslips.setdefault(employee, {})[date.year] = payslips
for payslip in tr_slips.sorted("date_from"):
reference_date = payslip.date_from
ytd_payslips = current_year_payslips.get(payslip.employee_id, {}).get(reference_date.year, self.env['hr.payslip']).filtered(lambda ps: ps.date_to < reference_date)
ytd_amounts = ytd_payslips._get_line_values(['CURTAXABLE', 'BTAXNET'], compute_sum=True)
payslip.l10n_tr_ytd_gross = ytd_amounts['CURTAXABLE']['sum']['total']
payslip.l10n_tr_ytd_tax = ytd_amounts['BTAXNET']['sum']['total']
def _l10n_tr_calculate_net_guess_accuracy(self, guess, target):
self.ensure_one()
self.l10n_tr_current_month_gross = guess
expected_ntg = next((line for line in self._get_payslip_lines() if line.get('code') == 'EXPNET'), {'amount': 0.0})
return float_round(expected_ntg['amount'], precision_rounding=self.currency_id.rounding) - target
def _estimate_l10n_tr_gross_from_net(self, target, max_iterations=50, tolerance=0.001):
"""
A safe version of Newton's method using the secant method to avoid division by zero.
"""
self.ensure_one()
guess1, guess2 = self.version_id.wage, self.version_id.wage * 2
for _ in range(max_iterations):
value1 = self._l10n_tr_calculate_net_guess_accuracy(guess1, target)
value2 = self._l10n_tr_calculate_net_guess_accuracy(guess2, target)
if abs(value1) < tolerance:
return guess1
# prevent division by zero
if float_compare(value1, value2, precision_digits=self.currency_id.decimal_places) == 0:
break
# Estimate the slope (secant) and update guess using the secant method
slope = (value2 - value1) / (guess2 - guess1)
next_guess = guess1 - value1 / slope
guess1, guess2 = guess2, next_guess
return guess1
@api.depends('version_id')
def _compute_l10n_tr_current_month_gross(self):
for payslip in self:
if payslip.country_code == 'TR' and payslip.version_id.l10n_tr_is_net_to_gross:
payslip.l10n_tr_current_month_gross = payslip._estimate_l10n_tr_gross_from_net(payslip.version_id.wage)
else:
payslip.l10n_tr_current_month_gross = 0
def _l10n_tr_get_tax(self, taxable_amount):
self.ensure_one()
total_tax = 0
rates = iter(self._rule_parameter('l10_tr_tax_rates'))
lower, upper, rate = next(rates)
while lower < taxable_amount:
total_tax += min((taxable_amount - lower, float(upper) - lower)) * rate
lower, upper, rate = next(rates)
return total_tax
def _get_data_files_to_update(self):
# Note: file order should be maintained
return super()._get_data_files_to_update() + [(
'l10n_tr_hr_payroll', [
'data/hr_rule_parameter_data.xml',
])]