2096 lines
140 KiB
Python
2096 lines
140 KiB
Python
# -*- coding: utf-8 -*-
|
|
import math
|
|
from datetime import datetime
|
|
import datetime as dt
|
|
|
|
from odoo import SUPERUSER_ID, fields
|
|
from odoo import models, fields, api, _
|
|
from dateutil.relativedelta import relativedelta
|
|
from dateutil import relativedelta
|
|
from datetime import timedelta
|
|
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
|
|
from odoo.exceptions import AccessError, ValidationError, MissingError, UserError
|
|
import calendar
|
|
from collections import defaultdict
|
|
|
|
|
|
def daterange(start_date, end_date):
|
|
for n in range(int((end_date - start_date).days)):
|
|
yield start_date + timedelta(n)
|
|
|
|
|
|
class HRHolidays(models.Model):
|
|
_inherit = 'hr.holidays'
|
|
_order = "create_date desc"
|
|
|
|
|
|
replace_by = fields.Many2one(comodel_name='hr.employee', string="Replace By")
|
|
emp_id = fields.Integer(string="id")
|
|
employee_id = fields.Many2one('hr.employee', 'Employee', default=lambda item: item.get_user_id(), )
|
|
last_employee_contract = fields.Many2one(related='employee_id.contract_id', readonly=True)
|
|
employee_type = fields.Selection(related='employee_id.contract_id.emp_type', store=True, readonly=True)
|
|
issuing_clearance_form = fields.Selection(selection=[('yes', 'Yes'), ('no', 'No')], default='no')
|
|
issuing_deliver_custdy = fields.Selection(selection=[('yes', 'Yes'), ('no', 'No')], default='no')
|
|
start_date = fields.Date(string="Start Date", default=fields.Datetime.now())
|
|
end_date = fields.Date(string="End Date")
|
|
notes = fields.Text()
|
|
issuing_exit_return = fields.Selection(selection=[('yes', 'Yes'), ('no', 'No')], default='no')
|
|
check_allocation_view = fields.Selection(selection=[('allocation', 'Allocation'), ('balance', 'Balance')])
|
|
exit_return_duration = fields.Float(related='number_of_days_temp')
|
|
permission_request_for = fields.Selection(selection=[
|
|
('employee', 'For Employee Only'),
|
|
('family', 'For Family Only'),
|
|
('all', 'For Employee And Family Only')
|
|
])
|
|
issuing_ticket = fields.Selection(selection=[('yes', 'Yes'), ('no', 'No')], default='no')
|
|
# ticket_cash_request_type = fields.Selection(
|
|
# selection=[('request_ticket', _('Request Ticket')), ('cash_in_lieu', _('Cash In Lieu'))])
|
|
ticket_cash_request_type = fields.Many2one('hr.ticket.request.type')
|
|
ticket_cash_request_for = fields.Selection(selection=[
|
|
('employee', 'For Employee Only'),
|
|
('family', 'For Family Only'),
|
|
('all', 'For Employee And Family Only')
|
|
])
|
|
check_state = fields.Boolean('check', default=True)
|
|
check_related = fields.Boolean('Check Related', default=False)
|
|
leaves_taken = fields.Float(tracking=True)
|
|
remaining_leaves = fields.Float(tracking=False)
|
|
|
|
cron_run_date = fields.Date()
|
|
holiday_ids = fields.One2many(comodel_name='hr.inverse.holidays', inverse_name='holiday_id')
|
|
hiring_date = fields.Date(related='employee_id.first_hiring_date', store=True, readonly=True)
|
|
# check_boolean = fields.Boolean(default=True)
|
|
check_unlimit = fields.Boolean()
|
|
holiday_limit_check = fields.Boolean(store=True)
|
|
|
|
canceled_duration = fields.Float(readonly=True)
|
|
state = fields.Selection(selection_add=[
|
|
('approved', 'Wait Approval')
|
|
])
|
|
holiday_status_id = fields.Many2one('hr.holidays.status', domain=[('id', 'in', [])])
|
|
number_of_days_temp = fields.Float(
|
|
'Allocation', copy=False, readonly=False,
|
|
help='Number of days of the leave request according to your working schedule.')
|
|
# TODO: Add readonly logic in XML view: readonly="state not in ('draft', 'confirm')"
|
|
return_from_leave = fields.Boolean(readonly=True)
|
|
current_date_plus_years = fields.Datetime()
|
|
official_holiday_days = fields.Float('Included Official Holidays', copy=False, readonly=False, default=0,
|
|
help='Number of days of official holiday during this period.')
|
|
reconcile_leave = fields.Boolean()
|
|
date_on = fields.Boolean()
|
|
leave_balance = fields.Float('Current Balance', copy=False, compute='_compute_leave_balance', store=True,
|
|
readonly=True)
|
|
delegate_acc = fields.Boolean('Delegate Permissions', default=False)
|
|
delegated_group_ids = fields.Many2many('res.groups', string="Delegated Access Groups", )
|
|
delegated_company_ids = fields.Many2many('res.company', string="Delegated Companies", )
|
|
delegated_department_ids = fields.Many2many('hr.department', string="Delegated Departments")
|
|
|
|
leave_balance_date = fields.Float(store=True, readonly=True, compute='_leave_balance_date',
|
|
help='The Balance Remains until the end date of the current Holiday Request')
|
|
|
|
attach_ids = fields.One2many('ir.attachment', 'att_holiday_ids')
|
|
company_id = fields.Many2one('res.company', default=lambda self: self.env.company)
|
|
emp_company_id = fields.Many2one(related='employee_id.company_id')
|
|
|
|
emp_number = fields.Char(related='employee_id.emp_no',store=True)
|
|
|
|
request_done = fields.Boolean(default=False,readonly=True)
|
|
is_branch = fields.Many2one(related='department_id.branch_name', store=True, readonly=True)
|
|
|
|
to_date = fields.Boolean(string='Today',compute='_leave_today_run', store=True)
|
|
|
|
def _compute_number_of_days(self):
|
|
res = super(HRHolidays, self)._compute_number_of_days()
|
|
self._leave_today_run()
|
|
|
|
@api.depends('date_from','date_to', 'create_date','state')
|
|
def _leave_today_run(self):
|
|
today = datetime.now().date()
|
|
for item in self:
|
|
if item.date_to and item.date_from:
|
|
if item.date_from.date() <= today <= item.date_to.date():
|
|
item.to_date = True
|
|
else:
|
|
item.to_date = False
|
|
|
|
## to remove followers ##
|
|
def _remove_followers_holiday(self):
|
|
for rec in self:
|
|
followers = self.env['mail.followers'].search(
|
|
[('res_id', '=', rec.id),('res_model','=','hr.holidays')])
|
|
followers.sudo().unlink()
|
|
|
|
|
|
def _check_state_access_right(self, vals):
|
|
if vals.get('state') and vals['state'] not in ['draft', 'confirm', 'cancel'] and not \
|
|
(self.env.user and self.env.user.has_group(
|
|
'hr_holidays_community.group_hr_holidays_user,hr.group_hr_manager,hr_base.group_general_manager')):
|
|
return False
|
|
return True
|
|
|
|
def check_annual_balance(self):
|
|
for rec in self:
|
|
if rec.type == "remove":
|
|
annual = rec.env['hr.holidays.status'].search([('leave_type', '=', 'annual')])
|
|
if rec.employee_id and annual:
|
|
balance = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id), ('type', '=', 'add'),
|
|
# ('holiday_status_id', '=', annual.id),
|
|
('holiday_status_id.leave_type', '=', 'annual'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], order='id desc', limit=1).remaining_leaves or 0.0
|
|
if balance >= 1:
|
|
raise ValidationError(_("You Can not order this leave and you have annual Leave Balance"))
|
|
|
|
def check_balance_per_period(self, rec):
|
|
old_holiday_id = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id), ('type', '=', 'remove'),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('state', 'not in', ['cancel', 'refuse']),
|
|
], limit=1, order="date_from desc")
|
|
if old_holiday_id:
|
|
old_leave_date_from = datetime.strptime(str(old_holiday_id.date_from),
|
|
DEFAULT_SERVER_DATETIME_FORMAT)
|
|
date_from = datetime.strptime(str(rec.date_from),
|
|
DEFAULT_SERVER_DATETIME_FORMAT)
|
|
if old_leave_date_from > date_from:
|
|
date_from = datetime.strptime(str(rec.date_from),
|
|
DEFAULT_SERVER_DATETIME_FORMAT)
|
|
date_of_period = date_from + relativedelta.relativedelta(months=rec.holiday_status_id.period_unpaid_leave)
|
|
holiday_ids = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id), ('type', '=', 'remove'),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('state', 'not in', ['cancel', 'refuse']),
|
|
('date_from', '>=', str(date_from)),
|
|
('date_from', '<=', str(date_of_period))])
|
|
number_days_of_leaves = sum(holiday_ids.mapped('number_of_days_temp')) + rec.number_of_days_temp
|
|
max_allowed = rec.holiday_status_id.unpaid_leave_days_per_period
|
|
if rec.holiday_status_id.type_unpaid == 'termination':
|
|
if number_days_of_leaves > max_allowed:
|
|
raise ValidationError(_('The number of days of leave exceed the maximum allowed %s') % (max_allowed))
|
|
|
|
@api.depends('date_from','date_to', 'holiday_status_id')
|
|
def _leave_balance_date(self):
|
|
for rec in self:
|
|
rec._remove_followers_holiday()
|
|
to_work_days = 0
|
|
to_work_days2 = 0
|
|
leave = rec.holiday_status_id
|
|
current_date = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
if rec.type == 'remove' and rec.employee_id.first_hiring_date and rec.date_from:
|
|
first_hiring_date = datetime.strptime(str(rec.employee_id.first_hiring_date), "%Y-%m-%d").date()
|
|
contract_start_date1 = datetime.strftime(first_hiring_date, DEFAULT_SERVER_DATE_FORMAT)
|
|
contract_start_date = datetime.strptime(str(contract_start_date1), "%Y-%m-%d").date()
|
|
working_days = (current_date - contract_start_date).days + 1
|
|
working_years = working_days / 365
|
|
holiday_duration = 0
|
|
for item in leave.duration_ids:
|
|
if item.date_from <= working_years < item.date_to:
|
|
holiday_duration = item.duration
|
|
###### get last cron date to compute leave_balance_date #
|
|
if leave:
|
|
balance_holiday = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id), ('type', '=', 'add'),
|
|
('holiday_status_id', '=', leave.id),('check_allocation_view', '=', 'balance')
|
|
], order='id desc', limit=1).holiday_ids
|
|
|
|
if balance_holiday:
|
|
last_cron_date = datetime.strptime(str(balance_holiday[-1].cron_run_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
holi_date = (datetime.strptime(str(rec.date_from), "%Y-%m-%d %H:%M:%S")).date()
|
|
if last_cron_date < holi_date:
|
|
to_work_days2 = (holi_date - last_cron_date).days
|
|
#else:
|
|
#to_work_days2 = -(last_cron_date - holi_date).days
|
|
##### end ########
|
|
'''to_work_days = fields.Datetime.from_string(rec.date_from) > \
|
|
fields.Datetime.from_string(fields.Datetime.now()) and \
|
|
(datetime.strptime(str(rec.date_from), "%Y-%m-%d %H:%M:%S") -
|
|
(datetime.utcnow() + timedelta(hours=3))).days + 1 or 0'''
|
|
|
|
upcoming_leave = self.remaining_leaves_of_day_by_date(rec.employee_id, str(current_date), leave , is_month=False, is_years=False) * to_work_days2
|
|
if upcoming_leave > 0 and leave.leave_type == 'annual':
|
|
rec.leave_balance_date = round(rec.leave_balance + upcoming_leave, 2)
|
|
exceed_days = leave.number_of_save_days + holiday_duration
|
|
if rec.leave_balance_date > exceed_days and not (
|
|
rec.holiday_limit_check or leave.limit or self._context.get('stop_const')):
|
|
raise ValidationError(
|
|
_("Sorry the days you are trying to taken exceed tha maximum allowed days to be given"))
|
|
else:
|
|
rec.leave_balance_date = round(rec.leave_balance, 2)
|
|
|
|
@api.constrains('date_from', 'date_to', 'holiday_status_id', 'holiday_limit_check')
|
|
def _check_number_of_days(self):
|
|
for rec in self:
|
|
leave = rec.holiday_status_id
|
|
if rec.type == 'add' and rec.check_allocation_view == 'allocation':
|
|
holidays = self.env['hr.holidays'].search([('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'allocation'),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('employee_id', '=', rec.employee_id.id),
|
|
('state', 'not in', ('cancel', 'refuse')),
|
|
('id', '!=', rec.id)])
|
|
taken = holidays and sum(holidays.mapped('number_of_days_temp')) or 0
|
|
if leave.payslip_type == 'unpaid' and leave.check_annual_holiday:
|
|
rec.check_annual_balance()
|
|
if leave.leave_annual_type == 'open_balance' \
|
|
and rec.number_of_days_temp \
|
|
and (leave.visible_fields
|
|
and
|
|
(
|
|
leave.number_of_save_days
|
|
and leave.number_of_save_days >= 1
|
|
and rec.number_of_days_temp > (leave.duration + leave.number_of_save_days - taken)
|
|
)
|
|
or (
|
|
not leave.number_of_save_days
|
|
and leave.number_of_years
|
|
and (
|
|
(
|
|
leave.number_of_years > 0
|
|
and rec.number_of_days_temp > (leave.duration + (
|
|
leave.duration * leave.number_of_years) - taken)
|
|
)
|
|
or
|
|
(
|
|
leave.number_of_years < 1
|
|
and rec.number_of_days_temp > (leave.duration - taken)
|
|
)
|
|
)
|
|
)
|
|
) \
|
|
or (not leave.visible_fields and leave.duration and (
|
|
rec.number_of_days_temp > (leave.duration - taken))):
|
|
raise ValidationError(_(
|
|
"Sorry the days you are trying to allocate exceed tha maximum allowed days to be given"))
|
|
elif rec.type == 'remove':
|
|
if leave.payslip_type == 'unpaid' and leave.check_annual_holiday:
|
|
rec.check_annual_balance()
|
|
rec.check_balance_per_period(rec)
|
|
if not (rec.employee_id.contract_id and rec.employee_id.first_hiring_date and rec.date_from):
|
|
raise ValidationError(_('Please make sure employee contract, first hiring date and leave start date'
|
|
' are set.'))
|
|
balance = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id),
|
|
('type', '=', 'add'),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('check_allocation_view', '=', 'balance')
|
|
], order='id desc', limit=1)
|
|
if not balance:
|
|
raise ValidationError(_('Sorry you have no balance'))
|
|
worked_days = ((datetime.utcnow() + timedelta(hours=3)).date() -
|
|
datetime.strptime(str(rec.employee_id.first_hiring_date), "%Y-%m-%d").date()).days + 1
|
|
#if worked_days < rec.holiday_status_id.number_of_days:
|
|
# raise UserError(_('Sorry you can not create leave request you have not holidays'))
|
|
#### Delete upcoming_leave and add up##
|
|
|
|
if rec.number_of_days_temp > rec.leave_balance_date and rec.state != 'validate1' and not (
|
|
rec.holiday_limit_check or leave.limit or self._context.get('stop_const')):
|
|
|
|
raise UserError(
|
|
_('Sorry your request refused your holidays not cover this duration For Employee %s') % (
|
|
rec.employee_id.name))
|
|
|
|
if leave.leave_type == 'hajj':
|
|
if self.env['ir.module.module'].sudo().search([('state', '=', 'installed'),
|
|
('name', '=', 'web_hijri_datepicker')]):
|
|
if rec.date_to and rec.date_from:
|
|
hijri_df = rec.employee_id.contract_id.change_current_date_hijri(fields.Date.to_string(
|
|
fields.Date.from_string(rec.date_from)))
|
|
hijri_dt = rec.employee_id.contract_id.change_current_date_hijri(fields.Date.to_string(
|
|
fields.Date.from_string(rec.date_to)))
|
|
if hijri_df.datetuple()[1] != 12 and hijri_dt.datetuple()[1] != 12:
|
|
raise UserError(
|
|
_('Sorry Hajj Leave can only be requested in the month of Zu Alhejja'))
|
|
|
|
@api.constrains('date_from', 'date_to', 'holiday_status_id')
|
|
#@api.constrains('holiday_status_id')
|
|
def _chick_leave_balance(self):
|
|
for rec in self:
|
|
taken_days = 0.0
|
|
if rec.employee_id and rec.holiday_status_id:
|
|
holi_id = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id), ('type', '=', 'add'),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('check_allocation_view', '=', 'balance')
|
|
], order='id desc', limit=1).id
|
|
taken_days = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id),
|
|
('type', '=', 'add'),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('check_allocation_view', '=', 'balance')
|
|
], order='id desc', limit=1).leaves_taken
|
|
if taken_days < 0 and rec.type == 'remove':
|
|
raise UserError(_('Sorry the leaves taken is less than zero'))
|
|
if not holi_id and rec.type == 'remove':
|
|
raise UserError(_('There Is No Holiday Balance Record'))
|
|
|
|
if rec.holiday_status_id.leave_type == 'sick' and rec.type == 'remove':
|
|
sickness_leaves = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id),
|
|
('type', '=', 'add'),
|
|
('holiday_status_id.leave_type', '=', 'sick'),
|
|
('holiday_status_id', '!=', rec.holiday_status_id.id),
|
|
('holiday_status_id.sickness_severity', '<',
|
|
rec.holiday_status_id.sickness_severity),
|
|
('check_allocation_view', '=', 'balance')
|
|
])
|
|
active_leaves = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id),
|
|
('type', '=', 'remove'),
|
|
('holiday_status_id.leave_type', '=', 'sick'),
|
|
('holiday_status_id', '!=', rec.holiday_status_id.id),
|
|
('holiday_status_id.sickness_severity', '<',
|
|
rec.holiday_status_id.sickness_severity),
|
|
('state', 'not in', ('refuse', 'validate1', 'cancel'))
|
|
])
|
|
if sickness_leaves:
|
|
msg = ''
|
|
for sk in sickness_leaves:
|
|
if sk.remaining_leaves > 0:
|
|
days = sum(active_leaves.filtered(
|
|
lambda t: t.holiday_status_id.id == sk.holiday_status_id.id).mapped(
|
|
'number_of_days_temp')) or 0
|
|
if days < sk.remaining_leaves:
|
|
msg += sk.holiday_status_id.name + '\n'
|
|
if msg:
|
|
raise UserError(_('Sorry you can not request %s leave while you '
|
|
'still have available balance in the following leaves \n %s')
|
|
% (rec.holiday_status_id.name, msg))
|
|
|
|
###############chick other request same holiday type####
|
|
|
|
active_leaves_request = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id),
|
|
('type', '=', 'remove'),
|
|
('id', '!=', rec.id),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('state', 'not in', ('refuse', 'validate1', 'cancel'))
|
|
])
|
|
if active_leaves_request and rec.type == 'remove':
|
|
list_holiday = ''
|
|
sum_duration = 0.0
|
|
for lis in active_leaves_request:
|
|
list_holiday += lis.holiday_status_id.name + '\n'
|
|
sum_duration += lis.number_of_days_temp
|
|
#curnet_balance = rec.leave_balance - sum_duration
|
|
curnet_balance = rec.leave_balance_date - sum_duration
|
|
if list_holiday and rec.number_of_days_temp > curnet_balance:
|
|
raise UserError(
|
|
_('Sorry you can not Create Holiday Request while you still have available Holiday request '
|
|
'in state Not Approve or Refuse Same Type : \n %s') % list_holiday)
|
|
|
|
# check end of service
|
|
Module = self.env['ir.module.module'].sudo()
|
|
modules = Module.search([('state', '=', 'installed'), ('name', '=', 'hr_termination')])
|
|
if modules:
|
|
end_of_service_rec = self.env['hr.termination'].search(
|
|
[('employee_id', '=', rec.employee_id.id), ('state', 'not in', ('refused', 'draft'))])
|
|
if end_of_service_rec and rec.type == 'remove':
|
|
raise UserError(
|
|
_('Sorry ! you can not Create Holiday Request and there is an Employee Termination Request %s') % (
|
|
rec.employee_id.name))
|
|
|
|
@api.depends('holiday_status_id', 'date_from', 'date_to')
|
|
def _compute_leave_balance(self):
|
|
for rec in self:
|
|
balance = 0.0
|
|
if rec.employee_id and rec.holiday_status_id:
|
|
balance = self.env['hr.holidays'].search([('employee_id', '=', rec.employee_id.id), ('type', '=', 'add'),
|
|
('holiday_status_id', '=', rec.holiday_status_id.id),
|
|
('check_allocation_view', '=', 'balance')
|
|
], order='id desc', limit=1).remaining_leaves or 0.0
|
|
rec.leave_balance = balance
|
|
|
|
# chick missions for employee or holiday period employee replace
|
|
|
|
@api.constrains('replace_by', 'holiday_status_id', 'date_from', 'date_to','state')
|
|
def replace_by_not_holiday(self):
|
|
for rec in self:
|
|
if rec.type == 'remove':
|
|
Module = self.env['ir.module.module'].sudo()
|
|
modules = Module.search([('state', '=', 'installed'), ('name', '=', 'exp_official_mission')])
|
|
modules_permission = Module.search([('state', '=', 'installed'), ('name', '=', 'employee_requests')])
|
|
if modules and rec.holiday_status_id.mission_chick == False:
|
|
mission_dfrm = self.env['hr.official.mission'].search(
|
|
[('employee_ids.employee_id', '=', rec.employee_id.id),
|
|
('mission_type.special_hours', '!=', 'True'), ('state', '!=', 'refused'),
|
|
('employee_ids.date_from', '<=', rec.date_from),
|
|
('employee_ids.date_to', '>=', rec.date_from)], order='id desc',
|
|
limit=1)
|
|
if mission_dfrm:
|
|
raise UserError(
|
|
_('Sorry The Employee %s Actually On %s For this Period') % (
|
|
rec.employee_id.name, mission_dfrm.mission_type.name))
|
|
|
|
mission_dto = self.env['hr.official.mission'].search(
|
|
[('employee_ids.employee_id', '=', rec.employee_id.id),
|
|
('mission_type.special_hours', '!=', 'True'),
|
|
('state', '!=', 'refused'), ('employee_ids.date_from', '<=', rec.date_to),
|
|
('employee_ids.date_to', '>=', rec.date_to)], order='id desc', limit=1)
|
|
if mission_dto:
|
|
raise UserError(
|
|
_('Sorry The Employee %s Actually On %s For this Period') % (
|
|
rec.employee_id.name, mission_dto.mission_type.name))
|
|
|
|
mission_btw = self.env['hr.official.mission'].search(
|
|
[('employee_ids.employee_id', '=', rec.employee_id.id),
|
|
('mission_type.special_hours', '!=', 'True'),
|
|
('state', '!=', 'refused'), ('employee_ids.date_from', '>=', rec.date_from),
|
|
('employee_ids.date_from', '<=', rec.date_to)], order='id desc',
|
|
limit=1)
|
|
if mission_btw:
|
|
raise UserError(
|
|
_('Sorry The Employee %s Actually On %s For this Period') % (
|
|
rec.employee_id.name, mission_btw.mission_type.name))
|
|
|
|
mission_hours = self.env['hr.official.mission'].search(
|
|
[('employee_ids.employee_id', '=', rec.employee_id.id),
|
|
('mission_type.special_hours', '!=', 'True'),
|
|
('mission_type.duration_type', '=', 'hours'), ('state', '!=', 'refused'),
|
|
('date', '>=', rec.date_from), ('date', '<=', rec.date_to)], order='id desc', limit=1)
|
|
if mission_hours:
|
|
raise UserError(_('Sorry The Employee %s Actually On %s For this Period') %
|
|
(rec.employee_id.name, mission_hours.mission_type.name))
|
|
if modules_permission:
|
|
date_to = datetime.strptime(str(rec.date_to), DEFAULT_SERVER_DATETIME_FORMAT)
|
|
hour = datetime.strptime(str("23:59:00"), "%H:%M:%S").time()
|
|
date_to = str(datetime.combine(date_to, hour))
|
|
date_from = datetime.strftime(datetime.strptime(str(rec.date_from), DEFAULT_SERVER_DATETIME_FORMAT),
|
|
DEFAULT_SERVER_DATETIME_FORMAT)
|
|
clause_1 = ['&', ('date_to', '<=', date_to), ('date_to', '>=', date_from)]
|
|
clause_2 = ['&', ('date_from', '<=', date_to), ('date_from', '>=', date_from)]
|
|
clause_3 = ['&', ('date_from', '<=', date_from), ('date_to', '>=', date_to)]
|
|
final_case = [('employee_id', '=', rec.employee_id.id), ('state', '!=', 'refused'), '|',
|
|
'|'] + clause_1 + clause_2 + clause_3
|
|
permissions_day = self.env['hr.personal.permission'].search(final_case)
|
|
if permissions_day:
|
|
raise UserError(
|
|
_('Sorry The Employee %s Actually On Permission For this Period') % rec.employee_id.name)
|
|
|
|
if rec.replace_by:
|
|
if rec.state != 'validate1':
|
|
holiday_dfrm = self.env['hr.holidays'].search(
|
|
[('employee_id', '=', rec.replace_by.id), ('type', '=', 'remove'), ('state', '!=', 'refuse'),
|
|
('date_from', '<=', rec.date_from), ('date_to', '>=', rec.date_from)], order='id desc',
|
|
limit=1)
|
|
|
|
holiday_dto = self.env['hr.holidays'].search(
|
|
[('employee_id', '=', rec.replace_by.id), ('type', '=', 'remove'), ('state', '!=', 'refuse'),
|
|
('date_from', '<=', rec.date_to), ('date_to', '>=', rec.date_to)], order='id desc', limit=1)
|
|
|
|
holiday_btw = self.env['hr.holidays'].search(
|
|
[('employee_id', '=', rec.replace_by.id), ('type', '=', 'remove'), ('state', '!=', 'refuse'),
|
|
('date_from', '>=', rec.date_from), ('date_from', '<=', rec.date_to)], order='id desc',
|
|
limit=1)
|
|
|
|
if holiday_dfrm or holiday_dto or holiday_btw:
|
|
raise UserError(
|
|
_('Sorry The Replacement Employee %s Actually On Holiday For this Period') % rec.replace_by.name)
|
|
rec.delegate_acc = True
|
|
else:
|
|
rec.delegate_acc = False
|
|
|
|
def send_email_holiday(self, holiday, emp):
|
|
if holiday.visible_fields and holiday.remained_before > 0 and holiday.leave_type=='annual':
|
|
today = datetime.now().date()
|
|
today = datetime.strptime(str(today), "%Y-%m-%d").date()
|
|
years = today.year
|
|
last_date = datetime.strftime(today, "{0}-12-31".format(years))
|
|
last_date = datetime.strptime(str(last_date), "%Y-%m-%d").date()
|
|
start_days = last_date - timedelta(days=int(holiday.remained_before))
|
|
if today < last_date:
|
|
balance = self.env['hr.holidays'].search([('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', holiday.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], limit=1, order="id desc")
|
|
holi_balance = balance.remaining_leaves or 0.0
|
|
if today >= start_days < last_date:
|
|
if holi_balance > holiday.number_of_save_days:
|
|
if emp.user_id:
|
|
template = self.env.ref('hr_holidays_public.email_template_holiday_balance', False)
|
|
template.write({'email_to': emp.user_id.partner_id.email, 'lang': emp.user_id.lang,
|
|
'email_cc': '%s' % (self.env.company.hr_email)})
|
|
template.send_mail(balance.id)
|
|
if holiday.payslip_type == 'unpaid':
|
|
tomorrow = (fields.Datetime.from_string(fields.Datetime.now()) + timedelta(days=1)).strftime(
|
|
'%Y-%m-%d 00:00:00')
|
|
upcoming_leaves = self.env['hr.holidays'].search([('employee_id', '=', emp.id),
|
|
('type', '=', 'remove'),
|
|
('holiday_status_id', '=', holiday.id),
|
|
('state', '=', 'validate1'),
|
|
('date_from', '=', tomorrow)])
|
|
for up in upcoming_leaves:
|
|
template = self.env.ref('hr_holidays_public.unpaid_leave_email', False)
|
|
template.write({'lang': up.employee_id.parent_id.user_id.lang,
|
|
'subject': _('%s unpaid leave beginning') % (up.employee_id.name),
|
|
'email_to': up.employee_id.parent_id.work_email,
|
|
'email_cc': '%s, %s' % (self.env.company.hr_email, up.employee_id.work_email)
|
|
})
|
|
template.send_mail(up.id, force_send=True, raise_exception=False)
|
|
|
|
def remaining_leaves_of_day_by_date(self, employee_id, specific_date, item , is_years=False, is_month=False):
|
|
first_hiring_date = datetime.strptime(str(employee_id.first_hiring_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
specific_date = datetime.strptime(str(specific_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
working_years = relativedelta.relativedelta(specific_date, first_hiring_date).years
|
|
holiday_duration = 0
|
|
num_days = calendar.monthrange(specific_date.year, specific_date.month)[1]
|
|
|
|
for rec in item.duration_ids:
|
|
if rec.date_from <= working_years < rec.date_to:
|
|
holiday_duration = rec.duration
|
|
if is_years:
|
|
return holiday_duration
|
|
if is_month:
|
|
return holiday_duration / 12
|
|
return (holiday_duration / 12) / num_days
|
|
|
|
def calc_not_balance_annual_leave(self, emp, cron_date, is_month=False, is_year=False):
|
|
holiday_status = self.env['hr.holidays.status'].search([('company_id', '=', self.env.company.id)])
|
|
holiday_current_date_ids = self.env['hr.holidays'] \
|
|
.search(
|
|
[('employee_id', '=', emp.id), ('type', '=', 'remove'),
|
|
('date_from', '<=', str(cron_date)),
|
|
('holiday_status_id', 'in', holiday_status.filtered(lambda h: h.not_balance_annual_leave).ids),
|
|
('date_to', '>=', str(cron_date)), ('state', 'not in', ['refuse', 'cancel', 'draft'])])
|
|
return holiday_current_date_ids and 1 or 0
|
|
|
|
def reset_remaining_leaves(self , current_date, item, emp, already_exist , is_years):
|
|
if self.is_last_day_of_year(current_date):
|
|
dutartion = 0
|
|
this_year_balance = 0
|
|
if item.number_of_save_days > 0:
|
|
dutartion = item.number_of_save_days
|
|
if is_years:
|
|
open_year = datetime(current_date.year - 1, 12, 31).date()
|
|
days_diff = (current_date - open_year).days + 1
|
|
for index in range(1, days_diff):
|
|
open_year_date = open_year + timedelta(days=index)
|
|
this_year_balance += self.remaining_leaves_of_day_by_date(emp, str(open_year_date), item , is_month=False, is_years=False)
|
|
else:
|
|
years_save = item.number_of_years
|
|
if is_years:
|
|
years_save = item.number_of_years + 1
|
|
open_year = datetime(current_date.year - years_save, 12, 31).date()
|
|
days_diff = (current_date - open_year).days + 1
|
|
|
|
for index in range(1, days_diff):
|
|
open_year_date = open_year + timedelta(days=index)
|
|
dutartion += self.remaining_leaves_of_day_by_date(emp, str(open_year_date), item , is_month=False, is_years=False)
|
|
# fix fix
|
|
if already_exist.remaining_leaves - this_year_balance > dutartion:
|
|
dutartion = dutartion + this_year_balance
|
|
already_exist.write({'remaining_leaves': dutartion})
|
|
@api.model
|
|
def process_holidays_scheduler_queue(self):
|
|
current_date_run = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
employees = self.env['hr.employee'].search(
|
|
[('state', '=', 'open'), ('company_id', '=', self.env.company.id)]
|
|
).filtered(
|
|
lambda emp: emp.first_hiring_date
|
|
and emp.first_hiring_date <= current_date_run
|
|
and emp.contract_id
|
|
and emp.contract_id.emp_type in ('other', 'saudi', 'displaced', 'external', 'external2')
|
|
and (not emp.contract_id.date_end or emp.contract_id.date_end and emp.contract_id.date_end >= current_date_run))
|
|
holiday_status = self.env['hr.holidays.status'].search([('company_id', '=', self.env.company.id)])
|
|
for emp in employees:
|
|
already_exist = None
|
|
current_date = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
# current_date = datetime(2025, 12, 31).date()
|
|
first_hiring_date = datetime.strptime(str(emp.first_hiring_date), "%Y-%m-%d").date()
|
|
days = (current_date - first_hiring_date).days + 1
|
|
for item in holiday_status:
|
|
# current_date = datetime(2025, 12, 31).date()
|
|
if days < item.number_of_days:
|
|
continue
|
|
is_years , is_month = False , False
|
|
if item.balance_type == 'monthly':
|
|
is_month = True
|
|
if item.balance_type == 'yearly':
|
|
is_years = True # True
|
|
self.send_email_holiday(item, emp)
|
|
if item.leave_type == 'annual' and (item.emp_type == 'all' or item.emp_type == emp.contract_id.emp_type):
|
|
if item.leave_annual_type == 'open_balance': # or item.leave_annual_type == 'save_annual_year' and item.number_of_holidays_save_years <= 0:
|
|
already_exist = self.env['hr.holidays'].search([('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', item.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], limit=1, order="id desc")
|
|
create_open_balance = self.env['hr.holidays'].search([('holiday_status_id', '=', item.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], order="id desc")
|
|
if is_years:
|
|
current_date = current_date.replace(year=current_date.year , month=12, day=31)
|
|
current_date = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
if is_month:
|
|
if self.is_last_day_of_month(current_date):
|
|
current_date = current_date
|
|
else:
|
|
current_date = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
current_date = current_date.replace(day=1) - timedelta(days=1)
|
|
if not already_exist and len(create_open_balance) > 1 and item.leave_annual_type == 'open_balance':
|
|
# if not already_exist and item.leave_annual_type == 'open_balance':
|
|
balance_leaves = self.remaining_leaves_of_day_by_date(emp, str(first_hiring_date), item , is_month=False, is_years=False)
|
|
already_exist = self.env['hr.holidays'].create({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'employee_id': emp.id,
|
|
'holiday_status_id': item.id,
|
|
'check_allocation_view': 'balance',
|
|
'type': 'add',
|
|
'remaining_leaves': balance_leaves,
|
|
})
|
|
self.env['hr.inverse.holidays'].create(
|
|
{'holiday_id': already_exist.id, 'cron_run_date': first_hiring_date, 'balance_leaves':balance_leaves })
|
|
if already_exist:
|
|
if not already_exist.holiday_ids:
|
|
not_balance = 0
|
|
not_no_balance = 0
|
|
if not is_years and not is_month:
|
|
not_balance = self.remaining_leaves_of_day_by_date(emp, str(current_date), item , is_month=False, is_years=False)
|
|
not_no_balance = self.calc_not_balance_annual_leave(emp, current_date) * not_balance
|
|
already_exist.write({
|
|
'remaining_leaves': already_exist.remaining_leaves + not_balance - not_no_balance,
|
|
})
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': already_exist.id, 'cron_run_date': current_date, 'balance_leaves':not_balance, 'no_balance_leaves':not_no_balance })
|
|
holiday = already_exist.holiday_ids[-1]
|
|
if not holiday.cron_run_date:
|
|
not_balance = 0
|
|
not_no_balance = 0
|
|
if not is_years and not is_month:
|
|
not_balance = self.remaining_leaves_of_day_by_date(emp, str(current_date), item , is_month=False, is_years=False)
|
|
not_no_balance = self.calc_not_balance_annual_leave(emp, current_date) * not_balance
|
|
already_exist.write({
|
|
'remaining_leaves': already_exist.remaining_leaves + not_balance - not_no_balance,
|
|
})
|
|
holiday.write({'cron_run_date': current_date})
|
|
holiday_cron_run_date = datetime.strptime(str(holiday.cron_run_date ), "%Y-%m-%d").date()
|
|
if current_date > holiday_cron_run_date or is_years:
|
|
holiday_cron_run_date = datetime.strptime(str(holiday.cron_run_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
current_date = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
if holiday_cron_run_date >= current_date:
|
|
continue
|
|
delta = current_date - holiday_cron_run_date # timedelta
|
|
months_date = holiday_cron_run_date
|
|
months = (current_date.year - months_date.year) * 12 + (current_date.month - months_date.month) + 1
|
|
item_values = []
|
|
year = current_date.year - holiday_cron_run_date.year
|
|
range_val = []
|
|
range_val = range(1, delta.days + 1)
|
|
if is_years:
|
|
range_val = range(1, year + 1)
|
|
end_day = (holiday_cron_run_date.replace(day=1) + relativedelta.relativedelta(months=1)) - timedelta(days=1)
|
|
end_date_cron = (end_day - holiday_cron_run_date).days
|
|
if end_date_cron > 0:
|
|
no_year_balance , year_balance = 0, 0
|
|
for i in range(1, end_date_cron + 1):
|
|
cala_date = holiday_cron_run_date + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + year_balance - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})]})
|
|
complete_month = datetime.strptime(str(already_exist.holiday_ids[-1].cron_run_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
cron_run_date_y = 12 - complete_month.month
|
|
for x in range(1 , cron_run_date_y + 1):
|
|
no_year_balance , year_balance = 0, 0
|
|
end_day = (complete_month.replace(day=1) + relativedelta.relativedelta(months=x + 1)) - timedelta(days=1)
|
|
y = end_day.replace(day=1)
|
|
for i in range(0, (end_day - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': (already_exist.remaining_leaves + year_balance) - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})]})
|
|
if is_month:
|
|
end_day = (holiday_cron_run_date.replace(day=1) + relativedelta.relativedelta(months=1)) - timedelta(days=1)
|
|
end_date_cron = (end_day - holiday_cron_run_date).days
|
|
if end_date_cron > 0:
|
|
no_year_balance , year_balance = 0, 0
|
|
for i in range(1, end_date_cron + 1):
|
|
cala_date = holiday_cron_run_date + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + (year_balance - no_year_balance),
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})]})
|
|
range_val = range(1, months)
|
|
for i in range_val:
|
|
no_year_balance , year_balance = 0, 0
|
|
cron_date = holiday_cron_run_date + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cron_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cron_date) * balance)
|
|
year_balance = year_balance + balance
|
|
if is_years:
|
|
no_year_balance , year_balance = 0, 0
|
|
cron_date = holiday_cron_run_date.replace(year=holiday_cron_run_date.year + i , month=12, day=31)
|
|
y = cron_date.replace(year=cron_date.year - 1 , month=12, day=31)
|
|
for i in range(1, (cron_date - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
if is_month:
|
|
cron_date = (holiday_cron_run_date.replace(day=1) + relativedelta.relativedelta(months=i + 1)) - timedelta(days=1)
|
|
y = cron_date.replace(day=1)
|
|
no_year_balance , year_balance = 0, 0
|
|
for i in range(0, (cron_date - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
item_values.append({'cron_run_date': cron_date, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves':(already_exist.remaining_leaves + year_balance) - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [(0, 0, item) for item in item_values]})
|
|
# TO DO cala dutartion if val days_save == 0 or set days_save BY ABUZAR
|
|
# DONE call end method ---
|
|
# self.reset_remaining_leaves(current_date, item, emp, already_exist,is_years)
|
|
elif item.leave_annual_type == 'save_annual_year':
|
|
# and item.number_of_holidays_save_years > 0:
|
|
current_date = (datetime.utcnow() + timedelta(hours=3)).date() # replace with: current_date_time++
|
|
# current_date = datetime(2025, 12, 31).date()
|
|
current_date_val = current_date.year
|
|
old_year_date_val = current_date.year - 1 # previous year
|
|
end_of_save_year = datetime.strftime(current_date, "{0}-12-31 23:59:59".format(old_year_date_val)) # previous_year_end
|
|
end_of_save_year_value = datetime.strptime(str(end_of_save_year), "%Y-%m-%d 23:59:59").date() # previous_year_end Date
|
|
start_of_new_year = datetime.strftime(first_hiring_date, "{0}-01-01".format(current_date_val))
|
|
start_of_save_year_value = datetime.strptime(str(start_of_new_year), "%Y-%m-%d").date() # start of new year
|
|
working_days_old = (end_of_save_year_value - first_hiring_date).days + 1 # working days till end prv year عدد ايام عمل الموظف الي السنة الحالية
|
|
employee_working_years = item.number_of_holidays_save_years * 365 # balance of prev years to give?
|
|
|
|
if working_days_old >= employee_working_years:
|
|
|
|
remaining_leaves_of_day = self.remaining_leaves_of_day_by_date(emp, str(current_date), item , is_month=False, is_years=False)
|
|
already_exist = self.env['hr.holidays'].search([('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', item.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], limit=1, order="id desc")
|
|
if already_exist:
|
|
"""
|
|
Important note:
|
|
we are no longer saving years but carrying balance from year to the next only;
|
|
according to Bakri Request done by Yousra.
|
|
"""
|
|
if is_years:
|
|
current_date = current_date.replace(year=current_date.year , month=12, day=31)
|
|
current_date = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
if is_month:
|
|
if self.is_last_day_of_month(current_date):
|
|
current_date = current_date
|
|
else:
|
|
current_date = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
current_date = current_date.replace(day=1) - timedelta(days=1)
|
|
if not already_exist.holiday_ids:
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': already_exist.id, 'cron_run_date': current_date, })
|
|
holiday = already_exist.holiday_ids[-1]
|
|
if holiday.cron_run_date:
|
|
cron_run_date1 = datetime.strptime(str(holiday.cron_run_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
current_date = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
if cron_run_date1 > current_date:
|
|
continue
|
|
delta = current_date - cron_run_date1 # timedelta
|
|
months_date = cron_run_date1
|
|
year = current_date.year - cron_run_date1.year
|
|
months = (current_date.year - months_date.year) * 12 + (current_date.month - months_date.month) + 1
|
|
item_values = []
|
|
range_val = []
|
|
range_val = range(1, delta.days + 1)
|
|
if is_years:
|
|
if year <= item.number_of_holidays_save_years:
|
|
range_val = range(1, year + 1)
|
|
end_day = (cron_run_date1.replace(day=1) + relativedelta.relativedelta(months=1)) - timedelta(days=1)
|
|
end_date_cron = (end_day - cron_run_date1).days
|
|
if end_date_cron > 0:
|
|
no_year_balance , year_balance = 0, 0
|
|
for i in range(1, end_date_cron + 1):
|
|
cala_date = cron_run_date1 + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + year_balance - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})]})
|
|
|
|
complete_month = datetime.strptime(str(already_exist.holiday_ids[-1].cron_run_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
cron_run_date_y = 12 - complete_month.month
|
|
for x in range(1, cron_run_date_y + 1):
|
|
no_year_balance , year_balance = 0, 0
|
|
end_day = (complete_month.replace(day=1) + relativedelta.relativedelta(months=x + 1)) - timedelta(days=1)
|
|
y = end_day.replace(day=1)
|
|
for i in range(0, (end_day - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': (already_exist.remaining_leaves + year_balance) - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})]})
|
|
else:
|
|
cron_run_date1 = current_date.replace(year=current_date.year - item.number_of_holidays_save_years, month=12, day=31)
|
|
range_val = range(0, item.number_of_holidays_save_years + 1)
|
|
if is_month:
|
|
if year <= item.number_of_holidays_save_years:
|
|
end_day = (cron_run_date1.replace(day=1) + relativedelta.relativedelta(months=1)) - timedelta(days=1)
|
|
end_date_cron = (end_day - cron_run_date1).days
|
|
if end_date_cron > 0:
|
|
no_year_balance , year_balance = 0, 0
|
|
for i in range(1, end_date_cron + 1):
|
|
cala_date = cron_run_date1 + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + (year_balance - no_year_balance),
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})]})
|
|
range_val = range(1, months)
|
|
else:
|
|
cron_run_date1 = current_date.replace(year=current_date.year - item.number_of_holidays_save_years , month=1, day=31)
|
|
range_val = range(0, (item.number_of_holidays_save_years) * 12 + (current_date.month - cron_run_date1.month) + 1)
|
|
for i in range_val:
|
|
no_year_balance , year_balance = 0, 0
|
|
cron_date = cron_run_date1 + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cron_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cron_date) * balance)
|
|
year_balance = year_balance + balance
|
|
if is_years:
|
|
no_year_balance , year_balance = 0, 0
|
|
cron_date = cron_run_date1.replace(year=cron_run_date1.year + i , month=12, day=31)
|
|
y = cron_date.replace(year=cron_date.year - 1 , month=12, day=31)
|
|
for i in range(1, (cron_date - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
if is_month:
|
|
cron_date = (cron_run_date1.replace(day=1) + relativedelta.relativedelta(months=i + 1)) - timedelta(days=1)
|
|
y = cron_date.replace(day=1)
|
|
no_year_balance , year_balance = 0, 0
|
|
for i in range(0, (cron_date - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
item_values.append({'cron_run_date': cron_date, 'balance_leaves':year_balance, 'no_balance_leaves':no_year_balance})
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves':(already_exist.remaining_leaves + year_balance) - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [(0, 0, item) for item in item_values]})
|
|
else:
|
|
holiday.write({'cron_run_date': current_date, 'balance_leaves':remaining_leaves_of_day })
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + float(remaining_leaves_of_day),
|
|
})
|
|
else:
|
|
year_balance = 0
|
|
no_year_balance = 0
|
|
start_of_save_year_value = start_of_save_year_value.replace(year=(current_date.year - item.number_of_holidays_save_years))
|
|
working_days_new = ((current_date - start_of_save_year_value).days + 1)
|
|
if not is_years and not is_month:
|
|
for index in range(0, working_days_new):
|
|
current_date = start_of_save_year_value + timedelta(days=index)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(current_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, current_date) * balance)
|
|
year_balance = year_balance + balance
|
|
if is_years:
|
|
year_balance = 0
|
|
remaining_leaves_of_day = 0
|
|
current_date = current_date.replace(year=current_date.year , month=12, day=31)
|
|
year_new = (current_date.year - first_hiring_date.year)
|
|
year_new = min(year_new, item.number_of_holidays_save_years) + 1
|
|
y = current_date.replace(year=current_date.year - year_new , month=12, day=31)
|
|
for i in range(1, (current_date - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
if is_month:
|
|
year_balance = 0
|
|
remaining_leaves_of_day = 0
|
|
current_date = (current_date.replace(day=1) + relativedelta.relativedelta(months=0)) - timedelta(days=1)
|
|
first_hiring_date = datetime.strptime(str(emp.first_hiring_date), "%Y-%m-%d").date()
|
|
remaining_leaves_of_day = self.remaining_leaves_of_day_by_date(emp, str(current_date), item , is_month=is_month, is_years=is_years)
|
|
months_date = first_hiring_date
|
|
months_new = (current_date.year - months_date.year) * 12 + current_date.month - months_date.month
|
|
save_month = item.number_of_holidays_save_years * 12
|
|
min_months = min(months_new, save_month)
|
|
if months_new > save_month:
|
|
last_date = current_date.replace(year=current_date.year - 1, month=12, day=31)
|
|
open_years_month = (current_date.year - last_date.year) * 12 + current_date.month - last_date.month
|
|
min_months = min_months + open_years_month
|
|
y = (current_date - relativedelta.relativedelta(months=min_months - 1)).replace(day=1)
|
|
for i in range(0, (current_date - y).days + 1):
|
|
cala_date = y + timedelta(days=i)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(cala_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, cala_date) * balance)
|
|
year_balance = year_balance + balance
|
|
already_exist = self.env['hr.holidays'].create({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'employee_id': emp.id,
|
|
'holiday_status_id': item.id,
|
|
'check_allocation_view': 'balance',
|
|
'type': 'add',
|
|
'remaining_leaves': year_balance - no_year_balance,
|
|
})
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': already_exist.id, 'cron_run_date': current_date, 'balance_leaves':year_balance , 'no_balance_leaves':no_year_balance })
|
|
else:
|
|
if is_years:
|
|
current_date = current_date.replace(year=current_date.year , month=12, day=31)
|
|
current_date = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
if is_month:
|
|
current_date = (current_date.replace(day=1) + relativedelta.relativedelta(months=0)) - timedelta(days=1)
|
|
days_of_new_employee = (current_date - first_hiring_date).days + 1
|
|
if days_of_new_employee >= item.number_of_days:
|
|
already_exist = self.env['hr.holidays'].search([('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', item.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], limit=1, order="id desc")
|
|
if not already_exist:
|
|
balance_leaves = self.remaining_leaves_of_day_by_date(emp, str(first_hiring_date), item , is_month=False, is_years=False)
|
|
already_exist = self.env['hr.holidays'].create({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'employee_id': emp.id,
|
|
'holiday_status_id': item.id,
|
|
'check_allocation_view': 'balance',
|
|
'type': 'add',
|
|
'remaining_leaves': balance_leaves,
|
|
})
|
|
self.env['hr.inverse.holidays'].create(
|
|
{'holiday_id': already_exist.id, 'cron_run_date': first_hiring_date, 'balance_leaves':balance_leaves })
|
|
if not is_years and not is_month:
|
|
working_days_new = ((current_date - first_hiring_date).days) + 1
|
|
year_balance = already_exist.remaining_leaves or 0
|
|
no_balance = 0
|
|
for index in range(1, working_days_new):
|
|
current_date = first_hiring_date + timedelta(days=index)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(current_date), item , is_month=False, is_years=False)
|
|
no_balance = no_balance + self.calc_not_balance_annual_leave(emp, current_date) * balance
|
|
year_balance = year_balance + balance
|
|
already_exist.holiday_ids[-1].cron_run_date = current_date
|
|
already_exist.holiday_ids[-1].balance_leaves = year_balance
|
|
already_exist.holiday_ids[-1].no_balance_leaves = no_balance
|
|
already_exist.remaining_leaves = year_balance - no_balance
|
|
if already_exist:
|
|
if not already_exist.holiday_ids:
|
|
current_date_new = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
balance_leaves = self.remaining_leaves_of_day_by_date(emp, str(current_date_new), item , is_month=False, is_years=False)
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': already_exist.id, 'cron_run_date': current_date_new, 'balance_leaves':balance_leaves })
|
|
holiday.write({'cron_run_date': current_date, })
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + balance_leaves,
|
|
})
|
|
else:
|
|
holiday = already_exist.holiday_ids[-1]
|
|
if holiday.cron_run_date:
|
|
cron_run_date1 = datetime.strptime(str(holiday.cron_run_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
if cron_run_date1 >= current_date:
|
|
continue
|
|
current_date1 = datetime.strptime(str(current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
delta = current_date1 - cron_run_date1 # timedelta
|
|
months_date = cron_run_date1
|
|
months = (current_date1.year - months_date.year) * 12 + current_date1.month - months_date.month
|
|
year = current_date1.year - cron_run_date1.year
|
|
item_values = []
|
|
range_val = []
|
|
range_val = range(1, delta.days + 1)
|
|
if is_years:
|
|
range_val = range(1, year + 1)
|
|
end_day = (cron_run_date1.replace(day=1) + relativedelta.relativedelta(months=1)) - timedelta(days=1)
|
|
end_date_cron = (end_day - cron_run_date1).days
|
|
if end_date_cron > 0:
|
|
balance_leaves = 0
|
|
no_year_balance = 0
|
|
start_date = (end_day - cron_run_date1).days + 1
|
|
for index in range(1, start_date):
|
|
calc_date = cron_run_date1 + timedelta(days=index)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(calc_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, calc_date) * balance)
|
|
balance_leaves = balance_leaves + balance
|
|
# balance_leaves = balance_leaves - no_year_balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + balance_leaves - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':balance_leaves, 'no_balance_leaves':no_year_balance})]})
|
|
complete_month = datetime.strptime(str(already_exist.holiday_ids[-1].cron_run_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
cron_run_date_y = 12 - complete_month.month
|
|
for x in range(1, cron_run_date_y + 1):
|
|
end_day = (complete_month.replace(day=1) + relativedelta.relativedelta(months=x + 1)) - timedelta(days=1)
|
|
balance_leaves = 0
|
|
no_year_balance = 0
|
|
start_date = (end_day - end_day.replace(day=1)).days + 1
|
|
for index in range(0, start_date):
|
|
calc_date = end_day.replace(day=1) + timedelta(days=index)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(calc_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, calc_date) * balance)
|
|
balance_leaves = balance_leaves + balance
|
|
# balance_leaves = balance_leaves - no_year_balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + balance_leaves - no_year_balance
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({\
|
|
'cron_run_date': end_day, \
|
|
'balance_leaves':balance_leaves, \
|
|
'no_balance_leaves':no_year_balance})]})
|
|
if is_month:
|
|
balance_leaves = 0
|
|
no_year_balance = 0
|
|
end_day = (cron_run_date1.replace(day=1) + relativedelta.relativedelta(months=1)) - timedelta(days=1)
|
|
end_date_cron = (end_day - cron_run_date1).days
|
|
if end_date_cron > 0:
|
|
for index in range(1, end_date_cron + 1):
|
|
calc_date = cron_run_date1 + timedelta(days=index)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(calc_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, calc_date) * balance)
|
|
balance_leaves = balance_leaves + balance
|
|
# balance_leaves = balance_leaves - no_year_balance
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + balance_leaves - no_year_balance,
|
|
})
|
|
already_exist.write({'holiday_ids': [fields.Command.create({'cron_run_date': end_day, 'balance_leaves':balance_leaves, 'no_balance_leaves':no_year_balance})]})
|
|
range_val = range(1, months + 1)
|
|
for i in range_val:
|
|
cron_date = cron_run_date1 + timedelta(days=i)
|
|
balance_leaves = self.remaining_leaves_of_day_by_date(emp, str(cron_date), item , is_month=False, is_years=False)
|
|
no_year_balance = self.calc_not_balance_annual_leave(emp, cron_date) * balance_leaves
|
|
if is_years:
|
|
cron_date = cron_run_date1.replace(year=cron_run_date1.year + i , month=12, day=31)
|
|
if is_month:
|
|
cron_date = (cron_run_date1.replace(day=1) + relativedelta.relativedelta(months=i + 1)) - timedelta(days=1)
|
|
if current_date >= cron_date:
|
|
if is_month:
|
|
balance_leaves = 0
|
|
no_year_balance = 0
|
|
start_date = cron_date.replace(day=1)
|
|
start_days = (cron_date - cron_date.replace(day=1)).days + 1
|
|
for index in range(0, start_days):
|
|
calc_date = start_date + timedelta(days=index)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(calc_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, calc_date) * balance)
|
|
balance_leaves = balance_leaves + balance
|
|
if is_years:
|
|
balance_leaves = 0
|
|
no_year_balance = 0
|
|
start_date = cron_date.replace(year=cron_date.year - 1)
|
|
start_date_number = (cron_date - start_date).days + 1
|
|
for index in range(1, start_date_number):
|
|
calc_date = start_date + timedelta(days=index)
|
|
balance = self.remaining_leaves_of_day_by_date(emp, str(calc_date), item , is_month=False, is_years=False)
|
|
no_year_balance = no_year_balance + (self.calc_not_balance_annual_leave(emp, calc_date) * balance)
|
|
balance_leaves = balance_leaves + balance
|
|
remaining_leaves_of_day = balance_leaves - no_year_balance
|
|
item_values.append({ 'cron_run_date': cron_date, 'balance_leaves':balance_leaves, 'no_balance_leaves':no_year_balance})
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': already_exist.remaining_leaves + float(balance_leaves - no_year_balance),
|
|
})
|
|
already_exist.write({'holiday_ids': [(0, 0, item) for item in item_values]})
|
|
else:
|
|
current_date_new = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
holiday.write({'cron_run_date': current_date_new})
|
|
self.reset_remaining_leaves(current_date, item, emp, already_exist, is_years)
|
|
|
|
elif item.leave_type != 'annual' and (item.gender == 'both' or item.gender == emp.gender):
|
|
if days >= item.number_of_days:
|
|
current_date = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
already_exist = self.env['hr.holidays'].search([('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', item.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], limit=1, order="id desc")
|
|
remaining_leaves = item.duration
|
|
if already_exist:
|
|
# Renewal Balance of Sick Leave
|
|
self.renewal_balance_sick_leave(emp, item, already_exist, current_date)
|
|
# Renewal Balance of Unpaid Leave
|
|
self.renewal_balance_unpaid_leave(emp, item, already_exist, current_date)
|
|
# Renewal Balance of Last Day OF Year
|
|
# leave_type = ['exam','death'] # Just set type leave in list add new Leave
|
|
self.renewal_balance_leaves_last_day_of_year(current_date, item, already_exist , emp)
|
|
# Renewal Balance of daily
|
|
#renewal_daily_leave_type = ['death', 'new_baby'] # Just set type leave in list add new Leave
|
|
self.renewal_balance_leaves_daily(current_date, item, already_exist )
|
|
else:
|
|
hr_holidays = self.env['hr.holidays'].create({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'employee_id': emp.id,
|
|
'holiday_status_id': item.id,
|
|
'remaining_leaves': remaining_leaves,
|
|
'check_allocation_view': 'balance',
|
|
'type': 'add'
|
|
})
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': hr_holidays.id,
|
|
'cron_run_date': current_date,
|
|
'balance_leaves':remaining_leaves})
|
|
|
|
def is_last_day_of_month(self, date):
|
|
# Get the last day of the month for the given date
|
|
last_day_of_month = (date.replace(day=1) \
|
|
.replace(month=date.month % 12 + 1, day=1) \
|
|
-timedelta(days=1)).day
|
|
# Return True if the given date is the last day of the month, False otherwise
|
|
return date.day == last_day_of_month
|
|
|
|
def is_last_day_of_year(self, date):
|
|
# Get the last day of the year for the given date
|
|
last_day_of_year = datetime(date.year, 12, 31).date()
|
|
# Check if the given date is the same as the last day of the year
|
|
return date == last_day_of_year
|
|
|
|
def renewal_balance_leaves_daily(self , current_date, item, already_exist ):
|
|
renewal_daily_leave_type = ['death', 'new_baby'] # Just set type leave in list add new Leave
|
|
if (item.leave_type in renewal_daily_leave_type) and (already_exist.remaining_leaves != item.duration):
|
|
duration = item.duration
|
|
already_exist.write({'remaining_leaves': duration })
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': already_exist.id,
|
|
'cron_run_date': current_date,
|
|
'balance_leaves':duration})
|
|
|
|
def renewal_balance_leaves_last_day_of_year(self , current_date, item, already_exist , emp):
|
|
leave_type = ['exam', 'marriage','parental','emergency']#'death'] # Just set type leave in list add new Leave
|
|
if (item.leave_type in leave_type) and (already_exist.remaining_leaves < item.duration):
|
|
duration = item.duration # >= already_exist.remaining_leaves and item.duration or already_exist.remaining_leaves
|
|
# current_date = datetime(2024, 12, 31).date()
|
|
|
|
cron_run_date = current_date
|
|
if already_exist.holiday_ids:
|
|
holiday = already_exist.holiday_ids[-1]
|
|
cron_run_date = datetime.strptime(str(holiday.cron_run_date or current_date), DEFAULT_SERVER_DATE_FORMAT).date()
|
|
current_date = current_date + timedelta(days=1)
|
|
current_date = current_date.replace(year=current_date.year , month=12, day=31) # last day curent year
|
|
start_date = current_date.replace(day=1).replace(month=1)
|
|
|
|
domain = [
|
|
('employee_id', '=', emp.id),
|
|
('type', '=', 'remove'),
|
|
('date_from', '<=', str(current_date)),
|
|
('date_to', '>=', str(start_date)),
|
|
('state', 'in', ['validate1']), # Added comma here
|
|
('holiday_status_id', '=', item.id), # Fixed typo in the variable name
|
|
]
|
|
diff_years = current_date.year - cron_run_date.year
|
|
holiday_current_date_ids = self.env['hr.holidays'].read_group(domain, ['date_from', 'number_of_days'], ['date_from:year'])
|
|
if (diff_years >= 0) and (current_date > cron_run_date):
|
|
days = (current_date - start_date).days + 1
|
|
total_day = 0
|
|
if holiday_current_date_ids:
|
|
for day in range(0, days):
|
|
calc_date = start_date + timedelta(days=day)
|
|
holiday_current_date_ids = self.env['hr.holidays'].search([
|
|
('employee_id', '=', emp.id),
|
|
('type', '=', 'remove'),
|
|
('date_from', '<=', str(calc_date)),
|
|
('date_to', '>=', str(calc_date)),
|
|
('state', 'in', ['validate1']), # Added comma here
|
|
('holiday_status_id', '=', item.id), # Fixed typo in the variable name
|
|
])
|
|
if holiday_current_date_ids:
|
|
total_day = total_day + 1
|
|
remaining_leaves = duration - total_day
|
|
already_exist.write({'remaining_leaves': remaining_leaves > 0 and remaining_leaves or 0 })
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': already_exist.id,
|
|
'cron_run_date': current_date,
|
|
'balance_leaves':duration})
|
|
|
|
def renewal_balance_sick_leave(self, emp, item, already_exist, current_date):
|
|
if item.leave_type != 'sick':
|
|
return True
|
|
# current_date = datetime(2024, 11, 23).date()
|
|
while True:
|
|
# old_sick_leave = self._get_latest_approved_leave(emp, item)
|
|
|
|
# if not old_sick_leave:
|
|
# break
|
|
cron_run_date = emp.first_hiring_date
|
|
if already_exist.holiday_ids:
|
|
cron_run_date = already_exist.holiday_ids[-1].cron_run_date
|
|
|
|
old_sick_leave = self._get_latest_approved_leave_after_date(emp, item, cron_run_date)
|
|
|
|
if not old_sick_leave:
|
|
break
|
|
|
|
holiday_id = old_sick_leave[-1]
|
|
date_from = fields.Date.from_string(holiday_id.date_from)
|
|
next_years = date_from.replace(year=date_from.year + 1)
|
|
number_of_years = relativedelta.relativedelta(current_date, date_from).years
|
|
if number_of_years >= 1:
|
|
remaining_leaves = self._calculate_remaining_leaves(emp , item, next_years)
|
|
self._create_inverse_holiday(already_exist, next_years,remaining_leaves)
|
|
already_exist.write({
|
|
'name': f'Yearly Allocation of {item.name} Leaves',
|
|
'remaining_leaves': remaining_leaves,
|
|
})
|
|
else:
|
|
break
|
|
|
|
return True
|
|
|
|
def _get_latest_approved_leave(self, emp, item):
|
|
domain = [
|
|
('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', item.id),
|
|
('type', '=', 'remove'),
|
|
('state', 'in', ['validate1'])
|
|
]
|
|
return self.env['hr.holidays'].search(domain, limit=1, order="date_from desc")
|
|
|
|
def _get_latest_approved_leave_after_date(self, emp, item, date):
|
|
domain = [
|
|
('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', item.id),
|
|
('type', '=', 'remove'),
|
|
('state', 'in', ['validate1']),
|
|
('date_from', '>', str(date))
|
|
]
|
|
return self.env['hr.holidays'].search(domain, order="date_from desc")
|
|
|
|
def _create_inverse_holiday(self, already_exist, next_years,balance):
|
|
self.env['hr.inverse.holidays'].create({
|
|
'holiday_id': already_exist.id,
|
|
'cron_run_date': next_years,
|
|
'balance_leaves': balance,
|
|
|
|
})
|
|
|
|
def _calculate_remaining_leaves(self, emp,item, next_years):
|
|
domain = [
|
|
('employee_id', '=', emp.id),
|
|
('holiday_status_id', '=', item.id),
|
|
('type', '=', 'remove'),
|
|
('state', 'in', ['validate1']),
|
|
('date_from', '<=', str(next_years.replace(year=next_years.year + 1))),
|
|
('date_from', '>', str(next_years))
|
|
]
|
|
old_sick_leave = self.env['hr.holidays'].search(domain, order="date_from desc")
|
|
return float(item.duration) - sum(old_sick_leave.mapped('number_of_days_temp'))
|
|
|
|
def renewal_balance_unpaid_leave(self, emp, item, already_exist, current_date):
|
|
if item.type_unpaid == 'termination':
|
|
if already_exist.holiday_ids:
|
|
last_update_date = datetime.strptime(str(already_exist.holiday_ids[-1].cron_run_date),
|
|
DEFAULT_SERVER_DATE_FORMAT)
|
|
next_run_date = current_date.replace(year=last_update_date.year + item.period_giving_balance)
|
|
if current_date >= next_run_date:
|
|
already_exist.write({
|
|
'name': 'Yearly Allocation of ' + item.name + ' Leaves',
|
|
'remaining_leaves': float(item.duration),
|
|
})
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': already_exist.id,
|
|
'cron_run_date': current_date, })
|
|
return True
|
|
|
|
@api.onchange('employee_id')
|
|
def _get_leaves_ids(self):
|
|
for item in self:
|
|
item.holiday_status_id = False
|
|
genders, emp_types, durations = ['both', ], ['all', ], ['all', ]
|
|
genders.append(item.employee_id.gender)
|
|
emp_types.append(item.employee_type)
|
|
domain = [('active', '=', True), ('gender', 'in', genders), ('emp_type', 'in', emp_types)]
|
|
if item.employee_id.contract_id.contract_description == 'permanent':
|
|
durations.append(item.employee_id.contract_id.contract_duration)
|
|
domain += [('contract_duration', 'in', durations)]
|
|
return {'domain': {'holiday_status_id': [('id', 'in', self.env['hr.holidays.status'].search(domain).ids)]}}
|
|
|
|
def draft_state(self):
|
|
for item in self:
|
|
item._remove_followers_holiday()
|
|
have_cancel_request = self.env['leave.cancellation'].search([('leave_request_id', '=', item.id),
|
|
('state', 'not in', ('draft', 'refuse'))])
|
|
have_return_from_leave = self.env['return.from.leave'].search(
|
|
[('leave_request_id', '=', item.id), ('state', 'not in', ('draft', 'refuse'))])
|
|
if have_cancel_request:
|
|
raise UserError(_("You can't set this request to draft as it's already have a cancel request"))
|
|
if have_return_from_leave:
|
|
raise UserError(_("You can't set this request to draft as it's already have a return from "
|
|
"leave request"))
|
|
|
|
Module = self.env['ir.module.module'].sudo()
|
|
modules_reconcile = Module.search([('state', '=', 'installed'), ('name', '=', 'exp_payroll_loans')])
|
|
if modules_reconcile:
|
|
have_reconcile_leaves = self.env['reconcile.leaves'].search(
|
|
[('yearly_vacation', '=', item.id), ('state', 'not in', ('draft', 'refuse'))])
|
|
|
|
if have_reconcile_leaves:
|
|
raise ValidationError(
|
|
_("You can't set this request to draft as it's already have Reconcile Holiday"))
|
|
|
|
if item.state == 'validate1':
|
|
item.reconcile_holiday_balance()
|
|
item.remove_delegated_access()
|
|
item.write({'state': 'draft','request_done':False})
|
|
item.call_cron_function()
|
|
self.check_allocation_balance_annual('addition')
|
|
# if item.state == 'validate1':
|
|
# self.reconcile_holiday_balance()
|
|
self.write({'state': 'draft'})
|
|
|
|
def confirm(self):
|
|
for item in self:
|
|
self._compute_leave_balance()
|
|
item._remove_followers_holiday()
|
|
## ticket allowance per year ###
|
|
if item.issuing_ticket == 'yes':
|
|
current_date = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
if item.employee_id.contract_id.hiring_date:
|
|
employee_date = datetime.strptime(str(item.employee_id.contract_id.hiring_date), "%Y-%m-%d").date()
|
|
else:
|
|
employee_date = datetime.strptime(str(item.employee_id.contract_id.date_start), "%Y-%m-%d").date()
|
|
last_ticket = self.env['hr.ticket.request'].search(
|
|
[('employee_id', '=', item.employee_id.id), ('state', 'not in', ['draft', 'refuse'])],
|
|
order='id desc', limit=1)
|
|
if last_ticket:
|
|
last_ticket_date = datetime.strptime(str(last_ticket.request_date), "%Y-%m-%d").date()
|
|
dif2 = relativedelta.relativedelta(current_date, last_ticket_date)
|
|
dif_ticket_date = dif2.years * 12 + dif2.months
|
|
if dif_ticket_date < item.employee_id.contract_id.period_ticket:
|
|
raise UserError(_(
|
|
'Sorry The employee can not be given a ticket allowance until contractual year after the previous ticket'))
|
|
|
|
dif1 = relativedelta.relativedelta(current_date, employee_date)
|
|
dif_employee_date = dif1.years * 12 + dif1.months
|
|
if dif_employee_date < item.employee_id.contract_id.period_ticket and item.type == 'remove':
|
|
raise UserError(_(
|
|
'Sorry The employee can not be given a ticket allowance unless a contractual year has been established'))
|
|
|
|
if item.issuing_deliver_custdy == 'yes' and item.type == 'remove':
|
|
Module = self.env['ir.module.module'].sudo()
|
|
emp_modules = Module.search([('state', '=', 'installed'), ('name', '=', 'exp_employee_custody')])
|
|
petty_cash_modules = Module.search(
|
|
[('state', '=', 'installed'), ('name', '=', 'hr_expense_petty_cash')])
|
|
if emp_modules:
|
|
custody_ids = self.env['custom.employee.custody'].search(
|
|
[('employee_id', '=', item.employee_id.id),
|
|
('state', 'not in', ['done', 'refuse', 'draft'])]).ids
|
|
if custody_ids:
|
|
raise UserError(_(
|
|
'Sorry employee have custody he must deliver his custody to take this request'))
|
|
if petty_cash_modules:
|
|
payment_ids = self.env['petty.cash'].search(
|
|
[('partner_id', '=', self.employee_id.user_id.partner_id.id),
|
|
('state', 'in', ['submit', 'direct', 'fm', 'ceo', 'accepted', 'validate'])]).ids
|
|
|
|
if payment_ids:
|
|
raise UserError(_(
|
|
'Sorry employee have custody he must deliver his custody to take this request'))
|
|
if item.holiday_status_id.attach_chick == True and item.type == 'remove':
|
|
if item.attach_ids:
|
|
for rec in item.attach_ids:
|
|
if not rec.datas:
|
|
raise UserError(_('Attach the attachment to the Document %s') % (rec.name))
|
|
else:
|
|
raise UserError(_('Sorry This %s requires a Documents To Be Attached To Approve') % (
|
|
item.holiday_status_id.name))
|
|
|
|
self.set_date()
|
|
self._chick_leave_balance()
|
|
self._check_number_of_days()
|
|
self.write({'state': 'confirm'})
|
|
|
|
def hr_manager(self):
|
|
self._compute_leave_balance()
|
|
self._remove_followers_holiday()
|
|
if not self.replace_by and self.type == 'remove':
|
|
if self.holiday_status_id.alternative_chick == True and self.number_of_days_temp >= self.holiday_status_id.alternative_days:
|
|
raise UserError(_('Select employee Replacement before The approve holiday Request'))
|
|
if self.delegate_acc == True:
|
|
raise UserError(_('The replacement Employee Must be entered To Giving him Access'))
|
|
if self.holiday_status_id.attach_chick == True and self.type == 'remove':
|
|
if self.attach_ids:
|
|
for rec in self.attach_ids:
|
|
if not rec.datas:
|
|
raise UserError(_('Attach the attachment to the Document %s') % (rec.name))
|
|
else:
|
|
raise UserError(
|
|
_('Sorry This %s requires a Documents To Be Attached To Approve') % (self.holiday_status_id.name))
|
|
#The Approval For The Direct Manager only
|
|
for rec in self:
|
|
manager = rec.sudo().employee_id.parent_id
|
|
hr_manager = rec.sudo().employee_id.company_id.hr_manager_id
|
|
if manager:
|
|
if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
|
|
rec._chick_leave_balance()
|
|
rec.write({'state': 'validate'})
|
|
else:
|
|
raise UserError(_("Sorry, The Approval For The Direct Manager '%s' Only OR HR Manager!")%(rec.employee_id.parent_id.name))
|
|
else:
|
|
rec._chick_leave_balance()
|
|
rec.write({'state': 'validate'})
|
|
|
|
#self._chick_leave_balance()
|
|
#self.write({'state': 'validate'})
|
|
|
|
def approved(self):
|
|
self.check_sickness_leave_approval()
|
|
self._chick_leave_balance()
|
|
self.write({'state': 'approved'})
|
|
|
|
def financial_manager(self):
|
|
for item in self:
|
|
item._remove_followers_holiday()
|
|
if not item.replace_by and item.type == 'remove':
|
|
if item.holiday_status_id.alternative_chick == True and item.number_of_days_temp >= item.holiday_status_id.alternative_days:
|
|
raise UserError(_('Select employee Replacement before The approve holiday Request'))
|
|
if item.delegate_acc == True:
|
|
raise UserError(_('The replacement Employee Must be entered To Giving him Access'))
|
|
|
|
current_date = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
employee_balance = self.env['hr.holidays'].search([('employee_id', '=', item.employee_id.id),
|
|
('holiday_status_id', '=', item.holiday_status_id.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')],
|
|
order='id desc', limit=1)
|
|
employee_allocation = self.env['hr.holidays'].search([('employee_id', '=', item.employee_id.id),
|
|
('holiday_status_id', '=', item.holiday_status_id.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'allocation')],
|
|
order='id desc', limit=1)
|
|
if employee_balance:
|
|
if item.type == 'remove' and item.request_done==False:
|
|
holiday_days = item._chick_year_leavs_balance()
|
|
employee_balance.write({
|
|
'remaining_leaves': employee_balance.remaining_leaves - holiday_days,
|
|
'leaves_taken': employee_balance.leaves_taken + item.number_of_days_temp,
|
|
})
|
|
if item.issuing_ticket == 'yes':
|
|
self.env['hr.ticket.request'].create({
|
|
'employee_id': item.employee_id.id,
|
|
'leave_request_id': item.id,
|
|
'request_for': item.ticket_cash_request_for,
|
|
'request_type': item.ticket_cash_request_type.id,
|
|
# 'state':item.submit,
|
|
})
|
|
|
|
if item.issuing_exit_return == 'yes':
|
|
self.env['hr.exit.return'].create({
|
|
'employee_id': item.employee_id.id,
|
|
'leave_request_id': item.id,
|
|
'request_for': item.ticket_cash_request_for,
|
|
# 'state':item.request,
|
|
})
|
|
if item.issuing_clearance_form == 'yes':
|
|
self.env['hr.clearance.form'].create({
|
|
'employee_id': item.employee_id.id,
|
|
'leave_request_id': item.id,
|
|
'request_for': item.ticket_cash_request_for,
|
|
'clearance_type': 'vacation',
|
|
'date_deliver_work': item.date_from,
|
|
'date': current_date,
|
|
})
|
|
# self.env['hr.sick.leave'].create({
|
|
# 'leave_request': item.name,
|
|
# 'date_from': item.date_from,
|
|
# 'date_to':item.date_to,
|
|
# 'duration':item.number_of_days_temp,
|
|
# 'status':item.state,
|
|
# 'allocation_start_date':item.date_from,
|
|
# 'allocation_end_date':item.date_to,
|
|
# 'sick_relation':item.employee_id.id
|
|
#
|
|
# })
|
|
else:
|
|
employee_balance.write({
|
|
'name': 'Yearly Allocation of ' + item.holiday_status_id.name + ' Leaves',
|
|
'employee_id': item.employee_id.id,
|
|
'holiday_status_id': item.holiday_status_id.id,
|
|
'remaining_leaves': employee_balance.remaining_leaves + employee_allocation.number_of_days_temp,
|
|
'type': 'add',
|
|
})
|
|
else:
|
|
hr_holidays = self.env['hr.holidays'].create({
|
|
'name': 'Yearly Allocation of ' + item.holiday_status_id.name + ' Leaves',
|
|
'number_of_days_temp': employee_allocation.number_of_days_temp,
|
|
'employee_id': item.employee_id.id,
|
|
'holiday_status_id': item.holiday_status_id.id,
|
|
'remaining_leaves': employee_allocation.number_of_days_temp,
|
|
'check_allocation_view': 'balance',
|
|
'type': 'add',
|
|
})
|
|
self.env['hr.inverse.holidays'].create(
|
|
{'holiday_id': hr_holidays.id,
|
|
'cron_run_date': current_date, })
|
|
# if item.delegate_acc:
|
|
# item.delegate_access()
|
|
|
|
self._chick_leave_balance()
|
|
self.check_sickness_leave_approval()
|
|
self.write({'state': 'validate1','request_done': True})
|
|
self.send_email()
|
|
self.call_cron_function()
|
|
self.check_allocation_balance_annual('deduction')
|
|
|
|
def check_allocation_balance_annual(self, action):
|
|
if self.type == 'remove':
|
|
start_date = datetime.strptime(str(self.date_from), DEFAULT_SERVER_DATETIME_FORMAT)
|
|
end_date = datetime.strptime(str(self.date_to), DEFAULT_SERVER_DATETIME_FORMAT)
|
|
delta = timedelta(days=1)
|
|
if self.holiday_status_id.not_balance_annual_leave:
|
|
balance_holiday_id = self.env['hr.holidays'].search(
|
|
[('employee_id', '=', self.employee_id.id), ('type', '=', 'add'),
|
|
('holiday_status_id.leave_type', '=', 'annual'),
|
|
('check_allocation_view', '=', 'balance')
|
|
], order='id desc', limit=1)
|
|
while start_date <= end_date:
|
|
current_date = start_date.strftime("%Y-%m-%d")
|
|
remaining_leave_to_deduct = self.remaining_leaves_of_day_by_date(self.employee_id, current_date,
|
|
balance_holiday_id.holiday_status_id)
|
|
if action == 'deduction' and \
|
|
current_date in balance_holiday_id.holiday_ids.mapped('cron_run_date'):
|
|
balance_holiday_id.write({
|
|
'remaining_leaves': balance_holiday_id.remaining_leaves - float(remaining_leave_to_deduct)
|
|
})
|
|
balance_holiday_id.holiday_ids.filtered(lambda t: t.cron_run_date == current_date).unlink()
|
|
if action == 'addition' and \
|
|
current_date not in balance_holiday_id.holiday_ids.mapped('cron_run_date'):
|
|
balance_holiday_id.write({
|
|
'remaining_leaves': balance_holiday_id.remaining_leaves + float(remaining_leave_to_deduct)
|
|
})
|
|
self.env['hr.inverse.holidays'].create({'holiday_id': balance_holiday_id.id,
|
|
'cron_run_date': current_date, })
|
|
start_date += delta
|
|
return True
|
|
|
|
def send_email(self):
|
|
if self.replace_by:
|
|
template = self.sudo().env.ref('hr_holidays_public.email_template_employee_replace', False)
|
|
template.sudo().send_mail(self.id)
|
|
|
|
def check_sickness_leave_approval(self):
|
|
for leave in self:
|
|
if leave.type == 'remove' and leave.holiday_status_id.leave_type == 'sick':
|
|
emp_sickness = self.env['hr.holidays'].search([('employee_id', '=', self.employee_id.id),
|
|
('type', '=', 'remove'),
|
|
('state', 'not in', ('refuse', 'cancel', 'validate1')),
|
|
('holiday_status_id.leave_type', '=', 'sick'),
|
|
('holiday_status_id', '!=', self.holiday_status_id.id),
|
|
('holiday_status_id.sickness_severity', '<',
|
|
self.holiday_status_id.sickness_severity)]
|
|
)
|
|
if emp_sickness:
|
|
msg = ''
|
|
for sk in emp_sickness:
|
|
msg += sk.display_name + ' - ' + sk.date_from + '\n'
|
|
raise UserError(_('Sorry you can not approve this leave while following '
|
|
'leaves are not approved yet \n %s') % (msg))
|
|
|
|
def call_cron_function(self):
|
|
if self.date_from and self.date_to and self.type == 'remove':
|
|
transaction = self.env['hr.attendance.transaction'].sudo()
|
|
start_date = datetime.strptime(str(self.date_from), "%Y-%m-%d %H:%M:%S")
|
|
end_date = datetime.strptime(str(self.date_to), "%Y-%m-%d %H:%M:%S")
|
|
delta = end_date - start_date
|
|
today = datetime.now().date()
|
|
today = datetime.strptime(str(today), "%Y-%m-%d").date()
|
|
for i in range(delta.days + 1):
|
|
day = start_date + timedelta(days=i)
|
|
if today >= day.date():
|
|
transaction.process_attendance_scheduler_queue(day, self.employee_id)
|
|
|
|
def cancel(self):
|
|
for item in self:
|
|
have_cancel_request = self.env['leave.cancellation'].search(
|
|
[('leave_request_id', '=', item.id), ('state', 'not in', ('draft', 'refuse'))])
|
|
have_return_from_leave = self.env['return.from.leave'].search(
|
|
[('leave_request_id', '=', item.id), ('state', 'not in', ('draft', 'refuse'))])
|
|
if have_cancel_request:
|
|
raise UserError(
|
|
_("You can't set this request to draft as it's already have a cancel request\n" "Please contact with your administrator"))
|
|
if have_return_from_leave:
|
|
raise UserError(_(
|
|
"You can't set this request to draft as it's already have a return from leave request\n" "Please contact with your administrator"))
|
|
self._chick_leave_balance()
|
|
self.reconcile_holiday_balance()
|
|
self.remove_delegated_access()
|
|
self.write({'state': 'cancel'})
|
|
|
|
def _chick_year_leavs_balance(self):
|
|
"""chick a holiday take balance with years balance"""
|
|
for item in self:
|
|
holiday_days = item.number_of_days_temp
|
|
employee_balance = self.env['hr.holidays'].search([('employee_id', '=', item.employee_id.id),
|
|
('holiday_status_id', '=', item.holiday_status_id.id),
|
|
('type', '=', 'add'),
|
|
('check_allocation_view', '=', 'balance')],
|
|
order='id desc', limit=1)
|
|
leave_type = ['exam', 'marriage','parental','emergency','sick']
|
|
if item.holiday_status_id.leave_type in leave_type:
|
|
cron_run_date = item.employee_id.first_hiring_date
|
|
holi_date_from = fields.Date.from_string(item.date_from)
|
|
holi_date_to = fields.Date.from_string(item.date_to)
|
|
if employee_balance.holiday_ids:
|
|
cron_run_date = employee_balance.holiday_ids[-1].cron_run_date
|
|
|
|
if cron_run_date > holi_date_to:
|
|
holiday_days = 0
|
|
if holi_date_to >= cron_run_date > holi_date_from:
|
|
holiday_days = (holi_date_to - cron_run_date).days+1
|
|
|
|
return holiday_days
|
|
|
|
def reconcile_holiday_balance(self):
|
|
for holiday in self:
|
|
domain = [('check_allocation_view', '=', 'balance'),
|
|
('employee_id', '=', holiday.employee_id.id),
|
|
('holiday_status_id', '=', holiday.holiday_status_id.id),
|
|
('type', '=', 'add')]
|
|
balance = self.env['hr.holidays'].search(domain, order='id desc', limit=1)
|
|
domain[0] = ('check_allocation_view', '=', 'allocation')
|
|
allocation = self.env['hr.holidays'].search(domain, order='id desc', limit=1)
|
|
if balance:
|
|
if holiday.type == 'remove':
|
|
holiday_days = holiday._chick_year_leavs_balance()
|
|
balance.write({
|
|
'remaining_leaves': balance.remaining_leaves + holiday_days,
|
|
'leaves_taken': balance.leaves_taken - holiday.number_of_days_temp,
|
|
})
|
|
[self.env[model].search([('employee_id', '=', holiday.employee_id.id),
|
|
('leave_request_id', '=', holiday.id)]).unlink() \
|
|
for model in ['hr.ticket.request', 'hr.exit.return', 'hr.clearance.form']]
|
|
else:
|
|
balance.write({'remaining_leaves': balance.remaining_leaves - allocation.number_of_days_temp, })
|
|
|
|
|
|
def refuse(self):
|
|
for item in self:
|
|
item._remove_followers_holiday()
|
|
have_cancel_request = self.env['leave.cancellation'].search(
|
|
[('leave_request_id', '=', item.id), ('cancellation_type', '!=', 'cancel'), ('state', 'not in', ('draft', 'refuse'))])
|
|
have_return_from_leave = self.env['return.from.leave'].search(
|
|
[('leave_request_id', '=', item.id), ('state', 'not in', ('draft', 'refuse'))])
|
|
if have_cancel_request:
|
|
raise UserError(
|
|
_("You can't set this request to draft as it's already have a cancel request\n"
|
|
"Please contact with your administrator"))
|
|
if have_return_from_leave:
|
|
raise UserError(_(
|
|
"You can't set this request to draft as it's already have a return from leave request\n"
|
|
"Please contact with your administrator"))
|
|
|
|
Module = self.env['ir.module.module'].sudo()
|
|
modules_reconcile = Module.search([('state', '=', 'installed'), ('name', '=', 'exp_payroll_loans')])
|
|
if modules_reconcile:
|
|
have_reconcile_leaves = self.env['reconcile.leaves'].search(
|
|
[('yearly_vacation', '=', item.id), ('state', 'not in', ('draft', 'refuse'))])
|
|
|
|
if have_reconcile_leaves:
|
|
raise ValidationError(_("You can't set this request to draft"
|
|
" as it's already have Reconcile Holiday"))
|
|
|
|
if item.state == 'validate1':
|
|
item.reconcile_holiday_balance()
|
|
item.remove_delegated_access()
|
|
item.call_cron_function()
|
|
self.check_allocation_balance_annual('addition')
|
|
#Need Review
|
|
self.write({'request_done': False})
|
|
|
|
item.write({'state': 'refuse'})
|
|
|
|
def direct_manager_refused(self):
|
|
for rec in self:
|
|
manager = rec.sudo().employee_id.parent_id
|
|
hr_manager = rec.sudo().employee_id.company_id.hr_manager_id
|
|
if manager:
|
|
if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
|
|
rec.refuse()
|
|
else:
|
|
raise UserError(_("Sorry, The Refuse For The Direct Manager '%s' Only OR HR Manager!") % (manager.name))
|
|
else:
|
|
rec.refuse()
|
|
|
|
@api.onchange('holiday_status_id')
|
|
def _get_holiday_related_date(self):
|
|
for item in self:
|
|
if item.employee_id.first_hiring_date:
|
|
leaves_ids = self.env['hr.holidays'].search([('employee_id', '=', item.employee_id.id),
|
|
('state', '=', 'validate1'),
|
|
('type', '=', 'remove'),
|
|
('holiday_status_id.used_once', '=', True)])
|
|
for leave_id in leaves_ids:
|
|
if item.holiday_status_id == leave_id.holiday_status_id:
|
|
raise UserError(_('Sorry you used this type of leave before and it used for once'))
|
|
if item.holiday_status_id.exit_return_permission and item.employee_id.check_nationality == False:
|
|
item.issuing_exit_return = 'yes'
|
|
item.check_related = True
|
|
else:
|
|
item.issuing_exit_return = 'no'
|
|
item.check_related = False
|
|
if item.holiday_status_id.issuing_ticket and item.employee_id.check_nationality == False:
|
|
item.issuing_ticket = 'yes'
|
|
item.check_related = True
|
|
else:
|
|
item.issuing_ticket = 'no'
|
|
item.check_related = False
|
|
hiring_valu = datetime.strptime(str(self.employee_id.first_hiring_date), "%Y-%m-%d")
|
|
current_date = (datetime.utcnow() + timedelta(hours=3)).date()
|
|
different_years = relativedelta.relativedelta(current_date, hiring_valu).years
|
|
if not self.holiday_status_id:
|
|
self.check_unlimit = False
|
|
elif self.holiday_status_id.advance_request_years <= different_years:
|
|
self.check_unlimit = True
|
|
else:
|
|
self.check_unlimit = False
|
|
if item.holiday_status_id.issuing_clearance_form:
|
|
item.issuing_clearance_form = 'yes'
|
|
else:
|
|
item.issuing_clearance_form = 'no'
|
|
if item.holiday_status_id.issuing_deliver_custody:
|
|
item.issuing_deliver_custdy = 'yes'
|
|
else:
|
|
item.issuing_deliver_custdy = 'no'
|
|
if item.holiday_status_id.include_weekend and item.date_to: self.include_weekend()
|
|
|
|
def _check_state_access_right(self, vals):
|
|
"""
|
|
override the function to allow division manager to confirm employee's holiday.
|
|
"""
|
|
|
|
if vals.get('state') and vals['state'] not in ['draft', 'confirm', 'cancel'] \
|
|
and not ((self.env.user and self.env.user.has_group('hr_holidays_community.group_hr_holidays_user')) or
|
|
(self.env.user and self.env.user.has_group('hr_base.group_division_manager'))):
|
|
return False
|
|
return True
|
|
|
|
def get_user_id(self):
|
|
employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
|
|
if employee_id:
|
|
return employee_id.id
|
|
else:
|
|
return False
|
|
|
|
@api.model
|
|
def _get_employee_domain(self):
|
|
if self.env.user.id != SUPERUSER_ID:
|
|
employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1).ids
|
|
if employee_id:
|
|
return [('id', 'in', employee_id)]
|
|
else:
|
|
return [('id', 'in', [])]
|
|
else:
|
|
employee_id = self.env['hr.employee']
|
|
if employee_id:
|
|
return employee_id.id
|
|
else:
|
|
return False
|
|
|
|
@api.onchange('employee_id')
|
|
def _onchange_employee(self):
|
|
self.department_id = self.employee_id.department_id
|
|
self.emp_id = self.employee_id.id
|
|
|
|
def unlink(self):
|
|
for i in self:
|
|
if i.state != 'draft':
|
|
raise UserError(_('You can not delete record in state not in draft'))
|
|
|
|
if i.check_allocation_view == 'balance' and i.leaves_taken > 0:
|
|
raise UserError(_('You can not delete Balance records and there is leave taken'))
|
|
return super(HRHolidays, self).unlink()
|
|
|
|
def _get_number_of_days(self, date_from, date_to, employee_id, official_event=True, working_days=False):
|
|
|
|
""" Returns a float equals to the timedelta between two dates given as string."""
|
|
from_dt = fields.Datetime.from_string(date_from)
|
|
to_dt = fields.Datetime.from_string(date_to)
|
|
if not employee_id: return 0.0
|
|
employee = self.env['hr.employee'].browse(employee_id)
|
|
# if not employee.is_calender:
|
|
# return employee.get_work_days_count(from_dt, fields.Datetime.from_string(date_to) + timedelta(hours=23))
|
|
time_delta = to_dt - from_dt
|
|
time_delta = math.ceil(time_delta.days + 1 + float(time_delta.seconds) / 86400)
|
|
if not official_event or working_days:
|
|
hlist = []
|
|
for i in range((to_dt - from_dt).days + 1): hlist.append(from_dt.date() + timedelta(days=i))
|
|
if official_event is False:
|
|
# dont count official holidays in the holiday
|
|
dlist = []
|
|
for event in self.env['hr.holiday.officials'].search([('active', '=', True), ('state', '=', 'confirm'),
|
|
('date_from', '<=', date_to),
|
|
('date_to', '>=', date_from)]):
|
|
if event.religion and employee_id and employee.religion != event.religion: continue
|
|
edate_from = datetime.strptime(str(event.date_from), '%Y-%m-%d').date()
|
|
dlt = datetime.strptime(str(event.date_to), '%Y-%m-%d').date() - edate_from
|
|
for i in range(dlt.days + 1): dlist.append(edate_from + timedelta(days=i))
|
|
time_delta = len(list(set(hlist) - set(dlist)))
|
|
self.official_holiday_days = len([rec for rec in dlist if rec >= from_dt.date() and rec <= to_dt.date()])
|
|
if working_days:
|
|
wkends = employee.resource_calendar_id.full_day_off or employee.resource_calendar_id.shift_day_off
|
|
wknd_days = [d.name.lower() for d in wkends]
|
|
rlist = official_event is False and dlist and list(set(hlist) - set(dlist)) or hlist
|
|
for dt in rlist:
|
|
if dt.strftime('%A').lower() in wknd_days: time_delta -= 1
|
|
return time_delta
|
|
|
|
def set_date(self):
|
|
for item in self:
|
|
if item.date_from and item.date_to:
|
|
current_date = (datetime.utcnow() + timedelta(hours=3))
|
|
date_from_value = datetime.strptime(str(item.date_from), "%Y-%m-%d %H:%M:%S")
|
|
date_to_value = datetime.strptime(str(item.date_to), "%Y-%m-%d %H:%M:%S")
|
|
|
|
user = self.env.user
|
|
|
|
# Check if user is **NOT** in the HR group
|
|
# self.env.user.has_group(
|
|
|
|
if date_to_value < date_from_value:
|
|
raise UserError(_('Sorry Date TO must be bigger than Date From'))
|
|
|
|
if not self.env.user.has_group("hr.group_hr_user"):
|
|
if (date_from_value - current_date).days + 1 < item.holiday_status_id.request_before:
|
|
raise UserError(_('Sorry your request must be before %s Days of your leave') \
|
|
% item.holiday_status_id.request_before)
|
|
|
|
# if date_to_value < date_from_value:
|
|
# raise UserError(_('Sorry Date TO must be bigger than Date From'))
|
|
# if (date_from_value - current_date).days + 1 < item.holiday_status_id.request_before:
|
|
# raise UserError(_('Sorry your request must be before %s Days of your leave') \
|
|
# % item.holiday_status_id.request_before)
|
|
|
|
|
|
self.number_of_days_temp = self._get_number_of_days(item.date_from, item.date_to, self.employee_id.id,
|
|
self.holiday_status_id.official_holidays,
|
|
self.holiday_status_id.working_days)
|
|
if item.number_of_days_temp < item.holiday_status_id.minimum_duration:
|
|
raise UserError(_('Sorry duration must be bigger than or equal to %s Days') \
|
|
% item.holiday_status_id.minimum_duration)
|
|
|
|
@api.onchange('date_from')
|
|
def _onchange_date_from(self):
|
|
if self.date_from:
|
|
self.date_from = fields.Datetime.from_string(self.date_from).replace(hour=0, minute=0, second=0,
|
|
microsecond=0)
|
|
if self.holiday_status_id.include_weekend:
|
|
self.include_weekend()
|
|
self.date_on = True
|
|
self.set_date()
|
|
|
|
@api.onchange('date_from')
|
|
def _onchange_date_from_2(self):
|
|
if self.date_to:
|
|
self.date_to = False
|
|
self.number_of_days_temp = False
|
|
|
|
@api.onchange('date_to')
|
|
def _onchange_date_to(self):
|
|
""" Update the number_of_days. """
|
|
if self.date_to:
|
|
self.date_to = fields.Datetime.from_string(self.date_to).replace(hour=0, minute=0, second=0, microsecond=0)
|
|
self.date_on = True
|
|
self.set_date()
|
|
|
|
@api.onchange('number_of_days_temp')
|
|
def _get_end_date(self, context=None):
|
|
if self.date_on:
|
|
self.date_on = False
|
|
return
|
|
for item in self:
|
|
if item.number_of_days_temp:
|
|
if item.date_from:
|
|
self.date_to = (datetime.strptime(str(item.date_from), "%Y-%m-%d %H:%M:%S") +
|
|
dt.timedelta(days=item.number_of_days_temp - 1))
|
|
self.set_date()
|
|
if item.number_of_days_temp < item.holiday_status_id.minimum_duration:
|
|
raise UserError(_(
|
|
'Sorry duration must be bigger than or equal to %s Days') % item.holiday_status_id.minimum_duration)
|
|
|
|
@api.constrains('date_from')
|
|
def _check_include_weekend(self):
|
|
if self.holiday_status_id.include_weekend:
|
|
prev_holiday = self.env['hr.holidays'].search([('employee_id', '=', self.employee_id.id),
|
|
('holiday_status_id', '=', self.holiday_status_id.id),
|
|
('date_to', '<', self.date_from),
|
|
('type', '=', 'remove'),
|
|
('state', '!=', 'refuse')], limit=1, order="date_to desc")
|
|
if not prev_holiday: return
|
|
if not self.date_from : return
|
|
date_from, last_dt_to = fields.Date.from_string(self.date_from), fields.Date.from_string(prev_holiday.date_to)
|
|
days_btwn = (date_from - last_dt_to).days - 1
|
|
if days_btwn < 1: return
|
|
count, dates_btwn, wknd_days = 0, [], [d.name.lower() for d in
|
|
self.employee_id.resource_calendar_id.full_day_off]
|
|
for r in range(days_btwn): dates_btwn.append(date_from + relativedelta.relativedelta(days=-(r + 1)))
|
|
if days_btwn > len(wknd_days) or dates_btwn[0].strftime('%A').lower() not in wknd_days: return
|
|
for dt in dates_btwn:
|
|
if dt.strftime('%A').lower() in wknd_days: count += 1
|
|
if count == days_btwn:
|
|
raise UserError(_('Sorry, it is not possible to request a leave at the beginning of the week, as there is a leave before it that ends at the end of the week, and the leave includes the holiday.'))
|
|
|
|
def include_weekend(self):
|
|
prev_holiday = self.env['hr.holidays'].search([('employee_id', '=', self.employee_id.id),
|
|
('holiday_status_id', '=', self.holiday_status_id.id),
|
|
('type', '=', 'remove'),
|
|
('state', '!=', 'refuse')], limit=1, order="date_to desc")
|
|
if not prev_holiday: return
|
|
if not self.date_from : return
|
|
date_from, last_dt_to = fields.Date.from_string(self.date_from), fields.Date.from_string(prev_holiday.date_to)
|
|
days_btwn = (date_from - last_dt_to).days - 1
|
|
if days_btwn < 1: return
|
|
count, dates_btwn, wknd_days = 0, [], [d.name.lower() for d in
|
|
self.employee_id.resource_calendar_id.full_day_off]
|
|
for r in range(days_btwn): dates_btwn.append(date_from + relativedelta.relativedelta(days=-(r + 1)))
|
|
if days_btwn > len(wknd_days) or dates_btwn[0].strftime('%A').lower() not in wknd_days: return
|
|
for dt in dates_btwn:
|
|
if dt.strftime('%A').lower() in wknd_days: count += 1
|
|
if count == days_btwn: self.date_from = date_from + relativedelta.relativedelta(days=-count)
|
|
|
|
def delegate_access(self):
|
|
for rec in self:
|
|
if not rec.employee_id.user_id: raise UserError(
|
|
_('Kindly set a user for employee %s') % (rec.employee_id.name))
|
|
if not rec.replace_by.user_id: raise UserError(
|
|
_('Kindly set a user or employee for replace by %s') % (rec.replace_by.name))
|
|
group_ids = list(
|
|
set(rec.employee_id.user_id.groups_id.ids) - set(rec.sudo().replace_by.user_id.groups_id.ids))
|
|
rec.replace_by.user_id.sudo().write({'groups_id': [(4, g) for g in group_ids]})
|
|
rec.sudo().write({'delegated_group_ids': [fields.Command.set(group_ids)], })
|
|
allowed_comp = set(rec.employee_id.user_id.company_ids.ids) - set(
|
|
rec.sudo().replace_by.user_id.company_ids.ids)
|
|
# if allowed_comp:
|
|
rec.replace_by.user_id.sudo().write({'company_ids': [(4, c) for c in allowed_comp]})
|
|
rec.delegated_company_ids = [fields.Command.set(list(allowed_comp))]
|
|
dept_ids = rec.employee_id.department_id.search(
|
|
[('manager_id', '=', rec.employee_id.id), ('active', '=', True)])
|
|
for d in dept_ids: d.sudo().write({'manager_id': rec.replace_by.id, }, )
|
|
rec.delegated_department_ids = [fields.Command.set(dept_ids.ids)]
|
|
|
|
def remove_delegated_access(self):
|
|
for rec in self:
|
|
groups, companies = rec.delegated_group_ids.ids, rec.delegated_company_ids.ids
|
|
departments, manager, holiday = rec.delegated_department_ids.ids, rec.employee_id.id, rec
|
|
while (holiday):
|
|
rec.remove_access(holiday, groups, companies, departments, manager)
|
|
holiday = holiday.search([('employee_id', '=', holiday.replace_by.id), ('type', '=', 'remove'),
|
|
('date_from', '<=', fields.Date.today()),
|
|
('date_to', '>=', fields.Date.today()),
|
|
('state', '=', 'validate1')])
|
|
|
|
def remove_access(self, holiday, groups, companies, departments, manager):
|
|
if set(holiday.delegated_group_ids.ids) & set(groups):
|
|
holiday.replace_by.user_id.sudo().write({'groups_id': [(3, g) for g in groups]})
|
|
holiday.delegated_group_ids = [(3, g) for g in groups]
|
|
if set(holiday.delegated_department_ids.ids) & set(departments):
|
|
for d in holiday.department_id.search([('id', 'in', departments)]): d.sudo().write(
|
|
{'manager_id': manager, }, )
|
|
holiday.delegated_department_ids = [(3, d) for d in departments]
|
|
if set(holiday.delegated_company_ids.ids) & set(companies):
|
|
holiday.replace_by.user_id.sudo().write({'company_ids': [(3, c) for c in companies]})
|
|
holiday.delegated_company_ids = [(3, c) for c in companies]
|
|
|
|
def _process_delegation_access(self):
|
|
"""cron for Handling Delegation Access Process"""
|
|
holiday = self.env['hr.holidays']
|
|
start_day_holiday = holiday.search([('delegate_acc', '=', True),
|
|
('date_from', '=', fields.Date.today()),
|
|
('state', '=', 'validate1')])
|
|
end_day_holiday = holiday.search([('delegate_acc', '=', True),
|
|
('date_to', '=', fields.Date.today()),
|
|
('state', '=', 'validate1')])
|
|
|
|
if start_day_holiday:
|
|
start_day_holiday.delegate_access()
|
|
if end_day_holiday:
|
|
end_day_holiday.remove_delegated_access()
|
|
|
|
|
|
class HRINVERSHolidays(models.Model):
|
|
_name = 'hr.inverse.holidays'
|
|
_description = 'Inverse Holidays'
|
|
|
|
holiday_id = fields.Many2one(comodel_name='hr.holidays')
|
|
cron_run_date = fields.Date()
|
|
balance_leaves = fields.Float(string='Balance')
|
|
no_balance_leaves = fields.Float(string='No Balance')
|
|
|
|
|
|
class holidaysAttach(models.Model):
|
|
_inherit = 'ir.attachment'
|
|
|
|
att_holiday_ids = fields.Many2one(comodel_name='hr.holidays')
|
|
|
|
|
|
@api.onchange('att_holiday_ids')
|
|
def _onchange_att_holiday_ids(self):
|
|
if self.att_holiday_ids:
|
|
self.res_model = self.att_holiday_ids._name
|
|
self.res_id = self.att_holiday_ids.id
|
|
|
|
@api.model
|
|
def check(self, mode, values=None):
|
|
"""Restricts the access to an ir.attachment, according to referred model
|
|
In the 'document' module, it is overriden to relax this hard rule, since
|
|
more complex ones apply there.
|
|
"""
|
|
# collect the records to check (by model)
|
|
model_ids = defaultdict(set) # {model_name: set(ids)}
|
|
require_employee = False
|
|
if self:
|
|
self._cr.execute('SELECT res_model, res_id, create_uid, public FROM ir_attachment WHERE id IN %s', [tuple(self.ids)])
|
|
for res_model, res_id, create_uid, public in self._cr.fetchall():
|
|
if public and mode == 'read':
|
|
continue
|
|
if not (res_model and res_id):
|
|
if create_uid != self._uid:
|
|
require_employee = True
|
|
continue
|
|
model_ids[res_model].add(res_id)
|
|
if values and values.get('res_model') and values.get('res_id'):
|
|
model_ids[values['res_model']].add(values['res_id'])
|
|
|
|
# check access rights on the records
|
|
for res_model, res_ids in model_ids.items():
|
|
# ignore attachments that are not attached to a resource anymore
|
|
# when checking access rights (resource was deleted but attachment
|
|
# was not)
|
|
if res_model not in self.env:
|
|
require_employee = True
|
|
continue
|
|
elif res_model == 'res.users' and len(res_ids) == 1 and self._uid == list(res_ids)[0]:
|
|
# by default a user cannot write on itself, despite the list of writeable fields
|
|
# e.g. in the case of a user inserting an image into his image signature
|
|
# we need to bypass this check which would needlessly throw us away
|
|
continue
|
|
records = self.env[res_model].browse(res_ids).exists()
|
|
if len(records) < len(res_ids):
|
|
require_employee = True
|
|
# For related models, check if we can write to the model, as unlinking
|
|
# and creating attachments can be seen as an update to the model
|
|
records.check_access('write' if mode in ('create', 'unlink') else mode)
|
|
records.check_access(mode)
|
|
|
|
if require_employee:
|
|
if not (self.env.user._is_admin() or self.env.user.has_group('base.group_user') or self._context.get('bypass_check_public_holidays', False)):
|
|
raise AccessError(_("Sorry, you are not allowed to access this document."))
|
|
|
|
# @api.model
|
|
# def create(self, values):
|
|
# print("**********************************************create")
|
|
# result = super(holidaysAttach, self).create(values)
|
|
# print("**********************************************result",result)
|
|
# if result.att_holiday_ids:
|
|
# result.update({
|
|
# 'res_model': result.att_holiday_ids._name,
|
|
# 'res_id': result.att_holiday_ids.id
|
|
# })
|
|
# return result
|
|
|
|
|
|
|
|
|