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

166 lines
7.5 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from dateutil.relativedelta import relativedelta
from datetime import date
import calendar
from odoo import api, fields, models
class HrPayslip(models.Model):
_inherit = 'hr.payslip'
l10n_mx_daily_salary = fields.Float('MX: Daily Salary', compute='_compute_daily_salary')
l10n_mx_years_worked = fields.Integer('MX: Years Worked', compute='_compute_integration_factor')
l10n_mx_days_of_year = fields.Integer('MX: Days of the Year', compute='_compute_days_of_year')
l10n_mx_integration_factor = fields.Float('MX: Integration Factor', compute='_compute_integration_factor')
@api.depends('version_id.wage', 'version_id.schedule_pay')
def _compute_daily_salary(self):
for payslip in self:
if not payslip.version_id:
payslip.l10n_mx_daily_salary = 0
continue
payslip.l10n_mx_daily_salary = payslip.version_id.wage / payslip._rule_parameter('l10n_mx_schedule_table')[payslip.version_id.schedule_pay]
@api.depends('date_to')
def _compute_days_of_year(self):
for payslip in self:
year = payslip.date_to.year
payslip.l10n_mx_days_of_year = (date(year, 12, 31) - date(year, 1, 1)).days + 1
@api.depends('l10n_mx_days_of_year', 'date_from', 'date_to', 'version_id')
def _compute_integration_factor(self):
for payslip in self:
if not payslip.version_id:
payslip.l10n_mx_years_worked = 0
payslip.l10n_mx_integration_factor = 0
continue
start_date = (
payslip.employee_id.with_context(before_date=payslip.date_from)._get_first_contract_date()
or payslip.employee_id._get_first_contract_date()
)
payslip.l10n_mx_years_worked = payslip.date_to.year - start_date.year
if start_date <= payslip.date_to + relativedelta(year=start_date.year):
payslip.l10n_mx_years_worked += 1
holidays_count = payslip._rule_parameter('l10n_mx_holiday_tables')[payslip.l10n_mx_years_worked]
holiday_bonus_factor = holidays_count * payslip.version_id.l10n_mx_holiday_bonus_rate / 100
number_of_days_year = payslip.l10n_mx_days_of_year
payslip.l10n_mx_integration_factor = (holiday_bonus_factor + payslip._rule_parameter('l10n_mx_christmas_bonus') + number_of_days_year) / number_of_days_year
def _get_data_files_to_update(self):
# Note: file order should be maintained
return super()._get_data_files_to_update() + [(
'l10n_mx_hr_payroll', [
'data/hr_salary_rule_category_data.xml',
'data/hr_payroll_structure_type_data.xml',
'data/hr_payroll_structure_data.xml',
'data/hr_rule_parameters_data.xml',
'data/salary_rules/hr_salary_rule_christmas_bonus_data.xml',
'data/salary_rules/hr_salary_rule_regular_pay_data.xml',
])]
def _get_payslips_in_month(self):
self.ensure_one()
first_day_of_month = self.date_from.replace(day=1)
return self.env['hr.payslip'].search([
('employee_id', '=', self.employee_id.id),
('structure_code', '=', 'MX_REGULAR'),
('state', 'in', ['paid', 'validated']),
('date_to', '>=', first_day_of_month),
('date_from', '<', first_day_of_month + relativedelta(months=1))
])
def _get_accumulated_monthly_subsidy(self):
payslips_in_month = self._get_payslips_in_month()
line_values = payslips_in_month._get_line_values(['SUBSIDY_CURRENT_MONTH', 'SUBSIDY_NEXT_MONTH'])
accumulated_subsidy = 0.0
for slip in payslips_in_month:
subsidy_code = (
'SUBSIDY_CURRENT_MONTH'
if slip.date_from.month == self.date_from.month
else 'SUBSIDY_NEXT_MONTH'
)
accumulated_subsidy += line_values[subsidy_code][slip.id]['total']
return accumulated_subsidy
@api.model
def _schedule_timedelta(self, schedule, date_from, country_code=False):
if country_code == 'MX':
if schedule == '10_days':
return relativedelta(days=9)
elif schedule == '14_days':
return relativedelta(days=13)
elif schedule == 'bi-weekly':
days_in_month = calendar.monthrange(date_from.year, date_from.month)[1]
return relativedelta(day=15 if date_from.day <= 15 else days_in_month)
elif schedule == 'bi-monthly':
return relativedelta(months=2, days=-1)
return super()._schedule_timedelta(schedule, date_from, country_code)
def _get_schedule_days(self):
self.ensure_one()
if self.is_wrong_duration:
return (self.date_to - self.date_from).days + 1
return self._rule_parameter('l10n_mx_schedule_table')[self.version_id.schedule_pay]
def _get_worked_day_lines(self, domain=None, check_out_of_version=True):
self.ensure_one()
version = self.version_id
if (
self.struct_id.country_id.code != 'MX'
or not check_out_of_version
or version.work_entry_source != 'calendar'
):
return super()._get_worked_day_lines(domain, check_out_of_version)
worked_days_vals_list = self._get_worked_day_lines_values(domain=domain)
attendance_entry_type = self.env.ref('hr_work_entry.work_entry_type_attendance', raise_if_not_found=False)
out_of_contract_entry_type = self.env.ref('hr_work_entry.hr_work_entry_type_out_of_contract', raise_if_not_found=False)
attendance_vals = next((wd for wd in worked_days_vals_list if wd['work_entry_type_id'] == attendance_entry_type.id), None)
hours_per_day = self._get_worked_day_lines_hours_per_day() or 8.0
schedule_days = self._get_schedule_days()
if (
out_of_contract_entry_type
and (
version.contract_date_start > self.date_from
or (version.contract_date_end and version.contract_date_end < self.date_to)
)
):
date_from = max(version.contract_date_start, self.date_from)
date_to = min(version.contract_date_end or self.date_to, self.date_to)
period_days = (date_to - date_from).days + 1
out_of_contract_days = schedule_days - period_days
worked_days_vals_list.append({
'sequence': out_of_contract_entry_type.sequence,
'work_entry_type_id': out_of_contract_entry_type.id,
'number_of_days': out_of_contract_days,
'number_of_hours': out_of_contract_days * hours_per_day,
})
if attendance_vals:
entry_type_ids = [wd['work_entry_type_id'] for wd in worked_days_vals_list]
extra_hours_entry_types = self.env['hr.work.entry.type'].browse(entry_type_ids).filtered(lambda et: et.is_extra_hours)
recorded_hours = sum(
wd['number_of_hours']
for wd in worked_days_vals_list
if wd['work_entry_type_id'] not in extra_hours_entry_types.ids
)
hours_to_adjust = schedule_days * hours_per_day - recorded_hours
days_to_adjust = round(hours_to_adjust / hours_per_day, 5) if hours_per_day else 0.0
attendance_vals['number_of_hours'] += hours_to_adjust
attendance_vals['number_of_days'] += self._round_days(attendance_entry_type, days_to_adjust)
return worked_days_vals_list