summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Frantzen (bfr) <bfr@odoo.com>2019-12-18 13:45:38 +0000
committerYannick Tivisse <yti@odoo.com>2020-05-08 12:17:20 +0000
commite4a4ffb974ccde1842e1b2389450718f8576ad5b (patch)
tree6159873058286c65a3a3bfb5a48f2ef5ffbf7565
parentf427a38c4e0dcd63d086706ad66a3e5570fbf0aa (diff)
[IMP] customer ratings flow improvements
- Rename the 'Use Rating on Project' feature into 'Customer Ratings' - Rename the 'Set Email Template to Stages' link to 'Set a Rating Email Template on Stages' - Add an optional list view for the Stages menu - display warning if the rating_template_id field is set and if one of the selected project_ids doesn't have the rating_status field set to true - Project form view revamp - rename the '% on tasks' stat button into 'Customer Satisfaction' - Remove the 'no option' for the rating frequency field because it is required - project form : Add a 'Go to Website' stat button - Project dashboard: remove the 'Customer Ratings' menu item in more - Ratings page: the 'Last 30 days' filter include ratings from today - remove the Appointment / Helpdesk Customer Satisfaction / Live Support menu items TASK ID : 1251
-rw-r--r--addons/im_livechat/data/im_livechat_channel_demo.xml14
-rw-r--r--addons/im_livechat/report/im_livechat_report_channel.py6
-rw-r--r--addons/im_livechat/static/src/js/im_livechat.js2
-rw-r--r--addons/im_livechat/static/src/xml/im_livechat.xml4
-rw-r--r--addons/portal_rating/static/src/js/portal_chatter.js9
-rw-r--r--addons/portal_rating/static/src/js/portal_composer.js8
-rw-r--r--addons/portal_rating/static/src/js/portal_rating_composer.js4
-rw-r--r--addons/portal_rating/views/rating_templates.xml2
-rw-r--r--addons/project/__manifest__.py1
-rw-r--r--addons/project/controllers/__init__.py1
-rw-r--r--addons/project/data/project_data.xml1
-rw-r--r--addons/project/data/project_demo.xml4
-rw-r--r--addons/project/data/project_mail_template_data.xml8
-rw-r--r--addons/project/models/project.py50
-rw-r--r--addons/project/models/res_config_settings.py16
-rw-r--r--addons/project/tests/test_project_flow.py6
-rw-r--r--addons/project/views/project_views.xml334
-rw-r--r--addons/project/views/rating_views.xml5
-rw-r--r--addons/project/views/res_config_settings_views.xml2
-rw-r--r--addons/rating/controllers/main.py8
-rw-r--r--addons/rating/models/rating.py34
-rw-r--r--addons/rating/models/rating_mixin.py6
-rw-r--r--addons/rating/static/src/img/rating_10.pngbin2398 -> 0 bytes
-rw-r--r--addons/rating/static/src/img/rating_10_white.pngbin2293 -> 0 bytes
-rw-r--r--addons/rating/static/src/img/rating_3.pngbin0 -> 2186 bytes
-rw-r--r--addons/rating/static/src/img/rating_3_white.pngbin0 -> 2180 bytes
-rw-r--r--addons/rating/static/src/img/rating_5.pngbin2186 -> 2398 bytes
-rw-r--r--addons/rating/static/src/img/rating_5_white.pngbin2180 -> 2293 bytes
-rw-r--r--addons/rating/tests/test_security.py6
-rw-r--r--addons/website_livechat/data/website_livechat_data.xml4
-rw-r--r--addons/website_livechat/static/tests/tours/website_livechat_common.js4
-rw-r--r--addons/website_livechat/tests/common.py2
-rw-r--r--addons/website_livechat/tests/test_livechat_request.py2
-rw-r--r--addons/website_livechat/views/website_livechat.xml10
-rw-r--r--addons/website_project/__init__.py5
-rw-r--r--addons/website_project/__manifest__.py18
-rw-r--r--addons/website_project/controllers/__init__.py4
-rw-r--r--addons/website_project/controllers/rating.py (renamed from addons/project/controllers/rating.py)18
-rw-r--r--addons/website_project/models/__init__.py4
-rw-r--r--addons/website_project/models/project.py23
-rw-r--r--addons/website_project/views/project_project_views.xml16
-rw-r--r--addons/website_project/views/project_rating_templates.xml (renamed from addons/project/views/project_rating_templates.xml)72
-rw-r--r--addons/website_slides/data/slide_user_demo.xml10
-rw-r--r--addons/website_slides/models/slide_channel.py2
-rw-r--r--addons/website_slides/views/rating_rating_views.xml2
-rw-r--r--doc/reference/mixins.rst4
46 files changed, 407 insertions, 324 deletions
diff --git a/addons/im_livechat/data/im_livechat_channel_demo.xml b/addons/im_livechat/data/im_livechat_channel_demo.xml
index e6cde7dbfa6..5e3ca7fc937 100644
--- a/addons/im_livechat/data/im_livechat_channel_demo.xml
+++ b/addons/im_livechat/data/im_livechat_channel_demo.xml
@@ -31,7 +31,7 @@
<field eval="False" name="partner_id"/>
<field eval="True" name="consumed"/>
</record>
- <function eval="([ref('im_livechat.im_livechat_mail_channel_data_0')], 10)" model="mail.channel" name="rating_apply"/>
+ <function eval="([ref('im_livechat.im_livechat_mail_channel_data_0')], 5)" model="mail.channel" name="rating_apply"/>
<record id="im_livechat_mail_message_5_data" model="mail.message">
<field name="model">mail.channel</field>
<field name="res_id" ref="im_livechat.im_livechat_mail_channel_data_0"/>
@@ -92,7 +92,7 @@
<field eval="False" name="partner_id"/>
<field eval="True" name="consumed"/>
</record>
- <function eval="([ref('im_livechat.im_livechat_mail_channel_data_1')], 10)" model="mail.channel" name="rating_apply"/>
+ <function eval="([ref('im_livechat.im_livechat_mail_channel_data_1')], 5)" model="mail.channel" name="rating_apply"/>
<record id="im_livechat_mail_message_10_data" model="mail.message">
<field name="model">mail.channel</field>
<field name="res_id" ref="im_livechat.im_livechat_mail_channel_data_1"/>
@@ -205,7 +205,7 @@
<field name="partner_id" ref="base.partner_demo_portal"/>
<field eval="True" name="consumed"/>
</record>
- <function eval="([ref('im_livechat.im_livechat_mail_channel_data_3')], 10)" model="mail.channel" name="rating_apply"/>
+ <function eval="([ref('im_livechat.im_livechat_mail_channel_data_3')], 5)" model="mail.channel" name="rating_apply"/>
<record id="im_livechat_mail_message_17_data" model="mail.message">
<field name="model">mail.channel</field>
<field name="res_id" ref="im_livechat.im_livechat_mail_channel_data_3"/>
@@ -300,7 +300,7 @@
<field eval="False" name="partner_id"/>
<field eval="True" name="consumed"/>
</record>
- <function eval="([ref('im_livechat.im_livechat_mail_channel_data_5')], 10)" model="mail.channel" name="rating_apply"/>
+ <function eval="([ref('im_livechat.im_livechat_mail_channel_data_5')], 5)" model="mail.channel" name="rating_apply"/>
<record id="im_livechat_mail_message_25_data" model="mail.message">
<field name="model">mail.channel</field>
<field name="res_id" ref="im_livechat.im_livechat_mail_channel_data_5"/>
@@ -352,7 +352,7 @@
<field name="partner_id" ref="base.partner_demo_portal"/>
<field eval="True" name="consumed"/>
</record>
- <function eval="([ref('im_livechat.im_livechat_mail_channel_data_6')], 10)" model="mail.channel" name="rating_apply"/>
+ <function eval="([ref('im_livechat.im_livechat_mail_channel_data_6')], 5)" model="mail.channel" name="rating_apply"/>
<record id="im_livechat_mail_message_29_data" model="mail.message">
<field name="model">mail.channel</field>
<field name="res_id" ref="im_livechat.im_livechat_mail_channel_data_6"/>
@@ -404,7 +404,7 @@
<field eval="False" name="partner_id"/>
<field eval="True" name="consumed"/>
</record>
- <function eval="([ref('im_livechat.im_livechat_mail_channel_data_7')], 10)" model="mail.channel" name="rating_apply"/>
+ <function eval="([ref('im_livechat.im_livechat_mail_channel_data_7')], 5)" model="mail.channel" name="rating_apply"/>
<record id="im_livechat_mail_message_33_data" model="mail.message">
<field name="model">mail.channel</field>
<field name="res_id" ref="im_livechat.im_livechat_mail_channel_data_7"/>
@@ -514,7 +514,7 @@
<field name="partner_id" ref="base.partner_admin"/>
<field name="create_date" eval="datetime.now() - timedelta(days=1, seconds=-53)"/>
<field name="res_id" ref="im_livechat.mail_channel_livechat_1"/>
- <field name="rating" eval="10"/>
+ <field name="rating" eval="5"/>
<field name="consumed" eval="True"/>
</record>
diff --git a/addons/im_livechat/report/im_livechat_report_channel.py b/addons/im_livechat/report/im_livechat_report_channel.py
index b22e040327a..07c01110b8e 100644
--- a/addons/im_livechat/report/im_livechat_report_channel.py
+++ b/addons/im_livechat/report/im_livechat_report_channel.py
@@ -72,14 +72,14 @@ class ImLivechatReportChannel(models.Model):
END as is_anonymous,
C.country_id,
CASE
- WHEN rate.rating = 10 THEN 1
+ WHEN rate.rating = 5 THEN 1
ELSE 0
END as is_happy,
Rate.rating as rating,
CASE
WHEN Rate.rating = 1 THEN 'Unhappy'
- WHEN Rate.rating = 10 THEN 'Happy'
- WHEN Rate.rating = 5 THEN 'Neutral'
+ WHEN Rate.rating = 5 THEN 'Happy'
+ WHEN Rate.rating = 3 THEN 'Neutral'
ELSE null
END as rating_text,
CASE
diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js
index 000ac6665b9..134679e933d 100644
--- a/addons/im_livechat/static/src/js/im_livechat.js
+++ b/addons/im_livechat/static/src/js/im_livechat.js
@@ -525,7 +525,7 @@ var Feedback = Widget.extend({
this.$('.o_livechat_rating_choices img[data-value="'+this.rating+'"]').addClass('selected');
// only display textearea if bad smiley selected
- if (this.rating !== 10) {
+ if (this.rating !== 5) {
this.$('.o_livechat_rating_reason').show();
} else {
this.$('.o_livechat_rating_reason').hide();
diff --git a/addons/im_livechat/static/src/xml/im_livechat.xml b/addons/im_livechat/static/src/xml/im_livechat.xml
index c19e2d09881..9e85f86baeb 100644
--- a/addons/im_livechat/static/src/xml/im_livechat.xml
+++ b/addons/im_livechat/static/src/xml/im_livechat.xml
@@ -9,8 +9,8 @@
Did we correctly answer your question ?
</div>
<div class="o_livechat_rating_choices">
- <img t-att-src="widget.server_origin + '/rating/static/src/img/rating_10.png'" alt="Good" data-value="10"/>
- <img t-att-src="widget.server_origin + '/rating/static/src/img/rating_5.png'" alt="OK" data-value="5"/>
+ <img t-att-src="widget.server_origin + '/rating/static/src/img/rating_5.png'" alt="Good" data-value="5"/>
+ <img t-att-src="widget.server_origin + '/rating/static/src/img/rating_3.png'" alt="OK" data-value="3"/>
<img t-att-src="widget.server_origin + '/rating/static/src/img/rating_1.png'" alt="Bad" data-value="1" />
</div>
</div>
diff --git a/addons/portal_rating/static/src/js/portal_chatter.js b/addons/portal_rating/static/src/js/portal_chatter.js
index 51b403b521b..baaebd17be6 100644
--- a/addons/portal_rating/static/src/js/portal_chatter.js
+++ b/addons/portal_rating/static/src/js/portal_chatter.js
@@ -9,7 +9,6 @@ var time = require('web.time');
var _t = core._t;
var PortalChatter = portalChatter.PortalChatter;
var qweb = core.qweb;
-var STAR_RATING_RATIO = 2; // conversion factor from the star (1-5) to the db rating range (1-10)
/**
* PortalChatter
@@ -67,7 +66,7 @@ PortalChatter.include({
messages = this._super.apply(this, arguments);
if (this.options['display_rating']) {
_.each(messages, function (m, i) {
- m.rating_value = self.roundToHalf(m['rating_value'] / STAR_RATING_RATIO);
+ m.rating_value = self.roundToHalf(m['rating_value']);
m.rating = self._preprocessCommentData(m.rating, i);
});
}
@@ -114,13 +113,13 @@ PortalChatter.include({
return;
}
var ratingData = {
- 'avg': Math.round(result['rating_stats']['avg'] / STAR_RATING_RATIO * 100) / 100,
+ 'avg': Math.round(result['rating_stats']['avg'] * 100) / 100,
'percent': [],
};
_.each(_.keys(result['rating_stats']['percent']).reverse(), function (rating) {
if (rating % 2 === 0) {
ratingData['percent'].push({
- 'num': rating / STAR_RATING_RATIO,
+ 'num': rating,
'percent': utils.round_precision(result['rating_stats']['percent'][rating], 0.01),
});
}
@@ -347,7 +346,7 @@ PortalChatter.include({
_onChangeRatingDomain: function () {
var domain = [];
if (this.get('rating_value')) {
- domain = [['rating_value', '=', this.get('rating_value') * STAR_RATING_RATIO]];
+ domain = [['rating_value', '=', this.get('rating_value')]];
}
this._changeCurrentPage(1, domain);
},
diff --git a/addons/portal_rating/static/src/js/portal_composer.js b/addons/portal_rating/static/src/js/portal_composer.js
index 37b0b9e3511..63854caa302 100644
--- a/addons/portal_rating/static/src/js/portal_composer.js
+++ b/addons/portal_rating/static/src/js/portal_composer.js
@@ -8,8 +8,6 @@ var _t = core._t;
var PortalComposer = portalComposer.PortalComposer;
-var STAR_RATING_RATIO = 2; // conversion factor from the star (1-5) to the db rating range (1-10)
-
/**
* PortalComposer
*
@@ -31,7 +29,7 @@ PortalComposer.include({
// apply ratio to default rating value
if (options.default_rating_value) {
- options.default_rating_value = parseFloat(options.default_rating_value) / STAR_RATING_RATIO;
+ options.default_rating_value = parseFloat(options.default_rating_value);
}
// default options
@@ -66,7 +64,7 @@ PortalComposer.include({
// set the default value to trigger the display of star widget and update the hidden input value.
self.set("star_value", self.options.default_rating_value);
- self.$input.val(self.options.default_rating_value * STAR_RATING_RATIO);
+ self.$input.val(self.options.default_rating_value);
});
},
@@ -97,7 +95,7 @@ PortalComposer.include({
var index = this.$('.stars i').index(ev.currentTarget);
this.set("star_value", index + 1);
this.user_click = true;
- this.$input.val(this.get("star_value") * STAR_RATING_RATIO);
+ this.$input.val(this.get("star_value"));
},
/**
* @private
diff --git a/addons/portal_rating/static/src/js/portal_rating_composer.js b/addons/portal_rating/static/src/js/portal_rating_composer.js
index 8bf205a6cb8..af7a55b8749 100644
--- a/addons/portal_rating/static/src/js/portal_rating_composer.js
+++ b/addons/portal_rating/static/src/js/portal_rating_composer.js
@@ -7,8 +7,6 @@ var portalComposer = require('portal.composer');
var PortalComposer = portalComposer.PortalComposer;
-var STAR_RATING_RATIO = 2; // conversion factor from the star (1-5) to the db rating range (1-10)
-
/**
* RatingPopupComposer
*
@@ -25,7 +23,7 @@ var RatingPopupComposer = publicWidget.Widget.extend({
init: function (parent, options) {
this._super.apply(this, arguments);
- this.rating_avg = Math.round(options['ratingAvg'] / STAR_RATING_RATIO * 100) / 100 || 0.0;
+ this.rating_avg = Math.round(options['ratingAvg'] / 100) / 100 || 0.0;
this.rating_total = options['ratingTotal'] || 0.0;
this.options = _.defaults({}, options, {
diff --git a/addons/portal_rating/views/rating_templates.xml b/addons/portal_rating/views/rating_templates.xml
index a1faae6aa21..78561828a00 100644
--- a/addons/portal_rating/views/rating_templates.xml
+++ b/addons/portal_rating/views/rating_templates.xml
@@ -4,7 +4,7 @@
Rating widget static: show 5 stars (full or empty) regarding the given rating_avg and rating_count
-->
<template id="rating_widget_stars_static" name="Rating: static star widget">
- <t t-set="rating_avg" t-value="round(rating_avg * 100) / 100 / 2"/>
+ <t t-set="rating_avg" t-value="round(rating_avg * 100) / 100"/>
<t t-set="val_decimal" t-value="round(rating_avg % 1, 1)"/>
<t t-set="val_integer" t-value="int(rating_avg)"/>
<t t-set="empty_star" t-value="5 - (val_integer+1) if val_decimal else 5 - val_integer"/>
diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py
index 87d37668020..d727cf9cfc2 100644
--- a/addons/project/__manifest__.py
+++ b/addons/project/__manifest__.py
@@ -33,7 +33,6 @@
'views/mail_activity_views.xml',
'views/project_assets.xml',
'views/project_portal_templates.xml',
- 'views/project_rating_templates.xml',
'data/digest_data.xml',
'data/project_mail_template_data.xml',
'data/project_data.xml',
diff --git a/addons/project/controllers/__init__.py b/addons/project/controllers/__init__.py
index 10825353c81..903b755e71e 100644
--- a/addons/project/controllers/__init__.py
+++ b/addons/project/controllers/__init__.py
@@ -2,4 +2,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import portal
-from . import rating
diff --git a/addons/project/data/project_data.xml b/addons/project/data/project_data.xml
index d78834cfad2..5e45f5c628b 100644
--- a/addons/project/data/project_data.xml
+++ b/addons/project/data/project_data.xml
@@ -95,6 +95,7 @@
<field name="alias_defaults">{'project_id': 1}</field>
<field name="alias_force_thread_id">0</field>
<field name="alias_parent_model_id" ref="model_project_project"/>
+ <field name="rating_status">stage</field>
</record>
<record forcecreate="False" id="project_stage_data_0" model="project.task.type">
diff --git a/addons/project/data/project_demo.xml b/addons/project/data/project_demo.xml
index 44be6bc5ec4..be21ed9dfa3 100644
--- a/addons/project/data/project_demo.xml
+++ b/addons/project/data/project_demo.xml
@@ -324,8 +324,8 @@ Send it ASAP, its urgent.</field>
<field name="rating_template_id" ref="rating_project_request_email_template"/>
</record>
- <function model="project.task" name="rating_apply" eval="([ref('project.project_task_3')], 10)"/>
- <function model="project.task" name="rating_apply" eval="([ref('project.project_task_24')], 10)"/>
+ <function model="project.task" name="rating_apply" eval="([ref('project.project_task_3')], 5)"/>
+ <function model="project.task" name="rating_apply" eval="([ref('project.project_task_24')], 5)"/>
<function model="project.task" name="rating_apply" eval="([ref('project.project_task_8')], 1)"/>
</data>
</odoo>
diff --git a/addons/project/data/project_mail_template_data.xml b/addons/project/data/project_mail_template_data.xml
index bc96d795102..441be455f31 100644
--- a/addons/project/data/project_mail_template_data.xml
+++ b/addons/project/data/project_mail_template_data.xml
@@ -60,13 +60,13 @@
<table style="width:100%;text-align:center;">
<tr>
<td>
- <a href="/rating/${access_token}/10">
- <img alt="Satisfied" src="/rating/static/src/img/rating_10.png" title="Satisfied"/>
+ <a href="/rating/${access_token}/5">
+ <img alt="Satisfied" src="/rating/static/src/img/rating_5.png" title="Satisfied"/>
</a>
</td>
<td>
- <a href="/rating/${access_token}/5">
- <img alt="Not satisfied" src="/rating/static/src/img/rating_5.png" title="Not satisfied"/>
+ <a href="/rating/${access_token}/3">
+ <img alt="Not satisfied" src="/rating/static/src/img/rating_3.png" title="Not satisfied"/>
</a>
</td>
<td>
diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index 8accaa81de7..cc90d132df1 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -51,6 +51,7 @@ class ProjectTaskType(models.Model):
" * A good feedback from the customer will update the kanban state to 'ready for the new stage' (green bullet).\n"
" * A medium or a bad feedback will set the kanban state to 'blocked' (red bullet).\n")
is_closed = fields.Boolean('Closing Stage', help="Tasks in this stage are considered as closed.")
+ disabled_rating_warning = fields.Text(compute='_compute_disabled_rating_warning')
def unlink_wizard(self, stage_view=False):
self = self.with_context(active_test=False)
@@ -82,6 +83,15 @@ class ProjectTaskType(models.Model):
self.env['project.task'].search([('stage_id', 'in', self.ids)]).write({'active': False})
return super(ProjectTaskType, self).write(vals)
+ @api.depends('project_ids', 'project_ids.rating_active')
+ def _compute_disabled_rating_warning(self):
+ for stage in self:
+ disabled_projects = stage.project_ids.filtered(lambda p: not p.rating_active)
+ if disabled_projects:
+ stage.disabled_rating_warning = '\n'.join('- %s' % p.name for p in disabled_projects)
+ else:
+ stage.disabled_rating_warning = False
+
class Project(models.Model):
_name = "project.project"
@@ -172,11 +182,6 @@ class Project(models.Model):
def _get_default_favorite_user_ids(self):
return [(6, 0, [self.env.uid])]
- def _get_default_rating_status(self):
- if self.user_has_groups('project.group_project_rating'):
- return 'stage'
- return 'no'
-
name = fields.Char("Name", index=True, required=True, tracking=True)
active = fields.Boolean(default=True,
help="If the active field is set to False, it will allow you to hide the project without removing it.")
@@ -238,20 +243,22 @@ class Project(models.Model):
# rating fields
rating_request_deadline = fields.Datetime(compute='_compute_rating_request_deadline', store=True)
- rating_status = fields.Selection([
- ('stage', 'Rating when changing stage'),
- ('periodic', 'Periodical Rating'),
- ('no', 'No rating')], string='Customer Ratings', default=_get_default_rating_status, required=True,
+ rating_active = fields.Boolean('Customer Ratings', default=True)
+ rating_status = fields.Selection(
+ [('stage', 'Rating when changing stage'),
+ ('periodic', 'Periodical Rating')
+ ], 'Customer Ratings Status', default="stage", required=True,
help="How to get customer feedback?\n"
- "- Rating when changing stage: an email will be sent when a task is pulled in another stage.\n"
- "- Periodical Rating: email will be sent periodically.\n\n"
- "Don't forget to set up the mail templates on the stages for which you want to get the customer's feedbacks.")
+ "- Rating when changing stage: an email will be sent when a task is pulled in another stage.\n"
+ "- Periodical Rating: email will be sent periodically.\n\n"
+ "Don't forget to set up the mail templates on the stages for which you want to get the customer's feedbacks.")
rating_status_period = fields.Selection([
- ('daily', 'Daily'), ('weekly', 'Weekly'), ('bimonthly', 'Twice a Month'),
- ('monthly', 'Once a Month'), ('quarterly', 'Quarterly'), ('yearly', 'Yearly')
- ], 'Rating Frequency')
-
- portal_show_rating = fields.Boolean('Rating visible publicly', copy=False)
+ ('daily', 'Daily'),
+ ('weekly', 'Weekly'),
+ ('bimonthly', 'Twice a Month'),
+ ('monthly', 'Once a Month'),
+ ('quarterly', 'Quarterly'),
+ ('yearly', 'Yearly')], 'Rating Frequency', required=True, default='monthly')
_sql_constraints = [
('project_date_greater', 'check(date >= date_start)', 'Error! project start-date must be lower than project end-date.')
@@ -452,14 +459,7 @@ class Project(models.Model):
return action
def action_view_all_rating(self):
- """ return the action to see all the rating of the project, and activate default filters """
- if self.portal_show_rating:
- return {
- 'type': 'ir.actions.act_url',
- 'name': "Redirect to the Website Projcet Rating Page",
- 'target': 'self',
- 'url': "/project/rating/%s" % (self.id,)
- }
+ """ return the action to see all the rating of the project and activate default filters"""
action = self.env['ir.actions.act_window'].for_xml_id('project', 'rating_rating_action_view_project_rating')
action['name'] = _('Ratings of %s') % (self.name,)
action_context = ast.literal_eval(action['context']) if action['context'] else {}
diff --git a/addons/project/models/res_config_settings.py b/addons/project/models/res_config_settings.py
index 439e09d7705..e63c53edc59 100644
--- a/addons/project/models/res_config_settings.py
+++ b/addons/project/models/res_config_settings.py
@@ -10,18 +10,4 @@ class ResConfigSettings(models.TransientModel):
module_project_forecast = fields.Boolean(string="Planning")
module_hr_timesheet = fields.Boolean(string="Task Logs")
group_subtask_project = fields.Boolean("Sub-tasks", implied_group="project.group_subtask_project")
- group_project_rating = fields.Boolean("Use Rating on Project", implied_group='project.group_project_rating')
-
- def _get_subtasks_projects_domain(self):
- return []
-
- def execute(self):
- res = super(ResConfigSettings, self).execute()
- if self.group_project_rating:
- # Change the rating status on existing projects from 'no' to 'stage'
- self.env['project.project'].search([('rating_status', '=', 'no')]).write({
- 'rating_status': 'stage'})
- if self.group_subtask_project:
- domain = self._get_subtasks_projects_domain()
- self.env['project.project'].search(domain).write({'allow_subtasks': True})
- return res
+ group_project_rating = fields.Boolean("Customer Ratings", implied_group='project.group_project_rating')
diff --git a/addons/project/tests/test_project_flow.py b/addons/project/tests/test_project_flow.py
index 5f1d8e3400c..fdb5450e12a 100644
--- a/addons/project/tests/test_project_flow.py
+++ b/addons/project/tests/test_project_flow.py
@@ -142,7 +142,7 @@ class TestProjectFlow(TestProjectCommon):
'parent_res_id': self.project_pigs.id,
'rated_partner_id': self.partner_2.id,
'partner_id': self.partner_2.id,
- 'rating': 10,
+ 'rating': 5,
'consumed': False,
})
@@ -153,7 +153,7 @@ class TestProjectFlow(TestProjectCommon):
'parent_res_id': self.project_pigs.id,
'rated_partner_id': self.partner_2.id,
'partner_id': self.partner_2.id,
- 'rating': 5,
+ 'rating': 3,
'consumed': True,
})
@@ -170,7 +170,7 @@ class TestProjectFlow(TestProjectCommon):
self.assertEqual(self.project_pigs.rating_percentage_satisfaction, 0) # There is a rating but not a "great" on, just an "okay".
# Consuming rating_good
- first_task.rating_apply(10, rating_good.access_token)
+ first_task.rating_apply(5, rating_good.access_token)
# We need to invalidate cache since it is not done automatically by the ORM
# Our One2Many is linked to a res_id (int) for which the orm doesn't create an inverse
diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index 2fd2c4ef026..72a73474862 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -89,6 +89,161 @@
</field>
</record>
+ <!-- Task types -->
+ <record id="task_type_search" model="ir.ui.view">
+ <field name="name">project.task.type.search</field>
+ <field name="model">project.task.type</field>
+ <field name="arch" type="xml">
+ <search string="Tasks Stages">
+ <field name="name" string="Tasks Stages"/>
+ <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="task_type_edit" model="ir.ui.view">
+ <field name="name">project.task.type.form</field>
+ <field name="model">project.task.type</field>
+ <field name="arch" type="xml">
+ <form string="Task Stage" delete="0">
+ <field name="active" invisible="1" />
+ <sheet>
+ <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}" />
+ <group>
+ <group>
+ <field name="name"/>
+ <field name="mail_template_id"/>
+ <field name="rating_template_id" groups="project.group_project_rating"/>
+ <div class="alert alert-warning" role="alert" colspan='2' attrs="{'invisible': ['|', ('rating_template_id','=', False), ('disabled_rating_warning', '=', False)]}">
+ <i class="fa fa-warning" title="Customer disabled on projects"/><b> Customer Ratings</b> are disabled on the following project(s) : <br/>
+ <field name="disabled_rating_warning" class="mb-0" />
+ </div>
+ <field name="auto_validation_kanban_state" attrs="{'invisible': [('rating_template_id','=', False)]}" groups="project.group_project_rating"/>
+ </group>
+ <group>
+ <field name="fold"/>
+ <field name="is_closed" groups="base.group_no_one"/>
+ <field name="project_ids" widget="many2many_tags" groups="base.group_no_one"/>
+ <field name="sequence" groups="base.group_no_one"/>
+ </group>
+ </group>
+ <group string="Stage Description and Tooltips">
+ <p class="text-muted" colspan="2">
+ At each stage employees can block or make task/issue ready for next stage.
+ You can define here labels that will be displayed for the state instead
+ of the default labels.
+ </p>
+ <label for="legend_normal" string=" " class="o_status oe_project_kanban_legend"
+ title="Task in progress. Click to block or set as done."
+ aria-label="Task in progress. Click to block or set as done." role="img"/>
+ <field name="legend_normal" nolabel="1"/>
+ <label for="legend_blocked" string=" " class="o_status o_status_red oe_project_kanban_legend"
+ title="Task is blocked. Click to unblock or set as done."
+ aria-label="Task is blocked. Click to unblock or set as done." role="img"/>
+ <field name="legend_blocked" nolabel="1"/>
+ <label for="legend_done" string=" " class="o_status o_status_green oe_project_kanban_legend"
+ title="This step is done. Click to block or set in progress."
+ aria-label="This step is done. Click to block or set in progress." role="img"/>
+ <field name="legend_done" nolabel="1"/>
+
+ <p class="text-muted" colspan="2">
+ You can also add a description to help your coworkers understand the meaning and purpose of the stage.
+ </p>
+ <field name="description" placeholder="Add a description..." nolabel="1" colspan="2"/>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="task_type_tree" model="ir.ui.view">
+ <field name="name">project.task.type.tree</field>
+ <field name="model">project.task.type</field>
+ <field name="arch" type="xml">
+ <tree string="Task Stage" delete="0">
+ <field name="sequence" widget="handle" optional="show"/>
+ <field name="name"/>
+ <field name="fold" optional="show"/>
+ <field name="mail_template_id" optional="show"/>
+ <field name="rating_template_id" optional="show"/>
+ <field name="auto_validation_kanban_state" optional="hide" />
+ <field name="description" optional="hide"/>
+ <field name="project_ids" optional="show" widget="many2many_tags" options="{'color_field': 'color'}" />
+ </tree>
+ </field>
+ </record>
+
+ <record id="view_project_task_type_kanban" model="ir.ui.view">
+ <field name="name">project.task.type.kanban</field>
+ <field name="model">project.task.type</field>
+ <field name="arch" type="xml">
+ <kanban class="o_kanban_mobile">
+ <field name="name"/>
+ <field name="fold"/>
+ <field name="description"/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_global_click">
+ <div class="row">
+ <div class="col-12">
+ <strong><t t-esc="record.name.value"/></strong>
+ </div>
+ </div>
+ <t t-if="record.description.value">
+ <hr class="mt8 mb8"/>
+ <t t-esc="record.description.value"/>
+ </t>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <record id="open_task_type_form" model="ir.actions.act_window">
+ <field name="name">Stages</field>
+ <field name="res_model">project.task.type</field>
+ <field name="view_mode">tree,kanban,form</field>
+ <field name="view_id" ref="task_type_tree"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_smiling_face">
+ Create a new stage in the task pipeline
+ </p><p>
+ Define the steps that will be used in the project from the
+ creation of the task, up to the closing of the task or issue.
+ You will use these stages in order to track the progress in
+ solving a task or an issue.
+ </p>
+ </field>
+ </record>
+
+ <record id="open_task_type_form_domain" model="ir.actions.act_window">
+ <field name="name">Stages</field>
+ <field name="res_model">project.task.type</field>
+ <field name="view_mode">tree,kanban,form</field>
+ <field name="domain">[('project_ids','=', project_id)]</field>
+ <field name="view_id" ref="task_type_tree"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_smiling_face">
+ Create a new stage in the task pipeline
+ </p><p>
+ Define the steps that will be used in the project from the
+ creation of the task, up to the closing of the task or issue.
+ You will use these stages in order to track the progress in
+ solving a task or an issue.
+ </p>
+ </field>
+ </record>
+
+ <record id="unlink_task_type_action" model="ir.actions.server">
+ <field name="name">Delete</field>
+ <field name="model_id" ref="project.model_project_task_type"/>
+ <field name="binding_model_id" ref="project.model_project_task_type"/>
+ <field name="binding_view_types">form,list</field>
+ <field name="state">code</field>
+ <field name="code">action = records.unlink_wizard(stage_view=True)</field>
+ </record>
+
<!-- Project -->
<record id="edit_project" model="ir.ui.view">
<field name="name">project.project.form</field>
@@ -107,16 +262,17 @@
name="%(act_project_project_2_project_task_all)d" icon="fa-tasks">
<field string="Tasks In Progress" name="task_count" widget="statinfo" options="{'label_field': 'label_tasks'}"/>
</button>
- <button name="action_view_all_rating" type="object" attrs="{'invisible': ['|', ('rating_status', '=', 'no'), ('rating_percentage_satisfaction', '=', -1)]}" class="oe_stat_button oe_percent" icon="fa-smile-o" groups="project.group_project_rating">
+ <button name="action_view_all_rating" type="object" attrs="{'invisible': ['|', ('rating_active', '=', False), ('rating_percentage_satisfaction', '=', -1)]}" class="oe_stat_button oe_percent oe_read_only" icon="fa-smile-o" groups="project.group_project_rating">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="rating_percentage_satisfaction" nolabel="1"/>
+ %
</span>
<span class="o_stat_text">
- % On <field readonly="1" name="label_tasks" options="{'label_field': 'label_tasks'}" />
+ Customer Satisfaction
</span>
</div>
- </button>
+ </button>
</div>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<div class="oe_title">
@@ -151,23 +307,24 @@
</group>
<div class="row mt16 o_settings_container">
<div id="rating_settings" class="col-lg-6 o_setting_box" groups="project.group_project_rating">
+ <div class="o_setting_left_pane">
+ <field name="rating_active"/>
+ </div>
<div class="o_setting_right_pane">
- <label for="rating_status"/>
+ <label for="rating_active" />
<div class="text-muted">
Get customer feedback
</div>
- <div class="mt16">
- <field name="rating_status" widget="radio"/>
- <p attrs="{'invisible': [('rating_status','not in',('periodic','stage'))]}" class="text-muted oe_edit_only">
- Edit project's stages and set an email template on the stages on which you want to activate the rating.
- </p>
+ <div class="mt16" attrs="{'invisible':[('rating_active','==',False)]}">
+ <field name="rating_status" widget="radio" />
<div attrs="{'required': [('rating_status','=','periodic')], 'invisible': [('rating_status','!=','periodic')]}">
<label for="rating_status_period"/>
- <field name="rating_status_period" class="oe_inline"/>
+ <field name="rating_status_period"/>
</div>
- <div attrs="{'invisible': [('rating_status','==','no')]}">
- <label for="portal_show_rating"/>
- <field name="portal_show_rating"/>
+ <div class="content-group">
+ <div class="mt8">
+ <button name="%(project.open_task_type_form_domain)d" context="{'project_id':id}" icon="fa-arrow-right" type="action" string="Set a Rating Email Template on Stages" class="btn-link"/>
+ </div>
</div>
</div>
</div>
@@ -352,6 +509,7 @@
<field name="is_favorite"/>
<field name="rating_percentage_satisfaction"/>
<field name="rating_status"/>
+ <field name="rating_active" />
<field name="analytic_account_id"/>
<templates>
<t t-name="kanban-box">
@@ -368,16 +526,13 @@
<div t-if="record.alias_name.value and record.alias_domain.value">
<span><i class="fa fa-envelope" role="img" aria-label="Domain Alias" title="Domain Alias"></i> <t t-esc="record.alias_id.value"/></span>
</div>
- <div t-if="record.rating_status.raw_value != 'no'" class="mt8 text-primary" title="Percentage of happy ratings over the past 30 days. Get rating details from the More menu." groups="project.group_project_rating">
+ <div t-if="record.rating_active.raw_value" class="mt8 text-primary" title="Percentage of happy ratings over the past 30 days." groups="project.group_project_rating">
<b>
- <t t-if="record.rating_percentage_satisfaction.value == -1">
- <i class="fa fa-smile-o"/> No rating yet
- </t>
- <t t-if="record.rating_percentage_satisfaction.value != -1">
- <a name="action_view_all_rating" type="object" context="{'search_default_rating_last_30_days':1}">
+ <a name="action_view_all_rating" type="object" context="{'search_default_rating_last_30_days':1}">
+ <t t-if="record.rating_percentage_satisfaction.value != -1">
<i class="fa fa-smile-o" role="img" aria-label="Percentage of satisfaction" title="Percentage of satisfaction"/> <t t-esc="record.rating_percentage_satisfaction.value"/>%
- </a>
- </t>
+ </t>
+ </a>
</b>
</div>
</div>
@@ -390,9 +545,6 @@
<div role="menuitem">
<a type="edit">Edit</a>
</div>
- <div role="menuitem" t-if="record.rating_status.raw_value != 'no'">
- <a name="action_view_all_rating" type="object">Customer Ratings</a>
- </div>
</div>
<div role="menuitem" aria-haspopup="true" class="o_no_padding_kanban_colorpicker">
<ul class="oe_kanban_colorpicker" data-field="color" role="popup"/>
@@ -674,8 +826,8 @@
<span role="alert" class='oe_kanban_mail_new' title='Unread Messages'><i class='fa fa-comments' role="img" aria-label="Unread Messages"/><t t-raw="record.message_needaction_counter.raw_value"/></span>
</t>
<b t-if="record.rating_ids.raw_value.length">
- <span style="font-weight:bold;" class="fa fa-fw mt4 fa-smile-o text-success" t-if="record.rating_last_value.value == 10" title="Latest Rating: Satisfied" role="img" aria-label="Happy face"/>
- <span style="font-weight:bold;" class="fa fa-fw mt4 fa-meh-o text-warning" t-if="record.rating_last_value.value == 5" title="Latest Rating: Not Satisfied" role="img" aria-label="Neutral face"/>
+ <span style="font-weight:bold;" class="fa fa-fw mt4 fa-smile-o text-success" t-if="record.rating_last_value.value == 5" title="Latest Rating: Satisfied" role="img" aria-label="Happy face"/>
+ <span style="font-weight:bold;" class="fa fa-fw mt4 fa-meh-o text-warning" t-if="record.rating_last_value.value == 3" title="Latest Rating: Not Satisfied" role="img" aria-label="Neutral face"/>
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-frown-o text-danger" t-if="record.rating_last_value.value == 1" title="Latest Rating: Higly Dissatisfied" role="img" aria-label="Sad face"/>
</b>
<!-- formating of the date -->
@@ -874,135 +1026,7 @@
<field name="context">{'project_id':active_id}</field>
</record>
- <!-- Task types -->
- <record id="task_type_search" model="ir.ui.view">
- <field name="name">project.task.type.search</field>
- <field name="model">project.task.type</field>
- <field name="arch" type="xml">
- <search string="Tasks Stages">
- <field name="name" string="Tasks Stages"/>
- <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
- </search>
- </field>
- </record>
-
- <record id="task_type_edit" model="ir.ui.view">
- <field name="name">project.task.type.form</field>
- <field name="model">project.task.type</field>
- <field name="arch" type="xml">
- <form string="Task Stage" delete="0">
- <field name="active" invisible="1" />
- <sheet>
- <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}" />
- <group>
- <group>
- <field name="name"/>
- <field name="mail_template_id"/>
- <field name="rating_template_id" groups="project.group_project_rating"/>
- <field name="auto_validation_kanban_state" attrs="{'invisible': [('rating_template_id','=', False)]}" groups="project.group_project_rating"/>
- </group>
- <group>
- <field name="fold"/>
- <field name="is_closed" groups="base.group_no_one"/>
- <field name="project_ids" widget="many2many_tags" groups="base.group_no_one"/>
- <field name="sequence" groups="base.group_no_one"/>
- </group>
- </group>
- <group string="Stage Description and Tooltips">
- <p class="text-muted" colspan="2">
- At each stage employees can block or make task/issue ready for next stage.
- You can define here labels that will be displayed for the state instead
- of the default labels.
- </p>
- <label for="legend_normal" string=" " class="o_status oe_project_kanban_legend"
- title="Task in progress. Click to block or set as done."
- aria-label="Task in progress. Click to block or set as done." role="img"/>
- <field name="legend_normal" nolabel="1"/>
- <label for="legend_blocked" string=" " class="o_status o_status_red oe_project_kanban_legend"
- title="Task is blocked. Click to unblock or set as done."
- aria-label="Task is blocked. Click to unblock or set as done." role="img"/>
- <field name="legend_blocked" nolabel="1"/>
- <label for="legend_done" string=" " class="o_status o_status_green oe_project_kanban_legend"
- title="This step is done. Click to block or set in progress."
- aria-label="This step is done. Click to block or set in progress." role="img"/>
- <field name="legend_done" nolabel="1"/>
-
- <p class="text-muted" colspan="2">
- You can also add a description to help your coworkers understand the meaning and purpose of the stage.
- </p>
- <field name="description" placeholder="Add a description..." nolabel="1" colspan="2"/>
- </group>
- </sheet>
- </form>
- </field>
- </record>
-
- <record id="task_type_tree" model="ir.ui.view">
- <field name="name">project.task.type.tree</field>
- <field name="model">project.task.type</field>
- <field name="arch" type="xml">
- <tree string="Task Stage" delete="0">
- <field name="sequence" widget="handle" groups="base.group_no_one"/>
- <field name="name"/>
- <field name="fold"/>
- <field name="description"/>
- </tree>
- </field>
- </record>
-
- <record id="view_project_task_type_kanban" model="ir.ui.view">
- <field name="name">project.task.type.kanban</field>
- <field name="model">project.task.type</field>
- <field name="arch" type="xml">
- <kanban class="o_kanban_mobile">
- <field name="name"/>
- <field name="fold"/>
- <field name="description"/>
- <templates>
- <t t-name="kanban-box">
- <div t-attf-class="oe_kanban_global_click">
- <div class="row">
- <div class="col-12">
- <strong><t t-esc="record.name.value"/></strong>
- </div>
- </div>
- <t t-if="record.description.value">
- <hr class="mt8 mb8"/>
- <t t-esc="record.description.value"/>
- </t>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <record id="open_task_type_form" model="ir.actions.act_window">
- <field name="name">Stages</field>
- <field name="res_model">project.task.type</field>
- <field name="view_mode">tree,kanban,form</field>
- <field name="view_id" ref="task_type_tree"/>
- <field name="help" type="html">
- <p class="o_view_nocontent_smiling_face">
- Create a new stage in the task pipeline
- </p><p>
- Define the steps that will be used in the project from the
- creation of the task, up to the closing of the task or issue.
- You will use these stages in order to track the progress in
- solving a task or an issue.
- </p>
- </field>
- </record>
-
- <record id="unlink_task_type_action" model="ir.actions.server">
- <field name="name">Delete</field>
- <field name="model_id" ref="project.model_project_task_type"/>
- <field name="binding_model_id" ref="project.model_project_task_type"/>
- <field name="binding_view_types">form,list</field>
- <field name="state">code</field>
- <field name="code">action = records.unlink_wizard(stage_view=True)</field>
- </record>
-
+ <!-- Menu item for project -->
<menuitem id="menu_tasks_config" name="GTD" parent="menu_project_config" sequence="2"/>
<menuitem action="open_task_type_form" id="menu_project_config_project" name="Stages" parent="menu_project_config" sequence="3" groups="base.group_no_one"/>
diff --git a/addons/project/views/rating_views.xml b/addons/project/views/rating_views.xml
index 2cfe3dc4e47..7d7da90fa75 100644
--- a/addons/project/views/rating_views.xml
+++ b/addons/project/views/rating_views.xml
@@ -12,8 +12,7 @@
</xpath>
<xpath expr="/search" position="inside">
<filter string="Last 30 Days" name="rating_last_30_days" domain="[
- ('create_date', '>=', (datetime.datetime.combine(context_today() + relativedelta(days=-30), datetime.time(0,0,0)).to_utc()).strftime('%Y-%m-%d %H:%M:%S')),
- ('create_date', '&lt;', (datetime.datetime.combine(context_today(), datetime.time(0,0,0)).to_utc()).strftime('%Y-%m-%d %H:%M:%S'))]"
+ ('create_date', '>=', (datetime.datetime.combine(context_today() + relativedelta(days=-30), datetime.time(0,0,0)).to_utc()).strftime('%Y-%m-%d %H:%M:%S'))]"
/>
<separator/>
</xpath>
@@ -28,7 +27,7 @@
<field name="search_view_id" ref="rating_rating_view_search_project"/>
<field name="help" type="html">
<p class="o_view_nocontent_empty_folder">
- There are no ratings for this object at the moment
+ There are no ratings for this project at the moment
</p>
</field>
</record>
diff --git a/addons/project/views/res_config_settings_views.xml b/addons/project/views/res_config_settings_views.xml
index 759291fc286..8346330c8c7 100644
--- a/addons/project/views/res_config_settings_views.xml
+++ b/addons/project/views/res_config_settings_views.xml
@@ -44,7 +44,7 @@
</div>
<div class="content-group" attrs="{'invisible': [('group_project_rating', '=', False)]}">
<div class="mt8">
- <button name="%(project.open_task_type_form)d" icon="fa-arrow-right" type="action" string="Set Email Template to Stages" class="btn-link"/>
+ <button name="%(project.open_task_type_form)d" icon="fa-arrow-right" type="action" string="Set a Rating Email Template on Stages" class="btn-link"/>
</div>
</div>
</div>
diff --git a/addons/rating/controllers/main.py b/addons/rating/controllers/main.py
index 5c1f7f5f4ce..e27fc6b7c53 100644
--- a/addons/rating/controllers/main.py
+++ b/addons/rating/controllers/main.py
@@ -11,13 +11,13 @@ class Rating(http.Controller):
@http.route('/rating/<string:token>/<int:rate>', type='http', auth="public", website=True)
def open_rating(self, token, rate, **kwargs):
- assert rate in (1, 5, 10), "Incorrect rating"
+ assert rate in (1, 3, 5), "Incorrect rating"
rating = request.env['rating.rating'].sudo().search([('access_token', '=', token)])
if not rating:
return request.not_found()
rate_names = {
- 10: _("satisfied"),
- 5: _("not satisfied"),
+ 5: _("satisfied"),
+ 3: _("not satisfied"),
1: _("highly dissatisfied")
}
rating.write({'rating': rate, 'consumed': True})
@@ -30,7 +30,7 @@ class Rating(http.Controller):
@http.route(['/rating/<string:token>/submit_feedback'], type="http", auth="public", methods=['post'], website=True)
def submit_rating(self, token, **kwargs):
rate = int(kwargs.get('rate'))
- assert rate in (1, 5, 10), "Incorrect rating"
+ assert rate in (1, 3, 5), "Incorrect rating"
rating = request.env['rating.rating'].sudo().search([('access_token', '=', token)])
if not rating:
return request.not_found()
diff --git a/addons/rating/models/rating.py b/addons/rating/models/rating.py
index 391c6781626..7c0ee789177 100644
--- a/addons/rating/models/rating.py
+++ b/addons/rating/models/rating.py
@@ -7,7 +7,7 @@ from odoo import api, fields, models
from odoo.modules.module import get_resource_path
-RATING_LIMIT_SATISFIED = 7
+RATING_LIMIT_SATISFIED = 5
RATING_LIMIT_OK = 3
RATING_LIMIT_MIN = 1
@@ -18,7 +18,7 @@ class Rating(models.Model):
_order = 'write_date desc'
_rec_name = 'res_name'
_sql_constraints = [
- ('rating_range', 'check(rating >= 0 and rating <= 10)', 'Rating should be between 0 to 10'),
+ ('rating_range', 'check(rating >= 0 and rating <= 5)', 'Rating should be between 0 to 5'),
]
@api.depends('res_model', 'res_id')
@@ -41,7 +41,7 @@ class Rating(models.Model):
parent_res_id = fields.Integer('Parent Document', index=True)
rated_partner_id = fields.Many2one('res.partner', string="Rated person", help="Owner of the rated resource")
partner_id = fields.Many2one('res.partner', string='Customer', help="Author of the rating")
- rating = fields.Float(string="Rating Number", group_operator="avg", default=0, help="Rating value: 0=Unhappy, 10=Happy")
+ rating = fields.Float(string="Rating Number", group_operator="avg", default=0, help="Rating value: 0=Unhappy, 5=Happy")
rating_image = fields.Binary('Image', compute='_compute_rating_image')
rating_text = fields.Selection([
('satisfied', 'Satisfied'),
@@ -66,21 +66,23 @@ class Rating(models.Model):
name = name and name[0][1] or ('%s/%s') % (rating.parent_res_model, rating.parent_res_id)
rating.parent_res_name = name
- @api.depends('rating')
+ def _get_rating_image_filename(self):
+ self.ensure_one()
+ if self.rating >= RATING_LIMIT_SATISFIED:
+ rating_int = 5
+ elif self.rating >= RATING_LIMIT_OK:
+ rating_int = 3
+ elif self.rating >= RATING_LIMIT_MIN:
+ rating_int = 1
+ else:
+ rating_int = 0
+ return 'rating_%s.png' % rating_int
+
def _compute_rating_image(self):
- # Due to some new widgets, we may have ratings different from 0/1/5/10 (e.g. slide.channel review)
- # Let us have some custom rounding while finding a better solution for images.
for rating in self:
- rating_for_img = 0
- if rating.rating >= 8:
- rating_for_img = 10
- elif rating.rating > 3:
- rating_for_img = 5
- elif rating.rating >= 1:
- rating_for_img = 1
try:
- image_path = get_resource_path('rating', 'static/src/img', 'rating_%s.png' % rating_for_img)
- rating.rating_image = base64.b64encode(open(image_path, 'rb').read())
+ image_path = get_resource_path('rating', 'static/src/img', rating._get_rating_image_filename())
+ rating.rating_image = base64.b64encode(open(image_path, 'rb').read()) if image_path else False
except (IOError, OSError):
rating.rating_image = False
@@ -89,7 +91,7 @@ class Rating(models.Model):
for rating in self:
if rating.rating >= RATING_LIMIT_SATISFIED:
rating.rating_text = 'satisfied'
- elif rating.rating > RATING_LIMIT_OK:
+ elif rating.rating >= RATING_LIMIT_OK:
rating.rating_text = 'not_satisfied'
elif rating.rating >= RATING_LIMIT_MIN:
rating.rating_text = 'highly_dissatisfied'
diff --git a/addons/rating/models/rating_mixin.py b/addons/rating/models/rating_mixin.py
index c95bc3d5ed1..e0cf0e6837e 100644
--- a/addons/rating/models/rating_mixin.py
+++ b/addons/rating/models/rating_mixin.py
@@ -199,9 +199,9 @@ class RatingMixin(models.AbstractModel):
author_id=rating.partner_id and rating.partner_id.id or None # None will set the default author in mail_thread.py
)
if hasattr(self, 'stage_id') and self.stage_id and hasattr(self.stage_id, 'auto_validation_kanban_state') and self.stage_id.auto_validation_kanban_state:
- if rating.rating > 5:
+ if rating.rating > 2:
self.write({'kanban_state': 'done'})
- if rating.rating < 5:
+ else:
self.write({'kanban_state': 'blocked'})
return rating
@@ -222,7 +222,7 @@ class RatingMixin(models.AbstractModel):
base_domain += domain
data = self.env['rating.rating'].read_group(base_domain, ['rating'], ['rating', 'res_id'])
# init dict with all posible rate value, except 0 (no value for the rating)
- values = dict.fromkeys(range(1, 11), 0)
+ values = dict.fromkeys(range(1, 6), 0)
values.update((d['rating'], d['rating_count']) for d in data)
# add other stats
if add_stats:
diff --git a/addons/rating/static/src/img/rating_10.png b/addons/rating/static/src/img/rating_10.png
deleted file mode 100644
index 3af034234c0..00000000000
--- a/addons/rating/static/src/img/rating_10.png
+++ /dev/null
Binary files differ
diff --git a/addons/rating/static/src/img/rating_10_white.png b/addons/rating/static/src/img/rating_10_white.png
deleted file mode 100644
index d756eee931f..00000000000
--- a/addons/rating/static/src/img/rating_10_white.png
+++ /dev/null
Binary files differ
diff --git a/addons/rating/static/src/img/rating_3.png b/addons/rating/static/src/img/rating_3.png
new file mode 100644
index 00000000000..8ec3a2a7325
--- /dev/null
+++ b/addons/rating/static/src/img/rating_3.png
Binary files differ
diff --git a/addons/rating/static/src/img/rating_3_white.png b/addons/rating/static/src/img/rating_3_white.png
new file mode 100644
index 00000000000..d146dc5b37d
--- /dev/null
+++ b/addons/rating/static/src/img/rating_3_white.png
Binary files differ
diff --git a/addons/rating/static/src/img/rating_5.png b/addons/rating/static/src/img/rating_5.png
index 8ec3a2a7325..3af034234c0 100644
--- a/addons/rating/static/src/img/rating_5.png
+++ b/addons/rating/static/src/img/rating_5.png
Binary files differ
diff --git a/addons/rating/static/src/img/rating_5_white.png b/addons/rating/static/src/img/rating_5_white.png
index d146dc5b37d..d756eee931f 100644
--- a/addons/rating/static/src/img/rating_5_white.png
+++ b/addons/rating/static/src/img/rating_5_white.png
Binary files differ
diff --git a/addons/rating/tests/test_security.py b/addons/rating/tests/test_security.py
index 44d47c9e4cf..440ce58396d 100644
--- a/addons/rating/tests/test_security.py
+++ b/addons/rating/tests/test_security.py
@@ -51,14 +51,14 @@ class TestAccessRating(common.SavepointCase):
'res_model_id': self.env['ir.model'].sudo().search([('model', '=', 'res.partner')], limit=1).id,
'res_model': 'res.partner',
'res_id': self.partner_to_rate.id,
- 'rating': 2
+ 'rating': 1
})
with self.assertRaises(AccessError):
self.env['rating.rating'].with_user(self.user_public).create({
'res_model_id': self.env['ir.model'].sudo().search([('model', '=', 'res.partner')], limit=1).id,
'res_model': 'res.partner',
'res_id': self.partner_to_rate.id,
- 'rating': 5
+ 'rating': 3
})
# No error with employee
@@ -66,7 +66,7 @@ class TestAccessRating(common.SavepointCase):
'res_model_id': self.env['ir.model'].sudo().search([('model', '=', 'res.partner')], limit=1).id,
'res_model': 'res.partner',
'res_id': self.partner_to_rate.id,
- 'rating': 5
+ 'rating': 3
})
with self.assertRaises(AccessError):
diff --git a/addons/website_livechat/data/website_livechat_data.xml b/addons/website_livechat/data/website_livechat_data.xml
index 556079a5970..46db5021300 100644
--- a/addons/website_livechat/data/website_livechat_data.xml
+++ b/addons/website_livechat/data/website_livechat_data.xml
@@ -4,9 +4,5 @@
<record id="website.default_website" model="website">
<field name="channel_id" ref="im_livechat.im_livechat_channel_data"></field>
</record>
-
- <record id="im_livechat.im_livechat_channel_data" model="im_livechat.channel">
- <field name="is_published" eval="True"/>
- </record>
</data>
</odoo>
diff --git a/addons/website_livechat/static/tests/tours/website_livechat_common.js b/addons/website_livechat/static/tests/tours/website_livechat_common.js
index 446b1fa8fc9..1ec61f700ec 100644
--- a/addons/website_livechat/static/tests/tours/website_livechat_common.js
+++ b/addons/website_livechat/static/tests/tours/website_livechat_common.js
@@ -132,7 +132,7 @@ var closeStep = [{
var goodRatingStep = [{
content: "Send Good Rating",
- trigger: "div.o_livechat_rating_choices > img[data-value=10]",
+ trigger: "div.o_livechat_rating_choices > img[data-value=5]",
}, {
content: "Check if feedback has been sent",
trigger: "div.o_thread_window_header.feedback_sent",
@@ -143,7 +143,7 @@ var goodRatingStep = [{
var okRatingStep = [{
content: "Send ok Rating",
- trigger: "div.o_livechat_rating_choices > img[data-value=5]",
+ trigger: "div.o_livechat_rating_choices > img[data-value=3]",
}];
var sadRatingStep = [{
diff --git a/addons/website_livechat/tests/common.py b/addons/website_livechat/tests/common.py
index 800dde98b4e..9cc162ff709 100644
--- a/addons/website_livechat/tests/common.py
+++ b/addons/website_livechat/tests/common.py
@@ -69,7 +69,7 @@ class TestLivechatCommon(tests.TransactionCase):
def _send_rating(self, channel, visitor, rating_value, reason=False):
channel_messages_count = len(channel.message_ids)
- rating_to_emoji = {1: "😞", 5: "😐", 10: "😊"}
+ rating_to_emoji = {1: "😞", 3: "😐", 5: "😊"}
self.opener.post(url=self.send_feedback_url, json={'params': {
'uuid': channel.uuid,
'rate': rating_value,
diff --git a/addons/website_livechat/tests/test_livechat_request.py b/addons/website_livechat/tests/test_livechat_request.py
index 3e4025e4524..f50780da6a6 100644
--- a/addons/website_livechat/tests/test_livechat_request.py
+++ b/addons/website_livechat/tests/test_livechat_request.py
@@ -13,7 +13,7 @@ class TestLivechatRequestHttpCase(tests.HttpCase, TestLivechatCommon):
# Send first chat request - Open chat from operator side
channel_1 = self._common_chat_request_flow()
# Visitor Rates the conversation (Good)
- self._send_rating(channel_1, self.visitor, 10)
+ self._send_rating(channel_1, self.visitor, 5)
# Operator Re-Send a chat request
channel_2 = self._common_chat_request_flow()
diff --git a/addons/website_livechat/views/website_livechat.xml b/addons/website_livechat/views/website_livechat.xml
index 23c27eadb10..f961e862679 100644
--- a/addons/website_livechat/views/website_livechat.xml
+++ b/addons/website_livechat/views/website_livechat.xml
@@ -80,7 +80,7 @@
<div class="col-lg-4 d-flex justify-content-end flex-column">
<div class="card bg-success text-white" t-attf-style="height: #{160 + int(percentage['great'])}px;">
<div class="card-body text-center">
- <img src="/rating/static/src/img/rating_10.png" style="height:40px" alt="Happy face"/>
+ <img src="/rating/static/src/img/rating_5.png" style="height:40px" alt="Happy face"/>
</div>
<div class="card-body text-center">
<h2 style="margin: 0">
@@ -95,7 +95,7 @@
<div class="col-lg-4 d-flex justify-content-end flex-column">
<div class="card bg-warning text-white" t-attf-style="height: #{160 + int(percentage['okay'])}px;">
<div class="card-body text-center">
- <img src="/rating/static/src/img/rating_5.png" style="height:40px" alt="Neutral face"/>
+ <img src="/rating/static/src/img/rating_3.png" style="height:40px" alt="Neutral face"/>
</div>
<div class="card-body text-center">
<h2 style="margin: 0">
@@ -131,7 +131,7 @@
<div>
<t t-foreach="ratings" t-as="rating">
<img t-attf-src='/rating/static/src/img/rating_#{int(rating.rating)}.png' t-att-alt="rating.res_name" width="48px" height="48px"/>
- <t t-if="(rating_index+1) % 10 == 0">
+ <t t-if="(rating_index+1) % 5 == 0">
<br/>
</t>
</t>
@@ -161,11 +161,11 @@
<div class="row">
<t t-if="user.partner_id.id in ratings_per_user">
<div class="col-lg-4 pl-0 pr-0">
- <img t-attf-src='/rating/static/src/img/rating_10.png' alt="Great" width="16px" height="16px"/>
+ <img t-attf-src='/rating/static/src/img/rating_5.png' alt="Great" width="16px" height="16px"/>
<span class="align-middle"><t t-esc="ratings_per_user[user.partner_id.id]['great']"/>%</span>
</div>
<div class="col-lg-4 pl-0 pr-0">
- <img t-attf-src='/rating/static/src/img/rating_5.png' alt="Okay" width="16px" height="16px"/>
+ <img t-attf-src='/rating/static/src/img/rating_3.png' alt="Okay" width="16px" height="16px"/>
<span class="align-middle"><t t-esc="ratings_per_user[user.partner_id.id]['okay']"/>%</span>
</div>
<div class="col-lg-4 pl-0 pr-0">
diff --git a/addons/website_project/__init__.py b/addons/website_project/__init__.py
new file mode 100644
index 00000000000..7d34c7c054a
--- /dev/null
+++ b/addons/website_project/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import controllers
+from . import models
diff --git a/addons/website_project/__manifest__.py b/addons/website_project/__manifest__.py
new file mode 100644
index 00000000000..cb467db65e6
--- /dev/null
+++ b/addons/website_project/__manifest__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ 'name': 'Website Project',
+ 'category': 'Website/Website',
+ 'summary': 'Publish your project ratings',
+ 'version': '1.0',
+ 'description': """
+Allows to publish all ratings for a particular project.""",
+ 'depends': ['website', 'project'],
+ 'data': [
+ 'views/project_rating_templates.xml',
+ 'views/project_project_views.xml',
+ ],
+ 'installable': True,
+ 'auto_install': True,
+}
diff --git a/addons/website_project/controllers/__init__.py b/addons/website_project/controllers/__init__.py
new file mode 100644
index 00000000000..7367c6c8192
--- /dev/null
+++ b/addons/website_project/controllers/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import rating
diff --git a/addons/project/controllers/rating.py b/addons/website_project/controllers/rating.py
index c57734e6a99..7e0c3d5dda2 100644
--- a/addons/project/controllers/rating.py
+++ b/addons/website_project/controllers/rating.py
@@ -11,9 +11,11 @@ class RatingProject(http.Controller):
@http.route(['/project/rating'], type='http', auth="public", website=True, sitemap=True)
def index(self, **kw):
- projects = request.env['project.project'].sudo().search([('rating_status', '!=', 'no'), ('portal_show_rating', '=', True)])
+ projects = request.env['project.project'].sudo().search([
+ ('rating_active', '=', True),
+ ('is_published', '=', True)])
values = {'projects': projects}
- return request.render('project.rating_index', values)
+ return request.render('website_project.rating_index', values)
def _calculate_period_partner_stats(self, project_id):
# get raw data: number of rating by rated partner, by rating value, by period
@@ -36,7 +38,7 @@ class RatingProject(http.Controller):
AND res_model = 'project.task'
AND rated_partner_id IS NOT NULL
AND write_date >= current_date - interval '90' day
- AND rating IN (1,5,10)
+ AND rating IN (1, 3, 5)
GROUP BY
rated_partner_id, rating, period
""", (project_id, ))
@@ -44,7 +46,7 @@ class RatingProject(http.Controller):
raw_data = request.env.cr.dictfetchall()
# periodical statistics
- default_period_dict = {'rating_10': 0, 'rating_5': 0, 'rating_1': 0, 'total': 0}
+ default_period_dict = {'rating_5': 0, 'rating_3': 0, 'rating_1': 0, 'total': 0}
period_statistics = {
'days_06': dict(default_period_dict),
'days_15': dict(default_period_dict),
@@ -58,7 +60,7 @@ class RatingProject(http.Controller):
period_statistics[period_statistics_key]['total'] += row['rating_count']
# partner statistics
- default_partner_dict = {'rating_10': 0, 'rating_5': 0, 'rating_1': 0, 'total': 0, 'rated_partner': None, 'percentage_happy': 0.0}
+ default_partner_dict = {'rating_5': 0, 'rating_3': 0, 'rating_1': 0, 'total': 0, 'rated_partner': None, 'percentage_happy': 0.0}
partner_statistics = {}
for row in raw_data:
if row['period'] <= 'days_15':
@@ -69,7 +71,7 @@ class RatingProject(http.Controller):
partner_statistics[row['rated_partner_id']]['total'] += row['rating_count']
for partner_id, stat_values in partner_statistics.items():
- stat_values['percentage_happy'] = (stat_values['rating_10'] / float(stat_values['total'])) * 100 if stat_values['total'] else 0.0
+ stat_values['percentage_happy'] = (stat_values['rating_5'] / float(stat_values['total'])) * 100 if stat_values['total'] else 0.0
return {
'partner_statistics': partner_statistics,
@@ -82,10 +84,10 @@ class RatingProject(http.Controller):
project = request.env['project.project'].sudo().browse(project_id)
# to avoid giving any access rights on projects to the public user, let's use sudo
# and check if the user should be able to view the project (project managers only if it's unpublished or has no rating)
- if not ((project.rating_status != 'no') and project.portal_show_rating) and not user.with_user(user).has_group('project.group_project_manager'):
+ if not (project.rating_active and project.is_published) and not user.with_user(user).has_group('project.group_project_manager'):
raise NotFound()
- return request.render('project.rating_project_rating_page', {
+ return request.render('website_project.rating_project_rating_page', {
'project': project,
'ratings': request.env['rating.rating'].sudo().search([
('consumed', '=', True),
diff --git a/addons/website_project/models/__init__.py b/addons/website_project/models/__init__.py
new file mode 100644
index 00000000000..527e2954c06
--- /dev/null
+++ b/addons/website_project/models/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import project
diff --git a/addons/website_project/models/project.py b/addons/website_project/models/project.py
new file mode 100644
index 00000000000..ded92b699a2
--- /dev/null
+++ b/addons/website_project/models/project.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+
+
+class Project(models.Model):
+ _name = 'project.project'
+ _inherit = ['project.project', 'website.published.mixin']
+
+ def action_view_all_rating(self):
+ if self.website_published:
+ return self.open_website_url()
+ return super(Project, self).action_view_all_rating()
+
+ def open_website_url(self):
+ """ return the action to see all the rating of the project, and activate default filters """
+ return {
+ 'type': 'ir.actions.act_url',
+ 'name': "Redirect to the Website Project Rating Page",
+ 'target': 'self',
+ 'url': "/project/rating/%s" % (self.id)
+ }
diff --git a/addons/website_project/views/project_project_views.xml b/addons/website_project/views/project_project_views.xml
new file mode 100644
index 00000000000..354adc30d5b
--- /dev/null
+++ b/addons/website_project/views/project_project_views.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+ <record id="project_project_view_form" model="ir.ui.view">
+ <field name="name">project.project.form</field>
+ <field name="model">project.project</field>
+ <field name="inherit_id" ref="project.edit_project"/>
+ <field name="arch" type="xml">
+ <xpath expr="//div[@name='button_box']" position="inside">
+ <field name="is_published"
+ widget="website_redirect_button"
+ groups="project.group_project_rating"
+ attrs="{'invisible': [('rating_active', '=', False)]}"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/project/views/project_rating_templates.xml b/addons/website_project/views/project_rating_templates.xml
index a1b8613ccb3..197bf781b30 100644
--- a/addons/project/views/project_rating_templates.xml
+++ b/addons/website_project/views/project_rating_templates.xml
@@ -58,15 +58,15 @@
<!-- Page : Project Rating Page (and widgets) -->
<template id="portal_project_rating_progressbar">
<div class="progress o_rating_progress">
- <t t-set="percent_rating_10" t-value="(stats['rating_10'] / float(stats['total'])) * 100 if stats['total'] else 0.0"/>
- <div class="progress-bar bg-success" t-attf-style="width: #{percent_rating_10}%;" title="Happy">
- <t t-esc="percent_rating_10"/>%
- </div>
- <t t-set="percent_rating_5" t-value="(stats['rating_5'] / float(stats['total'])) * 100 if stats['total'] else 0.0"/>
- <div class="progress-bar bg-warning" t-attf-style="width: #{percent_rating_5}%;" title="Average">
+ <t t-set="percent_rating_5" t-value="int(round((stats['rating_5'] / float(stats['total'])) * 100)) if stats['total'] else 0.0"/>
+ <div class="progress-bar bg-success" t-attf-style="width: #{percent_rating_5}%;" title="Happy">
<t t-esc="percent_rating_5"/>%
</div>
- <t t-set="percent_rating_1" t-value="(stats['rating_1'] / float(stats['total'])) * 100 if stats['total'] else 0.0"/>
+ <t t-set="percent_rating_3" t-value="int(round((stats['rating_3'] / float(stats['total'])) * 100)) if stats['total'] else 0.0"/>
+ <div class="progress-bar bg-warning" t-attf-style="width: #{percent_rating_3}%;" title="Average">
+ <t t-esc="percent_rating_3"/>%
+ </div>
+ <t t-set="percent_rating_1" t-value="int(round((stats['rating_1'] / float(stats['total'])) * 100)) if stats['total'] else 0.0"/>
<div class="progress-bar bg-danger" t-attf-style="width: #{percent_rating_1}%;" title="Bad">
<t t-esc="percent_rating_1"/>%
</div>
@@ -85,26 +85,26 @@
</div>
<div class="col-md-4 o_smiley_no_padding_right">
<div class="col-md-4 o_smiley_no_padding_right o_smiley_no_padding_left">
- <span t-if="partner_stat['rating_10']">
- <img title="happy" alt="Happy face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_10.png'/>
- <span class="o_rating_count" t-esc="partner_stat['rating_10']"/>
- </span>
- <span t-if="not partner_stat['rating_10']">
- <img title="happy" alt="Happy face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_10.png'/>
- <span class="o_rating_count o_lighter_smileys" t-esc="partner_stat['rating_10']"/>
- </span>
- </div>
- <div class="col-md-4 o_smiley_no_padding_right o_smiley_no_padding_left">
<span t-if="partner_stat['rating_5']">
- <img title="average" alt="Neutral face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_5.png'/>
+ <img title="happy" alt="Happy face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_5.png'/>
<span class="o_rating_count" t-esc="partner_stat['rating_5']"/>
</span>
<span t-if="not partner_stat['rating_5']">
- <img title="average" alt="Neutral face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_5.png'/>
+ <img title="happy" alt="Happy face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_5.png'/>
<span class="o_rating_count o_lighter_smileys" t-esc="partner_stat['rating_5']"/>
</span>
</div>
<div class="col-md-4 o_smiley_no_padding_right o_smiley_no_padding_left">
+ <span t-if="partner_stat['rating_3']">
+ <img title="average" alt="Neutral face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_3.png'/>
+ <span class="o_rating_count" t-esc="partner_stat['rating_3']"/>
+ </span>
+ <span t-if="not partner_stat['rating_3']">
+ <img title="average" alt="Neutral face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_3.png'/>
+ <span class="o_rating_count o_lighter_smileys" t-esc="partner_stat['rating_3']"/>
+ </span>
+ </div>
+ <div class="col-md-4 o_smiley_no_padding_right o_smiley_no_padding_left">
<span t-if="partner_stat['rating_1']">
<img title="bad" alt="Sad face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_1.png'/>
<span class="o_rating_count" t-esc="partner_stat['rating_1']"/>
@@ -116,7 +116,7 @@
</div>
</div>
<div class="col-md-3 o_smiley_no_padding_left">
- <strong class="float-right"><t t-esc="partner_stat['percentage_happy']"/>%</strong>
+ <strong class="float-right"><t t-esc="int(round(partner_stat['percentage_happy']))"/>%</strong>
</div>
</div>
</t>
@@ -125,11 +125,11 @@
<template id="portal_project_rating_popover">
<div t-attf-id="rating_#{rating.id}" class="container d-none">
<div class="row">
- <div class="col-lg-2">
- <img t-if="rating.partner_id.image_1024" class="o_top_partner_image" t-att-src="image_data_uri(rating.partner_id.image_1024)" alt="Rated partner"/>
- <img t-if="not rating.partner_id.image_1024" class="o_top_partner_image" src='/web/static/src/img/placeholder.png' alt="Placeholder"/>
+ <div class="col-lg-4">
+ <img t-if="rating.partner_id.image_1024" class="img-fluid" t-att-src="image_data_uri(rating.partner_id.image_1024)" alt="Rated partner"/>
+ <img t-if="not rating.partner_id.image_1024" class="img-fluid" src='/web/static/src/img/placeholder.png' alt="Placeholder"/>
</div>
- <div class="col-lg-10">
+ <div class="col-lg-8">
<div class="mt4">
<strong><t t-esc="rating.rated_partner_id.name"/></strong> - <span class="rating_timeduration"><t t-esc="rating.create_date"/></span>
</div>
@@ -149,42 +149,52 @@
<div id="wrap" class="row mt4">
<div class="oe_structure" id="oe_structure_project_rating_page_1"/>
<div class="container o_project o_portal_project_rating">
+ <br/>
<h2 t-esc="project.name"/>
+ <br/>
<div class="row mb32">
<div class="col-lg-12">
- <h3 class="o_page_header">Customer Ratings</h3>
+ <div class="o_page_header">
+ <div class="justify-content-end" groups="project.group_project_manager">
+ <t t-call="website.publish_management">
+ <t t-set="object" t-value="project"/>
+ <t t-set="publish_edit" t-value="True"/>
+ </t>
+ </div>
+ <h3>Customer Ratings</h3>
+ </div>
<div class="row">
<div class="col-lg-7">
<t t-foreach="ratings" t-as="rating">
- <img t-attf-src='/rating/static/src/img/rating_#{int(rating.rating)}.png'
+ <img t-attf-src='/rating/static/src/img/#{rating._get_rating_image_filename()}'
class="mt4 o_rating_image"
t-att-alt="rating.res_name"
t-att-data-id="rating.id"
t-att-data-rating-date="rating.write_date"/>
- <t t-call="project.portal_project_rating_popover"/>
+ <t t-call="website_project.portal_project_rating_popover"/>
</t>
</div>
<div class="col-lg-5 o_vertical_separator">
<div t-if="statistics['period_statistics']['days_06']">
<h5>Last 7 days</h5>
- <t t-call="project.portal_project_rating_progressbar">
+ <t t-call="website_project.portal_project_rating_progressbar">
<t t-set="stats" t-value="statistics['period_statistics']['days_06']"/>
</t>
</div>
<div t-if="statistics['period_statistics']['days_30']">
<h5>Last 30 days</h5>
- <t t-call="project.portal_project_rating_progressbar">
+ <t t-call="website_project.portal_project_rating_progressbar">
<t t-set="stats" t-value="statistics['period_statistics']['days_30']"/>
</t>
</div>
<div t-if="statistics['period_statistics']['days_90']">
<h5>Last 3 months</h5>
- <t t-call="project.portal_project_rating_progressbar">
+ <t t-call="website_project.portal_project_rating_progressbar">
<t t-set="stats" t-value="statistics['period_statistics']['days_90']"/>
</t>
</div>
<div>
- <t t-call="project.portal_project_rating_partner_stat">
+ <t t-call="website_project.portal_project_rating_partner_stat">
<t t-set="partner_stats" t-value="statistics['partner_statistics']"/>
</t>
</div>
diff --git a/addons/website_slides/data/slide_user_demo.xml b/addons/website_slides/data/slide_user_demo.xml
index 95fcc2cbd67..80d87842323 100644
--- a/addons/website_slides/data/slide_user_demo.xml
+++ b/addons/website_slides/data/slide_user_demo.xml
@@ -52,7 +52,7 @@
<field name="partner_id" ref="base.partner_admin"/>
<field name="consumed" eval="True"/>
<field name="feedback">I fear beginners could be lost... Isn't it a bit harsh for a "basics" course ?</field>
- <field name="rating">6</field>
+ <field name="rating">3</field>
<field name="message_id" ref="website_slides.message_channel_0_admin"/>
</record>
<record id="message_channel_0_demo" model="mail.message">
@@ -68,7 +68,7 @@
<field name="res_id" ref="website_slides.slide_channel_demo_0_gard_0"/>
<field name="partner_id" ref="base.partner_demo"/>
<field name="consumed" eval="True"/>
- <field name="rating">10</field>
+ <field name="rating">5</field>
<field name="feedback">Back to basics and interesting. Just WOW !</field>
<field name="message_id" ref="website_slides.message_channel_0_demo"/>
</record>
@@ -140,7 +140,7 @@
<field name="partner_id" ref="base.partner_admin"/>
<field name="consumed" eval="True"/>
<field name="feedback">Very good course.</field>
- <field name="rating">10</field>
+ <field name="rating">5</field>
<field name="message_id" ref="website_slides.message_channel_1_admin"/>
</record>
<record id="message_channel_1_demo" model="mail.message">
@@ -156,7 +156,7 @@
<field name="res_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="partner_id" ref="base.partner_demo"/>
<field name="consumed" eval="True"/>
- <field name="rating">8</field>
+ <field name="rating">4</field>
<field name="feedback">Interesting !</field>
<field name="message_id" ref="website_slides.message_channel_1_demo"/>
</record>
@@ -173,7 +173,7 @@
<field name="res_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="partner_id" ref="base.partner_demo_portal"/>
<field name="consumed" eval="True"/>
- <field name="rating">6</field>
+ <field name="rating">3</field>
<field name="feedback">Interesting. Could be great to include more examples.</field>
<field name="message_id" ref="website_slides.message_channel_1_portal"/>
<field name="publisher_comment">Thanks for the feedback! We just updated first lessons to better include newcomers.</field>
diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py
index 34b5bc0fcd9..fe2c009f6ee 100644
--- a/addons/website_slides/models/slide_channel.py
+++ b/addons/website_slides/models/slide_channel.py
@@ -260,7 +260,7 @@ class Channel(models.Model):
def _compute_rating_stats(self):
super(Channel, self)._compute_rating_stats()
for record in self:
- record.rating_avg_stars = record.rating_avg / 2
+ record.rating_avg_stars = record.rating_avg
@api.depends('slide_partner_ids', 'total_slides')
@api.depends_context('uid')
diff --git a/addons/website_slides/views/rating_rating_views.xml b/addons/website_slides/views/rating_rating_views.xml
index 379bb72d781..688bc40df44 100644
--- a/addons/website_slides/views/rating_rating_views.xml
+++ b/addons/website_slides/views/rating_rating_views.xml
@@ -12,7 +12,7 @@
<field name="partner_id"/>
<templates>
<t t-name="kanban-box">
- <t t-set="val_stars" t-value="record.rating.raw_value / 2"/>
+ <t t-set="val_stars" t-value="record.rating.raw_value"/>
<t t-set="val_integer" t-value="Math.floor(val_stars)"/>
<t t-set="val_decimal" t-value="val_stars - val_integer"/>
<t t-set="empty_star" t-value="5 - (val_integer + Math.ceil(val_decimal))"/>
diff --git a/doc/reference/mixins.rst b/doc/reference/mixins.rst
index d290e418652..b64387fcc2c 100644
--- a/doc/reference/mixins.rst
+++ b/doc/reference/mixins.rst
@@ -1030,8 +1030,8 @@ links to the rating object. A very basic email template could look like this:
<p>Hi,</p>
<p>How satsified are you?</p>
<ul>
- <li><a href="/rating/${access_token}/10">Satisfied</a></li>
- <li><a href="/rating/${access_token}/5">Not satisfied</a></li>
+ <li><a href="/rating/${access_token}/5">Satisfied</a></li>
+ <li><a href="/rating/${access_token}/3">Not satisfied</a></li>
<li><a href="/rating/${access_token}/1">Very unsatisfied</a></li>
</ul>
]]></field>