diff options
Diffstat (limited to 'addons/event/models')
| -rw-r--r-- | addons/event/models/__init__.py | 3 | ||||
| -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.py | 1 | ||||
| -rw-r--r-- | addons/event/models/event_registration.py | 202 | ||||
| -rw-r--r-- | addons/event/models/event_stage.py | 1 |
5 files changed, 207 insertions, 198 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.') |
