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 /addons/project | |
| 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
Diffstat (limited to 'addons/project')
| -rw-r--r-- | addons/project/__manifest__.py | 1 | ||||
| -rw-r--r-- | addons/project/controllers/__init__.py | 1 | ||||
| -rw-r--r-- | addons/project/controllers/rating.py | 96 | ||||
| -rw-r--r-- | addons/project/data/project_data.xml | 1 | ||||
| -rw-r--r-- | addons/project/data/project_demo.xml | 4 | ||||
| -rw-r--r-- | addons/project/data/project_mail_template_data.xml | 8 | ||||
| -rw-r--r-- | addons/project/models/project.py | 50 | ||||
| -rw-r--r-- | addons/project/models/res_config_settings.py | 16 | ||||
| -rw-r--r-- | addons/project/tests/test_project_flow.py | 6 | ||||
| -rw-r--r-- | addons/project/views/project_rating_templates.xml | 202 | ||||
| -rw-r--r-- | addons/project/views/project_views.xml | 334 | ||||
| -rw-r--r-- | addons/project/views/rating_views.xml | 5 | ||||
| -rw-r--r-- | addons/project/views/res_config_settings_views.xml | 2 |
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', '<', (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> |
