summaryrefslogtreecommitdiff
path: root/addons/event_sale/models/sale_order.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/event_sale/models/sale_order.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/event_sale/models/sale_order.py')
-rw-r--r--addons/event_sale/models/sale_order.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py
new file mode 100644
index 00000000..bf3aca6c
--- /dev/null
+++ b/addons/event_sale/models/sale_order.py
@@ -0,0 +1,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)