[MERGE] (website_)event: add tags on event and improve registration flow
# Purpose **Add tags and category of tags in event and make various improvements in event app** # Specs - Add tags that users will be able to filter events with. - Light design improvements in /event page - Improve event registration in front-end # Links Task ID 2188807 closes odoo/odoo#44690 Related: odoo/upgrade#998 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -32,6 +32,7 @@ Key Features
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/event_templates.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'views/event_tag_views.xml'
|
||||
],
|
||||
'demo': [
|
||||
'data/res_users_demo.xml',
|
||||
|
||||
@@ -25,6 +25,50 @@
|
||||
<field name="default_timezone">Europe/Brussels</field>
|
||||
</record>
|
||||
|
||||
<!-- Category and Tags -->
|
||||
<record id="event_tag_category_1" model="event.tag.category">
|
||||
<field name="name">Age</field>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_2" model="event.tag.category">
|
||||
<field name="name">Activity</field>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_1_tag_1" model="event.tag">
|
||||
<field name="name">5-10</field>
|
||||
<field name="category_id" ref="event_tag_category_1"/>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_1_tag_2" model="event.tag">
|
||||
<field name="name">10-14</field>
|
||||
<field name="category_id" ref="event_tag_category_1"/>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_1_tag_3" model="event.tag">
|
||||
<field name="name">15-18</field>
|
||||
<field name="category_id" ref="event_tag_category_1"/>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_1_tag_4" model="event.tag">
|
||||
<field name="name">18+</field>
|
||||
<field name="category_id" ref="event_tag_category_1"/>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_2_tag_1" model="event.tag">
|
||||
<field name="name">Culture</field>
|
||||
<field name="category_id" ref="event_tag_category_2"/>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_2_tag_2" model="event.tag">
|
||||
<field name="name">Music</field>
|
||||
<field name="category_id" ref="event_tag_category_2"/>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_2_tag_3" model="event.tag">
|
||||
<field name="name">Sport</field>
|
||||
<field name="category_id" ref="event_tag_category_2"/>
|
||||
</record>
|
||||
|
||||
<!-- Event -->
|
||||
<record id="event.event_0" model="event.event">
|
||||
<field name="name">Design Fair Los Angeles</field>
|
||||
@@ -37,6 +81,7 @@
|
||||
<field name="date_tz">US/Pacific</field>
|
||||
<field name="event_type_id" ref="event_type_0"/>
|
||||
<field name="stage_id" ref="event_stage_booked"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [event_tag_category_1_tag_1,event_tag_category_2_tag_1])]"/>
|
||||
</record>
|
||||
<record id="event_0_ticket_0" model="event.event.ticket">
|
||||
<field name="name">Free</field>
|
||||
@@ -72,6 +117,7 @@
|
||||
<field name="address_id" ref="event.res_partner_location_0"/>
|
||||
<field name="stage_id" ref="event_stage_booked"/>
|
||||
<field name="kanban_state">blocked</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [event_tag_category_1_tag_4,event_tag_category_2_tag_3])]"/>
|
||||
</record>
|
||||
|
||||
<record id="message_event_1_0" model="mail.message">
|
||||
@@ -125,6 +171,7 @@
|
||||
<field name="seats_availability">limited</field>
|
||||
<field name="seats_max">200</field>
|
||||
<field name="stage_id" ref="event_stage_booked"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [event_tag_category_1_tag_4,event_tag_category_2_tag_1])]"/>
|
||||
</record>
|
||||
<record id="event_2_ticket_1" model="event.event.ticket">
|
||||
<field name="name">Standard</field>
|
||||
@@ -160,6 +207,7 @@
|
||||
<field name="event_type_id" ref="event_type_0"/>
|
||||
<field name="address_id" ref="event.res_partner_location_1"/>
|
||||
<field name="stage_id" ref="event_stage_announced"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [event_tag_category_1_tag_3,event_tag_category_2_tag_2])]"/>
|
||||
</record>
|
||||
<record id="event_3_ticket_0" model="event.event.ticket">
|
||||
<field name="name">Standard</field>
|
||||
@@ -200,6 +248,7 @@
|
||||
<field name="event_type_id" ref="event_type_1"/>
|
||||
<field name="stage_id" ref="event_stage_done"/>
|
||||
<field name="kanban_state">done</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [event_tag_category_1_tag_4,event_tag_category_2_tag_1])]"/>
|
||||
</record>
|
||||
<record id="event_4_ticket_0" model="event.event.ticket">
|
||||
<field name="name">General Admission</field>
|
||||
@@ -224,6 +273,7 @@
|
||||
<field eval="(DateTime.today()+ timedelta(days=371)).strftime('%Y-%m-%d 17:00:00')" name="date_end"/>
|
||||
<field name="event_type_id" ref="event_type_2"/>
|
||||
<field name="address_id" ref="event.res_partner_location_1"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [event_tag_category_1_tag_2,event_tag_category_2_tag_3])]"/>
|
||||
</record>
|
||||
|
||||
<record id="event.event_6" model="event.event">
|
||||
|
||||
@@ -5,6 +5,7 @@ from . import event_event
|
||||
from . import event_mail
|
||||
from . import event_registration
|
||||
from . import event_stage
|
||||
from . import event_tag
|
||||
from . import event_ticket
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
|
||||
@@ -21,10 +21,10 @@ except ImportError:
|
||||
|
||||
class EventType(models.Model):
|
||||
_name = 'event.type'
|
||||
_description = 'Event Category'
|
||||
_description = 'Event Template'
|
||||
_order = 'sequence, id'
|
||||
|
||||
name = fields.Char('Event Category', required=True, translate=True)
|
||||
name = fields.Char('Event Template', required=True, translate=True)
|
||||
sequence = fields.Integer()
|
||||
# tickets
|
||||
use_ticket = fields.Boolean('Ticketing')
|
||||
@@ -32,6 +32,7 @@ class EventType(models.Model):
|
||||
'event.type.ticket', 'event_type_id',
|
||||
string='Tickets', compute='_compute_event_type_ticket_ids',
|
||||
readonly=False, store=True)
|
||||
tag_ids = fields.Many2many('event.tag', string="Tags", copy=True)
|
||||
# registration
|
||||
has_seats_limitation = fields.Boolean('Limited Seats')
|
||||
seats_max = fields.Integer(
|
||||
@@ -91,7 +92,6 @@ class EventType(models.Model):
|
||||
if not template.has_seats_limitation:
|
||||
template.seats_max = 0
|
||||
|
||||
|
||||
class EventEvent(models.Model):
|
||||
"""Event"""
|
||||
_name = 'event.event'
|
||||
@@ -118,11 +118,13 @@ class EventEvent(models.Model):
|
||||
'res.partner', string='Organizer', tracking=True,
|
||||
default=lambda self: self.env.company.partner_id,
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
|
||||
event_type_id = fields.Many2one('event.type', string='Category', ondelete='set null')
|
||||
event_type_id = fields.Many2one('event.type', string='Template', ondelete='set null')
|
||||
color = fields.Integer('Kanban Color Index')
|
||||
event_mail_ids = fields.One2many(
|
||||
'event.mail', 'event_id', string='Mail Schedule', copy=True,
|
||||
compute='_compute_from_event_type', readonly=False, store=True)
|
||||
tag_ids = fields.Many2many('event.tag', string="Tags", readonly=False,
|
||||
copy=True, store=True, compute="_compute_from_event_type")
|
||||
# Kanban fields
|
||||
kanban_state = fields.Selection([('normal', 'In Progress'), ('done', 'Done'), ('blocked', 'Blocked')], default='normal')
|
||||
kanban_state_label = fields.Char(
|
||||
@@ -379,6 +381,9 @@ class EventEvent(models.Model):
|
||||
})
|
||||
for line in event.event_type_id.event_type_ticket_ids]
|
||||
|
||||
if event.event_type_id.tag_ids:
|
||||
event.tag_ids = event.event_type_id.tag_ids
|
||||
|
||||
@api.constrains('seats_max', 'seats_available', 'seats_availability')
|
||||
def _check_seats_limit(self):
|
||||
if any(event.seats_availability == 'limited' and event.seats_max and event.seats_available < 0 for event in self):
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class EventTagCategory(models.Model):
|
||||
_name = "event.tag.category"
|
||||
_description = "Event Tag Category"
|
||||
_order = "sequence"
|
||||
|
||||
name = fields.Char("Name", required=True, translate=True)
|
||||
sequence = fields.Integer('Sequence', default=0)
|
||||
tag_ids = fields.One2many('event.tag', 'category_id', string="Tags")
|
||||
|
||||
class EventTag(models.Model):
|
||||
_name = "event.tag"
|
||||
_description = "Event Tag"
|
||||
_order = "sequence"
|
||||
|
||||
name = fields.Char("Name", required=True, translate=True)
|
||||
sequence = fields.Integer('Sequence', default=0)
|
||||
category_id = fields.Many2one("event.tag.category", string="Category", required=True, ondelete='cascade')
|
||||
|
||||
def name_get(self):
|
||||
return [(tag.id, _("%s: %s" % (tag.category_id.name, tag.name))) for tag in self]
|
||||
@@ -82,12 +82,13 @@ class EventTicket(models.Model):
|
||||
else:
|
||||
ticket.is_expired = False
|
||||
|
||||
@api.depends('start_sale_date', 'end_sale_date', 'event_id.date_tz')
|
||||
@api.depends('start_sale_date', 'end_sale_date', 'event_id.date_tz', 'seats_available', 'seats_max')
|
||||
def _compute_sale_available(self):
|
||||
for ticket in self:
|
||||
current_date = fields.Date.context_today(ticket.with_context(tz=ticket._get_ticket_tz()))
|
||||
if (ticket.start_sale_date and ticket.start_sale_date > current_date) or \
|
||||
ticket.end_sale_date and ticket.end_sale_date < current_date:
|
||||
ticket.end_sale_date and ticket.end_sale_date < current_date or \
|
||||
ticket.seats_available <= 0 and ticket.seats_max > 0:
|
||||
ticket.sale_available = False
|
||||
else:
|
||||
ticket.sale_available = True
|
||||
|
||||
@@ -19,3 +19,7 @@ access_event_type_mail_event_user,event.type.mail.event.user,model_event_type_ma
|
||||
access_event_type_mail_event_manager,event.type.mail.event.manager,model_event_type_mail,event.group_event_manager,1,1,1,1
|
||||
access_event_stage_user,event.stage.user,model_event_stage,event.group_event_user,1,1,1,1
|
||||
access_event_stage_manager,event.stage.manager,model_event_stage,event.group_event_manager,1,1,1,1
|
||||
access_event_category,event.tag.category,model_event_tag_category,event.group_event_user,1,0,0,0
|
||||
access_event_category_manager,event.tag.category manager,model_event_tag_category,event.group_event_manager,1,1,1,1
|
||||
access_event_tag,event.tag,model_event_tag,event.group_event_user,1,0,0,0
|
||||
access_event_tag_manager,event.tag manager,model_event_tag,event.group_event_manager,1,1,1,1
|
||||
|
||||
|
@@ -28,7 +28,7 @@
|
||||
sequence="99"
|
||||
parent="event_main_menu"
|
||||
groups="event.group_event_user"/>
|
||||
<menuitem name="Event Categories"
|
||||
<menuitem name="Event Templates"
|
||||
id="menu_event_type"
|
||||
sequence="1"
|
||||
parent="menu_event_configuration"/>
|
||||
@@ -37,5 +37,9 @@
|
||||
sequence="10"
|
||||
parent="menu_event_configuration"
|
||||
groups="base.group_no_one"/>
|
||||
<menuitem name="Event Tags Categories"
|
||||
id="menu_event_category"
|
||||
sequence="20"
|
||||
parent="menu_event_configuration"/>
|
||||
|
||||
</data></odoo>
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- EVENT.TAG.CATEGORY VIEWS -->
|
||||
<record id="event_tag_category_view_tree" model="ir.ui.view">
|
||||
<field name="name">event.tag.category.view.tree</field>
|
||||
<field name="model">event.tag.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Event Category">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="tag_ids" widget="many2many_tags" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_view_form" model="ir.ui.view">
|
||||
<field name="name">event.tag.category.view.form</field>
|
||||
<field name="model">event.tag.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Event Category">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1><field nolabel="1" name="name"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="tag_ids" context="{'default_category_id': active_id}">
|
||||
<tree string="Tags" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_category_action_tree" model="ir.actions.act_window" >
|
||||
<field name="name">Event Tags Categories</field>
|
||||
<field name="res_model">event.tag.category</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- EVENT.TAG VIEWS -->
|
||||
<record id="event_tag_view_tree" model="ir.ui.view">
|
||||
<field name="name">event.tag.view.tree</field>
|
||||
<field name="model">event.tag</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Event Tags Categories">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="category_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_tag_view_form" model="ir.ui.view">
|
||||
<field name="name">event.tag.view.form</field>
|
||||
<field name="model">event.tag</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Event Category Tag">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
</div>
|
||||
<field name="category_id" widget="many2one"/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="menu_event_category" model="ir.ui.menu">
|
||||
<field name="action" ref="event.event_tag_category_action_tree"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -86,6 +86,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box" >
|
||||
<label for="tag_ids" string="Tags"/>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'no_quick_create': True}"/>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Attendees</h2>
|
||||
<div class="row mt16 o_settings_container" name="event_type_attendees">
|
||||
@@ -131,7 +135,7 @@
|
||||
<field name="name">event.type.tree</field>
|
||||
<field name="model">event.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Event Category">
|
||||
<tree string="Event Template">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
@@ -142,7 +146,7 @@
|
||||
<field name="name">event.type.search</field>
|
||||
<field name="model">event.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Event Categories">
|
||||
<search string="Event Templates">
|
||||
<field name="name"/>
|
||||
<filter string="Online Event" name="online_event" help="Online Event" domain="[('is_online', '=', True)]"/>
|
||||
</search>
|
||||
@@ -150,7 +154,7 @@
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_event_type">
|
||||
<field name="name">Event Categories</field>
|
||||
<field name="name">Event Templates</field>
|
||||
<field name="res_model">event.type</field>
|
||||
</record>
|
||||
|
||||
@@ -227,6 +231,7 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="event_type_id" options="{'no_create':True}"/>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'no_quick_create': True}"/>
|
||||
<field name="auto_confirm" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group>
|
||||
@@ -446,7 +451,7 @@
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Responsible" name="responsible" context="{'group_by': 'user_id'}"/>
|
||||
<filter string="Event Category" name="event_type_id" context="{'group_by': 'event_type_id'}"/>
|
||||
<filter string="Template" name="event_type_id" context="{'group_by': 'event_type_id'}"/>
|
||||
<filter string="Stage" name="stage_id" context="{'group_by': 'stage_id'}"/>
|
||||
<filter string="Start Date" name="date_begin" domain="[]" context="{'group_by': 'date_begin'}"/>
|
||||
</group>
|
||||
|
||||
@@ -94,6 +94,7 @@ class EventTemplateTicket(models.Model):
|
||||
|
||||
class EventTicket(models.Model):
|
||||
_inherit = 'event.event.ticket'
|
||||
_order = "event_id, price"
|
||||
|
||||
# product
|
||||
price_reduce_taxinc = fields.Float(string='Price Reduce Tax inc', compute='_compute_price_reduce_taxinc')
|
||||
|
||||
@@ -5,6 +5,8 @@ import re
|
||||
import werkzeug
|
||||
from werkzeug.datastructures import OrderedMultiDict
|
||||
|
||||
from ast import literal_eval
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
@@ -12,6 +14,7 @@ from odoo import fields, http, _
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
from odoo.addons.website.controllers.main import QueryURL
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.misc import get_lang, format_date
|
||||
|
||||
|
||||
@@ -28,6 +31,7 @@ class WebsiteEventController(http.Controller):
|
||||
|
||||
searches.setdefault('search', '')
|
||||
searches.setdefault('date', 'all')
|
||||
searches.setdefault('tags', '')
|
||||
searches.setdefault('type', 'all')
|
||||
searches.setdefault('country', 'all')
|
||||
|
||||
@@ -70,6 +74,19 @@ class WebsiteEventController(http.Controller):
|
||||
if searches['search']:
|
||||
domain_search['search'] = [('name', 'ilike', searches['search'])]
|
||||
|
||||
search_tags = self._extract_searched_event_tags(searches)
|
||||
if search_tags:
|
||||
# Example: You filter on age: 10-12 and activity: football.
|
||||
# Doing it this way allows to only get events who are tagged "age: 10-12" AND "activity: football".
|
||||
# Add another tag "age: 12-15" to the search and it would fetch the ones who are tagged:
|
||||
# ("age: 10-12" OR "age: 12-15") AND "activity: football
|
||||
grouped_tags = defaultdict(list)
|
||||
for tag in search_tags:
|
||||
grouped_tags[tag.category_id].append(tag)
|
||||
domain_search['tags'] = []
|
||||
for group in grouped_tags:
|
||||
domain_search['tags'] = expression.AND([domain_search['tags'], [('tag_ids', 'in', [tag.id for tag in grouped_tags[group]])]])
|
||||
|
||||
current_date = None
|
||||
current_type = None
|
||||
current_country = None
|
||||
@@ -102,11 +119,6 @@ class WebsiteEventController(http.Controller):
|
||||
date[3] = Event.search_count(dom_without('date') + date[2])
|
||||
|
||||
domain = dom_without('type')
|
||||
types = Event.read_group(domain, ["id", "event_type_id"], groupby=["event_type_id"], orderby="event_type_id")
|
||||
types.insert(0, {
|
||||
'event_type_id_count': sum([int(type['event_type_id_count']) for type in types]),
|
||||
'event_type_id': ("all", _("All Categories"))
|
||||
})
|
||||
|
||||
domain = dom_without('country')
|
||||
countries = Event.read_group(domain, ["id", "country_id"], groupby="country_id", orderby="country_id")
|
||||
@@ -141,10 +153,11 @@ class WebsiteEventController(http.Controller):
|
||||
'current_type': current_type,
|
||||
'event_ids': events, # event_ids used in website_event_track so we keep name as it is
|
||||
'dates': dates,
|
||||
'types': types,
|
||||
'categories': request.env['event.tag.category'].search([]),
|
||||
'countries': countries,
|
||||
'pager': pager,
|
||||
'searches': searches,
|
||||
'search_tags': search_tags,
|
||||
'keep': keep,
|
||||
}
|
||||
|
||||
@@ -199,6 +212,7 @@ class WebsiteEventController(http.Controller):
|
||||
urls = event._get_event_resource_urls()
|
||||
values = {
|
||||
'event': event,
|
||||
'sold_out': all(not ticket.sale_available and not ticket.is_expired for ticket in event.event_ticket_ids),
|
||||
'main_object': event,
|
||||
'range': range,
|
||||
'registrable': event.sudo().event_registrations_open,
|
||||
@@ -342,3 +356,15 @@ class WebsiteEventController(http.Controller):
|
||||
'google_url': urls.get('google_url'),
|
||||
'iCal_url': urls.get('iCal_url')
|
||||
})
|
||||
|
||||
def _extract_searched_event_tags(self, searches):
|
||||
tags = request.env['event.tag']
|
||||
if searches.get('tags'):
|
||||
try:
|
||||
tag_ids = literal_eval(searches['tags'])
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# perform a search to filter on existing / valid tags implicitely
|
||||
tags = request.env['event.tag'].search([('id', 'in', tag_ids)])
|
||||
return tags
|
||||
|
||||
@@ -4,3 +4,5 @@ access_event_event_portal,event.event.portal,event.model_event_event,base.group_
|
||||
access_event_type_public,event.type.public,event.model_event_type,,1,0,0,0
|
||||
access_event_event_ticket_public,event.event.ticket.public,event.model_event_event_ticket,base.group_public,1,0,0,0
|
||||
access_event_event_ticket_portal,event.event.ticket.portal,event.model_event_event_ticket,base.group_portal,1,0,0,0
|
||||
access_event_category_public,event.tag.category.public,event.model_event_tag_category,,1,0,0,0
|
||||
access_event_tag_public,event.tag.public,event.model_event_tag,,1,0,0,0
|
||||
|
||||
|
@@ -9,9 +9,6 @@ var _t = core._t;
|
||||
|
||||
// Catch registration form event, because of JS for attendee details
|
||||
var EventRegistrationForm = Widget.extend({
|
||||
events: {
|
||||
'click .o_wevent_registration_btn': '_onRegistrationBtnClick',
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
@@ -70,15 +67,6 @@ var EventRegistrationForm = Widget.extend({
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onRegistrationBtnClick: function (ev) {
|
||||
var $btn = $(ev.currentTarget);
|
||||
$btn.toggleClass('btn-primary text-left pl-0');
|
||||
$btn.siblings().toggleClass('d-none');
|
||||
},
|
||||
});
|
||||
|
||||
publicWidget.registry.EventRegistrationFormInstance = publicWidget.Widget.extend({
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
odoo.define('website_event.ticket_details', function (require) {
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
publicWidget.registry.ticketDetailsWidget = publicWidget.Widget.extend({
|
||||
selector: '.o_wevent_js_ticket_details',
|
||||
events: {
|
||||
'click .o_wevent_registration_btn': '_onTicketDetailsClick',
|
||||
'change .custom-select': '_onTicketQuantityChange'
|
||||
},
|
||||
start: function (){
|
||||
this.foldedByDefault = this.$el.data('foldedByDefault') === 1;
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getTotalTicketCount: function (){
|
||||
var ticketCount = 0;
|
||||
this.$('.custom-select').each(function (){
|
||||
ticketCount += parseInt($(this).val());
|
||||
});
|
||||
return ticketCount;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {*} ev
|
||||
*/
|
||||
_onTicketDetailsClick: function (ev){
|
||||
ev.preventDefault();
|
||||
if (this.foldedByDefault){
|
||||
$(ev.currentTarget).toggleClass('btn-primary text-left pl-0');
|
||||
$(ev.currentTarget).siblings().toggleClass('d-none');
|
||||
this.$('.close').toggleClass('d-none');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onTicketQuantityChange: function (){
|
||||
this.$('button.btn-primary').attr('disabled', this._getTotalTicketCount() === 0);
|
||||
}
|
||||
});
|
||||
|
||||
return publicWidget.registry.ticketDetailsWidget;
|
||||
});
|
||||
@@ -1,11 +1,3 @@
|
||||
.o_wevent_event_searchbar_form {
|
||||
flex: 0 0 100%;
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
flex: 0 0 33%;
|
||||
}
|
||||
}
|
||||
|
||||
.o_wevent_index_topbar_filters {
|
||||
.dropdown-toggle {
|
||||
border: $border-width solid gray('400');
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/website_event/static/src/js/website_geolocation.js"></script>
|
||||
<script type="text/javascript" src="/website_event/static/src/js/website_event.js"></script>
|
||||
<script type="text/javascript" src="/website_event/static/src/js/website_event_ticket_details.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
<!-- Content -->
|
||||
<div t-attf-class="o_wevent_events_list #{opt_events_list_cards and 'opt_event_list_cards_bg'}">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<t t-call="website_event.searched_tags"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div id="o_wevent_index_main_col" t-attf-class="col-md my-5 #{opt_events_list_columns and 'opt_events_list_columns' or 'opt_events_list_rows'}">
|
||||
<div class="row">
|
||||
@@ -42,20 +45,40 @@
|
||||
<template id="index_topbar" name="Topbar">
|
||||
<nav class="navbar navbar-light border-top shadow-sm d-print-none">
|
||||
<div class="container">
|
||||
<span class="navbar-brand h4 my-0 mr-4">Events</span>
|
||||
<ul class="o_wevent_index_topbar_filters nav mr-n2"/>
|
||||
<t t-call="website_event.events_search_box">
|
||||
<t t-set="_classes" t-value="'ml-auto pl-lg-3'"/>
|
||||
<t t-set="_searches" t-value="searches"/>
|
||||
</t>
|
||||
<div class="row d-flex justify-content-between w-100 mr-0">
|
||||
<div class="col-lg-8 col-sm-12">
|
||||
<span class="navbar-brand h4 my-0">Events</span>
|
||||
<ul class="o_wevent_index_topbar_filters nav"/>
|
||||
</div>
|
||||
<div class="col-lg-3 col-sm-12 d-flex align-items-center flex-wrap px16">
|
||||
<t t-call="website_event.events_search_box">
|
||||
<t t-set="_searches" t-value="searches"/>
|
||||
</t>
|
||||
<a href="/event" class="btn btn-info text-nowrap mr-0 w-100" role="button" title="Clear filters">
|
||||
<i class="fa fa-eraser"/> Clear filters
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<template id="searched_tags" name="Searched tags">
|
||||
<div class="d-flex align-items-center mt16">
|
||||
<t t-foreach="search_tags" t-as="tag">
|
||||
<span class="align-items-baseline border d-inline-flex pl-2 rounded ml16 mb-2 bg-white">
|
||||
<i class="fa fa-tag mr-2 text-muted"/>
|
||||
<t t-esc="tag.display_name"/>
|
||||
<a t-att-href="'/event?%s' % keep_query('*', tags=str((search_tags - tag).ids))" class="btn border-0 py-1">×</a>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Filter - Date -->
|
||||
<template id="event_time" inherit_id="website_event.index_topbar" customize_show="True" name="Filter by Date">
|
||||
<xpath expr="//ul[hasclass('o_wevent_index_topbar_filters')]" position="inside">
|
||||
<li class="nav-item dropdown mr-2 my-1 my-lg-0">
|
||||
<li class="nav-item dropdown mr-2 my-1">
|
||||
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-calendar"/>
|
||||
<t t-if="current_date" t-esc="current_date"/>
|
||||
@@ -75,33 +98,31 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Filter - Category -->
|
||||
<template id="event_category" inherit_id="website_event.index_topbar" active="False" customize_show="True" name="Filter by Category">
|
||||
<template id="event_category_tag" inherit_id="website_event.index_topbar" active="False" customize_show="True" name="Filter by Tags">
|
||||
<xpath expr="//ul[hasclass('o_wevent_index_topbar_filters')]" position="inside">
|
||||
<li class="nav-item dropdown mr-2 my-1 my-lg-0">
|
||||
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-folder-open"/>
|
||||
<t t-if="current_type" t-esc="current_type.name"/>
|
||||
<t t-else="">All Categories</t>
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<t t-foreach="types" t-as="type">
|
||||
<t t-if="type['event_type_id']">
|
||||
<a t-att-href="keep('/event', type=type['event_type_id'][0])" t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{searches.get('type') == str(type['event_type_id'] and type['event_type_id'][0]) and 'active'}">
|
||||
<t t-esc="type['event_type_id'][1]"/>
|
||||
<span t-esc="type['event_type_id_count']" class="badge badge-pill badge-primary ml-3"/>
|
||||
<t t-foreach="categories" t-as="category">
|
||||
<li t-if="category.tag_ids" class="nav-item dropdown mr-2 my-1">
|
||||
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-folder-open"/>
|
||||
<t t-esc="category.name"/>
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<t t-foreach="category.tag_ids" t-as="tag">
|
||||
<a t-att-href="'/event?%s' % keep_query('*', tags=str((search_tags - tag).ids if tag in search_tags else (tag | search_tags).ids))"
|
||||
t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{'active' if tag in search_tags else ''}">
|
||||
<t t-esc="tag.name"/>
|
||||
</a>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Filter - Location -->
|
||||
<template id="event_location" inherit_id="website_event.index_topbar" active="False" customize_show="True" name="Filter by Country">
|
||||
<xpath expr="//ul[hasclass('o_wevent_index_topbar_filters')]" position="inside">
|
||||
<li class="nav-item dropdown mr-2 my-1 my-lg-0">
|
||||
<li class="nav-item dropdown mr-2 my-1">
|
||||
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-map-marker"/>
|
||||
<t t-if="current_country" t-esc="current_country.name"/>
|
||||
@@ -116,7 +137,7 @@
|
||||
</a>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<a t-att-href="keep('/event', country='online')" t-attf-class="dropdown-item d-flex align-items-center #{searches.get('country') == 'online' and 'active'}">
|
||||
<a t-att-href="keep('/event', country='online')" t-attf-class="dropdown-item d-flex align-items-center justify-content-between #{searches.get('country') == 'online' and 'active'}">
|
||||
<span>Online Events</span>
|
||||
<span t-esc="country['country_id_count']" class="badge badge-pill badge-primary ml-3"/>
|
||||
</a>
|
||||
@@ -130,7 +151,7 @@
|
||||
<!-- Search Box -->
|
||||
<template id="events_search_box" inherit_id="website.website_search_box" primary="True">
|
||||
<xpath expr="//div[@role='search']" position="replace">
|
||||
<form t-attf-class="o_wevent_event_searchbar_form o_wait_lazy_js my-1 my-lg-0 #{_classes}"
|
||||
<form t-attf-class="o_wevent_event_searchbar_form o_wait_lazy_js w-100 my-1 my-lg-0 #{_classes}"
|
||||
t-att-action="action if action else '/event'" method="get">
|
||||
<t t-set="search" t-value="search or _searches and _searches['search']"/>
|
||||
<t>$0</t>
|
||||
@@ -160,66 +181,72 @@
|
||||
</t>
|
||||
<!-- List -->
|
||||
<div t-foreach="event_ids" t-as="event" t-attf-class=" #{opt_event_size} mb-4">
|
||||
<article t-attf-class="h-100 #{opt_events_list_cards and 'card border-0 shadow-sm'}" itemscope="itemscope" itemtype="http://schema.org/Event">
|
||||
<div class="h-100 row no-gutters">
|
||||
<!-- Header -->
|
||||
<header t-attf-class="overflow-hidden bg-secondary #{opt_events_list_columns and 'col-12 rounded-top' or 'col-sm-4 col-lg-3 rounded-left'} #{(not opt_events_list_cards) and 'rounded shadow'} #{(not opt_events_list_cards and not opt_events_list_columns) and 'rounded-top'}">
|
||||
<!-- Image + Link -->
|
||||
<a t-attf-href="/event/#{ slug(event) }/#{(not event.menu_id) and 'register'}" class="d-block h-100 w-100">
|
||||
<t t-call="website.record_cover">
|
||||
<t t-set="_record" t-value="event"/>
|
||||
<a t-attf-href="/event/#{ slug(event) }/#{(not event.menu_id) and 'register'}" class="text-decoration-none">
|
||||
<article t-attf-class="h-100 #{opt_events_list_cards and 'card border-0 shadow-sm'}" itemscope="itemscope" itemtype="http://schema.org/Event">
|
||||
<div class="h-100 row no-gutters">
|
||||
<!-- Header -->
|
||||
<header t-attf-class="overflow-hidden bg-secondary #{opt_events_list_columns and 'col-12 rounded-top' or 'col-sm-4 col-lg-3 rounded-left'} #{(not opt_events_list_cards) and 'rounded shadow'} #{(not opt_events_list_cards and not opt_events_list_columns) and 'rounded-top'}">
|
||||
<!-- Image + Link -->
|
||||
<div class="d-block h-100 w-100">
|
||||
<t t-call="website.record_cover">
|
||||
<t t-set="_record" t-value="event"/>
|
||||
|
||||
<!-- Short Date -->
|
||||
<div class="o_wevent_event_date position-absolute bg-white shadow-sm">
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'format': 'LLL'}" class="o_wevent_event_month"/>
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'format': 'dd'}" class="o_wevent_event_day"/>
|
||||
</div>
|
||||
<!-- Participating -->
|
||||
<small t-if="event.is_participating" class="o_wevent_participating bg-success">
|
||||
<i class="fa fa-check mr-2"/>Registered
|
||||
</small>
|
||||
<!-- Unpublished -->
|
||||
<small t-if="not event.website_published" class="o_wevent_unpublished bg-danger">
|
||||
<i class="fa fa-ban mr-2"/>Unpublished
|
||||
</small>
|
||||
</t>
|
||||
</a>
|
||||
</header>
|
||||
<div t-attf-class="#{opt_events_list_columns and 'col-12' or 'col'}">
|
||||
<!-- Body -->
|
||||
<main t-attf-class="#{opt_events_list_cards and 'card-body' or (opt_events_list_columns and 'py-3' or 'px-4')}">
|
||||
<!-- Title -->
|
||||
<h5 t-attf-class="card-title mt-2 mb-0 text-truncate #{(not event.website_published) and 'text-danger'}">
|
||||
<a t-attf-href="/event/#{ slug(event) }/#{(not event.menu_id) and 'register'}"
|
||||
class="text-reset text-decoration-none"
|
||||
itemprop="url">
|
||||
<span t-field="event.name" itemprop="name"/>
|
||||
</a>
|
||||
<div class="badge badge-pill badge-info ml-2 mt-2 pt4 pb4" t-if="event.is_online">Online</div>
|
||||
</h5>
|
||||
<!-- Start Date & Time -->
|
||||
<time itemprop="startDate" t-att-datetime="event.date_begin">
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'date_only': 'true', 'format': 'long'}"/> -
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'time_only': 'true', 'format': 'short'}"/>
|
||||
</time>
|
||||
<!-- Organizer -->
|
||||
<div t-if="event.organizer_id" class="mb-3">
|
||||
<small class="text-muted text-truncate">Organizer: <span t-field="event.organizer_id" itemprop="organizer"/></small>
|
||||
<!-- Short Date -->
|
||||
<div class="o_wevent_event_date position-absolute bg-white shadow-sm text-dark">
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'format': 'LLL'}" class="o_wevent_event_month"/>
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'format': 'dd'}" class="o_wevent_event_day"/>
|
||||
</div>
|
||||
<!-- Participating -->
|
||||
<small t-if="event.is_participating" class="o_wevent_participating bg-success">
|
||||
<i class="fa fa-check mr-2"/>Registered
|
||||
</small>
|
||||
<!-- Unpublished -->
|
||||
<small t-if="not event.website_published" class="o_wevent_unpublished bg-danger">
|
||||
<i class="fa fa-ban mr-2"/>Unpublished
|
||||
</small>
|
||||
</t>
|
||||
</div>
|
||||
<!-- Location -->
|
||||
<div t-if="not event.is_online" itemprop="location" t-field="event.address_id" t-options="{'widget': 'contact', 'fields': ['city'], 'no_marker': 'true'}"/>
|
||||
</main>
|
||||
</header>
|
||||
<div t-attf-class="#{opt_events_list_columns and 'col-12' or 'col'}">
|
||||
<!-- Body -->
|
||||
<main t-attf-class="#{opt_events_list_cards and 'card-body' or (opt_events_list_columns and 'py-3' or 'px-4')}">
|
||||
<!-- Title -->
|
||||
<h5 t-attf-class="card-title mt-2 mb-0 text-truncate #{(not event.website_published) and 'text-danger'}">
|
||||
<span t-field="event.name" itemprop="name"/>
|
||||
<div class="badge badge-pill badge-info ml-2 mt-2 pt4 pb4" t-if="event.is_online">Online</div>
|
||||
</h5>
|
||||
<!-- Start Date & Time -->
|
||||
<time itemprop="startDate" t-att-datetime="event.date_begin">
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'date_only': 'true', 'format': 'long'}"/> -
|
||||
<span t-field="event.with_context(tz=event.date_tz).date_begin" t-options="{'time_only': 'true', 'format': 'short'}"/>
|
||||
</time>
|
||||
<!-- Organizer -->
|
||||
<div t-if="event.organizer_id" class="mb-3">
|
||||
<small class="text-muted text-truncate">Organizer: <span t-field="event.organizer_id" itemprop="organizer"/></small>
|
||||
</div>
|
||||
<!-- Location -->
|
||||
<div t-if="not event.is_online" itemprop="location" t-field="event.address_id" t-options="{'widget': 'contact', 'fields': ['city'], 'no_marker': 'true'}"/>
|
||||
<div t-else="" class="invisible">online</div>
|
||||
<div class="mt8 d-flex align-items-center">
|
||||
<t t-foreach="event.tag_ids" t-as="tag">
|
||||
<span t-attf-class="badge mr8 #{'badge-primary' if tag in search_tags else 'badge-light'}">
|
||||
<span t-esc="tag.name"/>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<footer
|
||||
t-attf-class="small align-self-end w-100 #{opt_events_list_cards and 'card-footer' or ((not opt_events_list_columns) and 'border-top mx-4 mt-auto pt-2' or 'border-top py-2')}">
|
||||
<span class="text-danger"
|
||||
t-if="event.seats_availability == 'limited' and not event.seats_available">
|
||||
Sold Out
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<footer t-if="event.event_registrations_open"
|
||||
t-attf-class="small align-self-end w-100 #{opt_events_list_cards and 'card-footer' or ((not opt_events_list_columns) and 'border-top mx-4 mt-auto pt-2' or 'border-top py-2')}">
|
||||
<span class="text-danger"
|
||||
t-if="event.seats_availability == 'limited' and not event.seats_available">
|
||||
Sold Out
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</article>
|
||||
</article>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Pager -->
|
||||
<div class="form-inline justify-content-center my-3">
|
||||
@@ -231,9 +258,9 @@
|
||||
|
||||
<template id="opt_events_list_cards" inherit_id="website_event.events_list" active="True" customize_show="True" name="'Cards' Design"/>
|
||||
|
||||
<template id="opt_events_list_categories" inherit_id="website_event.events_list" active="True" customize_show="True" name="Show categories">
|
||||
<template id="opt_events_list_categories" inherit_id="website_event.events_list" active="False" customize_show="True" name="Show Templates">
|
||||
<xpath expr="//main/*" position="before">
|
||||
<a t-if="event.event_type_id" t-attf-href="/event?type=#{event.event_type_id.id}" t-attf-class="badge bg-secondary o_wevent_badge #{opt_events_list_columns and 'o_wevent_badge_event' or 'float-right'}" t-field="event.event_type_id"/>
|
||||
<span t-if="event.event_type_id" t-attf-href="/event?type=#{event.event_type_id.id}" t-attf-class="badge bg-secondary o_wevent_badge #{opt_events_list_columns and 'o_wevent_badge_event' or 'float-right'}" t-field="event.event_type_id"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -290,20 +317,6 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Index - Sidebar - Categories -->
|
||||
<template id="index_sidebar_categories" inherit_id="website_event.index_sidebar" active="True" customize_show="True" name="Categories" priority="50">
|
||||
<xpath expr="//div[@id='o_wevent_index_sidebar']" position="inside">
|
||||
<div class="o_wevent_sidebar_block">
|
||||
<h6 class="o_wevent_sidebar_title">Categories</h6>
|
||||
<t t-foreach="types" t-as="type">
|
||||
<t t-if="type['event_type_id']">
|
||||
<a t-att-href="keep('/event', type=type['event_type_id'][0])" class="badge badge-secondary o_wevent_badge mb-2"><t t-esc="type['event_type_id'][1]"/></a>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Index - Sidebar - Quotes -->
|
||||
<template id="index_sidebar_quotes" inherit_id="website_event.index_sidebar" active="True" customize_show="True" name="Quotes" priority="60">
|
||||
<xpath expr="//div[@id='o_wevent_index_sidebar']" position="inside">
|
||||
@@ -414,7 +427,7 @@
|
||||
<div class="container overflow-hidden">
|
||||
<div class="row no-gutters mt-n4">
|
||||
<!-- Description -->
|
||||
<div id="o_wevent_event_main_col" class="col-lg-8 bg-white px-5 pt-4 pb-0 shadow-sm">
|
||||
<div id="o_wevent_event_main_col" class="col-lg-8 bg-white px-5 pt64 pb-0 shadow-sm">
|
||||
<span t-field="event.description" itemprop="description"/>
|
||||
</div>
|
||||
<div class="col-lg-4 bg-light shadow-sm d-print-none">
|
||||
@@ -473,6 +486,11 @@
|
||||
<a t-att-href="event.google_map_link()" target="_blank">Get the direction</a>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="o_wevent_sidebar_block">
|
||||
<h6 class="o_wevent_sidebar_title">Online Event</h6>
|
||||
</div>
|
||||
</t>
|
||||
<!-- Organizer -->
|
||||
<div t-if="event.organizer_id" class="o_wevent_sidebar_block">
|
||||
<h6 class="o_wevent_sidebar_title">Organizer</h6>
|
||||
@@ -512,36 +530,56 @@
|
||||
</template>
|
||||
|
||||
<!-- Event - Registration -->
|
||||
<template id="registration_template">
|
||||
<div t-if="event.start_sale_date and event.start_sale_date > datetime.date.today() and not event.event_registrations_open"
|
||||
class="alert alert-info mb-5" role="status">
|
||||
<em >Ticket Sales starting on <t t-esc="event.start_sale_date"/></em>
|
||||
<template id="registration_template" name="Registration">
|
||||
<div t-if="request.env.user.has_group('event.group_event_manager')" class="alert alert-info rounded-0 o_website_event_configuration o_not_editable" role="status">
|
||||
<a t-attf-href="/web#id=#{event.id}&view_type=form&model=event.event">
|
||||
<i class="fa fa-pencil mr-2" role="img" aria-label="Edit" title="Edit event registration in backend"/><em>Configure Event Tickets</em>
|
||||
</a>
|
||||
</div>
|
||||
<div t-if="event.start_sale_date and event.start_sale_date > datetime.date.today() or not event.event_registrations_open" class="bg-white mb-5">
|
||||
<div class="alert alert-info mb-0 d-flex justify-content-between align-items-center" role="status">
|
||||
<t t-if="event.start_sale_date and event.start_sale_date > datetime.date.today()">
|
||||
<em class="col-md-8">Ticket Sales starting on <t t-esc="event.start_sale_date"/></em>
|
||||
<button class="btn btn-danger mr-1 ml-3 col-md-4" disabled="1">Registrations not yet open</button>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="col-md-8">
|
||||
<em t-if="not sold_out or event.date_end < datetime.datetime.now()">Registrations are <b>closed</b></em>
|
||||
<em t-else="">Tickets for this Event are <b>Sold Out</b></em>
|
||||
</div>
|
||||
<button class="btn btn-danger ml-3 col-md-4 py-2" disabled="1">
|
||||
<span t-if="not sold_out or event.date_end < datetime.datetime.now()">Registrations Closed</span>
|
||||
<span t-else="">Sold Out</span>
|
||||
</button>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<form t-if="event.event_registrations_open and (not event.event_ticket_ids or not all([ticket.is_expired for ticket in event.event_ticket_ids]))"
|
||||
id="registration_form"
|
||||
class="mb-5"
|
||||
t-attf-action="/event/#{slug(event)}/registration/new" method="post"
|
||||
class="mb-5" itemscope="itemscope" itemprop="offers" itemtype="http://schema.org/AggregateOffer">
|
||||
itemscope="itemscope" itemprop="offers" itemtype="http://schema.org/AggregateOffer">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<div id="o_wevent_tickets" class="bg-white shadow-sm">
|
||||
|
||||
<div id="o_wevent_tickets" class="bg-white shadow-sm o_wevent_js_ticket_details" data-folded-by-default="0">
|
||||
<t t-set="tickets" t-value="event.event_ticket_ids.filtered(lambda ticket: not ticket.is_expired)"/>
|
||||
<!-- If some tickets expired and there is only one type left, we keep the same layout -->
|
||||
<t t-if="len(event.event_ticket_ids) > 1">
|
||||
<div class="d-flex align-items-center py-2 pl-3 pr-2 border-bottom">
|
||||
<div class="d-flex justify-content-between align-items-center py-2 pl-3 pr-2 border-bottom">
|
||||
<div id="price-range" class="pr-3 d-none"/>
|
||||
<span t-if="not event.event_registrations_open" class="text-danger">
|
||||
<i class="fa fa-ban mr-2"/>Sold Out
|
||||
</span>
|
||||
<div class="ml-auto pr-3">
|
||||
<span t-if="event.is_participating" class="text-success ml-3">
|
||||
<div t-if="event.is_participating" id="registered" class="ml-auto pr-3 d-none">
|
||||
<span class="text-success ml-3">
|
||||
<i class="fa fa-check mr-2"/>Registered
|
||||
</span>
|
||||
</div>
|
||||
<a href="#" role="button" class="btn btn-primary o_wevent_registration_btn collapsed" data-toggle="collapse" data-target="#o_wevent_tickets_collapse">
|
||||
<a href="#" role="button" class="btn o_wevent_registration_btn text-left pl-0" data-target="#o_wevent_tickets_collapse">
|
||||
Tickets
|
||||
<span class="close">×</span>
|
||||
<span class="close d-none">×</span>
|
||||
</a>
|
||||
</div>
|
||||
<div id="o_wevent_tickets_collapse" class="collapse">
|
||||
<div id="o_wevent_tickets_collapse" class="collapse show">
|
||||
<div t-foreach="tickets" t-as="ticket" class="row px-3 mx-0 bg-light border-bottom">
|
||||
<div class="col-8 py-3" itemscope="itemscope" itemtype="http://schema.org/Offer">
|
||||
<h5 itemprop="name" t-field="ticket.name" class="my-0"/>
|
||||
@@ -565,12 +603,16 @@
|
||||
<option t-esc="nb" t-att-selected="len(ticket) == 0 and nb == 0 and 'selected'"/>
|
||||
</t>
|
||||
</select>
|
||||
<div t-else="" class="text-danger">
|
||||
<span t-if="not ticket.sale_available and not ticket.is_expired and ticket.start_sale_date < datetime.date.today()" >Sold Out</span>
|
||||
<span t-if="ticket.is_expired">Expired</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-md-4 offset-md-8 py-2 pl-md-0 pr-md-2">
|
||||
<button type="submit" class="btn btn-primary o_wait_lazy_js btn-block a-submit" t-att-disabled="not event.event_registrations_open or None" t-attf-id="#{event.id}">Register</button>
|
||||
<button type="submit" class="btn btn-primary o_wait_lazy_js btn-block a-submit" disabled="" t-attf-id="#{event.id}">Register</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -578,17 +620,13 @@
|
||||
<div t-else="" class="o_wevent_registration_single">
|
||||
<div class="row p-2 pl-3">
|
||||
<div class="col-lg-8 d-flex flex-columns align-items-center" itemscope="itemscope" itemtype="http://schema.org/Offer">
|
||||
<h6 itemprop="name" class="my-0 pr-3 border-right">
|
||||
<h6 itemprop="name" class="my-0 pr-3 border-right text-dark">
|
||||
<span t-if="tickets" t-field="tickets.name"/>
|
||||
<span t-else="">Registration</span>
|
||||
</h6>
|
||||
|
||||
<small t-if="tickets.start_sale_date" class="text-muted ml-3" itemprop="availabilityEnds">
|
||||
Sales end on <span itemprop="priceValidUntil" t-field="tickets.end_sale_date"/>
|
||||
</small>
|
||||
<div class="ml-auto">
|
||||
<t t-if="event.event_registrations_open">
|
||||
<span class="font-weight-bold align-middle pr-2">Qty</span>
|
||||
<span class="text-dark font-weight-bold align-middle pr-2">Qty</span>
|
||||
<link itemprop="availability" content="http://schema.org/InStock"/>
|
||||
<select t-att-name="'nb_register-%s' % (tickets.id if tickets else 0)" class="w-auto custom-select">
|
||||
<t t-set="seats_max_ticket" t-value="(not tickets or tickets.seats_availability == 'unlimited' or tickets.seats_available > 9) and 10 or tickets.seats_available + 1"/>
|
||||
@@ -610,19 +648,9 @@
|
||||
<button type="submit" class="btn btn-primary o_wait_lazy_js btn-block a-submit" t-attf-id="#{event.id}">Register</button>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="tickets.description" class="row mx-0">
|
||||
<div class="col-12 bg-200 rounded-bottom">
|
||||
<p itemprop="description" t-field="tickets.description" class="small my-2"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div t-elif="request.env.user.has_group('event.group_event_manager')" class="alert alert-info rounded-0 mb-5 o_website_event_configuration o_not_editable" role="status">
|
||||
<a t-attf-href="/web#id=#{event.id}&view_type=form&model=event.event">
|
||||
<i class="fa fa-pencil mr-2" role="img" aria-label="Create" title="Create"/><em>Configure Event Registration</em>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="registration_attendee_details" name="Registration Attendee Details">
|
||||
@@ -812,4 +840,26 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="fold_register_details" inherit_id="website_event.registration_template" active="False" customize_show="True" name="Fold ticket details">
|
||||
<xpath expr="//div[@id='o_wevent_tickets']" position="attributes">
|
||||
<attribute name="data-folded-by-default">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='registered']" position="attributes">
|
||||
<attribute name="class">ml-auto pr-3</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='price-range']" position="attributes">
|
||||
<attribute name="class">ml-0 pr-3</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='o_wevent_tickets_collapse']" position="attributes">
|
||||
<attribute name="class">collapse</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//a[hasclass('o_wevent_registration_btn')]" position="attributes">
|
||||
<attribute name="class">btn btn-primary o_wevent_registration_btn collapsed</attribute>
|
||||
<attribute name="data-toggle">collapse</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//span[hasclass('close')]" position="attributes">
|
||||
<attribute name="class">close d-none</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -15,9 +15,17 @@
|
||||
</t>
|
||||
<span t-else="" class="font-weight-bold text-uppercase">Free</span>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='price-range']" position="inside">
|
||||
<span class="text-dark">
|
||||
From
|
||||
<span t-esc="event.event_ticket_ids[0].price_reduce" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
to
|
||||
<span t-esc="event.event_ticket_ids[-1].price_reduce" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
|
||||
</span>
|
||||
</xpath>
|
||||
<!-- Add price information on tickets (mono ticket, aka not in collapse) -->
|
||||
<xpath expr="//div[hasclass('o_wevent_registration_single')]//h6" position="after">
|
||||
<div class="border-right px-3">
|
||||
<div class="border-right px-3 text-dark">
|
||||
<t t-if="tickets.price">
|
||||
<t t-if="(tickets.price-website.get_current_pricelist().currency_id._convert(tickets.price_reduce, event.company_id.sudo().currency_id, event.company_id, datetime.date.today())) > 1 and website.get_current_pricelist().discount_policy == 'without_discount'">
|
||||
<del class="text-danger mr-1" t-field="tickets.price" t-options="{'widget': 'monetary', 'from_currency': event.company_id.sudo().currency_id, 'display_currency': website.get_current_pricelist().currency_id}"/>
|
||||
|
||||
Reference in New Issue
Block a user