94 lines
4.7 KiB
Python
94 lines
4.7 KiB
Python
# -*- 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 ValidationError
|
|
|
|
|
|
class AppointmentQuestion(models.Model):
|
|
_name = 'appointment.question'
|
|
_description = "Appointment Questions"
|
|
_order = "sequence,id"
|
|
|
|
active = fields.Boolean('Active', default=True)
|
|
sequence = fields.Integer('Sequence')
|
|
appointment_type_ids = fields.Many2many('appointment.type', relation='appointment_type_appointment_question_rel', string='Appointment Types')
|
|
appointment_count = fields.Integer('# Appointments', compute='_compute_appointment_count')
|
|
is_default = fields.Boolean('Default question', help="Include by default in new appointment types.")
|
|
is_reusable = fields.Boolean('Is Reusable',
|
|
compute='_compute_is_reusable', default=True, store=True, readonly=False,
|
|
help="Will appear in the list of available questions when adding one in any appointment. Always true for default questions.")
|
|
name = fields.Char('Question', translate=True, required=True)
|
|
placeholder = fields.Char('Placeholder', translate=True)
|
|
question_required = fields.Boolean('Mandatory Answer')
|
|
question_type = fields.Selection([
|
|
('char', 'Single line text'),
|
|
('text', 'Multi-line text'),
|
|
('phone', 'Phone Number'),
|
|
('select', 'Dropdown (one answer)'),
|
|
('radio', 'Radio (one answer)'),
|
|
('checkbox', 'Checkboxes (multiple answers)')], 'Answer Type', default='char', required=True)
|
|
answer_ids = fields.One2many('appointment.answer', 'question_id', string='Available Answers', copy=True)
|
|
answer_input_ids = fields.One2many('appointment.answer.input', 'question_id', string='Submitted Answers')
|
|
extra_comment = fields.Html('Extra Comment', translate=True,
|
|
help="This will appear below the question in the appointment form.")
|
|
|
|
_check_default_question_is_reusable = models.Constraint(
|
|
'CHECK(is_default IS DISTINCT FROM TRUE OR is_reusable IS TRUE)',
|
|
"A default question must be reusable."
|
|
)
|
|
|
|
@api.constrains('question_type', 'answer_ids')
|
|
def _check_question_type(self):
|
|
incomplete_questions = self.filtered(lambda question: question.question_type in ['select', 'radio', 'checkbox'] and not question.answer_ids)
|
|
if incomplete_questions:
|
|
raise ValidationError(
|
|
_('The following question(s) do not have any selectable answers : %s',
|
|
', '.join(incomplete_questions.mapped('name'))
|
|
)
|
|
)
|
|
|
|
@api.depends('appointment_type_ids')
|
|
def _compute_appointment_count(self):
|
|
appointment_data = self.env["appointment.type"]._read_group(
|
|
[('question_ids', 'in', self.ids)],
|
|
['question_ids'],
|
|
['__count']
|
|
)
|
|
mapped_data = {appointment_question.id: count for appointment_question, count in appointment_data}
|
|
for question in self:
|
|
if not question.id: # new record
|
|
question.appointment_count = len(question.appointment_type_ids)
|
|
else:
|
|
question.appointment_count = mapped_data.get(question.id, 0)
|
|
|
|
@api.depends('is_default')
|
|
def _compute_is_reusable(self):
|
|
for question in self:
|
|
if question.is_default:
|
|
question.is_reusable = True
|
|
|
|
def action_view_question_answer_inputs(self):
|
|
""" Allow analyzing the answers to a question on an appointment in a convenient way:
|
|
- A graph view showing counts of each suggested answers for multiple-choice questions:
|
|
select / radio / checkbox. (Along with secondary pivot and list views)
|
|
- A list view showing textual answers values for char / text questions"""
|
|
self.ensure_one()
|
|
action = self.env["ir.actions.actions"]._for_xml_id("appointment.appointment_answer_input_action")
|
|
if self.question_type in ['select', 'radio', 'checkbox']:
|
|
action['views'] = [(False, 'pivot'), (False, 'graph'), (False, 'list'), (False, 'form')]
|
|
elif self.question_type in ['char', 'text', 'phone']:
|
|
action['views'] = [(False, 'list'), (False, 'form')]
|
|
action['context'] = {
|
|
'create': False,
|
|
'search_default_question_id': self.id,
|
|
}
|
|
if appointment_id := self.env.context.get('search_default_appointment_type_id'):
|
|
action['context'].update(search_default_appointment_type_id=appointment_id)
|
|
return action
|
|
|
|
def action_view_appointment_types(self):
|
|
action = self.env["ir.actions.actions"]._for_xml_id("appointment.appointment_type_action")
|
|
action['domain'] = [('question_ids', 'in', self.ids)]
|
|
return action
|