[MERGE][IMP] website_event_questions: allow 'free text' questions
PURPOSE Allow "free text" questions in the event questions asked to attendees, in addition to the existing "selection" type. e.g: "What are your allergies?" Rework existing reporting on Questions to ease implementation and give more meaningful answers analysis. SPECS **Free Text Questions** The implementation is heavily based on the modelisation we chose for the survey module since the features become similar in terms of question types and inputs. - Introduce a 'question_type' that is either 'simple_choice' (= previous behavior) or 'text_box' that will contain the attendee text answer. - Rename the event.answer model to event.question.answer to make it more obvious that it contains answer "suggestions" to a question of type 'simple_choice'. - Introduce a 'event.user_input.line' that is an answer to a specific question for a specific registration. It works very similarly to 'survey.user_input.line' but it's not contained in a 'user_input' model, it's directly linked to the registration using a 'user_input_line_ids' o2m field. The frontend view has been adapted accordingly to display either a selection input or a textarea based on the question type. The frontend orm processing now takes in both answer types (id of suggested answer or text input). On the backend side, views have been adapted to display event questions and attendee answers in a more convenient and logical manner. **Questions Reporting** Based on latest testing, the reporting on event questions was completely unusable if you have more than a hundred questions (all events combined) and needs to be refactored based on new specifications. We dropped the current reporting and re-introduced a new one after the commits that let us handled free text questions. The new reporting allow users to conveniently analyze attendees answers on a per-question basis. It shows: - A graph view showing counts of each suggestions for simple_choice questions - A pivot view showing registrations vs answers for simple_choice questions - A tree view showing textual answers values for text_box questions FIXIMP When changing the event.type, configured event questions that have answers (type 'simple_choice') new have their answers properly copied onto the event. LINKS Upgrade PR odoo/upgrade#799 Task ID 2189529 closes odoo/odoo#45497 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -3,5 +3,4 @@
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import report
|
||||
from . import tests
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
'name': 'Questions on Events',
|
||||
'description': 'Questions on Events',
|
||||
'category': 'Marketing',
|
||||
'version': '1.0',
|
||||
'version': '1.1',
|
||||
'depends': ['website_event'],
|
||||
'data': [
|
||||
'views/assets.xml',
|
||||
'views/event_views.xml',
|
||||
'views/event_registration_answer_views.xml',
|
||||
'views/event_registration_views.xml',
|
||||
'views/event_question_views.xml',
|
||||
'views/event_templates.xml',
|
||||
'report/report_event_question_view.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'demo': [
|
||||
|
||||
@@ -1,28 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.http import request
|
||||
|
||||
from odoo.addons.website_event.controllers.main import WebsiteEventController
|
||||
|
||||
|
||||
class WebsiteEvent(WebsiteEventController):
|
||||
|
||||
def _process_attendees_form(self, event, form_details):
|
||||
''' Process data posted from the attendee details form. '''
|
||||
""" 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. """
|
||||
registrations = super(WebsiteEvent, self)._process_attendees_form(event, form_details)
|
||||
|
||||
for registration in registrations:
|
||||
registration['answer_ids'] = []
|
||||
registration['registration_answer_ids'] = []
|
||||
|
||||
general_answer_ids = []
|
||||
for key, value in form_details.items():
|
||||
if 'answer_ids' in key:
|
||||
index = int(key.split('-')[0])
|
||||
if not index:
|
||||
general_answer_ids.append([4, int(value)])
|
||||
else:
|
||||
registrations[index-1]['answer_ids'].append([4, int(value)])
|
||||
if 'question_answer' in key and value:
|
||||
dummy, registration_index, question_id = key.split('-')
|
||||
question = request.env['event.question'].browse(int(question_id))
|
||||
answer_values = None
|
||||
if question.question_type == 'simple_choice':
|
||||
answer_values = {
|
||||
'question_id': int(question_id),
|
||||
'value_answer_id': int(value)
|
||||
}
|
||||
elif question.question_type == 'text_box':
|
||||
answer_values = {
|
||||
'question_id': int(question_id),
|
||||
'value_text_box': value
|
||||
}
|
||||
|
||||
if answer_values and not int(registration_index):
|
||||
general_answer_ids.append((0, 0, answer_values))
|
||||
elif answer_values:
|
||||
registrations[int(registration_index) - 1]['registration_answer_ids'].append((0, 0, answer_values))
|
||||
|
||||
for registration in registrations:
|
||||
registration['answer_ids'].extend(general_answer_ids)
|
||||
registration['registration_answer_ids'].extend(general_answer_ids)
|
||||
|
||||
return registrations
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<field name="once_per_order" eval="False"/>
|
||||
<field name="event_type_id" ref="event.event_type_data_conference"/>
|
||||
</record>
|
||||
<record id="event_type_data_conference_question_0_answer_0" model="event.answer">
|
||||
<record id="event_type_data_conference_question_0_answer_0" model="event.question.answer">
|
||||
<field name="name">Yes</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="question_id" ref="website_event_questions.event_type_data_conference_question_0"/>
|
||||
</record>
|
||||
<record id="event_type_data_conference_question_0_answer_1" model="event.answer">
|
||||
<record id="event_type_data_conference_question_0_answer_1" model="event.question.answer">
|
||||
<field name="name">No</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="question_id" ref="website_event_questions.event_type_data_conference_question_0"/>
|
||||
@@ -20,22 +20,50 @@
|
||||
<!-- EVENT SPECIFIC -->
|
||||
<record id="event_0_question_0" model="event.question">
|
||||
<field name="title">Meal Type</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="once_per_order" eval="False"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
</record>
|
||||
<record id="event_0_question_0_answer_0" model="event.answer">
|
||||
<record id="event_0_question_0_answer_0" model="event.question.answer">
|
||||
<field name="name">Mixed</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_0"/>
|
||||
</record>
|
||||
<record id="event_0_question_0_answer_1" model="event.answer">
|
||||
<record id="event_0_question_0_answer_1" model="event.question.answer">
|
||||
<field name="name">Vegetarian</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_0"/>
|
||||
</record>
|
||||
<record id="event_0_question_0_answer_2" model="event.answer">
|
||||
<record id="event_0_question_0_answer_2" model="event.question.answer">
|
||||
<field name="name">Pastafarian</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_0"/>
|
||||
</record>
|
||||
<record id="event_0_question_1" model="event.question">
|
||||
<field name="title">Allergies</field>
|
||||
<field name="question_type">text_box</field>
|
||||
<field name="once_per_order" eval="False"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
</record>
|
||||
<record id="event_0_question_2" model="event.question">
|
||||
<field name="title">How did you learn about this event?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="once_per_order" eval="True"/>
|
||||
<field name="event_id" ref="event.event_0"/>
|
||||
</record>
|
||||
<record id="event_0_question_2_answer_0" model="event.question.answer">
|
||||
<field name="name">Our website</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_2"/>
|
||||
</record>
|
||||
<record id="event_0_question_2_answer_1" model="event.question.answer">
|
||||
<field name="name">Commercials</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_2"/>
|
||||
</record>
|
||||
<record id="event_0_question_2_answer_2" model="event.question.answer">
|
||||
<field name="name">A friend</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_2"/>
|
||||
</record>
|
||||
</data></odoo>
|
||||
|
||||
@@ -1,18 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data>
|
||||
<record id="event.event_registration_0_0" model="event.registration">
|
||||
<field name="answer_ids" eval="[
|
||||
(5, 0),
|
||||
(4, ref('website_event_questions.event_0_question_0_answer_0'))]"/>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="event_registration_0_0_registration_answer_0" model="event.registration.answer">
|
||||
<field name="value_answer_id" ref="website_event_questions.event_0_question_0_answer_0" />
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_0" />
|
||||
<field name="registration_id" ref="event.event_registration_0_0" />
|
||||
</record>
|
||||
<record id="event.event_registration_0_1" model="event.registration">
|
||||
<field name="answer_ids" eval="[
|
||||
(5, 0),
|
||||
(4, ref('website_event_questions.event_0_question_0_answer_1'))]"/>
|
||||
<record id="event_registration_0_0_registration_answer_1" model="event.registration.answer">
|
||||
<field name="value_text_box">Fish
|
||||
Nuts</field>
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_1" />
|
||||
<field name="registration_id" ref="event.event_registration_0_0" />
|
||||
</record>
|
||||
<record id="event.event_registration_0_2" model="event.registration">
|
||||
<field name="answer_ids" eval="[
|
||||
(5, 0),
|
||||
(4, ref('website_event_questions.event_0_question_0_answer_2'))]"/>
|
||||
<record id="event_registration_0_0_registration_answer_2" model="event.registration.answer">
|
||||
<field name="value_answer_id" ref="website_event_questions.event_0_question_2_answer_0" />
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_2" />
|
||||
<field name="registration_id" ref="event.event_registration_0_0" />
|
||||
</record>
|
||||
</data></odoo>
|
||||
<record id="event_registration_0_1_registration_answer_0" model="event.registration.answer">
|
||||
<field name="value_answer_id" ref="website_event_questions.event_0_question_0_answer_1" />
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_0" />
|
||||
<field name="registration_id" ref="event.event_registration_0_1" />
|
||||
</record>
|
||||
<record id="event_registration_0_1_registration_answer_1" model="event.registration.answer">
|
||||
<field name="value_answer_id" ref="website_event_questions.event_0_question_2_answer_0" />
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_2" />
|
||||
<field name="registration_id" ref="event.event_registration_0_1" />
|
||||
</record>
|
||||
<record id="event_registration_0_2_registration_answer_0" model="event.registration.answer">
|
||||
<field name="value_answer_id" ref="website_event_questions.event_0_question_0_answer_2" />
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_0" />
|
||||
<field name="registration_id" ref="event.event_registration_0_2" />
|
||||
</record>
|
||||
<record id="event_registration_0_2_registration_answer_1" model="event.registration.answer">
|
||||
<field name="value_answer_id" ref="website_event_questions.event_0_question_2_answer_2" />
|
||||
<field name="question_id" ref="website_event_questions.event_0_question_2" />
|
||||
<field name="registration_id" ref="event.event_registration_0_2" />
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -33,8 +33,13 @@ class EventEvent(models.Model):
|
||||
event.question_ids = [(5, 0, 0)] + [
|
||||
(0, 0, {
|
||||
'title': question.title,
|
||||
'question_type': question.question_type,
|
||||
'sequence': question.sequence,
|
||||
'once_per_order': question.once_per_order,
|
||||
'answer_ids': [(0, 0, {
|
||||
'name': answer.name,
|
||||
'sequence': answer.sequence
|
||||
}) for answer in question.answer_ids],
|
||||
})
|
||||
for question in event.event_type_id.question_ids
|
||||
]
|
||||
|
||||
@@ -12,9 +12,12 @@ class EventQuestion(models.Model):
|
||||
_description = 'Event Question'
|
||||
|
||||
title = fields.Char(required=True, translate=True)
|
||||
question_type = fields.Selection([
|
||||
('simple_choice', 'Selection'),
|
||||
('text_box', 'Text Input')], default='simple_choice', string="Question Type", required=True)
|
||||
event_type_id = fields.Many2one('event.type', 'Event Type', ondelete='cascade')
|
||||
event_id = fields.Many2one('event.event', 'Event', ondelete='cascade')
|
||||
answer_ids = fields.One2many('event.answer', 'question_id', "Answers", required=True, copy=True)
|
||||
answer_ids = fields.One2many('event.question.answer', 'question_id', "Answers", copy=True)
|
||||
sequence = fields.Integer(default=10)
|
||||
once_per_order = fields.Boolean('Ask only once per order',
|
||||
help="If True, this question will be asked only once and its value will be propagated to every attendees."
|
||||
@@ -25,23 +28,37 @@ class EventQuestion(models.Model):
|
||||
if any(question.event_type_id and question.event_id for question in self):
|
||||
raise UserError(_('Question cannot belong to both the event category and itself.'))
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
event_id = vals.get('event_id', False)
|
||||
if event_id:
|
||||
event = self.env['event.event'].browse([event_id])
|
||||
if event.event_type_id.use_questions and event.event_type_id.question_ids:
|
||||
vals['answer_ids'] = vals.get('answer_ids', []) + [(0, 0, {
|
||||
'name': answer.name,
|
||||
'sequence': answer.sequence,
|
||||
}) for answer in event.event_type_id.question_ids.filtered(lambda question: question.title == vals.get('title')).mapped('answer_ids')]
|
||||
return super(EventQuestion, self).create(vals)
|
||||
def write(self, vals):
|
||||
""" We add a check to prevent changing the question_type of a question that already has answers.
|
||||
Indeed, it would mess up the event.registration.answer (answer type not matching the question type). """
|
||||
|
||||
if 'question_type' in vals:
|
||||
questions_new_type = self.filtered(lambda question: question.question_type != vals['question_type'])
|
||||
if questions_new_type:
|
||||
answer_count = self.env['event.registration.answer'].search_count([('question_id', 'in', questions_new_type.ids)])
|
||||
if answer_count > 0:
|
||||
raise UserError(_("You cannot change the question type of a question that already has answers!"))
|
||||
return super(EventQuestion, self).write(vals)
|
||||
|
||||
class EventAnswer(models.Model):
|
||||
_name = 'event.answer'
|
||||
def action_view_question_answers(self):
|
||||
""" Allow analyzing the attendees answers to event questions in a convenient way:
|
||||
- A graph view showing counts of each suggestions for simple_choice questions
|
||||
(Along with secondary pivot and tree views)
|
||||
- A tree view showing textual answers values for text_box questions. """
|
||||
self.ensure_one()
|
||||
action = self.env.ref('website_event_questions.action_event_registration_report').read()[0]
|
||||
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 Answer'
|
||||
_description = 'Event Question Answer'
|
||||
|
||||
name = fields.Char('Answer', required=True, translate=True)
|
||||
question_id = fields.Many2one('event.question', required=True, ondelete='cascade')
|
||||
|
||||
@@ -4,22 +4,25 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class EventRegistrationAnswer(models.Model):
|
||||
''' This m2m table has to be explicitly instanciated as we need unique ids
|
||||
in the reporting view event.question.report.
|
||||
|
||||
This model is purely technical. '''
|
||||
|
||||
_name = 'event.registration.answer'
|
||||
_table = 'event_registration_answer'
|
||||
_description = 'Event Registration Answer'
|
||||
|
||||
event_answer_id = fields.Many2one('event.answer', required=True, ondelete='cascade')
|
||||
event_registration_id = fields.Many2one('event.registration', required=True, ondelete='cascade')
|
||||
|
||||
|
||||
class EventRegistration(models.Model):
|
||||
""" Store answers on attendees. """
|
||||
_inherit = 'event.registration'
|
||||
|
||||
answer_ids = fields.Many2many('event.answer', 'event_registration_answer', string='Answers')
|
||||
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'
|
||||
_description = 'Event Registration Answer'
|
||||
|
||||
question_id = fields.Many2one('event.question', required=True, ondelete='cascade')
|
||||
registration_id = fields.Many2one('event.registration', required=True, ondelete='cascade')
|
||||
partner_id = fields.Many2one('res.partner', related='registration_id.partner_id')
|
||||
event_id = fields.Many2one('event.event', related='registration_id.event_id')
|
||||
question_type = fields.Selection(related='question_id.question_type')
|
||||
value_answer_id = fields.Many2one('event.question.answer', string="Suggested answer")
|
||||
value_text_box = fields.Text('Text answer')
|
||||
|
||||
_sql_constraints = [
|
||||
('value_check', "CHECK(value_answer_id IS NOT NULL OR COALESCE(value_text_box, '') <> '')", "There must be a suggested value or a text value.")
|
||||
]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import report_event_registrations_questions
|
||||
@@ -1,63 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- Graph view for question analysis-->
|
||||
<record model="ir.ui.view" id="view_event_question_report_graph">
|
||||
<field name="name">event.question.report.graph</field>
|
||||
<field name="model">event.question.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Questions Analysis">
|
||||
<field name="answer_id"/>
|
||||
<field name="question_id"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Pivot view for question analysis -->
|
||||
<record model="ir.ui.view" id="view_event_question_report_pivot">
|
||||
<field name="name">event.question.report.pivot</field>
|
||||
<field name="model">event.question.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Questions Analysis" disable_linking="True">
|
||||
<field name="event_id" type="row"/>
|
||||
<field name="attendee_id" type="row"/>
|
||||
<field name="question_id" type="col"/>
|
||||
<field name="answer_id" type="col"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Seach view for question analysis-->
|
||||
<record model="ir.ui.view" id="view_event_question_report_search">
|
||||
<field name="name">event.question.report.search</field>
|
||||
<field name="model">event.question.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Questions Analysis">
|
||||
<field name="event_id" string="Event"/>
|
||||
<field name="attendee_id" string="Registration"/>
|
||||
<group expand="1" string="Group By">
|
||||
<filter string="Event" name="group_by_event" context="{'group_by': 'event_id'}" />
|
||||
<filter string="Question" name="group_by_question" context="{'group_by': 'question_id'}"/>
|
||||
<filter string="Answer" name="group_by_answer" context="{'group_by': 'answer_id'}" />
|
||||
<filter name="exclude_cancelled"
|
||||
string="Exclude cancelled registrations"
|
||||
domain="[('attendee_id.state', '!=', 'cancel')]" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action for reporting -->
|
||||
<record model="ir.actions.act_window" id="action_event_question_report">
|
||||
<field name="name">Questions Analysis</field>
|
||||
<field name="res_model">event.question.report</field>
|
||||
<field name="view_mode">graph,pivot</field>
|
||||
</record>
|
||||
|
||||
<!-- Menu -->
|
||||
<menuitem name="Questions" id="menu_report_event_questions"
|
||||
parent="event.menu_reporting_events" action="action_event_question_report" sequence="4"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,39 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, tools
|
||||
|
||||
|
||||
class ReportEventRegistrationQuestions(models.Model):
|
||||
_name = "event.question.report"
|
||||
_auto = False
|
||||
_description = 'Event Question Report'
|
||||
|
||||
attendee_id = fields.Many2one(comodel_name='event.registration', string='Registration')
|
||||
question_id = fields.Many2one(comodel_name='event.question', string='Question')
|
||||
answer_id = fields.Many2one(comodel_name='event.answer', string='Answer')
|
||||
event_id = fields.Many2one(comodel_name='event.event', string='Event')
|
||||
|
||||
def init(self):
|
||||
""" Event Question main report """
|
||||
tools.drop_view_if_exists(self._cr, 'event_question_report')
|
||||
self._cr.execute(""" CREATE VIEW event_question_report AS (
|
||||
SELECT
|
||||
att_answer.id as id,
|
||||
att_answer.event_registration_id as attendee_id,
|
||||
answer.question_id as question_id,
|
||||
answer.id as answer_id,
|
||||
question.event_id as event_id
|
||||
FROM
|
||||
event_registration_answer as att_answer
|
||||
LEFT JOIN
|
||||
event_answer as answer ON answer.id = att_answer.event_answer_id
|
||||
LEFT JOIN
|
||||
event_question as question ON question.id = answer.question_id
|
||||
GROUP BY
|
||||
attendee_id,
|
||||
event_id,
|
||||
question_id,
|
||||
answer_id,
|
||||
att_answer.id
|
||||
)""")
|
||||
@@ -1,7 +1,6 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
event_question_all,event.question.all,model_event_question,,1,0,0,0
|
||||
event_question_event_user,event.question.event.user,model_event_question,event.group_event_user,1,1,1,1
|
||||
event_answer_all,event.answer.all,model_event_answer,,1,0,0,0
|
||||
event_answer_event_user,event.answer.event.user,model_event_answer,event.group_event_user,1,1,1,1
|
||||
event_question_report_all,event.question.report.all,model_event_question_report,event.group_event_user,1,1,1,1
|
||||
event_registration_answer_all,access_event_registration_answer,model_event_registration_answer,,1,1,1,1
|
||||
event_question_answer_all,event.question.answer.all,model_event_question_answer,,1,0,0,0
|
||||
event_question_answer_event_user,event.question.answer.event.user,model_event_question_answer,event.group_event_user,1,1,1,1
|
||||
event_registration_answer_event_user,event.registration.answer.event.user,model_event_registration_answer,event.group_event_user,1,1,1,1
|
||||
|
||||
|
@@ -0,0 +1,54 @@
|
||||
odoo.define('website_event_questions.tour_test_tickets_questions', function (require) {
|
||||
'use strict';
|
||||
|
||||
var tour = require('web_tour.tour');
|
||||
tour.register('test_tickets_questions', {
|
||||
test: true,
|
||||
url: '/event'
|
||||
}, [{
|
||||
content: "Click on the Design Fair event",
|
||||
trigger: 'article:contains("Design Fair New York")',
|
||||
}, {
|
||||
content: "Click on Buy tickets button",
|
||||
trigger: 'a:contains("Tickets")',
|
||||
}, {
|
||||
content: "Select 2 'Free' tickets to buy",
|
||||
trigger: 'div.row:contains("Free") select.custom-select',
|
||||
run: 'text 2'
|
||||
}, {
|
||||
content: "Click on Register tickets button",
|
||||
trigger: 'button:contains("Register")',
|
||||
run: 'click'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #1").modal-body input[name*="name"]',
|
||||
run: 'text Attendee A'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #1").modal-body input[name*="email"]',
|
||||
run: 'text attendee-a@gmail.com'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #1").modal-body input[name*="phone"]',
|
||||
run: 'text +32499123456'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #1").modal-body select[name*="question_answer"]',
|
||||
run: 'text Vegetarian'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #1").modal-body textarea[name*="question_answer"]',
|
||||
run: 'text Fish and Nuts'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #2").modal-body input[name*="name"]',
|
||||
run: 'text Attendee B'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #2").modal-body input[name*="email"]',
|
||||
run: 'text attendee-b@gmail.com'
|
||||
}, {
|
||||
trigger: 'div:contains("Ticket #2").modal-body select[name*="question_answer"]',
|
||||
run: 'text Pastafarian'
|
||||
}, {
|
||||
trigger: 'div:contains("General questions").modal-body select[name*="question_answer"]',
|
||||
run: 'text A friend'
|
||||
}, {
|
||||
trigger: 'button:contains("Continue")',
|
||||
run: 'click'
|
||||
}]);
|
||||
|
||||
});
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
from . import common
|
||||
from . import test_event_internals
|
||||
from . import test_event_ui
|
||||
|
||||
@@ -12,6 +12,7 @@ class TestEventQuestionCommon(TestEventCommon):
|
||||
|
||||
cls.event_question_1 = cls.env['event.question'].create({
|
||||
'title': 'Question1',
|
||||
'question_type': 'simple_choice',
|
||||
'event_type_id': cls.event_type_complex.id,
|
||||
'once_per_order': False,
|
||||
'answer_ids': [
|
||||
@@ -21,6 +22,7 @@ class TestEventQuestionCommon(TestEventCommon):
|
||||
})
|
||||
cls.event_question_2 = cls.env['event.question'].create({
|
||||
'title': 'Question2',
|
||||
'question_type': 'simple_choice',
|
||||
'event_type_id': cls.event_type_complex.id,
|
||||
'once_per_order': True,
|
||||
'answer_ids': [
|
||||
@@ -28,4 +30,10 @@ class TestEventQuestionCommon(TestEventCommon):
|
||||
(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_complex.id,
|
||||
'once_per_order': True,
|
||||
})
|
||||
cls.event_type_complex.write({'use_questions': True})
|
||||
|
||||
@@ -23,13 +23,18 @@ class TestEventData(TestEventQuestionCommon):
|
||||
'date_end': FieldsDatetime.to_string(datetime.today() + timedelta(days=15)),
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
event.question_ids.mapped('question_type'),
|
||||
['simple_choice', 'simple_choice', 'text_box'])
|
||||
self.assertEqual(event.specific_question_ids.title, 'Question1')
|
||||
self.assertEqual(
|
||||
set(event.specific_question_ids.mapped('answer_ids.name')),
|
||||
set(['Q1-Answer1', 'Q1-Answer2']))
|
||||
self.assertEqual(event.general_question_ids.title, 'Question2')
|
||||
self.assertEqual(len(event.general_question_ids), 2)
|
||||
self.assertEqual(event.general_question_ids[0].title, 'Question2')
|
||||
self.assertEqual(event.general_question_ids[1].title, 'Question3')
|
||||
self.assertEqual(
|
||||
set(event.general_question_ids.mapped('answer_ids.name')),
|
||||
set(event.general_question_ids[0].mapped('answer_ids.name')),
|
||||
set(['Q2-Answer1', 'Q2-Answer2']))
|
||||
|
||||
def test_process_attendees_form(self):
|
||||
@@ -45,20 +50,28 @@ class TestEventData(TestEventQuestionCommon):
|
||||
'1-email': 'pixis@gmail.com',
|
||||
'1-phone': '+32444444444',
|
||||
'1-event_ticket_id': '2',
|
||||
'1-answer_ids-8': '5',
|
||||
'2-name': 'Geluchat',
|
||||
'2-email': 'geluchat@gmail.com',
|
||||
'2-phone': '+32777777777',
|
||||
'2-event_ticket_id': '3',
|
||||
'2-answer_ids-8': '9',
|
||||
'0-answer_ids-3': '7',
|
||||
'0-answer_ids-4': '1',
|
||||
'question_answer-1-%s' % self.event_question_1.id: '5',
|
||||
'question_answer-2-%s' % self.event_question_1.id: '9',
|
||||
'question_answer-0-%s' % self.event_question_2.id: '7',
|
||||
'question_answer-0-%s' % self.event_question_3.id: 'Free Text',
|
||||
}
|
||||
|
||||
with MockRequest(self.env):
|
||||
registrations = WebsiteEvent()._process_attendees_form(event, form_details)
|
||||
|
||||
self.assertEqual(registrations, [
|
||||
{'name': 'Pixis', 'email': 'pixis@gmail.com', 'phone': '+32444444444', 'event_ticket_id': 2, 'answer_ids': [[4, 5], [4, 7], [4, 1]]},
|
||||
{'name': 'Geluchat', 'email': 'geluchat@gmail.com', 'phone': '+32777777777', 'event_ticket_id': 3, 'answer_ids': [[4, 9], [4, 7], [4, 1]]}
|
||||
{'name': 'Pixis', 'email': 'pixis@gmail.com', 'phone': '+32444444444', 'event_ticket_id': 2,
|
||||
'registration_answer_ids': [
|
||||
(0, 0, {'question_id': self.event_question_1.id, 'value_answer_id': 5}),
|
||||
(0, 0, {'question_id': self.event_question_2.id, 'value_answer_id': 7}),
|
||||
(0, 0, {'question_id': self.event_question_3.id, 'value_text_box': 'Free Text'})]},
|
||||
{'name': 'Geluchat', 'email': 'geluchat@gmail.com', 'phone': '+32777777777', 'event_ticket_id': 3,
|
||||
'registration_answer_ids': [
|
||||
(0, 0, {'question_id': self.event_question_1.id, 'value_answer_id': 9}),
|
||||
(0, 0, {'question_id': self.event_question_2.id, 'value_answer_id': 7}),
|
||||
(0, 0, {'question_id': self.event_question_3.id, 'value_text_box': 'Free Text'})]}
|
||||
])
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import fields, tests
|
||||
|
||||
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestUi(tests.HttpCase):
|
||||
|
||||
def test_01_tickets_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_date': fields.Datetime.now() - relativedelta(days=15)
|
||||
}), (0, 0, {
|
||||
'name': 'Other',
|
||||
'start_sale_date': 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')
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="assets_tests" name="Website Event Questions Assets Tests" inherit_id="web.assets_tests">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_event_questions/static/tests/tours/tickets_questions.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="event_question_view_form" model="ir.ui.view">
|
||||
<field name="name">event.question.view.form</field>
|
||||
<field name="model">event.question</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Question">
|
||||
<sheet>
|
||||
<h1><field name="title" /></h1>
|
||||
<group>
|
||||
<group>
|
||||
<div colspan="2">
|
||||
<field name="once_per_order"/>
|
||||
<label for="once_per_order"/>
|
||||
</div>
|
||||
<field name="question_type" widget="radio" options="{'horizontal': true}" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook attrs="{'invisible': [('question_type', '!=', 'simple_choice')]}">
|
||||
<page string="Answers" name="answers">
|
||||
<field name="answer_ids">
|
||||
<tree editable="bottom">
|
||||
<!-- 'display_name' is necessary for the many2many_tags to work on the event view -->
|
||||
<field name="display_name" invisible="1" />
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="event_registration_answer_view_search" model="ir.ui.view">
|
||||
<field name="name">event.registration.answer.view.search</field>
|
||||
<field name="model">event.registration.answer</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="value_text_box" />
|
||||
<field name="value_answer_id" />
|
||||
<field name="question_id" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_registration_answer_view_tree" model="ir.ui.view">
|
||||
<field name="name">event.registration.answer.view.tree</field>
|
||||
<field name="model">event.registration.answer</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Answer Breakdown" create="0">
|
||||
<field name="registration_id" optional="show" />
|
||||
<field name="partner_id" optional="hide" />
|
||||
<field name="question_id" optional="show" />
|
||||
<field name="value_text_box" />
|
||||
<field name="value_answer_id" string="Selected answer" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_registration_answer_view_graph" model="ir.ui.view">
|
||||
<field name="name">event.registration.answer.view.graph</field>
|
||||
<field name="model">event.registration.answer</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Answer Breakdown">
|
||||
<field name="value_answer_id" />
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_registration_answer_view_pivot" model="ir.ui.view">
|
||||
<field name="name">event.registration.answer.view.pivot</field>
|
||||
<field name="model">event.registration.answer</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Answer Breakdown">
|
||||
<field name="registration_id" type="row"/>
|
||||
<field name="value_answer_id" type="col"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_event_registration_report" model="ir.actions.act_window">
|
||||
<field name="name">Answer Breakdown</field>
|
||||
<field name="res_model">event.registration.answer</field>
|
||||
<field name="view_mode">search,tree,graph,pivot</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="event_registration_view_form_inherit_question" model="ir.ui.view">
|
||||
<field name="name">event.registration.view.form.inherit.question</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_event_registration_form" />
|
||||
<field name="arch" type="xml">
|
||||
<sheet position="inside">
|
||||
<notebook>
|
||||
<page string="Questions" name="questions">
|
||||
<field name="registration_answer_ids" widget="one2many">
|
||||
<tree editable="bottom">
|
||||
<field name="event_id" invisible="1" />
|
||||
<field name="question_id" domain="[('event_id', '=', event_id)]" options="{'no_create': True}" />
|
||||
<field name="question_type" string="Type" />
|
||||
<field name="value_answer_id"
|
||||
attrs="{'invisible': [('question_type', '!=', 'simple_choice')]}"
|
||||
domain="[('question_id', '=', question_id)]" options="{'no_create': True}"/>
|
||||
<field name="value_text_box" attrs="{'invisible': [('question_type', '!=', 'text_box')]}" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -7,12 +7,9 @@
|
||||
<t t-if="event.specific_question_ids">
|
||||
<div t-foreach="event.specific_question_ids" t-as="question" class="row">
|
||||
<div class="col-lg-6 mt-2">
|
||||
<label t-esc="question.title"/>
|
||||
<select t-attf-name="#{counter}-answer_ids-#{question.id}" class="custom-select" required="true">
|
||||
<t t-foreach="question.answer_ids" t-as="answer">
|
||||
<option t-esc="answer.name" t-att-value="answer.id"/>
|
||||
</t>
|
||||
</select>
|
||||
<t t-call="website_event_questions.registration_event_question">
|
||||
<t t-set="registration_index" t-value="counter"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
@@ -21,14 +18,12 @@
|
||||
<xpath expr="//*[hasclass('modal-footer')]" position="before">
|
||||
<t t-if="event.general_question_ids">
|
||||
<div class="modal-body bg-light border-bottom">
|
||||
<h5>General questions</h5>
|
||||
<div t-foreach="event.general_question_ids" t-as="question" class="row">
|
||||
<div class="col-lg-6">
|
||||
<label class="h5" t-esc="question.title"/>
|
||||
<select t-attf-name="0-answer_ids-#{question.id}" class="custom-select" required="true">
|
||||
<t t-foreach="question.answer_ids" t-as="answer">
|
||||
<option t-esc="answer.name" t-att-value="answer.id"/>
|
||||
</t>
|
||||
</select>
|
||||
<div class="col-lg-6 mt-2">
|
||||
<t t-call="website_event_questions.registration_event_question">
|
||||
<t t-set="registration_index" t-value="0"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,4 +31,18 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="registration_event_question" name="Registration Event Question">
|
||||
<label t-esc="question.title"/>
|
||||
<t t-if="question.question_type == 'simple_choice'">
|
||||
<select t-attf-name="question_answer-#{registration_index}-#{question.id}" class="custom-select" required="true">
|
||||
<t t-foreach="question.answer_ids" t-as="answer">
|
||||
<option t-esc="answer.name" t-att-value="answer.id"/>
|
||||
</t>
|
||||
</select>
|
||||
</t>
|
||||
<t t-elif="question.question_type == 'text_box'">
|
||||
<textarea t-attf-name="question_answer-#{registration_index}-#{question.id}" class="col-lg-12 form-control"/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,101 +1,84 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_question_form">
|
||||
<field name="name">event.question.form</field>
|
||||
<field name="model">event.question</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Question">
|
||||
<h1><field name="title" /></h1>
|
||||
<field name="once_per_order"/>
|
||||
<label for="once_per_order"/>
|
||||
<field name="answer_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_answer_simplified_form">
|
||||
<field name="name">event.answer.simplified.form</field>
|
||||
<field name="model">event.answer</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Question">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="event_type_view_form_inherit_question" model="ir.ui.view">
|
||||
<field name="name">event.type.view.form.inherit.question</field>
|
||||
<field name="model">event.type</field>
|
||||
<field name="inherit_id" ref="website_event.event_type_view_form_inherit_website"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name="event_type_attendees_auto_confirm" position="after">
|
||||
<div class="col-12 col-lg-12 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="use_questions"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="use_questions"/>
|
||||
<div class="row mt16" attrs="{'invisible': [('use_questions', '=', False)]}">
|
||||
<div class="col-lg-9">
|
||||
<field name="question_ids"/>
|
||||
<record id="event_type_view_form_inherit_question" model="ir.ui.view">
|
||||
<field name="name">event.type.view.form.inherit.question</field>
|
||||
<field name="model">event.type</field>
|
||||
<field name="inherit_id" ref="website_event.event_type_view_form_inherit_website"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name="event_type_attendees_auto_confirm" position="after">
|
||||
<div class="col-12 col-lg-12 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="use_questions"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="use_questions"/>
|
||||
<div class="row mt16" attrs="{'invisible': [('use_questions', '=', False)]}">
|
||||
<div class="col-lg-9">
|
||||
<field name="question_ids">
|
||||
<tree>
|
||||
<field name="title"/>
|
||||
<field name="question_type" />
|
||||
</tree>
|
||||
</field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_form_inherit_question">
|
||||
<field name="name">event.event.view.form.inherit.question</field>
|
||||
<field name="model">event.event</field>
|
||||
<field name="inherit_id" ref="website_event.event_event_view_form_inherit_website"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<page name="event_notes" position="before">
|
||||
<page string="Questions" name="questions">
|
||||
<field name="question_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="title"/>
|
||||
<field name="once_per_order"/>
|
||||
<field name="answer_ids"/>
|
||||
</tree>
|
||||
</field>
|
||||
<record id="event_event_view_form_inherit_question" model="ir.ui.view">
|
||||
<field name="name">event.event.view.form.inherit.question</field>
|
||||
<field name="model">event.event</field>
|
||||
<field name="inherit_id" ref="website_event.event_event_view_form_inherit_website"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<page name="event_notes" position="before">
|
||||
<page string="Questions" name="questions">
|
||||
<field name="question_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="title"/>
|
||||
<field name="once_per_order"/>
|
||||
<field name="question_type" string="Type" />
|
||||
<field name="answer_ids" widget="many2many_tags"
|
||||
attrs="{'invisible': [('question_type', '!=', 'simple_choice')]}" />
|
||||
<button name="action_view_question_answers" type="object" class="fa fa-bar-chart p-0" title="Answer Breakdown" />
|
||||
</tree>
|
||||
<!-- Need to repeat the whole tree form here to be able to create answers properly
|
||||
Without this, the sub-fields of answer_ids are unknown to the web framework.
|
||||
We need this because we create questions and answers when the event type changes. -->
|
||||
<form string="Question">
|
||||
<sheet>
|
||||
<h1><field name="title" /></h1>
|
||||
<group class="mb-0">
|
||||
<group class="mb-0">
|
||||
<div colspan="2">
|
||||
<field name="once_per_order"/>
|
||||
<label for="once_per_order"/>
|
||||
</div>
|
||||
<field name="question_type" widget="radio" options="{'horizontal': true}" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook attrs="{'invisible': [('question_type', '!=', 'simple_choice')]}">
|
||||
<page string="Answers" name="answers">
|
||||
<field name="answer_ids">
|
||||
<tree editable="bottom">
|
||||
<!-- 'display_name' is necessary for the many2many_tags to work on the event view -->
|
||||
<field name="display_name" invisible="1" />
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
</page>
|
||||
</page>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_registration_form_inherit_question">
|
||||
<field name="name">event.registration.form.inherit.question</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_event_registration_form" />
|
||||
<field name="arch" type="xml">
|
||||
<group name="event" position="inside">
|
||||
<field name="answer_ids" widget="many2many_tags" domain="[('question_id.event_id', '=', event_id)]" options="{'no_create_edit': True}"/>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_registration_search_inherit_question">
|
||||
<field name="name">event.registration.search.inherit.question</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="inherit_id" ref="event.view_registration_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<search position="inside">
|
||||
<field name="answer_ids" string="Answers"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user