[REF] survey: move from onchange to editable stored fields

PURPOSE

Try to move from onchange / default_get to stored editable computed fields.
Behavior should be the same (computed or set by user), with support of
create / write / onchange field update without additional code.

SPECIFICATIONS

Update classic fields updated in some cases by onchange and/or default methods
by fields with store=True, readonly=False. It means their value comes either
from manual user input, either from trigger based computation.

Remove onchange and default_get when possible, leading to an unique computation
method and clearing fields definition.

Also clean some fields definition inconsistencies, notably required fields
that should instead be correctly computed or default that have no real meaning.

SURVEY USE CASES

- On survey.question, the question_type field should be emptied if the question
  is a page (is_page = True)
- On survey.survey, if scoring is configured, the minimum scoring success
  should be between 0 and 100 (added SQL constraint).
- On survey.survey, the certification field cannot be True if we're in no
  scoring mode (scoring_type = 'no_scoring')
- On survey.survey, the attempts number cannot be limited (is_attempts_limited
  is False) if the access mode is public and the login is not required.
- On survey.survey, we cannot give a badge (certification_give_badge is False)
  if it's not a certification or if the login is not required.
- On survey.invite, the "subject" and "body" field are based on the template
  used (template_id field).

MISC

- As "validation_email" works with "validation_required", we removed the
  onchange on "validation_email".
- To avoid extra complexity with an onchange on "is_time_limited" and
  "time_limit", we simply default the time_limit field to 10.

LINKS

Upgrade PR odoo/upgrade#847
Task 2187377

closes odoo/odoo#46291

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Aurélien Warnon
2020-05-06 13:01:08 +00:00
parent eb08e2c228
commit 71e17248a4
4 changed files with 42 additions and 49 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Surveys',
'version': '3.1',
'version': '3.2',
'category': 'Marketing/Surveys',
'description': """
Create beautiful surveys and visualize answers
+7 -10
View File
@@ -79,7 +79,8 @@ class SurveyQuestion(models.Model):
('datetime', 'Datetime'),
('simple_choice', 'Multiple choice: only one answer'),
('multiple_choice', 'Multiple choice: multiple answers allowed'),
('matrix', 'Matrix')], string='Question Type')
('matrix', 'Matrix')], string='Question Type',
compute='_compute_question_type', readonly=False, store=True)
is_scored_question = fields.Boolean(
'Scored', compute='_compute_is_scored_question',
readonly=False, store=True, copy=True,
@@ -170,15 +171,11 @@ class SurveyQuestion(models.Model):
'All "Is a scored question = True" and "Question Type: Date" questions need an answer')
]
@api.onchange('validation_email')
def _onchange_validation_email(self):
if self.validation_email:
self.validation_required = False
@api.onchange('is_page')
def _onchange_is_page(self):
if self.is_page:
self.question_type = False
@api.depends('is_page')
def _compute_question_type(self):
for question in self:
if not question.question_type or question.is_page:
question.question_type = False
@api.depends('survey_id.question_and_page_ids.is_page', 'survey_id.question_and_page_ids.sequence')
def _compute_question_ids(self):
+22 -30
View File
@@ -106,9 +106,10 @@ class Survey(models.Model):
compute="_compute_is_attempts_limited", store=True, readonly=False)
attempts_limit = fields.Integer('Number of attempts', default=1)
is_time_limited = fields.Boolean('The survey is limited in time')
time_limit = fields.Float("Time limit (minutes)")
time_limit = fields.Float("Time limit (minutes)", default=10)
# certification
certification = fields.Boolean('Is a Certification')
certification = fields.Boolean('Is a Certification', compute='_compute_certification',
readonly=False, store=True)
certification_mail_template_id = fields.Many2one(
'mail.template', 'Email Template',
domain="[('model', '=', 'survey.user_input')]",
@@ -126,7 +127,8 @@ class Survey(models.Model):
# - If the certification badge is not set, show certification_badge_id and only display create option in the m2o
# - If the certification badge is set, show certification_badge_id_dummy in 'no create' mode.
# So it can be edited but not removed or replaced.
certification_give_badge = fields.Boolean('Give Badge')
certification_give_badge = fields.Boolean('Give Badge', compute='_compute_certification_give_badge',
readonly=False, store=True)
certification_badge_id = fields.Many2one('gamification.badge', 'Certification Badge')
certification_badge_id_dummy = fields.Many2one(related='certification_badge_id', string='Certification Badge ')
# live sessions
@@ -157,6 +159,8 @@ class Survey(models.Model):
('session_code_unique', 'unique(session_code)', 'Session code should be unique'),
('certification_check', "CHECK( scoring_type!='no_scoring' OR certification=False )",
'You can only create certifications for surveys that have a scoring mechanism.'),
('scoring_success_min_check', "CHECK( scoring_success_min IS NULL OR (scoring_success_min>=0 AND scoring_success_min<=100) )",
'The percentage of success has to be defined between 0 and 100.'),
('time_limit_check', "CHECK( (is_time_limited=False) OR (time_limit is not null AND time_limit > 0) )",
'The time limit needs to be a positive number if the survey is time limited.'),
('attempts_limit_check', "CHECK( (is_attempts_limited=False) OR (attempts_limit is not null AND attempts_limit > 0) )",
@@ -207,9 +211,9 @@ class Survey(models.Model):
@api.depends('question_and_page_ids.is_conditional', 'users_login_required', 'access_mode')
def _compute_is_attempts_limited(self):
for survey in self:
if any(question.is_conditional for question in survey.question_and_page_ids)\
or (survey.access_mode == 'public' and not survey.users_login_required)\
or survey.is_attempts_limited is None:
if not survey.is_attempts_limited or \
(survey.access_mode == 'public' and not survey.users_login_required) or \
any(question.is_conditional for question in survey.question_and_page_ids):
survey.is_attempts_limited = False
@api.depends('session_start_time', 'user_input_ids')
@@ -276,36 +280,24 @@ class Survey(models.Model):
for survey in self:
survey.has_conditional_questions = any(question.is_conditional for question in survey.question_and_page_ids)
@api.onchange('scoring_success_min')
def _onchange_scoring_success_min(self):
if self.scoring_success_min < 0 or self.scoring_success_min > 100:
self.scoring_success_min = 80.0
@api.depends('scoring_type')
def _compute_certification(self):
for survey in self:
if not survey.certification or survey.scoring_type == 'no_scoring':
survey.certification = False
@api.onchange('scoring_type')
def _onchange_scoring_type(self):
if self.scoring_type == 'no_scoring':
self.certification = False
self.is_time_limited = False
@api.onchange('attempts_limit')
def _onchange_attempts_limit(self):
if self.attempts_limit <= 0:
self.attempts_limit = 1
@api.onchange('is_time_limited', 'time_limit')
def _onchange_time_limit(self):
if self.is_time_limited and (not self.time_limit or self.time_limit <= 0):
self.time_limit = 10
@api.depends('users_login_required', 'certification')
def _compute_certification_give_badge(self):
for survey in self:
if not survey.certification_give_badge or \
not survey.users_login_required or \
not survey.certification:
survey.certification_give_badge = False
def _read_group_states(self, values, domain, order):
selection = self.env['survey.survey'].fields_get(allfields=['state'])['state']['selection']
return [s[0] for s in selection]
@api.onchange('users_login_required', 'certification')
def _onchange_set_certification_give_badge(self):
if not self.users_login_required or not self.certification:
self.certification_give_badge = False
# ------------------------------------------------------------
# CRUD
# ------------------------------------------------------------
+12 -8
View File
@@ -28,8 +28,8 @@ class SurveyInvite(models.TransientModel):
return self.env.user.partner_id
# composer content
subject = fields.Char('Subject')
body = fields.Html('Contents', default='', sanitize_style=True)
subject = fields.Char('Subject', compute='_compute_template_values', readonly=False, store=True)
body = fields.Html('Contents', sanitize_style=True, compute='_compute_template_values', readonly=False, store=True)
attachment_ids = fields.Many2many(
'ir.attachment', 'survey_mail_compose_message_ir_attachments_rel', 'wizard_id', 'attachment_id',
string='Attachments')
@@ -137,12 +137,16 @@ class SurveyInvite(models.TransientModel):
_('The following recipients have no user account: %s. You should create user accounts for them or allow external signup in configuration.' %
(','.join(invalid_partners.mapped('name')))))
@api.onchange('template_id')
def _onchange_template_id(self):
""" UPDATE ME """
if self.template_id:
self.subject = self.template_id.subject
self.body = self.template_id.body_html
@api.depends('template_id')
def _compute_template_values(self):
for invite in self:
if not invite.subject:
invite.subject = ''
if not invite.body:
invite.body = ''
if invite.template_id:
invite.subject = invite.template_id.subject
invite.body = invite.template_id.body_html
@api.model
def create(self, values):