[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
This commit is contained in:
Thibault Delavallée
2017-03-03 13:09:53 +01:00
parent 5d4dd98af2
commit 91617bfc78
18 changed files with 468 additions and 100 deletions
-3
View File
@@ -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>
+78 -25
View File
@@ -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):
@@ -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")
+27 -1
View File
@@ -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)
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
10 access_event_mail_manager event.mail manager model_event_mail event.group_event_manager 1 1 1 1
11 access_event_mail_registration event.mail.registration model_event_mail_registration event.group_event_user 1 0 0 0
12 access_event_mail_registration_manager event.mail.registration.manager model_event_mail_registration event.group_event_manager 1 1 1 1
13 access_event_type_mail_event_user event.type.mail.event.user model_event_type_mail event.group_event_user 1 0 0 0
14 access_event_type_mail_event_manager event.type.mail.event.manager model_event_type_mail event.group_event_manager 1 1 1 1
+1 -9
View File
@@ -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()
+1 -1
View File
@@ -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,
@@ -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">
+135 -8
View File
@@ -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>
+49 -13
View File
@@ -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 EventType(models.Model):
_inherit = 'event.type'
@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 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
})
class Event(models.Model):
_inherit = 'event.event'
def _default_tickets(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 [{
'name': _('Registration'),
'product_id': product.id,
'price': 0,
}]
event_ticket_ids = fields.One2many(
'event.event.ticket', 'event_id', string='Event Ticket',
copy=True)
event_ticket_ids = fields.One2many('event.event.ticket', 'event_id', string='Event Ticket',
default=lambda self: self._default_tickets(), 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
+29
View File
@@ -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>
+14 -4
View File
@@ -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 [
@@ -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"/>
+30 -5
View File
@@ -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"
+35 -2
View File
@@ -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'
@@ -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">
@@ -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()
@@ -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>