Files
hr-tools/hr_luncheon_voucher/models/hr_lv_allocation.py
2023-04-28 14:55:46 +02:00

187 lines
7.0 KiB
Python

from datetime import datetime, timedelta, time
from dateutil.rrule import rrule, DAILY
from pytz import UTC
from odoo import fields, models, api
class LuncheonVouchersAllocation(models.Model):
_name = "hr.lv.allocation"
_description = "Luncheon Vouchers Allocation"
name = fields.Char('Name')
distrib_campaign_name = fields.Char('Distribution campaign')
state = fields.Selection(
[
("draft", "Draft"),
("confirmed", "Confirmed"),
("distributed", "Distributed"),
],
string="Status",
readonly=True,
copy=False,
default="draft",
help="The status is set to 'Draft', when an allocation request is created. The status is 'Confirmed', when an allocation request is confirmed by HR manager. The status is 'Distributed', when the luncheon vouchers have been distributed.",
)
date_from = fields.Datetime(
string="Start Date",
store=True,
readonly=False,
copy=False,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
date_to = fields.Datetime(
string="End Date",
store=True,
readonly=False,
copy=False,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
employee_id = fields.Many2one(
"hr.employee",
store=True,
string="Employee",
index=True,
readonly=False,
ondelete="restrict",
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
number_acquired_lv = fields.Integer(
string="Acquired Vouchers",
store=True,
readonly=False,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
number_dued_lv = fields.Integer(
string="Dued Vouchers",
store=True,
readonly=False,
states={
"confirmed": [("readonly", True)],
"distributed": [("readonly", True)],
},
)
number_distributed_lv = fields.Integer(
string="Distributed Vouchers",
store=True,
readonly=False,
states={
"confirmed": [("readonly", False)],
"distributed": [("readonly", True)],
},
)
lv_balance = fields.Integer('Vouchers balance', compute="_get_lv_balance", help="Vouchers available after distribution. Dued vouchers - Distributed vouchers")
@api.depends("number_dued_lv", "number_distributed_lv")
def _get_lv_balance(self):
for allocation in self:
allocation.lv_balance = allocation.number_dued_lv - allocation.number_distributed_lv
@api.model_create_multi
def create(self, values):
res = super(LuncheonVouchersAllocation, self).create(values)
res._calculate_number_acquired_lv()
res._calculate_number_dued_lv()
res._default_number_distributed_lv()
return res
@api.depends("employee_id")
def _default_number_distributed_lv(self):
for record in self:
record.number_distributed_lv = record.employee_id.default_monthly_lv
def _has_cancelling_voucher_event(self, day):
category_no_voucher_ids = self.env["calendar.event.type"].search([("remove_luncheon_voucher", "=", True)])
events = self.env["calendar.event"].search(
[
("categ_ids", "in", category_no_voucher_ids.ids),
("partner_ids", "in", self.employee_id.user_id.partner_id.id)
]
)
day_start = fields.Datetime.to_datetime(day.date())
day_end = fields.Datetime.to_datetime(day.date()) + timedelta(hours=24)
cancelling_events = events.filtered(lambda x: not((x.start < day_start) and (x.stop <= day_start)) and not((x.start >= day_end) and (x.stop > day_end)) )
if len(cancelling_events) > 0:
return True
else:
return False
def _calculate_number_acquired_lv(self):
nb_eligible_days = 0
dfrom = datetime.combine(fields.Date.from_string(self.date_from), time.min).replace(tzinfo=UTC)
dto = datetime.combine(fields.Date.from_string(self.date_to), time.max).replace(tzinfo=UTC)
period_days = rrule(DAILY, dfrom, until=dto)
calendar_resource = self.employee_id.resource_calendar_id
for day in period_days:
# Check if this days is a working day
if not calendar_resource.is_working_day(day):
continue
# The employee should work this day but...
if self.env.company.hr_half_day_cancels_voucher and not calendar_resource.is_full_working_day(day):
# The luncheon voucher is acquired only if the employee has worked the entire day
continue
# Check leaves
if not calendar_resource.is_worked_day(self.employee_id, day):
continue
# The employee has worked this day but...
if self.env.company.hr_half_day_cancels_voucher and not calendar_resource.all_attendances_worked(self.employee_id.resource_id, day):
# The luncheon voucher is acquired only if the employee has worked the entire day
continue
# Check there is no event cancelling the voucher
if self._has_cancelling_voucher_event(day):
continue
# All checks passed, the days is eligible for a voucher
nb_eligible_days += 1
self.number_acquired_lv = nb_eligible_days
def _calculate_number_dued_lv(self):
for record in self:
if record.state == "distributed":
record.number_dued_lv = record.employee_id.dued_lv
else:
record.number_dued_lv = (
record.employee_id.dued_lv + record.number_acquired_lv
)
def confirm_allocation(self):
for record in self:
if record.state == "draft":
record.state = "confirmed"
record.employee_id._compute_total_acquired_lv()
record.employee_id._compute_dued_lv()
def back_to_draft(self):
for record in self:
if record.state in ["confirmed","distributed"]:
record.state = "draft"
record.employee_id._compute_total_acquired_lv()
record.employee_id._compute_distributed_lv()
record.employee_id._compute_dued_lv()
def distribute_allocation(self):
for record in self:
if record.state == "confirmed":
record.state = "distributed"
record.employee_id._compute_distributed_lv()
record.employee_id._compute_dued_lv()
def adjust_distribution(self):
for record in self:
for record in self:
if record.state == "draft":
record.number_distributed_lv = record.employee_id.dued_lv + record.number_acquired_lv