diff options
| author | jem-odoo <jem@openerp.com> | 2019-09-12 14:37:49 +0000 |
|---|---|---|
| committer | jem-odoo <jem@openerp.com> | 2019-09-27 11:43:13 +0000 |
| commit | 749810a2e110f37bbfb04a12bc08ccb0122dd6df (patch) | |
| tree | 0fdb48a62792a55979aa1246968e432e8f7468d6 /addons | |
| parent | 5c8f83ef6ba388028b91f6b8c0be9b6495a9e59f (diff) | |
[IMP] project: substask can live their own life
This commit completes what was initiated a few time ago : a substask
is like a normal task and can now live its own life, being in another
project, attached to another customer, billed on another sale line, ...
than its mother.
The informations coming from the parent are only use as default values to
prefill the form view (with default options on creation, or though `onchange`
when setting the parent on existing task).
No values are forced now.
Form, default context, and others views has been adapted to make this feature
works.
Task-1982072
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/project/models/project.py | 85 | ||||
| -rw-r--r-- | addons/project/views/project_views.xml | 7 | ||||
| -rw-r--r-- | addons/sale_timesheet/models/project.py | 33 |
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 # --------------------------------------------------- |
