summaryrefslogtreecommitdiff
path: root/addons/project
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 /addons/project
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
Diffstat (limited to 'addons/project')
-rw-r--r--addons/project/__manifest__.py1
-rw-r--r--addons/project/controllers/__init__.py1
-rw-r--r--addons/project/controllers/rating.py96
-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_rating_templates.xml202
-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
13 files changed, 218 insertions, 508 deletions
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/controllers/rating.py b/addons/project/controllers/rating.py
deleted file mode 100644
index c57734e6a99..00000000000
--- a/addons/project/controllers/rating.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from werkzeug.exceptions import NotFound
-
-from odoo import http
-from odoo.http import request
-
-
-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)])
- values = {'projects': projects}
- return request.render('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
- request.env.cr.execute("""
- SELECT
- rated_partner_id,
- rating,
- COUNT(rating) as rating_count,
- CASE
- WHEN now()::date - write_date::date BETWEEN 0 AND 6 Then 'days_06'
- WHEN now()::date - write_date::date BETWEEN 0 AND 15 Then 'days_15'
- WHEN now()::date - write_date::date BETWEEN 0 AND 30 Then 'days_30'
- WHEN now()::date - write_date::date BETWEEN 0 AND 90 Then 'days_90'
- END AS period
- FROM
- rating_rating
- WHERE
- parent_res_model = 'project.project'
- AND parent_res_id = %s
- 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)
- GROUP BY
- rated_partner_id, rating, period
- """, (project_id, ))
-
- raw_data = request.env.cr.dictfetchall()
-
- # periodical statistics
- default_period_dict = {'rating_10': 0, 'rating_5': 0, 'rating_1': 0, 'total': 0}
- period_statistics = {
- 'days_06': dict(default_period_dict),
- 'days_15': dict(default_period_dict),
- 'days_30': dict(default_period_dict),
- 'days_90': dict(default_period_dict),
- }
- for period_statistics_key in period_statistics.keys():
- for row in raw_data:
- if row['period'] <= period_statistics_key:
- period_statistics[period_statistics_key]['rating_%s' % (int(row['rating']),)] += row['rating_count']
- 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}
- partner_statistics = {}
- for row in raw_data:
- if row['period'] <= 'days_15':
- if row['rated_partner_id'] not in partner_statistics:
- partner_statistics[row['rated_partner_id']] = dict(default_partner_dict)
- partner_statistics[row['rated_partner_id']]['rated_partner'] = request.env['res.partner'].sudo().browse(row['rated_partner_id'])
- partner_statistics[row['rated_partner_id']]['rating_%s' % (int(row['rating']),)] += row['rating_count']
- 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
-
- return {
- 'partner_statistics': partner_statistics,
- 'period_statistics': period_statistics,
- }
-
- @http.route(['/project/rating/<int:project_id>'], type='http', auth="public", website=True)
- def page(self, project_id=None, **kw):
- user = request.env.user
- 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'):
- raise NotFound()
-
- return request.render('project.rating_project_rating_page', {
- 'project': project,
- 'ratings': request.env['rating.rating'].sudo().search([
- ('consumed', '=', True),
- ('parent_res_model', '=', 'project.project'),
- ('parent_res_id', '=', project_id)
- ], order='write_date DESC', limit=50),
- 'statistics': self._calculate_period_partner_stats(project_id),
- })
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_rating_templates.xml b/addons/project/views/project_rating_templates.xml
deleted file mode 100644
index a1b8613ccb3..00000000000
--- a/addons/project/views/project_rating_templates.xml
+++ /dev/null
@@ -1,202 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <data>
-
- <!-- Page : List of project -->
- <template id="rating_index" name="Project Rating List">
- <t t-call="portal.frontend_layout">
- <t t-set="additional_title">Project Satisfaction</t>
- <div id="wrap" class="row mt4">
- <div class="oe_structure" id="oe_structure_project_rating_index_1"/>
- <div class="container o_portal_project_rating">
- <h3>Projects</h3>
- <div class="oe_structure" id="oe_structure_project_rating_index_2"/>
- <div class="row">
- <t t-if="not projects">
- <div class="mt64 text-center">
- No project rating published for now.
- </div>
- </t>
- <t t-foreach="projects" t-as="project">
- <div t-if="project.rating_percentage_satisfaction != -1" class="col-md-6 col-4 col-lg-4 col-xl-4">
- <div class="card">
- <div class="card-body">
- <div class="caption">
- <span class="badge badge-secondary float-right"><t t-esc="project.privacy_visibility"/></span>
- <h4><t t-esc="project.name"/></h4>
- <p t-if="project.date" class="text-muted">
- <i class="fa fa-calendar"/> End date : <t t-esc="project.date"/>
- </p>
- <p t-if="project.alias_name and project.alias_domain" class="text-muted">
- <i class="fa fa-envelope"/> Email : <t t-esc="project.alias_name"/>@<t t-esc="project.alias_domain"/>
- </p>
- <div class="row">
- <div class="col-lg-6 text-center">
- <h2><t t-esc="len(project.task_ids)"/></h2>
- <p><t t-esc="project.label_tasks"/></p>
- </div>
- </div>
- </div>
- <div class="caption">
- <p>
- <a role="button" t-att-href="'/project/rating/%s' % project.id" class="btn btn-primary btn-lg btn-block">
- <i class="fa fa-arrow-circle-right "/> See the feedbacks
- </a>
- </p>
- </div>
- </div>
- </div>
- </div>
- </t>
- </div>
- </div>
- <div class="oe_structure" id="oe_structure_project_rating_index_3"/>
- </div>
- </t>
- </template>
-
- <!-- 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-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"/>
- <div class="progress-bar bg-danger" t-attf-style="width: #{percent_rating_1}%;" title="Bad">
- <t t-esc="percent_rating_1"/>%
- </div>
- </div>
- </template>
-
- <template id="portal_project_rating_partner_stat">
- <t t-foreach="partner_stats.values()" t-as="partner_stat">
- <div class="row mt8" t-if="partner_stat['total']" title="Top 5 partner ratings of last 15 days.">
- <div class="col-md-1">
- <img t-if="partner_stat['rated_partner'].image_1024" class="o_top_partner_image" t-att-src="image_data_uri(partner_stat['rated_partner'].image_1024)" alt="Rated partner"/>
- <img t-if="not partner_stat['rated_partner'].image_1024" class="o_top_partner_image" src='/web/static/src/img/placeholder.png' alt="Placeholder"/>
- </div>
- <div class="col-md-4 o_smiley_no_padding_right">
- <span class="ml8" t-esc="partner_stat['rated_partner'].name"/>
- </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'/>
- <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'/>
- <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_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']"/>
- </span>
- <span t-if="not partner_stat['rating_1']">
- <img title="bad" alt="Sad face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_1.png'/>
- <span class="o_rating_count o_lighter_smileys" t-esc="partner_stat['rating_1']"/>
- </span>
- </div>
- </div>
- <div class="col-md-3 o_smiley_no_padding_left">
- <strong class="float-right"><t t-esc="partner_stat['percentage_happy']"/>%</strong>
- </div>
- </div>
- </t>
- </template>
-
- <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>
- <div class="col-lg-10">
- <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>
- <strong>Rated by: </strong><t t-esc="rating.partner_id.name" />
- </div>
- </div>
- <div class="row o_top_partner_feedback" t-if="rating.feedback">
- <div class="col-lg-12">
- <t t-esc="rating.feedback" />
- </div>
- </div>
- </div>
- </template>
-
- <template id="rating_project_rating_page" name="Project Rating Page">
- <t t-call="portal.frontend_layout">
- <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">
- <h2 t-esc="project.name"/>
- <div class="row mb32">
- <div class="col-lg-12">
- <h3 class="o_page_header">Customer Ratings</h3>
- <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'
- 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>
- </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-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-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-set="stats" t-value="statistics['period_statistics']['days_90']"/>
- </t>
- </div>
- <div>
- <t t-call="project.portal_project_rating_partner_stat">
- <t t-set="partner_stats" t-value="statistics['partner_statistics']"/>
- </t>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="oe_structure" id="oe_structure_project_rating_page_2"/>
- </div>
- </t>
- </template>
-
- </data>
-</odoo>
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>