summaryrefslogtreecommitdiff
path: root/addons/event
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2020-01-09 14:29:26 +0000
committerThibault Delavallée <tde@odoo.com>2020-01-10 16:06:40 +0000
commitabcd6c15cf27d0e5da77b2e08a742f40e11c4e84 (patch)
tree123ce41b18972f5cd4ad65a24215499fe4877866 /addons/event
parent59e5c8630fef744de7d1f4cdd648edd809507c6d (diff)
[MOV] (website_)event(_sale/_question/_track): split main model files
PURPOSE As event will soon evolve (onchange -> compute, code improvements, addition of new features) cleaning and improving tests is necessary to help avoid issues. SPECIFICATIONS As event model grow in complexity and features, it is easier to find its way through the application with having registration model lying in its own file to separate it from event-specific models (event.type, event.event). Ticket (event_sale) and sponsor (website_event_track) models are also extracted in their own file. LINKS LINKS Side effect of Task ID 2089156 (event onchange to compute) Community PR odoo/odoo#43127 Enterprise PR odoo/enterprise#7656
Diffstat (limited to 'addons/event')
-rw-r--r--addons/event/models/__init__.py3
-rw-r--r--addons/event/models/event_event.py (renamed from addons/event/models/event.py)198
-rw-r--r--addons/event/models/event_mail.py1
-rw-r--r--addons/event/models/event_registration.py202
-rw-r--r--addons/event/models/event_stage.py1
-rw-r--r--addons/event/tests/test_event_internals.py2
6 files changed, 208 insertions, 199 deletions
diff --git a/addons/event/models/__init__.py b/addons/event/models/__init__.py
index 750d7b5de79..727b34645d7 100644
--- a/addons/event/models/__init__.py
+++ b/addons/event/models/__init__.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import event
+from . import event_event
from . import event_mail
+from . import event_registration
from . import event_stage
from . import res_config_settings
from . import res_partner
diff --git a/addons/event/models/event.py b/addons/event/models/event_event.py
index d49b79d369a..4f366871155 100644
--- a/addons/event/models/event.py
+++ b/addons/event/models/event_event.py
@@ -1,16 +1,15 @@
# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import pytz
from odoo import _, api, fields, models
from odoo.tools import format_datetime
-from odoo.exceptions import AccessError, UserError, ValidationError
+from odoo.exceptions import ValidationError
from odoo.tools.translate import html_translate
from odoo.tools.misc import _format_time_ago
-from dateutil.relativedelta import relativedelta
-
_logger = logging.getLogger(__name__)
try:
@@ -395,196 +394,3 @@ class EventEvent(models.Model):
result[event.id] = cal.serialize().encode('utf-8')
return result
-
-
-class EventRegistration(models.Model):
- _name = 'event.registration'
- _description = 'Event Registration'
- _inherit = ['mail.thread', 'mail.activity.mixin']
- _order = 'id desc'
-
- # event
- origin = fields.Char(
- string='Source Document', readonly=True,
- help="Reference of the document that created the registration, for example a sales order")
- event_id = fields.Many2one(
- 'event.event', string='Event', required=True,
- readonly=True, states={'draft': [('readonly', False)]})
- # attendee
- partner_id = fields.Many2one(
- 'res.partner', string='Contact',
- states={'done': [('readonly', True)]})
- name = fields.Char(string='Attendee Name', index=True)
- email = fields.Char(string='Email')
- phone = fields.Char(string='Phone')
- mobile = fields.Char(string='Mobile')
- # 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)
- 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(
- 'res.company', string='Company', related='event_id.company_id',
- store=True, readonly=True, states={'draft': [('readonly', False)]})
- state = fields.Selection([
- ('draft', 'Unconfirmed'), ('cancel', 'Cancelled'),
- ('open', 'Confirmed'), ('done', 'Attended')],
- string='Status', default='draft', readonly=True, copy=False, tracking=True)
-
- @api.constrains('event_id', 'state')
- def _check_seats_limit(self):
- for registration in self:
- if registration.event_id.seats_availability == 'limited' and registration.event_id.seats_max and registration.event_id.seats_available < (1 if registration.state == 'draft' else 0):
- raise ValidationError(_('No more seats available for this event.'))
-
- def _check_auto_confirmation(self):
- if self._context.get('registration_force_draft'):
- return False
- if any(not registration.event_id.auto_confirm or
- (not registration.event_id.seats_available and registration.event_id.seats_availability == 'limited') for registration in self):
- return False
- return True
-
- @api.model
- def create(self, vals):
- registration = super(EventRegistration, self).create(vals)
- if registration._check_auto_confirmation():
- registration.sudo().confirm_registration()
- return registration
-
- @api.model
- def _prepare_attendee_values(self, registration):
- """ Method preparing the values to create new attendees based on a
- sales order line. It takes some registration data (dict-based) that are
- optional values coming from an external input like a web page. This method
- is meant to be inherited in various addons that sell events. """
- partner_id = registration.pop('partner_id', self.env.user.partner_id)
- event_id = registration.pop('event_id', False)
- data = {
- 'name': registration.get('name', partner_id.name),
- 'phone': registration.get('phone', partner_id.phone),
- 'mobile': registration.get('mobile', partner_id.mobile),
- 'email': registration.get('email', partner_id.email),
- 'partner_id': partner_id.id,
- 'event_id': event_id and event_id.id or False,
- }
- data.update({key: value for key, value in registration.items() if key in self._fields})
- return data
-
- def do_draft(self):
- self.write({'state': 'draft'})
-
- def confirm_registration(self):
- self.write({'state': 'open'})
-
- # auto-trigger after_sub (on subscribe) mail schedulers, if needed
- onsubscribe_schedulers = self.event_id.event_mail_ids.filtered(
- lambda s: s.interval_type == 'after_sub')
- onsubscribe_schedulers.execute()
-
- def button_reg_close(self):
- """ Close Registration """
- for registration in self:
- registration.write({'state': 'done', 'date_closed': fields.Datetime.now()})
-
- def button_reg_cancel(self):
- self.write({'state': 'cancel'})
-
- @api.onchange('partner_id')
- def _onchange_partner(self):
- if self.partner_id:
- contact_id = self.partner_id.address_get().get('contact', False)
- if contact_id:
- contact = self.env['res.partner'].browse(contact_id)
- self.name = contact.name or self.name
- self.email = contact.email or self.email
- self.phone = contact.phone or self.phone
- self.mobile = contact.mobile or self.mobile
-
- def _message_get_suggested_recipients(self):
- recipients = super(EventRegistration, self)._message_get_suggested_recipients()
- public_users = self.env['res.users'].sudo()
- public_groups = self.env.ref("base.group_public", raise_if_not_found=False)
- if public_groups:
- public_users = public_groups.sudo().with_context(active_test=False).mapped("users")
- try:
- for attendee in self:
- is_public = attendee.sudo().with_context(active_test=False).partner_id.user_ids in public_users if public_users else False
- if attendee.partner_id and not is_public:
- attendee._message_add_suggested_recipient(recipients, partner=attendee.partner_id, reason=_('Customer'))
- elif attendee.email:
- attendee._message_add_suggested_recipient(recipients, email=attendee.email, reason=_('Customer Email'))
- except AccessError: # no read access rights -> ignore suggested recipients
- pass
- return recipients
-
- def _message_get_default_recipients(self):
- # Prioritize registration email over partner_id, which may be shared when a single
- # partner booked multiple seats
- return {r.id: {
- 'partner_ids': [],
- 'email_to': r.email,
- 'email_cc': False}
- for r in self}
-
- def _message_post_after_hook(self, message, msg_vals):
- if self.email and not self.partner_id:
- # we consider that posting a message with a specified recipient (not a follower, a specific one)
- # on a document without customer means that it was created through the chatter using
- # suggested recipients. This heuristic allows to avoid ugly hacks in JS.
- new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email)
- if new_partner:
- self.search([
- ('partner_id', '=', False),
- ('email', '=', new_partner.email),
- ('state', 'not in', ['cancel']),
- ]).write({'partner_id': new_partner.id})
- return super(EventRegistration, self)._message_post_after_hook(message, msg_vals)
-
- def action_send_badge_email(self):
- """ Open a window to compose an email, with the template - 'event_badge'
- message loaded by default
- """
- self.ensure_one()
- template = self.env.ref('event.event_registration_mail_template_badge')
- compose_form = self.env.ref('mail.email_compose_message_wizard_form')
- ctx = dict(
- default_model='event.registration',
- default_res_id=self.id,
- default_use_template=bool(template),
- default_template_id=template.id,
- default_composition_mode='comment',
- custom_layout="mail.mail_notification_light",
- )
- return {
- 'name': _('Compose Email'),
- 'type': 'ir.actions.act_window',
- 'view_mode': 'form',
- 'res_model': 'mail.compose.message',
- 'views': [(compose_form.id, 'form')],
- 'view_id': compose_form.id,
- 'target': 'new',
- 'context': ctx,
- }
-
- def get_date_range_str(self):
- self.ensure_one()
- today = fields.Datetime.now()
- event_date = self.event_begin_date
- diff = (event_date.date() - today.date())
- if diff.days <= 0:
- return _('today')
- elif diff.days == 1:
- return _('tomorrow')
- elif (diff.days < 7):
- return _('in %d days') % (diff.days, )
- elif (diff.days < 14):
- return _('next week')
- elif event_date.month == (today + relativedelta(months=+1)).month:
- return _('next month')
- else:
- return _('on ') + format_datetime(self.env, self.event_begin_date, tz=self.event_id.date_tz, dt_format='medium')
-
- def summary(self):
- self.ensure_one()
- return {'information': []}
diff --git a/addons/event/models/event_mail.py b/addons/event/models/event_mail.py
index fd53adbb0bc..4f2407609f9 100644
--- a/addons/event/models/event_mail.py
+++ b/addons/event/models/event_mail.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from dateutil.relativedelta import relativedelta
diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py
new file mode 100644
index 00000000000..8f940a2812e
--- /dev/null
+++ b/addons/event/models/event_registration.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+
+from odoo import _, api, fields, models
+from odoo.tools import format_datetime
+from odoo.exceptions import AccessError, ValidationError
+
+from dateutil.relativedelta import relativedelta
+
+
+class EventRegistration(models.Model):
+ _name = 'event.registration'
+ _description = 'Event Registration'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _order = 'id desc'
+
+ # event
+ origin = fields.Char(
+ string='Source Document', readonly=True,
+ help="Reference of the document that created the registration, for example a sales order")
+ event_id = fields.Many2one(
+ 'event.event', string='Event', required=True,
+ readonly=True, states={'draft': [('readonly', False)]})
+ # attendee
+ partner_id = fields.Many2one(
+ 'res.partner', string='Contact',
+ states={'done': [('readonly', True)]})
+ name = fields.Char(string='Attendee Name', index=True)
+ email = fields.Char(string='Email')
+ phone = fields.Char(string='Phone')
+ mobile = fields.Char(string='Mobile')
+ # 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)
+ 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(
+ 'res.company', string='Company', related='event_id.company_id',
+ store=True, readonly=True, states={'draft': [('readonly', False)]})
+ state = fields.Selection([
+ ('draft', 'Unconfirmed'), ('cancel', 'Cancelled'),
+ ('open', 'Confirmed'), ('done', 'Attended')],
+ string='Status', default='draft', readonly=True, copy=False, tracking=True)
+
+ @api.constrains('event_id', 'state')
+ def _check_seats_limit(self):
+ for registration in self:
+ if registration.event_id.seats_availability == 'limited' and registration.event_id.seats_max and registration.event_id.seats_available < (1 if registration.state == 'draft' else 0):
+ raise ValidationError(_('No more seats available for this event.'))
+
+ def _check_auto_confirmation(self):
+ if self._context.get('registration_force_draft'):
+ return False
+ if any(not registration.event_id.auto_confirm or
+ (not registration.event_id.seats_available and registration.event_id.seats_availability == 'limited') for registration in self):
+ return False
+ return True
+
+ @api.model
+ def create(self, vals):
+ registration = super(EventRegistration, self).create(vals)
+ if registration._check_auto_confirmation():
+ registration.sudo().confirm_registration()
+ return registration
+
+ @api.model
+ def _prepare_attendee_values(self, registration):
+ """ Method preparing the values to create new attendees based on a
+ sales order line. It takes some registration data (dict-based) that are
+ optional values coming from an external input like a web page. This method
+ is meant to be inherited in various addons that sell events. """
+ partner_id = registration.pop('partner_id', self.env.user.partner_id)
+ event_id = registration.pop('event_id', False)
+ data = {
+ 'name': registration.get('name', partner_id.name),
+ 'phone': registration.get('phone', partner_id.phone),
+ 'mobile': registration.get('mobile', partner_id.mobile),
+ 'email': registration.get('email', partner_id.email),
+ 'partner_id': partner_id.id,
+ 'event_id': event_id and event_id.id or False,
+ }
+ data.update({key: value for key, value in registration.items() if key in self._fields})
+ return data
+
+ def do_draft(self):
+ self.write({'state': 'draft'})
+
+ def confirm_registration(self):
+ self.write({'state': 'open'})
+
+ # auto-trigger after_sub (on subscribe) mail schedulers, if needed
+ onsubscribe_schedulers = self.event_id.event_mail_ids.filtered(
+ lambda s: s.interval_type == 'after_sub')
+ onsubscribe_schedulers.execute()
+
+ def button_reg_close(self):
+ """ Close Registration """
+ for registration in self:
+ registration.write({'state': 'done', 'date_closed': fields.Datetime.now()})
+
+ def button_reg_cancel(self):
+ self.write({'state': 'cancel'})
+
+ @api.onchange('partner_id')
+ def _onchange_partner(self):
+ if self.partner_id:
+ contact_id = self.partner_id.address_get().get('contact', False)
+ if contact_id:
+ contact = self.env['res.partner'].browse(contact_id)
+ self.name = contact.name or self.name
+ self.email = contact.email or self.email
+ self.phone = contact.phone or self.phone
+ self.mobile = contact.mobile or self.mobile
+
+ def _message_get_suggested_recipients(self):
+ recipients = super(EventRegistration, self)._message_get_suggested_recipients()
+ public_users = self.env['res.users'].sudo()
+ public_groups = self.env.ref("base.group_public", raise_if_not_found=False)
+ if public_groups:
+ public_users = public_groups.sudo().with_context(active_test=False).mapped("users")
+ try:
+ for attendee in self:
+ is_public = attendee.sudo().with_context(active_test=False).partner_id.user_ids in public_users if public_users else False
+ if attendee.partner_id and not is_public:
+ attendee._message_add_suggested_recipient(recipients, partner=attendee.partner_id, reason=_('Customer'))
+ elif attendee.email:
+ attendee._message_add_suggested_recipient(recipients, email=attendee.email, reason=_('Customer Email'))
+ except AccessError: # no read access rights -> ignore suggested recipients
+ pass
+ return recipients
+
+ def _message_get_default_recipients(self):
+ # Prioritize registration email over partner_id, which may be shared when a single
+ # partner booked multiple seats
+ return {r.id: {
+ 'partner_ids': [],
+ 'email_to': r.email,
+ 'email_cc': False}
+ for r in self}
+
+ def _message_post_after_hook(self, message, msg_vals):
+ if self.email and not self.partner_id:
+ # we consider that posting a message with a specified recipient (not a follower, a specific one)
+ # on a document without customer means that it was created through the chatter using
+ # suggested recipients. This heuristic allows to avoid ugly hacks in JS.
+ new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email)
+ if new_partner:
+ self.search([
+ ('partner_id', '=', False),
+ ('email', '=', new_partner.email),
+ ('state', 'not in', ['cancel']),
+ ]).write({'partner_id': new_partner.id})
+ return super(EventRegistration, self)._message_post_after_hook(message, msg_vals)
+
+ def action_send_badge_email(self):
+ """ Open a window to compose an email, with the template - 'event_badge'
+ message loaded by default
+ """
+ self.ensure_one()
+ template = self.env.ref('event.event_registration_mail_template_badge')
+ compose_form = self.env.ref('mail.email_compose_message_wizard_form')
+ ctx = dict(
+ default_model='event.registration',
+ default_res_id=self.id,
+ default_use_template=bool(template),
+ default_template_id=template.id,
+ default_composition_mode='comment',
+ custom_layout="mail.mail_notification_light",
+ )
+ return {
+ 'name': _('Compose Email'),
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'res_model': 'mail.compose.message',
+ 'views': [(compose_form.id, 'form')],
+ 'view_id': compose_form.id,
+ 'target': 'new',
+ 'context': ctx,
+ }
+
+ def get_date_range_str(self):
+ self.ensure_one()
+ today = fields.Datetime.now()
+ event_date = self.event_begin_date
+ diff = (event_date.date() - today.date())
+ if diff.days <= 0:
+ return _('today')
+ elif diff.days == 1:
+ return _('tomorrow')
+ elif (diff.days < 7):
+ return _('in %d days') % (diff.days, )
+ elif (diff.days < 14):
+ return _('next week')
+ elif event_date.month == (today + relativedelta(months=+1)).month:
+ return _('next month')
+ else:
+ return _('on ') + format_datetime(self.env, self.event_begin_date, tz=self.event_id.date_tz, dt_format='medium')
+
+ def summary(self):
+ self.ensure_one()
+ return {'information': []}
diff --git a/addons/event/models/event_stage.py b/addons/event/models/event_stage.py
index d4064e3f9d4..e985df62c68 100644
--- a/addons/event/models/event_stage.py
+++ b/addons/event/models/event_stage.py
@@ -16,7 +16,6 @@ class EventStage(models.Model):
pipe_end = fields.Boolean(
string='End Stage', default=False,
help='Events will be automatically be moved into this stage their are passed and event moved in the stage will be automatically greened')
-
legend_blocked = fields.Char(
'Red Kanban Label', default=lambda s: _('Blocked'), translate=True, required=True,
help='Override the default value displayed for the blocked state for kanban selection.')
diff --git a/addons/event/tests/test_event_internals.py b/addons/event/tests/test_event_internals.py
index 905222dcc5f..2a79086808a 100644
--- a/addons/event/tests/test_event_internals.py
+++ b/addons/event/tests/test_event_internals.py
@@ -16,7 +16,7 @@ class TestEventData(TestEventCommon):
@users('user_eventmanager')
def test_event_date_computation(self):
- self.patcher = patch('odoo.addons.event.models.event.fields.Datetime', wraps=FieldsDatetime)
+ 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, 31, 8, 0, 0)