add survey_score_ranges: displays different messages depending on the score obtained, allowing different score ranges

This commit is contained in:
Luis 2025-09-22 12:47:58 +02:00
parent 9cabf044c8
commit 4d1eaebc06
11 changed files with 269 additions and 1 deletions

View file

@ -0,0 +1 @@
from . import models

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
{
"name": "Survey Score Ranges",
"version": "17.0.1.0.0",
"summary": "Rangos de puntuación para encuestas",
"category": "Survey",
"author": "Criptomart",
"website": "https://criptomart.net",
"license": "LGPL-3",
"depends": ["survey", "website"],
"data": [
"security/ir.model.access.csv",
"views/survey_score_range_views.xml",
"views/survey_survey_views.xml",
"views/survey_templates.xml",
],
"installable": True,
"application": False,
}

View file

@ -0,0 +1,3 @@
from . import survey_score_range
from . import survey_survey
from . import survey_user_input

View file

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
class SurveyScoreRange(models.Model):
_name = "survey.score.range"
_description = "Survey Score Range"
_order = "survey_id, min_score, max_score, id"
name = fields.Char("Name", required=True, translate=True)
survey_id = fields.Many2one(
"survey.survey", string="Survey", required=True, ondelete="cascade", index=True
)
min_score = fields.Float("Min Score", required=True, help="Inclusive lower bound")
max_score = fields.Float(
"Max Score",
required=True,
help="Inclusive upper bound (use same value as min for exact match). Deje vacío para indicar sin límite superior.",
default=0.0,
)
no_upper_limit = fields.Boolean(
"No upper limit",
help="If checked, max_score is ignored and the range is open-ended ( >= min_score ).",
)
result_html = fields.Html(
"Result Content", translate=True, sanitize=True, sanitize_overridable=True
)
active = fields.Boolean(default=True)
_sql_constraints = [
(
"min_le_max",
"CHECK( min_score <= max_score )",
"El mínimo debe ser menor o igual que el máximo.",
)
]
@api.constrains("min_score", "max_score", "no_upper_limit", "survey_id", "active")
def _check_overlapping(self):
for rec in self:
if rec.no_upper_limit:
domain = [
("id", "!=", rec.id),
("survey_id", "=", rec.survey_id.id),
("active", "=", True),
"|",
("no_upper_limit", "=", True),
("max_score", ">=", rec.min_score),
]
else:
domain = [
("id", "!=", rec.id),
("survey_id", "=", rec.survey_id.id),
("active", "=", True),
("min_score", "<=", rec.max_score),
"|",
("no_upper_limit", "=", True),
("max_score", ">=", rec.min_score),
]
overlaps = self.search_count(domain)
if overlaps:
raise ValidationError(
_("Los rangos de puntuación no pueden solaparse.")
)
def name_get(self):
res = []
for r in self:
if r.no_upper_limit:
label = _("%s (%s+ pts)") % (r.name, r.min_score)
else:
label = _("%s (%s-%s pts)") % (r.name, r.min_score, r.max_score)
res.append((r.id, label))
return res

View file

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class SurveySurvey(models.Model):
_inherit = "survey.survey"
enable_score_ranges = fields.Boolean(
"Enable Score Ranges", help="Show a result page based on score ranges."
)
score_range_ids = fields.One2many(
"survey.score.range", "survey_id", string="Score Ranges"
)

View file

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class SurveyUserInput(models.Model):
_inherit = "survey.user_input"
result_range_id = fields.Many2one(
"survey.score.range",
string="Score Range",
compute="_compute_result_range_id",
store=True,
compute_sudo=True,
)
result_range_html = fields.Html(
string="Result Range Content",
compute="_compute_result_range_html",
sanitize=False,
)
@api.depends(
"scoring_total", "survey_id.enable_score_ranges", "survey_id.score_range_ids"
)
def _compute_result_range_id(self):
for user_input in self:
selected = False
if user_input.survey_id.enable_score_ranges and user_input.state == "done":
total = user_input.scoring_total
ranges = user_input.survey_id.score_range_ids.filtered("active").sorted(
lambda r: r.min_score
)
for r in ranges:
if r.no_upper_limit:
if total >= r.min_score:
selected = r
else:
if total >= r.min_score and total <= r.max_score:
selected = r
if selected:
break
user_input.result_range_id = selected
@api.depends("result_range_id")
def _compute_result_range_html(self):
for user_input in self:
user_input.result_range_html = (
user_input.result_range_id.result_html
if user_input.result_range_id
else False
)

View file

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_survey_score_range_user,survey.score.range user,model_survey_score_range,survey.group_survey_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_survey_score_range_user survey.score.range user model_survey_score_range survey.group_survey_manager 1 1 1 1

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_survey_score_range_tree" model="ir.ui.view">
<field name="name">survey.score.range.tree</field>
<field name="model">survey.score.range</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="survey_id"/>
<field name="min_score"/>
<field name="max_score"/>
<field name="no_upper_limit"/>
<field name="active"/>
</tree>
</field>
</record>
<record id="view_survey_score_range_form" model="ir.ui.view">
<field name="name">survey.score.range.form</field>
<field name="model">survey.score.range</field>
<field name="arch" type="xml">
<form string="Score Range">
<sheet>
<group>
<field name="name"/>
<field name="survey_id"/>
<field name="min_score"/>
<field name="no_upper_limit"/>
<field name="max_score" invisible="no_upper_limit"/>
<field name="active"/>
</group>
<notebook>
<page string="Result Content">
<field name="result_html" widget="html"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_survey_score_range" model="ir.actions.act_window">
<field name="name">Score Ranges</field>
<field name="res_model">survey.score.range</field>
<field name="view_mode">tree,form</field>
<field name="context">{}</field>
</record>
<menuitem id="menu_survey_score_range_root" name="Score Ranges" parent="survey.menu_surveys" action="action_survey_score_range" sequence="60" groups="base.group_user"/>
</odoo>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_survey_survey_form_inherit_score_ranges" model="ir.ui.view">
<field name="name">survey.survey.form.score.ranges</field>
<field name="model">survey.survey</field>
<field name="inherit_id" ref="survey.survey_survey_view_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet/notebook" position="inside">
<page string="Score Ranges" invisible='not scoring_type'>
<group>
<field name="enable_score_ranges"/>
</group>
<group invisible="not enable_score_ranges" col="1">
<field name="score_range_ids" context="{'default_survey_id': active_id}" nolabel="1">
<tree editable="bottom">
<field name="name"/>
<field name="min_score"/>
<field name="no_upper_limit"/>
<field name="max_score" invisible="no_upper_limit"/>
<field name="active"/>
</tree>
<!-- <form string="Score Range">
<sheet>
<group>
<field name="name"/>
<field name="min_score"/>
<field name="no_upper_limit"/>
<field name="max_score" invisible="no_upper_limit"/>
<field name="active"/>
</group>
<group>
<field name="result_html" widget="html"/>
</group>
</sheet>
</form> -->
</field>
</group>
</page>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Inherit finished template to inject range result -->
<template id="survey_fill_form_done_inherit_score_ranges" inherit_id="survey.survey_fill_form_done">
<xpath expr="//div[@class='o_survey_finished mt32 mb32']/div[@class='row']/div[@class='col']" position="inside">
<t t-if="survey.enable_score_ranges and answer.result_range_html">
<div class="o_survey_score_range_result mt-3">
<t t-raw="answer.result_range_html"/>
</div>
</t>
</xpath>
</template>
</odoo>

View file

@ -3,7 +3,7 @@
"version": "17.0.1.0.0",
"summary": "Directorio de empresas en website con mapa Leaflet",
"category": "Website",
"author": "Luis",
"author": "Criptomart",
"depends": ["website_partner", "base_geolocalize"],
"data": ["views/res_partner_views.xml", "views/directory_template.xml"],
"assets": {