from datetime import datetime, time from dateutil.relativedelta import relativedelta from odoo import api, fields, models from odoo.addons.resource.models.resource import HOURS_PER_DAY class HRLeaveAllocation(models.Model): _inherit = 'hr.leave.allocation' interval_unit = fields.Selection(selection_add=[('payslip', 'Payslip')]) @api.model def payslip_update_accrual(self, payslips): employees = payslips.mapped('employee_id') holidays = self.env['hr.leave.allocation'].search([ ('employee_id', 'in', employees.ids), ('accrual', '=', True), ('state', '=', 'validate'), ('holiday_type', '=', 'employee'), '|', ('date_to', '=', False), ('date_to', '>', fields.Datetime.now()), ]) for holiday in holidays: days_to_give = holiday.number_per_interval if holiday.unit_per_interval == 'hours': # As we encode everything in days in the database we need to convert # the number of hours into days for this we use the # mean number of hours set on the employee's calendar days_to_give = days_to_give / (holiday.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY) new_number_of_days = holiday.number_of_days + days_to_give if holiday.accrual_limit > 0: new_number_of_days = min(new_number_of_days, holiday.accrual_limit) holiday.number_of_days = new_number_of_days # This is a 'patch' because I cannot influence the domain/search here to filter out the ones by payslip. @api.model def _update_accrual(self): """ Method called by the cron task in order to increment the number_of_days when necessary. """ today = fields.Date.from_string(fields.Date.today()) holidays = self.search([('interval_unit', '!=', 'payslip'), # new domain filter ('accrual', '=', True), ('state', '=', 'validate'), ('holiday_type', '=', 'employee'), '|', ('date_to', '=', False), ('date_to', '>', fields.Datetime.now()), '|', ('nextcall', '=', False), ('nextcall', '<=', today)]) for holiday in holidays: values = {} delta = relativedelta(days=0) if holiday.interval_unit == 'weeks': delta = relativedelta(weeks=holiday.interval_number) if holiday.interval_unit == 'months': delta = relativedelta(months=holiday.interval_number) if holiday.interval_unit == 'years': delta = relativedelta(years=holiday.interval_number) values['nextcall'] = (holiday.nextcall if holiday.nextcall else today) + delta period_start = datetime.combine(today, time(0, 0, 0)) - delta period_end = datetime.combine(today, time(0, 0, 0)) # We have to check when the employee has been created # in order to not allocate him/her too much leaves start_date = holiday.employee_id._get_date_start_work() # If employee is created after the period, we cancel the computation if period_end <= start_date: holiday.write(values) continue # If employee created during the period, taking the date at which he has been created if period_start <= start_date: period_start = start_date worked = holiday.employee_id.get_work_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] left = holiday.employee_id.get_leave_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] prorata = worked / (left + worked) if worked else 0 days_to_give = holiday.number_per_interval if holiday.unit_per_interval == 'hours': # As we encode everything in days in the database we need to convert # the number of hours into days for this we use the # mean number of hours set on the employee's calendar days_to_give = days_to_give / (holiday.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY) values['number_of_days'] = holiday.number_of_days + days_to_give * prorata if holiday.accrual_limit > 0: values['number_of_days'] = min(values['number_of_days'], holiday.accrual_limit) holiday.write(values)