enhancements of PMT
This commit is contained in:
parent
582225e11e
commit
05bdddc472
|
|
@ -12,4 +12,4 @@
|
|||
],
|
||||
},
|
||||
"installable": True,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,23 @@ class projectTask(models.Model):
|
|||
if stage and stage.mail_template_id:
|
||||
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):
|
||||
self.ensure_one()
|
||||
if not stage:
|
||||
|
|
@ -1051,16 +1068,19 @@ class projectTask(models.Model):
|
|||
self._post_to_project_channel(note, users.mapped('partner_id'))
|
||||
self._send_stage_mail_template(stage)
|
||||
if reassignment:
|
||||
self._notify_reassigned_users(users, note)
|
||||
self._append_reassignment_history(stage, users, reason)
|
||||
|
||||
def _apply_task_stage_user_lines(self, lines, reason=False, reassignment=False, update_assignees=True):
|
||||
self.ensure_one()
|
||||
self._ensure_task_timeline_rows()
|
||||
selected_users = self.env['res.users']
|
||||
replaced_users = self.env['res.users']
|
||||
notes = []
|
||||
for wizard_line in lines:
|
||||
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)
|
||||
if not stage or not users:
|
||||
continue
|
||||
|
|
@ -1068,22 +1088,33 @@ class projectTask(models.Model):
|
|||
timeline = self.assignees_timelines.filtered(lambda t: t.stage_id == stage)[:1]
|
||||
primary_user = users[0]
|
||||
if timeline:
|
||||
if reassignment and timeline.assigned_to:
|
||||
replaced_users |= timeline.assigned_to
|
||||
timeline.sudo().write({
|
||||
'assigned_to': 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,
|
||||
})
|
||||
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)
|
||||
if reassignment:
|
||||
self._append_reassignment_history(stage, users, reason)
|
||||
self._append_reassignment_history(stage, users, line_reason)
|
||||
|
||||
if not selected_users:
|
||||
raise UserError(_("Please select at least one user."))
|
||||
|
||||
if update_assignees:
|
||||
self.sudo().write({'user_ids': [(6, 0, selected_users.ids)]})
|
||||
self.sudo().write({'involved_user_ids': [(6, 0, selected_users.ids)]})
|
||||
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:
|
||||
self.sudo().write({'user_ids': [(6, 0, selected_users.ids)]})
|
||||
self.sudo().write({'involved_user_ids': [(6, 0, selected_users.ids)]})
|
||||
action = _("Re-assigned") if reassignment else _("Timeline users selected")
|
||||
note = _("%s: %s") % (action, "; ".join(notes))
|
||||
if reason:
|
||||
|
|
@ -1096,6 +1127,8 @@ class projectTask(models.Model):
|
|||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
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):
|
||||
self.ensure_one()
|
||||
|
|
|
|||
|
|
@ -38,6 +38,28 @@
|
|||
<value eval="{'noupdate': True}"/>
|
||||
</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">
|
||||
<field name="name">company: Own Company</field>
|
||||
<field name="model_id" ref="model_project_portfolio"/>
|
||||
|
|
@ -48,10 +70,10 @@
|
|||
<field name="name">Manager: Own Projects</field>
|
||||
<field name="model_id" ref="project.model_project_project"/>
|
||||
<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_write" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
</record>
|
||||
|
||||
|
|
@ -95,11 +117,33 @@
|
|||
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
||||
<field name="perm_read" 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"/>
|
||||
</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">
|
||||
<field name="domain_force">[
|
||||
'&',
|
||||
|
|
|
|||
|
|
@ -62,22 +62,9 @@
|
|||
max-width: calc(100% - 18px);
|
||||
}
|
||||
|
||||
.o_task_reassign_arrow {
|
||||
padding: 0;
|
||||
margin-left: 2px;
|
||||
min-width: 12px;
|
||||
width: 12px;
|
||||
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;
|
||||
}
|
||||
.o_task_reassign_button {
|
||||
margin-left: 8px;
|
||||
padding: 2px 10px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
<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" 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>
|
||||
</xpath>
|
||||
<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">
|
||||
<field name="view_id" ref="project_task_timesheet_extended.project_task_kanban_task_id_module_project_tasks"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class TaskBackStageWizard(models.TransientModel):
|
|||
for wizard in self:
|
||||
stages = wizard.task_id.project_id.type_ids
|
||||
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)]
|
||||
|
||||
def action_confirm(self):
|
||||
|
|
@ -89,7 +89,7 @@ class TaskRequestTimelinesWizard(models.TransientModel):
|
|||
(0, 0, {
|
||||
"stage_id": line.stage_id.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)
|
||||
if line.stage_id
|
||||
|
|
@ -98,7 +98,7 @@ class TaskRequestTimelinesWizard(models.TransientModel):
|
|||
|
||||
def action_apply(self):
|
||||
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:
|
||||
raise UserError(_("Please select users for at least one stage."))
|
||||
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)
|
||||
stage_id = fields.Many2one("project.task.type", string="Stage")
|
||||
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")
|
||||
|
||||
@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):
|
||||
for line in self:
|
||||
users = line.stage_id.involved_user_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)]
|
||||
line.available_user_ids = [(6, 0, line._get_project_involved_users().ids)]
|
||||
|
||||
|
||||
class TaskReassignAssigneesWizard(models.TransientModel):
|
||||
|
|
@ -133,10 +156,8 @@ class TaskReassignAssigneesWizard(models.TransientModel):
|
|||
_description = "Task Re-Assign Assignees Wizard"
|
||||
|
||||
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)
|
||||
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")
|
||||
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"))
|
||||
if task:
|
||||
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("user_ids", [(6, 0, default_users.ids)])
|
||||
if "line_ids" in fields_list and default_stage:
|
||||
values["line_ids"] = [
|
||||
(0, 0, {
|
||||
"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
|
||||
]
|
||||
|
|
@ -173,34 +192,46 @@ class TaskReassignAssigneesWizard(models.TransientModel):
|
|||
if stage.team_id:
|
||||
users |= stage.team_id.all_members_ids
|
||||
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
|
||||
wizard.available_user_ids = [(6, 0, users.filtered(lambda u: not u.share).ids)]
|
||||
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: u.active and not u.share).ids)]
|
||||
|
||||
@api.onchange("stage_ids", "user_ids")
|
||||
def _onchange_stage_or_users(self):
|
||||
def _get_default_user_for_stage(self, stage):
|
||||
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:
|
||||
existing_users_by_stage = {
|
||||
line.stage_id.id: line.user_ids.ids
|
||||
for line in wizard.line_ids
|
||||
if line.stage_id
|
||||
}
|
||||
common_user_ids = wizard.user_ids.ids
|
||||
wizard.line_ids = [(5, 0, 0)] + [
|
||||
(0, 0, {
|
||||
"stage_id": stage.id,
|
||||
"user_ids": [(6, 0, common_user_ids or existing_users_by_stage.get(stage.id, []))],
|
||||
})
|
||||
for stage in wizard.stage_ids.sorted(lambda s: s.sequence)
|
||||
]
|
||||
lines = Line
|
||||
for stage in wizard.stage_ids.sorted(lambda s: s.sequence):
|
||||
existing_line = wizard._preferred_existing_line(stage)
|
||||
if existing_line:
|
||||
lines |= existing_line
|
||||
else:
|
||||
lines |= Line.new({
|
||||
"stage_id": stage.id,
|
||||
"user_id": wizard._get_default_user_for_stage(stage),
|
||||
})
|
||||
wizard.line_ids = lines
|
||||
|
||||
def action_confirm(self):
|
||||
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:
|
||||
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"}
|
||||
|
||||
|
||||
class TaskReassignAssigneesWizardLine(models.TransientModel):
|
||||
_name = "task.reassign.assignees.wizard.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")
|
||||
task_id = fields.Many2one(related="wizard_id.task_id", store=False)
|
||||
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")
|
||||
|
||||
@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):
|
||||
for line in self:
|
||||
users = line.stage_id.involved_user_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)]
|
||||
line.available_user_ids = [(6, 0, line._get_project_involved_users().ids)]
|
||||
|
|
|
|||
|
|
@ -79,9 +79,9 @@
|
|||
<field name="line_ids" nolabel="1">
|
||||
<list editable="bottom" create="0" delete="0">
|
||||
<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="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>
|
||||
</field>
|
||||
<footer>
|
||||
|
|
@ -100,16 +100,14 @@
|
|||
<group>
|
||||
<field name="task_id" 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="user_ids" widget="many2many_tags" domain="[('id', 'in', available_user_ids)]" options="{'no_create': True, 'no_open': True}"/>
|
||||
</group>
|
||||
<field name="line_ids" nolabel="1" invisible="not stage_ids">
|
||||
<list editable="bottom" create="0" delete="0">
|
||||
<field name="stage_id" readonly="1" force_save="1" options="{'no_open': True}"/>
|
||||
<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>
|
||||
</field>
|
||||
<footer>
|
||||
|
|
|
|||
Loading…
Reference in New Issue