mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-23 00:51:59 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
144
odoo-bringout-oca-ocb-event/event/models/event_slot.py
Normal file
144
odoo-bringout-oca-ocb-event/event/models/event_slot.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import pytz
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools.date_utils import float_to_time
|
||||
from odoo.tools import (
|
||||
format_date,
|
||||
format_datetime,
|
||||
formatLang,
|
||||
format_time,
|
||||
)
|
||||
|
||||
|
||||
class EventSlot(models.Model):
|
||||
_name = "event.slot"
|
||||
_description = "Event Slot"
|
||||
_order = "event_id, date, start_hour, end_hour, id"
|
||||
|
||||
event_id = fields.Many2one("event.event", "Event", required=True, ondelete="cascade", index=True)
|
||||
color = fields.Integer("Color", default=0)
|
||||
date = fields.Date("Date", required=True)
|
||||
date_tz = fields.Selection(related="event_id.date_tz")
|
||||
start_hour = fields.Float("Starting Hour", required=True, help="Expressed in the event timezone.")
|
||||
end_hour = fields.Float("Ending Hour", required=True, help="Expressed in the event timezone.")
|
||||
start_datetime = fields.Datetime("Start Datetime", compute="_compute_datetimes", store=True)
|
||||
end_datetime = fields.Datetime("End Datetime", compute="_compute_datetimes", store=True)
|
||||
|
||||
# Registrations
|
||||
is_sold_out = fields.Boolean(
|
||||
"Sold Out", compute="_compute_is_sold_out",
|
||||
help="Whether seats are sold out for this slot.")
|
||||
registration_ids = fields.One2many("event.registration", "event_slot_id", string="Attendees")
|
||||
seats_available = fields.Integer(
|
||||
string="Available Seats",
|
||||
store=False, readonly=True, compute="_compute_seats")
|
||||
seats_reserved = fields.Integer(
|
||||
string="Number of Registrations",
|
||||
store=False, readonly=True, compute="_compute_seats")
|
||||
seats_taken = fields.Integer(
|
||||
string="Number of Taken Seats",
|
||||
store=False, readonly=True, compute="_compute_seats")
|
||||
seats_used = fields.Integer(
|
||||
string="Number of Attendees",
|
||||
store=False, readonly=True, compute="_compute_seats")
|
||||
|
||||
@api.constrains("start_hour", "end_hour")
|
||||
def _check_hours(self):
|
||||
for slot in self:
|
||||
if not (0 <= slot.start_hour <= 23.99 and 0 <= slot.end_hour <= 23.99):
|
||||
raise ValidationError(_("A slot hour must be between 0:00 and 23:59."))
|
||||
if slot.end_hour <= slot.start_hour:
|
||||
raise ValidationError(_("A slot end hour must be later than its start hour.\n%s", slot.display_name))
|
||||
|
||||
@api.constrains("date", "start_hour", "end_hour")
|
||||
def _check_time_range(self):
|
||||
for slot in self:
|
||||
event_start = slot.event_id.date_begin
|
||||
event_end = slot.event_id.date_end
|
||||
if not (event_start <= slot.start_datetime <= event_end) or not (event_start <= slot.end_datetime <= event_end):
|
||||
raise ValidationError(_(
|
||||
"A slot cannot be scheduled outside of its event time range.\n\n"
|
||||
"Event:\t\t%(event_start)s - %(event_end)s\n"
|
||||
"Slot:\t\t%(slot_name)s",
|
||||
event_start=format_datetime(self.env, event_start, tz=slot.date_tz, dt_format='medium'),
|
||||
event_end=format_datetime(self.env, event_end, tz=slot.date_tz, dt_format='medium'),
|
||||
slot_name=slot.display_name,
|
||||
))
|
||||
|
||||
@api.depends("date", "date_tz", "start_hour", "end_hour")
|
||||
def _compute_datetimes(self):
|
||||
for slot in self:
|
||||
event_tz = pytz.timezone(slot.date_tz)
|
||||
start = datetime.combine(slot.date, float_to_time(slot.start_hour))
|
||||
end = datetime.combine(slot.date, float_to_time(slot.end_hour))
|
||||
slot.start_datetime = event_tz.localize(start).astimezone(pytz.UTC).replace(tzinfo=None)
|
||||
slot.end_datetime = event_tz.localize(end).astimezone(pytz.UTC).replace(tzinfo=None)
|
||||
|
||||
@api.depends("seats_available")
|
||||
@api.depends_context('name_with_seats_availability')
|
||||
def _compute_display_name(self):
|
||||
"""Adds slot seats availability if requested by context.
|
||||
Always display the name without availabilities if the event is multi slots
|
||||
because the availability displayed won't be relative to the possible ticket combinations
|
||||
but only relative to the event and this will confuse the user.
|
||||
"""
|
||||
for slot in self:
|
||||
date = format_date(self.env, slot.date, date_format="medium")
|
||||
start = format_time(self.env, float_to_time(slot.start_hour), time_format="short")
|
||||
end = format_time(self.env, float_to_time(slot.end_hour), time_format="short")
|
||||
name = f"{date}, {start} - {end}"
|
||||
if (
|
||||
self.env.context.get('name_with_seats_availability') and slot.event_id.seats_limited
|
||||
and not slot.event_id.is_multi_slots
|
||||
):
|
||||
name = _('%(slot_name)s (Sold out)', slot_name=name) if not slot.seats_available else \
|
||||
_(
|
||||
'%(slot_name)s (%(count)s seats remaining)',
|
||||
slot_name=name,
|
||||
count=formatLang(self.env, slot.seats_available, digits=0),
|
||||
)
|
||||
slot.display_name = name
|
||||
|
||||
@api.depends("event_id.seats_limited", "seats_available")
|
||||
def _compute_is_sold_out(self):
|
||||
for slot in self:
|
||||
slot.is_sold_out = slot.event_id.seats_limited and not slot.seats_available
|
||||
|
||||
@api.depends("event_id", "event_id.seats_max", "registration_ids.state", "registration_ids.active")
|
||||
def _compute_seats(self):
|
||||
# initialize fields to 0
|
||||
for slot in self:
|
||||
slot.seats_reserved = slot.seats_used = slot.seats_available = 0
|
||||
# aggregate registrations by slot and by state
|
||||
state_field = {
|
||||
'open': 'seats_reserved',
|
||||
'done': 'seats_used',
|
||||
}
|
||||
base_vals = dict.fromkeys(state_field.values(), 0)
|
||||
results = {slot_id: dict(base_vals) for slot_id in self.ids}
|
||||
if self.ids:
|
||||
query = """ SELECT event_slot_id, state, count(event_slot_id)
|
||||
FROM event_registration
|
||||
WHERE event_slot_id IN %s AND state IN ('open', 'done') AND active = true
|
||||
GROUP BY event_slot_id, state
|
||||
"""
|
||||
self.env['event.registration'].flush_model(['event_slot_id', 'state', 'active'])
|
||||
self.env.cr.execute(query, (tuple(self.ids),))
|
||||
res = self.env.cr.fetchall()
|
||||
for slot_id, state, num in res:
|
||||
results[slot_id][state_field[state]] = num
|
||||
# compute seats_available
|
||||
for slot in self:
|
||||
slot.update(results.get(slot._origin.id or slot.id, base_vals))
|
||||
if slot.event_id.seats_max > 0:
|
||||
slot.seats_available = slot.event_id.seats_max - (slot.seats_reserved + slot.seats_used)
|
||||
slot.seats_taken = slot.seats_reserved + slot.seats_used
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_if_registrations(self):
|
||||
if self.registration_ids:
|
||||
raise UserError(_(
|
||||
"The following slots cannot be deleted while they have one or more registrations linked to them:\n- %s",
|
||||
'\n- '.join(self.mapped('display_name'))))
|
||||
Loading…
Add table
Add a link
Reference in a new issue