summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/project/models/project.py85
-rw-r--r--addons/project/views/project_views.xml7
-rw-r--r--addons/sale_timesheet/models/project.py33
3 files changed, 63 insertions, 62 deletions
diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index 9629d9c7d5a..53a5befe9f1 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -436,9 +436,13 @@ class Task(models.Model):
@api.model
def default_get(self, fields_list):
result = super(Task, self).default_get(fields_list)
- # force some parent values, if needed
- if 'parent_id' in result and result['parent_id']:
- result.update(self._subtask_values_from_parent(result['parent_id']))
+ # find default value from parent for the not given ones
+ parent_task_id = result.get('parent_id') or self._context.get('default_parent_id')
+ if parent_task_id:
+ parent_values = self._subtask_values_from_parent(parent_task_id)
+ for fname, value in parent_values.items():
+ if fname not in result:
+ result[fname] = value
return result
@api.model
@@ -538,11 +542,6 @@ class Task(models.Model):
# customer portal: include comment and incoming emails in communication history
website_message_ids = fields.One2many(domain=lambda self: [('model', '=', self._name), ('message_type', 'in', ['email', 'comment'])])
- @api.constrains('parent_id')
- def _check_parent_id(self):
- if not self._check_recursion():
- raise ValidationError(_('You cannot create recursive tasks.'))
-
def _compute_attachment_ids(self):
for task in self:
attachment_ids = self.env['ir.attachment'].search([('res_id', '=', task.id), ('res_model', '=', 'project.task')]).ids
@@ -619,14 +618,17 @@ class Task(models.Model):
@api.onchange('parent_id')
def _onchange_parent_id(self):
if self.parent_id:
- for field_name in self._subtask_implied_fields():
- self[field_name] = self.parent_id[field_name]
+ for field_name, value in self._subtask_values_from_parent(self.parent_id.id).items():
+ if not self[field_name]:
+ self[field_name] = value
@api.onchange('project_id')
def _onchange_project(self):
if self.project_id:
- if not self.parent_id and self.project_id.partner_id:
+ # find partner
+ if self.project_id.partner_id:
self.partner_id = self.project_id.partner_id
+ # find stage
if self.project_id not in self.stage_id.project_ids:
self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
# keep multi company consistency
@@ -709,10 +711,6 @@ class Task(models.Model):
def create(self, vals):
# context: no_log, because subtype already handle this
context = dict(self.env.context)
- # force some parent values, if needed
- if 'parent_id' in vals and vals['parent_id']:
- vals.update(self._subtask_values_from_parent(vals['parent_id']))
- context.pop('default_parent_id', None)
# for default stage
if vals.get('project_id') and not context.get('default_project_id'):
context['default_project_id'] = vals.get('project_id')
@@ -723,14 +721,16 @@ class Task(models.Model):
if vals.get('stage_id'):
vals.update(self.update_date_end(vals['stage_id']))
vals['date_last_stage_update'] = fields.Datetime.now()
+ # substask default values
+ if vals.get('parent_id'):
+ for fname, value in self._subtask_values_from_parent(vals['parent_id']).items():
+ if fname not in vals:
+ vals[fname] = value
task = super(Task, self.with_context(context)).create(vals)
return task
def write(self, vals):
now = fields.Datetime.now()
- # subtask: force some parent values, if needed
- if 'parent_id' in vals and vals['parent_id']:
- vals.update(self._subtask_values_from_parent(vals['parent_id']))
# stage change: update date_last_stage_update
if 'stage_id' in vals:
vals.update(self.update_date_end(vals['stage_id']))
@@ -746,12 +746,6 @@ class Task(models.Model):
# 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)
- # subtask: update subtask according to parent values
- subtask_values_to_write = self._subtask_write_values(vals)
- if subtask_values_to_write:
- subtasks = self.filtered(lambda task: not task.parent_id).mapped('child_ids')
- if subtasks:
- subtasks.write(subtask_values_to_write)
return result
def update_date_end(self, stage_id):
@@ -764,27 +758,18 @@ class Task(models.Model):
# Subtasks
# ---------------------------------------------------
- @api.model
- def _subtask_implied_fields(self):
- """ Return the list of field name to apply on subtask when changing parent_id or when updating parent task. """
+ def _subtask_default_fields(self):
+ """ Return the list of field name for default value when creating a subtask """
return ['partner_id', 'email_from']
- def _subtask_write_values(self, values):
- """ Return the values to write on subtask when `values` is written on parent tasks
- :param values: dict of values to write on parent
- """
- result = {}
- for field_name in self._subtask_implied_fields():
- if field_name in values:
- result[field_name] = values[field_name]
- return result
-
def _subtask_values_from_parent(self, parent_id):
""" Get values for substask implied field of the given"""
result = {}
parent_task = self.env['project.task'].browse(parent_id)
- for field_name in self._subtask_implied_fields():
+ for field_name in self._subtask_default_fields():
result[field_name] = parent_task[field_name]
+ # special case for the subtask default project
+ result['project_id'] = parent_task.project_id.subtask_project_id
return self._convert_to_write(result)
# ---------------------------------------------------
@@ -942,16 +927,28 @@ class Task(models.Model):
def action_subtask(self):
action = self.env.ref('project.project_task_action_sub_task').read()[0]
- ctx = self.env.context.copy()
+
+ # only display subtasks of current task
+ action['domain'] = [('id', 'child_of', self.id), ('id', '!=', self.id)]
+
+ # update context, with all default values as 'quick_create' does not contains all field in its view
+ if self._context.get('default_project_id'):
+ default_project = self.env['project.project'].browse(self.env.context['default_project_id'])
+ else:
+ default_project = self.project_id.subtask_project_id or self.project_id
+ ctx = dict(self.env.context)
ctx.update({
- 'default_parent_id': self.id,
- 'default_project_id': self.env.context.get('project_id', self.project_id.id),
'default_name': self.env.context.get('name', self.name) + ':',
- 'default_partner_id': self.env.context.get('partner_id', self.partner_id.id),
- 'search_default_project_id': self.env.context.get('project_id', self.project_id.id),
+ 'default_parent_id': self.id, # will give default subtask field in `default_get`
+ 'default_company_id': default_project.company_id.id if default_project else self.env.company.id,
+ 'search_default_parent_id': self.id,
})
+ parent_values = self._subtask_values_from_parent(self.id)
+ for fname, value in parent_values.items():
+ if 'default_' + fname not in ctx:
+ ctx['default_' + fname] = value
action['context'] = ctx
- action['domain'] = [('id', 'child_of', self.id), ('id', '!=', self.id)]
+
return action
# ---------------------------------------------------
diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index 7940111441a..f8abdfd8e80 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -22,6 +22,7 @@
<field name="partner_id" operator="child_of"/>
<field name="stage_id"/>
<field name="project_id"/>
+ <field name="parent_id" groups="project.group_subtask_project"/>
<filter string="My Tasks" name="my_tasks" domain="[('user_id', '=', uid)]"/>
<filter string="Followed Tasks" name="my_followed_tasks" domain="[('message_is_follower', '=', True)]" />
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
@@ -449,8 +450,7 @@
<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="action_subtask" type="object" 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">
+ attrs="{'invisible' : ['|', ('parent_id', '!=', False), ('id', '=', False)]}" context="{'default_user_id': user_id, 'default_parent_id': id, 'default_project_id': subtask_project_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">
@@ -490,7 +490,7 @@
<group>
<group>
<field name="sequence" groups="base.group_no_one"/>
- <field name="partner_id" attrs="{'readonly': [('parent_id', '!=', False)]}"/>
+ <field name="partner_id"/>
<field name="email_from" invisible="1"/>
<field name="email_cc" groups="base.group_no_one"/>
<field name="parent_id" attrs="{'invisible' : [('subtask_count', '>', 0)]}" groups="project.group_subtask_project"/>
@@ -542,6 +542,7 @@
<group>
<field name="name" string = "Task Title"/>
<field name="user_id" options="{'no_open': True,'no_create': True}"/>
+ <field name="parent_id" invisible="1"/>
</group>
</form>
</field>
diff --git a/addons/sale_timesheet/models/project.py b/addons/sale_timesheet/models/project.py
index d611d47fbeb..d46303097f7 100644
--- a/addons/sale_timesheet/models/project.py
+++ b/addons/sale_timesheet/models/project.py
@@ -183,10 +183,15 @@ class ProjectTask(models.Model):
@api.onchange('project_id')
def _onchange_project(self):
result = super(ProjectTask, self)._onchange_project()
- if self.project_id.sale_line_id:
- self.sale_line_id = self.project_id.sale_line_id
- if not self.parent_id and not self.partner_id:
- self.partner_id = self.sale_line_id.order_partner_id
+ if self.project_id:
+ if self.project_id.billable_type == 'employee_rate':
+ if not self.partner_id:
+ self.partner_id = self.project_id.sale_order_id.partner_id
+ elif self.project_id.billable_type == 'task_rate':
+ if not self.sale_line_id:
+ self.sale_line_id = self.project_id.sale_line_id
+ if not self.partner_id:
+ self.partner_id = self.sale_line_id.order_partner_id
# set domain on SO: on non billable project, all SOL of customer, otherwise the one from the SO
result = result or {}
domain = [('is_service', '=', True), ('is_expense', '=', False), ('order_partner_id', 'child_of', self.partner_id.commercial_partner_id.id), ('state', 'in', ['sale', 'done'])]
@@ -205,6 +210,13 @@ class ProjectTask(models.Model):
result.setdefault('domain', {})['sale_line_id'] = [('is_service', '=', True), ('is_expense', '=', False), ('order_partner_id', 'child_of', self.partner_id.commercial_partner_id.id), ('state', 'in', ['sale', 'done'])]
return result
+ @api.onchange('parent_id')
+ def _onchange_parent_id(self):
+ super(ProjectTask, self)._onchange_parent_id()
+ # check sale_line_id and customer are coherent
+ if self.sale_line_id and self.partner_id != self.sale_line_id.order_partner_id:
+ self.sale_line_id = False
+
@api.constrains('sale_line_id')
def _check_sale_line_type(self):
for task in self.sudo():
@@ -229,19 +241,10 @@ class ProjectTask(models.Model):
# ---------------------------------------------------
@api.model
- def _subtask_implied_fields(self):
- result = super(ProjectTask, self)._subtask_implied_fields()
+ def _subtask_default_fields(self):
+ result = super(ProjectTask, self)._subtask_default_fields()
return result + ['sale_line_id']
- def _subtask_write_values(self, values):
- result = super(ProjectTask, self)._subtask_write_values(values)
- # changing the partner on a task will reset the sale line of its subtasks
- if 'partner_id' in result:
- result['sale_line_id'] = False
- elif 'sale_line_id' in result:
- result.pop('sale_line_id')
- return result
-
# ---------------------------------------------------
# Actions
# ---------------------------------------------------