From e6ac7a7d2571ce046a1d9befd64d4216054494c7 Mon Sep 17 00:00:00 2001 From: Thibault Francois Date: Mon, 22 Feb 2016 10:13:19 +0100 Subject: [PATCH] [ADD] project_timesheet, sale_service: support for project sub-tasks This commit introduces "Sub-task", a concept remotely similar to the "delegated tasks" available in version 7.0, but covering a more specific and narrow scope. Activating Sub-tasks -------------------- Sub-tasks are made available within any project by configuring a "sub-task project" in the project settings. This feature is considered low-level and technical, and only accessible with "Technical Features" enabled. The "sub-task project" may be the project itself. Using Sub-tasks --------------- Conceptually, a sub-task represents the allocation of a part of the scope of its parent task. Multiple sub-task levels are possible. Once activated in project settings, a new "Sub-tasks" smart button will appear on the task form, giving the option to view and create sub-tasks. In addition, a new field "Sub-tasks Hours" will appear next to the other computed "Hours" fields, representing the total hours allocated to sub-tasks. (Technically, the sum of the maximum between the planned hours and spent hours of all sub-tasks, with spent hours always used when a task is closed) These sub-task hours will be dynamically subtracted from the remaining hours of the task, and will therefore affect the task's progress as well. Upon creation, a sub-task will inherit the link to the original sales order and procurement from its parent task, and will by default be assigned to the "sub-task project" set on the project of the parent task. Enhancement ----------- The kanban view for tasks will now display a color-coded label with the number of remaining hours, whenever the percentage of remaining hours is below 20% (orange=20%-, red=1%-). Notes ----------- This is a forward port of 43c42b304ddac02665420f115afaa57007cea642 Since hr_timesheet has been migrated to the new API, the forward port changes mostly consists in migrating the sub-tasks system to the new API as well. --- addons/hr_timesheet/project_timesheet.py | 35 +++++++++++- .../hr_timesheet/project_timesheet_view.xml | 55 ++++++++++++++++++- addons/project/project_view.xml | 6 +- addons/sale_timesheet/models/sale_service.py | 11 ++++ .../views/sale_service_view.xml | 4 ++ .../static/src/less/kanban_view.less | 4 ++ 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/addons/hr_timesheet/project_timesheet.py b/addons/hr_timesheet/project_timesheet.py index 15c898baf0b..f96d9efe6f0 100644 --- a/addons/hr_timesheet/project_timesheet.py +++ b/addons/hr_timesheet/project_timesheet.py @@ -2,6 +2,14 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from openerp import models, fields, api +from openerp.osv import osv + + +class Project(models.Model): + _inherit = "project.project" + + subtask_project_id = fields.Many2one('project.project', string='Sub-task Project', help="Choosing a sub-tasks project will both enable sub-tasks and set their default project (possibly the project itself)", + ondelete="restrict") class Task(models.Model): @@ -10,18 +18,31 @@ class Task(models.Model): def _get_total_hours(self): return super(Task, self)._get_total_hours() + self.effective_hours - @api.depends('timesheet_ids', 'timesheet_ids.unit_amount', 'planned_hours') + @api.multi + def _get_subtask_count(self): + for task in self: + task.subtask_count = self.search_count([('id', 'child_of', task.id)]) - 1 # Don't count task itself + + @api.depends('timesheet_ids.unit_amount', 'planned_hours', 'child_ids.stage_id', + 'child_ids.planned_hours', 'child_ids.effective_hours', 'child_ids.children_hours') def _hours_get(self): for task in self: + + for child_task in task.child_ids: + if child_task.stage_id and child_task.stage_id.fold: + task.children_hours += child_task.effective_hours + child_task.children_hours + else: + task.children_hours += max(child_task.planned_hours, child_task.effective_hours + child_task.children_hours) + task.effective_hours = sum(task.timesheet_ids.mapped('unit_amount')) - task.remaining_hours = task.planned_hours - task.effective_hours + task.remaining_hours = task.planned_hours - task.effective_hours - task.children_hours task.total_hours = max(task.planned_hours, task.effective_hours) task.delay_hours = max(-task.remaining_hours, 0.0) if task.stage_id and task.stage_id.fold: task.progress = 100.0 elif (task.planned_hours > 0.0 and task.effective_hours): - task.progress = round(min(100.0 * task.effective_hours / task.planned_hours, 99.99), 2) + task.progress = round(min(100.0 * (task.effective_hours + task.children_hours) / task.planned_hours, 99.99), 2) else: task.progress = 0.0 @@ -30,4 +51,12 @@ class Task(models.Model): total_hours = fields.Float(compute='_hours_get', store=True, string='Total', help="Computed as: Time Spent + Remaining Time.") progress = fields.Float(compute='_hours_get', store=True, string='Progress (%)', group_operator="avg", help="If the task has a progress of 99.99% you should close the task if it's finished or reevaluate the time", default=0.0) delay_hours = fields.Float(compute='_hours_get', store=True, string='Delay Hours', help="Computed as difference between planned hours by the project manager and the total hours of the task.") + 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') + subtask_count = fields.Integer(compute='_get_subtask_count', type='integer', string="Sub-task count") + + _constraints = [(osv.osv._check_recursion, 'Circular references are not permitted between tasks and sub-tasks', ['parent_id'])] diff --git a/addons/hr_timesheet/project_timesheet_view.xml b/addons/hr_timesheet/project_timesheet_view.xml index 2ebd93257b6..9932c8e87ed 100644 --- a/addons/hr_timesheet/project_timesheet_view.xml +++ b/addons/hr_timesheet/project_timesheet_view.xml @@ -28,21 +28,52 @@
+ + + + + Sub-tasks + project.task + kanban,tree,form,calendar,pivot,graph + [('id', 'child_of', active_id), ('id', '!=', active_id)] + + { + 'default_parent_id' : active_id, + 'default_project_id' : project_id, + 'default_name' : name + ':', + 'default_partner_id' : partner_id, + } + + + + project.task.form.inherited project.task + + + + + + + @@ -57,7 +88,8 @@ - + + @@ -96,5 +128,26 @@ + + + project.task.timesheet.kanban.inherited.progress + project.task + + + + + + +
+ +
+ +
+
+
+
+
+ diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 37893d6acc3..9efe89f3d84 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -281,14 +281,14 @@
- - + + - + diff --git a/addons/sale_timesheet/models/sale_service.py b/addons/sale_timesheet/models/sale_service.py index 6d1c189334a..146f2d837ba 100644 --- a/addons/sale_timesheet/models/sale_service.py +++ b/addons/sale_timesheet/models/sale_service.py @@ -111,6 +111,17 @@ class project_task(osv.osv): "context": {"create": False, "show_sale": True}, } + def onchange_parent_id(self, cr, uid, ids, parent_id, context=None): + if not parent_id: + return {'value' : {'procurement_id': False, 'sale_line_id': False }} + parent_task = self.browse(cr, uid, parent_id, context=context) + return { + 'value' : { + 'procurement_id' : parent_task.procurement_id.id, + 'sale_line_id' : parent_task.sale_line_id.id, + } + } + class ProductProduct(models.Model): _inherit = 'product.product' diff --git a/addons/sale_timesheet/views/sale_service_view.xml b/addons/sale_timesheet/views/sale_service_view.xml index 70f62aed1df..c9b2fb4b8dc 100644 --- a/addons/sale_timesheet/views/sale_service_view.xml +++ b/addons/sale_timesheet/views/sale_service_view.xml @@ -24,8 +24,12 @@ string="Sales Order"/> + + + onchange_parent_id(parent_id) + diff --git a/addons/web_kanban/static/src/less/kanban_view.less b/addons/web_kanban/static/src/less/kanban_view.less index fbbcc92365f..ddd7f6374d1 100644 --- a/addons/web_kanban/static/src/less/kanban_view.less +++ b/addons/web_kanban/static/src/less/kanban_view.less @@ -96,6 +96,10 @@ .oe_kanban_bottom_left { float: left; + .oe_kanban_align { + display: inline-block; + vertical-align: middle; + } } .o_kanban_tags {