224 lines
7.7 KiB
Python
224 lines
7.7 KiB
Python
from odoo import Command, api, fields, models, _
|
|
from odoo.exceptions import ValidationError
|
|
|
|
class ProjectProject(models.Model):
|
|
_inherit = 'project.project'
|
|
|
|
team_line_ids = fields.One2many(
|
|
'project.team.line',
|
|
'project_id',
|
|
string="Team Details"
|
|
)
|
|
can_manage_team_lines = fields.Boolean(
|
|
compute='_compute_can_manage_team_lines',
|
|
string='Can Manage Team Lines'
|
|
)
|
|
|
|
@api.depends('user_id', 'project_lead')
|
|
def _compute_can_manage_team_lines(self):
|
|
current_user = self.env.user
|
|
for project in self:
|
|
project.can_manage_team_lines = bool(
|
|
self.env.is_superuser()
|
|
or project.user_id == current_user
|
|
or ('project_lead' in project._fields and project.project_lead == current_user)
|
|
)
|
|
|
|
@api.onchange('team_line_ids')
|
|
def _onchange_team_line_ids(self):
|
|
for project in self:
|
|
users = project.team_line_ids.mapped('user_id')
|
|
project.members_ids = [(6, 0, users.ids)]
|
|
|
|
def _sync_members_from_team_lines(self):
|
|
if self.env.context.get('skip_project_team_member_sync'):
|
|
return
|
|
for project in self:
|
|
users = project.team_line_ids.mapped('user_id')
|
|
if set(project.members_ids.ids) != set(users.ids):
|
|
project.with_context(skip_project_team_member_sync=True).sudo().write({
|
|
'members_ids': [Command.set(users.ids)],
|
|
})
|
|
|
|
def _sync_team_lines_from_members(self):
|
|
if self.env.context.get('skip_project_team_member_sync'):
|
|
return
|
|
|
|
TeamLine = self.env['project.team.line'].sudo().with_context(skip_project_team_member_sync=True)
|
|
for project in self.sudo():
|
|
member_ids = set(project.members_ids.ids)
|
|
kept_user_ids = set()
|
|
lines_to_remove = self.env['project.team.line'].sudo()
|
|
|
|
for line in project.team_line_ids.sorted('id'):
|
|
user_id = line.user_id.id
|
|
if not user_id or user_id not in member_ids or user_id in kept_user_ids:
|
|
lines_to_remove |= line
|
|
else:
|
|
kept_user_ids.add(user_id)
|
|
|
|
if lines_to_remove:
|
|
lines_to_remove.with_context(skip_project_team_member_sync=True).unlink()
|
|
|
|
for user_id in member_ids - kept_user_ids:
|
|
TeamLine.create({
|
|
'project_id': project.id,
|
|
'user_id': user_id,
|
|
})
|
|
|
|
@api.model
|
|
def _sync_all_team_lines_from_members(self):
|
|
self.search([])._sync_team_lines_from_members()
|
|
return True
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
projects = super().create(vals_list)
|
|
for project, vals in zip(projects, vals_list):
|
|
if 'team_line_ids' in vals:
|
|
project._sync_members_from_team_lines()
|
|
elif 'members_ids' in vals:
|
|
project._sync_team_lines_from_members()
|
|
return projects
|
|
|
|
def write(self, vals):
|
|
res = super().write(vals)
|
|
if 'team_line_ids' in vals:
|
|
self._sync_members_from_team_lines()
|
|
elif 'members_ids' in vals:
|
|
self._sync_team_lines_from_members()
|
|
return res
|
|
|
|
|
|
|
|
|
|
|
|
class ProjectTeamLine(models.Model):
|
|
_name = 'project.team.line'
|
|
_description = 'Project Team Line'
|
|
_rec_name = 'project_id'
|
|
|
|
project_id = fields.Many2one('project.project', ondelete='cascade')
|
|
user_id = fields.Many2one('res.users')
|
|
|
|
employee_id = fields.Many2one(
|
|
'hr.employee',
|
|
compute="_compute_employee",
|
|
store=True
|
|
)
|
|
|
|
job_id = fields.Many2one(
|
|
'hr.job',
|
|
related='employee_id.job_id',
|
|
store=True
|
|
)
|
|
|
|
start_date = fields.Date()
|
|
end_date = fields.Date()
|
|
|
|
status = fields.Selection([
|
|
('not_started', 'Not Started'),
|
|
('in_progress', 'In Progress'),
|
|
('done', 'Completed')
|
|
], compute='_compute_status', inverse='_inverse_status', store=True, readonly=False)
|
|
can_edit_assignment = fields.Boolean(
|
|
compute='_compute_can_edit_assignment',
|
|
string='Can Edit Assignment'
|
|
)
|
|
|
|
# ------------------------
|
|
# COMPUTE EMPLOYEE
|
|
# ------------------------
|
|
@api.depends('user_id')
|
|
def _compute_employee(self):
|
|
for rec in self:
|
|
rec.employee_id = self.env['hr.employee'].search([
|
|
('user_id', '=', rec.user_id.id)
|
|
], limit=1)
|
|
|
|
@api.depends('start_date', 'end_date')
|
|
def _compute_status(self):
|
|
today = fields.Date.context_today(self)
|
|
for rec in self:
|
|
if rec.end_date and rec.end_date < today:
|
|
rec.status = 'done'
|
|
elif rec.start_date and rec.start_date > today:
|
|
rec.status = 'not_started'
|
|
else:
|
|
rec.status = 'in_progress'
|
|
|
|
@api.depends('project_id.user_id', 'project_id.project_lead')
|
|
def _compute_can_edit_assignment(self):
|
|
current_user = self.env.user
|
|
for rec in self:
|
|
project = rec.project_id
|
|
rec.can_edit_assignment = bool(
|
|
self.env.is_superuser()
|
|
or (project and project.user_id == current_user)
|
|
or (project and 'project_lead' in project._fields and project.project_lead == current_user)
|
|
)
|
|
|
|
def _inverse_status(self):
|
|
# Allow manual edits to the stored computed field.
|
|
# When start/end dates change later, compute will refresh it again.
|
|
return True
|
|
|
|
def _check_manager_access(self):
|
|
if self.env.is_superuser():
|
|
return
|
|
unauthorized = self.filtered(lambda rec: not rec.can_edit_assignment)
|
|
if unauthorized:
|
|
raise ValidationError(_("Only the related project manager can update team assignment dates or status."))
|
|
|
|
# ------------------------
|
|
# SYNC BENCH
|
|
# ------------------------
|
|
def _sync_bench(self):
|
|
# Bench data is read live from SQL view / computed fields,
|
|
# so there is no separate sync model to refresh here.
|
|
return True
|
|
|
|
# ------------------------
|
|
# CREATE
|
|
# ------------------------
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
if not self.env.is_superuser():
|
|
for vals in vals_list:
|
|
project_id = vals.get('project_id')
|
|
if project_id:
|
|
project = self.env['project.project'].browse(project_id)
|
|
if not (
|
|
project.user_id == self.env.user
|
|
or ('project_lead' in project._fields and project.project_lead == self.env.user)
|
|
):
|
|
raise ValidationError(_("Only the related project manager can add team assignments."))
|
|
records = super().create(vals_list)
|
|
records._sync_bench()
|
|
records.mapped('project_id')._sync_members_from_team_lines()
|
|
return records
|
|
|
|
# ------------------------
|
|
# WRITE
|
|
# ------------------------
|
|
def write(self, vals):
|
|
if any(key in vals for key in ('status', 'start_date', 'end_date', 'user_id', 'project_id')):
|
|
self._check_manager_access()
|
|
projects = self.mapped('project_id')
|
|
res = super().write(vals)
|
|
self._sync_bench()
|
|
if any(key in vals for key in ('user_id', 'project_id')):
|
|
(projects | self.mapped('project_id'))._sync_members_from_team_lines()
|
|
return res
|
|
|
|
# ------------------------
|
|
# UNLINK
|
|
# ------------------------
|
|
def unlink(self):
|
|
projects = self.mapped('project_id')
|
|
self._check_manager_access()
|
|
res = super().unlink()
|
|
self._sync_bench()
|
|
projects._sync_members_from_team_lines()
|
|
return res
|