enhancements of PMT
This commit is contained in:
parent
582225e11e
commit
05bdddc472
|
|
@ -1025,6 +1025,23 @@ class projectTask(models.Model):
|
||||||
if stage and stage.mail_template_id:
|
if stage and stage.mail_template_id:
|
||||||
stage.mail_template_id.sudo().send_mail(self.id, force_send=True)
|
stage.mail_template_id.sudo().send_mail(self.id, force_send=True)
|
||||||
|
|
||||||
|
def _notify_reassigned_users(self, users, note):
|
||||||
|
self.ensure_one()
|
||||||
|
partners = users.mapped('partner_id')
|
||||||
|
if not partners:
|
||||||
|
return
|
||||||
|
task_name = self.display_name or self.name
|
||||||
|
body = Markup("<p>%s</p><p>%s</p>") % (
|
||||||
|
_("You have been re-assigned to task %s.") % task_name,
|
||||||
|
note,
|
||||||
|
)
|
||||||
|
self.with_context(mail_notify_force_send=True).message_notify(
|
||||||
|
partner_ids=partners.ids,
|
||||||
|
subject=_("Task Re-assigned: %s") % task_name,
|
||||||
|
body=body,
|
||||||
|
email_layout_xmlid="mail.mail_notification_light",
|
||||||
|
)
|
||||||
|
|
||||||
def _apply_task_stage_users(self, stage, users, reason=False, reassignment=False):
|
def _apply_task_stage_users(self, stage, users, reason=False, reassignment=False):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if not stage:
|
if not stage:
|
||||||
|
|
@ -1051,16 +1068,19 @@ class projectTask(models.Model):
|
||||||
self._post_to_project_channel(note, users.mapped('partner_id'))
|
self._post_to_project_channel(note, users.mapped('partner_id'))
|
||||||
self._send_stage_mail_template(stage)
|
self._send_stage_mail_template(stage)
|
||||||
if reassignment:
|
if reassignment:
|
||||||
|
self._notify_reassigned_users(users, note)
|
||||||
self._append_reassignment_history(stage, users, reason)
|
self._append_reassignment_history(stage, users, reason)
|
||||||
|
|
||||||
def _apply_task_stage_user_lines(self, lines, reason=False, reassignment=False, update_assignees=True):
|
def _apply_task_stage_user_lines(self, lines, reason=False, reassignment=False, update_assignees=True):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self._ensure_task_timeline_rows()
|
self._ensure_task_timeline_rows()
|
||||||
selected_users = self.env['res.users']
|
selected_users = self.env['res.users']
|
||||||
|
replaced_users = self.env['res.users']
|
||||||
notes = []
|
notes = []
|
||||||
for wizard_line in lines:
|
for wizard_line in lines:
|
||||||
stage = wizard_line.stage_id
|
stage = wizard_line.stage_id
|
||||||
users = wizard_line.user_ids
|
user = getattr(wizard_line, 'user_id', False)
|
||||||
|
users = user or getattr(wizard_line, 'user_ids', self.env['res.users'])
|
||||||
responsible_user = getattr(wizard_line, 'responsible_user_id', False)
|
responsible_user = getattr(wizard_line, 'responsible_user_id', False)
|
||||||
if not stage or not users:
|
if not stage or not users:
|
||||||
continue
|
continue
|
||||||
|
|
@ -1068,19 +1088,30 @@ class projectTask(models.Model):
|
||||||
timeline = self.assignees_timelines.filtered(lambda t: t.stage_id == stage)[:1]
|
timeline = self.assignees_timelines.filtered(lambda t: t.stage_id == stage)[:1]
|
||||||
primary_user = users[0]
|
primary_user = users[0]
|
||||||
if timeline:
|
if timeline:
|
||||||
|
if reassignment and timeline.assigned_to:
|
||||||
|
replaced_users |= timeline.assigned_to
|
||||||
timeline.sudo().write({
|
timeline.sudo().write({
|
||||||
'assigned_to': primary_user.id,
|
'assigned_to': primary_user.id,
|
||||||
'responsible_lead': responsible_user.id if responsible_user else timeline.responsible_lead.id or primary_user.id,
|
'responsible_lead': responsible_user.id if responsible_user else timeline.responsible_lead.id or primary_user.id,
|
||||||
'team_id': stage.team_id.id if stage.team_id else timeline.team_id.id,
|
'team_id': stage.team_id.id if stage.team_id else timeline.team_id.id,
|
||||||
})
|
})
|
||||||
notes.append(_("%s: %s") % (stage.name, ", ".join(users.mapped('name'))))
|
line_reason = getattr(wizard_line, 'reason', False) or reason
|
||||||
|
note = _("%s: %s") % (stage.name, ", ".join(users.mapped('name')))
|
||||||
|
if line_reason:
|
||||||
|
note += _(". Reason: %s") % line_reason
|
||||||
|
notes.append(note)
|
||||||
self._send_stage_mail_template(stage)
|
self._send_stage_mail_template(stage)
|
||||||
if reassignment:
|
if reassignment:
|
||||||
self._append_reassignment_history(stage, users, reason)
|
self._append_reassignment_history(stage, users, line_reason)
|
||||||
|
|
||||||
if not selected_users:
|
if not selected_users:
|
||||||
raise UserError(_("Please select at least one user."))
|
raise UserError(_("Please select at least one user."))
|
||||||
|
|
||||||
|
if reassignment:
|
||||||
|
assignees = (self.user_ids - replaced_users) | selected_users
|
||||||
|
self.sudo().write({'user_ids': [(6, 0, assignees.ids)]})
|
||||||
|
self._sync_involved_assignees_from_timelines()
|
||||||
|
else:
|
||||||
if update_assignees:
|
if update_assignees:
|
||||||
self.sudo().write({'user_ids': [(6, 0, selected_users.ids)]})
|
self.sudo().write({'user_ids': [(6, 0, selected_users.ids)]})
|
||||||
self.sudo().write({'involved_user_ids': [(6, 0, selected_users.ids)]})
|
self.sudo().write({'involved_user_ids': [(6, 0, selected_users.ids)]})
|
||||||
|
|
@ -1096,6 +1127,8 @@ class projectTask(models.Model):
|
||||||
subtype_xmlid='mail.mt_comment',
|
subtype_xmlid='mail.mt_comment',
|
||||||
)
|
)
|
||||||
self._post_to_project_channel(note, selected_users.mapped('partner_id'))
|
self._post_to_project_channel(note, selected_users.mapped('partner_id'))
|
||||||
|
if reassignment:
|
||||||
|
self._notify_reassigned_users(selected_users, note)
|
||||||
|
|
||||||
def _move_to_selected_stage(self, stage):
|
def _move_to_selected_stage(self, stage):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,28 @@
|
||||||
<value eval="{'noupdate': True}"/>
|
<value eval="{'noupdate': True}"/>
|
||||||
</function>
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
<function name="write" model="ir.model.data">
|
||||||
|
<function name="search" model="ir.model.data">
|
||||||
|
<value eval="[('module', '=', 'project'), ('name', '=', 'project_project_manager_rule')]"/>
|
||||||
|
</function>
|
||||||
|
<value eval="{'noupdate': False}"/>
|
||||||
|
</function>
|
||||||
|
|
||||||
|
<record id="project.project_project_manager_rule" model="ir.rule">
|
||||||
|
<field name="domain_force">[(1, '=', 1)]</field>
|
||||||
|
<field name="perm_read" eval="1"/>
|
||||||
|
<field name="perm_write" eval="1"/>
|
||||||
|
<field name="perm_create" eval="1"/>
|
||||||
|
<field name="perm_unlink" eval="1"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<function name="write" model="ir.model.data">
|
||||||
|
<function name="search" model="ir.model.data">
|
||||||
|
<value eval="[('module', '=', 'project'), ('name', '=', 'project_project_manager_rule')]"/>
|
||||||
|
</function>
|
||||||
|
<value eval="{'noupdate': True}"/>
|
||||||
|
</function>
|
||||||
<record id="portfolio_rule_company_projects" model="ir.rule">
|
<record id="portfolio_rule_company_projects" model="ir.rule">
|
||||||
<field name="name">company: Own Company</field>
|
<field name="name">company: Own Company</field>
|
||||||
<field name="model_id" ref="model_project_portfolio"/>
|
<field name="model_id" ref="model_project_portfolio"/>
|
||||||
|
|
@ -48,10 +70,10 @@
|
||||||
<field name="name">Manager: Own Projects</field>
|
<field name="name">Manager: Own Projects</field>
|
||||||
<field name="model_id" ref="project.model_project_project"/>
|
<field name="model_id" ref="project.model_project_project"/>
|
||||||
<field name="groups" eval="[(4, ref('project_task_timesheet_extended.group_project_supervisor'))]"/>
|
<field name="groups" eval="[(4, ref('project_task_timesheet_extended.group_project_supervisor'))]"/>
|
||||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
<field name="domain_force">['|', ('user_id', '=', user.id), ('user_id', '=', False)]</field>
|
||||||
<field name="perm_read" eval="1"/>
|
<field name="perm_read" eval="1"/>
|
||||||
<field name="perm_write" eval="1"/>
|
<field name="perm_write" eval="1"/>
|
||||||
<field name="perm_create" eval="1"/>
|
<field name="perm_create" eval="0"/>
|
||||||
<field name="perm_unlink" eval="0"/>
|
<field name="perm_unlink" eval="0"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
@ -95,11 +117,33 @@
|
||||||
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
||||||
<field name="perm_read" eval="1"/>
|
<field name="perm_read" eval="1"/>
|
||||||
<field name="perm_write" eval="1"/>
|
<field name="perm_write" eval="1"/>
|
||||||
<field name="perm_create" eval="1"/>
|
<field name="perm_create" eval="0"/>
|
||||||
<field name="perm_unlink" eval="0"/>
|
<field name="perm_unlink" eval="0"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<function name="write" model="ir.model.data">
|
||||||
|
<function name="search" model="ir.model.data">
|
||||||
|
<value eval="[('module', '=', 'project'), ('name', '=', 'project_manager_all_project_tasks_rule')]"/>
|
||||||
|
</function>
|
||||||
|
<value eval="{'noupdate': False}"/>
|
||||||
|
</function>
|
||||||
|
|
||||||
|
<record id="project.project_manager_all_project_tasks_rule" model="ir.rule">
|
||||||
|
<field name="domain_force">[(1, '=', 1)]</field>
|
||||||
|
<field name="perm_read" eval="1"/>
|
||||||
|
<field name="perm_write" eval="1"/>
|
||||||
|
<field name="perm_create" eval="1"/>
|
||||||
|
<field name="perm_unlink" eval="1"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<function name="write" model="ir.model.data">
|
||||||
|
<function name="search" model="ir.model.data">
|
||||||
|
<value eval="[('module', '=', 'project'), ('name', '=', 'project_manager_all_project_tasks_rule')]"/>
|
||||||
|
</function>
|
||||||
|
<value eval="{'noupdate': True}"/>
|
||||||
|
</function>
|
||||||
<record model="ir.rule" id="project.ir_rule_private_task">
|
<record model="ir.rule" id="project.ir_rule_private_task">
|
||||||
<field name="domain_force">[
|
<field name="domain_force">[
|
||||||
'&',
|
'&',
|
||||||
|
|
|
||||||
|
|
@ -62,22 +62,9 @@
|
||||||
max-width: calc(100% - 18px);
|
max-width: calc(100% - 18px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.o_task_reassign_arrow {
|
.o_task_reassign_button {
|
||||||
padding: 0;
|
margin-left: 8px;
|
||||||
margin-left: 2px;
|
padding: 2px 10px;
|
||||||
min-width: 12px;
|
line-height: 20px;
|
||||||
width: 12px;
|
white-space: nowrap;
|
||||||
height: 12px;
|
|
||||||
line-height: 12px;
|
|
||||||
color: #374151;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_task_reassign_arrow .fa {
|
|
||||||
font-size: 10px;
|
|
||||||
line-height: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.o_task_reassign_arrow:hover {
|
|
||||||
color: #017e84;
|
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
<div class="o_task_assignees_inline">
|
<div class="o_task_assignees_inline">
|
||||||
<field name="user_ids" nolabel="1" widget="involved_assignee_avatar_user" domain="[('id', 'in', involved_user_ids)]" options="{'no_create': True, 'no_quick_create': True, 'no_create_edit': True}" invisible="is_generic"/>
|
<field name="user_ids" nolabel="1" widget="involved_assignee_avatar_user" domain="[('id', 'in', involved_user_ids)]" options="{'no_create': True, 'no_quick_create': True, 'no_create_edit': True}" invisible="is_generic"/>
|
||||||
<field name="user_ids" nolabel="1" class="o_task_user_field" options="{'no_open': True, 'no_quick_create': True}" widget="many2many_avatar_user" domain="[('id', 'in', assignee_domain_ids)]" invisible="not is_generic"/>
|
<field name="user_ids" nolabel="1" class="o_task_user_field" options="{'no_open': True, 'no_quick_create': True}" widget="many2many_avatar_user" domain="[('id', 'in', assignee_domain_ids)]" invisible="not is_generic"/>
|
||||||
<button name="action_open_reassign_wizard" type="object" icon="fa-caret-down" title="Re-Assign Assignees" class="btn-link o_task_reassign_arrow" invisible="record_paused"/>
|
<button name="action_open_reassign_wizard" type="object" string="Re-Assign" title="Re-Assign Assignees" class="btn btn-secondary btn-sm o_task_reassign_button" invisible="record_paused"/>
|
||||||
</div>
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='timesheet_ids']" position="attributes">
|
<xpath expr="//field[@name='timesheet_ids']" position="attributes">
|
||||||
|
|
@ -293,7 +293,6 @@
|
||||||
<record id="project.project_task_kanban_action_view" model="ir.actions.act_window.view">
|
<record id="project.project_task_kanban_action_view" model="ir.actions.act_window.view">
|
||||||
<field name="view_id" ref="project_task_timesheet_extended.project_task_kanban_task_id_module_project_tasks"/>
|
<field name="view_id" ref="project_task_timesheet_extended.project_task_kanban_task_id_module_project_tasks"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ class TaskBackStageWizard(models.TransientModel):
|
||||||
for wizard in self:
|
for wizard in self:
|
||||||
stages = wizard.task_id.project_id.type_ids
|
stages = wizard.task_id.project_id.type_ids
|
||||||
if wizard.task_id.stage_id:
|
if wizard.task_id.stage_id:
|
||||||
stages = stages.filtered(lambda s: s.id != wizard.task_id.stage_id.id)
|
stages = stages.filtered(lambda s: s.id != wizard.task_id.stage_id.id and s.sequence < wizard.task_id.stage_id.sequence)
|
||||||
wizard.available_stage_ids = [(6, 0, stages.ids)]
|
wizard.available_stage_ids = [(6, 0, stages.ids)]
|
||||||
|
|
||||||
def action_confirm(self):
|
def action_confirm(self):
|
||||||
|
|
@ -89,7 +89,7 @@ class TaskRequestTimelinesWizard(models.TransientModel):
|
||||||
(0, 0, {
|
(0, 0, {
|
||||||
"stage_id": line.stage_id.id,
|
"stage_id": line.stage_id.id,
|
||||||
"responsible_user_id": line.responsible_lead.id,
|
"responsible_user_id": line.responsible_lead.id,
|
||||||
"user_ids": [(6, 0, line.assigned_to.ids)],
|
"user_id": line.assigned_to.id,
|
||||||
})
|
})
|
||||||
for line in task.assignees_timelines.sorted(lambda l: l.stage_sequence)
|
for line in task.assignees_timelines.sorted(lambda l: l.stage_sequence)
|
||||||
if line.stage_id
|
if line.stage_id
|
||||||
|
|
@ -98,7 +98,7 @@ class TaskRequestTimelinesWizard(models.TransientModel):
|
||||||
|
|
||||||
def action_apply(self):
|
def action_apply(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
selected_lines = self.line_ids.filtered(lambda line: line.stage_id and line.user_ids)
|
selected_lines = self.line_ids.filtered(lambda line: line.stage_id and line.user_id)
|
||||||
if not selected_lines:
|
if not selected_lines:
|
||||||
raise UserError(_("Please select users for at least one stage."))
|
raise UserError(_("Please select users for at least one stage."))
|
||||||
self.task_id.sudo().timelines_requested = True
|
self.task_id.sudo().timelines_requested = True
|
||||||
|
|
@ -114,18 +114,41 @@ class TaskRequestTimelinesWizardLine(models.TransientModel):
|
||||||
task_id = fields.Many2one(related="wizard_id.task_id", store=False)
|
task_id = fields.Many2one(related="wizard_id.task_id", store=False)
|
||||||
stage_id = fields.Many2one("project.task.type", string="Stage")
|
stage_id = fields.Many2one("project.task.type", string="Stage")
|
||||||
responsible_user_id = fields.Many2one("res.users", string="Responsible User")
|
responsible_user_id = fields.Many2one("res.users", string="Responsible User")
|
||||||
user_ids = fields.Many2many("res.users", string="Assignees")
|
user_id = fields.Many2one("res.users", string="Assignee")
|
||||||
available_user_ids = fields.Many2many("res.users", compute="_compute_available_user_ids")
|
available_user_ids = fields.Many2many("res.users", compute="_compute_available_user_ids")
|
||||||
|
|
||||||
@api.depends("stage_id", "task_id")
|
def _get_project_involved_users(self):
|
||||||
|
self.ensure_one()
|
||||||
|
users = self.env["res.users"]
|
||||||
|
task = self.task_id
|
||||||
|
project = task.project_id
|
||||||
|
if project:
|
||||||
|
users |= project.members_ids | project.user_id | project.project_lead | project.project_sponsor
|
||||||
|
if self.stage_id:
|
||||||
|
users |= self.stage_id.involved_user_ids
|
||||||
|
if self.stage_id.team_id:
|
||||||
|
users |= self.stage_id.team_id.team_lead | self.stage_id.team_id.all_members_ids
|
||||||
|
if task:
|
||||||
|
users |= task.user_ids | task.involved_user_ids
|
||||||
|
return users.filtered(lambda user: user.active and not user.share)
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
"stage_id",
|
||||||
|
"stage_id.involved_user_ids",
|
||||||
|
"stage_id.team_id",
|
||||||
|
"stage_id.team_id.team_lead",
|
||||||
|
"stage_id.team_id.all_members_ids",
|
||||||
|
"task_id",
|
||||||
|
"task_id.user_ids",
|
||||||
|
"task_id.involved_user_ids",
|
||||||
|
"task_id.project_id.members_ids",
|
||||||
|
"task_id.project_id.user_id",
|
||||||
|
"task_id.project_id.project_lead",
|
||||||
|
"task_id.project_id.project_sponsor",
|
||||||
|
)
|
||||||
def _compute_available_user_ids(self):
|
def _compute_available_user_ids(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
users = line.stage_id.involved_user_ids
|
line.available_user_ids = [(6, 0, line._get_project_involved_users().ids)]
|
||||||
if line.stage_id.team_id:
|
|
||||||
users |= line.stage_id.team_id.all_members_ids
|
|
||||||
if line.task_id.project_id:
|
|
||||||
users |= line.task_id.project_id.members_ids | line.task_id.project_id.user_id | line.task_id.project_id.project_lead
|
|
||||||
line.available_user_ids = [(6, 0, users.filtered(lambda u: not u.share).ids)]
|
|
||||||
|
|
||||||
|
|
||||||
class TaskReassignAssigneesWizard(models.TransientModel):
|
class TaskReassignAssigneesWizard(models.TransientModel):
|
||||||
|
|
@ -133,10 +156,8 @@ class TaskReassignAssigneesWizard(models.TransientModel):
|
||||||
_description = "Task Re-Assign Assignees Wizard"
|
_description = "Task Re-Assign Assignees Wizard"
|
||||||
|
|
||||||
task_id = fields.Many2one("project.task", string="Task", required=True)
|
task_id = fields.Many2one("project.task", string="Task", required=True)
|
||||||
reason = fields.Text(string="Re-assignment Reason", required=True)
|
|
||||||
stage_ids = fields.Many2many("project.task.type", string="Stages", required=True)
|
stage_ids = fields.Many2many("project.task.type", string="Stages", required=True)
|
||||||
available_stage_ids = fields.Many2many("project.task.type", compute="_compute_available_stage_ids")
|
available_stage_ids = fields.Many2many("project.task.type", compute="_compute_available_stage_ids")
|
||||||
user_ids = fields.Many2many("res.users", string="New Assignees", required=True)
|
|
||||||
available_user_ids = fields.Many2many("res.users", compute="_compute_available_user_ids")
|
available_user_ids = fields.Many2many("res.users", compute="_compute_available_user_ids")
|
||||||
line_ids = fields.One2many("task.reassign.assignees.wizard.line", "wizard_id", string="Selected Stage Assignees")
|
line_ids = fields.One2many("task.reassign.assignees.wizard.line", "wizard_id", string="Selected Stage Assignees")
|
||||||
|
|
||||||
|
|
@ -146,14 +167,12 @@ class TaskReassignAssigneesWizard(models.TransientModel):
|
||||||
task = self.env["project.task"].browse(values.get("task_id") or self.env.context.get("default_task_id"))
|
task = self.env["project.task"].browse(values.get("task_id") or self.env.context.get("default_task_id"))
|
||||||
if task:
|
if task:
|
||||||
default_stage = self.env["project.task.type"].browse(self.env.context.get("default_stage_id")) or task.stage_id
|
default_stage = self.env["project.task.type"].browse(self.env.context.get("default_stage_id")) or task.stage_id
|
||||||
default_users = task.user_ids
|
|
||||||
values.setdefault("stage_ids", [(6, 0, default_stage.ids)])
|
values.setdefault("stage_ids", [(6, 0, default_stage.ids)])
|
||||||
values.setdefault("user_ids", [(6, 0, default_users.ids)])
|
|
||||||
if "line_ids" in fields_list and default_stage:
|
if "line_ids" in fields_list and default_stage:
|
||||||
values["line_ids"] = [
|
values["line_ids"] = [
|
||||||
(0, 0, {
|
(0, 0, {
|
||||||
"stage_id": stage.id,
|
"stage_id": stage.id,
|
||||||
"user_ids": [(6, 0, default_users.ids)],
|
"user_id": task.assignees_timelines.filtered(lambda line: line.stage_id == stage)[:1].assigned_to.id,
|
||||||
})
|
})
|
||||||
for stage in default_stage
|
for stage in default_stage
|
||||||
]
|
]
|
||||||
|
|
@ -173,34 +192,46 @@ class TaskReassignAssigneesWizard(models.TransientModel):
|
||||||
if stage.team_id:
|
if stage.team_id:
|
||||||
users |= stage.team_id.all_members_ids
|
users |= stage.team_id.all_members_ids
|
||||||
if wizard.task_id.project_id:
|
if wizard.task_id.project_id:
|
||||||
users |= wizard.task_id.project_id.members_ids | wizard.task_id.project_id.user_id | wizard.task_id.project_id.project_lead
|
users |= wizard.task_id.project_id.members_ids | wizard.task_id.project_id.user_id | wizard.task_id.project_id.project_lead | wizard.task_id.project_id.project_sponsor
|
||||||
wizard.available_user_ids = [(6, 0, users.filtered(lambda u: not u.share).ids)]
|
wizard.available_user_ids = [(6, 0, users.filtered(lambda u: u.active and not u.share).ids)]
|
||||||
|
|
||||||
@api.onchange("stage_ids", "user_ids")
|
def _get_default_user_for_stage(self, stage):
|
||||||
def _onchange_stage_or_users(self):
|
self.ensure_one()
|
||||||
|
timeline = self.task_id.assignees_timelines.filtered(lambda line: line.stage_id == stage)[:1]
|
||||||
|
return timeline.assigned_to.id if timeline else False
|
||||||
|
|
||||||
|
def _preferred_existing_line(self, stage):
|
||||||
|
self.ensure_one()
|
||||||
|
lines = self.line_ids.filtered(lambda line: line.stage_id == stage)
|
||||||
|
return lines.filtered(lambda line: line.user_id or line.reason)[:1] or lines[:1]
|
||||||
|
|
||||||
|
@api.onchange("stage_ids")
|
||||||
|
def _onchange_stage_ids(self):
|
||||||
|
Line = self.env["task.reassign.assignees.wizard.line"]
|
||||||
for wizard in self:
|
for wizard in self:
|
||||||
existing_users_by_stage = {
|
lines = Line
|
||||||
line.stage_id.id: line.user_ids.ids
|
for stage in wizard.stage_ids.sorted(lambda s: s.sequence):
|
||||||
for line in wizard.line_ids
|
existing_line = wizard._preferred_existing_line(stage)
|
||||||
if line.stage_id
|
if existing_line:
|
||||||
}
|
lines |= existing_line
|
||||||
common_user_ids = wizard.user_ids.ids
|
else:
|
||||||
wizard.line_ids = [(5, 0, 0)] + [
|
lines |= Line.new({
|
||||||
(0, 0, {
|
|
||||||
"stage_id": stage.id,
|
"stage_id": stage.id,
|
||||||
"user_ids": [(6, 0, common_user_ids or existing_users_by_stage.get(stage.id, []))],
|
"user_id": wizard._get_default_user_for_stage(stage),
|
||||||
})
|
})
|
||||||
for stage in wizard.stage_ids.sorted(lambda s: s.sequence)
|
wizard.line_ids = lines
|
||||||
]
|
|
||||||
|
|
||||||
def action_confirm(self):
|
def action_confirm(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
selected_lines = self.line_ids.filtered(lambda line: line.stage_id and line.user_ids)
|
selected_lines = self.line_ids.filtered(lambda line: line.stage_id in self.stage_ids and line.user_id)
|
||||||
if not selected_lines:
|
if not selected_lines:
|
||||||
raise UserError(_("Please select stages and new assignees."))
|
raise UserError(_("Please select stages and new assignees."))
|
||||||
self.task_id._apply_task_stage_user_lines(selected_lines, reason=self.reason, reassignment=True)
|
if any(not line.reason for line in selected_lines):
|
||||||
|
raise UserError(_("Please enter a reason for each selected stage."))
|
||||||
|
self.task_id._apply_task_stage_user_lines(selected_lines, reassignment=True)
|
||||||
return {"type": "ir.actions.act_window_close"}
|
return {"type": "ir.actions.act_window_close"}
|
||||||
|
|
||||||
|
|
||||||
class TaskReassignAssigneesWizardLine(models.TransientModel):
|
class TaskReassignAssigneesWizardLine(models.TransientModel):
|
||||||
_name = "task.reassign.assignees.wizard.line"
|
_name = "task.reassign.assignees.wizard.line"
|
||||||
_description = "Task Re-Assign Assignees Stage User Line"
|
_description = "Task Re-Assign Assignees Stage User Line"
|
||||||
|
|
@ -208,15 +239,39 @@ class TaskReassignAssigneesWizardLine(models.TransientModel):
|
||||||
wizard_id = fields.Many2one("task.reassign.assignees.wizard", required=True, ondelete="cascade")
|
wizard_id = fields.Many2one("task.reassign.assignees.wizard", required=True, ondelete="cascade")
|
||||||
task_id = fields.Many2one(related="wizard_id.task_id", store=False)
|
task_id = fields.Many2one(related="wizard_id.task_id", store=False)
|
||||||
stage_id = fields.Many2one("project.task.type", string="Stage")
|
stage_id = fields.Many2one("project.task.type", string="Stage")
|
||||||
user_ids = fields.Many2many("res.users", string="New Assignees")
|
user_id = fields.Many2one("res.users", string="New Assignee")
|
||||||
|
reason = fields.Text(string="Reason")
|
||||||
available_user_ids = fields.Many2many("res.users", compute="_compute_available_user_ids")
|
available_user_ids = fields.Many2many("res.users", compute="_compute_available_user_ids")
|
||||||
|
|
||||||
@api.depends("stage_id", "task_id")
|
def _get_project_involved_users(self):
|
||||||
|
self.ensure_one()
|
||||||
|
users = self.env["res.users"]
|
||||||
|
task = self.task_id
|
||||||
|
project = task.project_id
|
||||||
|
if project:
|
||||||
|
users |= project.members_ids | project.user_id | project.project_lead | project.project_sponsor
|
||||||
|
if self.stage_id:
|
||||||
|
users |= self.stage_id.involved_user_ids
|
||||||
|
if self.stage_id.team_id:
|
||||||
|
users |= self.stage_id.team_id.team_lead | self.stage_id.team_id.all_members_ids
|
||||||
|
if task:
|
||||||
|
users |= task.user_ids | task.involved_user_ids
|
||||||
|
return users.filtered(lambda user: user.active and not user.share)
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
"stage_id",
|
||||||
|
"stage_id.involved_user_ids",
|
||||||
|
"stage_id.team_id",
|
||||||
|
"stage_id.team_id.team_lead",
|
||||||
|
"stage_id.team_id.all_members_ids",
|
||||||
|
"task_id",
|
||||||
|
"task_id.user_ids",
|
||||||
|
"task_id.involved_user_ids",
|
||||||
|
"task_id.project_id.members_ids",
|
||||||
|
"task_id.project_id.user_id",
|
||||||
|
"task_id.project_id.project_lead",
|
||||||
|
"task_id.project_id.project_sponsor",
|
||||||
|
)
|
||||||
def _compute_available_user_ids(self):
|
def _compute_available_user_ids(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
users = line.stage_id.involved_user_ids
|
line.available_user_ids = [(6, 0, line._get_project_involved_users().ids)]
|
||||||
if line.stage_id.team_id:
|
|
||||||
users |= line.stage_id.team_id.all_members_ids
|
|
||||||
if line.task_id.project_id:
|
|
||||||
users |= line.task_id.project_id.members_ids | line.task_id.project_id.user_id | line.task_id.project_id.project_lead
|
|
||||||
line.available_user_ids = [(6, 0, users.filtered(lambda u: not u.share).ids)]
|
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,9 @@
|
||||||
<field name="line_ids" nolabel="1">
|
<field name="line_ids" nolabel="1">
|
||||||
<list editable="bottom" create="0" delete="0">
|
<list editable="bottom" create="0" delete="0">
|
||||||
<field name="stage_id" readonly="1" force_save="1" options="{'no_open': True}"/>
|
<field name="stage_id" readonly="1" force_save="1" options="{'no_open': True}"/>
|
||||||
<field name="responsible_user_id" force_save="1" options="{'no_create': True, 'no_open': True}"/>
|
<field name="responsible_user_id" force_save="1" domain="[('id', 'in', available_user_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
||||||
<field name="available_user_ids" column_invisible="1"/>
|
<field name="available_user_ids" column_invisible="1"/>
|
||||||
<field name="user_ids" string="Assignees" widget="many2many_tags" domain="[('id', 'in', available_user_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
<field name="user_id" string="Assignee" domain="[('id', 'in', available_user_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
<footer>
|
<footer>
|
||||||
|
|
@ -100,16 +100,14 @@
|
||||||
<group>
|
<group>
|
||||||
<field name="task_id" invisible="1"/>
|
<field name="task_id" invisible="1"/>
|
||||||
<field name="available_stage_ids" invisible="1"/>
|
<field name="available_stage_ids" invisible="1"/>
|
||||||
<field name="available_user_ids" invisible="1"/>
|
|
||||||
<field name="reason" placeholder="Enter re-assignment reason..."/>
|
|
||||||
<field name="stage_ids" widget="many2many_tags" domain="[('id', 'in', available_stage_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
<field name="stage_ids" widget="many2many_tags" domain="[('id', 'in', available_stage_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
||||||
<field name="user_ids" widget="many2many_tags" domain="[('id', 'in', available_user_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
|
||||||
</group>
|
</group>
|
||||||
<field name="line_ids" nolabel="1" invisible="not stage_ids">
|
<field name="line_ids" nolabel="1" invisible="not stage_ids">
|
||||||
<list editable="bottom" create="0" delete="0">
|
<list editable="bottom" create="0" delete="0">
|
||||||
<field name="stage_id" readonly="1" force_save="1" options="{'no_open': True}"/>
|
<field name="stage_id" readonly="1" force_save="1" options="{'no_open': True}"/>
|
||||||
<field name="available_user_ids" column_invisible="1"/>
|
<field name="available_user_ids" column_invisible="1"/>
|
||||||
<field name="user_ids" widget="many2many_tags" domain="[('id', 'in', available_user_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
<field name="user_id" string="New Assignee" force_save="1" domain="[('id', 'in', available_user_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
||||||
|
<field name="reason" required="1" force_save="1"/>
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
<footer>
|
<footer>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue