187 lines
7.0 KiB
Python
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
|
|
|