Skip to content

Commit

Permalink
[WIP][ADD] reintroduced seat calculations, added security for new
Browse files Browse the repository at this point in the history
models
  • Loading branch information
ntsirintanis committed Dec 6, 2018
1 parent fabaf8c commit 47171fe
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 11 deletions.
74 changes: 73 additions & 1 deletion event_session/models/event.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Copyright 2017 David Vidal<david.vidal@tecnativa.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError


class EventEvent(models.Model):
Expand All @@ -17,13 +18,63 @@ class EventEvent(models.Model):
string='Total event sessions',
store=True,
)
seats_expected = fields.Integer(store=True)
seats_available_expected = fields.Integer(
compute='_compute_seats_available_expected',
string='Available expected seats',
readonly=True,
store=True,
)
draft_state = fields.Integer(
compute='_compute_state_numbers',
string=' # No of Draft Registrations',
store=True,
)
cancel_state = fields.Integer(
compute='_compute_state_numbers',
string=' # No of Cancelled Registrations',
store=True,
)
confirm_state = fields.Integer(
compute='_compute_state_numbers',
string=' # No of Confirmed Registrations',
store=True,
)

@api.multi
@api.depends('session_ids')
def _compute_sessions_count(self):
for event in self:
event.sessions_count = len(event.session_ids)

@api.multi
@api.constrains('seats_max', 'seats_available')
def _check_seats_limit(self):
for event in self:
if not event.session_ids:
return super(EventEvent, event)._check_seats_limit()

@api.multi
@api.depends('seats_max', 'seats_expected')
def _compute_seats_available_expected(self):
for this in self:
seats = this.seats_max - this.seats_expected
this.seats_available_expected = seats

@api.multi
@api.depends('registration_ids.state')
def _compute_state_numbers(self):
for this in self:
this.draft_state = len(this.registration_ids.filtered(
lambda x: x.state == 'draft'
))
this.cancel_state = len(this.registration_ids.filtered(
lambda x: x.state == 'cancel'
))
this.confirm_state = len(this.registration_ids.filtered(
lambda x: x.state == 'confirm'
))


class EventRegistration(models.Model):
_inherit = 'event.registration'
Expand All @@ -37,3 +88,24 @@ class EventRegistration(models.Model):
string='Session',
ondelete='restrict',
)

@api.multi
@api.constrains('event_id', 'session_id', 'state')
def _check_seats_limit(self):
for registration in self.filtered('session_id'):
if (registration.session_id.seats_availability == 'limited' and
registration.session_id.seats_available < 1 and
registration.state == 'open'):
raise ValidationError(
_('No more seats available for this event.'))

@api.multi
def confirm_registration(self):
for reg in self:
if not reg.event_id.session_ids:
super(EventRegistration, reg).confirm_registration()
reg.state = 'open'
onsubscribe_schedulers = \
reg.session_id.event_mail_ids.filtered(
lambda s: s.interval_type == 'after_sub')
onsubscribe_schedulers.execute()
1 change: 0 additions & 1 deletion event_session/models/event_mail.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2017 David Vidal<david.vidal@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

Expand Down
95 changes: 95 additions & 0 deletions event_session/models/event_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,42 @@ class EventSession(models.Model):
comodel_name='event.event',
string='Event',
)
seats_min = fields.Integer(
string='Minimum seats',
)
seats_max = fields.Integer(
string="Maximum seats",
)
seats_availability = fields.Selection(
[('limited', 'Limited'), ('unlimited', 'Unlimited')],
'Maximum Attendees', required=True, default='unlimited',
)
seats_reserved = fields.Integer(
string='Reserved Seats', store=True, readonly=True,
compute='_compute_seats',
)
seats_available = fields.Integer(
oldname='register_avail', string='Available Seats',
store=True, readonly=True, compute='_compute_seats')
seats_unconfirmed = fields.Integer(
oldname='register_prospect', string='Unconfirmed Seat Reservations',
store=True, readonly=True, compute='_compute_seats')
seats_used = fields.Integer(
oldname='register_attended', string='Number of Participants',
store=True, readonly=True, compute='_compute_seats')
seats_expected = fields.Integer(
string='Number of Expected Attendees',
readonly=True, compute='_compute_seats',
store=True)
seats_available_expected = fields.Integer(
string='Available Expected Seats',
readonly=True, compute='_compute_seats',
store=True)
seats_available_pc = fields.Float(
string='Full %',
readonly=True,
compute='_compute_seats',
)
date_tz = fields.Selection(
string='Timezone', related="event_id.date_tz",
)
Expand Down Expand Up @@ -110,13 +146,60 @@ def name_get(self):

@api.model
def create(self, vals):
# Config availabilities based on event
if vals.get('event_id', False):
event = self.env['event.event'].browse(vals.get('event_id'))
vals['seats_availability'] = event.seats_availability
vals['seats_max'] = event.seats_max
if not vals.get('event_mail_ids', False):
vals.update({
'event_mail_ids':
self._session_mails_from_template(vals['event_id'])
})
return super(EventSession, self).create(vals)

@api.multi
def unlink(self):
for this in self:
if this.registration_ids:
raise ValidationError(_("You are trying to delete one or more \
sessions with active registrations"))
return super(EventSession, self).unlink()

@api.multi
@api.depends('seats_max', 'registration_ids.state')
def _compute_seats(self):
"""Determine reserved, available, reserved but unconfirmed and used
seats by session.
"""
# aggregate registrations by event session and by state
if len(self.ids) > 0:
state_field = {
'draft': 'seats_unconfirmed',
'open': 'seats_reserved',
'done': 'seats_used',
}
result = self.env['event.registration'].read_group([
('session_id', 'in', self.ids),
('state', 'in', ['draft', 'open', 'done'])
], ['state', 'session_id'], ['session_id', 'state'], lazy=False)
for res in result:
session = self.browse(res['session_id'][0])
session[state_field[res['state']]] += res['__count']
# compute seats_available
for session in self:
if session.seats_max > 0:
session.seats_available = session.seats_max - (
session.seats_reserved + session.seats_used)
session.seats_expected = (
session.seats_unconfirmed + session.seats_reserved +
session.seats_used)
session.seats_available_expected = (
session.seats_max - session.seats_expected)
if session.seats_max > 0:
session.seats_available_pc = (
session.seats_expected * 100 / float(session.seats_max))

@api.multi
@api.depends('date_tz', 'date_begin')
def _compute_date_begin_located(self):
Expand Down Expand Up @@ -144,10 +227,22 @@ def _compute_date_end_located(self):
@api.onchange('event_id')
def onchange_event_id(self):
self.update({
'seats_min': self.event_id.seats_min,
'seats_max': self.event_id.seats_max,
'seats_availability': self.event_id.seats_availability,
'date_begin': self.event_id.date_begin,
'date_end': self.event_id.date_end,
})

@api.multi
@api.constrains('seats_max', 'seats_available')
def _check_seats_limit(self):
for session in self:
if (session.seats_availability == 'limited' and
session.seats_max and session.seats_available < 0):
raise ValidationError(
_('No more available seats for this session.'))

@api.multi
@api.constrains('date_begin', 'date_end')
def _check_dates(self):
Expand Down
1 change: 0 additions & 1 deletion event_session/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# © 2016 Sergio Teruel <sergio.teruel@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

Expand Down
1 change: 1 addition & 0 deletions event_session/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_event_session_user,event.session.user,event_session.model_event_session,event.group_event_user,1,0,0,0
access_event_session_admin,event.session.admin,event_session.model_event_session,event.group_event_manager,1,1,1,1
access_event_mail_template,access_event_mail_template,model_event_mail_template,base.group_user,1,0,0,0
83 changes: 79 additions & 4 deletions event_session/tests/test_session.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Copyright 2017 Tecnativa - David Vidal
# Copyright 2017 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl-3.0).

from odoo.tests import common
from odoo.exceptions import ValidationError
Expand All @@ -15,11 +15,22 @@ def setUpClass(cls):
'name': 'Test event',
'date_begin': '2017-05-26 20:00:00',
'date_end': '2017-05-30 22:00:00',
'seats_availability': 'limited',
'seats_max': '5',
'seats_min': '1',
})
cls.session = cls.env['event.session'].create({
'date_begin': '2017-05-26 20:00:00',
'date_end': '2017-05-26 22:00:00',
'event_id': cls.event.id,
'seats_availability': cls.event.seats_availability,
'seats_max': cls.event.seats_max,
'seats_min': cls.event.seats_min,
})
cls.attendee = cls.env['event.registration'].create({
'name': 'Test attendee',
'event_id': cls.event.id,
'session_id': cls.session.id,
})
cls.wizard = cls.env['wizard.event.session'].create({
'event_id': cls.event.id,
Expand All @@ -29,12 +40,20 @@ def setUpClass(cls):
'thursdays': True,
'fridays': True,
'sundays': True,
'saturdays': False,
'saturdays': True,
'delete_existing_sessions': False,
'session_hour_ids': [
(0, 0, {'start_time': 20.0, 'end_time': 21.0}),
],
})
cls.template = cls.env['event.mail.template'].create({
'name': 'Template test 01',
'scheduler_template_ids': [(0, 0, {
'interval_nbr': 15,
'interval_unit': 'days',
'interval_type': 'before_event',
'template_id': cls.env.ref('event.event_reminder').id})],
})

def test_session_name_get(self):
self.assertEqual(
Expand Down Expand Up @@ -62,6 +81,52 @@ def test_check_zero_duration(self):
'date_end': '2017-05-28 22:00:00',
})

def test_open_registrations(self):
# registrations button
res = self.session.button_open_registration()
attendees = self.env['event.registration'].search([
['session_id', '=', self.session.id]
])
self.assertEqual(res['domain'], [('id', 'in', attendees.ids)])

def test_assign_mail_template(self):
vals = ({
'event_mail_ids':
self.session._session_mails_from_template(self.event.id)
})
self.session.write(vals)
self.assertEqual(len(self.session.event_mail_ids), 0)
vals = ({
'event_mail_ids':
self.session._session_mails_from_template(self.event.id,
self.template)
})
self.session.write(vals)
self.assertEqual(len(self.session.event_mail_ids), 1)

def test_session_seats(self):
""" Session seat """
self.assertEqual(
self.event.seats_available,
self.session.seats_available)
self.assertEqual(
self.event.seats_unconfirmed,
self.session.seats_unconfirmed
)
self.assertEqual(
self.event.seats_used,
self.session.seats_used
)
with self.assertRaises(ValidationError), self.cr.savepoint():
# check limit regs
for i in range(int(self.session.seats_available)+1):
self.env['event.registration'].create({
'name': 'Test Attendee',
'event_id': self.event.id,
'session_id': self.session.id,
'state': 'open',
})

def test_compute_name(self):
vals = {
'date_begin': '2017-05-28 22:00:00',
Expand All @@ -80,11 +145,22 @@ def test_wizard(self):
self.wizard.action_generate_sessions()
# delete previous sessions
self.wizard.update({'delete_existing_sessions': True})
self.wizard.update({'event_mail_template_id': self.template})
with self.assertRaises(ValidationError) as error, self.cr.savepoint():
self.wizard.action_generate_sessions()
self.assertEqual(error, "You are trying to delete one or more \
sessions with active registrations")
self.attendee.session_id = False
self.wizard.action_generate_sessions()
sessions = self.env['event.session'].search([
['event_id', '=', self.event.id]
])
self.assertEqual(len(sessions), 6)
self.assertEqual(len(sessions), 7)
for session in sessions:
self.assertTrue(session.event_mail_ids)
self.assertEqual(session.seats_max, self.event.seats_max)
self.assertEqual(session.seats_availability,
self.event.seats_availability)
with self.assertRaises(ValidationError), self.cr.savepoint():
# session duration = 0
self.wizard.update({'session_hour_ids': [
Expand All @@ -105,7 +181,6 @@ def test_wizard(self):
],
})
with self.assertRaises(ValidationError), self.cr.savepoint():
# schedules overlap
self.wizard.update({'session_hour_ids': [
(0, 0, {'start_time': 20.0, 'end_time': 21.0}),
(0, 0, {'start_time': 19.5, 'end_time': 21.5}),
Expand Down

0 comments on commit 47171fe

Please sign in to comment.