You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

519 lines
20 KiB

# Copyright 2017-2020 Coop IT Easy (http://coopiteasy.be)
# Rémy Taymans <remy@coopiteasy.be>
# Copyright 2017-2018 Thibault François
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from ast import literal_eval
from datetime import datetime, timedelta
from itertools import groupby
from pytz import timezone, utc
from odoo import fields, http
from odoo.http import request
from odoo.addons.beesdoo_shift.models.planning import float_to_time
class WebsiteShiftController(http.Controller):
def is_user_worker(self):
user = request.env["res.users"].browse(request.uid)
return user.partner_id.is_worker
def is_user_irregular(self):
user = request.env["res.users"].browse(request.uid)
working_mode = user.partner_id.working_mode
return working_mode == "irregular"
def is_user_regular(self):
user = request.env["res.users"].browse(request.uid)
working_mode = user.partner_id.working_mode
return working_mode == "regular"
def is_user_regular_without_shift(self):
user = request.env["res.users"].browse(request.uid)
return (
not user.partner_id.subscribed_shift_ids.id
and self.is_user_regular()
)
def is_user_exempted(self):
user = request.env["res.users"].browse(request.uid)
working_mode = user.partner_id.working_mode
return working_mode == "exempt"
def user_can_subscribe(self, user=None):
"""Return True if a user can subscribe to a shift. A user can
subiscribe if:
* the user is an irregular worker
* the user is not unsubscribed
* the user is not resigning
"""
if not user:
user = request.env["res.users"].browse(request.uid)
return (
user.partner_id.working_mode == "irregular"
and user.partner_id.state != "unsubscribed"
and user.partner_id.state != "resigning"
)
def add_days(self, datetime, days):
"""
Add the number of days to datetime. This take the DST in
account, meaning that the UTC time will be correct even if the
new datetime has cross the DST boundary.
:param datetime: a naive datetime expressed in UTC
:return: a naive datetime expressed in UTC with the added days
"""
# Ensure that the datetime given is without a timezone
assert datetime.tzinfo is None
# Get current user and user timezone
# Take user tz, if empty use context tz, if empty use UTC
cur_user = request.env["res.users"].browse(request.uid)
user_tz = utc
if cur_user.tz:
user_tz = timezone(cur_user.tz)
elif request.env.context["tz"]:
user_tz = timezone(request.env.context["tz"])
# Convert to UTC
dt_utc = utc.localize(datetime, is_dst=False)
# Convert to user TZ
dt_local = dt_utc.astimezone(user_tz)
# Add the number of days
newdt_local = dt_local + timedelta(days=days)
# If the newdt_local has cross the DST boundary, its tzinfo is
# no longer correct. So it will be replaced by the correct one.
newdt_local = user_tz.localize(newdt_local.replace(tzinfo=None))
# Now the newdt_local has the right DST so it can be converted
# to UTC.
newdt_utc = newdt_local.astimezone(utc)
return newdt_utc.replace(tzinfo=None)
@http.route("/my/shift", auth="user", website=True)
def my_shift(self, **kw):
"""
Personal page for managing your shifts
"""
if self.is_user_irregular():
return request.render(
"beesdoo_website_shift.my_shift_irregular_worker",
self.my_shift_irregular_worker(nexturl="/my/shift"),
)
if self.is_user_regular_without_shift():
return request.render(
"beesdoo_website_shift.my_shift_regular_worker_without_shift",
self.my_shift_regular_worker_without_shift(),
)
if self.is_user_regular():
return request.render(
"beesdoo_website_shift.my_shift_regular_worker",
self.my_shift_regular_worker(),
)
if self.is_user_exempted():
return request.render(
"beesdoo_website_shift.my_shift_exempted_worker",
self.my_shift_exempted_worker(),
)
if self.is_user_worker():
return request.render(
"beesdoo_website_shift.my_shift_new_worker", {}
)
return request.render("beesdoo_website_shift.my_shift_non_worker", {})
@http.route("/shift/<int:shift_id>/subscribe", auth="user", website=True)
def subscribe_to_shift(self, shift_id=-1, **kw):
"""
Subscribe the current connected user into the given shift
This is done only if :
* shift sign up is authorised via configuration panel
* the user can subscribe
* the given shift exist
* the shift status is open
* the shift is free for subscription
* the shift is starting after the time interval
for attendance sheet generation defined in beesdoo_shift settings
"""
# Get current user
cur_user = request.env["res.users"].browse(request.uid)
# Get the shift
shift = request.env["beesdoo.shift.shift"].sudo().browse(shift_id)
# Get config
irregular_enable_sign_up = request.website.irregular_enable_sign_up
# Set start time limit as defined in beesdoo_shift settings
# TODO: Move this into the attendance_sheet module
# setting = request.website.attendance_sheet_generation_interval
start_time_limit = datetime.now() # + timedelta(minutes=setting)
request.session["success"] = False
if (
irregular_enable_sign_up
and self.user_can_subscribe()
and shift
and shift.state == "open"
and shift.start_time > start_time_limit
and not shift.worker_id
):
shift.worker_id = cur_user.partner_id
request.session["success"] = True
return request.redirect(kw["nexturl"])
@http.route("/shift_irregular_worker", auth="public", website=True)
def public_shift_irregular_worker(self, **kw):
"""
Show a public access page that show all the available shifts for irregular worker.
"""
nexturl = "/shift_irregular_worker"
irregular_enable_sign_up = False
# Create template context
template_context = {}
template_context.update(
self.available_shift_irregular_worker(
irregular_enable_sign_up, nexturl
)
)
return request.render(
"beesdoo_website_shift.public_shift_irregular_worker",
template_context,
)
@http.route("/shift_template_regular_worker", auth="public", website=True)
def public_shift_template_regular_worker(self, **kw):
"""
Show a public access page that show all the available shift templates for regular worker.
"""
# Get all the task template
template = request.env["beesdoo.shift.template"]
task_templates = template.sudo().search(
[], order="planning_id, day_nb_id, start_time"
)
# Get config
regular_highlight_rule = request.website.regular_highlight_rule
task_tpls_data = []
for task_tpl in task_templates:
has_enough_workers = task_tpl.remaining_worker <= (
task_tpl.worker_nb * regular_highlight_rule / 100
)
task_tpls_data.append((task_tpl, has_enough_workers))
return request.render(
"beesdoo_website_shift.public_shift_template_regular_worker",
{"task_tpls_data": task_tpls_data, "float_to_time": float_to_time},
)
def my_shift_irregular_worker(self, nexturl=""):
"""
Return template variables for 'beesdoo_website_shift.my_shift_irregular_worker' template
"""
# Get config
irregular_enable_sign_up = request.website.irregular_enable_sign_up
# Create template context
template_context = {}
template_context.update(self.my_shift_worker_status())
template_context.update(self.my_shift_next_shifts())
template_context.update(self.my_shift_past_shifts())
template_context.update(
self.available_shift_irregular_worker(
irregular_enable_sign_up and self.user_can_subscribe(), nexturl
)
)
# Add feedback about the success or the fail of the subscription
template_context["back_from_subscription"] = False
if "success" in request.session:
template_context["back_from_subscription"] = True
template_context["success"] = request.session.get("success")
del request.session["success"]
# Add setting for subscription allowed time
# TODO: move this to the attendance_sheet module
# subscription_time_limit = (
# request.website.attendance_sheet_generation_interval
# )
subscription_time_limit = 0
template_context["subscription_time_limit"] = subscription_time_limit
return template_context
def my_shift_regular_worker_without_shift(self):
"""
Return template variables for 'beesdoo_website_shift.my_shift_regular_worker_without_shift' template
"""
return self.my_shift_worker_status()
def my_shift_regular_worker(self):
"""
Return template variables for 'beesdoo_website_shift.my_shift_regular_worker' template
"""
# Create template context
template_context = {}
# Get all the task template
template = request.env["beesdoo.shift.template"]
task_templates = template.sudo().search(
[], order="planning_id, day_nb_id, start_time"
)
template_context.update(self.my_shift_worker_status())
template_context.update(self.my_shift_next_shifts())
template_context.update(self.my_shift_past_shifts())
template_context.update(
{"task_templates": task_templates, "float_to_time": float_to_time}
)
return template_context
def my_shift_exempted_worker(self):
"""
Return template variables for 'beesdoo_website_shift.my_shift_exempted_worker' template
"""
return self.my_shift_worker_status()
def available_shift_irregular_worker(
self, irregular_enable_sign_up=False, nexturl=""
):
"""
Return template variables for
'beesdoo_website_shift.available_shift_irregular_worker' template
"""
# Get current user
cur_user = request.env["res.users"].browse(request.uid)
# Get all the shifts in the future with no worker
now = datetime.now()
shifts = (
request.env["beesdoo.shift.shift"]
.sudo()
.search(
[
("start_time", ">", now.strftime("%Y-%m-%d %H:%M:%S")),
("worker_id", "=", False),
("state", "=", "open"),
],
order="start_time, task_template_id, task_type_id",
)
)
# Get shifts where user is subscribed
subscribed_shifts = (
request.env["beesdoo.shift.shift"]
.sudo()
.search(
[
("start_time", ">", now.strftime("%Y-%m-%d %H:%M:%S")),
("worker_id", "=", cur_user.partner_id.id),
],
order="start_time, task_template_id, task_type_id",
)
)
# Get config
irregular_shift_limit = request.website.irregular_shift_limit
highlight_rule_pc = request.website.highlight_rule_pc
hide_rule = request.website.hide_rule / 100.0
# Grouby task_template_id, if no task_template_id is specified
# then group by start_time, if no start_time specified sort by
# task_type
groupby_iter = groupby(
shifts,
lambda s: (s.task_template_id, s.start_time, s.task_type_id),
)
shifts_count_subscribed = []
nb_displayed_shift = 0 # Number of shift displayed
for (keys, grouped_shifts) in groupby_iter:
(task_template, start_time, task_type) = keys
nb_displayed_shift = nb_displayed_shift + 1
shift_list = list(grouped_shifts)
# Compute available space
free_space = len(shift_list)
# Is the current user subscribed to this task_template
is_subscribed = any(
(
sub_shift.task_template_id == task_template
and sub_shift.start_time == start_time
and sub_shift.task_type_id == task_type
)
for sub_shift in subscribed_shifts
)
# Check the necessary number of worker based on the
# highlight_rule_pc
has_enough_workers = (
free_space
<= (task_template.worker_nb * highlight_rule_pc) / 100
)
if free_space >= task_template.worker_nb * hide_rule:
shifts_count_subscribed.append(
[
shift_list[0],
free_space,
is_subscribed,
has_enough_workers,
]
)
# Stop showing shifts if the limit is reached
if (
irregular_shift_limit > 0
and nb_displayed_shift >= irregular_shift_limit
):
break
return {
"shift_templates": shifts_count_subscribed,
"nexturl": nexturl,
"irregular_enable_sign_up": irregular_enable_sign_up,
}
def my_shift_next_shifts(self):
"""
Return template variables for 'beesdoo_website_shift.my_shift_next_shifts' template
"""
# Get current user
cur_user = request.env["res.users"].browse(request.uid)
# Get shifts where user is subscribed
now = datetime.now()
subscribed_shifts_rec = (
request.env["beesdoo.shift.shift"]
.sudo()
.search(
[
("start_time", ">", now.strftime("%Y-%m-%d %H:%M:%S")),
("worker_id", "=", cur_user.partner_id.id),
],
order="start_time, task_template_id, task_type_id",
)
)
# Create a list of record in order to add new record to it later
subscribed_shifts = []
for rec in subscribed_shifts_rec:
subscribed_shifts.append(rec)
# In case of regular worker, we compute his fictive next shifts
# according to the regular_next_shift_limit
if self.is_user_regular():
# Compute main shift
nb_subscribed_shifts = len(subscribed_shifts)
if nb_subscribed_shifts > 0:
main_shift = subscribed_shifts[-1]
else:
task_template = (
request.env["beesdoo.shift.template"]
.sudo()
.search(
[("worker_ids", "in", cur_user.partner_id.id)], limit=1
)
)
main_shift = (
request.env["beesdoo.shift.shift"]
.sudo()
.search(
[
("task_template_id", "=", task_template[0].id),
("start_time", "!=", False),
("end_time", "!=", False),
],
order="start_time desc",
limit=1,
)
)
# Get config
regular_next_shift_limit = request.website.regular_next_shift_limit
shift_period = int(
request.env["ir.config_parameter"].get_param(
"beesdoo_website_shift.shift_period"
)
)
for i in range(nb_subscribed_shifts, regular_next_shift_limit):
# Create the fictive shift
shift = main_shift.new()
shift.name = main_shift.name
shift.task_template_id = shift.task_template_id
shift.planning_id = main_shift.planning_id
shift.task_type_id = main_shift.task_type_id
shift.worker_id = main_shift.worker_id
shift.state = "draft"
shift.super_coop_id = main_shift.super_coop_id
shift.color = main_shift.color
shift.is_regular = main_shift.is_regular
shift.replaced_id = main_shift.replaced_id
shift.revert_info = main_shift.revert_info
# Set new date
shift.start_time = self.add_days(
main_shift.start_time, days=i * shift_period
)
shift.end_time = self.add_days(
main_shift.end_time, days=i * shift_period
)
# Add the fictive shift to the list of shift
subscribed_shifts.append(shift)
return {
"is_regular": self.is_user_regular(),
"subscribed_shifts": subscribed_shifts,
}
def my_shift_past_shifts(self):
"""
Return template variables for 'beesdoo_website_shift.my_shift_past_shifts' template
"""
# Get current user
cur_user = request.env["res.users"].browse(request.uid)
# Get config
past_shift_limit = 0
if self.is_user_irregular():
past_shift_limit = request.website.irregular_past_shift_limit
if self.is_user_regular():
past_shift_limit = request.website.regular_past_shift_limit
# Get shifts where user was subscribed
now = datetime.now()
if past_shift_limit > 0:
past_shifts = (
request.env["beesdoo.shift.shift"]
.sudo()
.search(
[
(
"start_time",
"<=",
now.strftime("%Y-%m-%d %H:%M:%S"),
),
("worker_id", "=", cur_user.partner_id.id),
],
order="start_time desc, task_template_id, task_type_id",
limit=past_shift_limit,
)
)
else:
past_shifts = (
request.env["beesdoo.shift.shift"]
.sudo()
.search(
[
(
"start_time",
"<=",
now.strftime("%Y-%m-%d %H:%M:%S"),
),
("worker_id", "=", cur_user.partner_id.id),
],
order="start_time desc, task_template_id, task_type_id",
)
)
return {"past_shifts": past_shifts}
def my_shift_worker_status(self):
"""
Return template variables for 'beesdoo_website_shift.my_shift_worker_status_*' template
"""
cur_user = request.env["res.users"].browse(request.uid)
return {"status": cur_user.partner_id.cooperative_status_ids}