diff --git a/event_session/README.rst b/event_session/README.rst index 0041d9b27..f9d80111c 100644 --- a/event_session/README.rst +++ b/event_session/README.rst @@ -33,6 +33,7 @@ Contributors * Sergio Teruel * David Vidal +* Nikos Tsirintanis Maintainer ---------- diff --git a/event_session/__init__.py b/event_session/__init__.py index c4e9a8c2f..1c15bc7ee 100644 --- a/event_session/__init__.py +++ b/event_session/__init__.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - from . import models from . import wizards -from . import reports from . import tests diff --git a/event_session/__manifest__.py b/event_session/__manifest__.py index e607827b9..2c96f20ff 100644 --- a/event_session/__manifest__.py +++ b/event_session/__manifest__.py @@ -1,24 +1,22 @@ -# -*- coding: utf-8 -*- # Copyright 2017 David Vidal # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Event Sessions', - 'version': '10.0.1.0.0', + 'version': '11.0.1.0.0', 'author': 'Tecnativa, ' 'Odoo Community Association (OCA)', "license": "AGPL-3", 'website': 'https://odoo-community.org/', 'category': 'Marketing', 'summary': 'Sessions in events', - 'depends': ['event', 'event_mail'], + 'depends': ['event'], 'data': [ 'security/ir.model.access.csv', 'security/event_session_security.xml', 'views/event_session_view.xml', 'views/event_view.xml', 'wizards/wizard_event_session_view.xml', - 'reports/report_event_registration_view.xml', ], 'installable': True, } diff --git a/event_session/models/__init__.py b/event_session/models/__init__.py index d01dffd0a..26e891099 100644 --- a/event_session/models/__init__.py +++ b/event_session/models/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -from . import event_session from . import event -from . import event_mail +from . import event_session diff --git a/event_session/models/event.py b/event_session/models/event.py index 8d6821fb1..8b59172f7 100644 --- a/event_session/models/event.py +++ b/event_session/models/event.py @@ -1,9 +1,7 @@ -# -*- coding: utf-8 -*- # Copyright 2017 David Vidal # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError +from odoo import api, fields, models class EventEvent(models.Model): @@ -20,6 +18,19 @@ class EventEvent(models.Model): store=True, ) seats_expected = fields.Integer(store=True) + seats_available_expected = fields.Integer( + string='Available expected seats', + compute='_compute_seats_available_expected', + store=True, + readonly=True, + ) + + @api.multi + @api.depends('seats_max', 'seats_expected') + def _compute_seats_available_expected(self): + for event in self: + event.seats_available_expected =\ + event.seats_max - event.seats_expected @api.multi @api.depends('session_ids') @@ -33,38 +44,3 @@ def _check_seats_limit(self): for event in self: if not event.session_ids: return super(EventEvent, event)._check_seats_limit() - - -class EventRegistration(models.Model): - _inherit = 'event.registration' - - event_sessions_count = fields.Integer( - related='event_id.sessions_count', - readonly=True, - ) - session_id = fields.Many2one( - comodel_name='event.session', - 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() diff --git a/event_session/models/event_mail.py b/event_session/models/event_mail.py deleted file mode 100644 index 8eed7d395..000000000 --- a/event_session/models/event_mail.py +++ /dev/null @@ -1,84 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 David Vidal -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, fields, models -import logging - -_logger = logging.getLogger(__name__) - -try: - from odoo.addons.event.models.event_mail import _INTERVALS -except ImportError: - _logger.debug('Can not import events module.') - - -class EventMailScheduler(models.Model): - _inherit = 'event.mail' - - session_id = fields.Many2one( - comodel_name='event.session', - string='Session', - ondelete='cascade', - ) - - @api.multi - @api.depends('mail_sent', 'interval_type', 'event_id.registration_ids', - 'mail_registration_ids') - def _compute_done(self): - super(EventMailScheduler, self)._compute_done() - for event_mail in self: - if (event_mail.session_id and - event_mail.interval_type not in - ['before_event', 'after_event']): - event_mail.done = ( - True if event_mail.event_id.sessions_count > 0 and - not event_mail.session_id else - len(event_mail.mail_registration_ids) == len( - event_mail.session_id.registration_ids) and - all(line.mail_sent for line in - event_mail.mail_registration_ids) - ) - - @api.multi - @api.depends('event_id.state', 'event_id.date_begin', 'interval_type', - 'interval_unit', 'interval_nbr') - def _compute_scheduled_date(self): - super(EventMailScheduler, self)._compute_scheduled_date() - for event_mail in self: - if not event_mail.session_id: - continue - if event_mail.event_id.state not in ['confirm', 'done']: - event_mail.scheduled_date = False - else: - if event_mail.interval_type == 'after_sub': - date, sign = event_mail.session_id.create_date, 1 - elif event_mail.interval_type == 'before_event': - date, sign = event_mail.session_id.date_begin, -1 - else: - date, sign = event_mail.session_id.date_end, 1 - event_mail.scheduled_date = ( - fields.Datetime.from_string(date) + _INTERVALS[ - event_mail.interval_unit - ](sign * event_mail.interval_nbr) - ) - - -class EventMailRegistration(models.Model): - _inherit = 'event.mail.registration' - - @api.multi - @api.depends('registration_id', 'scheduler_id.interval_unit', - 'scheduler_id.interval_type') - def _compute_scheduled_date(self): - super(EventMailRegistration, self)._compute_scheduled_date() - for event_mail_reg in self: - if (event_mail_reg.registration_id and - event_mail_reg.registration_id.session_id): - date_open = event_mail_reg.registration_id.date_open - date_open_datetime = date_open and fields.Datetime.from_string( - date_open) or fields.datetime.now() - event_mail_reg.scheduled_date = ( - date_open_datetime + - _INTERVALS[event_mail_reg.scheduler_id.interval_unit]( - event_mail_reg.scheduler_id.interval_nbr)) diff --git a/event_session/models/event_session.py b/event_session/models/event_session.py index f6a1a983e..7833feaaf 100644 --- a/event_session/models/event_session.py +++ b/event_session/models/event_session.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 David Vidal # Copyright 2017 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -38,32 +37,6 @@ class EventSession(models.Model): [('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", ) @@ -83,18 +56,6 @@ class EventSession(models.Model): date_end_located = fields.Datetime( string='End Date Located', compute='_compute_date_end_located', ) - registration_ids = fields.One2many( - comodel_name='event.registration', - inverse_name='session_id', - string='Attendees', - state={'done': [('readonly', True)]}, - ) - event_mail_ids = fields.One2many( - comodel_name='event.mail', - inverse_name='session_id', - string='Mail Schedule', - copy=True - ) @api.multi @api.depends('date_begin', 'date_end') @@ -114,27 +75,6 @@ def _compute_name(self): name += " - " + date_end.strftime(dt_format) session.name = name.capitalize() - def _session_mails_from_template(self, event_id, mail_template=None): - vals = [(6, 0, [])] - if not mail_template: - mail_template = self.env['ir.values'].get_default( - 'event.config.settings', 'event_mail_template_id') - if not mail_template: - # Not template scheduler defined in event settings - return vals - if isinstance(mail_template, int): - mail_template = self.env['event.mail.template'].browse( - mail_template) - for scheduler in mail_template.scheduler_template_ids: - vals.append((0, 0, { - 'event_id': event_id, - 'interval_nbr': scheduler.interval_nbr, - 'interval_unit': scheduler.interval_unit, - 'interval_type': scheduler.interval_type, - 'template_id': scheduler.template_id.id, - })) - return vals - @api.multi def name_get(self): """Redefine the name_get method to show the event name with the event @@ -145,49 +85,6 @@ def name_get(self): res.append((item.id, "[%s] %s" % (item.event_id.name, item.name))) return res - @api.model - def create(self, vals): - 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 - @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): @@ -222,15 +119,6 @@ def onchange_event_id(self): '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): @@ -251,16 +139,3 @@ def _check_zero_duration(self): raise ValidationError( _("Ending and starting time can't be the same!") ) - - @api.multi - def button_open_registration(self): - """Opens session registrations""" - self.ensure_one() - action = self.env.ref( - 'event.act_event_registration_from_event').read()[0] - action['domain'] = [('id', 'in', self.registration_ids.ids)] - action['context'] = { - 'default_event_id': self.event_id.id, - 'default_session_id': self.id, - } - return action diff --git a/event_session/reports/__init__.py b/event_session/reports/__init__.py deleted file mode 100644 index d544480a3..000000000 --- a/event_session/reports/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import report_event_registration diff --git a/event_session/reports/report_event_registration.py b/event_session/reports/report_event_registration.py deleted file mode 100644 index 37fd382af..000000000 --- a/event_session/reports/report_event_registration.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2016 Sergio Teruel -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo import models, fields - - -class ReportEventRegistration(models.Model): - """Events Analysis""" - _inherit = "report.event.registration" - - session_id = fields.Many2one( - comodel_name='event.session', - string='Session', - required=True, - readonly=True, - ) - seats_expected = fields.Integer(string="Seats expected", readonly=True, - group_operator="max") - seats_max = fields.Integer(group_operator="max") - seats_available = fields.Integer( - string='Available seats', readonly=True, group_operator="min") - seats_available_expected = fields.Integer( - string='Available expected seats', readonly=True, group_operator="min") - session_count = fields.Integer( - string="# of Event Sessions", readonly=True, group_operator="min") - - def _select(self): - return super(ReportEventRegistration, self)._select() + """, - MIN(r.session_id) AS session_id, - COALESCE(MAX(es.seats_expected), - MAX(e.seats_expected)) - AS seats_expected, - COALESCE(MIN(es.seats_available), MIN(e.seats_available)) - AS seats_available, - COALESCE(MAX(es.seats_available_expected), - MAX(e.seats_max - e.seats_expected)) - AS seats_available_expected, - MIN(e.sessions_count) AS session_count - """ - - def _from(self): - from_str = super(ReportEventRegistration, self)._from() - from_str += """ - LEFT JOIN event_session es ON r.session_id = es.id - """ - return from_str - - def _group_by(self): - group_by_str = super(ReportEventRegistration, self)._group_by() - group_by_str = group_by_str.replace('event_id', 'r.event_id') - return group_by_str + ", r.session_id" diff --git a/event_session/reports/report_event_registration_view.xml b/event_session/reports/report_event_registration_view.xml deleted file mode 100644 index 88dfe15c8..000000000 --- a/event_session/reports/report_event_registration_view.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - report.event.registration - - - - - - - - - - - - - - - - - report.event.registration - - - - - - - - - - - - diff --git a/event_session/tests/__init__.py b/event_session/tests/__init__.py index e7f04fd77..b24bf22b9 100644 --- a/event_session/tests/__init__.py +++ b/event_session/tests/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import test_session diff --git a/event_session/tests/test_session.py b/event_session/tests/test_session.py index 0e0b7d72c..d90d0abdb 100644 --- a/event_session/tests/test_session.py +++ b/event_session/tests/test_session.py @@ -1,11 +1,9 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Tecnativa - David Vidal # Copyright 2017 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl-3.0). from odoo.tests import common from odoo.exceptions import ValidationError -from psycopg2 import IntegrityError class EventSession(common.SavepointCase): @@ -29,11 +27,6 @@ def setUpClass(cls): '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, 'mondays': True, @@ -42,20 +35,12 @@ def setUpClass(cls): 'thursdays': True, 'fridays': True, 'sundays': True, - 'saturdays': True, + 'saturdays': False, '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( @@ -83,50 +68,15 @@ 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, - }) + def test_compute_name(self): + vals = { + 'date_begin': '2017-05-28 22:00:00', + 'date_end': '2017-05-28 23:00:00', + } + session = self.env['event.session'].new(vals) + self.assertEqual(session.name, 'Sunday 28/05/17 22:00 - 23:00') + session.date_begin = session.date_end = False + self.assertEqual(session.name, '/') def test_wizard(self): """Test Session Generation Wizard""" @@ -136,17 +86,12 @@ 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(IntegrityError), self.cr.savepoint(): - self.wizard.action_generate_sessions() - 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), 7) + self.assertEqual(len(sessions), 6) 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) @@ -156,6 +101,12 @@ def test_wizard(self): (0, 0, {'start_time': 20.0, 'end_time': 20.0}), ], }) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # hour invalidity + self.wizard.update({'session_hour_ids': [ + (0, 0, {'start_time': 24.0, 'end_time': 24.1}), + ], + }) with self.assertRaises(ValidationError), self.cr.savepoint(): # schedules overlap self.wizard.update({'session_hour_ids': [ @@ -163,6 +114,13 @@ def test_wizard(self): (0, 0, {'start_time': 20.5, 'end_time': 21.5}), ], }) + 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}), + ], + }) with self.assertRaises(ValidationError), self.cr.savepoint(): # weekday not set self.wizard.update({ @@ -175,3 +133,22 @@ def test_wizard(self): 'saturdays': False, }) self.wizard.action_generate_sessions() + + +class EventEvent(common.SavepointCase): + + @classmethod + def setUpClass(cls): + super(EventEvent, cls).setUpClass() + cls.event = cls.env['event.event'].create({ + 'name': 'Another test event', + 'date_begin': '2017-05-26 20:00:00', + 'date_end': '2017-05-30 23:00:00', + 'seats_availability': 'limited', + 'seats_max': '5', + 'seats_min': '1', + }) + + def test_compute_seats_available_expected(self): + self.event.update({'seats_expected': '3'}) + self.assertEqual(self.event.seats_available_expected, 2) diff --git a/event_session/views/event_session_view.xml b/event_session/views/event_session_view.xml index f6693f356..51de5e975 100644 --- a/event_session/views/event_session_view.xml +++ b/event_session/views/event_session_view.xml @@ -23,16 +23,6 @@
-
- -
@@ -40,52 +30,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -103,19 +55,6 @@ - - - - - - diff --git a/event_session/views/event_view.xml b/event_session/views/event_view.xml index b1abcea9e..6f7410519 100644 --- a/event_session/views/event_view.xml +++ b/event_session/views/event_view.xml @@ -1,31 +1,6 @@ - - event.registration - - - - - - - - - - - event.registration - - - - - - - - event.session @@ -48,11 +23,6 @@ - - - - diff --git a/event_session/wizards/__init__.py b/event_session/wizards/__init__.py index f23e87e25..5a6bb71ae 100644 --- a/event_session/wizards/__init__.py +++ b/event_session/wizards/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import wizard_event_session diff --git a/event_session/wizards/wizard_event_session.py b/event_session/wizards/wizard_event_session.py index bdabd697a..653527206 100644 --- a/event_session/wizards/wizard_event_session.py +++ b/event_session/wizards/wizard_event_session.py @@ -70,10 +70,6 @@ class WizardEventSession(models.TransientModel): inverse_name='wizard_event_session_id', string='Hours', ) - event_mail_template_id = fields.Many2one( - comodel_name='event.mail.template', - string='Mail Schedule Template', - ) @api.multi @api.constrains('session_hour_ids') @@ -123,16 +119,6 @@ def _prepare_session_values(self, date_begin, date_end): "seats_max": self.event_id.seats_max, "seats_availability": self.event_id.seats_availability, } - mail_template = ( - self.event_mail_template_id or - self.env['ir.values'].get_default( - 'event.config.settings', 'event_mail_template_id')) - - if mail_template: - template_values = \ - self.env['event.session']._session_mails_from_template( - self.event_id.id, mail_template) - vals['event_mail_ids'] = template_values return vals @api.multi diff --git a/event_session/wizards/wizard_event_session_view.xml b/event_session/wizards/wizard_event_session_view.xml index 4a48085e3..4a7dd1f2f 100644 --- a/event_session/wizards/wizard_event_session_view.xml +++ b/event_session/wizards/wizard_event_session_view.xml @@ -55,7 +55,6 @@ -