diff options
| author | Thibault Delavallée <tde@odoo.com> | 2017-02-09 10:08:01 +0100 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2017-03-03 13:09:53 +0100 |
| commit | 91617bfc78e3da94be2276fe1c82c4ce47eeb896 (patch) | |
| tree | c7550bc94f201c3987384b19fe82f5a902c19140 | |
| parent | 5d4dd98af2672ccdde37d9587653c80a0ec7c661 (diff) | |
[IMP] event: ease event configuration and management through categories
This commit improve event.type (Event Categories) model and views in order
to ease event configuration through more detailed categories. The purpose
is to be able to define categories holding default data for website,
tickets, attendee mailing, ... Choosing a category on a new event takes
those default values to help users creating finely-tuned events.
Main configuration on event.type is
* auto confirmation, replacing the old system-wide auto confirmation
parameter
* seats limitation
* location: online events, timezone
* communication: reply-to email address, twitter hashtag, automated
mailing of attendees
* ticketing
* website parameters: display on website, display tracks, allow track
proposal
* question to attendees
| -rw-r--r-- | addons/event/data/event_data.xml | 3 | ||||
| -rw-r--r-- | addons/event/models/event.py | 103 | ||||
| -rw-r--r-- | addons/event/models/event_config_settings.py | 1 | ||||
| -rw-r--r-- | addons/event/models/event_mail.py | 28 | ||||
| -rw-r--r-- | addons/event/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | addons/event/tests/test_event_flow.py | 10 | ||||
| -rw-r--r-- | addons/event/tests/test_mail_schedule.py | 2 | ||||
| -rw-r--r-- | addons/event/views/event_config_settings_views.xml | 13 | ||||
| -rw-r--r-- | addons/event/views/event_views.xml | 143 | ||||
| -rw-r--r-- | addons/event_sale/models/event.py | 56 | ||||
| -rw-r--r-- | addons/event_sale/views/event_views.xml | 29 | ||||
| -rw-r--r-- | addons/website_event/models/event.py | 18 | ||||
| -rw-r--r-- | addons/website_event/views/event_config_settings_views.xml | 6 | ||||
| -rw-r--r-- | addons/website_event/views/event_views.xml | 35 | ||||
| -rw-r--r-- | addons/website_event_questions/models/event.py | 37 | ||||
| -rw-r--r-- | addons/website_event_questions/views/event_views.xml | 27 | ||||
| -rw-r--r-- | addons/website_event_track/models/event.py | 14 | ||||
| -rw-r--r-- | addons/website_event_track/views/event_views.xml | 35 |
18 files changed, 465 insertions, 97 deletions
diff --git a/addons/event/data/event_data.xml b/addons/event/data/event_data.xml index 54acbe204a3..0986e7d2942 100644 --- a/addons/event/data/event_data.xml +++ b/addons/event/data/event_data.xml @@ -8,9 +8,6 @@ <value>Module Events has been installed</value> </function> - <!-- Automatic confirmation of events by default --> - <function id="default_auto_confirmation" model="ir.values" name="set_default" eval="('event.config.settings', 'default_auto_confirmation', True)"/> - <!-- Event Mail Scheduler--> <record model="ir.cron" forcecreate="True" id="event_mail_scheduler"> <field name="name">Event: Mail Scheduler</field> diff --git a/addons/event/models/event.py b/addons/event/models/event.py index 7a4a2496f0b..b25f41b783a 100644 --- a/addons/event/models/event.py +++ b/addons/event/models/event.py @@ -14,25 +14,8 @@ class EventType(models.Model): _name = 'event.type' _description = 'Event Category' - name = fields.Char('Event Category', required=True, translate=True) - default_reply_to = fields.Char('Reply To') - default_registration_min = fields.Integer( - 'Default Minimum Registration', default=0, - help="It will select this default minimum value when you choose this event") - default_registration_max = fields.Integer( - 'Default Maximum Registration', default=0, - help="It will select this default maximum value when you choose this event") - - -class EventEvent(models.Model): - """Event""" - _name = 'event.event' - _description = 'Event' - _inherit = ['mail.thread'] - _order = 'date_begin' - @api.model - def _default_event_mail_ids(self): + def _get_default_event_type_mail_ids(self): return [(0, 0, { 'interval_unit': 'now', 'interval_type': 'after_sub', @@ -49,6 +32,57 @@ class EventEvent(models.Model): 'template_id': self.env.ref('event.event_reminder') })] + name = fields.Char('Event Category', required=True, translate=True) + # registration + has_seats_limitation = fields.Boolean( + 'Limited Seats', default=False) + default_registration_min = fields.Integer( + 'Minimum Registrations', default=0, + help="It will select this default minimum value when you choose this event") + default_registration_max = fields.Integer( + 'Maximum Registrations', default=0, + help="It will select this default maximum value when you choose this event") + auto_confirm = fields.Boolean( + 'Automatically Confirm Registrations', default=True, + help="Events and registrations will automatically be confirmed" + "upon creation, easing the flow for simple events.") + # location + is_online = fields.Boolean( + 'Online Event', help='Online events like webinars do not require a specific location and are hosted online.') + use_timezone = fields.Boolean('Use Default Timezone') + default_timezone = fields.Selection( + '_tz_get', string='Timezone', + default=lambda self: self.env.user.tz) + # communication + use_reply_to = fields.Boolean('Use Default Reply-To') + default_reply_to = fields.Char('Reply To') + use_hashtag = fields.Boolean('Use Default Hashtag') + default_hashtag = fields.Char('Twitter Hashtag') + 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=_get_default_event_type_mail_ids) + + @api.onchange('has_seats_limitation') + def _onchange_has_seats_limitation(self): + if not self.has_seats_limitation: + self.default_registration_min = 0 + self.default_registration_max = 0 + + @api.model + def _tz_get(self): + return [(x, x) for x in pytz.all_timezones] + + +class EventEvent(models.Model): + """Event""" + _name = 'event.event' + _description = 'Event' + _inherit = ['mail.thread'] + _order = 'date_begin' + name = fields.Char( string='Event Name', translate=True, required=True, readonly=False, states={'done': [('readonly', True)]}) @@ -71,7 +105,7 @@ class EventEvent(models.Model): readonly=False, states={'done': [('readonly', True)]}, oldname='type') color = fields.Integer('Kanban Color Index') - event_mail_ids = fields.One2many('event.mail', 'event_id', string='Mail Schedule', default=_default_event_mail_ids, copy=True) + event_mail_ids = fields.One2many('event.mail', 'event_id', string='Mail Schedule', copy=True) # Seats and computation seats_max = fields.Integer( @@ -120,16 +154,18 @@ class EventEvent(models.Model): ('confirm', 'Confirmed'), ('done', 'Done')], string='Status', default='draft', readonly=True, required=True, copy=False, help="If event is created, the status is 'Draft'. If event is confirmed for the particular dates the status is set to 'Confirmed'. If the event is over, the status is set to 'Done'. If event is cancelled the status is set to 'Cancelled'.") - auto_confirm = fields.Boolean(string='Autoconfirm Registrations', compute='_compute_auto_confirm') + auto_confirm = fields.Boolean(string='Autoconfirm Registrations') reply_to = fields.Char( 'Reply-To Email', readonly=False, states={'done': [('readonly', True)]}, help="The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one.") + is_online = fields.Boolean('Online Event') address_id = fields.Many2one( 'res.partner', string='Location', default=lambda self: self.env.user.company_id.partner_id, readonly=False, states={'done': [('readonly', True)]}, track_visibility="onchange") country_id = fields.Many2one('res.country', 'Country', related='address_id.country_id', store=True) + twitter_hashtag = fields.Char('Twitter Hashtag') description = fields.Html( string='Description', oldname='note', translate=html_translate, sanitize_attributes=False, readonly=False, states={'done': [('readonly', True)]}) @@ -189,10 +225,6 @@ class EventEvent(models.Model): else: self.date_end_located = False - @api.one - def _compute_auto_confirm(self): - self.auto_confirm = self.env['ir.values'].get_default('event.config.settings', 'default_auto_confirmation') - @api.onchange('event_type_id') def _onchange_type(self): if self.event_type_id: @@ -200,7 +232,28 @@ class EventEvent(models.Model): self.seats_max = self.event_type_id.default_registration_max if self.event_type_id.default_registration_max: self.seats_availability = 'limited' - self.reply_to = self.event_type_id.default_reply_to + + if self.event_type_id.auto_confirm: + self.auto_confirm = self.event_type_id.auto_confirm + + if self.event_type_id.use_reply_to: + self.reply_to = self.event_type_id.default_reply_to + + if self.event_type_id.use_reply_to: + self.twitter_hashtag = self.event_type_id.default_hashtag + + if self.event_type_id.use_timezone: + self.date_tz = self.event_type_id.default_timezone + + self.is_online = self.event_type_id.is_online + + if self.event_type_id.event_type_mail_ids: + self.event_mail_ids = [(5, 0, 0)] + [{ + 'template_id': line.template_id, + 'interval_nbr': line.interval_nbr, + 'interval_unit': line.interval_unit, + 'interval_type': line.interval_type} + for line in self.event_type_id.event_type_mail_ids] @api.constrains('seats_min', 'seats_max', 'seats_availability') def _check_seats_min_max(self): diff --git a/addons/event/models/event_config_settings.py b/addons/event/models/event_config_settings.py index 66207c594e6..f268fcf0f6b 100644 --- a/addons/event/models/event_config_settings.py +++ b/addons/event/models/event_config_settings.py @@ -9,7 +9,6 @@ class event_config_settings(models.TransientModel): module_event_sale = fields.Boolean("Tickets") module_website_event_track = fields.Boolean("Tracks and Agenda") module_website_event_questions = fields.Boolean("Registration Survey") - default_auto_confirmation = fields.Boolean("Auto Confirmation", default_model='event.config.settings') module_event_barcode = fields.Boolean("Barcode") module_website_event = fields.Boolean("Online Events") module_website_event_sale = fields.Boolean("Online Ticketing") diff --git a/addons/event/models/event_mail.py b/addons/event/models/event_mail.py index c8ba8f8b40d..9c462c9abe7 100644 --- a/addons/event/models/event_mail.py +++ b/addons/event/models/event_mail.py @@ -15,6 +15,32 @@ _INTERVALS = { } +class EventTypeMail(models.Model): + """ Template of event.mail to attach to event.type. Those will be copied + upon all events created in that type to ease event creation. """ + _name = 'event.type.mail' + _description = 'Mail Scheduling on Event Type' + + event_type_id = fields.Many2one( + 'event.type', string='Event Type', + ondelete='cascade', required=True) + interval_nbr = fields.Integer('Interval', default=1) + interval_unit = fields.Selection([ + ('now', 'Immediately'), + ('hours', 'Hour(s)'), ('days', 'Day(s)'), + ('weeks', 'Week(s)'), ('months', 'Month(s)')], + string='Unit', default='hours', required=True) + interval_type = fields.Selection([ + ('after_sub', 'After each registration'), + ('before_event', 'Before the event'), + ('after_event', 'After the event')], + string='Trigger', default="before_event", required=True) + template_id = fields.Many2one( + 'mail.template', string='Email Template', + domain=[('model', '=', 'event.registration')], required=True, ondelete='restrict', + help='This field contains the template of the mail that will be automatically sent') + + class EventMailScheduler(models.Model): """ Event automated mailing. This model replaces all existing fields and configuration allowing to send emails on events since Odoo 9. A cron exists @@ -37,7 +63,7 @@ class EventMailScheduler(models.Model): ('after_event', 'After the event')], string='Trigger ', default="before_event", required=True) template_id = fields.Many2one( - 'mail.template', string='Email to Send', + 'mail.template', string='Email Template', domain=[('model', '=', 'event.registration')], required=True, ondelete='restrict', help='This field contains the template of the mail that will be automatically sent') scheduled_date = fields.Datetime('Scheduled Sent Mail', compute='_compute_scheduled_date', store=True) diff --git a/addons/event/security/ir.model.access.csv b/addons/event/security/ir.model.access.csv index d17116d26e9..49d859ad183 100644 --- a/addons/event/security/ir.model.access.csv +++ b/addons/event/security/ir.model.access.csv @@ -10,3 +10,5 @@ access_event_mail,event.mail,model_event_mail,event.group_event_user,1,0,0,0 access_event_mail_manager,event.mail manager,model_event_mail,event.group_event_manager,1,1,1,1
access_event_mail_registration,event.mail.registration,model_event_mail_registration,event.group_event_user,1,0,0,0
access_event_mail_registration_manager,event.mail.registration.manager,model_event_mail_registration,event.group_event_manager,1,1,1,1
+access_event_type_mail_event_user,event.type.mail.event.user,model_event_type_mail,event.group_event_user,1,0,0,0
+access_event_type_mail_event_manager,event.type.mail.event.manager,model_event_type_mail,event.group_event_manager,1,1,1,1
diff --git a/addons/event/tests/test_event_flow.py b/addons/event/tests/test_event_flow.py index 12086101a16..a351fa584a9 100644 --- a/addons/event/tests/test_event_flow.py +++ b/addons/event/tests/test_event_flow.py @@ -15,14 +15,10 @@ class TestEventFlow(TestEventCommon): @mute_logger('odoo.addons.base.ir.ir_model', 'odoo.models') def test_00_basic_event_auto_confirm(self): """ Basic event management with auto confirmation """ - event_config = self.env['event.config.settings'].sudo(self.user_eventmanager).create({ - 'default_auto_confirmation': 1 - }) - event_config.execute() - # EventUser creates a new event: ok test_event = self.Event.sudo(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, @@ -66,13 +62,10 @@ class TestEventFlow(TestEventCommon): with self.assertRaises(UserError): test_event.button_cancel() - @mute_logger('odoo.addons.base.ir.ir_model', 'odoo.models') def test_10_advanced_event_flow(self): """ Avanced event flow: no auto confirmation, manage minimum / maximum seats, ... """ - self.env['ir.values'].set_default('event.config.settings', 'default_auto_confirmation', False) - # EventUser creates a new event: ok test_event = self.Event.sudo(self.user_eventmanager).create({ 'name': 'TestEvent', @@ -114,7 +107,6 @@ class TestEventFlow(TestEventCommon): ]}) with self.assertRaises(AccessError): event_config = self.env['event.config.settings'].sudo(self.user_eventmanager).create({ - 'default_auto_confirmation': 1 }) event_config.execute() diff --git a/addons/event/tests/test_mail_schedule.py b/addons/event/tests/test_mail_schedule.py index 6cbeeded7f2..7ada77218f3 100644 --- a/addons/event/tests/test_mail_schedule.py +++ b/addons/event/tests/test_mail_schedule.py @@ -13,13 +13,13 @@ class TestMailSchedule(TestEventCommon): @mute_logger('odoo.addons.base.ir.ir_model', 'odoo.models') def test_00_event_mail_schedule(self): """ Test mail scheduling for events """ - self.env['ir.values'].set_default('event.config.settings', 'default_auto_confirmation', True) now = fields.datetime.now() event_date_begin = now + relativedelta(days=1) event_date_end = now + relativedelta(days=3) test_event = self.Event.sudo(self.user_eventmanager).create({ 'name': 'TestEventMail', + 'auto_confirm': True, 'date_begin': event_date_begin, 'date_end': event_date_end, 'seats_max': 10, diff --git a/addons/event/views/event_config_settings_views.xml b/addons/event/views/event_config_settings_views.xml index 67601b1274d..aba1ec2f607 100644 --- a/addons/event/views/event_config_settings_views.xml +++ b/addons/event/views/event_config_settings_views.xml @@ -49,7 +49,7 @@ </div> </div> </div> - <div class="col-xs-12 col-md-6 o_setting_box"> + <div class="col-xs-12 col-md-6 o_setting_box" name="event_settings_website"> <div class="o_setting_left_pane"> <field name="module_website_event_sale"/> </div> @@ -60,17 +60,6 @@ </div> </div> </div> - <div class="col-xs-12 col-md-6 o_setting_box" id="default_auto_confirmation" title="Uncheck this option to manually manage draft event and draft registration."> - <div class="o_setting_left_pane"> - <field name="default_auto_confirmation"/> - </div> - <div class="o_setting_right_pane"> - <label for="default_auto_confirmation"/> - <div class="text-muted"> - Confirm registrations automatically - </div> - </div> - </div> </div> <h2>Attendance</h2> <div class="row mt16 o_settings_container"> diff --git a/addons/event/views/event_views.xml b/addons/event/views/event_views.xml index b7ae32f62ad..66208b3da65 100644 --- a/addons/event/views/event_views.xml +++ b/addons/event/views/event_views.xml @@ -12,13 +12,134 @@ <field name="model">event.type</field> <field name="arch" type="xml"> <form string="Event Category"> - <sheet> - <group> - <field name="name"/> - <field name="default_reply_to"/> - <field name="default_registration_min"/> - <field name="default_registration_max"/> - </group> + <sheet> + <div class="oe_title"> + <label for="name" class="oe_edit_only"/> + <h1><field name="name"/></h1> + </div> + <h2>Location</h2> + <div class="row mt16 o_settings_container" name="event_type_location"> + <div class="col-xs-12 col-md-6 o_setting_box"> + <div class="o_setting_left_pane"> + <field name="is_online"/> + </div> + <div class="o_setting_right_pane"> + <label for="is_online"/> + <div class="row"> + <div class="col-md-8 mt16 text-muted"> + Online events like webinars do not require a specific location + and are hosted online. + </div> + </div> + </div> + </div> + <div class="col-xs-12 col-md-6 o_setting_box"> + <div class="o_setting_left_pane"> + <field name="use_timezone"/> + </div> + <div class="o_setting_right_pane"> + <label for="use_timezone"/> + <div class="row"> + <div class="col-md-8 mt16" attrs="{'invisible': [('use_timezone', '=', False)]}"> + <label for="default_timezone"/> + <field name="default_timezone" + attrs="{'required': [('use_timezone', '=', True)]}"/> + </div> + </div> + </div> + </div> + </div> + <h2>Communication</h2> + <div class="row mt16 o_settings_container" name="event_type_communication"> + <div class="col-xs-12 col-md-6 o_setting_box"> + <div class="o_setting_left_pane"> + <field name="use_reply_to"/> + </div> + <div class="o_setting_right_pane"> + <label for="use_reply_to"/> + <div class="row"> + <div class="col-md-8 mt16" attrs="{'invisible': [('use_reply_to', '=', False)]}"> + <label for="default_reply_to"/> + <field name="default_reply_to" + attrs="{'required': [('use_reply_to', '=', True)]}"/> + </div> + </div> + </div> + </div> + <div class="col-xs-12 col-md-6 o_setting_box"> + <div class="o_setting_left_pane"> + <field name="use_hashtag"/> + </div> + <div class="o_setting_right_pane"> + <label for="use_hashtag"/> + <div class="row"> + <div class="col-md-8 mt16" attrs="{'invisible': [('use_hashtag', '=', False)]}"> + <label for="default_hashtag"/> + <field name="default_hashtag" + attrs="{'required': [('use_hashtag', '=', True)]}"/> + </div> + </div> + </div> + </div> + <div class="col-xs-12 col-md-12 o_setting_box"> + <div class="o_setting_left_pane"> + <field name="use_mail_schedule"/> + </div> + <div class="o_setting_right_pane"> + <label for="use_mail_schedule"/> + <div class="row mt16" attrs="{'invisible': [('use_mail_schedule', '=', False)]}"> + <div class="col-md-9"> + <field name="event_type_mail_ids"> + <tree string="Email Schedule" editable="bottom"> + <field name="template_id"/> + <field name="interval_nbr" attrs="{'readonly':[('interval_unit', '=', 'now')]}"/> + <field name="interval_unit"/> + <field name="interval_type"/> + </tree> + </field> + </div> + </div> + </div> + </div> + </div> + <h2>Visibility</h2> + <div class="row mt16 o_settings_container" name="event_type_visibility"> + <div class="col-xs-12 col-md-6 o_setting_box" name="event_type_visibility_seats"> + <div class="o_setting_left_pane"> + <field name="has_seats_limitation"/> + </div> + <div class="o_setting_right_pane"> + <label for="has_seats_limitation"/> + <div class="row"> + <div class="col-md-8 mt16" attrs="{'invisible': [('has_seats_limitation', '=', False)]}"> + <div> + <label for="default_registration_min"/> <field name="default_registration_min"/> + </div> + <div> + <label for="default_registration_max"/> <field name="default_registration_max"/> + </div> + </div> + </div> + </div> + </div> + </div> + <h2>Attendees</h2> + <div class="row mt16 o_settings_container" name="event_type_attendees"> + <div class="col-xs-12 col-md-6 o_setting_box" name="event_type_attendees_auto_confirm"> + <div class="o_setting_left_pane"> + <field name="auto_confirm"/> + </div> + <div class="o_setting_right_pane"> + <label for="auto_confirm"/> + <div class="row"> + <div class="col-md-8 mt16 text-muted"> + Events and registrations will automatically be confirmed + upon creation, easing the flow for simple events. + </div> + </div> + </div> + </div> + </div> </sheet> </form> </field> @@ -93,17 +214,23 @@ <div class="oe_title"> <label for="name" class="oe_edit_only"/> <h1><field name="name" placeholder="Event Name"/></h1> + <field name="is_online" groups="base.group_no_one"/> + <label for="is_online" string="Online" groups="base.group_no_one"/> </div> <group> <group> - <field name="address_id" context="{'show_address': 1}" options='{"always_reload": True}'/> <field name="organizer_id"/> + <field name="address_id" + attrs="{'invisible': [('is_online', '=', True)]}" + context="{'show_address': 1}" + options='{"always_reload": True}'/> <field name="company_id" groups="base.group_multi_company"/> <field name="user_id"/> </group> <group> <field name="event_type_id" options="{'no_create':True}"/> <field name="reply_to" string="Contact Email"/> + <field name="twitter_hashtag"/> <field name="auto_confirm" groups="base.group_no_one"/> </group> <group> diff --git a/addons/event_sale/models/event.py b/addons/event_sale/models/event.py index fb2d843c349..6fadf6ce756 100644 --- a/addons/event_sale/models/event.py +++ b/addons/event_sale/models/event.py @@ -2,26 +2,56 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError +from odoo.exceptions import ValidationError, UserError import odoo.addons.decimal_precision as dp -class Event(models.Model): - _inherit = 'event.event' +class EventType(models.Model): + _inherit = 'event.type' - def _default_tickets(self): + @api.model + def _get_default_event_ticket_ids(self): product = self.env.ref('event_sale.product_product_event', raise_if_not_found=False) if not product: - return self.env['event.event.ticket'] - return [{ + return False + return [(0, 0, { 'name': _('Registration'), 'product_id': product.id, 'price': 0, - }] + })] + + use_ticketing = fields.Boolean('Ticketing') + event_ticket_ids = fields.One2many( + 'event.event.ticket', 'event_type_id', + string='Tickets', default=_get_default_event_ticket_ids) + + @api.onchange('name') + def _onchange_name(self): + if self.name: + self.event_ticket_ids.filtered(lambda ticket: ticket.name == _('Registration')).update({ + 'name': _('Registration for %s') % self.name + }) + - event_ticket_ids = fields.One2many('event.event.ticket', 'event_id', string='Event Ticket', - default=lambda self: self._default_tickets(), copy=True) +class Event(models.Model): + _inherit = 'event.event' + + event_ticket_ids = fields.One2many( + 'event.event.ticket', 'event_id', string='Event Ticket', + copy=True) + + @api.onchange('event_type_id') + def _onchange_type(self): + super(Event, self)._onchange_type() + if self.event_type_id.use_ticketing: + self.event_ticket_ids = [(5, 0, 0)] + [ + (0, 0, { + 'name': self.name and _('Registration for %s') % self.name or ticket.name, + 'product_id': ticket.product_id.id, + 'price': ticket.price, + }) + for ticket in self.event_type_id.event_ticket_ids] class EventTicket(models.Model): @@ -32,7 +62,8 @@ class EventTicket(models.Model): return self.env.ref('event_sale.product_product_event', raise_if_not_found=False) name = fields.Char(string='Name', required=True, translate=True) - event_id = fields.Many2one('event.event', string="Event", required=True, ondelete='cascade') + event_type_id = fields.Many2one('event.type', string='Event Category', ondelete='cascade') + event_id = fields.Many2one('event.event', string="Event", ondelete='cascade') product_id = fields.Many2one('product.product', string='Product', required=True, domain=[("event_ok", "=", True)], default=_default_product_id) @@ -113,6 +144,11 @@ class EventTicket(models.Model): if record.seats_max and record.seats_available < 0: raise ValidationError(_('No more available seats for the ticket')) + @api.constrains('event_type_id', 'event_id') + def _constrains_event(self): + if any(ticket.event_type_id and ticket.event_id for ticket in self): + raise UserError(_('Ticket should belong to either event category or event but not both')) + @api.onchange('product_id') def _onchange_product_id(self): self.price = self.product_id.list_price or 0 diff --git a/addons/event_sale/views/event_views.xml b/addons/event_sale/views/event_views.xml index f706ee317e6..32027519042 100644 --- a/addons/event_sale/views/event_views.xml +++ b/addons/event_sale/views/event_views.xml @@ -41,6 +41,35 @@ </field> </record> + <record id="event_type_view_form_inherit_sale" model="ir.ui.view"> + <field name="name">event.type.view.form.inherit.sale</field> + <field name="model">event.type</field> + <field name="inherit_id" ref="event.view_event_type_form"/> + <field name="arch" type="xml"> + <div name="event_type_attendees_auto_confirm" position="after"> + <div class="col-xs-12 col-md-12 o_setting_box"> + <div class="o_setting_left_pane"> + <field name="use_ticketing"/> + </div> + <div class="o_setting_right_pane"> + <label for="use_ticketing"/> + <div class="row mt16" attrs="{'invisible': [('use_ticketing', '=', False)]}"> + <div class="col-md-9"> + <field name="event_ticket_ids"> + <tree string="Tickets" editable="bottom"> + <field name="name"/> + <field name="product_id"/> + <field name="price"/> + </tree> + </field> + </div> + </div> + </div> + </div> + </div> + </field> + </record> + <record id="view_event_form_inherit_ticket" model="ir.ui.view"> <field name="name">event.form.inherit</field> <field name="model">event.event</field> diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py index b6596223e3b..640eb9a3121 100644 --- a/addons/website_event/models/event.py +++ b/addons/website_event/models/event.py @@ -6,14 +6,18 @@ from odoo import api, fields, models, _ from odoo.addons.website.models.website import slug +class EventType(models.Model): + _name = 'event.type' + _inherit = ['event.type'] + + website_menu = fields.Boolean( + 'Display a dedicated menu on Website') + + class Event(models.Model): _name = 'event.event' _inherit = ['event.event', 'website.seo.metadata', 'website.published.mixin'] - def _default_hashtag(self): - return re.sub("[- \\.\\(\\)\\@\\#\\&]+", "", self.env.user.company_id.name).lower() - - twitter_hashtag = fields.Char('Twitter Hashtag', default=_default_hashtag) website_published = fields.Boolean(track_visibility='onchange') website_message_ids = fields.One2many( 'mail.message', 'res_id', @@ -47,6 +51,12 @@ 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 + def _get_menu_entries(self): self.ensure_one() return [ diff --git a/addons/website_event/views/event_config_settings_views.xml b/addons/website_event/views/event_config_settings_views.xml index aa713ad4b68..48da1930637 100644 --- a/addons/website_event/views/event_config_settings_views.xml +++ b/addons/website_event/views/event_config_settings_views.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> - <record id="event_config_settings_view_form_inherit_website_event" model="ir.ui.view"> - <field name="name">event.config.settings.form.inherit.website.event</field> + <record id="event_config_settings_view_form_inherit_website" model="ir.ui.view"> + <field name="name">event.config.settings.view.form.inherit.website</field> <field name="model">event.config.settings</field> <field name="inherit_id" ref="event.event_config_settings_view_form"/> <field name="arch" type="xml"> - <div id="default_auto_confirmation" position="before"> + <div name="event_settings_website" position="after"> <div class="col-xs-12 col-md-6 o_setting_box"> <div class="o_setting_left_pane"> <field name="module_website_event_questions"/> diff --git a/addons/website_event/views/event_views.xml b/addons/website_event/views/event_views.xml index 61666898621..a59b27ffa5d 100644 --- a/addons/website_event/views/event_views.xml +++ b/addons/website_event/views/event_views.xml @@ -1,8 +1,32 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> - <record model="ir.ui.view" id="view_event_sale_form"> - <field name="name">event.event.website.form</field> + <record id="event_type_view_form_inherit_website" model="ir.ui.view"> + <field name="name">event.type.view.form.inherit.website</field> + <field name="model">event.type</field> + <field name="inherit_id" ref="event.view_event_type_form"/> + <field name="arch" type="xml"> + <div name="event_type_visibility_seats" position="after"> + <div class="col-xs-12 col-md-6 o_setting_box" name="event_type_visibility_website"> + <div class="o_setting_left_pane"> + <field name="website_menu"/> + </div> + <div class="o_setting_right_pane"> + <label for="website_menu"/> + <div class="row"> + <div class="col-md-8 mt16 text-muted"> + Check this option to have menus for your event on the + website: registrations, schedule, map, ... + </div> + </div> + </div> + </div> + </div> + </field> + </record> + + <record id="event_event_view_form_inherit_website" model="ir.ui.view"> + <field name="name">event.event.view.form.inherit.website</field> <field name="model">event.event</field> <field name="inherit_id" ref="event.view_event_form"/> <field name="arch" type="xml"> @@ -15,9 +39,10 @@ <field name="website_published" widget="website_button"/> </button> </button> - <xpath expr="//field[@name='organizer_id']" position="after"> - <field name="twitter_hashtag"/> - </xpath> + <label for="is_online" position="after"> + <field name="website_menu"/> + <label for="website_menu" string="Website Menu"/> + </label> <xpath expr="//button[@name='button_done']" position="before"> <button name="action_open_badge_editor" type="object" diff --git a/addons/website_event_questions/models/event.py b/addons/website_event_questions/models/event.py index c45a924ffa9..4645d73c4d2 100644 --- a/addons/website_event_questions/models/event.py +++ b/addons/website_event_questions/models/event.py @@ -1,7 +1,17 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class EventType(models.Model): + _inherit = 'event.type' + + use_questions = fields.Boolean('Questions to Attendees') + question_ids = fields.One2many( + 'event.question', 'event_type_id', + string='Questions', copy=True) class EventEvent(models.Model): @@ -14,6 +24,23 @@ class EventEvent(models.Model): specific_question_ids = fields.One2many('event.question', 'event_id', 'Specific Questions', domain=[('is_individual', '=', True)]) + @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, + 'is_individual': question.is_individual, + 'answer_ids': [(0, 0, { + 'name': answer.name, + 'sequence': answer.sequence, + }) for answer in question.answer_ids] + }) + for question in self.event_type_id.question_ids + ] + class EventRegistrationAnswer(models.Model): ''' This m2m table has to be explicitly instanciated as we need unique ids @@ -41,13 +68,19 @@ class EventQuestion(models.Model): _order = 'sequence,id' title = fields.Char(required=True, translate=True) - event_id = fields.Many2one('event.event', required=True, ondelete='cascade') + event_type_id = fields.Many2one('event.type', 'Event Type', ondelete='cascade') + event_id = fields.Many2one('event.event', 'Event', ondelete='cascade') answer_ids = fields.One2many('event.answer', 'question_id', "Answers", required=True, copy=True) sequence = fields.Integer(default=10) is_individual = fields.Boolean('Ask each attendee', help="If True, this question will be asked for every attendee of a reservation. If " "not it will be asked only once and its value propagated to every attendees.") + @api.constrains('event_type_id', 'event_id') + def _constrains_event(self): + if any(question.event_type_id and question.event_id for question in self): + raise UserError(_('Question should belong to either event category or event but not both')) + class EventAnswer(models.Model): _name = 'event.answer' diff --git a/addons/website_event_questions/views/event_views.xml b/addons/website_event_questions/views/event_views.xml index a4428bd8094..e848c27de83 100644 --- a/addons/website_event_questions/views/event_views.xml +++ b/addons/website_event_questions/views/event_views.xml @@ -31,10 +31,33 @@ </field> </record> +<record id="event_type_view_form_inherit_question" model="ir.ui.view"> + <field name="name">event.type.view.form.inherit.question</field> + <field name="model">event.type</field> + <field name="inherit_id" ref="website_event.event_type_view_form_inherit_website"/> + <field name="arch" type="xml"> + <div name="event_type_attendees_auto_confirm" position="after"> + <div class="col-xs-12 col-md-12 o_setting_box"> + <div class="o_setting_left_pane"> + <field name="use_questions"/> + </div> + <div class="o_setting_right_pane"> + <label for="use_questions"/> + <div class="row mt16" attrs="{'invisible': [('use_ticketing', '=', False)]}"> + <div class="col-md-9"> + <field name="question_ids"/> + </div> + </div> + </div> + </div> + </div> + </field> +</record> + <record model="ir.ui.view" id="view_event_form_inherit_question"> - <field name="name">event.form.inherit.question</field> + <field name="name">event.event.view.form.inherit.question</field> <field name="model">event.event</field> - <field name="inherit_id" ref="event.view_event_form"/> + <field name="inherit_id" ref="website_event.event_event_view_form_inherit_website"/> <field name="arch" type="xml"> <data> <xpath expr="//notebook" position="inside"> diff --git a/addons/website_event_track/models/event.py b/addons/website_event_track/models/event.py index 6bcf0f1a757..d1088c1d500 100644 --- a/addons/website_event_track/models/event.py +++ b/addons/website_event_track/models/event.py @@ -5,6 +5,13 @@ from odoo import api, fields, models, _ from odoo.addons.website.models.website import slug +class EventType(models.Model): + _inherit = 'event.type' + + website_track = fields.Boolean('Tracks on Website') + website_track_proposal = fields.Boolean('Tracks Proposals on Website') + + class Event(models.Model): _inherit = "event.event" @@ -54,6 +61,13 @@ class Event(models.Model): for event in self: event.tracks_tag_ids = event.track_ids.mapped('tag_ids').ids + @api.onchange('event_type_id') + def _onchange_type(self): + super(Event, self)._onchange_type() + if self.event_type_id: + self.website_track = self.event_type_id.website_track + self.website_track_proposal = self.event_type_id.website_track_proposal + def _get_menu_entries(self): self.ensure_one() res = super(Event, self)._get_menu_entries() diff --git a/addons/website_event_track/views/event_views.xml b/addons/website_event_track/views/event_views.xml index a46d73b7ba4..1b58e78404d 100644 --- a/addons/website_event_track/views/event_views.xml +++ b/addons/website_event_track/views/event_views.xml @@ -1,9 +1,26 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> + + <record id="event_type_view_form_inherit_track" model="ir.ui.view"> + <field name="name">event.type.view.form.inherit.track</field> + <field name="model">event.type</field> + <field name="inherit_id" ref="website_event.event_type_view_form_inherit_website"/> + <field name="arch" type="xml"> + <xpath expr="//div[@name='event_type_visibility_website']/div[@class='o_setting_right_pane']" position='inside'> + <div class="row mt16"> + <label class="col-md-4" for="website_track"/> <field name="website_track"/> + </div> + <div class="row mt16"> + <label class="col-md-4" for="website_track_proposal"/> <field name="website_track_proposal"/> + </div> + </xpath> + </field> + </record> + <!-- Events Organisation/CONFIGURATION/EVENTS --> <record id="view_event_form" model="ir.ui.view"> <field name="name">Event Tracks</field> - <field name="inherit_id" ref="event.view_event_form"/> + <field name="inherit_id" ref="website_event.event_event_view_form_inherit_website"/> <field name="model">event.event</field> <field name="arch" type="xml"> <button name="website_publish_button" position="before"> @@ -20,16 +37,12 @@ <field name="sponsor_count" widget="statinfo"/> </button> </button> - <xpath expr="//div[@class='oe_title']" position="inside"> - <div name="event_checkboxes"> - <field name="website_menu"/> - <label for="website_menu"/> - <field name="website_track" attrs="{'invisible': [('website_menu', '=', False)]}"/> - <label for="website_track" attrs="{'invisible': [('website_menu', '=', False)]}"/> - <field name="website_track_proposal" attrs="{'invisible': [('website_menu', '=', False)]}"/> - <label for="website_track_proposal" attrs="{'invisible': [('website_menu', '=', False)]}"/> - </div> - </xpath> + <label for="website_menu" position="after"> + <field name="website_track" attrs="{'invisible': [('website_menu', '=', False)]}"/> + <label for="website_track" string="Tracks on Website" attrs="{'invisible': [('website_menu', '=', False)]}"/> + <field name="website_track_proposal" attrs="{'invisible': [('website_menu', '=', False)]}"/> + <label for="website_track_proposal" string="Track Proposals on Website" attrs="{'invisible': [('website_menu', '=', False)]}"/> + </label> </field> </record> </odoo> |
