[IMP] project, *_timesheet: move subtasks to project

-> Move Subtasks mechanism from hr_timesheet to project.

-> Various functional improvements:
 - Add new 'Sub-tasks' setting and 'Sub-task Project' field based on
     this setting.
 - Relabel 'Team Collaboration' section to 'Task Management'.
 - Remove 'Customer Satisfaction' section and move setting
     'Rating on Tasks' into 'Task Management' section.
 - Add customer name on the project's kanban view.
 - Set default value of 'Sub-task Project' field is the same project.
 - Add 'Parent Task' stat button on task form view.
 - Make 'Sub-Tasks' stat button visible when no parent task
This commit is contained in:
Hiral Bhavsar
2017-05-24 11:39:54 +02:00
committed by Denis Vermylen
parent 7d99811a0f
commit 332b83ef9e
8 changed files with 96 additions and 72 deletions
-19
View File
@@ -8,19 +8,11 @@ from odoo.exceptions import UserError
class Project(models.Model):
_inherit = "project.project"
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)")
allow_timesheets = fields.Boolean("Allow timesheets", default=True)
class Task(models.Model):
_inherit = "project.task"
@api.multi
def _get_subtask_count(self):
for task in self:
task.subtask_count = self.search_count([('id', 'child_of', task.id), ('id', '!=', task.id)])
@api.depends('stage_id', 'timesheet_ids.unit_amount', 'planned_hours', 'child_ids.stage_id',
'child_ids.planned_hours', 'child_ids.effective_hours', 'child_ids.children_hours', 'child_ids.timesheet_ids.unit_amount')
def _hours_get(self):
@@ -55,15 +47,4 @@ class Task(models.Model):
children_hours = fields.Float(compute='_hours_get', store=True, string='Sub-tasks Hours', help="Sum of the planned hours of all sub-tasks (when a sub-task is closed or its spent hours exceed its planned hours, spent hours are counted instead)")
timesheet_ids = fields.One2many('account.analytic.line', 'task_id', 'Timesheets')
parent_id = fields.Many2one('project.task', string='Parent Task')
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='_get_subtask_count', type='integer', string="Sub-task count")
_constraints = [(models.BaseModel._check_recursion, 'Circular references are not permitted between tasks and sub-tasks', ['parent_id'])]
@api.constrains('parent_id')
def _check_subtask_project(self):
for task in self:
if task.parent_id and task.parent_id.project_id and task.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"))
@@ -45,9 +45,6 @@
<button name="toggle_active" position="before">
<button class="oe_stat_button" name="%(act_hr_timesheet_line_by_project)d" type="action" icon="fa-calendar" string="Timesheets" attrs="{'invisible': [('allow_timesheets', '=', False)]}"/>
</button>
<field name="user_id" position="after">
<field name="subtask_project_id" groups="base.group_no_one" attrs="{'invisible': [('allow_timesheets', '=', False)]}"/>
</field>
<xpath expr="//div[@name='options_active']" position="inside">
<div>
<field name="allow_timesheets" class="oe_inline" string="Allow timesheets"/>
@@ -57,46 +54,18 @@
</field>
</record>
<record id="act_project_task_sub_task" model="ir.actions.act_window">
<field name="name">Sub-tasks</field>
<field name="res_model">project.task</field>
<field name="view_mode">kanban,tree,form,calendar,pivot,graph</field>
<field name="domain">[('id', 'child_of', active_id), ('id', '!=', active_id)]</field>
<field name="context">
{
'default_parent_id' : active_id,
'default_project_id' : project_id,
'default_name' : name + ':',
'default_partner_id' : partner_id,
}
</field>
<field name="search_view_id" ref="project.view_task_search_form"/>
</record>
<record model="ir.ui.view" id="view_task_form2_inherited">
<field name="name">project.task.form.inherited</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2" />
<field name="groups_id" eval="[(4, ref('base.group_user'))]"/>
<field name="arch" type="xml">
<button name="toggle_active" position="before">
<button name="%(act_project_task_sub_task)d" type="action" class="oe_stat_button" icon="fa-tasks"
attrs="{'invisible' : [('subtask_project_id', '=', False)]}"
context="{'project_id': subtask_project_id, 'name': name, 'partner_id': partner_id}" >
<field string="Sub-tasks" name="subtask_count" widget="statinfo"/>
</button>
</button>
<field name="tag_ids" position="after">
<field name="progress" widget="progressbar"/>
</field>
<field name="user_id" position="after">
<field name="planned_hours" widget="float_time"/>
</field>
<field name="partner_id" position="after">
<field name="parent_id" groups="base.group_no_one"/>
<field name="child_ids" invisible="1" />
<field name="subtask_project_id" invisible="1" />
</field>
<xpath expr="//notebook/page[@name='description_page']" position="after">
<page string="Timesheets" attrs="{'invisible': [('project_id', '=', False)]}">
<field name="timesheet_ids" context="{'default_project_id': project_id}">
+29
View File
@@ -219,6 +219,8 @@ class Project(models.Model):
doc_count = fields.Integer(compute='_compute_attached_docs_count', string="Number of documents attached")
date_start = fields.Date(string='Start Date')
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)")
_sql_constraints = [
('project_date_greater', 'check(date >= date_start)', 'Error! project start-date must be lower than project end-date.')
@@ -256,6 +258,8 @@ class Project(models.Model):
# Prevent double project creation when 'use_tasks' is checked
self = self.with_context(project_creation_in_progress=True, mail_create_nosubscribe=True)
project = super(Project, self).create(vals)
if not vals.get('subtask_project_id'):
project.subtask_project_id = project.id
if project.privacy_visibility == 'portal' and project.partner_id:
project.message_subscribe(project.partner_id.ids)
return project
@@ -411,6 +415,10 @@ class Task(models.Model):
legend_blocked = fields.Char(related='stage_id.legend_blocked', string='Kanban Blocked Explanation', readonly=True)
legend_done = fields.Char(related='stage_id.legend_done', string='Kanban Valid Explanation', readonly=True)
legend_normal = fields.Char(related='stage_id.legend_normal', string='Kanban Ongoing Explanation', readonly=True)
parent_id = fields.Many2one('project.task', string='Parent Task')
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")
@api.onchange('project_id')
def _onchange_project(self):
@@ -438,6 +446,17 @@ class Task(models.Model):
default['remaining_hours'] = self.planned_hours
return super(Task, self).copy(default)
@api.multi
def _compute_subtask_count(self):
for task in self:
task.subtask_count = self.search_count([('id', 'child_of', task.id), ('id', '!=', task.id)])
@api.constrains('parent_id')
def _check_subtask_project(self):
for task in self:
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)):
@@ -685,6 +704,16 @@ class Task(models.Model):
res['headers'] = repr(headers)
return res
def action_open_parent_task(self):
return {
'name': _('Parent Task'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'project.task',
'res_id': self.parent_id.id,
'type': 'ir.actions.act_window'
}
class AccountAnalyticAccount(models.Model):
_inherit = 'account.analytic.account'
+1
View File
@@ -25,6 +25,7 @@ class ProjectConfiguration(models.TransientModel):
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.multi
def set_default_generate_project_alias(self):
@@ -19,6 +19,11 @@
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="group_subtask_project" model="res.groups">
<field name="name">Use Subtask 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'))]"/>
+31
View File
@@ -80,6 +80,23 @@
</field>
</record>
<record id="project_task_action_sub_task" model="ir.actions.act_window">
<field name="name">Sub-tasks</field>
<field name="res_model">project.task</field>
<field name="view_mode">kanban,tree,form,calendar,pivot,graph</field>
<field name="domain">[('id', 'child_of', active_id), ('id', '!=', active_id)]</field>
<field name="context">
{
'default_parent_id' : active_id,
'default_project_id' : project_id,
'default_name' : name + ':',
'default_partner_id' : partner_id,
'search_default_project_id': [project_id],
}
</field>
<field name="search_view_id" ref="project.view_task_search_form"/>
</record>
<!-- Project -->
<record id="edit_project" model="ir.ui.view">
<field name="name">project.project.form</field>
@@ -121,6 +138,7 @@
<group>
<field name="user_id" string="Project Manager"
attrs="{'readonly':[('active','=',False)]}"/>
<field name="subtask_project_id" groups="project.group_subtask_project"/>
<field name="privacy_visibility" widget="radio"/>
<field name="partner_id" string="Customer"/>
</group>
@@ -239,6 +257,7 @@
<field name="arch" type="xml">
<kanban class="oe_background_grey o_kanban_dashboard o_project_kanban" on_create="project.open_create_project">
<field name="name"/>
<field name="partner_id"/>
<field name="use_tasks"/>
<field name="color"/>
<field name="task_count"/>
@@ -256,6 +275,9 @@
<div class="o_kanban_primary_left">
<div class="o_primary">
<span><t t-esc="record.name.value"/></span>
<span t-if="record.partner_id.value">
<strong><t t-esc="record.partner_id.value"/></strong>
</span>
</div>
<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>
@@ -353,6 +375,12 @@ action = model.activate_sample_project()
</header>
<sheet string="Task">
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" icon="fa-tasks" type="object" name="action_open_parent_task" string="Parent Task" attrs="{'invisible' : [('parent_id', '=', False)]}" groups="project.group_subtask_project"/>
<button name="%(project_task_action_sub_task)d" type="action" class="oe_stat_button" icon="fa-tasks"
attrs="{'invisible' : [('parent_id', '!=', False)]}"
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="toggle_active" type="object" groups="base.group_user"
class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button"
@@ -391,6 +419,9 @@ action = model.activate_sample_project()
<group>
<field name="sequence" groups="base.group_no_one"/>
<field name="partner_id"/>
<field name="parent_id" groups="project.group_subtask_project"/>
<field name="child_ids" invisible="1" />
<field name="subtask_project_id" invisible="1" />
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field name="displayed_image_id" groups="base.group_no_one"/>
</group>
+29 -21
View File
@@ -11,7 +11,7 @@
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
</header>
<sheet>
<h2>Team Collaboration</h2>
<h2>Task Management</h2>
<div class="row mt16 o_settings_container" title="This replaces task's description text field by a collaborative rich text pad. This is advised if tasks involve several people.">
<div id="use_collaborative_pad" class="col-xs-12 col-md-6 o_setting_box" title="Lets the company customize which Pad installation should be used to link to new pads (for example: http://ietherpad.com/).">
<div class="o_setting_left_pane">
@@ -20,7 +20,34 @@
<div class="o_setting_right_pane" name="pad_project_right_pane">
<label for="module_pad"/>
<div class="text-muted">
Use collaborative rich text pads in tasks
Use collaborative rich text pads on tasks
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_subtask_project"/>
</div>
<div class="o_setting_right_pane">
<label for="group_subtask_project"/>
<div class="text-muted">
Split your tasks to organize your work into sub-milestones
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_rating_project"/>
</div>
<div class="o_setting_right_pane">
<label for="module_rating_project"/>
<div class="text-muted">
Track customer satisfaction on tasks
</div>
<div class="content-group" id="msg_module_rating_project">
<div class="text-warning mt16" attrs="{'invisible': [('module_rating_project','=',False)]}">
Save this page and come back here to set up the feature.
</div>
</div>
</div>
</div>
@@ -132,25 +159,6 @@
</div>
</div>
</div>
<h2>Customer Satisfaction</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_rating_project"/>
</div>
<div class="o_setting_right_pane">
<label for="module_rating_project"/>
<div class="text-muted">
Track customer satisfaction on tasks
</div>
<div class="content-group" id="msg_module_rating_project">
<div class="text-warning mt16" attrs="{'invisible': [('module_rating_project','=',False)]}">
Save this page and come back here to set up the feature.
</div>
</div>
</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">
+1 -1
View File
@@ -8,7 +8,7 @@ class ProductTemplate(models.Model):
_inherit = 'product.template'
track_service = fields.Selection(selection_add=[
('timesheet', 'Timesheets on project'),
('timesheet', 'Timesheets on project (one fare per SO/Project)'),
('task', 'Create a task per order line to track hours')])
project_id = fields.Many2one(
'project.project', 'Project', company_dependent=True,