diff options
| author | Benjamin Frantzen (bfr) <bfr@odoo.com> | 2019-12-18 13:45:38 +0000 |
|---|---|---|
| committer | Yannick Tivisse <yti@odoo.com> | 2020-05-08 12:17:20 +0000 |
| commit | e4a4ffb974ccde1842e1b2389450718f8576ad5b (patch) | |
| tree | 6159873058286c65a3a3bfb5a48f2ef5ffbf7565 | |
| parent | f427a38c4e0dcd63d086706ad66a3e5570fbf0aa (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
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', '<', (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 Binary files differdeleted file mode 100644 index 3af034234c0..00000000000 --- a/addons/rating/static/src/img/rating_10.png +++ /dev/null diff --git a/addons/rating/static/src/img/rating_10_white.png b/addons/rating/static/src/img/rating_10_white.png Binary files differdeleted file mode 100644 index d756eee931f..00000000000 --- a/addons/rating/static/src/img/rating_10_white.png +++ /dev/null diff --git a/addons/rating/static/src/img/rating_3.png b/addons/rating/static/src/img/rating_3.png Binary files differnew file mode 100644 index 00000000000..8ec3a2a7325 --- /dev/null +++ b/addons/rating/static/src/img/rating_3.png diff --git a/addons/rating/static/src/img/rating_3_white.png b/addons/rating/static/src/img/rating_3_white.png Binary files differnew file mode 100644 index 00000000000..d146dc5b37d --- /dev/null +++ b/addons/rating/static/src/img/rating_3_white.png diff --git a/addons/rating/static/src/img/rating_5.png b/addons/rating/static/src/img/rating_5.png Binary files differindex 8ec3a2a7325..3af034234c0 100644 --- a/addons/rating/static/src/img/rating_5.png +++ b/addons/rating/static/src/img/rating_5.png diff --git a/addons/rating/static/src/img/rating_5_white.png b/addons/rating/static/src/img/rating_5_white.png Binary files differindex d146dc5b37d..d756eee931f 100644 --- a/addons/rating/static/src/img/rating_5_white.png +++ b/addons/rating/static/src/img/rating_5_white.png 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> |
