[IMP] project: Merge tasks and issues
Purpose ======= Advantages : ~~~~~~~~~~~~ - using tasks and issues in a project is not easy. Indeed stages are shared but the process is rarely exactly the same. That's why we have a project for master bugs in our prod. - having tasks and issues causes issues with email templates (it is easy to put a task-related template on an issue-related stage and that may cause crashes) and rating (same here) - views are globally the same - using tasks and issues causes issues with mail alias, as you can create only one of them. We have some strange onchange to ensure the alias creates the right model but I don't think it is really user friendly - code is globally the same between task and issue and therefore will cut the code in half for those models - timesheeting on issues is not as complete as timesheeting on tasks, and merging them makes sure we have only one way of working with timesheets in project - customer portal can be simplified as we have only one menu item for project, leading to code reduction and portal simplification
This commit is contained in:
+6
-31
@@ -192,9 +192,9 @@ file_filter = addons/crm_phone_validation/i18n/<lang>.po
|
||||
source_file = addons/crm_phone_validation/i18n/crm_phone_validation.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.crm_project_issue]
|
||||
file_filter = addons/crm_project_issue/i18n/<lang>.po
|
||||
source_file = addons/crm_project_issue/i18n/crm_project_issue.pot
|
||||
[odoo-master.crm_project]
|
||||
file_filter = addons/crm_project/i18n/<lang>.po
|
||||
source_file = addons/crm_project/i18n/crm_project.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.decimal_precision]
|
||||
@@ -567,16 +567,6 @@ file_filter = addons/project/i18n/<lang>.po
|
||||
source_file = addons/project/i18n/project.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.project_issue]
|
||||
file_filter = addons/project_issue/i18n/<lang>.po
|
||||
source_file = addons/project_issue/i18n/project_issue.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.project_issue_sheet]
|
||||
file_filter = addons/project_issue_sheet/i18n/<lang>.po
|
||||
source_file = addons/project_issue_sheet/i18n/project_issue_sheet.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.project_timesheet_holidays]
|
||||
file_filter = addons/project_timesheet_holidays/i18n/<lang>.po
|
||||
source_file = addons/project_timesheet_holidays/i18n/project_timesheet_holidays.pot
|
||||
@@ -607,11 +597,6 @@ file_filter = addons/rating_project/i18n/<lang>.po
|
||||
source_file = addons/rating_project/i18n/rating_project.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.rating_project_issue]
|
||||
file_filter = addons/rating_project_issue/i18n/<lang>.po
|
||||
source_file = addons/rating_project_issue/i18n/rating_project_issue.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.report]
|
||||
file_filter = addons/report/i18n/<lang>.po
|
||||
source_file = addons/report/i18n/report.pot
|
||||
@@ -902,16 +887,6 @@ file_filter = addons/website_project/i18n/<lang>.po
|
||||
source_file = addons/website_project/i18n/website_project.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.website_project_issue]
|
||||
file_filter = addons/website_project_issue/i18n/<lang>.po
|
||||
source_file = addons/website_project_issue/i18n/website_project_issue.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.website_project_issue_sheet]
|
||||
file_filter = addons/website_project_issue_sheet/i18n/<lang>.po
|
||||
source_file = addons/website_project_issue_sheet/i18n/website_project_issue_sheet.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.website_project_timesheet]
|
||||
file_filter = addons/website_project_timesheet/i18n/<lang>.po
|
||||
source_file = addons/website_project_timesheet/i18n/website_project_timesheet.pot
|
||||
@@ -922,9 +897,9 @@ file_filter = addons/website_quote/i18n/<lang>.po
|
||||
source_file = addons/website_quote/i18n/website_quote.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.website_rating_project_issue]
|
||||
file_filter = addons/website_rating_project_issue/i18n/<lang>.po
|
||||
source_file = addons/website_rating_project_issue/i18n/website_rating_project_issue.pot
|
||||
[odoo-master.website_rating_project]
|
||||
file_filter = addons/website_rating_project/i18n/<lang>.po
|
||||
source_file = addons/website_rating_project/i18n/website_rating_project.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.website_sale]
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
'name': 'Lead to Issue',
|
||||
'summary': 'Create Issues from Leads',
|
||||
'name': 'Lead to Tasks',
|
||||
'summary': 'Create Tasks from Leads',
|
||||
'sequence': '19',
|
||||
'category': 'Project',
|
||||
'complexity': 'easy',
|
||||
'description': """
|
||||
Lead to Issues
|
||||
==============
|
||||
Lead to Tasks
|
||||
=============
|
||||
|
||||
Link module to map leads to issues
|
||||
Link module to map leads to tasks
|
||||
""",
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/crm_lead2projectissue_wizard_view.xml',
|
||||
'wizard/crm_lead_convert2task_views.xml',
|
||||
'views/crm_lead_views.xml'
|
||||
],
|
||||
'depends': ['crm', 'project_issue'],
|
||||
'depends': ['crm', 'project'],
|
||||
}
|
||||
+5
-5
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<!-- inherit view of Lead Form : adding the "convert to issue" button -->
|
||||
<record id="crm_case_form_view_leads_project_issue" model="ir.ui.view">
|
||||
<field name="name">crm.lead.form.inherit.lead2projectissue</field>
|
||||
<!-- inherit view of Lead Form : adding the "convert to task" button -->
|
||||
<record id="crm_lead_view_form" model="ir.ui.view">
|
||||
<field name="name">crm.lead.form</field>
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="inherit_id" ref="crm.crm_case_form_view_leads" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='toggle_active']" position="before">
|
||||
<button class="oe_stat_button" type="action" name="%(convert_lead2projectissue_wizard_action)d" icon="fa-bug" help="Convert to Issue">
|
||||
<button class="oe_stat_button" type="action" name="%(crm_lead_convert2task_action)d" icon="fa-bug" help="Convert to Task">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_text">Convert To</span>
|
||||
<span class="o_stat_text">Issue</span>
|
||||
<span class="o_stat_text">Task</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import crm_lead_convert2task
|
||||
+15
-15
@@ -2,32 +2,32 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class CrmLeadToProjectIssueWizard(models.TransientModel):
|
||||
""" wizard to convert a Lead into a Project Issue and move the Mail Thread """
|
||||
class CrmLeadConvert2Task(models.TransientModel):
|
||||
""" wizard to convert a Lead into a Project task and move the Mail Thread """
|
||||
|
||||
_name = "crm.lead2projectissue.wizard"
|
||||
_name = "crm.lead.convert2task"
|
||||
_inherit = 'crm.partner.binding'
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
result = super(CrmLeadToProjectIssueWizard, self).default_get(fields)
|
||||
result = super(CrmLeadConvert2Task, self).default_get(fields)
|
||||
lead_id = self.env.context.get('active_id')
|
||||
if lead_id:
|
||||
result['lead_id'] = lead_id
|
||||
return result
|
||||
|
||||
lead_id = fields.Many2one('crm.lead', string='Lead', domain=[('type', '=', 'lead')])
|
||||
project_id = fields.Many2one('project.project', string='Project', domain=[('use_issues', '=', True)])
|
||||
project_id = fields.Many2one('project.project', string='Project', domain=[('use_tasks', '=', True)])
|
||||
|
||||
@api.multi
|
||||
def action_lead_to_project_issue(self):
|
||||
def action_lead_to_project_task(self):
|
||||
self.ensure_one()
|
||||
# get the lead to transform
|
||||
lead = self.lead_id
|
||||
partner_id = self._find_matching_partner()
|
||||
if not partner_id and (lead.partner_name or lead.contact_name):
|
||||
partner_id = lead.handle_partner_assignation()[lead.id]
|
||||
# create new project.issue
|
||||
# create new project.task
|
||||
vals = {
|
||||
"name": lead.name,
|
||||
"description": lead.description,
|
||||
@@ -36,23 +36,23 @@ class CrmLeadToProjectIssueWizard(models.TransientModel):
|
||||
"partner_id": partner_id,
|
||||
"user_id": None
|
||||
}
|
||||
issue = self.env['project.issue'].create(vals)
|
||||
task = self.env['project.task'].create(vals)
|
||||
# move the mail thread
|
||||
lead.message_change_thread(issue)
|
||||
lead.message_change_thread(task)
|
||||
# move attachments
|
||||
attachments = self.env['ir.attachment'].search([('res_model', '=', 'crm.lead'), ('res_id', '=', lead.id)])
|
||||
attachments.write({'res_model': 'project.issue', 'res_id': issue.id})
|
||||
attachments.write({'res_model': 'project.task', 'res_id': task.id})
|
||||
# archive the lead
|
||||
lead.write({'active': False})
|
||||
# return the action to go to the form view of the new Issue
|
||||
view = self.env.ref('project_issue.project_issue_form_view')
|
||||
# return the action to go to the form view of the new Task
|
||||
view = self.env.ref('project.view_task_form2')
|
||||
return {
|
||||
'name': 'Issue created',
|
||||
'name': 'Task created',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'view_id': view.id,
|
||||
'res_model': 'project.issue',
|
||||
'res_model': 'project.task',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': issue.id,
|
||||
'res_id': task.id,
|
||||
'context': self.env.context
|
||||
}
|
||||
+8
-8
@@ -2,16 +2,16 @@
|
||||
<odoo>
|
||||
|
||||
<!-- view of the Wizard Form -->
|
||||
<record id="view_crm_lead2projectissue_wizard" model="ir.ui.view">
|
||||
<field name="name">crm.lead2projectissue.wizard.form</field>
|
||||
<field name="model">crm.lead2projectissue.wizard</field>
|
||||
<record id="crm_lead_convert2task_view_form" model="ir.ui.view">
|
||||
<field name="name">crm.lead.convert2task.form</field>
|
||||
<field name="model">crm.lead.convert2task</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Convert to Issue">
|
||||
<form string="Convert to Task">
|
||||
<group>
|
||||
<field name="project_id"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button type="object" name="action_lead_to_project_issue" string="Create Issue" class="btn-primary"/>
|
||||
<button type="object" name="action_lead_to_project_task" string="Create Task" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
@@ -19,9 +19,9 @@
|
||||
</record>
|
||||
|
||||
<!-- action of converting, via wizard -->
|
||||
<record id="convert_lead2projectissue_wizard_action" model="ir.actions.act_window">
|
||||
<field name="name">Convert to Issue</field>
|
||||
<field name="res_model">crm.lead2projectissue.wizard</field>
|
||||
<record id="crm_lead_convert2task_action" model="ir.actions.act_window">
|
||||
<field name="name">Convert to Task</field>
|
||||
<field name="res_model">crm.lead.convert2task</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import crm_lead2projectissue_wizard
|
||||
@@ -2172,8 +2172,8 @@ class MailThread(models.AbstractModel):
|
||||
:param new_res_id : the new res_id of the mail.message
|
||||
:param new_model : the name of the new model of the mail.message
|
||||
|
||||
Example : my_lead.message_change_thread(my_project_issue)
|
||||
will transfer the context of the thread of my_lead to my_project_issue
|
||||
Example : my_lead.message_change_thread(my_project_task)
|
||||
will transfer the context of the thread of my_lead to my_project_task
|
||||
"""
|
||||
self.ensure_one()
|
||||
# get the subtype of the comment Message
|
||||
|
||||
@@ -15,7 +15,7 @@ This module adds a PAD in all project form views.
|
||||
],
|
||||
'data': [
|
||||
'views/project_config_settings_views.xml',
|
||||
'views/project_task.xml'
|
||||
'views/project_views.xml'
|
||||
],
|
||||
'auto_install': True,
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import project_config_settings
|
||||
from . import project_task
|
||||
from . import project
|
||||
|
||||
@@ -9,3 +9,10 @@ class ProjectTask(models.Model):
|
||||
_inherit = ["project.task", 'pad.common']
|
||||
|
||||
description_pad = fields.Char('Pad URL', pad_content_field='description')
|
||||
use_pad = fields.Boolean(related="project_id.use_pads", string="Use collaborative pad")
|
||||
|
||||
|
||||
class ProjectProject(models.Model):
|
||||
_inherit = "project.project"
|
||||
|
||||
use_pads = fields.Boolean("Use collaborative pads", help="Use collaborative pad for the tasks on this project.")
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_task_form2_inherit_pad_project" model="ir.ui.view">
|
||||
<field name="name">project.task.form.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_form2" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="description" position="replace">
|
||||
<field name="description_pad" widget="pad" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_task_form2_inherit_pad_project" model="ir.ui.view">
|
||||
<field name="name">project.task.form.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="project.view_task_form2"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='description']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('use_pad', '=', True)]}</attribute>
|
||||
</xpath>
|
||||
<field name="description" position="after">
|
||||
<field name="use_pad" invisible="1"/>
|
||||
<field name="description_pad" widget="pad" attrs="{'invisible': [('use_pad', '=', False)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_project_view_form" model="ir.ui.view">
|
||||
<field name="name">project.project.view.form</field>
|
||||
<field name="model">project.project</field>
|
||||
<field name="inherit_id" ref="project.edit_project"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="after">
|
||||
<field name="use_pads"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -98,7 +98,6 @@
|
||||
<field name="privacy_visibility">followers</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="active" eval="False"/>
|
||||
<field name="alias_model">project.task</field>
|
||||
<field name="alias_name">discover</field>
|
||||
<field name="alias_model_id" ref="model_project_task"/>
|
||||
<field name="alias_contact">everyone</field>
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
<field name="name">Website for Sales & WMS</field>
|
||||
<field name="color">3</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="alias_model">project.task</field>
|
||||
<field name="type_ids" eval="[(4, ref('project_stage_0')), (4, ref('project_stage_1')), (4, ref('project_stage_2')), (4, ref('project_stage_3'))]"/>
|
||||
</record>
|
||||
|
||||
@@ -74,7 +73,6 @@
|
||||
<field name="name">Research & Development</field>
|
||||
<field name="privacy_visibility">followers</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="alias_model">project.task</field>
|
||||
<field name="type_ids" eval="[(4, ref('project_stage_0')), (4, ref('project_stage_1')), (4, ref('project_stage_2')), (4, ref('project_stage_3'))]"/>
|
||||
</record>
|
||||
|
||||
@@ -83,7 +81,6 @@
|
||||
<field name="name">E-Learning Integration</field>
|
||||
<field name="label_tasks">Trainings</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="alias_model">project.task</field>
|
||||
<field name="type_ids" eval="[(4, ref('project_stage_0')), (4, ref('project_stage_1')), (4, ref('project_stage_2')), (4, ref('project_stage_3'))]"/>
|
||||
<field name="privacy_visibility">employees</field>
|
||||
</record>
|
||||
@@ -94,7 +91,6 @@
|
||||
<field name="label_tasks">Designs</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="privacy_visibility">followers</field>
|
||||
<field name="alias_model">project.task</field>
|
||||
<field name="type_ids" eval="[(4, ref('project_stage_0')), (4, ref('project_stage_1')), (4, ref('project_stage_2')), (4, ref('project_stage_3'))]"/>
|
||||
<field name="privacy_visibility">employees</field>
|
||||
</record>
|
||||
@@ -104,7 +100,6 @@
|
||||
<field name="partner_id" ref="base.res_partner_4"/>
|
||||
<field name="name">Data Import/Export Plugin</field>
|
||||
<field name="label_tasks">Databases</field>
|
||||
<field name="alias_model">project.task</field>
|
||||
<field name="type_ids" eval="[(4, ref('project_stage_0')), (4, ref('project_stage_1')), (4, ref('project_stage_2')), (4, ref('project_stage_3'))]"/>
|
||||
<field name="privacy_visibility">followers</field>
|
||||
</record>
|
||||
|
||||
@@ -117,10 +117,10 @@
|
||||
<t t-call="web_planner.page">
|
||||
<t t-set="page_title">Your Projects</t>
|
||||
<t t-set="page_menutitle" t-value="'Projects'"/>
|
||||
<p class="text-muted">Depending on what you need and how you want to operate, there are several ways to work with Odoo. First, decide if you want to think in terms of tasks or issues. Then, activate the Timesheets app if you need it.</p>
|
||||
<h4>1. Learn about Tasks, Issues and Timesheets.</h4>
|
||||
<p class="text-muted">Depending on what you need and how you want to operate, there are several ways to work with Odoo. First, decide if you want to think in terms of tasks. Then, activate the Timesheets app if you need it.</p>
|
||||
<h4>1. Learn about Tasks and Timesheets.</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-success">
|
||||
<div class="panel-heading">
|
||||
<span class="panel-title">
|
||||
@@ -138,24 +138,7 @@
|
||||
<p>Tasks are the main mechanism in Odoo and are activated by default.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><div class="col-md-4">
|
||||
<div class="panel panel-success">
|
||||
<div class="panel-heading">
|
||||
<span class="panel-title">
|
||||
<span class="fa fa-exclamation-circle"></span>
|
||||
<strong> Issues</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
<strong>A problem or request from a customer</strong> that needs to be identified, solved and followed up asap.</p>
|
||||
<p><strong>Example: </strong></p>
|
||||
<p>A customer sends a complaint to support@yourcompany.com: an Issue is automatically created into a 'Support level 1' project based on the original email from the customer.</p><br/>
|
||||
<p>To use Issues, install the <a t-att-href="prepare_backend_url('base.open_module_tree', 'form', 'project_issue')">Issue Tracking app.</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><div class="col-md-4">
|
||||
</div><div class="col-md-6">
|
||||
<div class="panel panel-success">
|
||||
<div class="panel-heading">
|
||||
<span class="panel-title">
|
||||
@@ -171,9 +154,7 @@
|
||||
<li> <strong>Getting statistics</strong> on how much time a task takes to be completed</li>
|
||||
<li> <strong>Invoicing your customers</strong> on Time & Material projects</li></p>
|
||||
<p>To use Timesheets, go to your <a t-att-href="prepare_backend_url(
|
||||
'project.action_config_settings')">Project Settings</a> and activate:</p>
|
||||
<p><li>for Tasks: 'Log work activities on tasks'</li>
|
||||
<li>for Issues: 'Activate timesheets on issues'</li></p>
|
||||
'project.action_config_settings')">Project Settings</a> and activate 'Log work activities on tasks'</p>
|
||||
<p>You can learn more about Timesheets in the 'Use Timesheets' section of this planner.</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -189,36 +170,26 @@
|
||||
<li><span>#1</span>
|
||||
<div class="small">
|
||||
<input id="input_element_prj1" value="" line="1" type="text"/>
|
||||
<input type="radio" id="input_element_rowtask1" name="row1" checked="checked"/>Tasks
|
||||
<input type="radio" id="input_element_rowissue1" name="row1"/>Issues
|
||||
<input type="checkbox" id="input_element_rowtime1" name="rowtime1"/>with Timesheet
|
||||
</div></li>
|
||||
<li><span>#2</span>
|
||||
<div class="small">
|
||||
<input id="input_element_prj2" value="" line="1" type="text"/>
|
||||
<input type="radio" id="input_element_rowtask2" name="row2" checked="checked"/>Tasks
|
||||
<input type="radio" id="input_element_rowissue2" name="row2"/>Issues
|
||||
<input type="checkbox" id="input_element_rowtime2" name="rowtime2"/>with Timesheet
|
||||
</div></li>
|
||||
<li><span>#3</span>
|
||||
<div class="small">
|
||||
<input id="input_element_prj3" value="" line="1" type="text"/>
|
||||
<input type="radio" id="input_element_rowtask3" name="row3" checked="checked"/>Tasks
|
||||
<input type="radio" id="input_element_rowissue3" name="row3"/>Issues
|
||||
<input type="checkbox" id="input_element_rowtime3" name="rowtime3"/>with Timesheet
|
||||
</div></li>
|
||||
<li><span>#4</span>
|
||||
<div class="small">
|
||||
<input id="input_element_prj4" value="" line="1" type="text"/>
|
||||
<input type="radio" id="input_element_rowtask4" name="row4" checked="checked"/>Tasks
|
||||
<input type="radio" id="input_element_rowissue4" name="row4"/>Issues
|
||||
<input type="checkbox" id="input_element_rowtime4" name="rowtime4"/>with Timesheet
|
||||
</div></li>
|
||||
<li><span>#5</span>
|
||||
<div class="small">
|
||||
<input id="input_element_prj5" value="" line="1" type="text"/>
|
||||
<input type="radio" id="input_element_rowtask5" name="row5" checked="checked"/>Tasks
|
||||
<input type="radio" id="input_element_rowissue5" name="row5"/>Issues
|
||||
<input type="checkbox" id="input_element_rowtime5" name="rowtime5"/>with Timesheet
|
||||
</div></li>
|
||||
</ul>
|
||||
@@ -255,7 +226,7 @@
|
||||
<t t-set="page_title">Kanban Stages</t>
|
||||
<p class="text-muted">
|
||||
It is time to think about how you will transform your activities into real projects in Odoo.<br/>
|
||||
For that, the most important part is defining the stages of your projects. Stages are the different steps a task or an issue can go through, from its creation to its ending. They will appear in what we call the 'Kanban' view of your projects.
|
||||
For that, the most important part is defining the stages of your projects. Stages are the different steps a task can go through, from its creation to its ending. They will appear in what we call the 'Kanban' view of your projects.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="o_planner_notebook_container col-md-12">
|
||||
@@ -371,9 +342,9 @@
|
||||
<t t-set="menu_categorytitle">Implement</t>
|
||||
<t t-set="menu_categoryclasses" t-value="'fa-cogs'"/>
|
||||
<t t-call="web_planner.page">
|
||||
<t t-set="page_title">Tasks & Issues</t>
|
||||
<t t-set="page_title">Tasks</t>
|
||||
<p class="text-muted">
|
||||
Creating Tasks and/or Issues is the next step in managing your Projects.<br/>
|
||||
Creating Tasks is the next step in managing your Projects.<br/>
|
||||
In Odoo, it is pretty straightforward, but here are some explanations you may find useful.
|
||||
</p>
|
||||
<section>
|
||||
@@ -390,7 +361,7 @@
|
||||
<div class="col-sm-1 col-md-2 icon"><span data-icon=""></span></div>
|
||||
<div class="col-sm-11 col-md-10">
|
||||
<p><strong>Add a Deadline</strong><br/>
|
||||
The deadline will help you determine if a task or issue is progressing as expected and to anticipate its next update.
|
||||
The deadline will help you determine if a task is progressing as expected and to anticipate its next update.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -398,7 +369,7 @@
|
||||
<div class="col-sm-1 col-md-2 icon"><span data-icon=""></span></div>
|
||||
<div class="col-sm-11 col-md-10">
|
||||
<p><strong>Use Tags</strong><br/>
|
||||
Tags are complementary to your project stages: they can work as a second level categorization which is very useful if you have a lot of Tasks or Issues to manage. Also, it becomes very easy to find Tasks or Issues by typing the Tag into the main Search bar.
|
||||
Tags are complementary to your project stages: they can work as a second level categorization which is very useful if you have a lot of Tasks to manage. Also, it becomes very easy to find Tasks by typing the Tag into the main Search bar.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -406,7 +377,7 @@
|
||||
<div class="col-sm-1 col-md-2 icon"><span data-icon=""></span></div>
|
||||
<div class="col-sm-11 col-md-10">
|
||||
<p><strong>Use Status Indicator</strong><br/>
|
||||
The Status indicator helps you manage Tasks and Issues by giving them one of 3 different colour: Grey, Green or Red. The meaning of these Statuses can be freely configured, for example:
|
||||
The Status indicator helps you manage Tasks by giving them one of 3 different colour: Grey, Green or Red. The meaning of these Statuses can be freely configured, for example:
|
||||
<li>Grey: the Task is in progress (someone is working on it)</li>
|
||||
<li>Green: the Task is ready for next stage (the job for this stage is complete)</li>
|
||||
<li>Red: the Task is blocked (there's a problem)</li></p>
|
||||
@@ -417,7 +388,7 @@
|
||||
<div class="col-sm-1 col-md-2 icon"><span data-icon=""></span></div>
|
||||
<div class="col-sm-11 col-md-10">
|
||||
<p><strong>Set Priorities</strong><br/>
|
||||
The <i class="fa fa-star"/> is used to indicate priority: in Kanban views, high priority Tasks or Issues will be displayed on top. This is particulary useful if you use a Scrum methodology to indicate the tasks for the week. Similarly to Status, you can change the meaning of the Star indicator from the Project Stages tab.
|
||||
The <i class="fa fa-star"/> is used to indicate priority: in Kanban views, high priority Tasks will be displayed on top. This is particulary useful if you use a Scrum methodology to indicate the tasks for the week. Similarly to Status, you can change the meaning of the Star indicator from the Project Stages tab.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -434,7 +405,7 @@
|
||||
<div class="alert alert-info info_icon" role="alert">
|
||||
<span class="fa fa-lightbulb-o fa-lg"/>
|
||||
<p><strong>A great feature in Odoo is the integration of a Collaborative Notepad called Etherpad.</strong><br/>
|
||||
It replaces the standard Description area in Tasks and Issues and is extremely useful for several cases.</p><br/>
|
||||
It replaces the standard Description area in Tasks and is extremely useful for several cases.</p><br/>
|
||||
<p><li><strong>You have different users writing Tasks descriptions</strong><br/>
|
||||
It can quickly become messy if everyone uses his own layout. Etherpad will allow you to create a basic template with a few titles and bullet points, making it much easier for everyone.</li></p><br/>
|
||||
<p><li><strong>You organize distant meetings</strong><br/>
|
||||
@@ -480,7 +451,6 @@
|
||||
<a t-if="not is_module_installed('hr_timesheet')" t-att-href="prepare_backend_url('base.open_module_tree', 'form', 'hr_timesheet')">Current Timesheet</a>
|
||||
view of the HR module is the main tool to check, modify and confirm Timesheets.</li><br/>
|
||||
<li>Alternatively, Timesheets can be added directly from Tasks by activating <strong>'Log work activities on tasks'</strong> in the <a t-att-href="prepare_backend_url('project.action_config_settings')">Project Settings</a>.</li><br/>
|
||||
<li>Or, if you are using Issues, by activating 'Activate timesheets on issues', also in the <a t-att-href="prepare_backend_url('project.action_config_settings')">Project Settings</a>.</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -528,7 +498,7 @@
|
||||
</div>
|
||||
<h3><strong>Invoice your customers</strong> (every month)</h3>
|
||||
<p>
|
||||
If you work on a Time & Material project, you'll probably want to extract a Timesheet of the tasks and issues to invoice directly to the customer. To do that:
|
||||
If you work on a Time & Material project, you'll probably want to extract a Timesheet of the tasks to invoice directly to the customer. To do that:
|
||||
</p>
|
||||
<ul>
|
||||
<li> Activate 'Allow invoicing based on timesheets' from the <i>Human Resources Settings</i>.
|
||||
@@ -536,21 +506,18 @@
|
||||
<li> This will add an Invoice Tasks menu in the Project module, that can be used to select the Timesheet to invoice.</li>
|
||||
</ul>
|
||||
<h3><strong>Analyze reports</strong> (every a year)</h3>
|
||||
<p>It's usually a good idea to take time to analyze your tasks and issues once a year. Here are some KPIs you should take a look at. Ask yourself 'How can they be improved?'</p>
|
||||
<p>It's usually a good idea to take time to analyze your tasks once a year. Here are some KPIs you should take a look at. Ask yourself 'How can they be improved?'</p>
|
||||
<p><strong>For tasks:</strong></p>
|
||||
<ul>
|
||||
<li>What is the number of missed deadlines?</li>
|
||||
<li>What is the difference between initial time estimation and final time spent?</li>
|
||||
</ul>
|
||||
<p><strong>For issues:</strong></p>
|
||||
<ul>
|
||||
<li>What is the average time before an issue is assigned / closed?</li>
|
||||
<li>What is the average number of working hours necessary to close an issue?</li>
|
||||
<li>What is the average time before a task is assigned / closed?</li>
|
||||
<li>What is the average number of working hours necessary to close a task?</li>
|
||||
</ul>
|
||||
<p><strong>About Employees:</strong></p>
|
||||
<ul>
|
||||
<li>What is their average number of working hours over the year?</li>
|
||||
<li>What is their average number of tasks or issues worked on / closed?</li>
|
||||
<li>What is their average number of tasks worked on / closed?</li>
|
||||
</ul>
|
||||
<div class="well well-lg">
|
||||
<strong>Need help with Timesheets management? Contact us now:</strong><br/>
|
||||
@@ -601,7 +568,7 @@
|
||||
We can automate steps in your workflow, for example:
|
||||
</p>
|
||||
<li> Send an alert when a task is stuck in red for more than a few days</li>
|
||||
<li> Send an automatic confirmation to all issue emails sent to your customer support</li>
|
||||
<li> Send an automatic confirmation to all emails sent to your customer support</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -823,12 +790,12 @@
|
||||
<div class="col-md-7">
|
||||
<p>
|
||||
<strong>Follow only what you need</strong><br/>
|
||||
The simplest way to use notifications is to follow a whole project: you will receive notifications for all the new and existing tasks or issues of a project.
|
||||
The simplest way to use notifications is to follow a whole project: you will receive notifications for all the new and existing tasks of a project.
|
||||
</p>
|
||||
<p>
|
||||
Don't hesitate to select only the events you are interested in!
|
||||
</p>
|
||||
<p>Another good way to limit the number of notifications you receive is to only follow your Project's 'Task Assigned' events. Then you'll be notified when a Task or Issue is created, and can manually decide if you want to be notified for its other events too.</p>
|
||||
<p>Another good way to limit the number of notifications you receive is to only follow your Project's 'Task Assigned' events. Then you'll be notified when a Task is created, and can manually decide if you want to be notified for its other events too.</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="signature" src="/project/static/src/img/web_planner_subtype.png" />
|
||||
@@ -839,10 +806,10 @@
|
||||
<div class="col-md-10">
|
||||
<p>
|
||||
<strong>Rely on the chatter</strong><br/>
|
||||
Below every Task and Issue (or more generally, below every Document in Odoo) is an area called Chatter.
|
||||
Below every Task (or more generally, below every Document in Odoo) is an area called Chatter.
|
||||
</p>
|
||||
<p>It's for logging every change, event or message related to the Document.</p>
|
||||
<p>So if you're looking for the history of a Task, or the latest message on an Issue, simply go to the corresponding Document and you'll find it!</p>
|
||||
<p>So if you're looking for the history of a Task, or the latest message on a Task, simply go to the corresponding Document and you'll find it!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -864,10 +831,10 @@
|
||||
<div class="col-md-2 icon"><span data-icon=""></span></div>
|
||||
<div class="col-md-10">
|
||||
<p>
|
||||
<strong>Create tasks and issues by email</strong><br/>
|
||||
In Odoo, every project has an email alias. If you send an email to this alias, it will automatically create a task or issue in the first stage of the project, with all the email recipients as its default followers.</p>
|
||||
<strong>Create tasks by email</strong><br/>
|
||||
In Odoo, every project has an email alias. If you send an email to this alias, it will automatically create a task in the first stage of the project, with all the email recipients as its default followers.</p>
|
||||
<p>
|
||||
This is particularly useful to manage help and support: all incoming email from customers will be transformed into an issue that you'll be able to track easily!
|
||||
This is particularly useful to manage help and support: all incoming email from customers will be transformed into a task that you'll be able to track easily!
|
||||
</p>
|
||||
</div>
|
||||
<p class="col-md-8 col-md-push-2">
|
||||
@@ -902,7 +869,7 @@
|
||||
<strong>Ask participants to prepare</strong><br/>
|
||||
To run an effective meeting, prepare participants beforehand. Inform them of how they are expected to contribute.</p>
|
||||
<p>
|
||||
For example, risk and issue owners should come prepared to share the status of their item and, ideally, a path to resolution.
|
||||
For example, risk and task owners should come prepared to share the status of their item and, ideally, a path to resolution.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -924,7 +891,7 @@
|
||||
<strong>Start with an answer.</strong><br/>
|
||||
The worst question to ask is, "What did you do this week?" It invariably generates unnecessary, time-consuming dialogue from team members. Plus, you should already know what everyone on the team did during the week.</p>
|
||||
<p>
|
||||
For example, risk and issue owners should come prepared to share the status of their item and, ideally, a path to resolution.
|
||||
For example, risk and task owners should come prepared to share the status of their item and, ideally, a path to resolution.
|
||||
</p>
|
||||
<p>It is better to start with a "project answer", such as: "We are two weeks late", "We are at planned budget" or "We are 50% complete with the process model". Also if you can, start the meeting on a positive note, such as milestones that have been met or are ahead of schedule. This will make participants feel motivated to engage in the conversation.</p>
|
||||
</div>
|
||||
@@ -934,17 +901,17 @@
|
||||
<div class="col-xs-12 col-md-10">
|
||||
<strong> Prepare an agenda (and keep to it)</strong>
|
||||
<p>Be aware of the team’s productivity and time: try to keep meetings to one hour or less.</p>
|
||||
<p>Use separate meetings to solve big issues or issues that aren’t important for the entire team.</p>
|
||||
<p>Use separate meetings to solve big tasks or tasks that aren’t important for the entire team.</p>
|
||||
<p><li><strong> Roll call (5 min)</strong>
|
||||
Let everyone know who is in attendance before starting. This is important for remote team members who have dialled in and will save you time later.</li>
|
||||
<li><strong> New business issues (5 min)</strong>
|
||||
You may have learned something during the week that affects the project. Share the news early in the meeting to help team members consider the issue as you walk through the project status session. Not every issue warrants spending time here, so keep discussions to a minimum.</li>
|
||||
<li><strong> New business tasks (5 min)</strong>
|
||||
You may have learned something during the week that affects the project. Share the news early in the meeting to help team members consider the task as you walk through the project status session. Not every task warrants spending time here, so keep discussions to a minimum.</li>
|
||||
<li><strong> Project plan status review (20 min) </strong>
|
||||
Walk through your project plan and allow each team member to provide a brief status of assignments due this week and tasks planned for the next two weeks. You want to know whether tasks are on track and if any will miss their projected deadline. You also want to allow the team member to share any special considerations that might affect other tasks or members of the project. Carefully manage this part because some team members will want to pontificate and spend more time than is really needed. Remember, you’re the project manager.</li>
|
||||
<li><strong> Critical items (10 min)</strong>
|
||||
Upon completing the project status review session, summarize the critical items. </li>
|
||||
<li><strong> Q&A and discussion time (10 min)</strong>
|
||||
Always give your team time to ask questions on issues that were not discussed. This gives you another opportunity to reinforce key points that you've picked up during the week or discovered during the meeting.</li>
|
||||
Always give your team time to ask questions on tasks that were not discussed. This gives you another opportunity to reinforce key points that you've picked up during the week or discovered during the meeting.</li>
|
||||
<li><strong> Summary and follow-up items (5 min)</strong>
|
||||
Always wrap up with a project status summary and a list of action items dentified in the meeting to help move the project along.</li>
|
||||
<li><strong> Adjourn (5 min)</strong>
|
||||
@@ -956,7 +923,7 @@
|
||||
<div class="col-xs-12 col-md-10">
|
||||
<p>
|
||||
<strong>Follow every meeting with a recap email</strong>
|
||||
As soon as the meeting is over, publish meeting minutes and distribute them along with the updated project schedule, issues/action item matrix, and any other appropriate documents. Try to use the same template throughout meetings and improve it continuously.
|
||||
As soon as the meeting is over, publish meeting minutes and distribute them along with the updated project schedule, task/action item matrix, and any other appropriate documents. Try to use the same template throughout meetings and improve it continuously.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1002,15 +969,15 @@
|
||||
<span class="panel-title">
|
||||
<span class="fa fa-thumb-tack"></span>
|
||||
<strong> Exercise 2</strong><br/>
|
||||
<span class="small">Delay to close an Issue</span>
|
||||
<span class="small">Delay to close an Task</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
<strong>Exercise:</strong> Try to create a graph with the monthly evolution of the 'Average delay to close' of Issues.
|
||||
<strong>Exercise:</strong> Try to create a graph with the monthly evolution of the 'Average delay to close' of Tasks.
|
||||
</p>
|
||||
<li>
|
||||
<a t-att-href="prepare_backend_url('project_issue.project_issue_action_analysis', 'graph', 'project_issue')">Issues analysis</a>
|
||||
<a t-att-href="prepare_backend_url('project.action_project_task_user_tree', 'graph', 'project_task')">Tasks analysis</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1064,7 +1031,7 @@
|
||||
<field name="menu_id" ref="menu_main_pm"/>
|
||||
<field name="planner_application">planner_project</field>
|
||||
<field name="tooltip_planner"><![CDATA[
|
||||
Learn how to better organize your company using Projects, Tasks, Issues and Timesheets.
|
||||
Learn how to better organize your company using Projects, Tasks and Timesheets.
|
||||
]]>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -13,9 +13,6 @@ class ProjectTaskType(models.Model):
|
||||
_description = 'Task Stage'
|
||||
_order = 'sequence, id'
|
||||
|
||||
def _get_mail_template_id_domain(self):
|
||||
return [('model', '=', 'project.task')]
|
||||
|
||||
def _get_default_project_ids(self):
|
||||
default_project_id = self.env.context.get('default_project_id')
|
||||
return [default_project_id] if default_project_id else None
|
||||
@@ -40,7 +37,7 @@ class ProjectTaskType(models.Model):
|
||||
mail_template_id = fields.Many2one(
|
||||
'mail.template',
|
||||
string='Email Template',
|
||||
domain=lambda self: self._get_mail_template_id_domain(),
|
||||
domain=[('model', '=', 'project.task')],
|
||||
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.')
|
||||
@@ -101,11 +98,6 @@ class Project(models.Model):
|
||||
for project in self:
|
||||
project.task_needaction_count = mapped_data.get(project.id, 0)
|
||||
|
||||
@api.model
|
||||
def _get_alias_models(self):
|
||||
""" Overriden in project_issue to offer more options """
|
||||
return [('project.task', "Tasks")]
|
||||
|
||||
@api.multi
|
||||
def attachment_tree_view(self):
|
||||
self.ensure_one()
|
||||
@@ -168,9 +160,6 @@ class Project(models.Model):
|
||||
result['use_tasks'] = True
|
||||
return result
|
||||
|
||||
# Lambda indirection method to avoid passing a copy of the overridable method when declaring the field
|
||||
_alias_models = lambda self: self._get_alias_models()
|
||||
|
||||
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.")
|
||||
sequence = fields.Integer(default=10, help="Gives the sequence order when displaying a list of Projects.")
|
||||
@@ -201,8 +190,6 @@ class Project(models.Model):
|
||||
alias_id = fields.Many2one('mail.alias', string='Alias', ondelete="restrict", required=True,
|
||||
help="Internal email associated with this project. Incoming emails are automatically synchronized "
|
||||
"with Tasks (or optionally Issues if the Issue Tracker module is installed).")
|
||||
alias_model = fields.Selection(_alias_models, string="Alias Model", index=True, required=True, default='project.task',
|
||||
help="The kind of document created when an email is received on this project's email alias")
|
||||
privacy_visibility = fields.Selection([
|
||||
('followers', _('On invitation only')),
|
||||
('employees', _('Visible by all employees')),
|
||||
@@ -263,9 +250,6 @@ class Project(models.Model):
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
# if alias_model has been changed, update alias_model_id accordingly
|
||||
if vals.get('alias_model'):
|
||||
vals['alias_model_id'] = self.env['ir.model']._get(vals.get('alias_model', 'project.task')).id
|
||||
res = super(Project, self).write(vals)
|
||||
if 'active' in vals:
|
||||
# archiving/unarchiving a project does it on its tasks, too
|
||||
@@ -320,14 +304,6 @@ class Task(models.Model):
|
||||
_mail_post_access = 'read'
|
||||
_order = "priority desc, sequence, date_start, name, id"
|
||||
|
||||
@api.model
|
||||
def default_get(self, field_list):
|
||||
""" Set 'date_assign' if user_id is set. """
|
||||
result = super(Task, self).default_get(field_list)
|
||||
if 'user_id' in result:
|
||||
result['date_assign'] = fields.Datetime.now()
|
||||
return result
|
||||
|
||||
def _get_default_partner(self):
|
||||
if 'default_project_id' in self.env.context:
|
||||
default_project_id = self.env['project.project'].browse(self.env.context['default_project_id'])
|
||||
@@ -353,9 +329,10 @@ class Task(models.Model):
|
||||
name = fields.Char(string='Task Title', track_visibility='always', required=True, index=True)
|
||||
description = fields.Html(string='Description')
|
||||
priority = fields.Selection([
|
||||
('0','Non Starred'),
|
||||
('1','Starred')
|
||||
], default='0', index=True, string="Starred")
|
||||
('0', 'Low'),
|
||||
('1', 'Normal'),
|
||||
('2', 'High')
|
||||
], default='0', index=True, string="Priority")
|
||||
sequence = fields.Integer(string='Sequence', index=True, default=10,
|
||||
help="Gives the sequence order when displaying a list of tasks.")
|
||||
stage_id = fields.Many2one('project.task.type', string='Stage', track_visibility='onchange', index=True,
|
||||
@@ -417,6 +394,38 @@ class Task(models.Model):
|
||||
child_ids = fields.One2many('project.task', 'parent_id', string="Sub-tasks")
|
||||
subtask_project_id = fields.Many2one('project.project', related="project_id.subtask_project_id", string='Sub-task Project', readonly=True)
|
||||
subtask_count = fields.Integer(compute='_compute_subtask_count', type='integer', string="Sub-task count")
|
||||
email_from = fields.Char(string='Email', help="These people will receive email.", index=True)
|
||||
email_cc = fields.Char(string='Watchers Emails', help="""These email addresses will be added to the CC field of all inbound
|
||||
and outbound emails for this record before being sent. Separate multiple email addresses with a comma""")
|
||||
# Computed field about working time elapsed between record creation and assignation/closing.
|
||||
working_hours_open = fields.Float(compute='_compute_elapsed', string='Working hours to assign', store=True, group_operator="avg")
|
||||
working_hours_close = fields.Float(compute='_compute_elapsed', string='Working hours to close', store=True, group_operator="avg")
|
||||
working_days_open = fields.Float(compute='_compute_elapsed', string='Working days to assign', store=True, group_operator="avg")
|
||||
working_days_close = fields.Float(compute='_compute_elapsed', string='Working days to close', store=True, group_operator="avg")
|
||||
|
||||
@api.multi
|
||||
@api.depends('create_date', 'date_end', 'date_assign')
|
||||
def _compute_elapsed(self):
|
||||
task_linked_to_calendar = self.filtered(
|
||||
lambda task: task.project_id.resource_calendar_id and task.create_date
|
||||
)
|
||||
for task in task_linked_to_calendar:
|
||||
dt_create_date = fields.Datetime.from_string(task.create_date)
|
||||
|
||||
if task.date_assign:
|
||||
dt_date_assign = fields.Datetime.from_string(task.date_assign)
|
||||
task.working_hours_open = task.project_id.resource_calendar_id.get_work_hours_count(
|
||||
dt_create_date, dt_date_assign, False, compute_leaves=True)
|
||||
task.working_days_open = task.working_hours_open / 24.0
|
||||
|
||||
if task.date_end:
|
||||
dt_date_end = fields.Datetime.from_string(task.date_end)
|
||||
task.working_hours_close = task.project_id.resource_calendar_id.get_work_hours_count(
|
||||
dt_create_date, dt_date_end, False, compute_leaves=True)
|
||||
task.working_days_close = task.working_hours_close / 24.0
|
||||
|
||||
(self - task_linked_to_calendar).update(dict.fromkeys(
|
||||
['working_hours_open', 'working_hours_close', 'working_days_open', 'working_days_close'], 0.0))
|
||||
|
||||
@api.depends('stage_id', 'kanban_state')
|
||||
def _compute_kanban_state_label(self):
|
||||
@@ -428,6 +437,10 @@ class Task(models.Model):
|
||||
else:
|
||||
task.kanban_state_label = task.legend_done
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def _onchange_partner_id(self):
|
||||
self.email_from = self.partner_id.email
|
||||
|
||||
@api.onchange('project_id')
|
||||
def _onchange_project(self):
|
||||
default_partner_id = self.env.context.get('default_partner_id')
|
||||
@@ -436,8 +449,11 @@ class Task(models.Model):
|
||||
self.partner_id = self.project_id.partner_id or default_partner
|
||||
if self.project_id not in self.stage_id.project_ids:
|
||||
self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
|
||||
if not self.email_from:
|
||||
self.email_from = self.partner_id.email
|
||||
else:
|
||||
self.partner_id = default_partner
|
||||
self.email_from = default_partner.email
|
||||
self.stage_id = False
|
||||
|
||||
@api.onchange('user_id')
|
||||
@@ -466,11 +482,6 @@ class Task(models.Model):
|
||||
if task.parent_id.project_id and task.project_id != task.parent_id.project_id.subtask_project_id:
|
||||
raise UserError(_("You can't define a parent task if its project is not correctly configured. The sub-task's project of the parent task's project should be this task's project"))
|
||||
|
||||
@api.constrains('date_start', 'date_end')
|
||||
def _check_dates(self):
|
||||
if any(self.filtered(lambda task: task.date_start and task.date_end and task.date_start > task.date_end)):
|
||||
raise ValidationError(_('Error ! Task starting date must be lower than its ending date.'))
|
||||
|
||||
# Override view according to the company definition
|
||||
@api.model
|
||||
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
@@ -561,6 +572,9 @@ class Task(models.Model):
|
||||
# user_id change: update date_assign
|
||||
if vals.get('user_id'):
|
||||
vals['date_assign'] = fields.Datetime.now()
|
||||
# Stage change: Update date_end if folded stage
|
||||
if vals.get('stage_id'):
|
||||
vals.update(self.update_date_end(vals['stage_id']))
|
||||
task = super(Task, self.with_context(context)).create(vals)
|
||||
return task
|
||||
|
||||
@@ -569,18 +583,25 @@ class Task(models.Model):
|
||||
now = fields.Datetime.now()
|
||||
# stage change: update date_last_stage_update
|
||||
if 'stage_id' in vals:
|
||||
vals.update(self.update_date_end(vals['stage_id']))
|
||||
vals['date_last_stage_update'] = now
|
||||
# reset kanban state when changing stage
|
||||
if 'kanban_state' not in vals:
|
||||
vals['kanban_state'] = 'normal'
|
||||
# user_id change: update date_assign
|
||||
if vals.get('user_id'):
|
||||
if vals.get('user_id') and 'date_assign' not in vals:
|
||||
vals['date_assign'] = now
|
||||
|
||||
result = super(Task, self).write(vals)
|
||||
|
||||
return result
|
||||
|
||||
def update_date_end(self, stage_id):
|
||||
project_task_type = self.env['project.task.type'].browse(stage_id)
|
||||
if project_task_type.fold:
|
||||
return {'date_end': fields.Datetime.now()}
|
||||
return {'date_end': False}
|
||||
|
||||
# ---------------------------------------------------
|
||||
# Mail gateway
|
||||
# ---------------------------------------------------
|
||||
@@ -646,17 +667,28 @@ class Task(models.Model):
|
||||
|
||||
@api.model
|
||||
def message_new(self, msg, custom_values=None):
|
||||
""" Override to updates the document according to the email. """
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
# remove default author when going through the mail gateway. Indeed we
|
||||
# do not want to explicitly set user_id to False; however we do not
|
||||
# want the gateway user to be responsible if no other responsible is
|
||||
# found.
|
||||
create_context = dict(self.env.context or {})
|
||||
create_context['default_user_id'] = False
|
||||
if custom_values is None:
|
||||
custom_values = {}
|
||||
defaults = {
|
||||
'name': msg.get('subject'),
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
'planned_hours': 0.0,
|
||||
'partner_id': msg.get('author_id')
|
||||
}
|
||||
defaults.update(custom_values)
|
||||
|
||||
task = super(Task, self).message_new(msg, custom_values=defaults)
|
||||
task = super(Task, self.with_context(create_context)).message_new(msg, custom_values=defaults)
|
||||
email_list = task.email_split(msg)
|
||||
partner_ids = [p for p in task._find_partner_from_emails(email_list, force_create=False) if p]
|
||||
task.message_subscribe(partner_ids)
|
||||
@@ -692,7 +724,10 @@ class Task(models.Model):
|
||||
recipients = super(Task, self).message_get_suggested_recipients()
|
||||
for task in self.filtered('partner_id'):
|
||||
reason = _('Customer Email') if task.partner_id.email else _('Customer')
|
||||
task._message_add_suggested_recipient(recipients, partner=task.partner_id, reason=reason)
|
||||
if task.partner_id:
|
||||
task._message_add_suggested_recipient(recipients, partner=task.partner_id, reason=reason)
|
||||
elif task.email_from:
|
||||
task._message_add_suggested_recipient(recipients, partner=task.email_from, reason=reason)
|
||||
return recipients
|
||||
|
||||
@api.multi
|
||||
@@ -713,6 +748,22 @@ class Task(models.Model):
|
||||
res['headers'] = repr(headers)
|
||||
return res
|
||||
|
||||
def _message_post_after_hook(self, message):
|
||||
if self.email_from and not self.partner_id:
|
||||
# we consider that posting a message with a specified recipient (not a follower, a specific one)
|
||||
# on a document without customer means that it was created through the chatter using
|
||||
# suggested recipients. This heuristic allows to avoid ugly hacks in JS.
|
||||
new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email_from)
|
||||
if new_partner:
|
||||
self.search([
|
||||
('partner_id', '=', False),
|
||||
('email_from', '=', new_partner.email),
|
||||
('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id})
|
||||
return super(Task, self)._message_post_after_hook(message)
|
||||
|
||||
def action_assign_to_me(self):
|
||||
self.write({'user_id': self.env.user.id})
|
||||
|
||||
def action_open_parent_task(self):
|
||||
return {
|
||||
'name': _('Parent Task'),
|
||||
@@ -812,9 +863,9 @@ class AccountAnalyticAccount(models.Model):
|
||||
return result
|
||||
|
||||
class ProjectTags(models.Model):
|
||||
""" Tags of project's tasks (or issues) """
|
||||
""" Tags of project's tasks """
|
||||
_name = "project.tags"
|
||||
_description = "Tags of project's tasks, issues..."
|
||||
_description = "Tags of project's tasks"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
color = fields.Integer(string='Color Index', default=10)
|
||||
|
||||
@@ -19,5 +19,9 @@ class ProjectConfiguration(models.TransientModel):
|
||||
module_hr_timesheet_attendance = fields.Boolean("Attendances")
|
||||
module_sale_timesheet = fields.Boolean("Time Billing")
|
||||
module_hr_expense = fields.Boolean("Expenses")
|
||||
module_project_issue = fields.Boolean("Issue Tracking")
|
||||
group_subtask_project = fields.Boolean("Sub-tasks", implied_group="project.group_subtask_project")
|
||||
|
||||
@api.onchange('module_sale_timesheet')
|
||||
def _onchange_module_sale_timesheet(self):
|
||||
if self.module_sale_timesheet:
|
||||
self.module_hr_timesheet = True
|
||||
|
||||
@@ -13,23 +13,23 @@ class ReportProjectTaskUser(models.Model):
|
||||
name = fields.Char(string='Task Title', readonly=True)
|
||||
user_id = fields.Many2one('res.users', string='Assigned To', readonly=True)
|
||||
date_start = fields.Datetime(string='Assignation Date', readonly=True)
|
||||
no_of_days = fields.Integer(string='# Working Days', readonly=True)
|
||||
date_end = fields.Datetime(string='Ending Date', readonly=True)
|
||||
date_deadline = fields.Date(string='Deadline', readonly=True)
|
||||
date_last_stage_update = fields.Datetime(string='Last Stage Update', readonly=True)
|
||||
project_id = fields.Many2one('project.project', string='Project', readonly=True)
|
||||
closing_days = fields.Float(string='# Days to Close',
|
||||
working_days_close = fields.Float(string='# Working Days to Close',
|
||||
digits=(16,2), readonly=True, group_operator="avg",
|
||||
help="Number of Days to close the task")
|
||||
opening_days = fields.Float(string='# Days to Assign',
|
||||
help="Number of Working Days to close the task")
|
||||
working_days_open = fields.Float(string='# Working Days to Assign',
|
||||
digits=(16,2), readonly=True, group_operator="avg",
|
||||
help="Number of Days to Open the task")
|
||||
help="Number of Working Days to Open the task")
|
||||
delay_endings_days = fields.Float(string='# Days to Deadline', digits=(16,2), readonly=True)
|
||||
nbr = fields.Integer('# of Tasks', readonly=True) # TDE FIXME master: rename into nbr_tasks
|
||||
priority = fields.Selection([
|
||||
('0','Non Starred'),
|
||||
('1','Starred')
|
||||
], size=1, readonly=True, string="Starred")
|
||||
('0', 'Low'),
|
||||
('1', 'Normal'),
|
||||
('2', 'High')
|
||||
], size=1, readonly=True, string="Priority")
|
||||
state = fields.Selection([
|
||||
('normal', 'In Progress'),
|
||||
('blocked', 'Blocked'),
|
||||
@@ -48,7 +48,6 @@ class ReportProjectTaskUser(models.Model):
|
||||
t.date_end as date_end,
|
||||
t.date_last_stage_update as date_last_stage_update,
|
||||
t.date_deadline as date_deadline,
|
||||
abs((extract('epoch' from (t.write_date-t.date_start)))/(3600*24)) as no_of_days,
|
||||
t.user_id,
|
||||
t.project_id,
|
||||
t.priority,
|
||||
@@ -57,8 +56,8 @@ class ReportProjectTaskUser(models.Model):
|
||||
t.partner_id,
|
||||
t.stage_id as stage_id,
|
||||
t.kanban_state as state,
|
||||
(extract('epoch' from (t.write_date-t.create_date)))/(3600*24) as closing_days,
|
||||
(extract('epoch' from (t.date_start-t.create_date)))/(3600*24) as opening_days,
|
||||
t.working_days_close as working_days_close,
|
||||
t.working_days_open as working_days_open,
|
||||
(extract('epoch' from (t.date_deadline-(now() at time zone 'UTC'))))/(3600*24) as delay_endings_days
|
||||
"""
|
||||
return select_str
|
||||
|
||||
@@ -54,8 +54,6 @@ class TestProjectFlow(TestProjectBase):
|
||||
# Test: messages
|
||||
self.assertEqual(len(task.message_ids), 2,
|
||||
'project: message_process: newly created task should have 2 messages: creation and email')
|
||||
self.assertEqual(task.message_ids[1].subtype_id.name, 'Task Opened',
|
||||
'project: message_process: first message of new task should have Task Created subtype')
|
||||
self.assertEqual(task.message_ids[0].author_id, self.user_projectuser.partner_id,
|
||||
'project: message_process: second message should be the one from Agrolait (partner failed)')
|
||||
self.assertEqual(task.message_ids[0].subject, 'Frogs',
|
||||
|
||||
@@ -159,20 +159,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Issue Management</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" id="project_issue">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_project_issue"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="module_project_issue"/>
|
||||
<div class="text-muted">
|
||||
Track and solve issues from a Kanban view
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<filter string="My Followed Tasks" domain="[('message_is_follower', '=', True)]" />
|
||||
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Starred" name="starred" domain="[('priority','=',1)]"/>
|
||||
<filter string="Starred" name="starred" domain="[('priority','in',[1, 2])]"/>
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
@@ -127,8 +127,7 @@
|
||||
<div name="options_active" class="oe_edit_only">
|
||||
<div>
|
||||
<field name="use_tasks" class="oe_inline"/>
|
||||
<label for="use_tasks" class="oe_inline" string="Tasks"/>
|
||||
<span attrs="{'invisible':[('use_tasks', '=', False)]}">as </span>
|
||||
<label for="use_tasks" class="oe_inline" string="Name of the tasks :"/>
|
||||
<field name="label_tasks" class="oe_inline oe_input_align" attrs="{'invisible': [('use_tasks', '=', False)]}"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -161,8 +160,6 @@
|
||||
<field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="alias_model" string="Incoming Emails create" class="oe_edit_only"/>
|
||||
<field name="alias_model" class="oe_inline oe_edit_only" nolabel="1"/>
|
||||
<field name="alias_contact" class="oe_inline oe_edit_only"
|
||||
string="Accept Emails From"/>
|
||||
</group>
|
||||
@@ -370,6 +367,7 @@ action = model.activate_sample_project()
|
||||
<field name="arch" type="xml">
|
||||
<form string="Task" class="o_form_project_tasks">
|
||||
<header>
|
||||
<button name="action_assign_to_me" string="Assign to Me" type="object" class="oe_highlight"/>
|
||||
<field name="stage_id" widget="statusbar" clickable="True"
|
||||
options="{'fold_field': 'fold'}"/>
|
||||
</header>
|
||||
@@ -419,6 +417,8 @@ action = model.activate_sample_project()
|
||||
<group>
|
||||
<field name="sequence" groups="base.group_no_one"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="email_from"/>
|
||||
<field name="email_cc"/>
|
||||
<field name="parent_id" groups="project.group_subtask_project"/>
|
||||
<field name="child_ids" invisible="1" />
|
||||
<field name="subtask_project_id" invisible="1" />
|
||||
@@ -429,6 +429,14 @@ action = model.activate_sample_project()
|
||||
<field name="date_assign" groups="base.group_no_one"/>
|
||||
<field name="date_last_stage_update" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group string="Working Time to Assign" attrs="{'invisible': [('working_hours_open', '=', 0.0)]}">
|
||||
<field name="working_hours_open" string="Hours"/>
|
||||
<field name="working_days_open" string="Days"/>
|
||||
</group>
|
||||
<group string="Working Time to Close" attrs="{'invisible': [('working_hours_close', '=', 0.0)]}">
|
||||
<field name="working_hours_close" string="Hours"/>
|
||||
<field name="working_days_close" string="Days"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
@@ -492,6 +500,7 @@ action = model.activate_sample_project()
|
||||
<field name="project_id" invisible="context.get('default_project_id', False)"/>
|
||||
<t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="red">oe_kanban_text_red</t>
|
||||
<span t-attf-class="#{red || ''}"><i><field name="date_deadline"/></i></span>
|
||||
<t t-if="record.email_from.raw_value"><span><field name="email_from"/></span></t>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_left">
|
||||
<field name="priority" widget="priority"/>
|
||||
@@ -626,7 +635,7 @@ action = model.activate_sample_project()
|
||||
</record>
|
||||
|
||||
<record id="project_task_action_activity" model="ir.actions.act_window">
|
||||
<field name="name">My Next Task Activities</field>
|
||||
<field name="name">My Next Activities</field>
|
||||
<field name="res_model">project.task</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,kanban,calendar</field>
|
||||
@@ -650,7 +659,7 @@ action = model.activate_sample_project()
|
||||
</record>
|
||||
|
||||
<menuitem id="project_task_menu_activity"
|
||||
name="Next Task Activities"
|
||||
name="Next Activities"
|
||||
parent="project.menu_project_management"
|
||||
action="project_task_action_activity"
|
||||
sequence="25"/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user