[MERGE] (website_)event(_sale): prepare ticket model update (views, tests)
PURPOSE
Integration between event and eCommerce is required only when users handle the
entire selling process online. However users may require ticketing support
while managing payments outside of Odoo. Purpose of this commit is to prepare
support of tickets directly in event application without need of sales.
SPECIFICATIONS
Clean existing event type demo and data
Clean fields definition and parameters (no functional or technical
changes, just cleaning their definition).
Add tests before updating models. As some event models (event.type and
event.ticket notably) are about to be modified let us add some tests to avoid
regressions. Security tests are also moved in their own file. Some internals
tests defined in event_sale are also moved in their own file. That way we
avoid bloating files with unrelated tests. Future commits will add tests for
event and event.type models.
Prepare views for ticket model update :
* define views for ticket model. Currently views are directly embedded
in o2m of event.type and event.event, leading to complex xpath to
modify them. They are now real views;
* split templates in website_event_sale, to have templates related to
website_event and website_sale separated, leading to more simple diff
comprehension;
Add some fixes :
* copy event type mail scheduling only if activated on event type;
fix seats_max field not editable;
See sub commits for more details.
LINKS
Task ID 2177281 (support tickets directly in event)
Prepares Community PR #43488
Closes Community PR #44066
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -14,13 +14,13 @@
|
||||
<field name="doall" eval="False" />
|
||||
</record>
|
||||
|
||||
<!-- Event Categories -->
|
||||
<record id="event_type_data_online" model="event.type">
|
||||
<field name="name">Online</field>
|
||||
<field name="is_online" eval="True"/>
|
||||
<field name="auto_confirm" eval="True"/>
|
||||
<field name="use_mail_schedule" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="event_type_data_physical" model="event.type">
|
||||
<field name="name">Physical Event</field>
|
||||
<field name="is_online" eval="False"/>
|
||||
|
||||
@@ -7,27 +7,16 @@
|
||||
|
||||
<!-- Event Type -->
|
||||
<record id="event_type_0" model="event.type">
|
||||
<field name="name">Seminar</field>
|
||||
</record>
|
||||
|
||||
<record id="event_type_1" model="event.type">
|
||||
<field name="name">Exhibition</field>
|
||||
<field name="use_mail_schedule" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="event_type_2" model="event.type">
|
||||
<field name="name">Conference</field>
|
||||
</record>
|
||||
|
||||
<record id="event_type_3" model="event.type">
|
||||
<field name="name">Show</field>
|
||||
</record>
|
||||
|
||||
<record id="event_type_4" model="event.type">
|
||||
<record id="event_type_1" model="event.type">
|
||||
<field name="name">Training</field>
|
||||
<field name="use_mail_schedule" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="event_type_5" model="event.type">
|
||||
<record id="event_type_2" model="event.type">
|
||||
<field name="name">Sport</field>
|
||||
<field name="use_mail_schedule" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- Event -->
|
||||
@@ -39,7 +28,7 @@
|
||||
<field name="seats_availability">limited</field>
|
||||
<field name="seats_max">500</field>
|
||||
<field name="address_id" ref="base.res_partner_1"/>
|
||||
<field name="event_type_id" ref="event_type_1"/>
|
||||
<field name="event_type_id" ref="event_type_0"/>
|
||||
<field name="stage_id" ref="event_stage_booked"/>
|
||||
</record>
|
||||
|
||||
@@ -48,7 +37,7 @@
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=100)).strftime('%Y-%m-%d 20:15:00')" name="date_begin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=101)).strftime('%Y-%m-%d 00:30:00')" name="date_end"/>
|
||||
<field name="event_type_id" ref="event_type_1"/>
|
||||
<field name="event_type_id" ref="event_type_data_physical"/>
|
||||
<field name="address_id" ref="base.res_partner_1"/>
|
||||
<field name="seats_availability">unlimited</field>
|
||||
<field name="stage_id" ref="event_stage_booked"/>
|
||||
@@ -59,7 +48,7 @@
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=5)).strftime('%Y-%m-%d 07:00:00')" name="date_begin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=5)).strftime('%Y-%m-%d 16:30:00')" name="date_end"/>
|
||||
<field name="event_type_id" ref="event_type_2"/>
|
||||
<field name="event_type_id" ref="event_type_data_online"/>
|
||||
<field name="address_id" ref="base.res_partner_4"/>
|
||||
<field name="seats_availability">limited</field>
|
||||
<field name="seats_max">200</field>
|
||||
@@ -71,7 +60,7 @@
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=130)).strftime('%Y-%m-%d 20:15:00')" name="date_begin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=133)).strftime('%Y-%m-%d 00:30:00')" name="date_end"/>
|
||||
<field name="event_type_id" ref="event_type_3"/>
|
||||
<field name="event_type_id" ref="event_type_0"/>
|
||||
<field name="address_id" ref="base.res_partner_3"/>
|
||||
<field name="seats_availability">unlimited</field>
|
||||
<field name="stage_id" ref="event_stage_announced"/>
|
||||
@@ -82,7 +71,7 @@
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=50)).strftime('%Y-%m-%d 18:00:00')" name="date_begin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=50)).strftime('%Y-%m-%d 22:30:00')" name="date_end"/>
|
||||
<field name="event_type_id" ref="event_type_4"/>
|
||||
<field name="event_type_id" ref="event_type_1"/>
|
||||
<field name="address_id" ref="base.res_partner_4"/>
|
||||
<field name="stage_id" ref="event_stage_announced"/>
|
||||
</record>
|
||||
@@ -92,7 +81,7 @@
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=370)).strftime('%Y-%m-%d 09:00:00')" name="date_begin"/>
|
||||
<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_5"/>
|
||||
<field name="event_type_id" ref="event_type_2"/>
|
||||
<field name="address_id" ref="base.res_partner_1"/>
|
||||
</record>
|
||||
|
||||
@@ -101,7 +90,7 @@
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=30)).strftime('%Y-%m-%d 09:30:00')" name="date_begin"/>
|
||||
<field eval="(DateTime.today()+ timedelta(days=30)).strftime('%Y-%m-%d 17:30:00')" name="date_end"/>
|
||||
<field name="event_type_id" ref="event_type_1"/>
|
||||
<field name="event_type_id" ref="event_type_0"/>
|
||||
<field name="address_id" ref="base.res_partner_1"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -31,12 +31,6 @@ class EventType(models.Model):
|
||||
'interval_unit': 'now',
|
||||
'interval_type': 'after_sub',
|
||||
'template_id': self.env.ref('event.event_subscription').id,
|
||||
}), (0, 0, {
|
||||
'notification_type': 'mail',
|
||||
'interval_nbr': 1,
|
||||
'interval_unit': 'days',
|
||||
'interval_type': 'before_event',
|
||||
'template_id': self.env.ref('event.event_reminder').id,
|
||||
}), (0, 0, {
|
||||
'notification_type': 'mail',
|
||||
'interval_nbr': 10,
|
||||
@@ -48,8 +42,7 @@ class EventType(models.Model):
|
||||
name = fields.Char('Event Category', required=True, translate=True)
|
||||
sequence = fields.Integer()
|
||||
# registration
|
||||
has_seats_limitation = fields.Boolean(
|
||||
'Limited Seats', default=False)
|
||||
has_seats_limitation = fields.Boolean('Limited Seats')
|
||||
default_registration_min = fields.Integer(
|
||||
'Minimum Registrations', default=0,
|
||||
help="It will select this default minimum value when you choose this event")
|
||||
@@ -94,28 +87,22 @@ class EventEvent(models.Model):
|
||||
event_stages = self.env['event.stage'].search([])
|
||||
return event_stages[0] if event_stages else False
|
||||
|
||||
name = fields.Char(
|
||||
string='Event', translate=True, required=True,
|
||||
readonly=False)
|
||||
name = fields.Char(string='Event', translate=True, required=True)
|
||||
note = fields.Text(string='Note')
|
||||
description = fields.Html(string='Description', translate=html_translate, sanitize_attributes=False)
|
||||
active = fields.Boolean(default=True)
|
||||
user_id = fields.Many2one(
|
||||
'res.users', string='Responsible',
|
||||
default=lambda self: self.env.user,
|
||||
tracking=True,
|
||||
readonly=False)
|
||||
'res.users', string='Responsible', tracking=True,
|
||||
default=lambda self: self.env.user)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company', change_default=True,
|
||||
default=lambda self: self.env.company,
|
||||
required=False, readonly=False)
|
||||
required=False)
|
||||
organizer_id = fields.Many2one(
|
||||
'res.partner', string='Organizer',
|
||||
tracking=True,
|
||||
'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',
|
||||
readonly=False)
|
||||
event_type_id = fields.Many2one('event.type', string='Category', ondelete='set null')
|
||||
color = fields.Integer('Kanban Color Index')
|
||||
event_mail_ids = fields.One2many('event.mail', 'event_id', string='Mail Schedule', copy=True)
|
||||
# Kanban fields
|
||||
@@ -130,7 +117,6 @@ class EventEvent(models.Model):
|
||||
# Seats and computation
|
||||
seats_max = fields.Integer(
|
||||
string='Maximum Attendees Number',
|
||||
readonly=True,
|
||||
help="For each event you can define a maximum registration of seats(number of attendees), above this numbers the registrations are not accepted.")
|
||||
seats_availability = fields.Selection(
|
||||
[('limited', 'Limited'), ('unlimited', 'Unlimited')],
|
||||
@@ -154,36 +140,26 @@ class EventEvent(models.Model):
|
||||
string='Number of Expected Attendees',
|
||||
compute_sudo=True, readonly=True, compute='_compute_seats')
|
||||
# Registration fields
|
||||
registration_ids = fields.One2many(
|
||||
'event.registration', 'event_id', string='Attendees',
|
||||
readonly=False)
|
||||
auto_confirm = fields.Boolean(string='Autoconfirm Registrations')
|
||||
registration_ids = fields.One2many('event.registration', 'event_id', string='Attendees')
|
||||
event_registrations_open = fields.Boolean('Registration open', compute='_compute_event_registrations_open')
|
||||
# Date fields
|
||||
date_tz = fields.Selection(
|
||||
_tz_get, string='Timezone', required=True,
|
||||
default=lambda self: self.env.user.tz or 'UTC')
|
||||
date_begin = fields.Datetime(
|
||||
string='Start Date', required=True,
|
||||
tracking=True)
|
||||
date_end = fields.Datetime(
|
||||
string='End Date', required=True,
|
||||
tracking=True)
|
||||
date_begin = fields.Datetime(string='Start Date', required=True, tracking=True)
|
||||
date_end = fields.Datetime(string='End Date', required=True, tracking=True)
|
||||
date_begin_located = fields.Char(string='Start Date Located', compute='_compute_date_begin_tz')
|
||||
date_end_located = fields.Char(string='End Date Located', compute='_compute_date_end_tz')
|
||||
is_one_day = fields.Boolean(compute='_compute_field_is_one_day')
|
||||
auto_confirm = fields.Boolean(string='Autoconfirm Registrations')
|
||||
# Location and communication
|
||||
is_online = fields.Boolean('Online Event')
|
||||
address_id = fields.Many2one(
|
||||
'res.partner', string='Location',
|
||||
'res.partner', string='Location', tracking=True,
|
||||
default=lambda self: self.env.company.partner_id,
|
||||
readonly=False,
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
|
||||
tracking=True)
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
|
||||
country_id = fields.Many2one('res.country', 'Country', related='address_id.country_id', store=True, readonly=False)
|
||||
twitter_hashtag = fields.Char('Twitter Hashtag')
|
||||
description = fields.Html(
|
||||
string='Description', translate=html_translate, sanitize_attributes=False,
|
||||
readonly=False)
|
||||
# badge fields
|
||||
badge_front = fields.Html(string='Badge Front')
|
||||
badge_back = fields.Html(string='Badge Back')
|
||||
@@ -287,7 +263,7 @@ class EventEvent(models.Model):
|
||||
|
||||
self.is_online = self.event_type_id.is_online
|
||||
|
||||
if self.event_type_id.event_type_mail_ids:
|
||||
if self.event_type_id.use_mail_schedule and self.event_type_id.event_type_mail_ids:
|
||||
self.event_mail_ids = [(5, 0, 0)] + [
|
||||
(0, 0, {
|
||||
attribute_name: line[attribute_name] if not isinstance(line[attribute_name], models.BaseModel) else line[attribute_name].id
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
from . import test_event_flow
|
||||
from . import test_event_internals
|
||||
from . import test_event_mail_schedule
|
||||
from . import test_event_security
|
||||
|
||||
@@ -44,7 +44,7 @@ class TestEventCommon(common.SavepointCase):
|
||||
|
||||
cls.customer = cls.env['res.partner'].create({
|
||||
'name': 'Constantin Customer',
|
||||
'email': 'constantin@example.com',
|
||||
'email': 'constantin@test.example.com',
|
||||
'country_id': cls.env.ref('base.be').id,
|
||||
'phone': '0485112233',
|
||||
})
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.event.tests.common import TestEventCommon
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.fields import Datetime as FieldsDatetime
|
||||
from odoo.tests.common import users
|
||||
from odoo.tools import mute_logger
|
||||
@@ -99,14 +97,19 @@ class TestEventData(TestEventCommon):
|
||||
|
||||
@users('user_eventmanager')
|
||||
@mute_logger('odoo.models.unlink')
|
||||
def test_event_type_configuration(self):
|
||||
def test_event_configuration_from_type(self):
|
||||
""" Test data computation of event coming from its event.type template.
|
||||
Some one2many notably are duplicated from type configuration and some
|
||||
advanced testing is required, notably mail schedulers. """
|
||||
|
||||
self.assertEqual(self.env.user.tz, 'Europe/Brussels')
|
||||
|
||||
event_type = self.event_type_complex.with_user(self.env.user)
|
||||
event_type.write({
|
||||
'use_mail_schedule': False,
|
||||
})
|
||||
# self.assertEqual(event_type.event_type_mail_ids, self.env['event.type.mail']) # TDE FIXME: currently crashing
|
||||
# Event type does not use mail schedule but data is kept for compatibility and avoid recreating them
|
||||
self.assertEqual(len(event_type.event_type_mail_ids), 2)
|
||||
|
||||
event = self.env['event.event'].create({
|
||||
'name': 'Event Update Type',
|
||||
@@ -132,7 +135,7 @@ class TestEventData(TestEventCommon):
|
||||
self.assertTrue(event.auto_confirm)
|
||||
self.assertEqual(event.twitter_hashtag, event_type.default_hashtag)
|
||||
self.assertFalse(event.is_online)
|
||||
# self.assertEqual(event.event_mail_ids, self.env['event.mail']) # TDE FIXME: currently crashing
|
||||
self.assertEqual(event.event_mail_ids, self.env['event.mail'])
|
||||
|
||||
event_type.write({
|
||||
'use_mail_schedule': True,
|
||||
@@ -147,97 +150,26 @@ class TestEventData(TestEventCommon):
|
||||
self.assertEqual(event.event_mail_ids.template_id, self.env.ref('event.event_reminder'))
|
||||
|
||||
|
||||
class TestEventSecurity(TestEventCommon):
|
||||
|
||||
@users('user_employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||
def test_event_access_employee(self):
|
||||
# employee can read events (sure ?)
|
||||
event = self.event_0.with_user(self.env.user)
|
||||
event.read(['name'])
|
||||
# event.stage_id.read(['name', 'description'])
|
||||
# event.event_type_id.read(['name', 'has_seats_limitation'])
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['event.event'].create({
|
||||
'name': 'TestEvent',
|
||||
'date_begin': datetime.now() + relativedelta(days=-1),
|
||||
'date_end': datetime.now() + relativedelta(days=1),
|
||||
'seats_max': 10,
|
||||
})
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
event.write({
|
||||
'name': 'TestEvent Modified',
|
||||
})
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['event.stage'].create({
|
||||
'name': 'TestStage',
|
||||
})
|
||||
|
||||
@users('user_eventuser')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||
def test_event_access_event_user(self):
|
||||
event = self.event_0.with_user(self.env.user)
|
||||
event.read(['name', 'user_id', 'kanban_state_label'])
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['event.event'].create({
|
||||
'name': 'TestEvent',
|
||||
'date_begin': datetime.now() + relativedelta(days=-1),
|
||||
'date_end': datetime.now() + relativedelta(days=1),
|
||||
'seats_max': 10,
|
||||
})
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
event.write({
|
||||
'name': 'TestEvent Modified',
|
||||
})
|
||||
|
||||
# with self.assertRaises(AccessError):
|
||||
# self.env['event.stage'].create({
|
||||
# 'name': 'TestStage',
|
||||
# })
|
||||
class TestEventTypeData(TestEventCommon):
|
||||
|
||||
@users('user_eventmanager')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||
def test_event_access_event_manager(self):
|
||||
# EventManager can do about everything
|
||||
def test_event_type_fields(self):
|
||||
""" Test event type fields synchronization """
|
||||
# create test type and ensure its initial values
|
||||
event_type = self.env['event.type'].create({
|
||||
'name': 'ManagerEventType',
|
||||
'use_mail_schedule': True,
|
||||
'event_type_mail_ids': [(5, 0), (0, 0, {
|
||||
'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event',
|
||||
'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')})]
|
||||
'name': 'Testing fields computation',
|
||||
'has_seats_limitation': True,
|
||||
'default_registration_min': 5,
|
||||
'default_registration_max': 30,
|
||||
})
|
||||
event = self.env['event.event'].create({
|
||||
'name': 'ManagerEvent',
|
||||
'date_begin': datetime.now() + relativedelta(days=-1),
|
||||
'date_end': datetime.now() + relativedelta(days=1),
|
||||
})
|
||||
event.update({'event_type_id': event_type.id})
|
||||
event._onchange_type()
|
||||
event.flush()
|
||||
event_type._onchange_has_seats_limitation()
|
||||
self.assertTrue(event_type.has_seats_limitation)
|
||||
self.assertEqual(event_type.default_registration_min, 5)
|
||||
self.assertEqual(event_type.default_registration_max, 30)
|
||||
|
||||
registration = self.env['event.registration'].create({'event_id': event.id})
|
||||
registration.write({'name': 'Myself'})
|
||||
|
||||
stage = self.env.ref('event.event_stage_done')
|
||||
stage.write({'name': 'ManagerEnded'})
|
||||
event.write({'stage_id': stage.id})
|
||||
|
||||
registration.unlink()
|
||||
event.unlink()
|
||||
stage.unlink()
|
||||
event_type.unlink()
|
||||
|
||||
# Settings access rights required to enable some features
|
||||
self.user_eventmanager.write({'groups_id': [
|
||||
(3, self.env.ref('base.group_system').id),
|
||||
(4, self.env.ref('base.group_erp_manager').id)
|
||||
]})
|
||||
with self.assertRaises(AccessError):
|
||||
event_config = self.env['res.config.settings'].with_user(self.user_eventmanager).create({
|
||||
})
|
||||
event_config.execute()
|
||||
# reset seats limitation
|
||||
event_type.write({'has_seats_limitation': False})
|
||||
event_type._onchange_has_seats_limitation()
|
||||
self.assertFalse(event_type.has_seats_limitation)
|
||||
self.assertEqual(event_type.default_registration_min, 0)
|
||||
self.assertEqual(event_type.default_registration_max, 0)
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo.addons.event.tests.common import TestEventCommon
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.tests.common import users
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
class TestEventSecurity(TestEventCommon):
|
||||
|
||||
@users('user_employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||
def test_event_access_employee(self):
|
||||
# employee can read events (sure ?)
|
||||
event = self.event_0.with_user(self.env.user)
|
||||
event.read(['name'])
|
||||
# event.stage_id.read(['name', 'description'])
|
||||
# event.event_type_id.read(['name', 'has_seats_limitation'])
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['event.event'].create({
|
||||
'name': 'TestEvent',
|
||||
'date_begin': datetime.now() + relativedelta(days=-1),
|
||||
'date_end': datetime.now() + relativedelta(days=1),
|
||||
'seats_max': 10,
|
||||
})
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
event.write({
|
||||
'name': 'TestEvent Modified',
|
||||
})
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['event.stage'].create({
|
||||
'name': 'TestStage',
|
||||
})
|
||||
|
||||
@users('user_eventuser')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||
def test_event_access_event_user(self):
|
||||
event = self.event_0.with_user(self.env.user)
|
||||
event.read(['name', 'user_id', 'kanban_state_label'])
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['event.event'].create({
|
||||
'name': 'TestEvent',
|
||||
'date_begin': datetime.now() + relativedelta(days=-1),
|
||||
'date_end': datetime.now() + relativedelta(days=1),
|
||||
'seats_max': 10,
|
||||
})
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
event.write({
|
||||
'name': 'TestEvent Modified',
|
||||
})
|
||||
|
||||
# with self.assertRaises(AccessError):
|
||||
# self.env['event.stage'].create({
|
||||
# 'name': 'TestStage',
|
||||
# })
|
||||
|
||||
@users('user_eventmanager')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||
def test_event_access_event_manager(self):
|
||||
# EventManager can do about everything
|
||||
event_type = self.env['event.type'].create({
|
||||
'name': 'ManagerEventType',
|
||||
'use_mail_schedule': True,
|
||||
'event_type_mail_ids': [(5, 0), (0, 0, {
|
||||
'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event',
|
||||
'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')})]
|
||||
})
|
||||
event = self.env['event.event'].create({
|
||||
'name': 'ManagerEvent',
|
||||
'date_begin': datetime.now() + relativedelta(days=-1),
|
||||
'date_end': datetime.now() + relativedelta(days=1),
|
||||
})
|
||||
event.update({'event_type_id': event_type.id})
|
||||
event._onchange_type()
|
||||
event.flush()
|
||||
|
||||
registration = self.env['event.registration'].create({'event_id': event.id})
|
||||
registration.write({'name': 'Myself'})
|
||||
|
||||
stage = self.env.ref('event.event_stage_done')
|
||||
stage.write({'name': 'ManagerEnded'})
|
||||
event.write({'stage_id': stage.id})
|
||||
|
||||
registration.unlink()
|
||||
event.unlink()
|
||||
stage.unlink()
|
||||
event_type.unlink()
|
||||
|
||||
# Settings access rights required to enable some features
|
||||
self.user_eventmanager.write({'groups_id': [
|
||||
(3, self.env.ref('base.group_system').id),
|
||||
(4, self.env.ref('base.group_erp_manager').id)
|
||||
]})
|
||||
with self.assertRaises(AccessError):
|
||||
event_config = self.env['res.config.settings'].with_user(self.user_eventmanager).create({
|
||||
})
|
||||
event_config.execute()
|
||||
@@ -21,6 +21,8 @@ this event.
|
||||
'depends': ['event', 'sale_management'],
|
||||
'data': [
|
||||
'views/assets.xml',
|
||||
'views/event_ticket_views.xml',
|
||||
'views/event_registration_views.xml',
|
||||
'views/event_views.xml',
|
||||
'views/product_views.xml',
|
||||
'views/sale_order_views.xml',
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_event_internals
|
||||
from . import test_event_sale
|
||||
from . import test_event_sale_ui
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.event.tests.common import TestEventCommon
|
||||
|
||||
|
||||
class TestEventSaleCommon(TestEventCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestEventSaleCommon, cls).setUpClass()
|
||||
|
||||
cls.event_product = cls.env['product.product'].create({
|
||||
'name': 'Test Registration Product',
|
||||
'list_price': 10,
|
||||
'event_ok': True,
|
||||
'standard_price': 30.0,
|
||||
'type': 'service',
|
||||
})
|
||||
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
from odoo.addons.event_sale.tests.common import TestEventSaleCommon
|
||||
from odoo.fields import Datetime as FieldsDatetime
|
||||
from odoo.tests.common import users
|
||||
|
||||
|
||||
class EventSaleTest(TestEventSaleCommon):
|
||||
|
||||
@users('user_eventmanager')
|
||||
def test_event_configuration_from_type(self):
|
||||
""" In addition to event test, also test tickets configuration coming
|
||||
from event_sale capabilities. """
|
||||
event_type = self.event_type_complex.with_user(self.env.user)
|
||||
event_type.write({
|
||||
'use_mail_schedule': False,
|
||||
'use_ticketing': False,
|
||||
})
|
||||
# Event type does not use tickets but data is kept for compatibility and avoid recreating them
|
||||
self.assertEqual(len(event_type.event_ticket_ids), 1)
|
||||
self.assertEqual(event_type.event_ticket_ids.name, 'Registration')
|
||||
|
||||
event = self.env['event.event'].create({
|
||||
'name': 'Event Update Type',
|
||||
'event_type_id': event_type.id,
|
||||
'date_begin': FieldsDatetime.to_string(datetime.today() + timedelta(days=1)),
|
||||
'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)),
|
||||
})
|
||||
event._onchange_type()
|
||||
self.assertEqual(event.event_ticket_ids, self.env['event.event.ticket'])
|
||||
|
||||
event_type.write({
|
||||
'use_ticketing': True,
|
||||
'event_ticket_ids': [(5, 0), (0, 0, {
|
||||
'name': 'First Ticket',
|
||||
'product_id': self.event_product.id,
|
||||
})]
|
||||
})
|
||||
event_type.event_ticket_ids._onchange_product_id()
|
||||
event._onchange_type()
|
||||
self.assertEqual(event.event_ticket_ids.name, 'Registration for %s' % event.name)
|
||||
self.assertEqual(event.event_ticket_ids.product_id, self.event_product)
|
||||
self.assertEqual(event.event_ticket_ids.price, self.event_product.list_price)
|
||||
|
||||
def test_event_registrable(self):
|
||||
"""Test if `_compute_event_registrations_open` works properly."""
|
||||
test_event = self.env['event.event'].create({
|
||||
'name': 'TestEvent',
|
||||
'date_begin': datetime.now() - timedelta(days=5),
|
||||
'date_end': datetime.now() + timedelta(days=5),
|
||||
})
|
||||
test_event_ticket = self.env['event.event.ticket'].create({
|
||||
'name': 'TestTicket',
|
||||
'event_id': test_event.id,
|
||||
'product_id': self.env['product.product'].search([], limit=1).id,
|
||||
})
|
||||
|
||||
self.assertEqual(test_event.event_registrations_open, True)
|
||||
|
||||
test_event.write({
|
||||
'date_begin': datetime.now() - timedelta(days=5),
|
||||
'date_end': datetime.now() - timedelta(days=1),
|
||||
})
|
||||
|
||||
test_event.date_end = datetime.now() - timedelta(days=1)
|
||||
self.assertEqual(test_event.event_registrations_open, False)
|
||||
|
||||
test_event.write({
|
||||
'date_begin': datetime.now() - timedelta(days=1),
|
||||
'date_end': datetime.now() + timedelta(days=5),
|
||||
})
|
||||
|
||||
test_event.write({'event_ticket_ids': [(5, 0)]})
|
||||
self.assertEqual(test_event.event_registrations_open, False, 'cannot register if no tickets')
|
||||
|
||||
test_event_ticket = self.env['event.event.ticket'].create({
|
||||
'name': 'TestTicket',
|
||||
'event_id': test_event.id,
|
||||
'product_id': self.env['product.product'].search([], limit=1).id,
|
||||
})
|
||||
test_event_ticket.product_id.active = False
|
||||
self.assertEqual(test_event.event_registrations_open, False, 'cannot register if product linked to the tickets are all archived')
|
||||
@@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import datetime
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
@@ -68,42 +67,3 @@ class EventSaleTest(common.TransactionCase):
|
||||
# I check if a registration is created
|
||||
registrations = self.EventRegistration.search([('origin', '=', self.sale_order.name)])
|
||||
self.assertTrue(registrations, "The registration is not created.")
|
||||
|
||||
def test_event_is_registrable(self):
|
||||
"""Test if `_compute_event_registrations_open` works properly."""
|
||||
test_event = self.env['event.event'].create({
|
||||
'name': 'TestEvent',
|
||||
'date_begin': datetime.datetime.now() - datetime.timedelta(days=5),
|
||||
'date_end': datetime.datetime.now() + datetime.timedelta(days=5),
|
||||
})
|
||||
test_event_ticket = self.env['event.event.ticket'].create({
|
||||
'name': 'TestTicket',
|
||||
'event_id': test_event.id,
|
||||
'product_id': self.env['product.product'].search([], limit=1).id,
|
||||
})
|
||||
|
||||
self.assertEqual(test_event.event_registrations_open, True)
|
||||
|
||||
test_event.write({
|
||||
'date_begin': datetime.datetime.now() - datetime.timedelta(days=5),
|
||||
'date_end': datetime.datetime.now() - datetime.timedelta(days=1),
|
||||
})
|
||||
|
||||
test_event.date_end = datetime.datetime.now() - datetime.timedelta(days=1)
|
||||
self.assertEqual(test_event.event_registrations_open, False)
|
||||
|
||||
test_event.write({
|
||||
'date_begin': datetime.datetime.now() - datetime.timedelta(days=1),
|
||||
'date_end': datetime.datetime.now() + datetime.timedelta(days=5),
|
||||
})
|
||||
|
||||
test_event.write({'event_ticket_ids': [(6, 0, [])]})
|
||||
self.assertEqual(test_event.event_registrations_open, False, 'cannot register if no tickets')
|
||||
|
||||
test_event_ticket = self.env['event.event.ticket'].create({
|
||||
'name': 'TestTicket',
|
||||
'event_id': test_event.id,
|
||||
'product_id': self.env['product.product'].search([], limit=1).id,
|
||||
})
|
||||
test_event_ticket.product_id.active = False
|
||||
self.assertEqual(test_event.event_registrations_open, False, 'cannot register if product linked to the tickets are all archived')
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo.tests
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo.fields import Datetime
|
||||
from odoo.tests import HttpCase, tagged
|
||||
|
||||
|
||||
@odoo.tests.tagged('post_install', '-at_install')
|
||||
class TestUi(odoo.tests.HttpCase):
|
||||
def test_01_event_configurator(self):
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestUi(HttpCase):
|
||||
|
||||
def test_event_configurator(self):
|
||||
|
||||
event = self.env['event.event'].create({
|
||||
'name': 'Design Fair Los Angeles',
|
||||
'date_begin': Datetime.now() + timedelta(days=1),
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data>
|
||||
|
||||
<record id="view_event_registration_ticket_search" model="ir.ui.view">
|
||||
<field name="name">event.registration.search.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_registration_search" />
|
||||
<field name="arch" type="xml">
|
||||
<filter name="group_event" position="after">
|
||||
<filter string="Ticket Type" name ="tickettype" domain="['|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]" context="{'group_by':'event_ticket_id'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_event_registration_ticket_tree" model="ir.ui.view">
|
||||
<field name="name">event.registration.tree.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_event_registration_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="event_id" position="after">
|
||||
<field name="event_ticket_id"/>
|
||||
<field name="origin"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_registration_view_kanban" model="ir.ui.view">
|
||||
<field name="name">event.registration.kanban.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.event_registration_view_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='event_id']" position="after">
|
||||
<br/>
|
||||
<field name="event_ticket_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_event_registration_ticket_form" model="ir.ui.view">
|
||||
<field name="name">event.registration.form.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_event_registration_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="event_id" position="after">
|
||||
<field
|
||||
name="event_ticket_id"
|
||||
domain="[
|
||||
('event_id', '=', event_id),
|
||||
'|', ('seats_availability', '=', 'unlimited'), ('seats_available', '>', 0)
|
||||
]"
|
||||
attrs="{'invisible': [('event_id', '=', False)]}"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
/>
|
||||
</field>
|
||||
<group name="event" position="after">
|
||||
<group string="Origin">
|
||||
<field name="sale_order_id"/>
|
||||
<field name="origin" attrs="{'invisible': [('sale_order_id', '!=', False)]}"/>
|
||||
<field name="sale_order_line_id" readonly="1" attrs="{'invisible': [('sale_order_id', '=', False)]}"/>
|
||||
</group>
|
||||
<group string="Marketing" name="utm_link" groups="base.group_no_one">
|
||||
<field name="campaign_id"/>
|
||||
<field name="medium_id"/>
|
||||
<field name="source_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
||||
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- EVENT.TICKET -->
|
||||
<record id="event_event_ticket_view_tree_from_event" model="ir.ui.view">
|
||||
<field name="name">event.event.ticket.view.tree.from_event</field>
|
||||
<field name="model">event.event.ticket</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Tickets" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="product_id" context="{'default_event_ok':1}"/>
|
||||
<field name="start_sale_date"/>
|
||||
<field name="end_sale_date"/>
|
||||
<field name="price"/>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved" readonly="1"/>
|
||||
<field name="seats_unconfirmed" readonly="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_event_ticket_view_form_from_event" model="ir.ui.view">
|
||||
<field name="name">event.event.ticket.view.form.from.event</field>
|
||||
<field name="model">event.event.ticket</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Ticket">
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="product_id" context="{'default_event_ok':1}"/>
|
||||
<field name="end_sale_date"/>
|
||||
<field name="price"/>
|
||||
</group><group>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved" readonly="1"/>
|
||||
<field name="seats_unconfirmed" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_event_ticket_view_kanban_from_event" model="ir.ui.view">
|
||||
<field name="name">event.event.ticket.view.kanban.from.event</field>
|
||||
<field name="model">event.event.ticket</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="product_id"/>
|
||||
<field name="name"/>
|
||||
<field name="price"/>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved"/>
|
||||
<field name="seats_unconfirmed"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_card oe_kanban_global_click">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<strong><t t-esc="record.name.value"/></strong>
|
||||
</div>
|
||||
<div class="col-4 text-right"><strong> <t t-esc="record.price.value"/></strong></div>
|
||||
</div>
|
||||
<div t-esc="record.product_id.value"/>
|
||||
<div><i>
|
||||
<t t-esc="record.seats_reserved.value"/> reserved + <t t-esc="record.seats_reserved.value"/> unconfirmed
|
||||
</i></div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_event_ticket_view_form" model="ir.ui.view">
|
||||
<field name="name">event.event.ticket.view.form</field>
|
||||
<field name="model">event.event.ticket</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Event's Ticket">
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name" placeholder="Event Name"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="event_id"/>
|
||||
<field name="seats_availability"/>
|
||||
<field name="seats_available"/>
|
||||
<field name="end_sale_date"/>
|
||||
<field name="price"/>
|
||||
<field name="price_reduce" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved"/>
|
||||
<field name="seats_unconfirmed"/>
|
||||
<field name="seats_used"/>
|
||||
<field name="product_id"/>
|
||||
<field name="is_expired"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -1,206 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_event_registration_ticket_search" model="ir.ui.view">
|
||||
<field name="name">event.registration.search.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_registration_search" />
|
||||
<field name="arch" type="xml">
|
||||
<filter name="group_event" position="after">
|
||||
<filter string="Ticket Type" name ="tickettype" domain="['|', ('company_id', '=', False), ('company_id', '=', parent.company_id)]" context="{'group_by':'event_ticket_id'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_event_registration_ticket_tree" model="ir.ui.view">
|
||||
<field name="name">event.registration.tree.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_event_registration_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="event_id" position="after">
|
||||
<field name="event_ticket_id"/>
|
||||
<field name="origin"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_registration_view_kanban" model="ir.ui.view">
|
||||
<field name="name">event.registration.kanban.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.event_registration_view_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='event_id']" position="after">
|
||||
<br/>
|
||||
<field name="event_ticket_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_event_registration_ticket_form" model="ir.ui.view">
|
||||
<field name="name">event.registration.form.inherit</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_event_registration_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="event_id" position="after">
|
||||
<field
|
||||
name="event_ticket_id"
|
||||
domain="[
|
||||
('event_id', '=', event_id),
|
||||
'|', ('seats_availability', '=', 'unlimited'), ('seats_available', '>', 0)
|
||||
]"
|
||||
attrs="{'invisible': [('event_id', '=', False)]}"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
/>
|
||||
</field>
|
||||
<group name="event" position="after">
|
||||
<group string="Origin">
|
||||
<field name="sale_order_id"/>
|
||||
<field name="origin" attrs="{'invisible': [('sale_order_id', '!=', False)]}"/>
|
||||
<field name="sale_order_line_id" readonly="1" attrs="{'invisible': [('sale_order_id', '=', False)]}"/>
|
||||
</group>
|
||||
<group string="Marketing" name="utm_link" groups="base.group_no_one">
|
||||
<field name="campaign_id"/>
|
||||
<field name="medium_id"/>
|
||||
<field name="source_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</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-12 col-lg-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-lg-9">
|
||||
<field name="event_ticket_ids">
|
||||
<tree string="Tickets" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="product_id"/>
|
||||
<field name="price"/>
|
||||
</tree>
|
||||
</field>
|
||||
</div>
|
||||
<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-12 col-lg-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-lg-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>
|
||||
</field>
|
||||
</record>
|
||||
</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>
|
||||
<field name="inherit_id" ref="event.view_event_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<button name="action_view_linked_orders"
|
||||
type="object" class="oe_stat_button" icon="fa-dollar"
|
||||
help="Total sales for this event"
|
||||
attrs="{'invisible': ['|', ('sale_price_subtotal', '=', 0), ('sale_price_subtotal', '=', False)]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field string="Sales" name="sale_price_subtotal"
|
||||
widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</span>
|
||||
<span class="o_stat_text">Sales</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
<page name="event_communication" position="before">
|
||||
<page string="Tickets" name="tickets">
|
||||
<field name="event_ticket_ids" context="{'default_name': name}" mode="tree,kanban">
|
||||
<tree string="Tickets" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="product_id" context="{'default_event_ok':1}"/>
|
||||
<field name="start_sale_date"/>
|
||||
<field name="end_sale_date"/>
|
||||
<field name="price"/>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved" readonly="1"/>
|
||||
<field name="seats_unconfirmed" readonly="1"/>
|
||||
</tree>
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="product_id"/>
|
||||
<field name="name"/>
|
||||
<field name="price"/>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved"/>
|
||||
<field name="seats_unconfirmed"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_card oe_kanban_global_click">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<strong><t t-esc="record.name.value"/></strong>
|
||||
</div>
|
||||
<div class="col-4 text-right"><strong> <t t-esc="record.price.value"/></strong></div>
|
||||
</div>
|
||||
<div t-esc="record.product_id.value"/>
|
||||
<div><i>
|
||||
<t t-esc="record.seats_reserved.value"/> reserved + <t t-esc="record.seats_reserved.value"/> unconfirmed
|
||||
</i></div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
<form string="Tickets">
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="product_id" context="{'default_event_ok':1}"/>
|
||||
<field name="end_sale_date"/>
|
||||
<field name="price"/>
|
||||
</group><group>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved" readonly="1"/>
|
||||
<field name="seats_unconfirmed" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_ticket_form_view" model="ir.ui.view">
|
||||
<field name="name">event.event.ticket.form</field>
|
||||
<field name="model">event.event.ticket</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Event's Ticket">
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name" placeholder="Event Name"/></h1>
|
||||
<record id="view_event_form_inherit_ticket" model="ir.ui.view">
|
||||
<field name="name">event.form.inherit</field>
|
||||
<field name="model">event.event</field>
|
||||
<field name="inherit_id" ref="event.view_event_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<button name="action_view_linked_orders"
|
||||
type="object" class="oe_stat_button" icon="fa-dollar"
|
||||
help="Total sales for this event"
|
||||
attrs="{'invisible': ['|', ('sale_price_subtotal', '=', 0), ('sale_price_subtotal', '=', False)]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field string="Sales" name="sale_price_subtotal"
|
||||
widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</span>
|
||||
<span class="o_stat_text">Sales</span>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="event_id"/>
|
||||
<field name="seats_availability"/>
|
||||
<field name="seats_available"/>
|
||||
<field name="end_sale_date"/>
|
||||
<field name="price"/>
|
||||
<field name="price_reduce" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="seats_max"/>
|
||||
<field name="seats_reserved"/>
|
||||
<field name="seats_unconfirmed"/>
|
||||
<field name="seats_used"/>
|
||||
<field name="product_id"/>
|
||||
<field name="is_expired"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</button>
|
||||
</xpath>
|
||||
<page name="event_communication" position="before">
|
||||
<page string="Tickets" name="tickets">
|
||||
<field name="event_ticket_ids" context="{
|
||||
'default_name': name,
|
||||
'tree_view_ref': 'event_sale.event_event_ticket_view_tree_from_event',
|
||||
'form_view_ref': 'event_sale.event_event_ticket_view_form_from_event',
|
||||
'kanban_view_ref': 'event_sale.event_event_ticket_view_kanban_from_event'}" mode="tree,kanban"/>
|
||||
</page>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
'depends': ['website', 'website_partner', 'website_mail', 'event'],
|
||||
'data': [
|
||||
'data/event_data.xml',
|
||||
'views/assets.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/event_snippets.xml',
|
||||
'views/event_templates.xml',
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import pytz
|
||||
import werkzeug
|
||||
import json
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
GOOGLE_CALENDAR_URL = 'https://www.google.com/calendar/render?'
|
||||
|
||||
@@ -15,27 +14,26 @@ class EventType(models.Model):
|
||||
_name = 'event.type'
|
||||
_inherit = ['event.type']
|
||||
|
||||
website_menu = fields.Boolean(
|
||||
'Display a dedicated menu on Website')
|
||||
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.multi.mixin']
|
||||
|
||||
website_published = fields.Boolean(tracking=True)
|
||||
|
||||
# description
|
||||
subtitle = fields.Char('Event Subtitle', translate=True)
|
||||
|
||||
# registration
|
||||
is_participating = fields.Boolean("Is Participating", compute="_compute_is_participating")
|
||||
|
||||
# website
|
||||
website_published = fields.Boolean(tracking=True)
|
||||
cover_properties = fields.Text(
|
||||
'Cover Properties',
|
||||
default='{"background-image": "none", "background-color": "oe_blue", "opacity": "0.4", "resize_class": "o_half_screen_height"}')
|
||||
|
||||
website_menu = fields.Boolean('Dedicated Menu',
|
||||
website_menu = fields.Boolean(
|
||||
'Dedicated Menu', copy=False,
|
||||
help="Creates menus Introduction, Location and Register on the page "
|
||||
" of the event on the website.", copy=False)
|
||||
" of the event on the website.")
|
||||
menu_id = fields.Many2one('website.menu', 'Event Menu', copy=False)
|
||||
|
||||
def _compute_is_participating(self):
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Event Assets Frontend">
|
||||
<xpath expr="//link[last()]" position="after">
|
||||
<link rel="stylesheet" type="text/scss" href="/website_event/static/src/scss/website_event.scss"/>
|
||||
</xpath>
|
||||
<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>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_editor" inherit_id="website.assets_editor" name="Event Assets Editor">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_event/static/src/js/website_event.editor.js"></script>
|
||||
<script type="text/javascript" src="/website_event/static/src/js/tours/website_event.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -1,23 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Event Assets Frontend">
|
||||
<xpath expr="//link[last()]" position="after">
|
||||
<link rel="stylesheet" type="text/scss" href="/website_event/static/src/scss/website_event.scss"/>
|
||||
</xpath>
|
||||
<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>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_editor" inherit_id="website.assets_editor" name="Event Assets Editor">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_event/static/src/js/website_event.editor.js"></script>
|
||||
<script type="text/javascript" src="/website_event/static/src/js/tours/website_event.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Index -->
|
||||
<template id="index" name="Events">
|
||||
<t t-call="website.layout">
|
||||
|
||||
@@ -11,8 +11,10 @@ Sell event tickets through eCommerce app.
|
||||
'depends': ['website_event', 'event_sale', 'website_sale'],
|
||||
'data': [
|
||||
'data/event_data.xml',
|
||||
'views/event_templates.xml',
|
||||
'views/event_views.xml',
|
||||
'views/assets.xml',
|
||||
'views/event_event_views.xml',
|
||||
'views/website_event_templates.xml',
|
||||
'views/website_sale_templates.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/website_event_sale_security.xml',
|
||||
],
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="assets_tests" inherit_id="web.assets_tests" name="Website Event Sale Assets Tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_event_sale/static/tests/tours/website_event_sale.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="event_form_mandatory_company" model="ir.ui.view">
|
||||
<field name="name">event.event.view.form.inherit.company.mandatory</field>
|
||||
-44
@@ -1,12 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="assets_tests" inherit_id="web.assets_tests" name="Website Event Sale Assets Tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_event_sale/static/tests/tours/website_event_sale.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="index" inherit_id="website_event.events_list" name="Event's Ticket">
|
||||
<xpath expr="//div[@t-foreach='event_ids']//footer" position="inside">
|
||||
<t t-if="event.event_registrations_open and event.event_ticket_ids">
|
||||
@@ -122,44 +116,6 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want the "product" link to point to the event itself and not to the product on the ecommerce -->
|
||||
<template id="cart_line_product_link_inherit_website_event_sale" inherit_id="website_sale.cart_line_product_link" name="Event Shopping Cart Line Product Link">
|
||||
<xpath expr="//a" position="attributes">
|
||||
<attribute name="t-attf-href"/>
|
||||
<attribute name="t-att-href">
|
||||
line.event_id and ('/event/%s/register' % slug(line.event_id)) or line.product_id.website_url
|
||||
</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name even on small screens -->
|
||||
<template id="cart_lines_inherit_website_event_sale" inherit_id="website_sale.cart_lines" name="Event Shopping Cart Lines">
|
||||
<xpath expr="//t[@t-call='website_sale.cart_line_description_following_lines']/t[@t-set='div_class']" position="after">
|
||||
<t t-if="line.event_id">
|
||||
<t t-set="div_class" t-value="''"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name -->
|
||||
<template id="cart_popover_inherit_website_event_sale" inherit_id="website_sale.cart_popover" name="Event Cart Popover">
|
||||
<xpath expr="//t[@t-call='website_sale.cart_line_product_link']" position="after">
|
||||
<t t-if="line.event_id" t-call="website_sale.cart_line_description_following_lines"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name -->
|
||||
<template id="cart_summary_inherit_website_event_sale" inherit_id="website_sale.cart_summary" name="Event Cart right column">
|
||||
<xpath expr="//td[hasclass('td-product_name')]/div/strong" position="after">
|
||||
<t t-if="line.event_id" t-call="website_sale.cart_line_description_following_lines"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="cart_lines_inherit_website_event_sale" inherit_id="website_sale.cart_lines" name="Hide product reduction for event tickets">
|
||||
<xpath expr="//del" position="attributes">
|
||||
<attribute name="t-attf-class" separator=" " add="#{line.event_id and 'd-none' or ''}"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Add the ticket name on the registration complete page. -->
|
||||
<template id="registration_complete_inherit_website_event_sale" inherit_id="website_event.registration_complete" name="Sale Registration Completed">
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want the "product" link to point to the event itself and not to the product on the ecommerce -->
|
||||
<template id="cart_line_product_link_inherit_website_event_sale" inherit_id="website_sale.cart_line_product_link" name="Event Shopping Cart Line Product Link">
|
||||
<xpath expr="//a" position="attributes">
|
||||
<attribute name="t-attf-href"/>
|
||||
<attribute name="t-att-href">
|
||||
line.event_id and ('/event/%s/register' % slug(line.event_id)) or line.product_id.website_url
|
||||
</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name even on small screens -->
|
||||
<template id="cart_lines_inherit_website_event_sale" inherit_id="website_sale.cart_lines" name="Event Shopping Cart Lines">
|
||||
<xpath expr="//t[@t-call='website_sale.cart_line_description_following_lines']/t[@t-set='div_class']" position="after">
|
||||
<t t-if="line.event_id">
|
||||
<t t-set="div_class" t-value="''"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name -->
|
||||
<template id="cart_popover_inherit_website_event_sale" inherit_id="website_sale.cart_popover" name="Event Cart Popover">
|
||||
<xpath expr="//t[@t-call='website_sale.cart_line_product_link']" position="after">
|
||||
<t t-if="line.event_id" t-call="website_sale.cart_line_description_following_lines"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- If the sale order line concerns an event, we want to show an additional line with the event name -->
|
||||
<template id="cart_summary_inherit_website_event_sale" inherit_id="website_sale.cart_summary" name="Event Cart right column">
|
||||
<xpath expr="//td[hasclass('td-product_name')]/div/strong" position="after">
|
||||
<t t-if="line.event_id" t-call="website_sale.cart_line_description_following_lines"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="cart_lines_inherit_website_event_sale" inherit_id="website_sale.cart_lines" name="Hide product reduction for event tickets">
|
||||
<xpath expr="//del" position="attributes">
|
||||
<attribute name="t-attf-class" separator=" " add="#{line.event_id and 'd-none' or ''}"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user