diff --git a/addons/event/models/event_event.py b/addons/event/models/event_event.py index e5f89ecf884..a1fd7ae8df7 100644 --- a/addons/event/models/event_event.py +++ b/addons/event/models/event_event.py @@ -24,21 +24,6 @@ class EventType(models.Model): _description = 'Event Category' _order = 'sequence, id' - @api.model - def _get_default_event_type_mail_ids(self): - return [(0, 0, { - 'notification_type': 'mail', - 'interval_unit': 'now', - 'interval_type': 'after_sub', - 'template_id': self.env.ref('event.event_subscription').id, - }), (0, 0, { - 'notification_type': 'mail', - 'interval_nbr': 10, - 'interval_unit': 'days', - 'interval_type': 'before_event', - 'template_id': self.env.ref('event.event_reminder').id, - })] - name = fields.Char('Event Category', required=True, translate=True) sequence = fields.Integer() # tickets @@ -50,7 +35,8 @@ class EventType(models.Model): # registration has_seats_limitation = fields.Boolean('Limited Seats') default_registration_max = fields.Integer( - 'Maximum Registrations', default=0, + 'Maximum Registrations', compute='_compute_default_registration', + copy=True, readonly=False, store=True, help="It will select this default maximum value when you choose this event") auto_confirm = fields.Boolean( 'Automatically Confirm Registrations', default=True, @@ -66,9 +52,28 @@ class EventType(models.Model): use_mail_schedule = fields.Boolean( 'Automatically Send Emails', default=True) event_type_mail_ids = fields.One2many( - 'event.type.mail', 'event_type_id', string='Mail Schedule', - copy=False, - default=lambda self: self._get_default_event_type_mail_ids()) + 'event.type.mail', 'event_type_id', + string='Mail Schedule', compute='_compute_event_type_mail_ids', + readonly=False, store=True) + + @api.depends('use_mail_schedule') + def _compute_event_type_mail_ids(self): + for template in self: + if not template.use_mail_schedule: + template.event_type_mail_ids = [(5, 0)] + elif not template.event_type_mail_ids: + template.event_type_mail_ids = [(0, 0, { + 'notification_type': 'mail', + 'interval_unit': 'now', + 'interval_type': 'after_sub', + 'template_id': self.env.ref('event.event_subscription').id, + }), (0, 0, { + 'notification_type': 'mail', + 'interval_nbr': 10, + 'interval_unit': 'days', + 'interval_type': 'before_event', + 'template_id': self.env.ref('event.event_reminder').id, + })] @api.depends('use_ticket') def _compute_event_type_ticket_ids(self): @@ -80,10 +85,11 @@ class EventType(models.Model): 'name': _('Registration'), })] - @api.onchange('has_seats_limitation') - def _onchange_has_seats_limitation(self): - if not self.has_seats_limitation: - self.default_registration_max = 0 + @api.depends('has_seats_limitation') + def _compute_default_registration(self): + for template in self: + if not template.has_seats_limitation: + template.default_registration_max = 0 class EventEvent(models.Model): @@ -114,10 +120,14 @@ class EventEvent(models.Model): domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]") event_type_id = fields.Many2one('event.type', string='Category', ondelete='set null') color = fields.Integer('Kanban Color Index') - event_mail_ids = fields.One2many('event.mail', 'event_id', string='Mail Schedule', copy=True) + event_mail_ids = fields.One2many( + 'event.mail', 'event_id', string='Mail Schedule', copy=True, + compute='_compute_from_event_type', readonly=False, store=True) # Kanban fields kanban_state = fields.Selection([('normal', 'In Progress'), ('done', 'Done'), ('blocked', 'Blocked')], default='normal') - kanban_state_label = fields.Char(compute='_compute_kanban_state_label', string='Kanban State Label', tracking=True, store=True) + kanban_state_label = fields.Char( + string='Kanban State Label', compute='_compute_kanban_state_label', + store=True, tracking=True) stage_id = fields.Many2one( 'event.stage', ondelete='restrict', default=_get_default_stage_id, group_expand='_read_group_stage_ids', tracking=True) @@ -127,10 +137,12 @@ class EventEvent(models.Model): # Seats and computation seats_max = fields.Integer( string='Maximum Attendees Number', + compute='_compute_from_event_type', copy=True, readonly=False, store=True, help="For each event you can define a maximum registration of seats(number of attendees), above this numbers the registrations are not accepted.") seats_availability = fields.Selection( [('unlimited', 'Unlimited'), ('limited', 'Limited')], - 'Maximum Attendees', required=True, default='unlimited') + string='Maximum Attendees', required=True, + compute='_compute_seats_availability', copy=True, readonly=False, store=True) seats_reserved = fields.Integer( string='Reserved Seats', store=True, readonly=True, compute='_compute_seats') @@ -147,16 +159,18 @@ class EventEvent(models.Model): string='Number of Expected Attendees', compute_sudo=True, readonly=True, compute='_compute_seats') # Registration fields - auto_confirm = fields.Boolean(string='Autoconfirm Registrations') + auto_confirm = fields.Boolean( + string='Autoconfirm Registrations', + compute='_compute_from_event_type', copy=True, readonly=False, store=True) registration_ids = fields.One2many('event.registration', 'event_id', string='Attendees') event_registrations_open = fields.Boolean('Registration open', compute='_compute_event_registrations_open') event_ticket_ids = fields.One2many( - 'event.event.ticket', 'event_id', string='Event Ticket', - copy=True) + 'event.event.ticket', 'event_id', string='Event Ticket', copy=True, + compute='_compute_from_event_type', readonly=False, store=True) # Date fields date_tz = fields.Selection( _tz_get, string='Timezone', required=True, - default=lambda self: self.env.user.tz or 'UTC') + compute='_compute_date_tz', copy=True, readonly=False, store=True) date_begin = fields.Datetime(string='Start Date', required=True, tracking=True) date_end = fields.Datetime(string='End Date', required=True, tracking=True) date_begin_located = fields.Char(string='Start Date Located', compute='_compute_date_begin_tz') @@ -165,12 +179,16 @@ class EventEvent(models.Model): is_one_day = fields.Boolean(compute='_compute_field_is_one_day') start_sale_date = fields.Date('Start sale date', compute='_compute_start_sale_date') # Location and communication - is_online = fields.Boolean('Online Event') + is_online = fields.Boolean( + string='Online Event', compute='_compute_from_event_type', + copy=True, readonly=False, store=True) address_id = fields.Many2one( - 'res.partner', string='Venue', tracking=True, - default=lambda self: self.env.company.partner_id, + 'res.partner', string='Venue', compute='_compute_address_id', + copy=True, readonly=False, store=True, tracking=True, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]") - country_id = fields.Many2one('res.country', 'Country', related='address_id.country_id', store=True, readonly=False) + country_id = fields.Many2one( + 'res.country', 'Country', related='address_id.country_id', + copy=True, readonly=False, store=True) # badge fields badge_front = fields.Html(string='Badge Front') badge_back = fields.Html(string='Badge Back') @@ -282,47 +300,86 @@ class EventEvent(models.Model): start_dates = [ticket.start_sale_date for ticket in event.event_ticket_ids if ticket.start_sale_date] event.start_sale_date = min(start_dates) if start_dates else False - @api.onchange('is_online') - def _onchange_is_online(self): - if self.is_online: - self.address_id = False + @api.depends('is_online') + def _compute_address_id(self): + for event in self: + if event.is_online: + event.address_id = False + elif not event.address_id: + event.address_id = self.env.company.partner_id.id - @api.onchange('event_type_id') - def _onchange_type(self): - if self.event_type_id: - self.seats_max = self.event_type_id.default_registration_max - if self.event_type_id.default_registration_max: - self.seats_availability = 'limited' + @api.depends('event_type_id') + def _compute_date_tz(self): + for event in self: + if event.event_type_id.use_timezone: + event.date_tz = event.event_type_id.default_timezone + if not event.date_tz: + event.date_tz = self.env.user.tz or 'UTC' - if self.event_type_id.auto_confirm: - self.auto_confirm = self.event_type_id.auto_confirm + @api.depends('event_type_id') + def _compute_seats_availability(self): + """ Make it separate from ``_compute_from_event_type`` because otherwise + a value given at create (see create override) would protect all other fields + depending on event type id from being computed as compute method will be + blacklisted during create (see ``_field_computed`` attribute used in create + to compute protected field from re-computation) """ + for event in self: + if event.event_type_id.default_registration_max: + event.seats_availability = 'limited' + if not event.seats_availability: + event.seats_availability = 'unlimited' - if self.event_type_id.use_timezone: - self.date_tz = self.event_type_id.default_timezone + @api.depends('event_type_id') + def _compute_from_event_type(self): + """ Update event configuration from its event type. Depends are set only + on event_type_id itself, not its sub fields. Indeed purpose is to emulate + an onchange: if event type is changed, update event configuration. Changing + event type content itself should not trigger this method. - self.is_online = self.event_type_id.is_online + Updated by this method + * seats_max -> triggers _compute_seats (all seats computation) + * auto_confirm + * is_online -> triggers _compute_address_id (address_id computation) + * event_mail_ids + * event_ticket_ids -> triggers _compute_start_sale_date (start_sale_date computation) + """ + for event in self: + if not event.event_type_id: + if not event.seats_max: + event.seats_max = 0 + if not event.is_online: + event.is_online = False + if not event.event_ticket_ids: + event.event_ticket_ids = False + continue - if self.event_type_id.use_mail_schedule and self.event_type_id.event_type_mail_ids: - self.event_mail_ids = [(5, 0, 0)] + [ + if event.event_type_id.default_registration_max: + event.seats_max = event.event_type_id.default_registration_max + + if event.event_type_id.auto_confirm: + event.auto_confirm = event.event_type_id.auto_confirm + + event.is_online = event.event_type_id.is_online + + # compute mailing information (force only if activated and mailing defined) + if event.event_type_id.use_mail_schedule and event.event_type_id.event_type_mail_ids: + event.event_mail_ids = [(5, 0, 0)] + [ (0, 0, { attribute_name: line[attribute_name] if not isinstance(line[attribute_name], models.BaseModel) else line[attribute_name].id for attribute_name in self.env['event.type.mail']._get_event_mail_fields_whitelist() }) - for line in self.event_type_id.event_type_mail_ids] + for line in event.event_type_id.event_type_mail_ids] - # compute tickets information - if self.event_type_id.use_ticket: - all_ticket_values = [] - for ticket in self.event_type_id.event_type_ticket_ids: - ticket_vals = dict( - (attribute_name, ticket[attribute_name] if not isinstance(ticket[attribute_name], models.BaseModel) else ticket[attribute_name].id) + # compute tickets information (force only if activated and tickets defined) + if event.event_type_id.use_ticket and event.event_type_id.event_type_ticket_ids: + event.event_ticket_ids = [(5, 0, 0)] + [ + (0, 0, { + attribute_name: line[attribute_name] if not isinstance(line[attribute_name], models.BaseModel) else line[attribute_name].id for attribute_name in self.env['event.type.ticket']._get_event_ticket_fields_whitelist() - ) - all_ticket_values.append(ticket_vals) + }) + for line in event.event_type_id.event_type_ticket_ids] - self.event_ticket_ids = [(5, 0, 0)] + [(0, 0, item) for item in all_ticket_values] - - @api.constrains('seats_max', 'seats_available') + @api.constrains('seats_max', 'seats_available', 'seats_availability') def _check_seats_limit(self): if any(event.seats_availability == 'limited' and event.seats_max and event.seats_available < 0 for event in self): raise ValidationError(_('No more available seats.')) @@ -350,6 +407,9 @@ class EventEvent(models.Model): @api.model def create(self, vals): + # Temporary fix for ``seats_availability`` and ``date_tz`` required fields (see ``_compute_from_event_type`` + vals.update(self._sync_required_computed(vals)) + res = super(EventEvent, self).create(vals) if res.organizer_id: res.message_subscribe([res.organizer_id.id]) @@ -367,6 +427,17 @@ class EventEvent(models.Model): default = dict(default or {}, name=_("%s (copy)") % (self.name)) return super(EventEvent, self).copy(default) + def _sync_required_computed(self, values): + """ Call compute fields in cache to find missing values for required fields + (seats_availability and date_tz) in case they are not given in values """ + missing_fields = list(set(['seats_availability', 'date_tz']).difference(set(values.keys()))) + if missing_fields and values: + cache_event = self.new(values) + cache_event._compute_from_event_type() + return dict((fname, cache_event[fname]) for fname in missing_fields) + else: + return {} + def action_set_done(self): """ Action which will move the events diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py index 5f6c32a620b..b9d88dab020 100644 --- a/addons/event/models/event_registration.py +++ b/addons/event/models/event_registration.py @@ -29,13 +29,17 @@ class EventRegistration(models.Model): partner_id = fields.Many2one( 'res.partner', string='Contact', states={'done': [('readonly', True)]}) - name = fields.Char(string='Attendee Name', index=True, required=True, tracking=True) - email = fields.Char(string='Email') - phone = fields.Char(string='Phone') - mobile = fields.Char(string='Mobile') + name = fields.Char( + string='Attendee Name', index=True, + compute='_compute_contact_info', copy=True, readonly=False, store=True, tracking=True) + email = fields.Char(string='Email', compute='_compute_contact_info', copy=True, readonly=False, store=True) + phone = fields.Char(string='Phone', compute='_compute_contact_info', copy=True, readonly=False, store=True) + mobile = fields.Char(string='Mobile', compute='_compute_contact_info', copy=True, readonly=False, store=True) # organization date_open = fields.Datetime(string='Registration Date', readonly=True, default=lambda self: fields.Datetime.now()) # weird crash is directly now - date_closed = fields.Datetime(string='Attended Date', readonly=True) + date_closed = fields.Datetime( + string='Attended Date', compute='_compute_date_closed', + copy=True, readonly=False, store=True) event_begin_date = fields.Datetime(string="Event Start Date", related='event_id.date_begin', readonly=True) event_end_date = fields.Datetime(string="Event End Date", related='event_id.date_end', readonly=True) company_id = fields.Many2one( @@ -46,11 +50,23 @@ class EventRegistration(models.Model): ('open', 'Confirmed'), ('done', 'Attended')], string='Status', default='draft', readonly=True, copy=False, tracking=True) - @api.onchange('event_id') - def _onchange_event_id(self): - # We reset the ticket when keeping it would lead to an inconstitent state. - if self.event_ticket_id and (not self.event_id or self.event_id != self.event_ticket_id.event_id): - self.event_ticket_id = None + @api.depends('partner_id') + def _compute_contact_info(self): + for registration in self: + if registration.partner_id: + partner_vals = self._synchronize_partner_values(registration.partner_id) + registration.update( + dict((fname, fvalue) + for fname, fvalue in partner_vals.items() + if fvalue and not (registration[fname] or registration._origin[fname]) + ) + ) + + @api.depends('state') + def _compute_date_closed(self): + for registration in self: + if registration.state == 'done' and not registration.date_closed: + registration.date_closed = fields.Datetime.now() @api.constrains('event_id', 'state') def _check_seats_limit(self): @@ -64,10 +80,10 @@ class EventRegistration(models.Model): if record.event_ticket_id.seats_max and record.event_ticket_id.seats_available < 0: raise ValidationError(_('No more available seats for this ticket')) - @api.onchange('partner_id') - def _onchange_partner(self): - if self.partner_id: - self.update(self._synchronize_partner_values(self.partner_id)) + @api.constrains('event_id', 'event_ticket_id') + def _check_event_ticket(self): + if any(registration.event_id != registration.event_ticket_id.event_id for registration in self if registration.event_ticket_id): + raise ValidationError(_('Invalid event / ticket choice')) # ------------------------------------------------------------ # CRUD @@ -75,13 +91,6 @@ class EventRegistration(models.Model): @api.model def create(self, vals): - # update missing pieces of information from partner - if vals.get('partner_id'): - partner_vals = self._synchronize_partner_values( - self.env['res.partner'].browse(vals['partner_id']) - ) - vals = dict(partner_vals, **vals) - registration = super(EventRegistration, self).create(vals) if registration._check_auto_confirmation(): registration.sudo().action_confirm() @@ -89,21 +98,8 @@ class EventRegistration(models.Model): return registration def write(self, vals): - if vals.get('state') == 'done' and 'date_closed' not in vals: - vals['date_closed'] = fields.Datetime.now() - ret = super(EventRegistration, self).write(vals) - # update missing pieces of information from partner - if vals.get('partner_id'): - partner_vals = self._synchronize_partner_values( - self.env['res.partner'].browse(vals['partner_id']) - ) - for registration in self: - partner_info = dict((key, val) for key, val in partner_vals.items() if not registration[key]) - if partner_info: - registration.write(partner_info) - if vals.get('state') == 'open': # auto-trigger after_sub (on subscribe) mail schedulers, if needed onsubscribe_schedulers = self.mapped('event_id.event_mail_ids').filtered(lambda s: s.interval_type == 'after_sub') diff --git a/addons/event/tests/common.py b/addons/event/tests/common.py index f58722efcd1..8c32e558185 100644 --- a/addons/event/tests/common.py +++ b/addons/event/tests/common.py @@ -58,7 +58,7 @@ class TestEventCommon(common.SavepointCase): 'default_timezone': 'Europe/Paris', 'use_ticket': True, 'event_type_ticket_ids': [(0, 0, { - 'name': 'First Ticket', + 'name': 'First Ticket', }), (0, 0, { 'name': 'Second Ticket', }) diff --git a/addons/event/tests/test_event_flow.py b/addons/event/tests/test_event_flow.py index 572e64a8231..1ec9208e1b3 100644 --- a/addons/event/tests/test_event_flow.py +++ b/addons/event/tests/test_event_flow.py @@ -24,6 +24,7 @@ class TestEventFlow(TestEventCommon): 'seats_max': 2, 'seats_availability': 'limited', }) + self.assertTrue(test_event.auto_confirm) # EventUser create registrations for this event test_reg1 = self.env['event.registration'].with_user(self.user_eventuser).create({ @@ -56,15 +57,17 @@ class TestEventFlow(TestEventCommon): @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') def test_event_flow(self): - """ Avanced event flow: no auto confirmation, manage minimum / maximum + """ 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_availability': 'limited', '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({ diff --git a/addons/event/tests/test_event_internals.py b/addons/event/tests/test_event_internals.py index 95e935972b3..b0ab686fa76 100644 --- a/addons/event/tests/test_event_internals.py +++ b/addons/event/tests/test_event_internals.py @@ -58,7 +58,7 @@ class TestEventData(TestEventCommon): 'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)), 'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)), }) - event._onchange_type() + self.assertFalse(event.is_online) self.assertEqual(event.address_id, self.env.user.company_id.partner_id) # seats: coming from event type configuration @@ -71,7 +71,6 @@ class TestEventData(TestEventCommon): # set is_online: should reset the address_id field event.update({'is_online': True}) - event._onchange_is_online() self.assertTrue(event.is_online) self.assertFalse(event.address_id) @@ -112,14 +111,12 @@ class TestEventData(TestEventCommon): event_type.write({ 'use_mail_schedule': False, }) - # Event type does not use mail schedule but data is kept for compatibility and avoid recreating them - self.assertEqual(len(event_type.event_type_mail_ids), 2) + self.assertEqual(event_type.event_type_mail_ids, self.env['event.type.mail']) 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)), - 'auto_confirm': False, 'is_online': True, }) self.assertEqual(event.date_tz, self.env.user.tz) @@ -128,22 +125,18 @@ class TestEventData(TestEventCommon): self.assertTrue(event.is_online) self.assertEqual(event.event_mail_ids, self.env['event.mail']) - event.update({'event_type_id': event_type.id}) - event._onchange_type() - self.assertEqual(event.date_tz, 'Europe/Paris') - self.assertEqual(event.seats_availability, 'limited') - self.assertEqual(event.seats_max, event_type.default_registration_max) - self.assertTrue(event.auto_confirm) - self.assertFalse(event.is_online) - self.assertEqual(event.event_mail_ids, self.env['event.mail']) - 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')})] }) - event._onchange_type() + event.write({'event_type_id': event_type.id}) + self.assertEqual(event.date_tz, 'Europe/Paris') + self.assertEqual(event.seats_availability, 'limited') + self.assertEqual(event.seats_max, event_type.default_registration_max) + self.assertTrue(event.auto_confirm) + self.assertFalse(event.is_online) 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') @@ -194,7 +187,7 @@ class TestEventData(TestEventCommon): self.assertFalse(event.event_registrations_open) @users('user_eventmanager') - def test_ongoing_events(self): + def test_event_ongoing(self): self.patcher = patch('odoo.addons.event.models.event_event.fields.Datetime', wraps=FieldsDatetime) self.mock_datetime = self.patcher.start() self.mock_datetime.now.return_value = datetime(2020, 1, 10, 8, 0, 0) @@ -297,14 +290,12 @@ class TestEventTypeData(TestEventCommon): 'default_registration_max': 30, 'use_ticket': True, }) - event_type._onchange_has_seats_limitation() self.assertTrue(event_type.has_seats_limitation) self.assertEqual(event_type.default_registration_max, 30) self.assertEqual(event_type.event_type_ticket_ids.mapped('name'), ['Registration']) # reset seats limitation event_type.write({'has_seats_limitation': False}) - event_type._onchange_has_seats_limitation() self.assertFalse(event_type.has_seats_limitation) self.assertEqual(event_type.default_registration_max, 0) diff --git a/addons/event/tests/test_event_mail_schedule.py b/addons/event/tests/test_event_mail_schedule.py index ba1223ccc0f..42e1d18017d 100644 --- a/addons/event/tests/test_event_mail_schedule.py +++ b/addons/event/tests/test_event_mail_schedule.py @@ -22,7 +22,6 @@ class TestMailSchedule(TestEventCommon): 'auto_confirm': True, 'date_begin': event_date_begin, 'date_end': event_date_end, - 'seats_max': 10, 'event_mail_ids': [ (0, 0, { # right at subscription 'interval_unit': 'now', diff --git a/addons/event/tests/test_event_security.py b/addons/event/tests/test_event_security.py index d2ac6d275a3..50cf81a84ec 100644 --- a/addons/event/tests/test_event_security.py +++ b/addons/event/tests/test_event_security.py @@ -26,6 +26,7 @@ class TestEventSecurity(TestEventCommon): 'name': 'TestEvent', 'date_begin': datetime.now() + relativedelta(days=-1), 'date_end': datetime.now() + relativedelta(days=1), + 'seats_availability': 'limited', 'seats_max': 10, }) @@ -50,7 +51,6 @@ class TestEventSecurity(TestEventCommon): 'name': 'TestEvent', 'date_begin': datetime.now() + relativedelta(days=-1), 'date_end': datetime.now() + relativedelta(days=1), - 'seats_max': 10, }) with self.assertRaises(AccessError): @@ -76,12 +76,10 @@ class TestEventSecurity(TestEventCommon): }) 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), }) - event.update({'event_type_id': event_type.id}) - event._onchange_type() - event.flush() registration = self.env['event.registration'].create({'event_id': event.id, 'name': 'Myself'}) registration.write({'name': 'Myself2'}) diff --git a/addons/event_sale/tests/test_event_internals.py b/addons/event_sale/tests/test_event_internals.py index d70911b1524..0f3a7ae4d08 100644 --- a/addons/event_sale/tests/test_event_internals.py +++ b/addons/event_sale/tests/test_event_internals.py @@ -28,7 +28,6 @@ class TestEventData(TestEventSaleCommon): 'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)), 'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)), }) - event._onchange_type() self.assertEqual(event.event_ticket_ids, self.env['event.event.ticket']) event_type.write({ @@ -43,7 +42,7 @@ class TestEventData(TestEventSaleCommon): self.assertEqual(event_type.event_type_ticket_ids.description, self.event_product.description_sale) # synchronize event - event._onchange_type() + event.write({'event_type_id': event_type.id}) self.assertEqual(event.event_ticket_ids.name, event.event_type_id.event_type_ticket_ids.name) self.assertEqual(event.event_ticket_ids.seats_availability, 'limited') self.assertEqual(event.event_ticket_ids.seats_max, 5) diff --git a/addons/website_event/models/event_event.py b/addons/website_event/models/event_event.py index 472a60f802a..e67d6da0ce6 100644 --- a/addons/website_event/models/event_event.py +++ b/addons/website_event/models/event_event.py @@ -33,9 +33,10 @@ class Event(models.Model): # website website_published = fields.Boolean(tracking=True) website_menu = fields.Boolean( - 'Dedicated Menu', copy=False, + string='Dedicated Menu', copy=False, + compute='_compute_from_event_type', readonly=False, store=True, help="Creates menus Introduction, Location and Register on the page " - " of the event on the website.") + "of the event on the website.") menu_id = fields.Many2one('website.menu', 'Event Menu', copy=False) def _compute_is_participating(self): @@ -55,11 +56,17 @@ class Event(models.Model): if event.id: # avoid to perform a slug on a not yet saved record in case of an onchange. event.website_url = '/event/%s' % slug(event) - @api.onchange('event_type_id') - def _onchange_type(self): - super(Event, self)._onchange_type() - if self.event_type_id: - self.website_menu = self.event_type_id.website_menu + @api.depends('event_type_id') + def _compute_from_event_type(self): + """ Also ensure a value for website_menu as it is a trigger notably for + track related menus. """ + super(Event, self)._compute_from_event_type() + for event in self: + if not event.event_type_id: + if not event.website_menu: + event.website_menu = False + elif event.event_type_id: + event.website_menu = event.event_type_id.website_menu @api.model def create(self, vals): diff --git a/addons/website_event_questions/models/event_event.py b/addons/website_event_questions/models/event_event.py index 53e1cfc0533..3a7984a30a0 100644 --- a/addons/website_event_questions/models/event_event.py +++ b/addons/website_event_questions/models/event_event.py @@ -17,21 +17,24 @@ class EventEvent(models.Model): """ Override Event model to add optional questions when buying tickets. """ _inherit = 'event.event' - question_ids = fields.One2many('event.question', 'event_id', 'Questions', copy=True) + question_ids = fields.One2many( + 'event.question', 'event_id', 'Questions', copy=True, + compute='_compute_from_event_type', readonly=False, store=True) general_question_ids = fields.One2many('event.question', 'event_id', 'General Questions', domain=[('once_per_order', '=', True)]) specific_question_ids = fields.One2many('event.question', 'event_id', 'Specific Questions', domain=[('once_per_order', '=', False)]) - @api.onchange('event_type_id') - def _onchange_type(self): - super(EventEvent, self)._onchange_type() - if self.event_type_id.use_questions and self.event_type_id.question_ids: - self.question_ids = [(5, 0, 0)] + [ - (0, 0, { - 'title': question.title, - 'sequence': question.sequence, - 'once_per_order': question.once_per_order, - }) - for question in self.event_type_id.question_ids - ] + @api.depends('event_type_id') + def _compute_from_event_type(self): + super(EventEvent, self)._compute_from_event_type() + for event in self: + if event.event_type_id.use_questions and event.event_type_id.question_ids: + event.question_ids = [(5, 0, 0)] + [ + (0, 0, { + 'title': question.title, + 'sequence': question.sequence, + 'once_per_order': question.once_per_order, + }) + for question in event.event_type_id.question_ids + ] diff --git a/addons/website_event_questions/tests/test_event_internals.py b/addons/website_event_questions/tests/test_event_internals.py index aedaf148fc9..d3494cae49c 100644 --- a/addons/website_event_questions/tests/test_event_internals.py +++ b/addons/website_event_questions/tests/test_event_internals.py @@ -22,7 +22,7 @@ class TestEventData(TestEventQuestionCommon): 'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)), 'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)), }) - event._onchange_type() + self.assertEqual(event.specific_question_ids.title, 'Question1') self.assertEqual( set(event.specific_question_ids.mapped('answer_ids.name')), diff --git a/addons/website_event_sale/tests/test_frontend_buy_tickets.py b/addons/website_event_sale/tests/test_frontend_buy_tickets.py index e4698895587..a64dca5d4ad 100644 --- a/addons/website_event_sale/tests/test_frontend_buy_tickets.py +++ b/addons/website_event_sale/tests/test_frontend_buy_tickets.py @@ -4,6 +4,7 @@ import odoo.tests from datetime import timedelta + from odoo.addons.base.tests.common import HttpCaseWithUserDemo from odoo.fields import Datetime @@ -16,8 +17,8 @@ class TestUi(HttpCaseWithUserDemo): self.event_2 = self.env['event.event'].create({ 'name': 'Conference for Architects TEST', 'user_id': self.env.ref('base.user_admin').id, - 'date_begin': (Datetime.today()+ timedelta(days=5)).strftime('%Y-%m-%d 07:00:00'), - 'date_end': (Datetime.today()+ timedelta(days=5)).strftime('%Y-%m-%d 16:30:00'), + 'date_begin': (Datetime.today() + timedelta(days=5)).strftime('%Y-%m-%d 07:00:00'), + 'date_end': (Datetime.today() + timedelta(days=5)).strftime('%Y-%m-%d 16:30:00'), }) self.env['event.event.ticket'].create([{ @@ -35,6 +36,9 @@ class TestUi(HttpCaseWithUserDemo): 'price': 1500.0, }]) + # flush event to ensure having tickets available in the tests + self.event_2.flush() + (self.env.ref('base.partner_admin') + self.partner_demo).write({ 'street': '215 Vine St', 'city': 'Scranton',