sale_order.py 8.4 KB

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