From cc60fcd45cbaf584a08ca51e02979e8d61b3ea7a Mon Sep 17 00:00:00 2001 From: Patrick Hoste Date: Thu, 23 Mar 2023 14:13:09 +0000 Subject: [PATCH] [MOV] website_event(_crm)_questions: move event questions code This commit moves the code from website_event_questions to website_event and from website_event_crm_questions to website_event_crm module. Task-3056380 Part-of: odoo/odoo#112164 --- addons/event/models/res_config_settings.py | 1 - addons/test_event_full/__manifest__.py | 2 - addons/website_event/__manifest__.py | 4 +- addons/website_event/controllers/main.py | 52 ++- .../data/event_question_demo.xml | 36 +-- .../data/event_registration_answer_demo.xml} | 37 ++- addons/website_event/models/__init__.py | 3 + addons/website_event/models/event_event.py | 53 ++++ .../models/event_question.py | 17 +- .../models/event_question_answer.py | 21 ++ .../models/event_registration.py | 1 + .../models/event_registration_answer.py} | 6 - addons/website_event/models/event_type.py | 3 + .../website_event/security/event_security.xml | 44 +++ .../security/ir.model.access.csv | 6 + .../static/tests/tours/tickets_questions.js | 0 addons/website_event/tests/__init__.py | 1 + addons/website_event/tests/common.py | 44 +++ .../tests/test_event_internals.py | 6 +- .../website_event/tests/test_website_event.py | 83 +++++ .../website_event/views/event_event_views.xml | 45 +++ .../views/event_question_views.xml | 0 .../views/event_registration_answer_views.xml | 0 .../views/event_registration_views.xml | 31 ++ .../event_templates_page_registration.xml | 38 +++ .../website_event/views/event_type_views.xml | 13 + .../views/res_config_settings_views.xml | 17 - addons/website_event_crm/__init__.py | 4 + .../models/__init__.py | 0 .../models/event_registration.py | 0 .../website_event_crm_questions/__init__.py | 4 - .../__manifest__.py | 17 - addons/website_event_questions/__init__.py | 5 - .../website_event_questions/__manifest__.py | 29 -- .../controllers/__init__.py | 3 - .../controllers/main.py | 47 --- addons/website_event_questions/i18n/af.po | 251 --------------- addons/website_event_questions/i18n/ar.po | 225 ------------- addons/website_event_questions/i18n/az.po | 212 ------------- addons/website_event_questions/i18n/bg.po | 251 --------------- addons/website_event_questions/i18n/bs.po | 217 ------------- addons/website_event_questions/i18n/ca.po | 219 ------------- addons/website_event_questions/i18n/cs.po | 226 ------------- addons/website_event_questions/i18n/da.po | 230 -------------- addons/website_event_questions/i18n/de.po | 232 -------------- addons/website_event_questions/i18n/el.po | 216 ------------- addons/website_event_questions/i18n/en_AU.po | 251 --------------- addons/website_event_questions/i18n/en_GB.po | 251 --------------- addons/website_event_questions/i18n/es.po | 233 -------------- addons/website_event_questions/i18n/es_BO.po | 251 --------------- addons/website_event_questions/i18n/es_CL.po | 251 --------------- addons/website_event_questions/i18n/es_CO.po | 255 --------------- addons/website_event_questions/i18n/es_CR.po | 251 --------------- addons/website_event_questions/i18n/es_DO.po | 251 --------------- addons/website_event_questions/i18n/es_EC.po | 255 --------------- addons/website_event_questions/i18n/es_PA.po | 251 --------------- addons/website_event_questions/i18n/es_PE.po | 251 --------------- addons/website_event_questions/i18n/es_PY.po | 251 --------------- addons/website_event_questions/i18n/es_VE.po | 251 --------------- addons/website_event_questions/i18n/et.po | 222 ------------- addons/website_event_questions/i18n/eu.po | 250 --------------- addons/website_event_questions/i18n/fa.po | 218 ------------- addons/website_event_questions/i18n/fi.po | 224 ------------- addons/website_event_questions/i18n/fo.po | 250 --------------- addons/website_event_questions/i18n/fr.po | 229 -------------- addons/website_event_questions/i18n/fr_BE.po | 251 --------------- addons/website_event_questions/i18n/fr_CA.po | 251 --------------- addons/website_event_questions/i18n/gl.po | 250 --------------- addons/website_event_questions/i18n/gu.po | 217 ------------- addons/website_event_questions/i18n/he.po | 223 ------------- addons/website_event_questions/i18n/hi.po | 254 --------------- addons/website_event_questions/i18n/hr.po | 225 ------------- addons/website_event_questions/i18n/hu.po | 227 ------------- addons/website_event_questions/i18n/id.po | 277 ---------------- addons/website_event_questions/i18n/is.po | 218 ------------- addons/website_event_questions/i18n/it.po | 230 -------------- addons/website_event_questions/i18n/ja.po | 217 ------------- addons/website_event_questions/i18n/ka.po | 250 --------------- addons/website_event_questions/i18n/kab.po | 250 --------------- addons/website_event_questions/i18n/km.po | 217 ------------- addons/website_event_questions/i18n/ko.po | 219 ------------- addons/website_event_questions/i18n/lb.po | 217 ------------- addons/website_event_questions/i18n/lt.po | 229 -------------- addons/website_event_questions/i18n/lv.po | 270 ---------------- addons/website_event_questions/i18n/mk.po | 252 --------------- addons/website_event_questions/i18n/ml_IN.po | 251 --------------- addons/website_event_questions/i18n/mn.po | 226 ------------- addons/website_event_questions/i18n/nb.po | 226 ------------- addons/website_event_questions/i18n/nl.po | 227 ------------- addons/website_event_questions/i18n/pl.po | 231 -------------- addons/website_event_questions/i18n/pt.po | 217 ------------- addons/website_event_questions/i18n/pt_BR.po | 228 ------------- addons/website_event_questions/i18n/ro.po | 217 ------------- addons/website_event_questions/i18n/ru.po | 225 ------------- addons/website_event_questions/i18n/sk.po | 219 ------------- addons/website_event_questions/i18n/sl.po | 270 ---------------- addons/website_event_questions/i18n/sq.po | 250 --------------- addons/website_event_questions/i18n/sr.po | 215 ------------- .../website_event_questions/i18n/sr@latin.po | 276 ---------------- addons/website_event_questions/i18n/sv.po | 224 ------------- addons/website_event_questions/i18n/ta.po | 252 --------------- addons/website_event_questions/i18n/th.po | 218 ------------- addons/website_event_questions/i18n/tr.po | 229 -------------- addons/website_event_questions/i18n/uk.po | 225 ------------- addons/website_event_questions/i18n/vi.po | 226 ------------- .../i18n/website_event_questions.pot | 299 ------------------ addons/website_event_questions/i18n/zh_CN.po | 229 -------------- addons/website_event_questions/i18n/zh_TW.po | 223 ------------- .../models/__init__.py | 5 - .../models/event_event.py | 69 ---- .../security/ir.model.access.csv | 7 - .../security/security.xml | 46 --- .../static/description/icon.png | Bin 7062 -> 0 bytes .../static/description/icon.svg | 1 - .../website_event_questions/tests/__init__.py | 6 - .../website_event_questions/tests/common.py | 48 --- .../tests/test_event_ui.py | 90 ------ .../views/event_registration_views.xml | 49 --- .../views/event_templates.xml | 51 --- .../views/event_views.xml | 78 ----- 120 files changed, 474 insertions(+), 17721 deletions(-) rename addons/{website_event_questions => website_event}/data/event_question_demo.xml (79%) rename addons/{website_event_questions/data/event_registration_demo.xml => website_event/data/event_registration_answer_demo.xml} (51%) rename addons/{website_event_questions => website_event}/models/event_question.py (81%) create mode 100644 addons/website_event/models/event_question_answer.py rename addons/{website_event_questions/models/event_registration.py => website_event/models/event_registration_answer.py} (86%) rename addons/{website_event_questions => website_event}/static/tests/tours/tickets_questions.js (100%) rename addons/{website_event_questions => website_event}/tests/test_event_internals.py (96%) rename addons/{website_event_questions => website_event}/views/event_question_views.xml (100%) rename addons/{website_event_questions => website_event}/views/event_registration_answer_views.xml (100%) delete mode 100644 addons/website_event/views/res_config_settings_views.xml rename addons/{website_event_crm_questions => website_event_crm}/models/__init__.py (100%) rename addons/{website_event_crm_questions => website_event_crm}/models/event_registration.py (100%) delete mode 100644 addons/website_event_crm_questions/__init__.py delete mode 100644 addons/website_event_crm_questions/__manifest__.py delete mode 100644 addons/website_event_questions/__init__.py delete mode 100644 addons/website_event_questions/__manifest__.py delete mode 100644 addons/website_event_questions/controllers/__init__.py delete mode 100644 addons/website_event_questions/controllers/main.py delete mode 100644 addons/website_event_questions/i18n/af.po delete mode 100644 addons/website_event_questions/i18n/ar.po delete mode 100644 addons/website_event_questions/i18n/az.po delete mode 100644 addons/website_event_questions/i18n/bg.po delete mode 100644 addons/website_event_questions/i18n/bs.po delete mode 100644 addons/website_event_questions/i18n/ca.po delete mode 100644 addons/website_event_questions/i18n/cs.po delete mode 100644 addons/website_event_questions/i18n/da.po delete mode 100644 addons/website_event_questions/i18n/de.po delete mode 100644 addons/website_event_questions/i18n/el.po delete mode 100644 addons/website_event_questions/i18n/en_AU.po delete mode 100644 addons/website_event_questions/i18n/en_GB.po delete mode 100644 addons/website_event_questions/i18n/es.po delete mode 100644 addons/website_event_questions/i18n/es_BO.po delete mode 100644 addons/website_event_questions/i18n/es_CL.po delete mode 100644 addons/website_event_questions/i18n/es_CO.po delete mode 100644 addons/website_event_questions/i18n/es_CR.po delete mode 100644 addons/website_event_questions/i18n/es_DO.po delete mode 100644 addons/website_event_questions/i18n/es_EC.po delete mode 100644 addons/website_event_questions/i18n/es_PA.po delete mode 100644 addons/website_event_questions/i18n/es_PE.po delete mode 100644 addons/website_event_questions/i18n/es_PY.po delete mode 100644 addons/website_event_questions/i18n/es_VE.po delete mode 100644 addons/website_event_questions/i18n/et.po delete mode 100644 addons/website_event_questions/i18n/eu.po delete mode 100644 addons/website_event_questions/i18n/fa.po delete mode 100644 addons/website_event_questions/i18n/fi.po delete mode 100644 addons/website_event_questions/i18n/fo.po delete mode 100644 addons/website_event_questions/i18n/fr.po delete mode 100644 addons/website_event_questions/i18n/fr_BE.po delete mode 100644 addons/website_event_questions/i18n/fr_CA.po delete mode 100644 addons/website_event_questions/i18n/gl.po delete mode 100644 addons/website_event_questions/i18n/gu.po delete mode 100644 addons/website_event_questions/i18n/he.po delete mode 100644 addons/website_event_questions/i18n/hi.po delete mode 100644 addons/website_event_questions/i18n/hr.po delete mode 100644 addons/website_event_questions/i18n/hu.po delete mode 100644 addons/website_event_questions/i18n/id.po delete mode 100644 addons/website_event_questions/i18n/is.po delete mode 100644 addons/website_event_questions/i18n/it.po delete mode 100644 addons/website_event_questions/i18n/ja.po delete mode 100644 addons/website_event_questions/i18n/ka.po delete mode 100644 addons/website_event_questions/i18n/kab.po delete mode 100644 addons/website_event_questions/i18n/km.po delete mode 100644 addons/website_event_questions/i18n/ko.po delete mode 100644 addons/website_event_questions/i18n/lb.po delete mode 100644 addons/website_event_questions/i18n/lt.po delete mode 100644 addons/website_event_questions/i18n/lv.po delete mode 100644 addons/website_event_questions/i18n/mk.po delete mode 100644 addons/website_event_questions/i18n/ml_IN.po delete mode 100644 addons/website_event_questions/i18n/mn.po delete mode 100644 addons/website_event_questions/i18n/nb.po delete mode 100644 addons/website_event_questions/i18n/nl.po delete mode 100644 addons/website_event_questions/i18n/pl.po delete mode 100644 addons/website_event_questions/i18n/pt.po delete mode 100644 addons/website_event_questions/i18n/pt_BR.po delete mode 100644 addons/website_event_questions/i18n/ro.po delete mode 100644 addons/website_event_questions/i18n/ru.po delete mode 100644 addons/website_event_questions/i18n/sk.po delete mode 100644 addons/website_event_questions/i18n/sl.po delete mode 100644 addons/website_event_questions/i18n/sq.po delete mode 100644 addons/website_event_questions/i18n/sr.po delete mode 100644 addons/website_event_questions/i18n/sr@latin.po delete mode 100644 addons/website_event_questions/i18n/sv.po delete mode 100644 addons/website_event_questions/i18n/ta.po delete mode 100644 addons/website_event_questions/i18n/th.po delete mode 100644 addons/website_event_questions/i18n/tr.po delete mode 100644 addons/website_event_questions/i18n/uk.po delete mode 100644 addons/website_event_questions/i18n/vi.po delete mode 100644 addons/website_event_questions/i18n/website_event_questions.pot delete mode 100644 addons/website_event_questions/i18n/zh_CN.po delete mode 100644 addons/website_event_questions/i18n/zh_TW.po delete mode 100644 addons/website_event_questions/models/__init__.py delete mode 100644 addons/website_event_questions/models/event_event.py delete mode 100644 addons/website_event_questions/security/ir.model.access.csv delete mode 100644 addons/website_event_questions/security/security.xml delete mode 100644 addons/website_event_questions/static/description/icon.png delete mode 100644 addons/website_event_questions/static/description/icon.svg delete mode 100644 addons/website_event_questions/tests/__init__.py delete mode 100644 addons/website_event_questions/tests/common.py delete mode 100644 addons/website_event_questions/tests/test_event_ui.py delete mode 100644 addons/website_event_questions/views/event_registration_views.xml delete mode 100644 addons/website_event_questions/views/event_templates.xml delete mode 100644 addons/website_event_questions/views/event_views.xml diff --git a/addons/event/models/res_config_settings.py b/addons/event/models/res_config_settings.py index 38907e18fb1..fdb24bf3bb2 100644 --- a/addons/event/models/res_config_settings.py +++ b/addons/event/models/res_config_settings.py @@ -24,7 +24,6 @@ class ResConfigSettings(models.TransientModel): module_website_event_track_live = fields.Boolean("Live Mode") module_website_event_track_quiz = fields.Boolean("Quiz on Tracks") module_website_event_exhibitor = fields.Boolean("Advanced Sponsors") - module_website_event_questions = fields.Boolean("Registration Survey") module_event_barcode = fields.Boolean("Barcode") module_website_event_sale = fields.Boolean("Online Ticketing") module_event_booth = fields.Boolean("Booth Management") diff --git a/addons/test_event_full/__manifest__.py b/addons/test_event_full/__manifest__.py index 82d233c4717..417c155d0a9 100644 --- a/addons/test_event_full/__manifest__.py +++ b/addons/test_event_full/__manifest__.py @@ -19,9 +19,7 @@ automatic lead generation, full Online support, ... 'event_sms', 'payment_demo', 'website_event_booth_sale_exhibitor', - 'website_event_crm_questions', 'website_event_exhibitor', - 'website_event_questions', 'website_event_meet', 'website_event_sale', 'website_event_track', diff --git a/addons/website_event/__manifest__.py b/addons/website_event/__manifest__.py index 52cd7758a2b..3c3b86c7694 100644 --- a/addons/website_event/__manifest__.py +++ b/addons/website_event/__manifest__.py @@ -17,7 +17,6 @@ 'data': [ 'data/event_data.xml', 'data/website_snippet_data.xml', - 'views/res_config_settings_views.xml', 'views/event_snippets.xml', 'views/snippets/s_events.xml', 'views/snippets/snippets.xml', @@ -28,6 +27,8 @@ 'views/event_templates_widgets.xml', 'views/event_event_views.xml', 'views/event_registration_views.xml', + 'views/event_question_views.xml', + 'views/event_registration_answer_views.xml', 'views/event_tag_category_views.xml', 'views/event_type_views.xml', 'views/website_event_menu_views.xml', @@ -41,6 +42,7 @@ 'demo': [ 'data/res_partner_demo.xml', 'data/event_demo.xml', + 'data/event_question_demo.xml', 'data/event_registration_demo.xml', ], 'application': True, diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index c0b33bcfa5d..7ae4c40491e 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -256,6 +256,10 @@ class WebsiteEventController(http.Controller): def _process_attendees_form(self, event, form_details): """ Process data posted from the attendee details form. + Extracts question answers: + - For both questions asked 'once_per_order' and questions asked to every attendee + - For questions of type 'simple_choice', extracting the suggested answer id + - For questions of type 'text_box', extracting the text answer of the attendee. :param form_details: posted data from frontend registration form, like {'1-name': 'r', '1-email': 'r@r.com', '1-phone': '', '1-event_ticket_id': '1'} @@ -267,24 +271,50 @@ class WebsiteEventController(http.Controller): raise UserError(_("This ticket is not available for sale for this event")) registrations = {} global_values = {} + general_answer_ids = [] for key, value in form_details.items(): - counter, attr_name = key.split('-', 1) - field_name = attr_name.split('-')[0] - if field_name not in registration_fields: - continue - elif isinstance(registration_fields[field_name], (fields.Many2one, fields.Integer)): - value = int(value) or False # 0 is considered as a void many2one aka False - else: - value = value + if 'question_answer' in key and value: + dummy, registration_index, question_id = key.split('-') + question_sudo = request.env['event.question'].browse(int(question_id)) + answer_values = None + if question_sudo.question_type == 'simple_choice': + answer_values = { + 'question_id': int(question_id), + 'value_answer_id': int(value) + } + elif question_sudo.question_type == 'text_box': + answer_values = { + 'question_id': int(question_id), + 'value_text_box': value + } - if counter == '0': - global_values[attr_name] = value + if answer_values and not int(registration_index): + general_answer_ids.append((0, 0, answer_values)) + elif answer_values: + registrations.setdefault(registration_index, dict())\ + .setdefault('registration_answer_ids', list()).append((0, 0, answer_values)) else: - registrations.setdefault(counter, dict())[attr_name] = value + counter, attr_name = key.split('-', 1) + field_name = attr_name.split('-')[0] + if field_name not in registration_fields: + continue + elif isinstance(registration_fields[field_name], (fields.Many2one, fields.Integer)): + value = int(value) or False # 0 is considered as a void many2one aka False + else: + value = value + + if counter == '0': + global_values[attr_name] = value + else: + registrations.setdefault(counter, dict())[attr_name] = value for key, value in global_values.items(): for registration in registrations.values(): registration[key] = value + if general_answer_ids: + for registration in registrations.values(): + registration.setdefault('registration_answer_ids', list()).extend(general_answer_ids) + return list(registrations.values()) def _create_attendees_from_registration_post(self, event, registration_data): diff --git a/addons/website_event_questions/data/event_question_demo.xml b/addons/website_event/data/event_question_demo.xml similarity index 79% rename from addons/website_event_questions/data/event_question_demo.xml rename to addons/website_event/data/event_question_demo.xml index 91fbb6e44c2..db169e8c328 100644 --- a/addons/website_event_questions/data/event_question_demo.xml +++ b/addons/website_event/data/event_question_demo.xml @@ -9,17 +9,17 @@ Social Media 1 - + Blog Post 2 - + Radio Ad 3 - + @@ -32,17 +32,17 @@ Mixed 1 - + Vegetarian 2 - + Pastafarian 3 - + Allergies @@ -59,17 +59,17 @@ Our website 1 - + Commercials 2 - + A friend 3 - + @@ -82,17 +82,17 @@ Social Media 1 - + Blog Post 2 - + Radio Ad 3 - + @@ -105,17 +105,17 @@ Social Media 1 - + Blog Post 2 - + Radio Ad 3 - + What's your Hockey level? @@ -134,17 +134,17 @@ Consumers 1 - + Sales 2 - + Research 3 - + How did you hear about us? diff --git a/addons/website_event_questions/data/event_registration_demo.xml b/addons/website_event/data/event_registration_answer_demo.xml similarity index 51% rename from addons/website_event_questions/data/event_registration_demo.xml rename to addons/website_event/data/event_registration_answer_demo.xml index f2a10283f10..043cc82c1b3 100644 --- a/addons/website_event_questions/data/event_registration_demo.xml +++ b/addons/website_event/data/event_registration_answer_demo.xml @@ -1,41 +1,40 @@ - - + + - - + + - Fish -Nuts - + Fish Nuts + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/addons/website_event/models/__init__.py b/addons/website_event/models/__init__.py index 1e1a81e486f..d18c42893cd 100644 --- a/addons/website_event/models/__init__.py +++ b/addons/website_event/models/__init__.py @@ -2,7 +2,10 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import event_event +from . import event_question +from . import event_question_answer from . import event_registration +from . import event_registration_answer from . import event_tag_category from . import event_type from . import website diff --git a/addons/website_event/models/event_event.py b/addons/website_event/models/event_event.py index 3366534ef24..b7b42da0509 100644 --- a/addons/website_event/models/event_event.py +++ b/addons/website_event/models/event_event.py @@ -89,6 +89,14 @@ class Event(models.Model): start_remaining = fields.Integer( 'Remaining before start', compute='_compute_time_data', help="Remaining time before event starts (minutes)") + # questions + question_ids = fields.One2many( + 'event.question', 'event_id', 'Questions', copy=True, + compute='_compute_question_ids', readonly=False, store=True) + general_question_ids = fields.One2many('event.question', 'event_id', 'General Questions', + domain=[('once_per_order', '=', True)]) + specific_question_ids = fields.One2many('event.question', 'event_id', 'Specific Questions', + domain=[('once_per_order', '=', False)]) def _compute_is_participating(self): """Heuristic @@ -191,6 +199,51 @@ class Event(models.Model): if event.id: # avoid to perform a slug on a not yet saved record in case of an onchange. event.website_url = '/event/%s' % slug(event) + @api.depends('event_type_id') + def _compute_question_ids(self): + """ Update event questions from its event type. Depends are set only on + event_type_id itself to emulate an onchange. Changing event type content + itself should not trigger this method. + + When synchronizing questions: + + * lines that no answer are removed; + * type lines are added; + """ + if self._origin.question_ids: + # lines to keep: those with already given answers + questions_tokeep_ids = self.env['event.registration.answer'].search( + [('question_id', 'in', self._origin.question_ids.ids)] + ).question_id.ids + else: + questions_tokeep_ids = [] + for event in self: + if not event.event_type_id and not event.question_ids: + event.question_ids = False + continue + + if questions_tokeep_ids: + questions_toremove = event._origin.question_ids.filtered( + lambda question: question.id not in questions_tokeep_ids) + command = [(3, question.id) for question in questions_toremove] + else: + command = [(5, 0)] + if event.event_type_id.question_ids: + command += [ + (0, 0, { + 'answer_ids': [(0, 0, { + 'name': answer.name, + 'sequence': answer.sequence + }) for answer in question.answer_ids], + 'is_mandatory_answer': question.is_mandatory_answer, + 'once_per_order': question.once_per_order, + 'question_type': question.question_type, + 'sequence': question.sequence, + 'title': question.title, + }) for question in event.event_type_id.question_ids + ] + event.question_ids = command + # ------------------------------------------------------------ # CRUD # ------------------------------------------------------------ diff --git a/addons/website_event_questions/models/event_question.py b/addons/website_event/models/event_question.py similarity index 81% rename from addons/website_event_questions/models/event_question.py rename to addons/website_event/models/event_question.py index 8450cc7c255..b005731cd17 100644 --- a/addons/website_event_questions/models/event_question.py +++ b/addons/website_event/models/event_question.py @@ -52,25 +52,10 @@ class EventQuestion(models.Model): (Along with secondary pivot and tree views) - A tree view showing textual answers values for text_box questions. """ self.ensure_one() - action = self.env["ir.actions.actions"]._for_xml_id("website_event_questions.action_event_registration_report") + action = self.env["ir.actions.actions"]._for_xml_id("website_event.action_event_registration_report") action['domain'] = [('question_id', '=', self.id)] if self.question_type == 'simple_choice': action['views'] = [(False, 'graph'), (False, 'pivot'), (False, 'tree')] elif self.question_type == 'text_box': action['views'] = [(False, 'tree')] return action - -class EventQuestionAnswer(models.Model): - """ Contains suggested answers to a 'simple_choice' event.question. """ - _name = 'event.question.answer' - _order = 'sequence,id' - _description = 'Event Question Answer' - - name = fields.Char('Answer', required=True, translate=True) - question_id = fields.Many2one('event.question', required=True, ondelete='cascade') - sequence = fields.Integer(default=10) - - @api.ondelete(at_uninstall=False) - def _unlink_except_selected_answer(self): - if self.env['event.registration.answer'].search_count([('value_answer_id', 'in', self.ids)]): - raise UserError(_('You cannot delete an answer that has already been selected by attendees.')) diff --git a/addons/website_event/models/event_question_answer.py b/addons/website_event/models/event_question_answer.py new file mode 100644 index 00000000000..b6005b3ebd7 --- /dev/null +++ b/addons/website_event/models/event_question_answer.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class EventQuestionAnswer(models.Model): + """ Contains suggested answers to a 'simple_choice' event.question. """ + _name = 'event.question.answer' + _order = 'sequence,id' + _description = 'Event Question Answer' + + name = fields.Char('Answer', required=True, translate=True) + question_id = fields.Many2one('event.question', required=True, ondelete='cascade') + sequence = fields.Integer(default=10) + + @api.ondelete(at_uninstall=False) + def _unlink_except_selected_answer(self): + if self.env['event.registration.answer'].search_count([('value_answer_id', 'in', self.ids)]): + raise UserError(_('You cannot delete an answer that has already been selected by attendees.')) diff --git a/addons/website_event/models/event_registration.py b/addons/website_event/models/event_registration.py index f62f206934f..6fea1061f26 100644 --- a/addons/website_event/models/event_registration.py +++ b/addons/website_event/models/event_registration.py @@ -9,6 +9,7 @@ class EventRegistration(models.Model): _inherit = ['event.registration'] visitor_id = fields.Many2one('website.visitor', string='Visitor', ondelete='set null') + registration_answer_ids = fields.One2many('event.registration.answer', 'registration_id', string='Attendee Answers') def _get_website_registration_allowed_fields(self): return {'name', 'phone', 'email', 'mobile', 'event_id', 'partner_id', 'event_ticket_id'} diff --git a/addons/website_event_questions/models/event_registration.py b/addons/website_event/models/event_registration_answer.py similarity index 86% rename from addons/website_event_questions/models/event_registration.py rename to addons/website_event/models/event_registration_answer.py index 45fbfe717a0..b78590cd1ca 100644 --- a/addons/website_event_questions/models/event_registration.py +++ b/addons/website_event/models/event_registration_answer.py @@ -4,12 +4,6 @@ from odoo import fields, models -class EventRegistration(models.Model): - """ Store answers on attendees. """ - _inherit = 'event.registration' - - registration_answer_ids = fields.One2many('event.registration.answer', 'registration_id', string='Attendee Answers') - class EventRegistrationAnswer(models.Model): """ Represents the user input answer for a single event.question """ _name = 'event.registration.answer' diff --git a/addons/website_event/models/event_type.py b/addons/website_event/models/event_type.py index ee2afc69e07..57e963f8bff 100644 --- a/addons/website_event/models/event_type.py +++ b/addons/website_event/models/event_type.py @@ -16,6 +16,9 @@ class EventType(models.Model): menu_register_cta = fields.Boolean( 'Extra Register Button', compute='_compute_menu_register_cta', readonly=False, store=True) + question_ids = fields.One2many( + 'event.question', 'event_type_id', + string='Questions', copy=True) @api.depends('website_menu') def _compute_community_menu(self): diff --git a/addons/website_event/security/event_security.xml b/addons/website_event/security/event_security.xml index b0b51557785..711cdc82654 100644 --- a/addons/website_event/security/event_security.xml +++ b/addons/website_event/security/event_security.xml @@ -33,6 +33,50 @@ + + + Event Question: not event groups: event published read + + [('event_id.is_published', '=', True)] + + + + + + + + + Event Question: event user: read all + + [(1, '=', 1)] + + + + + + + + + Event Question Answer: not event groups: event published read + + [('question_id.event_id.is_published', '=', True)] + + + + + + + + + Event Question Answer: event user: read all + + [(1, '=', 1)] + + + + + + diff --git a/addons/website_event/security/ir.model.access.csv b/addons/website_event/security/ir.model.access.csv index f81720ec096..6fe59cba5df 100644 --- a/addons/website_event/security/ir.model.access.csv +++ b/addons/website_event/security/ir.model.access.csv @@ -7,3 +7,9 @@ access_event_tag,event.tag,event.model_event_tag,,1,0,0,0 access_website_event_menu,website.event.menu,model_website_event_menu,,1,0,0,0 access_website_event_menu_user,website.event.menu.user,model_website_event_menu,event.group_event_user,1,1,1,1 access_website_visitor_user,website.visitor.user,model_website_visitor,event.group_event_user,1,1,0,0 +access_event_question,event.question,model_event_question,,1,0,0,0 +access_event_question_user,event.question.user,model_event_question,event.group_event_user,1,1,1,1 +access_event_question_answer,event.question.answer,model_event_question_answer,,1,0,0,0 +access_event_question_answer_registration,event.question.answer.registration,model_event_question_answer,event.group_event_registration_desk,1,1,0,0 +access_event_question_answer_user,event.question.answer.user,model_event_question_answer,event.group_event_user,1,1,1,1 +access_event_registration_answer,event.registration.answer,model_event_registration_answer,event.group_event_registration_desk,1,1,1,1 diff --git a/addons/website_event_questions/static/tests/tours/tickets_questions.js b/addons/website_event/static/tests/tours/tickets_questions.js similarity index 100% rename from addons/website_event_questions/static/tests/tours/tickets_questions.js rename to addons/website_event/static/tests/tours/tickets_questions.js diff --git a/addons/website_event/tests/__init__.py b/addons/website_event/tests/__init__.py index 3865a64e323..84a718ac464 100644 --- a/addons/website_event/tests/__init__.py +++ b/addons/website_event/tests/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import test_event_internals from . import test_event_menus from . import test_event_visitor from . import test_website_event diff --git a/addons/website_event/tests/common.py b/addons/website_event/tests/common.py index 734f9c6ce9d..bf19adcb309 100644 --- a/addons/website_event/tests/common.py +++ b/addons/website_event/tests/common.py @@ -77,3 +77,47 @@ class TestEventOnlineCommon(OnlineEventCase): 'date_end': datetime.combine(cls.reference_now, time(13, 0)) + timedelta(days=1), 'date_tz': 'Europe/Brussels', }) + + +class TestEventQuestionCommon(EventCase): + + @classmethod + def setUpClass(cls): + super(TestEventQuestionCommon, cls).setUpClass() + + cls.event_type_questions = cls.env['event.type'].create({ + 'name': 'Update Type', + 'auto_confirm': True, + 'has_seats_limitation': True, + 'seats_max': 30, + 'default_timezone': 'Europe/Paris', + 'event_type_ticket_ids': [], + 'event_type_mail_ids': [], + }) + + cls.event_question_1 = cls.env['event.question'].create({ + 'title': 'Question1', + 'question_type': 'simple_choice', + 'event_type_id': cls.event_type_questions.id, + 'once_per_order': False, + 'answer_ids': [ + (0, 0, {'name': 'Q1-Answer1'}), + (0, 0, {'name': 'Q1-Answer2'}) + ], + }) + cls.event_question_2 = cls.env['event.question'].create({ + 'title': 'Question2', + 'question_type': 'simple_choice', + 'event_type_id': cls.event_type_questions.id, + 'once_per_order': True, + 'answer_ids': [ + (0, 0, {'name': 'Q2-Answer1'}), + (0, 0, {'name': 'Q2-Answer2'}) + ], + }) + cls.event_question_3 = cls.env['event.question'].create({ + 'title': 'Question3', + 'question_type': 'text_box', + 'event_type_id': cls.event_type_questions.id, + 'once_per_order': True, + }) diff --git a/addons/website_event_questions/tests/test_event_internals.py b/addons/website_event/tests/test_event_internals.py similarity index 96% rename from addons/website_event_questions/tests/test_event_internals.py rename to addons/website_event/tests/test_event_internals.py index 3b73b447164..ca5f364b5aa 100644 --- a/addons/website_event_questions/tests/test_event_internals.py +++ b/addons/website_event/tests/test_event_internals.py @@ -6,8 +6,8 @@ from datetime import datetime, timedelta from odoo.fields import Datetime as FieldsDatetime from odoo.tests.common import users from odoo.addons.website.tools import MockRequest -from odoo.addons.website_event_questions.controllers.main import WebsiteEvent -from odoo.addons.website_event_questions.tests.common import TestEventQuestionCommon +from odoo.addons.website_event.controllers.main import WebsiteEventController +from odoo.addons.website_event.tests.common import TestEventQuestionCommon class TestEventData(TestEventQuestionCommon): @@ -71,7 +71,7 @@ class TestEventData(TestEventQuestionCommon): } with MockRequest(self.env): - registrations = WebsiteEvent()._process_attendees_form(event, form_details) + registrations = WebsiteEventController()._process_attendees_form(event, form_details) self.assertEqual(registrations, [ {'name': 'Pixis', 'email': 'pixis@gmail.com', 'phone': '+32444444444', 'event_ticket_id': ticket_id_1.id, diff --git a/addons/website_event/tests/test_website_event.py b/addons/website_event/tests/test_website_event.py index b01cb6920c0..ab694d3cdb4 100644 --- a/addons/website_event/tests/test_website_event.py +++ b/addons/website_event/tests/test_website_event.py @@ -2,7 +2,9 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta +from odoo import fields from odoo.addons.base.tests.common import HttpCaseWithUserDemo from odoo.addons.mail.tests.common import mail_new_test_user from odoo.addons.website.tests.test_base_url import TestUrlCommon @@ -29,6 +31,87 @@ class TestUi(HttpCaseWithUserDemo): self.assertEqual(intro_event_menu.view_id.website_meta_title, "Hello, world!") self.assertEqual(event.website_meta_title, False) + def test_website_event_questions(self): + """ Will execute the tour that fills up two tickets with a few questions answers + and then assert that the answers are correctly saved for each attendee. """ + + self.design_fair_event = self.env['event.event'].create({ + 'name': 'Design Fair New York', + 'date_begin': fields.Datetime.now() - relativedelta(days=15), + 'date_end': fields.Datetime.now() + relativedelta(days=15), + 'event_ticket_ids': [(0, 0, { + 'name': 'Free', + 'start_sale_datetime': fields.Datetime.now() - relativedelta(days=15) + }), (0, 0, { + 'name': 'Other', + 'start_sale_datetime': fields.Datetime.now() - relativedelta(days=15) + })], + 'website_published': True, + 'question_ids': [(0, 0, { + 'title': 'Meal Type', + 'question_type': 'simple_choice', + 'answer_ids': [ + (0, 0, {'name': 'Mixed'}), + (0, 0, {'name': 'Vegetarian'}), + (0, 0, {'name': 'Pastafarian'}) + ] + }), (0, 0, { + 'title': 'Allergies', + 'question_type': 'text_box' + }), (0, 0, { + 'title': 'How did you learn about this event?', + 'question_type': 'simple_choice', + 'once_per_order': True, + 'answer_ids': [ + (0, 0, {'name': 'Our website'}), + (0, 0, {'name': 'Commercials'}), + (0, 0, {'name': 'A friend'}) + ] + })] + }) + + self.start_tour("/", 'test_tickets_questions', login="portal") + + registrations = self.env['event.registration'].search([ + ('email', 'in', ['attendee-a@gmail.com', 'attendee-b@gmail.com']) + ]) + self.assertEqual(len(registrations), 2) + first_registration = registrations.filtered(lambda reg: reg.email == 'attendee-a@gmail.com') + second_registration = registrations.filtered(lambda reg: reg.email == 'attendee-b@gmail.com') + self.assertEqual(first_registration.name, 'Attendee A') + self.assertEqual(first_registration.phone, '+32499123456') + self.assertEqual(second_registration.name, 'Attendee B') + + event_questions = registrations.mapped('event_id.question_ids') + self.assertEqual(len(event_questions), 3) + + first_registration_answers = first_registration.registration_answer_ids + self.assertEqual(len(first_registration_answers), 3) + + self.assertEqual(first_registration_answers.filtered( + lambda answer: answer.question_id.title == 'Meal Type' + ).value_answer_id.name, 'Vegetarian') + + self.assertEqual(first_registration_answers.filtered( + lambda answer: answer.question_id.title == 'Allergies' + ).value_text_box, 'Fish and Nuts') + + self.assertEqual(first_registration_answers.filtered( + lambda answer: answer.question_id.title == 'How did you learn about this event?' + ).value_answer_id.name, 'A friend') + + second_registration_answers = second_registration.registration_answer_ids + self.assertEqual(len(second_registration_answers), 2) + + self.assertEqual(second_registration_answers.filtered( + lambda answer: answer.question_id.title == 'Meal Type' + ).value_answer_id.name, 'Pastafarian') + + self.assertEqual(first_registration_answers.filtered( + lambda answer: answer.question_id.title == 'How did you learn about this event?' + ).value_answer_id.name, 'A friend') + + @tagged('-at_install', 'post_install') class TestURLs(TestUrlCommon): diff --git a/addons/website_event/views/event_event_views.xml b/addons/website_event/views/event_event_views.xml index 76117732d6d..c5547adc2d5 100644 --- a/addons/website_event/views/event_event_views.xml +++ b/addons/website_event/views/event_event_views.xml @@ -28,6 +28,51 @@ + + + + + + + + + + + @@ -327,6 +347,24 @@ +