1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
class SaleOrder(models.Model):
_inherit = "sale.order"
attendee_count = fields.Integer('Attendee Count', compute='_compute_attendee_count')
def write(self, vals):
""" Synchronize partner from SO to registrations. This is done notably
in website_sale controller shop/address that updates customer, but not
only. """
result = super(SaleOrder, self).write(vals)
if vals.get('partner_id'):
registrations_toupdate = self.env['event.registration'].search([('sale_order_id', 'in', self.ids)])
registrations_toupdate.write({'partner_id': vals['partner_id']})
return result
def action_confirm(self):
res = super(SaleOrder, self).action_confirm()
for so in self:
# confirm registration if it was free (otherwise it will be confirmed once invoice fully paid)
so.order_line._update_registrations(confirm=so.amount_total == 0, cancel_to_draft=False)
if any(line.event_id for line in so.order_line):
return self.env['ir.actions.act_window'] \
.with_context(default_sale_order_id=so.id) \
._for_xml_id('event_sale.action_sale_order_event_registration')
return res
def action_cancel(self):
self.order_line._cancel_associated_registrations()
return super(SaleOrder, self).action_cancel()
def action_view_attendee_list(self):
action = self.env["ir.actions.actions"]._for_xml_id("event.event_registration_action_tree")
action['domain'] = [('sale_order_id', 'in', self.ids)]
return action
def _compute_attendee_count(self):
sale_orders_data = self.env['event.registration'].read_group(
[('sale_order_id', 'in', self.ids)],
['sale_order_id'], ['sale_order_id']
)
attendee_count_data = {
sale_order_data['sale_order_id'][0]:
sale_order_data['sale_order_id_count']
for sale_order_data in sale_orders_data
}
for sale_order in self:
sale_order.attendee_count = attendee_count_data.get(sale_order.id, 0)
def unlink(self):
self.mapped('order_line')._unlink_associated_registrations()
return super(SaleOrder, self).unlink()
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
event_id = fields.Many2one(
'event.event', string='Event',
help="Choose an event and it will automatically create a registration for this event.")
event_ticket_id = fields.Many2one(
'event.event.ticket', string='Event Ticket',
help="Choose an event ticket and it will automatically create a registration for this event ticket.")
event_ok = fields.Boolean(related='product_id.event_ok', readonly=True)
@api.depends('state', 'event_id')
def _compute_product_uom_readonly(self):
event_lines = self.filtered(lambda line: line.event_id)
event_lines.update({'product_uom_readonly': True})
super(SaleOrderLine, self - event_lines)._compute_product_uom_readonly()
def _update_registrations(self, confirm=True, cancel_to_draft=False, registration_data=None, mark_as_paid=False):
""" Create or update registrations linked to a sales order line. A sale
order line has a product_uom_qty attribute that will be the number of
registrations linked to this line. This method update existing registrations
and create new one for missing one. """
RegistrationSudo = self.env['event.registration'].sudo()
registrations = RegistrationSudo.search([('sale_order_line_id', 'in', self.ids)])
registrations_vals = []
for so_line in self.filtered('event_id'):
existing_registrations = registrations.filtered(lambda self: self.sale_order_line_id.id == so_line.id)
if confirm:
existing_registrations.filtered(lambda self: self.state not in ['open', 'cancel']).action_confirm()
if mark_as_paid:
existing_registrations.filtered(lambda self: not self.is_paid)._action_set_paid()
if cancel_to_draft:
existing_registrations.filtered(lambda self: self.state == 'cancel').action_set_draft()
for count in range(int(so_line.product_uom_qty) - len(existing_registrations)):
values = {
'sale_order_line_id': so_line.id,
'sale_order_id': so_line.order_id.id
}
# TDE CHECK: auto confirmation
if registration_data:
values.update(registration_data.pop())
registrations_vals.append(values)
if registrations_vals:
RegistrationSudo.create(registrations_vals)
return True
@api.onchange('product_id')
def _onchange_product_id(self):
# We reset the event when keeping it would lead to an inconstitent state.
# We need to do it this way because the only relation between the product and the event is through the corresponding tickets.
if self.event_id and (not self.product_id or self.product_id.id not in self.event_id.mapped('event_ticket_ids.product_id.id')):
self.event_id = None
@api.onchange('event_id')
def _onchange_event_id(self):
# We reset the ticket when keeping it would lead to an inconstitent state.
if self.event_ticket_id and (not self.event_id or self.event_id != self.event_ticket_id.event_id):
self.event_ticket_id = None
@api.onchange('product_uom', 'product_uom_qty')
def product_uom_change(self):
if not self.event_ticket_id:
super(SaleOrderLine, self).product_uom_change()
@api.onchange('event_ticket_id')
def _onchange_event_ticket_id(self):
# we call this to force update the default name
self.product_id_change()
def unlink(self):
self._unlink_associated_registrations()
return super(SaleOrderLine, self).unlink()
def _cancel_associated_registrations(self):
self.env['event.registration'].search([('sale_order_line_id', 'in', self.ids)]).action_cancel()
def _unlink_associated_registrations(self):
self.env['event.registration'].search([('sale_order_line_id', 'in', self.ids)]).unlink()
def get_sale_order_line_multiline_description_sale(self, product):
""" We override this method because we decided that:
The default description of a sales order line containing a ticket must be different than the default description when no ticket is present.
So in that case we use the description computed from the ticket, instead of the description computed from the product.
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.
"""
if self.event_ticket_id:
ticket = self.event_ticket_id.with_context(
lang=self.order_id.partner_id.lang,
)
return ticket._get_ticket_multiline_description() + self._get_sale_order_line_multiline_description_variants()
else:
return super(SaleOrderLine, self).get_sale_order_line_multiline_description_sale(product)
def _get_display_price(self, product):
if self.event_ticket_id and self.event_id:
return self.event_ticket_id.with_context(pricelist=self.order_id.pricelist_id.id, uom=self.product_uom.id).price_reduce
else:
return super()._get_display_price(product)
|