diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/event/tests | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/event/tests')
| -rw-r--r-- | addons/event/tests/__init__.py | 7 | ||||
| -rw-r--r-- | addons/event/tests/common.py | 107 | ||||
| -rw-r--r-- | addons/event/tests/test_event_flow.py | 132 | ||||
| -rw-r--r-- | addons/event/tests/test_event_internals.py | 495 | ||||
| -rw-r--r-- | addons/event/tests/test_event_mail_schedule.py | 258 | ||||
| -rw-r--r-- | addons/event/tests/test_event_security.py | 99 |
6 files changed, 1098 insertions, 0 deletions
diff --git a/addons/event/tests/__init__.py b/addons/event/tests/__init__.py new file mode 100644 index 00000000..72c2d9ab --- /dev/null +++ b/addons/event/tests/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import test_event_flow +from . import test_event_internals +from . import test_event_mail_schedule +from . import test_event_security diff --git a/addons/event/tests/common.py b/addons/event/tests/common.py new file mode 100644 index 00000000..f89f4864 --- /dev/null +++ b/addons/event/tests/common.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import datetime, timedelta + +from odoo import fields +from odoo.addons.mail.tests.common import mail_new_test_user +from odoo.tests import common + + +class TestEventCommon(common.SavepointCase): + + @classmethod + def setUpClass(cls): + super(TestEventCommon, cls).setUpClass() + + # Test users to use through the various tests + cls.user_portal = mail_new_test_user( + cls.env, login='portal_test', + name='Patrick Portal', email='patrick.portal@test.example.com', + notification_type='email', company_id=cls.env.ref("base.main_company").id, + groups='base.group_portal') + cls.user_employee = mail_new_test_user( + cls.env, login='user_employee', + name='Eglantine Employee', email='eglantine.employee@test.example.com', + tz='Europe/Brussels', notification_type='inbox', + company_id=cls.env.ref("base.main_company").id, + groups='base.group_user', + ) + cls.user_eventuser = mail_new_test_user( + cls.env, login='user_eventuser', + name='Ursule EventUser', email='ursule.eventuser@test.example.com', + tz='Europe/Brussels', notification_type='inbox', + company_id=cls.env.ref("base.main_company").id, + groups='base.group_user,event.group_event_user', + ) + cls.user_eventmanager = mail_new_test_user( + cls.env, login='user_eventmanager', + name='Martine EventManager', email='martine.eventmanager@test.example.com', + tz='Europe/Brussels', notification_type='inbox', + company_id=cls.env.ref("base.main_company").id, + groups='base.group_user,event.group_event_manager', + ) + + cls.event_customer = cls.env['res.partner'].create({ + 'name': 'Constantin Customer', + 'email': 'constantin@test.example.com', + 'country_id': cls.env.ref('base.be').id, + 'phone': '0485112233', + 'mobile': False, + }) + cls.event_customer2 = cls.env['res.partner'].create({ + 'name': 'Constantin Customer 2', + 'email': 'constantin2@test.example.com', + 'country_id': cls.env.ref('base.be').id, + 'phone': '0456987654', + 'mobile': '0456654321', + }) + + cls.event_type_complex = cls.env['event.type'].create({ + 'name': 'Update Type', + 'auto_confirm': True, + 'has_seats_limitation': True, + 'seats_max': 30, + 'use_timezone': True, + 'default_timezone': 'Europe/Paris', + 'use_ticket': True, + 'event_type_ticket_ids': [(0, 0, { + 'name': 'First Ticket', + }), (0, 0, { + 'name': 'Second Ticket', + }) + ], + 'use_mail_schedule': True, + 'event_type_mail_ids': [ + (0, 0, { # right at subscription + 'interval_unit': 'now', + 'interval_type': 'after_sub', + 'template_id': cls.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), + (0, 0, { # 1 days before event + 'interval_nbr': 1, + 'interval_unit': 'days', + 'interval_type': 'before_event', + 'template_id': cls.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), + ], + }) + cls.event_0 = cls.env['event.event'].create({ + 'name': 'TestEvent', + 'auto_confirm': True, + 'date_begin': fields.Datetime.to_string(datetime.today() + timedelta(days=1)), + 'date_end': fields.Datetime.to_string(datetime.today() + timedelta(days=15)), + 'date_tz': 'Europe/Brussels', + }) + + # set country in order to format Belgian numbers + cls.event_0.company_id.write({'country_id': cls.env.ref('base.be').id}) + + @classmethod + def _create_registrations(cls, event, reg_count): + # create some registrations + registrations = cls.env['event.registration'].create([{ + 'event_id': event.id, + 'name': 'Test Registration %s' % x, + 'email': '_test_reg_%s@example.com' % x, + 'phone': '04560000%s%s' % (x, x), + } for x in range(0, reg_count)]) + return registrations diff --git a/addons/event/tests/test_event_flow.py b/addons/event/tests/test_event_flow.py new file mode 100644 index 00000000..e1602efe --- /dev/null +++ b/addons/event/tests/test_event_flow.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import datetime + +from dateutil.relativedelta import relativedelta + +from odoo.addons.event.tests.common import TestEventCommon +from odoo.exceptions import ValidationError +from odoo.tests.common import Form +from odoo.tools import mute_logger + + +class TestEventUI(TestEventCommon): + + def test_event_registration_partner_sync(self): + """ Ensure onchange on partner_id is kept for interface, not for computed + fields. """ + registration_form = Form(self.env['event.registration'].with_context( + default_name='WrongName', + default_event_id=self.event_0.id + )) + self.assertEqual(registration_form.event_id, self.event_0) + self.assertEqual(registration_form.name, 'WrongName') + self.assertFalse(registration_form.email) + self.assertFalse(registration_form.phone) + self.assertFalse(registration_form.mobile) + + # trigger onchange + registration_form.partner_id = self.event_customer + self.assertEqual(registration_form.name, self.event_customer.name) + self.assertEqual(registration_form.email, self.event_customer.email) + self.assertEqual(registration_form.phone, self.event_customer.phone) + self.assertEqual(registration_form.mobile, self.event_customer.mobile) + + # save, check record matches Form values + registration = registration_form.save() + self.assertEqual(registration.partner_id, self.event_customer) + self.assertEqual(registration.name, self.event_customer.name) + self.assertEqual(registration.email, self.event_customer.email) + self.assertEqual(registration.phone, self.event_customer.phone) + self.assertEqual(registration.mobile, self.event_customer.mobile) + + # allow writing on some fields independently from customer config + registration.write({'phone': False, 'mobile': False}) + self.assertFalse(registration.phone) + self.assertFalse(registration.mobile) + + # reset partner should not reset other fields + registration.write({'partner_id': False}) + self.assertEqual(registration.partner_id, self.env['res.partner']) + self.assertEqual(registration.name, self.event_customer.name) + self.assertEqual(registration.email, self.event_customer.email) + self.assertFalse(registration.phone) + self.assertFalse(registration.mobile) + + # update to a new partner not through UI -> update only void feilds + registration.write({'partner_id': self.event_customer2.id}) + self.assertEqual(registration.partner_id, self.event_customer2) + self.assertEqual(registration.name, self.event_customer.name) + self.assertEqual(registration.email, self.event_customer.email) + self.assertEqual(registration.phone, self.event_customer2.phone) + self.assertEqual(registration.mobile, self.event_customer2.mobile) + + +class TestEventFlow(TestEventCommon): + + @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') + def test_event_auto_confirm(self): + """ Basic event management with auto confirmation """ + # EventUser creates a new event: ok + test_event = self.env['event.event'].with_user(self.user_eventmanager).create({ + 'name': 'TestEvent', + 'auto_confirm': True, + 'date_begin': datetime.datetime.now() + relativedelta(days=-1), + 'date_end': datetime.datetime.now() + relativedelta(days=1), + 'seats_max': 2, + 'seats_limited': True, + }) + self.assertTrue(test_event.auto_confirm) + + # EventUser create registrations for this event + test_reg1 = self.env['event.registration'].with_user(self.user_eventuser).create({ + 'name': 'TestReg1', + 'event_id': test_event.id, + }) + self.assertEqual(test_reg1.state, 'open', 'Event: auto_confirmation of registration failed') + self.assertEqual(test_event.seats_reserved, 1, 'Event: wrong number of reserved seats after confirmed registration') + test_reg2 = self.env['event.registration'].with_user(self.user_eventuser).create({ + 'name': 'TestReg2', + 'event_id': test_event.id, + }) + self.assertEqual(test_reg2.state, 'open', 'Event: auto_confirmation of registration failed') + self.assertEqual(test_event.seats_reserved, 2, 'Event: wrong number of reserved seats after confirmed registration') + + # EventUser create registrations for this event: too much registrations + with self.assertRaises(ValidationError): + self.env['event.registration'].with_user(self.user_eventuser).create({ + 'name': 'TestReg3', + 'event_id': test_event.id, + }) + + # EventUser validates registrations + test_reg1.action_set_done() + self.assertEqual(test_reg1.state, 'done', 'Event: wrong state of attended registration') + self.assertEqual(test_event.seats_used, 1, 'Event: incorrect number of attendees after closing registration') + test_reg2.action_set_done() + self.assertEqual(test_reg1.state, 'done', 'Event: wrong state of attended registration') + self.assertEqual(test_event.seats_used, 2, 'Event: incorrect number of attendees after closing registration') + + @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') + def test_event_flow(self): + """ Advanced event flow: no auto confirmation, manage minimum / maximum + seats, ... """ + # EventUser creates a new event: ok + test_event = self.env['event.event'].with_user(self.user_eventmanager).create({ + 'name': 'TestEvent', + 'date_begin': datetime.datetime.now() + relativedelta(days=-1), + 'date_end': datetime.datetime.now() + relativedelta(days=1), + 'seats_limited': True, + 'seats_max': 10, + }) + self.assertFalse(test_event.auto_confirm) + + # EventUser create registrations for this event -> no auto confirmation + test_reg1 = self.env['event.registration'].with_user(self.user_eventuser).create({ + 'name': 'TestReg1', + 'event_id': test_event.id, + }) + self.assertEqual( + test_reg1.state, 'draft', + 'Event: new registration should not be confirmed with auto_confirmation parameter being False') diff --git a/addons/event/tests/test_event_internals.py b/addons/event/tests/test_event_internals.py new file mode 100644 index 00000000..5f4c7753 --- /dev/null +++ b/addons/event/tests/test_event_internals.py @@ -0,0 +1,495 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import date, datetime, timedelta +from unittest.mock import patch + +from odoo.addons.event.tests.common import TestEventCommon +from odoo import exceptions +from odoo.fields import Datetime as FieldsDatetime, Date as FieldsDate +from odoo.tests.common import users +from odoo.tools import mute_logger + + +class TestEventData(TestEventCommon): + + @classmethod + def setUpClass(cls): + super(TestEventData, cls).setUpClass() + cls.patcher = patch('odoo.addons.event.models.event_event.fields.Datetime', wraps=FieldsDatetime) + cls.mock_datetime = cls.patcher.start() + cls.mock_datetime.now.return_value = datetime(2020, 1, 31, 10, 0, 0) + cls.addClassCleanup(cls.patcher.stop) + + cls.event_0.write({ + 'date_begin': datetime(2020, 2, 1, 8, 30, 0), + 'date_end': datetime(2020, 2, 4, 18, 45, 0), + }) + + @users('user_eventmanager') + def test_event_date_computation(self): + event = self.event_0.with_user(self.env.user) + event.write({ + 'registration_ids': [(0, 0, {'partner_id': self.event_customer.id, 'name': 'test_reg'})], + 'date_begin': datetime(2020, 1, 31, 15, 0, 0), + 'date_end': datetime(2020, 4, 5, 18, 0, 0), + }) + registration = event.registration_ids[0] + self.assertEqual(registration.get_date_range_str(), u'today') + + event.date_begin = datetime(2020, 2, 1, 15, 0, 0) + self.assertEqual(registration.get_date_range_str(), u'tomorrow') + + event.date_begin = datetime(2020, 2, 2, 6, 0, 0) + self.assertEqual(registration.get_date_range_str(), u'in 2 days') + + event.date_begin = datetime(2020, 2, 20, 17, 0, 0) + self.assertEqual(registration.get_date_range_str(), u'next month') + + event.date_begin = datetime(2020, 3, 1, 10, 0, 0) + self.assertEqual(registration.get_date_range_str(), u'on Mar 1, 2020, 11:00:00 AM') + + # Is actually 8:30 to 20:00 in Mexico + event.write({ + 'date_begin': datetime(2020, 1, 31, 14, 30, 0), + 'date_end': datetime(2020, 2, 1, 2, 0, 0), + 'date_tz': 'Mexico/General' + }) + self.assertTrue(event.is_one_day) + + @users('user_eventmanager') + def test_event_date_timezone(self): + event = self.event_0.with_user(self.env.user) + # Is actually 8:30 to 20:00 in Mexico + event.write({ + 'date_begin': datetime(2020, 1, 31, 14, 30, 0), + 'date_end': datetime(2020, 2, 1, 2, 0, 0), + 'date_tz': 'Mexico/General' + }) + self.assertTrue(event.is_one_day) + self.assertFalse(event.is_ongoing) + + @users('user_eventmanager') + @mute_logger('odoo.models.unlink') + def test_event_configuration_from_type(self): + """ Test data computation of event coming from its event.type template. + Some one2many notably are duplicated from type configuration and some + advanced testing is required, notably mail schedulers. """ + self.assertEqual(self.env.user.tz, 'Europe/Brussels') + + # ------------------------------------------------------------ + # STARTING DATA + # ------------------------------------------------------------ + + event_type = self.env['event.type'].browse(self.event_type_complex.id) + event_type.write({ + 'use_mail_schedule': False, + 'use_ticket': False, + }) + self.assertEqual(event_type.event_type_mail_ids, self.env['event.type.mail']) + self.assertEqual(event_type.event_type_ticket_ids, self.env['event.type.ticket']) + + event = self.env['event.event'].create({ + 'name': 'Event Update Type', + 'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)), + 'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)), + }) + self.assertEqual(event.date_tz, self.env.user.tz) + self.assertFalse(event.seats_limited) + self.assertFalse(event.auto_confirm) + self.assertEqual(event.event_mail_ids, self.env['event.mail']) + self.assertEqual(event.event_ticket_ids, self.env['event.event.ticket']) + + registration = self._create_registrations(event, 1) + self.assertEqual(registration.state, 'draft') # event is not auto confirm + + # ------------------------------------------------------------ + # FILL SYNC TEST + # ------------------------------------------------------------ + + # change template to a one with mails -> fill event as it is void + event_type.write({ + 'use_mail_schedule': True, + 'event_type_mail_ids': [(5, 0), (0, 0, { + 'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')})], + 'use_ticket': True, + 'event_type_ticket_ids': [(5, 0), (0, 0, {'name': 'TestRegistration'})], + }) + event.write({'event_type_id': event_type.id}) + self.assertEqual(event.date_tz, 'Europe/Paris') + self.assertTrue(event.seats_limited) + self.assertEqual(event.seats_max, event_type.seats_max) + self.assertTrue(event.auto_confirm) + # check 2many fields being populated + self.assertEqual(len(event.event_mail_ids), 1) + self.assertEqual(event.event_mail_ids.interval_nbr, 1) + self.assertEqual(event.event_mail_ids.interval_unit, 'days') + self.assertEqual(event.event_mail_ids.interval_type, 'before_event') + self.assertEqual(event.event_mail_ids.template_id, self.env.ref('event.event_reminder')) + self.assertEqual(len(event.event_ticket_ids), 1) + + # update template, unlink from event -> should not impact event + event_type.write({'has_seats_limitation': False}) + self.assertEqual(event_type.seats_max, 0) + self.assertTrue(event.seats_limited) + self.assertEqual(event.seats_max, 30) # original template value + event.write({'event_type_id': False}) + self.assertEqual(event.event_type_id, self.env["event.type"]) + + # set template back -> update event + event.write({'event_type_id': event_type.id}) + self.assertFalse(event.seats_limited) + self.assertEqual(event.seats_max, 0) + self.assertEqual(len(event.event_ticket_ids), 1) + event_ticket1 = event.event_ticket_ids[0] + self.assertEqual(event_ticket1.name, 'TestRegistration') + + # ------------------------------------------------------------ + # RESET TEST + # ------------------------------------------------------------ + + # link registration to ticket + registration.write({'event_ticket_id': event_ticket1.id}) + self.assertEqual(registration.event_ticket_id, event_ticket1) + + # change template to a void one for mails -> reset event lines that are void + # change template to a one with other tickets -> keep line linked to a registration + event_type.write({ + 'use_mail_schedule': False, + 'event_type_mail_ids': [(5, 0)], + 'event_type_ticket_ids': [(5, 0), + (0, 0, {'name': 'Registration1'}), + (0, 0, {'name': 'Registration2'})], + }) + event._compute_event_ticket_ids() + event._compute_event_mail_ids() + self.assertEqual(event.event_mail_ids, self.env['event.mail']) + self.assertEqual(len(event.event_ticket_ids), 3) + self.assertEqual( + set(event.mapped('event_ticket_ids.name')), + set(['TestRegistration', 'Registration1', 'Registration2']) + ) + # registration loose its ticket + self.assertEqual(registration.event_ticket_id, event_ticket1) + + # change template to a one with different mails -> reset event + event_type.write({ + 'use_mail_schedule': True, + 'event_type_mail_ids': [(5, 0), (0, 0, { + 'interval_nbr': 3, 'interval_unit': 'days', 'interval_type': 'after_event', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')})] + }) + event._compute_event_ticket_ids() + event._compute_event_mail_ids() + self.assertEqual(len(event.event_mail_ids), 1) + self.assertEqual(event.event_mail_ids.interval_nbr, 3) + self.assertEqual(event.event_mail_ids.interval_type, 'after_event') + + @users('user_eventmanager') + def test_event_registrable(self): + """Test if `_compute_event_registrations_open` works properly.""" + event = self.event_0.with_user(self.env.user) + event.write({ + 'date_begin': datetime(2020, 1, 30, 8, 0, 0), + 'date_end': datetime(2020, 1, 31, 8, 0, 0), + }) + self.assertFalse(event.event_registrations_open) + event.write({ + 'date_end': datetime(2020, 2, 4, 8, 0, 0), + }) + self.assertTrue(event.event_registrations_open) + + # ticket without dates boundaries -> ok + ticket = self.env['event.event.ticket'].create({ + 'name': 'TestTicket', + 'event_id': event.id, + }) + self.assertTrue(event.event_registrations_open) + + # even with valid tickets, date limits registrations + event.write({ + 'date_begin': datetime(2020, 1, 28, 15, 0, 0), + 'date_end': datetime(2020, 1, 30, 15, 0, 0), + }) + self.assertFalse(event.event_registrations_open) + + # no more seats available + registration = self.env['event.registration'].create({ + 'name': 'Albert Test', + 'event_id': event.id, + }) + registration.action_confirm() + event.write({ + 'date_end': datetime(2020, 2, 1, 15, 0, 0), + 'seats_max': 1, + 'seats_limited': True, + }) + self.assertEqual(event.seats_available, 0) + self.assertFalse(event.event_registrations_open) + + # seats available are back + registration.unlink() + self.assertEqual(event.seats_available, 1) + self.assertTrue(event.event_registrations_open) + + # but tickets are expired + ticket.write({'end_sale_date': datetime(2020, 1, 30, 15, 0, 0)}) + self.assertTrue(ticket.is_expired) + self.assertFalse(event.event_registrations_open) + + @users('user_eventmanager') + def test_event_ongoing(self): + event_1 = self.env['event.event'].create({ + 'name': 'Test Event 1', + 'date_begin': datetime(2020, 1, 25, 8, 0, 0), + 'date_end': datetime(2020, 2, 1, 18, 0, 0), + }) + self.assertTrue(event_1.is_ongoing) + ongoing_event_ids = self.env['event.event']._search([('is_ongoing', '=', True)]) + self.assertIn(event_1.id, ongoing_event_ids) + + event_1.update({'date_begin': datetime(2020, 2, 1, 9, 0, 0)}) + self.assertFalse(event_1.is_ongoing) + ongoing_event_ids = self.env['event.event']._search([('is_ongoing', '=', True)]) + self.assertNotIn(event_1.id, ongoing_event_ids) + + event_2 = self.env['event.event'].create({ + 'name': 'Test Event 2', + 'date_begin': datetime(2020, 1, 25, 8, 0, 0), + 'date_end': datetime(2020, 1, 28, 8, 0, 0), + }) + self.assertFalse(event_2.is_ongoing) + finished_or_upcoming_event_ids = self.env['event.event']._search([('is_ongoing', '=', False)]) + self.assertIn(event_2.id, finished_or_upcoming_event_ids) + + event_2.update({'date_end': datetime(2020, 2, 2, 8, 0, 1)}) + self.assertTrue(event_2.is_ongoing) + finished_or_upcoming_event_ids = self.env['event.event']._search([('is_ongoing', '=', False)]) + self.assertNotIn(event_2.id, finished_or_upcoming_event_ids) + + @users('user_eventmanager') + def test_event_seats(self): + event_type = self.event_type_complex.with_user(self.env.user) + event = self.env['event.event'].create({ + 'name': 'Event Update Type', + 'event_type_id': event_type.id, + 'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)), + 'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)), + }) + + self.assertEqual(event.address_id, self.env.user.company_id.partner_id) + # seats: coming from event type configuration + self.assertTrue(event.seats_limited) + self.assertEqual(event.seats_available, event.event_type_id.seats_max) + self.assertEqual(event.seats_unconfirmed, 0) + self.assertEqual(event.seats_reserved, 0) + self.assertEqual(event.seats_used, 0) + self.assertEqual(event.seats_expected, 0) + + # create registration in order to check the seats computation + self.assertTrue(event.auto_confirm) + for x in range(5): + reg = self.env['event.registration'].create({ + 'event_id': event.id, + 'name': 'reg_open', + }) + self.assertEqual(reg.state, 'open') + reg_draft = self.env['event.registration'].create({ + 'event_id': event.id, + 'name': 'reg_draft', + }) + reg_draft.write({'state': 'draft'}) + reg_done = self.env['event.registration'].create({ + 'event_id': event.id, + 'name': 'reg_done', + }) + reg_done.write({'state': 'done'}) + self.assertEqual(event.seats_available, event.event_type_id.seats_max - 6) + self.assertEqual(event.seats_unconfirmed, 1) + self.assertEqual(event.seats_reserved, 5) + self.assertEqual(event.seats_used, 1) + self.assertEqual(event.seats_expected, 7) + + +class TestEventRegistrationData(TestEventCommon): + + @users('user_eventmanager') + def test_registration_partner_sync(self): + """ Test registration computed fields about partner """ + test_email = '"Nibbler In Space" <nibbler@futurama.example.com>' + test_phone = '0456001122' + + event = self.env['event.event'].browse(self.event_0.ids) + customer = self.env['res.partner'].browse(self.event_customer.id) + + # take all from partner + event.write({ + 'registration_ids': [(0, 0, { + 'partner_id': customer.id, + })] + }) + new_reg = event.registration_ids[0] + self.assertEqual(new_reg.partner_id, customer) + self.assertEqual(new_reg.name, customer.name) + self.assertEqual(new_reg.email, customer.email) + self.assertEqual(new_reg.phone, customer.phone) + + # partial update + event.write({ + 'registration_ids': [(0, 0, { + 'partner_id': customer.id, + 'name': 'Nibbler In Space', + 'email': test_email, + })] + }) + new_reg = event.registration_ids.sorted()[0] + self.assertEqual(new_reg.partner_id, customer) + self.assertEqual( + new_reg.name, 'Nibbler In Space', + 'Registration should take user input over computed partner value') + self.assertEqual( + new_reg.email, test_email, + 'Registration should take user input over computed partner value') + self.assertEqual( + new_reg.phone, customer.phone, + 'Registration should take partner value if not user input') + + # already filled information should not be updated + event.write({ + 'registration_ids': [(0, 0, { + 'name': 'Nibbler In Space', + 'phone': test_phone, + })] + }) + new_reg = event.registration_ids.sorted()[0] + self.assertEqual(new_reg.name, 'Nibbler In Space') + self.assertEqual(new_reg.email, False) + self.assertEqual(new_reg.phone, test_phone) + new_reg.write({'partner_id': customer.id}) + self.assertEqual(new_reg.partner_id, customer) + self.assertEqual(new_reg.name, 'Nibbler In Space') + self.assertEqual(new_reg.email, customer.email) + self.assertEqual(new_reg.phone, test_phone) + + @users('user_eventmanager') + def test_registration_partner_sync_company(self): + """ Test synchronization involving companies """ + event = self.env['event.event'].browse(self.event_0.ids) + customer = self.env['res.partner'].browse(self.event_customer.id) + + # create company structure (using sudo as required partner manager group) + company = self.env['res.partner'].sudo().create({ + 'name': 'Customer Company', + 'is_company': True, + 'type': 'other', + }) + customer.sudo().write({'type': 'invoice', 'parent_id': company.id}) + contact = self.env['res.partner'].sudo().create({ + 'name': 'ContactName', + 'parent_id': company.id, + 'type': 'contact', + 'email': 'ContactEmail <contact.email@test.example.com>', + 'phone': '+32456998877', + }) + + # take all from partner + event.write({ + 'registration_ids': [(0, 0, { + 'partner_id': customer.id, + })] + }) + new_reg = event.registration_ids[0] + self.assertEqual(new_reg.partner_id, customer) + self.assertEqual(new_reg.name, contact.name) + self.assertEqual(new_reg.email, contact.email) + self.assertEqual(new_reg.phone, contact.phone) + + +class TestEventTicketData(TestEventCommon): + + def setUp(self): + super(TestEventTicketData, self).setUp() + self.ticket_date_patcher = patch('odoo.addons.event.models.event_ticket.fields.Date', wraps=FieldsDate) + self.ticket_date_patcher_mock = self.ticket_date_patcher.start() + self.ticket_date_patcher_mock.context_today.return_value = date(2020, 1, 31) + + def tearDown(self): + super(TestEventTicketData, self).tearDown() + self.ticket_date_patcher.stop() + + @users('user_eventmanager') + def test_event_ticket_fields(self): + """ Test event ticket fields synchronization """ + event = self.event_0.with_user(self.env.user) + event.write({ + 'event_ticket_ids': [ + (5, 0), + (0, 0, { + 'name': 'First Ticket', + 'seats_max': 30, + }), (0, 0, { # limited in time, available (01/10 (start) < 01/31 (today) < 02/10 (end)) + 'name': 'Second Ticket', + 'start_sale_date': date(2020, 1, 10), + 'end_sale_date': date(2020, 2, 10), + }) + ], + }) + first_ticket = event.event_ticket_ids.filtered(lambda t: t.name == 'First Ticket') + second_ticket = event.event_ticket_ids.filtered(lambda t: t.name == 'Second Ticket') + + self.assertTrue(first_ticket.seats_limited) + self.assertTrue(first_ticket.sale_available) + self.assertFalse(first_ticket.is_expired) + + self.assertFalse(second_ticket.seats_limited) + self.assertTrue(second_ticket.sale_available) + self.assertFalse(second_ticket.is_expired) + # sale is ended + second_ticket.write({'end_sale_date': date(2020, 1, 20)}) + self.assertFalse(second_ticket.sale_available) + self.assertTrue(second_ticket.is_expired) + # sale has not started + second_ticket.write({ + 'start_sale_date': date(2020, 2, 10), + 'end_sale_date': date(2020, 2, 20), + }) + self.assertFalse(second_ticket.sale_available) + self.assertFalse(second_ticket.is_expired) + # sale started today + second_ticket.write({ + 'start_sale_date': date(2020, 1, 31), + 'end_sale_date': date(2020, 2, 20), + }) + self.assertTrue(second_ticket.sale_available) + self.assertTrue(second_ticket.is_launched()) + self.assertFalse(second_ticket.is_expired) + # incoherent dates are invalid + with self.assertRaises(exceptions.UserError): + second_ticket.write({'end_sale_date': date(2020, 1, 20)}) + + +class TestEventTypeData(TestEventCommon): + + @users('user_eventmanager') + def test_event_type_fields(self): + """ Test event type fields synchronization """ + # create test type and ensure its initial values + event_type = self.env['event.type'].create({ + 'name': 'Testing fields computation', + 'has_seats_limitation': True, + 'seats_max': 30, + 'use_ticket': True, + }) + self.assertTrue(event_type.has_seats_limitation) + self.assertEqual(event_type.seats_max, 30) + self.assertEqual(event_type.event_type_ticket_ids.mapped('name'), ['Registration']) + + # reset seats limitation + event_type.write({'has_seats_limitation': False}) + self.assertFalse(event_type.has_seats_limitation) + self.assertEqual(event_type.seats_max, 0) + + # reset tickets + event_type.write({'use_ticket': False}) + self.assertEqual(event_type.event_type_ticket_ids, self.env['event.type.ticket']) diff --git a/addons/event/tests/test_event_mail_schedule.py b/addons/event/tests/test_event_mail_schedule.py new file mode 100644 index 00000000..5e8e908c --- /dev/null +++ b/addons/event/tests/test_event_mail_schedule.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import datetime +from dateutil.relativedelta import relativedelta +from freezegun import freeze_time + +from odoo.addons.event.tests.common import TestEventCommon +from odoo.addons.mail.tests.common import MockEmail +from odoo.tools import formataddr, mute_logger + + +class TestMailSchedule(TestEventCommon, MockEmail): + + @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') + def test_event_mail_schedule(self): + """ Test mail scheduling for events """ + event_cron_id = self.env.ref('event.event_mail_scheduler') + + # deactivate other schedulers to avoid messing with crons + self.env['event.mail'].search([]).unlink() + + # freeze some datetimes, and ensure more than 1D+1H before event starts + # to ease time-based scheduler check + now = datetime(2021, 3, 20, 14, 30, 15) + event_date_begin = datetime(2021, 3, 22, 8, 0, 0) + event_date_end = datetime(2021, 3, 24, 18, 0, 0) + + with freeze_time(now): + test_event = self.env['event.event'].with_user(self.user_eventmanager).create({ + 'name': 'TestEventMail', + 'auto_confirm': True, + 'date_begin': event_date_begin, + 'date_end': event_date_end, + 'event_mail_ids': [ + (0, 0, { # right at subscription + 'interval_unit': 'now', + 'interval_type': 'after_sub', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), + (0, 0, { # one day after subscription + 'interval_nbr': 1, + 'interval_unit': 'hours', + 'interval_type': 'after_sub', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}), + (0, 0, { # 1 days before event + 'interval_nbr': 1, + 'interval_unit': 'days', + 'interval_type': 'before_event', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), + (0, 0, { # immediately after event + 'interval_nbr': 1, + 'interval_unit': 'hours', + 'interval_type': 'after_event', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')}), + ] + }) + + # check subscription scheduler + after_sub_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_sub'), ('interval_unit', '=', 'now')]) + self.assertEqual(len(after_sub_scheduler), 1, 'event: wrong scheduler creation') + self.assertEqual(after_sub_scheduler.scheduled_date, test_event.create_date) + self.assertTrue(after_sub_scheduler.done) + after_sub_scheduler_2 = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_sub'), ('interval_unit', '=', 'hours')]) + self.assertEqual(len(after_sub_scheduler_2), 1, 'event: wrong scheduler creation') + self.assertEqual(after_sub_scheduler_2.scheduled_date, test_event.create_date + relativedelta(hours=1)) + self.assertTrue(after_sub_scheduler_2.done) + # check before event scheduler + event_prev_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')]) + self.assertEqual(len(event_prev_scheduler), 1, 'event: wrong scheduler creation') + self.assertEqual(event_prev_scheduler.scheduled_date, event_date_begin + relativedelta(days=-1)) + self.assertFalse(event_prev_scheduler.done) + # check after event scheduler + event_next_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_event')]) + self.assertEqual(len(event_next_scheduler), 1, 'event: wrong scheduler creation') + self.assertEqual(event_next_scheduler.scheduled_date, event_date_end + relativedelta(hours=1)) + self.assertFalse(event_next_scheduler.done) + + # create some registrations + with freeze_time(now), self.mock_mail_gateway(): + reg1 = self.env['event.registration'].with_user(self.user_eventuser).create({ + 'event_id': test_event.id, + 'name': 'Reg1', + 'email': 'reg1@example.com', + }) + reg2 = self.env['event.registration'].with_user(self.user_eventuser).create({ + 'event_id': test_event.id, + 'name': 'Reg2', + 'email': 'reg2@example.com', + }) + + # REGISTRATIONS / PRE SCHEDULERS + # -------------------------------------------------- + + # check registration state + self.assertTrue(all(reg.state == 'open' for reg in reg1 + reg2), 'Registrations: should be auto-confirmed') + self.assertTrue(all(reg.date_open == now for reg in reg1 + reg2), 'Registrations: should have open date set to confirm date') + + # verify that subscription scheduler was auto-executed after each registration + self.assertEqual(len(after_sub_scheduler.mail_registration_ids), 2, 'event: should have 2 scheduled communication (1 / registration)') + for mail_registration in after_sub_scheduler.mail_registration_ids: + self.assertEqual(mail_registration.scheduled_date, now) + self.assertTrue(mail_registration.mail_sent, 'event: registration mail should be sent at registration creation') + self.assertTrue(after_sub_scheduler.done, 'event: all subscription mails should have been sent') + + # check emails effectively sent + self.assertEqual(len(self._new_mails), 2, 'event: should have 2 scheduled emails (1 / registration)') + self.assertMailMailWEmails( + [formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))], + 'outgoing', + content=None, + fields_values={'subject': 'Your registration at %s' % test_event.name, + 'email_from': self.user_eventmanager.company_id.email_formatted, + }) + + # same for second scheduler: scheduled but not sent + self.assertEqual(len(after_sub_scheduler_2.mail_registration_ids), 2, 'event: should have 2 scheduled communication (1 / registration)') + for mail_registration in after_sub_scheduler_2.mail_registration_ids: + self.assertEqual(mail_registration.scheduled_date, now + relativedelta(hours=1)) + self.assertFalse(mail_registration.mail_sent, 'event: registration mail should be scheduled, not sent') + self.assertFalse(after_sub_scheduler_2.done, 'event: all subscription mails should be scheduled, not sent') + + # execute event reminder scheduler explicitly, before scheduled date -> should not do anything + with freeze_time(now), self.mock_mail_gateway(): + after_sub_scheduler_2.execute() + self.assertFalse(any(mail_reg.mail_sent for mail_reg in after_sub_scheduler_2.mail_registration_ids)) + self.assertFalse(after_sub_scheduler_2.done) + self.assertEqual(len(self._new_mails), 0, 'event: should not send mails before scheduled date') + + # execute event reminder scheduler explicitly, right at scheduled date -> should sent mails + now_registration = now + relativedelta(hours=1) + with freeze_time(now_registration), self.mock_mail_gateway(): + after_sub_scheduler_2.execute() + + # verify that subscription scheduler was auto-executed after each registration + self.assertEqual(len(after_sub_scheduler_2.mail_registration_ids), 2, 'event: should have 2 scheduled communication (1 / registration)') + self.assertTrue(all(mail_reg.mail_sent for mail_reg in after_sub_scheduler_2.mail_registration_ids)) + # FIXME: field not updated + # self.assertTrue(after_sub_scheduler_2.done, 'event: all subscription mails should have been sent') + + # check emails effectively sent + self.assertEqual(len(self._new_mails), 2, 'event: should have 2 scheduled emails (1 / registration)') + self.assertMailMailWEmails( + [formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))], + 'outgoing', + content=None, + fields_values={'subject': 'Your registration at %s' % test_event.name, + 'email_from': self.user_eventmanager.company_id.email_formatted, + }) + + # PRE SCHEDULERS (MOVE FORWARD IN TIME) + # -------------------------------------------------- + + self.assertFalse(event_prev_scheduler.mail_sent) + self.assertFalse(event_prev_scheduler.done) + + # execute event reminder scheduler explicitly, before scheduled date -> should not do anything + now_start = event_date_begin + relativedelta(hours=-25) + with freeze_time(now_start), self.mock_mail_gateway(): + event_prev_scheduler.execute() + + self.assertFalse(event_prev_scheduler.mail_sent) + self.assertFalse(event_prev_scheduler.done) + self.assertEqual(len(self._new_mails), 0) + + # execute cron to run schedulers + now_start = event_date_begin + relativedelta(hours=-23) + with freeze_time(now_start), self.mock_mail_gateway(): + event_cron_id.method_direct_trigger() + + # check that scheduler is finished + self.assertTrue(event_prev_scheduler.mail_sent, 'event: reminder scheduler should have run') + self.assertTrue(event_prev_scheduler.done, 'event: reminder scheduler should have run') + + # check emails effectively sent + self.assertEqual(len(self._new_mails), 2, 'event: should have scheduled 2 mails (1 / registration)') + self.assertMailMailWEmails( + [formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))], + 'outgoing', + content=None, + fields_values={'subject': '%s: tomorrow' % test_event.name, + 'email_from': self.user_eventmanager.company_id.email_formatted, + }) + + # NEW REGISTRATION EFFECT ON SCHEDULERS + # -------------------------------------------------- + + test_event.write({'auto_confirm': False}) + with freeze_time(now_start), self.mock_mail_gateway(): + reg3 = self.env['event.registration'].with_user(self.user_eventuser).create({ + 'event_id': test_event.id, + 'name': 'Reg3', + 'email': 'reg3@example.com', + }) + + # no more seats + self.assertEqual(reg3.state, 'draft') + + # schedulers state untouched + self.assertTrue(event_prev_scheduler.mail_sent) + self.assertTrue(event_prev_scheduler.mail_sent) + self.assertFalse(event_next_scheduler.mail_sent) + self.assertFalse(event_next_scheduler.done) + self.assertFalse(after_sub_scheduler.done, 'event: scheduler registrations should be lower than effective registrations') + self.assertFalse(after_sub_scheduler_2.done, 'event: scheduler registrations should be lower than effective registrations') + + # confirm registration -> should trigger registration schedulers + # NOTE: currently all schedulers are based on date_open which equals create_date + # meaning several communications may be sent in the time time + with freeze_time(now_start + relativedelta(hours=1)), self.mock_mail_gateway(): + reg3.action_confirm() + + # verify that subscription scheduler was auto-executed after new registration confirmed + self.assertEqual(len(after_sub_scheduler.mail_registration_ids), 3, 'event: should have 3 scheduled communication (1 / registration)') + new_mail_reg = after_sub_scheduler.mail_registration_ids.filtered(lambda mail_reg: mail_reg.registration_id == reg3) + self.assertEqual(new_mail_reg.scheduled_date, now_start) + self.assertTrue(new_mail_reg.mail_sent, 'event: registration mail should be sent at registration creation') + self.assertTrue(after_sub_scheduler.done, 'event: all subscription mails should have been sent') + + # verify that subscription scheduler was auto-executed after new registration confirmed + self.assertEqual(len(after_sub_scheduler_2.mail_registration_ids), 3, 'event: should have 3 scheduled communication (1 / registration)') + new_mail_reg = after_sub_scheduler_2.mail_registration_ids.filtered(lambda mail_reg: mail_reg.registration_id == reg3) + self.assertEqual(new_mail_reg.scheduled_date, now_start + relativedelta(hours=1)) + self.assertTrue(new_mail_reg.mail_sent, 'event: registration mail should be sent at registration creation') + self.assertTrue(after_sub_scheduler_2.done, 'event: all subscription mails should have been sent') + + # check emails effectively sent + self.assertEqual(len(self._new_mails), 2, 'event: should have 1 scheduled emails (new registration only)') + # manual check because 2 identical mails are sent and mail tools do not support it easily + for mail in self._new_mails: + self.assertEqual(mail.email_from, self.user_eventmanager.company_id.email_formatted) + self.assertEqual(mail.subject, 'Your registration at %s' % test_event.name) + self.assertEqual(mail.state, 'outgoing') + self.assertEqual(mail.email_to, formataddr((reg3.name, reg3.email))) + + # POST SCHEDULERS (MOVE FORWARD IN TIME) + # -------------------------------------------------- + + self.assertFalse(event_next_scheduler.mail_sent) + self.assertFalse(event_next_scheduler.done) + + # execute event reminder scheduler explicitly after its schedule date + new_end = event_date_end + relativedelta(hours=2) + with freeze_time(new_end), self.mock_mail_gateway(): + event_cron_id.method_direct_trigger() + + # check that scheduler is finished + self.assertTrue(event_next_scheduler.mail_sent, 'event: reminder scheduler should should have run') + self.assertTrue(event_next_scheduler.done, 'event: reminder scheduler should have run') + + # check emails effectively sent + self.assertEqual(len(self._new_mails), 3, 'event: should have scheduled 3 mails, one for each registration') + self.assertMailMailWEmails( + [formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email)), formataddr((reg3.name, reg3.email))], + 'outgoing', + content=None, + fields_values={'subject': '%s: today' % test_event.name, + 'email_from': self.user_eventmanager.company_id.email_formatted, + }) diff --git a/addons/event/tests/test_event_security.py b/addons/event/tests/test_event_security.py new file mode 100644 index 00000000..573dc57a --- /dev/null +++ b/addons/event/tests/test_event_security.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import datetime +from dateutil.relativedelta import relativedelta + +from odoo.addons.event.tests.common import TestEventCommon +from odoo.exceptions import AccessError +from odoo.tests.common import users +from odoo.tools import mute_logger + + +class TestEventSecurity(TestEventCommon): + + @users('user_employee') + @mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model') + def test_event_access_employee(self): + # employee can read events (sure ?) + event = self.event_0.with_user(self.env.user) + event.read(['name']) + # event.stage_id.read(['name', 'description']) + # event.event_type_id.read(['name', 'has_seats_limitation']) + + with self.assertRaises(AccessError): + self.env['event.event'].create({ + 'name': 'TestEvent', + 'date_begin': datetime.now() + relativedelta(days=-1), + 'date_end': datetime.now() + relativedelta(days=1), + 'seats_limited': True, + 'seats_max': 10, + }) + + with self.assertRaises(AccessError): + event.write({ + 'name': 'TestEvent Modified', + }) + + with self.assertRaises(AccessError): + self.env['event.stage'].create({ + 'name': 'TestStage', + }) + + @users('user_eventuser') + @mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model') + def test_event_access_event_user(self): + event = self.event_0.with_user(self.env.user) + event.read(['name', 'user_id', 'kanban_state_label']) + + with self.assertRaises(AccessError): + self.env['event.event'].create({ + 'name': 'TestEvent', + 'date_begin': datetime.now() + relativedelta(days=-1), + 'date_end': datetime.now() + relativedelta(days=1), + }) + + with self.assertRaises(AccessError): + event.write({ + 'name': 'TestEvent Modified', + }) + + @users('user_eventmanager') + @mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model') + def test_event_access_event_manager(self): + # EventManager can do about everything + event_type = self.env['event.type'].create({ + 'name': 'ManagerEventType', + 'use_mail_schedule': True, + 'event_type_mail_ids': [(5, 0), (0, 0, { + 'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event', + 'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')})] + }) + event = self.env['event.event'].create({ + 'name': 'ManagerEvent', + 'event_type_id': event_type.id, + 'date_begin': datetime.now() + relativedelta(days=-1), + 'date_end': datetime.now() + relativedelta(days=1), + }) + + registration = self.env['event.registration'].create({'event_id': event.id, 'name': 'Myself'}) + registration.write({'name': 'Myself2'}) + + stage = self.env['event.stage'].create({'name': 'test'}) + stage.write({'name': 'ManagerTest'}) + event.write({'stage_id': stage.id}) + + registration.unlink() + event.unlink() + stage.unlink() + event_type.unlink() + + # Settings access rights required to enable some features + self.user_eventmanager.write({'groups_id': [ + (3, self.env.ref('base.group_system').id), + (4, self.env.ref('base.group_erp_manager').id) + ]}) + with self.assertRaises(AccessError): + event_config = self.env['res.config.settings'].with_user(self.user_eventmanager).create({ + }) + event_config.execute() |
