From ec121565ccc5b9ab42bf381db15a08c98e76afba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 28 Jan 2020 15:38:46 +0000 Subject: [PATCH] [IMP] event: clean code about synchronizing partner / sale order line PURPOSE This commit is part of ticket model support directly in event application. SPECIFICATIONS In this commit we make a quixotic attempt to clean call chain implied by making registrations online, either in event frontend (website_event) or with eCommerce inclusion (website_event_sale). Purpose is to get rid of some preparation and data management methods to handle most of the code directly at CRUD level whenever it makes sense. We notably * move at create and write level support of partner_id update: updating its name / email / phone / mobile; * move at create and write level support of sale_order-id update: updating event, ticket, partner; Using that and some code cleaning some methods are removed and call chain is a bit reduced. LINKS Task ID 2177281 Community PR #43488 --- addons/event/models/event_registration.py | 88 +++++++++++-------- .../event_sale/models/event_registration.py | 43 +++++---- addons/event_sale/models/sale_order.py | 9 +- addons/website_event/controllers/main.py | 9 +- addons/website_event_sale/controllers/main.py | 8 +- 5 files changed, 91 insertions(+), 66 deletions(-) diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py index 3a85bee3e68..e8789776628 100644 --- a/addons/event/models/event_registration.py +++ b/addons/event/models/event_registration.py @@ -49,52 +49,53 @@ class EventRegistration(models.Model): if registration.event_id.seats_availability == 'limited' and registration.event_id.seats_max and registration.event_id.seats_available < (1 if registration.state == 'draft' else 0): raise ValidationError(_('No more seats available for this event.')) - def _check_auto_confirmation(self): - if self._context.get('registration_force_draft'): - return False - if any(not registration.event_id.auto_confirm or - (not registration.event_id.seats_available and registration.event_id.seats_availability == 'limited') for registration in self): - return False - return True + @api.onchange('partner_id') + def _onchange_partner(self): + if self.partner_id: + self.update(self._synchronize_partner_values(self.partner_id)) + + # ------------------------------------------------------------ + # CRUD + # ------------------------------------------------------------ @api.model def create(self, vals): + # update missing pieces of information from partner + if vals.get('partner_id'): + partner_vals = self._synchronize_partner_values( + self.env['res.partner'].browse(vals['partner_id']) + ) + vals = dict(partner_vals, **vals) + registration = super(EventRegistration, self).create(vals) if registration._check_auto_confirmation(): registration.sudo().action_confirm() + return registration def write(self, vals): + if vals.get('state') == 'done' and 'date_closed' not in vals: + vals['date_closed'] = fields.Datetime.now() + ret = super(EventRegistration, self).write(vals) + # update missing pieces of information from partner + if vals.get('partner_id'): + partner_vals = self._synchronize_partner_values( + self.env['res.partner'].browse(vals['partner_id']) + ) + for registration in self: + partner_info = dict((key, val) for key, val in partner_vals.items() if not registration[key]) + if partner_info: + registration.write(partner_info) + if vals.get('state') == 'open': # auto-trigger after_sub (on subscribe) mail schedulers, if needed onsubscribe_schedulers = self.mapped('event_id.event_mail_ids').filtered(lambda s: s.interval_type == 'after_sub') onsubscribe_schedulers.execute() - elif vals.get('state') == 'done' and 'date_closed' not in vals: - vals['date_closed'] = fields.Datetime.now() return ret - @api.model - def _prepare_attendee_values(self, registration): - """ Method preparing the values to create new attendees based on a - sales order line. It takes some registration data (dict-based) that are - optional values coming from an external input like a web page. This method - is meant to be inherited in various addons that sell events. """ - partner_id = registration.pop('partner_id', self.env.user.partner_id) - event_id = registration.pop('event_id', False) - data = { - 'name': registration.get('name', partner_id.name), - 'phone': registration.get('phone', partner_id.phone), - 'mobile': registration.get('mobile', partner_id.mobile), - 'email': registration.get('email', partner_id.email), - 'partner_id': partner_id.id, - 'event_id': event_id and event_id.id or False, - } - data.update({key: value for key, value in registration.items() if key in self._fields}) - return data - def name_get(self): """ Custom name_get implementation to better differentiate registrations linked to a given partner but with different name (one partner buying @@ -116,6 +117,26 @@ class EventRegistration(models.Model): ret_list.append((registration.id, name)) return ret_list + def _check_auto_confirmation(self): + if self._context.get('registration_force_draft'): + return False + if any(not registration.event_id.auto_confirm or + (not registration.event_id.seats_available and registration.event_id.seats_availability == 'limited') for registration in self): + return False + return True + + def _synchronize_partner_values(self, partner): + if partner: + contact_id = partner.address_get().get('contact', False) + if contact_id: + contact = self.env['res.partner'].browse(contact_id) + return dict((fname, contact[fname]) for fname in ['name', 'email', 'phone', 'mobile'] if contact[fname]) + return {} + + # ------------------------------------------------------------ + # ACTIONS / BUSINESS + # ------------------------------------------------------------ + def action_set_draft(self): self.write({'state': 'draft'}) @@ -129,17 +150,6 @@ class EventRegistration(models.Model): def action_cancel(self): self.write({'state': 'cancel'}) - @api.onchange('partner_id') - def _onchange_partner(self): - if self.partner_id: - contact_id = self.partner_id.address_get().get('contact', False) - if contact_id: - contact = self.env['res.partner'].browse(contact_id) - self.name = contact.name or self.name - self.email = contact.email or self.email - self.phone = contact.phone or self.phone - self.mobile = contact.mobile or self.mobile - def _message_get_suggested_recipients(self): recipients = super(EventRegistration, self)._message_get_suggested_recipients() public_users = self.env['res.users'].sudo() diff --git a/addons/event_sale/models/event_registration.py b/addons/event_sale/models/event_registration.py index bc9c2ae4917..88086102663 100644 --- a/addons/event_sale/models/event_registration.py +++ b/addons/event_sale/models/event_registration.py @@ -41,29 +41,38 @@ class EventRegistration(models.Model): @api.model def create(self, vals): + if vals.get('sale_order_line_id'): + so_line_vals = self._synchronize_so_line_values( + self.env['sale.order.line'].browse(vals['sale_order_line_id']) + ) + vals.update(so_line_vals) res = super(EventRegistration, self).create(vals) if res.origin or res.sale_order_id: - res.message_post_with_view('mail.message_origin_link', + res.message_post_with_view( + 'mail.message_origin_link', values={'self': res, 'origin': res.sale_order_id}, subtype_id=self.env.ref('mail.mt_note').id) return res - @api.model - def _prepare_attendee_values(self, registration): - """ Override to add sale related stuff """ - line_id = registration.get('sale_order_line_id') - if line_id: - registration.setdefault('partner_id', line_id.order_id.partner_id) - att_data = super(EventRegistration, self)._prepare_attendee_values(registration) - if line_id and line_id.event_ticket_id.sale_available: - att_data.update({ - 'event_id': line_id.event_id.id, - 'event_ticket_id': line_id.event_ticket_id.id, - 'origin': line_id.order_id.name, - 'sale_order_id': line_id.order_id.id, - 'sale_order_line_id': line_id.id, - }) - return att_data + def write(self, vals): + if vals.get('sale_order_line_id'): + so_line_vals = self._synchronize_so_line_values( + self.env['sale.order.line'].browse(vals['sale_order_line_id']) + ) + vals.update(so_line_vals) + return super(EventRegistration, self).write(vals) + + def _synchronize_so_line_values(self, so_line): + if so_line: + return { + 'partner_id': so_line.order_id.partner_id.id, + 'event_id': so_line.event_id.id, + 'event_ticket_id': so_line.event_ticket_id.id, + 'origin': so_line.order_id.name, + 'sale_order_id': so_line.order_id.id, + 'sale_order_line_id': so_line.id, + } + return {} def summary(self): res = super(EventRegistration, self).summary() diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index 10a8e0a5e2c..e1ff94f78b6 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -60,13 +60,12 @@ class SaleOrderLine(models.Model): existing_registrations.filtered(lambda self: self.state == 'cancel').action_set_draft() for count in range(int(so_line.product_uom_qty) - len(existing_registrations)): - registration = {} + registration_vals = {} if registration_data: - registration = registration_data.pop() + registration_vals = registration_data.pop() # TDE CHECK: auto confirmation - registration['sale_order_line_id'] = so_line - Registration.with_context(registration_force_draft=True).create( - Registration._prepare_attendee_values(registration)) + registration_vals['sale_order_line_id'] = so_line.id + Registration.with_context(registration_force_draft=True).create(registration_vals) return True @api.onchange('product_id') diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index 15c83004bb2..d4d8e7f06ca 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -290,10 +290,11 @@ class WebsiteEventController(http.Controller): Attendees = request.env['event.registration'] registrations = self._process_registration_details(post) - for registration in registrations: - registration['event_id'] = event - Attendees += Attendees.sudo().create( - Attendees._prepare_attendee_values(registration)) + for registration_vals in registrations: + registration_vals['event_id'] = event.id + if not registration_vals.get('partner_id'): + registration_vals['partner_id'] = request.env.user.partner_id.id + Attendees += Attendees.sudo().create(registration_vals) urls = event._get_event_resource_urls() return request.render("website_event.registration_complete", { diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index 9dbb339cde9..b47d2f0f2cc 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -29,6 +29,12 @@ class WebsiteEventSaleController(WebsiteEventController): tickets = request.env['event.event.ticket'].browse(tuple(ticket_post)) return [{'id': ticket.id, 'name': ticket.name, 'quantity': ticket_post[ticket.id], 'price': ticket.price} for ticket in tickets if ticket_post[ticket.id]] + def _process_registration_details(self, details): + res = super(WebsiteEventSaleController, self)._process_registration_details(details) + for registration_vals in res: + registration_vals['event_ticket_id'] = int(registration_vals.pop('ticket_id')) if registration_vals.get('ticket_id') else False + return res + @http.route() def registration_confirm(self, event, **post): order = request.website.sale_get_order(force_create=1) @@ -36,7 +42,7 @@ class WebsiteEventSaleController(WebsiteEventController): registrations = self._process_registration_details(post) for registration in registrations: - ticket = request.env['event.event.ticket'].sudo().browse(int(registration['ticket_id'])) + ticket = request.env['event.event.ticket'].sudo().browse(int(registration['event_ticket_id'])) cart_values = order.with_context(event_ticket_id=ticket.id, fixed_price=True)._cart_update(product_id=ticket.product_id.id, add_qty=1, registration_data=[registration]) attendee_ids |= set(cart_values.get('attendee_ids', []))