From cb928ed6133bc3d2104fbf586bba5cae2a482524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 22 Jan 2020 10:20:19 +0000 Subject: [PATCH] [REF][MOV] website_event(_sale): support tickets in website_event frontend PURPOSE Integration between event and eCommerce is required only when users handle the entire selling process online. However users may require ticketing support while managing payments outside of Odoo. Purpose of this commit is to support tickets directly in event application without need of sales. RATIONALE Remove the need to have event_sale installed to manage basic multi ticket event type. Integration with eCommerce is needed only when one wants to handle the entire selling flow online, i.e. order, payment, ... Integration with Sales is needed only when one wants to create sale orders linked to attendees. Many event users do not need all of this. Their attendees pay through bank transfers or they simply manage payments outside of Odoo while still requiring tickets management. SPECIFICATIONS In this commit we move frontend part of ticket support from website_event_sale to website_event. Now eCommerce / event integration adds only payment information when registering. About tickets * if there is no ticket -> generic registration allowed; * if there is one ticket -> quick registration box; * more than one ticket -> unfolding registration box with all available tickets; About price * sale not installed -> no mention of price. A ticket without price is not free. Its description allow to tell how to pay for example; * a price is set: price is displayed; * no price is set: FREE is displayed; Most event frontend templates and controllers are therefore moved from website_event_sale to website_event. Only part about pricing and sale order creation is now located in website_event_sale. Buy flow remains mainly untouched. Indeed this commit is mainly about moving template to support tickets. LINKS Task ID 2177281 Community PR #43488 --- addons/website_event/controllers/main.py | 78 +++++++-- .../static/src/scss/website_event.scss | 6 + .../website_event/views/event_templates.xml | 160 +++++++++++++----- .../controllers/main.py | 4 +- addons/website_event_sale/controllers/main.py | 70 ++++---- .../views/website_event_templates.xml | 141 +++------------ 6 files changed, 239 insertions(+), 220 deletions(-) diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index d4d8e7f06ca..dfb66f7f834 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -254,25 +254,62 @@ class WebsiteEventController(http.Controller): "url": event.website_url}) return request.env['ir.ui.view'].render_template("website_event.country_events_list", result) - def _process_tickets_details(self, data): - nb_register = int(data.get('nb_register-0', 0)) - if nb_register: - return [{'id': 0, 'name': 'Registration', 'quantity': nb_register, 'price': 0}] - return [] + def _process_tickets_form(self, event, form_details): + """ Process posted data about ticket order. Generic ticket are supported + for event without tickets (generic registration). + + :return: list of order per ticket: [{ + 'id': if of ticket if any (0 if no ticket), + 'ticket': browse record of ticket if any (None if no ticket), + 'name': ticket name (or generic 'Registration' name if no ticket), + 'quantity': number of registrations for that ticket, + }, {...}] + """ + ticket_order = {} + for key, value in form_details.items(): + registration_items = key.split('nb_register-') + if len(registration_items) != 2: + continue + ticket_order[int(registration_items[1])] = int(value) + + ticket_dict = dict((ticket.id, ticket) for ticket in request.env['event.event.ticket'].search([ + ('id', 'in', [tid for tid in ticket_order.keys() if tid]), + ('event_id', '=', event.id) + ])) + + return [{ + 'id': tid if ticket_dict.get(tid) else 0, + 'ticket': ticket_dict.get(tid), + 'name': ticket_dict[tid]['name'] if ticket_dict.get(tid) else _('Registration'), + 'quantity': count, + } for tid, count in ticket_order.items() if count] @http.route(['/event//registration/new'], type='json', auth="public", methods=['POST'], website=True) def registration_new(self, event, **post): - tickets = self._process_tickets_details(post) + tickets = self._process_tickets_form(event, post) if not tickets: return False return request.env['ir.ui.view'].render_template("website_event.registration_attendee_details", {'tickets': tickets, 'event': event}) - def _process_registration_details(self, details): - ''' Process data posted from the attendee details form. ''' + def _process_attendees_form(self, event, form_details): + """ Process data posted from the attendee details form. + + :param details: posted data from frontend registration form, like + {'1-name': 'r', '1-email': 'r@r.com', '1-phone': '', '1-event_ticket_id': '1'} + """ + registration_fields = request.env['event.registration']._fields + registrations = {} global_values = {} - for key, value in details.items(): + for key, value in form_details.items(): counter, field_name = key.split('-', 1) + if field_name not in registration_fields: + continue + elif isinstance(registration_fields[field_name], (fields.Many2one, fields.Integer)): + value = int(value) + else: + value = value + if counter == '0': global_values[field_name] = value else: @@ -282,23 +319,28 @@ class WebsiteEventController(http.Controller): registration[key] = value return list(registrations.values()) + def _create_attendees_from_registration_post(self, event, registration_data): + attendees_sudo = request.env['event.registration'].sudo() + + for registration_values in registration_data: + registration_values['event_id'] = event.id + if not registration_values.get('partner_id'): + registration_values['partner_id'] = request.env.user.partner_id.id + attendees_sudo += request.env['event.registration'].sudo().create(registration_values) + + return attendees_sudo + @http.route(['''/event//registration/confirm'''], type='http', auth="public", methods=['POST'], website=True) def registration_confirm(self, event, **post): if not event.can_access_from_current_website(): raise werkzeug.exceptions.NotFound() - Attendees = request.env['event.registration'] - registrations = self._process_registration_details(post) - - 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) + registrations = self._process_attendees_form(event, post) + attendees = self._create_attendees_from_registration_post(event, registrations) urls = event._get_event_resource_urls() return request.render("website_event.registration_complete", { - 'attendees': Attendees.sudo(), + 'attendees': attendees, 'event': event, 'google_url': urls.get('google_url'), 'iCal_url': urls.get('iCal_url') diff --git a/addons/website_event/static/src/scss/website_event.scss b/addons/website_event/static/src/scss/website_event.scss index 576d0360bb6..ebd7d31ce2b 100644 --- a/addons/website_event/static/src/scss/website_event.scss +++ b/addons/website_event/static/src/scss/website_event.scss @@ -269,3 +269,9 @@ $o-wevent-event-title-sizes-variants: ( } } } + +.modal-open { + > .modal-backdrop { + z-index: $zindex-dropdown - 1; + } +} diff --git a/addons/website_event/views/event_templates.xml b/addons/website_event/views/event_templates.xml index bc08e29530c..52a9e9be649 100644 --- a/addons/website_event/views/event_templates.xml +++ b/addons/website_event/views/event_templates.xml @@ -209,7 +209,18 @@
-
Free
+ + + Sold Out + + + Only Remaining + +
@@ -505,22 +516,109 @@ - -