Skip to content

Commit 092c452

Browse files
committed
[IMP] event_sale: add multi slots events
1 parent 4706ecf commit 092c452

12 files changed

+95
-17
lines changed

addons/event_sale/data/mail_templates.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<odoo>
33

4-
<template id="event_ticket_id_change_exception" name="Message: Alert on event ticket id change">
4+
<template id="event_registration_change_exception" name="Message: Alert on event registration data change">
55
<div>
66
<p>
77
<span>Registration modification for attendee:</span>
@@ -13,7 +13,7 @@
1313
<ul>
1414
<li>
1515
<a href="#" data-oe-model="event.registration" t-att-data-oe-id="registration.id"><t t-out="registration.name"/></a>:
16-
<span>Ticket changed from <strong><t t-out="old_ticket_name"/></strong> to <strong><t t-out="new_ticket_name"/></strong></span>
16+
<span><t t-out="record_type"/> changed from <strong><t t-out="old_name"/></strong> to <strong><t t-out="new_name"/></strong></span>
1717
</li>
1818
</ul>
1919
</div>

addons/event_sale/models/event_registration.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
# Part of Odoo. See LICENSE file for full copyright and licensing details.
33

4-
from odoo import api, fields, models
4+
from odoo import _, api, fields, models
55
from odoo.tools import float_is_zero
66

77

@@ -101,10 +101,15 @@ def write(self, vals):
101101
)
102102
vals.update(so_line_vals)
103103

104+
updated_fields_to_notify = []
105+
if vals.get('event_slot_id'):
106+
updated_fields_to_notify.append(('event.slot', 'event_slot_id'))
104107
if vals.get('event_ticket_id'):
108+
updated_fields_to_notify.append(('event.event.ticket', 'event_ticket_id'))
109+
for model, field in updated_fields_to_notify:
105110
self.filtered(
106-
lambda registration: registration.event_ticket_id and registration.event_ticket_id.id != vals['event_ticket_id']
107-
)._sale_order_ticket_type_change_notify(self.env['event.event.ticket'].browse(vals['event_ticket_id']))
111+
lambda registration: registration[field] and registration[field].id != vals[field]
112+
)._sale_order_registration_data_change_notify(field, self.env[model].browse(vals[field]))
108113

109114
return super(EventRegistration, self).write(vals)
110115

@@ -114,25 +119,27 @@ def _synchronize_so_line_values(self, so_line):
114119
# Avoid registering public users but respect the portal workflows
115120
'partner_id': False if self.env.user._is_public() and self.env.user.partner_id == so_line.order_id.partner_id else so_line.order_id.partner_id.id,
116121
'event_id': so_line.event_id.id,
122+
'event_slot_id': so_line.event_slot_id.id,
117123
'event_ticket_id': so_line.event_ticket_id.id,
118124
'sale_order_id': so_line.order_id.id,
119125
'sale_order_line_id': so_line.id,
120126
}
121127
return {}
122128

123-
def _sale_order_ticket_type_change_notify(self, new_event_ticket):
129+
def _sale_order_registration_data_change_notify(self, new_record_field, new_record):
124130
fallback_user_id = self.env.user.id if not self.env.user._is_public() else self.env.ref("base.user_admin").id
125131
for registration in self:
126132
render_context = {
127133
'registration': registration,
128-
'old_ticket_name': registration.event_ticket_id.name,
129-
'new_ticket_name': new_event_ticket.name
134+
'record_type': _('Ticket') if new_record_field == 'event_ticket_id' else _('Slot'),
135+
'old_name': registration[new_record_field].name,
136+
'new_name': new_record.name
130137
}
131138
user_id = registration.event_id.user_id.id or registration.sale_order_id.user_id.id or fallback_user_id
132139
registration.sale_order_id._activity_schedule_with_view(
133140
'mail.mail_activity_data_warning',
134141
user_id=user_id,
135-
views_or_xmlid='event_sale.event_ticket_id_change_exception',
142+
views_or_xmlid='event_sale.event_registration_change_exception',
136143
render_context=render_context)
137144

138145
def _get_registration_summary(self):

addons/event_sale/models/sale_order_line.py

+26-5
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,29 @@ class SaleOrderLine(models.Model):
1111
'event.event', string='Event',
1212
compute="_compute_event_id", store=True, readonly=False, precompute=True, index='btree_not_null',
1313
help="Choose an event and it will automatically create a registration for this event.")
14+
event_slot_id = fields.Many2one(
15+
'event.slot', string='Slot',
16+
compute="_compute_event_slot_id", store=True, readonly=False, precompute=True,
17+
help="Choose an event slot and it will automatically create a registration for this event slot.")
1418
event_ticket_id = fields.Many2one(
1519
'event.event.ticket', string='Ticket Type',
1620
compute="_compute_event_ticket_id", store=True, readonly=False, precompute=True,
1721
help="Choose an event ticket and it will automatically create a registration for this event ticket.")
22+
is_multi_slots = fields.Boolean(related="event_id.is_multi_slots")
1823
registration_ids = fields.One2many('event.registration', 'sale_order_line_id', string="Registrations")
1924

20-
@api.constrains('event_id', 'event_ticket_id', 'product_id')
25+
@api.constrains('event_id', 'event_slot_id', 'event_ticket_id', 'product_id')
2126
def _check_event_registration_ticket(self):
2227
for so_line in self:
23-
if so_line.product_id.service_tracking == "event" and (not so_line.event_id or not so_line.event_ticket_id):
24-
raise ValidationError(
25-
_("The sale order line with the product %(product_name)s needs an event and a ticket.", product_name=so_line.product_id.name))
28+
if so_line.product_id.service_tracking == "event" and (
29+
not so_line.event_id or
30+
not so_line.event_ticket_id or
31+
(so_line.is_multi_slots and not so_line.event_slot_id)
32+
):
33+
raise ValidationError(_(
34+
"The sale order line with the product %(product_name)s needs an event,"
35+
" a ticket and a slot in case the event has multiple time slots.",
36+
product_name=so_line.product_id.name))
2637

2738
@api.depends('state', 'event_id')
2839
def _compute_product_uom_readonly(self):
@@ -58,6 +69,14 @@ def _compute_event_id(self):
5869
if line.product_id not in line.event_id.event_ticket_ids.product_id:
5970
line.event_id = False
6071

72+
@api.depends('event_id')
73+
def _compute_event_slot_id(self):
74+
event_lines = self.filtered('event_id')
75+
(self - event_lines).event_slot_id = False
76+
for line in event_lines:
77+
if line.event_id != line.event_slot_id.event_id:
78+
line.event_slot_id = False
79+
6180
@api.depends('event_id')
6281
def _compute_event_ticket_id(self):
6382
event_lines = self.filtered('event_id')
@@ -85,7 +104,9 @@ def _get_sale_order_line_multiline_description_sale(self):
85104
We need this override to be defined here in sales order line (and not in product) because here is the only place where the event_ticket_id is referenced.
86105
"""
87106
if self.event_ticket_id:
88-
return self.event_ticket_id._get_ticket_multiline_description() + self._get_sale_order_line_multiline_description_variants()
107+
return ('%s\n' % self.event_slot_id.name if self.event_slot_id else '') + \
108+
self.event_ticket_id._get_ticket_multiline_description() + \
109+
self._get_sale_order_line_multiline_description_variants()
89110
else:
90111
return super()._get_sale_order_line_multiline_description_sale()
91112

addons/event_sale/report/event_sale_report.py

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class EventSaleReport(models.Model):
1717
event_id = fields.Many2one('event.event', string='Event', readonly=True)
1818
event_date_begin = fields.Date(string='Event Start Date', readonly=True)
1919
event_date_end = fields.Date(string='Event End Date', readonly=True)
20+
event_slot_id = fields.Many2one('event.slot', string='Event Slot', readonly=True)
2021
event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket', readonly=True)
2122
event_ticket_price = fields.Float(string='Ticket price', readonly=True)
2223
event_registration_create_date = fields.Date(string='Registration Date', readonly=True)
@@ -73,6 +74,7 @@ def _select_clause(self, *select):
7374
event_registration.id AS event_registration_id,
7475
event_registration.company_id AS company_id,
7576
event_registration.event_id AS event_id,
77+
event_registration.event_slot_id AS event_slot_id,
7678
event_registration.event_ticket_id AS event_ticket_id,
7779
event_registration.create_date AS event_registration_create_date,
7880
event_registration.name AS event_registration_name,
@@ -115,6 +117,7 @@ def _from_clause(self, *join_):
115117
return """
116118
FROM event_registration
117119
LEFT JOIN event_event ON event_event.id = event_registration.event_id
120+
LEFT JOIN event_slot ON event_slot.id = event_registration.event_slot_id
118121
LEFT JOIN event_event_ticket ON event_event_ticket.id = event_registration.event_ticket_id
119122
LEFT JOIN sale_order ON sale_order.id = event_registration.sale_order_id
120123
LEFT JOIN sale_order_line ON sale_order_line.id = event_registration.sale_order_line_id

addons/event_sale/report/event_sale_report_views.xml

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<field name="event_registration_id"/>
2929
<field name="event_registration_name"/>
3030
<field name="event_registration_create_date"/>
31+
<field name="event_slot_id"/>
3132
<field name="event_ticket_id"/>
3233
<field name="event_registration_state"/>
3334
</group>
@@ -69,6 +70,7 @@
6970
<field name="arch" type="xml">
7071
<list string="Revenues" edit="false" create="false">
7172
<field name="event_id"/>
73+
<field name="event_slot_id"/>
7274
<field name="event_ticket_id"/>
7375
<field name="product_id" optional="hide"/>
7476
<field name="event_ticket_price"/>
@@ -113,6 +115,7 @@
113115
<filter string="Event" name="group_by_event_id" context="{'group_by': 'event_id' }"/>
114116
<separator/>
115117
<filter string="Product" name="group_by_product_id" context="{'group_by': 'product_id'}"/>
118+
<filter string="Slot" name="group_by_slot_id" context="{'group_by': 'event_slot_id'}"/>
116119
<filter string="Ticket" name="group_by_ticket_id" context="{'group_by': 'event_ticket_id'}"/>
117120
<separator/>
118121
<filter string="Registration Status" name="group_by_registration_state"

addons/event_sale/static/src/js/event_configurator_controller.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { formView } from "@web/views/form/form_view";
77
* window when a service product linked to events is selected.
88
*
99
* This allows keeping an editable list view for sales order and remove the noise of
10-
* those 2 fields ('event_id' + 'event_ticket_id')
10+
* those 3 fields ('event_id' + 'event_slot_id' + 'event_ticket_id')
1111
*/
1212

1313
export class EventConfiguratorController extends formView.Controller {
@@ -26,12 +26,13 @@ export class EventConfiguratorController extends formView.Controller {
2626
*/
2727
async onRecordSaved(record) {
2828
await super.onRecordSaved(...arguments);
29-
const { event_id, event_ticket_id } = record.data;
29+
const { event_id, event_slot_id, event_ticket_id } = record.data;
3030
return this.action.doAction({
3131
type: "ir.actions.act_window_close",
3232
infos: {
3333
eventConfiguration: {
3434
event_id,
35+
event_slot_id,
3536
event_ticket_id,
3637
},
3738
},

addons/event_sale/static/src/js/sale_product_field.js

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ patch(SaleOrderLineProductField.prototype, {
3434
if (this.props.record.data.event_id) {
3535
actionContext.default_event_id = this.props.record.data.event_id[0];
3636
}
37+
if (this.props.record.data.event_slot_id) {
38+
actionContext.default_event_slot_id = this.props.record.data.event_slot_id[0];
39+
}
3740
if (this.props.record.data.event_ticket_id) {
3841
actionContext.default_event_ticket_id = this.props.record.data.event_ticket_id[0];
3942
}

addons/event_sale/views/sale_order_views.xml

+12
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
invisible="service_tracking != 'event'"
2626
required="service_tracking == 'event'"
2727
options="{'no_open': True, 'no_create': True}"/>
28+
<field
29+
name="event_slot_id"
30+
domain="[('event_id','=', event_id)]"
31+
invisible="not is_multi_slots or service_tracking != 'event'"
32+
required="is_multi_slots and service_tracking == 'event'"
33+
options="{'no_open': True, 'no_create': True}"/>
2834
<field
2935
name="event_ticket_id"
3036
domain="[
@@ -46,6 +52,12 @@
4652
required="service_tracking == 'event'"
4753
options="{'no_open': True, 'no_create': True}"
4854
placeholder="All Events"/>
55+
<field
56+
name="event_slot_id"
57+
column_invisible="True"
58+
domain="[('event_id','=', event_id)]"
59+
required="is_multi_slots and service_tracking == 'event'"
60+
options="{'no_open': True, 'no_create': True}"/>
4961
<field name="event_ticket_id"
5062
column_invisible="True"
5163
domain="[

addons/event_sale/wizard/event_configurator.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@ class EventEventConfigurator(models.TransientModel):
1111

1212
product_id = fields.Many2one('product.product', string="Product", readonly=True)
1313
event_id = fields.Many2one('event.event', string="Event")
14+
event_slot_id = fields.Many2one('event.slot', string="Slot",
15+
compute="_compute_event_slot_id", readonly=False, store=True)
1416
event_ticket_id = fields.Many2one('event.event.ticket', string="Ticket Type",
1517
compute="_compute_event_ticket_id", readonly=False, store=True)
18+
is_multi_slots = fields.Boolean(related="event_id.is_multi_slots")
1619
has_available_tickets = fields.Boolean("Has Available Tickets", compute="_compute_has_available_tickets")
1720

18-
@api.constrains('event_id', 'event_ticket_id')
21+
@api.constrains('event_id', 'event_slot_id', 'event_ticket_id')
1922
def check_event_id(self):
2023
error_messages = []
2124
for record in self:
2225
if record.event_id.id != record.event_ticket_id.event_id.id:
2326
error_messages.append(
2427
_('Invalid ticket choice "%(ticket_name)s" for event "%(event_name)s".'))
28+
if record.event_slot_id and record.event_id.id != record.event_slot_id.event_id.id:
29+
error_messages.append(
30+
_('Invalid slot choice "%(slot_name)s" for event "%(event_name)s".'))
2531
if error_messages:
2632
raise ValidationError('\n'.join(error_messages))
2733

@@ -36,6 +42,16 @@ def _compute_has_available_tickets(self):
3642
for configurator in self:
3743
configurator.has_available_tickets = bool(mapped_data.get(configurator.product_id, 0))
3844

45+
@api.depends("is_multi_slots")
46+
def _compute_event_slot_id(self):
47+
""" Pre-select the slot of the multi slots event selected if it is the only one """
48+
for configurator in self:
49+
if not configurator.is_multi_slots:
50+
configurator.event_slot_id = False
51+
event_slot_ids = self.env['event.slot'].search([
52+
('event_id', '=', configurator.event_id.id)], limit=2)
53+
configurator.event_slot_id = event_slot_ids if len(event_slot_ids) == 1 else False
54+
3955
@api.depends('event_id')
4056
def _compute_event_ticket_id(self):
4157
""" Pre-select the ticket of the event selected if it is the only one """

addons/event_sale/wizard/event_configurator_views.xml

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
options="{'no_open': True, 'no_create': True}"
2626
placeholder="All Events"
2727
/>
28+
<field name="event_slot_id"
29+
domain="[('event_id', '=', event_id)]"
30+
invisible="not is_multi_slots"
31+
required="is_multi_slots"
32+
context="{'name_with_seats_availability': True}"
33+
options="{'no_open': True, 'no_create': True}"/>
2834
<field
2935
name="event_ticket_id"
3036
domain="[('event_id', '=', event_id), ('product_id', '=', product_id)]"

addons/event_sale/wizard/event_edit_registration.py

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def default_get(self, fields):
2222
sale_order = self.env['sale.order'].browse(res.get('sale_order_id'))
2323
registrations = self.env['event.registration'].search([
2424
('sale_order_id', '=', sale_order.id),
25+
('event_slot_id', 'in', sale_order.mapped('order_line.event_slot_id').ids or [False]),
2526
('event_ticket_id', 'in', sale_order.mapped('order_line.event_ticket_id').ids),
2627
('state', '!=', 'cancel')])
2728

@@ -33,6 +34,7 @@ def default_get(self, fields):
3334
# Add existing registrations
3435
attendee_list += [[0, 0, {
3536
'event_id': reg.event_id.id,
37+
'event_slot_id': reg.event_slot_id.id,
3638
'event_ticket_id': reg.event_ticket_id.id,
3739
'registration_id': reg.id,
3840
'name': reg.name,
@@ -43,6 +45,7 @@ def default_get(self, fields):
4345
# Add new registrations
4446
attendee_list += [[0, 0, {
4547
'event_id': so_line.event_id.id,
48+
'event_slot_id': so_line.event_slot_id.id,
4649
'event_ticket_id': so_line.event_ticket_id.id,
4750
'sale_order_line_id': so_line.id,
4851
'name': so_line.order_partner_id.name,
@@ -78,6 +81,7 @@ class RegistrationEditorLine(models.TransientModel):
7881
event_id = fields.Many2one('event.event', string='Event', required=True)
7982
company_id = fields.Many2one(related="event_id.company_id")
8083
registration_id = fields.Many2one('event.registration', 'Original Registration')
84+
event_slot_id = fields.Many2one('event.slot', string='Event Slot')
8185
event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket')
8286
email = fields.Char(string='Email')
8387
phone = fields.Char(string='Phone')
@@ -94,6 +98,7 @@ def _prepare_registration_data(self, include_event_values=False):
9498
if include_event_values:
9599
registration_data.update({
96100
'event_id': self.event_id.id,
101+
'event_slot_id': self.event_slot_id.id,
97102
'event_ticket_id': self.event_ticket_id.id,
98103
'sale_order_id': self.editor_id.sale_order_id.id,
99104
'sale_order_line_id': self.sale_order_line_id.id,

addons/event_sale/wizard/event_edit_registration.xml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<list string="Registration" editable="top" create="false" delete="false">
1414
<field name="event_id" readonly='1' force_save="1"/>
1515
<field name="registration_id" readonly='1' force_save="1"/>
16+
<field name="event_slot_id" domain="[('event_id', '=', event_id)]" readonly='1' force_save="1"/>
1617
<field name="event_ticket_id" domain="[('event_id', '=', event_id)]" readonly='1' force_save="1"/>
1718
<field name="name"/>
1819
<field name="email"/>

0 commit comments

Comments
 (0)