summaryrefslogtreecommitdiff
path: root/addons/project
diff options
context:
space:
mode:
Diffstat (limited to 'addons/project')
-rw-r--r--addons/project/__manifest__.py2
-rw-r--r--addons/project/data/project_data.xml23
-rw-r--r--addons/project/data/project_demo.xml46
-rw-r--r--addons/project/data/project_mail_template_data.xml85
-rw-r--r--addons/project/models/project.py101
-rw-r--r--addons/project/models/res_config_settings.py2
-rw-r--r--addons/project/security/project_security.xml5
-rw-r--r--addons/project/views/project_views.xml52
-rw-r--r--addons/project/views/rating_views.xml74
-rw-r--r--addons/project/views/res_config_settings_views.xml10
10 files changed, 393 insertions, 7 deletions
diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py
index d95a21d1ee7..43b36689d88 100644
--- a/addons/project/__manifest__.py
+++ b/addons/project/__manifest__.py
@@ -14,6 +14,7 @@
'analytic',
'mail',
'portal',
+ 'rating',
'resource',
'web',
'web_tour',
@@ -24,6 +25,7 @@
'security/ir.model.access.csv',
'data/project_data.xml',
'report/project_report_views.xml',
+ 'views/rating_views.xml',
'views/project_views.xml',
'views/res_partner_views.xml',
'views/res_config_settings_views.xml',
diff --git a/addons/project/data/project_data.xml b/addons/project/data/project_data.xml
index de45107ae71..a8f8be15f2b 100644
--- a/addons/project/data/project_data.xml
+++ b/addons/project/data/project_data.xml
@@ -17,6 +17,15 @@
<field name="noupdate">True</field>
</record>
+ <record id="ir_cron_rating_project" model="ir.cron">
+ <field name="name">Project: Send rating</field>
+ <field name="model_id" ref="project.model_project_project"/>
+ <field name="state">code</field>
+ <field name="code">model._send_rating_all()</field>
+ <field name="interval_type">days</field>
+ <field name="numbercall">-1</field>
+ </record>
+
</data>
@@ -46,6 +55,12 @@
<field name="default" eval="False"/>
<field name="description">Stage changed</field>
</record>
+ <record id="mt_task_rating" model="mail.message.subtype">
+ <field name="name">Task Rating</field>
+ <field name="res_model">project.task</field>
+ <field name="default" eval="True"/>
+ <field name="description">Ratings</field>
+ </record>
<!-- Project-related subtypes for messaging / Chatter -->
<record id="mt_project_task_new" model="mail.message.subtype">
<field name="name">Task Opened</field>
@@ -79,6 +94,14 @@
<field name="parent_id" eval="ref('mt_task_stage')"/>
<field name="relation_field">project_id</field>
</record>
+ <record id="mt_project_task_rating" model="mail.message.subtype">
+ <field name="name">Task Rating</field>
+ <field name="sequence">14</field>
+ <field name="res_model">project.project</field>
+ <field name="default" eval="True"/>
+ <field name="parent_id" eval="ref('mt_task_rating')"/>
+ <field name="relation_field">project_id</field>
+ </record>
<data noupdate="1">
diff --git a/addons/project/data/project_demo.xml b/addons/project/data/project_demo.xml
index f5e53c769df..592acc33a7d 100644
--- a/addons/project/data/project_demo.xml
+++ b/addons/project/data/project_demo.xml
@@ -430,5 +430,51 @@ Send it ASAP, its urgent.</field>
<function model="project.project" name="message_subscribe"
eval="[ref('project.project_project_1')], [ref('base.partner_demo_portal')]"/>
+
+ <!-- Rating Demo Data -->
+ <record id="rating_task_1" model="rating.rating">
+ <field name="res_model_id" ref="project.model_project_task"/>
+ <field name="rated_partner_id" ref="base.partner_root"/>
+ <field name="partner_id" ref="base.partner_demo"/>
+ <field name="res_id" ref="project.project_task_3"/>
+ </record>
+ <record id="rating_task_2" model="rating.rating">
+ <field name="res_model_id" ref="project.model_project_task"/>
+ <field name="rated_partner_id" ref="base.partner_demo"/>
+ <field name="partner_id" ref="base.partner_demo"/>
+ <field name="res_id" ref="project.project_task_8"/>
+ </record>
+ <record id="rating_task_3" model="rating.rating">
+ <field name="res_model_id" ref="project.model_project_task"/>
+ <field name="rated_partner_id" ref="base.partner_root"/>
+ <field name="partner_id" ref="base.partner_root"/>
+ <field name="res_id" ref="project.project_task_15"/>
+ </record>
+ <record id="rating_task_4" model="rating.rating">
+ <field name="res_model_id" ref="project.model_project_task"/>
+ <field name="rated_partner_id" ref="base.partner_root"/>
+ <field name="partner_id" ref="base.partner_root"/>
+ <field name="res_id" ref="project.project_task_18"/>
+ </record>
+ <record id="rating_task_5" model="rating.rating">
+ <field name="res_model_id" ref="project.model_project_task"/>
+ <field name="rated_partner_id" ref="base.partner_root"/>
+ <field name="partner_id" ref="base.res_partner_4"/>
+ <field name="res_id" ref="project.project_task_24"/>
+ </record>
+
+ <!-- add the email template as value for the project stage 2 -->
+ <record id="project.project_stage_2" model="project.task.type">
+ <field name="rating_template_id" ref="rating_project_request_email_template"/>
+ </record>
+ <record id="project.project_project_3" model="project.project">
+ <field name="rating_status">stage</field>
+ </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_18')], 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_8')], 1)"/>
+ <function model="project.task" name="rating_apply" eval="([ref('project.project_task_15')], 5)"/>
</data>
</odoo>
diff --git a/addons/project/data/project_mail_template_data.xml b/addons/project/data/project_mail_template_data.xml
index d33aabf2cd3..808c7d570d6 100644
--- a/addons/project/data/project_mail_template_data.xml
+++ b/addons/project/data/project_mail_template_data.xml
@@ -109,8 +109,91 @@
<function
id="mail_template_function_module_install_project"
- model="mail.template"
+ model="mail.template"
name="send_mail"
eval="[ref('mail_template_data_module_install_project'), ref('base.module_project')]"/>
+
+ <!-- Mail sent to request a rating for a task -->
+ <record id="rating_project_request_email_template" model="mail.template">
+ <field name="name">Task: Rating Request</field>
+ <field name="email_from">${object.rating_get_rated_partner_id().email or '' | safe}</field>
+ <field name="subject">${object.project_id.company_id.name}: Service Rating Request</field>
+ <field name="model_id" ref="project.model_project_task"/>
+ <field name="partner_to" >${object.rating_get_partner_id().id}</field>
+ <field name="auto_delete" eval="True"/>
+ <field name="body_html"><![CDATA[
+<div style="background:#F0F0F0;color:#515166;padding:10px 0px;font-family:Arial,Helvetica,sans-serif;font-size:14px;">
+ % set access_token = object.rating_get_access_token()
+ <table style="width:600px;margin:5px auto;">
+ <tbody>
+ <tr><td>
+ <a href="/"><img src="/web/binary/company_logo" style="vertical-align:baseline;max-width:100px;" /></a>
+ </td><td style="text-align:right;vertical-align:middle;">
+ Satisfaction Survey
+ </td></tr>
+ </tbody>
+ </table>
+ <table style="width:600px;margin:0px auto;background:white;border:1px solid #e1e1e1;">
+ <tbody>
+ <tr><td style="padding:15px 20px 10px 20px;">
+ <p>Hello,</p>
+ <p>Please take a moment to rate our services related to the task "<strong>${object.name}</strong>"
+ assigned to <strong>${object.rating_get_rated_partner_id().name}</strong>.</p>
+ <p>We appreciate your feedback. It helps us to improve continuously.</p>
+ </td></tr>
+ <tr><td style="padding:10px 20px">
+ <table summary="o_mail_notification" style="width:100%;border-top:1px solid #e1e1e1;">
+ <tr>
+ <td style="text-align:center;">
+ <h2 style="font-weight:300;font-size:18px;">
+ Tell us how you feel about our service:
+ </h2>
+ <div style="text-color: #888888">(click on one of these smileys)</div>
+ </td>
+ </tr>
+ <tr>
+ <td style="padding:10px 10px;">
+ <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>
+ </td>
+ <td>
+ <a href="/rating/${access_token}/5">
+ <img alt="Not satisfied" src="/rating/static/src/img/rating_5.png" title="Not satisfied"/>
+ </a>
+ </td>
+ <td>
+ <a href="/rating/${access_token}/1">
+ <img alt="Highly Dissatisfied" src="/rating/static/src/img/rating_1.png" title="Highly Dissatisfied"/>
+ </a>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td></tr>
+ <tr><td style="padding:15px 20px 10px 20px;">${(object.user_id.signature or '')| safe}</td></tr>
+ </tbody>
+ </table>
+ <table style="width:600px;margin:auto;text-align:center;font-size:12px;">
+ <tbody>
+ <tr><td style="padding-top:10px;color:#afafaf;">
+ % if object.project_id.rating_status == 'stage':
+ <p>This customer survey has been sent because your task has been moved to the stage <b>${object.stage_id.name}</b></p>
+ % endif
+ % if object.project_id.rating_status == 'periodic':
+ <p>This customer survey is sent <b>${object.project_id.rating_status_period}</b> as long as the task is in the <b>${object.stage_id.name}</b> stage.</p>
+ % endif
+ <p>Email automatically sent by <a target="_blank" href="https://www.odoo.com/page/project-management" style="color:#875A7B;text-decoration:none;">Odoo Project</a> for <a href="${object.project_id.company_id.website}" style="color:#875A7B;text-decoration:none;">${object.project_id.company_id.name}</a></p>
+ </td></tr>
+ </tbody>
+ </table>
+</div>
+]]></field>
+ </record>
</data>
</odoo>
diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index 2bb472fa2c3..4f5d68b0aea 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from datetime import timedelta
from lxml import etree
from odoo import api, fields, models, tools, SUPERUSER_ID, _
@@ -41,6 +42,15 @@ class ProjectTaskType(models.Model):
help="If set an email will be sent to the customer when the task or issue reaches this step.")
fold = fields.Boolean(string='Folded in Kanban',
help='This stage is folded in the kanban view when there are no records in that stage to display.')
+ rating_template_id = fields.Many2one(
+ 'mail.template',
+ string='Rating Email Template',
+ domain=[('model', '=', 'project.task')],
+ help="If set and if the project's rating configuration is 'Rating when changing stage', then an email will be sent to the customer when the task reaches this step.")
+ auto_validation_kanban_state = fields.Boolean('Automatic kanban status', default=False,
+ help="Automatically modify the kanban state when the customer replies to the feedback for this stage.\n"
+ " * 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")
class Project(models.Model):
@@ -214,6 +224,20 @@ class Project(models.Model):
date = fields.Date(string='Expiration Date', index=True, track_visibility='onchange')
subtask_project_id = fields.Many2one('project.project', string='Sub-task Project', ondelete="restrict",
help="Choosing a sub-tasks project will both enable sub-tasks and set their default project (possibly the project itself)")
+ # rating fields
+ percentage_satisfaction_task = fields.Integer(
+ compute='_compute_percentage_satisfaction_task', string="Happy % on Task", store=True, default=-1)
+ percentage_satisfaction_project = fields.Integer(
+ compute="_compute_percentage_satisfaction_project", string="Happy % on Project", store=True, default=-1)
+ 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')], 'Customer(s) Ratings', help="How to get the customer's feedbacks?\n"
+ "- Rating when changing stage: Email will be sent when a task/issue 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.", default="no", required=True)
+ rating_status_period = fields.Selection([
+ ('daily', 'Daily'), ('weekly', 'Weekly'), ('bimonthly', 'Twice a Month'),
+ ('monthly', 'Once a Month'), ('quarterly', 'Quarterly'), ('yearly', 'Yearly')
+ ], 'Rating Frequency')
_sql_constraints = [
('project_date_greater', 'check(date >= date_start)', 'Error! project start-date must be lower than project end-date.')
@@ -224,6 +248,26 @@ class Project(models.Model):
for project in self:
project.portal_url = '/my/project/%s' % project.id
+ @api.depends('percentage_satisfaction_task')
+ def _compute_percentage_satisfaction_project(self):
+ domain = [('create_date', '>=', fields.Datetime.to_string(fields.datetime.now() - timedelta(days=30)))]
+ for project in self:
+ activity = project.tasks.rating_get_grades(domain)
+ project.percentage_satisfaction_project = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1
+
+ #TODO JEM: Only one field can be kept since project only contains task
+ @api.depends('tasks.rating_ids.rating')
+ def _compute_percentage_satisfaction_task(self):
+ for project in self:
+ activity = project.tasks.rating_get_grades()
+ project.percentage_satisfaction_task = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1
+
+ @api.depends('rating_status', 'rating_status_period')
+ def _compute_rating_request_deadline(self):
+ periods = {'daily': 1, 'weekly': 7, 'bimonthly': 15, 'monthly': 30, 'quarterly': 90, 'yearly': 365}
+ for project in self:
+ project.rating_request_deadline = fields.datetime.now() + timedelta(days=periods.get(project.rating_status_period, 0))
+
@api.multi
def map_tasks(self, new_project_id):
""" copy and map tasks from old to new project """
@@ -325,6 +369,10 @@ class Project(models.Model):
return groups
+ # ---------------------------------------------------
+ # Actions
+ # ---------------------------------------------------
+
@api.multi
def toggle_favorite(self):
favorite_projects = not_fav_projects = self.env['project.project'].sudo()
@@ -354,12 +402,33 @@ class Project(models.Model):
'context': ctx
}
+ @api.multi
+ def action_view_all_rating(self):
+ """ 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 = safe_eval(action['context']) if action['context'] else {}
+ action_context.update(self._context)
+ action_context['search_default_rating_tasks'] = 1
+ return dict(action, context=action_context)
+
+ # ---------------------------------------------------
+ # Rating business
+ # ---------------------------------------------------
+
+ # This method should be called once a day by the scheduler
+ @api.model
+ def _send_rating_all(self):
+ projects = self.search([('rating_status', '=', 'periodic'), ('rating_request_deadline', '<=', fields.Datetime.now())])
+ projects.mapped('task_ids')._send_task_rating_mail()
+ projects._compute_rating_request_deadline()
+
class Task(models.Model):
_name = "project.task"
_description = "Task"
_date_name = "date_start"
- _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin']
+ _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin', 'rating.mixin']
_mail_post_access = 'read'
_order = "priority desc, sequence, date_start, name, id"
@@ -656,7 +725,9 @@ class Task(models.Model):
vals['date_assign'] = now
result = super(Task, self).write(vals)
-
+ # rating on stage
+ if 'stage_id' in vals and vals.get('stage_id'):
+ self.filtered(lambda x: x.project_id.rating_status == 'stage')._send_task_rating_mail(force_send=True)
return result
def update_date_end(self, stage_id):
@@ -865,6 +936,32 @@ class Task(models.Model):
'type': 'ir.actions.act_window'
}
+ # ---------------------------------------------------
+ # Rating business
+ # ---------------------------------------------------
+
+ def _send_task_rating_mail(self, force_send=False):
+ for task in self:
+ rating_template = task.stage_id.rating_template_id
+ if rating_template:
+ task.rating_send_request(rating_template, lang=task.partner_id.lang, force_send=force_send)
+
+ def rating_get_partner_id(self):
+ res = super(Task, self).rating_get_partner_id()
+ if not res and self.project_id.partner_id:
+ return self.project_id.partner_id
+ return res
+
+ @api.multi
+ def rating_apply(self, rate, token=None, feedback=None, subtype=None):
+ return super(Task, self).rating_apply(rate, token=token, feedback=feedback, subtype="project.mt_task_rating")
+
+ def rating_get_parent_model_name(self, vals):
+ return 'project.project'
+
+ def rating_get_parent_id(self):
+ return self.project_id.id
+
class ProjectTags(models.Model):
""" Tags of project's tasks """
diff --git a/addons/project/models/res_config_settings.py b/addons/project/models/res_config_settings.py
index f008f52a5aa..ea1eb675904 100644
--- a/addons/project/models/res_config_settings.py
+++ b/addons/project/models/res_config_settings.py
@@ -8,6 +8,6 @@ class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
module_hr_timesheet = fields.Boolean("Timesheets")
- module_rating_project = fields.Boolean(string="Rating on Tasks")
module_project_forecast = fields.Boolean(string="Forecasts")
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')
diff --git a/addons/project/security/project_security.xml b/addons/project/security/project_security.xml
index c2cfb562e56..f2f70e949f9 100644
--- a/addons/project/security/project_security.xml
+++ b/addons/project/security/project_security.xml
@@ -24,6 +24,11 @@
<field name="category_id" ref="base.module_category_hidden"/>
</record>
+ <record id="group_project_rating" model="res.groups">
+ <field name="name">Use Rating on Project</field>
+ <field name="category_id" ref="base.module_category_hidden"/>
+ </record>
+
<data noupdate="1">
<record id="base.default_user" model="res.users">
<field name="groups_id" eval="[(4,ref('project.group_project_manager'))]"/>
diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index 3656f306383..a09293f80df 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -47,6 +47,8 @@
<filter string="Future Activities" name="activities_upcoming_all"
domain="[('activity_ids.date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))
]"/>
+ <separator/>
+ <filter string="Rated tasks" name="rating_task" domain="[('rating_last_value', '!=', 0.0)]" groups="project.group_project_rating"/>
<group expand="0" string="Group By">
<filter string="Project" name="project" context="{'group_by':'project_id'}"/>
<filter string="Task" name="task" context="{'group_by':'name'}"/>
@@ -113,12 +115,18 @@
name="%(act_project_project_2_project_task_all)d" icon="fa-tasks">
<field string="Tasks" name="task_count" widget="statinfo" options="{'label_field': 'label_tasks'}"/>
</button>
+ <button name="action_view_all_rating" type="object" attrs="{'invisible': ['|', '|', ('rating_status', '=', 'no'), ('percentage_satisfaction_task', '=', -1)]}" class="oe_stat_button oe_percent" icon="fa-smile-o" groups="project.group_project_rating">
+ <field string="% On tasks" name="percentage_satisfaction_task" widget="statinfo"/>
+ </button>
<button name="toggle_active" type="object"
confirm="(Un)archiving a project automatically (un)archives its tasks and issues. Do you want to proceed?"
class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button"
options='{"terminology": "archive"}'/>
</button>
+ <button name="%(project.rating_rating_action_view_project_rating_task)d" attrs="{'invisible': ['|', '|', ('rating_status', '=', 'no'), ('use_tasks','=', False), ('percentage_satisfaction_task', '=', -1)]}" class="oe_stat_button oe_percent" type="action" icon="fa-smile-o" groups="project.group_project_rating">
+ <field string="% On tasks" name="percentage_satisfaction_task" widget="statinfo"/>
+ </button>
</div>
<div class="oe_title">
<h1>
@@ -139,6 +147,14 @@
<field name="subtask_project_id" groups="project.group_subtask_project"/>
<field name="privacy_visibility" widget="radio"/>
<field name="partner_id" string="Customer"/>
+ <label for="rating_status" groups="project.group_project_rating"/>
+ <div groups="project.group_project_rating">
+ <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>
+ <field name="rating_status_period" class="oe_inline" attrs="{'required': [('rating_status','=','periodic')], 'invisible': [('rating_status','!=','periodic')]}" groups="project.group_project_rating"/>
</group>
<group name="misc">
<group string="Configuration" groups="base.group_no_one">
@@ -286,6 +302,9 @@
<field name="alias_id"/>
<field name="alias_name"/>
<field name="alias_domain"/>
+ <field name="is_favorite"/>
+ <field name="percentage_satisfaction_project"/>
+ <field name="rating_status"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click o_has_icon">
@@ -301,6 +320,18 @@
<div t-if="record.alias_name.value and record.alias_domain.value">
<span><i class="fa fa-envelope"></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.">
+ <b>
+ <t t-if="record.percentage_satisfaction_project.value == -1">
+ <i class="fa fa-smile-o"/> No rating yet
+ </t>
+ <t t-if="record.percentage_satisfaction_project.value != -1">
+ <a name="action_view_all_rating" type="object" context="{'search_default_rating_last_30_days':1}">
+ <i class="fa fa-smile-o"/> <t t-esc="record.percentage_satisfaction_project.value"/>%
+ </a>
+ </t>
+ </b>
+ </div>
</div>
</div>
<div class="o_kanban_card_manage_pane" groups="project.group_project_manager">
@@ -308,6 +339,9 @@
<div>
<a type="edit">Edit</a>
</div>
+ <div t-if="record.rating_status.raw_value != 'no'">
+ <a name="action_view_all_rating" type="object">Customer Ratings</a>
+ </div>
</div>
<div class="o_project_kanban_colorpicker">
<ul class="oe_kanban_colorpicker" data-field="color"/>
@@ -401,6 +435,9 @@
context="{'project_id': subtask_project_id, 'name': name, 'partner_id': partner_id}" groups="project.group_subtask_project">
<field string="Sub-tasks" name="subtask_count" widget="statinfo"/>
</button>
+ <button name="%(rating_rating_action_task)d" type="action" attrs="{'invisible': [('rating_count', '=', 0)]}" class="oe_stat_button" icon="fa-smile-o" groups="project.group_project_rating">
+ <field name="rating_count" string="Rating" widget="statinfo"/>
+ </button>
<button name="toggle_active" type="object" groups="base.group_user"
class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button"
@@ -496,6 +533,8 @@
<field name="legend_done"/>
<field name="activity_ids"/>
<field name="activity_state"/>
+ <field name="rating_last_value"/>
+ <field name="rating_ids"/>
<progressbar field="kanban_state" colors='{"done": "success", "blocked": "danger"}'/>
<templates>
<t t-name="kanban-box">
@@ -540,6 +579,11 @@
<t t-if="record.message_needaction_counter.raw_value">
<span class='oe_kanban_mail_new' title='Unread Messages'><i class='fa fa-comments'/><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"/>
+ <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"/>
+ <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"/>
+ </b>
</div>
<div class="oe_kanban_bottom_right">
<field name="kanban_state" widget="state_selection" groups="base.group_user"/>
@@ -741,6 +785,8 @@
<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"/>
@@ -891,4 +937,10 @@
parent="menu_project_report"
sequence="10"/>
+ <menuitem id="rating_rating_menu_project"
+ action="rating_rating_action_project_report"
+ parent="menu_project_report"
+ groups="project.group_project_rating"
+ sequence="40"/>
+
</odoo>
diff --git a/addons/project/views/rating_views.xml b/addons/project/views/rating_views.xml
new file mode 100644
index 00000000000..87dc95ac43f
--- /dev/null
+++ b/addons/project/views/rating_views.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+ <record id="rating_rating_view_search_project" model="ir.ui.view">
+ <field name="name">rating.rating.search.project</field>
+ <field name="model">rating.rating</field>
+ <field name="inherit_id" ref="rating.rating_rating_view_search"/>
+ <field name="mode">primary</field>
+ <field name="arch" type="xml">
+ <xpath expr="//filter[@name='resource']" position="after">
+ <field name="parent_res_name"/>
+ <filter string="Project" name="groupby_project" context="{'group_by': 'parent_res_name'}"/>
+ </xpath>
+ <xpath expr="/search" position="inside">
+ <filter string="Last 30 days" name="rating_last_30_days" domain="[('create_date', '>=', (context_today()-datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]"/>
+ <separator/>
+ </xpath>
+ </field>
+ </record>
+
+ <record id="rating_rating_action_view_project_rating" model="ir.actions.act_window">
+ <field name="name">Rating</field>
+ <field name="res_model">rating.rating</field>
+ <field name="view_mode">kanban,tree,graph,pivot,form</field>
+ <field name="domain">[('consumed','=',True), ('parent_res_model','=','project.project'), ('parent_res_id','=',active_id)]</field>
+ <field name="search_view_id" ref="rating_rating_view_search_project"/>
+ <field name="help" type="html">
+ <p>
+ There is no rating for this object at the moment.
+ </p>
+ </field>
+ </record>
+
+ <record id="rating_rating_action_view_project_rating_task" model="ir.actions.act_window">
+ <field name="name">Ratings for tasks</field>
+ <field name="res_model">rating.rating</field>
+ <field name="view_mode">kanban,tree,graph,pivot,form</field>
+ <field name="domain">[('consumed','=',True), ('parent_res_model','=','project.project'), ('parent_res_id','=',active_id), ('res_model','=','project.task')]</field>
+ <field name="search_view_id" ref="rating_rating_view_search_project"/>
+ <field name="help" type="html">
+ <p>
+ There is no rating for tasks of this project at the moment.
+ </p>
+ </field>
+ </record>
+
+ <record id="rating_rating_action_task" model="ir.actions.act_window">
+ <field name="name">Customer Ratings</field>
+ <field name="res_model">rating.rating</field>
+ <field name="view_mode">kanban,tree,pivot,graph,form</field>
+ <field name="domain">[('res_model', '=', 'project.task'), ('res_id', '=', active_id), ('consumed', '=', True)]</field>
+ <field name="search_view_id" ref="rating_rating_view_search_project"/>
+ <field name="help" type="html">
+ <p>
+ Customer ratings on tasks. If you have no rating, change your project Settings to activate it.
+ </p>
+ </field>
+ </record>
+
+ <record id="rating_rating_action_project_report" model="ir.actions.act_window">
+ <field name="name">Customer Ratings</field>
+ <field name="res_model">rating.rating</field>
+ <field name="view_mode">kanban,tree,pivot,graph,form</field>
+ <field name="domain">[('parent_res_model','=','project.project'), ('consumed', '=', True)]</field>
+ <field name="search_view_id" ref="rating_rating_view_search_project"/>
+ <field name="help" type="html">
+ <p>
+ Customer ratings on tasks. If you have no rating, change your project Settings to activate it.
+ </p>
+ </field>
+ <field name="context">{}</field>
+ </record>
+
+</odoo>
diff --git a/addons/project/views/res_config_settings_views.xml b/addons/project/views/res_config_settings_views.xml
index 9914fe6b525..030b9090445 100644
--- a/addons/project/views/res_config_settings_views.xml
+++ b/addons/project/views/res_config_settings_views.xml
@@ -34,14 +34,18 @@
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
- <field name="module_rating_project"/>
+ <field name="group_project_rating"/>
</div>
<div class="o_setting_right_pane">
- <label for="module_rating_project"/>
+ <label for="group_project_rating"/>
<div class="text-muted">
Track customer satisfaction on tasks
</div>
- <div class="content-group" id="msg_module_rating_project"/>
+ <div class="content-group" attrs="{'invisible': [('group_project_rating', '=', False)]}">
+ <div class="mt16">
+ <button name="%(project.open_task_type_form)d" icon="fa-arrow-right" type="action" string="Set Email Template to Stages" class="btn-link"/>
+ </div>
+ </div>
</div>
</div>
</div>